Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
104
Help us understand the problem. What is going on with this article?
@yamoridon

Mac で始める「ゼロからのOS自作入門」

Mac で「ゼロからのOS自作入門」の「6.4 ポーリングでマウス入力」まで進められたので、ひとまずそこまでの手順をメモとして残しておきます。この環境構築方法で本の最後まで進められるかどうかはまだわかりません。

環境は macOS BigSur(11.2.3), CPU は Intel でも Apple Silicon でも OK です。Homebrew はすでにインストールされているものとします。

PATH にいろいろ追加していきますが、適宜 .zshrc 等の PATH の設定にも追加してください。

EDK II の準備

後でインストールする binutils がすでにインストールされ PATH に追加されていると失敗するので注意してください。

$ cd $HOME
$ git clone https://github.com/tianocore/edk2.git
$ cd edk2
$ git checkout 38c8be123aced4cc8ad5c7e0da9121a181b94251
$ git submodule init
$ git submodule update
$ cd BaseTools/Source/C
$ make

mikanos-build リポジトリの準備

$ cd $HOME
$ git clone https://github.com/uchan-nos/mikanos-build.git osbook
$ cd osbook/devenv
$ curl -L https://github.com/uchan-nos/mikanos-build/releases/download/v2.0/x86_64-elf.tar.gz | tar xz
$ cd ..

次の内容の mac.patch というファイルを作って適用します。

--- a/devenv/make_image.sh
+++ b/devenv/make_image.sh
@@ -23,11 +23,24 @@ qemu-img create -f raw $DISK_IMG 200M
 mkfs.fat -n 'MIKAN OS' -s 2 -f 2 -R 32 -F 32 $DISK_IMG

 $DEVENV_DIR/mount_image.sh $DISK_IMG $MOUNT_POINT
-sudo mkdir -p $MOUNT_POINT/EFI/BOOT
-sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+if [ `uname` = 'Darwin' ]; then
+    mkdir -p $MOUNT_POINT/EFI/BOOT
+    cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+else
+    sudo mkdir -p $MOUNT_POINT/EFI/BOOT
+    sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
+fi
 if [ "$ANOTHER_FILE" != "" ]
 then
-    sudo cp $ANOTHER_FILE $MOUNT_POINT/
+    if [ `uname` = 'Darwin' ]; then
+        cp $ANOTHER_FILE $MOUNT_POINT/
+    else
+        sudo cp $ANOTHER_FILE $MOUNT_POINT/
+    fi
 fi
 sleep 0.5
-sudo umount $MOUNT_POINT
+if [ `uname` = 'Darwin' ]; then
+    hdiutil detach $MOUNT_POINT
+else
+    sudo umount $MOUNT_POINT
+fi
diff --git a/devenv/mount_image.sh b/devenv/mount_image.sh
index ba8233e..aea4d7d 100755
--- a/devenv/mount_image.sh
+++ b/devenv/mount_image.sh
@@ -16,5 +16,9 @@ then
     exit 1
 fi

-mkdir -p $MOUNT_POINT
-sudo mount -o loop $DISK_IMG $MOUNT_POINT
+if [ `uname` = 'Darwin' ]; then
+    hdiutil attach -mountpoint $MOUNT_POINT $DISK_IMG
+else
+    mkdir -p $MOUNT_POINT
+    sudo mount -o loop $DISK_IMG $MOUNT_POINT
+fi
$ patch -p1 < mac.patch

QEMU のインストール

Intel Mac の場合

Intel Mac の場合は Homebrew でインストールします。

$ brew install qemu

Apple Silicon Mac の場合

Apple Silicon Mac の場合、2021 年 3 月末の時点では Homebrew でインストールされるバージョン (5.2.0) の QEMU はちゃんとうごきません1。ソースにパッチ2を当ててビルドする必要があります。

まず、QEMU のビルドに必要なツール、ライブラリをインストールします。

$ brew install libffi gettext glib pkg-config autoconf automake pixman ninja

それから QEMU をビルドします。

$ cd $HOME
$ git clone https://git.qemu.org/git/qemu.git
$ cd qemu
$ git checkout 9950da284fa5e2ea9ab57d87e05b693fb60c79ce -b work
$ curl https://patchew.org/QEMU/161280769492.2878.8851519112088854609.malone@chaenomeles.canonical.com/mbox | git am --3way
$ mkdir build
$ cd build
$ ../configure --prefix=/opt/qemu --target-list=x86_64-softmmu --enable-cocoa
$ make -j 8
$ sudo make install
$ export PATH=/opt/qemu/bin:$PATH

qemu-system-x86_64 を実行してみて次のように表示されればインストール成功です。

$ qemu-system-x86_64 --version
QEMU emulator version 5.2.50 (v5.2.0-3303-g712479ce00)
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers

