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); }
/* 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 = PCI_BRIDGE(d); 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) || /* vga enable */ ranges_overlap(address, len, PCI_BRIDGE_CONTROL, 2)) { 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. */ qbus_reset_all(&s->sec_bus.qbus); } }
/* * 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)); }
/* pci express slot for pci express root/downstream port PCI express capability slot registers */ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) { uint32_t pos = dev->exp.exp_cap; pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS, PCI_EXP_FLAGS_SLOT); pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, ~PCI_EXP_SLTCAP_PSN); pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | PCI_EXP_SLTCAP_EIP | PCI_EXP_SLTCAP_HPS | PCI_EXP_SLTCAP_HPC | PCI_EXP_SLTCAP_PIP | PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_ABP); if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, PCI_EXP_SLTCAP_PCP); pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC); pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC); } pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC); pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF); pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE); /* Although reading PCI_EXP_SLTCTL_EIC returns always 0, * make the bit writable here in order to detect 1b is written. * pcie_cap_slot_write_config() test-and-clear the bit, so * this bit always returns 0 to the guest. */ pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_EIC); pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, PCI_EXP_HP_EV_SUPPORTED); dev->exp.hpev_notified = false; qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), DEVICE(dev), NULL); }
static int pci_bridge_dev_initfn(PCIDevice *dev) { PCIBridge *br = PCI_BRIDGE(dev); PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); int err; pci_bridge_initfn(dev, TYPE_PCI_BUS); if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); if (err) { goto shpc_error; } } else { /* MSI is not applicable without SHPC */ bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); } err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); if (err) { goto slotid_error; } if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && msi_supported) { err = msi_init(dev, 0, 1, true, true); if (err < 0) { goto msi_error; } } if (shpc_present(dev)) { /* TODO: spec recommends using 64 bit prefetcheable BAR. * Check whether that works well. */ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); } return 0; msi_error: slotid_cap_cleanup(dev); slotid_error: if (shpc_present(dev)) { shpc_cleanup(dev, &bridge_dev->bar); } shpc_error: pci_bridge_exitfn(dev); return err; }
void pcie_cap_slot_reset(PCIDevice *dev) { uint8_t *exp_cap = dev->config + dev->exp.exp_cap; uint8_t port_type = pcie_cap_get_type(dev); assert(port_type == PCI_EXP_TYPE_DOWNSTREAM || port_type == PCI_EXP_TYPE_ROOT_PORT); PCIE_DEV_PRINTF(dev, "reset\n"); pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_EIC | PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE); pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_AIC_OFF); if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { /* Downstream ports enforce device number 0. */ bool populated = pci_bridge_get_sec_bus(PCI_BRIDGE(dev))->devices[0]; uint16_t pic; if (populated) { pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC); } else { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC); } pic = populated ? PCI_EXP_SLTCTL_PIC_ON : PCI_EXP_SLTCTL_PIC_OFF; pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, pic); } pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_EIS |/* on reset, the lock is released */ PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_ABP); hotplug_event_update_event_status(dev); }
PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, uint8_t port) { PCIDevice *d; PCIBridge *br; DeviceState *qdev; d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); if (!d) { return NULL; } br = PCI_BRIDGE(d); qdev = DEVICE(d); pci_bridge_map_irq(br, bus_name, map_irq); qdev_prop_set_uint8(qdev, "port", port); qdev_init_nofail(qdev); return PCIE_PORT(d); }
PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, uint8_t port, uint8_t chassis, uint16_t slot) { PCIDevice *d; PCIBridge *br; DeviceState *qdev; d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420"); if (!d) { return NULL; } br = PCI_BRIDGE(d); qdev = DEVICE(d); pci_bridge_map_irq(br, bus_name, map_irq); qdev_prop_set_uint8(qdev, "port", port); qdev_prop_set_uint8(qdev, "chassis", chassis); qdev_prop_set_uint16(qdev, "slot", slot); qdev_init_nofail(qdev); return PCIE_SLOT(d); }
static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp) { PCIBridge *br = PCI_BRIDGE(dev); PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); int err; Error *local_err = NULL; pci_bridge_initfn(dev, TYPE_PCI_BUS); if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0, errp); if (err) { goto shpc_error; } } else { /* MSI is not applicable without SHPC */ bridge_dev->msi = ON_OFF_AUTO_OFF; } err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0, errp); if (err) { goto slotid_error; } if (bridge_dev->msi != ON_OFF_AUTO_OFF) { /* it means SHPC exists, because MSI is needed by SHPC */ err = msi_init(dev, 0, 1, true, true, &local_err); /* Any error other than -ENOTSUP(board's MSI support is broken) * is a programming error */ assert(!err || err == -ENOTSUP); if (err && bridge_dev->msi == ON_OFF_AUTO_ON) { /* Can't satisfy user's explicit msi=on request, fail */ error_append_hint(&local_err, "You have to use msi=auto (default) " "or msi=off with this machine type.\n"); error_propagate(errp, local_err); goto msi_error; } assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO); /* With msi=auto, we fall back to MSI off silently */ error_free(local_err); } if (shpc_present(dev)) { /* TODO: spec recommends using 64 bit prefetcheable BAR. * Check whether that works well. */ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); } return; msi_error: slotid_cap_cleanup(dev); slotid_error: if (shpc_present(dev)) { shpc_cleanup(dev, &bridge_dev->bar); } shpc_error: pci_bridge_exitfn(dev); }
static void boston_mach_init(MachineState *machine) { DeviceState *dev; BostonState *s; Error *err = NULL; const char *cpu_model; MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg; MemoryRegion *sys_mem = get_system_memory(); XilinxPCIEHost *pcie2; PCIDevice *ahci; DriveInfo *hd[6]; Chardev *chr; int fw_size, fit_err; bool is_64b; if ((machine->ram_size % G_BYTE) || (machine->ram_size > (2 * G_BYTE))) { error_report("Memory size must be 1GB or 2GB"); exit(1); } cpu_model = machine->cpu_model ?: "I6400"; dev = qdev_create(NULL, TYPE_MIPS_BOSTON); qdev_init_nofail(dev); s = BOSTON(dev); s->mach = machine; s->cps = g_new0(MIPSCPSState, 1); if (!cpu_supports_cps_smp(cpu_model)) { error_report("Boston requires CPUs which support CPS"); exit(1); } is_64b = cpu_supports_isa(cpu_model, ISA_MIPS64); object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS); qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default()); object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err); object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err); object_property_set_bool(OBJECT(s->cps), true, "realized", &err); if (err != NULL) { error_report("%s", error_get_pretty(err)); exit(1); } sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s, "boston.flash", 128 * M_BYTE, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); ddr = g_new(MemoryRegion, 1); memory_region_allocate_system_memory(ddr, NULL, "boston.ddr", machine->ram_size); memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0); ddr_low_alias = g_new(MemoryRegion, 1); memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", ddr, 0, MIN(machine->ram_size, (256 * M_BYTE))); memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); xilinx_pcie_init(sys_mem, 0, 0x10000000, 32 * M_BYTE, 0x40000000, 1 * G_BYTE, get_cps_irq(s->cps, 2), false); xilinx_pcie_init(sys_mem, 1, 0x12000000, 32 * M_BYTE, 0x20000000, 512 * M_BYTE, get_cps_irq(s->cps, 1), false); pcie2 = xilinx_pcie_init(sys_mem, 2, 0x14000000, 32 * M_BYTE, 0x16000000, 1 * M_BYTE, get_cps_irq(s->cps, 0), true); platreg = g_new(MemoryRegion, 1); memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, "boston-platregs", 0x1000); memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0); if (!serial_hds[0]) { serial_hds[0] = qemu_chr_new("serial0", "null"); } s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2, get_cps_irq(s->cps, 3), 10000000, serial_hds[0], DEVICE_NATIVE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0); chr = qemu_chr_new("lcd", "vc:320x240"); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, boston_lcd_event, s, NULL, true); ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), true, TYPE_ICH9_AHCI); g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports); ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); ahci_ide_create_devs(ahci, hd); if (machine->firmware) { fw_size = load_image_targphys(machine->firmware, 0x1fc00000, 4 * M_BYTE); if (fw_size == -1) { error_printf("unable to load firmware image '%s'\n", machine->firmware); exit(1); } } else if (machine->kernel_filename) { fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); if (fit_err) { error_printf("unable to load FIT image\n"); exit(1); } gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000, s->kernel_entry, s->fdt_base, is_64b); } else if (!qtest_enabled()) { error_printf("Please provide either a -kernel or -bios argument\n"); exit(1); } }
PCI_MEMORY_RANGE_MASK & 0xffff); pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE, PCI_PREF_RANGE_MASK & 0xffff); pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, PCI_PREF_RANGE_MASK & 0xffff); pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); } /* default qdev initialization function for PCI-to-PCI bridge */ int pci_bridge_initfn(PCIDevice *dev, const char *typename) { PCIBus *parent = dev->bus; PCIBridge *br = PCI_BRIDGE(dev); PCIBus *sec_bus = &br->sec_bus; pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); /* * TODO: We implement VGA Enable in the Bridge Control Register * therefore per the PCI to PCI bridge spec we must also implement * VGA Palette Snooping. When done, set this bit writable: * * pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, * PCI_COMMAND_VGA_PALETTE); */ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);