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