Confusion about Paging and creating new page tables
Posted: Sat Aug 15, 2015 12:41 am
I'm having some trouble with how my page fault handling code works, and I think it's due to some confusion about how I should dynamically create new page tables.
My current situation is as follows:
When a page fault occurs, I read the relevant registers to get the Page Directory Index and the Page Table Entry that caused it. If the fault occurred in a page table that already exists, then I use my physical memory manager to find a free page frame and I map the physical address of that page frame to the virtual address that the fault was generated for. As far as my current testing has gone, this works fine.
If the fault occurred in a page table that doesn't exist, I run into some problems. I get my physical memory manager to give me a free page frame that I will use for the new page table. I then need to fill the page frame with 0's, however, doing this would cause a page fault because the page frame has not been mapped to any virtual address. I would be writing to a physical address that the CPU would think is a virtual address. How do I get around this?
At the moment, I have an "emergency" Page table hard coded in. I always put it as the last page table of the page directory. The idea is that when I need to zero out my new page frame, I can temporarily map it to the 0th entry in this emergency page table. I can then guarantee that I can find this new page frame at virtual address: 1024 (for the last Page Table) * 1 (for the 0th entry in the last page table) * 4096 (for the size of each page frame). I can then zero out this page frame without causing another page fault. I then point the Page Directory to this new Page Table's physical address. Lastly, I remove the mapping of this new Page Table from my emergency page frame.
All that this serves to do is cause a chain of "page table not present" page faults before the VM double-faults. What part of paging am I failing to grasp?
My code for this all is as follows:
Thanks
My current situation is as follows:
When a page fault occurs, I read the relevant registers to get the Page Directory Index and the Page Table Entry that caused it. If the fault occurred in a page table that already exists, then I use my physical memory manager to find a free page frame and I map the physical address of that page frame to the virtual address that the fault was generated for. As far as my current testing has gone, this works fine.
If the fault occurred in a page table that doesn't exist, I run into some problems. I get my physical memory manager to give me a free page frame that I will use for the new page table. I then need to fill the page frame with 0's, however, doing this would cause a page fault because the page frame has not been mapped to any virtual address. I would be writing to a physical address that the CPU would think is a virtual address. How do I get around this?
At the moment, I have an "emergency" Page table hard coded in. I always put it as the last page table of the page directory. The idea is that when I need to zero out my new page frame, I can temporarily map it to the 0th entry in this emergency page table. I can then guarantee that I can find this new page frame at virtual address: 1024 (for the last Page Table) * 1 (for the 0th entry in the last page table) * 4096 (for the size of each page frame). I can then zero out this page frame without causing another page fault. I then point the Page Directory to this new Page Table's physical address. Lastly, I remove the mapping of this new Page Table from my emergency page frame.
All that this serves to do is cause a chain of "page table not present" page faults before the VM double-faults. What part of paging am I failing to grasp?
My code for this all is as follows:
Code: Select all
void handle_page_fault(uint32_t errcode){
uint32_t cr2;
asm volatile("mov %%cr2, %0" : "=r" (cr2));
//Get the middle 10 bits
uint32_t pagedir_entry = ((cr2 >> 12) & 0x3FF);
//Get the upper 10 bits
uint32_t pagetab_entry = (cr2 >> 22);
bool isPresent = (errcode % 2);
bool isUser = ((errcode >> 2) % 2);
if(!isPresent){
printf("Page table not present\n");
//We need to put a new block in the page directory
uint32_t* newblock = physmm_alloc_block();
//Point to it with our helper block so we can use it
insert_Kernel_PTValue(helper_block, HELPER_BLOCK_INSERT_INDEX, addr_to_block_32((void*)newblock));
//Now we can initialize it full of 0's (Initialize it from the virtual address it is mapped to in the helper block)
init_KernelPT((uint32_t*) ((HELPER_BLOCK_INSERT_INDEX + 1) * PHYSMM_BLOCK_SIZE * (MAX_TABLE_ENTRY + 1)));
//Now put this new page table in the page directory
insert_KernelPTentry(PageDir_Ptr, newblock, pagedir_entry);
//And now stop pointing to it from helper block
remove_Kernel_PTValue(helper_block, HELPER_BLOCK_INSERT_INDEX);
}else{
printf("Page table was present\n");
uint32_t* tabptr = get_pagetable_ptr(PageDir_Ptr, pagedir_entry);
void* toallocate = physmm_alloc_block();
uint32_t toallocate_block = addr_to_block_32(toallocate);
if(isUser){
//Map the block with user privilege
insert_User_PTValue(tabptr, pagetab_entry, toallocate_block);
}else{
//Map the block with kernel privilege
insert_Kernel_PTValue(tabptr, pagetab_entry, toallocate_block);
}
}
}