Commit 7feb640c authored by Alexey Korolev's avatar Alexey Korolev Committed by Michael S. Tsirkin
Browse files

Fix guest OS hang when 64bit PCI bar present

This patch addresses the issue fully described here:
http://lists.nongnu.org/archive/html/qemu-devel/2013-02/msg01804.html



Linux kernels prior to 2.6.36 do not disable the PCI device during
enumeration process. Since lower and higher parts of a 64bit BAR
are programmed separately this leads to qemu receiving a request to occupy
a completely wrong address region for a short period of time.
We have found that the boot process screws up completely if kvm-apic range
is overlapped even for a short period of time (it is fine for other
regions though).

This patch raises the priority of the kvm-apic memory region, so it is
never pushed out by PCI devices. The patch is quite safe as it does not
touch memory manager.

Signed-off-by: default avatarAlexey Korolev <akorolex@gmail.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 2af234e6
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -48,7 +48,8 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
    }
}

void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
                                   bool may_overlap, unsigned priority)
{
    assert(n >= 0 && n < dev->num_mmio);

@@ -61,11 +62,29 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
        memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
    }
    dev->mmio[n].addr = addr;
    if (may_overlap) {
        memory_region_add_subregion_overlap(get_system_memory(),
                                            addr,
                                            dev->mmio[n].memory,
                                            priority);
    }
    else {
        memory_region_add_subregion(get_system_memory(),
                                    addr,
                                    dev->mmio[n].memory);
    }
}

void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
{
    sysbus_mmio_map_common(dev, n, addr, false, 0);
}

void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
                             unsigned priority)
{
    sysbus_mmio_map_common(dev, n, addr, true, priority);
}

/* Request an IRQ source.  The actual IRQ object may be populated later.  */
void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size);

void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq);
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr);
void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
                             unsigned priority);
void sysbus_add_memory(SysBusDevice *dev, hwaddr addr,
                       MemoryRegion *mem);
void sysbus_add_memory_overlap(SysBusDevice *dev, hwaddr addr,
+2 −1
Original line number Diff line number Diff line
@@ -2088,7 +2088,8 @@ static void x86_cpu_apic_init(X86CPU *cpu, Error **errp)
        /* NOTE: the APIC is directly connected to the CPU - it is not
           on the global memory bus. */
        /* XXX: what if the base changes? */
        sysbus_mmio_map(SYS_BUS_DEVICE(env->apic_state), 0, MSI_ADDR_BASE);
        sysbus_mmio_map_overlap(SYS_BUS_DEVICE(env->apic_state), 0,
                                MSI_ADDR_BASE, 0x1000);
        apic_mapped = 1;
    }
}