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