0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

Tips マルチブート仕様

マルチブート仕様概要

マルチブート仕様 フリーソフトウェア財団 で、1995年に設計され、 GNUプロジェクト

発行しています。この仕様は、複数のOSを起動させるときのカーネルをロードするブートローダーと

カーネルとブートローダーのインターフェースを規定したものです。

このような規定が無いと、過去のOS(現在の商用OSもそうですが)をロードするブートローダーは、

各OSによってまちまちのローダーがあり、複数のOSをマルチブートするにはかなり面倒でした。

このような問題があるため、ブートローダーとOSのインターフェースの仕様を決めて、複数のOSを

(少なくともフリーなOSを)起動できるように規定したのが「マルチブート仕様」となります。

このマルチブート仕様に基づいて設計されている主なカーネルとして、 GNU Hurd

VMware ESXi L4マイクロカーネル があります。

そして、ブートローダーとしてGRUBがあります。

マルチブート仕様について

まずは、マルチブート仕様が想定しているベースについてと、ブートローダーやOSの設計方針に

ついて見ていきます

マルチブート仕様が想定しているアーキテクチャー

マルチブート仕様は主にPC(IBMのPC/AT互換機)で使用するブートローダーとカーネルを

想定しています。(但し、互換性のために、PCで現在でも使用しているブート方法が無い

システムでも仕様を柔軟に変えて適用することはできます)。

マルチブート仕様が想定しているOS

フリーでこの仕様を簡単に適用できる32ビットOSを想定しています。特に、Linux、FreeBSD、

NetBSD、Mach、VSTa向けに設計されており、これから新しく開発されるカーネルについても、

この仕様を適用することで、Grubなどの仕様に準拠しているブートローダーからブートできるように

なります。

マルチブート仕様に基いて設計するブートローダーについて

ブートローダーは、フロッピーディスク、ハードディスクやネットワークなど様々なメディアからOSを

ブートできるように設計しておくと、移植性が高くユーザーフレンドリーなブートローダーとなります。

また、ディスクからOSを起動する場合には、OSが格納されている色々なファイルシステムに

対応して、OSを起動できるように設計します。

ブート時にOSに渡すパラメーター情報について

ブート時にOSの挙動を変更するために、OSにパラメーター情報を渡したいときがあります。

マルチブート仕様では、特にパラメーター情報を渡すインターフェースについて規定していませんが

ブートローダーはOSにパラメーターを渡すように設計しておくほうが良いです。

OSの開発について

OSは32ビットの実行形式を想定しています。OS開発では、GCCなど汎用的なコンパイラで

作ることができますので、特別なツールは特に必要ありません。PCでOSを起動する場合は

ブートローダーで32ビットのプロテクティッドモードに移行しますので、OSは特にプロテクティッド

モードへの移行処理を行う必要はありません。(ブートローダーは1MBのメモリー境界を越えて

OSをロードします)。



プログラムの実行フォーマットとしては、たくさんのフォーマットがありますが、ブートローダーは

全部の実行フォーマットに対応する必要はありません。(従来では、色々なa.outフォーマット

が使用されてきましたが、現在ではELFフォーマットが主に採用されています)。

全部の実行フォーマットに対応しなくてもよいように、OSにマルチブートヘッダーを付けることで

異なるa.outフォーマットでもロードできるように設計しています。マルチブートヘッダーは

OSのイメージファイルの一番先頭に付ける必要はなく、先頭から8Kバイトまでの、

ところに付ければOKです。

ブートモジュールについて

現在のOSは、ファイルシステムドライバーなどをOSイメージに持っていません。モジュールに

分割して、モジュールは必要に応じて(ファイルシステムのマウントなど)ロードするように

設計されています。必須となるモジュールはカーネル自体に結合しておきますが、

それ以外のモジュールについてはブートローダーがロードできるようにしておきます。

ブートローダーはカーネルが使用するローダブルモジュールの情報を、カーネルに渡す

ように設計しておくことを強くおすすめします。(例えばLinuxでは起動時にinitrdが

必要です)。

マルチブート仕様で使用する型式

まずは、マルチブート仕様で使用する変数の型式を定義します。

マルチブート仕様で使用する変数の型式
型式 説明
u8 符号なし(unsigned)の8ビットデータです
u16 符号なし(unsigned)の16ビットデータです
u32 符号なし(unsigned)の32ビットデータです
u64 符号なし(unsigned)の64ビットデータです


マルチブート仕様で仕様する助詞の定義について


マルチブート仕様

いよいよマルチブート仕様について本格的に見ていきます。

マルチブート仕様では、OSとブートローダーのインターフェースとして下記3点を規定しています。

  1. マルチブートヘッダー
  2. カーネルイメージに付けるマルチブートについて規定します

  3. OS起動時のマシン状態

  4. ブートローダーがOSに渡す情報(パラメーター)

では、まずマルチブートヘッダーについて見ていきます

マルチブートヘッダー

OSイメージにはマルチブートヘッダーを必ず付ける"必要があります"。マルチブートヘッダーは

OSイメージの8192バイト(8KB)以内に付ける必要があり、ヘッダーは32ビット境界に配置”しなければ

なりません”。

OSイメージ

マルチブートヘッダーは次のような構造となっています。

(下記図表のオフセットはマルチブートヘッダーの先頭から何バイト目に配置するかを意味しています)

マルチブートヘッダーの構造

マルチブートヘッダー
マジックフィールド
オフセット 型式 フィールド名 説明
0 u32 magic マルチブートヘッダーを特定するためのマジックナンバーです。 マジックナンバーとして”必ず0x1BADB002を設定します”。
4 u32 flags このフィールドにはOSが必要な機能とブートローダーが必要な情報を ヘッダーに格納しているかどうかを示すフラグです。

ブートローダーは、予約ビットに値がセットされている場合や、有効なビットが セットされていてもOSが起動できない(フラグの条件を満たせない)場合には ユーザーに通知して、OSの起動を中止する必要があります。 ビット16からビット31はオプション機能ビットとなります。 ブートローダーはビット16-31がセットされているが、 ビット16-31の機能について対応していない場合には、 このビットを無視してOSを起動させても問題ありません。

ビット0:1に設定している場合には、ブートローダーは、 OSと一緒にロードするモジュールをページ境界(4KB境界)に配置する 必要があります。(OSのなかには起動時に用意しているページに、 モジュールがロードされることを前提としているものがあるためです。)

