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