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を引数として渡してからカーネルの本筋の処理へ移っていきます。
準備をしてから展開先のアドレスにジャンプ。
コメント