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