2020301918-os/kern/pmap.c
2022-11-21 23:07:57 +08:00

154 lines
3.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <assert.h>
#include <elf.h>
#include <string.h>
#include <mmu.h>
#include <kern/kmalloc.h>
#include <kern/pmap.h>
/*
* 申请一个新的物理页并更新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);
}
}