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