From fb813990b4f8df585eae504a3ff956486c478dde Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 2 Nov 2026 17:57:45 +0100 Subject: [PATCH 1/3] real-hw-test: use a fixed CPU model for aarch64 under TCG The aarch64 QEMU smoke test has failed since ubuntu-latest moved to Ubuntu 26.04. Its AAVMF 2025.11 firmware hangs after printing its banner when the guest uses -cpu max, before it reaches BDS. The same firmware boots with named models such as cortex-a57 or neoverse-n1, and AAVMF 2024.02 from Ubuntu 24.04 boots with either. TCG now uses neoverse-n1, which also keeps the emulated CPU the same across QEMU versions. KVM keeps max because it needs the host CPU. --- real-hw-test/scripts/qemu-machine.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/real-hw-test/scripts/qemu-machine.sh b/real-hw-test/scripts/qemu-machine.sh index 56bbd22..b1daf7a 100644 --- a/real-hw-test/scripts/qemu-machine.sh +++ b/real-hw-test/scripts/qemu-machine.sh @@ -43,8 +43,14 @@ qemu_machine_setup() { # Writable per-run variable store; the template may be read-only on disk. cp "$aavmf_vars" "$vars_file" chmod u+w "$vars_file" + # Under TCG, a fixed CPU model instead of max: AAVMF 2025.11 (Ubuntu + # 26.04) hangs before BDS with QEMU 10.2's max. KVM needs max (host). + local cpu=max + if [[ "$accel" == tcg ]]; then + cpu=neoverse-n1 + fi # virt has no built-in keyboard; the UEFI prompts need one. - machine_args=(-machine "virt,accel=$accel" -cpu max -m 512M + machine_args=(-machine "virt,accel=$accel" -cpu "$cpu" -m 512M -drive "if=pflash,format=raw,file=$aavmf_code,readonly=on" -drive "if=pflash,format=raw,file=$vars_file" -device qemu-xhci -device usb-kbd) From e7539d6bd50c6e7f180844154957fe9534b3f411 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 2 Nov 2026 18:05:05 +0100 Subject: [PATCH 2/3] real-hw-test: log QEMU and firmware versions in CI The QEMU and firmware packages come from the CI runner image, which GitHub upgrades without a change in this repository. The aarch64 smoke test broke this way when ubuntu-latest moved to Ubuntu 26.04, and the versions were only visible in the apt output. --- .github/workflows/real-hw-test.yml | 8 ++++++-- real-hw-test/scripts/run-qemu-ci.sh | 26 ++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/real-hw-test.yml b/.github/workflows/real-hw-test.yml index 9c5ee7d..f1e2cbb 100644 --- a/.github/workflows/real-hw-test.yml +++ b/.github/workflows/real-hw-test.yml @@ -38,11 +38,15 @@ jobs: - name: Install QEMU, firmware, and harness tools run: | sudo apt-get update + packages=(dosfstools mtools socat) if [[ "${{ matrix.arch }}" == x86_64 ]]; then - sudo apt-get install -y dosfstools mtools ovmf qemu-system-x86 socat + packages+=(ovmf qemu-system-x86) else - sudo apt-get install -y dosfstools mtools qemu-efi-aarch64 qemu-system-arm socat + packages+=(qemu-efi-aarch64 qemu-system-arm) fi + sudo apt-get install -y "${packages[@]}" + # QEMU and firmware versions follow the runner image. + dpkg-query -W "${packages[@]}" - name: Run deterministic UEFI checks working-directory: real-hw-test run: | diff --git a/real-hw-test/scripts/run-qemu-ci.sh b/real-hw-test/scripts/run-qemu-ci.sh index 9865f18..d09f0a2 100755 --- a/real-hw-test/scripts/run-qemu-ci.sh +++ b/real-hw-test/scripts/run-qemu-ci.sh @@ -47,16 +47,22 @@ mkfs.vfat "$disk" >/dev/null mmd -i "$disk" ::/EFI ::/EFI/BOOT mcopy -i "$disk" "$artifact" "::/EFI/BOOT/$boot_name" -"$qemu" \ - "${machine_args[@]}" \ - -drive "format=raw,file=$disk" \ - -nic none \ - -display none \ - -monitor "unix:$monitor,server,nowait" \ - -serial "file:$console_log" \ - -chardev "file,id=pci_serial,path=$pci_log" \ - -device pci-serial,chardev=pci_serial \ - -no-reboot & +qemu_args=( + "${machine_args[@]}" + -drive "format=raw,file=$disk" + -nic none + -display none + -monitor "unix:$monitor,server,nowait" + -serial "file:$console_log" + -chardev "file,id=pci_serial,path=$pci_log" + -device "pci-serial,chardev=pci_serial" + -no-reboot +) +# Host QEMU and firmware change with the CI runner image; log what this run +# actually used. +"$qemu" --version | head -n1 +echo "$qemu ${qemu_args[*]}" +"$qemu" "${qemu_args[@]}" & qemu_pid=$! trap 'kill -9 "$qemu_pid" 2>/dev/null || true' EXIT deadline=$(($(date +%s) + timeout_s)) From 311805988b2b36fc0d3ef392ce3bcaab64d7deed Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 2 Nov 2026 18:05:05 +0100 Subject: [PATCH 3/3] real-hw-test: report console progress in the CI harness A stuck run was silent until the timeout of 300 s, and the logs only showed up afterwards. The harness now prints the last console line every 5 s, so the log shows right away whether the firmware or the application is stuck. --- real-hw-test/scripts/run-qemu-ci.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/real-hw-test/scripts/run-qemu-ci.sh b/real-hw-test/scripts/run-qemu-ci.sh index d09f0a2..98f70c0 100755 --- a/real-hw-test/scripts/run-qemu-ci.sh +++ b/real-hw-test/scripts/run-qemu-ci.sh @@ -65,7 +65,8 @@ echo "$qemu ${qemu_args[*]}" "$qemu" "${qemu_args[@]}" & qemu_pid=$! trap 'kill -9 "$qemu_pid" 2>/dev/null || true' EXIT -deadline=$(($(date +%s) + timeout_s)) +start=$(date +%s) +deadline=$((start + timeout_s)) monitor_cmd() { printf '%s\n' "$1" | socat -t 1 - "UNIX-CONNECT:$monitor" >/dev/null 2>&1 \ @@ -76,6 +77,22 @@ qemu_alive() { kill -0 "$qemu_pid" 2>/dev/null } +# A periodic snapshot of the console shows where a stuck run stopped, e.g., +# in firmware before the application started, without waiting for the +# timeout to dump the logs. +progress_interval_s=5 +last_progress=$start +report_progress() { + local now last_line + now=$(date +%s) + ((now - last_progress >= progress_interval_s)) || return 0 + last_progress=$now + # Strip CSI sequences and carriage returns of the firmware console. + last_line=$(sed -E 's/\x1b\[[0-9;=?]*[A-Za-z]//g; s/\r//g' "$console_log" \ + 2>/dev/null | grep -v '^[[:space:]]*$' | tail -n1 || true) + echo "[$((now - start))s] console: ${last_line:-}" +} + # Mid-run extraction from the live FAT image is best-effort progress polling; # only the extraction after QEMU quit is authoritative. extract_persisted_log() { @@ -115,6 +132,7 @@ until extract_persisted_log | grep -qF 'then press Enter.'; do if (($(date +%s) >= deadline)); then fail_run "timeout waiting for the first operator prompt" fi + report_progress sleep 2 done @@ -128,6 +146,7 @@ while (($(date +%s) < deadline)); do # A queued Enter can finish the final prompt early; the persisted log then # already contains every line the assertions below need. qemu_alive || break + report_progress monitor_cmd 'sendkey ret' sleep 1 monitor_cmd 'sendkey esc'