Mikan 本の環境構築

先日の edk2 のエントリもそうだが、Mikan本 を今さらやっているところです。

本の通りに環境構築すれば何も難しいことはないのですが、フォルダ構成を好みな状態に変更するために行ったよもやまの記録です。。

環境構築で躓いたので3週間位経っているのに未だ 3 日目、4 日目あたりをうろうろしているがようやくスピードアップできそうだ。

Mikan 本からの変更点

  • edk2 は最新版(詳細は、前回のエントリ)
  • edk2 フォルダの配置先
  • day01 とかの日付で作業フォルダを区切っていく
  • devenv フォルダのスクリプトを makefile に直接書いていく

Mikan 本では、edk2 を ホームディレクトリ(~) に置いているのですが、自分としてはあまり好みでないのでプロジェクトフォルダ内に配置するようにした。

というのも、 github から取ってきた os のソースファイルは checkout して作業時点の日付を切り替えるようになっている。このスタイルだととても変更作業がやりにくいのでローカルにスナップショットを残す形にしようと思う。

前回(前日)分の作業内容は残して参照しながら進めたいので、日付で作業フォルダを区切ってその中からビルドできるようにしていく。それにともなって MikanLoader も日付フォルダの中に入れ込むことにした。

devenv フォルダのスクリプトの分割のしかたが自分にはどうしてもすっと入ってこないので、いっそ makefile に直接書いていくことにした。

まとめるとプロジェクトフォルダはこんなイメージになる。

  • [プロジェクトフォルダのトップ]
    • env フォルダ ひとまず、 .fd だけ置いている。
    • edk2 フォルダ
    • day03 のような日付フォルダ
      • MikanLoader03Pkg フォルダ
      • … その他

ちょうど kernel フォルダが新たに登場したのでそのうち追加しようと思う。後ろの章に進むとフォントファイルや画像リソース、newLib が追加されるようだが、それはまたその時考える。ことにする。

MikanLoaderPkg

Mikan 本では、UEFI で動く bootloader を MikanLoader と呼んでいるが、本の中では、プロジェクトフォルダの MikanLoaderPkg フォルダを edk2 フォルダ内にリンクすることで edk2 のビルドを実行するようにしている。

今回は本と異なり、day03 等の日付名のフォルダで作業をしたいので MikanLoader03Pkg のように日付の数字を埋め込んで名前の衝突を回避することにした。

それに伴い、 Mikanloarder の MikanLoader.dsc を書き換える必要がある。

6 行目: ビルド出力先を指定し直している。
23 行目: 最新の edk2 に対応するために追加。
26 行目: 使用する Loader.inf を指定している。

[Defines]
PLATFORM_NAME           = MikanLoaderPkg
PLATFORM_GUID           = 1b6bbd8c-686c-11ef-b861-b30cfa469952
PLATFORM_VERSION        = 0.2
DSC_SPECIFICATION       = 0x00010005
OUTPUT_DIRECTORY        = Build/MikanLoader03$(ARCH)
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS           = DEBUG|RELEASE|NOOPT

[LibraryClasses]
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf

BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf

[Components]
MikanLoader03Pkg/Loader.inf

[Components] 節は注意が必要だ。どうも Components で指定されたものがパッケージに含まれるようなしくみらしく、MikanLoader.dsc が他のフォルダの Loader.inf を読み込んでもビルドは問題なく通る。
この場合だとビルドされるソースは他のフォルダのものが MikanLoader03Pkg として生成される。

はじめこのことに気づかず、もともの MikanLoaderPkg の挙動になってしまい原因究明に時間がかかった。

また、Loader.inf も最新の edks2 を使うために変更している。RegisterFilterLib が必要なので追加した。

[Defines]
INF_VERSION     = 0x00010006
BASE_NAME       = Loader
FILE_GUID       = c9d0d202-71e9-11e8-9e52-cfbfd0063fbf
MODULE_TYPE     = UEFI_APPLICATION
VERSION_STRING  = 0.2
ENTRY_POINT     = UefiMain

