GRUB2から自作OSを起動する

# はじめに
ひさしぶりに[30日でできる! OS自作入門]を読み返しだした．
本ではフロッピーからOSを起動しているが，後々にUSBからOSを起動したいので，GRUBから自作OSの起動方法を調べてみた．

該当しそうな記事がいくつか見つかったので，参考に自分でも試してみた．

- [Linuxをはじめよう！：1から創る自作OS 0x20]
 - GRUBからのブート方法，load.S，boot.cの作成
- [Linuxをはじめよう！：1から創る自作OS 0x30]
 - Makefileの作成
- [ももいろテクノロジー：GRUBで簡単なOSカーネルを動かしてみる]
 - ISOファイルの作成方法
- [０から作るOS開発　Grubその４　GRUBから自作OSを起動する]
 - VirtualBoxからISOファイルを開く方法

手元にある環境で試したので，開発環境は少し複雑で，自作OS開発はvagrant上のcentos 7で，自作OS（ISOファイル）の実行はホスト側（windows 10）のVirtual Boxで実施している．

# 作成手順
## 1. 自作OSのソースコードを作成する
GRUBから自作OSを起動するには，[Multiboot Specification](https://www.gnu.org/software/grub/manual/multiboot/multiboot.html)の決まりに従って作成するとよいらしい．

### 1.1 multiboot.hをダウンロードする
https://www.gnu.org/software/grub/manual/multiboot/html_node/multiboot_002eh.html
作業フォルダのinclude/以下に保存する．

### 1.2 C言語を開始するためのアセンブラ部分を作成
```nasm:load.S
#define ASM_FILE 1
#include "multiboot.h"
#define EXT_C(func) func
#define STACK_SIZE 0x4000
#define MULTIBOOT_HEADER_FLAGS 0x00000003

.text
.global start, _start

start:
_start:
        jmp     multiboot_entry
        .align  4

multiboot_header:
        .long   MULTIBOOT_HEADER_MAGIC
        .long   MULTIBOOT_HEADER_FLAGS
        .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

multiboot_entry:
        movl    $(stack + STACK_SIZE), %esp
        pushl   $0
        popf
        pushl   %ebx
        pushl   %eax

        call    EXT_C(cstart)

loop:   hlt
        jmp     loop

        .comm   stack, STACK_SIZE
```

### 1.3 C言語の記述
```c:boot.c
#include "multiboot.h"

void cstart(unsigned long magic, unsigned long addr)
{
        unsigned short *screen = (unsigned short *)0xb8000;
        char *msg = "Starting MyOS!";
        char ch;
        char attr = 0x0f;

        while (ch = *msg++) {
                *screen++ = ((attr) << 8) | ch;
        }
        return;
}
```
（このあたりは，ほぼ[Linuxをはじめよう！：1から創る自作OS 0x20]のまま）

## 2. コンパイルするためのMakefileを作成する
```Makefile:Makefile
TARGET = myos.elf

CC = gcc
LN_S = ln -s
RANLIB = ranlib
INCLUDES = -I./include
FLAGS = -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c
LD = ld  -melf_i386  -Ttext=0x100000 --oformat elf32-i386 -o

.S.o:
        ${CC} ${INCLUDES} ${FLAGS} $<
.c.o:
        ${CC} ${INCLUDES} ${FLAGS} $<

BOOT_S = load.S
BOOT_C = boot.c

BOOT_OBJ=${BOOT_S:.S=.o} ${BOOT_C:.c=.o}

all:    myos

myos:   ${BOOT_OBJ}
        ${LD} ${TARGET} ${BOOT_OBJ}

${BOOT_OBJ}:    ${BOOT_SRC}
clean::
        -${RM}  -f *~ *.lo *.o
```
（Makefileもほぼ[Linuxをはじめよう！：1から創る自作OS 0x30]のまま）

`$ make`を実行しelfファイルを作成する

## 3. 仮想マシンで動作確認をするためISOファイルを作成する
### 3.1 grub.cfgを作成する
```:grub.cfg
set timeout=0
set default=0

menuentry "MyOS" {
    multiboot /boot/myos.elf
    boot
}
```
> `multiboot`ではなく，`multiboot2 /boot/myos.elf`だと実行時に`error: no multiboot header found`エラーがでた．

### 3.2 ISOファイルを作成する
```
$ mkdir -p isofiles/boot/grub
$ cp grub.cfg isofiles/boot/grub/
$ cp myos.elf isofiles/boot/
$ grub2-mkrescue -o os.iso isofiles/
```

> `grub2-mkrescue`時に以下のエラーが発生した．
 `Your xorriso doesn't support '--grub2-mkrescue'. Some features are disabled. Please use xorriso 1.2.9 or later.`
そこで，以下よりxorrisoをrpmでインストールした．
https://centos.pkgs.org/7/repoforge-x86_64/xorriso-1.3.6-1.el7.rf.x86_64.rpm.html

> xorrisoの`rpm -Uvh`時に`/usr/bin/wish is needed`といったパッケージの依存関係エラーがでた．
以下を参考にyumでtk/tclをインストールすると解消できた．
（参考）http://www.realgeek.com/forums/usr-bin-wish-339946.html

## 4. VirtualBoxからGRUBを起動し動作を確認する
VirtualBoxから作成したISOファイル（os.iso）を読み込み開く．
（参考）http://softwaretechnique.jp/OS_Development/Grub/grub02.html

GRUB起動後，MyOSを選択し，Starting MyOS!が表示されるのを確認した．

[30日でできる! OS自作入門]:https://book.mynavi.jp/supportsite/detail/4839919844.html
[Linuxをはじめよう！：1から創る自作OS 0x20]:http://blog.livedoor.jp/hiroumauma/archives/1398977.html
[Linuxをはじめよう！：1から創る自作OS 0x30]:http://blog.livedoor.jp/hiroumauma/archives/1498465.html
[ももいろテクノロジー：GRUBで簡単なOSカーネルを動かしてみる]:http://inaz2.hatenablog.com/entry/2015/12/31/221319
[０から作るOS開発　Grubその４　GRUBから自作OSを起動する]:http://softwaretechnique.jp/OS_Development/Grub/grub04.html