forked from Github_Repos/cvw
274 lines
7.0 KiB
C
274 lines
7.0 KiB
C
// See LICENSE for license details.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "riscv_test.h"
|
|
|
|
void trap_entry();
|
|
void pop_tf(trapframe_t*);
|
|
|
|
volatile uint64_t tohost;
|
|
volatile uint64_t fromhost;
|
|
|
|
static void do_tohost(uint64_t tohost_value)
|
|
{
|
|
while (tohost)
|
|
fromhost = 0;
|
|
tohost = tohost_value;
|
|
}
|
|
|
|
#define pa2kva(pa) ((void*)(pa) - DRAM_BASE - MEGAPAGE_SIZE)
|
|
#define uva2kva(pa) ((void*)(pa) - MEGAPAGE_SIZE)
|
|
|
|
#define flush_page(addr) asm volatile ("sfence.vma %0" : : "r" (addr) : "memory")
|
|
|
|
static uint64_t lfsr63(uint64_t x)
|
|
{
|
|
uint64_t bit = (x ^ (x >> 1)) & 1;
|
|
return (x >> 1) | (bit << 62);
|
|
}
|
|
|
|
static void cputchar(int x)
|
|
{
|
|
do_tohost(0x0101000000000000 | (unsigned char)x);
|
|
}
|
|
|
|
static void cputstring(const char* s)
|
|
{
|
|
while (*s)
|
|
cputchar(*s++);
|
|
}
|
|
|
|
static void terminate(int code)
|
|
{
|
|
do_tohost(code);
|
|
while (1);
|
|
}
|
|
|
|
void wtf()
|
|
{
|
|
terminate(841);
|
|
}
|
|
|
|
#define stringify1(x) #x
|
|
#define stringify(x) stringify1(x)
|
|
#define assert(x) do { \
|
|
if (x) break; \
|
|
cputstring("Assertion failed: " stringify(x) "\n"); \
|
|
terminate(3); \
|
|
} while(0)
|
|
|
|
#define l1pt pt[0]
|
|
#define user_l2pt pt[1]
|
|
#if __riscv_xlen == 64
|
|
# define NPT 4
|
|
#define kernel_l2pt pt[2]
|
|
# define user_l3pt pt[3]
|
|
#else
|
|
# define NPT 2
|
|
# define user_l3pt user_l2pt
|
|
#endif
|
|
pte_t pt[NPT][PTES_PER_PT] __attribute__((aligned(PGSIZE)));
|
|
|
|
typedef struct { pte_t addr; void* next; } freelist_t;
|
|
|
|
freelist_t user_mapping[MAX_TEST_PAGES];
|
|
freelist_t freelist_nodes[MAX_TEST_PAGES];
|
|
freelist_t *freelist_head, *freelist_tail;
|
|
|
|
void printhex(uint64_t x)
|
|
{
|
|
char str[17];
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
str[15-i] = (x & 0xF) + ((x & 0xF) < 10 ? '0' : 'a'-10);
|
|
x >>= 4;
|
|
}
|
|
str[16] = 0;
|
|
|
|
cputstring(str);
|
|
}
|
|
|
|
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);
|
|
|
|
user_mapping[addr/PGSIZE].addr = 0;
|
|
|
|
if (freelist_tail == 0)
|
|
freelist_head = freelist_tail = node;
|
|
else
|
|
{
|
|
freelist_tail->next = node;
|
|
freelist_tail = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
void handle_fault(uintptr_t addr, uintptr_t cause)
|
|
{
|
|
assert(addr >= PGSIZE && addr < MAX_TEST_PAGES * PGSIZE);
|
|
addr = addr/PGSIZE*PGSIZE;
|
|
|
|
if (user_l3pt[addr/PGSIZE]) {
|
|
if (!(user_l3pt[addr/PGSIZE] & PTE_A)) {
|
|
user_l3pt[addr/PGSIZE] |= PTE_A;
|
|
} else {
|
|
assert(!(user_l3pt[addr/PGSIZE] & PTE_D) && cause == CAUSE_STORE_PAGE_FAULT);
|
|
user_l3pt[addr/PGSIZE] |= PTE_D;
|
|
}
|
|
flush_page(addr);
|
|
return;
|
|
}
|
|
|
|
freelist_t* node = freelist_head;
|
|
assert(node);
|
|
freelist_head = node->next;
|
|
if (freelist_head == freelist_tail)
|
|
freelist_tail = 0;
|
|
|
|
uintptr_t new_pte = (node->addr >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V | PTE_U | PTE_R | PTE_W | PTE_X;
|
|
user_l3pt[addr/PGSIZE] = new_pte | PTE_A | PTE_D;
|
|
flush_page(addr);
|
|
|
|
assert(user_mapping[addr/PGSIZE].addr == 0);
|
|
user_mapping[addr/PGSIZE] = *node;
|
|
|
|
uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM);
|
|
memcpy((void*)addr, uva2kva(addr), PGSIZE);
|
|
write_csr(sstatus, sstatus);
|
|
|
|
user_l3pt[addr/PGSIZE] = new_pte;
|
|
flush_page(addr);
|
|
|
|
__builtin___clear_cache(0,0);
|
|
}
|
|
|
|
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);
|
|
}
|
|
else if (tf->cause == CAUSE_ILLEGAL_INSTRUCTION)
|
|
{
|
|
assert(tf->epc % 4 == 0);
|
|
|
|
int* fssr;
|
|
asm ("jal %0, 1f; fssr x0; 1:" : "=r"(fssr));
|
|
|
|
if (*(int*)tf->epc == *fssr)
|
|
terminate(1); // FP test on non-FP hardware. "succeed."
|
|
else
|
|
assert(!"illegal instruction");
|
|
tf->epc += 4;
|
|
}
|
|
else if (tf->cause == CAUSE_FETCH_PAGE_FAULT || tf->cause == CAUSE_LOAD_PAGE_FAULT || tf->cause == CAUSE_STORE_PAGE_FAULT)
|
|
handle_fault(tf->badvaddr, tf->cause);
|
|
else
|
|
assert(!"unexpected exception");
|
|
|
|
pop_tf(tf);
|
|
}
|
|
|
|
static void coherence_torture()
|
|
{
|
|
// cause coherence misses without affecting program semantics
|
|
unsigned int random = ENTROPY;
|
|
while (1) {
|
|
uintptr_t paddr = DRAM_BASE + ((random % (2 * (MAX_TEST_PAGES + 1) * PGSIZE)) & -4);
|
|
#ifdef __riscv_atomic
|
|
if (random & 1) // perform a no-op write
|
|
asm volatile ("amoadd.w zero, zero, (%0)" :: "r"(paddr));
|
|
else // perform a read
|
|
#endif
|
|
asm volatile ("lw zero, (%0)" :: "r"(paddr));
|
|
random = lfsr63(random);
|
|
}
|
|
}
|
|
|
|
void vm_boot(uintptr_t test_addr)
|
|
{
|
|
unsigned int random = ENTROPY;
|
|
if (read_csr(mhartid) > 0)
|
|
coherence_torture();
|
|
|
|
_Static_assert(SIZEOF_TRAPFRAME_T == sizeof(trapframe_t), "???");
|
|
|
|
#if (MAX_TEST_PAGES > PTES_PER_PT) || (DRAM_BASE % MEGAPAGE_SIZE) != 0
|
|
# error
|
|
#endif
|
|
// map user to lowermost megapage
|
|
l1pt[0] = ((pte_t)user_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V;
|
|
// map kernel to uppermost megapage
|
|
#if __riscv_xlen == 64
|
|
l1pt[PTES_PER_PT-1] = ((pte_t)kernel_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V;
|
|
kernel_l2pt[PTES_PER_PT-1] = (DRAM_BASE/RISCV_PGSIZE << PTE_PPN_SHIFT) | PTE_V | PTE_R | PTE_W | PTE_X | PTE_A | PTE_D;
|
|
user_l2pt[0] = ((pte_t)user_l3pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V;
|
|
uintptr_t vm_choice = SATP_MODE_SV39;
|
|
#else
|
|
l1pt[PTES_PER_PT-1] = (DRAM_BASE/RISCV_PGSIZE << PTE_PPN_SHIFT) | PTE_V | PTE_R | PTE_W | PTE_X | PTE_A | PTE_D;
|
|
uintptr_t vm_choice = SATP_MODE_SV32;
|
|
#endif
|
|
write_csr(satp, ((uintptr_t)l1pt >> PGSHIFT) |
|
|
(vm_choice * (SATP_MODE & ~(SATP_MODE<<1))));
|
|
|
|
// Set up PMPs if present, ignoring illegal instruction trap if not.
|
|
uintptr_t pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X;
|
|
asm volatile ("la t0, 1f\n\t"
|
|
"csrrw t0, mtvec, t0\n\t"
|
|
"csrw pmpaddr0, %1\n\t"
|
|
"csrw pmpcfg0, %0\n\t"
|
|
".align 2\n\t"
|
|
"1:"
|
|
: : "r" (pmpc), "r" (-1UL) : "t0");
|
|
|
|
// set up supervisor trap handling
|
|
write_csr(stvec, pa2kva(trap_entry));
|
|
write_csr(sscratch, pa2kva(read_csr(mscratch)));
|
|
write_csr(medeleg,
|
|
(1 << CAUSE_USER_ECALL) |
|
|
(1 << CAUSE_FETCH_PAGE_FAULT) |
|
|
(1 << CAUSE_LOAD_PAGE_FAULT) |
|
|
(1 << CAUSE_STORE_PAGE_FAULT));
|
|
// FPU on; accelerator on; allow supervisor access to user memory access
|
|
write_csr(mstatus, MSTATUS_FS | MSTATUS_XS);
|
|
write_csr(mie, 0);
|
|
|
|
random = 1 + (random % MAX_TEST_PAGES);
|
|
freelist_head = pa2kva((void*)&freelist_nodes[0]);
|
|
freelist_tail = pa2kva(&freelist_nodes[MAX_TEST_PAGES-1]);
|
|
for (long i = 0; i < MAX_TEST_PAGES; i++)
|
|
{
|
|
freelist_nodes[i].addr = DRAM_BASE + (MAX_TEST_PAGES + random)*PGSIZE;
|
|
freelist_nodes[i].next = pa2kva(&freelist_nodes[i+1]);
|
|
random = LFSR_NEXT(random);
|
|
}
|
|
freelist_nodes[MAX_TEST_PAGES-1].next = 0;
|
|
|
|
trapframe_t tf;
|
|
memset(&tf, 0, sizeof(tf));
|
|
tf.epc = test_addr - DRAM_BASE;
|
|
pop_tf(&tf);
|
|
}
|