これらのキーワードがハイライトされています:USB

HDD系デバイスから起動しよう

  • (by K, 2009.04.01)
  • 発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)

  • 原理的にはここで紹介する方法で、内蔵・外付けHDD、USBメモリ、USB接続のカードリーダ、(最近のパソコンによく付いている)メモリカード端子などから起動できます。これらはBIOSからは同じように扱われているためです。ただしこの通りの対応ではない機種も存在し、その場合はこの方法ではうまく行かないかもしれません。

注意

  • このページは未完成で、ツール群も準備できていないままです。このページは参考のために残しますが、質問は一切受け付けません。すみません。

まえがき

  • 筆者は先日、ASUSのEeePC-900-XというネットブックPCを買いました。それでいろいろ遊んでいるうちに、このパソコンでも「はりぼてOS」で遊んでみたいと思いました。もちろんUSB-FDDをつければ起動はしますが、当然ながらかなり時間がかかります。またUSB-CDドライブをつければCDから起動もできますが、そういうかさばるものをつけるのはなんか気乗りがしません。・・・そしてこのPCにはSDカードスロットが標準で付いています。だからここにSDを入れて、そのSDから「はりぼてOS」が起動したらかっこいいだろうなあと思いました。ということで数日ほど試行錯誤したら結構簡単にできるようになったので、紹介しようと思います。
    • ちなみに電源ボタンを押してから11秒で起動するようになりました。WindowsXPの3倍は速いです(笑)。

