diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
| -rw-r--r-- | arch/s390/mm/fault.c | 63 | 
1 files changed, 44 insertions, 19 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 9649d9382e0a..a2632fd97d00 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -46,11 +46,15 @@  #define __SUBCODE_MASK 0x0600  #define __PF_RES_FIELD 0x8000000000000000ULL -#define VM_FAULT_BADCONTEXT	((__force vm_fault_t) 0x010000) -#define VM_FAULT_BADMAP		((__force vm_fault_t) 0x020000) -#define VM_FAULT_BADACCESS	((__force vm_fault_t) 0x040000) -#define VM_FAULT_SIGNAL		((__force vm_fault_t) 0x080000) -#define VM_FAULT_PFAULT		((__force vm_fault_t) 0x100000) +/* + * Allocate private vm_fault_reason from top.  Please make sure it won't + * collide with vm_fault_reason. + */ +#define VM_FAULT_BADCONTEXT	((__force vm_fault_t)0x80000000) +#define VM_FAULT_BADMAP		((__force vm_fault_t)0x40000000) +#define VM_FAULT_BADACCESS	((__force vm_fault_t)0x20000000) +#define VM_FAULT_SIGNAL		((__force vm_fault_t)0x10000000) +#define VM_FAULT_PFAULT		((__force vm_fault_t)0x8000000)  enum fault_type {  	KERNEL_FAULT, @@ -96,6 +100,20 @@ static enum fault_type get_fault_type(struct pt_regs *regs)  	return KERNEL_FAULT;  } +static unsigned long get_fault_address(struct pt_regs *regs) +{ +	unsigned long trans_exc_code = regs->int_parm_long; + +	return trans_exc_code & __FAIL_ADDR_MASK; +} + +static bool fault_is_write(struct pt_regs *regs) +{ +	unsigned long trans_exc_code = regs->int_parm_long; + +	return (trans_exc_code & store_indication) == 0x400; +} +  static int bad_address(void *p)  {  	unsigned long dummy; @@ -228,15 +246,26 @@ static noinline void do_sigsegv(struct pt_regs *regs, int si_code)  			(void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK));  } -static noinline void do_no_context(struct pt_regs *regs) +static noinline void do_no_context(struct pt_regs *regs, vm_fault_t fault)  { +	enum fault_type fault_type; +	unsigned long address; +	bool is_write; +  	if (fixup_exception(regs))  		return; +	fault_type = get_fault_type(regs); +	if ((fault_type == KERNEL_FAULT) && (fault == VM_FAULT_BADCONTEXT)) { +		address = get_fault_address(regs); +		is_write = fault_is_write(regs); +		if (kfence_handle_page_fault(address, is_write, regs)) +			return; +	}  	/*  	 * Oops. The kernel tried to access some bad page. We'll have to  	 * terminate things with extreme prejudice.  	 */ -	if (get_fault_type(regs) == KERNEL_FAULT) +	if (fault_type == KERNEL_FAULT)  		printk(KERN_ALERT "Unable to handle kernel pointer dereference"  		       " in virtual kernel address space\n");  	else @@ -255,7 +284,7 @@ static noinline void do_low_address(struct pt_regs *regs)  		die (regs, "Low-address protection");  	} -	do_no_context(regs); +	do_no_context(regs, VM_FAULT_BADACCESS);  }  static noinline void do_sigbus(struct pt_regs *regs) @@ -286,28 +315,28 @@ static noinline void do_fault_error(struct pt_regs *regs, vm_fault_t fault)  		fallthrough;  	case VM_FAULT_BADCONTEXT:  	case VM_FAULT_PFAULT: -		do_no_context(regs); +		do_no_context(regs, fault);  		break;  	case VM_FAULT_SIGNAL:  		if (!user_mode(regs)) -			do_no_context(regs); +			do_no_context(regs, fault);  		break;  	default: /* fault & VM_FAULT_ERROR */  		if (fault & VM_FAULT_OOM) {  			if (!user_mode(regs)) -				do_no_context(regs); +				do_no_context(regs, fault);  			else  				pagefault_out_of_memory();  		} else if (fault & VM_FAULT_SIGSEGV) {  			/* Kernel mode? Handle exceptions or die */  			if (!user_mode(regs)) -				do_no_context(regs); +				do_no_context(regs, fault);  			else  				do_sigsegv(regs, SEGV_MAPERR);  		} else if (fault & VM_FAULT_SIGBUS) {  			/* Kernel mode? Handle exceptions or die */  			if (!user_mode(regs)) -				do_no_context(regs); +				do_no_context(regs, fault);  			else  				do_sigbus(regs);  		} else @@ -334,7 +363,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)  	struct mm_struct *mm;  	struct vm_area_struct *vma;  	enum fault_type type; -	unsigned long trans_exc_code;  	unsigned long address;  	unsigned int flags;  	vm_fault_t fault; @@ -351,9 +379,8 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)  		return 0;  	mm = tsk->mm; -	trans_exc_code = regs->int_parm_long; -	address = trans_exc_code & __FAIL_ADDR_MASK; -	is_write = (trans_exc_code & store_indication) == 0x400; +	address = get_fault_address(regs); +	is_write = fault_is_write(regs);  	/*  	 * Verify that the fault happened in user space, that @@ -364,8 +391,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)  	type = get_fault_type(regs);  	switch (type) {  	case KERNEL_FAULT: -		if (kfence_handle_page_fault(address, is_write, regs)) -			return 0;  		goto out;  	case USER_FAULT:  	case GMAP_FAULT:  |