セキュリティキャンプでxv6を64bitUEFIから起動した話

どうもみなさん。お久しぶりです。PiBVTです。

今回、セキュリティキャンプに参加し、xv6_uefiを開発したことについて書いていきたいと思います。

キャンプ全体のことや、今回の技術的な話はまた後日僕のブログで記事にします。

セキュリティキャンプとは?

セキュリティ・キャンプとは

セキュリティ・キャンプとは、日本における将来の高度IT人材となり得る優れた人材の発掘と育成を目的とした独立行政法人情報処理推進機構(IPA)の事業の一つです。

現代においては、情報セキュリティの脅威は高まる一方です。
本事業では、セキュリティ分野に興味を持ち、将来同分野で活躍したいという意志をもった若者に対して、高度な情報セキュリティ技術の習得機会を提供しています。また、モラルや法律遵守の意識、セキュリティ意識、職業意識、自立的な学習意識についても向上のための機会を提供しています。

本事業は、2004年度のスタート以来、2017年度のセキュリティ・キャンプまでで計663名の将来が有望なIT人材を輩出しています。セキュリティ業界はもとより各方面から、高度なIT人材育成に有益なイベントとして高く評価されています。

https://www.ipa.go.jp/jinzai/camp/2018/zenkoku2018_about.htmlより

と、こんな感じです。今年は8月14日から8月18日までの5日間開催されました。要するに、優秀なIT人材の卵を育てようというものです。

システムプログラミングトラック OS開発ゼミ

以前の記事で応募用紙は晒したのですが、僕は応募した結果、システムプログラミングトラック OS開発ゼミ フルスクラッチOSの講座に参加することとなりました。

応募用紙の記事を見てもらえば分かるとおり、とにかく情報を集めまとめただけだったので、実際の実装経験や方法すら知らない状態で参加してしまいました。

しかも、ぼくの本格的なプログラミング歴は大学生になってからなので、およそ1年と半年程度といったところでしょうか。つまり、ガチのプログラミング初心者だったのです。

開発の道のり

グダグダ書いてもアレなので、時系列順に僕がやっていたことを追いかけていきます。

参加決定後〜6月下旬

参加が決定し、講師の@uchan_nos先生と一緒に具体的に作るOSの方針を決めました。当時の僕はxv6を改造したいと考えていたので、

  • x86-64,UEFI対応化
  • NICデバドラ開発
  • TCP/IPプロトコルスタック開発

の3つを目標と掲げていました。当時は何を考えていたのか分かりませんが、とにかくやりたいことを目標にした感じです。

目標は具体的に決定したので、まずはxv6の構造を知ることから始めました。

6月下旬〜7月上旬

このころは、通学中の電車内でxv6 bookをひたすら読み、暇があるときはUEFIアプリケーションの開発SDKであるEDK2を使ってHello UEFIをしたりしていました。

xv6 book自体は英語で書かれていて、読めなくは無いのですが中々頭に入ってこなくて大変でした。UEFIアプリの開発の方は、他の方が作られたソースコードを参考にしたりして割と順調に進んでいたように思います。まだこのころは幸せでした….

7月中旬〜7月下旬

試験期間に突入し、進捗がほとんど生まれなくなります。また、実験的に32bitバイナリのxv6を32bitUEFIから起動しようとしたものの、ELFファイルの読み込みをする段階でQEMUがフリーズしてしまうという謎現象に悩まされ、何もできなくなってしましました。

そこで、xv6をx86-64に移植するのではなく、64bit UEFIで起動して、CPUのモードを32bit protected modeに移行してからxv6を実行するという方法で作業量を圧倒的に減らす方法を講じることにしました。我ながら変態なことを考えたものだと思います。uchan先生もおもしろい方法だと褒めてくださり、Intel SDMでmode switchingの章を教えてくださりました。

その後はxv6のELF形式のバイナリをメモリー上に展開するためのUEFIプログラムを書き、IntelSDMの手順に従ってアセンブリを書いたりしていました。

が、中々うまく行きませんでした。

8月上旬

試験が大体終わり、ある程度の進捗が生まれ始めます。

今となっては何言ってるの?って感じですが、当時はそれなりに嬉しかったんですよ。

この時点で割と真理に気がついていた。

32bitバイナリを実行できる状態にはなりましたが、ページングが有効化できない症状にこれから10日間近く悩まされることになります。

切実。

完成したと思っていたUEFIローダーで.dataと.bss領域が読み込めてないバグが発覚。かなり落ち込んでました。

キャンプ直前

もはやxv6を起動すらできてない状態でキャンプに突入しそうになり、半ばヤケになってます。

LinuxのRaw Socketでパケットを読み込んだり、パケットを作って送信したりして何とか精神状態を保とうとしています。

ほぼ諦めてます。

キャンプ当日

