* HDD系デバイスから起動しよう
-(by [[K]], 2009.04.01)
-発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)
----
-原理的にはここで紹介する方法で、内蔵・外付けHDD、USBメモリ、USB接続のカードリーダ、(最近のパソコンによく付いている)メモリカード端子などから起動できます。これらはBIOSからは同じように扱われているためです。ただしこの通りの対応ではない機種も存在し、その場合はこの方法ではうまく行かないかもしれません。
*** 注意
-COLOR(#FF0000){このページは未完成で、ツール群も準備できていないままです。このページは参考のために残しますが、質問は一切受け付けません。すみません。}
*** まえがき
-筆者は先日、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'' SIZE(10){2009-04-03 (金) 23:58:54}
-できるかどうかは試してみないと分かりません。 -- [[K]] SIZE(10){2009-04-05 (日) 00:16:10}
-pcctolってなんですか? -- ''イシハラシュウイチ'' SIZE(10){2018-03-07 (水) 02:27:24}
#comment