Differences

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

Link to this comparison view

si:laboratoare:05 [2024/10/27 09:23]
florin.stancu removed
si:laboratoare:05 [2024/11/07 11:16] (current)
cosmin.chenaru Adaugat --import la virt-install
Line 1: Line 1:
-====== Laboratorul 05. The embedded boot process ​======+====== Laboratorul 05. QEMU & Tools ======
  
-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. +===== Introducere =====
-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 ====+Până acum am interacționat cu sisteme embedded ce au avut la baza un sistem ​de operare Real Time numit NuttX, ce a fost configurat si compilat folosind Kconfig-uri si CMake.
  
-Arhitectura ARMv8 propune ​următoarea structură:+În continuare, laboratorul își propune ​să vă familiarizeze cu sisteme embedded care rulează Linux, începând de la dezvoltare și configurare,​ până la mentenanță. Vom trata subiecte precum:
  
-{{:​si:​laboratoare:​arm_booting_process.png?700}}+  * Emularea sistemelor;​ 
 +  * Rularea/​Compilarea de aplicații pe un sistem embedded; 
 +  * Bootloadere,​ Kernel, rootfs & initramfs;​ 
 +  * Construirea unei distribuții Linux optimizată pentru sisteme embedded; 
 +  * Instalarea și configurarea de servicii.
  
-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. +==== De ce Linux? ====
-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 memorie flash externă (de obiceiprin 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!).+Sistemele Linux oferă o mulțime de avantaje dezvoltatorilor ​de produsecare micșorează timpul de dezvoltarelucru care este din ce în ce mai important ​în zilele noastre:
  
-Stagiile ''​BL3x''​ devin opționaletotul depinzând dacă ''​BL2''​ conține ​funcționalitatea de a porni ș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-urisau 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''​).+  * **versatilitate**:​ Sistemele Linux nu trebuie să fie single-purposese pot adăuga multiple ​funcționalitățcu ușurință (chiar ​și în etapa de post-producție) 
 +  * **codebase mare**: Sistemele Linux abundă de aplicații user-space, drivere pentru o mulțime de dispozitivesuport pentru multe protocoale/​sisteme de fișiere/etc. 
 +  * **securitate**:​ Sistemele care folosesc servicii comune în Linux beneficiază de același nivel de securitate ca pe un sistem desktop sau server
  
-Î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 oritoate 3 fiind necesare) ale sistemului ​de operare, să încarce în RAM și apoi să paseze execuția CPU-ului către kernel.+De-a lungul anilor Linux a devenit cel mai folosit sistem ​de operare pentru aplicațiile embedded. Îl puteți găsi folosit în orice: 
 +  * telefoane mobile (Android) 
 +  * router-
 +  * DVRNAS 
 +  * quadcoptere 
 +  * [[http://​store.steampowered.com/​livingroom/​SteamMachines/​ | console ​de jocuri]] 
 +  * [[http://​www.geek.com/​chips/​this-intelligent-fridge-runs-linux-on-an-arm-chip-1297126/​ |  frigidere ]]
  
-==== Procesul ​de boot al Raspberry PI ====+Sistemele embedded diferă foarte mult în dimensuni și putere ​de procesare, unele dintre ele apropiindu-se chiar de puterea de procesare a unui calculator obișnuit. De asemenea, aplicațiile pe care acestea le rulează pot fi foarte variate (ex: smartphone),​ amestecând diferențele dintre un calculator obișnuit și un sistem embedded. Un lucru care deosebește însă sistemele embedded este modul de interacțiune cu utilizatorii,​ care foarte rar se face printr-un ecran și o tastatură. Lipsa unui mod tradițional de interacțiune cu utilizatorul este și ceea ce face dezvoltarea unui sistem embedded mai grea, dar și mai interesantă.
  
-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.+Cele mai întâlnite două metode de interacțiune ​cu un sistem embedded în timpul dezvoltării sunt: consola serială ​și conexiunea SSH. Dintre acesteaconexiunea SSH este metoda mai robustă ​și mai simplu de utilizatînsă ea e disponibilă doar pe sistemele care dispun de o interfață de rețea. Consola serialăînsă este de obicei prezentă pe orice sistem și permite interacțiunea cu sistemul chiar și înainte ca interfața ​de rețea să fie disponibilă (ex: în bootloader sau înainte de inițializarea driver-ului de rețea).
  
-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).+==== RaspberryPi ====
  
-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).+| {{:​si:​lab:​2015:​intro:​raspberry-pi-3-ports.jpg?400|}} | 
 +^ RaspberryPi Model B ^
  
-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).+Vom lucra în principal cu RaspberryPi 3, un sistem de calcul bazat pe un procesor "​System on Chip" ARM de la Broadcom. Specificațiile complete sunt: 
 +  * procesor: 64-bit quad-core ARM Cortex-A53, 1.2GHz 
 +  * 2GB RAM 
 +  * 4 porturi USB 2.0 
 +  * 1 conector Ethernet 
 +  * card microSD 
 +  * HDMI, jack audio, RCA 
 +  * Diverse alte periferice: GPIO, UART-uri, I²C, SPI, I²S 
 + 
 +| {{:​si:​lab:​2015:​intro:​rasp-hardware.png?​400|}} | 
 +^ Schema perifericelor RaspberryPi ^ 
 + 
 +=== Schema bloc === 
 + 
 +Din punct de vedere hardware, RaspberryPi este un dispozitiv simplu, care expune diferitele periferice pe care le oferă SoC-ul Broadcom. Singura excepție o reprezintă Hub-ul USB, care dublează numărul de porturi USB disponibile ​și atașează și un dispozitiv Ethernet la SoC-ul Broadcom.  
 + 
 +<spoiler Diagrame bloc> 
 +| {{:​si:​lab:​2015:​intro:​raspberrypi_blockdiagram.png?​direct&​600 | }} | 
 +^ Diagrama bloc ^ 
 + 
 +| {{:​si:​lab:​2015:​intro:​9512_blockdiagram.png?​direct&​600 | }} | 
 +^ Diagrama block a chip-ului de USB și Ethernet ^ 
 +</​spoiler>​ 
 + 
 +===== Unelte de dezvoltare ===== 
 + 
 +Există două concepte importante folosite în dezvoltarea unui sistem embedded: **target** și **host**. //​Target//​-ul este reprezentat de sistemul embedded pe care îl dezvoltăm și la care ne conectăm (ex: RaspberryPiIntel Galileo etc.). //Host//-ul este reprezentat de calculatorul pe care îl folosim pentru dezvoltare și prin care ne conectăm cu sistemul embedded. Pentru a elimina inconvenientele compilării pe sistemul embedded (**target**-ul) compilarea se face de obicei pe un sistem desktop (**host**-ul). Bineînțeles,​ acum pot apărea probleme dacă //​target//​-ul și //host//-ul folosesc procesoare cu arhitecturi diferite (executabilul generat ​de //host// nu va fi înțeles de procesorul //​target//​-ului). Aceste probleme apar deoarece compilarea va folosi în mod implicit compilatorul //​host//​-ului:​ //​host-compiler//​-ul (ex: gcc). 
 + 
 +Rezolvarea constă în instalarea ​pe //host// a unui compilator ​care poate genera executabile înțelese de //target//. Acest compilator poartă denumirea de **cross-compiler** sau **toolchain**,​ el rulând pe arhitectura //​host//​-ului,​ dar generând cod pentru arhitectura //​target//​-ului. Procesul prin care un program este compilat pe un alt sistem diferit de sistemul //​target// ​se numește **cross-compiling**. 
 + 
 +==== Cross-compiler toolchain ==== 
 + 
 +Există patru componente de bază într-un lanț de instrumente de compilare încrucișată Linux. În plus față de acestea, sunt necesare câteva dependențe pentru a construi gcc în sine: 
 + 
 +  - **gcc** (contine compilatorul în sine, cc1 pentru C, cc1plus pentru C++ ce generează numai cod de asamblare în format text, apoi gcc, g++, care apeleaza compilatorul în sine, dar și asamblatorul și linkerul binutils, biblioteci precum libgcc (gcc runtime), libstdc++ (the C++ library), libgfortran etc și fișiere antet pentru biblioteca standard C++); 
 +  - **binutils** (contine ld, as, addr2line, ar, c++filt, gold, gprof, nm, objcopy, objdump, ranlib, readelf, size, strings, strip); 
 +  - **kernel headers**: antetele kernelului de Linux (definiții ale apelurilor de sistem, diferitelor tipuri de structuri și alte definiții similare);​ 
 +  ​**biblioteca standard C** (e.g., glibc, newlib, uclibs, musl etc.), ce oferă implementarea funcțiilor standard POSIX, plus câteva alte standarde și extensii). 
 + 
 +<​note>​Versiunea kernelului ​de Linux folosită pentru anteturile kernelului trebuie să fie aceeași versiune sau mai vechi decât versiunea kernelului care rulează pe sistemul ​țintă. În caz contrar, biblioteca standard C ar putea folosi apeluri de sistem care nu sunt furnizate de kernel.</​note>​ 
 + 
 +Diferențierea între //​host-compiler//​ și //​cross-compiler//​ se face prin prefixarea acestuia din urmă cu un string, denumit **prefix** de forma  ''<​arch>​-<​furnizor>​-<​os>​-<​libc/​abi>'' ​(ex: ''​aarch64-linux-gnu-''​)ce conține următoarele variabile (trăsături ale //​target//​-ului):​ 
 + 
 +  * ''​<​arch>'',​ arhitectura CPU: arm, mips, powerpc, i386, i686 etc. 
 +  ​''<​furnizor>'',​ (în mare parte) șir de formă liberă, ignorat de autoconf 
 +  * ''<​os>'',​ sistemul de operareFie ''​none''​ , fie ''​linux''​ în scopul acestei discuții. 
 +  * ''​<​libc/​abi>'',​ combinație de detalii despre biblioteca C și ABI-ul în uz
  
 <note important>​ <note important>​
