🚩 核心目標

今天的任務是完成內核搬遷的最後一塊拼圖:將 GDT (Global Descriptor Table) 移至虛擬地址高位。同時,我們實現了物理記憶體管理員(Physical Memory Manager),利用 BIOS 提供的記憶體映射資訊,建立位圖(Bitmap)來追蹤系統中每一頁物理記憶體的使用狀態。


1. GDT 的高位重定位:徹底脫離低位地址

雖然內核代碼已經在 3GB 以上運行,但如果 GDT 仍留在低位物理地址,我們在邏輯上就不算完成了完全的隔離。

1.1 重新定義 GDTR

GDT 的指針(GDTR)儲存的是線性地址。在開啟分頁後,為了確保內核在任何情況下都能正確訪問段描述符,我們將 GDT 搬遷到內核數據段中,並更新 GDTR 指向高位虛擬地址。

1.2 刷新段寄存器

重新加載 GDT 後,必須立即刷新 cs, ds, es, fs, gsss。特別是 cs,需要透過一次遠跳轉(Far Jump)來更新。

void init_gdt() {
    gdt_ptr.limit = (sizeof(gdt_entry_t) * 3) - 1;
    gdt_ptr.base  = (u32)&gdt;

    // 1. Null segment
    gdt_set_gate(0, 0, 0, 0, 0);
    // 2. Code segment: Base=0, Limit=4GB, Type=Code/Exec/Read, Ring 0
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    // 3. Data segment: Base=0, Limit=4GB, Type=Data/Read/Write, Ring 0
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

    // 重新加載 GDTR 並刷新段暫存器
    gdt_flush((u32)&gdt_ptr);
}

void gdt_flush(u32 gdt_ptr_addr) {
__asm__ __volatile__ (
        "lgdt (%0)        \\n\\t"
        "mov $0x10, %%ax  \\n\\t"
        "mov %%ax, %%ds   \\n\\t"
        "mov %%ax, %%es   \\n\\t"
        "mov %%ax, %%fs   \\n\\t"
        "mov %%ax, %%gs   \\n\\t"
        "mov %%ax, %%ss   \\n\\t"
        "ljmp $0x08, $1f  \\n\\t"  // 修正點:去掉引用的點,改為 $1f
        "1:               \\n\\t"  // 修正點:標籤改為 1:
        : 
        : "r" (gdt_ptr_addr)
        : "eax", "memory"        // 建議加上 "memory" 屏障
    );
}

2. 物理記憶體管理 (PMM) 與位圖 (Bitmap)

要管理數 GB 的記憶體,我們需要一種高效的數據結構來標記哪些頁面(Page, 4KB)是可用的,哪些是被佔用的。

2.1 獲取 BIOS Memory Map

我們透過 BIOS 中斷 int 0x15, eax=0xE820 獲取了系統的物理記憶體分佈圖。這張圖告訴我們:

2.2 位圖 (Bitmap) 的實現

我們使用一個連續的字節數組作為位圖,每個 bit 代表一個 4KB 的物理頁: