From a940b0fa8f08ede800dbcf838161da686eb7dc5d Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Tue, 26 Aug 2025 00:17:54 -0400 Subject: [PATCH] XHCI: Submit physical transfers directly without a bounce buffer. XHCI's DMA requirements are very relaxed, we can set TRB addresses without having to align everything to page boundaries, it seems. The only restriction is that some controllers may not support 64-bit DMA, but we only do 32-bit at present anyway, so just reject transfers with non-32-bit addresses and add a constraint in the USB disk driver. Tested with QEMU, seems to be working. Change-Id: I6a2b1689ce1a718aaba7622addbd8ab2fc57ca75 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9612 Reviewed-by: waddlesplash Tested-by: Commit checker robot --- src/add-ons/kernel/busses/usb/xhci.cpp | 59 +++++++++++++++---- .../drivers/disk/usb/usb_disk/usb_disk.cpp | 1 + 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index 11ccdaaa72..f02f1a7faa 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -874,6 +874,11 @@ XHCI::SubmitControlRequest(Transfer *transfer) return B_NO_INIT; } + if (transfer->IsPhysical()) { + // We don't handle this case. + return B_NOT_SUPPORTED; + } + status_t status = transfer->InitKernelAccess(); if (status != B_OK) return status; @@ -904,13 +909,13 @@ XHCI::SubmitControlRequest(Transfer *transfer) | TRB_2_BYTES(requestData->Length) | TRB_2_TD_SIZE(0); descriptor->trbs[index].flags = TRB_3_TYPE(TRB_TYPE_DATA_STAGE) - | (directionIn ? TRB_3_DIR_IN : 0) - | TRB_3_CYCLE_BIT; + | (directionIn ? TRB_3_DIR_IN : 0) + | TRB_3_CYCLE_BIT; if (!directionIn) { transfer->PrepareKernelAccess(); - WriteDescriptor(descriptor, transfer->Vector(), - transfer->VectorCount(), transfer->IsPhysical()); + memcpy(descriptor->buffers[0], + (uint8 *)transfer->Vector()[0].base, requestData->Length); } index++; @@ -982,7 +987,24 @@ XHCI::SubmitNormalRequest(Transfer *transfer) } // Now that we know trbSize, compute the count. - const int32 trbCount = (transfer->FragmentLength() + trbSize - 1) / trbSize; + int32 trbCount = (transfer->FragmentLength() + trbSize - 1) / trbSize; + + generic_io_vec* transferVec = transfer->Vector(); + generic_size_t transferVecOffset = 0; + if (transfer->IsPhysical()) { + trbSize = 0; + trbCount = 0; + + for (size_t i = 0; i < transfer->VectorCount(); i++) { + // There's an XHCI context parameter to indicate if the controller is + // 64-bit capable, but for consistency we require 32-bit DMA. + if ((transferVec[i].base + transferVec[i].length) > UINT32_MAX) + return B_BAD_VALUE; + + trbCount += (transferVec[i].length + endpoint->max_burst_payload - 1) + / endpoint->max_burst_payload; + } + } xhci_td *td = CreateDescriptor(trbCount, trbCount, trbSize); if (td == NULL) @@ -992,7 +1014,24 @@ XHCI::SubmitNormalRequest(Transfer *transfer) const size_t maxPacketSize = pipe->MaxPacketSize(); size_t remaining = transfer->FragmentLength(); for (int32 i = 0; i < trbCount; i++) { - int32 trbLength = (remaining < trbSize) ? remaining : trbSize; + phys_addr_t address; + generic_size_t trbLength; + if (!transfer->IsPhysical()) { + address = td->buffer_addrs[i]; + trbLength = (remaining < trbSize) ? remaining : trbSize; + } else { + address = transferVec->base + transferVecOffset; + trbLength = transferVec->length - transferVecOffset; + if (trbLength > endpoint->max_burst_payload) + trbLength = endpoint->max_burst_payload; + + transferVecOffset += trbLength; + if (transferVecOffset == transferVec->length) { + transferVec++; + transferVecOffset = 0; + } + } + remaining -= trbLength; // The "TD Size" field of a transfer TRB indicates the number of @@ -1003,7 +1042,7 @@ XHCI::SubmitNormalRequest(Transfer *transfer) if (tdSize > 31) tdSize = 31; - td->trbs[i].address = td->buffer_addrs[i]; + td->trbs[i].address = address; td->trbs[i].status = TRB_2_IRQ(0) | TRB_2_BYTES(trbLength) | TRB_2_TD_SIZE(tdSize); @@ -1061,7 +1100,7 @@ XHCI::SubmitNormalRequest(Transfer *transfer) // ENT bit. (XHCI 1.2 ยง 4.12.3 p250.) td->trbs[td->trb_used - 1].flags |= TRB_3_ENT_BIT; - if (!directionIn) { + if (!directionIn && !transfer->IsPhysical()) { TRACE("copying out iov count %ld\n", transfer->VectorCount()); status_t status = transfer->PrepareKernelAccess(); if (status != B_OK) { @@ -1261,7 +1300,7 @@ XHCI::CheckDebugTransfer(Transfer *transfer) status_t status = (td->trb_completion_code == COMP_SUCCESS || td->trb_completion_code == COMP_SHORT_PACKET) ? B_OK : B_ERROR; - if (status == B_OK && directionIn) { + if (status == B_OK && directionIn && !transfer->IsPhysical()) { ReadDescriptor(td, transfer->Vector(), transfer->VectorCount(), transfer->IsPhysical()); } @@ -3165,7 +3204,7 @@ XHCI::FinishTransfers() actualLength); } - if (directionIn && actualLength > 0) { + if (directionIn && actualLength > 0 && !transfer->IsPhysical()) { TRACE("copying in iov count %ld\n", transfer->VectorCount()); status_t status = transfer->PrepareKernelAccess(); if (status == B_OK) { diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp index c5be3c6f31..9ddd53bfde 100644 --- a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp @@ -956,6 +956,7 @@ usb_disk_update_capacity(device_lun *lun) if (lun->io_scheduler == NULL) { dma_restrictions restrictions = {}; + restrictions.high_address = UINT32_MAX; restrictions.max_transfer_size = (lun->block_size * MAX_IO_BLOCKS); DMAResource* dmaResource = new DMAResource;