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

How to memory-map a PCI BAR using PCIDriverKit?

如何将 PCI 基地址寄存器 (BAR) 从 PCIDriverKit 驱动程序 (DEXT) 内存映射到用户space 应用程序?

从驱动程序扩展到应用程序的内存映射可以通过实现 IOUserClient::CopyClientMemoryForType in the user client subclass (on the driver side) and then calling IOConnectMapMemory64 (from the user-space application side). This has been very nicely and thoroughly explained in .

唯一缺少的一点是获取对应于所需 PCI BAR 的 IOMemoryDescriptor,以便从 CopyClientMemoryForType 实现中 return 它。

示例代码

换个方式问,给定以下简化代码,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 (e.g.: it has been succesfully matched, opened and configured according to latest best practices)。

这意味着已经可以使用各种 configuration and memory read/write 方法从所需的 PCI BAR 内存访问显式偏移量。缺少(或不清楚)的是如何使用这些 API(或等效 API)将对应于 PCI BAR 的整个缓冲区映射到用户-space 应用程序。

可能相关也可能不相关的随机注释

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

示例代码

下面是一些示例代码,展示了如何实现(代码使用 MyDriver 驱动程序 class 名称和 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 project (which implements a macOS driver for IVSHMEM).

中找到使用此模式的完整实现