Paging in IA-32: Spieghiamo gli indirizzi “Ricorsivi”

Uno degli argomenti più ostici nell’osdev è la gestione della memoria paginata. E personalmente ci ho messo a capire un pò l’utilità dell’utilizzo dei cosidetti indirizzi logici “ricorsivi”. In questo articolo cercherò di spiegarne l’utilità e perche conviene utilizzarli.

La memoria paginata è una particolare suddivsione della memoria in blocchi di dimensione fissa, normalmente di 4k o 8k. Questo tipo di suddivisione è supportato dall’architettura IA32 anche se non si tratta del modello di default. Infatti i processori intel utilizzano sempre un modello di memoria segmentato, mentre quello paginato si attiva solo su richiesta.

Lo scopo di questo tutorial non è spiegare la paginazione per i processori IA32 ma cerca di chiarire una tecnica particolare usata con la paginazione attiva, quindi qui non troverete un tutorial  dettagliato su  come funziona la paginazione, ma mi limiterò ricordare solo lo stretto necessario utile per i miei scopi . Le strutture fondamentali usate nella memoria paginata sono la pagedir e la pagetable.

  • Una page dir è composta da 1024 elementi che puntano ognuna a ad una pagetable.
  • Una pagetable che strutturalmente è molto simile alla pagedir, contiene sempre 1024 elementi ma questa volta contengono l’indirizzo base di ogni pagina

La figura seguente mostra come sono formate rispettrivamente le entry della pagedir e della pagetable

Come potete notare, le due entry sono pressochè identiche a parte alcuni bit di controllo (fra poco vedremo come questa somiglianza ci tornerà utile).

Prima però ricordiamo anche come viene tradotto un indirizzo logico:

Come possiamo vedere dalla figura un indirizzo logico viene scomposto in 3 parti:

  • I bit dal 31 al 22 indicano l’entry della page directory. Quindi il processore prenderà questa parte di indirizzo e leggera il valore dell’entry relativa. Prendendo il base address.
  • I bit dal 21 al 12 invece selezionano l’entry della pagetable, dalla quale verrà preso l’indirizzo base (fisico)
  • I restanti bit dal 11 al 0 invece rappresentano l’offset all’interno della pagina da 4k.

Quindi l’indirizzo fisico viene come vediamo ricostruito scomponendo l’indirizzo logico in tre parti che fanno riferimento alla page dir e alla page table, e l’indirizzo della page dir viene ricavato grazie al contenuto del registro PDBR, che contiene appunto l’indirizzo base della tabella.

In tutto questo quale è il problema? E perche esiste questa tecnica chiamata paging ricorsivo? La risposta viene dopo che vi siete posti questa domanda: quando si verifica un #PF o in qualsiasi momento si verifica la necessità di leggere o modificare una entry della page dir o della page table come possiamo fare?

Con il paging abilitato non possiamo piu accedere direttamente all’indirizzo fisico (in quanto ogni volta che il processore incontra un indirizzo lo traduce secondo lo schema appena visto) , quello che sapete solo (leggendolo dal pdbr) è l’indirizzo base della pagedir. Quindi l’unica soluzione apparentemente disponibile è quella di:

  • Disabilitare temporaneamente il paging
  • Leggere/modificare le entry di pagedir e pagetable necessarie
  • Abilitare nuovamente il paging.

Ma come si può ben immaginare disabilitare/abilitare ogni volta il paging non è una bella soluzione, se non che ogni volta per modificare un indirizzo di memoria dobbiamo eseguire almeno 3 istruzioni. (se aggiungiamo disabilitazione e abilitazioen degli interrupt diventano 5) , ed è qui che ci viene in aiuto la ricorsione. Infatti questa tecnica ci permette di costruire artificialmente un indirizzo logico che punta ad ognuna delle entry sia di pagedir che di page table. Con un minimo sacrificio: 4mb di spazio di indirizzamento (ricordo che con il paging abilitato lo spazio di indirizzamento disponibile è di 4gb, Attenzione: non si tratta di memoria ma di indirizzi).

Vediamo ora come funziona. Si sfrutterà una caratteristica delle entry di pagedir e pagetable, quella che sono pressochè identiche.

Il meccanismo di traduzione indirizzi da logico a fisico, non entra nel merito dell’entry richiesta (ovvero non va a controllare se si tratta di una pagedir o di una pagetable) ,  ma semplicemente combina la pdentry con la base in PDBR, grazie alla quale ottiene l’indirizzo base dalla pagetable, a questo punto prende l’entry richiesta, e infine con l’offset si raggiunge la cella di memora desiderata.

Quindi se noi per esempio facessimo credere al meccanismo di traduzione, che una pagedir è una page table, cosa succede? Beh vediamolo insieme, il nostro indirizzo logico sarà quindi composto da 3 elementi :

PDENTRYA| PDENTRYB | OFFSET

Ora cosa contiene una pagedir? Una lista di indirizzi di pagetable, ma se l’entry della pdir considerata, punta a se stessa allora quello che è l’entry della pagetable in realta non sarà altri che una entry della pagedir stessa (PDENTRYB) quindi  dato che una entry della pagedir, punta alla base di una page table, ne abbiamo appena avuto accesso all’indirizzo base. Ora la selezione di una pagetable entry è piuttosto semplice, si utilizza l’Offset come indicatore dell’entry della pagetalbe. Ricordiamo però che dal momento che una entry di queste tabelle è di 4byte per selezionarne un elemento dobbiamo moltiplicare l’offset per 4.

Come facciamo tutto questo? Semplice: definiamo una entry della pagedir (quindi una pagetable) come la pagedir stessa. Anche se non vi è un vincolo preciso su quale elemento selezionare, la via più comoda scelta è quella di mettere tale entry nell’ultima posizione della pagedir (la 1023), così facendo lo spazio di indirizzamento non disponibile si troverà alla fine e non nel mezzo. Con questo accorgimento se vogliamo modificare un entry della pagetable dovremo costruire un indirizzo speciale con i seguenti elementi:

1023 | PDENTRY | PTENTRY

Facciamo un esempio vogliamo modificare la prima entry  (la numero 0) della 44ma pagetable quindi il nostro indirizzo sarà così composto:

1023 | 44 | 0

Che come indirizzo sarà: FFC44000 (provate a convertire in binario e vedere i valori delle 3 componenti se non ci credete).

Se invece vogliamo modificare una entry della pagedir stessa il ragionamento è simile, ma questa volta l’indirizzo da costruire è il seguente:

1023 | 1023 | PDENTRY

Questo perchè, così facendo abbiamo detto che la page dir entry è la 1023ma quindi come page table prende se stessa, ora la page table entry è sempre la 1023 quindi viene preso come indirizzo base di nuovo quello della pagedir stessa,  questo punto con lo stesso ragionamento di prima l’offset ci permette di selezionare agevolmente l’entry della pagedir.

Un breve esempio, l’indirizzo FFFFF004 punterà alla 4 entry della pagedir.

Con questo è tutto, spero di non avervi fatto venire il mal di testa 😀

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *