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 /* Bytes per pixel */ 713 bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY; 714 715 /* vbe_mode_is_supported() excludes the rest */ 716 switch (mi.MemoryModel) { 717 case 0x4: 718 gfx_state.tg_ctype = CT_INDEXED; 719 break; 720 case 0x6: 721 gfx_state.tg_ctype = CT_RGB; 722 break; 723 } 724 725 #define COLOR_MASK(size, pos) (((1 << size) - 1) << pos) 726 if (gfx_state.tg_ctype == CT_INDEXED) { 727 gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16); 728 gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8); 729 gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0); 730 } else if (vbe->VbeVersion >= 0x300) { 731 gfx_state.tg_fb.fb_mask_red = 732 COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition); 733 gfx_state.tg_fb.fb_mask_green = 734 COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition); 735 gfx_state.tg_fb.fb_mask_blue = 736 COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition); 737 } else { 738 gfx_state.tg_fb.fb_mask_red = 739 COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition); 740 gfx_state.tg_fb.fb_mask_green = 741 COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition); 742 gfx_state.tg_fb.fb_mask_blue = 743 COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition); 744 } 745 gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red | 746 gfx_state.tg_fb.fb_mask_green | 747 gfx_state.tg_fb.fb_mask_blue); 748 749 if (vbe->VbeVersion >= 0x300) 750 gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp; 751 else 752 gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp; 753 754 gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride * 755 bpp; 756 757 return (0); 758 } 759 760 /* 761 * Verify existance of mode number or find mode by 762 * dimensions. If depth is not given, walk values 32, 24, 16, 8. 763 */ 764 static int 765 vbe_find_mode_xydm(int x, int y, int depth, int m) 766 { 767 struct modeinfoblock mi; 768 uint16_t *farptr; 769 uint16_t mode; 770 int idx, nentries, i; 771 772 memset(vbe, 0, sizeof (*vbe)); 773 if (biosvbe_info(vbe) != VBE_SUCCESS) 774 return (0); 775 776 if (m != -1) 777 i = 8; 778 else if (depth == -1) 779 i = 32; 780 else 781 i = depth; 782 783 nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); 784 while (i > 0) { 785 for (idx = 0; idx < nentries; idx++) { 786 mode = vbe_mode_list[idx]; 787 if (mode == 0xffff) 788 break; 789 790 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { 791 continue; 792 } 793 794 /* we only care about linear modes here */ 795 if (vbe_mode_is_supported(&mi) == 0) 796 continue; 797 798 if (m != -1) { 799 if (m == mode) 800 return (mode); 801 else 802 continue; 803 } 804 805 if (mi.XResolution == x && 806 mi.YResolution == y && 807 mi.BitsPerPixel == i) 808 return (mode); 809 } 810 if (depth != -1) 811 break; 812 813 i -= 8; 814 } 815 816 return (0); 817 } 818 819 static int 820 vbe_find_mode(char *str) 821 { 822 int x, y, depth; 823 824 if (!gfx_parse_mode_str(str, &x, &y, &depth)) 825 return (0); 826 827 return (vbe_find_mode_xydm(x, y, depth, -1)); 828 } 829 830 static void 831 vbe_dump_mode(int modenum, struct modeinfoblock *mi) 832 { 833 printf("0x%x=%dx%dx%d", modenum, 834 mi->XResolution, mi->YResolution, mi->BitsPerPixel); 835 } 836 837 static bool 838 vbe_get_edid(edid_res_list_t *res) 839 { 840 struct vesa_edid_info *edidp; 841 const uint8_t magic[] = EDID_MAGIC; 842 int ddc_caps; 843 bool ret = false; 844 845 if (edid_info != NULL) 846 return (gfx_get_edid_resolution(edid_info, res)); 847 848 ddc_caps = biosvbe_ddc_caps(); 849 if (ddc_caps == 0) { 850 return (ret); 851 } 852 853 edidp = bio_alloc(sizeof(*edidp)); 854 if (edidp == NULL) 855 return (ret); 856 memset(edidp, 0, sizeof(*edidp)); 857 858 if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp))) 859 goto done; 860 861 if (memcmp(edidp, magic, sizeof(magic)) != 0) 862 goto done; 863 864 /* Unknown EDID version. */ 865 if (edidp->header.version != 1) 866 goto done; 867 868 ret = gfx_get_edid_resolution(edidp, res); 869 edid_info = malloc(sizeof(*edid_info)); 870 if (edid_info != NULL) 871 memcpy(edid_info, edidp, sizeof (*edid_info)); 872 done: 873 bio_free(edidp, sizeof(*edidp)); 874 return (ret); 875 } 876 877 static bool 878 vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight) 879 { 880 struct vesa_flat_panel_info *fp_info; 881 bool ret = false; 882 883 fp_info = bio_alloc(sizeof (*fp_info)); 884 if (fp_info == NULL) 885 return (ret); 886 memset(fp_info, 0, sizeof (*fp_info)); 887 888 if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info))) 889 goto done; 890 891 *pwidth = fp_info->HSize; 892 *pheight = fp_info->VSize; 893 ret = true; 894 895 done: 896 bio_free(fp_info, sizeof (*fp_info)); 897 return (ret); 898 } 899 900 static void 901 vbe_print_memory(unsigned vmem) 902 { 903 char unit = 'K'; 904 905 vmem /= 1024; 906 if (vmem >= 10240000) { 907 vmem /= 1048576; 908 unit = 'G'; 909 } else if (vmem >= 10000) { 910 vmem /= 1024; 911 unit = 'M'; 912 } 913 printf("Total memory: %u%cB\n", vmem, unit); 914 } 915 916 static void 917 vbe_print_vbe_info(struct vbeinfoblock *vbep) 918 { 919 char *oemstring = ""; 920 char *oemvendor = "", *oemproductname = "", *oemproductrev = ""; 921 922 if (vbep->OemStringPtr != 0) 923 oemstring = vbe_farptr(vbep->OemStringPtr); 924 925 if (vbep->OemVendorNamePtr != 0) 926 oemvendor = vbe_farptr(vbep->OemVendorNamePtr); 927 928 if (vbep->OemProductNamePtr != 0) 929 oemproductname = vbe_farptr(vbep->OemProductNamePtr); 930 931 if (vbep->OemProductRevPtr != 0) 932 oemproductrev = vbe_farptr(vbep->OemProductRevPtr); 933 934 printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8, 935 vbep->VbeVersion & 0xF, oemstring); 936 937 if (vbep->OemSoftwareRev != 0) { 938 printf("OEM Version %d.%d, %s (%s, %s)\n", 939 vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF, 940 oemvendor, oemproductname, oemproductrev); 941 } 942 vbe_print_memory(vbep->TotalMemory << 16); 943 printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages); 944 } 945 946 /* List available modes, filter by depth. If depth is -1, list all. */ 947 void 948 vbe_modelist(int depth) 949 { 950 struct modeinfoblock mi; 951 uint16_t mode; 952 int nmodes, idx, nentries; 953 int ddc_caps; 954 uint32_t width, height; 955 bool edid = false; 956 edid_res_list_t res; 957 struct resolution *rp; 958 959 if (!vbe_check()) 960 return; 961 962 ddc_caps = biosvbe_ddc_caps(); 963 if (ddc_caps & 3) { 964 printf("DDC"); 965 if (ddc_caps & 1) 966 printf(" [DDC1]"); 967 if (ddc_caps & 2) 968 printf(" [DDC2]"); 969 970 TAILQ_INIT(&res); 971 edid = vbe_get_edid(&res); 972 if (edid) { 973 printf(": EDID"); 974 while ((rp = TAILQ_FIRST(&res)) != NULL) { 975 printf(" %dx%d", rp->width, rp->height); 976 TAILQ_REMOVE(&res, rp, next); 977 free(rp); 978 } 979 printf("\n"); 980 } else { 981 printf(": no EDID information\n"); 982 } 983 } 984 if (!edid) 985 if (vbe_get_flatpanel(&width, &height)) 986 printf(": Panel %dx%d\n", width, height); 987 988 nmodes = 0; 989 memset(vbe, 0, sizeof (*vbe)); 990 memcpy(vbe->VbeSignature, "VBE2", 4); 991 if (biosvbe_info(vbe) != VBE_SUCCESS) 992 goto done; 993 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 994 goto done; 995 996 vbe_print_vbe_info(vbe); 997 printf("Modes: "); 998 999 nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); 1000 for (idx = 0; idx < nentries; idx++) { 1001 mode = vbe_mode_list[idx]; 1002 if (mode == 0xffff) 1003 break; 1004 1005 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) 1006 continue; 1007 1008 /* we only care about linear modes here */ 1009 if (vbe_mode_is_supported(&mi) == 0) 1010 continue; 1011 1012 /* apply requested filter */ 1013 if (depth != -1 && mi.BitsPerPixel != depth) 1014 continue; 1015 1016 if (nmodes % 4 == 0) 1017 printf("\n"); 1018 else 1019 printf(" "); 1020 1021 vbe_dump_mode(mode, &mi); 1022 nmodes++; 1023 } 1024 1025 done: 1026 if (nmodes == 0) 1027 printf("none found"); 1028 printf("\n"); 1029 } 1030 1031 static void 1032 vbe_print_mode(bool verbose __unused) 1033 { 1034 int nc, mode, i, rc; 1035 1036 nc = NCOLORS; 1037 1038 memset(vbe, 0, sizeof (*vbe)); 1039 if (biosvbe_info(vbe) != VBE_SUCCESS) 1040 return; 1041 1042 vbe_print_vbe_info(vbe); 1043 1044 if (biosvbe_get_mode(&mode) != VBE_SUCCESS) { 1045 printf("Error getting current VBE mode\n"); 1046 return; 1047 } 1048 1049 if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS || 1050 vbe_mode_is_supported(vbe_mode) == 0) { 1051 printf("VBE mode (0x%x) is not framebuffer mode\n", mode); 1052 return; 1053 } 1054 1055 printf("\nCurrent VBE mode: "); 1056 vbe_dump_mode(mode, vbe_mode); 1057 printf("\n"); 1058 1059 printf("%ux%ux%u, stride=%u\n", 1060 gfx_state.tg_fb.fb_width, 1061 gfx_state.tg_fb.fb_height, 1062 gfx_state.tg_fb.fb_bpp, 1063 gfx_state.tg_fb.fb_stride * 1064 (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY)); 1065 printf(" frame buffer: address=%jx, size=%jx\n", 1066 (uintmax_t)gfx_state.tg_fb.fb_addr, 1067 (uintmax_t)gfx_state.tg_fb.fb_size); 1068 1069 if (vbe_mode->MemoryModel == 0x6) { 1070 printf(" color mask: R=%08x, G=%08x, B=%08x\n", 1071 gfx_state.tg_fb.fb_mask_red, 1072 gfx_state.tg_fb.fb_mask_green, 1073 gfx_state.tg_fb.fb_mask_blue); 1074 pager_open(); 1075 for (i = 0; i < nc; i++) { 1076 printf("%d: R=%02x, G=%02x, B=%02x %08x", i, 1077 (cmap[i] & gfx_state.tg_fb.fb_mask_red) >> 1078 ffs(gfx_state.tg_fb.fb_mask_red) - 1, 1079 (cmap[i] & gfx_state.tg_fb.fb_mask_green) >> 1080 ffs(gfx_state.tg_fb.fb_mask_green) - 1, 1081 (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >> 1082 ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]); 1083 if (pager_output("\n") != 0) 1084 break; 1085 } 1086 pager_close(); 1087 return; 1088 } 1089 1090 mode = 1; /* get DAC palette width */ 1091 rc = biosvbe_palette_format(&mode); 1092 if (rc != VBE_SUCCESS) 1093 return; 1094 1095 printf(" palette format: %x bits per primary\n", mode); 1096 if (pe8 == NULL) 1097 return; 1098 1099 pager_open(); 1100 for (i = 0; i < nc; i++) { 1101 printf("%d: R=%02x, G=%02x, B=%02x", i, 1102 pe8[i].Red, pe8[i].Green, pe8[i].Blue); 1103 if (pager_output("\n") != 0) 1104 break; 1105 } 1106 pager_close(); 1107 } 1108 1109 /* 1110 * Try EDID preferred mode, if EDID or the suggested mode is not available, 1111 * then try flat panel information. 1112 * Fall back to VBE_DEFAULT_MODE. 1113 */ 1114 int 1115 vbe_default_mode(void) 1116 { 1117 edid_res_list_t res; 1118 struct resolution *rp; 1119 int modenum; 1120 uint32_t width, height; 1121 1122 modenum = 0; 1123 vbe_get_max_resolution(&width, &height); 1124 if (width != 0 && height != 0) 1125 modenum = vbe_find_mode_xydm(width, height, -1, -1); 1126 1127 TAILQ_INIT(&res); 1128 if (vbe_get_edid(&res)) { 1129 while ((rp = TAILQ_FIRST(&res)) != NULL) { 1130 if (modenum == 0) { 1131 modenum = vbe_find_mode_xydm( 1132 rp->width, rp->height, -1, -1); 1133 } 1134 TAILQ_REMOVE(&res, rp, next); 1135 free(rp); 1136 } 1137 } 1138 1139 if (modenum == 0 && 1140 vbe_get_flatpanel(&width, &height)) { 1141 modenum = vbe_find_mode_xydm(width, height, -1, -1); 1142 } 1143 1144 /* Still no mode? Fall back to default. */ 1145 if (modenum == 0) 1146 modenum = vbe_find_mode(VBE_DEFAULT_MODE); 1147 return (modenum); 1148 } 1149 1150 COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa); 1151 1152 int 1153 command_vesa(int argc, char *argv[]) 1154 { 1155 char *arg, *cp; 1156 int modenum = -1, n; 1157 1158 if (!vbe_check()) 1159 return (CMD_OK); 1160 1161 if (argc < 2) 1162 goto usage; 1163 1164 if (strcmp(argv[1], "list") == 0) { 1165 n = -1; 1166 if (argc != 2 && argc != 3) 1167 goto usage; 1168 1169 if (argc == 3) { 1170 arg = argv[2]; 1171 errno = 0; 1172 n = strtoul(arg, &cp, 0); 1173 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 1174 snprintf(command_errbuf, 1175 sizeof (command_errbuf), 1176 "depth should be an integer"); 1177 return (CMD_ERROR); 1178 } 1179 } 1180 vbe_modelist(n); 1181 return (CMD_OK); 1182 } 1183 1184 if (strcmp(argv[1], "get") == 0) { 1185 bool verbose = false; 1186 1187 if (argc != 2) { 1188 if (argc > 3 || strcmp(argv[2], "-v") != 0) 1189 goto usage; 1190 verbose = true; 1191 } 1192 vbe_print_mode(verbose); 1193 return (CMD_OK); 1194 } 1195 1196 if (strcmp(argv[1], "off") == 0) { 1197 if (argc != 2) 1198 goto usage; 1199 1200 if (gfx_state.tg_mode == VGA_TEXT_MODE) 1201 return (CMD_OK); 1202 1203 reset_font_flags(); 1204 bios_text_font(true); 1205 bios_set_text_mode(VGA_TEXT_MODE); 1206 cons_update_mode(false); 1207 return (CMD_OK); 1208 } 1209 1210 if (strcmp(argv[1], "on") == 0) { 1211 if (argc != 2) 1212 goto usage; 1213 1214 modenum = vbe_default_mode(); 1215 if (modenum == 0) { 1216 snprintf(command_errbuf, sizeof (command_errbuf), 1217 "%s: no suitable VBE mode number found", argv[0]); 1218 return (CMD_ERROR); 1219 } 1220 } else if (strcmp(argv[1], "set") == 0) { 1221 if (argc != 3) 1222 goto usage; 1223 1224 if (strncmp(argv[2], "0x", 2) == 0) { 1225 arg = argv[2]; 1226 errno = 0; 1227 n = strtoul(arg, &cp, 0); 1228 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 1229 snprintf(command_errbuf, 1230 sizeof (command_errbuf), 1231 "mode should be an integer"); 1232 return (CMD_ERROR); 1233 } 1234 modenum = vbe_find_mode_xydm(0, 0, 0, n); 1235 } else if (strchr(argv[2], 'x') != NULL) { 1236 modenum = vbe_find_mode(argv[2]); 1237 } 1238 } else { 1239 goto usage; 1240 } 1241 1242 if (modenum == 0) { 1243 snprintf(command_errbuf, sizeof (command_errbuf), 1244 "%s: mode %s not supported by firmware\n", 1245 argv[0], argv[2]); 1246 return (CMD_ERROR); 1247 } 1248 1249 if (modenum >= VESA_MODE_BASE) { 1250 if (gfx_state.tg_mode != modenum) { 1251 reset_font_flags(); 1252 bios_text_font(false); 1253 vbe_set_mode(modenum); 1254 cons_update_mode(true); 1255 } 1256 return (CMD_OK); 1257 } else { 1258 snprintf(command_errbuf, sizeof (command_errbuf), 1259 "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]); 1260 return (CMD_ERROR); 1261 } 1262 1263 usage: 1264 snprintf(command_errbuf, sizeof (command_errbuf), 1265 "usage: %s on | off | get | list [depth] | " 1266 "set <display or VBE mode number>", argv[0]); 1267 return (CMD_ERROR); 1268 } 1269