-Fiecare microprocesor are propriile convenții de stabilire a adreselor de încărcare a stagiului secundar. +Prefixul unui cross compiler se termină întotdeaduna cu ''​-''​El va fi concatenat ​la numele utilitarelor ​(ex: ''​gcc''​) ​pentru ​obține numele complet (ex: ''​aarch64-linux-gnu-gcc''​)
-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 crea prima partiție abia începând cu offset-ul de ''​1MB''​.+
 </​note>​ </​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]]).+==== Make și Bash ====
  
-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).+După cum v-ați obișnuit, aceste două utilitare sunt de-facto standard în dezvoltarea ​de programe de sistem. 
 +Extindeți secțiunea de mai jos pentru mai multe detalii:
  
-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).+<spoiler Make and Bash intro> 
 +** GNU Make **
  
-==== Componentele ​pentru ​boot ale Linux ====+Un program important ​pentru ​dezvoltarea unui sistem embedded, și nu numai, îl reprezintă //make//. Acest utilitar ne permite automatizarea și eficientizarea procesului de compilare prin intermediul fișierelor //​Makefile//​. Pentru o reamintire a modului de scriere a unui //​Makefile//​ revedeți urmatoarea resursa - [[https://​makefiletutorial.com/​| makefiletuturial]].
  
-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 ori3 componente:+Pentru ușurarea dezvoltării pe multiple sisteme embedded, fiecare având toolchain-ul lui propriu, vom dori să scriem //​Makefile//​-uri genericecare pot fi refolosite atunci când prefixul //​cross-compiler//​-ului se schimbă. Pentru aceasta va trebui să parametrizăm numele utilitarelor apelate în //​Makefile//​. Putem folosi în acest caz variabile ​de mediu în cadrul //​Makefile//​-ului. Acestea pot fi configurate apoi din exterior în funcție de sistemul //target// pentru care compilăm la un moment datfără a mai fi necesară editarea //​Makefile//​-urilor.
  
-  - **Imaginea Kernel-ului** (''​kernel*.img''​),​ ce conține ​codul executabil al nucleului Linux; +Cel mai simplu mod de a face acest lucru este să urmăm convenția deja stabilită pentru variabila de mediu care conține ​prefixul //cross-compiler//-ului: ''​CROSS_COMPILE''​. ​Putem folosi această variabilă de mediu în cadrul //​Makefile//​-ului nostru utilizând sintaxa de expandare unei variabile, ''​$(//<​variabila>//​)'',​ și prefixând numele utilitarului ​cu variabila ​pentru ​prefix.
-  ​**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ționalpoate 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 inclusva î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 ​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ă]].+<code makefile Makefile>​ 
 +hello: hello.c 
 + $(CROSS_COMPILE)gcc hello.c ​-o hello 
 +</code>
  
-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).+Orice variabilă exportată în shell-ul curent ​va fi disponibilă și în fișierul //Makefile//. Putem de asemenea pasa variabile utilitarului //​make// ​și sub formă de parametri, astfel:
  
-==== U-Boot ====+<code shell> 
 +$ make CROSS_COMPILE=aarch64-linux-gnu- hello 
 +</​code>​
  
-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).+** Bash **
  
