aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Perret <qperret@google.com>2022-02-24 08:55:28 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-02-24 08:55:28 +0000
commit6fd12f5d04bd263abe67b426a1d5d476433deced (patch)
treeda7a830d8aa594efdd4e3706a44fa5bf18dd2293
parent0b74690243009f2719ad31168841e2580b1e1340 (diff)
parenta9dc703806031c8535c84c9905979f86dfaec1d3 (diff)
downloadlinux-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.bp15
-rw-r--r--android/kselftest_test_list.mk1
-rw-r--r--tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c174
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) &reg_data;
+ reg_data = val;
+ reg.id = reg_id;
+
+ ret = ioctl(vcpufd, KVM_SET_ONE_REG, &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, &region);
+ 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();
+}