Bootstrapper 1: Multibootperikelen

Baron Munchausen wist zichzelf aan de lusjes van zijn laarzen uit het moeras te trekken. De computerterm 'bootstrappen' verwijst daarnaar, omdat het daarbij ook gaat om een systeem dat zichzelf eigenlijk nodig heeft om op te starten. In deze reeks gaan we in op een paar typische probleemsituaties rond het bootstrappen, alsmede praktisch oplossingen daarvoor. We trappen af met de perikelen van een multibootsysteem.
| posted on Mon, 01 Mar 2004, 00:00 | weblog | rss | spin-off | comments |
Translate to English Translate to English

Een computer die meerdere operating systemen kan booten wordt steeds vaker gebruikt, zowel thuis als zakelijk. Meestal gaat het dan om het naast elkaar draaien van Windows en Linux. Er zijn dan bootloaders nodig die een keuze bieden bij het opstarten, maar daar kan aardig wat mee mislopen.

Werking van een bootloader

Een systeem dat je net hebt aangezet vindt in eerste instantie een BIOS met rudimentaire code voor tekstafdrukken en toetsenbordinvoer, plus ondersteuning om een blok data van een harde schijf te lezen. Het aanspreken van files is al te hoog gegrepen, laat staan grafische interfaces. Hoe start je daarmee een systeem op?

De BIOS slingert dit aan door een blok met programmacode in te lezen van de harde schijf. In eerste instantie probeert hij daartoe de Master Boot Record of MBR, wat het allereerste blok van 512 bytes op het eerste bootdevice is. Als de MBR geen bootcode bevat maar wel een partitietabel, dan wordt de aktieve (of bootbare) partitie opgezocht, en wordt daar het eerste blok van ingelezen in de hoop daar bootcode in te vinden.

Er is maar heel weinig ruimte voor bootcode in zo'n eerste diskblok -- minder dan 512 bytes zelfs. Het enige wat die code daarom doet is het inlezen van een aantal andere blokken van de schijf en die code kan dus al wat uitgebreider zijn en de rest van het systeem verder de lucht in helpen, bijvoorbeeld door een Linux kernel te laden of Windows op te starten. Maar als daar reden toe is, krijg je eerst nog een keuzemogelijkheid.

Het opstarten van een systeem is dus een soort drietrapsraket. De eerste trap komt uit het initiële diskblok, de tweede trap is een wat groter programma op een vaste positie op de schijf. De derde trap kan al veel dynamischer zijn, zonodig afhankelijk van een menukeuze door de gebruiker.

Ketens van bootloaders

Bootloaders zijn in staat elkaar op te starten om zo over te schakelen op een andere set bootmogelijkheden. Dat lijkt wat zot, maar het maakt het bijvoorbeeld mogelijk om vanaf een harde schijf een expliciete "boot van floppy" mogelijkheid op te nemen, bijvoorbeeld beveiligd met een password. Dit overschakelen op een andere bootloader heet chainloaden.

Bij de installatie van een operating systeem krijg je uiteraard de optie om het te laten booten. Soms gaat de bootcode automatisch naar de MBR, soms kun je er ook voor kiezen het in het begin van de partitie van het operating systeem te plaatsen. Dit eerste blok is nuttig als aangrijpingspunt voor chainloading. Onder Knoppix Linux kun je een kopie van de MBR naar een file maken met

dd if=/dev/hda of=/mbr bs=512 count=1

Om het bootblok van bijvoorbeeld partitie 3 op te halen, gebruik je

dd if=/dev/hda3 of=/part3br bs=512 count=1

Knoppix is uitermate geschikt voor dit soort akties, omdat het erg simpel is om daarmee een Linux-omgeving op te roepen die niet afhankelijk is van de harde schijf, omdat hij van CDROM en ramdisk werkt. Het meest comfortabel is het bootcommando knoppix single, omdat daarmee meteen een root prompt beschikbaar komt. Knoppix is te downloaden vanaf knoppix.net.

Flexiboot

Windows NT en latere systemen gebruiken een bootloader die Flexiboot heet. Hoewel niet ondersteund vanuit de installer is Linux ook met Flexiboot te booten, door gebruik te maken van chainloading. Je moet daarvoor de MBR van een Linux bootloader als bestand op je C: partitie plaatsen, bijvoorbeeld in C:LINUX.BIN. Aan de (verborgen) file C:BOOT.INI voeg je dan een regel toe als

