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