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