1 /* $NetBSD: wsdisplay_compat_usl.c,v 1.53 2020/05/23 23:42:42 ad 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.53 2020/05/23 23:42:42 ad 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 84 static const struct wscons_syncops usl_syncops = { 85 usl_detachproc, 86 usl_attachproc, 87 usl_sync_check, 88 #define _usl_sync_destroy ((void (*)(void *))usl_sync_done) 89 _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 int 298 wsdisplay_usl_ioctl1(device_t dv, u_long cmd, void *data, 299 int flag, struct lwp *l) 300 { 301 struct wsdisplay_softc *sc = device_private(dv); 302 int idx, maxidx; 303 304 switch (cmd) { 305 case VT_OPENQRY: 306 maxidx = wsdisplay_maxscreenidx(sc); 307 for (idx = 0; idx <= maxidx; idx++) { 308 if (wsdisplay_screenstate(sc, idx) == 0) { 309 *(int *)data = idx + 1; 310 return 0; 311 } 312 } 313 return ENXIO; 314 case VT_GETACTIVE: 315 idx = wsdisplay_getactivescreen(sc); 316 *(int *)data = idx + 1; 317 return 0; 318 case VT_ACTIVATE: 319 /* 320 * a gross and disgusting hack to make this abused up ioctl, 321 * which is a gross and disgusting hack on its own, work on 322 * LP64/BE - we want the lower 32bit so we simply dereference 323 * the argument pointer as long. May cause problems with 32bit 324 * kernels on sparc64? 325 */ 326 327 idx = *(long *)data - 1; 328 if (idx < 0) 329 return EINVAL; 330 return wsdisplay_switch(dv, idx, 1); 331 case VT_WAITACTIVE: 332 idx = *(long *)data - 1; 333 if (idx < 0) 334 return EINVAL; 335 return wsscreen_switchwait(sc, idx); 336 case VT_GETSTATE: 337 #define ss ((struct vt_stat *)data) 338 idx = wsdisplay_getactivescreen(sc); 339 ss->v_active = idx + 1; 340 ss->v_state = 0; 341 maxidx = wsdisplay_maxscreenidx(sc); 342 for (idx = 0; idx <= maxidx; idx++) 343 if (wsdisplay_screenstate(sc, idx) == EBUSY) 344 ss->v_state |= (1 << (idx + 1)); 345 #undef ss 346 return 0; 347 348 #ifdef WSDISPLAY_COMPAT_PCVT 349 case VGAPCVTID: 350 #define id ((struct pcvtid *)data) 351 strlcpy(id->name, "pcvt", sizeof(id->name)); 352 id->rmajor = 3; 353 id->rminor = 32; 354 #undef id 355 return 0; 356 #endif 357 #ifdef WSDISPLAY_COMPAT_SYSCONS 358 case CONS_GETVERS: 359 *(int *)data = 0x200; /* version 2.0 */ 360 return 0; 361 #endif 362 363 default: 364 return EPASSTHROUGH; 365 } 366 } 367 368 int 369 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 370 u_long cmd, void *data, int flag, struct lwp *l) 371 { 372 struct proc *p = l->l_proc; 373 int intarg = 0, res; 374 u_long req; 375 void *arg; 376 struct usl_syncdata *sd; 377 struct wskbd_bell_data bd; 378 379 switch (cmd) { 380 case VT_SETMODE: 381 #define newmode ((struct vt_mode *)data) 382 if (newmode->mode == VT_PROCESS) { 383 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 384 newmode->relsig, newmode->frsig); 385 if (res) 386 return res; 387 } else { 388 sd = usl_sync_get(scr); 389 if (sd) 390 usl_sync_done(sd); 391 } 392 #undef newmode 393 return 0; 394 case VT_GETMODE: 395 #define cmode ((struct vt_mode *)data) 396 sd = usl_sync_get(scr); 397 if (sd) { 398 cmode->mode = VT_PROCESS; 399 cmode->relsig = sd->s_relsig; 400 cmode->acqsig = sd->s_acqsig; 401 cmode->frsig = sd->s_frsig; 402 } else 403 cmode->mode = VT_AUTO; 404 #undef cmode 405 return 0; 406 case VT_RELDISP: 407 #define d (*(long *)data) 408 sd = usl_sync_get(scr); 409 if (!sd) 410 return EINVAL; 411 switch (d) { 412 case VT_FALSE: 413 case VT_TRUE: 414 return usl_detachack(sd, (d == VT_TRUE)); 415 case VT_ACKACQ: 416 return usl_attachack(sd, 1); 417 default: 418 return EINVAL; 419 } 420 #undef d 421 422 case KDENABIO: 423 #if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 424 if (kauth_authorize_machdep(l->l_cred, KAUTH_MACHDEP_IOPL, 425 NULL, NULL, NULL, NULL) != 0) 426 return EPERM; 427 #endif 428 /* FALLTHRU */ 429 case KDDISABIO: 430 #if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 431 { 432 /* XXX NJWLWP */ 433 struct trapframe *fp = (struct trapframe *)curlwp->l_md.md_regs; 434 if (cmd == KDENABIO) 435 fp->tf_eflags |= PSL_IOPL; 436 else 437 fp->tf_eflags &= ~PSL_IOPL; 438 } 439 #endif 440 return 0; 441 case KDSETRAD: 442 /* XXX ignore for now */ 443 return 0; 444 445 default: 446 return EPASSTHROUGH; 447 448 /* 449 * the following are converted to wsdisplay ioctls 450 */ 451 case KDSETMODE: 452 req = WSDISPLAYIO_SMODE; 453 #define d (*(long *)data) 454 switch (d) { 455 case KD_GRAPHICS: 456 intarg = WSDISPLAYIO_MODE_MAPPED; 457 break; 458 case KD_TEXT: 459 intarg = WSDISPLAYIO_MODE_EMUL; 460 break; 461 default: 462 return EINVAL; 463 } 464 #undef d 465 arg = &intarg; 466 break; 467 case KDMKTONE: 468 req = WSKBDIO_COMPLEXBELL; 469 #define d (*(long *)data) 470 if (d) { 471 #define PCVT_SYSBEEPF 1193182 472 if (d >> 16) { 473 bd.which = WSKBD_BELL_DOPERIOD; 474 bd.period = d >> 16; /* ms */ 475 } 476 else 477 bd.which = 0; 478 if (d & 0xffff) { 479 bd.which |= WSKBD_BELL_DOPITCH; 480 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 481 } 482 } else 483 bd.which = 0; /* default */ 484 #undef d 485 arg = &bd; 486 break; 487 case KDSETLED: 488 req = WSKBDIO_SETLEDS; 489 intarg = 0; 490 #define d (*(long *)data) 491 if (d & LED_CAP) 492 intarg |= WSKBD_LED_CAPS; 493 if (d & LED_NUM) 494 intarg |= WSKBD_LED_NUM; 495 if (d & LED_SCR) 496 intarg |= WSKBD_LED_SCROLL; 497 #undef d 498 arg = &intarg; 499 break; 500 case KDGETLED: 501 req = WSKBDIO_GETLEDS; 502 arg = &intarg; 503 break; 504 #ifdef WSDISPLAY_COMPAT_RAWKBD 505 case KDSKBMODE: 506 req = WSKBDIO_SETMODE; 507 switch (*(long *)data) { 508 case K_RAW: 509 intarg = WSKBD_RAW; 510 break; 511 case K_XLATE: 512 intarg = WSKBD_TRANSLATED; 513 break; 514 default: 515 return EINVAL; 516 } 517 arg = &intarg; 518 break; 519 case KDGKBMODE: 520 req = WSKBDIO_GETMODE; 521 arg = &intarg; 522 break; 523 #endif 524 } 525 526 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, l); 527 if (res != EPASSTHROUGH) 528 return res; 529 530 switch (cmd) { 531 case KDGETLED: 532 #define d (*(int *)data) 533 d = 0; 534 if (intarg & WSKBD_LED_CAPS) 535 d |= LED_CAP; 536 if (intarg & WSKBD_LED_NUM) 537 d |= LED_NUM; 538 if (intarg & WSKBD_LED_SCROLL) 539 d |= LED_SCR; 540 #undef d 541 break; 542 #ifdef WSDISPLAY_COMPAT_RAWKBD 543 case KDGKBMODE: 544 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 545 break; 546 #endif 547 } 548 549 return 0; 550 } 551