summaryrefslogtreecommitdiffstats
path: root/qemu/hw/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/pci/pci.c')
-rw-r--r--qemu/hw/pci/pci.c97
1 files changed, 71 insertions, 26 deletions
diff --git a/qemu/hw/pci/pci.c b/qemu/hw/pci/pci.c
index a017614d4..bb605efae 100644
--- a/qemu/hw/pci/pci.c
+++ b/qemu/hw/pci/pci.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bridge.h"
@@ -38,6 +39,8 @@
#include "hw/pci/msix.h"
#include "exec/address-spaces.h"
#include "hw/hotplug.h"
+#include "hw/boards.h"
+#include "qemu/cutils.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -276,9 +279,9 @@ static void pcibus_reset(BusState *qbus)
}
}
-static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
+static void pci_host_bus_register(DeviceState *host)
{
- PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent);
+ PCIHostState *host_bridge = PCI_HOST_BRIDGE(host);
QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next);
}
@@ -329,7 +332,6 @@ const char *pci_root_bus_path(PCIDevice *dev)
}
static void pci_bus_init(PCIBus *bus, DeviceState *parent,
- const char *name,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min)
@@ -342,7 +344,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent,
/* host bridge */
QLIST_INIT(&bus->child);
- pci_host_bus_register(bus, parent);
+ pci_host_bus_register(parent);
}
bool pci_bus_is_express(PCIBus *bus)
@@ -362,8 +364,7 @@ void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent,
uint8_t devfn_min, const char *typename)
{
qbus_create_inplace(bus, bus_size, typename, parent, name);
- pci_bus_init(bus, parent, name, address_space_mem,
- address_space_io, devfn_min);
+ pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min);
}
PCIBus *pci_bus_new(DeviceState *parent, const char *name,
@@ -374,8 +375,7 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name,
PCIBus *bus;
bus = PCI_BUS(qbus_create(typename, parent, name));
- pci_bus_init(bus, parent, name, address_space_mem,
- address_space_io, devfn_min);
+ pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min);
return bus;
}
@@ -846,6 +846,16 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCIConfigWriteFunc *config_write = pc->config_write;
Error *local_err = NULL;
AddressSpace *dma_as;
+ DeviceState *dev = DEVICE(pci_dev);
+
+ pci_dev->bus = bus;
+ /* Only pci bridges can be attached to extra PCI root buses */
+ if (pci_bus_is_root(bus) && bus->parent_dev && !pc->is_bridge) {
+ error_setg(errp,
+ "PCI: Only PCI/PCIe bridges can be plugged into %s",
+ bus->parent_dev->name);
+ return NULL;
+ }
if (devfn < 0) {
for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
@@ -863,9 +873,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCI_SLOT(devfn), PCI_FUNC(devfn), name,
bus->devices[devfn]->name);
return NULL;
+ } else if (dev->hotplugged &&
+ pci_get_function_0(pci_dev)) {
+ error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
+ " new func %s cannot be exposed to guest.",
+ PCI_SLOT(devfn),
+ bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name,
+ name);
+
+ return NULL;
}
- pci_dev->bus = bus;
pci_dev->devfn = devfn;
dma_as = pci_device_iommu_address_space(pci_dev);
@@ -1065,6 +1083,10 @@ static pcibus_t pci_bar_address(PCIDevice *d,
pcibus_t new_addr, last_addr;
int bar = pci_bar(d, reg);
uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+ Object *machine = qdev_get_machine();
+ ObjectClass *oc = object_get_class(machine);
+ MachineClass *mc = MACHINE_CLASS(oc);
+ bool allow_0_address = mc->pci_allow_0_address;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
if (!(cmd & PCI_COMMAND_IO)) {
@@ -1075,7 +1097,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* Check if 32 bit BAR wraps around explicitly.
* TODO: make priorities correct and remove this work around.
*/
- if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) {
+ if (last_addr <= new_addr || last_addr >= UINT32_MAX ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
return new_addr;
@@ -1099,8 +1122,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* XXX: as we cannot support really dynamic
mappings, we handle specific values as invalid
mappings. */
- if (last_addr <= new_addr || new_addr == 0 ||
- last_addr == PCI_BAR_UNMAPPED) {
+ if (last_addr <= new_addr || last_addr == PCI_BAR_UNMAPPED ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
@@ -1148,16 +1171,16 @@ static void pci_update_mappings(PCIDevice *d)
/* now do the real mapping */
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_del(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_del_subregion(r->address_space, r->memory);
}
r->addr = new_addr;
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_add(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_add_subregion_overlap(r->address_space,
r->addr, r->memory, 1);
@@ -2065,9 +2088,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
g_free(path);
return;
}
- if (size & (size - 1)) {
- size = 1 << qemu_fls(size);
- }
+ size = pow2ceil(size);
vmsd = qdev_get_vmsd(DEVICE(pdev));
@@ -2077,7 +2098,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev)));
}
pdev->has_rom = true;
- memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_abort);
+ memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
vmstate_register_ram(&pdev->rom, &pdev->qdev);
ptr = memory_region_get_ram_ptr(&pdev->rom);
load_image(path, ptr);
@@ -2379,17 +2400,14 @@ static void pci_device_class_init(ObjectClass *klass, void *data)
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
{
PCIBus *bus = PCI_BUS(dev->bus);
+ PCIBus *iommu_bus = bus;
- if (bus->iommu_fn) {
- return bus->iommu_fn(bus, bus->iommu_opaque, dev->devfn);
+ while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) {
+ iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus);
}
-
- if (bus->parent_dev) {
- /** We are ignoring the bus master DMA bit of the bridge
- * as it would complicate things such as VFIO for no good reason */
- return pci_device_iommu_address_space(bus->parent_dev);
+ if (iommu_bus && iommu_bus->iommu_fn) {
+ return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn);
}
-
return &address_space_memory;
}
@@ -2453,6 +2471,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range)
pci_for_each_device_under_bus(bus, pci_dev_get_w64, range);
}
+static bool pcie_has_upstream_port(PCIDevice *dev)
+{
+ PCIDevice *parent_dev = pci_bridge_get_device(dev->bus);
+
+ /* Device associated with an upstream port.
+ * As there are several types of these, it's easier to check the
+ * parent device: upstream ports are always connected to
+ * root or downstream ports.
+ */
+ return parent_dev &&
+ pci_is_express(parent_dev) &&
+ parent_dev->exp.exp_cap &&
+ (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
+}
+
+PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
+{
+ if(pcie_has_upstream_port(pci_dev)) {
+ /* With an upstream PCIe port, we only support 1 device at slot 0 */
+ return pci_dev->bus->devices[0];
+ } else {
+ /* Other bus types might support multiple devices at slots 0-31 */
+ return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)];
+ }
+}
+
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,