From ee24927de301002db00b00beae1508cdd30bd3f5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 27 Feb 2026 10:24:36 +0100 Subject: [PATCH] Fix QEMU test flakiness on ARM64 macOS On ARM64 macOS, qemu-system-x86_64 uses TCG (software emulation) instead of KVM. The isa-debug-exit shutdown request is processed asynchronously, so the guest vCPU can continue executing after the first port write. This lets the fallthrough `exit_qemu(Failed)` in `_start` overwrite the success exit code before QEMU actually shuts down, causing intermittent test failures with exit code 35 instead of 33. Add `loop {}` after each `exit_qemu(Success)` in test runners so the vCPU spins harmlessly until the shutdown completes. Co-Authored-By: Claude Opus 4.6 --- example-kernels/runner-doctest/src/lib.rs | 7 +++++++ example-kernels/runner-fail-reboot/src/lib.rs | 3 +++ example-kernels/runner-test/src/lib.rs | 3 +++ 3 files changed, 13 insertions(+) diff --git a/example-kernels/runner-doctest/src/lib.rs b/example-kernels/runner-doctest/src/lib.rs index 437f374..66c8d88 100644 --- a/example-kernels/runner-doctest/src/lib.rs +++ b/example-kernels/runner-doctest/src/lib.rs @@ -16,6 +16,8 @@ extern crate rlibc; /// extern "C" fn start() { /// assert_eq!(add(1, 2), 3); /// unsafe { exit_qemu(ExitCode::Success); } +/// // Spin until QEMU shuts down to avoid overwriting the exit code (TCG race on ARM64). +/// loop {} /// } /// ``` pub fn add(a: u32, b: u32) -> u32 { @@ -32,6 +34,8 @@ pub fn add(a: u32, b: u32) -> u32 { /// extern "C" fn start() { /// assert_eq!(mul(2, 3), 6); /// unsafe { exit_qemu(ExitCode::Success); } +/// // Spin until QEMU shuts down to avoid overwriting the exit code (TCG race on ARM64). +/// loop {} /// } /// ``` pub fn mul(a: u32, b: u32) -> u32 { @@ -47,6 +51,9 @@ fn test_runner(tests: &[&dyn Fn()]) { unsafe { exit_qemu(ExitCode::Success); } + // Keep spinning so that the fallthrough `exit_qemu(Failed)` in `_start` + // cannot overwrite the exit code before QEMU shuts down (TCG race on ARM64). + loop {} } pub enum ExitCode { diff --git a/example-kernels/runner-fail-reboot/src/lib.rs b/example-kernels/runner-fail-reboot/src/lib.rs index 3472c56..f2f8c72 100644 --- a/example-kernels/runner-fail-reboot/src/lib.rs +++ b/example-kernels/runner-fail-reboot/src/lib.rs @@ -14,6 +14,9 @@ pub fn test_runner(tests: &[&dyn Fn()]) { unsafe { exit_qemu(ExitCode::Success); } + // Keep spinning so that the fallthrough `exit_qemu(Failed)` in `_start` + // cannot overwrite the exit code before QEMU shuts down (TCG race on ARM64). + loop {} } #[test_case] diff --git a/example-kernels/runner-test/src/lib.rs b/example-kernels/runner-test/src/lib.rs index 740e7a9..4e9614a 100644 --- a/example-kernels/runner-test/src/lib.rs +++ b/example-kernels/runner-test/src/lib.rs @@ -13,6 +13,9 @@ pub fn test_runner(tests: &[&dyn Fn()]) { } unsafe { exit_qemu(ExitCode::Success); } + // Keep spinning so that the fallthrough `exit_qemu(Failed)` in `_start` + // cannot overwrite the exit code before QEMU shuts down (TCG race on ARM64). + loop {} } #[test_case]