summaryrefslogtreecommitdiff
path: root/tests/test-progs/asmtest/src/riscv/env/v/vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-progs/asmtest/src/riscv/env/v/vm.c')
-rw-r--r--tests/test-progs/asmtest/src/riscv/env/v/vm.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/tests/test-progs/asmtest/src/riscv/env/v/vm.c b/tests/test-progs/asmtest/src/riscv/env/v/vm.c
new file mode 100644
index 000000000..a2e5533c4
--- /dev/null
+++ b/tests/test-progs/asmtest/src/riscv/env/v/vm.c
@@ -0,0 +1,273 @@
+// 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(sptbr, ((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);
+}