方法

  • 普通HDD系デバイスにOSをインストールさせるには、以下の手順を踏みます。
    • パーティションを準備して、そこから起動できるように設定する。
    • パーティションのフォーマットを考える。
    • パーティションの中にどうにかしてOSに必要なファイルを書き込む。
    • パーティションがHDD内のどこに確保されるのかを前もって決めておくことはできないので(強引に決めておくこともできるけど、そうするとそのHDDを他のOSと共存させるのは難しくなる)、IPL等のプログラムは複雑になる。もしくはインストーラが複雑になる。
  • はっきりいって、これは全部面倒です。いきなりこういうのをやると理解できなくなるのは間違いないので、最初は簡単な構成を考えようと思います。最近すっかり安くなったUSBメモリやSDカードなどは、実はHDDと同じフォーマットをすることになっています。だからこれらを「容量が小さくて、万一失敗しても大損害にはならずに済んで、しかもパーティションが一つしかない(=パーティションで悩む必要のない)特別なHDD」だと思うことにしましょう。
  • FDからの起動を思い出してほしいのですが、結局CYLSは最大でも33くらいにしかできませんでした。つまりどんなに容量があっても当面は600KBくらいしか利用できないのです(将来的にはこの問題もクリアするとしても)。だから4GBとか32GBとかそんな大容量のものは全くいりません(とりあえず2MBくらいあれば十分です)。アクセススピードもBIOSを経由して起動する今回の方法では、あまり速くはならないみたいです(これも将来的には解決するかもしれないけど)。だからとにかく安物を準備しましょう。いきなりいいものを買ってきて失敗したらどうしますか。まずは練習用・実験用にできるだけ安いものを使いましょう。
  • 以下ではUSBメモリとして書きますが、実際はどのデバイスでも同じです。
  • ちなみにSDカードなどを扱う場合にカードリーダを買うことがあるかもしれません。その場合カードリーダは端子がたくさんある30in1みたいなタイプよりは、端子が一つしかなくてせいぜい2〜3種類くらいのメモリカードにしか対応していないようなカードリーダのほうが、今回の目的に限っては扱いやすいです(複数の端子を制御するタイプは、BIOSも扱いに苦労して、うまく行かないものがあったので・・・筆者の経験でしかないですが)。

  • USBメモリにはたいていFAT16かFAT32のフォーマットがかかっています。そしてここにOSやアプリケーションのファイルをポンポンいれておいて、あとはIPLががんばってそれらのファイルを見つけ出して起動するというのが普通です。しかしこれはとんでもなく大変です。IPLは基本的にはアセンブラで書かないといけないですし、しかも512バイトしかありません。そんな容量でFAT16かFAT32かも分からないフォーマットを自動判別し、クラスタサイズも512とは限らないので自動で判別するようなものを書けるでしょうか。いやまあ普通のOSではそれを何とかしてやっているわけですが、それは「入門」レベルをはるかに超えています。説明だけでやる気がなくなること請け合いです。
  • ということで、今回はもっと簡単な方法を考えました。まず今までどおりの方法でFD用のディスクイメージを作ります(余談ですがFDは容量が1440KBと決まっているので、説明するのがとても簡単だったのです)。そしてその1440KBのファイルを、たとえば64MBのUSBメモリがあったとして、その中にコピーします。これはimgtolとかを使うことなく、普通にコピーしてかまいません。・・・というのは、FDドライブであるA:に対してimgtolを使うことはできるのですが、それ以外のドライブに対してimgtolやその他のディスクイメージツールを使おうとすると、Windowsがエラーを出して妨害してしまうのです。だから結局入門用としては普通のファイルコピーをするしかない、というのが筆者の結論なのです。
  • これでとにかくUSBメモリのどこかにFDのときと同じ1440KBが書き込まれたはずです。あとはIPLでこの1440KBの先頭だけを探し出して、そしてその中を今までと同じ方法で読めばいいわけです。やったー、頭いい!
    • とまあそういう方法なので、この1440KBが連続したセクタに並んでいないと困ります。ファイルを書いたり消去したりを繰り返した状態だと、ファイルの断片化というのがおきて、もしかしたら1440KBが連続したセクタに書かれていないかもしれません(飛び飛びになっていて、どこに続きがあるのかはFATをたどらないと分からない)。そういうのは困ります。これを防ぐ一番簡単な方法は、一度USBメモリをフォーマットしなおすことです。これで全部のファイルが消えるので、ここに1440KBのファイルをコピーすれば、もちろんファイル領域の先頭から連続してきれいに書かれます。
    • とにかくこの1440KBさえ連続していればいいので、このファイルをコピーした後は、残った容量を好きなことに使ってかまいません。64MBのUSBメモリなら60MBくらいは残るでしょうから、そこに好きなファイルを入れてもいいわけです。

  • さて1440KBの先頭を探し出す方法ですが、これもものすごく簡単な方法を取ります。・・・普通は、MBRのパーティションテーブルがどうのこうので、そこからパーティションを見つけて、そして今度はそのパーティションのフォーマットがどうなっているか調べて、ファイル名情報がどこにあるかを探して・・・みたいな話になるのですが、こんなのができるのなら、今回の入門仕様にする必要が無いじゃないですか。
  • そうじゃなくて、1440KBの中に見つけやすい(そして誤解しにくい)シグネチャを書いておいて、USBメモリの中からそのシグネチャを探すんです。これはもちろんそれなりに時間がかかります。10秒とか20秒とかかかるかもしれません。でもその代わり、将来USBメモリがどんなフォーマットになってもこの手法は有効です。毎回起動するたびに探すのは起動時間がもったいないので、ディスク内でのFDイメージの位置が変わったときにだけやることにしましょう。だから起動時には探しません。探しておいた場所をいきなり読むのです。
    • 余談ですが、筆者はかつてこれとは違うアプローチを試みたことがあります。つまりUSBメモリのフォーマットがFAT12だったりFAT16だったりFAT32だったりしたのに腹を立てて、単純で統一的な一種類のフォーマットにそろえようとしたのです(SF16という名前にしました)。そうすればこの中にファイルを置いてもFDのときのように比較的簡単にアクセスできるわけです。・・・これはうまく行きましたが、今回の方法よりは複雑なので、ここではやっていません。

