Protected Mode Memory Management and Paging

Flat Memory Model

Contemporary operating systems typically avoid complex segmentation schemes, instead adopting a flat memory model combined with paging for memory management.

The flat memory model, widely used in modern systems like Linux and Windows, simplifies address translation by setting all segment bases to zero and limits to 4GB. This creates a contiguous virtual addres space appearance, allowing processes full access to their virtual memory. While the theoretical address range spans 0 to 4GB, actual physical memory may be significantly less.

Assembly Implementation

[SECTION .gdt]
CODE_SEGMENT: dd 0x0000FFFF
              dd 0x00CF9800
DATA_SEGMENT: dd 0x0000FFFF
              dd 0x00CF9200

Physical Memory Detection

Several methods exist for determining system memory:

  • INT 0x12: Reports conventional memory below 1MB
  • INT 0x15, AH=0x88: Provides extended memory above 1MB without layout details
  • INT 0x15, EAX=0xE820: Modern approach offering complete memory map information
  • CMOS Reading: Limited to 64MB extended memory detection
  • UEFI Services: Advanced capabilities requiring EFI environment

The INT 0x15, EAX=0xE820 method is preferred for contemporary systems.

E820 Memory Map Retrieval

This interrupt returns one Address Range Descriptor Structure (ARDS) per call. Complete physical memory requires iterating through all Type=1 entries and accumulating totals in a buffer (since interrupts become unavailable in protected mode).

Input Parameters

  • EAX = 0xE820: Function identifier for memory layout request
  • EBX: Offset counter (initialize to 0, use returned value for subsequent calls)
  • ES:DI: Buffer pointer for ARDS storage
  • ECX = 20: ARDS structure size in bytes
  • EDX = 0x534D4150: Required 'SMAP' signature

Return Values

  • EAX: Maintains 'SMAP' signature on success
  • EBX = 0: Indicates final entry when zero
  • CF = 0: Success flag (CF = 1 indicates error)
  • ES:DI: Contains populated ARDS data

Implementation Example

; Memory scanning routine
xor ebx, ebx
mov edx, 0x534D4150
mov di, MemoryBuffer
.scan_loop:
    mov eax, 0xE820
    mov ecx, 20
    int 0x15
    jc .scan_failed
    add di, cx
    inc word [DescriptorCount]
    cmp ebx, 0
    jnz .scan_loop
    mov cx, word [DescriptorCount]
    mov ebx, MemoryBuffer
    xor edx, edx
.locate_available:
    mov eax, [ebx]
    add eax, [ebx + 8]
    add ebx, 20
    cmp edx, eax
    jge .check_next
    mov edx, eax
.check_next:
    loop .locate_available
    jmp .memory_success
.scan_failed:
    ; Error handling
    halt
.memory_success:
    mov [SystemMemorySize], edx

Paging Architecture

Compilers generate continuous addresses known as linear addresses. In pure segmentation, CPUs treat linear addresses as physical addresses, creating limitations:

  • Segmantation requires physically contiguous memory blocks, leading to fragmentation issues during large allocations
  • Inadequate process isolation due to shared memory models and potential descriptor misconfigurations

Paging overcomes these by dividing memory into fixed-size pages that can be non-contiguous in physical memory. Hardware-level CPU support enables automatic linear-to-physical address translation via page tables.

Single-Level Page Tables

CPU specification defines 4KB page sizes, partitioning 4GB space into 1 million pages. 32-bit linear addresses split into high 20 bits (page table index) and low 12 bits (page offset).

Two-Level Page Tables

Single-level tables require 4MB for full mapping, consuming excessive memory per process. Two-level paging distributes 1 million pages across 1024 tables, each containing 1024 entries (4 bytes each), fitting perfectly within 4KB pages.

x86 two-level addressing divides linear space into:

  • High 10 bits: Page Directory Entry (PDE) selector
  • Middle 10 bits: Page Table Entry (PTE) selector
  • Low 12 bits: Page offset

