From b7c86420571ab4430801fd6f2c6d44848699f37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Mon, 3 Aug 2026 21:22:53 +0800 Subject: [PATCH] feat(native): add JD resident recovery gate Parameterize the raw recovery beacon, add one-time JD resident preparation and return verification, and provide an external ICMP peer for HLDP login, code, branch, and recovery commands. --- .../hololake-platform/guanghu-os/README.md | 6 + .../scripts/arm-jd-native-resident-once.sh | 33 +++++ .../scripts/clear-jd-native-probe-proof.sh | 5 +- .../scripts/clear-native-recovery-beacon.sh | 12 +- .../scripts/physical-native-icmp-peer.py | 115 ++++++++++++++++ .../prepare-jd-native-resident-once.sh | 129 ++++++++++++++++++ .../scripts/render-native-recovery-beacon.sh | 18 ++- .../test-native-recovery-beacon-contract.sh | 4 +- .../verify-jd-native-resident-return.sh | 96 +++++++++++++ 9 files changed, 403 insertions(+), 15 deletions(-) create mode 100755 product-source/hololake-platform/guanghu-os/scripts/arm-jd-native-resident-once.sh create mode 100755 product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py create mode 100755 product-source/hololake-platform/guanghu-os/scripts/prepare-jd-native-resident-once.sh create mode 100755 product-source/hololake-platform/guanghu-os/scripts/verify-jd-native-resident-return.sh diff --git a/product-source/hololake-platform/guanghu-os/README.md b/product-source/hololake-platform/guanghu-os/README.md index 0f04ec1..eb6b48b 100644 --- a/product-source/hololake-platform/guanghu-os/README.md +++ b/product-source/hololake-platform/guanghu-os/README.md @@ -120,6 +120,12 @@ queue and accepts the observed 4096-entry maximum. A failed probe remains a failure; its LBA 134 proof must be sealed and cleared with `clear-jd-native-probe-proof.sh` before an exact-hardware retry. +The exact-hardware retry passed with net RX/TX queues of 4096 and a block queue +of 128. The next binary gate is a one-time resident boot. Its JD-specific +prepare, arm, external ICMP peer, and return verifier must prove native network +login, code commit, branch movement, world and gestational storage, and the +`HLDP-RECOVER-OS!` return to Linux before any native default is selected. + ## Hosted Stage 1 installation `scripts/install-hosted-stage1.sh` is the repeatable Ubuntu construction diff --git a/product-source/hololake-platform/guanghu-os/scripts/arm-jd-native-resident-once.sh b/product-source/hololake-platform/guanghu-os/scripts/arm-jd-native-resident-once.sh new file mode 100755 index 0000000..7d34b8c --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/arm-jd-native-resident-once.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 2 ]] || { + echo "usage: arm-jd-native-resident-once.sh " >&2 + exit 64 +} +[[ ${EUID} -eq 0 ]] || exit 77 + +world_root=$(readlink -f "$1") +recovery_root=$(readlink -f "$2") +grep -q '^status: VERIFIED_NOT_ARMED$' \ + "${recovery_root}/PREPARE-RECEIPT.hldp" +/guanghu/bin/ghctl authorize "${world_root}" \ + reboot_and_recover_jd_fd_primary >/dev/null +grub-editenv /boot/grub/grubenv unset initrdfail prev_entry next_entry +grub-reboot guanghu-jd-native-once +grep -q '^next_entry=guanghu-jd-native-once$' \ + < <(grub-editenv /boot/grub/grubenv list) + +observed_at=$(date --iso-8601=seconds) +cat >"${recovery_root}/ARM-RECEIPT.hldp" </dev/null @@ -61,7 +62,7 @@ cleared_range: lba_start: 105 sector_count: 30 zero_readback_sha256: ${readback_sha} -reason: SEALED_FAIL_0_CONSUMED_BEFORE_EXACT_HARDWARE_ADAPTER_RETRY +reason: SEALED_BINARY_PROBE_RESULT_CONSUMED_BEFORE_NEXT_NATIVE_GATE EOF chmod 0400 "${recovery_root}/PROOF-CLEAR-RECEIPT.hldp" cat "${recovery_root}/PROOF-CLEAR-RECEIPT.hldp" diff --git a/product-source/hololake-platform/guanghu-os/scripts/clear-native-recovery-beacon.sh b/product-source/hololake-platform/guanghu-os/scripts/clear-native-recovery-beacon.sh index 38e698f..e66eb4f 100755 --- a/product-source/hololake-platform/guanghu-os/scripts/clear-native-recovery-beacon.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/clear-native-recovery-beacon.sh @@ -1,13 +1,15 @@ #!/usr/bin/env bash set -euo pipefail -[[ $# -eq 2 ]] || { - echo "usage: clear-native-recovery-beacon.sh " >&2 +[[ $# -ge 2 && $# -le 3 ]] || { + echo "usage: clear-native-recovery-beacon.sh [beacon-lba]" >&2 exit 64 } disk=$1 clear_template=$2 +beacon_lba=${3:-68} +[[ ${beacon_lba} =~ ^[0-9]+$ ]] [[ -f ${clear_template} ]] [[ $(stat -c %s "${clear_template}" 2>/dev/null || stat -f %z "${clear_template}") -eq 1024 ]] @@ -21,7 +23,7 @@ fi readback=$(mktemp) trap 'rm -f "${readback}"' EXIT -dd if="${disk}" of="${readback}" bs=512 skip=68 count=2 status=none +dd if="${disk}" of="${readback}" bs=512 skip="${beacon_lba}" count=2 status=none if cmp -s "${readback}" "${clear_template}"; then echo "GUANGHU_NATIVE_RECOVERY_BEACON_ALREADY_CLEAR" @@ -33,8 +35,8 @@ if ! grep -aFq 'guanghu_recovery=ubuntu' "${readback}"; then exit 65 fi -dd if="${clear_template}" of="${disk}" bs=512 seek=68 count=2 \ +dd if="${clear_template}" of="${disk}" bs=512 seek="${beacon_lba}" count=2 \ conv=notrunc,fsync status=none -dd if="${disk}" of="${readback}" bs=512 skip=68 count=2 status=none +dd if="${disk}" of="${readback}" bs=512 skip="${beacon_lba}" count=2 status=none cmp "${readback}" "${clear_template}" echo "GUANGHU_NATIVE_RECOVERY_BEACON_CLEARED" 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 new file mode 100755 index 0000000..4a92065 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/physical-native-icmp-peer.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +import argparse +import pathlib +import socket +import struct +import time + + +LOGIN_MAGIC = b"HLDP-GHOS-LOGIN!" +COMMIT_MAGIC = b"HLDP-CODE-COMMIT" +BRANCH_MAGIC = b"HLDP-BRANCH-MOVE" +RECOVERY_MAGIC = b"HLDP-RECOVER-OS!" + + +def checksum(payload: bytes) -> int: + if len(payload) % 2: + payload += b"\0" + words = struct.unpack(f"!{len(payload) // 2}H", payload) + total = sum(words) + while total >> 16: + total = (total & 0xFFFF) + (total >> 16) + return (~total) & 0xFFFF + + +def request(sequence: int, magic: bytes) -> bytes: + payload = b"\0" * 8 + magic + magic + packet = struct.pack("!BBHHH", 8, 0, 0, 0x4748, sequence) + payload + return packet[:2] + struct.pack("!H", checksum(packet)) + packet[4:] + + +def verify_reply(packet: bytes, magic: bytes) -> None: + if packet and packet[0] >> 4 == 4: + header_length = (packet[0] & 0x0F) * 4 + packet = packet[header_length:] + if len(packet) < 40 or packet[0] != 0 or packet[1] != 0: + raise RuntimeError("unexpected ICMP reply shape") + if packet[16:32] != magic: + raise RuntimeError("ICMP reply does not carry the requested HLDP magic") + if checksum(packet) != 0: + raise RuntimeError("ICMP reply checksum failed") + + +def exchange( + peer: socket.socket, + target: tuple[str, int], + sequence: int, + magic: bytes, + deadline: float, +) -> None: + while time.monotonic() < deadline: + peer.sendto(request(sequence, magic), target) + try: + packet, _ = peer.recvfrom(4096) + except TimeoutError: + continue + verify_reply(packet, magic) + return + raise TimeoutError(f"no verified reply for sequence {sequence}") + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--target", required=True) + parser.add_argument("--receipt", required=True) + parser.add_argument("--timeout", type=float, default=90.0) + parser.add_argument("--resident", action="store_true") + parser.add_argument("--login-only", action="store_true") + args = parser.parse_args() + if args.resident and args.login_only: + raise SystemExit("--resident and --login-only are mutually exclusive") + + peer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP) + peer.settimeout(1.0) + target = (socket.gethostbyname(args.target), 0) + deadline = time.monotonic() + args.timeout + sequence = 1 + phases: list[tuple[str, bytes]] = [ + ("login", LOGIN_MAGIC), + ("login", LOGIN_MAGIC), + ("login", LOGIN_MAGIC), + ] + if not args.login_only: + phases.extend( + [ + ("code_commit", COMMIT_MAGIC), + ("branch_move", BRANCH_MAGIC), + ] + ) + if args.resident: + phases.extend([("resident_login", LOGIN_MAGIC)] * 10) + phases.append(("recovery", RECOVERY_MAGIC)) + + completed: list[str] = [] + for phase, magic in phases: + exchange(peer, target, sequence, magic, deadline) + completed.append(phase) + sequence += 1 + + receipt = pathlib.Path(args.receipt) + receipt.write_text( + "schema: guanghu.physical-native-icmp-peer/v1\n" + "status: PASS_100\n" + f"target: {args.target}\n" + f"login_reply_count: {completed.count('login')}\n" + f"code_commit_reply_verified: {str('code_commit' in completed).lower()}\n" + f"branch_move_reply_verified: {str('branch_move' in completed).lower()}\n" + f"resident_login_reply_count: {completed.count('resident_login')}\n" + f"recovery_reply_verified: {str('recovery' in completed).lower()}\n", + encoding="utf-8", + ) + print(receipt.read_text(encoding="utf-8"), end="") + + +if __name__ == "__main__": + main() diff --git a/product-source/hololake-platform/guanghu-os/scripts/prepare-jd-native-resident-once.sh b/product-source/hololake-platform/guanghu-os/scripts/prepare-jd-native-resident-once.sh new file mode 100755 index 0000000..340f306 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/prepare-jd-native-resident-once.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ $# -eq 5 ]] || { + echo "usage: prepare-jd-native-resident-once.sh " >&2 + exit 64 +} +[[ ${EUID} -eq 0 ]] || exit 77 + +world_root=$(readlink -f "$1") +candidate=$(readlink -f "$2") +recovery_root=$(readlink -m "$3") +expected_candidate_sha=$4 +probe_recovery_root=$(readlink -f "$5") +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +render_root=$(mktemp -d) +trap 'rm -rf "${render_root}"' EXIT + +grep -q '^node_id: JD-FD-PRIMARY$' "${world_root}/CURRENT.hldp" +grep -q '^status: PASS_100$' "${probe_recovery_root}/RETURN-RECEIPT.hldp" +/guanghu/bin/ghctl authorize "${world_root}" \ + write_bootloader_and_system_partitions >/dev/null +[[ ${expected_candidate_sha} =~ ^[0-9a-f]{64}$ ]] +[[ $(stat -c %s "${candidate}") -eq 14848 ]] +[[ $(sha256sum "${candidate}" | awk '{print $1}') == \ + "${expected_candidate_sha}" ]] +[[ ! -e ${recovery_root} ]] + +partition_dump=$(sfdisk -d /dev/vda) +grep -q '^label: dos$' <<<"${partition_dump}" +grep -q '^label-id: 0xf4bb72a5$' <<<"${partition_dump}" +grep -Eq '^/dev/vda1 : start= *2048, size= *209713119, type=83$' \ + <<<"${partition_dump}" +[[ $(dd if=/dev/vda bs=512 count=105 status=none | sha256sum | + awk '{print $1}') == \ + 32291877e37647ee5b778f968676cc69b59fb5b07604366a21cbfd8e4a4b87a8 ]] +[[ $(dd if=/dev/vda bs=512 skip=105 count=38 status=none | sha256sum | + awk '{print $1}') == \ + c597b1b463b7cec49548fac00d7588c9652bb67cc2b1b0c88676bf8f1558571e ]] + +mkdir -m 0700 "${recovery_root}" +printf '%s\n' "${partition_dump}" >"${recovery_root}/sfdisk-before.txt" +dd if=/dev/vda of="${recovery_root}/first-2MiB.before.bin" \ + bs=1M count=2 status=none +disk_sectors=$(blockdev --getsz /dev/vda) +dd if=/dev/vda of="${recovery_root}/last-2MiB.before.bin" \ + bs=512 skip=$((disk_sectors - 4096)) count=4096 status=none +cp /boot/grub/grub.cfg "${recovery_root}/grub.cfg.before" +cp /boot/grub/grubenv "${recovery_root}/grubenv.before" +cp /etc/default/grub "${recovery_root}/default-grub.before" +cp /etc/grub.d/40_custom "${recovery_root}/40_custom.before" +cp "${candidate}" "${recovery_root}/jd-native-resident.img" +cat /proc/sys/kernel/random/boot_id >"${recovery_root}/linux-boot-id.before" + +"${source_root}/scripts/render-native-recovery-beacon.sh" "${render_root}" \ + 139 gnulinux-simple-9e4550a0-452b-4f28-b5a5-d5364aa450f6 +install -m 0755 "${render_root}/08_guanghu_native_recovery" \ + /etc/grub.d/08_guanghu_native_recovery +install -m 0755 /dev/stdin /etc/grub.d/41_guanghu_jd_native_once <<'EOF' +#!/bin/sh +exec tail -n +3 $0 +menuentry 'Guanghu OS JD native one-time resident' --id 'guanghu-jd-native-once' { + insmod chain + chainloader (hd0)105+1 +} +EOF +install -D -m 0644 "${render_root}/guanghu-recovery-clear.env" \ + /usr/lib/guanghu-os/jd-recovery-clear.env +install -D -m 0755 "${source_root}/scripts/clear-native-recovery-beacon.sh" \ + /usr/local/sbin/guanghu-clear-native-recovery-beacon +install -m 0644 /dev/stdin \ + /etc/systemd/system/guanghu-jd-native-recovery-clear.service <<'EOF' +[Unit] +Description=Verify and clear the JD Guanghu native recovery beacon +After=local-fs.target +Before=multi-user.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/guanghu-clear-native-recovery-beacon /dev/vda /usr/lib/guanghu-os/jd-recovery-clear.env 139 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable guanghu-jd-native-recovery-clear.service >/dev/null +dd if="${render_root}/guanghu-recovery-clear.env" of=/dev/vda \ + bs=512 seek=139 count=2 conv=notrunc,fsync status=none +dd if="${candidate}" of=/dev/vda bs=512 seek=105 count=29 \ + conv=notrunc,fsync status=none +update-grub >/dev/null +grub-script-check /boot/grub/grub.cfg +grep -q "load_env --file '(hd0)139+2' guanghu_recovery" \ + /boot/grub/grub.cfg +grep -q "menuentry 'Guanghu OS JD native one-time resident'.*'guanghu-jd-native-once'" \ + /boot/grub/grub.cfg +[[ $(dd if=/dev/vda bs=512 skip=105 count=29 status=none | sha256sum | + awk '{print $1}') == "${expected_candidate_sha}" ]] +[[ $(dd if=/dev/vda bs=512 skip=139 count=2 status=none | sha256sum | + awk '{print $1}') == \ + $(sha256sum "${render_root}/guanghu-recovery-clear.env" | awk '{print $1}') ]] + +observed_at=$(date --iso-8601=seconds) +cat >"${recovery_root}/PREPARE-RECEIPT.hldp" <" >&2 +[[ $# -ge 1 && $# -le 3 ]] || { + echo "usage: render-native-recovery-beacon.sh [beacon-lba] [hosted-entry-id]" >&2 exit 64 } mkdir -p "$1" output_root=$(cd "$1" && pwd) +beacon_lba=${2:-68} +hosted_entry_id=${3:-gnulinux-simple-9842d3d6-a839-4127-bda7-f19137effe71} +[[ ${beacon_lba} =~ ^[0-9]+$ ]] +[[ ${hosted_entry_id} =~ ^[A-Za-z0-9._-]+$ ]] python3 - "${output_root}" <<'PY' import pathlib @@ -32,14 +36,14 @@ write_environment( write_environment("guanghu-recovery-clear.env") PY -install -m 0755 /dev/stdin "${output_root}/08_guanghu_native_recovery" <<'EOF' +install -m 0755 /dev/stdin "${output_root}/08_guanghu_native_recovery" < " >&2 + exit 64 +} +[[ ${EUID} -eq 0 ]] || exit 77 + +world_root=$(readlink -f "$1") +recovery_root=$(readlink -f "$2") +grep -q '^status: ARMED_FOR_ONE_BOOT$' "${recovery_root}/ARM-RECEIPT.hldp" +/guanghu/bin/ghctl authorize "${world_root}" run_tests_and_health_checks >/dev/null +before_boot_id=$(<"${recovery_root}/linux-boot-id.before") +after_boot_id=$("${recovery_root}/RETURN-RECEIPT.hldp" <