Commit 41786cc5 authored by Paolo Bonzini's avatar Paolo Bonzini

Merge tag 'kvm-x86-misc-6.12' of https://github.com/kvm-x86/linux into HEAD

KVM x86 misc changes for 6.12

 - Advertise AVX10.1 to userspace (effectively prep work for the "real" AVX10
   functionality that is on the horizon).

 - Rework common MSR handling code to suppress errors on userspace accesses to
   unsupported-but-advertised MSRs.  This will allow removing (almost?) all of
   KVM's exemptions for userspace access to MSRs that shouldn't exist based on
   the vCPU model (the actual cleanup is non-trivial future work).

 - Rework KVM's handling of x2APIC ICR, again, because AMD (x2AVIC) splits the
   64-bit value into the legacy ICR and ICR2 storage, whereas Intel (APICv)
   stores the entire 64-bit value a the ICR offset.

 - Fix a bug where KVM would fail to exit to userspace if one was triggered by
   a fastpath exit handler.

 - Add fastpath handling of HLT VM-Exit to expedite re-entering the guest when
   there's already a pending wake event at the time of the exit.

 - Finally fix the RSM vs. nested VM-Enter WARN by forcing the vCPU out of
   guest mode prior to signalling SHUTDOWN (architecturally, the SHUTDOWN is
   supposed to hit L1, not L2).
