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