Linux勉強 パート5 GRUB先頭のコードを読み出す

Linux

GRUBの先頭コードを読む

ソースコード

ここを参照しながら読んでみてください。
ソースコード

LBAとCHSモード

CHS

物理的なディスクの構造通りにセクターを指定します。
C(シリンダー)H(ヘッダー)S(セクター)
をそれぞれ指定し、例えばヘッダーは何番でシリンダーは何番で、セクターは何番というような感じです。

LBAモードは(Logical block addressing)はディスクの物理的に断続している構造を論理的に連続した数値として判断します。

処理スタート

前の投稿でBIOSにアクセスして利用できる機能について問い合わせました。
問い合わせの結果はレジスタに保存され元の処理へ復帰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)

キャリーフラグが立っていた場合とbxに0xaa55が保存されていなかった場合などサポートしているファンクション番号によってcxには違う値が保存されます。
andw $1, %cxで論理積をとってゼロフラクが立っていたとき、つまりこの場合cxの値が1以外の場合、LOCAL(chs_mode)へジャンプしてchsモードで処理を実行していきます。

上のチェックを素通りできた時だけ、LBAモードで処理を実行することができます。
lba_mode:で処理を実行する前提で進めていきます。
LBAモードでデータ構造体を読み込むDAP(ディスクアドレスポケット)のデータを作成します。

DAPはディスクの番号、読み込み先アドレスなどディスクからデータをプログラムを読み込むために利用されるデータ構造体です。

DAPにディスク読み込みに必要な値を保存する

siにはDAPのアドレスが設定されています。

movw %ax, 4(%si)
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
ディスクから読み込んだ内容を展開するアドレスを設定。

incw %ax
movb %al, -1(%si)
この2つの命令はLBAでデータを読み込んだ印を残しています。

movw %ax, 2(%si)
は読み込むセクタ数をを格納します。この時axには1が設定されています。
movw $0x0010, (%si)
アドレスの先頭にdapよサイズを格納。

movl kernel_sector, %ebx
movl %ebx, 8(%si)
movl kernel_sector + 4, %ebx
movl %ebx, 12(%si)
GRUBカーネルが保存されているセクター、読み込むセクターの数を保存

DAP(ディスクアドレスポケット)の構造

オフセット 内容 サイズ

オフセット内容サイズ  
0dapサイズ
10
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が起動され、残りの部分をメモリに読み込みますが、その前半部分を読み込みました。

プログラミングおすすめ書籍

おすすめ書籍

なぜプログラムが動くのか。
平易な言葉で学べますが、初心者には難しいかもしれません。
でも読み終わった頃にはなんとなく理解しているでしょう。
初心者には家にあって損はありません。

UNIXの思想を学べます。
高尚なタイトルですが、コードはおろか難しい言葉も殆どありません。取っ掛かりに最適でその後のプログラミングに対する理解を一変させます。
なぜならこの思想はプログラミングそのものだからです。
一読の価値ありで、これは良書なので今後紹介したいと思っています。

まだまだありますがまたの機会に紹介したいと思います。

Linux コンピューター
スポンサーリンク
himazinをフォローする
私の頭の上の消しゴム

コメント

タイトルとURLをコピーしました