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 5e99ea7f111134c7a1c7103590b711d59edafa9b
parent 05a4fbf680b806327a9c53525e9d8716dcdb55f3
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 11 Dec 2022 02:51:47 +0100

Adding support for exec based tracking (unstable)

Diffstat:
M.gitmodules | 3+++
MMakefile | 28++++++++++++++++++----------
Mcachepc/cachepc.h | 26+++++++++++++++++---------
Mcachepc/event.c | 42+++++++++++++++++++++++++++++++-----------
Mcachepc/event.h | 4++--
Mcachepc/kvm.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mcachepc/mmu.c | 143+++++++++++++++++++++++++++++--------------------------------------------------
Mcachepc/track.c | 10+++++-----
Mcachepc/uapi.h | 23+++++++++--------------
Alinux | 1+
Mpatch.diff | 344++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mtest/.gitignore | 1-
Mtest/access-detect_guest.c | 6+++---
Mtest/access-detect_host.c | 146++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mtest/sevstep.c | 30+++++++++++-------------------
Autil/.gitignore | 2++
Rtest/debug.c -> util/debug.c | 0
Autil/reset.c | 29+++++++++++++++++++++++++++++
18 files changed, 584 insertions(+), 382 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -1,3 +1,6 @@ [submodule "guest/libkcapi"] path = test/libkcapi url = https://github.com/smuellerDD/libkcapi.git +[submodule "cachepc-linux"] + path = linux + url = git@cachepclinux.github.com:Sinitax/cachepc-linux.git diff --git a/Makefile b/Makefile @@ -1,17 +1,19 @@ -LINUX ?= /usr/src/linux +LINUX ?= linux PWD := $(shell pwd) -TARGETS = build test/eviction test/access test/kvm test/sev test/sev-es test/sevstep -TARGETS += test/aes-detect_guest test/aes-detect_host -TARGETS += test/access-detect_guest test/access-detect_host -TARGETS += test/readsvme test/debug +BINS = test/eviction test/access test/kvm test/sev test/sev-es test/sevstep +BINS += test/aes-detect_guest test/aes-detect_host +BINS += test/access-detect_guest test/access-detect_host +BINS += test/readsvme util/debug util/reset CFLAGS = -I . -I test -Wunused-variable -Wunknown-pragmas -all: $(TARGETS) +all: build $(BINS) clean: $(MAKE) -C $(LINUX) SUBDIRS=arch/x86/kvm clean + $(MAKE) -C $(LINUX) SUBDIRS=crypto clean + rm $(BINS) $(LINUX)/arch/x86/kvm/cachepc: ln -sf $(PWD)/cachepc $@ @@ -27,9 +29,9 @@ load: sudo insmod $(LINUX)/arch/x86/kvm/kvm-amd.ko freq: - sudo cpupower frequency-set -f 1.5GHz - sudo cpupower frequency-set -u 1.5GHz - sudo cpupower frequency-set -d 1.5GHz + sudo cpupower frequency-set -f 3.7GHz + sudo cpupower frequency-set -u 3.7GHz + sudo cpupower frequency-set -d 3.7GHz update: git -C $(LINUX) diff 0aaa1e599bee256b3b15643bbb95e80ce7aa9be5 -G. > patch.diff @@ -37,7 +39,13 @@ update: test/aes-detect_%: test/aes-detect_%.c test/aes-detect.c cachepc/uapi.h clang -o $@ $< $(CFLAGS) -I test/libkcapi/lib -L test/libkcapi/.libs -lkcapi -static +test/access-detect_%: test/access-detect_%.c cachepc/uapi.h + clang -o $@ $< $(CFLAGS) -static + test/%: test/%.c cachepc/uapi.h - clang -o $@ $< $(CFLAGS) -fsanitize=address + clang -o $@ $< $(CFLAGS) -fsanitize=address + +util/%: util/%.c cachepc/uapi.h + clang -o $@ $< $(CFLAGS) -fsanitize=address .PHONY: all clean build load freq update diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h @@ -78,6 +78,13 @@ struct cacheline { char padding[24]; }; +struct cpc_fault { + uint64_t gfn; + uint32_t err; + + struct list_head list; +}; + static_assert(sizeof(struct cacheline) == CACHELINE_SIZE, "Bad cache line struct size"); static_assert(CL_NEXT_OFFSET == 0 && CL_PREV_OFFSET == 8); @@ -132,23 +139,24 @@ extern cpc_msrmt_t *cachepc_baseline; extern bool cachepc_baseline_measure; extern bool cachepc_baseline_active; -extern uint64_t cachepc_retinst; - extern bool cachepc_single_step; extern uint32_t cachepc_track_mode; extern uint32_t cachepc_apic_timer; -extern uint64_t cachepc_prev_rip; -extern uint32_t cachepc_track_state; -extern uint32_t cachepc_track_state_next; +extern uint64_t cachepc_track_start_gfn; +extern uint64_t cachepc_track_end_gfn; + +extern uint64_t cachepc_retinst; +extern uint64_t cachepc_retinst_prev; + +extern uint64_t cachepc_rip; +extern uint64_t cachepc_rip_prev; -extern bool cachepc_inst_fault_avail; extern uint64_t cachepc_inst_fault_gfn; extern uint32_t cachepc_inst_fault_err; +extern uint64_t cachepc_inst_fault_retinst; -extern bool cachepc_data_fault_avail; -extern uint64_t cachepc_data_fault_gfn; -extern uint32_t cachepc_data_fault_err; +extern struct list_head cachepc_faults; extern cache_ctx *cachepc_ctx; extern cacheline *cachepc_ds; diff --git a/cachepc/event.c b/cachepc/event.c @@ -52,7 +52,7 @@ cachepc_send_event(struct cpc_event event) write_unlock(&cachepc_event_lock); /* wait for ack with timeout */ - deadline = ktime_get_ns() + 60000000000ULL; /* 60s in ns */ + deadline = ktime_get_ns() + 20000000000ULL; /* 20s in ns */ while (!cachepc_event_is_done(cachepc_event.id)) { if (ktime_get_ns() > deadline) { pr_warn("CachePC: Timeout waiting for ack of event %llu\n", @@ -77,23 +77,43 @@ cachepc_send_guest_event(uint64_t type, uint64_t val) } int -cachepc_send_track_event(uint64_t inst_fault_gfn, uint32_t inst_fault_err, - uint64_t data_fault_gfn, uint32_t data_fault_err) +cachepc_send_track_event(struct list_head *list) { - struct cpc_event event; - + struct cpc_event event = { 0 }; + struct cpc_fault *fault; + uint64_t count; + + count = 0; event.type = CPC_EVENT_TRACK; - event.track.inst_fault_gfn = inst_fault_gfn; - event.track.inst_fault_err = inst_fault_err; - event.track.data_fault_avail = (data_fault_err != 0); - event.track.data_fault_gfn = data_fault_gfn; - event.track.data_fault_err = data_fault_err; + list_for_each_entry(fault, list, list) { + if (count >= 16) + break; + event.track.fault_gfns[count] = fault->gfn; + event.track.fault_errs[count] = fault->err; + count += 1; + } + event.track.fault_count = count; event.track.timestamp_ns = ktime_get_real_ns(); event.track.retinst = cachepc_retinst; return cachepc_send_event(event); } +int +cachepc_send_track_event_single(uint64_t gfn, uint32_t err, uint64_t retinst) +{ + struct cpc_event event = { 0 }; + + event.type = CPC_EVENT_TRACK; + event.track.fault_count = 1; + event.track.fault_gfns[0] = gfn; + event.track.fault_errs[0] = err; + event.track.timestamp_ns = ktime_get_real_ns(); + event.track.retinst = retinst; + + return cachepc_send_event(event); +} + bool cachepc_event_is_done(uint64_t id) { @@ -136,7 +156,7 @@ cachepc_handle_ack_event_ioctl(uint64_t eventid) int err; write_lock(&cachepc_event_lock); - if (eventid == cachepc_last_event_sent) { + if (!eventid || eventid == cachepc_last_event_sent) { err = 0; cachepc_last_event_acked = cachepc_last_event_sent; } else { diff --git a/cachepc/event.h b/cachepc/event.h @@ -18,8 +18,8 @@ extern bool cachepc_events_init; void cachepc_events_reset(void); int cachepc_send_guest_event(uint64_t type, uint64_t val); -int cachepc_send_track_event(uint64_t inst_fault_gfn, uint32_t inst_fault_err, - uint64_t data_fault_gfn, uint32_t data_fault_err); +int cachepc_send_track_event(struct list_head *list); +int cachepc_send_track_event_single(uint64_t gfn, uint32_t err, uint64_t event); bool cachepc_event_is_done(uint64_t id); int cachepc_handle_poll_event_ioctl(struct cpc_event *user); diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -13,7 +13,7 @@ #include <linux/sev.h> #include <asm/uaccess.h> -bool cachepc_debug = true; +bool cachepc_debug = false; EXPORT_SYMBOL(cachepc_debug); cpc_msrmt_t *cachepc_msrmts = NULL; @@ -29,35 +29,36 @@ EXPORT_SYMBOL(cachepc_baseline_measure); EXPORT_SYMBOL(cachepc_baseline_active); uint64_t cachepc_retinst = 0; +uint64_t cachepc_retinst_prev = 0; EXPORT_SYMBOL(cachepc_retinst); +EXPORT_SYMBOL(cachepc_retinst_prev); + +uint64_t cachepc_rip = 0; +uint64_t cachepc_rip_prev = 0; +EXPORT_SYMBOL(cachepc_rip); +EXPORT_SYMBOL(cachepc_rip_prev); bool cachepc_single_step = false; uint32_t cachepc_track_mode = false; uint32_t cachepc_apic_timer = 0; -uint64_t cachepc_prev_rip = 0; EXPORT_SYMBOL(cachepc_single_step); EXPORT_SYMBOL(cachepc_track_mode); EXPORT_SYMBOL(cachepc_apic_timer); -EXPORT_SYMBOL(cachepc_prev_rip); -uint32_t cachepc_track_state; -uint32_t cachepc_track_state_next; -EXPORT_SYMBOL(cachepc_track_state); -EXPORT_SYMBOL(cachepc_track_state_next); +uint64_t cachepc_track_start_gfn = 0; +uint64_t cachepc_track_end_gfn = 0; +EXPORT_SYMBOL(cachepc_track_start_gfn); +EXPORT_SYMBOL(cachepc_track_end_gfn); + +LIST_HEAD(cachepc_faults); +EXPORT_SYMBOL(cachepc_faults); -bool cachepc_inst_fault_avail = false; uint64_t cachepc_inst_fault_gfn = 0; uint32_t cachepc_inst_fault_err = 0; -EXPORT_SYMBOL(cachepc_inst_fault_avail); +uint64_t cachepc_inst_fault_retinst = 0; EXPORT_SYMBOL(cachepc_inst_fault_gfn); EXPORT_SYMBOL(cachepc_inst_fault_err); - -bool cachepc_data_fault_avail = false; -uint64_t cachepc_data_fault_gfn = 0; -uint32_t cachepc_data_fault_err = 0; -EXPORT_SYMBOL(cachepc_data_fault_avail); -EXPORT_SYMBOL(cachepc_data_fault_gfn); -EXPORT_SYMBOL(cachepc_data_fault_err); +EXPORT_SYMBOL(cachepc_inst_fault_retinst); cache_ctx *cachepc_ctx = NULL; cacheline *cachepc_ds = NULL; @@ -106,9 +107,12 @@ static int cachepc_kvm_track_mode_ioctl(void __user *arg_user); static int cachepc_kvm_track_page_ioctl(void __user *arg_user); static int cachepc_kvm_track_all_ioctl(void __user *arg_user); static int cachepc_kvm_untrack_all_ioctl(void __user *arg_user); -static int cachepc_kvm_uspt_reset_ioctl(void __user *arg_user); +static int cachepc_kvm_reset_tracking_ioctl(void __user *arg_user); static int cachepc_kvm_poll_event_ioctl(void __user *arg_user); -static int cachepc_kvm_uscpt_ack_event_ioctl(void __user *arg_user); +static int cachepc_kvm_ack_event_ioctl(void __user *arg_user); +static int cachepc_kvm_track_range_start_ioctl(void __user *arg_user); +static int cachepc_kvm_track_range_end_ioctl(void __user *arg_user); +static int cachepc_kvm_track_exec_cur_ioctl(void __user *arg_user); void cachepc_kvm_prime_probe_test(void *p) @@ -497,6 +501,7 @@ cachepc_kvm_track_mode_ioctl(void __user *arg_user) if (copy_from_user(&mode, arg_user, sizeof(mode))) return -EFAULT; + cachepc_single_step = false; cachepc_track_mode = mode; return 0; @@ -589,7 +594,7 @@ int cachepc_kvm_track_all_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; - uint64_t mode; + uint32_t mode; if (!arg_user) return -EINVAL; @@ -614,7 +619,7 @@ int cachepc_kvm_untrack_all_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; - uint64_t mode; + uint32_t mode; if (!arg_user) return -EINVAL; @@ -636,17 +641,38 @@ cachepc_kvm_untrack_all_ioctl(void __user *arg_user) } int -cachepc_kvm_uspt_reset_ioctl(void __user *arg_user) +cachepc_kvm_reset_tracking_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; + struct cpc_fault *fault, *next; cachepc_events_reset(); + BUG_ON(xa_empty(&main_vm->vcpu_array)); vcpu = xa_load(&main_vm->vcpu_array, 0); cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_EXEC); cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_ACCESS); cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_WRITE); + cachepc_track_mode = CPC_TRACK_NONE; + + cachepc_baseline_active = false; + cachepc_baseline_measure = false; + memset(cachepc_baseline, 0, cachepc_msrmts_count * sizeof(cpc_msrmt_t)); + + cachepc_inst_fault_gfn = 0; + cachepc_inst_fault_err = 0; + + cachepc_track_start_gfn = 0; + cachepc_track_end_gfn = 0; + + cachepc_single_step = false; + + list_for_each_entry_safe(fault, next, &cachepc_faults, list) { + list_del(&fault->list); + kfree(fault); + } + return 0; } @@ -660,7 +686,7 @@ cachepc_kvm_poll_event_ioctl(void __user *arg_user) } int -cachepc_kvm_uscpt_ack_event_ioctl(void __user *arg_user) +cachepc_kvm_ack_event_ioctl(void __user *arg_user) { uint64_t eventid; @@ -670,11 +696,49 @@ cachepc_kvm_uscpt_ack_event_ioctl(void __user *arg_user) return -EINVAL; if (copy_from_user(&eventid, arg_user, sizeof(eventid))) - return -EINVAL; + return -EFAULT; return cachepc_handle_ack_event_ioctl(eventid); } +int +cachepc_kvm_track_range_start_ioctl(void __user *arg_user) +{ + if (!arg_user) return -EINVAL; + + if (copy_from_user(&cachepc_track_start_gfn, arg_user, sizeof(uint64_t))) + return -EFAULT; + + return 0; +} + +int +cachepc_kvm_track_range_end_ioctl(void __user *arg_user) +{ + if (!arg_user) return -EINVAL; + + if (copy_from_user(&cachepc_track_end_gfn, arg_user, sizeof(uint64_t))) + return -EFAULT; + + return 0; +} + +int +cachepc_kvm_track_exec_cur_ioctl(void __user *arg_user) +{ + struct cpc_fault *fault; + + if (!arg_user) return -EINVAL; + + fault = list_first_entry(&cachepc_faults, struct cpc_fault, list); + if (!fault) return -EFAULT; + + if (copy_to_user(arg_user, &fault->gfn, sizeof(uint64_t))) + return -EFAULT; + + return 0; +} + long cachepc_kvm_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) { @@ -717,11 +781,17 @@ cachepc_kvm_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) case KVM_CPC_UNTRACK_ALL: return cachepc_kvm_untrack_all_ioctl(arg_user); case KVM_CPC_RESET_TRACKING: - return cachepc_kvm_uspt_reset_ioctl(arg_user); + return cachepc_kvm_reset_tracking_ioctl(arg_user); case KVM_CPC_POLL_EVENT: return cachepc_kvm_poll_event_ioctl(arg_user); case KVM_CPC_ACK_EVENT: - return cachepc_kvm_uscpt_ack_event_ioctl(arg_user); + return cachepc_kvm_ack_event_ioctl(arg_user); + case KVM_CPC_TRACK_RANGE_START: + return cachepc_kvm_track_range_start_ioctl(arg_user); + case KVM_CPC_TRACK_RANGE_END: + return cachepc_kvm_track_range_end_ioctl(arg_user); + case KVM_CPC_TRACK_EXEC_CUR: + return cachepc_kvm_track_exec_cur_ioctl(arg_user); default: return kvm_arch_dev_ioctl(file, ioctl, arg); } @@ -762,19 +832,21 @@ cachepc_kvm_init(void) cachepc_ds = NULL; cachepc_retinst = 0; + cachepc_debug = false; cachepc_single_step = false; cachepc_track_mode = CPC_TRACK_NONE; - cachepc_track_state = CPC_TRACK_AWAIT_INST_FAULT; + cachepc_inst_fault_gfn = 0; + cachepc_inst_fault_err = 0; - cachepc_data_fault_avail = false; - cachepc_inst_fault_avail = false; + INIT_LIST_HEAD(&cachepc_faults); cachepc_msrmts_count = L1_SETS; cachepc_msrmts = kzalloc(cachepc_msrmts_count * sizeof(cpc_msrmt_t), GFP_KERNEL); BUG_ON(cachepc_msrmts == NULL); + cachepc_baseline_active = false; cachepc_baseline_measure = false; cachepc_baseline = kzalloc(cachepc_msrmts_count * sizeof(cpc_msrmt_t), GFP_KERNEL); BUG_ON(cachepc_baseline == NULL); diff --git a/cachepc/mmu.c b/cachepc/mmu.c @@ -1,16 +1,26 @@ #include "../cachepc/cachepc.h" #include "../cachepc/track.h" #include "../cachepc/event.h" +#include "svm/svm.h" static void cachepc_page_fault_handle(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { + int modes[] = { + KVM_PAGE_TRACK_EXEC, + KVM_PAGE_TRACK_ACCESS, + }; + struct cpc_fault *tmp, *alloc; + size_t count, i; bool inst_fetch; - if (!kvm_slot_page_track_is_active(vcpu->kvm, - fault->slot, fault->gfn, KVM_PAGE_TRACK_ACCESS)) - return; + for (i = 0; i < 2; i++) { + if (kvm_slot_page_track_is_active(vcpu->kvm, + fault->slot, fault->gfn, modes[i])) + break; + } + if (i == 2) return; CPC_DBG("Tracked page fault (gfn:%llu err:%u)\n", fault->gfn, fault->error_code); @@ -19,84 +29,40 @@ cachepc_page_fault_handle(struct kvm_vcpu *vcpu, CPC_DBG("Tracked page fault attrs p:%i w:%i x:%i f:%i\n", fault->present, inst_fetch, fault->write, fault->exec); - cachepc_untrack_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); + count = 0; + list_for_each_entry(tmp, &cachepc_faults, list) + count += 1; + + CPC_INFO("Got %lu. fault gfn:%llu err:%u\n", count + 1, + fault->gfn, fault->error_code); if (cachepc_track_mode == CPC_TRACK_DATA_ACCESS) { - if (cachepc_track_state == CPC_TRACK_AWAIT_INST_FAULT) { - /* first fault from instruction fetch */ - CPC_DBG("Got inst fault gfn:%llu err:%u\n", - fault->gfn, fault->error_code); - - cachepc_inst_fault_gfn = fault->gfn; - cachepc_inst_fault_err = fault->error_code; - cachepc_inst_fault_avail = true; - cachepc_data_fault_avail = false; - - cachepc_single_step = true; - cachepc_apic_timer = 0; - - cachepc_track_state_next = CPC_TRACK_AWAIT_DATA_FAULT; - } else if (cachepc_track_state == CPC_TRACK_AWAIT_DATA_FAULT) { - /* second fault from data access */ - CPC_DBG("Got data fault gfn:%llu err:%u\n", - fault->gfn, fault->error_code); - if (!cachepc_inst_fault_avail) - CPC_ERR("Waiting for data fault without inst\n"); - - cachepc_data_fault_gfn = fault->gfn; - cachepc_data_fault_err = fault->error_code; - cachepc_data_fault_avail = true; - - cachepc_single_step = true; - cachepc_apic_timer = 0; - - cachepc_track_state_next = CPC_TRACK_AWAIT_STEP_INTR; - } else if (cachepc_track_state == CPC_TRACK_AWAIT_STEP_INTR) { - /* unexpected extra fault before APIC interrupt */ - CPC_ERR("Got unexpected data fault gfn:%llu err:%u\n", - fault->gfn, fault->error_code); - CPC_ERR("Data access step apic timer too large?\n"); - - cachepc_track_single(vcpu, cachepc_inst_fault_gfn, - KVM_PAGE_TRACK_ACCESS); - cachepc_inst_fault_avail = false; - - cachepc_track_single(vcpu, cachepc_data_fault_gfn, - KVM_PAGE_TRACK_ACCESS); - cachepc_data_fault_avail = false; - - /* retrack fault we just got so we can start from scratch */ - cachepc_track_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); - - cachepc_send_track_event( - cachepc_inst_fault_gfn, cachepc_inst_fault_err, - cachepc_data_fault_gfn, cachepc_data_fault_err); - - cachepc_single_step = false; - - cachepc_track_state_next = CPC_TRACK_AWAIT_INST_FAULT; - } else { - CPC_ERR("Invalid tracking state: %i\n", - cachepc_track_state); - - cachepc_track_state_next = CPC_TRACK_AWAIT_INST_FAULT; - } - } else if (cachepc_track_mode == CPC_TRACK_EXEC_PAGES) { - /* untrack pages that are not code (could be a problem for WX */ - if (!inst_fetch) return; - - /* TODO: calculate retired instructions (save and subtract global counter) */ - if (cachepc_inst_fault_avail) { - /* track previous faulted page, current stays untracked */ - cachepc_track_single(vcpu, cachepc_inst_fault_gfn, - KVM_PAGE_TRACK_ACCESS); - } - cachepc_inst_fault_gfn = fault->gfn; - cachepc_inst_fault_err = fault->error_code; - cachepc_send_track_event(fault->gfn, fault->error_code, 0, 0); + cachepc_untrack_single(vcpu, fault->gfn, modes[i]); + + alloc = kmalloc(sizeof(struct cpc_fault), GFP_KERNEL); + BUG_ON(!alloc); + alloc->gfn = fault->gfn; + alloc->err = fault->error_code; + list_add_tail(&alloc->list, &cachepc_faults); + + cachepc_single_step = true; + cachepc_apic_timer = 0; + } else if (cachepc_track_mode == CPC_TRACK_EXEC) { + cachepc_untrack_single(vcpu, fault->gfn, modes[i]); + + if (modes[i] != KVM_PAGE_TRACK_EXEC) + CPC_WARN("Wrong page track mode for TRACK_EXEC"); + + alloc = kmalloc(sizeof(struct cpc_fault), GFP_KERNEL); + BUG_ON(!alloc); + alloc->gfn = fault->gfn; + alloc->err = fault->error_code; + list_add_tail(&alloc->list, &cachepc_faults); + + cachepc_single_step = true; + cachepc_apic_timer = 0; } else if (cachepc_track_mode == CPC_TRACK_ACCESS) { - cachepc_track_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); - cachepc_send_track_event(fault->gfn, fault->error_code, 0, 0); + cachepc_send_track_event(&cachepc_faults); } } @@ -110,32 +76,27 @@ cachepc_spte_protect(u64 *sptep, bool pt_protect, enum kvm_page_track_mode mode) if (!is_writable_pte(spte) && !(pt_protect && is_mmu_writable_spte(spte))) return false; - rmap_printk("spte %p %llx\n", sptep, *sptep); - if (pt_protect) spte &= ~shadow_mmu_writable_mask; flush = false; if (mode == KVM_PAGE_TRACK_WRITE) { - spte = spte & ~PT_WRITABLE_MASK; + spte &= ~PT_WRITABLE_MASK; flush = true; } else if (mode == KVM_PAGE_TRACK_RESET_ACCESSED) { - spte = spte & ~PT_ACCESSED_MASK; + spte &= ~PT_ACCESSED_MASK; } else if (mode == KVM_PAGE_TRACK_ACCESS) { - spte = spte & ~PT_PRESENT_MASK; - spte = spte & ~PT_WRITABLE_MASK; - spte = spte & ~PT_USER_MASK; - spte = spte | (0x1ULL << PT64_NX_SHIFT); + spte &= ~PT_PRESENT_MASK; + spte &= ~PT_WRITABLE_MASK; + spte &= ~PT_USER_MASK; + spte |= (0x1ULL << PT64_NX_SHIFT); flush = true; } else if (mode == KVM_PAGE_TRACK_EXEC) { - spte = spte | (0x1ULL << PT64_NX_SHIFT); + spte |= (0x1ULL << PT64_NX_SHIFT); flush = true; } else if (mode == KVM_PAGE_TRACK_RESET_EXEC) { - spte = spte & ~(0x1ULL << PT64_NX_SHIFT); + spte &= ~(0x1ULL << PT64_NX_SHIFT); flush = true; - } else { - printk(KERN_WARNING "spte_protect was called with invalid mode" - "parameter %d\n",mode); } flush |= mmu_spte_update(sptep, spte); diff --git a/cachepc/track.c b/cachepc/track.c @@ -62,7 +62,7 @@ cachepc_track_single(struct kvm_vcpu *vcpu, gfn_t gfn, srcu_read_unlock(&vcpu->kvm->srcu, idx); - if (!slot) pr_err("Sevstep: Failed to track gfn %llu\n", gfn); + if (!slot) CPC_ERR("Failed to track gfn %llu\n", gfn); return slot != NULL; } @@ -86,7 +86,7 @@ cachepc_untrack_single(struct kvm_vcpu *vcpu, gfn_t gfn, srcu_read_unlock(&vcpu->kvm->srcu, idx); - if (!slot) pr_err("Sevstep: Failed to untrack gfn %llu\n", gfn); + if (!slot) CPC_ERR("Failed to untrack gfn %llu\n", gfn); return slot != NULL; } @@ -101,11 +101,11 @@ cachepc_track_all(struct kvm_vcpu *vcpu, enum kvm_page_track_mode mode) int bkt; u64 gfn; - pr_warn("Sevstep: Start tracking (mode:%i)\n", mode); + CPC_DBG("Start tracking (mode:%i)\n", mode); slots = kvm_vcpu_memslots(vcpu); kvm_for_each_memslot(slot, bkt, slots) { - pr_warn("Sevstep: Slot page count: %lu\n", slot->npages); + CPC_DBG("Slot page count: %lu\n", slot->npages); for (gfn = slot->base_gfn; gfn < slot->base_gfn + slot->npages; gfn++) { if (!kvm_slot_page_track_is_active(vcpu->kvm, slot, gfn, mode)) { write_lock(&vcpu->kvm->mmu_lock); @@ -129,7 +129,7 @@ cachepc_untrack_all(struct kvm_vcpu *vcpu, enum kvm_page_track_mode mode) int bkt; u64 gfn; - pr_warn("Sevstep: Stop tracking (mode:%i)\n", mode); + CPC_DBG("Stop tracking (mode:%i)\n", mode); slots = kvm_vcpu_memslots(vcpu); kvm_for_each_memslot(slot, bkt, slots) { diff --git a/cachepc/uapi.h b/cachepc/uapi.h @@ -44,11 +44,14 @@ #define KVM_CPC_DEBUG _IOW(KVMIO, 0x2D, __u32) #define KVM_CPC_TRACK_PAGE _IOWR(KVMIO, 0x30, struct cpc_track_config) -#define KVM_CPC_TRACK_ALL _IOWR(KVMIO, 0x31, __u64) -#define KVM_CPC_UNTRACK_ALL _IOWR(KVMIO, 0x32, __u64) +#define KVM_CPC_TRACK_ALL _IOWR(KVMIO, 0x31, __u32) +#define KVM_CPC_UNTRACK_ALL _IOWR(KVMIO, 0x32, __u32) #define KVM_CPC_RESET_TRACKING _IO(KVMIO, 0x33) #define KVM_CPC_POLL_EVENT _IOWR(KVMIO, 0x34, struct cpc_track_event) #define KVM_CPC_ACK_EVENT _IOWR(KVMIO, 0x35, __u64) +#define KVM_CPC_TRACK_RANGE_START _IOWR(KVMIO, 0x36, __u64) +#define KVM_CPC_TRACK_RANGE_END _IOWR(KVMIO, 0x37, __u64) +#define KVM_CPC_TRACK_EXEC_CUR _IOWR(KVMIO, 0x38, __u64) enum { CPC_EVENT_NONE, @@ -65,13 +68,7 @@ enum { CPC_TRACK_NONE, CPC_TRACK_ACCESS, CPC_TRACK_DATA_ACCESS, - CPC_TRACK_EXEC_PAGES -}; - -enum { - CPC_TRACK_AWAIT_INST_FAULT, - CPC_TRACK_AWAIT_DATA_FAULT, - CPC_TRACK_AWAIT_STEP_INTR + CPC_TRACK_EXEC }; enum kvm_page_track_mode { @@ -89,11 +86,9 @@ struct cpc_track_config { }; struct cpc_track_event { - __u64 inst_fault_gfn; - __u64 inst_fault_err; - __u32 data_fault_avail; - __u64 data_fault_gfn; - __u32 data_fault_err; + __u64 fault_gfns[16]; + __u32 fault_errs[16]; + __u64 fault_count; __u64 timestamp_ns; __u64 retinst; }; diff --git a/linux b/linux @@ -0,0 +1 @@ +Subproject commit be291b876f75894ecb80308be35d2983f069038f diff --git a/patch.diff b/patch.diff @@ -16,6 +16,36 @@ index eb186bc57f6a..b96e80934005 100644 /* * The notifier represented by @kvm_page_track_notifier_node is linked into +diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c +index f603a724b08e..5c2d9b07c6aa 100644 +--- a/arch/x86/kernel/sev.c ++++ b/arch/x86/kernel/sev.c +@@ -1034,6 +1034,8 @@ static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip) + if (!vmsa) + return -ENOMEM; + ++ CPC_WARN("New VMSA allocated!\n"); ++ + /* CR4 should maintain the MCE value */ + cr4 = native_read_cr4() & X86_CR4_MCE; + +@@ -2589,11 +2591,11 @@ static int rmpupdate(u64 pfn, struct rmpupdate *val) + * direct map. + */ + if (val->assigned) { +- if (invalid_direct_map(pfn, npages)) { +- pr_err("Failed to unmap pfn 0x%llx pages %d from direct_map\n", +- pfn, npages); +- return -EFAULT; +- } ++ // if (invalid_direct_map(pfn, npages)) { ++ // pr_err("Failed to unmap pfn 0x%llx pages %d from direct_map\n", ++ // pfn, npages); ++ // return -EFAULT; ++ // } + } + + retry: diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 30f244b64523..a1e3c5ae2f80 100644 --- a/arch/x86/kvm/Makefile @@ -367,19 +397,20 @@ index 7b9265d67131..68b9134970da 100644 /* diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c -index a4f6d10b0ef3..0c5aae1de162 100644 +index a4f6d10b0ef3..62c1fcd563a6 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c -@@ -35,6 +35,8 @@ +@@ -35,6 +35,9 @@ #include "trace.h" #include "mmu.h" ++#include "asm/set_memory.h" +#include "cachepc/cachepc.h" + #ifndef CONFIG_KVM_AMD_SEV /* * When this config is not defined, SEV feature is not supported and APIs in -@@ -888,7 +890,7 @@ static int __sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src, +@@ -888,7 +891,7 @@ static int __sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src, &data, error); } @@ -388,7 +419,7 @@ index a4f6d10b0ef3..0c5aae1de162 100644 unsigned long dst_paddr, int sz, int *err) { int offset; -@@ -904,12 +906,20 @@ static int __sev_dbg_decrypt(struct kvm *kvm, unsigned long src_paddr, +@@ -904,12 +907,20 @@ static int __sev_dbg_decrypt(struct kvm *kvm, unsigned long src_paddr, return __sev_issue_dbg_cmd(kvm, src_paddr, dst_paddr, sz, err, false); } @@ -409,7 +440,7 @@ index a4f6d10b0ef3..0c5aae1de162 100644 int ret, offset; /* if inputs are not 16-byte then use intermediate buffer */ -@@ -923,6 +933,11 @@ static int __sev_dbg_decrypt_user(struct kvm *kvm, unsigned long paddr, +@@ -923,6 +934,11 @@ static int __sev_dbg_decrypt_user(struct kvm *kvm, unsigned long paddr, dst_paddr = __sme_page_pa(tpage); } @@ -421,7 +452,7 @@ index a4f6d10b0ef3..0c5aae1de162 100644 ret = __sev_dbg_decrypt(kvm, paddr, dst_paddr, size, err); if (ret) goto e_free; -@@ -1024,6 +1039,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) +@@ -1024,6 +1040,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) struct kvm_sev_dbg debug; unsigned long n; unsigned int size; @@ -429,7 +460,7 @@ index a4f6d10b0ef3..0c5aae1de162 100644 int ret; if (!sev_guest(kvm)) -@@ -1037,6 +1053,13 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) +@@ -1037,6 +1054,13 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) if (!debug.dst_uaddr) return -EINVAL; @@ -443,7 +474,7 @@ index a4f6d10b0ef3..0c5aae1de162 100644 vaddr = debug.src_uaddr; size = debug.len; vaddr_end = vaddr + size; -@@ -1075,7 +1098,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) +@@ -1075,7 +1099,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) if (dec) ret = __sev_dbg_decrypt_user(kvm, __sme_page_pa(src_p[0]) + s_off, @@ -453,7 +484,92 @@ index a4f6d10b0ef3..0c5aae1de162 100644 __sme_page_pa(dst_p[0]) + d_off, len, &argp->error); else -@@ -3149,9 +3173,9 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm, u64 *exit_code) +@@ -2170,6 +2195,62 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) + return ret; + } + ++static int rmpupdate_noremap(u64 pfn, struct rmpupdate *val) ++{ ++ unsigned long paddr = pfn << PAGE_SHIFT; ++ int ret, level, npages; ++ int retries = 0; ++ ++ if (!pfn_valid(pfn)) ++ return -EINVAL; ++ ++ if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP)) ++ return -ENXIO; ++ ++ level = RMP_TO_X86_PG_LEVEL(val->pagesize); ++ npages = page_level_size(level) / PAGE_SIZE; ++ ++ ++retry: ++ /* Binutils version 2.36 supports the RMPUPDATE mnemonic. */ ++ asm volatile(".byte 0xF2, 0x0F, 0x01, 0xFE" ++ : "=a"(ret) ++ : "a"(paddr), "c"((unsigned long)val) ++ : "memory", "cc"); ++ ++ if (ret) { ++ if (!retries) { ++ pr_err("rmpupdate failed, ret: %d, pfn: %llx, npages: %d, level: %d, retrying (max: %d)...\n", ++ ret, pfn, npages, level, 2 * num_present_cpus()); ++ dump_stack(); ++ } ++ retries++; ++ if (retries < 2 * num_present_cpus()) ++ goto retry; ++ } else if (retries > 0) { ++ pr_err("rmpupdate for pfn %llx succeeded after %d retries\n", pfn, retries); ++ } ++ ++ return ret; ++} ++ ++int rmp_make_private_noremap(u64 pfn, u64 gpa, enum pg_level level, int asid, bool immutable) ++{ ++ struct rmpupdate val; ++ ++ if (!pfn_valid(pfn)) ++ return -EINVAL; ++ ++ memset(&val, 0, sizeof(val)); ++ val.assigned = 1; ++ val.asid = asid; ++ val.immutable = immutable; ++ val.gpa = gpa; ++ val.pagesize = X86_TO_RMP_PG_LEVEL(level); ++ ++ return rmpupdate_noremap(pfn, &val); ++} ++ + static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) + { + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; +@@ -2183,16 +2264,20 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) + struct vcpu_svm *svm = to_svm(xa_load(&kvm->vcpu_array, i)); + u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT; + ++ CPC_WARN("RIP READ PRE-PRIVATE: %llu\n", svm->sev_es.vmsa->rip); ++ + /* Perform some pre-encryption checks against the VMSA */ + ret = sev_es_sync_vmsa(svm); + if (ret) + return ret; + + /* Transition the VMSA page to a firmware state. */ +- ret = rmp_make_private(pfn, -1, PG_LEVEL_4K, sev->asid, true); ++ ret = rmp_make_private_noremap(pfn, -1, PG_LEVEL_4K, sev->asid, true); + if (ret) + return ret; + ++ CPC_WARN("RIP READ POST-PRIVATE: %llu\n", svm->sev_es.vmsa->rip); ++ + /* Issue the SNP command to encrypt the VMSA */ + data.address = __sme_pa(svm->sev_es.vmsa); + ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE, +@@ -3149,9 +3234,9 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm, u64 *exit_code) } break; case SVM_EXIT_VMMCALL: @@ -466,8 +582,16 @@ index a4f6d10b0ef3..0c5aae1de162 100644 break; case SVM_EXIT_RDTSCP: break; +@@ -3920,6 +4005,7 @@ static int sev_snp_ap_creation(struct vcpu_svm *svm) + goto out; + } + ++ CPC_WARN("VMSA_GPA SET via VMGEXIT_AP_CREATE\n"); + target_svm->sev_es.snp_vmsa_gpa = svm->vmcb->control.exit_info_2; + break; + case SVM_VMGEXIT_AP_DESTROY: diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index cf0bf456d520..dee33c011251 100644 +index cf0bf456d520..4a25e306543a 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2,6 +2,10 @@ @@ -481,80 +605,32 @@ index cf0bf456d520..dee33c011251 100644 #include "irq.h" #include "mmu.h" #include "kvm_cache_regs.h" -@@ -1887,6 +1891,8 @@ static int npf_interception(struct kvm_vcpu *vcpu) - u64 fault_address = svm->vmcb->control.exit_info_2; - u64 error_code = svm->vmcb->control.exit_info_1; - -+ cachepc_track_state_next = cachepc_track_state; -+ - trace_kvm_page_fault(fault_address, error_code); - rc = kvm_mmu_page_fault(vcpu, fault_address, error_code, - static_cpu_has(X86_FEATURE_DECODEASSISTS) ? -@@ -1896,6 +1902,8 @@ static int npf_interception(struct kvm_vcpu *vcpu) - if (error_code & PFERR_GUEST_RMP_MASK) - handle_rmp_page_fault(vcpu, fault_address, error_code); - -+ cachepc_track_state = cachepc_track_state_next; -+ - return rc; - } - -@@ -2081,9 +2089,139 @@ static int smi_interception(struct kvm_vcpu *vcpu) +@@ -2081,9 +2085,74 @@ static int smi_interception(struct kvm_vcpu *vcpu) return 1; } -+ -+static inline int svm_map_ghcb(struct vcpu_svm *svm, struct kvm_host_map *map) -+{ -+ struct vmcb_control_area *control = &svm->vmcb->control; -+ u64 gfn = gpa_to_gfn(control->ghcb_gpa); -+ struct kvm_vcpu *vcpu = &svm->vcpu; -+ -+ if (kvm_vcpu_map(vcpu, gfn, map)) { -+ /* Unable to map GHCB from guest */ -+ pr_err("error mapping GHCB GFN [%#llx] from guest\n", gfn); -+ return -EFAULT; -+ } -+ -+ if (sev_post_map_gfn(vcpu->kvm, map->gfn, map->pfn)) { -+ kvm_vcpu_unmap(vcpu, map, false); -+ return -EBUSY; -+ } -+ -+ return 0; -+} -+ -+static inline void svm_unmap_ghcb(struct vcpu_svm *svm, struct kvm_host_map *map) -+{ -+ struct kvm_vcpu *vcpu = &svm->vcpu; -+ -+ kvm_vcpu_unmap(vcpu, map, true); -+ sev_post_unmap_gfn(vcpu->kvm, map->gfn, map->pfn); -+} -+ -+static void hexdump(uint8_t *prev, uint8_t *cur, size_t len) -+{ -+ size_t i; -+ -+ for (i = 0; i < len; i++) { -+ if (cur[i] != prev[i]) -+ printk(KERN_CONT "%02X ", cur[i]); -+ else -+ printk(KERN_CONT " "); -+ if ((i+1) % 16 == 0) -+ printk(KERN_CONT "\n"); -+ } -+ printk(KERN_CONT "\n"); -+} ++// static void hexdump(uint8_t *prev, uint8_t *cur, size_t len) ++// { ++// size_t i; ++// ++// for (i = 0; i < len; i++) { ++// //printk(KERN_CONT "%02X ", cur[i]); ++// if (cur[i] != prev[i]) ++// printk(KERN_CONT "%02X ", cur[i]); ++// else ++// printk(KERN_CONT " "); ++// if ((i+1) % 16 == 0) ++// printk(KERN_CONT "\n"); ++// } ++// printk(KERN_CONT "\n"); ++// } + static int intr_interception(struct kvm_vcpu *vcpu) { -+ static struct vmcb_control_area prev_control; -+ //static struct ghcb prev_ghcb; -+ struct vcpu_svm *svm; + struct vmcb_control_area *control; -+ //struct kvm_host_map map; -+ //struct ghcb *ghcb; ++ struct vcpu_svm *svm; ++ struct cpc_fault *fault, *next; ++ size_t count; + ++vcpu->stat.irq_exits; + @@ -562,84 +638,49 @@ index cf0bf456d520..dee33c011251 100644 + svm = to_svm(vcpu); + control = &svm->vmcb->control; + -+ CPC_WARN("RETINST %llu\n", cachepc_retinst); -+ -+ // if (svm_map_ghcb(svm, &map)) { -+ // CPC_ERR("Mapping GHCB\n"); -+ // return 1; -+ // } -+ // ghcb = map.hva; -+ -+ // if (memcmp(&prev_ghcb, ghcb, sizeof(struct ghcb))) { -+ // pr_warn("GHCB DIFF HEXDUMP:\n"); -+ // hexdump((void*)&prev_ghcb, (void *)ghcb, -+ // sizeof(struct ghcb)); -+ // } -+ -+ // memcpy(&prev_ghcb, ghcb, sizeof(struct ghcb)); -+ -+ // svm_unmap_ghcb(svm, &map); -+ -+ if (memcmp(&prev_control, control, sizeof(struct vmcb_control_area))) { -+ pr_warn("VMCB DIFF HEXDUMP:\n"); -+ hexdump((void*)&prev_control, (void *)control, -+ sizeof(struct vmcb_control_area)); -+ } -+ -+ memcpy(&prev_control, control, sizeof(struct vmcb_control_area)); -+ -+ if (cachepc_apic_timer < 1000) { ++ cachepc_rip = svm->sev_es.vmsa->rip; ++ if (!cachepc_rip_prev) ++ cachepc_rip_prev = cachepc_rip; ++ if (cachepc_rip == cachepc_rip_prev) { + cachepc_apic_timer += 1; + return 1; + } ++ CPC_INFO("Detected RIP change! (%u)\n", cachepc_apic_timer); + -+ // if (svm->sev_es.vmsa->rip == cachepc_prev_rip) { ++ // if (!cachepc_retinst_prev) ++ // cachepc_retinst_prev = cachepc_retinst; ++ // if (cachepc_retinst_prev == cachepc_retinst) { + // cachepc_apic_timer += 1; + // return 1; + // } ++ // CPC_INFO("Detected RETINST change! (%llu,%u)\n", ++ // cachepc_retinst, cachepc_apic_timer); + + cachepc_single_step = false; + -+ switch (cachepc_track_state) { -+ case CPC_TRACK_AWAIT_DATA_FAULT: -+ CPC_INFO("Caught single step WITHOUT data!\n"); -+ -+ cachepc_track_single(vcpu, cachepc_inst_fault_gfn, -+ KVM_PAGE_TRACK_ACCESS); -+ cachepc_inst_fault_avail = false; -+ -+ cachepc_send_track_event( -+ cachepc_inst_fault_gfn, cachepc_inst_fault_err, -+ 0, 0); ++ count = 0; ++ list_for_each_entry(fault, &cachepc_faults, list) ++ count += 1; + -+ cachepc_track_state = CPC_TRACK_AWAIT_INST_FAULT; -+ break; -+ case CPC_TRACK_AWAIT_STEP_INTR: -+ CPC_INFO("Caught single step WITH data!\n"); ++ CPC_INFO("Caught single step with %lu faults!\n", count); ++ if (count == 0 || count > 2) ++ CPC_ERR("Unexpected step fault count: %lu faults!\n", count); + -+ cachepc_track_single(vcpu, cachepc_data_fault_gfn, -+ KVM_PAGE_TRACK_ACCESS); -+ cachepc_data_fault_avail = false; ++ list_for_each_entry(fault, &cachepc_faults, list) ++ cachepc_track_single(vcpu, fault->gfn, KVM_PAGE_TRACK_ACCESS); + -+ cachepc_track_single(vcpu, cachepc_inst_fault_gfn, -+ KVM_PAGE_TRACK_ACCESS); -+ cachepc_inst_fault_avail = false; ++ cachepc_send_track_event(&cachepc_faults); + -+ cachepc_send_track_event( -+ cachepc_inst_fault_gfn, cachepc_inst_fault_err, -+ cachepc_data_fault_gfn, cachepc_data_fault_err); -+ -+ cachepc_track_state = CPC_TRACK_AWAIT_INST_FAULT; -+ break; -+ default: -+ CPC_ERR("Unexpected single step\n"); ++ list_for_each_entry_safe(fault, next, &cachepc_faults, list) { ++ list_del(&fault->list); ++ kfree(fault); + } + } + return 1; } -@@ -3269,9 +3407,25 @@ static int svm_handle_invalid_exit(struct kvm_vcpu *vcpu, u64 exit_code) +@@ -3269,9 +3338,25 @@ static int svm_handle_invalid_exit(struct kvm_vcpu *vcpu, u64 exit_code) int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 exit_code) { @@ -665,7 +706,7 @@ index cf0bf456d520..dee33c011251 100644 #ifdef CONFIG_RETPOLINE if (exit_code == SVM_EXIT_MSR) return msr_interception(vcpu); -@@ -3787,15 +3941,46 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) +@@ -3787,15 +3872,48 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -677,11 +718,12 @@ index cf0bf456d520..dee33c011251 100644 if (sev_es_guest(vcpu->kvm)) { + if (cachepc_single_step && cachepc_apic_timer == 0) { -+ cachepc_apic_timer = 100; ++ cachepc_apic_timer = 200; ++ cachepc_retinst_prev = 0; ++ cachepc_rip_prev = 0; + } + + cpu = get_cpu(); -+ // local_irq_disable(); + WARN_ON(cpu != 2); + + memset(cachepc_msrmts, 0, @@ -694,17 +736,18 @@ index cf0bf456d520..dee33c011251 100644 + cachepc_save_msrmts(cachepc_ds); + if (cachepc_baseline_measure) + cachepc_update_baseline(); -+ // local_irq_enable(); ++ + put_cpu(); } else { struct svm_cpu_data *sd = per_cpu(svm_data, vcpu->cpu); -+ if (cachepc_apic_timer == 0) { -+ cachepc_apic_timer = 100; ++ if (cachepc_single_step && cachepc_apic_timer == 0) { ++ cachepc_apic_timer = 50; ++ cachepc_retinst_prev = 0; ++ cachepc_rip_prev = 0; + } + + cpu = get_cpu(); -+ // local_irq_disable(); + WARN_ON(cpu != 2); + + memset(cachepc_msrmts, 0, @@ -713,7 +756,7 @@ index cf0bf456d520..dee33c011251 100644 /* * Use a single vmcb (vmcb01 because it's always valid) for * context switching guest state via VMLOAD/VMSAVE, that way -@@ -3806,7 +3991,15 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu) +@@ -3806,7 +3924,15 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu) __svm_vcpu_run(vmcb_pa, (unsigned long *)&vcpu->arch.regs); vmsave(svm->vmcb01.pa); @@ -724,7 +767,7 @@ index cf0bf456d520..dee33c011251 100644 + cachepc_save_msrmts(cachepc_ds); + if (cachepc_baseline_measure) + cachepc_update_baseline(); -+ // local_irq_enable(); ++ + put_cpu(); } @@ -1034,6 +1077,19 @@ index e089fbf9017f..7899e1efe852 static int __sev_init_locked(int *error) { +diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h +index 5b1019dab328..ec317e7c348a 100644 +--- a/drivers/iommu/amd/amd_iommu_types.h ++++ b/drivers/iommu/amd/amd_iommu_types.h +@@ -275,7 +275,7 @@ + * + * 512GB Pages are not supported due to a hardware bug + */ +-#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38)) ++#define AMD_IOMMU_PGSIZES (PAGE_SIZE) + + /* Bit value definition for dte irq remapping fields*/ + #define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index f2a63cb2658b..869faf927e5d 100644 --- a/virt/kvm/kvm_main.c diff --git a/test/.gitignore b/test/.gitignore @@ -10,4 +10,3 @@ aes-detect_host access-detect_guest access-detect_host readsvme -debug diff --git a/test/access-detect_guest.c b/test/access-detect_guest.c @@ -4,6 +4,7 @@ #include <unistd.h> #include <stdint.h> #include <string.h> +#include <stdio.h> #include <stdlib.h> int @@ -17,10 +18,9 @@ main(int argc, const char **argv) memset(buf, 0, L1_LINESIZE * L1_SETS); while (1) { + printf("LOOP\n"); CPC_DO_VMMCALL(CPC_CPUID_START_TRACK, 0); - - *(uint8_t *)(buf + L1_LINESIZE * 15) += 1; - + *(uint8_t *)(buf + L1_LINESIZE * 15) = 1; CPC_DO_VMMCALL(CPC_CPUID_STOP_TRACK, 0); } } diff --git a/test/access-detect_host.c b/test/access-detect_host.c @@ -20,6 +20,7 @@ #include <err.h> #include <fcntl.h> #include <sched.h> +#include <dirent.h> #include <string.h> #include <stdbool.h> #include <stdlib.h> @@ -79,7 +80,7 @@ read_counts() if (!counts) err(1, "malloc"); ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret == -1) err(1, "ioctl READ_COUNTS"); + if (ret) err(1, "ioctl READ_COUNTS"); for (i = 0; i < L1_SETS; i++) { if (counts[i] > 8) @@ -132,8 +133,9 @@ monitor(bool baseline) { struct cpc_event event; cpc_msrmt_t counts[64]; - uint64_t track_mode; - uint32_t arg; + uint64_t inst_fault_gfn; + uint64_t read_fault_gfn; + uint64_t arg; int ret, i; /* Get page fault info */ @@ -142,25 +144,32 @@ monitor(bool baseline) if (event.type == CPC_EVENT_CPUID) { printf("CPUID EVENT\n"); if (event.guest.type == CPC_CPUID_START_TRACK) { - arg = CPC_TRACK_DATA_ACCESS; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret == -1) err(1, "ioctl TRACK_MODE"); + 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"); - track_mode = KVM_PAGE_TRACK_ACCESS; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &track_mode); - if (ret) err(1, "ioctl TRACK_ALL"); + 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_CPUID_STOP_TRACK) { - arg = CPC_TRACK_NONE; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret == -1) err(1, "ioctl TRACK_MODE"); + arg = 0; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_START, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_START"); - track_mode = KVM_PAGE_TRACK_ACCESS; - ret = ioctl(kvm_dev, KVM_CPC_UNTRACK_ALL, &track_mode); - if (ret) err(1, "ioctl UNTRACK_ALL"); + arg = 0; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_RANGE_END, &arg); + if (ret) err(1, "ioctl TRACK_RANGE_END"); } ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret == -1) err(1, "ioctl ACK_EVENT"); + if (ret) err(1, "ioctl ACK_EVENT"); + + faultcnt++; return 0; } else if (event.type != CPC_EVENT_TRACK) { @@ -170,28 +179,37 @@ monitor(bool baseline) printf("EVENT\n"); ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret == -1) err(1, "ioctl READ_COUNTS"); + if (ret) err(1, "ioctl READ_COUNTS"); + + inst_fault_gfn = 0; + read_fault_gfn = 0; + for (i = 0; i < event.track.fault_count; i++) { + if ((event.track.fault_errs[i] & 0b11111) == 0b10100) + inst_fault_gfn = event.track.fault_gfns[i]; + else if ((event.track.fault_errs[i] & 0b00110) == 0b00100) + read_fault_gfn = event.track.fault_gfns[i]; + } if (!baseline) { - printf("Event: inst:%llu data:%llu retired:%llu\n", - event.track.inst_fault_gfn, - event.track.data_fault_gfn, - event.track.retinst); + printf("Event: cnt:%llu inst:%lu data:%lu retired:%llu\n", + event.track.fault_count, inst_fault_gfn, + read_fault_gfn, event.track.retinst); print_counts(counts); printf("\n"); } for (i = 0; i < 64; i++) { if (counts[i] > 8) { - errx(1, "Invalid count for set %i (%llu)", + warnx("Invalid count for set %i (%llu)", i, counts[i]); + counts[i] = 8; } } ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret == -1) err(1, "ioctl ACK_EVENT"); + if (ret) err(1, "ioctl ACK_EVENT"); - faultcnt++; + if (baseline) faultcnt++; } else if (errno != EAGAIN) { perror("ioctl POLL_EVENT"); return 1; @@ -201,6 +219,39 @@ monitor(bool baseline) } 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; @@ -208,20 +259,17 @@ main(int argc, const char **argv) struct cpc_event event; cpc_msrmt_t baseline[64]; int ret, i; - - if (argc <= 1 || !atoi(argv[1])) { - printf("Specify qemu process to pin\n"); - return 0; - } kvm_dev = open("/dev/kvm", O_RDWR); if (!kvm_dev) err(1, "open /dev/kvm"); setvbuf(stdout, NULL, _IONBF, 0); - pid = atoi(argv[1]); - pin_process(pid, TARGET_CORE, true); + 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 */ @@ -230,18 +278,18 @@ main(int argc, const char **argv) /* Reset previous tracking */ ret = ioctl(kvm_dev, KVM_CPC_RESET_TRACKING, NULL); - if (ret == -1) err(1, "ioctl RESET_TRACKING"); + 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 == -1) err(1, "ioctl SUB_BASELINE"); + if (ret) err(1, "ioctl SUB_BASELINE"); arg = true; ret = ioctl(kvm_dev, KVM_CPC_MEASURE_BASELINE, &arg); - if (ret == -1) err(1, "ioctl MEASURE_BASELINE"); + if (ret) err(1, "ioctl MEASURE_BASELINE"); arg = KVM_PAGE_TRACK_ACCESS; ret = ioctl(kvm_dev, KVM_CPC_TRACK_ALL, &arg); @@ -249,7 +297,7 @@ main(int argc, const char **argv) arg = CPC_TRACK_DATA_ACCESS; ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret == -1) err(1, "ioctl TRACK_MODE"); + if (ret) err(1, "ioctl TRACK_MODE"); faultcnt = 0; while (faultcnt < 100) { @@ -258,24 +306,28 @@ main(int argc, const char **argv) do { ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret == -1 && errno != EAGAIN) + if (ret && errno != EAGAIN) err(1, "ioctl POLL_EVENT"); - } while (ret == -1 && errno == EAGAIN); - - arg = CPC_TRACK_NONE; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &arg); - if (ret == -1) err(1, "ioctl TRACK_MODE"); + } 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 == -1) err(1, "ioctl MEASURE_BASELINE"); + if (ret) err(1, "ioctl MEASURE_BASELINE"); ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); - if (ret == -1) err(1, "ioctl READ_BASELINE"); + if (ret) err(1, "ioctl READ_BASELINE"); printf("\n>>> BASELINE:\n"); print_counts(baseline); @@ -291,14 +343,18 @@ main(int argc, const char **argv) arg = true; ret = ioctl(kvm_dev, KVM_CPC_SUB_BASELINE, &arg); - if (ret == -1) err(1, "ioctl SUB_BASELINE"); + if (ret) err(1, "ioctl SUB_BASELINE"); ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret == -1) err(1, "ioctl ACK_EVENT"); + 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/sevstep.c b/test/sevstep.c @@ -109,6 +109,9 @@ vm_guest_with(void) while (1) { asm volatile("mov (%0), %%eax" : : "r" (L1_LINESIZE * (L1_SETS * 3 + TARGET_SET)) : "rax"); + asm volatile("nop"); + asm volatile("mov (%0), %%eax" : : + "r" (L1_LINESIZE * (L1_SETS * 3 + TARGET_SET)) : "rax"); } } @@ -231,7 +234,7 @@ snp_dbg_decrypt(int vmfd, void *dst, void *src, size_t size) struct kvm_sev_dbg enc; int ret, fwerr; - assert(false); /* ioctl not implemented yet */ + // assert(false); /* ioctl not implemented yet */ memset(&enc, 0, sizeof(struct kvm_sev_dbg)); enc.src_uaddr = (uintptr_t) src; @@ -246,22 +249,14 @@ snp_dbg_decrypt(int vmfd, void *dst, void *src, size_t size) uint64_t snp_dbg_rip(int vmfd) { - void *vmsa; + uint8_t vmsa[PAGE_SIZE]; uint64_t rip; - int ret; - vmsa = NULL; - if (posix_memalign(&vmsa, PAGE_SIZE, PAGE_SIZE)) - err(1, "memalign"); memset(vmsa, 0, PAGE_SIZE); - snp_dbg_decrypt(vmfd, vmsa, CPC_VMSA_MAGIC_ADDR, PAGE_SIZE); - // hexdump(vmsa, PAGE_SIZE); rip = *(uint64_t *)(vmsa + 0x178); - free(vmsa); - return rip; } @@ -447,7 +442,6 @@ runonce(struct kvm *kvm) int monitor(struct kvm *kvm, bool baseline) { - static uint64_t rip_prev = 1; struct cpc_event event; cpc_msrmt_t counts[64]; uint64_t rip; @@ -463,18 +457,16 @@ monitor(struct kvm *kvm, bool baseline) if (ret == -1) err(1, "ioctl READ_COUNTS"); rip = 0; // snp_dbg_rip(kvm->vmfd); - if (!baseline && rip != rip_prev) { - printf("Event: inst:%llu data:%llu retired:%llu rip:%lu\n", - event.track.inst_fault_gfn, - event.track.data_fault_gfn, + if (!baseline) { + printf("Event: cnt:%llu inst:%llu data:%llu retired:%llu rip:%lu\n", + event.track.fault_count, + event.track.fault_gfns[0], + event.track.fault_gfns[1], event.track.retinst, rip); print_counts(counts); printf("\n"); - rip_prev = rip; - faultcnt++; - } else if (baseline) { - faultcnt++; } + faultcnt++; for (i = 0; i < 64; i++) { if (counts[i] > 8) { diff --git a/util/.gitignore b/util/.gitignore @@ -0,0 +1,2 @@ +debug +reset diff --git a/test/debug.c b/util/debug.c diff --git a/util/reset.c b/util/reset.c @@ -0,0 +1,29 @@ +#include "cachepc/uapi.h" + +#include <sys/ioctl.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdint.h> +#include <err.h> +#include <fcntl.h> +#include <unistd.h> + +int +main(int argc, const char **argv) +{ + uint64_t arg; + int fd, ret; + + fd = open("/dev/kvm", O_RDONLY); + if (fd < 0) err(1, "open"); + + ret = ioctl(fd, KVM_CPC_RESET_TRACKING); + if (ret) warn("ioctl RESET_TRACKING"); + + arg = 0; + ret = ioctl(fd, KVM_CPC_ACK_EVENT, &arg); + if (ret) warn("ioctl ACK_EVENT"); + + close(fd); +}