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