Доступ к буферам данных в драйверах WDF (KMDF или UMDF)

Когда драйвер Windows Driver Frameworks (WDF) получает запрос на чтение, запись или управление вводом-выводом устройства, объект запроса содержит входной буфер, выходной буфер или и то, и другое.

Входные буферы содержат сведения, необходимые драйверу. Для запросов на запись эти сведения обычно являются данными, которые драйвер функции должен отправить на устройство. Для запросов управления вводом-выводом устройства входной буфер может содержать сведения, указывающие тип операции, которую должен выполнить драйвер.

Выходные буферы получают сведения от драйвера. Для запросов на чтение эти сведения обычно являются данными, которые драйвер функции получает от устройства. Для запросов управления вводом-выводом устройства выходной буфер может получать состояние или другие сведения, указанные в коде элемента управления ввода-вывода запроса.

Метод, используемый драйвером для доступа к буферам данных запроса, зависит от метода драйвера для доступа к буферам данных для устройства. Существует три метода доступа:

  • Буферизованное ввод-вывод. Диспетчер ввода-вывода создает промежуточные буферы, совместно с которыми он использует драйвер.
  • Прямой ввод-вывод. Диспетчер ввода-вывода блокирует буферное пространство в физической памяти, а затем предоставляет драйверу прямой доступ к буферу.
  • Ни буферизованного, ни прямого ввода-вывода. Диспетчер ввода-вывода предоставляет драйверу виртуальные адреса буферного пространства запроса. Диспетчер операций ввода-вывода не проверяет буферное пространство запроса, поэтому драйвер должен убедиться, что буферное пространство доступно, и заблокировать буферное пространство в физической памяти.

Драйвер Kernel-Mode Driver Framework (KMDF) может использовать любой из трех методов доступа. Драйвер User-Mode Driver Framework (UMDF) может использовать буферизацию или прямой ввод-вывод для запросов чтения, записи и IOCTL, а также может преобразовывать запросы, указывающие метод METHOD_NEITHER.

Указание метода доступа к буферу

Драйверы KMDF

Для запросов на чтение и запись все драйверы в стеке драйверов должны использовать один и тот же метод для доступа к буферам устройства, за исключением драйвера самого высокого уровня, который может использовать метод "ни один", независимо от того, какой метод используется более низкими драйверами.

Начиная с версии 1.13 драйвер KMDF определяет метод доступа для всех запросов на чтение и запись устройства, вызывая WdfDeviceInitSetIoTypeEx для каждого устройства. Например, если драйвер указывает метод буферизованного ввода-вывода для одного из своих устройств, диспетчер ввода-вывода использует метод буферизованного ввода-вывода при доставке запросов на чтение и запись в драйвер для этого устройства.

Для запросов управления вводом-выводом устройства код управления вводом-выводом (IOCTL) содержит биты, определяющие метод доступа к буферу. В результате драйверу KMDF не нужно предпринимать никаких действий для выбора метода буферизации для списков IOCTL. Дополнительные сведения о ioCTL см. в разделе Определение кодов элементов управления ввода-вывода. В отличие от запросов на чтение и запись, все ioCTL устройства не должны указывать один и тот же метод доступа.

Драйверы UMDF

Драйвер UMDF задает параметры метода доступа, который платформа использует для запросов на чтение и запись, а также для запросов управления вводом-выводом устройства. Значения, предоставляемые драйвером UMDF, являются только предпочтениями и не гарантируются для использования платформой. Дополнительные сведения см. в статье Управление методами доступа к буферу в драйверах UMDF.

Драйвер UMDF задает метод доступа для всех запросов на чтение, запись и IOCTL устройства, вызывая WdfDeviceInitSetIoTypeEx для каждого устройства. Например, если драйвер указывает метод буферизованного ввода-вывода для одного из своих устройств, платформа использует метод буферизованного ввода-вывода при доставке запросов на чтение, запись и IOCTL к драйверу для этого устройства.

Обратите внимание на разницу в методе доступа к буферу для ioCTL между KMDF и UMDF. Драйверы KMDF не указывают метод доступа к буферу для IOCTL, в то время как драйверы UMDF указывают метод доступа к буферу для ioCTLs.

Если драйвер WDF описывает буфер запроса ввода-вывода с помощью метода, который является неправильным для метода ввода-вывода, используемого целевым объектом ввода-вывода, платформа исправляет описание буфера. Например, если драйвер использует MDL для описания буфера, который он передает в WdfIoTargetSendReadSynchronous, а целевой объект ввода-вывода использует буферный ввод-вывод (что требует, чтобы буферы были указаны с помощью виртуальных адресов, а не многомерных выражений), платформа преобразует описание буфера из MDL в виртуальный адрес и длину. Однако это более эффективно, если драйвер задает буферы в правильном формате.

