「ゼロからのOS自作入門」 02日目

参考文献

こちらの記事を参考にいたしました! 良記事ありがとうございます。


試行錯誤メモ

前回は、01 日めということで M1 Mac にビルドツール(llvm 等)をインストールして、C 言語で書かれた UEFI プログラムを実行できるようにしました。

本日は、UEFI の SDK である「EDK II」を導入していきたいと思います。

EDK II とは何者なのか

EDK II のインストール

Github からリポジトリをクローンしてビルドします。

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

# (※不確かなメモ※)
# VSCode で MikanOS のソースを見たときにヘッダーファイルが見つからない警告が出るのが
# 私の精神衛生上良くないので、こんな感じでインクルードパスを指定した。
$ vim ~/.zshrc
export CPATH=$CPATH:$HOME/Code/edk2/MdePkg/Include
export CPATH=$CPATH:$HOME/Code/edk2/MdePkg/Include/X64

MikanLoaderPkg のビルド

EDK II を使って MikanLoaderPkg (day02a) をビルドする。 (私は書籍と違うディレクトリにソースを展開しているのでその辺りはうまく読み替える必要がある)

# OS プロジェクトのタグを切り替えておく
$ cd ~/Code/mikanos
$ git checkout dosbook_day02a

# EDK にプロジェクトのシンボリックリンクを張る
$ cd ~/Code/edk2
$ ln -s ~/Code/mikanos/MikanLoaderPkg .

# こんな感じになるはず
$ ls -l
 :
lrwxr-xr-x   1 roca  staff     39  3 27 10:56 MikanLoaderPkg -> /Users/roca/Code/mikanos/MikanLoaderPkg
 :

# ビルド前の準備(ツールチェーンの指定は後半のテーブルを参照)
$ source edksetup.sh
$ vim ./Conf/target.txt

# ビルド!!
$ build
$ ls -l ./Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi
-rw-r--r--  1 roca  staff  6160  3 27 11:24 ./Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi

Mac 上で開発をする場合、ツールチェインの指定が書籍と異なるので注意。

ちなみに、TOOL_CHAIN_TAG とはビルドに使用するコンパイラなどの指定のことらしい。 今の私の環境だと具体的には macOS@11.2.3 / llvm@11.1.0 なので、「CLANGPDB」を指定するのが正解みたい。

設定項目設定値
ACTIVE_PLATFORMMikanLoaderPkg/MikanLoaderPkg.dsc
TARGETDEBUG
TARGET_ARCHX64
TOOL_CHAIN_TAGCLANGPDB
(./Conf/tools_def.txt 抜粋)

#   CLANG38  -Linux-  Requires:
#                             Clang v3.8, LLVMgold plugin and GNU binutils 2.26 targeting x86_64-linux-gnu, aarch64-linux-gnu or arm-linux-gnueabi
#                             Clang v3.9 or later, LLVMgold plugin and GNU binutils 2.28 targeting x86_64-linux-gnu, aarch64-linux-gnu or arm-linux-gnueabi
#                        Optional:
#                             Required to build platforms or ACPI tables:
#                               Intel(r) ACPI Compiler from
#                               https://acpica.org/downloads
#   CLANGPDB -Linux, Windows, Mac-  Requires:
#                             Clang 9 or above from http://releases.llvm.org/
#                        Optional:
#                             Required to compile nasm source:
#                               nasm compiler from
#                               NASM -- http://www.nasm.us/
#                             Required to build platforms or ACPI tables:
#                               Intel(r) ACPI Compiler from
#                               https://acpica.org/downloads

QEMU で動作確認

あとは例によって QEMU で動作確認していきます。

いつもどおり、起動ディスクイメージ(disk.img)を作成してもいいですし、ローカルディレクトリを起動ディスクに見せかけて起動してもいいかもしれない。

