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);
 | 
						|
}
 |