From 29929e349009731a2fb22a983da75f67b6e78362 Mon Sep 17 00:00:00 2001 From: ths Date: Sun, 13 May 2007 13:49:44 +0000 Subject: 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 --- target-mips/cpu.h | 32 +++++++++++++++----- target-mips/exec.h | 6 +--- target-mips/helper.c | 72 +++++++++++++++++++++++++------------------- target-mips/mips-defs.h | 2 -- target-mips/op.c | 17 +++-------- target-mips/op_helper.c | 64 +++++++++++++-------------------------- target-mips/translate.c | 25 +++++++++------ target-mips/translate_init.c | 45 ++++++++++++++++++++++++--- 8 files changed, 149 insertions(+), 114 deletions(-) diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 33cb657..9e02919 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -33,9 +33,8 @@ union fpr_t { # define FP_ENDIAN_IDX 0 #endif -#if defined(MIPS_USES_R4K_TLB) -typedef struct tlb_t tlb_t; -struct tlb_t { +typedef struct r4k_tlb_t r4k_tlb_t; +struct r4k_tlb_t { target_ulong VPN; uint32_t PageMask; uint_fast8_t ASID; @@ -48,7 +47,6 @@ struct tlb_t { uint_fast16_t D1:1; target_ulong PFN[2]; }; -#endif typedef struct CPUMIPSState CPUMIPSState; struct CPUMIPSState { @@ -100,11 +98,19 @@ struct CPUMIPSState { #define FP_INVALID 16 #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; -#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_Random; target_ulong CP0_EntryLo0; @@ -289,6 +295,16 @@ struct CPUMIPSState { 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; 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, ...)); diff --git a/target-mips/exec.h b/target-mips/exec.h index d9160a1..44314a9 100644 --- a/target-mips/exec.h +++ b/target-mips/exec.h @@ -105,10 +105,6 @@ void do_mfc0_count(void); void do_mtc0_entryhi(uint32_t in); void do_mtc0_status_debug(uint32_t old, uint32_t val); 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 fpu_dump_state(CPUState *env, FILE *f, 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 is_user, int is_softmmu); 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 do_raise_exception_err (uint32_t exception, int error_code); diff --git a/target-mips/helper.c b/target-mips/helper.c index 1b61a31..cb568f1 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -36,16 +36,42 @@ enum { TLBRET_MATCH = 0 }; -/* MIPS32 4K MMU emulation */ -#ifdef MIPS_USES_R4K_TLB -static int map_address (CPUState *env, target_ulong *physical, int *prot, +/* no MMU emulation */ +int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot, 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; int 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. */ target_ulong mask = tlb->PageMask | 0x1FFF; target_ulong tag = address & ~mask; @@ -71,7 +97,6 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot, } return TLBRET_NOMATCH; } -#endif static int get_physical_address (CPUState *env, target_ulong *physical, int *prot, target_ulong address, @@ -104,14 +129,9 @@ static int get_physical_address (CPUState *env, target_ulong *physical, if (address <= (int32_t)0x7FFFFFFFUL) { /* useg */ if (!(env->CP0_Status & (1 << CP0St_ERL) && user_mode)) { -#ifdef MIPS_USES_R4K_TLB - ret = map_address(env, physical, prot, address, rw, access_type); -#else - *physical = address + 0x40000000UL; - *prot = PAGE_READ | PAGE_WRITE; -#endif + ret = env->map_address(env, physical, prot, address, rw, access_type); } else { - *physical = address; + *physical = address & 0xFFFFFFFF; *prot = PAGE_READ | PAGE_WRITE; } #ifdef TARGET_MIPS64 @@ -123,14 +143,14 @@ static int get_physical_address (CPUState *env, target_ulong *physical, } else if (address < 0x3FFFFFFFFFFFFFFFULL) { /* xuseg */ 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 { ret = TLBRET_BADADDR; } } else if (address < 0x7FFFFFFFFFFFFFFFULL) { /* xsseg */ 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 { ret = TLBRET_BADADDR; } @@ -148,7 +168,7 @@ static int get_physical_address (CPUState *env, target_ulong *physical, /* xkseg */ /* XXX: check supervisor mode */ 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 { ret = TLBRET_BADADDR; } @@ -165,22 +185,12 @@ static int get_physical_address (CPUState *env, target_ulong *physical, *prot = PAGE_READ | PAGE_WRITE; } else if (address < (int32_t)0xE0000000UL) { /* kseg2 */ -#ifdef MIPS_USES_R4K_TLB - ret = map_address(env, physical, prot, address, rw, access_type); -#else - *physical = address & 0xFFFFFFFF; - *prot = PAGE_READ | PAGE_WRITE; -#endif + ret = env->map_address(env, physical, prot, address, rw, access_type); } else { /* kseg3 */ /* XXX: check supervisor mode */ /* XXX: debug segment is not emulated */ -#ifdef MIPS_USES_R4K_TLB - ret = map_address(env, physical, prot, address, rw, access_type); -#else - *physical = address & 0xFFFFFFFF; - *prot = PAGE_READ | PAGE_WRITE; -#endif + ret = env->map_address(env, physical, prot, address, rw, access_type); } #if 0 if (logfile) { @@ -483,15 +493,15 @@ void do_interrupt (CPUState *env) } #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 end; uint8_t ASID = env->CP0_EntryHi & 0xFF; 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 flush these entries again. */ 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 a new (fake) TLB entry, as long as the guest can not 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++; return; } diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index 414f476..6f012a9 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -6,8 +6,6 @@ /* real pages are variable size... */ #define TARGET_PAGE_BITS 12 -/* Uses MIPS R4Kc TLB model */ -#define MIPS_USES_R4K_TLB #define MIPS_TLB_MAX 128 #ifdef TARGET_MIPS64 diff --git a/target-mips/op.c b/target-mips/op.c index 748f997..a0611e0 100644 --- a/target-mips/op.c +++ b/target-mips/op.c @@ -1411,12 +1411,7 @@ void op_mtc0_ebase (void) void op_mtc0_config0 (void) { -#if defined(MIPS_USES_R4K_TLB) - /* 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 + env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (T0 & 0x00000001); RETURN(); } @@ -2680,31 +2675,29 @@ void op_bc1tany4 (void) RETURN(); } -#if defined(MIPS_USES_R4K_TLB) void op_tlbwi (void) { - CALL_FROM_TB0(do_tlbwi); + CALL_FROM_TB0(env->do_tlbwi); RETURN(); } void op_tlbwr (void) { - CALL_FROM_TB0(do_tlbwr); + CALL_FROM_TB0(env->do_tlbwr); RETURN(); } void op_tlbp (void) { - CALL_FROM_TB0(do_tlbp); + CALL_FROM_TB0(env->do_tlbp); RETURN(); } void op_tlbr (void) { - CALL_FROM_TB0(do_tlbr); + CALL_FROM_TB0(env->do_tlbr); RETURN(); } -#endif /* Specials */ #if defined (CONFIG_USER_ONLY) diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 2ebcede..9d7a560 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -298,26 +298,6 @@ void do_mtc0_status_irqraise_debug (void) 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) { cpu_abort(env, "mips_tlb_flush\n"); @@ -389,7 +369,6 @@ void fpu_handle_exception(void) } /* TLB management */ -#if defined(MIPS_USES_R4K_TLB) void cpu_mips_tlb_flush (CPUState *env, int flush_global) { /* 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; } -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. */ 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 */ - tlb = &env->tlb[idx]; + tlb = &env->mmu.r4k.tlb[idx]; tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF; tlb->ASID = env->CP0_EntryHi & 0xFF; tlb->PageMask = env->CP0_PageMask; @@ -425,28 +404,28 @@ static void fill_tlb (int idx) 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 tlbwi is just upgrading access permissions on the current entry; 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); - fill_tlb(env->CP0_Index % env->nb_tlb); + r4k_invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0); + 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); - invalidate_tlb(env, r, 1); - fill_tlb(r); + r4k_invalidate_tlb(env, r, 1); + r4k_fill_tlb(r); } -void do_tlbp (void) +void r4k_do_tlbp (void) { - tlb_t *tlb; + r4k_tlb_t *tlb; target_ulong tag; uint8_t ASID; int i; @@ -454,7 +433,7 @@ void do_tlbp (void) tag = env->CP0_EntryHi & (int32_t)0xFFFFE000; ASID = env->CP0_EntryHi & 0xFF; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i]; + tlb = &env->mmu.r4k.tlb[i]; /* Check ASID, virtual page number & size */ if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) { /* TLB match */ @@ -465,11 +444,11 @@ void do_tlbp (void) if (i == env->nb_tlb) { /* No match. Discard any shadow entries, if any of them match. */ 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 */ if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) { - mips_tlb_flush_extra (env, i); + r4k_mips_tlb_flush_extra (env, i); 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; 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 (ASID != tlb->ASID) 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_PageMask = tlb->PageMask; @@ -499,7 +478,6 @@ void do_tlbr (void) env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | (tlb->C1 << 3) | (tlb->PFN[1] >> 6); } -#endif #endif /* !CONFIG_USER_ONLY */ diff --git a/target-mips/translate.c b/target-mips/translate.c index 79a33c5..fb802f0 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -4164,7 +4164,7 @@ die: } #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"; @@ -4199,24 +4199,30 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd) opn = "dmtc0"; break; #endif -#if defined(MIPS_USES_R4K_TLB) case OPC_TLBWI: - gen_op_tlbwi(); opn = "tlbwi"; + if (!env->do_tlbwi) + goto die; + gen_op_tlbwi(); break; case OPC_TLBWR: - gen_op_tlbwr(); opn = "tlbwr"; + if (!env->do_tlbwr) + goto die; + gen_op_tlbwr(); break; case OPC_TLBP: - gen_op_tlbp(); opn = "tlbp"; + if (!env->do_tlbp) + goto die; + gen_op_tlbp(); break; case OPC_TLBR: - gen_op_tlbr(); opn = "tlbr"; + if (!env->do_tlbr) + goto die; + gen_op_tlbr(); break; -#endif case OPC_ERET: opn = "eret"; 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; break; default: + die: MIPS_INVAL(opn); generate_exception(ctx, EXCP_RI); return; @@ -5576,10 +5583,10 @@ static void decode_opc (CPUState *env, DisasContext *ctx) case OPC_DMFC0: case OPC_DMTC0: #endif - gen_cp0(ctx, op1, rt, rd); + gen_cp0(env, ctx, op1, rt, rd); break; 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; case OPC_MFMC0: op2 = MASK_MFMC0(ctx->opcode); diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index e38ab28..7d60f19 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -148,7 +148,7 @@ static mips_def_t mips_defs[] = .Status_rw_bitmask = 0x3678FFFF, .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x4 << FCR0_PRID) | (0x0 << FCR0_REV), + (0x5 << FCR0_PRID) | (0x0 << FCR0_REV), }, #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) { if (!def) @@ -199,10 +223,23 @@ int cpu_mips_register (CPUMIPSState *env, mips_def_t *def) env->CCRes = def->CCRes; env->Status_rw_bitmask = def->Status_rw_bitmask; env->fcr0 = def->CP1_fcr0; -#if defined (MIPS_USES_R4K_TLB) - env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); +#ifndef CONFIG_USER_ONLY + 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->tlb_in_use = env->nb_tlb; -#endif +#endif /* CONFIG_USER_ONLY */ return 0; } -- cgit v1.1