diff mbox series

tests: kvm: Check for a kernel warning

Message ID 20190531141452.158909-1-aaronlewis@google.com (mailing list archive)
State New, archived
Headers show
Series tests: kvm: Check for a kernel warning | expand

Commit Message

Aaron Lewis May 31, 2019, 2:14 p.m. UTC
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 bbeac2830f4de ("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>
---
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../testing/selftests/kvm/include/kvm_util.h  |   2 +
 .../selftests/kvm/include/x86_64/processor.h  |   2 +
 tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
 .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
 .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
 7 files changed, 184 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c

Comments

Aaron Lewis June 18, 2019, 2:13 p.m. UTC | #1
On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
>
> 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 bbeac2830f4de ("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>
> ---
>  tools/testing/selftests/kvm/.gitignore        |   1 +
>  tools/testing/selftests/kvm/Makefile          |   1 +
>  .../testing/selftests/kvm/include/kvm_util.h  |   2 +
>  .../selftests/kvm/include/x86_64/processor.h  |   2 +
>  tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
>  .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
>  .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
>  7 files changed, 184 insertions(+)
>  create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>
> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index df1bf9230a74..41266af0d3dc 100644
> --- a/tools/testing/selftests/kvm/.gitignore
> +++ b/tools/testing/selftests/kvm/.gitignore
> @@ -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
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 79c524395ebe..670b938f1049 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>  TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>  TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
>  TEST_GEN_PROGS_x86_64 += dirty_log_test
>  TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
>
> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
> index 8c6b9619797d..c5c427c86598 100644
> --- a/tools/testing/selftests/kvm/include/kvm_util.h
> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
> @@ -137,6 +137,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);
> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
> index 6063d5b2f356..af4d26de32d1 100644
> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
> @@ -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,
> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
> index e9113857f44e..b93b09ad9a11 100644
> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
> @@ -1584,3 +1584,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';
> +}
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> index dc7fae9fa424..bcc0e70e1856 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> @@ -1139,3 +1139,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]);
> +}
> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> new file mode 100644
> index 000000000000..00bb97d76000
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> @@ -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;
> +}
> --
> 2.22.0.rc1.311.g5d7573a151-goog
>

ping
Krish Sadhukhan June 18, 2019, 7:38 p.m. UTC | #2
On 06/18/2019 07:13 AM, Aaron Lewis wrote:
> On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
>> 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 bbeac2830f4de ("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>
>> ---
>>   tools/testing/selftests/kvm/.gitignore        |   1 +
>>   tools/testing/selftests/kvm/Makefile          |   1 +
>>   .../testing/selftests/kvm/include/kvm_util.h  |   2 +
>>   .../selftests/kvm/include/x86_64/processor.h  |   2 +
>>   tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
>>   .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
>>   .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
>>   7 files changed, 184 insertions(+)
>>   create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>
>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>> index df1bf9230a74..41266af0d3dc 100644
>> --- a/tools/testing/selftests/kvm/.gitignore
>> +++ b/tools/testing/selftests/kvm/.gitignore
>> @@ -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
>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>> index 79c524395ebe..670b938f1049 100644
>> --- a/tools/testing/selftests/kvm/Makefile
>> +++ b/tools/testing/selftests/kvm/Makefile
>> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>>   TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>>   TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
>>   TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
>> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
>>   TEST_GEN_PROGS_x86_64 += dirty_log_test
>>   TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
>>
>> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
>> index 8c6b9619797d..c5c427c86598 100644
>> --- a/tools/testing/selftests/kvm/include/kvm_util.h
>> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
>> @@ -137,6 +137,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);
>> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
>> index 6063d5b2f356..af4d26de32d1 100644
>> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
>> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
>> @@ -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,
>> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
>> index e9113857f44e..b93b09ad9a11 100644
>> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
>> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
>> @@ -1584,3 +1584,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';
>> +}
>> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>> index dc7fae9fa424..bcc0e70e1856 100644
>> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
>> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>> @@ -1139,3 +1139,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]);
>> +}
>> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>> new file mode 100644
>> index 000000000000..00bb97d76000
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>> @@ -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();

