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;