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

qemu-targetstep.c (5678B)


      1#include "test/kvm-eviction.h"
      2#include "test/kvm.h"
      3#include "test/util.h"
      4#include "cachepc/uapi.h"
      5
      6#include <sys/ioctl.h>
      7#include <sys/mman.h>
      8#include <signal.h>
      9#include <unistd.h>
     10#include <fcntl.h>
     11#include <errno.h>
     12#include <err.h>
     13#include <string.h>
     14#include <stdbool.h>
     15#include <stdio.h>
     16#include <stdlib.h>
     17
     18static struct cpc_event event;
     19static struct cpc_event_batch batch;
     20static uint64_t last_user_inst_gfn;
     21
     22int
     23monitor(bool baseline)
     24{
     25	uint8_t counts[L1_SETS];
     26	int ret;
     27
     28	ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event);
     29	if (ret && errno == EAGAIN) return 0;
     30	if (ret) err(1, "KVM_CPC_POLL_EVENT");
     31
     32	switch (event.type) {
     33	case CPC_EVENT_GUEST:
     34		printf("Guest %s\n", !event.guest.type ? "start" : "stop");
     35		if (event.guest.type == CPC_GUEST_STOP_TRACK)
     36			return 2;
     37		break;
     38	case CPC_EVENT_TRACK_STEP:
     39		ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts);
     40		if (ret) err(1, "KVM_CPC_READ_COUNTS");
     41
     42		printf("Event: rip:%016llx cnt:%llu "
     43			"inst:%08llx ret:%llu\n",
     44			vm_get_rip(), event.step.fault_count,
     45			event.step.inst_gfn, event.step.retinst);
     46		print_counts(counts);
     47		printf("\n");
     48		print_counts_raw(counts);
     49		printf("\n");
     50		break;
     51	default:
     52		errx(1, "unexpected event type %i", event.type);
     53	}
     54
     55	ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id);
     56	if (ret) err(1, "KVM_CPC_ACK_EVENT");
     57
     58	return 1;
     59}
     60
     61void
     62read_batch(void)
     63{
     64	uint32_t i;
     65	int ret;
     66
     67	ret = ioctl(kvm_dev, KVM_CPC_READ_BATCH, &batch);
     68	if (ret && errno == EAGAIN) return;
     69	if (ret && errno != EAGAIN) err(1, "KVM_CPC_READ_BATCH");
     70
     71	for (i = 0; i < batch.cnt; i++) {
     72		if (batch.buf[i].type != CPC_EVENT_TRACK_PAGE)
     73			continue;
     74
     75		if (batch.buf[i].page.retinst_user > 0) {
     76			printf("GFN %08llx %04x %4llu %4llu\n",
     77				batch.buf[i].page.inst_gfn,
     78				batch.buf[i].page.fault_err,
     79				batch.buf[i].page.retinst,
     80				batch.buf[i].page.retinst_user);
     81			last_user_inst_gfn = batch.buf[i].page.inst_gfn;
     82		}
     83	}
     84}
     85
     86void
     87reset(int sig)
     88{
     89	int ret;
     90
     91	ret = ioctl(kvm_dev, KVM_CPC_RESET);
     92	if (ret) err(1, "KVM_CPC_RESET");
     93
     94	exit(1);
     95}
     96
     97int
     98main(int argc, const char **argv)
     99{
    100	uint8_t baseline[L1_SETS];
    101	struct cpc_track_cfg cfg;
    102	bool first_guest_event;
    103	uint32_t eventcnt;
    104	uint32_t arg;
    105	int ret;
    106
    107	pin_process(0, SECONDARY_CORE, true);
    108
    109	setvbuf(stdout, NULL, _IONBF, 0);
    110
    111	kvm_setup_init();
    112
    113	ret = ioctl(kvm_dev, KVM_CPC_RESET);
    114	if (ret) err(1, "KVM_CPC_RESET");
    115
    116	signal(SIGINT, reset);
    117
    118	arg = true;
    119	ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg);
    120	if (ret) err(1, "KVM_CPC_CALC_BASELINE");
    121
    122	memset(&cfg, 0, sizeof(cfg));
    123	cfg.mode = CPC_TRACK_STEPS;
    124	cfg.steps.with_data = true;
    125	cfg.steps.use_filter = true;
    126	ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg);
    127	if (ret) err(1, "KVM_CPC_RESET");
    128
    129	eventcnt = 0;
    130	while (eventcnt < 50) {
    131		eventcnt += monitor(true);
    132	}
    133
    134	ret = ioctl(kvm_dev, KVM_CPC_VM_REQ_PAUSE);
    135	if (ret) err(1, "KVM_CPC_VM_REQ_PAUSE");
    136
    137	while (1) {
    138		ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event);
    139		if (ret && errno == EAGAIN) continue;
    140		if (ret) err(1, "KVM_CPC_POLL_EVENT");
    141
    142		if (event.type == CPC_EVENT_PAUSE) break;
    143
    144		ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id);
    145		if (ret) err(1, "KVM_CPC_ACK_EVENT");
    146	}
    147
    148	arg = false;
    149	ret = ioctl(kvm_dev, KVM_CPC_CALC_BASELINE, &arg);
    150	if (ret) err(1, "KVM_CPC_CALC_BASELINE");
    151
    152	arg = true;
    153	ret = ioctl(kvm_dev, KVM_CPC_APPLY_BASELINE, &arg);
    154	if (ret) err(1, "KVM_CPC_APPLY_BASELINE");
    155
    156	ret = ioctl(kvm_dev, KVM_CPC_READ_BASELINE, baseline);
    157	if (ret) err(1, "KVM_CPC_READ_BASELINE");
    158
    159	printf("\nBaseline:\n");
    160	print_counts(baseline);
    161	printf("\n");
    162	print_counts_raw(baseline);
    163	printf("\n\n");
    164
    165	memset(&cfg, 0, sizeof(&cfg));
    166	cfg.mode = CPC_TRACK_NONE;
    167	ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg);
    168	if (ret) err(1, "KVM_CPC_TRACK_MODE");
    169
    170	ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id);
    171	if (ret) err(1, "KVM_CPC_ACK_EVENT");
    172
    173	/* wait until guest program is run */
    174	printf("Press enter to continue..\n");
    175	getchar();
    176
    177	arg = true;
    178	ret = ioctl(kvm_dev, KVM_CPC_BATCH_EVENTS, &arg);
    179	if (ret) err(1, "KVM_CPC_BATCH_EVENTS");
    180
    181	memset(&cfg, 0, sizeof(cfg));
    182	cfg.mode = CPC_TRACK_PAGES;
    183	ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg);
    184	if (ret) err(1, "KVM_CPC_TRACK_MODE");
    185
    186	batch.cnt = 0;
    187	batch.maxcnt = CPC_EVENT_BATCH_MAX;
    188	batch.buf = malloc(sizeof(struct cpc_event) * batch.maxcnt);
    189	if (!batch.buf) err(1, "malloc");
    190
    191	first_guest_event = true;
    192	while (1) {
    193		ret = ioctl(kvm_dev, KVM_CPC_POLL_EVENT, &event);
    194		if (ret && errno == EAGAIN) continue;
    195		if (ret) err(1, "KVM_CPC_POLL_EVENT");
    196
    197		if (event.type == CPC_EVENT_GUEST) {
    198			read_batch();
    199			printf("Guest %s\n",
    200				!event.guest.type ? "start" : "stop");
    201		}
    202
    203		if (event.type == CPC_EVENT_GUEST
    204				&& event.guest.type == CPC_GUEST_START_TRACK) {
    205			if (!first_guest_event)
    206				break;
    207			first_guest_event = false;
    208		}
    209
    210		if (event.type == CPC_EVENT_BATCH)
    211			read_batch();
    212
    213		ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id);
    214		if (ret) err(1, "KVM_CPC_ACK_EVENT");
    215	}
    216
    217	read_batch();
    218
    219	if (!batch.cnt) errx(1, "empty batch buffer");
    220	memset(&cfg, 0, sizeof(cfg));
    221	cfg.mode = CPC_TRACK_STEPS;
    222	cfg.steps.target_gfn = last_user_inst_gfn;
    223	cfg.steps.target_user = true;
    224	cfg.steps.use_target = true;
    225	cfg.steps.use_filter = true;
    226	//cfg.steps.with_data = true;
    227	ret = ioctl(kvm_dev, KVM_CPC_TRACK_MODE, &cfg);
    228	if (ret) err(1, "KVM_CPC_TRACK_MODE");
    229
    230	ret = ioctl(kvm_dev, KVM_CPC_ACK_EVENT, &event.id);
    231	if (ret) err(1, "KVM_CPC_ACK_EVENT");
    232
    233	printf("Target GFN: %08llx\n", cfg.steps.target_gfn);
    234
    235	while (monitor(false) != 2);
    236	read_batch();
    237
    238	signal(SIGINT, NULL);
    239
    240	ret = ioctl(kvm_dev, KVM_CPC_RESET);
    241	if (ret) err(1, "KVM_CPC_RESET");
    242
    243	free(batch.buf);
    244
    245	kvm_setup_deinit();
    246}
    247