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