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).
This commit is contained in:
@@ -107,6 +107,21 @@ static void ucall_abort(const char *assert_msg, const char *expected_assert_msg)
|
||||
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,
|
||||
const char *expected_assert)
|
||||
{
|
||||
@@ -114,7 +129,7 @@ static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf,
|
||||
struct ucall uc;
|
||||
|
||||
while (1) {
|
||||
vcpu_run(vcpu);
|
||||
do_vcpu_run(vcpu);
|
||||
|
||||
TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
|
||||
"Unexpected exit reason: %u (%s),",
|
||||
@@ -159,7 +174,7 @@ static void test_limits(void)
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_limits);
|
||||
run = vcpu->run;
|
||||
vcpu_run(vcpu);
|
||||
do_vcpu_run(vcpu);
|
||||
|
||||
TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
|
||||
"Unexpected exit reason: %u (%s),",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "processor.h"
|
||||
#include "ucall_common.h"
|
||||
|
||||
#define APIC_DEFAULT_GPA 0xfee00000ULL
|
||||
|
||||
@@ -93,9 +94,27 @@ static inline uint64_t x2apic_read_reg(unsigned int reg)
|
||||
return rdmsr(APIC_BASE_MSR + (reg >> 4));
|
||||
}
|
||||
|
||||
static inline void x2apic_write_reg(unsigned int reg, uint64_t value)
|
||||
static inline uint8_t x2apic_write_reg_safe(unsigned int reg, uint64_t value)
|
||||
{
|
||||
wrmsr(APIC_BASE_MSR + (reg >> 4), value);
|
||||
return wrmsr_safe(APIC_BASE_MSR + (reg >> 4), value);
|
||||
}
|
||||
|
||||
static inline void x2apic_write_reg(unsigned int reg, uint64_t 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 */
|
||||
|
||||
@@ -566,10 +566,8 @@ void route_exception(struct ex_regs *regs)
|
||||
if (kvm_fixup_exception(regs))
|
||||
return;
|
||||
|
||||
ucall_assert(UCALL_UNHANDLED,
|
||||
"Unhandled exception in guest", __FILE__, __LINE__,
|
||||
"Unhandled exception '0x%lx' at guest RIP '0x%lx'",
|
||||
regs->vector, regs->rip);
|
||||
GUEST_FAIL("Unhandled exception '0x%lx' at guest RIP '0x%lx'",
|
||||
regs->vector, regs->rip);
|
||||
}
|
||||
|
||||
static void vm_init_descriptor_tables(struct kvm_vm *vm)
|
||||
@@ -611,7 +609,7 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct ucall uc;
|
||||
|
||||
if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED)
|
||||
if (get_ucall(vcpu, &uc) == UCALL_ABORT)
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
struct xapic_vcpu {
|
||||
struct kvm_vcpu *vcpu;
|
||||
bool is_x2apic;
|
||||
bool has_xavic_errata;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
asm volatile("cli");
|
||||
@@ -41,7 +46,12 @@ static void x2apic_guest_code(void)
|
||||
uint64_t val = x2apic_read_reg(APIC_IRR) |
|
||||
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);
|
||||
} while (1);
|
||||
}
|
||||
@@ -71,27 +81,28 @@ static void ____test_icr(struct xapic_vcpu *x, uint64_t val)
|
||||
icr = (u64)(*((u32 *)&xapic.regs[APIC_ICR])) |
|
||||
(u64)(*((u32 *)&xapic.regs[APIC_ICR2])) << 32;
|
||||
if (!x->is_x2apic) {
|
||||
val &= (-1u | (0xffull << (32 + 24)));
|
||||
TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY);
|
||||
} else {
|
||||
TEST_ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY);
|
||||
if (!x->has_xavic_errata)
|
||||
val &= (-1u | (0xffull << (32 + 24)));
|
||||
} else if (val & X2APIC_RSVD_BITS_MASK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define X2APIC_RSVED_BITS_MASK (GENMASK_ULL(31,20) | \
|
||||
GENMASK_ULL(17,16) | \
|
||||
GENMASK_ULL(13,13))
|
||||
if (x->has_xavic_errata)
|
||||
TEST_ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY);
|
||||
else
|
||||
TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY);
|
||||
}
|
||||
|
||||
static void __test_icr(struct xapic_vcpu *x, uint64_t val)
|
||||
{
|
||||
if (x->is_x2apic) {
|
||||
/* Hardware writing vICR register requires reserved bits 31:20,
|
||||
* 17:16 and 13 kept as zero to avoid #GP exception. Data value
|
||||
* written to vICR should mask out those bits above.
|
||||
*/
|
||||
val &= ~X2APIC_RSVED_BITS_MASK;
|
||||
}
|
||||
____test_icr(x, val | APIC_ICR_BUSY);
|
||||
/*
|
||||
* The BUSY bit is reserved on both AMD and Intel, but only AMD treats
|
||||
* it is as _must_ be zero. Intel simply ignores the bit. Don't test
|
||||
* the BUSY bit for x2APIC, as there is no single correct behavior.
|
||||
*/
|
||||
if (!x->is_x2apic)
|
||||
____test_icr(x, val | APIC_ICR_BUSY);
|
||||
|
||||
____test_icr(x, val & ~(u64)APIC_ICR_BUSY);
|
||||
}
|
||||
|
||||
@@ -231,6 +242,15 @@ int main(int argc, char *argv[])
|
||||
vm = vm_create_with_one_vcpu(&x.vcpu, xapic_guest_code);
|
||||
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);
|
||||
|
||||
virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
|
||||
|
||||
Reference in New Issue
Block a user