Differences

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

Link to this comparison view

si:laboratoare:05 [2023/11/04 22:50]
florin.stancu
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 ======
  
 +===== Introducere =====
  
-Atunci când un microprocesor primește semnalul de reset (prin alimentare, transmiterea unor comenzi / întreruperi interne etc.), acesta începe să booteze. +Până acum am interacționat cu sisteme embedded ce au avut la baza un sistem ​de operare Real Time numit NuttXce a fost configurat si compilat folosind Kconfig-uri si CMake.
-Toate sistemele moderne folosesc ​un proces ​de boot multi-stagiuînsă primul program este mereu încărcat dintr-un ROM (Read Only Memory) ce este, de cele mai multe ori, integrată în același chip.+
  
-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?600}}+  * 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 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).+
  
-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 și Kernel-ul ​de Linux și este suficient ​de configurabil ​pentru ​a putea acoperi ​mare parte a cazurilor ​de utilizare ale sistemului (ceea ce este adevărat pentru RPIînsă ​pentru ​altele nu prea) sau dezvoltatorul software dorește să beneficieze ​de funcționalităț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ăți 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ș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 opreare, și să încarce în RAM și 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 folosesc SoC-ul ''​BCM2837''​) ​și cele **după 4** (cu ''​BCM2711''​, și, pe viitor, v5)stagiile pot fi încadrate î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 folosit 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.+<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:
  
-==== RootFS ====+<code shell> 
 +$ make CROSS_COMPILE=aarch64-linux-gnu- hello 
 +</​code>​
  
-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șiere. Acest 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 necesare. De 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:+** Bash **
  
-Standardul Unix recomandă ca //rootfs//-ul să aibă dimensiuni relativ mici, deoarece orice problemă sau corupere a acestuia poate împiedica inițializarea corectă a sistemuluiDacă, totuși, rootfs-ul devine corupt, există modalități prin care poate fi remediat. O soluție des folosită este de a monta acest sistem de fișiere ​într-un alt sistem, funcțional,​ pentru a putea fi explorat și verificat.+O mare parte din dezvoltarea unui sistem embedded se face prin intermediul terminalului. Shell-ul care rulează în terminal permite personalizarea unor aspecte utile pentru dezvoltare precum variabilele de mediu încărcate la fiecare rulareAceste personalizări se fac însă în fișiere ​de configurare specifice fiecărui shell.
  
-Î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 inițializarefie este stocat pe un server accesibil prin rețea și montat ​de către ​kernel ​la inițializare.+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 nevoi. Aceste fișiere sunt:  
 + 
 +  * ''​.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. 
 + 
 +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ă]].  
 + 
 +<note tip> 
 +În dezvoltarea unui sistem embedded este deseori utilă adăugarea în variabila ''​$PATH''​ a căilor că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>​ 
 + 
 +</​spoiler>​ 
 + 
 +==== QEMU ==== 
 + 
 +[[http://​qemu.org| QEMU]] este un emulator / hipervizor, care 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]]]. 
 + 
 +Este un hypervizor, deoarece poate virtualiza componentele fizice ale unui sistem de calcul, pentru a permite rularea unui sistem de operare, numit oaspete (//​guest//​),​ în cadrul altui sistem de operare, numit gazdă (//host//). În acest mod de funcționare,​ atât sistemul //guest//, cât și 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 arhitecturi,​ printre care x86, PowerPC și S390 [[#​referinte| [1]]]. 
 + 
 +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. 
 + 
 +În dezvoltarea ​sistemelor embedded, ​QEMU este folosit deoarece poate emula un sistem ​de calcul complet, nefiind necesar ca sistemul ​țintă (//​target//​) pentru care se face dezvoltarea,​ și 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 a sistemelor low-end, este o viteză superioară a emulării pe un sistem //host// performant, în comparație cu sistemul //​target//​. 
 + 
 +<spoiler Instalare>​ 
 + 
 +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''​ și 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 a 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 info> 
 +Pe VM-ul de laborator aveți gata instalat qemu! 
 +</​note>​ 
 + 
 +</​spoiler>​ 
 + 
 +=== Rulare === 
 + 
 +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 (exx86_64). 
 + 
 +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țeles,​ acest 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]]] 
 + 
 +Exemplu de rulare in **user-mode emulation**:​ 
 +<​code>​ 
 +qemu-arm -cpu <​procesor>​ <​executabil>​ 
 +</​code>​ 
 + 
 +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]]]. 
 + 
 +Exemplu de rulare in modul **system emulation**:​ 
 +<​code>​ 
 +qemu-system-arm -machine <​arhitectura>​ -drive file=... [+ multe alte argumente] 
 +</​code>​ 
 + 
 +=== Configurare Qemu === 
 + 
 +În modul mașină virtuală sau //system emulation// QEMU simulează un întreg sistem de calcul. În lipsa unor alte argumente se folosește însă ​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''​. 
 + 
 +<​note>​ 
 +<code shell Nokia N800 tablet>​ 
 +qemu-system-arm -machine n800 <disk image> 
 +</​code>​ 
 +</​note>​ 
 + 
 +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țiuni, precum ș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>​ 
 +Pe unele sisteme emulate este chiar obligatoriu ca opțiunea ''​-kernel''​ să fie prezentă, deoarece emularea sistemului nu include și un bootloader. Fără un bootloader, sistemul nu știe altfel cum să găsească imaginea de kernel. 
 +</​note>​ 
 + 
 +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. 
 + 
 +O ultimă opțiune, folositoare mai ales pentru debugging, o 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. 
 + 
 +=== Networking === 
 + 
 +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'',​ și //​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țeaea 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''​. 
 + 
 +Pentru //​back-end//,​ QEMU suporta mai multe moduri, printre care: 
 +  * ''​-netdev user''​ - //​user-mode//,​ rulează în user-space și 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// și 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 și 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 ===== ===== Exerciții =====
  
-<​note ​warning+<​note ​important
-**În laborator, vom folosi ​echipamente Raspberry PI 4!**+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>​ 
 + 
 +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 ====
  
-Înainte de a începe exercițiileasigurați-vă că aveți cel puțin 10GB de storage disponibili în mașină virtuala.+<​note>​ 
 +Dacă folosiți VM-ul de laboratorpașii de mai sus au fost deja efectuați, însă tot mai trebuie să descărcați imaginea ​de debian (ultimul subtask).
 </​note>​ </​note>​
  
-1. Găsiți [[TODO|aici o imagine ​de referință]] pentru ​Raspberry PI 4.+  * Actualizați lista de pachete ''​sudo apt update''​ 
 +  * Instalați ''​git'',​ ''​vim''​ și ''​bridge-utils''​. 
 +<​code>​ 
 +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>​
  
-  * Aflați dimensiunea în MB a imaginii oficiale (''​ls ​-l %%--%%block-size=M''​)+  * Descărcați și dezarhivați o imagine de Debian Bookworm pentru Raspberry PI Model 3B+ [[https://​raspi.debian.net/​tested-images/|de aici]]. 
-  * Creați un fișier de dimensiunea necesară pentru ​imaginea completă, folosind ''​dd''​. + 
-  * Aflați ​informațiile tabelei ​de partiții a imaginii oficiale ​folosind ​''​fdisk''​+==== 1User-mode emulation ==== 
-  * Creațtabela de partiții și partițiile necesare pentru imaginea noastră+ 
-  * Partițiile imaginii nou create trebuie să aibă acelașUUID ca și imaginea veche pentru ​a o folosi cu succes. Pentru a modifica UUID-ul diskului folosiți comanda ​''​x''​ în consola deschisa de ''​fdisk''​ pentru a intra în //expert mode//. Folosiți comanda ''​m''​ pentru a vedea toate opțiunile ​și modificați //disk identifier//​ încât să corespunda cu cel listat pentru imaginea oficială.+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-A53Salvați comanda folosită pentru emulare.  
 + 
 +<​code>​ 
 +#include <​stdio.h>​ 
 +int main(void) 
 +
 +    printf("​hello world\n"​);​ 
 +
 +</​code>​ 
 + 
 +  * Utilizaţ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>​ 
 +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>​
  
 <note tip> <note tip>
-  * Puteți folosi informațiile din tabela ​de partiții pentru a găsi numărul de blocuri ai fiecărei partiții, precum și offsetul lor în cadrul diskului virtual+  * Re-citiți despre modalitățile de [[#​rulare|rulare]]
-  * Asigurați-vă că tipurile partițiilor sunt aceleași precum în imaginea oficialăPentru a schimba tipul unei partiții folosiți comanda ''​t''​ în cadrul consolei deschise de ''​fdisk'',​ urmată de codul asociat tipului pe care îl dorim.+  * Citiți introducerea acestui [[https://​www.kernel.org/​doc/​Documentation/​admin-guide/​binfmt-misc.rst| kernel feature]].
 </​note>​ </​note>​
 +==== 2. System-mode emulation ====
  
-2. În acest moment, cele doua partiții nu au niciun sistem de fișiere creat. Creați un //loop block device// pentru fiecare dintre cele doua imaginiCreați-le sistemele ​de fișiere, având grija la tipul acestora (revedeți secțiunea ​ //Formatul imaginii RaspberryPI//​). Odată create sistemele de fișiere, montați fiecare partiție într-un director.+Rulați distribuția Debian folosind QEMU în modul //system emulation//. Veți avea nevoie ​de următoarele argumente pentru emulare.
  
-<note warning+  * **Kernel-ul** de Linux, prin argumentul “-kernel ​<kernel_image_file>”. 
-In procesul ​de boot al sistemuluipartitiile sunt cautate de kernel dupa label-ul acestora. Pentru a ne asigura ca sistemul booteaza cu succesva trebui ​sa denumim partitiile conform imaginii ​de Debian.+  * 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 (procesor, memory, 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).
  
-Optiunea ''​-L''​ a comenzii ''​mkfs.ext4'',​ respectiv optiunea ''​-n''​ a comenzii ''​mkfs.vfat''​ ne ofera posibilitatea de a atribui un label atunci cand cream sistemul de fisiere.+<​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>​
  
-Paritia ​de boot trebuie ​sa aiba label-ul ''​RPI_BOOT'', ​iar partitia pentru rootfs trebuie sa aiba label-ul ''​RPI_ROOTFS''​.+Extrageți imaginea ​de kernel, dtb-ul si initrd-ul din imaginea de disc de Debian downloadată 
 +<​code>​ 
 +sudo losetup --show -fP 20220808_raspi_3_bookworm.img ​  # Notați numărul device-ului /dev/loop returnat de comanda losetup 
 +sudo mkdir /​mnt/​debian 
 +sudo mount /​dev/​loop16p1 /​mnt/​debian ​    # Înlocuiți valoarea 16 cu valoarea numărului vostru 
 +cp /​mnt/​debian/​vmlinuz-5.18.0-3-arm64 . 
 +cp /​mnt/​debian/​initrd.img-5.18.0-3-arm64 . 
 +cp /​mnt/​debian/​bcm2837-rpi-3-b.dtb . 
 +# facem unmount și deconectăm imaginea din dispozitivul bloc 
 +sudo umount /​mnt/​debian 
 +sudo losetup -d /​dev/​loop16 
 +</​code>​ 
 + 
 +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): 
 +<​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. 512MB, 1024MB). 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 hostopriti 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>​
  
-3. Ne dorim să copiem conținutul fișierelor din imaginea oficiala în imaginea noastră. Copiați conținutul directoarelor imaginii oficiale în directoarele imaginii noastre. După ce procesul de copiere s-a terminat, demontați device-urile și ștergeți loop device-urile.+==== 3. Instalați serviciul Libvirt ====
  
-<note important+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 a crea o noua mașină virtuală. 
-Conținutul partiției ​de boot trebuie copiat în partiția de boot corespondenta din imaginea noastră. În mod similarcopierea trebuie făcută intre partițiile de rootfs.+ 
 +<code
 +sudo apt install virtinst 
 +</​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 implicitkernel-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 ====
  
-  ​[[https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#boot-sequence|Procesul ​de boot al Raspberry PI]]+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>​ <​hidden>​
-  * TODO {{:si:si:lab:lab5:sol_lab5.txt | Soluție laborator}}+  * {{:si:laboratoare:qemu_solutii_2022.txt | Solutii laborator Qemu }} 
 +  * {{:si:laboratoare:​tools_solutii_2022.zip| Soluție laborator ​toolchain}}
 </​hidden>​ </​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]]
 +
 +
  
si/laboratoare/05.1699131013.txt.gz · Last modified: 2023/11/04 22:50 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