はじめに Edit

本ソフトウェアはKernelの切り替えを動的に行うプログラムです。

最近、2.6カーネルが動き出したり、SH-Linuxのホームページにおいて、
SH版 Fedora Core 2 互換パッケージ等が公開されてたりして、
SH-Linux周辺に新たな動きがあります。

それらを見て、私もカーネルを変更したくなりまして。。。
しかし、LANDISKの箱を開けたくなかったのと、システムを壊してしまうのが嫌だったので、
本プログラムの作成を思い立った次第です。

なお、本ソフトウェアは、mipsマシンに実装されているakmemや
2.6カーネルで実装されるといわれるkexec等の思想をそっくりそのまま頂きました。

ソフトウェアの構成と動作概略 Edit

  • ソフトウェアの構成
    本ソフトウェアは次の2つのパーツから構成されています。
    kernelsw.pl ・・・ フロントエンドスクリプト
    kernelsw.o ・・・ デバイスドライバモジュール

  • 動作概略
    kernelsw.plは、kernelsw.oへ、Kernel起動パラメータとKernelイメージの転送を行うPerlスクリプトです。
    kernelsw.oは上記スクリプトから送られてくるパラメータとカーネルイメージを内部メモリに取り込み、
    Linux起動時の初期領域へ再配置して、新Kernelの起動を行うデバイスドライバです。

    kernelsw.plのもう一つの機能は、 シャットダウンシーケンスの最終段階で、kernelsw.oを起動して
    カーネルのスイッチを行う仕掛けを提供することです。
    すなわち、/sbin/rebootや/sbin/haltのラッパーとして動作する機能です。
    本機能により、rebootやhaltに偽装したkernelsw.plは、内部の状態に応じて、
    本来のhaltコマンドを起動してシャットダウンシーケンスを行う、
    あるいは、kernelsw.oを呼び出してカーネルのスイッチ動作を行う、
    等の処理を適切に選択して実行します。

  • Ver 2.1の追加オプション
    追記:2005/06/11: 実は、kernelバージョンに係わらず本オプションは不要でした。
    2.6系カーネル対応機能として、カーネルのロードアドレスを指定するオプションを追加しました。
    offset_address=0x00800000 等。
    
    本オプションを指定しない場合、カーネルイメージは、LinuxSHのデフォルトアドレス、
    0x00210000へロードされます。

  • Ver 3.2の追加オプション
    • カーネルサイズの制限を2MB->16MBへ拡張
    • NetBSDカーネルのブート機能を追加
      offset_address=0x00001000   ←  0x1000を指定するとNetBSDモードになる。
      
    • NetBSDカーネルと共にRAMDISKをルートデバイスとして起動する機能を追加
      miniroot=./ramdisk.img      ← NetBSDモードでは、ルートデバイス用のRAMDISKを指定可能
      

インストール Edit

  • リマウント
    ルートファイルシステムを以下のコマンドで書き換え可能にします。
    # mount -o rw,remount /       # RWでリマウント
  • デバイスファイル/dev/kernelsw(c,52,0)の作成
    本デバイスドライバモジュールはキャラクタ型デバイスとして実装されており、
    /dev/kernelsw(c,52,0)を経由して情報のやりとりを行います。

    次のコマンドで/dev/kernelsw(c,52,0)の作成を行います。
    # mknod /dev/kernelsw c 52 0  # デバイスファイルの作成
    # chmod 600 /dev/kernelsw
  • kernelsw.oのコピー
    /lib/module...下へmiscディレクトリを作ってその中へkernelsw.oコピーします。
    # cd /lib/modules/2.4.21/kernel/drivers
    # mkdir misc
    # cp kernelsw.o /lib/modules/2.4.21/kernel/drivers/misc
  • reboot、haltコマンドの置き換え
    /sbin/rebootや、/sbin/haltは、busyboxへの相対リンクになっています。
    # cd /sbin
    # ls -la reboot halt
    lrwxrwxrwx   1 root     root           14 Mar 18  2004 halt -> ../bin/busybox
    lrwxrwxrwx   1 root     root           14 Mar 18  2004 reboot -> ../bin/busybox
    これらのコマンドをkernelsw.plで置き換えます。
    # cd /sbin
    # cp kernelsw.pl .                             /sbinへkernelsw.plをコピー
    # rm halt reboot                               rebootとhaltを削除
    # ln -s kernelsw.pl reboot                     rebootへリンク
    # ln -s kernelsw.pl halt                       haltへリンク
    これで、reboot、haltコマンドが呼ばれた時、kernelsw.plが実行されるようになりました。
    # ls -la reboot halt
    lrwxrwxrwx   1 root     root           11 Jan  5 14:57 halt -> kernelsw.pl
    lrwxrwxrwx   1 root     root           11 Jan  5 14:57 reboot -> kernelsw.pl
    次に先ほど削除したrebootとhaltコマンドを別の場所(/sbin/reboot_org/)へ作成します。
    ディレクトリが一つ深くなっているので、それにあわせて相対リンクします。
    # mkdir reboot_org
    # cd reboot_org
    # ln -s ../../bin/busybox reboot 
    # ln -s ../../bin/busybox halt
    これでインストールは完了です。
    ルートファイルシステムをリードオンリーに戻しておきます。
    # mount -o ro,remount /       # read-onlyでリマウント

