ex20-mmu.c (4037B)
1 // Copyright 2022, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0 3 4 #include <hw/riscv.h> 5 #include <hw/context.h> 6 #include <hw/debug.h> 7 #include <hw/intrinsics.h> 8 9 #include <hw/platform.h> 10 #include <hw/litex.h> 11 12 #include <string.h> 13 14 // use inline assembly to defeat gcc from converting stuff like 15 // *((unsigned*) 0) = 42; to unimplemented instructions... 16 17 static inline void wr32(uint32_t addr, uint32_t val) { 18 asm volatile ("sw %0, 0(%1)" :: "r"(val), "r"(addr)); 19 } 20 static inline uint32_t rd32(uint32_t addr) { 21 uint32_t val; 22 asm volatile ("lw %0, 0(%1)" : "=r"(val) : "r"(addr)); 23 return val; 24 } 25 26 // this ideally should do a read safe from faulting 27 uint32_t read32_safe(uint32_t addr) { 28 return *((volatile uint32_t*) addr); 29 } 30 31 void dump_pte(uint32_t addr, uint32_t vaddr, uint32_t pte, uint32_t sz) { 32 char bits[11]; 33 for (unsigned n = 0; n < 10; n++) { 34 bits[9-n] = (pte & (1<<n)) ? "vrwxugad01"[n] : '-'; 35 } 36 bits[10] = 0; 37 xprintf("%08x %08x %08x0 %08x %s\n", 38 addr, vaddr, (pte & 0xFFFFFC00) >> 2, sz, bits); 39 } 40 41 void dump_mmu0(uint32_t ptbl, uint32_t vaddr) { 42 for (unsigned n = 0; n < 1024; n++) { 43 uint32_t addr = ptbl + n * 4; 44 uint32_t pte = read32_safe(addr); 45 dump_pte(addr, vaddr | (n << 12), pte, 4*1024); 46 } 47 } 48 49 void dump_mmu(void *_ptbl) { 50 xprintf("pte addr vaddr paddr size flags\n"); 51 xprintf("-------- -------- --------- -------- ----------\n"); 52 uint32_t ptbl = (uint32_t) _ptbl; 53 for (unsigned n = 0; n < 1024; n++) { 54 uint32_t addr = ptbl + n * 4; 55 uint32_t pte = read32_safe(addr); 56 if (pte & PTE_V) { 57 dump_pte(addr, n << 22, pte, 4*1024*1024); 58 if (!(pte & (PTE_X|PTE_W|PTE_R))) { 59 dump_mmu0((pte & 0xFFFFFC00) << 2, n << 22); 60 } 61 } 62 } 63 } 64 65 uint32_t alloc_top = 0x40000000 + 4*1024*1024; 66 67 void* alloc_page(void) { 68 alloc_top -= 4096; 69 return (void*) alloc_top; 70 } 71 72 volatile uint32_t expect_cause = 0; 73 volatile uint32_t expect_addr = 0; 74 75 void start(void) { 76 xprintf("Example 20 - MMU\n\n"); 77 78 // set trap vector to trap_entry() in trap-entry-single.S 79 // it will call exception_handler() or interrupt_handler() 80 csr_write(CSR_STVEC, (uintptr_t) trap_entry); 81 82 uint32_t* pt1 = alloc_page(); 83 memset(pt1, 0, 4096); 84 85 // map ram as a set of 4MB pages 1:1 86 uint32_t phys; 87 for (phys = 0x40000000 ; phys < 0x42000000; phys += 0x00400000) { 88 pt1[phys >> 22] = (phys >> 2) | PTE_D | PTE_A | PTE_X | PTE_W | PTE_R | PTE_V; 89 } 90 91 // unset A & D bit for second to last 4MB page 92 pt1[0x41800000 >> 22] &= ~(PTE_A | PTE_D); 93 94 // map mmio region 1:1 95 pt1[0xF0000000 >> 22] = (0xF0000000 >> 2) | PTE_D | PTE_A | PTE_W | PTE_R | PTE_V; 96 97 dump_mmu(pt1); 98 99 // enable MMU 100 csr_write(CSR_SATP, SATP_MODE_SV32 | ((uintptr_t) pt1) >> 12); 101 tlb_flush_all(); 102 asm volatile ("nop ; nop ; nop ; nop ; nop"); 103 104 xprintf("\n[ mmu enabled ]\n\n"); 105 106 // read and write second to last page and observe the A & D bits changing 107 // note: CPU is allowed to fault instead of setting A or D 108 // qemu sets A/D and does not fault 109 // vexrisc does not set A or D but also does not fault 110 111 xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); 112 xprintf("[ read from 0x41800000 ]\n"); 113 rd32(0x41800000); 114 xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); 115 116 xprintf("[ write to 0x41800000 ]\n"); 117 wr32(0x41800000, 17); 118 xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); 119 120 121 // cause some faults 122 expect_cause = EXCn_STORE_PAGE_FAULT; 123 expect_addr = 0; 124 wr32(0, 42); 125 126 expect_cause = EXCn_LOAD_PAGE_FAULT; 127 expect_addr = 0x100000; 128 rd32(0x100000); 129 130 void (*fn)(void) = (void*) 0x777000; 131 fn(); 132 } 133 134 void interrupt_handler(void) { 135 } 136 137 // if an exception occurs, dump register state and halt 138 void exception_handler(eframe_t *ef) { 139 xprintf("\n** SUPERVISOR EXCEPTION **\n"); 140 xprint_s_exception(ef); 141 142 if ((expect_cause == csr_read(CSR_SCAUSE)) && 143 (expect_addr == csr_read(CSR_STVAL))) { 144 ef->pc += 4; 145 xprintf("\n"); 146 return; 147 } 148 149 xprintf("\nHALT\n"); 150 for (;;) ; 151 } 152