C:\LINUX.BIN="Linux"

De tekst tussen quotes wordt afgebeeld in een keuzemenu dat wordt getoond als er meerdere systemen in BOOT.INI staan. Let even op als je BOOT.INI aanpast -- de notatie voor de drive en partitie varieert tussen varianten van Windows. Ook zijn er verschillen tussen versies van Flexiboot -- sommige willen per sé een directorystructuur met een bepaalde executable terugvinden.

Al met al is Flexiboot erg op Windows gericht. Bij de installatie wordt alleen gescand op Windows bootopties, en de bootinformatie moet in een Windows filesysteem worden opgeslagen. Diverse 'open' varianten zijn populairder omdat ze deze beperkingen niet kennen.

LILO: de Linux Loader

LILO is van de twee populaire multibootloaders de oudste. Het wordt geconfigureerd in een file zoals /etc/lilo.conf die vanuit Linux bereikbaar is. In deze file worden zoveel bootopties beschreven als gewenst. Een voorbeeld van zo'n configuratiefile is

prompt
message=/boot/message

other=/dev/hda1
  label=windows
  table=/dev/hda

image=/boot/vmlinuz
  label=linux
  root=/dev/hda2
  read-only

De eerste paar declaraties zijn globaal; ze leggen vast dat er een prompt moet worden afgebeeld en welke boodschap daarbij moet worden geprint. Dan volgen twee alternatieve bootopties.

De other bootoptie is een chainloader. Die leest de nieuwe eerstetraps bootloader in van het eerste blok van Linux-device hda1, ofwel van partitie 1 van de master drive op de eerste IDE interface. De table-optie geeft de van toepassing zijnde partitietabel voor de doorstart, die plaatsvindt door de labelnaam windows in te tikken achter de prompt wordt deze bootoptie gekozen.

De image bootoptie is een daadwerkelijke linuxload-optie. Er wordt een kernel ingeladen vanaf de file /boot/vmlinuz als achter de LILO-prompt linux wordt ingetikt. De root partitie /dev/hda2 (partitie 2 op de eerste IDE drive) wordt aan de kernel doorgegeven om / van te mounten.

Probleem is nu wel dat we een configuratiefile hebben die ook nog eens op andere files berust. Door de beperkte BIOS-functionaliteit tijdens het booten kan een bootloader daar natuurlijk niets mee. LILO lost dat op door de configuratie om te zetten in bloknummers die ten tijde van boot moeten worden ingelezen. Dat gebeurt met het commando

/sbin/lilo

Hierna is het zoeken in het filesysteem alvast voorgekookt en kan de bootloader zonder die ballast toch de gewenste keuzemogelijkheid bieden. LILO heeft dus helemaal niets meer te maken met filesystemen.

Problemen met LILO

Een probleem met LILO's gefixeerde bloknummers is dat elke wijziging aan het bootsysteem een nieuwe aanroep van /sbin/lilo nodig maakt. Dus wanneer de inhoud van /boot/message is aangepast, of als een nieuwe kernel is geïnstalleerd. Op zich geen probleem, maar het wordt weleens vergeten, en dan probeert LILO nog steeds de eerder bepaalde blokken in te lezen, en daardoor loopt het bootproces doorgaans vast.

Bij het opstarten drukt LILO de letters van zijn naam af; komt hij niet helemaal tot de vierde letter dan is er ergens tijdens de bootstrap iets misgegaan. Ook als een eindeloze herhaling van een foutcode optreedt is er iets misgegaan. Dit is simpel op te lossen vanaf de root prompt onder bijvoorbeeld Knoppix. Wat je doet is /sbin/lilo draaien in een geschikte omgeving:

mkdir -p /tux
mount /dev/hda2 /tux
chroot /tux
mount -a
/sbin/lilo
umount -a
exit
umount /tux

We voeren hier een mount -a uit om te zorgen dat alle files die LILO nodig heeft bereikbaar zijn -- mogelijk staat alles echter op de root partitie, dan is dit commando niet nodig. De chroot is echter wel van belang, want behalve dat we dan niet expliciet hoeven aan te geven waar de configuratiefile staat is het ook nodig omdat de filenamen in de configuratiefile relatief zijn aan de partitie die hij als root partitie verwacht terwijl de blokken worden berekend bij de filenamen in de configuratiefile.

