/* * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich. * All rights reserved. * * This file is distributed under the terms in the attached LICENSE file. * If you do not find this file, copies can be found by writing to: * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \brief User-space IRQ dispatch table. * * This is essentially a big CNode holding #NDISPATCH capability * entries to local endpoints of user-space applications listening to * the interrupts. */ static struct cte irq_dispatch[NDISPATCH]; errval_t irq_table_set(unsigned int nidt, capaddr_t endpoint) { errval_t err; struct cte *recv; err = caps_lookup_slot(&dcb_current->cspace.cap, endpoint, 2, &recv, CAPRIGHTS_WRITE); if (err_is_fail(err)) { return err_push(err, SYS_ERR_IRQ_LOOKUP); } assert(recv != NULL); // Return w/error if cap is not an endpoint if (recv->cap.type != ObjType_EndPointLMP) { return SYS_ERR_IRQ_NOT_ENDPOINT; } // Return w/error if no listener on endpoint if (recv->cap.u.endpointlmp.listener == NULL) { return SYS_ERR_IRQ_NO_LISTENER; } if (nidt < NDISPATCH) { // check that we don't overwrite someone else's handler if (irq_dispatch[nidt].cap.type != ObjType_Null) { printf("kernel: replacing handler for IRQ %d\n", nidt); } err = caps_copy_to_cte(&irq_dispatch[nidt], recv, false, 0, 0); // Correct interrupt forwarding by the distributor must be ensured // in userspace. return err; } return SYS_ERR_IRQ_INVALID; } errval_t irq_connect(struct capability *irq_dest, capaddr_t endpoint) { assert(irq_dest->type == ObjType_IRQDest); return irq_table_set(irq_dest->u.irqdest.vector, endpoint); } errval_t irq_table_delete(unsigned int nidt) { if (nidt < NDISPATCH) { irq_dispatch[nidt].cap.type = ObjType_Null; /* todo: gic disable irq */ return SYS_ERR_OK; } return SYS_ERR_IRQ_INVALID; } errval_t irq_table_alloc_dest_cap(uint8_t dcn_level, capaddr_t dcn, capaddr_t out_cap_addr, int vec_hint) { errval_t err; if(vec_hint < 0){ printk(LOG_WARN, "irq: vec_hint must be provided on ARM\n", vec_hint); return SYS_ERR_IRQ_INVALID; } if(vec_hint >= NDISPATCH){ printk(LOG_WARN, "irq: vec_hint (%d) invalid\n", vec_hint); return SYS_ERR_IRQ_INVALID; } // TODO: Keep track of allocations struct cte * cn; err = caps_lookup_slot(&dcb_current->cspace.cap, dcn, dcn_level, &cn, CAPRIGHTS_WRITE); if(err_is_fail(err)){ return err; } struct cte out_cap; memset(&out_cap, 0, sizeof(struct cte)); out_cap.cap.type = ObjType_IRQDest; out_cap.cap.u.irqdest.cpu = my_core_id; out_cap.cap.u.irqdest.vector = vec_hint; caps_copy_to_cnode(cn, out_cap_addr, &out_cap, 0, 0, 0); return SYS_ERR_OK; } errval_t irq_table_notify_domains(struct kcb *kcb) { uintptr_t msg[] = { 1 }; for (int i = 0; i < NDISPATCH; i++) { if (kcb->irq_dispatch[i].cap.type == ObjType_EndPointLMP) { struct capability *cap = &kcb->irq_dispatch[i].cap; // 1 word message as notification errval_t err = lmp_deliver_payload(cap, NULL, msg, 1, false, false); if (err_is_fail(err)) { if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) { struct dispatcher_shared_generic *disp = get_dispatcher_shared_generic(cap->u.endpointlmp.listener->disp); printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n", DISP_NAME_LEN, disp->name); } else { printk(LOG_ERR, "Unexpected error delivering IRQ\n"); } } } kcb->irq_dispatch[i].cap.type = ObjType_Null; } return SYS_ERR_OK; } /** * \brief Send interrupt notification to user-space listener. * * Sends an interrupt notification IDC to a local endpoint that * listens for IRQ notifications. * * \param irq IRQ# to send in notification. */ void send_user_interrupt(int irq) { assert(irq >= 0 && irq < NDISPATCH); struct capability *cap = &irq_dispatch[irq].cap; // Return on null cap (unhandled interrupt) if (cap->type == ObjType_Null) { printk(LOG_WARN, "unhandled IRQ %d\n", irq); return; } // Otherwise, cap needs to be an endpoint assert(cap->type == ObjType_EndPointLMP); errval_t err = lmp_deliver_notification(cap); if (err_is_fail(err)) { if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) { struct dispatcher_shared_generic *disp = get_dispatcher_shared_generic( cap->u.endpointlmp.listener->disp); printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n", DISP_NAME_LEN, disp->name); } else { printk(LOG_ERR, "Unexpected error delivering IRQ\n"); } } #ifdef SCHEDULER_RR /* XXX: run the handler dispatcher immediately * we shouldn't do this (we should let the scheduler decide), but because * our default scheduler is braindead, this is a quick hack to make sure * that mostly-sane things happen */ dispatch(cap->u.endpointlmp.listener); #else dispatch(schedule()); #endif }