Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull more kvm updates from Paolo Bonzini:
"Generic:
- selftest compilation fix for non-x86
- KVM: avoid warning on s390 in mark_page_dirty
x86:
- fix page write-protection bug and improve comments
- use binary search to lookup the PMU event filter, add test
- enable_pmu module parameter support for Intel CPUs
- switch blocked_vcpu_on_cpu_lock to raw spinlock
- cleanups of blocked vCPU logic
- partially allow KVM_SET_CPUID{,2} after KVM_RUN (5.16 regression)
- various small fixes"
* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (46 commits)
docs: kvm: fix WARNINGs from api.rst
selftests: kvm/x86: Fix the warning in lib/x86_64/processor.c
selftests: kvm/x86: Fix the warning in pmu_event_filter_test.c
kvm: selftests: Do not indent with spaces
kvm: selftests: sync uapi/linux/kvm.h with Linux header
selftests: kvm: add amx_test to .gitignore
KVM: SVM: Nullify vcpu_(un)blocking() hooks if AVIC is disabled
KVM: SVM: Move svm_hardware_setup() and its helpers below svm_x86_ops
KVM: SVM: Drop AVIC's intermediate avic_set_running() helper
KVM: VMX: Don't do full kick when handling posted interrupt wakeup
KVM: VMX: Fold fallback path into triggering posted IRQ helper
KVM: VMX: Pass desired vector instead of bool for triggering posted IRQ
KVM: VMX: Don't do full kick when triggering posted interrupt "fails"
KVM: SVM: Skip AVIC and IRTE updates when loading blocking vCPU
KVM: SVM: Use kvm_vcpu_is_blocking() in AVIC load to handle preemption
KVM: SVM: Remove unnecessary APICv/AVIC update in vCPU unblocking path
KVM: SVM: Don't bother checking for "running" AVIC when kicking for IPIs
KVM: SVM: Signal AVIC doorbell iff vCPU is in guest mode
KVM: x86: Remove defunct pre_block/post_block kvm_x86_ops hooks
KVM: x86: Unexport LAPIC's switch_to_{hv,sw}_timer() helpers
...
This commit is contained in:
5
tools/testing/selftests/kvm/.gitignore
vendored
5
tools/testing/selftests/kvm/.gitignore
vendored
@@ -8,11 +8,12 @@
|
||||
/s390x/memop
|
||||
/s390x/resets
|
||||
/s390x/sync_regs_test
|
||||
/x86_64/amx_test
|
||||
/x86_64/cpuid_test
|
||||
/x86_64/cr4_cpuid_sync_test
|
||||
/x86_64/debug_regs
|
||||
/x86_64/evmcs_test
|
||||
/x86_64/emulator_error_test
|
||||
/x86_64/get_cpuid_test
|
||||
/x86_64/get_msr_index_features
|
||||
/x86_64/kvm_clock_test
|
||||
/x86_64/kvm_pv_test
|
||||
@@ -22,6 +23,7 @@
|
||||
/x86_64/mmio_warning_test
|
||||
/x86_64/mmu_role_test
|
||||
/x86_64/platform_info_test
|
||||
/x86_64/pmu_event_filter_test
|
||||
/x86_64/set_boot_cpu_id
|
||||
/x86_64/set_sregs_test
|
||||
/x86_64/sev_migrate_tests
|
||||
@@ -36,6 +38,7 @@
|
||||
/x86_64/vmx_apic_access_test
|
||||
/x86_64/vmx_close_while_nested_test
|
||||
/x86_64/vmx_dirty_log_test
|
||||
/x86_64/vmx_exception_with_invalid_guest_state
|
||||
/x86_64/vmx_invalid_nested_guest_state
|
||||
/x86_64/vmx_preemption_timer_test
|
||||
/x86_64/vmx_set_nested_state_test
|
||||
|
||||
@@ -43,11 +43,11 @@ LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handler
|
||||
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
|
||||
LIBKVM_riscv = lib/riscv/processor.c lib/riscv/ucall.c
|
||||
|
||||
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
|
||||
TEST_GEN_PROGS_x86_64 = x86_64/cpuid_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/emulator_error_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/get_cpuid_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_clock
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_features
|
||||
@@ -56,6 +56,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/mmu_role_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/smm_test
|
||||
@@ -69,6 +70,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/userspace_msr_exit_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_apic_access_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_exception_with_invalid_guest_state
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_invalid_nested_guest_state
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
|
||||
|
||||
@@ -364,6 +364,24 @@ static inline unsigned long get_xmm(int n)
|
||||
}
|
||||
|
||||
bool is_intel_cpu(void);
|
||||
bool is_amd_cpu(void);
|
||||
|
||||
static inline unsigned int x86_family(unsigned int eax)
|
||||
{
|
||||
unsigned int x86;
|
||||
|
||||
x86 = (eax >> 8) & 0xf;
|
||||
|
||||
if (x86 == 0xf)
|
||||
x86 += (eax >> 20) & 0xff;
|
||||
|
||||
return x86;
|
||||
}
|
||||
|
||||
static inline unsigned int x86_model(unsigned int eax)
|
||||
{
|
||||
return ((eax >> 12) & 0xf0) | ((eax >> 4) & 0x0f);
|
||||
}
|
||||
|
||||
struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
@@ -375,6 +393,8 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index);
|
||||
struct kvm_cpuid2 *kvm_get_supported_cpuid(void);
|
||||
|
||||
struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
int __vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
struct kvm_cpuid2 *cpuid);
|
||||
void vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
struct kvm_cpuid2 *cpuid);
|
||||
|
||||
@@ -418,6 +438,11 @@ uint64_t vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr);
|
||||
void vm_set_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr,
|
||||
uint64_t pte);
|
||||
|
||||
/*
|
||||
* get_cpuid() - find matching CPUID entry and return pointer to it.
|
||||
*/
|
||||
struct kvm_cpuid_entry2 *get_cpuid(struct kvm_cpuid2 *cpuid, uint32_t function,
|
||||
uint32_t index);
|
||||
/*
|
||||
* set_cpuid() - overwrites a matching cpuid entry with the provided value.
|
||||
* matches based on ent->function && ent->index. returns true
|
||||
|
||||
@@ -393,10 +393,12 @@ struct kvm_vm *vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus,
|
||||
struct kvm_vm *vm;
|
||||
int i;
|
||||
|
||||
#ifdef __x86_64__
|
||||
/*
|
||||
* Permission needs to be requested before KVM_SET_CPUID2.
|
||||
*/
|
||||
vm_xsave_req_perm();
|
||||
#endif
|
||||
|
||||
/* Force slot0 memory size not small than DEFAULT_GUEST_PHY_PAGES */
|
||||
if (slot0_mem_pages < DEFAULT_GUEST_PHY_PAGES)
|
||||
@@ -497,9 +499,11 @@ void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log)
|
||||
void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log,
|
||||
uint64_t first_page, uint32_t num_pages)
|
||||
{
|
||||
struct kvm_clear_dirty_log args = { .dirty_bitmap = log, .slot = slot,
|
||||
.first_page = first_page,
|
||||
.num_pages = num_pages };
|
||||
struct kvm_clear_dirty_log args = {
|
||||
.dirty_bitmap = log, .slot = slot,
|
||||
.first_page = first_page,
|
||||
.num_pages = num_pages
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = ioctl(vm->fd, KVM_CLEAR_DIRTY_LOG, &args);
|
||||
|
||||
@@ -886,6 +886,17 @@ kvm_get_supported_cpuid_index(uint32_t function, uint32_t index)
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
int __vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
struct kvm_cpuid2 *cpuid)
|
||||
{
|
||||
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
|
||||
|
||||
TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid);
|
||||
|
||||
return ioctl(vcpu->fd, KVM_SET_CPUID2, cpuid);
|
||||
}
|
||||
|
||||
/*
|
||||
* VM VCPU CPUID Set
|
||||
*
|
||||
@@ -903,12 +914,9 @@ kvm_get_supported_cpuid_index(uint32_t function, uint32_t index)
|
||||
void vcpu_set_cpuid(struct kvm_vm *vm,
|
||||
uint32_t vcpuid, struct kvm_cpuid2 *cpuid)
|
||||
{
|
||||
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
|
||||
int rc;
|
||||
|
||||
TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid);
|
||||
|
||||
rc = ioctl(vcpu->fd, KVM_SET_CPUID2, cpuid);
|
||||
rc = __vcpu_set_cpuid(vm, vcpuid, cpuid);
|
||||
TEST_ASSERT(rc == 0, "KVM_SET_CPUID2 failed, rc: %i errno: %i",
|
||||
rc, errno);
|
||||
|
||||
@@ -1136,25 +1144,25 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0]));
|
||||
list->nmsrs = nmsrs;
|
||||
r = ioctl(vm->kvm_fd, KVM_GET_MSR_INDEX_LIST, list);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MSR_INDEX_LIST, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MSR_INDEX_LIST, r: %i",
|
||||
r);
|
||||
|
||||
state = malloc(sizeof(*state) + nmsrs * sizeof(state->msrs.entries[0]));
|
||||
r = ioctl(vcpu->fd, KVM_GET_VCPU_EVENTS, &state->events);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_VCPU_EVENTS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_VCPU_EVENTS, r: %i",
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_GET_MP_STATE, &state->mp_state);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MP_STATE, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MP_STATE, r: %i",
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_GET_REGS, &state->regs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_REGS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_REGS, r: %i",
|
||||
r);
|
||||
|
||||
r = vcpu_save_xsave_state(vm, vcpu, state);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XSAVE, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XSAVE, r: %i",
|
||||
r);
|
||||
|
||||
if (kvm_check_cap(KVM_CAP_XCRS)) {
|
||||
r = ioctl(vcpu->fd, KVM_GET_XCRS, &state->xcrs);
|
||||
@@ -1163,17 +1171,17 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
}
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_GET_SREGS, &state->sregs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_SREGS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_SREGS, r: %i",
|
||||
r);
|
||||
|
||||
if (nested_size) {
|
||||
state->nested.size = sizeof(state->nested_);
|
||||
r = ioctl(vcpu->fd, KVM_GET_NESTED_STATE, &state->nested);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_NESTED_STATE, r: %i",
|
||||
r);
|
||||
r);
|
||||
TEST_ASSERT(state->nested.size <= nested_size,
|
||||
"Nested state size too big, %i (KVM_CHECK_CAP gave %i)",
|
||||
state->nested.size, nested_size);
|
||||
"Nested state size too big, %i (KVM_CHECK_CAP gave %i)",
|
||||
state->nested.size, nested_size);
|
||||
} else
|
||||
state->nested.size = 0;
|
||||
|
||||
@@ -1181,12 +1189,12 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
for (i = 0; i < nmsrs; i++)
|
||||
state->msrs.entries[i].index = list->indices[i];
|
||||
r = ioctl(vcpu->fd, KVM_GET_MSRS, &state->msrs);
|
||||
TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed MSR was 0x%x)",
|
||||
r, r == nmsrs ? -1 : list->indices[r]);
|
||||
TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed MSR was 0x%x)",
|
||||
r, r == nmsrs ? -1 : list->indices[r]);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_GET_DEBUGREGS, &state->debugregs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_DEBUGREGS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_DEBUGREGS, r: %i",
|
||||
r);
|
||||
|
||||
free(list);
|
||||
return state;
|
||||
@@ -1199,7 +1207,7 @@ void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *s
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_SREGS, &state->sregs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_SREGS, r: %i",
|
||||
r);
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_MSRS, &state->msrs);
|
||||
TEST_ASSERT(r == state->msrs.nmsrs,
|
||||
@@ -1214,28 +1222,28 @@ void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *s
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_XSAVE, state->xsave);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XSAVE, r: %i",
|
||||
r);
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_VCPU_EVENTS, &state->events);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_VCPU_EVENTS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_VCPU_EVENTS, r: %i",
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_MP_STATE, &state->mp_state);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_MP_STATE, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_MP_STATE, r: %i",
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_DEBUGREGS, &state->debugregs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_DEBUGREGS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_DEBUGREGS, r: %i",
|
||||
r);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_SET_REGS, &state->regs);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_REGS, r: %i",
|
||||
r);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_REGS, r: %i",
|
||||
r);
|
||||
|
||||
if (state->nested.size) {
|
||||
r = ioctl(vcpu->fd, KVM_SET_NESTED_STATE, &state->nested);
|
||||
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_NESTED_STATE, r: %i",
|
||||
r);
|
||||
r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,10 +1253,10 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state)
|
||||
free(state);
|
||||
}
|
||||
|
||||
bool is_intel_cpu(void)
|
||||
static bool cpu_vendor_string_is(const char *vendor)
|
||||
{
|
||||
const uint32_t *chunk = (const uint32_t *)vendor;
|
||||
int eax, ebx, ecx, edx;
|
||||
const uint32_t *chunk;
|
||||
const int leaf = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
@@ -1257,10 +1265,22 @@ bool is_intel_cpu(void)
|
||||
"=c"(ecx), "=d"(edx)
|
||||
: /* input */ "0"(leaf), "2"(0));
|
||||
|
||||
chunk = (const uint32_t *)("GenuineIntel");
|
||||
return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
|
||||
}
|
||||
|
||||
bool is_intel_cpu(void)
|
||||
{
|
||||
return cpu_vendor_string_is("GenuineIntel");
|
||||
}
|
||||
|
||||
/*
|
||||
* Exclude early K5 samples with a vendor string of "AMDisbetter!"
|
||||
*/
|
||||
bool is_amd_cpu(void)
|
||||
{
|
||||
return cpu_vendor_string_is("AuthenticAMD");
|
||||
}
|
||||
|
||||
uint32_t kvm_get_cpuid_max_basic(void)
|
||||
{
|
||||
return kvm_get_supported_cpuid_entry(0)->eax;
|
||||
@@ -1384,6 +1404,23 @@ void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
}
|
||||
}
|
||||
|
||||
struct kvm_cpuid_entry2 *get_cpuid(struct kvm_cpuid2 *cpuid, uint32_t function,
|
||||
uint32_t index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cpuid->nent; i++) {
|
||||
struct kvm_cpuid_entry2 *cur = &cpuid->entries[i];
|
||||
|
||||
if (cur->function == function && cur->index == index)
|
||||
return cur;
|
||||
}
|
||||
|
||||
TEST_FAIL("CPUID function 0x%x index 0x%x not found ", function, index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool set_cpuid(struct kvm_cpuid2 *cpuid,
|
||||
struct kvm_cpuid_entry2 *ent)
|
||||
{
|
||||
@@ -1479,22 +1516,6 @@ struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vm *vm, uint32_t vcpui
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx 0x68747541
|
||||
#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx 0x444d4163
|
||||
#define X86EMUL_CPUID_VENDOR_AuthenticAMD_edx 0x69746e65
|
||||
|
||||
static inline unsigned x86_family(unsigned int eax)
|
||||
{
|
||||
unsigned int x86;
|
||||
|
||||
x86 = (eax >> 8) & 0xf;
|
||||
|
||||
if (x86 == 0xf)
|
||||
x86 += (eax >> 20) & 0xff;
|
||||
|
||||
return x86;
|
||||
}
|
||||
|
||||
unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
|
||||
{
|
||||
const unsigned long num_ht_pages = 12 << (30 - vm->page_shift); /* 12 GiB */
|
||||
@@ -1504,11 +1525,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
|
||||
max_gfn = (1ULL << (vm->pa_bits - vm->page_shift)) - 1;
|
||||
|
||||
/* Avoid reserved HyperTransport region on AMD processors. */
|
||||
eax = ecx = 0;
|
||||
cpuid(&eax, &ebx, &ecx, &edx);
|
||||
if (ebx != X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx ||
|
||||
ecx != X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx ||
|
||||
edx != X86EMUL_CPUID_VENDOR_AuthenticAMD_edx)
|
||||
if (!is_amd_cpu())
|
||||
return max_gfn;
|
||||
|
||||
/* On parts with <40 physical address bits, the area is fully hidden */
|
||||
@@ -1518,6 +1535,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
|
||||
/* Before family 17h, the HyperTransport area is just below 1T. */
|
||||
ht_gfn = (1 << 28) - num_ht_pages;
|
||||
eax = 1;
|
||||
ecx = 0;
|
||||
cpuid(&eax, &ebx, &ecx, &edx);
|
||||
if (x86_family(eax) < 0x17)
|
||||
goto done;
|
||||
|
||||
@@ -154,6 +154,34 @@ struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct
|
||||
return guest_cpuids;
|
||||
}
|
||||
|
||||
static void set_cpuid_after_run(struct kvm_vm *vm, struct kvm_cpuid2 *cpuid)
|
||||
{
|
||||
struct kvm_cpuid_entry2 *ent;
|
||||
int rc;
|
||||
u32 eax, ebx, x;
|
||||
|
||||
/* Setting unmodified CPUID is allowed */
|
||||
rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid);
|
||||
TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
|
||||
|
||||
/* Changing CPU features is forbidden */
|
||||
ent = get_cpuid(cpuid, 0x7, 0);
|
||||
ebx = ent->ebx;
|
||||
ent->ebx--;
|
||||
rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid);
|
||||
TEST_ASSERT(rc, "Changing CPU features should fail");
|
||||
ent->ebx = ebx;
|
||||
|
||||
/* Changing MAXPHYADDR is forbidden */
|
||||
ent = get_cpuid(cpuid, 0x80000008, 0);
|
||||
eax = ent->eax;
|
||||
x = eax & 0xff;
|
||||
ent->eax = (eax & ~0xffu) | (x - 1);
|
||||
rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid);
|
||||
TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
|
||||
ent->eax = eax;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_cpuid2 *supp_cpuid, *cpuid2;
|
||||
@@ -175,5 +203,7 @@ int main(void)
|
||||
for (stage = 0; stage < 3; stage++)
|
||||
run_vcpu(vm, VCPU_ID, stage);
|
||||
|
||||
set_cpuid_after_run(vm, cpuid2);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
434
tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
Normal file
434
tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
Normal file
@@ -0,0 +1,434 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Test for x86 KVM_SET_PMU_EVENT_FILTER.
|
||||
*
|
||||
* Copyright (C) 2022, Google LLC.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2.
|
||||
*
|
||||
* Verifies the expected behavior of allow lists and deny lists for
|
||||
* virtual PMU events.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for program_invocation_short_name */
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
/*
|
||||
* In lieu of copying perf_event.h into tools...
|
||||
*/
|
||||
#define ARCH_PERFMON_EVENTSEL_OS (1ULL << 17)
|
||||
#define ARCH_PERFMON_EVENTSEL_ENABLE (1ULL << 22)
|
||||
|
||||
union cpuid10_eax {
|
||||
struct {
|
||||
unsigned int version_id:8;
|
||||
unsigned int num_counters:8;
|
||||
unsigned int bit_width:8;
|
||||
unsigned int mask_length:8;
|
||||
} split;
|
||||
unsigned int full;
|
||||
};
|
||||
|
||||
union cpuid10_ebx {
|
||||
struct {
|
||||
unsigned int no_unhalted_core_cycles:1;
|
||||
unsigned int no_instructions_retired:1;
|
||||
unsigned int no_unhalted_reference_cycles:1;
|
||||
unsigned int no_llc_reference:1;
|
||||
unsigned int no_llc_misses:1;
|
||||
unsigned int no_branch_instruction_retired:1;
|
||||
unsigned int no_branch_misses_retired:1;
|
||||
} split;
|
||||
unsigned int full;
|
||||
};
|
||||
|
||||
/* End of stuff taken from perf_event.h. */
|
||||
|
||||
/* Oddly, this isn't in perf_event.h. */
|
||||
#define ARCH_PERFMON_BRANCHES_RETIRED 5
|
||||
|
||||
#define VCPU_ID 0
|
||||
#define NUM_BRANCHES 42
|
||||
|
||||
/*
|
||||
* This is how the event selector and unit mask are stored in an AMD
|
||||
* core performance event-select register. Intel's format is similar,
|
||||
* but the event selector is only 8 bits.
|
||||
*/
|
||||
#define EVENT(select, umask) ((select & 0xf00UL) << 24 | (select & 0xff) | \
|
||||
(umask & 0xff) << 8)
|
||||
|
||||
/*
|
||||
* "Branch instructions retired", from the Intel SDM, volume 3,
|
||||
* "Pre-defined Architectural Performance Events."
|
||||
*/
|
||||
|
||||
#define INTEL_BR_RETIRED EVENT(0xc4, 0)
|
||||
|
||||
/*
|
||||
* "Retired branch instructions", from Processor Programming Reference
|
||||
* (PPR) for AMD Family 17h Model 01h, Revision B1 Processors,
|
||||
* Preliminary Processor Programming Reference (PPR) for AMD Family
|
||||
* 17h Model 31h, Revision B0 Processors, and Preliminary Processor
|
||||
* Programming Reference (PPR) for AMD Family 19h Model 01h, Revision
|
||||
* B1 Processors Volume 1 of 2.
|
||||
*/
|
||||
|
||||
#define AMD_ZEN_BR_RETIRED EVENT(0xc2, 0)
|
||||
|
||||
/*
|
||||
* This event list comprises Intel's eight architectural events plus
|
||||
* AMD's "retired branch instructions" for Zen[123] (and possibly
|
||||
* other AMD CPUs).
|
||||
*/
|
||||
static const uint64_t event_list[] = {
|
||||
EVENT(0x3c, 0),
|
||||
EVENT(0xc0, 0),
|
||||
EVENT(0x3c, 1),
|
||||
EVENT(0x2e, 0x4f),
|
||||
EVENT(0x2e, 0x41),
|
||||
EVENT(0xc4, 0),
|
||||
EVENT(0xc5, 0),
|
||||
EVENT(0xa4, 1),
|
||||
AMD_ZEN_BR_RETIRED,
|
||||
};
|
||||
|
||||
/*
|
||||
* If we encounter a #GP during the guest PMU sanity check, then the guest
|
||||
* PMU is not functional. Inform the hypervisor via GUEST_SYNC(0).
|
||||
*/
|
||||
static void guest_gp_handler(struct ex_regs *regs)
|
||||
{
|
||||
GUEST_SYNC(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that we can write a new value to the given MSR and read it back.
|
||||
* The caller should provide a non-empty set of bits that are safe to flip.
|
||||
*
|
||||
* Return on success. GUEST_SYNC(0) on error.
|
||||
*/
|
||||
static void check_msr(uint32_t msr, uint64_t bits_to_flip)
|
||||
{
|
||||
uint64_t v = rdmsr(msr) ^ bits_to_flip;
|
||||
|
||||
wrmsr(msr, v);
|
||||
if (rdmsr(msr) != v)
|
||||
GUEST_SYNC(0);
|
||||
|
||||
v ^= bits_to_flip;
|
||||
wrmsr(msr, v);
|
||||
if (rdmsr(msr) != v)
|
||||
GUEST_SYNC(0);
|
||||
}
|
||||
|
||||
static void intel_guest_code(void)
|
||||
{
|
||||
check_msr(MSR_CORE_PERF_GLOBAL_CTRL, 1);
|
||||
check_msr(MSR_P6_EVNTSEL0, 0xffff);
|
||||
check_msr(MSR_IA32_PMC0, 0xffff);
|
||||
GUEST_SYNC(1);
|
||||
|
||||
for (;;) {
|
||||
uint64_t br0, br1;
|
||||
|
||||
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
||||
wrmsr(MSR_P6_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
|
||||
ARCH_PERFMON_EVENTSEL_OS | INTEL_BR_RETIRED);
|
||||
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 1);
|
||||
br0 = rdmsr(MSR_IA32_PMC0);
|
||||
__asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES}));
|
||||
br1 = rdmsr(MSR_IA32_PMC0);
|
||||
GUEST_SYNC(br1 - br0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* To avoid needing a check for CPUID.80000001:ECX.PerfCtrExtCore[bit 23],
|
||||
* this code uses the always-available, legacy K7 PMU MSRs, which alias to
|
||||
* the first four of the six extended core PMU MSRs.
|
||||
*/
|
||||
static void amd_guest_code(void)
|
||||
{
|
||||
check_msr(MSR_K7_EVNTSEL0, 0xffff);
|
||||
check_msr(MSR_K7_PERFCTR0, 0xffff);
|
||||
GUEST_SYNC(1);
|
||||
|
||||
for (;;) {
|
||||
uint64_t br0, br1;
|
||||
|
||||
wrmsr(MSR_K7_EVNTSEL0, 0);
|
||||
wrmsr(MSR_K7_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
|
||||
ARCH_PERFMON_EVENTSEL_OS | AMD_ZEN_BR_RETIRED);
|
||||
br0 = rdmsr(MSR_K7_PERFCTR0);
|
||||
__asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES}));
|
||||
br1 = rdmsr(MSR_K7_PERFCTR0);
|
||||
GUEST_SYNC(br1 - br0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the VM to the next GUEST_SYNC(value), and return the value passed
|
||||
* to the sync. Any other exit from the guest is fatal.
|
||||
*/
|
||||
static uint64_t run_vm_to_sync(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_run *run = vcpu_state(vm, VCPU_ID);
|
||||
struct ucall uc;
|
||||
|
||||
vcpu_run(vm, VCPU_ID);
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"Exit_reason other than KVM_EXIT_IO: %u (%s)\n",
|
||||
run->exit_reason,
|
||||
exit_reason_str(run->exit_reason));
|
||||
get_ucall(vm, VCPU_ID, &uc);
|
||||
TEST_ASSERT(uc.cmd == UCALL_SYNC,
|
||||
"Received ucall other than UCALL_SYNC: %lu", uc.cmd);
|
||||
return uc.args[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* In a nested environment or if the vPMU is disabled, the guest PMU
|
||||
* might not work as architected (accessing the PMU MSRs may raise
|
||||
* #GP, or writes could simply be discarded). In those situations,
|
||||
* there is no point in running these tests. The guest code will perform
|
||||
* a sanity check and then GUEST_SYNC(success). In the case of failure,
|
||||
* the behavior of the guest on resumption is undefined.
|
||||
*/
|
||||
static bool sanity_check_pmu(struct kvm_vm *vm)
|
||||
{
|
||||
bool success;
|
||||
|
||||
vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler);
|
||||
success = run_vm_to_sync(vm);
|
||||
vm_install_exception_handler(vm, GP_VECTOR, NULL);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static struct kvm_pmu_event_filter *make_pmu_event_filter(uint32_t nevents)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f;
|
||||
int size = sizeof(*f) + nevents * sizeof(f->events[0]);
|
||||
|
||||
f = malloc(size);
|
||||
TEST_ASSERT(f, "Out of memory");
|
||||
memset(f, 0, size);
|
||||
f->nevents = nevents;
|
||||
return f;
|
||||
}
|
||||
|
||||
static struct kvm_pmu_event_filter *event_filter(uint32_t action)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f;
|
||||
int i;
|
||||
|
||||
f = make_pmu_event_filter(ARRAY_SIZE(event_list));
|
||||
f->action = action;
|
||||
for (i = 0; i < ARRAY_SIZE(event_list); i++)
|
||||
f->events[i] = event_list[i];
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the first occurrence of 'event' (if any) from the filter's
|
||||
* event list.
|
||||
*/
|
||||
static struct kvm_pmu_event_filter *remove_event(struct kvm_pmu_event_filter *f,
|
||||
uint64_t event)
|
||||
{
|
||||
bool found = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < f->nevents; i++) {
|
||||
if (found)
|
||||
f->events[i - 1] = f->events[i];
|
||||
else
|
||||
found = f->events[i] == event;
|
||||
}
|
||||
if (found)
|
||||
f->nevents--;
|
||||
return f;
|
||||
}
|
||||
|
||||
static void test_without_filter(struct kvm_vm *vm)
|
||||
{
|
||||
uint64_t count = run_vm_to_sync(vm);
|
||||
|
||||
if (count != NUM_BRANCHES)
|
||||
pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
|
||||
__func__, count, NUM_BRANCHES);
|
||||
TEST_ASSERT(count, "Allowed PMU event is not counting");
|
||||
}
|
||||
|
||||
static uint64_t test_with_filter(struct kvm_vm *vm,
|
||||
struct kvm_pmu_event_filter *f)
|
||||
{
|
||||
vm_ioctl(vm, KVM_SET_PMU_EVENT_FILTER, (void *)f);
|
||||
return run_vm_to_sync(vm);
|
||||
}
|
||||
|
||||
static void test_member_deny_list(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY);
|
||||
uint64_t count = test_with_filter(vm, f);
|
||||
|
||||
free(f);
|
||||
if (count)
|
||||
pr_info("%s: Branch instructions retired = %lu (expected 0)\n",
|
||||
__func__, count);
|
||||
TEST_ASSERT(!count, "Disallowed PMU Event is counting");
|
||||
}
|
||||
|
||||
static void test_member_allow_list(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW);
|
||||
uint64_t count = test_with_filter(vm, f);
|
||||
|
||||
free(f);
|
||||
if (count != NUM_BRANCHES)
|
||||
pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
|
||||
__func__, count, NUM_BRANCHES);
|
||||
TEST_ASSERT(count, "Allowed PMU event is not counting");
|
||||
}
|
||||
|
||||
static void test_not_member_deny_list(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY);
|
||||
uint64_t count;
|
||||
|
||||
remove_event(f, INTEL_BR_RETIRED);
|
||||
remove_event(f, AMD_ZEN_BR_RETIRED);
|
||||
count = test_with_filter(vm, f);
|
||||
free(f);
|
||||
if (count != NUM_BRANCHES)
|
||||
pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
|
||||
__func__, count, NUM_BRANCHES);
|
||||
TEST_ASSERT(count, "Allowed PMU event is not counting");
|
||||
}
|
||||
|
||||
static void test_not_member_allow_list(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW);
|
||||
uint64_t count;
|
||||
|
||||
remove_event(f, INTEL_BR_RETIRED);
|
||||
remove_event(f, AMD_ZEN_BR_RETIRED);
|
||||
count = test_with_filter(vm, f);
|
||||
free(f);
|
||||
if (count)
|
||||
pr_info("%s: Branch instructions retired = %lu (expected 0)\n",
|
||||
__func__, count);
|
||||
TEST_ASSERT(!count, "Disallowed PMU Event is counting");
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a non-zero PMU version, at least one general-purpose
|
||||
* counter per logical processor, an EBX bit vector of length greater
|
||||
* than 5, and EBX[5] clear.
|
||||
*/
|
||||
static bool check_intel_pmu_leaf(struct kvm_cpuid_entry2 *entry)
|
||||
{
|
||||
union cpuid10_eax eax = { .full = entry->eax };
|
||||
union cpuid10_ebx ebx = { .full = entry->ebx };
|
||||
|
||||
return eax.split.version_id && eax.split.num_counters > 0 &&
|
||||
eax.split.mask_length > ARCH_PERFMON_BRANCHES_RETIRED &&
|
||||
!ebx.split.no_branch_instruction_retired;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that CPUID leaf 0xa is Intel-specific. This leaf should be
|
||||
* clear on AMD hardware.
|
||||
*/
|
||||
static bool use_intel_pmu(void)
|
||||
{
|
||||
struct kvm_cpuid_entry2 *entry;
|
||||
|
||||
entry = kvm_get_supported_cpuid_index(0xa, 0);
|
||||
return is_intel_cpu() && entry && check_intel_pmu_leaf(entry);
|
||||
}
|
||||
|
||||
static bool is_zen1(uint32_t eax)
|
||||
{
|
||||
return x86_family(eax) == 0x17 && x86_model(eax) <= 0x0f;
|
||||
}
|
||||
|
||||
static bool is_zen2(uint32_t eax)
|
||||
{
|
||||
return x86_family(eax) == 0x17 &&
|
||||
x86_model(eax) >= 0x30 && x86_model(eax) <= 0x3f;
|
||||
}
|
||||
|
||||
static bool is_zen3(uint32_t eax)
|
||||
{
|
||||
return x86_family(eax) == 0x19 && x86_model(eax) <= 0x0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determining AMD support for a PMU event requires consulting the AMD
|
||||
* PPR for the CPU or reference material derived therefrom. The AMD
|
||||
* test code herein has been verified to work on Zen1, Zen2, and Zen3.
|
||||
*
|
||||
* Feel free to add more AMD CPUs that are documented to support event
|
||||
* select 0xc2 umask 0 as "retired branch instructions."
|
||||
*/
|
||||
static bool use_amd_pmu(void)
|
||||
{
|
||||
struct kvm_cpuid_entry2 *entry;
|
||||
|
||||
entry = kvm_get_supported_cpuid_index(1, 0);
|
||||
return is_amd_cpu() && entry &&
|
||||
(is_zen1(entry->eax) ||
|
||||
is_zen2(entry->eax) ||
|
||||
is_zen3(entry->eax));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void (*guest_code)(void) = NULL;
|
||||
struct kvm_vm *vm;
|
||||
int r;
|
||||
|
||||
/* Tell stdout not to buffer its content */
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
r = kvm_check_cap(KVM_CAP_PMU_EVENT_FILTER);
|
||||
if (!r) {
|
||||
print_skip("KVM_CAP_PMU_EVENT_FILTER not supported");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
if (use_intel_pmu())
|
||||
guest_code = intel_guest_code;
|
||||
else if (use_amd_pmu())
|
||||
guest_code = amd_guest_code;
|
||||
|
||||
if (!guest_code) {
|
||||
print_skip("Don't know how to test this guest PMU");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
vm = vm_create_default(VCPU_ID, 0, guest_code);
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
vcpu_init_descriptor_tables(vm, VCPU_ID);
|
||||
|
||||
if (!sanity_check_pmu(vm)) {
|
||||
print_skip("Guest PMU is not functional");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
test_without_filter(vm);
|
||||
test_member_deny_list(vm);
|
||||
test_member_allow_list(vm);
|
||||
test_not_member_deny_list(vm);
|
||||
test_not_member_allow_list(vm);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -77,8 +77,8 @@ static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage)
|
||||
switch (get_ucall(vm, vcpuid, &uc)) {
|
||||
case UCALL_SYNC:
|
||||
TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
|
||||
uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx",
|
||||
stage + 1, (ulong)uc.args[1]);
|
||||
uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx",
|
||||
stage + 1, (ulong)uc.args[1]);
|
||||
return;
|
||||
case UCALL_DONE:
|
||||
return;
|
||||
|
||||
@@ -30,8 +30,8 @@ static struct kvm_vm *vm;
|
||||
static void l2_guest_code(void)
|
||||
{
|
||||
/* Exit to L0 */
|
||||
asm volatile("inb %%dx, %%al"
|
||||
: : [port] "d" (PORT_L0_EXIT) : "rax");
|
||||
asm volatile("inb %%dx, %%al"
|
||||
: : [port] "d" (PORT_L0_EXIT) : "rax");
|
||||
}
|
||||
|
||||
static void l1_guest_code(struct vmx_pages *vmx_pages)
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "kselftest.h"
|
||||
|
||||
#define VCPU_ID 0
|
||||
|
||||
static struct kvm_vm *vm;
|
||||
|
||||
static void guest_ud_handler(struct ex_regs *regs)
|
||||
{
|
||||
/* Loop on the ud2 until guest state is made invalid. */
|
||||
}
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
static void __run_vcpu_with_invalid_state(void)
|
||||
{
|
||||
struct kvm_run *run = vcpu_state(vm, VCPU_ID);
|
||||
|
||||
vcpu_run(vm, VCPU_ID);
|
||||
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
|
||||
"Expected KVM_EXIT_INTERNAL_ERROR, got %d (%s)\n",
|
||||
run->exit_reason, exit_reason_str(run->exit_reason));
|
||||
TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
|
||||
"Expected emulation failure, got %d\n",
|
||||
run->emulation_failure.suberror);
|
||||
}
|
||||
|
||||
static void run_vcpu_with_invalid_state(void)
|
||||
{
|
||||
/*
|
||||
* Always run twice to verify KVM handles the case where _KVM_ queues
|
||||
* an exception with invalid state and then exits to userspace, i.e.
|
||||
* that KVM doesn't explode if userspace ignores the initial error.
|
||||
*/
|
||||
__run_vcpu_with_invalid_state();
|
||||
__run_vcpu_with_invalid_state();
|
||||
}
|
||||
|
||||
static void set_timer(void)
|
||||
{
|
||||
struct itimerval timer;
|
||||
|
||||
timer.it_value.tv_sec = 0;
|
||||
timer.it_value.tv_usec = 200;
|
||||
timer.it_interval = timer.it_value;
|
||||
ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0);
|
||||
}
|
||||
|
||||
static void set_or_clear_invalid_guest_state(bool set)
|
||||
{
|
||||
static struct kvm_sregs sregs;
|
||||
|
||||
if (!sregs.cr0)
|
||||
vcpu_sregs_get(vm, VCPU_ID, &sregs);
|
||||
sregs.tr.unusable = !!set;
|
||||
vcpu_sregs_set(vm, VCPU_ID, &sregs);
|
||||
}
|
||||
|
||||
static void set_invalid_guest_state(void)
|
||||
{
|
||||
set_or_clear_invalid_guest_state(true);
|
||||
}
|
||||
|
||||
static void clear_invalid_guest_state(void)
|
||||
{
|
||||
set_or_clear_invalid_guest_state(false);
|
||||
}
|
||||
|
||||
static void sigalrm_handler(int sig)
|
||||
{
|
||||
struct kvm_vcpu_events events;
|
||||
|
||||
TEST_ASSERT(sig == SIGALRM, "Unexpected signal = %d", sig);
|
||||
|
||||
vcpu_events_get(vm, VCPU_ID, &events);
|
||||
|
||||
/*
|
||||
* If an exception is pending, attempt KVM_RUN with invalid guest,
|
||||
* otherwise rearm the timer and keep doing so until the timer fires
|
||||
* between KVM queueing an exception and re-entering the guest.
|
||||
*/
|
||||
if (events.exception.pending) {
|
||||
set_invalid_guest_state();
|
||||
run_vcpu_with_invalid_state();
|
||||
} else {
|
||||
set_timer();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (!is_intel_cpu() || vm_is_unrestricted_guest(NULL)) {
|
||||
print_skip("Must be run with kvm_intel.unrestricted_guest=0");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
vm = vm_create_default(VCPU_ID, 0, (void *)guest_code);
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
vcpu_init_descriptor_tables(vm, VCPU_ID);
|
||||
|
||||
vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler);
|
||||
|
||||
/*
|
||||
* Stuff invalid guest state for L2 by making TR unusuable. The next
|
||||
* KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support
|
||||
* emulating invalid guest state for L2.
|
||||
*/
|
||||
set_invalid_guest_state();
|
||||
run_vcpu_with_invalid_state();
|
||||
|
||||
/*
|
||||
* Verify KVM also handles the case where userspace gains control while
|
||||
* an exception is pending and stuffs invalid state. Run with valid
|
||||
* guest state and a timer firing every 200us, and attempt to enter the
|
||||
* guest with invalid state when the handler interrupts KVM with an
|
||||
* exception pending.
|
||||
*/
|
||||
clear_invalid_guest_state();
|
||||
TEST_ASSERT(signal(SIGALRM, sigalrm_handler) != SIG_ERR,
|
||||
"Failed to register SIGALRM handler, errno = %d (%s)",
|
||||
errno, strerror(errno));
|
||||
|
||||
set_timer();
|
||||
run_vcpu_with_invalid_state();
|
||||
}
|
||||
@@ -46,20 +46,20 @@ static struct kvm_vm *vm;
|
||||
#define MIN_STEAL_TIME 50000
|
||||
|
||||
struct pvclock_vcpu_time_info {
|
||||
u32 version;
|
||||
u32 pad0;
|
||||
u64 tsc_timestamp;
|
||||
u64 system_time;
|
||||
u32 tsc_to_system_mul;
|
||||
s8 tsc_shift;
|
||||
u8 flags;
|
||||
u8 pad[2];
|
||||
u32 version;
|
||||
u32 pad0;
|
||||
u64 tsc_timestamp;
|
||||
u64 system_time;
|
||||
u32 tsc_to_system_mul;
|
||||
s8 tsc_shift;
|
||||
u8 flags;
|
||||
u8 pad[2];
|
||||
} __attribute__((__packed__)); /* 32 bytes */
|
||||
|
||||
struct pvclock_wall_clock {
|
||||
u32 version;
|
||||
u32 sec;
|
||||
u32 nsec;
|
||||
u32 version;
|
||||
u32 sec;
|
||||
u32 nsec;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct vcpu_runstate_info {
|
||||
@@ -74,11 +74,11 @@ struct arch_vcpu_info {
|
||||
};
|
||||
|
||||
struct vcpu_info {
|
||||
uint8_t evtchn_upcall_pending;
|
||||
uint8_t evtchn_upcall_mask;
|
||||
unsigned long evtchn_pending_sel;
|
||||
struct arch_vcpu_info arch;
|
||||
struct pvclock_vcpu_time_info time;
|
||||
uint8_t evtchn_upcall_pending;
|
||||
uint8_t evtchn_upcall_mask;
|
||||
unsigned long evtchn_pending_sel;
|
||||
struct arch_vcpu_info arch;
|
||||
struct pvclock_vcpu_time_info time;
|
||||
}; /* 64 bytes (x86) */
|
||||
|
||||
struct shared_info {
|
||||
@@ -493,7 +493,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
vm_ts.tv_sec = wc->sec;
|
||||
vm_ts.tv_nsec = wc->nsec;
|
||||
TEST_ASSERT(wc->version && !(wc->version & 1),
|
||||
TEST_ASSERT(wc->version && !(wc->version & 1),
|
||||
"Bad wallclock version %x", wc->version);
|
||||
TEST_ASSERT(cmp_timespec(&min_ts, &vm_ts) <= 0, "VM time too old");
|
||||
TEST_ASSERT(cmp_timespec(&max_ts, &vm_ts) >= 0, "VM time too new");
|
||||
|
||||
Reference in New Issue
Block a user