ビット1:1に設定している場合には、ブートローダーは OSに引数として渡す ブート情報構造体 のmem_*フィールドに、システムで利用可能なメモリー情報を セットする必要があります。また、メモリーマップ情報がある場合にも、 構造体のmmap_*フィールドにメモリーマップ情報をセットする 必要があります。

ビット2:1に設定している場合、OSは ブート情報構造体 のビデオモードテーブルを使用しますので、ブートローダーは テーブル値をセットする必要があります。

ビット16:1に設定している場合には、 マルチブートヘッダーのオフセット12から28が有効となります。 セットされている場合には、ブートローダーは実行フォーマットの ヘッダー(ELFヘッダーなど)ではなく、マルチブートヘッダーの 情報からOSをロードするアドレスを計算”するべき”です。 ”するべき”ですが、OSイメージがa.outフォーマットの場合には、 ”する必要がある”になります。 この仕様に準拠するブートローダーは、ELFフォーマットの OSイメージと、マルチブートヘッダーにロードするアドレスが 設定されているOSイメージ両方をロードできる必要があります。 また、必須ではありませんが、ELFフォーマット以外の実行形式に対応するのもありです。
8 u32 checksum チェックサムを設定します。 magicフィールドの値とflagsフィールドの値の合計と checksumフィールドの値を足すと0になるような32ビットのチェックサムの値を設定する 必要があります。

    checksum = - ( magic + flags )

アドレスフィールド
flagsのビット16が1の場合に、アドレスフィールドの全部のフィールドが 有効となります。アドレスは物理アドレスです。
12 u32 header_addr マルチブートヘッダーの先頭が配置される物理アドレスを設定します。 (magicフィールドの値が配置されるメモリーの物理アドレスです)。 OSの実行バイナリが使用するオフセットアドレスにあわせるために 使用します。
16 u32 load_addr OSイメージのテキストセグメント(.text)の開始物理アドレスを 設定します。OSの実行バイナリだけをロードするために、 OSイメージファイルの先頭から何バイト目に実行バイナリが あるのかを計算するために使用します。 (header_addr - load_addrの値がマルチブートヘッダーから 数えた実行バイナリのオフセット値となります)。 このため、load_addrはheader_addr以下の値に設定する必要があります。
20 u32 load_end_addr OSイメージの初期値付きデータセグメント(.data)の最終物理アドレスを 設定します。(load_end_addr - load_addr)は ロードする初期値有りデータのバイト数となります。 初期値有りデータセグメントとテキストセグメントは連続しているものとして ブートローダーは処理します。このように想定するのは、a.out実行形式にそのような 形式があるからです。このフィールドが0の場合、ブートローダーは OSイメージがテキストセグメントとデータセグメントだけであると判断します。
24 u32 bss_end_addr 初期値無し変数のデータセグメント(.bss)の最終物理アドレス を設定します。ブートローダーはこのエリアをゼロで初期化します。 (ANSI C規格に準拠するためなど)。 また、ブートローダーはこのエリアにはモジュールをロードしたり、 OSに渡す情報を格納しないようにします。 このフィールドが0に設定されている場合には、ブートローダーは 初期値無し変数のデータセグメント(.bss)が無いものとして (初期化しません)処理します。
28 u32 entry_addr OSをロードした後にジャンプする物理アドレスを設定します。 (カーネルのメイン関数のアドレスを設定します)。
グラフィックフィールド
flagsのビット2が1の場合に、グラフィックフィールドの全部のフィールドが 有効となります。OS起動時にカーネルが要求するグラフィックモードを設定します。 グラフィックモードは”推奨”モードなだけで、要求されたグラフィックモードが あれば、ブートローダーはそのモードに設定”するべき”です。 特に指定が無い場合や要求モードが無い場合は、ブートローダーは 要求モードに似たモードに設定”するべき”です。
32 u32 entry_addr 0:ブートローダーはグラフィックモードに設定します
1:ブートローダーはEGA標準のテキストモードに設定します
その他の値:将来仕様のため予約されています
(値が0の場合でもブートローダーはOS起動時にテキストモードに 設定してもよいです。)
36 u32 width グラフィックの列数を設定します。
グラフィックモード:ピクセル単位で設定します。
テキストモード:文字数単位で設定します。
(値を0に設定した場合、OSはどのように設定されるかは気にしません)
40 u32 height グラフィックの行数を設定します。
グラフィックモード:ピクセル単位で設定します。
テキストモード:文字数単位で設定します。
(値を0に設定した場合、OSはどのように設定されるかは気にしません)
44 u32 depth 0以外:グラフィックモードの1ピクセル毎のビット数を設定します
0:ビット数をどのように設定されてもOSには問題ありません


マシン状態

ブートローダーが32ビットOSを起動したときのマシン状態は次のように設定する必要があります。

マシン状態
レジスター 設定値 説明
EAXレジスター 0x2BADB002 ブートローダーはEAXレジスターに0x2BADB002を設定する必要があります。 OSでこの値を確認することでマルチブート仕様に準拠したブートローダーから ブートされたことが確認できます。(例えば、マルチブート仕様に準拠していない ブートローダーからブートされたことを判断する場合にも使用できます)。
EBXレジスター ブート情報構造体の格納アドレス ブートローダーはブート情報構造体の格納アドレスが設定されている必要があります。
CSレジスター コードセグメントセレクター値 ブートローダーが構築したGDTのコードセグメントディスクリプターを指す セレクター値が格納されています。 リミットが0xFFFFFFFでオフセットが0の読み込み/実行可能な 32ビットコードセグメント値が設定されている必要があります。 実際に設定される値は未定義となります。
DS、ES、FS、GS、SSレジスター データセグメントセレクター値 ブートローダーが構築したGDTのデータセグメントディスクリプターを指す セレクター値が格納されています。 リミットが0xFFFFFFFでオフセットが0の読み込み/書き込み可能な 32ビットデータセグメント値が設定されている必要があります。 実際に設定される値は未定義となります。
A20 A20ゲート有効 OS起動時にはA20が有効となっている必要があります。
CR0制御レジスター ビット31(PGビット) = 0 OS起動時にはCR0制御レジスターのビット31(PGビット)が0にクリアー されている必要があります。CR0の他のビットは未定義となります。
EFLAGS ビット9(IFビット) = 0
ビット17(VMビット) = 0
OS起動時にはEFLAGSレジスターのビット17(VMビット)と ビット9(IFビット)が0にクリアー されている必要があります。EFLAGSの他のビットは未定義となります。
その他のレジスター 未定義 その他のCPUのレジスターとフラグは未定義となります。


