|
30 | 30 | #endif |
31 | 31 | #else |
32 | 32 | static unsigned int *wolfcrypt_linuxkm_fpu_states = NULL; |
| 33 | + #define WC_FPU_COUNT_MASK 0x7fffffffU |
| 34 | + #define WC_FPU_SAVED_MASK 0x80000000U |
33 | 35 | #endif |
34 | 36 |
|
35 | 37 | static WARN_UNUSED_RESULT inline int am_in_hard_interrupt_handler(void) |
|
223 | 225 | */ |
224 | 226 | ((unsigned char *)wolfcrypt_linuxkm_fpu_states[processor_id])[PAGE_SIZE-1] = 1; |
225 | 227 | #else /* !LINUXKM_SIMD_IRQ */ |
226 | | - if (wolfcrypt_linuxkm_fpu_states[processor_id] != 0) { |
227 | | - if (wolfcrypt_linuxkm_fpu_states[processor_id] == ~0U) { |
| 228 | + if (wolfcrypt_linuxkm_fpu_states[processor_id] != 0U) { |
| 229 | + if ((wolfcrypt_linuxkm_fpu_states[processor_id] & WC_FPU_COUNT_MASK) |
| 230 | + == WC_FPU_COUNT_MASK) |
| 231 | + { |
228 | 232 | preempt_enable(); |
229 | 233 | pr_err("save_vector_registers_x86 recursion register overflow for " |
230 | 234 | "cpu id %d.\n", processor_id); |
|
234 | 238 | return 0; |
235 | 239 | } |
236 | 240 | } |
237 | | - kernel_fpu_begin(); |
238 | | - preempt_enable(); /* kernel_fpu_begin() does its own |
239 | | - * preempt_disable(). decrement ours. |
240 | | - */ |
241 | | - wolfcrypt_linuxkm_fpu_states[processor_id] = 1; |
| 241 | + |
| 242 | + /* if kernel_fpu_begin() won't actually save the reg file (because |
| 243 | + * it was already saved and invalidated, or because we're in a |
| 244 | + * kernel thread), don't call kernel_fpu_begin() here, nor call |
| 245 | + * kernel_fpu_end() in cleanup. this avoids pointless overhead. in |
| 246 | + * kernels >=5.17.12 (from changes to irq_fpu_usable() in linux |
| 247 | + * commit 59f5ede3bc0f, backported somewhere >5.17.5), this also |
| 248 | + * fixes register corruption. |
| 249 | + */ |
| 250 | + if ((current->flags & PF_KTHREAD) || |
| 251 | + test_thread_flag(TIF_NEED_FPU_LOAD)) |
| 252 | + { |
| 253 | + wolfcrypt_linuxkm_fpu_states[processor_id] = |
| 254 | + WC_FPU_SAVED_MASK + 1U; /* set msb 1 to inhibit kernel_fpu_end() at cleanup. */ |
| 255 | + /* keep preempt_disable()d from above. */ |
| 256 | + } else { |
| 257 | + kernel_fpu_begin(); |
| 258 | + preempt_enable(); /* kernel_fpu_begin() does its own |
| 259 | + * preempt_disable(). decrement ours. |
| 260 | + */ |
| 261 | + wolfcrypt_linuxkm_fpu_states[processor_id] = 1U; /* set msb 0 to trigger kernel_fpu_end() at cleanup. */ |
| 262 | + } |
242 | 263 | #endif /* !LINUXKM_SIMD_IRQ */ |
243 | 264 |
|
244 | 265 | return 0; |
|
287 | 308 | kernel_fpu_end(); |
288 | 309 | } |
289 | 310 | #else /* !LINUXKM_SIMD_IRQ */ |
290 | | - if (wolfcrypt_linuxkm_fpu_states[processor_id] == 0) |
| 311 | + if ((wolfcrypt_linuxkm_fpu_states[processor_id] & WC_FPU_COUNT_MASK) == 0U) |
291 | 312 | { |
292 | 313 | pr_err("restore_vector_registers_x86 called for cpu id %d " |
293 | 314 | "without saved context.\n", processor_id); |
294 | 315 | return; |
295 | 316 | } |
296 | 317 |
|
297 | | - if (--wolfcrypt_linuxkm_fpu_states[processor_id] > 0) { |
298 | | - preempt_enable(); /* preempt_disable count will still be nonzero after this decrement. */ |
| 318 | + if ((--wolfcrypt_linuxkm_fpu_states[processor_id] & WC_FPU_COUNT_MASK) > 0U) { |
| 319 | + preempt_enable(); /* preempt_disable count may still be nonzero |
| 320 | + * after this decrement, but any remaining |
| 321 | + * count(s) aren't ours. |
| 322 | + */ |
299 | 323 | return; |
300 | 324 | } |
301 | 325 |
|
302 | | - kernel_fpu_end(); |
| 326 | + if (wolfcrypt_linuxkm_fpu_states[processor_id] == 0U) { |
| 327 | + kernel_fpu_end(); |
| 328 | + } else { |
| 329 | + preempt_enable(); /* preempt_disable count will still be nonzero |
| 330 | + * after this decrement. |
| 331 | + */ |
| 332 | + wolfcrypt_linuxkm_fpu_states[processor_id] = 0U; |
| 333 | + } |
303 | 334 | #endif /* !LINUXKM_SIMD_IRQ */ |
304 | 335 |
|
305 | 336 | return; |
|
0 commit comments