GRUBの先頭コードを読む
ソースコード
ここを参照しながら読んでみてください。
ソースコード
LBAとCHSモード
CHS
物理的なディスクの構造通りにセクターを指定します。 LBAモードは(Logical block addressing)はディスクの物理的に断続している構造を論理的に連続した数値として判断します。 前の投稿でBIOSにアクセスして利用できる機能について問い合わせました。 キャリーフラグが立っていた場合とbxに0xaa55が保存されていなかった場合などサポートしているファンクション番号によってcxには違う値が保存されます。 上のチェックを素通りできた時だけ、LBAモードで処理を実行することができます。 DAPはディスクの番号、読み込み先アドレスなどディスクからデータをプログラムを読み込むために利用されるデータ構造体です。 siにはDAPのアドレスが設定されています。 movw %ax, 4(%si) incw %ax movw %ax, 2(%si) movl kernel_sector, %ebx オフセット 内容 サイズ
C(シリンダー)H(ヘッダー)S(セクター)
をそれぞれ指定し、例えばヘッダーは何番でシリンダーは何番で、セクターは何番というような感じです。
処理スタート
問い合わせの結果はレジスタに保存され元の処理へ復帰sれます。
なのでまずはその値をチェックすることからスタート。
jc LOCAL(chs_mode)
cmpw $0xaa55, %bx
jne LOCAL(chs_mode)
andw $1, %cx
jz LOCAL(chs_mode)
lba_mode:
xorw %ax, %ax
movw %ax, 4(%si)
incw %ax
movb %al, -1(%si)
movw %ax, 2(%si)
movw $0x0010, (%si)
movl kernel_sector, %ebx
movl %ebx, 8(%si)
movl kernel_sector + 4, %ebx
movl %ebx, 12(%si)
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
andw $1, %cxで論理積をとってゼロフラクが立っていたとき、つまりこの場合cxの値が1以外の場合、LOCAL(chs_mode)へジャンプしてchsモードで処理を実行していきます。
lba_mode:で処理を実行する前提で進めていきます。
LBAモードでデータ構造体を読み込むDAP(ディスクアドレスポケット)のデータを作成します。DAPにディスク読み込みに必要な値を保存する
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
ディスクから読み込んだ内容を展開するアドレスを設定。
movb %al, -1(%si)
この2つの命令はLBAでデータを読み込んだ印を残しています。
は読み込むセクタ数をを格納します。この時axには1が設定されています。
movw $0x0010, (%si)
アドレスの先頭にdapよサイズを格納。
movl %ebx, 8(%si)
movl kernel_sector + 4, %ebx
movl %ebx, 12(%si)
GRUBカーネルが保存されているセクター、読み込むセクターの数を保存DAP(ディスクアドレスポケット)の構造
オフセット 内容 サイズ
0 dapサイズ
1 0
2 セクタ数
4 セグメントオフセット
6
セグメント
8 セクタ開始位置
lba_modeから復帰してディスクの読み込み処理へ復帰
lba_modeルーチンでDAPの設定を済ませました。
movb $0x42, %ah int $0x13 jc LOCAL(chs_mode) movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx jmp LOCAL(copy_buffer)
ファンクション番号をahに設定してディスクから読み出すBIOSのルーチンを呼び出します。
失敗された場合CF(キャリーフラグ)が設定されます。
失敗していた場合はCHSモードで実行し直すためにLOCAL(chs_mode)へジャンプします。
問題なければbxにディスクから読み込んだ内容のコピー先アドレスを設定してcoppy_bufferへジャンプ。
MBRがGRUB先頭を読み出す
ここからはcopy_bufferのラベルの名前が示す通りMBRが読み込んだ内容のコピーを作ります。
0x7000にMBRでOSカーネルを呼び出すGRUBの先頭を設置します。
MBR→GURB先頭→GRUB後半という順番で読みんでいきますが、今はMBRがGRUBを読み込みを完了した段階です。
ややこしいので混乱しそうですが、MBRはブートローダーのブートローダーのような感じですね。
そして一旦0x7000読み込んだデータをx08000から続く広大なメモリ領域へ移します。
最初から0x08000へ読み込めばいいだろって感じですよね。
何故こんなめんどくさいことをするかと言うと、昔のコンピュータはメモリが今ほど巨大ではなかったのでx08000というアドレスが存在しませんでした。
なので古いモデルにも存在していた0x7000に一旦読み込まないといけないんです。
要は古いモデルのコードをそのまま使う互換性のためです。
面倒ですが、またも互換性というやつの登場です。
copy_bufferルーチンの処理
メモリに読みだしたディスクの内容を別のメモリアドレスへ移動させます。
LOCAL(copy_buffer): pusha pushw %ds movw $0x100, %cx movw %bx, %ds xorw %si, %si movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di movw %si, %es cld rep movsw
pusha、はpush allの略だと思ってください。複数のレジスタの内容一気にをスタックへ保存します。
pushaで保存できないdsレジスタもあるので、別の命令を使って一緒に保存します。
cxにはループ回数100を設定。rep命令で後程使います。
bxのこのファンクションにジャンプする前に設定したセグメントの値0x7c000をdsに設定しています。
siを0でクリア。
diに0x8000を設定し、esにコピーします。
repは次に続くmovswをcxの回数繰り返します。
movswはds:siのセグメントの値をes:diで指定したアドレスへ2倍とづつコピーします。
dfフラグが0の時はコピーしたバイト数だけcxデクリメント(減らす)されます。
2バイトづつ256回コピーされるので
2×256(16進数で0x100)=512バイト(1セクター分)が0x8000以降にコピーされます。
最初にMBRが起動され、残りの部分をメモリに読み込みますが、その前半部分を読み込みました。
コメント