# VALID_ARCHITECTURES = X64

[Sources]
Main.c

[Packages]
MdePkg/MdePkg.dec

[LibraryClasses]
UefiLib
UefiApplicationEntryPoint
RegisterFilterLib

[Guids]
gEfiFileInfoGuid

[Protocols]
gEfiLoadedImageProtocolGuid
gEfiLoadFileProtocolGuid
gEfiSimpleFileSystemProtocolGuid

makefile

本で進められる 環境では、 devenv フォルダに必要なスクリプトが全部入っている形になっている。が、makefile に書き直している。読み取って書き直す時間はかかるが、この方が自分には何をやっているか理解できるのであえてそうしている。

3日目の時点で使用できる makefile はこんな感じになる。

PWD 		:=$(shell pwd)
MNT_POINT	:=mnt
DISK_IMG	    :=disk.img
EDK_DIR		:=../edk2
EDK_OUT		:=Build/MikanLoader03X64/DEBUG_CLANGDWARF/X64/Loader.efi
ENV_DIR		:=../env  # 本でいうところの devenv フォルダ、ここに .fd がある。

CXXFLAGS	:=-O2 -Wall -g --target=x86_64 -ffreestanding -mno-red-zone -fno-exceptions -std=c++17
LDFLAGS		:=--entry KernelMain -z norelro --image-base 0x100000 --static


.PHONY: help
help:
	@echo "avilable targets"
	@echo "loader: build bootloader with edk2"
	@echo "kernel: build kernel"
	@echo "image: build bootddisk image"
	@echo "run: boot diskimage on QEMU"
	@echo "help: display this message"


# source edk2/edk-setup.sh はエラーになるので、予め実行する必要があるかも。
# ここの挙動はいまいちよくわかっていない。
.PHONY: loader
loader:
	ln -sf $(PWD)/MikanLoader03Pkg ../edk2
	cd $(EDK_DIR) && \
	# source edksetup.sh && \
	build -p MikanLoader03Pkg/MikanLoaderPkg.dsc && \
	cd $(PWD)

.PHONY: kernel
kernel:
	@echo "building kernel..."
	clang++ $(CXXFLAGS) -c main.cpp 
	ld.lld $(LDFLAGS) -o kernel.elf main.o

.PHONY: image
image: 
	mkdir -p $(MNT_POINT)
	sudo umount $(MNT_POINT) || :
	qemu-img create -f raw $(DISK_IMG) 200M
	mkfs.fat -n MIKAN -s 2 -f 2 -R 32 -F 32 $(DISK_IMG)
	sudo mount -o loop $(DISK_IMG) $(MNT_POINT)
	sudo mkdir -p $(MNT_POINT)/EFI/BOOT
	sudo cp $(EDK_DIR)/$(EDK_OUT) $(MNT_POINT)/EFI/BOOT/BOOTX64.EFI
	sudo cp kernel.elf $(MNT_POINT)/
	sleep 0.5
	# sudo umount $(MNT_POINT)


.PHONY: run
run:
	@echo "launch bootdisk on QEMU!!"
	qemu-system-x86_64 -m 1G \
	-drive if=pflash,format=raw,readonly=on,file=$(ENV_DIR)/OVMF_CODE.fd \
	-drive if=pflash,format=raw,file=$(ENV_DIR)/OVMF_VARS.fd \
	-drive if=ide,index=0,media=disk,format=raw,file=$(DISK_IMG) \
	-device nec-usb-xhci,id=xhci \
	-device usb-mouse -device usb-kbd \
	-monitor stdio

.PHONY: clean
clean:
	@echo "do cleaning"
	rm kernel.elf
	rm -rf $(EDK_DIR)/Build/MikanLoader03X64

使用するには、ひとつずつ手で実行していく必要がある。

; boot loader のビルド
$ make loader

;  kernel のビルド
$ make kernel

; イメージファイルの生成
$ make image

; qemu で実行
$ make run

この makefile は全然だめだめなのはわかっているが、ここからすこしずつ直していこうと思う。

ぼちぼち続けていきます。