OS自作入門4日目

読んだ。
用意されている開発環境は、CygwinやMinGW/MSYSのmakeではうまく動かないようです。
SET PATH=
としてPATHを消すと用意されたものが動くようです。
MinGW/MSYSのrxvtで動かすときは、Makefileを
COPY = cp
DEL = rm -f
としてやるといいです。haribote.sysを作るときは、copy /Bの代わりにcatが使えます。
cat asmhead.bin bootpack.hrb >haribote.sys
コマンドの前に-をつけると、エラーがあっても続けるという意味になるんですね。
-$(DEL)とか。
ポインタが説明されていますけど、自分の場合はgdbで変数の実際の値を何度も確認することで理解したように思います。
ただ説明を聞いただけでは、なかなか実感することは難しいと思うので、値を書き出したりgdbで確認したりするといいのではないかと思いますけどね。

OS自作入門3日目

読んだ。
この辺でつまずく人が多い気がする。bimとかhrbとか出てきて気になった。せっかく起動から追っかけてきたのにというのもあって、少し調べてみることにしました。
bimとhrbは、情報を見つけました。
omake/tolsrc/bim2hrb.cの引用。
******************************
[ .bimファイルの構造 ]
+ 0 : .textサイズ
+ 4 : ファイル中の.textスタートアドレス(0x24)
+ 8 : メモリロード時の.textスタートアドレス(0x24)
+12 : .dataサイズ
+16 : ファイル中の.dataスタートアドレス
+20 : メモリロード時の.dataスタートアドレス
+24 : エントリポイント
+28 : bss領域のバイト数
+36 : コード
[ .hrbファイルの構造 ]
+ 0 : stack+.data+heap の大きさ(4KBの倍数)
+ 4 : シグネチャ "Hari"
+ 8 : mmarea の大きさ(4KBの倍数)
+12 : スタック初期値&.data転送先
+16 : .dataのサイズ
+20 : .dataの初期値列がファイルのどこにあるか
+24 : 0xe9000000
+28 : エントリアドレス-0x20
+32 : heap領域(malloc領域)開始アドレス
*******************************
.bimファイルも、.hrbファイルも、上のような構造のヘッダーを持ったファイルになっています。WCOFFという形式は、調べてみたけどわからなかった。普通のCOFFと同じなのか。
Haribote.sysは、asmhead.binとbootpack.hrbを単純にくっつけたものになっているので、
asmhead.bin本体
bootpack.hrbのヘッダー
bootpack.hrbの本体
の三層構造になっています。これが、起動のときに重要な役割をします。
パソコンの電源を投入後、フロッピーが読み込まれて、IPLが実行され、asmhead.binに制御が移るところまではこれまで説明されているのでわかると思います。
その後、asmhead.nasでは、bootpack以下をBOTPAK(0x280000)に転送しています。bootpack:というラベルはasmhead.nasの最後にありますが、上に書いたようにasmhead.binの後ろにはbootpack.hrbがあるので、bootpack.hrbを0x280000に転送していることになります。また、最後にbootpackの起動のところで
JMP DWORD 2*8:0x0000001b
として2番目のデスクリプタの0x1bにジャンプすることで処理が終わっています。2番目のデスクリプタというのは、コピーされたbootpack.hrbがあるところなので、bootpack.hrbの0x1bに飛ぶことになるけど、0x1bというのが謎だった。
bootpack.hrbのヘッダーを見るとちょうど+24の0xe9000000の最後のバイトを指している。バイナリエディタで調べてみると、こんな風になっている。
+24: 0x00 0x00 0x00 0xe9 (0xe9000000)
+32: 0x09 0x00 0x00 0x00 (エントリーアドレス)
ここで、bootpack.hrbの0x1b番目(27番目)から並べてみると、エントリーアドレスはリトルエンディアンなので並べかえて、
0xe9 0x00000009
となって、これを逆アセンブルすると、
JMP _HariStartup
になります。つまり、0xe9というデータを実行可能なコードとみたてて、_HariStartupにジャンプするようになっていました。この_HariStartupというのは、obj2bimで、bootpack.bimにリンクされるライブラリの中にあります。ソースコードはomake/tolsrc/hrblib0a/startup.cにあって、内部でCのソースコードのHariMainを呼んでいます。そこでやっと、bootpack.cのHariMainが実行されます。
HariMainの起動までを要約すると、こんな感じです。
BIOSが、フロッピーのシリンダー0、ヘッド0、セクタ1の512バイト(IPL)を、0x7c00に読み込みます。
[ipl10.nas]
フロッピーのシリンダー10まで、0x8200を先頭に読み込み。Haribote.sysが読み込まれます。
Haribote.sysの先頭asmhead.bin(0xc200)にジャンプ。
[asmhead.nas]
Haribote.sys内のbootpack.hrbを0x280000を先頭にコピー。
コピーしたbootpack.hrbのヘッダー(0x280000 + 0x1b)にジャンプ。
[bootpack.hrbのヘッダー]
bootpack.hrb本体のHariStartupにジャンプ。
[startup.c]
HariMainを呼び出す。
[bootpack.c]
HariMain()を実行します。
フロッピーの内容を1度、メモリーにロードした後、コピーして実行しているところが複雑になっているのかな。

