diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 5 | ||||
| -rw-r--r-- | arch/i386/kernel/traps.c | 3 | ||||
| -rw-r--r-- | arch/i386/mm/fault.c | 39 | ||||
| -rw-r--r-- | include/linux/kernel.h | 3 | ||||
| -rw-r--r-- | kernel/panic.c | 97 |
5 files changed, 130 insertions, 17 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 880be3a30d8d..7b7382d0f758 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -1126,6 +1126,11 @@ running once the system is up. | |||
| 1126 | pas16= [HW,SCSI] | 1126 | pas16= [HW,SCSI] |
| 1127 | See header of drivers/scsi/pas16.c. | 1127 | See header of drivers/scsi/pas16.c. |
| 1128 | 1128 | ||
| 1129 | pause_on_oops= | ||
| 1130 | Halt all CPUs after the first oops has been printed for | ||
| 1131 | the specified number of seconds. This is to be used if | ||
| 1132 | your oopses keep scrolling off the screen. | ||
| 1133 | |||
| 1129 | pcbit= [HW,ISDN] | 1134 | pcbit= [HW,ISDN] |
| 1130 | 1135 | ||
| 1131 | pcd. [PARIDE] | 1136 | pcd. [PARIDE] |
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 1b7ad4115d81..de5386b01d38 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
| @@ -352,6 +352,8 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
| 352 | static int die_counter; | 352 | static int die_counter; |
| 353 | unsigned long flags; | 353 | unsigned long flags; |
| 354 | 354 | ||
| 355 | oops_enter(); | ||
| 356 | |||
| 355 | if (die.lock_owner != raw_smp_processor_id()) { | 357 | if (die.lock_owner != raw_smp_processor_id()) { |
| 356 | console_verbose(); | 358 | console_verbose(); |
| 357 | spin_lock_irqsave(&die.lock, flags); | 359 | spin_lock_irqsave(&die.lock, flags); |
| @@ -404,6 +406,7 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
| 404 | ssleep(5); | 406 | ssleep(5); |
| 405 | panic("Fatal exception"); | 407 | panic("Fatal exception"); |
| 406 | } | 408 | } |
| 409 | oops_exit(); | ||
| 407 | do_exit(SIGSEGV); | 410 | do_exit(SIGSEGV); |
| 408 | } | 411 | } |
| 409 | 412 | ||
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 47a3b72ec7b6..7f0fcf219a26 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c | |||
| @@ -509,24 +509,31 @@ no_context: | |||
| 509 | 509 | ||
| 510 | bust_spinlocks(1); | 510 | bust_spinlocks(1); |
| 511 | 511 | ||
| 512 | #ifdef CONFIG_X86_PAE | 512 | if (oops_may_print()) { |
| 513 | if (error_code & 16) { | 513 | #ifdef CONFIG_X86_PAE |
| 514 | pte_t *pte = lookup_address(address); | 514 | if (error_code & 16) { |
| 515 | 515 | pte_t *pte = lookup_address(address); | |
| 516 | if (pte && pte_present(*pte) && !pte_exec_kernel(*pte)) | 516 | |
| 517 | printk(KERN_CRIT "kernel tried to execute NX-protected page - exploit attempt? (uid: %d)\n", current->uid); | 517 | if (pte && pte_present(*pte) && !pte_exec_kernel(*pte)) |
| 518 | printk(KERN_CRIT "kernel tried to execute " | ||
| 519 | "NX-protected page - exploit attempt? " | ||
| 520 | "(uid: %d)\n", current->uid); | ||
| 521 | } | ||
| 522 | #endif | ||
| 523 | if (address < PAGE_SIZE) | ||
| 524 | printk(KERN_ALERT "BUG: unable to handle kernel NULL " | ||
| 525 | "pointer dereference"); | ||
| 526 | else | ||
| 527 | printk(KERN_ALERT "BUG: unable to handle kernel paging" | ||
| 528 | " request"); | ||
| 529 | printk(" at virtual address %08lx\n",address); | ||
| 530 | printk(KERN_ALERT " printing eip:\n"); | ||
| 531 | printk("%08lx\n", regs->eip); | ||
| 518 | } | 532 | } |
| 519 | #endif | ||
| 520 | if (address < PAGE_SIZE) | ||
| 521 | printk(KERN_ALERT "BUG: unable to handle kernel NULL pointer dereference"); | ||
| 522 | else | ||
| 523 | printk(KERN_ALERT "BUG: unable to handle kernel paging request"); | ||
| 524 | printk(" at virtual address %08lx\n",address); | ||
| 525 | printk(KERN_ALERT " printing eip:\n"); | ||
| 526 | printk("%08lx\n", regs->eip); | ||
| 527 | page = read_cr3(); | 533 | page = read_cr3(); |
| 528 | page = ((unsigned long *) __va(page))[address >> 22]; | 534 | page = ((unsigned long *) __va(page))[address >> 22]; |
| 529 | printk(KERN_ALERT "*pde = %08lx\n", page); | 535 | if (oops_may_print()) |
| 536 | printk(KERN_ALERT "*pde = %08lx\n", page); | ||
| 530 | /* | 537 | /* |
| 531 | * We must not directly access the pte in the highpte | 538 | * We must not directly access the pte in the highpte |
| 532 | * case, the page table might be allocated in highmem. | 539 | * case, the page table might be allocated in highmem. |
| @@ -534,7 +541,7 @@ no_context: | |||
| 534 | * it's allocated already. | 541 | * it's allocated already. |
| 535 | */ | 542 | */ |
| 536 | #ifndef CONFIG_HIGHPTE | 543 | #ifndef CONFIG_HIGHPTE |
| 537 | if (page & 1) { | 544 | if ((page & 1) && oops_may_print()) { |
| 538 | page &= PAGE_MASK; | 545 | page &= PAGE_MASK; |
| 539 | address &= 0x003ff000; | 546 | address &= 0x003ff000; |
| 540 | page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; | 547 | page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 3b507bf05d09..bb6e7ddee2fd 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
| @@ -91,6 +91,9 @@ extern struct notifier_block *panic_notifier_list; | |||
| 91 | extern long (*panic_blink)(long time); | 91 | extern long (*panic_blink)(long time); |
| 92 | NORET_TYPE void panic(const char * fmt, ...) | 92 | NORET_TYPE void panic(const char * fmt, ...) |
| 93 | __attribute__ ((NORET_AND format (printf, 1, 2))); | 93 | __attribute__ ((NORET_AND format (printf, 1, 2))); |
| 94 | extern void oops_enter(void); | ||
| 95 | extern void oops_exit(void); | ||
| 96 | extern int oops_may_print(void); | ||
| 94 | fastcall NORET_TYPE void do_exit(long error_code) | 97 | fastcall NORET_TYPE void do_exit(long error_code) |
| 95 | ATTRIB_NORET; | 98 | ATTRIB_NORET; |
| 96 | NORET_TYPE void complete_and_exit(struct completion *, long) | 99 | NORET_TYPE void complete_and_exit(struct completion *, long) |
diff --git a/kernel/panic.c b/kernel/panic.c index 126dc43f1c74..acd95adddb93 100644 --- a/kernel/panic.c +++ b/kernel/panic.c | |||
| @@ -20,10 +20,13 @@ | |||
| 20 | #include <linux/nmi.h> | 20 | #include <linux/nmi.h> |
| 21 | #include <linux/kexec.h> | 21 | #include <linux/kexec.h> |
| 22 | 22 | ||
| 23 | int panic_timeout; | ||
| 24 | int panic_on_oops; | 23 | int panic_on_oops; |
| 25 | int tainted; | 24 | int tainted; |
| 25 | static int pause_on_oops; | ||
| 26 | static int pause_on_oops_flag; | ||
| 27 | static DEFINE_SPINLOCK(pause_on_oops_lock); | ||
| 26 | 28 | ||
| 29 | int panic_timeout; | ||
| 27 | EXPORT_SYMBOL(panic_timeout); | 30 | EXPORT_SYMBOL(panic_timeout); |
| 28 | 31 | ||
| 29 | struct notifier_block *panic_notifier_list; | 32 | struct notifier_block *panic_notifier_list; |
| @@ -174,3 +177,95 @@ void add_taint(unsigned flag) | |||
| 174 | tainted |= flag; | 177 | tainted |= flag; |
| 175 | } | 178 | } |
| 176 | EXPORT_SYMBOL(add_taint); | 179 | EXPORT_SYMBOL(add_taint); |
| 180 | |||
| 181 | static int __init pause_on_oops_setup(char *str) | ||
| 182 | { | ||
| 183 | pause_on_oops = simple_strtoul(str, NULL, 0); | ||
| 184 | return 1; | ||
| 185 | } | ||
| 186 | __setup("pause_on_oops=", pause_on_oops_setup); | ||
| 187 | |||
| 188 | static void spin_msec(int msecs) | ||
| 189 | { | ||
| 190 | int i; | ||
| 191 | |||
| 192 | for (i = 0; i < msecs; i++) { | ||
| 193 | touch_nmi_watchdog(); | ||
| 194 | mdelay(1); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 199 | * It just happens that oops_enter() and oops_exit() are identically | ||
| 200 | * implemented... | ||
| 201 | */ | ||
| 202 | static void do_oops_enter_exit(void) | ||
| 203 | { | ||
| 204 | unsigned long flags; | ||
| 205 | static int spin_counter; | ||
| 206 | |||
| 207 | if (!pause_on_oops) | ||
| 208 | return; | ||
| 209 | |||
| 210 | spin_lock_irqsave(&pause_on_oops_lock, flags); | ||
| 211 | if (pause_on_oops_flag == 0) { | ||
| 212 | /* This CPU may now print the oops message */ | ||
| 213 | pause_on_oops_flag = 1; | ||
| 214 | } else { | ||
| 215 | /* We need to stall this CPU */ | ||
| 216 | if (!spin_counter) { | ||
| 217 | /* This CPU gets to do the counting */ | ||
| 218 | spin_counter = pause_on_oops; | ||
| 219 | do { | ||
| 220 | spin_unlock(&pause_on_oops_lock); | ||
| 221 | spin_msec(MSEC_PER_SEC); | ||
| 222 | spin_lock(&pause_on_oops_lock); | ||
| 223 | } while (--spin_counter); | ||
| 224 | pause_on_oops_flag = 0; | ||
| 225 | } else { | ||
| 226 | /* This CPU waits for a different one */ | ||
| 227 | while (spin_counter) { | ||
| 228 | spin_unlock(&pause_on_oops_lock); | ||
| 229 | spin_msec(1); | ||
| 230 | spin_lock(&pause_on_oops_lock); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | } | ||
| 234 | spin_unlock_irqrestore(&pause_on_oops_lock, flags); | ||
| 235 | } | ||
| 236 | |||
| 237 | /* | ||
| 238 | * Return true if the calling CPU is allowed to print oops-related info. This | ||
| 239 | * is a bit racy.. | ||
| 240 | */ | ||
| 241 | int oops_may_print(void) | ||
| 242 | { | ||
| 243 | return pause_on_oops_flag == 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 247 | * Called when the architecture enters its oops handler, before it prints | ||
| 248 | * anything. If this is the first CPU to oops, and it's oopsing the first time | ||
| 249 | * then let it proceed. | ||
| 250 | * | ||
| 251 | * This is all enabled by the pause_on_oops kernel boot option. We do all this | ||
| 252 | * to ensure that oopses don't scroll off the screen. It has the side-effect | ||
| 253 | * of preventing later-oopsing CPUs from mucking up the display, too. | ||
| 254 | * | ||
| 255 | * It turns out that the CPU which is allowed to print ends up pausing for the | ||
| 256 | * right duration, whereas all the other CPUs pause for twice as long: once in | ||
| 257 | * oops_enter(), once in oops_exit(). | ||
| 258 | */ | ||
| 259 | void oops_enter(void) | ||
| 260 | { | ||
| 261 | do_oops_enter_exit(); | ||
| 262 | } | ||
| 263 | |||
| 264 | /* | ||
| 265 | * Called when the architecture exits its oops handler, after printing | ||
| 266 | * everything. | ||
| 267 | */ | ||
| 268 | void oops_exit(void) | ||
| 269 | { | ||
| 270 | do_oops_enter_exit(); | ||
| 271 | } | ||
