Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Page Structures in Rust

The C structures for paging map directly to Rust. Understanding the C originals helps when reading documentation and OSDev resources.

The Page Entry

In C, a page entry is often defined as a bitfield:

struct page {
    u32 present:1;   // Present in memory
    u32 rw:1;        // Read-only if clear
    u32 user:1;      // Supervisor only if clear
    u32 accessed:1;  // Has page been accessed?
    u32 dirty:1;     // Has page been written?
    u32 unused:7;    // Reserved bits
    u32 frame:20;    // Frame address (shifted right 12)
};

In Rust, we use a plain u32 with bit manipulation:

#![allow(unused)]
fn main() {
// A page entry is just a u32 with the address and flags packed in
// Physical address occupies bits 31-12, flags occupy bits 11-0
let entry: u32 = physical_address | PAGE_PRESENT | PAGE_WRITABLE;
}

We don’t use Rust bitfields — they’re not part of the language. Bit manipulation with constants is cleaner and more explicit.

Page Directory

// C
struct page_directory {
    struct page tables[1024];
};
#![allow(unused)]
fn main() {
// Rust
#[repr(C, align(4096))]
pub struct PageDirectory {
    entries: [u32; 1024],
}

impl PageDirectory {
    pub const fn new() -> Self {
        Self { entries: [0; 1024] }
    }
}
}

#[repr(C, align(4096))] is critical:

  • repr(C) — C layout, no Rust reordering
  • align(4096) — Must start on a 4KB boundary. The CPU requires this; CR3 stores the physical address with the lower 12 bits masked off.

Page Table

// C
struct page_table {
    struct page pages[1024];
};
#![allow(unused)]
fn main() {
// Rust — same structure as PageDirectory
// In our implementation, page tables are raw *mut u32 pointers
// because they're allocated dynamically from the frame allocator
}

In our implementation, we don’t have a named PageTable struct because page tables are created dynamically. We allocate a frame and treat its address as a *mut u32 pointing to 1024 entries.

Memory Layout of a Page Directory

Physical address of PageDirectory (must be 4KB-aligned):
┌──────────────────────────────────┐  ← 4KB aligned address
│ entries[0]   (4 bytes)           │  → Points to page table 0 (covers 0x00000000-0x003FFFFF)
│ entries[1]   (4 bytes)           │  → Points to page table 1 (covers 0x00400000-0x007FFFFF)
│ ...                              │
│ entries[1022] (4 bytes)          │  → 0 (unmapped)
│ entries[1023] (4 bytes)          │  → Points back to THIS directory (recursive mapping)
└──────────────────────────────────┘  ← 4KB aligned address + 4096 bytes

The entire structure is exactly one page (4KB = 1024 × 4 bytes).