Skip to content

Commit 29faeb9

Browse files
suomilewisbonzini
authored andcommitted
selftests: kvm: Add exception handling to selftests
Add the infrastructure needed to enable exception handling in selftests. This allows any of the exception and interrupt vectors to be overridden in the guest. Signed-off-by: Aaron Lewis <aaronlewis@google.com> Reviewed-by: Alexander Graf <graf@amazon.com> Message-Id: <20201012194716.3950330-4-aaronlewis@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 85f2a43 commit 29faeb9

9 files changed

Lines changed: 244 additions & 9 deletions

File tree

tools/testing/selftests/kvm/Makefile

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ ifeq ($(ARCH),s390)
3434
endif
3535

3636
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c
37-
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c
37+
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
3838
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
3939
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c
4040

@@ -111,14 +111,21 @@ LDFLAGS += -pthread $(no-pie-option) $(pgste-option)
111111
include ../lib.mk
112112

113113
STATIC_LIBS := $(OUTPUT)/libkvm.a
114-
LIBKVM_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM))
115-
EXTRA_CLEAN += $(LIBKVM_OBJ) $(STATIC_LIBS) cscope.*
114+
LIBKVM_C := $(filter %.c,$(LIBKVM))
115+
LIBKVM_S := $(filter %.S,$(LIBKVM))
116+
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
117+
LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
118+
EXTRA_CLEAN += $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(STATIC_LIBS) cscope.*
119+
120+
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ))))
121+
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c
122+
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
116123

117-
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_OBJ))))
118-
$(LIBKVM_OBJ): $(OUTPUT)/%.o: %.c
124+
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
119125
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
120126

121-
$(OUTPUT)/libkvm.a: $(LIBKVM_OBJ)
127+
LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)
128+
$(OUTPUT)/libkvm.a: $(LIBKVM_OBJS)
122129
$(AR) crs $@ $^
123130

124131
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))

tools/testing/selftests/kvm/include/kvm_util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd);
294294
memcpy(&(g), _p, sizeof(g)); \
295295
})
296296

297+
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid);
298+
297299
/* Common ucalls */
298300
enum {
299301
UCALL_NONE,

tools/testing/selftests/kvm/include/x86_64/processor.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#define X86_CR4_SMAP (1ul << 21)
3737
#define X86_CR4_PKE (1ul << 22)
3838

39+
#define UNEXPECTED_VECTOR_PORT 0xfff0u
40+
3941
/* General Registers in 64-Bit Mode */
4042
struct gpr64_regs {
4143
u64 rax;
@@ -239,6 +241,11 @@ static inline struct desc_ptr get_idt(void)
239241
return idt;
240242
}
241243

244+
static inline void outl(uint16_t port, uint32_t value)
245+
{
246+
__asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value));
247+
}
248+
242249
#define SET_XMM(__var, __xmm) \
243250
asm volatile("movq %0, %%"#__xmm : : "r"(__var) : #__xmm)
244251

@@ -338,6 +345,23 @@ uint32_t kvm_get_cpuid_max_basic(void);
338345
uint32_t kvm_get_cpuid_max_extended(void);
339346
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits);
340347

348+
struct ex_regs {
349+
uint64_t rax, rcx, rdx, rbx;
350+
uint64_t rbp, rsi, rdi;
351+
uint64_t r8, r9, r10, r11;
352+
uint64_t r12, r13, r14, r15;
353+
uint64_t vector;
354+
uint64_t error_code;
355+
uint64_t rip;
356+
uint64_t cs;
357+
uint64_t rflags;
358+
};
359+
360+
void vm_init_descriptor_tables(struct kvm_vm *vm);
361+
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid);
362+
void vm_handle_exception(struct kvm_vm *vm, int vector,
363+
void (*handler)(struct ex_regs *));
364+
341365
/*
342366
* Basic CPU control in CR0
343367
*/

tools/testing/selftests/kvm/lib/aarch64/processor.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,7 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)
350350

351351
va_end(ap);
352352
}
353+
354+
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
355+
{
356+
}

