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 #endif /* CONFIG_IWMMXT */ 60 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 #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 73 struct vfp_sigframe 74 { 75 unsigned long magic; 76 unsigned long size; 77 - union vfp_state storage; 78 - }; 79 #endif /* CONFIG_VFP */ 80 81 /* ··· 90 #ifdef CONFIG_IWMMXT 91 struct iwmmxt_sigframe iwmmxt; 92 #endif 93 - #if 0 && defined CONFIG_VFP /* Not yet saved. */ 94 struct vfp_sigframe vfp; 95 #endif 96 /* Something that isn't a valid magic number for any coprocessor. */
··· 59 #endif /* CONFIG_IWMMXT */ 60 61 #ifdef CONFIG_VFP 62 #define VFP_MAGIC 0x56465001 63 64 struct vfp_sigframe 65 { 66 unsigned long magic; 67 unsigned long size; 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 + 78 #endif /* CONFIG_VFP */ 79 80 /* ··· 91 #ifdef CONFIG_IWMMXT 92 struct iwmmxt_sigframe iwmmxt; 93 #endif 94 + #ifdef CONFIG_VFP 95 struct vfp_sigframe vfp; 96 #endif 97 /* Something that isn't a valid magic number for any coprocessor. */
+11 -1
arch/arm/include/asm/user.h
··· 83 84 /* 85 * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 86 - * are ignored by the ptrace system call. 87 */ 88 struct user_vfp { 89 unsigned long long fpregs[32]; 90 unsigned long fpscr; 91 }; 92 93 #endif /* _ARM_USER_H */
··· 83 84 /* 85 * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 86 + * are ignored by the ptrace system call and the signal handler. 87 */ 88 struct user_vfp { 89 unsigned long long fpregs[32]; 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; 101 }; 102 103 #endif /* _ARM_USER_H */
+89 -4
arch/arm/kernel/signal.c
··· 18 #include <asm/cacheflush.h> 19 #include <asm/ucontext.h> 20 #include <asm/unistd.h> 21 22 #include "ptrace.h" 23 #include "signal.h" ··· 176 177 #endif 178 179 /* 180 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 181 */ ··· 318 err |= restore_iwmmxt_context(&aux->iwmmxt); 319 #endif 320 #ifdef CONFIG_VFP 321 - // if (err == 0) 322 - // err |= vfp_restore_state(&sf->aux.vfp); 323 #endif 324 325 return err; ··· 433 err |= preserve_iwmmxt_context(&aux->iwmmxt); 434 #endif 435 #ifdef CONFIG_VFP 436 - // if (err == 0) 437 - // err |= vfp_save_state(&sf->aux.vfp); 438 #endif 439 __put_user_error(0, &aux->end_magic, err); 440
··· 18 #include <asm/cacheflush.h> 19 #include <asm/ucontext.h> 20 #include <asm/unistd.h> 21 + #include <asm/vfp.h> 22 23 #include "ptrace.h" 24 #include "signal.h" ··· 175 176 #endif 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 + 262 /* 263 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 264 */ ··· 233 err |= restore_iwmmxt_context(&aux->iwmmxt); 234 #endif 235 #ifdef CONFIG_VFP 236 + if (err == 0) 237 + err |= restore_vfp_context(&aux->vfp); 238 #endif 239 240 return err; ··· 348 err |= preserve_iwmmxt_context(&aux->iwmmxt); 349 #endif 350 #ifdef CONFIG_VFP 351 + if (err == 0) 352 + err |= preserve_vfp_context(&aux->vfp); 353 #endif 354 __put_user_error(0, &aux->end_magic, err); 355