vm.c int allocuvm(pde_t *pgdir, uint oldsz, uint newsz)

トップページ
jupiteroak.hatenablog.com


vm.c

int allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
  char *mem;
  uint a;

  if(newsz >= KERNBASE)
    return 0;
  if(newsz < oldsz)
    return oldsz;

  a = PGROUNDUP(oldsz);
  for(; a < newsz; a += PGSIZE){
    mem = kalloc();
    if(mem == 0){
      cprintf("allocuvm out of memory\n");
      deallocuvm(pgdir, newsz, oldsz);
      return 0;
    }
    memset(mem, 0, PGSIZE);
    if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
      cprintf("allocuvm out of memory (2)\n");
      deallocuvm(pgdir, newsz, oldsz);
      kfree(mem);
      return 0;
    }
  }
  return newsz;
}

allocuvm関数は、ページディレクトリpgdirを使って、oldz以上newsz未満の仮想アドレスの範囲に対し、ページフレームを必要な数だけマッピングします。

引数 pde_t *pgdir
ページディレクトリの先頭アドレスです。

引数 uint oldsz
ページフレームのマッピングの対象となる仮想アドレスの範囲における、先頭アドレスです。

引数 uint newsz
ページフレームのマッピングの対象となる仮想アドレスの範囲における、終端アドレスです。

戻り値 int newsz または 0 または int oldsz
ページフレームのマッピングが成功した場合は、newszが戻り値となります。
ページフレームのマッピングが失敗した場合は、0が戻り値となります。
ページフレームのマッピングを行わなかった場合は、oldszが戻り値となります。


処理の内容

アドレスnewszがカーネル空間に入ってしまう場合は処理を終了する

if(newsz >= KERNBASE)
    return 0;

newsz >= KERNBASE(#define KERNBASE 0x8000 0000)が真となる場合→アドレスnewszがカーネル空間に入ってしまう場合は、0を戻り値として処理を終了します。

アドレスoldszが下位側・アドレスnewszが上位側にあることを確認する

if(newsz < oldsz)
    return oldsz;

newsz < oldszが真となる場合は→アドレスoldszが下位側・アドレスnewszが上位側ではない場合は、0を戻り値として処理を終了します。

アドレスoldszを4KB境界に切り上げる

 a = PGROUNDUP(oldsz);

仮想アドレスoldzから仮想アドレスnewsz未満の範囲に対してページフレームの割り当てとマッピングを行う

for(; a < newsz; a += PGSIZE){
    mem = kalloc();
    if(mem == 0){
      cprintf("allocuvm out of memory\n");
      deallocuvm(pgdir, newsz, oldsz);
      return 0;
    }
    memset(mem, 0, PGSIZE);
    if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
      cprintf("allocuvm out of memory (2)\n");
      deallocuvm(pgdir, newsz, oldsz);
      kfree(mem);
      return 0;
    }
  }

ページフレームとして使用する4KBのメモリ領域を割り当てる

mem = kalloc();

kalloc関数を呼び出して、ページフレームとして使用する4KBのメモリ領域を割り当てます。

メモリ領域の割り当てに失敗した場合はメモリ領域を解放して処理を終了
if(mem == 0){
  cprintf("allocuvm out of memory\n");
  deallocuvm(pgdir, newsz, oldsz);
  return 0;
}

mem == 0が真となる場合→メモリ領域の割り当てに失敗した場合は、cprintf関数を呼び出して、メッセージを出力します。
次に、deallocuvm関数を呼び出して、仮想アドレスoldszから仮想アドレス未満newszの範囲にマッピングされている全てのページフレームを解放します。
最後に、0を戻り値として処理を終了します。

ページフレームを0で初期化する

memset(mem, 0, PGSIZE);

memset関数を呼び出して、ページフレームとして使用する4KB(#define PGSIZE 4096)のメモリ領域を0で初期化します。

ページフレームのマッピングを行う

if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
  cprintf("allocuvm out of memory (2)\n");
  deallocuvm(pgdir, newsz, oldsz);
  kfree(mem);
  return 0;
}

mappages関数を呼び出して、仮想アドレスa~a+0x0FFF(ページ)と物理アドレスV2P(mem)~V2P(mem)+0x0FFF(ページフレーム)のマッピングを設定します。
ページフレームとして使用する4KBのメモリ領域の先頭アドレスmemは仮想アドレスなので、V2Pマクロを使って物理アドレスにV2P(mem)に変換しています。
マッピングで使用するPTEのbit1(ページテーブルエントリのR/Wフラグ)を1、PTEのbit2(ページテーブルエントリのU/Sフラグ)を1にするために、mappages関数の第3引数にPTE_W(#define PTE_W 0x002)とPTE_U(#define PTE_U 0x004)の論理和を指定しています。
PTEのbit1(ページテーブルエントリのR/Wフラグ)が1の時は、ページフレームが読み書き可能であることを示しています。
PTEのbit2(ページテーブルエントリのU/Sフラグ)が1の時は、ユーザープロセスがページフレームにアクセスできることを示しています。

マッピングに失敗した場合はメモリ領域を解放して処理を終了

mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0 が真の場合→マッピングに失敗した場合は、cprintf関数を呼び出してメッセージを出力します。
次に、deallocuvm関数を呼び出して、仮想アドレスoldszから仮想アドレス未満newszの範囲にマッピングされているページフレームを解放し、kfree関数を呼び出して、ページフレーム(割り当てられていた4KBのメモリ領域)を解放します。
最後に、0を戻り値として処理を終了します。

アドレスnewszを戻り値としてリターンする

return newsz;