1/*
2 * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9#include <kernel.h>
10#include <stdio.h>
11#include <string.h>
12#include <arch/arm/arm.h>
13#include <barrelfish_kpi/lmp.h>
14#include <barrelfish_kpi/syscalls.h>
15#include <barrelfish_kpi/sys_debug.h>
16
17#include <arch/armv7/irq.h>
18
19#include <paging_kernel_arch.h>
20#include <dispatch.h>
21#include <exec.h>
22#include <stdio.h>
23#include <syscall.h>
24#include <arch/arm/syscall_arm.h>
25#include <kcb.h>
26#include <arch/arm/gic.h>
27
28/**
29 * \brief User-space IRQ dispatch table.
30 *
31 * This is essentially a big CNode holding #NDISPATCH capability
32 * entries to local endpoints of user-space applications listening to
33 * the interrupts.
34 */
35static struct cte irq_dispatch[NDISPATCH];
36
37errval_t irq_table_set(unsigned int nidt, capaddr_t endpoint)
38{
39    errval_t err;
40    struct cte *recv;
41
42    err = caps_lookup_slot(&dcb_current->cspace.cap, endpoint,
43                           2, &recv, CAPRIGHTS_WRITE);
44    if (err_is_fail(err)) {
45        return err_push(err, SYS_ERR_IRQ_LOOKUP);
46    }
47
48    assert(recv != NULL);
49
50    // Return w/error if cap is not an endpoint
51    if (recv->cap.type != ObjType_EndPoint) {
52        return SYS_ERR_IRQ_NOT_ENDPOINT;
53    }
54
55    // Return w/error if no listener on endpoint
56    if (recv->cap.u.endpoint.listener == NULL) {
57        return SYS_ERR_IRQ_NO_LISTENER;
58    }
59
60    if (nidt < NDISPATCH) {
61        // check that we don't overwrite someone else's handler
62        if (irq_dispatch[nidt].cap.type != ObjType_Null) {
63            printf("kernel: installing new handler for IRQ %d\n", nidt);
64        }
65        err = caps_copy_to_cte(&irq_dispatch[nidt], recv, false, 0, 0);
66
67        // Route the interrupt to this CPU, as we're the only CPU driver that
68        // knows where to send it.  XXX - this should change once we have more
69        // sophisticated interrupt routing.
70        //
71        // The mapping of interrupt interfaces to cores doesn't seem to be
72        // documented anywhere for the A9, and this will have to be different
73        // if we're using affinity routing on GICv3+ systems.
74        gic_enable_interrupt(nidt, BIT(my_core_id), 0,
75                GIC_IRQ_EDGE_TRIGGERED, GIC_IRQ_N_TO_N);
76#if 0
77        if (err_is_ok(err)) {
78            // Unmask interrupt if on PIC
79            if(nidt < 16) {
80                pic_toggle_irq(nidt, true);
81            }
82        }
83#endif
84        return err;
85    }
86
87    return SYS_ERR_IRQ_INVALID;
88}
89
90errval_t irq_table_delete(unsigned int nidt)
91{
92    if (nidt < NDISPATCH) {
93        irq_dispatch[nidt].cap.type = ObjType_Null;
94
95        /* todo: gic disable irq */
96
97        return SYS_ERR_OK;
98    }
99    return SYS_ERR_IRQ_INVALID;
100}
101
102errval_t irq_table_notify_domains(struct kcb *kcb)
103{
104    uintptr_t msg[] = { 1 };
105    for (int i = 0; i < NDISPATCH; i++) {
106        if (kcb->irq_dispatch[i].cap.type == ObjType_EndPoint) {
107            struct capability *cap = &kcb->irq_dispatch[i].cap;
108            // 1 word message as notification
109            errval_t err = lmp_deliver_payload(cap, NULL, msg, 1, false, false);
110            if (err_is_fail(err)) {
111                if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
112                    struct dispatcher_shared_generic *disp =
113                        get_dispatcher_shared_generic(cap->u.endpoint.listener->disp);
114                    printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
115                            DISP_NAME_LEN, disp->name);
116                } else {
117                    printk(LOG_ERR, "Unexpected error delivering IRQ\n");
118                }
119            }
120        }
121        kcb->irq_dispatch[i].cap.type = ObjType_Null;
122    }
123    return SYS_ERR_OK;
124}
125
126/**
127 * \brief Send interrupt notification to user-space listener.
128 *
129 * Sends an interrupt notification IDC to a local endpoint that
130 * listens for IRQ notifications.
131 *
132 * \param irq   IRQ# to send in notification.
133 */
134void send_user_interrupt(int irq)
135{
136    assert(irq >= 0 && irq < NDISPATCH);
137    struct capability *cap = &irq_dispatch[irq].cap;
138
139    // Return on null cap (unhandled interrupt)
140    if (cap->type == ObjType_Null) {
141        printk(LOG_WARN, "unhandled IRQ %d\n", irq);
142        return;
143    }
144
145    // Otherwise, cap needs to be an endpoint
146    assert(cap->type == ObjType_EndPoint);
147    errval_t err = lmp_deliver_notification(cap);
148    if (err_is_fail(err)) {
149        if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
150            struct dispatcher_shared_generic *disp =
151                    get_dispatcher_shared_generic(
152                            cap->u.endpoint.listener->disp);
153            printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
154                    DISP_NAME_LEN, disp->name);
155        } else {
156            printk(LOG_ERR, "Unexpected error delivering IRQ\n");
157        }
158    }
159
160#ifdef SCHEDULER_RR
161    /* XXX: run the handler dispatcher immediately
162     * we shouldn't do this (we should let the scheduler decide), but because
163     * our default scheduler is braindead, this is a quick hack to make sure
164     * that mostly-sane things happen
165     */
166    dispatch(cap->u.endpoint.listener);
167#else
168    dispatch(schedule());
169#endif
170}
171