bootmain.c void readsect(void *dst, uint offset)

トップページ
jupiteroak.hatenablog.com


bootmain.c
https://github.com/mit-pdos/xv6-public/blob/master/bootmain.c#L59

void
readsect(void *dst, uint offset)
{
  // Issue command.
  waitdisk();
  outb(0x1F2, 1);   // count = 1
  outb(0x1F3, offset);
  outb(0x1F4, offset >> 8);
  outb(0x1F5, offset >> 16);
  outb(0x1F6, (offset >> 24) | 0xE0);
  outb(0x1F7, 0x20);  // cmd 0x20 - read sectors

  // Read data.
  waitdisk();
  insl(0x1F0, dst, SECTSIZE/4);
}

readsect関数は、引数offsetが指定するハードディスク上のセクタから、引数dstを先頭アドレスとするメモリ領域へ、512バイトのデータをロードします。

引数 dst
ロード先となるメモリ領域の先頭アドレスです。

引数 offset
ロード元となるセクタを指定するセクタ番号(28bitLBAの値)です。

処理の内容

ハードディスクが使用できる状態であることを確認する

waitdisk();

waitdisk関数を呼び出して、ハードディスクが使用できる状態であることを確認します。
ハードディスクが使用できる状態であれば、この関数から処理が戻ってきます。

読み込むデータサイズを1セクタに指定する

outb(0x1F2, 1); 

out命令(ポート出力命令)でポートアドレス0x1F2を指定することにより、ATAホストコントローラが持つSector Count Registerへの書き込みを行なっています。Sector Count Registerは、読み込み・書き出しを行う際のデータサイズをセクタ単位で指定するために使用されるレジスタです。
設定値は1なので、readsect関数の処理において、読み込みを行うデータサイズは1セクタ(512バイト)となります。

読み込み開始位置となるセクタ番号のbit7-0を設定する

outb(0x1F3, offset);

out命令(ポート出力命令)でポートアドレス0x1F3を指定することにより、ATAホストコントローラが持つLBAlo Registerへの書き込みを行なっています
LBAlo Registerには、読み込み開始位置となるセクタを指定するセクタ番号offset(28bitLBA値)のうちbit7-0の値を設定します。

読み込み開始位置となるセクタ番号のbit15-8を設定する

outb(0x1F4, offset >> 8);

out命令(ポート出力命令)でポートアドレス0x1F4を指定することにより、ATAホストコントローラが持つLBAmid Registerへの書き込みを行なっています
LBAmid Registerには、読み込み開始位置となるセクタを指定するセクタ番号offset(28bitLBA)のうちbit15-8の値を設定します。
offsetの値を8bit右シフト演算により、セクタ番号offset(28bitLBA値)のbit15-8の値を取り出しています。

読み込み開始位置となるセクタ番号のbit23-16を設定する

outb(0x1F5, offset >> 16);

out命令(ポート出力命令)でポートアドレス0x1F5を指定することにより、ATAホストコントローラが持つLBAhi Registerへの書き込みを行なっています。
LBAhi Registerには、 読み込み開始位置となるセクタを指定するセクタ番号offset(28bitLBA)のうちbit23-16の値を設定します。
offsetの値を16bit右シフト演算により、セクタ番号offset(28bitLBA値)のbit23-16の値を取り出しています。

セクタ番号、ハードディスクドライブの選択、アドレッシングモードの選択を設定する

outb(0x1F6, (offset >> 24) | 0xE0);

out命令(ポート出力命令)でポートアドレス0x1F6を指定することにより、ATAホストコントローラが持つDrive / Head Registerへの書き込みを行なっています。
Drive / Head Registerは、読み込み・書き出しの開始位置となるセクタの指定、読み込み・書き出し対象となるハードディスクドライブの選択、アドレッシングモードの選択に使用されるレジスタです。
Drive / Head Registerのbit3-0には、読み込み開始位置となるセクタを指定するセクタ番号offset(28bitLBA)のうちbit27-24の値を設定します。
Drive / Head Registerのbit4には、マスタードライブを使用する場合は0、スレイブドライブを使用する場合は1を設定します。
Drive / Head Registerのbit5には、常に1を設定します。
Drive / Head Registerのbit6には、LBA アドレッシングモードを使用する場合は1を、CHSアドレッシングモードを使用する場合は0を設定します。
Drive / Head Registerのbit7には、常に1を設定します。
設定値は、(offset >> 24) | 0xE0 (offsetのbit27-24の値と0xE0(0b 1110 0000)の論理和)です。
よって、Drive / Head Registerのbit3-0にはoffsetのbit27-24の値が、bit4には0の値が(マスタードライブを選択)、bit6には1の値が(LBA アドレッシングモード)が設定されます。

ハードディスクドライブへ読み込みコマンドを発行する

outb(0x1F7, 0x20); 

out命令(ポート出力命令)でポートアドレス0x1F7を指定することにより、ATAホストコントローラが持つCommand Registerへの書き込みを行なっています。
Command Registerは、ATAコマンドを送信するために使用されるレジスタです。
ハードディスクからの読み込み処理を行う際は、Command Registerに読み込みコマンド0x20(READ SECTORS command)を設定します。

ハードディスクが使用できる状態であることを確認する

waitdisk();

データの読み込みが完了して、ハードディスクが使用できる状態になっているか確認します。
ハードディスクが使用できる状態であれば、この関数から処理が戻ってきます。

ハードディスクのセクタから読み込んだデータ(512バイト)をロードする

insl(0x1F0, dst, SECTSIZE/4);

insl関数を呼び出して、offsetで指定したセクタのデータ512バイトを、アドレスdstから始まるメモリ領域にロードします。
insl関数内のinsl命令(ポート出力命令)でI/Oポートアドレス0x1F0を指定することにより、ATAホストコントローラが持つData Registerから、4バイトのデータを読み取ります。
セクタのデータ512バイトを読み取るには、insl関数内においてinsl命令を、512/4 = SECTSIZE/4 = 128回繰り返す必要があるので、第3引数にSECTSIZE/4を設定しています。