GRUB: GRand en Unified Bootloader

De wat hoogdravende naam GRUB slaat erop dat hij weliswaar nog alleen op PC's draait, maar dat specifieke rariteiten zoals extended partities met andere partities erin worden verborgen.

GRUB snapt voldoende van filesystemen om bestandsnamen te kunnen interpreteren. Die kennis is opgenomen in de tweede trap en dat helpt om kernels te vinden op een partitie. Als een nieuwe kernel de plaats inneemt van de oude dan worden de diskblokken automatisch gevonden in het filesysteem. Het opslaan van gefixeerde diskbloknummers is alleen nodig voor de tweede trap van GRUB.

De tweede trap van GRUB is een stuk ingewikkelder dan die van LILO, en dat houdt in dat hij veel groter is. Niet echt een probleem, maar het betekent wel dat er een substantiële hoeveelheid code op een vaste plaats moet worden ingelezen in het filesysteem.

Een alternatieve opslagplek in de meeste partities is in de allereerste track, waarin ook de bootloader huist. De bootloader neemt echter maar een enkele sector in beslag, en omdat een moderne BIOS de diskparameters transformeert tot 63 sectoren per track, zijn er dus 62*512 bytes, dus 31 kB beschikbaar waar de tweede trap een echt vaste plaats zou kunnen innemen. Dit is overigens ook een plek waar bootvirussen zich weleens nestelen, maar nu ten goede ingezet.

Omdat de tweede trap van GRUB met al haar kennis van filesystemen te groot is, kan er een 'anderhalfste' trap worden geïnstalleerd op dat deel van de partitie. Dit is een trap die slechts een enkel filesysteem begrijpt en die van zo'n filesysteem de tweede trap inlaadt.

De tweede trap toont een bootmenu, maar omdat het systeem een stuk intelligenter is dan LILO is het zelfs mogelijk om de bootopties te editen alvorens ze te proberen te booten. Dat is ideaal in gevallen waar een bootoptie fout ingevoerd was, of waarin geëxperimenteerd moet worden om te ontdekken hoe-het-ook-alweer-zat. Het gaat echter weer niet zo ver dat op de harde schijf kan worden rondgebrowst om een filenaam op te zoeken.

De configuratie van GRUB abstraheert van technologie-specifieke rariteiten. Zo is er gekozen om alles vanaf 0 te nummeren, zodat partitie 2 nu nummer 1 krijgt. Verder is de notatie van drives en partities anders, de floppy is (fd0), de eerste IDE drive is (hd0) en de tweede partitie op de eerste IDE drive is (hd0,1). GRUB gebruikt een zogenaamde rootdrive of -partitie als de basis waarvandaan filenamen worden opgezocht die geen expliciete drive voor de padnaam hebben staan.

Qua structuur is de configuratiefile net als bij LILO opgebouwd uit algemene definities gevolgd door een aantal met title beginnende bootopties. Bovenstaande LILO-configuratie is om te zetten in de volgende configuratie voor GRUB:

default 0

title windows
rootnoverify (hd0,0)
chainloader +1
boot

title linux
root (hd0,1)
kernel /boot/vmlinuz root=/dev/hda2
boot

We zien hier dat booten van de eerste partitie weer wordt overgelaten aan Windows' eigen bootloader, door van de root partitie (die niet wordt geverifieerd wegens afwijkingen van standaards) het eerste blok in te lezen, en daarvan vervolgens te booten. Deze bootoptie heeft volgnummer 0 en is de default.

De tweede bootoptie is in het bootmenu te selecteren. Hij leest de kernel in uit de file /boot/vmlinuz op hda2, of in GRUB-termen van (hd0,1). Daarbij wordt meteen ook een argument root=/dev/hda2 meegegeven aan de kernel. Het boot-commando start de ingeladen kernel vervolgens op.

Het installeren van GRUB is het enige moment waarop bloknummers op de harde schijf moeten worden bepaald en gefixeerd. Dat kan door GRUB op te starten op een werkend systeem. Het zal niemand verbazen dat ook dit weer mogelijk is vanaf Knoppix.