(参考)CR0制御レジスターとEFLAGSレジスター

CR0制御レジスター

32ビットの制御レジスタCR0。CPUの動作モードと状態を制御するレジスタでビット0のPEビットがプロテクティッドモードをONするビット

CR0レジスタ
ビット ビット名称 説明
0 PE Protection Enableビット。このビットをONにするとプロテクティッドモードへ移行します
1 MP Monitor Co-Processorビット。モニタ・コプロセッサビット
WAIT命令を実行したときの動作を変更することができます
0:TSビットが0でも1でも関係なく無視する
1:TSビットが1であればコプロセッサ使用不可能例外を発生させる
2 EM Emulationビット。エミュレーションビット
浮動小数点演算(x87 FPU)命令を実行したときの動作を変更することができます
0:x87 FPUを持っているので命令実行可能
1:命令実行時x87 FPUを持っていないのでコプロセッサ使用不可例外が発生する
  ソフトウェアによってエミュレーションを行う
※細かい条件はIntelのCPU仕様書をご確認ください
3 TS Task Switchビット。タスクスイッチビット
CPUはタスクスイッチする時にこのビットを1にします
※EM、MPビットの設定によっては影響があります
4 ET Extended Typeビット。拡張タイプビット
0:80287以前のCPU
1:80387以後のCPU
5 NE 数値演算エラービット
0:x87 FPUエラーレポート無効
1:x87 FPUエラーレポート有効
16 WP Write Protectビット。書き込み保護ビット
0:リング0のプログラムが読み取り専用のユーザ空間に書き込むことができる
1:リング0のプログラムが読み取り専用のユーザ空間に書き込むことを禁止
18 AM Alignment Maskビット。アライメントマスクビット
0:自動アライメントチェック無効
1:自動アライメントチェック有効
29 NW Not Write throughビット。ノットライトスルービット
0:CDビットが0の場合キャッシュをライトバックまたはライトスルーが有効
1:CDビットとNWビットの組み合わせはCPUのマニュアル10-17ページを参照ください
30 CD Cache Disableビット。キャッシュ無効ビット
0:NWビットが0の場合キャッシュ操作が有効
1:CDビットとNWビットの組み合わせはCPUのマニュアル10-17ページを参照ください
31 PG PaGingビット。ページングビット
0:ページング無効
1:ページング有効


EFLAGSレジスター

EFLAGSレジスタのビットアサイン

EFLAGSレジスタ
ビット ステータス
フラグ
説明
0 CF Carry Flag キャリーフラグ
1:足し算なので最上位ビットが繰り上げた場合。(例:0xFFFF+1や0x0000-1をした場合)
0:繰り上げが発生しなかった場合。(例:x0FFFE+1をした場合)
2 PF Parity Flag パリティフラグ
1:演算した結果の最下位バイトに値1のビットが偶数個ある場合(例:0xFF03など)
0:逆に奇数個の場合(例:0x0F10など)
4 AF Adjust Flag 調整フラグ
1:演算した結果のビット3(最下位バイトの最上位ビット)に繰り上げが発生した場合(例:0x0080+0x08など)
0:逆に繰り上げが発生しなかった場合
6 ZF Zero Flag ゼロフラグ
1:演算した結果が0の場合
0:逆に0位外の場合
7 SF Sign Flag 符号フラグ
1:演算した結果が負の場合
0:逆に正の場合
8 TF Trap Flag トラップフラグ
1:デバッグのシングルステップモードが有効
0:逆にシングルステップモードが無効
9 IF Interrupt Enable Flag 割り込み可能フラグ
1:割り込みが有効。STI命令を実行するとこのフラグが1になります
0:割り込みが無効。CLI命令を実行するとこのフラグが0になります
割り込みを有効にするか無効にするかするフラグです。
10 DF Direction Flag 方向フラグ
1:文字列関係の命令(LODSBなど)で実行した後例えばSIレジスタの値がインクリメントされる
0:逆にデクリメントされる
11 OF Overflow Flag オバーフローフラグ
1:演算結果が格納するレジスタより大きい数字になった場合(例:0xFF+0x01をALレジスタに格納)
0:逆に格納するレジスタより小さい場合
12-13 IOPL I/O privilege level field I/O特権レベルフィールド
このフィールドには特権レベルの値を入れます(通常0から3)

0以外:ユーザモードとして動作します
0   :特権モードとして動作します
このフィールドの値は特にCPL(Current Privilege Level)と言います。後々出てきます。
14 NT Nested Task Flag ネストタスクフラグ
1:現在のタスクが直前に実行されたタスクにリンクされている場合
0:逆にリンクされていない場合
16 RF Resume Flag 再開フラグ
デバッグ例外に対するプロセッサの応答を制御する
17 VM Virtual-8086 mode Flag 仮想8086モードフラグ
1:仮想8086モードが有効
0:仮想8086モードが無効(プロテクティッドモード)
18 AC Alignment check Flag アライメントチェックフラグ
1:アライメントチェックが有効(CR0レジスタのAMビットもセットしておく必要があります)
0:アライメントチェックが無効
19 VIF Virtual interrupt Flag 仮想割り込みフラグ
1:仮想割り込みが有効(CR4レジスタのVMEビットもセットしておく必要があります)
0:仮想割り込みが無効
20 VIP Virtual interrupt pending Flag 仮想割り込み保留フラグ
1:仮想割り込みが保留状態(CR4レジスタのVMEビットもセットしておく必要があります)
0:仮想割り込みに保留無し
21 ID Identification Flag 識別フラグ
プログラムから値0か1を書き込むことができると、CPUID命令が使えることを意味します


ESPレジスター、GDTR、IDTRについて

ESPレジスター、GDTR、IDTRについては下記のようになります

ESPレジスター、GDTR、IDTR
レジスター 説明
ESPレジスター OS起動後、カーネルは直ぐにスタックポインターを設定する必要があります
GDTR セグメントレジスターはブートローダーが設定しますが、 GDTRが有効でない”可能性があります”ので、 OSはそのカーネル専用のGDTを構築するまでは、 セグメントレジスターの値を変更してはいけません。
IDTR OSはそのカーネル専用のIDTを構築するまでは、 割り込みを禁止しておく必要があります。


ブートローダーが設定しておくべきのその他のマシン状態

ブートローダーは、その他のマシン状態(メモリーなど)はBIOSが初期化したままの状態に

