[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 5/6] PPC: Implement e500 (FSL) MMU
From: |
Alexander Graf |
Subject: |
[Qemu-devel] [PATCH 5/6] PPC: Implement e500 (FSL) MMU |
Date: |
Mon, 2 May 2011 17:03:21 +0200 |
Most of the code to support e500 style MMUs is already in place, but
we're missing on some of the special TLB0-TLB1 handling code and slightly
different TLB modification.
This patch adds support for the FSL style MMU.
Signed-off-by: Alexander Graf <address@hidden>
---
v1 -> v2:
- fix linux-user build
- optimize tlb invalidate & search
---
target-ppc/cpu.h | 217 ++++++++++++++++++++++-
target-ppc/helper.c | 233 +++++++++++++++++++-----
target-ppc/helper.h | 5 +
target-ppc/op_helper.c | 418 +++++++++++++++++++++++++++++++++++++++++++
target-ppc/translate.c | 91 +++++++++-
target-ppc/translate_init.c | 127 +++++++++-----
6 files changed, 994 insertions(+), 97 deletions(-)
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index b8d9049..785ae53 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -608,6 +608,215 @@ enum {
#define vscr_sat (((env->vscr) >> VSCR_SAT) & 0x1)
/*****************************************************************************/
+/* BookE e500 MMU registers */
+
+#define MAS0_NV_SHIFT 0
+#define MAS0_NV_MASK (0xfff << MAS0_NV_SHIFT)
+
+#define MAS0_WQ_SHIFT 12
+#define MAS0_WQ_MASK (3 << MAS0_WQ_SHIFT)
+/* Write TLB entry regardless of reservation */
+#define MAS0_WQ_ALWAYS (0 << MAS0_WQ_SHIFT)
+/* Write TLB entry only already in use */
+#define MAS0_WQ_COND (1 << MAS0_WQ_SHIFT)
+/* Clear TLB entry */
+#define MAS0_WQ_CLR_RSRV (2 << MAS0_WQ_SHIFT)
+
+#define MAS0_HES_SHIFT 14
+#define MAS0_HES (1 << MAS0_HES_SHIFT)
+
+#define MAS0_ESEL_SHIFT 16
+#define MAS0_ESEL_MASK (0xfff << MAS0_ESEL_SHIFT)
+
+#define MAS0_TLBSEL_SHIFT 28
+#define MAS0_TLBSEL_MASK (3 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB0 (0 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB1 (1 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB2 (2 << MAS0_TLBSEL_SHIFT)
+#define MAS0_TLBSEL_TLB3 (3 << MAS0_TLBSEL_SHIFT)
+
+#define MAS0_ATSEL_SHIFT 31
+#define MAS0_ATSEL (1 << MAS0_ATSEL_SHIFT)
+#define MAS0_ATSEL_TLB 0
+#define MAS0_ATSEL_LRAT MAS0_ATSEL
+
+#define MAS1_TSIZE_SHIFT 8
+#define MAS1_TSIZE_MASK (0xf << MAS1_TSIZE_SHIFT)
+
+#define MAS1_TS_SHIFT 12
+#define MAS1_TS (1 << MAS1_TS_SHIFT)
+
+#define MAS1_IND_SHIFT 13
+#define MAS1_IND (1 << MAS1_IND_SHIFT)
+
+#define MAS1_TID_SHIFT 16
+#define MAS1_TID_MASK (0x3fff << MAS1_TID_SHIFT)
+
+#define MAS1_IPROT_SHIFT 30
+#define MAS1_IPROT (1 << MAS1_IPROT_SHIFT)
+
+#define MAS1_VALID_SHIFT 31
+#define MAS1_VALID 0x80000000
+
+#define MAS2_EPN_SHIFT 12
+#define MAS2_EPN_MASK (0xfffff << MAS2_EPN_SHIFT)
+
+#define MAS2_ACM_SHIFT 6
+#define MAS2_ACM (1 << MAS2_ACM_SHIFT)
+
+#define MAS2_VLE_SHIFT 5
+#define MAS2_VLE (1 << MAS2_VLE_SHIFT)
+
+#define MAS2_W_SHIFT 4
+#define MAS2_W (1 << MAS2_W_SHIFT)
+
+#define MAS2_I_SHIFT 3
+#define MAS2_I (1 << MAS2_I_SHIFT)
+
+#define MAS2_M_SHIFT 2
+#define MAS2_M (1 << MAS2_M_SHIFT)
+
+#define MAS2_G_SHIFT 1
+#define MAS2_G (1 << MAS2_G_SHIFT)
+
+#define MAS2_E_SHIFT 0
+#define MAS2_E (1 << MAS2_E_SHIFT)
+
+#define MAS3_RPN_SHIFT 12
+#define MAS3_RPN_MASK (0xfffff << MAS3_RPN_SHIFT)
+
+#define MAS3_U0 0x00000200
+#define MAS3_U1 0x00000100
+#define MAS3_U2 0x00000080
+#define MAS3_U3 0x00000040
+#define MAS3_UX 0x00000020
+#define MAS3_SX 0x00000010
+#define MAS3_UW 0x00000008
+#define MAS3_SW 0x00000004
+#define MAS3_UR 0x00000002
+#define MAS3_SR 0x00000001
+#define MAS3_SPSIZE_SHIFT 1
+#define MAS3_SPSIZE_MASK (0x3e << MAS3_SPSIZE_SHIFT)
+
+#define MAS4_TLBSELD_SHIFT MAS0_TLBSEL_SHIFT
+#define MAS4_TLBSELD_MASK MAS0_TLBSEL_MASK
+#define MAS4_TIDSELD_MASK 0x00030000
+#define MAS4_TIDSELD_PID0 0x00000000
+#define MAS4_TIDSELD_PID1 0x00010000
+#define MAS4_TIDSELD_PID2 0x00020000
+#define MAS4_TIDSELD_PIDZ 0x00030000
+#define MAS4_INDD 0x00008000 /* Default IND */
+#define MAS4_TSIZED_SHIFT MAS1_TSIZE_SHIFT
+#define MAS4_TSIZED_MASK MAS1_TSIZE_MASK
+#define MAS4_ACMD 0x00000040
+#define MAS4_VLED 0x00000020
+#define MAS4_WD 0x00000010
+#define MAS4_ID 0x00000008
+#define MAS4_MD 0x00000004
+#define MAS4_GD 0x00000002
+#define MAS4_ED 0x00000001
+#define MAS4_WIMGED_MASK 0x0000001f /* Default WIMGE */
+#define MAS4_WIMGED_SHIFT 0
+
+#define MAS5_SGS 0x80000000
+#define MAS5_SLPID_MASK 0x00000fff
+
+#define MAS6_SPID0 0x3fff0000
+#define MAS6_SPID1 0x00007ffe
+#define MAS6_ISIZE(x) MAS1_TSIZE(x)
+#define MAS6_SAS 0x00000001
+#define MAS6_SPID MAS6_SPID0
+#define MAS6_SIND 0x00000002 /* Indirect page */
+#define MAS6_SIND_SHIFT 1
+#define MAS6_SPID_MASK 0x3fff0000
+#define MAS6_SPID_SHIFT 16
+#define MAS6_ISIZE_MASK 0x00000f80
+#define MAS6_ISIZE_SHIFT 7
+
+#define MAS7_RPN 0xffffffff
+
+#define MAS8_TGS 0x80000000
+#define MAS8_VF 0x40000000
+#define MAS8_TLBPID 0x00000fff
+
+/* Bit definitions for MMUCFG */
+#define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */
+#define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */
+#define MMUCFG_MAVN_V2 0x00000001 /* v2.0 */
+#define MMUCFG_NTLBS 0x0000000c /* Number of TLBs */
+#define MMUCFG_PIDSIZE 0x000007c0 /* PID Reg Size */
+#define MMUCFG_TWC 0x00008000 /* TLB Write Conditional (v2.0) */
+#define MMUCFG_LRAT 0x00010000 /* LRAT Supported (v2.0) */
+#define MMUCFG_RASIZE 0x00fe0000 /* Real Addr Size */
+#define MMUCFG_LPIDSIZE 0x0f000000 /* LPID Reg Size */
+
+/* Bit definitions for MMUCSR0 */
+#define MMUCSR0_TLB1FI 0x00000002 /* TLB1 Flash invalidate */
+#define MMUCSR0_TLB0FI 0x00000004 /* TLB0 Flash invalidate */
+#define MMUCSR0_TLB2FI 0x00000040 /* TLB2 Flash invalidate */
+#define MMUCSR0_TLB3FI 0x00000020 /* TLB3 Flash invalidate */
+#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
+ MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
+#define MMUCSR0_TLB0PS 0x00000780 /* TLB0 Page Size */
+#define MMUCSR0_TLB1PS 0x00007800 /* TLB1 Page Size */
+#define MMUCSR0_TLB2PS 0x00078000 /* TLB2 Page Size */
+#define MMUCSR0_TLB3PS 0x00780000 /* TLB3 Page Size */
+
+/* TLBnCFG encoding */
+#define TLBnCFG_N_ENTRY 0x00000fff /* number of entries */
+#define TLBnCFG_HES 0x00002000 /* HW select supported */
+#define TLBnCFG_AVAIL 0x00004000 /* variable page size */
+#define TLBnCFG_IPROT 0x00008000 /* IPROT supported */
+#define TLBnCFG_GTWE 0x00010000 /* Guest can write */
+#define TLBnCFG_IND 0x00020000 /* IND entries supported */
+#define TLBnCFG_PT 0x00040000 /* Can load from page table */
+#define TLBnCFG_MINSIZE 0x00f00000 /* Minimum Page Size (v1.0) */
+#define TLBnCFG_MINSIZE_SHIFT 20
+#define TLBnCFG_MAXSIZE 0x000f0000 /* Maximum Page Size (v1.0) */
+#define TLBnCFG_MAXSIZE_SHIFT 16
+#define TLBnCFG_ASSOC 0xff000000 /* Associativity */
+#define TLBnCFG_ASSOC_SHIFT 24
+
+/* TLBnPS encoding */
+#define TLBnPS_4K 0x00000004
+#define TLBnPS_8K 0x00000008
+#define TLBnPS_16K 0x00000010
+#define TLBnPS_32K 0x00000020
+#define TLBnPS_64K 0x00000040
+#define TLBnPS_128K 0x00000080
+#define TLBnPS_256K 0x00000100
+#define TLBnPS_512K 0x00000200
+#define TLBnPS_1M 0x00000400
+#define TLBnPS_2M 0x00000800
+#define TLBnPS_4M 0x00001000
+#define TLBnPS_8M 0x00002000
+#define TLBnPS_16M 0x00004000
+#define TLBnPS_32M 0x00008000
+#define TLBnPS_64M 0x00010000
+#define TLBnPS_128M 0x00020000
+#define TLBnPS_256M 0x00040000
+#define TLBnPS_512M 0x00080000
+#define TLBnPS_1G 0x00100000
+#define TLBnPS_2G 0x00200000
+#define TLBnPS_4G 0x00400000
+#define TLBnPS_8G 0x00800000
+#define TLBnPS_16G 0x01000000
+#define TLBnPS_32G 0x02000000
+#define TLBnPS_64G 0x04000000
+#define TLBnPS_128G 0x08000000
+#define TLBnPS_256G 0x10000000
+
+/* tlbilx action encoding */
+#define TLBILX_T_ALL 0
+#define TLBILX_T_TID 1
+#define TLBILX_T_FULLMATCH 3
+#define TLBILX_T_CLASS0 4
+#define TLBILX_T_CLASS1 5
+#define TLBILX_T_CLASS2 6
+#define TLBILX_T_CLASS3 7
+
+
+/*****************************************************************************/
/* The whole PowerPC CPU context */
#define NB_MMU_MODES 3
@@ -678,8 +887,9 @@ struct CPUPPCState {
int nb_BATs;
target_ulong DBAT[2][8];
target_ulong IBAT[2][8];
- /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
+ /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */
int nb_tlb; /* Total number of TLB */
+ int nb_tlbs[4]; /* Number of respective TLB entries (e500) */
int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */
int nb_ways; /* Number of ways in the TLB set */
int last_way; /* Last used way used to allocate TLB in a LRU way */
@@ -1546,6 +1756,11 @@ enum {
PPC_DCRUX = 0x4000000000000000ULL,
/* popcntw and popcntd instructions */
PPC_POPCNTWD = 0x8000000000000000ULL,
+
+ /* extended type values */
+
+ /* BookE FSL (e500) PowerPC specification */
+ PPC2_BOOKE_FSL = 0x0000000000000001ULL,
};
/*****************************************************************************/
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 5e4030b..261c1a9 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -1153,48 +1153,144 @@ void store_40x_sler (CPUPPCState *env, uint32_t val)
env->spr[SPR_405_SLER] = val;
}
+static inline int mmubooke_check_tlb (CPUState *env, ppcemb_tlb_t *tlb,
+ target_phys_addr_t *raddr, int *prot,
+ target_ulong address, int rw,
+ int access_type, int i)
+{
+ int ret, _prot;
+
+ if (ppcemb_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID],
+ !env->nb_pids, i) >= 0) {
+ goto found_tlb;
+ }
+
+ if ((env->nb_pids > 1) && env->spr[SPR_BOOKE_PID1] &&
+ ppcemb_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID1], 0, i) >= 0) {
+ goto found_tlb;
+ }
+
+ if ((env->nb_pids > 2) && env->spr[SPR_BOOKE_PID2] &&
+ ppcemb_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID2], 0, i) >= 0) {
+ goto found_tlb;
+ }
+
+ return -1;
+
+found_tlb:
+
+ if (msr_pr != 0) {
+ _prot = tlb->prot & 0xF;
+ } else {
+ _prot = (tlb->prot >> 4) & 0xF;
+ }
+
+ /* Check the address space */
+ if (access_type == ACCESS_CODE) {
+ if (msr_ir != (tlb->attr & 1)) {
+ return -1;
+ }
+
+ *prot = _prot;
+ if (_prot & PAGE_EXEC) {
+ return 0;
+ }
+
+ ret = -3;
+ } else {
+ if (msr_dr != (tlb->attr & 1)) {
+ return -1;
+ }
+
+ *prot = _prot;
+ if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) {
+ return 0;
+ }
+
+ ret = -2;
+ }
+
+ return ret;
+}
+
static 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;
+ int i, ret;
ret = -1;
raddr = (target_phys_addr_t)-1ULL;
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 != 0)
- prot = tlb->prot & 0xF;
- else
- prot = (tlb->prot >> 4) & 0xF;
- /* Check the address space */
- if (access_type == ACCESS_CODE) {
- if (msr_ir != (tlb->attr & 1))
- continue;
- ctx->prot = prot;
- if (prot & PAGE_EXEC) {
- ret = 0;
- break;
- }
- ret = -3;
- } else {
- if (msr_dr != (tlb->attr & 1))
- continue;
- ctx->prot = prot;
- if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) {
- ret = 0;
- break;
- }
- ret = -2;
+ ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+ access_type, i);
+ if (!ret) {
+ break;
+ }
+ }
+
+ if (ret >= 0) {
+ ctx->raddr = raddr;
+ LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, ctx->raddr, ctx->prot,
+ ret);
+ } else {
+ LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, raddr, ctx->prot, ret);
+ }
+
+ return ret;
+}
+
+static int mmue500_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, ret;
+ uint32_t ea = (address >> MAS2_EPN_SHIFT) & 0x7f;
+
+ ret = -1;
+ raddr = (target_phys_addr_t)-1ULL;
+
+ /* check TLB1 - hits often because the kernel is here */
+ for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+ access_type, i);
+ if (!ret) {
+ goto found_tlb;
}
}
- if (ret >= 0)
+
+ /* check possible TLB0 entries */
+ for (i = 0; i < env->nb_ways; i++) {
+ tlb = &env->tlb[ea | (i << 7)].tlbe;
+ ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
+ access_type, i);
+ if (!ret) {
+ goto found_tlb;
+ }
+ }
+
+found_tlb:
+
+ if (ret >= 0) {
ctx->raddr = raddr;
+ LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, ctx->raddr, ctx->prot,
+ ret);
+ } else {
+ LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, raddr, ctx->prot, ret);
+ }
return ret;
}
@@ -1255,8 +1351,7 @@ static inline int check_physical(CPUState *env, mmu_ctx_t
*ctx,
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
case POWERPC_MMU_BOOKE_FSL:
- /* XXX: TODO */
- cpu_abort(env, "BookE FSL MMU model not implemented\n");
+ cpu_abort(env, "BookE FSL MMU model doesn't have physical real
mode\n");
break;
default:
cpu_abort(env, "Unknown or invalid MMU model\n");
@@ -1281,6 +1376,9 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx,
target_ulong eaddr,
IS and DS bits only affect the address space. */
ret = mmubooke_get_physical_address(env, ctx, eaddr,
rw, access_type);
+ } else if (env->mmu_model == POWERPC_MMU_BOOKE_FSL) {
+ ret = mmue500_get_physical_address(env, ctx, eaddr, rw,
+ access_type);
} else {
/* No address translation. */
ret = check_physical(env, ctx, eaddr, rw);
@@ -1314,14 +1412,14 @@ int get_physical_address (CPUState *env, mmu_ctx_t
*ctx, target_ulong eaddr,
ret = mmubooke_get_physical_address(env, ctx, eaddr,
rw, access_type);
break;
+ case POWERPC_MMU_BOOKE_FSL:
+ ret = mmue500_get_physical_address(env, ctx, eaddr, rw,
+ access_type);
+ break;
case POWERPC_MMU_MPC8xx:
/* XXX: TODO */
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
- case POWERPC_MMU_BOOKE_FSL:
- /* XXX: TODO */
- cpu_abort(env, "BookE FSL MMU model not implemented\n");
- return -1;
case POWERPC_MMU_REAL:
cpu_abort(env, "PowerPC in real mode do not do any translation\n");
return -1;
@@ -1348,6 +1446,44 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState
*env, target_ulong addr)
return ctx.raddr & TARGET_PAGE_MASK;
}
+static void e500_update_mas_tlb_miss(CPUState *env, target_ulong address,
+ int rw)
+{
+ env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
+ env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
+ env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
+ env->spr[SPR_BOOKE_MAS6] = 0;
+
+ /* AS */
+ if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) {
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+ env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS;
+ }
+
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID;
+ env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK;
+
+ switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) {
+ case MAS4_TIDSELD_PID0:
+ env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT;
+ break;
+ case MAS4_TIDSELD_PID1:
+ env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT;
+ break;
+ case MAS4_TIDSELD_PID2:
+ env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT;
+ break;
+ }
+
+ env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16;
+
+ /* next victim logic */
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
+ env->last_way++;
+ env->last_way &= 3;
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
/* Perform address translation */
int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int mmu_idx, int is_softmmu)
@@ -1403,15 +1539,14 @@ int cpu_ppc_handle_mmu_fault (CPUState *env,
target_ulong address, int rw,
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x40000000;
break;
+ case POWERPC_MMU_BOOKE_FSL:
+ e500_update_mas_tlb_miss(env, address, rw);
+ /* fall through */
case POWERPC_MMU_BOOKE:
env->exception_index = POWERPC_EXCP_ITLB;
env->error_code = 0;
env->spr[SPR_BOOKE_DEAR] = address;
return -1;
- case POWERPC_MMU_BOOKE_FSL:
- /* XXX: TODO */
- cpu_abort(env, "BookE FSL MMU model is not implemented\n");
- return -1;
case POWERPC_MMU_MPC8xx:
/* XXX: TODO */
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
@@ -1432,7 +1567,8 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong
address, int rw,
break;
case -3:
/* No execute protection violation */
- if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+ (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
env->spr[SPR_BOOKE_ESR] = 0x00000000;
}
env->exception_index = POWERPC_EXCP_ISI;
@@ -1522,16 +1658,15 @@ int cpu_ppc_handle_mmu_fault (CPUState *env,
target_ulong address, int rw,
/* XXX: TODO */
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
+ case POWERPC_MMU_BOOKE_FSL:
+ e500_update_mas_tlb_miss(env, address, rw);
+ /* fall through */
case POWERPC_MMU_BOOKE:
env->exception_index = POWERPC_EXCP_DTLB;
env->error_code = 0;
env->spr[SPR_BOOKE_DEAR] = address;
env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
return -1;
- case POWERPC_MMU_BOOKE_FSL:
- /* XXX: TODO */
- cpu_abort(env, "BookE FSL MMU model is not implemented\n");
- return -1;
case POWERPC_MMU_REAL:
cpu_abort(env, "PowerPC in real mode should never raise "
"any MMU exceptions\n");
@@ -1551,7 +1686,8 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong
address, int rw,
if (rw) {
env->spr[SPR_40x_ESR] |= 0x00800000;
}
- } else if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ } else if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+ (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
env->spr[SPR_BOOKE_DEAR] = address;
env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
} else {
@@ -1820,12 +1956,8 @@ void ppc_tlb_invalidate_all (CPUPPCState *env)
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
case POWERPC_MMU_BOOKE:
- tlb_flush(env, 1);
- break;
case POWERPC_MMU_BOOKE_FSL:
- /* XXX: TODO */
- if (!kvm_enabled())
- cpu_abort(env, "BookE MMU model is not implemented\n");
+ tlb_flush(env, 1);
break;
case POWERPC_MMU_32B:
case POWERPC_MMU_601:
@@ -2589,7 +2721,8 @@ static inline void powerpc_excp(CPUState *env, int
excp_model, int excp)
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
- if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+ (env->mmu_model == POWERPC_MMU_BOOKE_FSL)) {
/* XXX: The BookE changes address space when switching modes,
we should probably implement that as different MMU indexes,
but for the moment we do it the slow way and flush all. */
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 7c02be9..7839aad 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -334,6 +334,11 @@ DEF_HELPER_1(4xx_tlbsx, tl, tl)
DEF_HELPER_2(440_tlbre, tl, i32, tl)
DEF_HELPER_3(440_tlbwe, void, i32, tl, tl)
DEF_HELPER_1(440_tlbsx, tl, tl)
+DEF_HELPER_0(e500_tlbre, void)
+DEF_HELPER_0(e500_tlbwe, void)
+DEF_HELPER_2(e500_tlbsx, void, tl, tl)
+DEF_HELPER_1(e500_tlbivax, void, tl)
+DEF_HELPER_1(e500_tlbflush, void, i32)
DEF_HELPER_1(6xx_tlbd, void, tl)
DEF_HELPER_1(6xx_tlbi, void, tl)
DEF_HELPER_1(74xx_tlbd, void, tl)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index d5db484..3c0b061 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -4206,4 +4206,422 @@ target_ulong helper_440_tlbsx (target_ulong address)
return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
}
+/* PowerPC e500 TLB management */
+
+static inline int e500_tlb_num(CPUState *env, ppcemb_tlb_t *tlb)
+{
+ ulong tlbel = (ulong)tlb;
+ ulong tlbl = (ulong)env->tlb;
+
+ return (tlbel - tlbl) / sizeof(env->tlb[0]);
+}
+
+static inline ppcemb_tlb_t *e500_cur_tlb(CPUState *env)
+{
+ uint32_t tlbncfg = 0;
+ int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT;
+ int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK) >> MAS2_EPN_SHIFT;
+ int tlb;
+ int r;
+
+ tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
+ tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb];
+
+ if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) {
+ cpu_abort(env, "we don't support HES yet\n");
+ }
+
+ if (tlb == 1) {
+ r = env->nb_tlbs[0] + (esel % env->nb_tlbs[1]);
+ } else {
+ esel &= env->nb_ways - 1;
+ esel <<= 7;
+ ea &= 0x7f;
+ r = (esel | ea) % env->nb_tlbs[0];
+ }
+
+ return &env->tlb[r].tlbe;
+}
+
+static const target_phys_addr_t e500_tlb_sizes[] = {
+ 0,
+ 4 * 1024,
+ 16 * 1024,
+ 64 * 1024,
+ 256 * 1024,
+ 1024 * 1024,
+ 4 * 1024 * 1024,
+ 16 * 1024 * 1024,
+ 64 * 1024 * 1024,
+ 256 * 1024 * 1024,
+ 1024 * 1024 * 1024,
+ (target_phys_addr_t)(4ULL * 1024ULL * 1024ULL * 1024ULL),
+};
+
+static inline target_phys_addr_t e500_tlb_to_page_size(int size)
+{
+ target_phys_addr_t r;
+
+ if (size > 11) {
+ /* should not happen */
+ r = 1024 * 1024 * 1024;
+ } else {
+ r = e500_tlb_sizes[size];
+ }
+
+ return r;
+}
+
+static inline target_phys_addr_t e500_page_size_to_tlb(uint64_t size)
+{
+ int i;
+ target_phys_addr_t r = 0;
+
+ for (i = 0; i < ARRAY_SIZE(e500_tlb_sizes); i++) {
+ if (e500_tlb_sizes[i] == size) {
+ r = i;
+ break;
+ }
+ }
+
+ return r;
+}
+
+void helper_e500_tlbwe(void)
+{
+ uint32_t tlbncfg;
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t rpn;
+
+ switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) {
+ case MAS0_WQ_ALWAYS:
+ /* good to go, write that entry */
+ break;
+ case MAS0_WQ_COND:
+ /* XXX check if reserved */
+ if (0) {
+ return;
+ }
+ break;
+ case MAS0_WQ_CLR_RSRV:
+ /* XXX clear entry */
+ return;
+ default:
+ /* no idea what to do */
+ return;
+ }
+
+ if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) &&
+ !msr_gs) {
+ /* XXX we don't support direct LRAT setting yet */
+ fprintf(stderr, "cpu: don't support LRAT setting yet\n");
+ return;
+ }
+
+ switch (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) {
+ case MAS0_TLBSEL_TLB0:
+ tlbncfg = env->spr[SPR_BOOKE_TLB0CFG];
+ break;
+ case MAS0_TLBSEL_TLB1:
+ tlbncfg = env->spr[SPR_BOOKE_TLB1CFG];
+ break;
+ case MAS0_TLBSEL_TLB2:
+ tlbncfg = env->spr[SPR_BOOKE_TLB2CFG];
+ break;
+ case MAS0_TLBSEL_TLB3:
+ tlbncfg = env->spr[SPR_BOOKE_TLB3CFG];
+ break;
+ }
+
+ tlb = e500_cur_tlb(env);
+
+ /* XXX check for IPROT */
+
+ if (msr_gs) {
+ cpu_abort(env, "missing HV implementation\n");
+ } else {
+ rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
+ (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
+ }
+ tlb->RPN = rpn;
+
+ tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
+ tlb->size = e500_tlb_to_page_size((env->spr[SPR_BOOKE_MAS1]
+ & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT);
+ tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
+ tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W |
+ MAS2_I | MAS2_M | MAS2_G | MAS2_E)
+ << 1;
+ tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT;
+ tlb->attr |= (env->spr[SPR_BOOKE_MAS3] &
+ ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8);
+ if (env->spr[SPR_BOOKE_MAS1] & MAS1_TS) {
+ tlb->attr |= 1;
+ }
+
+ tlb->prot = 0;
+
+ if (env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) {
+ tlb->prot |= PAGE_VALID;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UX) {
+ tlb->prot |= PAGE_EXEC;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SX) {
+ tlb->prot |= PAGE_EXEC << 4;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UW) {
+ tlb->prot |= PAGE_WRITE;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SW) {
+ tlb->prot |= PAGE_WRITE << 4;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UR) {
+ tlb->prot |= PAGE_READ;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SR) {
+ tlb->prot |= PAGE_READ << 4;
+ }
+
+ if (tlb->size == TARGET_PAGE_SIZE) {
+ tlb_flush_page(env, tlb->EPN);
+ } else {
+ tlb_flush(env, 1);
+ }
+}
+
+static inline void e500_tlb_to_mas(CPUState *env, ppcemb_tlb_t *tlb, int i)
+{
+ env->spr[SPR_BOOKE_MAS0] = 0;
+ env->spr[SPR_BOOKE_MAS1] = MAS1_VALID;
+ env->spr[SPR_BOOKE_MAS2] = 0;
+
+ if (i >= env->nb_tlbs[0]) {
+ env->spr[SPR_BOOKE_MAS0] |= MAS0_TLBSEL_TLB1;
+ env->spr[SPR_BOOKE_MAS0] |= (i - env->nb_tlbs[0]) << MAS0_ESEL_SHIFT;
+ } else {
+ env->spr[SPR_BOOKE_MAS0] |= MAS0_TLBSEL_TLB0;
+ env->spr[SPR_BOOKE_MAS0] |= (i >> 7) << MAS0_ESEL_SHIFT;
+ }
+
+ env->spr[SPR_BOOKE_MAS7] = (uint64_t)tlb->RPN >> 32;
+ env->spr[SPR_BOOKE_MAS3] = tlb->RPN;
+ env->spr[SPR_BOOKE_MAS1] |= tlb->PID << MAS1_TID_SHIFT;
+ env->spr[SPR_BOOKE_MAS1] |= e500_page_size_to_tlb(tlb->size)
+ << MAS1_TSIZE_SHIFT;
+ env->spr[SPR_BOOKE_MAS1] |= tlb->attr & MAS1_IPROT;
+ if (tlb->attr & 1) {
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+ }
+
+ env->spr[SPR_BOOKE_MAS2] = tlb->EPN;
+ env->spr[SPR_BOOKE_MAS2] |= (tlb->attr >> 1) &
+ (MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E);
+
+ if (tlb->prot & PAGE_EXEC) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UX;
+ }
+ if (tlb->prot & (PAGE_EXEC << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SX;
+ }
+ if (tlb->prot & PAGE_WRITE) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UW;
+ }
+ if (tlb->prot & (PAGE_WRITE << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SW;
+ }
+ if (tlb->prot & PAGE_READ) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UR;
+ }
+ if (tlb->prot & (PAGE_READ << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SR;
+ }
+
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+void helper_e500_tlbre(void)
+{
+ ppcemb_tlb_t *tlb = NULL;
+
+ tlb = e500_cur_tlb(env);
+ e500_tlb_to_mas(env, tlb, e500_tlb_num(env, tlb));
+}
+
+/* Generic TLB check function for embedded PowerPC implementations */
+static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
+ target_phys_addr_t *raddrp,
+ target_ulong address, uint32_t pid, int ext,
+ int i)
+{
+ target_ulong mask;
+
+ /* Check valid flag */
+ if (!(tlb->prot & PAGE_VALID)) {
+ return -1;
+ }
+ mask = ~(tlb->size - 1);
+
+ /* Check PID */
+ if (tlb->PID != 0 && tlb->PID != pid) {
+ return -1;
+ }
+ /* Check effective address */
+ if ((address & mask) != tlb->EPN) {
+ return -1;
+ }
+ *raddrp = (tlb->RPN & mask) | (address & ~mask);
+#if (TARGET_PHYS_ADDR_BITS >= 36)
+ if (ext) {
+ /* Extend the physical address to 36 bits */
+ *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32;
+ }
+#endif
+
+ return 0;
+}
+
+void helper_e500_tlbsx(target_ulong address_hi, target_ulong address_lo)
+{
+ ppcemb_tlb_t *tlb = NULL;
+ int i;
+ target_phys_addr_t raddr;
+ uint32_t spid, sas;
+ uint32_t ea = (address_lo >> MAS2_EPN_SHIFT) & 0x7f;
+
+ spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
+ sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
+
+ /* check possible TLB0 entries */
+ for (i = 0; i < env->nb_ways; i++) {
+ tlb = &env->tlb[ea | (i << 7)].tlbe;
+
+ if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
+ continue;
+ }
+
+ if (sas != (tlb->attr & MAS6_SAS)) {
+ continue;
+ }
+
+ e500_tlb_to_mas(env, tlb, ea | (i << 7));
+ return;
+ }
+
+ /* check TLB1 */
+ for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
+ tlb = &env->tlb[i].tlbe;
+
+ if (ppcemb_tlb_check(env, tlb, &raddr, address_lo, spid, 0, i) < 0) {
+ continue;
+ }
+
+ if (sas != (tlb->attr & MAS6_SAS)) {
+ continue;
+ }
+
+ e500_tlb_to_mas(env, tlb, i);
+ return;
+ }
+
+ /* no entry found, fill with defaults */
+ env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
+ env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
+ env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
+
+ if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) {
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+ }
+
+ env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16)
+ << MAS1_TID_SHIFT;
+
+ /* next victim logic */
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
+ env->last_way++;
+ env->last_way &= 3;
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+static inline void e500_invalidate_ea_tlb0(CPUState *env, uint32_t ea)
+{
+ int i;
+ ppcemb_tlb_t *tlb;
+
+ ea &= 0x7f;
+
+ /* check only possible TLB0 entries */
+ for (i = 0; i < env->nb_ways; i++) {
+ tlb = &env->tlb[ea | (i << 7)].tlbe;
+ if (tlb->EPN == ea) {
+ tlb->prot = 0;
+ }
+ }
+}
+
+static inline void e500_invalidate_ea_tlb1(CPUState *env, uint32_t ea)
+{
+ int i;
+
+ for (i = env->nb_tlbs[0]; i < (env->nb_tlbs[0] + env->nb_tlbs[1]); i++) {
+ ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+ if ((tlb->EPN == (ea & ~(tlb->size - 1))) &&
+ !(tlb->attr & MAS1_IPROT)) {
+ tlb->prot = 0;
+ }
+ }
+}
+
+void helper_e500_tlbivax(target_ulong address)
+{
+ uint32_t ea = address >> MAS2_EPN_SHIFT;
+
+ if (address & 0x4) {
+ /* flush all entries */
+ if (address & 0x8) {
+ /* flush all of TLB1 */
+ helper_e500_tlbflush(2);
+ } else {
+ /* flush all of TLB0 */
+ helper_e500_tlbflush(4);
+ }
+ return;
+ }
+
+ if (address & 0x8) {
+ /* flush TLB1 entries */
+ e500_invalidate_ea_tlb1(env, ea);
+ tlb_flush(env, 1);
+ } else {
+ /* flush TLB0 entries */
+ e500_invalidate_ea_tlb0(env, ea);
+ tlb_flush_page(env, address & MAS2_EPN_MASK);
+ }
+}
+
+void helper_e500_tlbflush(uint32_t type)
+{
+ int i;
+
+ if (type & 4) {
+ /* flush tlb0 */
+ for (i = 0; i < env->nb_tlbs[0]; i++) {
+ ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+ tlb->prot = 0;
+ }
+ }
+
+ if (type & 2) {
+ /* flush tlb1 */
+ for (i = env->nb_tlbs[0]; i < env->nb_tlb; i++) {
+ ppcemb_tlb_t *tlb = &env->tlb[i].tlbe;
+ if (!(tlb->attr & MAS1_IPROT)) {
+ tlb->prot = 0;
+ }
+ }
+ }
+
+ tlb_flush(env, 1);
+}
+
#endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 9076df0..629d019 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -5987,6 +5987,78 @@ static void gen_tlbwe_440(DisasContext *ctx)
#endif
}
+/* TLB management - PowerPC e500 implementation */
+
+/* tlbre */
+static void gen_tlbre_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+
+ gen_helper_e500_tlbre();
+#endif
+}
+
+/* tlbsx - tlbsx. */
+static void gen_tlbsx_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+
+ if (rA(ctx->opcode)) {
+ t0 = cpu_gpr[rD(ctx->opcode)];
+ } else {
+ t0 = tcg_const_tl(0);
+ }
+
+ gen_helper_e500_tlbsx(t0, cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbwe */
+static void gen_tlbwe_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_e500_tlbwe();
+#endif
+}
+
+static void gen_tlbivax_e500(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+
+ gen_helper_e500_tlbivax(t0);
+#endif
+}
+
+
/* wrtee */
static void gen_wrtee(DisasContext *ctx)
{
@@ -8433,7 +8505,7 @@ GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08,
0x03E00001, PPC_40x_ICBT),
GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
-GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
+GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE_FSL),
GEN_HANDLER(rfdi, 0x13, 0x07, 0x01, 0x03FF8001, PPC_RFDI),
GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_RFMCI),
GEN_HANDLER2(tlbre_40x, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_TLB),
@@ -8442,12 +8514,23 @@ GEN_HANDLER2(tlbwe_40x, "tlbwe", 0x1F, 0x12, 0x1E,
0x00000001, PPC_40x_TLB),
GEN_HANDLER2(tlbre_440, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE),
GEN_HANDLER2(tlbsx_440, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE),
GEN_HANDLER2(tlbwe_440, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE),
+GEN_HANDLER2_E(tlbre_e500, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001,
+ PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbsx_e500, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000,
+ PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbwe_e500, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001,
+ PPC_NONE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(tlbivax_e500, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001,
+ PPC_NONE, PPC2_BOOKE_FSL),
GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE),
GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE),
GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC),
-GEN_HANDLER(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, PPC_BOOKE),
-GEN_HANDLER(msync, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE),
-GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE),
+GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801,
+ PPC_BOOKE, PPC2_BOOKE_FSL),
+GEN_HANDLER_E(msync, 0x1F, 0x16, 0x12, 0x03FFF801,
+ PPC_BOOKE, PPC2_BOOKE_FSL),
+GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
+ PPC_BOOKE, PPC2_BOOKE_FSL),
GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index aec0e13..2d7afa2 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -1355,6 +1355,31 @@ static void gen_74xx_soft_tlb (CPUPPCState *env, int
nb_tlbs, int nb_ways)
#endif
}
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, cpu_gpr[gprn], ~256);
+ gen_store_spr(sprn, t0);
+ tcg_temp_free(t0);
+}
+
+static void spr_write_e500_mmucsr0 (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_const_i32(sprn);
+ gen_helper_e500_tlbflush(t0);
+ tcg_temp_free(t0);
+}
+
+static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
+{
+ gen_store_spr(sprn, cpu_gpr[gprn]);
+ /* switching context, so need to flush tlb */
+ gen_helper_tlbia();
+}
+#endif
+
static void gen_spr_usprgh (CPUPPCState *env)
{
spr_register(env, SPR_USPRG4, "USPRG4",
@@ -1494,7 +1519,7 @@ static void gen_spr_BookE (CPUPPCState *env, uint64_t
ivor_mask)
}
spr_register(env, SPR_BOOKE_PID, "PID",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_booke_pid,
0x00000000);
spr_register(env, SPR_BOOKE_TCR, "TCR",
SPR_NOACCESS, SPR_NOACCESS,
@@ -1536,8 +1561,19 @@ static void gen_spr_BookE (CPUPPCState *env, uint64_t
ivor_mask)
0x00000000);
}
+static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize,
+ uint32_t maxsize, uint32_t flags,
+ uint32_t nentries)
+{
+ return (assoc << TLBnCFG_ASSOC_SHIFT) |
+ (minsize << TLBnCFG_MINSIZE_SHIFT) |
+ (maxsize << TLBnCFG_MAXSIZE_SHIFT) |
+ flags | nentries;
+}
+
/* FSL storage control registers */
-static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
+static void gen_spr_BookE_FSL(CPUPPCState *env, uint32_t mas_mask,
+ uint32_t *tlbncfg)
{
#if !defined(CONFIG_USER_ONLY)
const char *mas_names[8] = {
@@ -1563,14 +1599,14 @@ static void gen_spr_BookE_FSL (CPUPPCState *env,
uint32_t mas_mask)
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_PID1, "PID1",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_booke_pid,
0x00000000);
}
if (env->nb_pids > 2) {
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_PID2, "PID2",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_booke_pid,
0x00000000);
}
/* XXX : not implemented */
@@ -1578,45 +1614,38 @@ static void gen_spr_BookE_FSL (CPUPPCState *env,
uint32_t mas_mask)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, SPR_NOACCESS,
0x00000000); /* TOFIX */
- /* XXX : not implemented */
- spr_register(env, SPR_MMUCSR0, "MMUCSR0",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000); /* TOFIX */
switch (env->nb_ways) {
case 4:
- /* XXX : not implemented */
spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, SPR_NOACCESS,
- 0x00000000); /* TOFIX */
+ tlbncfg[3]);
/* Fallthru */
case 3:
- /* XXX : not implemented */
spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, SPR_NOACCESS,
- 0x00000000); /* TOFIX */
+ tlbncfg[2]);
/* Fallthru */
case 2:
- /* XXX : not implemented */
spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, SPR_NOACCESS,
- 0x00000000); /* TOFIX */
+ tlbncfg[1]);
/* Fallthru */
case 1:
- /* XXX : not implemented */
spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, SPR_NOACCESS,
- 0x00000000); /* TOFIX */
+ tlbncfg[0]);
/* Fallthru */
case 0:
default:
break;
}
#endif
+
+ gen_spr_usprgh(env);
}
/* SPR specific to PowerPC 440 implementation */
@@ -4134,7 +4163,7 @@ static void init_proc_e200 (CPUPPCState *env)
&spr_read_spefscr, &spr_write_spefscr,
0x00000000);
/* Memory management */
- gen_spr_BookE_FSL(env, 0x0000005D);
+ gen_spr_BookE_FSL(env, 0x0000005D, NULL);
/* XXX : not implemented */
spr_register(env, SPR_HID0, "HID0",
SPR_NOACCESS, SPR_NOACCESS,
@@ -4205,6 +4234,11 @@ static void init_proc_e200 (CPUPPCState *env)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
spr_register(env, SPR_BOOKE_DSRR0, "DSRR0",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
@@ -4282,9 +4316,8 @@ static void init_proc_e300 (CPUPPCState *env)
PPC_WRTEE | PPC_RFDI | \
PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
- PPC_MEM_TLBSYNC | PPC_TLBIVAX | \
- PPC_BOOKE)
-#define POWERPC_INSNS2_e500v1 (PPC_NONE)
+ PPC_MEM_TLBSYNC | PPC_TLBIVAX)
+#define POWERPC_INSNS2_e500v1 (PPC2_BOOKE_FSL)
#define POWERPC_MSRM_e500v1 (0x000000000606FF30ULL)
#define POWERPC_MMU_e500v1 (POWERPC_MMU_BOOKE_FSL)
#define POWERPC_EXCP_e500v1 (POWERPC_EXCP_BOOKE)
@@ -4302,9 +4335,8 @@ static void init_proc_e300 (CPUPPCState *env)
PPC_WRTEE | PPC_RFDI | \
PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
- PPC_MEM_TLBSYNC | PPC_TLBIVAX | \
- PPC_BOOKE)
-#define POWERPC_INSNS2_e500v2 (PPC_NONE)
+ PPC_MEM_TLBSYNC | PPC_TLBIVAX)
+#define POWERPC_INSNS2_e500v2 (PPC2_BOOKE_FSL)
#define POWERPC_MSRM_e500v2 (0x000000000606FF30ULL)
#define POWERPC_MMU_e500v2 (POWERPC_MMU_BOOKE_FSL)
#define POWERPC_EXCP_e500v2 (POWERPC_EXCP_BOOKE)
@@ -4318,9 +4350,16 @@ static void init_proc_e300 (CPUPPCState *env)
static void init_proc_e500 (CPUPPCState *env)
{
+ uint32_t tlbncfg[2];
+
/* Time base */
gen_tbl(env);
- gen_spr_BookE(env, 0x0000000F0000FD7FULL);
+ /*
+ * XXX The e500 doesn't implement IVOR7 and IVOR9, but doesn't
+ * complain when accessing them.
+ * gen_spr_BookE(env, 0x0000000F0000FD7FULL);
+ */
+ gen_spr_BookE(env, 0x0000000F0000FFFFULL);
/* Processor identification */
spr_register(env, SPR_BOOKE_PIR, "PIR",
SPR_NOACCESS, SPR_NOACCESS,
@@ -4334,8 +4373,23 @@ static void init_proc_e500 (CPUPPCState *env)
/* Memory management */
#if !defined(CONFIG_USER_ONLY)
env->nb_pids = 3;
+ env->nb_ways = 2;
+ env->id_tlbs = 0;
+ if ((env->spr[SPR_PVR] & 0x00010000)) {
+ /* e500v2 */
+ env->nb_tlbs[0] = 512;
+ tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, env->nb_tlbs[0]);
+ tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+ } else {
+ /* e500v1 */
+ env->nb_tlbs[0] = 256;
+ tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, env->nb_tlbs[0]);
+ tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
+ }
+ env->nb_tlbs[1] = 16;
+ env->nb_tlb = env->nb_tlbs[0] + env->nb_tlbs[1];
#endif
- gen_spr_BookE_FSL(env, 0x0000005F);
+ gen_spr_BookE_FSL(env, 0x000000DF, tlbncfg);
/* XXX : not implemented */
spr_register(env, SPR_HID0, "HID0",
SPR_NOACCESS, SPR_NOACCESS,
@@ -4384,23 +4438,13 @@ static void init_proc_e500 (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_e500_l1csr0,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_Exxx_L1CSR1, "L1CSR1",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
@@ -4409,11 +4453,10 @@ static void init_proc_e500 (CPUPPCState *env)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
-#if !defined(CONFIG_USER_ONLY)
- env->nb_tlb = 64;
- env->nb_ways = 1;
- env->id_tlbs = 0;
-#endif
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_e500_mmucsr0,
+ 0x00000000);
init_excp_e200(env);
env->dcache_line_size = 32;
env->icache_line_size = 32;
--
1.6.0.2
[Qemu-devel] [PATCH 4/6] PPC: Add another 64 bits to instruction feature mask, Alexander Graf, 2011/05/02
Re: [Qemu-devel] [PATCH 0/6] PPC: Add FSL (e500) MMU emulation v3, Blue Swirl, 2011/05/04