Tijdens de installatie wordt de eerste trap uit een file stage1 gekopieerd naar de bootsector, met een daarin een verwijzing naar de tweede trap die meestal in een file genaamd stage2 te vinden is. Plaats die file liefst op dezelfde partitie als die waarop stage1 is ondergebracht, bijvoorbeeld in /boot/grub.conf. Neem tenslotte de configuratie bovendien op in een file /etc/grub.

Om GRUB dus op de tweede partitie van de eerste IDE drive te installeren tikken we onder Knoppix het volgende:

mkdir -p /tux
mount /dev/hda2 /tux
mkdir -p /tux/boot/grub
cp /usr/lib/grub/i386-pc/stage* /tux/boot/grub
umount /tux
grub

Vervolgens wordt GRUB gestart en kunnen we tot installatie overgaan. Dat kan nu simpelweg door /dev/hda2 als root drive aan te melden en de files daarop te noemen:

root (hd0,1)
install /boot/grub/stage1 (hd0) /boot/grub/stage2 /etc/grub.conf
quit

Dankzij de (hd0) wordt de stage1 in de MBR geïnstalleerd. Hierna is GRUB gebruiksklaar, en hoeft behoudens upgrades van GRUB zelf niets meer geïnstalleerd te worden. Ook niet als de kernel wordt aangepast, of de configuratiefile /etc/grub.conf. Een stuk dynamischer dus dan LILO.

Problemen oplossen met GRUB

Doordat GRUB zo flexibel is, is het goed mogelijk om er een systeem mee te bootstrappen als bijvoorbeeld de bootcode in de MBR beschadigd of overschreven is. Dat kan al met een simpele bootfloppy. In tegenstelling tot Knoppix, waarmee meteen een heel operating systeem wordt ingeladen, kun je daarmee een systeem werkelijk vanaf het meest prille stadium van de grond tillen.

Op zo'n bootfloppy hoef je alleen de GRUB bootloader te installeren. Daarbij zullen we de diverse bestanden in de root directory plaatsen voor eenvoudiger gebruik. Vanuit Knoppix maken we zo'n floppy als volgt:

mkfs.msdos /dev/fd0
mcopy /usr/lib/grub/i386-pc/stage* A:
grub

Waarna de volgende commando's in GRUB de installatie completeren:

root (fd0)
install /stage1 (fd0) /stage2 /grub.conf
quit

Hiermee is een heel kleine flop gemaakt, met maar 150 kB aan files, waarmee van alle opties in grub.conf te booten valt. De floppy is hier opgebouwd als MSDOS floppy, maar het kan evengoed BSD FFS of Linux ext2 bevatten. Zoland de file grub.conf er maar wel op wordt gezet, want die wordt verwacht door de bootloader. Doordat die file met kennis van het filesysteem wordt ingelezen kan die op een andere computer worden ingegeven. In de configuratiefile kunnen zelfs verwijzen voorkomen naar de harde schijf in een vastlopend systeem, dankzij de handigheid van het omzeilen van diskbloknummers!

Systeembeheer op afstand

Wie een systeem op afstand beheert, is doorgaans niet in de gelegenheid om een nieuwe kernel of rootpartitie helemaal zonder risiko te testen. Het is vaak wel mogelijk om een resetknop in te laten drukken en met dat in het achterhoofd is het handig als je een bootloader kunt verzoeken om bij de volgende reboot eenmalig een te testen bootoptie te kiezen.

Onder LILO is hiervoor de optie -R in het leven geroepen; onder GRUB is zoiets momenteel nog niet beschikbaar, maar er zijn ontwikkelingen in die richting gaande, in de vorm van opties --once en --expires. Een kwestie van tijd dus.

Aan de lusjes uit de drek

Het opstarten van een systeem is, met name op het kunstmatig in leven gehouden PC-ontwerp, een ingewikkelde zaak. Bootloaders kunnen helpen om dit alles in goede banen te leiden, en met name GRUB bleek een bijzonder flexibel systeem. Volgende keer bespreken we een ander probleem dat typisch samenhangt met het bootstrappen van een systeem, namelijk verminkte partitietabellen.

Verantwoording

Deze reeks is gedurende 2004 verschenen in NetOpus. De reeks staat als geheel onder http://rick.vanrein.org/blog/netopus/bootstrapper

Translate to English Translate to English

Comments on this article