bootmain.c void readseg(uchar* pa, uint count, uint offset)

トップページ
jupiteroak.hatenablog.com


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

void
readseg(uchar* pa, uint count, uint offset)
{
  uchar* epa;

  epa = pa + count;

  // Round down to sector boundary.
  pa -= offset % SECTSIZE;

  // Translate from bytes to sectors; kernel starts at sector 1.
  offset = (offset / SECTSIZE) + 1;

  // If this is too slow, we could read lots of sectors at a time.
  // We'd write more to memory than asked, but it doesn't matter --
  // we load in increasing order.
  for(; pa < epa; pa += SECTSIZE, offset++)
    readsect(pa, offset);
}

readseg関数は、ELFファイル(kernelプログラム)内における位置offsetから、paを先頭アドレスとするメモリ領域へ、countで指定されたサイズのデータをロードします。


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

引数 count
ロードされるデータのサイズ(バイト単位)です。

引数 offset
ELFファイル先頭からの位置をバイトオフセットとして示した値です。


処理の内容

ロード先のメモリ領域の終端アドレス+1のアドレス値を求める

epa = pa + count;

ロード先のメモリ領域の先頭アドレスを512バイト境界のアドレス値に切り下げる

pa -= offset % SECTSIZE;

ロード先のメモリ領域の先頭アドレス値paを512バイト境界になるように切り下げています。
後半にあるreadsect関数では、ハードディスクからメインメモリへSECTSIZEバイト(512バイト)ずつデータがロードされるので、メインメモリ上でデータを整列させるためにこのような処理を行っていると思われます。例えば、offsetの値が0x0804 95e4の場合、pa(-= offset % SECTSIZE)の値は0x08004 9400になります(具体的な数字は、リンカ・ローダ実践開発テクニックp52から引用)。

セグメントのオフセットアドレスからそのオフセットが位置するセクタを求める

offset = (offset / SECTSIZE) + 1;

ELFファイル(kernelプログラム)内における位置を示した値offsetから、そのoffsetが位置するセクタのセクタ番号(28bitLBA値)を求めています。
最初の1番目のセクタのセクタ番号(28bitLBA値)は0です。2番目のセクタのセクタ番号(28bitLBA値)は1です。
最初の1番目のセクタには、ブートセクタ(bootblock)のプログラムが格納されていています。
2番目のセクタ以降から、ELFファイル(kernelプログラム)が格納されています。
そのため、セクタ番号(28bitLBA値)が必ず1以上の値になるように (offset / SECTSIZE) + 1 としています。

データをロードする

 for(; pa < epa; pa += SECTSIZE, offset++)
    readsect(pa, offset);

paを先頭アドレスとするメモリ領域に、セグメントをロードします。
readsect関数は、セクタ番号offset(28bitLBA値)で指定されたセクタのデータ(512バイトのデータ)を、paで指定された物理アドレス以降にロードします。
この関数処理を繰り返して、countで指定されたサイズ分のデータをロードします。
pa(ロード先のアドレス)がepa(ロード先のメモリ領域の終端アドレス)を越えたところで、繰り返しを終了します。
SECTSIZEバイト(512バイト)ごとにデータをロードするので、readsect関数で指定する引数paの値(ロード先のアドレス)をSECTSIZEバイト(512バイト)インクリメントします。それに伴って、readsect関数で指定する引数offsetの値も(ロード元のセクタに対応する28bitLBA値)も+1インクリメントします。