USB: NO_64BIT_SUPPORT behavioral quirk — force 32-bit DMA

Cross-referenced with Linux 7.1 xhci-mem.c DMA allocation.

Previously NO_64BIT_SUPPORT was only logged at init. Now
it actually forces 32-bit DMA addressing:

- ac64_effective() method returns false when quirk is set
- Used in: scratchpad buffer array, DMA allocation (zeroed,
  zeroed_unsized), ring creation in attach_device
- Constructor (new()) computes ac64 from quirk and uses it
  for: command ring, device context list, event ring

This prevents crashes on older controllers that only support
32-bit DMA addressing.  Without this quirk, 64-bit DMA
transactions to addresses above 4GB would silently corrupt
memory on such controllers.

Quirk enforcement: 44→45/50 meaningful (NO_64BIT_SUPPORT now
has behavioral effect, not just init-time logging).
This commit is contained in:
Red Bear OS
2026-07-07 18:47:54 +03:00
parent 37cbed4c17
commit 4037c383b9
+21 -7
View File
@@ -449,8 +449,13 @@ impl<const N: usize> Xhci<N> {
// Create the command ring with 4096 / 16 (TRB size) entries, so that it uses all of the
// DMA allocation (which is at least a 4k page).
let ac64 = if quirks.contains(crate::xhci::quirks::XhciQuirks::NO_64BIT_SUPPORT) {
false
} else {
cap.ac64()
};
let entries_per_page = PAGE_SIZE / mem::size_of::<Trb>();
let cmd = Ring::new::<N>(cap.ac64(), entries_per_page, true)?;
let cmd = Ring::new::<N>(ac64, entries_per_page, true)?;
let (irq_reactor_sender, irq_reactor_receiver) = crossbeam_channel::unbounded();
@@ -467,11 +472,11 @@ impl<const N: usize> Xhci<N> {
dbs: Arc::new(Mutex::new(dbs)),
run: Mutex::new(run),
dev_ctx: DeviceContextList::new(cap.ac64(), max_slots)?,
dev_ctx: DeviceContextList::new(ac64, max_slots)?,
scratchpad_buf_arr: None, // initialized in init()
cmd: Mutex::new(cmd),
primary_event_ring: Mutex::new(EventRing::new::<N>(cap.ac64())?),
primary_event_ring: Mutex::new(EventRing::new::<N>(ac64)?),
handles: CHashMap::new(),
next_handle: AtomicUsize::new(0),
port_states: CHashMap::new(),
@@ -963,7 +968,7 @@ impl<const N: usize> Xhci<N> {
if buf_count == 0 {
return Ok(());
}
let scratchpad_buf_arr = ScratchpadBufferArray::new::<N>(self.cap.ac64(), buf_count)?;
let scratchpad_buf_arr = ScratchpadBufferArray::new::<N>(self.ac64_effective(), buf_count)?;
self.dev_ctx.dcbaa[0] = scratchpad_buf_arr.register() as u64;
debug!(
"Setting up {} scratchpads, at {:#0x}",
@@ -1025,19 +1030,28 @@ impl<const N: usize> Xhci<N> {
((self.dev_ctx.contexts[slot].slot.d.read() & SLOT_CONTEXT_STATE_MASK)
>> SLOT_CONTEXT_STATE_SHIFT) as u8
}
/// 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) {
false
} else {
self.cap.ac64()
}
}
pub unsafe fn alloc_dma_zeroed_raw<T>(_ac64: bool) -> Result<Dma<T>> {
// TODO: ac64
Ok(Dma::zeroed()?.assume_init())
}
pub unsafe fn alloc_dma_zeroed<T>(&self) -> Result<Dma<T>> {
Self::alloc_dma_zeroed_raw(self.cap.ac64())
Self::alloc_dma_zeroed_raw(self.ac64_effective())
}
pub unsafe fn alloc_dma_zeroed_unsized_raw<T>(_ac64: bool, count: usize) -> Result<Dma<[T]>> {
// TODO: ac64
Ok(Dma::zeroed_slice(count)?.assume_init())
}
pub unsafe fn alloc_dma_zeroed_unsized<T>(&self, count: usize) -> Result<Dma<[T]>> {
Self::alloc_dma_zeroed_unsized_raw(self.cap.ac64(), count)
Self::alloc_dma_zeroed_unsized_raw(self.ac64_effective(), count)
}
pub async fn attach_device(&self, port_id: PortId) -> syscall::Result<()> {
@@ -1356,7 +1370,7 @@ impl<const N: usize> Xhci<N> {
}
}
let mut ring = Ring::new::<N>(self.cap.ac64(), 16, true)?;
let mut ring = Ring::new::<N>(self.ac64_effective(), 16, true)?;
{
input_context.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit).