aboutsummaryrefslogtreecommitdiff
path: root/target-ppc
diff options
context:
space:
mode:
Diffstat (limited to 'target-ppc')
-rw-r--r--target-ppc/cpu.h29
-rw-r--r--target-ppc/helper.c176
-rw-r--r--target-ppc/helper.h1
-rw-r--r--target-ppc/op_helper.c9
4 files changed, 79 insertions, 136 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index deb8d7c9c5..124bbbf650 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -43,6 +43,8 @@
# define TARGET_VIRT_ADDR_SPACE_BITS 64
#endif
+#define TARGET_PAGE_BITS_16M 24
+
#else /* defined (TARGET_PPC64) */
/* PowerPC 32 definitions */
#define TARGET_LONG_BITS 32
@@ -359,10 +361,31 @@ union ppc_tlb_t {
typedef struct ppc_slb_t ppc_slb_t;
struct ppc_slb_t {
- uint64_t tmp64;
- uint32_t tmp;
+ uint64_t esid;
+ uint64_t vsid;
};
+/* Bits in the SLB ESID word */
+#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL
+#define SLB_ESID_V 0x0000000008000000ULL /* valid */
+
+/* Bits in the SLB VSID word */
+#define SLB_VSID_SHIFT 12
+#define SLB_VSID_SSIZE_SHIFT 62
+#define SLB_VSID_B 0xc000000000000000ULL
+#define SLB_VSID_B_256M 0x0000000000000000ULL
+#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL
+#define SLB_VSID_KS 0x0000000000000800ULL
+#define SLB_VSID_KP 0x0000000000000400ULL
+#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */
+#define SLB_VSID_L 0x0000000000000100ULL
+#define SLB_VSID_C 0x0000000000000080ULL /* class */
+#define SLB_VSID_LP 0x0000000000000030ULL
+#define SLB_VSID_ATTR 0x0000000000000FFFULL
+
+#define SEGMENT_SHIFT_256M 28
+#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1))
+
/*****************************************************************************/
/* Machine state register bits definition */
#define MSR_SF 63 /* Sixty-four-bit mode hflags */
@@ -755,7 +778,7 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
void ppc_store_asr (CPUPPCState *env, target_ulong value);
target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
-void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
+int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
#endif /* defined(TARGET_PPC64) */
void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
#endif /* !defined(CONFIG_USER_ONLY) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 4b491012d7..2094ca36f9 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -672,85 +672,36 @@ static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
}
#if defined(TARGET_PPC64)
-static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr)
-{
- ppc_slb_t *retval = &env->slb[nr];
-
-#if 0 // XXX implement bridge mode?
- if (env->spr[SPR_ASR] & 1) {
- target_phys_addr_t sr_base;
-
- sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000;
- sr_base += (12 * nr);
-
- retval->tmp64 = ldq_phys(sr_base);
- retval->tmp = ldl_phys(sr_base + 8);
- }
-#endif
-
- return retval;
-}
-
-static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb)
-{
- ppc_slb_t *entry = &env->slb[nr];
-
- if (slb == entry)
- return;
-
- entry->tmp64 = slb->tmp64;
- entry->tmp = slb->tmp;
-}
-
-static inline int slb_is_valid(ppc_slb_t *slb)
-{
- return (int)(slb->tmp64 & 0x0000000008000000ULL);
-}
-
-static inline void slb_invalidate(ppc_slb_t *slb)
-{
- slb->tmp64 &= ~0x0000000008000000ULL;
-}
-
static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
target_ulong *vsid, target_ulong *page_mask,
int *attr, int *target_page_bits)
{
- target_ulong mask;
- int n, ret;
+ uint64_t esid;
+ int n;
- ret = -5;
LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
- mask = 0x0000000000000000ULL; /* Avoid gcc warning */
+
+ esid = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
+
for (n = 0; n < env->slb_nr; n++) {
- ppc_slb_t *slb = slb_get_entry(env, n);
-
- LOG_SLB("%s: seg %d %016" PRIx64 " %08"
- PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
- if (slb_is_valid(slb)) {
- /* SLB entry is valid */
- mask = 0xFFFFFFFFF0000000ULL;
- if (slb->tmp & 0x8) {
- /* 16 MB PTEs */
- if (target_page_bits)
- *target_page_bits = 24;
- } else {
- /* 4 KB PTEs */
- if (target_page_bits)
- *target_page_bits = TARGET_PAGE_BITS;
- }
- if ((eaddr & mask) == (slb->tmp64 & mask)) {
- /* SLB match */
- *vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
- *page_mask = ~mask;
- *attr = slb->tmp & 0xFF;
- ret = n;
- break;
+ ppc_slb_t *slb = &env->slb[n];
+
+ LOG_SLB("%s: slot %d %016" PRIx64 " %016"
+ PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
+ if (slb->esid == esid) {
+ *vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+ *page_mask = ~SEGMENT_MASK_256M;
+ *attr = slb->vsid & SLB_VSID_ATTR;
+ if (target_page_bits) {
+ *target_page_bits = (slb->vsid & SLB_VSID_L)
+ ? TARGET_PAGE_BITS_16M
+ : TARGET_PAGE_BITS;
}
+ return n;
}
}
- return ret;
+ return -5;
}
void ppc_slb_invalidate_all (CPUPPCState *env)
@@ -760,11 +711,10 @@ void ppc_slb_invalidate_all (CPUPPCState *env)
do_invalidate = 0;
/* XXX: Warning: slbia never invalidates the first segment */
for (n = 1; n < env->slb_nr; n++) {
- ppc_slb_t *slb = slb_get_entry(env, n);
+ ppc_slb_t *slb = &env->slb[n];
- if (slb_is_valid(slb)) {
- slb_invalidate(slb);
- slb_set_entry(env, n, slb);
+ if (slb->esid & SLB_ESID_V) {
+ slb->esid &= ~SLB_ESID_V;
/* XXX: given the fact that segment size is 256 MB or 1TB,
* and we still don't have a tlb_flush_mask(env, n, mask)
* in Qemu, we just invalidate all TLBs
@@ -781,68 +731,44 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
target_ulong vsid, page_mask;
int attr;
int n;
+ ppc_slb_t *slb;
n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
- if (n >= 0) {
- ppc_slb_t *slb = slb_get_entry(env, n);
-
- if (slb_is_valid(slb)) {
- slb_invalidate(slb);
- slb_set_entry(env, n, slb);
- /* XXX: given the fact that segment size is 256 MB or 1TB,
- * and we still don't have a tlb_flush_mask(env, n, mask)
- * in Qemu, we just invalidate all TLBs
- */
- tlb_flush(env, 1);
- }
+ if (n < 0) {
+ return;
}
-}
-target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
-{
- target_ulong rt;
- ppc_slb_t *slb = slb_get_entry(env, slb_nr);
+ slb = &env->slb[n];
- if (slb_is_valid(slb)) {
- /* SLB entry is valid */
- /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
- rt = slb->tmp >> 8; /* 65:88 => 40:63 */
- rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
- /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
- rt |= ((slb->tmp >> 4) & 0xF) << 27;
- } else {
- rt = 0;
- }
- LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d "
- TARGET_FMT_lx "\n", __func__, slb->tmp64, slb->tmp, slb_nr, rt);
+ if (slb->esid & SLB_ESID_V) {
+ slb->esid &= ~SLB_ESID_V;
- return rt;
+ /* XXX: given the fact that segment size is 256 MB or 1TB,
+ * and we still don't have a tlb_flush_mask(env, n, mask)
+ * in Qemu, we just invalidate all TLBs
+ */
+ tlb_flush(env, 1);
+ }
}
-void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
+int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
{
- ppc_slb_t *slb;
-
- uint64_t vsid;
- uint64_t esid;
- int flags, valid, slb_nr;
-
- vsid = rs >> 12;
- flags = ((rs >> 8) & 0xf);
+ int slot = rb & 0xfff;
+ uint64_t esid = rb & ~0xfff;
+ ppc_slb_t *slb = &env->slb[slot];
- esid = rb >> 28;
- valid = (rb & (1 << 27));
- slb_nr = rb & 0xfff;
+ if (slot >= env->slb_nr) {
+ return -1;
+ }
- slb = slb_get_entry(env, slb_nr);
- slb->tmp64 = (esid << 28) | valid | (vsid >> 24);
- slb->tmp = (vsid << 8) | (flags << 3);
+ slb->esid = esid;
+ slb->vsid = rs;
LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
- " %08" PRIx32 "\n", __func__, slb_nr, rb, rs, slb->tmp64,
- slb->tmp);
+ " %016" PRIx64 "\n", __func__, slot, rb, rs,
+ slb->esid, slb->vsid);
- slb_set_entry(env, slb_nr, slb);
+ return 0;
}
#endif /* defined(TARGET_PPC64) */
@@ -860,24 +786,22 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
{
target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask;
target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
-#if defined(TARGET_PPC64)
- int attr;
-#endif
int ds, vsid_sh, sdr_sh, pr, target_page_bits;
int ret, ret2;
pr = msr_pr;
#if defined(TARGET_PPC64)
if (env->mmu_model & POWERPC_MMU_64) {
+ int attr;
+
LOG_MMU("Check SLBs\n");
ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr,
&target_page_bits);
if (ret < 0)
return ret;
- ctx->key = ((attr & 0x40) && (pr != 0)) ||
- ((attr & 0x80) && (pr == 0)) ? 1 : 0;
+ ctx->key = !!(pr ? (attr & SLB_VSID_KP) : (attr & SLB_VSID_KS));
ds = 0;
- ctx->nx = attr & 0x10 ? 1 : 0;
+ ctx->nx = !!(attr & SLB_VSID_N);
ctx->eaddr = eaddr;
vsid_mask = 0x00003FFFFFFFFF80ULL;
vsid_sh = 7;
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 2bf9283486..d512cb00e2 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -340,7 +340,6 @@ DEF_HELPER_1(74xx_tlbi, void, tl)
DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
#if defined(TARGET_PPC64)
-DEF_HELPER_FLAGS_1(load_slb, TCG_CALL_CONST, tl, tl)
DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index 17e070ae75..bf41627b21 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -3746,14 +3746,11 @@ void helper_store_sr (target_ulong sr_num, target_ulong val)
/* SLB management */
#if defined(TARGET_PPC64)
-target_ulong helper_load_slb (target_ulong slb_nr)
-{
- return ppc_load_slb(env, slb_nr);
-}
-
void helper_store_slb (target_ulong rb, target_ulong rs)
{
- ppc_store_slb(env, rb, rs);
+ if (ppc_store_slb(env, rb, rs) < 0) {
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL);
+ }
}
void helper_slbia (void)