1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <rumprun/gen_config.h>
15#include <bmk-core/types.h>
16#include <sel4/kernel.h>
17#include <assert.h>
18
19#include <bmk-core/pgalloc.h>
20#include <bmk-core/printf.h>
21#include <bmk-pcpu/pcpu.h>
22
23#include <vka/object_capops.h>
24
25#include <platsupport/io.h>
26#include <sel4/helpers.h>
27#include "pci_user.h"
28
29#define PCI_CONF_ADDR 0xcf8
30#define PCI_CONF_DATA 0xcfc
31
32
33// #define TRACK_PCI_MAPPINGS
34
35#ifdef TRACK_PCI_MAPPINGS
36/* Currently an arbitrary number */
37#define NUM_PCI_MAPPINGS 50
38static void *addresses[NUM_PCI_MAPPINGS];
39static uint32_t sizes[NUM_PCI_MAPPINGS];
40#endif /* TRACK_PCI_MAPPINGS */
41
42static struct {
43    int intrs;
44    int bus;
45    int dev;
46    int function;
47} pci_data[BMK_MAXINTR];
48
49/* Wrappers to pass through to sel4 */
50int rumpcomp_pci_port_out(uint32_t port, int io_size, uint32_t val)
51{
52    if (env.custom_simple.camkes) {
53        ZF_LOGF("ERROR\n");
54    } else {
55        return ps_io_port_out(&env.io_ops.io_port_ops, port, io_size, val);
56    }
57    return 0;
58}
59
60int rumpcomp_pci_port_in(uint32_t port, int io_size, uint32_t *result)
61{
62    if (env.custom_simple.camkes) {
63        ZF_LOGF("ERROR\n");
64
65    } else {
66        return ps_io_port_in(&env.io_ops.io_port_ops, port, io_size, result);
67    }
68    return 0;
69}
70
71int rumpcomp_pci_intr_type(void)
72{
73#ifdef CONFIG_USE_MSI_ETH
74    return 1; //PCI_INTR_TYPE_MSI;
75#else
76    return 0; //PCI_INTR_TYPE_INTX;
77#endif
78}
79
80/* Don't support iospace yet */
81int rumpcomp_pci_iospace_init(void)
82{
83    return 0;
84}
85
86static uint32_t makeaddr(unsigned bus, unsigned dev, unsigned fun, int reg)
87{
88
89    return (BIT(31)) | (bus << 16u) | (dev << 11u) | (fun << 8u) | (reg & 0xfc);
90}
91
92int rumpcomp_pci_confread(unsigned bus, unsigned dev, unsigned fun, int reg,
93                          unsigned int *value)
94{
95    uint32_t addr;
96    unsigned int data;
97    int res;
98    addr = makeaddr(bus, dev, fun, reg);
99    if (!is_hw_pci_config(&env.custom_simple)) {
100        *value =  env.custom_simple.pci_config_config.pci_config_read32(bus, dev, fun, reg);
101        return 0;
102    }
103    res = rumpcomp_pci_port_out(PCI_CONF_ADDR, 4, addr);
104
105    if (res) {
106        return res;
107    }
108    res = rumpcomp_pci_port_in(PCI_CONF_DATA, 4, &data);
109    if (res) {
110        return res;
111    }
112    *value = data;
113    return res;
114}
115
116int rumpcomp_pci_confwrite(unsigned bus, unsigned dev, unsigned fun, int reg,
117                           unsigned int value)
118{
119    uint32_t addr;
120    int res;
121    if (!is_hw_pci_config(&env.custom_simple)) {
122        env.custom_simple.pci_config_config.pci_config_write32(bus, dev, fun, reg, value);
123        return 0;
124    }
125
126    addr = makeaddr(bus, dev, fun, reg);
127    res = rumpcomp_pci_port_out(PCI_CONF_ADDR, 4, addr);
128
129    if (res) {
130        return res;
131    }
132    res = rumpcomp_pci_port_out(PCI_CONF_DATA, 4, value);
133
134    if (res) {
135        return res;
136    }
137    return 0;
138}
139
140/* map interrupts */
141/* TODO Refactor this section to better support different underlying platforms.
142    Implement the arch_simple interface */
143int rumpcomp_pci_irq_map(unsigned bus, unsigned device, unsigned fun,
144                         int intrline, unsigned cookie)
145{
146    if (cookie > BMK_MAXINTR) {
147        return BMK_EGENERIC;
148    }
149
150    pci_data[cookie].intrs = intrline;
151    pci_data[cookie].bus = bus;
152    pci_data[cookie].dev = device;
153    pci_data[cookie].function = fun;
154    return 0;
155}
156
157int rumpcomp_pci_get_bdf(unsigned cookie, unsigned *bus, unsigned *dev, unsigned *function)
158{
159    if (cookie > BMK_MAXINTR) {
160        return 1;
161    }
162
163    *bus = pci_data[cookie].bus;
164    *dev = pci_data[cookie].dev;
165    *function = pci_data[cookie].function;
166    return 0;
167}
168
169/* Create interrupt and notification objects */
170void *rumpcomp_pci_irq_establish(unsigned cookie, int (*handler)(void *), void *data)
171{
172    if (env.caps[pci_data[cookie].intrs] == 0 && !env.custom_simple.camkes) {
173        int error = vka_cspace_alloc(&env.vka, &env.caps[pci_data[cookie].intrs]);
174        ZF_LOGF_IF(error != 0, "Failed to allocate cslot, error %d", error);
175        cspacepath_t path;
176        vka_cspace_make_path(&env.vka, env.caps[pci_data[cookie].intrs], &path);
177
178#if defined CONFIG_IRQ_IOAPIC && CONFIG_USE_MSI_ETH
179        int irq = pci_data[cookie].intrs;
180        irq = 6;
181        error = seL4_IRQControl_GetMSI(simple_get_irq_ctrl(&env.simple), path.root, path.capPtr, path.capDepth,
182                                       pci_data[cookie].bus, pci_data[cookie].dev, pci_data[cookie].function, 0, irq);
183
184        if (error != 0) {
185            bmk_printf("Failed IRQControl Get MSI\n");
186        }
187        /*
188         *  XXX: We use a pretty static way of picking IRQ numbers here.
189         *  The rumpkernel pci driver is giving us interrupt numbers for the PIC
190         *  even when we are running in IOAPIC mode.  For now, we override the IRQ
191         *  numbers based on what the root task gives us.
192         */
193#elif defined CONFIG_IRQ_IOAPIC
194        ps_irq_t irq = {0};
195        error = custom_irq_from_pci_device(&env.custom_simple, pci_data[cookie].bus, pci_data[cookie].dev,
196                                           pci_data[cookie].function, &irq);
197        ZF_LOGF_IF(error == -1, "Failed to find IRQ number\n");
198        error = seL4_IRQControl_GetIOAPIC(simple_get_irq_ctrl(&env.simple), path.root, path.capPtr,
199                                          path.capDepth, irq.ioapic.ioapic, irq.ioapic.pin, irq.ioapic.level, irq.ioapic.polarity, irq.ioapic.vector);
200        if (error != 0) {
201            bmk_printf("Failed to get IOAPIC, error = %d\n", error);
202        }
203        error = seL4_IRQHandler_Ack(env.caps[pci_data[cookie].intrs]);
204        if (error != 0) {
205            bmk_printf("Failed to Ack IOAPIC\n");
206        }
207
208#else /* using PIC */
209
210        error = seL4_IRQControl_Get(simple_get_irq_ctrl(&env.simple),  pci_data[cookie].intrs, path.root, path.capPtr,
211                                    path.capDepth);
212        ZF_LOGF_IF(error != 0, "Failed to get IRQControl\n");
213        error = seL4_IRQHandler_Ack(env.caps[pci_data[cookie].intrs]);
214        ZF_LOGF_IF(error != 0, "Failed to ack IRQ handler\n");
215#endif /* CONFIG_IRQ_IOAPIC */
216
217        cspacepath_t path2;
218        error = vka_mint_object(&env.vka, &env.pci_notification, &path2,
219                                seL4_AllRights, 1 << (pci_data[cookie].intrs));
220        ZF_LOGF_IF(error != 0, "Failed to mint notification object\n");
221        error = seL4_IRQHandler_SetNotification(env.caps[pci_data[cookie].intrs], path2.capPtr);
222        ZF_LOGF_IF(error != 0, "Failed to bind IRQ to notification\n");
223    }
224
225    bmk_isr_rumpkernel(handler, data, pci_data[cookie].intrs, HARDWARE_INT);
226    return &pci_data[cookie].intrs;
227}
228
229
230
231void *rumpcomp_pci_map(unsigned long addr, unsigned long len)
232{
233    ZF_LOGE("MAP: %lx, %ld", addr, len);
234    void *vaddr = ps_io_map(&env.io_ops.io_mapper, addr, len, 0, PS_MEM_NORMAL);
235#ifdef TRACK_PCI_MAPPINGS
236    int i;
237    for (i = 0; i < NUM_PCI_MAPPINGS; i++) {
238        if (addresses[i] == NULL) {
239            addresses[i] = vaddr;
240            sizes[i] = len;
241            break;
242        }
243    }
244    ZF_LOGF_IF(i == NUM_PCI_MAPPINGS, "ERROR: Overwrote memory\n");
245#endif /* TRACK_PCI_MAPPINGS */
246    return vaddr;
247}
248
249void rumpcomp_pci_unmap(void *addr)
250{
251    uint32_t len = 0;
252#ifdef TRACK_PCI_MAPPINGS
253    int i;
254    for (i = 0; i < NUM_PCI_MAPPINGS; i++) {
255        if (addresses[i] == addr) {
256            addresses[i] = NULL;
257            len = sizes[i];
258            break;
259        }
260    }
261    ZF_LOGF_IF(i == NUM_PCI_MAPPINGS, "ERROR: Cannot find entry\n");
262#endif /* TRACK_PCI_MAPPINGS */
263    ZF_LOGD("\trumpcomp_pci_unmap: addr 0x%p len %d\n", addr, len);
264    return ps_io_unmap(&env.io_ops.io_mapper, addr, len);
265
266}
267