MBRのプログラム

  • USBメモリの場合、シリンダ番号0、ヘッド番号0、セクタ番号1のセクタのことを、ブートセクタとは言いません。IPLとも言いません。MBR(マスターブートレコード)といいます。なぜこんな風に言うのか、それは今は気にしないことにします。そして言い方は違うものの、結局この部分が起動時に0x7c00番地に読み込まれるということに変わりはありません。
  • MBRの場合も0x7dfeに 55 aa があります(これがないと起動しない)。しかもその上、0x7dbeから0x7dfdまでも使用禁止です。ここにはパーティション情報が書かれるためです。その代わり、FAT12とかディスクの名前とかを書かなくていいので、最初にジャンプ命令を書く必要はありません。いきなりプログラムを書けます。
  • また以下のプログラムでは、USBメモリを読み込むときに、シリンダやヘッド番号などを指定してはいません。基本的にFDDと同じようにもできるのですが、それをやるにはBIOSに最大シリンダ番号や最大ヘッド番号などを問い合わせなければいけません(USBメモリにはそもそもシリンダもヘッドもないのですが)。どうしてかというと、容量や製造会社によって、総シリンダ数などが違うせいです(FDDのときは80シリンダ・2ヘッド・18セクタと決まっていたのでとても簡単でした)。そしてどうにかして最大シリンダやなどを取得したとしても、実はその方法でアクセスできるのはUSBメモリの先頭から8GBまでで、残りの領域にはアクセスできません。・・・そこでこのプログラムではFDDの時とは異なるBIOSの機能番号を使ってアクセスしています。これはシリンダやヘッドは全部無視して、先頭から何番目かのセクタなのかだけを指定します。これをLBA(ロジカルブロックアドレス)方式といいます。この方法の場合、8796093022208GBまで指定できます。
    • 参考: OS-wikiのINT(0x13);のAH=0x42;
      [INSTRSET "i486p"]
      
              ORG     0x7c00
      
              STI     ; BIOSがSTIし忘れていても大丈夫なために
      
      ; MBR全体を0x0600〜0x07ffへコピー
      
              MOV     AX,0 
              MOV     SS,AX
              MOV     SP,0x7c00
              MOV     DS,AX
              MOV     SI,SP
              MOV     DI,0x600
              MOV     CX,512/2
      copyloop:
              MOV     AX,[SI]
              ADD     SI,2
              MOV     [DI],AX
              ADD     DI,2
              SUB     CX,1
              JNZ     copyloop
              JMP     0x0060:0x0027 ; コピーが終わったら、コピー先へジャンプ(0x0629)
      
              ORG     0x0027
      
      ; LBA方式が使えるかどうかの確認
      
              PUSH    CS
              POP     DS
              PUSH    CS
              POP     ES
              MOV     [drv],DL ; 起動ドライブ番号がDLに入っている(BIOSがDLに入れてからMBRを起動するので)
              CMP     DL,0x80
              JB      error    ; HDD系デバイスでなければエラー
              MOV     AH,0x41
              MOV     BX,0x55aa
              INT     0x13
              JC      error
              CMP     BX,0xaa55
              JNE     error
              TEST    CL,0x01
              JZ      error
      
      ; あらかじめ探しておいたIPLを読み込んで実行
      
              MOV     SP,0x0640
              MOV     DI,load_LBA
              MOV     CX,[load_secs]
              MOV     BX,[load_addr]
              CALL    0x60:0x01b0
              JC      error1
              JMP     FAR WORD [farjmp_vector]
      
      error:
              MOV     SI,msg
      putloop:
              MOV     AL,[SI]
              ADD     SI,1
              CMP     AL,0
              JZ      fin
              MOV     AH,0x0e
              MOV     BX,15
              INT     0x10    ; putc
              JMP     putloop
      fin:
              HLT
              JMP     fin
      
      error1:
              MOV     SI,msg1
              JMP     putloop
      
      read_LBA:       ; [ES:DI]からの8バイトに相対LBA, CXにセクタ数, BXにセグメント
      
              PUSH    DS
              PUSHAD
              MOV     AX,0x4200
      rw_LBA:
              PUSH    CS
              POP     DS
              MOV     SI,param
              MOV     [SI+6],BX
              MOV     EBP,[ES:DI+0]
              MOV     EDX,[ES:DI+4]
              ADD     EBP,[LBA0+0]
              ADC     EDX,[LBA0+4] ; ADCは直前の計算の繰り上がりも含めて足す命令
              MOV     [SI+8],EBP
              MOV     [SI+12],EDX
      .readloop:
              MOV     DL,[drv]
              PUSH    CX
              PUSH    AX
              INT     0x13
              POP     AX
              POP     CX
              JC      .error
              ADD     WORD [SI+6],0x20
              ADD     DWORD [SI+8],1
              ADC     DWORD [SI+12],0
              SUB     CX,1         ; CF == 0
              JNZ     .readloop
      .error:
              POPAD
              POP     DS
              RETF
      
      write_LBA:      ; [ES:DI]からの8バイトに相対LBA, CXにセクタ数, BXにセグメント
      
              PUSH   DS
              PUSHAD
              MOV    AX,0x4300
              JMP    rw_LBA
      
      drv:    DB     0x80
      msg:    DB     "ExtINT13H not ready",0x0d,0x0a,0
      msg1:   DB     "Load error",0x0d,0x0a,0
      
              RESB    0x0188-$
      load_secs:
              DW      0 ; IPLのセクタ数(1より大きくてもよい、pcctolで自動設定)
      load_addr:
              DW      0 ; IPLのロード番地(0x07c0以外でもよい、ppctolで自動設定)
      farjmp_vector:
              DW      0,0 ; IPLの実行開始位置(0:0x7c00以外でもよい、pcctolで自動設定)
      param:
              DW      0x10,1,0,0
              DD      0,0
      load_LBA:
              DD      0,0 ; IPLの相対LBA(0以外でもよい、pcctolで自動設定)
      LBA0:
              DD      0,0 ; ディスクイメージ開始のLBA(pcctolで自動設定)
      
              RESB    0x01b0-$
              JMP     read_LBA
              RESB    0x01b4-$
              JMP     write_LBA
              RESB    0x01b8-$
              DB      "KHBH",0,0
      
  • 説明:
  • このMBRではIPLがディスクイメージの先頭から始まっているとは決めていません。ディスクイメージのどこから始まっていてもいいのです。またIPLが512バイト(1セクタ)だとも決めていません。5KB(10セクタ)でも50KB(100セクタ)でもいいのです。読み込む番地も0x7c00とは決めていません。どこだっていいのです。
  • なんでこうしたのかといえば、IPLが512バイト以下でなければいけないとか、先頭に書かなくてはいけないとか、0x7c00に読み込まれるとか決まっているせいで、OSを書くのが大変になったからです(みなさんもそう思いますよね?ね?)。IBMのおじさん(おばさん?)たちが勝手に決めた不便なルールを引き継ぐ必要はないわけです。だから自由にできるようにしました。
  • 最初にMBRを0x0600〜0x07ffへ転送しているのは、0x00800〜0x9ffffを全部あきにするためです。こうすれば、きっとOSは使いやすいでしょう。
  • (つづく:個人的に忙しいので続きは数週間後?)

関連リンク

  • このページを作るまでの試行錯誤:
    • http://wiki.osask.jp/?KHBIOS.memo001
    • 試行錯誤を見たらかえって混乱するかもしれないので、お勧めはしません。
    • とりあえず「はりぼてOS」とOSAkkieの起動には完全に成功しています(起動だけではなく、アプリももちろん使えています。2009.04.01時点で)。

こめんと欄

  • USB メモリーは起動が可能なことではなければならないし, BIOS 設定を別にすると起動が可能だと聞いたが他の方法でも USB メモリーに起動することができるんですか? -- Kor_Lee_Hee_Rak 2009-04-03 (金) 23:58:54
  • できるかどうかは試してみないと分かりません。 -- K 2009-04-05 (日) 00:16:10
  • pcctolってなんですか? -- イシハラシュウイチ 2018-03-07 (水) 02:27:24

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2018-03-07 (水) 02:27:24 (537d)