しておくべきです。言い換えると、OSがロードされた後で、カーネルがBIOSサービス割り込みを

呼び出せるようにしておくべきです。更に言えば、カーネルはBIOSサービス割り込み呼び出しで、

BIOSが用意したデータ構造を上書きしないようにします。また、ブートローダーは PIC

(Programmable Interrupt Controller)
の割り込み設定をBIOSが初期化した状態のままに

しておく必要があります。

マルチブート情報のフォーマット

ブート情報はブートローダーがカーネルに渡す重要な情報(メモリーマップなど)となります

OS起動時には、ブート情報が格納されているアドレスはEBXレジスターに格納されています。

カーネルでブート情報を使用しても、全く使用しなくてもどちらでもかまいません。



ブート情報とその関連情報は、カーネルやモジュールがロードされる場所以外のメモリーに

配置されます。OSはブート情報と関連情報を上書きして壊さないようにします。



マルチブート情報のフォーマットは次のようになります。

マルチブート情報の構造

マルチブート情報
ラベル名 オフセット
(バイト)
サイズ
(バイト)
説明
flags 0 4 ブート情報の各フィールドが有効/無効(有り/無し)となっているか示すフラグです
各ビットが1になっていれば有効(有り)で、0であれば無効(無し)となります
ビット0:1であればmem_*フィールドが有効になります
ビット1:1であればboot_deviceフィールドが有効になります
ビット2:1であればcmdlineフィールドが有効になります
ビット3:1であればmods_*フィールドが有効になります
ビット4:1であればsymフィールドがa.outカーネルイメージ用の
            フォーマットになります
ビット5:1であればsymフィールドがELFカーネルイメージ用の
            フォーマットになります
ビット6:1であればmmap_*フィールドが有効になります
ビット7:1であればdrives_*フィールドが有効になります
ビット8:1であればconfig_tableフィールドが有効になります
ビット9:1であればboot_loader_nameフィールドが有効になります
ビット10:1であればapm_tableフィールドが有効になります
ビット11:1であればvbe_*フィールドが有効になります
mem_lower 4 4 ローメモリーのメモリーサイズが格納されています。 メモリーサイズはキロバイト単位で格納されます。 ローメモリーの最小アドレスは0から始まります。 最大アドレスは640KBとなります。
mem_upper 8 4 拡張メモリー(1MB以降のメモリー領域)のメモリーサイズが格納されています。 メモリーサイズはキロバイト単位で格納されます。 拡張メモリの最小アドレスは1MBから始まります。 最大アドレスは、拡張メモリー以降にある最初の予約メモリー領域の アドレスから1MBを引いたアドレスとなります。(このアドレス値 の保証はありません。)
boot_device 12 4 ブートローダーが、OSをロードしたBIOSのディスクデバイス情報が格納されます。 OSがBIOSディスク以外からロードされた場合には、このフィールドは 無効(存在しない)となっている必要があります。 このフィールドを見て、OSはルートデバイスを判断できます(必要時)。 boot_deviceフィールドは次のように4バイトに分かれています。
boot_deviceフィールド

drive:BIOSディスクサービス(INT 0x13)で使用される、BIOSドライブ番号が 格納されます。(0x00はフロッピーディスク、0x80ならHDDなど)。
part1:トップのパーディション番号が格納されます。
part2:サブパーティション番号が格納されます。
part3:サブパーティション番号が格納されます。
パーティション番号のは0から始まる数字となります。 使用されていないパーティションは0xFFに設定されている必要があります。 例えば、1つのDOSパーティションを使用している場合には、 part1はDOSパーティション番号が格納され、 part2とpart3は0xFFが格納されます。 また、最初のパーティションがDOSで、次のパーティションがBSDパーティション (DOSパーティションのサブパーティション)の 場合にはpart1にはDOSパーティション番号が格納され、 part2にはBSDのサブパーティション番号が格納され、 part3には0xFFが格納されます。
cmdline 16 4 カーネルに渡すコマンドラインの物理アドレスが格納されます。 コマンドラインはC言語のNullで終わる文字列となります。
mods_count 20 4 カーネルと一緒にロードされるモジュールの情報がモジュール構造体として メモリーに格納されます。そのモジュール構造体の数と格納されている 物理アドレスが格納されます。
mods_count:ロードされたモジュール数が格納されます。 モジュールが1つもロードされていない場合は0となります。
mods_addr:最初のモジュール構造体がロードされた物理アドレスが格納 されます。

各モジュール構造体は次のような構造となっています。
モジュール構造体

mod_start:ロードされたモジュールの開始アドレスが格納されます。
mode_end:ロードされたモジュールの最終アドレスが格納されます。
string:ロードされたモジュールに関する任意の文字列が格納されます。 文字列はASCIIで0x00が文字列の終端となります。通常文字列として、 コマンドラインや(OSがモジュールを実行ファイルとして 実行する場合など)、パス(モジュールをファイルとして扱う場合など)が 格納されますが、実際はどのように利用するかは各OSによって異なります。 reserved:0固定にする必要があります。OSはこのフィールドを 無視する必要があります。
mods_addr 24 4
syms 28-40 12 flagsのビット4とビット5はどちらか一方だけ1が設定されます(排他関係)。
ビット4が1の場合(ビット5は0)
symsフィールドは次のような構造となります。
flagsのビット4が1の場合のsymsフィールド

a.outフォーマットのカーネルイメージのヘッダーにあるシンボルテーブルの情報が 格納されます。
addr:a.outフォーマットにはnlist構造体の配列(テーブル)の 直ぐ次にそのテーブル自体のサイズが配置されています。 そのテーブルのサイズが格納されている物理アドレスが 格納されます。格納されているサイズ自体はunsigned longの 整数値でASCII文字で0終端の文字列で格納されています。
tabsize:a.outフォーマットの シンボルセクションの最初に配置されている サイズパラメーターが格納されています。シンボルが無い場合は 0が格納されます。
strsize:a.outフォーマットの ストリングクションの最初に配置されている サイズパラメーターが格納されています。
flagsのビット4とビット5はどちらか一方だけ1が設定されます(排他関係)。
ビット5が1の場合(ビット4は0)
symsフィールドは次のような構造となります。
flagsのビット5が1の場合のsymsフィールド

