OS自作~IDE Controllerと仮想ディスクの実装~
前回のあらすじ
前回はタイマーを実装しました。
今回はこのタイマーを使ってIDE Controller(PATA Driver)と、Live Mode用の仮想ディスクを実装しようと思います。
今回のコミットはこれ(この記事の内容の他にファイルの整理とかACPIのバグ修正とか入ってる):
ハードディスクのあれこれ
まず、ハードディスクのドライバを書く前に、ハードディスク周りの規格について整理します。まず、ハードディスクの接続方式ですが、僕が認識している中では主に以下の3つです:
・NVMe(Non-Volatile Memory Express):最も高速でSSD用
・SATA(Serial ATA):最も一般的
・PATA(Parallel ATA):古いが、ほぼ全てのOSでサポートされている
注:この辺は結構複雑で未だに混乱しているので、間違っているかもしれません。次に、これらに使われるインターフェイスです:
IDE:PATAとセット
AHCI:SATAで使われることが多いがPATAでも使われる?
この辺がどう対応しているのかはもう理解することを半分あきらめました()ただ、とりあえず、QEMUでボリュームイメージを保存しているのがIDEっていうのもあって、PATAをIDEで読み書きするIDE Controllerを書くことにしました。
IDE Controllerの実装
インラインアセンブラ
実装に関してはOSdevをひたすらRustに変換しました。途中結構アセンブラが出てくるので、インアセンブラに関する話をします。まず、RustのインラインアセンブラはIntel記法です。これを知らないと全然コンパイル通りません。あと、命令の中には特定のレジスタが重要なこともあるんですが、レジスタを明示的に指定したいときはこんな感じで書けばいいみたい:
asm!(
"push bx",
"mov es, bx",
"push bx",
"mov ax, es",
"rep insw",
"pop bx",
"mov bx, es",
"pop bx",
in("ax") selector,
in("ecx") words,
in("edx") bus,
in("edi") edi
);
まずは普通にレジスタの定義をして、アセンブラの中では直接レジスタを書く。当たり前といえば当たり前だけど、この情報ドキュメントからは分かりにくい気がする。ちなみにこのコードは改良する前のデータを読み出すためのアセンブラ。ChatGPTとかに聞くと、結構すらすらアセンブラ書いてくれるから、わからなくなったら頼るといい。
とりあえず、これでもmikan本みたいにボリュームイメージを表示するところまではできたので記念写真を載せときます。
改良
ただ、どうもあのプログラム汚いくて、よく考えると、本来データのやり取りはDataRegisterを使ってできるはず!ということでide_write_bufferを新たに実装してDataRegisterで読み出すように改良しました。コードは割愛
仮想ディスク
仮想ディスクの概要
仮想ディスクなんてかっこいい呼び方してますが、やってることはメモリ上にハードディスクとして使う分をバイト列として確保して、あとはそこにlbaを使ってアクセスするようにメソッドを作ればいいわけです。ただ、それにあたってパソコンのメモリの残り容量もわからず無闇に仮想ディスクを作成するとやたらカツカツになったり小さすぎたりするので、メモリの半量を割り当てるようにしたいと思います。
それにあたってこんなメソッドをメモリマネージャ構造体に追加しました:
pub fn check_free_memory(&self) -> usize {
let mut count: usize = 0;
for i in self.range_begin.id()..self.range_end.id() {
if !self.get_bit(FrameID::new(i)) {
count += 1
}
}
return count * BYTES_PER_FRAME;
}
やってることは関数名の通りで、実装もただ有効なフレームを全て調べて、あとはフリーフレーム数にフレームあたりのバイト数(確か2KB)をかけて返しています。
Storageトレイト
さて、仮想ディスクは先ほどのメソッドに基づいて配列確保すれば良いわけですが、IDE Controllerと同じように使えるようにする(つまり抽象化)が必要になります。
そこで使えるのがRustのトレイト。これでreadとwriteという関数にインターフェイスを統一すれば、ただのディスクとして扱えます。
pub trait Storage {
fn read(&mut self, bytes: u32, lba: u32, buf: &mut [u8]) -> u8;
fn write(&mut self, bytes: u32, lba: u32, buf: &[u8]) -> u8;
}
具体的な実装は/driver/ataのしたのそれぞれのディレクトリに入ってるのでそっちを見てください。今回のでトレイトが抽象化に便利なことを実感したから、他のデバイスドライバでも同じことするつもり。
次回にすること
ディスクドライバが書いたことでできることの可能性が一気に広がった感じがします。というわけで次回はいよいよFATファイルシステムを実装して、ファイルを読むところまではやりたいと思います。
ちなみに、今回のコミットの最後にほぼ全てのファイルが編集されてると思うんですが、「rust-analyzer」をVSCodeに追加したためか急に勝手にフォーマットされまして、せっかくなので全部コミットしました。結構綺麗に整えてくれたから今後も使うと思います。
この記事が気に入ったらサポートをしてみませんか?