x86 queue, 2021-06-01

Features:
 * Add CPU model versions supporting 'xsaves' (Vitaly Kuznetsov)
 * Support AVX512 ZMM regs dump (Robert Hoo)
 
 Bug fixes:
 * Use better matching family/model/stepping for generic CPUs
   (Daniel P. Berrangé)
 
 Cleanups:
 * Hyper-V feature initialization cleanup (Vitaly Kuznetsov)
 * SEV firmware error list touchups (Connor Kuehl)
 * Constify CPUCaches and X86CPUDefinition (Philippe Mathieu-Daudé)
 * Document when features can be added to kvm_default_props
   (Eduardo Habkost)
 -----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEEWjIv1avE09usz9GqKAeTb5hNxaYFAmC2d6EUHGVoYWJrb3N0
 QHJlZGhhdC5jb20ACgkQKAeTb5hNxabEQxAArySfPv0mPEX6ibkNbO/mtyCDGPyZ
 n7UzcYaC5WgYhJFdGywIM1e6FBusguyCPihtFak64XUSsFlpYy6gS42kZSvtNqxT
 Mak360gKTSvxhZ8gjWYfFXhbptNBNll1rdBOwiPlcVzKAfAO7xaS6pcT8RyuMcIl
 Y5JQakhPrFicXur7iJZ4DjHkGk4JT7vy0pEvSoWdh0O5q20fBX3Gjzm4xAhmFAZw
 Qj86ZFbvEIjcNfUtKNpm4GxgAnmBTn4Ys99ba0zjWuWfVOwQqnRPXW5UV5xZSJmW
 tHtroCMQvqT8UGqK00fSnRSP+bnJo1XU5xEJ7wT6ajdQ4Km/N+k7hoXyZA4BkH+a
 pnu1Hr7b821qYwj7GEEZkGZ6dEQlIYOkV+dJHtppP2t6xp4qmI8+d13enqpFULVn
 IG/ZAIhqRItROU/YgGnUZashqM7BcvU3ejhe2uxUJv4URd/EPj3ynzQqkbFGXOQx
 vAVhuTr5EW8WN2nB4JpAIpsDxxO4cyU6ZQ7oecB12/IVJfN/wt5FtIyVXdhccKRz
 RSeugoXmObzqxSrtKIDTAukZNdMbaaRaRwQeqcyNKdcnKIYbDC4kzCh7Ai9vgOYT
 MtP+rdD2wV5g7aNMn5ZveKR80TRA7j7aBJ3ntmRKu+AhuLGtyu71XiD7aeESAvSu
 N/Z32DtfMxSNu/0=
 =NrT0
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ehabkost-gl/tags/x86-next-pull-request' into staging

x86 queue, 2021-06-01

Features:
* Add CPU model versions supporting 'xsaves' (Vitaly Kuznetsov)
* Support AVX512 ZMM regs dump (Robert Hoo)

Bug fixes:
* Use better matching family/model/stepping for generic CPUs
  (Daniel P. Berrangé)

Cleanups:
* Hyper-V feature initialization cleanup (Vitaly Kuznetsov)
* SEV firmware error list touchups (Connor Kuehl)
* Constify CPUCaches and X86CPUDefinition (Philippe Mathieu-Daudé)
* Document when features can be added to kvm_default_props
  (Eduardo Habkost)

# gpg: Signature made Tue 01 Jun 2021 19:08:33 BST
# gpg:                using RSA key 5A322FD5ABC4D3DBACCFD1AA2807936F984DC5A6
# gpg:                issuer "ehabkost@redhat.com"
# gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>" [full]
# Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF  D1AA 2807 936F 984D C5A6

* remotes/ehabkost-gl/tags/x86-next-pull-request: (24 commits)
  sev: add missing firmware error conditions
  sev: use explicit indices for mapping firmware error codes to strings
  target/i386/sev: add support to query the attestation report
  i386: use global kvm_state in hyperv_enabled() check
  i386: prefer system KVM_GET_SUPPORTED_HV_CPUID ioctl over vCPU's one
  i386: adjust the expected KVM_GET_SUPPORTED_HV_CPUID array size
  i386: switch hyperv_expand_features() to using error_setg()
  i386: move eVMCS enablement to hyperv_init_vcpu()
  i386: split hyperv_handle_properties() into hyperv_expand_features()/hyperv_fill_cpuids()
  i386: introduce hv_cpuid_cache
  i386: drop FEAT_HYPERV feature leaves
  i386: introduce hv_cpuid_get_host()
  i386: introduce hyperv_feature_supported()
  i386: stop using env->features[] for filling Hyper-V CPUIDs
  i386: always fill Hyper-V CPUID feature leaves from X86CPU data
  i386: invert hyperv_spinlock_attempts setting logic with hv_passthrough
  i386: keep hyperv_vendor string up-to-date
  i386: use better matching family/model/stepping for 'max' CPU
  i386: use better matching family/model/stepping for 'qemu64' CPU
  i386/cpu_dump: support AVX512 ZMM regs dump
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-06-01 21:23:26 +01:00
commit dd2db39d78
14 changed files with 617 additions and 448 deletions

View File

@ -94,7 +94,11 @@
#include "trace.h"
#include CONFIG_DEVICES
GlobalProperty pc_compat_6_0[] = {};
GlobalProperty pc_compat_6_0[] = {
{ "qemu64" "-" TYPE_X86_CPU, "family", "6" },
{ "qemu64" "-" TYPE_X86_CPU, "model", "6" },
{ "qemu64" "-" TYPE_X86_CPU, "stepping", "3" },
};
const size_t pc_compat_6_0_len = G_N_ELEMENTS(pc_compat_6_0);
GlobalProperty pc_compat_5_2[] = {

View File

@ -1591,6 +1591,8 @@ enum sev_cmd_id {
KVM_SEV_DBG_ENCRYPT,
/* Guest certificates commands */
KVM_SEV_CERT_EXPORT,
/* Attestation report */
KVM_SEV_GET_ATTESTATION_REPORT,
KVM_SEV_NR_MAX,
};
@ -1643,6 +1645,12 @@ struct kvm_sev_dbg {
__u32 len;
};
struct kvm_sev_attestation_report {
__u8 mnonce[16];
__u64 uaddr;
__u32 len;
};
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)

View File

@ -285,3 +285,41 @@
##
{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'],
'if': 'defined(TARGET_ARM)' }
##
# @SevAttestationReport:
#
# The struct describes attestation report for a Secure Encrypted Virtualization
# feature.
#
# @data: guest attestation report (base64 encoded)
#
#
# Since: 6.1
##
{ 'struct': 'SevAttestationReport',
'data': { 'data': 'str'},
'if': 'defined(TARGET_I386)' }
##
# @query-sev-attestation-report:
#
# This command is used to get the SEV attestation report, and is supported on AMD
# X86 platforms only.
#
# @mnonce: a random 16 bytes value encoded in base64 (it will be included in report)
#
# Returns: SevAttestationReport objects.
#
# Since: 6.1
#
# Example:
#
# -> { "execute" : "query-sev-attestation-report", "arguments": { "mnonce": "aaaaaaa" } }
# <- { "return" : { "data": "aaaaaaaabbbddddd"} }
#
##
{ 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' },
'returns': 'SevAttestationReport',
'if': 'defined(TARGET_I386)' }

