diff options
| -rw-r--r-- | arch/x86/include/asm/cpufeature.h | 86 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/common.c | 6 |
2 files changed, 91 insertions, 1 deletions
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 252b28f1176a..47538a61c91b 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
| @@ -357,6 +357,7 @@ extern const char * const x86_power_flags[32]; | |||
| 357 | 357 | ||
| 358 | #if __GNUC__ >= 4 | 358 | #if __GNUC__ >= 4 |
| 359 | extern void warn_pre_alternatives(void); | 359 | extern void warn_pre_alternatives(void); |
| 360 | extern bool __static_cpu_has_safe(u16 bit); | ||
| 360 | 361 | ||
| 361 | /* | 362 | /* |
| 362 | * Static testing of CPU features. Used the same as boot_cpu_has(). | 363 | * Static testing of CPU features. Used the same as boot_cpu_has(). |
| @@ -437,11 +438,94 @@ static __always_inline __pure bool __static_cpu_has(u16 bit) | |||
| 437 | __static_cpu_has(bit) : \ | 438 | __static_cpu_has(bit) : \ |
| 438 | boot_cpu_has(bit) \ | 439 | boot_cpu_has(bit) \ |
| 439 | ) | 440 | ) |
| 441 | |||
| 442 | static __always_inline __pure bool _static_cpu_has_safe(u16 bit) | ||
| 443 | { | ||
| 444 | #if __GNUC__ > 4 || __GNUC_MINOR__ >= 5 | ||
| 445 | /* | ||
| 446 | * We need to spell the jumps to the compiler because, depending on the offset, | ||
| 447 | * the replacement jump can be bigger than the original jump, and this we cannot | ||
| 448 | * have. Thus, we force the jump to the widest, 4-byte, signed relative | ||
| 449 | * offset even though the last would often fit in less bytes. | ||
| 450 | */ | ||
| 451 | asm goto("1: .byte 0xe9\n .long %l[t_dynamic] - 2f\n" | ||
| 452 | "2:\n" | ||
| 453 | ".section .altinstructions,\"a\"\n" | ||
| 454 | " .long 1b - .\n" /* src offset */ | ||
| 455 | " .long 3f - .\n" /* repl offset */ | ||
| 456 | " .word %P1\n" /* always replace */ | ||
| 457 | " .byte 2b - 1b\n" /* src len */ | ||
| 458 | " .byte 4f - 3f\n" /* repl len */ | ||
| 459 | ".previous\n" | ||
| 460 | ".section .altinstr_replacement,\"ax\"\n" | ||
| 461 | "3: .byte 0xe9\n .long %l[t_no] - 2b\n" | ||
| 462 | "4:\n" | ||
| 463 | ".previous\n" | ||
| 464 | ".section .altinstructions,\"a\"\n" | ||
| 465 | " .long 1b - .\n" /* src offset */ | ||
| 466 | " .long 0\n" /* no replacement */ | ||
| 467 | " .word %P0\n" /* feature bit */ | ||
| 468 | " .byte 2b - 1b\n" /* src len */ | ||
| 469 | " .byte 0\n" /* repl len */ | ||
| 470 | ".previous\n" | ||
| 471 | : : "i" (bit), "i" (X86_FEATURE_ALWAYS) | ||
| 472 | : : t_dynamic, t_no); | ||
| 473 | return true; | ||
| 474 | t_no: | ||
| 475 | return false; | ||
| 476 | t_dynamic: | ||
| 477 | return __static_cpu_has_safe(bit); | ||
| 478 | #else /* GCC_VERSION >= 40500 */ | ||
| 479 | u8 flag; | ||
| 480 | /* Open-coded due to __stringify() in ALTERNATIVE() */ | ||
| 481 | asm volatile("1: movb $2,%0\n" | ||
| 482 | "2:\n" | ||
| 483 | ".section .altinstructions,\"a\"\n" | ||
| 484 | " .long 1b - .\n" /* src offset */ | ||
| 485 | " .long 3f - .\n" /* repl offset */ | ||
| 486 | " .word %P2\n" /* always replace */ | ||
| 487 | " .byte 2b - 1b\n" /* source len */ | ||
| 488 | " .byte 4f - 3f\n" /* replacement len */ | ||
| 489 | ".previous\n" | ||
| 490 | ".section .discard,\"aw\",@progbits\n" | ||
| 491 | " .byte 0xff + (4f-3f) - (2b-1b)\n" /* size check */ | ||
| 492 | ".previous\n" | ||
| 493 | ".section .altinstr_replacement,\"ax\"\n" | ||
| 494 | "3: movb $0,%0\n" | ||
| 495 | "4:\n" | ||
| 496 | ".previous\n" | ||
| 497 | ".section .altinstructions,\"a\"\n" | ||
| 498 | " .long 1b - .\n" /* src offset */ | ||
| 499 | " .long 5f - .\n" /* repl offset */ | ||
| 500 | " .word %P1\n" /* feature bit */ | ||
| 501 | " .byte 4b - 3b\n" /* src len */ | ||
| 502 | " .byte 6f - 5f\n" /* repl len */ | ||
| 503 | ".previous\n" | ||
| 504 | ".section .discard,\"aw\",@progbits\n" | ||
| 505 | " .byte 0xff + (6f-5f) - (4b-3b)\n" /* size check */ | ||
| 506 | ".previous\n" | ||
| 507 | ".section .altinstr_replacement,\"ax\"\n" | ||
| 508 | "5: movb $1,%0\n" | ||
| 509 | "6:\n" | ||
| 510 | ".previous\n" | ||
| 511 | : "=qm" (flag) | ||
| 512 | : "i" (bit), "i" (X86_FEATURE_ALWAYS)); | ||
| 513 | return (flag == 2 ? __static_cpu_has_safe(bit) : flag); | ||
| 514 | #endif | ||
| 515 | } | ||
| 516 | |||
| 517 | #define static_cpu_has_safe(bit) \ | ||
| 518 | ( \ | ||
| 519 | __builtin_constant_p(boot_cpu_has(bit)) ? \ | ||
| 520 | boot_cpu_has(bit) : \ | ||
| 521 | _static_cpu_has_safe(bit) \ | ||
| 522 | ) | ||
| 440 | #else | 523 | #else |
| 441 | /* | 524 | /* |
| 442 | * gcc 3.x is too stupid to do the static test; fall back to dynamic. | 525 | * gcc 3.x is too stupid to do the static test; fall back to dynamic. |
| 443 | */ | 526 | */ |
| 444 | #define static_cpu_has(bit) boot_cpu_has(bit) | 527 | #define static_cpu_has(bit) boot_cpu_has(bit) |
| 528 | #define static_cpu_has_safe(bit) boot_cpu_has(bit) | ||
| 445 | #endif | 529 | #endif |
| 446 | 530 | ||
| 447 | #define cpu_has_bug(c, bit) cpu_has(c, (bit)) | 531 | #define cpu_has_bug(c, bit) cpu_has(c, (bit)) |
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 59adaa17e220..a4a07c0acb1f 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c | |||
| @@ -1372,3 +1372,9 @@ void warn_pre_alternatives(void) | |||
| 1372 | } | 1372 | } |
| 1373 | EXPORT_SYMBOL_GPL(warn_pre_alternatives); | 1373 | EXPORT_SYMBOL_GPL(warn_pre_alternatives); |
| 1374 | #endif | 1374 | #endif |
| 1375 | |||
| 1376 | inline bool __static_cpu_has_safe(u16 bit) | ||
| 1377 | { | ||
| 1378 | return boot_cpu_has(bit); | ||
| 1379 | } | ||
| 1380 | EXPORT_SYMBOL_GPL(__static_cpu_has_safe); | ||
