mmu-hash*: Merge translate and fault handling functions

ppc_hash{32,64}_handle_mmu_fault() is now the only caller of
ppc_hash{32,64{_translate(), so this patch combines them together.  This
means that instead of one returning a variety of non-obvious error codes
which then get translated into the various mmu exception conditions, we can
just generate the exceptions as we discover problems in the translation
path.  This also removes the last usage of mmu_ctx_hash{32,64}.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
David Gibson 2013-03-12 00:31:46 +00:00 committed by Alexander Graf
parent 5883d8b296
commit caa597bd9f
2 changed files with 167 additions and 233 deletions

View File

@ -238,7 +238,9 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
if (rwx == 2) { if (rwx == 2) {
/* No code fetch is allowed in direct-store areas */ /* No code fetch is allowed in direct-store areas */
return -4; env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
return 1;
} }
switch (env->access_type) { switch (env->access_type) {
@ -247,10 +249,20 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
break; break;
case ACCESS_FLOAT: case ACCESS_FLOAT:
/* Floating point load/store */ /* Floating point load/store */
return -4; env->exception_index = POWERPC_EXCP_ALIGN;
env->error_code = POWERPC_EXCP_ALIGN_FP;
env->spr[SPR_DAR] = eaddr;
return 1;
case ACCESS_RES: case ACCESS_RES:
/* lwarx, ldarx or srwcx. */ /* lwarx, ldarx or srwcx. */
return -4; env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x06000000;
} else {
env->spr[SPR_DSISR] = 0x04000000;
}
return 1;
case ACCESS_CACHE: case ACCESS_CACHE:
/* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
/* Should make the instruction do no-op. /* Should make the instruction do no-op.
@ -260,17 +272,33 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr,
return 0; return 0;
case ACCESS_EXT: case ACCESS_EXT:
/* eciwx or ecowx */ /* eciwx or ecowx */
return -4; env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x06100000;
} else {
env->spr[SPR_DSISR] = 0x04100000;
}
return 1;
default: default:
qemu_log("ERROR: instruction should not need " qemu_log("ERROR: instruction should not need "
"address translation\n"); "address translation\n");
return -4; abort();
} }
if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) {
*raddr = eaddr; *raddr = eaddr;
return 2; return 0;
} else { } else {
return -2; env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0a000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
}
return 1;
} }
} }
@ -352,32 +380,53 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
return (rpn & ~mask) | (eaddr & mask); return (rpn & ~mask) | (eaddr & mask);
} }
static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, int rwx,
target_ulong eaddr, int rwx) int mmu_idx)
{ {
target_ulong sr; target_ulong sr;
hwaddr pte_offset; hwaddr pte_offset;
ppc_hash_pte32_t pte; ppc_hash_pte32_t pte;
int prot;
uint32_t new_pte1; uint32_t new_pte1;
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
hwaddr raddr;
assert((rwx == 0) || (rwx == 1) || (rwx == 2)); assert((rwx == 0) || (rwx == 1) || (rwx == 2));
/* 1. Handle real mode accesses */ /* 1. Handle real mode accesses */
if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
/* Translation is off */ /* Translation is off */
ctx->raddr = eaddr; raddr = eaddr;
ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
TARGET_PAGE_SIZE);
return 0; return 0;
} }
/* 2. Check Block Address Translation entries (BATs) */ /* 2. Check Block Address Translation entries (BATs) */
if (env->nb_BATs != 0) { if (env->nb_BATs != 0) {
ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot);
if (ctx->raddr != -1) { if (raddr != -1) {
if (need_prot[rwx] & ~ctx->prot) { if (need_prot[rwx] & ~prot) {
return -2; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x08000000;
} else {
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0a000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
} }
}
return 1;
}
tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
raddr & TARGET_PAGE_MASK, prot, mmu_idx,
TARGET_PAGE_SIZE);
return 0; return 0;
} }
} }
@ -387,30 +436,66 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
/* 4. Handle direct store segments */ /* 4. Handle direct store segments */
if (sr & SR32_T) { if (sr & SR32_T) {
return ppc_hash32_direct_store(env, sr, eaddr, rwx, if (ppc_hash32_direct_store(env, sr, eaddr, rwx,
&ctx->raddr, &ctx->prot); &raddr, &prot) == 0) {
tlb_set_page(env, eaddr & TARGET_PAGE_MASK,
raddr & TARGET_PAGE_MASK, prot, mmu_idx,
TARGET_PAGE_SIZE);
return 0;
} else {
return 1;
}
} }
/* 5. Check for segment level no-execute violation */ /* 5. Check for segment level no-execute violation */
if ((rwx == 2) && (sr & SR32_NX)) { if ((rwx == 2) && (sr & SR32_NX)) {
return -3; env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
return 1;
} }
/* 6. Locate the PTE in the hash table */ /* 6. Locate the PTE in the hash table */
pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte);
if (pte_offset == -1) { if (pte_offset == -1) {
return -1; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x40000000;
} else {
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x42000000;
} else {
env->spr[SPR_DSISR] = 0x40000000;
}
}
return 1;
} }
LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
/* 7. Check access permissions */ /* 7. Check access permissions */
ctx->prot = ppc_hash32_pte_prot(env, sr, pte); prot = ppc_hash32_pte_prot(env, sr, pte);
if (need_prot[rwx] & ~ctx->prot) { if (need_prot[rwx] & ~prot) {
/* Access right violation */ /* Access right violation */
LOG_MMU("PTE access rejected\n"); LOG_MMU("PTE access rejected\n");
return -2; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x08000000;
} else {
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0a000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
}
}
return 1;
} }
LOG_MMU("PTE access granted !\n"); LOG_MMU("PTE access granted !\n");
@ -423,7 +508,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
} else { } else {
/* Treat the page as read-only for now, so that a later write /* Treat the page as read-only for now, so that a later write
* will pass through this function again to set the C bit */ * will pass through this function again to set the C bit */
ctx->prot &= ~PAGE_WRITE; prot &= ~PAGE_WRITE;
} }
if (new_pte1 != pte.pte1) { if (new_pte1 != pte.pte1) {
@ -432,7 +517,10 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx,
/* 9. Determine the real address from the PTE */ /* 9. Determine the real address from the PTE */
ctx->raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); raddr = ppc_hash32_pte_raddr(sr, pte, eaddr);
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
prot, mmu_idx, TARGET_PAGE_SIZE);
return 0; return 0;
} }
@ -470,117 +558,3 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr)
return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK;
} }
int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx,
int mmu_idx)
{
struct mmu_ctx_hash32 ctx;
int ret = 0;
ret = ppc_hash32_translate(env, &ctx, address, rwx);
if (ret == 0) {
tlb_set_page(env, address & TARGET_PAGE_MASK,
ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
mmu_idx, TARGET_PAGE_SIZE);
ret = 0;
} else if (ret < 0) {
LOG_MMU_STATE(env);
if (rwx == 2) {
switch (ret) {
case -1:
/* No matches in page tables or TLB */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x40000000;
break;
case -2:
/* Access rights violation */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x08000000;
break;
case -3:
/* No execute protection violation */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
break;
case -4:
/* Direct store exception */
/* No code fetch is allowed in direct-store areas */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
break;
}
} else {
switch (ret) {
case -1:
/* No matches in page tables or TLB */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x42000000;
} else {
env->spr[SPR_DSISR] = 0x40000000;
}
break;
case -2:
/* Access rights violation */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0A000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
}
break;
case -4:
/* Direct store exception */
switch (env->access_type) {
case ACCESS_FLOAT:
/* Floating point load/store */
env->exception_index = POWERPC_EXCP_ALIGN;
env->error_code = POWERPC_EXCP_ALIGN_FP;
env->spr[SPR_DAR] = address;
break;
case ACCESS_RES:
/* lwarx, ldarx or stwcx. */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x06000000;
} else {
env->spr[SPR_DSISR] = 0x04000000;
}
break;
case ACCESS_EXT:
/* eciwx or ecowx */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x06100000;
} else {
env->spr[SPR_DSISR] = 0x04100000;
}
break;
default:
printf("DSI: invalid exception (%d)\n", ret);
env->exception_index = POWERPC_EXCP_PROGRAM;
env->error_code =
POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL;
env->spr[SPR_DAR] = address;
break;
}
break;
}
}
#if 0
printf("%s: set exception to %d %02x\n", __func__,
env->exception, env->error_code);
#endif
ret = 1;
}
return ret;
}

