🚩 核心目標

今天的目標是啟動 x86 的 分頁機制 (Paging)。這不僅是為了更靈活地管理記憶體,更是為了未來實現多工隔離與虛擬位址空間。我們成功創建了第一個 4MB 的映射空間,將虛擬地址映射至實體地址 0x00000x3FFFFF 區間。


1. 基礎理論:x86 分頁原理

在 32 位元保護模式下,分頁機制將 4GB 的位址空間切分為多個 4KB 的頁面。


2. 代碼實作:內核分頁初始化

我們在 mem.c 中配置了第一個 Page Directory 和相關的 Page Table,實現了內存映射(Identity Mapping)。

#ifndef MEM_H
#define MEM_H
#include "types.h"
#include "isr.h"

#define ENTRIES_COUNT   1024
#define PAGE_SIZE       4096

typedef struct {
    u32 present     :1; //存在位元
    u32 rw          :1; //讀寫位元
    u32 user        :1; //用戶位元
    u32 accessed    :1; //是否被存取過
    u32 dirty       :1; //是否被寫入過(僅PT)
    u32 reserved    :7; //保留位元(未被使用)
    u32 frame_addr  :20;//物理地址高20位,不需要低12位,因為4KB對齊的關係
} page_entry_t;

extern u32 page_directory[ENTRIES_COUNT];
extern u32 first_page_table[ENTRIES_COUNT];

void init_paging();
void enable_paging();
void page_fault_handler(registers_t *regs);
#endif
#include "mem.h"
#include "display.h"
#include "isr.h"

u32 page_directory[ENTRIES_COUNT] __attribute__((aligned(4096))) = {0x1};
u32 first_page_table[ENTRIES_COUNT] __attribute__((aligned(4096))) = {0x1};

void init_paging() 
{
    // 初始化頁目錄,將所有入口設置成不存在(屬性0x02)
    for (int i = 0; i < ENTRIES_COUNT; i++)
    {
        page_directory[i] = 0x00000002;
    }

    // 填充第一個頁表,mapping 整個0x00000--0x3ffff (4M空間)
    // 為什麼是4MB,因為每個頁表目錄對應4KB內存,一共1024項
    // 地址的開始就是ith * 4kb, 屬性3 (rw and present)
    for (int i = 0; i < ENTRIES_COUNT; i++)
    {
        first_page_table[i] = (i * PAGE_SIZE) | 0x3;
    }

    // 將第一個頁表放入頁目錄第一項,同時重新設置屬性為0x03
    page_directory[0] = ((u32)first_page_table) | 0x3;

    // register_interrupt_handler(14, page_fault_handler);
}

void enable_paging() {
    // 將頁目錄的位址載入到 CR3 暫存器
    __asm__ __volatile__("mov %0, %%cr3" : : "r"(page_directory));

    // 讀取 CR0,將最高位 (PG bit, 第 31 位) 設為 1
    u32 cr0;
    __asm__ __volatile__("mov %%cr0, %0" : "=r"(cr0));
    cr0 |= 0x80000000;
    __asm__ __volatile__("mov %0, %%cr0" : : "r"(cr0));
}

void page_fault_handler(registers_t *) {
    u32 faulting_address;
    __asm__ __volatile__("mov %%cr2, %0" : "=r"(faulting_address));

    kprint("PAGE FAULT! at virtual address: ");
    kprintf("%x", faulting_address);
    kprint("\\nHalting system.");
    while(1)
    {
        __asm__ __volatile__("hlt");
    }
}

3. 今日遇到的挑戰與解決方案 (The Pitfalls)

在開發過程中,我們遇到了三個極具代表性的硬核問題:

3.1 突破 18 扇區:從 CHS 切換到 LBA 模式

問題描述:隨著內核功能的增加(尤其是加入 Paging 代碼後),kernel.bin 的體積超過了 18 個扇區(約 9KB)。傳統的 CHS (Cylinder-Head-Sector) 尋址模式在讀取軟碟或模擬磁碟時會因為跨磁頭/磁道問題導致加載失敗。 解決方案:我們將引導程式讀取磁碟的邏輯從 int 0x13, ah=0x02 切換到了 LBA (Logical Block Addressing) 模式(int 0x13, ah=0x42)。LBA 將磁碟視為線性排列的扇區,徹底解決了尋址限制。

3.2 .bss 區段的陷阱:未初始化記憶體