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