问题
I have allocated memory in my application and passed its pointer and size to IOConnectCallStructMethod
. Using IOMemoryDescriptor::CreateMapping I have then mapped this memory to the DriverKit system extension process, and it is possible to write to this mapped memory location and read the data from my application.
I would now like to do something similar for memory that is allocated in the system extension, and then map it to the application that is using the system extension. I would like to create a set of memory buffers in the system extension, and then write to it from the application and then signal to the system extension with IOConnectCallScalarMethod
that a given buffer should be sent to the USB device, using IOUSBHostPipe::AsyncIO. When the CompleteAsyncIO callback then comes as a result of the sending completing, I would notify back to the application that it is now possible to copy data to the first buffer that was sent. The mechanism for this could probably be done using IOConnectCallAsyncStructMethod
, and the OSAction
object that is created in the system extension. What I don't understand is how to map memory allocated in the system extension to the application.
回答1:
This is what IOUserClient::CopyClientMemoryForType in DriverKit is for, which gets invoked when your user process calls IOConnectMapMemory64 from IOKit.framework
. The kext equivalent, incidentally, is IOUserClient::clientMemoryForType and essentially works exactly the same.
To make it work, you need to override the CopyClientMemoryForType
virtual function in your user client subclass.
In the class definition in .iig
:
virtual kern_return_t CopyClientMemoryForType(
uint64_t type, uint64_t *options, IOMemoryDescriptor **memory) override;
In the implementation .cpp
, something along these lines:
kern_return_t IMPL(MyUserClient, CopyClientMemoryForType) //(uint64_t type, uint64_t *options, IOMemoryDescriptor **memory)
{
kern_return_t res;
if (type == 0)
{
IOBufferMemoryDescriptor* buffer = nullptr;
res = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, 128 /* capacity */, 8 /* alignment */, &buffer);
if (res != kIOReturnSuccess)
{
os_log(OS_LOG_DEFAULT, "MyUserClient::CopyClientMemoryForType(): IOBufferMemoryDescriptor::Create failed: 0x%x", res);
}
else
{
*memory = buffer; // returned with refcount 1
}
}
else
{
res = this->CopyClientMemoryForType(type, options, memory, SUPERDISPATCH);
}
return res;
}
In user space, you would call:
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
IOReturn res = IOConnectMapMemory64(connection, 0 /*memoryType*/, mach_task_self(), &address, &size, kIOMapAnywhere);
Some notes on this:
- The value in the
type
parameter comes from thememoryType
parameter to theIOConnectMapMemory64
call that caused this function to be called. Your driver therefore can have some kind of numbering convention; in the simplest case you can treat it similarly to the selector in external methods. memory
is effectively an output parameter and this is where you're expected to return the memory descriptor you want to map into user space when your function returnskIOReturnSuccess
. The function has copy semantics, i.e. the caller expects to take ownership of the memory descriptor, i.e. it will eventually drop the reference count by 1 when it is no longer needed. The returned memory descriptor need not be anIOBufferMemoryDescriptor
as I've used in the example, it can also be a PCI BAR or whatever.- The
kIOMapAnywhere
option in theIOConnectMapMemory64
call is important and normally what you want: if you don't specify this, theatAddress
parameter becomes an in-out parameter, and the caller is expected to select a location in the address space where the driver memory should be mapped. Normally you don't care where this is, and indeed specifying an explicit location can be dangerous if there's already something mapped there. - If user space must not write to the mapped memory, set the
options
parameter toCopyClientMemoryForType
accordingly:*options = kIOUserClientMemoryReadOnly;
To destroy the mapping, the user space process must call IOConnectUnmapMemory64()
.
来源:https://stackoverflow.com/questions/62213115/how-to-allocate-memory-in-a-driverkit-system-extension-and-map-it-to-another-pro