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 <assert.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <pci/pci.h>
18#include <pci/ioreg.h>
19#include <pci/virtual_pci.h>
20#include <pci/virtual_device.h>
21#include <utils/zf_log.h>
22
23bool libpci_virtual_pci_device_allow(libpci_virtual_pci_t* self, libpci_device_t *device) {
24    assert(self);
25    if (!device) {
26        ZF_LOGD("device_allow error: NULL device!\n");
27        return false;
28    }
29    if (!libpci_find_device_matching(device)) {
30        ZF_LOGD("device_allow error: invalid device!\n");
31        return false;
32    }
33    assert(self->num_allowed_devices + 1 < PCI_MAX_VDEVICES);
34    libpci_passthrough_vdevice_t *vd = &self->allowed_devices[self->num_allowed_devices];
35    vd->host_bus = device->bus;
36    vd->host_dev = device->dev;
37    vd->host_fun = device->fun;
38    self->num_allowed_devices++;
39    return true;
40}
41
42bool libpci_virtual_pci_device_allow_id(libpci_virtual_pci_t* self, uint16_t vendor_id, uint16_t device_id) {
43    libpci_device_t* matched_devices[PCI_MAX_DEVICES];
44    int nfound = libpci_find_device_all(vendor_id, device_id, matched_devices);
45    for (int i = 0; i < nfound; i++) {
46        bool ret = libpci_virtual_pci_device_allow(self, matched_devices[i]);
47        if (!ret) return ret;
48    }
49    return true;
50}
51
52bool libpci_virtual_pci_device_disallow(libpci_virtual_pci_t* self, const libpci_device_t *device) {
53    assert(self && device);
54    for (uint32_t i = 0; i < self->num_allowed_devices; i++) {
55        libpci_passthrough_vdevice_t *vd = &self->allowed_devices[i];
56        if (vd->host_bus == device->bus &&
57            vd->host_dev == device->dev &&
58            vd->host_fun == device->fun) {
59            vd->host_bus = PCI_HOST_BUS_INVALID;
60            return true;
61        }
62    }
63    return false;
64}
65
66bool libpci_virtual_pci_device_device_check(libpci_virtual_pci_t* self, uint8_t bus, uint8_t dev, uint8_t fun) {
67    assert(self);
68    if (self->override_allow_all_devices) {
69        return true;
70    }
71    for (uint32_t i = 0; i < self->num_allowed_devices; i++) {
72        libpci_passthrough_vdevice_t *vd = &self->allowed_devices[i];
73        if (vd->host_bus == PCI_HOST_BUS_INVALID) continue;
74        if (vd->host_bus == bus &&
75            vd->host_dev == dev &&
76            vd->host_fun == fun) {
77            return true;
78        }
79    }
80    return false;
81}
82
83libpci_vdevice_t* libpci_virtual_pci_vdevice_assign(libpci_virtual_pci_t* self) {
84    assert(self);
85    assert(self->num_virtual_devices + 1 < PCI_MAX_VDEVICES);
86    libpci_vdevice_init(&self->virtual_devices[self->num_virtual_devices]);
87    return &self->virtual_devices[self->num_virtual_devices++];
88}
89
90void libpci_virtual_pci_vdevice_resign(libpci_virtual_pci_t* self, libpci_vdevice_t* vdev) {
91    assert(self && vdev);
92    int index = (vdev - self->virtual_devices);
93    assert(index >= 0 && index <= PCI_MAX_VDEVICES);
94    self->virtual_devices[index].disable(&self->virtual_devices[index]);
95}
96
97libpci_vdevice_t* libpci_virtual_pci_vdevice_check(libpci_virtual_pci_t* self,
98                                                   uint8_t bus, uint8_t dev, uint8_t fun) {
99    assert(self);
100    for(uint32_t i = 0; i < self->num_virtual_devices; i++) {
101        libpci_vdevice_t *vd = &self->virtual_devices[i];
102        if (vd->match(vd, bus, dev, fun)) {
103            return vd;
104        }
105    }
106    return NULL;
107}
108
109int libpci_virtual_pci_ioread(libpci_virtual_pci_t* self, uint32_t port_no, uint32_t* val, uint32_t size) {
110    if (port_no >= PCI_CONF_PORT_ADDR && port_no < PCI_CONF_PORT_ADDR_END) {
111        if (port_no + size > PCI_CONF_PORT_ADDR_END) {
112            ZF_LOGD("vpci_ioread WARNING: portno + size = 0x%x invalid address.\n", port_no + size);
113            return 1;
114        }
115        /* Emulate read addr. */
116        *val = 0;
117        memcpy(val, ((char*)&self->current_addr) + (port_no - PCI_CONF_PORT_ADDR), size);
118        return 0;
119    }
120    if (port_no < PCI_CONF_PORT_DATA || port_no >= PCI_CONF_PORT_DATA_END) {
121        ZF_LOGD("vpci_ioread WARNING: port_no 0x%x size %d invalid.\n", port_no, size);
122        return 1;
123    }
124
125    /* Reverse lookup port_no to bus, dev, fun and reg. */
126    uint8_t bus, dev, fun, reg;
127    libpci_portno_reverse_lookup(self->current_addr, &bus, &dev, &fun, &reg);
128
129    /* Find a matching virtual device. */
130    libpci_vdevice_t *vd = self->vdevice_check(self, bus, dev, fun);
131    if (vd) {
132        uint32_t data_offset =  port_no - PCI_CONF_PORT_DATA;
133        *val = vd->ioread(vd, reg + data_offset, size);
134        return 0;
135    }
136
137    /* Find a matching passthrough device. */
138    bool allowed = self->device_check(self, bus, dev, fun);
139    if (!allowed) {
140        // Disallowed device, we hide it from the virtual PCI config.
141        // By returning a commonly accepted invalid value. (All 1 bits)
142        ZF_LOGV("vpci_ioread WARNING: disallowed device %d %d %d.\n", bus, dev, fun);
143        *val = PCI_INVALID_READ_VALUE;
144        return 0;
145    }
146
147    // Address is allowed. Perform normal ioread.
148    libpci_out32(PCI_CONF_PORT_ADDR, self->current_addr);
149    int ret = libpci_ioread(port_no, val, size);
150    return ret;
151}
152
153int libpci_virtual_pci_iowrite(libpci_virtual_pci_t* self, uint32_t port_no, uint32_t val, uint32_t size) {
154    if (port_no >= PCI_CONF_PORT_ADDR && port_no < PCI_CONF_PORT_ADDR_END) {
155        if (port_no + size > PCI_CONF_PORT_ADDR_END) {
156            ZF_LOGD("vpci_ioread WARNING: portno + size = 0x%x invalid address.\n", port_no + size);
157            return 1;
158        }
159        /* Emulated set addr. */
160        memcpy(((char*)&self->current_addr) + (port_no - PCI_CONF_PORT_ADDR), &val, size);
161        return 0;
162    }
163    if (port_no < PCI_CONF_PORT_DATA || port_no >= PCI_CONF_PORT_DATA_END) {
164        ZF_LOGD("vpci_iowrite WARNING: port_no 0x%x size %d invalid.\n", port_no, size);
165        return 1;
166    }
167
168    uint8_t bus, dev, fun, reg;
169    libpci_portno_reverse_lookup(self->current_addr, &bus, &dev, &fun, &reg);
170
171    /* Find a matching virtual device. */
172    libpci_vdevice_t *vd = self->vdevice_check(self, bus, dev, fun);
173    if (vd) {
174        uint32_t data_offset =  port_no - PCI_CONF_PORT_DATA;
175        vd->iowrite(vd, reg + data_offset, size, val);
176        return 0;
177    }
178
179    /* Find a matching passthrough device. */
180    bool allowed = self->device_check(self, bus, dev, fun);
181    if (!allowed) {
182        // Disallowed device, we hide it from the virtual PCI config.
183        ZF_LOGV("vpci_iowrite WARNING: disallowed device %d %d %d.\n", bus, dev, fun);
184        return 0;
185    }
186
187    // Address is allowed. Perform normal iowrite.
188    libpci_out32(PCI_CONF_PORT_ADDR, self->current_addr);
189    int ret = libpci_iowrite(port_no, val, size);
190    return ret;
191}
192
193void libpci_virtual_pci_init(libpci_virtual_pci_t* vp) {
194    assert(vp);
195
196    /* initialise state */
197    vp->num_allowed_devices = 0;
198    vp->num_virtual_devices = 0;
199    vp->override_allow_all_devices = false;
200    vp->current_addr = PCI_INVALID_READ_VALUE;
201
202    /* connect interface */
203    vp->device_allow = libpci_virtual_pci_device_allow;
204    vp->device_allow_id = libpci_virtual_pci_device_allow_id;
205    vp->device_disallow = libpci_virtual_pci_device_disallow;
206    vp->device_check = libpci_virtual_pci_device_device_check;
207    vp->vdevice_assign = libpci_virtual_pci_vdevice_assign;
208    vp->vdevice_resign = libpci_virtual_pci_vdevice_resign;
209    vp->vdevice_check = libpci_virtual_pci_vdevice_check;
210    vp->ioread = libpci_virtual_pci_ioread;
211    vp->iowrite = libpci_virtual_pci_iowrite;
212}
213