diff options
Diffstat (limited to 'arch/i386/kernel/traps.c')
| -rw-r--r-- | arch/i386/kernel/traps.c | 242 |
1 files changed, 142 insertions, 100 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 7e9edafffd8a..a13037fe0ee3 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/kprobes.h> | 28 | #include <linux/kprobes.h> |
| 29 | #include <linux/kexec.h> | 29 | #include <linux/kexec.h> |
| 30 | #include <linux/unwind.h> | 30 | #include <linux/unwind.h> |
| 31 | #include <linux/uaccess.h> | ||
| 31 | 32 | ||
| 32 | #ifdef CONFIG_EISA | 33 | #ifdef CONFIG_EISA |
| 33 | #include <linux/ioport.h> | 34 | #include <linux/ioport.h> |
| @@ -40,7 +41,6 @@ | |||
| 40 | 41 | ||
| 41 | #include <asm/processor.h> | 42 | #include <asm/processor.h> |
| 42 | #include <asm/system.h> | 43 | #include <asm/system.h> |
| 43 | #include <asm/uaccess.h> | ||
| 44 | #include <asm/io.h> | 44 | #include <asm/io.h> |
| 45 | #include <asm/atomic.h> | 45 | #include <asm/atomic.h> |
| 46 | #include <asm/debugreg.h> | 46 | #include <asm/debugreg.h> |
| @@ -51,6 +51,7 @@ | |||
| 51 | #include <asm/smp.h> | 51 | #include <asm/smp.h> |
| 52 | #include <asm/arch_hooks.h> | 52 | #include <asm/arch_hooks.h> |
| 53 | #include <asm/kdebug.h> | 53 | #include <asm/kdebug.h> |
| 54 | #include <asm/stacktrace.h> | ||
| 54 | 55 | ||
| 55 | #include <linux/module.h> | 56 | #include <linux/module.h> |
| 56 | 57 | ||
| @@ -118,26 +119,16 @@ static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | |||
| 118 | p < (void *)tinfo + THREAD_SIZE - 3; | 119 | p < (void *)tinfo + THREAD_SIZE - 3; |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | /* | ||
| 122 | * Print one address/symbol entries per line. | ||
| 123 | */ | ||
| 124 | static inline void print_addr_and_symbol(unsigned long addr, char *log_lvl) | ||
| 125 | { | ||
| 126 | printk(" [<%08lx>] ", addr); | ||
| 127 | |||
| 128 | print_symbol("%s\n", addr); | ||
| 129 | } | ||
| 130 | |||
| 131 | static inline unsigned long print_context_stack(struct thread_info *tinfo, | 122 | static inline unsigned long print_context_stack(struct thread_info *tinfo, |
| 132 | unsigned long *stack, unsigned long ebp, | 123 | unsigned long *stack, unsigned long ebp, |
| 133 | char *log_lvl) | 124 | struct stacktrace_ops *ops, void *data) |
| 134 | { | 125 | { |
| 135 | unsigned long addr; | 126 | unsigned long addr; |
| 136 | 127 | ||
| 137 | #ifdef CONFIG_FRAME_POINTER | 128 | #ifdef CONFIG_FRAME_POINTER |
| 138 | while (valid_stack_ptr(tinfo, (void *)ebp)) { | 129 | while (valid_stack_ptr(tinfo, (void *)ebp)) { |
| 139 | addr = *(unsigned long *)(ebp + 4); | 130 | addr = *(unsigned long *)(ebp + 4); |
| 140 | print_addr_and_symbol(addr, log_lvl); | 131 | ops->address(data, addr); |
| 141 | /* | 132 | /* |
| 142 | * break out of recursive entries (such as | 133 | * break out of recursive entries (such as |
| 143 | * end_of_stack_stop_unwind_function): | 134 | * end_of_stack_stop_unwind_function): |
| @@ -150,30 +141,37 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo, | |||
| 150 | while (valid_stack_ptr(tinfo, stack)) { | 141 | while (valid_stack_ptr(tinfo, stack)) { |
| 151 | addr = *stack++; | 142 | addr = *stack++; |
| 152 | if (__kernel_text_address(addr)) | 143 | if (__kernel_text_address(addr)) |
| 153 | print_addr_and_symbol(addr, log_lvl); | 144 | ops->address(data, addr); |
| 154 | } | 145 | } |
| 155 | #endif | 146 | #endif |
| 156 | return ebp; | 147 | return ebp; |
| 157 | } | 148 | } |
| 158 | 149 | ||
| 150 | struct ops_and_data { | ||
| 151 | struct stacktrace_ops *ops; | ||
| 152 | void *data; | ||
| 153 | }; | ||
| 154 | |||
| 159 | static asmlinkage int | 155 | static asmlinkage int |
| 160 | show_trace_unwind(struct unwind_frame_info *info, void *log_lvl) | 156 | dump_trace_unwind(struct unwind_frame_info *info, void *data) |
| 161 | { | 157 | { |
| 158 | struct ops_and_data *oad = (struct ops_and_data *)data; | ||
| 162 | int n = 0; | 159 | int n = 0; |
| 163 | 160 | ||
| 164 | while (unwind(info) == 0 && UNW_PC(info)) { | 161 | while (unwind(info) == 0 && UNW_PC(info)) { |
| 165 | n++; | 162 | n++; |
| 166 | print_addr_and_symbol(UNW_PC(info), log_lvl); | 163 | oad->ops->address(oad->data, UNW_PC(info)); |
| 167 | if (arch_unw_user_mode(info)) | 164 | if (arch_unw_user_mode(info)) |
| 168 | break; | 165 | break; |
| 169 | } | 166 | } |
| 170 | return n; | 167 | return n; |
| 171 | } | 168 | } |
| 172 | 169 | ||
| 173 | static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | 170 | void dump_trace(struct task_struct *task, struct pt_regs *regs, |
| 174 | unsigned long *stack, char *log_lvl) | 171 | unsigned long *stack, |
| 172 | struct stacktrace_ops *ops, void *data) | ||
| 175 | { | 173 | { |
| 176 | unsigned long ebp; | 174 | unsigned long ebp = 0; |
| 177 | 175 | ||
| 178 | if (!task) | 176 | if (!task) |
| 179 | task = current; | 177 | task = current; |
| @@ -181,54 +179,116 @@ static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
| 181 | if (call_trace >= 0) { | 179 | if (call_trace >= 0) { |
| 182 | int unw_ret = 0; | 180 | int unw_ret = 0; |
| 183 | struct unwind_frame_info info; | 181 | struct unwind_frame_info info; |
| 182 | struct ops_and_data oad = { .ops = ops, .data = data }; | ||
| 184 | 183 | ||
| 185 | if (regs) { | 184 | if (regs) { |
| 186 | if (unwind_init_frame_info(&info, task, regs) == 0) | 185 | if (unwind_init_frame_info(&info, task, regs) == 0) |
| 187 | unw_ret = show_trace_unwind(&info, log_lvl); | 186 | unw_ret = dump_trace_unwind(&info, &oad); |
| 188 | } else if (task == current) | 187 | } else if (task == current) |
| 189 | unw_ret = unwind_init_running(&info, show_trace_unwind, log_lvl); | 188 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); |
| 190 | else { | 189 | else { |
| 191 | if (unwind_init_blocked(&info, task) == 0) | 190 | if (unwind_init_blocked(&info, task) == 0) |
| 192 | unw_ret = show_trace_unwind(&info, log_lvl); | 191 | unw_ret = dump_trace_unwind(&info, &oad); |
| 193 | } | 192 | } |
| 194 | if (unw_ret > 0) { | 193 | if (unw_ret > 0) { |
| 195 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | 194 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
| 196 | print_symbol("DWARF2 unwinder stuck at %s\n", | 195 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", |
| 197 | UNW_PC(&info)); | 196 | UNW_PC(&info)); |
| 198 | if (UNW_SP(&info) >= PAGE_OFFSET) { | 197 | if (UNW_SP(&info) >= PAGE_OFFSET) { |
| 199 | printk("Leftover inexact backtrace:\n"); | 198 | ops->warning(data, "Leftover inexact backtrace:\n"); |
| 200 | stack = (void *)UNW_SP(&info); | 199 | stack = (void *)UNW_SP(&info); |
| 200 | if (!stack) | ||
| 201 | return; | ||
| 202 | ebp = UNW_FP(&info); | ||
| 201 | } else | 203 | } else |
| 202 | printk("Full inexact backtrace again:\n"); | 204 | ops->warning(data, "Full inexact backtrace again:\n"); |
| 203 | } else if (call_trace >= 1) | 205 | } else if (call_trace >= 1) |
| 204 | return; | 206 | return; |
| 205 | else | 207 | else |
| 206 | printk("Full inexact backtrace again:\n"); | 208 | ops->warning(data, "Full inexact backtrace again:\n"); |
| 207 | } else | 209 | } else |
| 208 | printk("Inexact backtrace:\n"); | 210 | ops->warning(data, "Inexact backtrace:\n"); |
| 211 | } | ||
| 212 | if (!stack) { | ||
| 213 | unsigned long dummy; | ||
| 214 | stack = &dummy; | ||
| 215 | if (task && task != current) | ||
| 216 | stack = (unsigned long *)task->thread.esp; | ||
| 209 | } | 217 | } |
| 210 | 218 | ||
| 211 | if (task == current) { | 219 | #ifdef CONFIG_FRAME_POINTER |
| 212 | /* Grab ebp right from our regs */ | 220 | if (!ebp) { |
| 213 | asm ("movl %%ebp, %0" : "=r" (ebp) : ); | 221 | if (task == current) { |
| 214 | } else { | 222 | /* Grab ebp right from our regs */ |
| 215 | /* ebp is the last reg pushed by switch_to */ | 223 | asm ("movl %%ebp, %0" : "=r" (ebp) : ); |
| 216 | ebp = *(unsigned long *) task->thread.esp; | 224 | } else { |
| 225 | /* ebp is the last reg pushed by switch_to */ | ||
| 226 | ebp = *(unsigned long *) task->thread.esp; | ||
| 227 | } | ||
| 217 | } | 228 | } |
| 229 | #endif | ||
| 218 | 230 | ||
| 219 | while (1) { | 231 | while (1) { |
| 220 | struct thread_info *context; | 232 | struct thread_info *context; |
| 221 | context = (struct thread_info *) | 233 | context = (struct thread_info *) |
| 222 | ((unsigned long)stack & (~(THREAD_SIZE - 1))); | 234 | ((unsigned long)stack & (~(THREAD_SIZE - 1))); |
| 223 | ebp = print_context_stack(context, stack, ebp, log_lvl); | 235 | ebp = print_context_stack(context, stack, ebp, ops, data); |
| 236 | /* Should be after the line below, but somewhere | ||
| 237 | in early boot context comes out corrupted and we | ||
| 238 | can't reference it -AK */ | ||
| 239 | if (ops->stack(data, "IRQ") < 0) | ||
| 240 | break; | ||
| 224 | stack = (unsigned long*)context->previous_esp; | 241 | stack = (unsigned long*)context->previous_esp; |
| 225 | if (!stack) | 242 | if (!stack) |
| 226 | break; | 243 | break; |
| 227 | printk("%s =======================\n", log_lvl); | ||
| 228 | } | 244 | } |
| 229 | } | 245 | } |
| 246 | EXPORT_SYMBOL(dump_trace); | ||
| 247 | |||
| 248 | static void | ||
| 249 | print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) | ||
| 250 | { | ||
| 251 | printk(data); | ||
| 252 | print_symbol(msg, symbol); | ||
| 253 | printk("\n"); | ||
| 254 | } | ||
| 255 | |||
| 256 | static void print_trace_warning(void *data, char *msg) | ||
| 257 | { | ||
| 258 | printk("%s%s\n", (char *)data, msg); | ||
| 259 | } | ||
| 260 | |||
| 261 | static int print_trace_stack(void *data, char *name) | ||
| 262 | { | ||
| 263 | return 0; | ||
| 264 | } | ||
| 265 | |||
| 266 | /* | ||
| 267 | * Print one address/symbol entries per line. | ||
| 268 | */ | ||
| 269 | static void print_trace_address(void *data, unsigned long addr) | ||
| 270 | { | ||
| 271 | printk("%s [<%08lx>] ", (char *)data, addr); | ||
| 272 | print_symbol("%s\n", addr); | ||
| 273 | } | ||
| 274 | |||
| 275 | static struct stacktrace_ops print_trace_ops = { | ||
| 276 | .warning = print_trace_warning, | ||
| 277 | .warning_symbol = print_trace_warning_symbol, | ||
| 278 | .stack = print_trace_stack, | ||
| 279 | .address = print_trace_address, | ||
| 280 | }; | ||
| 281 | |||
| 282 | static void | ||
| 283 | show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | ||
| 284 | unsigned long * stack, char *log_lvl) | ||
| 285 | { | ||
| 286 | dump_trace(task, regs, stack, &print_trace_ops, log_lvl); | ||
| 287 | printk("%s =======================\n", log_lvl); | ||
| 288 | } | ||
| 230 | 289 | ||
| 231 | void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long * stack) | 290 | void show_trace(struct task_struct *task, struct pt_regs *regs, |
| 291 | unsigned long * stack) | ||
| 232 | { | 292 | { |
| 233 | show_trace_log_lvl(task, regs, stack, ""); | 293 | show_trace_log_lvl(task, regs, stack, ""); |
| 234 | } | 294 | } |
| @@ -291,8 +351,9 @@ void show_registers(struct pt_regs *regs) | |||
| 291 | ss = regs->xss & 0xffff; | 351 | ss = regs->xss & 0xffff; |
| 292 | } | 352 | } |
| 293 | print_modules(); | 353 | print_modules(); |
| 294 | printk(KERN_EMERG "CPU: %d\nEIP: %04x:[<%08lx>] %s VLI\n" | 354 | printk(KERN_EMERG "CPU: %d\n" |
| 295 | "EFLAGS: %08lx (%s %.*s) \n", | 355 | KERN_EMERG "EIP: %04x:[<%08lx>] %s VLI\n" |
| 356 | KERN_EMERG "EFLAGS: %08lx (%s %.*s)\n", | ||
| 296 | smp_processor_id(), 0xffff & regs->xcs, regs->eip, | 357 | smp_processor_id(), 0xffff & regs->xcs, regs->eip, |
| 297 | print_tainted(), regs->eflags, system_utsname.release, | 358 | print_tainted(), regs->eflags, system_utsname.release, |
| 298 | (int)strcspn(system_utsname.version, " "), | 359 | (int)strcspn(system_utsname.version, " "), |
| @@ -313,6 +374,8 @@ void show_registers(struct pt_regs *regs) | |||
| 313 | */ | 374 | */ |
| 314 | if (in_kernel) { | 375 | if (in_kernel) { |
| 315 | u8 __user *eip; | 376 | u8 __user *eip; |
| 377 | int code_bytes = 64; | ||
| 378 | unsigned char c; | ||
| 316 | 379 | ||
| 317 | printk("\n" KERN_EMERG "Stack: "); | 380 | printk("\n" KERN_EMERG "Stack: "); |
| 318 | show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG); | 381 | show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG); |
| @@ -320,9 +383,12 @@ void show_registers(struct pt_regs *regs) | |||
| 320 | printk(KERN_EMERG "Code: "); | 383 | printk(KERN_EMERG "Code: "); |
| 321 | 384 | ||
| 322 | eip = (u8 __user *)regs->eip - 43; | 385 | eip = (u8 __user *)regs->eip - 43; |
| 323 | for (i = 0; i < 64; i++, eip++) { | 386 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { |
| 324 | unsigned char c; | 387 | /* try starting at EIP */ |
| 325 | 388 | eip = (u8 __user *)regs->eip; | |
| 389 | code_bytes = 32; | ||
| 390 | } | ||
| 391 | for (i = 0; i < code_bytes; i++, eip++) { | ||
| 326 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { | 392 | if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) { |
| 327 | printk(" Bad EIP value."); | 393 | printk(" Bad EIP value."); |
| 328 | break; | 394 | break; |
| @@ -343,7 +409,7 @@ static void handle_BUG(struct pt_regs *regs) | |||
| 343 | 409 | ||
| 344 | if (eip < PAGE_OFFSET) | 410 | if (eip < PAGE_OFFSET) |
| 345 | return; | 411 | return; |
| 346 | if (__get_user(ud2, (unsigned short __user *)eip)) | 412 | if (probe_kernel_address((unsigned short __user *)eip, ud2)) |
| 347 | return; | 413 | return; |
| 348 | if (ud2 != 0x0b0f) | 414 | if (ud2 != 0x0b0f) |
| 349 | return; | 415 | return; |
| @@ -356,7 +422,8 @@ static void handle_BUG(struct pt_regs *regs) | |||
| 356 | char *file; | 422 | char *file; |
| 357 | char c; | 423 | char c; |
| 358 | 424 | ||
| 359 | if (__get_user(line, (unsigned short __user *)(eip + 2))) | 425 | if (probe_kernel_address((unsigned short __user *)(eip + 2), |
| 426 | line)) | ||
| 360 | break; | 427 | break; |
| 361 | if (__get_user(file, (char * __user *)(eip + 4)) || | 428 | if (__get_user(file, (char * __user *)(eip + 4)) || |
| 362 | (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) | 429 | (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) |
| @@ -629,18 +696,24 @@ gp_in_kernel: | |||
| 629 | } | 696 | } |
| 630 | } | 697 | } |
| 631 | 698 | ||
| 632 | static void mem_parity_error(unsigned char reason, struct pt_regs * regs) | 699 | static __kprobes void |
| 700 | mem_parity_error(unsigned char reason, struct pt_regs * regs) | ||
| 633 | { | 701 | { |
| 634 | printk(KERN_EMERG "Uhhuh. NMI received. Dazed and confused, but trying " | 702 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " |
| 635 | "to continue\n"); | 703 | "CPU %d.\n", reason, smp_processor_id()); |
| 636 | printk(KERN_EMERG "You probably have a hardware problem with your RAM " | 704 | printk(KERN_EMERG "You probably have a hardware problem with your RAM " |
| 637 | "chips\n"); | 705 | "chips\n"); |
| 706 | if (panic_on_unrecovered_nmi) | ||
| 707 | panic("NMI: Not continuing"); | ||
| 708 | |||
| 709 | printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); | ||
| 638 | 710 | ||
| 639 | /* Clear and disable the memory parity error line. */ | 711 | /* Clear and disable the memory parity error line. */ |
| 640 | clear_mem_error(reason); | 712 | clear_mem_error(reason); |
| 641 | } | 713 | } |
| 642 | 714 | ||
| 643 | static void io_check_error(unsigned char reason, struct pt_regs * regs) | 715 | static __kprobes void |
| 716 | io_check_error(unsigned char reason, struct pt_regs * regs) | ||
| 644 | { | 717 | { |
| 645 | unsigned long i; | 718 | unsigned long i; |
| 646 | 719 | ||
| @@ -656,7 +729,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs) | |||
| 656 | outb(reason, 0x61); | 729 | outb(reason, 0x61); |
| 657 | } | 730 | } |
| 658 | 731 | ||
| 659 | static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) | 732 | static __kprobes void |
| 733 | unknown_nmi_error(unsigned char reason, struct pt_regs * regs) | ||
| 660 | { | 734 | { |
| 661 | #ifdef CONFIG_MCA | 735 | #ifdef CONFIG_MCA |
| 662 | /* Might actually be able to figure out what the guilty party | 736 | /* Might actually be able to figure out what the guilty party |
| @@ -666,15 +740,18 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) | |||
| 666 | return; | 740 | return; |
| 667 | } | 741 | } |
| 668 | #endif | 742 | #endif |
| 669 | printk("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", | 743 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " |
| 670 | reason, smp_processor_id()); | 744 | "CPU %d.\n", reason, smp_processor_id()); |
| 671 | printk("Dazed and confused, but trying to continue\n"); | 745 | printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n"); |
| 672 | printk("Do you have a strange power saving mode enabled?\n"); | 746 | if (panic_on_unrecovered_nmi) |
| 747 | panic("NMI: Not continuing"); | ||
| 748 | |||
| 749 | printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); | ||
| 673 | } | 750 | } |
| 674 | 751 | ||
| 675 | static DEFINE_SPINLOCK(nmi_print_lock); | 752 | static DEFINE_SPINLOCK(nmi_print_lock); |
| 676 | 753 | ||
| 677 | void die_nmi (struct pt_regs *regs, const char *msg) | 754 | void __kprobes die_nmi(struct pt_regs *regs, const char *msg) |
| 678 | { | 755 | { |
| 679 | if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) == | 756 | if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) == |
| 680 | NOTIFY_STOP) | 757 | NOTIFY_STOP) |
| @@ -706,7 +783,7 @@ void die_nmi (struct pt_regs *regs, const char *msg) | |||
| 706 | do_exit(SIGSEGV); | 783 | do_exit(SIGSEGV); |
| 707 | } | 784 | } |
| 708 | 785 | ||
| 709 | static void default_do_nmi(struct pt_regs * regs) | 786 | static __kprobes void default_do_nmi(struct pt_regs * regs) |
| 710 | { | 787 | { |
| 711 | unsigned char reason = 0; | 788 | unsigned char reason = 0; |
| 712 | 789 | ||
| @@ -723,12 +800,12 @@ static void default_do_nmi(struct pt_regs * regs) | |||
| 723 | * Ok, so this is none of the documented NMI sources, | 800 | * Ok, so this is none of the documented NMI sources, |
| 724 | * so it must be the NMI watchdog. | 801 | * so it must be the NMI watchdog. |
| 725 | */ | 802 | */ |
| 726 | if (nmi_watchdog) { | 803 | if (nmi_watchdog_tick(regs, reason)) |
| 727 | nmi_watchdog_tick(regs); | ||
| 728 | return; | 804 | return; |
| 729 | } | 805 | if (!do_nmi_callback(regs, smp_processor_id())) |
| 730 | #endif | 806 | #endif |
| 731 | unknown_nmi_error(reason, regs); | 807 | unknown_nmi_error(reason, regs); |
| 808 | |||
| 732 | return; | 809 | return; |
| 733 | } | 810 | } |
| 734 | if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) | 811 | if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) |
| @@ -744,14 +821,7 @@ static void default_do_nmi(struct pt_regs * regs) | |||
| 744 | reassert_nmi(); | 821 | reassert_nmi(); |
| 745 | } | 822 | } |
| 746 | 823 | ||
| 747 | static int dummy_nmi_callback(struct pt_regs * regs, int cpu) | 824 | fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code) |
| 748 | { | ||
| 749 | return 0; | ||
| 750 | } | ||
| 751 | |||
| 752 | static nmi_callback_t nmi_callback = dummy_nmi_callback; | ||
| 753 | |||
| 754 | fastcall void do_nmi(struct pt_regs * regs, long error_code) | ||
| 755 | { | 825 | { |
| 756 | int cpu; | 826 | int cpu; |
| 757 | 827 | ||
| @@ -761,25 +831,11 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code) | |||
| 761 | 831 | ||
| 762 | ++nmi_count(cpu); | 832 | ++nmi_count(cpu); |
| 763 | 833 | ||
| 764 | if (!rcu_dereference(nmi_callback)(regs, cpu)) | 834 | default_do_nmi(regs); |
| 765 | default_do_nmi(regs); | ||
| 766 | 835 | ||
| 767 | nmi_exit(); | 836 | nmi_exit(); |
| 768 | } | 837 | } |
| 769 | 838 | ||
| 770 | void set_nmi_callback(nmi_callback_t callback) | ||
| 771 | { | ||
| 772 | vmalloc_sync_all(); | ||
| 773 | rcu_assign_pointer(nmi_callback, callback); | ||
| 774 | } | ||
| 775 | EXPORT_SYMBOL_GPL(set_nmi_callback); | ||
| 776 | |||
| 777 | void unset_nmi_callback(void) | ||
| 778 | { | ||
| 779 | nmi_callback = dummy_nmi_callback; | ||
| 780 | } | ||
| 781 | EXPORT_SYMBOL_GPL(unset_nmi_callback); | ||
| 782 | |||
| 783 | #ifdef CONFIG_KPROBES | 839 | #ifdef CONFIG_KPROBES |
| 784 | fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) | 840 | fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) |
| 785 | { | 841 | { |
| @@ -1119,20 +1175,6 @@ void __init trap_init_f00f_bug(void) | |||
| 1119 | } | 1175 | } |
| 1120 | #endif | 1176 | #endif |
| 1121 | 1177 | ||
| 1122 | #define _set_gate(gate_addr,type,dpl,addr,seg) \ | ||
| 1123 | do { \ | ||
| 1124 | int __d0, __d1; \ | ||
| 1125 | __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ | ||
| 1126 | "movw %4,%%dx\n\t" \ | ||
| 1127 | "movl %%eax,%0\n\t" \ | ||
| 1128 | "movl %%edx,%1" \ | ||
| 1129 | :"=m" (*((long *) (gate_addr))), \ | ||
| 1130 | "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \ | ||
| 1131 | :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ | ||
| 1132 | "3" ((char *) (addr)),"2" ((seg) << 16)); \ | ||
| 1133 | } while (0) | ||
| 1134 | |||
| 1135 | |||
| 1136 | /* | 1178 | /* |
| 1137 | * This needs to use 'idt_table' rather than 'idt', and | 1179 | * This needs to use 'idt_table' rather than 'idt', and |
| 1138 | * thus use the _nonmapped_ version of the IDT, as the | 1180 | * thus use the _nonmapped_ version of the IDT, as the |
| @@ -1141,7 +1183,7 @@ do { \ | |||
| 1141 | */ | 1183 | */ |
| 1142 | void set_intr_gate(unsigned int n, void *addr) | 1184 | void set_intr_gate(unsigned int n, void *addr) |
| 1143 | { | 1185 | { |
| 1144 | _set_gate(idt_table+n,14,0,addr,__KERNEL_CS); | 1186 | _set_gate(n, DESCTYPE_INT, addr, __KERNEL_CS); |
| 1145 | } | 1187 | } |
| 1146 | 1188 | ||
| 1147 | /* | 1189 | /* |
| @@ -1149,22 +1191,22 @@ void set_intr_gate(unsigned int n, void *addr) | |||
| 1149 | */ | 1191 | */ |
| 1150 | static inline void set_system_intr_gate(unsigned int n, void *addr) | 1192 | static inline void set_system_intr_gate(unsigned int n, void *addr) |
| 1151 | { | 1193 | { |
| 1152 | _set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS); | 1194 | _set_gate(n, DESCTYPE_INT | DESCTYPE_DPL3, addr, __KERNEL_CS); |
| 1153 | } | 1195 | } |
| 1154 | 1196 | ||
| 1155 | static void __init set_trap_gate(unsigned int n, void *addr) | 1197 | static void __init set_trap_gate(unsigned int n, void *addr) |
| 1156 | { | 1198 | { |
| 1157 | _set_gate(idt_table+n,15,0,addr,__KERNEL_CS); | 1199 | _set_gate(n, DESCTYPE_TRAP, addr, __KERNEL_CS); |
| 1158 | } | 1200 | } |
| 1159 | 1201 | ||
| 1160 | static void __init set_system_gate(unsigned int n, void *addr) | 1202 | static void __init set_system_gate(unsigned int n, void *addr) |
| 1161 | { | 1203 | { |
| 1162 | _set_gate(idt_table+n,15,3,addr,__KERNEL_CS); | 1204 | _set_gate(n, DESCTYPE_TRAP | DESCTYPE_DPL3, addr, __KERNEL_CS); |
| 1163 | } | 1205 | } |
| 1164 | 1206 | ||
| 1165 | static void __init set_task_gate(unsigned int n, unsigned int gdt_entry) | 1207 | static void __init set_task_gate(unsigned int n, unsigned int gdt_entry) |
| 1166 | { | 1208 | { |
| 1167 | _set_gate(idt_table+n,5,0,0,(gdt_entry<<3)); | 1209 | _set_gate(n, DESCTYPE_TASK, (void *)0, (gdt_entry<<3)); |
| 1168 | } | 1210 | } |
| 1169 | 1211 | ||
| 1170 | 1212 | ||