Сведения об объектах памяти платформы, списках lookaside, mdls и локальных буферах см. в статье Использование буферов памяти.

Сведения об удалении буферов памяти см. в разделе Жизненный цикл буфера памяти.

Доступ к буферам данных для буферизованного ввода-вывода

Если драйвер использует буферизированные операции ввода-вывода, его поведение меняется в зависимости от типа запроса данных и использования KMDF или UMDF.

Драйверы KMDF

Если драйвер KMDF использует буферизированные операции ввода-вывода, диспетчер ввода-вывода создает один промежуточный буфер, к которому драйвер может получить доступ для каждого типа запроса. Происходит следующее:

  • Запись запросов. Диспетчер ввода-вывода передает входную информацию из входного буфера вызывающего приложения перед вызовом стека драйверов. Затем драйвер KMDF считывает входные данные из промежуточного буфера и записывает их на устройство.
  • Запросы на чтение. Драйвер KMDF считывает сведения с устройства и сохраняет их в промежуточном буфере. Затем диспетчер ввода-вывода копирует выходные данные из промежуточного буфера в выходной буфер приложения.
  • Запросы управления вводом-выводом устройства. Драйвер KMDF считывает или записывает данные для этого запроса в промежуточный буфер или из нее.

Драйверы UMDF

Если драйвер UMDF использует буферизированные операции ввода-вывода, процесс узла драйвера создает один или два промежуточных буфера в зависимости от типа запроса. Происходит следующее:

  • Запись запросов. Платформа создает один буфер, передает входную информацию из входного буфера вызывающего приложения, а затем вызывает стек драйверов. Драйвер UMDF считывает входные данные из промежуточного буфера и записывает их на устройство.
  • Запросы на чтение. Драйвер UMDF считывает сведения с устройства и сохраняет их в буфере, созданном платформой. Ведущий процесс драйвера копирует выходные данные из промежуточного буфера в выходной буфер приложения.
  • Запросы управления вводом-выводом устройства. Платформа создает два буфера, соответствующие входным и выходным буферам IOCTL, к которым может получить доступ драйвер. Платформа копирует входные данные из IOCTL в новый промежуточный буфер и делает их доступными для драйвера. Платформа не копирует содержимое выходного буфера, поэтому драйвер не должен пытаться считывать из него (в противном случае он будет считывать данные мусора). Все данные, которые драйвер записывает в выходной буфер, копируются обратно в исходный буфер IOCTL и возвращаются приложению после успешного выполнения запроса ввода-вывода. Обратите внимание, что все данные, которые драйвер записывает во входной буфер, удаляются и не возвращаются в вызывающее приложение.

Чтобы получить дескриптор объекта памяти платформы, который представляет буфер, драйверы KMDF и UMDF вызывают WdfRequestRetrieveInputMemory или WdfRequestRetrieveOutputMemory в зависимости от того, является ли это запросом на чтение или запись. Затем драйвер может получить указатель на буфер, вызвав WdfMemoryGetBuffer. Для чтения и записи буфера драйвер вызывает WdfMemoryCopyFromBuffer или WdfMemoryCopyToBuffer.

Чтобы получить виртуальный адрес и длину буфера, драйвер вызывает WdfRequestRetrieveInputBuffer или WdfRequestRetrieveOutputBuffer.

Чтобы выделить и создать список дескрипторов памяти (MDL) для буфера, драйвер KMDF вызывает WdfRequestRetrieveInputWdmMdl или WdfRequestRetrieveOutputWdmMdl.

Доступ к буферам данных для прямого ввода-вывода

Драйверы KMDF

Если драйвер использует прямой ввод-вывод, диспетчер операций ввода-вывода проверяет доступность буферного пространства, указанного инициатором запроса ввода-вывода (обычно это приложение в пользовательском режиме), блокирует буферное пространство в физической памяти, а затем предоставляет драйверу прямой доступ к буферу.

Драйверы UMDF

Если драйвер указал предпочтение для прямого ввода-вывода и все требования UMDF для прямого ввода-вывода выполнены (см. раздел Управление методами доступа к буферу в драйверах UMDF), платформа сопоставляет буфер памяти, получаемый от диспетчера ввода-вывода, непосредственно в адресное пространство хост-процесса драйвера и, таким образом, предоставляет драйверу прямой доступ к буферу.

Чтобы получить дескриптор для объекта памяти платформы, представляющего буферное пространство, драйвер вызывает WdfRequestRetrieveInputMemory или WdfRequestRetrieveOutputMemory. Затем драйвер может получить указатель на буфер, вызвав WdfMemoryGetBuffer. Для чтения и записи буфера драйвер вызывает WdfMemoryCopyFromBuffer или WdfMemoryCopyToBuffer.

Чтобы получить виртуальный адрес и длину буферного пространства, драйвер вызывает WdfRequestRetrieveInputBuffer или WdfRequestRetrieveOutputBuffer.

Если драйверы устройства используют прямой ввод-вывод, диспетчер ввода-вывода описывает буферы с помощью mdl. Чтобы получить указатель на MDL буфера, драйвер KMDF вызывает WdfRequestRetrieveInputWdmMdl или WdfRequestRetrieveOutputWdmMdl. Драйвер UMDF не может получить доступ к спискам MDL.

Доступ к буферам данных для операций ввода-вывода без буферизации или прямого ввода-вывода

Драйверы KMDF

Если драйвер использует метод доступа к буферу, известный как метод не буферизованного ввода-вывода или метод прямого ввода-вывода (или, короче говоря, метод "ни то, ни другой"), диспетчер операций ввода-вывода просто предоставляет драйверу виртуальные адреса, указанные инициатором запроса ввода-вывода для буферного пространства запроса. Диспетчер ввода-вывода не проверяет буферное пространство запроса ввода-вывода, поэтому драйвер должен убедиться, что буферное пространство доступно, и заблокировать буферное пространство в физической памяти.

Доступ к виртуальным адресам, которые предоставляет диспетчер ввода-вывода, можно получить только в контексте процесса инициатора запроса ввода-вывода. Только драйвер самого высокого уровня в стеке драйверов гарантированно будет выполняться в контексте процесса инициатора.

Чтобы получить доступ к буферу запроса ввода-вывода, драйвер самого высокого уровня должен предоставить функцию обратного вызова EvtIoInCallerContext . Платформа вызывает эту функцию обратного вызова каждый раз, когда получает запрос ввода-вывода для драйвера.

Если метод доступа к буферу запроса не имеет значения "ни", драйвер KMDF должен выполнить следующие действия для каждого буфера:

  1. Вызовите метод WdfRequestRetrieveUnsafeUserInputBuffer или WdfRequestRetrieveUnsafeUserOutputBuffer , чтобы получить виртуальный адрес буфера.

  2. Вызовите метод WdfRequestProbeAndLockUserBufferForRead или WdfRequestProbeAndLockUserBufferForWrite для проверки и блокировки буфера и получения дескриптора объекта памяти платформы для буфера.

  3. Сохраните дескрипторы объектов памяти в контекстном пространстве запроса.

  4. Вызовите WdfDeviceEnqueueRequest, который возвращает запрос к платформе.

Платформа впоследствии добавляет запрос в одну из очередей ввода-вывода драйвера. Если драйвер предоставил обработчики запросов, платформа в конечном итоге вызовет соответствующий обработчик запросов.

Обработчик запросов может получить дескрипторы объекта памяти запроса из контекстного пространства запроса. Драйвер может передать дескриптор wdfMemoryGetBuffer , чтобы получить адрес буфера.

Иногда драйверу самого высокого уровня приходится использовать описанные выше шаги для доступа к буферу пользовательского режима, даже если драйвер не использует метод доступа "ни то, ни то, ни то. Например, предположим, что драйвер использует буферизированные операции ввода-вывода. Код элемента управления вводом-выводом, использующий метод буферизованного доступа, может передавать структуру, содержащую внедренный указатель, в буфер пользовательского режима. В этом случае драйвер должен предоставить функцию обратного вызова EvtIoInCallerContext , которая извлекает указатели из структуры, а затем использует предыдущие шаги 2–4.

Драйверы UMDF

UMDF не поддерживает буферные или прямые буферы типа ввода-вывода, поэтому драйверу UMDF никогда не нужно обрабатывать этот тип буфера напрямую.

Однако если платформа получает такие буферы для чтения или записи от диспетчера ввода-вывода, она делает их доступными для драйвера UMDF в качестве буферизованного ввода-вывода или прямого ввода-вывода в зависимости от метода доступа, выбранного драйвером. Если платформа получает IOCTL с указанием метода буфера "ни", она может при необходимости преобразовать метод доступа к буферу запроса IOCTL в буферный ввод-вывод или прямой ввод-вывод на основе наличия директивы INF. Дополнительные сведения см. в статье Управление методами доступа к буферу в драйверах UMDF .