1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1991-1997 Søren Schmidt 5 * 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 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <signal.h> 35 #include <stdio.h> 36 #include <sys/types.h> 37 #include <sys/file.h> 38 #include <sys/ioctl.h> 39 #include <sys/mman.h> 40 #include <sys/fbio.h> 41 #include <sys/kbio.h> 42 #include <sys/consio.h> 43 #include "vgl.h" 44 45 /* XXX Direct Color 24bits modes unsupported */ 46 47 #define min(x, y) (((x) < (y)) ? (x) : (y)) 48 #define max(x, y) (((x) > (y)) ? (x) : (y)) 49 50 VGLBitmap *VGLDisplay; 51 VGLBitmap VGLVDisplay; 52 video_info_t VGLModeInfo; 53 video_adapter_info_t VGLAdpInfo; 54 byte *VGLBuf; 55 56 static int VGLMode; 57 static int VGLOldMode; 58 static size_t VGLBufSize; 59 static byte *VGLMem = MAP_FAILED; 60 static int VGLSwitchPending; 61 static int VGLAbortPending; 62 static int VGLOnDisplay; 63 static unsigned int VGLCurWindow; 64 static int VGLInitDone = 0; 65 static video_info_t VGLOldModeInfo; 66 static vid_info_t VGLOldVInfo; 67 static int VGLOldVXsize; 68 69 void 70 VGLEnd() 71 { 72 struct vt_mode smode; 73 int size[3]; 74 75 if (!VGLInitDone) 76 return; 77 VGLInitDone = 0; 78 signal(SIGUSR1, SIG_IGN); 79 signal(SIGUSR2, SIG_IGN); 80 VGLSwitchPending = 0; 81 VGLAbortPending = 0; 82 VGLMousePointerHide(); 83 84 if (VGLMem != MAP_FAILED) { 85 VGLClear(VGLDisplay, 0); 86 munmap(VGLMem, VGLAdpInfo.va_window_size); 87 } 88 89 ioctl(0, FBIO_SETLINEWIDTH, &VGLOldVXsize); 90 91 if (VGLOldMode >= M_VESA_BASE) 92 ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0); 93 else 94 ioctl(0, _IO('S', VGLOldMode), 0); 95 if (VGLOldModeInfo.vi_flags & V_INFO_GRAPHICS) { 96 size[0] = VGLOldVInfo.mv_csz; 97 size[1] = VGLOldVInfo.mv_rsz; 98 size[2] = VGLOldVInfo.font_size;; 99 ioctl(0, KDRASTER, size); 100 } 101 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT) 102 ioctl(0, KDDISABIO, 0); 103 ioctl(0, KDSETMODE, KD_TEXT); 104 smode.mode = VT_AUTO; 105 ioctl(0, VT_SETMODE, &smode); 106 if (VGLBuf) 107 free(VGLBuf); 108 VGLBuf = NULL; 109 free(VGLDisplay); 110 VGLDisplay = NULL; 111 VGLKeyboardEnd(); 112 } 113 114 static void 115 VGLAbort(int arg) 116 { 117 sigset_t mask; 118 119 VGLAbortPending = 1; 120 signal(SIGINT, SIG_IGN); 121 signal(SIGTERM, SIG_IGN); 122 signal(SIGUSR2, SIG_IGN); 123 if (arg == SIGBUS || arg == SIGSEGV) { 124 signal(arg, SIG_DFL); 125 sigemptyset(&mask); 126 sigaddset(&mask, arg); 127 sigprocmask(SIG_UNBLOCK, &mask, NULL); 128 VGLEnd(); 129 kill(getpid(), arg); 130 } 131 } 132 133 static void 134 VGLSwitch(int arg __unused) 135 { 136 if (!VGLOnDisplay) 137 VGLOnDisplay = 1; 138 else 139 VGLOnDisplay = 0; 140 VGLSwitchPending = 1; 141 signal(SIGUSR1, VGLSwitch); 142 } 143 144 int 145 VGLInit(int mode) 146 { 147 struct vt_mode smode; 148 int adptype, depth; 149 150 if (VGLInitDone) 151 return -1; 152 153 signal(SIGUSR1, VGLSwitch); 154 signal(SIGINT, VGLAbort); 155 signal(SIGTERM, VGLAbort); 156 signal(SIGSEGV, VGLAbort); 157 signal(SIGBUS, VGLAbort); 158 signal(SIGUSR2, SIG_IGN); 159 160 VGLOnDisplay = 1; 161 VGLSwitchPending = 0; 162 VGLAbortPending = 0; 163 164 if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype)) 165 return -1; 166 if (IOCGROUP(mode) == 'V') /* XXX: this is ugly */ 167 VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE; 168 else 169 VGLModeInfo.vi_mode = mode & 0x0ff; 170 if (ioctl(0, CONS_MODEINFO, &VGLModeInfo)) /* FBIO_MODEINFO */ 171 return -1; 172 173 /* Save info for old mode to restore font size if old mode is graphics. */ 174 VGLOldModeInfo.vi_mode = VGLOldMode; 175 if (ioctl(0, CONS_MODEINFO, &VGLOldModeInfo)) 176 return -1; 177 VGLOldVInfo.size = sizeof(VGLOldVInfo); 178 if (ioctl(0, CONS_GETINFO, &VGLOldVInfo)) 179 return -1; 180 181 VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap)); 182 if (VGLDisplay == NULL) 183 return -2; 184 185 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT && ioctl(0, KDENABIO, 0)) { 186 free(VGLDisplay); 187 return -3; 188 } 189 190 VGLInitDone = 1; 191 192 /* 193 * vi_mem_model specifies the memory model of the current video mode 194 * in -CURRENT. 195 */ 196 switch (VGLModeInfo.vi_mem_model) { 197 case V_INFO_MM_PLANAR: 198 /* we can handle EGA/VGA planner modes only */ 199 if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4 200 || (adptype != KD_EGA && adptype != KD_VGA)) { 201 VGLEnd(); 202 return -4; 203 } 204 VGLDisplay->Type = VIDBUF4; 205 VGLDisplay->PixelBytes = 1; 206 break; 207 case V_INFO_MM_PACKED: 208 /* we can do only 256 color packed modes */ 209 if (VGLModeInfo.vi_depth != 8) { 210 VGLEnd(); 211 return -4; 212 } 213 VGLDisplay->Type = VIDBUF8; 214 VGLDisplay->PixelBytes = 1; 215 break; 216 case V_INFO_MM_VGAX: 217 VGLDisplay->Type = VIDBUF8X; 218 VGLDisplay->PixelBytes = 1; 219 break; 220 case V_INFO_MM_DIRECT: 221 VGLDisplay->PixelBytes = VGLModeInfo.vi_pixel_size; 222 switch (VGLDisplay->PixelBytes) { 223 case 2: 224 VGLDisplay->Type = VIDBUF16; 225 break; 226 #if notyet 227 case 3: 228 VGLDisplay->Type = VIDBUF24; 229 break; 230 #endif 231 case 4: 232 VGLDisplay->Type = VIDBUF32; 233 break; 234 default: 235 VGLEnd(); 236 return -4; 237 } 238 break; 239 default: 240 VGLEnd(); 241 return -4; 242 } 243 244 ioctl(0, VT_WAITACTIVE, 0); 245 ioctl(0, KDSETMODE, KD_GRAPHICS); 246 if (ioctl(0, mode, 0)) { 247 VGLEnd(); 248 return -5; 249 } 250 if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) { /* FBIO_ADPINFO */ 251 VGLEnd(); 252 return -6; 253 } 254 255 /* 256 * Calculate the shadow screen buffer size. In -CURRENT, va_buffer_size 257 * always holds the entire frame buffer size, wheather it's in the linear 258 * mode or windowed mode. 259 * VGLBufSize = VGLAdpInfo.va_buffer_size; 260 * In -STABLE, va_buffer_size holds the frame buffer size, only if 261 * the linear frame buffer mode is supported. Otherwise the field is zero. 262 * We shall calculate the minimal size in this case: 263 * VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes 264 * or 265 * VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes; 266 * Use whichever is larger. 267 */ 268 if (VGLAdpInfo.va_buffer_size != 0) 269 VGLBufSize = VGLAdpInfo.va_buffer_size; 270 else 271 VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height, 272 VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes; 273 /* 274 * The above is for old -CURRENT. Current -CURRENT since r203535 and/or 275 * r248799 restricts va_buffer_size to the displayed size in VESA modes to 276 * avoid wasting kva for mapping unused parts of the frame buffer. But all 277 * parts were usable here. Applying the same restriction to user mappings 278 * makes our virtualization useless and breaks our panning, but large frame 279 * buffers are also difficult for us to manage (clearing and switching may 280 * be too slow, and malloc() may fail). Restrict ourselves similarly to 281 * get the same efficiency and bugs for all kernels. 282 */ 283 if (VGLModeInfo.vi_mode >= M_VESA_BASE) 284 VGLBufSize = VGLAdpInfo.va_line_width*VGLModeInfo.vi_height* 285 VGLModeInfo.vi_planes; 286 VGLBuf = malloc(VGLBufSize); 287 if (VGLBuf == NULL) { 288 VGLEnd(); 289 return -7; 290 } 291 292 #ifdef LIBVGL_DEBUG 293 fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize); 294 #endif 295 296 /* see if we are in the windowed buffer mode or in the linear buffer mode */ 297 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) { 298 switch (VGLDisplay->Type) { 299 case VIDBUF4: 300 VGLDisplay->Type = VIDBUF4S; 301 break; 302 case VIDBUF8: 303 VGLDisplay->Type = VIDBUF8S; 304 break; 305 case VIDBUF16: 306 VGLDisplay->Type = VIDBUF16S; 307 break; 308 case VIDBUF24: 309 VGLDisplay->Type = VIDBUF24S; 310 break; 311 case VIDBUF32: 312 VGLDisplay->Type = VIDBUF32S; 313 break; 314 default: 315 VGLEnd(); 316 return -8; 317 } 318 } 319 320 VGLMode = mode; 321 VGLCurWindow = 0; 322 323 VGLDisplay->Xsize = VGLModeInfo.vi_width; 324 VGLDisplay->Ysize = VGLModeInfo.vi_height; 325 depth = VGLModeInfo.vi_depth; 326 if (depth == 15) 327 depth = 16; 328 VGLOldVXsize = 329 VGLDisplay->VXsize = VGLAdpInfo.va_line_width 330 *8/(depth/VGLModeInfo.vi_planes); 331 VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 332 VGLDisplay->Xorigin = 0; 333 VGLDisplay->Yorigin = 0; 334 335 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 336 MAP_FILE | MAP_SHARED, 0, 0); 337 if (VGLMem == MAP_FAILED) { 338 VGLEnd(); 339 return -7; 340 } 341 VGLDisplay->Bitmap = VGLMem; 342 343 VGLVDisplay = *VGLDisplay; 344 VGLVDisplay.Type = MEMBUF; 345 if (VGLModeInfo.vi_depth < 8) 346 VGLVDisplay.Bitmap = malloc(2 * VGLBufSize); 347 else 348 VGLVDisplay.Bitmap = VGLBuf; 349 350 VGLSavePalette(); 351 352 #ifdef LIBVGL_DEBUG 353 fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width); 354 fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 355 VGLDisplay->Xsize, VGLDisplay->Ysize, 356 VGLDisplay->VXsize, VGLDisplay->VYsize); 357 #endif 358 359 smode.mode = VT_PROCESS; 360 smode.waitv = 0; 361 smode.relsig = SIGUSR1; 362 smode.acqsig = SIGUSR1; 363 smode.frsig = SIGINT; 364 if (ioctl(0, VT_SETMODE, &smode)) { 365 VGLEnd(); 366 return -9; 367 } 368 VGLTextSetFontFile((byte*)0); 369 VGLClear(VGLDisplay, 0); 370 return 0; 371 } 372 373 void 374 VGLCheckSwitch() 375 { 376 if (VGLAbortPending) { 377 VGLEnd(); 378 exit(0); 379 } 380 while (VGLSwitchPending) { 381 VGLSwitchPending = 0; 382 if (VGLOnDisplay) { 383 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT) 384 ioctl(0, KDENABIO, 0); 385 ioctl(0, KDSETMODE, KD_GRAPHICS); 386 ioctl(0, VGLMode, 0); 387 VGLCurWindow = 0; 388 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 389 MAP_FILE | MAP_SHARED, 0, 0); 390 391 /* XXX: what if mmap() has failed! */ 392 VGLDisplay->Type = VIDBUF8; /* XXX */ 393 switch (VGLModeInfo.vi_mem_model) { 394 case V_INFO_MM_PLANAR: 395 if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) { 396 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 397 VGLDisplay->Type = VIDBUF4S; 398 else 399 VGLDisplay->Type = VIDBUF4; 400 } else { 401 /* shouldn't be happening */ 402 } 403 break; 404 case V_INFO_MM_PACKED: 405 if (VGLModeInfo.vi_depth == 8) { 406 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 407 VGLDisplay->Type = VIDBUF8S; 408 else 409 VGLDisplay->Type = VIDBUF8; 410 } 411 break; 412 case V_INFO_MM_VGAX: 413 VGLDisplay->Type = VIDBUF8X; 414 break; 415 case V_INFO_MM_DIRECT: 416 switch (VGLModeInfo.vi_pixel_size) { 417 case 2: 418 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 419 VGLDisplay->Type = VIDBUF16S; 420 else 421 VGLDisplay->Type = VIDBUF16; 422 break; 423 case 3: 424 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 425 VGLDisplay->Type = VIDBUF24S; 426 else 427 VGLDisplay->Type = VIDBUF24; 428 break; 429 case 4: 430 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 431 VGLDisplay->Type = VIDBUF32S; 432 else 433 VGLDisplay->Type = VIDBUF32; 434 break; 435 default: 436 /* shouldn't be happening */ 437 break; 438 } 439 default: 440 /* shouldn't be happening */ 441 break; 442 } 443 444 VGLDisplay->Bitmap = VGLMem; 445 VGLDisplay->Xsize = VGLModeInfo.vi_width; 446 VGLDisplay->Ysize = VGLModeInfo.vi_height; 447 VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize); 448 VGLRestoreBlank(); 449 VGLRestoreBorder(); 450 VGLMouseRestore(); 451 VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin); 452 VGLBitmapCopy(&VGLVDisplay, 0, 0, VGLDisplay, 0, 0, 453 VGLDisplay->VXsize, VGLDisplay->VYsize); 454 VGLRestorePalette(); 455 ioctl(0, VT_RELDISP, VT_ACKACQ); 456 } 457 else { 458 VGLMem = MAP_FAILED; 459 munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size); 460 ioctl(0, VGLOldMode, 0); 461 ioctl(0, KDSETMODE, KD_TEXT); 462 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT) 463 ioctl(0, KDDISABIO, 0); 464 ioctl(0, VT_RELDISP, VT_TRUE); 465 VGLDisplay->Bitmap = VGLBuf; 466 VGLDisplay->Type = MEMBUF; 467 VGLDisplay->Xsize = VGLDisplay->VXsize; 468 VGLDisplay->Ysize = VGLDisplay->VYsize; 469 while (!VGLOnDisplay) pause(); 470 } 471 } 472 } 473 474 int 475 VGLSetSegment(unsigned int offset) 476 { 477 if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) { 478 ioctl(0, CONS_SETWINORG, offset); /* FBIO_SETWINORG */ 479 VGLCurWindow = offset/VGLAdpInfo.va_window_size; 480 } 481 return (offset%VGLAdpInfo.va_window_size); 482 } 483 484 int 485 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize) 486 { 487 int depth; 488 489 if (VXsize < object->Xsize || VYsize < object->Ysize) 490 return -1; 491 if (object->Type == MEMBUF) 492 return -1; 493 if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize)) 494 return -1; 495 ioctl(0, CONS_ADPINFO, &VGLAdpInfo); /* FBIO_ADPINFO */ 496 depth = VGLModeInfo.vi_depth; 497 if (depth == 15) 498 depth = 16; 499 object->VXsize = VGLAdpInfo.va_line_width 500 *8/(depth/VGLModeInfo.vi_planes); 501 object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 502 if (VYsize < object->VYsize) 503 object->VYsize = VYsize; 504 505 #ifdef LIBVGL_DEBUG 506 fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 507 object->Xsize, object->Ysize, object->VXsize, object->VYsize); 508 #endif 509 510 return 0; 511 } 512 513 int 514 VGLPanScreen(VGLBitmap *object, int x, int y) 515 { 516 video_display_start_t origin; 517 518 if (x < 0 || x + object->Xsize > object->VXsize 519 || y < 0 || y + object->Ysize > object->VYsize) 520 return -1; 521 if (object->Type == MEMBUF) 522 return 0; 523 origin.x = x; 524 origin.y = y; 525 if (ioctl(0, FBIO_SETDISPSTART, &origin)) 526 return -1; 527 object->Xorigin = x; 528 object->Yorigin = y; 529 530 #ifdef LIBVGL_DEBUG 531 fprintf(stderr, "new origin: (%d, %d)\n", x, y); 532 #endif 533 534 return 0; 535 } 536