Message ID | 20180607143705.3531-2-yu-cheng.yu@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jun 7, 2018 at 7:40 AM Yu-cheng Yu <yu-cheng.yu@intel.com> wrote: > > A control protection exception is triggered when a control flow transfer > attempt violated shadow stack or indirect branch tracking constraints. > For example, the return address for a RET instruction differs from the > safe copy on the shadow stack; or a JMP instruction arrives at a non- > ENDBR instruction. > > The control protection exception handler works in a similar way as the > general protection fault handler. > > Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> > --- > arch/x86/entry/entry_32.S | 5 ++++ > arch/x86/entry/entry_64.S | 2 +- > arch/x86/include/asm/traps.h | 3 +++ > arch/x86/kernel/idt.c | 1 + > arch/x86/kernel/traps.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 71 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S > index bef8e2b202a8..14b63ef0d7d8 100644 > --- a/arch/x86/entry/entry_32.S > +++ b/arch/x86/entry/entry_32.S > @@ -1070,6 +1070,11 @@ ENTRY(general_protection) > jmp common_exception > END(general_protection) > > +ENTRY(control_protection) > + pushl $do_control_protection > + jmp common_exception > +END(control_protection) Ugh, you're seriously supporting this on 32-bit? Please test double fault handling very carefully -- the CET interaction with task switches is so gross that I didn't even bother reading the spec except to let the architects know that they were a but nuts to support it at all. > + > #ifdef CONFIG_KVM_GUEST > ENTRY(async_page_fault) > ASM_CLAC > diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S > index 3166b9674429..5230f705d229 100644 > --- a/arch/x86/entry/entry_64.S > +++ b/arch/x86/entry/entry_64.S > @@ -999,7 +999,7 @@ idtentry spurious_interrupt_bug do_spurious_interrupt_bug has_error_code=0 > idtentry coprocessor_error do_coprocessor_error has_error_code=0 > idtentry alignment_check do_alignment_check has_error_code=1 > idtentry simd_coprocessor_error do_simd_coprocessor_error has_error_code=0 > - > +idtentry control_protection do_control_protection has_error_code=1 > diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c > index 03f3d7695dac..4e8769a19aaf 100644 > --- a/arch/x86/kernel/traps.c > +++ b/arch/x86/kernel/traps.c > +/* > + * When a control protection exception occurs, send a signal > + * to the responsible application. Currently, control > + * protection is only enabled for the user mode. This > + * exception should not come from the kernel mode. > + */ > +dotraplinkage void > +do_control_protection(struct pt_regs *regs, long error_code) > +{ > + struct task_struct *tsk; > + > + RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); > + cond_local_irq_enable(regs); > + > + tsk = current; > + if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && > + !cpu_feature_enabled(X86_FEATURE_IBT)) { static_cpu_has(), please. But your handling here is odd -- I think that we should at least warn if we get #CP with CET disable. > + goto exit; > + } > + > + if (!user_mode(regs)) { > + tsk->thread.error_code = error_code; > + tsk->thread.trap_nr = X86_TRAP_CP; I realize you copied this from elsewhere in the file, but please either delete these assignments to error_code and trap_nr or at least hoist them out of the if block. > + if (notify_die(DIE_TRAP, "control protection fault", regs, > + error_code, X86_TRAP_CP, SIGSEGV) != NOTIFY_STOP) Does this notify_die() check serve any purpose at all? Removing all the old ones would be a project, but let's try not to add new callers. > + die("control protection fault", regs, error_code); > + return; > + } > + > + tsk->thread.error_code = error_code; > + tsk->thread.trap_nr = X86_TRAP_CP; > + > + if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && > + printk_ratelimit()) { > + unsigned int max_idx, err_idx; > + > + max_idx = ARRAY_SIZE(control_protection_err) - 1; > + err_idx = min((unsigned int)error_code - 1, max_idx); What if error_code == 0? Is that also invalid? > + pr_info("%s[%d] control protection ip:%lx sp:%lx error:%lx(%s)", > + tsk->comm, task_pid_nr(tsk), > + regs->ip, regs->sp, error_code, > + control_protection_err[err_idx]); > + print_vma_addr(" in ", regs->ip); > + pr_cont("\n"); > + } > + > +exit: > + force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); This is definitely wrong for the feature-disabled, !user_mode case. Also, are you planning on enabling CET for kernel code too?
On Thu, 2018-06-07 at 08:46 -0700, Andy Lutomirski wrote: > On Thu, Jun 7, 2018 at 7:40 AM Yu-cheng Yu <yu-cheng.yu@intel.com> wrote: > > ... > > diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S > > index bef8e2b202a8..14b63ef0d7d8 100644 > > --- a/arch/x86/entry/entry_32.S > > +++ b/arch/x86/entry/entry_32.S > > @@ -1070,6 +1070,11 @@ ENTRY(general_protection) > > jmp common_exception > > END(general_protection) > > > > +ENTRY(control_protection) > > + pushl $do_control_protection > > + jmp common_exception > > +END(control_protection) > > Ugh, you're seriously supporting this on 32-bit? Please test double > fault handling very carefully -- the CET interaction with task > switches is so gross that I didn't even bother reading the spec except > to let the architects know that they were a but nuts to support it at > all. > I will remove this. ... > > diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c > > index 03f3d7695dac..4e8769a19aaf 100644 > > --- a/arch/x86/kernel/traps.c > > +++ b/arch/x86/kernel/traps.c > > > +/* > > + * When a control protection exception occurs, send a signal > > + * to the responsible application. Currently, control > > + * protection is only enabled for the user mode. This > > + * exception should not come from the kernel mode. > > + */ > > +dotraplinkage void > > +do_control_protection(struct pt_regs *regs, long error_code) > > +{ > > + struct task_struct *tsk; > > + > > + RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); > > + cond_local_irq_enable(regs); > > + > > + tsk = current; > > + if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && > > + !cpu_feature_enabled(X86_FEATURE_IBT)) { > > static_cpu_has(), please. But your handling here is odd -- I think > that we should at least warn if we get #CP with CET disable. I will fix it. > > > + goto exit; > > + } > > + > > + if (!user_mode(regs)) { > > + tsk->thread.error_code = error_code; > > + tsk->thread.trap_nr = X86_TRAP_CP; > > I realize you copied this from elsewhere in the file, but please > either delete these assignments to error_code and trap_nr or at least > hoist them out of the if block. I will fix it. > > > + if (notify_die(DIE_TRAP, "control protection fault", regs, > > + error_code, X86_TRAP_CP, SIGSEGV) != NOTIFY_STOP) > > Does this notify_die() check serve any purpose at all? Removing all > the old ones would be a project, but let's try not to add new callers. OK. > > > + die("control protection fault", regs, error_code); > > + return; > > + } > > + > > + tsk->thread.error_code = error_code; > > + tsk->thread.trap_nr = X86_TRAP_CP; > > + > > + if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && > > + printk_ratelimit()) { > > + unsigned int max_idx, err_idx; > > + > > + max_idx = ARRAY_SIZE(control_protection_err) - 1; > > + err_idx = min((unsigned int)error_code - 1, max_idx); > > What if error_code == 0? Is that also invalid? The error code is between 1 and 5 inclusive. I thought if it is 0, then err_idx would become max_idx here. I can change it to: if (error_code == 0) error_code = max_idx; Or, add some comments for this case. > > > + pr_info("%s[%d] control protection ip:%lx sp:%lx error:%lx(%s)", > > + tsk->comm, task_pid_nr(tsk), > > + regs->ip, regs->sp, error_code, > > + control_protection_err[err_idx]); > > + print_vma_addr(" in ", regs->ip); > > + pr_cont("\n"); > > + } > > + > > +exit: > > + force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); > > This is definitely wrong for the feature-disabled, !user_mode case. > I will fix it. > Also, are you planning on enabling CET for kernel code too? Yes, kernel protection will be enabled later.
Hi Yu-cheng, Thank you for the patch! Yet something to improve: [auto build test ERROR on asm-generic/master] [also build test ERROR on v4.17 next-20180607] [cannot apply to tip/x86/core mmotm/master] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Yu-cheng-Yu/Control-Flow-Enforcement-Part-2/20180608-111152 base: https://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git master config: i386-randconfig-a0-201822 (attached as .config) compiler: gcc-4.9 (Debian 4.9.4-2) 4.9.4 reproduce: # save the attached .config to linux build tree make ARCH=i386 All errors (new ones prefixed by >>): In file included from arch/x86/include/asm/thread_info.h:53:0, from include/linux/thread_info.h:38, from arch/x86/include/asm/preempt.h:7, from include/linux/preempt.h:81, from include/linux/rcupdate.h:40, from include/linux/rculist.h:11, from include/linux/pid.h:5, from include/linux/sched.h:14, from include/linux/context_tracking.h:5, from arch/x86/kernel/traps.c:15: arch/x86/kernel/traps.c: In function 'do_control_protection': >> arch/x86/kernel/traps.c:605:27: error: 'X86_FEATURE_SHSTK' undeclared (first use in this function) if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^ arch/x86/kernel/traps.c:605:27: note: each undeclared identifier is reported only once for each function it appears in if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^ >> arch/x86/kernel/traps.c:606:27: error: 'X86_FEATURE_IBT' undeclared (first use in this function) !cpu_feature_enabled(X86_FEATURE_IBT)) { ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^ vim +/X86_FEATURE_SHSTK +605 arch/x86/kernel/traps.c 589 590 /* 591 * When a control protection exception occurs, send a signal 592 * to the responsible application. Currently, control 593 * protection is only enabled for the user mode. This 594 * exception should not come from the kernel mode. 595 */ 596 dotraplinkage void 597 do_control_protection(struct pt_regs *regs, long error_code) 598 { 599 struct task_struct *tsk; 600 601 RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); 602 cond_local_irq_enable(regs); 603 604 tsk = current; > 605 if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && > 606 !cpu_feature_enabled(X86_FEATURE_IBT)) { 607 goto exit; 608 } 609 610 if (!user_mode(regs)) { 611 tsk->thread.error_code = error_code; 612 tsk->thread.trap_nr = X86_TRAP_CP; 613 if (notify_die(DIE_TRAP, "control protection fault", regs, 614 error_code, X86_TRAP_CP, SIGSEGV) != NOTIFY_STOP) 615 die("control protection fault", regs, error_code); 616 return; 617 } 618 619 tsk->thread.error_code = error_code; 620 tsk->thread.trap_nr = X86_TRAP_CP; 621 622 if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && 623 printk_ratelimit()) { 624 unsigned int max_idx, err_idx; 625 626 max_idx = ARRAY_SIZE(control_protection_err) - 1; 627 err_idx = min((unsigned int)error_code - 1, max_idx); 628 pr_info("%s[%d] control protection ip:%lx sp:%lx error:%lx(%s)", 629 tsk->comm, task_pid_nr(tsk), 630 regs->ip, regs->sp, error_code, 631 control_protection_err[err_idx]); 632 print_vma_addr(" in ", regs->ip); 633 pr_cont("\n"); 634 } 635 636 exit: 637 force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); 638 } 639 NOKPROBE_SYMBOL(do_control_protection); 640 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Yu-cheng, Thank you for the patch! Yet something to improve: [auto build test ERROR on asm-generic/master] [also build test ERROR on v4.17 next-20180607] [cannot apply to tip/x86/core mmotm/master] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Yu-cheng-Yu/Control-Flow-Enforcement-Part-2/20180608-111152 base: https://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git master config: i386-tinyconfig (attached as .config) compiler: gcc-7 (Debian 7.3.0-16) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=i386 All errors (new ones prefixed by >>): In file included from arch/x86/include/asm/thread_info.h:53:0, from include/linux/thread_info.h:38, from arch/x86/include/asm/preempt.h:7, from include/linux/preempt.h:81, from include/linux/rcupdate.h:40, from include/linux/rculist.h:11, from include/linux/pid.h:5, from include/linux/sched.h:14, from include/linux/context_tracking.h:5, from arch/x86/kernel/traps.c:15: arch/x86/kernel/traps.c: In function 'do_control_protection': >> arch/x86/kernel/traps.c:605:27: error: 'X86_FEATURE_SHSTK' undeclared (first use in this function); did you mean 'X86_FEATURE_EST'? if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^~~ arch/x86/kernel/traps.c:605:27: note: each undeclared identifier is reported only once for each function it appears in if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^~~ >> arch/x86/kernel/traps.c:606:27: error: 'X86_FEATURE_IBT' undeclared (first use in this function); did you mean 'X86_FEATURE_IBS'? !cpu_feature_enabled(X86_FEATURE_IBT)) { ^ arch/x86/include/asm/cpufeature.h:127:24: note: in definition of macro 'cpu_feature_enabled' (__builtin_constant_p(bit) && DISABLED_MASK_BIT_SET(bit) ? 0 : static_cpu_has(bit)) ^~~ vim +605 arch/x86/kernel/traps.c 589 590 /* 591 * When a control protection exception occurs, send a signal 592 * to the responsible application. Currently, control 593 * protection is only enabled for the user mode. This 594 * exception should not come from the kernel mode. 595 */ 596 dotraplinkage void 597 do_control_protection(struct pt_regs *regs, long error_code) 598 { 599 struct task_struct *tsk; 600 601 RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); 602 cond_local_irq_enable(regs); 603 604 tsk = current; > 605 if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && > 606 !cpu_feature_enabled(X86_FEATURE_IBT)) { 607 goto exit; 608 } 609 610 if (!user_mode(regs)) { 611 tsk->thread.error_code = error_code; 612 tsk->thread.trap_nr = X86_TRAP_CP; 613 if (notify_die(DIE_TRAP, "control protection fault", regs, 614 error_code, X86_TRAP_CP, SIGSEGV) != NOTIFY_STOP) 615 die("control protection fault", regs, error_code); 616 return; 617 } 618 619 tsk->thread.error_code = error_code; 620 tsk->thread.trap_nr = X86_TRAP_CP; 621 622 if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && 623 printk_ratelimit()) { 624 unsigned int max_idx, err_idx; 625 626 max_idx = ARRAY_SIZE(control_protection_err) - 1; 627 err_idx = min((unsigned int)error_code - 1, max_idx); 628 pr_info("%s[%d] control protection ip:%lx sp:%lx error:%lx(%s)", 629 tsk->comm, task_pid_nr(tsk), 630 regs->ip, regs->sp, error_code, 631 control_protection_err[err_idx]); 632 print_vma_addr(" in ", regs->ip); 633 pr_cont("\n"); 634 } 635 636 exit: 637 force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); 638 } 639 NOKPROBE_SYMBOL(do_control_protection); 640 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index bef8e2b202a8..14b63ef0d7d8 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -1070,6 +1070,11 @@ ENTRY(general_protection) jmp common_exception END(general_protection) +ENTRY(control_protection) + pushl $do_control_protection + jmp common_exception +END(control_protection) + #ifdef CONFIG_KVM_GUEST ENTRY(async_page_fault) ASM_CLAC diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index 3166b9674429..5230f705d229 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -999,7 +999,7 @@ idtentry spurious_interrupt_bug do_spurious_interrupt_bug has_error_code=0 idtentry coprocessor_error do_coprocessor_error has_error_code=0 idtentry alignment_check do_alignment_check has_error_code=1 idtentry simd_coprocessor_error do_simd_coprocessor_error has_error_code=0 - +idtentry control_protection do_control_protection has_error_code=1 /* * Reload gs selector with exception handling diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 3de69330e6c5..5196050ff3d5 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -26,6 +26,7 @@ asmlinkage void invalid_TSS(void); asmlinkage void segment_not_present(void); asmlinkage void stack_segment(void); asmlinkage void general_protection(void); +asmlinkage void control_protection(void); asmlinkage void page_fault(void); asmlinkage void async_page_fault(void); asmlinkage void spurious_interrupt_bug(void); @@ -77,6 +78,7 @@ dotraplinkage void do_stack_segment(struct pt_regs *, long); dotraplinkage void do_double_fault(struct pt_regs *, long); #endif dotraplinkage void do_general_protection(struct pt_regs *, long); +dotraplinkage void do_control_protection(struct pt_regs *, long); dotraplinkage void do_page_fault(struct pt_regs *, unsigned long); dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long); dotraplinkage void do_coprocessor_error(struct pt_regs *, long); @@ -142,6 +144,7 @@ enum { X86_TRAP_AC, /* 17, Alignment Check */ X86_TRAP_MC, /* 18, Machine Check */ X86_TRAP_XF, /* 19, SIMD Floating-Point Exception */ + X86_TRAP_CP = 21, /* 21 Control Protection Fault */ X86_TRAP_IRET = 32, /* 32, IRET Exception */ }; diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index 2c3a1b4294eb..d00493709cec 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -85,6 +85,7 @@ static const __initconst struct idt_data def_idts[] = { INTG(X86_TRAP_MF, coprocessor_error), INTG(X86_TRAP_AC, alignment_check), INTG(X86_TRAP_XF, simd_coprocessor_error), + INTG(X86_TRAP_CP, control_protection), #ifdef CONFIG_X86_32 TSKG(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS), diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 03f3d7695dac..4e8769a19aaf 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -577,6 +577,67 @@ do_general_protection(struct pt_regs *regs, long error_code) } NOKPROBE_SYMBOL(do_general_protection); +static const char *control_protection_err[] = +{ + "near-ret", + "far-ret/iret", + "endbranch", + "rstorssp", + "setssbsy", + "unknown", +}; + +/* + * When a control protection exception occurs, send a signal + * to the responsible application. Currently, control + * protection is only enabled for the user mode. This + * exception should not come from the kernel mode. + */ +dotraplinkage void +do_control_protection(struct pt_regs *regs, long error_code) +{ + struct task_struct *tsk; + + RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); + cond_local_irq_enable(regs); + + tsk = current; + if (!cpu_feature_enabled(X86_FEATURE_SHSTK) && + !cpu_feature_enabled(X86_FEATURE_IBT)) { + goto exit; + } + + if (!user_mode(regs)) { + tsk->thread.error_code = error_code; + tsk->thread.trap_nr = X86_TRAP_CP; + if (notify_die(DIE_TRAP, "control protection fault", regs, + error_code, X86_TRAP_CP, SIGSEGV) != NOTIFY_STOP) + die("control protection fault", regs, error_code); + return; + } + + tsk->thread.error_code = error_code; + tsk->thread.trap_nr = X86_TRAP_CP; + + if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && + printk_ratelimit()) { + unsigned int max_idx, err_idx; + + max_idx = ARRAY_SIZE(control_protection_err) - 1; + err_idx = min((unsigned int)error_code - 1, max_idx); + pr_info("%s[%d] control protection ip:%lx sp:%lx error:%lx(%s)", + tsk->comm, task_pid_nr(tsk), + regs->ip, regs->sp, error_code, + control_protection_err[err_idx]); + print_vma_addr(" in ", regs->ip); + pr_cont("\n"); + } + +exit: + force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); +} +NOKPROBE_SYMBOL(do_control_protection); + dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) { #ifdef CONFIG_DYNAMIC_FTRACE
A control protection exception is triggered when a control flow transfer attempt violated shadow stack or indirect branch tracking constraints. For example, the return address for a RET instruction differs from the safe copy on the shadow stack; or a JMP instruction arrives at a non- ENDBR instruction. The control protection exception handler works in a similar way as the general protection fault handler. Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> --- arch/x86/entry/entry_32.S | 5 ++++ arch/x86/entry/entry_64.S | 2 +- arch/x86/include/asm/traps.h | 3 +++ arch/x86/kernel/idt.c | 1 + arch/x86/kernel/traps.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-)