1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <sel4/sel4.h> 11 12#include <pci/virtual_pci.h> 13#include <pci/helper.h> 14 15#include <sel4vmmplatsupport/drivers/pci_helper.h> 16 17#define PCI_CAPABILITY_SPACE_OFFSET 0x40 18 19/* Read PCI memory device */ 20int vmm_pci_mem_device_read(void *cookie, int offset, int size, uint32_t *result) 21{ 22 if (offset < 0) { 23 ZF_LOGE("Offset should not be negative"); 24 return -1; 25 } 26 if (offset + size >= PCI_CAPABILITY_SPACE_OFFSET) { 27 ZF_LOGI("Indexing capability space not yet supported, returning 0"); 28 *result = 0; 29 return 0; 30 } 31 *result = 0; 32 /* Read the PCI device field at the given offset 33 * We are passed the device header through the cookie parameter */ 34 memcpy(result, cookie + offset, size); 35 return 0; 36} 37 38/* Write PCI memory device */ 39int vmm_pci_mem_device_write(void *cookie, int offset, int size, uint32_t value) 40{ 41 if (offset < 0) { 42 ZF_LOGE("Offset should not be negative"); 43 return -1; 44 } 45 if (offset + size >= PCI_CAPABILITY_SPACE_OFFSET) { 46 ZF_LOGI("Indexing capability space not yet supported, returning 0"); 47 return 0; 48 } 49 50 /* Ensure we aren't writing data greater than the size of 'value' */ 51 if (size > sizeof(value)) { 52 ZF_LOGE("Unable to perform a read of size 0x%x", size); 53 return -1; 54 } 55 56 /* Write the PCI device field at the given offset 57 * We are passed the device header through the cookie parameter */ 58 memcpy(cookie + offset, &value, size); 59 60 return 0; 61} 62 63int vmm_pci_entry_ignore_write(void *cookie, int offset, int size, uint32_t value) 64{ 65 ZF_LOGI("Ignoring PCI entry write @ offset 0x%x", offset); 66 return 0; 67} 68 69void define_pci_host_bridge(vmm_pci_device_def_t *bridge) 70{ 71 *bridge = (vmm_pci_device_def_t) { 72 .vendor_id = 0x5E14, 73 .device_id = 0x42, 74 .command = 0, 75 .status = 0, 76 .revision_id = 0x1, 77 .prog_if = 0, 78 .subclass = 0x0, 79 .class_code = 0x06, 80 .cache_line_size = 0, 81 .latency_timer = 0, 82 .header_type = 0x00, 83 .bist = 0, 84 .bar0 = 0, 85 .bar1 = 0, 86 .bar2 = 0, 87 .bar3 = 0, 88 .bar4 = 0, 89 .bar5 = 0, 90 .cardbus = 0, 91 .subsystem_vendor_id = 0, 92 .subsystem_id = 0, 93 .expansion_rom = 0, 94 .caps_pointer = 0, 95 .reserved1 = 0, 96 .reserved2 = 0, 97 .reserved3 = 0, 98 .interrupt_line = 0, 99 .interrupt_pin = 0, 100 .min_grant = 0, 101 .max_latency = 0, 102 .caps_len = 0, 103 .caps = NULL 104 }; 105} 106 107static int passthrough_pci_config_ioread(void *cookie, int offset, int size, uint32_t *result) 108{ 109 pci_passthrough_device_t *dev = (pci_passthrough_device_t *)cookie; 110 switch (size) { 111 case 1: 112 *result = dev->config.ioread8(dev->config.cookie, dev->addr, offset); 113 break; 114 case 2: 115 *result = dev->config.ioread16(dev->config.cookie, dev->addr, offset); 116 break; 117 case 4: 118 *result = dev->config.ioread32(dev->config.cookie, dev->addr, offset); 119 break; 120 default: 121 assert(!"Invalid size"); 122 } 123 return 0; 124} 125 126static int passthrough_pci_config_iowrite(void *cookie, int offset, int size, uint32_t val) 127{ 128 pci_passthrough_device_t *dev = (pci_passthrough_device_t *)cookie; 129 switch (size) { 130 case 1: 131 dev->config.iowrite8(dev->config.cookie, dev->addr, offset, val); 132 break; 133 case 2: 134 dev->config.iowrite16(dev->config.cookie, dev->addr, offset, val); 135 break; 136 case 4: 137 dev->config.iowrite32(dev->config.cookie, dev->addr, offset, val); 138 break; 139 default: 140 assert(!"Invalid size"); 141 } 142 return 0; 143} 144 145static int pci_bar_emul_check_range(unsigned int offset, unsigned int size) 146{ 147 if (offset < PCI_BASE_ADDRESS_0 || offset + size > PCI_BASE_ADDRESS_5 + 4) { 148 return 1; 149 } 150 return 0; 151} 152 153static uint32_t pci_make_bar(pci_bar_emulation_t *emul, int bar) 154{ 155 if (bar >= emul->num_bars) { 156 return 0; 157 } 158 uint32_t raw = 0; 159 raw |= emul->bars[bar].address; 160 if (!(emul->bars[bar].mem_type)) { 161 raw |= 1; 162 } else { 163 if (emul->bars[bar].mem_type == PREFETCH_MEM) { 164 raw |= BIT(3); 165 } 166 } 167 raw |= (emul->bar_writes[bar] & ~MASK(emul->bars[bar].size_bits)); 168 return raw; 169} 170 171static int pci_irq_emul_read(void *cookie, int offset, int size, uint32_t *result) 172{ 173 pci_irq_emulation_t *emul = (pci_irq_emulation_t *)cookie; 174 if (offset <= PCI_INTERRUPT_LINE && offset + size > PCI_INTERRUPT_LINE) { 175 /* do the regular read, then patch in our value */ 176 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 177 if (ret) { 178 return ret; 179 } 180 int bit_offset = (PCI_INTERRUPT_LINE - offset) * 8; 181 *result &= ~(MASK(8) << bit_offset); 182 *result |= (emul->irq << bit_offset); 183 return 0; 184 } else { 185 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 186 } 187} 188 189static int pci_irq_emul_write(void *cookie, int offset, int size, uint32_t value) 190{ 191 pci_irq_emulation_t *emul = (pci_irq_emulation_t *)cookie; 192 if (offset == PCI_INTERRUPT_LINE && size == 1) { 193 /* ignore */ 194 return 0; 195 } else if (offset < PCI_INTERRUPT_LINE && offset + size >= PCI_INTERRUPT_LINE) { 196 assert(!"Guest writing PCI configuration in an unsupported way"); 197 return -1; 198 } else { 199 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value); 200 } 201} 202 203static int pci_bar_emul_read(void *cookie, int offset, int size, uint32_t *result) 204{ 205 pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie; 206 if (pci_bar_emul_check_range(offset, size)) { 207 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 208 } 209 /* Construct the bar value */ 210 int bar = (offset - PCI_BASE_ADDRESS_0) / 4; 211 int bar_offset = offset & 3; 212 uint32_t bar_raw = pci_make_bar(emul, bar); 213 char *barp = (char *)&bar_raw; 214 *result = 0; 215 memcpy(result, barp + bar_offset, size); 216 return 0; 217} 218 219static int pci_bar_emul_write(void *cookie, int offset, int size, uint32_t value) 220{ 221 pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie; 222 if (pci_bar_emul_check_range(offset, size)) { 223 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value); 224 } 225 /* Construct the bar value */ 226 int bar = (offset - PCI_BASE_ADDRESS_0) / 4; 227 int bar_offset = offset & 3; 228 char *barp = (char *)&emul->bar_writes[bar]; 229 memcpy(barp + bar_offset, &value, size); 230 return 0; 231} 232 233static int pci_bar_passthrough_emul_read(void *cookie, int offset, int size, uint32_t *result) 234{ 235 pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie; 236 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 237} 238 239static int pci_bar_passthrough_emul_write(void *cookie, int offset, int size, uint32_t value) 240{ 241 pci_bar_emulation_t *emul = (pci_bar_emulation_t *)cookie; 242 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value); 243} 244 245vmm_pci_entry_t vmm_pci_create_bar_emulation(vmm_pci_entry_t existing, int num_bars, vmm_pci_bar_t *bars) 246{ 247 pci_bar_emulation_t *bar_emul = calloc(1, sizeof(*bar_emul)); 248 assert(bar_emul); 249 memcpy(bar_emul->bars, bars, sizeof(vmm_pci_bar_t) * num_bars); 250 bar_emul->passthrough = existing; 251 bar_emul->num_bars = num_bars; 252 memset(bar_emul->bar_writes, 0, sizeof(bar_emul->bar_writes)); 253 return (vmm_pci_entry_t) { 254 .cookie = bar_emul, .ioread = pci_bar_emul_read, .iowrite = pci_bar_emul_write 255 }; 256} 257 258vmm_pci_entry_t vmm_pci_create_passthrough_bar_emulation(vmm_pci_entry_t existing, int num_bars, vmm_pci_bar_t *bars) 259{ 260 pci_bar_emulation_t *bar_emul = calloc(1, sizeof(*bar_emul)); 261 assert(bar_emul); 262 memcpy(bar_emul->bars, bars, sizeof(vmm_pci_bar_t) * num_bars); 263 bar_emul->passthrough = existing; 264 bar_emul->num_bars = num_bars; 265 memset(bar_emul->bar_writes, 0, sizeof(bar_emul->bar_writes)); 266 return (vmm_pci_entry_t) { 267 .cookie = bar_emul, .ioread = pci_bar_passthrough_emul_read, .iowrite = pci_bar_passthrough_emul_write 268 }; 269} 270 271vmm_pci_entry_t vmm_pci_create_irq_emulation(vmm_pci_entry_t existing, int irq) 272{ 273 pci_irq_emulation_t *irq_emul = calloc(1, sizeof(*irq_emul)); 274 assert(irq_emul); 275 irq_emul->passthrough = existing; 276 irq_emul->irq = irq; 277 return (vmm_pci_entry_t) { 278 .cookie = irq_emul, .ioread = pci_irq_emul_read, .iowrite = pci_irq_emul_write 279 }; 280} 281 282vmm_pci_entry_t vmm_pci_create_passthrough(vmm_pci_address_t addr, vmm_pci_config_t config) 283{ 284 pci_passthrough_device_t *dev = calloc(1, sizeof(*dev)); 285 assert(dev); 286 dev->addr = addr; 287 dev->config = config; 288 ZF_LOGI("Creating passthrough device for %02x:%02x.%d", addr.bus, addr.dev, addr.fun); 289 return (vmm_pci_entry_t) { 290 .cookie = dev, .ioread = passthrough_pci_config_ioread, .iowrite = passthrough_pci_config_iowrite 291 }; 292} 293 294static int pci_cap_emul_read(void *cookie, int offset, int size, uint32_t *result) 295{ 296 pci_cap_emulation_t *emul = (pci_cap_emulation_t *)cookie; 297 if (offset <= PCI_STATUS && offset + size > PCI_STATUS) { 298 /* do the regular read, then patch in our value */ 299 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 300 if (ret) { 301 return ret; 302 } 303 int bit_offset = (PCI_STATUS - offset) * 8; 304 *result &= ~(PCI_STATUS_CAP_LIST << bit_offset); 305 if (emul->num_caps > 0) { 306 *result |= (PCI_STATUS_CAP_LIST << bit_offset); 307 } 308 return 0; 309 } else if (offset <= PCI_CAPABILITY_LIST && offset + size > PCI_CAPABILITY_LIST) { 310 /* do the regular read, then patch in our value */ 311 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 312 if (ret) { 313 return ret; 314 } 315 int bit_offset = (PCI_CAPABILITY_LIST - offset) * 8; 316 *result &= ~(MASK(8) << bit_offset); 317 if (emul->num_caps > 0) { 318 *result |= (emul->caps[0] << bit_offset); 319 } 320 return 0; 321 } 322 /* see if we are reading from any location that we would prefer not to */ 323 int i; 324 for (i = 0; i < emul->num_ignore; i++) { 325 if (offset <= emul->ignore_start[i] && offset + size > emul->ignore_end[i]) { 326 /* who cares about the size, just ignore everything */ 327 ZF_LOGI("Attempted read at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i], 328 emul->ignore_end[i]); 329 *result = 0; 330 return 0; 331 } 332 } 333 /* See if we are reading a capability index */ 334 for (i = 0; i < emul->num_caps; i++) { 335 if (offset <= emul->caps[i] + 1 && offset + size > emul->caps[i] + 1) { 336 /* do the regular read, then patch in our value */ 337 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 338 if (ret) { 339 return ret; 340 } 341 int bit_offset = (emul->caps[i] + 1 - offset) * 8; 342 *result &= ~(MASK(8) << bit_offset); 343 if (i + 1 < emul->num_caps) { 344 *result |= (emul->caps[i + 1] << bit_offset); 345 } 346 return 0; 347 } 348 } 349 /* Pass through whatever is left */ 350 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result); 351} 352 353static int pci_cap_emul_write(void *cookie, int offset, int size, uint32_t value) 354{ 355 pci_cap_emulation_t *emul = (pci_cap_emulation_t *)cookie; 356 /* Prevents writes to our ignored ranges. but let anything else through */ 357 int i; 358 for (i = 0; i < emul->num_ignore; i++) { 359 if (offset <= emul->ignore_start[i] && offset + size > emul->ignore_end[i]) { 360 /* who cares about the size, just ignore everything */ 361 ZF_LOGI("Attempted write at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i], 362 emul->ignore_end[i]); 363 return 0; 364 } 365 } 366 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value); 367} 368 369vmm_pci_entry_t vmm_pci_create_cap_emulation(vmm_pci_entry_t existing, int num_caps, uint8_t *caps, int num_ranges, 370 uint8_t *range_starts, uint8_t *range_ends) 371{ 372 pci_cap_emulation_t *emul = calloc(1, sizeof(*emul)); 373 emul->passthrough = existing; 374 assert(emul); 375 emul->num_caps = num_caps; 376 emul->caps = calloc(1, sizeof(uint8_t) * num_caps); 377 assert(emul->caps); 378 memcpy(emul->caps, caps, sizeof(uint8_t) * num_caps); 379 emul->num_ignore = num_ranges; 380 emul->ignore_start = calloc(1, sizeof(uint8_t) * num_ranges); 381 assert(emul->ignore_start); 382 emul->ignore_end = calloc(1, sizeof(uint8_t) * num_ranges); 383 assert(emul->ignore_end); 384 memcpy(emul->ignore_start, range_starts, sizeof(uint8_t) * num_ranges); 385 memcpy(emul->ignore_end, range_ends, sizeof(uint8_t) * num_ranges); 386 return (vmm_pci_entry_t) { 387 .cookie = emul, .ioread = pci_cap_emul_read, .iowrite = pci_cap_emul_write 388 }; 389} 390 391#define MAX_CAPS 256 392 393vmm_pci_entry_t vmm_pci_no_msi_cap_emulation(vmm_pci_entry_t existing) 394{ 395 uint32_t value; 396 int UNUSED error; 397 /* Ensure this is a type 0 device */ 398 value = 0; 399 error = existing.ioread(existing.cookie, PCI_HEADER_TYPE, 1, &value); 400 assert(!error); 401 assert((value & (~BIT(7))) == PCI_HEADER_TYPE_NORMAL); 402 /* Check if it has capability space */ 403 error = existing.ioread(existing.cookie, PCI_STATUS, 1, &value); 404 assert(!error); 405 if (!(value & PCI_STATUS_CAP_LIST)) { 406 return existing; 407 } 408 /* First we need to scan the capability space, and detect any PCI caps 409 * while we're at it */ 410 int num_caps; 411 uint8_t caps[MAX_CAPS]; 412 int num_ignore; 413 uint8_t ignore_start[2]; 414 uint8_t ignore_end[2]; 415 error = existing.ioread(existing.cookie, PCI_CAPABILITY_LIST, 1, &value); 416 assert(!error); 417 /* Mask off the bottom 2 bits, which are reserved */ 418 value &= ~MASK(2); 419 num_caps = 0; 420 num_ignore = 0; 421 while (value != 0) { 422 uint32_t cap_type = 0; 423 error = existing.ioread(existing.cookie, value, 1, &cap_type); 424 assert(!error); 425 if (cap_type == PCI_CAP_ID_MSI) { 426 assert(num_ignore < 2); 427 ignore_start[num_ignore] = value; 428 ignore_end[num_ignore] = value + 20; 429 num_ignore++; 430 } else if (cap_type == PCI_CAP_ID_MSIX) { 431 ignore_start[num_ignore] = value; 432 ignore_end[num_ignore] = value + 8; 433 num_ignore++; 434 } else { 435 assert(num_caps < MAX_CAPS); 436 caps[num_caps] = (uint8_t)value; 437 num_caps++; 438 } 439 error = existing.ioread(existing.cookie, value + 1, 1, &value); 440 assert(!error); 441 } 442 if (num_ignore > 0) { 443 return vmm_pci_create_cap_emulation(existing, num_caps, caps, num_ignore, ignore_start, ignore_end); 444 } else { 445 return existing; 446 } 447} 448