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 | } | ||