BOOT loader LanDisk Edit

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」にて、大体完成しているので、ここでは、ブートセレクタ部と、ローダ部の作成(説明)を行うことにします。

ブートセレクタの仕様 Edit

  • 機能仕様
    • ブートセレクトの数は4個(ユーザが使えるラベルは3個で、残り1個はレスキュー用)あれば十分かな?
    • 電源スイッチのトグル(ON/OFF)回数でブートラベルを指定。
    • 現在のブートラベルは赤LEDの点滅パターンを見ればわかるようにしたい。
    • 6秒間、スイッチがトグルしなかった場合、現時点のブートラベルでもってブート開始。
  • LEDの仕様
    • ブートラベル1…LED点滅
    • ブートラベル2…LED2回点滅→休止→LED2回点滅→休止→繰り返し
    • ブートラベル3…LED3回点滅→休止→LED3回点滅→休止→繰り返し
    • ブートラベル0…LED常時点灯(レスキュー用途)
  • スイッチの仕様
    • 初期状態はブートラベル1。
    • トグル動作が行われる毎に、ブートラベル2→ブートラベル3→ブートラベル0→ブートラベル1→繰り返し

ローダ部の仕様 Edit

  • カーネルとブートパラメータの指定方法
    • /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
      }
      

実装 Edit

  • /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
      
  • 補足
    • fdisk -l /dev/sd?
      USBストレージデバイスが認識されたり、されなかったり、状態が不安定で困っていたのですが、fdiskでパーティション情報をリストすることで、問題が一気に解決しました。
      どうやら、fdiskは、未認識のデバイス(scsi、usb-strage)を初期化する?効果があるようです。
      ただし、本来のやり方と違うと思うので、正当なやり方をご存知の方は、ぜひ、教えてください。m(_ _)m

    • $CDEV
      Current Deviceの略で/boot/bootld.confを読み込んだデバイス名、/dev/sdb1 等がセットされる。
      デバイス番号が実行時に確定するようなデバイス(例えばリムバーブルメディア)内に/boot/bootld.confを置き、かつそのデバイス内のカーネルやルートファイルシステムを読み出す場合に、set_bootlabelの記述に本変数を使うと幸せになれるかも。
      	    IMAGE=$CDEV:/vmlinuz
      	    APPEND="mem=64M console=ttySC1,9600 root=$CDEV"
      

インストール方法 Edit

  • ファイル一式
    bootld-20070617.tgz
    失敗しても泣かない方のみ、ダウンロードしてお試しください。無保証です。

  • /dev/sda1(/dev/hda1)へインストールを行う
    /dev/sdaを自分の環境にあわせて読み替えること。
    例えば、PC-Linuxを使ってインストールする場合は、そのディスクが割り当てられたデバイスを正しく把握して読み替えること。
    # mount /dev/sda1 /mnt
    # tar vxfz bootld-20070617.tgz -C /mnt
    ./boot
    ./boot/bootld.conf
    ./boot/bootld.dont_touch
    ./boot/bootld.dont_touch/lilo
    ./boot/bootld.dont_touch/boot.b-selk
    ./boot/bootld.dont_touch/lilo.conf
    ./boot/bootld.dont_touch/vmlinuz-initramfs-landisk.08
    ./boot/bootld.dont_touch/lilo.x86
    
  • liloでbootldを起動
    • /mnt/bootld.dont_touch/lilo.conf の編集
      • timeoutを0にする。
      • /dev/sdaを各自の環境に合わせてよみかえること。
      • bootld/dont_touchの実体がdiskで指定したデバイス内に存在することを確認すること。
        linear
        boot=/dev/sda
        disk=/dev/sda
        bios=0x80
        timeout=0
        install=/boot/bootld.dont_touch/boot.b-selk
        map=/boot/bootld.dont_touch/map
        default=bootld
        image=/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"
        
        usb-storage.delay_use=0 : USBストレージを即座に認識させるためのオマジナイ。
    • lilo
      # /mnt/boot/bootld.dont_touch/lilo -r /mnt -C /boot/bootld.dont_touch/lilo.conf
      Added bootld *
      
  • 補足事項
    • 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 Edit

  • (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 Edit

選択肢 投票
動いた 6  
動かなかった 0  
動かなかったに投票したけど、なんとかして動かした 0  
bootld.mbrを試した。起動成功 3  
bootld.mbrを試した。起動失敗 0  

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-01-14 (日) 09:28:29 (2457d)