#include #include #include #include #include #include #include #include #include #include /* vmm design include two parts: mm_struct (mm) & vma_struct (vma) mm is the memory manager for the set of continuous virtual memory area which have the same PDT. vma is a continuous virtual memory area. There a linear link list for vma & a redblack link list for vma in mm. --------------- mm related functions: golbal functions struct mm_struct * mm_create(void) void mm_destroy(struct mm_struct *mm) int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) -------------- vma related functions: global functions struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...) void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr) local functions inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) --------------- check correctness functions void check_vmm(void); void check_vma_struct(void); void check_pgfault(void); */ static void check_vmm(void); static void check_vma_struct(void); static void check_pgfault(void); // mm_create - alloc a mm_struct & initialize it. struct mm_struct * mm_create(void) { struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); if (mm != NULL) { list_init(&(mm->mmap_list)); mm->mmap_cache = NULL; mm->pgdir = NULL; mm->map_count = 0; if (swap_init_ok) swap_init_mm(mm); else mm->sm_priv = NULL; set_mm_count(mm, 0); sem_init(&(mm->mm_sem), 1); } return mm; } // vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end) struct vma_struct * vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) { struct vma_struct *vma = kmalloc(sizeof(struct vma_struct)); if (vma != NULL) { vma->vm_start = vm_start; vma->vm_end = vm_end; vma->vm_flags = vm_flags; } return vma; } // find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end) struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr) { struct vma_struct *vma = NULL; if (mm != NULL) { vma = mm->mmap_cache; if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) { bool found = 0; list_entry_t *list = &(mm->mmap_list), *le = list; while ((le = list_next(le)) != list) { vma = le2vma(le, list_link); if (vma->vm_start<=addr && addr < vma->vm_end) { found = 1; break; } } if (!found) { vma = NULL; } } if (vma != NULL) { mm->mmap_cache = vma; } } return vma; } // check_vma_overlap - check if vma1 overlaps vma2 ? static inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) { assert(prev->vm_start < prev->vm_end); assert(prev->vm_end <= next->vm_start); assert(next->vm_start < next->vm_end); } // insert_vma_struct -insert vma in mm's list link void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) { assert(vma->vm_start < vma->vm_end); list_entry_t *list = &(mm->mmap_list); list_entry_t *le_prev = list, *le_next; list_entry_t *le = list; while ((le = list_next(le)) != list) { struct vma_struct *mmap_prev = le2vma(le, list_link); if (mmap_prev->vm_start > vma->vm_start) { break; } le_prev = le; } le_next = list_next(le_prev); /* check overlap */ if (le_prev != list) { check_vma_overlap(le2vma(le_prev, list_link), vma); } if (le_next != list) { check_vma_overlap(vma, le2vma(le_next, list_link)); } vma->vm_mm = mm; list_add_after(le_prev, &(vma->list_link)); mm->map_count ++; } // mm_destroy - free mm and mm internal fields void mm_destroy(struct mm_struct *mm) { assert(mm_count(mm) == 0); list_entry_t *list = &(mm->mmap_list), *le; while ((le = list_next(list)) != list) { list_del(le); kfree(le2vma(le, list_link)); //kfree vma } kfree(mm); //kfree mm mm=NULL; } int mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags, struct vma_struct **vma_store) { uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE); if (!USER_ACCESS(start, end)) { return -E_INVAL; } assert(mm != NULL); int ret = -E_INVAL; struct vma_struct *vma; if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) { goto out; } ret = -E_NO_MEM; if ((vma = vma_create(start, end, vm_flags)) == NULL) { goto out; } insert_vma_struct(mm, vma); if (vma_store != NULL) { *vma_store = vma; } ret = 0; out: return ret; } int dup_mmap(struct mm_struct *to, struct mm_struct *from) { assert(to != NULL && from != NULL); list_entry_t *list = &(from->mmap_list), *le = list; while ((le = list_prev(le)) != list) { struct vma_struct *vma, *nvma; vma = le2vma(le, list_link); nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags); if (nvma == NULL) { return -E_NO_MEM; } insert_vma_struct(to, nvma); bool share = 0; if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) { return -E_NO_MEM; } } return 0; } void exit_mmap(struct mm_struct *mm) { assert(mm != NULL && mm_count(mm) == 0); pde_t *pgdir = mm->pgdir; list_entry_t *list = &(mm->mmap_list), *le = list; while ((le = list_next(le)) != list) { struct vma_struct *vma = le2vma(le, list_link); unmap_range(pgdir, vma->vm_start, vma->vm_end); } while ((le = list_next(le)) != list) { struct vma_struct *vma = le2vma(le, list_link); exit_range(pgdir, vma->vm_start, vma->vm_end); } } bool copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) { if (!user_mem_check(mm, (uintptr_t)src, len, writable)) { return 0; } memcpy(dst, src, len); return 1; } bool copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) { if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) { return 0; } memcpy(dst, src, len); return 1; } // vmm_init - initialize virtual memory management // - now just call check_vmm to check correctness of vmm void vmm_init(void) { check_vmm(); } // check_vmm - check correctness of vmm static void check_vmm(void) { size_t nr_free_pages_store = nr_free_pages(); check_vma_struct(); check_pgfault(); // assert(nr_free_pages_store == nr_free_pages()); cprintf("check_vmm() succeeded.\n"); } static void check_vma_struct(void) { size_t nr_free_pages_store = nr_free_pages(); struct mm_struct *mm = mm_create(); assert(mm != NULL); int step1 = 10, step2 = step1 * 10; int i; for (i = step1; i >= 1; i --) { struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); assert(vma != NULL); insert_vma_struct(mm, vma); } for (i = step1 + 1; i <= step2; i ++) { struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); assert(vma != NULL); insert_vma_struct(mm, vma); } list_entry_t *le = list_next(&(mm->mmap_list)); for (i = 1; i <= step2; i ++) { assert(le != &(mm->mmap_list)); struct vma_struct *mmap = le2vma(le, list_link); assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2); le = list_next(le); } for (i = 5; i <= 5 * step2; i +=5) { struct vma_struct *vma1 = find_vma(mm, i); assert(vma1 != NULL); struct vma_struct *vma2 = find_vma(mm, i+1); assert(vma2 != NULL); struct vma_struct *vma3 = find_vma(mm, i+2); assert(vma3 == NULL); struct vma_struct *vma4 = find_vma(mm, i+3); assert(vma4 == NULL); struct vma_struct *vma5 = find_vma(mm, i+4); assert(vma5 == NULL); assert(vma1->vm_start == i && vma1->vm_end == i + 2); assert(vma2->vm_start == i && vma2->vm_end == i + 2); } for (i =4; i>=0; i--) { struct vma_struct *vma_below_5= find_vma(mm,i); if (vma_below_5 != NULL ) { cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end); } assert(vma_below_5 == NULL); } mm_destroy(mm); // assert(nr_free_pages_store == nr_free_pages()); cprintf("check_vma_struct() succeeded!\n"); } struct mm_struct *check_mm_struct; // check_pgfault - check correctness of pgfault handler static void check_pgfault(void) { size_t nr_free_pages_store = nr_free_pages(); check_mm_struct = mm_create(); assert(check_mm_struct != NULL); struct mm_struct *mm = check_mm_struct; pde_t *pgdir = mm->pgdir = boot_pgdir; assert(pgdir[0] == 0); struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE); assert(vma != NULL); insert_vma_struct(mm, vma); uintptr_t addr = 0x100; assert(find_vma(mm, addr) == vma); int i, sum = 0; for (i = 0; i < 100; i ++) { *(char *)(addr + i) = i; sum += i; } for (i = 0; i < 100; i ++) { sum -= *(char *)(addr + i); } assert(sum == 0); page_remove(pgdir, ROUNDDOWN(addr, PGSIZE)); free_page(pde2page(pgdir[0])); pgdir[0] = 0; mm->pgdir = NULL; mm_destroy(mm); check_mm_struct = NULL; assert(nr_free_pages_store == nr_free_pages()); cprintf("check_pgfault() succeeded!\n"); } //page fault number volatile unsigned int pgfault_num=0; /* do_pgfault - interrupt handler to process the page fault execption * @mm : the control struct for a set of vma using the same PDT * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware * @addr : the addr which causes a memory access exception, (the contents of the CR2 register) * * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing * the exception and recovering from it. * (1) The contents of the CR2 register. The processor loads the CR2 register with the * 32-bit linear address that generated the exception. The do_pgfault fun can * use this address to locate the corresponding page directory and page-table * entries. * (2) An error code on the kernel stack. The error code for a page fault has a format different from * that for other exceptions. The error code tells the exception handler three things: * -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0) * or to either an access rights violation or the use of a reserved bit (1). * -- The W/R flag (bit 1) indicates whether the memory access that caused the exception * was a read (0) or write (1). * -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1) * or supervisor mode (0) at the time of the exception. */ int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) { int ret = -E_INVAL; //try to find a vma which include addr struct vma_struct *vma = find_vma(mm, addr); pgfault_num++; //If the addr is in the range of a mm's vma? if (vma == NULL || vma->vm_start > addr) { cprintf("not valid addr %x, and can not find it in vma\n", addr); goto failed; } //check the error_code switch (error_code & 3) { default: /* error code flag : default is 3 ( W/R=1, P=1): write, present */ case 2: /* error code flag : (W/R=1, P=0): write, not present */ if (!(vma->vm_flags & VM_WRITE)) { cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n"); goto failed; } break; case 1: /* error code flag : (W/R=0, P=1): read, present */ cprintf("do_pgfault failed: error code flag = read AND present\n"); goto failed; case 0: /* error code flag : (W/R=0, P=0): read, not present */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n"); goto failed; } } /* IF (write an existed addr ) OR * (write an non_existed addr && addr is writable) OR * (read an non_existed addr && addr is readable) * THEN * continue process */ uint32_t perm = PTE_U; if (vma->vm_flags & VM_WRITE) { perm |= PTE_W; } addr = ROUNDDOWN(addr, PGSIZE); ret = -E_NO_MEM; pte_t *ptep=NULL; /*LAB3 EXERCISE 1: YOUR CODE * Maybe you want help comment, BELOW comments can help you finish the code * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * get_pte : get an pte and return the kernel virtual address of this pte for la * if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1') * pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup * an addr map pa<--->la with linear address la and the PDT pgdir * DEFINES: * VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable * PTE_W 0x002 // page table/directory entry flags bit : Writeable * PTE_U 0x004 // page table/directory entry flags bit : User can access * VARIABLES: * mm->pgdir : the PDT of these vma * */ #if 0 /*LAB3 EXERCISE 1: YOUR CODE*/ ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. if (*ptep == 0) { //(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr } else { /*LAB3 EXERCISE 2: YOUR CODE * Now we think this pte is a swap entry, we should load data from disk to a page with phy addr, * and map the phy addr with logical addr, trigger swap manager to record the access situation of this page. * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr, * find the addr of disk page, read the content of disk page into this memroy page * page_insert : build the map of phy addr of an Page with the linear addr la * swap_map_swappable : set the page swappable */ /* * LAB5 CHALLENGE ( the implmentation Copy on Write) There are 2 situlations when code comes here. 1) *ptep & PTE_P == 1, it means one process try to write a readonly page. If the vma includes this addr is writable, then we can set the page writable by rewrite the *ptep. This method could be used to implement the Copy on Write (COW) thchnology(a fast fork process method). 2) *ptep & PTE_P == 0 & but *ptep!=0, it means this pte is a swap entry. We should add the LAB3's results here. */ if(swap_init_ok) { struct Page *page=NULL; //(1)According to the mm AND addr, try to load the content of right disk page // into the memory which page managed. //(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr //(3) make the page swappable. //(4) [NOTICE]: you myabe need to update your lab3's implementation for LAB5's normal execution. } else { cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); goto failed; } } #endif // try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. // (notice the 3th parameter '1') if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { cprintf("get_pte in do_pgfault failed\n"); goto failed; } if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { cprintf("pgdir_alloc_page in do_pgfault failed\n"); goto failed; } } else { struct Page *page=NULL; cprintf("do pgfault: ptep %x, pte %x\n",ptep, *ptep); if (*ptep & PTE_P) { //if process write to this existed readonly page (PTE_P means existed), then should be here now. //we can implement the delayed memory space copy for fork child process (AKA copy on write, COW). //we didn't implement now, we will do it in future. panic("error write a non-writable pte"); //page = pte2page(*ptep); } else{ // if this pte is a swap entry, then load data from disk to a page with phy addr // and call page_insert to map the phy addr with logical addr if(swap_init_ok) { if ((ret = swap_in(mm, addr, &page)) != 0) { cprintf("swap_in in do_pgfault failed\n"); goto failed; } } else { cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); goto failed; } } page_insert(mm->pgdir, page, addr, perm); swap_map_swappable(mm, addr, page, 1); page->pra_vaddr = addr; } ret = 0; failed: return ret; } bool user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) { if (mm != NULL) { if (!USER_ACCESS(addr, addr + len)) { return 0; } struct vma_struct *vma; uintptr_t start = addr, end = addr + len; while (start < end) { if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) { return 0; } if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) { return 0; } if (write && (vma->vm_flags & VM_STACK)) { if (start < vma->vm_start + PGSIZE) { //check stack start & size return 0; } } start = vma->vm_end; } return 1; } return KERN_ACCESS(addr, addr + len); }