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