On 12.02.2011, at 15:54, David Gibson wrote: > 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 <d...@au1.ibm.com> > --- > 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 <libfdt.h> > > @@ -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)
Braces.. > + 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++) Braces.. > + if (serial_hds[i]) Braces.. > + spapr_vty_create(spapr->vio_bus, i, serial_hds[i]); There might be a qdev way to do this. Blue? > + > 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, duplicate? > .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]); Slick and simple. Blue, do you think there's some random abstraction layer necessary? > 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 > <da...@gibson.dropbear.id.au> > + * Based on the s390 virtio bus code: > + * Copyright (c) 2009 Alexander Graf <ag...@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 > + > +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) Braces > + break; > + } > + > + return dev; What if the device doesn't exist? > +} > + > +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) Braces > + return vdevice_off; > + > + node_off = fdt_add_subnode(fdt, vdevice_off, dev->qdev.id); > + if (node_off < 0) Braces > + return node_off; > + > + ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg); > + if (ret < 0) Braces > + return ret; > + > + if (info->dt_type) { > + ret = fdt_setprop_string(fdt, node_off, "device_type", > + info->dt_type); > + if (ret < 0) Braces I'll stop complaining about braces now. Please go through the patch yourself and just fix them up :) > + 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 > <da...@gibson.dropbear.id.au> > + * Based on the s390 virtio bus definitions: > + * Copyright (c) 2009 Alexander Graf <ag...@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/>. > + */ > + > +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); > -- > 1.7.1 > Alex