qemu/hw/pci-host/prep.c

414 lines
13 KiB
C
Raw Normal View History

/*
* QEMU PREP PCI host
*
* Copyright (c) 2006 Fabrice Bellard
* Copyright (c) 2011-2013 Andreas Färber
*
* 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 "qemu/osdep.h"
#include "qemu/units.h"
2016-03-14 16:01:28 +08:00
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_host.h"
#include "hw/i386/pc.h"
#include "hw/loader.h"
#include "exec/address-spaces.h"
#include "elf.h"
#define TYPE_RAVEN_PCI_DEVICE "raven"
#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost"
#define RAVEN_PCI_DEVICE(obj) \
OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE)
typedef struct RavenPCIState {
PCIDevice dev;
uint32_t elf_machine;
char *bios_name;
MemoryRegion bios;
} RavenPCIState;
#define RAVEN_PCI_HOST_BRIDGE(obj) \
OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE)
typedef struct PRePPCIState {
PCIHostState parent_obj;
qemu_irq irq[PCI_NUM_PINS];
PCIBus pci_bus;
AddressSpace pci_io_as;
MemoryRegion pci_io;
MemoryRegion pci_io_non_contiguous;
MemoryRegion pci_memory;
MemoryRegion pci_intack;
MemoryRegion bm;
MemoryRegion bm_ram_alias;
MemoryRegion bm_pci_memory_alias;
AddressSpace bm_as;
RavenPCIState pci_dev;
int contiguous_map;
} PREPPCIState;
#define BIOS_SIZE (1 * MiB)
static inline uint32_t raven_pci_io_config(hwaddr addr)
{
int i;
for (i = 0; i < 11; i++) {
if ((addr & (1 << (11 + i))) != 0) {
break;
}
}
return (addr & 0x7ff) | (i << 11);
}
static void raven_pci_io_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
PREPPCIState *s = opaque;
PCIHostState *phb = PCI_HOST_BRIDGE(s);
pci_data_write(phb->bus, raven_pci_io_config(addr), val, size);
}
static uint64_t raven_pci_io_read(void *opaque, hwaddr addr,
unsigned int size)
{
PREPPCIState *s = opaque;
PCIHostState *phb = PCI_HOST_BRIDGE(s);
return pci_data_read(phb->bus, raven_pci_io_config(addr), size);
}
static const MemoryRegionOps raven_pci_io_ops = {
.read = raven_pci_io_read,
.write = raven_pci_io_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static uint64_t raven_intack_read(void *opaque, hwaddr addr,
unsigned int size)
{
return pic_read_irq(isa_pic);
}
static const MemoryRegionOps raven_intack_ops = {
.read = raven_intack_read,
.valid = {
.max_access_size = 1,
},
};
static inline hwaddr raven_io_address(PREPPCIState *s,
hwaddr addr)
{
if (s->contiguous_map == 0) {
/* 64 KB contiguous space for IOs */
addr &= 0xFFFF;
} else {
/* 8 MB non-contiguous space for IOs */
addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7);
}
/* FIXME: handle endianness switch */
return addr;
}
static uint64_t raven_io_read(void *opaque, hwaddr addr,
unsigned int size)
{
PREPPCIState *s = opaque;
uint8_t buf[4];
addr = raven_io_address(s, addr);
address_space_read(&s->pci_io_as, addr + 0x80000000,
MEMTXATTRS_UNSPECIFIED, buf, size);
if (size == 1) {
return buf[0];
} else if (size == 2) {
return lduw_le_p(buf);
} else if (size == 4) {
return ldl_le_p(buf);
} else {
g_assert_not_reached();
}
}
static void raven_io_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
PREPPCIState *s = opaque;
uint8_t buf[4];
addr = raven_io_address(s, addr);
if (size == 1) {
buf[0] = val;
} else if (size == 2) {
stw_le_p(buf, val);
} else if (size == 4) {
stl_le_p(buf, val);
} else {
g_assert_not_reached();
}
address_space_write(&s->pci_io_as, addr + 0x80000000,
MEMTXATTRS_UNSPECIFIED, buf, size);
}
static const MemoryRegionOps raven_io_ops = {
.read = raven_io_read,
.write = raven_io_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.max_access_size = 4,
.valid.unaligned = true,
};
static int raven_map_irq(PCIDevice *pci_dev, int irq_num)
{
return (irq_num + (pci_dev->devfn >> 3)) & 1;
}
static void raven_set_irq(void *opaque, int irq_num, int level)
{
qemu_irq *pic = opaque;
qemu_set_irq(pic[irq_num] , level);
}
static AddressSpace *raven_pcihost_set_iommu(PCIBus *bus, void *opaque,
int devfn)
{
PREPPCIState *s = opaque;
return &s->bm_as;
}
static void raven_change_gpio(void *opaque, int n, int level)
{
PREPPCIState *s = opaque;
s->contiguous_map = level;
}
static void raven_pcihost_realizefn(DeviceState *d, Error **errp)
{
SysBusDevice *dev = SYS_BUS_DEVICE(d);
PCIHostState *h = PCI_HOST_BRIDGE(dev);
PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev);
MemoryRegion *address_space_mem = get_system_memory();
int i;
for (i = 0; i < PCI_NUM_PINS; i++) {
sysbus_init_irq(dev, &s->irq[i]);
}
qdev_init_gpio_in(d, raven_change_gpio, 1);
pci_bus_irqs(&s->pci_bus, raven_set_irq, raven_map_irq, s->irq,
PCI_NUM_PINS);
memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, s,
"pci-conf-idx", 4);
memory_region_add_subregion(&s->pci_io, 0xcf8, &h->conf_mem);
memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, s,
"pci-conf-data", 4);
memory_region_add_subregion(&s->pci_io, 0xcfc, &h->data_mem);
memory_region_init_io(&h->mmcfg, OBJECT(s), &raven_pci_io_ops, s,
"pciio", 0x00400000);
memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg);
memory_region_init_io(&s->pci_intack, OBJECT(s), &raven_intack_ops, s,
"pci-intack", 1);
memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->pci_intack);
/* TODO Remove once realize propagates to child devices. */
object_property_set_bool(OBJECT(&s->pci_bus), true, "realized", errp);
object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp);
}
static void raven_pcihost_initfn(Object *obj)
{
PCIHostState *h = PCI_HOST_BRIDGE(obj);
PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj);
MemoryRegion *address_space_mem = get_system_memory();
DeviceState *pci_dev;
memory_region_init(&s->pci_io, obj, "pci-io", 0x3f800000);
memory_region_init_io(&s->pci_io_non_contiguous, obj, &raven_io_ops, s,
"pci-io-non-contiguous", 0x00800000);
memory_region_init(&s->pci_memory, obj, "pci-memory", 0x3f000000);
address_space_init(&s->pci_io_as, &s->pci_io, "raven-io");
/* CPU address space */
memory_region_add_subregion(address_space_mem, 0x80000000, &s->pci_io);
memory_region_add_subregion_overlap(address_space_mem, 0x80000000,
&s->pci_io_non_contiguous, 1);
memory_region_add_subregion(address_space_mem, 0xc0000000, &s->pci_memory);
pci_root_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), NULL,
&s->pci_memory, &s->pci_io, 0, TYPE_PCI_BUS);
/* Bus master address space */
memory_region_init(&s->bm, obj, "bm-raven", UINT32_MAX);
memory_region_init_alias(&s->bm_pci_memory_alias, obj, "bm-pci-memory",
&s->pci_memory, 0,
memory_region_size(&s->pci_memory));
memory_region_init_alias(&s->bm_ram_alias, obj, "bm-system",
get_system_memory(), 0, 0x80000000);
memory_region_add_subregion(&s->bm, 0 , &s->bm_pci_memory_alias);
memory_region_add_subregion(&s->bm, 0x80000000, &s->bm_ram_alias);
address_space_init(&s->bm_as, &s->bm, "raven-bm");
pci_setup_iommu(&s->pci_bus, raven_pcihost_set_iommu, s);
h->bus = &s->pci_bus;
object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_RAVEN_PCI_DEVICE);
pci_dev = DEVICE(&s->pci_dev);
qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus));
object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr",
NULL);
qdev_prop_set_bit(pci_dev, "multifunction", false);
}
static void raven_realize(PCIDevice *d, Error **errp)
{
RavenPCIState *s = RAVEN_PCI_DEVICE(d);
char *filename;
int bios_size = -1;
d->config[0x0C] = 0x08; // cache_line_size
d->config[0x0D] = 0x10; // latency_timer
d->config[0x34] = 0x00; // capabilities_pointer
memory_region_init_ram_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE,
Fix bad error handling after memory_region_init_ram() Symptom: $ qemu-system-x86_64 -m 10000000 Unexpected error in ram_block_add() at /work/armbru/qemu/exec.c:1456: upstream-qemu: cannot set up guest memory 'pc.ram': Cannot allocate memory Aborted (core dumped) Root cause: commit ef701d7 screwed up handling of out-of-memory conditions. Before the commit, we report the error and exit(1), in one place, ram_block_add(). The commit lifts the error handling up the call chain some, to three places. Fine. Except it uses &error_abort in these places, changing the behavior from exit(1) to abort(), and thus undoing the work of commit 3922825 "exec: Don't abort when we can't allocate guest memory". The three places are: * memory_region_init_ram() Commit 4994653 (right after commit ef701d7) lifted the error handling further, through memory_region_init_ram(), multiplying the incorrect use of &error_abort. Later on, imitation of existing (bad) code may have created more. * memory_region_init_ram_ptr() The &error_abort is still there. * memory_region_init_rom_device() Doesn't need fixing, because commit 33e0eb5 (soon after commit ef701d7) lifted the error handling further, and in the process changed it from &error_abort to passing it up the call chain. Correct, because the callers are realize() methods. Fix the error handling after memory_region_init_ram() with a Coccinelle semantic patch: @r@ expression mr, owner, name, size, err; position p; @@ memory_region_init_ram(mr, owner, name, size, ( - &error_abort + &error_fatal | err@p ) ); @script:python@ p << r.p; @@ print "%s:%s:%s" % (p[0].file, p[0].line, p[0].column) When the last argument is &error_abort, it gets replaced by &error_fatal. This is the fix. If the last argument is anything else, its position is reported. This lets us check the fix is complete. Four positions get reported: * ram_backend_memory_alloc() Error is passed up the call chain, ultimately through user_creatable_complete(). As far as I can tell, it's callers all handle the error sanely. * fsl_imx25_realize(), fsl_imx31_realize(), dp8393x_realize() DeviceClass.realize() methods, errors handled sanely further up the call chain. We're good. Test case again behaves: $ qemu-system-x86_64 -m 10000000 qemu-system-x86_64: cannot set up guest memory 'pc.ram': Cannot allocate memory [Exit 1 ] The next commits will repair the rest of commit ef701d7's damage. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <1441983105-26376-3-git-send-email-armbru@redhat.com> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
2015-09-11 22:51:43 +08:00
&error_fatal);
memory_region_set_readonly(&s->bios, true);
memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE),
&s->bios);
if (s->bios_name) {
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name);
if (filename) {
if (s->elf_machine != EM_NONE) {
bios_size = load_elf(filename, NULL, NULL, NULL,
NULL, NULL, 1, s->elf_machine, 0, 0);
}
if (bios_size < 0) {
bios_size = get_image_size(filename);
if (bios_size > 0 && bios_size <= BIOS_SIZE) {
hwaddr bios_addr;
bios_size = (bios_size + 0xfff) & ~0xfff;
bios_addr = (uint32_t)(-BIOS_SIZE);
bios_size = load_image_targphys(filename, bios_addr,
bios_size);
}
}
}
g_free(filename);
if (bios_size < 0 || bios_size > BIOS_SIZE) {
memory_region_del_subregion(get_system_memory(), &s->bios);
error_setg(errp, "Could not load bios image '%s'", s->bios_name);
return;
}
}
vmstate_register_ram_global(&s->bios);
}
static const VMStateDescription vmstate_raven = {
.name = "raven",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, RavenPCIState),
VMSTATE_END_OF_LIST()
},
};
static void raven_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->realize = raven_realize;
k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
dc->desc = "PReP Host Bridge - Motorola Raven";
dc->vmsd = &vmstate_raven;
/*
* Reason: PCI-facing part of the host bridge, not usable without
* the host-facing part, which can't be device_add'ed, yet.
*/
qdev: Replace cannot_instantiate_with_device_add_yet with !user_creatable cannot_instantiate_with_device_add_yet was introduced by commit efec3dd631d94160288392721a5f9c39e50fb2bc to replace no_user. It was supposed to be a temporary measure. When it was introduced, we had 54 cannot_instantiate_with_device_add_yet=true lines in the code. Today (3 years later) this number has not shrunk: we now have 57 cannot_instantiate_with_device_add_yet=true lines. I think it is safe to say it is not a temporary measure, and we won't see the flag go away soon. Instead of a long field name that misleads people to believe it is temporary, replace it a shorter and less misleading field: user_creatable. Except for code comments, changes were generated using the following Coccinelle patch: @@ expression DC; @@ ( -DC->cannot_instantiate_with_device_add_yet = false; +DC->user_creatable = true; | -DC->cannot_instantiate_with_device_add_yet = true; +DC->user_creatable = false; ) @@ typedef ObjectClass; expression dc; identifier class, data; @@ static void device_class_init(ObjectClass *class, void *data) { ... dc->hotpluggable = true; +dc->user_creatable = true; ... } @@ @@ struct DeviceClass { ... -bool cannot_instantiate_with_device_add_yet; +bool user_creatable; ... } @@ expression DC; @@ ( -!DC->cannot_instantiate_with_device_add_yet +DC->user_creatable | -DC->cannot_instantiate_with_device_add_yet +!DC->user_creatable ) Cc: Alistair Francis <alistair.francis@xilinx.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Marcel Apfelbaum <marcel@redhat.com> Cc: Markus Armbruster <armbru@redhat.com> Cc: Peter Maydell <peter.maydell@linaro.org> Cc: Thomas Huth <thuth@redhat.com> Acked-by: Alistair Francis <alistair.francis@xilinx.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Marcel Apfelbaum <marcel@redhat.com> Acked-by: Marcel Apfelbaum <marcel@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Message-Id: <20170503203604.31462-2-ehabkost@redhat.com> [ehabkost: kept "TODO remove once we're there" comment] Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2017-05-04 04:35:44 +08:00
dc->user_creatable = false;
}
static const TypeInfo raven_info = {
.name = TYPE_RAVEN_PCI_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(RavenPCIState),
.class_init = raven_class_init,
pci: Add INTERFACE_CONVENTIONAL_PCI_DEVICE to Conventional PCI devices Add INTERFACE_CONVENTIONAL_PCI_DEVICE to all direct subtypes of TYPE_PCI_DEVICE, except: 1) The ones that already have INTERFACE_PCIE_DEVICE set: * base-xhci * e1000e * nvme * pvscsi * vfio-pci * virtio-pci * vmxnet3 2) base-pci-bridge Not all PCI bridges are Conventional PCI devices, so INTERFACE_CONVENTIONAL_PCI_DEVICE is added only to the subtypes that are actually Conventional PCI: * dec-21154-p2p-bridge * i82801b11-bridge * pbm-bridge * pci-bridge The direct subtypes of base-pci-bridge not touched by this patch are: * xilinx-pcie-root: Already marked as PCIe-only. * pcie-pci-bridge: Already marked as PCIe-only. * pcie-port: all non-abstract subtypes of pcie-port are already marked as PCIe-only devices. 3) megasas-base Not all megasas devices are Conventional PCI devices, so the interface names are added to the subclasses registered by megasas_register_types(), according to information in the megasas_devices[] array. "megasas-gen2" already implements INTERFACE_PCIE_DEVICE, so add INTERFACE_CONVENTIONAL_PCI_DEVICE only to "megasas". Acked-by: Alberto Garcia <berto@igalia.com> Acked-by: John Snow <jsnow@redhat.com> Acked-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Acked-by: David Gibson <david@gibson.dropbear.id.au> Reviewed-by: Marcel Apfelbaum <marcel@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-09-28 03:56:34 +08:00
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
static Property raven_pcihost_properties[] = {
DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine,
EM_NONE),
DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name),
DEFINE_PROP_END_OF_LIST()
};
static void raven_pcihost_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->realize = raven_pcihost_realizefn;
dc->props = raven_pcihost_properties;
dc->fw_name = "pci";
}
static const TypeInfo raven_pcihost_info = {
.name = TYPE_RAVEN_PCI_HOST_BRIDGE,
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(PREPPCIState),
.instance_init = raven_pcihost_initfn,
.class_init = raven_pcihost_class_init,
};
static void raven_register_types(void)
{
type_register_static(&raven_pcihost_info);
type_register_static(&raven_info);
}
type_init(raven_register_types)