spapr_vty.c 5.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#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;

27 28 29 30
    if ((dev->in == dev->out) && size) {
        /* toggle line to simulate edge interrupt */
        qemu_irq_pulse(dev->sdev.qirq);
    }
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    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;

53 54
    /* FIXME: should check the qemu_chr_fe_write() return value */
    qemu_chr_fe_write(dev->chardev, buf, len);
55 56 57 58 59 60
}

static int spapr_vty_init(VIOsPAPRDevice *sdev)
{
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;

61 62 63 64 65
    if (!dev->chardev) {
        fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n");
        exit(1);
    }

66 67 68 69 70 71
    qemu_chr_add_handlers(dev->chardev, vty_can_receive,
                          vty_receive, NULL, dev);

    return 0;
}

72 73 74
/* Forward declaration */
static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg);

75 76 77 78 79 80 81
static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
{
    target_ulong reg = args[0];
    target_ulong len = args[1];
    target_ulong char0_7 = args[2];
    target_ulong char8_15 = args[3];
82
    VIOsPAPRDevice *sdev;
83 84
    uint8_t buf[16];

85
    sdev = vty_lookup(spapr, reg);
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    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 H_SUCCESS;
}

static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
                                    target_ulong opcode, target_ulong *args)
{
    target_ulong reg = args[0];
    target_ulong *len = args + 0;
    target_ulong *char0_7 = args + 1;
    target_ulong *char8_15 = args + 2;
109
    VIOsPAPRDevice *sdev;
110 111
    uint8_t buf[16];

112
    sdev = vty_lookup(spapr, reg);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    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;
}

P
Paolo Bonzini 已提交
128
void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
{
    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[]) {
146
        DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, SPAPR_VTY_BASE_ADDRESS, 0),
147 148 149 150 151
        DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
        DEFINE_PROP_END_OF_LIST(),
    },
};

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
static VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus)
{
    VIOsPAPRDevice *sdev, *selected;
    DeviceState *iter;

    /*
     * To avoid the console bouncing around we want one VTY to be
     * the "default". We haven't really got anything to go on, so
     * arbitrarily choose the one with the lowest reg value.
     */

    selected = NULL;
    QTAILQ_FOREACH(iter, &bus->bus.children, sibling) {
        /* Only look at VTY devices */
        if (iter->info != &spapr_vty.qdev) {
            continue;
        }

        sdev = DO_UPCAST(VIOsPAPRDevice, qdev, iter);

        /* First VTY we've found, so it is selected for now */
        if (!selected) {
            selected = sdev;
            continue;
        }

        /* Choose VTY with lowest reg value */
        if (sdev->reg < selected->reg) {
            selected = sdev;
        }
    }

    return selected;
}

187 188 189 190 191 192 193
static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg)
{
    VIOsPAPRDevice *sdev;

    sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
    if (!sdev && reg == 0) {
        /* Hack for kernel early debug, which always specifies reg==0.
194 195
         * We search all VIO devices, and grab the vty with the lowest
         * reg.  This attempts to mimic existing PowerVM behaviour
196 197
         * (early debug does work there, despite having no vty with
         * reg==0. */
198
        return spapr_vty_get_default(spapr->vio_bus);
199 200 201 202 203
    }

    return sdev;
}

204 205 206
static void spapr_vty_register(void)
{
    spapr_vio_bus_register_withprop(&spapr_vty);
D
David Gibson 已提交
207 208
    spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
    spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
209 210
}
device_init(spapr_vty_register);