Syntax highlighter

Showing posts with label OS Dev. Show all posts
Showing posts with label OS Dev. Show all posts

2010-04-30

OS: GRUBの続きの続き

前回GRUBでカーネルイメージが読み込めたと思って喜んでいたのだが、VRAM周りのコードが上手く動いていなかった。
GRUBが最初に読み込む部分はアセンブラで書いているのだが、カーネルのコード(になる予定)から戻ってきたときには文字列が表示されるのに、コード内部で表示しようとしたら意図しない文字が表示されていた。
それでもって、自分で作ったVRAM用のクラスを使うとGRUBが再起動しまくりで、大変なことになっていた。

何が原因か分からなかったので、とりあえずデバッグすることに。
qemuのデバッグってどうやるのかとググって見たら、こんなページに遭遇。
-sと-Sオプションをつけるのね。意味は調べてないけど・・・
っで、gdbをカーネルイメージを引数にして起動。
(gdb) target remote localhost:1234
このコマンドでqemuに接続してすれば、普通にブレークポイントとか設定可能。

っで、動かしてみたところ、GRUBはELF形式にしか対応してないことが判明。
Cygwinで作ってるので、PE形式なイメージなわけで、どうしろと?
クロスコンパイラを作るのか?(GCCのターゲット指定でコンパイルだと思う)
と思っていたが、objcopyでバイナリ形式にしたら動いた。
まぁ、とりあえずこれでいいか。GRUB周りで苦しむのは本意ではないし・・・

どうでもいいが、調べている過程でmultiboot.hを覗いたのだが、カーネルに渡す構造体のなかにVBEの情報が入ってた。
まぁ、そのうち対応する予定なので、自前でなんだかやらなくても済んだかもと期待しておこう。
(でも、先にメモリとか割り込みテーブルとかやることは満載・・・)

2010-04-10

OS: GRUBの続き

昨日のエントリーで、いまいち動かないと書いたが、少し前進した。

マルチブートに関してヘッダー等がいるので、GRUBのサイトからmultiboot-0.6.96をダウンロード。その中にboot.Sというファイルとkernel.cというファイルがあるのでコピー。
(とりあえず、動作確認できればいいので・・・)
っで、普通にビルド。
_edataが定義されてないと怒られる。

なんだこれ?

Google先生に聞いてみる。

・・・

・・・・・・

・・・・・・・・・

.dataセクションの終わりを示すポインタらしい。
うわさによるとリンカーが自動で作るものらしいが、CygwinのGCCではなのかGCC 3.4.4ではなのかは知らないが作ってくれないらしい。
とうことで、リンカスクリプトを書く。
というか、ここから拝借。
cygwinではelf-i386はサポートされてない(というかpe-i386しかサポートされてない)ので、OUTPUT_FORMATの行を削除。OUTPUT_ARCHも、まぁいらんかなと思い、削除。
っで、make。
なんか表示された。

激しく文字化けというか、意味不明の文字ばかり見えるが、とりあえず何か表示されているのでよしとしよう。
あとはカーネルっぽくいろいろ作るだか(というかそれが一番大変だと思うが・・・)

2010-04-09

OS: GRUBをCygwinで無理やり使う

ここ3日くらい調べてようやくなんとかなった。

ひょっとして同じところで躓いてる人がいるかもしれないので、メモ。
用意するもの:
Cygwin(当然か)
VFD(ここから)

あらかじめ、fstabに以下を記述。
a: /cygdrive/a vfat binary,posix=0 0 0
っで、ddでFDサイズのファイルを作って、VFDで開いてフォーマットしたら、
grub-installでFDイメージにインストール。このとき、デバイスは/dev/fd0を指定する。

面倒なので、スクリプト書いた。
#!/bin/sh -x
# generate floppy image with grub2.

grub=/lib/grub/i386-pc
file=fd.img
vfd=/cygdrive/d/bin/vfd/vfd
fd0=/cygdrive/a

# create floppy image.
dd if=/dev/zero of=$file bs=1k count=1440
$vfd open $file
# format
$vfd format
# いらないかも・・・
mkdir -p $fd0/boot/grub
# 適当にモジュールを指定しているので、必要なもの不必要なものは調べる。
grub-install --modules="_chain ls pc multiboot gpt fat boot reboot configfile cat help " --root-directory=$fd0 /dev/fd0
# grub.cfgは作ってくれないので、別途コピー
cp grub.cfg $fd0/boot/grub

$vfd save
$vfd close
こんな感じ。
きっと必要に応じて、イメージのdevice.mapとかを編集すればいいはず。とりあえず、GRUBが起動するところまでは確認。

あとは、自前のカーネルを読み込むだけなのだが、grub.cfgの書き方がいまいち分かっていない・・・
それとも、カーネルの方がまずいのかな?
「error: attempt to seek outside of file」
ってエラーだからカーネルかな・・・
調べないと・・・

2010-01-28

OS: メモリ

