EDK2を使ってメモリマップを取得してみよう

2022-11-28-00-51-18.webp
目次

イントロダクション

駆け込みでふるさと納税をしまして、蟹やホタテが届き始めました。 来年は食べ物以外の枕とか、包丁とかフライパンとか日用品が欲しいなとか考えてます。

ふるさと納税ワクワクする制度で良いですよね。 長期的に見ると自分が住んでる街にお金を振り込むほうが良いのだと思いますが、そういっても難しい。

さて、今日も続けて自作OS入門本(通称:みかん本)を進めて行きましょう。 途切れたら止まってしまいそうで……

EDL2使ってメモリマップを取得する方法

自作OS本のレポジトリを取得してきます。

git clone https://github.com/uchan-nos/mikanos.git
git checkout osbook_day02b
// #@@range_begin(get_memory_map)
EFI_STATUS GetMemoryMap(struct MemoryMap* map) {
  if (map->buffer == NULL) {
    return EFI_BUFFER_TOO_SMALL;
  }

  map->map_size = map->buffer_size;
  return gBS->GetMemoryMap(
      &map->map_size,
      (EFI_MEMORY_DESCRIPTOR*)map->buffer,
      &map->map_key,
      &map->descriptor_size,
      &map->descriptor_version);
}
// #@@range_end(get_memory_map)

UEFIは2種類のサービスで構成されている。

  • ブートサービス:OS起動に必要な構成
  • ランタイムサービス:OS起動前、起動後いつでも読み出せる。

メモリ管理はブートサービスに含まれる。

ビルドしてみる

2章に書いてあるコマンドを実行してビルドを開始する。

cd ~/edk2
source edksetup.sh
build
Build environment: Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
Build start time: 00:33:21, Nov.28 2022

WORKSPACE        = /home/ayahumohumo/edk2
EDK_TOOLS_PATH   = /home/ayahumohumo/edk2/BaseTools
CONF_PATH        = /home/ayahumohumo/edk2/Conf
PYTHON_COMMAND   = /usr/bin/python3



build.py...
 : error 2000: Invalid parameter
        Invalid ARCH specified. [Valid ARCH: X64]

- Failed -
Build end time: 00:33:21, Nov.28 2022
Build total time: 00:00:00

エラー解析

ビルド向けのパラメータが間違っているようです。

archコマンドを使って、アーキテクチャを確認できます。

arch
x86_64

x86_64なので、X64を設定ファイルに記載しておきます。

vim Conf/target.txt 

TARGET_ARCHを修正します。

#  TARGET_ARCH           List       Optional    What kind of architecture is the binary being target for.
#                                               One, or more, of the following, IA32, IPF, X64, EBC, ARM
#                                               or AArch64.
#                                               Multiple values can be specified on a single line, using
#                                               space characters to separate the values.  These are used
#                                               during the parsing of a platform description file,
#                                               restricting the build output target(s.)
#                                               The Build Target ARCH is determined by (precedence high to low):
#                                                 Command-line: -a ARCH option
#                                                 target.txt: TARGET_ARCH values
#                                                 DSC file: [Defines] SUPPORTED_ARCHITECTURES tag
#                                               If not specified, then all valid architectures specified
#                                               in the platform file, for which tools are available, will be
#                                               built.
TARGET_ARCH           = X64

error 4000: Instance of library class [RegisterFilterLib] is not found

 build
Build environment: Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
Build start time: 00:42:23, Nov.28 2022

WORKSPACE        = /home/ayahumohumo/edk2
EDK_TOOLS_PATH   = /home/ayahumohumo/edk2/BaseTools
CONF_PATH        = /home/ayahumohumo/edk2/Conf
PYTHON_COMMAND   = /usr/bin/python3


Processing meta-data .
Architecture(s)  = X64
Build target     = DEBUG
Toolchain        = CLANG38

Active Platform          = /home/ayahumohumo/edk2/MikanLoaderPkg/MikanLoaderPkg.dsc


build.py...
/home/ayahumohumo/edk2/MikanLoaderPkg/MikanLoaderPkg.dsc(...): error 4000: Instance of library class [RegisterFilterLib] is not found
        in [/home/ayahumohumo/edk2/MdePkg/Library/BaseLib/BaseLib.inf] [X64]
        consumed by module [/home/ayahumohumo/edk2/MikanLoaderPkg/Loader.inf]
 

- Failed -
Build end time: 00:42:23, Nov.28 2022
Build total time: 00:00:00

解決方法

edk2のバージョンが違うのが原因のようです. 当時のバージョンで実行しましょう。

cd ~/edk2
git checkout edk2-stable202011

これでビルドが通りました。

実行してメモリマップを確認する

~/osbook/devenv/run_qemu.sh Build/MikanLoaderX64/DEBUG_CLANG38/X64/Loader.efi 

メモリマップが表示

QEMU上のメモリマップファイルを確認する

ディスクイメージの中身を見てみると、Loader.efiによって保存されたメモリマップがあります。

カレントディレクトリに作成されるので、上記環境だと、edk2の直下にdisk.imgが作られているはずです。

mkdir -p mnt
sudo mount -o loop disk.img mnt
ls mnt

表示はこんな感じです。

EFI  memmap
cat mnt/memmap

メモリマップ

組み込み系の開発してるとたまにメモリマップ直接開いてコケている箇所を探したりなど解析することがあります。 自動車系だとリプロとかいうてECUを書き換える際に開始するメモリを指定して書き換えろみたいなこともあります。

まとめ

これにてメモリマップの話は終わりです。

ふと思い出したのですが、前職でメモリマップをイジった記憶があります。 書き込んでおいたソフトが改竄されたことを検知したら、ソフトを起動させなくするセキュアブートという機能があるのですが、 これを作っていたときには、ブートローダから立ち上げてメインプログラムを呼び出す前に、セキュリティ系のチップの演算処理を先に読み出して、ソフトを書き込んだ領域でハッシュ値を求め、予め書き換えできない領域に記載したハッシュ値と突合して改竄されていないことを保証するみたいな実装をしていました。 5年前ぐらいですが結構覚えてるもんですね。

このときにセキュア領域に指定されている領域を探してたのですが、仕様書と異なってたんですよ。まじで許せなかったですね。

みたいな与太話もありつつ、この辺でお開きにします。

アドレスやポインタの説明が書いてます。 C言語のポインタ説明については、みかん本がかなり詳細に書いてくれています。 原理というか実装から書かれている本ってC言語の本でも中々無いのでこの部分だけでも良書じゃないかと思います。

正直ポイントの話は知らなかったです。勉強になりました。