B01 — BIOS’s First Map for the OS: The e820 Memory Map

Key Insight: When the computer starts, the first problem the OS must solve is: how much memory is there and where is it?


The OS’s First Question After Power-On

After power-on, BIOS (UEFI) first completes hardware initialization. Then the OS starts booting. The first question the OS must solve is: how much physical memory is there, where is it, and which regions are usable.

Bash
启动顺序:

1. CPU 复位 → 执行 BIOS/UEFI(从 0xFFFF0 开始)
2. BIOS 执行 POST(Power-On Self-Test)→ 枚举内存设备
3. BIOS 读取 SPD → 知道每个 DIMM 的容量和速度
4. BIOS 构建 e820 内存映射表
5. BIOS 把控制权交给操作系统(bootloader)
6. 操作系统读取 e820 表 → 建立自己的内存管理

e820 表是 BIOS 和操作系统之间的"第一份合同"。

What is e820

e820 is the BIOS memory map of the x86 architecture. It is provided to the OS via INT 15h, function E820h.

Bash
e820 的全称:
  INT 15h, AX = E820h - "Get System Memory Map"

为什么叫 e820:
  - 功能号是 E820h(0xE820)
  - 调用这个中断,能获取完整的物理内存布局

返回的数据结构(SMAP):

Offset  Size    Description
------  ----    -----------
0x00    4B      Base Address(内存区域起始地址,64-bit)
0x08    8B      Length(内存区域长度,64-bit)
0x10    4B      Type(内存区域类型)

Type 取值:
  1 = AddressRangeMemory    (可用物理内存)
  2 = AddressRangeReserved   (保留/被外设占用)
  3 = ACPI Reclaim Memory    (ACPI 表,可以回收使用)
  4 = ACPI NVS Memory        (ACPI NVS,不可覆盖)
  其他  = 保留/不可用

Real Example of an e820 Table

Bash
用 dmesg 查看 e820 映射(Linux):

$ dmesg | grep -i "e820" | head -20

[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] e820:  0x0000000000000000 - 0x000000000009ffff (640 KB)
[    0.000000] e820:  0x0000000000100000 - 0x00000000bf79e000 (1918 MB)
[    0.000000] e820:  0x00000000bf79e000 - 0x00000000bf7de000 (240 KB)
[    0.000000] e820:  0x00000000bf7de000 - 0x00000000c0000000 (4224 KB)
[    0.000000] e820:  0x00000000c0000000 - 0x00000000fe000000 (960 MB)
[    0.000000] e820:  0x00000000fe000000 - 0x00000000ff000000 (16 MB)
[    0.000000] e820:  0x00000000ff000000 - 0x00000000fff00000 (15 MB)
[    0.000000] e820:  0x000000fff00000 - 0x000000ffff0000 (64 KB)

解析:
  0x0000000000000000 - 0x000000000009ffff (640 KB)         = 传统 640KB 区域(Type 1,可用)
  0x0000000000100000 - 0x00000000bf79e000 (1918 MB)       = 主内存区域(Type 1,可用)
  0x00000000c0000000 - 0x00000000fe000000 (960 MB)         = MMIO 区域(Type 2,保留)
  0x00000000fe000000 - 0x00000000ff000000 (16 MB)          = 固件区(Type 2,保留)
  0x00000000fff00000 - 0x00000000ffff0000 (64 KB)           = BIOS ROM(Type 2,保留)

总共 4GB 地址空间(0x0 ~ 0xFFFFFFFF)
  其中约 2.6GB 是可用物理内存,1.4GB 是 MMIO 保留区

Why e820 Is Needed: MMIO Regions Exist

The most important role of e820 is to tell the OS: which addresses are for peripherals and cannot be used as DRAM.

Bash
MMIO(Memory-Mapped I/O)和 DRAM 的区别:

DRAM(物理内存):
  地址 0x10000000 ~ 0x17FFFFFF = 128MB DDR 内存
  → CPU 访问这个地址,内存控制器会读写 DRAM 颗粒

MMIO(外设寄存器):
  地址 0xE0000000 ~ 0xEFFFFFFF = PCIe BAR 或集成显卡显存
  → CPU 访问这个地址,内存控制器会把这个请求发到 PCIe 总线
  → 外设寄存器响应,但 DRAM 颗粒不被访问

