【UEFI OSを作る!?】初めてのUEFIアプリ作り

OS開発

前書き

今から筆者のどうでもいい話が始まります。
大した内容ではないので読む気がないなら読み飛ばしてください。

四年ぐらい前、図書館でこんな本を見つけました

30日でできる! OS自作入門【電子書籍】[ 川合 秀実 ]

価格:3,520円
(2020/7/12 00:11時点)
感想(0件)

「なんか30日でOS開発できるんだったら借りてみようかな」と思って借りました。
そのころまだプログラミング初めて1年ぐらいで、まったくわかりませんでしたぴえん(殴
というか筆者は文章を読むのが嫌いなので斜め読みしてしまいました()
もう内容覚えてないなー…
ブログを書くのと一緒の時に読めたらいいですね。

っていう感じのことがあって、OS開発に興味を持ち始めました

で、なんで今になってもう一度興味を持ち始めたかというと、c言語を勉強し始めたからです。
正直言って、OS開発しながらC言語勉強する自分は頭がおかしいです。
「もうちょっとアプリとか簡単なものにしろよー」って話ですよ。
ですが、今作りたいものがOSなので、OSを作ります。
あ、完成するとは言ってません。

なので、OSの開発日記みたいなのを作りたいと思います。
それでこそブログなので。
(いろいろと支離滅裂な文でごめんなさい)

どんなOSを作るのか?

↑で紹介した本は2006年に発売されていて、結構古いです(実は2006年という年は筆者と深いかかわりがあります。運命!?)
PCの進化は速いのです。もう今後はその本で作るOSがエミュレーターでしか動かなくなる可能性があります。

なぜかというと、最近のPCはファームウェア(機械を動かすための最低限のプログラム?)がBIOSからUEFIに置き換わっているからです。
BIOSとUEFIは互換性がないので、BIOS対応のOSはUEFI搭載のPCで動かせません。
Legacy BIOSというBIOSを再現するモードがUEFIにはあるのですが、2020年には廃止予定だとか…(今年!?)
ということは今後は実機で動かせなくなる可能性が…

なので、今後のPCでも動くようなUEFI対応のOSを作っていきたいと思います。
でも、情報量が少なくて…そして英語が未熟な筆者にはまだ英語ドキュメントが読めない…
とてもやばいです。こんな感じなのでもう一度言いますが完成するとは言ってません。

とりあえずは作るOSは以下のようなものを目指そうと思います

  • GUI対応
  • HDDの読み書きができる

機能が少ないですが、目標は低く設定しておくのがいいでしょう。

お願い

このブログではクソ自称プログラマがクソブログでクソみたいな文章を書いているので、詳しい人には「それは違うだろ…」って思われるかもしれません。
そう思ったときにはどしどしご指摘ください。
あ、筆者はガラスのハートだから優しい文章でね…お願い

筆者は飽き性なのでもし途中で投稿が止まっても許してください。
できるだけこの目標は達成しようと思いますが…

と、長々と話を続けましたが、このままだと一万文字行きそうなので本題に入ります。

今回すること

OSを読み込むプログラム(ブートローダー)を作るために、まずはUEFIの汚勉強をしていきます。
まずUEFIは何かを少し説明します。

PCは
UEFI(BIOS) → ブートローダ → OS
という順序で起動します。
(POSTとかの処理は省略)

つまり、一番最初に起動するプログラムはUEFIです。
UEFIはPCにもともと備わっているので、自作する必要はありません(というか変にいじるとマジでPCただの箱になるから)
ということは、まずはブートローダを作らないといけません。

ブートローダはUEFIから呼び出されるので、UEFIの仕様に従って作ります(そのようなプログラムを UEFI アプリケーション という)
今回は、超簡単なUEFIアプリケーションを作ってみましょう!

開発環境を整える

仕様は決まっているので、開発を手助けしてくれる助っ人がいますが(ライブラリと言います)、ほぼ一からプログラムを書くのに、そのコードだけ他の人のコードに頼るっていうのもなんか変な気がするので、そういうのには一切頼らないようにします。
それに導入がややこしいので、混乱させないためにも…

とは言ってもプログラムを機械語に翻訳してくれるコンパイラは、ほかの人が提供してくださっているのを使います(それを作るまでに人間の寿命が来ます)

では早速始めていきましょう

OSはWindows10を想定して行いますが、Windows10でWSL(Windows Subsystem for Linux)を利用するのでLinuxとほぼやり方は一緒です。
別に完全にWindowsに固着してもいいんですが、Windowsない人が可哀想なので…
Windows10を持っていない方はVirtualBox等でLinuxを動かしてください。
Linuxでの作業も説明します。

さっさと初めてのプログラムを完成させたいので、最低限のツールだけ揃えます。
次回ぐらいに本格的に環境を構築していきます。

Windows10のみ : WSLのインストール

もしWSL2を使えるようならWSL1からアップデートしておきましょう。
どうやらWSL2では処理速度(特にディスクIO、ネットワークIO等)が早いらしいです(実際はどうかわかりません)

WSL1のインストール方法は
https://qiita.com/yoshige/items/dbc85b048fba51e597ee
が参考になると思います。

筆者はUbuntu 20.04 LTSを使います。

Windows10 のみ : WSLでGUIを使えるようにする

QEMU(エミュレータ―、Virtual BoxとかVMwareみたいなの)を起動するためには、WSLでGUIを使えるようにしないといけません。
https://qiita.com/yoshige/items/7a17bb7a3582d72a7e48
等を参考にしてGUI設定をしてください。

「5.デスクトップ環境構築」はデスクトップ環境を使わないので飛ばしてOKです。

作業用フォルダを作成する

主にWSL上で作業するので、WSL上に作業用フォルダを作成します。
今後はそのフォルダにプログラムを格納していきます。
ホームディレクトリ直下にos-devフォルダを作成することにします(Linuxを直接動かしている人も同様に)

WSLのUbuntuを起動して、↓のコマンドを実行します(Linuxを直接動かしている人は端末を起動する)

mkdir os-dev

コンパイラをインストールする

sudo apt update
sudo apt upgrade

これらのコマンドをWSLで実行して、既存のパッケージをアップデートします。
パスワードが求められるので、セットアップ時に設定したパスワードを入力します。
(文字が表示されませんが、しっかり入力されます)

sudo apt install gcc-mingw-w64-x86-64

このコマンドでコンパイラをインストールします

プログラムを作成する

これで最低限のツールがそろったので、プログラムを作っていきましょう!!

プログラムを作成する

cd os-dev
touch main.c
nano main.c

これらのコマンドでmain.cという名前のソースコード(のちにコンパイラに機械語へ変換してもらうファイル)ファイルを作成します。
見慣れないエディタが表示されますが安心してください。

↓のコードを入力します(コピーして、WSL上で右クリックすれば貼り付けられる)

#define EFIAPI
#define IN

typedef unsigned long long UINTN;
typedef UINTN EFI_STATUS;
typedef short CHAR16;
typedef void * EFI_HANDLE; 

typedef
EFI_STATUS
(EFIAPI *EFI_TEXT_STRING)(
  IN struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
  IN CHAR16 *String
);

typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
  char buf1[8];  /*EFI_TEXT_RESET                              Reset;                   */

  EFI_TEXT_STRING OutputString;
  char buf2[8];  /*EFI_TEXT_TEST_STRING                   TestString;             */

  char buf3[8];  /*EFI_TEXT_QUERY_MODE                   QueryMode;           */
  char buf4[8];  /*EFI_TEXT_SET_MODE                        SetMode;               */
  char buf5[8];  /*EFI_TEXT_SET_ATTRIBUTE                SetAttribute;          */

  char buf6[8];  /*EFI_TEXT_CLEAR_SCREEN                 ClearScreen;          */
  char buf7[8];  /*EFI_TEXT_SET_CURSOR_POSITION    SetCursorPosition;  */
  char buf8[8];  /*EFI_TEXT_ENABLE_CURSOR               EnableCursor;        */

  char buf9[8];  /*SIMPLE_TEXT_OUTPUT_MODE           *Mode;                   */
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;

typedef struct {
  char buf1[24];  /* EFI_TABLE_HEADER                              Hdr;                          */

  char buf2[8];    /* CHAR16                                                *FirmwareVendor;     */
  char buf3[4];    /* UINT32                                                 FirmwareRevision;     */

  char buf4[8];    /* EFI_HANDLE                                         ConsoleInHandle;       */
  char buf5[8];    /* EFI_SIMPLE_TEXT_INPUT_PROTOCOL    *ConIn;                     */

  char buf6[8];    /* EFI_HANDLE                                         ConsoleOutHandle;     */
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;

  char buf7[8];    /* EFI_HANDLE                                         StandardErrorHandle; */
  char buf8[8];    /* EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;                     */

  char buf9[8];    /* EFI_RUNTIME_SERVICES                       *RuntimeServices;      */
  char buf10[8];  /* EFI_BOOT_SERVICES                            *BootServices;            */

  char buf11[8];  /* UINTN                                                  NumberOfTableEntries;*/
  char buf12[8];  /* EFI_CONFIGURATION_TABLE                 *ConfigurationTable;    */
} EFI_SYSTEM_TABLE;

EFI_STATUS
efi_main (
  IN EFI_HANDLE ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
) {
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello, world!");

  while (1);
}

今回はプログラムを作成するのが目的なので、ソースコードの意味はまだ理解しなくて全然大丈夫です。
次回以降に説明します。

入力(または貼り付け)できたら、Ctrl+Sを押して保存した後、Ctrl+Xで終了します。

コンパイルする

x86_64-w64-mingw32-gcc -Wall -Wextra -nostdinc -nostdlib -Wl,--subsystem,10 -e efi_main -o main.efi main.c

引数がいろいろありますが、それも次回以降説明します。

QEMUをインストール

QEMUはVirtualBoxとかVMwareみたいな仮想PCを作るためのソフトウェアです。
(本当に実機で動いているかのようにOSを動かす)

いちいち実機で動かすのも面倒ですし、今後は実行テストをQEMUで行います。
(次回以降実機での動かし方は説明します)

QEMUは初期状態ではUEFIに対応していないので、UEFIに対応させるためのOVMFもインストールします。

sudo apt install qemu-system-x86
sudo apt install ovmf

QEMUで実行する

いよいよ最後の作業です。

mkdir root/
mkdir root/EFI
mkdir root/EFI/BOOT
mv main.efi root/EFI/BOOT/BOOTX64.EFI

QEMUに認識してもらうrootフォルダを作成します。
OSを起動するのに使うブートローダー(UEFIアプリケーション)は、64ビット環境ではEFI/BOOT/BOOTX64.EFIに置く決まりがあるので、そこにmain.efi(移動時にBOOTX64.EFIへリネームされる)を置きます。

qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -drive format=raw,file=fat:rw:root

するとQEMUが起動し、↓のように「Hello, world!」が表示されると思います。

まとめ

今回は開発環境の構築から、初めてのUEFIアプリケーションを作る方法を学びました。
次回はもうちょっと開発ツールをそろえたり、プログラムの解説等をしていきたいです(予定)

コメント

タイトルとURLをコピーしました