diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index e96afa61ec..eb793e03c0 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -113,11 +113,13 @@ enum powerpc_mmu_t { #if defined(TARGET_PPC64) #define POWERPC_MMU_64 0x00010000 #define POWERPC_MMU_1TSEG 0x00020000 +#define POWERPC_MMU_AMR 0x00040000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, - /* Architecture 2.06 "degraded" (no 1T segments) */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_AMR | 0x00000003, + /* Architecture 2.06 "degraded" (no 1T segments or AMR) */ POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003, #endif /* defined(TARGET_PPC64) */ }; @@ -1223,6 +1225,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_601_UDECR (0x006) #define SPR_LR (0x008) #define SPR_CTR (0x009) +#define SPR_UAMR (0x00C) #define SPR_DSCR (0x011) #define SPR_DSISR (0x012) #define SPR_DAR (0x013) /* DAE for PowerPC 601 */ @@ -1260,6 +1263,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_MPC_CMPH (0x09B) #define SPR_MPC_LCTRL1 (0x09C) #define SPR_MPC_LCTRL2 (0x09D) +#define SPR_UAMOR (0x09D) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_VRSAVE (0x100) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f18c98f021..43ccf456e3 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -275,6 +275,33 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, return prot; } +static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) +{ + int key, amrbits; + int prot = PAGE_EXEC; + + + /* Only recent MMUs implement Virtual Page Class Key Protection */ + if (!(env->mmu_model & POWERPC_MMU_AMR)) { + return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + } + + key = HPTE64_R_KEY(pte.pte1); + amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3; + + /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ + /* env->spr[SPR_AMR]); */ + + if (amrbits & 0x2) { + prot |= PAGE_WRITE; + } + if (amrbits & 0x1) { + prot |= PAGE_READ; + } + + return prot; +} + static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) @@ -375,7 +402,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; - int prot; + int pp_prot, amr_prot, prot; uint64_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; hwaddr raddr; @@ -437,7 +464,9 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, /* 5. Check access permissions */ - prot = ppc_hash64_pte_prot(env, slb, pte); + pp_prot = ppc_hash64_pte_prot(env, slb, pte); + amr_prot = ppc_hash64_amr_prot(env, pte); + prot = pp_prot & amr_prot; if ((need_prot[rwx] & ~prot) != 0) { /* Access right violation */ @@ -446,14 +475,21 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, env->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; } else { + target_ulong dsisr = 0; + 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; + if (need_prot[rwx] & ~pp_prot) { + dsisr |= 0x08000000; } + if (rwx == 1) { + dsisr |= 0x02000000; + } + if (need_prot[rwx] & ~amr_prot) { + dsisr |= 0x00200000; + } + env->spr[SPR_DSISR] = dsisr; } return 1; } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 37ed7ca4ef..55f5a230fd 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -69,6 +69,8 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE64_R_C 0x0000000000000080ULL #define HPTE64_R_R 0x0000000000000100ULL #define HPTE64_R_KEY_LO 0x0000000000000e00ULL +#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 60) | \ + (((x) & HPTE64_R_KEY_LO) >> 9)) #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 09fb29ec97..2780f92188 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -1017,6 +1017,54 @@ static void gen_spr_7xx (CPUPPCState *env) 0x00000000); } +#ifdef TARGET_PPC64 +#ifndef CONFIG_USER_ONLY +static void spr_read_uamr (void *opaque, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], SPR_AMR); + spr_load_dump_spr(SPR_AMR); +} + +static void spr_write_uamr (void *opaque, int sprn, int gprn) +{ + gen_store_spr(SPR_AMR, cpu_gpr[gprn]); + spr_store_dump_spr(SPR_AMR); +} + +static void spr_write_uamr_pr (void *opaque, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + gen_load_spr(t0, SPR_UAMOR); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + gen_store_spr(SPR_AMR, t0); + spr_store_dump_spr(SPR_AMR); +} +#endif /* CONFIG_USER_ONLY */ + +static void gen_spr_amr (CPUPPCState *env) +{ +#ifndef CONFIG_USER_ONLY + /* Virtual Page Class Key protection */ + /* The AMR is accessible either via SPR 13 or SPR 29. 13 is + * userspace accessible, 29 is privileged. So we only need to set + * the kvm ONE_REG id on one of them, we use 29 */ + spr_register(env, SPR_UAMR, "UAMR", + &spr_read_uamr, &spr_write_uamr_pr, + &spr_read_uamr, &spr_write_uamr, + 0); + spr_register_kvm(env, SPR_AMR, "AMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_AMR, 0xffffffffffffffffULL); + spr_register_kvm(env, SPR_UAMOR, "UAMOR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_UAMOR, 0); +#endif /* !CONFIG_USER_ONLY */ +} +#endif /* TARGET_PPC64 */ + static void gen_spr_thrm (CPUPPCState *env) { /* Thermal management */ @@ -6872,6 +6920,7 @@ static void init_proc_POWER7 (CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, 0x00000000); /* TOFIX */ + gen_spr_amr(env); /* XXX : not implemented */ spr_register(env, SPR_CTRL, "SPR_CTRLT", SPR_NOACCESS, SPR_NOACCESS,