parents 7056c4e2 4ca077f2
...@@ -179,6 +179,7 @@ static __always_inline bool cpuid_function_is_indexed(u32 function) ...@@ -179,6 +179,7 @@ static __always_inline bool cpuid_function_is_indexed(u32 function)
case 0x1d: case 0x1d:
case 0x1e: case 0x1e:
case 0x1f: case 0x1f:
case 0x24:
case 0x8000001d: case 0x8000001d:
return true; return true;
} }
......
...@@ -125,7 +125,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region) ...@@ -125,7 +125,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region)
KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from) KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from)
KVM_X86_OP_OPTIONAL(vm_move_enc_context_from) KVM_X86_OP_OPTIONAL(vm_move_enc_context_from)
KVM_X86_OP_OPTIONAL(guest_memory_reclaimed) KVM_X86_OP_OPTIONAL(guest_memory_reclaimed)
KVM_X86_OP(get_msr_feature) KVM_X86_OP(get_feature_msr)
KVM_X86_OP(check_emulate_instruction) KVM_X86_OP(check_emulate_instruction)
KVM_X86_OP(apic_init_signal_blocked) KVM_X86_OP(apic_init_signal_blocked)
KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush) KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush)
......
...@@ -212,6 +212,7 @@ enum exit_fastpath_completion { ...@@ -212,6 +212,7 @@ enum exit_fastpath_completion {
EXIT_FASTPATH_NONE, EXIT_FASTPATH_NONE,
EXIT_FASTPATH_REENTER_GUEST, EXIT_FASTPATH_REENTER_GUEST,
EXIT_FASTPATH_EXIT_HANDLED, EXIT_FASTPATH_EXIT_HANDLED,
EXIT_FASTPATH_EXIT_USERSPACE,
}; };
typedef enum exit_fastpath_completion fastpath_t; typedef enum exit_fastpath_completion fastpath_t;
...@@ -1730,6 +1731,8 @@ struct kvm_x86_ops { ...@@ -1730,6 +1731,8 @@ struct kvm_x86_ops {
void (*enable_nmi_window)(struct kvm_vcpu *vcpu); void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
void (*enable_irq_window)(struct kvm_vcpu *vcpu); void (*enable_irq_window)(struct kvm_vcpu *vcpu);
void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr); void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
const bool x2apic_icr_is_split;
const unsigned long required_apicv_inhibits; const unsigned long required_apicv_inhibits;
bool allow_apicv_in_x2apic_without_x2apic_virtualization; bool allow_apicv_in_x2apic_without_x2apic_virtualization;
void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu); void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
...@@ -1809,7 +1812,7 @@ struct kvm_x86_ops { ...@@ -1809,7 +1812,7 @@ struct kvm_x86_ops {
int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd); int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
void (*guest_memory_reclaimed)(struct kvm *kvm); void (*guest_memory_reclaimed)(struct kvm *kvm);
int (*get_msr_feature)(struct kvm_msr_entry *entry); int (*get_feature_msr)(u32 msr, u64 *data);
int (*check_emulate_instruction)(struct kvm_vcpu *vcpu, int emul_type, int (*check_emulate_instruction)(struct kvm_vcpu *vcpu, int emul_type,
void *insn, int insn_len); void *insn, int insn_len);
......
...@@ -705,7 +705,7 @@ void kvm_set_cpu_caps(void) ...@@ -705,7 +705,7 @@ void kvm_set_cpu_caps(void)
kvm_cpu_cap_init_kvm_defined(CPUID_7_1_EDX, kvm_cpu_cap_init_kvm_defined(CPUID_7_1_EDX,
F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) | F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) |
F(AMX_COMPLEX) F(AMX_COMPLEX) | F(AVX10)
); );
kvm_cpu_cap_init_kvm_defined(CPUID_7_2_EDX, kvm_cpu_cap_init_kvm_defined(CPUID_7_2_EDX,
...@@ -721,6 +721,10 @@ void kvm_set_cpu_caps(void) ...@@ -721,6 +721,10 @@ void kvm_set_cpu_caps(void)
SF(SGX1) | SF(SGX2) | SF(SGX_EDECCSSA) SF(SGX1) | SF(SGX2) | SF(SGX_EDECCSSA)
); );
kvm_cpu_cap_init_kvm_defined(CPUID_24_0_EBX,
F(AVX10_128) | F(AVX10_256) | F(AVX10_512)
);
kvm_cpu_cap_mask(CPUID_8000_0001_ECX, kvm_cpu_cap_mask(CPUID_8000_0001_ECX,
F(LAHF_LM) | F(CMP_LEGACY) | 0 /*SVM*/ | 0 /* ExtApicSpace */ | F(LAHF_LM) | F(CMP_LEGACY) | 0 /*SVM*/ | 0 /* ExtApicSpace */ |
F(CR8_LEGACY) | F(ABM) | F(SSE4A) | F(MISALIGNSSE) | F(CR8_LEGACY) | F(ABM) | F(SSE4A) | F(MISALIGNSSE) |
...@@ -949,7 +953,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) ...@@ -949,7 +953,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
switch (function) { switch (function) {
case 0: case 0:
/* Limited to the highest leaf implemented in KVM. */ /* Limited to the highest leaf implemented in KVM. */
entry->eax = min(entry->eax, 0x1fU); entry->eax = min(entry->eax, 0x24U);
break; break;
case 1: case 1:
cpuid_entry_override(entry, CPUID_1_EDX); cpuid_entry_override(entry, CPUID_1_EDX);
...@@ -1174,6 +1178,28 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) ...@@ -1174,6 +1178,28 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
break; break;
} }
break; break;
case 0x24: {
u8 avx10_version;
if (!kvm_cpu_cap_has(X86_FEATURE_AVX10)) {
entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
break;
}
/*
* The AVX10 version is encoded in EBX[7:0]. Note, the version
* is guaranteed to be >=1 if AVX10 is supported. Note #2, the
* version needs to be captured before overriding EBX features!
*/
avx10_version = min_t(u8, entry->ebx & 0xff, 1);
cpuid_entry_override(entry, CPUID_24_0_EBX);
entry->ebx |= avx10_version;
entry->eax = 0;
entry->ecx = 0;
entry->edx = 0;
break;
}
case KVM_CPUID_SIGNATURE: { case KVM_CPUID_SIGNATURE: {
const u32 *sigptr = (const u32 *)KVM_SIGNATURE; const u32 *sigptr = (const u32 *)KVM_SIGNATURE;
entry->eax = KVM_CPUID_FEATURES; entry->eax = KVM_CPUID_FEATURES;
......
...@@ -1944,7 +1944,7 @@ static void start_sw_tscdeadline(struct kvm_lapic *apic) ...@@ -1944,7 +1944,7 @@ static void start_sw_tscdeadline(struct kvm_lapic *apic)
u64 ns = 0; u64 ns = 0;
ktime_t expire; ktime_t expire;
struct kvm_vcpu *vcpu = apic->vcpu; struct kvm_vcpu *vcpu = apic->vcpu;
unsigned long this_tsc_khz = vcpu->arch.virtual_tsc_khz; u32 this_tsc_khz = vcpu->arch.virtual_tsc_khz;
unsigned long flags; unsigned long flags;
ktime_t now; ktime_t now;
...@@ -2453,6 +2453,43 @@ void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu) ...@@ -2453,6 +2453,43 @@ void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu)
} }
EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi);
#define X2APIC_ICR_RESERVED_BITS (GENMASK_ULL(31, 20) | GENMASK_ULL(17, 16) | BIT(13))
int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
{
if (data & X2APIC_ICR_RESERVED_BITS)
return 1;
/*
* The BUSY bit is reserved on both Intel and AMD in x2APIC mode, but
* only AMD requires it to be zero, Intel essentially just ignores the
* bit. And if IPI virtualization (Intel) or x2AVIC (AMD) is enabled,
* the CPU performs the reserved bits checks, i.e. the underlying CPU
* behavior will "win". Arbitrarily clear the BUSY bit, as there is no
* sane way to provide consistent behavior with respect to hardware.
*/
data &= ~APIC_ICR_BUSY;
kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
if (kvm_x86_ops.x2apic_icr_is_split) {
kvm_lapic_set_reg(apic, APIC_ICR, data);
kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32);
} else {
kvm_lapic_set_reg64(apic, APIC_ICR, data);
}
trace_kvm_apic_write(APIC_ICR, data);
return 0;
}
static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic)
{
if (kvm_x86_ops.x2apic_icr_is_split)
return (u64)kvm_lapic_get_reg(apic, APIC_ICR) |
(u64)kvm_lapic_get_reg(apic, APIC_ICR2) << 32;
return kvm_lapic_get_reg64(apic, APIC_ICR);
}
/* emulate APIC access in a trap manner */ /* emulate APIC access in a trap manner */
void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
{ {
...@@ -2470,7 +2507,7 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) ...@@ -2470,7 +2507,7 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
* maybe-unecessary write, and both are in the noise anyways. * maybe-unecessary write, and both are in the noise anyways.
*/ */
if (apic_x2apic_mode(apic) && offset == APIC_ICR) if (apic_x2apic_mode(apic) && offset == APIC_ICR)
kvm_x2apic_icr_write(apic, kvm_lapic_get_reg64(apic, APIC_ICR)); WARN_ON_ONCE(kvm_x2apic_icr_write(apic, kvm_x2apic_icr_read(apic)));
else else
kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset)); kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
} }
...@@ -2990,18 +3027,22 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu, ...@@ -2990,18 +3027,22 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
/* /*
* In x2APIC mode, the LDR is fixed and based on the id. And * In x2APIC mode, the LDR is fixed and based on the id. And
* ICR is internally a single 64-bit register, but needs to be * if the ICR is _not_ split, ICR is internally a single 64-bit
* split to ICR+ICR2 in userspace for backwards compatibility. * register, but needs to be split to ICR+ICR2 in userspace for
* backwards compatibility.
*/ */
if (set) { if (set)
*ldr = kvm_apic_calc_x2apic_ldr(x2apic_id); *ldr = kvm_apic_calc_x2apic_ldr(x2apic_id);
icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) | if (!kvm_x86_ops.x2apic_icr_is_split) {
(u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32; if (set) {
__kvm_lapic_set_reg64(s->regs, APIC_ICR, icr); icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
} else { (u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR); __kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32); } else {
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
}
} }
} }
...@@ -3194,22 +3235,12 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr) ...@@ -3194,22 +3235,12 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
return 0; return 0;
} }
int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
{
data &= ~APIC_ICR_BUSY;
kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
kvm_lapic_set_reg64(apic, APIC_ICR, data);
trace_kvm_apic_write(APIC_ICR, data);
return 0;
}
static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data) static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data)
{ {
u32 low; u32 low;
if (reg == APIC_ICR) { if (reg == APIC_ICR) {
*data = kvm_lapic_get_reg64(apic, APIC_ICR); *data = kvm_x2apic_icr_read(apic);
return 0; return 0;
} }
......
...@@ -96,7 +96,6 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); ...@@ -96,7 +96,6 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu); void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value); void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
void kvm_recalculate_apic_map(struct kvm *kvm); void kvm_recalculate_apic_map(struct kvm *kvm);
void kvm_apic_set_version(struct kvm_vcpu *vcpu); void kvm_apic_set_version(struct kvm_vcpu *vcpu);
void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu);
......
...@@ -223,8 +223,6 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, ...@@ -223,8 +223,6 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
bool kvm_mmu_may_ignore_guest_pat(void); bool kvm_mmu_may_ignore_guest_pat(void);
int kvm_arch_write_log_dirty(struct kvm_vcpu *vcpu);
int kvm_mmu_post_init_vm(struct kvm *kvm); int kvm_mmu_post_init_vm(struct kvm *kvm);
void kvm_mmu_pre_destroy_vm(struct kvm *kvm); void kvm_mmu_pre_destroy_vm(struct kvm *kvm);
......
...@@ -349,8 +349,6 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm, ...@@ -349,8 +349,6 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm,
void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault); void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault);
void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_level); void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_level);
void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc);
void track_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp); void track_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp);
void untrack_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp); void untrack_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp);
......
...@@ -17,6 +17,7 @@ enum kvm_only_cpuid_leafs { ...@@ -17,6 +17,7 @@ enum kvm_only_cpuid_leafs {
CPUID_8000_0007_EDX, CPUID_8000_0007_EDX,
CPUID_8000_0022_EAX, CPUID_8000_0022_EAX,
CPUID_7_2_EDX, CPUID_7_2_EDX,
CPUID_24_0_EBX,
NR_KVM_CPU_CAPS, NR_KVM_CPU_CAPS,
NKVMCAPINTS = NR_KVM_CPU_CAPS - NCAPINTS, NKVMCAPINTS = NR_KVM_CPU_CAPS - NCAPINTS,
...@@ -46,6 +47,7 @@ enum kvm_only_cpuid_leafs { ...@@ -46,6 +47,7 @@ enum kvm_only_cpuid_leafs {
#define X86_FEATURE_AVX_NE_CONVERT KVM_X86_FEATURE(CPUID_7_1_EDX, 5) #define X86_FEATURE_AVX_NE_CONVERT KVM_X86_FEATURE(CPUID_7_1_EDX, 5)
#define X86_FEATURE_AMX_COMPLEX KVM_X86_FEATURE(CPUID_7_1_EDX, 8) #define X86_FEATURE_AMX_COMPLEX KVM_X86_FEATURE(CPUID_7_1_EDX, 8)
#define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14) #define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14)
#define X86_FEATURE_AVX10 KVM_X86_FEATURE(CPUID_7_1_EDX, 19)
/* Intel-defined sub-features, CPUID level 0x00000007:2 (EDX) */ /* Intel-defined sub-features, CPUID level 0x00000007:2 (EDX) */
#define X86_FEATURE_INTEL_PSFD KVM_X86_FEATURE(CPUID_7_2_EDX, 0) #define X86_FEATURE_INTEL_PSFD KVM_X86_FEATURE(CPUID_7_2_EDX, 0)
...@@ -55,6 +57,11 @@ enum kvm_only_cpuid_leafs { ...@@ -55,6 +57,11 @@ enum kvm_only_cpuid_leafs {
#define KVM_X86_FEATURE_BHI_CTRL KVM_X86_FEATURE(CPUID_7_2_EDX, 4) #define KVM_X86_FEATURE_BHI_CTRL KVM_X86_FEATURE(CPUID_7_2_EDX, 4)
#define X86_FEATURE_MCDT_NO KVM_X86_FEATURE(CPUID_7_2_EDX, 5) #define X86_FEATURE_MCDT_NO KVM_X86_FEATURE(CPUID_7_2_EDX, 5)
/* Intel-defined sub-features, CPUID level 0x00000024:0 (EBX) */
#define X86_FEATURE_AVX10_128 KVM_X86_FEATURE(CPUID_24_0_EBX, 16)
#define X86_FEATURE_AVX10_256 KVM_X86_FEATURE(CPUID_24_0_EBX, 17)
#define X86_FEATURE_AVX10_512 KVM_X86_FEATURE(CPUID_24_0_EBX, 18)
/* CPUID level 0x80000007 (EDX). */ /* CPUID level 0x80000007 (EDX). */
#define KVM_X86_FEATURE_CONSTANT_TSC KVM_X86_FEATURE(CPUID_8000_0007_EDX, 8) #define KVM_X86_FEATURE_CONSTANT_TSC KVM_X86_FEATURE(CPUID_8000_0007_EDX, 8)
...@@ -90,6 +97,7 @@ static const struct cpuid_reg reverse_cpuid[] = { ...@@ -90,6 +97,7 @@ static const struct cpuid_reg reverse_cpuid[] = {
[CPUID_8000_0021_EAX] = {0x80000021, 0, CPUID_EAX}, [CPUID_8000_0021_EAX] = {0x80000021, 0, CPUID_EAX},
[CPUID_8000_0022_EAX] = {0x80000022, 0, CPUID_EAX}, [CPUID_8000_0022_EAX] = {0x80000022, 0, CPUID_EAX},
[CPUID_7_2_EDX] = { 7, 2, CPUID_EDX}, [CPUID_7_2_EDX] = { 7, 2, CPUID_EDX},
[CPUID_24_0_EBX] = { 0x24, 0, CPUID_EBX},
}; };
/* /*
......
...@@ -624,17 +624,31 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt) ...@@ -624,17 +624,31 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
#endif #endif
/* /*
* Give leave_smm() a chance to make ISA-specific changes to the vCPU * FIXME: When resuming L2 (a.k.a. guest mode), the transition to guest
* state (e.g. enter guest mode) before loading state from the SMM * mode should happen _after_ loading state from SMRAM. However, KVM
* state-save area. * piggybacks the nested VM-Enter flows (which is wrong for many other
* reasons), and so nSVM/nVMX would clobber state that is loaded from
* SMRAM and from the VMCS/VMCB.
*/ */
if (kvm_x86_call(leave_smm)(vcpu, &smram)) if (kvm_x86_call(leave_smm)(vcpu, &smram))
return X86EMUL_UNHANDLEABLE; return X86EMUL_UNHANDLEABLE;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) if (guest_cpuid_has(vcpu, X86_FEATURE_LM))
return rsm_load_state_64(ctxt, &smram.smram64); ret = rsm_load_state_64(ctxt, &smram.smram64);
else else
#endif #endif
return rsm_load_state_32(ctxt, &smram.smram32); ret = rsm_load_state_32(ctxt, &smram.smram32);
/*
* If RSM fails and triggers shutdown, architecturally the shutdown
* occurs *before* the transition to guest mode. But due to KVM's
* flawed handling of RSM to L2 (see above), the vCPU may already be
* in_guest_mode(). Force the vCPU out of guest mode before delivering
* the shutdown, so that L1 enters shutdown instead of seeing a VM-Exit
* that architecturally shouldn't be possible.
*/
if (ret != X86EMUL_CONTINUE && is_guest_mode(vcpu))
kvm_leave_nested(vcpu);
return ret;
} }
...@@ -2825,17 +2825,17 @@ static int efer_trap(struct kvm_vcpu *vcpu) ...@@ -2825,17 +2825,17 @@ static int efer_trap(struct kvm_vcpu *vcpu)
return kvm_complete_insn_gp(vcpu, ret); return kvm_complete_insn_gp(vcpu, ret);
} }
static int svm_get_msr_feature(struct kvm_msr_entry *msr) static int svm_get_feature_msr(u32 msr, u64 *data)
{ {
msr->data = 0; *data = 0;
switch (msr->index) { switch (msr) {
case MSR_AMD64_DE_CFG: case MSR_AMD64_DE_CFG:
if (cpu_feature_enabled(X86_FEATURE_LFENCE_RDTSC)) if (cpu_feature_enabled(X86_FEATURE_LFENCE_RDTSC))
msr->data |= MSR_AMD64_DE_CFG_LFENCE_SERIALIZE; *data |= MSR_AMD64_DE_CFG_LFENCE_SERIALIZE;
break; break;
default: default:
return KVM_MSR_RET_INVALID; return KVM_MSR_RET_UNSUPPORTED;
} }
return 0; return 0;
...@@ -3191,18 +3191,21 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) ...@@ -3191,18 +3191,21 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
kvm_pr_unimpl_wrmsr(vcpu, ecx, data); kvm_pr_unimpl_wrmsr(vcpu, ecx, data);
break; break;
case MSR_AMD64_DE_CFG: { case MSR_AMD64_DE_CFG: {
struct kvm_msr_entry msr_entry; u64 supported_de_cfg;
msr_entry.index = msr->index; if (svm_get_feature_msr(ecx, &supported_de_cfg))
if (svm_get_msr_feature(&msr_entry))
return 1; return 1;
/* Check the supported bits */ if (data & ~supported_de_cfg)
if (data & ~msr_entry.data)
return 1; return 1;
/* Don't allow the guest to change a bit, #GP */ /*
if (!msr->host_initiated && (data ^ msr_entry.data)) * Don't let the guest change the host-programmed value. The
* MSR is very model specific, i.e. contains multiple bits that
* are completely unknown to KVM, and the one bit known to KVM
* is simply a reflection of hardware capabilities.
*/
if (!msr->host_initiated && data != svm->msr_decfg)
return 1; return 1;
svm->msr_decfg = data; svm->msr_decfg = data;
...@@ -4156,12 +4159,21 @@ static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu) ...@@ -4156,12 +4159,21 @@ static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu)
static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
{ {
struct vcpu_svm *svm = to_svm(vcpu);
if (is_guest_mode(vcpu)) if (is_guest_mode(vcpu))
return EXIT_FASTPATH_NONE; return EXIT_FASTPATH_NONE;
if (to_svm(vcpu)->vmcb->control.exit_code == SVM_EXIT_MSR && switch (svm->vmcb->control.exit_code) {
to_svm(vcpu)->vmcb->control.exit_info_1) case SVM_EXIT_MSR:
if (!svm->vmcb->control.exit_info_1)
break;
return handle_fastpath_set_msr_irqoff(vcpu); return handle_fastpath_set_msr_irqoff(vcpu);
case SVM_EXIT_HLT:
return handle_fastpath_hlt(vcpu);
default:
break;
}
return EXIT_FASTPATH_NONE; return EXIT_FASTPATH_NONE;
} }
...@@ -5012,7 +5024,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { ...@@ -5012,7 +5024,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
.vcpu_unblocking = avic_vcpu_unblocking, .vcpu_unblocking = avic_vcpu_unblocking,
.update_exception_bitmap = svm_update_exception_bitmap, .update_exception_bitmap = svm_update_exception_bitmap,
.get_msr_feature = svm_get_msr_feature, .get_feature_msr = svm_get_feature_msr,
.get_msr = svm_get_msr, .get_msr = svm_get_msr,
.set_msr = svm_set_msr, .set_msr = svm_set_msr,
.get_segment_base = svm_get_segment_base, .get_segment_base = svm_get_segment_base,
...@@ -5063,6 +5075,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { ...@@ -5063,6 +5075,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
.enable_nmi_window = svm_enable_nmi_window, .enable_nmi_window = svm_enable_nmi_window,
.enable_irq_window = svm_enable_irq_window, .enable_irq_window = svm_enable_irq_window,
.update_cr8_intercept = svm_update_cr8_intercept, .update_cr8_intercept = svm_update_cr8_intercept,
.x2apic_icr_is_split = true,
.set_virtual_apic_mode = avic_refresh_virtual_apic_mode, .set_virtual_apic_mode = avic_refresh_virtual_apic_mode,
.refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl, .refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl,
.apicv_post_state_restore = avic_apicv_post_state_restore, .apicv_post_state_restore = avic_apicv_post_state_restore,
......
...@@ -43,7 +43,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { ...@@ -43,7 +43,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
.vcpu_put = vmx_vcpu_put, .vcpu_put = vmx_vcpu_put,
.update_exception_bitmap = vmx_update_exception_bitmap, .update_exception_bitmap = vmx_update_exception_bitmap,
.get_msr_feature = vmx_get_msr_feature, .get_feature_msr = vmx_get_feature_msr,
.get_msr = vmx_get_msr, .get_msr = vmx_get_msr,
.set_msr = vmx_set_msr, .set_msr = vmx_set_msr,
.get_segment_base = vmx_get_segment_base, .get_segment_base = vmx_get_segment_base,
...@@ -91,6 +91,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = { ...@@ -91,6 +91,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
.enable_nmi_window = vmx_enable_nmi_window, .enable_nmi_window = vmx_enable_nmi_window,
.enable_irq_window = vmx_enable_irq_window, .enable_irq_window = vmx_enable_irq_window,
.update_cr8_intercept = vmx_update_cr8_intercept, .update_cr8_intercept = vmx_update_cr8_intercept,
.x2apic_icr_is_split = false,
.set_virtual_apic_mode = vmx_set_virtual_apic_mode, .set_virtual_apic_mode = vmx_set_virtual_apic_mode,
.set_apic_access_page_addr = vmx_set_apic_access_page_addr, .set_apic_access_page_addr = vmx_set_apic_access_page_addr,
.refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl, .refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl,
......
...@@ -1998,15 +1998,15 @@ static inline bool is_vmx_feature_control_msr_valid(struct vcpu_vmx *vmx, ...@@ -1998,15 +1998,15 @@ static inline bool is_vmx_feature_control_msr_valid(struct vcpu_vmx *vmx,
return !(msr->data & ~valid_bits); return !(msr->data & ~valid_bits);
} }
int vmx_get_msr_feature(struct kvm_msr_entry *msr) int vmx_get_feature_msr(u32 msr, u64 *data)
{ {
switch (msr->index) { switch (msr) {
case KVM_FIRST_EMULATED_VMX_MSR ... KVM_LAST_EMULATED_VMX_MSR: case KVM_FIRST_EMULATED_VMX_MSR ... KVM_LAST_EMULATED_VMX_MSR:
if (!nested) if (!nested)
return 1; return 1;
return vmx_get_vmx_msr(&vmcs_config.nested, msr->index, &msr->data); return vmx_get_vmx_msr(&vmcs_config.nested, msr, data);
default: default:
return KVM_MSR_RET_INVALID; return KVM_MSR_RET_UNSUPPORTED;
} }
} }
...@@ -7265,6 +7265,8 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu, ...@@ -7265,6 +7265,8 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu,
return handle_fastpath_set_msr_irqoff(vcpu); return handle_fastpath_set_msr_irqoff(vcpu);
case EXIT_REASON_PREEMPTION_TIMER: case EXIT_REASON_PREEMPTION_TIMER:
return handle_fastpath_preemption_timer(vcpu, force_immediate_exit); return handle_fastpath_preemption_timer(vcpu, force_immediate_exit);
case EXIT_REASON_HLT:
return handle_fastpath_hlt(vcpu);
default: default:
return EXIT_FASTPATH_NONE; return EXIT_FASTPATH_NONE;
} }
......
...@@ -17,10 +17,6 @@ ...@@ -17,10 +17,6 @@
#include "run_flags.h" #include "run_flags.h"
#include "../mmu.h" #include "../mmu.h"
#define MSR_TYPE_R 1
#define MSR_TYPE_W 2
#define MSR_TYPE_RW 3
#define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4)) #define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4))
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
......
...@@ -57,7 +57,7 @@ bool vmx_has_emulated_msr(struct kvm *kvm, u32 index); ...@@ -57,7 +57,7 @@ bool vmx_has_emulated_msr(struct kvm *kvm, u32 index);
void vmx_msr_filter_changed(struct kvm_vcpu *vcpu); void vmx_msr_filter_changed(struct kvm_vcpu *vcpu);
void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu);
void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu); void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu);
int vmx_get_msr_feature(struct kvm_msr_entry *msr); int vmx_get_feature_msr(u32 msr, u64 *data);
int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info); int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg); u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg);
void vmx_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); void vmx_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
......
This diff is collapsed.
...@@ -108,6 +108,12 @@ static inline unsigned int __shrink_ple_window(unsigned int val, ...@@ -108,6 +108,12 @@ static inline unsigned int __shrink_ple_window(unsigned int val,
void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu); void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu);
int kvm_check_nested_events(struct kvm_vcpu *vcpu); int kvm_check_nested_events(struct kvm_vcpu *vcpu);
/* Forcibly leave the nested mode in cases like a vCPU reset */
static inline void kvm_leave_nested(struct kvm_vcpu *vcpu)
{
kvm_x86_ops.nested_ops->leave_nested(vcpu);
}
static inline bool kvm_vcpu_has_run(struct kvm_vcpu *vcpu) static inline bool kvm_vcpu_has_run(struct kvm_vcpu *vcpu)
{ {
return vcpu->arch.last_vmentry_cpu != -1; return vcpu->arch.last_vmentry_cpu != -1;
...@@ -334,6 +340,7 @@ int x86_decode_emulated_instruction(struct kvm_vcpu *vcpu, int emulation_type, ...@@ -334,6 +340,7 @@ int x86_decode_emulated_instruction(struct kvm_vcpu *vcpu, int emulation_type,
int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
int emulation_type, void *insn, int insn_len); int emulation_type, void *insn, int insn_len);
fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu); fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu);
fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu);
extern struct kvm_caps kvm_caps; extern struct kvm_caps kvm_caps;
extern struct kvm_host_values kvm_host; extern struct kvm_host_values kvm_host;
...@@ -504,13 +511,26 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r, ...@@ -504,13 +511,26 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva); int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva);
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type); bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type);
enum kvm_msr_access {
MSR_TYPE_R = BIT(0),
MSR_TYPE_W = BIT(1),
MSR_TYPE_RW = MSR_TYPE_R | MSR_TYPE_W,
};
/* /*
* Internal error codes that are used to indicate that MSR emulation encountered * Internal error codes that are used to indicate that MSR emulation encountered
* an error that should result in #GP in the guest, unless userspace * an error that should result in #GP in the guest, unless userspace handles it.
* handles it. * Note, '1', '0', and negative numbers are off limits, as they are used by KVM
* as part of KVM's lightly documented internal KVM_RUN return codes.
*
* UNSUPPORTED - The MSR isn't supported, either because it is completely
* unknown to KVM, or because the MSR should not exist according
* to the vCPU model.
*
* FILTERED - Access to the MSR is denied by a userspace MSR filter.
*/ */
#define KVM_MSR_RET_INVALID 2 /* in-kernel MSR emulation #GP condition */ #define KVM_MSR_RET_UNSUPPORTED 2
#define KVM_MSR_RET_FILTERED 3 /* #GP due to userspace MSR filter */ #define KVM_MSR_RET_FILTERED 3
#define __cr4_reserved_bits(__cpu_has, __c) \ #define __cr4_reserved_bits(__cpu_has, __c) \
({ \ ({ \
......
...@@ -107,6 +107,21 @@ static void ucall_abort(const char *assert_msg, const char *expected_assert_msg) ...@@ -107,6 +107,21 @@ static void ucall_abort(const char *assert_msg, const char *expected_assert_msg)
expected_assert_msg, &assert_msg[offset]); expected_assert_msg, &assert_msg[offset]);
} }
/*
* Open code vcpu_run(), sans the UCALL_ABORT handling, so that intentional
* guest asserts guest can be verified instead of being reported as failures.
*/
static void do_vcpu_run(struct kvm_vcpu *vcpu)
{
int r;
do {
r = __vcpu_run(vcpu);
} while (r == -1 && errno == EINTR);
TEST_ASSERT(!r, KVM_IOCTL_ERROR(KVM_RUN, r));
}
static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf, static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf,
const char *expected_assert) const char *expected_assert)
{ {
...@@ -114,7 +129,7 @@ static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf, ...@@ -114,7 +129,7 @@ static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf,
struct ucall uc; struct ucall uc;
while (1) { while (1) {
vcpu_run(vcpu); do_vcpu_run(vcpu);
TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON, TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
"Unexpected exit reason: %u (%s),", "Unexpected exit reason: %u (%s),",
...@@ -159,7 +174,7 @@ static void test_limits(void) ...@@ -159,7 +174,7 @@ static void test_limits(void)
vm = vm_create_with_one_vcpu(&vcpu, guest_code_limits); vm = vm_create_with_one_vcpu(&vcpu, guest_code_limits);
run = vcpu->run; run = vcpu->run;
vcpu_run(vcpu); do_vcpu_run(vcpu);
TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON, TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
"Unexpected exit reason: %u (%s),", "Unexpected exit reason: %u (%s),",
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <stdint.h> #include <stdint.h>
#include "processor.h" #include "processor.h"
#include "ucall_common.h"
#define APIC_DEFAULT_GPA 0xfee00000ULL #define APIC_DEFAULT_GPA 0xfee00000ULL
...@@ -93,9 +94,27 @@ static inline uint64_t x2apic_read_reg(unsigned int reg) ...@@ -93,9 +94,27 @@ static inline uint64_t x2apic_read_reg(unsigned int reg)
return rdmsr(APIC_BASE_MSR + (reg >> 4)); return rdmsr(APIC_BASE_MSR + (reg >> 4));
} }
static inline uint8_t x2apic_write_reg_safe(unsigned int reg, uint64_t value)
{
return wrmsr_safe(APIC_BASE_MSR + (reg >> 4), value);
}
static inline void x2apic_write_reg(unsigned int reg, uint64_t value) static inline void x2apic_write_reg(unsigned int reg, uint64_t value)
{ {
wrmsr(APIC_BASE_MSR + (reg >> 4), value); uint8_t fault = x2apic_write_reg_safe(reg, value);
__GUEST_ASSERT(!fault, "Unexpected fault 0x%x on WRMSR(%x) = %lx\n",
fault, APIC_BASE_MSR + (reg >> 4), value);
} }
static inline void x2apic_write_reg_fault(unsigned int reg, uint64_t value)
{
uint8_t fault = x2apic_write_reg_safe(reg, value);
__GUEST_ASSERT(fault == GP_VECTOR,
"Wanted #GP on WRMSR(%x) = %lx, got 0x%x\n",
APIC_BASE_MSR + (reg >> 4), value, fault);
}
#endif /* SELFTEST_KVM_APIC_H */ #endif /* SELFTEST_KVM_APIC_H */
...@@ -566,10 +566,8 @@ void route_exception(struct ex_regs *regs) ...@@ -566,10 +566,8 @@ void route_exception(struct ex_regs *regs)
if (kvm_fixup_exception(regs)) if (kvm_fixup_exception(regs))
return; return;
ucall_assert(UCALL_UNHANDLED, GUEST_FAIL("Unhandled exception '0x%lx' at guest RIP '0x%lx'",
"Unhandled exception in guest", __FILE__, __LINE__, regs->vector, regs->rip);
"Unhandled exception '0x%lx' at guest RIP '0x%lx'",
regs->vector, regs->rip);
} }
static void vm_init_descriptor_tables(struct kvm_vm *vm) static void vm_init_descriptor_tables(struct kvm_vm *vm)
...@@ -611,7 +609,7 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) ...@@ -611,7 +609,7 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
{ {
struct ucall uc; struct ucall uc;
if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) if (get_ucall(vcpu, &uc) == UCALL_ABORT)
REPORT_GUEST_ASSERT(uc); REPORT_GUEST_ASSERT(uc);
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
struct xapic_vcpu { struct xapic_vcpu {
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
bool is_x2apic; bool is_x2apic;
bool has_xavic_errata;
}; };
static void xapic_guest_code(void) static void xapic_guest_code(void)
...@@ -31,6 +32,10 @@ static void xapic_guest_code(void) ...@@ -31,6 +32,10 @@ static void xapic_guest_code(void)
} }
} }
#define X2APIC_RSVD_BITS_MASK (GENMASK_ULL(31, 20) | \
GENMASK_ULL(17, 16) | \
GENMASK_ULL(13, 13))
static void x2apic_guest_code(void) static void x2apic_guest_code(void)
{ {
asm volatile("cli"); asm volatile("cli");
...@@ -41,7 +46,12 @@ static void x2apic_guest_code(void) ...@@ -41,7 +46,12 @@ static void x2apic_guest_code(void)
uint64_t val = x2apic_read_reg(APIC_IRR) | uint64_t val = x2apic_read_reg(APIC_IRR) |
x2apic_read_reg(APIC_IRR + 0x10) << 32; x2apic_read_reg(APIC_IRR + 0x10) << 32;
x2apic_write_reg(APIC_ICR, val); if (val & X2APIC_RSVD_BITS_MASK) {
x2apic_write_reg_fault(APIC_ICR, val);
} else {
x2apic_write_reg(APIC_ICR, val);
GUEST_ASSERT_EQ(x2apic_read_reg(APIC_ICR), val);
}
GUEST_SYNC(val); GUEST_SYNC(val);
} while (1); } while (1);
} }
...@@ -71,27 +81,28 @@ static void ____test_icr(struct xapic_vcpu *x, uint64_t val) ...@@ -71,27 +81,28 @@ static void ____test_icr(struct xapic_vcpu *x, uint64_t val)
icr = (u64)(*((u32 *)&xapic.regs[APIC_ICR])) | icr = (u64)(*((u32 *)&xapic.regs[APIC_ICR])) |
(u64)(*((u32 *)&xapic.regs[APIC_ICR2])) << 32; (u64)(*((u32 *)&xapic.regs[APIC_ICR2])) << 32;
if (!x->is_x2apic) { if (!x->is_x2apic) {
val &= (-1u | (0xffull << (32 + 24))); if (!x->has_xavic_errata)
TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); val &= (-1u | (0xffull << (32 + 24)));
} else { } else if (val & X2APIC_RSVD_BITS_MASK) {
TEST_ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY); return;
} }
}
#define X2APIC_RSVED_BITS_MASK (GENMASK_ULL(31,20) | \ if (x->has_xavic_errata)
GENMASK_ULL(17,16) | \ TEST_ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY);
GENMASK_ULL(13,13)) else
TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY);
}
static void __test_icr(struct xapic_vcpu *x, uint64_t val) static void __test_icr(struct xapic_vcpu *x, uint64_t val)
{ {
if (x->is_x2apic) { /*
/* Hardware writing vICR register requires reserved bits 31:20, * The BUSY bit is reserved on both AMD and Intel, but only AMD treats
* 17:16 and 13 kept as zero to avoid #GP exception. Data value * it is as _must_ be zero. Intel simply ignores the bit. Don't test
* written to vICR should mask out those bits above. * the BUSY bit for x2APIC, as there is no single correct behavior.
*/ */
val &= ~X2APIC_RSVED_BITS_MASK; if (!x->is_x2apic)
} ____test_icr(x, val | APIC_ICR_BUSY);
____test_icr(x, val | APIC_ICR_BUSY);
____test_icr(x, val & ~(u64)APIC_ICR_BUSY); ____test_icr(x, val & ~(u64)APIC_ICR_BUSY);
} }
...@@ -231,6 +242,15 @@ int main(int argc, char *argv[]) ...@@ -231,6 +242,15 @@ int main(int argc, char *argv[])
vm = vm_create_with_one_vcpu(&x.vcpu, xapic_guest_code); vm = vm_create_with_one_vcpu(&x.vcpu, xapic_guest_code);
x.is_x2apic = false; x.is_x2apic = false;
/*
* AMD's AVIC implementation is buggy (fails to clear the ICR BUSY bit),
* and also diverges from KVM with respect to ICR2[23:0] (KVM and Intel
* drops writes, AMD does not). Account for the errata when checking
* that KVM reads back what was written.
*/
x.has_xavic_errata = host_cpu_is_amd &&
get_kvm_amd_param_bool("avic");
vcpu_clear_cpuid_feature(x.vcpu, X86_FEATURE_X2APIC); vcpu_clear_cpuid_feature(x.vcpu, X86_FEATURE_X2APIC);
virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment