From 8b5328993ebfec63f00f95dab510c19e817da761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Fri, 7 Aug 2026 00:27:55 +0800 Subject: [PATCH] feat(native): add protected JD final residency control --- ...ATIVE-RESIDENCY-CONTROL-GATE-20260807.hdlp | 34 ++++ .../guanghu-os/native/x86_64-bios/boot.asm | 22 +++ .../native/x86_64-bios/ghal-virtio.asm | 19 +- .../build-native-final-resident-candidate.sh | 51 ++++++ .../install-jd-final-resident-relay.sh | 77 ++++++++ .../scripts/physical-native-icmp-peer.py | 111 +++++++++-- .../scripts/qemu-native-net-peer.py | 41 ++++- .../test-native-final-resident-candidate.sh | 172 ++++++++++++++++++ 8 files changed, 503 insertions(+), 24 deletions(-) create mode 100644 product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/FINAL-NATIVE-RESIDENCY-CONTROL-GATE-20260807.hdlp create mode 100755 product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh create mode 100755 product-source/hololake-platform/guanghu-os/scripts/install-jd-final-resident-relay.sh create mode 100755 product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh diff --git a/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/FINAL-NATIVE-RESIDENCY-CONTROL-GATE-20260807.hdlp b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/FINAL-NATIVE-RESIDENCY-CONTROL-GATE-20260807.hdlp new file mode 100644 index 0000000..1b03437 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/deployments/JD-FD-PRIMARY/FINAL-NATIVE-RESIDENCY-CONTROL-GATE-20260807.hdlp @@ -0,0 +1,34 @@ +schema: guanghu.jd-final-native-residency-control-gate/v1 +gate_id: GH-OS-JD-FD-PRIMARY-001-FINAL-RESIDENCY-CONTROL +status: SOURCE_READY_QEMU_PASS_PHYSICAL_PENDING +node_id: JD-FD-PRIMARY +problem: + previous_native_default: PASS_100 + previous_native_runtime_selected_recovery_itself: true + final_native_residency_proven: false +required_model: + normal_state: GUANGHU_OS_NATIVE_RESIDENT + native_runtime_autonomously_selects_recovery: false + outbound_relay_keeps_nat_path_alive: true + ordinary_reply: HLDP-NATIVE-ACK + recovery_reply: PER_DEPLOYMENT_128_BIT_CAPABILITY + recovery_request_authority: EXPLICIT_ROOT_ACTION_ON_BS-SG-003 + token_repository_exposure: forbidden + wrong_capability: IGNORE_AND_REMAIN_NATIVE +qemu_acceptance: + native_initial_pipeline: PASS_100 + native_remained_resident_before_authorized_recovery: PASS_100 + wrong_capability_ignored: PASS_100 + protected_recovery_capability_returned_to_linux: PASS_100 +physical_acceptance: + source_published: pending + relay_installed: pending + one_time_final_candidate_booted: pending + sustained_native_residency_observed: pending + explicit_recovery_request_verified: pending + returned_linux_and_beacon_cleared: pending + final_native_residency_proven: false +boundary: + qemu_capability: 100 + physical_server_capability: 0 + do_not_claim_final_residency_until_physical_gate: true diff --git a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm index 536eaa7..d21f02a 100644 --- a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm +++ b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/boot.asm @@ -216,6 +216,27 @@ long_mode_start: jc native_block_proof_error call ghal_block_persist_branch_receipt jc native_world_store_error +%ifdef GHOS_NATIVE_FINAL_RESIDENT + mov byte [rel physical_proof_flag], 0xa7 + call ghal_block_write_proof + jc native_block_proof_error +.final_resident_loop: + ; Keep the outbound NAT path alive. Recovery is selected by the protected + ; relay capability, never autonomously by the native runtime. + mov byte [rel ghal_net_expected_sequence], 6 + lea rax, [rel ghal_login_magic] + mov [rel ghal_net_expected_magic], rax + mov byte [rel ghal_net_command_kind], 0 + call ghal_net_icmp_exchange_once + jc .final_resident_loop + cmp byte [rel ghal_net_matched_kind], 3 + jne .final_resident_loop + call ghal_write_native_recovery_beacon + jc native_world_store_error + call ghal_block_write_proof + jc native_block_proof_error + jmp hardware_reset64 +%else %ifdef GHOS_NATIVE_RESIDENT mov ecx, 10 .resident_login_loop: @@ -240,6 +261,7 @@ long_mode_start: jc native_block_proof_error call ghal_write_native_recovery_beacon jc native_world_store_error +%endif %endif mov byte [rel physical_proof_flag], 0xa7 %endif diff --git a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm index 990143f..8ea7310 100644 --- a/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm +++ b/product-source/hololake-platform/guanghu-os/native/x86_64-bios/ghal-virtio.asm @@ -1148,14 +1148,26 @@ ghal_net_icmp_exchange_once: lea rdi, [rel ghal_native_ack_magic] mov ecx, GHOS_LOGIN_MAGIC_SIZE repe cmpsb + je .expected_magic_match +%ifdef GHOS_NATIVE_FINAL_RESIDENT + cmp byte [rel ghal_net_command_kind], 0 jne .ignore_packet - jmp .expected_magic_match + mov rsi, VIRTIO_NET_RX_BUFFER + GHOS_LOGIN_MAGIC_OFFSET + GHOS_LOGIN_MAGIC_SIZE + lea rdi, [rel ghal_recovery_capability] + mov ecx, GHOS_LOGIN_MAGIC_SIZE + repe cmpsb + jne .ignore_packet + mov byte [rel ghal_net_matched_kind], 3 + jmp .matched_kind_ready +%else + jmp .ignore_packet +%endif .expected_magic_match: mov al, [rel ghal_net_command_kind] -.store_matched_kind: mov [rel ghal_net_matched_kind], al +.matched_kind_ready: cmp byte [rel ghal_net_matched_kind], 0 jne .record_command mov byte [rel physical_proof_ipv4_rx_verified], 1 @@ -1299,6 +1311,9 @@ ghal_code_commit_magic: db "HLDP-CODE-COMMIT" ghal_branch_move_magic: db "HLDP-BRANCH-MOVE" ghal_recovery_magic: db "HLDP-RECOVER-OS!" ghal_native_ack_magic: db "HLDP-NATIVE-ACK!" +%ifdef GHOS_NATIVE_FINAL_RESIDENT +%include "recovery-token.inc" +%endif ghal_branch_receipt_template: db "GHOS_BRANCH_MAIN_V1", 10 db "channel=HLP-MOD-CODE-CHANNEL", 10 diff --git a/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh new file mode 100755 index 0000000..a998837 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/build-native-final-resident-candidate.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 3 ]] || { + echo "usage: build-native-final-resident-candidate.sh " >&2 + exit 64 +} + +world_root=$(readlink -f "$1") +output_root=$(readlink -m "$2") +token_file=$(readlink -f "$3") +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +native_root=${source_root}/native/x86_64-bios + +command -v nasm >/dev/null +token_hex=$(tr -d '\r\n ' <"${token_file}") +[[ ${token_hex} =~ ^[0-9a-fA-F]{32}$ ]] || { + echo "recovery token must be exactly 16 bytes encoded as 32 hexadecimal characters" >&2 + exit 65 +} + +mkdir -p "${output_root}" +chmod 0700 "${output_root}" +cargo run --quiet --manifest-path "${source_root}/Cargo.toml" \ + -p hldp-native-compiler -- "${world_root}" "${output_root}/world.inc" +{ + printf 'ghal_recovery_capability: db ' + for index in $(seq 0 15); do + [[ ${index} -eq 0 ]] || printf ',' + printf '0x%s' "${token_hex:$((index * 2)):2}" + done + printf '\n' +} >"${output_root}/recovery-token.inc" +chmod 0600 "${output_root}/recovery-token.inc" + +layout_args=() +while IFS= read -r argument; do layout_args+=("${argument}"); done \ + < <("${source_root}/scripts/native-layout-nasm-args.sh" "${world_root}") +( + cd "${output_root}" + nasm -f bin -I "${output_root}/" -I "${native_root}/" \ + "${layout_args[@]}" \ + -dGHOS_PHYSICAL_CANDIDATE=1 \ + -dGHOS_NATIVE_RESIDENT=1 \ + -dGHOS_NATIVE_FINAL_RESIDENT=1 \ + "${native_root}/boot.asm" \ + -o guanghu-os-x86_64-bios-final-resident.img +) +[[ $(stat -c %s "${output_root}/guanghu-os-x86_64-bios-final-resident.img") -eq 14848 ]] +sha256sum "${output_root}/guanghu-os-x86_64-bios-final-resident.img" \ + >"${output_root}/guanghu-os-x86_64-bios-final-resident.img.sha256" diff --git a/product-source/hololake-platform/guanghu-os/scripts/install-jd-final-resident-relay.sh b/product-source/hololake-platform/guanghu-os/scripts/install-jd-final-resident-relay.sh new file mode 100755 index 0000000..946d909 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/install-jd-final-resident-relay.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 5 ]] || { + echo "usage: install-jd-final-resident-relay.sh " >&2 + exit 64 +} +[[ ${EUID} -eq 0 ]] || exit 77 + +peer_script=$(readlink -f "$1") +token_file=$(readlink -f "$2") +allowed_source=$3 +request_file=$(readlink -m "$4") +receipt_file=$(readlink -m "$5") + +token_hex=$(tr -d '\r\n ' <"${token_file}") +[[ ${token_hex} =~ ^[0-9a-fA-F]{32}$ ]] || exit 65 +[[ ${allowed_source} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit 65 +[[ ${request_file} == /run/guanghu-jd-native-final-recovery.request ]] || exit 65 +[[ ${receipt_file} == /var/lib/guanghu-os/jd-final-resident-relay.hldp ]] || exit 65 + +install -D -m 0755 "${peer_script}" \ + /usr/local/libexec/guanghu-os/physical-native-icmp-peer.py +install -D -m 0600 "${token_file}" \ + /etc/guanghu-os/jd-final-resident-recovery-token.hex +install -d -m 0700 /var/lib/guanghu-os +rm -f "${request_file}" + +install -m 0644 /dev/stdin \ + /etc/systemd/system/guanghu-jd-native-final-resident-relay.service </dev/null +echo "installed but not started" diff --git a/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py b/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py index 1854fbf..0df0b2b 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py +++ b/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import argparse +import os import pathlib import socket import struct @@ -72,12 +73,17 @@ def verify_native_request(packet: bytes, pipeline: list[bytes]) -> tuple[int, st return sequence, source -def native_ack_reply(packet: bytes) -> bytes: +def native_ack_reply( + packet: bytes, + response_capability: bytes = NATIVE_ACK_MAGIC, +) -> bytes: + if len(response_capability) != 16: + raise ValueError("response capability must be exactly 16 bytes") header_length = (packet[0] & 0x0F) * 4 icmp = bytearray(packet[header_length:]) icmp[0] = 0 icmp[2:4] = b"\0\0" - icmp[32:48] = NATIVE_ACK_MAGIC + icmp[32:48] = response_capability icmp[2:4] = struct.pack("!H", checksum(bytes(icmp))) return bytes(icmp) @@ -122,6 +128,9 @@ def main() -> None: parser.add_argument("--resume-resident-count", type=int) parser.add_argument("--resident-pipeline", action="store_true") parser.add_argument("--native-relay", action="store_true") + parser.add_argument("--final-resident-relay", action="store_true") + parser.add_argument("--recovery-token-file") + parser.add_argument("--recovery-request-file") parser.add_argument("--allowed-source") parser.add_argument("--native-ack-delay", type=float, default=0.25) parser.add_argument("--native-ack-repetitions", type=int, default=4) @@ -149,37 +158,66 @@ def main() -> None: args.resume_resident_count is not None, args.resident_pipeline, args.native_relay, + args.final_resident_relay, ) ) > 1: raise SystemExit( "--resident, --login-only, --resume-after-login, and " "--resume-after-commit, --resume-after-branch, and " - "--resume-resident-count, --resident-pipeline, and --native-relay " + "--resume-resident-count, --resident-pipeline, --native-relay, " + "and --final-resident-relay " "are mutually exclusive" ) socket_type = socket.SOCK_RAW if args.raw_socket else socket.SOCK_DGRAM peer = socket.socket(socket.AF_INET, socket_type, socket.IPPROTO_ICMP) deadline = time.monotonic() + args.timeout - if args.native_relay: + if args.native_relay or args.final_resident_relay: if not args.raw_socket: - raise SystemExit("--native-relay requires --raw-socket") + raise SystemExit("native relay modes require --raw-socket") if not args.allowed_source: - raise SystemExit("--native-relay requires --allowed-source") + raise SystemExit("native relay modes require --allowed-source") + recovery_capability = None + if args.final_resident_relay: + if not args.recovery_token_file or not args.recovery_request_file: + raise SystemExit( + "--final-resident-relay requires --recovery-token-file " + "and --recovery-request-file" + ) + token_hex = ( + pathlib.Path(args.recovery_token_file) + .read_text(encoding="ascii") + .strip() + ) + recovery_capability = bytes.fromhex(token_hex) + if len(recovery_capability) != 16: + raise SystemExit("recovery token must be exactly 16 bytes") allowed_source = socket.gethostbyname(args.allowed_source) - pipeline = [ - LOGIN_MAGIC, - LOGIN_MAGIC, - LOGIN_MAGIC, - COMMIT_MAGIC, - BRANCH_MAGIC, - *([LOGIN_MAGIC] * 10), - RECOVERY_MAGIC, - ] + pipeline = ( + [ + LOGIN_MAGIC, + LOGIN_MAGIC, + LOGIN_MAGIC, + COMMIT_MAGIC, + BRANCH_MAGIC, + LOGIN_MAGIC, + ] + if args.final_resident_relay + else [ + LOGIN_MAGIC, + LOGIN_MAGIC, + LOGIN_MAGIC, + COMMIT_MAGIC, + BRANCH_MAGIC, + *([LOGIN_MAGIC] * 10), + RECOVERY_MAGIC, + ] + ) peer.settimeout(0.5) acknowledged: set[int] = set() sources: set[str] = set() completed_at: float | None = None + recovery_capability_sent = False while time.monotonic() < deadline and ( completed_at is None or time.monotonic() < completed_at + 5.0 ): @@ -196,7 +234,15 @@ def main() -> None: # Repeat the authenticated frame because the physical JD path can # lose a single raw-socket reply after NAT has been established. time.sleep(args.native_ack_delay) - reply = native_ack_reply(packet) + response_capability = NATIVE_ACK_MAGIC + if ( + args.final_resident_relay + and sequence == 6 + and os.path.isfile(args.recovery_request_file) + ): + response_capability = recovery_capability + recovery_capability_sent = True + reply = native_ack_reply(packet, response_capability) for repetition in range(args.native_ack_repetitions): peer.sendto(reply, address) if repetition + 1 < args.native_ack_repetitions: @@ -208,8 +254,39 @@ def main() -> None: ) acknowledged.add(sequence) sources.add(source) - if len(acknowledged) == len(pipeline) and completed_at is None: + if args.final_resident_relay: + ready = all(sequence_id in acknowledged for sequence_id in range(1, 7)) + receipt = pathlib.Path(args.receipt) + receipt.write_text( + "schema: guanghu.physical-native-final-resident-relay/v1\n" + f"status: {'RECOVERY_CAPABILITY_SENT' if recovery_capability_sent else 'READY_NATIVE_RESIDENT' if ready else 'STARTING'}\n" + "handshake_direction: NATIVE_INITIATED_OUTBOUND_ICMP\n" + "native_ack_marker: HLDP-NATIVE-ACK!\n" + "relay_cannot_select_command: true\n" + "recovery_requires_root_owned_request_file: true\n" + f"recovery_capability_sent: {str(recovery_capability_sent).lower()}\n" + f"acknowledged_sequences: {','.join(str(item) for item in sorted(acknowledged))}\n" + f"observed_nat_sources: {','.join(sorted(sources))}\n", + encoding="utf-8", + ) + if ready and recovery_capability_sent and completed_at is None: + completed_at = time.monotonic() + elif len(acknowledged) == len(pipeline) and completed_at is None: completed_at = time.monotonic() + if args.final_resident_relay: + if not all(sequence_id in acknowledged for sequence_id in range(1, 7)): + raise TimeoutError( + "native final-resident relay did not reach ready state; " + "acknowledged sequences: " + + ",".join(str(item) for item in sorted(acknowledged)) + ) + if not recovery_capability_sent: + raise TimeoutError( + "native final-resident relay remained ready but no " + "authorized recovery request was observed" + ) + print(pathlib.Path(args.receipt).read_text(encoding="utf-8"), end="") + return if len(acknowledged) != len(pipeline): raise TimeoutError( "native relay pipeline incomplete; acknowledged sequences: " diff --git a/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py b/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py index 368acf7..04aa690 100644 --- a/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py +++ b/product-source/hololake-platform/guanghu-os/scripts/qemu-native-net-peer.py @@ -78,10 +78,12 @@ def ordinary_reply(frame: bytes) -> bytes: return bytes(reply) -def authenticated_reply(frame: bytes, magic: bytes) -> bytes: +def authenticated_reply(frame: bytes, response_capability: bytes = NATIVE_ACK_MAGIC) -> bytes: + if len(response_capability) != 16: + raise ValueError("response capability must be exactly 16 bytes") reply = bytearray(ordinary_reply(frame)) reply[36:38] = b"\0\0" - reply[66:82] = NATIVE_ACK_MAGIC + reply[66:82] = response_capability reply[36:38] = struct.pack("!H", checksum(bytes(reply[34:82]))) return bytes(reply) @@ -93,11 +95,23 @@ def main() -> None: parser.add_argument("--qemu-port", type=int, required=True) parser.add_argument("--receipt", required=True) parser.add_argument("--resident", action="store_true") + parser.add_argument("--final-resident", action="store_true") + parser.add_argument("--recovery-token-file") parser.add_argument("--login-only", action="store_true") parser.add_argument("--guest-ip", default="172.16.0.6") parser.add_argument("--peer-ip", default="172.16.0.1") parser.add_argument("--relay-ip", default="43.153.193.169") args = parser.parse_args() + if sum((args.resident, args.final_resident, args.login_only)) > 1: + raise SystemExit("--resident, --final-resident and --login-only are mutually exclusive") + if args.final_resident and not args.recovery_token_file: + raise SystemExit("--final-resident requires --recovery-token-file") + recovery_capability = None + if args.recovery_token_file: + token_hex = open(args.recovery_token_file, encoding="ascii").read().strip() + recovery_capability = bytes.fromhex(token_hex) + if len(recovery_capability) != 16: + raise SystemExit("recovery token must be exactly 16 bytes") GUEST_IP = socket.inet_aton(args.guest_ip) GATEWAY_IP = socket.inet_aton(args.peer_ip) RELAY_IP = socket.inet_aton(args.relay_ip) @@ -135,6 +149,9 @@ def main() -> None: elif sequence == 5: magic = BRANCH_MAGIC branch_verified = True + elif args.final_resident and sequence == 6: + magic = LOGIN_MAGIC + resident_login_count += 1 elif 6 <= sequence <= 15: magic = LOGIN_MAGIC resident_login_count += 1 @@ -146,14 +163,26 @@ def main() -> None: verified_request(frame, sequence, magic) peer.sendto(ordinary_reply(frame), qemu) time.sleep(0.05) - reply = authenticated_reply(frame, magic) + response_capability = NATIVE_ACK_MAGIC + if args.final_resident and resident_login_count >= 3: + response_capability = recovery_capability + recovery_verified = True + reply = authenticated_reply(frame, response_capability) for repetition in range(4): peer.sendto(reply, qemu) if repetition < 3: time.sleep(0.01) - terminal_sequence = 3 if args.login_only else (16 if args.resident else 5) - if sequence != terminal_sequence: + terminal = ( + sequence == 3 + if args.login_only + else recovery_verified + if args.final_resident + else sequence == 16 + if args.resident + else sequence == 5 + ) + if not terminal: continue with open(args.receipt, "w", encoding="utf-8") as output: output.write( @@ -169,6 +198,8 @@ def main() -> None: f"branch_move_reply_verified: {str(branch_verified).lower()}\n" f"resident_login_reply_count: {resident_login_count}\n" f"recovery_reply_verified: {str(recovery_verified).lower()}\n" + f"final_resident_control: {str(args.final_resident).lower()}\n" + "recovery_selected_by_native: false\n" "login_magic: HLDP-GHOS-LOGIN!\n" ) return diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh new file mode 100755 index 0000000..50429cd --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/test-native-final-resident-candidate.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 3 ]] || { + echo "usage: test-native-final-resident-candidate.sh " >&2 + exit 64 +} + +candidate=$(readlink -f "$1") +token_file=$(readlink -f "$2") +receipt=$(readlink -m "$3") +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +native_root=${source_root}/native/x86_64-bios +test_root=$(mktemp -d) +peer_pid= +cleanup() { + if [[ -n ${peer_pid} ]]; then + kill "${peer_pid}" 2>/dev/null || true + fi + if [[ ${GHOS_KEEP_TEST_ROOT:-0} != 1 ]]; then + rm -rf "${test_root}" + else + echo "GHOS_TEST_ROOT=${test_root}" >&2 + fi +} +trap cleanup EXIT + +[[ $(stat -c %s "${candidate}") -eq 14848 ]] +[[ $(tr -d '\r\n ' <"${token_file}" | wc -c) -eq 32 ]] +python3 - "${token_file}" "${test_root}/wrong-token.hex" <<'PY' +import pathlib +import sys + +token = bytearray.fromhex(pathlib.Path(sys.argv[1]).read_text().strip()) +token[0] ^= 0x01 +pathlib.Path(sys.argv[2]).write_text(token.hex() + "\n", encoding="ascii") +PY +truncate -s 700M "${test_root}/disk.img" +nasm -f bin -dCANDIDATE_LBA=105 -dPROOF_LBA=134 \ + "${native_root}/physical-test-mbr.asm" -o "${test_root}/mbr.bin" +dd if="${test_root}/mbr.bin" of="${test_root}/disk.img" \ + bs=512 seek=0 conv=notrunc status=none +dd if="${candidate}" of="${test_root}/disk.img" \ + bs=512 seek=105 conv=notrunc status=none + +peer_port=$((33000 + ${BASHPID:-$$} % 1000)) +qemu_port=$((peer_port + 1)) +python3 "${source_root}/scripts/qemu-native-net-peer.py" \ + --listen-port "${peer_port}" \ + --qemu-port "${qemu_port}" \ + --receipt "${test_root}/peer.hldp" \ + --final-resident \ + --recovery-token-file "${token_file}" >"${test_root}/peer.log" 2>&1 & +peer_pid=$! +set +e +timeout 40 qemu-system-x86_64 \ + -machine pc,accel=tcg \ + -m 64M \ + -drive "if=none,id=ghboot,format=raw,file=${test_root}/disk.img" \ + -device virtio-blk-pci,drive=ghboot,disable-modern=on,bootindex=0 \ + -netdev "dgram,id=ghnet,local.type=inet,local.host=127.0.0.1,local.port=${qemu_port},remote.type=inet,remote.host=127.0.0.1,remote.port=${peer_port}" \ + -device virtio-net-pci,netdev=ghnet,disable-modern=on,mac=52:54:00:26:71:98 \ + -display none \ + -monitor none \ + -serial "file:${test_root}/serial.log" \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 +qemu_status=$? +set -e +[[ ${qemu_status} -eq 33 ]] +wait "${peer_pid}" +peer_pid= + +grep -q '^final_resident_control: true$' "${test_root}/peer.hldp" +grep -q '^resident_login_reply_count: 3$' "${test_root}/peer.hldp" +grep -q '^recovery_reply_verified: true$' "${test_root}/peer.hldp" +grep -q '^recovery_selected_by_native: false$' "${test_root}/peer.hldp" +grep -q '^GHOS_NATIVE_RECOVERY_BEACON=WRITE_READ_VERIFIED' "${test_root}/serial.log" +grep -q '^GHOS_DISK_PROOF_OBSERVED_AFTER_RESET=LBA134' "${test_root}/serial.log" + +python3 - "${test_root}/disk.img" <<'PY' +import pathlib +import sys + +with pathlib.Path(sys.argv[1]).open("rb") as disk: + def sector(lba: int, count: int = 1) -> bytes: + disk.seek(lba * 512) + return disk.read(count * 512) + + proof = sector(134) + assert proof[0] == 0xA7 + assert proof[1:].startswith(b"GHOS_NATIVE_LONG64_DISK_PROOF\0") + assert proof[99:102] == bytes([1, 1, 1]) + assert proof[102:105] == bytes([1, 1, 1]) + assert proof[105] == 6 + assert sector(139, 2).startswith(b"# GRUB Environment Block\n") + assert sector(141).startswith(b"GHOS_GHCIP_INDEX_V1\n") +PY + +truncate -s 700M "${test_root}/wrong-capability-disk.img" +dd if="${test_root}/mbr.bin" of="${test_root}/wrong-capability-disk.img" \ + bs=512 seek=0 conv=notrunc status=none +dd if="${candidate}" of="${test_root}/wrong-capability-disk.img" \ + bs=512 seek=105 conv=notrunc status=none +wrong_peer_port=$((peer_port + 2)) +wrong_qemu_port=$((qemu_port + 2)) +python3 "${source_root}/scripts/qemu-native-net-peer.py" \ + --listen-port "${wrong_peer_port}" \ + --qemu-port "${wrong_qemu_port}" \ + --receipt "${test_root}/wrong-peer.hldp" \ + --final-resident \ + --recovery-token-file "${test_root}/wrong-token.hex" \ + >"${test_root}/wrong-peer.log" 2>&1 & +peer_pid=$! +set +e +timeout 5 qemu-system-x86_64 \ + -machine pc,accel=tcg \ + -m 64M \ + -drive "if=none,id=ghboot,format=raw,file=${test_root}/wrong-capability-disk.img" \ + -device virtio-blk-pci,drive=ghboot,disable-modern=on,bootindex=0 \ + -netdev "dgram,id=ghnet,local.type=inet,local.host=127.0.0.1,local.port=${wrong_qemu_port},remote.type=inet,remote.host=127.0.0.1,remote.port=${wrong_peer_port}" \ + -device virtio-net-pci,netdev=ghnet,disable-modern=on,mac=52:54:00:26:71:98 \ + -display none \ + -monitor none \ + -serial "file:${test_root}/wrong-capability-serial.log" \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 +wrong_qemu_status=$? +set -e +[[ ${wrong_qemu_status} -eq 124 ]] +wait "${peer_pid}" +peer_pid= +python3 - "${test_root}/wrong-capability-disk.img" <<'PY' +import pathlib +import sys + +with pathlib.Path(sys.argv[1]).open("rb") as disk: + disk.seek(134 * 512) + proof = disk.read(512) + disk.seek(139 * 512) + recovery = disk.read(1024) +assert proof[0] == 0xA7 +assert recovery == bytes(1024) +PY + +observed_at=$(date --iso-8601=seconds) +image_sha=$(sha256sum "${candidate}" | awk '{print $1}') +cat >"${receipt}" <