Как выделить память в системном расширении 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().

Другие вопросы по тегам