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:
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);
+}