Since you are grep'ing for the word "WARNING",  is there a possibility 
that the test can detect a false positive based on Warnings generated 
due to some other cause while it ran ?

>> +       TEST_ASSERT(warnings_before == warnings_after,
>> +                  "Warnings found in kernel.  Run 'dmesg' to inspect them.");
>> +
>> +       return 0;
>> +}
>> --
>> 2.22.0.rc1.311.g5d7573a151-goog
>>
> ping
Aaron Lewis June 20, 2019, 2:12 p.m. UTC | #3
On Tue, Jun 18, 2019 at 12:38 PM Krish Sadhukhan
<krish.sadhukhan@oracle.com> wrote:
>
>
>
> On 06/18/2019 07:13 AM, Aaron Lewis wrote:
> > On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
> >> 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 bbeac2830f4de ("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>
> >> ---
> >>   tools/testing/selftests/kvm/.gitignore        |   1 +
> >>   tools/testing/selftests/kvm/Makefile          |   1 +
> >>   .../testing/selftests/kvm/include/kvm_util.h  |   2 +
> >>   .../selftests/kvm/include/x86_64/processor.h  |   2 +
> >>   tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
> >>   .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
> >>   .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
> >>   7 files changed, 184 insertions(+)
> >>   create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >>
> >> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> >> index df1bf9230a74..41266af0d3dc 100644
> >> --- a/tools/testing/selftests/kvm/.gitignore
> >> +++ b/tools/testing/selftests/kvm/.gitignore
> >> @@ -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
> >> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> >> index 79c524395ebe..670b938f1049 100644
> >> --- a/tools/testing/selftests/kvm/Makefile
> >> +++ b/tools/testing/selftests/kvm/Makefile
> >> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
> >>   TEST_GEN_PROGS_x86_64 += x86_64/smm_test
> >>   TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
> >>   TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
> >> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
> >>   TEST_GEN_PROGS_x86_64 += dirty_log_test
> >>   TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
> >>
> >> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
> >> index 8c6b9619797d..c5c427c86598 100644
> >> --- a/tools/testing/selftests/kvm/include/kvm_util.h
> >> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
> >> @@ -137,6 +137,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);
> >> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
> >> index 6063d5b2f356..af4d26de32d1 100644
> >> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
> >> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
> >> @@ -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,
> >> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
> >> index e9113857f44e..b93b09ad9a11 100644
> >> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
> >> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
> >> @@ -1584,3 +1584,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';
> >> +}
> >> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >> index dc7fae9fa424..bcc0e70e1856 100644
> >> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >> @@ -1139,3 +1139,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]);
> >> +}
> >> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >> new file mode 100644
> >> index 000000000000..00bb97d76000
> >> --- /dev/null
> >> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >> @@ -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();
>
> Since you are grep'ing for the word "WARNING",  is there a possibility
> that the test can detect a false positive based on Warnings generated
> due to some other cause while it ran ?
>

Yes, this is a possibility, however, it is still a warning and should
still be dealt with.  We could special case the grep message to be
more specific to the case we are dealing with here, but I'd prefer to
keep it this way to alert on any warning.  That way other warnings,
should they occur, are brought to our attention.

