x86 ページングの設定に関して
解決したいこと
x86 32ビットでページングの練習をしています。
同じ仮想アドレスに2つの異なる物理アドレスを登録して、切り替えようとしています。
以下のコードを実行するとCPUが例外を起こして再起動します。
// Bに切り替え
load_cr3(page_directory_B);
volatile uint32_t *b = (uint32_t*)0x400000;
*b = 0xDDDDDDDD; // ここで落ちる
ブートローダ→32ビットモード切り替え→検証用カーネル(ベアメタルで検証する為の儀式)
boot.asm
boot.asm
[org 0x7C00]
bits 16
start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
sti
; --- INT 13h CHS 読み込み ---
mov ah, 0x02 ; BIOS: Read Sectors
mov al, 1 ; 読み込むセクタ数 (1)
mov ch, 0 ; シリンダ = 0
mov dh, 0 ; ヘッド = 0
mov cl, 2 ; セクタ = 2 (ブートローダの次)
mov dl, 0x80 ; ドライブ番号=HDD
mov bx, 0x7E00 ; ES:BX = 読み込み先
int 0x13
jc disk_error
jmp 0x0000:0x7E00 ; 読み込んだコードへ跳ぶ
disk_error:
hlt
jmp disk_error
times 510-($-$$) db 0
dw 0xAA55
16to32.asm
16to32.asm
bits 16
global start
extern kernel_main
start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov ax, 0x0003
int 0x10
; GDTロード
lgdt [gdt_descriptor]
; プロテクトモード有効化
mov eax, cr0
or eax, 1
mov cr0, eax
; 32ビットコードへ移行
jmp 0x08:pm_start
; -----------------------------
gdt_start:
dq 0x0000000000000000 ; NULL
gdt_code:
dw 0xFFFF ; limit 0-15
dw 0x0000 ; base 0-15
db 0x00 ; base 16-23
db 0x9A ; code segment
db 0xCF ; flags
db 0x00 ; base 24-31
gdt_data:
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92 ; data segment
db 0xCF
db 0x00
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
; -----------------------------
[bits 32]
pm_start:
; データセグメント設定
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x9FC00
call kernel_main
.hlt_loop:
hlt
jmp .hlt_loop
linker.ld
linker.ld
ENTRY(start)
SECTIONS {
. = 0x7E00;
.text : {
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
*(COMMON)
}
}
同じ仮想メモリに異なる物理メモリを割り当てる
nasm -f bin boot.asm -o boot.bin
gcc -m32 -ffreestanding -c kernel.c -o kernel.o
nasm -f elf32 16to32.asm -o 16to32.o
ld -m elf_i386 -T linker.ld -o kernel.elf 16to32.o kernel.o
objcopy -O binary kernel.elf kernel.bin
cat boot.bin kernel.bin > disk.img
qemu-system-i386 -hda disk.img -monitor stdio
; qemu
xp /1wx 0x00100000
xp /1wx 0x00200000
kernel.c
#include <stdint.h>
typedef unsigned int uint32_t;
// ---- ページディレクトリ ----
uint32_t page_directory_A[1024] __attribute__((aligned(4096)));
uint32_t page_directory_B[1024] __attribute__((aligned(4096)));
// ---- ページテーブル ----
uint32_t page_table0_A[1024] __attribute__((aligned(4096))); // 0x00000000~0x003FFFFF
uint32_t page_table1_A[1024] __attribute__((aligned(4096))); // 0x00400000~0x007FFFFF
uint32_t page_table0_B[1024] __attribute__((aligned(4096)));
uint32_t page_table1_B[1024] __attribute__((aligned(4096)));
#define PAGE_PRESENT (1 << 0)
#define PAGE_RW (1 << 1)
static inline void load_cr3(uint32_t *pd) {
asm volatile("mov %0, %%cr3" :: "r"(pd));
}
static inline void enable_paging() {
uint32_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 0x80000000; // PGビット
asm volatile("mov %0, %%cr0" :: "r"(cr0));
}
void setup_pages() {
// 1. ページディレクトリを全て無効化
for (int i = 0; i < 1024; i++) {
page_directory_A[i] = 0;
page_directory_B[i] = 0;
}
// 2. 0〜4MB identity map
for (int i = 0; i < 1024; i++) {
page_table0_A[i] = (i * 0x1000) | PAGE_PRESENT | PAGE_RW;
page_table0_B[i] = (i * 0x1000) | PAGE_PRESENT | PAGE_RW;
page_table1_A[i] = 0; // 未使用は0
page_table1_B[i] = 0;
}
// 3. ページディレクトリにセット
page_directory_A[0] = ((uint32_t)page_table0_A) | PAGE_PRESENT | PAGE_RW;
page_directory_B[0] = ((uint32_t)page_table0_B) | PAGE_PRESENT | PAGE_RW;
page_directory_A[1] = ((uint32_t)page_table1_A) | PAGE_PRESENT | PAGE_RW;
page_directory_B[1] = ((uint32_t)page_table1_B) | PAGE_PRESENT | PAGE_RW;
// 仮想 0x400000 → 物理 0x00100000 (A)
page_table1_A[0] = 0x00100000 | PAGE_PRESENT | PAGE_RW;
// 仮想 0x400000 → 物理 0x00200000 (B)
page_table1_B[0] = 0x00200000 | PAGE_PRESENT | PAGE_RW;
}
void kernel_main() {
// 物理メモリに値を書いておく
*(volatile uint32_t*)0x00100000 = 0xAAAAAAAA;
*(volatile uint32_t*)0x00200000 = 0xBBBBBBBB;
// ページテーブル初期化
setup_pages();
// Aに切り替え
load_cr3(page_directory_A);
enable_paging();
// 仮想 0x400000 を読めば 0xAAAAAAAA
volatile uint32_t *v = (uint32_t*)0x400000;
*v = 0xCCCCCCCC;
//uint32_t a = *v;
// Bに切り替え
load_cr3(page_directory_B);
volatile uint32_t *b = (uint32_t*)0x400000;
*b = 0xDDDDDDDD; // ここで落ちる
while (1) { }
}
0 likes