xref: /freebsd/stand/i386/libi386/vbe.c (revision 3110d4ebd6c0848cf5e25890d01791bb407e2a9b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  * Copyright 2020 Toomas Soome
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/cdefs.h>
33 #include <stand.h>
34 #include <sys/param.h>
35 #include <machine/psl.h>
36 #include <machine/cpufunc.h>
37 #include <stdbool.h>
38 #include <bootstrap.h>
39 #include <btxv86.h>
40 #include <gfx_fb.h>
41 #include <dev/vt/hw/vga/vt_vga_reg.h>
42 #include "libi386.h"
43 #include "vbe.h"
44 
45 /*
46  * VESA BIOS Extensions routines
47  */
48 
49 static struct vbeinfoblock *vbe;
50 static struct modeinfoblock *vbe_mode;
51 
52 static uint16_t *vbe_mode_list;
53 static size_t vbe_mode_list_size;
54 
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 
60 /*
61  * palette array for 8-bit indexed colors. In this case, cmap does store
62  * index and pe8 does store actual RGB. This is needed because we may
63  * not be able to read palette data from hardware.
64  */
65 struct paletteentry *pe8 = NULL;
66 
67 static struct named_resolution {
68 	const char *name;
69 	const char *alias;
70 	unsigned int width;
71 	unsigned int height;
72 } resolutions[] = {
73 	{
74 		.name = "480p",
75 		.width = 640,
76 		.height = 480,
77 	},
78 	{
79 		.name = "720p",
80 		.width = 1280,
81 		.height = 720,
82 	},
83 	{
84 		.name = "1080p",
85 		.width = 1920,
86 		.height = 1080,
87 	},
88 	{
89 		.name = "2160p",
90 		.alias = "4k",
91 		.width = 3840,
92 		.height = 2160,
93 	},
94 	{
95 		.name = "5k",
96 		.width = 5120,
97 		.height = 2880,
98 	}
99 };
100 
101 static bool
102 vbe_resolution_compare(struct named_resolution *res, const char *cmp)
103 {
104 
105 	if (strcasecmp(res->name, cmp) == 0)
106 		return (true);
107 	if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
108 		return (true);
109 	return (false);
110 }
111 
112 static void
113 vbe_get_max_resolution(int *width, int *height)
114 {
115 	struct named_resolution *res;
116 	char *maxres;
117 	char *height_start, *width_start;
118 	int idx;
119 
120 	*width = *height = 0;
121 	maxres = getenv("vbe_max_resolution");
122 	/* No max_resolution set? Bail out; choose highest resolution */
123 	if (maxres == NULL)
124 		return;
125 	/* See if it matches one of our known resolutions */
126 	for (idx = 0; idx < nitems(resolutions); ++idx) {
127 		res = &resolutions[idx];
128 		if (vbe_resolution_compare(res, maxres)) {
129 			*width = res->width;
130 			*height = res->height;
131 			return;
132 		}
133 	}
134 	/* Not a known resolution, try to parse it; make a copy we can modify */
135 	maxres = strdup(maxres);
136 	if (maxres == NULL)
137 		return;
138 	height_start = strchr(maxres, 'x');
139 	if (height_start == NULL) {
140 		free(maxres);
141 		return;
142 	}
143 	width_start = maxres;
144 	*height_start++ = 0;
145 	/* Errors from this will effectively mean "no max" */
146 	*width = (int)strtol(width_start, NULL, 0);
147 	*height = (int)strtol(height_start, NULL, 0);
148 	free(maxres);
149 }
150 
151 int
152 vga_get_reg(int reg, int index)
153 {
154 	return (inb(reg + index));
155 }
156 
157 int
158 vga_get_atr(int reg, int i)
159 {
160 	int ret;
161 
162 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
163 	outb(reg + VGA_AC_WRITE, i);
164 	ret = inb(reg + VGA_AC_READ);
165 
166 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
167 
168 	return (ret);
169 }
170 
171 void
172 vga_set_atr(int reg, int i, int v)
173 {
174 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
175 	outb(reg + VGA_AC_WRITE, i);
176 	outb(reg + VGA_AC_WRITE, v);
177 
178 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
179 }
180 
181 void
182 vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
183 {
184 	outb(reg + indexreg, index);
185 	outb(reg + datareg, val);
186 }
187 
188 int
189 vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
190 {
191 	outb(reg + indexreg, index);
192 	return (inb(reg + datareg));
193 }
194 
195 int
196 vga_get_crtc(int reg, int i)
197 {
198 	return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
199 }
200 
201 void
202 vga_set_crtc(int reg, int i, int v)
203 {
204 	vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
205 }
206 
207 int
208 vga_get_seq(int reg, int i)
209 {
210 	return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i));
211 }
212 
213 void
214 vga_set_seq(int reg, int i, int v)
215 {
216 	vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v);
217 }
218 
219 int
220 vga_get_grc(int reg, int i)
221 {
222 	return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i));
223 }
224 
225 void
226 vga_set_grc(int reg, int i, int v)
227 {
228 	vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v);
229 }
230 
231 /*
232  * Return true when this controller is VGA compatible.
233  */
234 bool
235 vbe_is_vga(void)
236 {
237 	if (vbe == NULL)
238 		return (false);
239 
240 	return ((vbe->Capabilities & VBE_CAP_NONVGA) == 0);
241 }
242 
243 /* Actually assuming mode 3. */
244 void
245 bios_set_text_mode(int mode)
246 {
247 	int atr;
248 
249 	if (vbe->Capabilities & VBE_CAP_DAC8) {
250 		int m;
251 
252 		/*
253 		 * The mode change should reset the palette format to
254 		 * 6 bits, but apparently some systems do fail with 8-bit
255 		 * palette, so we switch to 6-bit here.
256 		 */
257 		m = 0x0600;
258 		(void) biosvbe_palette_format(&m);
259 		palette_format = m;
260 	}
261 	v86.ctl = V86_FLAGS;
262 	v86.addr = 0x10;
263 	v86.eax = mode;				/* set VGA text mode */
264 	v86int();
265 	atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL);
266 	atr &= ~VGA_AC_MC_BI;
267 	atr &= ~VGA_AC_MC_ELG;
268 	vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr);
269 
270 	gfx_state.tg_mode = mode;
271 	gfx_state.tg_fb_type = FB_TEXT;
272 	gfx_state.tg_fb.fb_height = TEXT_ROWS;
273 	gfx_state.tg_fb.fb_width = TEXT_COLS;
274 
275 	gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16;
276 	gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8;
277 	gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0;
278 	gfx_state.tg_ctype = CT_INDEXED;
279 	env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL);
280 }
281 
282 /* Function 00h - Return VBE Controller Information */
283 static int
284 biosvbe_info(struct vbeinfoblock *vbep)
285 {
286 	struct vbeinfoblock *rvbe;
287 	int ret;
288 
289 	if (vbep == NULL)
290 		return (VBE_FAILED);
291 
292 	rvbe = bio_alloc(sizeof(*rvbe));
293 	if (rvbe == NULL)
294 		return (VBE_FAILED);
295 
296 	/* Now check if we have vesa. */
297 	memset(rvbe, 0, sizeof (*vbe));
298 	memcpy(rvbe->VbeSignature, "VBE2", 4);
299 
300 	v86.ctl = V86_FLAGS;
301 	v86.addr = 0x10;
302 	v86.eax = 0x4f00;
303 	v86.es = VTOPSEG(rvbe);
304 	v86.edi = VTOPOFF(rvbe);
305 	v86int();
306 	ret = v86.eax & 0xffff;
307 
308 	if (ret != VBE_SUCCESS)
309 		goto done;
310 
311 	if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) {
312 		ret = VBE_NOTSUP;
313 		goto done;
314 	}
315 	bcopy(rvbe, vbep, sizeof(*vbep));
316 done:
317 	bio_free(rvbe, sizeof(*rvbe));
318 	return (ret);
319 }
320 
321 /* Function 01h - Return VBE Mode Information */
322 static int
323 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
324 {
325 	struct modeinfoblock *rmi;
326 	int ret;
327 
328 	rmi = bio_alloc(sizeof(*rmi));
329 	if (rmi == NULL)
330 		return (VBE_FAILED);
331 
332 	v86.ctl = V86_FLAGS;
333 	v86.addr = 0x10;
334 	v86.eax = 0x4f01;
335 	v86.ecx = mode;
336 	v86.es = VTOPSEG(rmi);
337 	v86.edi = VTOPOFF(rmi);
338 	v86int();
339 
340 	ret = v86.eax & 0xffff;
341 	if (ret != VBE_SUCCESS)
342 		goto done;
343 	bcopy(rmi, mi, sizeof(*rmi));
344 done:
345 	bio_free(rmi, sizeof(*rmi));
346 	return (ret);
347 }
348 
349 /* Function 02h - Set VBE Mode */
350 static int
351 biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
352 {
353 	int rv;
354 
355 	if (vbe->Capabilities & VBE_CAP_DAC8) {
356 		int m;
357 
358 		/*
359 		 * The mode change should reset the palette format to
360 		 * 6 bits, but apparently some systems do fail with 8-bit
361 		 * palette, so we switch to 6-bit here.
362 		 */
363 		m = 0x0600;
364 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
365 			palette_format = m;
366 	}
367 	v86.ctl = V86_FLAGS;
368 	v86.addr = 0x10;
369 	v86.eax = 0x4f02;
370 	v86.ebx = mode | 0x4000;	/* set linear FB bit */
371 	v86.es = VTOPSEG(ci);
372 	v86.edi = VTOPOFF(ci);
373 	v86int();
374 	rv = v86.eax & 0xffff;
375 	if (vbe->Capabilities & VBE_CAP_DAC8) {
376 		int m;
377 
378 		/* Switch to 8-bits per primary color. */
379 		m = 0x0800;
380 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
381 			palette_format = m;
382 	}
383 	env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL);
384 	return (rv);
385 }
386 
387 /* Function 03h - Get VBE Mode */
388 static int
389 biosvbe_get_mode(int *mode)
390 {
391 	v86.ctl = V86_FLAGS;
392 	v86.addr = 0x10;
393 	v86.eax = 0x4f03;
394 	v86int();
395 	*mode = v86.ebx & 0x3fff;	/* Bits 0-13 */
396 	return (v86.eax & 0xffff);
397 }
398 
399 /* Function 08h - Set/Get DAC Palette Format */
400 int
401 biosvbe_palette_format(int *format)
402 {
403 	v86.ctl = V86_FLAGS;
404 	v86.addr = 0x10;
405 	v86.eax = 0x4f08;
406 	v86.ebx = *format;
407 	v86int();
408 	*format = (v86.ebx >> 8) & 0xff;
409 	return (v86.eax & 0xffff);
410 }
411 
412 /* Function 09h - Set/Get Palette Data */
413 static int
414 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
415 {
416 	v86.ctl = V86_FLAGS;
417 	v86.addr = 0x10;
418 	v86.eax = 0x4f09;
419 	v86.ebx = mode;
420 	v86.edx = reg;
421 	v86.ecx = 1;
422 	v86.es = VTOPSEG(pe);
423 	v86.edi = VTOPOFF(pe);
424 	v86int();
425 	return (v86.eax & 0xffff);
426 }
427 
428 /*
429  * Function 15h BL=00h - Report VBE/DDC Capabilities
430  *
431  * int biosvbe_ddc_caps(void)
432  * return: VBE/DDC capabilities
433  */
434 static int
435 biosvbe_ddc_caps(void)
436 {
437 	v86.ctl = V86_FLAGS;
438 	v86.addr = 0x10;
439 	v86.eax = 0x4f15;	/* display identification extensions */
440 	v86.ebx = 0;		/* report DDC capabilities */
441 	v86.ecx = 0;		/* controller unit number (00h = primary) */
442 	v86.es = 0;
443 	v86.edi = 0;
444 	v86int();
445 	if (VBE_ERROR(v86.eax & 0xffff))
446 		return (0);
447 	return (v86.ebx & 0xffff);
448 }
449 
450 /* Function 11h BL=01h - Flat Panel status */
451 static int
452 biosvbe_ddc_read_flat_panel_info(void *buf)
453 {
454 	v86.ctl = V86_FLAGS;
455 	v86.addr = 0x10;
456 	v86.eax = 0x4f11;	/* Flat Panel Interface extensions */
457 	v86.ebx = 1;		/* Return Flat Panel Information */
458 	v86.es = VTOPSEG(buf);
459 	v86.edi = VTOPOFF(buf);
460 	v86int();
461 	return (v86.eax & 0xffff);
462 }
463 
464 /* Function 15h BL=01h - Read EDID */
465 static int
466 biosvbe_ddc_read_edid(int blockno, void *buf)
467 {
468 	v86.ctl = V86_FLAGS;
469 	v86.addr = 0x10;
470 	v86.eax = 0x4f15;	/* display identification extensions */
471 	v86.ebx = 1;		/* read EDID */
472 	v86.ecx = 0;		/* controller unit number (00h = primary) */
473 	v86.edx = blockno;
474 	v86.es = VTOPSEG(buf);
475 	v86.edi = VTOPOFF(buf);
476 	v86int();
477 	return (v86.eax & 0xffff);
478 }
479 
480 static int
481 vbe_mode_is_supported(struct modeinfoblock *mi)
482 {
483 	if ((mi->ModeAttributes & 0x01) == 0)
484 		return (0);	/* mode not supported by hardware */
485 	if ((mi->ModeAttributes & 0x08) == 0)
486 		return (0);	/* linear fb not available */
487 	if ((mi->ModeAttributes & 0x10) == 0)
488 		return (0);	/* text mode */
489 	if (mi->NumberOfPlanes != 1)
490 		return (0);	/* planar mode not supported */
491 	if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
492 	    mi->MemoryModel != 0x06 /* Direct Color */)
493 		return (0);	/* unsupported pixel format */
494 	return (1);
495 }
496 
497 static bool
498 vbe_check(void)
499 {
500 
501 	if (vbe == NULL) {
502 		printf("VBE not available\n");
503 		return (false);
504 	}
505 	return (true);
506 }
507 
508 static int
509 mode_set(struct env_var *ev, int flags __unused, const void *value)
510 {
511 	int mode;
512 
513 	if (strcmp(ev->ev_name, "screen.textmode") == 0) {
514 		unsigned long v;
515 		char *end;
516 
517 		if (value == NULL)
518 			return (0);
519 		errno = 0;
520 		v = strtoul(value, &end, 0);
521 		if (errno != 0 || *(char *)value == '\0' || *end != '\0' ||
522 		    (v != 0 && v != 1))
523 			return (EINVAL);
524 		env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK,
525 		    value, NULL, NULL);
526 		if (v == 1) {
527 			reset_font_flags();
528 			bios_text_font(true);
529 			bios_set_text_mode(VGA_TEXT_MODE);
530 			(void) cons_update_mode(false);
531 			return (0);
532 		}
533 	} else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) {
534 		env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
535 		    value, NULL, NULL);
536 	} else {
537 		return (EINVAL);
538 	}
539 
540 	mode = vbe_default_mode();
541 	if (gfx_state.tg_mode != mode) {
542 		reset_font_flags();
543 		bios_text_font(false);
544 		vbe_set_mode(mode);
545 		cons_update_mode(true);
546 	}
547 	return (0);
548 }
549 
550 static void *
551 vbe_farptr(uint32_t farptr)
552 {
553 	return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
554 }
555 
556 void
557 vbe_init(void)
558 {
559 	uint16_t *p, *ml;
560 
561 	/* First set FB for text mode. */
562 	gfx_state.tg_fb_type = FB_TEXT;
563 	gfx_state.tg_fb.fb_height = TEXT_ROWS;
564 	gfx_state.tg_fb.fb_width = TEXT_COLS;
565 	gfx_state.tg_ctype = CT_INDEXED;
566 	gfx_state.tg_mode = 3;
567 
568 	env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set,
569 	    env_nounset);
570 	env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
571 	    env_nounset);
572 
573 	if (vbe == NULL) {
574 		vbe = malloc(sizeof(*vbe));
575 		if (vbe == NULL)
576 			return;
577 	}
578 
579 	if (vbe_mode == NULL) {
580 		vbe_mode = malloc(sizeof(*vbe_mode));
581 		if (vbe_mode == NULL) {
582 			free(vbe);
583 			vbe = NULL;
584 		}
585 	}
586 
587 	if (biosvbe_info(vbe) != VBE_SUCCESS) {
588 		free(vbe);
589 		vbe = NULL;
590 		free(vbe_mode);
591 		vbe_mode = NULL;
592 		return;
593 	}
594 
595 	/*
596 	 * Copy mode list. We must do this because some systems do
597 	 * corrupt the provided list (vbox 6.1 is one example).
598 	 */
599 	p = ml = vbe_farptr(vbe->VideoModePtr);
600 	while(*p++ != 0xFFFF)
601 		;
602 
603 	vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
604 
605 	/*
606 	 * Since vbe_init() is used only once at very start of the loader,
607 	 * we assume malloc will not fail there, but in case it does,
608 	 * we point vbe_mode_list to memory pointed by VideoModePtr.
609 	 */
610 	vbe_mode_list = malloc(vbe_mode_list_size);
611 	if (vbe_mode_list == NULL)
612 		vbe_mode_list = ml;
613 	else
614 		bcopy(ml, vbe_mode_list, vbe_mode_list_size);
615 
616 	/* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */
617 	vbe->VideoModePtr = 0;
618 
619 	/* vbe_set_mode() will set up the rest. */
620 }
621 
622 bool
623 vbe_available(void)
624 {
625 	return (gfx_state.tg_fb_type == FB_VBE);
626 }
627 
628 int
629 vbe_set_palette(const struct paletteentry *entry, size_t slot)
630 {
631 	struct paletteentry pe;
632 	int mode, ret;
633 
634 	if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
635 		return (1);
636 
637 	if (gfx_state.tg_ctype != CT_INDEXED) {
638 		return (1);
639 	}
640 
641 	pe.Blue = entry->Blue;
642 	pe.Green = entry->Green;
643 	pe.Red = entry->Red;
644 	pe.Reserved = entry->Reserved;
645 
646 	if (vbe->Capabilities & VBE_CAP_SNOW)
647 		mode = 0x80;
648 	else
649 		mode = 0;
650 
651 	ret = biosvbe_palette_data(mode, slot, &pe);
652 
653 	return (ret == VBE_SUCCESS ? 0 : 1);
654 }
655 
656 int
657 vbe_get_mode(void)
658 {
659 	return (gfx_state.tg_mode);
660 }
661 
662 int
663 vbe_set_mode(int modenum)
664 {
665 	struct modeinfoblock mi;
666 	int bpp, ret;
667 
668 	if (!vbe_check())
669 		return (1);
670 
671 	ret = biosvbe_get_mode_info(modenum, &mi);
672 	if (VBE_ERROR(ret)) {
673 		printf("mode 0x%x invalid\n", modenum);
674 		return (1);
675 	}
676 
677 	if (!vbe_mode_is_supported(&mi)) {
678 		printf("mode 0x%x not supported\n", modenum);
679 		return (1);
680 	}
681 
682 	/* calculate bytes per pixel */
683 	switch (mi.BitsPerPixel) {
684 	case 32:
685 	case 24:
686 	case 16:
687 	case 15:
688 	case 8:
689 		break;
690 	default:
691 		printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
692 		return (1);
693 	}
694 
695 	ret = biosvbe_set_mode(modenum, NULL);
696 	if (VBE_ERROR(ret)) {
697 		printf("mode 0x%x could not be set\n", modenum);
698 		return (1);
699 	}
700 
701 	gfx_state.tg_mode = modenum;
702 	gfx_state.tg_fb_type = FB_VBE;
703 	/* make sure we have current MI in vbestate */
704 	memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
705 
706 	gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
707 	gfx_state.tg_fb.fb_height = mi.YResolution;
708 	gfx_state.tg_fb.fb_width = mi.XResolution;
709 	gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel;
710 
711 	/* Bytes per pixel */
712 	bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;
713 
714 	/* vbe_mode_is_supported() excludes the rest */
715 	switch (mi.MemoryModel) {
716 	case 0x4:
717 		gfx_state.tg_ctype = CT_INDEXED;
718 		break;
719 	case 0x6:
720 		gfx_state.tg_ctype = CT_RGB;
721 		break;
722 	}
723 
724 #define	COLOR_MASK(size, pos) (((1 << size) - 1) << pos)
725 	if (gfx_state.tg_ctype == CT_INDEXED) {
726 		gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16);
727 		gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8);
728 		gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0);
729 	} else if (vbe->VbeVersion >= 0x300) {
730 		gfx_state.tg_fb.fb_mask_red =
731 		    COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition);
732 		gfx_state.tg_fb.fb_mask_green =
733 		    COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition);
734 		gfx_state.tg_fb.fb_mask_blue =
735 		    COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition);
736 	} else {
737 		gfx_state.tg_fb.fb_mask_red =
738 		    COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition);
739 		gfx_state.tg_fb.fb_mask_green =
740 		    COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition);
741 		gfx_state.tg_fb.fb_mask_blue =
742 		    COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition);
743 	}
744 	gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red |
745 	    gfx_state.tg_fb.fb_mask_green |
746 	    gfx_state.tg_fb.fb_mask_blue);
747 
748 	if (vbe->VbeVersion >= 0x300)
749 		gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp;
750 	else
751 		gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp;
752 
753 	gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride *
754 	    bpp;
755 
756 	return (0);
757 }
758 
759 /*
760  * Verify existance of mode number or find mode by
761  * dimensions. If depth is not given, walk values 32, 24, 16, 8.
762  */
763 static int
764 vbe_find_mode_xydm(int x, int y, int depth, int m)
765 {
766 	struct modeinfoblock mi;
767 	uint16_t *farptr;
768 	uint16_t mode;
769 	int idx, nentries, i;
770 
771 	memset(vbe, 0, sizeof (*vbe));
772 	if (biosvbe_info(vbe) != VBE_SUCCESS)
773 		return (0);
774 
775 	if (m != -1)
776 		i = 8;
777 	else if (depth == -1)
778 		i = 32;
779 	else
780 		i = depth;
781 
782 	nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
783 	while (i > 0) {
784 		for (idx = 0; idx < nentries; idx++) {
785 			mode = vbe_mode_list[idx];
786 			if (mode == 0xffff)
787 				break;
788 
789 			if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
790 				continue;
791 			}
792 
793 			/* we only care about linear modes here */
794 			if (vbe_mode_is_supported(&mi) == 0)
795 				continue;
796 
797 			if (m != -1) {
798 				if (m == mode)
799 					return (mode);
800 				else
801 					continue;
802 			}
803 
804 			if (mi.XResolution == x &&
805 			    mi.YResolution == y &&
806 			    mi.BitsPerPixel == i)
807 				return (mode);
808 		}
809 		if (depth != -1)
810 			break;
811 
812 		i -= 8;
813 	}
814 
815 	return (0);
816 }
817 
818 static int
819 vbe_find_mode(char *str)
820 {
821 	int x, y, depth;
822 
823 	if (!gfx_parse_mode_str(str, &x, &y, &depth))
824 		return (0);
825 
826 	return (vbe_find_mode_xydm(x, y, depth, -1));
827 }
828 
829 static void
830 vbe_dump_mode(int modenum, struct modeinfoblock *mi)
831 {
832 	printf("0x%x=%dx%dx%d", modenum,
833 	    mi->XResolution, mi->YResolution, mi->BitsPerPixel);
834 }
835 
836 static bool
837 vbe_get_edid(edid_res_list_t *res)
838 {
839 	struct vesa_edid_info *edid_info;
840 	const uint8_t magic[] = EDID_MAGIC;
841 	int ddc_caps;
842 	bool ret = false;
843 
844 	ddc_caps = biosvbe_ddc_caps();
845 	if (ddc_caps == 0) {
846 		return (ret);
847 	}
848 
849 	edid_info = bio_alloc(sizeof (*edid_info));
850 	if (edid_info == NULL)
851 		return (ret);
852 	memset(edid_info, 0, sizeof (*edid_info));
853 
854 	if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info)))
855 		goto done;
856 
857 	if (memcmp(edid_info, magic, sizeof (magic)) != 0)
858 		goto done;
859 
860 	/* Unknown EDID version. */
861 	if (edid_info->header.version != 1)
862 		goto done;
863 
864 	ret = gfx_get_edid_resolution(edid_info, res);
865 done:
866 	bio_free(edid_info, sizeof (*edid_info));
867 	return (ret);
868 }
869 
870 static bool
871 vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight)
872 {
873 	struct vesa_flat_panel_info *fp_info;
874 	bool ret = false;
875 
876 	fp_info = bio_alloc(sizeof (*fp_info));
877 	if (fp_info == NULL)
878 		return (ret);
879 	memset(fp_info, 0, sizeof (*fp_info));
880 
881 	if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
882 		goto done;
883 
884 	*pwidth = fp_info->HSize;
885 	*pheight = fp_info->VSize;
886 	ret = true;
887 
888 done:
889 	bio_free(fp_info, sizeof (*fp_info));
890 	return (ret);
891 }
892 
893 static void
894 vbe_print_memory(unsigned vmem)
895 {
896 	char unit = 'K';
897 
898 	vmem /= 1024;
899 	if (vmem >= 10240000) {
900 		vmem /= 1048576;
901 		unit = 'G';
902 	} else if (vmem >= 10000) {
903 		vmem /= 1024;
904 		unit = 'M';
905 	}
906 	printf("Total memory: %u%cB\n", vmem, unit);
907 }
908 
909 static void
910 vbe_print_vbe_info(struct vbeinfoblock *vbep)
911 {
912 	char *oemstring = "";
913 	char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
914 
915 	if (vbep->OemStringPtr != 0)
916 		oemstring = vbe_farptr(vbep->OemStringPtr);
917 
918 	if (vbep->OemVendorNamePtr != 0)
919 		oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
920 
921 	if (vbep->OemProductNamePtr != 0)
922 		oemproductname = vbe_farptr(vbep->OemProductNamePtr);
923 
924 	if (vbep->OemProductRevPtr != 0)
925 		oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
926 
927 	printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
928 	    vbep->VbeVersion & 0xF, oemstring);
929 
930 	if (vbep->OemSoftwareRev != 0) {
931 		printf("OEM Version %d.%d, %s (%s, %s)\n",
932 		    vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
933 		    oemvendor, oemproductname, oemproductrev);
934 	}
935 	vbe_print_memory(vbep->TotalMemory << 16);
936 	printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages);
937 }
938 
939 /* List available modes, filter by depth. If depth is -1, list all. */
940 void
941 vbe_modelist(int depth)
942 {
943 	struct modeinfoblock mi;
944 	uint16_t mode;
945 	int nmodes, idx, nentries;
946 	int ddc_caps;
947 	uint32_t width, height;
948 	bool edid = false;
949 	edid_res_list_t res;
950 	struct resolution *rp;
951 
952 	if (!vbe_check())
953 		return;
954 
955 	ddc_caps = biosvbe_ddc_caps();
956 	if (ddc_caps & 3) {
957 		printf("DDC");
958 		if (ddc_caps & 1)
959 			printf(" [DDC1]");
960 		if (ddc_caps & 2)
961 			printf(" [DDC2]");
962 
963 		TAILQ_INIT(&res);
964 		edid = vbe_get_edid(&res);
965 		if (edid) {
966 			printf(": EDID");
967 			while ((rp = TAILQ_FIRST(&res)) != NULL) {
968 				printf(" %dx%d", rp->width, rp->height);
969 				TAILQ_REMOVE(&res, rp, next);
970 				free(rp);
971 			}
972 			printf("\n");
973 		} else {
974 			printf(": no EDID information\n");
975 		}
976 	}
977 	if (!edid)
978 		if (vbe_get_flatpanel(&width, &height))
979 			printf(": Panel %dx%d\n", width, height);
980 
981 	nmodes = 0;
982 	memset(vbe, 0, sizeof (*vbe));
983 	memcpy(vbe->VbeSignature, "VBE2", 4);
984 	if (biosvbe_info(vbe) != VBE_SUCCESS)
985 		goto done;
986 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
987 		goto done;
988 
989 	vbe_print_vbe_info(vbe);
990 	printf("Modes: ");
991 
992 	nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
993 	for (idx = 0; idx < nentries; idx++) {
994 		mode = vbe_mode_list[idx];
995 		if (mode == 0xffff)
996 			break;
997 
998 		if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
999 			continue;
1000 
1001 		/* we only care about linear modes here */
1002 		if (vbe_mode_is_supported(&mi) == 0)
1003 			continue;
1004 
1005 		/* apply requested filter */
1006 		if (depth != -1 && mi.BitsPerPixel != depth)
1007 			continue;
1008 
1009 		if (nmodes % 4 == 0)
1010 			printf("\n");
1011 		else
1012 			printf("  ");
1013 
1014 		vbe_dump_mode(mode, &mi);
1015 		nmodes++;
1016 	}
1017 
1018 done:
1019 	if (nmodes == 0)
1020 		printf("none found");
1021 	printf("\n");
1022 }
1023 
1024 static void
1025 vbe_print_mode(bool verbose __unused)
1026 {
1027 	int nc, mode, i, rc;
1028 
1029 	nc = NCOLORS;
1030 
1031 	memset(vbe, 0, sizeof (*vbe));
1032 	if (biosvbe_info(vbe) != VBE_SUCCESS)
1033 		return;
1034 
1035 	vbe_print_vbe_info(vbe);
1036 
1037 	if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
1038 		printf("Error getting current VBE mode\n");
1039 		return;
1040 	}
1041 
1042 	if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
1043 	    vbe_mode_is_supported(vbe_mode) == 0) {
1044 		printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
1045 		return;
1046 	}
1047 
1048 	printf("\nCurrent VBE mode: ");
1049 	vbe_dump_mode(mode, vbe_mode);
1050 	printf("\n");
1051 
1052 	printf("%ux%ux%u, stride=%u\n",
1053 	    gfx_state.tg_fb.fb_width,
1054 	    gfx_state.tg_fb.fb_height,
1055 	    gfx_state.tg_fb.fb_bpp,
1056 	    gfx_state.tg_fb.fb_stride *
1057 	    (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY));
1058 	printf("    frame buffer: address=%jx, size=%jx\n",
1059 	    (uintmax_t)gfx_state.tg_fb.fb_addr,
1060 	    (uintmax_t)gfx_state.tg_fb.fb_size);
1061 
1062 	if (vbe_mode->MemoryModel == 0x6) {
1063 		printf("    color mask: R=%08x, G=%08x, B=%08x\n",
1064 		    gfx_state.tg_fb.fb_mask_red,
1065 		    gfx_state.tg_fb.fb_mask_green,
1066 		    gfx_state.tg_fb.fb_mask_blue);
1067 		pager_open();
1068 		for (i = 0; i < nc; i++) {
1069 			printf("%d: R=%02x, G=%02x, B=%02x %08x", i,
1070 			    (cmap[i] & gfx_state.tg_fb.fb_mask_red) >>
1071 			    ffs(gfx_state.tg_fb.fb_mask_red) - 1,
1072 			    (cmap[i] & gfx_state.tg_fb.fb_mask_green) >>
1073 			    ffs(gfx_state.tg_fb.fb_mask_green) - 1,
1074 			    (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >>
1075 			    ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]);
1076 			if (pager_output("\n") != 0)
1077 				break;
1078 		}
1079 		pager_close();
1080 		return;
1081 	}
1082 
1083 	mode = 1;	/* get DAC palette width */
1084 	rc = biosvbe_palette_format(&mode);
1085 	if (rc != VBE_SUCCESS)
1086 		return;
1087 
1088 	printf("    palette format: %x bits per primary\n", mode);
1089 	if (pe8 == NULL)
1090 		return;
1091 
1092 	pager_open();
1093 	for (i = 0; i < nc; i++) {
1094 		printf("%d: R=%02x, G=%02x, B=%02x", i,
1095 		    pe8[i].Red, pe8[i].Green, pe8[i].Blue);
1096 		if (pager_output("\n") != 0)
1097 			break;
1098 	}
1099 	pager_close();
1100 }
1101 
1102 /*
1103  * Try EDID preferred mode, if EDID or the suggested mode is not available,
1104  * then try flat panel information.
1105  * Fall back to VBE_DEFAULT_MODE.
1106  */
1107 int
1108 vbe_default_mode(void)
1109 {
1110 	edid_res_list_t res;
1111 	struct resolution *rp;
1112 	int modenum;
1113 	uint32_t width, height;
1114 
1115 	modenum = 0;
1116 	vbe_get_max_resolution(&width, &height);
1117 	if (width != 0 && height != 0)
1118 		modenum = vbe_find_mode_xydm(width, height, -1, -1);
1119 
1120 	TAILQ_INIT(&res);
1121 	if (vbe_get_edid(&res)) {
1122 		while ((rp = TAILQ_FIRST(&res)) != NULL) {
1123 			if (modenum == 0) {
1124 				modenum = vbe_find_mode_xydm(
1125 				    rp->width, rp->height, -1, -1);
1126 			}
1127 			TAILQ_REMOVE(&res, rp, next);
1128 			free(rp);
1129 		}
1130 	}
1131 
1132 	if (modenum == 0 &&
1133 	    vbe_get_flatpanel(&width, &height)) {
1134 		modenum = vbe_find_mode_xydm(width, height, -1, -1);
1135 	}
1136 
1137 	/* Still no mode? Fall back to default. */
1138 	if (modenum == 0)
1139 		modenum = vbe_find_mode(VBE_DEFAULT_MODE);
1140 	return (modenum);
1141 }
1142 
1143 COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa);
1144 
1145 int
1146 command_vesa(int argc, char *argv[])
1147 {
1148 	char *arg, *cp;
1149 	int modenum = -1, n;
1150 
1151 	if (!vbe_check())
1152 		return (CMD_OK);
1153 
1154 	if (argc < 2)
1155 		goto usage;
1156 
1157 	if (strcmp(argv[1], "list") == 0) {
1158 		n = -1;
1159 		if (argc != 2 && argc != 3)
1160 			goto usage;
1161 
1162 		if (argc == 3) {
1163 			arg = argv[2];
1164 			errno = 0;
1165 			n = strtoul(arg, &cp, 0);
1166 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
1167 				snprintf(command_errbuf,
1168 				    sizeof (command_errbuf),
1169 				    "depth should be an integer");
1170 				return (CMD_ERROR);
1171 			}
1172 		}
1173 		vbe_modelist(n);
1174 		return (CMD_OK);
1175 	}
1176 
1177 	if (strcmp(argv[1], "get") == 0) {
1178 		bool verbose = false;
1179 
1180 		if (argc != 2) {
1181 			if (argc > 3 || strcmp(argv[2], "-v") != 0)
1182 				goto usage;
1183 			verbose = true;
1184 		}
1185 		vbe_print_mode(verbose);
1186 		return (CMD_OK);
1187 	}
1188 
1189 	if (strcmp(argv[1], "off") == 0) {
1190 		if (argc != 2)
1191 			goto usage;
1192 
1193 		if (gfx_state.tg_mode == VGA_TEXT_MODE)
1194 			return (CMD_OK);
1195 
1196 		reset_font_flags();
1197 		bios_text_font(true);
1198 		bios_set_text_mode(VGA_TEXT_MODE);
1199 		cons_update_mode(false);
1200 		return (CMD_OK);
1201 	}
1202 
1203 	if (strcmp(argv[1], "on") == 0) {
1204 		if (argc != 2)
1205 			goto usage;
1206 
1207 		modenum = vbe_default_mode();
1208 		if (modenum == 0) {
1209 			snprintf(command_errbuf, sizeof (command_errbuf),
1210 			    "%s: no suitable VBE mode number found", argv[0]);
1211 			return (CMD_ERROR);
1212 		}
1213 	} else if (strcmp(argv[1], "set") == 0) {
1214 		if (argc != 3)
1215 			goto usage;
1216 
1217 		if (strncmp(argv[2], "0x", 2) == 0) {
1218 			arg = argv[2];
1219 			errno = 0;
1220 			n = strtoul(arg, &cp, 0);
1221 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
1222 				snprintf(command_errbuf,
1223 				    sizeof (command_errbuf),
1224 				    "mode should be an integer");
1225 				return (CMD_ERROR);
1226 			}
1227 			modenum = vbe_find_mode_xydm(0, 0, 0, n);
1228 		} else if (strchr(argv[2], 'x') != NULL) {
1229 			modenum = vbe_find_mode(argv[2]);
1230 		}
1231 	} else {
1232 		goto usage;
1233 	}
1234 
1235 	if (modenum == 0) {
1236 		snprintf(command_errbuf, sizeof (command_errbuf),
1237 		    "%s: mode %s not supported by firmware\n",
1238 		    argv[0], argv[2]);
1239 		return (CMD_ERROR);
1240 	}
1241 
1242 	if (modenum >= VESA_MODE_BASE) {
1243 		if (gfx_state.tg_mode != modenum) {
1244 			reset_font_flags();
1245 			bios_text_font(false);
1246 			vbe_set_mode(modenum);
1247 			cons_update_mode(true);
1248 		}
1249 		return (CMD_OK);
1250 	} else {
1251 		snprintf(command_errbuf, sizeof (command_errbuf),
1252 		    "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
1253 		return (CMD_ERROR);
1254 	}
1255 
1256 usage:
1257 	snprintf(command_errbuf, sizeof (command_errbuf),
1258 	    "usage: %s on | off | get | list [depth] | "
1259 	    "set <display or VBE mode number>", argv[0]);
1260 	return (CMD_ERROR);
1261 }
1262