自作OS(1) GRUBからの起動


はじめに

作りかけては断念し、というのを繰り返していたので、記録を兼ねて、記事にしていきます。

環境

今更、ブートローダーから書くのもだるいし、今時のOSっぽくないしということで、GRUBを使って起動させます。これで、アセンブリ言語を使う箇所が減らせます。

さらに、よくあるOS入門系の記事は、x86をターゲットに32bitOSを作っていますが、これも今時っぽくないので、x86_64をターゲットにしたいと思います。

なお、Bochsで、「The Multiboot Speci cation version 1.6」に準拠したものを起動させられなかったので、0.6.96準拠で行きたいと思います。

loader

GRUBに対応するには、multi boot headerが8192byte以内に存在している必要があります。それをloader.sで設定して上げる必要があります。

ついでに、なるべく早い段階でC言語で記述できるように、スタックポイントなどを指定して、メイン関数を呼び出します。

最低限の設定を行ったあと、C言語で記載した、カーネル本体にジャンプします。

loader.s
MAGIC_NUMBER    equ 0x1BADB002
MAGIC_FLAGS     equ 0x03
ALIGN_MODULE    equ 0x00000000
CHECK_SUM       equ -(MAGIC_NUMBER + MAGIC_FLAGS)

STACK_SIZE  equ 0x1000              ; Kernel Stack Size

global  loader
extern  kmain

section .mbheader
align 8
mbheader:
                dd  MAGIC_NUMBER
                dd  MAGIC_FLAGS
                dd  CHECK_SUM

section .text
align   8
loader:
;;; Initilized Stack Point
                mov esp, stack + STACK_SIZE

                push    ebx
                push    eax

                call    kmain
.loop:
                hlt
                JMP .loop

section .bss
align   8
stack:
                resb    STACK_SIZE
kernel.h
#ifndef __KERNEL_H__
#define __KERNEL_H__

typedef struct
{
    unsigned int    flags;
    unsigned int    mem_lower;
    unsigned int    mem_upper;
    unsigned int    boot_device;
    unsigned int    cmdline;
    unsigned int    mods_count;
    unsigned int    mods_addr;
    unsigned int    syms1;
    unsigned int    syms2;
    unsigned int    syms3;
    unsigned int  syms4;
    unsigned int    mmap_length;
    unsigned int    mmap_addr;
    unsigned int    drives_length;
    unsigned int    drives_addr;
    unsigned int    config_table;
    unsigned int    boot_loader_name;
    unsigned int    apm_table;
    unsigned int    vbe_control_info;
    unsigned int    vbe_mode_info;
    unsigned short  vbe_mode;
    unsigned short  vbe_interface_seg;
    unsigned short  vbe_interface_off;
    unsigned short  vbe_interrace_len;
} BOOTINFO;



void kmain(unsigned int magic, BOOTINFO *bootinfo);

#endif
kernel.c
#include <kernel.h>

void kmain(unsigned int magic, BOOTINFO *bootinfo)
{
}
link.ld
ENTRY(loader)

SECTIONS  {
        . = 0x00100000;
        .mbheader ALIGN (0x1000):
        {
                _kernel_start = .;
                *(.mbheader)
        }

        .text ALIGN (0x1000):
        {
                *(.text)
        }

        .rodata ALIGN (0x1000):
        {
                *(.rodata*)
        }
        .data ALIGN (0x1000):
        {
                *(.data)
        }
        .bss ALIGN (0x1000):
        {
                *(COMMON)
                *(.bss)
                _kernel_end = .;
        }
}

初期化状態

カーネル本体にジャンプした時点で、下記のように設定されています。

  • EAX 0x2BADB002
  • EBX ブート情報を記録したアドレスへのポインタ
  • CS 読み取り実行セグメント offset 0 limit 0xFFFFFFFF
  • DS,ES,FS,GS,SS 読み取り書き込みセグメント offset 0 limit 0xFFFFFFFF
  • A20GATE 有効
  • CR0 PG clear PE set
  • EFLAGS VM clear IF clear

今回のソース