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