# なんか適当な場所で。
$ mkdir -p ./DISK/EFI/BOOT/
$ cp $HOME/Code/edk2/Build/MikanLoaderX64/DEBUG_CLANGPDB/X64/Loader.efi ./DISK/EFI/BOOT/

# 起動!
$ qemu-system-x86_64 \
  -drive if=pflash,file=$HOME/Code/mikanos-build/devenv/OVMF_CODE.fd \
  -drive if=pflash,file=$HOME/Code/mikanos-build/devenv/OVMF_VARS.fd \
  -hda fat:rw:./DISK

おぉー。

boot


(2021/03/30 追記: 以下、day02b について。)

UEFI でのメモリマップの取得

  • メインメモリのどんな部分がどんな用途で使われているかが記載されている
  • 基本的には物理開始メモリアドレス / 用途(Type) / 利用ページ数で構成される。
  • x86_64 の場合、ページサイズは 4KiB / 2MiB / 4MiB / 1GiB 単位で設定できる。
  • メモリマップの配置には常に連続しているわけではない
    (ページサイズ x ページ数が次の物理開始アドレスにならない場合がある)

関数仕様

EFI_STATUS gBS->GetMemoryMap (
  IN OUT UINTN *MemoryMapSize,
  OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
  OUT UINTN *MapKey,
  OUT UINTN *DescriptorSize,
  OUT UINT32 *DescriptorVersion
);
引数用途
MemoryMapSizeUINTN*(入力)メモリマップ書き込み用のメモリ領域のサイズ
(出力) 実際のメモリマップの大きさ
MemoryMapEFI_MEMORY_DESCRIPTER*実際のメモリマップが書き込まれたあとのアドレス
MapKeyUINTN*メモリマップを識別するための値。メモリマップに変化があったら値が変わる。
DescriptorSizeUINTN*メモリディスクリプタのサイズ。
DescriptorVersionUINT32メモリディスクリプタのバージョン。

メモリディスクリプタ構造体(=メモリ利用の実態を表すモノ)

typedef struct {
  ///
  /// Type of the memory region.
  /// Type EFI_MEMORY_TYPE is defined in the
  /// AllocatePages() function description.
  ///
  UINT32                Type;
  ///
  /// Physical address of the first byte in the memory region. PhysicalStart must be
  /// aligned on a 4 KiB boundary, and must not be above 0xfffffffffffff000. Type
  /// EFI_PHYSICAL_ADDRESS is defined in the AllocatePages() function description
  ///
  EFI_PHYSICAL_ADDRESS  PhysicalStart;
  ///
  /// Virtual address of the first byte in the memory region.
  /// VirtualStart must be aligned on a 4 KiB boundary,
  /// and must not be above 0xfffffffffffff000.
  ///
  EFI_VIRTUAL_ADDRESS   VirtualStart;
  ///
  /// NumberOfPagesNumber of 4 KiB pages in the memory region.
  /// NumberOfPages must not be 0, and must not be any value
  /// that would represent a memory page with a start address,
  /// either physical or virtual, above 0xfffffffffffff000.
  ///
  UINT64                NumberOfPages;
  ///
  /// Attributes of the memory region that describe the bit mask of capabilities
  /// for that memory region, and not necessarily the current settings for that
  /// memory region.
  ///
  UINT64                Attribute;
} EFI_MEMORY_DESCRIPTOR;
フィールド名説明
TypeUINT32メモリ領域の種別
PhysicalStartEFI_PHYSICAL_ADDRESSメモリ領域先頭の物理メモリアドレス
VirtualStartEFI_VIRTUAL_ADDRESSメモリ領域先頭の仮想メモリアドレス
NumberOfPageUINT64メモリ領域の大きさ(4KiB ページ単位)
AttributeUINT64メモリ領域が使える用途を示すビット集合

QEMUで確認

# OSのソース準備
$ cd ~/Code/mikanos
$ git checkout osbook_day02b

