自作ISSで少しずつxv6が起動してきた。
最初に実行されるのはmain.cのmain()だが、これはどのようにして起動しているのか少し見て行こう。
そういえば自分でxv6の教科書を翻訳していたのだった。これを参考に進めて行く。
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をアトミックな交換、という命令だ。
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