* 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
This commit is contained in:
Michael Lotz
2006-09-20 21:12:51 +00:00
parent d912bed187
commit 7145ff1705
6 changed files with 148 additions and 5 deletions
@@ -7,6 +7,10 @@
#include <stdio.h>
#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)
{
@@ -39,6 +39,7 @@ Pipe::SubmitTransfer(Transfer *transfer)
status_t
Pipe::CancelQueuedTransfers()
{
TRACE_ERROR(("Pipe: cancelling transfers is not implemented!\n"));
return B_ERROR;
}
+70 -2
View File
@@ -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(
+1
View File
@@ -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;
+71 -3
View File
@@ -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(
+1
View File
@@ -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;