cachepc

Prime+Probe cache-based side-channel attack on AMD SEV-SNP protected virtual machines
git clone https://git.sinitax.com/sinitax/cachepc
Log | Files | Refs | Submodules | README | sfeed.txt

commit b8f40e776af1a82116a44ce8fac34f343194f033
parent bca09eea299f162a27be3f6a59160afe86c8d525
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed, 11 Jan 2023 18:56:23 +0100

Fix kvm-eviction kvm & sev support

Diffstat:
MMakefile | 19+++++++++++++------
Acachepc/const.h | 22++++++++++++++++++++++
Mcachepc/kvm.c | 6+++---
Mcachepc/uapi.h | 21++-------------------
Mtest/eviction.c | 2++
Mtest/kvm-eviction.c | 299++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Atest/kvm-eviction.h | 3+++
Atest/kvm-eviction_guest.S | 24++++++++++++++++++++++++
Mtest/sev-es.c | 35+++++++++++++++++------------------
9 files changed, 283 insertions(+), 148 deletions(-)

diff --git a/Makefile b/Makefile @@ -2,6 +2,7 @@ LINUX ?= linux CORES ?= $(shell ls /dev/cpu | wc -l) LOAD ?= $(CORES) JOBS ?= $(CORES) + PWD := $(shell pwd) BINS = test/eviction test/kvm-eviction # test/kvm-execstep @@ -9,7 +10,11 @@ BINS = test/eviction test/kvm-eviction # test/kvm-execstep # BINS += test/qemu-aes_guest test/qemu-aes_host BINS += util/svme util/debug util/reset -CFLAGS = -I . -I linux/usr/include -I test -Wunused-variable -Wunknown-pragmas +CFLAGS = -I . -I linux/usr/include -I test +CFLAGS += -g -Wunused-variable -Wunknown-pragmas +CFLAGS += -fsanitize=address + +CACHEPC_UAPI = cachepc/uapi.h cachepc/const.h all: build $(BINS) @@ -22,7 +27,7 @@ $(LINUX)/arch/x86/kvm/cachepc: ln -sf $(PWD)/cachepc $@ host: - # generate host kernel and Module.symvers for depmod + # build host kernel and Module.symvers for depmod cp extra/.config linux/.config git -C $(LINUX) add . git -C $(LINUX) stash @@ -50,10 +55,12 @@ freq: update: git -C $(LINUX) diff 0aaa1e599bee256b3b15643bbb95e80ce7aa9be5 -G. > patch.diff -test/%: test/%.c cachepc/uapi.h - clang -o $@ $< $(CFLAGS) -fsanitize=address +util/%: util/%.c $(CACHEPC_UAPI) + +test/%: test/%.c $(CACHEPC_UAPI) -util/%: util/%.c cachepc/uapi.h - clang -o $@ $< $(CFLAGS) -fsanitize=address +test/kvm-eviction: test/kvm-eviction.c test/kvm-eviction_guest.S \ + test/kvm-eviction.h $(CACHEPC_UAPI) + $(CC) -o $@ test/kvm-eviction.c test/kvm-eviction_guest.S $(CFLAGS) .PHONY: all clean host build load freq update diff --git a/cachepc/const.h b/cachepc/const.h @@ -0,0 +1,22 @@ +#pragma once + +#define CPC_ISOLCPU 2 + +#define CPC_L1MISS_PMC 0 +#define CPC_RETINST_PMC 1 + +#define L1_ASSOC 8 +#define L1_LINESIZE 64 +#define L1_SETS 64 +#define L1_SIZE (L1_SETS * L1_ASSOC * L1_LINESIZE) + +#define L2_ASSOC 8 +#define L2_LINESIZE 64 +#define L2_SETS 1024 +#define L2_SIZE (L2_SETS * L2_ASSOC * L2_LINESIZE) + +#define CPC_VMSA_MAGIC_ADDR ((void *) 0xC0FFEE) + +#define KVM_HC_CPC_VMMCALL_SIGNAL 0xEE01 +#define KVM_HC_CPC_VMMCALL_EXIT 0xEE02 + diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -190,9 +190,9 @@ cachepc_kvm_stream_hwpf_test(void *p) asm volatile ("mov (%0), %%rbx" : : "r"(lines + 4) : "rbx"); asm volatile ("mov (%0), %%rbx" : : "r"(lines + 5) : "rbx"); asm volatile ("mov (%0), %%rbx" : : "r"(lines + 6) : "rbx"); - asm volatile ("mov (%0), %%rbx" : : "r"(lines + 0) : "rbx"); - asm volatile ("mov (%0), %%rbx" : : "r"(lines + 1) : "rbx"); - asm volatile ("mov (%0), %%rbx" : : "r"(lines + 2) : "rbx"); + asm volatile ("mov (%0), %%rbx" : : "r"(lines + 7) : "rbx"); + asm volatile ("mov (%0), %%rbx" : : "r"(lines + 8) : "rbx"); + asm volatile ("mov (%0), %%rbx" : : "r"(lines + 9) : "rbx"); count += cachepc_read_pmc(CPC_L1MISS_PMC); CPC_WARN("HWPF test done (%u vs. %u => %s)\n", diff --git a/cachepc/uapi.h b/cachepc/uapi.h @@ -1,28 +1,11 @@ #pragma once +#include "const.h" + #include <linux/kvm.h> #include <linux/types.h> #include <linux/ioctl.h> -#define CPC_ISOLCPU 2 - -#define CPC_L1MISS_PMC 0 -#define CPC_RETINST_PMC 1 - -#define L1_ASSOC 8 -#define L1_LINESIZE 64 -#define L1_SETS 64 -#define L1_SIZE (L1_SETS * L1_ASSOC * L1_LINESIZE) - -#define L2_ASSOC 8 -#define L2_LINESIZE 64 -#define L2_SETS 1024 -#define L2_SIZE (L2_SETS * L2_ASSOC * L2_LINESIZE) - -#define CPC_VMSA_MAGIC_ADDR ((void *) 0xC0FFEE) - -#define KVM_HC_CPC_VMMCALL_SIGNAL 0xC0FFEE00 -#define KVM_HC_CPC_VMMCALL_EXIT 0xC0FFEE01 #define CPC_DO_VMMCALL(action, type, val) \ asm volatile("vmmcall" : : "a" (KVM_HC_CPC_VMMCALL_ ## action), \ "b"(type), "c" (val) : "rdx") diff --git a/test/eviction.c b/test/eviction.c @@ -21,6 +21,8 @@ main(int argc, const char **argv) set = 48; if (argc > 1) set = atoi(argv[1]); + if (set >= L1_SETS) + errx(1, "set out-of-bounds"); ret = ioctl(fd, KVM_CPC_TEST_EVICTION, &set); if (ret == -1) err(1, "ioctl KVM_CPC_TEST_EVICTION"); diff --git a/test/kvm-eviction.c b/test/kvm-eviction.c @@ -1,5 +1,6 @@ #define _GNU_SOURCE +#include "kvm-eviction.h" #include "cachepc/uapi.h" #include <linux/psp-sev.h> @@ -35,8 +36,6 @@ #define TARGET_CORE 2 #define SECONDARY_CORE 3 -#define TARGET_SET 15 - enum { WITH, WITHOUT @@ -50,10 +49,10 @@ struct kvm { }; /* start and end for guest assembly */ -extern uint8_t __start_guest_with[]; -extern uint8_t __stop_guest_with[]; -extern uint8_t __start_guest_without[]; -extern uint8_t __stop_guest_without[]; +extern uint8_t start_guest_with[]; +extern uint8_t stop_guest_with[]; +extern uint8_t start_guest_without[]; +extern uint8_t stop_guest_without[]; static const char *vmtype; @@ -120,24 +119,6 @@ hexdump(void *data, int len) printf("\n"); } -__attribute__((section("guest_with"))) void -vm_guest_with(void) -{ - while (1) { - asm volatile("mov (%[v]), %%bl" - : : [v] "r" (L1_LINESIZE * TARGET_SET)); - CPC_DO_VMMCALL(EXIT, 0, 0); - } -} - -__attribute__((section("guest_without"))) void -vm_guest_without(void) -{ - while (1) { - CPC_DO_VMMCALL(EXIT, 0, 0); - } -} - bool pin_process(pid_t pid, int cpu, bool assert) { @@ -147,7 +128,7 @@ pin_process(pid_t pid, int cpu, bool assert) CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); ret = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset); - if (ret < 0) { + if (ret == -1) { if (assert) err(1, "sched_setaffinity"); return false; } @@ -227,7 +208,7 @@ sev_get_measure(int vmfd) memset(&msrmt, 0, sizeof(msrmt)); ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr); - if (ret < 0 && fwerr != SEV_RET_INVALID_LEN) + if (ret == -1 && fwerr != SEV_RET_INVALID_LEN) errx(1, "KVM_SEV_LAUNCH_MEASURE: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); @@ -235,9 +216,8 @@ sev_get_measure(int vmfd) msrmt.uaddr = (uintptr_t) data; ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr); - if (ret < 0) - errx(1, "KVM_SEV_LAUNCH_MEASURE: (%s) %s", - strerror(errno), sev_fwerr_str(fwerr)); + if (ret == -1) errx(1, "KVM_SEV_LAUNCH_MEASURE: (%s) %s", + strerror(errno), sev_fwerr_str(fwerr)); free(data); } @@ -250,10 +230,8 @@ sev_guest_state(int vmfd, uint32_t handle) 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)); - } + if (ret == -1) errx(1, "KVM_SEV_GUEST_STATUS: (%s) %s", + strerror(errno), sev_fwerr_str(fwerr)); return status.state; } @@ -268,7 +246,7 @@ sev_debug_encrypt(int vmfd, void *src, void *dst, size_t size) 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", + if (ret == -1) errx(1, "KVM_SEV_DBG_ENCRYPT: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); } @@ -282,7 +260,7 @@ sev_debug_decrypt(int vmfd, void *src, void *dst, size_t size) 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", + if (ret == -1) errx(1, "KVM_SEV_DBG_DECRYPT: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); } @@ -314,31 +292,38 @@ kvm_init(struct kvm *kvm, size_t ramsize, region.guest_phys_addr = 0x0000; region.userspace_addr = (uintptr_t) kvm->mem; ret = ioctl(kvm->vmfd, KVM_SET_USER_MEMORY_REGION, &region); - if (ret < 0) err(1, "KVM_SET_USER_MEMORY_REGION"); + if (ret == -1) err(1, "KVM_SET_USER_MEMORY_REGION"); /* Create virtual cpu core */ 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 == -1) 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"); + if (ret == -1) 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 (ret == -1) err(1, "KVM_SET_SREGS"); /* Initialize rest of registers */ memset(&regs, 0, sizeof(regs)); - regs.rip = 0x0; + regs.rip = 0; regs.rsp = kvm->memsize - 8; regs.rbp = kvm->memsize - 8; - regs.rax = 0; - regs.rdx = 0; regs.rflags = 0x2; ret = ioctl(kvm->vcpufd, KVM_SET_REGS, &regs); - if (ret < 0) err(1, "KVM_SET_REGS"); + if (ret == -1) err(1, "KVM_SET_REGS"); } void @@ -368,22 +353,53 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, memset(&region, 0, sizeof(region)); region.slot = 0; region.memory_size = kvm->memsize; - region.guest_phys_addr = 0x0000; + region.guest_phys_addr = 0; region.userspace_addr = (uintptr_t) kvm->mem; ret = ioctl(kvm->vmfd, KVM_SET_USER_MEMORY_REGION, &region); - if (ret < 0) err(1, "KVM_SET_USER_MEMORY_REGION"); + if (ret == -1) err(1, "KVM_SET_USER_MEMORY_REGION"); /* Enable SEV for vm */ ret = sev_ioctl(kvm->vmfd, KVM_SEV_INIT, NULL, &fwerr); - if (ret < 0) errx(1, "KVM_SEV_INIT: (%s) %s", + if (ret == -1) errx(1, "KVM_SEV_INIT: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); + /* Create virtual cpu core */ + 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 == -1) 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 == -1) err(1, "KVM_GET_SREGS"); + sregs.cs.base = 0; + sregs.cs.selector = 0; + ret = ioctl(kvm->vcpufd, KVM_SET_SREGS, &sregs); + if (ret == -1) err(1, "KVM_SET_SREGS"); + + /* Initialize rest of registers */ + memset(&regs, 0, sizeof(regs)); + regs.rip = 0; + regs.rsp = kvm->memsize - 8; + regs.rbp = kvm->memsize - 8; + regs.rflags = 0x2; + ret = ioctl(kvm->vcpufd, KVM_SET_REGS, &regs); + if (ret == -1) err(1, "KVM_SET_REGS"); + /* Generate encryption keys and set policy */ memset(&start, 0, sizeof(start)); start.handle = 0; start.policy = 0; ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_START, &start, &fwerr); - if (ret < 0) errx(1, "KVM_SEV_LAUNCH_START: (%s) %s", + if (ret == -1) errx(1, "KVM_SEV_LAUNCH_START: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); /* Prepare the vm memory (by encrypting it) */ @@ -391,7 +407,7 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, update.uaddr = (uintptr_t) kvm->mem; update.len = ramsize; ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr); - if (ret < 0) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s", + if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s", strerror(errno), sev_fwerr_str(fwerr)); /* Collect a measurement (necessary) */ @@ -399,19 +415,60 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, /* 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)); + if (ret == -1) 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 core */ +void +sev_es_kvm_init(struct kvm *kvm, size_t ramsize, + void *code_start, void *code_stop) +{ + struct kvm_sev_launch_update_data update; + struct kvm_sev_launch_start start; + struct kvm_userspace_memory_region region; + struct kvm_sev_dbg dec; + struct kvm_regs regs; + struct kvm_sregs sregs; + int ret, fwerr; + void *buf; + + /* Create a kvm instance */ + kvm->vmfd = ioctl(kvm_dev, KVM_CREATE_VM, 0); + if (kvm->vmfd < 0) err(1, "KVM_CREATE_VM"); + + /* 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); + + /* Map it into the vm */ + memset(&region, 0, sizeof(region)); + region.slot = 0; + region.memory_size = kvm->memsize; + region.guest_phys_addr = 0; + region.userspace_addr = (uintptr_t) kvm->mem; + ret = ioctl(kvm->vmfd, KVM_SET_USER_MEMORY_REGION, &region); + if (ret == -1) err(1, "KVM_SET_USER_MEMORY_REGION"); + + /* Enable SEV-ES for vm */ + ret = sev_ioctl(kvm->vmfd, KVM_SEV_ES_INIT, NULL, &fwerr); + if (ret == -1) errx(1, "KVM_SEV_ES_INIT: (%s) %s", + strerror(errno), sev_fwerr_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 == -1) 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, @@ -421,29 +478,62 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, /* Initialize segment regs */ memset(&sregs, 0, sizeof(sregs)); ret = ioctl(kvm->vcpufd, KVM_GET_SREGS, &sregs); - if (ret < 0) err(1, "KVM_GET_SREGS"); + if (ret == -1) 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 (ret == -1) err(1, "KVM_SET_SREGS"); /* Initialize rest of registers */ memset(&regs, 0, sizeof(regs)); - regs.rip = 0x0; + regs.rip = 0; regs.rsp = kvm->memsize - 8; regs.rbp = kvm->memsize - 8; - regs.rax = 0; - regs.rdx = 0; - regs.rflags = 0x2; ret = ioctl(kvm->vcpufd, KVM_SET_REGS, &regs); - if (ret < 0) err(1, "KVM_SET_REGS"); -} + if (ret == -1) err(1, "KVM_SET_REGS"); -void -sev_es_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) -{ - errx(1, "Not implemented"); + /* Generate encryption keys and set policy */ + memset(&start, 0, sizeof(start)); + start.handle = 0; + start.policy = 1 << 2; /* require ES */ + ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_START, &start, &fwerr); + if (ret == -1) 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 = (uintptr_t) kvm->mem; + update.len = ramsize; + ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr); + if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s", + strerror(errno), sev_fwerr_str(fwerr)); + + /* Prepare the vm save area */ + ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fwerr); + if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_VMSA: (%s) %s", + strerror(errno), sev_fwerr_str(fwerr)); + + /* Collect a measurement (necessary) */ + sev_get_measure(kvm->vmfd); + + /* Finalize launch process */ + ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_FINISH, 0, &fwerr); + if (ret == -1) 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)); + + /* Validate code was encrypted correctly */ + buf = malloc(ramsize); + dec.src_uaddr = (uint64_t) kvm->mem; + dec.dst_uaddr = (uint64_t) buf; + dec.len = ramsize; + ret = sev_ioctl(kvm->vmfd, KVM_SEV_DBG_DECRYPT, &dec, &fwerr); + if (ret == -1) errx(1, "KVM_SEV_DBG_DECRYPT: (%s) %s", + strerror(errno), sev_fwerr_str(fwerr)); + if (memcmp(buf, code_start, code_stop - code_start)) + errx(1, "VM ram not encrypted correctly"); } void @@ -506,16 +596,16 @@ collect(struct kvm *kvm, uint8_t *counts) /* run vm twice, use count without initial stack setup */ ret = ioctl(kvm->vcpufd, KVM_RUN, NULL); - if (ret < 0) err(1, "KVM_RUN"); + if (ret == -1) err(1, "KVM_RUN"); if (kvm->run->exit_reason == KVM_EXIT_MMIO) { memset(&regs, 0, sizeof(regs)); ret = ioctl(kvm->vcpufd, KVM_GET_REGS, &regs); - if (ret < 0) err(1, "KVM_GET_REGS"); + if (ret == -1) err(1, "KVM_GET_REGS"); 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) { + } else if (kvm->run->exit_reason != KVM_EXIT_HYPERCALL) { errx(1, "KVM died: %i\n", kvm->run->exit_reason); } @@ -527,9 +617,8 @@ int main(int argc, const char **argv) { struct kvm vms[2]; - uint8_t counts[2][SAMPLE_COUNT][64]; - uint8_t baseline[64]; - uint32_t calc; + uint8_t counts[2][SAMPLE_COUNT][L1_SETS]; + uint8_t baseline[L1_SETS]; int i, k, ret; vmtype = "kvm"; @@ -537,7 +626,7 @@ main(int argc, const char **argv) if (strcmp(vmtype, "kvm") && strcmp(vmtype, "sev") && strcmp(vmtype, "sev-es") && strcmp(vmtype, "sev-snp")) - errx(1, "invalid version: %s", vmtype); + errx(1, "invalid vm mode: %s", vmtype); setvbuf(stdout, NULL, _IONBF, 0); @@ -551,57 +640,63 @@ main(int argc, const char **argv) /* 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 == -1) err(1, "KVM_GET_API_VERSION"); if (ret != 12) errx(1, "KVM_GET_API_VERSION %d, expected 12", ret); /* Reset kernel module state */ ret = ioctl(kvm_dev, KVM_CPC_RESET); - if (ret < 0) err(1, "ioctl KVM_CPC_RESET"); - - vm_init(&vms[WITH], 64 * 64 * 8 * 2, - __start_guest_with, __stop_guest_with); - vm_init(&vms[WITHOUT], 64 * 64 * 8 * 2, - __start_guest_without, __stop_guest_without); + if (ret == -1) err(1, "ioctl KVM_CPC_RESET"); - calc = true; - ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &calc); - if (ret == -1) err(1, "ioctl KVM_CPC_CALC_BASELINE"); + vm_init(&vms[WITH], 2 * L1_SIZE, + start_guest_with, stop_guest_with); + vm_init(&vms[WITHOUT], 2 * L1_SIZE, + start_guest_without, stop_guest_without); for (i = 0; i < SAMPLE_COUNT; i++) { collect(&vms[WITH], counts[WITH][i]); collect(&vms[WITHOUT], counts[WITHOUT][i]); } - calc = false; - ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &calc); - if (ret == -1) err(1, "ioctl KVM_CPC_CALC_BASELINE"); - - ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); - if (ret == -1) err(1, "ioctl KVM_CPC_READ_BASELINE"); + memset(baseline, 0xff, L1_SETS); + for (i = 0; i < SAMPLE_COUNT; i++) { + for (k = 0; k < L1_SETS; k++) { + if (counts[WITH][i][k] < baseline[k]) + baseline[k] = counts[WITH][i][k]; + if (counts[WITHOUT][i][k] < baseline[k]) + baseline[k] = counts[WITHOUT][i][k]; + } + } for (i = 0; i < SAMPLE_COUNT; i++) { - printf("Evictions with access:\n"); + for (k = 0; k < L1_SETS; k++) { + counts[WITH][i][k] -= baseline[k]; + counts[WITHOUT][i][k] -= baseline[k]; + } + + printf("=== Sample %2i ===\n\n", i); + + printf("With eviction:\n"); print_counts(counts[WITH][i]); - printf("Evictions without access:\n"); + printf("Without eviction:\n"); print_counts(counts[WITHOUT][i]); + } - for (k = 0; k < 64; k++) { - if (counts[WITH][i][k] >= 8) - warnx("with: Count OOB (%i, %i)", i, k); - if (baseline[k] > counts[WITH][i][k]) - warnx("with: Baseline oob (%i, %i)", i, k); - counts[WITH][i][k] -= baseline[k]; - - if (counts[WITHOUT][i][k] >= 8) - warnx("without: Count OOB (%i, %i)", i, k); - if (baseline[k] > counts[WITHOUT][i][k]) - warnx("without: Baseline OOB (%i, %i)", i, k); - counts[WITHOUT][i][k] -= baseline[k]; + for (i = 0; i < SAMPLE_COUNT; i++) { + for (k = 0; k < L1_SETS; k++) { + if (counts[WITH][i][k] + baseline[k] >= L1_ASSOC) + warnx("sample %i: With count OOB for set %i (=%i)", + i, k, counts[WITH][i][k] + baseline[k]); + + if (counts[WITHOUT][i][k] + baseline[k] >= L1_ASSOC) + warnx("sample %i: Without count OOB for set %i (=%i)", + i, k, counts[WITHOUT][i][k] + baseline[k]); } if (!counts[WITH][i][TARGET_SET]) - warnx("with: Missing eviction in target set"); + warnx("sample %i: Missing eviction in target set %i (=%i,%i)", + i, TARGET_SET, counts[WITH][i][TARGET_SET], + counts[WITH][i][TARGET_SET] + baseline[i]); } vm_deinit(&vms[WITH]); diff --git a/test/kvm-eviction.h b/test/kvm-eviction.h @@ -0,0 +1,3 @@ +#pragma once + +#define TARGET_SET 15 diff --git a/test/kvm-eviction_guest.S b/test/kvm-eviction_guest.S @@ -0,0 +1,24 @@ +#include "kvm-eviction.h" +#include "cachepc/const.h" + +#define TARGET_SET 15 + +.global start_guest_with +.global stop_guest_with + +.global start_guest_without +.global stop_guest_without + +start_guest_with: + mov $(L1_LINESIZE * TARGET_SET), %rbx + mov (%rbx), %bl + mov $KVM_HC_CPC_VMMCALL_EXIT, %rax + vmmcall + jmp start_guest_with +stop_guest_with: + +start_guest_without: + mov $KVM_HC_CPC_VMMCALL_EXIT, %rax + vmmcall + jmp start_guest_without +stop_guest_without: diff --git a/test/sev-es.c b/test/sev-es.c @@ -399,13 +399,13 @@ sev_kvm_deinit(struct kvm *kvm) munmap(kvm->mem, kvm->memsize); } -cpc_msrmt_t * +uint8_t * read_counts() { - cpc_msrmt_t *counts; + uint8_t *counts; int ret; - counts = malloc(64 * sizeof(cpc_msrmt_t)); + counts = malloc(64); if (!counts) err(1, "malloc"); ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); if (ret == -1) err(1, "ioctl READ_COUNTS"); @@ -414,7 +414,7 @@ read_counts() } void -print_counts(cpc_msrmt_t *counts) +print_counts(uint8_t *counts) { int i; @@ -429,11 +429,11 @@ print_counts(cpc_msrmt_t *counts) if (counts[i] > 0) printf("\x1b[0m"); } - printf("\n Target Set %i Count: %llu\n", TARGET_SET, counts[TARGET_SET]); + printf("\n Target Set %i Count: %u\n", TARGET_SET, counts[TARGET_SET]); printf("\n"); } -cpc_msrmt_t * +uint8_t * collect(struct kvm *kvm) { struct kvm_regs regs; @@ -459,10 +459,10 @@ collect(struct kvm *kvm) int main(int argc, const char **argv) { - cpc_msrmt_t without_access[SAMPLE_COUNT][64]; - cpc_msrmt_t with_access[SAMPLE_COUNT][64]; + uint8_t without_access[SAMPLE_COUNT][64]; + uint8_t with_access[SAMPLE_COUNT][64]; struct kvm kvm_without_access, kvm_with_access; - cpc_msrmt_t *counts, *baseline; + uint8_t *counts, *baseline; uint32_t arg, measure; int i, k, ret; @@ -482,11 +482,10 @@ main(int argc, const char **argv) if (ret != 12) errx(1, "KVM_GET_API_VERSION %d, expected 12", ret); /* init L1 miss counter for host kernel */ - arg = 0x002264D8; - ret = ioctl(kvm_dev, KVM_CPC_INIT_PMC, &arg); - if (ret < 0) err(1, "ioctl INIT_PMC"); + ret = ioctl(kvm_dev, KVM_CPC_RESET); + if (ret < 0) err(1, "ioctl KVM_CPC_RESET"); - baseline = calloc(sizeof(cpc_msrmt_t), 64); + baseline = malloc(64); if (!baseline) err(1, "calloc"); sev_kvm_init(&kvm_with_access, 64 * 64 * 8 * 2, __start_guest_with, __stop_guest_with); @@ -497,21 +496,21 @@ main(int argc, const char **argv) ioctl(kvm_without_access.vcpufd, KVM_RUN, NULL); measure = true; - ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &measure); + ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &measure); if (ret == -1) err(1, "ioctl MEASURE_BASELINE"); for (i = 0; i < SAMPLE_COUNT; i++) { counts = collect(&kvm_without_access); - memcpy(without_access[i], counts, 64 * sizeof(cpc_msrmt_t)); + memcpy(without_access[i], counts, 64); free(counts); counts = collect(&kvm_with_access); - memcpy(with_access[i], counts, 64 * sizeof(cpc_msrmt_t)); + memcpy(with_access[i], counts, 64); free(counts); } measure = false; - ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &measure); + ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &measure); if (ret == -1) err(1, "ioctl MEASURE_BASELINE"); ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); @@ -527,7 +526,7 @@ main(int argc, const char **argv) printf("Evictions with access:\n"); print_counts(with_access[i]); - printf("Evictions without access:\n"); + printf("Evictions withoCALCt access:\n"); print_counts(without_access[i]); }