ide.c static void idestart(struct buf *b)

トップページ
jupiteroak.hatenablog.com


ide.c
https://github.com/mit-pdos/xv6-public/blob/master/ide.c#L73

static void
idestart(struct buf *b)
{
  if(b == 0)
    panic("idestart");
  if(b->blockno >= FSSIZE)
    panic("incorrect blockno");
  int sector_per_block =  BSIZE/SECTOR_SIZE;
  int sector = b->blockno * sector_per_block;
  int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ :  IDE_CMD_RDMUL;
  int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL;

  if (sector_per_block > 7) panic("idestart");

  idewait(0);
  outb(0x3f6, 0);  // generate interrupt
  outb(0x1f2, sector_per_block);  // number of sectors
  outb(0x1f3, sector & 0xff);
  outb(0x1f4, (sector >> 8) & 0xff);
  outb(0x1f5, (sector >> 16) & 0xff);
  outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((sector>>24)&0x0f));
  if(b->flags & B_DIRTY){
    outb(0x1f7, write_cmd);
    outsl(0x1f0, b->data, BSIZE/4);
  } else {
    outb(0x1f7, read_cmd);
  }
}

idestart関数は、引数bで指定されるバッファを使ったハードディスクの読み込み処理・書き出し処理を開始するために、ハードディスクドライブへ読み込みコマンド・書き出しコマンドを発行します。

引数 struct buf *b
ハードディスクの読み込み処理・書き出し処理で使用されるバッファを指定するアドレスです。


処理の内容

バッファが指定されていない場合はメッセージを出力する

if(b == 0)
    panic("idestart");

ハードディスクのセクタから読み込んだデータを保存するバッファ、または、ハードディスクのセクタへ書き出すデータを保存しているバッファが用意されていない場合は、panic関数を呼び出してメッセージを出力します。

ファイルシステムで扱うことができるブロック数を超えている場合はメッセージを出力する

if(b->blockno >= FSSIZE)
    panic("incorrect blockno");

バッファのブロック番号(b->blockno)が1000以上となる場合は、panic関数を呼び出してメッセージを出力します。

1ブロック当たりに含まれるセクタ数を求める

int sector_per_block = BSIZE/SECTOR_SIZE;

