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