1/*
2 * Copyright 2019, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <assert.h>
14#include <stdint.h>
15#include <stdlib.h>
16#include <stdbool.h>
17#include <limits.h>
18#include <errno.h>
19
20#include <sel4/sel4.h>
21#include <sel4platsupport/irq.h>
22#include <sel4platsupport/device.h>
23#include <platsupport/irq.h>
24#include <platsupport/io.h>
25#include <simple/simple.h>
26#include <utils/util.h>
27#include <vka/vka.h>
28#include <vka/capops.h>
29
30#define UNPAIRED_ID -1
31#define UNALLOCATED_BADGE_INDEX -1
32
33typedef enum irq_iface_type {
34    STANDARD_IFACE,
35    MINI_IFACE
36} irq_iface_type_t;
37
38typedef struct {
39    /* These are always non-empty if this particular IRQ ID is in use */
40    bool allocated;
41    ps_irq_t irq;
42    cspacepath_t handler_path;
43
44    /* These are not always non-empty if this particular IRQ ID is in use */
45
46    /* Driver registers these */
47    irq_callback_fn_t irq_callback_fn;
48    void *callback_data;
49
50    /* User registers these */
51    cspacepath_t ntfn_path;
52    ntfn_id_t paired_ntfn;
53    int8_t allocated_badge_index;
54} irq_entry_t;
55
56typedef struct {
57    bool allocated;
58    cspacepath_t root_ntfn_path;
59    size_t num_irqs_bound;
60
61    seL4_Word usable_mask;
62    /* Bitfield tracking which of the bits in the badge are allocated */
63    seL4_Word status_bitfield;
64    /* Bitfield tracking which IRQs have arrived but not served */
65    seL4_Word pending_bitfield;
66
67    irq_id_t bound_irqs[MAX_INTERRUPTS_TO_NOTIFICATIONS];
68} ntfn_entry_t;
69
70typedef struct {
71    irq_iface_type_t iface_type;
72    size_t num_registered_irqs;
73    size_t num_allocated_ntfns;
74
75    size_t max_irq_ids;
76    size_t max_ntfn_ids;
77    size_t num_irq_bitfields;
78    size_t num_ntfn_bitfields;
79    /* Array of bitfields tracking which IDs have been allocated */
80    seL4_Word *allocated_irq_bitfields;
81    seL4_Word *allocated_ntfn_bitfields;
82
83    irq_entry_t *irq_table;
84    ntfn_entry_t *ntfn_table;
85    simple_t *simple;
86    vka_t *vka;
87    ps_malloc_ops_t *malloc_ops;
88} irq_cookie_t;
89
90typedef struct {
91    irq_cookie_t *irq_cookie;
92    irq_id_t irq_id;
93} ack_data_t;
94
95static inline bool check_irq_id_is_valid(irq_cookie_t *irq_cookie, irq_id_t id)
96{
97    if (unlikely(id < 0 || id >= irq_cookie->max_irq_ids)) {
98        return false;
99    }
100    return true;
101}
102
103static inline bool check_ntfn_id_is_valid(irq_cookie_t *irq_cookie, ntfn_id_t id)
104{
105    if (unlikely(id < 0 || id >= irq_cookie->max_ntfn_ids)) {
106        return false;
107    }
108    return true;
109}
110
111static inline bool check_irq_id_all_allocated(irq_cookie_t *irq_cookie)
112{
113    if (unlikely(irq_cookie->num_registered_irqs >= irq_cookie->max_irq_ids)) {
114        return true;
115    }
116    return false;
117}
118
119static inline bool check_ntfn_id_all_allocated(irq_cookie_t *irq_cookie)
120{
121    if (unlikely(irq_cookie->num_allocated_ntfns >= irq_cookie->max_ntfn_ids)) {
122        return true;
123    }
124    return false;
125}
126
127static inline bool check_ntfn_id_is_allocated(irq_cookie_t *irq_cookie, ntfn_id_t id)
128{
129    if (likely(irq_cookie->ntfn_table[id].allocated)) {
130        return true;
131    }
132    return false;
133}
134
135static inline bool check_irq_id_is_allocated(irq_cookie_t *irq_cookie, irq_id_t id)
136{
137    if (likely(irq_cookie->irq_table[id].allocated)) {
138        return true;
139    }
140    return false;
141}
142
143static inline void fill_bit_in_bitfield(seL4_Word *bitfield_array, int index)
144{
145    int bitfield_index = index % (sizeof(seL4_Word) * CHAR_BIT);
146    int array_index  = index / (sizeof(seL4_Word) * CHAR_BIT);
147    bitfield_array[array_index] |= BIT(bitfield_index);
148}
149
150static inline void unfill_bit_in_bitfield(seL4_Word *bitfield_array, int index)
151{
152    int bitfield_index = index % (sizeof(seL4_Word) * CHAR_BIT);
153    int array_index  = index / (sizeof(seL4_Word) * CHAR_BIT);
154    bitfield_array[array_index] &= ~(BIT(bitfield_index));
155}
156
157static irq_id_t find_free_irq_id(irq_cookie_t *irq_cookie)
158{
159    for (int i = 0; i < irq_cookie->num_irq_bitfields; i++) {
160        unsigned long unallocated_bitfield = ~(irq_cookie->allocated_irq_bitfields[i]);
161        if (i == irq_cookie->num_irq_bitfields - 1) {
162            /* Hide the bits that cannot be allocated, i.e. max_irq_ids is
163             * not a multiple of sizeof(seL4_Word) */
164            unsigned long num_leftover_bits = irq_cookie->num_irq_bitfields * sizeof(seL4_Word) -
165                                              irq_cookie->max_irq_ids;
166            unsigned long mask = MASK(num_leftover_bits);
167            /* Reverse the mask, (bit 0 is the smallest ID of that bitfield) */
168            mask = BSWAP_WORD(mask);
169
170            unallocated_bitfield &= mask;
171        }
172        /* Check to avoid undefined behaviour of CTZL(0) */
173        if (likely(unallocated_bitfield)) {
174            return CTZL(unallocated_bitfield) + (i * sizeof(seL4_Word) * CHAR_BIT);
175        }
176    }
177    return -1;
178}
179
180static ntfn_id_t find_free_ntfn_id(irq_cookie_t *irq_cookie)
181{
182    for (int i = 0; i < irq_cookie->num_ntfn_bitfields; i++) {
183        unsigned long unallocated_bitfield = ~(irq_cookie->allocated_ntfn_bitfields[i]);
184        if (i == irq_cookie->num_irq_bitfields - 1) {
185            /* Hide the bits that cannot be allocated, i.e. max_irq_ids is
186             * not a multiple of sizeof(seL4_Word) */
187            unsigned long num_leftover_bits = irq_cookie->num_ntfn_bitfields * sizeof(seL4_Word) -
188                                              irq_cookie->max_ntfn_ids;
189            unsigned long mask = MASK(num_leftover_bits);
190            /* Reverse the mask, (bit 0 is the smallest ID of that bitfield) */
191            mask = BSWAP_WORD(mask);
192
193            unallocated_bitfield &= mask;
194        }
195        /* Check to avoid undefined behaviour of CTZL(0) */
196        if (likely(unallocated_bitfield)) {
197            return CTZL(unallocated_bitfield) + (i * sizeof(seL4_Word) * CHAR_BIT);
198        }
199    }
200    return -1;
201}
202
203static int find_free_ntfn_badge_index(ntfn_entry_t *ntfn_entry)
204{
205    unsigned long unallocated_bitfield = ~(ntfn_entry->status_bitfield);
206    /* Mask the bits that we can use */
207    unallocated_bitfield &= ntfn_entry->usable_mask;
208    /* Check to avoid undefined behaviour of CTZL(0) */
209    if (unlikely(!unallocated_bitfield)) {
210        return -1;
211    }
212    return CTZL(unallocated_bitfield);
213}
214
215static int irq_set_ntfn_common(irq_cookie_t *irq_cookie, ntfn_id_t ntfn_id, irq_id_t irq_id,
216                               seL4_Word *ret_badge)
217{
218    irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]);
219
220    /* Check if the IRQ is already bound to something */
221    if (irq_entry->paired_ntfn > UNPAIRED_ID) {
222        return -EINVAL;
223    }
224
225    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]);
226
227    /* Check if we have space to bound another IRQ to this notification */
228    if (ntfn_entry->num_irqs_bound >= MAX_INTERRUPTS_TO_NOTIFICATIONS) {
229        return -ENOSPC;
230    }
231
232    /* Find an empty space (unclaimed bit in the badge) for the IRQ */
233    int badge_index = find_free_ntfn_badge_index(ntfn_entry);
234    if (badge_index == -1) {
235        return -ENOSPC;
236    }
237
238    /* Allocate a CSpace slot and mint the root notification object with badge */
239    cspacepath_t mint_path = {0};
240    int error = vka_cspace_alloc_path(irq_cookie->vka, &mint_path);
241    if (error) {
242        return -ENOMEM;
243    }
244
245    seL4_Word badge = BIT(badge_index);
246    error = vka_cnode_mint(&mint_path, &(ntfn_entry->root_ntfn_path), seL4_AllRights, badge);
247    if (error) {
248        vka_cspace_free_path(irq_cookie->vka, mint_path);
249        return -ENOMEM;
250    }
251
252    /* Bind the notification with the handler now */
253    error = seL4_IRQHandler_SetNotification(irq_entry->handler_path.capPtr, mint_path.capPtr);
254    if (error) {
255        ZF_LOGE("Failed to set a notification with the IRQ handler");
256        error = vka_cnode_delete(&mint_path);
257        ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation");
258        vka_cspace_free_path(irq_cookie->vka, mint_path);
259        return -EFAULT;
260    }
261
262    /* Acknowledge the handler so interrupts can arrive on the notification */
263    error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr);
264    if (error) {
265        ZF_LOGE("Failed to ack an IRQ handler");
266        error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr);
267        ZF_LOGF_IF(error, "Failed to unpair a notification after a failed IRQ set ntfn operation");
268        error = vka_cnode_delete(&mint_path);
269        ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation");
270        vka_cspace_free_path(irq_cookie->vka, mint_path);
271        return -EFAULT;
272    }
273
274    if (ret_badge) {
275        *ret_badge = badge;
276    }
277
278    /* Fill in the bookkeeping information */
279    ntfn_entry->num_irqs_bound++;
280    ntfn_entry->status_bitfield |= badge;
281    ntfn_entry->bound_irqs[badge_index] = irq_id;
282
283    irq_entry->ntfn_path = mint_path;
284    irq_entry->paired_ntfn = ntfn_id;
285    irq_entry->allocated_badge_index = badge_index;
286
287    return 0;
288}
289
290static irq_id_t irq_register_common(irq_cookie_t *irq_cookie, ps_irq_t irq, irq_callback_fn_t callback,
291                                    void *callback_data)
292{
293    if (check_irq_id_all_allocated(irq_cookie)) {
294        return -EMFILE;
295    }
296
297    if (!callback) {
298        return -EINVAL;
299    }
300
301    irq_id_t free_id = find_free_irq_id(irq_cookie);
302    if (free_id == -1) {
303        /* Probably wouldn't get here, as we checked above already */
304        ZF_LOGE("Failed to find a free IRQ id");
305        return -EMFILE;
306    }
307
308    /* Allocate a path for the IRQ handler object */
309    cspacepath_t irq_handler_path = {0};
310    vka_cspace_alloc_path(irq_cookie->vka, &irq_handler_path);
311
312    /* Create an IRQ handler object for this IRQ */
313    int err = sel4platsupport_copy_irq_cap(irq_cookie->vka, irq_cookie->simple, &irq, &irq_handler_path);
314    if (err) {
315        /* Give a slightly ambigious message as we don't want to leak implementation details */
316        ZF_LOGE("Failed to register an IRQ");
317        vka_cspace_free_path(irq_cookie->vka, irq_handler_path);
318        return -EFAULT;
319    }
320
321    irq_entry_t *irq_entry = &(irq_cookie->irq_table[free_id]);
322    irq_entry->allocated = true;
323    irq_entry->irq = irq;
324    irq_entry->handler_path = irq_handler_path;
325    irq_entry->irq_callback_fn = callback;
326    irq_entry->callback_data = callback_data;
327
328    irq_cookie->num_registered_irqs++;
329    fill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, free_id);
330
331    return free_id;
332}
333
334static void provide_ntfn_common(irq_cookie_t *irq_cookie, seL4_CPtr ntfn, seL4_Word usable_mask,
335                                ntfn_id_t allocated_id)
336{
337    cspacepath_t ntfn_path = {0};
338    vka_cspace_make_path(irq_cookie->vka, ntfn, &ntfn_path);
339
340    /* Clear the notification entry and then fill in bookkeeping information */
341    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[allocated_id]);
342    memset(ntfn_entry, 0, sizeof(ntfn_entry_t));
343    ntfn_entry->allocated = true;
344    ntfn_entry->root_ntfn_path = ntfn_path;
345    ntfn_entry->usable_mask = usable_mask;
346
347    irq_cookie->num_allocated_ntfns++;
348    fill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, allocated_id);
349}
350
351static irq_cookie_t *new_irq_ops_common(vka_t *vka, simple_t *simple, irq_interface_config_t irq_config,
352                                        ps_malloc_ops_t *malloc_ops, irq_iface_type_t iface_type)
353{
354    int err = 0;
355    /* Figure out how many bitfields we need to keep track of the allocation status of the IDs */
356    size_t bits_in_seL4_Word = sizeof(seL4_Word) * CHAR_BIT;
357    size_t num_irq_bitfields = ALIGN_UP(irq_config.max_irq_ids, bits_in_seL4_Word) / sizeof(seL4_Word);
358    size_t num_ntfn_bitfields = ALIGN_UP(irq_config.max_ntfn_ids, bits_in_seL4_Word) / sizeof(seL4_Word);
359
360    irq_cookie_t *cookie = 0;
361    err = ps_calloc(malloc_ops, 1, sizeof(irq_cookie_t), (void **) &cookie);
362    if (err) {
363        ZF_LOGE("Failed to allocate %zu bytes for cookie", sizeof(irq_cookie_t));
364        goto error;
365    }
366
367    /* Allocate the IRQ bookkeeping array, and set default values for some of the members */
368    err = ps_calloc(malloc_ops, 1, sizeof(irq_entry_t) * irq_config.max_irq_ids, (void **) & (cookie->irq_table));
369    if (err) {
370        ZF_LOGE("Failed to allocate IRQ bookkeeping array");
371        goto error;
372    }
373    for (int i = 0; i < irq_config.max_irq_ids; i++) {
374        cookie->irq_table[i].paired_ntfn = UNPAIRED_ID;
375        cookie->irq_table[i].allocated_badge_index = UNALLOCATED_BADGE_INDEX;
376    }
377
378    /* Allocate the notification bookkeeping array, and set default values for some of the members */
379    err = ps_calloc(malloc_ops, 1, sizeof(ntfn_entry_t) * irq_config.max_ntfn_ids,
380                    (void **) & (cookie->ntfn_table));
381    if (err) {
382        ZF_LOGE("Failed to allocate notification bookkeeping array");
383        goto error;
384    }
385    for (int i = 0; i < irq_config.max_ntfn_ids; i++) {
386        memset(cookie->ntfn_table[i].bound_irqs, UNPAIRED_ID, sizeof(irq_id_t) * MAX_INTERRUPTS_TO_NOTIFICATIONS);
387    }
388
389    err = ps_calloc(malloc_ops, 1, num_irq_bitfields * sizeof(seL4_Word),
390                    (void **) & (cookie->allocated_irq_bitfields));
391    if (err) {
392        ZF_LOGE("Failed to allocate the IRQ bitfields");
393        goto error;
394    }
395    err = ps_calloc(malloc_ops, 1, num_ntfn_bitfields * sizeof(seL4_Word),
396                    (void **) & (cookie->allocated_ntfn_bitfields));
397    if (err) {
398        ZF_LOGE("Failed to allocate the notification bitfields");
399        goto error;
400    }
401
402    cookie->iface_type = iface_type;
403    cookie->simple = simple;
404    cookie->vka = vka;
405    cookie->malloc_ops = malloc_ops;
406    cookie->max_irq_ids = irq_config.max_irq_ids;
407    cookie->max_ntfn_ids = irq_config.max_ntfn_ids;
408    cookie->num_irq_bitfields = num_irq_bitfields;
409    cookie->num_ntfn_bitfields = num_ntfn_bitfields;
410
411    return cookie;
412
413error:
414    if (cookie) {
415        if (cookie->irq_table) {
416            ps_free(malloc_ops, sizeof(irq_entry_t) * irq_config.max_irq_ids, cookie->irq_table);
417        }
418
419        if (cookie->ntfn_table) {
420            ps_free(malloc_ops, sizeof(ntfn_entry_t) * irq_config.max_ntfn_ids,
421                    cookie->ntfn_table);
422        }
423
424        if (cookie->allocated_irq_bitfields) {
425            ps_free(malloc_ops, sizeof(seL4_Word) * num_irq_bitfields, cookie->allocated_irq_bitfields);
426        }
427
428        ps_free(malloc_ops, sizeof(irq_cookie_t), cookie);
429    }
430
431    return NULL;
432}
433
434static int sel4platsupport_irq_unregister(void *cookie, irq_id_t irq_id)
435{
436    irq_cookie_t *irq_cookie = cookie;
437
438    if (!check_irq_id_is_valid(irq_cookie, irq_id)) {
439        return -EINVAL;
440    }
441
442    if (!check_irq_id_is_allocated(irq_cookie, irq_id)) {
443        return -EINVAL;
444    }
445
446    irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]);
447
448    if (irq_entry->paired_ntfn > UNPAIRED_ID) {
449        /* Clear the handler */
450        int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr);
451        if (error) {
452            /* Give a slightly ambigious message as we don't want to leak implementation details */
453            ZF_LOGE("Failed to unregister an IRQ");
454            return -EFAULT;
455        }
456
457        /* Delete the notification */
458        vka_cnode_delete(&(irq_entry->ntfn_path));
459        vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path);
460
461        /* Clear the necessary information in the notification array */
462        ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[irq_entry->paired_ntfn]);
463        ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index);
464        ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index);
465        ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID;
466        ntfn_entry->num_irqs_bound--;
467    }
468
469    /* Delete the handler */
470    vka_cnode_delete(&(irq_entry->handler_path));
471    vka_cspace_free_path(irq_cookie->vka, irq_entry->handler_path);
472
473    /* Zero-out the entire entry */
474    memset(irq_entry, 0, sizeof(irq_entry_t));
475    /* Reset parts of the entry */
476    irq_entry->paired_ntfn = UNPAIRED_ID;
477    irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX;
478
479    irq_cookie->num_registered_irqs--;
480    unfill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, irq_id);
481
482    return 0;
483}
484
485/* The register function for the standard IRQ interface */
486static irq_id_t sel4platsupport_irq_register(void *cookie, ps_irq_t irq, irq_callback_fn_t callback,
487                                             void *callback_data)
488{
489    irq_cookie_t *irq_cookie = cookie;
490
491    return irq_register_common(irq_cookie, irq, callback, callback_data);
492}
493
494/* The register function for the mini IRQ interface */
495static irq_id_t sel4platsupport_irq_register_mini(void *cookie, ps_irq_t irq, irq_callback_fn_t callback,
496                                                  void *callback_data)
497{
498    irq_cookie_t *irq_cookie = cookie;
499
500    irq_id_t assigned_id = irq_register_common(irq_cookie, irq, callback, callback_data);
501    if (assigned_id < 0) {
502        /* Contains the error code if < 0 */
503        return assigned_id;
504    }
505
506    /* Pair this IRQ with the only notification */
507    int error = irq_set_ntfn_common(irq_cookie, MINI_IRQ_INTERFACE_NTFN_ID, assigned_id, NULL);
508    if (error) {
509        ZF_LOGF_IF(sel4platsupport_irq_unregister(irq_cookie, assigned_id),
510                   "Failed to clean-up a failed operation");
511        return error;
512    }
513
514    return assigned_id;
515}
516
517static int sel4platsupport_irq_acknowledge(void *ack_data)
518{
519    if (!ack_data) {
520        return -EINVAL;
521    }
522
523    int ret = 0;
524
525    ack_data_t *data = ack_data;
526    irq_cookie_t *irq_cookie = data->irq_cookie;
527    irq_id_t irq_id = data->irq_id;
528
529    if (!check_irq_id_is_valid(irq_cookie, irq_id)) {
530        ret = -EINVAL;
531        goto exit;
532    }
533
534    if (!check_irq_id_is_allocated(irq_cookie, irq_id)) {
535        ret = -EINVAL;
536        goto exit;
537    }
538
539    irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]);
540    int error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr);
541    if (error) {
542        ZF_LOGE("Failed to acknowledge IRQ");
543        ret = -EFAULT;
544        goto exit;
545    }
546
547exit:
548    ps_free(irq_cookie->malloc_ops, sizeof(ack_data_t), data);
549
550    return ret;
551}
552
553int sel4platsupport_new_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple,
554                                irq_interface_config_t irq_config, ps_malloc_ops_t *malloc_ops)
555{
556    if (!irq_ops || !vka || !simple || !malloc_ops) {
557        return -EINVAL;
558    }
559
560    if (irq_config.max_irq_ids == 0 || irq_config.max_ntfn_ids == 0) {
561        return -EINVAL;
562    }
563
564    irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, STANDARD_IFACE);
565    if (!cookie) {
566        return -ENOMEM;
567    }
568
569    /* Fill in the actual IRQ ops structure now */
570    irq_ops->cookie = (void *) cookie;
571    irq_ops->irq_register_fn = sel4platsupport_irq_register;
572    irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister;
573
574    return 0;
575}
576
577int sel4platsupport_new_mini_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple,
578                                     ps_malloc_ops_t *malloc_ops, seL4_CPtr ntfn, seL4_Word usable_mask)
579{
580    if (!irq_ops || !vka || !simple || !malloc_ops || !usable_mask) {
581        return -EINVAL;
582    }
583
584    if (ntfn == seL4_CapNull) {
585        return -EINVAL;
586    }
587
588    /* Get the number of bits that we can use in the badge,
589     * this is the amount of interrupts that can be registered at a given time */
590    size_t bits_in_mask = POPCOUNTL(usable_mask);
591
592    /* max_ntfn_ids is kinda irrelevant in the mini interface, but set it anyway */
593    irq_interface_config_t irq_config = { .max_irq_ids = bits_in_mask, .max_ntfn_ids = 1 };
594
595    irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, MINI_IFACE);
596    if (!cookie) {
597        return -ENOMEM;
598    }
599
600    /* Fill in the actual IRQ ops structure now */
601    irq_ops->cookie = (void *) cookie;
602    irq_ops->irq_register_fn = sel4platsupport_irq_register_mini;
603    irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister;
604
605    /* Provide the ntfn */
606    provide_ntfn_common(cookie, ntfn, usable_mask, MINI_IRQ_INTERFACE_NTFN_ID);
607
608    return 0;
609}
610
611ntfn_id_t sel4platsupport_irq_provide_ntfn(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn, seL4_Word usable_mask)
612{
613    if (!irq_ops || ntfn == seL4_CapNull || !usable_mask) {
614        return -EINVAL;
615    }
616
617    irq_cookie_t *irq_cookie = irq_ops->cookie;
618
619    if (irq_cookie->iface_type == MINI_IFACE) {
620        ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__);
621        return -EPERM;
622    }
623
624    if (check_ntfn_id_all_allocated(irq_cookie)) {
625        return -EMFILE;
626    }
627
628    ntfn_id_t free_id = find_free_ntfn_id(irq_cookie);
629    if (free_id == -1) {
630        return -EMFILE;
631    }
632
633    provide_ntfn_common(irq_cookie, ntfn, usable_mask, free_id);
634
635    return free_id;
636}
637
638int sel4platsupport_irq_provide_ntfn_with_id(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn,
639                                             seL4_Word usable_mask, ntfn_id_t id_hint)
640{
641    if (!irq_ops || ntfn == seL4_CapNull || !usable_mask) {
642        return -EINVAL;
643    }
644
645    irq_cookie_t *irq_cookie = irq_ops->cookie;
646
647    if (irq_cookie->iface_type == MINI_IFACE) {
648        ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__);
649        return -EPERM;
650    }
651
652    if (check_ntfn_id_all_allocated(irq_cookie)) {
653        return -EMFILE;
654    }
655
656    if (irq_cookie->ntfn_table[id_hint].allocated) {
657        return -EADDRINUSE;
658    }
659
660    provide_ntfn_common(irq_cookie, ntfn, usable_mask, id_hint);
661
662    return 0;
663}
664
665int sel4platsupport_irq_return_ntfn(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id,
666                                    seL4_CPtr *ret_cptr)
667{
668    if (!irq_ops) {
669        return -EINVAL;
670    }
671
672    irq_cookie_t *irq_cookie = irq_ops->cookie;
673
674    if (irq_cookie->iface_type == MINI_IFACE) {
675        ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__);
676        return -EPERM;
677    }
678
679    if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) {
680        return -EINVAL;
681    }
682
683    if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) {
684        return -EINVAL;
685    }
686
687    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]);
688
689    if (ntfn_entry->num_irqs_bound > 0) {
690        unsigned long allocated_bits = ntfn_entry->status_bitfield;
691        while (allocated_bits) {
692            unsigned long index = CTZL(allocated_bits);
693            irq_entry_t *irq_entry = &(irq_cookie->irq_table[ntfn_entry->bound_irqs[index]]);
694            seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr);
695            int error = vka_cnode_delete(&(irq_entry->ntfn_path));
696            ZF_LOGF_IF(error, "Failed to delete a minted notification");
697            irq_entry->ntfn_path = (cspacepath_t) {
698                0
699            };
700            irq_entry->paired_ntfn = UNPAIRED_ID;
701            irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX;
702
703            allocated_bits &= ~BIT(index);
704        }
705    }
706
707    if (ret_cptr) {
708        *ret_cptr = ntfn_entry->root_ntfn_path.capPtr;
709    }
710
711    /* Zero out the entire entry */
712    memset(ntfn_entry, 0, sizeof(ntfn_entry_t));
713    /* Reset the bound_irqs array for the entry */
714    memset(ntfn_entry->bound_irqs, UNPAIRED_ID, sizeof(irq_id_t) * MAX_INTERRUPTS_TO_NOTIFICATIONS);
715
716    irq_cookie->num_allocated_ntfns--;
717    unfill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, ntfn_id);
718
719    return 0;
720}
721
722int sel4platsupport_irq_set_ntfn(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, irq_id_t irq_id, seL4_Word *ret_badge)
723{
724    if (!irq_ops) {
725        return -EINVAL;
726    }
727
728    irq_cookie_t *irq_cookie = irq_ops->cookie;
729
730    if (irq_cookie->iface_type == MINI_IFACE) {
731        ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__);
732        return -EPERM;
733    }
734
735    if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id) ||
736        !check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) {
737        return -EINVAL;
738    }
739
740    if (!check_irq_id_is_valid(irq_cookie, irq_id) ||
741        !check_irq_id_is_allocated(irq_cookie, irq_id)) {
742        return -EINVAL;
743    }
744
745    return irq_set_ntfn_common(irq_cookie, ntfn_id, irq_id, ret_badge);
746}
747
748int sel4platsupport_irq_unset_ntfn(ps_irq_ops_t *irq_ops, irq_id_t irq_id)
749{
750    if (!irq_ops) {
751        return -EINVAL;
752    }
753
754    irq_cookie_t *irq_cookie = irq_ops->cookie;
755
756    if (irq_cookie->iface_type == MINI_IFACE) {
757        ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__);
758        return -EPERM;
759    }
760
761    if (!check_irq_id_is_valid(irq_cookie, irq_id) ||
762        !check_irq_id_is_allocated(irq_cookie, irq_id)) {
763        return -EINVAL;
764    }
765
766    irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]);
767
768    /* Check if the IRQ is bound to a notification */
769    if (irq_entry->paired_ntfn == UNPAIRED_ID) {
770        return -EINVAL;
771    }
772
773    /* Unbind the notification from the handler */
774    int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr);
775    if (error) {
776        ZF_LOGE("Failed to unpair a notification");
777        return -EFAULT;
778    }
779
780    /* Delete the minted notification and free the path */
781    vka_cnode_delete(&(irq_entry->ntfn_path));
782    vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path);
783
784    /* Free the allocated badge index and update the bookkeeping in the notification array and the irq array */
785    ntfn_id_t paired_id = irq_entry->paired_ntfn;
786    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[paired_id]);
787    ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index);
788    ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index);
789    ntfn_entry->num_irqs_bound--;
790    ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID;
791
792    irq_entry->ntfn_path = (cspacepath_t) {
793        0
794    };
795    irq_entry->paired_ntfn = UNPAIRED_ID;
796    irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX;
797
798    return 0;
799}
800
801static bool perform_callback(irq_cookie_t *irq_cookie, irq_id_t irq_id, unsigned long badge_bit)
802{
803    irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]);
804    irq_callback_fn_t callback = irq_entry->irq_callback_fn;
805
806    /* Check if callback was registered, if so, then run it */
807    if (callback) {
808        ack_data_t *ack_data = NULL;
809        int error = ps_calloc(irq_cookie->malloc_ops, 1, sizeof(ack_data_t), (void **) &ack_data);
810        ZF_LOGF_IF(error, "Failed to allocate memory for a cookie for the acknowledge function");
811        *ack_data = (ack_data_t) {
812            .irq_cookie = irq_cookie, .irq_id = irq_id
813        };
814        callback(irq_entry->callback_data,
815                 sel4platsupport_irq_acknowledge, ack_data);
816        return true;
817    }
818    return false;
819}
820
821int sel4platsupport_irq_handle(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, seL4_Word handle_mask)
822{
823    if (!irq_ops) {
824        return -EINVAL;
825    }
826
827    irq_cookie_t *irq_cookie = irq_ops->cookie;
828
829    if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id) ||
830        !check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) {
831        return -EINVAL;
832    }
833
834    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]);
835
836    /* Just in case, but probably should throw an error at the user for passing in bits that
837     * we dont' handle */
838    unsigned long unchecked_bits = handle_mask & ntfn_entry->usable_mask;
839
840    while (unchecked_bits) {
841        unsigned long bit_index = CTZL(unchecked_bits);
842        irq_id_t paired_irq_id = ntfn_entry->bound_irqs[bit_index];
843        bool callback_called = perform_callback(irq_cookie, paired_irq_id, bit_index);
844        if (callback_called && ntfn_entry->pending_bitfield & BIT(bit_index)) {
845            /* Unset the bit, we've performed the callback for that interrupt */
846            ntfn_entry->pending_bitfield &= ~BIT(bit_index);
847        }
848        unchecked_bits &= ~BIT(bit_index);
849    }
850
851    return 0;
852}
853
854static void serve_irq(irq_cookie_t *irq_cookie, ntfn_id_t id, seL4_Word mask,
855                      seL4_Word badge, seL4_Word *ret_leftover_bits)
856{
857    seL4_Word served_mask = 0;
858
859    ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[id]);
860
861    /* Mask out the bits the are not relevant to us */
862    unsigned long unchecked_bits = badge & ntfn_entry->usable_mask;
863    /* Also check the interrupts that were leftover and not served */
864    unchecked_bits |= ntfn_entry->pending_bitfield;
865
866    while (unchecked_bits) {
867        unsigned long bit_index = CTZL(unchecked_bits);
868
869        if (likely(BIT(bit_index) & mask)) {
870            irq_id_t paired_irq_id = ntfn_entry->bound_irqs[bit_index];
871            if (perform_callback(irq_cookie, paired_irq_id, bit_index)) {
872                /* Record that this particular IRQ was served */
873                served_mask |= BIT(bit_index);
874            }
875        }
876
877        unchecked_bits &= ~BIT(bit_index);
878    }
879
880    /* Record the IRQs that were not served but arrived, note that we don't want to
881     * override the leftover IRQs still inside the pending bitfield */
882    ntfn_entry->pending_bitfield ^= badge & ntfn_entry->usable_mask & ~(served_mask);
883
884    /* Write bits that are leftover */
885    if (ret_leftover_bits) {
886        *ret_leftover_bits = badge & ~(served_mask);
887    }
888}
889
890int sel4platsupport_irq_wait(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id,
891                             seL4_Word wait_mask,
892                             seL4_Word *ret_leftover_bits)
893{
894    if (!irq_ops) {
895        return -EINVAL;
896    }
897
898    irq_cookie_t *irq_cookie = irq_ops->cookie;
899
900    if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) {
901        return -EINVAL;
902    }
903
904    if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) {
905        return -EINVAL;
906    }
907
908    seL4_CPtr ntfn = irq_cookie->ntfn_table[ntfn_id].root_ntfn_path.capPtr;
909
910    seL4_Word badge = 0;
911
912    /* Wait on the notification object */
913    seL4_Wait(ntfn, &badge);
914
915    serve_irq(irq_cookie, ntfn_id, wait_mask, badge, ret_leftover_bits);
916
917    return 0;
918
919}
920
921int sel4platsupport_irq_poll(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id,
922                             seL4_Word poll_mask,
923                             seL4_Word *ret_leftover_bits)
924{
925    if (!irq_ops) {
926        return -EINVAL;
927    }
928
929    irq_cookie_t *irq_cookie = irq_ops->cookie;
930
931    if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) {
932        return -EINVAL;
933    }
934
935    if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) {
936        return -EINVAL;
937    }
938
939    seL4_CPtr ntfn = irq_cookie->ntfn_table[ntfn_id].root_ntfn_path.capPtr;
940
941    seL4_Word badge = 0;
942
943    /* Poll the notification object */
944    seL4_Poll(ntfn, &badge);
945
946    serve_irq(irq_cookie, ntfn_id, poll_mask, badge, ret_leftover_bits);
947
948    return 0;
949}
950