libce
Libreria di Calcolatori Elettronici
Caricamento in corso...
Ricerca in corso...
Nessun risultato
vm.h
Vai alla documentazione di questo file.
1
5
10
15static const int MAX_LIV = 4;
17static const int MAX_PS_LVL = 2;
21static const natq VADDR_MSBIT = (1ULL << (12 + 9 * MAX_LIV - 1));
23static const natq VADDR_MASK = VADDR_MSBIT - 1;
24
35static inline constexpr vaddr norm(vaddr a)
36{
37 return (a & VADDR_MSBIT) ? // se il bit più sign. è 1
38 (a | ~VADDR_MASK) : // setta tutti bit alti
39 (a & VADDR_MASK); // altrimenti resettali
40}
41
47static inline constexpr natq dim_region(int liv)
48{
49 natq v = 1ULL << (liv * 9 + 12);
50 return v;
51}
52
60static inline constexpr vaddr base(vaddr v, int liv)
61{
62 natq mask = dim_region(liv) - 1;
63 return v & ~mask;
64}
65
73static inline constexpr vaddr limit(vaddr v, int liv)
74{
75 natq dr = dim_region(liv);
76 natq mask = dr - 1;
77 return (v + dr - 1) & ~mask;
78}
79
82
85
87const natq BIT_P = 1U << 0;
89const natq BIT_RW = 1U << 1;
91const natq BIT_US = 1U << 2;
93const natq BIT_PWT = 1U << 3;
95const natq BIT_PCD = 1U << 4;
97const natq BIT_A = 1U << 5;
99const natq BIT_D = 1U << 6;
101const natq BIT_PS = 1U << 7;
103const natq ACCB_MASK = 0x00000000000000FF;
105const natq ADDR_MASK = 0x7FFFFFFFFFFFF000;
107
113static inline constexpr paddr extr_IND_FISICO(tab_entry e)
114{
115 return e & ADDR_MASK;
116}
117
124static inline void set_IND_FISICO(tab_entry& e, paddr f)
125{
126 e &= ~ADDR_MASK;
127 e |= f & ADDR_MASK;
128}
129
136static inline constexpr int i_tab(vaddr v, int liv)
137{
138 int shift = 12 + (liv - 1) * 9;
139 natq mask = 0x1ffULL << shift;
140 return (v & mask) >> shift;
141}
142
154static inline tab_entry& get_entry(paddr tab, natl i)
155{
157 return pd[i];
158}
159
170void set_entry(paddr tab, natl j, tab_entry se);
171
183void copy_des(paddr src, paddr dst, natl i, natl n);
184
200void set_des(paddr dst, natl i, natl n, tab_entry e);
201
204class tab_iter {
205 // ogni riga dello stack, partendo dalla 0, traccia la posizione
206 // corrente all'interno dell'albero di traduzione. La riga 0 traccia
207 // la posizione nell'albero al livello massimo, la riga 1 nel livello
208 // subito inferiore, e così via. Il livello corrente è contenuto
209 // nella variabile 'l'.
210 //
211 // Ogni riga dello stack contiene l'indirizzo fisico (tab) della
212 // tabella, del livello corrispondente, attualmente in corso di visita.
213 // La riga contiene anche un intervallo [cur, end) di indirizzi ancora
214 // da visitare in quella tabella. I campi cur e end della riga MAX_LIV,
215 // altrimenti inutilizzati, vengono usati per contenere gli estremi
216 // dell'intervallo completo passato al costruttore dell'iteratore.
217 //
218 // La terminazione della visita si riconosce da p->tab == 0.
219
220 struct stack {
221 vaddr cur, end;
222 paddr tab;
223 } s[MAX_LIV + 1];
224
225 // livello corrente
226 int l;
227
228 // puntatore alla riga corrente dello stack
229 stack *sp() { return &s[l - 1]; }
230 stack const *sp() const { return &s[l - 1]; }
231
232 // puntatore alla riga dello stack relativa al livello 'lvl'
233 stack *sp(int lvl) { return &s[lvl - 1]; }
234
235 // puntatore alla riga dello stack contenente gli estremi
236 // dell'intervallo completo
237 stack *pp() { return &s[MAX_LIV]; }
238 stack const *pp() const { return &s[MAX_LIV]; }
239
240 // true sse la visita è finita
241 bool done() const { return !sp()->tab; }
242
243public:
253 static bool valid_interval(vaddr beg, natq dim)
254 {
255 // ultimo indirizzo interno all'intervallo (se dim > 0)
256 vaddr end = beg + dim - 1;
257 return !dim || (
258 // non inizia nel buco
259 norm(beg) == beg
260 // non termina nel buco
261 && norm(end) == end
262 // estremi entrambi dalla stessa parte rispetto al buco
263 && (beg & VADDR_MSBIT) == (end & VADDR_MSBIT)
264 // non fa wrap-around
265 && end >= beg);
266 }
267
277 tab_iter(paddr tab, vaddr beg, natq dim = 1);
278
283 operator bool() const {
284 return !done();
285 }
286
290 int get_l() const {
291 return l;
292 }
293
298 bool is_leaf() const {
299 tab_entry e = get_e();
300 return !(e & BIT_P) || (e & BIT_PS) || l == 1;
301 }
302
308 vaddr get_v() const {
309 return max(pp()->cur, sp()->cur);
310 }
311
315 tab_entry& get_e() const {
316 return get_entry(sp()->tab, i_tab(sp()->cur, l));
317 }
318
323 paddr get_tab() const {
324 return sp()->tab;
325 }
326
332 bool down();
338 bool up();
344 bool right();
345
349 void next();
350
353 void post();
354
358 void next_post();
359};
360
372paddr trasforma(paddr root_tab, vaddr v);
373
378extern "C" void loadCR3(paddr dir);
379
384extern "C" paddr readCR3();
385
388extern "C" void invalida_TLB();
389
394extern "C" void invalida_entrata_TLB(vaddr v);
395
401extern "C" vaddr readCR2();
402
414
416extern paddr alloca_tab();
418extern void rilascia_tab(paddr);
420extern void inc_ref(paddr);
422extern void dec_ref(paddr);
424extern natl get_ref(paddr);
426
454template<typename T>
455vaddr map(paddr tab, vaddr begin, vaddr end, natl flags, T& getpaddr, int ps_lvl = 1)
456{
457/*
458 * Lo schema generale della funzione, escludendo i controlli sugli errori e
459 * la gestione dei flag, è il seguente:
460 * new_f = 0;
461 * for (tutto il sotto-albero visitato in pre-ordine)
462 * tab_entry& e = riferimento all'entrata corrente
463 * vaddr v = indirizzo virtuale corrente
464 * if (livello non foglia)
465 * if (tabella assente)
466 * new_f = alloca_tab();
467 * else
468 * new_f = getpaddr(v);
469 * if (new_f)
470 * inizializza 'e' in modo che punti a 'new_f'
471 */
472 vaddr v; /* indirizzo virtuale corrente */
473 int l; /* livello (del TRIE) corrente */
474 natq dr; /* dimensione delle regioni di livello ps_lvl */
475 const char* err_msg = "";
476 static const natq allowed_flags = BIT_RW | BIT_US | BIT_PWT | BIT_PCD;
477
478 // controlliamo che ps_lvl abbia uno dei valori consentiti
479 if (ps_lvl < 1 || ps_lvl > MAX_PS_LVL) {
480 fpanic("map: ps_lvl %d non ammesso (deve essere compreso tra 1 e %d)",
481 ps_lvl, MAX_PS_LVL);
482 }
483 // il mapping coinvolge sempre almeno pagine, quindi consideriamo come
484 // erronei dei parametri beg, end che non sono allineati alle pagine
485 // (di livello ps_lvl)
486 dr = dim_region(ps_lvl - 1);
487 if (begin & (dr - 1)) {
488 fpanic("map: begin=%llx non allineato alle pagine di livello %d",
489 static_cast<unsigned long long>(begin), ps_lvl);
490 }
491 if (end & (dr - 1)) {
492 fpanic("map: end=%llx non allineato alle pagine di livello %d",
493 static_cast<unsigned long long>(end), ps_lvl);
494 }
495 // controlliamo che flags contenga solo i flag che possiamo gestire
496 if (flags & ~allowed_flags) {
497 fpanic("map: flags contiene bit non validi: %llx",
498 static_cast<unsigned long long>(flags & ~allowed_flags));
499 }
500
501 // usiamo un tab_iter per percorrere tutto il sottoalbero. Si noti che
502 // il sottoalbero verrà costruito man mano che lo visitiamo.
503 //
504 // Si noti che tab_iter fa ulteriori controlli sulla validità
505 // dell'intervallo (si veda tab_iter::valid_interval in libce)
506 //
507 // mentre percorriamo l'albero controlliamo che non esistano già
508 // traduzioni per qualche indirizzo in [begin, end). In caso contrario
509 // ci fermiamo restituendo errore.
510 tab_iter it(tab, begin, end - begin);
511 for ( /* niente */ ; it; it.next()) {
512 tab_entry& e = it.get_e();
513 l = it.get_l();
514 v = it.get_v();
515 // new_f diventerà diverso da 0 se dobbiamo settare a 1 il bit
516 // P di 'e'
517 paddr new_f = 0;
518
519 if (l > ps_lvl) {
520 // per tutti i livelli non "foglia" allochiamo la
521 // tabella di livello inferiore e facciamo in modo che
522 // il descrittore corrente la punti (Si noti che la
523 // tabella potrebbe esistere già, e in quel caso non
524 // facciamo niente)
525 if (!(e & BIT_P)) {
526 new_f = alloca_tab(); // [1]
527 if (!new_f) {
528 err_msg = "impossibile allocare tabella";
529 goto error;
530 }
531 } else if (e & BIT_PS) {
532 // se PS==1 questo descrittore non contiene un
533 // puntatore alla prossima tabella, ma è già
534 // una traduzione. La traduzione è di livello
535 // più alto di quella richiesta e dunque è
536 // sicuramente preesistente.
537 err_msg = "gia' mappato";
538 goto error;
539 }
540 } else {
541 // arrivati ai descrittori di livello ps_lvl creiamo la
542 // traduzione vera e propria, salvo errori.
543 if (e & BIT_P) {
544 err_msg = "gia' mappato";
545 goto error;
546 }
547 // otteniamo il corrispondente indirizzo fisico
548 // chiedendolo all'oggetto getpaddr
549 new_f = getpaddr(v); // [2]
550 if (!new_f) {
551 err_msg = "getpaddr() ha restituito 0";
552 goto error;
553 }
554
555 // se la pagina è di livello maggiore di 1 dobbiamo
556 // settare il bit PS
557 if (l > 1)
558 e |= BIT_PS;
559
560 // settiamo anche i bit PWT e PCD, se richiesto
561 e |= (flags & (BIT_PWT|BIT_PCD));
562 }
563 if (new_f) {
564 // siamo qui perché c'è da allocare una tabella (punto
565 // [1] qui sopra) o installare una traduzione (punto
566 // [2]). Le cose da fare sono le stesse in entrambi i
567 // casi:
568
569 // 'e' non puntava a niente e ora deve puntare a new_f
570 set_IND_FISICO(e, new_f);
571 e |= BIT_P;
572
573 // dobbiamo incrementare il contatore delle entrate
574 // valide della tabella a cui 'e' appartiene
575 inc_ref(it.get_tab());
576 }
577
578 // se sono stati richiesti i bit RW e/o US, questi vanno
579 // settati su tutti i livelli (anche su eventuali entrate di
580 // livello > ps_lvl già esistenti), altrimenti non hanno
581 // effetto.
582 e |= (flags & (BIT_RW|BIT_US));
583 }
584 return end;
585
586error:
587 flog(LOG_WARN, "map: indirizzo %llx, livello %d: %s", static_cast<unsigned long long>(v), l, err_msg);
588 // it punta all'ultima entrata visitata. Se si tratta di una tabella
589 // dobbiamo provare a deallocare tutto il percorso che abbiamo creato
590 // fino a quel punto. Una chiamata ad unmap() non è sufficiente, in
591 // quanto queste tabelle potrebbero non contenere traduzioni complete.
592 paddr p = it.get_tab();
593 for (;;) {
594 if (get_ref(p))
595 break;
596 rilascia_tab(p);
597 if (!it.up())
598 break;
599 it.get_e() = 0;
600 p = it.get_tab();
601 dec_ref(p);
602 }
603 return max(v, begin);
604}
605
607template<typename T>
608vaddr map(paddr tab, vaddr begin, vaddr end, natl flags, const T& getpaddr, int ps_lvl = 1)
609{
610 T tmp = getpaddr;
611 return map(tab, begin, end, flags, tmp, ps_lvl);
612}
613
627template<typename T>
628void unmap(paddr tab, vaddr begin, vaddr end, T& putpaddr)
629{
630 tab_iter it(tab, begin, end - begin);
631 // eseguiamo una visita in ordine posticipato
632 for (it.post(); it; it.next_post()) {
633 tab_entry& e = it.get_e();
634
635 if (!(e & BIT_P))
636 continue;
637
638 paddr p = extr_IND_FISICO(e);
639 if (!it.is_leaf()) {
640 // l'entrata punta a una tabella.
641 if (!get_ref(p)) {
642 // Se la tabella non contiene più entrate
643 // valide la deallochiamo
644 rilascia_tab(p);
645 } else {
646 // altrimenti non facciamo niente
647 // (la tabella serve per traduzioni esterne
648 // all'intervallo da eliminare)
649 continue;
650 }
651 } else {
652 // l'entrata punta ad una pagina (di livello it.get_l())
653 vaddr v = it.get_v();
654 int l = it.get_l();
655 // Tutta la pagina deve essere inclusa nell'intervallo da eliminare
656 if (v < begin || v + dim_region(l - 1) - 1 > end - 1) {
657 fpanic("unmap: tentativo di riumuovere traduzione esterna a [%lx, %lx)",
658 begin, end);
659 }
660 // lasciamo al chiamante decidere cosa fare
661 putpaddr(v, p, l);
662 }
663
664 // per tutte le pagine, e per le tabelle che abbiamo
665 // deallocato, azzeriamo l'entrata 'e' e decrementiamo il
666 // contatore delle entrate valide nella tabella che la contiene
667 e = 0;
668 dec_ref(it.get_tab());
669 }
670}
671
673template<typename T>
674void unmap(paddr tab, vaddr begin, vaddr end, const T& putpaddr)
675{
676 // adattatore nel caso in cui putpaddr non sia un lvalue
677 T tmp = putpaddr;
678 unmap(tab, begin, end, tmp);
679}
680
Iteratore per la visita di un TRIE.
Definition vm.h:204
tab_iter(paddr tab, vaddr beg, natq dim=1)
inzializza un tab_iter per una visita in ordine anticipato.
Definition tab_iter.cpp:3
void post()
inizia una visita in ordine posticipato
Definition post.cpp:3
void next()
porta l'iteratore alla prossima posizione della visita in ordine anticipato
Definition next.cpp:3
tab_entry & get_e() const
descrittore corrente
Definition vm.h:315
int get_l() const
livello corrente
Definition vm.h:290
bool is_leaf() const
fermo su una foglia?
Definition vm.h:298
vaddr get_v() const
indirizzo virtuale corrente
Definition vm.h:308
static bool valid_interval(vaddr beg, natq dim)
controlla che un intervallo sia valido.
Definition vm.h:253
bool right()
prova a spostare l'iteratore di una posizione a destra nell'albero (rimanendo nel livello corrente),...
Definition right.cpp:3
bool down()
prova a spostare l'iteratore di una posizione in basso nell'albero, se possibile, altrimenti non fa n...
Definition down.cpp:3
bool up()
prova a spostare l'iteratore di una posizione in alto nell'albero, se possibile, altrimenti non fa ni...
Definition up.cpp:3
void next_post()
porta l'iteratore alla prossima posizione della visita in ordine posticipato
Definition next_post.cpp:3
paddr get_tab() const
tabella corrente
Definition vm.h:323
unsigned long vaddr
indirizzo virtuale (64bit)
Definition libce.h:60
unsigned long natq
naturale su 8 byte (64bit)
Definition libce.h:58
unsigned long paddr
indirizzo fisico (64bit)
Definition libce.h:62
unsigned int natl
naturale su 4 byte
Definition libce.h:46
void flog(log_sev sev, const char *fmt,...)
Invio di un messaggio formattato sul log.
Definition flog.cpp:6
void fpanic(const char *fmt,...)
Invia un messaggio sul log (severità ERR) ed esegue lo shutdown.
Definition fpanic.cpp:4
T max(T a, T b)
Restituisce il massimo tra due valori confrontabili.
Definition libce.h:121
static To * ptr_cast(From v)
Converte da intero a puntatore non void.
Definition libce.h:253
@ LOG_WARN
avviso
Definition libce.h:197
static const int MAX_LIV
numero di livelli del TRIE (4 o 5)
Definition vm.h:15
const natq ADDR_MASK
maschera per l'indirizzo
Definition vm.h:105
void invalida_TLB()
Invalida tutto il TLB.
natl get_ref(paddr)
invocata per leggere il contatore delle entrate valide di una tabella
Definition get_ref.cpp:4
natq tab_entry
descrittore di pagina o tabella
Definition vm.h:81
static const int MAX_PS_LVL
massimo livello supportato per le pagine di grandi dimensioni
Definition vm.h:17
static tab_entry & get_entry(paddr tab, natl i)
Accesso ad un'entrata di una tabella.
Definition vm.h:154
void loadCR3(paddr dir)
Carica un nuovo valore in cr3.
static const natq VADDR_MASK
maschera per selezionare i bit significativi di un indirizzo virtuale
Definition vm.h:23
const natq BIT_D
bit "dirty"
Definition vm.h:99
const natq BIT_PWT
bit Page Wright Through
Definition vm.h:93
static constexpr vaddr norm(vaddr a)
Normalizza un indirizzo.
Definition vm.h:35
static constexpr natq dim_region(int liv)
Calcola la dimensione di una regione del TRIE dato il livello.
Definition vm.h:47
paddr alloca_tab()
invocata quando serve una nuova tabella
Definition alloca_tab.cpp:9
void rilascia_tab(paddr)
invocata quando una tabella non serve più
void set_des(paddr dst, natl i, natl n, tab_entry e)
Inizializza (parte dei) descrittori di una tabella.
Definition set_des.cpp:3
paddr trasforma(paddr root_tab, vaddr v)
Traduzione di un indirizzo virtuale in fisico.
Definition trasforma.cpp:3
static constexpr int i_tab(vaddr v, int liv)
Indice nelle tabelle.
Definition vm.h:136
const natq BIT_US
bit utente/sistema
Definition vm.h:91
static void set_IND_FISICO(tab_entry &e, paddr f)
Imposta l'indirizzo fisico in un descrittore di pagina o tabella.
Definition vm.h:124
void set_entry(paddr tab, natl j, tab_entry se)
Scrive una entrata di una tabella.
Definition set_entry.cpp:3
void dec_ref(paddr)
invocata quando una tabella perde una entrata precedentemente valida
Definition dec_ref.cpp:4
const natq BIT_PCD
bit Page Cache Disable
Definition vm.h:95
void inc_ref(paddr)
invocata quando una tabella acquisisce una nuova entrata valida
Definition inc_ref.cpp:4
const natq BIT_PS
bit "page size"
Definition vm.h:101
static constexpr paddr extr_IND_FISICO(tab_entry e)
Estrae l'indirizzo fisico da un descrittore di pagina o tabella.
Definition vm.h:113
static constexpr vaddr limit(vaddr v, int liv)
Calcola la base della prima regione di un dato livello che giace a destra di un dato indirizzo virtua...
Definition vm.h:73
const natq BIT_RW
bit di lettura/scrittura
Definition vm.h:89
vaddr map(paddr tab, vaddr begin, vaddr end, natl flags, T &getpaddr, int ps_lvl=1)
Crea tutto il sottoalbero necessario a tradurre tutti gli indirizzi di un intervallo.
Definition vm.h:455
static constexpr vaddr base(vaddr v, int liv)
Calcola la base della regione di un dato livello a cui appartiene un dato indirizzo virtuale.
Definition vm.h:60
void invalida_entrata_TLB(vaddr v)
Invalida una entrata del TLB.
vaddr readCR2()
Lettura di cr2.
const natq BIT_P
bit di presenza
Definition vm.h:87
static const natq VADDR_MSBIT
ultimo bit significativo di un indirizzo virtuale
Definition vm.h:21
const natq ACCB_MASK
maschera per il byte di accesso
Definition vm.h:103
void copy_des(paddr src, paddr dst, natl i, natl n)
Copia descrittori da una tabella ad un'altra.
Definition copy_des.cpp:3
const natq BIT_A
bit di accesso
Definition vm.h:97
paddr readCR3()
Lettura di cr3.
void unmap(paddr tab, vaddr begin, vaddr end, T &putpaddr)
Elimina tutte le traduzioni di un intervallo.
Definition vm.h:628