BOOT loader LanDisk †
uClibcベースで再構築、仕様を中変更しました。 (2007/06/02)
- BOOT loader LanDisk (BOOT LoaDer landiskの略かも)
大まかに言えば、lilo(boot.b-selk)の変わりになるセレクタ機能付きboot loaderを、linuxのkexec機能を使って実装しようという試みです。
同じようなことを考える人はたくさんいるようで、ぐぐるとkbootなるものが PS3Linux で実際に活用されているというでは・・・。ただし、コンソールがあってキー入力できることが前提なので、LANDISKで使用するのは、ちと厳しそうです。
そこで、lilo(boot.b-selk)の思想を受け継ぐべく、LANDISKの限られたI/O機能(ボタンとLED)を駆使してセレクタ機能を実現するブートローダを作ってみようかと。
なお、大まかな枠組みは「カーネルとユーザランドの統合単一イメージ:vmlinuz-initramfs-landisk」にて、大体完成しているので、ここでは、ブートセレクタ部と、ローダ部の作成(説明)を行うことにします。
ブートセレクタの仕様 †
- 機能仕様
- ブートセレクトの数は4個(ユーザが使えるラベルは3個で、残り1個はレスキュー用)あれば十分かな?
- 電源スイッチのトグル(ON/OFF)回数でブートラベルを指定。
- 現在のブートラベルは赤LEDの点滅パターンを見ればわかるようにしたい。
- 6秒間、スイッチがトグルしなかった場合、現時点のブートラベルでもってブート開始。
- LEDの仕様
- ブートラベル1…LED点滅
- ブートラベル2…LED2回点滅→休止→LED2回点滅→休止→繰り返し
- ブートラベル3…LED3回点滅→休止→LED3回点滅→休止→繰り返し
- ブートラベル0…LED常時点灯(レスキュー用途)
- スイッチの仕様
- 初期状態はブートラベル1。
- トグル動作が行われる毎に、ブートラベル2→ブートラベル3→ブートラベル0→ブートラベル1→繰り返し
ローダ部の仕様 †
- カーネルとブートパラメータの指定方法
- /boot/bootld.confへカーネルブート情報($IMAGEと$APPEND)をユーザが定義するシンプルな仕様とする。
具体的には、set_bootlabel()とset_network()関数をユーザが定義する(詳細後述)。
なお、set_network()はレスキューモードで使用されるネットワーク設定関数である。
- /boot/bootld.conf の格納場所
ファイルシステムタイプがext2,ext3,vfat,reiserfsのいずれかの任意のパーティションに格納可能。
複数のパーティションに /boot/bootld.conf が存在する場合はデバイス番号が大きい方を優先する。
『例えば、万が一、内蔵ディスク(/dev/sda3)のbootld.conf を書き損じて起動しなくなった場合でも、USBスティック(/dev/sdb1)内の/boot/bootld.conf が優先されるので、USBスティックを差して起動すればシステムを復旧可能。』てな使い方を想定
- set_bootlabel()
引数は1個。
1,2,3のいずれかのブートラベル番号が引数として渡ってくるので、それに応じて、$IMAGEと$APPENDをセットして返す。
- $IMAGE
パーティション指定部とカーネルイメージの場所をコロン":"で結合した形式とする。
- $IMAGE=/dev/sda1:/boot/vmlinuz-2.6.33-rc2-landisk
/dev/sda1の/boot/vmlinuz-2.6.33-rc2-landiskを使用するという意味。わかりやすいw
bootld上では、全てのデバイスはSCSIデバイスとして扱われるため、IDE上のデバイス番号と異なる点に注意。/dev/hda1→/dev/sda1へと置き換えて記述すること。
- $IMAGE=/dev/sda1
イメージの場所は省略可能。この場合、/zImage、/vmlinuz、/boot/zImage /boot/vmlinuz の順にサーチする。
カーネルの実態へリンクを張っておくと便利かも。
$IMAGE=""
イメージが無指定の場合、sdd→sdc→sdb→sdaの順にサーチする?←けっこう面倒かも。やらない。
パーティション指定部が未検出のデバイスだった場合 ← 本処理は不要になった
恐らくそれはusb_storageなので、自動的にusb_storageモジュールを組み込む。
(fdisk -l /dev/sdb等とやれば、未検出のデバイスが検出されるようだ。)
- $IMAGE="http://192.168.1.80/zImage"
パーティション指定部が"http"であった場合、カーネルをwgetする。クロスビルド環境で開発している場合、けっこう便利かも。
- $APPEND
カーネルの起動パラメータ。/proc/cmdline。kexecの--append=引数そのもの。
- set_bootlabel()の例
set_bootlabel()
{
case "$1" in
1 ) # search for /zImage /vmlinuz /boot/zImage /boot/vmlinuz
IMAGE=/dev/sda3
APPEND="mem=64M console=ttySC1,9600 root=/dev/sda3"
;;
2 ) # LANDISK original 2.4.21 kernel
IMAGE=/dev/sda1
APPEND="mem=64M console=ttySC1,9600 root=/dev/hda1"
;;
3 ) # from usb-storage
IMAGE=/dev/sdb1
APPEND="mem=64M console=ttySC1,9600 root=/dev/sdb1"
;;
sample1 ) # full path
IMAGE=/dev/sda3:/boot/vmlinuz-2.6.22-rc2-landisk
APPEND="mem=64M console=ttySC1,9600 root=/dev/sda3"
;;
sample2 ) # from Network
IMAGE=http://192.168.1.80/zImage
APPEND="mem=64M console=ttySC1,9600 root=/dev/sda3"
;;
esac
}
- set_network関数
説明省略。例をみてくだい。
- /boot/bootld.confのset_network()の例 … dhcpによるIPアドレス自動割当
set_network()
{
hostname bootld
ifup -a
}
- /boot/bootld.confのset_network()の例 … 固定IPアドレス
set_network()
{
hostname bootld
# over write /etc/network/interfaces
cat <<EOF > /etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.80
netmask 255.255.255.0
gateway 192.168.1.1
EOF
# over write /etc/resolv.conf
cat <<EOF > /etc/resolv.conf
nameserver 192.168.1.1
EOF
ifup -a
}
実装 †
- /etc/init.d/bootld
- 第1段階 … ブートセレクタ
- 選択されたブートラベルを$LABEL変数へセットする。
- `select_btn $SELECT_DELAY `にて $SELECT_DELAY秒間の無入力期間を過ぎれば抜ける。
#!/bin/sh
export PATH=/sbin:/bin:/usr/bin
/bin/mount -a
echo ; echo "BOOT loader LanDisk V1.3"
#----------------------------------------------
# 1. boot select
#----------------------------------------------
LABEL=0
BTN=push-switch.0
SELECT_DELAY=6
while [ "$BTN" != "" ] ; do
LABEL=`expr \( $LABEL + 1 \) % 4`
echo "Select Boot Label $LABEL"
case "$LABEL" in
0 ) ledctrl pwr=0 status=1 ;;
1 ) ledctrl pwr=0 status=10R,200 ;;
2 ) ledctrl pwr=0 status=10100000R,150 ;;
3 ) ledctrl pwr=0 status=1010100000R,150 ;;
esac
BTN=`select_btn $SELECT_DELAY`
done
- 第2段階 … /boot/bootld.confの探索
- fdisk -l で接続中のデバイスを捜査し、逆順で(デバイス番号の大きい方から)サーチ。
- /boot/bootld.confが見つかれば、上書き。
#----------------------------------------------
# 2. search /boot/bootld.conf
#----------------------------------------------
. /boot/bootld.conf
DEVLIST=$(fdisk -l /dev/sd? | awk '$1 ~ /^\/dev/ {print $1}' | sort -r )
for CDEV in $DEVLIST ; do
mount -r $CDEV /mnt || continue
if [ -r /mnt/boot/bootld.conf ] ; then
echo "/boot/bootld.conf found in $CDEV"
. /mnt/boot/bootld.conf
umount -l /mnt
break
fi
umount -l /mnt
done
- 第3段階 … カーネルの探索およびロード
- set_bootlabel を実行して ユーザが/boot/bootld.confで定義した$IMAGEと$APPENDを得る。
- $IMAGEをデバイス名$DEVとファイル名$KERNELに分解。
- $DEVが'http'であれば、カーネルを /tmp/zImage.tmp として wget。
- $DEVが'/dev/sd*'であれば、カーネルを /tmp/zImage.tmp としてコピー。
コピーする際、カーネル名が未定義であれば、/zImage /vmlinuz /boot/zImage /boot/vmlinuz の順にサーチしてコピー。
- レスキューモードが選択された場合は$DEVが未定義となるため、なにもせずにそのまま抜ける。
#----------------------------------------------
# 3. search and copy kernel
#----------------------------------------------
[ "$LABEL" != "0" ] && set_bootlabel $LABEL
IMAGE_="$IMAGE:"
KERNEL=$(echo $IMAGE_ | cut -d: -f2)
DEV=$(echo $IMAGE_ | cut -d: -f1)
rm -rf /tmp/zImage.tmp
case $DEV in
http )
echo "setup Network"
set_network
NETWORK=ON
wget $IMAGE -O /tmp/zImage.tmp
;;
/dev/sd* )
mkdir /mnt2
mount -r $DEV /mnt2 || break
if [ "$KERNEL" = "" ] ; then
for SIMG in /zImage /vmlinuz /boot/zImage /boot/vmlinuz ; do
if [ -e "/mnt2$SIMG" ] ; then
KERNEL=$SIMG
break
fi
done
fi
cp /mnt2$KERNEL /tmp/zImage.tmp
umount -l /mnt2
;;
esac
- 最終段階 … kexec
- カーネル(/tmp/zImage.tmp)があればkexec。
- なければレスキューモードに抜ける。
#----------------------------------------------
# 4. do kexec
#----------------------------------------------
if [ -e /tmp/zImage.tmp ] ; then
echo "Start Booting from Label $LABEL ..."
echo "IMAGE=$DEV:$KERNEL"
echo "APPEND=\"$APPEND\""
/bin/umount -a
exec /sbin/kexec -f --append="$APPEND" /tmp/zImage.tmp
else
echo "Start Booting from Rescue mode ..."
# Network
[ "$NETWORK" = "" ] && set_network
# Daemon
inetd
btnctrl /etc/btn_action.conf &
ledctrl pwr=0 status=1 buzzer=10
fi
exit 0
- 補足
インストール方法 †
- 補足事項
- PC-Linux上からはlilo.x86を使うと良いかも。ただし当方ではテストしていません。
- liloの書き込みはこの一回のみで金輪際不要です。lilo無しでも自在にカーネルを変更可能です。
- dont_touchディレクトリは読んで字のごとく、一切触らないことを推奨します。
- 個人メモ:lilo打ち込み方法、別の方法(-r オプションなし)
linear
boot=/dev/sda
disk=/dev/sda
bios=0x80
timeout=0
install=/mnt/boot/bootld.dont_touch/boot.b-selk
map=/mnt/boot/bootld.dont_touch/map
default=bootld
image=/mnt/boot/bootld.dont_touch/vmlinuz-initramfs-landisk.08
label=bootld
read-only
append="mem=64M usb-storage.delay_use=0 bootld=/etc/init.d/bootld"
# /mnt/boot/bootld.dont_touch/lilo -C /mnt/boot/bootld.dont_touch/lilo.conf
インストール方法 … 裏技版 -- bootld.mbr †
- (PC-)LINUXに中身が消去されても良いHDDを接続します。
USBストレージ経由で接続したHDDが、PC-Linux上で/dev/sdbと認識されたと仮定します。
- bootld.mbr
bootld_255h_63s.mbr.gzをダウンロードして、ターゲットディスクにddします。
bootldを含む約8MBのファーストパーティションが作成されます。
注意:MBRごと上書きしますので、ディスク上の元のデータは全て消えてなくなります(吸い出せなくなります)。
# gzip -d bootld_255h_63s.mbr.gz
# dd if=bootld_255h_63s.mbr of=/dev/sdb
# sync;sync;sync
以上で終了です。
- 確認
fdisk、または、マウントして中身を確認することができます。
なお、リブートするなどしてUSBストレージを再認識させる必要があるかもしれません。
(MBR毎強制書き換えを行ったため、OS内に保持されたdiskstat(情報)の一貫性が損なわれているかもしれませんので)
# fdisk -l /dev/sdb
Disk /dev/sdb: 40.9 GB, 40982151168 bytes
255 heads, 63 sectors/track, 4982 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device Boot Start End Blocks Id System
/dev/sdb1 1 1 8001 83 Linux
- 補足事項--作り方
- USBスティック内にfdiskで8MBのパーティションを作成。
- その中に、bootld関連ファイルをコピーして、liloを打ち込む。
- dd でMBRとパーティション毎吸い出して、bootld.mbrとする。
- 古いHDDにddで書き戻して、LANDISKに組み込み、起動することを確認した。
2.6最新カーネルを使って、LANDISKの2.4ベースオリジナルシステムをブートするこの矛盾……
comment †