ALMO TD n°7 - Gestionnaire d'Interruptions, Exceptions et Trappes
Préambule
Le but de ce TD est l'analyse du code du Gestionnaire d'Interruptions, Exceptions et Trappes (GIET) utilisé dans l'U.E. ALMO.
Segmentation de l'espace adressable
On rappelle que la plateforme matérielle définit un espace d'adressage de 4 Giga-octets (les adresses sont sur 32 bits), et que 8 segments sont définis dans cet espace, dont les adresses de base sont définies ci-dessous :
- le segment du code utilisateur de l'application, à l'adresse
0x00400000
- le segment des données de l'application, à l'adresse
0x10000000
- le segment de la pile d'exécution, à l'adresse
0x20000000
- le segment du code de reset, à l'adresse
0xBFC00000
- le segment du code du système, à l'adresse
0x80000000
- le segment des données du système, à l'adresse
0x81000000
- le segment des données non cachables du système, à l'adresse
0x82000000
- le segment des périphériques, à l'adresse
0x90000000
Suivant le nombre de périphériques présents dans la plateforme matérielle, le segment des périphériques est lui-même décomposé en plusieurs sous-segments (un par périphérique).
Les 5 derniers segments ne sont accessibles qu'en mode superviseur (adresses supérieures ou égales à 0x8000000
).
Les trois segments contenant du code exécutable sont les suivants :
- Le segment
seg_reset
contient le code de d'initialisation (fichierreset.s
) qui s'exécute en mode superviseur au démarrage de la machine, ou lors de l'activation du signalnreset
. Ses deux missions principales sont le chargement en mémoire du système d'exploitation et la configuration des périphériques. (Dans cette U.E., la phase de chargement de l'OS dans la mémoire n'est pas considérée).
- Le segment
seg_kcode
contient le code qui doit s'exécuter en mode superviseur, en cas d'interruption, d'exception, ou d'appel système. Il s'agit donc du code du GIET que nous voulons analyser dans ce TD.
- Le segment
seg_code
contient le code qui s'exécute en mode utilisateur, c'est-à-dire : le code de l'application logicielle (fichiermain.c
), et le code des appels système du côté utilisateur (fichierstdio.c
).
Structure du GIET
Lorsque le processeur est en train d'exécuter une application logicielle (en mode utilisateur), il existe trois types d'événements qui peuvent l'amener à passer en mode superviseur pour se brancher au GIET, c'est à dire à l'adresse 0x80000180
:
- Les exceptions sont généralement des erreurs de programmation, telles qu'une division par 0 ou une adresse mal formée (par exemple, la lecture d'un mot mémoire de 32 bits à une adresse non-alignée sur 4 octets, ou encore la lecture d'un mot à une adresse inexistante - non comprise dans les segments définis). Les exceptions entraînent généralement la fin du programme fautif. Typiquement, le gestionnaire d'exceptions doit déterminer le type de l'exception, et afficher un message permettant au programmeur de localiser et de corriger l'erreur. Cette classe d'événements est traitée par le gestionnaire d'exceptions (fichier
exc_handler.c
).
- Les interruptions provenant de périphériques sont imprévisibles du point de vue du logiciel en cours d'exécution sur le processeur, et peuvent donc survenir à tout instant. Si une interruption n'est pas masquée, l'activation d'une ligne d'interruption matérielle déclenche l'exécution d'une routine spécifique (ISR), permettant ainsi au périphérique source de voler quelques cycles au processeur pour effectuer un traitement spécifique. L'application interrompue reprend ensuite son exécution normale. Typiquement, le gestionnaire d'interruptions doit déterminer la source de l'interruption, en interrogeant le composant ICU (concentrateur d'interruptions), afin d'exécuter l'ISR appropriée. Le processeur
MIPS32
utilisé dispose de 6 entrées d'interruption matérielle, mais seule l'entrée 0 du processeur est utilisée, et il faut donc systématiquement interroger le composant ICU. Cette classe d'événements est traitée par le gestionnaire d'interruptions (fichierirq_handler.c
).
- Les trappes (ou appels système) sont des requêtes effectuées par une application logicielle utilisateur vers le système d'exploitation (au moyen de l'instruction assembleur
syscall
) pour demander un service, tel que l'écriture d'un caractère sur le terminal TTY, ou une écriture sur le disque dur. Les trappes s'apparentent à des appels de fonction un peu spéciaux. Il faut récupérer le numéro du service demandé (dans le registre$2
), récupérer les valeurs des arguments (situés dans les registres$4
,$5
,$6
et$7
), et enfin appeler la fonction système requise. Au besoin, le GIET peut utiliser la pile d'exécution de l'application ayant effectué l'appel système. Cette classe d'événements est traitée par le gestionnaire d'appels système (fichiersys_handler.c
).
Le code du GIET contient une toute petite partie en assembleur, mais la majeure partie du code (les trois gestionnaires d'événements définis ci-dessus) est écrite en C.
La partie en assembleur est dans le fichier giet.s
. Ce fichier contient le code d'analyse de la cause de l'appel GIET, et d'aiguillage vers l'un des trois gestionnaires d'événements. Il contient également la fonction de changement de contexte, _ctx_switch
, utilisée quand le processeur est utilisé en mode multi-tâches. Cette fonctionnalité ne sera utilisée qu'à la fin de l'U.E. ALMO, et ne sera pas analysée dans ce TD.
1. Analyse de la cause de l'appel au GIET : fichier giet.s
Il y a 16 causes possibles, qui sont définies par les 16 valeurs possibles du champs XCODE
du registre CR (Cause Register : registre 13 du coprocesseur 0).
- Dans quelle section se trouve le code d'entrée du GIET ? Quelle est la directive qui permet de définir le nom de la section ? Comment peut-on imposer le placement de cette section dans le segment
seg_kcode
? - Que contient le tableau
_cause_vector
? Quelle taille fait-il ? Où sont définies les étiquettes qu'il contient ?
Fichier giet.s
:
- À quoi sert la directive
.space 0x180
spécifiée au commencement de la section.giet
et avant le début de la fonction_giet
? - Expliquez ce que fait chacune des 6 instructions assembleur qui composent la fonction
_giet
. Comment pourrait-on l'écrire en une ligne de pseudo-code ? - Quels registres utilise-t-on dans ce code assembleur ? Comment expliquez-vous qu'on les utilise sans sauvegarder leur contenu au préalable ?
- Pourquoi les interruptions sont-elles automatiquement masquées par le processeur quand celui-ci se branche à la première instruction du GIET ?
2. Analyse du gestionnaire des appels systèmes : fichier sys_handler.c
- Rappelez comment une application logicielle utilisateur fait un appel système.
Fichier giet.s
:
- La fonction
_sys_handler
définie dans le fichiergiet.s
sert à aiguiller le programme vers l'appel système approprié, en fonction du numéro de service contenu dans le registre$2
. Expliquez ce que font les 6 premières instructions de cette fonction (deaddiu
jusqu'àsw
). - Analysez les 5 instructions suivantes (de
andi
àlw
) pour déterminer le nombre maximum d'appels systèmes acceptés par le GIET. Comment est obtenue l'adresse de la fonction système à exécuter pour traiter l'appel système requis ?
La compréhension des 6 instructions suivantes (de li
à mtc0
) nécessite une lecture attentive de la section E/6 du document MIPS32 : Architecture Externe afin de bien comprendre la signification des différents bits du registre de contrôle SR
. Ces instructions notamment ont pour effet :
- de passer le processeur en mode noyau de manière stable : bit
UM
à0
, bitEXL
à0
. - de démasquer les interruptions : bit
IE
à1
. - d'appeler la fonction système demandée (instruction
jal
) - de remasquer les interruptions lorsqu'on a terminé l'exécution de l'appel système.
- Pourquoi veut-on autoriser les interruptions pendant l'exécution d'un appel système ?
- Que font les 6 dernières instructions (de
lw
àeret
) ? - À quoi sert le registre de contrôle
EPC
? Pourquoi faut-il le sauver dans la pile ?
On analyse maintenant le fichier sys_handler.c
.
- Quelle est l'utilité de la routine
_sys_ukn
? - Où sont définies les fonctions du tableau
_syscall_vector
? Quel est le point commun de toutes ces routines ?
3. Analyse du gestionnaire de traitement des interruptions : fichier irq_handler.c
- La fonction
_int_handler
(définie dans le fichiergiet.s
), et la fonction_int_demux
(définie dans le fichier int_handler.c) permettent de se brancher vers l'ISR correspondant au numéro de l'interruption. Que veut dire ISR ? - Que fait la fonction assembleur _int_handler ?
- Que fait la fonction
_int_demux
(fichierirq_handler.c
). - La fonction
_int_demux
utilise aussi une table de saut. Quel nom donne-t-on habituellement à cette table de saut ? À quel moment cette table doit-elle être initialisée ? - Pourquoi le gestionnaire d'interruption,
_int_handler
, sauve-t-il le registre$31
dans la pile ? - Pourquoi le gestionnaire d'interruption ne démasque-t-il pas les interruptions ?
- La routine
_isr_default
est l'action par défaut appelée par le gestionnaire d'interruptions. Pourquoi faut-il définir cette ISR par défaut ?
4. Analyse du gestionnaire de traitement des exceptions : Fichier exc_handler.c
- En quoi consiste le traitement des exceptions ?
- Sur quel point important le traitement des exceptions diffère-t-il du traitement des interruptions ou des appels systèmes ?