blob: e3beb77c0c341c7c9568fbf43784f1646015df50 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_ARCH_X86_INCLUDE_ARCH_X86_FEATURE_H_
#define ZIRCON_KERNEL_ARCH_X86_INCLUDE_ARCH_X86_FEATURE_H_
#include <assert.h>
#include <stdint.h>
#include <zircon/compiler.h>
#include <arch/x86.h>
#include <arch/x86/idle_states.h>
#ifdef __cplusplus
namespace cpu_id {
class CpuId;
} // namespace cpu_id
class MsrAccess;
#endif // __cplusplus
__BEGIN_CDECLS
#define MAX_SUPPORTED_CPUID (0x17)
#define MAX_SUPPORTED_CPUID_HYP (0x40000001)
#define MAX_SUPPORTED_CPUID_EXT (0x8000001e)
struct cpuid_leaf {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
};
enum x86_cpuid_leaf_num {
X86_CPUID_BASE = 0,
X86_CPUID_MODEL_FEATURES = 0x1,
X86_CPUID_CACHE_V1 = 0x2,
X86_CPUID_CACHE_V2 = 0x4,
X86_CPUID_MON = 0x5,
X86_CPUID_THERMAL_AND_POWER = 0x6,
X86_CPUID_EXTENDED_FEATURE_FLAGS = 0x7,
X86_CPUID_PERFORMANCE_MONITORING = 0xa,
X86_CPUID_TOPOLOGY = 0xb,
X86_CPUID_XSAVE = 0xd,
X86_CPUID_PT = 0x14,
X86_CPUID_TSC = 0x15,
X86_CPUID_HYP_BASE = 0x40000000,
X86_CPUID_HYP_VENDOR = 0x40000000,
X86_CPUID_KVM_FEATURES = 0x40000001,
X86_CPUID_EXT_BASE = 0x80000000,
X86_CPUID_BRAND = 0x80000002,
X86_CPUID_ADDR_WIDTH = 0x80000008,
X86_CPUID_AMD_TOPOLOGY = 0x8000001e,
};
struct x86_cpuid_bit {
enum x86_cpuid_leaf_num leaf_num;
uint8_t word;
uint8_t bit;
};
#define X86_CPUID_BIT(leaf, word, bit) \
(struct x86_cpuid_bit) { (enum x86_cpuid_leaf_num)(leaf), (word), (bit) }
void x86_feature_init(void);
static inline const struct cpuid_leaf* x86_get_cpuid_leaf(enum x86_cpuid_leaf_num leaf) {
extern struct cpuid_leaf _cpuid[MAX_SUPPORTED_CPUID + 1];
extern struct cpuid_leaf _cpuid_hyp[MAX_SUPPORTED_CPUID_HYP - X86_CPUID_HYP_BASE + 1];
extern struct cpuid_leaf _cpuid_ext[MAX_SUPPORTED_CPUID_EXT - X86_CPUID_EXT_BASE + 1];
extern uint32_t max_cpuid;
extern uint32_t max_ext_cpuid;
extern uint32_t max_hyp_cpuid;
if (leaf < X86_CPUID_HYP_BASE) {
if (unlikely(leaf > max_cpuid))
return NULL;
return &_cpuid[leaf];
} else if (leaf < X86_CPUID_EXT_BASE) {
if (unlikely(leaf > max_hyp_cpuid))
return NULL;
return &_cpuid_hyp[(uint32_t)leaf - (uint32_t)X86_CPUID_HYP_BASE];
} else {
if (unlikely(leaf > max_ext_cpuid))
return NULL;
return &_cpuid_ext[(uint32_t)leaf - (uint32_t)X86_CPUID_EXT_BASE];
}
}
/* Retrieve the specified subleaf. This function is not cached.
* Returns false if leaf num is invalid */
bool x86_get_cpuid_subleaf(enum x86_cpuid_leaf_num, uint32_t, struct cpuid_leaf*);
static inline bool x86_feature_test(struct x86_cpuid_bit bit) {
DEBUG_ASSERT(bit.word <= 3 && bit.bit <= 31);
if (bit.word > 3 || bit.bit > 31)
return false;
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(bit.leaf_num);
if (!leaf)
return false;
switch (bit.word) {
case 0:
return !!((1u << bit.bit) & leaf->a);
case 1:
return !!((1u << bit.bit) & leaf->b);
case 2:
return !!((1u << bit.bit) & leaf->c);
case 3:
return !!((1u << bit.bit) & leaf->d);
default:
return false;
}
}
void x86_feature_debug(void);
/* add feature bits to test here */
/* format: X86_CPUID_BIT(cpuid leaf, register (eax-edx:0-3), bit) */
#define X86_FEATURE_SSE3 X86_CPUID_BIT(0x1, 2, 0)
#define X86_FEATURE_MON X86_CPUID_BIT(0x1, 2, 3)
#define X86_FEATURE_VMX X86_CPUID_BIT(0x1, 2, 5)
#define X86_FEATURE_TM2 X86_CPUID_BIT(0x1, 2, 8)
#define X86_FEATURE_SSSE3 X86_CPUID_BIT(0x1, 2, 9)
#define X86_FEATURE_PDCM X86_CPUID_BIT(0x1, 2, 15)
#define X86_FEATURE_PCID X86_CPUID_BIT(0x1, 2, 17)
#define X86_FEATURE_SSE4_1 X86_CPUID_BIT(0x1, 2, 19)
#define X86_FEATURE_SSE4_2 X86_CPUID_BIT(0x1, 2, 20)
#define X86_FEATURE_X2APIC X86_CPUID_BIT(0x1, 2, 21)
#define X86_FEATURE_TSC_DEADLINE X86_CPUID_BIT(0x1, 2, 24)
#define X86_FEATURE_AESNI X86_CPUID_BIT(0x1, 2, 25)
#define X86_FEATURE_XSAVE X86_CPUID_BIT(0x1, 2, 26)
#define X86_FEATURE_AVX X86_CPUID_BIT(0x1, 2, 28)
#define X86_FEATURE_RDRAND X86_CPUID_BIT(0x1, 2, 30)
#define X86_FEATURE_HYPERVISOR X86_CPUID_BIT(0x1, 2, 31)
#define X86_FEATURE_FPU X86_CPUID_BIT(0x1, 3, 0)
#define X86_FEATURE_SEP X86_CPUID_BIT(0x1, 3, 11)
#define X86_FEATURE_CLFLUSH X86_CPUID_BIT(0x1, 3, 19)
#define X86_FEATURE_ACPI X86_CPUID_BIT(0x1, 3, 22)
#define X86_FEATURE_MMX X86_CPUID_BIT(0x1, 3, 23)
#define X86_FEATURE_FXSR X86_CPUID_BIT(0x1, 3, 24)
#define X86_FEATURE_SSE X86_CPUID_BIT(0x1, 3, 25)
#define X86_FEATURE_SSE2 X86_CPUID_BIT(0x1, 3, 26)
#define X86_FEATURE_TM X86_CPUID_BIT(0x1, 3, 29)
#define X86_FEATURE_DTS X86_CPUID_BIT(0x6, 0, 0)
#define X86_FEATURE_PLN X86_CPUID_BIT(0x6, 0, 4)
#define X86_FEATURE_PTM X86_CPUID_BIT(0x6, 0, 6)
#define X86_FEATURE_HWP X86_CPUID_BIT(0x6, 0, 7)
#define X86_FEATURE_HWP_NOT X86_CPUID_BIT(0x6, 0, 8)
#define X86_FEATURE_HWP_ACT X86_CPUID_BIT(0x6, 0, 9)
#define X86_FEATURE_HWP_PREF X86_CPUID_BIT(0x6, 0, 10)
#define X86_FEATURE_HW_FEEDBACK X86_CPUID_BIT(0x6, 2, 0)
#define X86_FEATURE_PERF_BIAS X86_CPUID_BIT(0x6, 2, 3)
#define X86_FEATURE_FSGSBASE X86_CPUID_BIT(0x7, 1, 0)
#define X86_FEATURE_TSC_ADJUST X86_CPUID_BIT(0x7, 1, 1)
#define X86_FEATURE_AVX2 X86_CPUID_BIT(0x7, 1, 5)
#define X86_FEATURE_SMEP X86_CPUID_BIT(0x7, 1, 7)
#define X86_FEATURE_ERMS X86_CPUID_BIT(0x7, 1, 9)
#define X86_FEATURE_INVPCID X86_CPUID_BIT(0x7, 1, 10)
#define X86_FEATURE_RDSEED X86_CPUID_BIT(0x7, 1, 18)
#define X86_FEATURE_SMAP X86_CPUID_BIT(0x7, 1, 20)
#define X86_FEATURE_CLFLUSHOPT X86_CPUID_BIT(0x7, 1, 23)
#define X86_FEATURE_CLWB X86_CPUID_BIT(0x7, 1, 24)
#define X86_FEATURE_PT X86_CPUID_BIT(0x7, 1, 25)
#define X86_FEATURE_UMIP X86_CPUID_BIT(0x7, 2, 2)
#define X86_FEATURE_PKU X86_CPUID_BIT(0x7, 2, 3)
#define X86_FEATURE_MD_CLEAR X86_CPUID_BIT(0x7, 3, 10)
#define X86_FEATURE_IBRS_IBPB X86_CPUID_BIT(0x7, 3, 26)
#define X86_FEATURE_STIBP X86_CPUID_BIT(0x7, 3, 27)
#define X86_FEATURE_L1D_FLUSH X86_CPUID_BIT(0x7, 3, 28)
#define X86_FEATURE_ARCH_CAPABILITIES X86_CPUID_BIT(0x7, 3, 29)
#define X86_FEATURE_SSBD X86_CPUID_BIT(0x7, 3, 31)
#define X86_FEATURE_KVM_PVCLOCK_STABLE X86_CPUID_BIT(0x40000001, 0, 24)
#define X86_FEATURE_AMD_TOPO X86_CPUID_BIT(0x80000001, 2, 22)
#define X86_FEATURE_SYSCALL X86_CPUID_BIT(0x80000001, 3, 11)
#define X86_FEATURE_NX X86_CPUID_BIT(0x80000001, 3, 20)
#define X86_FEATURE_HUGE_PAGE X86_CPUID_BIT(0x80000001, 3, 26)
#define X86_FEATURE_RDTSCP X86_CPUID_BIT(0x80000001, 3, 27)
#define X86_FEATURE_INVAR_TSC X86_CPUID_BIT(0x80000007, 3, 8)
/* legacy accessors */
static inline uint8_t x86_linear_address_width(void) {
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_ADDR_WIDTH);
if (!leaf)
return 0;
/*
Extracting bit 15:8 from eax register
Bits 15-08: #Linear Address Bits
*/
return (leaf->a >> 8) & 0xff;
}
static inline uint8_t x86_physical_address_width(void) {
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_ADDR_WIDTH);
if (!leaf)
return 0;
/*
Extracting bit 7:0 from eax register
Bits 07-00: #Physical Address Bits
*/
return leaf->a & 0xff;
}
static inline uint32_t x86_get_clflush_line_size(void) {
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_MODEL_FEATURES);
if (!leaf)
return 0;
/*
Extracting bit 15:8 from ebx register
Bits 15-08: #CLFLUSH line size in quadwords
*/
return ((leaf->b >> 8) & 0xff) * 8u;
}
/* cpu vendors */
enum x86_vendor_list { X86_VENDOR_UNKNOWN, X86_VENDOR_INTEL, X86_VENDOR_AMD };
extern enum x86_vendor_list x86_vendor;
/* topology */
#define X86_TOPOLOGY_INVALID 0
#define X86_TOPOLOGY_SMT 1
#define X86_TOPOLOGY_CORE 2
struct x86_topology_level {
/* The number of bits to right shift to identify the next-higher topological
* level */
uint8_t right_shift;
/* The type of relationship this level describes (hyperthread/core/etc) */
uint8_t type;
};
/**
* @brief Fetch the topology information for the given level.
*
* This interface is uncached.
*
* @param level The level to retrieve info for. Should initially be 0 and
* incremented with each call.
* @param info The structure to populate with the discovered information
*
* @return true if the requested level existed (and there may be higher levels).
* @return false if the requested level does not exist (and no higher ones do).
*/
bool x86_topology_enumerate(uint8_t level, struct x86_topology_level* info);
struct x86_model_info {
uint8_t processor_type;
uint8_t family;
uint8_t model;
uint8_t stepping;
uint32_t display_family;
uint32_t display_model;
uint32_t patch_level;
};
const struct x86_model_info* x86_get_model(void);
enum x86_microarch_list {
X86_MICROARCH_UNKNOWN,
X86_MICROARCH_INTEL_NEHALEM,
X86_MICROARCH_INTEL_WESTMERE,
X86_MICROARCH_INTEL_SANDY_BRIDGE,
X86_MICROARCH_INTEL_IVY_BRIDGE,
X86_MICROARCH_INTEL_BROADWELL,
X86_MICROARCH_INTEL_HASWELL,
X86_MICROARCH_INTEL_SKYLAKE,
X86_MICROARCH_INTEL_KABYLAKE,
X86_MICROARCH_INTEL_SILVERMONT, // Silvermont, Airmont
X86_MICROARCH_INTEL_GOLDMONT, // Goldmont, Goldmont+
X86_MICROARCH_AMD_BULLDOZER,
X86_MICROARCH_AMD_JAGUAR,
X86_MICROARCH_AMD_ZEN,
};
extern bool g_x86_feature_fsgsbase;
extern bool g_x86_feature_pcid_good;
extern bool g_x86_feature_has_smap;
enum x86_hypervisor_list {
X86_HYPERVISOR_UNKNOWN,
X86_HYPERVISOR_NONE,
X86_HYPERVISOR_KVM,
};
extern enum x86_hypervisor_list x86_hypervisor;
/* returns 0 if unknown, otherwise value in Hz */
typedef uint64_t (*x86_get_timer_freq_func_t)(void);
/* attempt to reboot the system; may fail and simply return */
typedef void (*x86_reboot_system_func_t)(void);
/* attempt to set a reason flag and reboot the system; may fail and simply return */
typedef void (*x86_reboot_reason_func_t)(uint64_t reason);
/* Structure for supporting per-microarchitecture kernel configuration */
typedef struct {
enum x86_microarch_list x86_microarch;
x86_get_timer_freq_func_t get_apic_freq;
x86_get_timer_freq_func_t get_tsc_freq;
x86_reboot_system_func_t reboot_system;
x86_reboot_reason_func_t reboot_reason;
bool disable_c1e;
// Speculative execution information leak vulnerabilities
// True iff a microarchitecture is known to have a particular vulnerability. May
// be overriden by a more specific enumeration mechanism (ex: IA32_ARCH_CAPABILITIES)
bool has_meltdown;
bool has_l1tf;
bool has_mds;
bool has_swapgs_bug;
bool has_ssb;
x86_idle_states_t idle_states;
} x86_microarch_config_t;
static inline const x86_microarch_config_t* x86_get_microarch_config(void) {
extern const x86_microarch_config_t* x86_microarch_config;
return x86_microarch_config;
}
static inline bool x86_get_disable_spec_mitigations(void) {
extern bool g_disable_spec_mitigations;
return g_disable_spec_mitigations;
}
static inline bool x86_cpu_should_ras_fill_on_ctxt_switch(void) {
extern bool g_ras_fill_on_ctxt_switch;
return g_ras_fill_on_ctxt_switch;
}
static inline bool x86_cpu_should_ibpb_on_ctxt_switch(void) {
extern bool g_should_ibpb_on_ctxt_switch;
return g_should_ibpb_on_ctxt_switch;
}
static inline bool x86_cpu_should_mitigate_ssb(void) {
extern bool g_ssb_mitigated;
return g_ssb_mitigated;
}
static inline bool x86_cpu_should_l1d_flush_on_vmentry(void) {
extern bool g_l1d_flush_on_vmentry;
return g_l1d_flush_on_vmentry;
}
static inline bool x86_cpu_should_md_clear_on_user_return(void) {
extern bool g_md_clear_on_user_return;
return g_md_clear_on_user_return;
}
// Vendor-specific per-cpu init functions, in amd.cpp/intel.cpp
void x86_amd_init_percpu(void);
void x86_intel_init_percpu(void);
#ifdef __cplusplus
bool x86_intel_cpu_has_meltdown(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_intel_cpu_has_l1tf(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_intel_cpu_has_mds_taa(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_intel_cpu_has_swapgs_bug(const cpu_id::CpuId* cpuid);
bool x86_intel_cpu_has_ssb(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_amd_cpu_has_ssb(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_intel_cpu_has_ssbd(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_amd_cpu_has_ssbd(const cpu_id::CpuId* cpuid, MsrAccess* msr);
void x86_intel_cpu_set_ssbd(const cpu_id::CpuId* cpuid, MsrAccess* msr);
void x86_amd_cpu_set_ssbd(const cpu_id::CpuId* cpuid, MsrAccess* msr);
void x86_cpu_ibpb(MsrAccess* msr);
bool x86_intel_cpu_has_enhanced_ibrs(const cpu_id::CpuId* cpuid, MsrAccess* msr);
bool x86_amd_cpu_has_ibrs_always_on(const cpu_id::CpuId* cpuid);
#endif
uint32_t x86_amd_get_patch_level(void);
uint32_t x86_intel_get_patch_level(void);
__END_CDECLS
#ifdef __cplusplus
const x86_microarch_config_t* get_microarch_config(const cpu_id::CpuId* cpuid);
bool x86_intel_check_microcode_patch(cpu_id::CpuId* cpuid, MsrAccess* msr, struct iovec patch);
void x86_intel_load_microcode_patch(cpu_id::CpuId* cpuid, MsrAccess* msr, struct iovec patch);
#endif // __cplusplus
#endif // ZIRCON_KERNEL_ARCH_X86_INCLUDE_ARCH_X86_FEATURE_H_