【问题标题】:How to memory-map a PCI BAR using PCIDriverKit?如何使用 PCIDriverKit 对 PCI BAR 进行内存映射?
【发布时间】:2021-11-12 17:29:58
【问题描述】:

如何将 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(如:已成功匹配,根据latest best practices打开配置)。

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

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

  • This answer 来自相关问题:How to allocate memory in a DriverKit system extension and map it to another process? 包含以下引用:

    [...] 返回的内存描述符不必是我在示例中使用的 IOBufferMemoryDe​​scriptor,它也可以是 PCI BAR 或其他。

    正是我想做的,所以至少听起来应该是可能的。剩下的唯一问题是如何实现它。

  • A similar question 已发布在 Apple 论坛上,尽管尚未收到任何答复(截至撰写本文时),但它确实包含一些有用的提示。

    在 PCIDriverKit 中似乎有一个 private 函数,其签名如下:

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

    很难说出它应该做什么(因为它没有记录),但签名似乎与我正在寻找的功能相匹配。但是,尝试使用它总是会导致错误代码(类似于原始论坛帖子中报告的错误代码)。错误代码似乎有所不同,具体取决于作为IOService *forClient 传递的参数。

    该帖子的另一个好处是,有一个可用于内核扩展 (KEXT) 的 getDeviceMemoryWithIndex 可用于完成我们需要的工作(如果驱动程序是作为 PCI 内核实现的现在似乎已弃用的扩展名)。

    但是,此功能似乎不适用于驱动程序扩展 (DEXT)。因此,构建这个问题的另一个问题可能是:PCIDriverKit 驱动程序扩展的等效getDeviceMemoryWithIndex 是什么?

【问题讨论】:

    标签: c++ macos iokit driverkit macos-system-extension


    【解决方案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 驱动程序)。

    【讨论】:

    • 您确定这适用于基于 ARM64 的 Mac 吗?我的印象是内存映射 BAR 在那里是不可能的。
    • 这是一个很好的观点,你可能是对的,但不幸的是我没有 arm64 mac 来测试它。对于我的用例(链接的 IVHSMEM 驱动程序),我在 x64 虚拟机上运行 macOS,它似乎工作正常。
    • 将一些 kext 移植到 arm64e 后,我能找到的唯一 BAR 访问是 MemoryRead* and MemoryWrite* family of methods on IOPCIDevice。公平地说,据我所知,就公共汽车上发生的事情而言,这是有道理的。如果您的设备希望通过 BAR 交换大量数据,这很不幸,因为这正是 DMA 的用途。 DMA 像以前一样在 ARM 上工作。 (IODMACommand 等)
    • (我对 IVSHMEM 设备的工作原理并不十分熟悉,虽然看了一眼规范,但似乎确实不支持 DMA。考虑到 virtio 设备大量基于 DMA,这是一个奇怪的选择。 )
    • 我怀疑为 Qemu 编写一个补丁可能并不难,将其作为一种可选的操作模式来实现。 (我自己为 Qemu 编写了奇怪的补丁。)
    猜你喜欢
    • 2014-06-16
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    • 2019-01-10
    相关资源
    最近更新 更多