Compare commits
No commits in common. "5bb973e8daa1c2c783e9b2864897c640a8d7bcd0" and "5727356559c5efcc5bae1bfd3eec22b81e8eacfc" have entirely different histories.
5bb973e8da
...
5727356559
@ -1,25 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#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
|
|
@ -501,6 +501,7 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
case OPCODE_NOP:
|
case OPCODE_NOP:
|
||||||
{
|
{
|
||||||
// TODO : Implement PAUSE, FENCE, FENCE.TSO
|
// TODO : Implement PAUSE, FENCE, FENCE.TSO
|
||||||
|
fprintf(stderr, "Warning: Unsupported NOP instruction\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPCODE_SYSTEM:
|
case OPCODE_SYSTEM:
|
||||||
@ -531,7 +532,7 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
switch(instruction->func7)
|
switch(instruction->func7)
|
||||||
{
|
{
|
||||||
case FUNC7_SFENCEVMA:
|
case FUNC7_SFENCEVMA:
|
||||||
// TODO : Check if we really need to do something on SFENCE.VMA ?
|
fprintf(stderr, "SFENCE.VMA: Guest kernel must think we have an MMU. We have none.\n");
|
||||||
break;
|
break;
|
||||||
case FUNC7_WFI:
|
case FUNC7_WFI:
|
||||||
fprintf(stderr, "WFI: Guest kernel must think we have interrupts. We have none. Halting simulation.\n");
|
fprintf(stderr, "WFI: Guest kernel must think we have interrupts. We have none. Halting simulation.\n");
|
||||||
@ -601,12 +602,14 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
// Load-Reserved Word
|
// Load-Reserved Word
|
||||||
cpu->regs.x[instruction->rd] = mem_read32(address);
|
cpu->regs.x[instruction->rd] = mem_read32(address);
|
||||||
// TODO register reservation set that subsumes the bytes in word
|
// TODO register reservation set that subsumes the bytes in word
|
||||||
|
fprintf(stderr, "LR.W\n");
|
||||||
break;
|
break;
|
||||||
case FUNC75_SCW:
|
case FUNC75_SCW:
|
||||||
// Store-Conditional Word
|
// Store-Conditional Word
|
||||||
// TODO succeed only if the reservation is still valid and the reservation set contains the bytes written
|
// TODO succeed only if the reservation is still valid and the reservation set contains the bytes written
|
||||||
mem_write32(address, cpu->regs.x[instruction->rs2]);
|
mem_write32(address, cpu->regs.x[instruction->rs2]);
|
||||||
cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure
|
cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure
|
||||||
|
fprintf(stderr, "SC.W\n");
|
||||||
break;
|
break;
|
||||||
case FUNC75_AMOSWAPW:
|
case FUNC75_AMOSWAPW:
|
||||||
// Atomic Memory Operation SWAP Word
|
// Atomic Memory Operation SWAP Word
|
||||||
@ -678,7 +681,7 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn)) void cpu_loop(rv32_cpu_t* cpu)
|
void cpu_loop(rv32_cpu_t* cpu)
|
||||||
{
|
{
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
|
@ -201,6 +201,6 @@ typedef struct RV32_CPU
|
|||||||
extern rv32_cpu_t* cpu0;
|
extern rv32_cpu_t* cpu0;
|
||||||
extern pthread_mutex_t cpu0_mutex;
|
extern pthread_mutex_t cpu0_mutex;
|
||||||
void cpu_init();
|
void cpu_init();
|
||||||
__attribute__((noreturn)) void cpu_loop(rv32_cpu_t* cpu);
|
void cpu_loop(rv32_cpu_t* cpu);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,11 +10,6 @@ void sbi_call(rv32_cpu_t* cpu, uint32_t extension_id, uint32_t func_id)
|
|||||||
{
|
{
|
||||||
switch(func_id)
|
switch(func_id)
|
||||||
{
|
{
|
||||||
case SBI_FUNCTION_GET_SPEC_VERSION:
|
|
||||||
// Format: [31(reserved,0) 7 bits(major) 24 bits(minor)]
|
|
||||||
// Version 2.0
|
|
||||||
cpu->regs.a0 = 0x2000000;
|
|
||||||
break;
|
|
||||||
case SBI_FUNCTION_GET_MVENDOR_ID:
|
case SBI_FUNCTION_GET_MVENDOR_ID:
|
||||||
cpu->regs.a0 = 0;
|
cpu->regs.a0 = 0;
|
||||||
break;
|
break;
|
||||||
@ -30,12 +25,6 @@ void sbi_call(rv32_cpu_t* cpu, uint32_t extension_id, uint32_t func_id)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SBI_EXTENSION_SET_TIMER:
|
|
||||||
{
|
|
||||||
// TODO : Correctly implement that
|
|
||||||
cpu->regs.a0 = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SBI_EXTENSION_LEGACY_CONSOLE_PUTCHAR:
|
case SBI_EXTENSION_LEGACY_CONSOLE_PUTCHAR:
|
||||||
{
|
{
|
||||||
printf("%c", cpu->regs.a0);
|
printf("%c", cpu->regs.a0);
|
||||||
|
@ -3,17 +3,6 @@
|
|||||||
|
|
||||||
#include "cpu/rv32cpu.h"
|
#include "cpu/rv32cpu.h"
|
||||||
|
|
||||||
/* SBI Base extension */
|
|
||||||
#define SBI_EXTENSION_BASE 0x10
|
|
||||||
#define SBI_FUNCTION_GET_SPEC_VERSION 0x0
|
|
||||||
#define SBI_FUNCTION_GET_IMPL_ID 0x1
|
|
||||||
#define SBI_FUNCTION_GET_IMPL_VERSION 0x2
|
|
||||||
#define SBI_FUNCTION_PROBE_EXTENSION 0x3
|
|
||||||
#define SBI_FUNCTION_GET_MVENDOR_ID 0x4
|
|
||||||
#define SBI_FUNCTION_GET_MARCH_ID 0x5
|
|
||||||
#define SBI_FUNCTION_GET_MIMPL_ID 0x6
|
|
||||||
|
|
||||||
#define SBI_EXTENSION_SET_TIMER 0x0
|
|
||||||
#define SBI_EXTENSION_LEGACY_CONSOLE_PUTCHAR 0x1
|
#define SBI_EXTENSION_LEGACY_CONSOLE_PUTCHAR 0x1
|
||||||
#define SBI_EXTENSION_LEGACY_SHUTDOWN 0x8
|
#define SBI_EXTENSION_LEGACY_SHUTDOWN 0x8
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "vriscv.h"
|
#include "vriscv.h"
|
||||||
#include "mmu.h"
|
|
||||||
|
|
||||||
uint8_t* memory;
|
uint8_t* memory;
|
||||||
pthread_mutex_t memory_mutex;
|
pthread_mutex_t memory_mutex;
|
||||||
@ -40,8 +39,6 @@ void mem_register_mmio(uint32_t address, uint32_t reg_size, uint32_t reg_count,
|
|||||||
|
|
||||||
void mem_write8(uint32_t address, uint8_t value)
|
void mem_write8(uint32_t address, uint8_t value)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
@ -71,8 +68,6 @@ void mem_write8(uint32_t address, uint8_t value)
|
|||||||
|
|
||||||
void mem_write16(uint32_t address, uint16_t value)
|
void mem_write16(uint32_t address, uint16_t value)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
@ -102,8 +97,6 @@ void mem_write16(uint32_t address, uint16_t value)
|
|||||||
|
|
||||||
void mem_write32(uint32_t address, uint32_t value)
|
void mem_write32(uint32_t address, uint32_t value)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
@ -133,8 +126,6 @@ void mem_write32(uint32_t address, uint32_t value)
|
|||||||
|
|
||||||
uint8_t mem_read8(uint32_t address)
|
uint8_t mem_read8(uint32_t address)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
@ -164,8 +155,6 @@ uint8_t mem_read8(uint32_t address)
|
|||||||
|
|
||||||
uint16_t mem_read16(uint32_t address)
|
uint16_t mem_read16(uint32_t address)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
@ -195,8 +184,6 @@ uint16_t mem_read16(uint32_t address)
|
|||||||
|
|
||||||
uint32_t mem_read32(uint32_t address)
|
uint32_t mem_read32(uint32_t address)
|
||||||
{
|
{
|
||||||
address = mmu_resolve(cpu0, address);
|
|
||||||
|
|
||||||
// Look wether we are on an MMIO region
|
// Look wether we are on an MMIO region
|
||||||
struct MMIO_ENTRY* io = mmio;
|
struct MMIO_ENTRY* io = mmio;
|
||||||
while(io)
|
while(io)
|
||||||
|
114
src/memory/mmu.c
114
src/memory/mmu.c
@ -1,114 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#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
Block a user