Capsule de théorie Démarrage d’un ordinateur
Introduction
Lorsque nous avons étudié l’architecture d’un ordinateur, nous avons vu que :
- Un système informatique est composé d’une partie matérielle et d’une partie logiciel. Un ordinateur sans programme est juste un monceau de composants électroniques, de métal et de plastique parfaitement inutile.
- Pour des raisons de performance, la mémoire principale des ordinateurs moderne est volatile, c’est-à-dire qu’elle perd les données stockées en l’absence d’une source d’énergie (on ne sait pas faire des mémoires à la fois rapides et non volatiles). Cette mémoire est donc vide au démarrage de l’ordinateur.
- La mémoire secondaire est non volatile, mais n’est pas directement accessible par le CPU. Celui-ci doit exécuter un programme pour charger dans la mémoire principale, des données (possiblement le code machine d’un programme) stockées dans la mémoire secondaire (disque, DVD, clé USB, etc.).
Compte tenu de ce qui vient d’être dit, le code du système d’exploitation doit être conservé dans la mémoire secondaire, mais comme le CPU a besoin d’un programme pour charger des données de la mémoire secondaire dans la mémoire principale, comment ce premier programme peut-il se trouver dans la mémoire principale ?
C’est à cette question que se propose de répondre cette capsule. La première partie aborde les notions de firmware et de chargeur d’amorçage puis présente brièvement les deux types de firmwares les plus courants : BIOS et UEFI. La seconde partie décrit le processus de démarrage d’une machine avec l’un ou l’autre de firmware.
Firmware et chargeur d’amorçage
Pour charger le premier programme dans la mémoire principale d’un ordinateur, on peut imaginer au moins deux solutions :
- Charger manuellement le code machine du premier programme dans la mémoire (octet par octet) à chaque mise en route avant de démarrer le processeur.
- Charger le code du premier programme dans la mémoire principale à l’usine et faire en sorte que ce code y reste.
La première solution avait été retenue pour bon nombre de mini-ordinateurs (PDP-8, PDP-11, Nova, etc.) et pour les premiers micro-ordinateurs (Micral, ALTAIR 8800). En effet, la capacité de leur mémoire principale était insuffisante pour y stocker un chargeur d’amorçage capable de prendre en compte tous les types d’unité de mémoire de masse (lecteur de cartes perforées, lecteur de bandes perforées, lecteur de bandes magnétiques, disques durs, etc.).

