Acceso a búferes de datos en controladores WDF (KMDF o UMDF)
Cuando un controlador de Windows Driver Frameworks (WDF) recibe una solicitud de control de E/S de lectura, escritura o dispositivo, el objeto de solicitud contiene un búfer de entrada, un búfer de salida o ambos.
Los búferes de entrada contienen información que necesita el controlador. Para las solicitudes de escritura, esta información suele ser datos que un controlador de funciones debe enviar a un dispositivo. En el caso de las solicitudes de control de E/S del dispositivo, un búfer de entrada puede contener información que indica el tipo de operación que debe realizar el controlador.
Los búferes de salida reciben información del controlador. En el caso de las solicitudes de lectura, esta información suele ser los datos que recibe un controlador de función de un dispositivo. En el caso de las solicitudes de control de E/S del dispositivo, un búfer de salida podría recibir el estado u otra información especificada por el código de control de E/S de la solicitud.
La técnica que usa el controlador para acceder a los búferes de datos de una solicitud depende del método del controlador para acceder a los búferes de datos de un dispositivo. Hay tres métodos de acceso:
- E/S almacenada en búfer. El administrador de E/S crea búferes intermedios que comparte con el controlador.
- E/S directa. El administrador de E/S bloquea el espacio del búfer en la memoria física y, a continuación, proporciona al controlador acceso directo al espacio de búfer.
- Ni almacenado en búfer ni E/S directa. El administrador de E/S proporciona al controlador las direcciones virtuales del espacio de búfer de la solicitud. El administrador de E/S no valida el espacio de búfer de la solicitud, por lo que el controlador debe comprobar que el espacio del búfer es accesible y bloquear el espacio del búfer en la memoria física.
Un controlador de Kernel-Mode Driver Framework (KMDF) puede usar cualquiera de los tres métodos de acceso. Un controlador de User-Mode Driver Framework (UMDF) puede usar E/S almacenada en búfer o directa para las solicitudes de lectura, escritura e IOCTL, y puede convertir solicitudes que especifiquen el método METHOD_NEITHER.
Especificar el método de acceso del búfer
Para las solicitudes de lectura y escritura, todos los controladores de una pila de controladores deben usar el mismo método para acceder a los búferes de un dispositivo, excepto para el controlador de nivel superior, que puede usar el método "ninguno", independientemente del método que usen los controladores inferiores.
A partir de la versión 1.13, un controlador KMDF especifica el método de acceso para todas las solicitudes de lectura y escritura de un dispositivo llamando a WdfDeviceInitSetIoTypeEx para cada dispositivo. Por ejemplo, si un controlador especifica el método de E/S almacenado en búfer para uno de sus dispositivos, el administrador de E/S usa el método de E/S almacenado en búfer al entregar solicitudes de lectura y escritura al controlador para ese dispositivo.
Para las solicitudes de control de E/S del dispositivo, el código de control de E/S (IOCTL) contiene bits que especifican el método de acceso del búfer. Como resultado, un controlador KMDF no necesita realizar ninguna acción para seleccionar un método de almacenamiento en búfer para las ICTLs. Para obtener más información sobre las E/S, consulte Definición de códigos de control de E/S. A diferencia de las solicitudes de lectura y escritura, todas las ICTL de un dispositivo no tienen que especificar el mismo método de acceso.
Un controlador UMDF especifica las preferencias para el método de acceso que usa el marco para las solicitudes de lectura y escritura, así como las solicitudes de control de E/S del dispositivo. Los valores que proporciona un controlador UMDF solo son preferencias y no se garantiza que lo use el marco. Para obtener más información, consulte Administración de métodos de acceso de búfer en controladores umdf.
Un controlador UMDF especifica el método de acceso para todas las solicitudes de lectura, escritura e IOCTL de un dispositivo mediante una llamada a WdfDeviceInitSetIoTypeEx para cada dispositivo. Por ejemplo, si un controlador especifica el método de E/S almacenado en búfer para uno de sus dispositivos, el marco usa el método de E/S almacenado en búfer al entregar solicitudes de lectura, escritura e IOCTL al controlador para ese dispositivo.
Tenga en cuenta la diferencia en la técnica de acceso al búfer para las ITL entre KMDF y UMDF. Los controladores KMDF no especifican el método de acceso del búfer para las ICTL, mientras que los controladores UMDF especifican el método de acceso del búfer para las ICTL.
Si un controlador WDF describe el búfer de una solicitud de E/S mediante una técnica incorrecta para el método de E/S que usa un destino de E/S, el marco corrige la descripción del búfer. Por ejemplo, si un controlador usa un MDL para describir un búfer que pasa a WdfIoTargetSendReadSynchronousmente y si el destino de E/S usa E/S almacenada en búfer (lo que requiere que los búferes se especifiquen mediante direcciones virtuales en lugar de MDL), el marco convierte la descripción del búfer de una MDL en una dirección virtual y longitud. Sin embargo, es más eficaz si el controlador especifica búferes en el formato correcto.
Para obtener información sobre los objetos de memoria del marco, listas de búsqueda, MDL y búferes locales, consulte Uso de búferes de memoria.
Para obtener información sobre cuándo se eliminan los búferes de memoria, consulte Ciclo de vida del búfer de memoria.
Acceso a búferes de datos para E/S almacenadas en búfer
Si el controlador usa E/S almacenada en búfer, su comportamiento cambia según el tipo de solicitud de datos y si usa KMDF o UMDF.
Cuando un controlador KMDF usa E/S almacenada en búfer, el administrador de E/S crea un búfer intermedio al que el controlador puede acceder para cada tipo de solicitud. Esto es lo que sucede:
- Escritura de solicitudes. El administrador de E/S transfiere la información de entrada del búfer de entrada de la aplicación que realiza la llamada antes de llamar a la pila de controladores. A continuación, el controlador KMDF lee información de entrada del búfer intermedio y lo escribe en el dispositivo.
- Solicitudes de lectura. El controlador KMDF lee información del dispositivo y la almacena en el búfer intermedio. A continuación, el administrador de E/S copia los datos de salida del búfer intermedio en el búfer de salida de la aplicación.
- Solicitudes de control de E/S del dispositivo. El controlador KMDF lee o escribe datos para esa solicitud a o desde el búfer intermedio.
Cuando un controlador UMDF usa E/S almacenada en búfer, el proceso de host del controlador crea uno o dos búferes intermedios, según el tipo de solicitud. Esto es lo que sucede:
- Escritura de solicitudes. El marco crea un búfer, transfiere la información de entrada del búfer de entrada de la aplicación que realiza la llamada y, a continuación, llama a la pila de controladores. El controlador UMDF lee la información de entrada del búfer intermedio y lo escribe en el dispositivo.
- Solicitudes de lectura. Un controlador UMDF lee información de un dispositivo y lo almacena en un búfer que creó el marco. El proceso de host del controlador copia los datos de salida del búfer intermedio en el búfer de salida de la aplicación.
- Solicitudes de control de E/S del dispositivo. El marco crea dos búferes correspondientes a los búferes de entrada y salida del IOCTL al que puede acceder el controlador. El marco copia la información de entrada del IOCTL en el nuevo búfer intermedio y la pone a disposición del controlador. El marco no copia el contenido del búfer de salida, por lo que el controlador no debe intentar leerlo (de lo contrario, terminará leyendo datos no utilizados). Los datos que el controlador escribe en el búfer de salida se copian de nuevo en el búfer IOCTL original y se devuelven a la aplicación una vez completada correctamente la solicitud de E/S. Tenga en cuenta que los datos que escribe el controlador en el búfer de entrada se descartan y no se devuelven a la aplicación que realiza la llamada.
Para recuperar un identificador de un objeto de memoria de marco que representa el búfer, los controladores KMDF y UMDF llaman a WdfRequestRetrieveInputMemory o WdfRequestRetrieveOutputMemory, dependiendo de si se trata de una solicitud de lectura o escritura. Después, el controlador puede recuperar un puntero al búfer mediante una llamada a WdfMemoryGetBuffer. Para leer y escribir el búfer, el controlador llama a WdfMemoryCopyFromBuffer o WdfMemoryCopyToBuffer.
Para recuperar la dirección virtual y la longitud del búfer, el controlador llama a WdfRequestRetrieveInputBuffer o WdfRequestRetrieveOutputBuffer.
Para asignar y compilar una lista de descriptores de memoria (MDL) para el búfer, un controlador KMDF llama a WdfRequestRetrieveInputWdmMdl o WdfRequestRetrieveOutputWdmMdl.
Acceso a búferes de datos para E/S directa
Si el controlador usa E/S directa, el administrador de E/S comprueba la accesibilidad del espacio de búfer que el originador de la solicitud de E/S (normalmente una aplicación en modo de usuario) especificada, bloquea el espacio del búfer en la memoria física y, a continuación, proporciona al controlador acceso directo al espacio de búfer.
Si el controlador ha especificado una preferencia para la E/S directa y se han cumplido todos los requisitos de UMDF para la E/S directa (consulte Managing Buffer Access Methods in UMDF Drivers), el marco asigna el búfer de memoria que recibe del administrador de E/S directamente al espacio de direcciones del proceso de host del controlador y, por tanto, proporciona al controlador acceso directo al espacio de búfer.
Para recuperar un identificador de un objeto de memoria de marco que representa el espacio de búfer, el controlador llama a WdfRequestRetrieveInputMemory o WdfRequestRetrieveOutputMemory. Después, el controlador puede recuperar un puntero al búfer mediante una llamada a WdfMemoryGetBuffer. Para leer y escribir el búfer, el controlador llama a WdfMemoryCopyFromBuffer o WdfMemoryCopyToBuffer.
Para recuperar la dirección virtual y la longitud del espacio de búfer, el controlador llama a WdfRequestRetrieveInputBuffer o WdfRequestRetrieveOutputBuffer.
Si los controladores de un dispositivo usan E/S directa, el administrador de E/S describe los búferes mediante MDL. Para recuperar un puntero a mdL de un búfer, un controlador KMDF llama a WdfRequestRetrieveInputWdmMdl o WdfRequestRetrieveOutputWdmMdl. Un controlador UMDF no puede acceder a mdL.
Acceso a los búferes de datos para la E/S no almacenada en búfer ni en la E/S directa
Si el controlador usa el método de acceso al búfer conocido como método de E/S almacenado en búfer ni E/S directa (o bien, el método "ni", para abreviar), el administrador de E/S simplemente proporciona al controlador las direcciones virtuales que el originador de la solicitud de E/S especificada para el espacio de búfer de la solicitud. El administrador de E/S no valida el espacio de búfer de la solicitud de E/S, por lo que el controlador debe comprobar que el espacio del búfer es accesible y bloquear el espacio del búfer en la memoria física.
Las direcciones virtuales que proporciona el administrador de E/S solo se pueden acceder en el contexto de proceso del originador de la solicitud de E/S. Solo se garantiza que el controlador de nivel más alto de la pila de controladores se ejecute en el contexto de proceso del autor.
Para obtener acceso al espacio de búfer de una solicitud de E/S, el controlador de nivel superior debe proporcionar una función de devolución de llamada EvtIoInCallerContext . El marco llama a esta función de devolución de llamada cada vez que recibe una solicitud de E/S para el controlador.
Si el método de acceso al búfer de una solicitud es "ninguno", un controlador KMDF debe hacer lo siguiente para cada búfer:
Llame a WdfRequestRetrieveUnsafeUserInputBuffer o WdfRequestRetrieveUnsafeUserOutputBuffer para obtener la dirección virtual del búfer.
Llame a WdfRequestProbeAndLockUserBufferForRead o WdfRequestProbeAndLockUserBufferForWrite para sondear y bloquear el búfer y obtener un identificador para un objeto de memoria de marco para el búfer.
Guarde los identificadores de objeto de memoria en el espacio de contexto de la solicitud.
Llame a WdfDeviceEnqueueRequest, que devuelve la solicitud al marco de trabajo.
Posteriormente, el marco agrega la solicitud a una de las colas de E/S del controlador. Si el controlador ha proporcionado controladores de solicitudes, el marco llamará finalmente al controlador de solicitudes adecuado.
El controlador de solicitudes puede recuperar los identificadores de objeto de memoria de la solicitud desde el espacio de contexto de la solicitud. El controlador puede pasar los identificadores a WdfMemoryGetBuffer para obtener la dirección del búfer.
En ocasiones, un controlador de nivel superior debe usar los pasos anteriores para acceder a un búfer en modo de usuario, incluso si el controlador no usa el método de acceso "ninguno". Por ejemplo, supongamos que el controlador usa E/S almacenada en búfer. Un código de control de E/S que usa el método de acceso almacenado en búfer puede pasar una estructura que contiene un puntero incrustado a un búfer en modo de usuario. En tal caso, el controlador debe proporcionar una función de devolución de llamada EvtIoInCallerContext que extraiga los punteros de la estructura y, a continuación, use los pasos anteriores del 2 al 4.
UMDF no admite búfer ni búferes de tipo de E/S directas, por lo que un controlador UMDF nunca necesita controlar este tipo de búfer directamente.
Sin embargo, si el marco recibe estos búferes para lectura o escritura del administrador de E/S, los pone a disposición de un controlador UMDF como E/S almacenada en búfer o E/S directa, dependiendo del método de acceso seleccionado por el controlador. Si el marco recibe un IOCTL que especifica el método de búfer "ninguno", puede convertir opcionalmente el método de acceso del búfer de la solicitud IOCTL a E/S almacenada en búfer o E/S directa en función de la presencia de una directiva INF. Consulte Administración de métodos de acceso de búfer en controladores UMDF para obtener más información.