Port the Linux 7.1 hub.c port state machine into usbhubd, replacing the
minimal connect/reset handling:
New module port_ops.rs (pure logic, side effects injected, 14 unit
tests):
- debounce_until_connected: hub_port_debounce_be_connected() port
(hub.c:4696-4737) — 25ms polls, connection stable for 100ms, 2s
budget, connection-change bit cleared in-loop.
- wait_for_reset: hub_port_wait_reset() port (hub.c:2953-3047) — 10ms
polls until RESET clears with CONNECTION set, escalate to 200ms after
two short waits, 800ms budget; then 50ms TRSTRCY recovery (hub.c:3159)
and C_PORT_RESET clear. Replaces the previous bare sleep(10ms).
- wait_for_u0: USB 3.0 polling→U0 wait after port power-on — 36ms steps,
400ms ceiling (tPollingLFPSTimeout = 360ms; Linux hub.c:1226 debounce
path).
- accumulate_hub_delay_ns: wHubDelay chain rule (hub.c:1507-1519:
wHubDelay + parent->hub_delay + 40ns, cap 65535ns).
main.rs wiring:
- Port status normalized to PortStatusSnapshot (decouples the state
machine from the V2/V3 wire formats; V3 link state extracted from
bits 8:5).
- Debounce on connection-change before attach; C_PORT_ENABLE cleared
once handled (Linux port_event semantics).
- Reset path uses wait_for_reset instead of sleep(10ms).
- USB 3 power-on path waits for U0 before proceeding.
- wHubDelay: ancestor-chain walk fetching USB 3 ancestor hub
descriptors, accumulated per Linux; delivered to newly attached
SuperSpeed children via SET_ISOCH_DELAY (USB 3.0 9.4.11; Linux
message.c:1142 — hubs and non-SS skipped, children inherit the hub's
accumulated delay verbatim per hub.c:5128-5129).
- attach/detach failure logs now identify the port.
hub.rs (xhcid usb module):
- HubDescriptorV3 extended with device_removable: u16 — the SS hub
descriptor is 12 bytes (spec Table 10-15); the old struct under-read
by 2 bytes. Stale TODO corrected: SS descriptors have no
PortPwrCtrlMask (that is USB 2.0-only, still unparsed).
Verified: cargo check clean (0 usbhubd warnings), 14/14 usbhubd tests,
xhcid unaffected (43/43 tests).
Three bugs in the xHCI endpoint-restart path used by all error recovery
(stall hard reset, transaction-error soft retry, resource retry,
split/babble hard reset):
1. Latent deadlock: restart_endpoint held the port_states write guard
across set_tr_deque_ptr(), which internally re-acquires a read guard
on the same key (std RwLock read-while-write on one thread).
Unobserved because error injection is not yet exercised at runtime
(P8-C). Fixed by scoping phase-1 ring priming so the guard drops
before the async command.
2. Doorbell ordering violated xHCI spec 4.6.8/4.6.10: after Reset
Endpoint the TR Dequeue Pointer is undefined, so Set TR Dequeue
Pointer must complete BEFORE the doorbell transitions the endpoint
Stopped->Running. The old order (doorbell first) ran the endpoint
with an undefined dequeue pointer — undefined xHC behavior on real
hardware. Linux rings the doorbell from the Set TR Dequeue command
completion path (xhci_handle_cmd_set_deq, ring.c:1416-1554); xhcid
now issues Set TR Dequeue, awaits completion, then rings.
3. Priming NoOp never executed: ring.register() was captured after
ring.next() advanced the enqueue index, so the dequeue pointed past
the NoOp (dead TRB). Now captured before next(), priming the
hardware dequeue AT the NoOp so the xHC executes it on restart —
same semantics as Linux xhci_move_dequeue_past_td pointing at the
first valid TRB.
Verified: cargo check clean (138 warnings, unchanged), 43/43 tests pass.
Gate xHCI 1.1+ features on their capability bits, cross-referenced with
Linux 7.1 xhci driver behavior:
LEC (HCC2_LEC, scheme.rs):
- lec now uses Linux xhci-mem.c:1350 exact condition:
hci_version > 0x100 && hcc_params2 & HCC2_LEC (HCCPARAMS2 register
space is reserved on 1.0 controllers).
- Max ESIT Payload Hi zeroed when LEC=0 (spec Table 6-8 RsvdZ).
U3C (HCC2_U3C, mod.rs suspend_port):
- Refuse SuperSpeed U3 entry with ENOSYS when hci_ver >= 0x110 and
HCC2_U3C=0 (spec 4.15.1). USB2 suspend unaffected. Linux 7.1 defines
but never gates this bit; xhcid follows the spec.
CIC (HCC2_CIC): CIE gate pre-existing (set_cie from cic()); added the
hci_ver > 0x100 version guard. HCC2 capability log block similarly
guarded.
HW LPM (extended.rs, mod.rs, port.rs):
- New SupportedProtoCap::{l1_capable,hw_lpm_capable,besl_lpm_capable}
reading protocol-defined bits (Linux xhci-ext-caps.h:62-66 L1C/HLC/BLC).
- Xhci::hw_lpm_support computed per Linux xhci-mem.c:2137:
hci_ver >= 0x100 && !HW_LPM_DISABLE && any USB2 protocol cap has HLC.
- attach_device(): defensive LPM clear on USB 2.0 protocol ports
(rev_major() != 3) when hw_lpm_support is false — Linux xhci.c:4725
disable path; USB3 excluded because PORTPMSC L1DS aliases U2 timeout.
- Port::enable_lpm/disable_lpm register targets fixed: HLE/HIRD/RWE/
L1DS belong in PORTPMSC (offset 0x04), not PORTHLPMC (0x0C, bit 16
RsvdZ) — spec Tables 5-21/5-23, Linux xhci-port.h:135-158. Helpers
rewritten to Linux xhci.c:4686-4737 two-register sequence.
- Per-device L1 enablement (BESL, MEL Evaluate Context) defers to P3.
Bug fix: removed bogus CapabilityRegs::hlc() + HCC_PARAMS1_HLC_BIT —
they read xECP pointer bits 16-31 of HCCPARAMS1 (spec Table 5-13), not
HLC. HLC lives in the Supported Protocol capability port_info DWORD.
Verification: cargo check clean (138 warnings, -2 vs baseline: the new
disable_lpm call site also revived PORT_HLE/PORT_HIRD_MASK), 43/43
tests pass.
Replace xhcid's self-contained 294-line xhci/quirks.rs (7-flag bitflags
type + ~30-entry quirk table) with a thin re-export shim that delegates
to redox-driver-sys::quirks, which now carries all 51 Linux 7.1 quirk
flags and the full ~85-entry canonical controller table.
Changes:
- Cargo.toml: add redox-driver-sys path dependency
- xhci/quirks.rs: replace table with pub use XhciControllerQuirkFlags
as XhciQuirks + delegating lookup_quirks(vendor, device, revision,
hci_version)
- main.rs: read real HCIVERSION from MMIO offset 0x04 via
cap.hci_ver.read() instead of hardcoded 0x100 — matches Linux 7.1
xhci_gen_setup() at xhci.c:5455. Two table entries depend on the
value: AMD_0x96_HOST (xhci-pci.c:308) and >= 0x120 spec rule
(xhci-pci.c:511).
- main.rs: resolve quirks BEFORE get_int_method() so BROKEN_MSI skips
MSI/MSI-X probing entirely (matching Linux xhci_try_enable_msi at
xhci-pci.c:143-209). Previously the quirk only overrode the method
label while irq_file still held the MSI handle — a mismatch causing
silent interrupt-delivery failures on BROKEN_MSI controllers.
- get_int_method (both cfg variants): accept quirks parameter
maybe_recover_transfer_error previously handled only the first-tier
codes (UsbTransaction, Resource, Stall, BabbleDetected, DataBuffer,
Trb, SplitTransaction) and silently returned Ok(false) for every
other completion code via a catch-all arm.
Cross-referenced Linux 7.1 drivers/usb/host/xhci-ring.c
handle_tx_event() (line 2608+) and handle_transferless_tx_event()
(line 2561+) to add explicit recovery for the remaining ~29 codes:
- Stopped/StoppedLengthInvalid/StoppedShortPacket: restart endpoint
+ retry (up to MAX_SOFT_RETRY), then hard-reset
- InvalidStreamType/InvalidStreamId: soft reset + retry, then
hard-reset
- IncompatibleDevice: disable slot (device must re-enumerate)
- MissedService/NoPingResponse: log informational, surface to caller
- ContextState/Parameter: hard-reset to resync driver/xHC state
- Bandwidth/BandwidthOverrun/SecondaryBandwidth: log, no transfer
recovery (config must change)
- IsochBuffer: hard-reset endpoint
- MaxExitLatencyTooLarge: log, surface
- EventLost/Undefined: hard-reset (event ring may be corrupted)
- SlotNotEnabled/EndpointNotEnabled/NoSlotsAvailable: log driver
state mismatch
- CommandRingStopped/CommandAborted: log xHC state confusion
- Reserved/vendor: default arm logs explicitly
Added completion_code_to_errno() mapping transfer completion codes
to POSIX errnos matching Linux 7.1 semantics:
Stall -> EPIPE
BabbleDetected -> EOVERFLOW
UsbTransaction/SplitTransaction/IncompatibleDevice -> EPROTO
Trb -> EILSEQ
DataBuffer -> ENOSR
SlotNotEnabled/EndpointNotEnabled/NoSlotsAvailable -> ENODEV
default -> EIO
Rewrote handle_transfer_event_trb() to use the new errno mapping
instead of always returning EIO.
Added 14 unit tests covering all errno mappings and transfer
event handling paths. Full suite (41 tests) passes.
The keyboard input path pushed the raw character bytes, so the Enter key
(a bare CR on the keymap) never produced an LF. Line-oriented readers on
the console (e.g. a shell's read_line) then never saw a completed line and
appeared to hang. Convert CR->LF here, matching the serial input path
(push_input_bytes).
The Warn console level correlated with a deterministic login-auth
regression in the mini image (login reads the username but never reaches
MOTD/spawn_shell; 0/3 boots authenticated vs 1/1 with Info). Restore
Info-level console output while the interaction is re-verified; console
spam will be reduced later via a safer mechanism (routing daemon output,
not lowering the shared log level).
Daemons log via OutputBuilder::stderr() at output_level, which shares the
interactive console/login VT. At Info they spam routine chatter (cpufreqd,
thermald, i2c-hidd, ...) over the login prompt as boot continues in
parallel with getty. Drop the console filter to Warn; full Info logs are
still written to per-daemon log files (file_level), and a bootloader env
var can raise it for debugging.
fbcond now mirrors all framebuffer-console output to the kernel debug (serial)
scheme AND feeds serial input back into the active VT's input queue. Previously,
once fbcond took over the console after display handoff the serial line went
silent (headless operators saw the boot stop at 'Performing handoff' and never
saw the getty login prompt, which draws only to the framebuffer VT). Now the
same console — boot log, login prompt, and shell — is fully usable over serial,
reusing the working framebuffer getty for both surfaces.
- smolnetd: an empty ip_router (DHCP-managed or deliberately network-less like
the bare target) is a valid 'no default gateway' state, not a malformed config
-> no warning.
- router: a limited/directed IPv4 broadcast (DHCP DISCOVER to 255.255.255.255
before any lease/route exists) legitimately has no routing-table entry; don't
warn 'No route found' for broadcast destinations.
- e1000d: add the 82574L (0x10d3, QEMU '-device e1000e') to the E1000 match
list; it keeps the legacy descriptor/register interface this driver uses.
I219 is intentionally excluded (integrated MDIO PHY, different init).
- init: add condition_path_exists so optional-daemon units (dbus, seatd,
thermald, evdevd) are silently skipped when their binary is absent in a
minimal image, instead of emitting [FAILED] 'No such file or directory' on
the bare target. Boot-critical units omit the field and still hard-fail.
- ahcid: an empty ATAPI/optical drive (QEMU's default DVD-ROM, most bare-metal
optical bays) failed READ CAPACITY and logged 4 ERROR lines every boot;
register it with a zero block count as a normal no-media state and drop the
HBA register dump to debug level.
- netstack: a machine with no NIC (bare, or an unsupported NIC) logged an ERROR
and exited non-zero; treat 'no network adapter' as a normal idle state
(info + clean exit).
- dhcpd: cut the socket timeout 30s -> 8s so the network stage fails fast when
no DHCP server answers instead of stalling boot.
Re-adds the archived-but-unapplied scrollback support: a 1000-line ring
buffer in TextScreen capturing all output (incl. pre-handoff boot log), a
Handle::Scrollback variant opened via /scheme/fbcon/<vt>/scrollback, and a
read path returning the retained buffer. Adapted to the current single-step
openat scheme protocol.
The ACPI _LNK object enumeration logged 50+ INFO lines per boot on QEMU
(one per _UID/_CRS/_STA/_DIS/_SRS/_HID method), flooding the console and
slowing serial boot. This is PCI interrupt-routing detail, not needed at
INFO on every boot; move it to debug.
The suspend_to_ram, read_battery_status, and read_battery_info methods
were incorrectly placed inside 'impl Drop for PhysmapGuard'. They
reference fields (aml_symbols, fadt) that only exist on AcpiContext.
Move them to impl AcpiContext and fix field access (self.fadt.as_ref()
instead of self.fadt()).
- try_mem now returns Result (matching caller expectations)
- try_map_bar: non-panicking BAR mapping on PciFunctionHandle
- try_pci_allocate_interrupt_vector: non-panicking IRQ allocation
- virtio-core: reverted .ok_or() back to .map_err() for Result type
Added try_mem() returning Option<(usize, usize)> for non-panicking
memory BAR access. Fixed virtio-core/probe.rs to use .ok_or() instead
of .map_err() to match the Option return type.
Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the base
fork was carrying only 38 of 100 patches in local/patches/base/.
The other 62 patches' content was silently missing from the fork
working tree, even though their .patch files were preserved.
This commit re-applies 41 patches that genuinely still apply
cleanly + 17 hunks that partially applied. Recovery covers:
- D-Bus initfs service wiring (P4-initfs-dbus-services)
- USB service wiring (P4-initfs-usb-drm-services)
- netcfg/dhcp/dhcpv6 driver fixes
- acpid shutdown/PM/quirk fixes
- inputd/ps2d hard-fail logging
- pcid driver interface refinements (server cmd channel)
- virtio-core for VirtualBox support
- ixgbed/rtl8139/rtl8168 net drivers
- ahcid NCQ + per-function interrupt coalescing
- logd persistent logging
- bootstrap procmgr race-condition fixes
- cargo version pin to +rb0.3.0 (synchronized release branch)
58 files changed, +1444/-318 lines.
Untracked mnt/ tree and *.orig / *.rej files cleaned up after
patch application (leftover from absolute-path patch headers).
Otherwise, an open call to /scheme/acpi/tables will result in nsmgr
blocking on an `openat(acpi_root, "tables")` call, which will never
occur if acpid is itself blocking on a `ForkNs` call to nsmgr before it
can serve any scheme requests.
This functionality is needed according to POSIX. Additionally, some
logic had to be changed in order for the ptsname to be correct for the
manager terminal (ptmx). If you want to obtain a pty, you must go
through the `posix_openpt`-`grantpt`-`unlockpt`-`ptsname`-`open`
process. Alternatively, relibc as a function called `openpty`.
This is part of my ptyd series. This can be safely merged once the MRs
for relibc/ and userutils/ are merged at the same time.