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