Differences

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

Link to this comparison view

si:laboratoare:07 [2023/11/19 12:13]
cristian.vijelie [Exerciții]
si:laboratoare:07 [2024/11/19 19:03] (current)
florin.stancu [Exerciții]
Line 1: Line 1:
-====== Laboratorul 07. Kernel Build System ​======+====== Laboratorul 07. Root filesystem și servicii de sistem ​======
  
-Kernel-ul reprezintă o parte a sistemului de operare responsabilă cu accesul la hardware și managementul dispozitivelor dintr-un sistem de calcul (ex: procesoul, 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.+===== Root file system ​=====
  
-| {{ :​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]}} | +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șiereAcest 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 necesareDe 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:
-^ Architectura unui sistem ​de operare ^+
  
-Pentru a îndeplini toate aceste sarcini, codul kernel-ului rulează într-un mod special de lucru al procesoruluifapt 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). +  * ''/​bin'' ​conține programe și comenzi necesare la inițializare ce pot fi folosite apoi de către un utilizator neprivilegiat în timpul unei sesiuni 
- +  * ''/​sbin''​ - conține programe asemănătoare cu cele din /bin ca utilitatedar care pot fi utilizate în general doar de utilizatorul ​privilegiat ​//root// 
-===== Linux ===== +  * ''/​etc''​ - conține fișierele de configurare specifice sistemului 
- +  * ''/​lib''​ - conține bibliotecile folosite de programele din //rootfs// (cele din ''/​bin''​ și ''/​sbin''​) 
-Linux este numele unui kernel creat de către Linus Torvalds, care stă la baza tuturor distribuțiilor GNU/Linux. Inițialel a fost scris pentru procesorul Intel 80386 însădatorită licenței permisibilea 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ă versatilitatecât și numărul mare de arhitecturi ​și de periferice suportate, îl face ideal ca bază pentru un sistem embedded.+  * ''/​dev'' ​conține referințe la toate dispozitivele periferice; aceste referințe sunt în general fișiere speciale 
 +  * ''​/boot''​ - conține fișierele necesare bootării și imaginea ​kernel-ului; este posibil ca acestea să fie păstrate pe un sistem de fișiere separat de //rootfs// 
 +  * ''/​tmp''​ - conține fișiere temporare și este de obicei curățat la repornirea sistemului 
 +  * ''​/opt''​ - conține în principal aplicațiile third-party 
 +  * ''/​proc''​''/​usr''​''/​mnt''​''/​var'',​ ''/​home''​ - sunt directoare care, în general, reprezintă puncte în care se pot monta alte sisteme ​de fișiere pentru a stoca log-uribiblioteci ​și alte aplicații ​și programe folosite ​de către utilizatori.
  
 <​note>​ <​note>​
