From 7145ff1705037d7310ea0d608484279c1dabfa64 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Wed, 20 Sep 2006 21:12:51 +0000 Subject: [PATCH] * Implemented compatibility workaround for BeOS to be able to write user memory from the kernel finisher thread This fixes crashing of the USB Mass Storage driver under R5. It is fully working there now. I hope to be able to do this more cleanly under Haiku though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18894 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../bus_managers/usb/BeOSCompatibility.h | 4 + src/add-ons/kernel/bus_managers/usb/Pipe.cpp | 1 + src/add-ons/kernel/busses/usb/ehci.cpp | 72 +++++++++++++++++- src/add-ons/kernel/busses/usb/ehci.h | 1 + src/add-ons/kernel/busses/usb/uhci.cpp | 74 ++++++++++++++++++- src/add-ons/kernel/busses/usb/uhci.h | 1 + 6 files changed, 148 insertions(+), 5 deletions(-) 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;