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