blob: 00784106f2bf31e24dd1c8801a9b8c890c628bd8 [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pci/pci.h>
#include <pci/ioreg.h>
#include <pci/virtual_pci.h>
#include <pci/virtual_device.h>
#include <utils/zf_log.h>
bool libpci_virtual_pci_device_allow(libpci_virtual_pci_t* self, libpci_device_t *device) {
assert(self);
if (!device) {
ZF_LOGD("device_allow error: NULL device!\n");
return false;
}
if (!libpci_find_device_matching(device)) {
ZF_LOGD("device_allow error: invalid device!\n");
return false;
}
assert(self->num_allowed_devices + 1 < PCI_MAX_VDEVICES);
libpci_passthrough_vdevice_t *vd = &self->allowed_devices[self->num_allowed_devices];
vd->host_bus = device->bus;
vd->host_dev = device->dev;
vd->host_fun = device->fun;
self->num_allowed_devices++;
return true;
}
bool libpci_virtual_pci_device_allow_id(libpci_virtual_pci_t* self, uint16_t vendor_id, uint16_t device_id) {
libpci_device_t* matched_devices[PCI_MAX_DEVICES];
int nfound = libpci_find_device_all(vendor_id, device_id, matched_devices);
for (int i = 0; i < nfound; i++) {
bool ret = libpci_virtual_pci_device_allow(self, matched_devices[i]);
if (!ret) return ret;
}
return true;
}
bool libpci_virtual_pci_device_disallow(libpci_virtual_pci_t* self, const libpci_device_t *device) {
assert(self && device);
for (uint32_t i = 0; i < self->num_allowed_devices; i++) {
libpci_passthrough_vdevice_t *vd = &self->allowed_devices[i];
if (vd->host_bus == device->bus &&
vd->host_dev == device->dev &&
vd->host_fun == device->fun) {
vd->host_bus = PCI_HOST_BUS_INVALID;
return true;
}
}
return false;
}
bool libpci_virtual_pci_device_device_check(libpci_virtual_pci_t* self, uint8_t bus, uint8_t dev, uint8_t fun) {
assert(self);
if (self->override_allow_all_devices) {
return true;
}
for (uint32_t i = 0; i < self->num_allowed_devices; i++) {
libpci_passthrough_vdevice_t *vd = &self->allowed_devices[i];
if (vd->host_bus == PCI_HOST_BUS_INVALID) continue;
if (vd->host_bus == bus &&
vd->host_dev == dev &&
vd->host_fun == fun) {
return true;
}
}
return false;
}
libpci_vdevice_t* libpci_virtual_pci_vdevice_assign(libpci_virtual_pci_t* self) {
assert(self);
assert(self->num_virtual_devices + 1 < PCI_MAX_VDEVICES);
libpci_vdevice_init(&self->virtual_devices[self->num_virtual_devices]);
return &self->virtual_devices[self->num_virtual_devices++];
}
void libpci_virtual_pci_vdevice_resign(libpci_virtual_pci_t* self, libpci_vdevice_t* vdev) {
assert(self && vdev);
int index = (vdev - self->virtual_devices);
assert(index >= 0 && index <= PCI_MAX_VDEVICES);
self->virtual_devices[index].disable(&self->virtual_devices[index]);
}
libpci_vdevice_t* libpci_virtual_pci_vdevice_check(libpci_virtual_pci_t* self,
uint8_t bus, uint8_t dev, uint8_t fun) {
assert(self);
for(uint32_t i = 0; i < self->num_virtual_devices; i++) {
libpci_vdevice_t *vd = &self->virtual_devices[i];
if (vd->match(vd, bus, dev, fun)) {
return vd;
}
}
return NULL;
}
int libpci_virtual_pci_ioread(libpci_virtual_pci_t* self, uint32_t port_no, uint32_t* val, uint32_t size) {
if (port_no >= PCI_CONF_PORT_ADDR && port_no < PCI_CONF_PORT_ADDR_END) {
if (port_no + size > PCI_CONF_PORT_ADDR_END) {
ZF_LOGD("vpci_ioread WARNING: portno + size = 0x%x invalid address.\n", port_no + size);
return 1;
}
/* Emulate read addr. */
*val = 0;
memcpy(val, ((char*)&self->current_addr) + (port_no - PCI_CONF_PORT_ADDR), size);
return 0;
}
if (port_no < PCI_CONF_PORT_DATA || port_no >= PCI_CONF_PORT_DATA_END) {
ZF_LOGD("vpci_ioread WARNING: port_no 0x%x size %d invalid.\n", port_no, size);
return 1;
}
/* Reverse lookup port_no to bus, dev, fun and reg. */
uint8_t bus, dev, fun, reg;
libpci_portno_reverse_lookup(self->current_addr, &bus, &dev, &fun, &reg);
/* Find a matching virtual device. */
libpci_vdevice_t *vd = self->vdevice_check(self, bus, dev, fun);
if (vd) {
uint32_t data_offset = port_no - PCI_CONF_PORT_DATA;
*val = vd->ioread(vd, reg + data_offset, size);
return 0;
}
/* Find a matching passthrough device. */
bool allowed = self->device_check(self, bus, dev, fun);
if (!allowed) {
// Disallowed device, we hide it from the virtual PCI config.
// By returning a commonly accepted invalid value. (All 1 bits)
ZF_LOGV("vpci_ioread WARNING: disallowed device %d %d %d.\n", bus, dev, fun);
*val = PCI_INVALID_READ_VALUE;
return 0;
}
// Address is allowed. Perform normal ioread.
libpci_out32(PCI_CONF_PORT_ADDR, self->current_addr);
int ret = libpci_ioread(port_no, val, size);
return ret;
}
int libpci_virtual_pci_iowrite(libpci_virtual_pci_t* self, uint32_t port_no, uint32_t val, uint32_t size) {
if (port_no >= PCI_CONF_PORT_ADDR && port_no < PCI_CONF_PORT_ADDR_END) {
if (port_no + size > PCI_CONF_PORT_ADDR_END) {
ZF_LOGD("vpci_ioread WARNING: portno + size = 0x%x invalid address.\n", port_no + size);
return 1;
}
/* Emulated set addr. */
memcpy(((char*)&self->current_addr) + (port_no - PCI_CONF_PORT_ADDR), &val, size);
return 0;
}
if (port_no < PCI_CONF_PORT_DATA || port_no >= PCI_CONF_PORT_DATA_END) {
ZF_LOGD("vpci_iowrite WARNING: port_no 0x%x size %d invalid.\n", port_no, size);
return 1;
}
uint8_t bus, dev, fun, reg;
libpci_portno_reverse_lookup(self->current_addr, &bus, &dev, &fun, &reg);
/* Find a matching virtual device. */
libpci_vdevice_t *vd = self->vdevice_check(self, bus, dev, fun);
if (vd) {
uint32_t data_offset = port_no - PCI_CONF_PORT_DATA;
vd->iowrite(vd, reg + data_offset, size, val);
return 0;
}
/* Find a matching passthrough device. */
bool allowed = self->device_check(self, bus, dev, fun);
if (!allowed) {
// Disallowed device, we hide it from the virtual PCI config.
ZF_LOGV("vpci_iowrite WARNING: disallowed device %d %d %d.\n", bus, dev, fun);
return 0;
}
// Address is allowed. Perform normal iowrite.
libpci_out32(PCI_CONF_PORT_ADDR, self->current_addr);
int ret = libpci_iowrite(port_no, val, size);
return ret;
}
void libpci_virtual_pci_init(libpci_virtual_pci_t* vp) {
assert(vp);
/* initialise state */
vp->num_allowed_devices = 0;
vp->num_virtual_devices = 0;
vp->override_allow_all_devices = false;
vp->current_addr = PCI_INVALID_READ_VALUE;
/* connect interface */
vp->device_allow = libpci_virtual_pci_device_allow;
vp->device_allow_id = libpci_virtual_pci_device_allow_id;
vp->device_disallow = libpci_virtual_pci_device_disallow;
vp->device_check = libpci_virtual_pci_device_device_check;
vp->vdevice_assign = libpci_virtual_pci_vdevice_assign;
vp->vdevice_resign = libpci_virtual_pci_vdevice_resign;
vp->vdevice_check = libpci_virtual_pci_vdevice_check;
vp->ioread = libpci_virtual_pci_ioread;
vp->iowrite = libpci_virtual_pci_iowrite;
}