我正在尝试了解更多关于DriverKit和内存管理的信息,我读到了这个问题:
如何在DriverKit系统扩展中分配内存并将其映射到另一个进程?
我想了解如何使用IOMemoryDescriptor::CreateMapping
。
我写了一个小应用程序来测试这一点(非常简化的代码):
uint8_t * buffer = new uint8_t[256];
for (int i = 0 ; i < 256 ; i++)
buffer[i] = 0xC6;
clientData in, out;
in.nbytes = 256;
in.pbuffer = buffer;
size_t sout = sizeof(out);
IOConnectCallStructMethod(connection, selector,&in,sizeof(in),&out,&sout);
// out.pbuffer now has new values in it
在我的Kext用户客户端类中,我正在做(我正在简化):
IOReturn UserClient::MyExtFunction(clientData * in, clientData * out, IOByteCount inSize, IOByteCount * outSize)
{
MyFunction(in->nBytes, in->pbuffer);//this will change the content of pbuffer
*out = *in;
}
IOReturn UserClient::MyFunction(SInt32 nBytesToRead,void* pUserBuffer,SInt32* nBytesRead)
{
PrepareBuffer(nBytesToRead,&pBuffer);
...
(call function that will fill pBuffer)
}
IOReturn UserClient::PrepareBuffer(UInt32 nBytes,void** pBuffer);
{
IOMemoryDescriptor * desc = IOMemoryDescriptor::withAddressRange((mach_vm_address_t)*pBuffer,nBytes,direction, owner task);
desc->prepare();
IOMemoryMap * map = desc->map();
*pBuffer = (void*)map->getVirtualAddress();
return kIOReturnSuccess;
}
这就是我不知道如何在DExt中复制的地方,也是我认为我真的不理解CreateMapping
的基本原理的地方。
还是我以前做的不可能?
在我的驱动程序中,这是我不知道如何使用CreateMapping
和IOMemoryMap
的地方,所以这个缓冲区可以映射到内存位置,并用不同的值更新。
我可以创建一个IOBufferMemoryDescriptor
,但如何将它绑定到应用程序的缓冲区?我也不了解CreateMapping
的各种选项。
请注意,在另一个测试应用程序中,我已经成功地使用了IOConnectMapMemory64()
/CopyClientMemoryForType()
,但我想专门了解CreateMapping()。
(我希望它是好的,我编辑了这个问题很多…仍然是新的StackOverflow)
或者我过去所做的是不可能的吗?
简而言之,没有
您正试图映射任意用户进程内存,客户端应用程序没有使用IOKit显式标记为可供驱动程序使用。这与苹果关于安全、安保和沙箱的想法不符,所以DriverKit中没有这种东西。
显然,kexts都很强大,所以这在以前是可能的,事实上,我自己在运送驱动程序时也使用过这种技术,然后在将kexts移植到DriverKit时遇到了麻烦。
据我所知,直接访问客户端进程内存的唯一方法是:
- 通过传递缓冲区>=4097字节作为结构体输入或输出参数到
IOConnectCall…Method()
s,以便它们在驱动程序中作为IOMemoryDescriptor
s到达请注意,这些可以长期保留在驱动程序中,但至少对于输入结构,用户空间端的更新不会反映在驱动程序端,因为使用了写时复制映射因此,它们应该纯粹用于向预期方向发送数据 - 通过用户进程使用CCD_ 12/CCD_ 13将现有的CCD_
这意味着你不能使用像你正在使用的间接数据结构。您必须使用";包装的";结构或索引到持久共享缓冲区中。
通过";包装的";结构我指的是包含标头结构(如clientData
)的缓冲区,该标头结构在连续内存中后面是其他数据(如CCD15),通过偏移量将其引用到该连续内存中。整个连续内存块可以作为输入结构传递。
我已经向苹果公司提交了反馈意见,要求提供一种更强大的机制来在用户客户端和德克斯特之间交换数据;我不知道它是否会实施,但如果这样的设施有用,我建议你也这样做。(举例说明你想用它做什么)我们报告的越多,它发生的可能性就越大。(IOMemoryDescriptor::CreateSubMemoryDescriptor()
是在我提出请求后添加的;我不会声称我是第一个添加的,也不会声称苹果在我提出建议之前不打算添加,但他们正在积极改进DriverKit API。)
问题前的原始答案经过编辑,更加具体:
(保留,因为它一般解释了如何处理外部方法的缓冲区参数,这可能对未来的读者有帮助。)
你的问题有点模糊,但让我看看我是否能算出你在你的kext中做了什么,以及你在你自己的dext:中做了些什么
- 您正在应用程序中调用
IOConnectCallStructMethod(connection, selector, buffer, 256, NULL, NULL);
。这意味着CCD_;结构输入";外部方法的参数 - 因为缓冲区的长度为256字节,小于或等于
sizeof(io_struct_inband_t)
,所以缓冲区的内容会发送到带中的内核——换句话说,它在IOConnectCallStructMethod()
调用时被复制 - 这意味着,在kext的外部方法分派函数中,结构输入是通过传入的
IOExternalMethodArguments
结构中的structureInput
/structureInputSize
字段传递的。structureInput
是内核上下文中的指针,可以直接取消引用指针仅在执行方法调度期间有效,并且在方法同步返回后不能使用 - 如果需要将缓冲区用于设备I/O,则可能需要将其封装在
IOMemoryDescriptor
中。实现这一点的一种方法实际上是通过IOMemoryDescriptor::CreateMapping()
- 如果缓冲区为4097字节或更大,则它将通过
structureInputDescriptor
IOMemoryDescriptor
传递,CCD_27可以直接传递到设备I/O,也可以映射内存以在内核中解引用。此内存描述符直接引用用户进程的内存
DriverKit扩展的功能要有限得多,但外部方法参数的到达方式几乎完全相同。
- 小结构通过指向
OSData
对象的IOUserClientMethodArguments
'structureInput
字段到达。您可以通过getBytesNoCopy()
/getLength()
方法访问内容 - 如果您需要
IOMemoryDescriptor
中的这些数据来进行后续I/O,我所知道的唯一方法是使用IOUSBHostDevice::CreateIOBuffer()
或IOBufferMemoryDescriptor::Create
创建IOBufferMemoryDescriptor
,然后将数据从OSData
对象复制到缓冲区中 - 大的缓冲器再次已经通过CCD_ 39被引用。您可以将其传递给I/O函数,或者使用
CreateMapping()
将其映射到驱动程序的地址空间
namespace
{
/*
**********************************************************************************
** create a memory descriptor and map its address
**********************************************************************************
*/
IOReturn arcmsr_userclient_create_memory_descriptor_and_map_address(const void* address, size_t length, IOMemoryDescriptor** memory_descriptor)
{
IOBufferMemoryDescriptor *buffer_memory_descriptor = nullptr;
uint64_t buffer_address;
uint64_t len;
#if ARCMSR_DEBUG_IO_USER_CLIENT
arcmsr_debug_print("ArcMSRUserClient: *******************************************************n");
arcmsr_debug_print("ArcMSRUserClient: ** IOUserClient IOMemoryDescriptor create_with_bytes n");
arcmsr_debug_print("ArcMSRUserClient: *******************************************************n");
#endif
if (!address || !memory_descriptor)
{
return kIOReturnBadArgument;
}
if (IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, length, 0, &buffer_memory_descriptor) != kIOReturnSuccess)
{
if (buffer_memory_descriptor)
{
OSSafeReleaseNULL(buffer_memory_descriptor);
}
return kIOReturnError;
}
if (buffer_memory_descriptor->Map(0, 0, 0, 0, &buffer_address, &len) != kIOReturnSuccess)
{
if (buffer_memory_descriptor)
{
OSSafeReleaseNULL(buffer_memory_descriptor);
}
return kIOReturnError;
}
if (length != len)
{
if (buffer_memory_descriptor)
{
OSSafeReleaseNULL(buffer_memory_descriptor);
}
return kIOReturnNoMemory;
}
memcpy(reinterpret_cast<void*>(buffer_address), address, length);
*memory_descriptor = buffer_memory_descriptor;
return kIOReturnSuccess;
}
} /* namespace */