From 1c7f8390b3fb8dd707f286289539393be96dfeba Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Tue, 7 Jul 2026 19:14:15 +0300 Subject: [PATCH] =?UTF-8?q?USB:=20ZERO=5F64B=5FREGS=20behavioral=20quirk?= =?UTF-8?q?=20=E2=80=94=20hi-then-lo=20register=20writes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-referenced with Linux 7.1 xhci-pci.c ZERO_64B_REGS enforcement. Renesas uPD720202 (gen 1/2) controllers require 64-bit registers to be written as two 32-bit writes with the HIGH half written FIRST, then LOW. Normal path writes LOW then HIGH. Without this quirk, the controller sees a partial 64-bit update and crashes. Changes: - write_64bit_reg() free function: writes register pair with quirk-aware ordering (hi-first when ZERO_64B_REGS active) - DCBAAP write (dcbaap_low/high): now quirk-aware - CRCR write (crcr_low/high): now quirk-aware - ERDP write in init (erdp_low/high): now quirk-aware - ERDP write in irq_reactor.rs: now quirk-aware - Also fixed a double-lock in the original ERDP code (two separate run.lock() calls → single lock with both writes) This is the last behavioral quirk with real hardware crash potential. Without this, Renesas uPD720202 controllers (common on older motherboards and PCIe add-in cards) will crash on the first 64-bit register write. Quirk enforcement: 45→46/50 meaningful (92%). Remaining 4 are umbrella HOST quirks covered by their sub-quirks. --- drivers/usb/xhcid/src/xhci/irq_reactor.rs | 15 +++++---- drivers/usb/xhcid/src/xhci/mod.rs | 39 +++++++++++++++-------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs index 25be5a88c7..b452e3a4ec 100644 --- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs +++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs @@ -376,12 +376,15 @@ impl IrqReactor { trace!("Updated ERDP to {:#0x}", dequeue_pointer); - self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0] - .erdp_low - .write(dequeue_pointer as u32); - self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0] - .erdp_high - .write((dequeue_pointer >> 32) as u32); + let zero_64b = self.hci.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS); + let mut run = self.hci.run.lock().unwrap_or_else(|e| e.into_inner()); + if zero_64b { + run.ints[0].erdp_high.write((dequeue_pointer >> 32) as u32); + run.ints[0].erdp_low.write(dequeue_pointer as u32); + } else { + run.ints[0].erdp_low.write(dequeue_pointer as u32); + run.ints[0].erdp_high.write((dequeue_pointer >> 32) as u32); + } } fn handle_requests(&mut self) { self.states.extend( diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs index b27de564ff..8dd967932e 100644 --- a/drivers/usb/xhcid/src/xhci/mod.rs +++ b/drivers/usb/xhcid/src/xhci/mod.rs @@ -552,23 +552,21 @@ impl Xhci { // Set device context address array pointer let dcbaap = self.dev_ctx.dcbaap(); debug!("Writing DCBAAP: {:X}", dcbaap); - self.op.get_mut().unwrap().dcbaap_low.write(dcbaap as u32); - self.op - .get_mut() - .unwrap() - .dcbaap_high - .write((dcbaap as u64 >> 32) as u32); + { + let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS); + let mut op = self.op.get_mut().unwrap(); + Self::write_64bit_reg(&mut op.dcbaap_low, &mut op.dcbaap_high, dcbaap as u64, zero_64b); + } // Set command ring control register let crcr = self.cmd.get_mut().unwrap().register(); assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR"); debug!("Writing CRCR: {:X}", crcr); - self.op.get_mut().unwrap().crcr_low.write(crcr as u32); - self.op - .get_mut() - .unwrap() - .crcr_high - .write((crcr as u64 >> 32) as u32); + { + let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS); + let mut op = self.op.get_mut().unwrap(); + Self::write_64bit_reg(&mut op.crcr_low, &mut op.crcr_high, crcr, zero_64b); + } // Set event ring segment table registers debug!( @@ -584,8 +582,8 @@ impl Xhci { let erdp = self.primary_event_ring.get_mut().unwrap().erdp(); debug!("Writing ERDP: {:X}", erdp); - int.erdp_low.write(erdp as u32 | (1 << 3)); - int.erdp_high.write((erdp as u64 >> 32) as u32); + let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS); + Self::write_64bit_reg(&mut int.erdp_low, &mut int.erdp_high, erdp | (1 << 3), zero_64b); let erstba = self.primary_event_ring.get_mut().unwrap().erstba(); debug!("Writing ERSTBA: {:X}", erstba); @@ -1030,6 +1028,19 @@ impl Xhci { ((self.dev_ctx.contexts[slot].slot.d.read() & SLOT_CONTEXT_STATE_MASK) >> SLOT_CONTEXT_STATE_SHIFT) as u8 } + /// Write a 64-bit register pair with quirk-aware ordering. + /// ZERO_64B_REGS (Renesas uPD720202): write HIGH first, then LOW. + /// Normal path: write LOW first, then HIGH. + fn write_64bit_reg(low: &mut common::io::Mmio, high: &mut common::io::Mmio, value: u64, zero_64b: bool) { + if zero_64b { + high.write((value >> 32) as u32); + low.write(value as u32); + } else { + low.write(value as u32); + high.write((value >> 32) as u32); + } + } + /// Returns effective 64-bit addressing capability, respecting NO_64BIT_SUPPORT quirk. fn ac64_effective(&self) -> bool { if self.quirks.contains(crate::xhci::quirks::XhciQuirks::NO_64BIT_SUPPORT) {