Dans l’architecture de système informatique la plus simple, tout le contrôle incombe au CPU (Central Processing Unit). Cela signifie non seulement l’exécution de commandes qui affectent le registre interne ou l’état du cache du CPU, mais également le transfert de tous les octets de la mémoire vers des périphériques, tels que le stockage et des interfaces telles que les ports série, USB ou Ethernet. Cette approche est appelée «entrée / sortie programmée», ou PIO, et a été largement utilisée au début des années 1990, par exemple pour les périphériques de stockage PATA, notamment ATA-1, ATA-2 et CompactFlash.

De toute évidence, si le processeur doit gérer chaque transfert de mémoire, cela commence à avoir un impact significatif sur les performances du système. Pour chaque demande de transfert de mémoire, la CPU doit interrompre tout autre travail qu’elle effectuait, configurer le transfert et l’exécuter, et restaurer son état précédent avant de pouvoir continuer. À mesure que le stockage et les interfaces externes devenaient de plus en plus rapides, cela devenait de moins en moins acceptable. Au lieu que PIO ne prenne que quelques pour cent des cycles du processeur, un gros transfert pourrait prendre la plupart des cycles, obligeant le système à s’arrêter jusqu’à ce que le transfert soit terminé.

DMA (Direct Memory Access) libère le CPU de ces tâches subalternes. Avec DMA, les périphériques n’ont pas à demander au CPU de récupérer certaines données pour eux, mais peuvent le faire eux-mêmes. Malheureusement, cela signifie que plusieurs systèmes se disputent le contenu du même pool de mémoire, ce qui peut causer des problèmes. Voyons donc comment fonctionne DMA, en vue de déterminer comment cela peut fonctionner pour nous.

Matériel Memcpy

Au cœur de DMA se trouve le contrôleur DMA: sa seule fonction est de configurer les transferts de données entre les périphériques d’E / S et la mémoire. Essentiellement, elle fonctionne comme la fonction memcpy que nous connaissons et aimons tous de C. Cette fonction prend trois paramètres: une destination, une source et le nombre d’octets à copier de la source vers la destination.

Prenons par exemple l’Intel 8237: il s’agit du contrôleur DMA de la famille de microprocesseurs Intel MCS 85. Il dispose de quatre canaux DMA (DREQ0 à DREQ3) et a été utilisé dans les IBM PC et PC XT. En enchaînant plusieurs circuits intégrés 8237, on augmente le nombre de canaux DMA, comme c’était le cas dans l’architecture système IBM PC AT. La fiche technique 8237 montre à quoi ressemble une intégration de base (unique) 8237 IC dans un système de niveau 8080:

Dans une simple requête, le contrôleur DMA demande à la CPU de renoncer au contrôle des bus système (adresse, données et contrôle) en poussant HRQ vers le haut. Une fois accordée, le CPU répondra sur la broche HLDA, auquel point les demandes DMA en suspens (via les entrées DREQx) seront traitées. Le contrôleur DMA garantit qu’après avoir maintenu le bus pendant un cycle, la CPU puisse utiliser le bus tous les deux cycles, afin de ne pas encombrer le bus avec des requêtes potentiellement longues.

Le contrôleur 8237 DMA prend en charge les transferts à un octet, ainsi que les transferts par blocs. Un mode à la demande permet également des transferts continus. Cela permettait des transferts DMA sur le bus PC / PC AT («ISA»).

Avance rapide de quelques décennies, et le contrôleur DMA de la famille STM32 F7 de microcontrôleurs Cortex-M est à la fois très similaire, mais également très différent. Ce MCU comprend non seulement un contrôleur DMA, mais deux (DMA1, DMA2), chacun étant connecté aux bus système internes, comme décrit dans le manuel de référence STM32F7 (RM0385).

Dans ce contrôleur DMA, le concept de flux est introduit, où chacun des huit flux prend en charge huit canaux. Cela permet à plusieurs appareils de se connecter à chaque contrôleur DMA. Dans cette implémentation système, seul DMA2 peut effectuer des transferts de mémoire à mémoire, car seul il est connecté à la mémoire (via la matrice de bus) sur ses deux interfaces AHB.

Comme avec le contrôleur DMA Intel 8237, chaque canal est connecté à un périphérique d’E / S spécifique, ce qui lui permet de configurer une requête DMA. Cela se fait généralement en envoyant des instructions au périphérique en question, telles que la définition de bits dans un registre, ou en utilisant une interface de niveau supérieur, ou dans le cadre du protocole du périphérique ou du périphérique. Dans un flux, cependant, un seul canal peut être actif à un moment donné.

Contrairement au 8237 plus basique, cependant, ce type de contrôleur DMA peut également utiliser un tampon FIFO pour des fonctionnalités telles que la modification de la largeur de transfert (octet, mot, etc.) si cela diffère entre la source et la destination.

Lorsqu’il s’agit d’avoir plusieurs contrôleurs DMA dans un système, une sorte de système de priorité garantit toujours qu’il y a un ordre logique. Pour les canaux, soit le numéro de canal détermine la priorité (comme avec le 8237), soit il peut être défini dans les registres du contrôleur DMA (comme avec le STM32F7). Plusieurs contrôleurs DMA peuvent être placés dans une hiérarchie qui garantit l’ordre. Pour le 8237, cela se fait en faisant en sorte que les 8237 en cascade utilisent chacun une broche DREQx et DACKx sur le contrôleur maître.

Snooping le bus

La synchronisation des données du cache est essentielle.

