qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

pci: Add support for Designware IP block

Add code needed to get a functional PCI subsytem when using in
conjunction with upstream Linux guest (4.13+). Tested to work against
"e1000e" (network adapter, using MSI interrupts) as well as
"usb-ehci" (USB controller, using legacy PCI interrupts).

Based on "i.MX6 Applications Processor Reference Manual" (Document
Number: IMX6DQRM Rev. 4) as well as corresponding dirver in Linux
kernel (circa 4.13 - 4.16 found in drivers/pci/dwc/*)

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

authored by

Andrey Smirnov and committed by
Peter Maydell
d64e5eab 8f2ba1f2

+861
+1
default-configs/arm-softmmu.mak
··· 140 140 CONFIG_MSF2=y 141 141 CONFIG_FW_CFG_DMA=y 142 142 CONFIG_XILINX_AXI=y 143 + CONFIG_PCI_DESIGNWARE=y
+2
hw/pci-host/Makefile.objs
··· 17 17 common-obj-$(CONFIG_PCI_Q35) += q35.o 18 18 common-obj-$(CONFIG_PCI_GENERIC) += gpex.o 19 19 common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o 20 + 21 + common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
+754
hw/pci-host/designware.c
··· 1 + /* 2 + * Copyright (c) 2018, Impinj, Inc. 3 + * 4 + * Designware PCIe IP block emulation 5 + * 6 + * This library is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU Lesser General Public 8 + * License as published by the Free Software Foundation; either 9 + * version 2 of the License, or (at your option) any later version. 10 + * 11 + * This library is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 + * Lesser General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU Lesser General Public 17 + * License along with this library; if not, see 18 + * <http://www.gnu.org/licenses/>. 19 + */ 20 + 21 + #include "qemu/osdep.h" 22 + #include "qapi/error.h" 23 + #include "hw/pci/msi.h" 24 + #include "hw/pci/pci_bridge.h" 25 + #include "hw/pci/pci_host.h" 26 + #include "hw/pci/pcie_port.h" 27 + #include "hw/pci-host/designware.h" 28 + 29 + #define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710 30 + #define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C 31 + #define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4) 32 + #define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C 33 + #define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17) 34 + #define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820 35 + #define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824 36 + #define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828 37 + #define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C 38 + #define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830 39 + #define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900 40 + #define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31) 41 + #define DESIGNWARE_PCIE_ATU_CR1 0x904 42 + #define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0) 43 + #define DESIGNWARE_PCIE_ATU_CR2 0x908 44 + #define DESIGNWARE_PCIE_ATU_ENABLE BIT(31) 45 + #define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C 46 + #define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910 47 + #define DESIGNWARE_PCIE_ATU_LIMIT 0x914 48 + #define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918 49 + #define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff) 50 + #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) 51 + #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C 52 + 53 + static DesignwarePCIEHost * 54 + designware_pcie_root_to_host(DesignwarePCIERoot *root) 55 + { 56 + BusState *bus = qdev_get_parent_bus(DEVICE(root)); 57 + return DESIGNWARE_PCIE_HOST(bus->parent); 58 + } 59 + 60 + static void designware_pcie_root_msi_write(void *opaque, hwaddr addr, 61 + uint64_t val, unsigned len) 62 + { 63 + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque); 64 + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); 65 + 66 + root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable; 67 + 68 + if (root->msi.intr[0].status & ~root->msi.intr[0].mask) { 69 + qemu_set_irq(host->pci.irqs[0], 1); 70 + } 71 + } 72 + 73 + static const MemoryRegionOps designware_pci_host_msi_ops = { 74 + .write = designware_pcie_root_msi_write, 75 + .endianness = DEVICE_LITTLE_ENDIAN, 76 + .valid = { 77 + .min_access_size = 4, 78 + .max_access_size = 4, 79 + }, 80 + }; 81 + 82 + static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root) 83 + 84 + { 85 + MemoryRegion *mem = &root->msi.iomem; 86 + const uint64_t base = root->msi.base; 87 + const bool enable = root->msi.intr[0].enable; 88 + 89 + memory_region_set_address(mem, base); 90 + memory_region_set_enabled(mem, enable); 91 + } 92 + 93 + static DesignwarePCIEViewport * 94 + designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root) 95 + { 96 + const unsigned int idx = root->atu_viewport & 0xF; 97 + const unsigned int dir = 98 + !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND); 99 + return &root->viewports[dir][idx]; 100 + } 101 + 102 + static uint32_t 103 + designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) 104 + { 105 + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); 106 + DesignwarePCIEViewport *viewport = 107 + designware_pcie_root_get_current_viewport(root); 108 + 109 + uint32_t val; 110 + 111 + switch (address) { 112 + case DESIGNWARE_PCIE_PORT_LINK_CONTROL: 113 + /* 114 + * Linux guest uses this register only to configure number of 115 + * PCIE lane (which in our case is irrelevant) and doesn't 116 + * really care about the value it reads from this register 117 + */ 118 + val = 0xDEADBEEF; 119 + break; 120 + 121 + case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: 122 + /* 123 + * To make sure that any code in guest waiting for speed 124 + * change does not time out we always report 125 + * PORT_LOGIC_SPEED_CHANGE as set 126 + */ 127 + val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE; 128 + break; 129 + 130 + case DESIGNWARE_PCIE_MSI_ADDR_LO: 131 + val = root->msi.base; 132 + break; 133 + 134 + case DESIGNWARE_PCIE_MSI_ADDR_HI: 135 + val = root->msi.base >> 32; 136 + break; 137 + 138 + case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: 139 + val = root->msi.intr[0].enable; 140 + break; 141 + 142 + case DESIGNWARE_PCIE_MSI_INTR0_MASK: 143 + val = root->msi.intr[0].mask; 144 + break; 145 + 146 + case DESIGNWARE_PCIE_MSI_INTR0_STATUS: 147 + val = root->msi.intr[0].status; 148 + break; 149 + 150 + case DESIGNWARE_PCIE_PHY_DEBUG_R1: 151 + val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; 152 + break; 153 + 154 + case DESIGNWARE_PCIE_ATU_VIEWPORT: 155 + val = root->atu_viewport; 156 + break; 157 + 158 + case DESIGNWARE_PCIE_ATU_LOWER_BASE: 159 + val = viewport->base; 160 + break; 161 + 162 + case DESIGNWARE_PCIE_ATU_UPPER_BASE: 163 + val = viewport->base >> 32; 164 + break; 165 + 166 + case DESIGNWARE_PCIE_ATU_LOWER_TARGET: 167 + val = viewport->target; 168 + break; 169 + 170 + case DESIGNWARE_PCIE_ATU_UPPER_TARGET: 171 + val = viewport->target >> 32; 172 + break; 173 + 174 + case DESIGNWARE_PCIE_ATU_LIMIT: 175 + val = viewport->limit; 176 + break; 177 + 178 + case DESIGNWARE_PCIE_ATU_CR1: 179 + case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */ 180 + val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) / 181 + sizeof(uint32_t)]; 182 + break; 183 + 184 + default: 185 + val = pci_default_read_config(d, address, len); 186 + break; 187 + } 188 + 189 + return val; 190 + } 191 + 192 + static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr, 193 + uint64_t *val, unsigned len) 194 + { 195 + DesignwarePCIEViewport *viewport = opaque; 196 + DesignwarePCIERoot *root = viewport->root; 197 + 198 + const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target); 199 + const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target); 200 + PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root)); 201 + PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn); 202 + 203 + if (pcidev) { 204 + addr &= pci_config_size(pcidev) - 1; 205 + 206 + if (val) { 207 + pci_host_config_write_common(pcidev, addr, 208 + pci_config_size(pcidev), 209 + *val, len); 210 + } else { 211 + return pci_host_config_read_common(pcidev, addr, 212 + pci_config_size(pcidev), 213 + len); 214 + } 215 + } 216 + 217 + return UINT64_MAX; 218 + } 219 + 220 + static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr, 221 + unsigned len) 222 + { 223 + return designware_pcie_root_data_access(opaque, addr, NULL, len); 224 + } 225 + 226 + static void designware_pcie_root_data_write(void *opaque, hwaddr addr, 227 + uint64_t val, unsigned len) 228 + { 229 + designware_pcie_root_data_access(opaque, addr, &val, len); 230 + } 231 + 232 + static const MemoryRegionOps designware_pci_host_conf_ops = { 233 + .read = designware_pcie_root_data_read, 234 + .write = designware_pcie_root_data_write, 235 + .endianness = DEVICE_LITTLE_ENDIAN, 236 + .valid = { 237 + .min_access_size = 1, 238 + .max_access_size = 4, 239 + }, 240 + }; 241 + 242 + static void designware_pcie_update_viewport(DesignwarePCIERoot *root, 243 + DesignwarePCIEViewport *viewport) 244 + { 245 + const uint64_t target = viewport->target; 246 + const uint64_t base = viewport->base; 247 + const uint64_t size = (uint64_t)viewport->limit - base + 1; 248 + const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE; 249 + 250 + MemoryRegion *current, *other; 251 + 252 + if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) { 253 + current = &viewport->mem; 254 + other = &viewport->cfg; 255 + memory_region_set_alias_offset(current, target); 256 + } else { 257 + current = &viewport->cfg; 258 + other = &viewport->mem; 259 + } 260 + 261 + /* 262 + * An outbound viewport can be reconfigure from being MEM to CFG, 263 + * to account for that we disable the "other" memory region that 264 + * becomes unused due to that fact. 265 + */ 266 + memory_region_set_enabled(other, false); 267 + if (enabled) { 268 + memory_region_set_size(current, size); 269 + memory_region_set_address(current, base); 270 + } 271 + memory_region_set_enabled(current, enabled); 272 + } 273 + 274 + static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, 275 + uint32_t val, int len) 276 + { 277 + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); 278 + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); 279 + DesignwarePCIEViewport *viewport = 280 + designware_pcie_root_get_current_viewport(root); 281 + 282 + switch (address) { 283 + case DESIGNWARE_PCIE_PORT_LINK_CONTROL: 284 + case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: 285 + case DESIGNWARE_PCIE_PHY_DEBUG_R1: 286 + /* No-op */ 287 + break; 288 + 289 + case DESIGNWARE_PCIE_MSI_ADDR_LO: 290 + root->msi.base &= 0xFFFFFFFF00000000ULL; 291 + root->msi.base |= val; 292 + break; 293 + 294 + case DESIGNWARE_PCIE_MSI_ADDR_HI: 295 + root->msi.base &= 0x00000000FFFFFFFFULL; 296 + root->msi.base |= (uint64_t)val << 32; 297 + break; 298 + 299 + case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: { 300 + const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val; 301 + 302 + root->msi.intr[0].enable = val; 303 + 304 + if (update_msi_mapping) { 305 + designware_pcie_root_update_msi_mapping(root); 306 + } 307 + break; 308 + } 309 + 310 + case DESIGNWARE_PCIE_MSI_INTR0_MASK: 311 + root->msi.intr[0].mask = val; 312 + break; 313 + 314 + case DESIGNWARE_PCIE_MSI_INTR0_STATUS: 315 + root->msi.intr[0].status ^= val; 316 + if (!root->msi.intr[0].status) { 317 + qemu_set_irq(host->pci.irqs[0], 0); 318 + } 319 + break; 320 + 321 + case DESIGNWARE_PCIE_ATU_VIEWPORT: 322 + root->atu_viewport = val; 323 + break; 324 + 325 + case DESIGNWARE_PCIE_ATU_LOWER_BASE: 326 + viewport->base &= 0xFFFFFFFF00000000ULL; 327 + viewport->base |= val; 328 + break; 329 + 330 + case DESIGNWARE_PCIE_ATU_UPPER_BASE: 331 + viewport->base &= 0x00000000FFFFFFFFULL; 332 + viewport->base |= (uint64_t)val << 32; 333 + break; 334 + 335 + case DESIGNWARE_PCIE_ATU_LOWER_TARGET: 336 + viewport->target &= 0xFFFFFFFF00000000ULL; 337 + viewport->target |= val; 338 + break; 339 + 340 + case DESIGNWARE_PCIE_ATU_UPPER_TARGET: 341 + viewport->target &= 0x00000000FFFFFFFFULL; 342 + viewport->target |= val; 343 + break; 344 + 345 + case DESIGNWARE_PCIE_ATU_LIMIT: 346 + viewport->limit = val; 347 + break; 348 + 349 + case DESIGNWARE_PCIE_ATU_CR1: 350 + viewport->cr[0] = val; 351 + break; 352 + case DESIGNWARE_PCIE_ATU_CR2: 353 + viewport->cr[1] = val; 354 + designware_pcie_update_viewport(root, viewport); 355 + break; 356 + 357 + default: 358 + pci_bridge_write_config(d, address, val, len); 359 + break; 360 + } 361 + } 362 + 363 + static char *designware_pcie_viewport_name(const char *direction, 364 + unsigned int i, 365 + const char *type) 366 + { 367 + return g_strdup_printf("PCI %s Viewport %u [%s]", 368 + direction, i, type); 369 + } 370 + 371 + static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) 372 + { 373 + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev); 374 + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); 375 + MemoryRegion *address_space = &host->pci.memory; 376 + PCIBridge *br = PCI_BRIDGE(dev); 377 + DesignwarePCIEViewport *viewport; 378 + /* 379 + * Dummy values used for initial configuration of MemoryRegions 380 + * that belong to a given viewport 381 + */ 382 + const hwaddr dummy_offset = 0; 383 + const uint64_t dummy_size = 4; 384 + size_t i; 385 + 386 + br->bus_name = "dw-pcie"; 387 + 388 + pci_set_word(dev->config + PCI_COMMAND, 389 + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 390 + 391 + pci_config_set_interrupt_pin(dev->config, 1); 392 + pci_bridge_initfn(dev, TYPE_PCIE_BUS); 393 + 394 + pcie_port_init_reg(dev); 395 + 396 + pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT, 397 + 0, &error_fatal); 398 + 399 + msi_nonbroken = true; 400 + msi_init(dev, 0x50, 32, true, true, &error_fatal); 401 + 402 + for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) { 403 + MemoryRegion *source, *destination, *mem; 404 + const char *direction; 405 + char *name; 406 + 407 + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i]; 408 + viewport->inbound = true; 409 + viewport->base = 0x0000000000000000ULL; 410 + viewport->target = 0x0000000000000000ULL; 411 + viewport->limit = UINT32_MAX; 412 + viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; 413 + 414 + source = &host->pci.address_space_root; 415 + destination = get_system_memory(); 416 + direction = "Inbound"; 417 + 418 + /* 419 + * Configure MemoryRegion implementing PCI -> CPU memory 420 + * access 421 + */ 422 + mem = &viewport->mem; 423 + name = designware_pcie_viewport_name(direction, i, "MEM"); 424 + memory_region_init_alias(mem, OBJECT(root), name, destination, 425 + dummy_offset, dummy_size); 426 + memory_region_add_subregion_overlap(source, dummy_offset, mem, -1); 427 + memory_region_set_enabled(mem, false); 428 + g_free(name); 429 + 430 + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i]; 431 + viewport->root = root; 432 + viewport->inbound = false; 433 + viewport->base = 0x0000000000000000ULL; 434 + viewport->target = 0x0000000000000000ULL; 435 + viewport->limit = UINT32_MAX; 436 + viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; 437 + 438 + destination = &host->pci.memory; 439 + direction = "Outbound"; 440 + source = get_system_memory(); 441 + 442 + /* 443 + * Configure MemoryRegion implementing CPU -> PCI memory 444 + * access 445 + */ 446 + mem = &viewport->mem; 447 + name = designware_pcie_viewport_name(direction, i, "MEM"); 448 + memory_region_init_alias(mem, OBJECT(root), name, destination, 449 + dummy_offset, dummy_size); 450 + memory_region_add_subregion(source, dummy_offset, mem); 451 + memory_region_set_enabled(mem, false); 452 + g_free(name); 453 + 454 + /* 455 + * Configure MemoryRegion implementing access to configuration 456 + * space 457 + */ 458 + mem = &viewport->cfg; 459 + name = designware_pcie_viewport_name(direction, i, "CFG"); 460 + memory_region_init_io(&viewport->cfg, OBJECT(root), 461 + &designware_pci_host_conf_ops, 462 + viewport, name, dummy_size); 463 + memory_region_add_subregion(source, dummy_offset, mem); 464 + memory_region_set_enabled(mem, false); 465 + g_free(name); 466 + } 467 + 468 + /* 469 + * If no inbound iATU windows are configured, HW defaults to 470 + * letting inbound TLPs to pass in. We emulate that by exlicitly 471 + * configuring first inbound window to cover all of target's 472 + * address space. 473 + * 474 + * NOTE: This will not work correctly for the case when first 475 + * configured inbound window is window 0 476 + */ 477 + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0]; 478 + viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE; 479 + designware_pcie_update_viewport(root, viewport); 480 + 481 + memory_region_init_io(&root->msi.iomem, OBJECT(root), 482 + &designware_pci_host_msi_ops, 483 + root, "pcie-msi", 0x4); 484 + /* 485 + * We initially place MSI interrupt I/O region a adress 0 and 486 + * disable it. It'll be later moved to correct offset and enabled 487 + * in designware_pcie_root_update_msi_mapping() as a part of 488 + * initialization done by guest OS 489 + */ 490 + memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem); 491 + memory_region_set_enabled(&root->msi.iomem, false); 492 + } 493 + 494 + static void designware_pcie_set_irq(void *opaque, int irq_num, int level) 495 + { 496 + DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque); 497 + 498 + qemu_set_irq(host->pci.irqs[irq_num], level); 499 + } 500 + 501 + static const char * 502 + designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) 503 + { 504 + return "0000:00"; 505 + } 506 + 507 + static const VMStateDescription vmstate_designware_pcie_msi_bank = { 508 + .name = "designware-pcie-msi-bank", 509 + .version_id = 1, 510 + .minimum_version_id = 1, 511 + .fields = (VMStateField[]) { 512 + VMSTATE_UINT32(enable, DesignwarePCIEMSIBank), 513 + VMSTATE_UINT32(mask, DesignwarePCIEMSIBank), 514 + VMSTATE_UINT32(status, DesignwarePCIEMSIBank), 515 + VMSTATE_END_OF_LIST() 516 + } 517 + }; 518 + 519 + static const VMStateDescription vmstate_designware_pcie_msi = { 520 + .name = "designware-pcie-msi", 521 + .version_id = 1, 522 + .minimum_version_id = 1, 523 + .fields = (VMStateField[]) { 524 + VMSTATE_UINT64(base, DesignwarePCIEMSI), 525 + VMSTATE_STRUCT_ARRAY(intr, 526 + DesignwarePCIEMSI, 527 + DESIGNWARE_PCIE_NUM_MSI_BANKS, 528 + 1, 529 + vmstate_designware_pcie_msi_bank, 530 + DesignwarePCIEMSIBank), 531 + VMSTATE_END_OF_LIST() 532 + } 533 + }; 534 + 535 + static const VMStateDescription vmstate_designware_pcie_viewport = { 536 + .name = "designware-pcie-viewport", 537 + .version_id = 1, 538 + .minimum_version_id = 1, 539 + .fields = (VMStateField[]) { 540 + VMSTATE_UINT64(base, DesignwarePCIEViewport), 541 + VMSTATE_UINT64(target, DesignwarePCIEViewport), 542 + VMSTATE_UINT32(limit, DesignwarePCIEViewport), 543 + VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2), 544 + VMSTATE_END_OF_LIST() 545 + } 546 + }; 547 + 548 + static const VMStateDescription vmstate_designware_pcie_root = { 549 + .name = "designware-pcie-root", 550 + .version_id = 1, 551 + .minimum_version_id = 1, 552 + .fields = (VMStateField[]) { 553 + VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), 554 + VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot), 555 + VMSTATE_STRUCT_2DARRAY(viewports, 556 + DesignwarePCIERoot, 557 + 2, 558 + DESIGNWARE_PCIE_NUM_VIEWPORTS, 559 + 1, 560 + vmstate_designware_pcie_viewport, 561 + DesignwarePCIEViewport), 562 + VMSTATE_STRUCT(msi, 563 + DesignwarePCIERoot, 564 + 1, 565 + vmstate_designware_pcie_msi, 566 + DesignwarePCIEMSI), 567 + VMSTATE_END_OF_LIST() 568 + } 569 + }; 570 + 571 + static void designware_pcie_root_class_init(ObjectClass *klass, void *data) 572 + { 573 + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 574 + DeviceClass *dc = DEVICE_CLASS(klass); 575 + 576 + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 577 + 578 + k->vendor_id = PCI_VENDOR_ID_SYNOPSYS; 579 + k->device_id = 0xABCD; 580 + k->revision = 0; 581 + k->class_id = PCI_CLASS_BRIDGE_PCI; 582 + k->is_bridge = true; 583 + k->exit = pci_bridge_exitfn; 584 + k->realize = designware_pcie_root_realize; 585 + k->config_read = designware_pcie_root_config_read; 586 + k->config_write = designware_pcie_root_config_write; 587 + 588 + dc->reset = pci_bridge_reset; 589 + /* 590 + * PCI-facing part of the host bridge, not usable without the 591 + * host-facing part, which can't be device_add'ed, yet. 592 + */ 593 + dc->user_creatable = false; 594 + dc->vmsd = &vmstate_designware_pcie_root; 595 + } 596 + 597 + static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr, 598 + unsigned int size) 599 + { 600 + PCIHostState *pci = PCI_HOST_BRIDGE(opaque); 601 + PCIDevice *device = pci_find_device(pci->bus, 0, 0); 602 + 603 + return pci_host_config_read_common(device, 604 + addr, 605 + pci_config_size(device), 606 + size); 607 + } 608 + 609 + static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr, 610 + uint64_t val, unsigned int size) 611 + { 612 + PCIHostState *pci = PCI_HOST_BRIDGE(opaque); 613 + PCIDevice *device = pci_find_device(pci->bus, 0, 0); 614 + 615 + return pci_host_config_write_common(device, 616 + addr, 617 + pci_config_size(device), 618 + val, size); 619 + } 620 + 621 + static const MemoryRegionOps designware_pci_mmio_ops = { 622 + .read = designware_pcie_host_mmio_read, 623 + .write = designware_pcie_host_mmio_write, 624 + .endianness = DEVICE_LITTLE_ENDIAN, 625 + .impl = { 626 + /* 627 + * Our device would not work correctly if the guest was doing 628 + * unaligned access. This might not be a limitation on the real 629 + * device but in practice there is no reason for a guest to access 630 + * this device unaligned. 631 + */ 632 + .min_access_size = 4, 633 + .max_access_size = 4, 634 + .unaligned = false, 635 + }, 636 + }; 637 + 638 + static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque, 639 + int devfn) 640 + { 641 + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque); 642 + 643 + return &s->pci.address_space; 644 + } 645 + 646 + static void designware_pcie_host_realize(DeviceState *dev, Error **errp) 647 + { 648 + PCIHostState *pci = PCI_HOST_BRIDGE(dev); 649 + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev); 650 + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 651 + size_t i; 652 + 653 + for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) { 654 + sysbus_init_irq(sbd, &s->pci.irqs[i]); 655 + } 656 + 657 + memory_region_init_io(&s->mmio, 658 + OBJECT(s), 659 + &designware_pci_mmio_ops, 660 + s, 661 + "pcie.reg", 4 * 1024); 662 + sysbus_init_mmio(sbd, &s->mmio); 663 + 664 + memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16); 665 + memory_region_init(&s->pci.memory, OBJECT(s), 666 + "pcie-bus-memory", 667 + UINT64_MAX); 668 + 669 + pci->bus = pci_register_root_bus(dev, "pcie", 670 + designware_pcie_set_irq, 671 + pci_swizzle_map_irq_fn, 672 + s, 673 + &s->pci.memory, 674 + &s->pci.io, 675 + 0, 4, 676 + TYPE_PCIE_BUS); 677 + 678 + memory_region_init(&s->pci.address_space_root, 679 + OBJECT(s), 680 + "pcie-bus-address-space-root", 681 + UINT64_MAX); 682 + memory_region_add_subregion(&s->pci.address_space_root, 683 + 0x0, &s->pci.memory); 684 + address_space_init(&s->pci.address_space, 685 + &s->pci.address_space_root, 686 + "pcie-bus-address-space"); 687 + pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); 688 + 689 + qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus)); 690 + qdev_init_nofail(DEVICE(&s->root)); 691 + } 692 + 693 + static const VMStateDescription vmstate_designware_pcie_host = { 694 + .name = "designware-pcie-host", 695 + .version_id = 1, 696 + .minimum_version_id = 1, 697 + .fields = (VMStateField[]) { 698 + VMSTATE_STRUCT(root, 699 + DesignwarePCIEHost, 700 + 1, 701 + vmstate_designware_pcie_root, 702 + DesignwarePCIERoot), 703 + VMSTATE_END_OF_LIST() 704 + } 705 + }; 706 + 707 + static void designware_pcie_host_class_init(ObjectClass *klass, void *data) 708 + { 709 + DeviceClass *dc = DEVICE_CLASS(klass); 710 + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); 711 + 712 + hc->root_bus_path = designware_pcie_host_root_bus_path; 713 + dc->realize = designware_pcie_host_realize; 714 + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 715 + dc->fw_name = "pci"; 716 + dc->vmsd = &vmstate_designware_pcie_host; 717 + } 718 + 719 + static void designware_pcie_host_init(Object *obj) 720 + { 721 + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj); 722 + DesignwarePCIERoot *root = &s->root; 723 + 724 + object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT); 725 + object_property_add_child(obj, "root", OBJECT(root), NULL); 726 + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); 727 + qdev_prop_set_bit(DEVICE(root), "multifunction", false); 728 + } 729 + 730 + static const TypeInfo designware_pcie_root_info = { 731 + .name = TYPE_DESIGNWARE_PCIE_ROOT, 732 + .parent = TYPE_PCI_BRIDGE, 733 + .instance_size = sizeof(DesignwarePCIERoot), 734 + .class_init = designware_pcie_root_class_init, 735 + .interfaces = (InterfaceInfo[]) { 736 + { INTERFACE_PCIE_DEVICE }, 737 + { } 738 + }, 739 + }; 740 + 741 + static const TypeInfo designware_pcie_host_info = { 742 + .name = TYPE_DESIGNWARE_PCIE_HOST, 743 + .parent = TYPE_PCI_HOST_BRIDGE, 744 + .instance_size = sizeof(DesignwarePCIEHost), 745 + .instance_init = designware_pcie_host_init, 746 + .class_init = designware_pcie_host_class_init, 747 + }; 748 + 749 + static void designware_pcie_register(void) 750 + { 751 + type_register_static(&designware_pcie_root_info); 752 + type_register_static(&designware_pcie_host_info); 753 + } 754 + type_init(designware_pcie_register)
+102
include/hw/pci-host/designware.h
··· 1 + /* 2 + * Copyright (c) 2017, Impinj, Inc. 3 + * 4 + * Designware PCIe IP block emulation 5 + * 6 + * This library is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU Lesser General Public 8 + * License as published by the Free Software Foundation; either 9 + * version 2 of the License, or (at your option) any later version. 10 + * 11 + * This library is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 + * Lesser General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU Lesser General Public 17 + * License along with this library; if not, see 18 + * <http://www.gnu.org/licenses/>. 19 + */ 20 + 21 + #ifndef DESIGNWARE_H 22 + #define DESIGNWARE_H 23 + 24 + #include "hw/hw.h" 25 + #include "hw/sysbus.h" 26 + #include "hw/pci/pci.h" 27 + #include "hw/pci/pci_bus.h" 28 + #include "hw/pci/pcie_host.h" 29 + #include "hw/pci/pci_bridge.h" 30 + 31 + #define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host" 32 + #define DESIGNWARE_PCIE_HOST(obj) \ 33 + OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST) 34 + 35 + #define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root" 36 + #define DESIGNWARE_PCIE_ROOT(obj) \ 37 + OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT) 38 + 39 + struct DesignwarePCIERoot; 40 + typedef struct DesignwarePCIERoot DesignwarePCIERoot; 41 + 42 + typedef struct DesignwarePCIEViewport { 43 + DesignwarePCIERoot *root; 44 + 45 + MemoryRegion cfg; 46 + MemoryRegion mem; 47 + 48 + uint64_t base; 49 + uint64_t target; 50 + uint32_t limit; 51 + uint32_t cr[2]; 52 + 53 + bool inbound; 54 + } DesignwarePCIEViewport; 55 + 56 + typedef struct DesignwarePCIEMSIBank { 57 + uint32_t enable; 58 + uint32_t mask; 59 + uint32_t status; 60 + } DesignwarePCIEMSIBank; 61 + 62 + typedef struct DesignwarePCIEMSI { 63 + uint64_t base; 64 + MemoryRegion iomem; 65 + 66 + #define DESIGNWARE_PCIE_NUM_MSI_BANKS 1 67 + 68 + DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS]; 69 + } DesignwarePCIEMSI; 70 + 71 + struct DesignwarePCIERoot { 72 + PCIBridge parent_obj; 73 + 74 + uint32_t atu_viewport; 75 + 76 + #define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND 0 77 + #define DESIGNWARE_PCIE_VIEWPORT_INBOUND 1 78 + #define DESIGNWARE_PCIE_NUM_VIEWPORTS 4 79 + 80 + DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS]; 81 + DesignwarePCIEMSI msi; 82 + }; 83 + 84 + typedef struct DesignwarePCIEHost { 85 + PCIHostState parent_obj; 86 + 87 + DesignwarePCIERoot root; 88 + 89 + struct { 90 + AddressSpace address_space; 91 + MemoryRegion address_space_root; 92 + 93 + MemoryRegion memory; 94 + MemoryRegion io; 95 + 96 + qemu_irq irqs[4]; 97 + } pci; 98 + 99 + MemoryRegion mmio; 100 + } DesignwarePCIEHost; 101 + 102 + #endif /* DESIGNWARE_H */
+2
include/hw/pci/pci_ids.h
··· 269 269 #define PCI_VENDOR_ID_VMWARE 0x15ad 270 270 #define PCI_DEVICE_ID_VMWARE_PVRDMA 0x0820 271 271 272 + #define PCI_VENDOR_ID_SYNOPSYS 0x16C3 273 + 272 274 #endif