tools/testing/selftests/kvm/lib/kvm_util.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,9 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid)
12041204
do {
12051205
rc = ioctl(vcpu->fd, KVM_RUN, NULL);
12061206
} while (rc == -1 && errno == EINTR);
1207+
1208+
assert_on_unhandled_exception(vm, vcpuid);
1209+
12071210
return rc;
12081211
}
12091212

tools/testing/selftests/kvm/lib/kvm_util_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ struct kvm_vm {
5050
vm_paddr_t pgd;
5151
vm_vaddr_t gdt;
5252
vm_vaddr_t tss;
53+
vm_vaddr_t idt;
54+
vm_vaddr_t handlers;
5355
};
5456

5557
struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid);

tools/testing/selftests/kvm/lib/s390x/processor.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,7 @@ void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)
241241
fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n",
242242
indent, "", vcpu->state->psw_mask, vcpu->state->psw_addr);
243243
}
244+
245+
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
246+
{
247+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
handle_exception:
2+
push %r15
3+
push %r14
4+
push %r13
5+
push %r12
6+
push %r11
7+
push %r10
8+
push %r9
9+
push %r8
10+
11+
push %rdi
12+
push %rsi
13+
push %rbp
14+
push %rbx
15+
push %rdx
16+
push %rcx
17+
push %rax
18+
mov %rsp, %rdi
19+
20+
call route_exception
21+
22+
pop %rax
23+
pop %rcx
24+
pop %rdx
25+
pop %rbx
26+
pop %rbp
27+
pop %rsi
28+
pop %rdi
29+
pop %r8
30+
pop %r9
31+
pop %r10
32+
pop %r11
33+
pop %r12
34+
pop %r13
35+
pop %r14
36+
pop %r15
37+
38+
/* Discard vector and error code. */
39+
add $16, %rsp
40+
iretq
41+
42+
/*
43+
* Build the handle_exception wrappers which push the vector/error code on the
44+
* stack and an array of pointers to those wrappers.
45+
*/
46+
.pushsection .rodata
47+
.globl idt_handlers
48+
idt_handlers:
49+
.popsection
50+
51+
.macro HANDLERS has_error from to
52+
vector = \from
53+
.rept \to - \from + 1
54+
.align 8
55+
56+
/* Fetch current address and append it to idt_handlers. */
57+
current_handler = .
58+
.pushsection .rodata
59+
.quad current_handler
60+
.popsection
61+
62+
.if ! \has_error
63+
pushq $0
64+
.endif
65+
pushq $vector
66+
jmp handle_exception
67+
vector = vector + 1
68+
.endr
69+
.endm
70+
71+
.global idt_handler_code
72+
idt_handler_code:
73+
HANDLERS has_error=0 from=0 to=7
74+
HANDLERS has_error=1 from=8 to=8
75+
HANDLERS has_error=0 from=9 to=9
76+
HANDLERS has_error=1 from=10 to=14
77+
HANDLERS has_error=0 from=15 to=16
78+
HANDLERS has_error=1 from=17 to=17
79+
HANDLERS has_error=0 from=18 to=255
80+
81+
.section .note.GNU-stack, "", %progbits

tools/testing/selftests/kvm/lib/x86_64/processor.c

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,18 @@
1212
#include "../kvm_util_internal.h"
1313
#include "processor.h"
1414

15+
#ifndef NUM_INTERRUPTS
16+
#define NUM_INTERRUPTS 256
17+
#endif
18+
19+
#define DEFAULT_CODE_SELECTOR 0x8
20+
#define DEFAULT_DATA_SELECTOR 0x10
21+
1522
/* Minimum physical address used for virtual translation tables. */
1623
#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
1724

25+
vm_vaddr_t exception_handlers;
26+
1827
/* Virtual translation table structure declarations */
1928
struct pageMapL4Entry {
2029
uint64_t present:1;
@@ -557,9 +566,9 @@ static void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_m
557566
sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);
558567

559568
kvm_seg_set_unusable(&sregs.ldt);
560-
kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs);
561-
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds);
562-
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es);
569+
kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs.cs);
570+
kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.ds);
571+
kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.es);
563572
kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot);
564573
break;
565574