> >> +       TEST_ASSERT(warnings_before == warnings_after,
> >> +                  "Warnings found in kernel.  Run 'dmesg' to inspect them.");
> >> +
> >> +       return 0;
> >> +}
> >> --
> >> 2.22.0.rc1.311.g5d7573a151-goog
> >>
> > ping
>
Krish Sadhukhan June 20, 2019, 6:15 p.m. UTC | #4
On 6/20/19 7:12 AM, Aaron Lewis wrote:
> On Tue, Jun 18, 2019 at 12:38 PM Krish Sadhukhan
> <krish.sadhukhan@oracle.com> wrote:
>>
>>
>> On 06/18/2019 07:13 AM, Aaron Lewis wrote:
>>> On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
>>>> 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 bbeac2830f4de ("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>
>>>> ---
>>>>    tools/testing/selftests/kvm/.gitignore        |   1 +
>>>>    tools/testing/selftests/kvm/Makefile          |   1 +
>>>>    .../testing/selftests/kvm/include/kvm_util.h  |   2 +
>>>>    .../selftests/kvm/include/x86_64/processor.h  |   2 +
>>>>    tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
>>>>    .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
>>>>    .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
>>>>    7 files changed, 184 insertions(+)
>>>>    create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>>
>>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>>>> index df1bf9230a74..41266af0d3dc 100644
>>>> --- a/tools/testing/selftests/kvm/.gitignore
>>>> +++ b/tools/testing/selftests/kvm/.gitignore
>>>> @@ -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
>>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>>>> index 79c524395ebe..670b938f1049 100644
>>>> --- a/tools/testing/selftests/kvm/Makefile
>>>> +++ b/tools/testing/selftests/kvm/Makefile
>>>> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>>>>    TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>>>>    TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
>>>>    TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
>>>> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
>>>>    TEST_GEN_PROGS_x86_64 += dirty_log_test
>>>>    TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
>>>>
>>>> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
>>>> index 8c6b9619797d..c5c427c86598 100644
>>>> --- a/tools/testing/selftests/kvm/include/kvm_util.h
>>>> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
>>>> @@ -137,6 +137,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);
>>>> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>> index 6063d5b2f356..af4d26de32d1 100644
>>>> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>> @@ -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,
>>>> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
>>>> index e9113857f44e..b93b09ad9a11 100644
>>>> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
>>>> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
>>>> @@ -1584,3 +1584,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';
>>>> +}
>>>> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>> index dc7fae9fa424..bcc0e70e1856 100644
>>>> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>> @@ -1139,3 +1139,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]);
>>>> +}
>>>> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>> new file mode 100644
>>>> index 000000000000..00bb97d76000
>>>> --- /dev/null
>>>> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>> @@ -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();
>> Since you are grep'ing for the word "WARNING",  is there a possibility
>> that the test can detect a false positive based on Warnings generated
>> due to some other cause while it ran ?
>>
> Yes, this is a possibility, however, it is still a warning and should
> still be dealt with.  We could special case the grep message to be
> more specific to the case we are dealing with here, but I'd prefer to
> keep it this way to alert on any warning.  That way other warnings,
> should they occur, are brought to our attention.


OK.  In that case, does it make sense to provide some additional info in 
the ASSERT message below ? dmesg can contain Warnings that may have 
occurred either before or after the run of this test and so we can at 
least link the false positives to this test.


