This shows you the differences between two versions of the page.
si:laboratoare:08 [2023/12/03 22:30] florin.stancu |
si:laboratoare:08 [2024/11/24 22:15] (current) andreea.miu [Text & exercitii] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratorul 08. RootFS Bootstrapping ====== | + | ====== Laboratorul 08. Kernel Build System ====== |
- | ===== Crearea unei distribuții Linux ===== | + | Kernel-ul reprezintă o parte a sistemului de operare responsabilă cu accesul la hardware și managementul dispozitivelor dintr-un sistem de calcul (ex: procesorul, memoria, dispozitivele de I/O). De asemenea, el are rolul de a simplifica accesul la diferitele dispozitive hardware, oferind o interfață generică pentru aplicații prin intermediul system-call-urilor. În spatele interfeței generice se află porțiuni din kernel, numite drivere, care implementeză comunicația cu dispozitivele hardware. Un alt rol al kernel-ului este de a izola aplicațiile între ele, atât pentru stabilitatea sistemului, cât și din considerente de securitate. |
- | Până acum am folosit Root Filesystem de-a gata pre-instalat. | + | | {{ :si:lab:2015:kernel:architecture.png?300 |Architectura unui sistem de operare [By Bobbo (Own work) [CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0) or GFDL (http://www.gnu.org/copyleft/fdl.html)], via Wikimedia Commons]}} | |
+ | ^ Architectura unui sistem de operare ^ | ||
- | La acest laborator, dorim să învățăm procesul de generare a unui sistem de bază și modalitățile de personalizare a acestuia, proces numit **rootfs bootstrapping**. | + | Pentru a îndeplini toate aceste sarcini, codul kernel-ului rulează într-un mod special de lucru al procesorului, fapt care îi permite să execute o serie de instrucțiuni privilegiate. Acest mod privilegiat de lucru nu este accesibil aplicațiilor obișnuite. Spunem că aplicațiile rulează în //user-space// (modul neprivilegiat), iar kernel-ul rulează în //kernel-space// (modul privilegiat). |
- | Avem 2 variante: | + | ===== Linux ===== |
- | * plecarea de la o distribuție existentă și alegerea pachetelor pe care să le includem + configurații specifice; | + | |
- | * compilarea (manuală sau automată) a tuturor componentelor unui astfel de sistem; | + | |
- | Prima soluție, deși mult mai facilă (atât rapidă, cât și ușor de înțeles), nu ne permite o optimizare a spațiului utilizat (sisteme bazate pe Debian ocupă de ordinul ''~1GB'') și nici fine-tuningul ulterior pentru îmbunătățirea performanțelor în cazul rulării pe anumite dispozitive (e.g., ARMv8 cu extensii NEON pentru SIMD). | + | Linux este numele unui kernel creat de către Linus Torvalds, care stă la baza tuturor distribuțiilor GNU/Linux. Inițial, el a fost scris pentru procesorul Intel 80386 însă, datorită licenței permisibile, a cunoscut o dezvoltare extraordinară, în ziua de astăzi el rulând pe o gamă largă de dispozitive, de la ceasuri de mână până la super-calculatoare. Această versatilitate, cât și numărul mare de arhitecturi și de periferice suportate, îl face ideal ca bază pentru un sistem embedded. |
- | Pe de altă parte, dacă am dori să facem asta de la zero, ar trebui să plecăm de la un toolchain (pe care, de cele mai multe ori, trebuie să-l compilăm tot noi), pe care îl vom folosi să compilăm din cod sursă toate componentele necesare într-un sistem Linux: biblioteci standard (LibC), sistem de init (SystemV / SystemD / OpenRC etc.), suita de programe de bază Unix (e.g., ''mount'', ''cat'', ''ls'', ''cp'', ''sh'', ''awk'' și multe altele!), device manager (''udev'') precum și multe alte servicii (e.g., de networking) și aplicații necesare scopului nostru final (e.g., Python, X11 + QT Toolkit pentru interfețe grafice etc.). | + | <note> |
+ | Arhitecturile suportate de kernel-ul Linux se pot afla listând conținutul directorului ''arch'' din cadrul surselor. | ||
+ | </note> | ||
- | Din fericire, există suite software ([[https://www.yoctoproject.org/|Yocto Linux]], [[https://buildroot.org/|Buildroot]]) care pot automatiza repetabil acest proces. Însă un dezavantaj tot rămâne: timpul de compilare este de ordinul orelor, iar spațiul pe disk necesar între ''20GB -- 50GB''. | + | Linux este un kernel cu o arhitectură monolitică, acest lucru însemnând că toate serviciile oferite de kernel rulează în același spațiu de adresă și cu aceleași privilegii. Linux permite însă și încărcarea dinamică de cod (în timpul execuției) în kernel prin intermediul modulelor. Astfel, putem avea disponibile o mulțime de drivere, însă cele care nu sunt folosite des nu vor fi încărcate și nu vor rula. Spre deosebire de aplicații însă, care rulează în modul neprivilegiat (//user-space//) și nu pot afecta funcționarea kernel-ului, un modul are acces la toată memoria kernel-ului și se execută în //kernel-space// (poate executa orice instrucțiune privilegiată). Un bug într-un modul sau un modul malițios poate compromite întregul sistem. |
+ | Dezvoltarea kernel-ului Linux se face în mod distribuit, folosind sistemul de versionare Git. Versiunea oficială a kernel-ului, denumită //mainline// sau //vanilla// este disponibilă în repository-ul lui Linus Torvalds, la adresa [[https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]]. | ||
- | ===== Exerciții ===== | + | Versiunea oficială este însă rar folosită într-un sistem embedded nemodificată. Este foarte comun ca fiecare sistem să folosească o versiune proprie a kernelului (numită un //tree//) bazată mai mult sau mai puțin pe versiunea oficială. Datorită licenței GPLv2 a kernelului, însă, orice producător care folosește o versiune modificată a kernelului este obligat să pună la dispoziție modificările aduse. |
- | Pentru acest laborator, vom încerca ambele abordări! | + | Aceste modificări sunt puse la dispoziție sub formă de //patch//-uri care trebuie aplicate unei anumite versiuni de kernel. O altă modalitate, care este folosită și de către fundația RaspberryPi, este de a publica un repository de Git cu versiunea modificată (un //tree// alternativ). Datorită modelului distribuit de dezvoltare suportat de Git, această a doua metodă are avantajul că permite dezvoltarea ușoară în paralel a celor două versiuni. Modificările făcute într-una pot fi portate și în cealaltă, iar Git va ține minte ce diferențe există în fiecare versiune. Cele două versiuni sunt de fapt două //branch//-uri de dezvoltare, care se întâmplă să fie găzduite pe servere diferite. |
- | ==== 1. Crearea de distribuții bazate pe Debian (debootstrap) ==== | + | <note> |
+ | Kernel-ul folosit pe RaspberryPi, care include suportul pentru SoC-ul Broadcom BCM2835 folosit de acesta, se găsește la adresa [[https://github.com/raspberrypi/linux.git]]. | ||
+ | </note> | ||
- | Pentru a genera distribuții personalizate bazate pe Debian (și derivate, e.g., Ubuntu), se poate folosi utilitarul ''debootstrap''. | + | ===== Linux kernel build system ===== |
- | Acesta descarcă automat din repository-uri pachetele ''.dpkg'', pe care le instalează într-un prefix (director) precizat de utilizator, e.g.: | + | Pentru compilare și generarea tuturor componentelor kernel-ului (ex: imaginea principală - //vmlinux//, module, firmware) Linux folosește un sistem de build dezvoltat o dată cu kernel-ul, bazat pe utilitarul //make//. Acest sistem de build însă nu seamănă cu clasicul //config/make/make install//, deși ambele sunt bazate pe utilitarul //make//. |
- | <code shell> | + | |
- | $ sudo debootstrap --arch="arm64" --include="vim bash curl" \ | + | Toți pașii de compilare sunt implementați ca target-uri pentru //make//. De exemplu, pentru configurare se poate folosi target-ul ''config''. Acestă metodă de configurare însă nu este recomandată deoarece oferă o interfață foarte greoaie de configurare a kernel-ului. |
- | --variant=minbase "bookworm" "/root/mydebian/" "http://ftp.de.debian.org/debian" | + | |
- | </code> | + | |
<note tip> | <note tip> | ||
- | Aproape toate distribuțiile de Linux au câte un utilitar (de multe ori, chiar package managerul distribuției) care permite bootstrappingul RootFS-urilor din pachete (tot așa sunt generate și imaginile ISO live!). | + | Target-ul ''help'' oferă informații despre aproape toate operațiile suportate de către sistemul de build. |
- | Spre exemplu, Arch Linux are ''pacstrap'', Alpine Linux are ''apk.static'' and so on... | + | |
+ | <code shell> | ||
+ | $ make help | ||
+ | </code> | ||
</note> | </note> | ||
- | De menționat ar fi că instalarea unui pachet are loc în 2 etape: | + | Operațiile oferite sunt grupate în diferite categorii: |
- | * decompresia arhivelor și extragerea fișierelor în directorul destinație; | + | * //cleaning// - conține target-uri pentru ștergerea fișierelor generate la compilare |
- | * rularea unor scripturi post-install pentru crearea configurațiilor implicite / upgrade fișiere / integrarea cu alte aplicații / instalarea serviciilor etc.; | + | * spre deosebire de ''clean'', ''mrproper'' șterge în plus toate fișierele generate, plus configurarea și diferite fișiere de backup |
+ | * //configuration// - conține diferite target-uri pentru generarea unei configurări a kernelului; există target-uri care permit: | ||
+ | * generarea unui fișier de configurare nou; ex: ''defconfig'', ''allmodconfig'', ... | ||
+ | * actualizarea unui fișier de configurare existent; ex: ''olddefconfig'', ''localyesconfig'', ... | ||
+ | * editarea unui fișier de configurare existent; ex: ''menuconfig'', ''nconfig'', ... | ||
+ | * //generic// - conține target-uri generice; există target-uri pentru: | ||
+ | * compilare; ex: ''all'' - target-ul implicit care este executat la o invocare simplă a lui ''make'', iar ''vmlinux'' și ''modules'' compilează imaginea kernel-ului și, respectiv, modulele selectate | ||
+ | * instalare; ex: ''headers_install'', ''module_install'', ... | ||
+ | * informații; ex: ''kernelrelease'', ''image_name'', ... | ||
+ | * //architecture dependent// - conține target-uri dependente de architectură, care diferă în funcție de arhitectura selectată; există target-uri pentru; | ||
+ | * generarea de imagini în diferite formate: compresate (''zImage'' și ''bzImage''), pentru U-Boot (''uImage''), ... | ||
+ | * generarea de configurări implicite pentru sisteme bazate pe arhitectura selectată; ex: ''*_defconfig'' | ||
- | Acest aspect devine important când încercăm să generăm un rootfs pentru o arhitectură străină celei gazdă (cross-bootstrapping)! | + | <note> |
- | Pentru a realiza acest lucru, va trebui să distingem cele două etape: decompresia va putea avea loc direct pe host (avem utilitarele necesare instalate cu scriptul de bootstrapping), pe când etapa de execuție a programelor va trebui să fie virtualizată cumva (chroot + ''qemu-user-static'' FTW). | + | Arhitectura care va fi compilată este selectată de variabila de mediu ''ARCH''. |
- | Astfel, pentru a realiza ''debootstrap'' de pe o arhitectură Intel/AMD ''x86_64'' pentru o țintă AArch64 (ARM 64-bit), vom avea de realizat următoarele: | ||
<code shell> | <code shell> | ||
- | $ sudo debootstrap --foreign --arch="arm64" ... /root/mydebian ... # restul de argumente normale | + | $ ARCH=x86 make [<targets>] |
- | # apoi copiem emulatorul în noua rădăcină (va fi rulat de binfmt_misc ca în Lab 04) | + | sau |
- | $ sudo cp -af "$(which qemu-aarch64-static)" /root/mydebian/usr/bin/ | + | $ make ARCH=x86 [<targets>] |
- | # Acum va trebui să rulăm stagiul 2 al debootstrap, însă într-un container / chroot! | + | |
- | # Folosim systemd-nspawn pentru a rula binarul de second-stage instalat pe rootfs de la primul: | + | |
- | $ sudo systemd-nspawn -as-pid2 --resolv-conf=copy-host -D /root/mydebian \ | + | |
- | /debootstrap/debootstrap --second-stage --verbose | + | |
</code> | </code> | ||
+ | </note> | ||
- | Tehnica cu systemd-nspawn + qemu-user-static (și binfmt_misc) este utilă pentru a intra într-un chroot pe rootfs-ul nostru pentru a-l personaliza (putem chiar și instala programe noi sau configura servicii etc.). Însă va trebui configurat gestionarul de pachete, ''apt'': de adăugat mirror-uri la repo-urile de bază în ''/etc/apt/sources.list'' -- [[https://wiki.debian.org/SourcesList#Example_sources.list|găsiți exemplu aici]]. | + | <note important> |
+ | Dacă arhitectura nu este setată explicit, se folosește implicit arhitectura sistemului pe care se face compilarea. | ||
+ | </note> | ||
- | **Atenție:** momentan, rootfs-ul NU ARE parolă la contul ''root'' (și nu are nici un alt cont) și nu vă veți putea autentifica din niciun //tty//! | + | În cele mai multe situații se dorește compilarea unui kernel pentru un sistem deja existent, fie pentru a adăuga sau elimina funcționalități sau pentru a actualiza versiunea de kernel folosită. În aceste cazuri folosirea target-urilor de generare a unei configurații noi, chiar și a celor care generează o configurație implicită pentru arhitectura noastră nu sunt neapărat utile. Este posibil ca kernel-ul existent să aibă deja o configurație personalizată care se dorește doar a fi actualizată/modificată folosind target-urile de editare. |
- | Se recomandă accesarea unui chroot (rulați ''/usr/bin/bash'' ca ultim argument al lui ''systemd-nspawn'') și setarea uneia prin ''passwd''. | + | |
- | O dată generat rootfs-ul, acesta trebuie copiat (atenție să nu se piardă permisiunile și atributele unor fișiere speciale -- mai ales symlink-urile) pe o partiție și bootat! Se recomandă arhivarea acestuia folosind utilitarul ''tar'' (acesta salvând automat metadatele necesare în arhivă):<code shell> | + | Un kernel care rulează poate conține fișierul de configurare (de obicei în format comprimat //gzip//) din care a fost compilat, dacă această funcționalitate a fost selectată la build. Acest fișier se regăsește în ''/proc/config.gz''. Tot ce rămâne este să extragem acest fișier și să-l modificăm conform dorințelor. |
- | $ tar czf mydebian.tar.gz -C /root/mydebian | + | |
- | </code> | + | |
- | În final, dorim să vedem cât ocupă rootfs-ul nostru: | + | ===== Device Tree Structure ===== |
- | <code shell> | + | |
- | $ du -hs /root/mydebian | + | |
- | </code> | + | |
- | <note tip> | + | Device Tree-ul este o structură de date, la nivelul kernel-ului, care descrie componentele hardware prezente pe sistem, care nu pot fi descoperite automat de kernel. El este prezent pe sistemele cu arhitecturi ARM, dar și pe alte sisteme (nu pe cele care se bazează pe arhitectura x86). Fiecare intrare a device tree-ului descrie o componentă individuală. |
- | Vom trece să instalăm + rulăm sistemul pe un Raspberry PI 4 fizic imediat ce pornim compilarea unui sistem Buildroot ;) | + | |
- | </note> | + | |
- | ==== 2. Compilarea programelor de bază (buildroot) ==== | + | Device Tree-ul este stocat în 2 fișiere: ''.dtb'' (device tree blob), în format binar, și ''.dts'' (device tree source), în format text. Ambele tipuri de fișiere se găsesc în ''arch/<arhitectură>/boot/dts'', fiind generate de target-ul ''dtbs'' al comenzii ''make''. |
- | Mai departe, descriem procesul de utilizare al unui utilitar destul de popular (cel mai important fiind ușurința de învățare față de Yocto Linux). | + | ===== Testare ===== |
- | <note warning> | + | Pentru a testa un nou kernel acesta trebuie instalat pe //target//. Această procedură diferă de la sistem la sistem, iar pe RaspberryPi constă în copierea acestuia pe card-ul SD în partiția de //boot// sub numele de ''kernel8.img''. În momentul dezvoltării și testării unui nou kernel, instalarea fiecărei versiuni a acestuia pe //target// reprezintă un bottleneck major. |
- | Din păcate, vom avea nevoie de aproximativ ''~20-30GB'' de spațiu disponibil (în mașina virtuală). | + | |
- | Cel mai simplu este să creați un Virtual HDD nou de ''30GB'' (**nu îi pre-alocați** spațiul, deoarece nu va ajunge să ocupe atât azi -- poate la temă :P ) și atașați-l proiectului VirtualBox / VMWare ca dispozitiv de stocare suplimentar (din setări / storage, add [hard] disk pe ambele hipervizoare). | + | O alternativă la instalarea kernel-ului pe //target// o reprezintă încărcarea acestuia prin rețea direct pe de //host//-ul folosit la dezvoltare, dacă există suport din partea bootloader-ului. Din păcate, bootloader-ul implicit de pe RaspberryPi nu are suport pentru a încărca o imagine de kernel de pe rețea. Un bootloader care oferă însă acestă facilitate este //U-Boot// [[#referinte| [2]]], el folosind protocolul TFTP pentru a boota o imagine de kernel prin rețea. |
- | Apoi va trebui formatat + montat din VM: ''mkdir /media/big && mkfs.ext4 /dev/sd<X> && mount /dev/sd<X> /media/big''. | + | |
- | O idee bună ar mai fă să creați director de lucru pentru ''student'': ''mkdir /media/big/work && chown student:student /media/big/work -R''. | + | |
- | De asemenea, tot acum ar fi momentul să alocați memorie RAM mai multă (3-6GB, dacă aveți de unde) aproape câte nuclee aveți procesorului virtualizat (să se paralelizeze procesul). | + | ===== Instalare pachete/programe din surse ===== |
- | Și să conectați laptop-ul la 230V ;) | + | |
- | </note> | + | De multe ori ne lovim de problema instalării unui pachet sau a unui program pe care îl găsim doar pe un repository public, de cele mai multe ori bazat pe Git. Astfel, pentru a ne putea folosi de acel pachet/program, trebuie să cunoaștem următoarele utilitare: |
+ | |||
+ | ==== Git ==== | ||
+ | |||
+ | Opțiuni și comenzi git: | ||
+ | |||
+ | - ''git clone //<repo>//'' - va aduce toate fișierele conținute de repository-ul //repo// pe mașina locală. | ||
+ | - ''git pull'' - actualizează un repository deja clonat local la ultima versiune remote. | ||
+ | - ''git checkout //<branch>//'' sau ''git checkout //<tag>//'' - actualizează fișierele locale la versiunea indicată de //branch// sau de //tag//. Un repository poate avea mai multe branch-uri, care pot fi văzute ca niște versiuni diferite ale repository-ului. Un exemplu uzual de folosire a branch-urilor este pentru organizarea diferitelor versiuni ale aceluiași program. | ||
+ | - ''git apply //<patch file>//'' - aplică pe fișierele locale modificările conținute de fișierul //patch//. | ||
+ | |||
+ | |||
+ | |||
+ | ====== Exerciții ====== | ||
+ | |||
+ | **0.** Pregăriri | ||
+ | |||
+ | <hidden> | ||
+ | * **Pentru asistenți:** descărcați [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts/releases|de aici imaginea 'rpi-full.img' pentru Raspberry PI (v2)]] și scrieți-o pe Raspberry PI folosind un disk imager (ori cu un card reader, ori bootați un U-Boot, schimbați cardul SD, ''mmc rescan'', apoi ''ums mmc 0'' (găsiți disk-ul accesibil de pe calculatorul la care e conectat prin USB Type-C). | ||
+ | </hidden> | ||
+ | |||
+ | * Descarcati ultima varianta de kernel Linux pentru Raspberry Pi, de [[https://github.com/raspberrypi/linux.git|aici]]. <code> | ||
+ | git clone https://github.com/raspberrypi/linux.git --depth=1</code> | ||
- | Spre deosebire de ''debootstrap'', care avea nevoie de drepturi de ''root'', aceste utilitare folosesc principiul de ''fake root'' (metadatele sistemului de fișiere sunt virtualizate într-o bază de date externă kernelului), fiind utilizabile (uneori obligatoriu) și de către conturi non-superuser. Aproape toți pașii va trebui să-i urmați autentificați ca ''student''! | + | * Instalati pachetele necesare: <code> |
+ | apt-get install -y bc bison flex libssl-dev libc6-dev libncurses5-dev crossbuild-essential-arm64 </code> | ||
- | Primul pas, ar fi, desigur descărcarea lui ''buildroot''. Alegeți o versiune de la [[https://buildroot.org/downloads/]] și descărcați-o + dezarhivați-o (sub contul student și pe HDD-ul mare!)<code shell> | + | * Generati configurarea implicita a kernel-ului (tip: defconfig). <code> |
- | $ wget https://buildroot.org/downloads/buildroot-2023.08.tar.xz | + | cd linux/ |
- | $ tar xf ... | + | export KERNEL=kernel8 |
- | $ cd buildroot-* | + | make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig |
</code> | </code> | ||
- | Utilitarul Buildroot folosește mecanismul Kconfig (la fel ca U-Boot și kernel-ul Linux), deci: ''make menuconfig''. | + | * Modificati din ''menuconfig'' numele imaginii de kernel. |
- | Navigați prin meniuri și schimbați următoarele opțiuni: | + | |
- | * ''Target Options'' => ''Target Architecture'' => ''AArch64 (little endian)''; | + | <note tip>Lansati meniul pentru arhitectura selectata anterior:<code> |
- | * ''Target Options'' => ''Target Architecture Variant'' => ''cortex-A53'' (pentru compatibilitate sporită cu mai multe modele Raspberry PI + Qemu); | + | make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig |
- | * ''Target Options'' => ''Floating point strategy'' => ''VFPv4'' (la fel, compatibilitate sporită); | + | </code> |
- | * ''Toolchain'' => ''Enable compatibility shims ...''; | + | Optiunea se afla in General Setup -> Local Version</note> |
- | * ''System Configuration'' => ''Root password'' => ''student'' -- pentru a ne putea autentifica în shell! | + | |
- | * ''Filesystem images'' => ''Compression method'' => ''gzip'' -- opțional, imaginea ocupă extrem de puțin oricum. | + | |
- | Apoi rulați ''make''! Procesul poate dura între 10 minute și 1h, în funcție de numărul de nuclee, frecvența lor (ce poate fi scăzută dacă sunteți pe baterie) și memoria RAM disponibilă. | + | * Verificati daca driver-ul de mmc este activat (nu ca modul). |
- | <note tip> | + | <note tip>Trebuie sa vedeti ''MMC [=y]'' </note> |
- | Distribuția de bază Buildroot include doar utilitarele ce vin cu [[https://busybox.net/|busybox]]. | + | |
- | Aveți, însă, o mulțime de pachete de aplicații pe care le puteți bifa la secțiunea ''Target packages''! | + | |
- | Însă acest lucru va duce la o mărire drastică a timpului de compilare + consum de spațiu pe disk (în funcție de ce / câte pachete + câte dependințe au)! | + | * Compilati nucleul, device tree blob-urile si modulele de nucleu. <code> |
- | </note> | + | make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image Image.gz modules dtbs -j$(nproc)</code> |
- | **Nu așteptați! lăsați-l să ruleze și treceți la task-ul următor ;)** | + | <note important>Procesul de compilare va dura destul de mult. Daca folositi o masina virtuala, dati-i cat mai multe nuclee, pentru a reduce timpul</note> |
- | La final, să inspectați sistemul de fișiere rezultat, îl găsiți la calea ''<buildroot-dir>/output/images/''. Cât ocupă? | + | * Cat se compileaza nucleul, inspectati fisierul ''.dts'' corespunzator placii RaspberryPI 4B. <code> |
+ | arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts</code> | ||
- | ==== 3. Instalarea rootfs-ului în imaginea RPI 4 ==== | + | * Kernelul nostru experimental se instalează folosind următorii pași: <code> |
+ | # se presupune că aveți deja montate imaginea / cardul SD al RPi-ului (citiți mai jos!) | ||
+ | sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_PATH=<path_catre_partitia_de_boot> install | ||
+ | sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=<path_catre_partitia_de_rootfs> modules_install | ||
- | **0.** Descărcați [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts/releases|de aici imaginea rpi-full.img.tar.xz]]. | + | # Notă: nu suprascrieți device tree-ul deoarece o versiune greșită ar strica u-boot-ul :D |
+ | # Dar așa s-ar copia: | ||
+ | #sudo cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb <path_catre_partitia_de_boot>/ | ||
+ | #sudo cp arch/arm64/boot/dts/overlays/bcm2711-rpi-4-b.dtb* <path_catre_partitia_de_boot>/overlays/ | ||
- | **1.** Trebuie să montăm imaginea și să copiem noul RootFs pe cea de a doua partiție (pe care o vom formata): | + | # Redenumim imaginea de kernel (e deja cea compresata) |
+ | sudo mv <path_catre_partitia_de_boot>/vmlinuz-<versiunea-noua> <path_catre_partitia_de_boot>/vmlinuz-student | ||
- | * Dezarhivați fișierul imaginii ''rpi-full.img''; ar trebui să obțineți imaginea de ''2GB''; | + | # Demontam partitiile |
- | * Verificați că există ambele partiții: ''parted rpi-full.img print''; | + | sudo umount <path_catre_partitia_de_boot> |
- | * Conectați imaginea la un dispozitiv de tip bloc folosind ''losetup /dev/loop0 rpi-full.img'' și ''partprobe /dev/loop0'' (verificați cu ''losetup -L'' ce aveți conectat dacă întâmpinați probleme); | + | sudo umount <path_catre_partitia_de_rootfs> |
- | * Formatați sistemul de fișiere a celei de-a doua partiții în ext4: ''mkfs.ext4 -L RPI_ROOTFS /dev/loop0p2'' (confirmați!); | + | |
- | * Montați noua partiție goală ''ext4'' (puteți folosi ''/mnt'' ca mountpoint) și copiați conținutul arhivei rootfs generate anterior (ori cea de Debian, ori cea obținută prin Buildroot): <code shell> | + | # Oprim ums |
- | tar xf <ARCHIVE_FILE> -C <DEST_MOUNTPOINT> | + | |
- | # inspectați calea dezarhivată: | + | |
- | ls -l <DEST_MOUNTPOINT> | + | |
</code> | </code> | ||
- | * Demontați partiția ext4 și deconectați dispozitivul ''loop'' folosite! | ||
- | **2.** Porniți Raspberry PI-ul în u-boot, apoi intrați în modul ''ums mmc X'' (vedeți [[:si:laboratoare/05|laboratorul 5]]); folosiți ori ''dd'', ori un utilitar de Raw Disk Imager pentru Windows (recomandat: Raspberry PI Imager) ca la laboratoarele anterioare pentru a scrie noua imagine pe RPI. | + | * **Notă**: dacă aveți probleme cu USB device passthrough-ul pe Raspberry PI, este suficient să copiem DOAR fișierul vmlinuz pe partiția de boot, cea FAT32. Pentru aceasta, bootați-l în modul ''ums'' din U-Boot, conectat la USB Type-C) și copiați DOAR fișierul ''arch/arm64/boot/Image.gz'' sub numele de ''vmlinuz-student''. |
- | <note important> | + | <note tip>Pentru a porni ums, urmati urmatorii pasi: |
- | * **Atenție**: Va trebui, mai întâi, să aduceți imaginea pe Windows (folosiți ''scp'' cu ''username@<ip_sursa>:/cale/catre/fisier.img'' ca prim argument, iar, ca destinație, o cale de pe PC-ul fizic, e.g., ''C:\Users\..etc'')! | + | * conectati adaptorul de seriala la laptop |
- | * **Notă pentru VirtualBox**: va trebui să folosiți argumentul ''-P 2023'' și ''student@localhost:<cale>'' ca sursă. | + | * ''picocom /dev/ttyUSB0 -b 115200'' |
- | * **Notă pentru cei cu gazda pe Linux**: dacă utilizați ''dd'', va trebui să rulați comanda ''sync'' și să așteptați să se termine! Asta e important deoarece kernelul modern cache-uiește în memoria RAM și scrierea se termină, aproape instant (însă rămâne să se copieze pe SD în background, această operațiune fiind mult mai lentă). | + | * conectati cablu de alimentare |
+ | * la un moment dat va aparea **Hit any key to stop autoboot:**; apasati orice tasta | ||
+ | * daca nu ati intrat in meniul de U-Boot, apasati Ctrl-C | ||
+ | * din meniul de U-Boot, dati comanda ''ums mmc 0'' | ||
+ | * partitiile ar trebui sa fie vizibile: **/dev/sda1**(root) si **/dev/sda2**(rootfs) | ||
+ | * daca nu vi le monteaza sistemul de operare automat, montati-le | ||
</note> | </note> | ||
- | 3. Reporniți Raspberry PI-ul. Lăsați-l să booteze normal, ar trebui să pornească kernel-ul implicit cu rootfs-ul vostru! | + | <note important> |
+ | U-boot din imaginea nouă este configurat să booteze automat ''vmlinuz-student'', dacă există! | ||
- | * Inspectați-l! Cât ocupă pe disk (''df'')? | + | Acesta va aștepta 10 secunde, permițând să întrerupeți procesul pentru a putea re-accesa modul ''ums'' pentru a rescrie imaginea pe viitor. |
- | * Dacă vă printează mesajul cu ''waiting for device /dev/mmcblk0p2'' și nu se termină procesul, verificați dacă partiția a doua există din u-boot (''part list mmc 0''). | + | |
- | ==== 4. [BONUS] Rulați imaginea voastră în QEmu (system) ==== | + | Puteți vedea [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts/blob/labsi/configs/labsi-rpi4/files/uboot-script.txt|codul sursă al scriptului de u-boot aici]]. |
+ | </note> | ||
- | Observăm că procesul de testare a unei imagini embedded consumă ceva timp (mai ales la scrierea pe cardul SD). | + | * Porniti RaspberryPI-ul cu noua versiune, ca in [[https://ocw.cs.pub.ro/courses/si/laboratoare/06#exercitii|laboratorul 6]]. |
- | Dorim să optimizăm! | + | |
- | Rulați imaginea obținută anterior (va trebui să exrageți kernel-ul din imagine!) prin ''qemu-system-aarch64''. | + | <note warning> |
- | Re-vedeți instrucțiunile din [[:si:laboratoare/04|laboratorul 4]]. | + | Dacă nu ați copiat modulele externe pe rootfs-ul de pe Raspberry PI al noului kernel, Linux se va plânge când bootează că lipsesc o parte din modulele necesare de anumite dispozitive hardware / software non-esențiale (//ahem: multe sunt, defapt, necesare pentru a avea suport pentru rețelistică în Linux//), însă procesul de boot ar trebui să se finalizeze. |
+ | </note> | ||
- | ===== Resurse ===== | + | ====== Referințe ====== |
- | * [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts|Cod sursă scripturi compilare bootloader / kernel / generare rootfs]] | + | - [[https://www.raspberrypi.com/documentation/computers/linux_kernel.html| Ghid compilare kernel RaspberryPi]] |
- | * [[https://buildroot.org/downloads/manual/manual.html#_getting_started|Buildroot Manual - Getting started]] | + | - [[https://www.thegoodpenguin.co.uk/blog/build-boot-linux-on-raspberry-pi-3-model-b/| Ghid compilare U-Boot pentru RaspberryPi]] |