From 2065061ede22d401aae2ce995c3af54db9d28639 Mon Sep 17 00:00:00 2001 From: "Igor V. Kovalenko" Date: Mon, 3 May 2010 11:29:44 +0400 Subject: sparc64: handle asi referencing nucleus and secondary MMU contexts - increase max supported MMU modes to 6 - handle nucleus context asi - handle secondary context asi - handle non-faulting loads from secondary context Signed-off-by: Igor V. Kovalenko Signed-off-by: Blue Swirl --- softmmu_exec.h | 25 ++++- target-sparc/cpu.h | 13 ++- target-sparc/exec.h | 4 + target-sparc/helper.c | 42 ++++++--- target-sparc/op_helper.c | 235 ++++++++++++++++++++++++++++++++++------------- 5 files changed, 241 insertions(+), 78 deletions(-) diff --git a/softmmu_exec.h b/softmmu_exec.h index a43e621..28d1d53 100644 --- a/softmmu_exec.h +++ b/softmmu_exec.h @@ -100,9 +100,28 @@ #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 5) */ -#if (NB_MMU_MODES > 5) -#error "NB_MMU_MODES > 5 is not supported for now" -#endif /* (NB_MMU_MODES > 5) */ +#if (NB_MMU_MODES >= 6) + +#define ACCESS_TYPE 5 +#define MEMSUFFIX MMU_MODE5_SUFFIX +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 6) */ + +#if (NB_MMU_MODES > 6) +#error "NB_MMU_MODES > 6 is not supported for now" +#endif /* (NB_MMU_MODES > 6) */ /* these access are slower, they must be as rare as possible */ #define ACCESS_TYPE (NB_MMU_MODES) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index b705728..b679333 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -224,7 +224,7 @@ enum { #if !defined(TARGET_SPARC64) #define NB_MMU_MODES 2 #else -#define NB_MMU_MODES 3 +#define NB_MMU_MODES 6 typedef struct trap_state { uint64_t tpc; uint64_t tnpc; @@ -571,6 +571,9 @@ static inline void PUT_CWP64(CPUSPARCState *env1, int cwp) #if !defined(CONFIG_USER_ONLY) void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size); +target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, + int mmu_idx); + #endif int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); @@ -587,10 +590,18 @@ int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #define MMU_MODE1_SUFFIX _kernel #ifdef TARGET_SPARC64 #define MMU_MODE2_SUFFIX _hypv +#define MMU_MODE3_SUFFIX _nucleus +#define MMU_MODE4_SUFFIX _user_secondary +#define MMU_MODE5_SUFFIX _kernel_secondary #endif #define MMU_USER_IDX 0 #define MMU_KERNEL_IDX 1 #define MMU_HYPV_IDX 2 +#ifdef TARGET_SPARC64 +#define MMU_NUCLEUS_IDX 3 +#define MMU_USER_SECONDARY_IDX 4 +#define MMU_KERNEL_SECONDARY_IDX 5 +#endif static inline int cpu_mmu_index(CPUState *env1) { diff --git a/target-sparc/exec.h b/target-sparc/exec.h index 70df828..1e9de82 100644 --- a/target-sparc/exec.h +++ b/target-sparc/exec.h @@ -13,6 +13,10 @@ register struct CPUSPARCState *env asm(AREG0); #include "cpu.h" #include "exec-all.h" +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ + /* op_helper.c */ void do_interrupt(CPUState *env); diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 4ece01b..cac6cad 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -420,21 +420,32 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int rw, int is_user) + target_ulong address, int rw, int mmu_idx) { unsigned int i; uint64_t context; + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); + if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ *physical = ultrasparc_truncate_physical(address); *prot = PAGE_READ | PAGE_WRITE; return 0; } - if (env->tl == 0) { + switch(mmu_idx) { + case MMU_USER_IDX: + case MMU_KERNEL_IDX: context = env->dmmu.mmu_primary_context & 0x1fff; - } else { + break; + case MMU_USER_SECONDARY_IDX: + case MMU_KERNEL_SECONDARY_IDX: + context = env->dmmu.mmu_secondary_context & 0x1fff; + break; + case MMU_NUCLEUS_IDX: context = 0; + break; } for (i = 0; i < 64; i++) { @@ -482,11 +493,14 @@ static int get_physical_address_data(CPUState *env, static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int is_user) + target_ulong address, int mmu_idx) { unsigned int i; uint64_t context; + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); + if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) { /* IMMU disabled */ *physical = ultrasparc_truncate_physical(address); @@ -495,8 +509,10 @@ static int get_physical_address_code(CPUState *env, } if (env->tl == 0) { + /* PRIMARY context */ context = env->dmmu.mmu_primary_context & 0x1fff; } else { + /* NUCLEUS context */ context = 0; } @@ -535,17 +551,15 @@ static int get_physical_address(CPUState *env, target_phys_addr_t *physical, target_ulong address, int rw, int mmu_idx, target_ulong *page_size) { - int is_user = mmu_idx == MMU_USER_IDX; - /* ??? We treat everything as a small page, then explicitly flush everything when an entry is evicted. */ *page_size = TARGET_PAGE_SIZE; if (rw == 2) return get_physical_address_code(env, physical, prot, address, - is_user); + mmu_idx); else return get_physical_address_data(env, physical, prot, address, rw, - is_user); + mmu_idx); } /* Perform address translation */ @@ -659,21 +673,27 @@ void dump_mmu(CPUState *env) #if !defined(CONFIG_USER_ONLY) -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, + int mmu_idx) { target_phys_addr_t phys_addr; target_ulong page_size; int prot, access_index; if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, - MMU_KERNEL_IDX, &page_size) != 0) + mmu_idx, &page_size) != 0) if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, - 0, MMU_KERNEL_IDX, &page_size) != 0) + 0, mmu_idx, &page_size) != 0) return -1; if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) return -1; return phys_addr; } + +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return cpu_get_phys_page_nofault(env, addr, MMU_KERNEL_IDX); +} #endif void cpu_reset(CPUSPARCState *env) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 245eba7..b6b08d3 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -1,9 +1,6 @@ #include "exec.h" #include "host-utils.h" #include "helper.h" -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ //#define DEBUG_MMU //#define DEBUG_MXCC @@ -2142,17 +2139,29 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) switch (asi) { case 0x82: // Primary no-fault case 0x8a: // Primary no-fault LE - if (cpu_get_phys_page_debug(env, addr) == -1ULL) { + case 0x83: // Secondary no-fault + case 0x8b: // Secondary no-fault LE + { + /* secondary space access has lowest asi bit equal to 1 */ + int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX + : MMU_KERNEL_SECONDARY_IDX; + + if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) { #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read ", last_addr, asi, size, ret); #endif - return 0; + return 0; + } } // Fall through case 0x10: // As if user primary + case 0x11: // As if user secondary case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE case 0x80: // Primary + case 0x81: // Secondary case 0x88: // Primary LE + case 0x89: // Secondary LE case 0xe2: // UA2007 Primary block init case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { @@ -2174,37 +2183,75 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) break; } } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { + switch(size) { + case 1: + ret = ldub_kernel_secondary(addr); + break; + case 2: + ret = lduw_kernel_secondary(addr); + break; + case 4: + ret = ldl_kernel_secondary(addr); + break; + default: + case 8: + ret = ldq_kernel_secondary(addr); + break; + } + } else { + switch(size) { + case 1: + ret = ldub_kernel(addr); + break; + case 2: + ret = lduw_kernel(addr); + break; + case 4: + ret = ldl_kernel(addr); + break; + default: + case 8: + ret = ldq_kernel(addr); + break; + } + } + } + } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { switch(size) { case 1: - ret = ldub_kernel(addr); + ret = ldub_user_secondary(addr); break; case 2: - ret = lduw_kernel(addr); + ret = lduw_user_secondary(addr); break; case 4: - ret = ldl_kernel(addr); + ret = ldl_user_secondary(addr); break; default: case 8: - ret = ldq_kernel(addr); + ret = ldq_user_secondary(addr); + break; + } + } else { + switch(size) { + case 1: + ret = ldub_user(addr); + break; + case 2: + ret = lduw_user(addr); + break; + case 4: + ret = ldl_user(addr); + break; + default: + case 8: + ret = ldq_user(addr); break; } - } - } else { - switch(size) { - case 1: - ret = ldub_user(addr); - break; - case 2: - ret = lduw_user(addr); - break; - case 4: - ret = ldl_user(addr); - break; - default: - case 8: - ret = ldq_user(addr); - break; } } break; @@ -2235,22 +2282,27 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) // Only ldda allowed raise_exception(TT_ILL_INSN); return 0; - case 0x83: // Secondary no-fault - case 0x8b: // Secondary no-fault LE - if (cpu_get_phys_page_debug(env, addr) == -1ULL) { -#ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); -#endif - return 0; - } - // Fall through case 0x04: // Nucleus case 0x0c: // Nucleus Little Endian (LE) - case 0x11: // As if user secondary - case 0x19: // As if user secondary LE + { + switch(size) { + case 1: + ret = ldub_nucleus(addr); + break; + case 2: + ret = lduw_nucleus(addr); + break; + case 4: + ret = ldl_nucleus(addr); + break; + default: + case 8: + ret = ldq_nucleus(addr); + break; + } + break; + } case 0x4a: // UPA config - case 0x81: // Secondary - case 0x89: // Secondary LE // XXX break; case 0x45: // LSU @@ -2464,9 +2516,13 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size) switch(asi) { case 0x10: // As if user primary + case 0x11: // As if user secondary case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE case 0x80: // Primary + case 0x81: // Secondary case 0x88: // Primary LE + case 0x89: // Secondary LE case 0xe2: // UA2007 Primary block init case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { @@ -2488,37 +2544,75 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size) break; } } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { + switch(size) { + case 1: + stb_kernel_secondary(addr, val); + break; + case 2: + stw_kernel_secondary(addr, val); + break; + case 4: + stl_kernel_secondary(addr, val); + break; + case 8: + default: + stq_kernel_secondary(addr, val); + break; + } + } else { + switch(size) { + case 1: + stb_kernel(addr, val); + break; + case 2: + stw_kernel(addr, val); + break; + case 4: + stl_kernel(addr, val); + break; + case 8: + default: + stq_kernel(addr, val); + break; + } + } + } + } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { switch(size) { case 1: - stb_kernel(addr, val); + stb_user_secondary(addr, val); break; case 2: - stw_kernel(addr, val); + stw_user_secondary(addr, val); break; case 4: - stl_kernel(addr, val); + stl_user_secondary(addr, val); break; case 8: default: - stq_kernel(addr, val); + stq_user_secondary(addr, val); + break; + } + } else { + switch(size) { + case 1: + stb_user(addr, val); + break; + case 2: + stw_user(addr, val); + break; + case 4: + stl_user(addr, val); + break; + case 8: + default: + stq_user(addr, val); break; } - } - } else { - switch(size) { - case 1: - stb_user(addr, val); - break; - case 2: - stw_user(addr, val); - break; - case 4: - stl_user(addr, val); - break; - case 8: - default: - stq_user(addr, val); - break; } } break; @@ -2551,11 +2645,26 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size) return; case 0x04: // Nucleus case 0x0c: // Nucleus Little Endian (LE) - case 0x11: // As if user secondary - case 0x19: // As if user secondary LE + { + switch(size) { + case 1: + stb_nucleus(addr, val); + break; + case 2: + stw_nucleus(addr, val); + break; + case 4: + stl_nucleus(addr, val); + break; + default: + case 8: + stq_nucleus(addr, val); + break; + } + break; + } + case 0x4a: // UPA config - case 0x81: // Secondary - case 0x89: // Secondary LE // XXX return; case 0x45: // LSU -- cgit v1.1