Yabu.log

ITなどの雑記

第13回osdev-jpもくもく会に参加してLTしました

非常に有意義な1日でした。

LT

https://speakerdeck.com/yuyabu/gong-kai-yong

スパコンのOSを書いてる人、RustでOSを作っている人(2人も!)が登壇したりなどで、 色々と格差を感じるものがありましたがなんとかここ半月ほど調べていたことを20分くらいで話すことが出来ました。

ここ1週間くらいは日課のランニングができなかったり、3ヶ月ほど毎日書いていたブログが一切かけないくらいデバッグ/調査の日々でした。 前日は4時くらいまで起きて発表内容をまとめていたので、当日はちょっと疲労困憊でろれつが回っていませんでした。(解放されてちょっとホッとしている)

初めてのLTで不慣れなこともあり、上級者の人にとってはあまり面白いものではなかったと思うし、初心者の人には何を言っているか意味不明な発表になってしまっていたと思います。当日は会場の入り口で迷ったり、初めての参加で雰囲気に慣れなかったりなどでとにかく余裕がなかった。もうちょっと聞く側のことを考えるべきだと思います。

私は発表の中でデバッグを始めるのは簡単、と言いましたが、何点かハマりポイントがありますので、後日解説するブログを書きたいと思います。

解消された疑問

CPUに超絶詳しそうな人が目の前にいたので日頃の疑問を聞いて見ました。

CRレジスタのPEフラグが立った状態でセグメントレジスタ(CS)が0を指している状態はどういう意味か?

GDTの0番目のエントリはnullセレクタを格納するように決まっています。

Because the first entry of the GDT is not used by the processor, a selector that has an index of zero and a table indicator of zero (i.e., a selector that points to the first entry of the GDT), can be used as a null selector. The processor does not cause an exception when a segment register (other than CS or SS) is loaded with a null selector.

これはhariboteOSもその通りになっています

GDT0:
        RESB    8               ; ヌルセレクタ ※RESBはオペランドで指定したバイト数分0で埋める命令
        DW      0xffff,0x0000,0x9200,0x00cf ; 読み書き可能セグメント32bit
        DW      0xffff,0x0000,0x9a28,0x0047 ; 実行可能セグメント32bit(bootpack用)

ですが、null selectorをセグメントレジスタで指定した場合は一般保護例外が発生します。

が、以下にhariboteOSの2段目のIPLでプロテクトモードに切り替えている箇所を張りますが、CR0レジスタのPEフラグを立てた後(7行目)、CSレジスタ以外のセグメントレジスタを8にしてCSレジスタが0のまま OS本体をコピーする処理(bootpackの転送のコメント以下)が動作しています。

; プロテクトモード移行

        LGDT    [GDTR0]         ; 暫定GDTを設定
        MOV     EAX,CR0
        AND     EAX,0x7fffffff  ; bit31を0にする(ページング禁止のため)
        OR      EAX,0x00000001  ; bit0を1にする(プロテクトモード移行のため)
        MOV     CR0,EAX
        JMP     pipelineflush
pipelineflush:
        MOV     AX,1*8          ;  読み書き可能セグメント32bit
        MOV     DS,AX
        MOV     ES,AX
        MOV     FS,AX
        MOV     GS,AX
        MOV     SS,AX

; bootpackの転送

        MOV     ESI,bootpack    ; 転送元
        MOV     EDI,BOTPAK      ; 転送先
        MOV     ECX,512*1024/4
        CALL    memcpy

; ついでにディスクデータも本来の位置へ転送

; まずはブートセクタから

        MOV     ESI,0x7c00      ; 転送元
        MOV     EDI,DSKCAC      ; 転送先
        MOV     ECX,512/4
        CALL    memcpy

; 残り全部

        MOV     ESI,DSKCAC0+512 ; 転送元
        MOV     EDI,DSKCAC+512  ; 転送先
        MOV     ECX,0
        MOV     CL,BYTE [CYLS]
        IMUL    ECX,512*18*2/4  ; シリンダ数からバイト数/4に変換
        SUB     ECX,512/4       ; IPLの分だけ差し引く
        CALL    memcpy

; asmheadでしなければいけないことは全部し終わったので、
;   あとはbootpackに任せる

; bootpackの起動

        MOV     EBX,BOTPAK
        MOV     ECX,[EBX+16]
        ADD     ECX,3           ; ECX += 3;
        SHR     ECX,2           ; ECX /= 4;
        JZ      skip            ; 転送するべきものがない
        MOV     ESI,[EBX+20]    ; 転送元
        ADD     ESI,EBX
        MOV     EDI,[EBX+12]    ; 転送先
        CALL    memcpy
skip:
        MOV     ESP,[EBX+12]    ; スタック初期値
        JMP     DWORD 2*8:0x0000001b

この状態の時CSレジスタは0つまりnull selectorを指しているのではないか?なぜ一般保護例外が上がらずに動いていると疑問を持っていましたが、CPUがリアルモードからプロテクトモードに変わるタイミングはPEフラグに1を入れた後ではなく、そのあとにJMP命令(多分far jump)をしたタイミングだったのです。

CPUのドキュメントにもPEフラグを立てた後はJUMP命令を実行せよと書かれています

Immediately after setting the PE flag, the initialization code must flush the processor's instruction prefetch queue by executing a JMP instruction.

出典:INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986/10.3 Switching to Protected Mode*1

だとするとPEビットONからfar JUMPまでの間、変な状態でメモリ書き込みを行っているような気がしますね。

Haribote OSをCやnasmの情報付きでデバッグすることはできるか?

スライドを見ればわかる通り、デバッグはシンボル情報なしで機械語をステップ実行する形で行いました。 一般的にデバッグは普通機械語そのものではなく、コンパイル前のプログラムに対してステップ実行すると思いますが、 デバッグ情報付きでOSをコンパイルする方法がわからなかったので、*2相談してみたところできなさそうです。

ただし、著者オリジナルのツールを一切使わずにhariboteOSを作り上げた場合はその限りではないかもしれません。*3

もくもく会の感想

もくもく会ですが、強い人多すぎ!みたいな感想多いですが、 教育に関わっている人や学生の人、社会人で趣味でやってる人、本業の人など 参加者の層のボリュームが厚いというのが私の印象です。

私もこの会は3回ほど前から参加してみたいな〜と思いつつ 勝手にOS自作本読破が義務教育と思い込んで尻込みしていたのですが、 割と現在進められている方が参加されてかなり有意義なサポートを受けられている様子も見受けられるので、尻込みしている方もどんどん参加すればいいんじゃないかなと思います。

普段はこのレイヤーのトークができる人が周りにいないので雑談するだけでも楽しかったです。

あとサイボウズさんの食堂?的なスペースかっこいいですね。

もくもく

あまり作業出来ませんでしたが、パタヘネの機械語の学習環境を作ろうとしていました。

現在パタヘネを読んでいるのですが、MIPS向けのマシン語を学ぶ章に突入しました。 C言語MIPSマシン語コンパイルしたり、 バイナリを逆アセンブルする課題などあるのですが、 どれも紙面上の話なので実際のコンパイラ、逆アセンブルを動かして見たいと思い、 検証環境を調査しました。

自分のやりたいこととしてはクロスコンパイラ、というものを使って動作しているCPUとは別ターゲットのCPU(MIPS)のアセンブルを作る、とうことが該当しそうです

ほんとは生成したバイトコードをシュミレータか何かで動かせるといいのですが。

gcc -march=mips32 sample.c -o sample

みたいな感じで動かせるかなと思ったけど、うまくいきませんでした。 自分のmacOSgccは多分Xcodeコマンドラインツールで入れたものなので、対応していないのかもしれまんせん。

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

この辺試せばいけそう?な気がする

http://www.theairportwiki.com/index.php?title=Building_a_cross_compile_of_GCC_for_MIPS_on_OS_X

vagrantを使ってlinuxでやる

https://www.youtube.com/watch?v=AVTRTR589bk&t=305s&frags=pl%2Cwn

vagrantを入れる作戦で進めてみます。 (そういえばxv6の改造の発表をやってた人もvagrant使ってたような?)

Web版のmipsお試しコンパイラ

こういうのもある。ちょっとした検証にはいいかもしれないけど、 リンカとか使ったアドレスを置換する挙動とかも見てみたいし力不足感はある。

http://reliant.colab.duke.edu/c2mips/

教育用の組み込みOSの話

教育用の組み込みOSの話をしている人の発表が刺さりました。

結局昨今の教育はデバイスを叩くだけで終わる、と仰っていましたが、自分の大学時代を思い出すとすごく心当たりがあります。

マイコンをいじる授業でひたすらデータシートを読んでピンの配線を確認したりなど、そういうことに時間を使いすぎていたと思います。そういうものはハードが変われば無駄になる部分が多いので、もっと制御とは何かみたいな本質的な部分を納得するまで学ぶべきだったと思います。*4

私が発表でやったことも結局半分くらいはBIOSというもうすぐ廃止される規格と戯れるものだったので、 痛いほどよくわかります。

もっと視野を広げて勉強しないといけないなと思った1日でした。

*1:386の資料で申し訳ございません

*2:hariboteOSではオリジナルのCコンパイラを使っている。多分GCCを多少いじっているもの

*3:それがhariboteOSと言えるのか不明ですが

*4:もちろん新しいハードを扱う必要がきた時に、データシートをみてサクッと使い方を理解するようなスキルも必要だと思いますが。