どうもみなさん。お久しぶりです。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の講座に参加することとなりました。
セキュリティキャンプ全国大会2018 システムプログラミングトラック OS 開発ゼミ通りました!よろしくお願いします。#seccamp
— PiBVT (@PiBVT) June 14, 2018
応募用紙の記事を見てもらえば分かるとおり、とにかく情報を集めまとめただけだったので、実際の実装経験や方法すら知らない状態で参加してしまいました。
しかも、ぼくの本格的なプログラミング歴は大学生になってからなので、およそ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アプリの開発の方は、他の方が作られたソースコードを参考にしたりして割と順調に進んでいたように思います。まだこのころは幸せでした….
これから2ヶ月は寝食共にすることになる pic.twitter.com/gABHk8uVlN
— PiBVT (@PiBVT) July 3, 2018
GitHub等に上がっているソースコードを参考に書いたUEFIブートローダーがLinux Kernelを読み込んでくれた。 pic.twitter.com/DWWOuhiEKW
— PiBVT (@PiBVT) July 11, 2018
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の手順に従ってアセンブリを書いたりしていました。
は?3時間位同じところでエラーになるのまじで意味分からん pic.twitter.com/m8k1miUuDX
— PiBVT (@PiBVT) July 30, 2018
が、中々うまく行きませんでした。
8月上旬
試験が大体終わり、ある程度の進捗が生まれ始めます。
しゃあああああああああああああああああ!32bit用GDTをCS registerに読み込めたああああああ
— PiBVT (@PiBVT) August 4, 2018
今となっては何言ってるの?って感じですが、当時はそれなりに嬉しかったんですよ。
自作OSというより自作ブートローダーになってる気がするが何も気にしない。
— PiBVT (@PiBVT) August 4, 2018
この時点で割と真理に気がついていた。
うーん…xv6のブートシーケンス自体には入れたけど、ページングの設定でcr3レジスタにアクセスしたら落ちる…
— PiBVT (@PiBVT) August 5, 2018
32bitバイナリを実行できる状態にはなりましたが、ページングが有効化できない症状にこれから10日間近く悩まされることになります。
はやくCPUの設定から解放されたい…(ページングの設定ができない)
— PiBVT (@PiBVT) August 5, 2018
切実。
phでLOADなやつが複数あって.data .bssの方は読み込んでなかったとかポンコツやわ
— PiBVT (@PiBVT) August 8, 2018
完成したと思っていたUEFIローダーで.dataと.bss領域が読み込めてないバグが発覚。かなり落ち込んでました。
キャンプ直前
もはやxv6を起動すらできてない状態でキャンプに突入しそうになり、半ばヤケになってます。
arp,icmpパケットと戯れることで進捗を生んでいるように感じるテクニック。 xv6はまだ動かない
— PiBVT (@PiBVT) August 11, 2018
LinuxのRaw Socketでパケットを読み込んだり、パケットを作って送信したりして何とか精神状態を保とうとしています。
ARPを実装した。なおUEFIローダーの進捗は(ry
— PiBVT (@PiBVT) August 12, 2018
ほぼ諦めてます。
キャンプ当日
キャンプのことはまた別記事で詳しく書きたいと思います。ここでは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で読むという変態じみた手法でデバッグしていました。
今日学んだこと no.2
午後
・espをsubするとQEMUが落っこちる
→GDBとの相性・MP相当のものはACPIのMADTになってる。
→これを使うように書き換えよう・GDBがないからデバッグできない
→EAXレジスタとHLTを最大限に活用しよう— PiBVT (@PiBVT) August 15, 2018
結局この日はメモリーダンプをとってBIOSが構成するMP構造体がないか探していましたが無いことが判明したためuchan先生と相談し、@liva先生のRaphine OSを参考にMADTを利用する方針となりました。
OS側からMADTの取得に成功したからマルチプロセッサ問題は何とかなりそう
— PiBVT (@PiBVT) August 15, 2018
キャンプ DAY3
朝食を6:30から食べた後、すぐに前日の続きでMADTの情報取得を実装していました。前日の時点でほぼ実装は終了していたため、何個かバグを修正した後、LAPICとIOAPICの初期化処理が無事走るようになります。後少しの所まで来た感じになりましたが、IDEドライバとConsoleのtext mode問題が発覚します。
午後からIDEドライバ問題とConsoleのtext mode問題に取り組んでいました。IDEドライバ問題は、xv6にメモリーを利用した仮想IDEデバイスモードがあったためそれを利用することで回避し、text mode問題はディスプレイでの表示を諦め、シリアル通信経由で文字をやり取りすることにしました。
ついに起動!
DAY3の午後、ついにxv6のシェルが起動しました!このときはほんとに嬉しくてuchan先生と一緒にはしゃいでいました。同じ部屋でコンパイラを書いていた鮟鱇さん曰く「何か叫んでた」と言うぐらいはしゃいでました。ほんとにうれしくて感動してました。
32ビット用のxv6を64ビットUEFI(on QEMU)で起動させることに成功しました。セキュリティキャンプの大きな成果ですね。素晴らしい。スクリーンショットはxv6 on UEFIでlsコマンドを実行した場面です。 #seccamp2018 #seccamp pic.twitter.com/7MYs8hHmP1
— C++でOS自作 技術書典5 お05 (@uchan_nos) August 17, 2018
DAY3はその後、貸出実機のMinnowBoard上で動かすためにあーだこーだしていましたが、再起動を繰り返してばかりでうまくいきませんでした。
QEMU側でもメモリを増やすと起動できなくなる問題に直面しました。
その夜、
MADTがメモリ上のデバイスがマップされた場所と重なっているから駄目なのか。
— PiBVT (@PiBVT) August 16, 2018
ひらめきました。ひらめきを元に実装し直すと、
2Gのメモリ上でもxv6_uefiを起動できた。
— PiBVT (@PiBVT) August 16, 2018
4Gメモリでも起動できた。明日の実機テストが楽しみ
— PiBVT (@PiBVT) August 16, 2018
という感じであっけなく解決。もうこれは実機でも起動するだろうとルンルン気分で寝ました。
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で公開しています。
64bit UEFIから32bit xv6を起動することに成功しました!#seccamp #seccamp2018 pic.twitter.com/GhWgjGa30q
— PiBVT (@PiBVT) August 17, 2018
セキュリティキャンプ2018 システムプラグラミングトラック OS開発ゼミ フルスクラッチOS での成果です。
xv6を64bitUEFIから起動することに成功しました。 #seccamp #xv6_uefihttps://t.co/UKlJFoeEao— PiBVT (@PiBVT) August 20, 2018