diff options
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/kernel/head31.S | 11 | ||||
-rw-r--r-- | arch/s390/kernel/head64.S | 11 | ||||
-rw-r--r-- | arch/s390/mm/fault.c | 105 |
3 files changed, 63 insertions, 64 deletions
diff --git a/arch/s390/kernel/head31.S b/arch/s390/kernel/head31.S index da7c8bb80982..dc364c1419af 100644 --- a/arch/s390/kernel/head31.S +++ b/arch/s390/kernel/head31.S | |||
@@ -121,7 +121,7 @@ startup_continue: | |||
121 | .long .Lduct # cr2: dispatchable unit control table | 121 | .long .Lduct # cr2: dispatchable unit control table |
122 | .long 0 # cr3: instruction authorization | 122 | .long 0 # cr3: instruction authorization |
123 | .long 0 # cr4: instruction authorization | 123 | .long 0 # cr4: instruction authorization |
124 | .long 0xffffffff # cr5: primary-aste origin | 124 | .long .Lduct # cr5: primary-aste origin |
125 | .long 0 # cr6: I/O interrupts | 125 | .long 0 # cr6: I/O interrupts |
126 | .long 0 # cr7: secondary space segment table | 126 | .long 0 # cr7: secondary space segment table |
127 | .long 0 # cr8: access registers translation | 127 | .long 0 # cr8: access registers translation |
@@ -132,8 +132,6 @@ startup_continue: | |||
132 | .long 0 # cr13: home space segment table | 132 | .long 0 # cr13: home space segment table |
133 | .long 0xc0000000 # cr14: machine check handling off | 133 | .long 0xc0000000 # cr14: machine check handling off |
134 | .long 0 # cr15: linkage stack operations | 134 | .long 0 # cr15: linkage stack operations |
135 | .Lduct: .long 0,0,0,0,0,0,0,0 | ||
136 | .long 0,0,0,0,0,0,0,0 | ||
137 | .Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu | 135 | .Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu |
138 | .Lpccsp:.long 0x00080000,0x80000000 + .Lchkcsp | 136 | .Lpccsp:.long 0x00080000,0x80000000 + .Lchkcsp |
139 | .Lpcmvpg:.long 0x00080000,0x80000000 + .Lchkmvpg | 137 | .Lpcmvpg:.long 0x00080000,0x80000000 + .Lchkmvpg |
@@ -147,6 +145,13 @@ startup_continue: | |||
147 | .Linittu: .long init_thread_union | 145 | .Linittu: .long init_thread_union |
148 | .Lstartup_init: | 146 | .Lstartup_init: |
149 | .long startup_init | 147 | .long startup_init |
148 | .align 64 | ||
149 | .Lduct: .long 0,0,0,0,.Lduald,0,0,0 | ||
150 | .long 0,0,0,0,0,0,0,0 | ||
151 | .align 128 | ||
152 | .Lduald:.rept 8 | ||
153 | .long 0x80000000,0,0,0 # invalid access-list entries | ||
154 | .endr | ||
150 | 155 | ||
151 | .org 0x12000 | 156 | .org 0x12000 |
152 | .globl _ehead | 157 | .globl _ehead |
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index af09e18cc5d0..37010709fe68 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S | |||
@@ -134,7 +134,7 @@ startup_continue: | |||
134 | .quad .Lduct # cr2: dispatchable unit control table | 134 | .quad .Lduct # cr2: dispatchable unit control table |
135 | .quad 0 # cr3: instruction authorization | 135 | .quad 0 # cr3: instruction authorization |
136 | .quad 0 # cr4: instruction authorization | 136 | .quad 0 # cr4: instruction authorization |
137 | .quad 0xffffffffffffffff # cr5: primary-aste origin | 137 | .quad .Lduct # cr5: primary-aste origin |
138 | .quad 0 # cr6: I/O interrupts | 138 | .quad 0 # cr6: I/O interrupts |
139 | .quad 0 # cr7: secondary space segment table | 139 | .quad 0 # cr7: secondary space segment table |
140 | .quad 0 # cr8: access registers translation | 140 | .quad 0 # cr8: access registers translation |
@@ -145,14 +145,19 @@ startup_continue: | |||
145 | .quad 0 # cr13: home space segment table | 145 | .quad 0 # cr13: home space segment table |
146 | .quad 0xc0000000 # cr14: machine check handling off | 146 | .quad 0xc0000000 # cr14: machine check handling off |
147 | .quad 0 # cr15: linkage stack operations | 147 | .quad 0 # cr15: linkage stack operations |
148 | .Lduct: .long 0,0,0,0,0,0,0,0 | ||
149 | .long 0,0,0,0,0,0,0,0 | ||
150 | .Lpcmsk:.quad 0x0000000180000000 | 148 | .Lpcmsk:.quad 0x0000000180000000 |
151 | .L4malign:.quad 0xffffffffffc00000 | 149 | .L4malign:.quad 0xffffffffffc00000 |
152 | .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 | 150 | .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 |
153 | .Lnop: .long 0x07000700 | 151 | .Lnop: .long 0x07000700 |
154 | .Lparmaddr: | 152 | .Lparmaddr: |
155 | .quad PARMAREA | 153 | .quad PARMAREA |
154 | .align 64 | ||
155 | .Lduct: .long 0,0,0,0,.Lduald,0,0,0 | ||
156 | .long 0,0,0,0,0,0,0,0 | ||
157 | .align 128 | ||
158 | .Lduald:.rept 8 | ||
159 | .long 0x80000000,0,0,0 # invalid access-list entries | ||
160 | .endr | ||
156 | 161 | ||
157 | .org 0x12000 | 162 | .org 0x12000 |
158 | .globl _ehead | 163 | .globl _ehead |
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 641aef36ccc4..7462aebd3eb6 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -108,53 +108,40 @@ void bust_spinlocks(int yes) | |||
108 | } | 108 | } |
109 | 109 | ||
110 | /* | 110 | /* |
111 | * Check which address space is addressed by the access | 111 | * Returns the address space associated with the fault. |
112 | * register in S390_lowcore.exc_access_id. | 112 | * Returns 0 for kernel space, 1 for user space and |
113 | * Returns 1 for user space and 0 for kernel space. | 113 | * 2 for code execution in user space with noexec=on. |
114 | */ | 114 | */ |
115 | static int __check_access_register(struct pt_regs *regs, int error_code) | 115 | static inline int check_space(struct task_struct *tsk) |
116 | { | ||
117 | int areg = S390_lowcore.exc_access_id; | ||
118 | |||
119 | if (areg == 0) | ||
120 | /* Access via access register 0 -> kernel address */ | ||
121 | return 0; | ||
122 | save_access_regs(current->thread.acrs); | ||
123 | if (regs && areg < NUM_ACRS && current->thread.acrs[areg] <= 1) | ||
124 | /* | ||
125 | * access register contains 0 -> kernel address, | ||
126 | * access register contains 1 -> user space address | ||
127 | */ | ||
128 | return current->thread.acrs[areg]; | ||
129 | |||
130 | /* Something unhealthy was done with the access registers... */ | ||
131 | die("page fault via unknown access register", regs, error_code); | ||
132 | do_exit(SIGKILL); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * Check which address space the address belongs to. | ||
138 | * May return 1 or 2 for user space and 0 for kernel space. | ||
139 | * Returns 2 for user space in primary addressing mode with | ||
140 | * CONFIG_S390_EXEC_PROTECT on and kernel parameter noexec=on. | ||
141 | */ | ||
142 | static inline int check_user_space(struct pt_regs *regs, int error_code) | ||
143 | { | 116 | { |
144 | /* | 117 | /* |
145 | * The lowest two bits of S390_lowcore.trans_exc_code indicate | 118 | * The lowest two bits of S390_lowcore.trans_exc_code |
146 | * which paging table was used: | 119 | * indicate which paging table was used. |
147 | * 0: Primary Segment Table Descriptor | ||
148 | * 1: STD determined via access register | ||
149 | * 2: Secondary Segment Table Descriptor | ||
150 | * 3: Home Segment Table Descriptor | ||
151 | */ | 120 | */ |
152 | int descriptor = S390_lowcore.trans_exc_code & 3; | 121 | int desc = S390_lowcore.trans_exc_code & 3; |
153 | if (unlikely(descriptor == 1)) | 122 | |
154 | return __check_access_register(regs, error_code); | 123 | if (desc == 3) /* Home Segment Table Descriptor */ |
155 | if (descriptor == 2) | 124 | return switch_amode == 0; |
156 | return current->thread.mm_segment.ar4; | 125 | if (desc == 2) /* Secondary Segment Table Descriptor */ |
157 | return ((descriptor != 0) ^ (switch_amode)) << s390_noexec; | 126 | return tsk->thread.mm_segment.ar4; |
127 | #ifdef CONFIG_S390_SWITCH_AMODE | ||
128 | if (unlikely(desc == 1)) { /* STD determined via access register */ | ||
129 | /* %a0 always indicates primary space. */ | ||
130 | if (S390_lowcore.exc_access_id != 0) { | ||
131 | save_access_regs(tsk->thread.acrs); | ||
132 | /* | ||
133 | * An alet of 0 indicates primary space. | ||
134 | * An alet of 1 indicates secondary space. | ||
135 | * Any other alet values generate an | ||
136 | * alen-translation exception. | ||
137 | */ | ||
138 | if (tsk->thread.acrs[S390_lowcore.exc_access_id]) | ||
139 | return tsk->thread.mm_segment.ar4; | ||
140 | } | ||
141 | } | ||
142 | #endif | ||
143 | /* Primary Segment Table Descriptor */ | ||
144 | return switch_amode << s390_noexec; | ||
158 | } | 145 | } |
159 | 146 | ||
160 | /* | 147 | /* |
@@ -265,16 +252,16 @@ out_fault: | |||
265 | * 11 Page translation -> Not present (nullification) | 252 | * 11 Page translation -> Not present (nullification) |
266 | * 3b Region third trans. -> Not present (nullification) | 253 | * 3b Region third trans. -> Not present (nullification) |
267 | */ | 254 | */ |
268 | static inline void __kprobes | 255 | static inline void |
269 | do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | 256 | do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) |
270 | { | 257 | { |
271 | struct task_struct *tsk; | 258 | struct task_struct *tsk; |
272 | struct mm_struct *mm; | 259 | struct mm_struct *mm; |
273 | struct vm_area_struct * vma; | 260 | struct vm_area_struct * vma; |
274 | unsigned long address; | 261 | unsigned long address; |
275 | int user_address; | ||
276 | const struct exception_table_entry *fixup; | 262 | const struct exception_table_entry *fixup; |
277 | int si_code = SEGV_MAPERR; | 263 | int si_code; |
264 | int space; | ||
278 | 265 | ||
279 | tsk = current; | 266 | tsk = current; |
280 | mm = tsk->mm; | 267 | mm = tsk->mm; |
@@ -294,7 +281,7 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | |||
294 | NULL pointer write access in kernel mode. */ | 281 | NULL pointer write access in kernel mode. */ |
295 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) { | 282 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) { |
296 | address = 0; | 283 | address = 0; |
297 | user_address = 0; | 284 | space = 0; |
298 | goto no_context; | 285 | goto no_context; |
299 | } | 286 | } |
300 | 287 | ||
@@ -309,15 +296,15 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | |||
309 | * the address | 296 | * the address |
310 | */ | 297 | */ |
311 | address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK; | 298 | address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK; |
312 | user_address = check_user_space(regs, error_code); | 299 | space = check_space(tsk); |
313 | 300 | ||
314 | /* | 301 | /* |
315 | * Verify that the fault happened in user space, that | 302 | * Verify that the fault happened in user space, that |
316 | * we are not in an interrupt and that there is a | 303 | * we are not in an interrupt and that there is a |
317 | * user context. | 304 | * user context. |
318 | */ | 305 | */ |
319 | if (user_address == 0 || in_atomic() || !mm) | 306 | if (unlikely(space == 0 || in_atomic() || !mm)) |
320 | goto no_context; | 307 | goto no_context; |
321 | 308 | ||
322 | /* | 309 | /* |
323 | * When we get here, the fault happened in the current | 310 | * When we get here, the fault happened in the current |
@@ -328,12 +315,13 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | |||
328 | 315 | ||
329 | down_read(&mm->mmap_sem); | 316 | down_read(&mm->mmap_sem); |
330 | 317 | ||
331 | vma = find_vma(mm, address); | 318 | si_code = SEGV_MAPERR; |
332 | if (!vma) | 319 | vma = find_vma(mm, address); |
333 | goto bad_area; | 320 | if (!vma) |
321 | goto bad_area; | ||
334 | 322 | ||
335 | #ifdef CONFIG_S390_EXEC_PROTECT | 323 | #ifdef CONFIG_S390_EXEC_PROTECT |
336 | if (unlikely((user_address == 2) && !(vma->vm_flags & VM_EXEC))) | 324 | if (unlikely((space == 2) && !(vma->vm_flags & VM_EXEC))) |
337 | if (!signal_return(mm, regs, address, error_code)) | 325 | if (!signal_return(mm, regs, address, error_code)) |
338 | /* | 326 | /* |
339 | * signal_return() has done an up_read(&mm->mmap_sem) | 327 | * signal_return() has done an up_read(&mm->mmap_sem) |
@@ -389,7 +377,7 @@ survive: | |||
389 | * The instruction that caused the program check will | 377 | * The instruction that caused the program check will |
390 | * be repeated. Don't signal single step via SIGTRAP. | 378 | * be repeated. Don't signal single step via SIGTRAP. |
391 | */ | 379 | */ |
392 | clear_tsk_thread_flag(current, TIF_SINGLE_STEP); | 380 | clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP); |
393 | return; | 381 | return; |
394 | 382 | ||
395 | /* | 383 | /* |
@@ -419,7 +407,7 @@ no_context: | |||
419 | * Oops. The kernel tried to access some bad page. We'll have to | 407 | * Oops. The kernel tried to access some bad page. We'll have to |
420 | * terminate things with extreme prejudice. | 408 | * terminate things with extreme prejudice. |
421 | */ | 409 | */ |
422 | if (user_address == 0) | 410 | if (space == 0) |
423 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" | 411 | printk(KERN_ALERT "Unable to handle kernel pointer dereference" |
424 | " at virtual kernel address %p\n", (void *)address); | 412 | " at virtual kernel address %p\n", (void *)address); |
425 | else | 413 | else |
@@ -462,13 +450,14 @@ do_sigbus: | |||
462 | goto no_context; | 450 | goto no_context; |
463 | } | 451 | } |
464 | 452 | ||
465 | void do_protection_exception(struct pt_regs *regs, unsigned long error_code) | 453 | void __kprobes do_protection_exception(struct pt_regs *regs, |
454 | unsigned long error_code) | ||
466 | { | 455 | { |
467 | regs->psw.addr -= (error_code >> 16); | 456 | regs->psw.addr -= (error_code >> 16); |
468 | do_exception(regs, 4, 1); | 457 | do_exception(regs, 4, 1); |
469 | } | 458 | } |
470 | 459 | ||
471 | void do_dat_exception(struct pt_regs *regs, unsigned long error_code) | 460 | void __kprobes do_dat_exception(struct pt_regs *regs, unsigned long error_code) |
472 | { | 461 | { |
473 | do_exception(regs, error_code & 0xff, 0); | 462 | do_exception(regs, error_code & 0xff, 0); |
474 | } | 463 | } |