summaryrefslogtreecommitdiffstats
path: root/hw/spapr_vio.c
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2012-04-25 17:55:41 +0000
committerAlexander Graf <agraf@suse.de>2012-05-01 21:47:00 +0200
commitd601fac478eee7391f3e7005a9321fbf38d74809 (patch)
tree1cab637a7d0fe97299b8591b1dda492b63db247a /hw/spapr_vio.c
parent892c587f22fc97362a595d3c84669a39ce1cd2f5 (diff)
downloadhqemu-d601fac478eee7391f3e7005a9321fbf38d74809.zip
hqemu-d601fac478eee7391f3e7005a9321fbf38d74809.tar.gz
pseries: Implement automatic PAPR VIO address allocation
PAPR virtual IO (VIO) devices require a unique, but otherwise arbitrary, "address" used as a token to the hypercalls which manipulate them. Currently the pseries machine code does an ok job of allocating these addresses when the legacy -net nic / -serial and so forth options are used but will fail to allocate them properly when using -device. Specifically, you can use -device if all addresses are explicitly assigned. Without explicit assignment, only one VIO device of each type (network, console, SCSI) will be assigned properly, any further ones will attempt to take the same address leading to a fatal error. This patch fixes the situation by adding a proper address allocator to the VIO "bus" code. This is used both by -device and the legacy options and default devices. Addresses can still be explicitly assigned with -device options if desired. This patch changes the (guest visible) numbering of VIO devices, but since their addresses are discovered using the device tree and already differ from the numbering found on existing PowerVM systems, this does not break compatibility. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr_vio.c')
-rw-r--r--hw/spapr_vio.c54
1 files changed, 34 insertions, 20 deletions
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index fccf48b..315ab80 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -620,28 +620,22 @@ static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
rtas_st(rets, 0, 0);
}
-static int spapr_vio_check_reg(VIOsPAPRDevice *sdev)
+static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
{
- VIOsPAPRDevice *other_sdev;
+ VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
DeviceState *qdev;
- VIOsPAPRBus *sbus;
-
- sbus = DO_UPCAST(VIOsPAPRBus, bus, sdev->qdev.parent_bus);
+ VIOsPAPRDevice *other;
/*
- * Check two device aren't given clashing addresses by the user (or some
- * other mechanism). We have to open code this because we have to check
- * for matches with devices other than us.
+ * Check for a device other than the given one which is already
+ * using the requested address. We have to open code this because
+ * the given dev might already be in the list.
*/
- QTAILQ_FOREACH(qdev, &sbus->bus.children, sibling) {
- other_sdev = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
+ QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
+ other = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
- if (other_sdev != sdev && other_sdev->reg == sdev->reg) {
- fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
- object_get_typename(OBJECT(sdev)),
- object_get_typename(OBJECT(qdev)),
- sdev->reg);
- return -EEXIST;
+ if (other != dev && other->reg == dev->reg) {
+ return other;
}
}
@@ -667,11 +661,30 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
char *id;
- int ret;
- ret = spapr_vio_check_reg(dev);
- if (ret) {
- return ret;
+ if (dev->reg != -1) {
+ /*
+ * Explicitly assigned address, just verify that no-one else
+ * is using it. other mechanism). We have to open code this
+ * rather than using spapr_vio_find_by_reg() because sdev
+ * itself is already in the list.
+ */
+ VIOsPAPRDevice *other = reg_conflict(dev);
+
+ if (other) {
+ fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
+ object_get_typename(OBJECT(qdev)),
+ object_get_typename(OBJECT(&other->qdev)),
+ dev->reg);
+ return -1;
+ }
+ } else {
+ /* Need to assign an address */
+ VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
+
+ do {
+ dev->reg = bus->next_reg++;
+ } while (reg_conflict(dev));
}
/* Don't overwrite ids assigned on the command line */
@@ -731,6 +744,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio");
bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
+ bus->next_reg = 0x1000;
/* hcall-vio */
spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
OpenPOWER on IntegriCloud