MIPS TLB style selection at runtime, by Herve Poussineau.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2809 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
f707cfba9a
commit
29929e3490
@ -33,9 +33,8 @@ union fpr_t {
|
|||||||
# define FP_ENDIAN_IDX 0
|
# define FP_ENDIAN_IDX 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
typedef struct r4k_tlb_t r4k_tlb_t;
|
||||||
typedef struct tlb_t tlb_t;
|
struct r4k_tlb_t {
|
||||||
struct tlb_t {
|
|
||||||
target_ulong VPN;
|
target_ulong VPN;
|
||||||
uint32_t PageMask;
|
uint32_t PageMask;
|
||||||
uint_fast8_t ASID;
|
uint_fast8_t ASID;
|
||||||
@ -48,7 +47,6 @@ struct tlb_t {
|
|||||||
uint_fast16_t D1:1;
|
uint_fast16_t D1:1;
|
||||||
target_ulong PFN[2];
|
target_ulong PFN[2];
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct CPUMIPSState CPUMIPSState;
|
typedef struct CPUMIPSState CPUMIPSState;
|
||||||
struct CPUMIPSState {
|
struct CPUMIPSState {
|
||||||
@ -100,11 +98,19 @@ struct CPUMIPSState {
|
|||||||
#define FP_INVALID 16
|
#define FP_INVALID 16
|
||||||
#define FP_UNIMPLEMENTED 32
|
#define FP_UNIMPLEMENTED 32
|
||||||
|
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
|
||||||
tlb_t tlb[MIPS_TLB_MAX];
|
|
||||||
uint32_t tlb_in_use;
|
|
||||||
uint32_t nb_tlb;
|
uint32_t nb_tlb;
|
||||||
#endif
|
uint32_t tlb_in_use;
|
||||||
|
int (*map_address) (CPUMIPSState *env, target_ulong *physical, int *prot, target_ulong address, int rw, int access_type);
|
||||||
|
void (*do_tlbwi) (void);
|
||||||
|
void (*do_tlbwr) (void);
|
||||||
|
void (*do_tlbp) (void);
|
||||||
|
void (*do_tlbr) (void);
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
r4k_tlb_t tlb[MIPS_TLB_MAX];
|
||||||
|
} r4k;
|
||||||
|
} mmu;
|
||||||
|
|
||||||
int32_t CP0_Index;
|
int32_t CP0_Index;
|
||||||
int32_t CP0_Random;
|
int32_t CP0_Random;
|
||||||
target_ulong CP0_EntryLo0;
|
target_ulong CP0_EntryLo0;
|
||||||
@ -289,6 +295,16 @@ struct CPUMIPSState {
|
|||||||
struct QEMUTimer *timer; /* Internal timer */
|
struct QEMUTimer *timer; /* Internal timer */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int no_mmu_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type);
|
||||||
|
int fixed_mmu_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type);
|
||||||
|
int r4k_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type);
|
||||||
|
void r4k_do_tlbwi (void);
|
||||||
|
void r4k_do_tlbwr (void);
|
||||||
|
void r4k_do_tlbp (void);
|
||||||
|
void r4k_do_tlbr (void);
|
||||||
typedef struct mips_def_t mips_def_t;
|
typedef struct mips_def_t mips_def_t;
|
||||||
int mips_find_by_name (const unsigned char *name, mips_def_t **def);
|
int mips_find_by_name (const unsigned char *name, mips_def_t **def);
|
||||||
void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
||||||
|
@ -105,10 +105,6 @@ void do_mfc0_count(void);
|
|||||||
void do_mtc0_entryhi(uint32_t in);
|
void do_mtc0_entryhi(uint32_t in);
|
||||||
void do_mtc0_status_debug(uint32_t old, uint32_t val);
|
void do_mtc0_status_debug(uint32_t old, uint32_t val);
|
||||||
void do_mtc0_status_irqraise_debug(void);
|
void do_mtc0_status_irqraise_debug(void);
|
||||||
void do_tlbwi (void);
|
|
||||||
void do_tlbwr (void);
|
|
||||||
void do_tlbp (void);
|
|
||||||
void do_tlbr (void);
|
|
||||||
void dump_fpu(CPUState *env);
|
void dump_fpu(CPUState *env);
|
||||||
void fpu_dump_state(CPUState *env, FILE *f,
|
void fpu_dump_state(CPUState *env, FILE *f,
|
||||||
int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
|
int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
|
||||||
@ -151,7 +147,7 @@ void dump_sc (void);
|
|||||||
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
|
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
|
||||||
int is_user, int is_softmmu);
|
int is_user, int is_softmmu);
|
||||||
void do_interrupt (CPUState *env);
|
void do_interrupt (CPUState *env);
|
||||||
void invalidate_tlb (CPUState *env, int idx, int use_extra);
|
void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra);
|
||||||
|
|
||||||
void cpu_loop_exit(void);
|
void cpu_loop_exit(void);
|
||||||
void do_raise_exception_err (uint32_t exception, int error_code);
|
void do_raise_exception_err (uint32_t exception, int error_code);
|
||||||
|
@ -36,16 +36,42 @@ enum {
|
|||||||
TLBRET_MATCH = 0
|
TLBRET_MATCH = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
/* MIPS32 4K MMU emulation */
|
/* no MMU emulation */
|
||||||
#ifdef MIPS_USES_R4K_TLB
|
int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
|
||||||
static int map_address (CPUState *env, target_ulong *physical, int *prot,
|
|
||||||
target_ulong address, int rw, int access_type)
|
target_ulong address, int rw, int access_type)
|
||||||
|
{
|
||||||
|
*physical = address;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
return TLBRET_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixed mapping MMU emulation */
|
||||||
|
int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type)
|
||||||
|
{
|
||||||
|
if (address <= (int32_t)0x7FFFFFFFUL) {
|
||||||
|
if (!(env->CP0_Status & (1 << CP0St_ERL)))
|
||||||
|
*physical = address + 0x40000000UL;
|
||||||
|
else
|
||||||
|
*physical = address;
|
||||||
|
} else if (address <= (int32_t)0xBFFFFFFFUL)
|
||||||
|
*physical = address & 0x1FFFFFFF;
|
||||||
|
else
|
||||||
|
*physical = address;
|
||||||
|
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
return TLBRET_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MIPS32/MIPS64 R4000-style MMU emulation */
|
||||||
|
int r4k_map_address (CPUState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type)
|
||||||
{
|
{
|
||||||
uint8_t ASID = env->CP0_EntryHi & 0xFF;
|
uint8_t ASID = env->CP0_EntryHi & 0xFF;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < env->tlb_in_use; i++) {
|
for (i = 0; i < env->tlb_in_use; i++) {
|
||||||
tlb_t *tlb = &env->tlb[i];
|
r4k_tlb_t *tlb = &env->mmu.r4k.tlb[i];
|
||||||
/* 1k pages are not supported. */
|
/* 1k pages are not supported. */
|
||||||
target_ulong mask = tlb->PageMask | 0x1FFF;
|
target_ulong mask = tlb->PageMask | 0x1FFF;
|
||||||
target_ulong tag = address & ~mask;
|
target_ulong tag = address & ~mask;
|
||||||
@ -71,7 +97,6 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot,
|
|||||||
}
|
}
|
||||||
return TLBRET_NOMATCH;
|
return TLBRET_NOMATCH;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static int get_physical_address (CPUState *env, target_ulong *physical,
|
static int get_physical_address (CPUState *env, target_ulong *physical,
|
||||||
int *prot, target_ulong address,
|
int *prot, target_ulong address,
|
||||||
@ -104,14 +129,9 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
|
|||||||
if (address <= (int32_t)0x7FFFFFFFUL) {
|
if (address <= (int32_t)0x7FFFFFFFUL) {
|
||||||
/* useg */
|
/* useg */
|
||||||
if (!(env->CP0_Status & (1 << CP0St_ERL) && user_mode)) {
|
if (!(env->CP0_Status & (1 << CP0St_ERL) && user_mode)) {
|
||||||
#ifdef MIPS_USES_R4K_TLB
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
|
||||||
#else
|
|
||||||
*physical = address + 0x40000000UL;
|
|
||||||
*prot = PAGE_READ | PAGE_WRITE;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
*physical = address;
|
*physical = address & 0xFFFFFFFF;
|
||||||
*prot = PAGE_READ | PAGE_WRITE;
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
}
|
}
|
||||||
#ifdef TARGET_MIPS64
|
#ifdef TARGET_MIPS64
|
||||||
@ -123,14 +143,14 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
|
|||||||
} else if (address < 0x3FFFFFFFFFFFFFFFULL) {
|
} else if (address < 0x3FFFFFFFFFFFFFFFULL) {
|
||||||
/* xuseg */
|
/* xuseg */
|
||||||
if (UX && address < 0x000000FFFFFFFFFFULL) {
|
if (UX && address < 0x000000FFFFFFFFFFULL) {
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
} else {
|
} else {
|
||||||
ret = TLBRET_BADADDR;
|
ret = TLBRET_BADADDR;
|
||||||
}
|
}
|
||||||
} else if (address < 0x7FFFFFFFFFFFFFFFULL) {
|
} else if (address < 0x7FFFFFFFFFFFFFFFULL) {
|
||||||
/* xsseg */
|
/* xsseg */
|
||||||
if (SX && address < 0x400000FFFFFFFFFFULL) {
|
if (SX && address < 0x400000FFFFFFFFFFULL) {
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
} else {
|
} else {
|
||||||
ret = TLBRET_BADADDR;
|
ret = TLBRET_BADADDR;
|
||||||
}
|
}
|
||||||
@ -148,7 +168,7 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
|
|||||||
/* xkseg */
|
/* xkseg */
|
||||||
/* XXX: check supervisor mode */
|
/* XXX: check supervisor mode */
|
||||||
if (KX && address < 0xC00000FF7FFFFFFFULL) {
|
if (KX && address < 0xC00000FF7FFFFFFFULL) {
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
} else {
|
} else {
|
||||||
ret = TLBRET_BADADDR;
|
ret = TLBRET_BADADDR;
|
||||||
}
|
}
|
||||||
@ -165,22 +185,12 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
|
|||||||
*prot = PAGE_READ | PAGE_WRITE;
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
} else if (address < (int32_t)0xE0000000UL) {
|
} else if (address < (int32_t)0xE0000000UL) {
|
||||||
/* kseg2 */
|
/* kseg2 */
|
||||||
#ifdef MIPS_USES_R4K_TLB
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
|
||||||
#else
|
|
||||||
*physical = address & 0xFFFFFFFF;
|
|
||||||
*prot = PAGE_READ | PAGE_WRITE;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
/* kseg3 */
|
/* kseg3 */
|
||||||
/* XXX: check supervisor mode */
|
/* XXX: check supervisor mode */
|
||||||
/* XXX: debug segment is not emulated */
|
/* XXX: debug segment is not emulated */
|
||||||
#ifdef MIPS_USES_R4K_TLB
|
ret = env->map_address(env, physical, prot, address, rw, access_type);
|
||||||
ret = map_address(env, physical, prot, address, rw, access_type);
|
|
||||||
#else
|
|
||||||
*physical = address & 0xFFFFFFFF;
|
|
||||||
*prot = PAGE_READ | PAGE_WRITE;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
if (logfile) {
|
if (logfile) {
|
||||||
@ -483,15 +493,15 @@ void do_interrupt (CPUState *env)
|
|||||||
}
|
}
|
||||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||||
|
|
||||||
void invalidate_tlb (CPUState *env, int idx, int use_extra)
|
void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
r4k_tlb_t *tlb;
|
||||||
target_ulong addr;
|
target_ulong addr;
|
||||||
target_ulong end;
|
target_ulong end;
|
||||||
uint8_t ASID = env->CP0_EntryHi & 0xFF;
|
uint8_t ASID = env->CP0_EntryHi & 0xFF;
|
||||||
target_ulong mask;
|
target_ulong mask;
|
||||||
|
|
||||||
tlb = &env->tlb[idx];
|
tlb = &env->mmu.r4k.tlb[idx];
|
||||||
/* The qemu TLB is flushed then the ASID changes, so no need to
|
/* The qemu TLB is flushed then the ASID changes, so no need to
|
||||||
flush these entries again. */
|
flush these entries again. */
|
||||||
if (tlb->G == 0 && tlb->ASID != ASID) {
|
if (tlb->G == 0 && tlb->ASID != ASID) {
|
||||||
@ -502,7 +512,7 @@ void invalidate_tlb (CPUState *env, int idx, int use_extra)
|
|||||||
/* For tlbwr, we can shadow the discarded entry into
|
/* For tlbwr, we can shadow the discarded entry into
|
||||||
a new (fake) TLB entry, as long as the guest can not
|
a new (fake) TLB entry, as long as the guest can not
|
||||||
tell that it's there. */
|
tell that it's there. */
|
||||||
env->tlb[env->tlb_in_use] = *tlb;
|
env->mmu.r4k.tlb[env->tlb_in_use] = *tlb;
|
||||||
env->tlb_in_use++;
|
env->tlb_in_use++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
/* real pages are variable size... */
|
/* real pages are variable size... */
|
||||||
#define TARGET_PAGE_BITS 12
|
#define TARGET_PAGE_BITS 12
|
||||||
/* Uses MIPS R4Kc TLB model */
|
|
||||||
#define MIPS_USES_R4K_TLB
|
|
||||||
#define MIPS_TLB_MAX 128
|
#define MIPS_TLB_MAX 128
|
||||||
|
|
||||||
#ifdef TARGET_MIPS64
|
#ifdef TARGET_MIPS64
|
||||||
|
@ -1411,12 +1411,7 @@ void op_mtc0_ebase (void)
|
|||||||
|
|
||||||
void op_mtc0_config0 (void)
|
void op_mtc0_config0 (void)
|
||||||
{
|
{
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (T0 & 0x00000001);
|
||||||
/* Fixed mapping MMU not implemented */
|
|
||||||
env->CP0_Config0 = (env->CP0_Config0 & 0x8017FF88) | (T0 & 0x00000001);
|
|
||||||
#else
|
|
||||||
env->CP0_Config0 = (env->CP0_Config0 & 0xFE17FF88) | (T0 & 0x00000001);
|
|
||||||
#endif
|
|
||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2680,31 +2675,29 @@ void op_bc1tany4 (void)
|
|||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
|
||||||
void op_tlbwi (void)
|
void op_tlbwi (void)
|
||||||
{
|
{
|
||||||
CALL_FROM_TB0(do_tlbwi);
|
CALL_FROM_TB0(env->do_tlbwi);
|
||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
void op_tlbwr (void)
|
void op_tlbwr (void)
|
||||||
{
|
{
|
||||||
CALL_FROM_TB0(do_tlbwr);
|
CALL_FROM_TB0(env->do_tlbwr);
|
||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
void op_tlbp (void)
|
void op_tlbp (void)
|
||||||
{
|
{
|
||||||
CALL_FROM_TB0(do_tlbp);
|
CALL_FROM_TB0(env->do_tlbp);
|
||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
void op_tlbr (void)
|
void op_tlbr (void)
|
||||||
{
|
{
|
||||||
CALL_FROM_TB0(do_tlbr);
|
CALL_FROM_TB0(env->do_tlbr);
|
||||||
RETURN();
|
RETURN();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Specials */
|
/* Specials */
|
||||||
#if defined (CONFIG_USER_ONLY)
|
#if defined (CONFIG_USER_ONLY)
|
||||||
|
@ -298,26 +298,6 @@ void do_mtc0_status_irqraise_debug (void)
|
|||||||
cpu_abort(env, "mtc0 status irqraise debug\n");
|
cpu_abort(env, "mtc0 status irqraise debug\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tlbwi (void)
|
|
||||||
{
|
|
||||||
cpu_abort(env, "tlbwi\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_tlbwr (void)
|
|
||||||
{
|
|
||||||
cpu_abort(env, "tlbwr\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_tlbp (void)
|
|
||||||
{
|
|
||||||
cpu_abort(env, "tlbp\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_tlbr (void)
|
|
||||||
{
|
|
||||||
cpu_abort(env, "tlbr\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
||||||
{
|
{
|
||||||
cpu_abort(env, "mips_tlb_flush\n");
|
cpu_abort(env, "mips_tlb_flush\n");
|
||||||
@ -389,7 +369,6 @@ void fpu_handle_exception(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TLB management */
|
/* TLB management */
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
|
||||||
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
||||||
{
|
{
|
||||||
/* Flush qemu's TLB and discard all shadowed entries. */
|
/* Flush qemu's TLB and discard all shadowed entries. */
|
||||||
@ -397,20 +376,20 @@ void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
|||||||
env->tlb_in_use = env->nb_tlb;
|
env->tlb_in_use = env->nb_tlb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mips_tlb_flush_extra (CPUState *env, int first)
|
static void r4k_mips_tlb_flush_extra (CPUState *env, int first)
|
||||||
{
|
{
|
||||||
/* Discard entries from env->tlb[first] onwards. */
|
/* Discard entries from env->tlb[first] onwards. */
|
||||||
while (env->tlb_in_use > first) {
|
while (env->tlb_in_use > first) {
|
||||||
invalidate_tlb(env, --env->tlb_in_use, 0);
|
r4k_invalidate_tlb(env, --env->tlb_in_use, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fill_tlb (int idx)
|
static void r4k_fill_tlb (int idx)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
r4k_tlb_t *tlb;
|
||||||
|
|
||||||
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
|
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
|
||||||
tlb = &env->tlb[idx];
|
tlb = &env->mmu.r4k.tlb[idx];
|
||||||
tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF;
|
tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF;
|
||||||
tlb->ASID = env->CP0_EntryHi & 0xFF;
|
tlb->ASID = env->CP0_EntryHi & 0xFF;
|
||||||
tlb->PageMask = env->CP0_PageMask;
|
tlb->PageMask = env->CP0_PageMask;
|
||||||
@ -425,28 +404,28 @@ static void fill_tlb (int idx)
|
|||||||
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
|
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tlbwi (void)
|
void r4k_do_tlbwi (void)
|
||||||
{
|
{
|
||||||
/* Discard cached TLB entries. We could avoid doing this if the
|
/* Discard cached TLB entries. We could avoid doing this if the
|
||||||
tlbwi is just upgrading access permissions on the current entry;
|
tlbwi is just upgrading access permissions on the current entry;
|
||||||
that might be a further win. */
|
that might be a further win. */
|
||||||
mips_tlb_flush_extra (env, env->nb_tlb);
|
r4k_mips_tlb_flush_extra (env, env->nb_tlb);
|
||||||
|
|
||||||
invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
|
r4k_invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
|
||||||
fill_tlb(env->CP0_Index % env->nb_tlb);
|
r4k_fill_tlb(env->CP0_Index % env->nb_tlb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tlbwr (void)
|
void r4k_do_tlbwr (void)
|
||||||
{
|
{
|
||||||
int r = cpu_mips_get_random(env);
|
int r = cpu_mips_get_random(env);
|
||||||
|
|
||||||
invalidate_tlb(env, r, 1);
|
r4k_invalidate_tlb(env, r, 1);
|
||||||
fill_tlb(r);
|
r4k_fill_tlb(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tlbp (void)
|
void r4k_do_tlbp (void)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
r4k_tlb_t *tlb;
|
||||||
target_ulong tag;
|
target_ulong tag;
|
||||||
uint8_t ASID;
|
uint8_t ASID;
|
||||||
int i;
|
int i;
|
||||||
@ -454,7 +433,7 @@ void do_tlbp (void)
|
|||||||
tag = env->CP0_EntryHi & (int32_t)0xFFFFE000;
|
tag = env->CP0_EntryHi & (int32_t)0xFFFFE000;
|
||||||
ASID = env->CP0_EntryHi & 0xFF;
|
ASID = env->CP0_EntryHi & 0xFF;
|
||||||
for (i = 0; i < env->nb_tlb; i++) {
|
for (i = 0; i < env->nb_tlb; i++) {
|
||||||
tlb = &env->tlb[i];
|
tlb = &env->mmu.r4k.tlb[i];
|
||||||
/* Check ASID, virtual page number & size */
|
/* Check ASID, virtual page number & size */
|
||||||
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
||||||
/* TLB match */
|
/* TLB match */
|
||||||
@ -465,11 +444,11 @@ void do_tlbp (void)
|
|||||||
if (i == env->nb_tlb) {
|
if (i == env->nb_tlb) {
|
||||||
/* No match. Discard any shadow entries, if any of them match. */
|
/* No match. Discard any shadow entries, if any of them match. */
|
||||||
for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
|
for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
|
||||||
tlb = &env->tlb[i];
|
tlb = &env->mmu.r4k.tlb[i];
|
||||||
|
|
||||||
/* Check ASID, virtual page number & size */
|
/* Check ASID, virtual page number & size */
|
||||||
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
||||||
mips_tlb_flush_extra (env, i);
|
r4k_mips_tlb_flush_extra (env, i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,19 +457,19 @@ void do_tlbp (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tlbr (void)
|
void r4k_do_tlbr (void)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
r4k_tlb_t *tlb;
|
||||||
uint8_t ASID;
|
uint8_t ASID;
|
||||||
|
|
||||||
ASID = env->CP0_EntryHi & 0xFF;
|
ASID = env->CP0_EntryHi & 0xFF;
|
||||||
tlb = &env->tlb[env->CP0_Index % env->nb_tlb];
|
tlb = &env->mmu.r4k.tlb[env->CP0_Index % env->nb_tlb];
|
||||||
|
|
||||||
/* If this will change the current ASID, flush qemu's TLB. */
|
/* If this will change the current ASID, flush qemu's TLB. */
|
||||||
if (ASID != tlb->ASID)
|
if (ASID != tlb->ASID)
|
||||||
cpu_mips_tlb_flush (env, 1);
|
cpu_mips_tlb_flush (env, 1);
|
||||||
|
|
||||||
mips_tlb_flush_extra(env, env->nb_tlb);
|
r4k_mips_tlb_flush_extra(env, env->nb_tlb);
|
||||||
|
|
||||||
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
||||||
env->CP0_PageMask = tlb->PageMask;
|
env->CP0_PageMask = tlb->PageMask;
|
||||||
@ -499,7 +478,6 @@ void do_tlbr (void)
|
|||||||
env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
|
env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
|
||||||
(tlb->C1 << 3) | (tlb->PFN[1] >> 6);
|
(tlb->C1 << 3) | (tlb->PFN[1] >> 6);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
#endif /* !CONFIG_USER_ONLY */
|
||||||
|
|
||||||
|
@ -4164,7 +4164,7 @@ die:
|
|||||||
}
|
}
|
||||||
#endif /* TARGET_MIPS64 */
|
#endif /* TARGET_MIPS64 */
|
||||||
|
|
||||||
static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
|
static void gen_cp0 (CPUState *env, DisasContext *ctx, uint32_t opc, int rt, int rd)
|
||||||
{
|
{
|
||||||
const char *opn = "ldst";
|
const char *opn = "ldst";
|
||||||
|
|
||||||
@ -4199,24 +4199,30 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
|
|||||||
opn = "dmtc0";
|
opn = "dmtc0";
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
|
||||||
case OPC_TLBWI:
|
case OPC_TLBWI:
|
||||||
gen_op_tlbwi();
|
|
||||||
opn = "tlbwi";
|
opn = "tlbwi";
|
||||||
|
if (!env->do_tlbwi)
|
||||||
|
goto die;
|
||||||
|
gen_op_tlbwi();
|
||||||
break;
|
break;
|
||||||
case OPC_TLBWR:
|
case OPC_TLBWR:
|
||||||
gen_op_tlbwr();
|
|
||||||
opn = "tlbwr";
|
opn = "tlbwr";
|
||||||
|
if (!env->do_tlbwr)
|
||||||
|
goto die;
|
||||||
|
gen_op_tlbwr();
|
||||||
break;
|
break;
|
||||||
case OPC_TLBP:
|
case OPC_TLBP:
|
||||||
gen_op_tlbp();
|
|
||||||
opn = "tlbp";
|
opn = "tlbp";
|
||||||
|
if (!env->do_tlbp)
|
||||||
|
goto die;
|
||||||
|
gen_op_tlbp();
|
||||||
break;
|
break;
|
||||||
case OPC_TLBR:
|
case OPC_TLBR:
|
||||||
gen_op_tlbr();
|
|
||||||
opn = "tlbr";
|
opn = "tlbr";
|
||||||
|
if (!env->do_tlbr)
|
||||||
|
goto die;
|
||||||
|
gen_op_tlbr();
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
case OPC_ERET:
|
case OPC_ERET:
|
||||||
opn = "eret";
|
opn = "eret";
|
||||||
save_cpu_state(ctx, 0);
|
save_cpu_state(ctx, 0);
|
||||||
@ -4244,6 +4250,7 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
|
|||||||
ctx->bstate = BS_EXCP;
|
ctx->bstate = BS_EXCP;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
die:
|
||||||
MIPS_INVAL(opn);
|
MIPS_INVAL(opn);
|
||||||
generate_exception(ctx, EXCP_RI);
|
generate_exception(ctx, EXCP_RI);
|
||||||
return;
|
return;
|
||||||
@ -5576,10 +5583,10 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
|
|||||||
case OPC_DMFC0:
|
case OPC_DMFC0:
|
||||||
case OPC_DMTC0:
|
case OPC_DMTC0:
|
||||||
#endif
|
#endif
|
||||||
gen_cp0(ctx, op1, rt, rd);
|
gen_cp0(env, ctx, op1, rt, rd);
|
||||||
break;
|
break;
|
||||||
case OPC_C0_FIRST ... OPC_C0_LAST:
|
case OPC_C0_FIRST ... OPC_C0_LAST:
|
||||||
gen_cp0(ctx, MASK_C0(ctx->opcode), rt, rd);
|
gen_cp0(env, ctx, MASK_C0(ctx->opcode), rt, rd);
|
||||||
break;
|
break;
|
||||||
case OPC_MFMC0:
|
case OPC_MFMC0:
|
||||||
op2 = MASK_MFMC0(ctx->opcode);
|
op2 = MASK_MFMC0(ctx->opcode);
|
||||||
|
@ -148,7 +148,7 @@ static mips_def_t mips_defs[] =
|
|||||||
.Status_rw_bitmask = 0x3678FFFF,
|
.Status_rw_bitmask = 0x3678FFFF,
|
||||||
.CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
|
.CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
|
||||||
(1 << FCR0_D) | (1 << FCR0_S) |
|
(1 << FCR0_D) | (1 << FCR0_S) |
|
||||||
(0x4 << FCR0_PRID) | (0x0 << FCR0_REV),
|
(0x5 << FCR0_PRID) | (0x0 << FCR0_REV),
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@ -180,6 +180,30 @@ void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void no_mmu_init (CPUMIPSState *env, mips_def_t *def)
|
||||||
|
{
|
||||||
|
env->nb_tlb = 1;
|
||||||
|
env->map_address = &no_mmu_map_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixed_mmu_init (CPUMIPSState *env, mips_def_t *def)
|
||||||
|
{
|
||||||
|
env->nb_tlb = 1;
|
||||||
|
env->map_address = &fixed_mmu_map_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void r4k_mmu_init (CPUMIPSState *env, mips_def_t *def)
|
||||||
|
{
|
||||||
|
env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
|
||||||
|
env->map_address = &r4k_map_address;
|
||||||
|
env->do_tlbwi = r4k_do_tlbwi;
|
||||||
|
env->do_tlbwr = r4k_do_tlbwr;
|
||||||
|
env->do_tlbp = r4k_do_tlbp;
|
||||||
|
env->do_tlbr = r4k_do_tlbr;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_USER_ONLY */
|
||||||
|
|
||||||
int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
|
int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
|
||||||
{
|
{
|
||||||
if (!def)
|
if (!def)
|
||||||
@ -199,10 +223,23 @@ int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
|
|||||||
env->CCRes = def->CCRes;
|
env->CCRes = def->CCRes;
|
||||||
env->Status_rw_bitmask = def->Status_rw_bitmask;
|
env->Status_rw_bitmask = def->Status_rw_bitmask;
|
||||||
env->fcr0 = def->CP1_fcr0;
|
env->fcr0 = def->CP1_fcr0;
|
||||||
#if defined (MIPS_USES_R4K_TLB)
|
#ifndef CONFIG_USER_ONLY
|
||||||
env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
|
switch ((env->CP0_Config0 >> CP0C0_MT) & 3) {
|
||||||
|
case 0:
|
||||||
|
no_mmu_init(env, def);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r4k_mmu_init(env, def);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
fixed_mmu_init(env, def);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Older CPUs like the R3000 may need nonstandard handling here. */
|
||||||
|
cpu_abort(env, "MMU type not supported\n");
|
||||||
|
}
|
||||||
env->CP0_Random = env->nb_tlb - 1;
|
env->CP0_Random = env->nb_tlb - 1;
|
||||||
env->tlb_in_use = env->nb_tlb;
|
env->tlb_in_use = env->nb_tlb;
|
||||||
#endif
|
#endif /* CONFIG_USER_ONLY */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user