Page 1 of 1

Recover from page fault

Posted: Fri Jun 03, 2016 10:41 pm
by codyd51
Hello!

My paging implementation is largely from JamesM's series. Currently, when we encounter a page fault, we print out some debug info and halt execution, however this clearly isn't optimal. I'm trying to recover from a page fault and continue execution as if nothing happened. My current page fault handler is below. As you can see, if the page exists, then we allocate the page and simply return. When I run this (specifically, the iret), I get a general protection fault, indicating that my attempt at recovery doesn't work.

Code: Select all

void alloc_frame(page_t* page, int is_kernel, int is_writeable);
page_t* get_page(uint32_t address, int make, page_directory_t* dir);

void page_fault(registers_t* regs) {
   switch_to_text();

   //page fault has occured
   //faulting address is stored in CR2 register
   uint32_t faulting_address;
   asm volatile("mov %%cr2, %0" : "=r" (faulting_address));

   //error code tells us what happened
   int present = !(regs->err_code & 0x1); //page not present
   int rw = regs->err_code & 0x2; //write operation?
   int us = regs->err_code & 0x4; //were we in user mode?
   int reserved = regs->err_code & 0x8; //overwritten CPU-reserved bits of page entry?
   int id = regs->err_code & 0x10; //caused by instruction fetch?

   printf_err("Encountered page fault at %x. Info follows", faulting_address);

   if (present) printf_err("Page was present");
   else printf_err("Page was not present");
   
   if (rw) printf_err("Operation was a write");
   else printf_err("Operation was a read");

   if (us) printf_err("User mode");
   else printf_err("Supervisor mode");

   if (reserved) printf_err("Overwrote CPU-resereved bits of page entry");

   if (id) printf_err("Faulted during instruction fetch");

   if (regs->eip != faulting_address) {
      printf_err("Page fault caused by executing unpaged memory");
   } 
   else {
      printf_err("Page fault caused by reading unpaged memory");
   }

   //if this page was present, attempt to recover by allocating the page
   if (present) {
      alloc_frame(get_page(faulting_address, !present, current_directory), !us, rw);
      asm volatile("iret");
      return;
   }

   common_halt(&regs);
}
Does anyone have any idea as to what I could try? I'd be more than happy to post more of the implementation if necessary.

Thanks!

Re: Recover from page fault

Posted: Sat Jun 04, 2016 12:16 am
by alexfru
Your iret(d) in the middle of the C code completely disregards how the compiler generates the function prologue/epilogue code and the stack state. You should use proper exception handler wrappers written in assembly.

Re: Recover from page fault

Posted: Sat Jun 04, 2016 12:34 am
by codyd51
alexfru wrote:Your iret(d) in the middle of the C code completely disregards how the compiler generates the function prologue/epilogue code and the stack state. You should use proper exception handler wrappers written in assembly.
Thanks! Fixed, the calling routine now saves and restores the stack properly. I keep generating another page fault each time I try to handle one (resulting in an infinite loop of page faults), so it seems I'm not allocating the page correctly.

Re: Recover from page fault

Posted: Sat Jun 04, 2016 12:45 am
by alexfru
Do you remove the error code from the stack before iretd?

Re: Recover from page fault

Posted: Sat Jun 04, 2016 1:12 am
by codyd51
alexfru wrote:Do you remove the error code from the stack before iretd?
Yeah. Here's the IRQ routine:

Code: Select all

irq_common_stub:
	pusha		; pushes edi, esi, ebp, esp, ebx, edx, ecx, eax

	mov ax, ds	; lower 16 bits of eax = ds
	push eax 	; save data segment descriptor

	mov ax, 0x10	; load kernel data segment descriptor
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	call irq_handler

	pop ebx 	; reload original data segment descriptor
	mov ds, bx
	mov es, bx
	mov fs, bx
	mov gs, bx

	popa		; pops edi, esi, ebp, etc
	add esp, 8	; cleans up pushed error code and pushed ISR number
	iret		; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP

Re: Recover from page fault

Posted: Sat Jun 04, 2016 1:44 am
by alexfru
pushad
popad
iretd
?

Re: Recover from page fault

Posted: Sat Jun 04, 2016 10:13 am
by codyd51
Changed the pusha, popa, and iret to pushad, popad, and iretd, same issue. I noticed that the faulting address is 0x41013ffc, however the page address returned by get_page is 0x4008204c.

Re: Recover from page fault

Posted: Sat Jun 04, 2016 10:14 am
by Rew
There is a known issue with how JamesM tutorial handles passing registers during irq handling. Without seeing your irq_handler it is impossible to say for sure that it is your problem. However, you are at the stage that many people run into it.

Some questions that would give helpful background:
Does your paging code work generally for preallocation without page faults? Can you map (not identity map) different areas and successfully access memory? Can you alloc additional memory after paging is enabled and successfully read/write to it?
Does interrupt handling work as a whole? Have you been able to successfully handle other interrupts without faulting?
You have a switch_to_text function. How is this implemented? Do you have any evidence that you can call switch_to_text and continue running code without faulting?
Do the addresses and flags that are reported in your page fault handler make sense? Are you faulting at the address that you expect?