前回はハローワールドを表示するOSの起動まで行いました。今回はEDKⅡとメモリマップについてです。
前回の開発環境構築でedk2フォルダが作成されています。
用語整理
まずは用語を整理します。というかこの辺の用語全然わからない。私はノートパソコンのメモリ増設と、BTOデスクトップのSSD増設くらいしかしたことないにわかです。
UEFI
従来のBIOSでは古いよってことで従来のBIOSと代わるものとして採用されたそうですが、完全なBIOSの代替というわけではないようですが、新しいBIOSのような認識なのでしょう
ちなみに私のパソコンはBIOSモードがUEFIになってました

EDK2
UEFIとその周辺のプログラムを実装したオープンソースで、UEFIの開発にも、UEFIで動くアプリの開発にも使える”開発キット”だそうです。
BIOS
そもそもBIOSってのは、OSが起動する前にキーボードやマウス、CPUの管理制御を行うものだそうです。つまりハードウェアの管理と制御
ブートローダ
コンピュータを起動したときに、OSなどを動かすためのプログラム。ハードディスクにあるOSをメインメモリにロードする。
BIOSがブートローダを呼出し、ブートローダがOSを呼出すような動きです。
この書籍ではUSBメモリからメインメモリにOSをロードします。
メインメモリ
CPUが直接データを読み書きできるデータの一時保存場所
OS
今回の書籍で作るものですね。ユーザーやアプリケーションと、ハードウェアの中間に位置するもの。
マウスを動かすというインターフェースを提供するし、画面に表示するのようなインターフェースも提供するようなもの。かしらね。
OVMF
Open Virtual Machine Firmwareは仮想マシンでUEFI を使えるようにするプロジェクトです。
参考:OVMF による PCI パススルー
ブートローダの作成
用語を整理することでなんとなくやろうとしていることが理解できた。
まずEDK2の開発キット(ライブラリ)を使ってOSを作っていきます。
で、まずやることはOSをメインメモリにロードするためのブートローダを作成する。
まずは$HOME/workspaceを作ってそこにgitクローンする
https://github.com/uchan-nos/mikanos/tree/osbook_day02a

Cでメソッドを書くときにこのエントリーポイントのメソッドが開始メソッド(Javaでいうmainメソッド)になります。これはLoader.infに記載があります。つまりEDK2ではUEFIアプリごとにエントリポイントを自由に設定できる。
このクローンしてきたMain.Cと書籍に書いてあるMain.Cの全文が違うんだが・・・(このまま進めていきます。。。)
ビルドに必要な設定を参考書通りに行う
workspaceでチェックアウトする。。。え?いつ作ったんそれ?開発環境構築でクローンはしてきたけどworkspaceフォルダにやってないような・・・?
そもそもこれって開発環境じゃなくて、mikanos本体のリポジトリですよね?クローンしてくれなんてどこに記載があるんだろうか・・・Kindleの検索機能も使えないのがまじで辛い
ここで付録Bを見る
ここにいろいろ書いてありました。
MikanOSをビルドするには開発環境に加えてMikanOSそのもののソースコードも必要
MikanOSのソースコードはどこに配置してもOKで例としてworkspaceを利用します。→本はしっかり付録から読みましょう。。。???
気を取り直して付録通りにクローンしたら本章のday02に戻ってチェックアウトします。
ここでようやくMain.Cが参考書通りに記載されているのを確認。
edk2フォルダ(開発環境)に対して、$HOME/workspace/mikanos/MikanLoaderPkg(ソースコード)へのシンボリックリンクを張ります。
ln -s です。Windowsでいうところのショートカット作成のようなもの。
ls -lで確認

失敗すると赤で表示されます

Conf/target.txtの作成と、ビルドの設定、EDK2のビルドコマンドの実施まで行う。ビルドはズラーっと長文が流れます。
target.txtにてACTIVE_PLATFORM = MikanLoaderPkg/MikanLoaderPkg.dscとしていますので、先ほどのedk2に張ったシンボリックリンクが有効なら、ソースコードのほうを参照してくれるという感じです。
efiファイルができたのでエミュレータで起動してみます。
起動
前回やったのですが、ほとんど覚えてないので再確認します。
devenvのrun_qemu.shを起動したのだ。というわけで以下で実施
$HOME/osbook/devenv/run_qemu.sh Loader.efi

