1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sel4/sel4.h>
11
12#include <pci/virtual_pci.h>
13#include <pci/helper.h>
14
15#include <sel4vmmplatsupport/drivers/pci_helper.h>
16
17#define PCI_CAPABILITY_SPACE_OFFSET 0x40
18
19/* Read PCI memory device */
20int vmm_pci_mem_device_read(void *cookie, int offset, int size, uint32_t *result)
21{
22    if (offset < 0) {
23        ZF_LOGE("Offset should not be negative");
24        return -1;
25    }
26    if (offset + size >= PCI_CAPABILITY_SPACE_OFFSET) {
27        ZF_LOGI("Indexing capability space not yet supported, returning 0");
28        *result = 0;
29        return 0;
30    }
31    *result = 0;
32    /* Read the PCI device field at the given offset
33     * We are passed the device header through the cookie parameter */
34    memcpy(result, cookie + offset, size);
35    return 0;
36}
37
38/* Write PCI memory device */
39int vmm_pci_mem_device_write(void *cookie, int offset, int size, uint32_t value)
40{
41    if (offset < 0) {
42        ZF_LOGE("Offset should not be negative");
43        return -1;
44    }
45    if (offset + size >= PCI_CAPABILITY_SPACE_OFFSET) {
46        ZF_LOGI("Indexing capability space not yet supported, returning 0");
47        return 0;
48    }
49
50    /* Ensure we aren't writing data greater than the size of 'value' */
51    if (size > sizeof(value)) {
52        ZF_LOGE("Unable to perform a read of size 0x%x", size);
53        return -1;
54    }
55
56    /* Write the PCI device field at the given offset
57     * We are passed the device header through the cookie parameter */
58    memcpy(cookie + offset, &value, size);
59
60    return 0;
61}
62
63int vmm_pci_entry_ignore_write(void *cookie, int offset, int size, uint32_t value)
64{
65    ZF_LOGI("Ignoring PCI entry write @ offset 0x%x", offset);
66    return 0;
67}
68
69void define_pci_host_bridge(vmm_pci_device_def_t *bridge)
70{
71    *bridge = (vmm_pci_device_def_t) {
72        .vendor_id = 0x5E14,
73        .device_id = 0x42,
74        .command = 0,
75        .status = 0,
76        .revision_id = 0x1,
77        .prog_if = 0,
78        .subclass = 0x0,
79        .class_code = 0x06,
80        .cache_line_size = 0,
81        .latency_timer = 0,
82        .header_type = 0x00,
83        .bist = 0,
84        .bar0 = 0,
85        .bar1 = 0,
86        .bar2 = 0,
87        .bar3 = 0,
88        .bar4 = 0,
89        .bar5 = 0,
90        .cardbus = 0,
91        .subsystem_vendor_id = 0,
92        .subsystem_id = 0,
93        .expansion_rom = 0,
94        .caps_pointer = 0,
95        .reserved1 = 0,
96        .reserved2 = 0,
97        .reserved3 = 0,
98        .interrupt_line = 0,
99        .interrupt_pin = 0,
100        .min_grant = 0,
101        .max_latency = 0,
102        .caps_len = 0,
103        .caps = NULL
104    };
105}
106
107static int passthrough_pci_config_ioread(void *cookie, int offset, int size, uint32_t *result)
108{
109    pci_passthrough_device_t *dev = (pci_passthrough_device_t *)cookie;
110    switch (size) {
111    case 1:
112        *result = dev->config.ioread8(dev->config.cookie, dev->addr, offset);
113        break;
114    case 2:
115        *result = dev->config.ioread16(dev->config.cookie, dev->addr, offset);
116        break;
117    case 4:
118        *result = dev->config.ioread32(dev->config.cookie, dev->addr, offset);
119        break;
120    default:
121        assert(!"Invalid size");
122    }
123    return 0;
124}
125
126static int passthrough_pci_config_iowrite(void *cookie, int offset, int size, uint32_t val)
127{
128    pci_passthrough_device_t *dev = (pci_passthrough_device_t *)cookie;
129    switch (size) {
130    case 1:
131        dev->config.iowrite8(dev->config.cookie, dev->addr, offset, val);
132        break;
133    case 2:
134        dev->config.iowrite16(dev->config.cookie, dev->addr, offset, val);
135        break;
136    case 4:
137        dev->config.iowrite32(dev->config.cookie, dev->addr, offset, val);
138        break;
139    default:
140        assert(!"Invalid size");
141    }
142    return 0;
143}
144
145static int pci_bar_emul_check_range(unsigned int offset, unsigned int size)
146{
147    if (offset < PCI_BASE_ADDRESS_0 || offset + size > PCI_BASE_ADDRESS_5 + 4) {
148        return 1;
149    }
150    return 0;
151}
152
153static uint32_t pci_make_bar(pci_bar_emulation_t *emul, int bar)
154{
155    if (bar >= emul->num_bars) {
156        return 0;
157    }
158    uint32_t raw = 0;
159    raw |= emul->bars[bar].address;
160    if (!(emul->bars[bar].mem_type)) {
161        raw |= 1;
162    } else {
163        if (emul->bars[bar].mem_type == PREFETCH_MEM) {
164            raw |= BIT(3);
165        }
166    }
167    raw |= (emul->bar_writes[bar] & ~MASK(emul->bars[bar].size_bits));
168    return raw;
169}
170
171static int pci_irq_emul_read(void *cookie, int offset, int size, uint32_t *result)
172{
173    pci_irq_emulation_t *emul = (pci_irq_emulation_t *)cookie;
174    if (offset <= PCI_INTERRUPT_LINE && offset + size > PCI_INTERRUPT_LINE) {
175        /* do the regular read, then patch in our value */
176        int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
177        if (ret) {
178            return ret;
179        }
180        int bit_offset = (PCI_INTERRUPT_LINE - offset) * 8;
181        *result &= ~(MASK(8) << bit_offset);
182        *result |= (emul->irq << bit_offset);
183        return 0;
184    } else {
185        return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
186    }
187}
188
189static int pci_irq_emul_write(void *cookie, int offset, int size, uint32_t value)
190{
191    pci_irq_emulation_t *emul = (pci_irq_emulation_t *)cookie;
192    if (offset == PCI_INTERRUPT_LINE && size == 1) {
193        /* ignore */
194        return 0;
195    } else if (offset < PCI_INTERRUPT_LINE && offset + size >= PCI_INTERRUPT_LINE) {
196        assert(!"Guest writing PCI configuration in an unsupported way");
197        return -1;
198    } else {
199        return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
200    }
201}
202
203static int pci_bar_emul_read(void *cookie, int offset, int size, uint32_t *result)
204{
205    pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie;
206    if (pci_bar_emul_check_range(offset, size)) {
207        return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
208    }
209    /* Construct the bar value */
210    int bar = (offset - PCI_BASE_ADDRESS_0) / 4;
211    int bar_offset = offset & 3;
212    uint32_t bar_raw = pci_make_bar(emul, bar);
213    char *barp = (char *)&bar_raw;
214    *result = 0;
215    memcpy(result, barp + bar_offset, size);
216    return 0;
217}
218
219static int pci_bar_emul_write(void *cookie, int offset, int size, uint32_t value)
220{
221    pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie;
222    if (pci_bar_emul_check_range(offset, size)) {
223        return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
224    }
225    /* Construct the bar value */
226    int bar = (offset - PCI_BASE_ADDRESS_0) / 4;
227    int bar_offset = offset & 3;
228    char *barp = (char *)&emul->bar_writes[bar];
229    memcpy(barp + bar_offset, &value, size);
230    return 0;
231}
232
233static int pci_bar_passthrough_emul_read(void *cookie, int offset, int size, uint32_t *result)
234{
235    pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie;
236    return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
237}
238
239static int pci_bar_passthrough_emul_write(void *cookie, int offset, int size, uint32_t value)
240{
241    pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie;
242    return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
243}
244
245vmm_pci_entry_t vmm_pci_create_bar_emulation(vmm_pci_entry_t existing, int num_bars, vmm_pci_bar_t *bars)
246{
247    pci_bar_emulation_t *bar_emul = calloc(1, sizeof(*bar_emul));
248    assert(bar_emul);
249    memcpy(bar_emul->bars, bars, sizeof(vmm_pci_bar_t) * num_bars);
250    bar_emul->passthrough = existing;
251    bar_emul->num_bars = num_bars;
252    memset(bar_emul->bar_writes, 0, sizeof(bar_emul->bar_writes));
253    return (vmm_pci_entry_t) {
254        .cookie = bar_emul, .ioread = pci_bar_emul_read, .iowrite = pci_bar_emul_write
255    };
256}
257
258vmm_pci_entry_t vmm_pci_create_passthrough_bar_emulation(vmm_pci_entry_t existing, int num_bars, vmm_pci_bar_t *bars)
259{
260    pci_bar_emulation_t *bar_emul = calloc(1, sizeof(*bar_emul));
261    assert(bar_emul);
262    memcpy(bar_emul->bars, bars, sizeof(vmm_pci_bar_t) * num_bars);
263    bar_emul->passthrough = existing;
264    bar_emul->num_bars = num_bars;
265    memset(bar_emul->bar_writes, 0, sizeof(bar_emul->bar_writes));
266    return (vmm_pci_entry_t) {
267        .cookie = bar_emul, .ioread = pci_bar_passthrough_emul_read, .iowrite = pci_bar_passthrough_emul_write
268    };
269}
270
271vmm_pci_entry_t vmm_pci_create_irq_emulation(vmm_pci_entry_t existing, int irq)
272{
273    pci_irq_emulation_t *irq_emul = calloc(1, sizeof(*irq_emul));
274    assert(irq_emul);
275    irq_emul->passthrough = existing;
276    irq_emul->irq = irq;
277    return (vmm_pci_entry_t) {
278        .cookie = irq_emul, .ioread = pci_irq_emul_read, .iowrite = pci_irq_emul_write
279    };
280}
281
282vmm_pci_entry_t vmm_pci_create_passthrough(vmm_pci_address_t addr, vmm_pci_config_t config)
283{
284    pci_passthrough_device_t *dev = calloc(1, sizeof(*dev));
285    assert(dev);
286    dev->addr = addr;
287    dev->config = config;
288    ZF_LOGI("Creating passthrough device for %02x:%02x.%d", addr.bus, addr.dev, addr.fun);
289    return (vmm_pci_entry_t) {
290        .cookie = dev, .ioread = passthrough_pci_config_ioread, .iowrite = passthrough_pci_config_iowrite
291    };
292}
293
294static int pci_cap_emul_read(void *cookie, int offset, int size, uint32_t *result)
295{
296    pci_cap_emulation_t *emul = (pci_cap_emulation_t *)cookie;
297    if (offset <= PCI_STATUS && offset + size > PCI_STATUS) {
298        /* do the regular read, then patch in our value */
299        int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
300        if (ret) {
301            return ret;
302        }
303        int bit_offset = (PCI_STATUS - offset) * 8;
304        *result &= ~(PCI_STATUS_CAP_LIST << bit_offset);
305        if (emul->num_caps > 0) {
306            *result |= (PCI_STATUS_CAP_LIST << bit_offset);
307        }
308        return 0;
309    } else if (offset <= PCI_CAPABILITY_LIST && offset + size > PCI_CAPABILITY_LIST) {
310        /* do the regular read, then patch in our value */
311        int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
312        if (ret) {
313            return ret;
314        }
315        int bit_offset = (PCI_CAPABILITY_LIST - offset) * 8;
316        *result &= ~(MASK(8) << bit_offset);
317        if (emul->num_caps > 0) {
318            *result |= (emul->caps[0] << bit_offset);
319        }
320        return 0;
321    }
322    /* see if we are reading from any location that we would prefer not to */
323    int i;
324    for (i = 0; i < emul->num_ignore; i++) {
325        if (offset <= emul->ignore_start[i] && offset + size > emul->ignore_end[i]) {
326            /* who cares about the size, just ignore everything */
327            ZF_LOGI("Attempted read at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i],
328                    emul->ignore_end[i]);
329            *result = 0;
330            return 0;
331        }
332    }
333    /* See if we are reading a capability index */
334    for (i = 0; i < emul->num_caps; i++) {
335        if (offset <= emul->caps[i] + 1 && offset + size > emul->caps[i] + 1) {
336            /* do the regular read, then patch in our value */
337            int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
338            if (ret) {
339                return ret;
340            }
341            int bit_offset = (emul->caps[i] + 1 - offset) * 8;
342            *result &= ~(MASK(8) << bit_offset);
343            if (i + 1 < emul->num_caps) {
344                *result |= (emul->caps[i + 1] << bit_offset);
345            }
346            return 0;
347        }
348    }
349    /* Pass through whatever is left */
350    return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
351}
352
353static int pci_cap_emul_write(void *cookie, int offset, int size, uint32_t value)
354{
355    pci_cap_emulation_t *emul = (pci_cap_emulation_t *)cookie;
356    /* Prevents writes to our ignored ranges. but let anything else through */
357    int i;
358    for (i = 0; i < emul->num_ignore; i++) {
359        if (offset <= emul->ignore_start[i] && offset + size > emul->ignore_end[i]) {
360            /* who cares about the size, just ignore everything */
361            ZF_LOGI("Attempted write at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i],
362                    emul->ignore_end[i]);
363            return 0;
364        }
365    }
366    return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
367}
368
369vmm_pci_entry_t vmm_pci_create_cap_emulation(vmm_pci_entry_t existing, int num_caps, uint8_t *caps, int num_ranges,
370                                             uint8_t *range_starts, uint8_t *range_ends)
371{
372    pci_cap_emulation_t *emul = calloc(1, sizeof(*emul));
373    emul->passthrough = existing;
374    assert(emul);
375    emul->num_caps = num_caps;
376    emul->caps = calloc(1, sizeof(uint8_t) * num_caps);
377    assert(emul->caps);
378    memcpy(emul->caps, caps, sizeof(uint8_t) * num_caps);
379    emul->num_ignore = num_ranges;
380    emul->ignore_start = calloc(1, sizeof(uint8_t) * num_ranges);
381    assert(emul->ignore_start);
382    emul->ignore_end = calloc(1, sizeof(uint8_t) * num_ranges);
383    assert(emul->ignore_end);
384    memcpy(emul->ignore_start, range_starts, sizeof(uint8_t) * num_ranges);
385    memcpy(emul->ignore_end, range_ends, sizeof(uint8_t) * num_ranges);
386    return (vmm_pci_entry_t) {
387        .cookie = emul, .ioread = pci_cap_emul_read, .iowrite = pci_cap_emul_write
388    };
389}
390
391#define MAX_CAPS 256
392
393vmm_pci_entry_t vmm_pci_no_msi_cap_emulation(vmm_pci_entry_t existing)
394{
395    uint32_t value;
396    int UNUSED error;
397    /* Ensure this is a type 0 device */
398    value = 0;
399    error = existing.ioread(existing.cookie, PCI_HEADER_TYPE, 1, &value);
400    assert(!error);
401    assert((value & (~BIT(7))) == PCI_HEADER_TYPE_NORMAL);
402    /* Check if it has capability space */
403    error = existing.ioread(existing.cookie, PCI_STATUS, 1, &value);
404    assert(!error);
405    if (!(value & PCI_STATUS_CAP_LIST)) {
406        return existing;
407    }
408    /* First we need to scan the capability space, and detect any PCI caps
409     * while we're at it */
410    int num_caps;
411    uint8_t caps[MAX_CAPS];
412    int num_ignore;
413    uint8_t ignore_start[2];
414    uint8_t ignore_end[2];
415    error = existing.ioread(existing.cookie, PCI_CAPABILITY_LIST, 1, &value);
416    assert(!error);
417    /* Mask off the bottom 2 bits, which are reserved */
418    value &= ~MASK(2);
419    num_caps = 0;
420    num_ignore = 0;
421    while (value != 0) {
422        uint32_t cap_type = 0;
423        error = existing.ioread(existing.cookie, value, 1, &cap_type);
424        assert(!error);
425        if (cap_type == PCI_CAP_ID_MSI) {
426            assert(num_ignore < 2);
427            ignore_start[num_ignore] = value;
428            ignore_end[num_ignore] = value + 20;
429            num_ignore++;
430        } else if (cap_type == PCI_CAP_ID_MSIX) {
431            ignore_start[num_ignore] = value;
432            ignore_end[num_ignore] = value + 8;
433            num_ignore++;
434        } else {
435            assert(num_caps < MAX_CAPS);
436            caps[num_caps] = (uint8_t)value;
437            num_caps++;
438        }
439        error = existing.ioread(existing.cookie, value + 1, 1, &value);
440        assert(!error);
441    }
442    if (num_ignore > 0) {
443        return vmm_pci_create_cap_emulation(existing, num_caps, caps, num_ignore, ignore_start, ignore_end);
444    } else {
445        return existing;
446    }
447}
448