# EDK II 実行準備
$ cd ~/Code/edk2
$ source ./edksetup.sh
$ build

# 起動ディスクイメージの作成(省略)

# QEMU実行
$ qemu-system-x86_64 \
  -drive if=pflash,file=$HOME/Code/mikanos-build/devenv/OVMF_CODE.fd \
  -drive if=pflash,file=$HOME/Code/mikanos-build/devenv/OVMF_VARS.fd \
  -hda ./disk.img

QEMU 実行画面の様子。

day02b

取得できた mmap の内容。

Index, Type, Type(name), PhysicalStart, NumberOfPages, Attribute
0, 3, EfiBootServicesCode, 00000000, 1, F
1, 7, EfiConventionalMemory, 00001000, 9F, F
2, 7, EfiConventionalMemory, 00100000, 700, F
3, A, EfiACPIMemoryNVS, 00800000, 8, F
4, 7, EfiConventionalMemory, 00808000, 8, F
5, A, EfiACPIMemoryNVS, 00810000, F0, F
6, 4, EfiBootServicesData, 00900000, B00, F
7, 7, EfiConventionalMemory, 01400000, 2B36, F
8, 4, EfiBootServicesData, 03F36000, 20, F
9, 7, EfiConventionalMemory, 03F56000, 276E, F
10, 1, EfiLoaderCode, 066C4000, 2, F
11, 4, EfiBootServicesData, 066C6000, A, F
12, 9, EfiACPIReclaimMemory, 066D0000, 1, F
13, 4, EfiBootServicesData, 066D1000, 1BB, F
14, 3, EfiBootServicesCode, 0688C000, A8, F
15, A, EfiACPIMemoryNVS, 06934000, 12, F
16, 0, EfiReservedMemoryType, 06946000, 1C, F
17, 3, EfiBootServicesCode, 06962000, 10A, F
18, 6, EfiRuntimeServicesData, 06A6C000, 5, F
19, 5, EfiRuntimeServicesCode, 06A71000, 5, F
20, 6, EfiRuntimeServicesData, 06A76000, 5, F
21, 5, EfiRuntimeServicesCode, 06A7B000, 5, F
22, 6, EfiRuntimeServicesData, 06A80000, 5, F
23, 5, EfiRuntimeServicesCode, 06A85000, 7, F
24, 6, EfiRuntimeServicesData, 06A8C000, 8F, F
25, 4, EfiBootServicesData, 06B1B000, 730, F
26, 7, EfiConventionalMemory, 0724B000, 4, F
27, 4, EfiBootServicesData, 0724F000, 6, F
28, 7, EfiConventionalMemory, 07255000, 1, F
29, 4, EfiBootServicesData, 07256000, 7C5, F
30, 7, EfiConventionalMemory, 07A1B000, 1, F
31, 3, EfiBootServicesCode, 07A1C000, 17F, F
32, 5, EfiRuntimeServicesCode, 07B9B000, 30, F
33, 6, EfiRuntimeServicesData, 07BCB000, 24, F
34, 0, EfiReservedMemoryType, 07BEF000, 4, F
35, 9, EfiACPIReclaimMemory, 07BF3000, 8, F
36, A, EfiACPIMemoryNVS, 07BFB000, 4, F
37, 4, EfiBootServicesData, 07BFF000, 201, F
38, 7, EfiConventionalMemory, 07E00000, 8D, F
39, 4, EfiBootServicesData, 07E8D000, 20, F
40, 3, EfiBootServicesCode, 07EAD000, 20, F
41, 4, EfiBootServicesData, 07ECD000, 9, F
42, 3, EfiBootServicesCode, 07ED6000, 1E, F
43, 6, EfiRuntimeServicesData, 07EF4000, 84, F
44, A, EfiACPIMemoryNVS, 07F78000, 88, F
45, 6, EfiRuntimeServicesData, FFC00000, 400, 1
© 2021 czu.jp