Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | // SPDX-License-Identifier: GPL-2.0-only /* * x86 FPU boot time init code: */ #include <asm/fpu/api.h> #include <asm/tlbflush.h> #include <asm/setup.h> #include <linux/sched.h> #include <linux/sched/task.h> #include <linux/init.h> #include "internal.h" #include "legacy.h" #include "xstate.h" /* * Initialize the registers found in all CPUs, CR0 and CR4: */ static void fpu__init_cpu_generic(void) { unsigned long cr0; unsigned long cr4_mask = 0; if (boot_cpu_has(X86_FEATURE_FXSR)) cr4_mask |= X86_CR4_OSFXSR; if (boot_cpu_has(X86_FEATURE_XMM)) cr4_mask |= X86_CR4_OSXMMEXCPT; if (cr4_mask) cr4_set_bits(cr4_mask); cr0 = read_cr0(); cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ if (!boot_cpu_has(X86_FEATURE_FPU)) cr0 |= X86_CR0_EM; write_cr0(cr0); /* Flush out any pending x87 state: */ #ifdef CONFIG_MATH_EMULATION if (!boot_cpu_has(X86_FEATURE_FPU)) fpstate_init_soft(¤t->thread.fpu.fpstate->regs.soft); else #endif asm volatile ("fninit"); } /* * Enable all supported FPU features. Called when a CPU is brought online: */ void fpu__init_cpu(void) { fpu__init_cpu_generic(); fpu__init_cpu_xstate(); } static bool __init fpu__probe_without_cpuid(void) { unsigned long cr0; u16 fsw, fcw; fsw = fcw = 0xffff; cr0 = read_cr0(); cr0 &= ~(X86_CR0_TS | X86_CR0_EM); write_cr0(cr0); asm volatile("fninit ; fnstsw %0 ; fnstcw %1" : "+m" (fsw), "+m" (fcw)); pr_info("x86/fpu: Probing for FPU: FSW=0x%04hx FCW=0x%04hx\n", fsw, fcw); return fsw == 0 && (fcw & 0x103f) == 0x003f; } static void __init fpu__init_system_early_generic(void) { if (!boot_cpu_has(X86_FEATURE_CPUID) && !test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) { if (fpu__probe_without_cpuid()) setup_force_cpu_cap(X86_FEATURE_FPU); else setup_clear_cpu_cap(X86_FEATURE_FPU); } #ifndef CONFIG_MATH_EMULATION if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_FPU)) { pr_emerg("x86/fpu: Giving up, no FPU found and no math emulation present\n"); for (;;) asm volatile("hlt"); } #endif } /* * Boot time FPU feature detection code: */ unsigned int mxcsr_feature_mask __ro_after_init = 0xffffffffu; EXPORT_SYMBOL_GPL(mxcsr_feature_mask); static void __init fpu__init_system_mxcsr(void) { unsigned int mask = 0; if (boot_cpu_has(X86_FEATURE_FXSR)) { /* Static because GCC does not get 16-byte stack alignment right: */ static struct fxregs_state fxregs __initdata; asm volatile("fxsave %0" : "+m" (fxregs)); mask = fxregs.mxcsr_mask; /* * If zero then use the default features mask, * which has all features set, except the * denormals-are-zero feature bit: */ if (mask == 0) mask = 0x0000ffbf; } mxcsr_feature_mask &= mask; } /* * Once per bootup FPU initialization sequences that will run on most x86 CPUs: */ static void __init fpu__init_system_generic(void) { /* * Set up the legacy init FPU context. Will be updated when the * CPU supports XSAVE[S]. */ fpstate_init_user(&init_fpstate); fpu__init_system_mxcsr(); } /* * Enforce that 'MEMBER' is the last field of 'TYPE'. * * Align the computed size with alignment of the TYPE, * because that's how C aligns structs. */ #define CHECK_MEMBER_AT_END_OF(TYPE, MEMBER) \ BUILD_BUG_ON(sizeof(TYPE) != \ ALIGN(offsetofend(TYPE, MEMBER), _Alignof(TYPE))) /* * We append the 'struct fpu' to the task_struct: */ static void __init fpu__init_task_struct_size(void) { int task_size = sizeof(struct task_struct); /* * Subtract off the static size of the register state. * It potentially has a bunch of padding. */ task_size -= sizeof(current->thread.fpu.__fpstate.regs); /* * Add back the dynamically-calculated register state * size. */ task_size += fpu_kernel_cfg.default_size; /* * We dynamically size 'struct fpu', so we require that * it be at the end of 'thread_struct' and that * 'thread_struct' be at the end of 'task_struct'. If * you hit a compile error here, check the structure to * see if something got added to the end. */ CHECK_MEMBER_AT_END_OF(struct fpu, __fpstate); CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu); CHECK_MEMBER_AT_END_OF(struct task_struct, thread); arch_task_struct_size = task_size; } /* * Set up the user and kernel xstate sizes based on the legacy FPU context size. * * We set this up first, and later it will be overwritten by * fpu__init_system_xstate() if the CPU knows about xstates. */ static void __init fpu__init_system_xstate_size_legacy(void) { unsigned int size; /* * Note that the size configuration might be overwritten later * during fpu__init_system_xstate(). */ if (!cpu_feature_enabled(X86_FEATURE_FPU)) { size = sizeof(struct swregs_state); } else if (cpu_feature_enabled(X86_FEATURE_FXSR)) { size = sizeof(struct fxregs_state); fpu_user_cfg.legacy_features = XFEATURE_MASK_FPSSE; } else { size = sizeof(struct fregs_state); fpu_user_cfg.legacy_features = XFEATURE_MASK_FP; } fpu_kernel_cfg.max_size = size; fpu_kernel_cfg.default_size = size; fpu_user_cfg.max_size = size; fpu_user_cfg.default_size = size; fpstate_reset(¤t->thread.fpu); } /* * Called on the boot CPU once per system bootup, to set up the initial * FPU state that is later cloned into all processes: */ void __init fpu__init_system(void) { fpstate_reset(¤t->thread.fpu); fpu__init_system_early_generic(); /* * The FPU has to be operational for some of the * later FPU init activities: */ fpu__init_cpu(); fpu__init_system_generic(); fpu__init_system_xstate_size_legacy(); fpu__init_system_xstate(fpu_kernel_cfg.max_size); fpu__init_task_struct_size(); } |