1 /* $NetBSD: wsdisplay_compat_usl.c,v 1.54 2021/06/01 23:28:07 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 1998 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_compat_usl.c,v 1.54 2021/06/01 23:28:07 riastradh Exp $"); 31 32 #ifdef _KERNEL_OPT 33 #include "opt_compat_freebsd.h" 34 #include "opt_compat_netbsd.h" 35 #endif 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/callout.h> 40 #include <sys/ioctl.h> 41 #include <sys/kernel.h> 42 #include <sys/kmem.h> 43 #include <sys/proc.h> 44 #include <sys/signalvar.h> 45 #include <sys/errno.h> 46 #include <sys/kauth.h> 47 48 #include <dev/wscons/wsconsio.h> 49 #include <dev/wscons/wsdisplayvar.h> 50 #include <dev/wscons/wscons_callbacks.h> 51 #include <dev/wscons/wsdisplay_usl_io.h> 52 53 #include "opt_wsdisplay_compat.h" 54 55 struct usl_syncdata { 56 struct wsscreen *s_scr; 57 struct proc *s_proc; 58 pid_t s_pid; 59 int s_flags; 60 #define SF_DETACHPENDING 1 61 #define SF_ATTACHPENDING 2 62 int s_acqsig, s_relsig; 63 int s_frsig; /* unused */ 64 void (*s_callback)(void *, int, int); 65 void *s_cbarg; 66 callout_t s_attach_ch; 67 callout_t s_detach_ch; 68 }; 69 70 static int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 71 struct proc *, int, int, int); 72 static void usl_sync_done(struct usl_syncdata *); 73 static int usl_sync_check(void *); 74 static int usl_sync_check_sig(struct usl_syncdata *, int, int); 75 static struct usl_syncdata *usl_sync_get(struct wsscreen *); 76 77 static int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 78 static int usl_detachack(struct usl_syncdata *, int); 79 static void usl_detachtimeout(void *); 80 static int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 81 static int usl_attachack(struct usl_syncdata *, int); 82 static void usl_attachtimeout(void *); 83 static void usl_sync_destroy(void *); 84 85 static const struct wscons_syncops usl_syncops = { 86 .detach = usl_detachproc, 87 .attach = usl_attachproc, 88 .check = usl_sync_check, 89 .destroy = usl_sync_destroy, 90 }; 91 92 #ifndef WSCOMPAT_USL_SYNCTIMEOUT 93 #define WSCOMPAT_USL_SYNCTIMEOUT 5 /* seconds */ 94 #endif 95 static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT; 96 97 static int 98 usl_sync_init(struct wsscreen *scr, struct usl_syncdata **sdp, 99 struct proc *p, int acqsig, int relsig, int frsig) 100 { 101 struct usl_syncdata *sd; 102 int res; 103 104 sd = kmem_intr_alloc(sizeof(*sd), KM_SLEEP); 105 106 sd->s_scr = scr; 107 sd->s_proc = p; 108 sd->s_pid = p->p_pid; 109 sd->s_flags = 0; 110 sd->s_acqsig = acqsig; 111 sd->s_relsig = relsig; 112 sd->s_frsig = frsig; 113 callout_init(&sd->s_attach_ch, 0); 114 callout_setfunc(&sd->s_attach_ch, usl_attachtimeout, sd); 115 callout_init(&sd->s_detach_ch, 0); 116 callout_setfunc(&sd->s_detach_ch, usl_detachtimeout, sd); 117 res = wsscreen_attach_sync(scr, &usl_syncops, sd); 118 if (res) { 119 kmem_intr_free(sd, sizeof(*sd)); 120 return res; 121 } 122 *sdp = sd; 123 return 0; 124 } 125 126 static void 127 usl_sync_done(struct usl_syncdata *sd) 128 { 129 if (sd->s_flags & SF_DETACHPENDING) { 130 callout_stop(&sd->s_detach_ch); 131 (*sd->s_callback)(sd->s_cbarg, 0, 0); 132 } 133 if (sd->s_flags & SF_ATTACHPENDING) { 134 callout_stop(&sd->s_attach_ch); 135 (*sd->s_callback)(sd->s_cbarg, ENXIO, 0); 136 } 137 wsscreen_detach_sync(sd->s_scr); 138 kmem_intr_free(sd, sizeof(*sd)); 139 } 140 141 static int 142 usl_sync_check_sig(struct usl_syncdata *sd, int sig, int flags) 143 { 144 145 mutex_enter(&proc_lock); 146 if (sd->s_proc == proc_find(sd->s_pid)) { 147 sd->s_flags |= flags; 148 if (sig) 149 psignal(sd->s_proc, sig); 150 mutex_exit(&proc_lock); 151 return 1; 152 } 153 mutex_exit(&proc_lock); 154 155 printf("%s: process %d died\n", __func__, sd->s_pid); 156 usl_sync_done(sd); 157 return 0; 158 } 159 160 static int 161 usl_sync_check(void *vsd) 162 { 163 164 struct usl_syncdata *sd = vsd; 165 return usl_sync_check_sig(sd, 0, 0); 166 } 167 168 static struct usl_syncdata * 169 usl_sync_get(struct wsscreen *scr) 170 { 171 void *sd; 172 173 if (wsscreen_lookup_sync(scr, &usl_syncops, &sd)) 174 return 0; 175 return sd; 176 } 177 178 static int 179 usl_detachproc(void *cookie, int waitok, 180 void (*callback)(void *, int, int), void *cbarg) 181 { 182 struct usl_syncdata *sd = cookie; 183 184 /* we really need a callback */ 185 if (!callback) 186 return EINVAL; 187 188 /* 189 * Normally, this is called from the controlling process. 190 * Is is supposed to reply with a VT_RELDISP ioctl(), so 191 * it is not useful to tsleep() here. 192 */ 193 sd->s_callback = callback; 194 sd->s_cbarg = cbarg; 195 if (waitok) { 196 if (!usl_sync_check_sig(sd, sd->s_relsig, SF_DETACHPENDING)) 197 return 0; 198 } 199 200 callout_schedule(&sd->s_detach_ch, wscompat_usl_synctimeout * hz); 201 return EAGAIN; 202 } 203 204 static int 205 usl_detachack(struct usl_syncdata *sd, int ack) 206 { 207 if (!(sd->s_flags & SF_DETACHPENDING)) { 208 printf("%s: not detaching\n", __func__); 209 return EINVAL; 210 } 211 212 callout_stop(&sd->s_detach_ch); 213 sd->s_flags &= ~SF_DETACHPENDING; 214 215 if (sd->s_callback) 216 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 217 218 return 0; 219 } 220 221 static void 222 usl_detachtimeout(void *arg) 223 { 224 struct usl_syncdata *sd = arg; 225 226 printf("%s\n", __func__); 227 228 if (!(sd->s_flags & SF_DETACHPENDING)) { 229 printf("%s: not detaching\n", __func__); 230 return; 231 } 232 233 sd->s_flags &= ~SF_DETACHPENDING; 234 235 if (sd->s_callback) 236 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 237 238 (void) usl_sync_check(sd); 239 } 240 241 static int 242 usl_attachproc(void *cookie, int waitok, 243 void (*callback)(void *, int, int), void *cbarg) 244 { 245 struct usl_syncdata *sd = cookie; 246 247 /* we really need a callback */ 248 if (!callback) 249 return EINVAL; 250 251 sd->s_callback = callback; 252 sd->s_cbarg = cbarg; 253 if (!usl_sync_check_sig(sd, sd->s_acqsig, SF_ATTACHPENDING)) 254 return 0; 255 256 callout_schedule(&sd->s_attach_ch, wscompat_usl_synctimeout * hz); 257 return EAGAIN; 258 } 259 260 static int 261 usl_attachack(struct usl_syncdata *sd, int ack) 262 { 263 if (!(sd->s_flags & SF_ATTACHPENDING)) { 264 printf("%s: not attaching\n", __func__); 265 return EINVAL; 266 } 267 268 callout_stop(&sd->s_attach_ch); 269 sd->s_flags &= ~SF_ATTACHPENDING; 270 271 if (sd->s_callback) 272 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 273 274 return 0; 275 } 276 277 static void 278 usl_attachtimeout(void *arg) 279 { 280 struct usl_syncdata *sd = arg; 281 282 printf("%s\n", __func__); 283 284 if (!(sd->s_flags & SF_ATTACHPENDING)) { 285 printf("%s: not attaching\n", __func__); 286 return; 287 } 288 289 sd->s_flags &= ~SF_ATTACHPENDING; 290 291 if (sd->s_callback) 292 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 293 294 (void) usl_sync_check(sd); 295 } 296 297 static void 298 usl_sync_destroy(void *cookie) 299 { 300 struct usl_syncdata *sd = cookie; 301 302 usl_sync_done(sd); 303 } 304 305 int 306 wsdisplay_usl_ioctl1(device_t dv, u_long cmd, void *data, 307 int flag, struct lwp *l) 308 { 309 struct wsdisplay_softc *sc = device_private(dv); 310 int idx, maxidx; 311 312 switch (cmd) { 313 case VT_OPENQRY: 314 maxidx = wsdisplay_maxscreenidx(sc); 315 for (idx = 0; idx <= maxidx; idx++) { 316 if (wsdisplay_screenstate(sc, idx) == 0) { 317 *(int *)data = idx + 1; 318 return 0; 319 } 320 } 321 return ENXIO; 322 case VT_GETACTIVE: 323 idx = wsdisplay_getactivescreen(sc); 324 *(int *)data = idx + 1; 325 return 0; 326 case VT_ACTIVATE: 327 /* 328 * a gross and disgusting hack to make this abused up ioctl, 329 * which is a gross and disgusting hack on its own, work on 330 * LP64/BE - we want the lower 32bit so we simply dereference 331 * the argument pointer as long. May cause problems with 32bit 332 * kernels on sparc64? 333 */ 334 335 idx = *(long *)data - 1; 336 if (idx < 0) 337 return EINVAL; 338 return wsdisplay_switch(dv, idx, 1); 339 case VT_WAITACTIVE: 340 idx = *(long *)data - 1; 341 if (idx < 0) 342 return EINVAL; 343 return wsscreen_switchwait(sc, idx); 344 case VT_GETSTATE: 345 #define ss ((struct vt_stat *)data) 346 idx = wsdisplay_getactivescreen(sc); 347 ss->v_active = idx + 1; 348 ss->v_state = 0; 349 maxidx = wsdisplay_maxscreenidx(sc); 350 for (idx = 0; idx <= maxidx; idx++) 351 if (wsdisplay_screenstate(sc, idx) == EBUSY) 352 ss->v_state |= (1 << (idx + 1)); 353 #undef ss 354 return 0; 355 356 #ifdef WSDISPLAY_COMPAT_PCVT 357 case VGAPCVTID: 358 #define id ((struct pcvtid *)data) 359 strlcpy(id->name, "pcvt", sizeof(id->name)); 360 id->rmajor = 3; 361 id->rminor = 32; 362 #undef id 363 return 0; 364 #endif 365 #ifdef WSDISPLAY_COMPAT_SYSCONS 366 case CONS_GETVERS: 367 *(int *)data = 0x200; /* version 2.0 */ 368 return 0; 369 #endif 370 371 default: 372 return EPASSTHROUGH; 373 } 374 } 375 376 int 377 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 378 u_long cmd, void *data, int flag, struct lwp *l) 379 { 380 struct proc *p = l->l_proc; 381 int intarg = 0, res; 382 u_long req; 383 void *arg; 384 struct usl_syncdata *sd; 385 struct wskbd_bell_data bd; 386 387 switch (cmd) { 388 case VT_SETMODE: 389 #define newmode ((struct vt_mode *)data) 390 if (newmode->mode == VT_PROCESS) { 391 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 392 newmode->relsig, newmode->frsig); 393 if (res) 394 return res; 395 } else { 396 sd = usl_sync_get(scr); 397 if (sd) 398 usl_sync_done(sd); 399 } 400 #undef newmode 401 return 0; 402 case VT_GETMODE: 403 #define cmode ((struct vt_mode *)data) 404 sd = usl_sync_get(scr); 405 if (sd) { 406 cmode->mode = VT_PROCESS; 407 cmode->relsig = sd->s_relsig; 408 cmode->acqsig = sd->s_acqsig; 409 cmode->frsig = sd->s_frsig; 410 } else 411 cmode->mode = VT_AUTO; 412 #undef cmode 413 return 0; 414 case VT_RELDISP: 415 #define d (*(long *)data) 416 sd = usl_sync_get(scr); 417 if (!sd) 418 return EINVAL; 419 switch (d) { 420 case VT_FALSE: 421 case VT_TRUE: 422 return usl_detachack(sd, (d == VT_TRUE)); 423 case VT_ACKACQ: 424 return usl_attachack(sd, 1); 425 default: 426 return EINVAL; 427 } 428 #undef d 429 430 case KDENABIO: 431 #if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 432 if (kauth_authorize_machdep(l->l_cred, KAUTH_MACHDEP_IOPL, 433 NULL, NULL, NULL, NULL) != 0) 434 return EPERM; 435 #endif 436 /* FALLTHRU */ 437 case KDDISABIO: 438 #if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 439 { 440 /* XXX NJWLWP */ 441 struct trapframe *fp = (struct trapframe *)curlwp->l_md.md_regs; 442 if (cmd == KDENABIO) 443 fp->tf_eflags |= PSL_IOPL; 444 else 445 fp->tf_eflags &= ~PSL_IOPL; 446 } 447 #endif 448 return 0; 449 case KDSETRAD: 450 /* XXX ignore for now */ 451 return 0; 452 453 default: 454 return EPASSTHROUGH; 455 456 /* 457 * the following are converted to wsdisplay ioctls 458 */ 459 case KDSETMODE: 460 req = WSDISPLAYIO_SMODE; 461 #define d (*(long *)data) 462 switch (d) { 463 case KD_GRAPHICS: 464 intarg = WSDISPLAYIO_MODE_MAPPED; 465 break; 466 case KD_TEXT: 467 intarg = WSDISPLAYIO_MODE_EMUL; 468 break; 469 default: 470 return EINVAL; 471 } 472 #undef d 473 arg = &intarg; 474 break; 475 case KDMKTONE: 476 req = WSKBDIO_COMPLEXBELL; 477 #define d (*(long *)data) 478 if (d) { 479 #define PCVT_SYSBEEPF 1193182 480 if (d >> 16) { 481 bd.which = WSKBD_BELL_DOPERIOD; 482 bd.period = d >> 16; /* ms */ 483 } 484 else 485 bd.which = 0; 486 if (d & 0xffff) { 487 bd.which |= WSKBD_BELL_DOPITCH; 488 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 489 } 490 } else 491 bd.which = 0; /* default */ 492 #undef d 493 arg = &bd; 494 break; 495 case KDSETLED: 496 req = WSKBDIO_SETLEDS; 497 intarg = 0; 498 #define d (*(long *)data) 499 if (d & LED_CAP) 500 intarg |= WSKBD_LED_CAPS; 501 if (d & LED_NUM) 502 intarg |= WSKBD_LED_NUM; 503 if (d & LED_SCR) 504 intarg |= WSKBD_LED_SCROLL; 505 #undef d 506 arg = &intarg; 507 break; 508 case KDGETLED: 509 req = WSKBDIO_GETLEDS; 510 arg = &intarg; 511 break; 512 #ifdef WSDISPLAY_COMPAT_RAWKBD 513 case KDSKBMODE: 514 req = WSKBDIO_SETMODE; 515 switch (*(long *)data) { 516 case K_RAW: 517 intarg = WSKBD_RAW; 518 break; 519 case K_XLATE: 520 intarg = WSKBD_TRANSLATED; 521 break; 522 default: 523 return EINVAL; 524 } 525 arg = &intarg; 526 break; 527 case KDGKBMODE: 528 req = WSKBDIO_GETMODE; 529 arg = &intarg; 530 break; 531 #endif 532 } 533 534 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, l); 535 if (res != EPASSTHROUGH) 536 return res; 537 538 switch (cmd) { 539 case KDGETLED: 540 #define d (*(int *)data) 541 d = 0; 542 if (intarg & WSKBD_LED_CAPS) 543 d |= LED_CAP; 544 if (intarg & WSKBD_LED_NUM) 545 d |= LED_NUM; 546 if (intarg & WSKBD_LED_SCROLL) 547 d |= LED_SCR; 548 #undef d 549 break; 550 #ifdef WSDISPLAY_COMPAT_RAWKBD 551 case KDGKBMODE: 552 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 553 break; 554 #endif 555 } 556 557 return 0; 558 } 559