From patchwork Sat Feb 12 14:54:27 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Gibson X-Patchwork-Id: 82929 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 45021B70B0 for ; Sun, 13 Feb 2011 02:21:19 +1100 (EST) Received: from localhost ([127.0.0.1]:44792 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PoHFN-0004Ss-0u for incoming@patchwork.ozlabs.org; Sat, 12 Feb 2011 10:18:53 -0500 Received: from [140.186.70.92] (port=42366 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PoGsE-0000Bz-10 for qemu-devel@nongnu.org; Sat, 12 Feb 2011 09:55:00 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PoGsA-0006xZ-7R for qemu-devel@nongnu.org; Sat, 12 Feb 2011 09:54:57 -0500 Received: from ozlabs.org ([203.10.76.45]:59883) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PoGs9-0006sC-IF for qemu-devel@nongnu.org; Sat, 12 Feb 2011 09:54:54 -0500 Received: by ozlabs.org (Postfix, from userid 1007) id 4F40AB714D; Sun, 13 Feb 2011 01:54:38 +1100 (EST) From: David Gibson To: qemu-devel@nongnu.org Date: Sun, 13 Feb 2011 01:54:27 +1100 Message-Id: <1297522467-5975-16-git-send-email-david@gibson.dropbear.id.au> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1297522467-5975-1-git-send-email-david@gibson.dropbear.id.au> References: <1297522467-5975-1-git-send-email-david@gibson.dropbear.id.au> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 203.10.76.45 Cc: paulus@samba.org, agraf@suse.de, anton@samba.org Subject: [Qemu-devel] [PATCH 15/15] Implement the bus structure for PAPR virtual IO X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- Makefile.target | 3 +- hw/spapr.c | 31 +++++++++- hw/spapr.h | 10 +++ hw/spapr_hcall.c | 19 ++---- hw/spapr_vio.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/spapr_vio.h | 49 ++++++++++++++ hw/spapr_vty.c | 132 +++++++++++++++++++++++++++++++++++++ 7 files changed, 419 insertions(+), 16 deletions(-) create mode 100644 hw/spapr_vio.c create mode 100644 hw/spapr_vio.h create mode 100644 hw/spapr_vty.c diff --git a/Makefile.target b/Makefile.target index e0796ba..fe232da 100644 --- a/Makefile.target +++ b/Makefile.target @@ -232,7 +232,8 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o # IBM pSeries (sPAPR) -obj-ppc-y += spapr.o spapr_hcall.o +obj-ppc-y += spapr.o spapr_hcall.o spapr_vio.o +obj-ppc-y += spapr_vty.o # PowerPC 4xx boards obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o diff --git a/hw/spapr.c b/hw/spapr.c index 8aca4e0..da61061 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -37,6 +37,7 @@ #include "net.h" #include "blockdev.h" #include "hw/spapr.h" +#include "hw/spapr_vio.h" #include @@ -49,6 +50,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize, const char *cpu_model, CPUState *envs[], + sPAPREnvironment *spapr, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *kernel_cmdline) @@ -59,6 +61,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize, uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); int i; char *modelname; + int ret; #define _FDT(exp) \ do { \ @@ -151,9 +154,28 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize, _FDT((fdt_end_node(fdt))); + /* vdevice */ + _FDT((fdt_begin_node(fdt, "vdevice"))); + + _FDT((fdt_property_string(fdt, "device_type", "vdevice"))); + _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice"))); + _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); + + _FDT((fdt_end_node(fdt))); + _FDT((fdt_end_node(fdt))); /* close root node */ _FDT((fdt_finish(fdt))); + /* re-expand to allow for further tweaks */ + _FDT((fdt_open_into(fdt, fdt, FDT_MAX_SIZE))); + + ret = spapr_populate_vdevice(spapr->vio_bus, fdt); + if (ret < 0) + fprintf(stderr, "couldn't setup vio devices in fdt\n"); + + _FDT((fdt_pack(fdt))); + if (fdt_size) *fdt_size = fdt_totalsize(fdt); @@ -211,6 +233,12 @@ static void ppc_spapr_init (ram_addr_t ram_size, ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset); + spapr->vio_bus = spapr_vio_bus_init(); + + for (i = 0; i < MAX_SERIAL_PORTS; i++) + if (serial_hds[i]) + spapr_vty_create(spapr->vio_bus, i, serial_hds[i]); + if (kernel_filename) { uint64_t lowaddr = 0; @@ -242,7 +270,7 @@ static void ppc_spapr_init (ram_addr_t ram_size, } /* load fdt */ - fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, &env, + fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, &env, spapr, initrd_base, initrd_size, kernel_cmdline); if (!fdt) { @@ -267,6 +295,7 @@ static QEMUMachine spapr_machine = { .desc = "pSeries Logical Partition (PAPR compliant)", .init = ppc_spapr_init, .max_cpus = 1, + .no_parallel = 1, .no_vga = 1, .no_parallel = 1, }; diff --git a/hw/spapr.h b/hw/spapr.h index dae9617..168511f 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -1,7 +1,10 @@ #if !defined (__HW_SPAPR_H__) #define __HW_SPAPR_H__ +struct VIOsPAPRBus; + typedef struct sPAPREnvironment { + struct VIOsPAPRBus *vio_bus; } sPAPREnvironment; #define H_SUCCESS 0 @@ -237,4 +240,11 @@ typedef struct sPAPREnvironment { target_ulong spapr_hypercall(CPUState *env, sPAPREnvironment *spapr, target_ulong token, target_ulong *args); +target_ulong h_put_term_char(sPAPREnvironment *spapr, + target_ulong termno, target_ulong len, + target_ulong char0_7, target_ulong char8_15); +target_ulong h_get_term_char(sPAPREnvironment *spapr, + target_ulong termno, target_ulong *len, + target_ulong *char0_7, target_ulong *char8_15); + #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index c99c345..e2ed9cf 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -3,19 +3,6 @@ #include "qemu-char.h" #include "hw/spapr.h" -static target_ulong h_put_term_char(target_ulong termno, target_ulong len, - target_ulong char0_7, target_ulong char8_15) -{ - uint8_t buf[16]; - - *((uint64_t *)buf) = cpu_to_be64(char0_7); - *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); - - qemu_chr_write(serial_hds[0], buf, len); - - return 0; -} - target_ulong spapr_hypercall(CPUState *env, sPAPREnvironment *spapr, target_ulong token, target_ulong *args) { @@ -29,7 +16,11 @@ target_ulong spapr_hypercall(CPUState *env, sPAPREnvironment *spapr, switch (token) { case H_PUT_TERM_CHAR: - r = h_put_term_char(args[0], args[1], args[2], args[3]); + r = h_put_term_char(spapr, args[0], args[1], args[2], args[3]); + break; + + case H_GET_TERM_CHAR: + r = h_get_term_char(spapr, args[0], &args[0], &args[1], &args[2]); break; default: diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c new file mode 100644 index 0000000..d9c7292 --- /dev/null +++ b/hw/spapr_vio.c @@ -0,0 +1,191 @@ +/* + * QEMU sPAPR VIO code + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 Alexander Graf + * + * 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 . + */ + +#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 +#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 + +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; +} + +VIOsPAPRBus *spapr_vio_bus_init(void) +{ + VIOsPAPRBus *bus; + BusState *_bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, "spapr-vio-bridge"); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + + _bus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio"); + bus = DO_UPCAST(VIOsPAPRBus, bus, _bus); + + return bus; +} + +#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 *dev, DeviceInfo *info) +{ + VIOsPAPRDeviceInfo *_info = (VIOsPAPRDeviceInfo *)info; + VIOsPAPRDevice *_dev = (VIOsPAPRDevice *)dev; + 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); +} + +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 */ diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h new file mode 100644 index 0000000..fb5e301 --- /dev/null +++ b/hw/spapr_vio.h @@ -0,0 +1,49 @@ +#ifndef _HW_SPAPR_VIO_H +#define _HW_SPAPR_VIO_H +/* + * QEMU sPAPR VIO bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus definitions: + * Copyright (c) 2009 Alexander Graf + * + * 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 . + */ + +typedef struct VIOsPAPRDevice { + DeviceState qdev; + uint32_t reg; +} VIOsPAPRDevice; + +typedef struct VIOsPAPRBus { + BusState bus; +} VIOsPAPRBus; + +typedef struct { + DeviceInfo qdev; + const char *dt_name, *dt_type, *dt_compatible; + int (*init)(VIOsPAPRDevice *dev); + int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); +} VIOsPAPRDeviceInfo; + +extern VIOsPAPRBus *spapr_vio_bus_init(void); +extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); +extern void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info); +extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); + +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); +void spapr_vty_create(VIOsPAPRBus *bus, + uint32_t reg, CharDriverState *chardev); + +#endif /* _HW_SPAPR_VIO_H */ diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c new file mode 100644 index 0000000..9a2dc0b --- /dev/null +++ b/hw/spapr_vty.c @@ -0,0 +1,132 @@ +#include "qdev.h" +#include "qemu-char.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + +#define VTERM_BUFSIZE 16 + +typedef struct VIOsPAPRVTYDevice { + VIOsPAPRDevice sdev; + CharDriverState *chardev; + uint32_t in, out; + uint8_t buf[VTERM_BUFSIZE]; +} VIOsPAPRVTYDevice; + +static int vty_can_receive(void *opaque) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + + return (dev->in - dev->out) < VTERM_BUFSIZE; +} + +static void vty_receive(void *opaque, const uint8_t *buf, int size) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + int i; + + for (i = 0; i < size; i++) { + assert((dev->in - dev->out) < VTERM_BUFSIZE); + dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i]; + } +} + +static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + int n = 0; + + while ((n < max) && (dev->out != dev->in)) + buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; + + return n; +} + +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + /* FIXME: should check the qemu_chr_write() return value */ + qemu_chr_write(dev->chardev, buf, len); +} + +static int spapr_vty_init(VIOsPAPRDevice *sdev) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + qemu_chr_add_handlers(dev->chardev, vty_can_receive, + vty_receive, NULL, dev); + + return 0; +} + +target_ulong h_put_term_char(sPAPREnvironment *spapr, + target_ulong termno, target_ulong len, + target_ulong char0_7, target_ulong char8_15) +{ + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, termno); + uint8_t buf[16]; + + if (!sdev) + return H_PARAMETER; + + if (len > 16) + return H_PARAMETER; + + *((uint64_t *)buf) = cpu_to_be64(char0_7); + *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); + + vty_putchars(sdev, buf, len); + + return 0; +} + +target_ulong h_get_term_char(sPAPREnvironment *spapr, + target_ulong termno, target_ulong *len, + target_ulong *char0_7, target_ulong *char8_15) +{ + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, termno); + uint8_t buf[16]; + + if (!sdev) + return H_PARAMETER; + + *len = vty_getchars(sdev, buf, sizeof(buf)); + if (*len < 16) + memset(buf + *len, 0, 16 - *len); + + *char0_7 = be64_to_cpu(*((uint64_t *)buf)); + *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1)); + + return H_SUCCESS; +} + +void spapr_vty_create(VIOsPAPRBus *bus, + uint32_t reg, CharDriverState *chardev) +{ + DeviceState *dev; + + dev = qdev_create(&bus->bus, "spapr-vty"); + qdev_prop_set_uint32(dev, "reg", reg); + qdev_prop_set_chr(dev, "chardev", chardev); + qdev_init_nofail(dev); +} + +static VIOsPAPRDeviceInfo spapr_vty = { + .init = spapr_vty_init, + .dt_name = "vty", + .dt_type = "serial", + .dt_compatible = "hvterm1", + .qdev.name = "spapr-vty", + .qdev.size = sizeof(VIOsPAPRVTYDevice), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0), + DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void spapr_vty_register(void) +{ + spapr_vio_bus_register_withprop(&spapr_vty); +} +device_init(spapr_vty_register);