Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Programmation d'OS Assembleur Discussion :

Passage de données


Sujet :

Programmation d'OS Assembleur

  1. #1
    Membre régulier
    Passage de données
    Bonjour à tous

    Je travaille actuellement sur le tutoriel d'Arnauld Michelizza . J'en suis à la partie où il boot avec GRUB. Mais là n'est pas ma question.

    Je souhaiterais me passer entièrement de GRUB. Je voudrais donc partir du bootloader construit en mode réel, récupérer les infos nécessaires et les utiliser dans le noyau qui lui est en mode protégé.

    Pour un rappel, voici un bout du code du bootloader :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    %define BASE    0x100  ; 0x0100:0x0 = 0x1000
    %define KSIZE   50     ; nombre de secteurs à charger
     
    [BITS 16]
    [ORG 0x0]
     
    jmp start
    %include "UTIL.INC"
    start:
     
    ;; ...
     
    next:
        mov ax, 0x10        ; segment de donne
        mov ds, ax
        mov fs, ax
        mov gs, ax
        mov es, ax
        mov ss, ax
        mov esp, 0x9F000    
     
        jmp dword 0x8:0x1000    ; réinitialise le segment de code
     
    ;--------------------------------------------------------------------
    bootdrv:  db 0
    msgDebut: db "Chargement du kernel", 13, 10, 0
    ;--------------------------------------------------------------------
    gdt:
        db 0, 0, 0, 0, 0, 0, 0, 0
    gdt_cs:
        db 0xFF, 0xFF, 0x0, 0x0, 0x0, 10011011b, 11011111b, 0x0
    gdt_ds:
        db 0xFF, 0xFF, 0x0, 0x0, 0x0, 10010011b, 11011111b, 0x0
    gdtend:
    ;--------------------------------------------------------------------
    gdtptr:
        dw 0  ; limite
        dd 0  ; base
    ;--------------------------------------------------------------------
     
    ;; NOP jusqu'a 510
    times 510-($-$$) db 144
    dw 0xAA55


    Et pour le noyau j'ai pour l'instant conservé :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    [BITS 32]
    EXTERN print
    GLOBAL _start
     
    _start:
       mov eax, msg
       push eax
       call print
       pop eax
     
    end:
       jmp end


    Pour tester, et donc commencer, je souhaiterais que le contenu de la variable msg se trouve dans le bootlader.

    Merci d'avance pour votre aide.

  2. #2
    Responsable Systèmes

    Tu dois juste faire coïncider les adresses en mode réel avec les adresse en mode protégé.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  3. #3
    Membre régulier
    Bonjour

    Citation Envoyé par chrtophe Voir le message
    Tu dois juste faire coïncider les adresses en mode réel avec les adresse en mode protégé.
    Ok, je comprends le principe mais comment puis-je connaître l'adresse de mon étiquette msgDebut par exemple ?

  4. #4
    Responsable Systèmes

    Pas besoin de la connaitre, tu connais le nom. tu dois juste faire en sorte que l'adresse mémoire corresponde à l'adresse d'origine org.

    Comme ton code au boot n'est pas l'adresse 0, ça marchera si ton segment démarre là ou le code est chargé, à l'origine à l'adresse 0x7c00 de mémoire.
    Tu peux aussi copier ton code à l'adresse 0x1000 par exemple, mettre ton org à 0x1000, et avoir un descripteur de segment commençant à l'adr 0.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  5. #5
    Membre régulier
    Excuse-moi, mais autant je suis à l'aise avec la plupart des langages, autant l'assembleur ce n'est pas trop mon truc et je me considère comme débutant dans ce domaine.

    Dans le boot le code est chargé à l'adresse 0x7c00. La GDT a été initialisée. et chargée. Est à dire qu à ce moment, les données sont dans le segment de données défini par la GDT ?

    Le noyau est à l'adresse 0x1000.

    J'(avouerais qu'avec un petit exemple je m'en sortirais mieux.

  6. #6
    Responsable Systèmes

    Le noyau est à l'adresse 0x1000.
    Donc soit tu fais partir ta gdt de l'adresse de base 0x1000 et tu gardes ton code avec org 0, soit tu fais un org 0x1000 de façon à ce que les adresses partent de 0x1000, et tu met l'adresse de base de la GDT à 0, il faudra dans ce cas commencer par copier le code de l'adresse 0x7c00 à 0x1000, puis y sauter.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  7. #7
    Membre régulier
    En fait dans le tutoriel, il y a deux GDT : celle du boot juste avant de passer en mode protégé, puis après il recharge la GDT, mais ça je n'ai pas encore fait.

    Tu parles bien de la GDT du boot ?

    Pour :

    tu gardes ton code avec org 0
    C'est pour le code du boot, le code du noyau, ou le segment de code ?

    PS : j'ai l'impression d'être mes élèves qui ne comprennent rien lorsqu'ils ont un petit projet à coder...

  8. #8
    Responsable Systèmes

    oui.

    Comme tu es au niveau du boot, le plus simple est de faire un segment de code et un segment data en mode flat, çad l'adresse de base à 0, et de caler l'origine du code (org) sur l'adresse ou tu charges celui-ci (exemple 0x1000).
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  9. #9
    Membre régulier
    En fait c'est ce qui est fait dans le tutoriel d'Arnauld Michelizza si je ne me trompe pas.

    Si je comprends ce qu'il fait :
    - il boot
    - il copie le code du noyau en mémoire à l'adresse 0x1000
    - il définit la GDT avec des segments de code et data avec une base à 0.

    Mais le code du boot, et donc la variable à transmettre, elle n'est pas copiée en mémoire si je ne fais pas d'erreur. Dans ce cas-là, me faudrait-il copier son contenu dans le segment de donné ?

  10. #10
    Responsable Systèmes

    Le code de boot :
    - place une pile
    - affiche un message probablement via les fonction BIOS (voir code afficher)
    - charge 50 secteurs via les fonctions BIOS à partir du 3ème secteur (arbiraire, le noyau doit s'y trouver, pas de gestion de FS) à l'adresse 0x100:0 càd 0x1000
    - prépare la GDT
    - désactive les interruptions
    - active le mode protégé en mettant le bit de poids faible de cr0 à 1
    - met à jour les registres de descripteurs de segments
    - fait un saut dans le relecteur de segment 8 offset 0x1000, ce qui provoque le saut dans le code du noyau et permet la mise à jour du cache CPU.


    Si tu veux mettre le msg du noyau dans le secteur boot, il te faut mettre le code qui l'affiche et y intégrer le code de print qui doit être du code en mode protégé, donc sans accès aux fonctions BIOS.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  11. #11
    Responsable Systèmes

    Mais le code du boot, et donc la variable à transmettre, elle n'est pas copiée en mémoire si je ne fais pas d'erreur. Dans ce cas-là, me faudrait-il copier son contenu dans le segment de donné ?
    De quelle variable parles tu ?
    les descripteurs de segments servent à contrôler l'accès à la mémoire : exemple zone de mémoire contenant des données ne peut pas exécuter de code. A ce stade, comme tu peux contrôler les accès en créant/modifiant la GDT, tu peux utiliser un descripteur de code et un de data couvrant tout l’espace mémoire, à ta charge d'utiliser le bon descripteur sinon à ce stade, tu va rebooter car pas de handler d’exception (triple fault).

    Sur les OS modernes, tu vas avoir en général un descripteur de code et un de données pour le kernelland (ring 0) et un descripteur de code et un de données pour le userland (ring3). Ensuite la sécurité se fera au niveau de la pagination (voir chapitre 12).
    Ne passe donc pas trop de temps sur la segmentation, assez spécifique au x86.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  12. #12
    Membre confirmé
    il faut faire attention en chargeant le noyau a l'adresse 0x1000, pour l'instant il ne semble faire que 25Ko mais si il devient plus gros il y as un risque que le bootloader ne s'écrase lui même, je conseillerais de charger le noyau après le bootloader (c'est a dire a l'adresse 0x7E00 minimum) et a une adresse qui ne sera pas touché par le noyau dans sa phase initial. personnellement je charge mon noyau de 248Ko a l'adresse 0x50000 et je n'ai eu aucuns problème

    je ne trouve pas non plus que ce soit une bonne idée d'initialiser le mode protégé dans le bootloader, celui ci doit uniquement se contenter de charger un programme plus gros en mémoire. par exemple si on veux faire un bootloader qui charge un fichier plutôt qu'une suite de secteur, je peut vous dire que les 512 octets ne sont pas de trop (et encore 512 c'est si jamais vous n'avez pas de table de partition ou de donnée pour le système de fichier)

  13. #13
    Membre régulier
    Citation Envoyé par chrtophe Voir le message
    Le code de boot :

    Si tu veux mettre le msg du noyau dans le secteur boot, il te faut mettre le code qui l'affiche et y intégrer le code de print qui doit être du code en mode protégé, donc sans accès aux fonctions BIOS.

    De quelle variable parles tu ?
    Ma variable,pour l'instant, est un message à afficher. Je voudrais initialiser cette variable dans le boot pour pouvoir l'utiliser dans le noyau. C'est juste pour comprendre comment faire. En fait, par la suite, je veux récupérer les informations de la mémoire en utilisant les interruptions du Bios et les transmettre ensuite au noyau, sans utiliser GRUB. Je voulais surtout éviter de me taper le mode v8086 pour l'instant.

    D'après le tutoriel Passer en mode protégé, chaque descripteur est est initialisé de façon à pouvoir adresser l'ensemble de la RAM, avec une base à 0x0 et une limite de 0xFFFF pages.

    Le code débute alors en 0x8:0x1000. Mais le segment de code a pour adresse 0x8:0x0000 est-ce bien cela ?

    Les données commenceraient alors en 0xF:0x0000 ? Et je pourrais copier une donnée à cette adresse ? Données que je pourrais récupérer à partir du noyau ?

  14. #14
    Membre confirmé
    a mon avis la meilleur façon de faire passer des données de ton bootloader a ton noyau est de définir un espace d'échange entre les deux vois ça comme une zone mémoire ou copier ces données

    par exemple ton noyau est chargé en 0x1000 (le code et ses données propre) par le bootloader et la chaine de caractère du message est copié par le bootloader a l'adresse 0x7E00
    ensuite lorsque que ton noyau auras besoin de la chaine de caractère il ira chercher a l'adresse 0x7E00

    libre a toi de définir plusieurs données qui pourrait être passé du bootloader au noyau, il suffit de choisir a l'avance quelles seront les adresses des données

    ---

    je vais essayer de clarifier les adresse en mode protégé (c'est pas gagné je suis mauvais pédagogue)

    la première partie de l'adresse est le sélecteur de segment, il contient le numéros du descripteur associé et le niveau de privilège si tu n'as que deux descripteur de mémoire (en plus du premier qui est obligatoirement nul) et que tu est en niveau de protection zéro les sélecteur valide sont 0x8 et 0x10 (il s'agit du numéros de descripteur multiplié par 8)
    les deux descripteur décrivent le même espace mémoire mais pour deux usage différent, l'un pour le code et l'autre pour les donnée

    la seconde partie de l'adresse est l'offset, c'est a dire le décalage entre le début du segment et l'adresse, dans ton cas le segment débute a l'adresse zéro donc l'offset de ton adresse=adresse physique (du moins tant que tu n'as pas activé la pagination)


    Le code débute alors en 0x8:0x1000.

    oui, si le bootloader a bien copié le noyau en 0x1000

    Mais le segment de code a pour adresse 0x8:0x0000 est-ce bien cela ?
    ça c'est une adresse dans le segment de code, le segment de code débute a 0x0000 (je pinaille un peu)



    Les données commenceraient alors en 0xF:0x0000 ?
    non c'est plutôt 0x10:0x0000, le selecteur 0xF n'est pas valide dans ton cas


    Et je pourrais copier une donnée à cette adresse ? Données que je pourrais récupérer à partir du noyau ?
    en mode protégé oui mais en mode réel (le mode de fonctionnement lors du début de l'exécution du bootloader) cette espace (entre l'adresse 0x0 et 0x400) est reservé pour une table de conversion entre les numéros d'interruption et les adresse du code exécuté

  15. #15
    Responsable Systèmes

    Pour compléter ce qu'à dit Bifur sur le mode protégé :
    avec une entrée gdt avec base 0, l'adresse 0x10:0x1000 correspondra à l'adresse mémoire physique 0x1000 (mode flat)
    avec une entrée gdt avec une base 0x1000, l'adresse 0x10:0x1000 correspondra à l'adresse mémoire physique 0x2000, l'adresse d'offset venant s'ajouter à l'adresse de base. Dans ce cas, les adresse 0 à 0xfff seront inaccessibles.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur la création d'un système : http://chrtophe.developpez.com/tutor...s/minisysteme/
    Mon article sur le P2V : http://chrtophe.developpez.com/tutoriels/p2v/
    Consultez nos FAQ : Windows, Linux, Virtualisation

  16. #16
    Membre régulier
    Citation Envoyé par bifur Voir le message


    non c'est plutôt 0x10:0x0000, le selecteur 0xF n'est pas valide dans ton cas

    Oups ! en effet c'était une erreur de calcul de ma part.

    Merci à vous deux pour votre explication. Il faut donc que je trouve une position en mémoire où je serais tranquille, pas en 0000. Entre 400 et 1000, il n'y a rien d'autre ?

  17. #17
    Membre confirmé
    De mémoire je crois me souvenir que le bios stock certaines informations entre 0x400 et 0x500 . Sinon va voir sur le wiki du site osdev.org il devrait y avoir une memory map detaillé

  18. #18
    Membre régulier
    En effet, en revanche entre 500 et 7BFF c'est libre.

  19. #19
    Membre régulier
    Un bout de code pour expliquer la solution :

    Dans le boot :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
       push es ; je sauvegarde sur la pile l'adresse du segment de données
       mov ax, 0x50
       mov es, ax ; je place le segment de donné en 50
       mov di, 0x0 ; je défini mon offset à 0, soit un addresse linéaire à 0x500
       mov al, 66
       mov ah, 07 ; je défini les valeurs
       mov [es:di], ax ; je sauvegarde en mémoire les données
       pop es ; je dépile mon adresse de segment de données


    Dans le noyau :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
     
       mov byte al [0x500] ; je récupère la valeur à l'adresse 0x500
       mov byte [0xB8100], al ; je l'affiche pour réaliser un test