diff options
Diffstat (limited to 'arch/x86/kernel/traps.c')
-rw-r--r-- | arch/x86/kernel/traps.c | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 07ab8e9733c5..a9ae20579895 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -60,6 +60,7 @@ | |||
60 | #include <asm/fixmap.h> | 60 | #include <asm/fixmap.h> |
61 | #include <asm/mach_traps.h> | 61 | #include <asm/mach_traps.h> |
62 | #include <asm/alternative.h> | 62 | #include <asm/alternative.h> |
63 | #include <asm/mpx.h> | ||
63 | 64 | ||
64 | #ifdef CONFIG_X86_64 | 65 | #ifdef CONFIG_X86_64 |
65 | #include <asm/x86_init.h> | 66 | #include <asm/x86_init.h> |
@@ -228,7 +229,6 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ | |||
228 | 229 | ||
229 | DO_ERROR(X86_TRAP_DE, SIGFPE, "divide error", divide_error) | 230 | DO_ERROR(X86_TRAP_DE, SIGFPE, "divide error", divide_error) |
230 | DO_ERROR(X86_TRAP_OF, SIGSEGV, "overflow", overflow) | 231 | DO_ERROR(X86_TRAP_OF, SIGSEGV, "overflow", overflow) |
231 | DO_ERROR(X86_TRAP_BR, SIGSEGV, "bounds", bounds) | ||
232 | DO_ERROR(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op) | 232 | DO_ERROR(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op) |
233 | DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun",coprocessor_segment_overrun) | 233 | DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun",coprocessor_segment_overrun) |
234 | DO_ERROR(X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS) | 234 | DO_ERROR(X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS) |
@@ -286,6 +286,89 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) | |||
286 | } | 286 | } |
287 | #endif | 287 | #endif |
288 | 288 | ||
289 | dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) | ||
290 | { | ||
291 | struct task_struct *tsk = current; | ||
292 | struct xsave_struct *xsave_buf; | ||
293 | enum ctx_state prev_state; | ||
294 | struct bndcsr *bndcsr; | ||
295 | siginfo_t *info; | ||
296 | |||
297 | prev_state = exception_enter(); | ||
298 | if (notify_die(DIE_TRAP, "bounds", regs, error_code, | ||
299 | X86_TRAP_BR, SIGSEGV) == NOTIFY_STOP) | ||
300 | goto exit; | ||
301 | conditional_sti(regs); | ||
302 | |||
303 | if (!user_mode(regs)) | ||
304 | die("bounds", regs, error_code); | ||
305 | |||
306 | if (!cpu_feature_enabled(X86_FEATURE_MPX)) { | ||
307 | /* The exception is not from Intel MPX */ | ||
308 | goto exit_trap; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * We need to look at BNDSTATUS to resolve this exception. | ||
313 | * It is not directly accessible, though, so we need to | ||
314 | * do an xsave and then pull it out of the xsave buffer. | ||
315 | */ | ||
316 | fpu_save_init(&tsk->thread.fpu); | ||
317 | xsave_buf = &(tsk->thread.fpu.state->xsave); | ||
318 | bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR); | ||
319 | if (!bndcsr) | ||
320 | goto exit_trap; | ||
321 | |||
322 | /* | ||
323 | * The error code field of the BNDSTATUS register communicates status | ||
324 | * information of a bound range exception #BR or operation involving | ||
325 | * bound directory. | ||
326 | */ | ||
327 | switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) { | ||
328 | case 2: /* Bound directory has invalid entry. */ | ||
329 | if (mpx_handle_bd_fault(xsave_buf)) | ||
330 | goto exit_trap; | ||
331 | break; /* Success, it was handled */ | ||
332 | case 1: /* Bound violation. */ | ||
333 | info = mpx_generate_siginfo(regs, xsave_buf); | ||
334 | if (PTR_ERR(info)) { | ||
335 | /* | ||
336 | * We failed to decode the MPX instruction. Act as if | ||
337 | * the exception was not caused by MPX. | ||
338 | */ | ||
339 | goto exit_trap; | ||
340 | } | ||
341 | /* | ||
342 | * Success, we decoded the instruction and retrieved | ||
343 | * an 'info' containing the address being accessed | ||
344 | * which caused the exception. This information | ||
345 | * allows and application to possibly handle the | ||
346 | * #BR exception itself. | ||
347 | */ | ||
348 | do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, info); | ||
349 | kfree(info); | ||
350 | break; | ||
351 | case 0: /* No exception caused by Intel MPX operations. */ | ||
352 | goto exit_trap; | ||
353 | default: | ||
354 | die("bounds", regs, error_code); | ||
355 | } | ||
356 | |||
357 | exit: | ||
358 | exception_exit(prev_state); | ||
359 | return; | ||
360 | exit_trap: | ||
361 | /* | ||
362 | * This path out is for all the cases where we could not | ||
363 | * handle the exception in some way (like allocating a | ||
364 | * table or telling userspace about it. We will also end | ||
365 | * up here if the kernel has MPX turned off at compile | ||
366 | * time.. | ||
367 | */ | ||
368 | do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, NULL); | ||
369 | exception_exit(prev_state); | ||
370 | } | ||
371 | |||
289 | dotraplinkage void | 372 | dotraplinkage void |
290 | do_general_protection(struct pt_regs *regs, long error_code) | 373 | do_general_protection(struct pt_regs *regs, long error_code) |
291 | { | 374 | { |