5c6cd18599
The UHCI runtime proof grepped for the literal
'uhcid: controller initialized, polling ports'
but uhcid main.rs:535 emits it with ctrl.name interpolated mid-string:
info!("uhcid: {} controller initialized, polling ports", ctrl.name);
producing e.g. 'uhcid: 0000:00:01.2_uhci controller initialized, polling
ports'. The literal grep never matched and the proof would fail 100% of the
time at runtime.
Fix: grep with a regex anchored on the stable suffix:
grep -Eq 'uhcid: .* controller initialized, polling ports'
The ohcid, ehcid, usbhidd, and usbscsid markers were re-audited against
driver source and are all correct (ohcid main.rs:341 has no interpolation;
ehcid main.rs:294 interpolates after the matched comma prefix; all others
interpolate at line ends matched by prefix). Only the UHCI init-done marker
was broken.
Comment header and error echo updated to document the interpolated form and
point at main.rs:535 so the regex is not 'simplified' back to a literal.
No QEMU runs. bash -n passes. Verified the regex matches the real emitted
format and does not cross-match ohcid's identical suffix (the 'uhcid:' prefix
anchor disambiguates).
207 lines
7.1 KiB
Bash
Executable File
207 lines
7.1 KiB
Bash
Executable File
#!/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: <name> 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 <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 -Eq 'uhcid: .* controller initialized, polling ports' "$log_file"; then
|
|
echo "ERROR: uhcid did not finish controller initialization" >&2
|
|
echo " Expected: 'uhcid: <name> 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 <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
|