Projet système : environnement : Différence entre versions
m (A déprotégé « Projet système : environnement ») |
m (→Clé USB sur les machines de l'école) |
||
Ligne 136 : | Ligne 136 : | ||
Il est possible de reformatter une clé avec une table de partition, mais les données qu'elle contient seront perdues. | Il est possible de reformatter une clé avec une table de partition, mais les données qu'elle contient seront perdues. | ||
+ | |||
+ | === Tester sur les machines de l'école (pas besoin d'installer GRUB) === | ||
+ | |||
+ | * Changer la table des partitions de la clé en GPT (attention, ça efface tout) | ||
+ | * Créer une partition (fat16, fat32, ext2 (à tester)) et mettre votre <tt>kernel.bin</tt> à la racine | ||
+ | * Brancher la clé USB sur le poste et (re)démarrer | ||
+ | * Au moment du choix de l'OS, appuyez sur la touche <tt>c</tt>, vous voilà dans la console de GRUB (attention, QWERTY) | ||
+ | * Localiser votre partition avec la commande <tt>ls</tt>, généralement <tt>(hd1, gpt1)</tt> | ||
+ | * Rentrez la commande <tt>multiboot (hd1,gpt1)/kernel.bin</tt> | ||
+ | * Tapez la commande <tt>boot</tt> et voilà ! | ||
=== Installation de Grub sur la clé === | === Installation de Grub sur la clé === |
Version du 26 avril 2017 à 16:50

