xref: /openbmc/qemu/hw/char/spapr_vty.c (revision 8fa3b702)
1 #include "qemu/osdep.h"
2 #include "qemu/error-report.h"
3 #include "qemu/module.h"
4 #include "qapi/error.h"
5 #include "cpu.h"
6 #include "migration/vmstate.h"
7 #include "chardev/char-fe.h"
8 #include "hw/ppc/spapr.h"
9 #include "hw/ppc/spapr_vio.h"
10 #include "hw/qdev-properties.h"
11 #include "qom/object.h"
12 
13 #define VTERM_BUFSIZE   16
14 
15 struct SpaprVioVty {
16     SpaprVioDevice sdev;
17     CharBackend chardev;
18     uint32_t in, out;
19     uint8_t buf[VTERM_BUFSIZE];
20 };
21 typedef struct SpaprVioVty SpaprVioVty;
22 
23 #define TYPE_VIO_SPAPR_VTY_DEVICE "spapr-vty"
24 DECLARE_INSTANCE_CHECKER(SpaprVioVty, VIO_SPAPR_VTY_DEVICE,
25                          TYPE_VIO_SPAPR_VTY_DEVICE)
26 
27 static int vty_can_receive(void *opaque)
28 {
29     SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque);
30 
31     return VTERM_BUFSIZE - (dev->in - dev->out);
32 }
33 
34 static void vty_receive(void *opaque, const uint8_t *buf, int size)
35 {
36     SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque);
37     int i;
38 
39     if ((dev->in == dev->out) && size) {
40         /* toggle line to simulate edge interrupt */
41         spapr_vio_irq_pulse(&dev->sdev);
42     }
43     for (i = 0; i < size; i++) {
44         if (dev->in - dev->out >= VTERM_BUFSIZE) {
45             static bool reported;
46             if (!reported) {
47                 error_report("VTY input buffer exhausted - characters dropped."
48                              " (input size = %i)", size);
49                 reported = true;
50             }
51             break;
52         }
53         dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
54     }
55 }
56 
57 static int vty_getchars(SpaprVioDevice *sdev, uint8_t *buf, int max)
58 {
59     SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
60     int n = 0;
61 
62     while ((n < max) && (dev->out != dev->in)) {
63         /*
64          * Long ago, PowerVM's vty implementation had a bug where it
65          * inserted a \0 after every \r going to the guest.  Existing
66          * guests have a workaround for this which removes every \0
67          * immediately following a \r.  To avoid triggering this
68          * workaround, we stop before inserting a \0 if the preceding
69          * character in the output buffer is a \r.
70          */
71         if (n > 0 && (buf[n - 1] == '\r') &&
72                 (dev->buf[dev->out % VTERM_BUFSIZE] == '\0')) {
73             break;
74         }
75         buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
76     }
77 
78     qemu_chr_fe_accept_input(&dev->chardev);
79 
80     return n;
81 }
82 
83 void vty_putchars(SpaprVioDevice *sdev, uint8_t *buf, int len)
84 {
85     SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
86 
87     /* XXX this blocks entire thread. Rewrite to use
88      * qemu_chr_fe_write and background I/O callbacks */
89     qemu_chr_fe_write_all(&dev->chardev, buf, len);
90 }
91 
92 static void spapr_vty_realize(SpaprVioDevice *sdev, Error **errp)
93 {
94     SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev);
95 
96     if (!qemu_chr_fe_backend_connected(&dev->chardev)) {
97         error_setg(errp, "chardev property not set");
98         return;
99     }
100 
101     qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive,
102                              vty_receive, NULL, NULL, dev, NULL, true);
103 }
104 
105 /* Forward declaration */
106 static target_ulong h_put_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr,
107                                     target_ulong opcode, target_ulong *args)
108 {
109     target_ulong reg = args[0];
110     target_ulong len = args[1];
111     target_ulong char0_7 = args[2];
112     target_ulong char8_15 = args[3];
113     SpaprVioDevice *sdev;
114     uint8_t buf[16];
115 
116     sdev = vty_lookup(spapr, reg);
117     if (!sdev) {
118         return H_PARAMETER;
119     }
120 
121     if (len > 16) {
122         return H_PARAMETER;
123     }
124 
125     *((uint64_t *)buf) = cpu_to_be64(char0_7);
126     *((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
127 
128     vty_putchars(sdev, buf, len);
129 
130     return H_SUCCESS;
131 }
132 
133 static target_ulong h_get_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr,
134                                     target_ulong opcode, target_ulong *args)
135 {
136     target_ulong reg = args[0];
137     target_ulong *len = args + 0;
138     target_ulong *char0_7 = args + 1;
139     target_ulong *char8_15 = args + 2;
140     SpaprVioDevice *sdev;
141     uint8_t buf[16];
142 
143     sdev = vty_lookup(spapr, reg);
144     if (!sdev) {
145         return H_PARAMETER;
146     }
147 
148     *len = vty_getchars(sdev, buf, sizeof(buf));
149     if (*len < 16) {
150         memset(buf + *len, 0, 16 - *len);
151     }
152 
153     *char0_7 = be64_to_cpu(*((uint64_t *)buf));
154     *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
155 
156     return H_SUCCESS;
157 }
158 
159 void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev)
160 {
161     DeviceState *dev;
162 
163     dev = qdev_new("spapr-vty");
164     qdev_prop_set_chr(dev, "chardev", chardev);
165     qdev_realize_and_unref(dev, &bus->bus, &error_fatal);
166 }
167 
168 static Property spapr_vty_properties[] = {
169     DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev),
170     DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev),
171     DEFINE_PROP_END_OF_LIST(),
172 };
173 
174 static const VMStateDescription vmstate_spapr_vty = {
175     .name = "spapr_vty",
176     .version_id = 1,
177     .minimum_version_id = 1,
178     .fields = (VMStateField[]) {
179         VMSTATE_SPAPR_VIO(sdev, SpaprVioVty),
180 
181         VMSTATE_UINT32(in, SpaprVioVty),
182         VMSTATE_UINT32(out, SpaprVioVty),
183         VMSTATE_BUFFER(buf, SpaprVioVty),
184         VMSTATE_END_OF_LIST()
185     },
186 };
187 
188 static void spapr_vty_class_init(ObjectClass *klass, void *data)
189 {
190     DeviceClass *dc = DEVICE_CLASS(klass);
191     SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
192 
193     k->realize = spapr_vty_realize;
194     k->dt_name = "vty";
195     k->dt_type = "serial";
196     k->dt_compatible = "hvterm1";
197     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
198     device_class_set_props(dc, spapr_vty_properties);
199     dc->vmsd = &vmstate_spapr_vty;
200 }
201 
202 static const TypeInfo spapr_vty_info = {
203     .name          = TYPE_VIO_SPAPR_VTY_DEVICE,
204     .parent        = TYPE_VIO_SPAPR_DEVICE,
205     .instance_size = sizeof(SpaprVioVty),
206     .class_init    = spapr_vty_class_init,
207 };
208 
209 SpaprVioDevice *spapr_vty_get_default(SpaprVioBus *bus)
210 {
211     SpaprVioDevice *sdev, *selected;
212     BusChild *kid;
213 
214     /*
215      * To avoid the console bouncing around we want one VTY to be
216      * the "default". We haven't really got anything to go on, so
217      * arbitrarily choose the one with the lowest reg value.
218      */
219 
220     selected = NULL;
221     QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
222         DeviceState *iter = kid->child;
223 
224         /* Only look at VTY devices */
225         if (!object_dynamic_cast(OBJECT(iter), TYPE_VIO_SPAPR_VTY_DEVICE)) {
226             continue;
227         }
228 
229         sdev = VIO_SPAPR_DEVICE(iter);
230 
231         /* First VTY we've found, so it is selected for now */
232         if (!selected) {
233             selected = sdev;
234             continue;
235         }
236 
237         /* Choose VTY with lowest reg value */
238         if (sdev->reg < selected->reg) {
239             selected = sdev;
240         }
241     }
242 
243     return selected;
244 }
245 
246 SpaprVioDevice *vty_lookup(SpaprMachineState *spapr, target_ulong reg)
247 {
248     SpaprVioDevice *sdev;
249 
250     sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
251     if (!sdev && reg == 0) {
252         /* Hack for kernel early debug, which always specifies reg==0.
253          * We search all VIO devices, and grab the vty with the lowest
254          * reg.  This attempts to mimic existing PowerVM behaviour
255          * (early debug does work there, despite having no vty with
256          * reg==0. */
257         return spapr_vty_get_default(spapr->vio_bus);
258     }
259 
260     if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) {
261         return NULL;
262     }
263 
264     return sdev;
265 }
266 
267 static void spapr_vty_register_types(void)
268 {
269     spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
270     spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
271     type_register_static(&spapr_vty_info);
272 }
273 
274 type_init(spapr_vty_register_types)
275