1 /* $OpenBSD: vcons.c,v 1.18 2020/07/22 14:02:31 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2008 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/conf.h> 20 #include <sys/device.h> 21 #include <sys/proc.h> 22 #include <sys/systm.h> 23 #include <sys/tty.h> 24 25 #ifdef DDB 26 #include <ddb/db_var.h> 27 #endif 28 29 #include <machine/autoconf.h> 30 #include <machine/conf.h> 31 #include <machine/hypervisor.h> 32 #include <machine/openfirm.h> 33 34 #include <dev/cons.h> 35 #include <sparc64/dev/vbusvar.h> 36 37 struct vcons_softc { 38 struct device sc_dv; 39 bus_space_tag_t sc_bustag; 40 41 void *sc_ih; 42 uint64_t sc_sysino; 43 struct tty *sc_tty; 44 void *sc_si; 45 }; 46 47 int vcons_match(struct device *, void *, void *); 48 void vcons_attach(struct device *, struct device *, void *); 49 50 struct cfattach vcons_ca = { 51 sizeof(struct vcons_softc), vcons_match, vcons_attach 52 }; 53 54 struct cfdriver vcons_cd = { 55 NULL, "vcons", DV_DULL 56 }; 57 58 int vcons_intr(void *); 59 60 int vcons_cnlookc(dev_t, int *); 61 int vcons_cngetc(dev_t); 62 void vcons_cnputc(dev_t, int); 63 64 void vconsstart(struct tty *); 65 int vconsparam(struct tty *, struct termios *); 66 void vcons_softintr(void *); 67 68 int 69 vcons_match(struct device *parent, void *match, void *aux) 70 { 71 struct vbus_attach_args *va = aux; 72 73 if (strcmp(va->va_name, "console") == 0) 74 return (1); 75 76 return (0); 77 } 78 79 void 80 vcons_attach(struct device *parent, struct device *self, void *aux) 81 { 82 struct vcons_softc *sc = (struct vcons_softc *)self; 83 struct vbus_attach_args *va = aux; 84 int vcons_is_input, vcons_is_output; 85 int node, maj; 86 87 sc->sc_si = softintr_establish(IPL_TTY, vcons_softintr, sc); 88 if (sc->sc_si == NULL) 89 panic(": can't establish soft interrupt"); 90 91 if (vbus_intr_map(va->va_node, va->va_intr[0], &sc->sc_sysino)) 92 printf(": can't map interrupt\n"); 93 printf(": ivec 0x%llx", sc->sc_sysino); 94 95 sc->sc_bustag = va->va_bustag; 96 sc->sc_ih = bus_intr_establish(sc->sc_bustag, sc->sc_sysino, IPL_TTY, 97 BUS_INTR_ESTABLISH_MPSAFE, vcons_intr, sc, sc->sc_dv.dv_xname); 98 if (sc->sc_ih == NULL) { 99 printf(", can't establish interrupt\n"); 100 return; 101 } 102 103 node = OF_instance_to_package(OF_stdin()); 104 vcons_is_input = (va->va_node == node); 105 node = OF_instance_to_package(OF_stdout()); 106 vcons_is_output = (va->va_node == node); 107 108 if (vcons_is_input || vcons_is_output) { 109 if (vcons_is_input) { 110 cn_tab->cn_pollc = nullcnpollc; 111 cn_tab->cn_getc = vcons_cngetc; 112 113 /* Locate the major number. */ 114 for (maj = 0; maj < nchrdev; maj++) 115 if (cdevsw[maj].d_open == vconsopen) 116 break; 117 cn_tab->cn_dev = makedev(maj, self->dv_unit); 118 } 119 if (vcons_is_output) 120 cn_tab->cn_putc = vcons_cnputc; 121 122 printf(": console"); 123 } 124 125 printf("\n"); 126 } 127 128 int 129 vcons_intr(void *arg) 130 { 131 struct vcons_softc *sc = arg; 132 133 if (sc->sc_tty) 134 softintr_schedule(sc->sc_si); 135 136 return (1); 137 } 138 139 int 140 vcons_cnlookc(dev_t dev, int *cp) 141 { 142 int64_t ch; 143 144 if (hv_cons_getchar(&ch) == H_EOK) { 145 #ifdef DDB 146 if (ch == -1 && db_console) 147 db_enter(); 148 #endif 149 *cp = ch; 150 return (1); 151 } 152 153 return (0); 154 } 155 156 int 157 vcons_cngetc(dev_t dev) 158 { 159 int c; 160 161 while(!vcons_cnlookc(dev, &c)) 162 ; 163 164 return (c); 165 } 166 167 void 168 vcons_cnputc(dev_t dev, int c) 169 { 170 while (hv_cons_putchar(c) == H_EWOULDBLOCK); 171 } 172 173 int 174 vconsopen(dev_t dev, int flag, int mode, struct proc *p) 175 { 176 struct vcons_softc *sc; 177 struct tty *tp; 178 int unit = minor(dev); 179 int err; 180 181 if (unit >= vcons_cd.cd_ndevs) 182 return (ENXIO); 183 sc = vcons_cd.cd_devs[unit]; 184 if (sc == NULL) 185 return (ENXIO); 186 187 if (sc->sc_tty) 188 tp = sc->sc_tty; 189 else 190 tp = sc->sc_tty = ttymalloc(0); 191 192 tp->t_oproc = vconsstart; 193 tp->t_param = vconsparam; 194 tp->t_dev = dev; 195 if ((tp->t_state & TS_ISOPEN) == 0) { 196 ttychars(tp); 197 tp->t_iflag = TTYDEF_IFLAG; 198 tp->t_oflag = TTYDEF_OFLAG; 199 tp->t_cflag = TTYDEF_CFLAG; 200 tp->t_lflag = TTYDEF_LFLAG; 201 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 202 ttsetwater(tp); 203 } else if ((tp->t_state & TS_XCLUDE) && suser(p)) 204 return (EBUSY); 205 tp->t_state |= TS_CARR_ON; 206 207 err = (*linesw[tp->t_line].l_open)(dev, tp, p); 208 if (err == 0) 209 vbus_intr_setenabled(sc->sc_bustag, sc->sc_sysino, INTR_ENABLED); 210 211 return err; 212 } 213 214 int 215 vconsclose(dev_t dev, int flag, int mode, struct proc *p) 216 { 217 struct vcons_softc *sc; 218 struct tty *tp; 219 int unit = minor(dev); 220 221 if (unit >= vcons_cd.cd_ndevs) 222 return (ENXIO); 223 sc = vcons_cd.cd_devs[unit]; 224 if (sc == NULL) 225 return (ENXIO); 226 227 vbus_intr_setenabled(sc->sc_bustag, sc->sc_sysino, INTR_DISABLED); 228 229 tp = sc->sc_tty; 230 (*linesw[tp->t_line].l_close)(tp, flag, p); 231 ttyclose(tp); 232 return (0); 233 } 234 235 int 236 vconsread(dev_t dev, struct uio *uio, int flag) 237 { 238 struct vcons_softc *sc; 239 struct tty *tp; 240 int unit = minor(dev); 241 242 if (unit >= vcons_cd.cd_ndevs) 243 return (ENXIO); 244 sc = vcons_cd.cd_devs[unit]; 245 if (sc == NULL) 246 return (ENXIO); 247 248 tp = sc->sc_tty; 249 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 250 } 251 252 int 253 vconswrite(dev_t dev, struct uio *uio, int flag) 254 { 255 struct vcons_softc *sc; 256 struct tty *tp; 257 int unit = minor(dev); 258 259 if (unit >= vcons_cd.cd_ndevs) 260 return (ENXIO); 261 sc = vcons_cd.cd_devs[unit]; 262 if (sc == NULL) 263 return (ENXIO); 264 265 tp = sc->sc_tty; 266 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 267 } 268 269 int 270 vconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 271 { 272 struct vcons_softc *sc; 273 struct tty *tp; 274 int unit = minor(dev); 275 int error; 276 277 if (unit >= vcons_cd.cd_ndevs) 278 return (ENXIO); 279 sc = vcons_cd.cd_devs[unit]; 280 if (sc == NULL) 281 return (ENXIO); 282 283 tp = sc->sc_tty; 284 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 285 if (error >= 0) 286 return error; 287 error = ttioctl(tp, cmd, data, flag, p); 288 if (error >= 0) 289 return (error); 290 291 return (ENOTTY); 292 } 293 294 void 295 vconsstart(struct tty *tp) 296 { 297 int s; 298 299 s = spltty(); 300 if (tp->t_state & (TS_TTSTOP | TS_BUSY)) { 301 splx(s); 302 return; 303 } 304 ttwakeupwr(tp); 305 tp->t_state |= TS_BUSY; 306 while (tp->t_outq.c_cc != 0) 307 vcons_cnputc(tp->t_dev, getc(&tp->t_outq)); 308 tp->t_state &= ~TS_BUSY; 309 splx(s); 310 } 311 312 int 313 vconsstop(struct tty *tp, int flag) 314 { 315 int s; 316 317 s = spltty(); 318 if (tp->t_state & TS_BUSY) 319 if ((tp->t_state & TS_TTSTOP) == 0) 320 tp->t_state |= TS_FLUSH; 321 splx(s); 322 return (0); 323 } 324 325 struct tty * 326 vconstty(dev_t dev) 327 { 328 struct vcons_softc *sc; 329 int unit = minor(dev); 330 331 if (unit >= vcons_cd.cd_ndevs) 332 return (NULL); 333 sc = vcons_cd.cd_devs[unit]; 334 if (sc == NULL) 335 return (NULL); 336 337 return sc->sc_tty; 338 } 339 340 int 341 vconsparam(struct tty *tp, struct termios *t) 342 { 343 tp->t_ispeed = t->c_ispeed; 344 tp->t_ospeed = t->c_ospeed; 345 tp->t_cflag = t->c_cflag; 346 return (0); 347 } 348 349 void 350 vcons_softintr(void *arg) 351 { 352 struct vcons_softc *sc = arg; 353 struct tty *tp = sc->sc_tty; 354 int c; 355 356 while (vcons_cnlookc(tp->t_dev, &c)) { 357 if (tp->t_state & TS_ISOPEN) 358 (*linesw[tp->t_line].l_rint)(c, tp); 359 } 360 361 vbus_intr_setstate(sc->sc_bustag, sc->sc_sysino, INTR_IDLE); 362 } 363