This shows you the differences between two versions of the page.
so2:laboratoare:lab04:exercitii [2018/03/13 11:23] ionel.ghita [[0.5p] Intro] |
so2:laboratoare:lab04:exercitii [2019/03/12 21:54] (current) constantin.ghioc [1. [2p] Înregistrarea unui dispozitiv de tip caracter] |
||
---|---|---|---|
Line 83: | Line 83: | ||
</code> | </code> | ||
</note> | </note> | ||
+ | |||
+ | ===== Exerciții ===== | ||
<note warning> | <note warning> | ||
Line 92: | Line 94: | ||
</note> | </note> | ||
+ | <note important> | ||
+ | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | ||
+ | </note> | ||
===== [0.5p] Intro ===== | ===== [0.5p] Intro ===== | ||
Line 103: | Line 108: | ||
<note tip> | <note tip> | ||
- | La căutarea simbolurilor în [[https://elixir.bootlin.com/linux/v4.15.0/source|LXR]], asiguraţi-vă că selectaţi aceeaşi versiune de kernel ca cea din laborator(4.15.0). | + | La căutarea simbolurilor în [[https://elixir.bootlin.com/linux/v4.15/source|LXR]], asiguraţi-vă că selectaţi aceeaşi versiune de kernel ca cea din laborator (4.19). |
Pentru cscope, folosiți comanda '':cs f g //nume//'' pentru căutarea definiţiilor şi comanda '':cs f s //nume//'' pentru căutarea simbolurilor. Pentru mai multe informaţii despre comenzile disponibile folosiţi comanda '':help cscope''. | Pentru cscope, folosiți comanda '':cs f g //nume//'' pentru căutarea definiţiilor şi comanda '':cs f s //nume//'' pentru căutarea simbolurilor. Pentru mai multe informaţii despre comenzile disponibile folosiţi comanda '':help cscope''. | ||
Line 110: | Line 115: | ||
===== [10.5p] Character Device Driver ===== | ===== [10.5p] Character Device Driver ===== | ||
- | ==== 1. [1p] Înregistrarea unui dispozitiv de tip caracter ==== | + | ==== 1. [2p] Înregistrarea unui dispozitiv de tip caracter ==== |
Driver-ul va controla un singur dispozitiv cu majorul ''MY_MAJOR'' și minorul ''MY_MINOR ''(macro-urile definite în fișierul ''kernel/so2_cdev.c'') Ca prim pas, va trebui să creați fișierul de tip caracter ''/dev/so2_cdev'' folosind utilitarul ''mknod''. | Driver-ul va controla un singur dispozitiv cu majorul ''MY_MAJOR'' și minorul ''MY_MINOR ''(macro-urile definite în fișierul ''kernel/so2_cdev.c'') Ca prim pas, va trebui să creați fișierul de tip caracter ''/dev/so2_cdev'' folosind utilitarul ''mknod''. | ||
Line 117: | Line 122: | ||
Citiți secțiunea [[:so2:laboratoare:lab04#Identificator major și minor|Identificator major și minor]] din laborator. | Citiți secțiunea [[:so2:laboratoare:lab04#Identificator major și minor|Identificator major și minor]] din laborator. | ||
- | Pentru a evita folosirea ''mknod'' la fiecare boot-are a mașinii, adăugați comanda ''mknod'' cu parametrii relevanți la finalul fișierului ''qemu-vm/fsimg/etc/rcS''. | + | Pentru a evita folosirea ''mknod'' la fiecare boot-are a mașinii, adăugați comanda ''mknod'' cu parametrii relevanți la finalul fișierului ''/etc/rcS'' din mașina virtuală. |
</note> | </note> | ||
Implementați înregistrarea și deînregistrarea dispozitivului cu numele ''so2_cdev'', respectiv în funcția de intrare și cea de ieșire a modulului. Nu uitați că driver-ul controlează un singur dispozitiv. La acest exercițiu nu este nevoie să folosiți apelurile ''cdev_init'' și ''cdev_add''. Le veți folosi la exercițiul 3. | Implementați înregistrarea și deînregistrarea dispozitivului cu numele ''so2_cdev'', respectiv în funcția de intrare și cea de ieșire a modulului. Nu uitați că driver-ul controlează un singur dispozitiv. La acest exercițiu nu este nevoie să folosiți apelurile ''cdev_init'' și ''cdev_add''. Le veți folosi la exercițiul 3. | ||
+ | Urmăriți comentariile marcate cu ''TODO 1''. | ||
<note tip> | <note tip> | ||
Line 126: | Line 132: | ||
</note> | </note> | ||
- | Afișati, folosind macro-ul ''LOG_LEVEL'', un mesaj după operațiile de înregistrare, respectiv deînregistrare, care să confirme realizarea cu succes a acestora. | + | Afișati, folosind ''pr_info'', un mesaj după operațiile de înregistrare, respectiv deînregistrare, care să confirme realizarea cu succes a acestora. |
Încărcați apoi modulul în kernel<code> | Încărcați apoi modulul în kernel<code> | ||
Line 145: | Line 151: | ||
rmmod so2_cdev | rmmod so2_cdev | ||
</code> | </code> | ||
+ | |||
==== 2. [1p] Înregistrarea unui dispozitiv alocat ==== | ==== 2. [1p] Înregistrarea unui dispozitiv alocat ==== | ||
Modificați modulul de kernel pentru a încerca înregistrarea unui dispozitiv deja alocat. | Modificați modulul de kernel pentru a încerca înregistrarea unui dispozitiv deja alocat. | ||
- | Consultați [[http://lxr.free-electrons.com/source/include/uapi/asm-generic/errno-base.h?v=4.9|LXR]] pentru a identifica eroarea întoarsă la înregistrarea modulului. | + | Consultați [[https://elixir.bootlin.com/linux/v4.15/source/include/uapi/asm-generic/errno-base.h|LXR]] pentru a identifica eroarea întoarsă la înregistrarea modulului. |
Reveniți la configurația inițială a modulului. | Reveniți la configurația inițială a modulului. | ||
Line 156: | Line 163: | ||
==== 3. [1p] Implementarea deschiderii și închiderii dispozitivului ==== | ==== 3. [1p] Implementarea deschiderii și închiderii dispozitivului ==== | ||
Rulați comanda ''cat'' peste dispozitivul de tip caracter creat (''/dev/so2_cdev''). Citirea nu funcționează pentru că driver-ul nu are implementate funcțiile de deschidere, citire și închidere fișier. | Rulați comanda ''cat'' peste dispozitivul de tip caracter creat (''/dev/so2_cdev''). Citirea nu funcționează pentru că driver-ul nu are implementate funcțiile de deschidere, citire și închidere fișier. | ||
+ | Urmăriți comentariile marcate cu ''TODO 2'' pentru a realiza următoarele: | ||
- Inițializați dispozitivul | - Inițializați dispozitivul | ||
* Citiți secțiunea [[:so2:laboratoare:lab04#Înregistrarea și deînregistrarea dispozitivelor de tip caracter|Înregistrarea și deînregistrarea dispozitivelor de tip caracter]] din laborator. | * Citiți secțiunea [[:so2:laboratoare:lab04#Înregistrarea și deînregistrarea dispozitivelor de tip caracter|Înregistrarea și deînregistrarea dispozitivelor de tip caracter]] din laborator. | ||
Line 168: | Line 176: | ||
Parcurgeți secțiunea [[:so2:laboratoare:lab04#open și release|open și release]]. | Parcurgeți secțiunea [[:so2:laboratoare:lab04#open și release|open și release]]. | ||
</note> | </note> | ||
+ | |||
==== 4. [1.5p] Restricționare acces ==== | ==== 4. [1.5p] Restricționare acces ==== | ||
- | Restricționați accesul la dispozitiv cu [[:so2:laboratoare:lab03#Variabile atomice|variabile atomice]], astfel încât un singur proces să poată deschide dispozitivul la un moment dat. Restul vor primi eroarea %%"Device busy" (''-EBUSY'')%%. Restricționarea accesului se va face în funcția ''open'' expusă de driver. | + | Restricționați accesul la dispozitiv cu [[:so2:laboratoare:lab03#Variabile atomice|variabile atomice]], astfel încât un singur proces să poată deschide dispozitivul la un moment dat. Restul vor primi eroarea %%"Device busy" (''-EBUSY'')%%. Restricționarea accesului se va face în funcția ''open'' expusă de driver. Urmăriți comentariile marcate cu ''TODO 3''. |
- Adăugați o variabilă de tip ''atomic_t'' în structura dispozitivului. | - Adăugați o variabilă de tip ''atomic_t'' în structura dispozitivului. | ||
- Inițializați variabila la inițializarea dispozitivului. | - Inițializați variabila la inițializarea dispozitivului. | ||
- | - Folosiți variabila în funcția ''open'' pentru a restricționa accesul la dispozitiv. Recomandăm folosirea [[http://lxr.free-electrons.com/source/arch/x86/include/asm/atomic.h?v=4.9#L184|atomic_cmpxchg]] ((''atomic_cmpxchg'' întoarce **vechea** valoare a variabilei atomice)). | + | - Folosiți variabila în funcția ''open'' pentru a restricționa accesul la dispozitiv. Recomandăm folosirea [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/include/asm/atomic.h#L185|atomic_cmpxchg]] ((''atomic_cmpxchg'' întoarce **vechea** valoare a variabilei atomice)). |
- Resetați variabila în cadrul funcției de tip ''release'', pentru a repermite accesul la dispozitiv. | - Resetați variabila în cadrul funcției de tip ''release'', pentru a repermite accesul la dispozitiv. | ||
- Pentru a testa implementarea va trebui să simulați o utilizare îndelungată a dispozitivului. Apelați scheduler-ul la sfârşitul deschiderii dispozitivului:<code c> | - Pentru a testa implementarea va trebui să simulați o utilizare îndelungată a dispozitivului. Apelați scheduler-ul la sfârşitul deschiderii dispozitivului:<code c> | ||
Line 191: | Line 200: | ||
Mai multe detalii despre parametrii funcției găsiți [[https://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/atomic_cmpxchg.html | aici]]. | Mai multe detalii despre parametrii funcției găsiți [[https://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/atomic_cmpxchg.html | aici]]. | ||
- | Un exemplu de folosire găsiți [[http://lxr.free-electrons.com/source/lib/dump_stack.c?v=4.9#L24|aici]]. | + | Un exemplu de folosire găsiți [[https://elixir.bootlin.com/linux/v4.15/source/lib/dump_stack.c#L28|aici]]. |
</note> | </note> | ||
+ | |||
==== 5. [2.5p] Citirea de mesaje ==== | ==== 5. [2.5p] Citirea de mesaje ==== | ||
- | Implementați funcția ''read'' în driver. | + | |
+ | Implementați funcția ''read'' în driver. Urmăriți comentariile marcate cu ''TODO 4''. | ||
- Păstrați în cadrul structurii dispozitivului un buffer pe care să îl inițializați la mesajul dat de macro-ul ''MESSAGE''. Inițializarea acestui buffer se va face odată cu inițializarea dispozitivului. | - Păstrați în cadrul structurii dispozitivului un buffer pe care să îl inițializați la mesajul dat de macro-ul ''MESSAGE''. Inițializarea acestui buffer se va face odată cu inițializarea dispozitivului. | ||
- La un apel ''read'' copiați în bufferul de user space conținutul buffer-ului din kernel space. | - La un apel ''read'' copiați în bufferul de user space conținutul buffer-ului din kernel space. | ||
- | * Folosiți funcția [[http://lxr.free-electrons.com/source/include/asm-generic/uaccess.h?v=4.9#L270|copy_to_user]] pentru a copia informații din kernel space în user space. | + | * Folosiți funcția [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/uaccess.h#L152|copy_to_user]] pentru a copia informații din kernel space în user space. |
* Ignorați pentru moment parametrii ''size'' și ''offset''. Puteți presupune că buffer-ul din user space este suficient de mare. Nu este nevoie să verificați validitatea argumentului ''size'' al funcției ''read''. | * Ignorați pentru moment parametrii ''size'' și ''offset''. Puteți presupune că buffer-ul din user space este suficient de mare. Nu este nevoie să verificați validitatea argumentului ''size'' al funcției ''read''. | ||
* Valoarea întoarsă de apelul ''read'' este numărul de octeți transmiși din buffer-ul din kernel space către buffer-ul din user space. | * Valoarea întoarsă de apelul ''read'' este numărul de octeți transmiși din buffer-ul din kernel space către buffer-ul din user space. | ||
Line 225: | Line 236: | ||
Prin dereferențierea parametrului ''offset'' se permite citirea și deplasarea poziției curente din fișier. Valoarea acesteia trebuie actualizată de fiecare dată când o citire se efectuează cu succes. | Prin dereferențierea parametrului ''offset'' se permite citirea și deplasarea poziției curente din fișier. Valoarea acesteia trebuie actualizată de fiecare dată când o citire se efectuează cu succes. | ||
</note> | </note> | ||
+ | |||
==== 6. [1.5p] Scrierea de mesaje ==== | ==== 6. [1.5p] Scrierea de mesaje ==== | ||
- | Adăugați posibilitatea de a scrie un mesaj care să îl înlocuiască pe cel predefinit. Implementați funcția ''write'' în driver. | + | |
+ | Adăugați posibilitatea de a scrie un mesaj care să îl înlocuiască pe cel predefinit. Implementați funcția ''write'' în driver. Urmăriți comentariile marcate cu ''TODO 5''. | ||
Ignorați pe moment parametrul ''offset''. Puteți presupune că buffer-ul driverului este suficient de mare. Nu este nevoie să verificați validitatea argumentului ''size'' al funcției ''write''. | Ignorați pe moment parametrul ''offset''. Puteți presupune că buffer-ul driverului este suficient de mare. Nu este nevoie să verificați validitatea argumentului ''size'' al funcției ''write''. | ||
Line 241: | Line 254: | ||
</note> | </note> | ||
- | ==== 7. [2p] Operație de tip ''ioctl''. ==== | + | ==== 7. [1p] Operație de tip ''ioctl''. ==== |
- | Pentru acest exercițiu dorim să adăugăm operația ioctl ''MY_IOCTL_PRINT'' care să afișeze mesajul dat de macro-ul ''IOCTL_MESSAGE'' din driver. | + | Pentru acest exercițiu dorim să adăugăm operația ioctl ''MY_IOCTL_PRINT'' care să afișeze mesajul dat de macro-ul ''IOCTL_MESSAGE'' din driver. Urmăriți comentariile marcate cu ''TODO 6''. |
Pentru aceasta: | Pentru aceasta: | ||
- Implementați funcția ''ioctl'' în driver. | - Implementați funcția ''ioctl'' în driver. | ||
- | - Trebuie să scrieți un program în user-space (''user/so2_cdev_test.c'') care să apeleze funcția ''ioctl'' cu parametrii corespunzători. În fișierul de testare trebuie să apelați ''ioctl'' pentru fișierul dispozitivului. | ||
- Folosiți ''printk'' pentru a afișa mesajul din driver. | - Folosiți ''printk'' pentru a afișa mesajul din driver. | ||
+ | - Pentru testare, vom folosi un program în user-space (''user/so2_cdev_test.c'') care să apeleze funcția ''ioctl'' cu parametrii corespunzători. | ||
<note tip> | <note tip> | ||
- | Macrodefiniția ''MY_IOCTL_PRINT'' este definită în fișierul ''include/so2_cdev.h'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab04-tasks.zip|arhiva de sarcini]] a laboratorului (folosește ''_IOC'' pentru a defini operația) | + | Macrodefiniția ''MY_IOCTL_PRINT'' este definită în fișierul ''include/so2_cdev.h'' din scheletul laboratorului (folosește ''_IOC'' pentru a defini operația). Acest fișier antet este inclus de ambele fișiere sursă (modulul de kernel și programul de test din user space). |
Citiți secțiunile [[:so2:laboratoare:lab04#ioctl|ioctl]] și [[:so2:laboratoare:lab04#open și release|open și release]] din laborator. | Citiți secțiunile [[:so2:laboratoare:lab04#ioctl|ioctl]] și [[:so2:laboratoare:lab04#open și release|open și release]] din laborator. | ||
Line 257: | Line 270: | ||
<note tip> | <note tip> | ||
- | Pentru a compila codul sursă de user space folosiți compilatorul ''gcc-5''. Pentru aceasta rulați comanda:<code> | + | Codul sursă de user space este compilat automat în momentul în care rulați ''make build''. După ''make copy'', veți găsi executabilul în directorul ''/home/root/skels/device_drivers/user''. |
- | /usr/bin/gcc-5 -m32 -static -Wall -g -o so2_cdev_test so2_cdev_test.c | + | |
- | </code> | + | |
- | + | ||
- | Executabilul rezultat trebuie să îl copiați pe mașina virtuală la fel ca modulul de kernel și să îl rulați pe mașina virtuală pentru a valida implementarea corectă a ''ioctl''. | + | |
</note> | </note> | ||
==== Extra ==== | ==== Extra ==== | ||
+ | |||
+ | <note>Urmăriți comentariile marcate cu ''TODO 7''.</note> | ||
1. (**2 karma**) Ioctl cu transmitere de mesaje | 1. (**2 karma**) Ioctl cu transmitere de mesaje |