1 /* 2 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * VESA BIOS Extensions routines 29 */ 30 31 #include <stand.h> 32 #include <stdbool.h> 33 #include <bootstrap.h> 34 #include <machine/bootinfo.h> 35 #include <machine/metadata.h> 36 #include <sys/multiboot2.h> 37 #include <btxv86.h> 38 #include "libi386.h" 39 #include "gfx_fb.h" /* for EDID */ 40 #include "vbe.h" 41 #include <sys/font.h> 42 #include <sys/vgareg.h> 43 #include <sys/vgasubr.h> 44 45 multiboot_tag_vbe_t vbestate; 46 static struct vbeinfoblock *vbe = 47 (struct vbeinfoblock *)&vbestate.vbe_control_info; 48 static struct modeinfoblock *vbe_mode = 49 (struct modeinfoblock *)&vbestate.vbe_mode_info; 50 multiboot_color_t *cmap; 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 /* Actually assuming mode 3. */ 59 void 60 bios_set_text_mode(int mode) 61 { 62 int atr; 63 64 if (vbe->Capabilities & VBE_CAP_DAC8) { 65 int m; 66 67 /* 68 * The mode change should reset the palette format to 69 * 6 bits, but apparently some systems do fail with 8-bit 70 * palette, so we switch to 6-bit here. 71 */ 72 m = 0x0600; 73 (void) biosvbe_palette_format(&m); 74 palette_format = m; 75 } 76 v86.ctl = V86_FLAGS; 77 v86.addr = 0x10; 78 v86.eax = mode; /* set VGA text mode */ 79 v86int(); 80 atr = vga_get_atr(VGA_REG_ADDR, VGA_ATR_MODE); 81 atr &= ~VGA_ATR_MODE_BLINK; 82 atr &= ~VGA_ATR_MODE_9WIDE; 83 vga_set_atr(VGA_REG_ADDR, VGA_ATR_MODE, atr); 84 85 vbestate.vbe_mode = 0; /* vbe is disabled */ 86 gfx_fb.framebuffer_common.framebuffer_type = 87 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT; 88 /* 16 bits per character */ 89 gfx_fb.framebuffer_common.framebuffer_bpp = 16; 90 gfx_fb.framebuffer_common.framebuffer_addr = 91 VGA_MEM_ADDR + VGA_COLOR_BASE; 92 gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS; 93 gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS; 94 gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2; 95 } 96 97 /* Function 00h - Return VBE Controller Information */ 98 static int 99 biosvbe_info(struct vbeinfoblock *vbe) 100 { 101 v86.ctl = V86_FLAGS; 102 v86.addr = 0x10; 103 v86.eax = 0x4f00; 104 v86.es = VTOPSEG(vbe); 105 v86.edi = VTOPOFF(vbe); 106 v86int(); 107 return (v86.eax & 0xffff); 108 } 109 110 /* Function 01h - Return VBE Mode Information */ 111 static int 112 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi) 113 { 114 v86.ctl = V86_FLAGS; 115 v86.addr = 0x10; 116 v86.eax = 0x4f01; 117 v86.ecx = mode; 118 v86.es = VTOPSEG(mi); 119 v86.edi = VTOPOFF(mi); 120 v86int(); 121 return (v86.eax & 0xffff); 122 } 123 124 /* Function 02h - Set VBE Mode */ 125 static int 126 biosvbe_set_mode(int mode, struct crtciinfoblock *ci) 127 { 128 int rv; 129 130 if (vbe->Capabilities & VBE_CAP_DAC8) { 131 int m; 132 133 /* 134 * The mode change should reset the palette format to 135 * 6 bits, but apparently some systems do fail with 8-bit 136 * palette, so we switch to 6-bit here. 137 */ 138 m = 0x0600; 139 if (biosvbe_palette_format(&m) == VBE_SUCCESS) 140 palette_format = m; 141 } 142 v86.ctl = V86_FLAGS; 143 v86.addr = 0x10; 144 v86.eax = 0x4f02; 145 v86.ebx = mode | 0x4000; /* set linear FB bit */ 146 v86.es = VTOPSEG(ci); 147 v86.edi = VTOPOFF(ci); 148 v86int(); 149 rv = v86.eax & 0xffff; 150 if (vbe->Capabilities & VBE_CAP_DAC8) { 151 int m; 152 153 /* Switch to 8-bits per primary color. */ 154 m = 0x0800; 155 if (biosvbe_palette_format(&m) == VBE_SUCCESS) 156 palette_format = m; 157 } 158 return (rv); 159 } 160 161 /* Function 03h - Get VBE Mode */ 162 static int 163 biosvbe_get_mode(int *mode) 164 { 165 v86.ctl = V86_FLAGS; 166 v86.addr = 0x10; 167 v86.eax = 0x4f03; 168 v86int(); 169 *mode = v86.ebx & 0x3fff; /* Bits 0-13 */ 170 return (v86.eax & 0xffff); 171 } 172 173 /* Function 08h - Set/Get DAC Palette Format */ 174 int 175 biosvbe_palette_format(int *format) 176 { 177 v86.ctl = V86_FLAGS; 178 v86.addr = 0x10; 179 v86.eax = 0x4f08; 180 v86.ebx = *format; 181 v86int(); 182 *format = (v86.ebx >> 8) & 0xff; 183 return (v86.eax & 0xffff); 184 } 185 186 /* Function 09h - Set/Get Palette Data */ 187 static int 188 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe) 189 { 190 v86.ctl = V86_FLAGS; 191 v86.addr = 0x10; 192 v86.eax = 0x4f09; 193 v86.ebx = mode; 194 v86.edx = reg; 195 v86.ecx = 1; 196 v86.es = VTOPSEG(pe); 197 v86.edi = VTOPOFF(pe); 198 v86int(); 199 return (v86.eax & 0xffff); 200 } 201 202 /* 203 * Function 15h BL=00h - Report VBE/DDC Capabilities 204 * 205 * int biosvbe_ddc_caps(void) 206 * return: VBE/DDC capabilities 207 */ 208 static int 209 biosvbe_ddc_caps(void) 210 { 211 v86.ctl = V86_FLAGS; 212 v86.addr = 0x10; 213 v86.eax = 0x4f15; /* display identification extensions */ 214 v86.ebx = 0; /* report DDC capabilities */ 215 v86.ecx = 0; /* controller unit number (00h = primary) */ 216 v86.es = 0; 217 v86.edi = 0; 218 v86int(); 219 if (VBE_ERROR(v86.eax & 0xffff)) 220 return (0); 221 return (v86.ebx & 0xffff); 222 } 223 224 /* Function 11h BL=01h - Flat Panel status */ 225 static int 226 biosvbe_ddc_read_flat_panel_info(void *buf) 227 { 228 v86.ctl = V86_FLAGS; 229 v86.addr = 0x10; 230 v86.eax = 0x4f11; /* Flat Panel Interface extensions */ 231 v86.ebx = 1; /* Return Flat Panel Information */ 232 v86.es = VTOPSEG(buf); 233 v86.edi = VTOPOFF(buf); 234 v86int(); 235 return (v86.eax & 0xffff); 236 } 237 238 /* Function 15h BL=01h - Read EDID */ 239 static int 240 biosvbe_ddc_read_edid(int blockno, void *buf) 241 { 242 v86.ctl = V86_FLAGS; 243 v86.addr = 0x10; 244 v86.eax = 0x4f15; /* display identification extensions */ 245 v86.ebx = 1; /* read EDID */ 246 v86.ecx = 0; /* controller unit number (00h = primary) */ 247 v86.edx = blockno; 248 v86.es = VTOPSEG(buf); 249 v86.edi = VTOPOFF(buf); 250 v86int(); 251 return (v86.eax & 0xffff); 252 } 253 254 static int 255 vbe_mode_is_supported(struct modeinfoblock *mi) 256 { 257 if ((mi->ModeAttributes & 0x01) == 0) 258 return (0); /* mode not supported by hardware */ 259 if ((mi->ModeAttributes & 0x08) == 0) 260 return (0); /* linear fb not available */ 261 if ((mi->ModeAttributes & 0x10) == 0) 262 return (0); /* text mode */ 263 if (mi->NumberOfPlanes != 1) 264 return (0); /* planar mode not supported */ 265 if (mi->MemoryModel != 0x04 /* Packed pixel */ && 266 mi->MemoryModel != 0x06 /* Direct Color */) 267 return (0); /* unsupported pixel format */ 268 return (1); 269 } 270 271 static int 272 vbe_check(void) 273 { 274 if (vbestate.mb_type != MULTIBOOT_TAG_TYPE_VBE) { 275 printf("VBE not available\n"); 276 return (0); 277 } 278 return (1); 279 } 280 281 void 282 vbe_init(void) 283 { 284 /* First set FB for text mode. */ 285 gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; 286 gfx_fb.framebuffer_common.framebuffer_type = 287 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT; 288 /* 16 bits per character */ 289 gfx_fb.framebuffer_common.framebuffer_bpp = 16; 290 gfx_fb.framebuffer_common.framebuffer_addr = 291 VGA_MEM_ADDR + VGA_COLOR_BASE; 292 gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS; 293 gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS; 294 gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2; 295 296 /* Now check if we have vesa. */ 297 memset(vbe, 0, sizeof (*vbe)); 298 memcpy(vbe->VbeSignature, "VBE2", 4); 299 if (biosvbe_info(vbe) != VBE_SUCCESS) 300 return; 301 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 302 return; 303 304 vbestate.mb_type = MULTIBOOT_TAG_TYPE_VBE; 305 vbestate.mb_size = sizeof (vbestate); 306 vbestate.vbe_mode = 0; 307 /* vbe_set_mode() will set up the rest. */ 308 } 309 310 int 311 vbe_available(void) 312 { 313 return (vbestate.mb_type); 314 } 315 316 int 317 vbe_set_palette(const struct paletteentry *entry, size_t slot) 318 { 319 struct paletteentry pe; 320 int mode, ret; 321 322 if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0) 323 return (1); 324 325 if (gfx_fb.framebuffer_common.framebuffer_type != 326 MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) { 327 return (1); 328 } 329 330 if (cmap == NULL) 331 cmap = calloc(CMAP_SIZE, sizeof (*cmap)); 332 333 pe.Blue = entry->Blue; 334 pe.Green = entry->Green; 335 pe.Red = entry->Red; 336 pe.Reserved = entry->Reserved; 337 338 if (vbe->Capabilities & VBE_CAP_SNOW) 339 mode = 0x80; 340 else 341 mode = 0; 342 343 ret = biosvbe_palette_data(mode, slot, &pe); 344 if (cmap != NULL && slot < CMAP_SIZE) { 345 cmap[slot].mb_red = entry->Red; 346 cmap[slot].mb_green = entry->Green; 347 cmap[slot].mb_blue = entry->Blue; 348 } 349 350 return (ret == VBE_SUCCESS ? 0 : 1); 351 } 352 353 int 354 vbe_get_mode(void) 355 { 356 return (vbestate.vbe_mode); 357 } 358 359 int 360 vbe_set_mode(int modenum) 361 { 362 struct modeinfoblock mi; 363 int ret; 364 365 if (!vbe_check()) 366 return (1); 367 368 ret = biosvbe_get_mode_info(modenum, &mi); 369 if (VBE_ERROR(ret)) { 370 printf("mode 0x%x invalid\n", modenum); 371 return (1); 372 } 373 374 if (!vbe_mode_is_supported(&mi)) { 375 printf("mode 0x%x not supported\n", modenum); 376 return (1); 377 } 378 379 /* calculate bytes per pixel */ 380 switch (mi.BitsPerPixel) { 381 case 32: 382 case 24: 383 case 16: 384 case 15: 385 case 8: 386 break; 387 default: 388 printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel); 389 return (1); 390 } 391 392 ret = biosvbe_set_mode(modenum, NULL); 393 if (VBE_ERROR(ret)) { 394 printf("mode 0x%x could not be set\n", modenum); 395 return (1); 396 } 397 398 /* make sure we have current MI in vbestate */ 399 memcpy(vbe_mode, &mi, sizeof (*vbe_mode)); 400 vbestate.vbe_mode = modenum; 401 402 gfx_fb.framebuffer_common.framebuffer_addr = 403 (uint64_t)mi.PhysBasePtr & 0xffffffff; 404 gfx_fb.framebuffer_common.framebuffer_width = mi.XResolution; 405 gfx_fb.framebuffer_common.framebuffer_height = mi.YResolution; 406 gfx_fb.framebuffer_common.framebuffer_bpp = mi.BitsPerPixel; 407 408 /* vbe_mode_is_supported() excludes the rest */ 409 switch (mi.MemoryModel) { 410 case 0x4: 411 gfx_fb.framebuffer_common.framebuffer_type = 412 MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED; 413 break; 414 case 0x6: 415 gfx_fb.framebuffer_common.framebuffer_type = 416 MULTIBOOT_FRAMEBUFFER_TYPE_RGB; 417 break; 418 } 419 420 if (vbe->VbeVersion >= 0x300) { 421 gfx_fb.framebuffer_common.framebuffer_pitch = 422 mi.LinBytesPerScanLine; 423 gfx_fb.u.fb2.framebuffer_red_field_position = 424 mi.LinRedFieldPosition; 425 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.LinRedMaskSize; 426 gfx_fb.u.fb2.framebuffer_green_field_position = 427 mi.LinGreenFieldPosition; 428 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.LinGreenMaskSize; 429 gfx_fb.u.fb2.framebuffer_blue_field_position = 430 mi.LinBlueFieldPosition; 431 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.LinBlueMaskSize; 432 } else { 433 gfx_fb.framebuffer_common.framebuffer_pitch = 434 mi.BytesPerScanLine; 435 gfx_fb.u.fb2.framebuffer_red_field_position = 436 mi.RedFieldPosition; 437 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.RedMaskSize; 438 gfx_fb.u.fb2.framebuffer_green_field_position = 439 mi.GreenFieldPosition; 440 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.GreenMaskSize; 441 gfx_fb.u.fb2.framebuffer_blue_field_position = 442 mi.BlueFieldPosition; 443 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.BlueMaskSize; 444 } 445 446 return (0); 447 } 448 449 static void * 450 vbe_farptr(uint32_t farptr) 451 { 452 return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)))); 453 } 454 455 /* 456 * Verify existance of mode number or find mode by 457 * dimensions. If depth is not given, walk values 32, 24, 16, 8. 458 */ 459 static int 460 vbe_find_mode_xydm(int x, int y, int depth, int m) 461 { 462 struct modeinfoblock mi; 463 uint32_t farptr; 464 uint16_t mode; 465 int safety, i; 466 467 memset(vbe, 0, sizeof (vbe)); 468 memcpy(vbe->VbeSignature, "VBE2", 4); 469 if (biosvbe_info(vbe) != VBE_SUCCESS) 470 return (0); 471 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 472 return (0); 473 if (vbe->VideoModePtr == 0) 474 return (0); 475 476 if (m != -1) 477 i = 8; 478 else if (depth == -1) 479 i = 32; 480 else 481 i = depth; 482 483 while (i > 0) { 484 farptr = vbe->VideoModePtr; 485 safety = 0; 486 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 487 safety++; 488 farptr += 2; 489 if (safety == VESA_MODE_COUNT) 490 return (0); 491 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { 492 continue; 493 } 494 /* we only care about linear modes here */ 495 if (vbe_mode_is_supported(&mi) == 0) 496 continue; 497 498 if (m != -1) { 499 if (m == mode) 500 return (mode); 501 else 502 continue; 503 } 504 505 if (mi.XResolution == x && 506 mi.YResolution == y && 507 mi.BitsPerPixel == i) 508 return (mode); 509 } 510 if (depth != -1) 511 break; 512 513 i -= 8; 514 } 515 516 return (0); 517 } 518 519 static int 520 vbe_find_mode(char *str) 521 { 522 int x, y, depth; 523 524 if (!gfx_parse_mode_str(str, &x, &y, &depth)) 525 return (0); 526 527 return (vbe_find_mode_xydm(x, y, depth, -1)); 528 } 529 530 static void 531 vbe_dump_mode(int modenum, struct modeinfoblock *mi) 532 { 533 printf("0x%x=%dx%dx%d", modenum, 534 mi->XResolution, mi->YResolution, mi->BitsPerPixel); 535 } 536 537 static bool 538 vbe_get_edid(edid_res_list_t *res) 539 { 540 struct vesa_edid_info *edid_info; 541 const uint8_t magic[] = EDID_MAGIC; 542 int ddc_caps; 543 bool ret = false; 544 545 ddc_caps = biosvbe_ddc_caps(); 546 if (ddc_caps == 0) { 547 return (ret); 548 } 549 550 edid_info = bio_alloc(sizeof (*edid_info)); 551 if (edid_info == NULL) 552 return (ret); 553 memset(edid_info, 0, sizeof (*edid_info)); 554 555 if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info))) 556 goto done; 557 558 if (memcmp(edid_info, magic, sizeof (magic)) != 0) 559 goto done; 560 561 /* Unknown EDID version. */ 562 if (edid_info->header.version != 1) 563 goto done; 564 565 ret = gfx_get_edid_resolution(edid_info, res); 566 done: 567 bio_free(edid_info, sizeof (*edid_info)); 568 return (ret); 569 } 570 571 static bool 572 vbe_get_flatpanel(uint_t *pwidth, uint_t *pheight) 573 { 574 struct flatpanelinfo *fp_info; 575 bool ret = false; 576 577 fp_info = bio_alloc(sizeof (*fp_info)); 578 if (fp_info == NULL) 579 return (ret); 580 memset(fp_info, 0, sizeof (*fp_info)); 581 582 if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info))) 583 goto done; 584 585 *pwidth = fp_info->HorizontalSize; 586 *pheight = fp_info->VerticalSize; 587 ret = true; 588 589 done: 590 bio_free(fp_info, sizeof (*fp_info)); 591 return (ret); 592 } 593 594 static void 595 vbe_print_vbe_info(struct vbeinfoblock *vbep) 596 { 597 char *oemstring = ""; 598 char *oemvendor = "", *oemproductname = "", *oemproductrev = ""; 599 600 if (vbep->OemStringPtr != 0) 601 oemstring = vbe_farptr(vbep->OemStringPtr); 602 603 if (vbep->OemVendorNamePtr != 0) 604 oemvendor = vbe_farptr(vbep->OemVendorNamePtr); 605 606 if (vbep->OemProductNamePtr != 0) 607 oemproductname = vbe_farptr(vbep->OemProductNamePtr); 608 609 if (vbep->OemProductRevPtr != 0) 610 oemproductrev = vbe_farptr(vbep->OemProductRevPtr); 611 612 printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8, 613 vbep->VbeVersion & 0xF, oemstring); 614 615 if (vbep->OemSoftwareRev != 0) { 616 printf("OEM Version %d.%d, %s (%s, %s)\n", 617 vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF, 618 oemvendor, oemproductname, oemproductrev); 619 } 620 } 621 622 /* List available modes, filter by depth. If depth is -1, list all. */ 623 void 624 vbe_modelist(int depth) 625 { 626 struct modeinfoblock mi; 627 uint32_t farptr; 628 uint16_t mode; 629 int nmodes = 0, safety = 0; 630 int ddc_caps; 631 uint_t width, height; 632 bool edid = false; 633 edid_res_list_t res; 634 struct resolution *rp; 635 636 if (!vbe_check()) 637 return; 638 639 ddc_caps = biosvbe_ddc_caps(); 640 if (ddc_caps & 3) { 641 printf("DDC"); 642 if (ddc_caps & 1) 643 printf(" [DDC1]"); 644 if (ddc_caps & 2) 645 printf(" [DDC2]"); 646 647 TAILQ_INIT(&res); 648 edid = vbe_get_edid(&res); 649 if (edid) { 650 printf(": EDID"); 651 while ((rp = TAILQ_FIRST(&res)) != NULL) { 652 printf(" %dx%d", rp->width, rp->height); 653 TAILQ_REMOVE(&res, rp, next); 654 free(rp); 655 } 656 printf("\n"); 657 } else { 658 printf(": no EDID information\n"); 659 } 660 } 661 if (!edid) 662 if (vbe_get_flatpanel(&width, &height)) 663 printf(": Panel %dx%d\n", width, height); 664 665 memset(vbe, 0, sizeof (vbe)); 666 memcpy(vbe->VbeSignature, "VBE2", 4); 667 if (biosvbe_info(vbe) != VBE_SUCCESS) 668 goto done; 669 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 670 goto done; 671 672 vbe_print_vbe_info(vbe); 673 printf("Modes: "); 674 675 farptr = vbe->VideoModePtr; 676 if (farptr == 0) 677 goto done; 678 679 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 680 safety++; 681 farptr += 2; 682 if (safety == VESA_MODE_COUNT) { 683 printf("[?] "); 684 break; 685 } 686 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) 687 continue; 688 /* we only care about linear modes here */ 689 if (vbe_mode_is_supported(&mi) == 0) 690 continue; 691 692 /* we found some mode so reset safety counter */ 693 safety = 0; 694 695 /* apply requested filter */ 696 if (depth != -1 && mi.BitsPerPixel != depth) 697 continue; 698 699 if (nmodes % 4 == 0) 700 printf("\n"); 701 else 702 printf(" "); 703 704 vbe_dump_mode(mode, &mi); 705 nmodes++; 706 } 707 708 done: 709 if (nmodes == 0) 710 printf("none found"); 711 printf("\n"); 712 } 713 714 static void 715 vbe_print_mode(bool verbose) 716 { 717 int nc, mode, i, rc; 718 719 if (verbose) 720 nc = 256; 721 else 722 nc = 16; 723 724 memset(vbe, 0, sizeof (vbe)); 725 memcpy(vbe->VbeSignature, "VBE2", 4); 726 if (biosvbe_info(vbe) != VBE_SUCCESS) 727 return; 728 729 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) 730 return; 731 732 vbe_print_vbe_info(vbe); 733 734 if (biosvbe_get_mode(&mode) != VBE_SUCCESS) { 735 printf("Error getting current VBE mode\n"); 736 return; 737 } 738 739 if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS || 740 vbe_mode_is_supported(vbe_mode) == 0) { 741 printf("VBE mode (0x%x) is not framebuffer mode\n", mode); 742 return; 743 } 744 745 printf("\nCurrent VBE mode: "); 746 vbe_dump_mode(mode, vbe_mode); 747 printf("\n"); 748 749 printf("%ux%ux%u, stride=%u\n", 750 gfx_fb.framebuffer_common.framebuffer_width, 751 gfx_fb.framebuffer_common.framebuffer_height, 752 gfx_fb.framebuffer_common.framebuffer_bpp, 753 (gfx_fb.framebuffer_common.framebuffer_pitch << 3) / 754 gfx_fb.framebuffer_common.framebuffer_bpp); 755 printf(" frame buffer: address=%jx, size=%jx\n", 756 (uintmax_t)gfx_fb.framebuffer_common.framebuffer_addr, 757 (uintmax_t)gfx_fb.framebuffer_common.framebuffer_height * 758 gfx_fb.framebuffer_common.framebuffer_pitch); 759 760 if (vbe_mode->MemoryModel == 0x6) { 761 printf(" color mask: R=%08x, G=%08x, B=%08x\n", 762 ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1) << 763 gfx_fb.u.fb2.framebuffer_red_field_position, 764 ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1) << 765 gfx_fb.u.fb2.framebuffer_green_field_position, 766 ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1) << 767 gfx_fb.u.fb2.framebuffer_blue_field_position); 768 return; 769 } 770 771 mode = 1; /* get DAC palette width */ 772 rc = biosvbe_palette_format(&mode); 773 if (rc != VBE_SUCCESS) 774 return; 775 776 printf(" palette format: %x bits per primary\n", mode); 777 if (cmap == NULL) 778 return; 779 780 pager_open(); 781 for (i = 0; i < nc; i++) { 782 printf("%d: R=%02x, G=%02x, B=%02x", i, 783 cmap[i].mb_red, cmap[i].mb_green, cmap[i].mb_blue); 784 if (pager_output("\n") != 0) 785 break; 786 } 787 pager_close(); 788 } 789 790 /* 791 * Try EDID preferred mode, if EDID or the suggested mode is not available, 792 * then try flat panel information. 793 * Fall back to VBE_DEFAULT_MODE. 794 */ 795 int 796 vbe_default_mode(void) 797 { 798 edid_res_list_t res; 799 struct resolution *rp; 800 int modenum; 801 uint_t width, height; 802 803 modenum = 0; 804 TAILQ_INIT(&res); 805 if (vbe_get_edid(&res)) { 806 while ((rp = TAILQ_FIRST(&res)) != NULL) { 807 if (modenum == 0) { 808 modenum = vbe_find_mode_xydm( 809 rp->width, rp->height, -1, -1); 810 } 811 TAILQ_REMOVE(&res, rp, next); 812 free(rp); 813 } 814 } 815 816 if (modenum == 0 && 817 vbe_get_flatpanel(&width, &height)) { 818 modenum = vbe_find_mode_xydm(width, height, -1, -1); 819 } 820 821 /* Still no mode? Fall back to default. */ 822 if (modenum == 0) 823 modenum = vbe_find_mode(VBE_DEFAULT_MODE); 824 return (modenum); 825 } 826 827 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management", 828 command_vesa); 829 830 int 831 command_vesa(int argc, char *argv[]) 832 { 833 char *arg, *cp; 834 int modenum = -1, n; 835 836 if (!vbe_check()) 837 return (CMD_OK); 838 839 if (argc < 2) 840 goto usage; 841 842 if (strcmp(argv[1], "list") == 0) { 843 n = -1; 844 if (argc != 2 && argc != 3) 845 goto usage; 846 847 if (argc == 3) { 848 arg = argv[2]; 849 errno = 0; 850 n = strtoul(arg, &cp, 0); 851 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 852 snprintf(command_errbuf, 853 sizeof (command_errbuf), 854 "depth should be an integer"); 855 return (CMD_ERROR); 856 } 857 } 858 vbe_modelist(n); 859 return (CMD_OK); 860 } 861 862 if (strcmp(argv[1], "get") == 0) { 863 bool verbose = false; 864 865 if (argc > 2) { 866 if (argc > 3 || strcmp(argv[2], "-v") != 0) 867 goto usage; 868 verbose = true; 869 } 870 871 vbe_print_mode(verbose); 872 return (CMD_OK); 873 } 874 875 if (strcmp(argv[1], "off") == 0) { 876 if (argc != 2) 877 goto usage; 878 879 if (vbestate.vbe_mode == 0) 880 return (CMD_OK); 881 882 reset_font_flags(); 883 bios_text_font(true); 884 bios_set_text_mode(VGA_TEXT_MODE); 885 plat_cons_update_mode(0); 886 return (CMD_OK); 887 } 888 889 if (strcmp(argv[1], "on") == 0) { 890 if (argc != 2) 891 goto usage; 892 893 modenum = vbe_default_mode(); 894 if (modenum == 0) { 895 snprintf(command_errbuf, sizeof (command_errbuf), 896 "%s: no suitable VBE mode number found", argv[0]); 897 return (CMD_ERROR); 898 } 899 } else if (strcmp(argv[1], "set") == 0) { 900 if (argc != 3) 901 goto usage; 902 903 if (strncmp(argv[2], "0x", 2) == 0) { 904 arg = argv[2]; 905 errno = 0; 906 n = strtoul(arg, &cp, 0); 907 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 908 snprintf(command_errbuf, 909 sizeof (command_errbuf), 910 "mode should be an integer"); 911 return (CMD_ERROR); 912 } 913 modenum = vbe_find_mode_xydm(0, 0, 0, n); 914 } else if (strchr(argv[2], 'x') != NULL) { 915 modenum = vbe_find_mode(argv[2]); 916 } 917 } else { 918 goto usage; 919 } 920 921 if (modenum == 0) { 922 snprintf(command_errbuf, sizeof (command_errbuf), 923 "%s: mode %s not supported by firmware\n", 924 argv[0], argv[2]); 925 return (CMD_ERROR); 926 } 927 928 if (modenum >= VESA_MODE_BASE) { 929 if (vbestate.vbe_mode != modenum) { 930 reset_font_flags(); 931 bios_text_font(false); 932 vbe_set_mode(modenum); 933 plat_cons_update_mode(1); 934 } 935 return (CMD_OK); 936 } else { 937 snprintf(command_errbuf, sizeof (command_errbuf), 938 "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]); 939 return (CMD_ERROR); 940 } 941 942 usage: 943 snprintf(command_errbuf, sizeof (command_errbuf), 944 "usage: %s on | off | get [-v] | list [depth] | " 945 "set <display or VBE mode number>", argv[0]); 946 return (CMD_ERROR); 947 } 948