La figure 1 montre le panneau de contrôle d’un mini-ordinateur DEC PDP-11 qui permet la saisie du code d’un bootstrap loader de quelques dizaines d’octets comme celui de la figure 2. Les nombres sont écrits en octal (chaque chiffre représente 3 bits). L’utilisateur devait saisir la première adresse (157744) à l’aide des 18 commutateurs, puis chacune des 14 instructions (les nombres de la colonne Cont.) l’une après l’autre à l’aide des mêmes commutateurs. L’adresse était automatiquement incrémentée lorsqu’il chargeait une instruction avec le commutateur « load ».
Remarque : La mémoire principale des mini-ordinateurs des années 1970 était composée de mémoires à tores magnétiques qui est non volatile, il n’était donc pas nécessaire de resaisir le chargeur à chaque démarrage. Ce n’était pas le cas de l’ALTAIR 8800 qui était équipé de mémoire à semi-conducteur.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Loc. Cont. Label Instruction Comment
================================================
157744 016701 mov device,r1 get csr address
157746 000026
157750 012702 loop: mov #offset,r2 get offset
157752 000352 offset:
157754 005211 inc (r1) read frame
157756 105711 wait: tstb (r1) wait for ready
157760 100376 bpl wait
157762 116162 movb (r1),bnk(r2) store data
157764 000002
157766 157400
157770 005267 inc offset bump address
157772 177756
157774 000765 br loop
157776 177550 device: HSR csr, or 177560 for teletype
À partir de la fin des années 1970, la deuxième solution s’est progressivement généralisée. Le premier programme appelé fimware est chargé par le constructeur dans une zone non volatile de la mémoire principale, traditionnellement une ROM (read-only memory), aujourd’hui une forme ou une autre de NVRAM (non-volatil random access memory).
Firmwares courants
Le firmware peut être un chargeur d’amorçage relativement simple comme celui du BIOS des « compatibles PC », plus complexe comme celui de l’UEFI des ordinateurs actuels, ou un système d’exploitation complet comme le TOS de l’Atari ST ou le Mac OS jusqu’en 1997.
Le préfixe « firm » doit être compris dans le sens de « ferme », un intermédiaire entre mou (soft) et dur (hard), et exprime l’idée que le firmware se trouve entre le matériel (hardware) et le logiciel (software). En français, on parle de micrologiciel qui renvoie à l’idée de composant logiciel proche du matériel (microprocesseur).
Dans nos ordinateurs (PC et serveur d’entreprise), on trouve essentiellement deux types de firmwares :
- BIOS : Basic Input/Output System hérité de l’IBM PC du début des années 1980.
- UEFI : Unified Extensible Firmware Interface initiée par Intel au milieu des années 1990 pour ses processeurs Itanium avant d’être reprise par l’Unified EFI Forum en 2005.
Même s’ils ont tous deux un rôle similaire, le BIOS et l’UEFI fonctionnent de manière très différente. Pour faciliter la migration de l’un à l’autre, l’UEFI dispose le plus souvent d’un mode « BIOS compatible » dans lequel il simule le comportement d’un BIOS.
Processus d’amorçage (boot process)
Au démarrage de l’ordinateur, le processeur est initialisé et commence à exécuter le code qui se trouve à l’adresse correspondant au « vecteur de reset » (reset vector) définie par le constructeur du processeur. Pour les processeurs Intel x86 (32 et 64 bits) le vecteur de reset correspond, pour des raisons historiques, à l’adresse physique FFFFFFF0 (16 octets sous la barre des 4 Gio).
Cette adresse se trouve dans une zone réservée à de la mémoire non-volatile (NVRAM) qui contient le firmware. Comme l’exécution de code depuis la mémoire non volatile est très lente, la première étape du démarrage consiste à initialiser le contrôleur de mémoire et à copier le code du firmware dans la DRAM beaucoup plus rapide.
La seconde étape est l’autotest de démarrage (power on self-test ou POST). Durant cette phase, le programme effectue notamment les opérations suivantes :
- Test du CPU et de la stabilité de l’alimentation.
- Vérification de la somme de contrôle (checksum) du firmware.
- Test des composants importants de la carte mère (timer, contrôleur d’interruption, etc.).
- Test et initialisation de la sortie vidéo et du clavier.
- Test minimal de la mémoire (un test exhaustif prend trop de temps).
- Énumération, initialisation et catalogage des contrôleurs d’entrées-sorties.
Lorsque tous les tests sont passés, le programme affiche durant quelques secondes un message qui indique sur quelle touche appuyer pour entrer dans le setup du firmware. Le setup est une interface utilisateur qui permet de configurer les paramètres du matériel et de sauver la configuration dans la mémoire CMOS (voir encadré).
Si l’utilisateur ne fait rien, l’exécution du programme se poursuit. La prochaine étape consiste à charger le système d’exploitation, mais le processus est très différent selon que le firmware est de type BIOS ou de type UEFI. Voyons d’abord le processus pour un firmware de type BIOS.
Processus d’amorçage d’un ordinateur avec BIOS
Un firmware de type BIOS ne peut pas directement charger un système d’exploitation. Pour cela, il parcourt les unités de stockage dans l’ordre défini dans les paramètres du setup (ordre d’amorçage ou boot order) et teste si le premier secteur est un « secteur de démarrage principal » (master boot sector ou MBR).
La structure d’un MBR est la suivante :
- 446 octets pour une routine d’amorçage
- 64 octets pour la table de partition
- 2 octets pour la signature 0x55 et 0xAA
Pour savoir si un secteur est un MBR, le firmware compare les valeurs des deux derniers octets à celle de la signature. Dès qu’un MBR est trouvé, le firmware charge les 512 octets du secteur en mémoire puis passe la main à la routine d’amorçage.
La routine d’amorçage est spécifique à chaque système d’exploitation (ou famille de systèmes d’exploitation) et devrait, en principe, charger le système d’exploitation avant de lui passer la main. Toutefois, le chargement d’un système d’exploitation moderne est une opération complexe, beaucoup trop complexe pour être réalisée avec seulement 446 octets de code. Il s’agit donc le plus souvent un chargeur d’amorçage de premier niveau (first stage bootloader ou stage 1 bootloader) qui permet de charger un chargeur d’amorçage de second niveau (second stage bootloader ou stage 2 bootloader) plus complexe et généralement capable de charger le système d’exploitation.
Par exemple, lorsque l’on installe une distribution de Linux, la dernière étape de l’installation consiste à installer un chargeur d’amorçage. Le plus souvent il s’agit de GRUB (GRand Unified Bootloader). Lorsque l’on installe Windows, le programme d’installation installe toujours le chargeur d’amorçage de Windows (BOOTMGR). Dans les deux cas, le chargeur d’amorçage de premier niveau est copié dans les 446 premiers octets du MBR.
On entrevoit ici le principal problème du MBR. Le chargeur d’amorçage qu’il contient est celui du dernier système d’exploitation installé. Si l’on installe Linux après Windows, cela ne pose généralement pas de problème, car GRUB, comme son nom l’indique permet de charger, d’une manière ou d’une autre, à peu près n’importe quel système d’exploitation et si plusieurs systèmes d’exploitation sont présents au moment de l’installation, GRUB propose un menu de sélection au démarrage. La situation est plus problématique si l’on installe Windows après Linux. Non pas que BOOTMGR soit incapable de charger d’autres systèmes, mais le fait est que, par défaut, il ne charge que le ou les systèmes Windows et sa configuration n’est pas simple.
Même si le remplacement du chargeur d’amorçage du MBR ne change rien aux systèmes installés ni à la table de partition et qu’il est donc relativement facile de restaurer le chargeur d’amorçage manuellement ou à l’aide d’un utilitaire, c’est un problème courant et sa résolution demande plus de compétence qu’en ont la majorité des utilisateurs. La prochaine section décrit la solution apportée à ce problème par les firmwares de type UEFI.
Processus d’amorçage d’un ordinateur avec UEFI
Les firmwares de type BIOS souffrent d’un certain nombre de limitations. En voici une liste non exhaustive :
- Le firme n’a pas de support pour un système de fichier.
- La routine d’amorçage du MBR est spécifique au système d’exploitation.
- L’installation d’un deuxième système d’exploitation écrase la routine d’amorçage du précédent.
- Le disque d’amorçage doit obligatoirement être l’un des deux premiers disques (disque optique compris).
- Pour les systèmes d’exploitation de Microsoft, la partition système doit se trouver dans 8 premiers Gio du disque.
Ces limitations ont conduit Intel à développer un nouveau firmware pour son processeur Itanium au milieu des années 1990. Le processeur fut un terrible échec et il a aujourd’hui pratiquement disparu, mais le firmware est resté et équipe actuellement la majorité des ordinateurs personnels et des serveurs.
Un firmware de type UEFI est un système d’exploitation minimal capable de charger un exécutable au format PE (Portable Executable format), de monter un système de fichier (au minimum, la variante de FAT32 imposée par le standard) depuis n’importe quel disque, d’utiliser la carte graphique en haute résolution et d’utiliser une carte réseau. Ces fonctionnalités facilitent la réalisation du programme de setup qui est généralement bien plus élaboré et sophistiqué que celui des firmwares de type BIOS et la réalisation des chargeurs d’amorçage pour les système d’exploitation.
Durant l’amorçage, l’UEFI n’utilise plus le MBR ni l’ordre d’amorçage du BIOS. Par défaut, il parcourt tous les disques du système à la recherche une partition système EFI (EFI System Partition ou ESP) et lorsqu’il en trouve une, il la monte et lance le fichier bootx64.efi qui se trouve dans le répertoire \EFI\Boot\. Ce fichier est le chargeur d’amorçage par défaut. L’extension .efi est l’extension des exécutable pour l’UEFI, comme l’extension .exe pour Windows, et x64 est l’architecture du processeur.
Chaque système d’exploitation installe un ou plusieurs chargeurs d’amorçage dans son propre répertoire à l’intérieur de la partition EFI. Par exemple, pour Windows, le chargeur d’amorçage est le programme \EFI\Microsoft\Boot\bootmgfw.efi. Pour Ubuntu, il s’agit plutôt de \EFI\ubuntu\grubx64.efi Le fichier \EFI\Boot\bootx64.efi est une copie de l’un de ces chargeurs d’amorçage (Windows copie le sien dans tous les cas alors que Linux ne le copie que s’il est le premier).
Si on le souhaite, on peut remplacer le chargeur d’amorçage par défaut par n’importe quel autre chargeur d’amorçage. On peut le faire avec le shell UEFI ou en chargeant un système d’exploitation capable de monter une partition FAT32 et en utilisant un interpréteur pour remplacer le fichier.
Enfin, setup de l’UEFI permet généralement de configurer un menu pour choisir n’importe quel chargeur d’amorçage dans n’importe quelle ESP. Il est probable que l’UEFI remplacera, à terme, le menu de GRUB puisqu’il remplit les même fonctions.