ブロックに含まれるセクタ数を求めています。ブロックとは、ファイルシステムでとり扱われるデータサイズの単位です。
ブロックのサイズ(#define BSIZE 512)は、多くの場合、セクタに含まれるデータサイズ(#define SECTOR_SIZE 512)の整数倍となっています。
xv6では、ブロックのサイズ(#define BSIZE 512)とセクタに含まれるデータサイズ(#define SECTOR_SIZE 512)は等しくなるようにしているで、ブロックに含まれるセクタ数sector_per_blockは1となります。

読み込み・書き出しの開始位置となるセクタを指定するセクタ番号を求める

int sector = b->blockno * sector_per_block;

ハードディスク上において、読み込み・書き出しの開始位置となるセクタを指定するセクタ番号を求めます。
ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数がsector_per_blockの場合、ハードディスク上において読み込み・書き出し開始位置となるセクタの番号sectorは、b->blockno * sector_per_blockとなります。

Command Registerに設定する読み込みコマンドを準備する

int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL;

Command Registerに設定する読み込みコマンドを準備します。
ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数が1の場合は、IDE_CMD_READ(#define IDE_CMD_READ 0x20)を選択します。
ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数が複数の場合は、IDE_CMD_RDMUL(#define IDE_CMD_RDMUL 0xc4)を選択します。

Command Registerに設定する書き出しコマンドを準備する

int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL;

Command Registerに設定する書き出しコマンドを準備します。
ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数が1の場合は、IDE_CMD_WRITE(#define IDE_CMD_WRITE 0x30)を選択します。
ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数が複数の場合は、IDE_CMD_WRMUL(#define IDE_CMD_WRMUL 0xc5)を選択します。

1ブロック当たりに含まれるセクタ数が8以上の場合はメッセージを出力する

if (sector_per_block > 7) panic("idestart");

ブロック(ファイルシステムでとり扱われるデータサイズの単位)に含まれるセクタ数が8以上の場合は、panic関数を呼び出してメッセージを出力します。

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

idewait(0);

idewait関数を呼び出して、ハードディスクが使用できる状態であることを確認します。
今回は、検出したいエラーが特にないようなので引数に0を指定しています。

ハードディスクドライブへ読み込みコマンド・書き出しコマンド発行の準備をする

ATAホストコントローラからの割り込みを有効化する

outb(0x3f6, 0);

out命令(ポート出力命令)でI/Oポートアドレス0x03f6を指定することにより、ATAホストコントローラが持つDevice Control Registerrへの書き込みを行なっています。Device Control Registerのbit1に0を設定することで、ATAホストコントローラからの割り込みを有効化します(ATAホストコントローラが割り込み信号を送信するようにします)。Device Control Registerのその他のbitには、基本的に0を設定します。

読み込み・書き出しを行う際のデータサイズをセクタ単位で指定する

outb(0x1f2, sector_per_block);

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

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

outb(0x1f3, sector & 0xff);

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

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

outb(0x1f4, (sector >> 8) & 0xff);

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

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

outb(0x1f5, (sector >> 16) & 0xff);

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

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

outb(0x1f6, 0xe0 | ( (b->dev&1)<<4) | ( (sector>>24)&0x0f));

out命令(ポート出力命令)でポートアドレス0x1F6を指定することにより、ATAホストコントローラが持つDrive / Head Registerへの書き込みを行なっています。
Drive / Head Registerは、読み込み・書き出しの開始位置となるセクタの指定、読み込み・書き出し対象となるハードディスクドライブの選択、アドレッシングモードの選択に使用されるレジスタです。
Drive / Head Registerのbit3-0には、読み込み開始位置となるセクタを指定するセクタ番号sector(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を設定します。
設定値は、0xe0(bit7-5に設定する値)、(b->dev&1)<<4(bit4に設定する値)、(sector>>24)&0x0f(bit3-0に設定する値)の論理和です。
よって、Drive / Head Registerのbit6には1が(LBA アドレッシングモード)が、bit3-0にはsectorのbit27-24の値が、それぞれ設定されます。

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

ファイルシステムでは、ハードディスク上のセクタへ直接書き出し処理を行うのではなく、そのセクタに対応しているバッファに一度書き込みを行った後、バッファからセクタへの書き出し処理を行なっています。そのため、書き込みが行われたバッファはそのバッファに対応しているセクタと異なるデータを含んでいる状態となります。このような状態の時にバッファのflagsメンバのbit2を1に設定して、バッファが保存しているデータをハードディスクへ書き出す必要がある状態(DIRTY)であることを示します。

ハードディスクドライブへ書き出しコマンドを発行する場合

if(b->flags & B_DIRTY){
 outb(0x1f7, write_cmd);
 outsl(0x1f0, b->data, BSIZE/4);

b->flagsをB_DIRTY(#define B_DIRTY 0x4)でマスク処理することにより、バッファbのflagsメンバのbit2の値を取り出しています。
b->flags & B_DIRTYが真となる場合→バッファbのflagsメンバのbit2が1の場合→バッファbが保存しているデータをハードディスクへ書き出す必要がある状態の場合は、ハードディスクドライブへ書き出しコマンドを発行します。
はじめに、out命令(ポート出力命令)でポートアドレス0x1F7を指定することにより、ATAホストコントローラが持つCommand Registerへの書き込みを行なっています。Command Registerは、ATAコマンドを送信するために使用されるレジスタです。
ハードディスクへの書き出し処理を行う際は、Command Registerに書き出しコマンドwrite_cmdを設定します。
次に、outsl命令(ポート出力命令)でI/Oポートアドレス0x1F0を指定することにより、ATAホストコントローラが持つData Registerへ、バッファbに保存されている512バイトのデータ(先頭アドレスがb->data・サイズがBSIZE/4×4のメモリ領域に格納されているデータ)を、書き出します。

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

 } else {
    outb(0x1f7, read_cmd);
 }

b->flags & B_DIRTYが偽となる場合→バッファbのflagsメンバのbit2が0の場合→バッファbが保存しているデータをハードディスクへ書き出す必要がない状態の場合は、ハードディスクドライブへ読み込みコマンドを発行します。
out命令(ポート出力命令)でポートアドレス0x1F7を指定することにより、ATAホストコントローラが持つCommand Registerへの書き込みを行なっています。
Command Registerは、ATAコマンドを送信するために使用されるレジスタです。
ハードディスクからの読み込み処理を行う際は、Command Registerに読み込みコマンドread_cmdを設定します。

コマンド発行後

コマンドが発行され、ハードディスク側の読み込み処理・書き出し処理が完了したら、ATAホストコントローラは割り込み信号を発信します。
割り込み信号が発信されると、割り込みハンドラ(割り込みサービスルーチン)からideintr関数が呼び出され、ideintr関数内でハードディスクから読み込んだデータをバッファへ保存する処理やハードディスクへの書き出しが終わったバッファのフラグを変更する処理が行われます。