Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ingo van Lil and committed by
Linus Torvalds
fbfe9c84 b40997b8

+31 -5
+1 -1
arch/um/include/shared/registers.h
··· 16 16 extern int save_registers(int pid, struct uml_pt_regs *regs); 17 17 extern int restore_registers(int pid, struct uml_pt_regs *regs); 18 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 20 extern unsigned long get_thread_reg(int reg, jmp_buf *buf); 21 21 extern int get_fp_registers(int pid, unsigned long *regs); 22 22 extern int put_fp_registers(int pid, unsigned long *regs);
+1 -1
arch/um/kernel/process.c
··· 202 202 arch_copy_thread(&current->thread.arch, &p->thread.arch); 203 203 } 204 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 206 p->thread.request.u.thread = current->thread.request.u.thread; 207 207 handler = new_thread_handler; 208 208 }
+8 -1
arch/um/os-Linux/registers.c
··· 8 8 #include <string.h> 9 9 #include <sys/ptrace.h> 10 10 #include "sysdep/ptrace.h" 11 + #include "sysdep/ptrace_user.h" 12 + #include "registers.h" 11 13 12 14 int save_registers(int pid, struct uml_pt_regs *regs) 13 15 { ··· 34 32 /* This is set once at boot time and not changed thereafter */ 35 33 36 34 static unsigned long exec_regs[MAX_REG_NR]; 35 + static unsigned long exec_fp_regs[FP_SIZE]; 37 36 38 37 int init_registers(int pid) 39 38 { ··· 45 42 return -errno; 46 43 47 44 arch_init_registers(pid); 45 + get_fp_registers(pid, exec_fp_regs); 48 46 return 0; 49 47 } 50 48 51 - void get_safe_registers(unsigned long *regs) 49 + void get_safe_registers(unsigned long *regs, unsigned long *fp_regs) 52 50 { 53 51 memcpy(regs, exec_regs, sizeof(exec_regs)); 52 + 53 + if (fp_regs) 54 + memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs)); 54 55 }
+1 -1
arch/um/os-Linux/skas/mem.c
··· 39 39 40 40 static int __init init_syscall_regs(void) 41 41 { 42 - get_safe_registers(syscall_regs); 42 + get_safe_registers(syscall_regs, NULL); 43 43 syscall_regs[REGS_IP_INDEX] = STUB_CODE + 44 44 ((unsigned long) &batch_syscall_stub - 45 45 (unsigned long) &__syscall_stub_start);
+18 -1
arch/um/os-Linux/skas/process.c
··· 373 373 if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp)) 374 374 fatal_sigsegv(); 375 375 376 + if (put_fp_registers(pid, regs->fp)) 377 + fatal_sigsegv(); 378 + 376 379 /* Now we set local_using_sysemu to be used for one loop */ 377 380 local_using_sysemu = get_using_sysemu(); 378 381 ··· 398 395 regs->is_user = 1; 399 396 if (ptrace(PTRACE_GETREGS, pid, 0, regs->gp)) { 400 397 printk(UM_KERN_ERR "userspace - PTRACE_GETREGS failed, " 398 + "errno = %d\n", errno); 399 + fatal_sigsegv(); 400 + } 401 + 402 + if (get_fp_registers(pid, regs->fp)) { 403 + printk(UM_KERN_ERR "userspace - get_fp_registers failed, " 401 404 "errno = %d\n", errno); 402 405 fatal_sigsegv(); 403 406 } ··· 466 457 } 467 458 468 459 static unsigned long thread_regs[MAX_REG_NR]; 460 + static unsigned long thread_fp_regs[FP_SIZE]; 469 461 470 462 static int __init init_thread_regs(void) 471 463 { 472 - get_safe_registers(thread_regs); 464 + get_safe_registers(thread_regs, thread_fp_regs); 473 465 /* Set parent's instruction pointer to start of clone-stub */ 474 466 thread_regs[REGS_IP_INDEX] = STUB_CODE + 475 467 (unsigned long) stub_clone_handler - ··· 510 500 err = -errno; 511 501 printk(UM_KERN_ERR "copy_context_skas0 : PTRACE_SETREGS " 512 502 "failed, pid = %d, errno = %d\n", pid, -err); 503 + return err; 504 + } 505 + 506 + err = put_fp_registers(pid, thread_fp_regs); 507 + if (err < 0) { 508 + printk(UM_KERN_ERR "copy_context_skas0 : put_fp_registers " 509 + "failed, pid = %d, err = %d\n", pid, err); 513 510 return err; 514 511 } 515 512
+1
arch/um/sys-i386/shared/sysdep/ptrace.h
··· 53 53 54 54 struct uml_pt_regs { 55 55 unsigned long gp[MAX_REG_NR]; 56 + unsigned long fp[HOST_FPX_SIZE]; 56 57 struct faultinfo faultinfo; 57 58 long syscall; 58 59 int is_user;
+1
arch/um/sys-x86_64/shared/sysdep/ptrace.h
··· 85 85 86 86 struct uml_pt_regs { 87 87 unsigned long gp[MAX_REG_NR]; 88 + unsigned long fp[HOST_FP_SIZE]; 88 89 struct faultinfo faultinfo; 89 90 long syscall; 90 91 int is_user;