a4f6a7d96a87896bd2d35dcd2d4c165fe5261df8
[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 <acpi_client/acpi_client.h>
26 #include <mm/mm.h>
27 //#include "pci_confspace.h"
28
29 #include "pci.h"
30 #include "pci_debug.h"
31
32 /*****************************************************************
33  * Data types:
34  *****************************************************************/
35
36 /* Per-client state
37  * XXX: this assumes only one driver per client */
38 struct client_state {
39 //    struct device_mem *bar_info;
40     int nr_allocated_bars;
41     uint32_t nr_caps_bar[PCI_NBARS];
42     uint32_t bus;
43     uint32_t dev;
44     uint32_t fun;
45     void *cont_st;
46 };
47
48 /*****************************************************************
49  * Event handlers:
50  *****************************************************************/
51 /*
52     cc->bus = cc->dev = cc->fun = 0;
53 */
54
55 static void init_pci_device_handler(struct pci_binding *b,
56                                     uint32_t class_code, uint32_t sub_class,
57                                     uint32_t prog_if, uint32_t vendor_id,
58                                     uint32_t device_id,
59                                     uint32_t bus, uint32_t dev, uint32_t fun)
60 {
61     struct client_state *cc = (struct client_state *) b->st;
62     errval_t err;
63
64
65     err = device_init(class_code, sub_class, prog_if, vendor_id, device_id,
66                       &bus, &dev, &fun, &(cc->nr_allocated_bars));
67
68     cc->bus = bus;
69     cc->dev = dev;
70     cc->fun = fun;
71
72     for (int i = 0; i < cc->nr_allocated_bars; i++) {
73         cc->nr_caps_bar[i] = pci_get_nr_caps_for_bar(bus, dev, fun, i);
74     }
75
76     if (err_is_fail(err)) {
77         err = b->tx_vtbl.init_pci_device_response(b, NOP_CONT, err, 0,
78                                                   cc->nr_caps_bar);
79
80     } else {
81         err = b->tx_vtbl.init_pci_device_response(b, NOP_CONT, err,
82                                                   cc->nr_allocated_bars,
83                                                   cc->nr_caps_bar);
84     }
85     assert(err_is_ok(err));
86 }
87
88 static void init_legacy_device_handler(struct pci_binding *b,
89                                        uint16_t iomin, uint16_t iomax,
90                                        uint8_t irq, coreid_t coreid,
91                                        uint32_t vector)
92 {
93     struct capref iocap = NULL_CAP;
94     errval_t e = SYS_ERR_OK;
95
96     PCI_DEBUG("pci: init_legacy_device_handler: called.\n");
97
98     /* TODO: make sure nobody else has claimed iomin-iomax range */
99
100     /* construct iocap for this region */
101     if (iomin != 0 || iomax != 0) {
102         e = slot_alloc(&iocap);
103         if (err_is_fail(e)) {
104             e = err_push(e, LIB_ERR_SLOT_ALLOC);
105             goto reply;
106         }
107
108         e = cap_mint(iocap, cap_io, iomin, iomax);
109         if (err_is_fail(e)) {
110             e = err_push(e, PCI_ERR_MINT_IOCAP);
111             goto reply;
112         }
113     }
114
115     /* determine IOAPIC INTI for given GSI and map to core */
116     if (vector != (uint32_t)-1) {
117
118         struct acpi_rpc_client* cl = get_acpi_rpc_client();
119         errval_t ret_error;
120         e = cl->vtbl.enable_and_route_interrupt(cl, irq, coreid, vector, &ret_error);
121         assert(err_is_ok(e));
122         if (err_is_fail(ret_error)) {
123             DEBUG_ERR(e, "failed to route interrupt %d -> %d\n", irq, vector);
124             e = err_push(e, PCI_ERR_ROUTING_IRQ);
125             goto reply;
126         }
127     }
128
129     /* send reply */
130 reply:
131     e = b->tx_vtbl.init_legacy_device_response(b, NOP_CONT, e,
132                                                err_is_ok(e) ? iocap : NULL_CAP);
133     if (err_is_fail(e)) {
134         DEBUG_ERR(e, "failed to send reply");
135     }
136
137     PCI_DEBUG("pci: init_legacy_device_handler: terminated.\n");
138 }
139
140 static void get_bar_cap_response_resend(void *arg);
141
142 static void get_bar_cap_response_cont(struct pci_binding *b, errval_t err,
143                                   struct capref cap, uint8_t type, uint8_t bar_nr)
144 {
145     errval_t e;
146     e = b->tx_vtbl.get_bar_cap_response(b, NOP_CONT, err, cap, type, bar_nr);
147     if(err_is_fail(e)) {
148         if(err_no(e) == FLOUNDER_ERR_TX_BUSY) {
149             struct client_state *st = b->st;
150             struct pci_get_bar_cap_response__args *me = malloc(sizeof(*me));
151             assert(me != NULL);
152             me->err = err;
153             me->cap = cap;
154             me->type = type;
155             me->bar_nr = bar_nr;
156             st->cont_st = me;
157
158             e = b->register_send(b, get_default_waitset(),
159                                  MKCONT(get_bar_cap_response_resend, b));
160             assert(err_is_ok(e));
161         } else {
162             USER_PANIC_ERR(e, "get_bar_cap_response");
163         }
164     }
165 }
166
167 static void get_bar_cap_response_resend(void *arg)
168 {
169     struct pci_binding *b = arg;
170     struct client_state *st = b->st;
171     struct pci_get_bar_cap_response__args *a = st->cont_st;
172     get_bar_cap_response_cont(b, a->err, a->cap, a->type, a->bar_nr);
173     free(a);
174 }
175
176 static void get_bar_cap_handler(struct pci_binding *b, uint32_t idx,
177                             uint32_t cap_nr)
178 {
179     struct client_state *st = b->st;
180     assert(st != NULL);
181     errval_t e;
182
183     if (idx >= st->nr_allocated_bars) {
184         e = b->tx_vtbl.get_bar_cap_response(b, NOP_CONT, PCI_ERR_WRONG_INDEX,
185                                         NULL_CAP, 0, 0);
186         assert(err_is_ok(e));
187     } else {
188         struct capref cap = pci_get_bar_cap_for_device(st->bus, st->dev,
189                                                    st->fun, idx, cap_nr);
190         uint8_t type = pci_get_bar_cap_type_for_device(st->bus, st->dev,
191                                                    st->fun, idx);
192         uint8_t bar_nr = pci_get_bar_nr_for_index(st->bus, st->dev,
193                                                    st->fun, idx);
194 /*
195 XXX: I/O-Cap??
196         uint8_t type = st->bar_info[idx].type;
197         struct capref cap = NULL_CAP;
198
199         if(type == 0) {
200             cap = st->bar_info[idx].frame_cap;
201         } else {
202             cap = st->bar_info[idx].io_cap;
203         }
204 */
205
206         get_bar_cap_response_cont(b, SYS_ERR_OK, cap, type, bar_nr);
207     }
208 }
209 /*
210 static void get_vbe_bios_cap(struct pci_binding *b)
211 {
212     errval_t err;
213     err = b->tx_vtbl.get_vbe_bios_cap_response(b, NOP_CONT, SYS_ERR_OK, biosmem,
214                                                1UL << BIOS_BITS);
215     assert(err_is_ok(err));
216 }*/
217
218 static void read_conf_header_handler(struct pci_binding *b, uint32_t dword)
219 {
220
221     struct client_state *cc = (struct client_state *) b->st;
222     struct pci_address addr = {
223         .bus= cc->bus,
224         .device=cc->dev,
225         .function=cc->fun,
226     };
227     PCI_DEBUG("Read config header from %u:%u:%u\n",addr.bus, addr.device, addr.function);
228     uint32_t val = pci_read_conf_header(&addr, dword);
229
230     errval_t err;
231     err = b->tx_vtbl.read_conf_header_response(b, NOP_CONT, SYS_ERR_OK, val);
232     assert(err_is_ok(err));
233 }
234
235 static void reregister_interrupt_handler(struct pci_binding *b,
236                                     uint32_t class_code, uint32_t sub_class,
237                                     uint32_t prog_if, uint32_t vendor_id,
238                                     uint32_t device_id,
239                                     uint32_t bus, uint32_t dev, uint32_t fun,
240                                     coreid_t coreid, uint32_t vector)
241 {
242     errval_t err;
243     err = device_reregister_interrupt(coreid, vector,
244                       class_code, sub_class, prog_if, vendor_id, device_id,
245                       &bus, &dev, &fun);
246     err = b->tx_vtbl.reregister_interrupt_response(b, NOP_CONT, err);
247     assert(err_is_ok(err));
248 }
249
250 static void write_conf_header_handler(struct pci_binding *b, uint32_t dword, uint32_t val)
251 {
252     struct client_state *cc = (struct client_state *) b->st;
253     struct pci_address addr = {
254         .bus= cc->bus,
255         .device=cc->dev,
256         .function=cc->fun,
257     };
258     PCI_DEBUG("Write config header from %u:%u:%u\n",addr.bus, addr.device, addr.function);
259     pci_write_conf_header(&addr, dword, val);
260
261     errval_t err;
262     err = b->tx_vtbl.write_conf_header_response(b, NOP_CONT, SYS_ERR_OK);
263     assert(err_is_ok(err));
264 }
265
266 static void msix_enable_addr_handler(struct pci_binding *b, uint8_t bus,
267                                       uint8_t dev, uint8_t fun)
268 {
269     struct client_state *cc = (struct client_state *) b->st;
270     struct pci_address addr;
271
272     /* XXX: find another way to do this */
273
274     if (bus == cc->bus && dev == cc->dev) {
275         addr.bus= bus;
276         addr.device=dev;
277         addr.function=fun;
278     } else {
279         addr.bus= cc->bus;
280         addr.device=cc->dev;
281         addr.function=fun;
282     }
283
284     errval_t err;
285     uint16_t count;
286
287     debug_printf("enabling MSI-X for device (%u, %u, %u)\n", addr.bus,
288                  addr.device, addr.function);
289
290     err = pci_msix_enable(&addr, &count);
291     err = b->tx_vtbl.msix_enable_response(b, NOP_CONT, err, count);
292     assert(err_is_ok(err));
293 }
294
295 static void msix_enable_handler(struct pci_binding *b)
296 {
297     struct client_state *cc = (struct client_state *) b->st;
298     msix_enable_addr_handler(b, cc->bus, cc->dev, cc->fun);
299 }
300
301 static void msix_vector_init_addr_handler(struct pci_binding *b, uint8_t bus,
302                                           uint8_t dev, uint8_t fun, uint16_t idx,
303                                           uint8_t destination, uint8_t vector)
304 {
305     struct client_state *cc = (struct client_state *) b->st;
306     struct pci_address addr;
307
308     /* XXX: find another way to do this */
309
310     if (bus == cc->bus && dev == cc->dev) {
311         addr.bus= bus;
312         addr.device=dev;
313         addr.function=fun;
314     } else {
315         addr.bus= cc->bus;
316         addr.device=cc->dev;
317         addr.function=fun;
318     }
319
320     debug_printf("initialize MSI-X vector for device (%u, %u, %u)\n", addr.bus,
321                      addr.device, addr.function);
322
323     errval_t err;
324
325     err = pci_msix_vector_init(&addr, idx, destination, vector);
326     err = b->tx_vtbl.msix_vector_init_response(b, NOP_CONT, err);
327     assert(err_is_ok(err));
328 }
329
330 static void msix_vector_init_handler(struct pci_binding *b, uint16_t idx,
331                                      uint8_t destination, uint8_t vector)
332 {
333     struct client_state *cc = (struct client_state *) b->st;
334
335     msix_vector_init_addr_handler(b, cc->bus, cc->dev, cc->fun, idx, destination,
336                                   vector);
337 }
338
339 struct pci_rx_vtbl pci_rx_vtbl = {
340     .init_pci_device_call = init_pci_device_handler,
341     .init_legacy_device_call = init_legacy_device_handler,
342     .get_bar_cap_call = get_bar_cap_handler,
343     .reregister_interrupt_call = reregister_interrupt_handler,
344     //.get_vbe_bios_cap_call = get_vbe_bios_cap,
345     .read_conf_header_call = read_conf_header_handler,
346     .write_conf_header_call = write_conf_header_handler,
347
348     .msix_enable_call = msix_enable_handler,
349     .msix_enable_addr_call = msix_enable_addr_handler,
350     .msix_vector_init_call = msix_vector_init_handler,
351     .msix_vector_init_addr_call = msix_vector_init_addr_handler,
352 };
353
354 static void export_callback(void *st, errval_t err, iref_t iref)
355 {
356     assert(err_is_ok(err));
357
358     err = nameservice_register("pci", iref);
359     if (err_is_fail(err)) {
360         USER_PANIC_ERR(err, "nameservice_register failed");
361     }
362 }
363
364 static errval_t connect_callback(void *cst, struct pci_binding *b)
365 {
366     struct client_state *st = malloc(sizeof(struct client_state));
367     assert(st != NULL);
368
369     b->rx_vtbl = pci_rx_vtbl;
370     b->st = st;
371 //    st->bar_info = 0;
372     st->nr_allocated_bars = 0;
373     for (int i = 0; i < PCI_NBARS; i++) {
374         st->nr_caps_bar[i] = 0;
375     }
376
377     return SYS_ERR_OK;
378 }
379
380 /*****************************************************************
381  * Boots up the PCI server:
382  *****************************************************************/
383
384 void pci_init(void)
385 {
386     PCI_DEBUG("pci: pci_init: called\n");
387
388     PCI_DEBUG("pci: pci_init: launch listening\n");
389     errval_t r = pci_export(NULL, export_callback, connect_callback,
390                             get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
391     assert(err_is_ok(r));
392
393     PCI_DEBUG("pci: pci_init: terminated\n");
394 }