Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 1 | /* |
| 2 | * xsave/xrstor support. |
| 3 | * |
| 4 | * Author: Suresh Siddha <suresh.b.siddha@intel.com> |
| 5 | */ |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 6 | #include <linux/compat.h> |
Fenghua Yu | 7e7ce87 | 2014-05-29 11:12:43 -0700 | [diff] [blame] | 7 | #include <linux/cpu.h> |
Ingo Molnar | 59a36d1 | 2015-04-30 08:53:18 +0200 | [diff] [blame] | 8 | |
Ingo Molnar | df6b35f | 2015-04-24 02:46:00 +0200 | [diff] [blame] | 9 | #include <asm/fpu/api.h> |
Ingo Molnar | 78f7f1e | 2015-04-24 02:54:44 +0200 | [diff] [blame] | 10 | #include <asm/fpu/internal.h> |
Ingo Molnar | fcbc99c | 2015-04-30 08:45:02 +0200 | [diff] [blame] | 11 | #include <asm/fpu/signal.h> |
Ingo Molnar | 59a36d1 | 2015-04-30 08:53:18 +0200 | [diff] [blame] | 12 | #include <asm/fpu/regset.h> |
Ingo Molnar | b992c66 | 2015-04-30 12:45:38 +0200 | [diff] [blame] | 13 | |
Andy Lutomirski | 375074c | 2014-10-24 15:58:07 -0700 | [diff] [blame] | 14 | #include <asm/tlbflush.h> |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 15 | |
Ingo Molnar | 5b07343 | 2015-04-28 08:51:17 +0200 | [diff] [blame] | 16 | static const char *xfeature_names[] = |
| 17 | { |
| 18 | "x87 floating point registers" , |
| 19 | "SSE registers" , |
| 20 | "AVX registers" , |
| 21 | "MPX bounds registers" , |
| 22 | "MPX CSR" , |
| 23 | "AVX-512 opmask" , |
| 24 | "AVX-512 Hi256" , |
| 25 | "AVX-512 ZMM_Hi256" , |
| 26 | "unknown xstate feature" , |
| 27 | }; |
| 28 | |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 29 | /* |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 30 | * Mask of xstate features supported by the CPU and the kernel: |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 31 | */ |
Ingo Molnar | 5b07343 | 2015-04-28 08:51:17 +0200 | [diff] [blame] | 32 | u64 xfeatures_mask __read_mostly; |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 33 | |
Ingo Molnar | 966ece6 | 2015-04-25 05:04:41 +0200 | [diff] [blame] | 34 | static unsigned int xstate_offsets[XFEATURES_NR_MAX], xstate_sizes[XFEATURES_NR_MAX]; |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 35 | static unsigned int xstate_comp_offsets[sizeof(xfeatures_mask)*8]; |
Ingo Molnar | 84246fe4 | 2015-04-24 09:23:59 +0200 | [diff] [blame] | 36 | |
| 37 | /* The number of supported xfeatures in xfeatures_mask: */ |
| 38 | static unsigned int xfeatures_nr; |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 39 | |
Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 40 | /* |
Ingo Molnar | 5b07343 | 2015-04-28 08:51:17 +0200 | [diff] [blame] | 41 | * Return whether the system supports a given xfeature. |
| 42 | * |
| 43 | * Also return the name of the (most advanced) feature that the caller requested: |
| 44 | */ |
| 45 | int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_name) |
| 46 | { |
| 47 | u64 xfeatures_missing = xfeatures_needed & ~xfeatures_mask; |
| 48 | |
| 49 | if (unlikely(feature_name)) { |
| 50 | long xfeature_idx, max_idx; |
| 51 | u64 xfeatures_print; |
| 52 | /* |
| 53 | * So we use FLS here to be able to print the most advanced |
| 54 | * feature that was requested but is missing. So if a driver |
| 55 | * asks about "XSTATE_SSE | XSTATE_YMM" we'll print the |
| 56 | * missing AVX feature - this is the most informative message |
| 57 | * to users: |
| 58 | */ |
| 59 | if (xfeatures_missing) |
| 60 | xfeatures_print = xfeatures_missing; |
| 61 | else |
| 62 | xfeatures_print = xfeatures_needed; |
| 63 | |
| 64 | xfeature_idx = fls64(xfeatures_print)-1; |
| 65 | max_idx = ARRAY_SIZE(xfeature_names)-1; |
| 66 | xfeature_idx = min(xfeature_idx, max_idx); |
| 67 | |
| 68 | *feature_name = xfeature_names[xfeature_idx]; |
| 69 | } |
| 70 | |
| 71 | if (xfeatures_missing) |
| 72 | return 0; |
| 73 | |
| 74 | return 1; |
| 75 | } |
| 76 | EXPORT_SYMBOL_GPL(cpu_has_xfeatures); |
| 77 | |
| 78 | /* |
Ingo Molnar | aeb997b9 | 2015-05-01 09:59:04 +0200 | [diff] [blame] | 79 | * When executing XSAVEOPT (or other optimized XSAVE instructions), if |
| 80 | * a processor implementation detects that an FPU state component is still |
| 81 | * (or is again) in its initialized state, it may clear the corresponding |
| 82 | * bit in the header.xfeatures field, and can skip the writeout of registers |
| 83 | * to the corresponding memory layout. |
Ingo Molnar | 73a3aeb | 2015-04-24 11:32:59 +0200 | [diff] [blame] | 84 | * |
| 85 | * This means that when the bit is zero, the state component might still contain |
| 86 | * some previous - non-initialized register state. |
| 87 | * |
| 88 | * Before writing xstate information to user-space we sanitize those components, |
| 89 | * to always ensure that the memory layout of a feature will be in the init state |
| 90 | * if the corresponding header bit is zero. This is to ensure that user-space doesn't |
| 91 | * see some stale state in the memory layout during signal handling, debugging etc. |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 92 | */ |
Ingo Molnar | 36e49e7f | 2015-04-28 11:25:02 +0200 | [diff] [blame] | 93 | void fpstate_sanitize_xstate(struct fpu *fpu) |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 94 | { |
Ingo Molnar | c47ada3 | 2015-04-30 17:15:32 +0200 | [diff] [blame] | 95 | struct fxregs_state *fx = &fpu->state.fxsave; |
Ingo Molnar | 73a3aeb | 2015-04-24 11:32:59 +0200 | [diff] [blame] | 96 | int feature_bit; |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 97 | u64 xfeatures; |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 98 | |
Ingo Molnar | 1ac91a7 | 2015-04-28 11:17:55 +0200 | [diff] [blame] | 99 | if (!use_xsaveopt()) |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 100 | return; |
| 101 | |
Ingo Molnar | 36e49e7f | 2015-04-28 11:25:02 +0200 | [diff] [blame] | 102 | xfeatures = fpu->state.xsave.header.xfeatures; |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 103 | |
| 104 | /* |
| 105 | * None of the feature bits are in init state. So nothing else |
Lucas De Marchi | 0d2eb44 | 2011-03-17 16:24:16 -0300 | [diff] [blame] | 106 | * to do for us, as the memory layout is up to date. |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 107 | */ |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 108 | if ((xfeatures & xfeatures_mask) == xfeatures_mask) |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 109 | return; |
| 110 | |
| 111 | /* |
| 112 | * FP is in init state |
| 113 | */ |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 114 | if (!(xfeatures & XSTATE_FP)) { |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 115 | fx->cwd = 0x37f; |
| 116 | fx->swd = 0; |
| 117 | fx->twd = 0; |
| 118 | fx->fop = 0; |
| 119 | fx->rip = 0; |
| 120 | fx->rdp = 0; |
| 121 | memset(&fx->st_space[0], 0, 128); |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | * SSE is in init state |
| 126 | */ |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 127 | if (!(xfeatures & XSTATE_SSE)) |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 128 | memset(&fx->xmm_space[0], 0, 256); |
| 129 | |
Ingo Molnar | 73a3aeb | 2015-04-24 11:32:59 +0200 | [diff] [blame] | 130 | /* |
| 131 | * First two features are FPU and SSE, which above we handled |
| 132 | * in a special way already: |
| 133 | */ |
| 134 | feature_bit = 0x2; |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 135 | xfeatures = (xfeatures_mask & ~xfeatures) >> 2; |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 136 | |
| 137 | /* |
Ingo Molnar | 73a3aeb | 2015-04-24 11:32:59 +0200 | [diff] [blame] | 138 | * Update all the remaining memory layouts according to their |
| 139 | * standard xstate layout, if their header bit is in the init |
| 140 | * state: |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 141 | */ |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 142 | while (xfeatures) { |
| 143 | if (xfeatures & 0x1) { |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 144 | int offset = xstate_offsets[feature_bit]; |
| 145 | int size = xstate_sizes[feature_bit]; |
| 146 | |
Ingo Molnar | 73a3aeb | 2015-04-24 11:32:59 +0200 | [diff] [blame] | 147 | memcpy((void *)fx + offset, |
Ingo Molnar | 6f57502 | 2015-04-30 11:07:06 +0200 | [diff] [blame] | 148 | (void *)&init_fpstate.xsave + offset, |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 149 | size); |
| 150 | } |
| 151 | |
Ingo Molnar | 400e4b2 | 2015-04-24 10:19:47 +0200 | [diff] [blame] | 152 | xfeatures >>= 1; |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 153 | feature_bit++; |
| 154 | } |
| 155 | } |
| 156 | |
Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 157 | /* |
Ingo Molnar | 55cc467 | 2015-04-25 06:26:36 +0200 | [diff] [blame] | 158 | * Enable the extended processor state save/restore feature. |
| 159 | * Called once per CPU onlining. |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 160 | */ |
Ingo Molnar | 55cc467 | 2015-04-25 06:26:36 +0200 | [diff] [blame] | 161 | void fpu__init_cpu_xstate(void) |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 162 | { |
Ingo Molnar | e84611f | 2015-04-25 06:41:07 +0200 | [diff] [blame] | 163 | if (!cpu_has_xsave || !xfeatures_mask) |
Ingo Molnar | 55cc467 | 2015-04-25 06:26:36 +0200 | [diff] [blame] | 164 | return; |
| 165 | |
Andy Lutomirski | 375074c | 2014-10-24 15:58:07 -0700 | [diff] [blame] | 166 | cr4_set_bits(X86_CR4_OSXSAVE); |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 167 | xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | /* |
Ingo Molnar | 39f1acd | 2015-05-04 07:37:47 +0200 | [diff] [blame] | 171 | * Record the offsets and sizes of various xstates contained |
| 172 | * in the XSAVE state memory layout. |
| 173 | * |
| 174 | * ( Note that certain features might be non-present, for them |
| 175 | * we'll have 0 offset and 0 size. ) |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 176 | */ |
Robert Richter | 4995b9d | 2010-07-21 19:03:56 +0200 | [diff] [blame] | 177 | static void __init setup_xstate_features(void) |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 178 | { |
Ingo Molnar | 39f1acd | 2015-05-04 07:37:47 +0200 | [diff] [blame] | 179 | u32 eax, ebx, ecx, edx, leaf; |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 180 | |
Ingo Molnar | 84246fe4 | 2015-04-24 09:23:59 +0200 | [diff] [blame] | 181 | xfeatures_nr = fls64(xfeatures_mask); |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 182 | |
Ingo Molnar | 39f1acd | 2015-05-04 07:37:47 +0200 | [diff] [blame] | 183 | for (leaf = 2; leaf < xfeatures_nr; leaf++) { |
Robert Richter | ee813d5 | 2010-07-21 19:03:54 +0200 | [diff] [blame] | 184 | cpuid_count(XSTATE_CPUID, leaf, &eax, &ebx, &ecx, &edx); |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 185 | |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 186 | xstate_offsets[leaf] = ebx; |
| 187 | xstate_sizes[leaf] = eax; |
| 188 | |
Ingo Molnar | 39f1acd | 2015-05-04 07:37:47 +0200 | [diff] [blame] | 189 | printk(KERN_INFO "x86/fpu: xstate_offset[%d]: %04x, xstate_sizes[%d]: %04x\n", leaf, ebx, leaf, eax); |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 190 | leaf++; |
Ingo Molnar | 39f1acd | 2015-05-04 07:37:47 +0200 | [diff] [blame] | 191 | } |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 192 | } |
| 193 | |
Ingo Molnar | 3223187 | 2015-05-04 09:52:42 +0200 | [diff] [blame] | 194 | static void __init print_xstate_feature(u64 xstate_mask) |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 195 | { |
Ingo Molnar | 33588b5 | 2015-04-28 09:17:26 +0200 | [diff] [blame] | 196 | const char *feature_name; |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 197 | |
Ingo Molnar | 33588b5 | 2015-04-28 09:17:26 +0200 | [diff] [blame] | 198 | if (cpu_has_xfeatures(xstate_mask, &feature_name)) |
| 199 | pr_info("x86/fpu: Supporting XSAVE feature 0x%02Lx: '%s'\n", xstate_mask, feature_name); |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | /* |
| 203 | * Print out all the supported xstate features: |
| 204 | */ |
Ingo Molnar | 3223187 | 2015-05-04 09:52:42 +0200 | [diff] [blame] | 205 | static void __init print_xstate_features(void) |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 206 | { |
Ingo Molnar | 33588b5 | 2015-04-28 09:17:26 +0200 | [diff] [blame] | 207 | print_xstate_feature(XSTATE_FP); |
| 208 | print_xstate_feature(XSTATE_SSE); |
| 209 | print_xstate_feature(XSTATE_YMM); |
| 210 | print_xstate_feature(XSTATE_BNDREGS); |
| 211 | print_xstate_feature(XSTATE_BNDCSR); |
| 212 | print_xstate_feature(XSTATE_OPMASK); |
| 213 | print_xstate_feature(XSTATE_ZMM_Hi256); |
| 214 | print_xstate_feature(XSTATE_Hi16_ZMM); |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 215 | } |
| 216 | |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 217 | /* |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 218 | * This function sets up offsets and sizes of all extended states in |
| 219 | * xsave area. This supports both standard format and compacted format |
| 220 | * of the xsave aread. |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 221 | */ |
Ingo Molnar | 3223187 | 2015-05-04 09:52:42 +0200 | [diff] [blame] | 222 | static void __init setup_xstate_comp(void) |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 223 | { |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 224 | unsigned int xstate_comp_sizes[sizeof(xfeatures_mask)*8]; |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 225 | int i; |
| 226 | |
Fenghua Yu | 8ff925e | 2014-05-30 14:59:24 -0700 | [diff] [blame] | 227 | /* |
| 228 | * The FP xstates and SSE xstates are legacy states. They are always |
| 229 | * in the fixed offsets in the xsave area in either compacted form |
| 230 | * or standard form. |
| 231 | */ |
| 232 | xstate_comp_offsets[0] = 0; |
Ingo Molnar | c47ada3 | 2015-04-30 17:15:32 +0200 | [diff] [blame] | 233 | xstate_comp_offsets[1] = offsetof(struct fxregs_state, xmm_space); |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 234 | |
| 235 | if (!cpu_has_xsaves) { |
Ingo Molnar | 84246fe4 | 2015-04-24 09:23:59 +0200 | [diff] [blame] | 236 | for (i = 2; i < xfeatures_nr; i++) { |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 237 | if (test_bit(i, (unsigned long *)&xfeatures_mask)) { |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 238 | xstate_comp_offsets[i] = xstate_offsets[i]; |
| 239 | xstate_comp_sizes[i] = xstate_sizes[i]; |
| 240 | } |
| 241 | } |
| 242 | return; |
| 243 | } |
| 244 | |
| 245 | xstate_comp_offsets[2] = FXSAVE_SIZE + XSAVE_HDR_SIZE; |
| 246 | |
Ingo Molnar | 84246fe4 | 2015-04-24 09:23:59 +0200 | [diff] [blame] | 247 | for (i = 2; i < xfeatures_nr; i++) { |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 248 | if (test_bit(i, (unsigned long *)&xfeatures_mask)) |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 249 | xstate_comp_sizes[i] = xstate_sizes[i]; |
| 250 | else |
| 251 | xstate_comp_sizes[i] = 0; |
| 252 | |
| 253 | if (i > 2) |
| 254 | xstate_comp_offsets[i] = xstate_comp_offsets[i-1] |
| 255 | + xstate_comp_sizes[i-1]; |
| 256 | |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | /* |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 261 | * setup the xstate image representing the init state |
| 262 | */ |
Ingo Molnar | 3223187 | 2015-05-04 09:52:42 +0200 | [diff] [blame] | 263 | static void __init setup_init_fpu_buf(void) |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 264 | { |
Ingo Molnar | e97131a | 2015-05-05 11:34:49 +0200 | [diff] [blame] | 265 | static int on_boot_cpu = 1; |
| 266 | |
| 267 | WARN_ON_FPU(!on_boot_cpu); |
| 268 | on_boot_cpu = 0; |
| 269 | |
Suresh Siddha | 5d2bd70 | 2012-09-06 14:58:52 -0700 | [diff] [blame] | 270 | if (!cpu_has_xsave) |
| 271 | return; |
| 272 | |
| 273 | setup_xstate_features(); |
Ingo Molnar | 69496e1 | 2015-04-24 08:48:01 +0200 | [diff] [blame] | 274 | print_xstate_features(); |
Suresh Siddha | a1488f8 | 2010-07-19 16:05:48 -0700 | [diff] [blame] | 275 | |
Fenghua Yu | 47c2f29 | 2014-05-29 11:12:42 -0700 | [diff] [blame] | 276 | if (cpu_has_xsaves) { |
Ingo Molnar | 6f57502 | 2015-04-30 11:07:06 +0200 | [diff] [blame] | 277 | init_fpstate.xsave.header.xcomp_bv = (u64)1 << 63 | xfeatures_mask; |
| 278 | init_fpstate.xsave.header.xfeatures = xfeatures_mask; |
Fenghua Yu | 47c2f29 | 2014-05-29 11:12:42 -0700 | [diff] [blame] | 279 | } |
| 280 | |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 281 | /* |
| 282 | * Init all the features state with header_bv being 0x0 |
| 283 | */ |
Ingo Molnar | d65fcd6 | 2015-05-27 14:04:44 +0200 | [diff] [blame^] | 284 | copy_kernel_to_xregs_booting(&init_fpstate.xsave); |
Ingo Molnar | 3e261c1 | 2015-04-22 15:08:34 +0200 | [diff] [blame] | 285 | |
Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 286 | /* |
| 287 | * Dump the init state again. This is to identify the init state |
| 288 | * of any feature which is not represented by all zero's. |
| 289 | */ |
Ingo Molnar | c681314 | 2015-04-30 11:34:09 +0200 | [diff] [blame] | 290 | copy_xregs_to_kernel_booting(&init_fpstate.xsave); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 291 | } |
| 292 | |
Fenghua Yu | 7e7ce87 | 2014-05-29 11:12:43 -0700 | [diff] [blame] | 293 | /* |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 294 | * Calculate total size of enabled xstates in XCR0/xfeatures_mask. |
Fenghua Yu | 7e7ce87 | 2014-05-29 11:12:43 -0700 | [diff] [blame] | 295 | */ |
| 296 | static void __init init_xstate_size(void) |
| 297 | { |
| 298 | unsigned int eax, ebx, ecx, edx; |
| 299 | int i; |
| 300 | |
| 301 | if (!cpu_has_xsaves) { |
| 302 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); |
| 303 | xstate_size = ebx; |
| 304 | return; |
| 305 | } |
| 306 | |
| 307 | xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE; |
| 308 | for (i = 2; i < 64; i++) { |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 309 | if (test_bit(i, (unsigned long *)&xfeatures_mask)) { |
Fenghua Yu | 7e7ce87 | 2014-05-29 11:12:43 -0700 | [diff] [blame] | 310 | cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx); |
| 311 | xstate_size += eax; |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 316 | /* |
| 317 | * Enable and initialize the xsave feature. |
Ingo Molnar | 55cc467 | 2015-04-25 06:26:36 +0200 | [diff] [blame] | 318 | * Called once per system bootup. |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 319 | */ |
Ingo Molnar | 3223187 | 2015-05-04 09:52:42 +0200 | [diff] [blame] | 320 | void __init fpu__init_system_xstate(void) |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 321 | { |
| 322 | unsigned int eax, ebx, ecx, edx; |
Ingo Molnar | e97131a | 2015-05-05 11:34:49 +0200 | [diff] [blame] | 323 | static int on_boot_cpu = 1; |
| 324 | |
| 325 | WARN_ON_FPU(!on_boot_cpu); |
| 326 | on_boot_cpu = 0; |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 327 | |
Ingo Molnar | e9dbfd6 | 2015-04-25 06:47:24 +0200 | [diff] [blame] | 328 | if (!cpu_has_xsave) { |
| 329 | pr_info("x86/fpu: Legacy x87 FPU detected.\n"); |
| 330 | return; |
| 331 | } |
| 332 | |
Robert Richter | ee813d5 | 2010-07-21 19:03:54 +0200 | [diff] [blame] | 333 | if (boot_cpu_data.cpuid_level < XSTATE_CPUID) { |
Ingo Molnar | e97131a | 2015-05-05 11:34:49 +0200 | [diff] [blame] | 334 | WARN_ON_FPU(1); |
Robert Richter | ee813d5 | 2010-07-21 19:03:54 +0200 | [diff] [blame] | 335 | return; |
| 336 | } |
| 337 | |
| 338 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 339 | xfeatures_mask = eax + ((u64)edx << 32); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 340 | |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 341 | if ((xfeatures_mask & XSTATE_FPSSE) != XSTATE_FPSSE) { |
| 342 | pr_err("x86/fpu: FP/SSE not present amongst the CPU's xstate features: 0x%llx.\n", xfeatures_mask); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 343 | BUG(); |
| 344 | } |
| 345 | |
Ingo Molnar | 6e55359 | 2015-05-24 09:58:12 +0200 | [diff] [blame] | 346 | /* Support only the state known to the OS: */ |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 347 | xfeatures_mask = xfeatures_mask & XCNTXT_MASK; |
Robert Richter | 97e80a7 | 2010-07-21 19:03:53 +0200 | [diff] [blame] | 348 | |
Ingo Molnar | 55cc467 | 2015-04-25 06:26:36 +0200 | [diff] [blame] | 349 | /* Enable xstate instructions to be able to continue with initialization: */ |
| 350 | fpu__init_cpu_xstate(); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 351 | |
Ingo Molnar | 6e55359 | 2015-05-24 09:58:12 +0200 | [diff] [blame] | 352 | /* Recompute the context size for enabled features: */ |
Fenghua Yu | 7e7ce87 | 2014-05-29 11:12:43 -0700 | [diff] [blame] | 353 | init_xstate_size(); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 354 | |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 355 | update_regset_xstate_info(xstate_size, xfeatures_mask); |
Ingo Molnar | b992c66 | 2015-04-30 12:45:38 +0200 | [diff] [blame] | 356 | fpu__init_prepare_fx_sw_frame(); |
Suresh Siddha | 5d2bd70 | 2012-09-06 14:58:52 -0700 | [diff] [blame] | 357 | setup_init_fpu_buf(); |
Ingo Molnar | 5fd402d | 2015-05-04 09:43:55 +0200 | [diff] [blame] | 358 | setup_xstate_comp(); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 359 | |
Ingo Molnar | 32d4d9c | 2015-04-24 03:25:18 +0200 | [diff] [blame] | 360 | pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is 0x%x bytes, using '%s' format.\n", |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 361 | xfeatures_mask, |
Ingo Molnar | 32d4d9c | 2015-04-24 03:25:18 +0200 | [diff] [blame] | 362 | xstate_size, |
| 363 | cpu_has_xsaves ? "compacted" : "standard"); |
Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 364 | } |
Robert Richter | 82d4150 | 2010-07-20 20:50:51 +0200 | [diff] [blame] | 365 | |
H. Peter Anvin | 1cff92d | 2010-07-21 14:23:10 -0700 | [diff] [blame] | 366 | /* |
Ingo Molnar | 9254aaa | 2015-04-24 10:02:32 +0200 | [diff] [blame] | 367 | * Restore minimal FPU state after suspend: |
| 368 | */ |
| 369 | void fpu__resume_cpu(void) |
| 370 | { |
| 371 | /* |
| 372 | * Restore XCR0 on xsave capable CPUs: |
| 373 | */ |
| 374 | if (cpu_has_xsave) |
| 375 | xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask); |
| 376 | } |
| 377 | |
| 378 | /* |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 379 | * Given the xsave area and a state inside, this function returns the |
| 380 | * address of the state. |
| 381 | * |
| 382 | * This is the API that is called to get xstate address in either |
| 383 | * standard format or compacted format of xsave area. |
| 384 | * |
| 385 | * Inputs: |
| 386 | * xsave: base address of the xsave area; |
| 387 | * xstate: state which is defined in xsave.h (e.g. XSTATE_FP, XSTATE_SSE, |
| 388 | * etc.) |
| 389 | * Output: |
| 390 | * address of the state in the xsave area. |
| 391 | */ |
Ingo Molnar | c47ada3 | 2015-04-30 17:15:32 +0200 | [diff] [blame] | 392 | void *get_xsave_addr(struct xregs_state *xsave, int xstate) |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 393 | { |
| 394 | int feature = fls64(xstate) - 1; |
Ingo Molnar | 614df7f | 2015-04-24 09:20:33 +0200 | [diff] [blame] | 395 | if (!test_bit(feature, (unsigned long *)&xfeatures_mask)) |
Fenghua Yu | 7496d64 | 2014-05-29 11:12:44 -0700 | [diff] [blame] | 396 | return NULL; |
| 397 | |
| 398 | return (void *)xsave + xstate_comp_offsets[feature]; |
| 399 | } |
Paolo Bonzini | ba7b392 | 2014-11-24 10:57:42 +0100 | [diff] [blame] | 400 | EXPORT_SYMBOL_GPL(get_xsave_addr); |