1

I'm using DriverKit in my iPadOS.

I'm trying to create a IOBufferMemoryDescriptor and add the data I've got in an OSData object.

I'm creating the IOBufferMemoryDescriptor with:

    ret = ivars->interface->CreateIOBuffer(kIOMemoryDirectionOut,
                                           length,
                                           &myBuffer);

Although I think it can be created with this too:

    IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOut, length, 0, &mybuffer);

How do I add the data from my OSData object. I've seen examples of:

IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress((void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(), kIODirectionOut);

(from How to use deviceRequest of IOUSBHostDevice / IOUSBHostInterface? )

But that method doesn't seem to work in DriverKit.

I've also tried Mapping the Buffer:

    uint64_t bufferAddress;
    uint64_t bufferLength;
    ret = myBuffer->Map(0, 0, 0, 0, &bufferAddress, &bufferLength);

and then copying the data with:

    memcpy(reinterpret_cast<void*>(bufferAddress), inputOsData->getBytesNoCopy(), length);

But this would always make the driver to crash when tries to run the memcpy

Thanks

xarly
  • 2,054
  • 4
  • 24
  • 40

1 Answers1

1

Update: On first reading I missed that you used kIOMemoryDirectionOut. In my experience, this doesn't seem to work reliably for buffer memory descriptors, probably because write-only mapping isn't available on most CPU architectures. Use kIOMemoryDirectionInOut.

Main answer:

To obtain a pointer to the IOBufferMemoryDescriptor's internal buffer, you need to use the GetAddressRange method. The returned IOAddressSegment struct's address field can then be reinterpret_cast to void* and filled with memcpy or similar.

The API is rather clumsy, so I recommend wrapping it into a helper function. I have something like this:

djt_buffer djt_iobmd_get_byte_range(IOBufferMemoryDescriptor* buffer)
{
    IOAddressSegment range = {};
    kern_return_t ret = buffer->GetAddressRange(&range);
    if (ret != kIOReturnSuccess)
        return djt_buffer{};

    return djt_buffer { reinterpret_cast<void*>(range.address), range.length };
}

where djt_buffer is defined as:

struct djt_buffer
{
    void* bytes;
    size_t size;
};

Note that the CreateIOBuffer method of creating the buffer is preferable when you're going to use the buffer for USB I/O, as it's supposed to prevent the use of bounce buffers. The generic IOBufferMemoryDescriptor::Create() variant is fine for other purposes.

Note also that every memory descriptor creation, every call to GetAddressRange or GetLength, etc. requires a round trip to the kernel, so I recommend re-using/pooling descriptors instead of destroying/recreating them for every IO operation. You can also cache the address pointer and length of each descriptor in your driver.

pmdj
  • 22,018
  • 3
  • 52
  • 103
  • Thanks a lot for your answer @pmdj I'm a bit confused, in order to get the buffer address I can use both methods? The one created by you: djt_iobmd_get_byte_range or CreateMapping and then GetAddress(); and GetLength();? I tried both and they seems to fail in the same point in the memcpy. The first one I do: memcpy(bufferInfo.bytes, &inputChar, bufferInfo.size); using the map I have to do a reinterpret_cast so: memcpy(reinterpret_cast(bufferAddress), &inputChar, length); – xarly Dec 22 '22 at 17:16
  • Interestingly if I use: `IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOut, length, 0, &myBuffer);` instead of `CreateIOBuffer`, it works fine... With both ways btw :) – xarly Dec 22 '22 at 17:21
  • 1
    @xarly Ahh, yes I missed that you used `kIOMemoryDirectionOut`. You need to use `kIOMemoryDirectionInOut`. I'm surprised it works when using `IOBufferMemoryDescriptor::Create`. Note that if you use `CreateMapping` you MUST release the mapping once done or you will cause leaks. Passing the mapping back and forth between kernel and dext will also be more expensive than just using `GetAddressRange()`, so there's no reason to use `CreateMapping` on **Buffer** memory descriptors. (Plain `IOMemoryDescriptor`s don't have `GetAddressRange` however, and do not come pre-mapped.) – pmdj Dec 22 '22 at 18:35
  • 1
    nice, that works!!, I wish Apple gets the documentation a bit better, imposible to find out that was the problem. Thanks a lot again and Merry Christmas :) – xarly Dec 23 '22 at 10:25