3

I am porting a MacOS PCI driver written with IOKit to the new PCIDriverKit framework.

I am able to perform DMA with a contiguous buffer allocated inside the dext (with IOBufferMemoryDescriptor::Create).

But I would also like to perform DMA to and from a buffer allocated by an application.

What I exactly want to do is:

  • Allocate an aligned buffer in the app (with e.g. posix_memalign)
  • Send the pointer to this buffer to the dext
  • In the dext, retrieve the list of pages descriptors to be able to perform DMA without copy into (or from) this buffer.

In IOKit, we could use methods such IOMemoryDescriptor::withAddressRange and IODMACommand::gen64IOVMSegments to map and retrieve the scatter gather list but I cannot find any information on how to do this in a dext with the PCIDriverKit framework.

Can anybody help me on how to do that?

pmdj
  • 22,018
  • 3
  • 52
  • 103

1 Answers1

1

It's not quite the same question as yours, but in my answer to it I go into some detail on memory descriptors in DriverKit extensions.

Basically, in DriverKit you can only access user space memory that was explicitly provided as a buffer; you can't interpret arbitrary data as pointers in the user process's address space and create memory descriptors for them.

The only way to create memory descriptors for user space buffers that I'm aware of is via "struct" arguments to IOUserClient external methods, i.e. when the user process passes them to the IOConnectCallMethod* family of functions as either an input or output "struct". If the buffer is larger than 4096 bytes, it'll show up in the dext as an IOMemoryDescriptor, and you can perform the usual DMA operations with it.

Typically, you'll want to do this in combination with async external methods, which lets you implement sensible buffer lifetime semantics, but your dext can technically hold on to user space supplied memory descriptors past the return of a synchronous external method, or past the async completion of an asynchronous one.

pmdj
  • 22,018
  • 3
  • 52
  • 103
  • Thanks for your answer. So if I understand well, I can pass the pointer of the buffer to the inputStruct argument of the `IOConnectCallStructMethod` and directly cast the `structureInput` object (`OSData`) into an `IOMemoryDescriptor` object at the dext side? – Philippe Cornet Jan 07 '22 at 14:05
  • 1
    @PhilippeCornet No, `OSData` and `IOMemoryDescriptor` are not compatible. However, when the inputStruct buffer is larger than 4096 bytes, the `structureInput` will be `nullptr` and the `structureInputDescriptor` will be an `IOMemoryDescriptor` representing the user space buffer. (It will be mapped as copy-on-write however, so if the process makes any changes after calling the method, the changes will not be reflected on the dext side for *input* structs. Also, the data in buffers 4096 bytes or smaller is *copied*, not mapped, and in this case you get the `OSData` object instead of the IOMD) – pmdj Jan 07 '22 at 14:21
  • Your solution does the job, thanks a lot. And it seems that the data written by the application **after** the call to `IOConnectCallStructMethod` is taken into account. – Philippe Cornet Jan 12 '22 at 10:46