Linux Kernel 2.4 Documentation:
/usr/src/linux/Documentation/i386/boot.txt
i386/boot.txt
Linux/i386 ブートプロトコル
[プレインテキスト版]
- 原著作者: H. Peter Anvin <hpa@zytor.com>
- 翻訳者: 川崎 貴彦 <takahiko@hakubi.co.jp>
- バージョン: 2.4.15
- 翻訳日時: 2002/01/08
LINUX/I386 ブートプロトコル
----------------------------
H. Peter Anvin <hpa@zytor.com>
最終更新日 2000-10-29
日本語訳:JF プロジェクト
翻訳者:
川崎 貴彦 <takahiko@hakubi.co.jp>
校正協力者:
森本 淳 <morimoto@xantia.citroen.org>
瀬戸口 崇 <setzer@mx3.tiki.ne.jp>
野本 浩一 <hng@ps.ksky.ne.jp>
最終更新日 2002-01-08
i386 プラットフォームでは、Linux カーネルは多少複雑なブートの慣習を
用いています。これは一部には、カーネル自身を起動可能イメージにしたい
という初期の要望に加え、複雑な PC のメモリモデルという歴史的側面に
よって、及び、主要オペレーティングシステムとしてのリアルモード DOS の
事実上の消滅による PC 産業界の期待の変化によって進化してきました。
現在では、四つのバージョンの Linux/i386 ブートプロトコルが存在します。
古いカーネル: zImage/Image のサポートのみ。ごく初期のカーネルには
コマンドラインすらサポートしていないものもあります。
プロトコル 2.00: (カーネル 1.3.73) ブートローダとカーネル間の連携をとる
ための形式化された方法に加えて、bzImage と initrd の
サポートが追加されました。従来のセットアップ領域が
書込み可能であることを依然として仮定してはいますが、
setup.S は再配置可能になりました。
プロトコル 2.01: (カーネル 1.3.76) ヒープオーバーラン警告が追加されました。
プロトコル 2.02: (カーネル 2.4.0-test3-pre3) 新しいコマンドラインプロトコル。
従来のメモリ上限を下げました。従来のセットアップ領域を上書き
しないことにより、SMM もしくは 32 ビット BIOS エントリ
ポイントから EBDA を使用するシステムでも、ブートが安全に
なりました。zImage は推奨されませんが、サポートされます。
**** メモリレイアウト
Image もしくは zImage カーネルに用いられるカーネルローダの従来の
メモリマップは、典型的には次のようになっています。
| |
0A0000 +----------------------------------+
| BIOS 用に予約済み | 使用不可。BIOS EBDA 用に予約済み
09A000 +----------------------------------+
| スタック/ヒープ/コマンドライン | カーネルのリアルモードコードが使用
098000 +----------------------------------+
| カーネルセットアップ | カーネルのリアルモードコード
090200 +----------------------------------+
| カーネルブートセクタ | カーネルのレガシーブートセクタ
090000 +----------------------------------+
| プロテクトモードカーネル | カーネルイメージの大部分
010000 +----------------------------------+
| ブートローダ | <- ブートセクタ・エントリポイント 0000:7C00
001000 +----------------------------------+
| MBR/BIOS 用に予約済み |
000800 +----------------------------------+
| 典型的には MBR が使用 |
000600 +----------------------------------+
| BIOS 使用のみ |
000000 +----------------------------------+
bzImage を使用するとき、プロテクトモードカーネルは 0x100000 ("高位
メモリ") に再配置され、カーネルのリアルモードブロック (ブートセクタ、
セットアップ、スタック/ヒープ)は、0x10000 と低位メモリの終了位置の
間ならばどこにでも再配置可能とされていました。不運にも、プロトコル
2.00 とプロトコル 2.01 は、0x9XXXX のメモリ範囲にコマンドラインが
存在することを要求し、そのメモリ範囲は初動時カーネルによって依然として
上書きされます。プロトコル 2.02 では、これを修正します。
"メモリ上限" ――ブートローダが扱う低位メモリ内の最高位ポイント―― を
可能な限り低く保つことは望ましいことです。なぜなら、新しい BIOS の中には、
低位メモリの上限近くに、拡張 BIOS データ領域と呼ばれるかなり大きな量の
メモリを配置するものもあるからです。ブートローダは、"INT 12h" BIOS
コールを使って、低位メモリがどれくらい使用できるかを確認しなければ
なりません。
不運にも INT 12h がメモリ不足を検出した場合には、ブートローダは大抵
ユーザにエラーメッセージを出すくらいのことしかできません。そのため、
ブートローダは、合理的に可能な限り、できるだけ少ない領域を占めるように
設計されるべきです。0x90000 セグメントにデータが書き込まれることを
必要とする zImage カーネルや古い bzImage カーネルのためには、ブート
ローダは 0x9A000 より上のメモリを使わないようにしなければなりません。
非常に多くの BIOS が、これより上の領域を破壊してしまうのです。
**** リアルモードカーネルヘッダ
下記の文書内、及び、カーネルブートシーケンス内のどの位置においても、
"セクタ" は 512 バイトのことです。メディア内の実際のセクタサイズとは
関係ありません。
Linux カーネルをロードする最初のステップでは、リアルモードコード
(ブートセクタとセットアップコード) をロードし、オフセット 0x01f1 に
存在する下記のヘッダを検査しなければなりません。ブートローダは最初の
二つのセクタ (1K) だけをロードし、それからブートアップセクタサイズを
調べることもできますが、リアルモードコードは合計で 32K まで可能です。
ヘッダは次のようになっています。
オフセット プロトコル 名称 意味
/サイズ
01F1/1 ALL setup_sects セクタ単位のセットアップのサイズ
01F2/2 ALL root_flags セットされていれば、リードオンリーで root がマウントされる
01F4/2 ALL syssize 使用不可。bootsect.S による使用のみ。
01F6/2 ALL swap_dev 使用不可。旧式。
01F8/2 ALL ram_size 使用不可。bootsect.S による使用のみ。
01FA/2 ALL vid_mode ビデオモード制御
01FC/2 ALL root_dev デフォルトのルートデバイス番号
01FE/2 ALL boot_flag マジックナンバー 0xAA55
0200/2 2.00+ jump ジャンプ命令
0202/4 2.00+ header マジックナンバー "HdrS"
0206/2 2.00+ version サポートされるブートプロトコルのバージョン
0208/4 2.00+ realmode_swtch ブートローダフック (下記参照)
020C/4 2.00+ start_sys カーネルバージョン文字列を指す
0210/1 2.00+ type_of_loader ブートローダ識別子
0211/1 2.00+ loadflags ブートプロトコルオプションフラグ
0212/2 2.00+ setup_move_size 高位メモリサイズへ移動 (フックと共に使用)
0214/4 2.00+ code32_start ブートローダフック (下記参照)
0218/4 2.00+ ramdisk_image initrd ロードアドレス (ブートローダによりセットされる)
021C/4 2.00+ ramdisk_size initrd サイズ (ブートローダによりセットされる)
0220/4 2.00+ bootsect_kludge 使用不可。bootsect.S による使用のみ。
0224/2 2.01+ heap_end_ptr セットアップ終了位置以降のフリーメモリ
0226/2 N/A pad1 未使用
0228/4 2.02+ cmd_line_ptr カーネルコマンドラインへの 32 ビットポインタ
後方互換性のため、setup_sects フィールドが 0 の場合には、実際の値は
4 となります。
オフセット 0x202 の位置にマジックナンバー "HdrS" (0x53726448) を発見できない
場合、ブートプロトコルのバージョンは "古い" です。古いカーネルをロードする
ときには、下記のパラメータが仮定されます。
イメージタイプ = zImage
initrd 未サポート
リアルモードカーネルは 0x90000 に配置されなければならない。
それ以外ならば、"version" フィールドはプロトコルのバージョンを含んで
います。例えば、プロトコルバージョン 2.01 は、0x0201 をこのフィールドに
含んでいます。ヘッダ内のフィールドをセットするときには、使用中のプロ
トコルバージョンでサポートされているフィールドのみをセットするように
しなければなりません。
ほとんどのブートローダは、単にそのターゲットアドレスにカーネルを直接
ロードするでしょう。そのようなブートローダは、ヘッダ内のほとんどの
フィールドについては、情報を埋めることについて心配する必要はありません。
とはいうものの、下記のフィールドは埋めなければなりません。
vid_mode:
「特別なコマンドラインオプション」を参照してください。
type_of_loader:
あなたのブートローダが arch/i386/boot/setup.S で割り当てられ
ている識別子を持っているならば、その値を入れます。そうでなけ
れば、ここには 0xFF を入れます。
loadflags, heap_end_ptr:
プロトコルバージョンが 2.01 以上ならば、セットアップヒープの
オフセットリミットを head_end_ptr に入れ、loadflags の 0x80
ビット (CAN_USE_HEAP) をセットします。heap_end_ptr は、セット
アップの開始位置 (オフセット 0x0200) からの相対位置です。
setup_move_size:
プロトコル 2.00 もしくは 2.01 を使っているとき、リアルモード
カーネルが 0x90000 にロードされないならば、リアルモードカーネルは
ローディングシーケンス内で、のちほどそこに移動させられます。
リアルモードカーネル自体の他に追加のデータ (カーネルコマンド
ラインなど) も移動させたい場合には、このフィールドを埋めます。
ramdisk_image, ramdisk_size:
ブートローダがイニシャル RAM ディスク (initrd) をロードしている場合、
RAM ディスクデータを指す 32 ビットポインタを ramdisk_image にセットし、
その RAM ディスクデータのサイズを ramdisk_size にセットします。
initrd は典型的には、メモリ内のできる限り高位に配置されるべきです。
なぜなら、そうしなければ、初動時カーネルの初期化シーケンスによって
上書きされてしまうかもしれないからです。しかしながら、全種類の
カーネルに initrd を参照させたいのならば、initrd をアドレス
0x3C000000 より上に配置してはいけません。
cmd_line_ptr:
プロトコルバージョンが 2.02 以上ならば、これは、カーネルコマン
ドラインを指す 32 ビットポインタです。カーネルコマンドラインは、
セットアップ終了位置から 0xA0000 の間ならば、どこにでも配置す
ることができます。たとえコマンドラインをサポートしていない場合
でも、このフィールドは埋めなければなりません。そのような場合に
は、空文字列を指すようにします (もしくは、さらに良いのは文字列
"auto" を指すようにすることです) 。このフィールドがゼロのまま
ですと、カーネルは、ブートローダがプロトコル 2.02 をサポートし
ていないと仮定してしまいます。
**** カーネルコマンドライン
カーネルコマンドラインは、ブートローダがカーネルと連携するための
重要な方法となってきています。幾つかのオプションは、ブートローダ
自身にも関係しています。下記の "特別なコマンドラインオプション" を
参照してください。
カーネルコマンドラインは、ヌル終端された長さ 255 までの文字列、
プラス、最後のヌルです。
ブートプロトコルバージョンが 2.02 以降ならば、カーネルコマンド
ラインのアドレスは、ヘッダフィールド cmd_line_ptr で与えられます
(上述) 。
プロトコルバージョンが 2.02 よりも上ならば、カーネルコマンドラインは
下記のプロトコルを用いて入力されます。
オフセット 0x0020 (ワード) の "cmd_line_magic" に、マジック
ナンバー 0xA33F を入れます。
オフセット 0x0022 (ワード) の "cmd_line_offset" に、カーネル
コマンドラインのオフセットを入れます (リアルモードカーネルの
開始位置に対して相対) 。
カーネルコマンドラインは setup_move_size でカバーされる
メモリ範囲になければならないので、このフィールドを調整
する必要があります。
**** ブート設定の例
設定例として、下記のリアルモードセグメントレイアウトを仮定します。
0x0000-0x7FFF リアルモードカーネル
0x8000-0x8FFF スタックとヒープ
0x9000-0x90FF カーネルコマンドライン
このブートローダは、ヘッダに下記のフィールドを入れなければなりません。
unsigned long base_ptr; /* リアルモードセグメントのベースアドレス */
if ( setup_sects == 0 ) {
setup_sects = 4;
}
if ( protocol >= 0x0200 ) {
type_of_loader = <type code>;
if ( loading_initrd ) {
ramdisk_image = <initrd_address>;
ramdisk_size = <initrd_size>;
}
if ( protocol >= 0x0201 ) {
heap_end_ptr = 0x9000 - 0x200;
loadflags |= 0x80; /* CAN_USE_HEAP */
}
if ( protocol >= 0x0202 ) {
cmd_line_ptr = base_ptr + 0x9000;
} else {
cmd_line_magic = 0xA33F;
cmd_line_offset = 0x9000;
setup_move_size = 0x9100;
}
} else {
/* とても古いカーネル */
cmd_line_magic = 0xA33F;
cmd_line_offset = 0x9000;
/* とても古いカーネルは、そのリアルモードコードを
0x90000 にロードしなければならない */
if ( base_ptr != 0x90000 ) {
/* リアルモードカーネルをコピーする */
memcpy(0x90000, base_ptr, (setup_sects+1)*512);
/* コマンドラインをコピーする */
memcpy(0x99000, base_ptr+0x9000, 256);
base_ptr = 0x90000; /* 再配置された */
}
/* 32K までメモリをクリアすることが推奨される */
memset(0x90000 + (setup_sects+1)*512, 0,
(64-(setup_sects+1))*512);
}
**** カーネルの残りの部分をロードする
非リアルモードカーネルは、カーネルファイルのオフセット (setup_sects+1)*512
から始まります (再度述べますが、setup_sects が 0 のときは、実際の値は
4 です) 。非リアルモードカーネルは、Image/zImage カーネルならば、
アドレス 0x10000 に、bzImage カーネルならば 0x100000 にロードされ
なければなりません。
プロトコルが 2.00 以上で、loadflags フィールドの 0x01 ビット
(LOAD_HIGH) がセットされているならば、カーネルは bzImage です。
is_bzImage = (protocol >= 0x0200) && (loadflags & 0x01);
load_address = is_bzImage ? 0x100000 : 0x10000;
Image/zImage カーネルは、512K のサイズまで可能なので、0x10000 から
0x90000 の全メモリ範囲を使うということに注意してください。これは、
これらのカーネルがリアルモード部分を 0x90000 にロードするという
ことがかなり大変な要求であるということを意味します。bzImage の
カーネルは、もっと多くの柔軟性を提供します。
**** 特別なコマンドラインオプション
ブートローダによって提供されるコマンドラインがユーザによって入力される
ならば、ユーザは下記に示すコマンドラインオプションが動作することを期待
してもかまいません。これらは通常、それら全てがカーネルにとって実質的な
意味があるわけではないにしても、カーネルコマンドラインから削除される
べきではありません。ブートローダ自身用のコマンドラインオプションの追加を
必要とするブートローダの作者は、それらのオプションが、現在もしくは将来の
カーネルオプションと競合しないようにするため、
linux/Documentation/kernel-parameters.txt に、それらのオプションを
登録するべきです。
vga=<mode>
ここにおける <mode> は、整数 (C の記法で、10 進数、8 進数、
16 進数のいずれか) か、もしくは、"normal" (0xFFFF を意味する)、
"ext" (0xFFFE を 意味する)、"ask" (0xFFFD を意味する) という
文字列のいずれかです。この値は、コマンドラインが解析される前に
カーネルがそれを使用するときに、vid_mode フィールドにセット
されなければなりません。
mem=<size>
<size> は C の記法による整数で、K, M, G (<<10, <<20, <<30 を
意味します) を後ろにつけることもできます。これにより、カー
ネルにメモリの終了位置を伝えます。これは、initrd の配置可能
位置に影響を与えます。というのは、initrd はメモリの終了位置
近くに配置されるからです。このオプションは、カーネルとブート
ローダ *双方* に対するオプションであることに注意してください!
initrd=<file>
initrd がロードされます。<file> の意味は、明らかにブートローダ
依存事項であり、ブートローダ (LILO など) の中には、そのような
コマンドを持っていないものもあります。
さらに、ユーザ指定のコマンドラインに下記のオプションを加えている
ブートローダもあります。
BOOT_IMAGE=<file>
ロードされるブートイメージ。再度述べますが、<file> の意味は、
明らかにブートローダに依存しています。
auto
明示的なユーザによる介入なしでブートされるカーネル。
これらのオプションがブートローダにより追加されるならば、ユーザ指定の
もしくはコンフィギュレーション指定のコマンドラインよりも前に、これら
が *最初* に配置されることが強く推奨されます。そうでない場合、
"init=/bin/sh" は "auto" オプションで混乱してしまうでしょう。
**** カーネルを走らせる
カーネルエントリポイントにジャンプすることにより、カーネルはスタート
します。カーネルエントリポイントは、リアルモードカーネルの開始位置
から *セグメント* オフセット 0x20 のところに配置されます。これは、
もしもリアルモードカーネルコードを 0x90000 にロードした場合、カーネル
エントリポイントが 9020:0000 になることを意味しています。
エントリでは、ds = es = ss はリアルモードカーネルコードの開始位置
(もしもコードが 0x90000 にロードされているならば 0x9000) を指し、
sp は適切にセットされ ――通常はヒープのトップを指す――、割込みは
禁止されるべきです。さらに、カーネル内のバグから保護するため、ブート
ローダが fs = gs = ds = es = ss とセットすることが推奨されます。
上記の例では、次のようにすることになるでしょう。
/* 注意: "古い" カーネルプロトコルの場合、base_ptr は、この時点で
0x90000 でなければならない。前のサンプルコードを参照のこと。*/
seg = base_ptr >> 4;
cli(); /* 割込みを禁止にして入る! */
/* リアルモードカーネルスタックをセットする */
_SS = seg;
_SP = 0x9000; /* SS をロード後、すぐに SP をロードする! */
_DS = _ES = _FS = _GS = seg;
jmp_far(seg+0x20, 0); /* カーネルを走らせる */
ブートセクタがフロッピードライブにアクセスするならば、カーネルを
走らせる前にフロッピーモーターを切ることを推奨します。というのは、
カーネルのブートは割込みを禁止のままにしておくので、モーターが
オフにならないからです。特に、ロードされたカーネルがフロッピー
ドライバを要求時ロードモジュールとして持っていた場合に!
**** 高度なブート時フック
もしもブートローダが特に敵対的な環境 (DOS 下で走る LOADLIN など) で
走る場合は、標準的メモリ配置要求に従うことは不可能でしょう。そのような
ブートローダは、もしセットされているならば、適切な段階でカーネルに
よって実行される下記に示すフックを使ってもかまいません。これらの
フックの使用は、おそらく、絶対的最終手段としてのみ考慮されるべきです!
重要: 全てのフックは、おまじないとして %esp, %ebp, %esi, %edi を保存する
ことが要求されます。
realmode_swtch:
プロテクトモードに移行する直前に実行される 16 ビットリアルモードの
far サブルーチン。デフォルトのルーチンは、NMI を無効にするので、
あなたのルーチンもおそらくそうするべきでしょう。
code32_start:
プロテクトモード移行直後、カーネル解凍前に *ジャンプされる*
32 ビットのフラットモードのルーチン。CS 以外のセグメントは
セットされていません。他のセグメントは、あなた自身が、
KERNEL_DS (0x18) にセットしなければなりません。
フック処理完了後、あなたのブートローダが上書きする前にこの
フィールドにセットされていたアドレスへジャンプしなければ
なりません。
Linux カーネル 2.4 付属文書一覧へ戻る