Or is what I used to do not possible?
In short, no.
You're attempting to map arbitrary user process memory, which the client application did not explicitly mark as available to the driver using IOKit. This doesn't fit with Apple's ideas about safety, security, and sandboxing, so this sort of thing isn't available in DriverKit.
Obviously, kexts are all-powerful, so this was possible before, and indeed, I've used the technique myself in shipping drivers and then ran into trouble when porting said kexts to DriverKit.
The only ways to gain direct access to the client process's memory, as far as I'm aware, are:
- By passing buffers >= 4097 bytes as struct input or output arguments to
IOConnectCall…Method()
s so they arrive as IOMemoryDescriptor
s in the driver. Note that these can be retained in the driver longer term, but at least for input structs, updates on the user space side won't be reflected on the driver side as a copy-on-write mapping is used. So they should be used purely for sending data in the intended direction.
- By the user process mapping an existing
IOMemoryDescriptor
into its space using IOConnectMapMemory64()
/CopyClientMemoryForType()
.
This does mean you can't use indirect data structures like the one you are using. You'll have to use "packed" structures, or indices into long-lasting shared buffers.
By "packed" structures I mean buffers containing a header struct such as your clientData
which is followed in contiguous memory by further data, such as your buffer
, referencing it by offset into this contiguous memory. The whole contiguous memory block can be passed as an input struct.
I have filed feedback with Apple requesting a more powerful mechanism for exchanging data between user clients and dexts; I have no idea if it will be implemented, but if such a facility would be useful, I recommend you do the same. (explaining what you'd like to use it for with examples) The more of us report it, the more likely it'll happen. (IOMemoryDescriptor::CreateSubMemoryDescriptor()
was added after I filed a request for it; I won't claim I was the first to do so, or that Apple wasn't planning to add it prior to my suggestion, but they are actively improving the DriverKit APIs.)
Original answer before question was edited to be much more specific:
(Retained because it explains in general terms how buffer arguments to external methods are handled, which is likely helpful for future readers.)
Your question is a little vague, but let me see if I can work out what you did in your kext, vs what you're doing in your dext:
- You're calling
IOConnectCallStructMethod(connection, selector, buffer, 256, NULL, NULL);
in your app. This means buffer
is passed as a "struct input" argument to your external method.
- Because your buffer is 256 bytes long, which is less than or equal to
sizeof(io_struct_inband_t)
, the contents of the buffer is sent to the kernel in-band - in other words, it's copied at the time of the IOConnectCallStructMethod()
call.
- This means that in your kext's external method dispatch function, the struct input is passed via the
structureInput
/structureInputSize
fields in the incoming IOExternalMethodArguments
struct. structureInput
is a pointer in the kernel context and can be dereferenced directly. The pointer is only valid during execution of your method dispatch, and can't be used once the method has returned synchronously.
- If you need to use the buffer for device I/O, you may need to wrap it in an
IOMemoryDescriptor
. One way to do this is indeed via IOMemoryDescriptor::CreateMapping()
.
- If the buffer was 4097 bytes or larger, it would be passed via the
structureInputDescriptor
IOMemoryDescriptor
, which can either be passed along to device I/O directly, or memory-mapped for dereferencing in the kernel. This memory descriptor directly references the user process's memory.
DriverKit extensions are considerably more limited in what they can do, but external method arguments arrive in almost exactly the same way.
- Small structs arrive via the
IOUserClientMethodArguments
' structureInput
field, which points to an OSData
object. You can access the content via the getBytesNoCopy()
/getLength()
methods.
- If you need this data in an
IOMemoryDescriptor
for onward I/O, the only way I know of is to create an IOBufferMemoryDescriptor
using either IOUSBHostDevice::CreateIOBuffer()
or IOBufferMemoryDescriptor::Create
and then copying the data from the OSData
object into the buffer.
- Large buffers are again already referenced via an
IOMemoryDescriptor
. You can pass this on to I/O functions, or map it into the driver's address space using CreateMapping()