189 lines
5.3 KiB
Shell
189 lines
5.3 KiB
Shell
|
|
#!/usr/bin/env bash
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
[[ $# -eq 1 ]] || {
|
||
|
|
echo "usage: test-native-recovery-beacon-grub-qemu.sh <receipt-output>" >&2
|
||
|
|
exit 64
|
||
|
|
}
|
||
|
|
[[ ${EUID} -eq 0 ]] || {
|
||
|
|
echo "the GRUB/QEMU recovery gate requires root for a disposable loop disk" >&2
|
||
|
|
exit 77
|
||
|
|
}
|
||
|
|
|
||
|
|
source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||
|
|
receipt=$(readlink -m "$1")
|
||
|
|
test_root=$(mktemp -d)
|
||
|
|
disk=${test_root}/grub-recovery.img
|
||
|
|
mount_root=${test_root}/mnt
|
||
|
|
loop_device=
|
||
|
|
|
||
|
|
cleanup() {
|
||
|
|
if mountpoint -q "${mount_root}"; then
|
||
|
|
umount "${mount_root}"
|
||
|
|
fi
|
||
|
|
if [[ -n ${loop_device} ]]; then
|
||
|
|
losetup -d "${loop_device}"
|
||
|
|
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
|
||
|
|
|
||
|
|
for command_name in grub-install losetup mkfs.ext4 mount mountpoint nasm \
|
||
|
|
parted qemu-system-x86_64 timeout udevadm; do
|
||
|
|
command -v "${command_name}" >/dev/null
|
||
|
|
done
|
||
|
|
|
||
|
|
"${source_root}/scripts/render-native-recovery-beacon.sh" "${test_root}/beacon"
|
||
|
|
truncate -s 128M "${disk}"
|
||
|
|
parted -s "${disk}" mklabel gpt
|
||
|
|
parted -s "${disk}" mkpart bios_grub 1MiB 2MiB
|
||
|
|
parted -s "${disk}" set 1 bios_grub on
|
||
|
|
parted -s "${disk}" mkpart root ext4 2MiB 100%
|
||
|
|
|
||
|
|
loop_device=$(losetup --find --show --partscan "${disk}")
|
||
|
|
root_partition=${loop_device}p2
|
||
|
|
udevadm settle --timeout=10
|
||
|
|
for _ in {1..50}; do
|
||
|
|
[[ -b ${root_partition} ]] && break
|
||
|
|
sleep 0.1
|
||
|
|
done
|
||
|
|
[[ -b ${root_partition} ]]
|
||
|
|
mkfs.ext4 -q -F "${root_partition}"
|
||
|
|
mkdir -p "${mount_root}"
|
||
|
|
mount "${root_partition}" "${mount_root}"
|
||
|
|
grub-install \
|
||
|
|
--target=i386-pc \
|
||
|
|
--boot-directory="${mount_root}/boot" \
|
||
|
|
--no-floppy \
|
||
|
|
--recheck \
|
||
|
|
"${loop_device}" >/dev/null
|
||
|
|
|
||
|
|
install -m 0644 /dev/stdin "${mount_root}/boot/grub/grub.cfg" <<'EOF'
|
||
|
|
serial --unit=0 --speed=115200
|
||
|
|
terminal_input serial
|
||
|
|
terminal_output serial
|
||
|
|
set timeout=0
|
||
|
|
set timeout_style=hidden
|
||
|
|
set default="guanghu-native-once"
|
||
|
|
insmod loadenv
|
||
|
|
set guanghu_recovery=
|
||
|
|
if load_env --file '(hd0)68+2' guanghu_recovery; then
|
||
|
|
if [ "${guanghu_recovery}" = "ubuntu" ]; then
|
||
|
|
set default="ubuntu-recovery-test"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
menuentry 'Guanghu native default test' --id 'guanghu-native-once' {
|
||
|
|
insmod chain
|
||
|
|
chainloader (hd0)80+1
|
||
|
|
boot
|
||
|
|
}
|
||
|
|
menuentry 'Ubuntu recovery test' --id 'ubuntu-recovery-test' {
|
||
|
|
insmod chain
|
||
|
|
chainloader (hd0)81+1
|
||
|
|
boot
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
|
||
|
|
install -m 0644 /dev/stdin "${test_root}/exit-sector.asm" <<'EOF'
|
||
|
|
bits 16
|
||
|
|
org 0x7c00
|
||
|
|
mov dx, 0xf4
|
||
|
|
mov al, EXIT_VALUE
|
||
|
|
out dx, al
|
||
|
|
cli
|
||
|
|
hlt
|
||
|
|
times 510 - ($ - $$) db 0
|
||
|
|
dw 0xaa55
|
||
|
|
EOF
|
||
|
|
nasm -f bin -DEXIT_VALUE=0x10 "${test_root}/exit-sector.asm" \
|
||
|
|
-o "${test_root}/native-exit.bin"
|
||
|
|
nasm -f bin -DEXIT_VALUE=0x11 "${test_root}/exit-sector.asm" \
|
||
|
|
-o "${test_root}/ubuntu-exit.bin"
|
||
|
|
dd if="${test_root}/native-exit.bin" of="${loop_device}" \
|
||
|
|
bs=512 seek=80 count=1 conv=notrunc status=none
|
||
|
|
dd if="${test_root}/ubuntu-exit.bin" of="${loop_device}" \
|
||
|
|
bs=512 seek=81 count=1 conv=notrunc status=none
|
||
|
|
|
||
|
|
sync
|
||
|
|
umount "${mount_root}"
|
||
|
|
losetup -d "${loop_device}"
|
||
|
|
loop_device=
|
||
|
|
|
||
|
|
run_qemu() {
|
||
|
|
local stage=$1
|
||
|
|
local expected_status=$2
|
||
|
|
set +e
|
||
|
|
timeout 20 qemu-system-x86_64 \
|
||
|
|
-machine pc,accel=tcg \
|
||
|
|
-m 64M \
|
||
|
|
-drive "format=raw,file=${disk}" \
|
||
|
|
-display none \
|
||
|
|
-monitor none \
|
||
|
|
-serial null \
|
||
|
|
-no-reboot \
|
||
|
|
-device isa-debug-exit,iobase=0xf4,iosize=0x04
|
||
|
|
local observed_status=$?
|
||
|
|
set -e
|
||
|
|
printf 'GHNRP_QEMU_STAGE=%s EXPECTED=%s OBSERVED=%s\n' \
|
||
|
|
"${stage}" "${expected_status}" "${observed_status}"
|
||
|
|
[[ ${observed_status} -eq ${expected_status} ]]
|
||
|
|
}
|
||
|
|
|
||
|
|
dd if="${test_root}/beacon/guanghu-recovery-clear.env" of="${disk}" \
|
||
|
|
bs=512 seek=68 count=2 conv=notrunc,fsync status=none
|
||
|
|
run_qemu clear_selects_native 33
|
||
|
|
|
||
|
|
dd if="${test_root}/beacon/guanghu-recovery-active.env" of="${disk}" \
|
||
|
|
bs=512 seek=68 count=2 conv=notrunc,fsync status=none
|
||
|
|
run_qemu active_selects_hosted_recovery 35
|
||
|
|
|
||
|
|
"${source_root}/scripts/clear-native-recovery-beacon.sh" \
|
||
|
|
"${disk}" \
|
||
|
|
"${test_root}/beacon/guanghu-recovery-clear.env" >/dev/null
|
||
|
|
dd if="${disk}" of="${test_root}/beacon.after-ubuntu.env" \
|
||
|
|
bs=512 skip=68 count=2 status=none
|
||
|
|
if grep -aFq 'guanghu_recovery=ubuntu' \
|
||
|
|
"${test_root}/beacon.after-ubuntu.env"; then
|
||
|
|
echo "hosted recovery did not consume the Guanghu recovery beacon" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
cmp \
|
||
|
|
"${test_root}/beacon/guanghu-recovery-clear.env" \
|
||
|
|
"${test_root}/beacon.after-ubuntu.env"
|
||
|
|
run_qemu consumed_selects_native_again 33
|
||
|
|
|
||
|
|
mkdir -p "$(dirname "${receipt}")"
|
||
|
|
observed_at=$(date --iso-8601=seconds)
|
||
|
|
active_sha=$(sha256sum \
|
||
|
|
"${test_root}/beacon/guanghu-recovery-active.env" | awk '{print $1}')
|
||
|
|
consumed_sha=$(sha256sum \
|
||
|
|
"${test_root}/beacon.after-ubuntu.env" | awk '{print $1}')
|
||
|
|
cat >"${receipt}" <<EOF
|
||
|
|
schema: guanghu.native-recovery-beacon-grub-qemu/v1
|
||
|
|
receipt_id: GH-OS-LAB-001-NATIVE-RECOVERY-BEACON-GRUB-QEMU-001
|
||
|
|
status: VERIFIED
|
||
|
|
observed_at: ${observed_at}
|
||
|
|
beacon:
|
||
|
|
disk_lba_start: 68
|
||
|
|
sector_count: 2
|
||
|
|
active_sha256: ${active_sha}
|
||
|
|
grub:
|
||
|
|
raw_blocklist: (hd0)68+2
|
||
|
|
whitelisted_variable: guanghu_recovery
|
||
|
|
cleared_boot_selects_native: true
|
||
|
|
active_boot_selects_ubuntu: true
|
||
|
|
active_marker_consumed_by_grub: false
|
||
|
|
hosted_recovery:
|
||
|
|
active_marker_consumed_on_boot: true
|
||
|
|
post_consumption_sha256: ${consumed_sha}
|
||
|
|
acceptance:
|
||
|
|
independent_of_linux_filesystem_extent: true
|
||
|
|
qemu_permanent_default_recovery_cycle: true
|
||
|
|
physical_permanent_default_recovery_cycle: false
|
||
|
|
next_action: INSTALL_ON_BS_SH_005_AND_RUN_PHYSICAL_DEFAULT_RECOVERY_GATE
|
||
|
|
EOF
|
||
|
|
cat "${receipt}"
|