This shows you the differences between two versions of the page.
|
si:laboratoare:05 [2023/11/05 11:17] 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 ori transmiterea unor comenzi / întreruperi interne etc.), acesta începe să ruleze un program inițial numit bootloader. | ||
| - | Sistemele moderne folosesc un proces de boot multi-stagiu, însă primul este mereu încărcat dintr-un ROM (Read Only Memory) care, de cele mai multe ori, este 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 decât metoda naivă 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), însă necesitatea a 3 stagii diferite este dată de un motiv simplu: memoria disponibilă în diferitele momente ale procesului. | ||
| - | |||
| - | 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ă deseori nu pentru celelalte SoC-uri) 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 folosește SoC-ul ''BCM2837'') și cele **după 4** (cu ''BCM2711'', și, pe viitor, v5), stagiile se încadrează î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 ales să folosească 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). | ||
| - | |||
| - | |||
| - | ===== Exerciții ===== | ||
| - | |||
| - | <note warning> | ||
| - | **În laborator, vom folosi echipamente Raspberry PI 4!** conectate prin USB Type-C și un adaptor UART la USB pentru vizualizarea consolei dispozitivului (din păcate, nu dispunem de suficiente monitoare HDMI în laborator + cabluri adaptoare). | ||
| - | |||
| - | Înainte de a începe exercițiile, asigurați-vă că aveți cel puțin 10GB de storage disponibili în mașină virtuala de laborator. | ||
| - | </note> | ||
| - | |||
| - | **0.** Descărcați [[TODO|de aici o arhivă unui sistem de fișiere folosit ca referință laborator]] pentru Raspberry PI 4. | ||
| - | |||
| - | * Dezarhivați arhiva. **Atenție**: folosiți contul de ''root'', deoarece dorim să dezarhivăm fișierele și să păstrăm permisiunile originale (imaginea este un root filesystem de Linux pre-instalat!); | ||
| - | * Inspectați căile ''/boot'' și ''/boot/firmware''. Care credeți că este conținutul partiției de **BOOT**? | ||
| - | * Apoi, ne vom pregăti să pornim Raspberry PI 4! | ||
| - | * Asigurați-vă că firele ce conectează Raspberry PI-ul la adaptorul de serială nu sunt ieșite. Dacă da, [[https://pinout.xyz/|conectați-le corespunzător]] (UART RX/TX la TX/RS-ul adaptorului!) și **chemați asistentul înainte de a-l alimenta**! | ||
| - | * În final, înainte de a-l alimenta, conectați-vă adaptorul serial la USB în laptop și porniți programul preferat de consolă serială (e.g., ''picocom''), folosind baud rate-ul ''115200''. | ||
| - | |||
| - | **1.** Alimentați RPI-ul și urmăriți mesajele din consolă. Ar trebui să vă intre în U-Boot (preinstalat pe cardurile SD ca ''BL31''), apoi: | ||
| - | |||
| - | * Dacă apare că rulează un proces de network boot, apăsați Ctrl+C pentru a-l întrerupe; | ||
| - | * Din prompt-ul u-boot, rulați comanda ''mmc list''. O să vă apară o listă de dispozitive MMC (printre care și cardul SD); | ||
| - | * Apoi rulați comanda ''ums mmc <N>'', unde ''<N>'' este numărul perifericului MMC. Acesta va lansa în execuție un program care emulează un Mass Storage Device pe interfața USB Type-C folosită la conectarea la laptop; | ||
| - | * Inspectați noul dispozitiv montat (nu uitați să faceți pass-through la noul device USB în mașina virtuală, dacă este cazul!). | ||
| - | |||
| - | **2.** Ne dorim să copiem conținutul fișierelor de pe sistemul de fișiere referință pe cardul SD din Raspberry PI (folosind USB Mass Storage-ul prin USB Type-C). 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 ''/boot/firmware/'' trebuie copiat în partiția de boot corespondenta din imaginea noastră. | ||
| - | În mod similar, copierea trebuie făcută intre partițiile de rootfs. | ||
| - | </note> | ||
| - | |||
| - | <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.** Boot Linux! | ||
| - | |||
| - | ===== 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> | ||