Accesso ai buffer di dati nei driver WDF (KMDF o UMDF)
Quando un driver WDF (Windows Driver Frameworks) riceve una richiesta di controllo I/O di lettura, scrittura o dispositivo, l'oggetto richiesta contiene un buffer di input, un buffer di output o entrambi.
I buffer di input contengono informazioni necessarie per il driver. Per le richieste di scrittura, queste informazioni sono in genere dati che un driver di funzione deve inviare a un dispositivo. Per le richieste di controllo I/O del dispositivo, un buffer di input potrebbe contenere informazioni che indicano il tipo di operazione che il driver deve eseguire.
I buffer di output ricevono informazioni dal driver. Per le richieste di lettura, queste informazioni sono in genere dati ricevuti da un driver di funzione da un dispositivo. Per le richieste di controllo I/O del dispositivo, un buffer di output potrebbe ricevere lo stato o altre informazioni specificate dal codice di controllo I/O della richiesta.
La tecnica usata dal driver per accedere ai buffer di dati di una richiesta dipende dal metodo del driver per l'accesso ai buffer di dati per un dispositivo. Esistono tre metodi di accesso:
- I/O memorizzato nel buffer. Gestione I/O crea buffer intermedi che condivide con il driver.
- I/O diretto. Il gestore di I/O blocca lo spazio del buffer nella memoria fisica e quindi fornisce al driver l'accesso diretto allo spazio del buffer.
- Nessun buffer né I/O diretto. Gestione I/O fornisce al driver gli indirizzi virtuali dello spazio buffer della richiesta. Gestione I/O non convalida lo spazio buffer della richiesta, quindi il driver deve verificare che lo spazio del buffer sia accessibile e bloccare lo spazio del buffer nella memoria fisica.
Un driver Kernel-Mode Driver Framework (KMDF) può usare uno dei tre metodi di accesso. Un driver User-Mode Driver Framework (UMDF) può usare l'I/O con buffer o diretto per le richieste di lettura, scrittura e IOCTL e può convertire le richieste che specificano il metodo METHOD_NEITHER.
Specifica del metodo di accesso al buffer
Per le richieste di lettura e scrittura, tutti i driver in uno stack di driver devono usare lo stesso metodo per accedere ai buffer di un dispositivo, ad eccezione del driver di livello più alto, che può usare il metodo "nessuno", indipendentemente dal metodo usato dai driver inferiori.
A partire dalla versione 1.13, un driver KMDF specifica il metodo di accesso per tutte le richieste di lettura e scrittura di un dispositivo chiamando WdfDeviceInitSetIoTypeEx per ogni dispositivo. Ad esempio, se un driver specifica il metodo I/O con buffer per uno dei relativi dispositivi, il gestore I/O usa il metodo I/O memorizzato nel buffer durante la distribuzione di richieste di lettura e scrittura al driver per tale dispositivo.
Per le richieste di controllo I/O del dispositivo, il codice di controllo I/O (IOCTL) contiene bit che specificano il metodo di accesso al buffer. Di conseguenza, un driver KMDF non deve eseguire alcuna azione per selezionare un metodo di buffering per IOCTLs. Per altre informazioni sugli IOCTLs, vedere Definizione dei codici di controllo I/O. A differenza delle richieste di lettura e scrittura, tutti gli IOCTLs di un dispositivo non devono specificare lo stesso metodo di accesso.
Un driver UMDF specifica le preferenze per il metodo di accesso usato dal framework per le richieste di lettura e scrittura, nonché per le richieste di controllo I/O del dispositivo. I valori forniti da un driver UMDF sono solo preferenze e non sono garantiti che vengano usati dal framework. Per altre informazioni, vedere Gestione dei metodi di accesso al buffer nei driver UMDF.
Un driver UMDF specifica il metodo di accesso per tutte le richieste di lettura, scrittura e IOCTL di un dispositivo chiamando WdfDeviceInitSetIoTypeEx per ogni dispositivo. Ad esempio, se un driver specifica il metodo I/O memorizzato nel buffer per uno dei relativi dispositivi, il framework usa il metodo I/O memorizzato nel buffer durante la distribuzione di richieste di lettura, scrittura e IOCTL al driver per tale dispositivo.
Si noti la differenza nella tecnica di accesso al buffer per IOCTLs tra KMDF e UMDF. I driver KMDF non specificano il metodo di accesso al buffer per IOCTLs, mentre i driver UMDF specificano il metodo di accesso al buffer per IOCTLs.
Se un driver WDF descrive il buffer di una richiesta di I/O usando una tecnica non corretta per il metodo I/O usato da una destinazione di I/O, il framework corregge la descrizione del buffer. Ad esempio, se un driver usa un MDL per descrivere un buffer passato a WdfIoTargetSendReadSynchronously e se la destinazione di I/O usa i buffer di I/O (che richiede che i buffer vengano specificati usando indirizzi virtuali anziché mdls), il framework converte la descrizione del buffer da un MDL a un indirizzo e una lunghezza virtuale. Tuttavia, è più efficiente se il driver specifica i buffer nel formato corretto.
Per informazioni sugli oggetti di memoria del framework, sugli elenchi lookaside, sugli ELENCHI MDL e sui buffer locali, vedere Uso di buffer di memoria.
Per informazioni sull'eliminazione dei buffer di memoria, vedere Ciclo di vita del buffer di memoria.
Accesso ai buffer di dati per I/O memorizzati nel buffer
Se il driver usa l'I/O con buffer, il comportamento cambia a seconda del tipo di richiesta di dati e se usa KMDF o UMDF.
Quando un driver KMDF usa l'I/O con buffer, il gestore di I/O crea un buffer intermedio a cui può accedere il driver per ogni tipo di richiesta. Di seguito è illustrato ciò che accade:
- Scrivere richieste. Gestione I/O trasferisce informazioni di input dal buffer di input dell'app chiamante prima di chiamare lo stack di driver. Il driver KMDF legge quindi le informazioni di input dal buffer intermedio e lo scrive nel dispositivo.
- Leggere le richieste. Il driver KMDF legge le informazioni dal dispositivo e lo archivia nel buffer intermedio. Quindi, gestione I/O copia i dati di output dal buffer intermedio al buffer di output dell'app.
- Richieste di controllo I/O del dispositivo. Il driver KMDF legge o scrive i dati per tale richiesta da o verso il buffer intermedio.
Quando un driver UMDF usa l'I/O con buffer, il processo host driver crea uno o due buffer intermedi, a seconda del tipo di richiesta. Di seguito è illustrato ciò che accade:
- Scrivere richieste. Il framework crea un buffer, trasferisce informazioni di input dal buffer di input dell'app chiamante e quindi chiama lo stack di driver. Il driver UMDF legge le informazioni di input dal buffer intermedio e lo scrive nel dispositivo.
- Leggere le richieste. Un driver UMDF legge le informazioni da un dispositivo e lo archivia in un buffer creato dal framework. Il processo host driver copia i dati di output dal buffer intermedio al buffer di output dell'app.
- Richieste di controllo I/O del dispositivo. Il framework crea due buffer corrispondenti ai buffer di input e output dell'IOCTL a cui può accedere il driver. Il framework copia le informazioni di input da IOCTL nel nuovo buffer intermedio e lo rende disponibile per il driver. Il framework non copia il contenuto del buffer di output, quindi il driver non deve tentare di leggere da esso (altrimenti finirà per leggere i dati di garbage). Tutti i dati scritti dal driver nel buffer di output vengono copiati nel buffer IOCTL originale e vengono restituiti all'app al completamento della richiesta di I/O. Si noti che tutti i dati scritti dal driver nel buffer di input vengono eliminati e non restituiti all'app chiamante.
Per recuperare un handle in un oggetto di memoria del framework che rappresenta il buffer, sia i driver KMDF che i driver UMDF chiamano WdfRequestRetrieveInputMemory o WdfRequestRetrieveOutputMemory, a seconda che si tratti di una richiesta di lettura o scrittura. Il driver può quindi recuperare un puntatore al buffer chiamando WdfMemoryGetBuffer. Per leggere e scrivere il buffer, il driver chiama WdfMemoryCopyFromBuffer o WdfMemoryCopyToBuffer.
Per recuperare l'indirizzo virtuale e la lunghezza del buffer, il driver chiama WdfRequestRetrieveInputBuffer o WdfRequestRetrieveOutputBuffer.
Per allocare e compilare un elenco di descrittori di memoria (MDL) per il buffer, un driver KMDF chiama WdfRequestRetrieveInputWdmMdl o WdfRequestRetrieveOutputWdmMdl.
Accesso ai buffer di dati per I/O diretto
Se il driver usa l'I/O diretto, il gestore di I/O verifica l'accessibilità dello spazio buffer specificato dall'origine della richiesta di I/O (in genere un'applicazione in modalità utente), blocca lo spazio del buffer nella memoria fisica e quindi fornisce al driver l'accesso diretto allo spazio del buffer.
Se il driver ha specificato una preferenza per l'I/O diretta e tutti i requisiti di I/O diretti sono stati soddisfatti (vedere Gestione dei metodi di accesso al buffer nei driver UMDF), il framework esegue il mapping del buffer di memoria ricevuto dal gestore I/O direttamente nello spazio degli indirizzi del processo host del driver e fornisce quindi al driver l'accesso diretto allo spazio dei buffer.
Per recuperare un handle in un oggetto memoria del framework che rappresenta lo spazio del buffer, il driver chiama WdfRequestRetrieveInputMemory o WdfRequestRetrieveOutputMemory. Il driver può quindi recuperare un puntatore al buffer chiamando WdfMemoryGetBuffer. Per leggere e scrivere il buffer, il driver chiama WdfMemoryCopyFromBuffer o WdfMemoryCopyToBuffer.
Per recuperare l'indirizzo virtuale e la lunghezza dello spazio del buffer, il driver chiama WdfRequestRetrieveInputBuffer o WdfRequestRetrieveOutputBuffer.
Se i driver di un dispositivo usano l'I/O diretto, il gestore di I/O descrive i buffer usando gli ELENCHI di dati MDL. Per recuperare un puntatore a un MDL del buffer, un driver KMDF chiama WdfRequestRetrieveInputWdmMdl o WdfRequestRetrieveOutputWdmMdl. Un driver UMDF non può accedere a MDLs.
Accesso ai buffer dei dati per né i buffer con buffer né I/O diretto
Se il driver usa il metodo di accesso al buffer noto come metodo di I/O non memorizzato nel buffer né nel metodo I/ O diretto (o, il metodo "nessuno", per breve), il gestore di I/O fornisce semplicemente il driver con gli indirizzi virtuali che l'origine della richiesta di I/O specificata per lo spazio buffer della richiesta della richiesta. Gestione I/O non convalida lo spazio buffer della richiesta di I/O, pertanto il driver deve verificare che lo spazio del buffer sia accessibile e blocchi lo spazio del buffer nella memoria fisica.
Gli indirizzi virtuali forniti dalla gestione I/O possono essere accessibili solo nel contesto del processo dell'origine della richiesta di I/O. Solo il driver di livello più alto nello stack di driver è garantito per l'esecuzione nel contesto del processo dell'origine.
Per ottenere l'accesso allo spazio buffer di una richiesta di I/O, il driver di livello più alto deve fornire una funzione di callback EvtIoInCallerContext . Il framework chiama questa funzione di callback ogni volta che riceve una richiesta di I/O per il driver.
Se il metodo di accesso al buffer di una richiesta non è "nessuno", un driver KMDF deve eseguire le operazioni seguenti per ogni buffer:
Chiamare WdfRequestRetrieveUnsafeUserInputBuffer o WdfRequestRetrieveUnsafeUserOutputBuffer per ottenere l'indirizzo virtuale del buffer.
Chiamare WdfRequestProbeAndLockUserBufferForRead o WdfRequestProbeAndLockUserBufferForWrite per eseguire il probe e bloccare il buffer e ottenere un handle a un oggetto memoria del framework per il buffer.
Salvare gli handle dell'oggetto memoria nello spazio di contesto della richiesta.
Chiamare WdfDeviceEnqueueRequest, che restituisce la richiesta al framework.
Il framework aggiunge successivamente la richiesta a una delle code di I/O del driver. Se il driver ha fornito gestori di richieste, il framework chiamerà infine il gestore della richiesta appropriato.
Il gestore della richiesta può recuperare gli handle degli oggetti di memoria della richiesta dallo spazio di contesto della richiesta. Il driver può passare gli handle a WdfMemoryGetBuffer per ottenere l'indirizzo del buffer.
Occasionalmente, un driver di livello più alto deve usare i passaggi precedenti per accedere a un buffer in modalità utente, anche se il driver non usa il metodo di accesso "nessuno". Si supponga, ad esempio, che il driver usi L/O con buffer. Un codice di controllo di I/O che usa il metodo di accesso con buffer potrebbe passare una struttura contenente un puntatore incorporato a un buffer in modalità utente. In tal caso, il driver deve fornire una funzione di callback EvtIoInCallerContext che estrae i puntatori dalla struttura e quindi usa i passaggi precedenti da 2 a 4.
UMDF non supporta buffer né buffer di tipo I/O diretto, quindi un driver UMDF non deve mai gestire direttamente questo tipo di buffer.
Tuttavia, se il framework riceve tali buffer per la lettura o la scrittura dal gestore di I/O, le rende disponibili per un driver UMDF come I/O con buffer o I/O diretto, a seconda del metodo di accesso selezionato dal driver. Se il framework riceve un IOCTL che specifica il metodo di buffer "nessuno", può facoltativamente convertire il metodo di accesso al buffer della richiesta I/O con buffer di I/O o diretto in base alla presenza di una direttiva INF. Per altre informazioni, vedere Gestione dei metodi di accesso al buffer nei driver UMDF .