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>
This commit is contained in:
parent
5d180439d0
commit
e3e09d87c6
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user