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, ®); 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, ®); 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