aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/desc.h12
-rw-r--r--arch/x86/include/asm/processor.h6
-rw-r--r--arch/x86/kernel/cpu/common.c22
-rw-r--r--arch/x86/kernel/head_64.S4
-rw-r--r--arch/x86/kernel/nmi.c15
-rw-r--r--arch/x86/kernel/traps.c6
6 files changed, 65 insertions, 0 deletions
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index 41935fadfdfc..e95822d683f4 100644
--- a/arch/x86/include/asm/desc.h
+++ b/arch/x86/include/asm/desc.h
@@ -35,6 +35,8 @@ static inline void fill_ldt(struct desc_struct *desc, const struct user_desc *in
35 35
36extern struct desc_ptr idt_descr; 36extern struct desc_ptr idt_descr;
37extern gate_desc idt_table[]; 37extern gate_desc idt_table[];
38extern struct desc_ptr nmi_idt_descr;
39extern gate_desc nmi_idt_table[];
38 40
39struct gdt_page { 41struct gdt_page {
40 struct desc_struct gdt[GDT_ENTRIES]; 42 struct desc_struct gdt[GDT_ENTRIES];
@@ -307,6 +309,16 @@ static inline void set_desc_limit(struct desc_struct *desc, unsigned long limit)
307 desc->limit = (limit >> 16) & 0xf; 309 desc->limit = (limit >> 16) & 0xf;
308} 310}
309 311
312#ifdef CONFIG_X86_64
313static inline void set_nmi_gate(int gate, void *addr)
314{
315 gate_desc s;
316
317 pack_gate(&s, GATE_INTERRUPT, (unsigned long)addr, 0, 0, __KERNEL_CS);
318 write_idt_entry(nmi_idt_table, gate, &s);
319}
320#endif
321
310static inline void _set_gate(int gate, unsigned type, void *addr, 322static inline void _set_gate(int gate, unsigned type, void *addr,
311 unsigned dpl, unsigned ist, unsigned seg) 323 unsigned dpl, unsigned ist, unsigned seg)
312{ 324{
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index b650435ffb53..4b39d6d7e3a1 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -402,6 +402,9 @@ DECLARE_PER_CPU(char *, irq_stack_ptr);
402DECLARE_PER_CPU(unsigned int, irq_count); 402DECLARE_PER_CPU(unsigned int, irq_count);
403extern unsigned long kernel_eflags; 403extern unsigned long kernel_eflags;
404extern asmlinkage void ignore_sysret(void); 404extern asmlinkage void ignore_sysret(void);
405int is_debug_stack(unsigned long addr);
406void debug_stack_set_zero(void);
407void debug_stack_reset(void);
405#else /* X86_64 */ 408#else /* X86_64 */
406#ifdef CONFIG_CC_STACKPROTECTOR 409#ifdef CONFIG_CC_STACKPROTECTOR
407/* 410/*
@@ -416,6 +419,9 @@ struct stack_canary {
416}; 419};
417DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); 420DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
418#endif 421#endif
422static inline int is_debug_stack(unsigned long addr) { return 0; }
423static inline void debug_stack_set_zero(void) { }
424static inline void debug_stack_reset(void) { }
419#endif /* X86_64 */ 425#endif /* X86_64 */
420 426
421extern unsigned int xstate_size; 427extern unsigned int xstate_size;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index aa003b13a831..caa404556b9c 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1026,6 +1026,8 @@ __setup("clearcpuid=", setup_disablecpuid);
1026 1026
1027#ifdef CONFIG_X86_64 1027#ifdef CONFIG_X86_64
1028struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table }; 1028struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table };
1029struct desc_ptr nmi_idt_descr = { NR_VECTORS * 16 - 1,
1030 (unsigned long) nmi_idt_table };
1029 1031
1030DEFINE_PER_CPU_FIRST(union irq_stack_union, 1032DEFINE_PER_CPU_FIRST(union irq_stack_union,
1031 irq_stack_union) __aligned(PAGE_SIZE); 1033 irq_stack_union) __aligned(PAGE_SIZE);
@@ -1090,6 +1092,24 @@ unsigned long kernel_eflags;
1090 */ 1092 */
1091DEFINE_PER_CPU(struct orig_ist, orig_ist); 1093DEFINE_PER_CPU(struct orig_ist, orig_ist);
1092 1094
1095static DEFINE_PER_CPU(unsigned long, debug_stack_addr);
1096
1097int is_debug_stack(unsigned long addr)
1098{
1099 return addr <= __get_cpu_var(debug_stack_addr) &&
1100 addr > (__get_cpu_var(debug_stack_addr) - DEBUG_STKSZ);
1101}
1102
1103void debug_stack_set_zero(void)
1104{
1105 load_idt((const struct desc_ptr *)&nmi_idt_descr);
1106}
1107
1108void debug_stack_reset(void)
1109{
1110 load_idt((const struct desc_ptr *)&idt_descr);
1111}
1112
1093#else /* CONFIG_X86_64 */ 1113#else /* CONFIG_X86_64 */
1094 1114
1095DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; 1115DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
@@ -1208,6 +1228,8 @@ void __cpuinit cpu_init(void)
1208 estacks += exception_stack_sizes[v]; 1228 estacks += exception_stack_sizes[v];
1209 oist->ist[v] = t->x86_tss.ist[v] = 1229 oist->ist[v] = t->x86_tss.ist[v] =
1210 (unsigned long)estacks; 1230 (unsigned long)estacks;
1231 if (v == DEBUG_STACK-1)
1232 per_cpu(debug_stack_addr, cpu) = (unsigned long)estacks;
1211 } 1233 }
1212 } 1234 }
1213 1235
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index e11e39478a49..40f4eb3766d1 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -417,6 +417,10 @@ ENTRY(phys_base)
417ENTRY(idt_table) 417ENTRY(idt_table)
418 .skip IDT_ENTRIES * 16 418 .skip IDT_ENTRIES * 16
419 419
420 .align L1_CACHE_BYTES
421ENTRY(nmi_idt_table)
422 .skip IDT_ENTRIES * 16
423
420 __PAGE_ALIGNED_BSS 424 __PAGE_ALIGNED_BSS
421 .align PAGE_SIZE 425 .align PAGE_SIZE
422ENTRY(empty_zero_page) 426ENTRY(empty_zero_page)
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index e88f37b58ddd..de8d4b333f40 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -408,6 +408,18 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
408dotraplinkage notrace __kprobes void 408dotraplinkage notrace __kprobes void
409do_nmi(struct pt_regs *regs, long error_code) 409do_nmi(struct pt_regs *regs, long error_code)
410{ 410{
411 int update_debug_stack = 0;
412
413 /*
414 * If we interrupted a breakpoint, it is possible that
415 * the nmi handler will have breakpoints too. We need to
416 * change the IDT such that breakpoints that happen here
417 * continue to use the NMI stack.
418 */
419 if (unlikely(is_debug_stack(regs->sp))) {
420 debug_stack_set_zero();
421 update_debug_stack = 1;
422 }
411 nmi_enter(); 423 nmi_enter();
412 424
413 inc_irq_stat(__nmi_count); 425 inc_irq_stat(__nmi_count);
@@ -416,6 +428,9 @@ do_nmi(struct pt_regs *regs, long error_code)
416 default_do_nmi(regs); 428 default_do_nmi(regs);
417 429
418 nmi_exit(); 430 nmi_exit();
431
432 if (unlikely(update_debug_stack))
433 debug_stack_reset();
419} 434}
420 435
421void stop_nmi(void) 436void stop_nmi(void)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index a8e3eb83466c..a93c5cabc36a 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -723,4 +723,10 @@ void __init trap_init(void)
723 cpu_init(); 723 cpu_init();
724 724
725 x86_init.irqs.trap_init(); 725 x86_init.irqs.trap_init();
726
727#ifdef CONFIG_X86_64
728 memcpy(&nmi_idt_table, &idt_table, IDT_ENTRIES * 16);
729 set_nmi_gate(1, &debug);
730 set_nmi_gate(3, &int3);
731#endif
726} 732}