From 5eb7995e34ebf8cf9a3fc43ed2c7af93149d1b0d Mon Sep 17 00:00:00 2001 From: j_mayer Date: Wed, 19 Sep 2007 05:44:04 +0000 Subject: [PATCH] Code provision for PowerPC BookE MMU model support. Better MSR flags initialisation. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3189 c046a42c-6fe2-441c-8c8c-71466251a162 --- target-ppc/helper.c | 79 +++++++++++++++++++--- target-ppc/op.c | 48 ++++++++++++++ target-ppc/op_helper.c | 147 +++++++++++++++++++++++++++++++++++++++++ target-ppc/op_helper.h | 12 ++++ target-ppc/translate.c | 93 +++++++++++++++++++++++++- 5 files changed, 365 insertions(+), 14 deletions(-) diff --git a/target-ppc/helper.c b/target-ppc/helper.c index b6fc803b45..80315f6de2 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1013,6 +1013,52 @@ void store_40x_sler (CPUPPCState *env, uint32_t val) env->spr[SPR_405_SLER] = val; } +int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + int i, prot, ret; + + ret = -1; + raddr = -1; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb[i].tlbe; + if (ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_BOOKE_PID], 1, i) < 0) + continue; + if (msr_pr) + prot = tlb->prot & 0xF; + else + prot = (tlb->prot >> 4) & 0xF; + /* Check the address space */ + if (access_type == ACCESS_CODE) { + if (msr_is != (tlb->attr & 1)) + continue; + ctx->prot = prot; + if (prot & PAGE_EXEC) { + ret = 0; + break; + } + ret = -3; + } else { + if (msr_ds != (tlb->attr & 1)) + continue; + ctx->prot = prot; + if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) { + ret = 0; + break; + } + ret = -2; + } + } + if (ret >= 0) + ctx->raddr = raddr; + + return ret; +} + static int check_physical (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw) { @@ -1115,9 +1161,9 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, cpu_abort(env, "601 MMU model not implemented\n"); return -1; case PPC_FLAGS_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(env, "BookeE MMU model not implemented\n"); - return -1; + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; case PPC_FLAGS_MMU_BOOKE_FSL: /* XXX: TODO */ cpu_abort(env, "BookE FSL MMU model not implemented\n"); @@ -1950,7 +1996,7 @@ void do_interrupt (CPUState *env) cpu_abort(env, "Floating point assist exception " "is not implemented yet !\n"); goto store_next; - /* 64 bits PowerPC exceptions */ + /* 64 bits PowerPC exceptions */ case EXCP_DSEG: /* 0x0380 */ /* XXX: TODO */ cpu_abort(env, "Data segment exception is not implemented yet !\n"); @@ -2446,28 +2492,39 @@ void cpu_dump_rfi (target_ulong RA, target_ulong msr) void cpu_ppc_reset (void *opaque) { CPUPPCState *env; + int i; env = opaque; + /* XXX: some of those flags initialisation values could depend + * on the actual PowerPC implementation + */ + for (i = 0; i < 63; i++) + env->msr[i] = 0; +#if defined(TARGET_PPC64) + msr_hv = 0; /* Should be 1... */ +#endif + msr_ap = 0; /* TO BE CHECKED */ + msr_sa = 0; /* TO BE CHECKED */ + msr_ip = 0; /* TO BE CHECKED */ #if defined (DO_SINGLE_STEP) && 0 /* Single step trace mode */ msr_se = 1; msr_be = 1; -#endif - msr_fp = 1; /* Allow floating point exceptions */ - msr_me = 1; /* Allow machine check exceptions */ -#if defined(TARGET_PPC64) - msr_sf = 0; /* Boot in 32 bits mode */ - msr_cm = 0; #endif #if defined(CONFIG_USER_ONLY) + msr_fp = 1; /* Allow floating point exceptions */ msr_pr = 1; - tlb_flush(env, 1); #else env->nip = 0xFFFFFFFC; ppc_tlb_invalidate_all(env); #endif do_compute_hflags(env); env->reserve = -1; + /* Be sure no exception or interrupt is pending */ + env->pending_interrupts = 0; + env->exception_index = EXCP_NONE; + /* Flush all TLBs */ + tlb_flush(env, 1); } CPUPPCState *cpu_ppc_init (void) diff --git a/target-ppc/op.c b/target-ppc/op.c index 8ad222348c..1e9bd22765 100644 --- a/target-ppc/op.c +++ b/target-ppc/op.c @@ -2365,6 +2365,54 @@ void OPPROTO op_wrte (void) RETURN(); } +void OPPROTO op_booke_tlbre0 (void) +{ + do_booke_tlbre0(); + RETURN(); +} + +void OPPROTO op_booke_tlbre1 (void) +{ + do_booke_tlbre1(); + RETURN(); +} + +void OPPROTO op_booke_tlbre2 (void) +{ + do_booke_tlbre2(); + RETURN(); +} + +void OPPROTO op_booke_tlbsx (void) +{ + do_booke_tlbsx(); + RETURN(); +} + +void OPPROTO op_booke_tlbsx_ (void) +{ + do_booke_tlbsx_(); + RETURN(); +} + +void OPPROTO op_booke_tlbwe0 (void) +{ + do_booke_tlbwe0(); + RETURN(); +} + +void OPPROTO op_booke_tlbwe1 (void) +{ + do_booke_tlbwe1(); + RETURN(); +} + +void OPPROTO op_booke_tlbwe2 (void) +{ + do_booke_tlbwe2(); + RETURN(); +} + void OPPROTO op_4xx_tlbre_lo (void) { do_4xx_tlbre_lo(); diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 4afbfd84f1..56f2a5519c 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -2605,4 +2605,151 @@ void do_4xx_tlbwe_lo (void) } #endif } + +/* BookE TLB management */ +void do_booke_tlbwe0 (void) +{ + ppcemb_tlb_t *tlb; + target_ulong EPN, size; + int do_flush_tlbs; + +#if defined (DEBUG_SOFTWARE_TLB) + if (loglevel != 0) { + fprintf(logfile, "%s T0 " REGX " T1 " REGX "\n", __func__, T0, T1); + } +#endif + do_flush_tlbs = 0; + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + EPN = T1 & 0xFFFFFC00; + if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) + do_flush_tlbs = 1; + tlb->EPN = EPN; + size = booke_tlb_to_page_size((T1 >> 4) & 0xF); + if ((tlb->prot & PAGE_VALID) && tlb->size < size) + do_flush_tlbs = 1; + tlb->size = size; + tlb->attr &= ~0x1; + tlb->attr |= (T1 >> 8) & 1; + if (T1 & 0x200) { + tlb->prot |= PAGE_VALID; + } else { + if (tlb->prot & PAGE_VALID) { + tlb->prot &= ~PAGE_VALID; + do_flush_tlbs = 1; + } + } + tlb->PID = env->spr[SPR_BOOKE_PID]; + if (do_flush_tlbs) + tlb_flush(env, 1); +} + +void do_booke_tlbwe1 (void) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t RPN; + +#if defined (DEBUG_SOFTWARE_TLB) + if (loglevel != 0) { + fprintf(logfile, "%s T0 " REGX " T1 " REGX "\n", __func__, T0, T1); + } +#endif + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + RPN = T1 & 0xFFFFFC0F; + if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) + tlb_flush(env, 1); + tlb->RPN = RPN; +} + +void do_booke_tlbwe2 (void) +{ + ppcemb_tlb_t *tlb; + +#if defined (DEBUG_SOFTWARE_TLB) + if (loglevel != 0) { + fprintf(logfile, "%s T0 " REGX " T1 " REGX "\n", __func__, T0, T1); + } +#endif + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + tlb->attr = (tlb->attr & 0x1) | (T1 & 0x0000FF00); + tlb->prot = tlb->prot & PAGE_VALID; + if (T1 & 0x1) + tlb->prot |= PAGE_READ << 4; + if (T1 & 0x2) + tlb->prot |= PAGE_WRITE << 4; + if (T1 & 0x4) + tlb->prot |= PAGE_EXEC << 4; + if (T1 & 0x8) + tlb->prot |= PAGE_READ; + if (T1 & 0x10) + tlb->prot |= PAGE_WRITE; + if (T1 & 0x20) + tlb->prot |= PAGE_EXEC; +} + +void do_booke_tlbsx (void) +{ + T0 = ppcemb_tlb_search(env, T0, env->spr[SPR_440_MMUCR]); +} + +void do_booke_tlbsx_ (void) +{ + int tmp = xer_so; + + T0 = ppcemb_tlb_search(env, T0, env->spr[SPR_440_MMUCR]); + if (T0 != -1) + tmp |= 0x02; + env->crf[0] = tmp; +} + +void do_booke_tlbre0 (void) +{ + ppcemb_tlb_t *tlb; + int size; + + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + T0 = tlb->EPN; + size = booke_page_size_to_tlb(tlb->size); + if (size < 0 || size > 0xF) + size = 1; + T0 |= size << 4; + if (tlb->attr & 0x1) + T0 |= 0x100; + if (tlb->prot & PAGE_VALID) + T0 |= 0x200; + env->spr[SPR_BOOKE_PID] = tlb->PID; +} + +void do_booke_tlbre1 (void) +{ + ppcemb_tlb_t *tlb; + + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + T0 = tlb->RPN; +} + +void do_booke_tlbre2 (void) +{ + ppcemb_tlb_t *tlb; + + T0 &= 0x3F; + tlb = &env->tlb[T0].tlbe; + T0 = tlb->attr & ~0x1; + if (tlb->prot & (PAGE_READ << 4)) + T0 |= 0x1; + if (tlb->prot & (PAGE_WRITE << 4)) + T0 |= 0x2; + if (tlb->prot & (PAGE_EXEC << 4)) + T0 |= 0x4; + if (tlb->prot & PAGE_READ) + T0 |= 0x8; + if (tlb->prot & PAGE_WRITE) + T0 |= 0x10; + if (tlb->prot & PAGE_EXEC) + T0 |= 0x20; +} #endif /* !CONFIG_USER_ONLY */ diff --git a/target-ppc/op_helper.h b/target-ppc/op_helper.h index 33f053f96f..5c412ef9c7 100644 --- a/target-ppc/op_helper.h +++ b/target-ppc/op_helper.h @@ -156,6 +156,18 @@ void do_POWER_rfsvc (void); void do_op_602_mfrom (void); #endif +/* PowerPC BookE specific helpers */ +#if !defined(CONFIG_USER_ONLY) +void do_booke_tlbre0 (void); +void do_booke_tlbre1 (void); +void do_booke_tlbre2 (void); +void do_booke_tlbsx (void); +void do_booke_tlbsx_ (void); +void do_booke_tlbwe0 (void); +void do_booke_tlbwe1 (void); +void do_booke_tlbwe2 (void); +#endif + /* PowerPC 4xx specific helpers */ void do_405_check_ov (void); void do_405_check_sat (void); diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 6583a55deb..21e70195de 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4618,9 +4618,10 @@ GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_BOOKE) RET_CHG_FLOW(ctx); #endif } + /* TLB management - PowerPC 405 implementation */ /* tlbre */ -GEN_HANDLER(tlbre, 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_SPEC) +GEN_HANDLER(tlbre_40x, 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_SPEC) { #if defined(CONFIG_USER_ONLY) RET_PRIVOPC(ctx); @@ -4648,7 +4649,7 @@ GEN_HANDLER(tlbre, 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_SPEC) } /* tlbsx - tlbsx. */ -GEN_HANDLER(tlbsx, 0x1F, 0x12, 0x1C, 0x00000000, PPC_40x_SPEC) +GEN_HANDLER(tlbsx_40x, 0x1F, 0x12, 0x1C, 0x00000000, PPC_40x_SPEC) { #if defined(CONFIG_USER_ONLY) RET_PRIVOPC(ctx); @@ -4667,7 +4668,7 @@ GEN_HANDLER(tlbsx, 0x1F, 0x12, 0x1C, 0x00000000, PPC_40x_SPEC) } /* tlbwe */ -GEN_HANDLER(tlbwe, 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_SPEC) +GEN_HANDLER(tlbwe_40x, 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_SPEC) { #if defined(CONFIG_USER_ONLY) RET_PRIVOPC(ctx); @@ -4694,6 +4695,92 @@ GEN_HANDLER(tlbwe, 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_SPEC) #endif } +/* TLB management - PowerPC BookE implementation */ +/* tlbre */ +GEN_HANDLER(tlbre_booke, 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE) +{ +#if defined(CONFIG_USER_ONLY) + RET_PRIVOPC(ctx); +#else + if (unlikely(!ctx->supervisor)) { + RET_PRIVOPC(ctx); + return; + } + switch (rB(ctx->opcode)) { + case 0: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_booke_tlbre0(); + gen_op_store_T0_gpr(rD(ctx->opcode)); + break; + case 1: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_booke_tlbre1(); + gen_op_store_T0_gpr(rD(ctx->opcode)); + break; + case 2: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_booke_tlbre2(); + gen_op_store_T0_gpr(rD(ctx->opcode)); + break; + default: + RET_INVAL(ctx); + break; + } +#endif +} + +/* tlbsx - tlbsx. */ +GEN_HANDLER(tlbsx_booke, 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE) +{ +#if defined(CONFIG_USER_ONLY) + RET_PRIVOPC(ctx); +#else + if (unlikely(!ctx->supervisor)) { + RET_PRIVOPC(ctx); + return; + } + gen_addr_reg_index(ctx); + if (Rc(ctx->opcode)) + gen_op_booke_tlbsx_(); + else + gen_op_booke_tlbsx(); + gen_op_store_T0_gpr(rD(ctx->opcode)); +#endif +} + +/* tlbwe */ +GEN_HANDLER(tlbwe_booke, 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE) +{ +#if defined(CONFIG_USER_ONLY) + RET_PRIVOPC(ctx); +#else + if (unlikely(!ctx->supervisor)) { + RET_PRIVOPC(ctx); + return; + } + switch (rB(ctx->opcode)) { + case 0: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_load_gpr_T1(rS(ctx->opcode)); + gen_op_booke_tlbwe0(); + break; + case 1: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_load_gpr_T1(rS(ctx->opcode)); + gen_op_booke_tlbwe1(); + break; + case 2: + gen_op_load_gpr_T0(rA(ctx->opcode)); + gen_op_load_gpr_T1(rS(ctx->opcode)); + gen_op_booke_tlbwe2(); + break; + default: + RET_INVAL(ctx); + break; + } +#endif +} + /* wrtee */ GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_EMB_COMMON) {