commit 0c72f6ff527b37d229bd6488d92f48e6829efecc
parent 9a8c707c6f27f202d110f383b004a8be9948887e
Author: Louis Burda <quent.burda@gmail.com>
Date: Wed, 31 Aug 2022 19:13:35 +0200
Run SEV_INIT on vmfd before intializing vmcpu for runtime memory decryption
Diffstat:
5 files changed, 288 insertions(+), 399 deletions(-)
diff --git a/Makefile b/Makefile
@@ -29,7 +29,7 @@ load:
test/%: test/%.c kmod/cachepc_user.h
# $(CC) -o $@ $< -I kmod
- clang -fsanitize=address -o $@ $< -I kmod
+ clang -fsanitize=address -o $@ $< -I kmod -Wunused-variable
update:
git -C $(KERNEL_SOURCE) diff > patch.diff
diff --git a/test/.gitignore b/test/.gitignore
@@ -1,3 +1,4 @@
access
eviction
kvm
+sev
diff --git a/test/kvm.c b/test/kvm.c
@@ -125,55 +125,6 @@ read_stat_core(pid_t pid)
}
void
-clear_cores(uint64_t cpu_mask)
-{
- DIR *proc_dir, *task_dir;
- struct dirent *proc_ent, *task_ent;
- char taskpath[256];
- pid_t pid, tid;
- bool res;
- int cpu;
-
- /* move all processes from the target cpu to secondary */
-
- proc_dir = opendir("/proc");
- if (!proc_dir) err(1, "opendir");
-
- while ((proc_ent = readdir(proc_dir))) {
- pid = atoi(proc_ent->d_name);
- if (!pid) continue;
-
- cpu = read_stat_core(pid);
- if (cpu >= 0 && (1 << cpu) & cpu_mask) {
- res = pin_process(pid, SECONDARY_CORE, false);
- // if (!res) printf("Failed pin %i from %i\n", pid, cpu);
- if (res) printf("Pinned %i from %i\n", tid, cpu);
- continue;
- }
-
- snprintf(taskpath, sizeof(taskpath), "/proc/%u/task", pid);
- task_dir = opendir(taskpath);
- if (!task_dir) err(1, "opendir");
-
- while ((task_ent = readdir(task_dir))) {
- tid = atoi(task_ent->d_name);
- if (!tid || tid == pid) continue;
-
- cpu = read_stat_core(tid);
- if (cpu >= 0 && (1 << cpu) & cpu_mask) {
- res = pin_process(tid, SECONDARY_CORE, false);
- //if (!res) printf("Failed pin %i from %i\n", tid, cpu);
- if (res) printf("Pinned thread %i from %i\n", tid, cpu);
- }
- }
-
- closedir(task_dir);
- }
-
- closedir(proc_dir);
-}
-
-void
kvm_init(size_t ramsize, void *code_start, void *code_stop)
{
struct kvm_userspace_memory_region region;
@@ -293,18 +244,18 @@ collect(const char *prefix, void *code_start, void *code_stop)
/* run vm twice, use count without initial stack setup */
ret = ioctl(kvm.vcpufd, KVM_RUN, NULL);
ret = ioctl(kvm.vcpufd, KVM_RUN, NULL);
+ if (ret == -1) err(1, "KVM_RUN");
if (kvm_run->exit_reason == KVM_EXIT_MMIO || kvm_run->exit_reason == KVM_EXIT_HLT) {
memset(®s, 0, sizeof(regs));
ret = ioctl(kvm.vcpufd, KVM_GET_REGS, ®s);
if (ret < 0) err(1, "KVM_GET_REGS");
- errx(1, "Victim access OOB: %lu %08llx => %02X\n",
+ errx(1, "Victim access OOB: %llu %08llx => %02X\n",
kvm_run->mmio.phys_addr, regs.rip,
- ((uint8_t*)kvm.mem)[regs.rip]);
+ ((uint8_t *)kvm.mem)[regs.rip]);
+ } else if (kvm_run->exit_reason != KVM_EXIT_IO) {
+ errx(1, "KVM died: %i\n", kvm_run->exit_reason);
}
- printf("KVM exit reason %d \n", kvm_run->exit_reason);
- if (ret < 0 || (kvm_run->exit_reason != KVM_EXIT_IO && kvm_run->exit_reason != KVM_EXIT_HLT))
- errx(1, "KVM died: %i %i\n", ret, kvm_run->exit_reason);
counts = read_counts();
@@ -326,7 +277,6 @@ main(int argc, const char **argv)
setvbuf(stdout, NULL, _IONBF, 0);
- clear_cores(1 << TARGET_CORE);
pin_process(0, TARGET_CORE, true);
cachepc_fd = open("/proc/cachepc", O_RDONLY);
@@ -375,6 +325,7 @@ main(int argc, const char **argv)
//assert(without_access[i][TARGET_SET] == 0);
}
+ free(baseline);
close(cachepc_fd);
}
diff --git a/test/sev b/test/sev
Binary files differ.
diff --git a/test/sev.c b/test/sev.c
@@ -3,6 +3,7 @@
#include "cachepc_user.h"
+#include <linux/psp-sev.h>
#include <linux/kvm.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
@@ -35,11 +36,14 @@
#define TARGET_CORE 2
#define SECONDARY_CORE 3
+#define TARGET_CACHE_LINESIZE 64
+#define TARGET_SET 15
+
struct kvm {
- int fd;
- int vmfd;
- int vcpufd;
+ int fd, vmfd, vcpufd;
void *mem;
+ size_t memsize;
+ struct kvm_run *run;
};
/* start and end for guest assembly */
@@ -48,31 +52,87 @@ extern uint8_t __stop_guest_with[];
extern uint8_t __start_guest_without[];
extern uint8_t __stop_guest_without[];
-static bool ready = false;
-static bool processed = false;
+bool ready = false;
+bool processed = false;
+
+ssize_t sysret;
+pid_t victim_pid;
-static ssize_t sysret;
-static pid_t victim_pid;
+/* ioctl dev fds */
+int kvm_dev, sev_dev, cachepc_dev;
-static struct kvm kvm;
-static struct kvm_run *kvm_run;
+enum {
+ GSTATE_UNINIT,
+ GSTATE_LUPDATE,
+ GSTATE_LSECRET,
+ GSTATE_RUNNING,
+ GSTATE_SUPDATE,
+ GSTATE_RUPDATE,
+ GSTATE_SENT
+};
-static int cachepc_fd;
+const char *sev_fwerr_strs[] = {
+ "Success",
+ "Platform state is invalid",
+ "Guest state is invalid",
+ "Platform configuration is invalid",
+ "Buffer too small",
+ "Platform is already owned",
+ "Certificate is invalid",
+ "Policy is not allowed",
+ "Guest is not active",
+ "Invalid address",
+ "Bad signature",
+ "Bad measurement",
+ "Asid is already owned",
+ "Invalid ASID",
+ "WBINVD is required",
+ "DF_FLUSH is required",
+ "Guest handle is invalid",
+ "Invalid command",
+ "Guest is active",
+ "Hardware error",
+ "Hardware unsafe",
+ "Feature not supported",
+ "Invalid parameter",
+ "Out of resources",
+ "Integrity checks failed"
+};
-#define TARGET_CACHE_LINESIZE 64
-#define TARGET_SET 15
+const char *sev_gstate_strs[] = {
+ "UNINIT",
+ "LUPDATE",
+ "LSECRET",
+ "RUNNING",
+ "SUPDATE",
+ "RUPDATE",
+ "SEND"
+};
+
+void
+hexdump(void *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0 && i)
+ printf("\n");
+ printf("%02X ", *(uint8_t *)(data + i));
+ }
+ printf("\n");
+}
-//https://events19.linuxfoundation.org/wp-content/uploads/2017/12/Extending-Secure-Encrypted-Virtualization-with-SEV-ES-Thomas-Lendacky-AMD.pdf
-//https://www.spinics.net/lists/linux-kselftest/msg27206.html
+// REF: https://events19.linuxfoundation.org/wp-content/uploads/2017/12/Extending-Secure-Encrypted-Virtualization-with-SEV-ES-Thomas-Lendacky-AMD.pdf
+// REF: https://www.spinics.net/lists/linux-kselftest/msg27206.html
__attribute__((section("guest_with"))) void
vm_guest_with(void)
{
while (1) {
- //asm volatile("mov (%[v]), %%bl"
- // : : [v] "r" (TARGET_CACHE_LINESIZE * TARGET_SET));
- asm volatile("hlt");
+ asm volatile("mov (%[v]), %%bl"
+ : : [v] "r" (TARGET_CACHE_LINESIZE * TARGET_SET));
+ asm volatile("out %%al, (%%dx)" : : );
+ //asm volatile("hlt");
//asm volatile("rep; vmmcall\n\r");
- //asm volatile("out %%al, (%%dx)" : : );
}
}
@@ -80,8 +140,8 @@ __attribute__((section("guest_without"))) void
vm_guest_without(void)
{
while (1) {
- asm volatile("hlt");
- //asm volatile("out %%al, (%%dx)" : : );
+ //asm volatile("hlt");
+ asm volatile("out %%al, (%%dx)" : : );
}
}
@@ -89,12 +149,12 @@ bool
pin_process(pid_t pid, int cpu, bool assert)
{
cpu_set_t cpuset;
- int status;
+ int ret;
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
- status = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset);
- if (status < 0) {
+ ret = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset);
+ if (ret < 0) {
if (assert) err(1, "sched_setaffinity");
return false;
}
@@ -130,356 +190,227 @@ read_stat_core(pid_t pid)
return cpu;
}
-void
-clear_cores(uint64_t cpu_mask)
+const char *
+sev_fwerr_str(int code)
{
- DIR *proc_dir, *task_dir;
- struct dirent *proc_ent, *task_ent;
- char taskpath[256];
- pid_t pid, tid;
- bool res;
- int cpu;
-
- /* move all processes from the target cpu to secondary */
-
- proc_dir = opendir("/proc");
- if (!proc_dir) err(1, "opendir");
-
- while ((proc_ent = readdir(proc_dir))) {
- pid = atoi(proc_ent->d_name);
- if (!pid) continue;
-
- cpu = read_stat_core(pid);
- if (cpu >= 0 && (1 << cpu) & cpu_mask) {
- res = pin_process(pid, SECONDARY_CORE, false);
- // if (!res) printf("Failed pin %i from %i\n", pid, cpu);
- if (res) printf("Pinned %i from %i\n", tid, cpu);
- continue;
- }
-
- snprintf(taskpath, sizeof(taskpath), "/proc/%u/task", pid);
- task_dir = opendir(taskpath);
- if (!task_dir) err(1, "opendir");
-
- while ((task_ent = readdir(task_dir))) {
- tid = atoi(task_ent->d_name);
- if (!tid || tid == pid) continue;
+ if (code < 0 || code >= ARRLEN(sev_fwerr_strs))
+ return "Unknown error";
- cpu = read_stat_core(tid);
- if (cpu >= 0 && (1 << cpu) & cpu_mask) {
- res = pin_process(tid, SECONDARY_CORE, false);
- //if (!res) printf("Failed pin %i from %i\n", tid, cpu);
- if (res) printf("Pinned thread %i from %i\n", tid, cpu);
- }
- }
-
- closedir(task_dir);
- }
-
- closedir(proc_dir);
+ return sev_fwerr_strs[code];
}
-void
-kvm_init(size_t ramsize, void *code_start, void *code_stop)
+const char *
+sev_gstate_str(int code)
{
- struct kvm_userspace_memory_region region;
- struct kvm_regs regs;
- struct kvm_sregs sregs;
- int ret;
-
- kvm.fd = open("/dev/kvm", O_RDWR | O_CLOEXEC);
- if (kvm.fd < 0) err(1, "/dev/kvm");
-
- /* Make sure we have the stable version of the API */
- ret = ioctl(kvm.fd, KVM_GET_API_VERSION, NULL);
- if (ret == -1) err(1, "KVM_GET_API_VERSION");
- if (ret != 12) errx(1, "KVM_GET_API_VERSION %d, expected 12", ret);
-
- kvm.vmfd = ioctl(kvm.fd, KVM_CREATE_VM, 0);
- if (kvm.vmfd < 0) err(1, "KVM_CREATE_VM");
-
- /* Allocate one aligned page of guest memory to hold the code. */
- kvm.mem = mmap(NULL, ramsize, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- if (!kvm.mem) err(1, "allocating guest memory");
- assert(code_stop - code_start <= ramsize);
- memcpy(kvm.mem, code_start, code_stop - code_start);
-
- /* Map it into vm memory */
- memset(®ion, 0, sizeof(region));
- region.slot = 0;
- region.memory_size = ramsize;
- region.guest_phys_addr = 0x0000;
- region.userspace_addr = (uint64_t) kvm.mem;
-
- ret = ioctl(kvm.vmfd, KVM_SET_USER_MEMORY_REGION, ®ion);
- if (ret < 0) err(1, "KVM_SET_USER_MEMORY_REGION");
-
- kvm.vcpufd = ioctl(kvm.vmfd, KVM_CREATE_VCPU, 0);
- if (kvm.vcpufd < 0) err(1, "KVM_CREATE_VCPU");
-
- /* Map the shared kvm_run structure and following data. */
- ret = ioctl(kvm.fd, KVM_GET_VCPU_MMAP_SIZE, NULL);
- if (ret < 0) err(1, "KVM_GET_VCPU_MMAP_SIZE");
-
- if (ret < sizeof(struct kvm_run))
- errx(1, "KVM_GET_VCPU_MMAP_SIZE too small");
- kvm_run = mmap(NULL, ret, PROT_READ | PROT_WRITE,
- MAP_SHARED, kvm.vcpufd, 0);
- if (!kvm_run) err(1, "mmap vcpu");
-
- /* Initialize CS to point at 0, via a read-modify-write of sregs. */
- memset(&sregs, 0, sizeof(sregs));
- ret = ioctl(kvm.vcpufd, KVM_GET_SREGS, &sregs);
- if (ret < 0) err(1, "KVM_GET_SREGS");
- sregs.cs.base = 0;
- sregs.cs.selector = 0;
- ret = ioctl(kvm.vcpufd, KVM_SET_SREGS, &sregs);
- if (ret < 0) err(1, "KVM_SET_SREGS");
+ if (code < 0 || code >= ARRLEN(sev_gstate_strs))
+ return "Unknown gstate";
- /* Initialize registers: instruction pointer for our code, addends, and
- * initial flags required by x86 architecture. */
- memset(®s, 0, sizeof(regs));
- regs.rip = 0x0;
- regs.rsp = ramsize - 1;
- regs.rbp = ramsize - 1;
- regs.rax = 0;
- regs.rdx = 0;
- regs.rflags = 0x2;
- ret = ioctl(kvm.vcpufd, KVM_SET_REGS, ®s);
- if (ret < 0) err(1, "KVM_SET_REGS");
+ return sev_gstate_strs[code];
}
-
int
-kvm_vm_ioctl(int vmfd, int type, ...)
-{
- va_list ap;
- void *arg;
- int ret;
-
- va_start(ap, type);
- arg = va_arg(ap, void *);
- va_end(ap);
-
- ret = ioctl(vmfd, type, arg);
- if (ret == -1) {
- ret = -errno;
- }
- return ret;
-}
-
-static int
-sev_ioctl(int fd, int cmd, void *data, int *error)
+sev_ioctl(int vmfd, int cmd, void *data, int *error)
{
- // REF: https://github.dev/OpenChannelSSD/qemu-nvme/blob/master/target/i386/sev.c
struct kvm_sev_cmd input;
int ret;
memset(&input, 0, sizeof(input));
input.id = cmd;
- input.sev_fd = fd;
- input.data = (uint64_t) data;
+ input.sev_fd = sev_dev;
+ input.data = (uintptr_t) data;
- ret = kvm_vm_ioctl(kvm.vmfd, KVM_MEMORY_ENCRYPT_OP, &input);
+ ret = ioctl(vmfd, KVM_MEMORY_ENCRYPT_OP, &input);
if (error) *error = input.error;
return ret;
}
-void vini_hexdump(unsigned char *data, int len)
+uint8_t *
+sev_get_measure(int vmfd)
{
- char *out = (char *)malloc(len * 2 + 1);
- for (int i = 0; i != len; i++)
- {
- sprintf(&out[2 * i], "%02X", data[i]);
- }
- out[len * 2] = '\0';
- printf("###Vincent_hexdump: %s\n", out);
- free(out);
-}
-
-
+ struct kvm_sev_launch_measure msrmt;
+ int ret, fwerr;
+ uint8_t *data;
-static void
-sev_launch_get_measure(int sev_fd)
-{
- int ret, error;
- struct kvm_sev_launch_measure measurement;
- memset(&measurement, 0, sizeof(struct kvm_sev_launch_measure));
- /* query the measurement blob length */
- ret = sev_ioctl(sev_fd, KVM_SEV_LAUNCH_MEASURE,
- &measurement, &error);
- printf("Measurement len %d\n", measurement.len);
- if (!measurement.len) {
- printf("%s: LAUNCH_MEASURE first call ret=%d fw_error=%d",
- __func__, ret, error);
- //goto free_measurement;
- }
- printf("Measurement len %d\n", measurement.len);
- char *data = malloc(measurement.len);
- //data = g_new0(guchar, measurement->len);
- measurement.uaddr = (unsigned long)data;
- /* get the measurement blob */
- ret = sev_ioctl(sev_fd, KVM_SEV_LAUNCH_MEASURE,
- &measurement, &error);
- if (ret) {
- printf("%s: LAUNCH_MEASURE second calll ret=%d fw_error=%d ",
- __func__, ret, error);
- goto free_data;
- }
- printf("Measurement report \n");
- printf("######### \n");
- vini_hexdump(data, measurement.len);
- printf("############ \n");
-
-
- //sev_set_guest_state(SEV_STATE_LAUNCH_SECRET);
-
- /* encode the measurement value and emit the event */
- //s->measurement = g_base64_encode(data, measurement->len);
- //trace_kvm_sev_launch_measurement(s->measurement);
- free_data:
- free(data);
-}
+ memset(&msrmt, 0, sizeof(msrmt));
+ ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr);
+ if (ret < 0 && fwerr != SEV_RET_INVALID_LEN)
+ errx(1, "LAUNCH_MEASURE: (%s) %s", strerror(errno), sev_fwerr_str(fwerr));
-static const char *const sev_fw_errlist[] = {
- "",
- "Platform state is invalid",
- "Guest state is invalid",
- "Platform configuration is invalid",
- "Buffer too small",
- "Platform is already owned",
- "Certificate is invalid",
- "Policy is not allowed",
- "Guest is not active",
- "Invalid address",
- "Bad signature",
- "Bad measurement",
- "Asid is already owned",
- "Invalid ASID",
- "WBINVD is required",
- "DF_FLUSH is required",
- "Guest handle is invalid",
- "Invalid command",
- "Guest is active",
- "Hardware error",
- "Hardware unsafe",
- "Feature not supported",
- "Invalid parameter"
-};
+ data = malloc(msrmt.len);
+ msrmt.uaddr = (uintptr_t) data;
+ ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr);
+ if (ret < 0)
+ errx(1, "LAUNCH_MEASURE: (%s) %s", strerror(errno), sev_fwerr_str(fwerr));
-#define SEV_FW_MAX_ERROR 23 //TODO VU: Right?
+ return data;
+}
-static const char *
-fw_error_to_str(int code)
+uint8_t
+sev_guest_state(int vmfd, uint32_t handle)
{
- if (code < 0 || code >= SEV_FW_MAX_ERROR) {
- return "unknown error";
- }
+ struct kvm_sev_guest_status status;
+ int ret, fwerr;
+
+ status.handle = handle;
+ ret = sev_ioctl(vmfd, KVM_SEV_GUEST_STATUS, &status, &fwerr);
+ if (ret < 0) {
+ errx(1, "KVM_SEV_GUEST_STATUS: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
+ }
- return sev_fw_errlist[code];
+ return status.state;
}
-uint8_t get_guest_state(int sev_fd, uint32_t handle){
- //See Table 4.3 here
- //https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
- struct kvm_sev_guest_status guest_status_data;
- guest_status_data.handle = handle;
- int fwerror = 0;
- int ret = sev_ioctl(sev_fd, KVM_SEV_GUEST_STATUS, &guest_status_data, &fwerror);
- if (ret < 0) errx(1, "KVM_SEV_GUEST_STATUS: (%s) %s", strerror(errno), fw_error_to_str(fwerror));
- return guest_status_data.state;
-
+void
+sev_debug_encrypt(int vmfd, void *src, void *dst, size_t size)
+{
+ struct kvm_sev_dbg enc;
+ int ret, fwerr;
+
+ enc.src_uaddr = (uintptr_t) src;
+ enc.dst_uaddr = (uintptr_t) dst;
+ enc.len = size;
+ ret = sev_ioctl(vmfd, KVM_SEV_DBG_ENCRYPT, &enc, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_DBG_ENCRYPT: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
+}
+void
+sev_debug_decrypt(int vmfd, void *src, void *dst, size_t size)
+{
+ struct kvm_sev_dbg enc;
+ int ret, fwerr;
+
+ enc.src_uaddr = (uintptr_t) src;
+ enc.dst_uaddr = (uintptr_t) dst;
+ enc.len = size;
+ ret = sev_ioctl(vmfd, KVM_SEV_DBG_DECRYPT, &enc, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_DBG_DECRYPT: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
}
void
-kvm_svm_init(size_t ramsize, void *code_start, void *code_stop)
+sev_kvm_init(struct kvm *kvm, size_t ramsize, void *code_start, void *code_stop)
{
// REF: https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
struct kvm_sev_launch_update_data update;
struct kvm_sev_launch_start start;
+ struct kvm_userspace_memory_region region;
struct kvm_regs regs;
- uint16_t *counts;
- int sev_fd;
- int fwerr;
- int status;
- int ret;
+ struct kvm_sregs sregs;
+ uint8_t *msrmt;
+ int ret, fwerr;
- /* using cache size for alignment of kvm memory access */
- kvm_init(ramsize, code_start, code_stop);
- //kvm_svm_init(64 * 64 * 8 * 2, __start_guest_with, __stop_guest_with);
+ /* Create a kvm instance */
+ kvm->vmfd = ioctl(kvm_dev, KVM_CREATE_VM, 0);
+ if (kvm->vmfd < 0) err(1, "KVM_CREATE_VM");
- sev_fd = open("/dev/sev", O_RDWR | O_CLOEXEC);
- if (sev_fd < 0) err(1, "open /dev/sev");
+ /* Allocate guest memory */
+ kvm->memsize = ramsize;
+ kvm->mem = mmap(NULL, kvm->memsize, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (!kvm->mem) err(1, "Allocating guest memory");
+ assert(code_stop - code_start <= kvm->memsize);
+ memcpy(kvm->mem, code_start, code_stop - code_start);
- ret = ioctl(kvm.vmfd, KVM_MEMORY_ENCRYPT_OP, NULL);
- if (ret < 0) errx(1, "KVM_MEMORY_ENCRYPT_OPT: %i %i", ret, fwerr);
+ /* Map it into the vm */
+ memset(®ion, 0, sizeof(region));
+ region.slot = 0;
+ region.memory_size = kvm->memsize;
+ region.guest_phys_addr = 0x0000;
+ region.userspace_addr = (uintptr_t) kvm->mem;
+ ret = ioctl(kvm->vmfd, KVM_SET_USER_MEMORY_REGION, ®ion);
+ if (ret < 0) err(1, "KVM_SET_USER_MEMORY_REGION");
- ret = sev_ioctl(sev_fd, KVM_SEV_INIT, NULL, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_INIT: %i %i", ret, fwerr);
+ /* Enable SEV for vm */
+ ret = sev_ioctl(kvm->vmfd, KVM_SEV_INIT, NULL, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_INIT: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
- // REF: https://documentation.suse.com/sles/15-SP1/html/SLES-amd-sev/index.html
+ /* Generate encryption keys and set policy */
memset(&start, 0, sizeof(start));
start.handle = 0;
start.policy = 0;
- start.policy |= 0 << 2; // disable require sev-es -- we are working with SEV right now
- // start.policy |= 3 << 32; // minimum fw-version
- ret = sev_ioctl(sev_fd, KVM_SEV_LAUNCH_START, &start, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_LAUNCH_START: (%s) %i", strerror(errno), fwerr);
-
+ // start.policy |= 1 << 0; // disable debug
+ // start.policy |= 1 << 2; // require sev-es
+ // start.policy |= 3 << 32; // minimum guest fw-version
+ ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_START, &start, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_LAUNCH_START: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
+
+ /* Prepare the vm memory (by encrypting it) */
memset(&update, 0, sizeof(update));
- update.uaddr = (uint64_t) kvm.mem;
+ update.uaddr = (uintptr_t) kvm->mem;
update.len = ramsize;
- ret = sev_ioctl(sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: %i %i", ret, fwerr);
- //Get measurement
- sev_launch_get_measure(sev_fd);
- ret = sev_ioctl(sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: %i %i", ret, fwerr);
- //Test that memory encryption and decryption works
- char *input_data = "VINCENT_ENCRYPTION";
- int in_len = strlen(input_data)+1;
- printf("Input length %d \n", in_len);
- char *output_buffer = malloc(in_len);
- char *decrypt_buffer = malloc(in_len);
- struct kvm_sev_dbg dbg_enc_in;
- dbg_enc_in.src_uaddr = (uint64_t) input_data;
- dbg_enc_in.dst_uaddr = (uint64_t) output_buffer;
- dbg_enc_in.len = in_len;
- ret = sev_ioctl(sev_fd, KVM_SEV_DBG_ENCRYPT, &dbg_enc_in, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_DBG_ENCRYPT: %i %i", ret, fw_error_to_str(fwerr));
- printf("Encrypted data debug\n");
- vini_hexdump(output_buffer, in_len);
- dbg_enc_in.src_uaddr = (uint64_t) output_buffer;
- dbg_enc_in.dst_uaddr = (uint64_t) decrypt_buffer;
- ret = sev_ioctl(sev_fd, KVM_SEV_DBG_DECRYPT, &dbg_enc_in, &fwerr);
- if (ret < 0) errx(1, "KVM_SEV_DBG_ENCRYPT: %i %i", ret, fw_error_to_str(fwerr));
- printf("Decrypted data debug\n");
- vini_hexdump(decrypt_buffer, in_len);
- printf("%s \n", decrypt_buffer);
- printf("Guest state after setup %u", get_guest_state(sev_fd, start.handle));
-
-
-
+ ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
+
+ /* Collect a measurement (necessary) */
+ msrmt = sev_get_measure(kvm->vmfd);
+ free(msrmt);
+
+ /* Finalize launch process */
+ ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_FINISH, 0, &fwerr);
+ if (ret < 0) errx(1, "KVM_SEV_LAUNCH_FINISH: (%s) %s",
+ strerror(errno), sev_fwerr_str(fwerr));
+ ret = sev_guest_state(kvm->vmfd, start.handle);
+ if (ret != GSTATE_RUNNING)
+ errx(1, "Bad guest state: %s", sev_gstate_str(fwerr));
+
+ /* Create virtual cpu */
+ kvm->vcpufd = ioctl(kvm->vmfd, KVM_CREATE_VCPU, 0);
+ if (kvm->vcpufd < 0) err(1, "KVM_CREATE_VCPU");
+
+ /* Map the shared kvm_run structure and following data */
+ ret = ioctl(kvm_dev, KVM_GET_VCPU_MMAP_SIZE, NULL);
+ if (ret < 0) err(1, "KVM_GET_VCPU_MMAP_SIZE");
+ if (ret < sizeof(struct kvm_run))
+ errx(1, "KVM_GET_VCPU_MMAP_SIZE too small");
+ kvm->run = mmap(NULL, ret, PROT_READ | PROT_WRITE,
+ MAP_SHARED, kvm->vcpufd, 0);
+ if (!kvm->run) err(1, "mmap vcpu");
+ /* Initialize segment regs */
+ memset(&sregs, 0, sizeof(sregs));
+ ret = ioctl(kvm->vcpufd, KVM_GET_SREGS, &sregs);
+ if (ret < 0) err(1, "KVM_GET_SREGS");
+ sregs.cs.base = 0;
+ sregs.cs.selector = 0;
+ ret = ioctl(kvm->vcpufd, KVM_SET_SREGS, &sregs);
+ if (ret < 0) err(1, "KVM_SET_SREGS");
+ /* Initialize rest of registers */
+ memset(®s, 0, sizeof(regs));
+ regs.rip = 0x0;
+ regs.rsp = kvm->memsize - 1;
+ regs.rbp = kvm->memsize - 1;
+ regs.rax = 0;
+ regs.rdx = 0;
+ regs.rflags = 0x2;
+ ret = ioctl(kvm->vcpufd, KVM_SET_REGS, ®s);
+ if (ret < 0) err(1, "KVM_SET_REGS");
+}
-
- //printf("Return code opening /dev/sev %d\n", sev_fd);
- //printf("Return code %d \n", ioctl(sev_fd, KVM_SEV_ES_INIT, NULL));
+void
+sev_kvm_deinit(struct kvm *kvm)
+{
+ close(kvm->vmfd);
+ close(kvm->vcpufd);
+ munmap(kvm->mem, kvm->memsize);
}
uint16_t *
read_counts()
{
- uint16_t *counts = (uint16_t *)malloc(64*sizeof(uint16_t));
+ uint16_t *counts;
size_t len;
- lseek(cachepc_fd, 0, SEEK_SET);
- len = read(cachepc_fd, counts, 64 * sizeof(uint16_t));
+ counts = malloc(64 * sizeof(uint16_t));
+ lseek(cachepc_dev, 0, SEEK_SET);
+ len = read(cachepc_dev, counts, 64 * sizeof(uint16_t));
assert(len == 64 * sizeof(uint16_t));
return counts;
@@ -501,7 +432,7 @@ print_counts(uint16_t *counts)
if (counts[i] > 0)
printf("\x1b[0m");
}
- printf("\n Target Set Count: %d %hu \n", TARGET_SET, counts[TARGET_SET]);
+ printf("\n Target Set %i Count: %hu\n", TARGET_SET, counts[TARGET_SET]);
printf("\n");
}
@@ -509,36 +440,31 @@ uint16_t *
collect(const char *prefix, void *code_start, void *code_stop)
{
struct kvm_regs regs;
+ struct kvm kvm;
uint16_t *counts;
int ret;
- // using cache size for alignment of kvm memory access
- //kvm_init(64 * 64 * 8 * 2, code_start, code_stop);
- kvm_svm_init(64 * 64 * 8 * 2, code_start, code_stop);
- printf("Vincent: SEV init done done!\n");
+ sev_kvm_init(&kvm, 64 * 64 * 8 * 2, code_start, code_stop);
+
/* run vm twice, use count without initial stack setup */
- printf("First ioctl running the VM\n");
ret = ioctl(kvm.vcpufd, KVM_RUN, NULL);
- printf("Second ioctl call to run the VM\n");
ret = ioctl(kvm.vcpufd, KVM_RUN, NULL);
+ if (ret < 0) err(1, "KVM_RUN");
- if (kvm_run->exit_reason == KVM_EXIT_MMIO || kvm_run->exit_reason == KVM_EXIT_HLT) {
+ if (kvm.run->exit_reason == KVM_EXIT_MMIO) {
memset(®s, 0, sizeof(regs));
ret = ioctl(kvm.vcpufd, KVM_GET_REGS, ®s);
if (ret < 0) err(1, "KVM_GET_REGS");
- errx(1, "Victim access OOB: %lu %08llx => %02X\n",
- kvm_run->mmio.phys_addr, regs.rip,
- ((uint8_t*)kvm.mem)[regs.rip]);
+ errx(1, "Victim access OOB: %llu %08llx => %02X\n",
+ kvm.run->mmio.phys_addr, regs.rip,
+ ((uint8_t *)kvm.mem)[regs.rip]);
+ } else if (kvm.run->exit_reason != KVM_EXIT_IO) {
+ errx(1, "KVM died: %i\n", kvm.run->exit_reason);
}
- printf("KVM exit reason %d \n", kvm_run->exit_reason);
- if (ret < 0 || (kvm_run->exit_reason != KVM_EXIT_IO && kvm_run->exit_reason != KVM_EXIT_HLT))
- errx(1, "KVM died: %i %i\n", ret, kvm_run->exit_reason);
counts = read_counts();
- close(kvm.fd);
- close(kvm.vmfd);
- close(kvm.vcpufd);
+ sev_kvm_deinit(&kvm);
return counts;
}
@@ -554,29 +480,36 @@ main(int argc, const char **argv)
setvbuf(stdout, NULL, _IONBF, 0);
- clear_cores(1 << TARGET_CORE);
pin_process(0, TARGET_CORE, true);
- cachepc_fd = open("/proc/cachepc", O_RDONLY);
- if (cachepc_fd < 0) err(1, "open /proc/cachepc");
+ cachepc_dev = open("/proc/cachepc", O_RDONLY);
+ if (cachepc_dev < 0) err(1, "open /proc/cachepc");
- /* init L1 miss counter */
+ sev_dev = open("/dev/sev", O_RDWR | O_CLOEXEC);
+ if (sev_dev < 0) err(1, "open /dev/sev");
+
+ kvm_dev = open("/dev/kvm", O_RDWR | O_CLOEXEC);
+ if (kvm_dev < 0) err(1, "open /dev/kvm");
+
+ /* Make sure we have the stable version of the API */
+ ret = ioctl(kvm_dev, KVM_GET_API_VERSION, NULL);
+ if (ret < 0) err(1, "KVM_GET_API_VERSION");
+ if (ret != 12) errx(1, "KVM_GET_API_VERSION %d, expected 12", ret);
+
+ // Init L1 miss counter
arg = 0x000064D8;
- ret = ioctl(cachepc_fd, CACHEPC_IOCTL_INIT_PMC, &arg);
- if (ret == -1) err(1, "ioctl fail");
+ ret = ioctl(cachepc_dev, CACHEPC_IOCTL_INIT_PMC, &arg);
+ if (ret < 0) err(1, "ioctl fail");
baseline = calloc(sizeof(uint16_t), 64);
if (!baseline) err(1, "counts");
for (k = 0; k < 64; k++)
baseline[k] = UINT16_MAX;
- //kvm_svm_init(64 * 64 * 8 * 2, __start_guest_with, __stop_guest_with);
- //return 0;
-
for (i = 0; i < SAMPLE_COUNT; i++) {
- //counts = collect("without", __start_guest_without, __stop_guest_without);
- //memcpy(without_access[i], counts, 64 * sizeof(uint16_t));
- //free(counts);
+ counts = collect("without", __start_guest_without, __stop_guest_without);
+ memcpy(without_access[i], counts, 64 * sizeof(uint16_t));
+ free(counts);
counts = collect("with", __start_guest_with, __stop_guest_with);
memcpy(with_access[i], counts, 64 * sizeof(uint16_t));
@@ -606,6 +539,10 @@ main(int argc, const char **argv)
//assert(without_access[i][TARGET_SET] == 0);
}
- close(cachepc_fd);
+ free(baseline);
+
+ close(cachepc_dev);
+ close(kvm_dev);
+ close(sev_dev);
}