aboutsummaryrefslogtreecommitdiff
path: root/hw/spapr_vio.c
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2011-04-01 15:15:21 +1100
committerAlexander Graf <agraf@suse.de>2011-04-01 18:34:55 +0200
commit4040ab72379f75fe171c03f93ceb75efb48c14a5 (patch)
tree0d7f11215e5032e24f10cca36d48aeff4d7b0edb /hw/spapr_vio.c
parent9fdf0c2995d04a74a22ea4609b2931eef209e53d (diff)
Implement the bus structure for PAPR virtual IO
This extends the "pseries" (PAPR) machine to include a virtual IO bus supporting the PAPR defined hypercall based virtual IO mechanisms. So far only one VIO device is provided, the vty / vterm, providing a full console (polled only, for now). Signed-off-by: David Gibson <dwg@au1.ibm.com> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr_vio.c')
-rw-r--r--hw/spapr_vio.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
new file mode 100644
index 0000000000..10acb4c308
--- /dev/null
+++ b/hw/spapr_vio.c
@@ -0,0 +1,214 @@
+/*
+ * QEMU sPAPR VIO code
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
+ * Based on the s390 virtio bus code:
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+#include "device_tree.h"
+
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+
+#ifdef CONFIG_FDT
+#include <libfdt.h>
+#endif /* CONFIG_FDT */
+
+/* #define DEBUG_SPAPR */
+
+#ifdef DEBUG_SPAPR
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+static struct BusInfo spapr_vio_bus_info = {
+ .name = "spapr-vio",
+ .size = sizeof(VIOsPAPRBus),
+};
+
+VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
+{
+ DeviceState *qdev;
+ VIOsPAPRDevice *dev = NULL;
+
+ QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ dev = (VIOsPAPRDevice *)qdev;
+ if (dev->reg == reg) {
+ break;
+ }
+ }
+
+ return dev;
+}
+
+#ifdef CONFIG_FDT
+static int vio_make_devnode(VIOsPAPRDevice *dev,
+ void *fdt)
+{
+ VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+ int vdevice_off, node_off;
+ int ret;
+
+ vdevice_off = fdt_path_offset(fdt, "/vdevice");
+ if (vdevice_off < 0) {
+ return vdevice_off;
+ }
+
+ node_off = fdt_add_subnode(fdt, vdevice_off, dev->qdev.id);
+ if (node_off < 0) {
+ return node_off;
+ }
+
+ ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (info->dt_type) {
+ ret = fdt_setprop_string(fdt, node_off, "device_type",
+ info->dt_type);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (info->dt_compatible) {
+ ret = fdt_setprop_string(fdt, node_off, "compatible",
+ info->dt_compatible);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (info->devnode) {
+ ret = (info->devnode)(dev, fdt, node_off);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return node_off;
+}
+#endif /* CONFIG_FDT */
+
+static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
+{
+ VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
+ VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
+ char *id;
+
+ if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) {
+ return -1;
+ }
+
+ dev->qdev.id = id;
+
+ return info->init(dev);
+}
+
+void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info)
+{
+ info->qdev.init = spapr_vio_busdev_init;
+ info->qdev.bus_info = &spapr_vio_bus_info;
+
+ assert(info->qdev.size >= sizeof(VIOsPAPRDevice));
+ qdev_register(&info->qdev);
+}
+
+VIOsPAPRBus *spapr_vio_bus_init(void)
+{
+ VIOsPAPRBus *bus;
+ BusState *qbus;
+ DeviceState *dev;
+ DeviceInfo *qinfo;
+
+ /* Create bridge device */
+ dev = qdev_create(NULL, "spapr-vio-bridge");
+ qdev_init_nofail(dev);
+
+ /* Create bus on bridge device */
+
+ qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio");
+ bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
+
+ for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
+ VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
+
+ if (qinfo->bus_info != &spapr_vio_bus_info) {
+ continue;
+ }
+
+ if (info->hcalls) {
+ info->hcalls(bus);
+ }
+ }
+
+ return bus;
+}
+
+/* Represents sPAPR hcall VIO devices */
+
+static int spapr_vio_bridge_init(SysBusDevice *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+static SysBusDeviceInfo spapr_vio_bridge_info = {
+ .init = spapr_vio_bridge_init,
+ .qdev.name = "spapr-vio-bridge",
+ .qdev.size = sizeof(SysBusDevice),
+ .qdev.no_user = 1,
+};
+
+static void spapr_vio_register_devices(void)
+{
+ sysbus_register_withprop(&spapr_vio_bridge_info);
+}
+
+device_init(spapr_vio_register_devices)
+
+#ifdef CONFIG_FDT
+int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
+{
+ DeviceState *qdev;
+ int ret = 0;
+
+ QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
+
+ ret = vio_make_devnode(dev, fdt);
+
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_FDT */