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 requestEBX: Offset counter (initialize to 0, use returned value for subsequent calls)ES:DI: Buffer pointer for ARDS storageECX = 20: ARDS structure size in bytesEDX = 0x534D4150: Required 'SMAP' signature
Return Values
EAX: Maintains 'SMAP' signature on successEBX = 0: Indicates final entry when zeroCF = 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:
- Multiply high 10 bits by 4, add to page directory base → PDE physical address
- Read PDE content to obtain page tible physical address
- Multiply middle 10 bits by 4, add to page table address → PTE physical address
- Read PTE to get target physical address
- 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