os-workshop

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 770df1b47ce3e35effe57ec5bd959e8ca0eaf87c
parent ea8243df5b60e90e830a5cf8a76dcbf19093785d
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat, 14 May 2022 00:00:20 -0700

example: mmu setup, faults, A/D bits

Diffstat:
Aexample/ex20-mmu.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aproject/ex20-mmu.app.mk | 8++++++++
2 files changed, 160 insertions(+), 0 deletions(-)

diff --git a/example/ex20-mmu.c b/example/ex20-mmu.c @@ -0,0 +1,152 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/riscv.h> +#include <hw/context.h> +#include <hw/debug.h> +#include <hw/intrinsics.h> + +#include <hw/platform.h> +#include <hw/litex.h> + +#include <string.h> + +// use inline assembly to defeat gcc from converting stuff like +// *((unsigned*) 0) = 42; to unimplemented instructions... + +static inline void wr32(uint32_t addr, uint32_t val) { + asm volatile ("sw %0, 0(%1)" :: "r"(val), "r"(addr)); +} +static inline uint32_t rd32(uint32_t addr) { + uint32_t val; + asm volatile ("lw %0, 0(%1)" : "=r"(val) : "r"(addr)); + return val; +} + +// this ideally should do a read safe from faulting +uint32_t read32_safe(uint32_t addr) { + return *((volatile uint32_t*) addr); +} + +void dump_pte(uint32_t addr, uint32_t vaddr, uint32_t pte, uint32_t sz) { + char bits[11]; + for (unsigned n = 0; n < 10; n++) { + bits[9-n] = (pte & (1<<n)) ? "vrwxugad01"[n] : '-'; + } + bits[10] = 0; + xprintf("%08x %08x %08x0 %08x %s\n", + addr, vaddr, (pte & 0xFFFFFC00) >> 2, sz, bits); +} + +void dump_mmu0(uint32_t ptbl, uint32_t vaddr) { + for (unsigned n = 0; n < 1024; n++) { + uint32_t addr = ptbl + n * 4; + uint32_t pte = read32_safe(addr); + dump_pte(addr, vaddr | (n << 12), pte, 4*1024); + } +} + +void dump_mmu(void *_ptbl) { + xprintf("pte addr vaddr paddr size flags\n"); + xprintf("-------- -------- --------- -------- ----------\n"); + uint32_t ptbl = (uint32_t) _ptbl; + for (unsigned n = 0; n < 1024; n++) { + uint32_t addr = ptbl + n * 4; + uint32_t pte = read32_safe(addr); + if (pte & PTE_V) { + dump_pte(addr, n << 22, pte, 4*1024*1024); + if (!(pte & (PTE_X|PTE_W|PTE_R))) { + dump_mmu0((pte & 0xFFFFFC00) << 2, n << 22); + } + } + } +} + +uint32_t alloc_top = 0x40000000 + 4*1024*1024; + +void* alloc_page(void) { + alloc_top -= 4096; + return (void*) alloc_top; +} + +volatile uint32_t expect_cause = 0; +volatile uint32_t expect_addr = 0; + +void start(void) { + xprintf("Example 20 - MMU\n\n"); + + // set trap vector to trap_entry() in trap-entry-single.S + // it will call exception_handler() or interrupt_handler() + csr_write(CSR_STVEC, (uintptr_t) trap_entry); + + uint32_t* pt1 = alloc_page(); + memset(pt1, 0, 4096); + + // map ram as a set of 4MB pages 1:1 + uint32_t phys; + for (phys = 0x40000000 ; phys < 0x42000000; phys += 0x00400000) { + pt1[phys >> 22] = (phys >> 2) | PTE_D | PTE_A | PTE_X | PTE_W | PTE_R | PTE_V; + } + + // unset A & D bit for second to last 4MB page + pt1[0x41800000 >> 22] &= ~(PTE_A | PTE_D); + + // map mmio region 1:1 + pt1[0xF0000000 >> 22] = (0xF0000000 >> 2) | PTE_D | PTE_A | PTE_W | PTE_R | PTE_V; + + dump_mmu(pt1); + + // enable MMU + csr_write(CSR_SATP, 0x80000000 | ((uintptr_t) pt1) >> 12); + tlb_flush_all(); + asm volatile ("nop ; nop ; nop ; nop ; nop"); + + xprintf("\n[ mmu enabled ]\n\n"); + + // read and write second to last page and observe the A & D bits changing + // note: CPU is allowed to fault instead of setting A or D + // qemu sets A/D and does not fault + // vexrisc does not set A or D but also does not fault + + xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); + xprintf("[ read from 0x41800000 ]\n"); + rd32(0x41800000); + xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); + + xprintf("[ write to 0x41800000 ]\n"); + wr32(0x41800000, 17); + xprintf("PTE for vaddr 41800000 is %08x\n", rd32((uintptr_t) &pt1[0x41800000 >> 22])); + + + // cause some faults + expect_cause = EXCn_STORE_PAGE_FAULT; + expect_addr = 0; + wr32(0, 42); + + expect_cause = EXCn_LOAD_PAGE_FAULT; + expect_addr = 0x100000; + rd32(0x100000); + + void (*fn)(void) = (void*) 0x777000; + fn(); +} + +void interrupt_handler(void) { +} + +// if an exception occurs, dump register state and halt +void exception_handler(eframe_t *ef) { + xprintf("\n** SUPERVISOR EXCEPTION **\n"); + xprint_s_exception(ef); + + if ((expect_cause == csr_read(CSR_SCAUSE)) && + (expect_addr == csr_read(CSR_STVAL))) { + ef->pc += 4; + xprintf("\n"); + return; + } + + xprintf("\nHALT\n"); + for (;;) ; +} + diff --git a/project/ex20-mmu.app.mk b/project/ex20-mmu.app.mk @@ -0,0 +1,8 @@ + +MOD_NAME := ex20-mmu +MOD_SRC := hw/src/start.S hw/src/trap-entry-single-stack.S +MOD_SRC += example/ex20-mmu.c +MOD_SRC += hw/src/context-switch.S +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c hw/src/print-exception.c +MOD_LIB := c +include make/app.mk