From 620ac82eb0fc4218fb6a4937bcef3fdab3126703 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 13 May 2013 17:13:15 +0300 Subject: [PATCH 01/18] range: add Range structure Sometimes we need to pass ranges around, add a handy structure for this purpose. Note: memory.c defines its own concept of AddrRange structure for working with 128 addresses. It's necessary there for doing range math. This is not needed for most users: struct Range is much simpler, and is only used for passing the range around. Cc: Peter Maydell Signed-off-by: Michael S. Tsirkin --- include/qemu/range.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/qemu/range.h b/include/qemu/range.h index 350237212b..b76cc0df09 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -1,6 +1,22 @@ #ifndef QEMU_RANGE_H #define QEMU_RANGE_H +#include + +/* + * 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) From 3459a625215449b67b9c67d9151ff72892d0a42a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 30 May 2013 12:57:26 +0300 Subject: [PATCH 02/18] pci: store PCI hole ranges in guestinfo structure Will be used to pass hole ranges to guests. Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 46 ++++++++++++++++++++++++++++++++++++++- hw/i386/pc_piix.c | 14 +++++++++++- hw/i386/pc_q35.c | 6 ++++- hw/pci-host/q35.c | 8 +++++++ include/hw/i386/pc.h | 19 +++++++++++++++- include/hw/pci-host/q35.h | 2 ++ include/qemu/typedefs.h | 1 + 7 files changed, 92 insertions(+), 4 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 78f92e29a7..8af1e4e527 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -989,6 +989,48 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) } } +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); +} + +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; @@ -1030,7 +1072,8 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, 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; @@ -1082,6 +1125,7 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, 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; } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index fa59a0cb26..4637bde324 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -90,6 +90,7 @@ 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"); @@ -124,12 +125,23 @@ static void pc_init1(MemoryRegion *system_memory, rom_memory = system_memory; } + guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); + + /* 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)); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index bb0ce6ae6b..a13acf2968 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -77,6 +77,7 @@ 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", @@ -105,11 +106,13 @@ static void pc_q35_init(QEMUMachineInitArgs *args) rom_memory = get_system_memory(); } + guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); + /* 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 */ @@ -131,6 +134,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) 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; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 24df6b55cb..3a5cff989e 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -244,6 +244,14 @@ static int mch_init(PCIDevice *d) 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, "pci-hole", mch->pci_address_space, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index a417402bbb..eaf6a5dd4e 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -9,8 +9,20 @@ #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; + FWCfgState *fw_cfg; +}; + /* parallel.c */ static inline bool parallel_init(ISABus *bus, int index, CharDriverState *chr) { @@ -82,6 +94,10 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level); 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, @@ -89,7 +105,8 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, 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, diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index e182c820ac..b0838319a9 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -55,6 +55,7 @@ typedef struct MCHPCIState { 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 { @@ -81,6 +82,7 @@ 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)) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 698fc03d78..ac9f8d41a3 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -64,5 +64,6 @@ typedef struct VirtIODevice VirtIODevice; typedef struct QEMUSGList QEMUSGList; typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; +typedef struct PcGuestInfo PcGuestInfo; #endif /* QEMU_TYPEDEFS_H */ From f8c457b88d72a48989f190bc3d7b79f4f3b7d11c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 13 May 2013 20:00:23 +0300 Subject: [PATCH 03/18] pc: pass PCI hole ranges to Guests Guest currently has to jump through lots of hoops to guess the PCI hole ranges. It's fragile, and makes us change BIOS each time we add a new chipset. Let's report the window in a ROM file, to make BIOS do exactly what QEMU intends. Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 26 ++++++++++++++++++++++++++ hw/i386/pc_piix.c | 16 +++++++++++++++- hw/i386/pc_q35.c | 12 ++++++++++-- include/hw/i386/pc.h | 1 + 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 8af1e4e527..7c4794c0e8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -989,6 +989,31 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) } } +/* 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; @@ -1000,6 +1025,7 @@ 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, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 4637bde324..8a18dbedc4 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -57,6 +57,7 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; 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, @@ -126,6 +127,7 @@ static void pc_init1(MemoryRegion *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 */ @@ -260,8 +262,15 @@ static void pc_init_pci(QEMUMachineInitArgs *args) 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_pci_info = false; has_pvpanic = false; x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE); pc_init_pci(args); @@ -269,6 +278,7 @@ static void pc_init_pci_1_4(QEMUMachineInitArgs *args) static void pc_init_pci_1_3(QEMUMachineInitArgs *args) { + has_pci_info = false; enable_compat_apic_id_mode(); has_pvpanic = false; pc_init_pci(args); @@ -277,6 +287,7 @@ static void pc_init_pci_1_3(QEMUMachineInitArgs *args) /* PC machine init function for pc-1.1 to pc-1.2 */ static void pc_init_pci_1_2(QEMUMachineInitArgs *args) { + has_pci_info = false; disable_kvm_pv_eoi(); enable_compat_apic_id_mode(); has_pvpanic = false; @@ -286,6 +297,7 @@ static void pc_init_pci_1_2(QEMUMachineInitArgs *args) /* PC machine init function for pc-0.14 to pc-1.0 */ static void pc_init_pci_1_0(QEMUMachineInitArgs *args) { + has_pci_info = false; disable_kvm_pv_eoi(); enable_compat_apic_id_mode(); has_pvpanic = false; @@ -302,6 +314,7 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) 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(), @@ -320,6 +333,7 @@ static void pc_init_isa(QEMUMachineInitArgs *args) 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(); @@ -359,7 +373,7 @@ static QEMUMachine pc_i440fx_machine_v1_6 = { 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[]) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index a13acf2968..5b921601b5 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -47,6 +47,7 @@ #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) @@ -107,6 +108,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) } 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()) { @@ -212,11 +214,17 @@ static void pc_q35_init(QEMUMachineInitArgs *args) } } +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 = { @@ -232,7 +240,7 @@ 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[]) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index eaf6a5dd4e..dbdd523561 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -20,6 +20,7 @@ typedef struct PcPciInfo { struct PcGuestInfo { PcPciInfo pci_info; + bool has_pci_info; FWCfgState *fw_cfg; }; From fcbe0a707a227ea25f06382e61ff9b2761c77661 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 18 Jun 2013 17:11:42 +0300 Subject: [PATCH 04/18] pc_piix: cleanup init compat handling Make sure 1.4 calls 1.5, 1.3 calls 1.4 etc. This way it's enough to add enough new compat hook in a single place in piix. Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8a18dbedc4..e393022917 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -270,38 +270,28 @@ static void pc_init_pci_1_5(QEMUMachineInitArgs *args) static void pc_init_pci_1_4(QEMUMachineInitArgs *args) { - has_pci_info = false; 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) { - has_pci_info = false; 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) { - has_pci_info = false; 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) { - has_pci_info = false; - 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 */ From a0ae17a63e08a57a644eacc1f0fd89d535ed36bf Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 4 Jun 2013 10:49:48 +0200 Subject: [PATCH 05/18] e1000: cleanup process_tx_desc Coverity complains about two overruns in process_tx_desc(). The complaints are false positives, but we might as well eliminate them. The problem is that "hdr" is defined as an unsigned int, but then used to offset an array of size 65536, and another of size 256 bytes. hdr will actually never be greater than 255 though, as it's assigned only once and to the value of tp->hdr_len, which is an uint8_t. This patch simply gets rid of hdr, replacing it with tp->hdr_len, which makes it consistent with all other tp member use in the function. v2: - also cleanup coding style issues in the touched lines Signed-off-by: Andrew Jones Signed-off-by: Michael S. Tsirkin --- hw/net/e1000.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index e6f46f0c51..620f947134 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -556,7 +556,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) 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; @@ -603,8 +603,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) 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) @@ -612,14 +611,16 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) 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) { @@ -633,8 +634,9 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) 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; From c6d559d9cf8687d8378e888e7570c07ce53b94e2 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 4 Jun 2013 16:06:37 +0300 Subject: [PATCH 06/18] MAINTAINERS: s/Marcelo/Paolo/ Marcelo doesn't maintain kvm anymore, Paolo is taking over the job. Update MAINTAINERS to stop flooding Marcelo with mail. Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index ad9c8602ea..11dffee338 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -155,7 +155,7 @@ Guest CPU Cores (KVM): Overall M: Gleb Natapov -M: Marcelo Tosatti +M: Paolo Bonzini L: kvm@vger.kernel.org S: Supported F: kvm-* From bc3e6a0d6c8ab6cd7cd4b576ed567756f1dcabd2 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 16 Jun 2013 18:31:30 +0300 Subject: [PATCH 07/18] pvpanic: initialization cleanup Avoid use of static variables: PC systems initialize pvpanic device through pvpanic_init, so we can simply create the fw_cfg file at that point. This also makes it possible to skip device creation completely if fw_cfg is not there, e.g. for xen - so the ports it reserves are not discoverable by guests. Also, make pvpanic_init void since callers ignore return status anyway. Cc: Stefano Stabellini Cc: Laszlo Ersek Cc: Paul Durrant Signed-off-by: Michael S. Tsirkin --- hw/misc/pvpanic.c | 32 +++++++++++++++++--------------- include/hw/i386/pc.h | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 060099b553..83ed226081 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -97,26 +97,28 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) { 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; - } - } } -int pvpanic_init(ISABus *bus) +static void pvpanic_fw_cfg(ISADevice *dev, FWCfgState *fw_cfg) { - isa_create_simple(bus, TYPE_ISA_PVPANIC_DEVICE); - return 0; + PVPanicState *s = ISA_PVPANIC_DEVICE(dev); + + fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", + g_memdup(&s->ioport, sizeof(s->ioport)), + sizeof(s->ioport)); +} + +void pvpanic_init(ISABus *bus) +{ + 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[] = { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index dbdd523561..5949e7ec5f 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -193,7 +193,7 @@ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd) 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 From fea7d5966a54a5e5400cd38897a95ea576b5af4d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 16 Jun 2013 23:48:42 +0300 Subject: [PATCH 08/18] pvpanic: fix fwcfg for big endian hosts Convert port number to little endian when exposing it in fw cfg. Signed-off-by: Michael S. Tsirkin --- hw/misc/pvpanic.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 83ed226081..792d8e48ba 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -104,10 +104,11 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) 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", - g_memdup(&s->ioport, sizeof(s->ioport)), - sizeof(s->ioport)); + fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", pvpanic_port, + sizeof(*pvpanic_port)); } void pvpanic_init(ISABus *bus) From 79ca616f291124d166ca173e512c4ace1c2fe8b2 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:45 +1000 Subject: [PATCH 09/18] pci: Cleanup configuration for pci-hotplug.c pci-hotplug.c and the CONFIG_PCI_HOTPLUG variable which controls its compilation are misnamed. They're not about PCI hotplug in general, but rather about the pci_add/pci_del interface which are now deprecated in favour of the more general device_add/device_del interface. This patch therefore renames them to pci-hotplug-old.c and CONFIG_PCI_HOTPLUG_OLD. CONFIG_PCI_HOTPLUG=y was listed twice in {i386,x86_64}-softmmu.make for no particular reason, so we clean that up too. In addition it was included in ppc64-softmmu.mak for which the old hotplug interface was never used and is unsuitable, so we remove that too. Most of pci-hotplug.c was additionaly protected by #ifdef TARGET_I386. The small piece which wasn't is only called from the pci_add and pci_del hooks in hmp-commands.hx, which themselves were protected by #ifdef TARGET_I386. This patch therefore also removes the #ifdef from pci-hotplug-old.c, and changes the ifdefs in hmp-commands.hx to use CONFIG_PCI_HOTPLUG_OLD. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 3 +-- default-configs/ppc64-softmmu.mak | 2 -- default-configs/x86_64-softmmu.mak | 3 +-- hmp-commands.hx | 4 ++-- hw/pci/Makefile.objs | 2 +- hw/pci/{pci-hotplug.c => pci-hotplug-old.c} | 6 +++--- 6 files changed, 8 insertions(+), 12 deletions(-) rename hw/pci/{pci-hotplug.c => pci-hotplug-old.c} (98%) diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 03deca2dcb..4a0fc9c293 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -28,11 +28,10 @@ CONFIG_APPLESMC=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) diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index cb279cbcb6..5a72b5f038 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -45,7 +45,5 @@ CONFIG_OPENPIC=y 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 diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 599b63071f..10bb0c621c 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -28,11 +28,10 @@ CONFIG_APPLESMC=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) diff --git a/hmp-commands.hx b/hmp-commands.hx index 915b0d16fa..d1cdcfb71b 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1077,7 +1077,7 @@ STEXI 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?", @@ -1093,7 +1093,7 @@ STEXI Hot-add PCI device. ETEXI -#if defined(TARGET_I386) +#if defined(CONFIG_PCI_HOTPLUG_OLD) { .name = "pci_del", .args_type = "pci_addr:s", diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index a7fb9d0c11..720f438ac9 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -8,4 +8,4 @@ common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o 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 diff --git a/hw/pci/pci-hotplug.c b/hw/pci/pci-hotplug-old.c similarity index 98% rename from hw/pci/pci-hotplug.c rename to hw/pci/pci-hotplug-old.c index 12287d1efc..b3c233caef 100644 --- a/hw/pci/pci-hotplug.c +++ b/hw/pci/pci-hotplug-old.c @@ -1,5 +1,7 @@ /* - * QEMU PCI hotplug support + * 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 * @@ -34,7 +36,6 @@ #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) @@ -257,7 +258,6 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict) } else monitor_printf(mon, "failed to add %s\n", opts); } -#endif static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) { From 6ac363b50c569815786a795d806e068b3f6a07eb Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:46 +1000 Subject: [PATCH 10/18] pci: Move pci_read_devaddr to pci-hotplug-old.c pci_read_devaddr() is only used by the legacy functions for the old PCI hotplug interface in pci-hotplug-old.c. So we move the function there, and make it static. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci-hotplug-old.c | 14 ++++++++++++++ hw/pci/pci.c | 16 +--------------- include/hw/pci/pci.h | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index b3c233caef..a0b5558789 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -36,6 +36,20 @@ #include "sysemu/blockdev.h" #include "qapi/error.h" +static 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; +} + static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, const char *devaddr, const char *opts_str) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 61b681a91f..adf4da5b95 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -522,7 +522,7 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev) * Parse [[:]:], return -1 on error if funcp == NULL * [[:]:]., 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; @@ -581,20 +581,6 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, 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) { int dom, bus; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 6ef1f97393..b5edef832e 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -395,8 +395,8 @@ 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); -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); From 1ef7a2a2afedbba47e06af5081a8b4bf6dc1cf71 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:47 +1000 Subject: [PATCH 11/18] pci: Abolish pci_find_root_bus() pci_find_root_bus() takes a domain parameter. Currently PCI root buses with domain other than 0 can't be created, so this is more or less a long winded way of retrieving the main PCI root bus. Numbered domains don't actually properly cover the (non x86) possibilities for multiple PCI root buses, so this patch for now enforces the domain == 0 restriction in other places to replace pci_find_root_bus() with an explicit pci_find_primary_bus(). Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 2 +- hw/pci/pci-hotplug-old.c | 34 +++++++++++++++++++++++++--------- hw/pci/pci.c | 19 +++++++++++++++---- include/hw/pci/pci.h | 2 +- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e393022917..0276694b00 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -342,7 +342,7 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args) 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"); } diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index a0b5558789..7a47d6b0f0 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -36,17 +36,23 @@ #include "sysemu/blockdev.h" #include "qapi/error.h" -static int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, +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, domp, busp, slotp, NULL)) { + 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; } @@ -128,18 +134,22 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter, int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) { - int dom, pci_bus; + 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 (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { + if (!root) { + monitor_printf(mon, "no primary PCI bus\n"); goto err; } - dev = pci_find_device(pci_find_root_bus(dom), pci_bus, - PCI_DEVFN(slot, 0)); + 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; @@ -275,16 +285,22 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict) static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) { + PCIBus *root = pci_find_primary_bus(); PCIDevice *d; - int dom, bus; + int bus; unsigned slot; Error *local_err = NULL; - if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) { + if (!root) { + monitor_printf(mon, "no primary PCI bus\n"); return -1; } - d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0)); + 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; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index adf4da5b95..fc99e3bf69 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -246,12 +246,12 @@ static void pci_host_bus_register(int domain, PCIBus *bus) QLIST_INSERT_HEAD(&host_buses, host, next); } -PCIBus *pci_find_root_bus(int domain) +PCIBus *pci_find_primary_bus(void) { struct PCIHostBus *host; QLIST_FOREACH(host, &host_buses, next) { - if (host->domain == domain) { + if (host->domain == 0) { return host->bus; } } @@ -583,20 +583,31 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp, PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) { + PCIBus *root = pci_find_primary_bus(); int dom, bus; unsigned slot; + 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) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index b5edef832e..7b89d88106 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -389,7 +389,7 @@ int pci_bus_num(PCIBus *s); 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); +PCIBus *pci_find_primary_bus(void); int pci_find_domain(const PCIBus *bus); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); int pci_qdev_find_device(const char *id, PCIDevice **pdev); From c473d18da1b73301c580115e527207b73dcd597f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:48 +1000 Subject: [PATCH 12/18] pci: Use helper to find device's root bus in pci_find_domain() Currently pci_find_domain() performs two functions - it locates the PCI root bus above the given bus, then looks up that root bus's domain number. This patch adds a helper function to perform the first task, finding the root bus for a given PCI device. This is then used in pci_find_domain(). This changes pci_find_domain()'s signature slightly, taking a PCIDevice instead of a PCIBus - since all callers passed something of the form dev->bus, this simplifies things slightly. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci-hotplug-old.c | 2 +- hw/pci/pci.c | 20 +++++++++++++------- hw/pci/pcie_aer.c | 3 +-- include/hw/pci/pci.h | 3 ++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index 7a47d6b0f0..37e0720513 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -276,7 +276,7 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict) if (dev) { monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", - pci_find_domain(dev->bus), + pci_find_domain(dev), pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); } else diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fc99e3bf69..69a699574e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -259,18 +259,24 @@ PCIBus *pci_find_primary_bus(void) return NULL; } -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; } + return bus; +} + +int pci_find_domain(const PCIDevice *dev) +{ + const PCIBus *rootbus = pci_device_root_bus(dev); + struct PCIHostBus *host; + QLIST_FOREACH(host, &host_buses, next) { - if (host->bus == bus) { + if (host->bus == rootbus) { return host->domain; } } @@ -1997,7 +2003,7 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, fprintf(stderr, "ERROR: %04x:%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_find_domain(pdev), pci_bus_num(pdev->bus), PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), cap_id, offset, overlapping_cap, i); return -EINVAL; @@ -2152,7 +2158,7 @@ static char *pcibus_get_dev_path(DeviceState *dev) path[path_len] = '\0'; /* First field is the domain. */ - s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus)); + s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d)); assert(s == domain_len); memcpy(path, domain, domain_len); diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 1ce72ce944..06f77acc21 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -1022,8 +1022,7 @@ int do_pcie_aer_inject_error(Monitor *mon, *ret_data = qobject_from_jsonf("{'id': %s, " "'domain': %d, 'bus': %d, 'devfn': %d, " "'ret': %d}", - id, - pci_find_domain(dev->bus), + id, pci_find_domain(dev), pci_bus_num(dev->bus), dev->devfn, ret); assert(*ret_data); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 7b89d88106..f2bf1edbd9 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -390,7 +390,8 @@ void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), void *opaque); PCIBus *pci_find_primary_bus(void); -int pci_find_domain(const PCIBus *bus); +PCIBus *pci_device_root_bus(const PCIDevice *d); +int pci_find_domain(const 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); From 568f0690fd9aa4d39d84b04c1a5dbb53a915c3fe Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:49 +1000 Subject: [PATCH 13/18] pci: Replace pci_find_domain() with more general pci_root_bus_path() pci_find_domain() is used in a number of places where we want an id for a whole PCI domain (i.e. the subtree under a PCI root bus). The trouble is that many platforms may support multiple independent host bridges with no hardware supplied notion of domain number. This patch, therefore, replaces calls to pci_find_domain() with calls to a new pci_root_bus_path() returning a string. The new call is implemented in terms of a new callback in the host bridge class, so it can be defined in some way that's well defined for the platform. When no callback is available we fall back on the qbus name. Most current uses of pci_find_domain() are for error or informational messages, so the change in identifiers should be harmless. The exception is pci_get_dev_path(), whose results form part of migration streams. To maintain compatibility with old migration streams, the PIIX PCI host is altered to always supply "0000" for this path, which matches the old domain number (since the code didn't actually support domains other than 0). For the pseries (spapr) PCI bridge we use a different platform-unique identifier (pseries machines can routinely have dozens of PCI host bridges). Theoretically that breaks migration streams, but given that we don't yet have migration support for pseries, it doesn't matter. Any other machines that have working migration support including PCI devices will need to be updated to maintain migration stream compatibility. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci-host/piix.c | 9 +++++++++ hw/pci-host/q35.c | 9 +++++++++ hw/pci/pci-hotplug-old.c | 4 ++-- hw/pci/pci.c | 38 ++++++++++++++++++++------------------ hw/pci/pci_host.c | 1 + hw/pci/pcie_aer.c | 8 ++++---- hw/ppc/spapr_pci.c | 10 ++++++++++ include/hw/pci/pci.h | 2 +- include/hw/pci/pci_host.h | 10 ++++++++++ 9 files changed, 66 insertions(+), 25 deletions(-) diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index f9e68c3099..c36e725ba1 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -629,11 +629,20 @@ static const TypeInfo i440fx_info = { .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; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 3a5cff989e..13148ed533 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -63,6 +63,13 @@ static int q35_host_init(SysBusDevice *dev) 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), @@ -73,7 +80,9 @@ static void q35_host_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 = q35_host_root_bus_path; k->init = q35_host_init; dc->props = mch_props; dc->fw_name = "pci"; diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index 37e0720513..e2518108f4 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -275,8 +275,8 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict) } if (dev) { - monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", - pci_find_domain(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 diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 69a699574e..350b872294 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -25,6 +25,7 @@ #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" @@ -270,19 +271,20 @@ PCIBus *pci_device_root_bus(const PCIDevice *d) return bus; } -int pci_find_domain(const PCIDevice *dev) +const char *pci_root_bus_path(PCIDevice *dev) { - const PCIBus *rootbus = pci_device_root_bus(dev); - struct PCIHostBus *host; + 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); - QLIST_FOREACH(host, &host_buses, next) { - if (host->bus == rootbus) { - return host->domain; - } + 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, @@ -2000,10 +2002,10 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, 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), 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; @@ -2137,30 +2139,30 @@ static char *pcibus_get_dev_path(DeviceState *dev) * 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)); - 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. */ diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 12254b18a9..7dd9b25609 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -169,6 +169,7 @@ static const TypeInfo pci_host_type_info = { .name = TYPE_PCI_HOST_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, .abstract = true, + .class_size = sizeof(PCIHostBridgeClass), .instance_size = sizeof(PCIHostState), }; diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 06f77acc21..ca762ab09a 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -817,9 +817,9 @@ void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) 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)); } @@ -1020,9 +1020,9 @@ int do_pcie_aer_inject_error(Monitor *mon, 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), + id, pci_root_bus_path(dev), pci_bus_num(dev->bus), dev->devfn, ret); assert(*ret_data); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index c8c12c8241..f7be24c755 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -696,11 +696,21 @@ static Property spapr_phb_properties[] = { 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; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index f2bf1edbd9..e0597b7e9e 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -391,7 +391,7 @@ void pci_for_each_device(PCIBus *bus, int bus_num, void *opaque); PCIBus *pci_find_primary_bus(void); PCIBus *pci_device_root_bus(const PCIDevice *d); -int pci_find_domain(const PCIDevice *dev); +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); diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index 236cd0f75c..44052f2b4d 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -33,6 +33,10 @@ #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; @@ -44,6 +48,12 @@ struct PCIHostState { PCIBus *bus; }; +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); From 85c6e4fabb4c26e5cd8a024415ed2f5bcdd578db Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:50 +1000 Subject: [PATCH 14/18] pci: Add root bus argument to pci_get_bus_devfn() pci_get_bus_devfn() interprets a full PCI address string to give a PCIBus * and device/function number within that bus. Currently it assumes it is working on an address under the primary PCI root bus. This patch extends it to allow the caller to specify a root bus. This might seem a little odd since the supplied address can (theoretically) include a PCI domain number. However, attempting to use a non-zero domain number there is currently an error, so that shouldn't really cause problems. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci-hotplug-old.c | 4 ++-- hw/pci/pci.c | 7 ++++--- include/hw/pci/pci.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index e2518108f4..e92d6467be 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -65,7 +65,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, PCIBus *bus; int ret, devfn; - bus = pci_get_bus_devfn(&devfn, devaddr); + bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), devaddr); if (!bus) { monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); return NULL; @@ -205,7 +205,7 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, dinfo = NULL; } - bus = pci_get_bus_devfn(&devfn, devaddr); + bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), devaddr); if (!bus) { monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); return NULL; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 350b872294..c4f63ad9f3 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -589,12 +589,13 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp, return 0; } -PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) +PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr) { - PCIBus *root = pci_find_primary_bus(); int dom, bus; unsigned slot; + assert(!root->parent_dev); + if (!root) { fprintf(stderr, "No primary PCI bus\n"); return NULL; @@ -1588,7 +1589,7 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, if (i < 0) return NULL; - bus = pci_get_bus_devfn(&devfn, devaddr); + bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), devaddr); if (!bus) { error_report("Invalid PCI device address %s for device %s", devaddr, pci_nic_names[i]); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index e0597b7e9e..3a43fbaad2 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -394,7 +394,7 @@ 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_parse_devaddr(const char *addr, int *domp, int *busp, unsigned int *slotp, unsigned int *funcp); From 29b358f93a48a415853d11fc9b02f711b5ec8f76 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:51 +1000 Subject: [PATCH 15/18] pci: Add root bus parameter to pci_nic_init() At present, pci_nic_init() and pci_nic_init_nofail() assume that they will only create a NIC under the primary PCI root. As we add support for multiple PCI roots, that may no longer be the case. This patch adds a root bus parameter to pci_nic_init() (and updates callers accordingly) to allow the machine init code using it to specify the right PCI root for NICs created by old-style -net nic parameters. NICs created new-style, with -device can of course be put anywhere. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/alpha/dp264.c | 2 +- hw/arm/realview.c | 6 ++++-- hw/arm/versatilepb.c | 2 +- hw/i386/pc.c | 2 +- hw/mips/mips_fulong2e.c | 6 +++--- hw/mips/mips_malta.c | 6 +++--- hw/pci/pci-hotplug-old.c | 3 ++- hw/pci/pci.c | 10 ++++++---- hw/ppc/e500.c | 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/ppc440_bamboo.c | 2 +- hw/ppc/prep.c | 2 +- hw/ppc/spapr.c | 2 +- hw/sh4/r2d.c | 5 ++++- hw/sparc64/sun4u.c | 2 +- include/hw/pci/pci.h | 6 ++++-- 17 files changed, 36 insertions(+), 26 deletions(-) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 8695efb8ef..8dad08fac2 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -89,7 +89,7 @@ static void clipper_init(QEMUMachineInitArgs *args) /* 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. */ diff --git a/hw/arm/realview.c b/hw/arm/realview.c index d6f47bf4d4..036a1885b4 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -59,7 +59,7 @@ static void realview_init(QEMUMachineInitArgs *args, 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; @@ -250,7 +250,9 @@ static void realview_init(QEMUMachineInitArgs *args, } done_nic = 1; } else { - pci_nic_init_nofail(nd, "rtl8139", NULL); + if (pci_bus) { + pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); + } } } diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 753757ea19..15eb086831 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -244,7 +244,7 @@ static void versatile_init(QEMUMachineInitArgs *args, int board_id) 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)) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7c4794c0e8..80c27d6a30 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1310,7 +1310,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) 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); } } } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 00c9071af1..db67966531 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -231,7 +231,7 @@ static void audio_init (PCIBus *pci_bus) } /* Network support */ -static void network_init (void) +static void network_init (PCIBus *pci_bus) { int i; @@ -244,7 +244,7 @@ static void network_init (void) default_devaddr = "07"; } - pci_nic_init_nofail(nd, "rtl8139", default_devaddr); + pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr); } } @@ -393,7 +393,7 @@ static void mips_fulong2e_init(QEMUMachineInitArgs *args) /* Sound card */ audio_init(pci_bus); /* Network card */ - network_init(); + network_init(pci_bus); } static QEMUMachine mips_fulong2e_machine = { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 8a4459d0b2..5843fadcb0 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -468,7 +468,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, } /* Network support */ -static void network_init(void) +static void network_init(PCIBus *pci_bus) { int i; @@ -480,7 +480,7 @@ static void network_init(void) /* 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); } } @@ -985,7 +985,7 @@ void mips_malta_init(QEMUMachineInitArgs *args) fdctrl_init_isa(isa_bus, fd); /* Network card */ - network_init(); + network_init(pci_bus); /* Optional PCI video card */ pci_vga_init(pci_bus); diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index e92d6467be..807260cce9 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -92,7 +92,8 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, monitor_printf(mon, "Parameter addr not supported\n"); return NULL; } - return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); + return pci_nic_init(&nd_table[ret], pci_find_primary_bus(), + "rtl8139", devaddr); } static int scsi_hot_add(Monitor *mon, DeviceState *adapter, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c4f63ad9f3..2f2db0f8df 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1575,7 +1575,8 @@ static const char * const pci_nic_names[] = { /* 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; @@ -1589,7 +1590,7 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, if (i < 0) return NULL; - bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), 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]); @@ -1604,7 +1605,8 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, 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; @@ -1612,7 +1614,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, 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; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 38f799031a..75b05a21fc 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -707,7 +707,7 @@ void ppce500_init(PPCE500Params *params) 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); } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 3badfa3adb..98b902e5af 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -375,7 +375,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) 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); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 8faff300ff..1d73a73e94 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -259,7 +259,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) 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); diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index b0c1c027d4..5b039abb38 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -243,7 +243,7 @@ static void bamboo_init(QEMUMachineInitArgs *args) 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); } } diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 90828f2635..f674ab7079 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -628,7 +628,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) 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); } } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fe34291ffd..e2183d34a9 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -887,7 +887,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) 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); } } diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 3e4818e170..dd81d75330 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -236,6 +236,7 @@ static void r2d_init(QEMUMachineInitArgs *args) DeviceState *dev; SysBusDevice *busdev; MemoryRegion *address_space_mem = get_system_memory(); + PCIBus *pci_bus; if (cpu_model == NULL) { cpu_model = "SH7751R"; @@ -264,6 +265,7 @@ static void r2d_init(QEMUMachineInitArgs *args) 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]); @@ -295,7 +297,8 @@ static void r2d_init(QEMUMachineInitArgs *args) /* 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"); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index a6a3b76a76..19dd954da9 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -854,7 +854,7 @@ static void sun4uv_init(MemoryRegion *address_space_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); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 3a43fbaad2..209dda4f79 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -378,9 +378,11 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev, 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); From 9bc473057db773dd24be381ccbde4c686595d2e7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:52 +1000 Subject: [PATCH 16/18] pci: Simpler implementation of primary PCI bus Currently pci_find_primary_bus() searches the list of root buses for one with domain 0. But since host buses are always registered with domain 0, this just amounts to finding the only PCI host bus. The only remaining users of pci_find_primary_bus() are in pci-hotplug-old.c, which implements the old style pci_add/pci_del commands. Therefore, this patch redefines pci_find_primary_bus() to find the only PCI root bus, returning an error if there are multiple roots. The callers in pci-hotplug-old.c are updated correspondingly, to produce sensible error messages. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci-hotplug-old.c | 26 ++++++++++++++++++++------ hw/pci/pci.c | 9 ++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index 807260cce9..8077289756 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -62,10 +62,17 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, { Error *local_err = NULL; QemuOpts *opts; + PCIBus *root = pci_find_primary_bus(); PCIBus *bus; int ret, devfn; - bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), devaddr); + 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; @@ -92,8 +99,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, monitor_printf(mon, "Parameter addr not supported\n"); return NULL; } - return pci_nic_init(&nd_table[ret], pci_find_primary_bus(), - "rtl8139", devaddr); + return pci_nic_init(&nd_table[ret], root, "rtl8139", devaddr); } static int scsi_hot_add(Monitor *mon, DeviceState *adapter, @@ -144,7 +150,8 @@ int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) switch (dinfo->type) { case IF_SCSI: if (!root) { - monitor_printf(mon, "no primary PCI bus\n"); + 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)) { @@ -177,6 +184,7 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, DriveInfo *dinfo = NULL; int type = -1; char buf[128]; + PCIBus *root = pci_find_primary_bus(); PCIBus *bus; int devfn; @@ -206,7 +214,12 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, dinfo = NULL; } - bus = pci_get_bus_devfn(&devfn, pci_find_primary_bus(), devaddr); + 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; @@ -293,7 +306,8 @@ static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) Error *local_err = NULL; if (!root) { - monitor_printf(mon, "no primary PCI bus\n"); + monitor_printf(mon, "no primary PCI bus (if there are multiple" + " PCI roots, you must use device_del instead)"); return -1; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2f2db0f8df..e0995aa950 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -249,15 +249,18 @@ static void pci_host_bus_register(int domain, PCIBus *bus) PCIBus *pci_find_primary_bus(void) { + PCIBus *primary_bus = NULL; struct PCIHostBus *host; QLIST_FOREACH(host, &host_buses, next) { - if (host->domain == 0) { - return host->bus; + if (primary_bus) { + /* We have multiple root buses, refuse to select a primary */ + return NULL; } + primary_bus = host->bus; } - return NULL; + return primary_bus; } PCIBus *pci_device_root_bus(const PCIDevice *d) From 2b8cc89a5c4a8bccbef8c6862bae7371afbf3e76 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:53 +1000 Subject: [PATCH 17/18] pci: Remove domain from PCIHostBus There are now no users of the domain field of PCIHostBus, so remove it from the structure, and as a parameter from the pci_host_bus_register() function which sets it. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e0995aa950..d861b408e2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -91,7 +91,6 @@ 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; }; @@ -238,11 +237,10 @@ static int pcibus_reset(BusState *qbus) return 1; } -static void pci_host_bus_register(int domain, PCIBus *bus) +static void pci_host_bus_register(PCIBus *bus) { struct PCIHostBus *host; host = g_malloc0(sizeof(*host)); - host->domain = domain; host->bus = bus; QLIST_INSERT_HEAD(&host_buses, host, next); } @@ -303,7 +301,8 @@ 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); vmstate_register(NULL, -1, &vmstate_pcibus, bus); } From 7588e2b0559ae72d3c2952c7807fc05c03099970 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 6 Jun 2013 18:48:54 +1000 Subject: [PATCH 18/18] pci: Fold host_buses list into PCIHostState functionality The host_buses list is an odd structure - a list of pointers to PCI root buses existing in parallel to the normal qdev tree structure. This patch removes it, instead putting the link pointers into the PCIHostState structure, which have a 1:1 relationship to PCIHostBus structures anyway. Signed-off-by: David Gibson Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 33 ++++++++++++++------------------- include/hw/pci/pci_host.h | 2 ++ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index d861b408e2..868006338b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -90,11 +90,7 @@ static void pci_del_option_rom(PCIDevice *pdev); 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 { - 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", @@ -237,20 +233,19 @@ static int pcibus_reset(BusState *qbus) return 1; } -static void pci_host_bus_register(PCIBus *bus) +static void pci_host_bus_register(PCIBus *bus, DeviceState *parent) { - struct PCIHostBus *host; - host = g_malloc0(sizeof(*host)); - 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_primary_bus(void) { PCIBus *primary_bus = NULL; - struct PCIHostBus *host; + PCIHostState *host; - QLIST_FOREACH(host, &host_buses, next) { + QLIST_FOREACH(host, &pci_host_bridges, next) { if (primary_bus) { /* We have multiple root buses, refuse to select a primary */ return NULL; @@ -302,7 +297,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, /* host bridge */ QLIST_INIT(&bus->child); - pci_host_bus_register(bus); + pci_host_bus_register(bus, parent); vmstate_register(NULL, -1, &vmstate_pcibus, bus); } @@ -1533,11 +1528,11 @@ static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) 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) { @@ -2201,11 +2196,11 @@ static int pci_qdev_find_recursive(PCIBus *bus, 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; diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index 44052f2b4d..ba31595fc7 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -46,6 +46,8 @@ struct PCIHostState { MemoryRegion mmcfg; uint32_t config_reg; PCIBus *bus; + + QLIST_ENTRY(PCIHostState) next; }; typedef struct PCIHostBridgeClass {