  LILO "the Linux Loader" の動作について
  佐野武俊 / Taketoshi Sano, (kgh12351@nifty.ne.jp)
  $Date: 2000/12/15 18:39:54 $, ($Revision: 1.10 $)

  この文書は、LILO "the Linux Loader" の動作について説明するものです。な
  お、対象としている LILO は version 21.5.1 であり、現時点での最新版であ
  る LILO version 21.6 とは異なります。
  ______________________________________________________________________

  目次

  1. 起動時の動作
     1.1 1st boot loader
     1.2 2nd boot loader
     1.3 BIOS によるディスクデータのロード
     1.4 Option linear
     1.5 Option lba32
     1.6 その他

  2. /sbin/lilo コマンドを実行した時の動作
  3. 補足
     3.1 この文書について
     3.2 MBR 用ブートセレクタについて
     3.3 参考

  ______________________________________________________________________

  1.  起動時の動作

  起動時に実行される LILO のコードには、1st boot loader と 2nd boot
  loader の 2 種類があります。

  これらのコードは通常、/boot/boot.b というファイルの中に保存されていま
  す。 /boot/boot.b の先頭 438bytes が 1st boot loader のコードであり、
  先頭から 512bytes を除いた残りが 2nd boot loader のコードに相当しま
  す。

  "lilo" コマンドを実行するとコマンドラインオプションの -b bootdev や設
  定ファイル lilo.conf のboot=bootdev によって指定された場所のブートセク
  ターに 1st boot loader がインストールされます。このとき同時に "lilo"
  コマンドのオプションや設定ファイル lilo.conf によって指定された
  timeout、delay、prompt、デフォルトの (カーネルの) 起動オプションなどの
  パラメータ、また 2nd boot loader やマップ (カーネル位置情報) ファイ
  ル、それに (指定された場合には) 起動メッセージファイルやキーボード変換
  テーブルファイルのディスク上の位置を示すセクターアドレスなどの情報が
  1st boot loader の中のデータ領域に保存されます。

  1st boot loader は 2nd boot loader をメモリーにロードするのが役目で
  す。 LILO の起動コードの主要な機能はすべて 2nd boot loader の中にあり
  ます。 2nd boot loader は 1st boot loader の中に含まれている情報をもと
  にマップファイルをロードしてその情報を解析し、その結果に従ってカーネル
  コードをロードします。また必要な場合には initrd で使用するルートイメー
  ジのロードも 2nd boot loader が実行します。起動プロンプトの表示や使用
  するカーネルの選択も 2nd boot loader の機能です。

  1.1.  1st boot loader

  LILO によるブートでは、最初にブートセクターの先頭 438 bytes に記録され
  た 1st boot loader が BIOS または MBR に置かれた他のブートローダーに
  よってロードされます。ロードに成功し、1st boot loader が実行を開始する
  と最初の "L" という文字が表示されます。

  次に 1st boot loader は "lilo" コマンド実行時に自分の中に記録された
  2nd boot loader のディスク上の位置 (セクターアドレス) を BIOS に提示し
  て、セクター単位で BIOS に 2nd boot loader をロードさせます。ロードに
  成功すると、コンソールに "I" という文字を表示して 2nd boot loader に制
  御を渡します。

  lilo のソースコードで 1st boot loader に相当するのは first.S です。
  (なんてわかりやすい名前でしょう！) 上記の "L", "I", の表示を実行してい
  るのは、それぞれ

       go:
       ;       cli                     ! no interrupts
               mov     ds,ax           ! AX is already set
       ;;      mov     es,ax           ! (ES may be wrong when restarting)
               mov     ax,#STACKSEG
               mov     ss,ax
               mov     sp,#STACK       ! set the stack
               sti                     ! now it is safe

               mov     al,#0x0d        ! gimme a CR ...
               call    display
               mov     al,#0x0a        ! ... an LF ...
               call    display
               mov     al,#0x4c        ! ... an 'L' ...
               call    display

  と

       done:   mov     al,#0x49        ! display an 'I'
               call    display
               jmpi    0,SECONDSEG     ! start the second stage loader

  の部分です。コメントを読めば、何をしているか想像がつきますね。

  もうちょっと詳しく 1st boot loader の動作を説明するために、 lilo のソ
  ースと一緒に配布されている tech.tex から引用します。 (この tech.tex は
  古いバージョンを対象に書かれているので、現状のコードとは必ずしも一致し
  ていない部分があります。ただし基本的な処理の流れはあまり変っていないと
  思うので、概略を知る上での参考にはなるでしょう。)

       The boot sector is loaded by the ROM-BIOS at address
       0x07C00. It moves itself to address 0x9A000, sets up the
       stack (growing downwards from 0x9B000 to 0x9A200), loads the
       secondary boot loader at address 0x9B000 and transfers con-
       trol to it.

       ROM-BIOS が 1st boot loader を 0x07C00 にロードすると、 1st
       boot loader は自分自身を 0x9A00 にコピーし、スタックを
       0x9B000 から 0x9A200 へと用意し、2nd boot loader を 0x9B000
       にロードしてそこへ制御を移行します。

  1.2.  2nd boot loader

  2nd boot loader が実行を開始すると、 2 番目の "L" という文字が表示され
  ます。次に 2nd boot loader は、map ファイルの先頭の部分をロードしま
  す。ここには LILO のブートメニューで選択肢として表示されるラベルの名前
  や、パスワードなどの情報が記録されています。

  この先頭部分の構造は LILO のソースコードにある common.h 中で以下のよう
  に規定されています。

       typedef struct {
           char name[MAX_IMAGE_NAME+1];
           char password[MAX_PW+1];
           unsigned short rd_size[2]; /* RAM disk size in bytes, 0 if none */
           SECTOR_ADDR initrd,start;
           unsigned short start_page; /* page at which the kernel is loaded high, 0
                                         if loading low */
           unsigned short flags,vga_mode;
       } IMAGE_DESCR;

  このマップファイル先頭部分のロードが完了すると "O" という文字をコンソ
  ールに表示し、指定された待ち時間のあいだ、キー入力をチェックします。

  この後、プロンプト表示やキー入力に対する処理を行い、これから起動するカ
  ーネルに対応したアドレスマップを上記の SECTOR_ADDR initrd,start によっ
  て指定されたセクターアドレスからロードし、このマップファイルに記録され
  ているカーネルや initrd ファイルのセクターアドレスを解読して、それぞれ
  をロードしていきます。

  必要なセクターのロードがすべて成功した後、カーネルコードに制御を移行し
  て LILO は動作を完了します。

  2nd boot loader の動作について再び tech.tex から引用しておきます。

       The secondary boot loader loads the descriptor table at
       0x9D200 and the sector containing the default command line
       at 0x9D600. If the default command line is enabled, its
       magic number is invalidated and the sector is written back
       to disk. This potentially dangerous operation can be dis-
       abled by defining LCF_READONLY when passing second.S through
       cpp.  Next, the secondary boot loader checks for user input.
       If either the default is used or if the user has specified
       an alternate image, the options sector is loaded at 0x9D600
       and the parameter line is constructed at 0x9D800.  If the
       resulting line contains the option "lock", the command line
       as entered by the user (it is saved before the final line is
       constructed) is written to the disk as the new default com-
       mand line. Also, if a fallback command line is set, it is
       copied to the default command line sector.

       2nd boot loader はデスクリプタテーブル (map file をロードす
       るための中間情報) を 0x9D200 へ、デフォルトのコマンドライン
       パラメータ (lilo.conf で指定されたもの) を 0x9D600 へロー
       ド。次にユーザーからの入力を調べ、選択されたイメージのオプ
       ションセクターを 0x9D600 へロードして起動パラメータ情報を
       0x9D800 に構成。

       Next, the floppy boot sector of that image is loaded at
       0x90000 (The floppy boot sector is only used as a source of
       setup information.), the setup part is loaded at 0x90200 and
       the kernel part is loaded at 0x10000, or, if the kernel has
       been compiled for being loaded ``high'' (i.e. with make
       bzImage), it is loaded at 0x100000 instead. During the load
       operations, the sectors of the map file are loaded at
       0x9D000.

       次に、(カーネルの) フロッピーブートセクターを 0x90000 へロー
       ドし、セットアップコードを 0x90200 へ、カーネル本体コードを
       0x10000 または 0x100000 へロードする。前者はカーネルが zIm-
       age の場合、後者は bzImage の場合である。 (なおカーネルのフ
       ロッピーブートセクターとはフロッピーディスクから Linux カー
       ネルを起動するためのコードであり、カーネルファイルの先頭
       512bytes に相当する。ここには rdev コマンドで設定できるルー
       トデバイスやスワップデバイスなどのカーネルパラメータが記録さ
       れているため、HDD から起動する場合にもメモリーにロードされ
       る。またカーネルのセットアップコードとはフロッピーブートセク
       ターに続く部分であり、BIOS 関係の初期化やメモリーサイズなど
       デバイス情報の取得、ビデオ関係の設定や CPU の 32bit モードへ
       の移行などを行なう。) マップファイル中のカーネル位置情報 (セ
       クターアドレス) はこのカーネルロード作業実行中に 0x9D000 へ
       順次ロードされ、新しいセクターをロードするたびに次のセクター
       位置情報によって上書きされていく。

       If the loaded image is a kernel image, control is trans-
       ferred to its setup code.

       ロードされたイメージがカーネルなら、0x90200 へロードしたカー
       ネルのセットアップコードへ制御を移行して LILO の動作は完了。

       If a different operating system is booted, things are a bit
       more difficult: the chain loader is loaded at 0x90200 and
       the boot sector of the other OS is loaded at 0x90400. The
       chain loader moves the partition table (loaded at 0x903BE as
       part of the chain loader) to 0x00600 and the boot sector to
       0x07C00. After that, it passes control to the boot sector.

       他の OS を起動する場合は、ちょいと話が違ってくる。チェインロ
       ーダは 0x90200 にロードされ、他の OS の起動セクタは 0x90400
       にロードされる。次にチェインローダはパーティションテーブル
       (チェインローダの一部として 0x903BE にロードされている) を
       0x00600 にコピーし、(他の OS の) 起動セクタを (ROM-BIOS が
       1st boot loader をロードするとの同じ場所である) 0x07C00 にコ
       ピーする。この後で、0x07C00 の起動セクタへ制御を移行すれば、
       あたかも ROM-BIOS によってロードされたのと同様に他の OS の起
       動が開始される、という寸法。

       Chain loaders that allow booting from a second drive (either
       floppy or hard disk) also install a small function to inter-
       cept BIOS calls and to swap the drive numbers at the top of
       available memory.

       2 番目のドライブから起動するためのチェインローダには、BIOS
       コールをトラップ (intercept) してドライブ番号を入れ換える
       (swap) ためのちょっとした関数を利用可能なメモリーの一番上に
       インストールしておくという作業も必要になる。

  1.3.  BIOS によるディスクデータのロード

  1st boot loader および 2nd boot loader とも、ディスクまたはフロッピー
  からファイルをメモリーにロードする作業は、すべて BIOS を利用して行ない
  ます。このために LILO は BIOS が理解できるよう、対象とするディスクのデ
  バイス ID とセクターアドレスによってロードするファイルを指定します。

  LILO がうまく動作せず、カーネルを起動できない原因として、

  1. セクターアドレスが正しくない場合

  2. ディスクのデバイス ID が正しくない場合

     の 2 種類が考えられます (両方に該当する場合もあり得ます)。

  セクターアドレスが正しくない場合というのは、特に CHS アクセスの場合
  BIOS が認識しているディスクのジオメトリ (C,H,S の値) と Linux カーネル
  が認識しているジオメトリが異なる場合や、24bit の CHS アドレスでは表現
  できない位置にある場合 (いわゆる 1023cyl 問題) などがあります。これら
  については linear オプションや lba32 オプションを指定することで対処可
  能です (これらのオプションについては後で説明します)。

  一方ディスクのデバイス ID が正しくない場合というのは、LILO が推定した
  デバイス ID が実際に BIOS の認識する ID と一致していない場合です。この
  問題は SCSI と IDE のディスクが混在するシステムや、最近では UDMA66 の
  ディスクを利用したシステムの場合に発生する可能性が高いようです。

  BIOS コール 0x13 では最初のフロッピーデバイスを 0x00、2 番目のフロッピ
  ーデバイスを 0x01 として、また最初のディスクデバイスを 0x80、2 番目の
  ディスクデバイスを 0x81 として認識します。つまり BIOS が起動ハードディ
  スクとして認識しているディスクは常にデバイス ID 0x80 でアクセスされま
  す。

  一方 LILO のデフォルトでは hda, hdb, sda, sdb の順にデバイスを検索し、
  見つかった順に 0x80, 0x81, 0x82, 0x83 と番号を割り当てていきます。従っ
  て SCSI と IDE の混在したシステムで SCSI ディスクを起動ディスクとして
  指定している場合や、UDMA66 のディスク (hde) を起動ディスクとして指定し
  ている場合には BIOS の認識と LILO の推定値が一致せず、起動できないとい
  う問題が起こります。

  こういう場合には、man lilo.conf に記載されている disk=device_name の説
  明にある bios=dev_id を利用して LILO に BIOS の認識しているデバイス ID
  を教えてあげる必要があります。

  LILO のソースコードには BIOS を利用してデバイス ID とディスクドライブ
  との対応を調べるツール (disk.com や dparam.com など) が含まれているの
  で、これらを利用して BIOS の認識しているデバイス ID を確認しておくと良
  いでしょう。

  1.4.  Option linear

  ときどき勘違いされる方がいますが、lilo.conf のパラメータに "Option
  linear" を追加しても、LILO のブートローダーがディスクからファイルをロ
  ードする時に INT13 AH=4? な Extended BIOS コールを利用するわけではあり
  ません。

  このオプションは、マザーボードの BIOS が認識するディスクのジオメトリと
  Linux カーネルの認識するジオメトリが互いに異なる場合、LILO の使うマッ
  プファイルにはリニアセクターアドレスを保存しておいて、起動時に「BIOS
  が認識するディスクのジオメトリ」を基準とした CHS セクターアドレスに換
  算し、このアドレスを BIOS に渡してディスクから必要なセクターをロードす
  るという動作を指定するものです。

  このオプションを指定しても、LILO が「1023 シリンダ」を超える領域にアク
  セスすることは不可能ですので、御注意下さい。

  1.5.  Option lba32

  このオプションは LILO 21-3 以降でサポートされたものです。

  マザーボードもディスクドライブも最近のもので、どうしても 1023 シリンダ
  の壁を超えて起動させたい場合には、このオプションを試してみると良いで
  しょう。

  1.6.  その他

  Linux カーネルの起動コードの詳細については、コメントから読む Linux カ
  ーネル <http://www.linux.or.jp/JF/JFdocs/readkernel.html> を参照してく
  ださい。以下は LILO に関連する部分の概略です。

  Linux カーネルの先頭 512bytes はカーネルが自前で持っているブートローダ
  ーであり、 "make zdisk" で作成したブートフロッピーからの起動ではこれが
  使われます。

  この自前のブートローダーは i386 系の場合 カーネルソースツリーの
  arch/i386/boot/bootsect.S にあります。

  LILO や LOADLIN からの起動では、カーネルのロードが完了するとこの部分
  (bootsect) を飛ばして次の setup.S や video.S のコードに制御を移行しま
  す。 CPU の動作を 16bit モードから 32bit モードに移行するのは setup.S
  の中で実行しています。

  これらの実行が終わると次に arch/i386/boot/compressed/head.S が実行され
  ます。こちらは最初から 32bit モードで実行されることを前提としたコード
  になっています。この head.S の中にある decompress_kernel() が圧縮され
  たカーネル本体を展開します。 head.S の実行は展開されたカーネル本体に制
  御を渡して完了します。

  2.  /sbin/lilo コマンドを実行した時の動作

  "lilo" コマンドは別名 map creator (マップ作成ツール) と呼ばれており、
  その主な機能は設定ファイル lilo.conf の解読と、その中の指定およびコマ
  ンドラインオプションによる指定に従ってマップファイル (bootloader が起
  動時に使用するファイル) を作成し、その中に必要な Map Descriptor Table
  を適切に記録することです。

  通常 "lilo" コマンドは lilo.conf を解読して、使用するカーネルごとに指
  定された Image ファイル (および、もしあれば initrd ファイル) に対する
  セクターアドレスを取得し、それを起動時に使用する Map Descriptor Table
  のデータ型式にしてマップファイルに保存します。

  "lilo" コマンドでファイルからセクターアドレスを調べる部分は geometry.c
  の geo_find() にあります。

  "lilo" コマンドに -v オプションを複数重ねて起動すると、マップ作成に使
  用されたセクターアドレスを観察することができます。
  もし Image= で指定したファイルがマウントされていないファイルシステム上
  にあると、"lilo" コマンドはそのファイルに対するセクターアドレスを調べ
  ることができずにエラーを出します。

  なお、起動時に使用する 2nd boot loader のセクターアドレスを 1st boot
  loader のコード中に書き込むのも "lilo" コマンドの仕事です。

  3.  補足

  3.1.  この文書について

  Linux 用のブートローダーとして、この "lilo" を使う場合が多いのですが、
  マニュアルでエラーメッセージの意味を確認する際など、動作を理解しておく
  ときっと役に立つだろうと思ってこの文書を作成しました。

  この文書を書く際には LILO のソースコードや Linux カーネルのソースコー
  ド、またそれらについて過去に fj.os.linux や Nifty FUNIX で議論した内容
  などを参考にしています。議論の中でいろいろと教えてくださった方々に感謝
  します。また JF のみなさんには文章の校正や内容についての提案など、いろ
  いろと有益な御意見を頂きました。

  内容についてはできる限り正確を期したつもりですが、まだまだ間違いや誤解
  している個所などあるかもしれません。この文書に対する御意見、御感想、間
  違いの指摘などありましたら、著者または JF project まで御一報頂ければさ
  いわいです。

  なお、この文書の配布等については GPL2 に従うものとします。

  3.2.  MBR 用ブートセレクタについて

  個人的な好みですが、"lilo" のインストール先は MBR (Master Boot Record,
  ハードディスク先頭のブートセクタ) よりも Linux システムのルートパー
  ティションのほうが適していると思います。ハードディスクの MBR にはより
  一般的なブートセレクタ、たとえば os-bs, bteasy, extIPL, btsel, GAG  な
  どをインストールしたほうがトラブルが起きた時の対応などを考慮するとより
  安心できます。

  GAG の附属文書については JF に日本語訳があります。

  最近は MBM や SBM などの新しいブートマネージャも人気があるようです。
  MBM は起動メニューの背景として好きな画像を表示できる点が特徴です。

  この中でも特に個人的なお薦めは extIPL
  <http://www.tsden.org/takamiti/extipl/> です。バージョン 5 (現在ベータ
  版のテスト公開中です) からは LBA32 アクセスにも正式対応し、また起動時
  のメニューが GUI 風なものとなり、パーティションを識別する文字列を任意
  に設定できるようにもなりました。また拡張領域内の論理領域からも従来同様
  起動可能です。ソース公開 (Ver 5 からライセンスが GPL2 になりました) で
  あり、かつ Linux 上で make できる (gcc + nasm) 点も見逃せないポイント
  です。

  3.3.  参考

  o  コメントから読む Linux カーネル
     <http://www.linux.or.jp/JF/JFdocs/readkernel.html>

  o  ハードディスクドライブの基礎知識
     <http://www.linux.or.jp/JF/JFdocs/hdd-intro.html>

  o  Large Disk HOWTO <http://www.linux.or.jp/JF/JFdocs/Large-Disk-
     HOWTO.html>

  o  Lilo mini-Howto <http://www.linux.or.jp/JF/JFdocs/LILO.html>

  o  LILO FAQ <http://www.linux.or.jp/JF/JFdocs/LILO-FAQ.html>

  o  GAG (GRAPHICAL BOOT MANAGER)
     <http://www.linux.or.jp/JF/JFdocs/GAG.html>

  o  extIPL <http://www.tsden.org/takamiti/extipl/>

  o  ブート色々 <http://www.geocities.co.jp/SiliconValley-
     Bay/2414/linuxboot/boot.html>

  o  マルチブートの仕方
     <http://www.tkcity.net/~nobusan/hardware/boot_hdd/multi_boot/index.html>

  o  ブートとハードディスクのすべて
     <http://www.tkcity.net/~nobusan/hardware/boot_hdd/index.html>

