#!/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 " (main.rs:305 — controller bound) # "ohcid: controller initialized, polling ports" (main.rs:341 — init done) # "ohcid: port connect detected" (main.rs:348 — port event) # "ohcid: port device : class " (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 '" >&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 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