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

kvm.c (12881B)


      1#define _GNU_SOURCE
      2
      3#include "test/kvm.h"
      4#include "test/util.h"
      5#include "cachepc/uapi.h"
      6
      7#include <linux/psp-sev.h>
      8#include <linux/kvm.h>
      9#include <sys/syscall.h>
     10#include <sys/ioctl.h>
     11#include <sys/user.h>
     12#include <sys/wait.h>
     13#include <sys/ioctl.h>
     14#include <sys/mman.h>
     15#include <sys/stat.h>
     16#include <sys/types.h>
     17#include <unistd.h>
     18#include <signal.h>
     19#include <dirent.h>
     20#include <assert.h>
     21#include <errno.h>
     22#include <err.h>
     23#include <fcntl.h>
     24#include <sched.h>
     25#include <string.h>
     26#include <stdbool.h>
     27#include <stdint.h>
     28#include <stdio.h>
     29#include <stdlib.h>
     30
     31int kvm_dev = -1;
     32int sev_dev = -1;
     33const char *vmtype = NULL;
     34
     35const char *sev_fwerr_strs[] = {
     36	[0x00] = "Success",
     37	[0x01] = "Platform state is invalid",
     38	[0x02] = "Guest state is invalid",
     39	[0x03] = "Platform configuration is invalid",
     40	[0x04] = "Buffer too small",
     41	[0x05] = "Platform is already owned",
     42	[0x06] = "Certificate is invalid",
     43	[0x07] = "Request not allowed by policy",
     44	[0x08] = "Guest is inactive",
     45	[0x09] = "Invalid address",
     46	[0x0A] = "Bad signature",
     47	[0x0B] = "Bad measurement",
     48	[0x0C] = "Asid is already owned",
     49	[0x0D] = "Invalid ASID",
     50	[0x0E] = "WBINVD is required",
     51	[0x0F] = "DF_FLUSH is required",
     52	[0x10] = "Guest handle is invalid",
     53	[0x11] = "Invalid command",
     54	[0x12] = "Guest is active",
     55	[0x13] = "Hardware error",
     56	[0x14] = "Hardware unsafe",
     57	[0x15] = "Feature not supported",
     58	[0x16] = "Invalid parameter",
     59	[0x17] = "Out of resources",
     60	[0x18] = "Integrity checks failed",
     61	[0x19] = "RMP page size is incorrect",
     62	[0x1A] = "RMP page state is incorrect",
     63};
     64
     65const char *sev_gstate_strs[] = {
     66	"UNINIT",
     67	"LUPDATE",
     68	"LSECRET",
     69	"RUNNING",
     70	"SUPDATE",
     71	"RUPDATE",
     72	"SEND"
     73};
     74
     75const char *
     76sev_fwerr_str(int code)
     77{
     78	if (code < 0 || code >= ARRLEN(sev_fwerr_strs))
     79		return "Unknown error";
     80
     81	return sev_fwerr_strs[code];
     82}
     83
     84const char *
     85sev_gstate_str(int code)
     86{
     87	if (code < 0 || code >= ARRLEN(sev_gstate_strs))
     88		return "Unknown gstate";
     89
     90	return sev_gstate_strs[code];
     91}
     92
     93int
     94sev_ioctl(int vmfd, int cmd, void *data, int *error)
     95{
     96	struct kvm_sev_cmd input;
     97	int ret;
     98
     99	memset(&input, 0, sizeof(input));
    100	input.id = cmd;
    101	input.sev_fd = sev_dev;
    102	input.data = (uintptr_t) data;
    103
    104	if (vmfd == MAIN_VMFD) {
    105		ret = ioctl(kvm_dev, KVM_CPC_MEMORY_ENCRYPT_OP, &input);
    106		if (error) *error = input.error;
    107	} else {
    108		ret = ioctl(vmfd, KVM_MEMORY_ENCRYPT_OP, &input);
    109		if (error) *error = input.error;
    110	}
    111
    112	return ret;
    113}
    114
    115void
    116sev_get_measure(int vmfd)
    117{
    118	struct kvm_sev_launch_measure msrmt;
    119	int ret, fwerr;
    120	uint8_t *data;
    121
    122	memset(&msrmt, 0, sizeof(msrmt));
    123	ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr);
    124	if (ret == -1 && fwerr != SEV_RET_INVALID_LEN)
    125		errx(1, "KVM_SEV_LAUNCH_MEASURE: (%s) %s",
    126			strerror(errno), sev_fwerr_str(fwerr));
    127
    128	data = malloc(msrmt.len);
    129	msrmt.uaddr = (uintptr_t) data;
    130
    131	ret = sev_ioctl(vmfd, KVM_SEV_LAUNCH_MEASURE, &msrmt, &fwerr);
    132	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_MEASURE: (%s) %s",
    133		strerror(errno), sev_fwerr_str(fwerr));
    134
    135	free(data);
    136}
    137
    138uint8_t
    139sev_guest_state(int vmfd, uint32_t handle)
    140{
    141	struct kvm_sev_guest_status status;
    142	int ret, fwerr;
    143
    144	status.handle = handle;
    145	ret = sev_ioctl(vmfd, KVM_SEV_GUEST_STATUS, &status, &fwerr);
    146	if (ret == -1) errx(1, "KVM_SEV_GUEST_STATUS: (%s) %s",
    147		strerror(errno), sev_fwerr_str(fwerr));
    148
    149	return status.state;
    150}
    151
    152void
    153guest_init(struct guest *guest, const char *filename)
    154{
    155	FILE *f;
    156
    157	f = fopen(filename, "r");
    158	if (!f) err(1, "fopen");
    159
    160	fseek(f, 0, SEEK_END);
    161	guest->code_size = ftell(f);
    162	fseek(f, 0, SEEK_SET);
    163
    164	guest->code = malloc(guest->code_size);
    165	if (!guest->code) err(1, "malloc");
    166
    167	if (!fread(guest->code, guest->code_size, 1, f))
    168		errx(1, "read guest");
    169
    170	guest->mem_size = 0;
    171
    172	fclose(f);
    173}
    174
    175void
    176guest_deinit(struct guest *guest)
    177{
    178	free(guest->code);
    179}
    180
    181void
    182kvm_create_vm(struct kvm *kvm)
    183{
    184	kvm->vmfd = ioctl(kvm_dev, KVM_CREATE_VM, 0);
    185	if (kvm->vmfd < 0) err(1, "KVM_CREATE_VM");
    186}
    187
    188void
    189kvm_init_memory(struct kvm *kvm, size_t mem_size, void *code, size_t code_size)
    190{
    191	struct kvm_userspace_memory_region region;
    192	int ret;
    193
    194	kvm->memsize = mem_size;
    195	kvm->mem = mmap(NULL, kvm->memsize, PROT_READ | PROT_WRITE,
    196		MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    197	if (!kvm->mem) err(1, "mmap kvm->mem");
    198	/* nop slide oob to detect errors quickly */
    199	memset(kvm->mem, 0x90, kvm->memsize);
    200	assert(code_size <= kvm->memsize);
    201	memcpy(kvm->mem, code, code_size);
    202
    203	memset(&region, 0, sizeof(region));
    204	region.slot = 0;
    205	region.memory_size = kvm->memsize;
    206	region.guest_phys_addr = 0x0000;
    207	region.userspace_addr = (uintptr_t) kvm->mem;
    208	ret = ioctl(kvm->vmfd, KVM_SET_USER_MEMORY_REGION, &region);
    209	if (ret == -1) err(1, "KVM_SET_USER_MEMORY_REGION");
    210}
    211
    212void
    213kvm_create_vcpu(struct kvm *kvm)
    214{
    215	int ret;
    216
    217	kvm->vcpufd = ioctl(kvm->vmfd, KVM_CREATE_VCPU, 0);
    218	if (kvm->vcpufd < 0) err(1, "KVM_CREATE_VCPU");
    219
    220	ret = ioctl(kvm_dev, KVM_GET_VCPU_MMAP_SIZE, NULL);
    221	if (ret == -1) err(1, "KVM_GET_VCPU_MMAP_SIZE");
    222	if (ret < sizeof(struct kvm_run))
    223		errx(1, "KVM_GET_VCPU_MMAP_SIZE too small");
    224	kvm->runsize = ret;
    225	kvm->run = mmap(NULL, kvm->runsize, PROT_READ | PROT_WRITE,
    226		MAP_SHARED, kvm->vcpufd, 0);
    227	if (!kvm->run) err(1, "mmap kvm->run");
    228}
    229
    230void
    231kvm_init_regs(struct kvm *kvm)
    232{
    233	struct kvm_regs regs;
    234	struct kvm_sregs sregs;
    235	int ret;
    236
    237	/* Initialize segment regs */
    238	memset(&sregs, 0, sizeof(sregs));
    239	ret = ioctl(kvm->vcpufd, KVM_GET_SREGS, &sregs);
    240	if (ret == -1) err(1, "KVM_GET_SREGS");
    241	sregs.cs.base = 0;
    242	sregs.cs.selector = 0;
    243	ret = ioctl(kvm->vcpufd, KVM_SET_SREGS, &sregs);
    244	if (ret == -1) err(1, "KVM_SET_SREGS");
    245
    246	/* Initialize rest of registers */
    247	memset(&regs, 0, sizeof(regs));
    248	regs.rip = 0;
    249	regs.rsp = kvm->memsize - 8;
    250	regs.rbp = kvm->memsize - 8;
    251	ret = ioctl(kvm->vcpufd, KVM_SET_REGS, &regs);
    252	if (ret == -1) err(1, "KVM_SET_REGS");
    253}
    254
    255void
    256kvm_init(struct kvm *kvm, struct guest *guest)
    257{
    258	kvm_create_vm(kvm);
    259
    260	kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size);
    261
    262	kvm_create_vcpu(kvm);
    263
    264	kvm_init_regs(kvm);
    265}
    266
    267void
    268sev_kvm_init(struct kvm *kvm, struct guest *guest)
    269{
    270	struct kvm_sev_launch_update_data update;
    271	struct kvm_sev_launch_start start;
    272	int ret, fwerr;
    273
    274	kvm_create_vm(kvm);
    275
    276	kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size);
    277
    278	/* Enable SEV for vm */
    279	ret = sev_ioctl(kvm->vmfd, KVM_SEV_INIT, NULL, &fwerr);
    280	if (ret == -1) errx(1, "KVM_SEV_INIT: (%s) %s",
    281		strerror(errno), sev_fwerr_str(fwerr));
    282
    283	kvm_create_vcpu(kvm);
    284
    285	kvm_init_regs(kvm);
    286
    287	/* Generate encryption keys and set policy */
    288	memset(&start, 0, sizeof(start));
    289	start.handle = 0;
    290	start.policy = 0;
    291	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_START, &start, &fwerr);
    292	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_START: (%s) %s",
    293		strerror(errno), sev_fwerr_str(fwerr));
    294
    295	/* Prepare the vm memory (by encrypting it) */
    296	memset(&update, 0, sizeof(update));
    297	update.uaddr = (uintptr_t) kvm->mem;
    298	update.len = kvm->memsize;
    299	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr);
    300	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s",
    301		strerror(errno), sev_fwerr_str(fwerr));
    302
    303	/* Collect a measurement (necessary) */
    304	sev_get_measure(kvm->vmfd);
    305
    306	/* Finalize launch process */
    307	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_FINISH, 0, &fwerr);
    308	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_FINISH: (%s) %s",
    309		strerror(errno), sev_fwerr_str(fwerr));
    310
    311	ret = sev_guest_state(kvm->vmfd, start.handle);
    312	if (ret != GSTATE_RUNNING)
    313		errx(1, "Bad guest state: %s", sev_gstate_str(fwerr));
    314}
    315
    316void
    317sev_es_kvm_init(struct kvm *kvm, struct guest *guest)
    318{
    319	struct kvm_sev_launch_update_data update;
    320	struct kvm_sev_launch_start start;
    321	int ret, fwerr;
    322
    323	kvm_create_vm(kvm);
    324
    325	kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size);
    326
    327	/* Enable SEV for vm */
    328	ret = sev_ioctl(kvm->vmfd, KVM_SEV_ES_INIT, NULL, &fwerr);
    329	if (ret == -1) errx(1, "KVM_SEV_ES_INIT: (%s) %s",
    330		strerror(errno), sev_fwerr_str(fwerr));
    331
    332	kvm_create_vcpu(kvm);
    333
    334	kvm_init_regs(kvm);
    335
    336	/* Generate encryption keys and set policy */
    337	memset(&start, 0, sizeof(start));
    338	start.handle = 0;
    339	start.policy = 1 << 2; /* require SEV-ES */
    340	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_START, &start, &fwerr);
    341	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_START: (%s) %s",
    342		strerror(errno), sev_fwerr_str(fwerr));
    343
    344	/* Prepare the vm memory (by encrypting it) */
    345	memset(&update, 0, sizeof(update));
    346	update.uaddr = (uintptr_t) kvm->mem;
    347	update.len = kvm->memsize;
    348	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fwerr);
    349	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_DATA: (%s) %s",
    350		strerror(errno), sev_fwerr_str(fwerr));
    351
    352	/* Prepare the vm save area */
    353	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fwerr);
    354	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_UPDATE_VMSA: (%s) %s",
    355		strerror(errno), sev_fwerr_str(fwerr));
    356
    357	/* Collect a measurement (necessary) */
    358	sev_get_measure(kvm->vmfd);
    359
    360	/* Finalize launch process */
    361	ret = sev_ioctl(kvm->vmfd, KVM_SEV_LAUNCH_FINISH, 0, &fwerr);
    362	if (ret == -1) errx(1, "KVM_SEV_LAUNCH_FINISH: (%s) %s",
    363		strerror(errno), sev_fwerr_str(fwerr));
    364
    365	ret = sev_guest_state(kvm->vmfd, start.handle);
    366	if (ret != GSTATE_RUNNING)
    367		errx(1, "Bad guest state: %s", sev_gstate_str(fwerr));
    368}
    369
    370void
    371sev_snp_kvm_init(struct kvm *kvm, struct guest *guest)
    372{
    373	struct kvm_sev_snp_launch_update update;
    374	struct kvm_sev_snp_launch_start start;
    375	struct kvm_sev_snp_launch_finish finish;
    376	struct kvm_enc_region enc_region;
    377	struct kvm_snp_init init;
    378	int ret, fwerr;
    379
    380	kvm_create_vm(kvm);
    381
    382	kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size);
    383
    384	/* Enable SEV for vm */
    385	memset(&init, 0, sizeof(init));
    386	ret = sev_ioctl(kvm->vmfd, KVM_SEV_SNP_INIT, &init, &fwerr);
    387	if (ret == -1) errx(1, "KVM_SEV_SNP_INIT: (%s) %s",
    388		strerror(errno), sev_fwerr_str(fwerr));
    389
    390	/* Register memory region */
    391	memset(&enc_region, 0, sizeof(enc_region));
    392	enc_region.addr = (uintptr_t) kvm->mem;
    393	enc_region.size = kvm->memsize;
    394	ret = ioctl(kvm->vmfd, KVM_MEMORY_ENCRYPT_REG_REGION, &enc_region);
    395	if (ret == -1) err(1, "KVM_MEMORY_ENCRYPT_REG_REGION");
    396
    397	kvm_create_vcpu(kvm);
    398
    399	kvm_init_regs(kvm);
    400
    401	/* Generate encryption keys and set policy */
    402	memset(&start, 0, sizeof(start));
    403	start.policy = 1 << 17; /* must be set */
    404	start.policy |= 1 << 19; /* allow debug */
    405	start.policy |= 1 << 16; /* allow simultaneous multi-threading */ 
    406	ret = sev_ioctl(kvm->vmfd, KVM_SEV_SNP_LAUNCH_START, &start, &fwerr);
    407	if (ret == -1) errx(1, "KVM_SEV_SNP_LAUNCH_START: (%s) %s",
    408		strerror(errno), sev_fwerr_str(fwerr));
    409
    410	/* Prepare the vm memory */
    411	memset(&update, 0, sizeof(update));
    412	update.uaddr = (uintptr_t) kvm->mem;
    413	update.len = kvm->memsize;
    414	update.start_gfn = 0;
    415	update.page_type = KVM_SEV_SNP_PAGE_TYPE_NORMAL;
    416	ret = sev_ioctl(kvm->vmfd, KVM_SEV_SNP_LAUNCH_UPDATE, &update, &fwerr);
    417	if (ret == -1) errx(1, "KVM_SEV_SNP_LAUNCH_UPDATE: (%s) %s",
    418		strerror(errno), sev_fwerr_str(fwerr));
    419
    420	/* Finalize launch process */
    421	memset(&finish, 0, sizeof(finish));
    422	ret = sev_ioctl(kvm->vmfd, KVM_SEV_SNP_LAUNCH_FINISH, &finish, &fwerr);
    423	if (ret == -1) errx(1, "KVM_SEV_SNP_LAUNCH_FINISH: (%s) %s",
    424		strerror(errno), sev_fwerr_str(fwerr));
    425}
    426
    427void
    428kvm_deinit(struct kvm *kvm)
    429{
    430	close(kvm->vmfd);
    431	close(kvm->vcpufd);
    432	munmap(kvm->mem, kvm->memsize);
    433	munmap(kvm->run, kvm->runsize);
    434}
    435
    436uint64_t
    437vm_get_rip(void)
    438{
    439	struct cpc_sev_cmd cmd;
    440	int ret, fwerr;
    441
    442	cmd.id = SEV_CPC_GET_RIP;
    443	ret = sev_ioctl(MAIN_VMFD, KVM_SEV_CACHEPC, &cmd, &fwerr);
    444	if (ret == -1) errx(1, "KVM_SEV_CACHEPC: (%s) %s",
    445		strerror(errno), sev_fwerr_str(fwerr));
    446
    447	return cmd.data;
    448}
    449
    450void
    451parse_vmtype(int argc, const char **argv)
    452{
    453	vmtype = "kvm";
    454	if (argc > 1) vmtype = argv[1];
    455	if (strcmp(vmtype, "kvm") && strcmp(vmtype, "sev")
    456			&& strcmp(vmtype, "sev-es")
    457			&& strcmp(vmtype, "sev-snp"))
    458		errx(1, "invalid vm mode: %s", vmtype);
    459}
    460
    461void
    462vm_init(struct kvm *kvm, struct guest *guest)
    463{
    464	if (!guest->mem_size)
    465		guest->mem_size = L1_SIZE * 2;
    466
    467	if (!strcmp(vmtype, "kvm")) {
    468		kvm_init(kvm, guest);
    469	} else if (!strcmp(vmtype, "sev")) {
    470		sev_kvm_init(kvm, guest);
    471	} else if (!strcmp(vmtype, "sev-es")) {
    472		sev_es_kvm_init(kvm, guest);
    473	} else if (!strcmp(vmtype, "sev-snp")) {
    474		sev_snp_kvm_init(kvm, guest);
    475	} else {
    476		errx(1, "invalid version");
    477	}
    478}
    479
    480void
    481vm_deinit(struct kvm *kvm)
    482{
    483	kvm_deinit(kvm);
    484}
    485
    486void
    487kvm_setup_init(void)
    488{
    489	int ret;
    490
    491	kvm_dev = open("/dev/kvm", O_RDWR | O_CLOEXEC);
    492	if (kvm_dev < 0) err(1, "open /dev/kvm");
    493
    494	sev_dev = open("/dev/sev", O_RDWR | O_CLOEXEC);
    495	if (sev_dev < 0) err(1, "open /dev/sev");
    496
    497	/* ensure we have the stable version of the api */
    498	ret = ioctl(kvm_dev, KVM_GET_API_VERSION, NULL);
    499	if (ret == -1) err(1, "KVM_GET_API_VERSION");
    500	if (ret != 12) errx(1, "KVM_GET_API_VERSION %d, expected 12", ret);
    501
    502}
    503
    504void
    505kvm_setup_deinit(void)
    506{
    507	close(kvm_dev);
    508	kvm_dev = -1;
    509
    510	close(sev_dev);
    511	sev_dev = -1;
    512}