print · rss · source

< Un noyau en C qui recharge la GDT | TutoOS | Gérer les interruptions - la mise en oeuvre >


Programmer les interruptions du processeur i386 avec le contrôleur d'interruptions 8259A


Des interruptions pour prévenir le processeur d'un événement

Les interruptions sont des signaux envoyés au processeur pour l'avertir d'événements particuliers. On distingue trois types d'interruptions :

  • les interruptions matérielles sont déclenchées par les péripheriques (clavier, disque, souris, etc.). Par exemple, une interruption matérielle va être déclenchée par une unité de disque pour prévenir le processeur que des données sont prètes en lecture ou par une carte réseau pour prévenir de l'arrivée d'une trame éthernet.
  • les interruptions logicielles sont déclenchées volontairement par le programme. Elles permettent de gèrer les appels système.
  • les exceptions sont déclenchées par le processeur en cas de faute (division par zero, défaut de page, etc.)

Lorsque le processeur reçoit une interruption, il interrompt la tâche en cours, sauvegarde le contexte de celle-ci, et exécute la routine de service associée à l'interruption (ISR - Interrupt Service Routine) . Une fois la routine executée, le système redonne en général la main à la tâche interrompue.

Un exemple d'interruption

Quand on presse ou qu'on relache une touche du clavier d'un PC, le contrôleur du clavier envoie une interruption à un chipset chargé de multiplexer les interruptions : le contrôleur d'interruptions. Si l'interruption en question n'est pas masquée, le contrôleur déclenche une interruption matérielle pour prévenir le processeur. Le processeur exécute alors une routine pour traiter l'événement (touche pressée ou relachée). En général, cette routine va interroger le contrôleur du clavier pour savoir quelle touche a été pressée (ou relachée), puis il va éventuellement afficher le caractère correspondant et / ou le stocker dans un buffer. Une fois la routine de traitement de caractère terminée, la tâche interrompue peut reprendre.

Un chipset pour gérer les interruptions matérielles : le 8259A

A quoi sert le 8259A ?

Les interruptions matérielles peuvent êtres vues commes des sonnettes que tirées par les périphériques pour prévenir le processeur que quelque chose se passe. Mais si chaque périphérique pouvait envoyer directement un signal au processeur, il faudrait sur celui-ci autant de broches que de périphériques. Le contrôleur d'interruption programmable (PIC, Programmable interrupt controler) est un chipset qui permet d'éviter cela en multiplexant les requète d'interruption envoyées par les périphériques. Le 8259A est l'un des premiers PIC développé par Intel.

Chaque 8259A peut gérer les interruptions de 8 périphériques mais la plupart des PC ont deux contrôleurs cascadés entre eux, ce qui leur permet de gérer jusqu'à 14 périphériques. Le second contrôleur (esclave) est chaîné au premier (contrôleur maître). Un contrôleur peut réaliser des fonctions plus complexes que la simple transmission d'interruptions : il peut masquer des interruptions et définir des priorités de traitement.

Comment le 8259A traîte les interruptions

Quand un périphériques transmet au 8259A une requète pour une interruption, celle-ci subit un traitement complexe :

  1. Le registre IMR (Interrupt Mask Register) interne au contrôleur permet de masquer certaines interruptions. Si l'interruption est masquée on ne la traîte pas.
  2. Le registre IRR (Interrupt Request Register) est chargé de gérer les priorités entre interruptions au cas où plusieurs d'entre elles surviennent en même temps. La requète avec la plus haute priorité est evaluée.
  3. Le 8259A signale au processeur la demande d'interruption. La requète faite par le contrôleur au processeur est appelée IRQ (Interrupt Request).
  4. Le processeur acquitte ce signal puis demande au contrôleur, par un autre signal, le numéro de l'interruption demandée afin de déclencher la bonne routine ISR.
  5. Le contrôleur dépose sur le bus de données un octet, le vecteur d'interruption.
  6. Une fois la routine terminée, le processeur doit envoyer au contrôleur un signal pour l'avertir que le traitement de l'interruption est terminé.

Comment programmer le 8259A ?

Il est possible de paramétrer les deux PIC d'un PC en écrivant dans leurs registres internes via les ports d'entrée/sortie du processeur. On accède au contrôleur maître via les ports 0x20 et 0x21 et au contrôleur esclave via les ports 0xA0 et 0xA1. En écrivant sur ces ports, on envoie au contrôleur des données qui seront inscrites dans l'un de ses registres.

Il y a deux types de registres :

  • Les registres ICW (Initialization Command Word), qui réinitialisent le contrôleur.
  • Les registres OCW (Operation Control Word), qui permettent de paramétrer certaines fonctions du contrôleur une fois que celui-ci a été réinitialisé.

Pour réinitialiser un contrôleur, il faut remplir les registres d'initialisation ICW1, ICW2, ICW3 et ICW4 en respectant cet ordre. Après avoir transmis les données pour le registre ICW1, le contrôleur attend une valeur à mettre dans le registre ICW2 et ainsi de suite. Si il n'y a qu'un seul contrôleur, ce n'est pas la peine de renseigner les registres ICW3 et ICW4.

Les registres OCW peuvent être rempli à n'importe quel moment après l'initialisation des PIC. Le registre OCW1 permet notament de masquer et demasquer les interruptions.

Registres ICW

ICW1 (port 0x20 / port 0xA0)

|0|0|0|1|x|0|x|x|
         |   | +--- avec ICW4 (1) ou sans (0)
         |   +----- un seul contrôleur (1), ou cascadés (0)
         +--------- declenchement par niveau (level) (1) ou par front (edge) (0)

