ARM: 6051/1: VFP: preserve the HW context when calling signal handlers

From: Imre Deak <imre.deak@nokia.com>

Signal handlers can use floating point, so prevent them to corrupt
the main thread's VFP context. So far there were two signal stack
frame formats defined based on the VFP implementation, but the user
struct used for ptrace covers all posibilities, so use it for the
signal stack too.

Introduce also a new user struct for VFP exception registers. In
this too fields not relevant to the current VFP architecture are
ignored.

Support to save / restore the exception registers was added by
Will Deacon.

Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by Imre Deak and committed by Russell King 82c6f5a5 5c5cac63

+111 -17
+11 -12
arch/arm/include/asm/ucontext.h
··· 59 59 #endif /* CONFIG_IWMMXT */ 60 60 61 61 #ifdef CONFIG_VFP 62 - #if __LINUX_ARM_ARCH__ < 6 63 - /* For ARM pre-v6, we use fstmiax and fldmiax. This adds one extra 64 - * word after the registers, and a word of padding at the end for 65 - * alignment. */ 66 62 #define VFP_MAGIC 0x56465001 67 - #define VFP_STORAGE_SIZE 152 68 - #else 69 - #define VFP_MAGIC 0x56465002 70 - #define VFP_STORAGE_SIZE 144 71 - #endif 72 63 73 64 struct vfp_sigframe 74 65 { 75 66 unsigned long magic; 76 67 unsigned long size; 77 - union vfp_state storage; 78 - }; 68 + struct user_vfp ufp; 69 + struct user_vfp_exc ufp_exc; 70 + } __attribute__((__aligned__(8))); 71 + 72 + /* 73 + * 8 byte for magic and size, 264 byte for ufp, 12 bytes for ufp_exc, 74 + * 4 bytes padding. 75 + */ 76 + #define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe) 77 + 79 78 #endif /* CONFIG_VFP */ 80 79 81 80 /* ··· 90 91 #ifdef CONFIG_IWMMXT 91 92 struct iwmmxt_sigframe iwmmxt; 92 93 #endif 93 - #if 0 && defined CONFIG_VFP /* Not yet saved. */ 94 + #ifdef CONFIG_VFP 94 95 struct vfp_sigframe vfp; 95 96 #endif 96 97 /* Something that isn't a valid magic number for any coprocessor. */
+11 -1
arch/arm/include/asm/user.h
··· 83 83 84 84 /* 85 85 * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 86 - * are ignored by the ptrace system call. 86 + * are ignored by the ptrace system call and the signal handler. 87 87 */ 88 88 struct user_vfp { 89 89 unsigned long long fpregs[32]; 90 90 unsigned long fpscr; 91 + }; 92 + 93 + /* 94 + * VFP exception registers exposed to user space during signal delivery. 95 + * Fields not relavant to the current VFP architecture are ignored. 96 + */ 97 + struct user_vfp_exc { 98 + unsigned long fpexc; 99 + unsigned long fpinst; 100 + unsigned long fpinst2; 91 101 }; 92 102 93 103 #endif /* _ARM_USER_H */
+89 -4
arch/arm/kernel/signal.c
··· 18 18 #include <asm/cacheflush.h> 19 19 #include <asm/ucontext.h> 20 20 #include <asm/unistd.h> 21 + #include <asm/vfp.h> 21 22 22 23 #include "ptrace.h" 23 24 #include "signal.h" ··· 176 175 177 176 #endif 178 177 178 + #ifdef CONFIG_VFP 179 + 180 + static int preserve_vfp_context(struct vfp_sigframe __user *frame) 181 + { 182 + struct thread_info *thread = current_thread_info(); 183 + struct vfp_hard_struct *h = &thread->vfpstate.hard; 184 + const unsigned long magic = VFP_MAGIC; 185 + const unsigned long size = VFP_STORAGE_SIZE; 186 + int err = 0; 187 + 188 + vfp_sync_hwstate(thread); 189 + __put_user_error(magic, &frame->magic, err); 190 + __put_user_error(size, &frame->size, err); 191 + 192 + /* 193 + * Copy the floating point registers. There can be unused 194 + * registers see asm/hwcap.h for details. 195 + */ 196 + err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, 197 + sizeof(h->fpregs)); 198 + /* 199 + * Copy the status and control register. 200 + */ 201 + __put_user_error(h->fpscr, &frame->ufp.fpscr, err); 202 + 203 + /* 204 + * Copy the exception registers. 205 + */ 206 + __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err); 207 + __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); 208 + __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); 209 + 210 + return err ? -EFAULT : 0; 211 + } 212 + 213 + static int restore_vfp_context(struct vfp_sigframe __user *frame) 214 + { 215 + struct thread_info *thread = current_thread_info(); 216 + struct vfp_hard_struct *h = &thread->vfpstate.hard; 217 + unsigned long magic; 218 + unsigned long size; 219 + unsigned long fpexc; 220 + int err = 0; 221 + 222 + __get_user_error(magic, &frame->magic, err); 223 + __get_user_error(size, &frame->size, err); 224 + 225 + if (err) 226 + return -EFAULT; 227 + if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) 228 + return -EINVAL; 229 + 230 + /* 231 + * Copy the floating point registers. There can be unused 232 + * registers see asm/hwcap.h for details. 233 + */ 234 + err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs, 235 + sizeof(h->fpregs)); 236 + /* 237 + * Copy the status and control register. 238 + */ 239 + __get_user_error(h->fpscr, &frame->ufp.fpscr, err); 240 + 241 + /* 242 + * Sanitise and restore the exception registers. 243 + */ 244 + __get_user_error(fpexc, &frame->ufp_exc.fpexc, err); 245 + /* Ensure the VFP is enabled. */ 246 + fpexc |= FPEXC_EN; 247 + /* Ensure FPINST2 is invalid and the exception flag is cleared. */ 248 + fpexc &= ~(FPEXC_EX | FPEXC_FP2V); 249 + h->fpexc = fpexc; 250 + 251 + __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); 252 + __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); 253 + 254 + if (!err) 255 + vfp_flush_hwstate(thread); 256 + 257 + return err ? -EFAULT : 0; 258 + } 259 + 260 + #endif 261 + 179 262 /* 180 263 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 181 264 */ ··· 318 233 err |= restore_iwmmxt_context(&aux->iwmmxt); 319 234 #endif 320 235 #ifdef CONFIG_VFP 321 - // if (err == 0) 322 - // err |= vfp_restore_state(&sf->aux.vfp); 236 + if (err == 0) 237 + err |= restore_vfp_context(&aux->vfp); 323 238 #endif 324 239 325 240 return err; ··· 433 348 err |= preserve_iwmmxt_context(&aux->iwmmxt); 434 349 #endif 435 350 #ifdef CONFIG_VFP 436 - // if (err == 0) 437 - // err |= vfp_save_state(&sf->aux.vfp); 351 + if (err == 0) 352 + err |= preserve_vfp_context(&aux->vfp); 438 353 #endif 439 354 __put_user_error(0, &aux->end_magic, err); 440 355