TD11 Gestionnaire d'interruptions - PDF
Document Details
Uploaded by HotDandelion
Sorbonne Université / LIP6 - INRIA
Tags
Summary
Ce document (TD11) couvre le sujet de la gestion des interruptions sur une plateforme MIPS. Il présente les concepts fondamentaux en termes de requêtes d'interruptions (IRQ), routines de service des interruptions (ISR), et masquage des IRQ. Des rappels de cours et des questions sont inclus.
Full Transcript
DOCS [Start][Config][User][Kernel] — COURS [9bis] [10bis] — TD [9][10] — TP [9][10][11] — ZIP [gcc...] Gestionnaire d'interruptions Rappel de cours...
DOCS [Start][Config][User][Kernel] — COURS [9bis] [10bis] — TD [9][10] — TP [9][10][11] — ZIP [gcc...] Gestionnaire d'interruptions Rappel de cours Contrôleurs de périphériques IRQ et ISR Rappel de cours Routage et masquage des IRQ Conséquence dans le noyau Il est fortement recommandé de lire les transparents, toutefois, nous avons mis Rappel sur les 3 registres du coprocesseur 0 impliqués Questions ci-après quelques rappels utiles pour répondre aux questions du TD. Contrôleurs de périphériques Dans cette séance, nous allons manipuler 3 contrôleurs de périphériques: le TTY que vous connaissez déjà et deux autres, l'ICU et le TIMER. Ces trois contrôleurs s'utilisent grâce à des registres mappés (c.-à-d. placés) dans l'espace d'adressage du MIPS. Les registres du TTY sont placés à partir de l'adresse 0xd0200000 , ceux de l'ICU à partir de l'adresse 0xd2200000 et enfin ceux du TIMER à partir de l'adresse 0xd3200000. Le rôle de ces registres est rappelé en partie dans ce texte et pour plus de détails, vous devez revoir le cours. Le choix des adresses de ces contrôleurs est fait par le créateur du matériel, elles ne peuvent pas être changées par le logiciel. Ces adresses sont données dans le fichier ldscript du kernel ( kernel.ld ) parce qu'elles ne sont utilisables que si le MIPS est en mode kernel (adresses > 0x80000000 ). IRQ et ISR Les IRQ (Interrupt ReQuest)s sont des signaux électriques à 2 états (ON/OFF, on dit aussi Actif/Inactif ou encore Levé/Baissé). Les IRQ sont levés par les contrôleurs de périphériques pour prévenir d'un événement (fin de commande, arrivée d'une donnée, etc.). Quand une IRQ se lève, la conséquence est que le programme en cours d'exécution sur le processeur recevant l'IRQ est interrompu et qu'il est dérouté vers le noyau pour que ce dernier exécute l'ISR prévue pour l'IRQ. Les IRQ provoquent donc l'exécution d'ISR (Interrupt Service Routine) par le noyau. Les IRQ sont des signaux d'état qui doivent rester levés/activés tant qu'ils n'ont pas été acquittés par une ISR. Les ISR sont des fonctions qui reçoivent en argument un identifiant du contrôleur de périphérique qui a levé l'IRQ. Notez que ce n'est pas le processeur qui est interrompu, c'est bien le programme, car le processeur est seulement dérouté vers le noyau, mais il continue à travailler. Une ISR doit faire deux choses, 1. accéder aux registres du contrôleur de périphérique concerné pour faire ce que le périphérique demande 2. acquitter l'IRQ, c'est-à-dire demander au contrôleur de périphérique de baisser/désactiver son IRQ (puisque celle-ci a été traitée). La demande d'acquittement est spécifique à chaque contrôleur de périphérique. Pour le TTY, il faut lire le registre TTY_READ. Pour le TIMER, il faut écrire dans le registre TIMER_RSTIRQ. Routage et masquage des IRQ Sur le schéma de la plateforme des TP, on peut voir que seuls les composants TTY et TIMER peuvent lever des IRQ. Les IRQ de ces contrôleurs de périphériques sont envoyés au composant ICU qui va les combiner pour produire un unique signal IRQ pour le processeur. Une IRQ peut être masquée, c'est-à-dire que le processeur ne va pas interrompre le programme en cours. Le masquage peut être demandé à plusieurs endroits : dans le composant ICU et dans le processeur lui-même (parfois même dans le contrôleur de périphérique). Le masquage est demandé par le noyau, le plus souvent de manière temporaire, quand il doit exécuter un code critique qui ne doit surtout pas être interrompu. Sur le schéma ci-dessus, on voit que l'IRQ du TTY0 est reliée à l'entrée n° 10 de l'ICU, c'est un choix matériel qui n'est pas modifiable par logiciel. Son état est donc enregistré dans le bit n°10 du registre ICU_STATE. Il y a un AND avec le bit 10 du registre ICU_MASK. Si le bit 10 du registre ICU_MASK est à 0 , alors la sortie du AND est 0 et l'IRQ est masquée (donc invisible pour le processeur). Le registre ICU_HIGHEST contient toujours le numéro de l'IRQ active la plus prioritaire, comme il n'y en a qu'une dans cet exemple, ICU_HIGHEST contient 10 (l'IRQ prioritaire, pour cette ICU, est l'IRQ active dont le numéro est le plus petit). La sortie IRQ de l'ICU est reliée à l'entrée 0 des 6 IRQs possibles du MIPS et sa valeur s'inscrit dans le registre HWI0 du registre c0_cause. Il y a un AND avec le bit HWI0 du registre c0_status. Si le bit HWI0 du registre c0_status est à 0, alors la sortie du AND est 0 et l'IRQ est aussi masquée. Enfin, il y a un dernier AND avec le bit 0 de c0_status (correspondant au bit IE pour Interrupt Enable) qui permet de masquer globalement les IRQ et avec le NOT du bit 1 de c0_status (correspondant au bit EXL EXception Level). Quand le signal IRQ vue par le MIPS s'active (passe à 1), c'est que l'IRQ levée par le contrôleur de périphérique doit être prise en charge. Le programme en cours d'exécution est interrompu et dérouté vers kentry à l'adresse 0x80000180 et en même temps C0_EPC ← PC+4 , c0_cause.XCODE ← 0 , c0_status.EXL ← 1 (notez que le nom officiel de c0_status est C0_SR , mais dans ce document, on utilise c0_status pour plus de clarté). Conséquence dans le noyau Dans le schéma ci-après, à gauche c'est le matériel et à droite c'est un extrait de la RAM contenant les structures de données utilisées par le noyau pour la gestion des IRQ. À gauche, on voit que les IRQ venant des contrôleurs de périphériques sont connectés aux entrées d'IRQ de l'ICU. Il y a 32 entrées possibles. Sur notre plateforme, par exemple l'IRQ du TTY2 est connectée à l'entrée 12 de l'ICU. Ce numéro d'entrée est le numéro qui identifie le contrôleur de périphérique. Notez que le registre ICU_MASK est en lecture seule, c'est-à-dire qu'il ne peut pas être écrit directement. Pour modifier le contenu du registre ICU_MASK , il faut utiliser deux autres registres de l' ICU : ICU_SET et ICU_CLEAR. ICU_SET permet de mettre à 1 les bits de ICU_MASK , et ICU_CLEAR permet de les mettre à 0. Pour mettre à 1 le bit i du registre ICU_MASK , il faut écrire 1 dans le bit i du registre ICU_SET. Pour mettre à 0 le bit j du registre ICU_MASK , il faut aussi écrire 1 dans le bit j du registre ICU_CLEAR. À droite, il y a les deux tableaux que le noyau utilise pour connaitre l'ISR à exécuter pour chaque numéro d'IRQ. Ce couple de tableaux se nomme vecteur d'interruption et comme il y a 32 entrées d'IRQ dans l'ICU, ces tableaux ont 32 cases chacun. Ici, le vecteur d'interruption est composé des tableaux IRQ_VECTOR_ISR[] et IRQ_VECTOR_DEV[]. Le vecteur d'interruption est indexé par les numéros d'IRQ. Il contient deux informations: 1. dans la case n° i du tableau IRQ_VECTOR_ISR[] , on trouve le pointeur sur la fonction ISR à appeler si l'IRQ n° i est levée 2. dans la case n° i du tableau IRQ_VECTOR_DEV[] , on trouve le numéro de l'instance du périphérique. Cette dernière information est nécessaire dans le cas des contrôleurs de périphérique multi-instances comme le TTY afin de savoir quel jeu de registres la fonction ISR doit utiliser. En effet, il y a une fonction ISR unique à exécuter quel que soit le numéro du TTY. Dans cette plateforme, comme il y a 4 TTY, l'adresse de la fonction ISR est placée dans les cases 10 , 11 , 12 , et 13 du tableau IRQ_VECTOR_ISR[]. Dans les cases 10 , 11 , 12 , et 13 du tableau IRQ_VECTOR_DEV[] , on a les valeurs 0 , 1 , 2 et 3 qui correspondent bien au numéro d'instance des TTYs. Rappel sur les 3 registres du coprocesseur 0 impliqués Nous rappelons ci-après les 3 registres du coprocesseur 0 utilisés au moment de l'entrée dans le noyau, quelle que soit la cause : syscall (vu la semaine dernière), interruption (TD de cette semaine) et exception (dans le cas de problèmes lors de l'exécution du programme comme la division par 0). On rappelle aussi que les seules instructions qui peuvent manipuler ces registres sont mtc0 et mfc0 pour, respectivement, les écrire et les lire. Les bits HWI0 des registres c0_status (aussi nommé c0_sr ) et c0_cause contiennent respectivement le mask et le l'état de l'entrée n° 0 d'interruption du MIPS. Les bits UM , IE et EXL sont liés au mode d'exécution du MIPS: UM est le bit de mode du MIPS ( 1 = User Mode , 0 = Kernel Mode ), IE est le bit de masque général des interruptions ( 1 =autorisées, 0 =masquées) et enfin EXL est le bit que le MIPS met à 1 à l'entrée dans le noyau pour informer d'un niveau exceptionnel et dans ce cas les bits UM et IE ne sont plus significatifs, si EXL est à 1 alors le MIPS est en mode kernel, et les interruptions sont masquées. Questions La majorité des réponses aux questions ci-après sont dans le rappel du cours donné au début de cette page, c'est voulu. 1. À quelles adresses dans l'espace d'adressage sont placés les registres des 3 contrôleurs de périphériques de la plateforme et comment le kernel les connaît ? __tty_regs_map = 0xd0200000 ; __icu_regs_map = 0xd2200000 ; __timer_regs_map = 0xd3200000 ; Ces adresses sont définies dans le ldscript du kernel kernel.ld , elles doivent être déclarées extern dans les codes C qui les utilisent. 2. Que signifie l'acronyme I.R.Q. ? Interrupt ReQuest ou, en français, requête d'interruption 3. Une IRQ est un signal électrique, combien peut-il avoir d'états ? C'est un signal à 2 états, c'est binaire. Il y a l'état ON (on dit aussi levé ou actif) pour dire que l'interruption est demandée et il y a l'état OFF (on dit aussi baissé ou inactif) pour dire que l'interruption n'est pas demandée). 4. Qu'est-ce qui provoque une IRQ ? C'est un événement matériel sur le contrôleur de périphérique, comme la fin d'une commande ou l'arrivée d'une donnée. 5. Les IRQ relient des composants sources et des composants destinataires, quels sont ces composants ? Donnez un exemple. Les composants sources sont les contrôleurs de périphériques par exemple le TTY et les composants destinataires sont les processeurs (ici le MIPS). 6. Que signifie masquer une IRQ ? Cela signifie que l'on bloque le signal entre sa source et sa destination. Si une IRQ est levée par un contrôleur de périphérique et que l'on masque cette IRQ, alors l'IRQ est toujours levée à sa source, mais le MIPS destinataire ne le voit pas. L'information, le signal, a été masquée sur le chemin entre la source et la destination. Cette IRQ devient invisible pour le MIPS. 7. Quels composants peuvent masquer une IRQ ? Ici, c'est le composant ICU et le MIPS lui-même. 8. Est-ce qu'une application utilisateur peut demander le masquage d'une IRQ ? Non, puisque pour masquer une interruption, il faut écrire dans les registres de l'ICU ou dans les registres système du processeur. Or, les registres de configuration de l'ICU sont mappés dans la partie de l'espace d'adressage inaccessible en mode user et que les instructions mfc0 et mtc0 sont interdites en mode user, leur usage provoque une exception de type violation de privilège. 9. Que signifie l'acronyme I.S.R. ? Interrupt Service Routine ou, en français, routine d'interruption. En fait, c'est une fonction C normale. 10. Dans la plateforme des TPs, sur quelles entrées de l'ICU sont branchées les IRQ venant des TTYs et du TIMER ? Les 4 IRQ de TTYs sont branchées sur les entrées 10 , 11 , 12 et 13 de l'ICU et l'IRQ du TIMER est sur l'entrée 0. 11. Quelle valeur faut il avoir dans le registre ICU_MASK si on veut recevoir seulement les IRQ venant des 4 TTYs, dans le cas de la plateforme utilisée en TP ? Donnez le nombre en binaire et en hexadécimal. Il faut que les bits 10 , 11 , 12 et 13 de ICU_MASK soit à 1 donc 0b00000000.00000000.00111100.00000000 donc 0x00003C00. 12. L'écriture dans ICU_MASK n'est pas possible, comment modifier ce registre pour mettre à 1 le bit 10 ? Il faut écrire 1 dans le bit 10 de ICU_SET. 13. Sur une plateforme (autre que celle des TP) sur laquelle on aurait un TTY0 sur l'entrée 5, un TIMER sur l'entrée 2, et un autre TTY1 sur l'entrée 14. Que doit-on faire pour que seuls le TTY1 et le TIMER soient démasqués et que TTY0 soit masqué ? on doit écrire 1 dans les bits 2 et 14 du registre ICU_SET donc 0b00000000.00000000.01000000.00000100 = 0x00004004 on doit écrire 1 dans le bit 5 du registre ICU_CLEAR donc 0b00000000.00000000.00000000.00100000 = 0x00000020 pour être sûr que le bit 5 de ICU_MASK soit à 0. (Au reset, tous les bits de ICU_MASK sont à 0, mais là on ne sait pas si c'est juste après le reset) 14. Si on définit dans le code C: struct icu_s { int state; // état des IRQ à l'entrée de l'ICU int mask; // masque des IRQ int set; // registre de mise à 1 des bits du registre mask int clear; // registre de mise à 0 des bits du registre mask int highest; // numéro de l'IRQ active la plus prioritaire int unused; // 3 registres non utilisés }; extern volatile struct icu_s __icu_regs_map; // déclaration de cette struct pour gcc Ecrivez la fonction mettant à 1 le bit n° irq du registre ICU_MASK : void icu_set_mask (int irq) void icu_set_mask (int irq) { __icu_regs_map.set = 1 enable specific IRQs for this ICU int clear; // IRQ clear --> disable specific IRQs for this ICU int highest; // highest pritority IRQ number for this ICU int unused; // these 3 registers are not used }; extern volatile struct icu_s __icu_regs_map[NCPUS]; static int icu_get_highest (int icu) { return __icu_regs_map[icu].highest; } static void icu_set_mask (int icu, int irq) { __icu_regs_map[icu].set = 1