MacでGRUBから自作OSを起動
久々にOS書いてます。そろそろ取っ掛からないと、来年もSECCAMPの機会を逃しかねないですね。
以前までははりぼてOSをLinuxに移植していたのですが、途中でちょっと詰んでしまったので、一からフルスクラッチすることにしました。
ただ、今はMacbookを使っているので、以前とまた環境が違っていろいろと環境整備に苦労しました。
その一環がクロスコンパイラのビルド。詳しくは以下。
【参考】Handwriting「Mountain Lionユーザのためのクロスコンパイラビルド」
さて、今回はブートローダを丁寧に書くのはやめて、その辺の処理をGRUBに投げる事にしました。
ところが、Linuxは多くのディストリビューションがGRUBで起動するために非常に簡単に導入ができるのですが、MacはEFIという独自のブートローダを用いていてGRUBがうまくインストールできないため、この辺でちょっと困りました。
いちばん手っ取り早いのはLinuxでGRUBのレスキューイメージを出力してMacに持ってくる事なんですが。
まあそのあたりを軽くまとめたいと思います。
こんな感じになります。
GRUBの選択画面
自作OS選択後
(割り込みのデバッグ中でなんか吐いてますが気にしないでください)
GRUBのレスキューイメージの用意
Linuxならgrubコマンドを使って生成する事ができます。
以下のページを参考にするといいと思います。
【参考】0から作るOS開発「GRUBその2 GRUBのインストール」
Macではソースコードからgrubをビルドしようとしても(少なくとも僕の環境では)うまくいきませんでした。
しょうがないので、レスキューイメージ自体をダウンロードしてきます。
http://sourceforge.jp/projects/sfnet_supergrub.berlios/releases/
上記のページにgrub-rescue-cdrom.isoというファイルがあるはずなので、それをダウンロードします。
VirtualBoxでGRUBレスキューイメージを起動
適当な仮想マシンを作成して、IDEコントローラにgrub-rescue-cdrom.isoをつっこんで起動します。
すると、GRUBの起動選択画面が出ます。
"c"を押すとGRUBのコンソールに移動します。
プログラムを書く
GRUBはMultiboot Specificationという規格に則って開発されています。
このMultiboot Specificationとは、ざっくり言うとGNUが策定した「OSのブートローダを共通化するための約束事」です。
自分のOSをMultiboot Specificationに従って書いてやれば、GRUBで起動できるという寸法なのです。
【参考】Multiboot Specification
そのために、まずはヘッダファイルを作ります。
multiboot.h
#ifndef __MULTIBOOT_H #define __MULTIBOOT_H #define KERNEL_LOAD_ADDRESS 0x00100000 #define MBH_FLAG_PAGE_ALIGN 0x00000001 #define MBH_FLAG_MEMORY_INFO 0x00000002 #define MBH_FLAG_VIDEO_MODE 0x00000004 #define MBH_FLAG_ENABLE_ADDR 0x00010000 #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 #define MULTIBOOT_HEADER_FLAGS (MBH_FLAG_PAGE_ALIGN | MBH_FLAG_MEMORY_INFO) #define MULTIBOOT_HEADER_CHECKSUM -(MULTIBOOT_HEADER_MAGIC+MULTIBOOT_HEADER_FLAGS) #define KERNEL_STACK_SIZE 0x00100000 #define MULTIBOOT_HEADER_MODE_TYPE 0x00000001 // text mode #define MULTIBOOT_HEADER_WIDTH 0x00000050 #define MULTIBOOT_HEADER_HEIGHT 0x00000028 #define MULTIBOOT_HEADER_DEPTH 0x00000000 #endif // __MULTIBOOT_H
boot.S
#include "multiboot.h" .text .code32 .extern _kernel_entry .global _start _start: jmp entry .align 4 # multiboot header .long MULTIBOOT_HEADER_MAGIC .long MULTIBOOT_HEADER_FLAGS .long MULTIBOOT_HEADER_CHECKSUM entry: # reset eflags pushl $0x0 popf push %ebx # push MULTIBOOT_HEADER_MAGIC push %eax # push multiboot info structure pointer call _kernel_entry loop: hlt jmp loop
(最初ハマってたんだけど、大文字の.Sにしないとgccのプリプロセッサが効かないんだね)
主な処理は非常に単純で、ようするにentryにジャンプしてkernel_entryって関数を呼ぶだけです。
その途中で%eflagsをリセットしたり、kernel_entry関数に引数を詰んでいたりします。
jmp命令の直後にある、データを直接配置しているのはMultiboot Specificationの仕様です。
MULTIBOOT_HEADER_MAGICはMultibootのマジックナンバーです。
最後にkernel_entryを書いてやればOK。
kernel.c
#include "multiboot.h" void _kernel_entry(unsigned long magic, unsigned long addr) { while(1) { __asm__ __volatile__ ("hlt"); } }
今回は手抜きでhltしてるだけなので、残念ながら画面的には何も変化がおきないです。
これら一連のプログラムのコーディング例は、Multiboot Specificationのページにあります。
【参考】Multiboot Specification: Examples
もっとマトモに書かれているので、参考にしてみてください。
コンパイルする
Makefileを作ります。
CC=i686-elf-gcc # 自分のクロスコンパイラのパスを指定してください AS=i686-elf-as # 同上 LD=i686-elf-ld # 同上 OPTION=-nostdlib -fno-builtin -Wall a.img: boot.o kernel.o $(LD) -o a.img -Ttext=0x00100000 -nostdlib --script=boot.ls boot.o kernel.o # General Rules %.o: %.S $(AS) $(OPTION) $*.S -o $*.o %.o: %.c $(CC) $(OPTION) $*.c -c -o $*.o
これでmakeすればa.imgというバイナリが出力されます。
ちなみにldに与えた-Ttext=0x00100000というオプションは、エントリポイントのアドレスを0x00100000に再配置するという意味です。
フロッピーイメージを作成する
このままではまだ実行する事ができません。
最終的にどうすればいいかというと、GRUBで読み込むためにはフロッピーイメージにする必要があります。
そのためにはmformatというコマンドを用いてMS-DOS用にフロッピーイメージファイルをフォーマットする必要があります。
このmformatというコマンドは、Macではmtoolsというパッケージでまとめられていますので、これをportなりなんなりでインストールします。
sudo port -d install mtools
mtoolsはmformatの他にも、MS-DOSのファイル操作を実現するコマンド群が揃っています。
では、フロッピーイメージを作成します。
mformat -f 1440 -C -i fd.img :: tar cvf fd.img a.img
mformatでfd.imgというフロッピーイメージファイルをまず作成します。
そして、fd.imgのルートディレクトリにa.imgを書き込みます。
他に何かいい方法があるのだと思いますが、今のところtarで書き込む方法しか見つからなかった(というか成功しなかった)ので、これで運用しています。
これで、fd.imgのルートにa.imgが配置されました。
fd.img
/
+a.img
準備完了です。
仮想マシンのフロッピーコントローラにfd.imgをマウントします。
GRUBからa.imgを起動する
まずはVirtualBoxで再び仮想マシンを立ち上げて、GRUBの選択画面でcを押してコンソールに移動します。
そして、以下のコマンドを入力します。
set root=(fd0) multiboot /a.img boot
うまくいけば、これで起動されるはずです。
kernel_entryではhltしているので、フリーズするはずですね。
文字列表示なりなんなりをすると、わかりやすくなると思います。
set root=(fd0)がうまくいかないとき
仮想マシンにfd.imgはマウントされていますか?
GRUBコンソールでlsコマンドを叩いたときに一覧の中に(fd0)というのが見えなければ、恐らくマウントされていないです。
multiboot /a.imgがうまくいかないとき
ちゃんとfd.imgのルートディレクトリにa.imgがおかれていますか?
ls /
をたたくと、fd.imgのルートディレクトリに配置されているファイル・ディレクトリが見れます。
その中にa.imgがなければ、うまくいっていないです。
tarコマンドを叩くところをもう一度確認してください。