pte_t * pml4e_walk(pml4e_t *pml4e, const void *va, int create) { struct Page *newPage = NULL; //if(!create) cprintf("va = %0x, pml4e[PML4(va)] = %0x\n", va, pml4e[PML4(va)]); if (!pml4e[PML4(va)]) { if (!create) return NULL; else { newPage = page_alloc(0); if (newPage == 0) { return NULL; } else { newPage->pp_ref++; pml4e[PML4(va)] = page2pa(newPage) | PTE_U | PTE_W | PTE_P; memset(page2kva(newPage), 0x00, PGSIZE); } } } pdpe_t* pdpe = (pdpe_t*)(KADDR((PTE_ADDR(pml4e[PML4(va)])))); pte_t *result = pdpe_walk(pdpe, va, create); if (!result && newPage) { pml4e[PML4(va)] = 0; newPage->pp_ref = 0; page_free(newPage); } //return result + PTX(va); if (result) { return result + PTX(va); } else { return result; } }
// // Initialize the kernel virtual memory layout for environment e. // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly, // and initialize the kernel portion of the new environment's address space. // Do NOT (yet) map anything into the user portion // of the environment's virtual address space. // // Returns 0 on success, < 0 on error. Errors include: // -E_NO_MEM if page directory or table could not be allocated. // static int env_setup_vm(struct Env *e) { int i, r; struct Page *p = NULL; // Allocate a page for the page directory if ((r = page_alloc(&p)) < 0) return r; // Now, set e->env_pgdir and e->env_cr3, // and initialize the page directory. // // Hint: // - The VA space of all envs is identical above UTOP // (except at VPT and UVPT, which we've set below). // See inc/memlayout.h for permissions and layout. // Can you use boot_pgdir as a template? Hint: Yes. // (Make sure you got the permissions right in Lab 2.) // - The initial VA below UTOP is empty. // - You do not need to make any more calls to page_alloc. // - Note: pp_ref is not maintained for most physical pages // mapped above UTOP -- but you do need to increment // env_pgdir's pp_ref! // LAB 3: Your code here. e->env_pgdir = page2kva(p); e->env_cr3 = page2pa(p); memset(e->env_pgdir, 0, PGSIZE); p->pp_ref++; for(i = PDX(UTOP); i < NPDENTRIES; i++){ e->env_pgdir[i] = boot_pgdir[i]; } // VPT and UVPT map the env's own page table, with // different permissions. e->env_pgdir[PDX(VPT)] = e->env_cr3 | PTE_P | PTE_W; e->env_pgdir[PDX(UVPT)] = e->env_cr3 | PTE_P | PTE_U; //cprintf("env_setup_vm end!!\n"); return 0; }
// // Allocate the receive frame area. // // Initialize the RFA setting each rds's link pointer to point to the next RFD // in the ring. The pointers need to be physical addresses because a DMA ring // is created to be used by the device and a device on the PCI bus does not // have access to the CPU's MMU to translate virtual addresses into physical // addresses. // void e100_rfa_alloc(void) { int i, r; struct Page *pp; struct rfd *rfd = NULL, *tail = NULL; for (i = 0; i < RFASIZE; i++) { // Allocate a page for each command block. // Must zero out the contents of the page and // increment the reference count for it. if ((r = page_alloc(&pp)) != 0) panic("e100_rfa_alloc: %e\n", r); memset(page2kva(pp), 0, PGSIZE); ++pp->pp_ref; // Initialize the RFD rfd = page2kva(pp); rfd->pa = page2pa(pp); rfd->size = ETH_FRAME_LEN; if (i == 0) { e100.rfds = rfd; } else { // Extend the RFA by inserting the RFD // after the current tail in the list. tail->link = rfd->pa; tail->next = rfd; rfd->prev = tail; } // Set the new tail. tail = rfd; } // Complete the ring. tail->link = e100.rfds->pa; tail->next = e100.rfds; e100.rfds->prev = tail; e100.rfds_avail = RFASIZE; e100.rfd_to_clean = tail; e100.rfd_to_use = e100.rfds; }
int page_insert(pgd_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) { pte_t *ptep = get_pte(pgdir, la, 1); if (ptep == NULL) { return -E_NO_MEM; } page_ref_inc(page); if (*ptep & PTE_P) { struct Page *p = pte2page(*ptep); if (p == page) { page_ref_dec(page); } else { page_remove_pte(pgdir, la, ptep); } } *ptep = page2pa(page) | PTE_P | perm; tlb_invalidate(pgdir, la); return 0; }
// // 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'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // 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'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // However, try not to distinguish this case in your code, as this // frequently leads to subtle bugs; there's an elegant way to handle // everything in one code path. // // 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. // int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) { // Fill this function in pte_t *ptep = pgdir_walk(pgdir, va, true); if (!ptep) return -E_NO_MEM; //这儿非常trick! //如果va, pp和以前的调用的page_insert相同,且pp->pp_ref++写在page_remove() //下面的话, page_remove()就会把pp放到page_free_list上了 //所以必须先ref++,再检测page_remove() pp->pp_ref++; if (*ptep & PTE_P) page_remove(pgdir, va); *ptep = page2pa(pp) | perm | PTE_P; //page directory的权限 pgdir[PDX(va)] |= *ptep & 0xfff; tlb_invalidate(pgdir, va); return 0; }
// // Initialize the kernel virtual memory layout for environment e. // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly, // and initialize the kernel portion of the new environment's address space. // Do NOT (yet) map anything into the user portion // of the environment's virtual address space. // // Returns 0 on success, < 0 on error. Errors include: // -E_NO_MEM if page directory or table could not be allocated. // static int env_setup_vm(struct Env *e) { int i, r; struct Page *p = NULL; // Allocate a page for the page directory if ((r = page_alloc(&p)) < 0) return r; // Now, set e->env_pgdir and e->env_cr3, // and initialize the page directory. // Hint: // - Remember that page_alloc doesn't zero the page. // - The VA space of all envs is identical above UTOP // (except at VPT and UVPT, which we've set below). // See inc/memlayout.h for permissions and layout. // Can you use boot_pgdir as a template? Hint: Yes. // (Make sure you got the permissions right in Lab 2.) // - The initial VA below UTOP is empty. // - You do not need to make any more calls to page_alloc. // - Note: In general, pp_ref is not maintained for // physical pages mapped only above UTOP, but env_pgdir // is an exception -- you need to increment env_pgdir's // pp_ref for env_free to work correctly. // - The functions in kern/pmap.h are handy. // LAB 3: Your code here. //sunus,DEC 6,2010 p->pp_ref++; e->env_pgdir = page2kva(p); e->env_cr3 = page2pa(p); memset(e->env_pgdir, 0, PDX(UTOP) * 4); memmove(&(e->env_pgdir[PDX(UTOP)]), &(boot_pgdir[PDX(UTOP)]),(1024 - PDX(UTOP)) * 4); //sunus,DEC 6,2010 // VPT and UVPT map the env's own page table, with // different permissions. e->env_pgdir[PDX(VPT)] = e->env_cr3 | PTE_P | PTE_W; e->env_pgdir[PDX(UVPT)] = e->env_cr3 | PTE_P | PTE_U; return 0; }
/** * Allocate CB_MAX_NUM pages, each page for a control block */ static void cbl_alloc () { int i, r; struct Page *p; struct cb *prevcb = NULL; struct cb *currcb = NULL; // Allocate physical page for Control block for (i = 0; i < CB_MAX_NUM; i++) { if ((r = page_alloc (&p)) != 0) panic ("cbl_init: run out of physical memory! %e\n", r); p -> pp_ref ++; memset (page2kva (p), 0, PGSIZE); currcb = (struct cb *)page2kva (p); currcb->phy_addr = page2pa (p); if (i == 0) nic.cbl.start = currcb; else { prevcb->cb_link = currcb->phy_addr; prevcb->next = currcb; currcb->prev = prevcb; } prevcb = currcb; } prevcb->cb_link = nic.cbl.start->phy_addr; nic.cbl.start->prev = prevcb; prevcb->next = nic.cbl.start; nic.cbl.cb_avail = CB_MAX_NUM; nic.cbl.cb_wait = 0; nic.cbl.front = nic.cbl.start; nic.cbl.rear = nic.cbl.start->prev; }
// // 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'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // 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'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // Don't be tempted to write special-case code to handle this // situation, though; there's an elegant way to address it. // // 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. // int page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) { pte_t* pte = pgdir_walk(pgdir, va, 0); physaddr_t ppa = page2pa(pp); // If some page is already mapped there if (pte != NULL) { if (*pte & PTE_P) page_remove(pgdir, va); // also invalidates tlb if (page_free_list == pp) page_free_list = page_free_list->pp_link; } else { pte = pgdir_walk(pgdir, va, 1); // Allocate new page table if (pte == NULL) return -E_NO_MEM; // failed to alloc page table } *pte = ppa | perm | PTE_P; pp->pp_ref++; tlb_invalidate(pgdir, va); return 0; }
// // 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'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // 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'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // However, try not to distinguish this case in your code, as this // frequently leads to subtle bugs; there's an elegant way to handle // everything in one code path. // // 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. // int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) { // Fill this function in // SUNUS, 28, October, 2013 struct PageInfo *page; pte_t *pte; pte = pgdir_walk(pgdir, va, 1); if (!pte) { // indicate that there is no free pages. return -E_NO_MEM; } pp->pp_ref++; if (*pte) { page_remove(pgdir, va); } *pte = page2pa(pp); *pte |= (PTE_P|perm); tlb_invalidate(pgdir, va); return 0; }
/** * page_insert - build the map of phy addr of an Page with the linear addr @la * @param pgdir page directory * @param page the page descriptor of the page to be inserted * @param la logical address of the page * @param perm permission of the page * @return 0 on success and error code when failed */ int page_insert(pgd_t *pgdir, struct Page *page, uintptr_t la, pte_perm_t perm) { pte_t *ptep = get_pte(pgdir, la, 1); if (ptep == NULL) { return -E_NO_MEM; } page_ref_inc(page); if (*ptep != 0) { if (ptep_present(ptep) && pte2page(*ptep) == page) { page_ref_dec(page); goto out; } page_remove_pte(pgdir, la, ptep); } out: ptep_map(ptep, page2pa(page)); ptep_set_perm(ptep, perm); mp_tlb_update(pgdir, la); return 0; }
// // Return a page to the free list. // (This function should only be called when pp->pp_ref reaches 0.) // void page_free(struct Page *pp) { // Fill this function in if(pp->pp_ref==0) { if(page_free_list==NULL) { pp->pp_link=page_free_list; page_free_list=pp; return; } if(page2pa(pp)<page2pa(page_free_list)) { pp->pp_link=page_free_list; page_free_list=pp; return; } struct Page* skip=page_free_list; int found=0; while(skip->pp_link!=NULL) { if(page2pa(pp)>page2pa(skip)&& page2pa(pp)<page2pa(skip->pp_link)) { found=1; break; } skip=skip->pp_link; } if(found==0) { skip->pp_link=pp; pp->pp_link=NULL; return; } if(found==1) { struct Page* temp=skip->pp_link; skip->pp_link=pp; pp->pp_link=temp; } } else { panic("still have reference:%d\n",pp->pp_ref); } }
// check page_insert, page_remove, &c, with an installed kern_pgdir static void check_page_installed_pgdir(void) { struct Page *pp, *pp0, *pp1, *pp2; struct Page *fl; pte_t *ptep, *ptep1; uintptr_t va; int i; // check that we can read and write installed pages pp1 = pp2 = 0; assert((pp0 = page_alloc(0))); assert((pp1 = page_alloc(0))); assert((pp2 = page_alloc(0))); page_free(pp0); memset(page2kva(pp1), 1, PGSIZE); memset(page2kva(pp2), 2, PGSIZE); page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W); assert(pp1->pp_ref == 1); assert(*(uint32_t *)PGSIZE == 0x01010101U); page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W); assert(*(uint32_t *)PGSIZE == 0x02020202U); assert(pp2->pp_ref == 1); assert(pp1->pp_ref == 0); *(uint32_t *)PGSIZE = 0x03030303U; assert(*(uint32_t *)page2kva(pp2) == 0x03030303U); page_remove(kern_pgdir, (void*) PGSIZE); assert(pp2->pp_ref == 0); // forcibly take pp0 back assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); kern_pgdir[0] = 0; assert(pp0->pp_ref == 1); pp0->pp_ref = 0; // free the pages we took page_free(pp0); cprintf("check_page_installed_pgdir() succeeded!\n"); }
// 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. // // bluesea // pgdir_walk具体返回的是: // 虚拟地址va, 所在的页面对应的page table 表项的地址,所以是二级页表page table // 的表项的地址,而非page dir的表项 // (理由分析见check_page()中的相关分析) // 并且是该地址的虚拟地址! // // // 下面这个需求可能和这个想法有矛盾:PTE_P置为0,即缺页的时候本应该由缺页中断处理。 // 那是另外故事,在这儿,pgdir_walk基本上只用于初始化内核虚拟内存的映射, // 所以缺页新alloc page table没什么问题。 // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // (注:这种情况下也是返回页表项的地址,而页目录的地址。页表项的各个FLAG不用管 // 只需要把页目录对应的位置PTE_P置位即可。) // // Hint 1: you can turn a PageInfo * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // Fill this function in // bluesea uint32_t pdx = PDX(va), ptx = PTX(va); pde_t *pt = 0; if (pgdir[pdx] & PTE_P){ pt = KADDR(PTE_ADDR(pgdir[pdx])); return &pt[ptx]; } if (!create) return NULL; struct PageInfo *page = page_alloc(ALLOC_ZERO); if (!page) return NULL; page->pp_ref = 1; pgdir[pdx] = page2pa(page) | PTE_P | PTE_U; pt = page2kva(page); //pt[ptx] = PTE_U; return &pt[ptx]; }
// // 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'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // 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'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // However, try not to distinguish this case in your code, as this // frequently leads to subtle bugs; there's an elegant way to handle // everything in one code path. // // 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. // int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) { // Find the page_table_entry and create if needed pte_t * page_table_entry = pgdir_walk(pgdir, va, 1); if (page_table_entry == NULL) { return -E_NO_MEM; } pp->pp_ref++; // If it already exists, then remove it if (*page_table_entry & PTE_P) { // This will remove the page and tlb invalidate it page_remove(pgdir, va); } *page_table_entry = page2pa(pp) | perm | PTE_P; return 0; }
void ap_init(void) { gdt_init(per_cpu_ptr(cpus, bcpuid)); tls_init(per_cpu_ptr(cpus, bcpuid)); kprintf("CPU%d alive\n", myid()); /* load new pagetable(shared with bsp) */ pmm_init_ap(); idt_init(); // init interrupt descriptor table /* test pmm */ struct Page *p = alloc_pages(2); kprintf("I'm %d, get 0x%016llx(PA)\n", myid(), page2pa(p)); free_pages(p, 2); lapic_init(); proc_init_ap(); atomic_inc(&bsync); /* let BSP know we are up */ intr_enable(); // enable irq interrupt cpu_idle(); }
int page_alloc(struct Page **pp) { // Fill this function in struct Page *ppage_temp; ppage_temp = LIST_FIRST(&page_free_list); //printf("%x\n",ppage_temp); //printf("pages__%x\n",ppage_temp); if (ppage_temp != NULL) { *pp = ppage_temp; LIST_REMOVE(ppage_temp, pp_link); page_initpp(*pp); bzero((void *)KADDR(page2pa(ppage_temp)), BY2PG); return 0; } return -E_NO_MEM; }
pmd_t * get_pmd(pgd_t *pgdir, uintptr_t la, bool create) { #if PMXSHIFT == PUXSHIFT return get_pud(pgdir, la, create); #else /* PMXSHIFT == PUXSHIFT */ pud_t *pudp; if ((pudp = get_pud(pgdir, la, create)) == NULL) { return NULL; } if (!(*pudp & PTE_P)) { struct Page *page; if (!create || (page = alloc_page()) == NULL) { return NULL; } set_page_ref(page, 1); uintptr_t pa = page2pa(page); memset(VADDR_DIRECT(pa), 0, PGSIZE); *pudp = pa | PTE_U | PTE_W | PTE_P; } return &((pmd_t *)VADDR_DIRECT(PUD_ADDR(*pudp)))[PMX(la)]; #endif /* PMXSHIFT == PUXSHIFT */ }
int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte) { // Fill this function in Pde *pgdir_entryp; Pte *pgtable; struct Page *ppage; pgdir_entryp = (Pde *)(&pgdir[PDX(va)]); pgtable = (Pte *)KADDR(PTE_ADDR(*pgdir_entryp)); // pgtable = PTE_ADDR(*pgdir_entryp); //printf(" in pgdir_walk pgtable_entryp=%x\n",pgtable); if ((*pgdir_entryp & PTE_V) == 0) { //printf("pgdir_walk:come 1\n"); if (create == 0) { *ppte = 0; return 0; } else { //alloc a page for page table. if (page_alloc(&ppage) != 0) { //cannot alloc a page for page table *ppte = 0; return -E_NO_MEM; } pgtable = (Pte *)KADDR(page2pa(ppage)); *pgdir_entryp = PADDR(pgtable) | PTE_V | PTE_R; ppage->pp_ref++; } } //printf("pgdir_walk:come 2\n"); if (ppte) { *ppte = (Pte *)(&pgtable[PTX(va)]); } //printf("out of pgdir_walk\n"); return 0; }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // directory more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // need to handle permission!!! uintptr_t pd_index=0, pt_index=0; physaddr_t pa_ptba, pa_pte, pde_perm, pte_perm; pte_t *va_ptba=NULL, *va_pte=NULL; struct PageInfo *req_page; pd_index = PDX(va); //check address va_ptba+pt_index if it is correct pointer arithmatic or not. // permissions given for directory table entry are PTE_P and PTE_W //*(pgdir+pd_index) = *(pgdir+pd_index) | PTE_P | PTE_W ; pa_ptba = *(pgdir+pd_index); if(!(pa_ptba & PTE_P)) { // setting up page table for requested virtual address. if(create) { req_page = page_alloc(ALLOC_ZERO); if(req_page==NULL) return NULL; req_page->pp_ref++; pa_ptba = page2pa(req_page) | PTE_P | PTE_U | PTE_W; *(pgdir+pd_index) = pa_ptba; } else return NULL; } pde_perm = PGOFF(pa_ptba); pa_ptba = PTE_ADDR(pa_ptba); va_ptba = KADDR(pa_ptba); pt_index = PTX(va); va_pte = va_ptba + pt_index; return va_pte; }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // Fill this function in pde_t *pde; pte_t *pgtab; struct Page *pp; pde = &pgdir[PDX(va)]; if (*pde & PTE_P) { pgtab = (pte_t *)KADDR(PTE_ADDR(*pde)); } else { if (!create) return 0; if ((pp = page_alloc(ALLOC_ZERO)) == 0) return 0; pp->pp_ref = 1; pgtab = (pte_t *)KADDR(page2pa(pp)); *pde = PADDR(pgtab) | PTE_P | PTE_W | PTE_U; } return &pgtab[PTX(va)]; }
// // Check that the pages on the page_free_list are reasonable. // static void check_page_free_list(bool only_low_memory) { struct Page *pp; unsigned pdx_limit = only_low_memory ? 4 : NPDENTRIES; int nfree_basemem = 0, nfree_extmem = 0; char *first_free_page; if (!page_free_list) panic("'page_free_list' is a null pointer!"); // if there's a page that shouldn't be on the free list, // try to make sure it eventually causes trouble. for (pp = page_free_list; pp; pp = pp->pp_link) if (PDX(page2pa(pp)) < pdx_limit) memset(page2kva(pp), 0x97, 128); first_free_page = (char *) boot_alloc(0); for (pp = page_free_list; pp; pp = pp->pp_link) { // check that we didn't corrupt the free list itself assert(pp >= pages); assert(pp < pages + npages); assert(((char *) pp - (char *) pages) % sizeof(*pp) == 0); // check a few pages that shouldn't be on the free list assert(page2pa(pp) != 0); assert(page2pa(pp) != IOPHYSMEM); assert(page2pa(pp) != EXTPHYSMEM - PGSIZE); assert(page2pa(pp) != EXTPHYSMEM); assert(page2pa(pp) < EXTPHYSMEM || page2kva(pp) >= first_free_page); if (page2pa(pp) < EXTPHYSMEM) ++nfree_basemem; else ++nfree_extmem; } assert(nfree_basemem > 0); assert(nfree_extmem > 0); }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // directory more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // Fill this function in pde_t* pgdir_entry = &pgdir[PDX(va)]; pte_t* pgtb_entry = NULL; struct PageInfo * pg = NULL; if (!(*pgdir_entry & PTE_P)){ if(create){ pg = page_alloc(1); if (!pg) return NULL; memset(page2kva(pg), 0, PGSIZE); pg->pp_ref += 1; *pgdir_entry = page2pa(pg)|PTE_P|PTE_U|PTE_W; }else{ return NULL; } } pgtb_entry = KADDR(PTE_ADDR(*pgdir_entry)); return &pgtb_entry[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'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // 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'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // However, try not to distinguish this case in your code, as this // frequently leads to subtle bugs; there's an elegant way to handle // everything in one code path. // // 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. // int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) { // Look for the page table entry. int create = 1; pte_t *table_entry = pgdir_walk(pgdir, va, create); // No page table exists and we don't have enough memory to create one. if (table_entry == NULL) { return -E_NO_MEM; } // Checks against accidentally removing the same page as we're inserting. pp->pp_ref++; if (*table_entry & PTE_P) { // Remove page from table before creating a new mapping. page_remove(pgdir, va); } // Map table entry, and add extra permissions to page directory. *table_entry = page2pa(pp) | PTE_P | perm; pgdir[PDX(va)] |= perm; return 0; }
int mon_showmapping(int argc, char **argv, struct Trapframe *tf) { uint32_t va_begin = 0, va_end = 0; char *endptrb, *endptre; CHECKPARA(argc != 2 && argc != 3); if (argc == 2) { va_begin = ROUNDDOWN((uint32_t)strtol(argv[1], &endptrb, 0),PGSIZE); va_end = va_begin; CHECKPARA( *endptrb != '\0'); } else if (argc == 3) { va_begin = ROUNDDOWN((uint32_t)strtol(argv[1], &endptrb, 0),PGSIZE); va_end = ROUNDUP((uint32_t)strtol(argv[2], &endptre, 0),PGSIZE); CHECKPARA(*endptrb != '\0' || *endptre != '\0'); } cprintf("Virtual\tPhysical\tFlags\t\tRefer\n"); while(va_begin <= va_end) { struct PageInfo *pp; pte_t *pteptr; char buf[13]; pp = page_lookup(kern_pgdir, (void *)va_begin, &pteptr); if (pp == NULL || *pteptr ==0) cprintf("0x%08x\t%s\t\t%s\t\t%d\n", va_begin, "None", "None", 0); else cprintf("0x%08x\t0x%08x\t%s\t%d\n", va_begin, page2pa(pp),"Flag2Str"/*flag2str(*pteptr, buf)*/, pp->pp_ref ); va_begin += PGSIZE; } return 0; }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { uintptr_t page_directory_index = PDX(va); uintptr_t page_table_index = PTX(va); pde_t page_directory_entry = pgdir[page_directory_index]; pte_t* page_table_entry; if (page_directory_entry & PTE_P) { // Page table exists page_table_entry = (pte_t*) KADDR(PTE_ADDR(page_directory_entry)); return &page_table_entry[page_table_index]; } // If it does not exist and create is false, return NULL if (create == 0) { return NULL; } struct PageInfo *page = page_alloc(ALLOC_ZERO); // Make sure that the allocation succeeded first if (page == NULL) { return NULL; } physaddr_t addr = page2pa(page); page_table_entry = (pte_t *) page2kva(page); // Set the page table's permissions pgdir[page_directory_index] = addr | PTE_P | PTE_W | PTE_U; page->pp_ref++; return &page_table_entry[page_table_index]; }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { struct PageInfo *new_page_table; pte_t *result; physaddr_t table_addr = 0x0; // Reference the page directory array. pde_t dir_entry = pgdir[PDX(va)]; // If page table is not found in the directory and create is set, try page_alloc(). if (dir_entry & PTE_P) { // Table is already present. table_addr = PTE_ADDR(dir_entry); } else { if (!create) { // For lookups only. return NULL; } // Allocate a new page table. new_page_table = page_alloc(ALLOC_ZERO); if (!new_page_table) { // Allocation failed, exit. return NULL; } // Increment reference count and set page directory to point to page table's physical address. new_page_table->pp_ref++; table_addr = page2pa(new_page_table); pgdir[PDX(va)] = table_addr | PTE_P | PTE_W; } // Since our table address is a physical address, we need to index into it using PTX times // physaddr_t. This ensures that addresses are aligned on 2-byte boundaries. result = (pte_t *) KADDR(table_addr + sizeof(physaddr_t) * PTX(va)); return result; }
// 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. // // The relevant page table page might not exist yet. // If this is true, and create == false, then pgdir_walk returns NULL. // Otherwise, pgdir_walk allocates a new page table page with page_alloc. // - If the allocation fails, pgdir_walk returns NULL. // - Otherwise, the new page's reference count is incremented, // the page is cleared, // and pgdir_walk returns a pointer into the new page table page. // // Hint 1: you can turn a Page * into the physical address of the // page it refers to with page2pa() from kern/pmap.h. // // Hint 2: the x86 MMU checks permission bits in both the page directory // and the page table, so it's safe to leave permissions in the page // more permissive than strictly necessary. // // Hint 3: look at inc/mmu.h for useful macros that mainipulate page // table and page directory entries. // pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // Fill this function in // SUNUS, 23, October, 2013 pde_t *pde; pte_t *pte; pte_t *entry; struct PageInfo *p; pde = &pgdir[PDX(va)]; if ((!*pde) && create) { p = page_alloc(ALLOC_ZERO); if (!p) return NULL; p->pp_ref++; *pde = page2pa(p); *pde |= (PTE_P|PTE_U|PTE_W); } else if (!*pde) return NULL; entry = (pde_t *)PTE_ADDR(*pde); pte = &entry[PTX(va)]; return (pte_t *)KADDR((pte_t)pte); }
// check page_insert, page_remove, &c static void check_page(void) { struct Page *pp, *pp0, *pp1, *pp2; struct Page *fl; pte_t *ptep, *ptep1; void *va; uintptr_t mm1, mm2; int i; extern pde_t entry_pgdir[]; // should be able to allocate three pages pp0 = pp1 = pp2 = 0; assert((pp0 = page_alloc(0))); assert((pp1 = page_alloc(0))); assert((pp2 = page_alloc(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; page_free_list = 0; // should be no free memory assert(!page_alloc(0)); // there is no page allocated at address 0 assert(page_lookup(kern_pgdir, (void *) 0x0, &ptep) == NULL); // there is no free memory, so we can't allocate a page table assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) < 0); // free pp0 and try again: pp0 should be used for page table page_free(pp0); assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) == 0); assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); assert(check_va2pa(kern_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(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2)); assert(pp2->pp_ref == 1); // should be no free memory assert(!page_alloc(0)); // should be able to map pp2 at PGSIZE because it's already there assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); assert(check_va2pa(kern_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(0)); // check that pgdir_walk returns a pointer to the pte ptep = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(PGSIZE)])); assert(pgdir_walk(kern_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE)); // should be able to change permissions too. assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W|PTE_U) == 0); assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2)); assert(pp2->pp_ref == 1); assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U); assert(kern_pgdir[0] & PTE_U); // should be able to remap with fewer permissions assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_W); assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U)); // should not be able to map at PTSIZE because need free page for page table assert(page_insert(kern_pgdir, pp0, (void*) PTSIZE, PTE_W) < 0); // insert pp1 at PGSIZE (replacing pp2) assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W) == 0); assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U)); // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ... assert(check_va2pa(kern_pgdir, 0) == page2pa(pp1)); assert(check_va2pa(kern_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((pp = page_alloc(0)) && pp == pp2); // unmapping pp1 at 0 should keep pp1 at PGSIZE page_remove(kern_pgdir, 0x0); assert(check_va2pa(kern_pgdir, 0x0) == ~0); assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1)); assert(pp1->pp_ref == 1); assert(pp2->pp_ref == 0); // unmapping pp1 at PGSIZE should free it page_remove(kern_pgdir, (void*) PGSIZE); assert(check_va2pa(kern_pgdir, 0x0) == ~0); assert(check_va2pa(kern_pgdir, PGSIZE) == ~0); assert(pp1->pp_ref == 0); assert(pp2->pp_ref == 0); // so it should be returned by page_alloc assert((pp = page_alloc(0)) && pp == pp1); // should be no free memory assert(!page_alloc(0)); // forcibly take pp0 back assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); kern_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(kern_pgdir, va, 1); ptep1 = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(va)])); assert(ptep == ptep1 + PTX(va)); kern_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(kern_pgdir, 0x0, 1); ptep = (pte_t *) page2kva(pp0); for(i=0; i<NPTENTRIES; i++) assert((ptep[i] & PTE_P) == 0); kern_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); // test mmio_map_region mm1 = (uintptr_t) mmio_map_region(0, 4097); mm2 = (uintptr_t) mmio_map_region(0, 4096); // check that they're in the right region assert(mm1 >= MMIOBASE && mm1 + 8096 < MMIOLIM); assert(mm2 >= MMIOBASE && mm2 + 8096 < MMIOLIM); // check that they're page-aligned assert(mm1 % PGSIZE == 0 && mm2 % PGSIZE == 0); // check that they don't overlap assert(mm1 + 8096 <= mm2); // check page mappings assert(check_va2pa(kern_pgdir, mm1) == 0); assert(check_va2pa(kern_pgdir, mm1+PGSIZE) == PGSIZE); assert(check_va2pa(kern_pgdir, mm2) == 0); assert(check_va2pa(kern_pgdir, mm2+PGSIZE) == ~0); // check permissions assert(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & (PTE_W|PTE_PWT|PTE_PCD)); assert(!(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & PTE_U)); // clear the mappings *pgdir_walk(kern_pgdir, (void*) mm1, 0) = 0; *pgdir_walk(kern_pgdir, (void*) mm1 + PGSIZE, 0) = 0; *pgdir_walk(kern_pgdir, (void*) mm2, 0) = 0; cprintf("check_page() succeeded!\n"); }
// // Check the physical page allocator (page_alloc(), page_free(), // and page_init()). // static void check_page_alloc(void) { struct Page *pp, *pp0, *pp1, *pp2; int nfree; struct Page *fl; char *c; int i; if (!pages) panic("'pages' is a null pointer!"); // check number of free pages for (pp = page_free_list, nfree = 0; pp; pp = pp->pp_link) ++nfree; // should be able to allocate three pages pp0 = pp1 = pp2 = 0; assert((pp0 = page_alloc(0))); assert((pp1 = page_alloc(0))); assert((pp2 = page_alloc(0))); assert(pp0); assert(pp1 && pp1 != pp0); assert(pp2 && pp2 != pp1 && pp2 != pp0); assert(page2pa(pp0) < npages*PGSIZE); assert(page2pa(pp1) < npages*PGSIZE); assert(page2pa(pp2) < npages*PGSIZE); // temporarily steal the rest of the free pages fl = page_free_list; page_free_list = 0; // should be no free memory assert(!page_alloc(0)); // free and re-allocate? page_free(pp0); page_free(pp1); page_free(pp2); pp0 = pp1 = pp2 = 0; assert((pp0 = page_alloc(0))); assert((pp1 = page_alloc(0))); assert((pp2 = page_alloc(0))); assert(pp0); assert(pp1 && pp1 != pp0); assert(pp2 && pp2 != pp1 && pp2 != pp0); assert(!page_alloc(0)); // test flags memset(page2kva(pp0), 1, PGSIZE); page_free(pp0); assert((pp = page_alloc(ALLOC_ZERO))); assert(pp && pp0 == pp); c = page2kva(pp); for (i = 0; i < PGSIZE; i++) assert(c[i] == 0); // give free list back page_free_list = fl; // free the pages we took page_free(pp0); page_free(pp1); page_free(pp2); // number of free pages should be the same for (pp = page_free_list; pp; pp = pp->pp_link) --nfree; assert(nfree == 0); cprintf("check_page_alloc() succeeded!\n"); }
// // Check that the pages on the page_free_list are reasonable. // static void check_page_free_list(bool only_low_memory) { struct Page *pp; unsigned pdx_limit = only_low_memory ? 1 : NPDENTRIES; int nfree_basemem = 0, nfree_extmem = 0; char *first_free_page; if (!page_free_list) panic("'page_free_list' is a null pointer!"); if (only_low_memory) { // Move pages with lower addresses first in the free // list, since entry_pgdir does not map all pages. struct Page *pp1, *pp2; struct Page **tp[2] = { &pp1, &pp2 }; for (pp = page_free_list; pp; pp = pp->pp_link) { int pagetype = PDX(page2pa(pp)) >= pdx_limit; *tp[pagetype] = pp; tp[pagetype] = &pp->pp_link; } *tp[1] = 0; *tp[0] = pp2; page_free_list = pp1; } // if there's a page that shouldn't be on the free list, // try to make sure it eventually causes trouble. for(pp = page_free_list; pp; pp=pp->pp_link) { if (PDX(page2pa(pp)) < pdx_limit) memset(page2kva(pp), 0x97, 128); } first_free_page = (char *) boot_alloc(0); for (pp = page_free_list; pp; pp = pp->pp_link) { // check that we didn't corrupt the free list itself assert(pp >= pages); assert(pp < pages + npages); assert(((char *) pp - (char *) pages) % sizeof(*pp) == 0); // check a few pages that shouldn't be on the free list assert(page2pa(pp) != 0); assert(page2pa(pp) != IOPHYSMEM); assert(page2pa(pp) != EXTPHYSMEM - PGSIZE); assert(page2pa(pp) != EXTPHYSMEM); assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page); // (new test for lab 4) assert(page2pa(pp) != MPENTRY_PADDR); assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page); if (page2pa(pp) < EXTPHYSMEM) ++nfree_basemem; else ++nfree_extmem; } assert(nfree_basemem > 0); assert(nfree_extmem > 0); cprintf("check_page_free() succeeded!\n"); }