这是什么IOUserClient::CopyClientMemoryForType在DriverKit中是for,当你的用户进程调用时它会被调用IOConnectMapMemory64 from IOKit.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 或其他任何东西。
- The
kIOMapAnywhere
选项中的IOConnectMapMemory64
调用很重要,通常是您想要的:如果您不指定这一点,则atAddress
参数成为输入输出参数,并且调用者需要在地址空间中选择驱动程序内存应该映射的位置。通常,您并不关心它在哪里,而且如果那里已经映射了某些内容,那么指定显式位置确实可能很危险。
- 如果用户空间不得写入映射内存,请设置
options
参数为CopyClientMemoryForType
因此:*options = kIOUserClientMemoryReadOnly;
要破坏映射,用户空间进程必须调用IOConnectUnmapMemory64()
.