ee1da17617
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.
204 lines
6.7 KiB
Bash
Executable File
204 lines
6.7 KiB
Bash
Executable File
#!/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
|