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 <bootstrap.h>
  33 #include <machine/bootinfo.h>
  34 #include <machine/metadata.h>
  35 #include <sys/multiboot2.h>
  36 #include <btxv86.h>
  37 #include "libi386.h"
  38 #include "gfx_fb.h"     /* for EDID */
  39 #include "vbe.h"
  40 #include <sys/vgareg.h>
  41 #include <sys/vgasubr.h>
  42 
  43 multiboot_tag_vbe_t vbestate;
  44 static struct vbeinfoblock *vbe =
  45     (struct vbeinfoblock *) &vbestate.vbe_control_info;
  46 static struct modeinfoblock *vbe_mode =
  47     (struct modeinfoblock *) &vbestate.vbe_mode_info;
  48 multiboot_color_t cmap[16];
  49 
  50 /* Actually assuming mode 3. */
  51 void
  52 bios_set_text_mode(int mode)
  53 {
  54         int atr;
  55 
  56         v86.ctl = V86_FLAGS;
  57         v86.addr = 0x10;
  58         v86.eax = mode;                         /* set VGA text mode */
  59         v86int();
  60         atr = vga_get_atr(VGA_REG_ADDR, VGA_ATR_MODE);
  61         atr &= ~VGA_ATR_MODE_BLINK;
  62         atr &= ~VGA_ATR_MODE_9WIDE;
  63         vga_set_atr(VGA_REG_ADDR, VGA_ATR_MODE, atr);
  64 
  65         vbestate.vbe_mode = 0;                  /* vbe is disabled */
  66         gfx_fb.framebuffer_common.framebuffer_type =
  67             MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
  68         /* 16 bits per character */
  69         gfx_fb.framebuffer_common.framebuffer_bpp = 16;
  70         gfx_fb.framebuffer_common.framebuffer_addr =
  71             VGA_MEM_ADDR + VGA_COLOR_BASE;
  72         gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
  73         gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
  74         gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
  75 }
  76 
  77 /* Function 00h - Return VBE Controller Information */
  78 static int
  79 biosvbe_info(struct vbeinfoblock *vbe)
  80 {
  81         v86.ctl = V86_FLAGS;
  82         v86.addr = 0x10;
  83         v86.eax = 0x4f00;
  84         v86.es = VTOPSEG(vbe);
  85         v86.edi = VTOPOFF(vbe);
  86         v86int();
  87         return (v86.eax & 0xffff);
  88 }
  89 
  90 /* Function 01h - Return VBE Mode Information */
  91 static int
  92 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
  93 {
  94         v86.ctl = V86_FLAGS;
  95         v86.addr = 0x10;
  96         v86.eax = 0x4f01;
  97         v86.ecx = mode;
  98         v86.es = VTOPSEG(mi);
  99         v86.edi = VTOPOFF(mi);
 100         v86int();
 101         return (v86.eax & 0xffff);
 102 }
 103 
 104 /* Function 02h - Set VBE Mode */
 105 static int
 106 biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
 107 {
 108         v86.ctl = V86_FLAGS;
 109         v86.addr = 0x10;
 110         v86.eax = 0x4f02;
 111         v86.ebx = mode | 0x4000;        /* set linear FB bit */
 112         v86.es = VTOPSEG(ci);
 113         v86.edi = VTOPOFF(ci);
 114         v86int();
 115         return (v86.eax & 0xffff);
 116 }
 117 
 118 /* Function 03h - Get VBE Mode */
 119 static int
 120 biosvbe_get_mode(int *mode)
 121 {
 122         v86.ctl = V86_FLAGS;
 123         v86.addr = 0x10;
 124         v86.eax = 0x4f03;
 125         v86int();
 126         *mode = v86.ebx & 0xffff;
 127         return (v86.eax & 0xffff);
 128 }
 129 
 130 /* Function 08h - Set/Get DAC Palette Format */
 131 int
 132 biosvbe_palette_format(int *format)
 133 {
 134         v86.ctl = V86_FLAGS;
 135         v86.addr = 0x10;
 136         v86.eax = 0x4f08;
 137         v86.ebx = *format;
 138         v86int();
 139         *format = v86.ebx & 0xffff;
 140         return (v86.eax & 0xffff);
 141 }
 142 
 143 /* Function 09h - Set/Get Palette Data */
 144 static int
 145 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
 146 {
 147         v86.ctl = V86_FLAGS;
 148         v86.addr = 0x10;
 149         v86.eax = 0x4f09;
 150         v86.ebx = mode;
 151         v86.edx = reg;
 152         v86.ecx = 1;
 153         v86.es = VTOPSEG(pe);
 154         v86.edi = VTOPOFF(pe);
 155         v86int();
 156         return (v86.eax & 0xffff);
 157 }
 158 
 159 /*
 160  * Function 15h BL=00h - Report VBE/DDC Capabilities
 161  *
 162  * int biosvbe_ddc_caps(void)
 163  * return: VBE/DDC capabilities
 164  */
 165 static int
 166 biosvbe_ddc_caps(void)
 167 {
 168         v86.ctl = V86_FLAGS;
 169         v86.addr = 0x10;
 170         v86.eax = 0x4f15;       /* display identification extensions */
 171         v86.ebx = 0;            /* report DDC capabilities */
 172         v86.ecx = 0;            /* controller unit number (00h = primary) */
 173         v86.es = 0;
 174         v86.edi = 0;
 175         v86int();
 176         if (VBE_ERROR(v86.eax & 0xffff))
 177                 return (0);
 178         return (v86.ebx & 0xffff);
 179 }
 180 
 181 /* Function 15h BL=01h - Read EDID */
 182 static int
 183 biosvbe_ddc_read_edid(int blockno, void *buf)
 184 {
 185         v86.ctl = V86_FLAGS;
 186         v86.addr = 0x10;
 187         v86.eax = 0x4f15;       /* display identification extensions */
 188         v86.ebx = 1;            /* read EDID */
 189         v86.ecx = 0;            /* controller unit number (00h = primary) */
 190         v86.edx = blockno;
 191         v86.es = VTOPSEG(buf);
 192         v86.edi = VTOPOFF(buf);
 193         v86int();
 194         return (v86.eax & 0xffff);
 195 }
 196 
 197 static int
 198 vbe_mode_is_supported(struct modeinfoblock *mi)
 199 {
 200         if ((mi->ModeAttributes & 0x01) == 0)
 201                 return 0;       /* mode not supported by hardware */
 202         if ((mi->ModeAttributes & 0x08) == 0)
 203                 return 0;       /* linear fb not available */
 204         if ((mi->ModeAttributes & 0x10) == 0)
 205                 return 0;       /* text mode */
 206         if (mi->NumberOfPlanes != 1)
 207                 return 0;       /* planar mode not supported */
 208         if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
 209             mi->MemoryModel != 0x06 /* Direct Color */)
 210                 return 0;       /* unsupported pixel format */
 211         return 1;
 212 }
 213 
 214 static int
 215 vbe_check(void)
 216 {
 217         if (vbestate.mb_type != MULTIBOOT_TAG_TYPE_VBE) {
 218                 printf("VBE not available\n");
 219                 return (0);
 220         }
 221         return (1);
 222 }
 223 
 224 void
 225 vbe_init(void)
 226 {
 227         /* First set FB for text mode. */
 228         gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
 229         gfx_fb.framebuffer_common.framebuffer_type =
 230             MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
 231         /* 16 bits per character */
 232         gfx_fb.framebuffer_common.framebuffer_bpp = 16;
 233         gfx_fb.framebuffer_common.framebuffer_addr =
 234             VGA_MEM_ADDR + VGA_COLOR_BASE;
 235         gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
 236         gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
 237         gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
 238 
 239         /* Now check if we have vesa. */
 240         memset(vbe, 0, sizeof(*vbe));
 241         memcpy(vbe->VbeSignature, "VBE2", 4);
 242         if (biosvbe_info(vbe) != VBE_SUCCESS)
 243                 return;
 244         if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
 245                 return;
 246 
 247         vbestate.mb_type = MULTIBOOT_TAG_TYPE_VBE;
 248         vbestate.mb_size = sizeof (vbestate);
 249         vbestate.vbe_mode = 0;
 250         /* vbe_set_mode() will set up the rest. */
 251 }
 252 
 253 int
 254 vbe_available(void)
 255 {
 256         return vbestate.mb_type;
 257 }
 258 
 259 int
 260 vbe_set_palette(const struct paletteentry *entry, int slot)
 261 {
 262         struct paletteentry pe;
 263         int ret;
 264 
 265         if (!vbe_check())
 266                 return (1);
 267 
 268         if (gfx_fb.framebuffer_common.framebuffer_type !=
 269             MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
 270                 return (1);
 271         }
 272 
 273         pe.Blue = entry->Blue;
 274         pe.Green = entry->Green;
 275         pe.Red = entry->Red;
 276         pe.Alignment = entry->Alignment;
 277 
 278         ret = biosvbe_palette_data(0x00, slot, &pe);
 279         if (ret == VBE_SUCCESS && slot < sizeof (cmap)) {
 280                 cmap[slot].mb_red = entry->Red;
 281                 cmap[slot].mb_green = entry->Green;
 282                 cmap[slot].mb_blue = entry->Blue;
 283         }
 284 
 285         return (ret == VBE_SUCCESS ? 0 : 1);
 286 }
 287 
 288 int
 289 vbe_get_mode(void)
 290 {
 291         return vbestate.vbe_mode;
 292 }
 293 
 294 int
 295 vbe_set_mode(int modenum)
 296 {
 297         struct modeinfoblock mi;
 298         int ret, i, bpp;
 299 
 300         if (!vbe_check())
 301                 return (1);
 302 
 303         ret = biosvbe_get_mode_info(modenum, &mi);
 304         if (VBE_ERROR(ret)) {
 305                 printf("mode 0x%x invalid\n", modenum);
 306                 return (1);
 307         }
 308 
 309         if (!vbe_mode_is_supported(&mi)) {
 310                 printf("mode 0x%x not supported\n", modenum);
 311                 return (1);
 312         }
 313 
 314         /* calculate bytes per pixel */
 315         switch (mi.BitsPerPixel) {
 316         case 32:
 317                 bpp = 4;
 318                 break;
 319         case 24:
 320                 bpp = 3;
 321                 break;
 322         case 16:
 323         case 15:
 324                 bpp = 2;
 325                 break;
 326         case 8:
 327                 bpp = 1;
 328                 break;
 329         default:
 330                 printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
 331                 return (1);
 332         }
 333 
 334         ret = biosvbe_set_mode(modenum, NULL);
 335         if (VBE_ERROR(ret)) {
 336                 printf("mode 0x%x could not be set\n", modenum);
 337                 return (1);
 338         }
 339 
 340         /* make sure we have current MI in vbestate */
 341         memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
 342         vbestate.vbe_mode = modenum;
 343 
 344         gfx_fb.framebuffer_common.framebuffer_addr =
 345             (uint64_t)mi.PhysBasePtr & 0xffffffff;
 346         gfx_fb.framebuffer_common.framebuffer_width = mi.XResolution;
 347         gfx_fb.framebuffer_common.framebuffer_height = mi.YResolution;
 348         gfx_fb.framebuffer_common.framebuffer_bpp = mi.BitsPerPixel;
 349 
 350         /* vbe_mode_is_supported() excludes the rest */
 351         switch (mi.MemoryModel) {
 352         case 0x4:
 353                 gfx_fb.framebuffer_common.framebuffer_type =
 354                     MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
 355                 if (vbe->VbeVersion >= 0x300) {
 356                         gfx_fb.framebuffer_common.framebuffer_pitch =
 357                             mi.LinBytesPerScanLine;
 358                 } else {
 359                         gfx_fb.framebuffer_common.framebuffer_pitch =
 360                             mi.BytesPerScanLine;
 361                 }
 362                 gfx_fb.u.fb1.framebuffer_palette_num_colors = 16;
 363                 return (0);     /* done */
 364         case 0x6:
 365                 gfx_fb.framebuffer_common.framebuffer_type =
 366                     MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
 367                 break;
 368         }
 369 
 370         if (vbe->VbeVersion >= 0x300) {
 371                 gfx_fb.framebuffer_common.framebuffer_pitch =
 372                     mi.LinBytesPerScanLine;
 373                 gfx_fb.u.fb2.framebuffer_red_field_position =
 374                     mi.LinRedFieldPosition;
 375                 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.LinRedMaskSize;
 376                 gfx_fb.u.fb2.framebuffer_green_field_position =
 377                     mi.LinGreenFieldPosition;
 378                 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.LinGreenMaskSize;
 379                 gfx_fb.u.fb2.framebuffer_blue_field_position =
 380                     mi.LinBlueFieldPosition;
 381                 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.LinBlueMaskSize;
 382         } else {
 383                 gfx_fb.framebuffer_common.framebuffer_pitch =
 384                     mi.BytesPerScanLine;
 385                 gfx_fb.u.fb2.framebuffer_red_field_position =
 386                     mi.RedFieldPosition;
 387                 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.RedMaskSize;
 388                 gfx_fb.u.fb2.framebuffer_green_field_position =
 389                     mi.GreenFieldPosition;
 390                 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.GreenMaskSize;
 391                 gfx_fb.u.fb2.framebuffer_blue_field_position =
 392                     mi.BlueFieldPosition;
 393                 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.BlueMaskSize;
 394         }
 395 
 396         return (0);
 397 }
 398 
 399 static void *
 400 vbe_farptr(uint32_t farptr)
 401 {
 402         return PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)));
 403 }
 404 
 405 static int
 406 vbe_parse_mode_str(char *str, int *x, int *y, int *depth)
 407 {
 408         char *p;
 409 
 410         p = str;
 411         *x = strtoul(p, NULL, 0);
 412         if (*x == 0)
 413                 return (0);
 414         p = strchr(p, 'x');
 415         if (!p)
 416                 return (0);
 417         ++p;
 418         *y = strtoul(p, NULL, 0);
 419         if (*y == 0)
 420                 return (0);
 421         p = strchr(p, 'x');
 422         if (!p)
 423                 *depth = -1;    /* auto select */
 424         else {
 425                 ++p;
 426                 *depth = strtoul(p, NULL, 0);
 427                 if (*depth == 0)
 428                         return (0);
 429         }
 430 
 431         return (1);
 432 }
 433 
 434 /*
 435  * Verify existance of mode number or find mode by
 436  * dimensions. If depth is not given, walk values 32, 24, 16, 8.
 437  */
 438 static int
 439 vbe_find_mode_xydm(int x, int y, int depth, int m)
 440 {
 441         struct modeinfoblock mi;
 442         uint32_t farptr;
 443         uint16_t mode;
 444         int safety = 0, d, i;
 445 
 446         memset(vbe, 0, sizeof(vbe));
 447         memcpy(vbe->VbeSignature, "VBE2", 4);
 448         if (biosvbe_info(vbe) != VBE_SUCCESS)
 449                 return (0);
 450         if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
 451                 return (0);
 452         farptr = vbe->VideoModePtr;
 453         if (farptr == 0)
 454                 return (0);
 455 
 456         if (m != -1)
 457                 i = 8;
 458         else if (depth == -1)
 459                 i = 32;
 460         else
 461                 i = depth;
 462 
 463         while (i > 0) {
 464                 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
 465                         safety++;
 466                         farptr += 2;
 467                         if (safety == 100)
 468                                 return 0;
 469                         if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
 470                                 continue;
 471                         }
 472                         /* we only care about linear modes here */
 473                         if (vbe_mode_is_supported(&mi) == 0)
 474                                 continue;
 475                         safety = 0;
 476 
 477                         if (m != -1) {
 478                                 if (m == mode)
 479                                         return (mode);
 480                                 else
 481                                         continue;
 482                         }
 483 
 484                         if (mi.XResolution == x &&
 485                             mi.YResolution == y &&
 486                             mi.BitsPerPixel == i)
 487                                 return mode;
 488                 }
 489                 if (depth != -1)
 490                         break;
 491 
 492                 i -= 8;
 493         }
 494 
 495         return (0);
 496 }
 497 
 498 static int
 499 vbe_find_mode(char *str)
 500 {
 501         int x, y, depth;
 502 
 503         if (!vbe_parse_mode_str(str, &x, &y, &depth))
 504                 return (0);
 505 
 506         return (vbe_find_mode_xydm(x, y, depth, -1));
 507 }
 508 
 509 static void
 510 vbe_dump_mode(int modenum, struct modeinfoblock *mi)
 511 {
 512         printf("0x%x=%dx%dx%d", modenum,
 513             mi->XResolution, mi->YResolution, mi->BitsPerPixel);
 514 }
 515 
 516 static int
 517 vbe_get_edid(int *pwidth, int *pheight)
 518 {
 519         struct vesa_edid_info edid_info;
 520         const uint8_t magic[] = EDID_MAGIC;
 521         int ddc_caps, ret, i;
 522 
 523         ddc_caps = biosvbe_ddc_caps();
 524         if (ddc_caps == 0) {
 525                 return (1);
 526         }
 527 
 528         ret = biosvbe_ddc_read_edid(0, &edid_info);
 529         if (VBE_ERROR(ret))
 530                 return (1);
 531 
 532         if (memcmp(&edid_info, magic, sizeof(magic)) != 0)
 533                 return (1);
 534 
 535         if (!(edid_info.header.version == 1 &&
 536             (edid_info.display.supported_features
 537             & EDID_FEATURE_PREFERRED_TIMING_MODE) &&
 538             edid_info.detailed_timings[0].pixel_clock))
 539                 return (1);
 540 
 541         *pwidth = edid_info.detailed_timings[0].horizontal_active_lo |
 542             (((int)edid_info.detailed_timings[0].horizontal_hi & 0xf0) << 4);
 543         *pheight = edid_info.detailed_timings[0].vertical_active_lo |
 544             (((int)edid_info.detailed_timings[0].vertical_hi & 0xf0) << 4);
 545 
 546         return (0);
 547 }
 548 
 549 static void
 550 vbe_print_vbe_info(struct vbeinfoblock *vbep)
 551 {
 552         char *oemstring = "";
 553         char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
 554 
 555         if (vbep->OemStringPtr != 0)
 556                 oemstring = vbe_farptr(vbep->OemStringPtr);
 557 
 558         if (vbep->OemVendorNamePtr != 0)
 559                 oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
 560 
 561         if (vbep->OemProductNamePtr != 0)
 562                 oemproductname = vbe_farptr(vbep->OemProductNamePtr);
 563 
 564         if (vbep->OemProductRevPtr != 0)
 565                 oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
 566 
 567         printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
 568             vbep->VbeVersion & 0xF, oemstring);
 569 
 570         if (vbep->OemSoftwareRev != 0) {
 571                 printf("OEM Version %d.%d, %s (%s, %s)\n",
 572                     vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
 573                         oemvendor, oemproductname, oemproductrev);
 574         }
 575 }
 576 
 577 /* List available modes, filter by depth. If depth is -1, list all. */
 578 void
 579 vbe_modelist(int depth)
 580 {
 581         struct modeinfoblock mi;
 582         uint32_t farptr;
 583         uint16_t mode;
 584         int nmodes = 0, safety = 0;
 585         int ddc_caps, edid_width, edid_height;
 586 
 587         if (!vbe_check())
 588                 return;
 589 
 590         ddc_caps = biosvbe_ddc_caps();
 591         if (ddc_caps & 3) {
 592                 printf("DDC");
 593                 if (ddc_caps & 1)
 594                         printf(" [DDC1]");
 595                 if (ddc_caps & 2)
 596                         printf(" [DDC2]");
 597 
 598                 if (vbe_get_edid(&edid_width, &edid_height) != 0)
 599                         printf(": no EDID information\n");
 600                 else
 601                         printf(": EDID %dx%d\n", edid_width, edid_height);
 602         }
 603 
 604         memset(vbe, 0, sizeof(vbe));
 605         memcpy(vbe->VbeSignature, "VBE2", 4);
 606         if (biosvbe_info(vbe) != VBE_SUCCESS)
 607                 goto done;
 608         if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
 609                 goto done;
 610 
 611         vbe_print_vbe_info(vbe);
 612         printf("Modes: ");
 613 
 614         farptr = vbe->VideoModePtr;
 615         if (farptr == 0)
 616                 goto done;
 617 
 618         while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
 619                 safety++;
 620                 farptr += 2;
 621                 if (safety == 100) {
 622                         printf("[?] ");
 623                         break;
 624                 }
 625                 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
 626                         continue;
 627                 /* we only care about linear modes here */
 628                 if (vbe_mode_is_supported(&mi) == 0)
 629                         continue;
 630 
 631                 /* we found some mode so reset safety counter */
 632                 safety = 0;
 633 
 634                 /* apply requested filter */
 635                 if (depth != -1 && mi.BitsPerPixel != depth)
 636                         continue;
 637 
 638                 if (nmodes % 4 == 0)
 639                         printf("\n");
 640                 else
 641                         printf("  ");
 642 
 643                 vbe_dump_mode(mode, &mi);
 644                 nmodes++;
 645         }
 646 
 647 done:
 648         if (nmodes == 0)
 649                 printf("none found");
 650         printf("\n");
 651 }
 652 
 653 static void
 654 vbe_print_mode(void)
 655 {
 656         int mode, i, rc;
 657         struct paletteentry pe;
 658 
 659         memset(vbe, 0, sizeof(vbe));
 660         memcpy(vbe->VbeSignature, "VBE2", 4);
 661         if (biosvbe_info(vbe) != VBE_SUCCESS)
 662                 return;
 663 
 664         if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
 665                 return;
 666 
 667         vbe_print_vbe_info(vbe);
 668 
 669         if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
 670                 printf("Error getting current VBE mode\n");
 671                 return;
 672         }
 673 
 674         if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
 675             vbe_mode_is_supported(vbe_mode) == 0) {
 676                 printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
 677                 return;
 678         }
 679 
 680         printf("\nCurrent VBE mode: ");
 681         vbe_dump_mode(mode, vbe_mode);
 682         printf("\n");
 683 
 684         printf("%ux%ux%u, stride=%u",
 685             gfx_fb.framebuffer_common.framebuffer_width,
 686             gfx_fb.framebuffer_common.framebuffer_height,
 687             gfx_fb.framebuffer_common.framebuffer_bpp,
 688             (gfx_fb.framebuffer_common.framebuffer_pitch << 3) /
 689             gfx_fb.framebuffer_common.framebuffer_bpp);
 690         printf("\n    frame buffer: address=%jx, size=%jx",
 691             (uintmax_t) gfx_fb.framebuffer_common.framebuffer_addr,
 692             (uintmax_t) gfx_fb.framebuffer_common.framebuffer_height *
 693             gfx_fb.framebuffer_common.framebuffer_pitch);
 694 
 695         if (vbe_mode->MemoryModel == 0x6) {
 696                 printf("\n    color mask: R=%08x, G=%08x, B=%08x\n",
 697                     ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1) <<
 698                     gfx_fb.u.fb2.framebuffer_red_field_position,
 699                     ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1) <<
 700                     gfx_fb.u.fb2.framebuffer_green_field_position,
 701                     ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1) <<
 702                     gfx_fb.u.fb2.framebuffer_blue_field_position);
 703                 return;
 704         }
 705 
 706         mode = 1;       /* get DAC palette width */
 707         rc = biosvbe_palette_format(&mode);
 708         if (rc != VBE_SUCCESS)
 709                 return;
 710 
 711         printf("    palette format: %x bits per primary\n", mode >> 8);
 712         for (i = 0; i < 16; i++) {
 713                 rc = biosvbe_palette_data(1, i, &pe);
 714                 if (rc != VBE_SUCCESS)
 715                         break;
 716 
 717                 printf("%d: R=%02x, G=%02x, B=%02x\n", i,
 718                     pe.Red, pe.Green, pe.Blue);
 719         }
 720 }
 721 
 722 int
 723 vbe_default_mode(void)
 724 {
 725         int modenum, edid_width, edid_height;
 726 
 727         if (vbe_get_edid(&edid_width, &edid_height) != 0) {
 728                 modenum = vbe_find_mode(VBE_DEFAULT_MODE);
 729         } else {
 730                 modenum = vbe_find_mode_xydm(edid_width, edid_height, -1, -1);
 731                 if (modenum == 0)
 732                         modenum = vbe_find_mode(VBE_DEFAULT_MODE);
 733         }
 734         return (modenum);
 735 }
 736 
 737 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management",
 738     command_vesa);
 739 
 740 int
 741 command_vesa(int argc, char *argv[])
 742 {
 743         char *arg, *cp;
 744         int modenum = -1, n;
 745 
 746         if (!vbe_check())
 747                 return (CMD_OK);
 748 
 749         if (argc < 2)
 750                 goto usage;
 751 
 752         if (strcmp(argv[1], "list") == 0) {
 753                 n = -1;
 754                 if (argc != 2 && argc != 3)
 755                         goto usage;
 756 
 757                 if (argc == 3) {
 758                         arg = argv[2];
 759                         errno = 0;
 760                         n = strtoul(arg, &cp, 0);
 761                         if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
 762                                 snprintf(command_errbuf,
 763                                     sizeof (command_errbuf),
 764                                     "depth should be an integer");
 765                                 return (CMD_ERROR);
 766                         }
 767                 }
 768                 vbe_modelist(n);
 769                 return (CMD_OK);
 770         }
 771 
 772         if (strcmp(argv[1], "get") == 0) {
 773                 if (argc != 2)
 774                         goto usage;
 775 
 776                 vbe_print_mode();
 777                 return (CMD_OK);
 778         }
 779 
 780         if (strcmp(argv[1], "off") == 0) {
 781                 if (argc != 2)
 782                         goto usage;
 783 
 784                 if (vbestate.vbe_mode == 0)
 785                         return (CMD_OK);
 786 
 787                 bios_set_text_mode(3);          /* set VGA text mode 3 */
 788                 plat_cons_update_mode(0);
 789                 return (CMD_OK);
 790         }
 791 
 792         if (strcmp(argv[1], "on") == 0) {
 793                 if (argc != 2)
 794                         goto usage;
 795 
 796                 modenum = vbe_default_mode();
 797                 if (modenum == 0) {
 798                         sprintf(command_errbuf,
 799                             "%s: no suitable VBE mode number found", argv[0]);
 800                         return (CMD_ERROR);
 801                 }
 802         } else if (strcmp(argv[1], "set") == 0) {
 803                 if (argc != 3)
 804                         goto usage;
 805 
 806                 if (strncmp(argv[2], "0x", 2) == 0) {
 807                         arg = argv[2];
 808                         errno = 0;
 809                         n = strtoul(arg, &cp, 0);
 810                         if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
 811                                 snprintf(command_errbuf,
 812                                     sizeof (command_errbuf),
 813                                     "mode should be an integer");
 814                                 return (CMD_ERROR);
 815                         }
 816                         modenum = vbe_find_mode_xydm(0, 0, 0, n);
 817                 } else if (strchr(argv[2], 'x') != NULL) {
 818                         modenum = vbe_find_mode(argv[2]);
 819                 }
 820         }
 821 
 822         if (modenum == 0) {
 823                 sprintf(command_errbuf, "%s: mode %s not supported by "
 824                     "firmware\n", argv[0], argv[2]);
 825                 return (CMD_ERROR);
 826         }
 827 
 828         if (modenum >= 0x100) {
 829                 if (vbestate.vbe_mode != modenum) {
 830                         vbe_set_mode(modenum);
 831                         plat_cons_update_mode(1);
 832                 }
 833                 return (CMD_OK);
 834         } else {
 835                 sprintf(command_errbuf, "%s: mode %s is not framebuffer mode\n",
 836                     argv[0], argv[2]);
 837                 return (CMD_ERROR);
 838         }
 839 
 840 usage:
 841         sprintf(command_errbuf, "usage: %s on | off | get | list [depth] | "
 842             "set <display or VBE mode number>", argv[0]);
 843         return (CMD_ERROR);
 844 }