ELFフォーマットのカーネルイメージにあるshdr_*の情報が格納されます。 (ELFフォーマットのプログラムヘッダーにあるshdr_num、shdr_size、shdr_addr、shdr_shndxが それぞれ、num、size、addr、shndxにあたります)。 カーネルイメージのセクションが全てロードされて、各セクションが物理アドレスの どこにロードされているのかをELFセクションヘッダーの物理アドレスフィールドで 取得することができます。シンボルが無い場合には、shdr_numが0になります。
mmap_length 44 4 メモリマップ情報のデータ長が格納されています
mmap_addr 48 4 メモリーマップ情報が格納されているアドレスが格納されています。 メモリーマップ情報の各エントリーは次のようなフォーマットで格納されています。
メモリーマップ情報

メモリーマップ情報の1つのエントリーはbase_addr、length、typeの3つの フィールドとなります。このエントリーがメモリー領域数分の配列となって格納されています。 sizeフィールドは先頭のみとなります。
size:メモリーマップ情報のサイズが格納されます。 20(バイト)より大きい値が入ります。
base_addr:そのメモリー領域の開始アドレスとなります。
length:そのメモリー領域のサイズが格納されます
type:そのメモリー領域の種類が格納されます。値が1ならばRAMでその他の値であれば 予約領域となります。
メモリーマップ情報にあるRAMメモリー領域の情報は全てOSで利用可能な領域となります。
drive_length 52 4 ドライブ構造体の格納情報が格納されます。
drive_addr:ドライブ構造体が格納されている物理アドレスが 格納されます。
drive_length:ドライブ構造体のサイズが格納されます。 情報が無い場合は0が格納されます。
ドライブ構造体は次のようなフォーマットとなります。
ドライブ構造体

size:この構造体のサイズが格納されています。サイズはdrive_portsの数 で変わります。メモリー上のアライメントのため、サイズは必ずしも(10 + 2 * ポート数)と はなりません。
drive_number:BIOSドライブ番号が格納されます。
drive_mode:ブートローダーがドライブにアクセスするモードが格納されます。 値が0の場合、CHSモード(シリンダー/ヘッド/セクターを指定してアクセス)。値が1の 場合、LBA(Logical Block Addressing)モードとなります。
drive_cylinders、drive_head、drive_sectors: BIOSが検出したそのドライブが持っているシリンダー数、ヘッド数、セクター数が格納されます。
drive_ports:BIOSで使用されるI/Oポート番号が配列で格納されます。値は unsignedの2バイト整数か0となります。値が0であれば配列の終端となります。 (配列にはドライブと関係ないI/Oポート番号が格納されている可能性があります)。
drive_addr 56 4
config_table 60 4 BIOSのシステム設定情報取得 サービスコールで取得したテーブルが格納されている物理アドレスが格納されます。
boot_loader_name 64 4 OSを起動したブートローダーの名前が格納された物理アドレスが格納されます。 メモリーに格納される名前の文字列はC言語のNullで終わる文字列となります。
apm_table 68 4 APMテーブルが格納されている物理アドレスが格納されます。
APMテーブルのフォーマットは次のようになります。
APMテーブル

version:バージョン番号
cseg:プロテクティッドモード32ビットコードセグメント
offset:エントリーポイントのオフセット
cseg_16:プロテクティッドモード16ビットコードセグメント
dseg:プロテクティッドモード16ビットデータセグメント
flags:フラグ
cseg_len:プロテクティッドモード32ビットコードセグメントのサイズ
cseg_16_len:プロテクティッドモード16ビットコードセグメントのサイズ
dseg_len:データセグメントのサイズ

詳細はAdvanced Power Management (APM) BIOS Interface Specification (APM BIOSインターフェース仕様)を参照してください。
vbe_control_info 72 4 OS起動時にグラフィックモードが必要な場合で、マルチブートヘッダーに グラフィックモードの指定があるときのみ値が格納されます。
vbe_control_info:VBE(VESA BIOS Extension:VESA BIOS拡張)で規定されている VBE機能0x00の返り値として得られるVBE制御情報が格納されている物理アドレスが格納されます。
vbe_mode_info:VBE機能0x01の返り値として得られるVBEモード情報が格納されている物理アドレスが格納されます。
vbe_interface_seg:VBE 2.0+で規定されているプロテクティッドモードインターフェースのテーブル値が格納されます。
vbe_interface_off:VBE 2.0+で規定されているプロテクティッドモードインターフェースのテーブル値が格納されます。
vbe_interface_len:VBE 2.0+で規定されているプロテクティッドモードインターフェースのテーブル値が格納されます。
テーブル値の情報が利用できない場合には、vbe_interface_*の値は0となります。 VBE 3.0では旧バージョンのVBEと互換性の無いプロテクティッドモードインターフェースが定義されいますので、 注意してください。VBE 3.0以降のテーブルを使用する場合はカーネル自身でテーブル値を取得する 必要があります。VBE用に、グラフィックテーブル用のフィールドを定義していますが、マルチブートローダーは 非VBEモードでもVBEモードをシミュレートしてフィールドに値を設定することもできます。
vbe_mode_info 76 4
vbe_mode 80 2
vbe_interface_seg 82 2
vbe_interface_off 84 2
vbe_interface_len 86 2


実装例と注意事項

以降はマルチブート仕様としての規定ではありません。

パソコンでの注意事項

マルチブート情報のflagsのビット0について

ブートローダーがメモリー情報を取得するときに古いBIOSのサービス割り込みを使った場合、

または新しいBIOSのサービス割り込みの結果としてメモリーが利用不可の場合

(新しいサービス割り込みの場合はflagsのビット6となります)、メモリー情報として

最大で15または63Mバイトのメモリーサイズが格納されます。

BIOSに頼らずブートローダーでメモリーサイズを検出することを強くおすすめします。

マルチブート情報のflagsのビット1について

ビット1はBIOSが検出したドライブのどのドライブに、OSが格納されているのかを

決める手がかりとなります。多くのOSでは、とりあえずブートデバイスの検出処理を

作っているだけで、検出処理が正常に行われること無く、様々な条件下でこの情報が

壊れてしまっています。この問題の標準的な解決方法を実装することを推奨しています。

解決方法には2つの方法があります。 BIOSデバイスマッピングテクニックを参照してください。

マルチブート情報のflagsのビット6について

メモリーマップ情報の取得にはBIOSサービス割り込みの システムメモリーマップ取得

(INT 0x15、AX = 0xE820)ファンクションを使用します。 システムメモリマップ取得

ファンクションでメモリーマップを取得する方法は

物理メモリ管理その1 物理メモリとマルチブート仕様 を参照してください。

