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
オフセット | 内容 | サイズ | ||
---|---|---|---|---|
0 | dapサイズ | |||
1 | 0 | |||
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へジャンプ、コードを大きなメモリ空間へ移し替える処理を実行します。
コメント