aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-ppc/cpu.h1
-rw-r--r--target-ppc/helper.c78
2 files changed, 49 insertions, 30 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index d6feb88bb4..1c0b753f88 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -677,6 +677,7 @@ struct CPUPPCState {
typedef struct mmu_ctx_t mmu_ctx_t;
struct mmu_ctx_t {
target_phys_addr_t raddr; /* Real address */
+ target_phys_addr_t eaddr; /* Effective address */
int prot; /* Protection bits */
target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */
target_ulong ptem; /* Virtual segment ID | API */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index cc471969a2..5f3199ea3a 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -582,7 +582,8 @@ static always_inline int get_bat (CPUState *env, mmu_ctx_t *ctx,
/* PTE table lookup */
static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h,
- int rw, int type)
+ int rw, int type,
+ int target_page_bits)
{
target_ulong base, pte0, pte1;
int i, good = -1;
@@ -594,7 +595,14 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h,
#if defined(TARGET_PPC64)
if (is_64b) {
pte0 = ldq_phys(base + (i * 16));
- pte1 = ldq_phys(base + (i * 16) + 8);
+ pte1 = ldq_phys(base + (i * 16) + 8);
+
+ /* We have a TLB that saves 4K pages, so let's
+ * split a huge page to 4k chunks */
+ if (target_page_bits != TARGET_PAGE_BITS)
+ pte1 |= (ctx->eaddr & (( 1 << target_page_bits ) - 1))
+ & TARGET_PAGE_MASK;
+
r = pte64_check(ctx, pte0, pte1, h, rw, type);
LOG_MMU("Load pte from " ADDRX " => " ADDRX " " ADDRX
" %d %d %d " ADDRX "\n",
@@ -658,27 +666,30 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h,
return ret;
}
-static always_inline int find_pte32 (mmu_ctx_t *ctx, int h, int rw, int type)
+static always_inline int find_pte32 (mmu_ctx_t *ctx, int h, int rw,
+ int type, int target_page_bits)
{
- return _find_pte(ctx, 0, h, rw, type);
+ return _find_pte(ctx, 0, h, rw, type, target_page_bits);
}
#if defined(TARGET_PPC64)
-static always_inline int find_pte64 (mmu_ctx_t *ctx, int h, int rw, int type)
+static always_inline int find_pte64 (mmu_ctx_t *ctx, int h, int rw,
+ int type, int target_page_bits)
{
- return _find_pte(ctx, 1, h, rw, type);
+ return _find_pte(ctx, 1, h, rw, type, target_page_bits);
}
#endif
static always_inline int find_pte (CPUState *env, mmu_ctx_t *ctx,
- int h, int rw, int type)
+ int h, int rw, int type,
+ int target_page_bits)
{
#if defined(TARGET_PPC64)
if (env->mmu_model & POWERPC_MMU_64)
- return find_pte64(ctx, h, rw, type);
+ return find_pte64(ctx, h, rw, type, target_page_bits);
#endif
- return find_pte32(ctx, h, rw, type);
+ return find_pte32(ctx, h, rw, type, target_page_bits);
}
#if defined(TARGET_PPC64)
@@ -694,7 +705,8 @@ static always_inline void slb_invalidate (uint64_t *slb64)
static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
target_ulong *vsid,
- target_ulong *page_mask, int *attr)
+ target_ulong *page_mask, int *attr,
+ int *target_page_bits)
{
target_phys_addr_t sr_base;
target_ulong mask;
@@ -714,19 +726,16 @@ static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr,
PRIx32 "\n", __func__, n, sr_base, tmp64, tmp);
if (slb_is_valid(tmp64)) {
/* SLB entry is valid */
- switch (tmp64 & 0x0000000006000000ULL) {
- case 0x0000000000000000ULL:
- /* 256 MB segment */
- mask = 0xFFFFFFFFF0000000ULL;
- break;
- case 0x0000000002000000ULL:
- /* 1 TB segment */
+ if (tmp & 0x8) {
+ /* 1 TB Segment */
mask = 0xFFFF000000000000ULL;
- break;
- case 0x0000000004000000ULL:
- case 0x0000000006000000ULL:
- /* Reserved => segment is invalid */
- continue;
+ if (target_page_bits)
+ *target_page_bits = 24; // XXX 16M pages?
+ } else {
+ /* 256MB Segment */
+ mask = 0xFFFFFFFFF0000000ULL;
+ if (target_page_bits)
+ *target_page_bits = TARGET_PAGE_BITS;
}
if ((eaddr & mask) == (tmp64 & mask)) {
/* SLB match */
@@ -777,7 +786,7 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
int attr;
int n;
- n = slb_lookup(env, T0, &vsid, &page_mask, &attr);
+ n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
if (n >= 0) {
sr_base = env->spr[SPR_ASR];
sr_base += 12 * n;
@@ -871,20 +880,22 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
#if defined(TARGET_PPC64)
int attr;
#endif
- int ds, vsid_sh, sdr_sh, pr;
+ 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) {
LOG_MMU("Check SLBs\n");
- ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr);
+ 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;
ds = 0;
- ctx->nx = attr & 0x20 ? 1 : 0;
+ ctx->nx = attr & 0x10 ? 1 : 0;
+ ctx->eaddr = eaddr;
vsid_mask = 0x00003FFFFFFFFF80ULL;
vsid_sh = 7;
sdr_sh = 18;
@@ -903,6 +914,7 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
vsid_sh = 6;
sdr_sh = 16;
sdr_mask = 0xFFC0;
+ target_page_bits = TARGET_PAGE_BITS;
LOG_MMU("Check segment v=" ADDRX " %d " ADDRX
" nip=" ADDRX " lr=" ADDRX " ir=%d dr=%d pr=%d %d t=%d\n",
eaddr, (int)(eaddr >> 28), sr, env->nip,
@@ -918,7 +930,7 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
/* Page address translation */
/* Primary table address */
sdr = env->sdr1;
- pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS;
+ pgidx = (eaddr & page_mask) >> target_page_bits;
#if defined(TARGET_PPC64)
if (env->mmu_model & POWERPC_MMU_64) {
htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F));
@@ -944,7 +956,12 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
#if defined(TARGET_PPC64)
if (env->mmu_model & POWERPC_MMU_64) {
/* Only 5 bits of the page index are used in the AVPN */
- ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
+ if (target_page_bits > 23) {
+ ctx->ptem = (vsid << 12) |
+ ((pgidx << (target_page_bits - 16)) & 0xF80);
+ } else {
+ ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
+ }
} else
#endif
{
@@ -962,7 +979,7 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
" pg_addr=" PADDRX "\n",
sdr, vsid, pgidx, hash, ctx->pg_addr[0]);
/* Primary table lookup */
- ret = find_pte(env, ctx, 0, rw, type);
+ ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
if (ret < 0) {
/* Secondary table lookup */
if (eaddr != 0xEFFFFFFF)
@@ -970,7 +987,8 @@ static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx,
"api=" ADDRX " hash=" PADDRX
" pg_addr=" PADDRX "\n",
sdr, vsid, pgidx, hash, ctx->pg_addr[1]);
- ret2 = find_pte(env, ctx, 1, rw, type);
+ ret2 = find_pte(env, ctx, 1, rw, type,
+ target_page_bits);
if (ret2 != -1)
ret = ret2;
}