diff options
author | Jeff Dike <jdike@addtoit.com> | 2007-10-16 04:27:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 12:43:07 -0400 |
commit | a5f6096c805e6d2fa03ee932f8c70af34cee41a0 (patch) | |
tree | c74d984c0e2fc2958425df65605dd3451adc6520 /arch/um/sys-x86_64 | |
parent | 189872f968def833727b6bfef83ebd7440c538e6 (diff) |
uml: floating point signal delivery fixes
Handle floating point state in across signals correctly. UML/i386 needs to
know whether the host does PTRACE_[GS]ETFPXREGS, so an arch_init_registers
hook is added, which on x86_64 does nothing.
UML doesn't save and restore floating point registers on kernel entry and
exit, so they need to be copied between the host process and the sigcontext.
save_fpx_registers and restore_fpx_registers are added for this purpose.
save_fp_registers and restore_fp_registers already exist.
There was a bunch of floating point state conversion code in
arch/um/sys-i386/ptrace.c which isn't needed there, but is needed in signal.c,
so it is moved over.
The i386 code now distinguishes between fp and fpx state and handles them
correctly. The x86_64 code just needs to copy state as-is between the host
process and the stack. There are also some fixes there to pass the correct
address of the floating point state around.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um/sys-x86_64')
-rw-r--r-- | arch/um/sys-x86_64/signal.c | 53 |
1 files changed, 39 insertions, 14 deletions
diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c index c98dd7f31396..a8e5fd7b2adb 100644 --- a/arch/um/sys-x86_64/signal.c +++ b/arch/um/sys-x86_64/signal.c | |||
@@ -42,8 +42,10 @@ void copy_sc(struct uml_pt_regs *regs, void *from) | |||
42 | } | 42 | } |
43 | 43 | ||
44 | static int copy_sc_from_user(struct pt_regs *regs, | 44 | static int copy_sc_from_user(struct pt_regs *regs, |
45 | struct sigcontext __user *from) | 45 | struct sigcontext __user *from, |
46 | struct _fpstate __user *fpp) | ||
46 | { | 47 | { |
48 | struct user_i387_struct fp; | ||
47 | int err = 0; | 49 | int err = 0; |
48 | 50 | ||
49 | #define GETREG(regs, regno, sc, regname) \ | 51 | #define GETREG(regs, regno, sc, regname) \ |
@@ -69,10 +71,25 @@ static int copy_sc_from_user(struct pt_regs *regs, | |||
69 | err |= GETREG(regs, RIP, from, rip); | 71 | err |= GETREG(regs, RIP, from, rip); |
70 | err |= GETREG(regs, EFLAGS, from, eflags); | 72 | err |= GETREG(regs, EFLAGS, from, eflags); |
71 | err |= GETREG(regs, CS, from, cs); | 73 | err |= GETREG(regs, CS, from, cs); |
74 | if (err) | ||
75 | return 1; | ||
72 | 76 | ||
73 | #undef GETREG | 77 | #undef GETREG |
74 | 78 | ||
75 | return err; | 79 | err = copy_from_user(&fp, fpp, sizeof(struct user_i387_struct)); |
80 | if (err) | ||
81 | return 1; | ||
82 | |||
83 | err = restore_fp_registers(userspace_pid[current_thread->cpu], | ||
84 | (unsigned long *) &fp); | ||
85 | if (err < 0) { | ||
86 | printk(KERN_ERR "copy_sc_from_user - " | ||
87 | "restore_fp_registers failed, errno = %d\n", | ||
88 | -err); | ||
89 | return 1; | ||
90 | } | ||
91 | |||
92 | return 0; | ||
76 | } | 93 | } |
77 | 94 | ||
78 | static int copy_sc_to_user(struct sigcontext __user *to, | 95 | static int copy_sc_to_user(struct sigcontext __user *to, |
@@ -80,6 +97,7 @@ static int copy_sc_to_user(struct sigcontext __user *to, | |||
80 | unsigned long mask, unsigned long sp) | 97 | unsigned long mask, unsigned long sp) |
81 | { | 98 | { |
82 | struct faultinfo * fi = ¤t->thread.arch.faultinfo; | 99 | struct faultinfo * fi = ¤t->thread.arch.faultinfo; |
100 | struct user_i387_struct fp; | ||
83 | int err = 0; | 101 | int err = 0; |
84 | 102 | ||
85 | err |= __put_user(0, &to->gs); | 103 | err |= __put_user(0, &to->gs); |
@@ -120,6 +138,19 @@ static int copy_sc_to_user(struct sigcontext __user *to, | |||
120 | #undef PUTREG | 138 | #undef PUTREG |
121 | 139 | ||
122 | err |= __put_user(mask, &to->oldmask); | 140 | err |= __put_user(mask, &to->oldmask); |
141 | if (err) | ||
142 | return 1; | ||
143 | |||
144 | err = save_fp_registers(userspace_pid[current_thread->cpu], | ||
145 | (unsigned long *) &fp); | ||
146 | if (err < 0) { | ||
147 | printk(KERN_ERR "copy_sc_from_user - restore_fp_registers " | ||
148 | "failed, errno = %d\n", -err); | ||
149 | return 1; | ||
150 | } | ||
151 | |||
152 | if (copy_to_user(to_fp, &fp, sizeof(struct user_i387_struct))) | ||
153 | return 1; | ||
123 | 154 | ||
124 | return(err); | 155 | return(err); |
125 | } | 156 | } |
@@ -129,6 +160,7 @@ struct rt_sigframe | |||
129 | char __user *pretcode; | 160 | char __user *pretcode; |
130 | struct ucontext uc; | 161 | struct ucontext uc; |
131 | struct siginfo info; | 162 | struct siginfo info; |
163 | struct _fpstate fpstate; | ||
132 | }; | 164 | }; |
133 | 165 | ||
134 | #define round_down(m, n) (((m) / (n)) * (n)) | 166 | #define round_down(m, n) (((m) / (n)) * (n)) |
@@ -138,7 +170,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
138 | siginfo_t *info, sigset_t *set) | 170 | siginfo_t *info, sigset_t *set) |
139 | { | 171 | { |
140 | struct rt_sigframe __user *frame; | 172 | struct rt_sigframe __user *frame; |
141 | struct _fpstate __user *fp = NULL; | ||
142 | unsigned long save_sp = PT_REGS_RSP(regs); | 173 | unsigned long save_sp = PT_REGS_RSP(regs); |
143 | int err = 0; | 174 | int err = 0; |
144 | struct task_struct *me = current; | 175 | struct task_struct *me = current; |
@@ -148,13 +179,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
148 | /* Subtract 128 for a red zone and 8 for proper alignment */ | 179 | /* Subtract 128 for a red zone and 8 for proper alignment */ |
149 | frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8); | 180 | frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8); |
150 | 181 | ||
151 | if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate))) | ||
152 | goto out; | ||
153 | |||
154 | #if 0 /* XXX */ | ||
155 | if (save_i387(fp) < 0) | ||
156 | err |= -1; | ||
157 | #endif | ||
158 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 182 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) |
159 | goto out; | 183 | goto out; |
160 | 184 | ||
@@ -181,9 +205,9 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
181 | err |= __put_user(sas_ss_flags(save_sp), | 205 | err |= __put_user(sas_ss_flags(save_sp), |
182 | &frame->uc.uc_stack.ss_flags); | 206 | &frame->uc.uc_stack.ss_flags); |
183 | err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size); | 207 | err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size); |
184 | err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0], | 208 | err |= copy_sc_to_user(&frame->uc.uc_mcontext, &frame->fpstate, regs, |
185 | save_sp); | 209 | set->sig[0], save_sp); |
186 | err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate); | 210 | err |= __put_user(&frame->fpstate, &frame->uc.uc_mcontext.fpstate); |
187 | if (sizeof(*set) == 16) { | 211 | if (sizeof(*set) == 16) { |
188 | __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]); | 212 | __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]); |
189 | __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]); | 213 | __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]); |
@@ -246,7 +270,8 @@ long sys_rt_sigreturn(struct pt_regs *regs) | |||
246 | recalc_sigpending(); | 270 | recalc_sigpending(); |
247 | spin_unlock_irq(¤t->sighand->siglock); | 271 | spin_unlock_irq(¤t->sighand->siglock); |
248 | 272 | ||
249 | if (copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext)) | 273 | if (copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext, |
274 | &frame->fpstate)) | ||
250 | goto segfault; | 275 | goto segfault; |
251 | 276 | ||
252 | /* Avoid ERESTART handling */ | 277 | /* Avoid ERESTART handling */ |