Differences

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

Link to this comparison view

so2:laboratoare:lab05:exercitii [2018/03/21 08:53]
ionel.ghita [1. [1.5p] Alocare de porturi I/O]
so2:laboratoare:lab05:exercitii [2019/03/20 11:49] (current)
gabriel.bercaru [1. [2.5p] Alocare de porturi I/O] - fixed typo ('kdb' instead of 'kbd')
Line 104: Line 104:
 Obiectivul exercițiilor următoare este să creați un driver pentru a intercepta tastele apăsate (//​keylogger//​). Driver-ul va intercepta IRQ-ul destinat controller-ului de tastatură și va inspecta codurile tastelor primite, stocându-le într-un buffer. Obiectivul exercițiilor următoare este să creați un driver pentru a intercepta tastele apăsate (//​keylogger//​). Driver-ul va intercepta IRQ-ul destinat controller-ului de tastatură și va inspecta codurile tastelor primite, stocându-le într-un buffer.
  
-==== 1. [1.5p] Alocare de porturi I/O ====+==== 1. [2.5p] Alocare de porturi I/O ====
  
 Pentru început ne propunem să alocăm spații din zona I/O pentru dispozitive hardware. Vom vedea că nu putem să alocăm spațiu pentru tastatură întrucât regiunea aferentă e deja alocată. Apoi vom aloca spațiu I/O pentru porturi care nu sunt folosite. Pentru început ne propunem să alocăm spații din zona I/O pentru dispozitive hardware. Vom vedea că nu putem să alocăm spațiu pentru tastatură întrucât regiunea aferentă e deja alocată. Apoi vom aloca spațiu I/O pentru porturi care nu sunt folosite.
Line 141: Line 141:
  
 Descărcați modulul din kernel folosind comanda<​code>​ Descărcați modulul din kernel folosind comanda<​code>​
-rmmod kdb+rmmod kbd
 </​code>​ și verificați acum conținutul fișierului ''/​proc/​ioports'';​ observți că a dispărut alocarea spațiului I/O pentru cele două porturi. </​code>​ și verificați acum conținutul fișierului ''/​proc/​ioports'';​ observți că a dispărut alocarea spațiului I/O pentru cele două porturi.
  
Line 148: Line 148:
 </​note>​ </​note>​
  
-==== 2. [1.5p] Rutina de tratare a întreruperii ====+==== 2. [2p] Rutina de tratare a întreruperii ====
  
 Urmărim să înregistrăm o rutină de tratare a întreruperii de tastatură. Veți scrie o funcție (rutină) de tratare a înteruperii și o veți înregistra folosind ''​[[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​. Urmărim să înregistrăm o rutină de tratare a întreruperii de tastatură. Veți scrie o funcție (rutină) de tratare a înteruperii și o veți înregistra folosind ''​[[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​.
Line 207: Line 207:
 <note tip> <note tip>
 Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<​code>​ Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<​code>​
- printk(LOG_LEVEL ​"IRQ%dscancode=0x%x (%u%c)\n", + pr_info("IRQ %dscancode=0x%x (%u) pressed=%d ch=%c\n",​ 
- irq_no, scancode, scancode, ​scancode);+ irq_no, scancode, scancode, ​pressed, ch);
 </​code>​ </​code>​
 unde ''​scancode''​ este valoarea registrului citit cu ajutorul funcției ''​i8042_read_data()''​. unde ''​scancode''​ este valoarea registrului citit cu ajutorul funcției ''​i8042_read_data()''​.
Line 256: Line 256:
 <note tip> <note tip>
 Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<​code>​ Pentru afișare în cadrul handler-ului de întrerupere folosiți o construcție de forma:<​code>​
- printk(LOG_LEVEL "IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n",​+ pr_info(LOG_LEVEL "IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n",​
  irq_no, scancode, scancode, pressed, ch);  irq_no, scancode, scancode, pressed, ch);
 </​code>​ </​code>​
Line 268: Line 268:
 === c. [1p] Colectarea caracterelor în buffer === === c. [1p] Colectarea caracterelor în buffer ===
  
-Dorim să colectăm caracterele apăsate (nu și celelalte taste) într-un buffer care apoi va fi citit din user space.+Dorim să colectăm caracterele apăsate (nu și celelalte taste) într-un buffer ​circular ​care apoi va fi citit din user space.
  
-Actualizați handler-ul de tratare a întreruperii (''​kbd_interrupt_handler''​) astfel încât un caracter ASCII citit să fie adăugat la sfârșitul buffer-ului aferent dispozitivului. Dacă buffer-ul este plin, caracterul ​va fi ignorat.+Actualizați handler-ul de tratare a întreruperii (''​kbd_interrupt_handler''​) astfel încât un caracter ASCII citit să fie adăugat la sfârșitul buffer-ului aferent dispozitivului. Dacă buffer-ul este plin, caracterul ​cel mai vechi o sa fie suprascris.
  
 <note tip> <note tip>
Line 277: Line 277:
 </​code>​ </​code>​
  
-Dimensiunea curentă a buffer-ului este dată de câmpul ''​buf_idx''​ din structura de tip ''​struct kbd''​ aferentă dispozitivului. ​Va trebui să incrementați valoarea acestei variabile la fiecare caracter ​adăugat în buffer.+Dimensiunea curentă a buffer-ului este dată de câmpul ''​count''​ din structura de tip ''​struct kbd''​ aferentă dispozitivului. ​Câmpul ''​put_idx''​ specifică următoarea poziție de scriere, iar ''​get_idx''​ următoarea poziție de citire. Urmăriți implementarea funcției ''​put_char''​ pentru a observa cum sunt adăugate datele ​în buffer-ul circular.
 </​note>​ </​note>​
- 
-<note tip> 
-Capacitatea buffer-ului,​ peste care nu puteți scrie, este dată de macro-ul ''​BUFFER_SIZE''​. 
-</​note>​ 
- 
 ==== 4. [2p] Citirea bufferului printr-un apel ''​read''​ ==== ==== 4. [2p] Citirea bufferului printr-un apel ''​read''​ ====
  
Line 304: Line 299:
 </​note>​ </​note>​
  
-Pentru copierea în user space veți folosi apelul [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​uaccess.h#​L152|copy_to_user]]. Întrucât nu puteți folosi ​''​copy_to_user'' ​în timp ce aveți un spinlock luat, trebuie să folosiți un buffer ​temporar.+Completați funcția ''​get_char'' ​pentru a obține următorul caracter care trebuie ​citit din buffer. Aveți grijă la implementarea buffer-ului circular (să nu citiți dincolo de dimensiunea ​buffer-ului și de poziția de scriere).
  
-<​note ​tip+Pentru copierea în user space veți folosi apelul [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​arch/​x86/​include/​asm/​uaccess.h#​L249|put_user]] pentru fiecare caracter în parte, obținut folosind funcția ''​get_char''​. 
-Buffer-ul temporar șdimensiunea acestuia sunt indicate de câmpurile ​''​tmp_buf'' ​și ''​tmp_buf_idx'' ​din structura de tip dispozitiv.+ 
 +<​note ​warning
 +Nu putețfolosi ​''​copy_to_user'' ​sau ''​put_user'' ​în timp ce aveți un spinlock luat.
  
 Urmăriți în laboratorul 4 secțiunea [[:​so2:​laboratoare:​lab04#​accesul_la_spatiul_de_adresa_al_procesului|Accesul la spațiul de adresă al procesului]]. Urmăriți în laboratorul 4 secțiunea [[:​so2:​laboratoare:​lab04#​accesul_la_spatiul_de_adresa_al_procesului|Accesul la spațiul de adresă al procesului]].
Line 327: Line 324:
 O să obțineți conținutul bufferului keylogger-ului din kernel space. O să obțineți conținutul bufferului keylogger-ului din kernel space.
  
-==== 5. [3p] Curățarea buffer-ului ​dacă este tastată o parolă ​====+==== 5. [1.5p] Curățarea buffer-ului ​la scriere ​====
  
-Dorim să %%"​curățăm"​%% buffer-ul ​dacă este tastată o parolă. În acel moment buffer-ul va fi resetat: conținutul său va fi resetat și dimensiunea va fi ''​0''​.+Dorim să %%"​curățăm"​%% buffer-ul ​atunci când se scrie în device node-ul asociat driver-ului. În acel moment buffer-ul va fi resetat: conținutul său va fi resetat și dimensiunea va fi ''​0''​.
  
-Implementarea o veți face în fișierul ''​kbd.c''​. ​În rutina de tratare a întreruperii veți reține în câmpul ​''​passcnt'' ​câte caractere din parolă au fost potriviteParola este definită de macro-urile ​''​MAGIC_WORD''​ și ''​MAGIC_WORD_LEN''​. +Implementarea o veți face în fișierul ''​kbd.c''​. ​Urmăriți comentariile marcate cu ''​TODO 5''​. ​Implementați funcția ​''​reset_buffer''​ și adăugați operația ''​write''​ la ''​kbd_fops''​.
- +
-Când se detectează întreaga parolă, curătați buffer-ul. Pentru curățare, reinițializați câmpul ​''​buf_idx''​ la valoarea ​''​0'' ​și umpleți cu ''​0''​-uri buffer-ul. +
- +
-La primirea unui caracter nou, verificați dacă acesta corespunde cu caracterul de pe poziția curentă din parolă. În caz afirmativ, incrementați contorul; altfel, resetați counterul la 0.+
  
 <note tip> <note tip>
-În momentul resetării buffer-ului, ​în handler-ul de întrerupere ​va trebui să folosiți spinlock-ul pentru accesul exclusiv la buffer. Este posibil ca rutina de read să fie procesată de pe alt procesor și să acceseze buffer-ul în momentul în care handler-ul de întrerupere îl modifică. Veți folosi funcțiile [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L308|spin_lock]] și [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L348|spin_unlock]].+În momentul resetării buffer-ului,​ va trebui să folosiți spinlock-ul ​cu dezactivarea întreruperilor ​pentru accesul exclusiv la buffer.
  
 Parcurgeți secțiunea [[:​so2:​laboratoare:​lab05#​utilizare_spinlock-uri|Utilizarea spinlock-uri]]. Parcurgeți secțiunea [[:​so2:​laboratoare:​lab05#​utilizare_spinlock-uri|Utilizarea spinlock-uri]].
Line 350: Line 343:
 cat /dev/kbd cat /dev/kbd
 </​code>​ </​code>​
-Apăsați pe taste și apoi apăsați conținutul macro-ului ''​MAGIC_WORD''​ (adică ​''​root''​și apoi consultați din nou conținutul buffer-ului. Ar trebui să fie resetat.+Apăsați pe taste și apoi rulați comanda ​''​echo "​clear"​ > /dev/kbd''​ și apoi consultați din nou conținutul buffer-ului. Ar trebui să fie resetat.
  
 ===== Extra ===== ===== Extra =====
- 
-==== [1 karma] Resetarea buffer-ului în rutina de read ==== 
- 
-Implementarea curentă este problematică pentru că rutina de tratare a întreruperii este cea care face resetarea buffer-ului,​ lucru care este consumator de timp și nerecomandat să fie făcut în context întrerupere. 
- 
-O soluție este să actualizăm comportamentul astfel încât resetarea buffer-ului să se producă la apelul funcției ''​kbd_read()''​. După ce informația este copiată în user space, buffer-ul este resetat. Nu mai resetăm în momentul tastării ​ 
- 
-Simplificați implementarea în așa fel încât resetarea buffer-ului să se producă în funcția ''​kbd_read()''​ imediat după copierea datele în user space. 
- 
-<note tip> 
-În momentul în care copiați datele în buffer-ul temporare ''​tmp_buf''​ resetați buffer-ul principal: adică umpleți buffer-ul ''​buf''​ cu zero-uri și inițialiați ''​buf_idx''​ la ''​0''​. 
-</​note>​ 
- 
-Pentru verificarea după citirea din ''/​dev/​kbd''​ veți avea buffer-ul resetat. Citiri ulterioare vor găsi buffer-ul gol. 
  
 ==== [3 karma] Utilizare kfifo ==== ==== [3 karma] Utilizare kfifo ====
so2/laboratoare/lab05/exercitii.1521615204.txt.gz · Last modified: 2018/03/21 08:53 by ionel.ghita
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