aboutsummaryrefslogtreecommitdiff
path: root/target-s390x/mmu_helper.c
diff options
context:
space:
mode:
authorThomas Huth <thuth@linux.vnet.ibm.com>2015-02-12 18:09:22 +0100
committerChristian Borntraeger <borntraeger@de.ibm.com>2015-02-18 09:37:14 +0100
commite3e09d87c6e69c2da684d5aacabe3124ebcb6f8e (patch)
tree6cc5377c3394de2df9a71fb3f98a12b29a0a2c9a /target-s390x/mmu_helper.c
parent5d180439d07774c5a2477cf9901c9b51c9edd74b (diff)
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 <thuth@linux.vnet.ibm.com> Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Diffstat (limited to 'target-s390x/mmu_helper.c')
-rw-r--r--target-s390x/mmu_helper.c77
1 files changed, 49 insertions, 28 deletions
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;