View File

@ -478,6 +478,11 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
qemu_fprintf(f, "EFER=%016" PRIx64 "\n", env->efer);
if (flags & CPU_DUMP_FPU) {
int fptag;
const uint64_t avx512_mask = XSTATE_OPMASK_MASK | \
XSTATE_ZMM_Hi256_MASK | \
XSTATE_Hi16_ZMM_MASK | \
XSTATE_YMM_MASK | XSTATE_SSE_MASK,
avx_mask = XSTATE_YMM_MASK | XSTATE_SSE_MASK;
fptag = 0;
for(i = 0; i < 8; i++) {
fptag |= ((!env->fptags[i]) << i);
@ -499,21 +504,49 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
else
qemu_fprintf(f, " ");
}
if (env->hflags & HF_CS64_MASK)
nb = 16;
else
nb = 8;
for(i=0;i<nb;i++) {
qemu_fprintf(f, "XMM%02d=%08x%08x%08x%08x",
i,
env->xmm_regs[i].ZMM_L(3),
env->xmm_regs[i].ZMM_L(2),
env->xmm_regs[i].ZMM_L(1),
env->xmm_regs[i].ZMM_L(0));
if ((i & 1) == 1)
qemu_fprintf(f, "\n");
else
qemu_fprintf(f, " ");
if ((env->xcr0 & avx512_mask) == avx512_mask) {
/* XSAVE enabled AVX512 */
for (i = 0; i < NB_OPMASK_REGS; i++) {
qemu_fprintf(f, "Opmask%02d=%016"PRIx64"%s", i,
env->opmask_regs[i], ((i & 3) == 3) ? "\n" : " ");
}
nb = (env->hflags & HF_CS64_MASK) ? 32 : 8;
for (i = 0; i < nb; i++) {
qemu_fprintf(f, "ZMM%02d=%016"PRIx64" %016"PRIx64" %016"PRIx64
" %016"PRIx64" %016"PRIx64" %016"PRIx64
" %016"PRIx64" %016"PRIx64"\n",
i,
env->xmm_regs[i].ZMM_Q(7),
env->xmm_regs[i].ZMM_Q(6),
env->xmm_regs[i].ZMM_Q(5),
env->xmm_regs[i].ZMM_Q(4),
env->xmm_regs[i].ZMM_Q(3),
env->xmm_regs[i].ZMM_Q(2),
env->xmm_regs[i].ZMM_Q(1),
env->xmm_regs[i].ZMM_Q(0));
}
} else if ((env->xcr0 & avx_mask) == avx_mask) {
/* XSAVE enabled AVX */
nb = env->hflags & HF_CS64_MASK ? 16 : 8;
for (i = 0; i < nb; i++) {
qemu_fprintf(f, "YMM%02d=%016"PRIx64" %016"PRIx64" %016"PRIx64
" %016"PRIx64"\n", i,
env->xmm_regs[i].ZMM_Q(3),
env->xmm_regs[i].ZMM_Q(2),
env->xmm_regs[i].ZMM_Q(1),
env->xmm_regs[i].ZMM_Q(0));
}
} else { /* SSE and below cases */
nb = env->hflags & HF_CS64_MASK ? 16 : 8;
for (i = 0; i < nb; i++) {
qemu_fprintf(f, "XMM%02d=%016"PRIx64" %016"PRIx64"%s",
i,
env->xmm_regs[i].ZMM_Q(1),
env->xmm_regs[i].ZMM_Q(0),
(i & 1) ? "\n" : " ");
}
}
}
if (flags & CPU_DUMP_CODE) {

View File

@ -312,7 +312,7 @@ GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs)
CPUX86State *env = &cpu->env;
GuestPanicInformation *panic_info = NULL;
if (env->features[FEAT_HYPERV_EDX] & HV_GUEST_CRASH_MSR_AVAILABLE) {
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_CRASH)) {
panic_info = g_malloc0(sizeof(GuestPanicInformation));
panic_info->type = GUEST_PANIC_INFORMATION_TYPE_HYPER_V;

View File

@ -776,94 +776,6 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
*/
.no_autoenable_flags = ~0U,
},
/*
* .feat_names are commented out for Hyper-V enlightenments because we
* don't want to have two different ways for enabling them on QEMU command
* line. Some features (e.g. "hyperv_time", "hyperv_vapic", ...) require
* enabling several feature bits simultaneously, exposing these bits
* individually may just confuse guests.
*/
[FEAT_HYPERV_EAX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */,
NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */,
NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */,
NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */,
NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */,
NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */,
NULL /* hv_msr_debug_access */, NULL /* hv_msr_reenlightenment_access */,
NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid = { .eax = 0x40000003, .reg = R_EAX, },
},
[FEAT_HYPERV_EBX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */,
NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */,
NULL /* hv_post_messages */, NULL /* hv_signal_events */,
NULL /* hv_create_port */, NULL /* hv_connect_port */,
NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */,
NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */,
NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid = { .eax = 0x40000003, .reg = R_EBX, },
},
[FEAT_HYPERV_EDX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
NULL /* hv_mwait */, NULL /* hv_guest_debugging */,
NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */,
NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */,
NULL, NULL,
NULL, NULL, NULL /* hv_guest_crash_msr */, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid = { .eax = 0x40000003, .reg = R_EDX, },
},
[FEAT_HV_RECOMM_EAX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
NULL /* hv_recommend_pv_as_switch */,
NULL /* hv_recommend_pv_tlbflush_local */,
NULL /* hv_recommend_pv_tlbflush_remote */,
NULL /* hv_recommend_msr_apic_access */,
NULL /* hv_recommend_msr_reset */,
NULL /* hv_recommend_relaxed_timing */,
NULL /* hv_recommend_dma_remapping */,
NULL /* hv_recommend_int_remapping */,
NULL /* hv_recommend_x2apic_msrs */,
NULL /* hv_recommend_autoeoi_deprecation */,
NULL /* hv_recommend_pv_ipi */,
NULL /* hv_recommend_ex_hypercalls */,
NULL /* hv_hypervisor_is_nested */,
NULL /* hv_recommend_int_mbec */,
NULL /* hv_recommend_evmcs */,
NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid = { .eax = 0x40000004, .reg = R_EAX, },
},
[FEAT_HV_NESTED_EAX] = {
.type = CPUID_FEATURE_WORD,
.cpuid = { .eax = 0x4000000A, .reg = R_EAX, },
},
[FEAT_SVM] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
@ -1576,7 +1488,7 @@ typedef struct X86CPUDefinition {
int stepping;
FeatureWordArray features;
const char *model_id;
CPUCaches *cache_info;
const CPUCaches *const cache_info;
/*
* Definitions for alternative versions of CPU model.
* List is terminated by item with version == 0.
@ -1589,7 +1501,7 @@ typedef struct X86CPUDefinition {
/* Reference to a specific CPU model version */
struct X86CPUModel {
/* Base CPU definition */
X86CPUDefinition *cpudef;
const X86CPUDefinition *cpudef;
/* CPU model version */
X86CPUVersion version;
const char *note;
@ -1601,14 +1513,15 @@ struct X86CPUModel {
};
/* Get full model name for CPU version */
static char *x86_cpu_versioned_model_name(X86CPUDefinition *cpudef,
static char *x86_cpu_versioned_model_name(const X86CPUDefinition *cpudef,
X86CPUVersion version)
{
assert(version > 0);
return g_strdup_printf("%s-v%d", cpudef->name, (int)version);
}
static const X86CPUVersionDefinition *x86_cpu_def_get_versions(X86CPUDefinition *def)
static const X86CPUVersionDefinition *
x86_cpu_def_get_versions(const X86CPUDefinition *def)
{
/* When X86CPUDefinition::versions is NULL, we register only v1 */
static const X86CPUVersionDefinition default_version_list[] = {
@ -1619,7 +1532,7 @@ static const X86CPUVersionDefinition *x86_cpu_def_get_versions(X86CPUDefinition
return def->versions ?: default_version_list;
}
static CPUCaches epyc_cache_info = {
static const CPUCaches epyc_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
@ -1669,7 +1582,7 @@ static CPUCaches epyc_cache_info = {
},
};
static CPUCaches epyc_rome_cache_info = {
static const CPUCaches epyc_rome_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
@ -1719,7 +1632,7 @@ static CPUCaches epyc_rome_cache_info = {
},
};
static CPUCaches epyc_milan_cache_info = {
static const CPUCaches epyc_milan_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
@ -1797,14 +1710,14 @@ static CPUCaches epyc_milan_cache_info = {
* PT in VMX operation
*/
static X86CPUDefinition builtin_x86_defs[] = {
static const X86CPUDefinition builtin_x86_defs[] = {
{
.name = "qemu64",
.level = 0xd,
.vendor = CPUID_VENDOR_AMD,
.family = 6,
.model = 6,
.stepping = 3,
.family = 15,
.model = 107,
.stepping = 1,
.features[FEAT_1_EDX] =
PPRO_FEATURES |
CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
@ -2802,12 +2715,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
CPUID_7_0_EBX_SMAP,
/* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 4 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -2883,6 +2791,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
}
},
{
.version = 4,
.note = "IBRS, XSAVES, no TSX",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
}
},
{ /* end of list */ }
}
},
@ -2922,12 +2839,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT,
.features[FEAT_7_0_ECX] =
CPUID_7_0_ECX_PKU,
/* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 5 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3015,6 +2927,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
}
},
{
.version = 5,
.note = "IBRS, XSAVES, EPT switching, no TSX",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
}
},
{ /* end of list */ }
}
},
@ -3057,12 +2978,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_ECX_AVX512VNNI,
.features[FEAT_7_0_EDX] =
CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD,
/* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 5 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3146,6 +3062,14 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
},
},
{ .version = 5,
.note = "ARCH_CAPABILITIES, EPT switching, XSAVES, no TSX",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
},
},
{ /* end of list */ }
}
},
@ -3195,13 +3119,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_TAA_NO,
.features[FEAT_7_1_EAX] =
CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16,
/*
* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 2 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3257,6 +3175,18 @@ static X86CPUDefinition builtin_x86_defs[] = {
.features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING,
.xlevel = 0x80000008,
.model_id = "Intel Xeon Processor (Cooperlake)",
.versions = (X86CPUVersionDefinition[]) {
{ .version = 1 },
{ .version = 2,
.note = "XSAVES",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
},
},
{ /* end of list */ }
}
},
{
.name = "Icelake-Client",
@ -3299,12 +3229,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_ECX_AVX512_VPOPCNTDQ,
.features[FEAT_7_0_EDX] =
CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD,
/* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 3 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3372,6 +3297,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
},
},
{
.version = 3,
.note = "no TSX, XSAVES, deprecated",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
},
},
{ /* end of list */ }
},
.deprecation_note = "use Icelake-Server instead"
@ -3420,12 +3354,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57,
.features[FEAT_7_0_EDX] =
CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD,
/* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 5 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3518,6 +3447,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
},
},
{
.version = 5,
.note = "XSAVES",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ }
},
},
{ /* end of list */ }
}
},
@ -3552,13 +3490,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
.features[FEAT_7_0_EDX] =
CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_ARCH_CAPABILITIES |
CPUID_7_0_EDX_SPEC_CTRL_SSBD,
/*
* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is added in version 3 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | CPUID_XSAVE_XGETBV1,
.features[FEAT_6_EAX] =
@ -3625,6 +3557,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ },
},
},
{
.version = 3,
.note = "XSAVES, no MPX, no MONITOR",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ },
},
},
{ /* end of list */ },
},
},
@ -3683,13 +3624,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_EDX_CORE_CAPABILITY,
.features[FEAT_CORE_CAPABILITY] =
MSR_CORE_CAP_SPLIT_LOCK_DETECT,
/*
* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component,
* and the only one defined in Skylake (processor tracing)
* probably will block migration anyway.
*/
/* XSAVES is is added in version 3 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -3754,6 +3689,15 @@ static X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ },
},
},
{
.version = 3,
.note = "XSAVES, no MPX",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ "vmx-xsaves", "on" },
{ /* end of list */ },
},
},
{ /* end of list */ },
},
},
@ -4035,11 +3979,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 |
CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED |
CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT,
/*
* Missing: XSAVES (not supported by some Linux versions,
* including v4.1 to v4.12).
* KVM doesn't yet expose any XSAVES state save component.
*/
/* XSAVES is added in version 2 */
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1,
@ -4050,6 +3990,17 @@ static X86CPUDefinition builtin_x86_defs[] = {
.xlevel = 0x8000001E,
.model_id = "Hygon Dhyana Processor",
.cache_info = &epyc_cache_info,
.versions = (X86CPUVersionDefinition[]) {
{ .version = 1 },
{ .version = 2,
.note = "XSAVES",
.props = (PropValue[]) {
{ "xsaves", "on" },
{ /* end of list */ }
},
},
{ /* end of list */ }
}
},
{
.name = "EPYC-Rome",
@ -4246,9 +4197,15 @@ static void max_x86_cpu_initfn(Object *obj)
*/
object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD,
&error_abort);
#ifdef TARGET_X86_64
object_property_set_int(OBJECT(cpu), "family", 15, &error_abort);
object_property_set_int(OBJECT(cpu), "model", 107, &error_abort);
object_property_set_int(OBJECT(cpu), "stepping", 1, &error_abort);
#else
object_property_set_int(OBJECT(cpu), "family", 6, &error_abort);
object_property_set_int(OBJECT(cpu), "model", 6, &error_abort);
object_property_set_int(OBJECT(cpu), "stepping", 3, &error_abort);
#endif
object_property_set_str(OBJECT(cpu), "model-id",
"QEMU TCG CPU version " QEMU_HW_VERSION,
&error_abort);
@ -5023,7 +4980,7 @@ static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model)
*/
static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model)
{
X86CPUDefinition *def = model->cpudef;
const X86CPUDefinition *def = model->cpudef;
CPUX86State *env = &cpu->env;
FeatureWord w;
@ -5110,7 +5067,7 @@ static void x86_register_cpu_model_type(const char *name, X86CPUModel *model)
type_register(&ti);
}
static void x86_register_cpudef_types(X86CPUDefinition *def)
static void x86_register_cpudef_types(const X86CPUDefinition *def)
{
X86CPUModel *m;
const X86CPUVersionDefinition *vdef;
@ -6096,17 +6053,16 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu)
/* Hyper-V vendor id */
if (!cpu->hyperv_vendor) {
memcpy(cpu->hyperv_vendor_id, "Microsoft Hv", 12);
} else {
len = strlen(cpu->hyperv_vendor);
if (len > 12) {
warn_report("hv-vendor-id truncated to 12 characters");
len = 12;
}
memset(cpu->hyperv_vendor_id, 0, 12);
memcpy(cpu->hyperv_vendor_id, cpu->hyperv_vendor, len);
object_property_set_str(OBJECT(cpu), "hv-vendor-id", "Microsoft Hv",
&error_abort);
}
len = strlen(cpu->hyperv_vendor);
if (len > 12) {
warn_report("hv-vendor-id truncated to 12 characters");
len = 12;
}
memset(cpu->hyperv_vendor_id, 0, 12);
memcpy(cpu->hyperv_vendor_id, cpu->hyperv_vendor, len);
/* 'Hv#1' interface identification*/
cpu->hyperv_interface_id[0] = 0x31237648;

