Linux勉強 パート5 GRUB前半 コードを別のメモリアドレスへ移し替える

Linux

GRUB先頭コードを読み込む

ソースコード

ここを参照しながら読んでみてください。
MBRコード
ソースコード
GRUB前半コード
ソースコード
MBRに読み出されるファイルはdiskboot.Sというファイルです。
2セクター目に配置されています。

GRUB前半部分

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
popw	%ds
popa
jmp	*(kernel_address)

メモリへコピーする処理は終わっているので、次はpopw %dsで退避しておいたGURBカーネルが読み出されたアドレスを取り出します。
pop命令はスタックの先頭に積んでいるデータを取り出す命令です。
popaで残りのレジスタを復元してjmp *(kernel_address)でGURBカーネルのあるアドレスへ移動します。
ここからソースコードがGURB前半へ
2つ目のソースコード変わりますので注意してください。

GURUBコードスタート

ファイルが変わります

.code16
	.globl	start, _start
start:
_start:

pushw	%dx
pushw	%si

MSG(notification_string)

popw	%si
movw	$LOCAL(firstlist), %di
movl	(%di), %ebp

LOCAL(bootloop):
cmpw	$0, 8(%di)
je	LOCAL(bootit)
LOCAL(setup_sectors):
cmpb	$0, -1(%si)
je	LOCAL(chs_mode)
movl	(%di), %ebx
movl	4(%di), %ecx

引き続き16ビットで動いています。
pushw %dxはドライブ番号があるので退避。
MSG(notification_string)で真っ黒なディスプレイに文字を表示します。

firstlistには残りのセクターを読み込むための開始セクター、残っているセクターの情報を保存します。
bootloopルーチンで使用され、セクターを読み込む度に更新していきます。

必要な処理を完了したら、bootloopへそのまま処理が移ります。

8(%di)には残りのセクターが入っています。
cmpw $0, 8(%di)で0と比較し0なら読み込むセクター数は0、つまり完了したということなので、bootitへジャンプしてループ処理を抜けます。

再びDAP

オフセット内容サイズ  
0dapサイズ
10
2セクタ数
4セグメントオフセット
6
セグメント
8セクタ開始位置

DAPは上記の表のような構造体で大容量HDDが普及したことによりCHS方式で表せるサイズに収まらなくなりました。

シリンダー:10bit
ヘッド:8bit
セクタ:6bit
の24ビットでは収まり切らないので、代わりにメモリを使いたい、その先頭アドレスをレジスタにこのDAPのアドレスを渡します。
DAPには上の表の情報が保存されているので、広大なメモリにアクセスすることができるようになりました。

GRUB続き

コードの続き
boot_loopから処理をsetup_sectorsルーチンへ移します。

LOCAL(setup_sectors):
cmpb	$0, -1(%si)
je	LOCAL(chs_mode)
movl	(%di), %ebx
movl	4(%di), %ecx

xorl	%eax, %eax
movb	$0x7f, %al
cmpw	%ax, 8(%di)
jg	1f
movw	8(%di), %ax

1:
subw	%ax, 8(%di)
addl	%eax, (%di)
adcl	$0, 4(%di)
movw	$0x0010, (%si)
movw	%ax, 2(%si)
movl	%ebx, 8(%si)
movl	%ecx, 12(%si)
movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
pushw	%ax
movw	$0, 4(%si)

movb	$0x42, %ah
int	$0x13
jc	LOCAL(read_error)
movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
jmp	LOCAL(copy_buffer)

cmpb $0, -1(%si)はLBAで読み込まれたのかCHSで読み込まれたのかの印がされています。
0だった場合はCHSなのでchs_modeに飛ばして処理を続行します。
LBAの場合、
movl (%di), %ebx
movl 4(%di), %ecx
でセクターの開始位置を取得。

Phoenix製のBIOSは最大の読込みが128なので、128を超えていないかいちいちチェックします。
残りのセクター数と128を比較して128を超えていなければ、axの値をそのまま使用し続行。
超えていれば次の命令、movw 8(%di), %axを飛ばして1:へジャンプ。

sub命令でfirstlistに保存されている残りのセクター数から128を引いて、残った値を現在の開始セクターに加算。
これは次のループのための処理です。
こうすると2回目のループで使用する開始セクターを設定できます。
adc命令は桁あふれを処理する命令です。

一通り処理が終われば次のセクターを読み込んでいきます。

movb $0x42, %ah
int $0x13
でBIOSルーチンを呼び出す準備をしてintで割り込み命令、処理を呼び出します。
読み込みが成功していれば、GRUB_BOOT_MACHINE_BUFFER_SEGのアドレスをbxに設定してもう一度copy_bufferへジャンプ、コードを大きなメモリ空間へ移し替える処理を実行します。

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

おすすめ書籍

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

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

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

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

コメント

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