os-workshop

same materials and sample source for RV32 OS projects
git clone http://frotz.net/git/os-workshop.git
Log | Files | Refs

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