2 points par GN⁺ 2025-10-26 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Explication technique, étape par étape, du processus qui mène du moment où l’on appuie sur le bouton d’alimentation jusqu’à l’exécution du noyau Linux
  • Présente en détail la manière dont le CPU démarre en real mode, puis passe en protected mode et en long mode
  • Décrit en détail le rôle et le fonctionnement de chaque étape, notamment le firmware BIOS/UEFI, le bootloader (GRUB), ainsi que la décompression du noyau et la relocalisation des adresses
  • Explique, à l’aide d’exemples concis, les concepts essentiels à l’initialisation du noyau, comme le memory mapping, les interruptions, les page tables et le kASLR
  • En comprenant les mécanismes internes du démarrage de Linux, apporte un éclairage sur l’architecture système, la sécurité et l’optimisation des performances

Part 1 — Du bouton d’alimentation à la première exécution du noyau

  • Lorsqu’on appuie sur le bouton d’alimentation, le CPU est réinitialisé en real mode et exécute ses premières instructions

    • Le real mode est un schéma d’adressage simple hérité de l’époque du 8086, qui calcule les adresses physiques en combinant un segment et un offset
    • Exemple : physical_address = (segment << 4) + offset
    • Après le reset, le CPU saute à l’adresse 0xFFFFFFF0 (vecteur de reset) et transmet le contrôle au firmware
  • Les registres sont des emplacements de stockage ultra-rapides à l’intérieur du CPU, comme CS (code segment) ou IP (instruction pointer)

    • CS indique l’emplacement du code courant, IP pointe vers la prochaine instruction à exécuter

BIOS et UEFI

  • Le BIOS est un ancien firmware qui, après le POST (autotest à la mise sous tension), vérifie l’ordre de démarrage et recherche un disque amorçable
    • Un disque amorçable est identifié par 0x55AA à la fin de son premier secteur de 512 octets
    • Le BIOS copie ce secteur à l’adresse 0x7C00, puis y saute pour l’exécuter
  • UEFI est l’alternative moderne, capable de comprendre directement les systèmes de fichiers et de charger des programmes de démarrage plus volumineux
    • Contrairement au BIOS, il n’est pas limité par la contrainte du « premier secteur » et transmet au système d’exploitation des informations système plus riches

Bootloader

  • Le bootloader est le programme chargé de placer le noyau en mémoire et de préparer son exécution
    • GRUB est l’implémentation la plus courante : il lit son fichier de configuration et charge en mémoire le noyau ainsi que l’initrd
    • Le fichier du noyau se compose d’un petit programme de configuration pour le real mode et du corps principal du noyau compressé
    • GRUB inscrit dans la structure setup header des informations comme l’emplacement du noyau, la ligne de commande ou la position de l’initrd, puis saute vers le code de configuration du noyau

Programme de configuration (setup code)

  • Avant l’exécution du noyau, il sert à créer un espace de travail prévisible
    • Il aligne les registres de segment (CS, DS, SS) et efface le direction flag afin que les copies mémoire se comportent de manière cohérente
    • Il crée une stack pour stocker les données temporaires lors des appels de fonctions
    • Il initialise à zéro la zone BSS (espace des variables globales devant commencer avec la valeur 0)
  • Si l’option earlyprintk est présente, il peut configurer le port série afin d’afficher les premiers messages de debug
  • Il interroge le firmware pour obtenir la carte RAM (e820), afin d’identifier les zones mémoire disponibles et réservées
  • Une fois tout prêt, il appelle main, la première fonction C, puis entre dans l’étape de changement de mode

Interruptions

  • Une interruption est un mécanisme par lequel le CPU suspend brièvement son travail courant pour traiter un événement urgent
    • Les entrées clavier ou les timers sont des exemples typiques d’événements matériels
    • Les interruptions masquables peuvent être bloquées temporairement, tandis que les NMI (Non-Maskable Interrupt) sont toujours traitées
    • Pendant les changements de mode, elles sont temporairement bloquées pour éviter toute interruption inattendue

Part 2 — Du real mode au 32 bits, puis au 64 bits

  • Linux moderne fonctionne en long mode sur l’architecture x86_64
    • Une transition progressive est nécessaire : real mode → protected mode → long mode

Protected mode

  • Il s’agit du mode 32 bits introduit pour dépasser les limites des années 1980, avec deux structures clés
    • GDT (Global Descriptor Table) : définit l’adresse de départ, la taille et les droits des segments
      • Linux utilise un flat model, qui simplifie l’ensemble de l’espace 32 bits en une seule zone continue
    • IDT (Interrupt Descriptor Table) : stocke l’adresse des gestionnaires à appeler lors d’une interruption
      • Pendant le démarrage, seule une IDT minimale est chargée ; l’IDT complète est installée après l’initialisation du noyau

