OS起動編⑭ ideinit() (Xv6を読む~OSコードリーディング~)
前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com
main.c(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/main.c#L33
int main(void) { ... ideinit(); // disk ...
ide.c
https://github.com/mit-pdos/xv6-public/blob/master/ide.c#L50
void ideinit(void) { int i; initlock(&idelock, "ide"); ioapicenable(IRQ_IDE, ncpu - 1); idewait(0); // Check if disk 1 is present outb(0x1f6, 0xe0 | (1<<4)); for(i=0; i<1000; i++){ if(inb(0x1f7) != 0){ havedisk1 = 1; break; } } // Switch back to disk 0. outb(0x1f6, 0xe0 | (0<<4)); }
ideinit関数では、ハードディスクドライブの初期化を行なっています。
具体的には、ハードディスクドライブの読み込み・書き出し処理に関わる割り込みの有効化と、ハードディスクドライブにスレイブドライブがあるかどうかの確認を行なっています。
処理の内容
- idequeueに関わるロックを初期化する
- I/O APICが持つIRQピンへの割り込みを設定する
- ハードディスクが使用できる状態であることを確認する
- ハードディスクドライブにスレイブドライブが存在するかを確認する
idequeueに関わるロックを初期化する
initlock(&idelock, "ide");
initlock関数を呼び出して、ハードディスクドライブの操作(idequeue)に関わるロックを初期化しています。
I/O APICが持つIRQピンへの割り込みを設定する
ioapicenable(IRQ_IDE, ncpu - 1);
ioapicenable関数を呼び出して、I/O APICが持つピン番号が14(IRQ_IDE)のIRQピンに送信される割り込みについての設定を行っています。
第2引数にはncpu-1の値を設定しています(理由はわかりませんが、割り込みの標的となるプロセッサのAPIC IDの値を設定していないようです)。
ピン番号14(IRQ_IDE)のIRQピンにはハードディスクドライブからの割り込みが送信されるので、その割り込みのベクタ番号は46(T_IRQ0 32 + IRQ_IDE 14)となります。
ハードディスクが使用できる状態であることを確認する
idewait(0);
idewait関数を呼び出して、ハードディスクが使用できる状態であることを確認します。
引数(checkerr)には、検出したいエラーの種類を示す値を指定します。
ここでは、検出したいエラーが特にないようなので引数に0の値を指定しています。
ハードディスクドライブにスレイブドライブが存在するかを確認する
読み込み・書き出し対象となるハードディスクドライブをスレイブドライブにする
outb(0x1f6, 0xe0 | (1<<4));
out命令(ポート出力命令)でポートアドレス0x1f6を指定することにより、ATAホストコントローラが持つDrive / Head Register(8bit幅)への書き込みを行なっています。
Drive / Head Registerは、読み込み・書き出し対象となるハードディスクドライブの選択、アドレッシングモードの選択に使用されるレジスタです。
Drive / Head Registerのbit4には、マスタードライブを使用する場合は0、スレイブドライブを使用する場合は1を設定します。
Drive / Head Registerのbit5には、常に1を設定します。
Drive / Head Registerのbit6には、LBA アドレッシングモードを使用する場合は1を、CHSアドレッシングモードを使用する場合は0を設定します。
Drive / Head Registerのbit7には、常に1を設定します。
設定値は、0xe0 | (1<<4)→ 0b 1110 0000 | 0b 0001 0000 →0b 1111 0000なので、Drive / Head Registerのbit4には1の値が(スレイブドライブを選択)、bit6には1の値が(LBA アドレッシングモード)が設定されます。
スレイブドライブのStatus Registerを読み取る
for(i=0; i<1000; i++){ if(inb(0x1f7) != 0){ havedisk1 = 1; break; } }
in命令(ポート入力命令)でI/Oポートアドレス0x1f7を指定することにより、ATAホストコントローラが持つStatus Registerの値を読み取っています。
Status Registerは、現在のハードディスクドライブの状態を示しているレジスタです。
前の処理によって、読み込み・書き出し対象となる現在のハードディスクドライブは、スレイブドライブになっています。
inb(0x1f7) != 0 が真となる場合→Status Registerの値が0ではない場合→エラーを検知した場合は、スレイブドライブが存在しているとみなし、havedisk1のフラグを1に設定してfor文から脱出します。