From e3e09d87c6e69c2da684d5aacabe3124ebcb6f8e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 12 Feb 2015 18:09:22 +0100 Subject: [PATCH] s390x/mmu: Skip exceptions properly when translating addresses for debug When a fault occurs during the MMU lookup in s390_cpu_get_phys_page_debug(), the trigger_page_fault() function writes the translation exception code into the lowcore - something you would not expect during a memory access by the debugger. Ease this problem by adding an additional parameter to mmu_translate() which can be used to specify whether a program check and the translation exception code should be injected or not. Signed-off-by: Thomas Huth Signed-off-by: Jens Freimann Signed-off-by: Christian Borntraeger --- target-s390x/cpu.h | 2 +- target-s390x/helper.c | 8 ++-- target-s390x/mem_helper.c | 12 +++--- target-s390x/mmu_helper.c | 77 +++++++++++++++++++++++++-------------- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 556304241a..c0016b69fc 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -885,7 +885,7 @@ struct sysib_322 { void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr); int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags); + target_ulong *raddr, int *flags, bool exc); int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr); diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 76b4b36fe5..f8a3c5a5da 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -127,7 +127,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, vaddr &= 0x7fffffff; } - if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) { + if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) { /* Translation ended in exception */ return 1; } @@ -154,8 +154,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; target_ulong raddr; - int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - int old_exc = cs->exception_index; + int prot; uint64_t asc = env->psw.mask & PSW_MASK_ASC; /* 31-Bit mode */ @@ -163,8 +162,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) vaddr &= 0x7fffffff; } - mmu_translate(env, vaddr, 2, asc, &raddr, &prot); - cs->exception_index = old_exc; + mmu_translate(env, vaddr, 2, asc, &raddr, &prot, false); return raddr; } diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index d67b345ad1..0e8cd0f489 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -65,7 +65,7 @@ static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t asc = env->psw.mask & PSW_MASK_ASC; int flags; - if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) { cpu_stb_data(env, dest, byte); cpu_abort(CPU(cpu), "should never reach here"); } @@ -90,13 +90,13 @@ static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t asc = env->psw.mask & PSW_MASK_ASC; int flags; - if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) { cpu_stb_data(env, dest, 0); cpu_abort(CPU(cpu), "should never reach here"); } dest_phys |= dest & ~TARGET_PAGE_MASK; - if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) { + if (mmu_translate(env, src, 0, asc, &src_phys, &flags, true)) { cpu_ldub_data(env, src); cpu_abort(CPU(cpu), "should never reach here"); } @@ -967,12 +967,12 @@ static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1, cc = 3; } - if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) { + if (mmu_translate(env, a1, 1, mode1, &dest, &flags, true)) { cpu_loop_exit(CPU(s390_env_get_cpu(env))); } dest |= a1 & ~TARGET_PAGE_MASK; - if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) { + if (mmu_translate(env, a2, 0, mode2, &src, &flags, true)) { cpu_loop_exit(CPU(s390_env_get_cpu(env))); } src |= a2 & ~TARGET_PAGE_MASK; @@ -1088,7 +1088,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) } cs->exception_index = old_exc; - if (mmu_translate(env, addr, 0, asc, &ret, &flags)) { + if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) { cc = 3; } if (cs->exception_index == EXCP_PGM) { diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c index d4087ba3ce..67ad3ccad0 100644 --- a/target-s390x/mmu_helper.c +++ b/target-s390x/mmu_helper.c @@ -66,7 +66,7 @@ static int trans_bits(CPUS390XState *env, uint64_t mode) } static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, - uint64_t mode) + uint64_t mode, bool exc) { CPUState *cs = CPU(s390_env_get_cpu(env)); int ilen = ILEN_LATER_INC; @@ -74,25 +74,33 @@ static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); + if (!exc) { + return; + } + stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); trigger_pgm_exception(env, PGM_PROTECTION, ilen); } static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, - uint32_t type, uint64_t asc, int rw) + uint32_t type, uint64_t asc, int rw, bool exc) { CPUState *cs = CPU(s390_env_get_cpu(env)); int ilen = ILEN_LATER; int bits = trans_bits(env, asc); + DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); + + if (!exc) { + return; + } + /* Code accesses have an undefined ilc. */ if (rw == 2) { ilen = 2; } - DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); - stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); trigger_pgm_exception(env, type, ilen); @@ -115,11 +123,11 @@ static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr) /* Decode page table entry (normal 4KB page) */ static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t asce, - target_ulong *raddr, int *flags, int rw) + target_ulong *raddr, int *flags, int rw, bool exc) { if (asce & _PAGE_INVALID) { DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, asce); - trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc); return -1; } @@ -139,7 +147,8 @@ static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, /* Decode segment table entry */ static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t st_entry, - target_ulong *raddr, int *flags, int rw) + target_ulong *raddr, int *flags, int rw, + bool exc) { CPUState *cs = CPU(s390_env_get_cpu(env)); uint64_t origin, offs, pt_entry; @@ -161,13 +170,14 @@ static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr, pt_entry = ldq_phys(cs->as, origin + offs); PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", __func__, origin, offs, pt_entry); - return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw); + return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc); } /* Decode region table entries */ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t entry, int level, - target_ulong *raddr, int *flags, int rw) + target_ulong *raddr, int *flags, int rw, + bool exc) { CPUState *cs = CPU(s390_env_get_cpu(env)); uint64_t origin, offs, new_entry; @@ -188,12 +198,12 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, if ((new_entry & _REGION_ENTRY_INV) != 0) { /* XXX different regions have different faults */ DPRINTF("%s: invalid region\n", __func__); - trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc); return -1; } if ((new_entry & _REGION_ENTRY_TYPE_MASK) != level) { - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); return -1; } @@ -202,7 +212,7 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, if (level == _ASCE_TYPE_SEGMENT) { return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags, - rw); + rw, exc); } /* Check region table offset and length */ @@ -210,18 +220,18 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6) || offs > (new_entry & _REGION_ENTRY_LENGTH)) { DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry); - trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw); + trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc); return -1; } /* yet another region */ return mmu_translate_region(env, vaddr, asc, new_entry, level - 4, - raddr, flags, rw); + raddr, flags, rw, exc); } static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, uint64_t asc, target_ulong *raddr, int *flags, - int rw) + int rw, bool exc) { uint64_t asce = 0; int level; @@ -252,7 +262,7 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, switch (level) { case _ASCE_TYPE_REGION1: if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc); return -1; } break; @@ -260,11 +270,11 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, if (vaddr & 0xffe0000000000000ULL) { DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 " 0xffe0000000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); return -1; } if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc); return -1; } break; @@ -272,11 +282,11 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, if (vaddr & 0xfffffc0000000000ULL) { DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 " 0xfffffc0000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); return -1; } if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc); return -1; } break; @@ -284,27 +294,38 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, if (vaddr & 0xffffffff80000000ULL) { DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 " 0xffffffff80000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); return -1; } if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); + trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc); return -1; } break; } - r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw); + r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw, + exc); if ((rw == 1) && !(*flags & PAGE_WRITE)) { - trigger_prot_fault(env, vaddr, asc); + trigger_prot_fault(env, vaddr, asc, exc); return -1; } return r; } +/** + * Translate a virtual (logical) address into a physical (absolute) address. + * @param vaddr the virtual address + * @param rw 0 = read, 1 = write, 2 = code fetch + * @param asc address space control (one of the PSW_ASC_* modes) + * @param raddr the translated address is stored to this pointer + * @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer + * @param exc true = inject a program check if a fault occured + * @return 0 if the translation was successfull, -1 if a fault occured + */ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags) + target_ulong *raddr, int *flags, bool exc) { int r = -1; uint8_t *sk; @@ -321,7 +342,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, switch (asc) { case PSW_ASC_PRIMARY: case PSW_ASC_HOME: - r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw); + r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw, exc); break; case PSW_ASC_SECONDARY: /* @@ -330,11 +351,11 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, */ if (rw == 2) { r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags, - rw); + rw, exc); *flags &= ~(PAGE_READ | PAGE_WRITE); } else { r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags, - rw); + rw, exc); *flags &= ~(PAGE_EXEC); } break;