riscv-testsには、もう一つ、vm.c
にevict()
という関数が用意されている。evict()
はどうもECALL
が呼び出されたときに実行されるようだ。ECALL
はテストパタンが終了したときに呼ばれるので、最後の処理に使用されるようだ。
void handle_trap(trapframe_t* tf) { if (tf->cause == CAUSE_USER_ECALL) { int n = tf->gpr[10]; for (long i = 1; i < MAX_TEST_PAGES; i++) evict(i*PGSIZE); terminate(n); }
最後にterminate()
が呼び出されるので、終了前の処理であることは間違いない。evict()
なので読みだしたページテーブルを吐き出しているのだろうけども、良く分からないのがmemcpy
の方向がページテーブル割り当ての時と同じこと。カーネルモードのアドレスと、ユーザモードのアドレスが一致している場合にはmemcpy()
は実行しないが一致していない場合にはmemcpy()
でデータをコピーする。
static void evict(unsigned long addr) { assert(addr >= PGSIZE && addr < MAX_TEST_PAGES * PGSIZE); addr = addr/PGSIZE*PGSIZE; freelist_t* node = &user_mapping[addr/PGSIZE]; if (node->addr) { // check accessed and dirty bits assert(user_l3pt[addr/PGSIZE] & PTE_A); uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM); if (memcmp((void*)addr, uva2kva(addr), PGSIZE)) { assert(user_l3pt[addr/PGSIZE] & PTE_D); memcpy((void*)addr, uva2kva(addr), PGSIZE); } write_csr(sstatus, sstatus); /* ... 中略 ... */
ログを見てみると、0xffff
から始まるユーザモードから、0x0000
から始まるカーネルモードへのコピーをしているようだ。
13430:S:Sv39:ffffffffffe027b0:P0000800027b0:000d8593:addi x11,x27,0x000 :x27=>ffffffffffe04000 x11<=ffffffffffe04000 13431:S:Sv39:ffffffffffe027b4:P0000800027b4:00040513:addi x10,x08,0x000 :x08=>0000000000004000 x10<=0000000000004000 13432:S:Sv39:ffffffffffe027b8:P0000800027b8:849ff0ef:jal x01,43231 :x01<=ffffffffffe027bc pc<=ffffffffffe02000 // x10 = カーネルモードでのメモリ領域 // x11 = ユーザモードでのメモリ領域
新たに割り当てたNew Pageからカーネルの用意しているページへデータを書き戻しているものと思われる、が、新たに確保したページ側に書き戻しているのはなんでだ?
13441:S:Sv39:ffffffffffe02044:P000080002044:ff85b703:ld x14,0xff8(x11) :x11=>ffffffffffe04008 (0000000080004000)=>deadbeefdeadbeef x14<=deadbeefdeadbeef 13443:S:Sv39:ffffffffffe0204c:P00008000204c:fee7bc23:sd x14,0xff8(x15) :x15=>0000000000004008 x14=>deadbeefdeadbeef (0000000080064000)<=deadbeefdeadbeef