Files
nel_os_uefi/nel_os_kernel/triple_fault_fix.md
2025-07-03 01:09:55 +09:00

3.7 KiB
Raw Blame History

Triple Fault 修正内容

問題の概要

paging::initialize()関数を実行するとQEMUがトリプルフォルトで落ちる問題が発生していました。

原因

  1. カーネルの仮想アドレスを物理アドレスとして使用

    • 静的変数のページテーブルのアドレスをそのまま物理アドレスとして扱っていた
    • カーネルは0x100000付近の仮想アドレスで動作しているが、これは物理アドレスではない
  2. 物理アドレス0x0の使用

    • ビットマップメモリアロケータが最初の1MBを予約していなかった
    • 物理アドレス0x0にページテーブルを配置しようとしてトリプルフォルト
  3. 不十分なメモリマッピング範囲

    • 最初は4GBまでしかマッピングしていなかった
    • ヒープが5GB付近に配置されたためページフォルトが発生

修正内容

1. ビットマップメモリアロケータの修正 (memory.rs)

// 最初の1MB256ページを予約
const RESERVED_PAGES: usize = 256; // 1MB / 4KB

let mut table = Self {
    used_map,
    start: RESERVED_PAGES,  // 0ではなく256から開始
    end: usize::MAX,
};

// 最初の1MBを明示的に予約済みとしてマーク
for i in 0..RESERVED_PAGES {
    table.set_frame(i, false);
}

2. ページテーブル初期化の修正 (paging.rs)

// 静的変数を削除し、動的に物理メモリを確保
pub unsafe fn initialize(bitmap_table: &mut BitmapMemoryTable) -> PhysFrame {
    let new_frame = unsafe { initialize_identity_mapping(bitmap_table) };
    // ...
}

unsafe fn initialize_identity_mapping(bitmap_table: &mut BitmapMemoryTable) -> PhysFrame {
    // 物理フレームを動的に確保
    let pml4_frame = bitmap_table.allocate_frame().expect("Failed to allocate PML4 frame");
    let pml4_addr = pml4_frame.start_address().as_u64();
    let pml4_table = unsafe { &mut *(pml4_addr as *mut PageTable) };
    *pml4_table = PageTable::new();
    
    // PDPテーブルも同様に確保
    let pdp_frame = bitmap_table.allocate_frame().expect("Failed to allocate PDP frame");
    // ...
}

3. メモリマッピング範囲の拡張

// 4GBから64GBへ拡張
// Create identity mapping for first 64GB (64 PDP entries, each covering 1GB)
for i in 0..64 {
    // 各1GBエントリに対してページディレクトリを作成
    // ...
}

4. main.rsの修正

// bitmap_tableをpaging::initialize()に渡す
let mut mapper = {
    unsafe { paging::initialize(&mut bitmap_table) };
    let lv4_table = get_active_level_4_table();
    unsafe { OffsetPageTable::new(lv4_table, VirtAddr::new(0x0)) }
};

5. unsafe操作の適切な処理

// unsafe操作をunsafeブロックで囲む
let new_frame = unsafe { initialize_identity_mapping(bitmap_table) };

// raw constを使用してmutable staticへの参照を作成
PML4_TABLE[0].set_frame(phys_frame(&raw const PDP_TABLE), flags);

結果

  • 物理メモリアロケータが正しく0x208000から物理フレームを割り当てるようになった
  • 64GBまでの恒等マッピングにより、5GB付近のヒープアクセスも成功
  • トリプルフォルトが解消され、カーネルが正常に動作するようになった

学んだこと

  1. カーネルの仮想アドレスと物理アドレスを区別することの重要性
  2. 低位メモリ0-1MBは予約領域として扱うべき
  3. ページテーブルのマッピング範囲は使用するメモリ全体をカバーする必要がある
  4. UEFIは恒等マッピングを提供しているため、物理アドレスへの直接アクセスが可能