@earthen94

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

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

1Answer

32bit OS だと、メモリの上限って4GB だと思いますが、指定した番地って4GB越えてません?

0Like

Your answer might help someone💌