Another Long Mode Paging Question

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

Another Long Mode Paging Question

Post by proxy »

So I've been toying with the self mapping trick in long mode and have been having trouble with the 4 levels of indirection and keep it all straight in my head. From my understanding, if I self map the PML4 in its own last entry I'll end up with a mapping that looks like this:

Code: Select all

pml4_address    = 0xfffffffffffff000;
pdp_address     = 0xffffffffffe00000;
pd_address      = 0xffffffffc0000000;
pt_address      = 0xffffff8000000000;
The math is really easy for the PML4 stuff. I simply do:

Code: Select all

pml4_address + ((address >> 39) & 0x1ff)
and I've got the address of the correct PML4 entry for that address. But it gets more confusing after that :-(.

What is the correct bit-twiddling for PDPs, PDs and PTs? If I've done my math right, each PD represents 1GB of address space. So would that mean that the PD for address 0x40001000 (1GB + 1 Page) would be at the 513th entry aka:

Code: Select all

0xffffff8000000000 + (512 * 8)
.

I think this is right, but it gets more confusing with PD and PDP entries.

Extending this, I might end up with this for the page table index:

Code: Select all

0xffffff8000000000 + (8 * (pt_offset + (pd_offset << 9) + (pdp_offset << 18) + (pml4_offset << 27)))


But that yields addresses like PT addresses like 0xfffffffffffffff8 for 0xfffffffffffff000 which can't be right, since it is out side of the possible page ranges. I feel like the gap between kernel/user addresses (positive/negative) is what's messing up my math. Do I need to special case the upper half ones?

Could someone help clarify the proper way to formulate the different parts? Thanks.
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

Re: Another Long Mode Paging Question

Post by proxy »

I've been toying with the math, and I think that I may have actually had it correct. But I was using a poorly suited test case. So far the only addresses which give me values outside of the PT address at 0xffffff8000000000 are ones which are within the self mapping, which is invalid anyway.

It would be great if someone could reassure me that I'm right, but I think I'm on the right track.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Another Long Mode Paging Question

Post by Owen »

My suggestion is this:
Create an ENTRY_ADDR(_l4, _l3, _l2, _l1) macro (or inline function). This shouldn't be too difficult; just ors and bitshifts.

To find an PTE, you would provide all four values. To find a PDE, you would specify invoke as ENTRY_ADDR(0x1FF, pml4e, pdpte, pde). To find a PDPTE, you would invoke as ENTRY_ADDR(0x1FF, 0x1FF, pml4e, pdpte), and so on. You would probably want to define macros built on top of this to simplify things. It would also be advisable to make macros for getting indexes from addresses, and for getting the table responsible for a given address.

Remember than, when you modify a PDE (or higher level entry) you need to invtlb the recursive mapping addresses as well.

[Edit: I'm going to add that my ENTRY_ADDR macro was specified on top of a macro which took a given set of table indexes and converted them to the corresponding virtual address]
Post Reply