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

s390: fix save and restore of the floating-point-control register

The FPC_VALID_MASK has been used to check the validity of the value
to be loaded into the floating-point-control register. With the
introduction of the floating-point extension facility and the
decimal-floating-point additional bits have been defined which need
to be checked in a non straight forward way. So far these bits have
been ignored which can cause an incorrect results for decimal-
floating-point operations, e.g. an incorrect rounding mode to be
set after signal return.

The static check with the FPC_VALID_MASK is replaced with a trial
load of the floating-point-control value, see test_fp_ctl.

In addition an information leak with the padding word between the
floating-point-control word and the floating-point registers in
the s390_fp_regs is fixed.

Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+177 -113
+83 -45
arch/s390/include/asm/switch_to.h
··· 13 13 extern struct task_struct *__switch_to(void *, void *); 14 14 extern void update_cr_regs(struct task_struct *task); 15 15 16 - static inline void save_fp_regs(s390_fp_regs *fpregs) 16 + static inline int test_fp_ctl(u32 fpc) 17 17 { 18 - asm volatile( 19 - " std 0,%O0+8(%R0)\n" 20 - " std 2,%O0+24(%R0)\n" 21 - " std 4,%O0+40(%R0)\n" 22 - " std 6,%O0+56(%R0)" 23 - : "=Q" (*fpregs) : "Q" (*fpregs)); 18 + u32 orig_fpc; 19 + int rc; 20 + 24 21 if (!MACHINE_HAS_IEEE) 25 - return; 22 + return 0; 23 + 26 24 asm volatile( 27 - " stfpc %0\n" 28 - " std 1,%O0+16(%R0)\n" 29 - " std 3,%O0+32(%R0)\n" 30 - " std 5,%O0+48(%R0)\n" 31 - " std 7,%O0+64(%R0)\n" 32 - " std 8,%O0+72(%R0)\n" 33 - " std 9,%O0+80(%R0)\n" 34 - " std 10,%O0+88(%R0)\n" 35 - " std 11,%O0+96(%R0)\n" 36 - " std 12,%O0+104(%R0)\n" 37 - " std 13,%O0+112(%R0)\n" 38 - " std 14,%O0+120(%R0)\n" 39 - " std 15,%O0+128(%R0)\n" 40 - : "=Q" (*fpregs) : "Q" (*fpregs)); 25 + " efpc %1\n" 26 + " sfpc %2\n" 27 + "0: sfpc %1\n" 28 + " la %0,0\n" 29 + "1:\n" 30 + EX_TABLE(0b,1b) 31 + : "=d" (rc), "=d" (orig_fpc) 32 + : "d" (fpc), "0" (-EINVAL)); 33 + return rc; 41 34 } 42 35 43 - static inline void restore_fp_regs(s390_fp_regs *fpregs) 36 + static inline void save_fp_ctl(u32 *fpc) 44 37 { 45 - asm volatile( 46 - " ld 0,%O0+8(%R0)\n" 47 - " ld 2,%O0+24(%R0)\n" 48 - " ld 4,%O0+40(%R0)\n" 49 - " ld 6,%O0+56(%R0)" 50 - : : "Q" (*fpregs)); 51 38 if (!MACHINE_HAS_IEEE) 52 39 return; 40 + 53 41 asm volatile( 54 - " lfpc %0\n" 55 - " ld 1,%O0+16(%R0)\n" 56 - " ld 3,%O0+32(%R0)\n" 57 - " ld 5,%O0+48(%R0)\n" 58 - " ld 7,%O0+64(%R0)\n" 59 - " ld 8,%O0+72(%R0)\n" 60 - " ld 9,%O0+80(%R0)\n" 61 - " ld 10,%O0+88(%R0)\n" 62 - " ld 11,%O0+96(%R0)\n" 63 - " ld 12,%O0+104(%R0)\n" 64 - " ld 13,%O0+112(%R0)\n" 65 - " ld 14,%O0+120(%R0)\n" 66 - " ld 15,%O0+128(%R0)\n" 67 - : : "Q" (*fpregs)); 42 + " stfpc %0\n" 43 + : "+Q" (*fpc)); 44 + } 45 + 46 + static inline int restore_fp_ctl(u32 *fpc) 47 + { 48 + int rc; 49 + 50 + if (!MACHINE_HAS_IEEE) 51 + return 0; 52 + 53 + asm volatile( 54 + "0: lfpc %1\n" 55 + " la %0,0\n" 56 + "1:\n" 57 + EX_TABLE(0b,1b) 58 + : "=d" (rc) : "Q" (*fpc), "0" (-EINVAL)); 59 + return rc; 60 + } 61 + 62 + static inline void save_fp_regs(freg_t *fprs) 63 + { 64 + asm volatile("std 0,%0" : "=Q" (fprs[0])); 65 + asm volatile("std 2,%0" : "=Q" (fprs[2])); 66 + asm volatile("std 4,%0" : "=Q" (fprs[4])); 67 + asm volatile("std 6,%0" : "=Q" (fprs[6])); 68 + if (!MACHINE_HAS_IEEE) 69 + return; 70 + asm volatile("std 1,%0" : "=Q" (fprs[1])); 71 + asm volatile("std 3,%0" : "=Q" (fprs[3])); 72 + asm volatile("std 5,%0" : "=Q" (fprs[5])); 73 + asm volatile("std 7,%0" : "=Q" (fprs[7])); 74 + asm volatile("std 8,%0" : "=Q" (fprs[8])); 75 + asm volatile("std 9,%0" : "=Q" (fprs[9])); 76 + asm volatile("std 10,%0" : "=Q" (fprs[10])); 77 + asm volatile("std 11,%0" : "=Q" (fprs[11])); 78 + asm volatile("std 12,%0" : "=Q" (fprs[12])); 79 + asm volatile("std 13,%0" : "=Q" (fprs[13])); 80 + asm volatile("std 14,%0" : "=Q" (fprs[14])); 81 + asm volatile("std 15,%0" : "=Q" (fprs[15])); 82 + } 83 + 84 + static inline void restore_fp_regs(freg_t *fprs) 85 + { 86 + asm volatile("ld 0,%0" : : "Q" (fprs[0])); 87 + asm volatile("ld 2,%0" : : "Q" (fprs[2])); 88 + asm volatile("ld 4,%0" : : "Q" (fprs[4])); 89 + asm volatile("ld 6,%0" : : "Q" (fprs[6])); 90 + if (!MACHINE_HAS_IEEE) 91 + return; 92 + asm volatile("ld 1,%0" : : "Q" (fprs[1])); 93 + asm volatile("ld 3,%0" : : "Q" (fprs[3])); 94 + asm volatile("ld 5,%0" : : "Q" (fprs[5])); 95 + asm volatile("ld 7,%0" : : "Q" (fprs[7])); 96 + asm volatile("ld 8,%0" : : "Q" (fprs[8])); 97 + asm volatile("ld 9,%0" : : "Q" (fprs[9])); 98 + asm volatile("ld 10,%0" : : "Q" (fprs[10])); 99 + asm volatile("ld 11,%0" : : "Q" (fprs[11])); 100 + asm volatile("ld 12,%0" : : "Q" (fprs[12])); 101 + asm volatile("ld 13,%0" : : "Q" (fprs[13])); 102 + asm volatile("ld 14,%0" : : "Q" (fprs[14])); 103 + asm volatile("ld 15,%0" : : "Q" (fprs[15])); 68 104 } 69 105 70 106 static inline void save_access_regs(unsigned int *acrs) ··· 119 83 120 84 #define switch_to(prev,next,last) do { \ 121 85 if (prev->mm) { \ 122 - save_fp_regs(&prev->thread.fp_regs); \ 86 + save_fp_ctl(&prev->thread.fp_regs.fpc); \ 87 + save_fp_regs(prev->thread.fp_regs.fprs); \ 123 88 save_access_regs(&prev->thread.acrs[0]); \ 124 89 save_ri_cb(prev->thread.ri_cb); \ 125 90 } \ 126 91 if (next->mm) { \ 127 - restore_fp_regs(&next->thread.fp_regs); \ 92 + restore_fp_ctl(&next->thread.fp_regs.fpc); \ 93 + restore_fp_regs(next->thread.fp_regs.fprs); \ 128 94 restore_access_regs(&next->thread.acrs[0]); \ 129 95 restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ 130 96 update_cr_regs(next); \
+1 -1
arch/s390/include/uapi/asm/ptrace.h
··· 199 199 typedef struct 200 200 { 201 201 __u32 fpc; 202 + __u32 pad; 202 203 freg_t fprs[NUM_FPRS]; 203 204 } s390_fp_regs; 204 205 ··· 207 206 #define FPC_FLAGS_MASK 0x00F80000 208 207 #define FPC_DXC_MASK 0x0000FF00 209 208 #define FPC_RM_MASK 0x00000003 210 - #define FPC_VALID_MASK 0xF8F8FF03 211 209 212 210 /* this typedef defines how a Program Status Word looks like */ 213 211 typedef struct
+1
arch/s390/include/uapi/asm/sigcontext.h
··· 49 49 typedef struct 50 50 { 51 51 unsigned int fpc; 52 + unsigned int pad; 52 53 double fprs[__NUM_FPRS]; 53 54 } _s390_fp_regs; 54 55
+1
arch/s390/kernel/compat_linux.h
··· 27 27 typedef struct 28 28 { 29 29 unsigned int fpc; 30 + unsigned int pad; 30 31 freg_t32 fprs[__NUM_FPRS]; 31 32 } _s390_fp_regs32; 32 33
+30 -29
arch/s390/kernel/compat_signal.c
··· 153 153 154 154 static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) 155 155 { 156 - _s390_regs_common32 regs32; 157 - int err, i; 156 + _sigregs32 user_sregs; 157 + int i; 158 158 159 - regs32.psw.mask = psw32_user_bits | 159 + user_sregs.regs.psw.mask = psw32_user_bits | 160 160 ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER); 161 - regs32.psw.addr = (__u32) regs->psw.addr | 161 + user_sregs.regs.psw.addr = (__u32) regs->psw.addr | 162 162 (__u32)(regs->psw.mask & PSW_MASK_BA); 163 163 for (i = 0; i < NUM_GPRS; i++) 164 - regs32.gprs[i] = (__u32) regs->gprs[i]; 164 + user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; 165 165 save_access_regs(current->thread.acrs); 166 - memcpy(regs32.acrs, current->thread.acrs, sizeof(regs32.acrs)); 167 - err = __copy_to_user(&sregs->regs, &regs32, sizeof(regs32)); 168 - if (err) 169 - return -EFAULT; 170 - save_fp_regs(&current->thread.fp_regs); 171 - /* s390_fp_regs and _s390_fp_regs32 are the same ! */ 172 - err = __copy_to_user(&sregs->fpregs, &current->thread.fp_regs, 173 - sizeof(_s390_fp_regs32)); 174 - if (err) 166 + memcpy(&user_sregs.regs.acrs, current->thread.acrs, 167 + sizeof(user_sregs.regs.acrs)); 168 + save_fp_ctl(&current->thread.fp_regs.fpc); 169 + save_fp_regs(current->thread.fp_regs.fprs); 170 + memcpy(&user_sregs.fpregs, &current->thread.fp_regs, 171 + sizeof(user_sregs.fpregs)); 172 + if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) 175 173 return -EFAULT; 176 174 return 0; 177 175 } 178 176 179 177 static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) 180 178 { 181 - _s390_regs_common32 regs32; 182 - int err, i; 179 + _sigregs32 user_sregs; 180 + int i; 183 181 184 182 /* Alwys make any pending restarted system call return -EINTR */ 185 183 current_thread_info()->restart_block.fn = do_no_restart_syscall; 186 184 187 - err = __copy_from_user(&regs32, &sregs->regs, sizeof(regs32)); 188 - if (err) 185 + if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs))) 189 186 return -EFAULT; 187 + 188 + /* Loading the floating-point-control word can fail. Do that first. */ 189 + if (restore_fp_ctl(&user_sregs.fpregs.fpc)) 190 + return -EINVAL; 191 + 192 + /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ 190 193 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 191 - (__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32 | 192 - (__u64)(regs32.psw.addr & PSW32_ADDR_AMODE); 194 + (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 | 195 + (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE); 193 196 /* Check for invalid user address space control. */ 194 197 if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME) 195 198 regs->psw.mask = PSW_ASC_PRIMARY | 196 199 (regs->psw.mask & ~PSW_MASK_ASC); 197 - regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN); 200 + regs->psw.addr = (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_INSN); 198 201 for (i = 0; i < NUM_GPRS; i++) 199 - regs->gprs[i] = (__u64) regs32.gprs[i]; 200 - memcpy(current->thread.acrs, regs32.acrs, sizeof(current->thread.acrs)); 202 + regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; 203 + memcpy(&current->thread.acrs, &user_sregs.regs.acrs, 204 + sizeof(current->thread.acrs)); 201 205 restore_access_regs(current->thread.acrs); 202 206 203 - err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs, 204 - sizeof(_s390_fp_regs32)); 205 - current->thread.fp_regs.fpc &= FPC_VALID_MASK; 206 - if (err) 207 - return -EFAULT; 207 + memcpy(&current->thread.fp_regs, &user_sregs.fpregs, 208 + sizeof(current->thread.fp_regs)); 208 209 209 - restore_fp_regs(&current->thread.fp_regs); 210 + restore_fp_regs(current->thread.fp_regs.fprs); 210 211 clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */ 211 212 return 0; 212 213 }
+9 -4
arch/s390/kernel/process.c
··· 165 165 * save fprs to current->thread.fp_regs to merge them with 166 166 * the emulated registers and then copy the result to the child. 167 167 */ 168 - save_fp_regs(&current->thread.fp_regs); 168 + save_fp_ctl(&current->thread.fp_regs.fpc); 169 + save_fp_regs(current->thread.fp_regs.fprs); 169 170 memcpy(&p->thread.fp_regs, &current->thread.fp_regs, 170 171 sizeof(s390_fp_regs)); 171 172 /* Set a new TLS ? */ ··· 174 173 p->thread.acrs[0] = frame->childregs.gprs[6]; 175 174 #else /* CONFIG_64BIT */ 176 175 /* Save the fpu registers to new thread structure. */ 177 - save_fp_regs(&p->thread.fp_regs); 176 + save_fp_ctl(&p->thread.fp_regs.fpc); 177 + save_fp_regs(p->thread.fp_regs.fprs); 178 + p->thread.fp_regs.pad = 0; 178 179 /* Set a new TLS ? */ 179 180 if (clone_flags & CLONE_SETTLS) { 180 181 unsigned long tls = frame->childregs.gprs[6]; ··· 208 205 * save fprs to current->thread.fp_regs to merge them with 209 206 * the emulated registers and then copy the result to the dump. 210 207 */ 211 - save_fp_regs(&current->thread.fp_regs); 208 + save_fp_ctl(&current->thread.fp_regs.fpc); 209 + save_fp_regs(current->thread.fp_regs.fprs); 212 210 memcpy(fpregs, &current->thread.fp_regs, sizeof(s390_fp_regs)); 213 211 #else /* CONFIG_64BIT */ 214 - save_fp_regs(fpregs); 212 + save_fp_ctl(&fpregs->fpc); 213 + save_fp_regs(fpregs->fprs); 215 214 #endif /* CONFIG_64BIT */ 216 215 return 1; 217 216 }
+22 -18
arch/s390/kernel/ptrace.c
··· 239 239 offset = addr - (addr_t) &dummy->regs.fp_regs; 240 240 tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset); 241 241 if (addr == (addr_t) &dummy->regs.fp_regs.fpc) 242 - tmp &= (unsigned long) FPC_VALID_MASK 243 - << (BITS_PER_LONG - 32); 242 + tmp <<= BITS_PER_LONG - 32; 244 243 245 244 } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { 246 245 /* ··· 362 363 /* 363 364 * floating point regs. are stored in the thread structure 364 365 */ 365 - if (addr == (addr_t) &dummy->regs.fp_regs.fpc && 366 - (data & ~((unsigned long) FPC_VALID_MASK 367 - << (BITS_PER_LONG - 32))) != 0) 368 - return -EINVAL; 366 + if (addr == (addr_t) &dummy->regs.fp_regs.fpc) 367 + if ((unsigned int) data != 0 || 368 + test_fp_ctl(data >> (BITS_PER_LONG - 32))) 369 + return -EINVAL; 369 370 offset = addr - (addr_t) &dummy->regs.fp_regs; 370 371 *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data; 371 372 ··· 695 696 * floating point regs. are stored in the thread structure 696 697 */ 697 698 if (addr == (addr_t) &dummy32->regs.fp_regs.fpc && 698 - (tmp & ~FPC_VALID_MASK) != 0) 699 - /* Invalid floating point control. */ 699 + test_fp_ctl(tmp)) 700 700 return -EINVAL; 701 701 offset = addr - (addr_t) &dummy32->regs.fp_regs; 702 702 *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp; ··· 893 895 const struct user_regset *regset, unsigned int pos, 894 896 unsigned int count, void *kbuf, void __user *ubuf) 895 897 { 896 - if (target == current) 897 - save_fp_regs(&target->thread.fp_regs); 898 + if (target == current) { 899 + save_fp_ctl(&target->thread.fp_regs.fpc); 900 + save_fp_regs(target->thread.fp_regs.fprs); 901 + } 898 902 899 903 return user_regset_copyout(&pos, &count, &kbuf, &ubuf, 900 904 &target->thread.fp_regs, 0, -1); ··· 909 909 { 910 910 int rc = 0; 911 911 912 - if (target == current) 913 - save_fp_regs(&target->thread.fp_regs); 912 + if (target == current) { 913 + save_fp_ctl(&target->thread.fp_regs.fpc); 914 + save_fp_regs(target->thread.fp_regs.fprs); 915 + } 914 916 915 917 /* If setting FPC, must validate it first. */ 916 918 if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { 917 - u32 fpc[2] = { target->thread.fp_regs.fpc, 0 }; 918 - rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc, 919 + u32 ufpc[2] = { target->thread.fp_regs.fpc, 0 }; 920 + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ufpc, 919 921 0, offsetof(s390_fp_regs, fprs)); 920 922 if (rc) 921 923 return rc; 922 - if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0) 924 + if (ufpc[1] != 0 || test_fp_ctl(ufpc[0])) 923 925 return -EINVAL; 924 - target->thread.fp_regs.fpc = fpc[0]; 926 + target->thread.fp_regs.fpc = ufpc[0]; 925 927 } 926 928 927 929 if (rc == 0 && count > 0) ··· 931 929 target->thread.fp_regs.fprs, 932 930 offsetof(s390_fp_regs, fprs), -1); 933 931 934 - if (rc == 0 && target == current) 935 - restore_fp_regs(&target->thread.fp_regs); 932 + if (rc == 0 && target == current) { 933 + restore_fp_ctl(&target->thread.fp_regs.fpc); 934 + restore_fp_regs(target->thread.fp_regs.fprs); 935 + } 936 936 937 937 return rc; 938 938 }
+13 -8
arch/s390/kernel/signal.c
··· 62 62 user_sregs.regs.psw.addr = regs->psw.addr; 63 63 memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs)); 64 64 memcpy(&user_sregs.regs.acrs, current->thread.acrs, 65 - sizeof(sregs->regs.acrs)); 65 + sizeof(user_sregs.regs.acrs)); 66 66 /* 67 67 * We have to store the fp registers to current->thread.fp_regs 68 68 * to merge them with the emulated registers. 69 69 */ 70 - save_fp_regs(&current->thread.fp_regs); 70 + save_fp_ctl(&current->thread.fp_regs.fpc); 71 + save_fp_regs(current->thread.fp_regs.fprs); 71 72 memcpy(&user_sregs.fpregs, &current->thread.fp_regs, 72 - sizeof(s390_fp_regs)); 73 + sizeof(user_sregs.fpregs)); 73 74 if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) 74 75 return -EFAULT; 75 76 return 0; ··· 83 82 /* Alwys make any pending restarted system call return -EINTR */ 84 83 current_thread_info()->restart_block.fn = do_no_restart_syscall; 85 84 86 - if (__copy_from_user(&user_sregs, sregs, sizeof(_sigregs))) 85 + if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs))) 87 86 return -EFAULT; 87 + 88 + /* Loading the floating-point-control word can fail. Do that first. */ 89 + if (restore_fp_ctl(&user_sregs.fpregs.fpc)) 90 + return -EINVAL; 91 + 88 92 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ 89 93 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 90 94 (user_sregs.regs.psw.mask & PSW_MASK_USER); ··· 103 97 regs->psw.addr = user_sregs.regs.psw.addr; 104 98 memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); 105 99 memcpy(&current->thread.acrs, &user_sregs.regs.acrs, 106 - sizeof(sregs->regs.acrs)); 100 + sizeof(current->thread.acrs)); 107 101 restore_access_regs(current->thread.acrs); 108 102 109 103 memcpy(&current->thread.fp_regs, &user_sregs.fpregs, 110 - sizeof(s390_fp_regs)); 111 - current->thread.fp_regs.fpc &= FPC_VALID_MASK; 104 + sizeof(current->thread.fp_regs)); 112 105 113 - restore_fp_regs(&current->thread.fp_regs); 106 + restore_fp_regs(current->thread.fp_regs.fprs); 114 107 clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */ 115 108 return 0; 116 109 }
+15 -8
arch/s390/kvm/kvm-s390.c
··· 343 343 344 344 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) 345 345 { 346 - save_fp_regs(&vcpu->arch.host_fpregs); 346 + save_fp_ctl(&vcpu->arch.host_fpregs.fpc); 347 + save_fp_regs(vcpu->arch.host_fpregs.fprs); 347 348 save_access_regs(vcpu->arch.host_acrs); 348 - vcpu->arch.guest_fpregs.fpc &= FPC_VALID_MASK; 349 - restore_fp_regs(&vcpu->arch.guest_fpregs); 349 + restore_fp_ctl(&vcpu->arch.guest_fpregs.fpc); 350 + restore_fp_regs(vcpu->arch.guest_fpregs.fprs); 350 351 restore_access_regs(vcpu->run->s.regs.acrs); 351 352 gmap_enable(vcpu->arch.gmap); 352 353 atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); ··· 357 356 { 358 357 atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); 359 358 gmap_disable(vcpu->arch.gmap); 360 - save_fp_regs(&vcpu->arch.guest_fpregs); 359 + save_fp_ctl(&vcpu->arch.guest_fpregs.fpc); 360 + save_fp_regs(vcpu->arch.guest_fpregs.fprs); 361 361 save_access_regs(vcpu->run->s.regs.acrs); 362 - restore_fp_regs(&vcpu->arch.host_fpregs); 362 + restore_fp_ctl(&vcpu->arch.host_fpregs.fpc); 363 + restore_fp_regs(vcpu->arch.host_fpregs.fprs); 363 364 restore_access_regs(vcpu->arch.host_acrs); 364 365 } 365 366 ··· 621 618 622 619 int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) 623 620 { 621 + if (test_fp_ctl(fpu->fpc)) 622 + return -EINVAL; 624 623 memcpy(&vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs)); 625 - vcpu->arch.guest_fpregs.fpc = fpu->fpc & FPC_VALID_MASK; 626 - restore_fp_regs(&vcpu->arch.guest_fpregs); 624 + vcpu->arch.guest_fpregs.fpc = fpu->fpc; 625 + restore_fp_ctl(&vcpu->arch.guest_fpregs.fpc); 626 + restore_fp_regs(vcpu->arch.guest_fpregs.fprs); 627 627 return 0; 628 628 } 629 629 ··· 882 876 * copying in vcpu load/put. Lets update our copies before we save 883 877 * it into the save area 884 878 */ 885 - save_fp_regs(&vcpu->arch.guest_fpregs); 879 + save_fp_ctl(&vcpu->arch.guest_fpregs.fpc); 880 + save_fp_regs(vcpu->arch.guest_fpregs.fprs); 886 881 save_access_regs(vcpu->run->s.regs.acrs); 887 882 888 883 if (__guestcopy(vcpu, addr + offsetof(struct save_area, fp_regs),
+2
arch/s390/math-emu/math.c
··· 19 19 #include <math-emu/double.h> 20 20 #include <math-emu/quad.h> 21 21 22 + #define FPC_VALID_MASK 0xF8F8FF03 23 + 22 24 /* 23 25 * I miss a macro to round a floating point number to the 24 26 * nearest integer in the same floating point format.