10d75590dSPeter Maydell #include "qemu/osdep.h"
27bacfd7fSThomas Huth #include "qemu/error-report.h"
30b8fa32fSMarkus Armbruster #include "qemu/module.h"
4da34e65cSMarkus Armbruster #include "qapi/error.h"
5d6454270SMarkus Armbruster #include "migration/vmstate.h"
64d43a603SMarc-André Lureau #include "chardev/char-fe.h"
79944d320SPaolo Bonzini #include "hw/ppc/spapr.h"
89944d320SPaolo Bonzini #include "hw/ppc/spapr_vio.h"
9a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
10ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
11db1015e9SEduardo Habkost #include "qom/object.h"
129944d320SPaolo Bonzini
139944d320SPaolo Bonzini #define VTERM_BUFSIZE 16
149944d320SPaolo Bonzini
15db1015e9SEduardo Habkost struct SpaprVioVty {
16ce2918cbSDavid Gibson SpaprVioDevice sdev;
17becdfa00SMarc-André Lureau CharBackend chardev;
189944d320SPaolo Bonzini uint32_t in, out;
199944d320SPaolo Bonzini uint8_t buf[VTERM_BUFSIZE];
20db1015e9SEduardo Habkost };
219944d320SPaolo Bonzini
22fd506b4fSDavid Gibson #define TYPE_VIO_SPAPR_VTY_DEVICE "spapr-vty"
OBJECT_DECLARE_SIMPLE_TYPE(SpaprVioVty,VIO_SPAPR_VTY_DEVICE)238063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(SpaprVioVty, VIO_SPAPR_VTY_DEVICE)
24fd506b4fSDavid Gibson
259944d320SPaolo Bonzini static int vty_can_receive(void *opaque)
269944d320SPaolo Bonzini {
27ce2918cbSDavid Gibson SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque);
289944d320SPaolo Bonzini
298a273cbeSThomas Huth return VTERM_BUFSIZE - (dev->in - dev->out);
309944d320SPaolo Bonzini }
319944d320SPaolo Bonzini
vty_receive(void * opaque,const uint8_t * buf,int size)329944d320SPaolo Bonzini static void vty_receive(void *opaque, const uint8_t *buf, int size)
339944d320SPaolo Bonzini {
34ce2918cbSDavid Gibson SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque);
359944d320SPaolo Bonzini int i;
369944d320SPaolo Bonzini
379944d320SPaolo Bonzini if ((dev->in == dev->out) && size) {
389944d320SPaolo Bonzini /* toggle line to simulate edge interrupt */
397678b74aSDavid Gibson spapr_vio_irq_pulse(&dev->sdev);
409944d320SPaolo Bonzini }
419944d320SPaolo Bonzini for (i = 0; i < size; i++) {
427bacfd7fSThomas Huth if (dev->in - dev->out >= VTERM_BUFSIZE) {
437bacfd7fSThomas Huth static bool reported;
447bacfd7fSThomas Huth if (!reported) {
457bacfd7fSThomas Huth error_report("VTY input buffer exhausted - characters dropped."
467bacfd7fSThomas Huth " (input size = %i)", size);
477bacfd7fSThomas Huth reported = true;
487bacfd7fSThomas Huth }
497bacfd7fSThomas Huth break;
507bacfd7fSThomas Huth }
519944d320SPaolo Bonzini dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
529944d320SPaolo Bonzini }
539944d320SPaolo Bonzini }
549944d320SPaolo Bonzini
vty_getchars(SpaprVioDevice * sdev,uint8_t * buf,int max)55ce2918cbSDavid Gibson static int vty_getchars(SpaprVioDevice *sdev, uint8_t *buf, int max)
569944d320SPaolo Bonzini {
57ce2918cbSDavid Gibson SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
589944d320SPaolo Bonzini int n = 0;
599944d320SPaolo Bonzini
609944d320SPaolo Bonzini while ((n < max) && (dev->out != dev->in)) {
61fd38b162SPaul Mackerras /*
62fd38b162SPaul Mackerras * Long ago, PowerVM's vty implementation had a bug where it
63fd38b162SPaul Mackerras * inserted a \0 after every \r going to the guest. Existing
64fd38b162SPaul Mackerras * guests have a workaround for this which removes every \0
65fd38b162SPaul Mackerras * immediately following a \r. To avoid triggering this
66fd38b162SPaul Mackerras * workaround, we stop before inserting a \0 if the preceding
67fd38b162SPaul Mackerras * character in the output buffer is a \r.
68fd38b162SPaul Mackerras */
69fd38b162SPaul Mackerras if (n > 0 && (buf[n - 1] == '\r') &&
70fd38b162SPaul Mackerras (dev->buf[dev->out % VTERM_BUFSIZE] == '\0')) {
716c3bc244SDavid Gibson break;
726c3bc244SDavid Gibson }
73fd38b162SPaul Mackerras buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
749944d320SPaolo Bonzini }
759944d320SPaolo Bonzini
765345fdb4SMarc-André Lureau qemu_chr_fe_accept_input(&dev->chardev);
777770b6f7SAnton Blanchard
789944d320SPaolo Bonzini return n;
799944d320SPaolo Bonzini }
809944d320SPaolo Bonzini
vty_putchars(SpaprVioDevice * sdev,uint8_t * buf,int len)81ce2918cbSDavid Gibson void vty_putchars(SpaprVioDevice *sdev, uint8_t *buf, int len)
829944d320SPaolo Bonzini {
83ce2918cbSDavid Gibson SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
849944d320SPaolo Bonzini
856ab3fc32SDaniel P. Berrange /* XXX this blocks entire thread. Rewrite to use
866ab3fc32SDaniel P. Berrange * qemu_chr_fe_write and background I/O callbacks */
875345fdb4SMarc-André Lureau qemu_chr_fe_write_all(&dev->chardev, buf, len);
889944d320SPaolo Bonzini }
899944d320SPaolo Bonzini
spapr_vty_realize(SpaprVioDevice * sdev,Error ** errp)90ce2918cbSDavid Gibson static void spapr_vty_realize(SpaprVioDevice *sdev, Error **errp)
919944d320SPaolo Bonzini {
92ce2918cbSDavid Gibson SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
939944d320SPaolo Bonzini
9430650701SAnton Nefedov if (!qemu_chr_fe_backend_connected(&dev->chardev)) {
9528b07e73SMarkus Armbruster error_setg(errp, "chardev property not set");
9628b07e73SMarkus Armbruster return;
979944d320SPaolo Bonzini }
989944d320SPaolo Bonzini
995345fdb4SMarc-André Lureau qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive,
10081517ba3SAnton Nefedov vty_receive, NULL, NULL, dev, NULL, true);
1019944d320SPaolo Bonzini }
1029944d320SPaolo Bonzini
1039944d320SPaolo Bonzini /* Forward declaration */
h_put_term_char(PowerPCCPU * cpu,SpaprMachineState * spapr,target_ulong opcode,target_ulong * args)104ce2918cbSDavid Gibson static target_ulong h_put_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr,
1059944d320SPaolo Bonzini target_ulong opcode, target_ulong *args)
1069944d320SPaolo Bonzini {
1079944d320SPaolo Bonzini target_ulong reg = args[0];
1089944d320SPaolo Bonzini target_ulong len = args[1];
1099944d320SPaolo Bonzini target_ulong char0_7 = args[2];
1109944d320SPaolo Bonzini target_ulong char8_15 = args[3];
111ce2918cbSDavid Gibson SpaprVioDevice *sdev;
1129944d320SPaolo Bonzini uint8_t buf[16];
1139944d320SPaolo Bonzini
1149944d320SPaolo Bonzini sdev = vty_lookup(spapr, reg);
1159944d320SPaolo Bonzini if (!sdev) {
1169944d320SPaolo Bonzini return H_PARAMETER;
1179944d320SPaolo Bonzini }
1189944d320SPaolo Bonzini
1199944d320SPaolo Bonzini if (len > 16) {
1209944d320SPaolo Bonzini return H_PARAMETER;
1219944d320SPaolo Bonzini }
1229944d320SPaolo Bonzini
1239944d320SPaolo Bonzini *((uint64_t *)buf) = cpu_to_be64(char0_7);
1249944d320SPaolo Bonzini *((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
1259944d320SPaolo Bonzini
1269944d320SPaolo Bonzini vty_putchars(sdev, buf, len);
1279944d320SPaolo Bonzini
1289944d320SPaolo Bonzini return H_SUCCESS;
1299944d320SPaolo Bonzini }
1309944d320SPaolo Bonzini
h_get_term_char(PowerPCCPU * cpu,SpaprMachineState * spapr,target_ulong opcode,target_ulong * args)131ce2918cbSDavid Gibson static target_ulong h_get_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr,
1329944d320SPaolo Bonzini target_ulong opcode, target_ulong *args)
1339944d320SPaolo Bonzini {
1349944d320SPaolo Bonzini target_ulong reg = args[0];
1359944d320SPaolo Bonzini target_ulong *len = args + 0;
1369944d320SPaolo Bonzini target_ulong *char0_7 = args + 1;
1379944d320SPaolo Bonzini target_ulong *char8_15 = args + 2;
138ce2918cbSDavid Gibson SpaprVioDevice *sdev;
1399944d320SPaolo Bonzini uint8_t buf[16];
1409944d320SPaolo Bonzini
1419944d320SPaolo Bonzini sdev = vty_lookup(spapr, reg);
1429944d320SPaolo Bonzini if (!sdev) {
1439944d320SPaolo Bonzini return H_PARAMETER;
1449944d320SPaolo Bonzini }
1459944d320SPaolo Bonzini
1469944d320SPaolo Bonzini *len = vty_getchars(sdev, buf, sizeof(buf));
1479944d320SPaolo Bonzini if (*len < 16) {
1489944d320SPaolo Bonzini memset(buf + *len, 0, 16 - *len);
1499944d320SPaolo Bonzini }
1509944d320SPaolo Bonzini
1519944d320SPaolo Bonzini *char0_7 = be64_to_cpu(*((uint64_t *)buf));
1529944d320SPaolo Bonzini *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
1539944d320SPaolo Bonzini
1549944d320SPaolo Bonzini return H_SUCCESS;
1559944d320SPaolo Bonzini }
1569944d320SPaolo Bonzini
spapr_vty_create(SpaprVioBus * bus,Chardev * chardev)157ce2918cbSDavid Gibson void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev)
1589944d320SPaolo Bonzini {
1599944d320SPaolo Bonzini DeviceState *dev;
1609944d320SPaolo Bonzini
1613e80f690SMarkus Armbruster dev = qdev_new("spapr-vty");
1629944d320SPaolo Bonzini qdev_prop_set_chr(dev, "chardev", chardev);
1633e80f690SMarkus Armbruster qdev_realize_and_unref(dev, &bus->bus, &error_fatal);
1649944d320SPaolo Bonzini }
1659944d320SPaolo Bonzini
1669944d320SPaolo Bonzini static Property spapr_vty_properties[] = {
167ce2918cbSDavid Gibson DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev),
168ce2918cbSDavid Gibson DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev),
1699944d320SPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
1709944d320SPaolo Bonzini };
1719944d320SPaolo Bonzini
172db1b58e9SDavid Gibson static const VMStateDescription vmstate_spapr_vty = {
173db1b58e9SDavid Gibson .name = "spapr_vty",
174db1b58e9SDavid Gibson .version_id = 1,
175db1b58e9SDavid Gibson .minimum_version_id = 1,
176*2f6cab05SRichard Henderson .fields = (const VMStateField[]) {
177ce2918cbSDavid Gibson VMSTATE_SPAPR_VIO(sdev, SpaprVioVty),
178db1b58e9SDavid Gibson
179ce2918cbSDavid Gibson VMSTATE_UINT32(in, SpaprVioVty),
180ce2918cbSDavid Gibson VMSTATE_UINT32(out, SpaprVioVty),
181ce2918cbSDavid Gibson VMSTATE_BUFFER(buf, SpaprVioVty),
182db1b58e9SDavid Gibson VMSTATE_END_OF_LIST()
183db1b58e9SDavid Gibson },
184db1b58e9SDavid Gibson };
185db1b58e9SDavid Gibson
spapr_vty_class_init(ObjectClass * klass,void * data)1869944d320SPaolo Bonzini static void spapr_vty_class_init(ObjectClass *klass, void *data)
1879944d320SPaolo Bonzini {
1889944d320SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
189ce2918cbSDavid Gibson SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
1909944d320SPaolo Bonzini
19128b07e73SMarkus Armbruster k->realize = spapr_vty_realize;
1929944d320SPaolo Bonzini k->dt_name = "vty";
1939944d320SPaolo Bonzini k->dt_type = "serial";
1949944d320SPaolo Bonzini k->dt_compatible = "hvterm1";
19529fdedfeSAlexey Kardashevskiy set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
1964f67d30bSMarc-André Lureau device_class_set_props(dc, spapr_vty_properties);
197db1b58e9SDavid Gibson dc->vmsd = &vmstate_spapr_vty;
1989944d320SPaolo Bonzini }
1999944d320SPaolo Bonzini
2009944d320SPaolo Bonzini static const TypeInfo spapr_vty_info = {
201fd506b4fSDavid Gibson .name = TYPE_VIO_SPAPR_VTY_DEVICE,
2029944d320SPaolo Bonzini .parent = TYPE_VIO_SPAPR_DEVICE,
203ce2918cbSDavid Gibson .instance_size = sizeof(SpaprVioVty),
2049944d320SPaolo Bonzini .class_init = spapr_vty_class_init,
2059944d320SPaolo Bonzini };
2069944d320SPaolo Bonzini
spapr_vty_get_default(SpaprVioBus * bus)207ce2918cbSDavid Gibson SpaprVioDevice *spapr_vty_get_default(SpaprVioBus *bus)
2089944d320SPaolo Bonzini {
209ce2918cbSDavid Gibson SpaprVioDevice *sdev, *selected;
2109944d320SPaolo Bonzini BusChild *kid;
2119944d320SPaolo Bonzini
2129944d320SPaolo Bonzini /*
2139944d320SPaolo Bonzini * To avoid the console bouncing around we want one VTY to be
2149944d320SPaolo Bonzini * the "default". We haven't really got anything to go on, so
2159944d320SPaolo Bonzini * arbitrarily choose the one with the lowest reg value.
2169944d320SPaolo Bonzini */
2179944d320SPaolo Bonzini
2189944d320SPaolo Bonzini selected = NULL;
2199944d320SPaolo Bonzini QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
2209944d320SPaolo Bonzini DeviceState *iter = kid->child;
2219944d320SPaolo Bonzini
2229944d320SPaolo Bonzini /* Only look at VTY devices */
223e275934dSDavid Gibson if (!object_dynamic_cast(OBJECT(iter), TYPE_VIO_SPAPR_VTY_DEVICE)) {
2249944d320SPaolo Bonzini continue;
2259944d320SPaolo Bonzini }
2269944d320SPaolo Bonzini
227fd506b4fSDavid Gibson sdev = VIO_SPAPR_DEVICE(iter);
2289944d320SPaolo Bonzini
2299944d320SPaolo Bonzini /* First VTY we've found, so it is selected for now */
2309944d320SPaolo Bonzini if (!selected) {
2319944d320SPaolo Bonzini selected = sdev;
2329944d320SPaolo Bonzini continue;
2339944d320SPaolo Bonzini }
2349944d320SPaolo Bonzini
2359944d320SPaolo Bonzini /* Choose VTY with lowest reg value */
2369944d320SPaolo Bonzini if (sdev->reg < selected->reg) {
2379944d320SPaolo Bonzini selected = sdev;
2389944d320SPaolo Bonzini }
2399944d320SPaolo Bonzini }
2409944d320SPaolo Bonzini
2419944d320SPaolo Bonzini return selected;
2429944d320SPaolo Bonzini }
2439944d320SPaolo Bonzini
vty_lookup(SpaprMachineState * spapr,target_ulong reg)244ce2918cbSDavid Gibson SpaprVioDevice *vty_lookup(SpaprMachineState *spapr, target_ulong reg)
2459944d320SPaolo Bonzini {
246ce2918cbSDavid Gibson SpaprVioDevice *sdev;
2479944d320SPaolo Bonzini
2489944d320SPaolo Bonzini sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
2499944d320SPaolo Bonzini if (!sdev && reg == 0) {
2509944d320SPaolo Bonzini /* Hack for kernel early debug, which always specifies reg==0.
2519944d320SPaolo Bonzini * We search all VIO devices, and grab the vty with the lowest
2529944d320SPaolo Bonzini * reg. This attempts to mimic existing PowerVM behaviour
2539944d320SPaolo Bonzini * (early debug does work there, despite having no vty with
2549944d320SPaolo Bonzini * reg==0. */
2559944d320SPaolo Bonzini return spapr_vty_get_default(spapr->vio_bus);
2569944d320SPaolo Bonzini }
2579944d320SPaolo Bonzini
2580f888bfaSDavid Gibson if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) {
2590f888bfaSDavid Gibson return NULL;
2600f888bfaSDavid Gibson }
2610f888bfaSDavid Gibson
2629944d320SPaolo Bonzini return sdev;
2639944d320SPaolo Bonzini }
2649944d320SPaolo Bonzini
spapr_vty_register_types(void)2659944d320SPaolo Bonzini static void spapr_vty_register_types(void)
2669944d320SPaolo Bonzini {
2679944d320SPaolo Bonzini spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
2689944d320SPaolo Bonzini spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
2699944d320SPaolo Bonzini type_register_static(&spapr_vty_info);
2709944d320SPaolo Bonzini }
2719944d320SPaolo Bonzini
2729944d320SPaolo Bonzini type_init(spapr_vty_register_types)
273