diff options
Diffstat (limited to 'arch/x86/kernel/process.c')
-rw-r--r-- | arch/x86/kernel/process.c | 171 |
1 files changed, 132 insertions, 39 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 5284cd2b5776..0415c3ef91b5 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -9,7 +9,11 @@ | |||
9 | #include <linux/pm.h> | 9 | #include <linux/pm.h> |
10 | #include <linux/clockchips.h> | 10 | #include <linux/clockchips.h> |
11 | #include <linux/random.h> | 11 | #include <linux/random.h> |
12 | #include <linux/user-return-notifier.h> | ||
13 | #include <linux/dmi.h> | ||
14 | #include <linux/utsname.h> | ||
12 | #include <trace/events/power.h> | 15 | #include <trace/events/power.h> |
16 | #include <linux/hw_breakpoint.h> | ||
13 | #include <asm/system.h> | 17 | #include <asm/system.h> |
14 | #include <asm/apic.h> | 18 | #include <asm/apic.h> |
15 | #include <asm/syscalls.h> | 19 | #include <asm/syscalls.h> |
@@ -17,6 +21,7 @@ | |||
17 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
18 | #include <asm/i387.h> | 22 | #include <asm/i387.h> |
19 | #include <asm/ds.h> | 23 | #include <asm/ds.h> |
24 | #include <asm/debugreg.h> | ||
20 | 25 | ||
21 | unsigned long idle_halt; | 26 | unsigned long idle_halt; |
22 | EXPORT_SYMBOL(idle_halt); | 27 | EXPORT_SYMBOL(idle_halt); |
@@ -87,30 +92,37 @@ void exit_thread(void) | |||
87 | } | 92 | } |
88 | } | 93 | } |
89 | 94 | ||
90 | void flush_thread(void) | 95 | void show_regs(struct pt_regs *regs) |
91 | { | 96 | { |
92 | struct task_struct *tsk = current; | 97 | show_registers(regs); |
98 | show_trace(NULL, regs, (unsigned long *)kernel_stack_pointer(regs), | ||
99 | regs->bp); | ||
100 | } | ||
93 | 101 | ||
94 | #ifdef CONFIG_X86_64 | 102 | void show_regs_common(void) |
95 | if (test_tsk_thread_flag(tsk, TIF_ABI_PENDING)) { | 103 | { |
96 | clear_tsk_thread_flag(tsk, TIF_ABI_PENDING); | 104 | const char *board, *product; |
97 | if (test_tsk_thread_flag(tsk, TIF_IA32)) { | ||
98 | clear_tsk_thread_flag(tsk, TIF_IA32); | ||
99 | } else { | ||
100 | set_tsk_thread_flag(tsk, TIF_IA32); | ||
101 | current_thread_info()->status |= TS_COMPAT; | ||
102 | } | ||
103 | } | ||
104 | #endif | ||
105 | 105 | ||
106 | clear_tsk_thread_flag(tsk, TIF_DEBUG); | 106 | board = dmi_get_system_info(DMI_BOARD_NAME); |
107 | if (!board) | ||
108 | board = ""; | ||
109 | product = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
110 | if (!product) | ||
111 | product = ""; | ||
107 | 112 | ||
108 | tsk->thread.debugreg0 = 0; | 113 | printk(KERN_CONT "\n"); |
109 | tsk->thread.debugreg1 = 0; | 114 | printk(KERN_DEFAULT "Pid: %d, comm: %.20s %s %s %.*s %s/%s\n", |
110 | tsk->thread.debugreg2 = 0; | 115 | current->pid, current->comm, print_tainted(), |
111 | tsk->thread.debugreg3 = 0; | 116 | init_utsname()->release, |
112 | tsk->thread.debugreg6 = 0; | 117 | (int)strcspn(init_utsname()->version, " "), |
113 | tsk->thread.debugreg7 = 0; | 118 | init_utsname()->version, board, product); |
119 | } | ||
120 | |||
121 | void flush_thread(void) | ||
122 | { | ||
123 | struct task_struct *tsk = current; | ||
124 | |||
125 | flush_ptrace_hw_breakpoint(tsk); | ||
114 | memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); | 126 | memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); |
115 | /* | 127 | /* |
116 | * Forget coprocessor state.. | 128 | * Forget coprocessor state.. |
@@ -192,16 +204,6 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | |||
192 | else if (next->debugctlmsr != prev->debugctlmsr) | 204 | else if (next->debugctlmsr != prev->debugctlmsr) |
193 | update_debugctlmsr(next->debugctlmsr); | 205 | update_debugctlmsr(next->debugctlmsr); |
194 | 206 | ||
195 | if (test_tsk_thread_flag(next_p, TIF_DEBUG)) { | ||
196 | set_debugreg(next->debugreg0, 0); | ||
197 | set_debugreg(next->debugreg1, 1); | ||
198 | set_debugreg(next->debugreg2, 2); | ||
199 | set_debugreg(next->debugreg3, 3); | ||
200 | /* no 4 and 5 */ | ||
201 | set_debugreg(next->debugreg6, 6); | ||
202 | set_debugreg(next->debugreg7, 7); | ||
203 | } | ||
204 | |||
205 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ | 207 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ |
206 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { | 208 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { |
207 | /* prev and next are different */ | 209 | /* prev and next are different */ |
@@ -224,6 +226,7 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | |||
224 | */ | 226 | */ |
225 | memset(tss->io_bitmap, 0xff, prev->io_bitmap_max); | 227 | memset(tss->io_bitmap, 0xff, prev->io_bitmap_max); |
226 | } | 228 | } |
229 | propagate_user_return_notify(prev_p, next_p); | ||
227 | } | 230 | } |
228 | 231 | ||
229 | int sys_fork(struct pt_regs *regs) | 232 | int sys_fork(struct pt_regs *regs) |
@@ -247,6 +250,78 @@ int sys_vfork(struct pt_regs *regs) | |||
247 | NULL, NULL); | 250 | NULL, NULL); |
248 | } | 251 | } |
249 | 252 | ||
253 | long | ||
254 | sys_clone(unsigned long clone_flags, unsigned long newsp, | ||
255 | void __user *parent_tid, void __user *child_tid, struct pt_regs *regs) | ||
256 | { | ||
257 | if (!newsp) | ||
258 | newsp = regs->sp; | ||
259 | return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * This gets run with %si containing the | ||
264 | * function to call, and %di containing | ||
265 | * the "args". | ||
266 | */ | ||
267 | extern void kernel_thread_helper(void); | ||
268 | |||
269 | /* | ||
270 | * Create a kernel thread | ||
271 | */ | ||
272 | int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
273 | { | ||
274 | struct pt_regs regs; | ||
275 | |||
276 | memset(®s, 0, sizeof(regs)); | ||
277 | |||
278 | regs.si = (unsigned long) fn; | ||
279 | regs.di = (unsigned long) arg; | ||
280 | |||
281 | #ifdef CONFIG_X86_32 | ||
282 | regs.ds = __USER_DS; | ||
283 | regs.es = __USER_DS; | ||
284 | regs.fs = __KERNEL_PERCPU; | ||
285 | regs.gs = __KERNEL_STACK_CANARY; | ||
286 | #else | ||
287 | regs.ss = __KERNEL_DS; | ||
288 | #endif | ||
289 | |||
290 | regs.orig_ax = -1; | ||
291 | regs.ip = (unsigned long) kernel_thread_helper; | ||
292 | regs.cs = __KERNEL_CS | get_kernel_rpl(); | ||
293 | regs.flags = X86_EFLAGS_IF | 0x2; | ||
294 | |||
295 | /* Ok, create the new process.. */ | ||
296 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
297 | } | ||
298 | EXPORT_SYMBOL(kernel_thread); | ||
299 | |||
300 | /* | ||
301 | * sys_execve() executes a new program. | ||
302 | */ | ||
303 | long sys_execve(char __user *name, char __user * __user *argv, | ||
304 | char __user * __user *envp, struct pt_regs *regs) | ||
305 | { | ||
306 | long error; | ||
307 | char *filename; | ||
308 | |||
309 | filename = getname(name); | ||
310 | error = PTR_ERR(filename); | ||
311 | if (IS_ERR(filename)) | ||
312 | return error; | ||
313 | error = do_execve(filename, argv, envp, regs); | ||
314 | |||
315 | #ifdef CONFIG_X86_32 | ||
316 | if (error == 0) { | ||
317 | /* Make sure we don't return using sysenter.. */ | ||
318 | set_thread_flag(TIF_IRET); | ||
319 | } | ||
320 | #endif | ||
321 | |||
322 | putname(filename); | ||
323 | return error; | ||
324 | } | ||
250 | 325 | ||
251 | /* | 326 | /* |
252 | * Idle related variables and functions | 327 | * Idle related variables and functions |
@@ -451,21 +526,39 @@ static int __cpuinit mwait_usable(const struct cpuinfo_x86 *c) | |||
451 | } | 526 | } |
452 | 527 | ||
453 | /* | 528 | /* |
454 | * Check for AMD CPUs, which have potentially C1E support | 529 | * Check for AMD CPUs, where APIC timer interrupt does not wake up CPU from C1e. |
530 | * For more information see | ||
531 | * - Erratum #400 for NPT family 0xf and family 0x10 CPUs | ||
532 | * - Erratum #365 for family 0x11 (not affected because C1e not in use) | ||
455 | */ | 533 | */ |
456 | static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c) | 534 | static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c) |
457 | { | 535 | { |
536 | u64 val; | ||
458 | if (c->x86_vendor != X86_VENDOR_AMD) | 537 | if (c->x86_vendor != X86_VENDOR_AMD) |
459 | return 0; | 538 | goto no_c1e_idle; |
460 | |||
461 | if (c->x86 < 0x0F) | ||
462 | return 0; | ||
463 | 539 | ||
464 | /* Family 0x0f models < rev F do not have C1E */ | 540 | /* Family 0x0f models < rev F do not have C1E */ |
465 | if (c->x86 == 0x0f && c->x86_model < 0x40) | 541 | if (c->x86 == 0x0F && c->x86_model >= 0x40) |
466 | return 0; | 542 | return 1; |
467 | 543 | ||
468 | return 1; | 544 | if (c->x86 == 0x10) { |
545 | /* | ||
546 | * check OSVW bit for CPUs that are not affected | ||
547 | * by erratum #400 | ||
548 | */ | ||
549 | if (cpu_has(c, X86_FEATURE_OSVW)) { | ||
550 | rdmsrl(MSR_AMD64_OSVW_ID_LENGTH, val); | ||
551 | if (val >= 2) { | ||
552 | rdmsrl(MSR_AMD64_OSVW_STATUS, val); | ||
553 | if (!(val & BIT(1))) | ||
554 | goto no_c1e_idle; | ||
555 | } | ||
556 | } | ||
557 | return 1; | ||
558 | } | ||
559 | |||
560 | no_c1e_idle: | ||
561 | return 0; | ||
469 | } | 562 | } |
470 | 563 | ||
471 | static cpumask_var_t c1e_mask; | 564 | static cpumask_var_t c1e_mask; |
@@ -532,7 +625,7 @@ void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) | |||
532 | { | 625 | { |
533 | #ifdef CONFIG_SMP | 626 | #ifdef CONFIG_SMP |
534 | if (pm_idle == poll_idle && smp_num_siblings > 1) { | 627 | if (pm_idle == poll_idle && smp_num_siblings > 1) { |
535 | printk(KERN_WARNING "WARNING: polling idle and HT enabled," | 628 | printk_once(KERN_WARNING "WARNING: polling idle and HT enabled," |
536 | " performance may degrade.\n"); | 629 | " performance may degrade.\n"); |
537 | } | 630 | } |
538 | #endif | 631 | #endif |