コマンドオプション Edit

  • help
    まずは、ヘルプです。
    # kernelsw.pl -h
    Usage : kernelsw.pl [ OPTION | FILE ]
    
      ""          print current status
      FILE        kernel setup (ex. kernelsw.pl lilo.conf)
      -fswitch    force switch kernel
      -r          remove device driver and free allocate memory
      -h          help
    
    FILE format example :
    
     image=/boot/zImage
     root=/dev/hda1
     read-only
     append="mem=64M console=ttySC1,9600"
     offset_address=0x00210000
    
    more infomation :
    
     http://eggplant.ddo.jp/
    #
    ヘルプを含み5つのオプションが実装されています。
    引数は”なし”もしくは第一引数のみ有効です。
    コマンドオプションを順番に説明して行きます。

  • ステータス表示
    オプション無しでコマンドを実行すると現在のステータスを表示します。
    まだ何も設定されていないため、ほとんどの項目は0もしくは未定義値になっています。
    # kernelsw.pl
    ==============================================
            SH-Linux Kernel Switcher V2.1
    ----------------------------------------------
     image  =  (size:0,page:0)
     root   = Not defined (read/write)
     append = ""
     offset_address = 0
    ----------------------------------------------
     kernel boot parameter details
      0 : 0 # read/write root file system
      1 : 0 # RAMDISK Flags
      2 : 0 # Root device (Not defined)
      3 : 0 # Loader type (LILO = 1)
      4 : 0 # Initrd start
      5 : 0 # Initrd size
      6 : 0 # Not defined yet
      7 : 0 # Not defined yet
    ==============================================
    

  • Kernel起動パラメータとKernelイメージの設定
    lilo.confに準じたファイルを指定します。
    指定されたファイルの内容に従い、カーネルイメージと起動パラメータをメモリ内に読み込みます。
    最も重要なコマンドなので、次の節で詳しく説明します。

  • 新カーネルの強制起動
    "-fswitch"オプションを指定します。
    通常は使用しないでください。
    なぜなら、本オプションを指定すると即座にスイッチ動作が開始されるため、
    実行中のプロセスは、電源が突然落ちたに等しい状態になるからです。
    万が一使用する場合は、実行中のプロセスを可能な限り停止させ、
    さらに、ファイルシステムを可能な限りアンマウント、もしくは、リードオンリーでリマウントした
    状態で使用してください。

  • クリーンな状態への戻しかた
    "-r"オプションを指定します。
    確保されたメモリエリアを開放すると共に、ドライバをアンロードします。

  • 制限事項
    • カーネルサイズの上限は、2MB(2,097,152バイト)です。
      (Ver 3.2において、本制限は16MBまで拡張されました。)
    • SH4アーキテクチャのみ対応しています。(LANDISKアーキテクチャと言うべきかも)

