Changes between Version 91 and Version 92 of Archi-1-TP10


Ignore:
Timestamp:
Nov 28, 2021, 10:41:03 AM (3 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Archi-1-TP10

    v91 v92  
    204204
    205205
    206 1. En assembleur, vous utilisez les sections prédéfinies `.data` et `.text` pour placer respectivement les data et le code, mais vous pouvez créer vos propres sections avec la directive `.section` (nous avons utilisé cette possibilité pour la section `.boot`). Il est aussi possible d'imposer ou de créer des sections en langage C avec la directive `__attribute__`. Cette directive du C permet de demander certains comportements au compilateur. Il y a beaucoup d'attributs possibles (si cela vous intéresse vous pouvez regarder dans la [https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html doc de GCC sur les attributs]. En cours, nous avons vu un attribut permettant de désigner ou créer une section dans laquelle est mise la fonction concernée. Quelle est la syntaxe de cet attribut ?
     2061. En assembleur, vous utilisez les sections prédéfinies `.data` et `.text` pour placer respectivement les data et le code, mais vous pouvez créer vos propres sections avec la directive `.section` (nous avons utilisé cette possibilité pour la section `.boot`). Il est aussi possible d'imposer ou de créer des sections en langage C avec la directive `__attribute__((section("section-name")))`. La directive du C `__attribute__` permet de demander certains comportements au compilateur. Ici, c'est la création d'une section, mais il y a beaucoup d'attributs possibles (si cela vous intéresse vous pouvez regarder dans la [https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html doc de GCC sur les attributs]. Comment créer la section `.start` en C ?
    207207{{{#!protected ------------------------------------------------------------------------------------
    208208''
    209209Cours 10 / slide 38
    210 - `__attribute__ ((section (".start")))`\\Remarquez la syntaxe un peu curieuse avec les doubles underscore et les doubles parenthèses.
     210- `__attribute__ ((section (".start")))`\\La syntaxe est un peu curieuse avec les doubles underscore et les doubles parenthèses.
    211211''
    212212}}}
     
    378378      **mais** comme `EXL`=`1`, alors on reste en mode `kernel` avec interruptions masquées. L'exécution de l'instruction `eret` mettra `EXL` à `0` pour rendre les bits `UM` et `IE` actifs et passer en mode `user` (ici avec interruptions masquées).
    379379  - Le registre GPR `$29` reçoit l'adresse de la première adresse après la section `.data`. C'est le haut de la pile. 
    380 '''''''''''''''
     380''
    381381}}}
    3823821. Que faire avant l'exécution de la fonction `main()` du point de vue de l'initialisation? Et au retour de la fonction `main()`?
    383383{{{#!protected ------------------------------------------------------------------------------------
    384 '''''''''''''''
     384''
     385Cours 10 / slide 38
    385386- Comme dans la fonction `kinit()`, il faut explicitement initialiser les variables globales non initialisées dans le programme C.
    386 - Si on sort de la fonction `main()`, l'application s'achève. Cela signifie qu'il faut appeler la fonction `exit()` qui effectue l'appel système EXIT. Cette appel est réalisé au cas où l'application n'aurait pas explicitement exécuté `exit()`.
    387 '''''''''''''''
     387- Si on sort de la fonction `main()`, l'application s'achève. Cela signifie qu'il faut appeler la fonction `exit()` qui effectue l'appel système SYSCALL_EXIT. Cette appel est réalisé au cas où l'application n'aurait pas explicitement exécuté `exit()`. Dans ce cas la valeur rendue par l'application est la valeur de retour de la fonction `main()`.
     388''
    388389}}}
    3893901. Nous avons vu que le noyau est sollicité par des événements, quels sont-ils? Nous rappelons que l'instruction `syscall` initialise le registre `c0_cause`, comment le noyau fait-il pour connaître la cause de son appel?
    390391{{{#!protected ------------------------------------------------------------------------------------
    391 '''''''''''''''
     392''
     393Cours 10 / slide 17
    392394- Il y en a 3 (si on excepte le signal `reset` qui redémarre tout le système:
    393395  1. Les appels système donc l'exécution de l'instruction `syscall`.
     
    395397  1. Les interruptions qui sont des demandes d'intervention provenant des périphériques.
    396398- L'instruction `syscall` initialise les 4 bits `XCODE` du registre `c0_cause` avec un code indiquant la raison de l'entrée dans le noyau. Le noyau doit analyser ce champ `XCODE`.
    397 '''''''''''''''
    398 }}}
    399 1. `$26` et `$27` sont deux registres temporaires que le noyau se réserve pour faire des calculs sans qu'il ait besoin de les sauvegarder dans la pile. Ce ne sont pas des registres système comme `c0_sr` ou `c0_epc`. En effet, l'usage de ces registres (`$26` et `$27`) par l'utilisateur ne provoque pas d'exception du MIPS. Toutefois si le noyau est appelé alors il modifie ces registres et donc l'utilisateur perd leur valeur.\\Le code assembleur ci-après contient les instructions exécutées à l'entrée dans le noyau, quelle que soit la cause. Les commentaires présents dans le code ont été volontairement retirés (ils sont dans les fichiers du TP). La section `.kentry` est placée à l'adresse `0x80000000` par l'éditeur de lien. La directive `.org` (ligne 16) permet de déplacer le pointeur de remplissage de la section courante du nombre d'octets donnés en argument, ici `0x180`. Pouvez-vous dire pourquoi ? Expliquer les lignes 25 à 28.\\ \\**`kernel/hcpu.S`**
     399''
     400}}}
     4011. `$26` et `$27` sont deux registres temporaires que le noyau se réserve pour faire des calculs sans qu'il ait besoin de les sauvegarder dans la pile. **Ce ne sont pas des registres système** comme `c0_sr` ou `c0_epc`. En effet, l'usage de ces registres (`$26` et `$27`) par l'utilisateur ne provoque pas d'exception du MIPS. Toutefois si le noyau est appelé alors il modifie ces registres et donc l'utilisateur perd leur valeur.\\Le code assembleur ci-après contient les instructions exécutées à l'entrée dans le noyau, quelle que soit la cause. Les commentaires présents dans le code ont été volontairement retirés (ils sont dans les fichiers du TP). La section `.kentry` est placée à l'adresse `0x80000000` par l'éditeur de lien. Ligne 16, la directive `.org DEP` (`.org` pour `origine`) permet de placer le pointeur de remplissage de la section courante à `DEP` octets du début de la section, ici `DEP = 0x180`.  Aurait-on pu remplacer le `.org 0x180` par `.space 0x180` ? Expliquer les lignes 25 à 28.\\ \\**`kernel/hcpua.S`**
    400402{{{#!c
    401403 15 .section    .kentry,"ax"     
     
    410412}}}
    411413{{{#!protected ------------------------------------------------------------------------------------
    412 '''''''''''''''
    413 - La section `kentry` est placée à l'adresse `0x80000000` or l'entrée du noyau est `0x80000180`, il faut donc déplacer le pointeur de remplissage de la section `ktentry` de `0x180`. Remarquez qu'on aurait pu utiliser une directive `.space 0x180`.
     414''
     415Cours 10 / slide 41 (mais il n'y a pas ces détails)
     416- La section `kentry` est placée à l'adresse `0x80000000` or l'entrée du noyau est `0x80000180` (l'entrée du noyau est l'adresse à laquelle le processeur ''saute'' lors de l'exécution `syscall`), il faut donc déplacer le pointeur de remplissage de la section `ktentry` de `0x180`. La directive `.space 0x180` réserve `0x180`, si on met cette directive au tout début de la section, c'est équivalent.
    414417- Commentaire du code
    415418  - Ligne 25 : `$26` **←**  `c0_cause`\\⟶ donc le registre `$26`GPR réservé au kernel prend la valeur du registre de cause.
     
    417420  - Ligne 27 : `$27` **←**  `0b00100000`\\⟶ On initialise le registre GPR réservé au kernel $27 avec la valeur attendue dans $26 s'il s'agit d'une cause `syscall`.
    418421  - Ligne 28 : si `$26` ≠ `$27` goto not_syscall\\⟶ Si ce n'est pas un `syscall`, on va plus loin, sinon on continue en séquence.
    419 '''''''''''''''
    420 }}}
    421 1. Le gestionnaire de `syscall` est la partie du code qui gère le comportement du noyau lors de l'exécution de l'instruction `syscall`. C'est un code en assembleur présent dans le fichier `kernel/hcpu.S` que nous allons observer. Pour vous aider dans la compréhension de ce code, vous devez imaginer que l'instruction `syscall` est un peu comme un appel de fonction. Ce code utilise un tableau de pointeurs de fonctions nommé `syscall_vector` définit dans le fichier `kernel/ksyscalls.c`. Les lignes `36` à `43` sont chargées d'allouer de la place dans la pile.\\Dessinez l'état de la pile après l'exécution de ces instructions. Que fait l'instruction ligne `44` et quelle conséquence cela a-t-il? Que font les lignes `46` à `51`? Et enfin que font les lignes `53` à `59` sans détailler ligne à ligne.\\ \\**`common/syscalls.h`**
     422''
     423}}}
     4241. Le gestionnaire de `syscall` est la partie du code qui gère le comportement du noyau lors de l'exécution de l'instruction `syscall`. C'est un code en assembleur présent dans le fichier `kernel/hcpua.S` que nous allons observer. Pour vous aider dans la compréhension de ce code, vous devez imaginer que l'instruction `syscall` est un peu comme un appel de fonction. Ce code utilise un tableau de pointeurs de fonctions nommé `syscall_vector[]` définit dans le fichier `kernel/ksyscalls.c`. Les lignes `36` à `43` sont chargées d'allouer de la place dans la pile.\\Dessinez l'état de la pile après l'exécution de ces instructions. Que fait l'instruction ligne `44` et quelle conséquence cela a-t-il? Que font les lignes `46` à `51`? Et enfin que font les lignes `53` à `59` sans détailler ligne à ligne.\\ \\**`common/syscalls.h`**
    422425{{{#!c
    423426  1 #define SYSCALL_EXIT        0
     
    437440};
    438441}}}
    439   **`kernel/hcpu.S`**
     442  **`kernel/hcpua.S`**
    440443{{{#!xml
    441444 34 ksyscall:
     
    525528    $(OD) -D $@ > $@.s
    526529
    527 obj/hcpu.o : hcpu.S hcpu.h
     530obj/hcpua.o : hcpua.S hcpu.h
    528531    $(CC) -o $@ $(CFLAGS) $<
    529532    $(OD) -D $@ > $@.s
     
    533536- La `cible` finale est : `kernel.x`
    534537- Les `cibles` intermédiaires sont : `kernel.ld`, `obj/hcpu.o`, `obj/kinit.o`, `obj/klibc.o` et `obj/harch.o`.
    535 - La `source` est : `hcpu.S`
     538- La `source` est : `hcpua.S`
    536539- Les variables automatiques servent à extraire des noms dans la définition de la dépendance (`cible : dépendances`)
    537540  - dans la première règle :
     
    540543  - dans la seconde règle :
    541544    - `$@` = `cible` = `obj/hcpu.o`
    542     - `$<` = la première des dépendances = `hcpu.S`
     545    - `$<` = la première des dépendances = `hcpua.S`
    543546'''''''''''''''
    544547}}}
     
    632635├── harch.c         : code dépendant de l'architecture du SoC
    633636├── hcpu.h          : prototype de la fonction clock()
    634 ├── hcpu.S          : code dépendant du cpu matériel en assembleur
     637├── hcpua.S         : code dépendant du cpu matériel en assembleur
    635638├── kernel.ld       : ldscript décrivant l'espace d'adressage pour l'éditeur de lien
    636639├── klibc.h         : API de la klibc
     
    648651{{{#!make
    649652kernel.x : kernel.ld obj/hcpu.o obj/kinit.o obj/klibc.o obj/harch.o
    650 obj/hcpu.o : hcpu.S
     653obj/hcpua.o : hcpua.S
    651654obj/kinit.o : kinit.c klibc.h
    652655obj/klibc.o : klibc.c klibc.h harch.h
     
    659662{{{#!protected ------------------------------------------------------------------------------------
    660663'''''''''''''''
    661 - Ils sont dans le fichier `hcpu.S`
     664- Ils sont dans le fichier `hcpua.S`
    662665'''''''''''''''
    663666}}}
     
    667670
    668671
    669 * Le numéro du processeur est dans les 12 bits de poids faible du registre $15 (`c0_cpuid`) du coprocesseur système (à côté des registres `c0_epc`, `c0_sr`, etc.). Ajoutez la fonction `int cpuid(void)` qui lit le registre `c0_cpuid` et qui rend un entier contenant juste les 12 bits de poids faible.\\Vous pouvez vous inspirez fortement de la fonction `int clock(void)`. Comme il n'y a qu'un seul processeur dans cette architecture, `cpuid` rend toujours `0`.\\Ecrivez un programme de test (vous devrez modifier les fichiers `hcpu.h`, `hcpu.S` et `kinit.c`)
     672* Le numéro du processeur est dans les 12 bits de poids faible du registre $15 (`c0_cpuid`) du coprocesseur système (à côté des registres `c0_epc`, `c0_sr`, etc.). Ajoutez la fonction `int cpuid(void)` qui lit le registre `c0_cpuid` et qui rend un entier contenant juste les 12 bits de poids faible.\\Vous pouvez vous inspirez fortement de la fonction `int clock(void)`. Comme il n'y a qu'un seul processeur dans cette architecture, `cpuid` rend toujours `0`.\\Ecrivez un programme de test (vous devrez modifier les fichiers `hcpu.h`, `hcpua.S` et `kinit.c`)
    670673
    671674{{{#!protected
    672 **hcpu.S**
     675**hcpua.S**
    673676{{{#!asm
    674677.globl cpuid
     
    710713│   ├── harch.c     : code dépendant de l'architecture du SoC
    711714│   ├── hcpu.h      : prototype de la fonction clock()
    712 │   ├── hcpu.S      : code dépendant du cpu matériel en assembleur
     715│   ├── hcpua.S     : code dépendant du cpu matériel en assembleur
    713716│   ├── klibc.h     : API de la klibc
    714717│   ├── klibc.c     : fonctions standards utilisées par les modules du noyau
     
    788791│   ├── harch.c     : code dépendant de l'architecture du SoC
    789792│   ├── hcpu.h      : prototype de la fonction clock()
    790 │   ├── hcpu.S      : code dépendant du cpu matériel en assembleur
     793│   ├── hcpua.S     : code dépendant du cpu matériel en assembleur
     794│   ├── hcpuc.c     : code dépendant du cpu matériel en c
    791795│   ├── klibc.h     : API de la klibc
    792796│   ├── klibc.c     : fonctions standards utilisées par les modules du noyau
     
    822826{{{#!protected ------------------------------------------------------------------------------------
    823827'''''''''''''''
    824 - Il est dans le fichier `kernel/hcpu.S`.
     828- Il est dans le fichier `kernel/hcpua.S`.
    825829'''''''''''''''
    826830}}}
     
    830834
    831835
    832 - Vous allez ajouter un appel système nommé `SYSCALL_CPUID` qui rend le numéro du processeur. Nous allons lui attribuer le numéro 4 (notez que ces numéros de services n'ont rien à voir avec les numéros utilisés pour le simulateur MARS). Pour ajouter un appel système, vous devez modifier les fichiers : `common/syscalls.h`, `kernel/ksyscall.c`, `kernel/hcpu.S` et `kernel/hcpu.h`.cpuid(void)`.
     836- Vous allez ajouter un appel système nommé `SYSCALL_CPUID` qui rend le numéro du processeur. Nous allons lui attribuer le numéro 4 (notez que ces numéros de services n'ont rien à voir avec les numéros utilisés pour le simulateur MARS). Pour ajouter un appel système, vous devez modifier les fichiers : `common/syscalls.h`, `kernel/ksyscall.c`, `kernel/hcpua.S` et `kernel/hcpu.h`.cpuid(void)`.
    833837
    834838
     
    863867│   ├── harch.c     : code dépendant de l'architecture du SoC
    864868│   ├── hcpu.h      : prototype de la fonction clock()
    865 │   ├── hcpu.S      : code dépendant du cpu matériel en assembleur
     869│   ├── hcpua.S     : code dépendant du cpu matériel en assembleur
     870│   ├── hcpuc.c     : code dépendant du cpu matériel en c
    866871│   ├── klibc.h     : API de la klibc
    867872│   ├── klibc.c     : fonctions standards utilisées par les modules du noyau