現在作っているブートローダに関して

Table of Contents

  1. はじめに
  2. どのようなブートローダを目指しているのか?
  3. 現在の見た目
  4. 動作に関して
  5. どのようにしたか?
    1. 表示の遅延に関して
    2. statusのみの色を変えたことに関して
    3. ロゴの表示に関して
  6. 妙に引っかかった箇所
    1. ウォッチドッグタイマの無効化忘れ
  7. 今後に関して
  8. 書き終えて

はじめに

メリークリスマス!適当に入れたらクリスマスイブ担当となった猫(1010)です。ちなみにクリスマスはアドベントカレンダーを書いて、夕方にセキュリティキャンプのチームとRust入門をやり、深夜にVRChatをやるといった感じですね。充実しているんだかしてないんだか…。

仕切り直して、最近の近況といきます。今年は色々変化があった年でした。しかし、もう終わりとなると感慨深いものがあります。11月ごろから、セキュリティ・キャンプ全国大会2020onlineにYトラックOS自作ゼミとして参加をしてきました。同じような趣味の方にであうことができて非常に楽しい経験だったことを覚えています。また、セキュリティキャンプを通してブートローダを作成していました。セキュリティ・キャンプ中には完成はしなかったのですが(計画性の破綻)、期間中に作るためのイメージはついたのでチョコチョコ作業をしております。

どのようなブートローダを目指しているのか?

  • おしゃれ
  • できる限りの情報をじっくり確認できる
  • ELF形式のカーネルをブート

これらを達成できるといいなぁと考えながら作成をしています。

特にできる限りの情報をじっくり確認できるという点に関しては、以前からブートローダがスッ…と終わってしまい。ブートローダー何やっとるん?といった感覚になっていたというのが理由としてあります。要はLinuxのブート時のログのように表示できれば面白そうってことですね。ですが、Linuxのログも表示が早いため、なんか悲しいです。まぁログファイルが残るといえば早くてもいいだろうという感覚やブート時に遅かったらダルいと考えると理解できます。ですが、自作のブートローダーくらいじっくり見たいのです!

あとは、開発するにあたりはじめはフルスクラッチで書こうとしていました。ですが、ヘッダーファイルを書くのが結構大変ということもあり、現在ではEDK2を使っています。

現在の見た目

f:id:No000:20201224235220j:plain
このような感じで、Linux風のログを表示します。変わっている点としては、ELFヘッダの内容を一部表示させている点ですね。現状としては普通の自作のブートローダでも出力している内容に近いですが、色を変更したりしてわかりやすくすることと情報の量を増やしていこうと考えています。また、ロゴに関してはアスキージェネレータで生成したものをPrint()で愚直に表示をさせています。

ちなみに、動画バージョンは以下に

動作に関して

『どのようなブートローダを目指しているのか?』でも述べましたが、表示をじっくり見ることができるように画面出力の合間にStall()を利用して時間差をつけています。また、'''Kernel boot(press RET)'''の箇所では、Retrunを押すと処理が始まるようにしています。

どのようにしたか?

それぞれの調整はほんとに愚直にやりました。

表示の遅延に関して

以下のようにしています

SystemTable->BootServices->Stall(n);

nの箇所をマイクロ秒として仕込んであげることで遅延を起こすことができます。自分は、500000にしています。

statusのみの色を変えたことに関して

SetAttribute()を利用して色を変えていました。

SystemTable->ConOut->SetAttribute(SystemTable->ConOut, EFI_LIGHTRED);

上記コード例では、ライトレッドの色合い色合いにしています。このEFI_LIGHTREDはありがたいことにEDK2側で定義されており

edk2/MdePkg/Include/Protocol/SimpleTextOut.h

にて定義されています。git grepコマンド等で探せば早いかと思います。

ロゴの表示に関して

これは以下のURL先のASCIIジェネレータを利用して生成したものをPrint()で出力させています。
partorjk
これを使って以下のように、出力させると表示できました。

  Print(L" __       __                        __       ______    ______  \n");
  Print(L"/  |  _  /  |                      /  |     /      \\  /      \\ \n");
  Print(L"$$ | / \\ $$ |  _______   ______   _$$ |_   /$$$$$$  |/$$$$$$  |\n");
  Print(L"$$ |/$  \\$$ | /       | /      \\ / $$   |  $$ |  $$ |$$ \\__$$/ \n");
  Print(L"$$ /$$$  $$ |/$$$$$$$/  $$$$$$  |$$$$$$/   $$ |  $$ |$$      \\ \n");
  Print(L"$$ $$/$$ $$ |$$ |       /    $$ |  $$ | __ $$ |  $$ | $$$$$$  |\n");
  Print(L"$$$$/  $$$$ |$$ \\_____ /$$$$$$$ |  $$ |/  |$$ \\__$$ |/  \\__$$ |\n");
  Print(L"$$$/    $$$ |$$       |$$    $$ |  $$  $$/ $$    $$/ $$    $$/ \n");
  Print(L"$$/      $$/  $$$$$$$/  $$$$$$$/    $$$$/   $$$$$$/   $$$$$$/  \n");
  Print(L"                                                               \n");
  Print(L"                                                               \n");
  Print(L"                                                               \n");

ここで注意するべきなのはエスケープシーケンスです。そこだけを一つずつ修正すると良い感じとなります。

妙に引っかかった箇所

ウォッチドッグタイマの無効化忘れ

Qemuを放っておくと、5分ごとに再起動を起こしており不思議だったのですが、ウォッチドッグタイマを無 効化することで解決しました。

SystemTable->BootServices->SetWatchdogTimer(0, 0, 0, NULL);

今後に関して

今後は選択画面を実装したいと考えています。Clearscreenとprintを高速で繰り返すことで実装することをイメージしており、最初はUEFI shellに行ける選択肢とカーネルブートの選択肢か、ダミーの選択肢を作成するかで迷い中…。あとは、memorymapの取得まで実装が完了しているので、ExitBootServices()周辺を実装してあげれば動くかなと考えています。

書き終えて

気づいた人は少ない、もしくはいないかもしれないですが、いつものブログの書き方とは少し体裁が異なっています。その理由は、emacsのorg-modeで作成したorgファイルをmark-downに変換した後にはてなブログに貼っつけ、微調整をしたからなんですよね。もしかして、私はemacsに盆栽をするようにカスタマイズしまくることが好きなのかな、とか思いながらアドベントカレンダーを書いていました。