1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * Copyright 2020 Toomas Soome 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 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 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <stand.h> 31 #include <sys/param.h> 32 #include <machine/psl.h> 33 #include <machine/cpufunc.h> 34 #include <stdbool.h> 35 #include <bootstrap.h> 36 #include <btxv86.h> 37 #include <gfx_fb.h> 38 #include <dev/vt/hw/vga/vt_vga_reg.h> 39 #include "libi386.h" 40 #include "vbe.h" 41 42 /* 43 * VESA BIOS Extensions routines 44 */ 45 46 static struct vbeinfoblock *vbe; 47 static struct modeinfoblock *vbe_mode; 48 49 static uint16_t *vbe_mode_list; 50 static size_t vbe_mode_list_size; 51 struct vesa_edid_info *edid_info = NULL; 52 53 /* The default VGA color palette format is 6 bits per primary color. */ 54 int palette_format = 6; 55 56 #define VESA_MODE_BASE 0x100 57 58 /* 59 * palette array for 8-bit indexed colors. In this case, cmap does store 60 * index and pe8 does store actual RGB. This is needed because we may 61 * not be able to read palette data from hardware. 62 */ 63 struct paletteentry *pe8 = NULL; 64 65 static struct named_resolution { 66 const char *name; 67 const char *alias; 68 unsigned int width; 69 unsigned int height; 70 } resolutions[] = { 71 { 72 .name = "480p", 73 .width = 640, 74 .height = 480, 75 }, 76 { 77 .name = "720p", 78 .width = 1280, 79 .height = 720, 80 }, 81 { 82 .name = "1080p", 83 .width = 1920, 84 .height = 1080, 85 }, 86 { 87 .name = "1440p", 88 .width = 2560, 89 .height = 1440, 90 }, 91 { 92 .name = "2160p", 93 .alias = "4k", 94 .width = 3840, 95 .height = 2160, 96 }, 97 { 98 .name = "5k", 99 .width = 5120, 100 .height = 2880, 101 } 102 }; 103 104 static bool 105 vbe_resolution_compare(struct named_resolution *res, const char *cmp) 106 { 107 108 if (strcasecmp(res->name, cmp) == 0) 109 return (true); 110 if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0) 111 return (true); 112 return (false); 113 } 114 115 static void 116 vbe_get_max_resolution(int *width, int *height) 117 { 118 struct named_resolution *res; 119 char *maxres; 120 char *height_start, *width_start; 121 int idx; 122 123 *width = *height = 0; 124 maxres = getenv("vbe_max_resolution"); 125 /* No max_resolution set? Bail out; choose highest resolution */ 126 if (maxres == NULL) 127 return; 128 /* See if it matches one of our known resolutions */ 129 for (idx = 0; idx < nitems(resolutions); ++idx) { 130 res = &resolutions[idx]; 131 if (vbe_resolution_compare(res, maxres)) { 132 *width = res->width; 133 *height = res->height; 134 return; 135 } 136 } 137 /* Not a known resolution, try to parse it; make a copy we can modify */ 138 maxres = strdup(maxres); 139 if (maxres == NULL) 140 return; 141 height_start = strchr(maxres, 'x'); 142 if (height_start == NULL) { 143 free(maxres); 144 return; 145 } 146 width_start = maxres; 147 *height_start++ = 0; 148 /* Errors from this will effectively mean "no max" */ 149 *width = (int)strtol(width_start, NULL, 0); 150 *height = (int)strtol(height_start, NULL, 0); 151 free(maxres); 152 } 153 154 int 155 vga_get_reg(int reg, int index) 156 { 157 return (inb(reg + index)); 158 } 159 160 int 161 vga_get_atr(int reg, int i) 162 { 163 int ret; 164 165 (void) inb(reg + VGA_GEN_INPUT_STAT_1); 166 outb(reg + VGA_AC_WRITE, i); 167 ret = inb(reg + VGA_AC_READ); 168 169 (void) inb(reg + VGA_GEN_INPUT_STAT_1); 170 171 return (ret); 172 } 173 174 void 175 vga_set_atr(int reg, int i, int v) 176 { 177 (void) inb(reg + VGA_GEN_INPUT_STAT_1); 178 outb(reg + VGA_AC_WRITE, i); 179 outb(reg + VGA_AC_WRITE, v); 180 181 (void) inb(reg + VGA_GEN_INPUT_STAT_1); 182 } 183 184 void 185 vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val) 186 { 187 outb(reg + indexreg, index); 188 outb(reg + datareg, val); 189 } 190 191 int 192 vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index) 193 { 194 outb(reg + indexreg, index); 195 return (inb(reg + datareg)); 196 } 197 198 int 199 vga_get_crtc(int reg, int i) 200 { 201 return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i)); 202 } 203 204 void 205 vga_set_crtc(int reg, int i, int v) 206 { 207 vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v); 208 } 209 210 int 211 vga_get_seq(int reg, int i) 212 { 213 return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i)); 214 } 215 216 void 217 vga_set_seq(int reg, int i, int v) 218 { 219 vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v); 220 } 221 222 int 223 vga_get_grc(int reg, int i) 224 { 225 return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i)); 226 } 227 228 void 229 vga_set_grc(int reg, int i, int v) 230 { 231 vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v); 232 } 233 234 /* 235 * Return true when this controller is VGA compatible. 236 */ 237 bool 238 vbe_is_vga(void) 239 { 240 if (vbe == NULL) 241 return (false); 242 243 return ((vbe->Capabilities & VBE_CAP_NONVGA) == 0); 244 } 245 246 /* Actually assuming mode 3. */ 247 void 248 bios_set_text_mode(int mode) 249 { 250 int atr; 251 252 if (vbe->Capabilities & VBE_CAP_DAC8) { 253 int m; 254 255 /* 256 * The mode change should reset the palette format to 257 * 6 bits, but apparently some systems do fail with 8-bit 258 * palette, so we switch to 6-bit here. 259 */ 260 m = 0x0600; 261 (void) biosvbe_palette_format(&m); 262 palette_format = m; 263 } 264 v86.ctl = V86_FLAGS; 265 v86.addr = 0x10; 266 v86.eax = mode; /* set VGA text mode */ 267 v86int(); 268 atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL); 269 atr &= ~VGA_AC_MC_BI; 270 atr &= ~VGA_AC_MC_ELG; 271 vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr); 272 273 gfx_state.tg_mode = mode; 274 gfx_state.tg_fb_type = FB_TEXT; 275 gfx_state.tg_fb.fb_height = TEXT_ROWS; 276 gfx_state.tg_fb.fb_width = TEXT_COLS; 277 278 gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16; 279 gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8; 280 gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0; 281 gfx_state.tg_ctype = CT_INDEXED; 282 env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL); 283 } 284 285 /* Function 00h - Return VBE Controller Information */ 286 static int 287 biosvbe_info(struct vbeinfoblock *vbep) 288 { 289 struct vbeinfoblock *rvbe; 290 int ret; 291 292 if (vbep == NULL) 293 return (VBE_FAILED); 294 295 rvbe = bio_alloc(sizeof(*rvbe)); 296 if (rvbe == NULL) 297 return (VBE_FAILED); 298 299 /* Now check if we have vesa. */ 300 memset(rvbe, 0, sizeof (*vbe)); 301 memcpy(rvbe->VbeSignature, "VBE2", 4); 302 303 v86.ctl = V86_FLAGS; 304 v86.addr = 0x10; 305 v86.eax = 0x4f00; 306 v86.es = VTOPSEG(rvbe); 307 v86.edi = VTOPOFF(rvbe); 308 v86int(); 309 ret = v86.eax & 0xffff; 310 311 if (ret != VBE_SUCCESS) 312 goto done; 313 314 if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) { 315 ret = VBE_NOTSUP; 316 goto done; 317 } 318 bcopy(rvbe, vbep, sizeof(*vbep)); 319 done: 320 bio_free(rvbe, sizeof(*rvbe)); 321 return (ret); 322 } 323 324 /* Function 01h - Return VBE Mode Information */ 325 static int 326 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi) 327 { 328 struct modeinfoblock *rmi; 329 int ret; 330 331 rmi = bio_alloc(sizeof(*rmi)); 332 if (rmi == NULL) 333 return (VBE_FAILED); 334 335 v86.ctl = V86_FLAGS; 336 v86.addr = 0x10; 337 v86.eax = 0x4f01; 338 v86.ecx = mode; 339 v86.es = VTOPSEG(rmi); 340 v86.edi = VTOPOFF(rmi); 341 v86int(); 342 343 ret = v86.eax & 0xffff; 344 if (ret != VBE_SUCCESS) 345 goto done; 346 bcopy(rmi, mi, sizeof(*rmi)); 347 done: 348 bio_free(rmi, sizeof(*rmi)); 349 return (ret); 350 } 351 352 /* Function 02h - Set VBE Mode */ 353 static int 354 biosvbe_set_mode(int mode, struct crtciinfoblock *ci) 355 { 356 int rv; 357 358 if (vbe->Capabilities & VBE_CAP_DAC8) { 359 int m; 360 361 /* 362 * The mode change should reset the palette format to 363 * 6 bits, but apparently some systems do fail with 8-bit 364 * palette, so we switch to 6-bit here. 365 */ 366 m = 0x0600; 367 if (biosvbe_palette_format(&m) == VBE_SUCCESS) 368 palette_format = m; 369 } 370 v86.ctl = V86_FLAGS; 371 v86.addr = 0x10; 372 v86.eax = 0x4f02; 373 v86.ebx = mode | 0x4000; /* set linear FB bit */ 374 v86.es = VTOPSEG(ci); 375 v86.edi = VTOPOFF(ci); 376 v86int(); 377 rv = v86.eax & 0xffff; 378 if (vbe->Capabilities & VBE_CAP_DAC8) { 379 int m; 380 381 /* Switch to 8-bits per primary color. */ 382 m = 0x0800; 383 if (biosvbe_palette_format(&m) == VBE_SUCCESS) 384 palette_format = m; 385 } 386 env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL); 387 return (rv); 388 } 389 390 /* Function 03h - Get VBE Mode */ 391 static int 392 biosvbe_get_mode(int *mode) 393 { 394 v86.ctl = V86_FLAGS; 395 v86.addr = 0x10; 396 v86.eax = 0x4f03; 397 v86int(); 398 *mode = v86.ebx & 0x3fff; /* Bits 0-13 */ 399 return (v86.eax & 0xffff); 400 } 401 402 /* Function 08h - Set/Get DAC Palette Format */ 403 int 404 biosvbe_palette_format(int *format) 405 { 406 v86.ctl = V86_FLAGS; 407 v86.addr = 0x10; 408 v86.eax = 0x4f08; 409 v86.ebx = *format; 410 v86int(); 411 *format = (v86.ebx >> 8) & 0xff; 412 return (v86.eax & 0xffff); 413 } 414 415 /* Function 09h - Set/Get Palette Data */ 416 static int 417 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe) 418 { 419 v86.ctl = V86_FLAGS; 420 v86.addr = 0x10; 421 v86.eax = 0x4f09; 422 v86.ebx = mode; 423 v86.edx = reg; 424 v86.ecx = 1; 425 v86.es = VTOPSEG(pe); 426 v86.edi = VTOPOFF(pe); 427 v86int(); 428 return (v86.eax & 0xffff); 429 } 430 431 /* 432 * Function 15h BL=00h - Report VBE/DDC Capabilities 433 * 434 * int biosvbe_ddc_caps(void) 435 * return: VBE/DDC capabilities 436 */ 437 static int 438 biosvbe_ddc_caps(void) 439 { 440 v86.ctl = V86_FLAGS; 441 v86.addr = 0x10; 442 v86.eax = 0x4f15; /* display identification extensions */ 443 v86.ebx = 0; /* report DDC capabilities */ 444 v86.ecx = 0; /* controller unit number (00h = primary) */ 445 v86.es = 0; 446 v86.edi = 0; 447 v86int(); 448 if (VBE_ERROR(v86.eax & 0xffff)) 449 return (0); 450 return (v86.ebx & 0xffff); 451 } 452 453 /* Function 11h BL=01h - Flat Panel status */ 454 static int 455 biosvbe_ddc_read_flat_panel_info(void *buf) 456 { 457 v86.ctl = V86_FLAGS; 458 v86.addr = 0x10; 459 v86.eax = 0x4f11; /* Flat Panel Interface extensions */ 460 v86.ebx = 1; /* Return Flat Panel Information */ 461 v86.es = VTOPSEG(buf); 462 v86.edi = VTOPOFF(buf); 463 v86int(); 464 return (v86.eax & 0xffff); 465 } 466 467 /* Function 15h BL=01h - Read EDID */ 468 static int 469 biosvbe_ddc_read_edid(int blockno, void *buf) 470 { 471 v86.ctl = V86_FLAGS; 472 v86.addr = 0x10; 473 v86.eax = 0x4f15; /* display identification extensions */ 474 v86.ebx = 1; /* read EDID */ 475 v86.ecx = 0; /* controller unit number (00h = primary) */ 476 v86.edx = blockno; 477 v86.es = VTOPSEG(buf); 478 v86.edi = VTOPOFF(buf); 479 v86int(); 480 return (v86.eax & 0xffff); 481 } 482 483 static int 484 vbe_mode_is_supported(struct modeinfoblock *mi) 485 { 486 if ((mi->ModeAttributes & 0x01) == 0) 487 return (0); /* mode not supported by hardware */ 488 if ((mi->ModeAttributes & 0x08) == 0) 489 return (0); /* linear fb not available */ 490 if ((mi->ModeAttributes & 0x10) == 0) 491 return (0); /* text mode */ 492 if (mi->NumberOfPlanes != 1) 493 return (0); /* planar mode not supported */ 494 if (mi->MemoryModel != 0x04 /* Packed pixel */ && 495 mi->MemoryModel != 0x06 /* Direct Color */) 496 return (0); /* unsupported pixel format */ 497 return (1); 498 } 499 500 static bool 501 vbe_check(void) 502 { 503 504 if (vbe == NULL) { 505 printf("VBE not available\n"); 506 return (false); 507 } 508 return (true); 509 } 510 511 static int 512 mode_set(struct env_var *ev, int flags __unused, const void *value) 513 { 514 int mode; 515 516 if (strcmp(ev->ev_name, "screen.textmode") == 0) { 517 unsigned long v; 518 char *end; 519 520 if (value == NULL) 521 return (0); 522 errno = 0; 523 v = strtoul(value, &end, 0); 524 if (errno != 0 || *(char *)value == '\0' || *end != '\0' || 525 (v != 0 && v != 1)) 526 return (EINVAL); 527 env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, 528 value, NULL, NULL); 529 if (v == 1) { 530 reset_font_flags(); 531 bios_text_font(true); 532 bios_set_text_mode(VGA_TEXT_MODE); 533 (void) cons_update_mode(false); 534 return (0); 535 } 536 } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) { 537 env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK, 538 value, NULL, NULL); 539 } else { 540 return (EINVAL); 541 } 542 543 mode = vbe_default_mode(); 544 if (gfx_state.tg_mode != mode) { 545 reset_font_flags(); 546 bios_text_font(false); 547 vbe_set_mode(mode); 548 cons_update_mode(true); 549 } 550 return (0); 551 } 552 553 static void * 554 vbe_farptr(uint32_t farptr) 555 { 556 return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)))); 557 } 558 559 void 560 vbe_init(void) 561 { 562 uint16_t *p, *ml; 563 564 /* First set FB for text mode. */ 565 gfx_state.tg_fb_type = FB_TEXT; 566 gfx_state.tg_fb.fb_height = TEXT_ROWS; 567 gfx_state.tg_fb.fb_width = TEXT_COLS; 568 gfx_state.tg_ctype = CT_INDEXED; 569 gfx_state.tg_mode = 3; 570 571 env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set, 572 env_nounset); 573 env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set, 574 env_nounset); 575 576 if (vbe == NULL) { 577 vbe = malloc(sizeof(*vbe)); 578 if (vbe == NULL) 579 return; 580 } 581 582 if (vbe_mode == NULL) { 583 vbe_mode = malloc(sizeof(*vbe_mode)); 584 if (vbe_mode == NULL) { 585 free(vbe); 586 vbe = NULL; 587 } 588 } 589 590 if (biosvbe_info(vbe) != VBE_SUCCESS) { 591 free(vbe); 592 vbe = NULL; 593 free(vbe_mode); 594 vbe_mode = NULL; 595 return; 596 } 597 598 /* 599 * Copy mode list. We must do this because some systems do 600 * corrupt the provided list (vbox 6.1 is one example). 601 */ 602 p = ml = vbe_farptr(vbe->VideoModePtr); 603 while(*p++ != 0xFFFF) 604 ; 605 606 vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml; 607 608 /* 609 * Since vbe_init() is used only once at very start of the loader, 610 * we assume malloc will not fail there, but in case it does, 611 * we point vbe_mode_list to memory pointed by VideoModePtr. 612 */ 613 vbe_mode_list = malloc(vbe_mode_list_size); 614 if (vbe_mode_list == NULL) 615 vbe_mode_list = ml; 616 else 617 bcopy(ml, vbe_mode_list, vbe_mode_list_size); 618 619 /* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */ 620 vbe->VideoModePtr = 0; 621 622 /* vbe_set_mode() will set up the rest. */ 623 } 624 625 bool 626 vbe_available(void) 627 { 628 return (gfx_state.tg_fb_type == FB_VBE); 629 } 630 631 int 632 vbe_set_palette(const struct paletteentry *entry, size_t slot) 633 { 634 struct paletteentry pe; 635 int mode, ret; 636 637 if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0) 638 return (1); 639 640 if (gfx_state.tg_ctype != CT_INDEXED) { 641 return (1); 642 } 643 644 pe.Blue = entry->Blue; 645 pe.Green = entry->Green; 646 pe.Red = entry->Red; 647 pe.Reserved = entry->Reserved; 648 649 if (vbe->Capabilities & VBE_CAP_SNOW) 650 mode = 0x80; 651 else 652 mode = 0; 653 654 ret = biosvbe_palette_data(mode, slot, &pe); 655 656 return (ret == VBE_SUCCESS ? 0 : 1); 657 } 658 659 int 660 vbe_get_mode(void) 661 { 662 return (gfx_state.tg_mode); 663 } 664 665 int 666 vbe_set_mode(int modenum) 667 { 668 struct modeinfoblock mi; 669 int bpp, ret; 670 671 if (!vbe_check()) 672 return (1); 673 674 ret = biosvbe_get_mode_info(modenum, &mi); 675 if (VBE_ERROR(ret)) { 676 printf("mode 0x%x invalid\n", modenum); 677 return (1); 678 } 679 680 if (!vbe_mode_is_supported(&mi)) { 681 printf("mode 0x%x not supported\n", modenum); 682 return (1); 683 } 684 685 /* calculate bytes per pixel */ 686 switch (mi.BitsPerPixel) { 687 case 32: 688 case 24: 689 case 16: 690 case 15: 691 case 8: 692 break; 693 default: 694 printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel); 695 return (1); 696 } 697 698 ret = biosvbe_set_mode(modenum, NULL); 699 if (VBE_ERROR(ret)) { 700 printf("mode 0x%x could not be set\n", modenum); 701 return (1); 702 } 703 704 gfx_state.tg_mode = modenum; 705 gfx_state.tg_fb_type = FB_VBE; 706 /* make sure we have current MI in vbestate */ 707 memcpy(vbe_mode, &mi, sizeof (*vbe_mode)); 708 709 gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff; 710 gfx_state.tg_fb.fb_height = mi.YResolution; 711 gfx_state.tg_fb.fb_width = mi.XResolution; 712 gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel; 713 714 free(gfx_state.tg_shadow_fb); 715 gfx_state.tg_shadow_fb = malloc(mi.YResolution * mi.XResolution * 716 sizeof(struct paletteentry)); 717 718 /* Bytes per pixel */ 719 bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY; 720 721 /* vbe_mode_is_supported() excludes the rest */ 722 switch (mi.MemoryModel) { 723 case 0x4: 724 gfx_state.tg_ctype = CT_INDEXED; 725 break; 726 case 0x6: 727 gfx_state.tg_ctype = CT_RGB; 728 break; 729 } 730 731 #define COLOR_MASK(size, pos) (((1 << size) - 1) << pos) 732 if (gfx_state.tg_ctype == CT_INDEXED) { 733 gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16); 734 gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8); 735 gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0); 736 } else if (vbe->VbeVersion >= 0x300) { 737 gfx_state.tg_fb.fb_mask_red = 738 COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition); 739 gfx_state.tg_fb.fb_mask_green = 740 COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition); 741 gfx_state.tg_fb.fb_mask_blue = 742 COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition); 743 } else { 744 gfx_state.tg_fb.fb_mask_red = 745 COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition); 746 gfx_state.tg_fb.fb_mask_green = 747 COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition); 748 gfx_state.tg_fb.fb_mask_blue = 749 COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition); 750 } 751 gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red | 752 gfx_state.tg_fb.fb_mask_green | 753 gfx_state.tg_fb.fb_mask_blue); 754 755 if (vbe->VbeVersion >= 0x300) 756 gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp; 757 else 758 gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp; 759 760 gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride * 761 bpp; 762 763 return (0); 764 } 765 766 /* 767 * Verify existence of mode number or find mode by 768 * dimensions. If depth is not given, walk values 32, 24, 16, 8. 769 */ 770 static int 771 vbe_find_mode_xydm(int x, int y, int depth, int m) 772 { 773 struct modeinfoblock mi; 774 uint16_t *farptr; 775 uint16_t mode; 776 int idx, nentries, i; 777 778 memset(vbe, 0, sizeof (*vbe)); 779 if (biosvbe_info(vbe) != VBE_SUCCESS) 780 return (0); 781 782 if (m != -1) 783 i = 8; 784 else if (depth == -1) 785 i = 32; 786 else 787 i = depth; 788 789 nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); 790 while (i > 0) { 791 for (idx = 0; idx < nentries; idx++) { 792 mode = vbe_mode_list[idx]; 793 if (mode == 0xffff) 794 break; 795 796 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { 797 continue; 798 } 799 800 /* we only care about linear modes here */ 801 if (vbe_mode_is_supported(&mi) == 0) 802 continue; 803 804 if (m != -1) { 805 if (m == mode) 806 return (mode); 807 else 808 continue; 809 } 810 811 if (mi.XResolution == x && 812 mi.YResolution == y && 813 mi.BitsPerPixel == i) 814 return (mode); 815 } 816 if (depth != -1) 817 break; 818 819 i -= 8; 820 } 821 822 return (0); 823 } 824 825 static int 826 vbe_find_mode(char *str) 827 { 828 int x, y, depth; 829 830 if (!gfx_parse_mode_str(str, &x, &y, &depth)) 831 return (0); 832 833 return (vbe_find_mode_xydm(x, y, depth, -1)); 834 } 835 836 static void 837 vbe_dump_mode(int modenum, struct modeinfoblock *mi) 838 { 839 printf("0x%x=%dx%dx%d", modenum, 840 mi->XResolution, mi->YResolution, mi->BitsPerPixel); 841 } 842 843 static bool 844 vbe_get_edid(edid_res_list_t *res) 845 { 846 struct vesa_edid_info *edidp; 847 const uint8_t magic[] = EDID_MAGIC; 848 int ddc_caps; 849 bool ret = false; 850 851 if (edid_info != NULL) 852 return (gfx_get_edid_resolution(edid_info, res)); 853 854 ddc_caps = biosvbe_ddc_caps(); 855 if (ddc_caps == 0) { 856 return (ret); 857 } 858 859 edidp = bio_alloc(sizeof(*edidp)); 860 if (edidp == NULL) 861 return (ret); 862 memset(edidp, 0, sizeof(*edidp)); 863 864 if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp))) 865 goto done; 866 867 if (memcmp(edidp, magic, sizeof(magic)) != 0) 868 goto done; 869 870 /* Unknown EDID version. */ 871 if (edidp->header.version != 1) 872 goto done; 873 874 ret = gfx_get_edid_resolution(edidp, res); 875 edid_info = malloc(sizeof(*edid_info)); 876 if (edid_info != NULL) 877 memcpy(edid_info, edidp, sizeof (*edid_info)); 878 done: 879 bio_free(edidp, sizeof(*edidp)); 880 return (ret); 881 } 882 883 static bool 884 vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight) 885 { 886 struct vesa_flat_panel_info *fp_info; 887 bool ret = false; 888 889 fp_info = bio_alloc(sizeof (*fp_info)); 890 if (fp_info == NULL) 891 return (ret); 892 memset(fp_info, 0, sizeof (*fp_info)); 893 894 if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info))) 895 goto done; 896 897 *pwidth = fp_info->HSize; 898 *pheight = fp_info->VSize; 899 ret = true; 900 901 done: 902 bio_free(fp_info, sizeof (*fp_info)); 903 return (ret); 904 } 905 906 static void 907 vbe_print_memory(unsigned vmem) 908 { 909 char unit = 'K'; 910 911 vmem /= 1024; 912 if (vmem >= 10240000) { 913 vmem /= 1048576; 914 unit = 'G'; 915 } else if (vmem >= 10000) { 916 vmem /= 1024; 917 unit = 'M'; 918 } 919 printf("Total memory: %u%cB\n", vmem, unit); 920 } 921 922 static void 923 vbe_print_vbe_info(struct vbeinfoblock *vbep) 924 { 925 char *oemstring = ""; 926 char *oemvendor = "", *oemproductname = "", *oemproductrev = ""; 927 928 if (vbep->OemStringPtr != 0) 929 oemstring = vbe_farptr(vbep->OemStringPtr); 930 931 if (vbep->OemVendorNamePtr != 0) 932 oemvendor = vbe_farptr(vbep->OemVendorNamePtr); 933 934 if (vbep->OemProductNamePtr != 0) 935 oemproductname = vbe_farptr(vbep->OemProductNamePtr); 936 937 if (vbep->OemProductRevPtr != 0) 938 oemproductrev = vbe_farptr(vbep->OemProductRevPtr); 939 940 printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8, 941 vbep->VbeVersion & 0xF, oemstring); 942 943 if (vbep->OemSoftwareRev != 0) { 944 printf("OEM Version %d.%d, %s (%s, %s)\n", 945 vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF, 946 oemvendor, oemproductname, oemproductrev); 947 } 948 vbe_print_memory(vbep->TotalMemory << 16); 949 printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages); 950 } 951 952 /* List available modes, filter by depth. If depth is -1, list all. */ 953 void 954 vbe_modelist(int depth) 955 { 956 struct modeinfoblock mi; 957 uint16_t mode; 958 int nmodes, idx, nentries; 959 int ddc_caps; 960 uint32_t width, height; 961 bool edid = false; 962 edid_res_list_t res; 963 struct resolution *rp; 964 965 if (!vbe_check()) 966 return; 967 968 ddc_caps = biosvbe_ddc_caps(); 969 if (ddc_caps & 3) { 970 printf("DDC"); 971 if (ddc_caps & 1) 972 printf(" [DDC1]"); 973 if (ddc_caps & 2) 974 printf(" [DDC2]"); 975 976 TAILQ_INIT(&res); 977 edid = vbe_get_edid(&res); 978 if (edid) { 979 printf(": EDID"); 980 while ((rp = TAILQ_FIRST(&res)) != NULL) { 981 printf(" %dx%d", rp->width, rp->height); 982 TAILQ_REMOVE(&res, rp, next); 983 free(rp); 984 } 985 printf("\n"); 986 } else { 987 printf(": no EDID information\n"); 988 } 989 } 990 if (!edid) 991 if (vbe_get_flatpanel(&width, &height)) 992 printf(": Panel %dx%d\n", width, height); 993 994 nmodes = 0; 995 memset(vbe, 0, sizeof (*vbe)); 996 memcpy(vbe->VbeSignature, "VBE2", 4); 997 if (biosvbe_info(vbe) != VBE_SUCCESS) 998 goto done; 999 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 1000 goto done; 1001 1002 vbe_print_vbe_info(vbe); 1003 printf("Modes: "); 1004 1005 nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); 1006 for (idx = 0; idx < nentries; idx++) { 1007 mode = vbe_mode_list[idx]; 1008 if (mode == 0xffff) 1009 break; 1010 1011 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) 1012 continue; 1013 1014 /* we only care about linear modes here */ 1015 if (vbe_mode_is_supported(&mi) == 0) 1016 continue; 1017 1018 /* apply requested filter */ 1019 if (depth != -1 && mi.BitsPerPixel != depth) 1020 continue; 1021 1022 if (nmodes % 4 == 0) 1023 printf("\n"); 1024 else 1025 printf(" "); 1026 1027 vbe_dump_mode(mode, &mi); 1028 nmodes++; 1029 } 1030 1031 done: 1032 if (nmodes == 0) 1033 printf("none found"); 1034 printf("\n"); 1035 } 1036 1037 static void 1038 vbe_print_mode(bool verbose __unused) 1039 { 1040 int nc, mode, i, rc; 1041 1042 nc = NCOLORS; 1043 1044 memset(vbe, 0, sizeof (*vbe)); 1045 if (biosvbe_info(vbe) != VBE_SUCCESS) 1046 return; 1047 1048 vbe_print_vbe_info(vbe); 1049 1050 if (biosvbe_get_mode(&mode) != VBE_SUCCESS) { 1051 printf("Error getting current VBE mode\n"); 1052 return; 1053 } 1054 1055 if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS || 1056 vbe_mode_is_supported(vbe_mode) == 0) { 1057 printf("VBE mode (0x%x) is not framebuffer mode\n", mode); 1058 return; 1059 } 1060 1061 printf("\nCurrent VBE mode: "); 1062 vbe_dump_mode(mode, vbe_mode); 1063 printf("\n"); 1064 1065 printf("%ux%ux%u, stride=%u\n", 1066 gfx_state.tg_fb.fb_width, 1067 gfx_state.tg_fb.fb_height, 1068 gfx_state.tg_fb.fb_bpp, 1069 gfx_state.tg_fb.fb_stride * 1070 (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY)); 1071 printf(" frame buffer: address=%jx, size=%jx\n", 1072 (uintmax_t)gfx_state.tg_fb.fb_addr, 1073 (uintmax_t)gfx_state.tg_fb.fb_size); 1074 1075 if (vbe_mode->MemoryModel == 0x6) { 1076 printf(" color mask: R=%08x, G=%08x, B=%08x\n", 1077 gfx_state.tg_fb.fb_mask_red, 1078 gfx_state.tg_fb.fb_mask_green, 1079 gfx_state.tg_fb.fb_mask_blue); 1080 pager_open(); 1081 for (i = 0; i < nc; i++) { 1082 printf("%d: R=%02x, G=%02x, B=%02x %08x", i, 1083 (cmap[i] & gfx_state.tg_fb.fb_mask_red) >> 1084 ffs(gfx_state.tg_fb.fb_mask_red) - 1, 1085 (cmap[i] & gfx_state.tg_fb.fb_mask_green) >> 1086 ffs(gfx_state.tg_fb.fb_mask_green) - 1, 1087 (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >> 1088 ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]); 1089 if (pager_output("\n") != 0) 1090 break; 1091 } 1092 pager_close(); 1093 return; 1094 } 1095 1096 mode = 1; /* get DAC palette width */ 1097 rc = biosvbe_palette_format(&mode); 1098 if (rc != VBE_SUCCESS) 1099 return; 1100 1101 printf(" palette format: %x bits per primary\n", mode); 1102 if (pe8 == NULL) 1103 return; 1104 1105 pager_open(); 1106 for (i = 0; i < nc; i++) { 1107 printf("%d: R=%02x, G=%02x, B=%02x", i, 1108 pe8[i].Red, pe8[i].Green, pe8[i].Blue); 1109 if (pager_output("\n") != 0) 1110 break; 1111 } 1112 pager_close(); 1113 } 1114 1115 /* 1116 * Try EDID preferred mode, if EDID or the suggested mode is not available, 1117 * then try flat panel information. 1118 * Fall back to VBE_DEFAULT_MODE. 1119 */ 1120 int 1121 vbe_default_mode(void) 1122 { 1123 edid_res_list_t res; 1124 struct resolution *rp; 1125 int modenum; 1126 uint32_t width, height; 1127 1128 modenum = 0; 1129 vbe_get_max_resolution(&width, &height); 1130 if (width != 0 && height != 0) 1131 modenum = vbe_find_mode_xydm(width, height, -1, -1); 1132 1133 TAILQ_INIT(&res); 1134 if (vbe_get_edid(&res)) { 1135 while ((rp = TAILQ_FIRST(&res)) != NULL) { 1136 if (modenum == 0) { 1137 modenum = vbe_find_mode_xydm( 1138 rp->width, rp->height, -1, -1); 1139 } 1140 TAILQ_REMOVE(&res, rp, next); 1141 free(rp); 1142 } 1143 } 1144 1145 if (modenum == 0 && 1146 vbe_get_flatpanel(&width, &height)) { 1147 modenum = vbe_find_mode_xydm(width, height, -1, -1); 1148 } 1149 1150 /* Still no mode? Fall back to default. */ 1151 if (modenum == 0) 1152 modenum = vbe_find_mode(VBE_DEFAULT_MODE); 1153 return (modenum); 1154 } 1155 1156 COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa); 1157 1158 int 1159 command_vesa(int argc, char *argv[]) 1160 { 1161 char *arg, *cp; 1162 int modenum = -1, n; 1163 1164 if (!vbe_check()) 1165 return (CMD_OK); 1166 1167 if (argc < 2) 1168 goto usage; 1169 1170 if (strcmp(argv[1], "list") == 0) { 1171 n = -1; 1172 if (argc != 2 && argc != 3) 1173 goto usage; 1174 1175 if (argc == 3) { 1176 arg = argv[2]; 1177 errno = 0; 1178 n = strtoul(arg, &cp, 0); 1179 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 1180 snprintf(command_errbuf, 1181 sizeof (command_errbuf), 1182 "depth should be an integer"); 1183 return (CMD_ERROR); 1184 } 1185 } 1186 vbe_modelist(n); 1187 return (CMD_OK); 1188 } 1189 1190 if (strcmp(argv[1], "get") == 0) { 1191 bool verbose = false; 1192 1193 if (argc != 2) { 1194 if (argc > 3 || strcmp(argv[2], "-v") != 0) 1195 goto usage; 1196 verbose = true; 1197 } 1198 vbe_print_mode(verbose); 1199 return (CMD_OK); 1200 } 1201 1202 if (strcmp(argv[1], "off") == 0) { 1203 if (argc != 2) 1204 goto usage; 1205 1206 if (gfx_state.tg_mode == VGA_TEXT_MODE) 1207 return (CMD_OK); 1208 1209 reset_font_flags(); 1210 bios_text_font(true); 1211 bios_set_text_mode(VGA_TEXT_MODE); 1212 cons_update_mode(false); 1213 return (CMD_OK); 1214 } 1215 1216 if (strcmp(argv[1], "on") == 0) { 1217 if (argc != 2) 1218 goto usage; 1219 1220 modenum = vbe_default_mode(); 1221 if (modenum == 0) { 1222 snprintf(command_errbuf, sizeof (command_errbuf), 1223 "%s: no suitable VBE mode number found", argv[0]); 1224 return (CMD_ERROR); 1225 } 1226 } else if (strcmp(argv[1], "set") == 0) { 1227 if (argc != 3) 1228 goto usage; 1229 1230 if (strncmp(argv[2], "0x", 2) == 0) { 1231 arg = argv[2]; 1232 errno = 0; 1233 n = strtoul(arg, &cp, 0); 1234 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 1235 snprintf(command_errbuf, 1236 sizeof (command_errbuf), 1237 "mode should be an integer"); 1238 return (CMD_ERROR); 1239 } 1240 modenum = vbe_find_mode_xydm(0, 0, 0, n); 1241 } else if (strchr(argv[2], 'x') != NULL) { 1242 modenum = vbe_find_mode(argv[2]); 1243 } 1244 } else { 1245 goto usage; 1246 } 1247 1248 if (modenum == 0) { 1249 snprintf(command_errbuf, sizeof (command_errbuf), 1250 "%s: mode %s not supported by firmware\n", 1251 argv[0], argv[2]); 1252 return (CMD_ERROR); 1253 } 1254 1255 if (modenum >= VESA_MODE_BASE) { 1256 if (gfx_state.tg_mode != modenum) { 1257 reset_font_flags(); 1258 bios_text_font(false); 1259 vbe_set_mode(modenum); 1260 cons_update_mode(true); 1261 } 1262 return (CMD_OK); 1263 } else { 1264 snprintf(command_errbuf, sizeof (command_errbuf), 1265 "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]); 1266 return (CMD_ERROR); 1267 } 1268 1269 usage: 1270 snprintf(command_errbuf, sizeof (command_errbuf), 1271 "usage: %s on | off | get | list [depth] | " 1272 "set <display or VBE mode number>", argv[0]); 1273 return (CMD_ERROR); 1274 } 1275