非同期のWriteFile

非同期のWriteFileで、普通のファイルに書こうとするとファイルポインタが動かなくて、ファイルの先頭から毎度書き込もうとします。最初、1文字しか書かれなくてよくわからなかった。書こうとする位置を自分で管理しないといけないらしい。OVERLAPPED構造体のoffsetに書こうとする位置をセットするのだそうです。

名前つきパイプ

日本語キーボードと、シリアルのパッチをホームページに載せておきました。
名前つきパイプでwindbgと接続する方法を試してみた。
名前つきパイプのサンプルはMSのサイトにある。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/base/interprocess_communications.asp
非同期の通信でやることにした。
CreateNamedPipeでパイプを作り、ConnectNamedPipeでクライアントからの接続を待ち、あとはReadFile/WriteFileするだけみたい。クライアントはwindbgになるけど。
パイプのタイプにBYTEとMESSAGEがあるけど、BYTEのほうでいいみたいだ。
作ってみるとすごい遅いんです。Win2kが立ち上がるのに8分ぐらいかかります。VMwareでwindbgを使ったリモートデバッグでもこんなもんなんですかね。実機でもすごく遅いみたいなんですけど、どれくらい遅いのかやったことがないのでわからない。
ときどきCPUが100%になってマウスのカーソルが動かないことも起きるし。
バッファのサイズとか変えてみたけど変化ないみたい。
仕方ないかなと思いますが、少し残念です。使えるようになっただけでOKとするかな。

シリアルのパッチとバイナリ

シリアルのパッチとバイナリを作りました。
http://www.h7.dion.ne.jp/~qemu-win/download/qemu-20060311-serial-3.patch
http://www.h7.dion.ne.jp/~qemu-win/download/qemu-20060311-16550a.patch
http://www.h7.dion.ne.jp/~qemu-win/download/qemu-20060311-serial-win2kxp.patch
http://www.h6.dion.ne.jp/~kazuw/qemu-win/qemu-20060311-serial.zip
オプションは、次の通りです。
-serial COM1
COM番号に大文字を使ってください。
ダイアログが出てきますので、通信パラメータを設定してください。
パッチを当てた後、Windowsを起動すると最初に、デバイスがアップデートされたことを検出します。デバイスドライバがインストールされた後、再起動してください。
実際のCOMポートではテストしていません。
ComEmulDrvというWindows 2000/XP用に仮想COMポートのペアを提供するドライバを使って、COM5とCOM6を設定し、Win2kゲストとWinXPホストの間でHyperTerminalで通信できるのを確認しました。
http://mixw.net/related.htm#tncemu
通信パラメータは重要です。テストしたパラメータは次の通りです。
ビット/秒: 9600 bps
データビット: 8 bit
パリティ: なし
ストップビット: 1
フロー制御: なし
ホストとゲストとQEMUで同じパラメータを使う必要があります。
フロー制御をハードウェアにした場合、qemu-20060311-serial-win2kxp.patchを使わないと、Win2kゲストからWinXPホストへデータを送ることができません。
Windbgは、速度が遅すぎるととても使えないことがわかった。名前つきパイプを使えるようにしてみようと思います。
モデムをもってましたら試してみてくださいませ。

OS自作入門1日目

