Skip to content

Paging Implementation

opencode-agent[bot] edited this page May 9, 2026 · 1 revision

Paging Implementation

JNode's x86 hardware paging setup: flat 4GB identity-mapped address space with null-pointer guard pages and kernel read-only protection.

Overview

JNode uses hardware paging on x86 to establish a flat, identity-mapped 4GB virtual address space. Paging is initialized entirely in assembly during early boot, before the JVM is active. The design choices prioritize simplicity (no virtual-to-physical translation overhead) and safety (guard pages at null and the high end of memory).

Address Space Layout

32-bit (core/src/native/x86/mm32.asm)

The 4GB virtual address space is laid out as follows:

Virtual Range Size Page Size Protection Purpose
0x00000000 4KB 4KB Not present Null-pointer guard
0x000010000x003FFFFF 4MB 4KB RW (user) / RO (kernel) Low memory, kernel image
0x004000000xFFBFFFFF ~4080MB 4MB RW (user) / RO (kernel) Main address space
0xFFC000000xFFFFFFFF 4MB 4MB Not present High-end guard

64-bit (core/src/native/x86/mm64.asm)

The 64-bit setup uses a 4-level page table hierarchy (PML4 → PDP → PD → PT) with PAE enabled:

Structure Physical Address Entries
PML4 0x1000 512
PDP 0x2000 512
PD0–PD3 0x30000x6000 512 each
PT0 (low 2MB) 0x7000 512

The first 2MB (0x000000000x001FFFFF) is mapped via PT0 using 4KB pages (with page 0 cleared as a guard). The remaining addressable space uses 2MB pages via the PD tables.

Key Source Files

File Role
core/src/native/x86/mm32.asm 32-bit page table initialization, enable_paging
core/src/native/x86/mm64.asm 64-bit PML4/PDP/PD/PT setup, long-mode transition
core/src/native/x86/kernel.asm Calls Lsetup_mm after multiboot, before IDT
core/src/core/org/jnode/vm/memmgr/ GC heap managers (Default, MMTk) built above paging
core/src/core/org/jnode/vm/MemoryBlockManager.java Runtime physical page block allocation

How It Works

Page Table Setup (32-bit)

The setup code in mm32.asm performs these steps:

; 1. Fill Page Directory with 4MB pages (PF_DEFAULT | PSE flag)
mov eax, PF_DEFAULT | iPF_PSE
mov edi, pd_paddr     ; 0x1000
mov ecx, 1024
pd_lp:
    stosd
    add eax, 0x400000  ; next 4MB
    loop pd_lp

; 2. Fill first page table (pg0) with 4KB identity-mapped entries
; Kernel regions marked RO; user regions marked RW
mov edi, pg0_paddr     ; 0x2000
...
; 3. Clear first entry (page 0) to create null-pointer guard
and dword [edi], 0

; 4. Mark last PDE (entry 1023) as not present
and dword [pd_paddr + (1023*4)], 0

; 5. Enable paging
mov eax, pd_paddr
mov cr3, eax           ; Load page directory
mov eax, cr4
or eax, CR4_PSE        ; Enable 4MB page size extension
mov cr4, eax
mov eax, cr0
or eax, CR0_PG         ; Enable paging
mov cr0, eax

Page Table Setup (64-bit)

; Clear all table structures
CLEAR_PAGE_TABLE pml4_addr   ; 0x1000
CLEAR_PAGE_TABLE pdp0_addr    ; 0x2000
CLEAR_PAGE_TABLE pd0_addr     ; 0x3000
; ... pd1, pd2, pd3, pt0 ...

; Link PML4 → PDP → PD0-PD3
SET_PT_ENTRY pdp0_addr, 0, PF_DEFAULT   ; in PML4
SET_PT_ENTRY pd0_addr, 0, PF_DEFAULT    ; in PDP
SET_PT_ENTRY pd1_addr, 0, PF_DEFAULT
SET_PT_ENTRY pd2_addr, 0, PF_DEFAULT
SET_PT_ENTRY pd3_addr, 0, PF_DEFAULT

; Fill PDs with 2MB pages (PF_DEFAULT | iPF_PSE)
mov edi, pd0_addr
SETUP_PDIR (PF_DEFAULT | iPF_PSE)
; ... same for pd1, pd2, pd3 ...

; Override first PD entry with 4KB page table for low 2MB
SET_PT_ENTRY pt0_addr, 0, PF_DEFAULT  ; in pd0
SETUP_PTABLE PF_DEFAULT                ; fill pt0 with 4KB entries

; Clear first entry of pt0 (null guard)
SET_PT_ENTRY 0, 0, 0                   ; at pt0 start

Page Fault Handling

A page fault (interrupt 14) occurs when:

  • A PDE or PTE has its Present (P) bit cleared
  • A write is attempted to a Read-Only page
  • A user-mode access is made to a User (U) bit-cleared page

JNode's page fault handler is dispatched via the IDT (see vm-ints.asm and Interrupt-Handling). The handler determines the faulting virtual address, checks the page table entry flags, and either:

  1. Grow the heap — if the fault is in the heap region and a new physical page can be allocated
  2. Throw a NullPointerException — if the faulting address is near zero (null dereference)
  3. Throw a StackOverflowError — if the fault is in the guard page below a thread's stack
  4. Deliver a SegmentationFault-style exception — for invalid high-memory accesses

The guard pages at virtual 0x00000000 and 0xFFC00000 catch both null-pointer dereferences and accesses to unmapped high memory, converting them into Java exceptions rather than CPU faults.

Page Table Flag Definitions (kernel.asm / jnode.asm)

iPF_PRESENT   equ 0x001  ; Page is present in memory
iPF_WRITE     equ 0x002  ; Writable
iPF_USER      equ 0x004  ; Accessible from user mode
iPF_ACCESSED  equ 0x020  ; Has been accessed
iPF_DIRTY     equ 0x040  ; Has been written to
iPF_PSE       equ 0x080  ; 4MB/2MB page (PSE bit in PDE/PTE)
iPF_ADDRMASK  equ ~0xFFF ; Mask to extract physical address

PF_DEFAULT    equ iPF_PRESENT | iPF_WRITE | iPF_USER
PF_DEFAULT_RO equ iPF_PRESENT | iPF_USER

Gotchas

  • Identity mapping: Virtual addresses == physical addresses throughout the address space. No translation overhead, but also no isolation between processes.
  • Single address space: Because paging does not remap addresses, JNode relies on Java type safety and the Isolate abstraction (not hardware page protection) for process isolation.
  • Null guard: The first 4KB page (32-bit) or first page entry in PT0 (64-bit) is deliberately left unmapped. Any Object o = null; o.hashCode() pattern immediately triggers a page fault → NullPointerException.
  • Kernel read-only: The JIT-compiled kernel code and boot image are marked read-only in the page tables, catching accidental writes to code pages.
  • MMTk and paging: MMTk requests physical memory pages from MemoryBlockManager which operates above the paging layer. MMTk manages its own virtual-to-physical mapping tables internally.
  • 4MB page alignment: The 32-bit PDEs use iPF_PSE for 4MB pages throughout most of the address space. This is only valid when the physical memory being mapped is itself 4MB-aligned.

Related Pages

Clone this wiki locally