diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 107 |
1 files changed, 48 insertions, 59 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index a9a301866b3c..354dd39073ef 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -125,8 +125,7 @@ static inline int user_space_fault(unsigned long trans_exc_code) | |||
125 | return trans_exc_code != 3; | 125 | return trans_exc_code != 3; |
126 | } | 126 | } |
127 | 127 | ||
128 | static inline void report_user_fault(struct pt_regs *regs, long int_code, | 128 | static inline void report_user_fault(struct pt_regs *regs, long signr) |
129 | int signr, unsigned long address) | ||
130 | { | 129 | { |
131 | if ((task_pid_nr(current) > 1) && !show_unhandled_signals) | 130 | if ((task_pid_nr(current) > 1) && !show_unhandled_signals) |
132 | return; | 131 | return; |
@@ -134,10 +133,12 @@ static inline void report_user_fault(struct pt_regs *regs, long int_code, | |||
134 | return; | 133 | return; |
135 | if (!printk_ratelimit()) | 134 | if (!printk_ratelimit()) |
136 | return; | 135 | return; |
137 | printk("User process fault: interruption code 0x%lX ", int_code); | 136 | printk(KERN_ALERT "User process fault: interruption code 0x%X ", |
137 | regs->int_code); | ||
138 | print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN); | 138 | print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN); |
139 | printk("\n"); | 139 | printk(KERN_CONT "\n"); |
140 | printk("failing address: %lX\n", address); | 140 | printk(KERN_ALERT "failing address: %lX\n", |
141 | regs->int_parm_long & __FAIL_ADDR_MASK); | ||
141 | show_regs(regs); | 142 | show_regs(regs); |
142 | } | 143 | } |
143 | 144 | ||
@@ -145,24 +146,18 @@ static inline void report_user_fault(struct pt_regs *regs, long int_code, | |||
145 | * Send SIGSEGV to task. This is an external routine | 146 | * Send SIGSEGV to task. This is an external routine |
146 | * to keep the stack usage of do_page_fault small. | 147 | * to keep the stack usage of do_page_fault small. |
147 | */ | 148 | */ |
148 | static noinline void do_sigsegv(struct pt_regs *regs, long int_code, | 149 | static noinline void do_sigsegv(struct pt_regs *regs, int si_code) |
149 | int si_code, unsigned long trans_exc_code) | ||
150 | { | 150 | { |
151 | struct siginfo si; | 151 | struct siginfo si; |
152 | unsigned long address; | ||
153 | 152 | ||
154 | address = trans_exc_code & __FAIL_ADDR_MASK; | 153 | report_user_fault(regs, SIGSEGV); |
155 | current->thread.prot_addr = address; | ||
156 | current->thread.trap_no = int_code; | ||
157 | report_user_fault(regs, int_code, SIGSEGV, address); | ||
158 | si.si_signo = SIGSEGV; | 154 | si.si_signo = SIGSEGV; |
159 | si.si_code = si_code; | 155 | si.si_code = si_code; |
160 | si.si_addr = (void __user *) address; | 156 | si.si_addr = (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK); |
161 | force_sig_info(SIGSEGV, &si, current); | 157 | force_sig_info(SIGSEGV, &si, current); |
162 | } | 158 | } |
163 | 159 | ||
164 | static noinline void do_no_context(struct pt_regs *regs, long int_code, | 160 | static noinline void do_no_context(struct pt_regs *regs) |
165 | unsigned long trans_exc_code) | ||
166 | { | 161 | { |
167 | const struct exception_table_entry *fixup; | 162 | const struct exception_table_entry *fixup; |
168 | unsigned long address; | 163 | unsigned long address; |
@@ -178,55 +173,48 @@ static noinline void do_no_context(struct pt_regs *regs, long int_code, | |||
178 | * Oops. The kernel tried to access some bad page. We'll have to | 173 | * Oops. The kernel tried to access some bad page. We'll have to |
179 | * terminate things with extreme prejudice. | 174 | * terminate things with extreme prejudice. |
180 | */ | 175 | */ |
181 | address = trans_exc_code & __FAIL_ADDR_MASK; | 176 | address = regs->int_parm_long & __FAIL_ADDR_MASK; |
182 | if (!user_space_fault(trans_exc_code)) | 177 | if (!user_space_fault(regs->int_parm_long)) |
183 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" | 178 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" |
184 | " at virtual kernel address %p\n", (void *)address); | 179 | " at virtual kernel address %p\n", (void *)address); |
185 | else | 180 | else |
186 | printk(KERN_ALERT "Unable to handle kernel paging request" | 181 | printk(KERN_ALERT "Unable to handle kernel paging request" |
187 | " at virtual user address %p\n", (void *)address); | 182 | " at virtual user address %p\n", (void *)address); |
188 | 183 | ||
189 | die("Oops", regs, int_code); | 184 | die(regs, "Oops"); |
190 | do_exit(SIGKILL); | 185 | do_exit(SIGKILL); |
191 | } | 186 | } |
192 | 187 | ||
193 | static noinline void do_low_address(struct pt_regs *regs, long int_code, | 188 | static noinline void do_low_address(struct pt_regs *regs) |
194 | unsigned long trans_exc_code) | ||
195 | { | 189 | { |
196 | /* Low-address protection hit in kernel mode means | 190 | /* Low-address protection hit in kernel mode means |
197 | NULL pointer write access in kernel mode. */ | 191 | NULL pointer write access in kernel mode. */ |
198 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 192 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
199 | /* Low-address protection hit in user mode 'cannot happen'. */ | 193 | /* Low-address protection hit in user mode 'cannot happen'. */ |
200 | die ("Low-address protection", regs, int_code); | 194 | die (regs, "Low-address protection"); |
201 | do_exit(SIGKILL); | 195 | do_exit(SIGKILL); |
202 | } | 196 | } |
203 | 197 | ||
204 | do_no_context(regs, int_code, trans_exc_code); | 198 | do_no_context(regs); |
205 | } | 199 | } |
206 | 200 | ||
207 | static noinline void do_sigbus(struct pt_regs *regs, long int_code, | 201 | static noinline void do_sigbus(struct pt_regs *regs) |
208 | unsigned long trans_exc_code) | ||
209 | { | 202 | { |
210 | struct task_struct *tsk = current; | 203 | struct task_struct *tsk = current; |
211 | unsigned long address; | ||
212 | struct siginfo si; | 204 | struct siginfo si; |
213 | 205 | ||
214 | /* | 206 | /* |
215 | * Send a sigbus, regardless of whether we were in kernel | 207 | * Send a sigbus, regardless of whether we were in kernel |
216 | * or user mode. | 208 | * or user mode. |
217 | */ | 209 | */ |
218 | address = trans_exc_code & __FAIL_ADDR_MASK; | ||
219 | tsk->thread.prot_addr = address; | ||
220 | tsk->thread.trap_no = int_code; | ||
221 | si.si_signo = SIGBUS; | 210 | si.si_signo = SIGBUS; |
222 | si.si_errno = 0; | 211 | si.si_errno = 0; |
223 | si.si_code = BUS_ADRERR; | 212 | si.si_code = BUS_ADRERR; |
224 | si.si_addr = (void __user *) address; | 213 | si.si_addr = (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK); |
225 | force_sig_info(SIGBUS, &si, tsk); | 214 | force_sig_info(SIGBUS, &si, tsk); |
226 | } | 215 | } |
227 | 216 | ||
228 | static noinline void do_fault_error(struct pt_regs *regs, long int_code, | 217 | static noinline void do_fault_error(struct pt_regs *regs, int fault) |
229 | unsigned long trans_exc_code, int fault) | ||
230 | { | 218 | { |
231 | int si_code; | 219 | int si_code; |
232 | 220 | ||
@@ -238,24 +226,24 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code, | |||
238 | /* User mode accesses just cause a SIGSEGV */ | 226 | /* User mode accesses just cause a SIGSEGV */ |
239 | si_code = (fault == VM_FAULT_BADMAP) ? | 227 | si_code = (fault == VM_FAULT_BADMAP) ? |
240 | SEGV_MAPERR : SEGV_ACCERR; | 228 | SEGV_MAPERR : SEGV_ACCERR; |
241 | do_sigsegv(regs, int_code, si_code, trans_exc_code); | 229 | do_sigsegv(regs, si_code); |
242 | return; | 230 | return; |
243 | } | 231 | } |
244 | case VM_FAULT_BADCONTEXT: | 232 | case VM_FAULT_BADCONTEXT: |
245 | do_no_context(regs, int_code, trans_exc_code); | 233 | do_no_context(regs); |
246 | break; | 234 | break; |
247 | default: /* fault & VM_FAULT_ERROR */ | 235 | default: /* fault & VM_FAULT_ERROR */ |
248 | if (fault & VM_FAULT_OOM) { | 236 | if (fault & VM_FAULT_OOM) { |
249 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) | 237 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) |
250 | do_no_context(regs, int_code, trans_exc_code); | 238 | do_no_context(regs); |
251 | else | 239 | else |
252 | pagefault_out_of_memory(); | 240 | pagefault_out_of_memory(); |
253 | } else if (fault & VM_FAULT_SIGBUS) { | 241 | } else if (fault & VM_FAULT_SIGBUS) { |
254 | /* Kernel mode? Handle exceptions or die */ | 242 | /* Kernel mode? Handle exceptions or die */ |
255 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) | 243 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) |
256 | do_no_context(regs, int_code, trans_exc_code); | 244 | do_no_context(regs); |
257 | else | 245 | else |
258 | do_sigbus(regs, int_code, trans_exc_code); | 246 | do_sigbus(regs); |
259 | } else | 247 | } else |
260 | BUG(); | 248 | BUG(); |
261 | break; | 249 | break; |
@@ -273,12 +261,12 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code, | |||
273 | * 11 Page translation -> Not present (nullification) | 261 | * 11 Page translation -> Not present (nullification) |
274 | * 3b Region third trans. -> Not present (nullification) | 262 | * 3b Region third trans. -> Not present (nullification) |
275 | */ | 263 | */ |
276 | static inline int do_exception(struct pt_regs *regs, int access, | 264 | static inline int do_exception(struct pt_regs *regs, int access) |
277 | unsigned long trans_exc_code) | ||
278 | { | 265 | { |
279 | struct task_struct *tsk; | 266 | struct task_struct *tsk; |
280 | struct mm_struct *mm; | 267 | struct mm_struct *mm; |
281 | struct vm_area_struct *vma; | 268 | struct vm_area_struct *vma; |
269 | unsigned long trans_exc_code; | ||
282 | unsigned long address; | 270 | unsigned long address; |
283 | unsigned int flags; | 271 | unsigned int flags; |
284 | int fault; | 272 | int fault; |
@@ -288,6 +276,7 @@ static inline int do_exception(struct pt_regs *regs, int access, | |||
288 | 276 | ||
289 | tsk = current; | 277 | tsk = current; |
290 | mm = tsk->mm; | 278 | mm = tsk->mm; |
279 | trans_exc_code = regs->int_parm_long; | ||
291 | 280 | ||
292 | /* | 281 | /* |
293 | * Verify that the fault happened in user space, that | 282 | * Verify that the fault happened in user space, that |
@@ -387,45 +376,46 @@ out: | |||
387 | return fault; | 376 | return fault; |
388 | } | 377 | } |
389 | 378 | ||
390 | void __kprobes do_protection_exception(struct pt_regs *regs, long pgm_int_code, | 379 | void __kprobes do_protection_exception(struct pt_regs *regs) |
391 | unsigned long trans_exc_code) | ||
392 | { | 380 | { |
381 | unsigned long trans_exc_code; | ||
393 | int fault; | 382 | int fault; |
394 | 383 | ||
384 | trans_exc_code = regs->int_parm_long; | ||
395 | /* Protection exception is suppressing, decrement psw address. */ | 385 | /* Protection exception is suppressing, decrement psw address. */ |
396 | regs->psw.addr = __rewind_psw(regs->psw, pgm_int_code >> 16); | 386 | regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); |
397 | /* | 387 | /* |
398 | * Check for low-address protection. This needs to be treated | 388 | * Check for low-address protection. This needs to be treated |
399 | * as a special case because the translation exception code | 389 | * as a special case because the translation exception code |
400 | * field is not guaranteed to contain valid data in this case. | 390 | * field is not guaranteed to contain valid data in this case. |
401 | */ | 391 | */ |
402 | if (unlikely(!(trans_exc_code & 4))) { | 392 | if (unlikely(!(trans_exc_code & 4))) { |
403 | do_low_address(regs, pgm_int_code, trans_exc_code); | 393 | do_low_address(regs); |
404 | return; | 394 | return; |
405 | } | 395 | } |
406 | fault = do_exception(regs, VM_WRITE, trans_exc_code); | 396 | fault = do_exception(regs, VM_WRITE); |
407 | if (unlikely(fault)) | 397 | if (unlikely(fault)) |
408 | do_fault_error(regs, 4, trans_exc_code, fault); | 398 | do_fault_error(regs, fault); |
409 | } | 399 | } |
410 | 400 | ||
411 | void __kprobes do_dat_exception(struct pt_regs *regs, long pgm_int_code, | 401 | void __kprobes do_dat_exception(struct pt_regs *regs) |
412 | unsigned long trans_exc_code) | ||
413 | { | 402 | { |
414 | int access, fault; | 403 | int access, fault; |
415 | 404 | ||
416 | access = VM_READ | VM_EXEC | VM_WRITE; | 405 | access = VM_READ | VM_EXEC | VM_WRITE; |
417 | fault = do_exception(regs, access, trans_exc_code); | 406 | fault = do_exception(regs, access); |
418 | if (unlikely(fault)) | 407 | if (unlikely(fault)) |
419 | do_fault_error(regs, pgm_int_code & 255, trans_exc_code, fault); | 408 | do_fault_error(regs, fault); |
420 | } | 409 | } |
421 | 410 | ||
422 | #ifdef CONFIG_64BIT | 411 | #ifdef CONFIG_64BIT |
423 | void __kprobes do_asce_exception(struct pt_regs *regs, long pgm_int_code, | 412 | void __kprobes do_asce_exception(struct pt_regs *regs) |
424 | unsigned long trans_exc_code) | ||
425 | { | 413 | { |
426 | struct mm_struct *mm = current->mm; | 414 | struct mm_struct *mm = current->mm; |
427 | struct vm_area_struct *vma; | 415 | struct vm_area_struct *vma; |
416 | unsigned long trans_exc_code; | ||
428 | 417 | ||
418 | trans_exc_code = regs->int_parm_long; | ||
429 | if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm)) | 419 | if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm)) |
430 | goto no_context; | 420 | goto no_context; |
431 | 421 | ||
@@ -440,12 +430,12 @@ void __kprobes do_asce_exception(struct pt_regs *regs, long pgm_int_code, | |||
440 | 430 | ||
441 | /* User mode accesses just cause a SIGSEGV */ | 431 | /* User mode accesses just cause a SIGSEGV */ |
442 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 432 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
443 | do_sigsegv(regs, pgm_int_code, SEGV_MAPERR, trans_exc_code); | 433 | do_sigsegv(regs, SEGV_MAPERR); |
444 | return; | 434 | return; |
445 | } | 435 | } |
446 | 436 | ||
447 | no_context: | 437 | no_context: |
448 | do_no_context(regs, pgm_int_code, trans_exc_code); | 438 | do_no_context(regs); |
449 | } | 439 | } |
450 | #endif | 440 | #endif |
451 | 441 | ||
@@ -459,14 +449,15 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) | |||
459 | regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT; | 449 | regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT; |
460 | regs.psw.addr = (unsigned long) __builtin_return_address(0); | 450 | regs.psw.addr = (unsigned long) __builtin_return_address(0); |
461 | regs.psw.addr |= PSW_ADDR_AMODE; | 451 | regs.psw.addr |= PSW_ADDR_AMODE; |
462 | uaddr &= PAGE_MASK; | 452 | regs.int_code = pgm_int_code; |
453 | regs.int_parm_long = (uaddr & PAGE_MASK) | 2; | ||
463 | access = write ? VM_WRITE : VM_READ; | 454 | access = write ? VM_WRITE : VM_READ; |
464 | fault = do_exception(®s, access, uaddr | 2); | 455 | fault = do_exception(®s, access); |
465 | if (unlikely(fault)) { | 456 | if (unlikely(fault)) { |
466 | if (fault & VM_FAULT_OOM) | 457 | if (fault & VM_FAULT_OOM) |
467 | return -EFAULT; | 458 | return -EFAULT; |
468 | else if (fault & VM_FAULT_SIGBUS) | 459 | else if (fault & VM_FAULT_SIGBUS) |
469 | do_sigbus(®s, pgm_int_code, uaddr); | 460 | do_sigbus(®s); |
470 | } | 461 | } |
471 | return fault ? -EFAULT : 0; | 462 | return fault ? -EFAULT : 0; |
472 | } | 463 | } |
@@ -509,7 +500,7 @@ int pfault_init(void) | |||
509 | .reserved = __PF_RES_FIELD }; | 500 | .reserved = __PF_RES_FIELD }; |
510 | int rc; | 501 | int rc; |
511 | 502 | ||
512 | if (!MACHINE_IS_VM || pfault_disable) | 503 | if (pfault_disable) |
513 | return -1; | 504 | return -1; |
514 | asm volatile( | 505 | asm volatile( |
515 | " diag %1,%0,0x258\n" | 506 | " diag %1,%0,0x258\n" |
@@ -530,7 +521,7 @@ void pfault_fini(void) | |||
530 | .refversn = 2, | 521 | .refversn = 2, |
531 | }; | 522 | }; |
532 | 523 | ||
533 | if (!MACHINE_IS_VM || pfault_disable) | 524 | if (pfault_disable) |
534 | return; | 525 | return; |
535 | asm volatile( | 526 | asm volatile( |
536 | " diag %0,0,0x258\n" | 527 | " diag %0,0,0x258\n" |
@@ -643,8 +634,6 @@ static int __init pfault_irq_init(void) | |||
643 | { | 634 | { |
644 | int rc; | 635 | int rc; |
645 | 636 | ||
646 | if (!MACHINE_IS_VM) | ||
647 | return 0; | ||
648 | rc = register_external_interrupt(0x2603, pfault_interrupt); | 637 | rc = register_external_interrupt(0x2603, pfault_interrupt); |
649 | if (rc) | 638 | if (rc) |
650 | goto out_extint; | 639 | goto out_extint; |