View File

@ -531,11 +531,6 @@ typedef enum FeatureWord {
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
FEAT_KVM_HINTS, /* CPUID[4000_0001].EDX */
FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */
FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */
FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */
FEAT_HV_RECOMM_EAX, /* CPUID[4000_0004].EAX */
FEAT_HV_NESTED_EAX, /* CPUID[4000_000A].EAX */
FEAT_SVM, /* CPUID[8000_000A].EDX */
FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */
FEAT_6_EAX, /* CPUID[6].EAX */
@ -1699,6 +1694,7 @@ struct X86CPU {
uint32_t hyperv_interface_id[4];
uint32_t hyperv_version_id[4];
uint32_t hyperv_limits[3];
uint32_t hyperv_nested[4];
bool check_cpuid;
bool enforce_cpuid;

View File

@ -47,6 +47,11 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp)
/*
* KVM-specific features that are automatically added/removed
* from all CPU models when KVM is enabled.
*
* NOTE: features can be enabled by default only if they were
* already available in the oldest kernel version supported
* by the KVM accelerator (see "OS requirements" section at
* docs/system/target-i386.rst)
*/
static PropValue kvm_default_props[] = {
{ "kvmclock", "on" },

View File

@ -129,6 +129,7 @@ static int has_exception_payload;
static bool has_msr_mcg_ext_ctl;
static struct kvm_cpuid2 *cpuid_cache;
static struct kvm_cpuid2 *hv_cpuid_cache;
static struct kvm_msr_list *kvm_feature_msrs;
int kvm_has_pit_state2(void)
@ -715,8 +716,7 @@ unsigned long kvm_arch_vcpu_id(CPUState *cs)
static bool hyperv_enabled(X86CPU *cpu)
{
CPUState *cs = CPU(cpu);
return kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV) > 0 &&
return kvm_check_extension(kvm_state, KVM_CAP_HYPERV) > 0 &&
((cpu->hyperv_spinlock_attempts != HYPERV_SPINLOCK_NEVER_NOTIFY) ||
cpu->hyperv_features || cpu->hyperv_passthrough);
}
@ -801,7 +801,8 @@ static bool tsc_is_stable_and_known(CPUX86State *env)
static struct {
const char *desc;
struct {
uint32_t fw;
uint32_t func;
int reg;
uint32_t bits;
} flags[2];
uint64_t dependencies;
@ -809,25 +810,25 @@ static struct {
[HYPERV_FEAT_RELAXED] = {
.desc = "relaxed timing (hv-relaxed)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_HYPERCALL_AVAILABLE},
{.fw = FEAT_HV_RECOMM_EAX,
{.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX,
.bits = HV_RELAXED_TIMING_RECOMMENDED}
}
},
[HYPERV_FEAT_VAPIC] = {
.desc = "virtual APIC (hv-vapic)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_HYPERCALL_AVAILABLE | HV_APIC_ACCESS_AVAILABLE},
{.fw = FEAT_HV_RECOMM_EAX,
{.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX,
.bits = HV_APIC_ACCESS_RECOMMENDED}
}
},
[HYPERV_FEAT_TIME] = {
.desc = "clocksources (hv-time)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_HYPERCALL_AVAILABLE | HV_TIME_REF_COUNT_AVAILABLE |
HV_REFERENCE_TSC_AVAILABLE}
}
@ -835,42 +836,42 @@ static struct {
[HYPERV_FEAT_CRASH] = {
.desc = "crash MSRs (hv-crash)",
.flags = {
{.fw = FEAT_HYPERV_EDX,
{.func = HV_CPUID_FEATURES, .reg = R_EDX,
.bits = HV_GUEST_CRASH_MSR_AVAILABLE}
}
},
[HYPERV_FEAT_RESET] = {
.desc = "reset MSR (hv-reset)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_RESET_AVAILABLE}
}
},
[HYPERV_FEAT_VPINDEX] = {
.desc = "VP_INDEX MSR (hv-vpindex)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_VP_INDEX_AVAILABLE}
}
},
[HYPERV_FEAT_RUNTIME] = {
.desc = "VP_RUNTIME MSR (hv-runtime)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_VP_RUNTIME_AVAILABLE}
}
},
[HYPERV_FEAT_SYNIC] = {
.desc = "synthetic interrupt controller (hv-synic)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_SYNIC_AVAILABLE}
}
},
[HYPERV_FEAT_STIMER] = {
.desc = "synthetic timers (hv-stimer)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_SYNTIMERS_AVAILABLE}
},
.dependencies = BIT(HYPERV_FEAT_SYNIC) | BIT(HYPERV_FEAT_TIME)
@ -878,23 +879,23 @@ static struct {
[HYPERV_FEAT_FREQUENCIES] = {
.desc = "frequency MSRs (hv-frequencies)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_ACCESS_FREQUENCY_MSRS},
{.fw = FEAT_HYPERV_EDX,
{.func = HV_CPUID_FEATURES, .reg = R_EDX,
.bits = HV_FREQUENCY_MSRS_AVAILABLE}
}
},
[HYPERV_FEAT_REENLIGHTENMENT] = {
.desc = "reenlightenment MSRs (hv-reenlightenment)",
.flags = {
{.fw = FEAT_HYPERV_EAX,
{.func = HV_CPUID_FEATURES, .reg = R_EAX,
.bits = HV_ACCESS_REENLIGHTENMENTS_CONTROL}
}
},
[HYPERV_FEAT_TLBFLUSH] = {
.desc = "paravirtualized TLB flush (hv-tlbflush)",
.flags = {
{.fw = FEAT_HV_RECOMM_EAX,
{.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX,
.bits = HV_REMOTE_TLB_FLUSH_RECOMMENDED |
HV_EX_PROCESSOR_MASKS_RECOMMENDED}
},
@ -903,7 +904,7 @@ static struct {
[HYPERV_FEAT_EVMCS] = {
.desc = "enlightened VMCS (hv-evmcs)",
.flags = {
{.fw = FEAT_HV_RECOMM_EAX,
{.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX,
.bits = HV_ENLIGHTENED_VMCS_RECOMMENDED}
},
.dependencies = BIT(HYPERV_FEAT_VAPIC)
@ -911,7 +912,7 @@ static struct {
[HYPERV_FEAT_IPI] = {
.desc = "paravirtualized IPI (hv-ipi)",
.flags = {
{.fw = FEAT_HV_RECOMM_EAX,
{.func = HV_CPUID_ENLIGHTMENT_INFO, .reg = R_EAX,
.bits = HV_CLUSTER_IPI_RECOMMENDED |
HV_EX_PROCESSOR_MASKS_RECOMMENDED}
},
@ -920,14 +921,15 @@ static struct {
[HYPERV_FEAT_STIMER_DIRECT] = {
.desc = "direct mode synthetic timers (hv-stimer-direct)",
.flags = {
{.fw = FEAT_HYPERV_EDX,
{.func = HV_CPUID_FEATURES, .reg = R_EDX,
.bits = HV_STIMER_DIRECT_MODE_AVAILABLE}
},
.dependencies = BIT(HYPERV_FEAT_STIMER)
},
};
static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max)
static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max,
bool do_sys_ioctl)
{
struct kvm_cpuid2 *cpuid;
int r, size;
@ -936,7 +938,11 @@ static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max)
cpuid = g_malloc0(size);
cpuid->nent = max;
r = kvm_vcpu_ioctl(cs, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
if (do_sys_ioctl) {
r = kvm_ioctl(kvm_state, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
} else {
r = kvm_vcpu_ioctl(cs, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
}
if (r == 0 && cpuid->nent >= max) {
r = -E2BIG;
}
@ -960,16 +966,38 @@ static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max)
static struct kvm_cpuid2 *get_supported_hv_cpuid(CPUState *cs)
{
struct kvm_cpuid2 *cpuid;
int max = 7; /* 0x40000000..0x40000005, 0x4000000A */
/* 0x40000000..0x40000005, 0x4000000A, 0x40000080..0x40000080 leaves */
int max = 10;
int i;
bool do_sys_ioctl;
do_sys_ioctl =
kvm_check_extension(kvm_state, KVM_CAP_SYS_HYPERV_CPUID) > 0;
/*
* When the buffer is too small, KVM_GET_SUPPORTED_HV_CPUID fails with
* -E2BIG, however, it doesn't report back the right size. Keep increasing
* it and re-trying until we succeed.
*/
while ((cpuid = try_get_hv_cpuid(cs, max)) == NULL) {
while ((cpuid = try_get_hv_cpuid(cs, max, do_sys_ioctl)) == NULL) {
max++;
}
/*
* KVM_GET_SUPPORTED_HV_CPUID does not set EVMCS CPUID bit before
* KVM_CAP_HYPERV_ENLIGHTENED_VMCS is enabled but we want to get the
* information early, just check for the capability and set the bit
* manually.
*/
if (!do_sys_ioctl && kvm_check_extension(cs->kvm_state,
KVM_CAP_HYPERV_ENLIGHTENED_VMCS) > 0) {
for (i = 0; i < cpuid->nent; i++) {
if (cpuid->entries[i].function == HV_CPUID_ENLIGHTMENT_INFO) {
cpuid->entries[i].eax |= HV_ENLIGHTENED_VMCS_RECOMMENDED;
}
}
}
return cpuid;
}
@ -1066,56 +1094,62 @@ static struct kvm_cpuid2 *get_supported_hv_cpuid_legacy(CPUState *cs)
return cpuid;
}
static int hv_cpuid_get_fw(struct kvm_cpuid2 *cpuid, int fw, uint32_t *r)
static uint32_t hv_cpuid_get_host(CPUState *cs, uint32_t func, int reg)
{
struct kvm_cpuid_entry2 *entry;
uint32_t func;
int reg;
struct kvm_cpuid2 *cpuid;
switch (fw) {
case FEAT_HYPERV_EAX:
reg = R_EAX;
func = HV_CPUID_FEATURES;
break;
case FEAT_HYPERV_EDX:
reg = R_EDX;
func = HV_CPUID_FEATURES;
break;
case FEAT_HV_RECOMM_EAX:
reg = R_EAX;
func = HV_CPUID_ENLIGHTMENT_INFO;
break;
default:
return -EINVAL;
if (hv_cpuid_cache) {
cpuid = hv_cpuid_cache;
} else {
if (kvm_check_extension(kvm_state, KVM_CAP_HYPERV_CPUID) > 0) {
cpuid = get_supported_hv_cpuid(cs);
} else {
cpuid = get_supported_hv_cpuid_legacy(cs);
}
hv_cpuid_cache = cpuid;
}
if (!cpuid) {
return 0;
}
entry = cpuid_find_entry(cpuid, func, 0);
if (!entry) {
return -ENOENT;
return 0;
}
switch (reg) {
case R_EAX:
*r = entry->eax;
break;
case R_EDX:
*r = entry->edx;
break;
default:
return -EINVAL;
}
return 0;
return cpuid_entry_get_reg(entry, reg);
}
static int hv_cpuid_check_and_set(CPUState *cs, struct kvm_cpuid2 *cpuid,
int feature)
static bool hyperv_feature_supported(CPUState *cs, int feature)
{
uint32_t func, bits;
int i, reg;
for (i = 0; i < ARRAY_SIZE(kvm_hyperv_properties[feature].flags); i++) {
func = kvm_hyperv_properties[feature].flags[i].func;
reg = kvm_hyperv_properties[feature].flags[i].reg;
bits = kvm_hyperv_properties[feature].flags[i].bits;
if (!func) {
continue;
}
if ((hv_cpuid_get_host(cs, func, reg) & bits) != bits) {
return false;
}
}
return true;
}
static int hv_cpuid_check_and_set(CPUState *cs, int feature, Error **errp)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
uint32_t r, fw, bits;
uint64_t deps;
int i, dep_feat;
int dep_feat;
if (!hyperv_feat_enabled(cpu, feature) && !cpu->hyperv_passthrough) {
return 0;
@ -1125,35 +1159,22 @@ static int hv_cpuid_check_and_set(CPUState *cs, struct kvm_cpuid2 *cpuid,
while (deps) {
dep_feat = ctz64(deps);
if (!(hyperv_feat_enabled(cpu, dep_feat))) {
fprintf(stderr,
"Hyper-V %s requires Hyper-V %s\n",
kvm_hyperv_properties[feature].desc,
kvm_hyperv_properties[dep_feat].desc);
return 1;
error_setg(errp, "Hyper-V %s requires Hyper-V %s",
kvm_hyperv_properties[feature].desc,
kvm_hyperv_properties[dep_feat].desc);
return 1;
}
deps &= ~(1ull << dep_feat);
}
for (i = 0; i < ARRAY_SIZE(kvm_hyperv_properties[feature].flags); i++) {
fw = kvm_hyperv_properties[feature].flags[i].fw;
bits = kvm_hyperv_properties[feature].flags[i].bits;
if (!fw) {
continue;
if (!hyperv_feature_supported(cs, feature)) {
if (hyperv_feat_enabled(cpu, feature)) {
error_setg(errp, "Hyper-V %s is not supported by kernel",
kvm_hyperv_properties[feature].desc);
return 1;
} else {
return 0;
}
if (hv_cpuid_get_fw(cpuid, fw, &r) || (r & bits) != bits) {
if (hyperv_feat_enabled(cpu, feature)) {
fprintf(stderr,
"Hyper-V %s is not supported by kernel\n",
kvm_hyperv_properties[feature].desc);
return 1;
} else {
return 0;
}
}
env->features[fw] |= bits;
}
if (cpu->hyperv_passthrough) {
@ -1163,157 +1184,156 @@ static int hv_cpuid_check_and_set(CPUState *cs, struct kvm_cpuid2 *cpuid,
return 0;
}
/*
* Fill in Hyper-V CPUIDs. Returns the number of entries filled in cpuid_ent in
* case of success, errno < 0 in case of failure and 0 when no Hyper-V
* extentions are enabled.
*/
static int hyperv_handle_properties(CPUState *cs,
struct kvm_cpuid_entry2 *cpuid_ent)
static uint32_t hv_build_cpuid_leaf(CPUState *cs, uint32_t func, int reg)
{
X86CPU *cpu = X86_CPU(cs);
uint32_t r = 0;
int i, j;
for (i = 0; i < ARRAY_SIZE(kvm_hyperv_properties); i++) {
if (!hyperv_feat_enabled(cpu, i)) {
continue;
}
for (j = 0; j < ARRAY_SIZE(kvm_hyperv_properties[i].flags); j++) {
if (kvm_hyperv_properties[i].flags[j].func != func) {
continue;
}
if (kvm_hyperv_properties[i].flags[j].reg != reg) {
continue;
}
r |= kvm_hyperv_properties[i].flags[j].bits;
}
}
return r;
}
/*
* Expand Hyper-V CPU features. In partucular, check that all the requested
* features are supported by the host and the sanity of the configuration
* (that all the required dependencies are included). Also, this takes care
* of 'hv_passthrough' mode and fills the environment with all supported
* Hyper-V features.
*/
static void hyperv_expand_features(CPUState *cs, Error **errp)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
struct kvm_cpuid2 *cpuid;
struct kvm_cpuid_entry2 *c;
uint32_t cpuid_i = 0;
int r;
if (!hyperv_enabled(cpu))
return 0;
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS) ||
cpu->hyperv_passthrough) {
uint16_t evmcs_version;
r = kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, 0,
(uintptr_t)&evmcs_version);
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS) && r) {
fprintf(stderr, "Hyper-V %s is not supported by kernel\n",
kvm_hyperv_properties[HYPERV_FEAT_EVMCS].desc);
return -ENOSYS;
}
if (!r) {
env->features[FEAT_HV_RECOMM_EAX] |=
HV_ENLIGHTENED_VMCS_RECOMMENDED;
env->features[FEAT_HV_NESTED_EAX] = evmcs_version;
}
}
if (kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_CPUID) > 0) {
cpuid = get_supported_hv_cpuid(cs);
} else {
cpuid = get_supported_hv_cpuid_legacy(cs);
}
return;
if (cpu->hyperv_passthrough) {
memcpy(cpuid_ent, &cpuid->entries[0],
cpuid->nent * sizeof(cpuid->entries[0]));
cpu->hyperv_vendor_id[0] =
hv_cpuid_get_host(cs, HV_CPUID_VENDOR_AND_MAX_FUNCTIONS, R_EBX);
cpu->hyperv_vendor_id[1] =
hv_cpuid_get_host(cs, HV_CPUID_VENDOR_AND_MAX_FUNCTIONS, R_ECX);
cpu->hyperv_vendor_id[2] =
hv_cpuid_get_host(cs, HV_CPUID_VENDOR_AND_MAX_FUNCTIONS, R_EDX);
cpu->hyperv_vendor = g_realloc(cpu->hyperv_vendor,
sizeof(cpu->hyperv_vendor_id) + 1);
memcpy(cpu->hyperv_vendor, cpu->hyperv_vendor_id,
sizeof(cpu->hyperv_vendor_id));
cpu->hyperv_vendor[sizeof(cpu->hyperv_vendor_id)] = 0;
c = cpuid_find_entry(cpuid, HV_CPUID_VENDOR_AND_MAX_FUNCTIONS, 0);
if (c) {
cpu->hyperv_vendor_id[0] = c->ebx;
cpu->hyperv_vendor_id[1] = c->ecx;
cpu->hyperv_vendor_id[2] = c->edx;
}
cpu->hyperv_interface_id[0] =
hv_cpuid_get_host(cs, HV_CPUID_INTERFACE, R_EAX);
cpu->hyperv_interface_id[1] =
hv_cpuid_get_host(cs, HV_CPUID_INTERFACE, R_EBX);
cpu->hyperv_interface_id[2] =
hv_cpuid_get_host(cs, HV_CPUID_INTERFACE, R_ECX);
cpu->hyperv_interface_id[3] =
hv_cpuid_get_host(cs, HV_CPUID_INTERFACE, R_EDX);
c = cpuid_find_entry(cpuid, HV_CPUID_INTERFACE, 0);
if (c) {
cpu->hyperv_interface_id[0] = c->eax;
cpu->hyperv_interface_id[1] = c->ebx;
cpu->hyperv_interface_id[2] = c->ecx;
cpu->hyperv_interface_id[3] = c->edx;
}
cpu->hyperv_version_id[0] =
hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EAX);
cpu->hyperv_version_id[1] =
hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EBX);
cpu->hyperv_version_id[2] =
hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_ECX);
cpu->hyperv_version_id[3] =
hv_cpuid_get_host(cs, HV_CPUID_VERSION, R_EDX);
c = cpuid_find_entry(cpuid, HV_CPUID_VERSION, 0);
if (c) {
cpu->hyperv_version_id[0] = c->eax;
cpu->hyperv_version_id[1] = c->ebx;
cpu->hyperv_version_id[2] = c->ecx;
cpu->hyperv_version_id[3] = c->edx;
}
cpu->hv_max_vps = hv_cpuid_get_host(cs, HV_CPUID_IMPLEMENT_LIMITS,
R_EAX);
cpu->hyperv_limits[0] =
hv_cpuid_get_host(cs, HV_CPUID_IMPLEMENT_LIMITS, R_EBX);
cpu->hyperv_limits[1] =
hv_cpuid_get_host(cs, HV_CPUID_IMPLEMENT_LIMITS, R_ECX);
cpu->hyperv_limits[2] =
hv_cpuid_get_host(cs, HV_CPUID_IMPLEMENT_LIMITS, R_EDX);
c = cpuid_find_entry(cpuid, HV_CPUID_FEATURES, 0);
if (c) {
env->features[FEAT_HYPERV_EAX] = c->eax;
env->features[FEAT_HYPERV_EBX] = c->ebx;
env->features[FEAT_HYPERV_EDX] = c->edx;
}
c = cpuid_find_entry(cpuid, HV_CPUID_IMPLEMENT_LIMITS, 0);
if (c) {
cpu->hv_max_vps = c->eax;
cpu->hyperv_limits[0] = c->ebx;
cpu->hyperv_limits[1] = c->ecx;
cpu->hyperv_limits[2] = c->edx;
}
c = cpuid_find_entry(cpuid, HV_CPUID_ENLIGHTMENT_INFO, 0);
if (c) {
env->features[FEAT_HV_RECOMM_EAX] = c->eax;
/* hv-spinlocks may have been overriden */
if (cpu->hyperv_spinlock_attempts != HYPERV_SPINLOCK_NEVER_NOTIFY) {
c->ebx = cpu->hyperv_spinlock_attempts;
}
}
c = cpuid_find_entry(cpuid, HV_CPUID_NESTED_FEATURES, 0);
if (c) {
env->features[FEAT_HV_NESTED_EAX] = c->eax;
}
}
if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_ON) {
env->features[FEAT_HV_RECOMM_EAX] |= HV_NO_NONARCH_CORESHARING;
} else if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_AUTO) {
c = cpuid_find_entry(cpuid, HV_CPUID_ENLIGHTMENT_INFO, 0);
if (c) {
env->features[FEAT_HV_RECOMM_EAX] |=
c->eax & HV_NO_NONARCH_CORESHARING;
}
cpu->hyperv_spinlock_attempts =
hv_cpuid_get_host(cs, HV_CPUID_ENLIGHTMENT_INFO, R_EBX);
}
/* Features */
r = hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_RELAXED);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_VAPIC);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_TIME);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_CRASH);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_RESET);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_VPINDEX);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_RUNTIME);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_SYNIC);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_STIMER);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_FREQUENCIES);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_REENLIGHTENMENT);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_TLBFLUSH);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_EVMCS);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_IPI);
r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_STIMER_DIRECT);
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_RELAXED, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_VAPIC, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_TIME, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_CRASH, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_RESET, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_VPINDEX, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_RUNTIME, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_SYNIC, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_STIMER, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_FREQUENCIES, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_REENLIGHTENMENT, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_TLBFLUSH, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_EVMCS, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_IPI, errp)) {
return;
}
if (hv_cpuid_check_and_set(cs, HYPERV_FEAT_STIMER_DIRECT, errp)) {
return;
}
/* Additional dependencies not covered by kvm_hyperv_properties[] */
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNIC) &&
!cpu->hyperv_synic_kvm_only &&
!hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX)) {
fprintf(stderr, "Hyper-V %s requires Hyper-V %s\n",
kvm_hyperv_properties[HYPERV_FEAT_SYNIC].desc,
kvm_hyperv_properties[HYPERV_FEAT_VPINDEX].desc);
r |= 1;
error_setg(errp, "Hyper-V %s requires Hyper-V %s",
kvm_hyperv_properties[HYPERV_FEAT_SYNIC].desc,
kvm_hyperv_properties[HYPERV_FEAT_VPINDEX].desc);
}
}
/* Not exposed by KVM but needed to make CPU hotplug in Windows work */
env->features[FEAT_HYPERV_EDX] |= HV_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
if (r) {
r = -ENOSYS;
goto free;
}
if (cpu->hyperv_passthrough) {
/* We already copied all feature words from KVM as is */
r = cpuid->nent;
goto free;
}
/*
* Fill in Hyper-V CPUIDs. Returns the number of entries filled in cpuid_ent.
*/
static int hyperv_fill_cpuids(CPUState *cs,
struct kvm_cpuid_entry2 *cpuid_ent)
{
X86CPU *cpu = X86_CPU(cs);
struct kvm_cpuid_entry2 *c;
uint32_t cpuid_i = 0;
c = &cpuid_ent[cpuid_i++];
c->function = HV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
@ -1339,15 +1359,25 @@ static int hyperv_handle_properties(CPUState *cs,
c = &cpuid_ent[cpuid_i++];
c->function = HV_CPUID_FEATURES;
c->eax = env->features[FEAT_HYPERV_EAX];
c->ebx = env->features[FEAT_HYPERV_EBX];
c->edx = env->features[FEAT_HYPERV_EDX];
c->eax = hv_build_cpuid_leaf(cs, HV_CPUID_FEATURES, R_EAX);
c->ebx = hv_build_cpuid_leaf(cs, HV_CPUID_FEATURES, R_EBX);
c->edx = hv_build_cpuid_leaf(cs, HV_CPUID_FEATURES, R_EDX);
/* Not exposed by KVM but needed to make CPU hotplug in Windows work */
c->edx |= HV_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
c = &cpuid_ent[cpuid_i++];
c->function = HV_CPUID_ENLIGHTMENT_INFO;
c->eax = env->features[FEAT_HV_RECOMM_EAX];
c->eax = hv_build_cpuid_leaf(cs, HV_CPUID_ENLIGHTMENT_INFO, R_EAX);
c->ebx = cpu->hyperv_spinlock_attempts;
if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_ON) {
c->eax |= HV_NO_NONARCH_CORESHARING;
} else if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_AUTO) {
c->eax |= hv_cpuid_get_host(cs, HV_CPUID_ENLIGHTMENT_INFO, R_EAX) &
HV_NO_NONARCH_CORESHARING;
}
c = &cpuid_ent[cpuid_i++];
c->function = HV_CPUID_IMPLEMENT_LIMITS;
c->eax = cpu->hv_max_vps;
@ -1367,14 +1397,10 @@ static int hyperv_handle_properties(CPUState *cs,
c = &cpuid_ent[cpuid_i++];
c->function = HV_CPUID_NESTED_FEATURES;
c->eax = env->features[FEAT_HV_NESTED_EAX];
c->eax = cpu->hyperv_nested[0];
}
r = cpuid_i;
free:
g_free(cpuid);
return r;
return cpuid_i;
}
static Error *hv_passthrough_mig_blocker;
@ -1458,6 +1484,21 @@ static int hyperv_init_vcpu(X86CPU *cpu)
}
}
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_EVMCS)) {
uint16_t evmcs_version;
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, 0,
(uintptr_t)&evmcs_version);
if (ret < 0) {
fprintf(stderr, "Hyper-V %s is not supported by kernel\n",
kvm_hyperv_properties[HYPERV_FEAT_EVMCS].desc);
return ret;
}
cpu->hyperv_nested[0] = evmcs_version;
}
return 0;
}
@ -1516,11 +1557,19 @@ int kvm_arch_init_vcpu(CPUState *cs)
env->apic_bus_freq = KVM_APIC_BUS_FREQUENCY;
/* Paravirtualization CPUIDs */
r = hyperv_handle_properties(cs, cpuid_data.entries);
if (r < 0) {
return r;
} else if (r > 0) {
cpuid_i = r;
hyperv_expand_features(cs, &local_err);
if (local_err) {
error_report_err(local_err);
return -ENOSYS;
}
if (hyperv_enabled(cpu)) {
r = hyperv_init_vcpu(cpu);
if (r) {
return r;
}
cpuid_i = hyperv_fill_cpuids(cs, cpuid_data.entries);
kvm_base = KVM_CPUID_SIGNATURE_NEXT;
has_msr_hv_hypercall = true;
}
@ -1869,11 +1918,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
kvm_init_msrs(cpu);
r = hyperv_init_vcpu(cpu);
if (r) {
goto fail;
}
return 0;
fail:

View File

@ -757,3 +757,9 @@ void qmp_sev_inject_launch_secret(const char *packet_hdr,
sev_inject_launch_secret(packet_hdr, secret, gpa, errp);
}
SevAttestationReport *
qmp_query_sev_attestation_report(const char *mnonce, Error **errp)
{
return sev_get_attestation_report(mnonce, errp);
}

View File

@ -74,3 +74,10 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
{
abort();
}
SevAttestationReport *
sev_get_attestation_report(const char *mnonce, Error **errp)
{
error_setg(errp, "SEV is not available in this QEMU");
return NULL;
}

View File

@ -87,29 +87,31 @@ static SevGuestState *sev_guest;
static Error *sev_mig_blocker;
static const char *const sev_fw_errlist[] = {
"",
"Platform state is invalid",
"Guest state is invalid",
"Platform configuration is invalid",
"Buffer too small",
"Platform is already owned",
"Certificate is invalid",
"Policy is not allowed",
"Guest is not active",
"Invalid address",
"Bad signature",
"Bad measurement",
"Asid is already owned",
"Invalid ASID",
"WBINVD is required",
"DF_FLUSH is required",
"Guest handle is invalid",
"Invalid command",
"Guest is active",
"Hardware error",
"Hardware unsafe",
"Feature not supported",
"Invalid parameter"
[SEV_RET_SUCCESS] = "",
[SEV_RET_INVALID_PLATFORM_STATE] = "Platform state is invalid",
[SEV_RET_INVALID_GUEST_STATE] = "Guest state is invalid",
[SEV_RET_INAVLID_CONFIG] = "Platform configuration is invalid",
[SEV_RET_INVALID_LEN] = "Buffer too small",
[SEV_RET_ALREADY_OWNED] = "Platform is already owned",
[SEV_RET_INVALID_CERTIFICATE] = "Certificate is invalid",
[SEV_RET_POLICY_FAILURE] = "Policy is not allowed",
[SEV_RET_INACTIVE] = "Guest is not active",
[SEV_RET_INVALID_ADDRESS] = "Invalid address",
[SEV_RET_BAD_SIGNATURE] = "Bad signature",
[SEV_RET_BAD_MEASUREMENT] = "Bad measurement",
[SEV_RET_ASID_OWNED] = "ASID is already owned",
[SEV_RET_INVALID_ASID] = "Invalid ASID",
[SEV_RET_WBINVD_REQUIRED] = "WBINVD is required",
[SEV_RET_DFFLUSH_REQUIRED] = "DF_FLUSH is required",
[SEV_RET_INVALID_GUEST] = "Guest handle is invalid",
[SEV_RET_INVALID_COMMAND] = "Invalid command",
[SEV_RET_ACTIVE] = "Guest is active",
[SEV_RET_HWSEV_RET_PLATFORM] = "Hardware error",
[SEV_RET_HWSEV_RET_UNSAFE] = "Hardware unsafe",
[SEV_RET_UNSUPPORTED] = "Feature not supported",
[SEV_RET_INVALID_PARAM] = "Invalid parameter",
[SEV_RET_RESOURCE_LIMIT] = "Required firmware resource depleted",
[SEV_RET_SECURE_DATA_INVALID] = "Part-specific integrity check failure",
};
#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist)
@ -492,6 +494,73 @@ out:
return cap;
}
SevAttestationReport *
sev_get_attestation_report(const char *mnonce, Error **errp)
{
struct kvm_sev_attestation_report input = {};
SevAttestationReport *report = NULL;
SevGuestState *sev = sev_guest;
guchar *data;
guchar *buf;
gsize len;
int err = 0, ret;
if (!sev_enabled()) {
error_setg(errp, "SEV is not enabled");
return NULL;
}
/* lets decode the mnonce string */
buf = g_base64_decode(mnonce, &len);
if (!buf) {
error_setg(errp, "SEV: failed to decode mnonce input");
return NULL;
}
/* verify the input mnonce length */
if (len != sizeof(input.mnonce)) {
error_setg(errp, "SEV: mnonce must be %zu bytes (got %" G_GSIZE_FORMAT ")",
sizeof(input.mnonce), len);
g_free(buf);
return NULL;
}
/* Query the report length */
ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
&input, &err);
if (ret < 0) {
if (err != SEV_RET_INVALID_LEN) {
error_setg(errp, "failed to query the attestation report length "
"ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err));
g_free(buf);
return NULL;
}
}
data = g_malloc(input.len);
input.uaddr = (unsigned long)data;
memcpy(input.mnonce, buf, sizeof(input.mnonce));
/* Query the report */
ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
&input, &err);
if (ret) {
error_setg_errno(errp, errno, "Failed to get attestation report"
" ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err));
goto e_free_data;
}
report = g_new0(SevAttestationReport, 1);
report->data = g_base64_encode(data, input.len);
trace_kvm_sev_attestation_report(mnonce, report->data);
e_free_data:
g_free(data);
g_free(buf);
return report;
}
static int
sev_read_file_base64(const char *filename, guchar **data, gsize *len)
{

View File

@ -35,5 +35,7 @@ extern uint32_t sev_get_cbit_position(void);
extern uint32_t sev_get_reduced_phys_bits(void);
extern char *sev_get_launch_measurement(void);
extern SevCapability *sev_get_capabilities(Error **errp);
extern SevAttestationReport *
sev_get_attestation_report(const char *mnonce, Error **errp);
#endif

View File

@ -10,3 +10,4 @@ kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIx64
kvm_sev_launch_measurement(const char *value) "data %s"
kvm_sev_launch_finish(void) ""
kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d"
kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"