What is the correct structure of 1 GiB pages?
Posted: Sun Dec 27, 2020 6:47 pm
So I'm trying to set up my own preliminary paging in a 64-bit UEFI application, but according to QEMU's output, I appear to be getting a page fault when I set CR3.
I'm trying to identity map the first 512 GiB with 1 GiB pages just to get my own paging started, since it requires less paging structures.
I plan to move on to 2 MiB and 4 KiB pages once I thoroughly understand 1 GiB pages.
I could have sworn I built the PML4 and PDPT correctly by following the table 4-14 and 4-15 from the Intel manual.
Is there some bit I'm forgetting to set in my paging structure entries?
Am I not setting the addresses in the correct bits in each entry?
I was pretty darn sure I was setting each bit of the paging structure entries correctly.
Here's the logical flow:
Here's the state of CR4 just before I attempt to enable paging:
My paging structures:
My functions to create PML4 and PDPT entries:
And my function to actually populate the PML4 and PDPT and set CR3:
I'm trying to identity map the first 512 GiB with 1 GiB pages just to get my own paging started, since it requires less paging structures.
I plan to move on to 2 MiB and 4 KiB pages once I thoroughly understand 1 GiB pages.
I could have sworn I built the PML4 and PDPT correctly by following the table 4-14 and 4-15 from the Intel manual.
Is there some bit I'm forgetting to set in my paging structure entries?
Am I not setting the addresses in the correct bits in each entry?
I was pretty darn sure I was setting each bit of the paging structure entries correctly.
Here's the logical flow:
- 1. Call ExitBootServices
- 2. Disable Interrupts
- 3. Load GDT
- 4. Load IDT with ISRs
- 5. Enable Paging
- 6. Enable Interrupts
Here's the state of CR4 just before I attempt to enable paging:
Code: Select all
CR4.PAE: 1
CR4.PCIDE: 0
CR4.SMEP: 0
CR4.SMAP: 0
CR4.PKE: 0
My paging structures:
Code: Select all
// PML4
uint64_t pml4[512] __attribute__((aligned(0x1000)));
// PDPT
uint64_t pdpt[512] __attribute__((aligned(0x1000)));
Code: Select all
/**
* Creates a PML4 entry to hold the address of a PDPT.
* The resulting PML4 entry is marked as present, read/write,
* and supervisor mode.
* Page-level write through and page-level cache disable bits
* are left at 0.
*
*
* Params:
* uint64_t - the address of a PDPT
*
* Returns:
* pml4e - a PML4 entry
*/
pml4e make_pml4e(uint64_t pdpt_addr)
{
pml4e p = 0;
uint64_t bit_mask = 0x1; // single bit mask
// Bit 0 is the present flag.
// Mark the page as present.
p |= (bit_mask << 0);
// Bit 1 is the read/write flag.
// Set this entry to be read/write.
p |= (bit_mask << 1);
// Bits [51:12] are the address of the PDPT entry.
p |= (pdpt_addr << 12);
return p;
}
/**
* Creates a PDPT entry.
* The resulting PDPT entry is marked as present, read/write,
* and supervisor mode.
* Page-level write through and page-level cache disable bits
* are left at 0.
* The PAT and global translation bits are left at 0.
* The PKE bits [62:59] are left as 0 since CR4.PKE is 0.
*
* Params:
* uint64_t - base address of 1 Gib page
*
* Returns:
* pdpte - a PDPT entry
*/
pdpte make_pdpte(uint64_t page_base)
{
pdpte p = 0;
uint64_t bit_mask = 0x1; // single bit mask
// Bit 0 is the present flag.
// Mark the page as present.
p |= (bit_mask << 0);
// Bit 1 is the read/write flag.
// Set this entry to be read/write.
p |= (bit_mask << 1);
// Bit 7 is the page size bit and must be set
// in order for the PDPT entry to point to a 1 GiB page.
p |= (bit_mask << 7);
// Bits [51:30] are the base address of a 1 GiB page frame.
p |= (page_base);
return p;
}
And my function to actually populate the PML4 and PDPT and set CR3:
Code: Select all
void k_paging_init()
{
// Identity map 512 GiB of address space in the PDPT.
uint64_t phys = 0;
for (uint64_t i = 0; i < 512; i++)
{
pdpt[i] = make_pdpte(i * 0x40000000);
}
// Put the PDPT in the PML4.
pml4[0] = make_pml4e((uint64_t)(pdpt));
// Mark the rest of the entries in the PML4 as not present.
for (int i = 1; i < 512; i++)
{
pml4[i] = pml4[0] & ~((uint64_t)1);
}
// Create a new CR3 value.
uint64_t cr3 = (((uint64_t)(pml4)) << 12);
// Put the new CR3 value in CR3.
k_set_cr3(cr3);
}