mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	um: Save FPU registers between task switches
Some time ago Jeff prepared 42daba3165 ("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>
			
			
This commit is contained in:
		
							parent
							
								
									b40997b872
								
							
						
					
					
						commit
						fbfe9c847e
					
				
					 7 changed files with 31 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -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 restore_registers(int pid, struct uml_pt_regs *regs);
 | 
			
		||||
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 int get_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,
 | 
			
		|||
		arch_copy_thread(¤t->thread.arch, &p->thread.arch);
 | 
			
		||||
	}
 | 
			
		||||
	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;
 | 
			
		||||
		handler = new_thread_handler;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,8 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <sys/ptrace.h>
 | 
			
		||||
#include "sysdep/ptrace.h"
 | 
			
		||||
#include "sysdep/ptrace_user.h"
 | 
			
		||||
#include "registers.h"
 | 
			
		||||
 | 
			
		||||
int save_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 */
 | 
			
		||||
 | 
			
		||||
static unsigned long exec_regs[MAX_REG_NR];
 | 
			
		||||
static unsigned long exec_fp_regs[FP_SIZE];
 | 
			
		||||
 | 
			
		||||
int init_registers(int pid)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -42,10 +45,14 @@ int init_registers(int pid)
 | 
			
		|||
		return -errno;
 | 
			
		||||
 | 
			
		||||
	arch_init_registers(pid);
 | 
			
		||||
	get_fp_registers(pid, exec_fp_regs);
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
	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];
 | 
			
		|||
 | 
			
		||||
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 +
 | 
			
		||||
		((unsigned long) &batch_syscall_stub -
 | 
			
		||||
		 (unsigned long) &__syscall_stub_start);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -373,6 +373,9 @@ void userspace(struct uml_pt_regs *regs)
 | 
			
		|||
		if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp))
 | 
			
		||||
			fatal_sigsegv();
 | 
			
		||||
 | 
			
		||||
		if (put_fp_registers(pid, regs->fp))
 | 
			
		||||
			fatal_sigsegv();
 | 
			
		||||
 | 
			
		||||
		/* Now we set local_using_sysemu to be used for one loop */
 | 
			
		||||
		local_using_sysemu = get_using_sysemu();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -399,6 +402,12 @@ void userspace(struct uml_pt_regs *regs)
 | 
			
		|||
			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 */
 | 
			
		||||
 | 
			
		||||
		if (WIFSTOPPED(status)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -457,10 +466,11 @@ void userspace(struct uml_pt_regs *regs)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long thread_regs[MAX_REG_NR];
 | 
			
		||||
static unsigned long thread_fp_regs[FP_SIZE];
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
	thread_regs[REGS_IP_INDEX] = STUB_CODE +
 | 
			
		||||
				(unsigned long) stub_clone_handler -
 | 
			
		||||
| 
						 | 
				
			
			@ -503,6 +513,13 @@ int copy_context_skas0(unsigned long new_stack, int pid)
 | 
			
		|||
		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 */
 | 
			
		||||
	child_data->err = 12345678;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,7 @@ extern int sysemu_supported;
 | 
			
		|||
 | 
			
		||||
struct uml_pt_regs {
 | 
			
		||||
	unsigned long gp[MAX_REG_NR];
 | 
			
		||||
	unsigned long fp[HOST_FPX_SIZE];
 | 
			
		||||
	struct faultinfo faultinfo;
 | 
			
		||||
	long syscall;
 | 
			
		||||
	int is_user;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,6 +85,7 @@
 | 
			
		|||
 | 
			
		||||
struct uml_pt_regs {
 | 
			
		||||
	unsigned long gp[MAX_REG_NR];
 | 
			
		||||
	unsigned long fp[HOST_FP_SIZE];
 | 
			
		||||
	struct faultinfo faultinfo;
 | 
			
		||||
	long syscall;
 | 
			
		||||
	int is_user;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue