From 414b41c9537b1d9b7e11eee0ff736bbd628996a3 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Thu, 22 Feb 2024 13:20:19 +1100 Subject: [PATCH] Add API for registering passthrough IRQs This API is mainly for convenience, it does not do anything that people could not already do but makes the implementations of VMMs simpler. See the comments in the header file for details. Co-authored-by: Jingyao Zhou --- src/arch/aarch64/virq.c | 44 +++++++++++++++++++++++++++++++++++++++++ src/virq.h | 18 +++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/arch/aarch64/virq.c b/src/arch/aarch64/virq.c index eac507f3..298e4a22 100644 --- a/src/arch/aarch64/virq.c +++ b/src/arch/aarch64/virq.c @@ -3,6 +3,9 @@ #include "../../util/util.h" #include "../../virq.h" +/* Maps Microkit channel numbers with registered vIRQ */ +int virq_passthrough_map[MAX_PASSTHROUGH_IRQ] = {-1}; + #define SGI_RESCHEDULE_IRQ 0 #define SGI_FUNC_CALL 1 #define PPI_VTIMER_IRQ 27 @@ -51,3 +54,44 @@ bool virq_inject(size_t vcpu_id, int irq) { bool virq_register(size_t vcpu_id, size_t virq_num, virq_ack_fn_t ack_fn, void *ack_data) { return vgic_register_irq(vcpu_id, virq_num, ack_fn, ack_data); } + +static void virq_passthrough_ack(size_t vcpu_id, int irq, void *cookie) { + /* We are down-casting to microkit_channel so must first cast to size_t */ + microkit_irq_ack((microkit_channel)(size_t)cookie); +} + +bool virq_register_passthrough(size_t vcpu_id, size_t irq, microkit_channel irq_ch) { + assert(irq_ch < MICROKIT_MAX_CHANNELS); + if (irq_ch >= MICROKIT_MAX_CHANNELS) { + LOG_VMM_ERR("Invalid channel number given '0x%lx' for passthrough vIRQ 0x%lx\n", irq_ch, irq); + return false; + } + + LOG_VMM("Register passthrough vIRQ 0x%lx on vCPU 0x%lx (IRQ channel: 0x%lx)\n", irq, vcpu_id, irq_ch); + virq_passthrough_map[irq_ch] = irq; + + bool success = virq_register(GUEST_VCPU_ID, irq, &virq_passthrough_ack, (void *)(size_t)irq_ch); + assert(success); + if (!success) { + LOG_VMM_ERR("Failed to register passthrough vIRQ %d\n", irq); + return false; + } + + return true; +} + +bool virq_handle_passthrough(microkit_channel irq_ch) { + assert(virq_passthrough_map[irq_ch] >= 0); + if (virq_passthrough_map[irq_ch] < 0) { + LOG_VMM_ERR("attempted to handle invalid passthrough IRQ channel 0x%lx\n", irq_ch); + return false; + } + + bool success = vgic_inject_irq(GUEST_VCPU_ID, virq_passthrough_map[irq_ch]); + if (!success) { + LOG_VMM_ERR("could not inject passthrough vIRQ 0x%lx, dropped on vCPU 0x%lx\n", virq_passthrough_map[irq_ch], GUEST_VCPU_ID); + return false; + } + + return true; +} diff --git a/src/virq.h b/src/virq.h index 78d2e628..8e1df31d 100644 --- a/src/virq.h +++ b/src/virq.h @@ -1,8 +1,26 @@ #include #include +#include + +#ifndef MAX_PASSTHROUGH_IRQ +#define MAX_PASSTHROUGH_IRQ MICROKIT_MAX_CHANNELS +#endif typedef void (*virq_ack_fn_t)(size_t vcpu_id, int irq, void *cookie); bool virq_controller_init(size_t boot_vcpu_id); bool virq_register(size_t vcpu_id, size_t virq_num, virq_ack_fn_t ack_fn, void *ack_data); bool virq_inject(size_t vcpu_id, int irq); + +/* + * These two APIs are convenient for when you want to directly passthrough an IRQ from + * the hardware to the guest as the same vIRQ. This is useful when the guest has direct + * passthrough access to a particular device on the hardware. + * After registering the passthrough IRQ, call `virq_handle_passthrough` when + * the IRQ has come through from seL4. + * + * @ivanv: currently this API assumes a Microkit environment. This should be changed + * if/when we make libvmm agnostic to the seL4 environment it is running in. + */ +bool virq_register_passthrough(size_t vcpu_id, size_t irq, microkit_channel irq_ch); +bool virq_handle_passthrough(microkit_channel irq_ch);