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 9f603b90b8de3028791f8e487a3981ac8843c436
parent 27ac7a95b69d70622f281c1b8d0300d38e5c541d
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 30 Jan 2023 13:01:00 +0100

Event batching

Diffstat:
Mcachepc/event.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mcachepc/event.h | 9+++++++++
Mcachepc/kvm.c | 124++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mcachepc/uapi.h | 16++++++++++++----
4 files changed, 154 insertions(+), 83 deletions(-)

diff --git a/cachepc/event.c b/cachepc/event.c @@ -13,6 +13,10 @@ #define ARRLEN(x) (sizeof(x)/sizeof((x)[0])) +struct cpc_event *cpc_eventbuf; +size_t cpc_eventbuf_len; +bool cpc_event_batching; + uint64_t cpc_last_event_sent; uint64_t cpc_last_event_acked; rwlock_t cpc_event_lock; @@ -20,13 +24,32 @@ rwlock_t cpc_event_lock; struct cpc_event cpc_event; bool cpc_event_avail; -bool cpc_events_init; +EXPORT_SYMBOL(cpc_send_guest_event); +EXPORT_SYMBOL(cpc_send_pause_event); +EXPORT_SYMBOL(cpc_send_track_step_event); +EXPORT_SYMBOL(cpc_send_track_step_event_single); +EXPORT_SYMBOL(cpc_send_track_page_event); + +void +cpc_events_init(void) +{ + cpc_eventbuf = NULL; + cpc_eventbuf_len = 0; + cpc_event_batching = false; + cpc_events_reset(); +} + +void +cpc_events_deinit(void) +{ + kfree(cpc_eventbuf); + cpc_eventbuf = NULL; +} void cpc_events_reset(void) { write_lock(&cpc_event_lock); - cpc_events_init = true; cpc_last_event_sent = 1; cpc_last_event_acked = 1; cpc_event_avail = false; @@ -38,28 +61,31 @@ cpc_send_event(struct cpc_event event) { ktime_t deadline; - read_lock(&cpc_event_lock); - if (!cpc_events_init) { - CPC_WARN("events ctx not initialized!\n"); - read_unlock(&cpc_event_lock); - return 1; - } - read_unlock(&cpc_event_lock); - write_lock(&cpc_event_lock); if (cpc_last_event_sent != cpc_last_event_acked) { CPC_WARN("event IDs out of sync\n"); write_unlock(&cpc_event_lock); return 1; + } + + if (cpc_event_batching) { + if (cpc_eventbuf_len < CPC_EVENTBUF_CAP) { + memcpy(&cpc_eventbuf[cpc_eventbuf_len], &event, + sizeof(struct cpc_event)); + cpc_eventbuf_len++; + write_unlock(&cpc_event_lock); + return 0; + } else { + cpc_event.type = CPC_EVENT_BATCH; + } } else { - cpc_last_event_sent++; + cpc_event = event; } + cpc_last_event_sent++; event.id = cpc_last_event_sent; cpc_event_avail = true; - cpc_event = event; - //CPC_DBG("Sent Event: id %llu\n", event.id); write_unlock(&cpc_event_lock); /* wait for ack with timeout */ @@ -86,7 +112,6 @@ cpc_send_guest_event(uint64_t type, uint64_t val) return cpc_send_event(event); } -EXPORT_SYMBOL(cpc_send_guest_event); int cpc_send_pause_event(void) @@ -97,7 +122,6 @@ cpc_send_pause_event(void) return cpc_send_event(event); } -EXPORT_SYMBOL(cpc_send_pause_event); int cpc_send_track_step_event(struct list_head *list) @@ -122,7 +146,6 @@ cpc_send_track_step_event(struct list_head *list) return cpc_send_event(event); } -EXPORT_SYMBOL(cpc_send_track_step_event); int cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint64_t retinst) @@ -136,7 +159,6 @@ cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint64_t retinst) return cpc_send_event(event); } -EXPORT_SYMBOL(cpc_send_track_page_event); int cpc_send_track_step_event_single(uint64_t gfn, uint32_t err, uint64_t retinst) @@ -152,7 +174,6 @@ cpc_send_track_step_event_single(uint64_t gfn, uint32_t err, uint64_t retinst) return cpc_send_event(event); } -EXPORT_SYMBOL(cpc_send_track_step_event_single); bool cpc_event_is_done(void) @@ -220,3 +241,34 @@ cpc_ack_event_ioctl(void __user *arg_user) return err; } +int +cpc_read_events_ioctl(void __user *arg_user) +{ + struct cpc_event_batch batch; + size_t n; + + if (!arg_user) return -EINVAL; + + if (copy_from_user(&batch, arg_user, sizeof(batch))) + return -EFAULT; + + if (!batch.buf || !batch.maxcnt) return -EINVAL; + + write_lock(&cpc_event_lock); + if (!cpc_eventbuf_len) { + write_unlock(&cpc_event_lock); + return -EAGAIN; + } + + n = cpc_eventbuf_len; + if (batch.maxcnt < n) + n = batch.maxcnt; + + if (copy_to_user(batch.buf, cpc_eventbuf, sizeof(struct cpc_event) * n)) { + write_unlock(&cpc_event_lock); + return -EFAULT; + } + write_unlock(&cpc_event_lock); + + return 0; +} diff --git a/cachepc/event.h b/cachepc/event.h @@ -7,6 +7,14 @@ #include <linux/kvm_host.h> #include <linux/types.h> +#define CPC_EVENTBUF_CAP 5000 + +extern struct cpc_event *cpc_eventbuf; +extern size_t cpc_eventbuf_len; +extern bool cpc_event_batching; + +void cpc_event_init(void); +void cpc_event_deinit(void); void cpc_events_reset(void); int cpc_send_guest_event(uint64_t type, uint64_t val); @@ -19,3 +27,4 @@ bool cpc_event_is_done(void); int cpc_poll_event_ioctl(void __user *arg_user); int cpc_ack_event_ioctl(void __user *arg_user); +int cpc_read_events_ioctl(void __user *arg_user); diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -99,31 +99,31 @@ static noinline void cpc_stream_hwpf_test(void); void cpc_single_eviction_test_asm(void *ptr); static noinline void cpc_single_eviction_test(void *p); -static void cpc_kvm_pmc_setup(void *p); -static void cpc_kvm_system_setup(void); +static void cpc_pmc_setup(void *p); +static void cpc_system_setup(void); -static int cpc_kvm_reset_ioctl(void __user *arg_user); -static int cpc_kvm_debug_ioctl(void __user *arg_user); +static int cpc_reset_ioctl(void __user *arg_user); +static int cpc_debug_ioctl(void __user *arg_user); -static int cpc_kvm_memory_encrypt_op_ioctl(void __user *arg_user); +static int cpc_memory_encrypt_op_ioctl(void __user *arg_user); -static int cpc_kvm_test_eviction_ioctl(void __user *arg_user); +static int cpc_test_eviction_ioctl(void __user *arg_user); -static int cpc_kvm_read_counts_ioctl(void __user *arg_user); +static int cpc_read_counts_ioctl(void __user *arg_user); -static int cpc_kvm_reset_baseline_ioctl(void __user *arg_user); -static int cpc_kvm_calc_baseline_ioctl(void __user *arg_user); -static int cpc_kvm_read_baseline_ioctl(void __user *arg_user); -static int cpc_kvm_apply_baseline_ioctl(void __user *arg_user); +static int cpc_reset_baseline_ioctl(void __user *arg_user); +static int cpc_calc_baseline_ioctl(void __user *arg_user); +static int cpc_read_baseline_ioctl(void __user *arg_user); +static int cpc_apply_baseline_ioctl(void __user *arg_user); -static int cpc_kvm_reset_tracking_ioctl(void __user *arg_user); -static int cpc_kvm_track_mode_ioctl(void __user *arg_user); -// static int cpc_kvm_track_page_ioctl(void __user *arg_user); -// static int cpc_kvm_track_range_start_ioctl(void __user *arg_user); -// static int cpc_kvm_track_range_end_ioctl(void __user *arg_user); -// static int cpc_kvm_track_exec_cur_ioctl(void __user *arg_user); +static int cpc_reset_tracking_ioctl(void __user *arg_user); +static int cpc_track_mode_ioctl(void __user *arg_user); +// static int cpc_track_page_ioctl(void __user *arg_user); +// static int cpc_track_range_start_ioctl(void __user *arg_user); +// static int cpc_track_range_end_ioctl(void __user *arg_user); +// static int cpc_track_exec_cur_ioctl(void __user *arg_user); -static int cpc_kvm_req_pause_ioctl(void __user *arg_user); +static int cpc_req_pause_ioctl(void __user *arg_user); void cpc_prime_probe_test(void) @@ -228,7 +228,7 @@ cpc_single_eviction_test(void *p) } void -cpc_kvm_pmc_setup(void *p) +cpc_pmc_setup(void *p) { /* L1 misses in host kernel */ cpc_init_pmc(CPC_L1MISS_PMC, 0x64, 0xD8, @@ -240,7 +240,7 @@ cpc_kvm_pmc_setup(void *p) } void -cpc_kvm_system_setup(void) +cpc_system_setup(void) { /* NOTE: since most of these MSRs are poorly documented and some * guessing work was involved, it is likely that one or more of @@ -270,18 +270,18 @@ cpc_kvm_system_setup(void) } int -cpc_kvm_reset_ioctl(void __user *arg_user) +cpc_reset_ioctl(void __user *arg_user) { int ret; ret = smp_call_function_single(CPC_ISOLCPU, - cpc_kvm_pmc_setup, NULL, true); + cpc_pmc_setup, NULL, true); if (ret) return -EFAULT; cpc_events_reset(); - cpc_kvm_reset_tracking_ioctl(NULL); - cpc_kvm_reset_baseline_ioctl(NULL); + cpc_reset_tracking_ioctl(NULL); + cpc_reset_baseline_ioctl(NULL); cpc_pause_vm = false; @@ -300,7 +300,7 @@ cpc_kvm_reset_ioctl(void __user *arg_user) } int -cpc_kvm_debug_ioctl(void __user *arg_user) +cpc_debug_ioctl(void __user *arg_user) { uint32_t debug; @@ -315,7 +315,7 @@ cpc_kvm_debug_ioctl(void __user *arg_user) } int -cpc_kvm_memory_encrypt_op_ioctl(void __user *arg_user) +cpc_memory_encrypt_op_ioctl(void __user *arg_user) { if (!arg_user || !main_vm) return -EFAULT; @@ -323,7 +323,7 @@ cpc_kvm_memory_encrypt_op_ioctl(void __user *arg_user) } int -cpc_kvm_test_eviction_ioctl(void __user *arg_user) +cpc_test_eviction_ioctl(void __user *arg_user) { uint32_t u32; int ret; @@ -344,7 +344,7 @@ cpc_kvm_test_eviction_ioctl(void __user *arg_user) } int -cpc_kvm_read_counts_ioctl(void __user *arg_user) +cpc_read_counts_ioctl(void __user *arg_user) { if (!arg_user) return -EINVAL; @@ -355,7 +355,7 @@ cpc_kvm_read_counts_ioctl(void __user *arg_user) } int -cpc_kvm_reset_baseline_ioctl(void __user *arg_user) +cpc_reset_baseline_ioctl(void __user *arg_user) { if (arg_user) return -EINVAL; @@ -367,7 +367,7 @@ cpc_kvm_reset_baseline_ioctl(void __user *arg_user) } int -cpc_kvm_calc_baseline_ioctl(void __user *arg_user) +cpc_calc_baseline_ioctl(void __user *arg_user) { uint32_t state; @@ -382,7 +382,7 @@ cpc_kvm_calc_baseline_ioctl(void __user *arg_user) } int -cpc_kvm_read_baseline_ioctl(void __user *arg_user) +cpc_read_baseline_ioctl(void __user *arg_user) { if (!arg_user) return -EINVAL; @@ -393,7 +393,7 @@ cpc_kvm_read_baseline_ioctl(void __user *arg_user) } int -cpc_kvm_apply_baseline_ioctl(void __user *arg_user) +cpc_apply_baseline_ioctl(void __user *arg_user) { uint32_t state; @@ -408,7 +408,7 @@ cpc_kvm_apply_baseline_ioctl(void __user *arg_user) } int -cpc_kvm_long_step_ioctl(void __user *arg_user) +cpc_long_step_ioctl(void __user *arg_user) { if (arg_user) return -EINVAL; @@ -418,7 +418,7 @@ cpc_kvm_long_step_ioctl(void __user *arg_user) } int -cpc_kvm_reset_tracking_ioctl(void __user *arg_user) +cpc_reset_tracking_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; struct cpc_fault *fault, *next; @@ -450,7 +450,7 @@ cpc_kvm_reset_tracking_ioctl(void __user *arg_user) } int -cpc_kvm_track_mode_ioctl(void __user *arg_user) +cpc_track_mode_ioctl(void __user *arg_user) { struct kvm_vcpu *vcpu; uint32_t mode; @@ -513,7 +513,7 @@ cpc_kvm_track_mode_ioctl(void __user *arg_user) } // int -// cpc_kvm_track_page_ioctl(void __user *arg_user) +// cpc_track_page_ioctl(void __user *arg_user) // { // struct cpc_track_config cfg; // struct kvm_vcpu *vcpu; @@ -535,7 +535,7 @@ cpc_kvm_track_mode_ioctl(void __user *arg_user) // } // // int -// cpc_kvm_track_range_start_ioctl(void __user *arg_user) +// cpc_track_range_start_ioctl(void __user *arg_user) // { // if (!arg_user) return -EINVAL; // @@ -546,7 +546,7 @@ cpc_kvm_track_mode_ioctl(void __user *arg_user) // } // // int -// cpc_kvm_track_range_end_ioctl(void __user *arg_user) +// cpc_track_range_end_ioctl(void __user *arg_user) // { // if (!arg_user) return -EINVAL; // @@ -557,7 +557,7 @@ cpc_kvm_track_mode_ioctl(void __user *arg_user) // } // // int -// cpc_kvm_track_exec_cur_ioctl(void __user *arg_user) +// cpc_track_exec_cur_ioctl(void __user *arg_user) // { // struct cpc_fault *fault; // @@ -573,7 +573,7 @@ cpc_kvm_track_mode_ioctl(void __user *arg_user) // } int -cpc_kvm_req_pause_ioctl(void __user *arg_user) +cpc_req_pause_ioctl(void __user *arg_user) { if (arg_user) return -EINVAL; @@ -583,57 +583,59 @@ cpc_kvm_req_pause_ioctl(void __user *arg_user) } long -cpc_kvm_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) +cpc_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) { void __user *arg_user; arg_user = (void __user *)arg; switch (ioctl) { case KVM_CPC_RESET: - return cpc_kvm_reset_ioctl(arg_user); + return cpc_reset_ioctl(arg_user); case KVM_CPC_DEBUG: - return cpc_kvm_debug_ioctl(arg_user); + return cpc_debug_ioctl(arg_user); case KVM_CPC_MEMORY_ENCRYPT_OP: - return cpc_kvm_memory_encrypt_op_ioctl(arg_user); + return cpc_memory_encrypt_op_ioctl(arg_user); case KVM_CPC_TEST_EVICTION: - return cpc_kvm_test_eviction_ioctl(arg_user); + return cpc_test_eviction_ioctl(arg_user); case KVM_CPC_READ_COUNTS: - return cpc_kvm_read_counts_ioctl(arg_user); + return cpc_read_counts_ioctl(arg_user); case KVM_CPC_RESET_BASELINE: - return cpc_kvm_reset_baseline_ioctl(arg_user); + return cpc_reset_baseline_ioctl(arg_user); case KVM_CPC_READ_BASELINE: - return cpc_kvm_read_baseline_ioctl(arg_user); + return cpc_read_baseline_ioctl(arg_user); case KVM_CPC_CALC_BASELINE: - return cpc_kvm_calc_baseline_ioctl(arg_user); + return cpc_calc_baseline_ioctl(arg_user); case KVM_CPC_APPLY_BASELINE: - return cpc_kvm_apply_baseline_ioctl(arg_user); + return cpc_apply_baseline_ioctl(arg_user); case KVM_CPC_LONG_STEP: - return cpc_kvm_long_step_ioctl(arg_user); + return cpc_long_step_ioctl(arg_user); case KVM_CPC_RESET_TRACKING: - return cpc_kvm_reset_tracking_ioctl(arg_user); + return cpc_reset_tracking_ioctl(arg_user); case KVM_CPC_TRACK_MODE: - return cpc_kvm_track_mode_ioctl(arg_user); + return cpc_track_mode_ioctl(arg_user); case KVM_CPC_POLL_EVENT: return cpc_poll_event_ioctl(arg_user); case KVM_CPC_ACK_EVENT: return cpc_ack_event_ioctl(arg_user); + case KVM_CPC_READ_EVENTS: + return cpc_read_events_ioctl(arg_user); // case KVM_CPC_TRACK_PAGE: - // return cpc_kvm_track_page_ioctl(arg_user); + // return cpc_track_page_ioctl(arg_user); // case KVM_CPC_TRACK_RANGE_START: - // return cpc_kvm_track_range_start_ioctl(arg_user); + // return cpc_track_range_start_ioctl(arg_user); // case KVM_CPC_TRACK_RANGE_END: - // return cpc_kvm_track_range_end_ioctl(arg_user); + // return cpc_track_range_end_ioctl(arg_user); // case KVM_CPC_TRACK_EXEC_CUR: - // return cpc_kvm_track_exec_cur_ioctl(arg_user); + // return cpc_track_exec_cur_ioctl(arg_user); case KVM_CPC_VM_REQ_PAUSE: - return cpc_kvm_req_pause_ioctl(arg_user); + return cpc_req_pause_ioctl(arg_user); default: return kvm_arch_dev_ioctl(file, ioctl, arg); } } void -cpc_kvm_setup_test(void *p) +cpc_setup_test(void *p) { spinlock_t lock; int cpu; @@ -647,7 +649,7 @@ cpc_kvm_setup_test(void *p) cpc_ds = cpc_ds_alloc(&cpc_ds_ul); - cpc_kvm_system_setup(); + cpc_system_setup(); spin_lock_irq(&lock); cpc_prime_probe_test(); @@ -692,7 +694,7 @@ cpc_kvm_init(void) cpc_events_reset(); ret = smp_call_function_single(CPC_ISOLCPU, - cpc_kvm_setup_test, NULL, true); + cpc_setup_test, NULL, true); WARN_ON(ret != 0); } diff --git a/cachepc/uapi.h b/cachepc/uapi.h @@ -25,11 +25,12 @@ #define KVM_CPC_LONG_STEP _IO(KVMIO, 0x2A) -#define KVM_CPC_TRACK_MODE _IOWR(KVMIO, 0x40, __u32) -#define KVM_CPC_RESET_TRACKING _IO(KVMIO, 0x44) +#define KVM_CPC_TRACK_MODE _IOWR(KVMIO, 0x30, __u32) +#define KVM_CPC_RESET_TRACKING _IO(KVMIO, 0x31) -#define KVM_CPC_POLL_EVENT _IOWR(KVMIO, 0x48, struct cpc_event) -#define KVM_CPC_ACK_EVENT _IOWR(KVMIO, 0x49, __u64) +#define KVM_CPC_POLL_EVENT _IOWR(KVMIO, 0x40, struct cpc_event) +#define KVM_CPC_ACK_EVENT _IOWR(KVMIO, 0x41, __u64) +#define KVM_CPC_READ_EVENTS _IOWR(KVMIO, 0x42, struct cpc_event_batch) #define KVM_CPC_VM_REQ_PAUSE _IO(KVMIO, 0x50) @@ -41,6 +42,7 @@ enum { CPC_EVENT_TRACK_PAGE, CPC_EVENT_PAUSE, CPC_EVENT_GUEST, + CPC_EVENT_BATCH }; enum { @@ -96,6 +98,12 @@ struct cpc_event { }; }; +struct cpc_event_batch { + __u64 cnt; + __u64 maxcnt; + struct cpc_event *buf; +}; + struct cpc_sev_cmd { __u32 id; __u64 data;