From: Marc Zyngier <maz@kernel.org>
To: Ricardo Koller <ricarkol@google.com>
Cc: kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu,
pbonzini@redhat.com, drjones@redhat.com,
alexandru.elisei@arm.com, eric.auger@redhat.com
Subject: Re: [PATCH 1/3] KVM: selftests: Add exception handling support for aarch64
Date: Fri, 23 Apr 2021 09:58:24 +0100 [thread overview]
Message-ID: <87sg3hnzrj.wl-maz@kernel.org> (raw)
In-Reply-To: <20210423040351.1132218-2-ricarkol@google.com>
Hi Ricardo,
Thanks for starting this.
On Fri, 23 Apr 2021 05:03:49 +0100,
Ricardo Koller <ricarkol@google.com> wrote:
>
> 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_handle_exception with a (vector, error-code)
> tuple. The unhandled exception reporting from the guest is done using
> the new ucall UCALL_UNHANDLED.
>
> Signed-off-by: Ricardo Koller <ricarkol@google.com>
> ---
> tools/testing/selftests/kvm/Makefile | 2 +-
> .../selftests/kvm/include/aarch64/processor.h | 69 ++++++++++++
> .../testing/selftests/kvm/include/kvm_util.h | 1 +
> .../selftests/kvm/lib/aarch64/handlers.S | 104 ++++++++++++++++++
> .../selftests/kvm/lib/aarch64/processor.c | 56 ++++++++++
> 5 files changed, 231 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/kvm/lib/aarch64/handlers.S
>
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 4e548d7ab0ab..618c5903f478 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -35,7 +35,7 @@ endif
>
> LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
> 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
> -LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
> +LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S
> LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
>
> TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
> diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
> index b7fa0c8551db..5c902ad95c35 100644
> --- a/tools/testing/selftests/kvm/include/aarch64/processor.h
> +++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
> @@ -8,6 +8,7 @@
> #define SELFTEST_KVM_PROCESSOR_H
>
> #include "kvm_util.h"
> +#include <linux/stringify.h>
>
>
> #define ARM64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> @@ -18,6 +19,7 @@
> #define MAIR_EL1 3, 0, 10, 2, 0
> #define TTBR0_EL1 3, 0, 2, 0, 0
> #define SCTLR_EL1 3, 0, 1, 0, 0
> +#define VBAR_EL1 3, 0, 12, 0, 0
>
> /*
> * Default MAIR
> @@ -56,4 +58,71 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, int vcpuid, struct kvm_vcpu_init *ini
> void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid,
> struct kvm_vcpu_init *init, void *guest_code);
>
> +struct ex_regs {
> + u64 pc;
> + u64 pstate;
> + u64 sp;
> + u64 lr;
> + u64 regs[31];
> +};
Is there any reason why you are not using the layout of user_pt_regs?
I'm not specially attached to it, but it would at least avoid bugs
such as having both 31 GPRs *and* lr in this structure (LR really is
just another name for X30). It would also make the reviewing easier
for us kernel people ;-).
> +
> +#define VECTOR_NUM 16
> +
> +enum {
> + VECTOR_SYNC_EL1_SP0,
> + VECTOR_IRQ_EL1_SP0,
> + VECTOR_FIQ_EL1_SP0,
> + VECTOR_ERROR_EL1_SP0,
> +
> + VECTOR_SYNC_EL1,
> + VECTOR_IRQ_EL1,
> + VECTOR_FIQ_EL1,
> + VECTOR_ERROR_EL1,
> +
> + VECTOR_SYNC_EL0_64,
> + VECTOR_IRQ_EL0_64,
> + VECTOR_FIQ_EL0_64,
> + VECTOR_ERROR_EL0_64,
> +
> + VECTOR_SYNC_EL0_32,
> + VECTOR_IRQ_EL0_32,
> + VECTOR_FIQ_EL0_32,
> + VECTOR_ERROR_EL0_32,
nit: in order to be true to the architecture and to prepare for the
NV support, it'd be better to rename *_EL1_* to *_CURRENT_*, and
*_EL0_* to *_LOWER_*. This doesn't change the semantics, but instead
shows how the exceptions can nest across the various exception levels.
> +};
> +
> +/* Some common EC (Exception classes) */
> +#define ESR_EC_ILLEGAL_INS 0x0e
> +#define ESR_EC_SVC64 0x15
> +#define ESR_EC_IABORT_EL1 0x21
> +#define ESR_EC_DABORT_EL1 0x25
> +#define ESR_EC_SERROR 0x2f
> +#define ESR_EC_HW_BP_EL1 0x31
> +#define ESR_EC_SSTEP_EL1 0x33
> +#define ESR_EC_WP_EL1 0x35
> +#define ESR_EC_BRK_INS 0x3C
> +
> +#define ESR_EC_NUM 64
Isn't this redundant with the mask below?
> +
> +#define ESR_EC_SHIFT 26
> +#define ESR_EC_MASK 0x3f
> +
> +void vm_init_descriptor_tables(struct kvm_vm *vm);
> +void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid);
> +void vm_handle_exception(struct kvm_vm *vm, int vector, int ec,
> + void (*handler)(struct ex_regs *));
> +
> +#define SPSR_D (1 << 9)
> +#define SPSR_SS (1 << 21)
> +
> +#define write_sysreg(reg, val) \
> +({ \
> + asm volatile("msr "__stringify(reg)", %0" : : "r"(val)); \
> +})
> +
> +#define read_sysreg(reg) \
> +({ u64 val; \
> + asm volatile("mrs %0, "__stringify(reg) : "=r"(val) : : "memory");\
> + val; \
> +})
> +
> #endif /* SELFTEST_KVM_PROCESSOR_H */
> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
> index bea4644d645d..7880929ea548 100644
> --- a/tools/testing/selftests/kvm/include/kvm_util.h
> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
> @@ -347,6 +347,7 @@ enum {
> UCALL_SYNC,
> UCALL_ABORT,
> UCALL_DONE,
> + UCALL_UNHANDLED,
> };
>
> #define UCALL_MAX_ARGS 6
> diff --git a/tools/testing/selftests/kvm/lib/aarch64/handlers.S b/tools/testing/selftests/kvm/lib/aarch64/handlers.S
> new file mode 100644
> index 000000000000..c920679b87c0
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/lib/aarch64/handlers.S
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +.macro save_registers, el
> + stp x28, x29, [sp, #-16]!
> + stp x26, x27, [sp, #-16]!
> + stp x24, x25, [sp, #-16]!
> + stp x22, x23, [sp, #-16]!
> + stp x20, x21, [sp, #-16]!
> + stp x18, x19, [sp, #-16]!
> + stp x16, x17, [sp, #-16]!
> + stp x14, x15, [sp, #-16]!
> + stp x12, x13, [sp, #-16]!
> + stp x10, x11, [sp, #-16]!
> + stp x8, x9, [sp, #-16]!
> + stp x6, x7, [sp, #-16]!
> + stp x4, x5, [sp, #-16]!
> + stp x2, x3, [sp, #-16]!
> + stp x0, x1, [sp, #-16]!
> +
> + .if \el == 0
> + mrs x1, sp_el0
> + .else
> + mov x1, sp
> + .endif
It there any point in saving SP_EL1, given that you already have
altered it significantly and will not be restoring it? I don't care
much, and maybe it is useful as debug information, but a comment would
certainly make the intent clearer.
> + stp x1, lr, [sp, #-16]! /* SP, LR */
See my comment above about the X30/LR duality.
> +
> + mrs x1, elr_el1
> + mrs x2, spsr_el1
> + stp x1, x2, [sp, #-16]! /* PC, PSTATE */
> +.endm
> +
> +.macro restore_registers, el
> + ldp x1, x2, [sp], #16 /* PC, PSTATE */
> + msr elr_el1, x1
> + msr spsr_el1, x2
> +
> + ldp x1, lr, [sp], #16 /* SP, LR */
> + .if \el == 0
> + msr sp_el0, x1
> + .endif
> +
> + ldp x0, x1, [sp], #16
> + ldp x2, x3, [sp], #16
> + ldp x4, x5, [sp], #16
> + ldp x6, x7, [sp], #16
> + ldp x8, x9, [sp], #16
> + ldp x10, x11, [sp], #16
> + ldp x12, x13, [sp], #16
> + ldp x14, x15, [sp], #16
> + ldp x16, x17, [sp], #16
> + ldp x18, x19, [sp], #16
> + ldp x20, x21, [sp], #16
> + ldp x22, x23, [sp], #16
> + ldp x24, x25, [sp], #16
> + ldp x26, x27, [sp], #16
> + ldp x28, x29, [sp], #16
> +
> + eret
> +.endm
> +
> +.pushsection ".entry.text", "ax"
> +.balign 0x800
> +.global vectors
> +vectors:
> +.popsection
> +
> +/*
> + * Build an exception handler for vector and append a jump to it into
> + * vectors (while making sure that it's 0x80 aligned).
> + */
> +.macro HANDLER, el, label, vector
> +handler\()\vector:
> + save_registers \el
> + mov x0, sp
> + mov x1, \vector
> + bl route_exception
> + restore_registers \el
> +
> +.pushsection ".entry.text", "ax"
> +.balign 0x80
> + b handler\()\vector
> +.popsection
> +.endm
That's an interesting construct, wildly different from what we are
using elsewhere in the kernel, but hey, I like change ;-). It'd be
good to add a comment to spell out that anything that emits into
.entry.text between the declaration of 'vectors' and the end of this
file will break everything.
> +
> +.global ex_handler_code
> +ex_handler_code:
> + HANDLER 1, sync, 0 // Synchronous EL1t
> + HANDLER 1, irq, 1 // IRQ EL1t
> + HANDLER 1, fiq, 2 // FIQ EL1t
> + HANDLER 1, error, 3 // Error EL1t
Can any of these actually happen? As far as I can see, the whole
selftest environment seems to be designed around EL1h.
> +
> + HANDLER 1, sync, 4 // Synchronous EL1h
> + HANDLER 1, irq, 5 // IRQ EL1h
> + HANDLER 1, fiq, 6 // FIQ EL1h
> + HANDLER 1, error, 7 // Error EL1h
> +
> + HANDLER 0, sync, 8 // Synchronous 64-bit EL0
> + HANDLER 0, irq, 9 // IRQ 64-bit EL0
> + HANDLER 0, fiq, 10 // FIQ 64-bit EL0
> + HANDLER 0, error, 11 // Error 64-bit EL0
> +
> + HANDLER 0, sync, 12 // Synchronous 32-bit EL0
> + HANDLER 0, irq, 13 // IRQ 32-bit EL0
> + HANDLER 0, fiq, 14 // FIQ 32-bit EL0
> + HANDLER 0, error, 15 // Error 32-bit EL0
I find the whole numbering odd and pretty error prone. You could work
it out from the alignment of the vector if you really need it.
> diff --git a/tools/testing/selftests/kvm/lib/aarch64/processor.c b/tools/testing/selftests/kvm/lib/aarch64/processor.c
> index cee92d477dc0..286305b561d8 100644
> --- a/tools/testing/selftests/kvm/lib/aarch64/processor.c
> +++ b/tools/testing/selftests/kvm/lib/aarch64/processor.c
> @@ -6,6 +6,7 @@
> */
>
> #include <linux/compiler.h>
> +#include <assert.h>
>
> #include "kvm_util.h"
> #include "../kvm_util_internal.h"
> @@ -14,6 +15,8 @@
> #define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
> #define DEFAULT_ARM64_GUEST_STACK_VADDR_MIN 0xac0000
>
> +vm_vaddr_t exception_handlers;
> +
> static uint64_t page_align(struct kvm_vm *vm, uint64_t v)
> {
> return (v + vm->page_size) & ~(vm->page_size - 1);
> @@ -336,4 +339,57 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)
>
> void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
> {
> + struct ucall uc;
> +
> + if (get_ucall(vm, vcpuid, &uc) == UCALL_UNHANDLED) {
> + TEST_ASSERT(false,
> + "Unexpected exception guest (vector:0x%lx, ec:0x%lx)",
> + uc.args[0], uc.args[1]);
> + }
> +}
> +
> +void kvm_exit_unexpected_vector(int vector, uint64_t ec)
> +{
> + ucall(UCALL_UNHANDLED, 2, vector, ec);
> +}
> +
> +#define HANDLERS_IDX(_vector, _ec) ((_vector * ESR_EC_NUM) + _ec)
This is definitely odd. Not all the ECs are valid for all vector entry
points. Actually, ECs only make sense for synchronous exceptions, and
asynchronous events (IRQ, FIQ, SError) cannot populate ESR_ELx.
> +
> +void vm_init_descriptor_tables(struct kvm_vm *vm)
> +{
> + vm->handlers = vm_vaddr_alloc(vm,
> + VECTOR_NUM * ESR_EC_NUM * sizeof(void *),
> + vm->page_size, 0, 0);
> + *(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers;
> +}
> +
> +void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid)
> +{
> + extern char vectors;
> +
> + set_reg(vm, vcpuid, ARM64_SYS_REG(VBAR_EL1), (uint64_t)&vectors);
> +}
> +
> +void route_exception(struct ex_regs *regs, int vector)
> +{
> + typedef void(*handler)(struct ex_regs *);
> + uint64_t esr = read_sysreg(esr_el1);
> + uint64_t ec = (esr >> ESR_EC_SHIFT) & ESR_EC_MASK;
> +
> + handler *handlers = (handler *)exception_handlers;
> +
> + if (handlers && handlers[HANDLERS_IDX(vector, ec)])
> + handlers[HANDLERS_IDX(vector, ec)](regs);
See my comment above. This will do the wrong thing in presence of an
interrupt.
> + else
> + kvm_exit_unexpected_vector(vector, ec);
> +}
> +
> +void vm_handle_exception(struct kvm_vm *vm, int vector, int ec,
> + void (*handler)(struct ex_regs *))
The name seems to be slightly ill defined. To me "handle exception" is
the action of handling the exception. Here, you are merely installing
an exception handler.
> +{
> + vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers);
> +
> + assert(vector < VECTOR_NUM);
> + assert(ec < ESR_EC_NUM);
> + handlers[HANDLERS_IDX(vector, ec)] = (vm_vaddr_t)handler;
> }
> --
> 2.31.1.498.g6c1eba8ee3d-goog
>
>
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
next prev parent reply other threads:[~2021-04-23 8:58 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-23 4:03 [PATCH 0/3] KVM: selftests: arm64 exception handling and debug test Ricardo Koller
2021-04-23 4:03 ` [PATCH 1/3] KVM: selftests: Add exception handling support for aarch64 Ricardo Koller
2021-04-23 8:58 ` Marc Zyngier [this message]
2021-04-23 11:05 ` Andrew Jones
2021-04-26 18:58 ` Ricardo Koller
2021-04-29 17:51 ` Ricardo Koller
2021-04-29 19:59 ` Marc Zyngier
2021-04-29 20:48 ` Ricardo Koller
2021-04-23 4:03 ` [PATCH 2/3] KVM: selftests: Add aarch64/debug-exceptions test Ricardo Koller
2021-04-23 11:22 ` Andrew Jones
2021-04-23 4:03 ` [PATCH 3/3] KVM: selftests: Use a ucall for x86 unhandled vector reporting Ricardo Koller
2021-04-23 10:45 ` Andrew Jones
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87sg3hnzrj.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=alexandru.elisei@arm.com \
--cc=drjones@redhat.com \
--cc=eric.auger@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.cs.columbia.edu \
--cc=pbonzini@redhat.com \
--cc=ricarkol@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox