📄 pmap.c~
字号:
// Some of it is in use, some is free. Where is the kernel? // Which pages are used for page tables and other data structures? // // Change the code to reflect this. int i,con; LIST_INIT(&page_free_list); for (i = 0; i < npage; i++) { con=1; if(i!=0&&i<basemem/PGSIZE) con=0; if(i>=((int)boot_freemem-KERNBASE)/PGSIZE) con=0; pages[i].pp_ref = con; if(!con) LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); }}//// Initialize a Page structure.// The result has null links and 0 refcount.// Note that the corresponding physical page is NOT initialized!//static voidpage_initpp(struct Page *pp){ memset(pp, 0, sizeof(*pp));}//// Allocates a physical page.// Does NOT set the contents of the physical page to zero -// the caller must do that if necessary.//// *pp_store -- is set to point to the Page struct of the newly allocated// page//// RETURNS // 0 -- on success// -E_NO_MEM -- otherwise //// Hint: use LIST_FIRST, LIST_REMOVE, and page_initpp// Hint: pp_ref should not be incremented intpage_alloc(struct Page **pp_store){ // Fill this function in struct Page *p = LIST_FIRST(&page_free_list); if(p == NULL) return -E_NO_MEM; LIST_REMOVE(p, pp_link); page_initpp(p); *pp_store = p; return 0;}//// Return a page to the free list.// (This function should only be called when pp->pp_ref reaches 0.)//voidpage_free(struct Page *pp){ // Fill this function in LIST_INSERT_HEAD(&page_free_list, pp, pp_link);}//// Decrement the reference count on a page,// freeing it if there are no more refs.//voidpage_decref(struct Page* pp){ if (--pp->pp_ref == 0) page_free(pp);}// Given 'pgdir', a pointer to a page directory, pgdir_walk returns// a pointer to the page table entry (PTE) for linear address 'va'.// This requires walking the two-level page table structure.//// If the relevant page table doesn't exist in the page directory, then:// - If create == 0, pgdir_walk returns NULL.// - Otherwise, pgdir_walk tries to allocate a new page table// with page_alloc. If this fails, pgdir_walk returns NULL.// - pgdir_walk sets pp_ref to 1 for the new page table.// - Finally, pgdir_walk returns a pointer into the new page table.//// Hint: you can turn a Page * into the physical address of the// page it refers to with page2pa() from kern/pmap.h.pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create){ // Fill this function in pte_t *p; struct Page *pp; physaddr_t pa; pgdir = &pgdir[PDX(va)]; if(!(*pgdir & PTE_P)){ if(!create){ return NULL;} if(page_alloc(&pp) != 0) return NULL; pp->pp_ref++; pa = page2pa(pp); memset(KADDR(PTE_ADDR(pa)), 0 ,PGSIZE); *pgdir = pa | PTE_U | PTE_P | PTE_W; } p = (pte_t*) KADDR(PTE_ADDR((*pgdir))); return &p[PTX(va)];}//// Map the physical page 'pp' at virtual address 'va'.// The permissions (the low 12 bits) of the page table// entry should be set to 'perm|PTE_P'.//// Details// - If there is already a page mapped at 'va', it is page_remove()d.// - If necessary, on demand, allocates a page table and inserts it into// 'pgdir'.// - pp->pp_ref should be incremented if the insertion succeeds.// - The TLB must be invalidated if a page was formerly present at 'va'.//// RETURNS: // 0 on success// -E_NO_MEM, if page table couldn't be allocated//// Hint: The TA solution is implemented using pgdir_walk, page_remove,// and page2pa.//intpage_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) {// Fill this function in pte_t * p; if((p=pgdir_walk(pgdir,va,1))==0) return -E_NO_MEM; if(*p&PTE_P) { if(pa2page(PTE_ADDR(*p))!=pp) { page_remove(pgdir,va); pp->pp_ref++; } *p=page2pa(pp)|perm|PTE_P; } else { *p=page2pa(pp)|perm|PTE_P; pp->pp_ref++; } return 0;}//// Map [la, la+size) of linear address space to physical [pa, pa+size)// in the page table rooted at pgdir. Size is a multiple of PGSIZE.// Use permission bits perm|PTE_P for the entries.//// This function is only intended to set up the ``static'' mappings// above UTOP. As such, it should *not* change the pp_ref field on the// mapped pages.//// Hint: the TA solution uses pgdir_walkstatic voidboot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm){ int i=0; for(i=0;i<size;i+=PGSIZE) { *(pgdir_walk(pgdir,(void *)(la+i),1))=PTE_ADDR(pa+i)|perm|PTE_P; } // Fill this function in}//// Return the page mapped at virtual address 'va'.// If pte_store is not zero, then we store in it the address// of the pte for this page. This is used by page_remove// but should not be used by other callers.//// Return 0 if there is no page mapped at va.//// Hint: the TA solution uses pgdir_walk and pa2page.//struct Page *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store){ // Fill this function in *pte_store=pgdir_walk(pgdir,va,0); if(*pte_store != 0&&(**pte_store&PTE_P) ) return pa2page(PTE_ADDR(**pte_store)); return NULL;}//// Unmaps the physical page at virtual address 'va'.// If there is no physical page at that address, silently does nothing.//// Details:// - The ref count on the physical page should decrement.// - The physical page should be freed if the refcount reaches 0.// - The pg table entry corresponding to 'va' should be set to 0.// (if such a PTE exists)// - The TLB must be invalidated if you remove an entry from// the pg dir/pg table.//// Hint: The TA solution is implemented using page_lookup,// tlb_invalidate, and page_decref.//voidpage_remove(pde_t *pgdir, void *va){ // Fill this function in pte_t* pp; struct Page *p=page_lookup(pgdir,va,&pp); if( p!=NULL){ page_decref(p); *pp=0; tlb_invalidate(pgdir,va); } }//// Invalidate a TLB entry, but only if the page tables being// edited are the ones currently in use by the processor.//voidtlb_invalidate(pde_t *pgdir, void *va){ // Flush the entry only if we're modifying the current address space. // For now, there is only one address space, so always invalidate. invlpg(va);}static uintptr_t user_mem_check_addr;//// Check that an environment is allowed to access the range of memory// [va, va+len) with permissions 'perm | PTE_P'.// Normally 'perm' will contain PTE_U at least, but this is not required.// 'va' and 'len' need not be page-aligned; you must test every page that// contains any of that range. You will test either 'len/PGSIZE',// 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages.//// A user program can access a virtual address if (1) the address is below// ULIM, and (2) the page table gives it permission. These are exactly// the tests you should implement here.//// If there is an error, set the 'user_mem_check_addr' variable to the first// erroneous virtual address.//// Returns 0 if the user program can access this range of addresses,// and -E_FAULT otherwise.//intuser_mem_check(struct Env *env, const void *va, size_t len, int perm){ // LAB 3: Your code here. // LAB 3: Your code here. uintptr_t va_start = (uintptr_t) va; uintptr_t va_end = va_start + len; uintptr_t vp = PPN(va_start) << PGSHIFT; uintptr_t vp_end = PPN(va_end) << PGSHIFT; perm |= PTE_P; for (; vp <= vp_end; vp += PGSIZE) { pte_t *ppte = pgdir_walk(env->env_pgdir, (void *) vp, 0); if ((ppte != NULL) && ((*ppte & perm) == perm)) continue; user_mem_check_addr = (vp < va_start) ? va_start : vp; return -E_FAULT; } return 0;}//// Checks that environment 'env' is allowed to access the range// of memory [va, va+len) with permissions 'perm | PTE_U'.// If it can, then the function simply returns.// If it cannot, 'env' is destroyed.//voiduser_mem_assert(struct Env *env, const void *va, size_t len, int perm){ if (user_mem_check(env, va, len, perm | PTE_U) < 0) { cprintf("[%08x] user_mem_check assertion failure for " "va %08x\n", curenv->env_id, user_mem_check_addr); env_destroy(env); // may not return }}// check page_insert, page_remove, &cstatic voidpage_check(void){ struct Page *pp, *pp0, *pp1, *pp2; struct Page_list fl; pte_t *ptep, *ptep1; void *va; int i; // should be able to allocate three pages pp0 = pp1 = pp2 = 0; assert(page_alloc(&pp0) == 0); assert(page_alloc(&pp1) == 0); assert(page_alloc(&pp2) == 0); assert(pp0); assert(pp1 && pp1 != pp0); assert(pp2 && pp2 != pp1 && pp2 != pp0); // temporarily steal the rest of the free pages fl = page_free_list; LIST_INIT(&page_free_list); // should be no free memory assert(page_alloc(&pp) == -E_NO_MEM); // there is no page allocated at address 0 assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL); // there is no free memory, so we can't allocate a page table assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0); // free pp0 and try again: pp0 should be used for page table page_free(pp0); assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0); assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0)); assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1)); assert(pp1->pp_ref == 1); assert(pp0->pp_ref == 1); // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0); assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2)); assert(pp2->pp_ref == 1); // should be no free memory assert(page_alloc(&pp) == -E_NO_MEM); // should be able to map pp2 at PGSIZE because it's already there assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0); assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2)); assert(pp2->pp_ref == 1); // pp2 should NOT be on the free list // could happen in ref counts are handled sloppily in page_insert assert(page_alloc(&pp) == -E_NO_MEM); // check that pgdir_walk returns a pointer to the pte ptep = KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)])); assert(pgdir_walk(boot_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE)); // should be able to change permissions too. assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0); assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2)); assert(pp2->pp_ref == 1); assert(*pgdir_walk(boot_pgdir, (void*) PGSIZE, 0) & PTE_U); // should not be able to map at PTSIZE because need free page for page table assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0); // insert pp1 at PGSIZE (replacing pp2) assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0); // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ... assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1)); assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1)); // ... and ref counts should reflect this assert(pp1->pp_ref == 2); assert(pp2->pp_ref == 0); // pp2 should be returned by page_alloc assert(page_alloc(&pp) == 0 && pp == pp2); // unmapping pp1 at 0 should keep pp1 at PGSIZE page_remove(boot_pgdir, 0x0); assert(check_va2pa(boot_pgdir, 0x0) == ~0); assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1)); assert(pp1->pp_ref == 1); assert(pp2->pp_ref == 0); // unmapping pp1 at PGSIZE should free it page_remove(boot_pgdir, (void*) PGSIZE); assert(check_va2pa(boot_pgdir, 0x0) == ~0); assert(check_va2pa(boot_pgdir, PGSIZE) == ~0); assert(pp1->pp_ref == 0); assert(pp2->pp_ref == 0); // so it should be returned by page_alloc assert(page_alloc(&pp) == 0 && pp == pp1); // should be no free memory assert(page_alloc(&pp) == -E_NO_MEM); #if 0 // should be able to page_insert to change a page // and see the new data immediately. memset(page2kva(pp1), 1, PGSIZE); memset(page2kva(pp2), 2, PGSIZE); page_insert(boot_pgdir, pp1, 0x0, 0); assert(pp1->pp_ref == 1); assert(*(int*)0 == 0x01010101); page_insert(boot_pgdir, pp2, 0x0, 0); assert(*(int*)0 == 0x02020202); assert(pp2->pp_ref == 1); assert(pp1->pp_ref == 0); page_remove(boot_pgdir, 0x0); assert(pp2->pp_ref == 0);#endif // forcibly take pp0 back assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0)); boot_pgdir[0] = 0; assert(pp0->pp_ref == 1); pp0->pp_ref = 0; // check pointer arithmetic in pgdir_walk page_free(pp0); va = (void*)(PGSIZE * NPDENTRIES + PGSIZE); ptep = pgdir_walk(boot_pgdir, va, 1); ptep1 = KADDR(PTE_ADDR(boot_pgdir[PDX(va)])); assert(ptep == ptep1 + PTX(va)); boot_pgdir[PDX(va)] = 0; pp0->pp_ref = 0; // check that new page tables get cleared memset(page2kva(pp0), 0xFF, PGSIZE); page_free(pp0); pgdir_walk(boot_pgdir, 0x0, 1); ptep = page2kva(pp0); for(i=0; i<NPTENTRIES; i++) assert((ptep[i] & PTE_P) == 0); boot_pgdir[0] = 0; pp0->pp_ref = 0; // give free list back page_free_list = fl; // free the pages we took page_free(pp0); page_free(pp1); page_free(pp2); cprintf("page_check() succeeded!\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -