94e450dbfc11e8a9cf80e6a3ca97ae29fcd1c3e7
[barrelfish] / usr / pci / pci_service.c
1  /**
2  * \file
3  * \brief PCI service code
4  *
5  * This file exports the PCI service interface for drivers
6  */
7
8 /*
9  * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
10  * All rights reserved.
11  *
12  * This file is distributed under the terms in the attached LICENSE file.
13  * If you do not find this file, copies can be found by writing to:
14  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19
20 #include <barrelfish/barrelfish.h>
21 #include <barrelfish/nameservice_client.h>
22 #include <barrelfish/sys_debug.h>
23
24 #include <if/pci_defs.h>
25 #include <if/acpi_rpcclient_defs.h>
26
27 #include <acpi_client/acpi_client.h>
28 #include <mm/mm.h>
29 //#include "pci_confspace.h"
30
31 #include "pci.h"
32 #include "pci_debug.h"
33
34 /*****************************************************************
35  * Data types:
36  *****************************************************************/
37
38 /* Per-client state
39  * XXX: this assumes only one driver per client */
40 struct client_state {
41 //    struct device_mem *bar_info;
42     uint8_t initialized;
43     int nr_allocated_bars;
44     uint32_t nr_caps_bar[PCI_NBARS];
45     uint32_t bus;
46     uint32_t dev;
47     uint32_t fun;
48     bool pcie;
49     void *cont_st;
50 };
51
52 /*****************************************************************
53  * Event handlers:
54  *****************************************************************/
55 /*
56     cc->bus = cc->dev = cc->fun = 0;
57 */
58
59 static void init_pci_device_handler(struct pci_binding *b,
60                                     uint32_t class_code, uint32_t sub_class,
61                                     uint32_t prog_if, uint32_t vendor_id,
62                                     uint32_t device_id,
63                                     uint32_t bus, uint32_t dev, uint32_t fun)
64 {
65     struct client_state *cc = (struct client_state *) b->st;
66     errval_t err;
67
68
69     err = device_init(class_code, sub_class, prog_if, vendor_id, device_id,
70                       &bus, &dev, &fun, &(cc->pcie), &(cc->nr_allocated_bars));
71
72     cc->bus = bus;
73     cc->dev = dev;
74     cc->fun = fun;
75
76     for (int i = 0; i < cc->nr_allocated_bars; i++) {
77         cc->nr_caps_bar[i] = pci_get_nr_caps_for_bar(bus, dev, fun, i);
78     }
79
80     if (err_is_fail(err)) {
81         err = b->tx_vtbl.init_pci_device_response(b, NOP_CONT, err, 0,
82                                                   cc->nr_caps_bar[0],
83                                                   cc->nr_caps_bar[1],
84                                                   cc->nr_caps_bar[2],
85                                                   cc->nr_caps_bar[3],
86                                                   cc->nr_caps_bar[4],
87                                                   cc->nr_caps_bar[5]);
88
89     } else {
90         err = b->tx_vtbl.init_pci_device_response(b, NOP_CONT, err,
91                                                   cc->nr_allocated_bars,
92                                                   cc->nr_caps_bar[0],
93                                                   cc->nr_caps_bar[1],
94                                                   cc->nr_caps_bar[2],
95                                                   cc->nr_caps_bar[3],
96                                                   cc->nr_caps_bar[4],
97                                                   cc->nr_caps_bar[5]);
98
99     }
100     assert(err_is_ok(err));
101 }
102
103 static void irq_enable_handler(struct pci_binding *b)
104 {
105     struct client_state *cc = (struct client_state *) b->st;
106     if(!cc->initialized){
107         b->tx_vtbl.irq_enable_response(b, NOP_CONT, PCI_ERR_DEVICE_NOT_INIT);
108         return;
109     }
110
111     pci_enable_interrupt_for_device(cc->bus, cc->dev, cc->fun, cc->pcie);
112     b->tx_vtbl.irq_enable_response(b, NOP_CONT, SYS_ERR_OK);
113 }
114
115 static void init_legacy_device_handler(struct pci_binding *b,
116                                        uint16_t iomin, uint16_t iomax,
117                                        uint8_t irq, coreid_t coreid,
118                                        uint32_t vector)
119 {
120     struct capref iocap = NULL_CAP;
121     errval_t e = SYS_ERR_OK;
122
123     PCI_DEBUG("pci: init_legacy_device_handler: called. irq:%"PRIu8", coreid:%"PRIuCOREID", vector:%"PRIu32"\n", irq, coreid, vector);
124
125     /* TODO: make sure nobody else has claimed iomin-iomax range */
126
127     /* construct iocap for this region */
128     if (iomin != 0 || iomax != 0) {
129         e = slot_alloc(&iocap);
130         if (err_is_fail(e)) {
131             e = err_push(e, LIB_ERR_SLOT_ALLOC);
132             goto reply;
133         }
134
135         e = cap_mint(iocap, cap_io, iomin, iomax);
136         if (err_is_fail(e)) {
137             e = err_push(e, PCI_ERR_MINT_IOCAP);
138             goto reply;
139         }
140     }
141
142     /* determine IOAPIC INTI for given GSI and map to core */
143     if (vector != (uint32_t)-1) {
144
145         struct acpi_rpc_client* cl = get_acpi_rpc_client();
146         errval_t ret_error;
147         e = cl->vtbl.enable_and_route_interrupt(cl, irq, coreid, vector, &ret_error);
148         assert(err_is_ok(e));
149         if (err_is_fail(ret_error)) {
150             DEBUG_ERR(e, "failed to route interrupt %d -> %d\n", irq, vector);
151             e = err_push(e, PCI_ERR_ROUTING_IRQ);
152             goto reply;
153         }
154     }
155
156     /* send reply */
157 reply:
158     e = b->tx_vtbl.init_legacy_device_response(b, NOP_CONT, e,
159                                                err_is_ok(e) ? iocap : NULL_CAP);
160     if (err_is_fail(e)) {
161         DEBUG_ERR(e, "failed to send reply");
162     }
163
164     PCI_DEBUG("pci: init_legacy_device_handler: terminated.\n");
165 }
166
167 static void get_bar_cap_response_resend(void *arg);
168
169 static void get_bar_cap_response_cont(struct pci_binding *b, errval_t err,
170                                   struct capref cap, uint8_t type, uint8_t bar_nr)
171 {
172     errval_t e;
173     e = b->tx_vtbl.get_bar_cap_response(b, NOP_CONT, err, cap, type, bar_nr);
174     if(err_is_fail(e)) {
175         if(err_no(e) == FLOUNDER_ERR_TX_BUSY) {
176             struct client_state *st = b->st;
177             struct pci_get_bar_cap_response__tx_args *me = malloc(sizeof(*me));
178             assert(me != NULL);
179             me->err = err;
180             me->cap = cap;
181             me->type = type;
182             me->bar_nr = bar_nr;
183             st->cont_st = me;
184
185             e = b->register_send(b, get_default_waitset(),
186                                  MKCONT(get_bar_cap_response_resend, b));
187             assert(err_is_ok(e));
188         } else {
189             USER_PANIC_ERR(e, "get_bar_cap_response");
190         }
191     }
192 }
193
194 static void get_bar_cap_response_resend(void *arg)
195 {
196     struct pci_binding *b = arg;
197     struct client_state *st = b->st;
198     struct pci_get_bar_cap_response__tx_args *a = st->cont_st;
199     get_bar_cap_response_cont(b, a->err, a->cap, a->type, a->bar_nr);
200     free(a);
201 }
202
203 static void get_irq_cap_handler(struct pci_binding *b, uint16_t idx){
204     // TODO: This method works only for non legacy devices
205     // and supports only one interrupt per device at the moment
206     assert(idx == 0);
207     errval_t err;
208     struct capref cap;
209     slot_alloc(&cap);
210
211     struct client_state *st = b->st;
212
213     // TODO: This should be part of the routing step
214     int irq = pci_setup_interrupt(st->bus, st->dev, st->fun);
215     PCI_DEBUG("pci: init_device_handler_irq: init interrupt.\n");
216
217     pci_enable_interrupt_for_device(st->bus, st->dev, st->fun, st->pcie);
218
219     PCI_DEBUG("pci: Interrupt enabled.\n");
220
221     err = sys_debug_create_irq_src_cap(cap, irq);
222     if (err_is_fail(err)) {
223         USER_PANIC_ERR(err, "create irq src cap failed.");
224     }
225
226     err = b->tx_vtbl.get_irq_cap_response(b, NOP_CONT, err, cap);
227     if (err_is_fail(err)) {
228         USER_PANIC_ERR(err, "cap response failed.");
229     }
230 }
231
232 static void get_bar_cap_handler(struct pci_binding *b, uint32_t idx,
233                             uint32_t cap_nr)
234 {
235     struct client_state *st = b->st;
236     assert(st != NULL);
237     errval_t e;
238
239     if (idx >= st->nr_allocated_bars) {
240         e = b->tx_vtbl.get_bar_cap_response(b, NOP_CONT, PCI_ERR_WRONG_INDEX,
241                                         NULL_CAP, 0, 0);
242         assert(err_is_ok(e));
243     } else {
244         struct capref cap = pci_get_bar_cap_for_device(st->bus, st->dev,
245                                                    st->fun, idx, cap_nr);
246         uint8_t type = pci_get_bar_cap_type_for_device(st->bus, st->dev,
247                                                    st->fun, idx);
248         uint8_t bar_nr = pci_get_bar_nr_for_index(st->bus, st->dev,
249                                                    st->fun, idx);
250 /*
251 XXX: I/O-Cap??
252         uint8_t type = st->bar_info[idx].type;
253         struct capref cap = NULL_CAP;
254
255         if(type == 0) {
256             cap = st->bar_info[idx].frame_cap;
257         } else {
258             cap = st->bar_info[idx].io_cap;
259         }
260 */
261
262         get_bar_cap_response_cont(b, SYS_ERR_OK, cap, type, bar_nr);
263     }
264 }
265 /*
266 static void get_vbe_bios_cap(struct pci_binding *b)
267 {
268     errval_t err;
269     err = b->tx_vtbl.get_vbe_bios_cap_response(b, NOP_CONT, SYS_ERR_OK, biosmem,
270                                                1UL << BIOS_BITS);
271     assert(err_is_ok(err));
272 }*/
273
274 static void read_conf_header_handler(struct pci_binding *b, uint32_t dword)
275 {
276
277     struct client_state *cc = (struct client_state *) b->st;
278     struct pci_address addr = {
279         .bus= cc->bus,
280         .device=cc->dev,
281         .function=cc->fun,
282     };
283     PCI_DEBUG("Read config header from %u:%u:%u\n",addr.bus, addr.device, addr.function);
284     uint32_t val = pci_read_conf_header(&addr, dword);
285
286     errval_t err;
287     err = b->tx_vtbl.read_conf_header_response(b, NOP_CONT, SYS_ERR_OK, val);
288     assert(err_is_ok(err));
289 }
290
291 static void reregister_interrupt_handler(struct pci_binding *b,
292                                     uint32_t class_code, uint32_t sub_class,
293                                     uint32_t prog_if, uint32_t vendor_id,
294                                     uint32_t device_id,
295                                     uint32_t bus, uint32_t dev, uint32_t fun,
296                                     coreid_t coreid, uint32_t vector)
297 {
298     errval_t err;
299     err = device_reregister_interrupt(coreid, vector,
300                       class_code, sub_class, prog_if, vendor_id, device_id,
301                       &bus, &dev, &fun);
302     err = b->tx_vtbl.reregister_interrupt_response(b, NOP_CONT, err);
303     assert(err_is_ok(err));
304 }
305
306 static void write_conf_header_handler(struct pci_binding *b, uint32_t dword, uint32_t val)
307 {
308     struct client_state *cc = (struct client_state *) b->st;
309     struct pci_address addr = {
310         .bus= cc->bus,
311         .device=cc->dev,
312         .function=cc->fun,
313     };
314     PCI_DEBUG("Write config header from %u:%u:%u\n",addr.bus, addr.device, addr.function);
315     pci_write_conf_header(&addr, dword, val);
316
317     errval_t err;
318     err = b->tx_vtbl.write_conf_header_response(b, NOP_CONT, SYS_ERR_OK);
319     assert(err_is_ok(err));
320 }
321
322 static void msix_enable_addr_handler(struct pci_binding *b, uint8_t bus,
323                                       uint8_t dev, uint8_t fun)
324 {
325     struct client_state *cc = (struct client_state *) b->st;
326     struct pci_address addr;
327
328     /* XXX: find another way to do this */
329
330     if (bus == cc->bus && dev == cc->dev) {
331         addr.bus= bus;
332         addr.device=dev;
333         addr.function=fun;
334     } else {
335         addr.bus= cc->bus;
336         addr.device=cc->dev;
337         addr.function=fun;
338     }
339
340     errval_t err;
341     uint16_t count;
342
343     debug_printf("enabling MSI-X for device (%u, %u, %u)\n", addr.bus,
344                  addr.device, addr.function);
345
346     err = pci_msix_enable(&addr, &count);
347     err = b->tx_vtbl.msix_enable_response(b, NOP_CONT, err, count);
348     assert(err_is_ok(err));
349 }
350
351 static void msix_enable_handler(struct pci_binding *b)
352 {
353     struct client_state *cc = (struct client_state *) b->st;
354     msix_enable_addr_handler(b, cc->bus, cc->dev, cc->fun);
355 }
356
357 static void msix_vector_init_addr_handler(struct pci_binding *b, uint8_t bus,
358                                           uint8_t dev, uint8_t fun, uint16_t idx,
359                                           uint8_t destination, uint8_t vector)
360 {
361     struct client_state *cc = (struct client_state *) b->st;
362     struct pci_address addr;
363
364     /* XXX: find another way to do this */
365
366     if (bus == cc->bus && dev == cc->dev) {
367         addr.bus= bus;
368         addr.device=dev;
369         addr.function=fun;
370     } else {
371         addr.bus= cc->bus;
372         addr.device=cc->dev;
373         addr.function=fun;
374     }
375
376     debug_printf("initialize MSI-X vector for device (%u, %u, %u)\n", addr.bus,
377                      addr.device, addr.function);
378
379     errval_t err;
380
381     err = pci_msix_vector_init(&addr, idx, destination, vector);
382     err = b->tx_vtbl.msix_vector_init_response(b, NOP_CONT, err);
383     assert(err_is_ok(err));
384 }
385
386 static void msix_vector_init_handler(struct pci_binding *b, uint16_t idx,
387                                      uint8_t destination, uint8_t vector)
388 {
389     struct client_state *cc = (struct client_state *) b->st;
390
391     msix_vector_init_addr_handler(b, cc->bus, cc->dev, cc->fun, idx, destination,
392                                   vector);
393 }
394
395 struct pci_rx_vtbl pci_rx_vtbl = {
396     .init_pci_device_call = init_pci_device_handler,
397     .init_legacy_device_call = init_legacy_device_handler,
398     .get_bar_cap_call = get_bar_cap_handler,
399     .get_irq_cap_call = get_irq_cap_handler,
400     .reregister_interrupt_call = reregister_interrupt_handler,
401     //.get_vbe_bios_cap_call = get_vbe_bios_cap,
402     .read_conf_header_call = read_conf_header_handler,
403     .write_conf_header_call = write_conf_header_handler,
404     .irq_enable_call = irq_enable_handler,
405
406     .msix_enable_call = msix_enable_handler,
407     .msix_enable_addr_call = msix_enable_addr_handler,
408     .msix_vector_init_call = msix_vector_init_handler,
409     .msix_vector_init_addr_call = msix_vector_init_addr_handler,
410 };
411
412 static void export_callback(void *st, errval_t err, iref_t iref)
413 {
414     assert(err_is_ok(err));
415
416     err = nameservice_register("pci", iref);
417     if (err_is_fail(err)) {
418         USER_PANIC_ERR(err, "nameservice_register failed");
419     }
420 }
421
422 static errval_t connect_callback(void *cst, struct pci_binding *b)
423 {
424     struct client_state *st = malloc(sizeof(struct client_state));
425     assert(st != NULL);
426
427     b->rx_vtbl = pci_rx_vtbl;
428     b->st = st;
429 //    st->bar_info = 0;
430     st->nr_allocated_bars = 0;
431     for (int i = 0; i < PCI_NBARS; i++) {
432         st->nr_caps_bar[i] = 0;
433     }
434
435     return SYS_ERR_OK;
436 }
437
438 /*****************************************************************
439  * Boots up the PCI server:
440  *****************************************************************/
441
442 void pci_init(void)
443 {
444     PCI_DEBUG("pci: pci_init: called\n");
445
446     PCI_DEBUG("pci: pci_init: launch listening\n");
447     errval_t r = pci_export(NULL, export_callback, connect_callback,
448                             get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
449     assert(err_is_ok(r));
450
451     PCI_DEBUG("pci: pci_init: terminated\n");
452 }