Kernelite 0.1.0
Kernelite is a minimalist, educational operating system kernel built from scratch. The project aims to recreate core components of the Linux architecture to explore the fundamentals of operating system design and hardware-software interaction.
Loading...
Searching...
No Matches
Macros | Enumerations | Functions
page_table.c File Reference

Page table support implementation for kernel virtual memory. More...

#include "page_table/page_table.h"
#include "allocator/page_allocator.h"
#include "fdt/fdt.h"
#include "linker/linker_defines.h"
#include "linker/symbols.h"
#include "mem_layout/mem_layout.h"
#include "utils/kprintf.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
Include dependency graph for page_table.c:

Macros

#define PAGE_SHIFT   12U
 
#define IDX_MASK   0x1FFUL
 
#define PA_47_12_MASK   0xFFFFFFFFFULL
 
#define PA_49_48_SHIFT   48U
 
#define PA_49_48_MASK   0x3ULL
 
#define PA_51_50_SHIFT   50U
 
#define PA_51_50_MASK   0x3ULL
 

Enumerations

enum  aptable_values { APTABLE_EL0_NO_RESTRICTION_EL1_NO_RESTRICTION = 0b00 , APTABLE_EL0_NO_ACCESS_EL1_NO_RESTRICTION = 0b01 , APTABLE_EL0_NO_WRITE_EL1_NO_WRITE = 0b10 , APTABLE_EL0_NO_ACCESS_EL1_NO_WRITE = 0b11 }
 Access Permission Table bits (APTable[1:0]) for Stage 1 Table Descriptors. More...
 
enum  ap_values { AP_PRIV_RW = 0b00 , AP_PRIV_UNPRIV_RW = 0b01 , AP_PRIV_RO = 0b10 , AP_PRIV_UNPRIV_RO = 0b11 }
 AP[2:1] data access permission encodings for Stage 1 leaf descriptors. More...
 
enum  sh_values { SH_NON_SHAREABLE = 0b00 , SH_RESERVED = 0b01 , SH_OUTER_SHAREABLE = 0b10 , SH_INNER_SHAREABLE = 0b11 }
 SH[1:0] shareability domain encodings for Stage 1 leaf descriptors. More...
 

Functions

static void set_page_table_entry_address (table_desc_t *entry, const phy_addr phys_addr)
 Set the physical Next-Level Table Address (NLTA) in a table descriptor.
 
static void set_page_desc_entry_address (page_desc_t *entry, const phy_addr phys_addr)
 Set the physical Output Address (OA) in a block or page data descriptor.
 
static phy_addr get_page_table_entry_address (const table_desc_t *entry)
 Get the physical address from a page table entry.
 
static bool check_table_permissions (const table_desc_t *entry, const page_permissions_t perms)
 Checks if a table descriptor's hierarchical permissions allow the requested page permissions.
 
static void print_indent (int level)
 Helper to generate visual indentation based on the current tree depth.
 
static void page_table_init (page_table_t *table)
 Initialize a page table.
 
static void set_next_level_table (page_table_entry_t *entry, phy_addr next_table_addr)
 Set the next level table address in a page table entry union.
 
static bool allocate_new_table (table_desc_t *entry)
 Allocate and zero-initialize a new page table, then install it as the next level table in the entry.
 
static page_table_tget_next_level_table (table_desc_t *entry, const page_permissions_t perms)
 Get the next level table from a page table entry, allocating it on demand if the entry is not yet valid.
 
static bool map_mem_region (memory_region_t *region, page_table_t *root_table)
 Maps one physical region into the higher-half kernel map.
 
void print_page_descriptor (const page_desc_t desc)
 Pretty-prints the contents of a Stage 1 Block/Page descriptor.
 
void print_table_descriptor (const table_desc_t desc)
 Pretty-prints the contents of a Stage 1 Table descriptor (Levels 0, 1, or 2).
 
void print_page_table_tree (const page_table_entry_t *table_base, int current_level, uint64_t base_va)
 Recursively prints the structure of the page table tree starting from a given base table.
 
bool map_page (page_table_t *root, const virt_addr v_addr, const phy_addr phy_addr, const page_permissions_t perms, const mem_type_t mem_type)
 Map a virtual address to a physical address in the page table.
 
void dump_memory_map (page_table_t *root)
 Dump the memory map for debugging.
 
bool setup_kernel_id_map (phy_addr serial_device_addr_base)
 Set up the initial identity-mapped region for the kernel.
 
bool setup_kernel_map (memory_map_t *const mmap)
 Set up the kernel's higher-half virtual-memory map.
 

Detailed Description

Page table support implementation for kernel virtual memory.

This module contains the kernel page table implementation used to manage virtual memory mappings and page table creation routines.

Author
Abhin Parekadan Jose
Date
2026-05-17

Macro Definition Documentation

◆ IDX_MASK

#define IDX_MASK   0x1FFUL

Index mask for page table entries

◆ PA_47_12_MASK

#define PA_47_12_MASK   0xFFFFFFFFFULL

36-bit field: PA[47:12]

◆ PA_49_48_MASK

#define PA_49_48_MASK   0x3ULL

2-bit field: PA[49:48]

◆ PA_49_48_SHIFT

#define PA_49_48_SHIFT   48U

Shift for PA[49:48]

◆ PA_51_50_MASK

#define PA_51_50_MASK   0x3ULL

2-bit field: PA[51:50]

◆ PA_51_50_SHIFT

#define PA_51_50_SHIFT   50U

Shift for PA[51:50]

◆ PAGE_SHIFT

#define PAGE_SHIFT   12U

Shift for page offset

Enumeration Type Documentation

◆ ap_values

enum ap_values

AP[2:1] data access permission encodings for Stage 1 leaf descriptors.

Encodes the direct permissions written into bits [7:6] of a block or page descriptor. For a stage 1 translation supporting two Exception levels (Table D8-63, ARMv8/ARMv9 ARM):

+-------—+----------------------------------------—+ | AP[2:1] | Permissions | +-------—+----------------------------------------—+ | 0b00 | PrivRead, PrivWrite | | 0b01 | PrivRead, PrivWrite, UnprivRead, UnprivWrite | | 0b10 | PrivRead | | 0b11 | PrivRead, UnprivRead | +-------—+----------------------------------------—+

"Priv" == EL1 (kernel), "Unpriv" == EL0 (user).

Enumerator
AP_PRIV_RW 

[0b00] EL1 read/write. EL0 no access.

AP_PRIV_UNPRIV_RW 

[0b01] EL1 and EL0 read/write.

AP_PRIV_RO 

[0b10] EL1 read-only. EL0 no access.

AP_PRIV_UNPRIV_RO 

[0b11] EL1 and EL0 read-only.

◆ aptable_values

Access Permission Table bits (APTable[1:0]) for Stage 1 Table Descriptors.

This hierarchical control restricts the maximum permissions allowed for all downstream block or page descriptors reachable through this table entry.

Note
These controls can only restrict permissions down the chain; they cannot grant more privileges than what a leaf L3 page descriptor specifies.

+---—+-------------------------—+-------------------------—+ |Value | EL0 (Unprivileged) | EL1 (Privileged) | +---—+-------------------------—+-------------------------—+ | 0b00 | No restriction | No restriction | | 0b01 | No Access | No restriction | | 0b10 | Read-Only (No write) | Read-Only (No write) | | 0b11 | No Access | Read-Only (No write) | +---—+-------------------------—+-------------------------—+

Note
APTable[0] is treated as 0 when virtualization is active and HCR_EL2.{NV,NV1} == {1,1}.
Enumerator
APTABLE_EL0_NO_RESTRICTION_EL1_NO_RESTRICTION 

[0b00] No downstream permission modifications applied.

APTABLE_EL0_NO_ACCESS_EL1_NO_RESTRICTION 

