Setting up a provisional identical mapping for long mode, using 1G huge page on PDP(PUD).

x86_64 long mode mandates paging. Here we set up a provisional identical mapping for the early booting.

Math
Each PUD entry (on the 3rd level) maps to 1G memory region. We could set up 512 GiB mapping with only one single 3rd level table (i.e. no need for page directory or page table), this is ideal for a early boot provisional mapping.

We need:

  1. one PML4 table (and only 1 single entry pointing to the PUD table), that's 4KiB in size
  2. one PUD table, populated with 512 entries, each pointing to 1G memory region, this is another 4KiB in size.
  3. in total, we need to setup 2 tables and 1 + 512 = 513 entries.

assembly example

[SECTION .text]
[BITS 32]
; skipping other code.
setup_paging:
    ; PML4 (Page Map Level 4 / 1st level)
    mov    eax, pdp
    or     eax, 0xf
    mov    dword [pml4+0], eax
    mov    dword [pml4+4], 0
    ; PDPE flags
    ; start-address bytes bit [30:31] + flags
    mov    eax, 0x0 | 0x87    
    ; start-address bytes bit [32:38]
    mov    ebx, 0
    mov    ecx, 0
    fill_tables2:
    ; fill one single PDP table, with 1G pages, 
    ; 512 PDPE maps to 512 GB
    cmp    ecx, MAX_MEM
    je     fill_tables2_done
    ; low bytes
    mov    dword [pdp + 8*ecx + 0], eax
    ; high bytes
    mov    dword [pdp + 8*ecx + 4], ebx
    ; 1G per entry
    add    eax, 0x40000000
    ; increment bit[32:38] when eax overflows
    adc    ebx, 0
    inc    ecx
    ja     fill_tables2
    fill_tables2_done:
    ; set base pointer to PML4
    mov    eax, pml4
    mov    cr3, eax
    ret

[SECTION .pagetables]
[GLOBAL pml4]
[GLOBAL pdp]

pml4:
    resb   4096
    alignb 4096

pdp:
    resb   4096
    alignb 4096

Note that, each entry is 64bit, but when we are setting up the page tables, we are not in long mode yet, meaning that only 32bit registers/instructions are available. See how the code use both eax and ebx to keep the address with the carry flag (adc).

Also make sure to link and align the pagetables section properly. e.g.

/* global page table for 64-bit long mode */
.global_pagetable ALIGN(4096) (NOLOAD) :
{
    *(".global_pagetable")
}