Skip to content

Commit e3db757

Browse files
Ricardo KollerMarc Zyngier
authored andcommitted
KVM: selftests: Add exception handling support for aarch64
Add the infrastructure needed to enable exception handling in aarch64 selftests. The exception handling defaults to an unhandled-exception handler which aborts the test, just like x86. These handlers can be overridden by calling vm_install_exception_handler(vector) or vm_install_sync_handler(vector, ec). The unhandled exception reporting from the guest is done using the ucall type introduced in a previous commit, UCALL_UNHANDLED. The exception handling code is inspired on kvm-unit-tests. Signed-off-by: Ricardo Koller <ricarkol@google.com> Reviewed-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210611011020.3420067-6-ricarkol@google.com
1 parent 67f709f commit e3db757

4 files changed

Lines changed: 287 additions & 1 deletion

File tree

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ endif
3535

3636
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
3737
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
38-
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
38+
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S
3939
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
4040

4141
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define SELFTEST_KVM_PROCESSOR_H
99

1010
#include "kvm_util.h"
11+
#include <linux/stringify.h>
1112

1213

1314
#define ARM64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
@@ -18,6 +19,7 @@
1819
#define MAIR_EL1 3, 0, 10, 2, 0
1920
#define TTBR0_EL1 3, 0, 2, 0, 0
2021
#define SCTLR_EL1 3, 0, 1, 0, 0
22+
#define VBAR_EL1 3, 0, 12, 0, 0
2123

