diff options
author | Ingo van Lil <inguin@gmx.de> | 2011-09-14 19:21:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-14 21:09:37 -0400 |
commit | fbfe9c847edf57ac8232aeafb290f272289893a3 (patch) | |
tree | da25f3f4ece74b9fa328ce3884f19ff18eff516d /arch/um | |
parent | b40997b872cdb70140f127af6069f00a86b6cf81 (diff) |
um: Save FPU registers between task switches
Some time ago Jeff prepared 42daba316557 ("uml: stop saving process FP
state") for UML to stop saving the process FP state between task
switches. The assumption was that since with SKAS0 every guest process
runs inside a host process context the host OS will take care of keeping
the proper FP state.
Unfortunately this is not true for multi-threaded applications, where
all guest threads share a single host process context yet all may use
the FPU on their own. Although I haven't verified it I suspect things
to be even worse in SKAS3 mode where all guest processes run inside a
single host process.
The patch reintroduces the saving and restoring of the FP context
between task switches.
[richard@nod.at: Ingo posted this patch in 2009, sadly it was never applied
and got lost. Now in 2011 the problem was reported by Gunnar.]
Signed-off-by: Ingo van Lil <inguin@gmx.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
Reported-by: <gunnarlindroth@hotmail.com>
Tested-by: <gunnarlindroth@hotmail.com>
Cc: Stanislav Meduna <stano@meduna.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um')
-rw-r--r-- | arch/um/include/shared/registers.h | 2 | ||||
-rw-r--r-- | arch/um/kernel/process.c | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/registers.c | 9 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/mem.c | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/process.c | 19 | ||||
-rw-r--r-- | arch/um/sys-i386/shared/sysdep/ptrace.h | 1 | ||||
-rw-r--r-- | arch/um/sys-x86_64/shared/sysdep/ptrace.h | 1 |
7 files changed, 31 insertions, 5 deletions
diff --git a/arch/um/include/shared/registers.h b/arch/um/include/shared/registers.h index b0b4589e0ebc..f1e0aa56c52a 100644 --- a/arch/um/include/shared/registers.h +++ b/arch/um/include/shared/registers.h | |||
@@ -16,7 +16,7 @@ extern int restore_fpx_registers(int pid, unsigned long *fp_regs); | |||
16 | extern int save_registers(int pid, struct uml_pt_regs *regs); | 16 | extern int save_registers(int pid, struct uml_pt_regs *regs); |
17 | extern int restore_registers(int pid, struct uml_pt_regs *regs); | 17 | extern int restore_registers(int pid, struct uml_pt_regs *regs); |
18 | extern int init_registers(int pid); | 18 | extern int init_registers(int pid); |
19 | extern void get_safe_registers(unsigned long *regs); | 19 | extern void get_safe_registers(unsigned long *regs, unsigned long *fp_regs); |
20 | extern unsigned long get_thread_reg(int reg, jmp_buf *buf); | 20 | extern unsigned long get_thread_reg(int reg, jmp_buf *buf); |
21 | extern int get_fp_registers(int pid, unsigned long *regs); | 21 | extern int get_fp_registers(int pid, unsigned long *regs); |
22 | extern int put_fp_registers(int pid, unsigned long *regs); | 22 | extern int put_fp_registers(int pid, unsigned long *regs); |
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index fab4371184f6..21c1ae7c3d75 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c | |||
@@ -202,7 +202,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, | |||
202 | arch_copy_thread(¤t->thread.arch, &p->thread.arch); | 202 | arch_copy_thread(¤t->thread.arch, &p->thread.arch); |
203 | } | 203 | } |
204 | else { | 204 | else { |
205 | get_safe_registers(p->thread.regs.regs.gp); | 205 | get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp); |
206 | p->thread.request.u.thread = current->thread.request.u.thread; | 206 | p->thread.request.u.thread = current->thread.request.u.thread; |
207 | handler = new_thread_handler; | 207 | handler = new_thread_handler; |
208 | } | 208 | } |
diff --git a/arch/um/os-Linux/registers.c b/arch/um/os-Linux/registers.c index 830fe6a1518a..b866b9e3bef9 100644 --- a/arch/um/os-Linux/registers.c +++ b/arch/um/os-Linux/registers.c | |||
@@ -8,6 +8,8 @@ | |||
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include <sys/ptrace.h> | 9 | #include <sys/ptrace.h> |
10 | #include "sysdep/ptrace.h" | 10 | #include "sysdep/ptrace.h" |
11 | #include "sysdep/ptrace_user.h" | ||
12 | #include "registers.h" | ||
11 | 13 | ||
12 | int save_registers(int pid, struct uml_pt_regs *regs) | 14 | int save_registers(int pid, struct uml_pt_regs *regs) |
13 | { | 15 | { |
@@ -32,6 +34,7 @@ int restore_registers(int pid, struct uml_pt_regs *regs) | |||
32 | /* This is set once at boot time and not changed thereafter */ | 34 | /* This is set once at boot time and not changed thereafter */ |
33 | 35 | ||
34 | static unsigned long exec_regs[MAX_REG_NR]; | 36 | static unsigned long exec_regs[MAX_REG_NR]; |
37 | static unsigned long exec_fp_regs[FP_SIZE]; | ||
35 | 38 | ||
36 | int init_registers(int pid) | 39 | int init_registers(int pid) |
37 | { | 40 | { |
@@ -42,10 +45,14 @@ int init_registers(int pid) | |||
42 | return -errno; | 45 | return -errno; |
43 | 46 | ||
44 | arch_init_registers(pid); | 47 | arch_init_registers(pid); |
48 | get_fp_registers(pid, exec_fp_regs); | ||
45 | return 0; | 49 | return 0; |
46 | } | 50 | } |
47 | 51 | ||
48 | void get_safe_registers(unsigned long *regs) | 52 | void get_safe_registers(unsigned long *regs, unsigned long *fp_regs) |
49 | { | 53 | { |
50 | memcpy(regs, exec_regs, sizeof(exec_regs)); | 54 | memcpy(regs, exec_regs, sizeof(exec_regs)); |
55 | |||
56 | if (fp_regs) | ||
57 | memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs)); | ||
51 | } | 58 | } |
diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index d261f170d120..e771398be5f3 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c | |||
@@ -39,7 +39,7 @@ static unsigned long syscall_regs[MAX_REG_NR]; | |||
39 | 39 | ||
40 | static int __init init_syscall_regs(void) | 40 | static int __init init_syscall_regs(void) |
41 | { | 41 | { |
42 | get_safe_registers(syscall_regs); | 42 | get_safe_registers(syscall_regs, NULL); |
43 | syscall_regs[REGS_IP_INDEX] = STUB_CODE + | 43 | syscall_regs[REGS_IP_INDEX] = STUB_CODE + |
44 | ((unsigned long) &batch_syscall_stub - | 44 | ((unsigned long) &batch_syscall_stub - |
45 | (unsigned long) &__syscall_stub_start); | 45 | (unsigned long) &__syscall_stub_start); |
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index d6e0a2234b86..dee0e8cf8ad0 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c | |||
@@ -373,6 +373,9 @@ void userspace(struct uml_pt_regs *regs) | |||
373 | if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp)) | 373 | if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp)) |
374 | fatal_sigsegv(); | 374 | fatal_sigsegv(); |
375 | 375 | ||
376 | if (put_fp_registers(pid, regs->fp)) | ||
377 | fatal_sigsegv(); | ||
378 | |||
376 | /* Now we set local_using_sysemu to be used for one loop */ | 379 | /* Now we set local_using_sysemu to be used for one loop */ |
377 | local_using_sysemu = get_using_sysemu(); | 380 | local_using_sysemu = get_using_sysemu(); |
378 | 381 | ||
@@ -399,6 +402,12 @@ void userspace(struct uml_pt_regs *regs) | |||
399 | fatal_sigsegv(); | 402 | fatal_sigsegv(); |
400 | } | 403 | } |
401 | 404 | ||
405 | if (get_fp_registers(pid, regs->fp)) { | ||
406 | printk(UM_KERN_ERR "userspace - get_fp_registers failed, " | ||
407 | "errno = %d\n", errno); | ||
408 | fatal_sigsegv(); | ||
409 | } | ||
410 | |||
402 | UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */ | 411 | UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */ |
403 | 412 | ||
404 | if (WIFSTOPPED(status)) { | 413 | if (WIFSTOPPED(status)) { |
@@ -457,10 +466,11 @@ void userspace(struct uml_pt_regs *regs) | |||
457 | } | 466 | } |
458 | 467 | ||
459 | static unsigned long thread_regs[MAX_REG_NR]; | 468 | static unsigned long thread_regs[MAX_REG_NR]; |
469 | static unsigned long thread_fp_regs[FP_SIZE]; | ||
460 | 470 | ||
461 | static int __init init_thread_regs(void) | 471 | static int __init init_thread_regs(void) |
462 | { | 472 | { |
463 | get_safe_registers(thread_regs); | 473 | get_safe_registers(thread_regs, thread_fp_regs); |
464 | /* Set parent's instruction pointer to start of clone-stub */ | 474 | /* Set parent's instruction pointer to start of clone-stub */ |
465 | thread_regs[REGS_IP_INDEX] = STUB_CODE + | 475 | thread_regs[REGS_IP_INDEX] = STUB_CODE + |
466 | (unsigned long) stub_clone_handler - | 476 | (unsigned long) stub_clone_handler - |
@@ -503,6 +513,13 @@ int copy_context_skas0(unsigned long new_stack, int pid) | |||
503 | return err; | 513 | return err; |
504 | } | 514 | } |
505 | 515 | ||
516 | err = put_fp_registers(pid, thread_fp_regs); | ||
517 | if (err < 0) { | ||
518 | printk(UM_KERN_ERR "copy_context_skas0 : put_fp_registers " | ||
519 | "failed, pid = %d, err = %d\n", pid, err); | ||
520 | return err; | ||
521 | } | ||
522 | |||
506 | /* set a well known return code for detection of child write failure */ | 523 | /* set a well known return code for detection of child write failure */ |
507 | child_data->err = 12345678; | 524 | child_data->err = 12345678; |
508 | 525 | ||
diff --git a/arch/um/sys-i386/shared/sysdep/ptrace.h b/arch/um/sys-i386/shared/sysdep/ptrace.h index d50e62e07070..c398a5076111 100644 --- a/arch/um/sys-i386/shared/sysdep/ptrace.h +++ b/arch/um/sys-i386/shared/sysdep/ptrace.h | |||
@@ -53,6 +53,7 @@ extern int sysemu_supported; | |||
53 | 53 | ||
54 | struct uml_pt_regs { | 54 | struct uml_pt_regs { |
55 | unsigned long gp[MAX_REG_NR]; | 55 | unsigned long gp[MAX_REG_NR]; |
56 | unsigned long fp[HOST_FPX_SIZE]; | ||
56 | struct faultinfo faultinfo; | 57 | struct faultinfo faultinfo; |
57 | long syscall; | 58 | long syscall; |
58 | int is_user; | 59 | int is_user; |
diff --git a/arch/um/sys-x86_64/shared/sysdep/ptrace.h b/arch/um/sys-x86_64/shared/sysdep/ptrace.h index fdba5457947a..8ee8f8e12af1 100644 --- a/arch/um/sys-x86_64/shared/sysdep/ptrace.h +++ b/arch/um/sys-x86_64/shared/sysdep/ptrace.h | |||
@@ -85,6 +85,7 @@ | |||
85 | 85 | ||
86 | struct uml_pt_regs { | 86 | struct uml_pt_regs { |
87 | unsigned long gp[MAX_REG_NR]; | 87 | unsigned long gp[MAX_REG_NR]; |
88 | unsigned long fp[HOST_FP_SIZE]; | ||
88 | struct faultinfo faultinfo; | 89 | struct faultinfo faultinfo; |
89 | long syscall; | 90 | long syscall; |
90 | int is_user; | 91 | int is_user; |