Ce n'est pas un véritable système de fonctionnement. C'est juste un système d'opération simple créé à des fins éducatives.
L'objectif principal que je suis est d'apprendre comment le système d'exploitation fonctionne à partir de zéro. À partir du propre secteur de démarrage, des interruptions matérielles et logicielles, propres pilotes.
Le référentiel est suffixé avec GCC parce que je prévois d'écrire un autre système d'exploitation simple avec de la rouille. Donc, j'espère qu'il y aura Ghaiklor-OS-GCC et Ghaiklor-OS-Rustc .
| Bonjour le monde |
|---|
Ghaiklor-OS-GCC se compose de deux fichiers au format binaire brut: boot.bin et kernel.bin . Ils sont situés dans boot / boot.bin et kernel / kernel.bin en conséquence après la compilation.
boot.bin est compilé via NASM . MakeFile prend BOOT / BOOT.ASM et appelle nasm boot/boot.asm -f bin -o boot/boot.bin . Les manches NASM comprennent des sous-repliants lui-même, de sorte que tous les fichiers d'assemblage seront compilés en binaire. Rien d'autre, simple.
Kernel.bin est compilé via le GCC et LD de compilateur croisé que vous devez installer. Jetez un œil à la section de l'environnement de développement. Une fois le compilateur croisé installé, nous pouvons prendre des sources de CPU , les pilotes , incluent les dossiers de noyau et de LIBC récursivement. Tous les fichiers .c sont compilés via gcc . Les fichiers d'objets compilés sont utilisés pour compiler Kernel.bin puis, via LD - ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary .
OS-IMAGE.BIN est compilé concatenate de boot.bin et kernel.bin . Facilement réalisé avec cat boot/boot.bin kernel/kernel.bin > os-image.bin .
J'avais écrit bootstrap.sh Script, que vous pouvez exécuter. Il installera toutes les dépendances nécessaires pour votre machine hôte.
bash bootstrap.shLorsqu'un ordinateur est allumé ou réinitialisé, il passe par une série de diagnostics appelés Post - Power-on-Agest. Cette séquence se termine dans la localisation d'un dispositif de démarrage, comme une disquette, un CDROM ou un disque dur.
Un périphérique est démarrable s'il transporte un secteur de démarrage avec la séquence d'octets 0x55 , 0xAA dans les octets 511 et 512 respectivement. Lorsque le BIOS trouve un tel secteur de démarrage, il est chargé en mémoire à 0x0000:0x7C00 .
Une simple implémentation du périphérique de démarrage:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$ Résultats dans CURRENT_POINTER - START_POINTER . De cette façon, nous calculons la durée de notre enregistrement de démarrage. Ensuite, nous en soustracons 510 et remplissons de zéros, obtenant l'enregistrement de démarrage de 512 octets avec la signature du secteur de démarrage.
Par exemple, nous avons $ - $$ égal à 100. Donc, nous avons 510 - 100 = 410 octets gratuits. Nous remplissons ces 410 octets de zéros. Et les deux derniers octets 511 et 512 sont une signature botable que nous remplissons de dw 0xAA55 .
Fait! Nous avons notre appareil de démarrage et pouvons remplacer notre jmp $ par n'importe quel code que vous aimez.
Implémentation du secteur de démarrage
Au début, notre code s'exécute en mode réel.
Le mode réel est un mode 16 bits simpliste qui est présent sur tous les processeurs x86. Le mode réel a été la première conception de mode x86 et a été utilisé par de nombreux systèmes d'exploitation précoces. À des fins de compatibilité, tous les processeurs x86 commencent l'exécution en mode réel.
Qu'est-ce qui est mauvais et bon en mode réel?
Inconvénients
Pros
En raison des nombreuses limitations et problèmes de mode réel, nous devons passer en mode protégé.
Le mode protégé est le mode de fonctionnement principal des processeurs Intel modernes depuis le 80286. Il permet de travailler avec plusieurs espaces d'adresses virtuels, chacun ayant un maximum de 4 Go de mémoire adressable.
Étant donné que le processeur initialisé par le BIOS commence en mode réel, le passage en mode protégé vous empêche d'utiliser la plupart des interruptions du BIOS. Avant de passer en mode protégé, vous devez désactiver les interruptions, notamment NMI, activer la ligne A20 et charger la table des descripteurs globaux.
Algorithme pour la commutation en mode protégé:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pmImplémentation pour le passage à PM
Table des descripteurs globaux
Mais, nous pouvons aller plus loin ...
Qu'est-ce que le mode long et pourquoi le configurer?
Depuis l'introduction des processeurs x86-64, un nouveau mode a également été introduit, qui est appelé mode long. Le mode long se compose essentiellement de deux sous-modes qui sont le mode 64 bits réel et le mode de compatibilité (32 bits).
Ce qui nous intéresse, c'est simplement le mode 64 bits car ce mode fournit de nombreuses fonctionnalités telles que:
Avant de passer en mode long, nous devons vérifier si le CPU prend en charge ce mode. Dans le cas, si CPU ne prend pas en charge le mode long, nous devons se rendre en mode protégé.
Détecter si le mode long prend en charge
Si c'est le cas, passez en mode long
Tous ces modes sont excellents, mais nous ne pouvons pas écrire un système d'exploitation en 512 octets. Ainsi, notre secteur de démarrage doit savoir comment charger notre noyau compilé à partir du disque dur.
Lorsque nous sommes en mode réel, nous pouvons utiliser les interruptions du BIOS pour lire le disque. Dans notre cas, est INT 13,2 - Read Disk Sectors .
Comment l'utiliser?
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13 Ce code entraîne la lecture du disque dur dans l'adresse KERNEL_OFFSET_IN_MEMORY . Il lit 15 secteurs à partir du second et le stocke par l'adresse KERNEL_OFFSET_IN_MEMORY .
Étant donné que notre image OS compilée est une concaténation du secteur de démarrage et du noyau, et nous savons que notre secteur de démarrage est de 512 octets, nous pouvons être sûrs, que notre noyau commence dans le deuxième secteur.
Lorsque la lecture est terminée avec succès, nous pouvons appeler des instructions sur notre KERNEL_OFFSET_IN_MEMORY et donner une exécution au noyau.
call KERNEL_OFFSET_IN_MEMORY
jmp $Implémentation de la lecture du disque
Nous pouvons tracer une ligne ici sur notre secteur de démarrage. Le flux est simple:
call simple;À cette étape, notre secteur de démarrage a terminé son travail et commence à travailler avec le noyau.
Vous pouvez naviguer dans les sources de démarrage et essayer de comprendre comment cela fonctionne.
Lorsque nous appelons l'instruction par adresse, nous pouvons avoir quelques problèmes. Nous ne pouvons pas sûr que l'instruction par adresse est un kernel_main() . La solution est simple.
Nous pouvons écrire un sous-routine qui est attaché au début du code du noyau. Ce sous-routine appelle la fonction externe de notre noyau - kernel_main() . Lorsque les fichiers d'objets seront liés ensemble, cet appel sera traduit en appel de notre kernel_main() .
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $Implémentation de l'entrée du noyau
À cette étape, nous avons un point d'entrée sur notre méthode kernel_main() . Et c'est notre point d'entrée pour le noyau entier.
Je pense que c'est ennuyeux d'expliquer comment fonctionne #include et ce qui se passe dans notre kernel_main() . Vous pouvez facilement suivre les méthodes que j'en appelle.
Entrée du noyau en C
C'est la partie la plus simple.
Nous devons créer boot/boot.bin Image au format binaire brut. Pour ce faire, nous appelons l'assembleur nasm avec des drapeaux spéciaux.
nasm boot/boot.asm -f bin -o boot/boot.binIl en résulte un format binaire brut que vous pouvez exécuter via Qemu.
À cette étape, nous avons du secteur de démarrage compilé qui fonctionne.
Nous devons construire toutes les sources de tous les dossiers récursivement, à l'exception du dossier boot .
Tous les fichiers C sont compilés sur les fichiers d'objet via des fichiers gcc et d'assemblage via nasm :
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE > Il se traduit dans tous les fichiers d'objets nécessaires pour se lier au format binaire brut. Tout ce qui reste à faire est de les relier via ld :
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary Notez que kernel/kernel_entry.o à la première place, car nous avons un problème avec l'appel du kernel_main() . De cette façon, nous garantissons que la première instruction sera appelée à partir de notre boot/kernel_entry.asm .
Après tout, nous avons compilé l'image du noyau au format binaire brut.
Puisque, notre secteur de démarrage et notre noyau sont des formats binaires bruts, nous pouvons simplement les concaténer.
cat boot/boot.bin kernel/kernel.bin > os-image.bin Maintenant, nous pouvons exécuter os-image.bin via qemu-system-i386 . BIOS essayant de localiser le secteur bootable, découvrez notre boot/boot.bin et voit la signature. Commence à exécuter notre code d'assemblage sur boot/boot.bin qui charge notre kernel/kernel.bin via int 13,2 dans la mémoire et l'exécute.
C'est ainsi que tout fonctionne ensemble. N'hésitez pas à naviguer dans le projet, merci?
La licence du MIT (MIT)
Copyright (c) 2016 Eugene Obrezkov
L'autorisation est accordée gratuitement à toute personne qui obtient une copie de ce logiciel et des fichiers de documentation associés (le "logiciel"), pour traiter le logiciel sans restriction, y compris sans limiter les droits d'utilisation, de copie, de modification, de fusion, de publication, de distribution, de sublince et / ou de vendre des copies des conditions suivantes.
L'avis de droit d'auteur ci-dessus et le présent avis d'autorisation sont inclus dans toutes les copies ou des parties substantielles du logiciel.
Le logiciel est fourni "tel quel", sans garantie d'aucune sorte, express ou implicite, y compris, mais sans s'y limiter, les garanties de qualité marchande, d'adéquation à un usage particulier et de non-contrefaçon. En aucun cas, les auteurs ou les détenteurs de droits d'auteur ne seront pas responsables de toute réclamation, dommage ou autre responsabilité, que ce soit dans une action de contrat, de délit ou autre, découlant de, hors du logiciel ou de l'utilisation ou d'autres relations dans le logiciel.