pci_client: do not set up interrupt routing when pci_register_driver_noirq is called
[barrelfish] / lib / pci / pci_client.c
1 /**
2  * \file
3  * \brief PCI service client-side logic
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 <stdio.h>
16 #include <stdlib.h>
17 #include <barrelfish/barrelfish.h>
18 #include <barrelfish/nameservice_client.h>
19 #include <barrelfish/dispatch.h>
20 #include <barrelfish/inthandler.h>
21 #include <pci/pci.h>
22 #include <pci/pci_client_debug.h>
23 #include <if/pci_defs.h>
24 #include <if/pci_rpcclient_defs.h>
25 #include <if/acpi_rpcclient_defs.h>
26 #include <acpi_client/acpi_client.h>
27 #include <int_route/int_model.h>
28 #include <int_route/int_route_client.h>
29
30 #define INVALID_VECTOR ((uint64_t)-1)
31 #define INVALID_VECTOR_32 ((uint32_t)-1)
32
33 static struct pci_rpc_client *pci_client = NULL;
34
35
36 /*
37  * Parse the int_model=
38  */
39 static struct int_startup_argument int_arg;
40 static bool int_arg_found = false;
41
42 errval_t pci_parse_int_arg(int argc, char ** argv) {
43     errval_t err;
44     for(int i=0; i < argc; i++){
45         err = int_startup_argument_parse(argv[i], &int_arg);
46         if(err_is_ok(err)){
47             int_arg_found = true;
48             return err;
49         }
50     }
51     return SYS_ERR_IRQ_INVALID;
52 }
53
54 errval_t pci_reregister_irq_for_device(uint32_t class, uint32_t subclass, uint32_t prog_if,
55                                        uint32_t vendor, uint32_t device,
56                                        uint32_t bus, uint32_t dev, uint32_t fun,
57                                        interrupt_handler_fn handler,
58                                        void *handler_arg,
59                                        interrupt_handler_fn reloc_handler,
60                                        void *reloc_handler_arg)
61 {
62     uint64_t vector = INVALID_VECTOR;
63     errval_t err, msgerr;
64
65     if (handler != NULL && reloc_handler != NULL) {
66         // register movable interrupt
67         err = inthandler_setup_movable(handler, handler_arg, reloc_handler,
68                 reloc_handler_arg, &vector);
69         if (err_is_fail(err)) {
70             return err;
71         }
72
73         assert(vector != INVALID_VECTOR);
74     } else if (handler != NULL) {
75         // register non-movable interrupt
76         err = inthandler_setup(handler, handler_arg, &vector);
77         if (err_is_fail(err)) {
78             return err;
79         }
80
81         assert(vector != INVALID_VECTOR);
82     }
83
84     err = pci_client->vtbl.
85         reregister_interrupt(pci_client, class, subclass, prog_if, vendor,
86                 device, bus, dev, fun, disp_get_current_core_id(),
87                 vector, &msgerr);
88     if (err_is_fail(err)) {
89         return err;
90     } else if (err_is_fail(msgerr)) {
91         return msgerr;
92     }
93     return SYS_ERR_OK;
94 }
95
96 static errval_t check_src_capability(struct capref irq_src_cap){
97     struct capability irq_src_cap_data;
98     errval_t err;
99     err = debug_cap_identify(irq_src_cap, &irq_src_cap_data);
100     if(err_is_fail(err)){
101         DEBUG_ERR(err, "Could not identify cap?");
102         return err;
103     }
104     if(irq_src_cap_data.type != ObjType_IRQSrc){
105         PCI_CLIENT_DEBUG("First cap argument ist not of type IRQSrc (is=%d)."
106                 "Driver not started by kaluga?\n", irq_src_cap_data.type);
107         return SYS_ERR_IRQ_NOT_IRQ_TYPE;
108     }
109     return SYS_ERR_OK;
110 }
111
112
113 /**
114  * This function does all the interrupt routing setup. It uses the interrupt source
115  * capability passed from kaluga out of the cspace.
116  * It allocates an interrupt destination capability from the monitor.
117  * It sets up a route between these two using the interrupt routing service
118  * It registers the handler passed as an argument as handler for the int destination
119  * capability.
120  * Finally, it instructs the PCI service to activate interrupts for this card.
121  */
122 static errval_t setup_int_routing(int irq_idx, interrupt_handler_fn handler,
123                                          void *handler_arg,
124                                          interrupt_handler_fn reloc_handler,
125                                          void *reloc_handler_arg){
126     errval_t err, msgerr;
127     // We use the first passed vector of the device,
128     // for backward compatibility with function interface.
129     struct capref irq_src_cap;
130     irq_src_cap.cnode = build_cnoderef(cap_argcn, CNODE_TYPE_OTHER);
131     irq_src_cap.slot = irq_idx;
132
133     err = check_src_capability(irq_src_cap);
134     if(err_is_fail(err)){
135         USER_PANIC_ERR(err, "No interrupt capability");
136         return err;
137     }
138
139     // For now, we just use the first vector passed to the driver.
140     uint64_t gsi = INVALID_VECTOR;
141     err = invoke_irqsrc_get_vec_start(irq_src_cap, &gsi);
142     if (err_is_fail(err)) {
143         DEBUG_ERR(err, "Could not lookup GSI vector");
144         return err;
145     }
146     PCI_CLIENT_DEBUG("Got irqsrc cap, gsi: %"PRIu64"\n", gsi);
147
148     // Get irq_dest_cap from monitor
149     struct capref irq_dest_cap;
150     err = alloc_dest_irq_cap(&irq_dest_cap);
151     if(err_is_fail(err)){
152         DEBUG_ERR(err, "Could not allocate dest irq cap");
153         return err;
154     }
155     uint64_t irq_dest_vec = INVALID_VECTOR;
156     err = invoke_irqdest_get_vector(irq_dest_cap, &irq_dest_vec);
157     if (err_is_fail(err)) {
158         DEBUG_ERR(err, "Could not lookup irq vector");
159         return err;
160     }
161     PCI_CLIENT_DEBUG("Got dest cap, vector: %"PRIu64"\n", irq_dest_vec);
162
163
164     err = int_route_client_route(irq_src_cap, irq_dest_cap);
165     if(err_is_fail(err)){
166         DEBUG_ERR(err, "Could not set up route.");
167         return err;
168     } else {
169         PCI_CLIENT_DEBUG("Int route set-up success.\n");
170     }
171
172     if(handler != NULL){
173         err = inthandler_setup_movable_cap(irq_dest_cap, handler, handler_arg,
174                 reloc_handler, reloc_handler_arg);
175         if (err_is_fail(err)) {
176             return err;
177         }
178     }
179
180     // Activate PCI interrupt
181     err = pci_client->vtbl.irq_enable(pci_client, &msgerr);
182     assert(err_is_ok(err));
183     if(err_is_fail(msgerr)){
184         DEBUG_ERR(msgerr, "irq_enable");
185         return msgerr;
186     }
187     return err;
188 }
189
190 errval_t pci_register_driver_movable_irq(pci_driver_init_fn init_func, uint32_t class,
191                                          uint32_t subclass, uint32_t prog_if,
192                                          uint32_t vendor, uint32_t device,
193                                          uint32_t bus, uint32_t dev, uint32_t fun,
194                                          interrupt_handler_fn handler,
195                                          void *handler_arg,
196                                          interrupt_handler_fn reloc_handler,
197                                          void *reloc_handler_arg)
198 {
199     pci_caps_per_bar_t caps_per_bar;
200     uint8_t nbars;
201     errval_t err, msgerr;
202
203     err = pci_client->vtbl.
204         init_pci_device(pci_client, class, subclass, prog_if, vendor,
205                         device, bus, dev, fun, &msgerr,
206                         &nbars, caps_per_bar, caps_per_bar + 1, caps_per_bar + 2,
207                         caps_per_bar + 3, caps_per_bar + 4, caps_per_bar + 5);
208
209     if (err_is_fail(err)) {
210         PCI_CLIENT_DEBUG("init pci device failed.\n");
211         return err;
212     } else if (err_is_fail(msgerr)) {
213         free(caps_per_bar);
214         return msgerr;
215     }
216     assert(nbars > 0); // otherwise we should have received an error!
217
218     // Set-up int routing.
219     PCI_CLIENT_DEBUG("Calling setup_int_routing\n");
220     if (!(handler == NULL)) {
221         err = setup_int_routing(0, handler, handler_arg, reloc_handler, reloc_handler_arg);
222         if(err_is_fail(err)){
223            DEBUG_ERR(err, "Could not set up int routing. Continuing w/o interrupts");
224         }
225     }
226
227     // FIXME: leak
228     struct device_mem *bars = calloc(nbars, sizeof(struct device_mem));
229     assert(bars != NULL);
230
231     // request caps for all bars of device
232     for (int nb = 0; nb < nbars; nb++) {
233         struct device_mem *bar = &bars[nb];
234
235         int ncaps = (caps_per_bar)[nb];
236         if (ncaps != 0) {
237             bar->nr_caps = ncaps;
238             bar->frame_cap = malloc(ncaps * sizeof(struct capref)); // FIXME: leak
239             assert(bar->frame_cap != NULL);
240         }
241
242         for (int nc = 0; nc < ncaps; nc++) {
243             struct capref cap;
244             uint8_t type;
245
246             err = slot_alloc(&cap);
247             assert(err_is_ok(err));
248             err = pci_client->vtbl.get_bar_cap(pci_client, nb, nc, &msgerr, &cap,
249                                            &type, &bar->bar_nr);
250             if (err_is_fail(err) || err_is_fail(msgerr)) {
251                 if (err_is_ok(err)) {
252                     err = msgerr;
253                 }
254                 DEBUG_ERR(err, "requesting cap %d for BAR %d of device", nc, nb);
255                 goto out;
256             }
257
258             if (type == 0) { // Frame cap BAR
259                 bar->frame_cap[nc] = cap;
260                 if (nc == 0) {
261                     struct frame_identity id = { .base = 0, .bytes = 0 };
262                     err = frame_identify(cap, &id);
263                     if (err_is_fail(err)) {
264                         USER_PANIC_ERR(err, "frame identify failed.");
265                     }
266                     bar->paddr = id.base;
267                     bar->bits = log2ceil(id.bytes);
268                     bar->bytes = id.bytes * ncaps;
269                 }
270             } else { // IO BAR
271                 bar->io_cap = cap;
272                 err = cap_copy(cap_io, cap);
273                 if(err_is_fail(err) && err_no(err) != SYS_ERR_SLOT_IN_USE) {
274                     DEBUG_ERR(err, "cap_copy for IO cap");
275                     goto out;
276                 }
277             }
278         }
279     }
280
281     // initialize the device. We have all the caps now
282     PCI_CLIENT_DEBUG("Succesfully done with pci init.\n");
283     init_func(bars, nbars);
284
285     err = SYS_ERR_OK;
286
287  out:
288     return err;
289 }
290
291 errval_t pci_register_driver_irq(pci_driver_init_fn init_func, uint32_t class,
292                                  uint32_t subclass, uint32_t prog_if,
293                                  uint32_t vendor, uint32_t device,
294                                  uint32_t bus, uint32_t dev, uint32_t fun,
295                                  interrupt_handler_fn handler,
296                                  void *handler_arg)
297 {
298      return pci_register_driver_movable_irq(init_func, class, subclass,
299              prog_if, vendor, device, bus, dev, fun, handler, handler_arg,
300              NULL, NULL);
301 }
302
303
304 errval_t pci_register_driver_noirq(pci_driver_init_fn init_func, uint32_t class,
305                                    uint32_t subclass, uint32_t prog_if,
306                                    uint32_t vendor, uint32_t device,
307                                    uint32_t bus, uint32_t dev, uint32_t fun)
308 {
309     return pci_register_driver_irq(init_func, class, subclass, prog_if, vendor,
310                                    device, bus, dev, fun, NULL, NULL);
311 }
312
313 errval_t pci_register_legacy_driver_irq_cap(legacy_driver_init_fn init_func,
314                                         uint16_t iomin, uint16_t iomax, int irq_cap_idx,
315                                         interrupt_handler_fn handler,
316                                         void *handler_arg) {
317     errval_t err, msgerr;
318     struct capref iocap;
319     // Connect to PCI without interrupts
320     err = slot_alloc(&iocap);
321     assert(err_is_ok(err));
322     err = pci_client->vtbl.init_legacy_device(pci_client, iomin, iomax, 0,
323                                               disp_get_core_id(), INVALID_VECTOR_32,
324                                               &msgerr, &iocap);
325     if (err_is_fail(err)) {
326         DEBUG_ERR(err, "pci_client->init_legacy_device()\n");
327         return err;
328     } else if (err_is_fail(msgerr)) {
329         DEBUG_ERR(msgerr, "pci_client->init_legacy_device()\n");
330         return msgerr;
331     }
332
333     /* copy IO cap to default location */
334     err = cap_copy(cap_io, iocap);
335     if (err_is_fail(err) && err_no(err) != SYS_ERR_SLOT_IN_USE) {
336         DEBUG_ERR(err, "failed to copy legacy io cap to default slot\n");
337         return err;
338     }
339
340     err = cap_destroy(iocap);
341     assert(err_is_ok(err));
342
343     // Setup int routing
344     err = setup_int_routing(irq_cap_idx, handler, handler_arg, NULL, NULL);
345     if(err_is_fail(err)){
346        DEBUG_ERR(err, "Could not set up int routing. Continuing w/o interrupts");
347     } else {
348         PCI_CLIENT_DEBUG("setup_int_routing successful.\n");
349     }
350
351     // Run init function
352     init_func();
353
354     return SYS_ERR_OK;
355 }
356
357 errval_t pci_register_legacy_driver_irq(legacy_driver_init_fn init_func,
358                                         uint16_t iomin, uint16_t iomax, int irq,
359                                         interrupt_handler_fn handler,
360                                         void *handler_arg)
361 {
362     errval_t err, msgerr;
363     struct capref iocap;
364
365     debug_printf("WARNING: pci_register_legacy_driver_irq is deprecated."
366                  "Make sure driver is started by Kaluga and use"
367                  "pci_register_legacy_driver_irq_cap.\n");
368
369     uint64_t vector = INVALID_VECTOR;
370     err = inthandler_setup(handler, handler_arg, &vector);
371     if (err_is_fail(err)) {
372         DEBUG_ERR(err, "inthandler_setup()\n");
373         return err;
374     }
375
376     err = slot_alloc(&iocap);
377     assert(err_is_ok(err));
378     err = pci_client->vtbl.init_legacy_device(pci_client, iomin, iomax, irq,
379                                               disp_get_core_id(), vector,
380                                               &msgerr, &iocap);
381     if (err_is_fail(err)) {
382         DEBUG_ERR(err, "pci_client->init_legacy_device()\n");
383         return err;
384     } else if (err_is_fail(msgerr)) {
385         DEBUG_ERR(msgerr, "pci_client->init_legacy_device()\n");
386         return msgerr;
387     }
388
389     /* copy IO cap to default location */
390     err = cap_copy(cap_io, iocap);
391     if (err_is_fail(err) && err_no(err) != SYS_ERR_SLOT_IN_USE) {
392         DEBUG_ERR(err, "failed to copy legacy io cap to default slot\n");
393     }
394
395     err = cap_destroy(iocap);
396     assert(err_is_ok(err));
397
398     /* run init func */
399     init_func();
400
401     return msgerr;
402 }
403
404 errval_t pci_setup_inthandler(interrupt_handler_fn handler, void *handler_arg,
405                                       uint8_t *ret_vector)
406 {
407     errval_t err;
408     uint64_t vector = INVALID_VECTOR;
409     *ret_vector = 0;
410     err = inthandler_setup(handler, handler_arg, &vector);
411     if (err_is_ok(err)) {
412         *ret_vector = vector + 32; // FIXME: HACK
413     }
414     return err;
415 }
416
417 errval_t pci_read_conf_header(uint32_t dword, uint32_t *val)
418 {
419     errval_t err, msgerr;
420     err = pci_client->vtbl.read_conf_header(pci_client, dword, &msgerr, val);
421     return err_is_fail(err) ? err : msgerr;
422 }
423
424 errval_t pci_write_conf_header(uint32_t dword, uint32_t val)
425 {
426     errval_t err, msgerr;
427     err = pci_client->vtbl.write_conf_header(pci_client, dword, val, &msgerr);
428     return err_is_fail(err) ? err : msgerr;
429 }
430
431 errval_t pci_msix_enable_addr(struct pci_address *addr, uint16_t *count)
432 {
433     errval_t err, msgerr;
434     if (addr == NULL) {
435         err = pci_client->vtbl.msix_enable(pci_client, &msgerr, count);
436     } else {
437         err = pci_client->vtbl.msix_enable_addr(pci_client, addr->bus, addr->device,
438                                                 addr->function, &msgerr, count);
439     }
440     return err_is_fail(err) ? err : msgerr;
441 }
442
443 errval_t pci_msix_enable(uint16_t *count)
444 {
445     return pci_msix_enable_addr(NULL, count);
446 }
447
448 errval_t pci_msix_vector_init_addr(struct pci_address *addr, uint16_t idx,
449                                    uint8_t destination, uint8_t vector)
450 {
451     errval_t err, msgerr;
452     if (addr == NULL) {
453         err = pci_client->vtbl.msix_vector_init(pci_client, idx, destination,
454                                                     vector, &msgerr);
455     } else {
456         err = pci_client->vtbl.msix_vector_init_addr(pci_client, addr->bus,
457                                                      addr->device, addr->function,
458                                                      idx, destination,
459                                                      vector, &msgerr);
460     }
461
462     return err_is_fail(err) ? err : msgerr;
463 }
464
465 errval_t pci_msix_vector_init(uint16_t idx, uint8_t destination,
466                               uint8_t vector)
467 {
468     return pci_msix_vector_init_addr(NULL, idx, destination, vector);
469 }
470
471 static void bind_cont(void *st, errval_t err, struct pci_binding *b)
472 {
473     errval_t *reterr = st;
474     if (err_is_ok(err)) {
475         struct pci_rpc_client *r = malloc(sizeof(*r));
476         assert(r != NULL);
477         err = pci_rpc_client_init(r, b);
478         if (err_is_ok(err)) {
479             pci_client = r;
480         } else {
481             free(r);
482         }
483     }
484     *reterr = err;
485 }
486
487 errval_t pci_client_connect(void)
488 {
489     iref_t iref;
490     errval_t err, err2 = SYS_ERR_OK;
491
492     PCI_CLIENT_DEBUG("Connecting to acpi\n");
493     err = connect_to_acpi();
494     if(err_is_fail(err)){
495         return err;
496     }
497     PCI_CLIENT_DEBUG("Connected to ACPI\n");
498
499     /* Connect to the pci server */
500     PCI_CLIENT_DEBUG("Looking up pci iref\n");
501     err = nameservice_blocking_lookup("pci", &iref);
502     if (err_is_fail(err)) {
503         return err;
504     }
505
506     assert(iref != 0);
507
508     PCI_CLIENT_DEBUG("Connecting to pci\n");
509     /* Setup flounder connection with pci server */
510     err = pci_bind(iref, bind_cont, &err2, get_default_waitset(),
511                    IDC_BIND_FLAG_RPC_CAP_TRANSFER);
512     if (err_is_fail(err)) {
513         return err;
514     }
515
516     /* XXX: Wait for connection establishment */
517     while (pci_client == NULL && err2 == SYS_ERR_OK) {
518         messages_wait_and_handle_next();
519     }
520
521     if(err_is_ok(err2)){
522         PCI_CLIENT_DEBUG("PCI connection successful, connecting to int route service\n");
523         err = int_route_client_connect();
524         if(err_is_ok(err)){
525             PCI_CLIENT_DEBUG("Int route service connected.\n");
526         } else {
527             DEBUG_ERR(err, "Could not connect to int route service\n");
528         }
529     }
530     return err2;
531 }