forked from luck/tmp_suning_uos_patched
tests: kvm: Check for a kernel warning
When running with /sys/module/kvm_intel/parameters/unrestricted_guest=N,
test that a kernel warning does not occur informing us that
vcpu->mmio_needed=1. This can happen when KVM_RUN is called after a
triple fault.
This test was made to detect a bug that was reported by Syzkaller
(https://groups.google.com/forum/#!topic/syzkaller/lHfau8E3SOE) and
fixed with commit bbeac2830f
("KVM: X86: Fix residual mmio emulation
request to userspace").
Signed-off-by: Aaron Lewis <aaronlewis@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Peter Shier <pshier@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
61cfcd545e
commit
9dba988edb
1
tools/testing/selftests/kvm/.gitignore
vendored
1
tools/testing/selftests/kvm/.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
/x86_64/evmcs_test
|
||||
/x86_64/hyperv_cpuid
|
||||
/x86_64/kvm_create_max_vcpus
|
||||
/x86_64/mmio_warning_test
|
||||
/x86_64/platform_info_test
|
||||
/x86_64/set_sregs_test
|
||||
/x86_64/smm_test
|
||||
|
|
|
@ -15,6 +15,7 @@ TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
|
|||
TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/smm_test
|
||||
|
|
|
@ -139,6 +139,8 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_size,
|
|||
void *guest_code);
|
||||
void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code);
|
||||
|
||||
bool vm_is_unrestricted_guest(struct kvm_vm *vm);
|
||||
|
||||
struct kvm_userspace_memory_region *
|
||||
kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
|
||||
uint64_t end);
|
||||
|
|
|
@ -303,6 +303,8 @@ static inline unsigned long get_xmm(int n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool is_intel_cpu(void);
|
||||
|
||||
struct kvm_x86_state;
|
||||
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,
|
||||
|
|
|
@ -1583,3 +1583,39 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva)
|
|||
{
|
||||
return addr_gpa2hva(vm, addr_gva2gpa(vm, gva));
|
||||
}
|
||||
|
||||
/*
|
||||
* Is Unrestricted Guest
|
||||
*
|
||||
* Input Args:
|
||||
* vm - Virtual Machine
|
||||
*
|
||||
* Output Args: None
|
||||
*
|
||||
* Return: True if the unrestricted guest is set to 'Y', otherwise return false.
|
||||
*
|
||||
* Check if the unrestricted guest flag is enabled.
|
||||
*/
|
||||
bool vm_is_unrestricted_guest(struct kvm_vm *vm)
|
||||
{
|
||||
char val = 'N';
|
||||
size_t count;
|
||||
FILE *f;
|
||||
|
||||
if (vm == NULL) {
|
||||
/* Ensure that the KVM vendor-specific module is loaded. */
|
||||
f = fopen(KVM_DEV_PATH, "r");
|
||||
TEST_ASSERT(f != NULL, "Error in opening KVM dev file: %d",
|
||||
errno);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
f = fopen("/sys/module/kvm_intel/parameters/unrestricted_guest", "r");
|
||||
if (f) {
|
||||
count = fread(&val, sizeof(char), 1, f);
|
||||
TEST_ASSERT(count == 1, "Unable to read from param file.");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return val == 'Y';
|
||||
}
|
||||
|
|
|
@ -1137,3 +1137,19 @@ void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *s
|
|||
r);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_intel_cpu(void)
|
||||
{
|
||||
int eax, ebx, ecx, edx;
|
||||
const uint32_t *chunk;
|
||||
const int leaf = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"cpuid"
|
||||
: /* output */ "=a"(eax), "=b"(ebx),
|
||||
"=c"(ecx), "=d"(edx)
|
||||
: /* input */ "0"(leaf), "2"(0));
|
||||
|
||||
chunk = (const uint32_t *)("GenuineIntel");
|
||||
return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
|
||||
}
|
||||
|
|
126
tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
Normal file
126
tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* mmio_warning_test
|
||||
*
|
||||
* Copyright (C) 2019, Google LLC.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2.
|
||||
*
|
||||
* Test that we don't get a kernel warning when we call KVM_RUN after a
|
||||
* triple fault occurs. To get the triple fault to occur we call KVM_RUN
|
||||
* on a VCPU that hasn't been properly setup.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <kvm_util.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <processor.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <test_util.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NTHREAD 4
|
||||
#define NPROCESS 5
|
||||
|
||||
struct thread_context {
|
||||
int kvmcpu;
|
||||
struct kvm_run *run;
|
||||
};
|
||||
|
||||
void *thr(void *arg)
|
||||
{
|
||||
struct thread_context *tc = (struct thread_context *)arg;
|
||||
int res;
|
||||
int kvmcpu = tc->kvmcpu;
|
||||
struct kvm_run *run = tc->run;
|
||||
|
||||
res = ioctl(kvmcpu, KVM_RUN, 0);
|
||||
printf("ret1=%d exit_reason=%d suberror=%d\n",
|
||||
res, run->exit_reason, run->internal.suberror);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test(void)
|
||||
{
|
||||
int i, kvm, kvmvm, kvmcpu;
|
||||
pthread_t th[NTHREAD];
|
||||
struct kvm_run *run;
|
||||
struct thread_context tc;
|
||||
|
||||
kvm = open("/dev/kvm", O_RDWR);
|
||||
TEST_ASSERT(kvm != -1, "failed to open /dev/kvm");
|
||||
kvmvm = ioctl(kvm, KVM_CREATE_VM, 0);
|
||||
TEST_ASSERT(kvmvm != -1, "KVM_CREATE_VM failed");
|
||||
kvmcpu = ioctl(kvmvm, KVM_CREATE_VCPU, 0);
|
||||
TEST_ASSERT(kvmcpu != -1, "KVM_CREATE_VCPU failed");
|
||||
run = (struct kvm_run *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED,
|
||||
kvmcpu, 0);
|
||||
tc.kvmcpu = kvmcpu;
|
||||
tc.run = run;
|
||||
srand(getpid());
|
||||
for (i = 0; i < NTHREAD; i++) {
|
||||
pthread_create(&th[i], NULL, thr, (void *)(uintptr_t)&tc);
|
||||
usleep(rand() % 10000);
|
||||
}
|
||||
for (i = 0; i < NTHREAD; i++)
|
||||
pthread_join(th[i], NULL);
|
||||
}
|
||||
|
||||
int get_warnings_count(void)
|
||||
{
|
||||
int warnings;
|
||||
FILE *f;
|
||||
|
||||
f = popen("dmesg | grep \"WARNING:\" | wc -l", "r");
|
||||
fscanf(f, "%d", &warnings);
|
||||
fclose(f);
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int warnings_before, warnings_after;
|
||||
|
||||
if (!is_intel_cpu()) {
|
||||
printf("Must be run on an Intel CPU, skipping test\n");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
if (vm_is_unrestricted_guest(NULL)) {
|
||||
printf("Unrestricted guest must be disabled, skipping test\n");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
warnings_before = get_warnings_count();
|
||||
|
||||
for (int i = 0; i < NPROCESS; ++i) {
|
||||
int status;
|
||||
int pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
exit(1);
|
||||
if (pid == 0) {
|
||||
test();
|
||||
exit(0);
|
||||
}
|
||||
while (waitpid(pid, &status, __WALL) != pid)
|
||||
;
|
||||
}
|
||||
|
||||
warnings_after = get_warnings_count();
|
||||
TEST_ASSERT(warnings_before == warnings_after,
|
||||
"Warnings found in kernel. Run 'dmesg' to inspect them.");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user