このファンクションの目的としては、BIOSが取得した拡張メモリー情報を

変更することなくOSに渡し、OSが拡張情報を使用できるようにすることです。

BIOSデバイスマッピングテクニック

次の2のテクニックはOSで使用することができますし、デバイスドライバーでも必要となる

場合があります。

データ比較テクニック

OSがデバイスドライバーを有効にする前に、各デバイスの同じセクター番号の

データを集めておきます。(各デバイスのデータは、それぞれ異なる値で、デバイスを

特定できる必要があります)。デバイスドライバーを有効にした後に、デバイスドライバーで

デバイスから取得したデータを比較して、デバイスマッピング処理を行います。



この方法の問題点:

  1. BIOSが検出したデバイスから取得するデータが、実際にデバイス毎に異なって
    いるかどうかが分かりません。(BIOSデバイスからの部分的な読み込み処理は
    中断処理を実装しておくべきです)。

  2. BIOSがアクセスできないデバイスが存在する可能性があります。
    アクセスできないデバイスはBIOSが使用できるデバイスと同じデバイスと
    判断してしまう場合があります。(ですので、中断処理を実装しておくべきです)。

I/O制限テクニック

次に示す最初の手順は必ずしも必要で無い場合があります。しかし、RAMに書き込みを

行うデバイスドライバーについては、コピーオンライトできるデータを作っておきます。

元のデータは後で、初期化した状態のBIOSバーチャルマシンを作るのに保存しておきます。



オンラインで取得したデバイスドライバーを使用する場合には、アクセスできない

BIOSデバイスを次のように判断します。

  1. 初期状態のBIOSバーチャルマシンを作成します

  2. 初期状態ではデバイスドライバーに読み込み/書き込み許可がされていない
    I/Oエリアについて、デバイスドライバーが要求するI/Oエリアの許可情報マップを
    設定します。

  3. 各デバイスにアクセスします。

  4. アクセスの結果、どのデバイスで成功したかを記録しておき、ドライバーに
    アクセス許可を与えていないI/Oエリア(制限I/Oエリア)であったことも
    記録しておきます。(これはXOR演算で計算できます)。

各デバイスのドライバーによって、BIOSがデバイスとして認識されているデバイス数が得られます。

(認識されているデバイスのリストに空きが無いようにするべきです)。この手順により、

ドライバーが制御できるデバイス数がわかります。

通常システム上に2つのディスクドライブがあり、それぞれBIOSが割り振る番号が割り当てられて

います。ですが、ドライバーが論理的に認識できる最小デバイス数とは違います。

OSのプログラム例

マルチブート仕様に記載されているマルチブートカーネルの例です。

このプログラムでは、カーネルが受け取るマルチブート情報のflags、mem_lower、

mem_upperなどの内容を表示する例となります。カーネルのソースはboot.S、kernel.c、

multiboot.hの3つとなります。boot.SはGASのアセンブラとなります。

(C言語のヘッダーをインクルードしますので、拡張子は大文字のSのほうです)

また、このソースはgrubのソースのdocsフォルダにもありますので、

ダウンロードしてお試しいただけます。

[multiboot.h]

multiboot.hはマルチブートヘッダーとマルチブート情報を定義しています。


/*  multiboot.h - Multiboot header file.  */
/*  Copyright (C) 1999,2003,2007,2008,2009  Free Software Foundation, Inc.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL ANY
 *  DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 *  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1

/* How many bytes from the start of the file we search for the header.  */
#define MULTIBOOT_SEARCH				8192

/* The magic field should contain this.  */
#define MULTIBOOT_HEADER_MAGIC			0x1BADB002

/* This should be in %eax.  */
#define MULTIBOOT_BOOTLOADER_MAGIC		0x2BADB002

/* The bits in the required part of flags field we don't support.  */
#define MULTIBOOT_UNSUPPORTED			0x0000fffc

/* Alignment of multiboot modules.  */
#define MULTIBOOT_MOD_ALIGN			0x00001000

/* Alignment of the multiboot info structure.  */
#define MULTIBOOT_INFO_ALIGN			0x00000004

/* Flags set in the 'flags' member of the multiboot header.  */

/* Align all boot modules on i386 page (4KB) boundaries.  */
#define MULTIBOOT_PAGE_ALIGN			0x00000001

/* Must pass memory information to OS.  */
#define MULTIBOOT_MEMORY_INFO			0x00000002

/* Must pass video information to OS.  */
#define MULTIBOOT_VIDEO_MODE			0x00000004

/* This flag indicates the use of the address fields in the header.  */
#define MULTIBOOT_AOUT_KLUDGE			0x00010000

/* Flags to be set in the 'flags' member of the multiboot info structure.  */

/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY			0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV			0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE			0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS			0x00000008

/* These next two are mutually exclusive */

/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS		0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR			0X00000020

/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP			0x00000040

/* Is there drive info?  */
#define MULTIBOOT_INFO_DRIVE_INFO		0x00000080

/* Is there a config table?  */
#define MULTIBOOT_INFO_CONFIG_TABLE		0x00000100

/* Is there a boot loader name?  */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME		0x00000200

/* Is there a APM table?  */
#define MULTIBOOT_INFO_APM_TABLE		0x00000400

/* Is there video information?  */
#define MULTIBOOT_INFO_VIDEO_INFO		0x00000800

#ifndef ASM_FILE

typedef unsigned short		multiboot_uint16_t;
typedef unsigned int		multiboot_uint32_t;
typedef unsigned long long	multiboot_uint64_t;

struct multiboot_header
{
  /* Must be MULTIBOOT_MAGIC - see above.  */
  multiboot_uint32_t magic;

  /* Feature flags.  */
  multiboot_uint32_t flags;

  /* The above fields plus this one must equal 0 mod 2^32. */
  multiboot_uint32_t checksum;

  /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set.  */
  multiboot_uint32_t header_addr;
  multiboot_uint32_t load_addr;
  multiboot_uint32_t load_end_addr;
  multiboot_uint32_t bss_end_addr;
  multiboot_uint32_t entry_addr;

  /* These are only valid if MULTIBOOT_VIDEO_MODE is set.  */
  multiboot_uint32_t mode_type;
  multiboot_uint32_t width;
  multiboot_uint32_t height;
  multiboot_uint32_t depth;
};