読んだ。
いきなりバイナリエディタ触るんですね。Meadowでやろうと思って、M-x hexl-find-fileでバイナリモードでファイルを開いたりM-x hexl-modeでモードを変えられることは調べたけど、アルファベットしか入力できない。M-x hexl-insert-hex-char(C-M-x)で入力できるけど、いちいち面倒でBz使うことにした。
.nasという拡張子でasm-modeにするにはこんなのを.emacsに書いておくといいです。
(setq auto-mode-alist
(append '(("\.nas$" . asm-mode))
auto-mode-alist))
(add-hook 'asm-mode-hook
'(lambda ()
(progn
(setq tab-width 4)
; (setq tab-stop-list '(4 8 12 16 20 24 28 32 36 40))
)))
tab-stop-listは、入力の最初の位置を決めるのに使えます。コメントにしてありますけど。
asm-modeでのコメントですが、セミコロン;を2つ打ち込まないと行頭に行かないです。2つ打ち込むと3つ入るというおまけまでついていて、なんともおせっかいなと思った。でも、カスタマイズする方法がわからなかった…。

QEMUのシリアルの扱い方

QEMUの中では、次のようにシリアルに関するデータが渡されています。
vl.cで、
serial_hds[i] = qemu_chr_open(serial_devices[i]);
serial_hdsに、CharDriverState *chrが入る。
pc.cで、
serial_init( , , , serial_hds[i]);
で、シリアルに渡され、
serial.cで、
SerialState s->chr = chr;
として保存。
qemu_chr_add_read_handler(chr, serial_can_reveive, serial_receive1, s);
として、割り込みハンドラに渡される。
割り込みハンドラの登録と、データの送受信の関数など作り、とりあえずシリアルは動くようになりました。
ただ、Win2kゲスト/WinXPホストのHyperTerminal同士で通信したとき、通信パラメータのRTS/CTS制御を有効にすると、ゲストからホストに文字が送れないのです。ハードウェア制御がなしならばいいのですが。
QEMUのシリアルのエミュレーションに問題があるのか、使っているComEmulDrvという仮想COMポートに問題があるのかわからない…。
もっと細かく見ないといけないのかな。

30日でできる!OS自作入門

OS自作入門買ってきました。
最初の印象は、デカいでした。OSの解説となるとこれぐらいの分量になるんですかね。
QEMUが使われているんですね。やっぱりOS作るには便利なのか。
これ読んだら、64ビットOSとか作れるようになるんだろうか。ただ、64ビットのプログラムを作ってみたいだけなんですけど。「はじめて読むAMD64/EM64T」みたいな本書いてくれないかな。なければ作るとか。
QEMU専用のOSだったら、作るのもありかなとも思います。VT/Pacificaも使えるようにして。いろいろやろうとすると、Xenと同じになる気がしますけど。
わかった気になるためには、プログラムを打ち込んでみることが大切のような気がします。
とりあえず、少しずつ読んでみることにします。

シリアルのいろいろ

シリアルのメモ
シリアルのパラメータはいろいろあってよくわからない。ダイアログが出せるみたいなので、とりあえず使うことにした。RTS/CTSフロー制御を使うのが一般的なのかな。
ClearCommError()で、cbInQueにたまっているデータの数が入っているので、それでReadFileするとバッファからデータを読むことができる。
WaitCommEvent()でイベント待ちにして、WaitForMultipleObjects()/WaitForSingleObject()でシグナル状態になるのを待って、ReadFileする。
GetOverlappedResult( , FALSE)にすると完了を待たずに、抜ける。TRUEにすると、完了を待つ。
ReadFile/WriteFileは、デバイスドライバの送信/受信バッファに読み書きすること。データが届いたかどうかはわからない。
マルチスレッドにすれば、待ち受け関数をINFINITEで待つことができるけど、データの受け渡しにCriticalSectionを使ったりしないといけなく、バッファの取り扱いが面倒。
まず、マルチスレッドを使わずに動作を確認しようと思った。ReadFile/WriteFileは同期にすると問題があるそうなので、OVERLAPPEDを使った非同期にすることにした。
作ってみたけど、どうもうまく動かない。どうやらWaitForSingleObjectでイベント待ちにしてSleepしたところで何もいわずに落ちてしまう。テストプログラムだと大丈夫なんだけど。仕方ないので、WaitCommEventを使わないことにした。