target-or32: Add MMU support
Add OpenRISC MMU support. Signed-off-by: Jia Liu <proljc@gmail.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
parent
e67db06e9f
commit
726fe04572
@ -25,6 +25,9 @@
|
||||
|
||||
#define CPUArchState struct CPUOpenRISCState
|
||||
|
||||
/* cpu_openrisc_map_address_* in CPUOpenRISCTLBContext need this decl. */
|
||||
struct OpenRISCCPU;
|
||||
|
||||
#include "config.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu-defs.h"
|
||||
@ -57,6 +60,12 @@ typedef struct OpenRISCCPUClass {
|
||||
|
||||
#define NB_MMU_MODES 3
|
||||
|
||||
enum {
|
||||
MMU_NOMMU_IDX = 0,
|
||||
MMU_SUPERVISOR_IDX = 1,
|
||||
MMU_USER_IDX = 2,
|
||||
};
|
||||
|
||||
#define TARGET_PAGE_BITS 13
|
||||
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
@ -208,6 +217,56 @@ enum {
|
||||
OPENRISC_FEATURE_OV64S = (1 << 9),
|
||||
};
|
||||
|
||||
/* TLB size */
|
||||
enum {
|
||||
DTLB_WAYS = 1,
|
||||
DTLB_SIZE = 64,
|
||||
DTLB_MASK = (DTLB_SIZE-1),
|
||||
ITLB_WAYS = 1,
|
||||
ITLB_SIZE = 64,
|
||||
ITLB_MASK = (ITLB_SIZE-1),
|
||||
};
|
||||
|
||||
/* TLB prot */
|
||||
enum {
|
||||
URE = (1 << 6),
|
||||
UWE = (1 << 7),
|
||||
SRE = (1 << 8),
|
||||
SWE = (1 << 9),
|
||||
|
||||
SXE = (1 << 6),
|
||||
UXE = (1 << 7),
|
||||
};
|
||||
|
||||
/* check if tlb available */
|
||||
enum {
|
||||
TLBRET_INVALID = -3,
|
||||
TLBRET_NOMATCH = -2,
|
||||
TLBRET_BADADDR = -1,
|
||||
TLBRET_MATCH = 0
|
||||
};
|
||||
|
||||
typedef struct OpenRISCTLBEntry {
|
||||
uint32_t mr;
|
||||
uint32_t tr;
|
||||
} OpenRISCTLBEntry;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
typedef struct CPUOpenRISCTLBContext {
|
||||
OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE];
|
||||
OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE];
|
||||
|
||||
int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot,
|
||||
target_ulong address, int rw);
|
||||
int (*cpu_openrisc_map_address_data)(struct OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot,
|
||||
target_ulong address, int rw);
|
||||
} CPUOpenRISCTLBContext;
|
||||
#endif
|
||||
|
||||
typedef struct CPUOpenRISCState {
|
||||
target_ulong gpr[32]; /* General registers */
|
||||
target_ulong pc; /* Program counter */
|
||||
@ -241,6 +300,8 @@ typedef struct CPUOpenRISCState {
|
||||
CPU_COMMON
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
CPUOpenRISCTLBContext * tlb;
|
||||
|
||||
struct QEMUTimer *timer;
|
||||
uint32_t ttmr; /* Timer tick mode register */
|
||||
uint32_t ttcr; /* Timer tick count register */
|
||||
@ -280,13 +341,26 @@ void cpu_openrisc_list(FILE *f, fprintf_function cpu_fprintf);
|
||||
int cpu_openrisc_exec(CPUOpenRISCState *s);
|
||||
void do_interrupt(CPUOpenRISCState *env);
|
||||
void openrisc_translate_init(void);
|
||||
int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env,
|
||||
target_ulong address,
|
||||
int rw, int mmu_idx);
|
||||
|
||||
#define cpu_list cpu_openrisc_list
|
||||
#define cpu_exec cpu_openrisc_exec
|
||||
#define cpu_gen_code cpu_openrisc_gen_code
|
||||
#define cpu_handle_mmu_fault cpu_openrisc_handle_mmu_fault
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void cpu_openrisc_mmu_init(OpenRISCCPU *cpu);
|
||||
int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw);
|
||||
int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw);
|
||||
int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw);
|
||||
#endif
|
||||
|
||||
static inline CPUOpenRISCState *cpu_init(const char *cpu_model)
|
||||
@ -312,7 +386,10 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env,
|
||||
|
||||
static inline int cpu_mmu_index(CPUOpenRISCState *env)
|
||||
{
|
||||
return 0;
|
||||
if (!(env->sr & SR_IME)) {
|
||||
return MMU_NOMMU_IDX;
|
||||
}
|
||||
return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX;
|
||||
}
|
||||
|
||||
static inline bool cpu_has_work(CPUOpenRISCState *env)
|
||||
|
@ -26,14 +26,218 @@
|
||||
#include "hw/loader.h"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw)
|
||||
{
|
||||
*physical = address;
|
||||
*prot = PAGE_READ | PAGE_WRITE;
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
|
||||
int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw)
|
||||
{
|
||||
int vpn = address >> TARGET_PAGE_BITS;
|
||||
int idx = vpn & ITLB_MASK;
|
||||
int right = 0;
|
||||
|
||||
if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
|
||||
return TLBRET_NOMATCH;
|
||||
}
|
||||
if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
|
||||
return TLBRET_INVALID;
|
||||
}
|
||||
|
||||
if (cpu->env.sr & SR_SM) { /* supervisor mode */
|
||||
if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
|
||||
right |= PAGE_EXEC;
|
||||
}
|
||||
} else {
|
||||
if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
|
||||
right |= PAGE_EXEC;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
|
||||
return TLBRET_BADADDR;
|
||||
}
|
||||
|
||||
*physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
|
||||
(address & (TARGET_PAGE_SIZE-1));
|
||||
*prot = right;
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
|
||||
int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address, int rw)
|
||||
{
|
||||
int vpn = address >> TARGET_PAGE_BITS;
|
||||
int idx = vpn & DTLB_MASK;
|
||||
int right = 0;
|
||||
|
||||
if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
|
||||
return TLBRET_NOMATCH;
|
||||
}
|
||||
if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
|
||||
return TLBRET_INVALID;
|
||||
}
|
||||
|
||||
if (cpu->env.sr & SR_SM) { /* supervisor mode */
|
||||
if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
|
||||
right |= PAGE_READ;
|
||||
}
|
||||
if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
|
||||
right |= PAGE_WRITE;
|
||||
}
|
||||
} else {
|
||||
if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
|
||||
right |= PAGE_READ;
|
||||
}
|
||||
if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
|
||||
right |= PAGE_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rw & 0) && ((right & PAGE_READ) == 0)) {
|
||||
return TLBRET_BADADDR;
|
||||
}
|
||||
if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
|
||||
return TLBRET_BADADDR;
|
||||
}
|
||||
|
||||
*physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
|
||||
(address & (TARGET_PAGE_SIZE-1));
|
||||
*prot = right;
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
|
||||
static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
|
||||
target_phys_addr_t *physical,
|
||||
int *prot, target_ulong address,
|
||||
int rw)
|
||||
{
|
||||
int ret = TLBRET_MATCH;
|
||||
|
||||
/* [0x0000--0x2000]: unmapped */
|
||||
if (address < 0x2000 && (cpu->env.sr & SR_SM)) {
|
||||
*physical = address;
|
||||
*prot = PAGE_READ | PAGE_WRITE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rw == 2) { /* ITLB */
|
||||
*physical = 0;
|
||||
ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
|
||||
prot, address, rw);
|
||||
} else { /* DTLB */
|
||||
ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
|
||||
prot, address, rw);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
|
||||
target_ulong address,
|
||||
int rw, int tlb_error)
|
||||
{
|
||||
int exception = 0;
|
||||
|
||||
switch (tlb_error) {
|
||||
default:
|
||||
if (rw == 2) {
|
||||
exception = EXCP_IPF;
|
||||
} else {
|
||||
exception = EXCP_DPF;
|
||||
}
|
||||
break;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
case TLBRET_BADADDR:
|
||||
if (rw == 2) {
|
||||
exception = EXCP_IPF;
|
||||
} else {
|
||||
exception = EXCP_DPF;
|
||||
}
|
||||
break;
|
||||
case TLBRET_INVALID:
|
||||
case TLBRET_NOMATCH:
|
||||
/* No TLB match for a mapped address */
|
||||
if (rw == 2) {
|
||||
exception = EXCP_ITLBMISS;
|
||||
} else {
|
||||
exception = EXCP_DTLBMISS;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
cpu->env.exception_index = exception;
|
||||
cpu->env.eear = address;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env,
|
||||
target_ulong address, int rw, int mmu_idx)
|
||||
{
|
||||
int ret = 0;
|
||||
target_phys_addr_t physical = 0;
|
||||
int prot = 0;
|
||||
OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env));
|
||||
|
||||
ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
|
||||
address, rw);
|
||||
|
||||
if (ret == TLBRET_MATCH) {
|
||||
tlb_set_page(env, address & TARGET_PAGE_MASK,
|
||||
physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
|
||||
mmu_idx, TARGET_PAGE_SIZE);
|
||||
ret = 0;
|
||||
} else if (ret < 0) {
|
||||
cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env,
|
||||
target_ulong address, int rw, int mmu_idx)
|
||||
{
|
||||
int ret = 0;
|
||||
OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env));
|
||||
|
||||
cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
target_phys_addr_t cpu_get_phys_page_debug(CPUOpenRISCState *env,
|
||||
target_ulong addr)
|
||||
{
|
||||
return addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
int prot;
|
||||
OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env));
|
||||
|
||||
if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
|
||||
{
|
||||
cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
|
||||
|
||||
cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
|
||||
cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
|
||||
}
|
||||
#endif
|
||||
|
@ -39,5 +39,25 @@
|
||||
void tlb_fill(CPUOpenRISCState *env, target_ulong addr, int is_write,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
unsigned long pc;
|
||||
int ret;
|
||||
|
||||
ret = cpu_openrisc_handle_mmu_fault(env, addr, is_write, mmu_idx);
|
||||
|
||||
if (ret) {
|
||||
if (retaddr) {
|
||||
/* now we have a real cpu fault. */
|
||||
pc = (unsigned long)retaddr;
|
||||
tb = tb_find_pc(pc);
|
||||
if (tb) {
|
||||
/* the PC is inside the translated code. It means that we
|
||||
have a virtual CPU fault. */
|
||||
cpu_restore_state(tb, env, pc);
|
||||
}
|
||||
}
|
||||
/* Raise Exception. */
|
||||
cpu_loop_exit(env);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user