[0b01] APTable[0]=1: strip EL0 access entirely. EL1 unaffected.

APTABLE_EL0_NO_WRITE_EL1_NO_WRITE 

[0b10] APTable[1]=1: force read-only for all ELs.

APTABLE_EL0_NO_ACCESS_EL1_NO_WRITE 

[0b11] Highly restricted. EL0: No Access, EL1: Forced Read-Only.

◆ sh_values

enum sh_values

SH[1:0] shareability domain encodings for Stage 1 leaf descriptors.

Written into bits [9:8] of a block or page descriptor. Controls which observers share the coherency domain for Normal memory accesses. For Device memory the shareability is always Outer Shareable regardless of this field.

+-----—+---------------—+ | SH[1:0]| Domain | +-----—+---------------—+ | 0b00 | Non-shareable | | 0b01 | RESERVED | | 0b10 | Outer Shareable | | 0b11 | Inner Shareable | +-----—+---------------—+

Enumerator
SH_NON_SHAREABLE 

[0b00] Non-shareable — no coherency requirement with other observers.

SH_RESERVED 

[0b01] RESERVED — must not be used.

SH_OUTER_SHAREABLE 

[0b10] Outer Shareable — coherent with the outer shareability domain (typically all CPUs + GPU + DMA-capable devices).

SH_INNER_SHAREABLE 

[0b11] Inner Shareable — coherent within the inner shareability domain (typically all CPUs in the same cluster).

Function Documentation

◆ allocate_new_table()

static bool allocate_new_table ( table_desc_t entry)
static

Allocate and zero-initialize a new page table, then install it as the next level table in the entry.

Parameters
entryPointer to the page table entry to modify.
Returns
true if successful, false otherwise.

◆ check_table_permissions()

static bool check_table_permissions ( const table_desc_t entry,
const page_permissions_t  perms 
)
static

Checks if a table descriptor's hierarchical permissions allow the requested page permissions.

Parameters
entryPointer to the target table descriptor structure.
permsThe incoming software page permissions to validate against the table restrictions.
Returns
true if the table permits the mapping rules; false if the table actively restricts them.

◆ dump_memory_map()

void dump_memory_map ( page_table_t root)

Dump the memory map for debugging.

Parameters
rootPointer to the root page table to dump.

◆ get_next_level_table()

static page_table_t * get_next_level_table ( table_desc_t entry,
const page_permissions_t  perms 
)
static

Get the next level table from a page table entry, allocating it on demand if the entry is not yet valid.

Parameters
entryPointer to the page table entry.
permsThe requested page permissions for the downstream mapping.
Returns
Pointer to the next level page table, or NULL if entry was NULL, allocation of a new table failed, or the existing entry's permissions don't permit the requested page profile.

◆ get_page_table_entry_address()

static phy_addr get_page_table_entry_address ( const table_desc_t entry)
inlinestatic

Get the physical address from a page table entry.

Parameters
entryPointer to the page table entry.
Returns
The physical address.

◆ map_mem_region()

static bool map_mem_region ( memory_region_t region,
page_table_t root_table 
)
static

Maps one physical region into the higher-half kernel map.

Maps every page of region to its higher-half virtual address (physical + KERNEL_BASE). Pages that fall inside the kernel image [image_start, image_end) are mapped RWX; all other pages are mapped RW.

Parameters
regionPhysical-memory region to map; its base is assumed to be page-aligned.
root_tableRoot of the page table to populate.
Returns
true if every page was mapped, false on a NULL argument or a failed page mapping.

◆ map_page()

bool map_page ( page_table_t root,
virt_addr  v_addr,
phy_addr  phy_addr,
page_permissions_t  perms,
mem_type_t  mem_typ 
)

Map a virtual address to a physical address in the page table.

Walks the four-level (L0→L3) translation table rooted at root, allocating intermediate table descriptors as needed, and installs a leaf page descriptor at L3 that covers the 4 KB page containing v_addr. The descriptor's AttrIndx field is set from mem_typ, selecting the matching MAIR_EL1 slot (see mem_type_t).

