libsel4platsupport: Refactor the IRQ sources This commit refactors the IRQ sources and pulls out common code into separate functions. This is in preparation of a more simpler/mini IRQ interface.
diff --git a/libsel4platsupport/src/irq.c b/libsel4platsupport/src/irq.c index 3280837..fef4948 100644 --- a/libsel4platsupport/src/irq.c +++ b/libsel4platsupport/src/irq.c
@@ -206,11 +206,84 @@ return CTZL(unallocated_bitfield); } -static irq_id_t sel4platsupport_irq_register(void *cookie, ps_irq_t irq, irq_callback_fn_t callback, - void *callback_data) +static int irq_set_ntfn_common(irq_cookie_t *irq_cookie, ntfn_id_t ntfn_id, irq_id_t irq_id, + seL4_Word *ret_badge) { - irq_cookie_t *irq_cookie = cookie; + irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); + /* Check if the IRQ is already bound to something */ + if (irq_entry->paired_ntfn > UNPAIRED_ID) { + return -EINVAL; + } + + ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]); + + /* Check if we have space to bound another IRQ to this notification */ + if (ntfn_entry->num_irqs_bound >= MAX_INTERRUPTS_TO_NOTIFICATIONS) { + return -ENOSPC; + } + + /* Find an empty space (unclaimed bit in the badge) for the IRQ */ + int badge_index = find_free_ntfn_badge_index(ntfn_entry); + if (badge_index == -1) { + return -ENOSPC; + } + + /* Allocate a CSpace slot and mint the root notification object with badge */ + cspacepath_t mint_path = {0}; + int error = vka_cspace_alloc_path(irq_cookie->vka, &mint_path); + if (error) { + return -ENOMEM; + } + + seL4_Word badge = BIT(badge_index); + error = vka_cnode_mint(&mint_path, &(ntfn_entry->root_ntfn_path), seL4_AllRights, badge); + if (error) { + vka_cspace_free_path(irq_cookie->vka, mint_path); + return -ENOMEM; + } + + /* Bind the notification with the handler now */ + error = seL4_IRQHandler_SetNotification(irq_entry->handler_path.capPtr, mint_path.capPtr); + if (error) { + ZF_LOGE("Failed to set a notification with the IRQ handler"); + error = vka_cnode_delete(&mint_path); + ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); + vka_cspace_free_path(irq_cookie->vka, mint_path); + return -EFAULT; + } + + /* Acknowledge the handler so interrupts can arrive on the notification */ + error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); + if (error) { + ZF_LOGE("Failed to ack an IRQ handler"); + error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); + ZF_LOGF_IF(error, "Failed to unpair a notification after a failed IRQ set ntfn operation"); + error = vka_cnode_delete(&mint_path); + ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); + vka_cspace_free_path(irq_cookie->vka, mint_path); + return -EFAULT; + } + + if (ret_badge) { + *ret_badge = badge; + } + + /* Fill in the bookkeeping information */ + ntfn_entry->num_irqs_bound++; + ntfn_entry->status_bitfield |= badge; + ntfn_entry->bound_irqs[badge_index] = irq_id; + + irq_entry->ntfn_path = mint_path; + irq_entry->paired_ntfn = ntfn_id; + irq_entry->allocated_badge_index = badge_index; + + return 0; +} + +static irq_id_t irq_register_common(irq_cookie_t *irq_cookie, ps_irq_t irq, irq_callback_fn_t callback, + void *callback_data) +{ if (check_irq_id_all_allocated(irq_cookie)) { return -EMFILE; } @@ -252,104 +325,26 @@ return free_id; } -static int sel4platsupport_irq_unregister(void *cookie, irq_id_t irq_id) +static void provide_ntfn_common(irq_cookie_t *irq_cookie, seL4_CPtr ntfn, seL4_Word usable_mask, + ntfn_id_t allocated_id) { - irq_cookie_t *irq_cookie = cookie; + cspacepath_t ntfn_path = {0}; + vka_cspace_make_path(irq_cookie->vka, ntfn, &ntfn_path); - if (!check_irq_id_is_valid(irq_cookie, irq_id)) { - return -EINVAL; - } + /* Clear the notification entry and then fill in bookkeeping information */ + ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[allocated_id]); + memset(ntfn_entry, 0, sizeof(ntfn_entry_t)); + ntfn_entry->allocated = true; + ntfn_entry->root_ntfn_path = ntfn_path; + ntfn_entry->usable_mask = usable_mask; - if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { - return -EINVAL; - } - - irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); - - if (irq_entry->paired_ntfn > UNPAIRED_ID) { - /* Clear the handler */ - int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); - if (error) { - /* Give a slightly ambigious message as we don't want to leak implementation details */ - ZF_LOGE("Failed to unregister an IRQ"); - return -EFAULT; - } - - /* Delete the notification */ - vka_cnode_delete(&(irq_entry->ntfn_path)); - vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path); - - /* Clear the necessary information in the notification array */ - ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[irq_entry->paired_ntfn]); - ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index); - ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index); - ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID; - ntfn_entry->num_irqs_bound--; - } - - /* Delete the handler */ - vka_cnode_delete(&(irq_entry->handler_path)); - vka_cspace_free_path(irq_cookie->vka, irq_entry->handler_path); - - /* Zero-out the entire entry */ - memset(irq_entry, 0, sizeof(irq_entry_t)); - /* Reset parts of the entry */ - irq_entry->paired_ntfn = UNPAIRED_ID; - irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX; - - irq_cookie->num_registered_irqs--; - unfill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, irq_id); - - return 0; + irq_cookie->num_allocated_ntfns++; + fill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, allocated_id); } -static int sel4platsupport_irq_acknowledge(void *ack_data) +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) { - if (!ack_data) { - return -EINVAL; - } - - int ret = 0; - - ack_data_t *data = ack_data; - irq_cookie_t *irq_cookie = data->irq_cookie; - irq_id_t irq_id = data->irq_id; - - if (!check_irq_id_is_valid(irq_cookie, irq_id)) { - ret = -EINVAL; - goto exit; - } - - if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { - ret = -EINVAL; - goto exit; - } - - irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); - int error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); - if (error) { - ZF_LOGE("Failed to acknowledge IRQ"); - ret = -EFAULT; - goto exit; - } - -exit: - ps_free(irq_cookie->malloc_ops, sizeof(ack_data_t), data); - - return ret; -} - -int sel4platsupport_new_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, - irq_interface_config_t irq_config, ps_malloc_ops_t *malloc_ops) -{ - if (!irq_ops || !vka || !simple || !malloc_ops) { - return -EINVAL; - } - - if (irq_config.max_irq_ids == 0 || irq_config.max_ntfn_ids == 0) { - return -EINVAL; - } - int err = 0; irq_cookie_t *cookie = 0; @@ -406,12 +401,7 @@ cookie->num_irq_bitfields = num_irq_bitfields; cookie->num_ntfn_bitfields = num_ntfn_bitfields; - /* Fill in the actual IRQ ops structure now */ - irq_ops->cookie = (void *) cookie; - irq_ops->irq_register_fn = sel4platsupport_irq_register; - irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister; - - return 0; + return cookie; error: if (cookie) { @@ -431,24 +421,127 @@ ps_free(malloc_ops, sizeof(irq_cookie_t), cookie); } - return -ENOMEM; + return NULL; } -static void provide_ntfn_common(irq_cookie_t *irq_cookie, seL4_CPtr ntfn, seL4_Word usable_mask, - ntfn_id_t allocated_id) +static int sel4platsupport_irq_unregister(void *cookie, irq_id_t irq_id) { - cspacepath_t ntfn_path = {0}; - vka_cspace_make_path(irq_cookie->vka, ntfn, &ntfn_path); + irq_cookie_t *irq_cookie = cookie; - /* Clear the notification entry and then fill in bookkeeping information */ - ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[allocated_id]); - memset(ntfn_entry, 0, sizeof(ntfn_entry_t)); - ntfn_entry->allocated = true; - ntfn_entry->root_ntfn_path = ntfn_path; - ntfn_entry->usable_mask = usable_mask; + if (!check_irq_id_is_valid(irq_cookie, irq_id)) { + return -EINVAL; + } - irq_cookie->num_allocated_ntfns++; - fill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, allocated_id); + if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { + return -EINVAL; + } + + irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); + + if (irq_entry->paired_ntfn > UNPAIRED_ID) { + /* Clear the handler */ + int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); + if (error) { + /* Give a slightly ambigious message as we don't want to leak implementation details */ + ZF_LOGE("Failed to unregister an IRQ"); + return -EFAULT; + } + + /* Delete the notification */ + vka_cnode_delete(&(irq_entry->ntfn_path)); + vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path); + + /* Clear the necessary information in the notification array */ + ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[irq_entry->paired_ntfn]); + ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index); + ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index); + ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID; + ntfn_entry->num_irqs_bound--; + } + + /* Delete the handler */ + vka_cnode_delete(&(irq_entry->handler_path)); + vka_cspace_free_path(irq_cookie->vka, irq_entry->handler_path); + + /* Zero-out the entire entry */ + memset(irq_entry, 0, sizeof(irq_entry_t)); + /* Reset parts of the entry */ + irq_entry->paired_ntfn = UNPAIRED_ID; + irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX; + + irq_cookie->num_registered_irqs--; + unfill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, irq_id); + + return 0; +} + +/* The register function for the standard IRQ interface */ +static irq_id_t sel4platsupport_irq_register(void *cookie, ps_irq_t irq, irq_callback_fn_t callback, + void *callback_data) +{ + irq_cookie_t *irq_cookie = cookie; + + return irq_register_common(irq_cookie, irq, callback, callback_data); +} + +static int sel4platsupport_irq_acknowledge(void *ack_data) +{ + if (!ack_data) { + return -EINVAL; + } + + int ret = 0; + + ack_data_t *data = ack_data; + irq_cookie_t *irq_cookie = data->irq_cookie; + irq_id_t irq_id = data->irq_id; + + if (!check_irq_id_is_valid(irq_cookie, irq_id)) { + ret = -EINVAL; + goto exit; + } + + if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { + ret = -EINVAL; + goto exit; + } + + irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); + int error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); + if (error) { + ZF_LOGE("Failed to acknowledge IRQ"); + ret = -EFAULT; + goto exit; + } + +exit: + ps_free(irq_cookie->malloc_ops, sizeof(ack_data_t), data); + + return ret; +} + +int sel4platsupport_new_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, + irq_interface_config_t irq_config, ps_malloc_ops_t *malloc_ops) +{ + if (!irq_ops || !vka || !simple || !malloc_ops) { + return -EINVAL; + } + + if (irq_config.max_irq_ids == 0 || irq_config.max_ntfn_ids == 0) { + return -EINVAL; + } + + irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops); + 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; + irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister; + + return 0; } ntfn_id_t sel4platsupport_irq_provide_ntfn(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn, seL4_Word usable_mask) @@ -565,76 +658,7 @@ return -EINVAL; } - irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); - - /* Check if the IRQ is already bound to something */ - if (irq_entry->paired_ntfn > UNPAIRED_ID) { - return -EINVAL; - } - - ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]); - - /* Check if we have space to bound another IRQ to this notification */ - if (ntfn_entry->num_irqs_bound >= MAX_INTERRUPTS_TO_NOTIFICATIONS) { - return -ENOSPC; - } - - /* Find an empty space (unclaimed bit in the badge) for the IRQ */ - int badge_index = find_free_ntfn_badge_index(ntfn_entry); - if (badge_index == -1) { - return -ENOSPC; - } - - /* Allocate a CSpace slot and mint the root notification object with badge */ - cspacepath_t mint_path = {0}; - int error = vka_cspace_alloc_path(irq_cookie->vka, &mint_path); - if (error) { - return -ENOMEM; - } - - seL4_Word badge = BIT(badge_index); - error = vka_cnode_mint(&mint_path, &(ntfn_entry->root_ntfn_path), seL4_AllRights, badge); - if (error) { - vka_cspace_free_path(irq_cookie->vka, mint_path); - return -ENOMEM; - } - - /* Bind the notification with the handler now */ - error = seL4_IRQHandler_SetNotification(irq_entry->handler_path.capPtr, mint_path.capPtr); - if (error) { - ZF_LOGE("Failed to set a notification with the IRQ handler"); - error = vka_cnode_delete(&mint_path); - ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); - vka_cspace_free_path(irq_cookie->vka, mint_path); - return -EFAULT; - } - - /* Acknowledge the handler so interrupts can arrive on the notification */ - error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); - if (error) { - ZF_LOGE("Failed to ack an IRQ handler"); - error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); - ZF_LOGF_IF(error, "Failed to unpair a notification after a failed IRQ set ntfn operation"); - error = vka_cnode_delete(&mint_path); - ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); - vka_cspace_free_path(irq_cookie->vka, mint_path); - return -EFAULT; - } - - if (ret_badge) { - *ret_badge = badge; - } - - /* Fill in the bookkeeping information */ - ntfn_entry->num_irqs_bound++; - ntfn_entry->status_bitfield |= badge; - ntfn_entry->bound_irqs[badge_index] = irq_id; - - irq_entry->ntfn_path = mint_path; - irq_entry->paired_ntfn = ntfn_id; - irq_entry->allocated_badge_index = badge_index; - - return 0; + return irq_set_ntfn_common(irq_cookie, ntfn_id, irq_id, ret_badge); } int sel4platsupport_irq_unset_ntfn(ps_irq_ops_t *irq_ops, irq_id_t irq_id)