ここ数日(数週間?)まったく時間が取れなかった。
今も取れているかはよく分からんが。

OSがメモリを割り当てるところで停まっていた。
まぁ、今も停まっていると言えばそうだが・・・

いまいちページングがよく分からない。E820hで取得したメモリーマップ情報からメモリアドレスを取得して、そのアドレスに対してページングOnする。
そこまでは上手くいっている(と思う)
っで、とりあえずmallocもしくはnewが使えないと話しにならない。ということで実装していたのだが、どうも変な挙動をしていた。
今日ふと、ページを初期化してから、アロケータの初期化をしたら、動いた(・A・)

鼻から悪魔が出るコードかもしれんが、とりあえずこれでいこう。
この辺が動けば少しやりやすくなるし・・・

まじめに参考書探さないとなぁ。
だれかお勧めの本とか知りませんか?

2010-01-17

C++: 構造体でアドレスを読む

我ながら意味不明なタイトルだ。
やりたいことは、アセンブラが設定した配列をC++(またはC)の構造体に入れるってそれだけなんだけど、何故かはまっている。

アセンブラは適当なアドレスに値を入れる。こんな感じで。
0x00002840: 0x00000000 0x0000000 0x0009fc00 0x00000000
0x00002850: 0x00000001 ...
これが配列の一要素。
っで、まぁこれはE820hで取得したメモリーマップなので、普通にこんな構造体で受け取る。
struct MemoryMapEntry
{
  uint32_t baseAddrLow;
  uint32_t baseAddrHigh;
  uint32_t lengthLow;
  uint32_t lengthHigh;
  uint32_t type;
};

// どこかの関数で
MemoryMapEntry mapEntries = getMemoryMapEntry(); // これはアセンブラ内で定義した。
これで普通ならいけるはずなんだけど、取り出される値がおかしい。上記のアドレスなら、
baseAddrLow  = 0x00000000
baseAddrHigh = 0x00000000
lengthLow    = 0x0009fc00
lengthHigh   = 0x00000000
type         = 0x00000001
になるはずなんだけど、こんな感じに実際にはずれる。
baseAddrLow  = 0x00000000
baseAddrHigh = 0x00000000
lengthLow    = 0x00000000
lengthHigh   = 0x00000000
type         = 0x0009fc00
感じとしては、20バイト取得できてないといけないのだが、12バイトしか取れてない感じ。なぜ?
これが普通の動作なのかなと思い、ちょっと実験してみた。
#include 

struct sample
{
    unsigned int val1;
    unsigned int val2;
    unsigned int val3;
    unsigned int val4;
    unsigned int val5;
};

void* getsample()
{
    static int *addr = (int*)0x404030;
    addr[0] = 0x00000000;
    addr[1] = 0x00000000;
    addr[2] = 0x00000000;
    addr[3] = 0x0009fc00;
    addr[4] = 0x00000001;
    return addr;
}

int main()
{
    sample *s = (sample*)getsample();
    printf("addr: %x\n", s);
    printf("%x, %x, %x, %x, %d\n", s[0].val1, s[0].val2, s[0].val3, s[0].val4, s[0].val5);
    return 0;
}
こんなむちゃくちゃなコードでもなんか動いてるから不思議。
っで、結果は普通に予定通りの値が取れていた。
コンパイラの最適化が効いてるからなのか(何もオプションつけてないけど)、別の要因で直にアドレスから取るのとは違うのかよく分からん。

何より腹が立つのは、以前はまともに動いていたのだ!ページングの実装を終えたらおかしくなった・・・
関係があるのかな、やっぱり・・・

2010/01/17 追記

原因が分かった。
結論: まともに動いていました。
原因: VGAクラスで実装していたprintfメソッドの不具合。
64ビットの数値には%xの書式指定子が対応しておりません・・・
参考にしている人はいないと思うが、
64ビットの整数値を表示したいのに、可変引数のポインタを進める際に(int*)のポインタでやっとりました。つまり、8バイト移動させないといけないところを、4バイトしか進めていなかったっと。
これで2時間悩んだ。printfデバッグの限界やね。と勝手に納得・・・

OS: ページング

メモリ管理をしないと動的にメモリが取れない。
っで、とりあえずページングに手を出してみることにした。

とりあえずここを参考にしつつ、人のコードを見つつ。
っで、ページングONしてやって、qemuで起動すると、何故か無限リスタートしてくれる。
意味が分からんなぁ、と思いながらMonaのソースを見る。
(分からなくなったときのMona頼りというわけではないが・・・)