Par exemple, pour mettre à jour le registre ICW1 du contrôleur maître et du contrôleur esclave :

mov al, 0x11  ; Initialisation de ICW1
out 0x20, al  ; maître
out 0xA0, al  ; esclave

ICW2 (port 0x21 / port 0xA1)

|x|x|x|x|x|0|0|0|  
 | | | | |
 +----------------- adresse de base des vecteurs d'interruption

Les bits de poids fort servent à calculer l'adresse de base du vecteur d'interruption. Cette valeur correspond à un offset dans la table IDT (voir plus loin). Par exemple, pour initialiser les adresses de base des vecteurs d'interruption des deux contrôleurs :

mov al, 0x20
out 0x21, al  ; maître, vecteur de départ = 32
mov al, 0x70  
out 0xA1, al  ; esclave, vecteur de départ = 96

Dans l'exemple ci-dessus, les IRQs 0-7 sont mappées pour utiliser les interruptions 0x20-0x27 et les IRQs 8-15 sont mappées sur les interruptions 0x70-0x77. Note : sur les architectures de type x86, les 32 premiers vecteurs (0x0-0x19) sont réservés à la gestion des exceptions. Celà explique pourquoi les IRQs ne sont pas mappées à partir du début de la table.

ICW3 (port 0x21 / port 0xA1)

Ce registre informes les contrôleurs de la façon dont ils sont connectés entre eux :

|x|x|x|x|x|x|x|x|  pour le maître
 | | | | | | | |
 +------------------ contrôleur esclave rattaché à la broche d'interruption (1), ou non (0)

Remarque : un contrôleur maître peut être rattaché à plusieurs contrôleurs esclaves.

|0|0|0|0|0|x|x|x|  pour l'esclave
           | | |
           +-------- Identifiant de l'esclave, qui correspond au numéro de broche IR sur le maître

On détermine ici par quelles broches communiquent les deux contrôleurs. Dans l'exemple suivant, le contrôleur esclave est rattaché à la broche 2 du maître :

mov al, 0x04 ; maître
out 0x21, al
mov al, 0x02 ; esclave
out 0xA1, al

ICW4 (port 0x21 / port 0xA1)

Ce registre spécifie dans quel mode doit fonctionner le contrôleur. Ceci vient du fait que le 8259A a été conçu pour être générique et supporter différents systèmes :

|0|0|0|x|x|x|x|1|
       | | | +------ mode "automatic end of interrupt" AEOI (1)
       | | +-------- mode bufferisé esclave (0) ou maître (1)
       | +---------- mode bufferisé (1)
       +------------ mode "fully nested" (1)

Dans le cas présent, nous avons seulement besoin du mode par défaut :

mov al, 0x01
out 0x21, al
out 0xA1, al

Remarque importante !
Si on initialise les registres d'un contrôleur les uns à la suite des autres, une petite temporisation est nécessaire :

mov al, 0x11  ; initialisation de ICW1
out 0x20, al
jmp .1        ; temporisation
.1:
mov al, 0x20  ; initialisation de ICW2
out 0x21, al  ; vecteur de depart = 32
jmp .2        ; temporisation
.2:
mov al, 0x04  ; initialisation de ICW3
out 0x21, al
jmp .3  
.3:
mov al, 0x01  ; initialisation de ICW4
out 0x21, al

Registres OCW

OCW1 (port 0x21 / port 0xA1)

|x|x|x|x|x|x|x|x|  
 | | | | | | | |
 +------------------ pour chaque IRQ : masque d'interruption établi (1) ou non (0)

Pour modifier le registre IMR permettant de masquer les interruptions :

in  al, 0x21  ; lecture de l'Interrupt Mask Register (IMR)
and al, 0xEF  ; 0xEF => 11101111b. débloque l'IRQ 4
out 0x21, al  ; recharge l'IMR

OCW2 (port 0x20 / port 0xA0)

Ce registre est utilisé essentiellement pour avertir le contrôleur que l'interruption en cours a été traitée et qu'il peut rétablir les interruptions matérielles :

mov al, 0x20
out 0x20, al  ; "End Of Interrupt" (EOI) envoyee au PIC

Pour une description des registres OCW2 et OCW3, non utilisés dans le noyau, se réfèrer à la documentation de référence.

Exécuter la bonne routine de service grâce à la table des descripteurs d'interruptions

La table IDT (Interrupt Descriptor Table) permet d'associer à chaque interruption une routine de service. Chaque entrée de l'IDT est un descripteur particulier qui pointe sur une routine. Lorsqu'une interruption survient, le contrôleur transmet au processeur un vecteur d'interruption. Le processeur l'utilise pour calculer un offset dans l'IDT, charge un descripteur système, puis active la routine de service correspondant :

Les descripteurs dans l'IDT utilisés pour gérer les interruptions sont des descripteurs systèmes d'un type particulier : les interrupt gate.

Note : il est aussi possible d'utiliser des descripteurs du type trap gate. La seule différence est que ces derniers ne désactivent pas les interruptions (via le bit IF du registre EFLAGS) lorsqu'une interruption est reçue.

Descripteur système de type Interrupt Gate

  • le bit P est utilisé pour déterminer si le segment est présent en mémoire physique. Il est à 1 si c'est le cas.
  • le DPL indique le niveau de privilège du segment. Le niveau 0 correspond au mode super-utilisateur.

< Un noyau en C qui recharge la GDT | TutoOS | Gérer les interruptions - la mise en oeuvre >

print · rss · source
Page last modified on December 11, 2008, at 01:17 PM