Commit fbfe9c84 authored by Ingo van Lil's avatar Ingo van Lil Committed by Linus Torvalds

um: Save FPU registers between task switches

Some time ago Jeff prepared 42daba31 ("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: default avatarIngo van Lil <inguin@gmx.de>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
Reported-by: <gunnarlindroth@hotmail.com>
Tested-by: <gunnarlindroth@hotmail.com>
Cc: Stanislav Meduna <stano@meduna.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b40997b8
...@@ -16,7 +16,7 @@ extern int restore_fpx_registers(int pid, unsigned long *fp_regs); ...@@ -16,7 +16,7 @@ extern int restore_fpx_registers(int pid, unsigned long *fp_regs);
extern int save_registers(int pid, struct uml_pt_regs *regs); extern int save_registers(int pid, struct uml_pt_regs *regs);
extern int restore_registers(int pid, struct uml_pt_regs *regs); extern int restore_registers(int pid, struct uml_pt_regs *regs);
extern int init_registers(int pid); extern int init_registers(int pid);
extern void get_safe_registers(unsigned long *regs); extern void get_safe_registers(unsigned long *regs, unsigned long *fp_regs);
extern unsigned long get_thread_reg(int reg, jmp_buf *buf); extern unsigned long get_thread_reg(int reg, jmp_buf *buf);
extern int get_fp_registers(int pid, unsigned long *regs); extern int get_fp_registers(int pid, unsigned long *regs);
extern int put_fp_registers(int pid, unsigned long *regs); extern int put_fp_registers(int pid, unsigned long *regs);
......
...@@ -202,7 +202,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, ...@@ -202,7 +202,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
arch_copy_thread(&current->thread.arch, &p->thread.arch); arch_copy_thread(&current->thread.arch, &p->thread.arch);
} }
else { else {
get_safe_registers(p->thread.regs.regs.gp); get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
p->thread.request.u.thread = current->thread.request.u.thread; p->thread.request.u.thread = current->thread.request.u.thread;
handler = new_thread_handler; handler = new_thread_handler;
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <string.h> #include <string.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include "sysdep/ptrace.h" #include "sysdep/ptrace.h"
#include "sysdep/ptrace_user.h"
#include "registers.h"
int save_registers(int pid, struct uml_pt_regs *regs) int save_registers(int pid, struct uml_pt_regs *regs)
{ {
...@@ -32,6 +34,7 @@ int restore_registers(int pid, struct uml_pt_regs *regs) ...@@ -32,6 +34,7 @@ int restore_registers(int pid, struct uml_pt_regs *regs)
/* This is set once at boot time and not changed thereafter */ /* This is set once at boot time and not changed thereafter */
static unsigned long exec_regs[MAX_REG_NR]; static unsigned long exec_regs[MAX_REG_NR];
static unsigned long exec_fp_regs[FP_SIZE];
int init_registers(int pid) int init_registers(int pid)
{ {
...@@ -42,10 +45,14 @@ int init_registers(int pid) ...@@ -42,10 +45,14 @@ int init_registers(int pid)
return -errno; return -errno;
arch_init_registers(pid); arch_init_registers(pid);
get_fp_registers(pid, exec_fp_regs);
return 0; return 0;
} }
void get_safe_registers(unsigned long *regs) void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
{ {
memcpy(regs, exec_regs, sizeof(exec_regs)); memcpy(regs, exec_regs, sizeof(exec_regs));
if (fp_regs)
memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs));
} }
...@@ -39,7 +39,7 @@ static unsigned long syscall_regs[MAX_REG_NR]; ...@@ -39,7 +39,7 @@ static unsigned long syscall_regs[MAX_REG_NR];
static int __init init_syscall_regs(void) static int __init init_syscall_regs(void)
{ {
get_safe_registers(syscall_regs); get_safe_registers(syscall_regs, NULL);
syscall_regs[REGS_IP_INDEX] = STUB_CODE + syscall_regs[REGS_IP_INDEX] = STUB_CODE +
((unsigned long) &batch_syscall_stub - ((unsigned long) &batch_syscall_stub -
(unsigned long) &__syscall_stub_start); (unsigned long) &__syscall_stub_start);
......
...@@ -373,6 +373,9 @@ void userspace(struct uml_pt_regs *regs) ...@@ -373,6 +373,9 @@ void userspace(struct uml_pt_regs *regs)
if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp)) if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp))
fatal_sigsegv(); fatal_sigsegv();
if (put_fp_registers(pid, regs->fp))
fatal_sigsegv();
/* Now we set local_using_sysemu to be used for one loop */ /* Now we set local_using_sysemu to be used for one loop */
local_using_sysemu = get_using_sysemu(); local_using_sysemu = get_using_sysemu();
...@@ -399,6 +402,12 @@ void userspace(struct uml_pt_regs *regs) ...@@ -399,6 +402,12 @@ void userspace(struct uml_pt_regs *regs)
fatal_sigsegv(); fatal_sigsegv();
} }
if (get_fp_registers(pid, regs->fp)) {
printk(UM_KERN_ERR "userspace - get_fp_registers failed, "
"errno = %d\n", errno);
fatal_sigsegv();
}
UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */ UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
if (WIFSTOPPED(status)) { if (WIFSTOPPED(status)) {
...@@ -457,10 +466,11 @@ void userspace(struct uml_pt_regs *regs) ...@@ -457,10 +466,11 @@ void userspace(struct uml_pt_regs *regs)
} }
static unsigned long thread_regs[MAX_REG_NR]; static unsigned long thread_regs[MAX_REG_NR];
static unsigned long thread_fp_regs[FP_SIZE];
static int __init init_thread_regs(void) static int __init init_thread_regs(void)
{ {
get_safe_registers(thread_regs); get_safe_registers(thread_regs, thread_fp_regs);
/* Set parent's instruction pointer to start of clone-stub */ /* Set parent's instruction pointer to start of clone-stub */
thread_regs[REGS_IP_INDEX] = STUB_CODE + thread_regs[REGS_IP_INDEX] = STUB_CODE +
(unsigned long) stub_clone_handler - (unsigned long) stub_clone_handler -
...@@ -503,6 +513,13 @@ int copy_context_skas0(unsigned long new_stack, int pid) ...@@ -503,6 +513,13 @@ int copy_context_skas0(unsigned long new_stack, int pid)
return err; return err;
} }
err = put_fp_registers(pid, thread_fp_regs);
if (err < 0) {
printk(UM_KERN_ERR "copy_context_skas0 : put_fp_registers "
"failed, pid = %d, err = %d\n", pid, err);
return err;
}
/* set a well known return code for detection of child write failure */ /* set a well known return code for detection of child write failure */
child_data->err = 12345678; child_data->err = 12345678;
......
...@@ -53,6 +53,7 @@ extern int sysemu_supported; ...@@ -53,6 +53,7 @@ extern int sysemu_supported;
struct uml_pt_regs { struct uml_pt_regs {
unsigned long gp[MAX_REG_NR]; unsigned long gp[MAX_REG_NR];
unsigned long fp[HOST_FPX_SIZE];
struct faultinfo faultinfo; struct faultinfo faultinfo;
long syscall; long syscall;
int is_user; int is_user;
......
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
struct uml_pt_regs { struct uml_pt_regs {
unsigned long gp[MAX_REG_NR]; unsigned long gp[MAX_REG_NR];
unsigned long fp[HOST_FP_SIZE];
struct faultinfo faultinfo; struct faultinfo faultinfo;
long syscall; long syscall;
int is_user; int is_user;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment