diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 53 |
1 files changed, 16 insertions, 37 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 88cef505453b..19f623f1f21c 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -106,21 +106,24 @@ void bust_spinlocks(int yes) | |||
106 | * Returns the address space associated with the fault. | 106 | * Returns the address space associated with the fault. |
107 | * Returns 0 for kernel space and 1 for user space. | 107 | * Returns 0 for kernel space and 1 for user space. |
108 | */ | 108 | */ |
109 | static inline int user_space_fault(unsigned long trans_exc_code) | 109 | static inline int user_space_fault(struct pt_regs *regs) |
110 | { | 110 | { |
111 | unsigned long trans_exc_code; | ||
112 | |||
111 | /* | 113 | /* |
112 | * The lowest two bits of the translation exception | 114 | * The lowest two bits of the translation exception |
113 | * identification indicate which paging table was used. | 115 | * identification indicate which paging table was used. |
114 | */ | 116 | */ |
115 | trans_exc_code &= 3; | 117 | trans_exc_code = regs->int_parm_long & 3; |
116 | if (trans_exc_code == 2) | 118 | if (trans_exc_code == 3) /* home space -> kernel */ |
117 | /* Access via secondary space, set_fs setting decides */ | 119 | return 0; |
120 | if (user_mode(regs)) | ||
121 | return 1; | ||
122 | if (trans_exc_code == 2) /* secondary space -> set_fs */ | ||
118 | return current->thread.mm_segment.ar4; | 123 | return current->thread.mm_segment.ar4; |
119 | /* | 124 | if (current->flags & PF_VCPU) |
120 | * Access via primary space or access register is from user space | 125 | return 1; |
121 | * and access via home space is from the kernel. | 126 | return 0; |
122 | */ | ||
123 | return trans_exc_code != 3; | ||
124 | } | 127 | } |
125 | 128 | ||
126 | static inline void report_user_fault(struct pt_regs *regs, long signr) | 129 | static inline void report_user_fault(struct pt_regs *regs, long signr) |
@@ -172,7 +175,7 @@ static noinline void do_no_context(struct pt_regs *regs) | |||
172 | * terminate things with extreme prejudice. | 175 | * terminate things with extreme prejudice. |
173 | */ | 176 | */ |
174 | address = regs->int_parm_long & __FAIL_ADDR_MASK; | 177 | address = regs->int_parm_long & __FAIL_ADDR_MASK; |
175 | if (!user_space_fault(regs->int_parm_long)) | 178 | if (!user_space_fault(regs)) |
176 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" | 179 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" |
177 | " at virtual kernel address %p\n", (void *)address); | 180 | " at virtual kernel address %p\n", (void *)address); |
178 | else | 181 | else |
@@ -296,7 +299,7 @@ static inline int do_exception(struct pt_regs *regs, int access) | |||
296 | * user context. | 299 | * user context. |
297 | */ | 300 | */ |
298 | fault = VM_FAULT_BADCONTEXT; | 301 | fault = VM_FAULT_BADCONTEXT; |
299 | if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm)) | 302 | if (unlikely(!user_space_fault(regs) || in_atomic() || !mm)) |
300 | goto out; | 303 | goto out; |
301 | 304 | ||
302 | address = trans_exc_code & __FAIL_ADDR_MASK; | 305 | address = trans_exc_code & __FAIL_ADDR_MASK; |
@@ -441,30 +444,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs) | |||
441 | do_fault_error(regs, fault); | 444 | do_fault_error(regs, fault); |
442 | } | 445 | } |
443 | 446 | ||
444 | int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) | ||
445 | { | ||
446 | struct pt_regs regs; | ||
447 | int access, fault; | ||
448 | |||
449 | /* Emulate a uaccess fault from kernel mode. */ | ||
450 | regs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT | PSW_MASK_MCHECK; | ||
451 | if (!irqs_disabled()) | ||
452 | regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT; | ||
453 | regs.psw.addr = (unsigned long) __builtin_return_address(0); | ||
454 | regs.psw.addr |= PSW_ADDR_AMODE; | ||
455 | regs.int_code = pgm_int_code; | ||
456 | regs.int_parm_long = (uaddr & PAGE_MASK) | 2; | ||
457 | access = write ? VM_WRITE : VM_READ; | ||
458 | fault = do_exception(®s, access); | ||
459 | /* | ||
460 | * Since the fault happened in kernel mode while performing a uaccess | ||
461 | * all we need to do now is emulating a fixup in case "fault" is not | ||
462 | * zero. | ||
463 | * For the calling uaccess functions this results always in -EFAULT. | ||
464 | */ | ||
465 | return fault ? -EFAULT : 0; | ||
466 | } | ||
467 | |||
468 | #ifdef CONFIG_PFAULT | 447 | #ifdef CONFIG_PFAULT |
469 | /* | 448 | /* |
470 | * 'pfault' pseudo page faults routines. | 449 | * 'pfault' pseudo page faults routines. |
@@ -645,7 +624,7 @@ static int __init pfault_irq_init(void) | |||
645 | { | 624 | { |
646 | int rc; | 625 | int rc; |
647 | 626 | ||
648 | rc = register_external_interrupt(0x2603, pfault_interrupt); | 627 | rc = register_external_irq(EXT_IRQ_CP_SERVICE, pfault_interrupt); |
649 | if (rc) | 628 | if (rc) |
650 | goto out_extint; | 629 | goto out_extint; |
651 | rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; | 630 | rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; |
@@ -656,7 +635,7 @@ static int __init pfault_irq_init(void) | |||
656 | return 0; | 635 | return 0; |
657 | 636 | ||
658 | out_pfault: | 637 | out_pfault: |
659 | unregister_external_interrupt(0x2603, pfault_interrupt); | 638 | unregister_external_irq(EXT_IRQ_CP_SERVICE, pfault_interrupt); |
660 | out_extint: | 639 | out_extint: |
661 | pfault_disable = 1; | 640 | pfault_disable = 1; |
662 | return rc; | 641 | return rc; |