s390/mm: print control registers and page table walk on crash

Print extra debugging information to the console if the kernel or a user
space process crashed (with user space debugging enabled):

- contents of control register 7 and 13
- failing address and translation exception identification
- page table walk for the failing address

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Heiko Carstens 2014-04-07 10:20:40 +02:00 committed by Martin Schwidefsky
parent e7c46c66db
commit 3b7df3421f

View file

@ -126,6 +126,133 @@ static inline int user_space_fault(struct pt_regs *regs)
return 0;
}
static int bad_address(void *p)
{
unsigned long dummy;
return probe_kernel_address((unsigned long *)p, dummy);
}
#ifdef CONFIG_64BIT
static void dump_pagetable(unsigned long asce, unsigned long address)
{
unsigned long *table = __va(asce & PAGE_MASK);
pr_alert("AS:%016lx ", asce);
switch (asce & _ASCE_TYPE_MASK) {
case _ASCE_TYPE_REGION1:
table = table + ((address >> 53) & 0x7ff);
if (bad_address(table))
goto bad;
pr_cont("R1:%016lx ", *table);
if (*table & _REGION_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_REGION2:
table = table + ((address >> 42) & 0x7ff);
if (bad_address(table))
goto bad;
pr_cont("R2:%016lx ", *table);
if (*table & _REGION_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_REGION3:
table = table + ((address >> 31) & 0x7ff);
if (bad_address(table))
goto bad;
pr_cont("R3:%016lx ", *table);
if (*table & (_REGION_ENTRY_INVALID | _REGION3_ENTRY_LARGE))
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_SEGMENT:
table = table + ((address >> 20) & 0x7ff);
if (bad_address(table))
goto bad;
pr_cont(KERN_CONT "S:%016lx ", *table);
if (*table & (_SEGMENT_ENTRY_INVALID | _SEGMENT_ENTRY_LARGE))
goto out;
table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN);
}
table = table + ((address >> 12) & 0xff);
if (bad_address(table))
goto bad;
pr_cont("P:%016lx ", *table);
out:
pr_cont("\n");
return;
bad:
pr_cont("BAD\n");
}
#else /* CONFIG_64BIT */
static void dump_pagetable(unsigned long asce, unsigned long address)
{
unsigned long *table = __va(asce & PAGE_MASK);
pr_alert("AS:%08lx ", asce);
table = table + ((address >> 20) & 0x7ff);
if (bad_address(table))
goto bad;
pr_cont("S:%08lx ", *table);
if (*table & _SEGMENT_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN);
table = table + ((address >> 12) & 0xff);
if (bad_address(table))
goto bad;
pr_cont("P:%08lx ", *table);
out:
pr_cont("\n");
return;
bad:
pr_cont("BAD\n");
}
#endif /* CONFIG_64BIT */
static void dump_fault_info(struct pt_regs *regs)
{
unsigned long asce;
pr_alert("Fault in ");
switch (regs->int_parm_long & 3) {
case 3:
pr_cont("home space ");
break;
case 2:
pr_cont("secondary space ");
break;
case 1:
pr_cont("access register ");
break;
case 0:
pr_cont("primary space ");
break;
}
pr_cont("mode while using ");
if (!user_space_fault(regs)) {
asce = S390_lowcore.kernel_asce;
pr_cont("kernel ");
}
#ifdef CONFIG_PGSTE
else if ((current->flags & PF_VCPU) && S390_lowcore.gmap) {
struct gmap *gmap = (struct gmap *)S390_lowcore.gmap;
asce = gmap->asce;
pr_cont("gmap ");
}
#endif
else {
asce = S390_lowcore.user_asce;
pr_cont("user ");
}
pr_cont("ASCE.\n");
dump_pagetable(asce, regs->int_parm_long & __FAIL_ADDR_MASK);
}
static inline void report_user_fault(struct pt_regs *regs, long signr)
{
if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
@ -138,8 +265,9 @@ static inline void report_user_fault(struct pt_regs *regs, long signr)
regs->int_code);
print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN);
printk(KERN_CONT "\n");
printk(KERN_ALERT "failing address: %lX\n",
regs->int_parm_long & __FAIL_ADDR_MASK);
printk(KERN_ALERT "failing address: %016lx TEID: %016lx\n",
regs->int_parm_long & __FAIL_ADDR_MASK, regs->int_parm_long);
dump_fault_info(regs);
show_regs(regs);
}
@ -177,11 +305,13 @@ static noinline void do_no_context(struct pt_regs *regs)
address = regs->int_parm_long & __FAIL_ADDR_MASK;
if (!user_space_fault(regs))
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
" at virtual kernel address %p\n", (void *)address);
" in virtual kernel address space\n");
else
printk(KERN_ALERT "Unable to handle kernel paging request"
" at virtual user address %p\n", (void *)address);
" in virtual user address space\n");
printk(KERN_ALERT "failing address: %016lx TEID: %016lx\n",
regs->int_parm_long & __FAIL_ADDR_MASK, regs->int_parm_long);
dump_fault_info(regs);
die(regs, "Oops");
do_exit(SIGKILL);
}