IRQ: added irq source capability and make the inthandler setup use it
[barrelfish] / lib / barrelfish / inthandler.c
1 /**
2  * \file User-level interrupt handler support
3  * \brief
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/inthandler.h>
17 #include <if/monitor_blocking_rpcclient_defs.h>
18
19 struct waitset *barrelfish_interrupt_waitset = NULL;
20
21 /* allocate inrq */
22 static errval_t arm_allocirq(struct capref ep, uint32_t irq)
23 {
24     errval_t err, msgerr;
25
26     struct monitor_blocking_rpc_client *r = get_monitor_blocking_rpc_client();
27     err = r->vtbl.arm_irq_handle(r, ep, irq, &msgerr);
28     if (err_is_fail(err)){
29         return err;
30     } else {
31         return msgerr;
32     }
33 }
34
35
36 /**
37  * Get a new irq destination capability for the current core using the monitor.
38  */
39 errval_t alloc_dest_irq_cap(struct capref *retcap)
40 {
41     errval_t err, msgerr;
42
43     struct monitor_blocking_rpc_client *r = get_monitor_blocking_rpc_client();
44     err = r->vtbl.get_irq_dest_cap(r, retcap, &msgerr);
45     if (err_is_fail(err)){
46         return err;
47     } else {
48         return msgerr;
49     }
50 }
51
52
53
54 struct interrupt_handler_state {
55     struct lmp_endpoint *idcep;
56     interrupt_handler_fn handler;
57     void *handler_arg;
58     interrupt_handler_fn reloc_handler;
59     void *reloc_handler_arg;
60 };
61
62 static void generic_interrupt_handler(void *arg)
63 {
64     struct interrupt_handler_state *state = arg;
65     errval_t err;
66
67     // consume message
68     struct lmp_recv_msg buf = LMP_RECV_MSG_INIT;
69     err = lmp_endpoint_recv(state->idcep, &buf.buf, NULL);
70     assert(err_is_ok(err));
71
72     if (buf.buf.msglen == 1 && buf.words[0] == 1) {
73         // domain moved notification
74         debug_printf("got moved, need to reregister for interrupt\n");
75         if (!state->reloc_handler) {
76             debug_printf("no relocation handler registered, expect badness!\n");
77             return;
78         }
79         // run relocation handler
80         state->reloc_handler(state->reloc_handler_arg);
81     } else {
82         // run real handler
83         //if (init_complete) {
84         state->handler(state->handler_arg);
85     }
86     // re-register
87     struct event_closure cl = {
88         .handler = generic_interrupt_handler,
89         .arg = arg,
90     };
91     err = lmp_endpoint_register(state->idcep, barrelfish_interrupt_waitset, cl);
92     assert(err_is_ok(err));
93 }
94
95
96 /**
97  * \brief Setup an interrupt handler function to receive device interrupts
98  *        on the ARM platform
99  *
100  * \param handler Handler function
101  * \param handler_arg Argument passed to #handler
102  * \param irq the IRQ number to activate
103  */
104 errval_t inthandler_setup_arm(interrupt_handler_fn handler, void *handler_arg,
105         uint32_t irq)
106 {
107     errval_t err;
108
109     if(barrelfish_interrupt_waitset == NULL) {
110         barrelfish_interrupt_waitset = get_default_waitset();
111     }
112
113     /* alloc state */
114     struct interrupt_handler_state *state;
115     state = malloc(sizeof(struct interrupt_handler_state));
116     assert(state != NULL);
117
118     state->handler = handler;
119     state->handler_arg = handler_arg;
120
121     /* create endpoint to handle interrupts */
122     struct capref epcap;
123
124     // use minimum-sized endpoint, because we don't need to buffer >1 interrupt
125     err = endpoint_create(LMP_RECV_LENGTH, &epcap, &state->idcep);
126     if (err_is_fail(err)) {
127         free(state);
128         return err_push(err, LIB_ERR_ENDPOINT_CREATE);
129     }
130
131     // allocate a local interrupt vector for this endpoint
132     err = arm_allocirq(epcap, irq);
133     if (err_is_fail(err)) {
134         return err;
135     }
136
137     // register to receive on this endpoint
138     struct event_closure cl = {
139         .handler = generic_interrupt_handler,
140         .arg = state,
141     };
142     err = lmp_endpoint_register(state->idcep, barrelfish_interrupt_waitset, cl);
143     if (err_is_fail(err)) {
144         lmp_endpoint_free(state->idcep);
145         // TODO: release vector
146         free(state);
147         return err_push(err, LIB_ERR_LMP_ENDPOINT_REGISTER);
148     }
149
150     return SYS_ERR_OK;
151 }
152
153 /**
154  * \brief Setup an interrupt handler function to receive device interrupts targeted at dest_cap
155  *
156  * \param dest_cap Capability to an interrupt line that targets the last level controller (such as local APIC)
157  * \param handler Handler function
158  * \param handler_arg Argument passed to #handler
159  */
160 errval_t inthandler_setup_movable_cap(struct capref dest_cap, interrupt_handler_fn handler, void *handler_arg,
161                                   interrupt_handler_fn reloc_handler,
162                                   void *reloc_handler_arg)
163 {
164     errval_t err;
165
166     if(barrelfish_interrupt_waitset == NULL) {
167         barrelfish_interrupt_waitset = get_default_waitset();
168     }
169
170     /* alloc state */
171     struct interrupt_handler_state *state;
172     state = malloc(sizeof(struct interrupt_handler_state));
173     assert(state != NULL);
174
175     state->handler = handler;
176     state->handler_arg = handler_arg;
177     state->reloc_handler = reloc_handler;
178     state->reloc_handler_arg = reloc_handler_arg;
179
180     /* create endpoint to handle interrupts */
181     struct capref epcap;
182
183     // use minimum-sized endpoint, because we don't need to buffer >1 interrupt
184     err = endpoint_create(LMP_RECV_LENGTH, &epcap, &state->idcep);
185     if (err_is_fail(err)) {
186         free(state);
187         return err_push(err, LIB_ERR_ENDPOINT_CREATE);
188     }
189
190     // register to receive on this endpoint
191     struct event_closure cl = {
192         .handler = generic_interrupt_handler,
193         .arg = state,
194     };
195     err = lmp_endpoint_register(state->idcep, barrelfish_interrupt_waitset, cl);
196     if (err_is_fail(err)) {
197         lmp_endpoint_free(state->idcep);
198         // TODO: release vector
199         free(state);
200         return err_push(err, LIB_ERR_LMP_ENDPOINT_REGISTER);
201     }
202
203     // Connect dest_cap with endpoint
204     invoke_irqvector_connect(dest_cap, epcap);
205
206
207     return SYS_ERR_OK;
208 }
209
210 /**
211  * \brief Deprecated. inthandler_setup_moveable_cap Setup an interrupt handler function to receive device interrupts.
212  *
213  * \param handler Handler function
214  * \param handler_arg Argument passed to #handler
215  * \param ret_vector On success, returns interrupt vector with which
216  *                   handler is associated
217  */
218 errval_t inthandler_setup_movable(interrupt_handler_fn handler, void *handler_arg,
219                                   interrupt_handler_fn reloc_handler,
220                                   void *reloc_handler_arg,
221                                   uint32_t *ret_vector)
222 {
223     errval_t err;
224
225     if(barrelfish_interrupt_waitset == NULL) {
226         barrelfish_interrupt_waitset = get_default_waitset();
227     }
228
229     /* alloc state */
230     struct interrupt_handler_state *state;
231     state = malloc(sizeof(struct interrupt_handler_state));
232     assert(state != NULL);
233
234     state->handler = handler;
235     state->handler_arg = handler_arg;
236     state->reloc_handler = reloc_handler;
237     state->reloc_handler_arg = reloc_handler_arg;
238
239     // Get irq_dest_cap from monitor
240     struct capref irq_dest_cap;
241     err = alloc_dest_irq_cap(&irq_dest_cap);
242     if(err_is_fail(err)){
243         DEBUG_ERR(err, "Could not allocate dest irq cap");
244         return err;
245     }
246
247     // create endpoint to handle interrupts
248     struct capref epcap;
249
250     // use minimum-sized endpoint, because we don't need to buffer >1 interrupt
251     err = endpoint_create(LMP_RECV_LENGTH, &epcap, &state->idcep);
252     if (err_is_fail(err)) {
253         free(state);
254         return err_push(err, LIB_ERR_ENDPOINT_CREATE);
255     }
256
257     err = invoke_irqvector_connect(irq_dest_cap, epcap);
258     if (err_is_fail(err)) {
259         DEBUG_ERR(err, "Could not connect irq_cap and endpoint");
260         return err;
261     }
262
263     err = invoke_irqvector_get_vector(irq_dest_cap, ret_vector);
264     if (err_is_fail(err)) {
265         DEBUG_ERR(err, "Could not lookup irq vector");
266         return err;
267     }
268
269     // register to receive on this endpoint
270     struct event_closure cl = {
271         .handler = generic_interrupt_handler,
272         .arg = state,
273     };
274     err = lmp_endpoint_register(state->idcep, barrelfish_interrupt_waitset, cl);
275     if (err_is_fail(err)) {
276         lmp_endpoint_free(state->idcep);
277         // TODO: release vector
278         free(state);
279         return err_push(err, LIB_ERR_LMP_ENDPOINT_REGISTER);
280     }
281
282     return SYS_ERR_OK;
283 }
284
285 errval_t inthandler_setup(interrupt_handler_fn handler, void *handler_arg,
286                           uint32_t *ret_vector)
287 {
288
289     return inthandler_setup_movable(handler, handler_arg, NULL, NULL, ret_vector);
290 }