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