2224
/*
2325
* Default MAIR
@@ -56,4 +58,65 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, int vcpuid, struct kvm_vcpu_init *ini
5658
void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid,
5759
struct kvm_vcpu_init *init, void *guest_code);
5860

61+
struct ex_regs {
62+
u64 regs[31];
63+
u64 sp;
64+
u64 pc;
65+
u64 pstate;
66+
};
67+
68+
#define VECTOR_NUM 16
69+
70+
enum {
71+
VECTOR_SYNC_CURRENT_SP0,
72+
VECTOR_IRQ_CURRENT_SP0,
73+
VECTOR_FIQ_CURRENT_SP0,
74+
VECTOR_ERROR_CURRENT_SP0,
75+
76+
VECTOR_SYNC_CURRENT,
77+
VECTOR_IRQ_CURRENT,
78+
VECTOR_FIQ_CURRENT,
79+
VECTOR_ERROR_CURRENT,
80+
81+
VECTOR_SYNC_LOWER_64,
82+
VECTOR_IRQ_LOWER_64,
83+
VECTOR_FIQ_LOWER_64,
84+
VECTOR_ERROR_LOWER_64,
85+
86+
VECTOR_SYNC_LOWER_32,
87+
VECTOR_IRQ_LOWER_32,
88+
VECTOR_FIQ_LOWER_32,
89+
VECTOR_ERROR_LOWER_32,
90+
};
91+
92+
#define VECTOR_IS_SYNC(v) ((v) == VECTOR_SYNC_CURRENT_SP0 || \
93+
(v) == VECTOR_SYNC_CURRENT || \
94+
(v) == VECTOR_SYNC_LOWER_64 || \
95+
(v) == VECTOR_SYNC_LOWER_32)
96+
97+
#define ESR_EC_NUM 64
98+
#define ESR_EC_SHIFT 26
99+
#define ESR_EC_MASK (ESR_EC_NUM - 1)
100+
101+
void vm_init_descriptor_tables(struct kvm_vm *vm);
102+
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid);
103+
104+
typedef void(*handler_fn)(struct ex_regs *);
105+
void vm_install_exception_handler(struct kvm_vm *vm,
106+
int vector, handler_fn handler);
107+
void vm_install_sync_handler(struct kvm_vm *vm,
108+
int vector, int ec, handler_fn handler);
109+
110+
#define write_sysreg(reg, val) \
111+
({ \
112+
u64 __val = (u64)(val); \
113+
asm volatile("msr " __stringify(reg) ", %x0" : : "rZ" (__val)); \
114+
})
115+
116+
#define read_sysreg(reg) \
117+
({ u64 val; \
118+
asm volatile("mrs %0, "__stringify(reg) : "=r"(val) : : "memory");\
119+
val; \
120+
})
121+
59122
#endif /* SELFTEST_KVM_PROCESSOR_H */
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
.macro save_registers
3+
add sp, sp, #-16 * 17
4+
5+
stp x0, x1, [sp, #16 * 0]
6+
stp x2, x3, [sp, #16 * 1]
7+
stp x4, x5, [sp, #16 * 2]
8+
stp x6, x7, [sp, #16 * 3]
9+
stp x8, x9, [sp, #16 * 4]
10+
stp x10, x11, [sp, #16 * 5]
11+
stp x12, x13, [sp, #16 * 6]
12+
stp x14, x15, [sp, #16 * 7]
13+
stp x16, x17, [sp, #16 * 8]
14+
stp x18, x19, [sp, #16 * 9]
15+
stp x20, x21, [sp, #16 * 10]
16+
stp x22, x23, [sp, #16 * 11]
17+
stp x24, x25, [sp, #16 * 12]
18+
stp x26, x27, [sp, #16 * 13]
19+
stp x28, x29, [sp, #16 * 14]
20+
21+
/*
22+
* This stores sp_el1 into ex_regs.sp so exception handlers can "look"
23+
* at it. It will _not_ be used to restore the sp on return from the
24+
* exception so handlers can not update it.
25+
*/
26+
add x1, sp, #16 * 17
27+
stp x30, x1, [sp, #16 * 15] /* x30, SP */
28+
29+
mrs x1, elr_el1
30+
mrs x2, spsr_el1
31+
stp x1, x2, [sp, #16 * 16] /* PC, PSTATE */
32+
.endm
33+
34+
.macro restore_registers
35+
ldp x1, x2, [sp, #16 * 16] /* PC, PSTATE */
36+
msr elr_el1, x1
37+
msr spsr_el1, x2
38+
39+
/* sp is not restored */
40+
ldp x30, xzr, [sp, #16 * 15] /* x30, SP */
41+
42+
ldp x28, x29, [sp, #16 * 14]
43+
ldp x26, x27, [sp, #16 * 13]
44+
ldp x24, x25, [sp, #16 * 12]
45+
ldp x22, x23, [sp, #16 * 11]
46+
ldp x20, x21, [sp, #16 * 10]
47+
ldp x18, x19, [sp, #16 * 9]
48+
ldp x16, x17, [sp, #16 * 8]
49+
ldp x14, x15, [sp, #16 * 7]
50+
ldp x12, x13, [sp, #16 * 6]
51+
ldp x10, x11, [sp, #16 * 5]
52+
ldp x8, x9, [sp, #16 * 4]
53+
ldp x6, x7, [sp, #16 * 3]
54+
ldp x4, x5, [sp, #16 * 2]
55+
ldp x2, x3, [sp, #16 * 1]
56+
ldp x0, x1, [sp, #16 * 0]
57+
58+
add sp, sp, #16 * 17
59+
60+
eret
61+
.endm
62+
63+
.pushsection ".entry.text", "ax"
64+
.balign 0x800
65+
.global vectors
66+
vectors:
67+
.popsection
68+
69+
.set vector, 0
70+
71+
/*
72+
* Build an exception handler for vector and append a jump to it into
73+
* vectors (while making sure that it's 0x80 aligned).
74+
*/
75+
.macro HANDLER, label
76+
handler_\label:
77+
save_registers
78+
mov x0, sp
79+
mov x1, #vector
80+
bl route_exception
81+
restore_registers
82+
83+
.pushsection ".entry.text", "ax"
84+
.balign 0x80
85+
b handler_\label
86+
.popsection
87+
88+
.set vector, vector + 1
89+
.endm
90+
91+
.macro HANDLER_INVALID
92+
.pushsection ".entry.text", "ax"
93+
.balign 0x80
94+
/* This will abort so no need to save and restore registers. */
95+
mov x0, #vector
96+
mov x1, #0 /* ec */
97+
mov x2, #0 /* valid_ec */
98+
b kvm_exit_unexpected_exception
99+
.popsection
100+
101+
.set vector, vector + 1
102+
.endm
103+
104+
/*
105+
* Caution: be sure to not add anything between the declaration of vectors
106+
* above and these macro calls that will build the vectors table below it.
107+
*/
108+
HANDLER_INVALID // Synchronous EL1t
109+
HANDLER_INVALID // IRQ EL1t
110+
HANDLER_INVALID // FIQ EL1t
111+
HANDLER_INVALID // Error EL1t
112+
113+
HANDLER el1h_sync // Synchronous EL1h
114+
HANDLER el1h_irq // IRQ EL1h
115+
HANDLER el1h_fiq // FIQ EL1h
116+
HANDLER el1h_error // Error EL1h
117+
118+
HANDLER el0_sync_64 // Synchronous 64-bit EL0
119+
HANDLER el0_irq_64 // IRQ 64-bit EL0
120+
HANDLER el0_fiq_64 // FIQ 64-bit EL0
121+
HANDLER el0_error_64 // Error 64-bit EL0
122+
123+
HANDLER el0_sync_32 // Synchronous 32-bit EL0
124+
HANDLER el0_irq_32 // IRQ 32-bit EL0
125+
HANDLER el0_fiq_32 // FIQ 32-bit EL0
126+
HANDLER el0_error_32 // Error 32-bit EL0

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include <linux/compiler.h>
9+
#include <assert.h>
910

1011
#include "kvm_util.h"
1112
#include "../kvm_util_internal.h"
@@ -14,6 +15,8 @@
1415
#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
1516
#define DEFAULT_ARM64_GUEST_STACK_VADDR_MIN 0xac0000
1617

18+
static vm_vaddr_t exception_handlers;
19+
1720
static uint64_t page_align(struct kvm_vm *vm, uint64_t v)
1821
{
1922
return (v + vm->page_size) & ~(vm->page_size - 1);
@@ -334,6 +337,100 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)
334337
va_end(ap);
335338
}
336339

340+
void kvm_exit_unexpected_exception(int vector, uint64_t ec, bool valid_ec)
341+
{
342+
ucall(UCALL_UNHANDLED, 3, vector, ec, valid_ec);
343+
while (1)
344+
;
345+
}
346+
337347
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
338348
{
349+
struct ucall uc;
350+
351+
if (get_ucall(vm, vcpuid, &uc) != UCALL_UNHANDLED)
352+
return;
353+
354+
if (uc.args[2]) /* valid_ec */ {
355+
assert(VECTOR_IS_SYNC(uc.args[0]));
356+
TEST_FAIL("Unexpected exception (vector:0x%lx, ec:0x%lx)",
357+
uc.args[0], uc.args[1]);
358+
} else {
359+
assert(!VECTOR_IS_SYNC(uc.args[0]));
360+
TEST_FAIL("Unexpected exception (vector:0x%lx)",
361+
uc.args[0]);
362+
}
363+
}
364+
365+
struct handlers {
366+
handler_fn exception_handlers[VECTOR_NUM][ESR_EC_NUM];
367+
};
368+
369+
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid)
370+
{
371+
extern char vectors;
372+
373+
set_reg(vm, vcpuid, ARM64_SYS_REG(VBAR_EL1), (uint64_t)&vectors);
374+
}
375+
376+
void route_exception(struct ex_regs *regs, int vector)
377+
{
378+
struct handlers *handlers = (struct handlers *)exception_handlers;
379+
bool valid_ec;
380+
int ec = 0;
381+
382+
switch (vector) {
383+
case VECTOR_SYNC_CURRENT:
384+
case VECTOR_SYNC_LOWER_64:
385+
ec = (read_sysreg(esr_el1) >> ESR_EC_SHIFT) & ESR_EC_MASK;
386+
valid_ec = true;
387+
break;
388+
case VECTOR_IRQ_CURRENT:
389+
case VECTOR_IRQ_LOWER_64:
390+
case VECTOR_FIQ_CURRENT:
391+
case VECTOR_FIQ_LOWER_64:
392+
case VECTOR_ERROR_CURRENT:
393+
case VECTOR_ERROR_LOWER_64:
394+
ec = 0;
395+
valid_ec = false;
396+
break;
397+
default:
398+
valid_ec = false;
399+
goto unexpected_exception;
400+
}
401+
402+
if (handlers && handlers->exception_handlers[vector][ec])
403+
return handlers->exception_handlers[vector][ec](regs);
404+
405+
unexpected_exception:
406+
kvm_exit_unexpected_exception(vector, ec, valid_ec);
407+
}
408+
409+
void vm_init_descriptor_tables(struct kvm_vm *vm)
410+
{
411+
vm->handlers = vm_vaddr_alloc(vm, sizeof(struct handlers),
412+
vm->page_size, 0, 0);
413+
414+
*(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers;
415+
}
416+
417+
void vm_install_sync_handler(struct kvm_vm *vm, int vector, int ec,
418+
void (*handler)(struct ex_regs *))
419+
{
420+
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
421+
422+
assert(VECTOR_IS_SYNC(vector));
423+
assert(vector < VECTOR_NUM);
424+
assert(ec < ESR_EC_NUM);
425+
handlers->exception_handlers[vector][ec] = handler;
426+
}
427+
428+
void vm_install_exception_handler(struct kvm_vm *vm, int vector,
429+
void (*handler)(struct ex_regs *))
430+
{
431+
struct handlers *handlers = addr_gva2hva(vm, vm->handlers);
432+
433+
assert(!VECTOR_IS_SYNC(vector));
434+
assert(vector < VECTOR_NUM);
435+
handlers->exception_handlers[vector][0] = handler;
339436
}

0 commit comments

Comments
 (0)