@@ -1119,3 +1128,102 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)
11191128
*va_bits = (entry->eax >> 8) & 0xff;
11201129
}
11211130
}
1131+
1132+
struct idt_entry {
1133+
uint16_t offset0;
1134+
uint16_t selector;
1135+
uint16_t ist : 3;
1136+
uint16_t : 5;
1137+
uint16_t type : 4;
1138+
uint16_t : 1;
1139+
uint16_t dpl : 2;
1140+
uint16_t p : 1;
1141+
uint16_t offset1;
1142+
uint32_t offset2; uint32_t reserved;
1143+
};
1144+
1145+
static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr,
1146+
int dpl, unsigned short selector)
1147+
{
1148+
struct idt_entry *base =
1149+
(struct idt_entry *)addr_gva2hva(vm, vm->idt);
1150+
struct idt_entry *e = &base[vector];
1151+
1152+
memset(e, 0, sizeof(*e));
1153+
e->offset0 = addr;
1154+
e->selector = selector;
1155+
e->ist = 0;
1156+
e->type = 14;
1157+
e->dpl = dpl;
1158+
e->p = 1;
1159+
e->offset1 = addr >> 16;
1160+
e->offset2 = addr >> 32;
1161+
}
1162+
1163+
void kvm_exit_unexpected_vector(uint32_t value)
1164+
{
1165+
outl(UNEXPECTED_VECTOR_PORT, value);
1166+
}
1167+
1168+
void route_exception(struct ex_regs *regs)
1169+
{
1170+
typedef void(*handler)(struct ex_regs *);
1171+
handler *handlers = (handler *)exception_handlers;
1172+
1173+
if (handlers && handlers[regs->vector]) {
1174+
handlers[regs->vector](regs);
1175+
return;
1176+
}
1177+
1178+
kvm_exit_unexpected_vector(regs->vector);
1179+
}
1180+
1181+
void vm_init_descriptor_tables(struct kvm_vm *vm)
1182+
{
1183+
extern void *idt_handlers;
1184+
int i;
1185+
1186+
vm->idt = vm_vaddr_alloc(vm, getpagesize(), 0x2000, 0, 0);
1187+
vm->handlers = vm_vaddr_alloc(vm, 256 * sizeof(void *), 0x2000, 0, 0);
1188+
/* Handlers have the same address in both address spaces.*/
1189+
for (i = 0; i < NUM_INTERRUPTS; i++)
1190+
set_idt_entry(vm, i, (unsigned long)(&idt_handlers)[i], 0,
1191+
DEFAULT_CODE_SELECTOR);
1192+
}
1193+
1194+
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid)
1195+
{
1196+
struct kvm_sregs sregs;
1197+
1198+
vcpu_sregs_get(vm, vcpuid, &sregs);
1199+
sregs.idt.base = vm->idt;
1200+
sregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1;
1201+
sregs.gdt.base = vm->gdt;
1202+
sregs.gdt.limit = getpagesize() - 1;
1203+
kvm_seg_set_kernel_data_64bit(NULL, DEFAULT_DATA_SELECTOR, &sregs.gs);
1204+
vcpu_sregs_set(vm, vcpuid, &sregs);
1205+
*(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers;
1206+
}
1207+
1208+
void vm_handle_exception(struct kvm_vm *vm, int vector,
1209+
void (*handler)(struct ex_regs *))
1210+
{
1211+
vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers);
1212+
1213+
handlers[vector] = (vm_vaddr_t)handler;
1214+
}
1215+
1216+
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
1217+
{
1218+
if (vcpu_state(vm, vcpuid)->exit_reason == KVM_EXIT_IO
1219+
&& vcpu_state(vm, vcpuid)->io.port == UNEXPECTED_VECTOR_PORT
1220+
&& vcpu_state(vm, vcpuid)->io.size == 4) {
1221+
/* Grab pointer to io data */
1222+
uint32_t *data = (void *)vcpu_state(vm, vcpuid)
1223+
+ vcpu_state(vm, vcpuid)->io.data_offset;
1224+
1225+
TEST_ASSERT(false,
1226+
"Unexpected vectored event in guest (vector:0x%x)",
1227+
*data);
1228+
}
1229+
}

0 commit comments

Comments
 (0)