Differences

This shows you the differences between two versions of the page.

Link to this comparison view

si:laboratoare:05 [2023/11/04 22:53]
florin.stancu
— (current)
Line 1: Line 1:
-====== Laboratorul 05. The embedded boot process ====== 
- 
-Atunci când un microprocesor primește semnalul de reset (prin alimentare, transmiterea unor comenzi / întreruperi interne etc.), acesta începe să booteze. 
-Toate sistemele moderne folosesc un proces de boot multi-stagiu,​ însă primul program este mereu încărcat dintr-un ROM (Read Only Memory) ce este, de cele mai multe ori, integrată în același chip. 
- 
-==== Procesul de boot standard ARM ==== 
- 
-Arhitectura ARMv8 propune următoarea structură: 
- 
-{{:​si:​laboratoare:​arm_booting_process.png?​700}} 
- 
-Se observă numărul mare de pași pe care sistemul trebuie să-i realizeze până să pornească kernelul sistemului de operare. 
-Acest lucru se datorează punerii accentului pe securitatea soluțiilor incorporate (prin tehnologia ARM TrustZone, de care doar vom menționa scurt, pe Raspberry PI nefiind implementată în totalitate). 
- 
-Așadar, prima dată se începe prin rularea primului stagiu, ''​BL1'',​ stocat în memoria ROM. Acesta va încărca, de pe o memorie flash externă (de obicei, prin SPI: eMMC sau SD), următoarele stagii. Dintre acestea, ''​BL2''​ este, de obicei, un firmware foarte mic (//​10-100KB//​) ce este încărcat în memoria SRAM a SOC-ului (care, uneori, funcționează și pe post de cache) și care, mai departe, inițializează toate perifericele chip-ului (printre care, foare important este DRAM-ul -- folosit de următoarele stagii cu consum ridicat de RAM!). 
- 
-Stagiile ''​BL3x''​ devin opționale, totul depinzând dacă ''​BL2''​ conține funcționalitatea de a porni și Kernel-ul de Linux și este suficient de configurabil pentru a putea acoperi o mare parte a cazurilor de utilizare ale sistemului (ceea ce este adevărat pentru RPI, însă pentru altele nu prea) sau dezvoltatorul software dorește să beneficieze de funcționalități avansate de boot (aici, ''​BL31''​ poate oferi partiționare A/B cu toleranță la defecte, actualizări la distanță etc.) sau încărcarea unui sistem de operare securizat (Trusted OS, la pasul ''​BL32''​). 
- 
-În final, ultimul pas al bootloaderului va fi să citească configurația (de pe o partiție de boot sau dintr-o altă memorie ROM re-programabilă) și componentele de rulare (kernel, initrd, device tree blob -- de cele mai multe ori, toate 3 fiind necesare) ale sistemului de opreare, și să încarce în RAM și să paseze execuția CPU-ului către kernel. 
- 
-==== Procesul de boot al Raspberry PI ==== 
- 
-Deși procesul de boot diferă între //Raspberry PI// versiuni mai vechi sau egale cu **3** (ce folosesc SoC-ul ''​BCM2837''​) și cele **după 4** (cu ''​BCM2711'',​ și, pe viitor, v5), stagiile pot fi încadrate în arhitectura propusă de ARM. 
- 
-Toate versiunile procesoarelor Broadcom folosite la RPI încep prin rularea stagiului ''​BL1''​ pe microcontrollerul de gestionare a GPU-ul integrat (da, pe bune!), care, mai departe, inițializează memoria cache, interfața SPI (pentru accesarea memoriei flash din eMMC / card SD) și, folosind o bibliotecă incorporată de citire de pe partiții FAT32, scanează după existența firmware-ului pentru următorul stagiu, ''​BL2'',​ pe care îl va încărca în cache-ul L2 al procesorului (DRAM-ul încă nu a fost inițializat). 
- 
-Ordinea de scanare a perifericelor pentru continuarea procesului de boot (e.g., SD Card / eMMC, extenal SPI, USB Mass Storage, LAN boot) diferă în funcție de starea unor GPIO-uri sau a unor regiștri OTP (One Time Programmable). 
- 
-Așadar, un Raspberry PI are nevoie ca dispozitivul de pe care se efectuează bootarea să conțină o primă partiție FAT32 cu cel puțin firmware-urile de inițializare a platformei (''​bootcode.bin'',​ ''​start*.elf'',​ ''​config.txt''​ și altele câteva ce depind de modelul efectiv). 
- 
-<note important>​ 
-Fiecare microprocesor are propriile convenții de stabilire a adreselor de încărcare a stagiului secundar. 
-Cei de la Broadcom au folosit partiții FAT32, însă majoritatea producătorilor de SoC-ul incorporate cu ARM (e.g., Allwinner, NXP) preferă să încarce o imagine specială de la un anumit offset al disk-ului (e.g., la adresa ''​32KB''​ de la începutul cardului SD). De aici vine și recomandarea de a crea prima partiție abia începând cu offset-ul de ''​1MB''​. 
-</​note>​ 
- 
-Pentru [[https://​www.raspberrypi.com/​documentation/​computers/​raspberry-pi.html#​boot-sequence|Raspberry PI 3]], se va încărca fișierul ''​bootcode.bin''​ ca stagiu secundar, care, după inițializarea RAM-ului, va citi și încărca următorul stagiu (care este, de obicei, sistemul de operare propriu-zis,​ însă se poate interpune un alt bootloader -- ''​BL31'',​ e.g., [[https://​www.denx.de/​project/​u-boot/​|U-Boot]]). 
- 
-La versiunile de Raspberry PI ''>​=4'',​ ''​BL2''​ poate fi încărcat doar dintr-un EEPROM prin interfață SPI (și NU de pe SD / eMMC-ul extern -- însă există procedură de recovery în caz că se strică ceva), fapt ce ușurează aplicațiile care făceau network boot. De asemenea, firmware-ul este open-source (ceea ce nu era adevărat până acum). 
- 
-Mai departe, //Secondary Program Loader//-ul va încărca firmware-uri adiționale pentru GPU (pentru a putea afișa text prin HDMI), va analiza conținutul fișierului ''​config.txt''​ și va pune în aplicare procedurile configurate (încărcarea în memorie a fișierelor de kernel / device tree / initramfs). 
- 
-==== Componentele pentru boot ale Linux ==== 
- 
-După cum a fost menționat mai sus, pentru a porni un sistem de operare pe bază de Linux se folosesc, de cele mai multe ori, 3 componente: 
- 
-  - **Imaginea Kernel-ului** (''​kernel*.img''​),​ ce conține codul executabil al nucleului Linux; 
-  - **Device Tree Blob-ul** (''​*.dtb''​):​ conține descrierea și configurația tuturor componentelor specifice platformei hardware (i.e., pentru un anumit model + versiune a unei plăci de bază), însă se pot codifica și anumite setări și meta-informații pentru software (e.g., kernel command line, partiția rădăcină etc.); 
-  - **RamDisk inițial** (''​initrd*.img''​):​ opțional, poate conține o imagine a unui sistem de fișiere minimalist (max. câțiva zeci de MB) cu module și scripturi necesare pentru a monta sistemul de fișiere rădăcină (e.g., dacă se dorește montarea unui sistem la distanță, trebuie mai întâi să se conecteze la rețea și să primească IP prin DHCP). 
- 
-Bootloaderul (ori ''​BL2''​-ul integrat, ori ''​BL31''​ -- dacă a fost inclus) va încărca aceste fișiere în DRAM-ul sistemului și va completa anumiți regiștri (cu pointerii la locația de încărcare a tuturor componentelor necesare) și va executa o instrucțiune de //branch// pentru a lansa kernelul în execuție. 
- 
-Obiectivul final al componentelor de boot Linux va fi să caute și să monteze sistemul de fișiere rădăcină (**roofs**-ul) ce conține toată configurația și programele din //​user-space//,​ de unde se va lansa procesul ''​init''​ care va continua prin pornirea serviciilor predefinite (care pot, la rândul lor, să inițializeze dispozitive hardware noi și să ruleze procese de automatizare). 
- 
-==== RootFS ==== 
- 
-Pentru ca sistemul să fie inițializat corect după pornirea kernel-ului,​ este necesar ca toate script-urile și executabilele necesare pentru a porni daemon-ul de inițializare,​ //init//, și restul proceselor //​user-space//,​ să existe în anumite locații în sistemul de fișiere. Acest sistem minimal de fișiere necesar la inițializare poartă numele de **root file system** sau **rootfs**. În cazul sistemelor Unix, //​rootfs//​-ul are ca rădăcină directorul ''/''​ și înglobează o serie de directoare ce contin restul de fișiere necesare. De obicei, în directorul ''/''​ nu se află niciun fișier, doar subdirectoare. Aceste subdirectoare sunt organizate în funcție de fișierele pe care le conțin: 
- 
-Standardul Unix recomandă ca //​rootfs//​-ul să aibă dimensiuni relativ mici, deoarece orice problemă sau corupere a acestuia poate împiedica inițializarea corectă a sistemului. Dacă, totuși, rootfs-ul devine corupt, există modalități prin care poate fi remediat. O soluție des folosită este de a monta acest sistem de fișiere într-un alt sistem, funcțional,​ pentru a putea fi explorat și verificat. 
- 
-În cazul sistemelor embedded, de multe ori spațiul de stocare pe care îl avem la dispoziție este deja limitat: < 32MB. De cele mai multe ori, o bună parte din acest spațiu este ocupat de imaginea kernel-ului. De aceea, în majoritatea cazurilor, //​rootfs//​-ul pentru aceste sisteme este fie compus doar din minimul de programe necesar pentru inițializare,​ fie este stocat pe un server accesibil prin rețea și montat de către kernel la inițializare. 
- 
-===== Exerciții ===== 
- 
-<note warning> 
-**În laborator, vom folosi echipamente Raspberry PI 4!** 
- 
-Înainte de a începe exercițiile,​ asigurați-vă că aveți cel puțin 10GB de storage disponibili în mașină virtuala. 
-</​note>​ 
- 
-1. Găsiți [[TODO|aici o imagine de referință]] pentru Raspberry PI 4. 
- 
-  * Aflați dimensiunea în MB a imaginii oficiale (''​ls -l %%--%%block-size=M''​). 
-  * Creați un fișier de dimensiunea necesară pentru imaginea completă, folosind ''​dd''​. 
-  * Aflați informațiile tabelei de partiții a imaginii oficiale folosind ''​fdisk''​. 
-  * Creați tabela de partiții și partițiile necesare pentru imaginea noastră. 
-  * Partițiile imaginii nou create trebuie să aibă același UUID ca și imaginea veche pentru a o folosi cu succes. Pentru a modifica UUID-ul diskului folosiți comanda ''​x''​ în consola deschisa de ''​fdisk''​ pentru a intra în //expert mode//. Folosiți comanda ''​m''​ pentru a vedea toate opțiunile și modificați //disk identifier//​ încât să corespunda cu cel listat pentru imaginea oficială. 
- 
-<note tip> 
-  * Puteți folosi informațiile din tabela de partiții pentru a găsi numărul de blocuri ai fiecărei partiții, precum și offsetul lor în cadrul diskului virtual. 
-  * Asigurați-vă că tipurile partițiilor sunt aceleași precum în imaginea oficială. Pentru a schimba tipul unei partiții folosiți comanda ''​t''​ în cadrul consolei deschise de ''​fdisk'',​ urmată de codul asociat tipului pe care îl dorim. 
-</​note>​ 
- 
-2. În acest moment, cele doua partiții nu au niciun sistem de fișiere creat. Creați un //loop block device// pentru fiecare dintre cele doua imagini. Creați-le sistemele de fișiere, având grija la tipul acestora (revedeți secțiunea ​ //Formatul imaginii RaspberryPI//​). Odată create sistemele de fișiere, montați fiecare partiție într-un director. 
- 
-<note warning> 
-In procesul de boot al sistemului, partitiile sunt cautate de kernel dupa label-ul acestora. Pentru a ne asigura ca sistemul booteaza cu succes, va trebui sa denumim partitiile conform imaginii de Debian. 
- 
-Optiunea ''​-L''​ a comenzii ''​mkfs.ext4'',​ respectiv optiunea ''​-n''​ a comenzii ''​mkfs.vfat''​ ne ofera posibilitatea de a atribui un label atunci cand cream sistemul de fisiere. 
- 
-Paritia de boot trebuie sa aiba label-ul ''​RPI_BOOT'',​ iar partitia pentru rootfs trebuie sa aiba label-ul ''​RPI_ROOTFS''​. 
-</​note>​ 
- 
-3. Ne dorim să copiem conținutul fișierelor din imaginea oficiala în imaginea noastră. Copiați conținutul directoarelor imaginii oficiale în directoarele imaginii noastre. După ce procesul de copiere s-a terminat, demontați device-urile și ștergeți loop device-urile. 
- 
-<note important>​ 
-Conținutul partiției de boot trebuie copiat în partiția de boot corespondenta din imaginea noastră. În mod similar, copierea trebuie făcută intre partițiile de rootfs. 
-</​note>​ 
- 
-4. TODO start RPI4, enter BL31 
- 
-5. TODO load Linux components in memory and boot! 
- 
-===== Resurse ===== 
- 
-  * [[https://​www.raspberrypi.com/​documentation/​computers/​raspberry-pi.html#​boot-sequence|Procesul de boot al Raspberry PI]] 
-<​hidden>​ 
-  * TODO {{:​si:​si:​lab:​lab5:​sol_lab5.txt | Soluție laborator}} 
-</​hidden>​ 
  
si/laboratoare/05.1699131190.txt.gz · Last modified: 2023/11/04 22:53 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0