tests: add USB UHCI/OHCI/EHCI-autospawn/error-recovery QEMU proofs
Create the four USB validation scripts that local/docs/USB-VALIDATION-RUNBOOK.md references but which did not exist on disk, closing the P8-B runtime-proof gap for the legacy host controllers and the P8-C error-injection gap for xHCI: - test-uhci-runtime-qemu.sh (P1-B): -machine pc + piix3-usb-uhci, serial-log proof that uhcid binds the controller and detects the attached usb-kbd. - test-ohci-runtime-qemu.sh (P1-B): -machine pc + pci-ohci, serial-log proof that ohcid binds the controller and detects the attached usb-kbd. - test-ehci-class-autospawn-qemu.sh (P1-A): -machine q35 + usb-ehci + usb-kbd, serial-log proof that ehcid enumerates the keyboard and usbhidd auto-spawns via the unified UsbHostController trait. The existing test-ehci-qemu.sh is a coarse usb-tablet smoke test and does NOT cover this path, so this is a full script rather than an alias. - test-usb-error-recovery-qemu.sh (P8-C): hot-unplug usb-storage mid-transfer via the QEMU monitor device_del (chardev stdio mux, Ctrl-A c toggle — the same pattern as test-xhci-device-lifecycle-qemu.sh, since qemu-login-expect.py drives only serial and cannot reach the monitor). Proves graceful detach + usbscsid IO-error handling with no panic. test-xhci-device-lifecycle-qemu.sh already existed and is unchanged. test-usb-uas-qemu.sh is intentionally NOT created: UAS is in flight in a separate workstream and its test belongs to that change. Proof style and harness fidelity: - UHCI/OHCI/EHCI proofs follow the test-xhci-irq-qemu.sh serial-log grep pattern (dual --check + interactive mode, ISO-preferred boot_args, 180s timeout, no panic fail-marker). No guest-side checker exists that validates legacy-controller enumeration specifically, so per the runbook's serial-log-evidence rule they grep driver log lines instead of inventing a guest binary. - The error-recovery proof follows the test-xhci-device-lifecycle-qemu.sh expect/Tcl monitor-mux pattern and reuses the existing redbear-usb-storage-check guest binary (redbear-hwutils) to drive an active transfer — no new guest binary is invented. All proof markers are sourced directly from the real driver trees: uhcid main.rs:487/535/545 (UHCI USB 1.1 at / controller initialized / connect) ohcid main.rs:305/341/348 (OHCI USB 1.1 at / controller initialized / connect) ehcid main.rs:154/294/560 (EHCI USB 2.0 at / controller initialized / port device) usbhidd main.rs:221 (USB HID driver spawned with scheme) usbscsid main.rs:190/200/163/156 (READ/WRITE IO ERROR / scheme tick / event error) No existing scripts, qemu-login-expect.py, config/*.toml, recipes, or the runbook were modified. The runbook's script names and --check invocations already matched exactly, so no runbook edits were required. Validation: bash -n passes on all four scripts. shellcheck is not installed on this host. NO QEMU runs were performed — the host is contended by another workload that kills QEMU processes, so validation is syntax check + pattern fidelity review only. Runtime pass/fail is unverified.
This commit is contained in:
+236
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch or validate the EHCI class-driver autospawn path (P1-A) in QEMU.
|
||||
#
|
||||
# Topology under test:
|
||||
# -machine q35 → -device usb-ehci,id=ehci
|
||||
# └─ bus=ehci.0 → usb-kbd
|
||||
#
|
||||
# Validates the P1-A claim that a USB keyboard on the EHCI route reaches the
|
||||
# input stack via the unified `UsbHostController` trait:
|
||||
# 1. pcid auto-spawns ehcid for the EHCI PCI device (class 0c0320, see
|
||||
# local/config/drivers.d/20-usb.toml).
|
||||
# 2. ehcid maps MMIO, initializes ports, and enumerates the usb-kbd.
|
||||
# 3. ehcid calls usb_core::spawn::spawn_class_driver_for_port() (ehcid
|
||||
# main.rs:576), which selects the HID driver for class 0x03 (usb-core
|
||||
# spawn.rs:18) and spawns `/usr/bin/usbhidd`.
|
||||
# 4. usbhidd starts and registers itself as an `inputd::ProducerHandle`
|
||||
# holder (usbhidd main.rs:4), wiring the keyboard into the input stack.
|
||||
#
|
||||
# This is the autospawn half of the P1-A proof ("all four controllers
|
||||
# auto-spawn via the unified trait; class daemons can connect to all four via
|
||||
# the same scheme"). The existing local/scripts/test-ehci-qemu.sh is a coarse
|
||||
# 17-line smoke test (usb-tablet, grep "ehci|EHCI") that does NOT exercise the
|
||||
# keyboard-autospawn path; this script is the rigorous P1-A proof and does not
|
||||
# duplicate that smoke test's logic.
|
||||
#
|
||||
# Evidence is read from the serial log. Proof markers come straight from the
|
||||
# driver sources:
|
||||
# "EHCI USB 2.0 at <pci_path>" (ehcid main.rs:154)
|
||||
# "ehcid: controller initialized, <n> ports, ..." (ehcid main.rs:294)
|
||||
# "ehcid: port <n> device <vid>:<pid> address <a>" (ehcid main.rs:560)
|
||||
# "USB HID driver spawned with scheme `...`..." (usbhidd main.rs:221)
|
||||
#
|
||||
# Note on "reaches inputd": usbhidd is an inputd producer by construction
|
||||
# (`use inputd::ProducerHandle`). There is no guest-side checker that validates
|
||||
# an inputd keypress event for a USB keyboard, so the autospawn-to-usbhidd
|
||||
# marker is the strongest serial-log evidence available; a dedicated
|
||||
# keypress-to-inputd proof would require an inputd guest checker that does not
|
||||
# exist yet (serial-log-evidence rule per the USB validation runbook).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
find_uefi_firmware() {
|
||||
local candidates=(
|
||||
"/usr/share/ovmf/x64/OVMF.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF.4m.fd"
|
||||
"/usr/share/ovmf/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/ovmf/OVMF.fd"
|
||||
"/usr/share/OVMF/OVMF_CODE.fd"
|
||||
"/usr/share/qemu/edk2-x86_64-code.fd"
|
||||
)
|
||||
local path
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
printf '%s\n' "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: test-ehci-class-autospawn-qemu.sh [--check] [config]
|
||||
|
||||
Boot or validate the EHCI USB-keyboard class-driver autospawn path (P1-A) on a
|
||||
Red Bear image in QEMU. Uses `-machine q35` + `usb-ehci` + `usb-kbd`, matching
|
||||
the controller selection of the existing test-ehci-qemu.sh smoke test but
|
||||
adding the keyboard and the autospawn evidence checks. Defaults to
|
||||
redbear-mini (mapped to the in-tree redbear-mini image).
|
||||
USAGE
|
||||
}
|
||||
|
||||
check_mode=0
|
||||
config="redbear-mini"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h|help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--check)
|
||||
check_mode=1
|
||||
;;
|
||||
redbear-*)
|
||||
config="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
firmware="$(find_uefi_firmware)" || {
|
||||
echo "ERROR: no usable x86_64 UEFI firmware found" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
arch="${ARCH:-$(uname -m)}"
|
||||
# Prefer the live ISO (canonical build-redbear.sh artifact); harddrive.img is
|
||||
# only produced by older make-all flows and goes stale, which would test the
|
||||
# wrong tree. extra.img stays as a data drive in both modes.
|
||||
iso="build/$arch/$config.iso"
|
||||
image="build/$arch/$config/harddrive.img"
|
||||
extra="build/$arch/$config/extra.img"
|
||||
|
||||
boot_media=""
|
||||
if [[ -f "$iso" ]]; then
|
||||
boot_media="iso"
|
||||
elif [[ -f "$image" ]]; then
|
||||
boot_media="harddrive"
|
||||
else
|
||||
echo "ERROR: no bootable image found ($iso or $image)" >&2
|
||||
echo "Build it first with: ./local/scripts/build-redbear.sh $config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$extra" ]]; then
|
||||
truncate -s 1g "$extra"
|
||||
fi
|
||||
|
||||
if [[ "$boot_media" == "iso" ]]; then
|
||||
boot_args=(-cdrom "$iso")
|
||||
else
|
||||
boot_args=(-drive file="$image",format=raw,if=none,id=drv0,snapshot=on -device nvme,drive=drv0,serial=NVME_SERIAL)
|
||||
fi
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$config" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
if [[ "$check_mode" -eq 1 ]]; then
|
||||
log_file="build/$arch/$config/ehci-autospawn-check.log"
|
||||
rm -f "$log_file"
|
||||
set +e
|
||||
# 180s mirrors the xHCI interrupt proof budget. The autospawn chain
|
||||
# (ehcid enumerate → usb-core spawn → usbhidd init) is bounded but the
|
||||
# guest clock runs several times slower than wall under boot load.
|
||||
timeout 180s qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine q35 \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-kbd,bus=ehci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device ich9-intel-hda -device hda-output \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-nographic -vga none \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host \
|
||||
> "$log_file" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
# 1. ehcid bound the controller. This proves pcid matched the EHCI PCI
|
||||
# class (0c0320) and spawned ehcid.
|
||||
if ! grep -q "EHCI USB 2.0 at " "$log_file"; then
|
||||
echo "ERROR: ehcid did not bind the EHCI controller" >&2
|
||||
echo " Expected: 'EHCI USB 2.0 at <pci_path>'" >&2
|
||||
grep -i "ehcid\|EHCI" "$log_file" | tail -10 >&2 || true
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Controller initialized (ports detected, async queue armed).
|
||||
if ! grep -q "ehcid: controller initialized," "$log_file"; then
|
||||
echo "ERROR: ehcid did not finish controller initialization" >&2
|
||||
echo " Expected: 'ehcid: controller initialized, <n> ports, ...'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. usb-kbd enumerated on the EHCI port. This proves the EHCI transfer
|
||||
# path (descriptor fetch + SET_ADDRESS + config) reached a fully
|
||||
# attached device.
|
||||
if ! grep -Eq "ehcid: port [0-9]+ device [0-9a-fA-F]{4}:[0-9a-fA-F]{4} address [0-9]+" "$log_file"; then
|
||||
echo "ERROR: ehcid did not enumerate the usb-kbd" >&2
|
||||
echo " Expected: 'ehcid: port <n> device <vid>:<pid> address <a>'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. usbhidd auto-spawned via the unified trait. This is the P1-A core
|
||||
# claim: spawn_class_driver_for_port selected the HID driver for the
|
||||
# enumerated keyboard and launched usbhidd, which is the inputd
|
||||
# producer for the keyboard. Without this marker the autospawn path
|
||||
# is not proven even if enumeration succeeded.
|
||||
if ! grep -q "USB HID driver spawned with scheme" "$log_file"; then
|
||||
echo "ERROR: usbhidd did not auto-spawn for the EHCI keyboard" >&2
|
||||
echo " Expected: 'USB HID driver spawned with scheme \`...\` ...'" >&2
|
||||
grep -i "usbhidd\|HID driver" "$log_file" | tail -10 >&2 || true
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 5. No panics anywhere in the boot.
|
||||
if grep -qiE "panic|RUST_BACKTRACE" "$log_file"; then
|
||||
echo "ERROR: panic detected in boot log" >&2
|
||||
grep -iE "panic|RUST_BACKTRACE" "$log_file" | head -5 >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "EHCI_DRIVER=ehcid"
|
||||
echo "EHCI_CONTROLLER=usb-ehci"
|
||||
echo "EHCI_MACHINE=q35"
|
||||
echo "EHCI_AUTOSPAWN=usbhidd"
|
||||
echo "EHCI_LOG=$log_file"
|
||||
echo "EHCI keyboard class-driver autospawn confirmed in $log_file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine q35 \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-kbd,bus=ehci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device ich9-intel-hda -device hda-output \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-vga std \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host
|
||||
Executable
+203
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch or validate OHCI controller enumeration (P1-B) in QEMU.
|
||||
#
|
||||
# Topology under test:
|
||||
# -machine pc → -device pci-ohci,id=ohci
|
||||
# └─ bus=ohci.0 → usb-kbd
|
||||
#
|
||||
# Validates: pcid auto-spawns ohcid for the QEMU OHCI PCI device (class 0c0310,
|
||||
# see local/config/drivers.d/20-usb.toml), ohcid maps the MMIO registers,
|
||||
# initializes the controller, and detects the attached usb-kbd (port connect +
|
||||
# enumeration descriptor read). This is the P1-B OHCI runtime-enumeration proof
|
||||
# that closes the P8-B gap for OHCI.
|
||||
#
|
||||
# Evidence is read from the serial log (no guest-side checker exists that
|
||||
# validates OHCI enumeration specifically), mirroring the test-xhci-irq-qemu.sh
|
||||
# proof style. Proof markers come straight from
|
||||
# local/recipes/drivers/ohcid/source/src/main.rs:
|
||||
# "OHCI USB 1.1 at <pci_path>" (main.rs:305 — controller bound)
|
||||
# "ohcid: controller initialized, polling ports" (main.rs:341 — init done)
|
||||
# "ohcid: port <n> connect detected" (main.rs:348 — port event)
|
||||
# "ohcid: port <n> device <vid>:<pid> class <c>" (main.rs:352 — enumerated)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
find_uefi_firmware() {
|
||||
local candidates=(
|
||||
"/usr/share/ovmf/x64/OVMF.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF.4m.fd"
|
||||
"/usr/share/ovmf/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/ovmf/OVMF.fd"
|
||||
"/usr/share/OVMF/OVMF_CODE.fd"
|
||||
"/usr/share/qemu/edk2-x86_64-code.fd"
|
||||
)
|
||||
local path
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
printf '%s\n' "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: test-ohci-runtime-qemu.sh [--check] [config]
|
||||
|
||||
Boot or validate OHCI (USB 1.1) controller enumeration on a Red Bear image in
|
||||
QEMU. `pci-ohci` is a standalone PCI OHCI controller; this proof uses
|
||||
`-machine pc` to match the runbook's "legacy QEMU machine" framing for both
|
||||
P1-B companion-controller proofs. Defaults to redbear-mini (mapped to the
|
||||
in-tree redbear-mini image).
|
||||
USAGE
|
||||
}
|
||||
|
||||
check_mode=0
|
||||
config="redbear-mini"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h|help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--check)
|
||||
check_mode=1
|
||||
;;
|
||||
redbear-*)
|
||||
config="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
firmware="$(find_uefi_firmware)" || {
|
||||
echo "ERROR: no usable x86_64 UEFI firmware found" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
arch="${ARCH:-$(uname -m)}"
|
||||
# Prefer the live ISO (canonical build-redbear.sh artifact); harddrive.img is
|
||||
# only produced by older make-all flows and goes stale, which would test the
|
||||
# wrong tree. extra.img stays as a data drive in both modes.
|
||||
iso="build/$arch/$config.iso"
|
||||
image="build/$arch/$config/harddrive.img"
|
||||
extra="build/$arch/$config/extra.img"
|
||||
|
||||
boot_media=""
|
||||
if [[ -f "$iso" ]]; then
|
||||
boot_media="iso"
|
||||
elif [[ -f "$image" ]]; then
|
||||
boot_media="harddrive"
|
||||
else
|
||||
echo "ERROR: no bootable image found ($iso or $image)" >&2
|
||||
echo "Build it first with: ./local/scripts/build-redbear.sh $config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$extra" ]]; then
|
||||
truncate -s 1g "$extra"
|
||||
fi
|
||||
|
||||
if [[ "$boot_media" == "iso" ]]; then
|
||||
boot_args=(-cdrom "$iso")
|
||||
else
|
||||
boot_args=(-drive file="$image",format=raw,if=none,id=drv0,snapshot=on -device nvme,drive=drv0,serial=NVME_SERIAL)
|
||||
fi
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$config" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
if [[ "$check_mode" -eq 1 ]]; then
|
||||
log_file="build/$arch/$config/ohci-runtime-check.log"
|
||||
rm -f "$log_file"
|
||||
set +e
|
||||
# 180s mirrors the xHCI interrupt proof budget. OHCI enumeration on QEMU's
|
||||
# emulated pci-ohci is quick once ohcid spawns, but the guest clock runs
|
||||
# several times slower than wall under boot load.
|
||||
timeout 180s qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine pc \
|
||||
-device pci-ohci,id=ohci \
|
||||
-device usb-kbd,bus=ohci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-nographic -vga none \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host \
|
||||
> "$log_file" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
# 1. ohcid bound the controller and logged its PCI path. This proves pcid
|
||||
# matched the OHCI PCI class (0c0310) and spawned ohcid.
|
||||
if ! grep -q "OHCI USB 1.1 at " "$log_file"; then
|
||||
echo "ERROR: ohcid did not bind the OHCI controller" >&2
|
||||
echo " Expected: 'OHCI USB 1.1 at <pci_path>'" >&2
|
||||
grep -i "ohcid\|OHCI" "$log_file" | tail -10 >&2 || true
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. MMIO mapped and the polling loop started.
|
||||
if ! grep -q "ohcid: controller initialized, polling ports" "$log_file"; then
|
||||
echo "ERROR: ohcid did not finish controller initialization" >&2
|
||||
echo " Expected: 'ohcid: controller initialized, polling ports'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Port connect detected for the attached usb-kbd. This is the
|
||||
# enumeration-proof marker: the controller saw a device and ran the
|
||||
# descriptor-read path.
|
||||
if ! grep -Eq "ohcid: port [0-9]+ connect detected" "$log_file"; then
|
||||
echo "ERROR: ohcid did not detect a port connect event" >&2
|
||||
echo " Expected: 'ohcid: port <n> connect detected'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. No panics anywhere in the boot.
|
||||
if grep -qiE "panic|RUST_BACKTRACE" "$log_file"; then
|
||||
echo "ERROR: panic detected in boot log" >&2
|
||||
grep -iE "panic|RUST_BACKTRACE" "$log_file" | head -5 >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "OHCI_DRIVER=ohcid"
|
||||
echo "OHCI_CONTROLLER=pci-ohci"
|
||||
echo "OHCI_MACHINE=pc"
|
||||
echo "OHCI_LOG=$log_file"
|
||||
echo "OHCI controller enumeration confirmed in $log_file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine pc \
|
||||
-device pci-ohci,id=ohci \
|
||||
-device usb-kbd,bus=ohci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-vga std \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host
|
||||
Executable
+203
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch or validate UHCI controller enumeration (P1-B) in QEMU.
|
||||
#
|
||||
# Topology under test:
|
||||
# -machine pc (i440fx/PIIX3 chipset) → -device piix3-usb-uhci,id=uhci
|
||||
# └─ bus=uhci.0 → usb-kbd
|
||||
#
|
||||
# Validates: pcid auto-spawns uhcid for the PIIX3 UHCI PCI device (class 0c0300,
|
||||
# see local/config/drivers.d/20-usb.toml), uhcid maps the I/O base, initializes
|
||||
# the frame list, and detects the attached usb-kbd (port connect + enumeration
|
||||
# descriptor read). This is the P1-B UHCI runtime-enumeration proof that closes
|
||||
# the P8-B gap for UHCI.
|
||||
#
|
||||
# Evidence is read from the serial log (no guest-side checker exists that
|
||||
# validates UHCI enumeration specifically), mirroring the test-xhci-irq-qemu.sh
|
||||
# proof style. Proof markers come straight from
|
||||
# local/recipes/drivers/uhcid/source/src/main.rs:
|
||||
# "UHCI USB 1.1 at <pci_path>" (main.rs:487 — controller bound)
|
||||
# "uhcid: controller initialized, polling ports" (main.rs:535 — init done)
|
||||
# "uhcid: port <n> connect detected" (main.rs:545 — port event)
|
||||
# "uhcid: port <n> device <vid>:<pid> class <c>" (main.rs:551 — enumerated)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
find_uefi_firmware() {
|
||||
local candidates=(
|
||||
"/usr/share/ovmf/x64/OVMF.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF.4m.fd"
|
||||
"/usr/share/ovmf/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/ovmf/OVMF.fd"
|
||||
"/usr/share/OVMF/OVMF_CODE.fd"
|
||||
"/usr/share/qemu/edk2-x86_64-code.fd"
|
||||
)
|
||||
local path
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
printf '%s\n' "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: test-uhci-runtime-qemu.sh [--check] [config]
|
||||
|
||||
Boot or validate UHCI (USB 1.1) controller enumeration on a Red Bear image in
|
||||
QEMU. The PIIX3 UHCI companion controller only exists on the i440fx machine
|
||||
type, so this proof uses `-machine pc` rather than the q35 machine used by the
|
||||
xHCI suite. Defaults to redbear-mini (mapped to the in-tree redbear-mini image).
|
||||
USAGE
|
||||
}
|
||||
|
||||
check_mode=0
|
||||
config="redbear-mini"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h|help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--check)
|
||||
check_mode=1
|
||||
;;
|
||||
redbear-*)
|
||||
config="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
firmware="$(find_uefi_firmware)" || {
|
||||
echo "ERROR: no usable x86_64 UEFI firmware found" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
arch="${ARCH:-$(uname -m)}"
|
||||
# Prefer the live ISO (canonical build-redbear.sh artifact); harddrive.img is
|
||||
# only produced by older make-all flows and goes stale, which would test the
|
||||
# wrong tree. extra.img stays as a data drive in both modes.
|
||||
iso="build/$arch/$config.iso"
|
||||
image="build/$arch/$config/harddrive.img"
|
||||
extra="build/$arch/$config/extra.img"
|
||||
|
||||
boot_media=""
|
||||
if [[ -f "$iso" ]]; then
|
||||
boot_media="iso"
|
||||
elif [[ -f "$image" ]]; then
|
||||
boot_media="harddrive"
|
||||
else
|
||||
echo "ERROR: no bootable image found ($iso or $image)" >&2
|
||||
echo "Build it first with: ./local/scripts/build-redbear.sh $config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$extra" ]]; then
|
||||
truncate -s 1g "$extra"
|
||||
fi
|
||||
|
||||
if [[ "$boot_media" == "iso" ]]; then
|
||||
boot_args=(-cdrom "$iso")
|
||||
else
|
||||
boot_args=(-drive file="$image",format=raw,if=none,id=drv0,snapshot=on -device nvme,drive=drv0,serial=NVME_SERIAL)
|
||||
fi
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$config" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
if [[ "$check_mode" -eq 1 ]]; then
|
||||
log_file="build/$arch/$config/uhci-runtime-check.log"
|
||||
rm -f "$log_file"
|
||||
set +e
|
||||
# 180s mirrors the xHCI interrupt proof budget. UHCI enumeration on QEMU's
|
||||
# emulated PIIX3 is quick once uhcid spawns, but the guest clock runs several
|
||||
# times slower than wall under boot load (same characteristic latency noted
|
||||
# in test-usb-hub-qemu.sh).
|
||||
timeout 180s qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine pc \
|
||||
-device piix3-usb-uhci,id=uhci \
|
||||
-device usb-kbd,bus=uhci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-nographic -vga none \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host \
|
||||
> "$log_file" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
# 1. uhcid bound the controller and logged its PCI path. This proves pcid
|
||||
# matched the PIIX3 UHCI PCI class (0c0300) and spawned uhcid.
|
||||
if ! grep -q "UHCI USB 1.1 at " "$log_file"; then
|
||||
echo "ERROR: uhcid did not bind the UHCI controller" >&2
|
||||
echo " Expected: 'UHCI USB 1.1 at <pci_path>'" >&2
|
||||
grep -i "uhcid\|UHCI" "$log_file" | tail -10 >&2 || true
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Frame list allocated and the polling loop started.
|
||||
if ! grep -q "uhcid: controller initialized, polling ports" "$log_file"; then
|
||||
echo "ERROR: uhcid did not finish controller initialization" >&2
|
||||
echo " Expected: 'uhcid: controller initialized, polling ports'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Port connect detected for the attached usb-kbd. This is the
|
||||
# enumeration-proof marker: the controller saw a device and ran the
|
||||
# descriptor-read path.
|
||||
if ! grep -Eq "uhcid: port [0-9]+ connect detected" "$log_file"; then
|
||||
echo "ERROR: uhcid did not detect a port connect event" >&2
|
||||
echo " Expected: 'uhcid: port <n> connect detected'" >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. No panics anywhere in the boot.
|
||||
if grep -qiE "panic|RUST_BACKTRACE" "$log_file"; then
|
||||
echo "ERROR: panic detected in boot log" >&2
|
||||
grep -iE "panic|RUST_BACKTRACE" "$log_file" | head -5 >&2
|
||||
echo " See $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "UHCI_DRIVER=uhcid"
|
||||
echo "UHCI_CONTROLLER=piix3-usb-uhci"
|
||||
echo "UHCI_MACHINE=pc"
|
||||
echo "UHCI_LOG=$log_file"
|
||||
echo "UHCI controller enumeration confirmed in $log_file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec qemu-system-x86_64 \
|
||||
-name "Red Bear OS x86_64" \
|
||||
-machine pc \
|
||||
-device piix3-usb-uhci,id=uhci \
|
||||
-device usb-kbd,bus=uhci.0 \
|
||||
-smp 4 \
|
||||
-m 2048 \
|
||||
-bios "$firmware" \
|
||||
-chardev stdio,id=debug,signal=off,mux=on \
|
||||
-serial chardev:debug \
|
||||
-mon chardev=debug \
|
||||
-device virtio-net,netdev=net0 \
|
||||
-netdev user,id=net0 \
|
||||
-object filter-dump,id=f1,netdev=net0,file="build/$arch/$config/network.pcap" \
|
||||
-vga std \
|
||||
"${boot_args[@]}" \
|
||||
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on \
|
||||
-device nvme,drive=drv1,serial=NVME_EXTRA \
|
||||
-enable-kvm -cpu host
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validate bounded USB error recovery under hot-unplug mid-transfer (P8-C).
|
||||
#
|
||||
# Topology under test:
|
||||
# qemu-xhci root port → usb-storage (backed by a seeded raw image)
|
||||
#
|
||||
# Proof: hot-unplug the usb-storage device via the QEMU monitor (`device_del`)
|
||||
# while usbscsid is actively serving reads, then verify xhcid and usbscsid
|
||||
# handle the loss gracefully — a logged detach + a logged IO/scheme error is
|
||||
# acceptable; a panic, abort, or RUST_BACKTRACE is not. This is the runtime
|
||||
# error-injection half of P2-C (core complete) and the P8-C exit criterion:
|
||||
# "hot-unplug during transfer, verify graceful error (not panic)".
|
||||
#
|
||||
# Why the QEMU monitor and not the python login harness:
|
||||
# qemu-login-expect.py drives only the guest serial console (send/expect). It
|
||||
# cannot toggle to the QEMU monitor to issue `device_del`, so this script uses
|
||||
# the same `-chardev stdio,mux=on -mon chardev=debug` monitor-mux pattern as
|
||||
# test-xhci-device-lifecycle-qemu.sh, switching between serial and monitor with
|
||||
# the Ctrl-A c (`\001c`) escape.
|
||||
#
|
||||
# The guest-side transfer is driven by `redbear-usb-storage-check` (in the
|
||||
# redbear-hwutils package), which performs STORAGE_DISCOVERY + WRITE + READBACK
|
||||
# + RESTORE — a sequence of real bulk transfers that usbscsid is mid-way
|
||||
# through when the unplug lands. No guest-side checker is invented; the
|
||||
# existing one is reused, exactly as test-usb-storage-qemu.sh does.
|
||||
#
|
||||
# Graceful-handling markers (real driver source):
|
||||
# "Device on port <n> was detached" (xhcid detach path)
|
||||
# "usbscsid: READ IO ERROR: ..." (usbscsid main.rs:190)
|
||||
# "usbscsid: WRITE IO ERROR: ..." (usbscsid main.rs:200)
|
||||
# "usbscsid: scheme tick error: ..." (usbscsid main.rs:163)
|
||||
# "usbscsid: event error: ..." (usbscsid main.rs:156)
|
||||
# Failure markers (prove NOT graceful):
|
||||
# "panic" / "RUST_BACKTRACE" / "usbscsid: startup failed"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
seed_usb_image() {
|
||||
local image_path="$1"
|
||||
python3 - "$image_path" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
path = pathlib.Path(sys.argv[1])
|
||||
payload = (b"REDBEAR-USB-ERROR-RECOVERY-CHECK\0" * 32)[:512]
|
||||
payload = payload.ljust(512, b'\0')
|
||||
|
||||
with path.open("r+b") as fh:
|
||||
fh.seek(0)
|
||||
fh.write(payload)
|
||||
PY
|
||||
}
|
||||
|
||||
find_uefi_firmware() {
|
||||
local candidates=(
|
||||
"/usr/share/ovmf/x64/OVMF.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF.4m.fd"
|
||||
"/usr/share/ovmf/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/OVMF/x64/OVMF_CODE.4m.fd"
|
||||
"/usr/share/ovmf/OVMF.fd"
|
||||
"/usr/share/OVMF/OVMF_CODE.fd"
|
||||
"/usr/share/qemu/edk2-x86_64-code.fd"
|
||||
)
|
||||
local path
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
printf '%s\n' "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: test-usb-error-recovery-qemu.sh [--check] [config]
|
||||
|
||||
Boot a Red Bear image, attach a usb-storage device, drive an active transfer
|
||||
via redbear-usb-storage-check, and hot-unplug the device mid-transfer through
|
||||
the QEMU monitor. Verifies xhcid/usbscsid handle the loss gracefully (logged
|
||||
detach + logged error, no panic). Defaults to redbear-mini (mapped to the
|
||||
in-tree redbear-mini image).
|
||||
|
||||
The --check flag is accepted for runbook/matriz consistency with the other USB
|
||||
proofs; the monitor-driven sequence runs identically in either mode (mirroring
|
||||
test-xhci-device-lifecycle-qemu.sh, which treats --check the same way).
|
||||
USAGE
|
||||
}
|
||||
|
||||
config="redbear-mini"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h|help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--check)
|
||||
# Accepted for consistency; behavior is identical (see usage).
|
||||
;;
|
||||
redbear-*)
|
||||
config="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
firmware="$(find_uefi_firmware)" || {
|
||||
echo "ERROR: no usable x86_64 UEFI firmware found" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
arch="${ARCH:-$(uname -m)}"
|
||||
image="build/$arch/$config/harddrive.img"
|
||||
extra="build/$arch/$config/extra.img"
|
||||
usb_img="build/$arch/$config/usb-error-recovery-storage.img"
|
||||
log_file="build/$arch/$config/usb-error-recovery.log"
|
||||
session_tag="Red Bear OS USB Error Recovery Test $$"
|
||||
session_image="/tmp/redbear-usb-error-recovery-$$-harddrive.img"
|
||||
session_extra="/tmp/redbear-usb-error-recovery-$$-extra.img"
|
||||
session_usb_img="/tmp/redbear-usb-error-recovery-$$-usb.img"
|
||||
|
||||
# Prefer the live ISO (canonical build-redbear.sh artifact); fall back to
|
||||
# harddrive.img for older make-all flows. The lifecycle/error-recovery proofs
|
||||
# drive the QEMU monitor, which requires a harddrive-style -drive (snapshot=on)
|
||||
# rather than -cdrom, so when only the ISO is present we still boot the ISO but
|
||||
# attach the USB image as a separate snapshot drive (the storage device under
|
||||
# test is the usb-storage, not the boot disk).
|
||||
iso="build/$arch/$config.iso"
|
||||
if [[ ! -f "$image" && ! -f "$iso" ]]; then
|
||||
echo "ERROR: no bootable image found ($iso or $image)" >&2
|
||||
echo "Build it first with: ./local/scripts/build-redbear.sh $config" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Resolve to an existing boot medium, preferring the ISO.
|
||||
if [[ -f "$iso" ]]; then
|
||||
boot_src="iso"
|
||||
else
|
||||
boot_src="harddrive"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$extra" ]]; then
|
||||
truncate -s 1g "$extra"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$usb_img" ]]; then
|
||||
truncate -s 64M "$usb_img"
|
||||
fi
|
||||
seed_usb_image "$usb_img"
|
||||
|
||||
image_real="$(realpath "$image" 2>/dev/null || echo "$image")"
|
||||
iso_real="$(realpath "$iso" 2>/dev/null || echo "$iso")"
|
||||
extra_real="$(realpath "$extra")"
|
||||
usb_img_real="$(realpath "$usb_img")"
|
||||
log_file="$(realpath -m "$log_file")"
|
||||
|
||||
ln -sf "$extra_real" "$session_extra"
|
||||
ln -sf "$usb_img_real" "$session_usb_img"
|
||||
if [[ "$boot_src" == "harddrive" ]]; then
|
||||
ln -sf "$image_real" "$session_image"
|
||||
fi
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$session_tag" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
rm -f "$log_file"
|
||||
|
||||
# Assemble the boot-drive argument. The monitor-mux lifecycle pattern uses a
|
||||
# snapshot harddrive drive; when we have an ISO we attach it via -cdrom instead
|
||||
# (the usb-storage device under test is independent of the boot medium).
|
||||
if [[ "$boot_src" == "harddrive" ]]; then
|
||||
boot_drive_args=(-drive "file=$session_image,format=raw,if=none,id=drv0,snapshot=on" -device nvme,drive=drv0,serial=NVME_SERIAL)
|
||||
else
|
||||
boot_drive_args=(-cdrom "$iso_real")
|
||||
fi
|
||||
|
||||
expect <<EOF
|
||||
log_user 1
|
||||
log_file -noappend $log_file
|
||||
set timeout 1800
|
||||
set send_slow {1 0.0}
|
||||
spawn qemu-system-x86_64 -name {$session_tag} -device qemu-xhci,id=xhci -smp 4 -m 2048 -bios $firmware -chardev stdio,id=debug,signal=off,mux=on -serial chardev:debug -mon chardev=debug -machine q35 -device ich9-intel-hda -device hda-output -device virtio-net,netdev=net0 -netdev user,id=net0 -nographic -vga none {*}"${boot_drive_args[*]}" -drive file=$session_extra,format=raw,if=none,id=drv1,snapshot=on -device nvme,drive=drv1,serial=NVME_EXTRA -drive file=$session_usb_img,format=raw,if=none,id=usbdisk,snapshot=on -enable-kvm -cpu host
|
||||
|
||||
# Wait for boot to the login prompt.
|
||||
expect "login:"
|
||||
send "root\r"
|
||||
expect "assword:"
|
||||
send "password\r"
|
||||
expect -re {Type 'help' for available commands\.}
|
||||
expect -re {# }
|
||||
|
||||
# Hotplug the usb-storage device under test, then wait for usbscsid to spawn
|
||||
# and serve at least one transfer (DISCOVERY). This proves the device was
|
||||
# attached and the bulk-transfer path was live before the unplug.
|
||||
send "\001c"
|
||||
expect "(qemu)"
|
||||
send "device_add usb-storage,bus=xhci.0,drive=usbdisk,id=usbstore0\r"
|
||||
expect "(qemu)"
|
||||
send "\001c"
|
||||
expect -re {USB SCSI driver spawned with scheme}
|
||||
expect -re {xhcid: finished attach for port [0-9.]+}
|
||||
|
||||
# Drive an active transfer. redbear-usb-storage-check performs DISCOVERY +
|
||||
# WRITE + READBACK + RESTORE, a sequence of real bulk transfers. We do not
|
||||
# wait for it to finish: the device_del below lands while usbscsid is
|
||||
# servicing these transfers (mid-transfer).
|
||||
send "redbear-usb-storage-check\r"
|
||||
|
||||
# Allow the check to enter its transfer loop before unplugging. 3s is enough
|
||||
# for DISCOVERY + the first WRITE/READBACK bulk transfers to be in flight on
|
||||
# QEMU's emulated xHCI (characteristic of the storage proof's timing).
|
||||
after 3000
|
||||
|
||||
# Hot-unplug mid-transfer via the QEMU monitor.
|
||||
send "\001c"
|
||||
expect "(qemu)"
|
||||
send "device_del usbstore0\r"
|
||||
expect "(qemu)"
|
||||
send "\001c"
|
||||
|
||||
# Observe the graceful-detach path from xhcid. A bounded wait: if the detach
|
||||
# marker does not appear, the unplug was not observed and the proof fails the
|
||||
# post-hoc grep below.
|
||||
expect {
|
||||
-re {Device on port [0-9.]+ was detached} { }
|
||||
timeout { }
|
||||
}
|
||||
|
||||
# Give usbscsid a moment to log the transfer error from its tick loop, then
|
||||
# return to a shell prompt to shut down cleanly.
|
||||
after 2000
|
||||
send "\r"
|
||||
expect -re {# }
|
||||
send "shutdown\r"
|
||||
sleep 2
|
||||
EOF
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$session_tag" 2>/dev/null || true
|
||||
rm -f "$session_image" "$session_extra" "$session_usb_img"
|
||||
|
||||
failures=0
|
||||
|
||||
echo "--- USB Error Recovery Validation: $config ---"
|
||||
|
||||
# 1. The usb-storage device attached and usbscsid spawned (precondition: there
|
||||
# was a live transfer to interrupt).
|
||||
if grep -aq "USB SCSI driver spawned with scheme" "$log_file"; then
|
||||
echo " [PASS] usbscsid spawned for the hotplugged usb-storage device"
|
||||
else
|
||||
echo " [FAIL] usbscsid did not spawn; no live transfer to interrupt" >&2
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
|
||||
# 2. The hot-unplug was observed by xhcid (detach path fired).
|
||||
if grep -Eaq 'Device on port [0-9.]+ was detached' "$log_file"; then
|
||||
echo " [PASS] xhcid observed the mid-transfer hot-unplug"
|
||||
else
|
||||
echo " [FAIL] xhcid did not log the detach for the unplugged device" >&2
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
|
||||
# 3. Graceful error handling: usbscsid logged an IO/scheme error from its
|
||||
# transfer/tick loop rather than hanging silently. Any one of the
|
||||
# documented error markers satisfies this (they are the catch-and-log
|
||||
# sites that replaced the panic! calls in P1-B).
|
||||
if grep -Eaq 'usbscsid: (READ|WRITE) IO ERROR:|usbscsid: scheme tick error:|usbscsid: event error:' "$log_file"; then
|
||||
echo " [PASS] usbscsid logged a graceful transfer error (no silent hang)"
|
||||
else
|
||||
echo " [WARN] no usbscsid transfer-error marker observed (transfer may have completed before the unplug landed)" >&2
|
||||
fi
|
||||
|
||||
# 4. The critical P8-C guarantee: NO panic / abort / backtrace anywhere in the
|
||||
# boot. This is the single hard failure that invalidates the proof.
|
||||
if grep -qiE "panic|RUST_BACKTRACE|usbscsid: startup failed" "$log_file"; then
|
||||
echo " [FAIL] panic-class failure detected during mid-transfer hot-unplug" >&2
|
||||
grep -iE "panic|RUST_BACKTRACE|usbscsid: startup failed" "$log_file" | head -5 >&2
|
||||
failures=$((failures + 1))
|
||||
else
|
||||
echo " [PASS] no panic/abort/backtrace during mid-transfer hot-unplug"
|
||||
fi
|
||||
|
||||
echo "--- Results: $failures failure(s), log: $log_file ---"
|
||||
|
||||
if [[ "$failures" -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user