Sécuriser les demandes numériques qui utilisent des E/S étendues
Les demandes SD (Secure Digital) qui lisent ou écrivent plus de deux octets de données doivent utiliser une commande d’E/S étendue (appelée CMD53 dans la spécification SD). La commande d’E/S étendue indique au pilote de bus de transmettre des données sur les lignes DAT du carte SD. Les caractéristiques d’un transfert de données dépendent des fonctionnalités du contrôleur SD. Par instance, certains contrôleurs autorisent uniquement les E/S programmables (PIO) ; d’autres autorisent l’accès direct à la mémoire (DMA). Pour une compatibilité maximale entre différents types de contrôleurS SD, les pilotes de périphérique doivent charger le paquet de requête avec un pointeur vers une MDL qui décrit la mémoire tampon de données. Le pilote de périphérique doit construire son propre MDL, sauf si un pilote d’une couche supérieure construit le MDL et le transmet au pilote de périphérique.
L’exemple de code suivant montre comment un pilote peut effectuer une requête d’E/S étendue à l’aide d’une mémoire tampon de données décrite par une MDL. Cet exemple de code est similaire au format de l’exemple de code d’E/S directe décrit dans Demandes numériques sécurisées qui utilisent des E/S directes. Il peut donc être utile d’étudier l’exemple de code d’E/S directes avant d’étudier l’exemple de code d’E/S étendu.
La principale différence entre les deux exemples est que l’exemple de code d’E/S étendu illustre comment utiliser des DLL avec une requête SD. Il existe également de légères différences dans la façon dont les descripteurs et les paquets de requête sont définis pour les E/S directes et étendues.
const SDCMD_DESCRIPTOR WriteIoExtendedDesc =
{SDCMD_IO_RW_EXTENDED, SDCC_STANDARD,
SDTD_WRITE, SDTT_SINGLE_BLOCK, SDRT_1};
// first, get an MDL to map the data. Call IoAllocateMdl to
// allocate an MDL and pass in a pointer to a buffer
// allocated from the non-paged pool.
mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, NULL);
if (mdl == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool (mdl);
// next, allocate a request packet for the arguments of the command
sdrp = ExAllocatePool(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET));
if (!sdrp) {
IoFreeMdl(mdl);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(sdrp, sizeof(SDBUS_REQUEST_PACKET));
sdrp->RequestFunction = SDRF_DEVICE_COMMAND;
sdrp->Parameters.DeviceCommand.CmdDesc =
WriteIoExtendedDesc;
// then, set up the argument and command descriptor
sdIoArgument.u.AsULONG = 0;
sdIoArgument.u.bits.Address = Offset;
// retrieve function number, the driver previously initialized
// this value with the SdBus GetProperty call
sdIoArgument.u.bits.Function = pDevExt->FunctionNumber;
sdIoArgument.u.bits.WriteToDevice = 1;
sdrp->Parameters.DeviceCommand.Argument =
sdIoArgument.u.AsULONG;
sdrp->Parameters.DeviceCommand.Mdl = mdl;
sdrp->Parameters.DeviceCommand.Length = Length;
// finally, submit the request
status = SdBusSubmitRequest(pDevExt->BusInterface.Context,sdrp);
IoFreeMdl(mdl);
ExFreePool(sdrp);
Cet exemple de code comprend les étapes suivantes :
Initialiser le descripteur
La première étape de l’envoi d’une demande device-command consiste à définir un descripteur de commande SD , SDCMD_DESCRIPTOR. Le descripteur dans l’exemple de code définit une opération d’écriture d’E/S étendue avec les éléments suivants :
Élément Description L’opération définie par le descripteur effectue une écriture d’E/S étendue, de sorte que la valeur du code de commande est SDCMD_IO_RW_DIRECT.
Les opérations d’écriture d’E/S étendues appartiennent au jeu de commandes standard (codes de commande 0 à 63), de sorte que la valeur affectée à ce membre du descripteur est SDCC_STANDARD.
Les opérations d’écriture nécessitent un transfert de l’hôte vers l’appareil, de sorte que la valeur affectée à ce membre du descripteur est SDTD_WRITE.
Le descripteur pour une opération d’écriture d’E/S étendue doit inclure un type de transfert. L’exemple de code spécifie une écriture de bloc unique, SDTT_SINGLE_BLOCK, qui indique que l’hôte écrit un bloc de données sur l’appareil. Le pilote a établi la taille d’un bloc par une commande SET_BLOCKLEN précédente (non illustrée dans cet exemple de code). Pour obtenir une explication de la commande SET_BLOCKLEN et du type de transfert SDTT_SINGLE_BLOCK, consultez La spécification de la carte MultiMedia , publiée par le comité technique de la MultiMedia Card Association (MMCA).
Le descripteur spécifie un type de réponse de SDRT_1, qui spécifie une réponse R1 standard à la commande et contient status données. Pour obtenir une explication de la réponse R1, consultez la spécification MultiMedia Card Association .
Configurer mdL
Appelez IoAllocateMdl pour allouer une MDL et passez un pointeur vers une mémoire tampon allouée à partir d’un pool non paginé. Ensuite, la routine MmBuildMdlForNonPagedPool prend la mdL nouvellement allouée qui spécifie une mémoire tampon de mémoire virtuelle dans un pool non paginé et la met à jour pour décrire les pages physiques sous-jacentes. Les appelants de MmBuildMdlForNonPagedPool doivent être en cours d’exécution sur IRQL <= DISPATCH_LEVEL.
Initialisez le paquet de requête en effectuant les étapes suivantes :
Définissez la fonction request :
Après avoir créé un descripteur SD, l’exemple de code initialise le paquet de requête , SDBUS_REQUEST_PACKET. Le membre RequestFunction du paquet de requête spécifie si la requête contient une commande d’appareil (valeur de SDRF_DEVICE_COMMAND) ou une opération de propriété (valeur de SDRF_GET_PROPERTY ou SDRF_SET_PROPERTY). L’exemple de code envoie une commande d’appareil. Il définit donc le membre RequestFunction sur SDRF_DEVICE_COMMAND.
Chargez le descripteur de commande. Ensuite, l’exemple de code stocke le descripteur nouvellement initialisé dans le membre Parameters.DeviceCommand.CmdDesc du paquet de requête.
Initialisez l’argument lecture/écriture :
Le paquet de requête contient une structure SD_RW_DIRECT_ARGUMENT avec l’emplacement dans lequel le pilote de bus écrit. Cette structure stocke également le numéro de la fonction dont l’espace d’E/S est lu par le pilote de bus. L’exemple de code récupère le numéro de fonction de l’extension de périphérique, ce qui implique que le pilote a précédemment récupéré ces informations à partir de l’carte (probablement quand il a démarré l’appareil avec une demande de SDRF_GET_PROPERTY et l’a stockée dans l’extension de périphérique.
Envoyer la demande
Après avoir initialisé le descripteur et le paquet de requête, l’exemple utilise la routine de requête synchrone , SdBusSubmitRequest pour envoyer la demande. Il transmet le paquet de requête et les informations de contexte d’interface que le système a fournies au pilote quand il a ouvert l’interface SD. Étant donné qu’il s’agit d’une requête synchrone, le pilote doit s’exécuter sur IRQL moins de DISPATCH_LEVEL.
Résultats de la commande
Étant donné que l’exemple de code utilise une commande d’E/S directe, aucune mémoire tampon de données autre que le champ ResponseData dans le paquet de requête SD.
L’exemple de code alloue une mémoire tampon de transfert de données à partir d’un pool non paginé. Un pilote peut utiliser PagedPool pour une mémoire tampon de transfert de données, à condition qu’il verrouille les pages. Toutefois, les pilotes doivent toujours allouer des mémoires tampons de transfert de données à partir d’un pool non paginé lors de l’exécution de requêtes SDRF_GET_PROPERTY et SDRF_SET_PROPERTY. Les pilotes doivent également allouer des paquets de requête SD à partir d’un pool non paginé, car la routine d’achèvement de l’IRP qui accompagne la requête SD peut s’exécuter dans un appel de procédure différée (DPC).
Pour tous les types de requêtes, l’allocation de mémoires tampons à partir d’un pool non paginé présente des avantages en matière de performances lorsque les mémoires tampons sont petites et que le pilote les conserve brièvement.