Processus de changement de mode

  • Le code de configuration commence par désactiver les interruptions, arrêter la puce PIC, activer la ligne A20 et initialiser le coprocesseur mathématique
    • La ligne A20 est un mécanisme historique destiné à résoudre le problème de wraparound des adresses à 1 Mo
  • Après avoir chargé une GDT et une IDT minimales, il active le bit PE du registre CR0 puis effectue un far jump
    • Cela permet l’entrée en protected mode, puis la reconfiguration des segments et du pointeur de pile selon le nouveau schéma d’adressage

Registres de contrôle

  • CR0 : active le protected mode
  • CR3 : stocke l’adresse de plus haut niveau des page tables
  • CR4 : active les fonctionnalités étendues comme le PAE

Préparation de l’entrée en long mode

  • Deux conditions sont nécessaires pour passer en mode 64 bits
    • activation du paging : il réalise la correspondance entre adresses virtuelles et physiques
    • activation du bit LME (Long Mode Enable) du registre EFER
  • Les page tables font la correspondance par pages de 4 Ko ; au début du boot, elles sont configurées simplement sous forme d’identity map en blocs de 2 Mo

Procédure d’activation du paging

  • La fonctionnalité PAE est activée dans CR4 et une table de pages minimale couvrant les basses adresses en blocs de 2 Mo est créée
  • L’adresse de la table de plus haut niveau est écrite dans CR3, puis le paging est activé
  • Le bit LME d’EFER est positionné et le CPU saute vers du code 64 bits pour entrer en long mode
  • Les adresses et registres sont alors étendus sur 64 bits, et tout est prêt pour l’exécution du noyau

Part 3 — Décompression du noyau, correction des adresses et auto-déplacement

  • Le CPU est désormais en mode 64 bits, et une image compressée du noyau est présente en mémoire
    • Un petit stub 64 bits se charge de décompresser le noyau et d’ajuster ses adresses

Nettoyage initial et mise en place des garde-fous

  • Le stub calcule sa position réelle d’exécution et, si le noyau risque de se chevaucher, se déplace vers une zone sûre via self-relocation
  • Il initialise sa propre zone BSS et charge une IDT simple (incluant les handlers de page fault et de NMI)
    • En cas de page fault, il ajoute immédiatement le mapping manquant pour récupérer l’exécution
  • Il crée les identity mappings nécessaires pour le noyau, les paramètres de boot, le buffer de ligne de commande et les autres zones indispensables

Décompression du noyau

  • La fonction extract_kernel est exécutée pour décompresser le noyau
    • Elle prend en charge plusieurs algorithmes de compression, comme gzip, xz, zstd ou lzo
    • Après décompression, elle lit l’en-tête ELF afin de copier les sections de code et de données aux bonnes adresses
  • Si l’adresse à laquelle le noyau a été compilé diffère de son adresse réelle de chargement, une relocalisation est effectuée
    • Les instructions ou pointeurs contenant des adresses sont modifiés pour correspondre à la position mémoire réelle
  • Une fois tout prêt, l’exécution saute vers la fonction start_kernel, marquant le début de l’initialisation complète du noyau

Auto-déplacement du noyau avec kASLR

  • Le kASLR (Kernel Address Space Layout Randomization) randomise les adresses physiques et virtuelles du noyau afin de compliquer les attaques
    • Deux bases sont choisies aléatoirement au démarrage
      • base physique : l’adresse RAM où le noyau sera réellement placé
      • base virtuelle : le point de départ des adresses virtuelles utilisées par le noyau
  • Processus de sélection
    • Une liste des zones à protéger est établie : bootloader, initrd, buffer de ligne de commande, etc.
    • La carte mémoire fournie par le firmware est parcourue pour trouver une zone libre suffisamment grande
    • Un emplacement aléatoire est choisi à partir de l’entropie obtenue, par exemple, via des instructions matérielles de génération aléatoire
  • Si aucune zone appropriée n’est disponible, le système revient à l’adresse par défaut ; l’option nokaslr désactive cette randomisation

Résumé des termes

  • Hexadecimal (hexadécimal) : indiqué par le préfixe 0x, pratique pour représenter la structure des bits et les alignements matériels
  • Register : stockage temporaire interne au CPU (CS, DS, SS, IP, SP, etc.)
  • Segment/Offset : méthode de calcul d’adresse en real mode (segment * 16 + offset)
  • BIOS/UEFI : firmware chargé de l’initialisation du système et du chargement du programme de démarrage
  • Bootloader (GRUB) : charge le noyau et transmet les informations système
  • Stack/BSS : stockage temporaire des fonctions et zone des variables globales initialisées à zéro
  • Interrupt/NMI : mécanisme de traitement des événements matériels et logiciels
  • GDT/IDT : tables de définition des segments et des interruptions
  • A20 Line : commutateur empêchant le wraparound des adresses à 1 Mo
  • Protected Mode/Long Mode : modes d’exécution 32 bits et 64 bits
  • Paging/Page Tables : correspondance entre adresses virtuelles et adresses physiques

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.