libsel4platsupport: Add mini IRQ interface The new mini IRQ interface covers the common use case where there's only a few interrupts to manage and these can fit into a single notification's badge space. This also avoids the unneeded complexity that the standard IRQ interface offers.
diff --git a/libsel4platsupport/include/sel4platsupport/irq.h b/libsel4platsupport/include/sel4platsupport/irq.h index d0426e3..2d40c79 100644 --- a/libsel4platsupport/include/sel4platsupport/irq.h +++ b/libsel4platsupport/include/sel4platsupport/irq.h
@@ -39,10 +39,12 @@ * notification instance */ #define MAX_INTERRUPTS_TO_NOTIFICATIONS seL4_BadgeBits +#define MINI_IRQ_INTERFACE_NTFN_ID 0 + typedef int ntfn_id_t; /* - * NOTE: This implementation of the platsuport IRQ interface is not thread-safe. + * NOTE: These implementations of the platsupport IRQ interface is not thread-safe. */ /* @@ -66,6 +68,32 @@ ps_malloc_ops_t *malloc_ops); /* + * Initialises a 'mini' IRQ interface. + * + * It functions similar to the larger IRQ interface except that this interface + * only manages one notification. This is useful for situations in which you + * have an application which manages only a few devices and do not want the + * extra features like being able to pair specific interrupts with specific + * notifications and possibly use specific bits of a notification's badge + * space. + * + * Only functions 'sel4platsupport_irq_(handle/wait/poll)' can be used with + * this interface. + * + * You can expect the notification ID of this interface to be + * 'MINI_IRQ_INTERFACE_NTFN_ID'. + * + * @param irq_ops Interface to fill in + * @param vka A VKA interface that must remain valid for the lifetime of the interface + * @param simple A simple interface that must remain valid for the lifetime of the interface + * @param malloc_ops Malloc interface that is used to allocate memory for the IRQ interface + * @param ntfn Notification that the IRQ interface can used to pair interrupts with + * @param usable_mask Mask of bits that can be used for badging + */ +int sel4platsupport_new_mini_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, + ps_malloc_ops_t *malloc_ops, seL4_CPtr ntfn, seL4_Word usable_mask); + +/* * The following functions are not intended to be used by drivers. * * They are intended to be used by the applications which wrap around the drivers.
diff --git a/libsel4platsupport/src/irq.c b/libsel4platsupport/src/irq.c index fef4948..f7bbc73 100644 --- a/libsel4platsupport/src/irq.c +++ b/libsel4platsupport/src/irq.c
@@ -30,6 +30,11 @@ #define UNPAIRED_ID -1 #define UNALLOCATED_BADGE_INDEX -1 +typedef enum irq_iface_type { + STANDARD_IFACE, + MINI_IFACE +} irq_iface_type_t; + typedef struct { /* These are always non-empty if this particular IRQ ID is in use */ bool allocated; @@ -63,6 +68,7 @@ } ntfn_entry_t; typedef struct { + irq_iface_type_t iface_type; size_t num_registered_irqs; size_t num_allocated_ntfns; @@ -343,7 +349,7 @@ } static irq_cookie_t *new_irq_ops_common(vka_t *vka, simple_t *simple, irq_interface_config_t irq_config, - ps_malloc_ops_t *malloc_ops) + ps_malloc_ops_t *malloc_ops, irq_iface_type_t iface_type) { int err = 0; @@ -393,6 +399,7 @@ goto error; } + cookie->iface_type = iface_type; cookie->simple = simple; cookie->vka = vka; cookie->malloc_ops = malloc_ops; @@ -484,6 +491,29 @@ return irq_register_common(irq_cookie, irq, callback, callback_data); } +/* The register function for the mini IRQ interface */ +static irq_id_t sel4platsupport_irq_register_mini(void *cookie, ps_irq_t irq, irq_callback_fn_t callback, + void *callback_data) +{ + irq_cookie_t *irq_cookie = cookie; + + irq_id_t assigned_id = irq_register_common(irq_cookie, irq, callback, callback_data); + if (assigned_id < 0) { + /* Contains the error code if < 0 */ + return assigned_id; + } + + /* Pair this IRQ with the only notification */ + int error = irq_set_ntfn_common(irq_cookie, MINI_IRQ_INTERFACE_NTFN_ID, assigned_id, NULL); + if (error) { + ZF_LOGF_IF(sel4platsupport_irq_unregister(irq_cookie, assigned_id), + "Failed to clean-up a failed operation"); + return error; + } + + return assigned_id; +} + static int sel4platsupport_irq_acknowledge(void *ack_data) { if (!ack_data) { @@ -531,7 +561,7 @@ return -EINVAL; } - irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops); + irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, STANDARD_IFACE); if (!cookie) { return -ENOMEM; } @@ -544,6 +574,40 @@ return 0; } +int sel4platsupport_new_mini_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, + ps_malloc_ops_t *malloc_ops, seL4_CPtr ntfn, seL4_Word usable_mask) +{ + if (!irq_ops || !vka || !simple || !malloc_ops || !usable_mask) { + return -EINVAL; + } + + if (ntfn == seL4_CapNull) { + return -EINVAL; + } + + /* Get the number of bits that we can use in the badge, + * this is the amount of interrupts that can be registered at a given time */ + size_t bits_in_mask = POPCOUNTL(usable_mask); + + /* max_ntfn_ids is kinda irrelevant in the mini interface, but set it anyway */ + irq_interface_config_t irq_config = { .max_irq_ids = bits_in_mask, .max_ntfn_ids = 1 }; + + irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, MINI_IFACE); + if (!cookie) { + return -ENOMEM; + } + + /* Fill in the actual IRQ ops structure now */ + irq_ops->cookie = (void *) cookie; + irq_ops->irq_register_fn = sel4platsupport_irq_register_mini; + irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister; + + /* Provide the ntfn */ + provide_ntfn_common(cookie, ntfn, usable_mask, MINI_IRQ_INTERFACE_NTFN_ID); + + return 0; +} + ntfn_id_t sel4platsupport_irq_provide_ntfn(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn, seL4_Word usable_mask) { if (!irq_ops || ntfn == seL4_CapNull || !usable_mask) { @@ -552,6 +616,11 @@ irq_cookie_t *irq_cookie = irq_ops->cookie; + if (irq_cookie->iface_type == MINI_IFACE) { + ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); + return -EPERM; + } + if (check_ntfn_id_all_allocated(irq_cookie)) { return -EMFILE; } @@ -575,6 +644,11 @@ irq_cookie_t *irq_cookie = irq_ops->cookie; + if (irq_cookie->iface_type == MINI_IFACE) { + ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); + return -EPERM; + } + if (check_ntfn_id_all_allocated(irq_cookie)) { return -EMFILE; } @@ -597,6 +671,11 @@ irq_cookie_t *irq_cookie = irq_ops->cookie; + if (irq_cookie->iface_type == MINI_IFACE) { + ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); + return -EPERM; + } + if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) { return -EINVAL; } @@ -648,6 +727,11 @@ irq_cookie_t *irq_cookie = irq_ops->cookie; + if (irq_cookie->iface_type == MINI_IFACE) { + ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); + return -EPERM; + } + if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id) || !check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { return -EINVAL; @@ -669,6 +753,11 @@ irq_cookie_t *irq_cookie = irq_ops->cookie; + if (irq_cookie->iface_type == MINI_IFACE) { + ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); + return -EPERM; + } + if (!check_irq_id_is_valid(irq_cookie, irq_id) || !check_irq_id_is_allocated(irq_cookie, irq_id)) { return -EINVAL;