armv8: Port irq cap invocations
[barrelfish] / kernel / arch / arm / irq.c
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, Universitaetstrasse 6, 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 <arch/arm/platform.h>
14 #include <barrelfish_kpi/lmp.h>
15 #include <barrelfish_kpi/syscalls.h>
16 #include <barrelfish_kpi/sys_debug.h>
17
18 #include <irq.h>
19
20 #include <paging_kernel_arch.h>
21 #include <dispatch.h>
22 #include <exec.h>
23 #include <stdio.h>
24 #include <syscall.h>
25 #include <arch/arm/syscall_arm.h>
26 #include <kcb.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  */
35 static struct cte irq_dispatch[NDISPATCH];
36
37 errval_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_EndPointLMP) {
52         return SYS_ERR_IRQ_NOT_ENDPOINT;
53     }
54
55     // Return w/error if no listener on endpoint
56     if (recv->cap.u.endpointlmp.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: replacing handler for IRQ %d\n", nidt);
64         }
65         err = caps_copy_to_cte(&irq_dispatch[nidt], recv, false, 0, 0);
66
67         // Correct interrupt forwarding by the distributor must be ensured
68         // in userspace.
69         
70         return err;
71     }
72
73     return SYS_ERR_IRQ_INVALID;
74 }
75
76 errval_t irq_connect(struct capability *irq_dest, capaddr_t endpoint) {
77     assert(irq_dest->type == ObjType_IRQDest);
78     return irq_table_set(irq_dest->u.irqdest.vector, endpoint);
79 }
80
81 errval_t irq_table_delete(unsigned int nidt)
82 {
83     if (nidt < NDISPATCH) {
84         irq_dispatch[nidt].cap.type = ObjType_Null;
85
86         /* todo: gic disable irq */
87
88         return SYS_ERR_OK;
89     }
90     return SYS_ERR_IRQ_INVALID;
91 }
92
93 errval_t irq_table_alloc_dest_cap(uint8_t dcn_level, capaddr_t dcn,
94                                   capaddr_t out_cap_addr, int vec_hint)
95 {
96     errval_t err;
97     if(vec_hint < 0){
98         printk(LOG_WARN, "irq: vec_hint must be provided on ARM\n", vec_hint);
99         return SYS_ERR_IRQ_INVALID;
100     }
101     if(vec_hint >= NDISPATCH){
102         printk(LOG_WARN, "irq: vec_hint (%d) invalid\n", vec_hint);
103         return SYS_ERR_IRQ_INVALID;
104     }
105
106     // TODO: Keep track of allocations 
107     struct cte * cn;
108     err = caps_lookup_slot(&dcb_current->cspace.cap, dcn, dcn_level,
109                            &cn, CAPRIGHTS_WRITE);
110     if(err_is_fail(err)){
111         return err;
112     }
113
114     struct cte out_cap;
115     memset(&out_cap, 0, sizeof(struct cte));
116     out_cap.cap.type = ObjType_IRQDest;
117     out_cap.cap.u.irqdest.cpu = my_core_id;
118     out_cap.cap.u.irqdest.vector = vec_hint;
119     caps_copy_to_cnode(cn, out_cap_addr, &out_cap, 0, 0, 0);
120     return  SYS_ERR_OK;
121 }
122
123 errval_t irq_table_notify_domains(struct kcb *kcb)
124 {
125     uintptr_t msg[] = { 1 };
126     for (int i = 0; i < NDISPATCH; i++) {
127         if (kcb->irq_dispatch[i].cap.type == ObjType_EndPointLMP) {
128             struct capability *cap = &kcb->irq_dispatch[i].cap;
129             // 1 word message as notification
130             errval_t err = lmp_deliver_payload(cap, NULL, msg, 1, false, false);
131             if (err_is_fail(err)) {
132                 if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
133                     struct dispatcher_shared_generic *disp =
134                         get_dispatcher_shared_generic(cap->u.endpointlmp.listener->disp);
135                     printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
136                             DISP_NAME_LEN, disp->name);
137                 } else {
138                     printk(LOG_ERR, "Unexpected error delivering IRQ\n");
139                 }
140             }
141         }
142         kcb->irq_dispatch[i].cap.type = ObjType_Null;
143     }
144     return SYS_ERR_OK;
145 }
146
147 /**
148  * \brief Send interrupt notification to user-space listener.
149  *
150  * Sends an interrupt notification IDC to a local endpoint that
151  * listens for IRQ notifications.
152  *
153  * \param irq   IRQ# to send in notification.
154  */
155 void send_user_interrupt(int irq)
156 {
157     assert(irq >= 0 && irq < NDISPATCH);
158     struct capability *cap = &irq_dispatch[irq].cap;
159
160     // Return on null cap (unhandled interrupt)
161     if (cap->type == ObjType_Null) {
162         printk(LOG_WARN, "unhandled IRQ %d\n", irq);
163         return;
164     }
165
166     // Otherwise, cap needs to be an endpoint
167     assert(cap->type == ObjType_EndPointLMP);
168     errval_t err = lmp_deliver_notification(cap);
169     if (err_is_fail(err)) {
170         if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
171             struct dispatcher_shared_generic *disp =
172                     get_dispatcher_shared_generic(
173                             cap->u.endpointlmp.listener->disp);
174             printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
175                     DISP_NAME_LEN, disp->name);
176         } else {
177             printk(LOG_ERR, "Unexpected error delivering IRQ\n");
178         }
179     }
180
181 #ifdef SCHEDULER_RR
182     /* XXX: run the handler dispatcher immediately
183      * we shouldn't do this (we should let the scheduler decide), but because
184      * our default scheduler is braindead, this is a quick hack to make sure
185      * that mostly-sane things happen
186      */
187     dispatch(cap->u.endpointlmp.listener);
188 #else
189     dispatch(schedule());
190 #endif
191 }