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:
Jia Liu 2012-07-20 15:50:40 +08:00 committed by Blue Swirl
parent e67db06e9f
commit 726fe04572
3 changed files with 303 additions and 2 deletions

View File

@ -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)

View File

@ -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

View File

@ -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