| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* | |
| * CPUID-related helpers/definitions | |
| */ | |
| #ifndef _ASM_X86_CPUID_H | |
| #define _ASM_X86_CPUID_H | |
| #include <asm/string.h> | |
| struct cpuid_regs { | |
| u32 eax, ebx, ecx, edx; | |
| }; | |
| enum cpuid_regs_idx { | |
| CPUID_EAX = 0, | |
| CPUID_EBX, | |
| CPUID_ECX, | |
| CPUID_EDX, | |
| }; | |
| #ifdef CONFIG_X86_32 | |
| extern int have_cpuid_p(void); | |
| #else | |
| static inline int have_cpuid_p(void) | |
| { | |
| return 1; | |
| } | |
| #endif | |
| static inline void native_cpuid(unsigned int *eax, unsigned int *ebx, | |
| unsigned int *ecx, unsigned int *edx) | |
| { | |
| /* ecx is often an input as well as an output. */ | |
| asm volatile("cpuid" | |
| : "=a" (*eax), | |
| "=b" (*ebx), | |
| "=c" (*ecx), | |
| "=d" (*edx) | |
| : "0" (*eax), "2" (*ecx) | |
| : "memory"); | |
| } | |
| #define native_cpuid_reg(reg) \ | |
| static inline unsigned int native_cpuid_##reg(unsigned int op) \ | |
| { \ | |
| unsigned int eax = op, ebx, ecx = 0, edx; \ | |
| \ | |
| native_cpuid(&eax, &ebx, &ecx, &edx); \ | |
| \ | |
| return reg; \ | |
| } | |
| /* | |
| * Native CPUID functions returning a single datum. | |
| */ | |
| native_cpuid_reg(eax) | |
| native_cpuid_reg(ebx) | |
| native_cpuid_reg(ecx) | |
| native_cpuid_reg(edx) | |
| #ifdef CONFIG_PARAVIRT_XXL | |
| #include <asm/paravirt.h> | |
| #else | |
| #define __cpuid native_cpuid | |
| #endif | |
| /* | |
| * Generic CPUID function | |
| * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx | |
| * resulting in stale register contents being returned. | |
| */ | |
| static inline void cpuid(unsigned int op, | |
| unsigned int *eax, unsigned int *ebx, | |
| unsigned int *ecx, unsigned int *edx) | |
| { | |
| *eax = op; | |
| *ecx = 0; | |
| __cpuid(eax, ebx, ecx, edx); | |
| } | |
| /* Some CPUID calls want 'count' to be placed in ecx */ | |
| static inline void cpuid_count(unsigned int op, int count, | |
| unsigned int *eax, unsigned int *ebx, | |
| unsigned int *ecx, unsigned int *edx) | |
| { | |
| *eax = op; | |
| *ecx = count; | |
| __cpuid(eax, ebx, ecx, edx); | |
| } | |
| /* | |
| * CPUID functions returning a single datum | |
| */ | |
| static inline unsigned int cpuid_eax(unsigned int op) | |
| { | |
| unsigned int eax, ebx, ecx, edx; | |
| cpuid(op, &eax, &ebx, &ecx, &edx); | |
| return eax; | |
| } | |
| static inline unsigned int cpuid_ebx(unsigned int op) | |
| { | |
| unsigned int eax, ebx, ecx, edx; | |
| cpuid(op, &eax, &ebx, &ecx, &edx); | |
| return ebx; | |
| } | |
| static inline unsigned int cpuid_ecx(unsigned int op) | |
| { | |
| unsigned int eax, ebx, ecx, edx; | |
| cpuid(op, &eax, &ebx, &ecx, &edx); | |
| return ecx; | |
| } | |
| static inline unsigned int cpuid_edx(unsigned int op) | |
| { | |
| unsigned int eax, ebx, ecx, edx; | |
| cpuid(op, &eax, &ebx, &ecx, &edx); | |
| return edx; | |
| } | |
| static inline void __cpuid_read(unsigned int leaf, unsigned int subleaf, u32 *regs) | |
| { | |
| regs[CPUID_EAX] = leaf; | |
| regs[CPUID_ECX] = subleaf; | |
| __cpuid(regs + CPUID_EAX, regs + CPUID_EBX, regs + CPUID_ECX, regs + CPUID_EDX); | |
| } | |
| #define cpuid_subleaf(leaf, subleaf, regs) { \ | |
| static_assert(sizeof(*(regs)) == 16); \ | |
| __cpuid_read(leaf, subleaf, (u32 *)(regs)); \ | |
| } | |
| #define cpuid_leaf(leaf, regs) { \ | |
| static_assert(sizeof(*(regs)) == 16); \ | |
| __cpuid_read(leaf, 0, (u32 *)(regs)); \ | |
| } | |
| static inline void __cpuid_read_reg(unsigned int leaf, unsigned int subleaf, | |
| enum cpuid_regs_idx regidx, u32 *reg) | |
| { | |
| u32 regs[4]; | |
| __cpuid_read(leaf, subleaf, regs); | |
| *reg = regs[regidx]; | |
| } | |
| #define cpuid_subleaf_reg(leaf, subleaf, regidx, reg) { \ | |
| static_assert(sizeof(*(reg)) == 4); \ | |
| __cpuid_read_reg(leaf, subleaf, regidx, (u32 *)(reg)); \ | |
| } | |
| #define cpuid_leaf_reg(leaf, regidx, reg) { \ | |
| static_assert(sizeof(*(reg)) == 4); \ | |
| __cpuid_read_reg(leaf, 0, regidx, (u32 *)(reg)); \ | |
| } | |
| static __always_inline bool cpuid_function_is_indexed(u32 function) | |
| { | |
| switch (function) { | |
| case 4: | |
| case 7: | |
| case 0xb: | |
| case 0xd: | |
| case 0xf: | |
| case 0x10: | |
| case 0x12: | |
| case 0x14: | |
| case 0x17: | |
| case 0x18: | |
| case 0x1d: | |
| case 0x1e: | |
| case 0x1f: | |
| case 0x24: | |
| case 0x8000001d: | |
| return true; | |
| } | |
| return false; | |
| } | |
| #define for_each_possible_hypervisor_cpuid_base(function) \ | |
| for (function = 0x40000000; function < 0x40010000; function += 0x100) | |
| static inline uint32_t hypervisor_cpuid_base(const char *sig, uint32_t leaves) | |
| { | |
| uint32_t base, eax, signature[3]; | |
| for_each_possible_hypervisor_cpuid_base(base) { | |
| cpuid(base, &eax, &signature[0], &signature[1], &signature[2]); | |
| /* | |
| * This must not compile to "call memcmp" because it's called | |
| * from PVH early boot code before instrumentation is set up | |
| * and memcmp() itself may be instrumented. | |
| */ | |
| if (!__builtin_memcmp(sig, signature, 12) && | |
| (leaves == 0 || ((eax - base) >= leaves))) | |
| return base; | |
| } | |
| return 0; | |
| } | |
| #endif /* _ASM_X86_CPUID_H */ |