mirror of https://github.com/xqemu/xqemu.git
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
|
#define CPUArchState struct CPUOpenRISCState
|
||||||
|
|
||||||
|
/* cpu_openrisc_map_address_* in CPUOpenRISCTLBContext need this decl. */
|
||||||
|
struct OpenRISCCPU;
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "cpu-defs.h"
|
#include "cpu-defs.h"
|
||||||
|
@ -57,6 +60,12 @@ typedef struct OpenRISCCPUClass {
|
||||||
|
|
||||||
#define NB_MMU_MODES 3
|
#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_PAGE_BITS 13
|
||||||
|
|
||||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||||
|
@ -208,6 +217,56 @@ enum {
|
||||||
OPENRISC_FEATURE_OV64S = (1 << 9),
|
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 {
|
typedef struct CPUOpenRISCState {
|
||||||
target_ulong gpr[32]; /* General registers */
|
target_ulong gpr[32]; /* General registers */
|
||||||
target_ulong pc; /* Program counter */
|
target_ulong pc; /* Program counter */
|
||||||
|
@ -241,6 +300,8 @@ typedef struct CPUOpenRISCState {
|
||||||
CPU_COMMON
|
CPU_COMMON
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
CPUOpenRISCTLBContext * tlb;
|
||||||
|
|
||||||
struct QEMUTimer *timer;
|
struct QEMUTimer *timer;
|
||||||
uint32_t ttmr; /* Timer tick mode register */
|
uint32_t ttmr; /* Timer tick mode register */
|
||||||
uint32_t ttcr; /* Timer tick count 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);
|
int cpu_openrisc_exec(CPUOpenRISCState *s);
|
||||||
void do_interrupt(CPUOpenRISCState *env);
|
void do_interrupt(CPUOpenRISCState *env);
|
||||||
void openrisc_translate_init(void);
|
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_list cpu_openrisc_list
|
||||||
#define cpu_exec cpu_openrisc_exec
|
#define cpu_exec cpu_openrisc_exec
|
||||||
#define cpu_gen_code cpu_openrisc_gen_code
|
#define cpu_gen_code cpu_openrisc_gen_code
|
||||||
|
#define cpu_handle_mmu_fault cpu_openrisc_handle_mmu_fault
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
void cpu_openrisc_mmu_init(OpenRISCCPU *cpu);
|
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
|
#endif
|
||||||
|
|
||||||
static inline CPUOpenRISCState *cpu_init(const char *cpu_model)
|
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)
|
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)
|
static inline bool cpu_has_work(CPUOpenRISCState *env)
|
||||||
|
|
|
@ -26,14 +26,218 @@
|
||||||
#include "hw/loader.h"
|
#include "hw/loader.h"
|
||||||
#endif
|
#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
|
#ifndef CONFIG_USER_ONLY
|
||||||
target_phys_addr_t cpu_get_phys_page_debug(CPUOpenRISCState *env,
|
target_phys_addr_t cpu_get_phys_page_debug(CPUOpenRISCState *env,
|
||||||
target_ulong addr)
|
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)
|
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
|
#endif
|
||||||
|
|
|
@ -39,5 +39,25 @@
|
||||||
void tlb_fill(CPUOpenRISCState *env, target_ulong addr, int is_write,
|
void tlb_fill(CPUOpenRISCState *env, target_ulong addr, int is_write,
|
||||||
int mmu_idx, uintptr_t retaddr)
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue