Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b7df342 authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky
Browse files

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: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent e7c46c66
Loading
Loading
Loading
Loading
+135 −5
Original line number Diff line number Diff line
@@ -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);
}