Projet système PC : 2019 - GRAVALLON Guillaume, MOISSIARD Anael : Différence entre versions

De Ensiwiki
Aller à : navigation, rechercher
(Phase 7 : Implémentation d'un interprète de commandes)
Ligne 1 : Ligne 1 :
{{Projet Étudiant
 
|cadre=Projet système
 
|titre=MorphOS
 
|equipe=[mailto:guillaume.gravallon@grenoble-inp.org Guillaume Gravallon], [mailto:anael.moissiard@grenoble-inp.org Anaël Moissiard]
 
|encadrants=[mailto:yves.denneulin@univ-grenoble-alpes.fr Yves Denneulin ], [mailto:Gregory.Mounie@imag.fr Gregory Mounie], [mailto:Patrick.Reignier@imag.fr Patrick Reignier ]
 
|image=[[Fichier:morphos_image.png|300px]]
 
}}
 
 
 
==Présentation==
 
Voici la page de présentation du Projet Système réalisé dans le cadre de notre 2ème année en apprentissage à l'ENSIMAG.
 
 
L'objectif de ce projet est la réalisation, à partir de presque rien, d'un noyau de système d'exploitation sur une architecture Intel x86.
 
 
 
 
<center>[[Fichier:morphos_image.png]]</center>
 
 
 
 
Le projet est basé autour des documents suivants, qui ont constitué les ressources principales sur lesquelles nous nous sommes appuyé lors de ces 4 semaines de développement:
 
* [https://ensiwiki.ensimag.fr/index.php?title=Projet_syst%C3%A8me Présentation]
 
* [https://ensiwiki.ensimag.fr/index.php?title=Projet_syst%C3%A8me_:_sp%C3%A9cification Spécifications]
 
* [https://ensiwiki.ensimag.fr/index.php?title=Projet_syst%C3%A8me_:_roadmap Roadmap]
 
* [https://ensiwiki.ensimag.fr/index.php?title=Projet_syst%C3%A8me_:_Aspects_techniques Détails techniques]
 
* [https://ensiwiki.ensimag.fr/index.php?title=Projet_syst%C3%A8me_:_environnement Environnement]
 
 
==Equipe==
 
 
:*[mailto:guillaume.gravallon@grenoble-inp.org Guillaume GRAVALLON] (filière [http://ensimag.grenoble-inp.fr/fr/formation/ingenieur-par-apprentissage#page-presentation apprentissage])
 
:*[mailto:anael.moissiard@grenoble-inp.org Anaël MOISSIARD] (filière [http://ensimag.grenoble-inp.fr/fr/formation/ingenieur-par-apprentissage#page-presentation apprentissage])
 
 
==Organisation==
 
 
Nous avons pu réaliser les 7 phases contenues dans le projet dans le temps qui était imparti.
 
Nous avons décidé de programmer ensemble tout du long afin d'avoir tous les deux une vision globale et une compréhension de toutes les étapes du projet. De plus, certaines étapes étant assez ardues, réfléchir dessus à deux nous a permis d'avancer plus facilement et de corriger immédiatement les erreurs commises par l'un ou l'autre d'entre nous.
 
 
Nous avons utilisé Git en tant que logiciel de gestion de version décentralisé, et notre dépôt distant a été hébergé sur le gitlab fourni par l'Ensimag.
 
 
Nous n'avons pas pris le parti de mettre en place en amont du projet un planning prévisionnel, à la fois car il nous était assez difficile d'estimer la durée que prendrait la plupart des phases sans nous être plongé réellement dans les problèmes qu'elles posaient, mais également car les développements à effectuer semblaient assez séquentiels et que nous ne voyions pas forcément l'utilité de poser l'ensemble des étapes sur un planning. Comme l'a rappelé l'équipe enseignante au début du projet, l'objectif au vu du peu de temps imparti est plus de rentrer dans les développements concrets et d'essayer de faire marcher les choses afin de comprendre leur fonctionnement que de passer du temps à concevoir. Nous sommes donc rentré immédiatement dans le vif du sujet. Le fait de ne pas mettre en place de planning prévisionnel ne nous a apparemment pas particulièrement desservi étant donné que nous avons pu achever toutes les phases.
 
 
Il est à noter que nous avons beaucoup échangé avec la plupart des autres binômes, à la fois pour confronter nos compréhensions du sujet, pour réfléchir sur les aspects les plus complexes du projet à plus de deux, ou encore pour demander ou apporter de l'aide. Cela a été positif pour tout le monde, a créé une bonne dynamique et nous avons été plusieurs groupes à pouvoir grâce à cela avancer à un rythme suffisants pour pouvoir venir à bout des réalisations attendues. Si vous êtes amenés à réaliser ce projet, n'oubliez pas qu'ici comme dans la plupart des cas le partage profite à tous !
 
 
==Phases de développement==
 
 
=== Phase 1 : prise en main de l'environnement ===
 
{{Avancement | 100}}
 
 
Cette phase qui ne prend pas plus de quelques heures permet de prendre en main l'environnement et de réaliser les premiers développements en se basant sur ce qui a été réalisé en cours de [https://chamilo.grenoble-inp.fr/courses/ENSIMAG3MM1LDB/document/ldb.html Logiciel de Base] en 1ère année de la filière apprentissage.
 
 
L'objectif est de mettre en place l'affichage de caractères à l'écran. Il est très important de comprendre dès le début qu'on se situe dans un environnement complètement différent de ce dont on a l'habitude lorsque l'on développe ce noyau, et que de nombreuses choses que l'on peut croire acquises ne le sont pas. '''L'absence de libc''' signifie par exemple que toutes les fonctionnalités qu'on a l'habitude de connaître (affichage, gestion de mémoire dynamique, manipulation de chaînes de caractères...) sont soit incluses dans le squelette fourni par les enseignants, soit '''à développer soit même !''' C'est le cas par exemple de la fonction ''printf'', et c'est pourquoi on doit lors de cette première phase développer certaines fonctions nécessaire au bon fonctionnement de ''printf''.
 
 
On va donc développer les fonctions permettant de gérer certains caractères de bases et de les placer dans la zone mémoire correspondant à l'écran afin que ceux ci s'affichent. Cette phase n'est pas à négliger car l'utilisation d'un ''printf'' fonctionnel sera utile dans la suite du projet (même si l'affichage ne remplace pas l'utilisation de GDB lorsqu'il s'agit de débuguer, évidemment).
 
 
=== Phase 2 : Création et lancement de processus de niveau noyau ===
 
{{Avancement | 100}}
 
 
Cette phase vise à mettre en place la structure des processus qui vont être lancés par le kernel ou par l'utilisateur lors de toute la suite du projet. Pour ce faire, il est important de se reporter à la spécification qui permet de comprendre quelle sont (pour l'instant) les caractéristiques d'un processus, et notamment les états dans lesquels il peut se situer.
 
 
Le changement de contexte entre processus est une étape clé, réalisée par un code assembleur fourni dans les sources du projet de LBD de 1ère année, mais qui nécessite d'avoir créé une bonne structure de donnée pour les processus. On peut dans un premier temps faire un changement de contexte d'un processus A vers un processus B en s'assurant que tout se passe correctement.
 
 
Par la suite, on peut mettre en place la gestion des interruptions d'horloge afin de réaliser le changement de contexte de manière automatique à une certaine fréquence. Une nouvelle fois, le code développé en 1ère année peut être une base pour implanter cette gestion de l'interruption 33. Une fois arrivé à ce stade, on peut faire des aller-retours entre 2 processus A et B à une fréquence déterminée.
 
 
Lors de cette phase, on commence à développer les primitives liées au processus. '''Il est très important de suivre à la lettre les spécifications des primitives qui sont à développer tout au long du projet.''' Certains détails qui peuvent avoir une importance cruciale peuvent résider dans des mots à première vue anodins, c'est pourquoi il faut s'assurer avoir bien compris ce qu'il faut développer avant de le faire, sans quoi on a de grands risques de devoir apporter des corrections par la suite.
 
 
=== Phase 3 : Ordonnancement, création dynamique et terminaison de processus de niveau noyau ===
 
{{Avancement | 100}}
 
 
Cette troisième phase concerne la répartition du temps processeur entre les processus, la gestion du cycle de vie des processus ainsi que la filiation entre eux.
 
 
Afin de mettre en place l'ordonnancement, il convient d'utiliser une liste chaînée permettant de trier les processus selon leur priorité. Le squelette de liste chaînée fourni, bien que contraignant par moment, permet d'implanter l'ordonnancement correctement.
 
 
'''Conseil:''' vous pourriez être tenté à certains moments dans le projet d'utiliser comme certains groupes l'ont fait par le passé une liste chaînée externe moins contraignante (permettant notamment de supprimer à la volée des éléments lors d'un parcours) que celle fournie dans le squelette, notamment lorsque la priorité ne vous semble pas nécessaire. Nous vous recommandons néanmoins d'utiliser toujours la liste fournie, car elle permet de coller exactement aux spécifications des primitives et de valider le comportement attendu lors des tests.
 
 
Une fois toutes les primitives liées au processus développées correctement, on peut lancer et arrêter des processus, switcher entre eux, etc.
 
 
L'implantation de la filiation (avec une liste chaînée de processus fils pour chaque processus par exemple) permet également de faire en sorte qu'un processus père attende la terminaison de ses fils
 
 
Une fois cette phase achevée. On peut commencer à utiliser les tests fournis par l'équipe enseignante. Les tests n'existant à la base qu'en mode user, il convient de faire quelques modifications pour pouvoir les lancer en mode kernel (mais il ne fait aucun doute qu'un enseignant prendra le temps qu'il faut pour vous expliquer comment adapter ces tests).
 
On peut à la fin de cette phase lancer les 8 premiers tests afin de vérifier que les primitives implantées jusqu'à présent fonctionnent correctement.
 
Un test est validé si les trois conditions suivantes sont remplies:
 
* Les numéros à afficher (le cas échéant) s'affichent dans l'ordre
 
* Un pont s'affiche, signifiant que le dernier numéro a été affiché et que le test a été exécuté jusqu'au bout.
 
 
=== Phase 4 : Gestion des communications et synchronisation de processus de niveau noyau ===
 
{{Avancement | 100}}
 
 
Maintenant que nous avons des processus fonctionnels, cette partie vise à leur permettre de communiquer, et de s'endormir pour d'autres raisons que l'attente de terminaison d'un fils
 
Cette phase consiste en la mise en place de files de messages que les processus vont venir lire et écrire, restant en attente quand c'est nécessaire.
 
 
La composante majeure de cette partie est la mise en place de files de messages. Ces files vont permettre aux processus de communiquer entre eux en s'envoyant des entiers. Pour ce faire, on doit créer les primitives permettant de créer et supprimer des files, et celles permettant aux processus d'écrire ou de lire des messages dans les files.
 
 
Un aspect critique à gérer est l'endormissement des processus lorsqu'il y a en a un qui souhaite lire alors qu'il n'y a rien à lire dans la file choisie, ou inversement un qui souhaite écrire alors que la file dans laquelle il souhaite écrire est pleine. Il convient alors d'endormir le processus en question, et de le réveiller lorsque la lecture/l'écriture sera possible.
 
 
'''Important :''' Un détail à ne pas négliger est qu'un processus qui s'endort ne va '''jamais terminer le travail lui même'''. En effet, on pourrait penser qu'un processus qui n'a pas la place d'écrire et qui s'endort a juste à être réveillé lorsqu'un place dans la file aura été libérée. Le problème réside dans le fait que lorsque la place aura été libérée, si un processus plus prioritaire est choisi par l'ordonnanceur et que ce processus écrit dans la file, le processus qui attendait aura été réveillé pour rien et va se rendormir son tour venu. A cause de ce fonctionnement, on pourrait se retrouver à ne jamais donner la possibilité au processus d'écrire. Il convient donc pour éviter ce problème de faire en sorte que le processus qui vient lire récupère et mette lui même dans la file le message du processus qui attendait d'écrire, pour être sûr que le processus en attente d'écriture ne se retrouve pas en situation de famine. Il en va de même dans l'autre sens lorsque la file est vide.
 
 
On implémente également dans cette phase une primitive permettant d'endormir le processus jusqu'à une heure choisie, ultérieure à la durée écoulée depuis le lancement du système.
 
 
A la fin de cette phase, les tests 1 à 17 et 20 peuvent être exécutés côté kernel. '''Leur validation est vivement conseillée avant le passage à la phase 5.''' En effet, vous n'aurez pas envie de devoir à nouveau débugguer les primitives liées aux processus ou aux messages lorsque vous aurez à faire des appels systèmes en phase 5, cette dernière étant déjà bien assez complexe.
 
Les deux derniers tests étant basés sur la séparation entre l'espace mémoire user et kernel, et sur l'utilisation des interruptions clavier, il ne pourront être validés que lorsque le mode user sera implanté.
 
 
 
=== Phase 5 : Séparation des espaces mémoire noyau et utilisateur : gestion de processus utilisateur ===
 
=== Phase 5 : Séparation des espaces mémoire noyau et utilisateur : gestion de processus utilisateur ===
 
{{Avancement | 100}}
 
{{Avancement | 100}}
  
Cette phase est la plus complexe du projet. Elle nécessite peu de code, mais il faut absolument avoir bien compris ce que l'on veut faire et se documenter.
+
Cette phase est la plus complexe du projet. Elle nécessite peu de code, mais il faut absolument avoir bien compris ce que l'on veut faire et se documenter. Certaines pages de présentation des projets effectués les années précédentes ainsi que certaines ressources trouvables sur internet semblent nécessaires pour compléter les aspects de la spécification qui est en soit assez peu explicite sur certains points très techniques.
 
+
=== Phase 6 : Gestion du clavier et implémentation d'un pilote de console ===
+
{{Avancement | 100}}
+
 
+
Cette phase assez simple par rapport à la précédente vise à gérer les interruptions 33 générées par le clavier afin de pouvoir afficher à l'écran les caractères tapés sur le clavier.
+
 
+
Le mécanismes de gestion des interruptions est similaire à ce qui était fait pour gérer les interruptions de l'horloge, il convient simplement cette fois s'utiliser l'automate fourni par le squelette pour transformer le scancode propre à chaque touche (récupérable sur le port 0x60 lors d'une interruption) en un caractère qu'on va afficher.
+
 
+
Le but est à la fois d'afficher en echo ce qui est tapé par l'utilisateur, mais également de mettre les caractères écrits dans un tampon afin qu'ils puissent être lus ultérieurement par un processus qui aurait besoin de ces caractères, comme le shell implanté en phase 7.
+
 
+
Une fois cette phase terminée, le dernier test qui manquait, le 19, peut être validé, ce qui marque la fin de la phase de validation des tests fournis par les enseignants. Les tests que vous pourrez réaliser en phase 7 ne dépendront que de ce que vous jugerez pertinent pour évaluer les aspects fonctionnels de votre shell.
+
 
+
=== Phase 7 : Implémentation d'un interprète de commandes ===
+
{{Avancement | 100}}
+
 
+
Cette phase vise à mettre en place un interpréteur de commande minimaliste.
+
 
+
Pour ce faire, on attend constamment que le buffer du terminal soit rempli par l'utilisateur et on va comparer ce qu'on a lu avec certains mots clés qui vont nous permettre de réaliser certains actions. Les actions en question vont nécessiter de faire des appels systèmes afin de récupérer des informations sur le système (par exemple la liste des processus pour le ''ps'') ou d'effectuer des actions à haut niveau de privilège (comme tuer un processus ou relancer le système). En effet, le shell étant un programme utilisateur, il n'a pas les droits pour effectuer la plupart des actions et doit donc déléguer l’exécution des primitives au kernel.
+
 
+
=== Tests Kernel ===
+
{{Avancement | 100}}
+
 
+
=== Tests User ===
+
{{Avancement | 100}}
+
 
+
==Journal de bord==
+
 
+
===Semaine 1===
+
 
+
====05 Juin 2019====
+
'''Phase 1 :'''
+
* Découverte et prise en main de l'environnement
+
* Récupération et modification du code utilisé en cours de Logiciel de Base de 1ère année pour permettre l'affichage de caractères à l'écran
+
'''Phase 2 :'''
+
* Création de la structure des processus
+
* Début de travail sur le lancement de processus et le changement de contexte entre processus
+
 
+
====06 Juin 2019====
+
'''Phase 2 :'''
+
* Correction de la structure des processus
+
* Lancement des processus
+
* Changement de contexte entre processus
+
 
+
====07 Juin 2019====
+
'''Phase 2 :'''
+
* Gestion des interruptions de l'horloge afin de procéder au changement de contexte entre processus à une fréquence déterminée
+
'''Phase 3 :'''
+
* Mise en place de l'ordonnancement grâce aux macros de gestion de liste chaînée pré-existantes
+
* Adaptation du changement de contexte pour la compatibilité avec la liste chaînée
+
 
+
===Semaine 2===
+
 
+
====11 Juin 2019====
+
'''Phases précédentes :'''
+
* Correction d'erreurs concernant le défilement à l'écran et le changement de processus
+
'''Phase 3 :'''
+
* Création dynamique de processus
+
* Réflexion sur la filiation entre processus et la terminaison de processus
+
 
+
====12 Juin 2019====
+
'''Phase 3 :'''
+
* Travail sur les primitives de gestion des processus:
+
** Terminaison du processus actif
+
** Terminaison d'un autre processus
+
** Obtention de la priorité d'un processus
+
** Obtention du PID du processus appelant
+
* Ajout de la gestion dynamique des identifiants de processus, avec possibilité pour un nouveau processus de récupérer l'identifiant d'un processus préalablement détruit
+
* Gestion de la fin de processus lorsque la fonction se termine
+
* Amélioration de la fonction d'ordonnancement
+
 
+
====13 Juin 2019====
+
'''Phase 3 :'''
+
* Finalisation des primitives de processus:
+
** Attente d'un processus fils par son père
+
**''Correction sur les suppression de processus''
+
'''Phase 4 :'''
+
* Endormissement des processus
+
* Ajout d'un processus ''idle'' qui prend la main lorsqu'il n'y a aucun autre processus activable
+
'''Tests Kernel :'''
+
* Correction de bugs pour le passage des tests Kernel 1 à 5
+
 
+
====14 Juin 2019====
+
'''Tests Kernel :'''
+
* Correction de bugs pour le passage des tests Kernel 6 à 9
+
'''Phase 4 :'''
+
* Réflexions sur les files de messages et début de création des structures et primitives associées
+
 
+
===Semaine 3===
+
 
+
====17 Juin 2019====
+
'''Phase 4 :'''
+
* Création des primitives liées aux communications par messages entre processus
+
'''Tests Kernel :'''
+
* Validation des tests 10 à 12
+
 
+
====18 Juin 2019====
+
'''Phase 4 :'''
+
* Rectification des primitives liées au communications de messages
+
'''Tests Kernel :'''
+
* Validation des tests 13 à 16 et 20
+
 
+
====19 Juin 2019====
+
'''Phase 5 :'''
+
* Travail sur l'initialisation de processus utilisateurs
+
* Saut du mode kernel au mode utilisateur
+
* Début de mise en place des interruptions qui permettront d'effectuer les appels systèmes
+
 
+
====20 Juin 2019====
+
'''Tests Kernel :'''
+
* Validation du test 17.
+
'''Note :''' Les tests 17, 18 et 19 ne sont pas destinés à être exécutés en mode Kernel, leur validation est optionnelle ou impossible.
+
 
+
'''Phase 5 :'''
+
* Finalisation du saut du mode kernel au mode user lors du lancement du système
+
* Rectification de la procédure de déclenchement et de la gestion de l'interruption 49
+
* Mise en place de la bibliothèque d'appels système
+
 
+
====21 Juin 2019====
+
'''Phase 5 :'''
+
* Libération de pile user anciennement allouée
+
* Ajout d'appels systèmes sécurisés testant les valeurs des pointeurs passés en paramètres des appels
+
'''Tests User :'''
+
* Validation des tests 1 à 18 et 20
+
  
====22 Juin 2019====
+
L'objectif est ici de mettre en place le mode utilisateur. Cela va permettre de lancer non plus des processus kernel, mais des processus utilisateurs, évoluant dans la zone mémoire dédiée aux processus utilisateurs, et qui vont interagir avec le kernel via des appels système.
'''Phase 6 :'''
+
* Gestion du traitant 33 pour les interruptions claviers
+
  
===Semaine 4===
+
Pour ce faire il faut tout d'abord bien comprendre ce qui se passe lors d'un appel système. Lorsqu'un programme utilisateur fait un appel système, il sauvegarde les registres dans sa pile, fait appel à l'interruption 49 qui va être traitée au niveau du kernel, réalise l'action requise et procède au retour d'interruption qui va permettre de repasser en mode utilisateur.
  
====24 Juin 2019====
+
Lorsque notre programme se lance, on lance un processus ''idle'' qui est un processus kernel. L'objectif est que ce processus lance un autre processus qui lui sera le point d'entrée de tout ce qu'on va vouloir faire en mode utilisateur (dans notre cas, cela va être le processus exécutant la fonction ''user_start''). Vient alors un problème: si on sait comment on voudra passer de user à kernel puis retourner à user grâce aux interruptions, on ne sait pas comment passer simplement du mode kernel au mode user au début. Pour résoudre ce problème on va donc '''simuler un retour d'interruption''' pour arriver en mode user. Pour simuler ce retour d'interruption, il va falloir mettre certaines données à des endroits précis afin de simuler l'état su système tel qu'il serait si on était arrivé en mode kernel non pas parce qu'on vient de lancer le système mais parce qu'on avait déclenché une interruption 49.
'''Phase 6:'''
+
* Mise en place complète de la console avec echo et remplissage d'un buffer en vue d'un futur terminal
+
  
'''Tests User :'''
+
Pour simuler ce retour d'interruption, on va devoir maintenant créer non plus une seule pile kernel mais également une '''seconde pile''' utilisateur, et mettre dans la pile kernel certaines valeurs de sorte que l'instruction assembleur ''iret'' nous fasse sauter de la pile kernel à la pile user, pile user dans laquelle on trouvera l'adresse de la fonction ''user_start'' à exécuter.
* Validation du test 19 (après rectification pour que la signature de cons_read soit conforme à ce qu'on nous demande d'implanter)
+
  
'''Phase 7:'''
+
Le déroulement souhaité est donc être le suivant:
* Mise en place d'un processus shell pouvant interpréter certaines commandes au travers d'appels système
+
* Créer la pile user
 +
* Créer la pile kernel en mettant dedans les informations nécessaires
 +
* Appeler le contexte switch de sorte que la valeur d'esp permette de sauter dans une fonction assembleur ''kernel_to_user''
 +
* Mettre certains registres à une valeur
 +
* Procéder au retour d'interruption qui va dépiler les données de la pile kernel
 +
* Sauter sur la fonction user_start
  
====25 Juin 2019====
+
Une fois arrivé ici, nous sommes en mode user !
'''Phase 7 :'''
+
* Ajout de nouvelles commandes pour le shell
+
  
'''Documentation :'''
+
Il faut alors mettre en place une bibliothèque qui va réaliser les appels systèmes afin d’accéder aux fonctions implantées précédemment dans le kernel. Cette partie est assez triviale comparé à la première partie de la phase, et ne nécessite que de répliquer le code assembleur permettant de faire l'appel correctement.
* Contributions finales à la documentation
+
  
==Difficultés rencontrées==
+
Une fois cette phase terminée, les tests 1 à 18 et 20 peuvent êtres réalisés en mode utilisateur.
* Difficultés pour identifier l'origine de certains bugs, l'utilisation de gdb avec Qemu pour examiner la mémoire n'étant pas forcément triviale pour les non initiés.
+
* La phase 5, et notamment le passage en mode user depuis le mode kernel, était assez complexe et nécessitait une compréhension assez fine des mécanismes du processeur. Développer le code nécessaire à cette étape a sans nul doute été la partie la plus difficile du projet.
+

Version du 25 juin 2019 à 22:06

Phase 5 : Séparation des espaces mémoire noyau et utilisateur : gestion de processus utilisateur

100 %

Cette phase est la plus complexe du projet. Elle nécessite peu de code, mais il faut absolument avoir bien compris ce que l'on veut faire et se documenter. Certaines pages de présentation des projets effectués les années précédentes ainsi que certaines ressources trouvables sur internet semblent nécessaires pour compléter les aspects de la spécification qui est en soit assez peu explicite sur certains points très techniques.

L'objectif est ici de mettre en place le mode utilisateur. Cela va permettre de lancer non plus des processus kernel, mais des processus utilisateurs, évoluant dans la zone mémoire dédiée aux processus utilisateurs, et qui vont interagir avec le kernel via des appels système.

Pour ce faire il faut tout d'abord bien comprendre ce qui se passe lors d'un appel système. Lorsqu'un programme utilisateur fait un appel système, il sauvegarde les registres dans sa pile, fait appel à l'interruption 49 qui va être traitée au niveau du kernel, réalise l'action requise et procède au retour d'interruption qui va permettre de repasser en mode utilisateur.

Lorsque notre programme se lance, on lance un processus idle qui est un processus kernel. L'objectif est que ce processus lance un autre processus qui lui sera le point d'entrée de tout ce qu'on va vouloir faire en mode utilisateur (dans notre cas, cela va être le processus exécutant la fonction user_start). Vient alors un problème: si on sait comment on voudra passer de user à kernel puis retourner à user grâce aux interruptions, on ne sait pas comment passer simplement du mode kernel au mode user au début. Pour résoudre ce problème on va donc simuler un retour d'interruption pour arriver en mode user. Pour simuler ce retour d'interruption, il va falloir mettre certaines données à des endroits précis afin de simuler l'état su système tel qu'il serait si on était arrivé en mode kernel non pas parce qu'on vient de lancer le système mais parce qu'on avait déclenché une interruption 49.

Pour simuler ce retour d'interruption, on va devoir maintenant créer non plus une seule pile kernel mais également une seconde pile utilisateur, et mettre dans la pile kernel certaines valeurs de sorte que l'instruction assembleur iret nous fasse sauter de la pile kernel à la pile user, pile user dans laquelle on trouvera l'adresse de la fonction user_start à exécuter.

Le déroulement souhaité est donc être le suivant:

  • Créer la pile user
  • Créer la pile kernel en mettant dedans les informations nécessaires
  • Appeler le contexte switch de sorte que la valeur d'esp permette de sauter dans une fonction assembleur kernel_to_user
  • Mettre certains registres à une valeur
  • Procéder au retour d'interruption qui va dépiler les données de la pile kernel
  • Sauter sur la fonction user_start

Une fois arrivé ici, nous sommes en mode user !

Il faut alors mettre en place une bibliothèque qui va réaliser les appels systèmes afin d’accéder aux fonctions implantées précédemment dans le kernel. Cette partie est assez triviale comparé à la première partie de la phase, et ne nécessite que de répliquer le code assembleur permettant de faire l'appel correctement.

Une fois cette phase terminée, les tests 1 à 18 et 20 peuvent êtres réalisés en mode utilisateur.