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 reorderingalign(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).