Address translation process:

  1. Multiply high 10 bits by 4, add to page directory base → PDE physical address
  2. Read PDE content to obtain page tible physical address
  3. Multiply middle 10 bits by 4, add to page table address → PTE physical address
  4. Read PTE to get target physical address
  5. Add low 12 bits for final physical address

Page Table Entry Structure

Bit Position Name Description Values
0 P (Present) Page availability 0: Not present, 1: Present
1 R/W Access permissions 0: Read-only, 1: Read/write
2 U/S Privilege level 0: Supervisor, 1: User
3 PWT Write policy 0: Write-back, 1: Write-through
4 PCD Cache control 0: Cached, 1: Uncached
5 A Access tracking 0: Unaccessed, 1: Accessed
6 D Modification status 0: Clean, 1: Modified
7 PS Page size 0: 4KB, 1: 4MB
8 G Global page flag 0: Local, 1: Global
9-11 AVL Reserved for OS OS-defined usage
12-31 Base Address Aligned physical address 4KB-aligned base address

The page directory base address must be stored in CR3 register before enabling paging.

Multiprocess Support Through Paging

  • Independent page tables per process ensure memory isolation
  • Virtual memory management through demand paging, sharing, and swapping
  • Address space division between user and kernel regions

User processes rely on kernel system calls mapped to high virtual addresses. All processes share identical physical mappings for kernel space.

Linux-Style Address Mapping Example

initialize_paging:
    mov ecx, 4096
    mov esi, 0
.zero_directory:
    mov byte [DIRECTORY_BASE + esi], 0
    inc esi
    loop .zero_directory
.setup_entries:
    mov eax, DIRECTORY_BASE
    add eax, 0x1000
    mov ebx, eax
    or eax, PG_US_U | PG_RW_W | PG_P
    ; Map first 4MB to both low and high addresses
    mov dword [DIRECTORY_BASE + 0x0], eax
    mov dword [DIRECTORY_BASE + 0xc00], eax
    ; Self-reference directory entry
    sub eax, 0x1000
    mov dword [DIRECTORY_BASE + 4092], eax

    ; Configure initial page table entries
    mov ecx, 1024
    mov esi, 0
    mov edx, PG_US_U | PG_RW_W | PG_P
.fill_entries:
    mov dword [ebx + esi * 4], edx
    add edx, 4096
    inc esi
    loop .fill_entries

    ; Setup kernel page directory entries
    mov eax, DIRECTORY_BASE
    add eax, 0x2000
    or eax, PG_RW_W | PG_US_U | PG_P
    mov ebx, DIRECTORY_BASE
    mov ecx, 254
    mov esi, 769
.kernel_entries:
    mov [ebx + esi * 4], eax
    inc esi
    add eax, 0x1000
    loop .kernel_entries
    ret

Kernel Loading Process

GNU toolchain-generated kernels are ELF executable files requiring:

  • Link-time specification of code segment linear address (high 0xc0000000 region)
  • Loading raw kernel image to safe memory location
  • ELF section expansion into kernel space
  • Transfer of execution control to kernel entry point

Memory Layout Strategy

  • Kernel load addresses should reside in kernel space without overlapping loader regions (0x900-0x1500), EBDA (0x9FC00-0x9FFFF), or source image areas
  • Post-loading, original kernel images may be overwritten except for critical loader regions
; Kernel configuration constants
PAGE_DIRECTORY_BASE     equ 0x500000
KERNEL_VIRTUAL_ADDRESS  equ 0xc0100000
KERNEL_DISK_SECTOR      equ 0x9
KERNEL_LOAD_ADDRESS     equ 0x300000
KERNEL_STACK_BASE       equ 0xc0400000

Tags: protected-mode paging memory-management x86-assembly operating-systems

Posted on Sun, 09 Aug 2026 16:52:28 +0000 by pedrolopes10