This shows you the differences between two versions of the page.
si:laboratoare:06 [2024/11/11 12:12] florin.stancu |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratorul 06. 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 operare, să încarce în RAM și apoi 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, toate în condordanță cu [[https://www.kernel.org/doc/html//v5.8/arm64/booting.html|protocolul definit de kernel pentru arhitectura dată]]. | ||
- | |||
- | 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). | ||
- | |||
- | ==== U-Boot ==== | ||
- | |||
- | U-boot este probabil cel mai popular bootloader open-source folosit atât în lumea embedded / IoT / industrial, cât și în platformele mobile (majoritatea telefoanelor pe Android). | ||
- | |||
- | Acesta este scris în C și folosește KConfig (as expected) pentru personalizarea funcționalităților incluse pentru a se putea încadra în cerințele restrictive de memorie a anumitor sisteme. | ||
- | De asemenea, U-Boot are implementată o cantitate vastă de drivere necesare pentru a interfața cu cât mai multe periferice de pe care să se încarce sistemul de operare (SPI, MMC, USB devices, SATA / M.2 PCI-E, chiar și Rețea, prin PXE + TFTP) și conține chiar și un mini-limbaj de scripting (similar bash, însă compilat în binar) ce permite implementarea de proceduri avansate de boot cu redundanță și failover (necesare sistemelor realtime). | ||
- | |||
- | Noi vom folosi un U-Boot la laboratoare pentru a putea scrie SD card-ul direct de pe PC, prin conectarea Raspberry PI4-ului direct la PC prin USB OTG (fără a utiliza un cititor extern de carduri SD!), cât și pentru a avea o vizibilitate mult mai bună a procesului de boot Linux. | ||
- | |||
- | ===== Exerciții ===== | ||
- | |||
- | <note> | ||
- | **Î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 [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts/releases|de aici o arhivă unui sistem de fișiere folosit ca referință laborator]] pentru RPI4 + imaginea partiției ''rpi-boot.img'' (utilă în caz că se strică bootloaderul). | ||
- | |||
- | * Dezarhivați arhiva într-un subdirector (prin ''tar'', folosiți argumentul ''-C'' pentru a preciza directorul destinație, însă va trebui să îl creați înainte). **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'' (rețineți / copiați într-un fișier text output-ul, e util de comparat mai încolo). | ||
- | * 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!) + chemați asistentul să verifice! | ||
- | |||
- | <note warning> | ||
- | Pentru orice eventualitate, **vă rugăm să chemați asistentul înainte de a alimenta dispozitivul la laptop!** | ||
- | </note> | ||
- | |||
- | * În final, înainte de a-l alimenta, conectați-vă adaptorul serial la USB în laptop, faceți USB passthrough la mașina virtuală (dacă este cazul) și porniți programul preferat de consolă serială (e.g., ''picocom''), folosind baud rate-ul ''115200'': <code> | ||
- | sudo dmesg | tail -20 && ls -l /dev/ttyUSB* | ||
- | picocom -b 115200 /dev/ttyUSB0 # înlocuiți cu ce dispozitiv aveți | ||
- | </code> | ||
- | |||
- | <note important> | ||
- | Din păcate, în laborator avem ca adaptoare Serial to USB niște clone chinezești de Prolific PL2303, pe care driverele oficiale Microsoft [[http://wp.brodzinski.net/2014/10/01/fake-pl2303-how-to-install/|le dezactivează automat ca fiind "counterfeit" (găsiți o soluție aici)]]. De regulă, în mașina virtuală cu Linux nu ar trebui să conteze, doar că mecanismul driverelor de a dezactiva automat dispozitivele "copiate" poate interfera cu passthrough-ul USB care se face în mod normal. :(( problema apare frecvent pe VirtualBox (pe VMware nu au fost raportate probleme). | ||
- | </note> | ||
- | |||
- | **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 de foarte multe ori (//yep... scriptul de boot face un for cu vreo 10 iterații//) pentru a-l întrerupe; | ||
- | * După ce vă apare prompt-ul ''U-Boot> '', rulați comanda ''mmc list''. O să vă apară o listă de dispozitive MMC (ar trebui să vedeți cardul SD ca fiind detectat); încercați-le pe fiecare cu ''mmc dev <N>'' și apoi ''mmc info'' până găsiț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!). Mai precis, montați prima partiție (posibil să fie și singura) pe mașina voastră virtuală cu Linux. Ce fișiere există? Comparați cu partiția ''/boot'' a rootfs-ului. | ||
- | |||
- | <note important> | ||
- | Comanda ''ums'' este blocantă (poate fi întreruptă cu Ctrl+C). | ||
- | Dacă folosiți un soft de virtualizare (VirtualBox / VMWare), va trebui să faceți passthrough la acest dispozitiv (se numește "Netchip USB Download Gadget"). Apoi verificați prin ''lsblk'' / ''blkid'' care este dispozitivul nou apărut în VM. | ||
- | |||
- | Cât timp rulează, vă puteți conecta cu laptopul (prin interfața USB Type-C) la cardul SD introdus în Raspberry pentru a-l inspecta / [re]scrie (cu grijă să nu suprascrieți bootloaderul u-boot). | ||
- | </note> | ||
- | |||
- | **2.** Dorim să compilăm **U-Boot** local (pentru a învăța cum se face și explora ce funcționalități are) și să construim imaginea de boot de la zero. | ||
- | |||
- | * Aveți nevoie de toolchain-ul pentru Aarch64 să fie în ''$PATH'' (lucru adevărat pe VM-ul de la laborator); | ||
- | * Urmați pașii: <code> | ||
- | # pe Lab VM 2023 lipsește acest pachet, instalați-l: | ||
- | sudo apt install libssl-dev | ||
- | git clone --branch=v2023.07.02 https://github.com/u-boot/u-boot.git | ||
- | cd u-boot | ||
- | # ne pregatim de compilare: | ||
- | export CROSS_COMPILE="aarch64-linux-gnu-" | ||
- | # initializam configul default pentru RPI4 | ||
- | make rpi_4_defconfig | ||
- | make menuconfig | ||
- | # aici, căutați și activați următoarele opțiuni (ambele sunt necesare!): | ||
- | # CONFIG_USB_FUNCTION_MASS_STORAGE=y | ||
- | # CONFIG_CMD_USB_MASS_STORAGE=y | ||
- | make -j4 | ||
- | </code> | ||
- | |||
- | * O dată terminată compilarea, copiați fișierele ''u-boot.bin'' și ''arch/arm/dts/bcm2711-rpi-4-b.dtb'' din directorul lui U-Boot către un director nou creat (să zicem, ''~/rpi-boot-firmware''). | ||
- | * De asemenea, stagiul secundar (''BL2'') al RPI4 are nevoie de firmware-ul GPU-ului, ce poate fi descărcat de pe GitHub de la adresele: | ||
- | <code> | ||
- | cd ~/rpi-boot-firmware/ | ||
- | wget "https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf" | ||
- | wget "https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat" | ||
- | # însă le găsiți și pe sistemul rădăcină referință, la calea /boot/firmware | ||
- | </code> | ||
- | * Ultimul lucru care lipsește este fișierul ''config.txt''. Strictul necesar este următorul: <code> | ||
- | # config.txt contents to load U-Boot as BL31: | ||
- | arm_64bit=1 | ||
- | kernel=u-boot.bin | ||
- | enable_uart=1 | ||
- | </code> | ||
- | * Acum directorul ''rpi-boot-firmware'' ar trebui să fie identic ca imaginea referință din laborator! | ||
- | * //Notă: Pentru a boota Linux, este necesar și ''cmdline.txt'' (folosiți-l pe cel din imaginea referință de bootloader);// | ||
- | |||
- | <note important> | ||
- | Dacă doriți să testați noua imagine, ar trebui să copiați aceste fișiere pe partiția FAT32 a Raspberry PI-ului. | ||
- | Însă faceți asta doar dacă aveți încredere că nu stricați bootloaderul existent! | ||
- | Dacă ați pățit totuși asta și doriți să refaceți, există mai multe tehnici de recuperat, cea mai simplă fiind folosirea unui SD card reader extern și scrierea imaginii ''rpi-boot.img'' descărcate la ex. 0. Alternativ, puteți scrie imaginea pe un stick USB și introduce în RPI, apoi folosiți comanda ''ums'' pentru a scrie cardul SD introdus în dispozitiv și apoi ''dd'' pentru a scrie bootloaderul referință:<code> | ||
- | sudo dd if=~/Downloads/rpi-boot.img.bin of=/dev/sd<x> bs=4k status=progress && sudo sync | ||
- | </code> | ||
- | </note> | ||
- | |||
- | **3.** Ne dorim să instalăm sistemul de fișiere Linux referință pe cardul SD din Raspberry PI (folosind USB Mass Storage-ul prin USB Type-C). | ||
- | |||
- | * Folosiți comanda ''lsblk'' pentru a descoperi cum se numesc device-urile (și, desigur, aveți grijă la capacitate: să nu ștergeți partiția rădăcină a OS-ului real din VM / fizic)! | ||
- | * Primul lucru, va trebui să creați o partiție ''ext4'' pe cardul SD, **după cea de boot, FAT32 (NU O ȘTERGEȚI)!**. Putem folosi ''fdisk'' sau utilitarul mai modern, ''parted'' (tot în linia de comandă :D ): | ||
- | <code> | ||
- | lsblk # NOTAȚI CU ATENȚIE CARE E CARDUL SD DE ~16GB !!!! | ||
- | # probabil SDB/SDC..., poate să fie sda dacă aveți ssd pe NVME și rulați Linux nativ... | ||
- | sudo parted /dev/sd<X> # înlocuiți <X>-ul | ||
- | # în parted, dați comenzile 'print' pentru a vedea partițiile curente, 'help' pentru comenzi | ||
- | # creați o partiție nouă (primary, ext2 sunt setări bune) pe restul de spațiu liber (puteți scrie ''100%''), apoi quit | ||
- | # apoi, pentru a încărca tabela nouă de partiții, folosiți comanda: | ||
- | sudo partprobe /dev/sd<X> | ||
- | </code> | ||
- | |||
- | <note warning> | ||
- | **Mare atenție aici**: sunteți pe cale de a rula operații cu potențial destructiv! | ||
- | |||
- | Dacă folosiți mașina virtuală, este recomandat să faceți snapshot (riscați să ștergeți partițiile sistemului de operare real dacă nu aveți grijă!). | ||
- | |||
- | Dacă sunteți pe un Linux în mașină fizică (please don't), întrebați un asistent dacă dați comenzile bune **ÎNAINTE DE A LE DA**! | ||
- | </note> | ||
- | * Formatați noua partiție (cea de-a doua) folosind utilitarul ''mkfs.ext4''. Folosiți și argumentul ''-L RPI_ROOTFS'' pentru a-i da un label (nume). | ||
- | * Copiați conținutul directoarelor rootfs-ului oficial în partiția ''ext4'' nou-creată (folosiți ''sudo cp -ar'' pentru a păstra meta-informațiile fișierelor -- utilizatorii și chmod-ul atribuit). | ||
- | <note> | ||
- | Este posibil ca operația de copiere să dureze neobișnuit de mult (cel mult 10 min), totul depinde de calitatea cardului SD folosit în RPI (cele mai ieftine :P ). | ||
- | </note> | ||
- | |||
- | * Acum mai avem nevoie de câteva fișiere pentru a boota linux-ul (pe care le găsiți pe rootfs în ''/boot''), anume: | ||
- | * ''vmlinuz--*''; | ||
- | * ''initrd.img-*''; | ||
- | * //device tree blob-ul ar trebui să fie bun cel de la BL2, încărcat automat și de către u-boot!//; | ||
- | |||
- | <note important> | ||
- | Cardurile SD sunt extrem de lente, iar Linux are un obicei prost de a cache-ui fișierele copate în RAM-ul local și a le scrie în background după ce returnează comenzile de dezarhivare / copiere! Rulați comanda ''sudo sync'', care este blocantă până toate operațiile de scriere pe disk se vor completa. | ||
- | |||
- | Pentru a monitoriza progresul, utilizați one-liner-ul ''watch grep -e Dirty: -e Writeback: /proc/meminfo'' (așteptați până Writeback devine aproape de zero, iar Dirty sub 1MB). | ||
- | </note> | ||
- | |||
- | * NU UITAȚI: după ce procesul de copiere s-a terminat, demontați toate device-urile implicate (i.e., cele 2 partiții ale cardului SD). | ||
- | * După copiere, puteți vizualiza fișierele de pe partiția FAT32 din U-Boot: ''fatls mmc 1'' ;) | ||
- | |||
- | **4.** Pornim Raspberry PI-ul, din nou (avem mare grijă la firele de la serială, să nu facă contact cu alte părți ale dispozitivului!). | ||
- | |||
- | * Încercați, mai întâi, fără initramfs, apoi dați și comenzile care îl încarcă și îl dau argument la ''booti'': | ||
- | |||
- | <code> | ||
- | # inițial, fără initramfs: | ||
- | fatload mmc <N>:1 ${kernel_addr_r} vmlinuz-6.1.0-13-arm64 | ||
- | #fatload mmc <N>:1 ${ramdisk_addr_r} initrd.img-6.1.0-13-arm64 | ||
- | # astea sunt setate deja în cmdline.txt (verificați), apoi sunt încărcate în DTB (FDT) mai departe de către BL2 | ||
- | #setenv bootargs "earlycon=pl011,mmio32,0xfe201000 console=serial0,115200 root=/dev/mmcblk0p2 rw rootwait" | ||
- | booti ${kernel_addr_r} - ${fdt_addr} | ||
- | # sau cu initramfs: | ||
- | #booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr} | ||
- | </code> | ||
- | |||
- | <note> | ||
- | Pentru a avea vizibilitate la ce face kernelul când bootează pe serială, [[https://www.raspberrypi.com/documentation/computers/configuration.html#enabling-early-console-for-linux|am urmat pașii de aici]] să activăm earlycon pe RPI. | ||
- | </note> | ||
- | |||
- | * Este posibil ca ''root=<device>'' să nu fie bun, încercați să îi dați ''root=LABEL=RPI_ROOTFS'' sau prin UUID (depinde cum ați creat partiția și dacă ați dat label la ext4). | ||
- | |||
- | <note> | ||
- | Pentru a vedea parametrii de boot configurați în device tree de către BL2, rulați comenzile de u-boot: | ||
- | <code> | ||
- | fdt addr ${fdt_addr} | ||
- | fdt print /chosen | ||
- | </code> | ||
- | </note> | ||
- | |||
- | ===== Resurse ===== | ||
- | |||
- | * [[https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#boot-sequence|Procesul de boot al Raspberry PI]] | ||
- | * [[https://hechao.li/2021/12/20/Boot-Raspberry-Pi-4-Using-uboot-and-Initramfs/|Mod utilizare U-Boot pentru Raspberry PI 4]] | ||
- | * [[https://github-wiki-see.page/m/lulu98/projects-with-sel4/wiki/RPi4-Boot-Files|Explicație proces de boot al RPI 4 și fișierele firmware implicate]] | ||
- | * [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts|Cod sursă scripturi compilare bootloader / kernel / generare rootfs]] | ||
- | |||