Compare commits
4 Commits
5727356559
...
5bb973e8da
Author | SHA1 | Date |
---|---|---|
vhaudiquet | 5bb973e8da | 1 year ago |
vhaudiquet | 9da9b5045f | 1 year ago |
vhaudiquet | 3f6657fe00 | 1 year ago |
vhaudiquet | ce89df1ed4 | 1 year ago |
@ -0,0 +1,25 @@ |
|||||||
|
#include "exception.h" |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
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); |
||||||
|
} |
@ -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 |
@ -0,0 +1,114 @@ |
|||||||
|
#include "mmu.h" |
||||||
|
|
||||||
|
#include "cpu/exception.h" |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
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; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
#ifndef MMU_H |
||||||
|
#define MMU_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include "cpu/rv32cpu.h" |
||||||
|
|
||||||
|
uint32_t mmu_resolve(rv32_cpu_t* cpu, uint32_t vaddr); |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue