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


Reply via email to