キャンプのことはまた別記事で詳しく書きたいと思います。ここではxv6のことについてのみ触れます。

キャンプ DAY2

午前中にページングが有効化出来ない問題が解決します。原因はPAEが有効になっていたこと。たったレジスタの1bitに2週間近く溶かされたことが判明した瞬間でした。

正直、もっとしっかりSDMを確認していれば回避できていた問題なので出来れば気が付きたかった。

午後にはxv6のmain関数が走り始め、本格的に起動の兆しが見えてきます。と思ったのもつかの間、mp_init()という関数がBIOS依存のため、UEFIでは起動できないことが判明。「あんれまぁ」という感じで、uchan先生も「いばらの道」とおっしゃっていました。

さらに、GDBがCPUのmodeを正しく認識できてないことが判明し、逆にクラッシュする原因となっていました。そのため、GDBは使えず、hltやmov hoge,%eaxのインラインアセンブリで怪しい部分にhltを入れては変数の値をmov hoge,%eaxで読むという変態じみた手法でデバッグしていました。

結局この日はメモリーダンプをとってBIOSが構成するMP構造体がないか探していましたが無いことが判明したためuchan先生と相談し、@liva先生のRaphine OSを参考にMADTを利用する方針となりました。

キャンプ DAY3

朝食を6:30から食べた後、すぐに前日の続きでMADTの情報取得を実装していました。前日の時点でほぼ実装は終了していたため、何個かバグを修正した後、LAPICとIOAPICの初期化処理が無事走るようになります。後少しの所まで来た感じになりましたが、IDEドライバとConsoleのtext mode問題が発覚します。

午後からIDEドライバ問題とConsoleのtext mode問題に取り組んでいました。IDEドライバ問題は、xv6にメモリーを利用した仮想IDEデバイスモードがあったためそれを利用することで回避し、text mode問題はディスプレイでの表示を諦め、シリアル通信経由で文字をやり取りすることにしました。

ついに起動!

DAY3の午後、ついにxv6のシェルが起動しました!このときはほんとに嬉しくてuchan先生と一緒にはしゃいでいました。同じ部屋でコンパイラを書いていた鮟鱇さん曰く「何か叫んでた」と言うぐらいはしゃいでました。ほんとにうれしくて感動してました。

DAY3はその後、貸出実機のMinnowBoard上で動かすためにあーだこーだしていましたが、再起動を繰り返してばかりでうまくいきませんでした。

QEMU側でもメモリを増やすと起動できなくなる問題に直面しました。

その夜、

ひらめきました。ひらめきを元に実装し直すと、

という感じであっけなく解決。もうこれは実機でも起動するだろうとルンルン気分で寝ました。

DAY4

この日は朝から実機で動かす気満々で朝食を食べたのち、作業をしていました。部屋が開いてからすぐに実機でテストをしましたが、シリアルから何も出てこない。

これはどういうことだ?と、DAY3にMinnowBoardとPCをつなぐシリアル変換ケーブルを貸してくださったシステムプログラミングトラック ベアメタル開発のN先生に相談したところ、送信バッファの中身が空かどうか確認すると良いというアドバイスを頂いたので、早速試してみました。

アセンブリで、送信バッファが空ならばクラッシュさせ、空でなければ無限ループするというコードを書き実行。結果、クラッシュすることなく無限ループしているようだったので、送信バッファが吐けていないことが判明。さらに、MinnowBoardのシリアルはIOポートでなくPCI Express経由で入出力していることが判明し絶望。

仕様書をよく読むと、IOポートでの通信を可能にするオプションがあったためそれを有効化にしてみましたが、何も出てこず、あえなく時間切れとなりました。

ちなみに、実機でのデバッグはhltをかけても止まっているのかすら分からないので、xv6がクラッシュすることなく無限ループに入っていることを利用して、意図的にクラッシュさせることでカーネルパニックが起こることなく正常に処理が到達していることを確認するカオスな方法でデバッグしていました。

最後に

当初の目標とは大きく異なる着地地点となりましたが、これはこれでとても楽しかったです。もちろん、最初の3つの目標は達成するつもりなので、気長に頑張っていこうかなと考えています。OS開発はデバッグやら設定の作法などで大変な部分が多いですが、その分うまく動いてくれたときの感動は格別です。自作OSは最高に楽しいのでみなさんもチャレンジしてみてください。

謝辞

@uchan_nos先生のほぼマンツーマンでのアドバイスがなければここまでたどり着くことは出来なかったと思います。自作OS開発の辛さと楽しさを知ることが出来ました。また、GDBについては@sat先生、MADTについては@liva先生、実機での実験はベアメタル開発のN先生のアドバイスを元に問題を解決することができました。多くの講師の先生方のご指導のおかげでこのような成果を出すことが出来ました。本当にありがとうございました。

成果物

64bitのUEFIからxv6の起動には成功したので、ソースはGitHubで公開しています。