>
>>>> +       TEST_ASSERT(warnings_before == warnings_after,
>>>> +                  "Warnings found in kernel.  Run 'dmesg' to inspect them.");
>>>> +
>>>> +       return 0;
>>>> +}
>>>> --
>>>> 2.22.0.rc1.311.g5d7573a151-goog
>>>>
>>> ping
Aaron Lewis June 20, 2019, 7:47 p.m. UTC | #5
On Thu, Jun 20, 2019 at 11:17 AM Krish Sadhukhan
<krish.sadhukhan@oracle.com> wrote:
>
>
> On 6/20/19 7:12 AM, Aaron Lewis wrote:
> > On Tue, Jun 18, 2019 at 12:38 PM Krish Sadhukhan
> > <krish.sadhukhan@oracle.com> wrote:
> >>
> >>
> >> On 06/18/2019 07:13 AM, Aaron Lewis wrote:
> >>> On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
> >>>> 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 bbeac2830f4de ("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>
> >>>> ---
> >>>>    tools/testing/selftests/kvm/.gitignore        |   1 +
> >>>>    tools/testing/selftests/kvm/Makefile          |   1 +
> >>>>    .../testing/selftests/kvm/include/kvm_util.h  |   2 +
> >>>>    .../selftests/kvm/include/x86_64/processor.h  |   2 +
> >>>>    tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
> >>>>    .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
> >>>>    .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
> >>>>    7 files changed, 184 insertions(+)
> >>>>    create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >>>>
> >>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> >>>> index df1bf9230a74..41266af0d3dc 100644
> >>>> --- a/tools/testing/selftests/kvm/.gitignore
> >>>> +++ b/tools/testing/selftests/kvm/.gitignore
> >>>> @@ -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
> >>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> >>>> index 79c524395ebe..670b938f1049 100644
> >>>> --- a/tools/testing/selftests/kvm/Makefile
> >>>> +++ b/tools/testing/selftests/kvm/Makefile
> >>>> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
> >>>>    TEST_GEN_PROGS_x86_64 += x86_64/smm_test
> >>>>    TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
> >>>>    TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
> >>>> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
> >>>>    TEST_GEN_PROGS_x86_64 += dirty_log_test
> >>>>    TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
> >>>>
> >>>> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
> >>>> index 8c6b9619797d..c5c427c86598 100644
> >>>> --- a/tools/testing/selftests/kvm/include/kvm_util.h
> >>>> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
> >>>> @@ -137,6 +137,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);
> >>>> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
> >>>> index 6063d5b2f356..af4d26de32d1 100644
> >>>> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
> >>>> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
> >>>> @@ -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,
> >>>> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
> >>>> index e9113857f44e..b93b09ad9a11 100644
> >>>> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
> >>>> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
> >>>> @@ -1584,3 +1584,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';
> >>>> +}
> >>>> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >>>> index dc7fae9fa424..bcc0e70e1856 100644
> >>>> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >>>> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> >>>> @@ -1139,3 +1139,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]);
> >>>> +}
> >>>> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >>>> new file mode 100644
> >>>> index 000000000000..00bb97d76000
> >>>> --- /dev/null
> >>>> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
> >>>> @@ -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();
> >> Since you are grep'ing for the word "WARNING",  is there a possibility
> >> that the test can detect a false positive based on Warnings generated
> >> due to some other cause while it ran ?
> >>
> > Yes, this is a possibility, however, it is still a warning and should
> > still be dealt with.  We could special case the grep message to be
> > more specific to the case we are dealing with here, but I'd prefer to
> > keep it this way to alert on any warning.  That way other warnings,
> > should they occur, are brought to our attention.
>
>
> OK.  In that case, does it make sense to provide some additional info in
> the ASSERT message below ? dmesg can contain Warnings that may have
> occurred either before or after the run of this test and so we can at
> least link the false positives to this test.
>

I can go either way on this.  While there can be false positives in
dmesg, if this asset fires there will be warnings worth looking at and
addressing as it only fires if it detects warnings occurred during the
test.  That's the reason I made the asset message general and only say
to look at warnings.  We could add a more long winded message to the
end of this informing the user that because the test doesn't clear
dmesg there could be warnings not related to the test.  However, there
will be warnings caused by running this test as well.

