Vous êtes ici : Logram >> Les tutoriels >> Comprendre Stage 2

Comprendre Stage 2

Ca marche !
avatar
Auteur : steckdenis
Difficulté : 5/10
Date : le 14/09/2008 à 19:31

Commenter ce tutoriel

Le tutoriel que vous vous apprêtez à lire a été écrit par un bénévole, qui a mis à disposition gratuitement son savoir. Malgré le passage à la validation de ce tutoriel, nous ne pouvons garantir la véricité des informations contenues dedans. Merci de garder ceci à l'esprit pendant votre lecture
Bonjour,

Vous avez suivi le tutoriel qui vous explique comment créer un secteur de boot, mais vous désirez aller plus loin, suivez le guide !

Dans ce tutoriel, vous apprendrez comment marche le Stage2 de Logram. Vous verrez qu'il a une structure assez simple, mais beaucoup plus complexe que Stage1. Bonne chance :)

Table des matières

  1. Ce que devra faire Stage2
  2. Etape 1 : Initialisation de la mémoire
  3. Etape 2: Charger Logram\sys64\Kernel.ext
  4. Ouvrir un chemin d'accès

Ce que devra faire Stage2

Stage2 ne devra pas se contenter de charger quelque chose, il devra d'abord initialiser la mémoire. Ce n'est pas le plus difficile, il faut juste être attentif.

Là où il y aura des problèmes, c'est pour charger en mémoire Logram\sys64\kernel.ext. Ce fichier sera sur une partition FSL, et il faudra donc savoir lire le FSL. Heureusement, FSL est un système de fichier simple : il ne m'a fallut que 45 minutes pour coder la fonction de lecture, fonction même qui servira dans le noyau de Logram.

Dans la suite de ce tutoriel, ce sera Stage2 de Logram qui sera décrit. Si votre OS est différent (et j'en suis sûr), vous ne saurez que difficilement suivre le tutoriel. Si vous voulez un OS qui utilise un format reconnu par Linux (ext2/3, reiserfs, mais aussi NTFS et FAT), je vous conseille d'utiliser GRUB, qui permet de charger le noyau directement pour vous ;)

Etape 1 : Initialisation de la mémoire

Logram sera en mode Long. Pour utiliser le mode Long, il nous faut absolument une structure de pagination. Créer cette structure de pagination à la main serait une tâche colossale, et en plus elle fait 2Mo, et charger 2Mo sur le disque prendrait trop de temps.

Qu'est-ce qu'une structure de pagination ?

Une structure de pagination est une structure qui permet de traduire une adresse en une autre. L'adresse traduite s'appelle Adresse virtuelle, l'adresse résultante s'appelle Adresse physique. Une structure de pagination est pratique dans le sens qu'une application peut demander de charger par exemple un fichier MaVideo.avi de 130Mo. Cette application va ensuite le lire, sans problème, du début à la fin, alors que les données de ce fichier peuvent en fait se trouver sur le disque, dans la mémoire, ou les deux en même temps. La pagination simplifie donc énormément les applications.


A quoi ressemble une structure de pagination ?

Une structure de pagination est un bloc de mémoire comprenant une multitude d'enregistrements de 8 octets. Cette structure est une structure à plusieurs niveaux :


Si nous devions utiliser toute cette structure, c'est à dire 512 PLM4E, 512 PDPE et 512 PDE, nous aurions besoin d'une structure de pagination de 128Mo ! Heureusement, nous n'allons utiliser qu'une seule PLM4E et une seule PDPE. La structure fait donc 2Mo, ce qui est justement la taille d'une page. La gestion de la mémoire n'en sera que simplifiée.

Comment construire cette structure ?

Pour construire cette structure, nous ne devons que créer une PLM4E, une PDPE et 512 PDE (boucle for() en C). Nous devrons alors simplement mapper quelques pages. Voici le code qui se charge de créer la structure :

Code : c
  1. memset((void *)(0x100000-0x10000), 0, 2*1024*1024);     // On efface préalablement la mémoire
  2.  
  3. crtPLM4EPDPE(7, 100, 0, monPLM4E);                     // On crée l'unique PLM4E qui pointe vers la PDPE
  4. for (i=0; i<511; i++) {
  5.         crtPLM4EPDPE(7, 101+i, 0,(int64 *)((0x100000-0x10000)+(i*8)));  // On crée la PDPE qui contient 512 PDE
  6. }      
  7.  
  8. crtPDE(391 , 2, 0, (int64 *)(0x101008-0x10000));                // On mappe les premières pages de la mémoire en
  9. crtPDE(391 , 4, 0, (int64 *)(0x101010-0x10000));                // Sautant la première, pour éviter les pointeurs NULL       
  10. crtPDE(391 , 6, 0, (int64 *)(0x101018-0x10000));                //
  11. crtPDE(391 , 8, 0, (int64 *)(0x101020-0x10000));                //