-Arhitecturile suportate de kernel-ul ​Linux se pot afla listând conținutul directorului ''​arch''​ din cadrul surselor.+Organizarea unui rootfs ​Linux în termeni de directoare este bine definită de standardul ierarhiei sistemului de fișiere: [[https://​refspecs.linuxfoundation.org/​fhs.shtml|Filesystem Hierarchy Standard]], ultimul standard FHS 3.0 fiind definit in 2015.
 </​note>​ </​note>​
  
-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 privilegiiLinux permite însă și încărcarea dinamică de cod (în timpul execuției) în kernel ​prin intermediul modulelor. Astfel, putem avea disponibile o multime de drivere, însă cele care nu sunt folosite des nu vor fi încarcate și nu vor rulaSpre deosebire de aplicații însă, care rulează în modul neprivilegiat (//​user-space//​) și nu pot afecta ​funcționarea kernel-uluiun 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.+Standardul Unix recomandă ca //​rootfs//​-ul să aibe dimensiuni relativ micideoarece orice problemă sau corupere a acestuia poate împiedica inițializarea corectă a sistemuluiDacă, totuși, rootfs-ul devine corupt, există modalitățprin care poate fi remediatO soluție des folosită este de a monta acest sistem de fișiere într-un alt sistem, funcționalpentru a putea fi explorat ​și verificat.
  
-Dezvoltarea kernel-ului Linux se face în mod distribuitfolosind sistemul ​de versionare GitVersiunea oficială 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]].+În cazul sistemelor embedded, de multe ori spațiul de stocare pe care îl avem la dispoziție este deja limitat: < 32MBDe 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 funcționarefie este stocat pe un server accesibil prin rețea și montat de către kernel ​la inițializare (e.g., folosind protocolul NFS).
  
-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.+===== Pseudo Filesystems =====
  
-Aceste modificări sunt puse la dispoziție sub formă de //patch//-uri care trebuie aplicate unei anumite versiuni ​de kernel. O altă modalitatecare este folosită și de către fundația RaspberryPieste de a publica un repository de Git cu versiunea modificată (un //tree// alternativ)Datorită modelului distribuit ​de dezvoltare suportat de Gitaceastă ​doua metodă are avantajul că permite dezvoltarea ușoară în paralel ​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.+Un kernel Linux expune mai multe pseudo-sisteme ​de fișiereprecum ''/​dev''​ (unde există noduri pentru toate dispozitivele detectate)''​/proc''​ sau ''​/sys''​Unele aplicații ce gestionează sistemul ​de operare au nevoie ca aceste mountpointuri să existeele fiind folosite pentru ​interfațcu kernelul (mai ales în sisteme incorporate,unde programele trebuie să comunice cu hardware-ul).
  
-<​note>​ +==== Sistemul virtual proc ====
-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>​+
  
-===== Linux kernel build system =====+Acest sistem de fișiere există de când există și Linux-ul și permite expunerea de statistici despre procesele rulate în sistem dar și ajustarea la runtime a diverșilor parametrii ce implică managementul proceselor, ale memoriei etc. Este utilizat de majoritatea aplicațiilor standard. Aplicații precum **ps** și **top** nu pot funcționa fără acest filesystem.
  
-Pentru ​compilare și generarea tuturor componentelor kernel-ului (eximaginea 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//.+Pentru ​a monta **proc** se poate folosi comanda<​code>​ mount -t proc nodev /proc </code>
  
-Toți pașii de compilare sunt implementați ca target-uri pentru ​//make//. De exemplupentru 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.+Printre conținutul aflat în **proc** putem găsi: 
 +  * ///​proc/​interrupts,​ /​proc/​iomem,​ /​proc/​cpuinfo//:​ ce conțin informații specifice ​de device 
 +  * ///proc/<pid>/​proc/​3840/​cmdline//:​ conține detalii despre fișierele deschise de proces, utilizarea memoriei, procesorului etc. 
 +  * ///​proc/​cmdline//:​ conține linia de comandă cu care a fost pornită imaginea kernel-ului 
 +  * ///​proc/​sys//:​ conține  fișiere care pot fi scrise pentru ​ajusta parametrii ai kernel-ului. Poartă numele de **sysctl**. Mai multe detalii despre asta aici: [[https://​www.kernel.org/​doc/​html/​latest/​admin-guide/​sysctl/​|documentație sysctl]].
  
-<note tip> +==== Sistemul virtual sys ====
-Target-ul ''​help''​ oferă informații despre aproape toate operațiile suportate de către sistemul de build.+
  
-<code shell> +Permite reprezentarea în userspace a viziunii pe care o are kernel-ul asupra magistralelor,​ dispozitivelor și driverelor din sistem. Este util pentru diverse aplicații din userspace care trebuie să enumere și să interogheze hardware-ul disponibil, de exemplu **udev** sau **mdev**:
-$ make help +
-</​code>​ +
-</​note>​+
  
-Operațiile oferite sunt grupate în diferite categorii:​ +<​code>​ls ​/sys
-  * //cleaning// - conține target-uri pentru ștergerea fișierelor generate la compilare +block bus class dev devices firmware fs kernel ​module power
-    * 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''​ +
- +
-<​note>​ +
-Arhitectura care va fi compilată este selectată de variabila de mediu ''​ARCH''​. +
- +
-<code shell> +
-$ ARCH=x86 make [<​targets>​] +
-sau +
-$ make ARCH=x86 [<​targets>​]+
 </​code>​ </​code>​
-</​note>​ 
  
-<note important>​ +===== Utilitare folosite pentru crearea ​inspecția unui RootFS =====
-Dacă arhitectura nu este setată explicit, se folosește implicit arhitectura sistemului pe care se face compilarea. +
-</note>+
  
-Î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+Uneori vom fi în situația în care nu avem acces la un server ​și imaginea de care dispunem ​pentru ​sistemul nostru nu este satisfăcătoare. În aceste ​condiții va trebui ​să ne creăm propriul //rootfs// pe care să îl scriem în memoria sistemului embedded.
  
-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ționalitatea 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.+Imaginea recomandată pentru RaspberryPi,​ Raspbian, ​conține ​două partiții: o partiție ''​FAT''​ folosită pentru boot și o partiție ''​ext4'' ​pentru //rootfs//. Fiecare dintre aceste partiții începe de la un anumit offset în cadrul imaginiiAtunci când dorim să creăm o imagine nouă pentru RaspberryPi,​ trebuie ​să ne asigurăm că respectăm aceste partiții ​și formatele lor. Pașii pe care trebuie ​să îi urmăm sunt:
  
-===== Device Tree Structure =====+  * Stabilirea dimensiunii imaginii și inițializarea acesteia cu zero-uri 
 +  * Crearea tabelei de partiții și a celor două partiții necesare 
 +  * Formatarea partițiilor cu formatul corespunzător 
 +  * Pentru popularea rootfs-ului,​ putem fie să montam partiția și să copiem manual directoarele și fișierele, fie putem să copiem o partiție întreagă de pe o altă imagine
  
-Device Tree-ul este o structura 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 si pe alte sisteme (nu pe cele care se bazeaza pe arhitectura x86). Fiecare intrare a device tree-ului descrie o componenta individuala.+Pentru fiecare dintre acești pași există utilitare ce ne ajută să realizăm operațiile necesare.
  
-Un exemplu de intrare, prezenta in Device Tree-ul specific placii VersatilePB,​ este urmatorul:+==== dd ====
  
- uart@9000 { +Pentru copiereași eventual convertireaunui fișierla nivel de bytese poate folosit utilitarul //dd//.. Folosind //dd//putem de asemenea genera fișiere de anumite dimensiuni și le putem stabili conținutul. În cazul nostru, //dd// este util pentru a inițializa o imagine și pentru a copia în acea imagine conținutul care ne interesează. Dintre parametrii lui //dd//, cei mai des utilizați sunt:
- compatible = "arm,pl011""arm,primecell";​ +
- reg = <0x9000 0x1000>;​ +
- interrupt-parent = <&​sic>;​ +
- interrupts = <​6>;​ +
- clocks = <&​xtal24mhz>​<&​pclk>;​ +
- clock-names = "​uartclk"​"​apb_pclk";​ +
- };+
  
-Intrarea este specifica unui modul de UARTcare se afla la adresa 0x9000. Aceasta indica adresa registrelor,​ numarul ​de intreruperi disponibileceasurile existente si componenta hardware care gestioneaza intreruperile.+  * ''​if''​ - fișierul ​de intrare; dacă nu se specifică acest parametru, se va citi de la //standard input// 
 +  * ''​of''​ - fișierul ​de ieșire; ca și la ''​if''​dacă nu este specificat, se scrie la //standard output// 
 +  * ''​count''​ - numărul de blocuri de input ce vor fi copiate 
 +  * ''​bs''​ - numărul de bytes dintr-un bloc
  
-Device Tree-ul este stocat in 2 fisiere: ''​.dtb''​ (device tree blob), in format binar, si ''​.dts''​ (device tree source), in format text. Ambele tipuri de fisiere se gasesc in ''​arch/​<arhitectura>/​boot/​dts'',​ fiind generate ​de target-ul ​''​dtbs'' ​al comenzii ''​make''​.+<note tip> 
 +Un exemplu ​de utilizare a lui ''​dd'' ​pentru a inițializa cu zerouri un fișier de o dimensiune exactă:
  
-===== Testare =====+<code shell> 
 +$ dd if=/dev/zero of=<​file>​ bs=1M count=2048 
 +</​code>​
  
-Pentru a testa un nou kernel acesta trebuie instalat pe //target//Această procedură diferă ​de la sistem la sistemiar pe RaspberryPi constă în copierea acestuia pe card-ul SD în partiția ​de //boot// sub numele ​de ''​kernel.img''​. ​În momentul dezvoltării și testării unui nou kernel, instalarea fiecărei versiuni a acestuia pe //target// reprezintă un bottleneck major.+Observați valorile lui ''​count''​ si ''​bs''​Se vor copia în total 2048 de blocuri ​de câte 1MB fiecarerezultând o dimensiune ​de 2GB. Fișierul ​de intrare ​''​/dev/zero'' ​este un fișier special din care se pot citi oricâte caractere ASCII NUL (0x00). 
 +</note>
  
-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.+==== parted ====
  
-===== Instalare pachete/programe din surse =====+Pentru manipularea tabelei de partiții (crearea sau ștergerea partițiilor) se poate folosi utilitarul ​//parted//. Acesta recunoaște și poate manipula multiple formate de partiții: DOS / SUN / GNU / GPT etc. Utilitarul primește ca parametru device-ul a cărui tabelă de partiții dorim să o modificăm. Un exemplu de apel este:
  
-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:+<code shell> 
 +$ parted ​/dev/<​device>​ 
 +</​code>​
  
-==== Git ====+Rezultatul acestei comenzi este un prompt nou (similar cu //fdisk//) în care putem folosi tasta ''​m''​ pentru a afișa un meniu cu opțiunile disponibile. Opțiuni utile pentru crearea și ștergerea de partiții sunt: 
 +  
 +  * ''​print''​ (sau simplu, ''​p''​) - afișează tabela curentă; 
 +  * ''​mklabel <​type>''​ - șterge tot și crează o tabelă nouă de partiții (type poate fi ''​msdos'',​ ''​gpt''​ etc.); 
 +  * ''​mkpart [type] [fstype] <​START>​ <​END>''​ - crează o partiție nouă (type poate fi ''​primary''​ / ''​logical''​ / ''​extended''​);​ singurii parametrii obligatorii sunt cele 2 offseturi;​ 
 +  * ''​set <​partition>​ <​flag>​ <​state>''​ - setează un flag unei partiții;​ 
 +  * ''​rm <​NUMBER>''​ - șterge o partiție;​ 
 +  * ''​quit''​ - iese din program (toate comenzile salvează automat).
  
-Opțiuni și comenzi ​git:+Trebuie reținut că, în mod normal //parted// operează cu sectoare ​și nu cu octeți. Așadar, este de preferat să punem unitatea de măsură (K, M, G etc.) ca sufix la orice offset cerut de comenzi ​(e.g., ''​120MB''​).
  
-   - ''​git clone //<repo>//'' ​- va aduce toate fișierele conținute de repository-ul //repo// pe mașina locală. +<note> 
-   ​- ​''​git pull'' ​- actualizează un repository deja clonat local la ultima versiune remote+  * Dimensiunea standard a unui sector de disc este ''​512 bytes''​. Aceasta poate fi schimbată folosind opțiunea ''​-b''​. 
-   - ''​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-uluiUn exemplu uzual de folosire ​branch-urilor este pentru ​organizarea diferitelor versiuni ale aceluiași program. +  * Există două tipuri de partiții: ​''​primary'' ​și ''​extended''​. Un device ce folosește sistemul ​de partiții ''​DOS''​ nu poate avea mai mult de 4 partiții primare (la GPT s-au eliminat aceste restricții). 
-   - ''​git apply //<patch file>//'' ​aplică pe fișierele locale modificările conținute de fișierul //patch//.+  * Exemplu ​de creare ​celor 2 partiții necesare ​pentru ​o imagine bootabilă Raspberry PI:<​code>​ 
 +(parted) mklabel msdos 
 +(parted) mkpart primary fat32 1MiB 120MiB 
 +(parted) mkpart primary ext4 120MiB 100% 
 +(parted) print 
 +(parted) quit 
 +</code> 
 +  * Observați, în exemplu anterior, la ultima partiție s-a folosit o unitate specială, procentul, ceea ce înseamnă că ultimei partiții i s-a repartizat tot spațiul rămas. 
 +</​note>​
  
 +==== losetup ====
  
 +Dacă se lucrează cu imagini din fișiere, este util să le putem //monta// pentru a vedea ce conțin. Însă acest lucru nu se poate realiza pe Linux fără a avea un dispozitiv de tip bloc. Din fericire, utilitarul ''​losetup''​ ne poate salva în aceste situații, el permițând conectarea unui fișier imagine la un dispozitiv bloc virtual de forma ''/​dev/​loop<​N>'',​ unde //N// este un număr din intervalul //[0-9]//.
  
-====== Exerciții ======+**Atenție**: ''​losetup''​ nu poate fi utilizat decât cu privilegii de **root** (e.g., folosiți **sudo** la toate comenzile de manipulare a dispozitivelor de sistem)!
  
-  * Descarcati ultima varianta de kernel Linux pentru Raspberry Pi, de [[https://github.com/raspberrypi/linux.git|aici]].+Exemplu folosire<code bash> 
 +# conectăm fișierul la un dispozitiv loop 
 +losetup ​/dev/loop0 ./path/to/​your-image.bin 
 +# scanăm dispozitivul bloc de partiții:​ 
 +partprobe /​dev/​loop0 
 +ls -l /​dev/​loop0* 
 +# ^ ar trebui să vedeți cele 2 partiții ale imaginii RPI: loop0p1 și loop0p2 
 +</​code>​
  
-<note tip>''​git clone https://github.com/​raspberrypi/​linux.git --depth=1''​</​note>​+**Important / NU uitați:** la final, trebuie să deconectăm dispozitivul pentru a sincroniza cele scrise înapoi în fișier: ​''​losetup -d /dev/loop<​N>​''​!
  
-  * Instalati pachetele ''​bc'',​ ''​bison'',​ ''​flex'',​ ''​libssl-dev'',​ ''​libc6-dev'',​ ''​libncurses5-dev'',​ ''​crossbuild-essential-arm64''​.+==== mkfs ====
  
-<note tip>''​sudo apt-get install -y bc bison flex libssl-dev libc6-dev libncurses5-dev crossbuild-essential-arm64''​</​note>​+Crearea unei partiții nu este suficient pentru a putea stoca fișiere. Avem nevoie ca partiția să conțină și un sistem de fișiere. Utilitarul folosit pentru a crea (formata) sisteme de fișiere Linux este //mkfs//. Parametrii pe care îi primește //mkfs// sunt device-ul ce trebuie formatat și tipul sistemului de fișiere dorit, specificat cu parametrul ​''​-t''​.
  
-  * Generati configurarea implicita a kernel-ului (tip: defconfig).+Spre exemplu, comanda <code shell> 
 +$ mkfs -t ext4 -L MyRootFS /dev/fd0 
 +</​code>​
  
-<​note ​tip>''​ +Va crea pe device-ul //fd0// un sistem de fișiere //ext4// cu label-ul (denumire logică) //​MyRootFS//​. În Linux, utilitarul //mkfs// este împărțit în câte un executabil pentru fiecare ​tip de sistem de fișiere suportat. Pentru a le vedea pe cele disponibile,​ deschideți un terminal, scrieți ​''​mkfs.'' ​(punct la coadă!) și apasați tab.
-cd linux/ +
-export KERNEL=kernel8 +
-make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig +
-''​</​note>​+
  
-  * Modificati ​din ''​menuconfig'' ​numele imaginii de kernel+<​note>​ 
 +Dacă etichetați sistemele de fișiere, le puteți accesa ulterior după căi speciale ​din ''​/​dev/​disk/​by-label/​''​!  
 +</​note>​
  
-<note tip>​tip</​note>​+==== mount ====
  
-  * Compilati nucleuldevice tree blob-urile si modulele ​de nucleu (tip: (z)Image, modules, dtbs). +În sistemele Unixsistemul ​de fișiere ​este unul arborescent unde directoarele pot fi considerate noduriiar fișierele frunze. Utilitarul //mount// ne permite să atașăm unui arbore existent un alt subarbore ​(sistem de fișiere). Apelată fără niciun parametru, această comandă va afiștoate device-urile montate, locația în care sunt montate și opțiunile cu care au fost montate.
-  * Verificati ca noul nucleu ​este bunfolosind QEMU (tip: revedeti laboratorul 3) +
-  * Adaugati noul nucleu pe Raspberry PI si faceti configurarile necesare pentru ​fi folosit dupa restart.+
  
-<note tip> +Formatul general al comenzii este ''​mount -t <type<​device>​ <mount path>'',​ unde //<​type>//​ este același ca la //mkfs//. De cele mai multe oriargumentul ​''​-t <​type>​'' ​poate fi omis, acesta fiind autodetectat ​de către ​kernel.
-  * puteti urmari ghidul oficial al celor de la Raspberry PI: [[https://www.raspberrypi.com/documentation/computers/​linux_kernel.html]] +
-  * la compilare dati cat mai multe nuclee de procesor make-ului, pentru a reduce timpul de compilarecu optiunea ​''​-j''​+
-  * nu stergeti vechiul nucleu ​de pe Raspberry PI; adaugati noul nucleu cu alt nume, si modificati ​kernel-ul folosit in fisierul ''/​boot/​config.txt''​ +
-  * imaginea de nucleu compilata este in ''​arch/​arm/​boot''​ +
-  * device tree blob-urile sunt in ''​arch/​arm/​boot/​dts''​ +
-</​note>​+
  
-<note tip> +Și, desigurpentru ​de-montarea de la final a dispozitivelor,​ putem folosi ''​umount''​ cu argumentul ori dispozitivul sursă, ori mountpointul (oricare ne este mai la îndemână ​-- va face același lucru).
-Pentru a reduce timpul de downloadputeti descarca doar o anumita versiune ​de kernel<​code bash> +
-mkdir linux +
-cd linux +
-git init +
-git remote add origin <adresa repo> +
-git fetch --depth 1 origin <​versiune>​  +
-# eg: git fetch --depth 1 origin rpi-5.15.y +
-git checkout rpi-5.15.+
-</​code>​+
  
-Pentru a putea vedea alta versiune, trebuie facut fetch pentru ea, in mod similar +===== Exerciții =====
-</​note>​+
  
-<​note ​important+<​note>​ 
-Nu utiati sa folositi variabliele ARCH si CROSS_COMPILE cand rulati ''​make''​ +**Î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).
-</​note>​+
  
-<​ifauth>​ +Î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.
-<​hidden>​ +
-3. Compilați și rulați pe Qemu o versiune a kernel-ului, urmărind instrucțiunile de mai jos: +
- +
-<​note>​ +
-Pentru simplificarea execițiului vom folosi versiunea 3.12 a kernelului. Spre deosebire ​de ultimele versiuni, aceasta oferă o configurație minimală pentru platforma RaspberryPi,​ ceea ce duce la un timp de compilare rezonabil pe PC-urile disponibile în laborator. În plus, față de ultimele versiuni, compilarea acestei configurații implicite se face fără erori.+
 </​note>​ </​note>​
  
-  ​Schimbați repository-ul pe //branch-ul// ''​rpi-3.12.y'' ​pentru a obține sursele versiunii 3.12 a kernel-ului pentru RaspberryPi. +**0.** Descărcați [[https://github.com/​cs-pub-ro/SI-rpi-debian-scripts/releases|de aici arhiva rootfs referință pentru laborator (v2)]] + imaginea partiției de boot ''​rpi-boot.img'' ​(pe care o vom clona mai târziu șîi vom adăuga partiție nouă pentru ​Linux).
-    * Efectuațun //clean// pentru a curăța fișierele binare rămase de la compilarea unei alte versiuni. +
-  * Generați fișierul configurării implicite //​quick// ​pentru ​RaspberryPi. +
-  * Compilaţi kernel-ul. +
-  * Vom ignora instalarea modulelor pentru moment. +
-  * Afișați versiunea kernel-ului care rulează.+
  
-<note tip> +  ​Pentru cei cu Windows, descărcați și instalați utilitarul [[https://​www.raspberrypi.com/software/​|Raspberry Pi Imager]], pe care îl vom folosi să scriem imaginea pe dispozitivul fizic.
-  ​Nu uitați să curățați fișierele binare generate de compilarea unei alte versiuni a kernel-ului. +
-  * Compilarea durează aproximativ 8min. +
-</note>+
  
-4Modificați configurația anterioară astfel încât ​să integreze toate driverele folosite de kernel-ul original direct în imagine. Compilați ​și rulați noua configurație,​ afișând totodată versiunea kernel-ului.+1Dorim să creăm propria imagine pe care, ulterior, ​s-o urcăm pe Raspberry PI și să bootăm Linux-ul (prin U-Boot ​și modul ''​ums'' ​-- USB Mass Storage Device):
  
-<​note>​ +  * Imaginea de //​rpi-boot.img//​ a RaspberryPi are dimensiunea de ~150MB. Folosiți ''​dd''​ (citiți și documentația din laborator mai sus) și creați o imagine ​nouă, plină de zerouri, de 2GB (să zicem''​rpi-full.img''​). 
-Driverele integrare în imagine ​vor fi încărcate o dată cu imaginea kernel-ului, de fiecare dată, încă de la bootareAstfel nu se va pierde timp cu încărcarea dinamică a acestora ​în timpul rulării. Pentru o configurație hardware stabilă și o aplicație fixată (precum un sistem embeddedacest lucru poate constitui un avantaj+  * Dorim să copiem ​de-a întregul prima partiție din imaginea ''​rpi-boot.img''​ în cea de 2GB proaspăt creată. Putem folosi ''​dd''​ pentru asta:<​code>​ 
-</note>+dd if=rpi-boot.img of=rpi-full.img conv=notrunc 
 +</​code>​ 
 +  * Folosind ''​parted'',​ inspectați dacă există partiția de boot în noua imagine (cea //full//). Apoi creați pe cea de-doua partiție (ce va conține un sistem de tip ext4). Vedeți mai sus în laborator pentru exemple de folosire a utilitarului;​ 
 +  * Formatați sistemul de fișiere a celei de-a doua partiții în ext4. Pentru ​aceasta, va trebui, mai întâi, să conectați imaginea într-un dispozitiv de tip bloc prin ''​losetup'',​ apoi ''​mkfs''​ pentru a formata (urmați documentația din laborator a acestor utilitare);  
 +  * Montați noua partiție ''​ext4''​ (puteți folosi ''/​mnt''​ ca mountpoint) și copiați conținutul arhivei rootfs descărcate mai sus; folosiți argumentul ''​-C''​ al ''​tar''​ pentru a preciza directorul destinație,​ adică mountpointul partiției). **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!):​ <code bash> 
 +tar xf <​ARCHIVE_FILE>​ -C <​DEST_MOUNTPOINT>​ 
 +# inspectați calea dezarhivată:​ 
 +ls -l <​DEST_MOUNTPOINT>​ 
 +# ar trebui să vedeți toate directoarele unui rootfs clasic: /bin, /usr, /dev, /opt etc. 
 +</​code>​ 
 +  * Încă nu am terminat. Montați partiția de boot (prim partiție din loop device) într-cale (puteți crea directorul ''/​mnt/​boot/​rpi''​ și să-l folosiți); apoi copiați fișierele ''/​mnt/​boot/​vmlinuz-<​versiune>''​ ''/​mnt/​boot/​initrd.img-<​versiune>''​ pe prima partiție (în mountpointul acesteia, ofc); acestea sunt necesare deoarece bootloaderul RPI nu știe să citească partiții ''​ext4'' ​(ci doar FAT32). 
 +  * Tot pe partiția de boot, creați fișierul ''​cmdline.txt''​ și scrieți argumentele kernelului Linux:<code> 
 +earlycon=pl011,​mmio32,​0xfe201000 console=tty1 console=serial0,​115200 root=/dev/​mmcblk0p2 rw rootwait fsck.repair=yes 
 +</code> 
 +  * **Acum (atenție!):​** demontați partițiile (mai întâi pe cea de boot, montată ultima oară, apoi pe cea ext4) și deconectați dispozitivul ''​loop''​!
  
-  * Preluați lista de module folosite de kernel-ul original folosind comanda ​''​lsmod'' ​pe //target// în timp ce acest kernel rulează. Copiați lista de module într-un fișier pe //host//. +2. Acum că imaginea noastră este gata, vrem să vedem că funcționează. Pentru aceasta, va trebui să pornim u-boot în modul ''​ums'' ​(vedeți [[:​si:​laboratoare/​06|laboratorul anterior]]) ​și folosim un utilitar ​de Raw Disk Imager (precum cel descărcat mai sus) sau ''​dd'' ​(pentru cei cu Linux nativ sau rora le funcționează USB Device Passthrough în mașina virtualăpentru a scrie imaginea obținută anterior.
-  * Folosițtarget-ul ​de make ''​localyesconfig''​, împreună ​cu variabila de mediu ''​LSMOD''​ setată la calea tre fișierul care conține lista de module, pentru a transforma configurația curentă într-o configurație care integrează driverele necesare. +
-  * Editați manual configurația modificată anterior ​pentru a repara eventualele warning-uri apărute. Recomandăm rularea target-ului de make ''​menuconfig''​ pentru efectuarea de modificări manuale. +
-    * Căutați locația configurărilor care cauzează warning-uri și activați-le. +
-    * Verificați eliminarea warning-urilor prin rerularea target-ului de make ''​localyesconfig''​.+
  
-<​note ​tip+<​note ​important
-  *  +  * **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''​)! 
-  ​* ''​menuconfig'' ​suportă tasta ''/'' ​pentru căutare. +  * **Notă pentru ​VirtualBox**: va trebui să folosiți argumentul ''​-P 2023'' ​și ''​student@localhost:<​cale>''​ ca sursă
-  * Folosiți tasta ''​y/Y'' ​pentru a selecta integrarea unei opțiuni în kernel. +  * **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ă).
-  * Începeți căutarea cu configurările ​pentru ​modulele //​snd_soc_core//,​ //​snd_seq_device//​ și //​leds_gpio//​. +
-  ​Activați suportul pentru //​I2C// ​și //SPI// din meniul //Device Drivers//+
-  * Puteți particulariza versiunea afișată a kernel-ului din meniul ​''​General setup -> Local version''​.+
 </​note>​ </​note>​
  
-BonusComparați configurația kernel-ului original cu fișierul de configurare creat de voi la exercițiul 5Ce diferență observațla opțiunile selectate de voi la pasul anterior?+3Reporniți Raspberry PI-ulVom încerca să bootăm kernelul șrootfs-ul din prompt-ul ''​u-boot'':​
  
-<note tip> +  ​Deoarece kernelul este compresat, operațiunea este un pic mai complicată,​ trebuind să-spunem o zonă de memorie folosibilă pentru extracția imaginii: ​<code> 
-  ​Revedeți [[#​linux_kernel_build_system| finalul]] secțiunii despre sistemul ​de build. +# load bootargs from device tree (contains BL2-modified cmdline.txt data) 
-  * Dezarhivați configurația originală cu ''​gunzip //<fisier>//''​+fdt addr ${fdt_addr} && fdt get value bootargs ​/chosen bootargs 
-  * Folosiți ​''​kdiff3 //<​path1>//​ //<​path2>​//'' ​pentru a compara două fișiere sau două directoare. +# set decompression zone in RAM at 400MB, 64MB in size 
-</​note>​ +setenv kernel_comp_addr_r 0x19000000 
-Bonus. **(bragging rights)** Compilați ​și rulați ultima versiune ​kernel-ului disponibil pe Raspbian Wheezy.+setenv kernel_comp_size 0x04000000 
 +# load kernel from file 
 +fatload mmc 0:1 ${kernel_addr_r} vmlinuz-6.1.61-rpi+ 
 +# boot without initrd, for now 
 +booti ${kernel_addr_r} - ${fdt_addr} 
 +</​code>​ 
 +  * Dacă vă printează mesajul cu ''​waiting for device ​/dev/mmcblk0p2''​ și nu se termină procesul, verificați dacă ați rulat kernel-ul 6.(este în comanda de mai sus) și dacă partiția doua există (''​part list mmc 0''​).
  
-<​note>​ +4. Dorim să instalăm pachete ​în imagineDeoarece pe Raspberry PI nu avem Internet, vom face asta pe mașina virtuală, lucrând direct cu imaginea și utilitarele ​''​systemd-nspawn'' ​(pornește facil un chroot container) și ''​qemu-user-static'' ​(ce ne va permite emularea unei arhitecturi străine direct dintr-un container)!
-Configurațiile pentru RaspberryPi existente ​în //​branch//​-ul ''​rpi-3.18.y''​ sunt voluminoaseceea ce duce la un timp de compilare foarte mare pe PC-urile din laborator. Din acest motiv vom transplanta configurația //​bcmrpi_quick//​ de pe un alt branch. Ultimul //commit// care conțiune acea configurație a fost găsit folosind ​''​git bisect'' ​între ​un branch (''​rpi-3.17.y''​) care conține configurația și branch-ul nostru. Pentru mai multe informații despre utilizarea //git bisect// studiați această [[https://​www.kernel.org/​pub/​software/​scm/​git/​docs/​git-bisect-lk2009.html| pagină]]. +
-</​note>​+
  
-  * Folosiți ​''​git cherry-pick eff92148ee1b5a1ff07e5817179fefb4a0562b17'' ​pentru a transplanta commit-ul ​''​eff92148ee1b5a1ff07e5817179fefb4a0562b17'' ​care este cel care conține configurația dorită. +  * Folosind ​''​losetup''​''​partprobe'' ​și ''​mount''​, montați partiția a doua (e.g., în ''​/mnt''​). 
-  * Generațconfigurarea. +  * Copiați utilitarul ​''​qemu-<​arch>​-static''​ pentru ​arhitectura emulată (AArch64) în rootfs (în ''​/​mnt/​usr/​bin''​): <​code>​ 
-    * Actualizați fișierul de configurație conform versiunii 3.18 folosind target-ul de make ''​silentoldconfig''​. ​Opțiunile noi adăugate ​în kernel de la commit-ul ​''​eff92148'' ​încoace vor trebui configurate manualFolosiți valoarea ​''​0''​ pentru ​opțiunea ​''​PHYS_OFFSET''​. +# vedem unde e executabilul:​ 
-  * Compilați noul kernel. Vom ignora din nou instalarea modulelor. +which qemu-aarch64-static 
-    * Apariția unor erori de simboluri negăsite (//unresolved symbol//) cu o configurație transplantată astfel nu este un lucru neobișnuit. +/usr/bin/qemu-aarch64-static 
-    Notați funcția sau variabila care nu poate fi găsită. +cp -f /usr/​bin/​qemu-aarch64-static /​mnt/​usr/​bin 
-    Căutați această funcție/variabilă în surse folosind ​''​grep -nHR //<​string>//​'' ​și notați fișierul în care apare. +chmod +x /​mnt/​usr/​bin/​qemu-aarch64-static 
-    * Porniți configurarea manuală ​și căutați printre opțiunile oferite pe cea/cele care conțin bucăți din calea fișierului notat anterior. Activați-le cu tasta ''​y/Y''​. +</​code>​ 
-    Reporniți compilarea. Fișierele deja compilate vor fi refolosite pe cât posibil de //make//, astfel încât procesul de compilare se reia în mare parte de unde a rămas. +  ​Rulăm containerul:​ <​code>​ 
-    ​Dacă mai apar erori de compilare reluați procesul ​(nu ar trebui să mai apară). +systemd-nspawn --as-pid2 --resolv-conf=copy-host -D "/​mnt"​ bash 
-  * Porniți RaspberyPi-ul ​folosind ​noul kernel și afișati versiunea kernelul folosit. +</​code>​ 
-</​hidden>​  +  ​Ar trebui să fiți în containerul de Debian pe arhitectură străină (AArch64), emulat prin qemu-user-static cu ajutorul ​funcționalității din kernel ​''​binfmt_misc''​. ​Rulați ''​apt update'' ​și instalați pachetele ​''​wpasupplicant'' ​(pentru autentificare prin WiFi) și ce alte utilitare mai doriți
-</​ifauth>​+  La final, **nu uitați** să de-montați + deconectați dispozitivele loop utilizare! 
 +  * Bonus: testați noua imagine pe dispozitivul RPI4 (va trebui să repetați pașii copiere + burn folosind ​Disk Imager)!
  
 +===== Resurse =====
  
-<​hidden>​ +  ​[[https://github.com/​cs-pub-ro/​SI-rpi-debian-scripts|Cod sursă scripturi compilare bootloader / kernel ​generare rootfs]]
-  ​{{:si:​laboratoare:​lab8_2022.txtSoluție laborator}} +
-</hidden>​ +
-====== Referințe ======+
  
-  - [[https://​www.raspberrypi.com/​documentation/​computers/​linux_kernel.html| Ghid compilare kernel RaspberryPi]] 
-  - [[https://​www.thegoodpenguin.co.uk/​blog/​build-boot-linux-on-raspberry-pi-3-model-b/​| Ghid compilare U-Boot pentru RaspberryPi]] 
  
si/laboratoare/07.1700388838.txt.gz · Last modified: 2023/11/19 12:13 by cristian.vijelie
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