-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. +O mare parte din dezvoltarea unui sistem embedded ​se face prin intermediul terminaluluiShell-ul care rulează în terminal permite personalizarea unor aspecte utile pentru ​dezvoltare precum variabilele ​de mediu încărcate la fiecare rulare. Aceste personalizări se fac însă în fișiere ​de configurare specifice fiecărui shell.
-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.+Pentru //bash// aceste fișiere reprezintă niste script-uri care sunt rulate automat și se găsesc în ''/​etc'' ​(afectează toți utilizatorii) și în ''​$HOME''​ (afectează un singur utilizator). Prin intermediul fișierelor din ''​$HOME''​ fiecare utilizator își poate personaliza shell-urile ​pentru ​propriile nevoiAceste fișiere sunt: 
  
-===== Exerciții =====+  * ''​.bash_profile''​ - este executat când se pornește un shell de login (ex: primul shell după logare); 
 +  * ''​.bashrc''​ - este executat cand se pornește orice shell interactiv (ex: orice terminal deschis); 
 +  * ''​.bash_logout''​ - este executat când shell-ul de login se închide.
  
-<​note>​ +Un alt fișier util folosit de //bash// este ''​.bash_history'',​ care memorează ​un istoric al comenzilor interactive rulate. Istoricul comenzilor este salvat în acest fișier ​la închiderea unui shell. Pentru o reamintire a unor comenzi utile în linia de comandă puteți revizita laboratorul ​de USO - [[https://​ocw.cs.pub.ro/​courses/​uso/​laboratoare/​laborator-08/| Automatizare în linia de comandă]]
-**În laborator, vom folosi echipamente Raspberry PI 4!** conectate prin USB Type-C ​șun adaptor UART la USB pentru vizualizarea consolei dispozitivului (din păcate, nu dispunem ​de suficiente monitoare HDMI în laborator ​+ cabluri adaptoare).+
  
-Înainte de începe exercițiile,​ asigurați-vă că aveți cel puțin 10GB de storage disponibili în mașină virtuala ​de laborator.+<note tip> 
 +În dezvoltarea unui sistem embedded este deseori utilă adăugarea în variabila ''​$PATH'' ​căilor tre diferitele tool-uri folosite, pentru ca acestea să poată fi accesate direct prin numele executabilului. Modificarea variabilei ''​$PATH''​ pentru fiecare shell pornit se poate face ușor prin intermediul fișierelor ​de personalizare a shell-ului.
 </​note>​ </​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).+</spoiler>
  