Voilà, la mémoire est initialisée. Il ne faut plus qu'y charger kernel.ext

Etape 2: Charger Logram\sys64\Kernel.ext

Voici le plus dur. Le chargement en lui-même est simple, mais il faut d'abord une bonne explications du format FSL. Ensuite, il faudra trouver comment lire des secteurs sur le disque dur sans passer par le BIOS.

Qu'est-ce que le FSL ?

Le FSL est le système de fichiers utilisé par Logram. Il est simple, rapide et puissant. Ce qui fait que le FSL est simple, c'est qu'il n'utilise pas de MFT, ni de FAT. Tout le disque est géré grâce à des blocs. Ces blocs font une certaine quantité de secteurs. Chaque bloc commence par un secteur qui contient des informations sur ce bloc, comme par exemple l'adresse du bloc suivant.

Lecture d'un fichier


Vous l'aurez compris, lire un fichier est simple, il suffit de commencer par le premier bloc et de continuer avec les blocs suivant, de proche en proche, d'adresse en adresse, jusqu'à ce que le fichier soit lu jusqu'au bout.

Mine de rien, nous avons déjà notre première fonction : fsl_read. La voici :


Code : c
  1. void fsl_read(int baseblock, void *buf){
  2.         /*
  3.                 Pour lire un fichier:
  4.                  - Pour chaque bloc du fichier (commencer à baseblock)
  5.                          - Lire l'adresse du bloc suivant
  6.                          - Pour chaque secteur
  7.                                  - le lire et le placer dans le buffer
  8.                                  - si ce secteur était le dernier quitter
  9.                          - Fin boucle
  10.                  - Fin boucle
  11.         */
  12.         volatile int numblk, suivblk, i;
  13.         volatile FSL_BLOCK *blk;
  14.         volatile void *mbuf;
  15.  
  16.         numblk = baseblock;
  17.  
  18.         while (1) {
  19.                 //lire l'en-tête du bloc (adresse d'un secteur dans un bloc = 2048+(numblk*SC)+numsect.
  20.                 readsector((void *) &amp;bufsect, 2048+(numblk*disk.blocsize)+0);
  21.                 blk = (FSL_BLOCK *) &amp;bufsect
  22.                 suivblk = blk->follow;
  23.  
  24.                 //pour chaque secteur
  25.                 for (i=1; i<32; i++) { //for (i=1; i<disk.blocsize; i++) {
  26.                         //printd("boucle for");
  27.                         //lire le secteur et le placer dans le buffer
  28.                         readsector((void *) mbuf,  2048+(numblk*((int) disk.blocsize))+i);
  29.                 }
  30.                 //passer au bloc suivant, s'il existe
  31.                 numblk = suivblk;
  32.                 //Si il n'y a pas de boc suivant, quitter
  33.                 if (numblk==0) return;
  34.         }
  35. }


Maintenant, il ne nous reste plus qu'à trouver le premier bloc de ce fichier. C'est simple, c'est noté dans d'autres fichiers, les répertoires.

Lire le contenu d'un répertoire


Un répertoire est une suite de bloc, comme un fichier, à la différence qu'à la place de données, un répertoire contient une suite de secteurs, qui décrivent les fichiers contenus dedans. Pour trouver le premier bloc d'un fichier, il nous suffit de lire son répertoire parent, et de trouver le bon fichier dedans. Voici donc la fonction dérivée de fsl_read qui fait cela (attention, elle est plus longue) :


Code : c
  1. int fsl_open(int baseblock, const unsigned short *filename){
  2.         /*
  3.                 Pour ouvrir un fichier:
  4.                  - Pour chaque bloc du dossier parent
  5.                          - Lire l'id de bloc suivant
  6.                          - Pour chaque secteur
  7.                                  - Lire le nom du fichier
  8.                                  - Le comparer à filename
  9.                                  - si c'est bon, retourner le bloc contenant le fichier
  10.                          - Fin boucle
  11.                  - Fin boucle
  12.         */
  13.         volatile int numblk, suivblk, k;
  14.         volatile FSL_BLOCK *blk;
  15.         FSL_FILE *file;
  16.         unsigned char foundname[228];
  17.         numblk = baseblock;
  18.         //printd("1");
  19.         while (1) {
  20.                 //lire l'en-tête du bloc (adresse d'un secteur dans un bloc = 2048+(numblk*SC)+numsect.
  21.                 readsector((void *) &amp;bufsect, 2048+(numblk*disk.blocsize)+0);
  22.                 blk = (FSL_BLOCK *) &amp;bufsect
  23.                 suivblk = blk->follow;
  24.  
  25.                 //pour chaque secteur
  26.                 //printd("2");
  27.                 for (k=1; k<disk.blocsize; k++) {
  28.                     //printd("3");
  29.                         //lire le nom de fichier
  30.                         readsector((void *) &amp;bufsect,2048+ (numblk*disk.blocsize)+k);
  31.                         file = (FSL_FILE *) &amp;bufsect
  32.                         if (strcmp(file->filename, filename)) {
  33.                             print("File found");
  34.                                 //on a trouvé le bon fichier !
  35.                                 return file->startblock;        //remarquez que bufsect contient encore l'enregistrement, ça pourrait servir.
  36.                         }
  37.                         else if (file->filename[0] != 0);
  38.                         {
  39.                            wchartochar(foundname,file->filename);
  40.                            print(foundname);
  41.                         }
  42.                         }
  43.                 //passer au bloc suivant, s'il existe
  44.                 numblk = suivblk;
  45.                 if (numblk == 0) {
  46.                         //pas de bloc suivant, on est à la fin du répertoire
  47.                         print("File cannot be found :");
  48.                         print((char *) filename);
  49.                         asm("hlt");
  50.                         for(;;);        //boucle infinie.
  51.                 }
  52.         }
  53. }


