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 12 #define VTERM_BUFSIZE 16 13 14 typedef struct SpaprVioVty { 15 SpaprVioDevice sdev; 16 CharBackend chardev; 17 uint32_t in, out; 18 uint8_t buf[VTERM_BUFSIZE]; 19 } SpaprVioVty; 20 21 #define TYPE_VIO_SPAPR_VTY_DEVICE "spapr-vty" 22 #define VIO_SPAPR_VTY_DEVICE(obj) \ 23 OBJECT_CHECK(SpaprVioVty, (obj), TYPE_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_create(&bus->bus, "spapr-vty"); 162 qdev_prop_set_chr(dev, "chardev", chardev); 163 qdev_init_nofail(dev); 164 } 165 166 static Property spapr_vty_properties[] = { 167 DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev), 168 DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev), 169 DEFINE_PROP_END_OF_LIST(), 170 }; 171 172 static const VMStateDescription vmstate_spapr_vty = { 173 .name = "spapr_vty", 174 .version_id = 1, 175 .minimum_version_id = 1, 176 .fields = (VMStateField[]) { 177 VMSTATE_SPAPR_VIO(sdev, SpaprVioVty), 178 179 VMSTATE_UINT32(in, SpaprVioVty), 180 VMSTATE_UINT32(out, SpaprVioVty), 181 VMSTATE_BUFFER(buf, SpaprVioVty), 182 VMSTATE_END_OF_LIST() 183 }, 184 }; 185 186 static void spapr_vty_class_init(ObjectClass *klass, void *data) 187 { 188 DeviceClass *dc = DEVICE_CLASS(klass); 189 SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); 190 191 k->realize = spapr_vty_realize; 192 k->dt_name = "vty"; 193 k->dt_type = "serial"; 194 k->dt_compatible = "hvterm1"; 195 set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 196 dc->props = spapr_vty_properties; 197 dc->vmsd = &vmstate_spapr_vty; 198 } 199 200 static const TypeInfo spapr_vty_info = { 201 .name = TYPE_VIO_SPAPR_VTY_DEVICE, 202 .parent = TYPE_VIO_SPAPR_DEVICE, 203 .instance_size = sizeof(SpaprVioVty), 204 .class_init = spapr_vty_class_init, 205 }; 206 207 SpaprVioDevice *spapr_vty_get_default(SpaprVioBus *bus) 208 { 209 SpaprVioDevice *sdev, *selected; 210 BusChild *kid; 211 212 /* 213 * To avoid the console bouncing around we want one VTY to be 214 * the "default". We haven't really got anything to go on, so 215 * arbitrarily choose the one with the lowest reg value. 216 */ 217 218 selected = NULL; 219 QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { 220 DeviceState *iter = kid->child; 221 222 /* Only look at VTY devices */ 223 if (!object_dynamic_cast(OBJECT(iter), TYPE_VIO_SPAPR_VTY_DEVICE)) { 224 continue; 225 } 226 227 sdev = VIO_SPAPR_DEVICE(iter); 228 229 /* First VTY we've found, so it is selected for now */ 230 if (!selected) { 231 selected = sdev; 232 continue; 233 } 234 235 /* Choose VTY with lowest reg value */ 236 if (sdev->reg < selected->reg) { 237 selected = sdev; 238 } 239 } 240 241 return selected; 242 } 243 244 SpaprVioDevice *vty_lookup(SpaprMachineState *spapr, target_ulong reg) 245 { 246 SpaprVioDevice *sdev; 247 248 sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); 249 if (!sdev && reg == 0) { 250 /* Hack for kernel early debug, which always specifies reg==0. 251 * We search all VIO devices, and grab the vty with the lowest 252 * reg. This attempts to mimic existing PowerVM behaviour 253 * (early debug does work there, despite having no vty with 254 * reg==0. */ 255 return spapr_vty_get_default(spapr->vio_bus); 256 } 257 258 if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) { 259 return NULL; 260 } 261 262 return sdev; 263 } 264 265 static void spapr_vty_register_types(void) 266 { 267 spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); 268 spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); 269 type_register_static(&spapr_vty_info); 270 } 271 272 type_init(spapr_vty_register_types) 273