如何在 DriverKit 系统扩展中分配内存并将其映射到另一个进程?

tup*_*cat 3 c++ iokit macos-catalina macos-system-extension driverkit

我已在应用程序中分配内存并将其指针和大小传递给 IOConnectCallStructMethod. 然后,IOMemoryDescriptor::CreateMapping我已将此内存映射到 DriverKit 系统扩展进程,并且可以写入此映射的内存位置并从我的应用程序读取数据。

我现在想对系统扩展中分配的内存执行类似的操作,然后将其映射到使用系统扩展的应用程序。我想在系统扩展中创建一组内存缓冲区,然后从应用程序写入它,然后向系统扩展发出信号,告知给IOConnectCallScalarMethod定的缓冲区应发送到 USB 设备,使用IOUSBHostPipe::AsyncIO. 当CompleteAsyncIO发送完成后出现回调时,我会通知应用程序现在可以将数据复制到发送的第一个缓冲区。此机制可能可以使用IOConnectCallAsyncStructMethodOSAction在系统扩展中创建的对象来完成。我不明白的是如何将系统扩展中分配的内存映射到应用程序。

pmd*_*mdj 6

这就是IOUserClient::CopyClientMemoryForTypeDriverKit 的用途,当您的用户进程IOConnectMapMemory64IOKit.framework. 顺便说一句,kext 的等效项IOUserClient::clientMemoryForType在本质上是完全相同的。

为了使其工作,您需要重写CopyClientMemoryForType用户客户端子类中的虚拟函数。

在类定义中.iig

virtual kern_return_t CopyClientMemoryForType(
    uint64_t type, uint64_t *options, IOMemoryDescriptor **memory) override;
Run Code Online (Sandbox Code Playgroud)

在实现中.cpp,大致如下:

kern_return_t IMPL(MyUserClient, CopyClientMemoryForType) //(uint64_t type, uint64_t *options, IOMemoryDescriptor **memory)
{
    kern_return_t res;
    if (type == 0)
    {
        IOBufferMemoryDescriptor* buffer = nullptr;
        res = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, 128 /* capacity */, 8 /* alignment */, &buffer);
        if (res != kIOReturnSuccess)
        {
            os_log(OS_LOG_DEFAULT, "MyUserClient::CopyClientMemoryForType(): IOBufferMemoryDescriptor::Create failed: 0x%x", res);
        }
        else
        {
            *memory = buffer; // returned with refcount 1
        }
    }
    else
    {
        res = this->CopyClientMemoryForType(type, options, memory, SUPERDISPATCH);
    }
    return res;
}
Run Code Online (Sandbox Code Playgroud)

在用户空间中,您可以调用:

    mach_vm_address_t address = 0;
    mach_vm_size_t size = 0;
    IOReturn res = IOConnectMapMemory64(connection, 0 /*memoryType*/, mach_task_self(), &address, &size, kIOMapAnywhere);
Run Code Online (Sandbox Code Playgroud)

关于此的一些注释:

  • 参数中的值type来自导致调用此函数的调用的memoryType参数。IOConnectMapMemory64因此,您的驱动程序可以有某种编号约定;在最简单的情况下,您可以像外部方法中的选择器一样对待它。
  • memory实际上是一个输出参数,当函数返回时,您应该在此处返回要映射到用户空间的内存描述符kIOReturnSuccess。该函数具有复制语义,即调用者期望获得内存描述符的所有权,即当不再需要它时,它最终会将引用计数减1。返回的内存描述符不必是IOBufferMemoryDescriptor我在示例中使用的,它也可以是 PCI BAR 或其他。
  • kIOMapAnywhere调用中的选项很IOConnectMapMemory64重要,通常是您想要的:如果您不指定此选项,则该atAddress参数将成为输入输出参数,并且调用者应在驱动程序内存应映射的地址空间中选择一个位置。通常你并不关心它在哪里,而且如果那里已经映射了一些东西,那么指定一个显式的位置实际上可能是危险的。
  • 如果用户空间不得写入映射内存,则相应地设置options参数CopyClientMemoryForType*options = kIOUserClientMemoryReadOnly;

要破坏映射,用户空间进程必须调用IOConnectUnmapMemory64().