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 <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
Augustin Cavalier
2025-08-27 16:56:11 +00:00
committed by waddlesplash
parent a317b36cfc
commit a940b0fa8f
2 changed files with 50 additions and 10 deletions
+49 -10
View File
@@ -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) {
@@ -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;