xref: /illumos-gate/usr/src/boot/sys/boot/i386/libi386/vbe.c (revision 7449d372)
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