Linux勉強 パート10 GRUBコード GRUBカーネル

Linux

GRUBカーネル突入

ソースコード

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

GRUBカーネルの実行

前回の記事でA20マスクの機能を停止させる処理を行ったので今回はその続きです。
GRUBの後半部分は圧縮されているのでそれを展開する処理を行います。
最初のソースコードを参照してください。
リアルモードからプロテクトモードへの移行やA20マスクなどを勉強していましたが、本筋の処理に戻って続きからです。

LOCAL (codestart):
	cli		
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %ss
	movw	%ax, %es
	movl	$GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
	movl	%ebp, %esp
	sti		
	ADDR32	movb	%dl, LOCAL(boot_drive)
	int	$0x13
	DATA32	call real_to_prot
	.code32
	incl	%eax
	cld
	call	grub_gate_a20
	movl	LOCAL(compressed_size), %edx
#ifdef __APPLE__
	addl    $decompressor_end, %edx
	subl    $(LOCAL(reed_solomon_part)), %edx
#else
	addl    $(LOCAL(decompressor_end) - LOCAL(reed_solomon_part)), %edx
#endif
	movl    reed_solomon_redundancy, %ecx
	leal    LOCAL(reed_solomon_part), %eax
	cld
	call    EXT_C (grub_reed_solomon_recover)
	jmp	post_reed_solomon
#include "../../../kern/i386/realmode.S"
#include <rs_decoder.S>

cliで割り込みを停止させてセグメントレジスタをリセット。

A20マスクをセットしてプロテクトモードへ移行。

movl LOCAL(compressed_size), %edx
で展開する大きさをedxレジスタへセット。
以下はまたマクロを使いコンパイルする処理をOSによって分岐させます。
376行目のdecompressor_endのラベルはこのコードの最後尾を表します。
このラベルの後ろに展開するGRUBのカーネルがあるのでそのアドレスををレジスタに保存します。

誤り訂正

誤り訂正とはデジタルデータを保存したり読み込む過程で入り込んでしまうもので、致命的なエラーを引き起こしかねないものです。
必要なデータだけ送り込んでしまうと欠損してしまった場合にもうそのデータを復元することができません。
そこでデータに誤り訂正符号という冗長性を持たせます。
こうすることで欠損したデータを復元できる可能性を持たせています。

例え話をします。
1234というデータを読み込みたいとして、それが欠損して1○34のようになったとします。
これでは2度のこのデータは復元できません。
なのでその後ろに1234 1234という風に冗長なデータを持たせます。
そうすると1○34という風になっても後ろにあるデータを照合して復元できます。
実際のやり方は節約のためにそんなに簡単ではありません。
複雑な計算により導きだします。

リード・ソロモン符号

誤り訂正符号として使われるものらしいですが、とても難しいです。
数学的な理解も必要で時間がかかりそうで現状説明することができません。
なので雰囲気だけ。

addl $(LOCAL(decompressor_end) – LOCAL(reed_solomon_part)), %edxとあるのでファイルの終端アドレスからreed_solomon_partの開始アドレスを引いた値をedxの値に加えて保存しています。
誤り訂正を行う処理を展開するファイルに追加しています。
この高度な計算で圧縮して小さくしているんだと思います。

時間があるときに勉強してみたいと思いますが、いつになるやら…

movl	LOCAL(compressed_size), %edx
340行目までジャンプします。
post_reed_solomon:
#ifdef ENABLE_LZMA
movl	$GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, %edi
#ifdef __APPLE__
movl	$decompressor_end, %esi
#else
movl	$LOCAL(decompressor_end), %esi
#endif
pushl	%edi
movl	LOCAL (uncompressed_size), %ecx
leal	(%edi, %ecx), %ebx
push 	%ecx
call	_LzmaDecodeA
pop	%ecx
popl	%esi
movl	LOCAL(boot_dev), %edx
movl	$prot_to_real, %edi
movl	$real_to_prot, %ecx
movl	$LOCAL(realidt), %eax
jmp	*%esi

esiにセットした展開先のアドレスと未展開の値のアドレスをスタックへ退避。
必要な値を引数としてレジスタにセットした後はソースコードの118行目のjmp命令で340行目のpost_reed_solomon:へジャンプします。
LzmaDecodeAを呼び出してGRUBカーネルの展開処理を実行。メモリの0x100000に読み出されます。

prot_to_real、real_to_protのアドレス、IDTのアドレスをレジスタに保存しています。
プロテクトモードではBIOSのサービスを利用できません。
必要に応じてプロテクトモードとリアルモードを切り替える必要があるのでそのアドレスとIDTを引数として渡してからカーネルの本筋の処理へ移っていきます。

準備をしてから展開先のアドレスにジャンプ。

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

おすすめ書籍

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

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

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

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

コメント

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