#!/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 " (main.rs:487 — controller bound) # "uhcid: controller initialized, polling ports" (main.rs:535 — init # done; note ctrl.name is interpolated, e.g. "uhcid: uhci0_uhci controller # initialized, polling ports", so the grep uses a regex, not a literal) # "uhcid: port connect detected" (main.rs:545 — port event) # "uhcid: port device : class " (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 '" >&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 -Eq '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 " (main.rs:535 interpolates ctrl.name, e.g. 'uhcid: uhci0_uhci 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 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