>
> >
> >>>> +       TEST_ASSERT(warnings_before == warnings_after,
> >>>> +                  "Warnings found in kernel.  Run 'dmesg' to inspect them.");
> >>>> +
> >>>> +       return 0;
> >>>> +}
> >>>> --
> >>>> 2.22.0.rc1.311.g5d7573a151-goog
> >>>>
> >>> ping
Krish Sadhukhan June 21, 2019, 5:13 p.m. UTC | #6
On 6/20/19 12:47 PM, Aaron Lewis wrote:
> On Thu, Jun 20, 2019 at 11:17 AM Krish Sadhukhan
> <krish.sadhukhan@oracle.com> wrote:
>>
>> On 6/20/19 7:12 AM, Aaron Lewis wrote:
>>> On Tue, Jun 18, 2019 at 12:38 PM Krish Sadhukhan
>>> <krish.sadhukhan@oracle.com> wrote:
>>>>
>>>> On 06/18/2019 07:13 AM, Aaron Lewis wrote:
>>>>> On Fri, May 31, 2019 at 7:14 AM Aaron Lewis <aaronlewis@google.com> wrote:
>>>>>> 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 bbeac2830f4de ("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>
>>>>>> ---
>>>>>>     tools/testing/selftests/kvm/.gitignore        |   1 +
>>>>>>     tools/testing/selftests/kvm/Makefile          |   1 +
>>>>>>     .../testing/selftests/kvm/include/kvm_util.h  |   2 +
>>>>>>     .../selftests/kvm/include/x86_64/processor.h  |   2 +
>>>>>>     tools/testing/selftests/kvm/lib/kvm_util.c    |  36 +++++
>>>>>>     .../selftests/kvm/lib/x86_64/processor.c      |  16 +++
>>>>>>     .../selftests/kvm/x86_64/mmio_warning_test.c  | 126 ++++++++++++++++++
>>>>>>     7 files changed, 184 insertions(+)
>>>>>>     create mode 100644 tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>>>>
>>>>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>>>>>> index df1bf9230a74..41266af0d3dc 100644
>>>>>> --- a/tools/testing/selftests/kvm/.gitignore
>>>>>> +++ b/tools/testing/selftests/kvm/.gitignore
>>>>>> @@ -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
>>>>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>>>>>> index 79c524395ebe..670b938f1049 100644
>>>>>> --- a/tools/testing/selftests/kvm/Makefile
>>>>>> +++ b/tools/testing/selftests/kvm/Makefile
>>>>>> @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>>>>>>     TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>>>>>>     TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
>>>>>>     TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
>>>>>> +TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
>>>>>>     TEST_GEN_PROGS_x86_64 += dirty_log_test
>>>>>>     TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
>>>>>>
>>>>>> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
>>>>>> index 8c6b9619797d..c5c427c86598 100644
>>>>>> --- a/tools/testing/selftests/kvm/include/kvm_util.h
>>>>>> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
>>>>>> @@ -137,6 +137,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);
>>>>>> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>>>> index 6063d5b2f356..af4d26de32d1 100644
>>>>>> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>>>> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
>>>>>> @@ -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,
>>>>>> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
>>>>>> index e9113857f44e..b93b09ad9a11 100644
>>>>>> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
>>>>>> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
>>>>>> @@ -1584,3 +1584,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';
>>>>>> +}
>>>>>> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>>>> index dc7fae9fa424..bcc0e70e1856 100644
>>>>>> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>>>> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
>>>>>> @@ -1139,3 +1139,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]);
>>>>>> +}
>>>>>> diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>>>> new file mode 100644
>>>>>> index 000000000000..00bb97d76000
>>>>>> --- /dev/null
>>>>>> +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
>>>>>> @@ -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();
>>>> Since you are grep'ing for the word "WARNING",  is there a possibility
>>>> that the test can detect a false positive based on Warnings generated
>>>> due to some other cause while it ran ?
>>>>
>>> Yes, this is a possibility, however, it is still a warning and should
>>> still be dealt with.  We could special case the grep message to be
>>> more specific to the case we are dealing with here, but I'd prefer to
>>> keep it this way to alert on any warning.  That way other warnings,
>>> should they occur, are brought to our attention.
>>
>> OK.  In that case, does it make sense to provide some additional info in
>> the ASSERT message below ? dmesg can contain Warnings that may have
>> occurred either before or after the run of this test and so we can at
>> least link the false positives to this test.
>>
> I can go either way on this.  While there can be false positives in
> dmesg, if this asset fires there will be warnings worth looking at and
> addressing as it only fires if it detects warnings occurred during the
> test.  That's the reason I made the asset message general and only say
> to look at warnings.  We could add a more long winded message to the
> end of this informing the user that because the test doesn't clear
> dmesg there could be warnings not related to the test.  However, there
> will be warnings caused by running this test as well.


Yes, a little more info will certainly help in debugging.


>
>>>>>> +       TEST_ASSERT(warnings_before == warnings_after,
>>>>>> +                  "Warnings found in kernel.  Run 'dmesg' to inspect them.");
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> --
>>>>>> 2.22.0.rc1.311.g5d7573a151-goog
>>>>>>
>>>>> ping
diff mbox series

Patch

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index df1bf9230a74..41266af0d3dc 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -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
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 79c524395ebe..670b938f1049 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -22,6 +22,7 @@  TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
 TEST_GEN_PROGS_x86_64 += x86_64/smm_test
 TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
 TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
+TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
 TEST_GEN_PROGS_x86_64 += dirty_log_test
 TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
 
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 8c6b9619797d..c5c427c86598 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -137,6 +137,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);
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index 6063d5b2f356..af4d26de32d1 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -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,
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index e9113857f44e..b93b09ad9a11 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1584,3 +1584,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';
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index dc7fae9fa424..bcc0e70e1856 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -1139,3 +1139,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]);
+}
diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
new file mode 100644
index 000000000000..00bb97d76000
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
@@ -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;
+}