- Makefile
- Les fichiers du repertoire 'boot' permettent de construire
le secteur de boot. Pour une explication de leur contenu, voir
le chapitre 2
et le chapitre 6 :
- include/aff.h
voir le chapitre 6.
- include/gdt.h
voir le chapitre 6.
- include/types.h
voir le chapitre 6.
- include/sys.h
definit des macros permettant d'inclure de facon transparente
des instructions en assembleur dans un code en C.
Un certain nombre d'instructions existant en assembleur n'ont
pas d'equivalent en C. C'est par exemple le cas des instructions
'cli', 'sti', 'in' et 'out'. Or nous avons besoin de celles-ci
dans notre code. Pour les inclures, on utilise la fonction
asm.
Pour une explication detaillee de asm,
je conseille de se referer au manuel du
GCC.
L'assembleur GNU a une syntaxe assez particuliere (pour ne pas dire deroutante).
Le mieux est de se referer au tres clair manuel de
GAS.
On redefinit ici les instructions 'cli', qui inhibe les interruptions,
et 'sti' qui les retabli.
Les interruptions
doivent rester inhibees tant que le gestionnaire d'interruptions
n'est pas initialise par le kernel dans sa phase de boot.
Ensuite, on a regulierement besoin d'inhiber et de retablir
les interruptions quand on concoit un driver ou pour eviter
que du code lie a une interruption soit reentrant :
#define cli asm("cli"::)
#define sti asm("sti"::)
Les macros 'inb', 'outb' et 'outbp' permettent de lire et d'ecrire
sur des ports d'entree/sortie. La macro 'outbp' ajoute une temporisation
par le biais d'un saut :
#define outb(port,value) \
asm volatile ("outb %%al,%%dx" \
: \
:"d" (port), "a" (value));
#define outbp(port,value) \
asm volatile ("outb %%al,%%dx; jmp 1f; 1:" \
: \
:"d" (port), "a" (value));
#define inb(port) ({ \
unsigned char _v; \
asm volatile ("inb %%dx,%%al" \
: "=a" (_v) \
: "d" (port)); \
_v; })
- include/idt.h
definit les structures necessaires a la manipulation de
la table IDT.
Les macros IDTBASE et IDTSIZE definissent l'emplacement en memoire
ou residera l'IDT et sa taille :
#define IDTBASE 0 /* addr. physique ou doit resider la gdt */
#define IDTSIZE 256 /* nombre max. de descripteurs dans la table */
Les macros suivantes sont utilisees comme parametre aux fonctions
d'initialisation des descripteurs systemes definies dans le fichier
gdt.c. Dans le code actuel, nous allons utiliser uniquement
la macro INTGATE afin d'initialiser un descripteur systeme en tant
qu' "interrupt gate" :
#define INTGATE 0x8E00 /* utilise pour gerer les interruptions */
#define TRAPGATE 0x8F00 /* utilise pour faire des appels systemes */
#define TASKGATE 0x8500 /* utilise pour commuter des taches */
La structure 'idtdesc' correspond aux descripteurs systemes
utilises dans l'IDT :
/* descripteur de segment */
typedef struct idtdesc {
u16 offset0_15;
u16 select;
u16 type;
u16 offset16_31;
} idtdesc __attribute__ ((packed));
On definit la structure qui sera chargee dans le registre IDTR :
/* registre IDTR */
struct idtr {
u16 limite;
u32 base;
} __attribute__ ((packed));
struct idtr kidtr;
Puis on declare le tableau 'kidt' qui contient les descripteurs
et 'kidtptr', un index sur le premier descripteur non-initialise
dans la table.
/* table de IDT */
idtdesc kidt[IDTSIZE] = {0, 0, 0, 0, 0, 0, 0};
/* pointeur sur un descripteur libre dans la IDT */
unsigned int kidtptr = 0;
- kern/Makefile
- kern/aff.c
voir le chapitre 6.
- kern/gdt.c
voir le chapitre 6.
- kern/util.c
voir le chapitre 6.
- kern/pic.c
contient la sequence de commandes envoyees au controleur d'interruptions
afin de l'initialiser. On utilise la macro 'outbp' definie
dans le fichier 'sys.h'.
- kern/idt.c
contient les fonctions qui permettent d'initialiser et de manipuler
des descripteurs dans l'IDT.
La fonction 'init_idt' utilise les fonctions de ce fichier
pour mettre en place le gestionnaire d'interruptions.
Elle commence par initialiser tous les descripteurs systemes
en tant qu' "interrupt gate". Le code appele par defaut par les
interruptions est la fonction 'default_int' definie dans le
fichier 'int.asm' :
/*
* Cette fonction initialise la IDT apres que le kernel soit charge
* en memoire.
*/
void init_idt(void) {
idtdesc desc;
int i;
/* initialisation des descripteurs systeme */
for(i=0;i<IDTSIZE;i++) {
init_idt_desc(default_int, 0x08, INTGATE, &desc);
add_idt_desc(desc);
}
Ensuite, la fonction initialise les gates utilises par les
exceptions et les interruptions materielles.
Apres cela, on initialise la structure kidtr destinee a etre chargee
dans le registre IDTR :
/* initialisation de la structure pour IDTR */
kidtr.limite = IDTSIZE*8;
kidtr.base = IDTBASE;
Puis on recopie le contenu du tableau 'kidt' a la place ou doit
resider l'IDT en memoire (place definie dans le registre IDTR) :
/* recopie de la IDT a son adresse */
memcopy(kidt, kidtr.base, kidtr.limite);
Une fois que l'IDT est prete, on peut charger le registre IDTR :
/* chargement du registre IDTR */
asm("lidtl (kidtr)");
A partir de ce moment, le gestionnaire d'interruptions est
fonctionnel. Il ne restera plus qu'a retablir les interruptions
avec l'instruction 'sti' pour l'utiliser.
- kern/int.asm
contient les routines appelees lors d'interruptions.
Ce fichier est en assembleur car l'instruction de retour
de fonction utilisee est 'iret' (et non pas l'instruction
de retour 'ret', utilisee normalement). Cette instruction permet
de retablir le registre d'etat 'eflags' sauvegarde sur la pile
par l'interruption.
Chacune de ces fonctions appelle une routine ecrite en C,
definie dans le fichier 'interrupt.c' et envoie au
controleur d'interruptions une commande qui signale
que le traitement de l'interruption courante est termine
(signal EOI).
La routine appelee par l'irq1, liee au clavier, est definie
dans le fichier 'kbd.c'.
- kern/interrupt.c
contient les routines appelees lors d'interruptions.
Comme ce gestionnaire d'interruptions est minimaliste, elles
ne font qu'afficher une chaine de caractere.
- kern/kbd.c
contient la routine appelee lors d'interruptions provoquees
par l'appui ou le relachement d'une touche du clavier. Le code
sera explique plus en detail au chapitre suivant, mais en gros,
on communique avec les chipsets 6805 et 8042 pour vider le buffer
contenant le scancode de la touche actionnee pour ne pas bloquer
le clavier.
- kern/kernel.c
contient la fonction principale du noyau. Celui-ci initialise
la GDT, les interruptions et le controleur d'interruptions.
Ensuite, les interruptions jusque la inhibee sont retablies
grace la l'instruction 'sti' definie dans 'sys.h'.