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 65daf1cb353f4ba5e2f08ccbce6b0d5220b0099a
parent e4762c2cdefacf13d26967b7e5f0735c2748026b
Author: Louis Burda <quent.burda@gmail.com>
Date:   Tue, 24 Jan 2023 16:17:45 +0100

Create flat binaries to support more complex guests

Diffstat:
MMakefile | 31++++++++++++++++++++-----------
MREADME | 2+-
Mcachepc/kvm.c | 1-
Mtest/.gitignore | 8++++++++
Atest/guest.lds | 13+++++++++++++
Atest/kvm-eviction-with_guest.S | 14++++++++++++++
Atest/kvm-eviction-without_guest.S | 12++++++++++++
Mtest/kvm-eviction.c | 15++++++++-------
Dtest/kvm-eviction_guest.S | 26--------------------------
Mtest/kvm-pagestep.c | 8++++----
Mtest/kvm-pagestep_guest.S | 21++++++++-------------
Mtest/kvm-step.c | 8++++----
Mtest/kvm-step_guest.S | 28++++++++++++----------------
Mtest/kvm.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mtest/kvm.h | 25++++++++++++++++---------
Rtest/qemu-aes_host.c -> test/qemu-aes.c | 0
Atest/qemu-eviction.c | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtest/qemu-eviction_host.c | 361-------------------------------------------------------------------------------
Mtest/util.c | 1+
Mutil/disasm | 4++--
20 files changed, 536 insertions(+), 482 deletions(-)

