な毎か

なんて事ない毎日がかけがえないんだよなぁ…

x86(i386)向けのOS作成日記 - メモリマネージャ1

芸カ参加。サークル参加者に欠席多し、一般参加者も減っている感触。それを受けたのか分からんが次回開催は蒲田らしい。

現在はメモリ管理マネージャを実装中。最初はメモリ領域テストから:

#define EFLAGS_AC_BIT     0x00040000
#define CR0_CACHE_DISABLE 0x60000000

/* start - end間で使用可能なメモリサイズを計測 */
static uintptr_t memory_testsize(uintptr_t start, uintptr_t end);

uintptr_t Memory_Test(uintptr_t start, uintptr_t end)
{
  uint8_t is_486flag = 0;
  uint32_t eflags, cr0;
  uint32_t memory_size;

  /* 386 / 486以降なのかの判定 */
  eflags = io_load_eflags();
  eflags |= EFLAGS_AC_BIT;   /* ACビットを立てる */
  io_store_eflags(eflags);

  /* 386ではAC=1としても元に戻ってしまう */
  eflags = io_load_eflags();
  if ((eflags & EFLAGS_AC_BIT) != 0) {
    is_486flag = 1; /* フラグが立っていたら486 */
  }

  /* ACビットをクリア */
  eflags &= ~EFLAGS_AC_BIT;
  io_store_eflags(eflags);

  /* キャッシュ禁止 */
  if (is_486flag != 0) {
    cr0 = load_cr0();
    cr0 |= CR0_CACHE_DISABLE;
    store_cr0(cr0);
  }

  /* メモリサイズ計算サブルーチン呼び出し */
  memory_size = memory_testsize(start, end);

  /* キャッシュ許可 */
  if (is_486flag != 0) {
    cr0 = load_cr0();
    cr0 &= ~CR0_CACHE_DISABLE;
    store_cr0(cr0);
  }

  return memory_size;
    
}

static uint32_t memory_testsize(uint32_t start, uint32_t end)
{
  uintptr_t i, *p;
  uint32_t tmp_old;
  uint32_t pattern     = 0xAA55AA55; /* 書き込み確認するパターン */
  uint32_t pattern_rev = ~pattern;   /* パターンのビット反転    */

  /* 4KBずつ走査 */
  for (i = start; i <= end; i += 0x1000) {
    p   = (uintptr_t *)(i + 0xFFC); /* 4KBの内、末尾4バイトを見る */
    tmp_old = *p;                   /* 前の値を保存 */
    *p  = pattern;                  /* パターンで書き込む */
    *p  ^= 0xFFFFFFFF;              /* それを反転して代入 */
    if (*p != pattern_rev) {        /* 反転結果と一致するか? */
      *p = tmp_old;                 /* しなければ元に戻して終了 */
      break;
    }
    *p  ^= 0xFFFFFFFF;              /* もう一度反転 */
    if (*p != pattern) {            /* 元に戻るか? */
      *p = tmp_old;                 /* 戻らなければ終了 */
      break;    
    }
    *p = tmp_old;                   /* 元の値に戻す */
  }

  return i;
}

メモリ反転書き込みが失敗したところまでの領域が有効とみなされる。

なぜ反転書き込みテストが有効なのかは未知。(全体の)メモリ範囲外に書き込んだら一体どうなるのか…調べる必要はある。

呼び出し側(kernel_main.c)は次の通り:

  /* メモリサイズテスト */
  memory_size = Memory_Test(0x00400000, 0xBFFFFFFF);
  my_sprintf(str_buf, "Memory:%dMB", memory_size >> 20); /* MB単位に直す:2^20 */
  putfonts8_withback(binfo->vram, binfo->scrnx, 8, 41, COL8_FFFFFF, str_buf, COL8_000000);

コンパイル時に-O0だと正しく32MBとの結果が返ってくる(32MBになっているのはqemu-m 32に依る)。だが-O1, -O2, -O3, -O4だとやっぱり駄目で3072MBの結果が返ってくる。OS自作だとアセンブリを書くことで対処していたが、ここでは少しvolatileで対処できないか探ってみる。

この場合だと、*p周りで最適化が働いてしまって(*pの読み出しに副作用がないとしているから)望む結果が得られない。そこで、*pvolatile属性を付与した所、最適化オプションを付けても問題なく動作するようになった。

  volatile uintptr_t *p;

でも、実はvolatileを正しく理解していないフシが有る…(取り敢えずつけとけば最適化抑止できるんでしょ?位の感覚。)

qemuのメモリサイズと最適化オプションを色々変えても正しくメモリサイズを計れるようになったので、ここで今日の作業は打ち切る。しかしまだ不明確な点が3つ。要調査。

  1. メモリ反転書き込みでなぜ正しく有効なメモリサイズが計れるのか?
  2. EFLAGS_AC_BIT, CR0_CACHE_DISABLE: EFLAGSCR0のビットフィールドをまとめる。初めて読む486に書いてあるはず。
  3. 上記volatileに関する事項。voltile記憶クラス指定子とは一体何か?

後、機能要望として、キーボードとマウスの取得したコードのデコードを行いたい。