BIOS 的职责:
  - 扫描 PCIe 总线,发现有哪些外设
  - 给每个外设分配 MMIO 地址范围(BAR 地址)
  - 把这些范围记录在 e820 里(Type=2,保留)

操作系统读取 e820:
  → 知道 0xE0000000 ~ 0xEFFFFFFF 不能用于内存分配
  → 把这段地址从物理页分配器中排除

e820 Construction Process

Bash
e820 是怎么生成的:

1. BIOS 读取 SPD(Serial Presence Detect)
   → 知道每个 DIMM 的容量(例如:8GB DDR4 × 2 = 16GB)
   → 构建"可用内存"条目(Type=12. BIOS 枚举 PCIe 外设
   → 读取每个设备的 BAR(Base Address Register)
   → 决定 MMIO 地址范围
   → 添加"保留"条目(Type=23. BIOS 分配 ACPI 地址
   → ACPI tables 放在固定地址(通常是 0xE0000000 以下)
   → ACPI Reclaim Memory(Type=3)和 ACPI NVS(Type=44. BIOS 添加固件区域
   → BIOS ROM 在 0xFFFE0000 ~ 0xFFFFFFFF(64KB)
   → SMM(System Management Mode)区域

最终 e820 是一个连续的"内存段"列表。

Accessing e820 in Code

Bash
Linux 内核中的 e820 处理:

// arch/x86/include/asm/e820.h

struct e820_entry {
    __u64 addr;   // 起始地址
    __u64 size;   // 大小
    __u32 type;   // 类型
};

每次启动时,Linux 调用:
  e820__memory_setup() // 构建 e820
  e820__reserve_memory() // 标记保留区域
  e820__print_table() // 输出到 dmesg

C
// 用 INT 15h E820 读取内存映射的例子(x86 汇编)

mov ax, 0e820h      // AH = 0xE820, AL = 0xE820
mov edx, 534d4150h  // EDX = 'SMAP'(签名)
mov di, buffer       // ES:DI = 输出缓冲区
int 15h             // 调用 BIOS 中断
jc error            // 如果 CF=1,出错

// 返回的 buffer 格式:
// 偏移0:Base Address Low (4B)
// 偏移4:Base Address High (4B)
// 偏移8:Length Low (4B)
// 偏移12:Length High (4B)
// 偏移16:Type (4B)
// 偏移20:AcpiUid (4B,扩展用)

next_entry:
mov ax, 0e820h
mov di, buffer
int 15h
jnz done  // EAX=0 表示没有更多条目

Why 32-bit Systems Only Show 4GB of Memory

Bash
32 位系统的内存寻址限制:

32 位地址最大寻址:2^32 = 4GB

e820 里的 4GB 地址空间分配:
  0x00000000 ~ 0xBFFFFFFF:DRAM(最高 3GB 可用)
  0xC0000000 ~ 0xFFFFFFFF:MMIO(固件/显卡/PCIe,1GB)

所以即使插了 4GB 内存,32 位系统只能用到 ~2.5~3GB
其他被 MMIO 占用(地址空间被 MMIO "偷走"了)

PAE(Physical Address Extension):
  - IA-32 提供了 PAE 模式(36-bit 物理地址)
  - 可以访问最多 64GB 内存
  - 但每个进程仍然只能用 4GB 虚拟地址空间
  - 这就是为什么 32 位 Windows 服务器版能识别 >4GB 内存

64 位系统:
  - 虚拟地址 48-bit(用户空间)或 57-bit(Kernel)
  - 物理地址 48-bit~52-bit
  - 可以寻址 256TB~4PB 内存

Summary

  • e820: BIOS memory map for the OS; obtained via INT 15h E820h call
  • Structure: Base Address (64-bit) + Length (64-bit) + Type (32-bit)
  • Type Values: 1=usable memory, 2=reserved/MMIO, 3=ACPI Reclaim, 4=ACPI NVS
  • MMIO: Memory-mapped I/O; address space occupied by peripheral registers; cannot be used as DRAM
  • Why 32-bit Only Sees 4GB: Address space is partially consumed by MMIO; available DRAM space shrinks

Next (B02): The memory module’s ID card. What does the SPD chip actually store? Detailed SPD data format and how BIOS uses SPD to configure memory.


Last modified: 2024年5月23日

Author

Comments

Write a Reply or Comment

Your email address will not be published.