diff options
author | Quentin Perret <qperret@google.com> | 2022-02-24 08:55:28 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-02-24 08:55:28 +0000 |
commit | 6fd12f5d04bd263abe67b426a1d5d476433deced (patch) | |
tree | da7a830d8aa594efdd4e3706a44fa5bf18dd2293 | |
parent | 0b74690243009f2719ad31168841e2580b1e1340 (diff) | |
parent | a9dc703806031c8535c84c9905979f86dfaec1d3 (diff) | |
download | linux-kselftest-6fd12f5d04bd263abe67b426a1d5d476433deced.tar.gz |
ANDROID: kvm: Test that pVM memory is wiped during teardown am: a9dc703806
Original change: https://android-review.googlesource.com/c/platform/external/linux-kselftest/+/1997250
Change-Id: I32419eca03c9ff32dd1c6e811f2ba98aab461625
-rw-r--r-- | Android.bp | 15 | ||||
-rw-r--r-- | android/kselftest_test_list.mk | 1 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c | 174 |
3 files changed, 190 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp index 0b9166c56315..08805a161450 100644 --- a/Android.bp +++ b/Android.bp @@ -340,6 +340,21 @@ cc_test { defaults: ["kselftest_defaults"], } +// KVM test +cc_test { + name: "kselftest_kvm_arm64_tests", + relative_install_path: "linux-kselftest/kvm/aarch64", + local_include_dirs: [ "tools/testing/selftests"], + enabled: false, + arch: { + arm64: { + enabled: true, + srcs: ["tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c"], + }, + }, + defaults: ["kselftest_defaults"], +} + // Lib test sh_test { name: "kselftest_lib_printf", diff --git a/android/kselftest_test_list.mk b/android/kselftest_test_list.mk index 07df6fc5d758..46b9b92c58fb 100644 --- a/android/kselftest_test_list.mk +++ b/android/kselftest_test_list.mk @@ -31,6 +31,7 @@ kselftest_modules += \ kselftest_intel_pstate_tests_aperf \ kselftest_intel_pstate_tests_msr \ kselftest_kcmp_tests_kcmp_test \ + kselftest_kvm_arm64_tests_pvm_wipe_mem \ kselftest_net_tests_psock_tpacket \ kselftest_net_tests_socket \ kselftest_net_tests_reuseaddr_conflict \ diff --git a/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c new file mode 100644 index 000000000000..4af8ca3c4bad --- /dev/null +++ b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test checking that memory of protected guests is wiped after teardown. + * + * Copyright (C) 2022, Google LLC. + */ + +#define _GNU_SOURCE + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/kvm.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "kselftest.h" + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) + +#define REG_X(number) (0x6030000000100000ULL + (number) * 2UL) +#define REG_PC 0x6030000000100040ULL + +static void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val) +{ + uint64_t reg_data; + struct kvm_one_reg reg; + int ret; + + reg.addr = (__u64) ®_data; + reg_data = val; + reg.id = reg_id; + + ret = ioctl(vcpufd, KVM_SET_ONE_REG, ®); + if (ret < 0) + ksft_exit_fail_msg("Failed to set reg: %d\n", ret); +} + +static int get_kvm(void) +{ + size_t run_size; + int kvm, ret; + + kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC); + if (kvm < 0) + ksft_exit_skip("KVM not supported\n"); + + ret = ioctl(kvm, KVM_GET_API_VERSION, NULL); + if (ret != 12) + ksft_exit_fail_msg("KVM_GET_API_VERSION %d, expected 12", ret); + + run_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); + if (run_size < sizeof(struct kvm_run)) + ksft_exit_fail_msg("KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n"); + + return kvm; +} + +static int create_protected_vm(int kvm) +{ + int vmfd = ioctl(kvm, KVM_CREATE_VM, KVM_VM_TYPE_ARM_PROTECTED); + + if (vmfd < 0) + ksft_exit_skip("Protected guests not supported: %d\n", vmfd); + + return vmfd; +} + +static int create_vcpu(int vmfd, struct kvm_run **run) +{ + struct kvm_vcpu_init vcpu_init; + int vcpufd, ret; + + ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init); + if (ret) + ksft_exit_fail_msg("Failed to set kvm_vcpu_init %d\n", ret); + + vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0); + if (vcpufd < 0) + ksft_exit_fail_msg("Failed to create VCPU: %d\n", vcpufd); + + *run = mmap(NULL, sizeof(**run), PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); + if (!run) + ksft_exit_fail_msg("Failed to mmap vcpu_run struct\n"); + + ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init); + if (ret) + ksft_exit_fail_msg("Failed to initialize VCPU %d\n", ret); + + return vcpufd; +} + +static void teardown(int kvm, int vmfd, int vcpufd, struct kvm_run *run) +{ + int ret = munmap(run, sizeof(*run)); + + if (ret) + ksft_exit_fail_msg("Failed to unmap vCPU run: %d\n", ret); + + ret = close(vcpufd); + if (ret) + ksft_exit_fail_msg("Failed to destroy VCPU: %d\n", ret); + + ret = close(vmfd); + if (ret) + ksft_exit_fail_msg("Failed to destroy VM: %d\n", ret); + + ret = close(kvm); + if (ret) + ksft_exit_fail_msg("Failed to close KVM fd: %d\n", ret); +} + +int main(void) +{ + struct kvm_userspace_memory_region region; + long page_size = sysconf(_SC_PAGESIZE); + int ret, kvm, vmfd, vcpufd; + uint32_t guest_code[2]; + struct kvm_run *run; + uint8_t *guest_mem; + size_t run_size; + + kvm = get_kvm(); + vmfd = create_protected_vm(kvm); + vcpufd = create_vcpu(vmfd, &run); + + /* Create a one-page memslot for the guest */ + guest_mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (guest_mem == MAP_FAILED) + ksft_exit_fail_msg("Failed to mmap guest memory\n"); + region = (struct kvm_userspace_memory_region) { + .slot = 0, + .guest_phys_addr = 1UL << 30, + .memory_size = page_size, + .userspace_addr = (uint64_t)guest_mem, + }; + + /* Copy some code in guest memory. */ + guest_code[0] = 0xf9400001; /* 1: ldr x1, [x0] */ + guest_code[1] = 0x17ffffff; /* b 1b */ + memcpy(guest_mem, guest_code, sizeof(guest_code)); + ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); + if (ret) + ksft_exit_fail_msg("Failed to set memory region: %d\n", ret); + + /* + * Get the VCPU to run one instruction, to be sure the page containing + * the code has been faulted in. + */ + set_one_reg(vcpufd, REG_PC, region.guest_phys_addr); + set_one_reg(vcpufd, REG_X(0), region.guest_phys_addr + region.memory_size); + ret = ioctl(vcpufd, KVM_RUN, NULL); + if (ret) + ksft_exit_fail_msg("Failed to run vcpu: %d\n", ret); + if (run->exit_reason != KVM_EXIT_MMIO) + ksft_exit_fail_msg("Unexpected KVM exit reason: %u\n", run->exit_reason); + + /* + * Tear the guest down, and check that the donated memory has been + * wiped by the hypervisor. + */ + teardown(kvm, vmfd, vcpufd, run); + if (!memcmp(guest_mem, guest_code, sizeof(guest_code))) + ksft_exit_fail_msg("Protected guest memory has not been poisoned\n"); + + ksft_exit_pass(); +} |