Kernel Switcherの動作確認 Edit

  • 動作確認方法
    それでは実際に、
    /boot/zImage ... LANDISKオリジナルのカーネル
    /dev/hda1 ....LANDISKオリジナルのルートファイルシステム
    を用いてKernelのスイッチ動作を確認してみることにします。

    しかし、そのままではKernel Swicherが正常に動作したかどうかわからないので、
    起動パラメータを少しかえてみます。

  • オリジナルの起動パラメータ
    次のコマンドを実行するとカーネルの起動パラメータを確認できます。
    # cat /proc/cmdline
    # mem=64M console=ttySC1,9600
    これによると、メモリは64Mバイト、コンソールは/dev/ttySC1、その通信速度は9600bpsに設定されていることがわかります。

    それでは、メモリを1Mバイト減らしてみることにします。
    64Mバイトの現在の設定では、MemTotal=62800kBとなっています。
    # cat /proc/meminfo
            total:    used:    free:  shared: buffers:  cached:
    Mem:  64307200 62648320  1658880        0  1204224 40824832
    Swap: 575758336  8261632 567496704
    MemTotal:        62800 kB

  • lilo.confの準備
    カーネルのスイッチ動作を確認するために、わざと、appendのmemパラメータを63Mに設定してみます。
    lilo.confファイルは次のようになります。
    linear
    boot=/dev/hda
    disk=/dev/hda
    bios=0x80
    timeout=50
    default=linux
    image=/boot/zImage
            label=linux
            root=/dev/hda1
            read-only
    append="mem=63M console=ttySC1,9600"
    
    ※kernelsw.plは赤文字の部分だけを認識します。
    なお、マルチラベルには対応していないので、一つのラベルだけを記述するようにしてください。

  • Kernel起動パラメータとKernelイメージの設定
    準備したlilo.confファイルを指定して、kernlesw.plを実行します。
    # kernelsw.pl lilo.conf
    Loading .... done.
    ==============================================
            SH-Linux Kernel Switcher V2.1
    ----------------------------------------------
     image  = /boot/zImage (size:978185,page:239)
     root   = /dev/hda1 (read-only)
     append = "mem=63M console=ttySC1,9600"
     offset_address=0x00210000
    ----------------------------------------------
     kernel boot parameter details
      0 : 0x1 # read-only root file system
      1 : 0x0 # RAMDISK Flags
      2 : 0x301 # Root device (/dev/hda1)
      3 : 0x1 # Loader type (LILO = 1)
      4 : 0x0 # Initrd start
      5 : 0x0 # Initrd size
      6 : 0x0 # Not defined yet
      7 : 0x0 # Not defined yet
    ==============================================
    
    lilo.confファイルの指定に従い、カーネルイメージと起動パラメータがメモリ内に格納されました。

  • Kernel Switch
    この状態で、LANDISKの電源ボタンをOFFにすると、シャットダウン動作が開始されます。
    そして、その最終段階で、haltコマンド(実体はkernelsw.pl)が呼び出され、カーネルのスイッチが行われます。

    haltコマンド(実体はkernelsw.pl)が呼び出されると、恐らく1秒以内にLANDISKのLEDが全て消えます。
    これはカーネルローダの転送が終了したことを示しています。
    その後、赤いLEDが点灯します。この間、先ほど転送されたカーネルローダはカーネルの再配置を行っています。
    再配置が完了すると、赤と緑の両方のLEDが点灯して、新カーネルへ制御が移ります。

    ここからは、本プログラムの制御を離れ、カーネル自身のコードで動作します。
    カーネルは、通常圧縮された状態にあるので、自らその圧縮をとき、ブートシーケンスを開始します。
    カーネルは初期化後、ルートファイルシステムをマウントして、initプログラムを起動します。
    そして、そのinitから、芋ズル式に各プログラムが起動され、最終的にtelnet等からアクセスできるようになります。

  • カーネルスイッチ動作の確認
    カーネルのスイッチ動作を確認するため、telnetで接続後、起動パラメータと、MemTotalの値を確認してみます。
    正しく起動パラーメタが設定されており、カーネルスイッチが正常に行われたようです。
    # cat /proc/cmdline 
    mem=63M  console=ttySC1,9600
    # cat /proc/meminfo
            total:    used:    free:  shared: buffers:  cached:
    Mem:  63270912 54775808  8495104        0  1851392 32464896
    Swap: 575758336        0 575758336
    MemTotal:        61788 kB
    正しく動作確認できたなら、電源スイッチをON側に戻しておきましょう。

  • もとのLANDISKの環境へ
    電源スイッチをOFFにするとLANDISKはシャットダウン動作を開始して、 電源がOFFされます。
    再度電源をONすると、もとの64MBの状態に復帰します。

  • 次なる挑戦
    本Kernelsw.plを用いてプレーンなdebian環境を/dev/hda3上に構築したいと考えています。

参考文献 Edit

  • LinuxSH
    SH-IPL Sequence,LILO Sequence,LinuxSH Kernel Boot Sequence等、
    SH-Linuxの起動のシーケンスが非常に参考になりました。
  • SH Linux TIPS
    デバイスドライバの書き方を参考にさせて頂きました。
  • LANDISK - mizore Wiki
    唯一のデバッグ手段がLEDでしたので、その解析ネタが非常に参考になりました。
  • SH4のプログラミングマニュアル

人柱になっても良いという方へ Edit

非常に危険なプログラムですが、使ってみたいという方はどうぞ!
次の場所へソースを含め、一式置いておきます。
http://eggplant.ddo.jp/www/download/SH-Linux_Kernel_Switcher/

