diff --git a/target-ppc/helper.c b/target-ppc/helper.c index c835b1efe8..c541e7128b 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -549,8 +549,6 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) { /* Software TLB search */ ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); - } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { - /* XXX: TODO */ } else { #if defined (DEBUG_MMU) if (loglevel > 0) { @@ -632,6 +630,115 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, return ret; } +int mmu4xx_get_physical_address (CPUState *env, mmu_ctx_t *ctx, + uint32_t address, int rw, int access_type) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + target_ulong mask; + int i, ret, zsel, zpr; + + ret = -6; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb[i].tlbe; + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + if (loglevel) + fprintf(logfile, "%s: TLB %d not valid\n", __func__, i); + continue; + } + mask = ~(tlb->size - 1); + if (loglevel) { + fprintf(logfile, "%s: TLB %d address %08x PID %04x <=> " + "%08x %08x %04x\n", + __func__, i, address, env->spr[SPR_40x_PID], + tlb->EPN, mask, tlb->PID); + } + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID]) + continue; + /* Check effective address */ + if ((address & mask) != tlb->EPN) + continue; + raddr = (tlb->RPN & mask) | (address & ~mask); + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (28 - (2 * zsel))) & 0x3; + if (loglevel) { + fprintf(logfile, "%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", + __func__, i, zsel, zpr, rw, tlb->attr); + } + if (access_type == ACCESS_CODE) { + /* Check execute enable bit */ + switch (zpr) { + case 0x0: + if (msr_pr) { + ret = -3; + ctx->prot = 0; + break; + } + /* No break here */ + case 0x1: + case 0x2: + /* Check from TLB entry */ + if (!(tlb->prot & PAGE_EXEC)) { + ret = -3; + } else { + if (tlb->prot & PAGE_WRITE) + ctx->prot = PAGE_READ | PAGE_WRITE; + else + ctx->prot = PAGE_READ; + ret = 0; + } + break; + case 0x3: + /* All accesses granted */ + ret = 0; + ctx->prot = PAGE_READ | PAGE_WRITE; + break; + } + } else { + switch (zpr) { + case 0x0: + if (msr_pr) { + ret = -2; + ctx->prot = 0; + break; + } + /* No break here */ + case 0x1: + case 0x2: + /* Check from TLB entry */ + /* Check write protection bit */ + if (rw && !(tlb->prot & PAGE_WRITE)) { + ret = -2; + } else { + ret = 2; + if (tlb->prot & PAGE_WRITE) + ctx->prot = PAGE_READ | PAGE_WRITE; + else + ctx->prot = PAGE_READ; + } + break; + case 0x3: + /* All accesses granted */ + ret = 2; + ctx->prot = PAGE_READ | PAGE_WRITE; + break; + } + } + if (ret >= 0) { + ctx->raddr = raddr; + if (loglevel) { + fprintf(logfile, "%s: access granted " ADDRX " => " REGX + " %d\n", __func__, address, ctx->raddr, ctx->prot); + } + return i; + } + } + + return ret; +} + static int check_physical (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw) { @@ -682,13 +789,26 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, /* No address translation */ ret = check_physical(env, ctx, eaddr, rw); } else { - /* Try to find a BAT */ - ret = -1; - if (check_BATs) - ret = get_bat(env, ctx, eaddr, rw, access_type); - if (ret < 0) { - /* We didn't match any BAT entry */ - ret = get_segment(env, ctx, eaddr, rw, access_type); + switch (PPC_MMU(env)) { + case PPC_FLAGS_MMU_32B: + case PPC_FLAGS_MMU_SOFT_6xx: + /* Try to find a BAT */ + ret = -1; + if (check_BATs) + ret = get_bat(env, ctx, eaddr, rw, access_type); + if (ret < 0) { + /* We didn't match any BAT entry */ + ret = get_segment(env, ctx, eaddr, rw, access_type); + } + break; + case PPC_FLAGS_MMU_SOFT_4xx: + ret = mmu4xx_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; + default: + /* XXX: TODO */ + cpu_abort(env, "MMU model not implemented\n"); + return -1; } } #if 0 @@ -753,7 +873,10 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, error_code = 1 << 18; goto tlb_miss; } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { - /* XXX: TODO */ + exception = EXCP_40x_ITLBMISS; + error_code = 0; + env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_ESR] = 0x00000000; } else { error_code = 0x40000000; } @@ -799,7 +922,13 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, /* Do not alter DAR nor DSISR */ goto out; } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { - /* XXX: TODO */ + exception = EXCP_40x_DTLBMISS; + error_code = 0; + env->spr[SPR_40x_DEAR] = address; + if (rw) + env->spr[SPR_40x_ESR] = 0x00800000; + else + env->spr[SPR_40x_ESR] = 0x00000000; } else { error_code = 0x40000000; } @@ -1518,9 +1647,7 @@ void do_interrupt (CPUState *env) switch (PPC_EXCP(env)) { case PPC_FLAGS_EXCP_40x: /* DTLBMISS on 4xx */ - /* XXX: TODO */ - cpu_abort(env, - "40x DTLBMISS exception is not implemented yet !\n"); + msr &= ~0xFFFF0000; goto store_next; case PPC_FLAGS_EXCP_602: case PPC_FLAGS_EXCP_603: @@ -1538,9 +1665,7 @@ void do_interrupt (CPUState *env) switch (PPC_EXCP(env)) { case PPC_FLAGS_EXCP_40x: /* ITLBMISS on 4xx */ - /* XXX: TODO */ - cpu_abort(env, - "40x ITLBMISS exception is not implemented yet !\n"); + msr &= ~0xFFFF0000; goto store_next; case PPC_FLAGS_EXCP_602: case PPC_FLAGS_EXCP_603: diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index c21a38aea4..e1fff7f497 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -2365,65 +2365,139 @@ void do_load_6xx_tlb (int is_code) way, is_code, CMP, RPN); } +static target_ulong booke_tlb_to_page_size (int size) +{ + return 1024 << (2 * size); +} + +static int booke_page_size_to_tlb (target_ulong page_size) +{ + int size; + + switch (page_size) { + case 0x00000400UL: + size = 0x0; + break; + case 0x00001000UL: + size = 0x1; + break; + case 0x00004000UL: + size = 0x2; + break; + case 0x00010000UL: + size = 0x3; + break; + case 0x00040000UL: + size = 0x4; + break; + case 0x00100000UL: + size = 0x5; + break; + case 0x00400000UL: + size = 0x6; + break; + case 0x01000000UL: + size = 0x7; + break; + case 0x04000000UL: + size = 0x8; + break; + case 0x10000000UL: + size = 0x9; + break; + case 0x40000000UL: + size = 0xA; + break; +#if defined (TARGET_PPC64) + case 0x000100000000ULL: + size = 0xB; + break; + case 0x000400000000ULL: + size = 0xC; + break; + case 0x001000000000ULL: + size = 0xD; + break; + case 0x004000000000ULL: + size = 0xE; + break; + case 0x010000000000ULL: + size = 0xF; + break; +#endif + default: + size = -1; + break; + } + + return size; +} + /* Helpers for 4xx TLB management */ void do_4xx_tlbia (void) { -#if 0 - ppc_tlb_t *tlb; - target_ulong page, end; + ppcemb_tlb_t *tlb; int i; for (i = 0; i < 64; i++) { - tlb = &env->tlb[i]; + tlb = &env->tlb[i].tlbe; if (tlb->prot & PAGE_VALID) { +#if 0 end = tlb->EPN + tlb->size; for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) tlb_flush_page(env, page); +#endif tlb->prot &= ~PAGE_VALID; } } -#endif + tlb_flush(env, 1); } void do_4xx_tlbre_lo (void) { -#if 0 - ppc_tlb_t *tlb; + ppcemb_tlb_t *tlb; + int size; T0 &= 0x3F; - tlb = &env->tlb[T0]; - T0 = tlb->stor[0]; - env->spr[SPR_40x_PID] = tlb->pid; -#endif + tlb = &env->tlb[T0].tlbe; + T0 = tlb->EPN; + if (tlb->prot & PAGE_VALID) + T0 |= 0x400; + size = booke_page_size_to_tlb(tlb->size); + if (size < 0 || size > 0x7) + size = 1; + T0 |= size << 7; + env->spr[SPR_40x_PID] = tlb->PID; } void do_4xx_tlbre_hi (void) { -#if 0 - ppc_tlb_t *tlb; + ppcemb_tlb_t *tlb; T0 &= 0x3F; - tlb = &env->tlb[T0]; - T0 = tlb->stor[1]; -#endif + tlb = &env->tlb[T0].tlbe; + T0 = tlb->RPN; + if (tlb->prot & PAGE_EXEC) + T0 |= 0x200; + if (tlb->prot & PAGE_WRITE) + T0 |= 0x100; } static int tlb_4xx_search (target_ulong virtual) { -#if 0 - ppc_tlb_t *tlb; + ppcemb_tlb_t *tlb; target_ulong base, mask; int i, ret; /* Default return value is no match */ ret = -1; for (i = 0; i < 64; i++) { - tlb = &env->tlb[i]; + tlb = &env->tlb[i].tlbe; /* Check TLB validity */ if (!(tlb->prot & PAGE_VALID)) continue; /* Check TLB PID vs current PID */ - if (tlb->pid != 0 && tlb->pid != env->spr[SPR_40x_PID]) + if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID]) continue; /* Check TLB address vs virtual address */ base = tlb->EPN; @@ -2435,9 +2509,6 @@ static int tlb_4xx_search (target_ulong virtual) } return ret; -#else - return -1; -#endif } void do_4xx_tlbsx (void) @@ -2457,47 +2528,44 @@ void do_4xx_tlbsx_ (void) void do_4xx_tlbwe_lo (void) { -#if 0 - ppc_tlb_t *tlb; + ppcemb_tlb_t *tlb; target_ulong page, end; T0 &= 0x3F; - tlb = &env->tlb[T0]; + tlb = &env->tlb[T0].tlbe; /* Invalidate previous TLB (if it's valid) */ if (tlb->prot & PAGE_VALID) { end = tlb->EPN + tlb->size; for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) tlb_flush_page(env, page); } - tlb->size = 1024 << (2 * ((T1 >> 7) & 0x7)); + tlb->size = booke_tlb_to_page_size((T1 >> 7) & 0x7); tlb->EPN = (T1 & 0xFFFFFC00) & ~(tlb->size - 1); if (T1 & 0x400) tlb->prot |= PAGE_VALID; else tlb->prot &= ~PAGE_VALID; - tlb->pid = env->spr[SPR_BOOKE_PID]; /* PID */ + tlb->PID = env->spr[SPR_BOOKE_PID]; /* PID */ + tlb->attr = T1 & 0xFF; /* Invalidate new TLB (if valid) */ if (tlb->prot & PAGE_VALID) { end = tlb->EPN + tlb->size; for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) tlb_flush_page(env, page); } -#endif } void do_4xx_tlbwe_hi (void) { -#if 0 - ppc_tlb_t *tlb; + ppcemb_tlb_t *tlb; T0 &= 0x3F; - tlb = &env->tlb[T0]; + tlb = &env->tlb[T0].tlbe; tlb->RPN = T1 & 0xFFFFFC00; tlb->prot = PAGE_READ; if (T1 & 0x200) tlb->prot |= PAGE_EXEC; if (T1 & 0x100) tlb->prot |= PAGE_WRITE; -#endif } #endif /* !CONFIG_USER_ONLY */