読者です 読者をやめる 読者になる 読者になる

FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

xv6のブート時に実行していること(ブート時の初期化とxchg命令)

xv6

自作ISSで少しずつxv6が起動してきた。

最初に実行されるのはmain.cのmain()だが、これはどのようにして起動しているのか少し見て行こう。

そういえば自分でxv6の教科書を翻訳していたのだった。これを参考に進めて行く。

github.com

int
main(void)
{
  kinit1(end, P2V(4*1024*1024)); // phys page allocator
  kvmalloc();      // kernel page table
  mpinit();        // collect info about this machine
  lapicinit();
  seginit();       // set up segments
  cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
  picinit();       // interrupt controller
  ioapicinit();    // another interrupt controller
  consoleinit();   // I/O devices & their interrupts
  uartinit();      // serial port
  pinit();         // process table
  tvinit();        // trap vectors
  binit();         // buffer cache
  fileinit();      // file table
  ideinit();       // disk
  if(!ismp)
    timerinit();   // uniprocessor timer
  startothers();   // start other processors
  kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
  userinit();      // first user process
  // Finish setting up this processor in mpmain.
  mpmain();
}

kinit1, kinit2 : メモリの初期化

kinit1は先頭から4MBの初期化を行い、kinit2はロック系の変数を含めた全体の初期化を行う。

// 1. main() calls kinit1() while still using entrypgdir to place just
// the pages mapped by entrypgdir on free list.
// 2. main() calls kinit2() with the rest of the physical pages
// after installing a full page table that maps them on all cores.
void
kinit1(void *vstart, void *vend)
{
  initlock(&kmem.lock, "kmem");
  kmem.use_lock = 0;
  freerange(vstart, vend);
}

kvmalloc : カーネル用のページマッピング

vmallocはカーネルが実行するために、KERNBASEよりも上のマッピングを実行する。 最初にメモリのページを確保し、ページディレクトリを保持する。mappages()を実行し、メモリマップを作成する。

setupkvm(void)
{
  pde_t *pgdir;
  struct kmap *k;

  if((pgdir = (pde_t*)kalloc()) == 0)
    return 0;
  memset(pgdir, 0, PGSIZE);
  if (p2v(PHYSTOP) > (void*)DEVSPACE)
    panic("PHYSTOP too high");
  for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
    if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
                (uint)k->phys_start, k->perm) < 0)
      return 0;
  return pgdir;
}

xchg命令について(x86)

x86.hでは、xchgを実行するためのコードが入っている。xchg命令については、sourceとdestinationをアトミックな交換、という命令だ。

softwaretechnique.jp

xchg(volatile uint *addr, uint newval)
{
  uint result;

  // The + in "+m" denotes a read-modify-write operand.
  asm volatile("lock; xchgl %0, %1" :
               "+m" (*addr), "=a" (result) :
               "1" (newval) :
               "cc");
  return result;
}

これをRISC-Vに置き換えなければならないのだが、これを置き換えるのには、amoswap命令が調度いいだろう。

static inline uint
xchg(volatile uint *addr, uint newval)
{
  uint result;
  asm volatile ("amoswap.w %0, %0, (%1)":
                "=r" (result), "+r" (*addr) :
                "r" (newval));
  return result;
}

コンパイル後のコードをダンプしてみると、以下の様になっている。

80104e8c <xchg>:
80104e8c:       fd010113                addi    sp,sp,-48
80104e90:       02812623                sw      s0,44(sp)
80104e94:       03010413                addi    s0,sp,48
80104e98:       fca42e23                sw      a0,-36(s0)
80104e9c:       fcb42c23                sw      a1,-40(s0)
80104ea0:       fdc42783                lw      a5,-36(s0)
80104ea4:       0007a783                lw      a5,0(a5) # 80000000 <end+0xffee9458>
80104ea8:       fd842703                lw      a4,-40(s0)
80104eac:       08e7a72f                amoswap.w       a4,a4,(a5)
80104eb0:       fee42623                sw      a4,-20(s0)
80104eb4:       fdc42703                lw      a4,-36(s0)
80104eb8:       00f72023                sw      a5,0(a4)
80104ebc:       fec42783                lw      a5,-20(s0)
80104ec0:       00078513                mv      a0,a5
80104ec4:       02c12403                lw      s0,44(sp)
80104ec8:       03010113                addi    sp,sp,48
80104ecc:       00008067                ret