上手く動いてることを確認
メモリマップとは
メモリマップとはメインメモリのどの部分が何に使われているか記載した地図。つまりデータがメインメモリのどこにどのように記録されているのか。
https://www.kushiro-ct.ac.jp/yanagawa/pl2b-2018/01.html
メモリマップを取得するプログラム
とりあえずブランチ変えてビルドするだけでは意味ないのでソースを追ってみます。今回のソースは以下のソース
https://github.com/uchan-nos/mikanos/blob/osbook_day02b/MikanLoaderPkg/Main.c
さて、この章を読んで気が付いた。
「C言語がわからない」ドンッ!
とりあえずざっと読んで構造体とかポインタとかに目を通す。
https://www.tohoho-web.com/ex/c-lang.html
ポインタ
変数など宣言したときに、その変数のメモリ上のアドレスを取得するときに利用する。
変数aのポインタは int *b = &a;のようにすれば*bにはaのポインタが格納される。
値渡しと参照渡し
関数に引数を渡す際の渡し方。Javaではできないようなことですな。たしかオブジェクトを渡したときに参照の値渡しになるので引数のオブジェクトをメソッド内で再代入しても呼出し下では変化がない。(ojc.list = newlistはできる)
Cの場合は参照を渡せるのですね?
void hoge(int *x) { *x = 10 }
構造体(struct)
メンバ変数のみを定義できるclassのようなもの(ふむふむ)
型と変数名のセットを,で区切って定義する。
ポインタを返す関数
戻り値の型に*をつける
const CHAR16* GetMemoryTypeUnicode(EFI_MEMORY_TYPE type)
構造体の初期化
https://yu-nix.com/blog/2020/8/7/c-struct-init/
ようやくソースコードを見ます。。
とりあえず入り口であるUefiMainメソッドを見ると
CHAR8 memmap_buf[4096 * 4];
struct MemoryMap memmap = {sizeof(memmap_buf), memmap_buf, 0, 0, 0, 0};
GetMemoryMap(&memmap);
まずは構造体を初期化してメモリマップを取得するメソッドに構造体のポインタを渡します。
GetMemoryMapメソッドはgBSというブートサービスを表すグローバル変数です。とりあえずこれでメモリマップを取得でき、MemoryMapで指定されたメモリ領域に書き込みます。
あとはファイルへの保存とかみたいです。
早速ビルドしてみます。

次いでに作成されたdisc.imgをマウントして中身を見てみます

その後はポインタの説明で二章終わり。
ポインタ
以下のようなコードでポインタを少し理解する
#include <stdio.h>
int main(void)
{
// %pはポインタ %dはint
int i = 10;
printf("iの値 = %d \n", i);
printf("iのポインタ = %p \n", &i);
int* p = &i;
printf("iのポインタ変数p = %p \n", p);
int r = *p;
printf("iのポインタ変数pを代入したr = %d \n", r);
*p = 99;
printf("iのポインタ変数pに99を代入しました \n");
printf("iの値 = %d \n", i);
getchar();
return 0;
}
出力結果は以下です
iの値 = 10
iのポインタ = 0053FAB8
iのポインタ変数p = 0053FAB8
iのポインタ変数pを代入したr = 10
iのポインタ変数pに99を代入しました
iの値 = 99
・ポインタは整数と変換できます。
・ポインタのポインタを利用して値を変更することもテクニックとしてありよく使います。
サイトなど
https://osdev-jp.readthedocs.io/ja/latest/2017/create-uefi-app-with-edk2.html#
思ったこと
・時間かかりすぎているし理解度が低い。これって一通りやって何週かしたほうがいいのかな?
・私の読み方が悪い?どうやって手を動かすべきなのか散らばりすぎてる印象。→ようやくこの辺は落ち着いてきた。
・ここまではGithubでクローンとかするのはいいんだけど、なんか端折りすぎていて探すのに時間がかかった