mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
+5
-1
@@ -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[] = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)' }
|
||||
|
||||
+48
-15
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+123
-167
@@ -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;
|
||||
|
||||
+1
-5
@@ -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;
|
||||
|
||||
@@ -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" },
|
||||
|
||||
+278
-234
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+92
-23
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user