/* The symbol table for a.out.  */
struct multiboot_aout_symbol_table
{
  multiboot_uint32_t tabsize;
  multiboot_uint32_t strsize;
  multiboot_uint32_t addr;
  multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;

/* The section header table for ELF.  */
struct multiboot_elf_section_header_table
{
  multiboot_uint32_t num;
  multiboot_uint32_t size;
  multiboot_uint32_t addr;
  multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;

struct multiboot_info
{
  /* Multiboot info version number */
  multiboot_uint32_t flags;

  /* Available memory from BIOS */
  multiboot_uint32_t mem_lower;
  multiboot_uint32_t mem_upper;

  /* "root" partition */
  multiboot_uint32_t boot_device;

  /* Kernel command line */
  multiboot_uint32_t cmdline;

  /* Boot-Module list */
  multiboot_uint32_t mods_count;
  multiboot_uint32_t mods_addr;

  union
  {
    multiboot_aout_symbol_table_t aout_sym;
    multiboot_elf_section_header_table_t elf_sec;
  } u;

  /* Memory Mapping buffer */
  multiboot_uint32_t mmap_length;
  multiboot_uint32_t mmap_addr;

  /* Drive Info buffer */
  multiboot_uint32_t drives_length;
  multiboot_uint32_t drives_addr;

  /* ROM configuration table */
  multiboot_uint32_t config_table;

  /* Boot Loader Name */
  multiboot_uint32_t boot_loader_name;

  /* APM table */
  multiboot_uint32_t apm_table;

  /* Video */
  multiboot_uint32_t vbe_control_info;
  multiboot_uint32_t vbe_mode_info;
  multiboot_uint16_t vbe_mode;
  multiboot_uint16_t vbe_interface_seg;
  multiboot_uint16_t vbe_interface_off;
  multiboot_uint16_t vbe_interface_len;
};
typedef struct multiboot_info multiboot_info_t;

struct multiboot_mmap_entry
{
  multiboot_uint32_t size;
  multiboot_uint64_t addr;
  multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE		1
#define MULTIBOOT_MEMORY_RESERVED		2
  multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;

struct multiboot_mod_list
{
  /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
  multiboot_uint32_t mod_start;
  multiboot_uint32_t mod_end;

  /* Module command line */
  multiboot_uint32_t cmdline;

  /* padding to take it to 16 bytes (must be zero) */
  multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;

#endif /* ! ASM_FILE */

#endif /* ! MULTIBOOT_HEADER */



[boot.S]

ここにマルチブートヘッダーをつけます。ブートローダーがカーネルイメージを

ロードした後、カーネルを起動しますが、boot.Sのstart:ラベルから

起動するようにします。


/* boot.S - bootstrap the kernel */
/* Copyright (C) 1999, 2001  Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#define ASM_FILE	1
#include <multiboot.h>

/* C symbol format. HAVE_ASM_USCORE is defined by configure.  */
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym)			_ ## sym
#else
# define EXT_C(sym)			sym
#endif

/* The size of our stack (16KB).  */
#define STACK_SIZE			0x4000

/* The flags for the Multiboot header.  */
#ifdef __ELF__
# define MULTIBOOT_HEADER_FLAGS		0x00000003
#else
# define MULTIBOOT_HEADER_FLAGS		0x00010003
#endif
	
	.text

	.globl	start, _start
start:
_start:
	jmp	multiboot_entry

	/* Align 32 bits boundary.  */
	.align	4

	/* Multiboot header.  */
multiboot_header:
	/* magic */
	.long	MULTIBOOT_HEADER_MAGIC
	/* flags */
	.long	MULTIBOOT_HEADER_FLAGS
	/* checksum */
	.long	-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
#ifndef __ELF__
	/* header_addr */
	.long	multiboot_header
	/* load_addr */
	.long	_start
	/* load_end_addr */
	.long	_edata
	/* bss_end_addr */
	.long	_end
	/* entry_addr */
	.long	multiboot_entry
#endif /* ! __ELF__ */

multiboot_entry:
	/* Initialize the stack pointer.  */
	movl	$(stack + STACK_SIZE), %esp

	/* Reset EFLAGS.  */
	pushl	$0
	popf

	/* Push the pointer to the Multiboot information structure.  */
	pushl	%ebx
	/* Push the magic value.  */
	pushl	%eax

	/* Now enter the C main function...  */
	call	EXT_C(cmain)

	/* Halt.  */
	pushl	$halt_message
	call	EXT_C(printf)
	
loop:	hlt
	jmp	loop

halt_message:
	.asciz	"Halted."

	/* Our stack area.  */
	.comm	stack, STACK_SIZE



[kernel.c]

カーネルのメイン関数です。

カーネルのメイン関数ではマジックのチェックとflagsの各ビットをチェックして

マルチブート情報の内容を表示する処理となります。表示に関しては

簡単なビデオドライバーをputchar関数(1文字表示関数)とcls関数

(スクリーンクリアー関数)のなかで書かれています。この中の処理の内容については

シンプルビデオドライバ を参照してください。


/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999  Free Software Foundation, Inc.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <multiboot.h>

/* Macros.  */

/* Check if the bit BIT in FLAGS is set.  */
#define CHECK_FLAG(flags,bit)	((flags) & (1 << (bit)))

/* Some screen stuff.  */
/* The number of columns.  */
#define COLUMNS			80
/* The number of lines.  */
#define LINES			24
/* The attribute of an character.  */
#define ATTRIBUTE		7
/* The video memory address.  */
#define VIDEO			0xB8000

/* Variables.  */
/* Save the X position.  */
static int xpos;
/* Save the Y position.  */
static int ypos;
/* Point to the video memory.  */
static volatile unsigned char *video;

/* Forward declarations.  */
void cmain (unsigned long magic, unsigned long addr);
static void cls (void);
static void itoa (char *buf, int base, int d);
static void putchar (int c);
void printf (const char *format, ...);

/* Check if MAGIC is valid and print the Multiboot information structure
   pointed by ADDR.  */
void
cmain (unsigned long magic, unsigned long addr)
{
  multiboot_info_t *mbi;

  /* Clear the screen.  */
  cls ();

  /* Am I booted by a Multiboot-compliant boot loader?  */
  if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
    {
      printf ("Invalid magic number: 0x%x\n", (unsigned) magic);
      return;
    }

  /* Set MBI to the address of the Multiboot information structure.  */
  mbi = (multiboot_info_t *) addr;

  /* Print out the flags.  */
  printf ("flags = 0x%x\n", (unsigned) mbi->flags);

  /* Are mem_* valid?  */
  if (CHECK_FLAG (mbi->flags, 0))
    printf ("mem_lower = %uKB, mem_upper = %uKB\n",
	    (unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);

  /* Is boot_device valid?  */
  if (CHECK_FLAG (mbi->flags, 1))
    printf ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);

  /* Is the command line passed?  */
  if (CHECK_FLAG (mbi->flags, 2))
    printf ("cmdline = %s\n", (char *) mbi->cmdline);

  /* Are mods_* valid?  */
  if (CHECK_FLAG (mbi->flags, 3))
    {
      multiboot_module_t *mod;
      int i;
      
      printf ("mods_count = %d, mods_addr = 0x%x\n",
          (int) mbi->mods_count, (int) mbi->mods_addr);
      for (i = 0, mod = (multiboot_module_t *) mbi->mods_addr;
       i < mbi->mods_count;
       i++, mod++)
          printf (" mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n",
              (unsigned) mod->mod_start,
              (unsigned) mod->mod_end,
              (char *) mod->cmdline);
    }

  /* Bits 4 and 5 are mutually exclusive!  */
  if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
    {
      printf ("Both bits 4 and 5 are set.\n");
      return;
    }

  /* Is the symbol table of a.out valid?  */
  if (CHECK_FLAG (mbi->flags, 4))
    {
      multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym);
      
      printf ("multiboot_aout_symbol_table: tabsize = 0x%0x, "
          "strsize = 0x%x, addr = 0x%x\n",
          (unsigned) multiboot_aout_sym->tabsize,
          (unsigned) multiboot_aout_sym->strsize,
          (unsigned) multiboot_aout_sym->addr);
    }

