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