diff --git a/src/add-ons/kernel/bus_managers/usb/BeOSCompatibility.h b/src/add-ons/kernel/bus_managers/usb/BeOSCompatibility.h index 1045a0bc4e..6b46f17f9f 100644 --- a/src/add-ons/kernel/bus_managers/usb/BeOSCompatibility.h +++ b/src/add-ons/kernel/bus_managers/usb/BeOSCompatibility.h @@ -7,6 +7,10 @@ #include +#define IS_USER_ADDRESS(x) (((uint32)x & 0x80000000) > 0) +#define IS_KERNEL_ADDRESS(x) (((uint32)x & 0x80000000) == 0) + + inline status_t benaphore_init(benaphore *ben, const char *name) { diff --git a/src/add-ons/kernel/bus_managers/usb/Pipe.cpp b/src/add-ons/kernel/bus_managers/usb/Pipe.cpp index 28bcc36f35..0a57d5fa4a 100644 --- a/src/add-ons/kernel/bus_managers/usb/Pipe.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Pipe.cpp @@ -39,6 +39,7 @@ Pipe::SubmitTransfer(Transfer *transfer) status_t Pipe::CancelQueuedTransfers() { + TRACE_ERROR(("Pipe: cancelling transfers is not implemented!\n")); return B_ERROR; } diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index 3d62eba4e3..f52b67864e 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -805,9 +805,53 @@ EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead, data->transfer = transfer; data->queue_head = queueHead; data->data_descriptor = dataDescriptor; + data->user_area = -1; data->incoming = directionIn; data->link = NULL; +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + if (directionIn) { + // we might need to access a buffer in userspace. this will not + // be possible in the kernel space finisher thread unless we + // get the proper area id for the space we need and then clone it + // before writing to it. this is of course terribly inefficient... + iovec *vector = transfer->Vector(); + size_t vectorCount = transfer->VectorCount(); + for (size_t i = 0; i < vectorCount; i++) { + if (IS_USER_ADDRESS(vector[i].iov_base)) { + data->user_area = area_for(vector[i].iov_base); + if (data->user_area < B_OK) { + TRACE_ERROR(("usb_ehci: failed to get area of userspace buffer\n")); + delete data; + return B_BAD_ADDRESS; + } + + break; + } + } + + if (data->user_area >= B_OK) { + area_info areaInfo; + if (get_area_info(data->user_area, &areaInfo) < B_OK) { + TRACE_ERROR(("usb_ehci: failed to get info about user area\n")); + delete data; + return B_BAD_ADDRESS; + } + + for (size_t i = 0; i < vectorCount; i++) { + (uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address; + + if ((size_t)vector[i].iov_base > areaInfo.size + || (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) { + TRACE_ERROR(("usb_ehci: output data buffer spans across multiple areas!\n")); + delete data; + return B_BAD_ADDRESS; + } + } + } + } +#endif // !HAIKU_TARGET_PLATFORM_HAIKU + if (!Lock()) { delete data; return B_ERROR; @@ -952,11 +996,35 @@ EHCI::FinishTransfers() uint8 lastDataToggle = 0; if (transfer->data_descriptor && transfer->incoming) { // data to read out + iovec *vector = transfer->transfer->Vector(); + size_t vectorCount = transfer->transfer->VectorCount(); + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + area_id clonedArea = -1; + if (transfer->user_area >= B_OK) { + // we got a userspace output buffer, need to clone + // the area for that space first and map the iovecs + // to this cloned area. + void *clonedMemory = NULL; + clonedArea = clone_area("userspace accessor", + &clonedMemory, B_ANY_ADDRESS, + B_WRITE_AREA | B_KERNEL_WRITE_AREA, + transfer->user_area); + + for (size_t i = 0; i < vectorCount; i++) + (uint8 *)vector[i].iov_base += (addr_t)clonedMemory; + } +#endif // !HAIKU_TARGET_PLATFORM_HAIKU + actualLength = ReadDescriptorChain( transfer->data_descriptor, - transfer->transfer->Vector(), - transfer->transfer->VectorCount(), + vector, vectorCount, &lastDataToggle); + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + if (clonedArea >= B_OK) + delete_area(clonedArea); +#endif // !HAIKU_TARGET_PLATFORM_HAIKU } else { // calculate transfered length actualLength = ReadActualLength( diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 4bd6572b2e..cf73ae7f95 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -22,6 +22,7 @@ typedef struct transfer_data_s { Transfer *transfer; ehci_qh *queue_head; ehci_qtd *data_descriptor; + area_id user_area; bool incoming; transfer_data_s *link; } transfer_data; diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 21685921da..6b431016e3 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -412,7 +412,7 @@ UHCI::UHCI(pci_info *info, Stack *stack) // create semaphore the finisher thread will wait for fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers"); if (fFinishTransfersSem < B_OK) { - TRACE_ERROR(("usb_ehci: failed to create semaphore\n")); + TRACE_ERROR(("usb_uhci: failed to create semaphore\n")); return; } @@ -661,9 +661,53 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue, data->first_descriptor = firstDescriptor; data->data_descriptor = dataDescriptor; data->last_descriptor = lastDescriptor; + data->user_area = -1; data->incoming = directionIn; data->link = NULL; +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + if (directionIn) { + // we might need to access a buffer in userspace. this will not + // be possible in the kernel space finisher thread unless we + // get the proper area id for the space we need and then clone it + // before writing to it. this is of course terribly inefficient... + iovec *vector = transfer->Vector(); + size_t vectorCount = transfer->VectorCount(); + for (size_t i = 0; i < vectorCount; i++) { + if (IS_USER_ADDRESS(vector[i].iov_base)) { + data->user_area = area_for(vector[i].iov_base); + if (data->user_area < B_OK) { + TRACE_ERROR(("usb_uhci: failed to get area of userspace buffer\n")); + delete data; + return B_BAD_ADDRESS; + } + + break; + } + } + + if (data->user_area >= B_OK) { + area_info areaInfo; + if (get_area_info(data->user_area, &areaInfo) < B_OK) { + TRACE_ERROR(("usb_uhci: failed to get info about user area\n")); + delete data; + return B_BAD_ADDRESS; + } + + for (size_t i = 0; i < vectorCount; i++) { + (uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address; + + if ((size_t)vector[i].iov_base > areaInfo.size + || (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) { + TRACE_ERROR(("usb_uhci: output data buffer spans across multiple areas!\n")); + delete data; + return B_BAD_ADDRESS; + } + } + } + } +#endif // !HAIKU_TARGET_PLATFORM_HAIKU + if (!Lock()) { delete data; return B_ERROR; @@ -755,11 +799,35 @@ UHCI::FinishTransfers() uint8 lastDataToggle = 0; if (transfer->data_descriptor && transfer->incoming) { // data to read out + iovec *vector = transfer->transfer->Vector(); + size_t vectorCount = transfer->transfer->VectorCount(); + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + area_id clonedArea = -1; + if (transfer->user_area >= B_OK) { + // we got a userspace output buffer, need to clone + // the area for that space first and map the iovecs + // to this cloned area. + void *clonedMemory = NULL; + clonedArea = clone_area("userspace accessor", + &clonedMemory, B_ANY_ADDRESS, + B_WRITE_AREA | B_KERNEL_WRITE_AREA, + transfer->user_area); + + for (size_t i = 0; i < vectorCount; i++) + (uint8 *)vector[i].iov_base += (addr_t)clonedMemory; + } +#endif // !HAIKU_TARGET_PLATFORM_HAIKU + actualLength = ReadDescriptorChain( transfer->data_descriptor, - transfer->transfer->Vector(), - transfer->transfer->VectorCount(), + vector, vectorCount, &lastDataToggle); + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + if (clonedArea >= B_OK) + delete_area(clonedArea); +#endif // !HAIKU_TARGET_PLATFORM_HAIKU } else { // read the actual length that was sent actualLength = ReadActualLength( diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index bc24cb7bd2..fbca69f1ee 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -57,6 +57,7 @@ typedef struct transfer_data_s { uhci_td *first_descriptor; uhci_td *data_descriptor; uhci_td *last_descriptor; + area_id user_area; bool incoming; transfer_data_s *link; } transfer_data;