3

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

我现在想对系统扩展中分配的内存做类似的事情,然后将其映射到使用系统扩展的应用程序。我想在系统扩展中创建一组内存缓冲区,然后从应用程序写入它,然后IOConnectCallScalarMethod使用IOUSBHostPipe::AsyncIO. 当CompleteAsyncIO由于发送完成而出现回调时,我会通知应用程序现在可以将数据复制到发送的第一个缓冲区。这种机制可能可以使用IOConnectCallAsyncStructMethod, 以及OSAction在系统扩展中创建的对象来完成。我不明白的是如何将系统扩展中分配的内存映射到应用程序。

4

1 回答 1

4

这就是IOUserClient::CopyClientMemoryForTypeDriverKit 的用途,当您的用户进程IOConnectMapMemory64IOKit.framework. 顺便说一下,kext 等价物的IOUserClient::clientMemoryForType工作原理完全相同。

要使其工作,您需要覆盖CopyClientMemoryForType用户客户端子类中的虚函数。

在类定义中.iig

virtual kern_return_t CopyClientMemoryForType(
    uint64_t type, uint64_t *options, IOMemoryDescriptor **memory) override;

在实现.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;
}

在用户空间中,您会调用:

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

对此的一些说明:

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

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

于 2020-06-05T11:37:39.243 回答