Как выделить память в системном расширении DriverKit и сопоставить ее с другим процессом?
Я выделил память в своем приложении и передал ее указатель и размер в IOConnectCallStructMethod
. С помощью IOMemoryDescriptor::CreateMapping
Затем я сопоставил эту память процессу системного расширения DriverKit, и можно записать в эту сопоставленную ячейку памяти и прочитать данные из моего приложения.
Теперь я хотел бы сделать что-то подобное для памяти, выделенной в расширении системы, а затем сопоставить ее с приложением, использующим расширение системы. Я хотел бы создать набор буферов памяти в расширении системы, а затем записать в него из приложения, а затем передать сигнал расширению системы с помощьюIOConnectCallScalarMethod
что данный буфер должен быть отправлен на USB-устройство, используя IOUSBHostPipe::AsyncIO
. Когда CompleteAsyncIO
Затем обратный вызов происходит в результате завершения отправки, я бы уведомил приложение, что теперь можно скопировать данные в первый отправленный буфер. Механизм для этого, вероятно, можно было бы реализовать с помощьюIOConnectCallAsyncStructMethod
, а OSAction
объект, который создается в расширении системы. Я не понимаю, как сопоставить память, выделенную в расширении системы, с приложением.
1 ответ
Это что IOUserClient::CopyClientMemoryForType
в DriverKit предназначен для, который вызывается, когда ваш пользовательский процесс вызывает IOConnectMapMemory64
от 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 или что-то еще.- В
kIOMapAnywhere
вариант вIOConnectMapMemory64
звонок важен и обычно то, что вы хотите: если вы не укажете это,atAddress
Параметр становится входным-выходным параметром, и ожидается, что вызывающий абонент выберет место в адресном пространстве, куда должна быть отображена память драйвера. Обычно вам все равно, где это, и действительно, указание явного местоположения может быть опасным, если там уже что-то отображается. - Если пользовательское пространство не должно записывать в отображаемую память, установите
options
параметр дляCopyClientMemoryForType
соответственно:*options = kIOUserClientMemoryReadOnly;
Чтобы уничтожить отображение, процесс пользовательского пространства должен вызвать IOConnectUnmapMemory64()
.