如何使用PCIDriverKit内存映射PCI BAR?

9
如何将PCI基地址寄存器(BAR)从PCIDriverKit驱动程序(DEXT)内存映射到用户空间应用程序?
通过在用户客户端子类(在驱动程序侧)中实现IOUserClient :: CopyClientMemoryForType,然后从用户空间应用程序调用IOConnectMapMemory64,可以从驱动程序扩展内存映射到应用程序。这已经在this related answer中非常好地解释了。
唯一缺少的部分是获取与所需PCI BAR相对应的IOMemoryDescriptor,以便从CopyClientMemoryForType实现返回它。

示例代码

换句话说,假设有以下简化的代码,那么imaginaryFunctionWhichReturnsTheBARBuffer的实现是什么?

kern_return_t IMPL(MyUserClient, CopyClientMemoryForType) //(uint64_t type, uint64_t *options, IOMemoryDescriptor **memory)
{
    IOMemoryDescriptor* buffer = nullptr;
    
    imaginaryFunctionWhichReturnsTheBARBuffer(ivars->pciDevice /* IOPCIDevice */, kPCIMemoryRangeBAR0, &buffer);

    *memory = buffer;

    return kIOReturnSuccess;
}

在先前的代码中,ivars->pciDevice指的是一个已准备好使用的IOPCIDevice(例如:它已成功匹配、打开并根据最佳实践进行配置)。
这意味着已经可以使用各种配置内存读/写方法来访问所需PCI BAR内存的显式偏移量。缺少的是如何使用这些API(或等效的API)将对应于PCI BAR的整个缓冲区映射到用户空间应用程序。
可能相关的随机注释
  • This answer from the related question: How to allocate memory in a DriverKit system extension and map it to another process? contains the following quote:

    [...] The returned memory descriptor need not be an IOBufferMemoryDescriptor as I've used in the example, it can also be a PCI BAR or whatever.

    This is exactly what I want to do, so at least it sounds like it should be possible. The only remaining question is how to implement it.

  • A similar question has been posted on the Apple forums and even though it hasn't received any answers yet (as of the time of this writing), it does contain some useful pointers.

    It looks like there is a private function in PCIDriverKit with the following signature:

    virtual kern_return_t IOPCIDevice::_CopyDeviceMemoryWithIndex(uint64_t memoryIndex, IOMemoryDescriptor** memory, IOService* forClient)
    

    It's hard to tell what it's supposed to do exactly (since it is undocumented), but the signature does seem to match with the function I'm looking for. However, trying to use it always results in an error code (similar to the one reported in the original forum post). The error code seems to be different depending on which argument is passed as IOService *forClient).

    Another good point from that post is that there is a getDeviceMemoryWithIndex available to kernel extensions (KEXT) which could be used to accomplish what we need (if the driver was implemented as a PCI kernel extension which seems to be deprecated now).

    However, this function doesn't seem to be available to driver extensions (DEXT). So another question of framing this question could be: What's the equivalent getDeviceMemoryWithIndex for PCIDriverKit driver extensions?

1个回答

1

事实证明,IOPCIDevice::_CopyDeviceMemoryWithIndex确实是需要实现这个功能的函数(但它是私有的这一事实仍然很不方便)。

示例代码

下面是一些示例代码,展示了如何实现这个功能(代码使用MyDriver作为驱动程序类名,MyDriverUserClient作为用户客户端)。

MyDriver.cpp实现中相关部分:

struct MyDriver_IVars {
    IOPCIDevice* pciDevice = nullptr;
};

// MyDriver::init/free/Start/Stop/NewUserClient implementation ommited for brevity

IOMemoryDescriptor* MyDriver::copyBarMemory(uint8_t barIndex)
{
    IOMemoryDescriptor* memory;
    uint8_t barMemoryIndex, barMemoryType;
    uint64_t barMemorySize;

    // Warning: error handling is omitted for brevity
    ivars->pciDevice->GetBARInfo(barIndex, &barMemoryIndex, &barMemorySize, &barMemoryType);
    ivars->pciDevice->_CopyDeviceMemoryWithIndex(barMemoryIndex, &memory, this);

    return memory;
}

MyDriverUserClient.cpp 实现的相关部分:

struct MyDriverUserClient_IVars {
    MyDriver* myDriver = nullptr;
};

// MyDriverUserClient::init/free/Start/Stop implementation ommited for brevity

kern_return_t
IMPL(MyDriverUserClient, CopyClientMemoryForType) //(uint64_t type, uint64_t *options, IOMemoryDescriptor **memory)
{
    *memory = ivars->myDriver->copyBARMemory(kPCIMemoryRangeBAR0);

    return kIOReturnSuccess;
}

额外资源

一个完整的实现使用了这种模式,可以在ivshmem.dext项目中找到(该项目为IVSHMEM的macOS驱动实现)。


1
你确定这在基于ARM64的Mac上可行吗?我印象中那里无法进行内存映射BAR。 - pmdj
1
将一些kext移植到arm64e后,我能找到的唯一BAR访问是IOPCIDevice上的MemoryRead*MemoryWrite*方法系列。就我所知,在总线上发生的事情方面,这是有道理的。如果您的设备希望通过BAR交换大量数据,则这很不幸,因为这正是DMA的用途。而DMA在ARM上的工作方式与以前相同(IODMACommand等)。 - pmdj
1
我对IVSHMEM设备的工作原理并不是非常熟悉,但是浏览规格时,似乎确实不支持DMA。这是一个奇怪的选择,因为例如virtio设备在很大程度上基于DMA。 - pmdj
这很有道理。既然如此,我可能会向QEMU项目提出问题,看看是否有计划为IVSHMEM设备添加DMA支持。再次感谢您的建议! - vially
1
我怀疑编写一个补丁程序来实现Qemu的这种可选操作模式可能并不难。(我自己也曾经为Qemu编写过一些补丁程序。) - pmdj
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接