From 5bb973e8daa1c2c783e9b2864897c640a8d7bcd0 Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Thu, 19 Oct 2023 21:00:12 +0200 Subject: [PATCH] Added MMU --- src/cpu/exception.c | 25 ++++++++++ src/cpu/exception.h | 19 ++++++++ src/cpu/rv32cpu.c | 2 +- src/memory/memory.c | 13 +++++ src/memory/mmu.c | 114 ++++++++++++++++++++++++++++++++++++++++++++ src/memory/mmu.h | 9 ++++ 6 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/cpu/exception.c create mode 100644 src/cpu/exception.h create mode 100644 src/memory/mmu.c create mode 100644 src/memory/mmu.h diff --git a/src/cpu/exception.c b/src/cpu/exception.c new file mode 100644 index 0000000..53af166 --- /dev/null +++ b/src/cpu/exception.c @@ -0,0 +1,25 @@ +#include "exception.h" + +#include + +void exception_trigger(rv32_cpu_t* cpu, uint32_t scause) +{ + // An exception can only be triggered by the CPU itself, + // so we know we already own the mutex + // We are in the CPU thread itself, but we need + // the return of this function to be the beginning of + // the cpu loop + // To achieve that, we can just call cpu_loop (noreturn) + // at the end of this function + + // Save execution context, so that 'mret/sret/..' can restore it + // TODO + + // Set PC to STVEC, and set SCAUSE + // TODO: If PC cannot be mmu_resolved, throw a 'double fault' ? + cpu->pc = cpu->csr[CSR_STVEC]; + cpu->csr[CSR_SCAUSE] = scause; + + pthread_mutex_unlock(&cpu0_mutex); + cpu_loop(cpu); +} diff --git a/src/cpu/exception.h b/src/cpu/exception.h new file mode 100644 index 0000000..0e72d80 --- /dev/null +++ b/src/cpu/exception.h @@ -0,0 +1,19 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include "rv32cpu.h" + +void exception_trigger(rv32_cpu_t* cpu, uint32_t scause); + +#define SCAUSE_INSTRUCTION_MISSALIGNED 0x0 +#define SCAUSE_INSTRUCTION_ACCESS_FAULT 0x1 +#define SCAUSE_ILLEGAL_INSTRUCTION 0x2 +#define SCAUSE_BREAKPOINT 0x3 +#define SCAUSE_LOAD_ACCESS_FAULT 0x5 +#define SCAUSE_AMO_ADDRESS_MISALIGNED 0x6 +#define SCAUSE_ENVIRONMENT_CALL 0x8 +#define SCAUSE_INSTRUCTION_PAGE_FAULT 0xC +#define SCAUSE_LOAD_PAGE_FAULT 0xD +#define SCAUSE_STORE_AMO_PAGE_FAULT 0xF + +#endif \ No newline at end of file diff --git a/src/cpu/rv32cpu.c b/src/cpu/rv32cpu.c index a9aac00..ffe87d0 100644 --- a/src/cpu/rv32cpu.c +++ b/src/cpu/rv32cpu.c @@ -531,7 +531,7 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) switch(instruction->func7) { case FUNC7_SFENCEVMA: - fprintf(stderr, "SFENCE.VMA: Guest kernel must think we have an MMU. We have none.\n"); + // TODO : Check if we really need to do something on SFENCE.VMA ? break; case FUNC7_WFI: fprintf(stderr, "WFI: Guest kernel must think we have interrupts. We have none. Halting simulation.\n"); diff --git a/src/memory/memory.c b/src/memory/memory.c index 5c93bf2..a670aa9 100644 --- a/src/memory/memory.c +++ b/src/memory/memory.c @@ -1,5 +1,6 @@ #include "memory.h" #include "vriscv.h" +#include "mmu.h" uint8_t* memory; pthread_mutex_t memory_mutex; @@ -39,6 +40,8 @@ void mem_register_mmio(uint32_t address, uint32_t reg_size, uint32_t reg_count, void mem_write8(uint32_t address, uint8_t value) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) @@ -68,6 +71,8 @@ void mem_write8(uint32_t address, uint8_t value) void mem_write16(uint32_t address, uint16_t value) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) @@ -97,6 +102,8 @@ void mem_write16(uint32_t address, uint16_t value) void mem_write32(uint32_t address, uint32_t value) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) @@ -126,6 +133,8 @@ void mem_write32(uint32_t address, uint32_t value) uint8_t mem_read8(uint32_t address) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) @@ -155,6 +164,8 @@ uint8_t mem_read8(uint32_t address) uint16_t mem_read16(uint32_t address) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) @@ -184,6 +195,8 @@ uint16_t mem_read16(uint32_t address) uint32_t mem_read32(uint32_t address) { + address = mmu_resolve(cpu0, address); + // Look wether we are on an MMIO region struct MMIO_ENTRY* io = mmio; while(io) diff --git a/src/memory/mmu.c b/src/memory/mmu.c new file mode 100644 index 0000000..b120c91 --- /dev/null +++ b/src/memory/mmu.c @@ -0,0 +1,114 @@ +#include "mmu.h" + +#include "cpu/exception.h" + +#include +#include + +extern uint8_t* memory; + +// Memory Managment Unit implementation +// We only support Sv32 + +#define PAGE_SIZE (4 * 1024) // 4KiB, 2^12B +#define LEVELS 2 +#define PTE_SIZE 4 // sizeof(uint32_t) + +// SATP CSR Register: [MODE(1bit) ASID(9bits) PPN(22bits)] +#define SATP_MODE (1 << 31) +#define SATP_MODE_BARE (0) +#define SATP_MODE_SV32 (1 << 31) +#define SATP_ASID (0x1FF << 22) +#define SATP_PPN (0x3FFFFF) + +// Page Table entry: [PPN[1](12bits) PPN[0](10bits) RSW(2bits) D A G U X W R V] +#define PTE_PPN_1(pte) ((pte & 0xFFF00000) >> 20) +#define PTE_PPN_0(pte) ((pte & 0x000FFC00) >> 10) +#define PTE_PPN(pte) ((pte & 0xFFFFFC00) >> 10) +#define PTE_RSW (0b11 << 8) +#define PTE_D (1 << 7) +#define PTE_A (1 << 6) +#define PTE_G (1 << 5) +#define PTE_U (1 << 4) +#define PTE_X (1 << 3) +#define PTE_W (1 << 2) +#define PTE_R (1 << 1) +#define PTE_V (1 << 0) + +// Physical address: 34 bits [PPN[1](12bits, ) PPN[0](10bits) Offset(12bits)] +// We only use 32-bits addresses, so the top 2 are always 0 +#define PADDR_PAGE_OFFSET (0x00000FFF) + +// Virtual address: [VPN[1](10bits) VPN[0](10bits) Offset(12bits)] +#define VADDR_VPN_1 (0xFFC00000) +#define VADDR_VPN_0 (0x003FF000) +#define VADDR_PAGE_OFFSET (0x00000FFF) + +uint32_t mmu_resolve(rv32_cpu_t* cpu, uint32_t vaddr) +{ + // TODO: Make sure we are in S-mode or U-mode + + // Check if MODE field is 'bare', meaning no mmu + if((cpu->csr[CSR_SATP] & SATP_MODE) == SATP_MODE_BARE) + return vaddr; + + // fprintf(stderr, "MMU enabled on (virtual) address 0x%x resolution\n", vaddr); + + uint32_t page_table = (cpu->csr[CSR_SATP] & SATP_PPN) * PAGE_SIZE; + + // Resolve first-level page table entry + uint32_t vpn_1 = (vaddr & VADDR_VPN_1) >> 22; + uint32_t pte_address = page_table + vpn_1 * PTE_SIZE; + uint32_t pte = *((uint32_t*) (&memory[pte_address])); + + if(!(pte & PTE_V)) + { + // Invalid PTE + // TODO : Add a MEMORY_ACCESS_TYPE + exception_trigger(cpu, SCAUSE_INSTRUCTION_PAGE_FAULT); + } + + if((pte & PTE_R) || (pte & PTE_W) || (pte & PTE_X)) + { + // Leaf PTE, we are ready to resolve the mapping + // This is a 4 MiB megapage + + // Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = vaddr.VPN[0], offset] + uint32_t paddr = 0; + paddr |= (PTE_PPN_1(pte) << 22); + paddr |= (vaddr & VADDR_VPN_0); + paddr |= vaddr & VADDR_PAGE_OFFSET; + + return paddr; + } + + // PTE is a pointer to next level of page table + page_table = PTE_PPN(pte) * PAGE_SIZE; + + // Resolve second-level page table entry + uint32_t vpn_0 = (vaddr & VADDR_VPN_0) >> 12; + pte_address = page_table + vpn_0 * PTE_SIZE; + pte = *((uint32_t*) (&memory[pte_address])); + + if(!(pte & PTE_V)) + { + // Invalid PTE + exception_trigger(cpu, SCAUSE_INSTRUCTION_PAGE_FAULT); + } + + // This must be a leaf PTE, as Sv32 only supports 2-level mappings + // This is a 4 KiB page + if(!((pte & PTE_R) || (pte & PTE_W) || (pte & PTE_X))) + { + fprintf(stderr, "Error: Pointer second-level Page Table Entry 0x%x at 0x%x while resolving virtual address 0x%x\n", pte, pte_address, vaddr); + exit(EXIT_FAILURE); + } + + // Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = pte.PPN[0], offset] + uint32_t paddr = 0; + paddr |= (PTE_PPN_1(pte) << 22); + paddr |= (PTE_PPN_0(pte) << 12); + paddr |= vaddr & VADDR_PAGE_OFFSET; + + return paddr; +} diff --git a/src/memory/mmu.h b/src/memory/mmu.h new file mode 100644 index 0000000..c2cdacf --- /dev/null +++ b/src/memory/mmu.h @@ -0,0 +1,9 @@ +#ifndef MMU_H +#define MMU_H + +#include +#include "cpu/rv32cpu.h" + +uint32_t mmu_resolve(rv32_cpu_t* cpu, uint32_t vaddr); + +#endif