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");
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;
}
}
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;
pde_t *pgdir = mm->pgdir;
ptep = get_pte(pgdir, addr, 1); //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
if (ptep == NULL)
{
cprintf("get pte in do_faultpage failed\n");
goto failed;
}
//缺页中断发生 访问逻辑页不在内存
if (*ptep == 0) {
//当访问到没有物理页与之对应的逻辑页
//可能在程序中未使用
//调用pagdir_alloc_page分配一个物理页,并建立映射,可能需要进行换出操作
struct Page *page = pgdir_alloc_page(pgdir, addr, perm);
if (page == NULL)
{
cprintf("alloc a page failed\n");
goto failed;
}
else {
//逻辑页不再内存,pte中存储了在外存交换区的索引 swap_entry_t
if(swap_init_ok) {
struct Page *page=NULL;
//将外存的页读入,分配物理页,可能需要换出操作
swap_in(mm, addr, &page);
//建立隐射
if (page_insert(pgdir, page, addr, perm) != 0)
{
ret = -E_NO_MEM;
goto failed;
}
//插入fifo队列
swap_map_swappable(check_mm_struct, addr, page, 0);
page->pra_vaddr = addr;
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
ret = 0;
failed:
return ret;
}
int
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
{
//call graph: -> alloc(page) -> alloc_pages(1)
//分配物理页,可能需要换出操作
struct Page *result = alloc_page();
assert(result!=NULL);
pte_t *ptep = get_pte(mm->pgdir, addr, 0);
// cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
//磁盘io,根据pte中的所有读对应页
int r;
if ((r = swapfs_read((*ptep), result)) != 0)
{
assert(r!=0);
}
cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
*ptr_result=result;
return 0;
}
int
page_insert(pde_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);
}
}
//pte中记录物理页对应的物理地址 标记未persent 设置访问权限
*ptep = page2pa(page) | PTE_P | perm;
tlb_invalidate(pgdir, la);
return 0;
}
struct Page *
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
//分配物理页,可能需要换出操作
struct Page *page = alloc_page();
if (page != NULL) {
//建立映射
if (page_insert(pgdir, page, la, perm) != 0) {
free_page(page);
return NULL;
}
if (swap_init_ok){
//插入fifo队列
swap_map_swappable(check_mm_struct, la, page, 0);
page->pra_vaddr=la;
assert(page_ref(page) == 1);
//cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
}
}
return page;
}
struct Page *
alloc_pages(size_t n) {
struct Page *page=NULL;
bool intr_flag;
while (1)
{
local_intr_save(intr_flag);
{
//调用default_alloc_pages申请连续的n页物理空间
page = pmm_manager->alloc_pages(n);
}
local_intr_restore(intr_flag);
//如果成功申请退出循环
//不成功且申请多页(不肯发生在缺页中断中)退出循环
//缺页中断只申请1页
if (page != NULL || n > 1 || swap_init_ok == 0) break;
//缺页中断发生,且没有空闲物理页则进行换出操作
extern struct mm_struct *check_mm_struct;
//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
swap_out(check_mm_struct, n, 0);
}
//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
return page;
}