blob: 061993f5ebe3630b23bd67b92f73b62d5c8aa505 [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/pci_config.h>
#include <pci/virtual_pci.h>
#include <pci/virtual_device.h>
#include <pci/ioreg.h>
#include <utils/zf_log.h>
static uint8_t libpci_vdevice_rebase_callback_ioread(libpci_vdevice_t* vdevice, int offset) {
assert(vdevice);
assert(offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4);
int index = (offset - PCI_BASE_ADDRESS_0) / 4;
int byte_offset = (offset - PCI_BASE_ADDRESS_0) % 4;
uint8_t* rebased_addr_ptr = (uint8_t*)(&vdevice->rebased_addr[index]);
ZF_LOGD("returning rebased_addr rebased_addr[%d] 0x%x\n", index, vdevice->rebased_addr[index]);
return rebased_addr_ptr[byte_offset];
}
static void libpci_vdevice_rebase_callback_iowrite(libpci_vdevice_t* vdevice, int offset,
uint8_t val) {
assert(vdevice);
assert(offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4);
int index = (offset - PCI_BASE_ADDRESS_0) / 4;
int byte_offset = (offset - PCI_BASE_ADDRESS_0) % 4;
uint8_t* rebased_addr_ptr = (uint8_t*)(&vdevice->rebased_addr[index]);
uint8_t* rebased_mask_ptr = (uint8_t*)(&vdevice->rebased_writemask[index]);
/* Set all bits that are writable to zero. */
rebased_addr_ptr[byte_offset] &= ~rebased_mask_ptr[byte_offset];
/* Write into all bits writable. */
rebased_addr_ptr[byte_offset] |= val & rebased_mask_ptr[byte_offset];
}
void libpci_vdevice_enable(libpci_vdevice_t* self, uint8_t bus, uint8_t dev, uint8_t fun,
libpci_device_t* pdevice_passthrough) {
assert(self);
self->location_bus = bus;
self->location_dev = dev;
self->location_fun = fun;
self->physical_device_passthrough = pdevice_passthrough;
self->enabled = true;
}
void libpci_vdevice_disable(libpci_vdevice_t* self) {
assert(self);
self->enabled = false;
}
bool libpci_vdevice_match(libpci_vdevice_t* self, uint8_t bus, uint8_t dev, uint8_t fun) {
assert(self);
if(!self->enabled) {
/* match nothing if we are disabled. */
return false;
}
return (self->location_bus == bus &&
self->location_dev == dev &&
self->location_fun == fun);
}
void libpci_vdevice_set_mode(libpci_vdevice_t* self, int offset,
libpci_vdevice_mode_t m) {
assert(self);
assert(offset >= 0 && offset < PCI_STD_HEADER_SIZEOF);
int sz = libpci_device_cfg_sizeof(offset);
assert(sz > 0);
for (int i = 0; i < sz; i++) {
self->mode[offset + i] = m;
}
}
void libpci_vdevice_rebase_addr_realdevice(libpci_vdevice_t* self,
int base_addr_index,
uint32_t base_addr,
libpci_device_t* dev) {
assert(self && dev);
assert(base_addr_index >= 0 && base_addr_index < 6);
assert(dev->cfg.base_addr_space[base_addr_index] == PCI_BASE_ADDRESS_SPACE_MEMORY);
if (dev->cfg.base_addr_64H[base_addr_index]) {
/* Rebasing the HWORD of a 64-bit address is not supported yet. */
assert(!"Rebase HWORD of 64-bit address not supported.");
return;
}
self->rebase_addr_virtdevice(self, base_addr_index, base_addr,
dev->cfg.base_addr_size_mask[base_addr_index] & PCI_BASE_ADDRESS_MEM_MASK,
dev->cfg.base_addr_prefetchable[base_addr_index],
dev->cfg.base_addr_type[base_addr_index] == PCI_BASE_ADDRESS_MEM_TYPE_64);
self->physical_device_passthrough = dev;
}
void libpci_vdevice_rebase_ioaddr_realdevice(libpci_vdevice_t* self,
int base_addr_index,
uint32_t base_addr,
libpci_device_t* dev) {
assert(self && dev);
assert(base_addr_index >= 0 && base_addr_index < 6);
assert(dev->cfg.base_addr_space[base_addr_index] == PCI_BASE_ADDRESS_SPACE_IO);
self->rebase_ioaddr_virtdevice(self, base_addr_index, base_addr,
dev->cfg.base_addr_size_mask[base_addr_index] & PCI_BASE_ADDRESS_IO_MASK);
self->physical_device_passthrough = dev;
}
void libpci_vdevice_rebase_addr_virtdevice(libpci_vdevice_t* self,
int base_addr_index,
uint32_t base_addr,
uint32_t size_mask,
bool prefetch,
bool LWord64) {
assert(self);
assert(base_addr_index >= 0 && base_addr_index < 6);
if((size_mask & PCI_BASE_ADDRESS_MEM_MASK) != size_mask) {
printf("ERROR: size mask invalid 0x%x", size_mask);
assert(!"size mask invalid");
return;
}
if ((base_addr & size_mask) != base_addr) {
printf("ERROR: address alignment invalid 0x%x to mask 0x%x", base_addr, size_mask);
assert(!"address alignment invalid ");
return;
}
self->rebased_writemask[base_addr_index] = size_mask;
self->rebased_addr[base_addr_index] = PCI_BASE_ADDRESS_SPACE_MEMORY;
self->rebased_addr[base_addr_index] |= LWord64 ? PCI_BASE_ADDRESS_MEM_TYPE_64:
PCI_BASE_ADDRESS_MEM_TYPE_32;
self->rebased_addr[base_addr_index] |= prefetch ? PCI_BASE_ADDRESS_MEM_PREFETCH : 0;
self->rebased_addr[base_addr_index] |= base_addr;
self->rebased_type[base_addr_index] = PCI_BASE_ADDRESS_SPACE_MEMORY;
libpci_vdevice_mode_t m;
m.mode = PCI_VDEVICE_MODE_REBASED_ADDR;
m.callback_ioread = libpci_vdevice_rebase_callback_ioread;
m.callback_iowrite = libpci_vdevice_rebase_callback_iowrite;
self->set_mode(self, PCI_BASE_ADDRESS_0 + (base_addr_index * 4), m);
}
void libpci_vdevice_rebase_ioaddr_virtdevice(libpci_vdevice_t* self,
int base_addr_index,
uint32_t base_addr,
uint32_t size_mask) {
assert(self);
assert(base_addr_index >= 0 && base_addr_index < 6);
if((size_mask & PCI_BASE_ADDRESS_IO_MASK) != size_mask) {
printf("ERROR: io size mask invalid 0x%x", size_mask);
assert(!"io size mask invalid");
return;
}
if ((base_addr & size_mask) != base_addr) {
printf("ERROR: io address alignment invalid 0x%x to mask 0x%x", base_addr, size_mask);
assert(!"io address alignment invalid ");
return;
}
self->rebased_writemask[base_addr_index] = size_mask;
self->rebased_addr[base_addr_index] = base_addr;
self->rebased_addr[base_addr_index] |= PCI_BASE_ADDRESS_SPACE_IO;
self->rebased_type[base_addr_index] = PCI_BASE_ADDRESS_SPACE_IO;
libpci_vdevice_mode_t m;
m.mode = PCI_VDEVICE_MODE_REBASED_ADDR;
m.callback_ioread = libpci_vdevice_rebase_callback_ioread;
m.callback_iowrite = libpci_vdevice_rebase_callback_iowrite;
self->set_mode(self, PCI_BASE_ADDRESS_0 + (base_addr_index * 4), m);
}
uint32_t libpci_vdevice_ioread(libpci_vdevice_t* self, int offset, int size){
assert(self);
assert(size == 1 || size == 2 || size == 4);
assert(offset >= 0);
uint32_t result = 0;
uint8_t* result_p = (uint8_t*)(&result);
/* Check for attempted access to extended PCI space. */
if ((offset + size) >= PCI_STD_HEADER_SIZEOF) {
if (!self->allow_extended_pci_config_space) {
printf("ERROR: device tried to access extended PCI config space offset %d, but "
"allow_extended_pci_config_space was disabled. This is most likely a "
"misconfiguration.\n", offset + size);
assert(!"Device tried to access extended PCI config space.");
return 0xFFFFFFFF;
}
/* Only supported mode for extended PCI is passthrough. */
assert(self->physical_device_passthrough);
return libpci_read_reg(self->physical_device_passthrough->bus,
self->physical_device_passthrough->dev,
self->physical_device_passthrough->fun,
offset, size);
}
/* Loop through each byte and handle accordingly. */
for (int i = 0; i < size; i++) {
libpci_vdevice_mode_t* m = &self->mode[offset + i];
uint8_t result_byte = 0;
switch (m->mode) {
case PCI_VDEVICE_MODE_PASSTHROUGH:
assert(self->physical_device_passthrough);
result_byte = libpci_read_reg8(self->physical_device_passthrough->bus,
self->physical_device_passthrough->dev,
self->physical_device_passthrough->fun,
offset + i);
break;
case PCI_VDEVICE_MODE_FATAL_ERROR:
printf("PCI_VDEVICE_MODE_FATAL_ERROR triggered for offset %d size %d\n", offset, size);
assert(!"PCI_VDEVICE_MODE_FATAL_ERROR triggered.");
while(1);
break;
case PCI_VDEVICE_MODE_CALLBACK:
case PCI_VDEVICE_MODE_REBASED_ADDR:
assert(m->callback_ioread);
result_byte = m->callback_ioread(self, offset + i);
break;
default:
assert(!"unknown mode.");
break;
}
result_p[i] = result_byte;
}
return result;
}
void libpci_vdevice_iowrite(libpci_vdevice_t* self, int offset, int size, uint32_t val) {
assert(self);
assert(size == 1 || size == 2 || size == 4);
assert(offset >= 0);
uint8_t* val_p = (uint8_t*) &val;
/* Check for attempted access to extended PCI space. */
if ((offset + size) >= PCI_STD_HEADER_SIZEOF) {
if (!self->allow_extended_pci_config_space) {
printf("ERROR: device tried to access extended PCI config space offset %d, but "
"allow_extended_pci_config_space was disabled. This is most likely a "
"misconfiguration.\n", offset + size);
assert(!"Device tried to access extended PCI config space.");
return;
}
/* Only supported mode for extended PCI is passthrough. */
assert(self->physical_device_passthrough);
libpci_write_reg(self->physical_device_passthrough->bus,
self->physical_device_passthrough->dev,
self->physical_device_passthrough->fun,
offset, val, size);
return;
}
/* Special case handle the case when the entire range is under passthrough. */
bool passthrough_entire = true;
for (int i = 0; i < size; i++) {
libpci_vdevice_mode_t* m = &self->mode[offset + i];
if (m->mode != PCI_VDEVICE_MODE_PASSTHROUGH) {
passthrough_entire = false;
break;
}
}
if (passthrough_entire) {
assert(self->physical_device_passthrough);
libpci_write_reg(self->physical_device_passthrough->bus,
self->physical_device_passthrough->dev,
self->physical_device_passthrough->fun,
offset, val, size);
return;
}
/* Loop through each byte and handle accordingly. */
for (int i = 0; i < size; i++) {
libpci_vdevice_mode_t* m = &self->mode[offset + i];
switch (m->mode) {
case PCI_VDEVICE_MODE_PASSTHROUGH:
assert(self->physical_device_passthrough);
printf(" writing 0x%x into offset %d (total val = 0x%x)\n", val_p[i], offset + i, val);
libpci_write_reg8(self->physical_device_passthrough->bus,
self->physical_device_passthrough->dev,
self->physical_device_passthrough->fun,
offset + i, val_p[i]);
break;
case PCI_VDEVICE_MODE_FATAL_ERROR:
printf("PCI_VDEVICE_MODE_FATAL_ERROR triggered for offset %d size %d\n", offset, size);
assert(!"PCI_VDEVICE_MODE_FATAL_ERROR triggered.");
while(1);
break;
case PCI_VDEVICE_MODE_CALLBACK:
case PCI_VDEVICE_MODE_REBASED_ADDR:
assert(m->callback_iowrite);
m->callback_iowrite(self, offset + i, ((uint8_t*)(&val))[i]);
break;
default:
assert(!"unknown mode.");
break;
}
}
}
void libpci_vdevice_init(libpci_vdevice_t* vd) {
assert(vd);
memset(vd, 0, sizeof(libpci_vdevice_t));
vd->enable = libpci_vdevice_enable;
vd->disable = libpci_vdevice_disable;
vd->match = libpci_vdevice_match;
vd->set_mode = libpci_vdevice_set_mode;
vd->rebase_addr_realdevice = libpci_vdevice_rebase_addr_realdevice;
vd->rebase_ioaddr_realdevice = libpci_vdevice_rebase_ioaddr_realdevice;
vd->rebase_addr_virtdevice = libpci_vdevice_rebase_addr_virtdevice;
vd->rebase_ioaddr_virtdevice = libpci_vdevice_rebase_ioaddr_virtdevice;
vd->ioread = libpci_vdevice_ioread;
vd->iowrite = libpci_vdevice_iowrite;
}