diff options
-rw-r--r-- | arch/x86/include/asm/desc.h | 12 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 6 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/common.c | 22 | ||||
-rw-r--r-- | arch/x86/kernel/head_64.S | 4 | ||||
-rw-r--r-- | arch/x86/kernel/nmi.c | 15 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 6 |
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 | ||
36 | extern struct desc_ptr idt_descr; | 36 | extern struct desc_ptr idt_descr; |
37 | extern gate_desc idt_table[]; | 37 | extern gate_desc idt_table[]; |
38 | extern struct desc_ptr nmi_idt_descr; | ||
39 | extern gate_desc nmi_idt_table[]; | ||
38 | 40 | ||
39 | struct gdt_page { | 41 | struct 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 | ||
313 | static 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 | |||
310 | static inline void _set_gate(int gate, unsigned type, void *addr, | 322 | static 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); | |||
402 | DECLARE_PER_CPU(unsigned int, irq_count); | 402 | DECLARE_PER_CPU(unsigned int, irq_count); |
403 | extern unsigned long kernel_eflags; | 403 | extern unsigned long kernel_eflags; |
404 | extern asmlinkage void ignore_sysret(void); | 404 | extern asmlinkage void ignore_sysret(void); |
405 | int is_debug_stack(unsigned long addr); | ||
406 | void debug_stack_set_zero(void); | ||
407 | void 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 | }; |
417 | DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); | 420 | DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); |
418 | #endif | 421 | #endif |
422 | static inline int is_debug_stack(unsigned long addr) { return 0; } | ||
423 | static inline void debug_stack_set_zero(void) { } | ||
424 | static inline void debug_stack_reset(void) { } | ||
419 | #endif /* X86_64 */ | 425 | #endif /* X86_64 */ |
420 | 426 | ||
421 | extern unsigned int xstate_size; | 427 | extern 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 |
1028 | struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table }; | 1028 | struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table }; |
1029 | struct desc_ptr nmi_idt_descr = { NR_VECTORS * 16 - 1, | ||
1030 | (unsigned long) nmi_idt_table }; | ||
1029 | 1031 | ||
1030 | DEFINE_PER_CPU_FIRST(union irq_stack_union, | 1032 | DEFINE_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 | */ |
1091 | DEFINE_PER_CPU(struct orig_ist, orig_ist); | 1093 | DEFINE_PER_CPU(struct orig_ist, orig_ist); |
1092 | 1094 | ||
1095 | static DEFINE_PER_CPU(unsigned long, debug_stack_addr); | ||
1096 | |||
1097 | int 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 | |||
1103 | void debug_stack_set_zero(void) | ||
1104 | { | ||
1105 | load_idt((const struct desc_ptr *)&nmi_idt_descr); | ||
1106 | } | ||
1107 | |||
1108 | void 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 | ||
1095 | DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; | 1115 | DEFINE_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) | |||
417 | ENTRY(idt_table) | 417 | ENTRY(idt_table) |
418 | .skip IDT_ENTRIES * 16 | 418 | .skip IDT_ENTRIES * 16 |
419 | 419 | ||
420 | .align L1_CACHE_BYTES | ||
421 | ENTRY(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 |
422 | ENTRY(empty_zero_page) | 426 | ENTRY(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) | |||
408 | dotraplinkage notrace __kprobes void | 408 | dotraplinkage notrace __kprobes void |
409 | do_nmi(struct pt_regs *regs, long error_code) | 409 | do_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 | ||
421 | void stop_nmi(void) | 436 | void 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 | } |