BoCCHAN-1使ってみた
はてブ書いてなさすぎて広告表示されちゃいましたよ
ということで、何を書こうかなあと思ったのですが、最近弄っているBoCCHAN-1 OBC(On Board Computer)のことをメモ代わりに書いておこうと思います。
BoCCHAN-1 is 何
超小型衛星用標準搭載計算機ボード – Kimura-Lab.net
木村研究室が開発した技術を使った小型高性能な衛星用計算機ボードで、6cm四方の大きさでありながら従来の超小型衛星のOBCとは一線を画す性能を発揮します。
こういうやつです。
大学発の小型人工衛星、「ほどよし」の主計算機として使われていたりする、けっこうすごいやつです。
え?なんでそんな特殊なコンピュータ使ってんのかって? 実は今年、東京理科大学がやっている宇宙教育プログラムというものに参加していまして、
そこでやるCanSat実験用に使っています(贅沢すぎでは)。
機能とか
なんとこいつLinuxが動きます。第1回CHD(CanSat Hands-on Discussion)のときこれを聞いて、静かに興奮しておりました。 アーキテクチャはSH4*1。組み込みってかんじですね。
インターフェースについては、I2CとかUARTとかGPIOとかイケるみたいで中々良い。
Xbeeで通信もできるし。
チュートリアルやってみた
宇宙教育プログラムLETUS*2にアップロードされたクイックスタートガイドやユーザーズマニュアルとかを見ながらチュートリアルとかをやってみました。
そこに書いてあった開発手順を簡単に紹介すると、
- 開発環境一式が入っている仮想マシンイメージをダウンロード
- 仮想マシン内の、"ほどよしSDK"が導入されたEclipseを起動して、"Executable with HODOYOSHI SDK"プロジェクトを選択して作成
- C/C++でコード書いてビルド
- Eclipseの中にいる"BoCCHAN-1 ttyConsole"からBoCCHAN-1にシリアル接続
- バイナリを"BoCCHAN-1 ttyConsole"にD&Dして転送
- ttyConsoleから実行してみる
みたいなかんじでした。
開発環境の自作
というわけで、チュートリアルは出来たのですが、ここでいくつか問題が発生しました。 問題とは何かというと、
はい。開発環境自作のお時間です。
まずはEclipseどうなってんのかなと思ったので、適当な"Executable with HODOYOSHI SDK"プロジェクトを作って、Makefileを見てみました。 IDEが生成したMakefileとかクソ長いんじゃないの・・・とか思ったものの、かなり短かったのでやってることはすぐわかりました(Releaseだけだったし)。 主要な部分を抜き出すと、
subdir.mk(コンパイル部分)
%o:../%.cpp @echo 'Building file: $<' @echo 'Invoking: SH4-Linux G++ Compiler' sh4-linux-g++ -I/home/shdevelop/hodosdk/include/ -O0 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<" @echo 'Finished Building: $<'
makefile(リンク部分)
hoge: $(OBJS) $(USER_OBJS) @echo 'Building target: $@' @echo 'Invoking: SH4-Linux G++ Linker' sh4-linux-g++ -L/home/shdevelop/hodosdk/lib -o "hoge" $(OBJS) $(USER_OBJS) $(LIBS) @echo 'Finished building target: $@'
こんなかんじです。
ちなみに$(LIBS)は、objects.mkにて、
LIBS := -lpthread -lm -lhodoyoshi -lrt
となっていました。
ようするに、SuperH向けのg++でコンパイルして、ほどよしSDK内のライブラリをリンクしてるだけです。 なんだこれならホスト環境でも全然出来そうじゃん。
なんかpacmanでそれっぽいのが見つからなかったのと、ほどよしSDKとかが結構古いバージョンのもので構成されてたので、Ubuntu環境に移動してSH4向けのgcc,g++を探してみると、ありました。
# apt-get install gcc-sh4-linux-gnu g++-sh4-linux-gnu
よーし、これでいけるでしょう、ということで、
$ vim hello.c $ sh4-linux-gnu-gc hello.c -o hello $ file hello hello: ELF 32-bit LSB executable, Renesas SH, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped $ qemu-sh4 -L /usr/sh4-linux-gnu/ ./hello Hello, World!
うん、いいかんじ。じゃあこれをBoCCHAN-1に転送して・・・あれ?転送ってどうやってやってるんだ? シリアル接続してコンソールにD&Dってあれなにやってんの?
クイックスタートガイドだけではよく分からなかったので、ユーザーズマニュアルを見てみると、EclipseのttyConsoleの代わりにTera Term*3を使ったファイルのアップロード・ダウンロード方法が書いてありました。 読んでみると、Tera Termというより、それに付属しているZMODEMとかいうものでバイナリをやり取りしているみたいです。ZMODEMってなんぞ?
YMODEMというバイナリ転送プロトコルを改善したものらしい。1986年…生まれてねえ…
じゃあYMODEMってなんなの?
ZMODEMよりは説明が詳しい。これは1985年。1年で名前変えたのか…
これはこれでXMODEMとかいうのを発展させたものらしいので、それも見てみる
パソコン通信で広く使われてたそうな(こいつは何年なんだ…)。
さて、wikiだとあんまり使い方とかが分からなかったので色々と調べてみると、シリアル接続やtelnet接続、SSHなどでリモートにログインしている時に、受信側でrz、送信側でszを実行すると(速度は遅いが)バイナリが双方向に転送できるらしい。なるほど。
でもLinuxでTera Termとかは使えないし、出来れば端末内で済ませたいなあって思ったら、screenコマンドなるものがあったので使ってみた。 BoCCHAN-1をPCにつなぐと/dev/ttyUSB0が生えるので、screenコマンドを使って、ボードレート115200で接続する。
$ screen /dev/ttyUSB0 115200
BoCCHAN-1は電源を入れたらLinuxが起動して、自動的にログインまで済ましてくれているので、あとはZMODEMを使ってバイナリを転送する。
具体的には、BoCCHAN-1側でrzを実行したらCtrl+A+“:exec sz hello"とかやればファイルが転送できる。
ようやくバイナリが転送出来たので、張り切ってBoCCHAN-1の上で実行してみる。
$ ./hello sh: ./hello: not found
…は?
$ ls hello hello
は???
なんでえええええええ(泣)
これ、1週間ぐらい理由が分かりませんでした。
最初は転送がうまく行かなかったのかとか色々考えて、配布された仮想環境でビルドしてできたバイナリを同じ方法で転送してみたりしたのですが、これはうまくいくんですよねえ・・・
原因は、うまく行く方とうまく行かない方のバイナリをfileコマンドにかけてみたら分かりました。CTFかよ。
うまく行かない方
$ file hello hello: ELF 32-bit LSB executable, Renesas SH, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped
うまく行く方
$ file hello hello: ELF 32-bit LSB executable, Renesas SH, version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped
どうやら、インタプリタ(?)が違うようです。このインタプリタってなんぞ? 調べてみたら、ELFの共用ライブラリをmmapに配置するあいつでした。確かにそれ違ったら動かないよね じゃあうまく行く方のELFインタプリタ、/lib/ld-uClibc.so.0ってなんなんだ・・・なんかlibcっぽい名前だな・・・
これも調べてみたら、uClibcという組み込み向けのlibc用のインタプリタ、みたいなかんじでした。 心の奥底で、「libcってglibcでしょ?」みたいな先入観が抜け切れていませんでしたね。
ということで、どうやらuClibcを使わなきゃいけないみたいですが、使用するlibcを変更するとかやろうとすると、gccの設定ファイル弄ったり色々しないといけないらしく、どうも面倒です。
でも、そういえば仮想環境の方では普通にsh4用のg++を使っているだけでした。つまり何らかの方法があるはずです。
ここで、ユーザーズマニュアルを眺めていたら、buildrootという文字列を見つけました。BoCCHAN-1で動いているLinuxはこいつを使ってビルドした、ということでbuildrootについて調べてみると、
Buildroot - Making Embedded Linux Easy
Buildroot is a simple, efficient and easy-to-use tool to generate embedded Linux systems through cross-compilation.
組み込みLinux向けのクロスコンパイル用のツールを簡単に作る・・・?どういうことだ・・・?
なるほど。組み込みLinux向けのクロスコンパイラやリンカ、さらにLinuxイメージまで作ってくれるものらしい。
$ make menuconfig
でTUIの分かりやすい画面で細かいところまで設定出来る。これは良い。ちょっとcrosstool-ngっぽい。
TOOLCHAINの"C library"を見てみたら、もうデフォルトでuClibcが選択されていた。これはいけそうだ。
本当は仮想環境のbuildrootとgccを同じバージョン、にしたり、色々と設定を合わせたりした方がいいのだろうが、面倒だったので、とりあえずTARGET ArchitectureをSuperH、Target Architecture Variantをsh4(SH4 Little Endian)に設定した。
ついでに、C++を使いたいので"GCC Options"の"Enable C++ support"にもチェックを入れておく*4。
これで.configに設定が保存されるので、makeして数時間ほど待つと、buildroot/output/host/usr/bin下にクロスコンパイラやリンカが大量に生える。
$ ls output/host/usr/bin ... sh4-buildroot-linux-uclibc-g++ ...
このコンパイラでビルドして、ZMODEMでBoCCHAN-1に転送すると…
$ ./hello Hello, BoCCHAN-1!
キタアアアアア!!!
いやー時間かかりました。宇宙教育プログラム受講生でここまでしたの僕ぐらいでしょ(謎のイキリ)(無駄な努力)(素直に配布された環境使え)
今回作った開発環境もどきはGitHubに置いときました。
なんか間違ってるとことかあったらPRなりIssueなり頂けると嬉しいです(BoCCHAN-1ユーザーそんなにいないだろ
余談
このサイト落ちてる(数年前は割と活動してたっぽい)し、色々とBoCCHAN-1とかほどよしSDKの情報少なすぎでは・・・
もっと情報がオープンになったらいいなーと思うので、CanSat合宿の時にでも木村教授に話してみたいです。
*1:SH4ってなんか聞いたことがあるようなって思ってたけど、OSECPUのhh4と名前が似てる。関係ないけど。
*2:理科大の教育用のなんかの宇宙教育PG用鯖。掲示板とかファイルアップロード出来るのはいいなと思うけど通知とか遅らせられないんですかね。頻繁に見に行くというのは地味にめんどい。調べてみたらmoodleっていうOSS使ってるっぽくて、Rubyで書かれたAPIラッパーがあったから試してみようとしたのだけれど、管理者権限が無いといけないみたいで諦めた。仕方ないからBeautifulSoupからログインするスクリプト書いたけど、めんどくさくなってそれで終わってる。新規投稿が来たらSlackに流すみたいなのが出来れば便利なんだけど。
*3:そういえば作者の方がFF内…ひょええ
*4:最初気づかなくてビルドし直した
サイボウズ・ラボユースに採択された
ちょっと事後報告になりますが、これからはもうちょっと頻繁に更新していきたいので書きます。
ところで、
キターーーーーーーー!!!!!!! pic.twitter.com/QJ8WWL57Sp
— General Protected Ex (@sksat_tty) 2017年5月1日
ということで、タイトルの通り、サイボウズ・ラボユース研究生に採択されました。
サイボウズ・ラボユースって何よ?
http://labs.cybozu.co.jp/youth.htmllabs.cybozu.co.jp
まあ詳しくはこのサイトを見て欲しいのですが、
サイボウズ・ラボユースは、世界に通用する日本の若手エンジニアの発掘と育成を目指すことを目的とし、学生の若手クリエイターに研究開発の機会を提供する場として、2011年3月31日に設立されました。
という学生支援制度です。
何がいいの?
対象:中卒以上(大学生・院生・未就業者の方も応募できます)
個人のソフトウェア研究開発プロジェクトを会社が応援する制度です。 年間103万円を上限(※1)にサイボウズが奨励金を支払います。 (個別に契約を結ぶ、開発時間に応じた報酬です) 関東近郊在住(東京日本橋まで通えること)を条件としています。 (在宅による遠隔地からの参加も考慮いたします) サイボウズ・ラボ社員が直接指導いたします。 (サイボウズLiveによるオンライン指導もあります) 中間発表会・成果発表会の場で途中成果を報告していただきます。 交通費・宿泊費は規程により別途支給します。 (※1)学生が親の扶養を外れない程度の金額を想定しています
というわけで、端的に言って神です。
作りたいもの作って、ラボの超強いプロに色々教えてもらえて、しかもお金が貰えるんですよ!
NDAとかも結ぶ必要無いですし、基本的に成果物はOSSにする、ということ以外は制約はほとんどありません。
いやー、いいっすね。まあ僕は学校がバイト禁止で、「サイボウズ・ラボユース研究生」というちょっと毛色の違うやつにしたのでお金は貰えないんですが(それでも、交通費とかは支給されますし、基本的にはラボユース生と同じです)。
ラボユースはバイトじゃないし許可してくんねえかなー(小声)
まあ、僕は学校がちょっと忙しかったりするので、出社(?)できるのは夏休みの一部期間だけなんですけどね。 でも、オンラインでの指導とかもしていただけるみたいなので、そこらへん有効活用していきたいです。
あとは、ラボユース研究生は「ラボユースの登竜門」ってなってるので、大学生になったらラボユース生になりたいですね(こういう話も面接の時にさせてもらいました)。
で、お前なにやるの?
「QEMUとかBochsとかでよくね?」、え、はいそうです。新規性の欠片もねえ。
なんでこんなので通ったのかというと、ラボユースはあまり新規性を要求しないからです。
僕がラボユースに応募した主目的は「コンピュータ、というか、x86アーキテクチャの仕組みを学びたい」というのが大きいです。なので、出来たエミュレータがクソ遅くてとても使い物にならなかったとしても、ちゃんとしたエミュレーションができれば満足です。まあ速いに越したことはないんですが。
今のところの最終目標は、このエミュレータ上で「はりぼてOS」を動作させることです。自作OS on 自作エミュレータってカッコイイ。
ちょっと小噺
ところでですね?
採用決定、最短で5/10でわ???() pic.twitter.com/j9rNJa2Isu
— General Protected Ex (@sksat_tty) 2017年5月1日
えーっと、何をやったのかというとですね、
3月30日に、「第6期サイボウズ・ラボユース成果発表会」というものに行ってきたんですよ。
で、ここでラボユースの相談などもさせていただきました。
で、この成果発表会を境に第6期サイボウズ・ラボユースが終了したわけです。
しかし、この日にラボユースのサイトを見てみると、
「サイト見たらまだ第6期募集してる!」ということで、3月31日に応募メールを送りました。いやあとんだ悪ガキですね。
こちら、募集期間のバグを突いて応募したアカウントになります(マジ) https://t.co/SqZIDjMxAO
— General Protected Ex (@sksat_tty) 2017年5月1日
はい。反省してます(ホントか?)。
あ、ちなみに今のサイトでは、第7期の仕様に変更されてるのでご安心を。
あと、僕は第7期での採用です。はい。
セキュリティ・キャンプのススメ的なsomething(応募課題晒し)
超久しぶりの投稿です。
ところで、
今年のセキュリティ・キャンプの募集が開始されましたー!!!
いやー、めでたい(?)っすね〜
で、なんかセキュキャン2016卒業生のプロ各位が応募用紙晒したり、それをまとめたスプレッドシート(黒歴史収集・・・?)
セキュリティキャンプ全国大会卒業者の応募用紙の情報が集まる魔法のスプレッドシート2017
— a== (@warugaki_k_k) 2017年4月28日
を作った
誰でも編集可能なのでよろしくお願いします。https://t.co/YCUUtzfGO2
が爆誕したりしているので、僕もなんか書いてみようかなー、と思いました。
このスプレッドシートに載っているようなプロ各位の記事を読むと、
セキュキャンの卒業生=任意のプロって感じがしますが、
僕は全然プロではないので、全然進捗無いので、プロではないので(大事なことなので2回言いました)、あんまり参考にはならないと思いますが、ノリで書きます。あまり参考にはなりませんが、まあそんなかんじでお願いします。
まずは、去年の僕が応募用紙に何書いたかを晒していきます(ウワアアアア)。
去年の問題は、今は https://www.ipa.go.jp/files/000053055.pdf にあるようです。では、初めの方から見ていってみましょう。
とりあえず目次です
■はじめにお読みください
※このページは、60分程度でセッションが切断され、入力内容が無効となります。そのため、回答内容は応募者ご自身が適宜保存しながら回答するよう、 くれぐれもご注意ください。 ※一時保存機能による回答内容の保存は48時間有効です。一時保存URLは、①有効期限を過ぎた場合、②再度一時保存をした場合(新しく別の一時保 存URLが発行され、古いURLは無効となります)、③回答内容を送信した場合に、無効となりますので、ご注意ください。
はい。これ超重要です。もしかしたら僕がこの記事で書いた一番重要な情報かもしれませんよ! セキュキャンの応募課題はクソ時間かかります。いや、プロだと一瞬で出来るのかもしれませんが、僕は無理です。というわけで、ちゃんと保存しましょう。僕は当時安定的に使えるPCが無かったのと、どこでも編集できるようにクラウドサービス上で作業しました。僕はセキュキャン後にgitを知ったのでアレですが、git管理とかしてもいいかもしれません。応募課題をgit管理、かっけえ。
あと、消えてしまうととてつもなく悲しくなるので、バックアップを定期的に取ってもいいかもしれません(?)。今年は一時保存機能も無いようですし。
※回答の締め切りは、【2016年5月30日 17:00】です。再提出を含め、締め切りまでに回答を送信してください。
あー、これも重要ですよ。〆切。どんなに時間をかけてでも1限死んででも応募課題には時間をかけるべき(かもしれない)、と言いたいところですが、そんなに頑張っても出せなかったらなんの意味もありません。全部水の泡です。超もったいないです。まあ僕が学校の課題でもやるべきことなんですが。
はい、これで僕が言えるアドバイスとかもう9割9分9厘終わりですね。あとの記事は残りカスみたいなもんです。黒歴史ですし。
というわけで後はダラダラと去年の僕が書いた応募用紙を晒していきますが、まあまさかこんなところを読む人なんていないだろうということで、黒歴史ぶちまけちゃいましょうか。
共通問題
では共通問題から。
共通問題.1
共通問題.1 (1)
あなたが今まで作ってきたものにはどのようなものがありますか? いくつでもいいので、ありったけ自慢してください。
来ましたよ、この自慢コーナー。イキリオタク案件です。僕はどうイキリオタクしたんだっけか・・・
・ロケット 小学生の頃からペットボトルロケットを作っていて、地学部に入ってからは先輩とペットボトルロケットを設計、製作、シミュレーション、実験を行っていた。先輩が卒業し、僕が部長兼宇宙科学班班長になってからは、「モデルロケット」という、火薬を使用したロケットの設計、製作、シミュレーション、実験を行っている。 ・ロケットシミュレーション 上述のロケットの設計及び、実験結果のフィードバックを論理的かつ効率的に行うために、物理シミュレーションを行っている。ロケットの軌道は空気抵抗にかなり左右されるため、空気抵抗の計算がかなり重要となる。現在は近似式で計算を行っているが、PC教室のPCを全てLANで接続してスーパーコンピューターのようにして有限要素法で正確な軌道計算を行う試みを考えもした。 ・流体シミュレーション 「粒子法入門」を読んで、SPH法を用いてナビエ‐ストークス方程式を離散化して解いて、流体シミュレーションを行っている。また、シミュレーションだけやっていてもつまらないので、ParaviewやPov-Rayによる可視化も行っている。 ・天体シミュレーション 流体シミュレーションで学んだ粒子法シミュレーションの手法を用いた上で、多体重力シミュレーションをどのようにやればよいか考えてプログラムを作っている。この成果は今年の部誌、ホルストに掲載する予定。 ・HELIOS 「OS自作入門」を読んで自分で作り始めたOS。この開発コードネーム(ヘリウムの語源で太陽の意)を思いついたときは、かなり中二病に近かった。そして実際に中2だった。2回ほど「はりぼてOS」を作り、少しづつ自分で考えてコーディングをした。現在は勉強とか部活とか忙しくて中断している。ちなみにAPMシャットダウン成功一歩手前まで行った(シャットダウンだけする16bitOSを作った)。アルゴリズムまで思いついたもののコーディングせずにほったらかしているアイデアがたくさんある。
アアアアアなんじゃこりゃあああああああ(吐血)
とりあえず次に進みます。。。
共通問題.1 (2)
それをどのように作りましたか?ソフトウェアの場合にはどんな言語で作ったのか、どんなライブラリを使ったのかなども教えてください。
・ロケット ペットボトルロケットは、ペットボトル・ビニールテープ・ホチキスで製作。小型カメラを搭載して動画撮影を行ったり、パラボリックフライトの真似事をして疑似無重力実験を行ったりした。 モデルロケットは、残念ながらまだモデルロケットライセンス4級を取得していないためA型エンジンしか使用することができないが、近いうちに取得する。今年の文化祭までにArduinoを用いた無線打ち上げ制御を行いたいと考えている。 ・ロケットシミュレーション 日本モデルロケット協会の本を参考に、最初はExcelでシミュレーションを行っていたが、計算が遅い・開くたびに計算する・あくまで表計算ソフトなので精度が期待できない、などの理由で、C/C++(MinGW)言語によるシミュレーションプログラム開発に移行した。後述する流体シミュレーションから多大な影響を受け、計算精度などの問題をまじめに考え始めた。現在は、モデルロケットのシミュレーションに対応するため、エンジンデータなどを調べている。 ・流体シミュレーション 丸善で見た「粒子法入門」に感銘を受たので、学校の図書館に購入させ(理工書は高い)、何度も読み返して(僕以外借りる人がいなかった)、実際にコンパイル・実行してみて、「大学院の物理って面白そう」と感じた。あまり僕が手を加えられた部分は少ないので、これは「自分が作ったもの」とはいいがたいが、これを参考に後述の天体シミュレーションを行っているのでお許しいただきたい。 ・天体シミュレーション 前述の粒子法シミュレーションを参考に、「あれ、国立天文台の4D2Uってスパコン要るだけでやってることはそんなに難しくないんじゃないか?」と自惚れて作り始めたもの。数値積分の面倒くささを再認識した。言語はC/C++(MinGW)。衝突判定が足りないので、天体同士が接近すると計算不安定が大変なことになる。 ・HELIOS 「OS自作入門」を読んで作り始めた名前中二病OS。今は開発を中断している。言語はC/C++(GO)。「WinAPIが面倒なら自分でOSを作ればいいのに」という僕の迷言を生み出した。動的リンクライブラリとか実装したい。APMシャットダウンがうまくいかないまま中断している。
あうあう・・・
まだ自覚している節があるのでマシといえばマシですが、なんだこの厨2はってかんじですね。嘘にならない範囲でギリギリのことを書いているというか。これだけ見ると強そう。まだ終わってないことも多いし。
でも、この問題は、応募してくる人の、「どのくらいイキリオタクしてくるか」、ようするにやる気を見ていると個人的に思っている(ホントか?)ので、応募する人はバンバンイキリオタクするといいと思います(さあ黒歴史を作るんだ)。
「俺に自慢できるものなんてねえよ」なんて人もいると思います。僕もそうでした。でもきっと大丈夫です。セキュリティ・キャンプは必ずしもプロを求めているわけではありません。もしそうなら僕は確実に落ちています。CTFの大会に出たり、CVEを持ってたり、言語作ったりしていたわけではありません。
こんな僕でもキャンプに行けた理由は、多分、セキュリティ・キャンプという事業が、「プロをさらにプロにする」というイベントではなく、人材発掘、ということに重点を置いているからだと思います。
「キャンプで成長するというよりも、あくまでキャンプはきっかけにしてほしい。」
こんなことをキャンプの偉い人が言ってました。これは本当にその通りだと思います。いやもちろん、キャンプに来た時点でプロでプロプロしてるプロもいたのですが、僕はキャンプで無双することなんて全然無理でした。今でこそ受けた講義の内容はなんとなく分かっている(はず)ですが、キャンプ中には全然イミワカランなんだこれ、みたいな講義もありました。
しかし、僕はセキュリティ・キャンプに行かなければ、「OS自作入門」の著者のKさんに会うことも無かったでしょうし、参加者のすごい人たちに会うことも無かったでしょうし、OSCにも行くことも無かったでしょうし、サイボウズ・ラボユースに応募してみようなんて微塵も思わなかったでしょう。最悪、プログラミングをやめていたかもしれません。
なにより、「こんなすごいことをしてる人たちがいるのか」ということを知ることができたのが、一番大きいと思っています。井の中の蛙が大海を知った訳です。海じゃなくて湖、ひょっとしたら池くらいかもしれませんが。
・・・あれ?まだ1つしか問題終わってないのに、4.7k文字・・・? なんかいいかんじのこと言ってますし、もう晒すの終わりで良いのでわ? え、ダメですか。仕方ないですね・・・
共通問題.2
共通問題.2 (1)
あなたが経験した中で印象に残っている技術的な壁はなんでしょうか?(例えば、C言語プログラムを複数ファイルに分割する方法)
・コンパイラがうまく動かなかった 部のPCで、学習用C言語開発環境(ようするにtcc)がうまく動かなかった。Hello, Worldぐらいは動くのだが、少し行数が増えただけで全く違うところでコンパイルエラーが発生し、コンパイルできてもおかしな挙動をした(コードがおかしいわけではなかった)。 仕方ないので、Visual Studio 2008をインストールしたが、何もコンパイルできなかった。 ・クラスの概念が理解できなかった 軽くCをやったあと、C++をやろうとしたが、オブジェクト指向が理解できなかった
今考えるともう少し原因究明しろよとか、何がどう理解出来なかったのか書こうよってかんじですが、当時は本当にこれに困ってたんですよね・・・
共通問題.2 (2)
また、その壁を乗り越えるために取った解決法を具体的に教えてください。(例えば、知人に勧められた「○○」という書籍を読んだ)
・コンパイラがうまく動かなかった なにを試してもうまくいかなかったが、MinGWとGOだけまともに動いたので、以後gccを多用し、IDEという文明の利器から離れていくことになる(eclipseは展開不良のため使えなかった)。そもそもPCに問題がある可能性がかなり大きい。 ・クラスの概念が理解できなかった アセンブラからやり直し、構造化プログラミングの意義を理解し、その延長線上としてのオブジェクト指向を理解した
_人人人人人人人人人人人人人人人_ > オブジェクト指向を理解した <  ̄YYYYYYYYYYYYYY ̄
ギャアアアアアアなんだこいつはアアアアアアア(黒歴史(吐血))
オブジェクト指向を理解とか、出来てないです。それこそJavaとかほとんどやったことないですし。
共通問題.2 (3)
・コンパイラがうまく動かなかった IDEなんて甘えです。古いPCだとうまく動かないし、遅いし、はたまたインストールできないことだってあります。別に、IDEを使うことは悪いとは思いません。生産性も上がるでしょう。なんだかんだで僕も今はたまにNetBeansとか使います。でも、IDEの恩恵を普通だと思ってしまったり、IDE特有の癖(特にVC++)とかが分からないと、IDEが使えないときや、何らかの事情で(例えば、OS作るとか)IDEを使えないときに、絶望しか味わえません。というわけで、コマンドラインコンパイルとかやるようにしましょう。 ・クラスの概念が理解できなかった アセンブラからやり直しましょう。オブジェクト指向は楽ですが、それしかやらないとなぜそれが要求されたのかも、実際にはどのように動いているのかも理解できません。というわけで、低層からやりましょう。
いや、あのですね、こんなこと書いちゃいましたが、IDEがダメとか甘えとかは別に思ってません(言い訳)。この前Visual Studio 2017を使ってみたんですが、ヤバかったです。あれ、コード書かないで色々出来てやべえ。
ただですね、ちょーっと言わせてもらうと、僕はINF回Visual StudioやらEclipseやらの有名なIDEのインストールにコケていた(大体スペック不足)ので、IDEには未だにトラウマが・・・
当時のプログラミング環境は、gcc(MinGW or GO) + TeraPad でしたね。今はgcc(build-essential) + vimですね。vimはいいぞ。
えーっと、次はクラス云々の話ですか。これもちょっと黒歴史ですね。
まあでも、アセンブラやるのは悪いことではないと思います。そこらへんで売ってるC言語の入門書とか読んでも、僕は無限にポインタのことが分かりませんでしたし。
というわけで「OS自作入門」はいいぞ(N回目)。 https://www.amazon.co.jp/30%E6%97%A5%E3%81%A7%E3%81%A7%E3%81%8D%E3%82%8B-OS%E8%87%AA%E4%BD%9C%E5%85%A5%E9%96%80-%E5%B7%9D%E5%90%88-%E7%A7%80%E5%AE%9F/dp/4839919844www.amazon.co.jp
みなさんもOS自作してアセンブラとC言語で遊びましょう!(低レイヤー沼)
次は講義の希望です。
共通問題.3
共通問題.3 (1)
あなたが今年のセキュリティ・キャンプで受講したいと思っている講義は何ですか?(複数可) そこで、どのようなことを学びたいですか?なぜそれを学びたいのですか?
■1-B BareMetalで遊ぶ Raspberry Pi 入門編 ラズパイを使ってみたかったので ■3-B フィジカル・リバースエンジニアリング入門 2歳ぐらいから分解大好きなので ■5-B USBメモリからブートしてみよう HELIOSのUSBブートで失敗したので ■2-C 人工知能とセキュリティ ディープラーニングの実装に失敗したので ■3・4・5E システムに新機能を追加したときのセキュリティを考えよう 川合さんの話はとても好きなので。また、あえて今「C言語に機能を追加する」というところに惹かれたから。
これ、「なんでセキュリティ・キャンプに行きたいのか」ってのに直結する質問なので、もう少し色々書くべきでしたね・・・
僕の場合、合格通知の後の講義の選択希望で、割と希望を変えましたし。
それこそ今年度の場合は、希望する講義で応募課題が変わってくるくらいですし、どの講義を希望するかはちゃんと考えて応募したほうがいいと思います。
共通問題.3 (2)
あなたがセキュリティ・キャンプでやりたいことは何ですか?身につけたいものは何ですか?(複数可) 自由に答えてください。
・自分と同じようなことに興味を持つ人と出会いたい(基本ぼっち) ・今自分が作っているものの参考にしたい ・アイデアの発想の仕方を学びたい ・セキュリティについて詳しく学びたい
あー・・・これも、もう少し色々書いておくべきでした。 なにを言いたいのかはなんとなくは分かるのですが、情報がとても少ないですね・・・
ただ、応募した時は、正直自分が何をやりたいのか、キャンプで何を学べるのかとかがイマイチ分かっていなかったので、ある意味しょうがなかったかもしれません。
まあ自分が何をやりたいのか、なんてことは今でもあまり分かってないのですが、キャンプに行ったことで割と考えさせられたところはありますね。
選択問題
以降は選択問題です。
どの問題を選択するかはかなり迷ったのですが、知識が圧倒的に足りなかったので、どうにかして答えられそうなものを選びました。
ただ、もう少しよく調べたり、考えたりして書けばよかったかなあと思っています。
選択問題.3
RAMは主記憶装置、HDDやSSDなどは補助記憶装置と呼ばれます。一般にCPUは主記憶装置上のプログラムしか実行できません。ではなぜ、私たちは普段から補助記憶装 置に書き込んだプログラムを実行できているのでしょうか?パソコンの電源を入れてからのストーリーを考えてみてください。
PCの電源を入れると、ROMなどに書き込まれたBIOSが自動的に起動し、BIOSがOSを呼び出すための初期プログラムローダ(IPL)が書かれたブートセクタ(MBR)をHDD、SSD、CDなどの補助記憶装置から事前に登録された順番で探していく。そして、IPLを発見すると、IPLを実行する。IPLは1セクタ分、つまり512バイト分しかないので、OSを起動するためのプログラムを主記憶装置に転送するプログラムが書かれている(はりぼてOSではサイズが小さく、HDD読み込みができないのでOSとそのほかのファイルを全て転送する)。こうして主記憶に転送されたプログラムが、さらに必要なデータを補助記憶から転送するなどしてOSが起動する準備を行う。こうして、ようやくOSが起動する準備が完了し、プログラムがOS本体を実行する(正確にはジャンプする)。 OSが起動した後は、OSがおそらくout命令で補助記憶装置の制御チップに色々と命令を送って補助記憶から主記憶にデータを転送させている。
はい。ブートプロセスを考えろってことですね。僕がプログラミングを始めたきっかけが「OS自作入門」なので、この問題(と次の問題)を見つけたときは、「アーッこれあの本で見た!」って思いましたね。というか、これらの問題があったから応募してみた、という節もあるかもしれません。
でも、これも今見ると理解がイマイチでしたね・・・
書いた時ははりぼてOSのことしか知らなかったのでこうなったのですが、Linuxなどのブートプロセスも調べたほうが良かったと思います(GRUBとか)。
選択問題.5
PCなどに搭載されているOSは「汎用OS」と呼ばれますが、それに対して、家電やAV機器などの「組込みシステム」に搭載されているOSは「組込みOS」と呼ばれます。 組込みOSと汎用OSの違い、「OSが無い」や「ベアメタル」という環境、そもそもOSとは何なのか?など、あなた自身はどう考えているのかを、 あなた自身の言葉で自由に説明してください。(「正しい答え」を聞いているわけではありません。あなた自身の考えを教えてください)
正直、「OSはこうあるべきだ」というような明確な定義は無意味だし、必要ないと思う。「カーネルがどうこう」なんて話はこの際無視する。僕が考えるOSとは、本当の意味での「オペレーティングシステム」、つまり、なんらかの電子機器を直接制御するシステムのことだ。それは、家電製品を制御しているかもしれないし、人工衛星を制御しているかもしれないし、はたまたPCを制御して、その上でアプリケーションを実行しているかもしれない。タブレットやPCを制御し、そのうえで様々なアプリケーションを実行して多くのエンドユーザーがいるのであれば、それは「汎用」OSと呼ばれるのだろうし、人工衛星や、ロケットや、家電製品など、特定作業に特化した電子機器を制御するため、必要な機能のみを必要なICなどに「組み込まれた」OSは、「組み込みOS」と呼ばれるのだと思う。
この考え方は、割と今も変わってないですね。でも、自作OSもくもく会とかに行くと、人によってOSの定義、とでもいうものが色々あるので、「OSってなんだろう」みたいな問いは、これからも考えていきたいですね。
選択問題.6
IDとパスワードを入力してユーザの認証を行うWebアプリがあります。あなたがこのアプリに対してセキュリティテストを行う場合、まず、どのようなテストをします か? なぜそのテストを選択したのか、その背景や技術的根拠と共に記載してください。アプリの内部で使われている技術やシステム構成に、前提を置いても構いません。
認証される組み合わせがくるまで延々とIDとパスワードを生成し、入力してくる辞書攻撃に対応できるか判断するため、IDとパスワードを延々と生成し入力するプログラムを作り、このアプリに対して攻撃を行う。そして、これにクリアした後は、「同じIPアドレスから大量に認証申請が来ている」ということだけをもとにして前回の攻撃をクリアしたのではないか、という観点から、常時IPを変えつつ前回と同じ攻撃を行う。
う、う〜ん、これだけかよってかんじですね・・・(小並感)
Webアプリとかなにも分かんなかったんです!許してくださいなんでもしますから(なんでもするとは言っていない)
選択問題.9
マイナンバーカードの配布システムを構築・運用することになりました。あなたなら何に気をつけてどんなサービスを構築しますか? マイナンバーカードの仕様は現実通りのICカードとします。 ※ 注意: マイナンバーでは無くマイナンバーカードです。
・ICカードは少し離れていても情報を読み取ることが可能なので、それを防ぐために、磁気などを遮断する厳重な金庫に入れて輸送する ・配布要員が不正を行わないようにするため、作業員は全員ICタグなどで出入を管理し、配達用の車両の現在位置を追尾する ・苦情に迅速に対応するため事前に苦情対策について行政と協議する ・配達員にタブレットを配布し、配達時に本人確認を行い、タブレットで配達完了を管理センターに知らせるようにする
マイナ◯バーカードwww
なんか、誤配達とかあって大変だったらしいですね。結構当時としてはタイムリーな話題でしたね。 一応そこも踏まえて書いたような気がしていたのですが、これだと全然伝わらないですね。しかも箇条書き・・・
終わりに
はい。これで僕の黒歴史晒しは終わりです。書いててちょっとつらかった・・・
他の過去の参加者のみなさんの応募用紙と比べると、あまりにもクソということが分かっていただけたと思います。
もし、「キャンプ勢とか強そう・・・自分には無理だ・・・」と思って応募を諦めようとしている人が「こんなのでも受かるのか!」と思っていただけたなら幸いです。
何回でも言いますが、セキュリティ・キャンプは応募に際して技術力を要求していません。それはこの記事を見れば明白です。
しかし、だからといって応募課題をテキトーにやっていいわけでは決してありません。こんな応募課題でも当時の僕はかなり時間をかけて書きましたし(時間かけてこれかよとか言わないであげて・・・死んじゃう・・・)。
特に、今年の応募課題は去年のものと比較しても時間をかけて試行錯誤することを要求している(気がする)ので、今年応募する人は頑張ってほしいですね。
今年のセキュリティ・キャンプはとてもパワーアップしていてすごく面白そうなので、今年応募する人、マジで応援してます!!!
僕もチューターやりたいので頑張ります!(あまりにも面白そうなので行きたくなった)
CTF for ビギナーズ に当選した
@hiwwさんのお誘いでTeam Harekazeの一員になったものの、stringsぐらいしか出来ない情弱なので申し訳ない気持ちになっていた。ハリネズミ本も終わってないし。
マトモにやれたのはsometa1ぐらいじゃなかろうか(といっても解けなかったのだけれど)。
そんなこんなでCTF力のNASAを感じていたので、ctf4bにダメ元で申し込んでみた。抽選になるほどの人気イベントなので(そもそも、抽選で参加出来なかった人がたくさんいたから今回追加でやることになったらしい)、まあどうせ受からんだろと高を括っていたら、
当選してしまった(びっくり)。
ということで、CTFも頑張りたいと思います。
OpenGLでウィンドウのクローズにちょっとつまづいた話
自作エミュレータで自作OSを動かしてみようとした話 - sksat’s diary で書いたように、OpenGLを使ってQEMUやBochsのようなx86エミュレータっぽいものを作っているのですが、ウィンドウのクローズに関してちょっとつまづいたのでメモしておきます。
何が起こったのか
今僕が作っているエミュレータでは、画面表示のエミュレーションにOpenGL(freeglut)を使っているのですが、機械語の実行が遅れると困るので、OpenGLの初期設定やメッセージループは別のスレッドで実行しています。ちょっとGUIスレッドの部分のソースコードを一部抜粋すると、
void GUI::ThreadProc(){ int argc = 1; char *argv = new char[1]; glutInit(&argc, &argv); // fake command-line args glutInitDisplayMode(GLUT_RGBA); glutInitWindowSize(scrnx, scrny); hMainWin = glutCreateWindow("display"); while(msgflg){ //message loop glutMainLoopEvent(); this->display(); } glutDestroyWindow(hMainWin); } GUI::~GUI(){ msgflg = false; hThread->detach(); delete hThread; }
こんなかんじになります。GUIというクラスにGUIの制御を任せていて、GUIクラスのデストラクタが呼ばれるとフラグがfalseになってメッセージループが停止し、glutDestroyWindow()
が呼ばれてウィンドウが閉じるようになっています。
そして、このGUIクラスを制御しているmain.cppは、
Emulator *emu; GUI *gui; Display *disp; int main(int argc, char **argv){ emu = new Emulator(); disp = new Display(emu->memory + VRAM_ADDR); gui = new GUI(disp); /* 中略 */ gui->OpenWindow(); //これがスレッドを起動させる while(true){ /* 中略(機械語の実行) */ } delete emu; delete gui; delete disp; }
というようになっています。つまり、エミュレータが途中で停止すれば自動的にウィンドウが閉じられるというわけです。
では、EAXに0xffを代入するだけの簡単なプログラム(MOV EAX,0xff
)をこのエミュレータで実行してみましょう。
一瞬で終了しました。それもそのはず、メインスレッドで、MOV EAX,0xff
を実行したすぐあと、EIPの指すメモリに実装されていない命令がある(正確には、なにも命令を配置していないので0x00があり、また0x00の機械語を実装していない)ので、エラーを出してメインスレッドの機械語実行ループが終了、delete gui;
によりGUIクラスのデストラクタ内で、glutDestroyWindow()
が呼ばれ、ウィンドウが目に見える間もなく閉じられてしまっているからです。
これだとせっかく実装したウィンドウが見えないので、ウィンドウが見えるように、実行させる機械語にJMP 0x7c00
を追加してループさせてみましょう(一応x86エミュレータなので、プログラムは0x7c00番地にロードする仕様になっている)。
うん、ちゃんと画面描画できてますね〜、って、ん???
あれ、これ、おかしくないですか?ウィンドウ閉じたら機械語の実行も止まるなんてコード、僕書いた覚えありませんよ?
まあ、実際のところ☓ボタンを押したらエミュレータには終了してもらいたいですが、それでも、意図しない動作というのは嫌なものです。
というか、最後にレジスタの値を表示するのができていないので、これはかなり重大なバグと言っていいでしょう。メモリもちゃんと解放できてるのか心配です。
うーん、何がダメなんでしょうか。 ウィンドウの細かいことは全部OpenGlに丸投げしてしまっていますが、☓ボタンが押された時にどのような処理がされているのかちょっと追ってみましょう。
while(msgflg){ //message loop glutMainLoopEvent(); this->display(); } glutDestroyWindow(hMainWin);
メッセージループに入っている間はウィンドウの処理は全部glutMainLoopEvent()
でした...
つまり、glutMainLoopEvent()
かその中で呼ばれている関数が☓ボタンの処理を行っているはずです。
しかし、考えてみると、☓ボタンが押されたらメッセージループは終了するべきですが、glutMainLoopEvent()``内からメッセージループを終了させることができないようになっています。
もしかしたら、
glutMainLoop()では
glutMainLoopEvent()の返り値を見てメッセージループを終了させているのかもしれません。
ということで、
glutMainLoopEvent()```の返り値を見てみます。
とりあえずintでいいかなと思ったんですが、voidだったんですね...コンパイラに怒られてしまいました。
glutMainLoopEvent()
の返り値が無いとすると、次に考えられるのは、☓ボタンが押されるというイベントを処理するコールバック関数を登録する関数があるのではないか、ということです。
ではどんな関数があるのか、たまたますぐ近くにlibfreeglut.a
が転がっていたので(Windows用な気もするけれど)、objdumpで調べてみます。
objdump -D libfreeglut.a
「お〜、こんな関数あったんだ〜」と思いながらスクロールしていくと、おや? なんかそれっぽい関数名が出てきました。コールバック関数は「glut***Func」という名前なので、これは怪しいです。
ということで、さっそく関数を登録してみましょう。
void close(){ cout<<"close"<<endl; } void GUI::ThreadProc(){ /* 中略 */ glutCloseFunc(close); while(msgflg){ glutMainLoopEvent(); } glutDestroyWindow(hMainWin); }
...どこにいるんだよclose...
どこかにいるかもしれませんので見てみます。./vm test02.bin | grep close
うん、ちゃんといました。ウィンドウが閉じるときにちゃんと呼ばれているようです。しかし、結局はプロセスが終了しています。closeイベントが発生してglutCloseFuncで登録した関数が呼ばれた後にexit()
かなにかを呼んでいるのでしょうか。
exit()
が呼ばれているかどうかが気になったので、ちょっと`ltrace ./vm test02.bin
とかやってみました。
わからん...ostreamとか書いてあるのは標準出力関係か...マルチスレッドだしな...(しかしシングルスレッドで試してみてもよく分からなかった..力不足...)
よく分からなかったのですが、せっかくウィンドウのクローズ時にcloseが呼ばれるようになったので、close内でglutDestroyWindow()
してみます。
int g_msgflg, g_hMainWin; void close(){ cout<<"close"<<endl; glutDestroyWindow(g_hMainWin); g_msgflg = false; } //ThreadProcの一部 void GUI::ThreadProc(){ g_msgflg = true; while(g_msgflg){ glutMainLoopEvent(); this->display(); } //glutDestroyWindw(hMainWin); }
ハードコーディングは気にしないでください...
では、実行してみます。
正常終了どころかコアダンプ...むむう...
後日談、というか、今回のオチ
まだ調べ途中で結局できていないのですが、ここまで読んでくれた人ならお分かりでしょう。
「公式ドキュメント見ろよ!!! というか、関数名調べるだけならヘッダ見ればいいでしょ!!!」
はい、全くもってその通りです。CTFの本読んだからって調子に乗りました。
自作エミュレータで自作OSを動かしてみようとした話
これは自作OS Advent Calendar 2016の22日目の記事です。
『30日でできる! OS自作入門』という(このアドベントカレンダーの5,6,9,15日目の記事のKさん著)本があります。 この本はhikaliumさんも書かれていましたが、まさに「OS自作における聖書」といえるような本で、例によって今回やったこともこの本が発端となっています。
実際何をやったのかというと、タイトルの通り「自作OSを自作エミュレータで動かしてみたい」と思ったので色々やってみた、ということです。
エミュレータと自作OS
突然ですが、「ソフトウェアを開発する」ということに着目した時、一般的なアプリケーションの開発とOSの開発の一番の違いはなんでしょうか? 僕は、「デバッグに仮想環境を使うか使わないか」ではないかと思います。一般的なアプリケーション開発ではデバッグしようと思ったらとりあえず実行してみればいいですが、OSだとそうもいきません。なぜなら、OSを作って動作確認をするとき、いちいちCDやDVDに焼いたり、ハードディスクを犠牲にするのは面倒だからです(また、ファイルシステムを作っている時などに安易に実機で実行してしまうと重要なファイルを壊してしまうかもしれません)。そのため、OSを開発するときにはよくエミュレータ(仮想環境)を使います。QEMUやBochsなどですね。
しかし、ここで一つ疑問が生じます。一体全体この「エミュレータ」というプログラムは、「どうやってコンピュータをエミュレーションしているのか?」ということです。OSというとかなり低レイヤーな分野ですが、エミュレータを作ろうと思ったらそれより下層のハードウェアなどの動作を再現しなければなりません。ということで、エミュレータがどのような仕組みで動いているのかが気になりました。幸いにしてQEMUはオープンソースですので、思い立ったが吉日、QEMUのソースを見てみましょう。
まずはGitHubからソースコードを取ってきます。とりあえずどんなファイルがあるのか見てみましょう。
$ git clone https://github.com/qemu/qemu.git $ cd qemu $ ls
ごめんなさい、ファイルがたくさんありすぎて何を見ればいいのかわかりましぇん。。。 ということで僕のエミュレータへの思考はかなり断絶していました(QEMU開発者の方、すみません・・・)。
しかし、今年の夏にあったセキュリティキャンプ全国大会参加前に、色々な講義の事前課題を眺めていたら、「USBメモリからブートしてみよう」というなかなかそそられる講義を受ける人には「自作エミュレータで学ぶ x86アーキテクチャ」(このアドベントカレンダーの1日目,8日目,18日目のuchanさん著)なる本が教材として与えられるとのこと。 なんだそのとても面白そうな本は・・・ 残念ながら、僕はその講義の時間帯は別の講義を取っていたのでこの本を貰うことはありませんでしたが、とても気になっていたので、学校の文化祭も終わって落ち着いた10月に買いました。
「自作エミュレータで学ぶ x86アーキテクチャ」のエミュレータ
僕なんかが書くより本を読んでサポートページからtolset_p86をダウンロードしてソースコードを見たほうが速いとは思いますが、本のエミュレータがどのような仕組みなのか簡単に説明すると、
- 各レジスタに対応した変数を用意する(構造体にしてまとめてある)
- 仮想マシン用のメモリを用意する(とりあえず1MB)
- 外部から実行するバイナリファイルを読み込んで、用意したメモリの0x7c00番地にコピーする(512バイト)
- エミュレータのEIPに0x7c00を代入
- EIPがメモリをあふれない範囲でループして、EIPの番地の機械語を逐次実行していく
typedef struct { uint32_t registers[REGISTERS_COUNT]; // 汎用レジスタ uint32_t eflags; // EFLAGSレジスタ(キャリーフラグなど) uint8_t* memory; // メモリのアドレス uint32_t eip; // EIPレジスタ } Emulator; int main(int argc, char **argv){ /* 中略(初期化処理)*/ while(emu->eip < MEMORY_SIZE){ uint8_t code = get_code8(emu, 0); /* 中略(ログ出力したり、実装してない命令がきたらbreakしたり)*/ instructions[code](emu); // 命令の実行 if(emu->eip == 0){ break; } } /* 中略(終了処理)*/ return 0; }
instructionsというのは関数ポインタの配列で、なるほど頭いいなあと思ったのですが、ここに各機械語に対応した関数のアドレスを入れています。
で、結局なにをやったのか
はい。実はここからが本題です。 先ほど紹介したエミュレータプログラムを見て、僕は「おお!これにどんどん機能追加していけばQEMUやBochsみたいなことができるのでは?」と思いました。 では、その目標のために僕がどのようなことをしたのかを書いていきたいと思います。
まずは、すでにあるプログラムをC++で書き直しました。なんでそんなことをしたのかというと、一から書き直すことでちゃんとプログラムを理解したかったのと、あとはクラスにまとめてみたかったからです(ここは趣味ですね...)。
次に、OSを起動するためにはどのような機能をエミュレーションする必要があるかを考えました。とは言っても、僕がある程度中身を知っているOSははりぼてOSぐらいしかないので、はりぼてOSのエミュレーションに必要な機能を考えてみました。
- 起動時は16bitモードで、あとから32bitモードに遷移できる
- リアルモードとプロテクトモード
- セグメンテーション
- 割り込み処理
- 画面表示
- デバイスが使える
他にもあるかもしれませんが、簡単にまとめるとこんなかんじでしょうか。 この中で、デバイスについては本でIN,OUTが実装されているので、各ポートに対応した外部装置を実装していけばいいですかね。 リアルモード・プロテクトモードはどう違うのかイマイチよく分かっていないので今はパスです。 セグメンテーションや割り込みも難しそうなのでとりあえず後回しですね。
ということで、色々良くわからないところは飛ばして(いやIntelの資料とか見ろよというかんじですが)、画面表示をやってみたいと思いました。
ウィンドウを出す
画面表示というと、まずはウィンドウが無いとどうしようもありません。ウィンドウを作りましょう。
ウィンドウを作っていきたいのですが、去年ぐらいの僕がこんなことを考えると、Win32 APIを叩き初めてしまいます。まあそれでもできなくはないですが、最近Windowsマシンを学校の情報の授業以外で起動すらしていないので、ちょっとダメです。ということで、描画ライブラリを使いましょう。
さて、描画ライブラリを選ぶことになったわけですが、残念ながら僕はよく使われてる描画ライブラリがあんまり好きじゃなくてWin32 APIに走ったという経緯があるので、よく使われる描画ライブラリをまともに触ったことがありません。
じゃあどうするんだというと、流石に汎用性の高い描画ライブラリを作っていたら日が何回も暮れてしまうので、OpenGL、正確にはfreeglutを使うことにしました。
なんでOpenGLなのかというと、ほんのちょっとだけ使ったことがあるのと、設計がかなり汎用性を重視してるように思えたからです。
あとは、開発は基本的にUbuntuですが、Windowsでも使えたほうがいいだろうということでマルチプラットフォーム対応なものとして選びました。
なんでglutじゃなくてfreeglutなのかというのは、実はやってる途中に色々あって変えたので今回は割愛します。
また、ウィンドウ、というかGUIを作るためにはメッセージループを行わなければいけません。しかし、エミュレーションをしながらメッセージを処理するのはソースコードもぐちゃぐちゃになりますし僕もよくわからなくなります。そこで、新しくGUIのメッセージループ用にスレッドを作ることにしました。そして、スレッドやウィンドウの処理をGUIというクラスにまとめました。
class GUI { private: std::thread *hThread; bool msgflg; // メッセージループの制御フラグ int scrnx, scrny; // 描画するX,Y方向サイズ void ThreadProc(); // メッセージループスレッド void display(); // 描画処理 public: GUI(); ~GUI(); void OpenWindow(); }
しかし、ここで問題が発生しました。OpenGLを使ったプログラムというと、
void display(void); int main(int argc, char **argv){ glutInit(&argc, argv); glutCreateWindow("title"); glutDisplayFunc(display); glutMainLoop(); // 帰ってこない }
とかやるのが普通なんです。でもこれは困ります。glutMainLoop()は決して帰ってこないので、メインスレッドの方で、「エミュレーションが終わったからウィンドウも閉じたい!」と思っても、何かのフラグを変えてglutMainLoop()から脱出する、というようなことはできません(後から考えてみればできなくはなかったけれどいずれにしろきれいに書けない)。じゃあどうするんだ、ということでglutMainLoopについて調べていたらちょうどいい関数を見つけました。glutMainLoopEvent()という関数です。これを
while(true){ glutMainLoopEvent(); }
というようにループに入れてやればglutMainLoop()の代わりになります。恐らく、Win32 APIで言うところのGetMessage()とTlanslateMessage()を合わせたようなものでしょう。
画面表示の仕組み
ようやく画面表示を実装していく準備が整いました。それでは、画面表示がどのように行われているかを『30日でできる! OS自作入門』で復習してみました。
[VRAM]に0xa0000を入れているのですが、PCの世界でVRAMというのはビデオラムのことで「video RAM」と書き、画面用のメモリのことです。このメモリは、もちろんデータを記憶することがいつも通りできます。
しかしVRAMは普通のメモリ以上の存在で、それぞれの番地が画面上の画素に対応していて、これを利用することで画面に絵を出すことができるのです。
つまり、VRAMにあるデータを見てその通りにウィンドウに描画していけばどうにかなりそうですね。
ではまず、描画関数であるGUI::displayを整備します。
void GUI::display(){ glClearColor(0.0, 0.0, 0.0, 0.0); // 背景色を黒に設定 glClear(GL_COLOR_BUFFER_BIT); glRasterPos2f(-1, 1); // ラスター座標変更 glDrawPixels(scrnx, scrny, GL_RGB, GL_UNSIGNED_BYTE, img); // unsigned char配列の中身を描画 glFlush(); // 描画を反映させる }
表示するRGBデータを保存するunsigned char配列であるimgをGUIクラスに加えて、glDrawPixels()という関数で描画するようにしました。 これでimgにいいかんじにRGBデータを書き込んでやれば、このようにちゃんと表示されます。
これでもう画面表示のエミュレーションができたような気持ちになってしまいますが、実はそうではありません。
glDrawPixels()で指定しているアドレスはimgであって(Emulator.memory + VRAM_ADDR)
ではないのです。
何故VRAMを直接glDrawPixels()に渡してはいけないのかというと、実はVRAMに保存してあるのはRGBデータではなく、0x00から0xffまでの色番号だからです。
これではRGBで色を指定できないように思えますが、実はRGBデータはパレットという外部装置に、「どの色番号をどのRGBと対応させるか」ということをあらかじめ設定しておくという仕様になっているので、VRAMには色番号を書くだけで画面描画ができるようになっているのです。おそらく、画面描画を高速に行えるように、このように設計されているのでしょう。
ということで、このエミュレータでもパレットにRGBを設定して、VRAMを読んでその色番号に登録されているRGBで描画するようにしたいと思います。
パレットの作り方ですが、ようは0xff個のRGBデータがあればいいので、unsigned char palette[0xff * 3];
でいいでしょう。
そして、これは外部装置なのでin/outでpaletteのポート(0x03c7, 0x03c8, 0x0xc9)が来たときに設定とか読み出しができればいいわけです。
今回はin/outについていじる気はないのでやりませんが、外部装置はDeviceクラスを継承して作るようにしたので、Displayクラスを作ってその中にpaletteを置いています。
class Display : public Device { private: unsigned char palett[oxff * 3]; uint8_t *vram; unsigned char *img; int scrnx, scrny; void init_palette(); public: Display(uint8_t *vram); ~Display(); unsigned char* Draw(); };
今回はin/outについていじる気は無いので、init_palette()
でパレットの初期設定をしてしまいます。
void Display::init_palette(){ static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xc6, 0xc6, 0x84, 0x00, 0x00, 0x00, 0x84, 0x00, 0x84, 0x84, 0x00, 0x00, 0x00, 0x84, 0x84, 0x00, 0x84, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84 }; for(int i=0; i<(16*3); i++){ palette[i] = table_rgb[i]; } }
やっていることは「はりぼてOS」の画面表示初期化関数であるinit_palette()
とほとんど同じです。
設定するRGBのデータをそのままpaletteにコピーしています。
パレットができたので、次は画面描画をパレットに対応させます。ここまでできているので、あとはそんなに難しくありません。
unsigned char* Display::Draw(){ for(int x=0;x<scrnx;x++){ for(int y=0;y<scrny;y++){ int i=(y*scrnx + x)*3; char n = vram[y*scrnx + x]; //当該座標の色番号 img[i] = palette[n*3]; //色番号に対応したRGB img[i+1]= palette[n*3+1]; //green img[i+2]= palette[n*3+2]; //blue } } return img; }
Displayクラスの中にDraw()
という関数を作りました。この関数はVRAMから色番号を読んで、その色番号のパレットのRGBを参照してRGBの画面データを作るものです。あとはこの関数で作った画面データをglDrawPixels()
で指定してやれば、VRAMの内容を反映して画面描画できるようになるはずです。
void GUI::display(){ glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glRasterPos2f(-1,1); glDrawPixels(scrnx, scrny, GL_RGB, GL_UNSIGNED_BYTE, disp->Draw()); glFlush(); }
glDrawPixels()
で指定している画面データがimgからDisplay.Draw()になっただけですね。
ではmake run
して動作確認してみましょう。
何も表示されません...
まあそれはそうですね。いまのところVRAMに何も書き込んでいないわけですから(正確には、色番号0が黒に設定されているから)。
というわけで、VRAMになにか書き込んでみましょう。
では、『OS自作入門』の4日目の「画面表示の練習」と同程度のことをやってみます。
ここで、VRAMにデータを書き込むバイナリを読み込んでできれば文句はないのですが、僕が機械語の実装をサボっていた(本にすでにあるものぐらいやっとけよ、というかんじですね・・・)ので、バイナリのほうからメモリに値を書き込むことができません。そこで、main.cpp内でVRAMに書き込んでしまいます。
int i; char *p; for(i = 0xa0000; i< = 0xaffff; i++){ p = (char*)(emu->memory + i); *p = i & 0x0f; }
これでVRAMに書き込めたはずなので、make run
してみます。
おお!ちゃんとしましま模様が表示されました!成功です!
終わりに
(内部からVRAMに書き込むというズルはしていますが)画面表示のエミュレーションが一通りできたので、この記事はここまでです。ここまで見てくださり、ありがとうございました!
とりあえず、しばらくはバイナリのほうからVRAMに書き込めるようにすることを目標にして頑張ってみます。進捗が上がったらまたなにか書くかもしれません。
この記事で作っているエミュレータは、以下のリポジトリで公開しています。良かったら見てみてください。
「はりぼてOS」の背景を表示してみた(おまけ)
内部でboxfill()
を作って、はりぼてOSと同じ値でboxfill()
しています。