diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
| -rw-r--r-- | arch/s390/mm/fault.c | 140 | 
1 files changed, 135 insertions, 5 deletions
| diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 19f623f1f21c..2f51a998a67e 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -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);  } |