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