/* P = pte.present V = pte.valid W = pte.copyonwrite D = pte.disk N = pti.nailed > 0 il bit D indica se la pagina ha gia` un indirizzo su disco. se D=1 l'indirizzo e` in pti.diskaddr. - i bit N indicano un contatore di lock, se N=0 la pagina puo` essere swappata, altrimenti se N>0 e` lockata. - il bit W serve per indicare che se si verifica un #PF dovuto alla scrittura sulla pagina significa che la pagina era condivisa, e che non puo` piu` esserlo. - il bit V serve per indicare che l'indirizzo e` valido anche se il bit P=0. ------ */ #include "clib.h" #include "i386.h" #include "phmem.h" #include "paging.h" #include "globals.h" // avlGSDACTUWP // flags standard per pde 000000000011 /* * G = global S = Size * D = dirty A = accessed * C = Cache Disable T = write trought * U = user level W = writable * P = present */ #define VALID_TASK(t) if (t == NULL || (t->cr3 < (pde_t*)0x110000) || \ (t->cr3 >= (pde_t*)get_fault_address)) \ DEBUG_FATAL("invalid task"); /* * *r = l'indirizzo della dword che contiene i flags per la pagina * che rappresenta `va'. la pagina puo` essere da 4M o 4K. * * ritorna EFAULT se l'indirizzo non e` valido, o 0 se ok. * * - per sapere se la pagina e` da 4M o 4K : pte.size * - per sapere se l'indirizzo e` virtuale : pte.present == 0 * - se ritorna EFAULT, e *pte == NULL indica che la directory * di pagina non e` presente, altrimenti *pte punta comunque * al pte che avra` .present == 0 && .valid == 0 */ int get_pte_addr(tss_t *task, addr_t va, pte_t** r) { int pdi; VALID_TASK(task); if (r) *r = NULL; pdi = (u32)va >> 22; if (task->cr3[pdi].present) { if (task->cr3[pdi].size) { // size 4Mb if (r) *r = (pte_t*)&task->cr3[pdi]; return 0; } else { // size 4K pte_t* pte; int pti = ((u32)va >> 12) & 0x3FF; pte = (pte_t*)(task->cr3[pdi].pte_addr shl 12); if (r) *r = &pte[pti]; if (pte[pti].present || pte[pti].valid) return 0; } } return EFAULT; } int va2ph(tss_t* task, addr_t va, phaddr_t* pa) { int r; pte_t *pte; r = get_pte_addr(task, va, &pte); if (r == 0) { if (pte->size == 0) *pa = (phaddr_t)((pte->addr shl 12) + ((u32)va & 0xFFF)); else *pa = (phaddr_t)(((pte->addr & 0xFFC00) shl 12) + ((u32)va & 0x3FFFFF)); } return r; } static phaddr_t alloc_pte(void) { phaddr_t pa; pa = alloc_free_pages(2); ASSERT(pa != NULL); memset(pa, 0, 4096*2); DEBUG_VERBOSE("alloc_pte %xh\n", pa); return pa + 4096; } static void free_pte(pte_t *pte) { free_pages((phaddr_t) pte2ptinfo(pte), 2); } bool valid_va(tss_t* task, addr_t va, size_t len) { int pages; VALID_TASK(task); if (len == 0) return true; for (pages = (len+0xfff) shr 12; pages == 0; pages--, va += 4096) { if (get_pte_addr(task, va, NULL) != 0) return false; } return true; } phaddr_t alloc_standard_task_pd(tss_t *task) { u32 i; ASSERT(task != NULL); task->cr3 = alloc_free_0page(); /* Page Directory */ if (task->cr3 == NULL) goto _error; #if 0 /* FIX [23/09/97] la memoria fisica e` mappata solo per il kernel*/ /* * mappa 1:1 la memoria assegnata al kernel, calcolata come : * * * ???????? eventuale altra memoria libera * ________ fine memoria assegnata al kernel @1 * HHHHHHHH * ________ fine kernel, inizio memoria gestita da phmem.c * KKKKKKK * _______ stub_stat.kernel_base * ?????? memoria xms usata da altri programmi (puo` essere 0) * 1M_______ fine memoria dos * * 0 _______ inizio memoria DOS * * viene mappata tutta la memoria, da 0 fino a @1 (fault_address) * */ if (map_memory(task, 0, 0, 0x110, pfWritable) != 0x110) goto _error; if (map_memory(task, (addr_t)0x110000, (addr_t)0x110000, 0x1000-0x110, pfWritable+pfGlobal) != (0x1000-0x110)) goto _error; pa = get_fault_address(); // in pa l'indirizzo della prima pagina non utilizzabile if (pa > 0x3fffff) { r = map_memory4M(task, 0, 0, ((u32)pa + 0x3fffff) shr 22, pfWritable+pfGlobal); if (r == ENOSYS) { /* la cpu non ha le pagine da 4M, usa 4K */ r = map_memory(task, 0, 0, ((u32)pa + 0xfff) shr 12, pfWritable+pfGlobal); } } if (r <= 0) goto _error; #endif /* mappa il kernel : utilizza la stessa struttura di pagine utilizzate dal kernel, percio` qualunque struttura dinamica creata dal K sara` visibile dagli altri task. */ for (i = (u32)VA_KERNEL_BASE shr 22; i < (u32)VA_KERNEL_LIMIT shr 22; i++) { task->cr3[i] = tss_kernel.cr3[i]; } return task->cr3; _error: ; if (task->cr3) free_task_pgtable(task); WARNING("return NULL"); return NULL; } void free_task_pgtable(tss_t *task) { pte_t *pte; u32 pdi, pti; VALID_TASK(task); /* * libera TUTTE le pagine da 4K che hanno indirizzo < VA_KERNEL_BASE * */ for (pdi = 0; pdi < (u32)VA_KERNEL_BASE shr 22; pdi++) { if (task->cr3[pdi].present && !task->cr3[pdi].size) { // solo le pagine presenti da 4K ... pte = (pte_t*)(task->cr3[pdi].pte_addr shl 12); for(pti = 0; pti < 1024; pti++) { if (pte[pti].present) { free_page((phaddr_t)(pte[pti].addr shl 12)); } } free_pte(pte); } } free_page(task->cr3); task->cr3 = 0; } addr_t set_memory_limit(tss_t *task, addr_t base, addr_t max_addr) { addr_t va = base; VALID_TASK(task); /* solo il kernel puo` allocare memoria kernel! :) */ if (task->cs != KERNEL_CS) { if (max_addr > VA_KERNEL_BASE) max_addr = VA_KERNEL_BASE; } else { if (max_addr > VA_KERNEL_LIMIT) max_addr = VA_KERNEL_LIMIT; } while (va < max_addr) { int pdi; pdi = (u32)va shr 22; if (!task->cr3[pdi].present) { task->cr3[pdi].pte_addr = ((u32)alloc_pte() shr 12); /* se non c'e` + memoria esci dal while */ if (task->cr3[pdi].pte_addr == 0) break; task->cr3[pdi].present = 1; /* imposta i flag meno restrittivi per la directory, i "veri" flag li hanno i pte. */ task->cr3[pdi].writable = 1; task->cr3[pdi].user = 1; } va += 1 shl 22; } max_addr = va; /* * NB: la memoria kernel NON diminuisce mai, e non lo deve fare : * la memoria allocata con `valloc' e` marcata come `globale', * percio` non puo` essere liberata, a meno di resettare il TLB * globale .. */ #if 0 /* FIXME: questo e` da ottimizzare, e da debuggare */ while (va < VA_KERNEL_BASE) { free_vpages(task, va, 1); va += 4096; } #endif return max_addr; } /* alloc_vpages non alloca mai nuovi pte, percio` la memoria sara` limitata dalla chiamata a set_memory_limit, che imposta il + alto indirizzo utilizzabile. [23/09/97] per evitare inutili #PF sarebbe meglio allocare veramente la memoria se ce n'e` molta libera, almeno per le prime pagine, che di solito sono quelle utilizzate subito dopo. */ addr_t alloc_vpages(tss_t *task, addr_t at, int npages, page_flags_t pf) { int n = 0; pte_t *pte; addr_t va_lim; VALID_TASK(task); if (npages <= 0) return NULL; /* limiti diversi per task di cpl 0 e gli altri */ if (task->cs == KERNEL_CS) va_lim = VA_LIMIT; else va_lim = VA_KERNEL_BASE; #ifndef NDEBUG if (!(task->eflags & F_VM) && at < VA_BASE) { WARNING("alloc_vpages at %x for non VM86 task!\n",at); } #endif while (at < va_lim) { /* se la directory di pagina e` presente ... */ if ((get_pte_addr(task, at, &pte) == EFAULT) && pte) { if (++n == npages) { for (; npages > 0; npages--, at-= 4096) { get_pte_addr(task, at, &pte); clear_pte_flags(pte, true); pte->valid = 1; pte->writable = 1; if (pf & pfUser) pte->user = 1; } return at+4096; } } else n = 0; at += 4096; } WARNING("return NULL"); return 0; } int free_vpages(tss_t *task, addr_t va, int npages) { if (npages <= 0) return EINVAL; VALID_TASK(task); if (valid_va(task, va, npages shl 12)) { for (; npages > 0; npages--) { pte_t* pte; get_pte_addr(task, va, &pte); if (pte->present) { free_page((phaddr_t)(pte->addr shl 12)); } if (pte->disk) { /* * FIXME: bisogna liberare la pagina da disco * */ } clear_pte_flags(pte, true); } return 0; } else { WARNING("return EFAULT"); return EFAULT; } } int lock_memory(tss_t* task, addr_t va, size_t size) { if (size == 0) return 0; VALID_TASK(task); if (valid_va(task, va, size)) { int npages; for (npages = (size+0xfff) shr 12; npages == 0; npages--) { pte_t* pte; pti_t* ptif; get_pte_addr(task, va, &pte); ptif = pte2ptinfo(pte); if (ptif->lock < 7) ptif->lock++; } return 0; } else return EFAULT; } int unlock_memory(tss_t* task, addr_t va, size_t size) { if (size == 0) return 0; VALID_TASK(task); if (valid_va(task, va, size)) { int npages; for (npages = (size+0xfff) shr 12; npages == 0; npages--) { pte_t* pte; pti_t* ptif; get_pte_addr(task, va, &pte); ptif = pte2ptinfo(pte); if (ptif->lock > 0) ptif->lock--; } return 0; } else return EFAULT; } int map_memory(tss_t *task, addr_t va, phaddr_t pa, int npages, page_flags_t pf) { int i, pdi, pti; pte_t* pte; VALID_TASK(task); if (npages < 1) return EINVAL; (u32)pa >>= 12; (u32)va >>= 12; for (i = 0; i < npages; i++) { pti = (u32)va & 0x3FF; pdi = (u32)va >> 10; if (!task->cr3[pdi].present) { phaddr_t ph; ph = alloc_pte(); if (ph == 0) { WARNING("map_memory map only %xh pages\n",i) return i; } clear_pde_flags(&(task->cr3[pdi])); task->cr3[pdi].present = 1; task->cr3[pdi].writable = 1; task->cr3[pdi].user = 1; task->cr3[pdi].pte_addr = (u32)ph shr 12; } pte = (pte_t*)(task->cr3[pdi].pte_addr shl 12); clear_pte_flags(&(pte[pti]), true); pte[pti].addr = (dword)pa; pte[pti].present = 1; if (pf & pfWritable) pte[pti].writable = 1; if (pf & pfUser) pte[pti].user = 1; if ((pf & pfGlobal) && _cr4.pge) task->cr3[pdi].global = 1; va++; pa++; } return i+1; } int map_memory4M(tss_t *task, addr_t va, phaddr_t pa, int npages, page_flags_t pf) { int i, pdi; VALID_TASK(task); if (!_cr4.pse) return ENOSYS; if (npages < 1) return EINVAL; (u32)pa >>= 12; (u32)pa &= ~0x3FF; // 4Mb align (u32)va >>= 22; for (i = 0; i < npages; i++) { pdi = (u32)va; clear_pde_flags(&(task->cr3[pdi])); task->cr3[pdi].present = 1; task->cr3[pdi].size = 1; task->cr3[pdi].pte_addr = (dword)pa; if (pf & pfWritable) task->cr3[pdi].writable = 1; if (pf & pfUser) task->cr3[pdi].user = 1; if (pf & pfGlobal && _cr4.pge) task->cr3[pdi].global = 1; va++; pa += 1 shl 10; } return npages; } int commit_addr(tss_t *task, addr_t va) { pte_t *pte; if (get_pte_addr(task, va, &pte) == 0) { phaddr_t ph; pti_t *pti; if (pte->present == 1) return 0; ph = alloc_free_0page(); if (!ph) return ENOMEM; pte->addr = (u32)ph shr 12; pte->present = 1; pte->dirty = 0; if (pte->disk) { /* * bisogna leggere le pagina da disco */ } pti = pte2ptinfo(pte); pti->counter = 0x80; // contatore 0x80 indica `appena allocata' return 0; } else { return EFAULT; } } int uncommit_addr(tss_t *task, addr_t va) { pte_t *pte; if (get_pte_addr(task, va, &pte) == 0) { pti_t *ptif; if (pte->present == 0) return 0; ptif = pte2ptinfo(pte); if (ptif->lock != 0) return EPERM; if (!pte->disk) { /* * spazio per la nuova pagina nello swap file ptif->diskaddr = ... */ pte->disk = 1; pte->dirty = 1; } if (pte->dirty) { /* * bisogna scrivere la pagina su disco * all'indirizzo ptif->diskaddr */ } free_page((phaddr_t)((u32)pte->addr shl 12)); return 0; } else return EFAULT; } int read_from_task(tss_t* tss, addr_t va, phaddr_t dest, int count) { phaddr_t ph; int i,r; VALID_TASK(tss); /* loop non ottimizzato !*/ for (i = 0; i < count; i++) { commit_addr(tss, va); r = va2ph(tss, va, &ph); if (r != 0) return r; *(u8*)dest = *(u8*)ph; dest++; va++; } return 0; } int write_to_task(tss_t* tss, addr_t va, phaddr_t src, int count) { phaddr_t ph; int i,r; VALID_TASK(tss); /* loop non ottimizzato !*/ while (count > 0) { i = 4096 - ((u32)va & 0xFFF); if (i > count) i = count; r = commit_addr(tss, va); if (r != 0) goto _ret; r = va2ph(tss, va, &ph); if (r != 0) goto _ret; memcpy(ph, src, i); va += i; src+= i; count -= i; } return 0; _ret: ; WARNING("return %xh",r); return r; }