View File

@ -40,11 +40,6 @@
# define LOG_SLB(...) do { } while (0) # define LOG_SLB(...) do { } while (0)
#endif #endif
struct mmu_ctx_hash64 {
hwaddr raddr; /* Real address */
int prot; /* Protection bits */
};
/* /*
* SLB handling * SLB handling
*/ */
@ -374,14 +369,16 @@ static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
return (rpn & ~mask) | (eaddr & mask); return (rpn & ~mask) | (eaddr & mask);
} }
static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr,
target_ulong eaddr, int rwx) int rwx, int mmu_idx)
{ {
ppc_slb_t *slb; ppc_slb_t *slb;
hwaddr pte_offset; hwaddr pte_offset;
ppc_hash_pte64_t pte; ppc_hash_pte64_t pte;
int prot;
uint64_t new_pte1; uint64_t new_pte1;
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
hwaddr raddr;
assert((rwx == 0) || (rwx == 1) || (rwx == 2)); assert((rwx == 0) || (rwx == 1) || (rwx == 2));
@ -389,8 +386,10 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx,
if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
/* Translation is off */ /* Translation is off */
/* In real mode the top 4 effective address bits are ignored */ /* In real mode the top 4 effective address bits are ignored */
ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
TARGET_PAGE_SIZE);
return 0; return 0;
} }
@ -398,29 +397,65 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx,
slb = slb_lookup(env, eaddr); slb = slb_lookup(env, eaddr);
if (!slb) { if (!slb) {
return -5; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISEG;
env->error_code = 0;
} else {
env->exception_index = POWERPC_EXCP_DSEG;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
}
return 1;
} }
/* 3. Check for segment level no-execute violation */ /* 3. Check for segment level no-execute violation */
if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
return -3; env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
return 1;
} }
/* 4. Locate the PTE in the hash table */ /* 4. Locate the PTE in the hash table */
pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte); pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte);
if (pte_offset == -1) { if (pte_offset == -1) {
return -1; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x40000000;
} else {
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x42000000;
} else {
env->spr[SPR_DSISR] = 0x40000000;
}
}
return 1;
} }
LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
/* 5. Check access permissions */ /* 5. Check access permissions */
ctx->prot = ppc_hash64_pte_prot(env, slb, pte); prot = ppc_hash64_pte_prot(env, slb, pte);
if ((need_prot[rwx] & ~ctx->prot) != 0) { if ((need_prot[rwx] & ~prot) != 0) {
/* Access right violation */ /* Access right violation */
LOG_MMU("PTE access rejected\n"); LOG_MMU("PTE access rejected\n");
return -2; if (rwx == 2) {
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x08000000;
} else {
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0A000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
}
}
return 1;
} }
LOG_MMU("PTE access granted !\n"); LOG_MMU("PTE access granted !\n");
@ -433,7 +468,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx,
} else { } else {
/* Treat the page as read-only for now, so that a later write /* Treat the page as read-only for now, so that a later write
* will pass through this function again to set the C bit */ * will pass through this function again to set the C bit */
ctx->prot &= ~PAGE_WRITE; prot &= ~PAGE_WRITE;
} }
if (new_pte1 != pte.pte1) { if (new_pte1 != pte.pte1) {
@ -442,7 +477,10 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx,
/* 7. Determine the real address from the PTE */ /* 7. Determine the real address from the PTE */
ctx->raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); raddr = ppc_hash64_pte_raddr(slb, pte, eaddr);
tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
prot, mmu_idx, TARGET_PAGE_SIZE);
return 0; return 0;
} }
@ -470,81 +508,3 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK;
} }
int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx,
int mmu_idx)
{
struct mmu_ctx_hash64 ctx;
int ret = 0;
ret = ppc_hash64_translate(env, &ctx, address, rwx);
if (ret == 0) {
tlb_set_page(env, address & TARGET_PAGE_MASK,
ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
mmu_idx, TARGET_PAGE_SIZE);
ret = 0;
} else if (ret < 0) {
LOG_MMU_STATE(env);
if (rwx == 2) {
switch (ret) {
case -1:
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x40000000;
break;
case -2:
/* Access rights violation */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x08000000;
break;
case -3:
/* No execute protection violation */
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
break;
case -5:
/* No match in segment table */
env->exception_index = POWERPC_EXCP_ISEG;
env->error_code = 0;
break;
}
} else {
switch (ret) {
case -1:
/* No matches in page tables or TLB */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x42000000;
} else {
env->spr[SPR_DSISR] = 0x40000000;
}
break;
case -2:
/* Access rights violation */
env->exception_index = POWERPC_EXCP_DSI;
env->error_code = 0;
env->spr[SPR_DAR] = address;
if (rwx == 1) {
env->spr[SPR_DSISR] = 0x0A000000;
} else {
env->spr[SPR_DSISR] = 0x08000000;
}
break;
case -5:
/* No match in segment table */
env->exception_index = POWERPC_EXCP_DSEG;
env->error_code = 0;
env->spr[SPR_DAR] = address;
break;
}
}
#if 0
printf("%s: set exception to %d %02x\n", __func__,
env->exception, env->error_code);
#endif
ret = 1;
}
return ret;
}