-  * 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!);​ +==== QEMU ====
-  * 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>​ +[[http://​qemu.org| QEMU]] este un emulator / hipervizorcare permite rularea unui sistem de operare complet ca un simplu program în cadrul unui alt sistem. A fost dezvoltat inițial de [[https://​en.wikipedia.org/​wiki/​Fabrice_Bellard| Fabrice Bellard]] și este disponibil gratuit, sub o licență open source. QEMU poate rula atât pe Linux, cât și pe Windows [[#​referinte| [1]]][[#​referinte| [3]]][[#​referinte| [4]]].
-Pentru orice eventualitate**vă rugăm să chemați asistentul înainte ​de a alimenta dispozitivul la laptop!** +
-</note>+
  
-  * În finalînainte ​de a-l alimentaconectați-vă adaptorul serial la USB în laptop și porniți programul preferat de consolă serială (e.g., ''​picocom''​)folosind baud rate-ul ''​115200''​.+Este un hypervizordeoarece poate virtualiza componentele fizice ale unui sistem ​de calcul, pentru ​permite rularea unui sistem de operarenumit oaspete (//​guest//​),​ în cadrul altui sistem de operare, numit gazdă (//host//). În acest mod de funcționare, atât sistemul //guest//, cât șsistemul //host//, folosesc aceeași arhitectură (ex: x86). QEMU poate folosi un modul de nucleu, KVM, pentru a accelera rularea //guest//-ului, atunci când există suport pentru virtualizare ​în hardware. În acest caz QEMU poate atinge o performanță comparabilă cu sistemul nativ, deoarece lasă mare parte din cod să se execute direct pe procesorul //host//Folosind KVM sunt suportate diferite arhitecturiprintre care x86PowerPC și S390 [[#​referinte| [1]]].
  
-**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:+Este un emulator deoarece poate rula sisteme de operare și programe compilate pentru o platformă (ex: o placă ARM) pe o altă platformă (ex: un PC x86)Acest lucru este făcut prin translatarea dinamică a intrucțiunilor architecturii //guest// în instrucțiuni pentru arhitectura //host//. Ca un emulator, QEMU poate rula în două moduri [[#​referinte| [2]]][[#​referinte| [4]]]: 
 +  * //​[[http://​wiki.qemu.org/​download/​qemu-doc.html#​QEMU-User-space-emulator| User-mode emulation]]//,​ în care un executabil obișnuit (user-space),​ compilat pentru o arhitectură, este rulat pe o altă arhitectură. În acest mod de funcționare instrucțiunile ​din executabil sunt translatate în instrucțiuni ale arhitecturii //host//, iar argumentele apelurilor de sistem sunt convertite pentru a putea fi pasate sistemului de operare //host//Sistemele de operare emulate sunt: Linux, Mac OS X și BSD. Principalele utilizări sunt cross-debugging-ul și cross-compilarea,​ unde rulăm un compilator nativ al arhitecturii //target//, pe arhitectura //host//. 
 +  * //​[[http://​wiki.qemu.org/​download/​qemu-doc.html#​QEMU-System-emulator-for-non-PC-targets| System emulation]]//,​ în care este emulat un sistem de calcul complet. QEMU permite emularea unui număr mare de platforme, bazate ​pe diferite arhitecturi (ex: x86, ARM, PowerPC, MIPS, SPARC, MicroBlaze etc.), împreună cu perifericele lor.  În acest mod de funcționare pot fi rulate sisteme de operare întregi, printre care Windows, Linux, Solaris, BSD și DOS.
  
-  * Dacă apare că rulează ​un proces ​de network bootapăsați Ctrl+C de foarte multe ori (//yep... scriptul de boot face un for cu vreo 10 iterații//) pentru ​a-l întrerupe;​ +În dezvoltarea sistemelor embedded, QEMU este folosit deoarece poate emula un sistem ​de calcul completnefiind necesar ca sistemul țintă (//target//) pentru ​care se face dezvoltareașsistemul //host//, pe care se face dezvoltarea, ​să folosească aceeași arhitectură. Acest lucru permite ​ca dezvoltarea software-ului pentru un sistem embedded să poată fi făcută în paralel cu proiectarea hardware-ului,​ lucru crucial pentru obținerea unui timp de dezvoltare scurt. Un alt avantaj ​pe care il poate avea emularea, mai ales sistemelor low-end, este o viteză superioară a emulării pe un sistem //host// performantîn comparație cu sistemul //target//.
-  * După ce vă apare prompt-ul ''​U-Boot>​ ''​rulaț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ț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> +<spoiler Instalare>
-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).+Cel mai simplu mod de instalare pe o distribuție Linux este de a folosi //package manager//​-ul. În majoritatea distribuțiilor pachetul principal se numește ''​qemu''​ șcuprinde de obicei toate executabilele aferente diferitelor moduri de funcționare ale QEMU. Dacă se dorește doar modul de virtualizare cu KVM poate fi instalat pachetul ''​qemu-kvm'',​ iar dacă se dorește modul de emulare ​unui sistem ARM poate fi instalat pachetul ''​qemu-system-arm''​. 
 +<​note>​ 
 +<code shell Ubuntu 22.04> 
 +sudo apt update 
 +sudo apt install qemu qemu-kvm qemu-system-arm qemu-utils 
 +</​code>​
 </​note>​ </​note>​
  
-**2.** Dorim să compilăm **U-Boot** local (pentru a învăța cum se face șexplora ce funcționalități are) și să construim imaginea de boot de la zero.+<note info> 
 +Pe VM-ul de laborator aveți gata instalat qemu! 
 +</​note>​
  
-  * Aveți nevoie de toolchain-ul pentru Aarch64 să fie în ''​$PATH'' ​(lucru adevărat pe VM-ul de la laborator);​ +</​spoiler>​ 
-  * Urmați pașii: <​code>​ + 
-# pe Lab VM lipsește acest pachetinstalați-l:​ +=== Rulare === 
-sudo apt install libssl-dev + 
-git clone --branch=v2023.07.02 https://github.com/u-boot/u-boot.git +Pentru rularea unei mașini virtuale cu KVM se folosește comanda ​''​qemu-kvm'' ​împreună cu imaginea pentru hard disk. În acest caz imaginea hard disk-ului trebuie să conțină un sistem compatibil cu arhitectura //host//accelerarea oferită de KVM putând fi folosită doar dacă //guest//-ul și //host//-ul folosesc arhitecturi compatibile (ex: x86_64)
-cd u-boot + 
-# ne pregatim ​de compilare:​ +Pentru rularea în //user-mode emulation// poate fi folosit unul din executabilele ​de forma ''​qemu-<​arch>''​ împreună cu executabilul pe care vrem să-l rulăm [[#referinte| [5]]]. Bineînțelesacest executabil trebuie să fie compatibil cu arhitectura aleasă, ''<​arch>'',​ iar momentan QEMU oferă suport pentru //user-mode emulation// ​ doar pe Linux și BSD. Dintre cele două, suportul pentru BSD nu este însă la fel de complet ca cel pentru Linux [[#referinte| [6]]] 
-export CROSS_COMPILE="​aarch64-linux-gnu-"​ + 
-initializam configul default pentru RPI4 +Exemplu de rulare in **user-mode emulation**:​ 
-make rpi_4_defconfig +<​code>​ 
-make menuconfig +qemu-arm -cpu <​procesor>​ <​executabil>​
-# aicică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>​ </​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''​)+Pentru rularea în modul //system emulation// se folosește unul din executabilele de forma ''​qemu-system-<​arch>​'' ​împreună cu imaginea pentru hard disk [[#​referinte| [7]]]
-  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:+ 
 +Exemplu de rulare in modul **system emulation**:
 <​code>​ <​code>​
-cd ~/rpi-boot-firmware/ +qemu-system-arm -machine <​arhitectura>​ -drive file=... [+ multe alte argumente]
-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>​ </​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: +=== Configurare Qemu === 
-arm_64bit=1 + 
-kernel=u-boot.bin +În modul mașină virtuală sau //system emulation// QEMU simulează un întreg sistem de calcul. În lipsa unor alte argumente se folosește însă o configurație implicită de sistem, care este specifică fiecărei arhitecuri în parte. QEMU poate însă simula o gamă largă de configurații de sistem. În limbajul QEMU acestea se numesc //mașini// și pot fi selectate cu opțiunea ​''​-machine''​. 
-enable_uart=1+ 
 +<​note>​ 
 +<​code ​shell Nokia N800 tablet
 +qemu-system-arm -machine n800 <disk image>
 </​code>​ </​code>​
-  ​Acum directorul ​''​rpi-boot-firmware'' ​ar trebui să fie identic ca imaginea ​referință din laborator! +</​note>​ 
-  * //Notă: Pentru a boota Linuxeste necesar ​și ''​cmdline.txt'' ​(folosiți-pe cel din imaginea ​referință de bootloader);//​+ 
 +QEMU oferă însă și un control mai fin asupra configurației sistemului simulat printr-o serie de alte opțiuni, precum [[#​referinte| [8]]]: 
 +  ​* ''​-cpu'' ​specifică tipul de procesor care va fi emulat 
 +  * ''​-m''​ - specifică dimensiunea memoriei RAM 
 +  * ''​-hda'',​ ''​-hdb''​ etc. - specifică imaginea ​pentru primul hard disk, respectiv al doilea hard disk, ș.a.m.d 
 +  * ''​-fda'',​ ''​-fdb''​ - specifică imaginea pentru primul floppy disk, respectiv al doilea floppy disk 
 +  * ''​-cdrom''​ - specifică imaginea folosită de cdrom 
 +  * ''​-serial'',​ ''​-parallel''​ - specifică porturile seriale, respectiv, paralele și modul de interacțiune a acestora cu //host//-ul 
 +Configurații mai avansate pot fi obținute cu opțiunile ''​-device'',​ ''​-drive'',​ ''​-net'',​ ''​-soundhw'',​ ''​-bt''​ care adaugă dispozitive periferice, de stocare, plăci de rețea și de sunet și, respectiv, dispozitive bluetooth [[#​referinte| [8]]]. [[https://​qemu.weilnetz.de/​doc/​4.2/​qemu-doc.html| Documentația]] oferă informații despre toate aceste opțiuniprecum ​și multe altele. 
 + 
 +O altă opțiune utilă este ''​-kernel''​. Aceasta permite specificarea imaginii de kernel folosite de sistemul //guest// direct în comanda QEMU. Astfel, QEMU va încărca kernelul dintr-un fișier aflat pe sistemul //host// în loc de a-l cauta în imaginea ​de hard disk. Acest lucru poate reduce semnificativ timpul de iterație în momentul dezvoltării unui sistem embedded, deoarece nu mai este necesară recrearea imaginii ​de hard disk pentru fiecare modificare a kernel-ului.
  
 <note important>​ <note important>​
-Dacă dorițsă testați noua imaginear trebui să copiațaceste fișiere pe partiția FAT32 a Raspberry PI-ului. +Pe unele sisteme emulate este chiar obligatoriu ca opțiunea ''​-kernel'' ​să fie prezentădeoarece emularea sistemului nu include șun bootloaderFără un bootloader, sistemul ​nu știe altfel cum să găsească imaginea ​de kernel.
-Î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>​ </​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).+De obicei, împreună cu specificarea imaginii de kernel este nevoie ​să specificăm și linia de comandă a kernel-ului. Pentru aceasta se folosește opțiunea ''​-append''​ împreună cu string-ul care vrem să fie pasat kernel-ului la bootare.
  
-  * 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 ); +O ultimă opțiunefolositoare ​mai ales pentru debuggingo reprezintă redirectarea monitorului către consolă. Acest lucru se face cu opțiunea ''​-monitor stdio''​. Monitorul oferă o interfață în linie de comandă care permite un control interactiv al modului în care se face emularea.
-  * 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)!+
  
-<note warning>​ +=== Networking ===
-**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ă!).+Pentru a emula o interfață de rețeaQEMU se bazează pe două componente: //​device//​-ul prezentat //​guest//​-ului,​ configurat cu opțiunea ''​-device''​ sau ''​-net nic'',​ ș//​back-end//​-ul care leagă acest device de //host//, configurat cu opțiunea ''​-netdev''​. Opțiunea ''​-device''​ nu este limitată la a emula doar interfețe de rețea, ea putând configura orice dispozitiv suportat ​de către QEMU însă, unele plăci de rețea sunt suportate doar de opțiunea ''​-net nic''​.
  
-Dacă suntețpe un Linux în mașină fizică ​(please don't)întrebați un asistent ​dacă dați comenzile bune **ÎNAINTE DE A LE DA**!+Pentru //​back-end//,​ QEMU suporta mai multe moduri, printre care: 
 +  * ''​-netdev user''​ - //​user-mode//,​ rulează în user-space șnu necesită privilegii, însă interacțiunea cu rețeaua //​host//​-ului este complicată 
 +  * ''​-netdev tap''​ - //tap//, conectează o interfață TAP a //​host//​-ului la un VLAN emulat, permițând o configurare detaliată a topologiei folosite de //guest//, însă configurarea este mai complicată 
 +  * ''​-netdev bridge''​ - //​bridge//, ​ conectează o interfață TAP a //​host//​-ului la un bridge, care permite interacțiunea cu rețeaua fizică a //​host//​-ului 
 +  * ''​-netdev socket''​ - //socket//, interconectează VLAN-urile a două sisteme emulate folosind TCP sau UDP. 
 + 
 +În mod implicit QEMU emulează un sistem cu o interfață de rețea reprezentată de un //device// specificat de //mașina// selectată, în modul //​user-mode//​. Aceasta configurare implicită nu ne oferă însă toată flexibilitatea unui //target// real, conectat la o rețea ​fizică. Din acest motiv în cadrul laboratorului ne vom folosi de modul //​bridge//​. 
 + 
 +//​Bridge//​-ul folosit de către //​back-end//​ se configurează cu parametrul ''​br=//<​nume bridge>//'​', ​iar //​device//​-ul pentru opțiunea ''​-net nic''​ se specifică prin parametrul ''​model=//<​device>//''​. Legatura dintre cele două componente se face prin adăugarea parametrului ''​netdev=//<​id>//''​ la //device// șa parametrului ''​id=//<​id>//''​ la //​back-end//​. Valoarea ''//<​id>//''​ trebuie bineînteles să fie identică pentru ca cele două componente să fie legate. În final, cele două opțiuni arată astfel: ''​-net nic,​model=<​device>,​netdev=<​id>​ -netdev bridge,​br=<​nume bridge>,​id=<​id>''​. 
 + 
 +<spoiler Configurare acces Internet în QEMU folosind modul bridge>​ 
 + 
 +  * Pentru a crea și configura //​bridge//​-uri se folosește utilitarul **brctl** din pachetul **bridge-utils**. Crearea unui //bridge// care să ofere unui //guest// accesul la rețea fizică a //​host//​-ului se face astfel: <​code>​ 
 +sudo brctl addbr virbr0 #​ creăm bridge-ul 
 +sudo brctl addif virbr0 <​interfata fizica>​ #​ adăugam interfața fizică a host-ului la bridge 
 +sudo ip address flush dev <​interfata fizica>​  ​               # ștergem adresa IP de pe interfața fizică, doar dacă avem o adresă 
 +                                                                # IP pe interfață. Va șterge șruta default automat 
 +sudo dhclient virbr0 ​                                           # obținem adresa IP pentru bridge și ruta default prin DHCP 
 +</​code>​ 
 + 
 +  ​Dacă nu merge obținerea adreselor prin DHCP, se poate configura manual adresa și ruta default: <​code>​ 
 +ip address show #​ notăm ip-ul și prefixul interfeței fizice 
 +ip route show #​ notăm ruta implicită 
 +sudo brctl addbr virbr0 #​ creăm bridge-ul 
 +sudo brctl addif virbr0 <​interfata fizica>​ #​ adaugăm interfața fizică a host-ului la bridge 
 +sudo ip address del <​ip>/<​prefix>​ dev <​interfata fizica>​ #​ mutăm adresa interfeței fizice 
 +sudo ip address add <​ip>/<​prefix>​ dev virbr0 # pe bridge 
 +sudo ip link set dev virbr0 up 
 +sudo ip route add default via <​gateway>​ #​ readăugam ruta implicită 
 +</​code>​ 
 + 
 +  ​Pentru ca //​bridge//​-ul să fie acceptat de QEMU el trebuie configurat și în fișierul ''/​etc/​qemu/​bridge.conf''​ sub forma: 
 +<code text bridge.conf>​ 
 +allow virbr0 
 +</​code>​ 
 + 
 +    ​Connectati-va la ssh în sistemul emulat de qemu prin intermediul IP-ului VM-ului în bridge. 
 +<​code>​ 
 +student@virtual-machine:​~$ ssh root@192.168.122.<​X>​ 
 +</​code>​ 
 +</​spoiler>​ 
 + 
 +<spoiler Configurare acces Internet în QEMU folosind user networking (*recomandat*!)> 
 +   * Emulați interfața de rețea folosind un USB network adaptor virtual. Pentru a avea access la serviciul de SSH din QEMU o sa avem nevoie de port-forwarding pentru portul 22 (SSH default). Pentru a realiza acest lucru, adaugam parametrul ''​hostfwd=tcp::​5555-:​22''​ in optiunea de ''​-netdev''​ din comanda de ''​qemu-system-aarch64'':​ 
 + 
 +<​code>​ 
 +     ... 
 +    -device usb-net,​netdev=net0 \ 
 +    -netdev user,​id=net0,​hostfwd=tcp::​5555-:​22 \ 
 +</​code>​ 
 + 
 +  * Logarea pe //target// se face doar cu user-ul **root**. 
 +  * Verificam interfetele disponibile  
 +<​code>​ 
 +root@rpi3-20220807:​~#​ ip a s 
 +1: lo: <​LOOPBACK,​UP,​LOWER_UP>​ mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 
 +    link/​loopback 00:​00:​00:​00:​00:​00 brd 00:​00:​00:​00:​00:​00 
 +    inet 127.0.0.1/8 scope host lo 
 +       ​valid_lft forever preferred_lft forever 
 +    inet6 ::1/128 scope host  
 +       ​valid_lft forever preferred_lft forever 
 +2: enx405400123457:​ <​BROADCAST,​MULTICAST,​UP,​LOWER_UP>​ mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 
 +    link/ether 40:​54:​00:​12:​34:​57 brd ff:​ff:​ff:​ff:​ff:​ff 
 +    inet6 fe80::​4254:​ff:​fe12:​3457/​64 scope link  
 +       ​valid_lft forever preferred_lft forever 
 +</​code>​ 
 + 
 +   * Putem observa ca nu avem nicio adresa IP asociata interfetei. Pentru a realiza acest lucru, vom cere una prin intermediul protocolului DHCP. 
 +<​code>​ 
 +dhclient <​enx...> ​ # <-- numele interfeței ca parametru 
 +</​code>​ 
 + 
 +    * Connectati-va la ssh în sistemul emulat de qemu prin intermediul port-ului forward-uit local. 
 +<​code>​ 
 +student@virtual-machine:​~$ ssh -p 5555 root@localhost 
 +</​code>​ 
 +</​spoiler>​ 
 + 
 +===== Exerciții ===== 
 + 
 +<note important>​ 
 +Pentru rezolvarea laboratorului recomandăm folosirea mașininii virtuale de laborator cu Ubuntu 22.04 descărcabilă de pe [[https://​github.com/​cs-pub-ro/​SI-Lab-VM/​releases/​|GitHub + torrent]].
 </​note>​ </​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).+Ca sistem ​de operare ​pentru ​embedded / RPI (azi, emulat), vom folosi distribuția [[https://​raspi.debian.net/​|Debian Bookworm]] compilată pentru ​[[https://​raspi.debian.net/​tested-images|ARM64]]. 
 + 
 +==== 0. Setup ==== 
 <​note>​ <​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 ).+Dacă folosiți VM-ul de laborator, pașii de mai sus au fost deja efectuațiînsă tot mai trebuie să descărcați imaginea ​de debian ​(ultimul subtask).
 </​note>​ </​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: +  * Actualizați lista de pachete ​''​sudo apt update''​ 
-    * ''​vmlinuz--*''​+  Instalați ​''​git''​''​vim''​ și ''​bridge-utils''​. 
-    * ''​initrd.img-*''​; +<​code>​ 
-    ​* ​//device tree blob-ul ar trebui să fie bun cel de la BL2, încărcat automat ​și de către u-boot!//;+sudo apt install git vim wget bridge-utils 
 +</code> 
 +  * Instalați toolchain-ul necesar pentru a cross-compila programe pentru RaspberryPi 64 biți: 
 +<​code>​ 
 +sudo apt install crossbuild-essential-arm64 
 +</​code>​ 
 +  * Instalați QEMU folosind instrucțiunile următoare. 
 +<​code>​ 
 +sudo apt install qemu-user ​           # Pentru user-mode emulation 
 +sudo apt install qemu-system-aarch64 ​ # Pentru system-mode emulation 
 +sudo apt install qemu-utils ​          # Pentru utilitare precum qemu-nbd 
 +</​code>​ 
 + 
 +  * Descărcați ​și dezarhivați o imagine ​de Debian Bookworm pentru Raspberry PI Model 3B+ [[https://​raspi.debian.net/​tested-images/​|de aici]]. 
 + 
 +==== 1. User-mode emulation ==== 
 + 
 +Compilați următorul program **hello world** pentru RaspberryPi și linkați //static//. Aflați setul de instrucțiuni folosit de executabilul generat și apoi rulați-l în QEMU folosind ​//user-mode emulation// și emulând procesorul Cortex-A53. Salvați comanda folosită pentru emulare.  
 + 
 +<​code>​ 
 +#include <​stdio.h>​ 
 +int main(void) 
 +
 +    printf("​hello world\n"​); 
 +
 +</​code>​ 
 + 
 +  * Utilizaţi compilatorul **aarch64-linux-gnu-gcc**. 
 +  * Folosiți pentru compilator flag-ul ''​-static''​ pentru a obține un executabil linkat //​static//​. 
 +  * Utilitarul **file** oferă informații despre conținutul fișierelor primite ca argument. 
 +  * Ce se întâmplă dacă rulați executabilul direct, fără QEMU? De ce?
  
 <note important>​ <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.+In mod normal executabilul astfel obtinut nu merge rulat si pe sistemul host. 
 +In cazul in care merge rulata aplicatia ​de hello world si in host explicatie pentru ​care se intampla asta este: qemu instaleaza un handler care permite aceasta translatia direct, doar ca acest lucru se intampla selectiv, deoarece nu pe toate sistemele ​de operare este instalat/​configurat similar. 
 +</​note>​
  
-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 tip> 
 +  * Re-citiți despre modalitățile de [[#​rulare|rulare]]. 
 +  * Citiți introducerea acestui [[https://​www.kernel.org/​doc/​Documentation/​admin-guide/​binfmt-misc.rst| kernel feature]].
 </​note>​ </​note>​
 +==== 2. System-mode emulation ====
  
-  * 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). +Rulați distribuția Debian folosind QEMU în modul //system emulation//Veți avea nevoie ​de următoarele argumente pentru emulare.
-  * 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!).+  ​* **Kernel-ul** de Linuxprin argumentul “-kernel <​kernel_image_file>​”. 
 +  * Imaginea de **InitRD** ​(Initial RAM Disk), prin argumentul “-initrd <​initrd_file>"​. 
 +  * Pentru a funcționa mașina virtuală, este nevoie sa îi pasați emulatorului si un **Device Tree**, prin argumentul “-dtb <​device_tree_file>​”. 
 +  * Modelul mașinii emulate, prin argumentul “-machine”. Consultați documentația Qemu de [[https://​www.qemu.org/​docs/​master/​system/​arm/​raspi.html|aici]] pentru lista componentelor virtualizate (procesormemory, periferice) și alegeți modelul corespunzător pentru Raspberry Pi 3. 
 +  * Pentru imaginea discului (rootfs-ul),​ fom folosi Debian 12 (Bookworm) pentru [[https://​raspi.debian.net/​tested-images|RPi 3B+]]. Pasați imaginea discului ​cu argumentul “-sd <​disk_file>​”,​ deoarece vom emula un SD-card 
 +  * Folosiți string-ul root=/​dev/​mmcblk0p2 pentru linia de comandă a kernel-ului,​ deoarece rootfs-ul este pe a doua partitie a SD card-ului).
  
-  * Încercați,​ mai întâi, fără initramfs, apoi dați și comenzile care îl încarcă și îl dau argument la ''​booti'':​+<​code>​ 
 +qemu-system-aarch64 \ 
 + -machine … \ 
 + -kernel … \ 
 + -initrd … \ 
 + -dtb … \ 
 + -sd … \ 
 + -append "​console=ttyS1 root=/​dev/​mmcblk0p2 rw rootwait rootfstype=ext4"​ \ 
 + -nographic \ 
 + -serial null \ 
 + -serial stdio \ 
 + -monitor none 
 +</​code>​
  
 +Extrageți imaginea de kernel, dtb-ul si initrd-ul din imaginea de disc de Debian downloadată
 <​code>​ <​code>​
-inițial, fără initramfs: +sudo losetup --show -fP 20220808_raspi_3_bookworm.img ​  Notați numărul device-ului /dev/loop returnat de comanda losetup 
-fatload mmc <N>:1 ${kernel_addr_r} ​vmlinuz-6.1.0-13-arm64 +sudo mkdir /​mnt/​debian 
-#fatload mmc <N>:1 ${ramdisk_addr_r} ​initrd.img-6.1.0-13-arm64 +sudo mount /​dev/​loop16p1 /​mnt/​debian ​    # Înlocuiți valoarea 16 cu valoarea numărului vostru 
-# astea sunt setate deja în cmdline.txt (verificați), apoi sunt încărcate în DTB (FDT) mai departe de către BL2 +cp /​mnt/​debian/​vmlinuz-5.18.0-3-arm64 ​. 
-#setenv bootargs "​earlycon=pl011,​mmio32,​0xfe201000 console=serial0,​115200 root=/dev/mmcblk0p2 rw rootwait"​ +cp /​mnt/​debian/​initrd.img-5.18.0-3-arm64 ​. 
-booti ${kernel_addr_r} ​${fdt_addr} +cp /​mnt/​debian/​bcm2837-rpi-3-b.dtb . 
-# sau cu initramfs:​ +# facem unmount șdeconectăm imaginea din dispozitivul bloc 
-#booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr}+sudo umount ​/mnt/debian 
 +sudo losetup ​-d /dev/loop16
 </​code>​ </​code>​
  
-<note+Pentru montare, puteți folosi utilitarul qemu-nbd (care, în plus față de losetup, știe să deschidă mai multe formate de mașini virtuale precum vbox și vmdk): 
-Pentru ​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.+<code
 +sudo modprobe nbd max_part=8 
 +sudo qemu-nbd -c /dev/nbd0 220121_raspi_3_bullseye.img 
 +sudo mkdir /mnt/debian /​mnt/​debian/​boot 
 +sudo mount /dev/nbd0p2 /​mnt/​debian 
 +sudo mount /dev/nbd0p1 /​mnt/​debian/​boot 
 +# acum putem explora partiția de boot din imagine 
 +ls -l /​mnt/​debian/​boot 
 +# facem unmount și deconectăm imaginea din dispozitivul bloc 
 +sudo qemu-nbd -d /dev/nbd0 
 +</​code>​ 
 + 
 +Deoarece discul pasat către Qemu este de tip SD card, Qemu așteaptă ca dimensiunea discului să fie o putere a numărului 2 (ex. 512MB1024MB). Din acest motiv trebuie să redimensionăm discul, spre exemplu la 4GB. 
 +Qemu dispune, de asemenea, de utilitare pentru manipulat imagini (creare / redimensionare / conversie între formate): 
 + 
 +<​code>​ 
 +qemu-img resize 20220808_raspi_3_bookworm.img 4G 
 +</​code>​ 
 + 
 +<note tip> 
 +  * Re-citiți despre modalitățile de [[#​rulare|rulare]]. 
 +  * Revedeți parametrii de [[#​configurare| configurare]] ai QEMU și citiți pagina de manual sau [[https://qemu.weilnetz.de/doc/4.2/qemu-doc.html| ​documentația]] acestora. 
 +  * Pentru a inchide mașina virtuală, folosiți, din cadrul ei, comanda ''​sudo halt''​ sau ''​sudo poweroff''​. Dacă doriți să opriți din terminal de pe host, opriti procesul ''​qemu''​ cu ajutorul comenzii ''​killall qemu-system-aarch64''​. 
 +  * Dacă întâlniți probleme de rulare qemu, însă acesta se închide prea repede și nu puteți vedea eroarea, folosiți argumentele ''​-no-reboot -no-shutdown''​.
 </​note>​ </​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).+==== 3. Instalați serviciul Libvirt ==== 
 + 
 +Libvirt este un serviciu ce permite folosirea Qemu mult mai ușor. Împreună cu tool-uri precum ​''​virsh''​ (virtual shell) sau ''​virt-manager'',​ utilizatorul poate crea, porni, opri, clona sau migra mașini virtuale foarte ușor si rapid. În acest laborator vom folosi tool-ul ''​virt-install''​ pentru ​crea o noua mașină virtuală.
  
-<​note>​ 
-Pentru a vedea parametrii de boot configurați în device tree de către BL2, rulați comenzile de u-boot: 
 <​code>​ <​code>​
-fdt addr ${fdt_addr} +sudo apt install virtinst
-fdt print /chosen+
 </​code>​ </​code>​
 +
 +folosiți kernel-ul, initrd-ul si imaginea discului de la ex.3 pentru a crea o mașină virtuală:
 +
 +<​code>​
 +virt-install --name rpi3-qemu-si \
 + --arch aarch64 \
 + --machine virt \
 +       ​ --os-variant debian11 \
 + --boot kernel=...,​initrd=...,​kernel_args="​console=ttyAMA0 root=/​dev/​vda2 rw rootwait rootfstype=ext4"​ \
 + --disk ... \
 +       ​ --vcpus 2 \
 + --nographic \
 + --import \
 +       ​ --feature acpi=off
 +</​code>​
 +
 +Pentru a ieși din consola virsh, folosiți combinația ''​ctrl + ]''​. Avem la dispoziție următoarele comenzi:
 +
 +  * ''​virsh list --all''​ - listează toate mașinile virtuale
 +  * ''​virsh destroy NUME_VM''​ - opreste o mașină virtuală
 +  * ''​virsh undefine NUME_VM''​ - șterge o mașină virtuală
 +  * ''​virsh shutdown NUME_VM''​ - trimite o comandă de graceful shutdown, similar cu apăsarea butonul de Power
 +
 +Numele ''​destroy''​ poate induce putin în eroare, deoarece mașina este doar oprită și nu ștearsă.
 +
 +==== 4. Schimbați configurația VM-ului (CPU-ul si RAM) ====
 +
 +Cu mașina virtuală de la ex. 4 oprită, editați configurația cu ajutorul următoarei comenzi și adăugați 8 procesoare si 32GB de RAM:
 +
 +<​code>​
 +virsh edit NUME_VM
 +</​code>​
 +
 +În mod implicit, kernel-ul va refuza alocarea de memorie virtuală pentru un process de user-space, mai mult decât are sistemul disponibil. Dar îl putem convinge cu următoarea comanda executată in VM-ul de Ubuntu 22:
 +
 +<​code>​
 +sudo sysctl vm.overcommit_memory=1
 +</​code>​
 +
 +Desigur, în momentul în care VM-ul începe să folosească multă memorie, VM-ul (procesul) va fi automat oprit (killed).
 +
 +Porniți mașina virtuală, deschideți consola cu ajutorul comenzii "virsh console NUME_VM"​ și listați noua configurație:​
 +
 +<​code>​
 +cat /​proc/​cpuinfo
 +</​code>​
 +
 +<​code>​
 +free -m
 +</​code>​
 +
 +==== 5. Accesul la Internet ====
 +
 +Configurați și testați accesul //​guest//​-ului la Internet. Salvați comanda folosită pentru emulare.
 +
 +Pentru aceasta, opriți VM-ul din virsh si reluați comanda de la ex. 2 (vom folosi ''​qemu''​).
 +
 +  * Emulați interfața de rețea folosind un ''​USB network adaptor''​ virtual
 +<​code>​
 + -device usb-net,​netdev=net0 \
 + -netdev bridge,​br=...,​id=net0 ​
 +</​code>​
 +
 +Pentru a avea Internet in interiorul VM-ului, putem urma pașii din secțiunea [[#​networking| configurare a rețelei]].
 +
 +Dacă doriți să folosiți modul bridge, instalați daemon-ul libvirt (+ dnsmasq care lipsește ca dependință) care îl configurează automat:
 +
 +<​code>​
 +sudo apt install libvirt-daemon-system dnsmasq
 +</​code>​
 +
 +Apoi porniți interfața de rețea folosind virsh:
 +<​code>​
 +virsh net-start default
 +# verificați că există virbr0:
 +ip addr sh
 +</​code>​
 +
 +Folosiți numele noului bridge pentru adaptorul de retea virtual, și permiteți folosirea lui în libvirt:
 +
 +<​code>​
 +echo "allow virbr0"​ >> /​etc/​qemu/​bridge.conf
 +</​code>​
 +
 +După ce pornește mașina virtuală, listați interfețele de rețea si porniți clientul de DHCP:
 +
 +<​code>​
 +ip addr
 +dhclient en<​XYZ>​
 +</​code>​
 +
 +<note tip>
 +  * Înainte de realizarea configurațiilor de rețea, dezactivați conectarea automată din setările sistemului de operare (Settings -> Network -> Wired -> Connect Automatically (off)) și puneți placa de rețea a mașinii virtuale Ubuntu in modul de NAT (Devices -> Network -> Network Settings). Dacă folosiți o rețea wired și nu vă merge cu NAT atunci setați pe modul Bridged Adapter. Atenție, NU setați pe modul "NAT Network"​.
 +{{ :​si:​laboratoare:​screenshot_2020-10-12_at_17.47.41.png |Setare rețea}}
 +  * Recitiți secțiunea de [[#​networking| configurare a rețelei]] în QEMU.
 +  * Folosiți ''​sudo''​ pentru rularea ''​qemu-system-arm''​.
 </​note>​ </​note>​
  
-===== Resurse =====+==== 6. BONUS ==== 
 + 
 +Creați un //​Makefile//​ generic pentru programul **hello world** care poate compila pentru orice sistem //target// în funcție de variabilele primite (convenția ''​CROSS_COMPILE''​). Compilați programul pentru //host// și pentru //​target//​-ul RaspberryPi,​ apoi salvați executabilele generate. 
 + 
 +Copiati cu ''​scp''​ binarul necesar pe target, ce observati?​ 
 + 
 +Ce puteți spune despre conținutul celor 2 fișiere executabile create la exercițiul anterior? 
 + 
 +<note tip> 
 +  * Dacă o variabilă nu este setată, construcția ''​$(//<​variabilă>//​)''​ într-un //​Makefile//​ va fi echivalentă cu șirul vid. 
 +</​note>​ 
 + 
 +<note tip> 
 +  * Pentru informaţii legate de tipul fişierelor se poate folosi comanda **file**; 
 +  * Conținutul unui fișier executabil poate fi inspectat cu utilitarul **objdump** (ptr //target// folosiţi utilitarul din toolchain: **aarch64-linux-gnu-objdump**) 
 +</​note>​ 
 + 
 +==== Resurse ​==== 
 +<​hidden>​ 
 +  * {{:​si:​laboratoare:​qemu_solutii_2022.txt | Solutii laborator Qemu }} 
 +  * {{:​si:​laboratoare:​tools_solutii_2022.zip| Soluție laborator toolchain}} 
 +</​hidden>​ 
 +  * [[https://​raspi.debian.net/​tested/​20220808_raspi_3_bookworm.img.xz| Imaginea Debian 13 (Bookwork) pentru Raspberry Pi 3B]] 
 + 
 + 
 +===== Referințe ​===== 
 +  - [[http://​wiki.qemu.org/​Main_Page| QEMU website]] 
 +  - [[https://​qemu.weilnetz.de/​doc/​4.2/​qemu-doc.html#​Introduction| QEMU man page (Introduction)]] 
 +  - [[https://​en.wikibooks.org/​wiki/​QEMU| QEMU Wikibooks page]] 
 +  - [[https://​en.wikipedia.org/​wiki/​QEMU| QEMU Wikipedia page]] 
 +  - [[https://​qemu.weilnetz.de/​doc/​4.2/​qemu-doc.html#​Linux-User-space-emulator| QEMU man page (Linux User space emulator)]] 
 +  - [[https://​wiki.freebsd.org/​QemuUserModeToDo| QEMU BSD user-mode status page]] 
 +  - [[https://​qemu.weilnetz.de/​doc/​4.2/​qemu-doc.html#​ARM-System-emulator| QEMU man page (ARM System emulator)]] 
 +  - [[https://​qemu.weilnetz.de/​doc/​4.2/​qemu-doc.html#​sec_005finvocation| QEMU man page (Invocation)]] 
 +  - [[http://​elinux.org/​RPi_Hardware| RaspberryPi hardware description]] 
 +  - [[https://​www.techrepublic.com/​article/​how-to-enable-copy-and-paste-in-virtualbox/​|Copy-paste in VirtualBox]]
  
-  * [[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]] 
  
  
si/laboratoare/05.1730013827.txt.gz · Last modified: 2024/10/27 09:23 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0