PageManager.cppのsetupメソッドを見ると、VRAMのアドレスをページング対応してる。何でだろうと思いながら、とりあえず似たようなことをしてみたら、動いた(・д・)
ページングをONにするということはすべてのアドレスをページング対応する必要があるのかな?
よく分からん。
ちなみに、ページング対応させたアドレスの属性を、書き込み可能、ユーザー領域、カレントにしないとだめだった。意味はよく分かっていない・・・(汗
無知っていやだなぁ・・・
勉強しないといけないのだが、こっちじゃ書籍も探し難い・・・ネットの情報だけじゃ厳しくなってきたというのに・・・

っで、これ書いてるときに気がついたのだが。
そういえばバイオスのE820h命令でメモリーマップを取得してるんだから、適当なアドレスを使用すれば、動的メモリーの割り当てができるような気がするなぁ・・・
ページングの前に実装するべきだったか?

2010-01-16

OS: 便利リンク

自分のための便利リンク。
ChangeLogとか、メモとかHDDに置けよという意見は無視。

[Wiki]
OSDev(English)
osdev-j(MMA)(日本語)

[ブログ]
OSのようなもの
Outlandish Watch(日本語)

[BIOS関連]
Ralf Browns Interrupt List

何か見つけたら随時足すことにしよう。

2010-01-09

Makefile: 依存関係

自作OSをビルドする際に、当然(?)makeを使うわけだが、ずぼらな僕は「make」って打つだけで全部やってほしいと思うわけだ。

っが、依存関係の問題があって、今まで
「make clean all」
なんて、毎回プロジェクトをリビルドしていたわけだ。
けど、これ今はまだいいが、ファイル数が10とか100とか増えた時に非常に面倒になる。
というか、僕の貧弱なノートPCでは時間がかかりすぎるようになる。
(こんなことやってるのに、使用してるPCがノートかよ、って突っ込みはなしで)

っで、とりあえず、依存関係を自動で解決するようにMakefileを書き直した。
ポイントになるのは、gccの「-MM」オプション。
っが、makeのマニュアルにある方法だと、sedを使う。っが、WindowsでMingwだけで何とかしようという環境の僕にはsedはおろかCygwinもない。
適当に、gccが吐き出す依存関係を編集して上手いこと書き出すプログラムをC++で書いた。
なんでこんなことしなきゃならんのだろうか?

これだけいろいろ便利になってるんだから、こんな変なノウハウじゃなく、もう少しスマートな解決方法があってもいいような気がする。

2010-01-08

C++: 初歩的なこと・・・

VGAのを使って色を変えたり、文字を表示させたりするということ実装していた。
リアルモードからプロテクトモードに移行するとBIOSが使えないので、VGAを使う必要が(ないかもしれないが)ある。
っで、主にこの辺とかMonaの初期のソースとかを参考にしながら、しこしこ書いてみた。

っが、なぜか一行おきに空白がでる(縦に)。なんでだろうと、いろいろVRAMに与える値を変えたりしながら考えてみたが、一向に改善しない。
おかしいなぁと思いながらトイレにいって思いついた。
そういえばVRAMのアドレスはunsigned shortのポインタだなぁと・・・
Monaはunsigned charでサイトはunsigned shortなので、ポインタに加算を行った際のアドレスが1バイトずれる。

これか。

おさらい。

// ちなみに、0xb8000はVRAMのアドレス
unsigned char* ptr = (unsigned char*)0xb8000;
unsigned short* ptr2 = (unsigned short*)0x8b000;
ptr++; // 0xb8001
ptr2++;// 0xb8002

これを行った際に。ptrとptr2のアドレスは異なる。
あほかと思うくらい当たり前だが、はまった。

2010-01-07

OS: IPL

その筋には(というと変な雰囲気がでて格好いい?)有名だが、
2ch発祥のOSでMonaというのがある。最初の会社にいたころに知ったから、2005年くらいか?
あれから、5年。何を思ったのが、僕もOSを作りたいと思ってきた。
(昔から思っていたが、当時は何をしていいのかさっぱり分からなかった。が、最近なんとなく必要なものが見えてきた。暇にかまけて勉強したのが功を奏した感じ)

っで、Monaをはじめ、いろいろ調べてみた。
30日でできる! OS自作入門とかが有名っぽいが、こっちじゃ買えん。
っが、結構実践サイトとかあって、解説が読めないだけでソースは読めたりする。

っで、IPL(OSを起動する前にBIOSが起動する。ブートローダーと同義?)を作ることからはじめることになるのだが、これが難しい(当たり前だが)
アセンブラがよく分からない(アセンブリ言語自体は割りとシンプルなのだが、アドレスとかレジスタとかがいまいち分からん)
あと、GCCとNASMの相性が悪い(というかGASとGCCの相性がよすぎるのか?)というのもあって、とりあえずNASMを選択していた状態ではさっぱり分からんかった。

とりあえず、このサイトの自作OSのタグに書かれているのを参考に(ほとんど丸写し)、IPLを作成。
GASを使うとCの関数がそのまま呼べるので楽!!
リンカスクリプトを使うとコンパイル時にプログラムのサイズが取れるので、FDの読み取りに必要なセクタ数が分かって楽!!!
なんて感じだった。
はじめからこれにしておけばよかった。
回り道は無駄じゃないと信じたい。