Maintenant que nous savons lire des répertoire et des fichiers, comment l'utiliser ?

Ouvrir un chemin d'accès

Un chemin d'accès comme Logram\sys64\kernel.ext peut être décomposé en différentes parties :


Nous allons donc ouvrir Logram dans le répertoire principal, puis ouvrir sys64 dans Logram, et enfin kernel.ext dans sys64. C'est réalisé avec ce tout petit code qui utilise nos fonctions précédentes :

Code : c
  1. blk = fsl_open(0, L"Logram");   // des L car ça doit être en unicode.
  2. blk = fsl_open(blk, L"sys64");
  3. blk = fsl_open(blk, L"kernel.ext");
  4. fsl_read(blk, (void *)0x800000-0x10000);

Voilà, notre fichier est ouvert.

Remarque : la lecture sur un disque IDE



Vous avez pu remarquer que nous avons utilisé la fonction readsector, pour lire un secteur sur le disque principal de l'ordinateur. Cette fonction a été récupérée de KOS, un autre OS libre, et adaptée à Logram. Pas besoin de commentaires, elle est simple, courte, et tout se trouve dans les spécifications ATAPI/7. (voir wikipédia).

Code : c
  1. void readsector(void* buf, int block)
  2. {
  3.   volatile unsigned char cyl_lo, cyl_hi, sect, head, status;
  4.   volatile int devselect, i, timeout;
  5.   volatile int16* buffer;
  6.  
  7.   devselect = ATA_D_MASTER;
  8.  
  9.   /* Ask the controller to NOT send interrupts to acknowledge
  10.      commands */
  11.   outb(0x1F0 + ATA_DEVICE_CONTROL, ATA_A_nIEN | ATA_A_4BIT);
  12.   udelay(1);
  13.  
  14.   sect   = (block &amp; 0xff);
  15.   cyl_lo = (block >> 8) &amp; 0xff;
  16.   cyl_hi = (block >> 16) &amp; 0xff;
  17.   head   = ((block >> 24) &amp; 0x7) | 0x40;
  18.  
  19.  
  20.   /* Select device */
  21.   outb(0x1F0 + ATA_DRIVE, ATA_D_IBM | devselect);
  22.   udelay(100);
  23.  
  24.   /* Write to registers */
  25.   outb(0x1F0 + ATA_DEVICE_CONTROL, ATA_A_4BIT);
  26.   outb(0x1F0 + ATA_ERROR, 1);
  27.   outb(0x1F0 + ATA_PRECOMP, 0);
  28.   outb(0x1F0 + ATA_SECTOR_COUNT, 1);
  29.   outb(0x1F0 + ATA_SECTOR_NUMBER, sect);
  30.   outb(0x1F0 + ATA_CYL_LSB, cyl_lo);
  31.   outb(0x1F0 + ATA_CYL_MSB, cyl_hi);
  32.   outb(0x1F0 + ATA_DRIVE, (ATA_D_IBM | devselect | head));
  33.  
  34.   /* Send the command, either read or write */
  35.   outb(0x1F0 + ATA_CMD, ATA_C_READ);
  36.  
  37.   /* Wait for command completion (wait while busy bit is set) */
  38.   for(timeout = 0; timeout < 30000; timeout++)
  39.     {
  40.       status = inb(0x1F0 + ATA_STATUS);
  41.       if(!(status &amp; ATA_S_BSY))
  42.         break;
  43.  
  44.       udelay(1);
  45.     }
  46.  
  47.     /* copy data from the controller internal buffer to the buffer
  48.        provided by the user */
  49.     buffer = (int16 *) buf;
  50.     for (i = 0 ; i < 256 ; i++) buffer [i] = inw (0x1F0 + ATA_DATA);
  51. }

Ca marche !
avatar

Retour en haut

Auteur : steckdenis
Difficulté : 5/10
Date : le 14/09/2008 à 19:31

Commenter ce tutoriel