commit a554be1738d019e4b5d5b0b0ee9aac6b6ac302a6
parent 769e05dd63ed0379e7325da6e82c0c46c151ef4e
Author: Louis Burda <quent.burda@gmail.com>
Date: Mon, 23 Jan 2023 20:38:36 +0100
Use 16-bit realmode assembly for guests (!)
Diffstat:
12 files changed, 200 insertions(+), 47 deletions(-)
diff --git a/Makefile b/Makefile
@@ -51,7 +51,7 @@ load:
prep:
sudo sh -c "echo 0 > /proc/sys/kernel/watchdog"
- sudo cpupower frequency-set -f 3.7GHz
+ sudo cpupower frequency-set -d 3.7GHz -u 3.7GHz
util/%: util/%.c $(CACHEPC_UAPI)
diff --git a/cachepc/cachepc.c b/cachepc/cachepc.c
@@ -534,7 +534,7 @@ is_in_arr(uint32_t elem, uint32_t *arr, uint32_t arr_len)
}
void
-cachepc_apic_oneshot(uint32_t interval)
+cachepc_apic_oneshot_run(uint32_t interval)
{
native_apic_mem_write(APIC_LVTT, LOCAL_TIMER_VECTOR | APIC_LVT_TIMER_ONESHOT);
native_apic_mem_write(APIC_TDCR, APIC_TDR_DIV_1);
diff --git a/cachepc/cachepc.h b/cachepc/cachepc.h
@@ -97,7 +97,7 @@ void cachepc_probe(cacheline *head);
uint64_t cachepc_read_pmc(uint64_t event);
-void cachepc_apic_oneshot(uint32_t interval);
+void cachepc_apic_oneshot_run(uint32_t interval);
extern bool cachepc_debug;
@@ -112,6 +112,9 @@ extern bool cachepc_pause_vm;
extern bool cachepc_singlestep;
extern bool cachepc_singlestep_reset;
+extern bool cachepc_long_step;
+
+extern bool cachepc_apic_oneshot;
extern uint32_t cachepc_apic_timer;
extern uint32_t cachepc_track_mode;
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() + 20000000000ULL; /* 20s in ns */
+ deadline = ktime_get_ns() + 10000000000ULL; /* 10s in ns */
while (!cachepc_event_is_done(cachepc_event.id)) {
if (ktime_get_ns() > deadline) {
CPC_WARN("Timeout waiting for ack of event %llu\n",
diff --git a/cachepc/kvm.c b/cachepc/kvm.c
@@ -51,9 +51,14 @@ EXPORT_SYMBOL(cachepc_rip_prev_set);
bool cachepc_singlestep = false;
bool cachepc_singlestep_reset = false;
-uint32_t cachepc_apic_timer = 0;
+bool cachepc_long_step = false;
EXPORT_SYMBOL(cachepc_singlestep);
EXPORT_SYMBOL(cachepc_singlestep_reset);
+EXPORT_SYMBOL(cachepc_long_step);
+
+bool cachepc_apic_oneshot = false;
+uint32_t cachepc_apic_timer = 0;
+EXPORT_SYMBOL(cachepc_apic_oneshot);
EXPORT_SYMBOL(cachepc_apic_timer);
uint32_t cachepc_track_mode = false;
@@ -293,7 +298,11 @@ cachepc_kvm_reset_ioctl(void __user *arg_user)
cachepc_singlestep = false;
cachepc_singlestep_reset = false;
+
+ cachepc_apic_oneshot = false;
cachepc_apic_timer = 0;
+
+ cachepc_retinst = 0;
cachepc_rip_prev_set = false;
return 0;
@@ -315,6 +324,54 @@ cachepc_kvm_debug_ioctl(void __user *arg_user)
}
int
+cachepc_kvm_get_regs_ioctl(void __user *arg_user)
+{
+ struct kvm_regs *regs;
+ struct kvm_vcpu *vcpu;
+
+ if (!arg_user) return -EINVAL;
+
+ if (!main_vm || xa_empty(&main_vm->vcpu_array))
+ return -EFAULT;
+
+ vcpu = xa_load(&main_vm->vcpu_array, 0);
+
+ regs = kzalloc(sizeof(struct kvm_regs), GFP_KERNEL_ACCOUNT);
+ if (!regs) return -ENOMEM;
+
+ regs->rax = kvm_rax_read(vcpu);
+ regs->rbx = kvm_rbx_read(vcpu);
+ regs->rcx = kvm_rcx_read(vcpu);
+ regs->rdx = kvm_rdx_read(vcpu);
+ regs->rsi = kvm_rsi_read(vcpu);
+ regs->rdi = kvm_rdi_read(vcpu);
+ regs->rsp = kvm_rsp_read(vcpu);
+ regs->rbp = kvm_rbp_read(vcpu);
+#ifdef CONFIG_X86_64
+ regs->r8 = kvm_r8_read(vcpu);
+ regs->r9 = kvm_r9_read(vcpu);
+ regs->r10 = kvm_r10_read(vcpu);
+ regs->r11 = kvm_r11_read(vcpu);
+ regs->r12 = kvm_r12_read(vcpu);
+ regs->r13 = kvm_r13_read(vcpu);
+ regs->r14 = kvm_r14_read(vcpu);
+ regs->r15 = kvm_r15_read(vcpu);
+#endif
+
+ regs->rip = kvm_rip_read(vcpu);
+ regs->rflags = kvm_get_rflags(vcpu);
+
+ if (copy_to_user(arg_user, regs, sizeof(struct kvm_regs))) {
+ kfree(regs);
+ return -EFAULT;
+ }
+
+ kfree(regs);
+
+ return 0;
+}
+
+int
cachepc_kvm_test_eviction_ioctl(void __user *arg_user)
{
uint32_t u32;
@@ -400,14 +457,26 @@ cachepc_kvm_apply_baseline_ioctl(void __user *arg_user)
}
int
+cachepc_kvm_long_step_ioctl(void __user *arg_user)
+{
+ if (arg_user) return -EINVAL;
+
+ cachepc_long_step = true;
+
+ return 0;
+}
+
+int
cachepc_kvm_vmsa_read_ioctl(void __user *arg_user)
{
struct kvm_vcpu *vcpu;
struct vcpu_svm *svm;
- if (!main_vm || !arg_user) return -EINVAL;
+ if (!arg_user) return -EINVAL;
+
+ if (!main_vm || xa_empty(&main_vm->vcpu_array))
+ return -EFAULT;
- BUG_ON(xa_empty(&main_vm->vcpu_array));
vcpu = xa_load(&main_vm->vcpu_array, 0);
svm = to_svm(vcpu);
@@ -439,7 +508,9 @@ cachepc_kvm_reset_tracking_ioctl(void __user *arg_user)
struct kvm_vcpu *vcpu;
struct cpc_fault *fault, *next;
- BUG_ON(!main_vm || xa_empty(&main_vm->vcpu_array));
+ if (!main_vm || xa_empty(&main_vm->vcpu_array))
+ return -EFAULT;
+
vcpu = xa_load(&main_vm->vcpu_array, 0);
cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_EXEC);
cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_ACCESS);
@@ -475,7 +546,9 @@ cachepc_kvm_track_mode_ioctl(void __user *arg_user)
if (copy_from_user(&mode, arg_user, sizeof(mode)))
return -EFAULT;
- BUG_ON(!main_vm || xa_empty(&main_vm->vcpu_array));
+ if (!main_vm || xa_empty(&main_vm->vcpu_array))
+ return -EFAULT;
+
vcpu = xa_load(&main_vm->vcpu_array, 0);
cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_EXEC);
@@ -483,8 +556,10 @@ cachepc_kvm_track_mode_ioctl(void __user *arg_user)
cachepc_untrack_all(vcpu, KVM_PAGE_TRACK_WRITE);
cachepc_apic_timer = 0;
+ cachepc_apic_oneshot = false;
cachepc_singlestep = false;
cachepc_singlestep_reset = false;
+ cachepc_long_step = false;
switch (mode) {
case CPC_TRACK_FULL:
@@ -615,6 +690,8 @@ cachepc_kvm_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
return cachepc_kvm_reset_ioctl(arg_user);
case KVM_CPC_DEBUG:
return cachepc_kvm_debug_ioctl(arg_user);
+ case KVM_CPC_GET_REGS:
+ return cachepc_kvm_get_regs_ioctl(arg_user);
case KVM_CPC_TEST_EVICTION:
return cachepc_kvm_test_eviction_ioctl(arg_user);
case KVM_CPC_READ_COUNTS:
@@ -627,6 +704,8 @@ cachepc_kvm_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
return cachepc_kvm_calc_baseline_ioctl(arg_user);
case KVM_CPC_APPLY_BASELINE:
return cachepc_kvm_apply_baseline_ioctl(arg_user);
+ case KVM_CPC_LONG_STEP:
+ return cachepc_kvm_long_step_ioctl(arg_user);
case KVM_CPC_VMSA_READ:
return cachepc_kvm_vmsa_read_ioctl(arg_user);
case KVM_CPC_SVME_READ:
@@ -670,7 +749,7 @@ cachepc_kvm_setup_test(void *p)
cachepc_ctx = cachepc_get_ctx();
cachepc_ds = cachepc_prepare_ds(cachepc_ctx);
- cachepc_victim = cachepc_prepare_victim(cachepc_ctx, 13);
+ cachepc_victim = cachepc_prepare_victim(cachepc_ctx, 15);
cachepc_kvm_system_setup();
@@ -696,10 +775,14 @@ cachepc_kvm_init(void)
cachepc_victim = NULL;
cachepc_retinst = 0;
+ cachepc_long_step = false;
cachepc_singlestep = false;
cachepc_singlestep_reset = false;
cachepc_track_mode = CPC_TRACK_NONE;
+ cachepc_apic_oneshot = false;
+ cachepc_apic_timer = 0;
+
cachepc_inst_fault_gfn = 0;
cachepc_inst_fault_err = 0;
diff --git a/cachepc/uapi.h b/cachepc/uapi.h
@@ -13,7 +13,9 @@
#define KVM_CPC_RESET _IOWR(KVMIO, 0x20, __u32)
#define KVM_CPC_DEBUG _IOW(KVMIO, 0x21, __u32)
-#define KVM_CPC_TEST_EVICTION _IOWR(KVMIO, 0x22, __u32)
+#define KVM_CPC_GET_REGS _IOW(KVMIO, 0x22, __u32)
+
+#define KVM_CPC_TEST_EVICTION _IOWR(KVMIO, 0x23, __u32)
#define KVM_CPC_READ_COUNTS _IOR(KVMIO, 0x25, __u64)
@@ -22,7 +24,7 @@
#define KVM_CPC_CALC_BASELINE _IOR(KVMIO, 0x28, __u32)
#define KVM_CPC_APPLY_BASELINE _IOR(KVMIO, 0x29, __u32)
-#define KVM_CPC_SINGLE_STEP _IO(KVMIO, 0x2A)
+#define KVM_CPC_LONG_STEP _IO(KVMIO, 0x2A)
#define KVM_CPC_VMSA_READ _IOR(KVMIO, 0x2C, __u64)
#define KVM_CPC_SVME_READ _IOR(KVMIO, 0x2D, __u32)
diff --git a/test/kvm-eviction.c b/test/kvm-eviction.c
@@ -50,7 +50,7 @@ vm_init(struct kvm *kvm, void *code_start, void *code_end)
{
size_t ramsize;
- ramsize = L1_SIZE * 2;
+ ramsize = L1_SIZE;
if (!strcmp(vmtype, "kvm")) {
kvm_init(kvm, ramsize, code_start, code_end);
} else if (!strcmp(vmtype, "sev")) {
@@ -77,15 +77,17 @@ collect(struct kvm *kvm, uint8_t *counts)
ret = ioctl(kvm->vcpufd, KVM_RUN, NULL);
if (ret == -1) err(1, "KVM_RUN");
- // warnx("rip:%lu code:%i", vm_get_rip(kvm), kvm->run->exit_reason);
- if (kvm->run->exit_reason != KVM_EXIT_HLT) {
+ if (kvm->run->exit_reason == KVM_EXIT_MMIO) {
+ errx(1, "KVM died from OOB access! rip:%lu addr:%lu",
+ vm_get_rip(kvm), kvm->run->mmio.phys_addr);
+ } else if (kvm->run->exit_reason != KVM_EXIT_HLT) {
errx(1, "KVM died! rip:%lu code:%i",
vm_get_rip(kvm), kvm->run->exit_reason);
}
ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts);
- if (ret == -1) err(1, "ioctl KVM_CPC_READ_COUNTS");
+ if (ret == -1) err(1, "KVM_CPC_READ_COUNTS");
}
int
@@ -114,7 +116,10 @@ main(int argc, const char **argv)
/* reset kernel module state */
ret = ioctl(kvm_dev, KVM_CPC_RESET);
- if (ret == -1) err(1, "ioctl KVM_CPC_RESET");
+ if (ret == -1) err(1, "KVM_CPC_RESET");
+
+ ret = ioctl(kvm_dev, KVM_CPC_LONG_STEP);
+ if (ret == -1) err(1, "KVM_CPC_LONG_STEP");
/* resolve page faults in advance (code only covers 1 page)..
* we want the read counts to apply between KVM_RUN and KVM_EXIT_HLT,
diff --git a/test/kvm-eviction_guest.S b/test/kvm-eviction_guest.S
@@ -7,13 +7,20 @@
.global guest_without_start
.global guest_without_stop
+.align(16)
+.code16gcc
+
guest_with_start:
- mov (L1_LINESIZE * (TARGET_SET + L1_SETS)), %rbx
+ mov $(L1_LINESIZE * (L1_SETS + TARGET_SET)), %bx
+ movb (%bx), %bl
hlt
- jmp guest_with_start
+
+ mov $0x00, %ax
+ jmp *%ax
guest_with_stop:
guest_without_start:
hlt
- jmp guest_without_start
+ mov $0x00, %ax
+ jmp *%ax
guest_without_stop:
diff --git a/test/kvm-step.c b/test/kvm-step.c
@@ -35,19 +35,51 @@
extern uint8_t guest_start[];
extern uint8_t guest_stop[];
-uint8_t *
-read_counts()
+static const char *vmtype;
+
+uint64_t
+vm_get_rip(struct kvm *kvm)
{
- uint8_t *counts;
+ struct kvm_regs regs;
+ uint64_t rip;
int ret;
- counts = malloc(L1_SETS * sizeof(uint8_t));
- if (!counts) err(1, "malloc");
+ if (!strcmp(vmtype, "sev-snp")) {
+ rip = snp_dbg_decrypt_rip(kvm->vmfd);
+ } else if (!strcmp(vmtype, "sev-es")) {
+ rip = sev_dbg_decrypt_rip(kvm->vmfd);
+ } else {
+ ret = ioctl(kvm_dev, KVM_CPC_GET_REGS, ®s);
+ if (ret == -1) err(1, "KVM_CPC_GET_REGS");
+ rip = regs.rip;
+ }
- ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts);
- if (ret) err(1, "ioctl KVM_CPC_READ_COUNTS");
+ return rip;
+}
- return counts;
+void
+vm_init(struct kvm *kvm, void *code_start, void *code_end)
+{
+ size_t ramsize;
+
+ ramsize = L1_SIZE * 2;
+ if (!strcmp(vmtype, "kvm")) {
+ kvm_init(kvm, ramsize, code_start, code_end);
+ } else if (!strcmp(vmtype, "sev")) {
+ sev_kvm_init(kvm, ramsize, code_start, code_end);
+ } else if (!strcmp(vmtype, "sev-es")) {
+ sev_es_kvm_init(kvm, ramsize, code_start, code_end);
+ } else if (!strcmp(vmtype, "sev-snp")) {
+ sev_snp_kvm_init(kvm, ramsize, code_start, code_end);
+ } else {
+ errx(1, "invalid version");
+ }
+}
+
+void
+vm_deinit(struct kvm *kvm)
+{
+ kvm_deinit(kvm);
}
uint64_t
@@ -68,8 +100,8 @@ monitor(struct kvm *kvm, bool baseline)
ret = ioctl(kvm_dev, KVM_CPC_READ_COUNTS, counts);
if (ret) err(1, "ioctl KVM_CPC_READ_COUNTS");
- printf("Event: cnt:%llu rip:%lu inst:%llu data:%llu retired:%llu\n",
- event.step.fault_count, snp_dbg_decrypt_rip(kvm->vmfd),
+ printf("Event: rip:%llu cnt:%llu inst:%llu data:%llu ret:%llu\n",
+ vm_get_rip(kvm), event.step.fault_count,
event.step.fault_gfns[0], event.step.fault_gfns[1],
event.step.retinst);
print_counts(counts);
@@ -92,13 +124,20 @@ main(int argc, const char **argv)
uint32_t arg;
int ret;
+ vmtype = "kvm";
+ if (argc > 1) vmtype = argv[1];
+ if (strcmp(vmtype, "kvm") && strcmp(vmtype, "sev")
+ && strcmp(vmtype, "sev-es")
+ && strcmp(vmtype, "sev-snp"))
+ errx(1, "invalid vm mode: %s", vmtype);
+
setvbuf(stdout, NULL, _IONBF, 0);
pin_process(0, TARGET_CORE, true);
kvm_setup_init();
- sev_snp_kvm_init(&kvm, L1_SIZE * 2, guest_start, guest_stop);
+ vm_init(&kvm, guest_start, guest_stop);
/* reset kernel module state */
ret = ioctl(kvm_dev, KVM_CPC_RESET, NULL);
@@ -141,6 +180,7 @@ main(int argc, const char **argv)
while (eventcnt < 50) {
eventcnt += monitor(&kvm, true);
}
+ printf("Baseline done\n");
ret = ioctl(kvm_dev, KVM_CPC_VM_REQ_PAUSE);
if (ret) err(1, "ioctl KVM_CPC_VM_REQ_PAUSE");
@@ -191,7 +231,7 @@ main(int argc, const char **argv)
exit(0);
}
- kvm_deinit(&kvm);
+ vm_deinit(&kvm);
kvm_setup_deinit();
}
diff --git a/test/kvm-step_guest.S b/test/kvm-step_guest.S
@@ -5,18 +5,23 @@
.global guest_start
.global guest_stop
+.align(16)
+.code16gcc
+
guest_start:
- mov $(L1_LINESIZE * (L1_SETS + 9)), %rbx
-# hlt
-# mov $(L1_LINESIZE * (L1_SETS + 10)), %rbx
- mov $(L1_LINESIZE * (L1_SETS + 11)), %rbx
-# hlt
-# mov $(L1_LINESIZE * (L1_SETS + 12)), %rbx
- mov $(L1_LINESIZE * (L1_SETS + 13)), %rbx
-# hlt
-# mov $(L1_LINESIZE * (L1_SETS + 14)), %rbx
- mov $(L1_LINESIZE * (L1_SETS + 15)), %rbx
+ mov $(L1_LINESIZE * (L1_SETS + 11)), %bx
+ movb (%bx), %bl
+ hlt
+
+ mov $(L1_LINESIZE * (L1_SETS + 13)), %bx
+ movb (%bx), %bl
hlt
- jmp guest_start
+
+ mov $(L1_LINESIZE * (L1_SETS + 15)), %bx
+ movb (%bx), %bl
+ hlt
+
+ mov $0x00, %ax
+ jmp *%ax
guest_stop:
diff --git a/test/kvm.c b/test/kvm.c
@@ -220,10 +220,14 @@ kvm_init_memory(struct kvm *kvm, size_t ramsize,
kvm->mem = mmap(NULL, kvm->memsize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (!kvm->mem) err(1, "mmap kvm->mem");
- memset(kvm->mem, 0, kvm->memsize);
+ /* nop slide oob to detect errors quickly */
+ memset(kvm->mem, 0x90, kvm->memsize);
assert(code_stop - code_start <= kvm->memsize);
memcpy(kvm->mem, code_start, code_stop - code_start);
+ printf("KVM Memory:\n");
+ hexdump(code_start, code_stop - code_start);
+
memset(®ion, 0, sizeof(region));
region.slot = 0;
region.memory_size = kvm->memsize;
@@ -272,7 +276,6 @@ kvm_init_regs(struct kvm *kvm)
regs.rip = 0;
regs.rsp = kvm->memsize - 8;
regs.rbp = kvm->memsize - 8;
- regs.rflags = 0x2;
ret = ioctl(kvm->vcpufd, KVM_SET_REGS, ®s);
if (ret == -1) err(1, "KVM_SET_REGS");
}
diff --git a/util/disasm b/util/disasm
@@ -1,8 +1,13 @@
#!/bin/sh
if [ $# -lt 2 ]; then
- echo "Usage: guest_asm FILE FUNC"
+ echo "Usage: guest_asm FILE FUNC (guest)"
exit 1
fi
-gdb --batch -ex "disassemble $2" $1
+ARCH="i386"
+if [ "$3" = "guest" ]; then
+ ARCH="i8086"
+fi
+
+gdb --batch -ex "set architecture $ARCH" -ex "disassemble $2" $1