/* default write_config function for PCI-to-PCI bridge */ void pci_bridge_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { PCIBridge *s = container_of(d, PCIBridge, dev); uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); uint16_t newctl; pci_default_write_config(d, address, val, len); if (ranges_overlap(address, len, PCI_COMMAND, 2) || /* io base/limit */ ranges_overlap(address, len, PCI_IO_BASE, 2) || /* memory base/limit, prefetchable base/limit and io base/limit upper 16 */ ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { pci_bridge_update_mappings(s); } newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { /* Trigger hot reset on 0->1 transition. */ pci_bus_reset(&s->sec_bus); } }
/* * return value: * true: error message needs to be sent up * false: error message is masked * * 6.2.6 Error Message Control * Figure 6-3 * all pci express devices part */ static bool pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) { if (!(pcie_aer_msg_is_uncor(msg) && (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { return false; } /* Signaled System Error * * 7.5.1.1 Command register * Bit 8 SERR# Enable * * When Set, this bit enables reporting of Non-fatal and Fatal * errors detected by the Function to the Root Complex. Note that * errors are reported if enabled either through this bit or through * the PCI Express specific bits in the Device Control register (see * Section 7.8.4). */ pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_SIG_SYSTEM_ERROR); if (!(msg->severity & pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { return false; } /* send up error message */ return true; }
static void hotplug_event_update_event_status(PCIDevice *dev) { uint32_t pos = dev->exp.exp_cap; uint8_t *exp_cap = dev->config + pos; uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); }
static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) { PCIDevice *pd = PCI_DEVICE(br); PCIBus *parent = pd->bus; PCIBridgeWindows *w = g_new(PCIBridgeWindows, 1); uint16_t cmd = pci_get_word(pd->config + PCI_COMMAND); pci_bridge_init_alias(br, &w->alias_pref_mem, PCI_BASE_ADDRESS_MEM_PREFETCH, "pci_bridge_pref_mem", &br->address_space_mem, parent->address_space_mem, cmd & PCI_COMMAND_MEMORY); pci_bridge_init_alias(br, &w->alias_mem, PCI_BASE_ADDRESS_SPACE_MEMORY, "pci_bridge_mem", &br->address_space_mem, parent->address_space_mem, cmd & PCI_COMMAND_MEMORY); pci_bridge_init_alias(br, &w->alias_io, PCI_BASE_ADDRESS_SPACE_IO, "pci_bridge_io", &br->address_space_io, parent->address_space_io, cmd & PCI_COMMAND_IO); pci_bridge_init_vga_aliases(br, parent, w->alias_vga); return w; }
static void pci_bridge_region_init(PCIBridge *br) { PCIBus *parent = br->dev.bus; uint16_t cmd = pci_get_word(br->dev.config + PCI_COMMAND); pci_bridge_init_alias(br, &br->alias_pref_mem, PCI_BASE_ADDRESS_MEM_PREFETCH, "pci_bridge_pref_mem", &br->address_space_mem, parent->address_space_mem, cmd & PCI_COMMAND_MEMORY); pci_bridge_init_alias(br, &br->alias_mem, PCI_BASE_ADDRESS_SPACE_MEMORY, "pci_bridge_mem", &br->address_space_mem, parent->address_space_mem, cmd & PCI_COMMAND_MEMORY); pci_bridge_init_alias(br, &br->alias_io, PCI_BASE_ADDRESS_SPACE_IO, "pci_bridge_io", &br->address_space_io, parent->address_space_io, cmd & PCI_COMMAND_IO); /* TODO: optinal VGA and VGA palette snooping support. */ }
uint8_t pcie_cap_get_type(const PCIDevice *dev) { uint32_t pos = dev->exp.exp_cap; assert(pos > 0); return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; }
/* * Sync the PCIe Link Status negotiated speed and width of a bridge with the * downstream device. If downstream device is not present, re-write with the * Link Capability fields. If downstream device reports invalid width or * speed, replace with minimum values (LnkSta fields are RsvdZ on VFs but such * values interfere with PCIe native hotplug detecting new devices). Limit * width and speed to bridge capabilities for compatibility. Use config_read * to access the downstream device since it could be an assigned device with * volatile link information. */ void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) { PCIBridge *br = PCI_BRIDGE(bridge_dev); PCIBus *bus = pci_bridge_get_sec_bus(br); PCIDevice *target = bus->devices[0]; uint8_t *exp_cap = bridge_dev->config + bridge_dev->exp.exp_cap; uint16_t lnksta, lnkcap = pci_get_word(exp_cap + PCI_EXP_LNKCAP); if (!target || !target->exp.exp_cap) { lnksta = lnkcap; } else { lnksta = target->config_read(target, target->exp.exp_cap + PCI_EXP_LNKSTA, sizeof(lnksta)); if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) { lnksta &= ~PCI_EXP_LNKSTA_NLW; lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW; } else if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); } if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) { lnksta &= ~PCI_EXP_LNKSTA_CLS; lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS; } else if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); } } pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, lnksta & (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW)); }
void pcie_cap_slot_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { uint32_t pos = dev->exp.exp_cap; uint8_t *exp_cap = dev->config + pos; uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) { hotplug_event_clear(dev); } if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { return; } if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_EIC)) { sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta); PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: " "sltsta -> 0x%02"PRIx16"\n", sltsta); } /* * If the slot is polulated, power indicator is off and power * controller is off, it is safe to detach the devices. */ if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) && ((val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF)) { PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); pci_for_each_device(sec_bus, pci_bus_num(sec_bus), pcie_unplug_device, NULL); pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); } hotplug_event_notify(dev); /* * 6.7.3.2 Command Completed Events * * Software issues a command to a hot-plug capable Downstream Port by * issuing a write transaction that targets any portion of the Port’s Slot * Control register. A single write to the Slot Control register is * considered to be a single command, even if the write affects more than * one field in the Slot Control register. In response to this transaction, * the Port must carry out the requested actions and then set the * associated status field for the command completed event. */ /* Real hardware might take a while to complete requested command because * physical movement would be involved like locking the electromechanical * lock. However in our case, command is completed instantaneously above, * so send a command completion event right now. */ pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); }
static void unplug_nic(PCIBus *b, PCIDevice *d) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET) { qdev_unplug(&(d->qdev)); } }
static void unplug_disks(PCIBus *b, PCIDevice *d) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_STORAGE_IDE) { qdev_unplug(&(d->qdev)); } }
static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET && strcmp(d->name, "xen-pci-passthrough") != 0) { qdev_free(&d->qdev); } }
static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) { /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_STORAGE_IDE && strcmp(d->name, "xen-pci-passthrough") != 0) { qdev_unplug(&(d->qdev), NULL); } }
static void unplug_nic(PCIBus *b, PCIDevice *d) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET) { /* Until qdev_free includes a call to object_unparent, we call it here */ object_unparent(&d->qdev.parent_obj); qdev_free(&d->qdev); } }
static uint32_t pci_config_get_io_base(const PCIDevice *d, uint32_t base, uint32_t base_upper16) { uint32_t val; val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; if (d->config[base] & PCI_IO_RANGE_TYPE_32) { val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; } return val; }
void pcie_cap_flr_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) { /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler so the handler can detect FLR by looking at this bit. */ pci_device_reset(dev); pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR); } }
static pcibus_t pci_config_get_pref_base(const PCIDevice *d, uint32_t base, uint32_t upper) { pcibus_t tmp; pcibus_t val; tmp = (pcibus_t)pci_get_word(d->config + base); val = (tmp & PCI_PREF_RANGE_MASK) << 16; if (tmp & PCI_PREF_RANGE_TYPE_64) { val |= (pcibus_t)pci_get_long(d->config + upper) << 32; } return val; }
static void pcie_cap_slot_plug_common(PCIDevice *hotplug_dev, DeviceState *dev, Error **errp) { uint8_t *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta); if (sltsta & PCI_EXP_SLTSTA_EIS) { /* the slot is electromechanically locked. * This error is propagated up to qdev and then to HMP/QMP. */ error_setg_errno(errp, EBUSY, "slot is electromechanically locked"); } }
static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int l) { EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); bool busmaster; pci_default_write_config(dev, addr, val, l); if (!range_covers_byte(addr, l, PCI_COMMAND)) { return; } busmaster = pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_MASTER; i->ehci.dma = busmaster ? pci_dma_context(dev) : NULL; }
/* * return value: * true: error message is sent up * false: error message is masked * * 6.2.6 Error Message Control * Figure 6-3 * virtual pci bridge part */ static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) { uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); if (pcie_aer_msg_is_uncor(msg)) { /* Received System Error */ pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, PCI_SEC_STATUS_RCV_SYSTEM_ERROR); } if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { return false; } return true; }
static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) { PCIDevice *dev = inj->dev; uint16_t cmd; if (is_fatal) { inj->devsta |= PCI_EXP_DEVSTA_FED; } else { inj->devsta |= PCI_EXP_DEVSTA_NFED; } if (inj->unsupported_request) { inj->devsta |= PCI_EXP_DEVSTA_URD; } pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); if (inj->aer_cap) { uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); if (mask & inj->error_status) { pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, inj->error_status); return false; } inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, inj->error_status); } cmd = pci_get_word(dev->config + PCI_COMMAND); if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { return false; } if (is_fatal) { if (!((cmd & PCI_COMMAND_SERR) || (inj->devctl & PCI_EXP_DEVCTL_FERE))) { return false; } inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; } else { if (!((cmd & PCI_COMMAND_SERR) || (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { return false; } inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; } return true; }
static int pcie_cap_slot_hotplug(DeviceState *qdev, PCIDevice *pci_dev, PCIHotplugState state) { PCIDevice *d = PCI_DEVICE(qdev); uint8_t *exp_cap = d->config + d->exp.exp_cap; uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ if (state == PCI_COLDPLUG_ENABLED) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); return 0; } PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); if (sltsta & PCI_EXP_SLTSTA_EIS) { /* the slot is electromechanically locked. * This error is propagated up to qdev and then to HMP/QMP. */ return -EBUSY; } /* TODO: multifunction hot-plug. * Right now, only a device of function = 0 is allowed to be * hot plugged/unplugged. */ assert(PCI_FUNC(pci_dev->devfn) == 0); if (state == PCI_HOTPLUG_ENABLED) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); } else { object_unparent(OBJECT(pci_dev)); pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); } return 0; }
static void pci_bridge_init_vga_aliases(PCIBridge *br, PCIBus *parent, MemoryRegion *alias_vga) { PCIDevice *pd = PCI_DEVICE(br); uint16_t brctl = pci_get_word(pd->config + PCI_BRIDGE_CONTROL); memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_LO], OBJECT(br), "pci_bridge_vga_io_lo", &br->address_space_io, QEMU_PCI_VGA_IO_LO_BASE, QEMU_PCI_VGA_IO_LO_SIZE); memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_HI], OBJECT(br), "pci_bridge_vga_io_hi", &br->address_space_io, QEMU_PCI_VGA_IO_HI_BASE, QEMU_PCI_VGA_IO_HI_SIZE); memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_MEM], OBJECT(br), "pci_bridge_vga_mem", &br->address_space_mem, QEMU_PCI_VGA_MEM_BASE, QEMU_PCI_VGA_MEM_SIZE); if (brctl & PCI_BRIDGE_CTL_VGA) { pci_register_vga(pd, &alias_vga[QEMU_PCI_VGA_MEM], &alias_vga[QEMU_PCI_VGA_IO_LO], &alias_vga[QEMU_PCI_VGA_IO_HI]); } }
uint8_t pcie_cap_flags_get_vector(PCIDevice *dev) { return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) & PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT; }
static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base) { return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) << 16; }
/* * 6.2.6 Error Message Control * Figure 6-3 * root port part */ static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) { uint16_t cmd; uint8_t *aer_cap; uint32_t root_cmd; uint32_t root_status, prev_status; cmd = pci_get_word(dev->config + PCI_COMMAND); aer_cap = dev->config + dev->exp.aer_cap; root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); if (cmd & PCI_COMMAND_SERR) { /* System Error. * * The way to report System Error is platform specific and * it isn't implemented in qemu right now. * So just discard the error for now. * OS which cares of aer would receive errors via * native aer mechanims, so this wouldn't matter. */ } /* Errro Message Received: Root Error Status register */ switch (msg->severity) { case PCI_ERR_ROOT_CMD_COR_EN: if (root_status & PCI_ERR_ROOT_COR_RCV) { root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; } else { pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, msg->source_id); } root_status |= PCI_ERR_ROOT_COR_RCV; break; case PCI_ERR_ROOT_CMD_NONFATAL_EN: root_status |= PCI_ERR_ROOT_NONFATAL_RCV; break; case PCI_ERR_ROOT_CMD_FATAL_EN: if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { root_status |= PCI_ERR_ROOT_FIRST_FATAL; } root_status |= PCI_ERR_ROOT_FATAL_RCV; break; default: abort(); break; } if (pcie_aer_msg_is_uncor(msg)) { if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; } else { pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); } root_status |= PCI_ERR_ROOT_UNCOR_RCV; } pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); /* 6.2.4.1.2 Interrupt Generation */ /* All the above did was set some bits in the status register. * Specifically these that match message severity. * The below code relies on this fact. */ if (!(root_cmd & msg->severity) || (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { /* Condition is not being set or was already true so nothing to do. */ return; } pcie_aer_root_notify(dev); }
static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) { uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); return (pci_get_word(status) & msk) >> ctz32(msk); }