LLVM のインストール

本では LLVM7 を使っていますが、Mac で EDK II を使うためには LLVM9 以上が必要です。homebrewで最新版(2021 年 5 月時点で 12)をつかうことにします。

$ brew install llvm

# Intel Mac の場合
$ export PATH=/usr/local/opt/llvm/bin:$PATH

# Apple Silicon Mac の場合
$ export PATH=/opt/homebrew/opt/llvm/bin:$PATH

その他のビルドに必要なパッケージのインストール

$ brew install nasm dosfstools binutils

# Intel Mac の場合
$ export PATH=/usr/local/opt/binutils/bin:$PATH

# Apple Silicon Mac の場合
$ export PATH=/opt/homebrew/sbin:/opt/homebrew/opt/binutils/bin:$PATH

「2.2 EDK II でハローワールド」をビルドする

$ cd $HOME
$ mkdir workspace
$ cd workspace
$ git clone https://github.com/uchan-nos/mikanos.git
$ cd mikanos
$ git checkout osbook_day02a
$ cd $HOME/edk2
$ ln -s $HOME/workspace/mikanos/MikanLoaderPkg .
$ source edksetup.sh

Conf/target.txt を開き次のように変更します。TOOL_CHAIN_TAG が本とは異なるので注意してください。CLANG38 ツールチェインは Linux でしか使えないため代わりに CLANGPDB ツールチェインを使用します。

設定項目 設定値
ACTIVE_PLATFORM MikanLoaderPkg/MikanLoaderPkg.dsc
TARGET DEBUG
TARGET_ARCH X64
TOOL_CHAIN_TAG CLANGPDB

変更を保存したらビルドします。

$ build

$HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi というファイルが作られれば成功です。

QEMU で実行

次のようにして QEMU を実行します。

$ $HOME/osbook/devenv/run_qemu.sh $HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi

うまくいくと QEMU が起動し、さっきビルドしたプログラムが実行されるはずです。

スクリーンショット 2021-03-23 23.45.51.png

「3.5 カーネルからピクセルを描く」の補足

『「2.2 EDK II でハローワールド」をビルドする』で TOOL_CHAIN_TAG に本で指定している CLANG38 ではなく、CLANGPDB を指定しました。CLANG38 ツールチェインは System V AMD64 ABI を使用しますが、CLANGPDB ツールチェインは Microsoft x64 ABI を使用します。(ABI については本の「コラム 4.1 ABI」を参照してださい。)このため System V AMD64 ABI で引数が渡ってくることを想定しているカーネルの KernelMain に引数を渡せません。そこで本のリスト 3.8 の

typedef void EntryPointType(UINT64, UINT64);

typedef void __attribute__((sysv_abi)) EntryPointType(UINT64, UINT64);

にすると、強制的に System V AMD64 ABI でカーネルを呼び出すことができ、引数を渡せます。

本とは異なり「LLVMのインストール」で LLVM の最新版を使うようにしました。LLVM 10 以降では -z separate-code というオプションをつけてリンクしないと本書のブートローダでロードすることができません。(「ゼロからのOS自作入門」 03日目 を参考にさせていただきました。ありがとうございます。)

本の

$ ld.lld $LDFLAGS --entry KernelMain -z norelro --image-base 0x100000 --static -o kernel.elf main.o

の代わりに

$ ld.lld $LDFLAGS --entry=KernelMain -z norelro --image-base 0x100000 --static -o kernel.elf -z separate-code main.o

とします。

「4.1 make 入門」の補足

Makefile の LDFLAGS の行を変更し、-z separate-code をつけてリンクするようにします。

LDFLAGS += --entry KernelMain -z norelro --image-base 0x100000 --static -z separate-code

「5.2 分割コンパイル」の補足

Makefile で sed を使うようになりますが、Linux の sed と Mac の sed はコマンドラインオプションが異なります。sed を使う行は

    sed -I '' -e 's|$(notdir $(OBJ))|$(OBJ)|' $@

にする必要があります。

また本とは使用するツールチェインが異なるためだと思いますが、 make を実行すると

ld.lld: error: undefined symbol: __cxa_pure_virtual
>>> referenced by graphics.cpp
>>>               graphics.o:(vtable for PixelWriter)
make: *** [kernel.elf] Error 1

というエラーが出るようになります。完全仮想関数を呼び出そうとしてしまった場合に呼ばれるエラーハンドラの実装を提供する必要があるそうです。(OS 自作に便利な C++ の機能 10 選(前編))

main.cpp に次のような実装を書いておくとビルドできるようになります。

extern "C" void __cxa_pure_virtual() {
  while (1) __asm__("hlt");
}
104
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
yamoridon
たびのプヨグヤマ

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
104
Help us understand the problem. What is going on with this article?