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 <igor.v.kovalenko@gmail.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
Igor V. Kovalenko 2010-05-03 11:29:44 +04:00 committed by Blue Swirl
parent 299b520cd4
commit 2065061ede
5 changed files with 257 additions and 94 deletions

View File

@ -100,9 +100,28 @@
#undef MEMSUFFIX #undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 5) */ #endif /* (NB_MMU_MODES >= 5) */
#if (NB_MMU_MODES > 5) #if (NB_MMU_MODES >= 6)
#error "NB_MMU_MODES > 5 is not supported for now"
#endif /* (NB_MMU_MODES > 5) */ #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 */ /* these access are slower, they must be as rare as possible */
#define ACCESS_TYPE (NB_MMU_MODES) #define ACCESS_TYPE (NB_MMU_MODES)

View File

@ -224,7 +224,7 @@ enum {
#if !defined(TARGET_SPARC64) #if !defined(TARGET_SPARC64)
#define NB_MMU_MODES 2 #define NB_MMU_MODES 2
#else #else
#define NB_MMU_MODES 3 #define NB_MMU_MODES 6
typedef struct trap_state { typedef struct trap_state {
uint64_t tpc; uint64_t tpc;
uint64_t tnpc; uint64_t tnpc;
@ -571,6 +571,9 @@ static inline void PUT_CWP64(CPUSPARCState *env1, int cwp)
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
int is_asi, int size); int is_asi, int size);
target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
int mmu_idx);
#endif #endif
int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); 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 #define MMU_MODE1_SUFFIX _kernel
#ifdef TARGET_SPARC64 #ifdef TARGET_SPARC64
#define MMU_MODE2_SUFFIX _hypv #define MMU_MODE2_SUFFIX _hypv
#define MMU_MODE3_SUFFIX _nucleus
#define MMU_MODE4_SUFFIX _user_secondary
#define MMU_MODE5_SUFFIX _kernel_secondary
#endif #endif
#define MMU_USER_IDX 0 #define MMU_USER_IDX 0
#define MMU_KERNEL_IDX 1 #define MMU_KERNEL_IDX 1
#define MMU_HYPV_IDX 2 #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) static inline int cpu_mmu_index(CPUState *env1)
{ {

View File

@ -13,6 +13,10 @@ register struct CPUSPARCState *env asm(AREG0);
#include "cpu.h" #include "cpu.h"
#include "exec-all.h" #include "exec-all.h"
#if !defined(CONFIG_USER_ONLY)
#include "softmmu_exec.h"
#endif /* !defined(CONFIG_USER_ONLY) */
/* op_helper.c */ /* op_helper.c */
void do_interrupt(CPUState *env); void do_interrupt(CPUState *env);

View File

@ -420,21 +420,32 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
static int get_physical_address_data(CPUState *env, static int get_physical_address_data(CPUState *env,
target_phys_addr_t *physical, int *prot, 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; unsigned int i;
uint64_t context; 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 */ if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
*physical = ultrasparc_truncate_physical(address); *physical = ultrasparc_truncate_physical(address);
*prot = PAGE_READ | PAGE_WRITE; *prot = PAGE_READ | PAGE_WRITE;
return 0; return 0;
} }
if (env->tl == 0) { switch(mmu_idx) {
case MMU_USER_IDX:
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff; 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; context = 0;
break;
} }
for (i = 0; i < 64; i++) { 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, static int get_physical_address_code(CPUState *env,
target_phys_addr_t *physical, int *prot, target_phys_addr_t *physical, int *prot,
target_ulong address, int is_user) target_ulong address, int mmu_idx)
{ {
unsigned int i; unsigned int i;
uint64_t context; 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) { if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) {
/* IMMU disabled */ /* IMMU disabled */
*physical = ultrasparc_truncate_physical(address); *physical = ultrasparc_truncate_physical(address);
@ -495,8 +509,10 @@ static int get_physical_address_code(CPUState *env,
} }
if (env->tl == 0) { if (env->tl == 0) {
/* PRIMARY context */
context = env->dmmu.mmu_primary_context & 0x1fff; context = env->dmmu.mmu_primary_context & 0x1fff;
} else { } else {
/* NUCLEUS context */
context = 0; 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 address, int rw, int mmu_idx,
target_ulong *page_size) target_ulong *page_size)
{ {
int is_user = mmu_idx == MMU_USER_IDX;
/* ??? We treat everything as a small page, then explicitly flush /* ??? We treat everything as a small page, then explicitly flush
everything when an entry is evicted. */ everything when an entry is evicted. */
*page_size = TARGET_PAGE_SIZE; *page_size = TARGET_PAGE_SIZE;
if (rw == 2) if (rw == 2)
return get_physical_address_code(env, physical, prot, address, return get_physical_address_code(env, physical, prot, address,
is_user); mmu_idx);
else else
return get_physical_address_data(env, physical, prot, address, rw, return get_physical_address_data(env, physical, prot, address, rw,
is_user); mmu_idx);
} }
/* Perform address translation */ /* Perform address translation */
@ -659,21 +673,27 @@ void dump_mmu(CPUState *env)
#if !defined(CONFIG_USER_ONLY) #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_phys_addr_t phys_addr;
target_ulong page_size; target_ulong page_size;
int prot, access_index; int prot, access_index;
if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, 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, 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; return -1;
if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
return -1; return -1;
return phys_addr; 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 #endif
void cpu_reset(CPUSPARCState *env) void cpu_reset(CPUSPARCState *env)

View File

@ -1,9 +1,6 @@
#include "exec.h" #include "exec.h"
#include "host-utils.h" #include "host-utils.h"
#include "helper.h" #include "helper.h"
#if !defined(CONFIG_USER_ONLY)
#include "softmmu_exec.h"
#endif /* !defined(CONFIG_USER_ONLY) */
//#define DEBUG_MMU //#define DEBUG_MMU
//#define DEBUG_MXCC //#define DEBUG_MXCC
@ -2142,17 +2139,29 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
switch (asi) { switch (asi) {
case 0x82: // Primary no-fault case 0x82: // Primary no-fault
case 0x8a: // Primary no-fault LE 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 #ifdef DEBUG_ASI
dump_asi("read ", last_addr, asi, size, ret); dump_asi("read ", last_addr, asi, size, ret);
#endif #endif
return 0; return 0;
} }
}
// Fall through // Fall through
case 0x10: // As if user primary case 0x10: // As if user primary
case 0x11: // As if user secondary
case 0x18: // As if user primary LE case 0x18: // As if user primary LE
case 0x19: // As if user secondary LE
case 0x80: // Primary case 0x80: // Primary
case 0x81: // Secondary
case 0x88: // Primary LE case 0x88: // Primary LE
case 0x89: // Secondary LE
case 0xe2: // UA2007 Primary block init case 0xe2: // UA2007 Primary block init
case 0xe3: // UA2007 Secondary block init case 0xe3: // UA2007 Secondary block init
if ((asi & 0x80) && (env->pstate & PS_PRIV)) { if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
@ -2173,6 +2182,24 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
ret = ldq_hypv(addr); ret = ldq_hypv(addr);
break; 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 { } else {
switch(size) { switch(size) {
case 1: case 1:
@ -2190,6 +2217,25 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
break; break;
} }
} }
}
} else {
/* secondary space access has lowest asi bit equal to 1 */
if (asi & 1) {
switch(size) {
case 1:
ret = ldub_user_secondary(addr);
break;
case 2:
ret = lduw_user_secondary(addr);
break;
case 4:
ret = ldl_user_secondary(addr);
break;
default:
case 8:
ret = ldq_user_secondary(addr);
break;
}
} else { } else {
switch(size) { switch(size) {
case 1: case 1:
@ -2207,6 +2253,7 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
break; break;
} }
} }
}
break; break;
case 0x14: // Bypass case 0x14: // Bypass
case 0x15: // Bypass, non-cacheable case 0x15: // Bypass, non-cacheable
@ -2235,22 +2282,27 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
// Only ldda allowed // Only ldda allowed
raise_exception(TT_ILL_INSN); raise_exception(TT_ILL_INSN);
return 0; 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 0x04: // Nucleus
case 0x0c: // Nucleus Little Endian (LE) 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 0x4a: // UPA config
case 0x81: // Secondary
case 0x89: // Secondary LE
// XXX // XXX
break; break;
case 0x45: // LSU case 0x45: // LSU
@ -2464,9 +2516,13 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
switch(asi) { switch(asi) {
case 0x10: // As if user primary case 0x10: // As if user primary
case 0x11: // As if user secondary
case 0x18: // As if user primary LE case 0x18: // As if user primary LE
case 0x19: // As if user secondary LE
case 0x80: // Primary case 0x80: // Primary
case 0x81: // Secondary
case 0x88: // Primary LE case 0x88: // Primary LE
case 0x89: // Secondary LE
case 0xe2: // UA2007 Primary block init case 0xe2: // UA2007 Primary block init
case 0xe3: // UA2007 Secondary block init case 0xe3: // UA2007 Secondary block init
if ((asi & 0x80) && (env->pstate & PS_PRIV)) { if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
@ -2487,6 +2543,24 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
stq_hypv(addr, val); stq_hypv(addr, val);
break; 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 { } else {
switch(size) { switch(size) {
case 1: case 1:
@ -2504,6 +2578,25 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
break; break;
} }
} }
}
} else {
/* secondary space access has lowest asi bit equal to 1 */
if (asi & 1) {
switch(size) {
case 1:
stb_user_secondary(addr, val);
break;
case 2:
stw_user_secondary(addr, val);
break;
case 4:
stl_user_secondary(addr, val);
break;
case 8:
default:
stq_user_secondary(addr, val);
break;
}
} else { } else {
switch(size) { switch(size) {
case 1: case 1:
@ -2521,6 +2614,7 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
break; break;
} }
} }
}
break; break;
case 0x14: // Bypass case 0x14: // Bypass
case 0x15: // Bypass, non-cacheable case 0x15: // Bypass, non-cacheable
@ -2551,11 +2645,26 @@ void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
return; return;
case 0x04: // Nucleus case 0x04: // Nucleus
case 0x0c: // Nucleus Little Endian (LE) 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 0x4a: // UPA config
case 0x81: // Secondary
case 0x89: // Secondary LE
// XXX // XXX
return; return;
case 0x45: // LSU case 0x45: // LSU