Acessando buffers de dados em drivers WDF (KMDF ou UMDF)
Quando um driver do WDF (Windows Driver Frameworks) recebe uma solicitação de controle de leitura, gravação ou E/S do dispositivo, o objeto de solicitação contém um buffer de entrada, um buffer de saída ou ambos.
Os buffers de entrada contêm informações de que o driver precisa. Para solicitações de gravação, essas informações normalmente são dados que um driver de função deve enviar para um dispositivo. Para solicitações de controle de E/S do dispositivo, um buffer de entrada pode conter informações que indicam o tipo de operação que o driver deve executar.
Os buffers de saída recebem informações do driver. Para solicitações de leitura, essas informações normalmente são dados que um driver de função recebe de um dispositivo. Para solicitações de controle de E/S do dispositivo, um buffer de saída pode receber status ou outras informações especificadas pelo código de controle de E/S da solicitação.
A técnica usada pelo driver para acessar os buffers de dados de uma solicitação depende do método do driver para acessar buffers de dados de um dispositivo. Há três métodos de acesso:
- E/S em buffer. O gerenciador de E/S cria buffers intermediários que compartilha com o driver.
- E/S direta. O gerenciador de E/S bloqueia o espaço de buffer na memória física e fornece ao driver acesso direto ao espaço de buffer.
- Nem em buffer nem E/S direta. O gerenciador de E/S fornece ao driver os endereços virtuais do espaço de buffer da solicitação. O gerenciador de E/S não valida o espaço de buffer da solicitação, portanto, o driver deve verificar se o espaço do buffer está acessível e bloquear o espaço do buffer na memória física.
Um driver KMDF (Kernel-Mode Driver Framework) pode usar qualquer um dos três métodos de acesso. Um driver umDF (User-Mode Driver Framework) pode usar e/S em buffer ou direto para solicitações de leitura, gravação e IOCTL e pode converter solicitações que especificam o método METHOD_NEITHER.
Especificando o método de acesso ao buffer
Para solicitações de leitura e gravação, todos os drivers em uma pilha de driver devem usar o mesmo método para acessar os buffers de um dispositivo, exceto para o driver de nível mais alto, que pode usar o método "nem", independentemente de qual método é usado por drivers inferiores.
A partir da versão 1.13, um driver KMDF especifica o método de acesso para todas as solicitações de leitura e gravação de um dispositivo chamando WdfDeviceInitSetIoTypeEx para cada dispositivo. Por exemplo, se um driver especificar o método de E/S armazenado em buffer para um de seus dispositivos, o gerenciador de E/S usará o método de E/S armazenado em buffer ao fornecer solicitações de leitura e gravação para o driver desse dispositivo.
Para solicitações de controle de E/S do dispositivo, o IOCTL (código de controle de E/S) contém bits que especificam o método de acesso ao buffer. Como resultado, um driver KMDF não precisa executar nenhuma ação para selecionar um método de buffer para IOCTLs. Para obter mais informações sobre IOCTLs, consulte Definindo códigos de controle de E/S. Ao contrário das solicitações de leitura e gravação, todas as IOCTLs de um dispositivo não precisam especificar o mesmo método de acesso.
Um driver UMDF especifica preferências para o método de acesso que a estrutura usa para solicitações de leitura e gravação, bem como solicitações de controle de E/S do dispositivo. Os valores que um driver UMDF fornece são apenas preferências e não têm garantia de serem usados pela estrutura. Para obter mais informações, consulte Gerenciando métodos de acesso de buffer em drivers UMDF.
Um driver UMDF especifica o método de acesso para todas as solicitações de leitura, gravação e IOCTL de um dispositivo chamando WdfDeviceInitSetIoTypeEx para cada dispositivo. Por exemplo, se um driver especificar o método de E/S armazenado em buffer para um de seus dispositivos, a estrutura usará o método de E/S armazenado em buffer ao fornecer solicitações de leitura, gravação e IOCTL para o driver desse dispositivo.
Observe a diferença na técnica de acesso de buffer para IOCTLs entre KMDF e UMDF. Os drivers KMDF não especificam o método de acesso de buffer para IOCTLs, enquanto os drivers UMDF especificam o método de acesso de buffer para IOCTLs.
Se um driver WDF descrever o buffer de uma solicitação de E/S usando uma técnica incorreta para o método de E/S que um destino de E/S usa, a estrutura corrigirá a descrição do buffer. Por exemplo, se um driver usa um MDL para descrever um buffer que ele passa para WdfIoTargetSendReadSynchronously e se o destino de E/S usa E/S em buffer (o que exige que os buffers sejam especificados usando endereços virtuais em vez de MDLs), a estrutura converte a descrição do buffer de um MDL em um endereço virtual e comprimento. No entanto, será mais eficiente se o driver especificar buffers no formato correto.
Para obter informações sobre objetos de memória de estrutura, listas lookaside, MDLs e buffers locais, consulte Usando buffers de memória.
Para obter informações sobre quando os buffers de memória são excluídos, consulte Ciclo de Vida do Buffer de Memória.
Acessando buffers de dados para E/S em buffer
Se o driver estiver usando E/S em buffer, seu comportamento mudará dependendo do tipo de solicitação de dados e se ele está usando KMDF ou UMDF.
Quando um driver KMDF usa E/S em buffer, o gerenciador de E/S cria um buffer intermediário que o driver pode acessar para cada tipo de solicitação. Veja o que acontece:
- Gravar solicitações. O gerenciador de E/S transfere informações de entrada do buffer de entrada do aplicativo de chamada antes de chamar a pilha de driver. Em seguida, o driver KMDF lê informações de entrada do buffer intermediário e as grava no dispositivo.
- Ler solicitações. O driver KMDF lê informações do dispositivo e as armazena no buffer intermediário. Em seguida, o gerenciador de E/S copia os dados de saída do buffer intermediário para o buffer de saída do aplicativo.
- Solicitações de controle de E/S do dispositivo. O driver KMDF lê ou grava dados dessa solicitação de ou para o buffer intermediário.
Quando um driver UMDF usa E/S em buffer, o processo de host do driver cria um ou dois buffers intermediários, dependendo do tipo de solicitação. Veja o que acontece:
- Gravar solicitações. A estrutura cria um buffer, transfere informações de entrada do buffer de entrada do aplicativo de chamada e, em seguida, chama a pilha de driver. O driver UMDF lê informações de entrada do buffer intermediário e as grava no dispositivo.
- Ler solicitações. Um driver UMDF lê informações de um dispositivo e as armazena em um buffer criado pela estrutura. O processo de host do driver copia os dados de saída do buffer intermediário para o buffer de saída do aplicativo.
- Solicitações de controle de E/S do dispositivo. A estrutura cria dois buffers correspondentes aos buffers de entrada e saída do IOCTL que o driver pode acessar. A estrutura copia as informações de entrada do IOCTL para o novo buffer intermediário e as disponibiliza para o driver. A estrutura não copia o conteúdo do buffer de saída, portanto, o driver não deve tentar ler a partir dele (caso contrário, ele acabará lendo dados de lixo). Todos os dados que o driver grava no buffer de saída são copiados de volta para o buffer IOCTL original e são retornados ao aplicativo após a conclusão bem-sucedida da solicitação de E/S. Observe que todos os dados que o driver grava no buffer de entrada são descartados e não retornados ao aplicativo de chamada.
Para recuperar um identificador para um objeto de memória de estrutura que representa o buffer, os drivers KMDF e UMDF chamam WdfRequestRetrieveInputMemory ou WdfRequestRetrieveOutputMemory, dependendo se essa é uma solicitação de leitura ou gravação. Em seguida, o driver pode recuperar um ponteiro para o buffer chamando WdfMemoryGetBuffer. Para ler e gravar o buffer, o driver chama WdfMemoryCopyFromBuffer ou WdfMemoryCopyToBuffer.
Para recuperar o endereço virtual e o comprimento do buffer, o driver chama WdfRequestRetrieveInputBuffer ou WdfRequestRetrieveOutputBuffer.
Para alocar e criar uma MDL (lista de descritores de memória) para o buffer, um driver KMDF chama WdfRequestRetrieveInputWdmMdl ou WdfRequestRetrieveOutputWdmMdl.
Acessando buffers de dados para E/S direta
Se o driver estiver usando E/S direta, o gerente de E/S verificará a acessibilidade do espaço de buffer que o originador da solicitação de E/S (normalmente um aplicativo de modo de usuário) especificou, bloqueia o espaço de buffer na memória física e fornece ao driver acesso direto ao espaço do buffer.
Se o driver tiver especificado uma preferência por E/S direta e todos os requisitos de UMDF para E/S direta tiverem sido atendidos (consulte Gerenciando métodos de acesso de buffer em drivers UMDF), a estrutura mapeia o buffer de memória que recebe do gerenciador de E/S diretamente para o espaço de endereço do processo de host do driver e, portanto, fornece ao driver acesso direto ao espaço de buffer.
Para recuperar um identificador para um objeto de memória de estrutura que representa o espaço de buffer, o driver chama WdfRequestRetrieveInputMemory ou WdfRequestRetrieveOutputMemory. Em seguida, o driver pode recuperar um ponteiro para o buffer chamando WdfMemoryGetBuffer. Para ler e gravar o buffer, o driver chama WdfMemoryCopyFromBuffer ou WdfMemoryCopyToBuffer.
Para recuperar o endereço virtual e o comprimento do espaço em buffer, o driver chama WdfRequestRetrieveInputBuffer ou WdfRequestRetrieveOutputBuffer.
Se os drivers de um dispositivo estiverem usando E/S direta, o gerente de E/S descreverá buffers usando MDLs. Para recuperar um ponteiro para o MDL de um buffer, um driver KMDF chama WdfRequestRetrieveInputWdmMdl ou WdfRequestRetrieveOutputWdmMdl. Um driver UMDF não pode acessar MDLs.
Acessando buffers de dados para E/S não armazenada em buffer nem direta
Se o driver estiver usando o método de acesso de buffer conhecido como o método de E/S não armazenado em buffer nem e/S direto (ou, o método "nem", para abreviar), o gerenciador de E/S simplesmente fornecerá ao driver os endereços virtuais que o originador da solicitação de E/S especificou para o espaço de buffer da solicitação. O gerenciador de E/S não valida o espaço de buffer da solicitação de E/S, portanto, o driver deve verificar se o espaço do buffer está acessível e bloquear o espaço do buffer na memória física.
Os endereços virtuais fornecidos pelo gerenciador de E/S só podem ser acessados no contexto de processo do originador da solicitação de E/S. Somente o driver de nível mais alto na pilha de driver tem a garantia de ser executado no contexto de processo do originador.
Para obter acesso ao espaço de buffer de uma solicitação de E/S, o driver de nível mais alto deve fornecer uma função de retorno de chamada EvtIoInCallerContext . A estrutura chama essa função de retorno de chamada sempre que recebe uma solicitação de E/S para o driver.
Se o método de acesso ao buffer de uma solicitação não for "nenhum dos dois", um driver KMDF deverá fazer o seguinte para cada buffer:
Chame WdfRequestRetrieveUnsafeUserInputBuffer ou WdfRequestRetrieveUnsafeUserOutputBuffer para obter o endereço virtual do buffer.
Chame WdfRequestProbeAndLockUserBufferForRead ou WdfRequestProbeAndLockUserBufferForWrite para investigar e bloquear o buffer e obter um identificador para um objeto de memória de estrutura para o buffer.
Salve os identificadores de objeto de memória no espaço de contexto da solicitação.
Chame WdfDeviceEnqueueRequest, que retorna a solicitação para a estrutura.
Posteriormente, a estrutura adiciona a solicitação a uma das filas de E/S do driver. Se o driver tiver fornecido manipuladores de solicitação, a estrutura eventualmente chamará o manipulador de solicitação apropriado.
O manipulador de solicitação pode recuperar os identificadores de objeto de memória da solicitação do espaço de contexto da solicitação. O driver pode passar os identificadores para WdfMemoryGetBuffer para obter o endereço do buffer.
Ocasionalmente, um driver de nível mais alto deve usar as etapas anteriores para acessar um buffer de modo de usuário, mesmo que o driver não esteja usando o método de acesso "nenhum" . Por exemplo, suponha que o driver esteja usando E/S em buffer. Um código de controle de E/S que usa o método de acesso em buffer pode passar uma estrutura que contém um ponteiro inserido para um buffer de modo de usuário. Nesse caso, o driver deve fornecer uma função de retorno de chamada EvtIoInCallerContext que extraia os ponteiros da estrutura e, em seguida, usa as etapas anteriores de 2 a 4.
O UMDF não dá suporte a buffers de tipo de E/S não armazenados em buffer nem diretos, portanto, um driver UMDF nunca precisa lidar diretamente com esse tipo de buffer.
No entanto, se a estrutura receber esses buffers para leitura ou gravação do gerenciador de E/S, ela os disponibilizará para um driver UMDF como E/S em buffer ou E/S direta, dependendo do método de acesso selecionado pelo driver. Se a estrutura receber um IOCTL especificando o método de buffer "nenhum", ele poderá converter opcionalmente o método de acesso ao buffer da solicitação IOCTL para E/S em buffer ou E/S direta com base na presença de uma diretiva INF. Consulte Gerenciando métodos de acesso a buffer em drivers UMDF para obter mais informações.