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