Le projet système se déroule dans les salles affectées (cf. l'emploi du temps). La présentation détaille l'environnement. Ici, nous précisons la configuration des logiciels. Dans le cadre du projet de spécialité 2A système, nous conseillons fortement l'utilisation de Qemu pour les cycles de développement. Parfois vous aurez aussi avantage à utiliser Bochs, particulièrement lors de l'implantation de la pagination.
Conseil
Laisser tourner un noyau en machine virtuelle peut vite consommer beaucoup de ressources processeur, ce qui fait chauffer la machine de développement et peut faire "ramer" votre environnement. Il est donc conseillé d'arrêter les machines virtuelles après utilisation.
Il est possible de réduire au minimum la consommation processeur en utilisant l'instruction HLT à l'intérieur des boucles d'attente active. HLT a pour effet de mettre en pause le processeur jusqu'à ce qu'une interruption matérielle survienne. En général on entoure l'instruction HLT des instructions STI et CLI.
__asm__ __volatile__("sti"); __asm__ __volatile__("hlt"); __asm__ __volatile__("cli");
ou en utilisant les fonctions ASM fournies dans cpu.h
sti(); halt(); cli();
Qemu
Qemu est un logiciel complexe fournissant, entre autre, une machine virtuelle basée sur KVM. L'idée de cette page est de présenter l'utilisation de Qemu/KVM dans le cadre du Projet Système.
Installation
Qemu devrait normalement être disponible sur les machine Ensilinux de l'école. Sur vos machines personnelles, il suffit d'installer les paquetages qemu
et qemu-kvm
pour obtenir Qemu et le support KVM. Pour les personnes qui auraient une distribution un peu moins clefs en main qu'Ubuntu, n'oubliez pas d'ajouter votre utilisateur au groupe kvm
.
Pour les utilisateurs d'Ubuntu:
sudo apt-get install qemu qemu-kvm qemu-common
Pour ceux d'Archlinux:
sudo pacman -S qemu
Vous pouvez compléter avec les commandes pour d'autres distributions/gestionnaires de paquets.
Cas d'utilisation
Le noyau du projet système se lance très simplement avec Qemu. Notre noyau respecte le standard Multiboot qui lui permet d'être chargé aveuglément par un loader supportant ce format, or Qemu sait charger sans aide un noyau au format Multiboot. Ainsi lancer le noyau consiste simplement à lancer la commande suivante:
qemu-system-i386 -m 256 -kernel kernel/kernel.bin
-
-m <valeur>
précise la taille de la mémoire vive disponible en Mo. -
-kernel <chemin vers un binaire>
indique le binaire du noyau à lancer.
Notes :
- le noyau ne peut pas fonctionner correctement avec moins de 256 Mo de mémoire vive.
- sur certaines distributions il faudra peut être remplacer
qemu-system-i386
parqemu-kvm
, tout simplementqemu
ou encoreqemu-system-x86_64
. Les arguments de la ligne de commande restent identiques.
CentOS 6.5 de l'école
/usr/libexec/qemu-kvm -m 256 -kernel kernel/kernel.bin VNC server running on `::1:5900'
et
vinagre [::1]:5900
Les crochets viennent de la notation d'adresse IPv6 dans une URL
Affichage VGA sous CentOS
Qemu est capable de gèrer l'affichage (pour nous l'écran VGA) de plusieurs façons:
- directement dans une fenêtre graphique classique (sdl ou gtk)
- dans un fenêtre texte (en mode VGA) (curses)
- ou en utilisant spice (le même protocole que "pcvirtuel") ou vnc. Il faut alors utiliser un client d'affichage. Sous CentOS, par exemple, vous pouvez utiliser vinagre
vinagre localhost:6666
Débug
Qemu fourni un certain nombre d'outils embarqués pour débugguer facilement le programme qu'il fait tourner. Cette section n'est pas exhaustive, vous êtes invités à lire la manpage de Qemu et/ou à compléter cette page si vous connaissez des astuces de débug supplémentaires.
Connecter un débuggueur
Pour connecter un débuggueur à un noyau, il est nécessaire que celui-ci fournisse un minimum d'outils. Ce sont les gdb-stubs, ils sont disponibles sur le site de gdb. Le point intéressant avec Qemu, c'est qu'il implémente déjà de tels stubs et il n'est pas nécessaire que le noyau le supporte lui même.
Pour les utiliser et connecter le débuggueur, il faut lancer votre noyau de la manière suivante:
qemu-system-i386 -m 256 -kernel kernel/kernel.bin -s -S
- Les options
-m
et-kernel
sont documentées plus haut. -
-s
démarre un serveur gdb sur le port TCP 1234. -
-S
bloque le vCPU tant que le débuggueur n'est pas connecté et commande la machine.
Note pour kvm (accélération matérielle): le qemu de la Ubuntu utilise l'accélération matérielle (kvm) par défaut. Celui de la Debian utilise kvm avec l'option -enable-kvm
.Les breakpoints classiques (break
) fonctionnent mal avec kvm. Vous pouvez utilisez les hbreak, les breakpoints assistés par le matériel (ici c'est qemu, le "matériel"). Par exemple pour mettre un breakpoint sur la fonction kernel_start
(gdb) hbreak kernel_start Hardware assisted breakpoint 1 at 0x1126fd: file start.c, line 14. (gdb) cont Continuing. Breakpoint 1, kernel_start () at start.c:14 14 { (gdb)
CentOS 6.5 de l'école : Il faut désactiver l'accélération (kvm) et utiliser un démarrage avec le boot.pxe (pseudo-carte réseau) pour pouvoir faire des breakpoints. Il faut donc récupérer le fichier boot.pxe du ZIP fournit en PSE.
Il FAUT se placer dans le répertoire avec le fichier kernel.bin (il sera chargé par le boot.pxe):
qemu-system-i386 -m 256 --s -S -no-kvm -net nic -net user,tftp=`pwd`,bootfile=boot.pxe
Mais qemu sans kvm devient alors plus permissif que le vrai matériel. Certains bugs n'apparaîtront pas.
utiliser le débogueur
Qemu est bloqué pour le moment, il vous reste à démarrer gdb
(ou ddd
):
$ gdb kernel/kernel.bin GNU gdb (GDB) 7.6 [...] Reading symbols from /home/dejeand/psys/kernel/kernel.bin...done. (gdb)
puis à le connecter à la VM:
(gdb) target remote :1234 Remote debugging using :1234 0x0000fff0 in ?? ()
GDB est prêt, mettez en place vos breakpoints, watchpoints et continuez l'éxecution avec la commande cont
.
Console/sortie débug
Une fonctionnalité intéressante de Qemu est la console de débug. Quand vous écrivez dans la mémoire de la machine pour faire de l'affichage VGA 80x25, Qemu émule la carte et vous affiche le contenu dans une fenêtre. Quand vous modifiez l'affichage (passage en mode VESA, ou en mode dessin au pixel), vous n'avez plus la possibilité d'avoir des traces.
Qemu fourni un port sur son processeur virtuel qui permet de sortir des traces directement dans le terminal. Ainsi chaque caractère émis sur ce port à l'aide d'un outb
apparaît dans votre terminal. Vous pouvez ainsi débugguer même quand l'affichage est inutilisable. Pour l'activer, lancez qemu avec l'option -debugcon stdio
.
Exemple :
outb('a', 0xE9);
Fait apparaître un a
dans votre terminal, à vous d'adapter votre console_putbytes
pour qu'il puisse réaliser celà. Attention ce n'est qu'une possiblité de débug, non un élement requis pour l'évaluation de votre noyau.
Quelques options supplémentaires
Certaines des options qui suivent pourront vous être utile:
-no-reboot
quitte Qemu au lieu de redémarrer si il y a lieu (triples fautes, etc.).-no-shutdown
bloque le vCPU lors d'un arrêt de la VM au lieu de rebooter, pratique pour conserver les traces en cas du bug.-display <type>
change le type d'affichage pour type, utiliser le type curses permet d'obtenir un affichage VGA directement dans la console et non dans une fenêtre X, pratique pour une utilisation distante. Voir la manpage pour plus de détails.-hd[abcd] <file>
utiliser file comme disque dur n° (a=0 .. d=3).
Clé USB
Il est également possible de tester votre noyau sur un vrai PC, via le chargeur Grub.
Si Grub est déjà installé sur votre clé (c'est le cas sur la clé Ensimag distribuée en 1A en septembre 2010), il suffit d'ajouter la configuration suivante dans le fichier boot/grub/grub.cfg, à adapter selon le partitionnement de la clé :
menuentry "kernel.bin" { multiboot (hd0,1)/kernel.bin }
Sinon, nous vous proposons un programme pour installer Grub sur une clé USB.
Attention : le script d'installation ne supporte que les clés USB munies d'une table de partition. La commande mount vous permet de savoir si c'est le cas (la présence d'un chiffre à la fin du nom de périphérique) :
# Exemple de clé sans table de partition /dev/sdb on /media/disk type vfat ... # Exemple de clé avec table de partition /dev/sdb1 on /media/disk type vfat ...
Il est possible de reformatter une clé avec une table de partition, mais les données qu'elle contient seront perdues.
Tester sur les machines de l'école (pas besoin d'installer GRUB)
- Changer la table des partitions de la clé en GPT (attention, ça efface tout)
- Créer une partition (fat16, fat32, ext2 (à tester)) et mettre votre kernel.bin à la racine
- Brancher la clé USB sur le poste et (re)démarrer
- Au moment du choix de l'OS, appuyez sur la touche c, vous voilà dans la console de GRUB (attention, QWERTY)
- Localiser votre partition avec la commande ls, généralement (hd1, gpt1)
- Rentrez la commande multiboot (hd1,gpt1)/kernel.bin
- Tapez la commande boot et voilà !
Installation de Grub sur la clé
- Brancher la clé USB sur le poste de développement
- Créer sur la clé un fichier ou un répertoire vide nommé exactement : installer_grub_ici
- Taper dans un terminal la commande : usbgrub. Cette commande va localiser votre clé USB par le fichier marqueur créé précedemment, et y installer grub dans un répertoire nommé boot.
- Supprimer le fichier installer_grub_ici
Attention : si votre clé était déjà bootable, il ne sera plus possible de démarrer le système qu'elle contient sans retravailler la configuration de grub (nous ne l'avons pas testé).
Tester un noyau
Une fois la clé bootable créée, on peut l'utiliser pour démarrer le noyau sur n'importe quel PC récent :
- Copier le fichier kernel.bin à la racine de la clé
- Déconnecter proprement (démonter) la clé USB
- Brancher la clé sur un PC éteint
- Démarrer le PC et faire apparaître le menu de sélection d'amorçage (F12 sur les machines de D200 / D201)
- Choisir le périphérique correspondant à la clé (USB Device + Entrée en D20x)
On voit alors Grub qui affiche le menu de boot.
peut être démarré via le menu Applications -> Outils système -> Sun VirtualBox.
Note : si vous souhaitez utiliser VirtualBox sur votre machine personnelle sous Linux, il ne faut pas choisir la version OSE (Open Source Edition) qui n'est pas compatible avec le chargeur de noyau fourni. La version binaire fonctionne elle sans problème. Il faut également installer l'extension pack de VirtualBox pour avoir accès aux fonctionnalités PXE nécessaires au projet (voir la section Download de VirtualBox).
Création de la machine virtuelle
On crée d'abord une machine virtuelle de base.
- Nouveau -> Suivant.
- Choisir un nom pour la machine virtuelle. Nous choisissons de l'appeler Noyau. Système d'exploitation : Other, version : Other/Unknown. Suivant.
- Taille de la mémoire vive : 64 Mo.
- Décocher l'option Disque dur d'amorçage. La machine virtuelle n'a pas besoin de disque dur. Suivant. Continuer. Terminer.
Maintenant, on paramètre plus finement cette machine virtuelle.
- Clic droit sur Noyau -> Préférences -> Système. Tout décocher dans Ordre d'amorçage et cocher Réseau.
- Toujours dans Préférences -> Interfaces séries -> Port 1. Cocher l'activation. Port numéro : COM1. Mode port : Tuyau hôte. Cocher Créer un tuyau. Port/Chemin fichier : /tmp/vbox-psys. OK.
Finalement on installe un programme (Noyau.pxe.gz) nécessaire pour le boot du noyau par le réseau. C'est un chargeur intermédiaire, chargé par le BIOS de la machine virtuelle via un réseau virtuel, qui chargera lui-même votre noyau en mémoire de la machine virtuelle pour l'exécuter de manière compatible avec la Multiboot Specification (voir le manuel de Grub).
mkdir -p ~/.VirtualBox/TFTP gzip -dc Noyau.pxe.gz > ~/.VirtualBox/TFTP/Noyau.pxe
Tester un noyau
Après avoir configuré la machine virtuelle comme indiqué ci-dessus, il est possible de tester un noyau en le copiant dans le répertoire TFTP.
cp kernel.bin ~/.VirtualBox/TFTP/
Il suffit de lancer la machine virtuelle pour voir le noyau démarrer.
Pour l'arrêter, il suffit de fermer la fenêtre et de choisir l'option Eteindre la machine.
Débogage
Le fichier /tmp/vbox-psys créé par VirtualBox pour le port série est une socket Unix, à laquelle gdb ne peut pas se connecter. Il faut donc la convertir en un pseudo port série. Pour cela, vous pouvez utiliser la commande socat. Après le lancement de la machine virtuelle, la commande :
socat UNIX-CONNECT:/tmp/vbox-psys PTY,link=/tmp/gdb-psys
créera le lien symbolique /tmp/gdb-psys vers un nouveau pseudo port série, dont on peut alors se servir directement avec gdb ou ddd à la place de /dev/ttyS0.
Conseil
Laisser tourner un noyau en machine virtuelle peut vite consommer beaucoup de ressources processeur, ce qui fait chauffer la machine de développement et peut faire "ramer" votre environnement. Il est donc conseillé d'arrêter les machines virtuelles après utilisation.
Il est possible de réduire au minimum la consommation processeur en utilisant l'instruction HLT à l'intérieur des boucles d'attente active. HLT a pour effet de mettre en pause le processeur jusqu'à ce qu'une interruption matérielle survienne. En général on entoure l'instruction HLT des instructions STI et CLI.
__asm__ __volatile__("sti"); __asm__ __volatile__("hlt"); __asm__ __volatile__("cli");
-->
Bochs
Bochs est un émulateur x86. Contrairement à VirtualBox et Qemu, il n'éxecute pas le code directement sur le processeur quand c'est possible, mais émule entièrement le comportement du processeur, et de la machine de manière générale. L'inconvénient majeur est une grosse perte de performances, l'avantage est qu'il est possible de suivre et de tracer aboslument tous les comportements de CPU et du matériel émulé.
Cet outil est primordial pour le développement de la mémoire virtuelle car il permet à tout moment de l'éxecution:
- obtenir le mapping mémoire courant,
- tracer les fautes de pages, leur adresse et leur résolution,
- éxecuter en mode pas à pas quelque soit la situation dans le noyau, peu importe l'état des gdb-stubs par exemple.
Installation
Bochs ne sera disponible que sur vos machines personnelles, dans la mesure où il nécessite une image de disque pour fonctionner, et par conséquent les droits root pour la créer. Nous verrons ceci un peu plus loin. Il existe deux versions de Bochs dans vos dépôts, une version standard, et une version avec les outils de développement, notament le débuggueur intégré. Nous souhaitons installer cette seconde version.
Sur Ubuntu, la version proposée dans les dépôts n'est pas compilée avec le débuggueur. Il faudra donc la compiler manuellement. Pour les utilisateurs d'Archlinux, vous trouverez le package dans les dépôts AUR :
yaourt -S aur/bochs-with-debugger
Pour les autres vous pouvez vous inspirer des commandes suivantes pour compiler votre version:
$ cd /tmp $ wget http://downloads.sourceforge.net/project/bochs/bochs/2.6/bochs-2.6.tar.gz $ tar xzf bochs-2.6.tar.gz $ cd bochs-2.6/ $ ./configure --enable-smp --enable-all-optimizations --with-all-libs --prefix=/opt --enable-debugger --enable-plugins --enable-pci --enable-pcidev --enable-usb --enable-3dnow --enable-debugger-gui
Puis, si tout s'est bien passé :
$ make -j n
où n est jusqu'à deux fois le nombre de coeurs (threads) disponibles sur votre machine.
Enfin installez le tout (dans /opt pour garder une machine saine):
$ sudo make install
Configuration pour le projet
Emuler un système dans bochs nécessite de mettre en place un petit environnement d'éxecution. Il consiste en un fichier de configuration et une image de disque. Nous vous fournissons le nécessaire dans les paragraphes suivants.
Configuration de la machine émulée
Bochs utilises un fichier de configuration nommé bochsrc
qu'il cherche à l'endroit où vous lancez l'émulateur. Le fichier suivant devrait convenir pour un projet système :
bochsrc
megs: 256 boot: disk ata0-master: type=disk, path="disk.img", mode=flat, cylinders=260, heads=16, spt=63
Quelques explications :
-
megs: 256
quantité de mémoire alloué au système. -
boot: disk
la machine boot à partir d'un périphérique de type disque dur. -
ata0-master: type=disk [...]
décrit la configuration du disque dur 0, c'est un disque IDE, ayant comme base le fichierdisk.img
.
En lançant bochs dans le même répertoire que le fichier, il prendra cette configuration automatiquement, sinon vous pouvez utiliser :
bochs -f /chemin/vers/le/fichier/bochsrc
Utilisation d'une image de disque
Comme indiqué précédement, nous allons devoir utiliser une image de disque, un fichier qui représente le contenu d'un disque dur que Bochs va présenter comme tel au système qu'il émule. En effet Bochs n'est pas capable de booter un noyau au format Multiboot comme Qemu.
Pour cela il faut créer un fichier dont le contenu sera comme indiqué ci-dessous :
- Un Master Boot Record (MBR) avec un loader et une table des partitions
- Une partition primaire avec un système de fichier contenant le binaire du noyau et le fichier de configuration du loader.
Nous avons choisi d'utiliser un système de fichiers ext2, et GRUB comme loader. Comme la construction d'une telle image peut être compliquée et peu intéressante, nous vous en fournissons une : Fichier:Disk.img.tgz.
Décompressez (c'est du bzip2, l'extension est mauvaise à cause du wiki) et assurez-vous de son intégrité (utilisez le hash MD5 ci-dessous) :
$ tar xjf disk.img.tgz $ md5sum disk.img 78551d0692cc798a2624654c5fbd8fa2 disk.img
Une fois fait, placez l'image à l'endroit où vous lancerez bochs, ou modifiez le fichier de configuratioun pour qu'il pointe sur la bonne image.
Maintenant nous allons voir comment copier votre noyau dans l'image de disque afin d'utiliser l'émulateur. Pour cela il vous faudra les droits administrateurs pour monter et démonter l'image de disque. Ce qui explique pourquoi ceci ne peut avoir lieu sur les machines de l'école, l'utilisation de fuse-ext2 n'ayant pas encore été explorée. La séquence suivante copie le binaire du noyau dans l'image de
$ mkdir -p disk/ $ sudo mount -t ext2 -o loop,offset=1048576 disk.img disk/ $ sudo cp kernel/kernel.bin disk/kernel.bin $ sync $ sudo umount disk/ $ rm -r disk/
A titre d'exemple, on peut ajouter une cible pour lancer bochs dans le top-Makefile du noyau ce qui ajoute la cible suivante:
disk: mkdir -p $@ .PHONY: bochs bochs: all disk @echo "### This target will require root access to mont disk image ! ###" sudo mount -t ext2 -o loop,offset=1048576 disk.img disk/ sudo cp kernel/kernel.bin disk/kernel.bin sync sudo umount disk/ bochs
Il ne reste plus qu'à faire make bochs
pour compiler puis lancer automatiquement bochs avec une image mise à jour. La seule contrainte est l'entrée du mot de passe pour sudo
.
Utilisation d'une image de cdrom sans droit root
Il est possible de construire une image de cdrom sans avoir besoin des droits root
Il faut d'abord créer un répertoire (ici iso
), y mettre kernel.bin (ici dans iso/boot
) et un fichier de boot grub.cfg
dans iso/boot/grub/
mkdir -p iso/boot/grub cp kernel.bin iso/boot cp grub.cfg iso/boot/grub
Le fichier grub.cfg peut contenir les lignes suivantes:
set timeout=15 set default=0 # Set the default menu entry menuentry "OS projet System: kernel.bin" { multiboot /boot/kernel.bin # The multiboot command replaces the kernel command boot }
La construction de l'image cd.iso
est réalisée par grub2
grub-mkrescue -o cd.iso iso
Le bochsrc
suivant boot bochs sur l'image de cdrom:
megs: 256 boot: cdrom ata0-master: type=cdrom, path="cd.iso", status=inserted
Lancer bochs avec son débuggueur
Lorsque Bochs est compilé avec le débuggueur, celui-ci va arrêter automatiquement l'éxecution à chaque démarrage pour vous laisser le configurer, ajouter des breakpoints, etc. Vous allez donc obtenir :
$ bochs [...] Next at t=0 (0) [0x00000000fffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0 00000000000i[XGUI ] Mouse capture off <bochs:1>
La commande cont
vous permet de continuer l'éxecution telle quel.
Une fois l'émulation lancée, vous pouvez :
- Interrompre l'éxecution pour configurer le niveau de logs en utilisant la "boite à outils" disponible dans la fenêtre de bochs.
- Interrompre l'éxecution en faisant Ctrl+C pour obtenir le prompt. Ainsi vous configurer les points d'arrêts, les watch points, regardez les valeurs des registres, etc.
La page suivante documente à merveille l'utilisation du débuggueur intégré: Documentation du débuggueur.
Astuce de débug
Parfois au cours de votre développement, particulièrement si vous utilisez Bochs, vous obtiendrez comme unique information, l'adresse contenue dans le pointeur d'instruction au moment de la faute. Vous pouvez exploiter cette information, si faible soit-elle.
Exemple : La trace suivante est émise à Bochs:
00171331842d[CPU0 ] page fault for address 00000000 @ 001472c1
et m'indique que j'ai déréférencé NULL
, à l'adresse 0x001472c1
. Pour savoir où la faute à exactement eu lieu, je désassemble le binaire du noyau et je peux savoir dans quelle fonction se trouve cette adresse :
$ objdump -d kernel/kernel.bin | less [...] 001472b0 <kernel_start>: 1472b0: 55 push %ebp 1472b1: 89 e5 mov %esp,%ebp 1472b3: 83 ec 18 sub $0x18,%esp 1472b6: b8 00 00 00 00 mov $0x0,%eax 1472bb: c7 00 00 00 00 00 movl $0x0,(%eax) <------------- ICI 1472c1: c7 04 24 10 28 15 00 movl $0x152810,(%esp) 1472c8: e8 f7 51 00 00 call 14c4c4 <puts> [...]
A partir de ceci, on voit que l'on est dans la fonction kernel_start, peut de temps après le début, et on repère le pattern :
mov $0x0,%eax movl $0x0,(%eax)
typique du : *((int*)0) = 0;
que j'ai introduit pour créer la faute. Vous vouliez faire du système, vous êtes en plein dedans.