最近、Macで「ゼロからのOS自作入門」という本でOS開発を始めたのですが、他の方の記事では第1章のバイナリコードの実行方法が載っていなかったのでここに残しておきます。(他の章についても残すかもしれません)
まだ第2章の途中なので、このやり方で最後までできるかは分からないのと気ままにやっていくので本の最後まで終わるか分かりません。
ちなみに実行環境はM2 MacでMacOS Sequoia(15.6.1)です。Intel Macで動作するかは分かりません。
(私の環境ではこの記事に載せたやり方で問題なく実行できたことを確認していますが、ChatGPTとClaude Sonnet4に手伝ってもらって作ったものなので、もっと安全に実行できるやり方があるかもしれません。)
※ 第2章の途中で書いており、思い出しながら書いているため書くのを忘れた手順があるかもしれません。
参考記事
OS自作入門日記#1 M1 Macに「ゼロからのOS自作入門」の環境導入
【ゼロから作るOS自作入門】M1 MacでOSを作り、Goで実装できるようになるまで日誌。
第1章
ここでの目標
dockerとか使わずにqemuで動かしたい
準備
ファイルの作成
実行の前に実行するBOOTX64.EFIがないと話にならないので、バイナリエディタで作成します。私はhex-fiendを使用しましたが作成できれば何でも良いと思います。
手作業で作成するのが面倒な方は以下の公式GitHubからダウンロードしてください。
ここからダウンロード
ファイルはお好きなところにフォルダを作成して置いておいてください。
実行環境の準備
とりあえずHomebrewが入っていない場合は入れてください。
実行に必要なものを一気にインストールします。(私の場合、mtoolsは途中で入れましたが、多分これで問題なくできると思います。)LLVM20.1.8を使っていましたが(動作した)、LLVM14を使う方が確実だと思います。
brew install dosfstools qemu llvm nasm binutils mtools
それぞれが何なのかは調べて欲しいです。
次にLLVMとbinutilsのpathを通します。
export PATH=/opt/homebrew/opt/llvm/bin:$PATH
export PATH=/opt/homebrew/sbin:/opt/homebrew/opt/binutils/bin:$PATH
(第1章の時点で確か必要だったはず、もしかしたら第2章↓)
Mac で始める「ゼロからのOS自作入門」を参考に「ゼロからのOS自作入門」公式が用意しているmikanos-buildをクローンしてosbookとして$HOMEに置いて、Mac用に変更を加えます。以下のリンクから行けるGitHubのページにもクローンのコマンドが載っています。(Gitはインストールしてください)
mikanos-build
ここでは
- クローン
- ELF用のコンパイラの導入
- mac.patchを作成して反映させる
をしています。
実行
BOOTX64.EFIを置いたフォルダにcdコマンドで移動してください。
以下のコマンドで実行できるはずです。
# ディスクイメージ作成
qemu-img create -f raw disk.img 200M
mkfs.fat -n 'MIKAN OS' disk.img
mmd -i disk.img ::/EFI
mmd -i disk.img ::/EFI/BOOT
mcopy -i disk.img BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
# 以下2つのコマンドは実行しなくても良いですが、ここまで上手くいっていればエラーは出ない
mdir -i disk.img ::
mdir -i disk.img ::/EFI/BOOT
# QEMU実行
qemu-system-x86_64 \
-drive if=pflash,file=$HOME/osbook/devenv/OVMF_CODE.fd \
-drive if=pflash,file=$HOME/osbook/devenv/OVMF_VARS.fd \
-hda disk.img
実行できなかったら、環境構築で失敗していると思うので色々試して頑張ってください。
第2章
EDK Ⅱでハローワールド
Mac で始める「ゼロからのOS自作入門」の『「2.2 EDK II でハローワールド」をビルドする』までを行ってください。
※ MikanLoaderPkg/MikanLoaderPkg.dscの変更を忘れるとビルドできないので注意してください。(見落としがち)
以下の手順で実行します。.cファイルをEFIファイルに変換する手順が増えただけで、後半は第1章と代わりません。
(追記)
run_qemu.shの存在に気付かず、やってしまった
次のパートでrun_qemu.shを使っているので、そちらを参照してください...
# 1. オブジェクトファイルにコンパイル
clang -target x86_64-pc-win32-coff \
-mno-red-zone -fno-stack-protector -fshort-wchar -Wall -c hello.c
# 2. EFIファイルにリンク
lld-link /subsystem:efi_application /entry:EfiMain /out:hello.efi hello.o
# 3. BOOTX64.EFIとしてコピー
cp hello.efi BOOTX64.EFI
# 4. ディスクイメージ作成・更新
qemu-img create -f raw disk.img 200M
mkfs.fat -n 'MIKAN OS' disk.img
mmd -i disk.img ::/EFI
mmd -i disk.img ::/EFI/BOOT
mcopy -i disk.img BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
# 以下2つのコマンドは実行しなくても良いですが、ここまで上手くいっていればエラーは出ない
mdir -i disk.img ::
mdir -i disk.img ::/EFI/BOOT
# 5. QEMU実行
qemu-system-x86_64 \
-drive if=pflash,file=$HOME/osbook/devenv/OVMF_CODE.fd \
-drive if=pflash,file=$HOME/osbook/devenv/OVMF_VARS.fd \
-hda disk.img
メモリマップの確認
の通りに
$HOME/osbook/devenv/run_qemu.sh $HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi
を実行したら
error 4000: Instance of library class [RegisterFilterLib] is not found
mount: You must specify a filesystem type with -t.
というエラーが発生したので、ChatGPTと頑張って解決しました。(例によって、私ではなくChatGPTがやってくれました)
修正箇所
#@range_begin(defines)
[Defines]
PLATFORM_NAME = MikanLoaderPkg
PLATFORM_GUID = d3f11f4e-71e9-11e8-a7e1-33fd4f7d5a3e
PLATFORM_VERSION = 0.1
DSC_SPECIFICATION = 0x00010005
OUTPUT_DIRECTORY = Build/MikanLoader$(ARCH)
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
#@range_end(defines)
#@range_begin(library_classes)
[LibraryClasses]
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
#@range_end(library_classes)
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
#@range_begin(components)
[Components]
MikanLoaderPkg/Loader.inf
#@range_end(components)
#!/bin/sh -ex
if [ $# -lt 2 ]
then
echo "Usage: $0 <image name> <mount point>"
exit 1
fi
DEVENV_DIR=$(dirname "$0")
DISK_IMG=$1
MOUNT_POINT=$2
if [ ! -f $DISK_IMG ]
then
echo "No such file: $DISK_IMG"
exit 1
fi
- mkdir -p $MOUNT_POINT
- sudo mount -o loop $DISK_IMG $MOUNT_POINT
+ mkdir -p "$MOUNT_POINT"
+ # raw イメージをマウントしてデバイス名を取得
+ DEV=$(hdiutil attach "$DISK_IMG" -nomount | head -n1 | + awk '{print $1}')
+ # マウントポイント作成
+ sudo mkdir -p "$MOUNT_POINT"
+ # FAT32 でマウント
+ sudo mount -t msdos "$DEV" "$MOUNT_POINT"
これでできました。
次に上記の変更でどこかのディレクトリでqemuを実行して、できたdisk.imgをマウントしてmemmapを確認しますが、P61のマウントのコマンドでは動かないので私が使用したコマンドを載せます。
mkdir -p mnt # マウントポイント作成
DEV=$(hdiutil attach -nomount disk.img | head -n1 | awk '{print $1}') # デバイス名取得
sudo mount -t msdos $DEV mnt # FAT32 としてマウント
ls mnt # 中身を確認
catコマンドのところは同じです。