Parameters
rootPointer to the root (L0) page table. Must not be NULL.
v_addrVirtual address to map; only bits [47:12] are significant (the 4 KB page base — lower bits are ignored).
phy_addrPhysical address of the target frame; must be 4 KB-aligned.
permsSoftware permission flags (page_permissions_t) that are translated into the AP[2:1], PXN, and UXN hardware fields.
mem_typMemory type index (mem_type_t) written into AttrIndx[2:0] of the leaf descriptor, selecting the MAIR_EL1 attribute byte (e.g. DEVICE → 0x00 Device-nGnRnE, NORMAL → 0xFF Normal WB-RWA Cacheable).
Returns
true Mapping installed successfully.
false root was NULL, v_addr/phy_addr were not 4 KB aligned, or allocation of an intermediate table failed.

◆ page_table_init()

static void page_table_init ( page_table_t table)
static

Initialize a page table.

Parameters
tablePointer to the page table to initialize.

◆ print_indent()

static void print_indent ( int  level)
static

Helper to generate visual indentation based on the current tree depth.

Parameters
levelThe current depth level in the page table tree (0 for root).

◆ print_page_descriptor()

void print_page_descriptor ( const page_desc_t  desc)

Pretty-prints the contents of a Stage 1 Block/Page descriptor.

Parameters
descThe page descriptor structure to decode.

◆ print_page_table_tree()

void print_page_table_tree ( const page_table_entry_t table_base,
int  current_level,
uint64_t  base_va 
)

Recursively prints the structure of the page table tree starting from a given base table.

Parameters
table_basePointer to the base of the current page table level.
current_levelThe current level in the page table hierarchy (0-3).
base_vaThe starting virtual address range covered by this table.

◆ print_table_descriptor()

void print_table_descriptor ( const table_desc_t  desc)

Pretty-prints the contents of a Stage 1 Table descriptor (Levels 0, 1, or 2).

Parameters
descThe table descriptor structure to decode.

◆ set_next_level_table()

static void set_next_level_table ( page_table_entry_t entry,
phy_addr  next_table_addr 
)
static

Set the next level table address in a page table entry union.

Parameters
entryPointer to the root page table entry union to modify.
next_table_addrPhysical address of the next level page table (must be 4KB page aligned).

◆ set_page_desc_entry_address()

static void set_page_desc_entry_address ( page_desc_t entry,
const phy_addr  phys_addr 
)
inlinestatic

Set the physical Output Address (OA) in a block or page data descriptor.

Parameters
entryPointer to the page/block data descriptor structure.
phys_addrThe target 4KB-aligned physical address to assign.

◆ set_page_table_entry_address()

static void set_page_table_entry_address ( table_desc_t entry,
const phy_addr  phys_addr 
)
inlinestatic

Set the physical Next-Level Table Address (NLTA) in a table descriptor.

Parameters
entryPointer to the table descriptor structure.
phys_addrThe target 4KB-aligned physical address to assign.

◆ setup_kernel_id_map()

bool setup_kernel_id_map ( phy_addr  serial_device_addr_base)

Set up the initial identity-mapped region for the kernel.

Creates the early page-table mappings needed during low-virtual-address boot, including the serial-device mapping used by the early console.

Parameters
serial_device_addr_basePhysical base address of the serial device to map during early boot.
Returns
true on success, false on failure such as an allocation error or invalid mapping setup.

◆ setup_kernel_map()

bool setup_kernel_map ( memory_map_t mmap)

Set up the kernel's higher-half virtual-memory map.

Maps every region described by mmap into the higher half, giving pages within the kernel image RWX permissions and all other pages RW.

Parameters
mmapPointer to the memory map structure describing the available physical-memory regions.
Returns
true on success, false on failure such as an empty memory map, an allocation error, or an invalid mapping configuration.