diff --git a/Makefile b/Makefile @@ -6,9 +6,11 @@ JOBS ?= $(CORES) PWD := $(shell pwd) BINS = test/eviction test/kvm-eviction -BINS += test/kvm-step test/kvm-pagestep -# BINS += test/qemu-eviction_guest test/qemu-eviction_host -# BINS += test/qemu-aes_guest test/qemu-aes_host +BINS += test/kvm-eviction-with_guest test/kvm-eviction-without_guest +BINS += test/kvm-step test/kvm-step_guest +BINS += test/kvm-pagestep test/kvm-pagestep_guest +#BINS += test/qemu-eviction_guest test/qemu-eviction +# BINS += test/qemu-aes_guest test/qemu-aes BINS += util/debug util/reset CFLAGS = -I . -I linux/usr/include @@ -17,7 +19,8 @@ CFLAGS += -fsanitize=address LDLIBS = -lpthread -CACHEPC_UAPI = cachepc/uapi.h cachepc/const.h +TEST_SRCS = test/util.c test/util.h test/kvm.c test/kvm.h +TEST_SRCS += cachepc/uapi.h cachepc/const.h all: build $(BINS) @@ -57,19 +60,25 @@ prep: util/%: util/%.c $(CACHEPC_UAPI) -test/eviction: test/eviction.c test/util.c $(CACHEPC_UAPI) +test/%.o: test/%.c + $(CC) -c -o $@ $^ $(CFLAGS) + +test/%.o: test/%.S + $(CC) -c -o $@ $^ $(CFLAGS) + +test/%_guest: test/%_guest.o test/guest.lds + $(LD) -Ttest/guest.lds -o $@ $< + +test/eviction: test/eviction.c test/util.c $(TEST_SRCS) $(CC) -o $@ $(filter %.c,$^) $(filter %.S,$^) $(CFLAGS) $(LDLIBS) -test/kvm-eviction: test/kvm-eviction.c test/kvm-eviction_guest.S test/util.c \ - test/util.h test/kvm.c test/kvm.h test/kvm-eviction.h $(CACHEPC_UAPI) +test/kvm-eviction: test/kvm-eviction.c test/kvm-eviction.h $(TEST_SRCS) $(CC) -o $@ $(filter %.c,$^) $(filter %.S,$^) $(CFLAGS) $(LDLIBS) -test/kvm-step: test/kvm-step.c test/kvm-step_guest.S \ - test/util.c test/util.h test/kvm.c test/kvm.h $(CACHEPC_UAPI) +test/kvm-step: test/kvm-step.c $(TEST_SRCS) $(CC) -o $@ $(filter %.c,$^) $(filter %.S,$^) $(CFLAGS) $(LDLIBS) -test/kvm-pagestep: test/kvm-pagestep.c test/kvm-pagestep_guest.S \ - test/util.c test/util.h test/kvm.c test/kvm.h $(CACHEPC_UAPI) +test/kvm-pagestep: test/kvm-pagestep.c $(TEST_SRCS) $(CC) -o $@ $(filter %.c,$^) $(filter %.S,$^) $(CFLAGS) $(LDLIBS) .PHONY: all clean host build load prep diff --git a/README b/README @@ -1,7 +1,7 @@ CachePC ======= -This repository contains proof-of-concept code for a novel cache side-channel +This repository contains proof-of-concept code for a cache side-channel attack dubbed PRIME+COUNT that we demonstrate can be used to circumvent AMD's latest secure virtualization solution SEV-SNP to access sensitive guest information. diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -513,7 +513,6 @@ cachepc_kvm_track_mode_ioctl(void __user *arg_user) cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_ACCESS); cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_WRITE); - cachepc_apic_timer = 0; cachepc_apic_oneshot = false; cachepc_singlestep = false; cachepc_singlestep_reset = false; diff --git a/test/.gitignore b/test/.gitignore @@ -1,7 +1,15 @@ +*.o eviction kvm-eviction +kvm-eviction-with_guest +kvm-eviction-without_guest kvm-step +kvm-step_guest kvm-pagestep +kvm-pagestep_guest qemu-eviction +qemu-eviction_guest qemu-aes +qemu-aes_guest qemu-poc +qemu-poc_guest diff --git a/test/guest.lds b/test/guest.lds @@ -0,0 +1,13 @@ +OUTPUT_FORMAT(binary) + +SECTIONS +{ + .text : { + . = 0; + *(.text) + } + + .data : { + *(.data) + } +} diff --git a/test/kvm-eviction-with_guest.S b/test/kvm-eviction-with_guest.S @@ -0,0 +1,14 @@ +#include "test/kvm-eviction.h" +#include "cachepc/const.h" + +.text +.align(16) +.code16gcc + +guest: + mov $(L1_LINESIZE * (L1_SETS + TARGET_SET)), %bx + movb (%bx), %bl + hlt + + jmp guest + diff --git a/test/kvm-eviction-without_guest.S b/test/kvm-eviction-without_guest.S @@ -0,0 +1,12 @@ +#include "test/kvm-eviction.h" +#include "cachepc/const.h" + +.text +.align(16) +.code16gcc + +guest: + hlt + + jmp guest + diff --git a/test/kvm-eviction.c b/test/kvm-eviction.c @@ -18,11 +18,6 @@ #define TARGET_CORE 2 #define SECONDARY_CORE 3 -extern uint8_t guest_with_start[]; -extern uint8_t guest_with_stop[]; -extern uint8_t guest_without_start[]; -extern uint8_t guest_without_stop[]; - void collect(struct kvm *kvm, uint8_t *counts) { @@ -47,6 +42,7 @@ int main(int argc, const char **argv) { struct kvm vms[2]; + struct guest guests[2]; uint8_t counts[2][SAMPLE_COUNT][L1_SETS]; uint8_t baseline[L1_SETS]; int i, k, ret; @@ -64,8 +60,13 @@ main(int argc, const char **argv) kvm_setup_init(); - vm_init(&vms[WITH], guest_with_start, guest_with_stop); - vm_init(&vms[WITHOUT], guest_without_start, guest_without_stop); + guest_init(&guests[WITH], "test/kvm-eviction-with_guest"); + vm_init(&vms[WITH], &guests[WITH]); + guest_deinit(&guests[WITH]); + + guest_init(&guests[WITHOUT], "test/kvm-eviction-without_guest"); + vm_init(&vms[WITHOUT], &guests[WITHOUT]); + guest_deinit(&guests[WITHOUT]); /* reset kernel module state */ ret = ioctl(kvm_dev, KVM_CPC_RESET); diff --git a/test/kvm-eviction_guest.S b/test/kvm-eviction_guest.S @@ -1,26 +0,0 @@ -#include "test/kvm-eviction.h" -#include "cachepc/const.h" - -.global guest_with_start -.global guest_with_stop - -.global guest_without_start -.global guest_without_stop - -.align(16) -.code16gcc - -guest_with_start: - mov $(L1_LINESIZE * (L1_SETS + TARGET_SET)), %bx - movb (%bx), %bl - hlt - - mov $0x00, %ax - jmp *%ax -guest_with_stop: - -guest_without_start: - hlt - mov $0x00, %ax - jmp *%ax -guest_without_stop: diff --git a/test/kvm-pagestep.c b/test/kvm-pagestep.c @@ -18,9 +18,6 @@ #define TARGET_CORE 2 #define SECONDARY_CORE 3 -extern uint8_t guest_start[]; -extern uint8_t guest_stop[]; - static int child; uint64_t @@ -58,6 +55,7 @@ int main(int argc, const char **argv) { struct ipc *ipc; + struct guest guest; struct kvm kvm; uint64_t eventcnt; uint32_t arg; @@ -82,7 +80,9 @@ main(int argc, const char **argv) if (child == 0) { pin_process(0, TARGET_CORE, true); - vm_init(&kvm, guest_start, guest_stop); + guest_init(&guest, "test/kvm-pagestep_guest"); + vm_init(&kvm, &guest); + guest_deinit(&guest); /* reset kernel module state */ ret = ioctl(kvm_dev, KVM_CPC_RESET, NULL); diff --git a/test/kvm-pagestep_guest.S b/test/kvm-pagestep_guest.S @@ -2,28 +2,23 @@ #define TARGET_SET 15 -.global guest_start -.global guest_stop - +.text .align(16) .code16gcc -guest_start: +guest: .rept L1_SIZE - nop + nop .endr - mov $0x01, %bx - cmp $0x00, %bx + mov $0x01, %bx + cmp $0x00, %bx - # NOTE: this needs to be a relative jmp - je skip + je skip .rept L1_LINESIZE * L1_SETS * 2 - nop + nop .endr skip: - mov $0x00, %ax - jmp *%ax -guest_stop: + jmp guest diff --git a/test/kvm-step.c b/test/kvm-step.c @@ -18,9 +18,6 @@ #define TARGET_CORE 2 #define SECONDARY_CORE 3 -extern uint8_t guest_start[]; -extern uint8_t guest_stop[]; - static int child; uint64_t @@ -64,6 +61,7 @@ int main(int argc, const char **argv) { struct ipc *ipc; + struct guest guest; struct kvm kvm; uint8_t baseline[L1_SETS]; struct cpc_event event; @@ -90,7 +88,9 @@ main(int argc, const char **argv) if (child == 0) { pin_process(0, TARGET_CORE, true); - vm_init(&kvm, guest_start, guest_stop); + guest_init(&guest, "test/kvm-step_guest"); + vm_init(&kvm, &guest); + guest_deinit(&guest); /* reset kernel module state */ ret = ioctl(kvm_dev, KVM_CPC_RESET, NULL); diff --git a/test/kvm-step_guest.S b/test/kvm-step_guest.S @@ -2,26 +2,22 @@ #define TARGET_SET 15 -.global guest_start -.global guest_stop - +.text .align(16) .code16gcc -guest_start: - mov $(L1_LINESIZE * (L1_SETS + 11)), %bx - movb (%bx), %bl - hlt +guest: + mov $(L1_LINESIZE * (L1_SETS + 11)), %bx + movb (%bx), %bl + hlt - mov $(L1_LINESIZE * (L1_SETS + 13)), %bx - movb (%bx), %bl - hlt + mov $(L1_LINESIZE * (L1_SETS + 13)), %bx + movb (%bx), %bl + hlt - mov $(L1_LINESIZE * (L1_SETS + 15)), %bx - movb (%bx), %bl - hlt + mov $(L1_LINESIZE * (L1_SETS + 15)), %bx + movb (%bx), %bl + hlt - mov $0x00, %ax - jmp *%ax -guest_stop: + jmp guest diff --git a/test/kvm.c b/test/kvm.c @@ -204,6 +204,35 @@ snp_dbg_decrypt_rip(int vmfd) } void +guest_init(struct guest *guest, const char *filename) +{ + FILE *f; + + f = fopen(filename, "r"); + if (!f) err(1, "fopen"); + + fseek(f, 0, SEEK_END); + guest->code_size = ftell(f); + fseek(f, 0, SEEK_SET); + + guest->code = malloc(guest->code_size); + if (!guest->code) err(1, "malloc"); + + if (!fread(guest->code, guest->code_size, 1, f)) + errx(1, "read guest"); + + guest->mem_size = 0; + + fclose(f); +} + +void +guest_deinit(struct guest *guest) +{ + free(guest->code); +} + +void kvm_create_vm(struct kvm *kvm) { kvm->vmfd = ioctl(kvm_dev, KVM_CREATE_VM, 0); @@ -211,20 +240,19 @@ kvm_create_vm(struct kvm *kvm) } void -kvm_init_memory(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) +kvm_init_memory(struct kvm *kvm, size_t mem_size, void *code, size_t code_size) { struct kvm_userspace_memory_region region; int ret; - kvm->memsize = ramsize; + kvm->memsize = mem_size; kvm->mem = mmap(NULL, kvm->memsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (!kvm->mem) err(1, "mmap kvm->mem"); /* nop slide oob to detect errors quickly */ memset(kvm->mem, 0x90, kvm->memsize); - assert(code_stop - code_start <= kvm->memsize); - memcpy(kvm->mem, code_start, code_stop - code_start); + assert(code_size <= kvm->memsize); + memcpy(kvm->mem, code, code_size); memset(&region, 0, sizeof(region)); region.slot = 0; @@ -279,12 +307,11 @@ kvm_init_regs(struct kvm *kvm) } void -kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) +kvm_init(struct kvm *kvm, struct guest *guest) { kvm_create_vm(kvm); - kvm_init_memory(kvm, ramsize, code_start, code_stop); + kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size); kvm_create_vcpu(kvm); @@ -292,8 +319,7 @@ kvm_init(struct kvm *kvm, size_t ramsize, } void -sev_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) +sev_kvm_init(struct kvm *kvm, struct guest *guest) { struct kvm_sev_launch_update_data update; struct kvm_sev_launch_start start; @@ -301,7 +327,7 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, kvm_create_vm(kvm); - kvm_init_memory(kvm, ramsize, code_start, code_stop); + kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size); /* Enable SEV for vm */ ret = sev_ioctl(kvm->vmfd, KVM_SEV_INIT, NULL, &fwerr); @@ -323,7 +349,7 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, /* Prepare the vm memory (by encrypting it) */ memset(&update, 0, sizeof(update)); update.uaddr = (uintptr_t) kvm->mem; - update.len = ramsize; + update.len = kvm->memsize; 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)); @@ -342,8 +368,7 @@ sev_kvm_init(struct kvm *kvm, size_t ramsize, } void -sev_es_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) +sev_es_kvm_init(struct kvm *kvm, struct guest *guest) { struct kvm_sev_launch_update_data update; struct kvm_sev_launch_start start; @@ -351,7 +376,7 @@ sev_es_kvm_init(struct kvm *kvm, size_t ramsize, kvm_create_vm(kvm); - kvm_init_memory(kvm, ramsize, code_start, code_stop); + kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size); /* Enable SEV for vm */ ret = sev_ioctl(kvm->vmfd, KVM_SEV_ES_INIT, NULL, &fwerr); @@ -373,7 +398,7 @@ sev_es_kvm_init(struct kvm *kvm, size_t ramsize, /* Prepare the vm memory (by encrypting it) */ memset(&update, 0, sizeof(update)); update.uaddr = (uintptr_t) kvm->mem; - update.len = ramsize; + update.len = kvm->memsize; 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)); @@ -397,8 +422,7 @@ sev_es_kvm_init(struct kvm *kvm, size_t ramsize, } void -sev_snp_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop) +sev_snp_kvm_init(struct kvm *kvm, struct guest *guest) { struct kvm_sev_snp_launch_update update; struct kvm_sev_snp_launch_start start; @@ -409,7 +433,7 @@ sev_snp_kvm_init(struct kvm *kvm, size_t ramsize, kvm_create_vm(kvm); - kvm_init_memory(kvm, ramsize, code_start, code_stop); + kvm_init_memory(kvm, guest->mem_size, guest->code, guest->code_size); /* Enable SEV for vm */ memset(&init, 0, sizeof(init)); @@ -440,7 +464,7 @@ sev_snp_kvm_init(struct kvm *kvm, size_t ramsize, /* Prepare the vm memory */ memset(&update, 0, sizeof(update)); update.uaddr = (uintptr_t) kvm->mem; - update.len = ramsize; + update.len = kvm->memsize; update.start_gfn = 0; update.page_type = KVM_SEV_SNP_PAGE_TYPE_NORMAL; ret = sev_ioctl(kvm->vmfd, KVM_SEV_SNP_LAUNCH_UPDATE, &update, &fwerr); @@ -495,19 +519,19 @@ vm_get_rip(struct kvm *kvm) } void -vm_init(struct kvm *kvm, void *code_start, void *code_end) +vm_init(struct kvm *kvm, struct guest *guest) { - size_t ramsize; + if (!guest->mem_size) + guest->mem_size = L1_SIZE * 2; - ramsize = L1_SIZE * 2; if (!strcmp(vmtype, "kvm")) { - kvm_init(kvm, ramsize, code_start, code_end); + kvm_init(kvm, guest); } else if (!strcmp(vmtype, "sev")) { - sev_kvm_init(kvm, ramsize, code_start, code_end); + sev_kvm_init(kvm, guest); } else if (!strcmp(vmtype, "sev-es")) { - sev_es_kvm_init(kvm, ramsize, code_start, code_end); + sev_es_kvm_init(kvm, guest); } else if (!strcmp(vmtype, "sev-snp")) { - sev_snp_kvm_init(kvm, ramsize, code_start, code_end); + sev_snp_kvm_init(kvm, guest); } else { errx(1, "invalid version"); } diff --git a/test/kvm.h b/test/kvm.h @@ -1,5 +1,7 @@ #pragma once +#include "util.h" + #include <stdint.h> #include <stdlib.h> @@ -22,6 +24,12 @@ struct kvm { struct kvm_run *run; }; +struct guest { + void *code; + size_t code_size; + size_t mem_size; +}; + const char *sev_fwerr_str(int code); const char *sev_gstate_str(int code); @@ -33,19 +41,18 @@ uint64_t sev_dbg_decrypt_rip(int vmfd); void snp_dbg_decrypt(int vmfd, void *src, void *dst, size_t size); uint64_t snp_dbg_decrypt_rip(int vmfd); -void kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop); -void sev_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop); -void sev_es_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop); -void sev_snp_kvm_init(struct kvm *kvm, size_t ramsize, - void *code_start, void *code_stop); +void guest_init(struct guest *guest, const char *filename); +void guest_deinit(struct guest *guest); + +void kvm_init(struct kvm *kvm, struct guest *guest); +void sev_kvm_init(struct kvm *kvm, struct guest *guest); +void sev_es_kvm_init(struct kvm *kvm, struct guest *guest); +void sev_snp_kvm_init(struct kvm *kvm, struct guest *guest); void kvm_deinit(struct kvm *kvm); void parse_vmtype(int argc, const char **argv); uint64_t vm_get_rip(struct kvm *kvm); -void vm_init(struct kvm *kvm, void *code_start, void *code_end); +void vm_init(struct kvm *kvm, struct guest *guest); void vm_deinit(struct kvm *kvm); void kvm_setup_init(void); diff --git a/test/qemu-aes_host.c b/test/qemu-aes.c diff --git a/test/qemu-eviction.c b/test/qemu-eviction.c @@ -0,0 +1,362 @@ +#define _GNU_SOURCE + +#include "cachepc/uapi.h" + +#include <linux/psp-sev.h> +#include <linux/kvm.h> +#include <sys/syscall.h> +#include <sys/ioctl.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <dirent.h> +#include <assert.h> +#include <errno.h> +#include <err.h> +#include <fcntl.h> +#include <sched.h> +#include <dirent.h> +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdarg.h> + +#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) +#define MIN(a,b) ((a) > (b) ? (b) : (a)) + +#define TARGET_CORE 2 +#define SECONDARY_CORE 3 + +#define TARGET_SET 15 + +/* ioctl dev fds */ +static int kvm_dev; +static int faultcnt; + +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"); +} + +bool +pin_process(pid_t pid, int cpu, bool assert) +{ + cpu_set_t cpuset; + int ret; + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + ret = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset); + if (ret < 0) { + if (assert) err(1, "sched_setaffinity"); + return false; + } + + return true; +} + +cpc_msrmt_t * +read_counts() +{ + cpc_msrmt_t *counts; + int i, ret; + + counts = malloc(L1_SETS * sizeof(cpc_msrmt_t)); + if (!counts) err(1, "malloc"); + + ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); + if (ret) err(1, "ioctl READ_COUNTS"); + + for (i = 0; i < L1_SETS; i++) { + if (counts[i] > 8) + errx(1, "Invalid counts set %i", i); + } + + return counts; +} + +void +print_counts(cpc_msrmt_t *counts) +{ + int i; + + for (i = 0; i < 64; i++) { + if (i % 16 == 0 && i) + printf("\n"); + if (counts[i] == 1) + printf("\x1b[38;5;88m"); + else if (counts[i] > 1) + printf("\x1b[38;5;196m"); + printf("%2i ", i); + if (counts[i] > 0) + printf("\x1b[0m"); + } + printf("\n"); +} + +void +print_counts_raw(cpc_msrmt_t *counts) +{ + int i; + + for (i = 0; i < 64; i++) { + if (i % 16 == 0 && i) + printf("\n"); + if (counts[i] == 1) + printf("\x1b[38;5;88m"); + else if (counts[i] > 1) + printf("\x1b[38;5;196m"); + printf("%02X ", (uint8_t) counts[i]); + if (counts[i] > 0) + printf("\x1b[0m"); + } + printf("\n"); +} + +int +monitor(bool baseline) +{ + struct cpc_event event; + cpc_msrmt_t counts[64]; + uint64_t inst_fault_gfn; + uint64_t read_fault_gfn; + uint64_t arg; + int ret, i; + + /* Get page fault info */ + ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); + if (ret) { + if (errno == EAGAIN) + return 0; + perror("ioctl POLL_EVENT"); + return 1; + } + + if (event.type == CPC_EVENT_CPUID) { + printf("CPUID EVENT\n"); + if (event.guest.type == CPC_GUEST_START_TRACK) { + ret = ioctl(kvm_dev, KVM_CPC_TRACK_EXEC_CUR, &inst_fault_gfn); + if (ret) err(1, "ioctl TRACK_EXEC_CUR"); + + printf("CPUID INST PAGE: %lu\n", inst_fault_gfn); + + arg = inst_fault_gfn; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_START, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_START"); + + arg = inst_fault_gfn+8; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_END, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_END"); + } else if (event.guest.type == CPC_GUEST_STOP_TRACK) { + arg = 0; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_START, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_START"); + + arg = 0; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_END, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_END"); + } + + faultcnt++; + } else if (event.type == CPC_EVENT_TRACK_STEP) { + printf("STEP EVENT\n"); + + ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); + if (ret) err(1, "ioctl READ_COUNTS"); + + inst_fault_gfn = 0; + read_fault_gfn = 0; + for (i = 0; i < event.step.fault_count; i++) { + if ((event.step.fault_errs[i] & 0b11111) == 0b10100) + inst_fault_gfn = event.step.fault_gfns[i]; + else if ((event.step.fault_errs[i] & 0b00110) == 0b00100) + read_fault_gfn = event.step.fault_gfns[i]; + } + + if (!baseline) { + printf("Event: cnt:%llu inst:%lu data:%lu retired:%llu\n", + event.step.fault_count, inst_fault_gfn, + read_fault_gfn, event.step.retinst); + print_counts(counts); + printf("\n"); + } + + for (i = 0; i < 64; i++) { + if (counts[i] > 8) { + warnx("Invalid count for set %i (%llu)", + i, counts[i]); + counts[i] = 8; + } + } + + if (baseline) faultcnt++; + } else if (event.type == CPC_EVENT_TRACK_PAGE) { + printf("PAGE EVENT\n"); + + printf("Event: prev:%llu new:%llu retired:%llu\n", + event.page.inst_gfn_prev, event.page.inst_gfn, + event.page.retinst); + } + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "ioctl ACK_EVENT"); + + return 0; +} + +int +pgrep(const char *bin) +{ + char path[PATH_MAX]; + char buf[PATH_MAX]; + char *cmp; + struct dirent *ent; + FILE *f; + DIR *dir; + + dir = opendir("/proc"); + if (!dir) err(1, "opendir"); + + while ((ent = readdir(dir))) { + snprintf(path, sizeof(path), "/proc/%s/cmdline", ent->d_name); + f = fopen(path, "rb"); + if (!f) continue; + memset(buf, 0, sizeof(buf)); + fread(buf, 1, sizeof(buf), f); + if ((cmp = strrchr(buf, '/'))) + cmp += 1; + else + cmp = buf; + if (!strcmp(cmp, bin)) + return atoi(ent->d_name); + fclose(f); + } + + closedir(dir); + + return 0; +} + +int +main(int argc, const char **argv) +{ + pid_t pid; + uint32_t arg; + struct cpc_event event; + cpc_msrmt_t baseline[64]; + int ret, i; + + kvm_setup_init(); + + setvbuf(stdout, NULL, _IONBF, 0); + + pid = pgrep("qemu-system-x86_64"); + if (!pid) errx(1, "Failed to find qemu instance"); + printf("PID %i\n", pid); + + pin_process(pid, TARGET_CORE, true); + pin_process(0, TARGET_CORE, true); + + /* Setup needed performance counters */ + ret = ioctl(kvm_dev, KVM_CPC_SETUP_PMC, NULL); + if (ret < 0) err(1, "ioctl SETUP_PMC"); + + /* Reset previous tracking */ + ret = ioctl(kvm_dev, KVM_CPC_RESET_TRACKING, NULL); + if (ret) err(1, "ioctl RESET_TRACKING"); + + pin_process(0, SECONDARY_CORE, true); + printf("PINNED\n"); + + // arg = false; + // ret = ioctl(kvm_dev, KVM_CPC_SUB_BASELINE, &arg); + // if (ret) err(1, "ioctl SUB_BASELINE"); + + // arg = true; + // ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &arg); + // if (ret) err(1, "ioctl MEASURE_BASELINE"); + + // arg = KVM_PAGE_TRACK_ACCESS; + // ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &arg); + // if (ret) err(1, "ioctl TRACK_ALL"); + + // arg = CPC_TRACK_DATA_ACCESS; + // ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); + // if (ret) err(1, "ioctl TRACK_MODE"); + + // faultcnt = 0; + // while (faultcnt < 100) { + // if (monitor(true)) break; + // } + + // do { + // ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); + // if (ret && errno != EAGAIN) + // err(1, "ioctl POLL_EVENT"); + // } while (ret && errno == EAGAIN); + + // arg = KVM_PAGE_TRACK_ACCESS; + // ret = ioctl(kvm_dev, KVM_CPC_UNTRACK_ALL, &arg); + // if (ret) err(1, "ioctl UNTRACK_ALL"); + + arg = CPC_TRACK_EXEC; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); + if (ret) err(1, "ioctl TRACK_MODE"); + + arg = KVM_PAGE_TRACK_EXEC; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &arg); + if (ret) err(1, "ioctl TRACK_ALL"); + + // arg = false; + // ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &arg); + // if (ret) err(1, "ioctl MEASURE_BASELINE"); + + // ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); + // if (ret) err(1, "ioctl READ_BASELINE"); + + // printf("\n>>> BASELINE:\n"); + // print_counts(baseline); + // printf("\n"); + // print_counts_raw(baseline); + // printf("\n"); + + // /* Check baseline for saturated sets */ + // for (i = 0; i < 64; i++) { + // if (baseline[i] >= 8) + // errx(1, "!!! Baseline set %i full\n", i); + // } + + // arg = true; + // ret = ioctl(kvm_dev, KVM_CPC_SUB_BASELINE, &arg); + // if (ret) err(1, "ioctl SUB_BASELINE"); + + // ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + // if (ret) err(1, "ioctl ACK_EVENT"); + + faultcnt = 0; + while (faultcnt < 10) { + if (monitor(false)) break; + } + + arg = KVM_PAGE_TRACK_EXEC; + ret = ioctl(kvm_dev, KVM_CPC_UNTRACK_ALL, &arg); + if (ret) err(1, "ioctl UNTRACK_ALL"); + + kvm_setup_deinit(); +} + diff --git a/test/qemu-eviction_host.c b/test/qemu-eviction_host.c @@ -1,361 +0,0 @@ -#define _GNU_SOURCE - -#include "cachepc/uapi.h" - -#include <linux/psp-sev.h> -#include <linux/kvm.h> -#include <sys/syscall.h> -#include <sys/ioctl.h> -#include <sys/user.h> -#include <sys/wait.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#include <signal.h> -#include <dirent.h> -#include <assert.h> -#include <errno.h> -#include <err.h> -#include <fcntl.h> -#include <sched.h> -#include <dirent.h> -#include <string.h> -#include <stdbool.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdio.h> -#include <stdarg.h> - -#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) -#define MIN(a,b) ((a) > (b) ? (b) : (a)) - -#define TARGET_CORE 2 -#define SECONDARY_CORE 3 - -#define TARGET_SET 15 - -/* ioctl dev fds */ -static int kvm_dev; -static int faultcnt; - -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"); -} - -bool -pin_process(pid_t pid, int cpu, bool assert) -{ - cpu_set_t cpuset; - int ret; - - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); - ret = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset); - if (ret < 0) { - if (assert) err(1, "sched_setaffinity"); - return false; - } - - return true; -} - -cpc_msrmt_t * -read_counts() -{ - cpc_msrmt_t *counts; - int i, ret; - - counts = malloc(L1_SETS * sizeof(cpc_msrmt_t)); - if (!counts) err(1, "malloc"); - - ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret) err(1, "ioctl READ_COUNTS"); - - for (i = 0; i < L1_SETS; i++) { - if (counts[i] > 8) - errx(1, "Invalid counts set %i", i); - } - - return counts; -} - -void -print_counts(cpc_msrmt_t *counts) -{ - int i; - - for (i = 0; i < 64; i++) { - if (i % 16 == 0 && i) - printf("\n"); - if (counts[i] == 1) - printf("\x1b[38;5;88m"); - else if (counts[i] > 1) - printf("\x1b[38;5;196m"); - printf("%2i ", i); - if (counts[i] > 0) - printf("\x1b[0m"); - } - printf("\n"); -} - -void -print_counts_raw(cpc_msrmt_t *counts) -{ - int i; - - for (i = 0; i < 64; i++) { - if (i % 16 == 0 && i) - printf("\n"); - if (counts[i] == 1) - printf("\x1b[38;5;88m"); - else if (counts[i] > 1) - printf("\x1b[38;5;196m"); - printf("%02X ", (uint8_t) counts[i]); - if (counts[i] > 0) - printf("\x1b[0m"); - } - printf("\n"); -} - -int -monitor(bool baseline) -{ - struct cpc_event event; - cpc_msrmt_t counts[64]; - uint64_t inst_fault_gfn; - uint64_t read_fault_gfn; - uint64_t arg; - int ret, i; - - /* Get page fault info */ - ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret) { - if (errno == EAGAIN) - return 0; - perror("ioctl POLL_EVENT"); - return 1; - } - - if (event.type == CPC_EVENT_CPUID) { - printf("CPUID EVENT\n"); - if (event.guest.type == CPC_GUEST_START_TRACK) { - ret = ioctl(kvm_dev, KVM_CPC_TRACK_EXEC_CUR, &inst_fault_gfn); - if (ret) err(1, "ioctl TRACK_EXEC_CUR"); - - printf("CPUID INST PAGE: %lu\n", inst_fault_gfn); - - arg = inst_fault_gfn; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_START, &arg); - if (ret) err(1, "ioctl TRACK_RANGE_START"); - - arg = inst_fault_gfn+8; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_END, &arg); - if (ret) err(1, "ioctl TRACK_RANGE_END"); - } else if (event.guest.type == CPC_GUEST_STOP_TRACK) { - arg = 0; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_START, &arg); - if (ret) err(1, "ioctl TRACK_RANGE_START"); - - arg = 0; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_END, &arg); - if (ret) err(1, "ioctl TRACK_RANGE_END"); - } - - faultcnt++; - } else if (event.type == CPC_EVENT_TRACK_STEP) { - printf("STEP EVENT\n"); - - ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret) err(1, "ioctl READ_COUNTS"); - - inst_fault_gfn = 0; - read_fault_gfn = 0; - for (i = 0; i < event.step.fault_count; i++) { - if ((event.step.fault_errs[i] & 0b11111) == 0b10100) - inst_fault_gfn = event.step.fault_gfns[i]; - else if ((event.step.fault_errs[i] & 0b00110) == 0b00100) - read_fault_gfn = event.step.fault_gfns[i]; - } - - if (!baseline) { - printf("Event: cnt:%llu inst:%lu data:%lu retired:%llu\n", - event.step.fault_count, inst_fault_gfn, - read_fault_gfn, event.step.retinst); - print_counts(counts); - printf("\n"); - } - - for (i = 0; i < 64; i++) { - if (counts[i] > 8) { - warnx("Invalid count for set %i (%llu)", - i, counts[i]); - counts[i] = 8; - } - } - - if (baseline) faultcnt++; - } else if (event.type == CPC_EVENT_TRACK_PAGE) { - printf("PAGE EVENT\n"); - - printf("Event: prev:%llu new:%llu retired:%llu\n", - event.page.inst_gfn_prev, event.page.inst_gfn, - event.page.retinst); - } - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "ioctl ACK_EVENT"); - - return 0; -} - -int -pgrep(const char *bin) -{ - char path[PATH_MAX]; - char buf[PATH_MAX]; - char *cmp; - struct dirent *ent; - FILE *f; - DIR *dir; - - dir = opendir("/proc"); - if (!dir) err(1, "opendir"); - - while ((ent = readdir(dir))) { - snprintf(path, sizeof(path), "/proc/%s/cmdline", ent->d_name); - f = fopen(path, "rb"); - if (!f) continue; - memset(buf, 0, sizeof(buf)); - fread(buf, 1, sizeof(buf), f); - if ((cmp = strrchr(buf, '/'))) - cmp += 1; - else - cmp = buf; - if (!strcmp(cmp, bin)) - return atoi(ent->d_name); - fclose(f); - } - - closedir(dir); - - return 0; -} - -int -main(int argc, const char **argv) -{ - pid_t pid; - uint32_t arg; - struct cpc_event event; - cpc_msrmt_t baseline[64]; - int ret, i; - - kvm_dev = open("/dev/kvm", O_RDWR); - if (!kvm_dev) err(1, "open /dev/kvm"); - - setvbuf(stdout, NULL, _IONBF, 0); - - pid = pgrep("qemu-system-x86_64"); - if (!pid) errx(1, "Failed to find qemu instance"); - printf("PID %i\n", pid); - - pin_process(pid, TARGET_CORE, true); - pin_process(0, TARGET_CORE, true); - - /* Setup needed performance counters */ - ret = ioctl(kvm_dev, KVM_CPC_SETUP_PMC, NULL); - if (ret < 0) err(1, "ioctl SETUP_PMC"); - - /* Reset previous tracking */ - ret = ioctl(kvm_dev, KVM_CPC_RESET_TRACKING, NULL); - if (ret) err(1, "ioctl RESET_TRACKING"); - - pin_process(0, SECONDARY_CORE, true); - printf("PINNED\n"); - - // arg = false; - // ret = ioctl(kvm_dev, KVM_CPC_SUB_BASELINE, &arg); - // if (ret) err(1, "ioctl SUB_BASELINE"); - - // arg = true; - // ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &arg); - // if (ret) err(1, "ioctl MEASURE_BASELINE"); - - // arg = KVM_PAGE_TRACK_ACCESS; - // ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &arg); - // if (ret) err(1, "ioctl TRACK_ALL"); - - // arg = CPC_TRACK_DATA_ACCESS; - // ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - // if (ret) err(1, "ioctl TRACK_MODE"); - - // faultcnt = 0; - // while (faultcnt < 100) { - // if (monitor(true)) break; - // } - - // do { - // ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - // if (ret && errno != EAGAIN) - // err(1, "ioctl POLL_EVENT"); - // } while (ret && errno == EAGAIN); - - // arg = KVM_PAGE_TRACK_ACCESS; - // ret = ioctl(kvm_dev, KVM_CPC_UNTRACK_ALL, &arg); - // if (ret) err(1, "ioctl UNTRACK_ALL"); - - arg = CPC_TRACK_EXEC; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret) err(1, "ioctl TRACK_MODE"); - - arg = KVM_PAGE_TRACK_EXEC; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &arg); - if (ret) err(1, "ioctl TRACK_ALL"); - - // arg = false; - // ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &arg); - // if (ret) err(1, "ioctl MEASURE_BASELINE"); - - // ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); - // if (ret) err(1, "ioctl READ_BASELINE"); - - // printf("\n>>> BASELINE:\n"); - // print_counts(baseline); - // printf("\n"); - // print_counts_raw(baseline); - // printf("\n"); - - // /* Check baseline for saturated sets */ - // for (i = 0; i < 64; i++) { - // if (baseline[i] >= 8) - // errx(1, "!!! Baseline set %i full\n", i); - // } - - // arg = true; - // ret = ioctl(kvm_dev, KVM_CPC_SUB_BASELINE, &arg); - // if (ret) err(1, "ioctl SUB_BASELINE"); - - // ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - // if (ret) err(1, "ioctl ACK_EVENT"); - - faultcnt = 0; - while (faultcnt < 10) { - if (monitor(false)) break; - } - - arg = KVM_PAGE_TRACK_EXEC; - ret = ioctl(kvm_dev, KVM_CPC_UNTRACK_ALL, &arg); - if (ret) err(1, "ioctl UNTRACK_ALL"); -} - diff --git a/test/util.c b/test/util.c @@ -192,3 +192,4 @@ ipc_wait_parent(struct ipc *ipc) ipc->has_sig_parent = false; pthread_mutex_unlock(&ipc->lock); } + diff --git a/util/disasm b/util/disasm @@ -1,12 +1,12 @@ #!/bin/sh if [ $# -lt 2 ]; then - echo "Usage: guest_asm FILE FUNC (guest)" + echo "Usage: guest_asm FILE FUNC" exit 1 fi ARCH="i386" -if [ "$3" = "guest" ]; then +if [ "$2" = "guest" ]; then ARCH="i8086" fi