Bootloaders lanceren 🚀

-

Vandaag wil ik je meenemen naar een andere wereld. Een wereld met ontelbaar veel patch notes, email lists en forum posts vanuit de tijd dat ik nog op de basisschool zat. Ik heb het natuurlijk over de wereld van device startups. Laten we ons vergrootglas opzetten en kijken wat er gebeurt tussen ‘power-on’ en het starten van de applicatie. Onderweg leren we vast het een en ander over updates. Daar gaan we dan! 3.. 2.. 1.. *Klik*  
De stroom begint te lopen en opeens is daar NBoot. NBoot is een soort bootloader loader. Als er een bootloader aanwezig is in het flashgeheugen, dan start NBoot die. In ons geval UBoot. UBoot is een bootloader. Waarschijnlijk heb je wel eens met een bootloader gestoeid toen je Linux wilde dual booten. De bootloader op jouw computer werkt met configuratiebestanden die beschrijven op welke disks, partities of UEFI files het operating system staat. Vervolgens zoekt die de fysieke apparaten en start de kernel je OS.  
UBoot doet dat allemaal niet. UBoot biedt een hele simpele interface voor het starten van het operating system: het ‘boot’ commando. Dat commando vereist 2 argumenten: het memory address van de kernel en het memory address van de device tree. UBoot biedt ook een scripting taal (hush, vergelijkbaar met bash) en commando’s voor het lezen van bestanden vanuit disk. UBoot kan zelfs netwerk connecties maken en een sd kaart uitlezen. Het is aan de gebruiker om scripts te maken die de kernel en device tree in het ram geheugen te zetten.  
Dus, UBoot start en het eerste wat die doet is overal zoeken naar een ‘install.scr’ script (.scr is een gecompileerd hush script). Als het installatie-sd kaartje is ingeplugd wordt dat script gevonden en worden de MTB partities en UBIFS aangemaakt en het filesystem geflasht. Wat zijn dat dan weer?! 

Memory Technology Device (MTD) partities is een logische opdeling van flash geheugen in blokken zodat je bootloader en OS weten waar wat staat. Het grote verschil tussen MTD en de partities die jij gewend bent (zoals gpt) is dat bij MTD partities de partitietabel beheerd wordt door de bootloader. Er is dus niet, zoals bij GPT, een gestandaardiseerde plek op disk waar beschreven staat welke partities er zijn. In ons geval beheert UBoot de variabele mtdparts. Dit ziet er zo uit: 

Dit is de volledige beschrijving van de partitietabel! Dit wordt via kernel parameters meegegeven aan de kernel zodat ze die weet welke partities er zijn. 
Binnen die MTD partities kunnen we weer filesystems neerzetten. Voor de OS partitie (waar de kernel, device tree en root filesystem op staan) gebruiken we een UBI filesystem. Unsorted Block Image (UBI) is een filesystem layer voor flash memory. Het verspreidt writes zodat het geheugen langer meegaat en kan zo kapotte blokken omzeilen. Binnen een UBI filesystem (UBIFS) kun je weer volumes maken die door de kernel als partities worden gezien. Die volumes staan fysiek gezien niet naast elkaar, maar zijn juist verspreid voor een langere levensduur van het flash geheugen. Wat UBI doet qua verspreiding van volumes is te vergelijken met LVM. 

Terug naar ons apparaat. UBoot start het boot script. Het script maakt gebruik van een aantal variabelen uit een aparte MTD partitie. Daaruit kan het script een keuze maken om operating system A of B te starten. Dat gaat ongeveer zo: 

Waarom zijn er twee operating systems? Omdat we atomic system updates willen doen! Het kan natuurlijk niet zo zijn dat we een nieuwe versie van het operating system aan het installeren zijn, de stroom uitvalt en het apparaat onbruikbaar is. Dus staan er, net als bij Android, twee volledige installaties naast elkaar. Elke installatie in een eigen UBI partitie. Tijdens een update wordt het nieuwe OS weggeschreven naar het inactieve slot. Vervolgens wordt het inactieve slot actief gemaakt en een reboot ingezet. Welkom in je nieuw OS.  

Dus, dit script wordt uitgevoerd, de kernel en device tree wordt uit de juiste UBI partitie geladen in memory. Vanaf daar neemt UBoot het weer over en wordt de kernel daadwerkelijk gestart. Die kernel heeft de device tree nodig. Wat is een device tree? Nou, als jouw computer zichzelf opstart dan wordt de hardware automatisch gedetecteerd. Dat zorgt ervoor dat je je CPU of videokaart kan vervangen zonder je kernel opnieuw te compileren. Bij maatwerk hardware zoals het apparaat waar ik nu mee werk doen deze protocollen het niet. Dus moeten we beschrijven welke hardware er is. Zo’n beschrijving staat in de device tree en ziet er ongeveer zo uit: 


Zo weet de kernel hoe met de hardware te communiceren.  Maar hoe weet UBoot of NBoot hoe met die hardware te communiceren? Standaard kunnen ze dat ook niet. Daarom zijn deze twee speciaal gecomponeerd voor onze specifieke hardware configuratie, speciaal door de leverancier.  

We zijn er bijna! Het rootfs met alle tooling wordt gemount. Welkom in userspace! Init.d start, de netwerkadapters worden geïnitialiseerd en tot slot wordt een laatste applicatie-startup script gedraaid. Dit script zoekt naar de juiste applicatie-versie om te starten. De applicatie kan namelijk onafhankelijk van het OS geüpdatet worden. Ook deze updates zijn weer atomic, dus kunnen er op een systeem meerdere versies van de applicatie staan. Het script start standaard de oudst beschikbare versie. Alleen als een applicatie zichzelf afsluit en daarbij aangeeft dat de nieuwe versie gestart moet worden, zal het script een nieuwere versie starten. Zo zijn applicatie rollbacks gegarandeerd. Mooi he 😉 

En dan is het zo ver. De applicatie wordt gelanceerd.