Jusqu’à présent, tout cela semble assez simple et direct: transmettez simplement la requête DMA au contrôleur DMA et faites-le fonctionner comme une magie pendant que le processeur se met à faire quelque chose de plus productif que de copier des octets. Malheureusement, il y a un gros problème ici sous la forme de la cohérence du cache.

Comme les processeurs ont gagné de plus en plus de caches pour les instructions et les données, allant du cache de niveau de base 1 (L1) aux caches L2, L3 et même L4 plus récents, en maintenant les données de ces caches synchronisées avec les données de la mémoire principale est devenue une caractéristique essentielle.

Dans un système à un seul cœur et à un seul processeur, cela semble facile: vous récupérez les données de la RAM système, vous les gardez dans le cache et les réécrivez dans la RAM système une fois que le prochain cycle d’accès glacialement lent pour cet endroit dans la RAM système s’ouvre à nouveau. . Ajoutez un deuxième cœur au processeur, avec son propre cache L1 et éventuellement L2, et soudainement vous devez garder ces deux caches synchronisés, de peur qu’un logiciel multi-thread ne commence à renvoyer des résultats vraiment intéressants.

Ajoutez maintenant DMA à ce mélange et vous obtenez une situation dans laquelle non seulement les données des caches peuvent changer, mais les données de la RAM système peuvent également changer, le tout sans que le processeur ne le sache. Pour empêcher les processeurs d’utiliser des données obsolètes dans leurs caches au lieu d’utiliser les données mises à jour dans la RAM ou dans un cache voisin, une fonctionnalité appelée surveillance de bus a été introduite.

Ce que cela fait essentiellement est de garder une trace de l’adresse mémoire qui se trouve dans un cache, tout en surveillant toutes les demandes d’écriture dans les caches RAM ou CPU et en mettant à jour toutes les copies ou en marquant ces copies comme non valides. En fonction de l’architecture du système spécifique, cela peut être fait entièrement dans le matériel, ou une combinaison de matériel et de logiciel.

Que le début

Il doit être clair à ce stade que chaque implémentation DMA est différente, en fonction du système pour lequel elle a été conçue et des besoins qu’elle cherche à satisfaire. Alors que le contrôleur DMA d’un PC IBM et celui d’un MCU basé sur ARM sont assez similaires dans leur conception de base et ne s’éloignent pas si loin en termes d’ensemble de fonctionnalités, les contrôleurs DMA que l’on trouve également dans les ordinateurs de bureau d’aujourd’hui car les systèmes de serveur sont un tout autre jeu de balle.

Au lieu de gérer une connexion Ethernet de 100 Mbit, ou la fulgurante 12 Mbit de l’USB 2.0 Fast Speed, les contrôleurs DMA des systèmes serveurs sont obligés de faire face à des liaisons Ethernet 40 Gbit et plus Suite. Rien de tout cela ne devrait déranger trop le CPU si tout cela est possible.

Dans l’espace de bureau, la poussée continue vers plus de performances, en particulier dans le jeu, a conduit à un nouveau chapitre intéressant dans le DMA, sous la forme de demandes de stockage vers appareil, par exemple sous la forme de la technologie RTX IO de NVidia. RTX IO lui-même est basé sur l’API DirectStorage de Microsoft. Ce que fait RTX IO, c’est permettre au GPU de gérer autant de demandes de communication pour le stockage et la décompression d’actifs sans impliquer le CPU. Cela permet d’économiser les étapes de copie des données du stockage dans la RAM système, de les décompresser avec le CPU, puis de réécrire les données dans la RAM du GPU.

Attaque du DMA

Toute fonctionnalité bonne et utile doit bien sûr s’accompagner de quelques compromis, et pour DMA, cela peut être principalement trouvé dans des choses comme les attaques DMA. Ceux-ci utilisent le fait que DMA contourne beaucoup de sécurité avec sa capacité à écrire directement dans la mémoire système. Le système d’exploitation protège normalement contre l’accès aux parties sensibles de l’espace mémoire, mais DMA contourne le système d’exploitation, rendant ces protections inutiles.

La bonne nouvelle ici est que pour pouvoir utiliser une attaque DMA, un attaquant doit accéder physiquement à un port d’E / S de l’appareil qui utilise DMA. La mauvaise nouvelle est qu’il est peu probable que toute atténuation ait un impact réel sans compromettre ce qui fait du DMA une caractéristique si essentielle des ordinateurs modernes.

Bien que l’USB (contrairement à FireWire) n’utilise pas nativement le DMA, l’ajout de voies PCIe aux connecteurs USB-C (avec Thunderbolt 3 / USB 4) signifie qu’une attaque DMA via un port USB-C pourrait être une réelle possibilité.

Emballer

Comme nous l’avons vu au cours des dernières décennies, disposer d’un matériel spécialisé est hautement souhaitable pour certaines tâches. Ceux d’entre nous qui ont dû souffrir des ordinateurs domestiques qui ont dû abandonner le rendu à l’écran tout en passant tous les cycles de processeur à obtenir des données à partir d’une disquette ou similaire ont sûrement appris à profiter des avantages d’un monde rempli de DMA avec des coprocesseurs dédiés. nous a amenés.

Même ainsi, il existe certains risques de sécurité associés à l’utilisation de DMA. La mesure dans laquelle ils sont préoccupants dépend de l’application, des circonstances et des mesures d’atténuation. Tout comme l’humble memcpy() fonction, DMA est un outil très puissant qui peut être utilisé pour un grand bien ou un grand mal, selon la façon dont il est utilisé. Même si nous devons célébrer son existence, cela vaut la peine de considérer son impact sur la sécurité dans tout nouveau système.

LAISSER UN COMMENTAIRE

Rédigez votre commentaire !
Entrez votre nom ici