在 bootloader 啟動之前,BIOS 已經完成整個平台的初始化。雖然 CPU 還在實模式,沒有分頁,也沒有任何軟體邏輯,但 BIOS 已經知道整台機器的記憶體結構。這件事是在開機自檢(POST)階段完成的。它會啟用記憶體、掃描總線上的設備,確認哪些區塊是普通的 RAM、哪些被硬體或韌體佔用。最後 BIOS 會把這些資訊整理成一張表,這張表就是後來被 ACPI 與 E820 使用的記憶體布局。這裡有關於 E820的描述
當 bootloader 調用 INT 15h, AX=e820h 時,BIOS 並不會真的去偵測記憶體,而是直接回傳這張表。每一筆資料都包含 base、length、type,三項分別描述記憶體的起始位址、長度與屬性。type 將標示記憶體區塊是可用的、保留區、ACPI 專用區或壞掉的記憶體。對於 bootloader 而言只要蒐集所有 type=1 的範圍即可知道哪些地址可以安全使用。
調用時需設定以下暫存器,BIOS 會根據這些資訊回傳一筆 Address Range Descriptor。
暫存器 | 方向 | 功能 | 描述 |
---|---|---|---|
EAX | 輸入 | Function Code | E820h 表示請求系統記憶體地圖。 |
EDX | 輸入 | Signature | 'SMAP' (0x534D4150),用於識別 E820 請求。 |
EBX | 輸入 / 輸出 | Continuation | 第次調用時為 0 。BIOS 回傳後會更新此值,用於續傳。 |
ECX | 輸入 | Buffer Size | 調用方的緩衝區大小為 (20 或 24 bytes)。 |
ES:DI | 輸入 | Buffer Pointer | BIOS 會將 Address Range Descriptor 填到這裡。 |
暫存器 | 方向 | 功能 | 描述 |
---|---|---|---|
EAX | 輸出 | Signature | BIOS 會再次填入 'SMAP' (0x534D4150),表示成功。 |
ECX | 輸出 | Descriptor Size | 實際回傳的資料大小 20 或 24 bytes)。 |
EBX | 輸出 | Continuation | 更新續傳指標,供下一次呼叫使用。若回傳 0,表示傳輸完畢。 |
CF | 輸出 | Carry Flag | 若成功則 CF=0;失敗或不支援則 CF=1。 |
該結構的總長可能為 20 bytes 或 24 bytes,具體大小取決於是否包含 Extended Attributes 欄位。
Offset | 名稱 | 描述 |
---|---|---|
0 | BaseAddrLow | 地址的低 32 位 |
4 | BaseAddrHigh | 地址的高 32 位 |
8 | LengthLow | 長度的低 32 位 |
12 | LengthHigh | 長度的高 32 位 |
16 | Type | 用途(記憶體類型) |
20 | Extended Attributes | 可選欄位,用於標示屬性(如 ACPI 擴展旗標),若不支援則省略 |
值 | 名稱 | OS 可用 | 描述 |
---|---|---|---|
1 | AddressRangeMemory | 是 | 可用的 RAM |
2 | AddressRangeReserved | 否 | 保留給系統或設備使用 |
3 | AddressRangeACPI | 是 | ACPI Reclaim Memory,OS 讀取 ACPI 後可重新使用 |
4 | AddressRangeNVS | 否 | ACPI NVS 記憶體,系統休眠用,不可覆蓋 |
5 | AddressRangeUnusable | 否 | 壞掉的記憶體區塊 |
而對我們而言,只需要將分配給 KVM 的記憶體區塊,依照 E820 的格式填入 BIOS 中,讓 bootloader 透過 INT 15h, AX=E820h 就能直接取得這份記憶體地圖即可。
不過在 UEFI 時代,這個調用被 GetMemoryMap 取代,但概念是一樣的返回的仍然是 base、length、type 的結構,只是附加更多屬性。可參考這裡