個人メモ Edit

  • /dev/hda5等から起動するには?
    • /dev/hda1〜4以外から起動させる場合は、下記吉藤さんのパッチが有効。
    • 他の方法として、apppendオプションで「root=/dev/hda5」等と明示的に指定する方法もある。
    吉藤さんから頂いたパッチ
    diff -ru kernelsw-2.1/kernelsw.pl kernelsw-2.1-dev/kernelsw.pl
    --- kernelsw-2.1/kernelsw.pl	2005-03-02 20:13:28.000000000 +0900
    +++ kernelsw-2.1-dev/kernelsw.pl	2005-06-09 01:43:35.183832360 +0900
    @@ -4,7 +4,11 @@
     # kernelsw.pl version 2.1
     #   SH-Linux Kernel switcher 
     #   http://eggplant.ddo.jp/
    -
    +#
    +# Changes:
    +# 	YOSHIFUJI Hideaki <yoshfuji at linux-ipv6.org>
    +# 		support more root devices
    +#
     
     $version           = '2.1';
     $real_reboot_dir   = '/sbin/reboot_org';
    @@ -20,11 +24,26 @@
     $max_image_size    = 512*4096;
     
     %dev_tbl=(
    -'/dev/hda1' => '0x301',
    -'/dev/hda2' => '0x302',
    -'/dev/hda3' => '0x303',
    -'/dev/hda4' => '0x304',
    -'Not defined' => '0x000',
    +    '/dev/hda' => 0x0300,
    +    '/dev/hdb' => 0x0340,
    +    #'/dev/hdc' => 0x1200,
    +    #'/dev/hdd' => 0x1240,
    +    #'/dev/hde' => 0x3100,
    +    #'/dev/hdf' => 0x3140,
    +    #'/dev/hdg' => 0x3200,
    +    #'/dev/hdh' => 0x3240,
    +    #'/dev/hdi' => 0x3800,
    +    #'/dev/hdj' => 0x3840,
    +    #'/dev/hdk' => 0x3900,
    +    #'/dev/hdl' => 0x3940,
    +    #'/dev/hdm' => 0x5800,
    +    #'/dev/hdn' => 0x5840,
    +    #'/dev/hdo' => 0x5900,
    +    #'/dev/hdp' => 0x5940,
    +    #'/dev/hdq' => 0x5a00,
    +    #'/dev/hdr' => 0x5a40,
    +    #'/dev/hds' => 0x5b00,
    +    #'/dev/hdt' => 0x5b40,
     );
     
     #-------------- device driver check ---------------
    @@ -136,9 +155,9 @@
         close(CONF);
     
         # root_dev check
    -    $root_dev=$dev_tbl{$root};
    +    $root_dev=&dev2rdev($root);
         if($root_dev eq ''){
    -	print "root = $root not suppurt !!\n";
    +	print "root = $root not supported !!\n";
     	exit;
         }
     
    @@ -166,6 +185,38 @@
         return 0;
     }
     
    +sub dev2rdev{
    +    my ($dev_file) = @_;
    +    my $minor = 0;
    +    my $rdev;
    +
    +    if($dev_file =~ s/(\d+)$//){
    +	$minor = $1;
    +    }
    +    if(defined($dev_tbl{$dev_file})){
    +	if($minor && $minor < 0x40){
    +	    $rdev = sprintf("%04x", $dev_tbl{$dev_file}+$minor);
    +	}
    +    }
    +    return $rdev;
    +}
    +
    +sub rdev2dev{
    +    my ($devno) = @_;
    
    +    my ($base, $offset) = (($devno & 0xff40), ($devno & 0x3f));
    +    my $dev = "Unknown";
    +    my $rdev;
    +    unless($devno & 0x80){
    +    	foreach $rdev (keys %dev_tbl){
    +	    if ($dev_tbl{$rdev} == $base){
    +		$dev = sprintf("%s%u", $rdev, $devno - $base);
    +		last;
    +	    }
    +	}
    +    }
    +    return $dev;
    +}
    +
     sub switch(){
         if($driver_installed == 1){
     	open(DEV,"+>$dev_file") || die "Can't open $dev_file. $!\n";
    @@ -227,11 +278,7 @@
     	($append) =/^.*\"(.*)\".*$/;
         }
     
    -    foreach $i (sort keys %dev_tbl){
    -	if(hex($dev_tbl{$i}) == hex($param[2])){
    -	    $root = $i;
    -	}
    -    }
    +    $root = &rdev2dev(hex($param[2]));
         
         if(hex($param[0]) == 1){
     	 $readonly_='read-only';
    
    Counter: 11369, today: 1, yesterday: 1

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2006-06-13 (火) 20:02:26 (6529d)