  /* Is the section header table of ELF valid?  */
  if (CHECK_FLAG (mbi->flags, 5))
    {
      multiboot_elf_section_header_table_t *multiboot_elf_sec = &(mbi->u.elf_sec);

      printf ("multiboot_elf_sec: num = %u, size = 0x%x,"
          " addr = 0x%x, shndx = 0x%x\n",
          (unsigned) multiboot_elf_sec->num, (unsigned) multiboot_elf_sec->size,
          (unsigned) multiboot_elf_sec->addr, (unsigned) multiboot_elf_sec->shndx);
    }

  /* Are mmap_* valid?  */
  if (CHECK_FLAG (mbi->flags, 6))
    {
      multiboot_memory_map_t *mmap;
      
      printf ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
          (unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
      for (mmap = (multiboot_memory_map_t *) mbi->mmap_addr;
       (unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
       mmap = (multiboot_memory_map_t *) ((unsigned long) mmap
                    + mmap->size + sizeof (mmap->size)))
    printf (" size = 0x%x, base_addr = 0x%x%x,"
        " length = 0x%x%x, type = 0x%x\n",
        (unsigned) mmap->size,
        mmap->addr >> 32,
        mmap->addr & 0xffffffff,
        mmap->len >> 32,
        mmap->len & 0xffffffff,
       (unsigned) mmap->type);
    }
}    

/* Clear the screen and initialize VIDEO, XPOS and YPOS.  */
static void
cls (void)
{
  int i;

  video = (unsigned char *) VIDEO;
  
  for (i = 0; i < COLUMNS * LINES * 2; i++)
    *(video + i) = 0;

  xpos = 0;
  ypos = 0;
}

/* Convert the integer D to a string and save the string in BUF. If
   BASE is equal to 'd', interpret that D is decimal, and if BASE is
   equal to 'x', interpret that D is hexadecimal.  */
static void
itoa (char *buf, int base, int d)
{
  char *p = buf;
  char *p1, *p2;
  unsigned long ud = d;
  int divisor = 10;

  /* If %d is specified and D is minus, put `-' in the head.  */
  if (base == 'd' && d < 0)
    {
      *p++ = '-';
      buf++;
      ud = -d;
    }
  else if (base == 'x')
    divisor = 16;

  /* Divide UD by DIVISOR until UD == 0.  */
  do
    {
      int remainder = ud % divisor;
      
      *p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
    }
  while (ud /= divisor);

  /* Terminate BUF.  */
  *p = 0;

  /* Reverse BUF.  */
  p1 = buf;
  p2 = p - 1;
  while (p1 < p2)
    {
      char tmp = *p1;
      *p1 = *p2;
      *p2 = tmp;
      p1++;
      p2--;
    }
}

/* Put the character C on the screen.  */
static void
putchar (int c)
{
  if (c == '\n' || c == '\r')
    {
    newline:
      xpos = 0;
      ypos++;
      if (ypos >= LINES)
	ypos = 0;
      return;
    }

  *(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
  *(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;

  xpos++;
  if (xpos >= COLUMNS)
    goto newline;
}

/* Format a string and print it on the screen, just like the libc
   function printf.  */
void
printf (const char *format, ...)
{
  char **arg = (char **) &format;
  int c;
  char buf[20];

  arg++;
  
  while ((c = *format++) != 0)
    {
      if (c != '%')
    putchar (c);
      else
    {
      char *p;
  
      c = *format++;
      switch (c)
        {
        case 'd':
        case 'u':
        case 'x':
          itoa (buf, c, *((int *) arg++));
          p = buf;
          goto string;
          break;

        case 's':
          p = *arg++;
          if (! p)
    	p = "(null)";

        string:
          while (*p)
    	putchar (*p++);
          break;

        default:
          putchar (*((int *) arg++));
          break;
        }
    }
    }
}



コンパイル

3つのファイルをコンパイルしていきます。

ここでのコンパイルはCygwin上でマルチブートカーネルのコンパイルする例となります。

gccには クロスコンパイラ で用意しましたgccを使用しています。

LinuxなどのUnix系OSを使用されている方は標準のgccでコンパイルできます。



boot.Sとkernel.cとmultiboot.hは同じディレクトリにあります。

まずは、カーネルkernel.cをコンパイルします。


$ i686-pc-linux-gnu-gcc -c -o kernel.o kernel.c -I./



次にboot.Sをコンパイルします。


$ i686-pc-linux-gnu-gcc -c -o boot.o boot.S -I./



できたオブジェクトファイルkernel.oとboot.oをリンキングします。


$ i686-pc-linux-gnu-ld -Ttext=0x100000 --oformat=elf32-i386 boot.o kernel.o -o KImage



最終的にここではKImageというマルチブートカーネルを作りました。

このカーネルをGrubなどで起動させます。下記図はVirtualBOXで

起動させたときの画面となります。

マルチブートカーネルをVritualBoxで起動

inserted by FC2 system