#include #include #include #include #include #include /* * 申请一个新的物理页,并更新page_list页面信息 * 返回新申请的物理页面的物理地址 */ static phyaddr_t alloc_phy_page(struct page_node **page_list) { phyaddr_t paddr = phy_malloc_4k(); struct page_node *new_node = kmalloc(sizeof(struct page_node)); new_node->nxt = *page_list; new_node->paddr = paddr; new_node->laddr = -1; *page_list = new_node; return paddr; } /* * MINIOS中比较通用的页表映射函数 * 它将laddr处的虚拟页面映射到物理地址为paddr(如果paddr为-1则会自动申请一个新的物理页面)的物理页面 * 并将pte_flag置位到页表项(页目录项标志位默认为PTE_P | PTE_W | PTE_U) * 这个函数中所有新申请到的页面信息会存放到page_list这个链表中 */ static void lin_mapping_phy(u32 cr3, struct page_node **page_list, uintptr_t laddr, phyaddr_t paddr, u32 pte_flag) { assert(PGOFF(laddr) == 0); uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3); if ((pde_ptr[PDX(laddr)] & PTE_P) == 0) { phyaddr_t pte_phy = alloc_phy_page(page_list); memset((void *)K_PHY2LIN(pte_phy), 0, PGSIZE); pde_ptr[PDX(laddr)] = pte_phy | PTE_P | PTE_W | PTE_U; } phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]); uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy); phyaddr_t page_phy; if (paddr == (phyaddr_t)-1) { if ((pte_ptr[PTX(laddr)] & PTE_P) != 0) return; page_phy = alloc_phy_page(page_list); (*page_list)->laddr = laddr; } else { if ((pte_ptr[PTX(laddr)] & PTE_P) != 0) warn("this page was mapped before, laddr: %x", laddr); assert(PGOFF(paddr) == 0); page_phy = paddr; } pte_ptr[PTX(laddr)] = page_phy | pte_flag; } /* * 初始化进程页表的内核部分 * 将3GB ~ 3GB + 128MB的线性地址映射到0 ~ 128MB的物理地址 */ void map_kern(u32 cr3, struct page_node **page_list) { for (phyaddr_t paddr = 0 ; paddr < 128 * MB ; paddr += PGSIZE) { lin_mapping_phy(cr3, page_list, K_PHY2LIN(paddr), paddr, PTE_P | PTE_W | PTE_U); } } /* * 根据elf文件信息将数据搬迁到指定位置 * 中间包含了对页表的映射,eip的置位 * 这也是你们做实验五中最折磨的代码,可以看这份学习一下 */ void map_elf(PROCESS_0 *p_proc, void *elf_addr) { assert(p_proc->lock != 0); struct Elf *eh = (struct Elf *)elf_addr; struct Proghdr *ph = (struct Proghdr *)(elf_addr + eh->e_phoff); for (int i = 0 ; i < eh->e_phnum ; i++, ph++) { if (ph->p_type != PT_LOAD) continue; uintptr_t st = ROUNDDOWN(ph->p_va, PGSIZE); uintptr_t en = ROUNDUP(st + ph->p_memsz, PGSIZE); for (uintptr_t laddr = st ; laddr < en ; laddr += PGSIZE) { u32 pte_flag = PTE_P | PTE_U; if ((ph->p_flags & ELF_PROG_FLAG_WRITE) != 0) pte_flag |= PTE_W; lin_mapping_phy(p_proc->cr3, &p_proc->page_list, laddr, (phyaddr_t)-1, pte_flag); } memcpy( (void *)ph->p_va, (const void *)eh + ph->p_offset, ph->p_filesz); memset( (void *)ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz); } p_proc->user_regs.eip = eh->e_entry; } /* * 将用户栈映射到用户能够访问到的最后一个页面 * (0xbffff000~0xc0000000) * 并将esp寄存器放置好 */ void map_stack(PROCESS_0 *p_proc) { assert(p_proc->lock != 0); lin_mapping_phy(p_proc->cr3, &p_proc->page_list, K_PHY2LIN(-PGSIZE), (phyaddr_t)-1, PTE_P | PTE_W | PTE_U); p_proc->user_regs.esp = K_PHY2LIN(0); } /* * 根据page_list回收所有的页面(包括回收页面节点) */ void recycle_pages(struct page_node *page_list) { for (struct page_node *prevp, *p = page_list ; p ;) { phy_free_4k(p->paddr); prevp = p, p = p->nxt; kfree(prevp); } }