Page 1 of 1

Confusion with Page Fault Handler

Posted: Sun Sep 04, 2016 3:07 am
by snijj
Hey, I'm a long time lurker/user of the this site but never really been a participant until now.

So for the last several years I've been on/off working on my OS and have recently decided to restart it due to having forgotten what I was doing on the last incarnation of it (a 13 month break does not do favours to development for me). However the point in which I always seem to get stuck and confused with is with paging.

Usually the problem arises for me around the handling page directories/tables and physical and linear addresses. However this time I think I've been able to get past that particular problem. However now I'm getting a new problem, which I'm having difficulty understanding, or at least building a picture of in my head. That is the Page Fault interrupt. I understand that it will get triggered and the faulting linear address will be placed inside CR2. I can get that and work out which page needs to be allocated/created. However the part I'm having difficulty getting my head around (and maybe I'm over complicating things here) is how do I go about allocating this space?

I'll give a bit of information about the kernel thus far.
  • Located at 0x00100000.
  • The entire first 4MiB of memory (BIOS/Kernel/etc) is identity mapped.
  • I plan to use 4MiB to 16MiB as kernel heap space. The VM will detect who is requesting memory, or what purpose it is for and assign a linear address accordingly.
  • User land programs will ultimately exist from 0x10000000 (for no other reason that its a nice round number)
This is the flow of things that I've got at the moment (conceptual, not concrete yet)
  • Reference unmapped linear address (i.e. 0x10000000)
  • Page Fault Occurs
  • Calculate Page Directory Entry ((cr2 >> 22) & 0x3ff) = 0x40
  • Calculate Page Table Entry ((cr2 >> 12) & 0x3ff) = 0x00
  • page = pm_alloc_page() - Allocates the next available physical page in memory. Returns physical address.
  • vm_alloc_page(page, pde, pte) - simply allocates the page table entry
At this point I'm thinking that the page should be setup and present and can then return from the interrupt. Correct me if any of this part is wrong please.

However I have a couple of concerns. They are:
  • What happens if the page lies inside a page table that hasn't been setup yet?
  • If I allocate a page table through pm_alloc_page() directly the page table won't be directly usable when paging is enabled will it?
Sorry if this all sounds rather confused. I'm finding this particular topic rather confusing and just need a bit of help getting my understanding in order.

Re: Confusion with Page Fault Handler

Posted: Sun Sep 04, 2016 3:43 am
by Mikumiku747
snijj wrote:However I have a couple of concerns. They are:
  • What happens if the page lies inside a page table that hasn't been setup yet?
  • If I allocate a page table through pm_alloc_page() directly the page table won't be directly usable when paging is enabled will it?
For your first concern, if your page lies inside a page table which hasn't been set up yet, it will page fault when any of the pages that would be in that directory try to get translated. The key thing to note is that the page translation occurs in two steps, first the directory, and then the page itself, so if the first step fails, it just pagefaults and doesn't try the second step. So if you mark an entry in the page directory, it won't bother looking up the page table, it will just pagefault.

To put this into practice in your page fault handler, after you calculate the page directory entry, you should check that entry to see if the page table it points to is present. If not, then you can allocate another page of physical memory, set it up as an empty page directory, and then continue on with the rest of the handler.

As for your second concern, I'm not exactly sure what you mean, do you mean that if you create and fill in a new page table in your pm_alloc_page function, will it immediately be in effect? If so, the answer is... kind of. If you make changes to the paging structures, they take effect immediately, but something to note is that the CPU uses a buffer (called the traslation lookaside buffer, or TLB) to store recent page translations, so that it doesn't have to do them for every single memory reference. Because of this, if you change something in the paging directory, you need to let the CPU know that the buffer isn't correct any more. The easiest way to do this is to reload the CR3 register, which clears out this buffer. In short, if you change any paging structures, reload CR3. You can also use the INVLPG instruction, feel free to look up how that works too.

As a side note, you mention that user programs might be loaded at an address like 0x10000000. That means you only get 256 MB of kernel *address* space. It might seem like a lot now, but maybe not so in the future, just something to think about. If you think you only need that much kernel space, feel free to use that.

Re: Confusion with Page Fault Handler

Posted: Sun Sep 04, 2016 4:09 am
by snijj
Mikumiku747 wrote:
snijj wrote:However I have a couple of concerns. They are:
  • What happens if the page lies inside a page table that hasn't been setup yet?
  • If I allocate a page table through pm_alloc_page() directly the page table won't be directly usable when paging is enabled will it?
For your first concern, if your page lies inside a page table which hasn't been set up yet, it will page fault when any of the pages that would be in that directory try to get translated. The key thing to note is that the page translation occurs in two steps, first the directory, and then the page itself, so if the first step fails, it just pagefaults and doesn't try the second step. So if you mark an entry in the page directory, it won't bother looking up the page table, it will just pagefault.

To put this into practice in your page fault handler, after you calculate the page directory entry, you should check that entry to see if the page table it points to is present. If not, then you can allocate another page of physical memory, set it up as an empty page directory, and then continue on with the rest of the handler.
Thanks, that's actually explained a lot to me! I think i'd missed that the translation was happening in two steps in other documentation that I read.
Mikumiku747 wrote:As for your second concern, I'm not exactly sure what you mean, do you mean that if you create and fill in a new page table in your pm_alloc_page function, will it immediately be in effect? If so, the answer is... kind of. If you make changes to the paging structures, they take effect immediately, but something to note is that the CPU uses a buffer (called the traslation lookaside buffer, or TLB) to store recent page translations, so that it doesn't have to do them for every single memory reference. Because of this, if you change something in the paging directory, you need to let the CPU know that the buffer isn't correct any more. The easiest way to do this is to reload the CR3 register, which clears out this buffer. In short, if you change any paging structures, reload CR3. You can also use the INVLPG instruction, feel free to look up how that works too.
OK, I think I get that. Does this reload simply require rewriting the page directory location to CR3? If so does it matter if I set the same value. Does it still pick that up as a change?
Mikumiku747 wrote:As a side note, you mention that user programs might be loaded at an address like 0x10000000. That means you only get 256 MB of kernel *address* space. It might seem like a lot now, but maybe not so in the future, just something to think about. If you think you only need that much kernel space, feel free to use that.
Haha, no I fully get that. For now it's just a nice round number for clearly seeing boundaries in debugging. Once I get to user programs I'll deal with that address. I'm trying to design my VMM to be able to move these boundaries relatively easily if required.

Re: Confusion with Page Fault Handler

Posted: Sun Sep 04, 2016 4:44 am
by Mikumiku747
snijj wrote:OK, I think I get that. Does this reload simply require rewriting the page directory location to CR3? If so does it matter if I set the same value. Does it still pick that up as a change?
Yeah, it's usually just re-writing the same thing, that works fine. Good luck with paging, it's always daunting to start with, but the more you read about it and mess around with it, the easier it becomes to understand.

Re: Confusion with Page Fault Handler

Posted: Sun Sep 04, 2016 4:50 am
by snijj
Thanks a lot! I'm hoping that once I've got paging done that I should be past all the CPU setup and configuration and can get on to the more interesting aspects such as device drivers, multitasking, threading, processes and file systems.

Update/Edit: I got my Page Fault handler working. On a fault I now create the appropriate page tables and entries when required and everything seems to be working alright for now. I just need to sort out the arrangement of virtual memory with it now, as it is simply id mapping the physical page created for the page table.