diff options
Diffstat (limited to 'arch/x86/kernel/fpu/xstate.c')
-rw-r--r-- | arch/x86/kernel/fpu/xstate.c | 264 |
1 files changed, 213 insertions, 51 deletions
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index c24ac1efb12d..f1d5476c9022 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c | |||
@@ -483,6 +483,30 @@ int using_compacted_format(void) | |||
483 | return boot_cpu_has(X86_FEATURE_XSAVES); | 483 | return boot_cpu_has(X86_FEATURE_XSAVES); |
484 | } | 484 | } |
485 | 485 | ||
486 | /* Validate an xstate header supplied by userspace (ptrace or sigreturn) */ | ||
487 | int validate_xstate_header(const struct xstate_header *hdr) | ||
488 | { | ||
489 | /* No unknown or supervisor features may be set */ | ||
490 | if (hdr->xfeatures & (~xfeatures_mask | XFEATURE_MASK_SUPERVISOR)) | ||
491 | return -EINVAL; | ||
492 | |||
493 | /* Userspace must use the uncompacted format */ | ||
494 | if (hdr->xcomp_bv) | ||
495 | return -EINVAL; | ||
496 | |||
497 | /* | ||
498 | * If 'reserved' is shrunken to add a new field, make sure to validate | ||
499 | * that new field here! | ||
500 | */ | ||
501 | BUILD_BUG_ON(sizeof(hdr->reserved) != 48); | ||
502 | |||
503 | /* No reserved bits may be set */ | ||
504 | if (memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved))) | ||
505 | return -EINVAL; | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
486 | static void __xstate_dump_leaves(void) | 510 | static void __xstate_dump_leaves(void) |
487 | { | 511 | { |
488 | int i; | 512 | int i; |
@@ -867,7 +891,7 @@ const void *get_xsave_field_ptr(int xsave_state) | |||
867 | { | 891 | { |
868 | struct fpu *fpu = ¤t->thread.fpu; | 892 | struct fpu *fpu = ¤t->thread.fpu; |
869 | 893 | ||
870 | if (!fpu->fpstate_active) | 894 | if (!fpu->initialized) |
871 | return NULL; | 895 | return NULL; |
872 | /* | 896 | /* |
873 | * fpu__save() takes the CPU's xstate registers | 897 | * fpu__save() takes the CPU's xstate registers |
@@ -921,38 +945,129 @@ int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, | |||
921 | #endif /* ! CONFIG_ARCH_HAS_PKEYS */ | 945 | #endif /* ! CONFIG_ARCH_HAS_PKEYS */ |
922 | 946 | ||
923 | /* | 947 | /* |
948 | * Weird legacy quirk: SSE and YMM states store information in the | ||
949 | * MXCSR and MXCSR_FLAGS fields of the FP area. That means if the FP | ||
950 | * area is marked as unused in the xfeatures header, we need to copy | ||
951 | * MXCSR and MXCSR_FLAGS if either SSE or YMM are in use. | ||
952 | */ | ||
953 | static inline bool xfeatures_mxcsr_quirk(u64 xfeatures) | ||
954 | { | ||
955 | if (!(xfeatures & (XFEATURE_MASK_SSE|XFEATURE_MASK_YMM))) | ||
956 | return false; | ||
957 | |||
958 | if (xfeatures & XFEATURE_MASK_FP) | ||
959 | return false; | ||
960 | |||
961 | return true; | ||
962 | } | ||
963 | |||
964 | /* | ||
924 | * This is similar to user_regset_copyout(), but will not add offset to | 965 | * This is similar to user_regset_copyout(), but will not add offset to |
925 | * the source data pointer or increment pos, count, kbuf, and ubuf. | 966 | * the source data pointer or increment pos, count, kbuf, and ubuf. |
926 | */ | 967 | */ |
927 | static inline int xstate_copyout(unsigned int pos, unsigned int count, | 968 | static inline void |
928 | void *kbuf, void __user *ubuf, | 969 | __copy_xstate_to_kernel(void *kbuf, const void *data, |
929 | const void *data, const int start_pos, | 970 | unsigned int offset, unsigned int size, unsigned int size_total) |
930 | const int end_pos) | ||
931 | { | 971 | { |
932 | if ((count == 0) || (pos < start_pos)) | 972 | if (offset < size_total) { |
933 | return 0; | 973 | unsigned int copy = min(size, size_total - offset); |
934 | 974 | ||
935 | if (end_pos < 0 || pos < end_pos) { | 975 | memcpy(kbuf + offset, data, copy); |
936 | unsigned int copy = (end_pos < 0 ? count : min(count, end_pos - pos)); | 976 | } |
977 | } | ||
937 | 978 | ||
938 | if (kbuf) { | 979 | /* |
939 | memcpy(kbuf + pos, data, copy); | 980 | * Convert from kernel XSAVES compacted format to standard format and copy |
940 | } else { | 981 | * to a kernel-space ptrace buffer. |
941 | if (__copy_to_user(ubuf + pos, data, copy)) | 982 | * |
942 | return -EFAULT; | 983 | * It supports partial copy but pos always starts from zero. This is called |
984 | * from xstateregs_get() and there we check the CPU has XSAVES. | ||
985 | */ | ||
986 | int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total) | ||
987 | { | ||
988 | unsigned int offset, size; | ||
989 | struct xstate_header header; | ||
990 | int i; | ||
991 | |||
992 | /* | ||
993 | * Currently copy_regset_to_user() starts from pos 0: | ||
994 | */ | ||
995 | if (unlikely(offset_start != 0)) | ||
996 | return -EFAULT; | ||
997 | |||
998 | /* | ||
999 | * The destination is a ptrace buffer; we put in only user xstates: | ||
1000 | */ | ||
1001 | memset(&header, 0, sizeof(header)); | ||
1002 | header.xfeatures = xsave->header.xfeatures; | ||
1003 | header.xfeatures &= ~XFEATURE_MASK_SUPERVISOR; | ||
1004 | |||
1005 | /* | ||
1006 | * Copy xregs_state->header: | ||
1007 | */ | ||
1008 | offset = offsetof(struct xregs_state, header); | ||
1009 | size = sizeof(header); | ||
1010 | |||
1011 | __copy_xstate_to_kernel(kbuf, &header, offset, size, size_total); | ||
1012 | |||
1013 | for (i = 0; i < XFEATURE_MAX; i++) { | ||
1014 | /* | ||
1015 | * Copy only in-use xstates: | ||
1016 | */ | ||
1017 | if ((header.xfeatures >> i) & 1) { | ||
1018 | void *src = __raw_xsave_addr(xsave, 1 << i); | ||
1019 | |||
1020 | offset = xstate_offsets[i]; | ||
1021 | size = xstate_sizes[i]; | ||
1022 | |||
1023 | /* The next component has to fit fully into the output buffer: */ | ||
1024 | if (offset + size > size_total) | ||
1025 | break; | ||
1026 | |||
1027 | __copy_xstate_to_kernel(kbuf, src, offset, size, size_total); | ||
943 | } | 1028 | } |
1029 | |||
1030 | } | ||
1031 | |||
1032 | if (xfeatures_mxcsr_quirk(header.xfeatures)) { | ||
1033 | offset = offsetof(struct fxregs_state, mxcsr); | ||
1034 | size = MXCSR_AND_FLAGS_SIZE; | ||
1035 | __copy_xstate_to_kernel(kbuf, &xsave->i387.mxcsr, offset, size, size_total); | ||
1036 | } | ||
1037 | |||
1038 | /* | ||
1039 | * Fill xsave->i387.sw_reserved value for ptrace frame: | ||
1040 | */ | ||
1041 | offset = offsetof(struct fxregs_state, sw_reserved); | ||
1042 | size = sizeof(xstate_fx_sw_bytes); | ||
1043 | |||
1044 | __copy_xstate_to_kernel(kbuf, xstate_fx_sw_bytes, offset, size, size_total); | ||
1045 | |||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
1049 | static inline int | ||
1050 | __copy_xstate_to_user(void __user *ubuf, const void *data, unsigned int offset, unsigned int size, unsigned int size_total) | ||
1051 | { | ||
1052 | if (!size) | ||
1053 | return 0; | ||
1054 | |||
1055 | if (offset < size_total) { | ||
1056 | unsigned int copy = min(size, size_total - offset); | ||
1057 | |||
1058 | if (__copy_to_user(ubuf + offset, data, copy)) | ||
1059 | return -EFAULT; | ||
944 | } | 1060 | } |
945 | return 0; | 1061 | return 0; |
946 | } | 1062 | } |
947 | 1063 | ||
948 | /* | 1064 | /* |
949 | * Convert from kernel XSAVES compacted format to standard format and copy | 1065 | * Convert from kernel XSAVES compacted format to standard format and copy |
950 | * to a ptrace buffer. It supports partial copy but pos always starts from | 1066 | * to a user-space buffer. It supports partial copy but pos always starts from |
951 | * zero. This is called from xstateregs_get() and there we check the CPU | 1067 | * zero. This is called from xstateregs_get() and there we check the CPU |
952 | * has XSAVES. | 1068 | * has XSAVES. |
953 | */ | 1069 | */ |
954 | int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, | 1070 | int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total) |
955 | void __user *ubuf, struct xregs_state *xsave) | ||
956 | { | 1071 | { |
957 | unsigned int offset, size; | 1072 | unsigned int offset, size; |
958 | int ret, i; | 1073 | int ret, i; |
@@ -961,7 +1076,7 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, | |||
961 | /* | 1076 | /* |
962 | * Currently copy_regset_to_user() starts from pos 0: | 1077 | * Currently copy_regset_to_user() starts from pos 0: |
963 | */ | 1078 | */ |
964 | if (unlikely(pos != 0)) | 1079 | if (unlikely(offset_start != 0)) |
965 | return -EFAULT; | 1080 | return -EFAULT; |
966 | 1081 | ||
967 | /* | 1082 | /* |
@@ -977,8 +1092,7 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, | |||
977 | offset = offsetof(struct xregs_state, header); | 1092 | offset = offsetof(struct xregs_state, header); |
978 | size = sizeof(header); | 1093 | size = sizeof(header); |
979 | 1094 | ||
980 | ret = xstate_copyout(offset, size, kbuf, ubuf, &header, 0, count); | 1095 | ret = __copy_xstate_to_user(ubuf, &header, offset, size, size_total); |
981 | |||
982 | if (ret) | 1096 | if (ret) |
983 | return ret; | 1097 | return ret; |
984 | 1098 | ||
@@ -992,25 +1106,30 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, | |||
992 | offset = xstate_offsets[i]; | 1106 | offset = xstate_offsets[i]; |
993 | size = xstate_sizes[i]; | 1107 | size = xstate_sizes[i]; |
994 | 1108 | ||
995 | ret = xstate_copyout(offset, size, kbuf, ubuf, src, 0, count); | 1109 | /* The next component has to fit fully into the output buffer: */ |
1110 | if (offset + size > size_total) | ||
1111 | break; | ||
996 | 1112 | ||
1113 | ret = __copy_xstate_to_user(ubuf, src, offset, size, size_total); | ||
997 | if (ret) | 1114 | if (ret) |
998 | return ret; | 1115 | return ret; |
999 | |||
1000 | if (offset + size >= count) | ||
1001 | break; | ||
1002 | } | 1116 | } |
1003 | 1117 | ||
1004 | } | 1118 | } |
1005 | 1119 | ||
1120 | if (xfeatures_mxcsr_quirk(header.xfeatures)) { | ||
1121 | offset = offsetof(struct fxregs_state, mxcsr); | ||
1122 | size = MXCSR_AND_FLAGS_SIZE; | ||
1123 | __copy_xstate_to_user(ubuf, &xsave->i387.mxcsr, offset, size, size_total); | ||
1124 | } | ||
1125 | |||
1006 | /* | 1126 | /* |
1007 | * Fill xsave->i387.sw_reserved value for ptrace frame: | 1127 | * Fill xsave->i387.sw_reserved value for ptrace frame: |
1008 | */ | 1128 | */ |
1009 | offset = offsetof(struct fxregs_state, sw_reserved); | 1129 | offset = offsetof(struct fxregs_state, sw_reserved); |
1010 | size = sizeof(xstate_fx_sw_bytes); | 1130 | size = sizeof(xstate_fx_sw_bytes); |
1011 | 1131 | ||
1012 | ret = xstate_copyout(offset, size, kbuf, ubuf, xstate_fx_sw_bytes, 0, count); | 1132 | ret = __copy_xstate_to_user(ubuf, xstate_fx_sw_bytes, offset, size, size_total); |
1013 | |||
1014 | if (ret) | 1133 | if (ret) |
1015 | return ret; | 1134 | return ret; |
1016 | 1135 | ||
@@ -1018,55 +1137,98 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, | |||
1018 | } | 1137 | } |
1019 | 1138 | ||
1020 | /* | 1139 | /* |
1021 | * Convert from a ptrace standard-format buffer to kernel XSAVES format | 1140 | * Convert from a ptrace standard-format kernel buffer to kernel XSAVES format |
1022 | * and copy to the target thread. This is called from xstateregs_set() and | 1141 | * and copy to the target thread. This is called from xstateregs_set(). |
1023 | * there we check the CPU has XSAVES and a whole standard-sized buffer | ||
1024 | * exists. | ||
1025 | */ | 1142 | */ |
1026 | int copyin_to_xsaves(const void *kbuf, const void __user *ubuf, | 1143 | int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf) |
1027 | struct xregs_state *xsave) | ||
1028 | { | 1144 | { |
1029 | unsigned int offset, size; | 1145 | unsigned int offset, size; |
1030 | int i; | 1146 | int i; |
1031 | u64 xfeatures; | 1147 | struct xstate_header hdr; |
1032 | u64 allowed_features; | ||
1033 | 1148 | ||
1034 | offset = offsetof(struct xregs_state, header); | 1149 | offset = offsetof(struct xregs_state, header); |
1035 | size = sizeof(xfeatures); | 1150 | size = sizeof(hdr); |
1036 | 1151 | ||
1037 | if (kbuf) { | 1152 | memcpy(&hdr, kbuf + offset, size); |
1038 | memcpy(&xfeatures, kbuf + offset, size); | 1153 | |
1039 | } else { | 1154 | if (validate_xstate_header(&hdr)) |
1040 | if (__copy_from_user(&xfeatures, ubuf + offset, size)) | 1155 | return -EINVAL; |
1041 | return -EFAULT; | 1156 | |
1157 | for (i = 0; i < XFEATURE_MAX; i++) { | ||
1158 | u64 mask = ((u64)1 << i); | ||
1159 | |||
1160 | if (hdr.xfeatures & mask) { | ||
1161 | void *dst = __raw_xsave_addr(xsave, 1 << i); | ||
1162 | |||
1163 | offset = xstate_offsets[i]; | ||
1164 | size = xstate_sizes[i]; | ||
1165 | |||
1166 | memcpy(dst, kbuf + offset, size); | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | if (xfeatures_mxcsr_quirk(hdr.xfeatures)) { | ||
1171 | offset = offsetof(struct fxregs_state, mxcsr); | ||
1172 | size = MXCSR_AND_FLAGS_SIZE; | ||
1173 | memcpy(&xsave->i387.mxcsr, kbuf + offset, size); | ||
1042 | } | 1174 | } |
1043 | 1175 | ||
1044 | /* | 1176 | /* |
1045 | * Reject if the user sets any disabled or supervisor features: | 1177 | * The state that came in from userspace was user-state only. |
1178 | * Mask all the user states out of 'xfeatures': | ||
1179 | */ | ||
1180 | xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR; | ||
1181 | |||
1182 | /* | ||
1183 | * Add back in the features that came in from userspace: | ||
1046 | */ | 1184 | */ |
1047 | allowed_features = xfeatures_mask & ~XFEATURE_MASK_SUPERVISOR; | 1185 | xsave->header.xfeatures |= hdr.xfeatures; |
1048 | 1186 | ||
1049 | if (xfeatures & ~allowed_features) | 1187 | return 0; |
1188 | } | ||
1189 | |||
1190 | /* | ||
1191 | * Convert from a ptrace or sigreturn standard-format user-space buffer to | ||
1192 | * kernel XSAVES format and copy to the target thread. This is called from | ||
1193 | * xstateregs_set(), as well as potentially from the sigreturn() and | ||
1194 | * rt_sigreturn() system calls. | ||
1195 | */ | ||
1196 | int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf) | ||
1197 | { | ||
1198 | unsigned int offset, size; | ||
1199 | int i; | ||
1200 | struct xstate_header hdr; | ||
1201 | |||
1202 | offset = offsetof(struct xregs_state, header); | ||
1203 | size = sizeof(hdr); | ||
1204 | |||
1205 | if (__copy_from_user(&hdr, ubuf + offset, size)) | ||
1206 | return -EFAULT; | ||
1207 | |||
1208 | if (validate_xstate_header(&hdr)) | ||
1050 | return -EINVAL; | 1209 | return -EINVAL; |
1051 | 1210 | ||
1052 | for (i = 0; i < XFEATURE_MAX; i++) { | 1211 | for (i = 0; i < XFEATURE_MAX; i++) { |
1053 | u64 mask = ((u64)1 << i); | 1212 | u64 mask = ((u64)1 << i); |
1054 | 1213 | ||
1055 | if (xfeatures & mask) { | 1214 | if (hdr.xfeatures & mask) { |
1056 | void *dst = __raw_xsave_addr(xsave, 1 << i); | 1215 | void *dst = __raw_xsave_addr(xsave, 1 << i); |
1057 | 1216 | ||
1058 | offset = xstate_offsets[i]; | 1217 | offset = xstate_offsets[i]; |
1059 | size = xstate_sizes[i]; | 1218 | size = xstate_sizes[i]; |
1060 | 1219 | ||
1061 | if (kbuf) { | 1220 | if (__copy_from_user(dst, ubuf + offset, size)) |
1062 | memcpy(dst, kbuf + offset, size); | 1221 | return -EFAULT; |
1063 | } else { | ||
1064 | if (__copy_from_user(dst, ubuf + offset, size)) | ||
1065 | return -EFAULT; | ||
1066 | } | ||
1067 | } | 1222 | } |
1068 | } | 1223 | } |
1069 | 1224 | ||
1225 | if (xfeatures_mxcsr_quirk(hdr.xfeatures)) { | ||
1226 | offset = offsetof(struct fxregs_state, mxcsr); | ||
1227 | size = MXCSR_AND_FLAGS_SIZE; | ||
1228 | if (__copy_from_user(&xsave->i387.mxcsr, ubuf + offset, size)) | ||
1229 | return -EFAULT; | ||
1230 | } | ||
1231 | |||
1070 | /* | 1232 | /* |
1071 | * The state that came in from userspace was user-state only. | 1233 | * The state that came in from userspace was user-state only. |
1072 | * Mask all the user states out of 'xfeatures': | 1234 | * Mask all the user states out of 'xfeatures': |
@@ -1076,7 +1238,7 @@ int copyin_to_xsaves(const void *kbuf, const void __user *ubuf, | |||
1076 | /* | 1238 | /* |
1077 | * Add back in the features that came in from userspace: | 1239 | * Add back in the features that came in from userspace: |
1078 | */ | 1240 | */ |
1079 | xsave->header.xfeatures |= xfeatures; | 1241 | xsave->header.xfeatures |= hdr.xfeatures; |
1080 | 1242 | ||
1081 | return 0; | 1243 | return 0; |
1082 | } | 1244 | } |