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#include <assert.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <pci/pci.h>
17#include <pci/pci_config.h>
18#include <pci/virtual_pci.h>
19#include <pci/virtual_device.h>
20#include <pci/ioreg.h>
21#include <utils/zf_log.h>
22
23static uint8_t libpci_vdevice_rebase_callback_ioread(libpci_vdevice_t* vdevice, int offset) {
24    assert(vdevice);
25    assert(offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4);
26
27    int index = (offset - PCI_BASE_ADDRESS_0) / 4;
28    int byte_offset = (offset - PCI_BASE_ADDRESS_0) % 4;
29    uint8_t* rebased_addr_ptr = (uint8_t*)(&vdevice->rebased_addr[index]);
30    ZF_LOGD("returning rebased_addr rebased_addr[%d] 0x%x\n", index, vdevice->rebased_addr[index]);
31    return rebased_addr_ptr[byte_offset];
32}
33
34static void libpci_vdevice_rebase_callback_iowrite(libpci_vdevice_t* vdevice, int offset,
35                                                   uint8_t val) {
36    assert(vdevice);
37    assert(offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4);
38
39    int index = (offset - PCI_BASE_ADDRESS_0) / 4;
40    int byte_offset = (offset - PCI_BASE_ADDRESS_0) % 4;
41    uint8_t* rebased_addr_ptr = (uint8_t*)(&vdevice->rebased_addr[index]);
42    uint8_t* rebased_mask_ptr = (uint8_t*)(&vdevice->rebased_writemask[index]);
43
44    /* Set all bits that are writable to zero. */
45    rebased_addr_ptr[byte_offset] &= ~rebased_mask_ptr[byte_offset];
46
47    /* Write into all bits writable. */
48    rebased_addr_ptr[byte_offset] |= val & rebased_mask_ptr[byte_offset];
49}
50
51void libpci_vdevice_enable(libpci_vdevice_t* self, uint8_t bus, uint8_t dev, uint8_t fun,
52                           libpci_device_t* pdevice_passthrough) {
53    assert(self);
54    self->location_bus = bus;
55    self->location_dev = dev;
56    self->location_fun = fun;
57    self->physical_device_passthrough = pdevice_passthrough;
58    self->enabled = true;
59}
60
61void libpci_vdevice_disable(libpci_vdevice_t* self) {
62    assert(self);
63    self->enabled = false;
64}
65
66bool libpci_vdevice_match(libpci_vdevice_t* self, uint8_t bus, uint8_t dev, uint8_t fun) {
67    assert(self);
68    if(!self->enabled) {
69        /* match nothing if we are disabled. */
70        return false;
71    }
72    return (self->location_bus == bus &&
73            self->location_dev == dev &&
74            self->location_fun == fun);
75}
76
77void libpci_vdevice_set_mode(libpci_vdevice_t* self, int offset,
78                             libpci_vdevice_mode_t m) {
79    assert(self);
80    assert(offset >= 0 && offset < PCI_STD_HEADER_SIZEOF);
81
82    int sz = libpci_device_cfg_sizeof(offset);
83    assert(sz > 0);
84
85    for (int i = 0; i < sz; i++) {
86        self->mode[offset + i] = m;
87    }
88}
89
90void libpci_vdevice_rebase_addr_realdevice(libpci_vdevice_t* self,
91                                           int base_addr_index,
92                                           uint32_t base_addr,
93                                           libpci_device_t* dev) {
94    assert(self && dev);
95    assert(base_addr_index >= 0 && base_addr_index < 6);
96    assert(dev->cfg.base_addr_space[base_addr_index] == PCI_BASE_ADDRESS_SPACE_MEMORY);
97
98    if (dev->cfg.base_addr_64H[base_addr_index]) {
99        /* Rebasing the HWORD of a 64-bit address is not supported yet. */
100        assert(!"Rebase HWORD of 64-bit address not supported.");
101        return;
102    }
103
104    self->rebase_addr_virtdevice(self, base_addr_index, base_addr,
105            dev->cfg.base_addr_size_mask[base_addr_index] & PCI_BASE_ADDRESS_MEM_MASK,
106            dev->cfg.base_addr_prefetchable[base_addr_index],
107            dev->cfg.base_addr_type[base_addr_index] == PCI_BASE_ADDRESS_MEM_TYPE_64);
108    self->physical_device_passthrough = dev;
109}
110
111void libpci_vdevice_rebase_ioaddr_realdevice(libpci_vdevice_t* self,
112                                             int base_addr_index,
113                                             uint32_t base_addr,
114                                             libpci_device_t* dev) {
115    assert(self && dev);
116    assert(base_addr_index >= 0 && base_addr_index < 6);
117    assert(dev->cfg.base_addr_space[base_addr_index] == PCI_BASE_ADDRESS_SPACE_IO);
118
119    self->rebase_ioaddr_virtdevice(self, base_addr_index, base_addr,
120            dev->cfg.base_addr_size_mask[base_addr_index] & PCI_BASE_ADDRESS_IO_MASK);
121    self->physical_device_passthrough = dev;
122}
123
124void libpci_vdevice_rebase_addr_virtdevice(libpci_vdevice_t* self,
125                                           int base_addr_index,
126                                           uint32_t base_addr,
127                                           uint32_t size_mask,
128                                           bool prefetch,
129                                           bool LWord64) {
130    assert(self);
131    assert(base_addr_index >= 0 && base_addr_index < 6);
132
133    if((size_mask & PCI_BASE_ADDRESS_MEM_MASK) != size_mask) {
134        printf("ERROR: size mask invalid 0x%x", size_mask);
135        assert(!"size mask invalid");
136        return;
137    }
138
139    if ((base_addr & size_mask) != base_addr) {
140        printf("ERROR: address alignment invalid 0x%x to mask 0x%x", base_addr, size_mask);
141        assert(!"address alignment invalid ");
142        return;
143    }
144
145    self->rebased_writemask[base_addr_index] = size_mask;
146    self->rebased_addr[base_addr_index] = PCI_BASE_ADDRESS_SPACE_MEMORY;
147    self->rebased_addr[base_addr_index] |= LWord64 ? PCI_BASE_ADDRESS_MEM_TYPE_64:
148                                                     PCI_BASE_ADDRESS_MEM_TYPE_32;
149    self->rebased_addr[base_addr_index] |= prefetch ? PCI_BASE_ADDRESS_MEM_PREFETCH : 0;
150    self->rebased_addr[base_addr_index] |= base_addr;
151    self->rebased_type[base_addr_index] = PCI_BASE_ADDRESS_SPACE_MEMORY;
152
153    libpci_vdevice_mode_t m;
154    m.mode = PCI_VDEVICE_MODE_REBASED_ADDR;
155    m.callback_ioread = libpci_vdevice_rebase_callback_ioread;
156    m.callback_iowrite = libpci_vdevice_rebase_callback_iowrite;
157    self->set_mode(self, PCI_BASE_ADDRESS_0 + (base_addr_index * 4), m);
158}
159
160void libpci_vdevice_rebase_ioaddr_virtdevice(libpci_vdevice_t* self,
161                                           int base_addr_index,
162                                           uint32_t base_addr,
163                                           uint32_t size_mask) {
164    assert(self);
165    assert(base_addr_index >= 0 && base_addr_index < 6);
166
167    if((size_mask & PCI_BASE_ADDRESS_IO_MASK) != size_mask) {
168        printf("ERROR: io size mask invalid 0x%x", size_mask);
169        assert(!"io size mask invalid");
170        return;
171    }
172
173    if ((base_addr & size_mask) != base_addr) {
174        printf("ERROR: io address alignment invalid 0x%x to mask 0x%x", base_addr, size_mask);
175        assert(!"io address alignment invalid ");
176        return;
177    }
178
179    self->rebased_writemask[base_addr_index] = size_mask;
180    self->rebased_addr[base_addr_index] = base_addr;
181    self->rebased_addr[base_addr_index] |= PCI_BASE_ADDRESS_SPACE_IO;
182    self->rebased_type[base_addr_index] = PCI_BASE_ADDRESS_SPACE_IO;
183
184    libpci_vdevice_mode_t m;
185    m.mode = PCI_VDEVICE_MODE_REBASED_ADDR;
186    m.callback_ioread = libpci_vdevice_rebase_callback_ioread;
187    m.callback_iowrite = libpci_vdevice_rebase_callback_iowrite;
188    self->set_mode(self, PCI_BASE_ADDRESS_0 + (base_addr_index * 4), m);
189}
190
191uint32_t libpci_vdevice_ioread(libpci_vdevice_t* self, int offset, int size){
192    assert(self);
193    assert(size == 1 || size == 2 || size == 4);
194    assert(offset >= 0);
195
196    uint32_t result = 0;
197    uint8_t* result_p = (uint8_t*)(&result);
198
199    /* Check for attempted access to extended PCI space. */
200    if ((offset + size) >= PCI_STD_HEADER_SIZEOF) {
201        if (!self->allow_extended_pci_config_space) {
202            printf("ERROR: device tried to access extended PCI config space offset %d, but "
203                   "allow_extended_pci_config_space was disabled. This is most likely a "
204                   "misconfiguration.\n", offset + size);
205            assert(!"Device tried to access extended PCI config space.");
206            return 0xFFFFFFFF;
207        }
208        /* Only supported mode for extended PCI is passthrough. */
209        assert(self->physical_device_passthrough);
210        return libpci_read_reg(self->physical_device_passthrough->bus,
211                               self->physical_device_passthrough->dev,
212                               self->physical_device_passthrough->fun,
213                               offset, size);
214    }
215
216    /* Loop through each byte and handle accordingly. */
217    for (int i = 0; i < size; i++) {
218        libpci_vdevice_mode_t* m = &self->mode[offset + i];
219        uint8_t result_byte = 0;
220
221        switch (m->mode) {
222        case PCI_VDEVICE_MODE_PASSTHROUGH:
223            assert(self->physical_device_passthrough);
224            result_byte = libpci_read_reg8(self->physical_device_passthrough->bus,
225                                           self->physical_device_passthrough->dev,
226                                           self->physical_device_passthrough->fun,
227                                           offset + i);
228            break;
229
230        case PCI_VDEVICE_MODE_FATAL_ERROR:
231            printf("PCI_VDEVICE_MODE_FATAL_ERROR triggered for offset %d size %d\n", offset, size);
232            assert(!"PCI_VDEVICE_MODE_FATAL_ERROR triggered.");
233            while(1);
234            break;
235
236        case PCI_VDEVICE_MODE_CALLBACK:
237        case PCI_VDEVICE_MODE_REBASED_ADDR:
238            assert(m->callback_ioread);
239            result_byte = m->callback_ioread(self, offset + i);
240            break;
241
242        default:
243            assert(!"unknown mode.");
244            break;
245        }
246        result_p[i] = result_byte;
247    }
248
249    return result;
250}
251
252void libpci_vdevice_iowrite(libpci_vdevice_t* self, int offset, int size, uint32_t val) {
253    assert(self);
254    assert(size == 1 || size == 2 || size == 4);
255    assert(offset >= 0);
256    uint8_t* val_p = (uint8_t*) &val;
257
258    /* Check for attempted access to extended PCI space. */
259    if ((offset + size) >= PCI_STD_HEADER_SIZEOF) {
260        if (!self->allow_extended_pci_config_space) {
261            printf("ERROR: device tried to access extended PCI config space offset %d, but "
262                   "allow_extended_pci_config_space was disabled. This is most likely a "
263                   "misconfiguration.\n", offset + size);
264            assert(!"Device tried to access extended PCI config space.");
265            return;
266        }
267        /* Only supported mode for extended PCI is passthrough. */
268        assert(self->physical_device_passthrough);
269        libpci_write_reg(self->physical_device_passthrough->bus,
270                         self->physical_device_passthrough->dev,
271                         self->physical_device_passthrough->fun,
272                         offset, val, size);
273        return;
274    }
275
276    /* Special case handle the case when the entire range is under passthrough. */
277    bool passthrough_entire = true;
278    for (int i = 0; i < size; i++) {
279        libpci_vdevice_mode_t* m = &self->mode[offset + i];
280        if (m->mode != PCI_VDEVICE_MODE_PASSTHROUGH) {
281            passthrough_entire = false;
282            break;
283        }
284    }
285    if (passthrough_entire) {
286        assert(self->physical_device_passthrough);
287        libpci_write_reg(self->physical_device_passthrough->bus,
288                         self->physical_device_passthrough->dev,
289                         self->physical_device_passthrough->fun,
290                         offset, val, size);
291        return;
292    }
293
294    /* Loop through each byte and handle accordingly. */
295    for (int i = 0; i < size; i++) {
296        libpci_vdevice_mode_t* m = &self->mode[offset + i];
297
298        switch (m->mode) {
299        case PCI_VDEVICE_MODE_PASSTHROUGH:
300            assert(self->physical_device_passthrough);
301            printf("    writing 0x%x into offset %d (total val = 0x%x)\n", val_p[i], offset + i, val);
302            libpci_write_reg8(self->physical_device_passthrough->bus,
303                              self->physical_device_passthrough->dev,
304                              self->physical_device_passthrough->fun,
305                              offset + i, val_p[i]);
306            break;
307
308        case PCI_VDEVICE_MODE_FATAL_ERROR:
309            printf("PCI_VDEVICE_MODE_FATAL_ERROR triggered for offset %d size %d\n", offset, size);
310            assert(!"PCI_VDEVICE_MODE_FATAL_ERROR triggered.");
311            while(1);
312            break;
313
314        case PCI_VDEVICE_MODE_CALLBACK:
315        case PCI_VDEVICE_MODE_REBASED_ADDR:
316            assert(m->callback_iowrite);
317            m->callback_iowrite(self, offset + i, ((uint8_t*)(&val))[i]);
318            break;
319
320        default:
321            assert(!"unknown mode.");
322            break;
323        }
324    }
325}
326
327void libpci_vdevice_init(libpci_vdevice_t* vd) {
328    assert(vd);
329    memset(vd, 0, sizeof(libpci_vdevice_t));
330    vd->enable = libpci_vdevice_enable;
331    vd->disable = libpci_vdevice_disable;
332    vd->match = libpci_vdevice_match;
333    vd->set_mode = libpci_vdevice_set_mode;
334    vd->rebase_addr_realdevice = libpci_vdevice_rebase_addr_realdevice;
335    vd->rebase_ioaddr_realdevice = libpci_vdevice_rebase_ioaddr_realdevice;
336    vd->rebase_addr_virtdevice = libpci_vdevice_rebase_addr_virtdevice;
337    vd->rebase_ioaddr_virtdevice = libpci_vdevice_rebase_ioaddr_virtdevice;
338    vd->ioread = libpci_vdevice_ioread;
339    vd->iowrite = libpci_vdevice_iowrite;
340}
341