Overall
M: Gleb Natapov <gleb@redhat.com>
-M: Marcelo Tosatti <mtosatti@redhat.com>
+M: Paolo Bonzini <pbonzini@redhat.com>
L: kvm@vger.kernel.org
S: Supported
F: kvm-*
CONFIG_I8259=y
CONFIG_PFLASH_CFI01=y
CONFIG_TPM_TIS=$(CONFIG_TPM)
-CONFIG_PCI_HOTPLUG=y
+CONFIG_PCI_HOTPLUG_OLD=y
CONFIG_MC146818RTC=y
CONFIG_PAM=y
CONFIG_PCI_PIIX=y
-CONFIG_PCI_HOTPLUG=y
CONFIG_WDT_IB700=y
CONFIG_PC_SYSFW=y
CONFIG_XEN_I386=$(CONFIG_XEN)
CONFIG_PSERIES=y
CONFIG_E500=y
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
-# For pSeries
-CONFIG_PCI_HOTPLUG=y
# For PReP
CONFIG_MC146818RTC=y
CONFIG_I8259=y
CONFIG_PFLASH_CFI01=y
CONFIG_TPM_TIS=$(CONFIG_TPM)
-CONFIG_PCI_HOTPLUG=y
+CONFIG_PCI_HOTPLUG_OLD=y
CONFIG_MC146818RTC=y
CONFIG_PAM=y
CONFIG_PCI_PIIX=y
-CONFIG_PCI_HOTPLUG=y
CONFIG_WDT_IB700=y
CONFIG_PC_SYSFW=y
CONFIG_XEN_I386=$(CONFIG_XEN)
Add drive to PCI storage controller.
ETEXI
-#if defined(TARGET_I386)
+#if defined(CONFIG_PCI_HOTPLUG_OLD)
{
.name = "pci_add",
.args_type = "pci_addr:s,type:s,opts:s?",
Hot-add PCI device.
ETEXI
-#if defined(TARGET_I386)
+#if defined(CONFIG_PCI_HOTPLUG_OLD)
{
.name = "pci_del",
.args_type = "pci_addr:s",
/* Network setup. e1000 is good enough, failing Tulip support. */
for (i = 0; i < nb_nics; i++) {
- pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
}
/* IDE disk setup. */
qemu_irq *irqp;
qemu_irq pic[64];
qemu_irq mmc_irq[2];
- PCIBus *pci_bus;
+ PCIBus *pci_bus = NULL;
NICInfo *nd;
i2c_bus *i2c;
int n;
}
done_nic = 1;
} else {
- pci_nic_init_nofail(nd, "rtl8139", NULL);
+ if (pci_bus) {
+ pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL);
+ }
}
}
smc91c111_init(nd, 0x10010000, sic[25]);
done_smc = 1;
} else {
- pci_nic_init_nofail(nd, "rtl8139", NULL);
+ pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL);
}
}
if (usb_enabled(false)) {
}
}
+/* pci-info ROM file. Little endian format */
+typedef struct PcRomPciInfo {
+ uint64_t w32_min;
+ uint64_t w32_max;
+ uint64_t w64_min;
+ uint64_t w64_max;
+} PcRomPciInfo;
+
+static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
+{
+ PcRomPciInfo *info;
+ if (!guest_info->has_pci_info) {
+ return;
+ }
+
+ info = g_malloc(sizeof *info);
+ info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
+ info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
+ info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
+ info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
+ /* Pass PCI hole info to guest via a side channel.
+ * Required so guest PCI enumeration does the right thing. */
+ fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
+}
+
+typedef struct PcGuestInfoState {
+ PcGuestInfo info;
+ Notifier machine_done;
+} PcGuestInfoState;
+
+static
+void pc_guest_info_machine_done(Notifier *notifier, void *data)
+{
+ PcGuestInfoState *guest_info_state = container_of(notifier,
+ PcGuestInfoState,
+ machine_done);
+ pc_fw_cfg_guest_info(&guest_info_state->info);
+}
+
+PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
+ ram_addr_t above_4g_mem_size)
+{
+ PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
+ PcGuestInfo *guest_info = &guest_info_state->info;
+
+ guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
+ if (sizeof(hwaddr) == 4) {
+ guest_info->pci_info.w64.begin = 0;
+ guest_info->pci_info.w64.end = 0;
+ } else {
+ /*
+ * BIOS does not set MTRR entries for the 64 bit window, so no need to
+ * align address to power of two. Align address at 1G, this makes sure
+ * it can be exactly covered with a PAT entry even when using huge
+ * pages.
+ */
+ guest_info->pci_info.w64.begin =
+ ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
+ guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
+ (0x1ULL << 62);
+ assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
+ }
+
+ guest_info_state->machine_done.notify = pc_guest_info_machine_done;
+ qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
+ return guest_info;
+}
+
void pc_acpi_init(const char *default_dsdt)
{
char *filename;
ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
MemoryRegion *rom_memory,
- MemoryRegion **ram_memory)
+ MemoryRegion **ram_memory,
+ PcGuestInfo *guest_info)
{
int linux_boot, i;
MemoryRegion *ram, *option_rom_mr;
for (i = 0; i < nb_option_roms; i++) {
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
+ guest_info->fw_cfg = fw_cfg;
return fw_cfg;
}
if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
pc_init_ne2k_isa(isa_bus, nd);
} else {
- pci_nic_init_nofail(nd, "e1000", NULL);
+ pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
}
}
}
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
static bool has_pvpanic = true;
+static bool has_pci_info = true;
/* PC hardware initialisation */
static void pc_init1(MemoryRegion *system_memory,
MemoryRegion *rom_memory;
DeviceState *icc_bridge;
FWCfgState *fw_cfg = NULL;
+ PcGuestInfo *guest_info;
if (xen_enabled() && xen_hvm_init() != 0) {
fprintf(stderr, "xen hardware virtual machine initialisation failed\n");
rom_memory = system_memory;
}
+ guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
+ guest_info->has_pci_info = has_pci_info;
+
+ /* Set PCI window size the way seabios has always done it. */
+ /* Power of 2 so bios can cover it with a single MTRR */
+ if (ram_size <= 0x80000000)
+ guest_info->pci_info.w32.begin = 0x80000000;
+ else if (ram_size <= 0xc0000000)
+ guest_info->pci_info.w32.begin = 0xc0000000;
+ else
+ guest_info->pci_info.w32.begin = 0xe0000000;
+
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
fw_cfg = pc_memory_init(system_memory,
kernel_filename, kernel_cmdline, initrd_filename,
below_4g_mem_size, above_4g_mem_size,
- rom_memory, &ram_memory);
+ rom_memory, &ram_memory, guest_info);
}
gsi_state = g_malloc0(sizeof(*gsi_state));
initrd_filename, cpu_model, 1, 1);
}
+static void pc_init_pci_1_5(QEMUMachineInitArgs *args)
+{
+ has_pci_info = false;
+ pc_init_pci(args);
+}
+
static void pc_init_pci_1_4(QEMUMachineInitArgs *args)
{
has_pvpanic = false;
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- pc_init_pci(args);
+ pc_init_pci_1_5(args);
}
static void pc_init_pci_1_3(QEMUMachineInitArgs *args)
{
enable_compat_apic_id_mode();
- has_pvpanic = false;
- pc_init_pci(args);
+ pc_init_pci_1_4(args);
}
/* PC machine init function for pc-1.1 to pc-1.2 */
static void pc_init_pci_1_2(QEMUMachineInitArgs *args)
{
disable_kvm_pv_eoi();
- enable_compat_apic_id_mode();
- has_pvpanic = false;
- pc_init_pci(args);
+ pc_init_pci_1_3(args);
}
/* PC machine init function for pc-0.14 to pc-1.0 */
static void pc_init_pci_1_0(QEMUMachineInitArgs *args)
{
- disable_kvm_pv_eoi();
- enable_compat_apic_id_mode();
- has_pvpanic = false;
- pc_init_pci(args);
+ pc_init_pci_1_2(args);
}
/* PC init function for pc-0.10 to pc-0.13, and reused by xenfv */
const char *initrd_filename = args->initrd_filename;
const char *boot_device = args->boot_device;
has_pvpanic = false;
+ has_pci_info = false;
disable_kvm_pv_eoi();
enable_compat_apic_id_mode();
pc_init1(get_system_memory(),
const char *initrd_filename = args->initrd_filename;
const char *boot_device = args->boot_device;
has_pvpanic = false;
+ has_pci_info = false;
if (cpu_model == NULL)
cpu_model = "486";
disable_kvm_pv_eoi();
pc_init_pci(args);
- bus = pci_find_root_bus(0);
+ bus = pci_find_primary_bus();
if (bus != NULL) {
pci_create_simple(bus, -1, "xen-platform");
}
static QEMUMachine pc_i440fx_machine_v1_5 = {
.name = "pc-i440fx-1.5",
.desc = "Standard PC (i440FX + PIIX, 1996)",
- .init = pc_init_pci,
+ .init = pc_init_pci_1_5,
.hot_add_cpu = pc_hot_add_cpu,
.max_cpus = 255,
.compat_props = (GlobalProperty[]) {
#define MAX_SATA_PORTS 6
static bool has_pvpanic = true;
+static bool has_pci_info = true;
/* PC hardware initialisation */
static void pc_q35_init(QEMUMachineInitArgs *args)
ICH9LPCState *ich9_lpc;
PCIDevice *ahci;
DeviceState *icc_bridge;
+ PcGuestInfo *guest_info;
icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
object_property_add_child(qdev_get_machine(), "icc-bridge",
rom_memory = get_system_memory();
}
+ guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
+ guest_info->has_pci_info = has_pci_info;
+
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
pc_memory_init(get_system_memory(), kernel_filename, kernel_cmdline,
initrd_filename, below_4g_mem_size, above_4g_mem_size,
- rom_memory, &ram_memory);
+ rom_memory, &ram_memory, guest_info);
}
/* irq lines */
q35_host->mch.address_space_io = get_system_io();
q35_host->mch.below_4g_mem_size = below_4g_mem_size;
q35_host->mch.above_4g_mem_size = above_4g_mem_size;
+ q35_host->mch.guest_info = guest_info;
/* pci */
qdev_init_nofail(DEVICE(q35_host));
host_bus = q35_host->host.pci.bus;
}
}
+static void pc_q35_init_1_5(QEMUMachineInitArgs *args)
+{
+ has_pci_info = false;
+ pc_q35_init(args);
+}
+
static void pc_q35_init_1_4(QEMUMachineInitArgs *args)
{
has_pvpanic = false;
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- pc_q35_init(args);
+ pc_q35_init_1_5(args);
}
static QEMUMachine pc_q35_machine_v1_6 = {
static QEMUMachine pc_q35_machine_v1_5 = {
.name = "pc-q35-1.5",
.desc = "Standard PC (Q35 + ICH9, 2009)",
- .init = pc_q35_init,
+ .init = pc_q35_init_1_5,
.hot_add_cpu = pc_hot_add_cpu,
.max_cpus = 255,
.compat_props = (GlobalProperty[]) {
}
/* Network support */
-static void network_init (void)
+static void network_init (PCIBus *pci_bus)
{
int i;
default_devaddr = "07";
}
- pci_nic_init_nofail(nd, "rtl8139", default_devaddr);
+ pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr);
}
}
/* Sound card */
audio_init(pci_bus);
/* Network card */
- network_init();
+ network_init(pci_bus);
}
static QEMUMachine mips_fulong2e_machine = {
}
/* Network support */
-static void network_init(void)
+static void network_init(PCIBus *pci_bus)
{
int i;
/* The malta board has a PCNet card using PCI SLOT 11 */
default_devaddr = "0b";
- pci_nic_init_nofail(nd, "pcnet", default_devaddr);
+ pci_nic_init_nofail(nd, pci_bus, "pcnet", default_devaddr);
}
}
fdctrl_init_isa(isa_bus, fd);
/* Network card */
- network_init();
+ network_init(pci_bus);
/* Optional PCI video card */
pci_vga_init(pci_bus);
{
ISADevice *d = ISA_DEVICE(dev);
PVPanicState *s = ISA_PVPANIC_DEVICE(dev);
- static bool port_configured;
- FWCfgState *fw_cfg;
isa_register_ioport(d, &s->io, s->ioport);
+}
- if (!port_configured) {
- fw_cfg = fw_cfg_find();
- if (fw_cfg) {
- fw_cfg_add_file(fw_cfg, "etc/pvpanic-port",
- g_memdup(&s->ioport, sizeof(s->ioport)),
- sizeof(s->ioport));
- port_configured = true;
- }
- }
+static void pvpanic_fw_cfg(ISADevice *dev, FWCfgState *fw_cfg)
+{
+ PVPanicState *s = ISA_PVPANIC_DEVICE(dev);
+ uint16_t *pvpanic_port = g_malloc(sizeof(*pvpanic_port));
+ *pvpanic_port = cpu_to_le16(s->ioport);
+
+ fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", pvpanic_port,
+ sizeof(*pvpanic_port));
}
-int pvpanic_init(ISABus *bus)
+void pvpanic_init(ISABus *bus)
{
- isa_create_simple(bus, TYPE_ISA_PVPANIC_DEVICE);
- return 0;
+ ISADevice *dev;
+ FWCfgState *fw_cfg = fw_cfg_find();
+ if (!fw_cfg) {
+ return;
+ }
+ dev = isa_create_simple (bus, TYPE_ISA_PVPANIC_DEVICE);
+ pvpanic_fw_cfg(dev, fw_cfg);
}
static Property pvpanic_isa_properties[] = {
uint32_t txd_lower = le32_to_cpu(dp->lower.data);
uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D);
unsigned int split_size = txd_lower & 0xffff, bytes, sz, op;
- unsigned int msh = 0xfffff, hdr = 0;
+ unsigned int msh = 0xfffff;
uint64_t addr;
struct e1000_context_desc *xp = (struct e1000_context_desc *)dp;
struct e1000_tx *tp = &s->tx;
addr = le64_to_cpu(dp->buffer_addr);
if (tp->tse && tp->cptse) {
- hdr = tp->hdr_len;
- msh = hdr + tp->mss;
+ msh = tp->hdr_len + tp->mss;
do {
bytes = split_size;
if (tp->size + bytes > msh)
bytes = MIN(sizeof(tp->data) - tp->size, bytes);
pci_dma_read(&s->dev, addr, tp->data + tp->size, bytes);
- if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
- memmove(tp->header, tp->data, hdr);
+ sz = tp->size + bytes;
+ if (sz >= tp->hdr_len && tp->size < tp->hdr_len) {
+ memmove(tp->header, tp->data, tp->hdr_len);
+ }
tp->size = sz;
addr += bytes;
if (sz == msh) {
xmit_seg(s);
- memmove(tp->data, tp->header, hdr);
- tp->size = hdr;
+ memmove(tp->data, tp->header, tp->hdr_len);
+ tp->size = tp->hdr_len;
}
} while (split_size -= bytes);
} else if (!tp->tse && tp->cptse) {
if (!(txd_lower & E1000_TXD_CMD_EOP))
return;
- if (!(tp->tse && tp->cptse && tp->size < hdr))
+ if (!(tp->tse && tp->cptse && tp->size < tp->hdr_len)) {
xmit_seg(s);
+ }
tp->tso_frames = 0;
tp->sum_needed = 0;
tp->vlan_needed = 0;
.class_init = i440fx_class_init,
};
+static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ /* For backwards compat with old device paths */
+ return "0000";
+}
+
static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+ hc->root_bus_path = i440fx_pcihost_root_bus_path;
k->init = i440fx_pcihost_initfn;
dc->fw_name = "pci";
dc->no_user = 1;
return 0;
}
+static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ /* For backwards compat with old device paths */
+ return "0000";
+}
+
static Property mch_props[] = {
DEFINE_PROP_UINT64("MCFG", Q35PCIHost, host.base_addr,
MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+ hc->root_bus_path = q35_host_root_bus_path;
k->init = q35_host_init;
dc->props = mch_props;
dc->fw_name = "pci";
hwaddr pci_hole64_size;
MCHPCIState *mch = MCH_PCI_DEVICE(d);
+ /* Leave enough space for the biggest MCFG BAR */
+ /* TODO: this matches current bios behaviour, but
+ * it's not a power of two, which means an MTRR
+ * can't cover it exactly.
+ */
+ mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
+ MCH_HOST_BRIDGE_PCIEXBAR_MAX;
+
/* setup pci memory regions */
memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
mch->pci_address_space,
common-obj-$(CONFIG_NO_PCI) += pci-stub.o
common-obj-$(CONFIG_ALL) += pci-stub.o
-obj-$(CONFIG_PCI_HOTPLUG) += pci-hotplug.o
+common-obj-$(CONFIG_PCI_HOTPLUG_OLD) += pci-hotplug-old.o
--- /dev/null
+/*
+ * Deprecated PCI hotplug interface support
+ * This covers the old pci_add / pci_del command, whereas the more general
+ * device_add / device_del commands are now preferred.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "hw/i386/pc.h"
+#include "monitor/monitor.h"
+#include "hw/scsi/scsi.h"
+#include "hw/virtio/virtio-blk.h"
+#include "qemu/config-file.h"
+#include "sysemu/blockdev.h"
+#include "qapi/error.h"
+
+static int pci_read_devaddr(Monitor *mon, const char *addr,
+ int *busp, unsigned *slotp)
+{
+ int dom;
+
+ /* strip legacy tag */
+ if (!strncmp(addr, "pci_addr=", 9)) {
+ addr += 9;
+ }
+ if (pci_parse_devaddr(addr, &dom, busp, slotp, NULL)) {
+ monitor_printf(mon, "Invalid pci address\n");
+ return -1;
+ }
+ if (dom != 0) {
+ monitor_printf(mon, "Multiple PCI domains not supported, use device_add\n");
+ return -1;
+ }
+ return 0;
+}
+
+static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
+ const char *devaddr,
+ const char *opts_str)
+{
+ Error *local_err = NULL;
+ QemuOpts *opts;
+ PCIBus *root = pci_find_primary_bus();
+ PCIBus *bus;
+ int ret, devfn;
+
+ if (!root) {
+ monitor_printf(mon, "no primary PCI bus (if there are multiple"
+ " PCI roots, you must use device_add instead)");
+ return NULL;
+ }
+
+ bus = pci_get_bus_devfn(&devfn, root, devaddr);
+ if (!bus) {
+ monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
+ return NULL;
+ }
+ if (!((BusState*)bus)->allow_hotplug) {
+ monitor_printf(mon, "PCI bus doesn't support hotplug\n");
+ return NULL;
+ }
+
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
+ if (!opts) {
+ return NULL;
+ }
+
+ qemu_opt_set(opts, "type", "nic");
+
+ ret = net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return NULL;
+ }
+ if (nd_table[ret].devaddr) {
+ monitor_printf(mon, "Parameter addr not supported\n");
+ return NULL;
+ }
+ return pci_nic_init(&nd_table[ret], root, "rtl8139", devaddr);
+}
+
+static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
+ DriveInfo *dinfo, int printinfo)
+{
+ SCSIBus *scsibus;
+ SCSIDevice *scsidev;
+
+ scsibus = (SCSIBus *)
+ object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)),
+ TYPE_SCSI_BUS);
+ if (!scsibus) {
+ error_report("Device is not a SCSI adapter");
+ return -1;
+ }
+
+ /*
+ * drive_init() tries to find a default for dinfo->unit. Doesn't
+ * work at all for hotplug though as we assign the device to a
+ * specific bus instead of the first bus with spare scsi ids.
+ *
+ * Ditch the calculated value and reload from option string (if
+ * specified).
+ */
+ dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
+ dinfo->bus = scsibus->busnr;
+ scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit,
+ false, -1, NULL);
+ if (!scsidev) {
+ return -1;
+ }
+ dinfo->unit = scsidev->id;
+
+ if (printinfo)
+ monitor_printf(mon, "OK bus %d, unit %d\n",
+ scsibus->busnr, scsidev->id);
+ return 0;
+}
+
+int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo)
+{
+ int pci_bus;
+ unsigned slot;
+ PCIBus *root = pci_find_primary_bus();
+ PCIDevice *dev;
+ const char *pci_addr = qdict_get_str(qdict, "pci_addr");
+
+ switch (dinfo->type) {
+ case IF_SCSI:
+ if (!root) {
+ monitor_printf(mon, "no primary PCI bus (if there are multiple"
+ " PCI roots, you must use device_add instead)");
+ goto err;
+ }
+ if (pci_read_devaddr(mon, pci_addr, &pci_bus, &slot)) {
+ goto err;
+ }
+ dev = pci_find_device(root, pci_bus, PCI_DEVFN(slot, 0));
+ if (!dev) {
+ monitor_printf(mon, "no pci device with address %s\n", pci_addr);
+ goto err;
+ }
+ if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) {
+ goto err;
+ }
+ break;
+ default:
+ monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type);
+ goto err;
+ }
+
+ return 0;
+err:
+ return -1;
+}
+
+static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
+ const char *devaddr,
+ const char *opts)
+{
+ PCIDevice *dev;
+ DriveInfo *dinfo = NULL;
+ int type = -1;
+ char buf[128];
+ PCIBus *root = pci_find_primary_bus();
+ PCIBus *bus;
+ int devfn;
+
+ if (get_param_value(buf, sizeof(buf), "if", opts)) {
+ if (!strcmp(buf, "scsi"))
+ type = IF_SCSI;
+ else if (!strcmp(buf, "virtio")) {
+ type = IF_VIRTIO;
+ } else {
+ monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf);
+ return NULL;
+ }
+ } else {
+ monitor_printf(mon, "no if= specified\n");
+ return NULL;
+ }
+
+ if (get_param_value(buf, sizeof(buf), "file", opts)) {
+ dinfo = add_init_drive(opts);
+ if (!dinfo)
+ return NULL;
+ if (dinfo->devaddr) {
+ monitor_printf(mon, "Parameter addr not supported\n");
+ return NULL;
+ }
+ } else {
+ dinfo = NULL;
+ }
+
+ if (!root) {
+ monitor_printf(mon, "no primary PCI bus (if there are multiple"
+ " PCI roots, you must use device_add instead)");
+ return NULL;
+ }
+ bus = pci_get_bus_devfn(&devfn, root, devaddr);
+ if (!bus) {
+ monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
+ return NULL;
+ }
+ if (!((BusState*)bus)->allow_hotplug) {
+ monitor_printf(mon, "PCI bus doesn't support hotplug\n");
+ return NULL;
+ }
+
+ switch (type) {
+ case IF_SCSI:
+ dev = pci_create(bus, devfn, "lsi53c895a");
+ if (qdev_init(&dev->qdev) < 0)
+ dev = NULL;
+ if (dev && dinfo) {
+ if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) {
+ qdev_unplug(&dev->qdev, NULL);
+ dev = NULL;
+ }
+ }
+ break;
+ case IF_VIRTIO:
+ if (!dinfo) {
+ monitor_printf(mon, "virtio requires a backing file/device.\n");
+ return NULL;
+ }
+ dev = pci_create(bus, devfn, "virtio-blk-pci");
+ if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+ qdev_free(&dev->qdev);
+ dev = NULL;
+ break;
+ }
+ if (qdev_init(&dev->qdev) < 0)
+ dev = NULL;
+ break;
+ default:
+ dev = NULL;
+ }
+ return dev;
+}
+
+void pci_device_hot_add(Monitor *mon, const QDict *qdict)
+{
+ PCIDevice *dev = NULL;
+ const char *pci_addr = qdict_get_str(qdict, "pci_addr");
+ const char *type = qdict_get_str(qdict, "type");
+ const char *opts = qdict_get_try_str(qdict, "opts");
+
+ /* strip legacy tag */
+ if (!strncmp(pci_addr, "pci_addr=", 9)) {
+ pci_addr += 9;
+ }
+
+ if (!opts) {
+ opts = "";
+ }
+
+ if (!strcmp(pci_addr, "auto"))
+ pci_addr = NULL;
+
+ if (strcmp(type, "nic") == 0) {
+ dev = qemu_pci_hot_add_nic(mon, pci_addr, opts);
+ } else if (strcmp(type, "storage") == 0) {
+ dev = qemu_pci_hot_add_storage(mon, pci_addr, opts);
+ } else {
+ monitor_printf(mon, "invalid type: %s\n", type);
+ }
+
+ if (dev) {
+ monitor_printf(mon, "OK root bus %s, bus %d, slot %d, function %d\n",
+ pci_root_bus_path(dev),
+ pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ } else
+ monitor_printf(mon, "failed to add %s\n", opts);
+}
+
+static int pci_device_hot_remove(Monitor *mon, const char *pci_addr)
+{
+ PCIBus *root = pci_find_primary_bus();
+ PCIDevice *d;
+ int bus;
+ unsigned slot;
+ Error *local_err = NULL;
+
+ if (!root) {
+ monitor_printf(mon, "no primary PCI bus (if there are multiple"
+ " PCI roots, you must use device_del instead)");
+ return -1;
+ }
+
+ if (pci_read_devaddr(mon, pci_addr, &bus, &slot)) {
+ return -1;
+ }
+
+ d = pci_find_device(root, bus, PCI_DEVFN(slot, 0));
+ if (!d) {
+ monitor_printf(mon, "slot %d empty\n", slot);
+ return -1;
+ }
+
+ qdev_unplug(&d->qdev, &local_err);
+ if (error_is_set(&local_err)) {
+ monitor_printf(mon, "%s\n", error_get_pretty(local_err));
+ error_free(local_err);
+ return -1;
+ }
+
+ return 0;
+}
+
+void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict)
+{
+ pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr"));
+}
+++ /dev/null
-/*
- * QEMU PCI hotplug support
- *
- * Copyright (c) 2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/boards.h"
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "hw/i386/pc.h"
-#include "monitor/monitor.h"
-#include "hw/scsi/scsi.h"
-#include "hw/virtio/virtio-blk.h"
-#include "qemu/config-file.h"
-#include "sysemu/blockdev.h"
-#include "qapi/error.h"
-
-#if defined(TARGET_I386)
-static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
- const char *devaddr,
- const char *opts_str)
-{
- Error *local_err = NULL;
- QemuOpts *opts;
- PCIBus *bus;
- int ret, devfn;
-
- bus = pci_get_bus_devfn(&devfn, devaddr);
- if (!bus) {
- monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
- return NULL;
- }
- if (!((BusState*)bus)->allow_hotplug) {
- monitor_printf(mon, "PCI bus doesn't support hotplug\n");
- return NULL;
- }
-
- opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
- if (!opts) {
- return NULL;
- }
-
- qemu_opt_set(opts, "type", "nic");
-
- ret = net_client_init(opts, 0, &local_err);
- if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
- return NULL;
- }
- if (nd_table[ret].devaddr) {
- monitor_printf(mon, "Parameter addr not supported\n");
- return NULL;
- }
- return pci_nic_init(&nd_table[ret], "rtl8139", devaddr);
-}
-
-static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
- DriveInfo *dinfo, int printinfo)
-{
- SCSIBus *scsibus;
- SCSIDevice *scsidev;
-
- scsibus = (SCSIBus *)
- object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)),
- TYPE_SCSI_BUS);
- if (!scsibus) {
- error_report("Device is not a SCSI adapter");
- return -1;
- }
-
- /*
- * drive_init() tries to find a default for dinfo->unit. Doesn't
- * work at all for hotplug though as we assign the device to a
- * specific bus instead of the first bus with spare scsi ids.
- *
- * Ditch the calculated value and reload from option string (if
- * specified).
- */
- dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
- dinfo->bus = scsibus->busnr;
- scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit,
- false, -1, NULL);
- if (!scsidev) {
- return -1;
- }
- dinfo->unit = scsidev->id;
-
- if (printinfo)
- monitor_printf(mon, "OK bus %d, unit %d\n",
- scsibus->busnr, scsidev->id);
- return 0;
-}
-
-int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo)
-{
- int dom, pci_bus;
- unsigned slot;
- PCIDevice *dev;
- const char *pci_addr = qdict_get_str(qdict, "pci_addr");
-
- switch (dinfo->type) {
- case IF_SCSI:
- if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) {
- goto err;
- }
- dev = pci_find_device(pci_find_root_bus(dom), pci_bus,
- PCI_DEVFN(slot, 0));
- if (!dev) {
- monitor_printf(mon, "no pci device with address %s\n", pci_addr);
- goto err;
- }
- if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) {
- goto err;
- }
- break;
- default:
- monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type);
- goto err;
- }
-
- return 0;
-err:
- return -1;
-}
-
-static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
- const char *devaddr,
- const char *opts)
-{
- PCIDevice *dev;
- DriveInfo *dinfo = NULL;
- int type = -1;
- char buf[128];
- PCIBus *bus;
- int devfn;
-
- if (get_param_value(buf, sizeof(buf), "if", opts)) {
- if (!strcmp(buf, "scsi"))
- type = IF_SCSI;
- else if (!strcmp(buf, "virtio")) {
- type = IF_VIRTIO;
- } else {
- monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf);
- return NULL;
- }
- } else {
- monitor_printf(mon, "no if= specified\n");
- return NULL;
- }
-
- if (get_param_value(buf, sizeof(buf), "file", opts)) {
- dinfo = add_init_drive(opts);
- if (!dinfo)
- return NULL;
- if (dinfo->devaddr) {
- monitor_printf(mon, "Parameter addr not supported\n");
- return NULL;
- }
- } else {
- dinfo = NULL;
- }
-
- bus = pci_get_bus_devfn(&devfn, devaddr);
- if (!bus) {
- monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
- return NULL;
- }
- if (!((BusState*)bus)->allow_hotplug) {
- monitor_printf(mon, "PCI bus doesn't support hotplug\n");
- return NULL;
- }
-
- switch (type) {
- case IF_SCSI:
- dev = pci_create(bus, devfn, "lsi53c895a");
- if (qdev_init(&dev->qdev) < 0)
- dev = NULL;
- if (dev && dinfo) {
- if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) {
- qdev_unplug(&dev->qdev, NULL);
- dev = NULL;
- }
- }
- break;
- case IF_VIRTIO:
- if (!dinfo) {
- monitor_printf(mon, "virtio requires a backing file/device.\n");
- return NULL;
- }
- dev = pci_create(bus, devfn, "virtio-blk-pci");
- if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
- qdev_free(&dev->qdev);
- dev = NULL;
- break;
- }
- if (qdev_init(&dev->qdev) < 0)
- dev = NULL;
- break;
- default:
- dev = NULL;
- }
- return dev;
-}
-
-void pci_device_hot_add(Monitor *mon, const QDict *qdict)
-{
- PCIDevice *dev = NULL;
- const char *pci_addr = qdict_get_str(qdict, "pci_addr");
- const char *type = qdict_get_str(qdict, "type");
- const char *opts = qdict_get_try_str(qdict, "opts");
-
- /* strip legacy tag */
- if (!strncmp(pci_addr, "pci_addr=", 9)) {
- pci_addr += 9;
- }
-
- if (!opts) {
- opts = "";
- }
-
- if (!strcmp(pci_addr, "auto"))
- pci_addr = NULL;
-
- if (strcmp(type, "nic") == 0) {
- dev = qemu_pci_hot_add_nic(mon, pci_addr, opts);
- } else if (strcmp(type, "storage") == 0) {
- dev = qemu_pci_hot_add_storage(mon, pci_addr, opts);
- } else {
- monitor_printf(mon, "invalid type: %s\n", type);
- }
-
- if (dev) {
- monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n",
- pci_find_domain(dev->bus),
- pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn));
- } else
- monitor_printf(mon, "failed to add %s\n", opts);
-}
-#endif
-
-static int pci_device_hot_remove(Monitor *mon, const char *pci_addr)
-{
- PCIDevice *d;
- int dom, bus;
- unsigned slot;
- Error *local_err = NULL;
-
- if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) {
- return -1;
- }
-
- d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0));
- if (!d) {
- monitor_printf(mon, "slot %d empty\n", slot);
- return -1;
- }
-
- qdev_unplug(&d->qdev, &local_err);
- if (error_is_set(&local_err)) {
- monitor_printf(mon, "%s\n", error_get_pretty(local_err));
- error_free(local_err);
- return -1;
- }
-
- return 0;
-}
-
-void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict)
-{
- pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr"));
-}
#include "hw/pci/pci.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_host.h"
#include "monitor/monitor.h"
#include "net/net.h"
#include "sysemu/sysemu.h"
static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
-struct PCIHostBus {
- int domain;
- struct PCIBus *bus;
- QLIST_ENTRY(PCIHostBus) next;
-};
-static QLIST_HEAD(, PCIHostBus) host_buses;
+static QLIST_HEAD(, PCIHostState) pci_host_bridges;
static const VMStateDescription vmstate_pcibus = {
.name = "PCIBUS",
return 1;
}
-static void pci_host_bus_register(int domain, PCIBus *bus)
+static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
{
- struct PCIHostBus *host;
- host = g_malloc0(sizeof(*host));
- host->domain = domain;
- host->bus = bus;
- QLIST_INSERT_HEAD(&host_buses, host, next);
+ PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent);
+
+ QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next);
}
-PCIBus *pci_find_root_bus(int domain)
+PCIBus *pci_find_primary_bus(void)
{
- struct PCIHostBus *host;
+ PCIBus *primary_bus = NULL;
+ PCIHostState *host;
- QLIST_FOREACH(host, &host_buses, next) {
- if (host->domain == domain) {
- return host->bus;
+ QLIST_FOREACH(host, &pci_host_bridges, next) {
+ if (primary_bus) {
+ /* We have multiple root buses, refuse to select a primary */
+ return NULL;
}
+ primary_bus = host->bus;
}
- return NULL;
+ return primary_bus;
}
-int pci_find_domain(const PCIBus *bus)
+PCIBus *pci_device_root_bus(const PCIDevice *d)
{
- PCIDevice *d;
- struct PCIHostBus *host;
+ PCIBus *bus = d->bus;
- /* obtain root bus */
while ((d = bus->parent_dev) != NULL) {
bus = d->bus;
}
- QLIST_FOREACH(host, &host_buses, next) {
- if (host->bus == bus) {
- return host->domain;
- }
+ return bus;
+}
+
+const char *pci_root_bus_path(PCIDevice *dev)
+{
+ PCIBus *rootbus = pci_device_root_bus(dev);
+ PCIHostState *host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_GET_CLASS(host_bridge);
+
+ assert(!rootbus->parent_dev);
+ assert(host_bridge->bus == rootbus);
+
+ if (hc->root_bus_path) {
+ return (*hc->root_bus_path)(host_bridge, rootbus);
}
- abort(); /* should not be reached */
- return -1;
+ return rootbus->qbus.name;
}
static void pci_bus_init(PCIBus *bus, DeviceState *parent,
/* host bridge */
QLIST_INIT(&bus->child);
- pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */
+
+ pci_host_bus_register(bus, parent);
vmstate_register(NULL, -1, &vmstate_pcibus, bus);
}
* Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
* [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
*/
-static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
unsigned int *slotp, unsigned int *funcp)
{
const char *p;
return 0;
}
-int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
- unsigned *slotp)
-{
- /* strip legacy tag */
- if (!strncmp(addr, "pci_addr=", 9)) {
- addr += 9;
- }
- if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
- monitor_printf(mon, "Invalid pci address\n");
- return -1;
- }
- return 0;
-}
-
-PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
+PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr)
{
int dom, bus;
unsigned slot;
+ assert(!root->parent_dev);
+
+ if (!root) {
+ fprintf(stderr, "No primary PCI bus\n");
+ return NULL;
+ }
+
if (!devaddr) {
*devfnp = -1;
- return pci_find_bus_nr(pci_find_root_bus(0), 0);
+ return pci_find_bus_nr(root, 0);
}
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
return NULL;
}
+ if (dom != 0) {
+ fprintf(stderr, "No support for non-zero PCI domains\n");
+ return NULL;
+ }
+
*devfnp = PCI_DEVFN(slot, 0);
- return pci_find_bus_nr(pci_find_root_bus(dom), bus);
+ return pci_find_bus_nr(root, bus);
}
static void pci_init_cmask(PCIDevice *dev)
PciInfoList *qmp_query_pci(Error **errp)
{
PciInfoList *info, *head = NULL, *cur_item = NULL;
- struct PCIHostBus *host;
+ PCIHostState *host_bridge;
- QLIST_FOREACH(host, &host_buses, next) {
+ QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
info = g_malloc0(sizeof(*info));
- info->value = qmp_query_pci_bus(host->bus, 0);
+ info->value = qmp_query_pci_bus(host_bridge->bus, 0);
/* XXX: waiting for the qapi to support GSList */
if (!cur_item) {
/* Initialize a PCI NIC. */
/* FIXME callers should check for failure, but don't */
-PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
+ const char *default_model,
const char *default_devaddr)
{
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
if (i < 0)
return NULL;
- bus = pci_get_bus_devfn(&devfn, devaddr);
+ bus = pci_get_bus_devfn(&devfn, rootbus, devaddr);
if (!bus) {
error_report("Invalid PCI device address %s for device %s",
devaddr, pci_nic_names[i]);
return pci_dev;
}
-PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
+ const char *default_model,
const char *default_devaddr)
{
PCIDevice *res;
if (qemu_show_nic_models(nd->model, pci_nic_models))
exit(0);
- res = pci_nic_init(nd, default_model, default_devaddr);
+ res = pci_nic_init(nd, rootbus, default_model, default_devaddr);
if (!res)
exit(1);
return res;
for (i = offset; i < offset + size; i++) {
overlapping_cap = pci_find_capability_at_offset(pdev, i);
if (overlapping_cap) {
- fprintf(stderr, "ERROR: %04x:%02x:%02x.%x "
+ fprintf(stderr, "ERROR: %s:%02x:%02x.%x "
"Attempt to add PCI capability %x at offset "
"%x overlaps existing capability %x at offset %x\n",
- pci_find_domain(pdev->bus), pci_bus_num(pdev->bus),
+ pci_root_bus_path(pdev), pci_bus_num(pdev->bus),
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
cap_id, offset, overlapping_cap, i);
return -EINVAL;
* domain:Bus:Slot.Func for systems without nested PCI bridges.
* Slot.Function list specifies the slot and function numbers for all
* devices on the path from root to the specific device. */
- char domain[] = "DDDD:00";
+ const char *root_bus_path;
+ int root_bus_len;
char slot[] = ":SS.F";
- int domain_len = sizeof domain - 1 /* For '\0' */;
int slot_len = sizeof slot - 1 /* For '\0' */;
int path_len;
char *path, *p;
int s;
+ root_bus_path = pci_root_bus_path(d);
+ root_bus_len = strlen(root_bus_path);
+
/* Calculate # of slots on path between device and root. */;
slot_depth = 0;
for (t = d; t; t = t->bus->parent_dev) {
++slot_depth;
}
- path_len = domain_len + slot_len * slot_depth;
+ path_len = root_bus_len + slot_len * slot_depth;
/* Allocate memory, fill in the terminating null byte. */
path = g_malloc(path_len + 1 /* For '\0' */);
path[path_len] = '\0';
- /* First field is the domain. */
- s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus));
- assert(s == domain_len);
- memcpy(path, domain, domain_len);
+ memcpy(path, root_bus_path, root_bus_len);
/* Fill in slot numbers. We walk up from device to root, so need to print
* them in the reverse order, last to first. */
int pci_qdev_find_device(const char *id, PCIDevice **pdev)
{
- struct PCIHostBus *host;
+ PCIHostState *host_bridge;
int rc = -ENODEV;
- QLIST_FOREACH(host, &host_buses, next) {
- int tmp = pci_qdev_find_recursive(host->bus, id, pdev);
+ QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
+ int tmp = pci_qdev_find_recursive(host_bridge->bus, id, pdev);
if (!tmp) {
rc = 0;
break;
.name = TYPE_PCI_HOST_BRIDGE,
.parent = TYPE_SYS_BUS_DEVICE,
.abstract = true,
+ .class_size = sizeof(PCIHostBridgeClass),
.instance_size = sizeof(PCIHostState),
};
qdict = qobject_to_qdict(data);
devfn = (int)qdict_get_int(qdict, "devfn");
- monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n",
+ monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
qdict_get_str(qdict, "id"),
- (int) qdict_get_int(qdict, "domain"),
+ qdict_get_str(qdict, "root_bus"),
(int) qdict_get_int(qdict, "bus"),
PCI_SLOT(devfn), PCI_FUNC(devfn));
}
ret = pcie_aer_inject_error(dev, &err);
*ret_data = qobject_from_jsonf("{'id': %s, "
- "'domain': %d, 'bus': %d, 'devfn': %d, "
+ "'root_bus': %s, 'bus': %d, 'devfn': %d, "
"'ret': %d}",
- id,
- pci_find_domain(dev->bus),
+ id, pci_root_bus_path(dev),
pci_bus_num(dev->bus), dev->devfn,
ret);
assert(*ret_data);
if (pci_bus) {
/* Register network interfaces. */
for (i = 0; i < nb_nics; i++) {
- pci_nic_init_nofail(&nd_table[i], "virtio", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
}
}
escc_mem, 0, memory_region_size(escc_mem));
for(i = 0; i < nb_nics; i++)
- pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
ide_drive_get(hd, MAX_IDE_BUS);
escc_mem, 0, memory_region_size(escc_mem));
for(i = 0; i < nb_nics; i++)
- pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
ide_drive_get(hd, MAX_IDE_BUS);
for (i = 0; i < nb_nics; i++) {
/* There are no PCI NICs on the Bamboo board, but there are
* PCI slots, so we can pick whatever default model we want. */
- pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
+ pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL);
}
}
isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i],
&nd_table[i]);
} else {
- pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
}
}
if (strcmp(nd->model, "ibmveth") == 0) {
spapr_vlan_create(spapr->vio_bus, nd);
} else {
- pci_nic_init_nofail(&nd_table[i], nd->model, NULL);
+ pci_nic_init_nofail(&nd_table[i], phb->bus, nd->model, NULL);
}
}
DEFINE_PROP_END_OF_LIST(),
};
+static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(host_bridge);
+
+ return sphb->dtbusname;
+}
+
static void spapr_phb_class_init(ObjectClass *klass, void *data)
{
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+ hc->root_bus_path = spapr_phb_root_bus_path;
sdc->init = spapr_phb_init;
dc->props = spapr_phb_properties;
dc->reset = spapr_phb_reset;
DeviceState *dev;
SysBusDevice *busdev;
MemoryRegion *address_space_mem = get_system_memory();
+ PCIBus *pci_bus;
if (cpu_model == NULL) {
cpu_model = "SH7751R";
dev = qdev_create(NULL, "sh_pci");
busdev = SYS_BUS_DEVICE(dev);
qdev_init_nofail(dev);
+ pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci"));
sysbus_mmio_map(busdev, 0, P4ADDR(0x1e200000));
sysbus_mmio_map(busdev, 1, A7ADDR(0x1e200000));
sysbus_connect_irq(busdev, 0, irq[PCI_INTA]);
/* NIC: rtl8139 on-board, and 2 slots. */
for (i = 0; i < nb_nics; i++)
- pci_nic_init_nofail(&nd_table[i], "rtl8139", i==0 ? "2" : NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus,
+ "rtl8139", i==0 ? "2" : NULL);
/* USB keyboard */
usbdevice_create("keyboard");
}
for(i = 0; i < nb_nics; i++)
- pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
ide_drive_get(hd, MAX_IDE_BUS);
#include "net/net.h"
#include "hw/i386/ioapic.h"
+#include "qemu/range.h"
+
/* PC-style peripherals (also used by other machines). */
+typedef struct PcPciInfo {
+ Range w32;
+ Range w64;
+} PcPciInfo;
+
+struct PcGuestInfo {
+ PcPciInfo pci_info;
+ bool has_pci_info;
+ FWCfgState *fw_cfg;
+};
+
/* parallel.c */
static inline bool parallel_init(ISABus *bus, int index, CharDriverState *chr)
{
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge);
void pc_hot_add_cpu(const int64_t id, Error **errp);
void pc_acpi_init(const char *default_dsdt);
+
+PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
+ ram_addr_t above_4g_mem_size);
+
FWCfgState *pc_memory_init(MemoryRegion *system_memory,
const char *kernel_filename,
const char *kernel_cmdline,
ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
MemoryRegion *rom_memory,
- MemoryRegion **ram_memory);
+ MemoryRegion **ram_memory,
+ PcGuestInfo *guest_info);
qemu_irq *pc_allocate_cpu_irq(void);
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
void pc_system_firmware_init(MemoryRegion *rom_memory);
/* pvpanic.c */
-int pvpanic_init(ISABus *bus);
+void pvpanic_init(ISABus *bus);
/* e820 types */
#define E820_RAM 1
uint8_t smm_enabled;
ram_addr_t below_4g_mem_size;
ram_addr_t above_4g_mem_size;
+ PcGuestInfo *guest_info;
} MCHPCIState;
typedef struct Q35PCIHost {
#define MCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */
#define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */
#define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xb0000000
+#define MCH_HOST_BRIDGE_PCIEXBAR_MAX (0x10000000) /* 256M */
#define MCH_HOST_BRIDGE_PCIEXBAR_ADMSK Q35_MASK(64, 35, 28)
#define MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK ((uint64_t)(1 << 26))
#define MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK ((uint64_t)(1 << 25))
void pci_device_reset(PCIDevice *dev);
void pci_bus_reset(PCIBus *bus);
-PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
+ const char *default_model,
const char *default_devaddr);
-PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
+ const char *default_model,
const char *default_devaddr);
PCIDevice *pci_vga_init(PCIBus *bus);
void pci_for_each_device(PCIBus *bus, int bus_num,
void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque),
void *opaque);
-PCIBus *pci_find_root_bus(int domain);
-int pci_find_domain(const PCIBus *bus);
+PCIBus *pci_find_primary_bus(void);
+PCIBus *pci_device_root_bus(const PCIDevice *d);
+const char *pci_root_bus_path(PCIDevice *dev);
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn);
int pci_qdev_find_device(const char *id, PCIDevice **pdev);
-PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
+PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr);
-int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
- unsigned *slotp);
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+ unsigned int *slotp, unsigned int *funcp);
void pci_device_deassert_intx(PCIDevice *dev);
#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge"
#define PCI_HOST_BRIDGE(obj) \
OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE)
+#define PCI_HOST_BRIDGE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PCIHostBridgeClass, (klass), TYPE_PCI_HOST_BRIDGE)
+#define PCI_HOST_BRIDGE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PCIHostBridgeClass, (obj), TYPE_PCI_HOST_BRIDGE)
struct PCIHostState {
SysBusDevice busdev;
MemoryRegion mmcfg;
uint32_t config_reg;
PCIBus *bus;
+
+ QLIST_ENTRY(PCIHostState) next;
};
+typedef struct PCIHostBridgeClass {
+ SysBusDeviceClass parent_class;
+
+ const char *(*root_bus_path)(PCIHostState *, PCIBus *);
+} PCIHostBridgeClass;
+
/* common internal helpers for PCI/PCIe hosts, cut off overflows */
void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t limit, uint32_t val, uint32_t len);
#ifndef QEMU_RANGE_H
#define QEMU_RANGE_H
+#include <inttypes.h>
+
+/*
+ * Operations on 64 bit address ranges.
+ * Notes:
+ * - ranges must not wrap around 0, but can include the last byte ~0x0LL.
+ * - this can not represent a full 0 to ~0x0LL range.
+ */
+
+/* A structure representing a range of addresses. */
+struct Range {
+ uint64_t begin; /* First byte of the range, or 0 if empty. */
+ uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */
+};
+typedef struct Range Range;
+
/* Get last byte of a range from offset + length.
* Undefined for ranges that wrap around 0. */
static inline uint64_t range_get_last(uint64_t offset, uint64_t len)
typedef struct QEMUSGList QEMUSGList;
typedef struct SHPCDevice SHPCDevice;
typedef struct FWCfgState FWCfgState;
+typedef struct PcGuestInfo PcGuestInfo;
#endif /* QEMU_TYPEDEFS_H */