hololake-system-architecture/product-source/hololake-platform/guanghu-os/scripts/test-native-recovery-beacon-grub-qemu.sh

189 lines
5.3 KiB
Shell
Raw Normal View History

#!/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}"