xref: /freebsd/stand/i386/libi386/vbe.c (revision 3e15b01d)
13630506bSToomas Soome /*-
23630506bSToomas Soome  * SPDX-License-Identifier: BSD-2-Clause
33630506bSToomas Soome  *
43630506bSToomas Soome  * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
53630506bSToomas Soome  * All rights reserved.
63630506bSToomas Soome  * Copyright 2020 Toomas Soome
73630506bSToomas Soome  *
83630506bSToomas Soome  * Redistribution and use in source and binary forms, with or without
93630506bSToomas Soome  * modification, are permitted provided that the following conditions
103630506bSToomas Soome  * are met:
113630506bSToomas Soome  * 1. Redistributions of source code must retain the above copyright
123630506bSToomas Soome  *    notice, this list of conditions and the following disclaimer.
133630506bSToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
143630506bSToomas Soome  *    notice, this list of conditions and the following disclaimer in the
153630506bSToomas Soome  *    documentation and/or other materials provided with the distribution.
163630506bSToomas Soome  *
173630506bSToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
183630506bSToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
193630506bSToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
203630506bSToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
213630506bSToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
223630506bSToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
233630506bSToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
243630506bSToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
253630506bSToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
263630506bSToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
273630506bSToomas Soome  * SUCH DAMAGE.
283630506bSToomas Soome  */
293630506bSToomas Soome 
303630506bSToomas Soome #include <stand.h>
313630506bSToomas Soome #include <sys/param.h>
323630506bSToomas Soome #include <machine/psl.h>
333630506bSToomas Soome #include <machine/cpufunc.h>
343630506bSToomas Soome #include <stdbool.h>
353630506bSToomas Soome #include <bootstrap.h>
363630506bSToomas Soome #include <btxv86.h>
373630506bSToomas Soome #include <gfx_fb.h>
383630506bSToomas Soome #include <dev/vt/hw/vga/vt_vga_reg.h>
393630506bSToomas Soome #include "libi386.h"
403630506bSToomas Soome #include "vbe.h"
413630506bSToomas Soome 
423630506bSToomas Soome /*
433630506bSToomas Soome  * VESA BIOS Extensions routines
443630506bSToomas Soome  */
453630506bSToomas Soome 
463630506bSToomas Soome static struct vbeinfoblock *vbe;
473630506bSToomas Soome static struct modeinfoblock *vbe_mode;
48ad1ebbe5SToomas Soome 
49ad1ebbe5SToomas Soome static uint16_t *vbe_mode_list;
50ad1ebbe5SToomas Soome static size_t vbe_mode_list_size;
51becaac39SToomas Soome struct vesa_edid_info *edid_info = NULL;
52ad1ebbe5SToomas Soome 
533630506bSToomas Soome /* The default VGA color palette format is 6 bits per primary color. */
543630506bSToomas Soome int palette_format = 6;
553630506bSToomas Soome 
563630506bSToomas Soome #define	VESA_MODE_BASE	0x100
573630506bSToomas Soome 
583630506bSToomas Soome /*
593630506bSToomas Soome  * palette array for 8-bit indexed colors. In this case, cmap does store
603630506bSToomas Soome  * index and pe8 does store actual RGB. This is needed because we may
613630506bSToomas Soome  * not be able to read palette data from hardware.
623630506bSToomas Soome  */
633630506bSToomas Soome struct paletteentry *pe8 = NULL;
643630506bSToomas Soome 
653630506bSToomas Soome static struct named_resolution {
663630506bSToomas Soome 	const char *name;
673630506bSToomas Soome 	const char *alias;
683630506bSToomas Soome 	unsigned int width;
693630506bSToomas Soome 	unsigned int height;
703630506bSToomas Soome } resolutions[] = {
713630506bSToomas Soome 	{
723630506bSToomas Soome 		.name = "480p",
733630506bSToomas Soome 		.width = 640,
743630506bSToomas Soome 		.height = 480,
753630506bSToomas Soome 	},
763630506bSToomas Soome 	{
773630506bSToomas Soome 		.name = "720p",
783630506bSToomas Soome 		.width = 1280,
793630506bSToomas Soome 		.height = 720,
803630506bSToomas Soome 	},
813630506bSToomas Soome 	{
823630506bSToomas Soome 		.name = "1080p",
833630506bSToomas Soome 		.width = 1920,
843630506bSToomas Soome 		.height = 1080,
853630506bSToomas Soome 	},
863630506bSToomas Soome 	{
877edbf69bSDag-Erling Smørgrav 		.name = "1440p",
887edbf69bSDag-Erling Smørgrav 		.width = 2560,
897edbf69bSDag-Erling Smørgrav 		.height = 1440,
907edbf69bSDag-Erling Smørgrav 	},
917edbf69bSDag-Erling Smørgrav 	{
923630506bSToomas Soome 		.name = "2160p",
933630506bSToomas Soome 		.alias = "4k",
943630506bSToomas Soome 		.width = 3840,
953630506bSToomas Soome 		.height = 2160,
963630506bSToomas Soome 	},
973630506bSToomas Soome 	{
983630506bSToomas Soome 		.name = "5k",
993630506bSToomas Soome 		.width = 5120,
1003630506bSToomas Soome 		.height = 2880,
1013630506bSToomas Soome 	}
1023630506bSToomas Soome };
1033630506bSToomas Soome 
1043630506bSToomas Soome static bool
vbe_resolution_compare(struct named_resolution * res,const char * cmp)1053630506bSToomas Soome vbe_resolution_compare(struct named_resolution *res, const char *cmp)
1063630506bSToomas Soome {
1073630506bSToomas Soome 
1083630506bSToomas Soome 	if (strcasecmp(res->name, cmp) == 0)
1093630506bSToomas Soome 		return (true);
1103630506bSToomas Soome 	if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
1113630506bSToomas Soome 		return (true);
1123630506bSToomas Soome 	return (false);
1133630506bSToomas Soome }
1143630506bSToomas Soome 
1153630506bSToomas Soome static void
vbe_get_max_resolution(int * width,int * height)1163630506bSToomas Soome vbe_get_max_resolution(int *width, int *height)
1173630506bSToomas Soome {
1183630506bSToomas Soome 	struct named_resolution *res;
1193630506bSToomas Soome 	char *maxres;
1203630506bSToomas Soome 	char *height_start, *width_start;
1213630506bSToomas Soome 	int idx;
1223630506bSToomas Soome 
1233630506bSToomas Soome 	*width = *height = 0;
1243630506bSToomas Soome 	maxres = getenv("vbe_max_resolution");
1253630506bSToomas Soome 	/* No max_resolution set? Bail out; choose highest resolution */
1263630506bSToomas Soome 	if (maxres == NULL)
1273630506bSToomas Soome 		return;
1283630506bSToomas Soome 	/* See if it matches one of our known resolutions */
1293630506bSToomas Soome 	for (idx = 0; idx < nitems(resolutions); ++idx) {
1303630506bSToomas Soome 		res = &resolutions[idx];
1313630506bSToomas Soome 		if (vbe_resolution_compare(res, maxres)) {
1323630506bSToomas Soome 			*width = res->width;
1333630506bSToomas Soome 			*height = res->height;
1343630506bSToomas Soome 			return;
1353630506bSToomas Soome 		}
1363630506bSToomas Soome 	}
1373630506bSToomas Soome 	/* Not a known resolution, try to parse it; make a copy we can modify */
1383630506bSToomas Soome 	maxres = strdup(maxres);
1393630506bSToomas Soome 	if (maxres == NULL)
1403630506bSToomas Soome 		return;
1413630506bSToomas Soome 	height_start = strchr(maxres, 'x');
1423630506bSToomas Soome 	if (height_start == NULL) {
1433630506bSToomas Soome 		free(maxres);
1443630506bSToomas Soome 		return;
1453630506bSToomas Soome 	}
1463630506bSToomas Soome 	width_start = maxres;
1473630506bSToomas Soome 	*height_start++ = 0;
1483630506bSToomas Soome 	/* Errors from this will effectively mean "no max" */
1493630506bSToomas Soome 	*width = (int)strtol(width_start, NULL, 0);
1503630506bSToomas Soome 	*height = (int)strtol(height_start, NULL, 0);
1513630506bSToomas Soome 	free(maxres);
1523630506bSToomas Soome }
1533630506bSToomas Soome 
1543630506bSToomas Soome int
vga_get_reg(int reg,int index)1553630506bSToomas Soome vga_get_reg(int reg, int index)
1563630506bSToomas Soome {
1573630506bSToomas Soome 	return (inb(reg + index));
1583630506bSToomas Soome }
1593630506bSToomas Soome 
1603630506bSToomas Soome int
vga_get_atr(int reg,int i)1613630506bSToomas Soome vga_get_atr(int reg, int i)
1623630506bSToomas Soome {
1633630506bSToomas Soome 	int ret;
1643630506bSToomas Soome 
1653630506bSToomas Soome 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
1663630506bSToomas Soome 	outb(reg + VGA_AC_WRITE, i);
1673630506bSToomas Soome 	ret = inb(reg + VGA_AC_READ);
1683630506bSToomas Soome 
1693630506bSToomas Soome 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
1703630506bSToomas Soome 
1713630506bSToomas Soome 	return (ret);
1723630506bSToomas Soome }
1733630506bSToomas Soome 
1743630506bSToomas Soome void
vga_set_atr(int reg,int i,int v)1753630506bSToomas Soome vga_set_atr(int reg, int i, int v)
1763630506bSToomas Soome {
1773630506bSToomas Soome 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
1783630506bSToomas Soome 	outb(reg + VGA_AC_WRITE, i);
1793630506bSToomas Soome 	outb(reg + VGA_AC_WRITE, v);
1803630506bSToomas Soome 
1813630506bSToomas Soome 	(void) inb(reg + VGA_GEN_INPUT_STAT_1);
1823630506bSToomas Soome }
1833630506bSToomas Soome 
1843630506bSToomas Soome void
vga_set_indexed(int reg,int indexreg,int datareg,uint8_t index,uint8_t val)1853630506bSToomas Soome vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
1863630506bSToomas Soome {
1873630506bSToomas Soome 	outb(reg + indexreg, index);
1883630506bSToomas Soome 	outb(reg + datareg, val);
1893630506bSToomas Soome }
1903630506bSToomas Soome 
1913630506bSToomas Soome int
vga_get_indexed(int reg,int indexreg,int datareg,uint8_t index)1923630506bSToomas Soome vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
1933630506bSToomas Soome {
1943630506bSToomas Soome 	outb(reg + indexreg, index);
1953630506bSToomas Soome 	return (inb(reg + datareg));
1963630506bSToomas Soome }
1973630506bSToomas Soome 
1983630506bSToomas Soome int
vga_get_crtc(int reg,int i)1993630506bSToomas Soome vga_get_crtc(int reg, int i)
2003630506bSToomas Soome {
2013630506bSToomas Soome 	return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
2023630506bSToomas Soome }
2033630506bSToomas Soome 
2043630506bSToomas Soome void
vga_set_crtc(int reg,int i,int v)2053630506bSToomas Soome vga_set_crtc(int reg, int i, int v)
2063630506bSToomas Soome {
2073630506bSToomas Soome 	vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
2083630506bSToomas Soome }
2093630506bSToomas Soome 
2103630506bSToomas Soome int
vga_get_seq(int reg,int i)2113630506bSToomas Soome vga_get_seq(int reg, int i)
2123630506bSToomas Soome {
2133630506bSToomas Soome 	return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i));
2143630506bSToomas Soome }
2153630506bSToomas Soome 
2163630506bSToomas Soome void
vga_set_seq(int reg,int i,int v)2173630506bSToomas Soome vga_set_seq(int reg, int i, int v)
2183630506bSToomas Soome {
2193630506bSToomas Soome 	vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v);
2203630506bSToomas Soome }
2213630506bSToomas Soome 
2223630506bSToomas Soome int
vga_get_grc(int reg,int i)2233630506bSToomas Soome vga_get_grc(int reg, int i)
2243630506bSToomas Soome {
2253630506bSToomas Soome 	return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i));
2263630506bSToomas Soome }
2273630506bSToomas Soome 
2283630506bSToomas Soome void
vga_set_grc(int reg,int i,int v)2293630506bSToomas Soome vga_set_grc(int reg, int i, int v)
2303630506bSToomas Soome {
2313630506bSToomas Soome 	vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v);
2323630506bSToomas Soome }
2333630506bSToomas Soome 
23458661b3bSToomas Soome /*
23558661b3bSToomas Soome  * Return true when this controller is VGA compatible.
23658661b3bSToomas Soome  */
23758661b3bSToomas Soome bool
vbe_is_vga(void)23858661b3bSToomas Soome vbe_is_vga(void)
23958661b3bSToomas Soome {
24058661b3bSToomas Soome 	if (vbe == NULL)
24158661b3bSToomas Soome 		return (false);
24258661b3bSToomas Soome 
24358661b3bSToomas Soome 	return ((vbe->Capabilities & VBE_CAP_NONVGA) == 0);
24458661b3bSToomas Soome }
24558661b3bSToomas Soome 
2463630506bSToomas Soome /* Actually assuming mode 3. */
2473630506bSToomas Soome void
bios_set_text_mode(int mode)2483630506bSToomas Soome bios_set_text_mode(int mode)
2493630506bSToomas Soome {
2503630506bSToomas Soome 	int atr;
2513630506bSToomas Soome 
2523630506bSToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
2533630506bSToomas Soome 		int m;
2543630506bSToomas Soome 
2553630506bSToomas Soome 		/*
2563630506bSToomas Soome 		 * The mode change should reset the palette format to
2573630506bSToomas Soome 		 * 6 bits, but apparently some systems do fail with 8-bit
2583630506bSToomas Soome 		 * palette, so we switch to 6-bit here.
2593630506bSToomas Soome 		 */
2603630506bSToomas Soome 		m = 0x0600;
2613630506bSToomas Soome 		(void) biosvbe_palette_format(&m);
2623630506bSToomas Soome 		palette_format = m;
2633630506bSToomas Soome 	}
2643630506bSToomas Soome 	v86.ctl = V86_FLAGS;
2653630506bSToomas Soome 	v86.addr = 0x10;
2663630506bSToomas Soome 	v86.eax = mode;				/* set VGA text mode */
2673630506bSToomas Soome 	v86int();
2683630506bSToomas Soome 	atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL);
2693630506bSToomas Soome 	atr &= ~VGA_AC_MC_BI;
2703630506bSToomas Soome 	atr &= ~VGA_AC_MC_ELG;
2713630506bSToomas Soome 	vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr);
2723630506bSToomas Soome 
2733630506bSToomas Soome 	gfx_state.tg_mode = mode;
2743630506bSToomas Soome 	gfx_state.tg_fb_type = FB_TEXT;
2753630506bSToomas Soome 	gfx_state.tg_fb.fb_height = TEXT_ROWS;
2763630506bSToomas Soome 	gfx_state.tg_fb.fb_width = TEXT_COLS;
2773630506bSToomas Soome 
2783630506bSToomas Soome 	gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16;
2793630506bSToomas Soome 	gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8;
2803630506bSToomas Soome 	gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0;
2813630506bSToomas Soome 	gfx_state.tg_ctype = CT_INDEXED;
282babda095SToomas Soome 	env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL);
2833630506bSToomas Soome }
2843630506bSToomas Soome 
2853630506bSToomas Soome /* Function 00h - Return VBE Controller Information */
2863630506bSToomas Soome static int
biosvbe_info(struct vbeinfoblock * vbep)2873630506bSToomas Soome biosvbe_info(struct vbeinfoblock *vbep)
2883630506bSToomas Soome {
2893630506bSToomas Soome 	struct vbeinfoblock *rvbe;
2903630506bSToomas Soome 	int ret;
2913630506bSToomas Soome 
2923630506bSToomas Soome 	if (vbep == NULL)
2933630506bSToomas Soome 		return (VBE_FAILED);
2943630506bSToomas Soome 
2953630506bSToomas Soome 	rvbe = bio_alloc(sizeof(*rvbe));
2963630506bSToomas Soome 	if (rvbe == NULL)
2973630506bSToomas Soome 		return (VBE_FAILED);
2983630506bSToomas Soome 
2993630506bSToomas Soome 	/* Now check if we have vesa. */
3003630506bSToomas Soome 	memset(rvbe, 0, sizeof (*vbe));
3013630506bSToomas Soome 	memcpy(rvbe->VbeSignature, "VBE2", 4);
3023630506bSToomas Soome 
3033630506bSToomas Soome 	v86.ctl = V86_FLAGS;
3043630506bSToomas Soome 	v86.addr = 0x10;
3053630506bSToomas Soome 	v86.eax = 0x4f00;
3063630506bSToomas Soome 	v86.es = VTOPSEG(rvbe);
3073630506bSToomas Soome 	v86.edi = VTOPOFF(rvbe);
3083630506bSToomas Soome 	v86int();
3093630506bSToomas Soome 	ret = v86.eax & 0xffff;
3103630506bSToomas Soome 
3113630506bSToomas Soome 	if (ret != VBE_SUCCESS)
3123630506bSToomas Soome 		goto done;
3133630506bSToomas Soome 
3143630506bSToomas Soome 	if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) {
3153630506bSToomas Soome 		ret = VBE_NOTSUP;
3163630506bSToomas Soome 		goto done;
3173630506bSToomas Soome 	}
3183630506bSToomas Soome 	bcopy(rvbe, vbep, sizeof(*vbep));
3193630506bSToomas Soome done:
3203630506bSToomas Soome 	bio_free(rvbe, sizeof(*rvbe));
3213630506bSToomas Soome 	return (ret);
3223630506bSToomas Soome }
3233630506bSToomas Soome 
3243630506bSToomas Soome /* Function 01h - Return VBE Mode Information */
3253630506bSToomas Soome static int
biosvbe_get_mode_info(int mode,struct modeinfoblock * mi)3263630506bSToomas Soome biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
3273630506bSToomas Soome {
3283630506bSToomas Soome 	struct modeinfoblock *rmi;
3293630506bSToomas Soome 	int ret;
3303630506bSToomas Soome 
3313630506bSToomas Soome 	rmi = bio_alloc(sizeof(*rmi));
3323630506bSToomas Soome 	if (rmi == NULL)
3333630506bSToomas Soome 		return (VBE_FAILED);
3343630506bSToomas Soome 
3353630506bSToomas Soome 	v86.ctl = V86_FLAGS;
3363630506bSToomas Soome 	v86.addr = 0x10;
3373630506bSToomas Soome 	v86.eax = 0x4f01;
3383630506bSToomas Soome 	v86.ecx = mode;
3393630506bSToomas Soome 	v86.es = VTOPSEG(rmi);
3403630506bSToomas Soome 	v86.edi = VTOPOFF(rmi);
3413630506bSToomas Soome 	v86int();
3423630506bSToomas Soome 
3433630506bSToomas Soome 	ret = v86.eax & 0xffff;
3443630506bSToomas Soome 	if (ret != VBE_SUCCESS)
3453630506bSToomas Soome 		goto done;
3463630506bSToomas Soome 	bcopy(rmi, mi, sizeof(*rmi));
3473630506bSToomas Soome done:
3483630506bSToomas Soome 	bio_free(rmi, sizeof(*rmi));
3493630506bSToomas Soome 	return (ret);
3503630506bSToomas Soome }
3513630506bSToomas Soome 
3523630506bSToomas Soome /* Function 02h - Set VBE Mode */
3533630506bSToomas Soome static int
biosvbe_set_mode(int mode,struct crtciinfoblock * ci)3543630506bSToomas Soome biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
3553630506bSToomas Soome {
3563630506bSToomas Soome 	int rv;
3573630506bSToomas Soome 
3583630506bSToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
3593630506bSToomas Soome 		int m;
3603630506bSToomas Soome 
3613630506bSToomas Soome 		/*
3623630506bSToomas Soome 		 * The mode change should reset the palette format to
3633630506bSToomas Soome 		 * 6 bits, but apparently some systems do fail with 8-bit
3643630506bSToomas Soome 		 * palette, so we switch to 6-bit here.
3653630506bSToomas Soome 		 */
3663630506bSToomas Soome 		m = 0x0600;
3673630506bSToomas Soome 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
3683630506bSToomas Soome 			palette_format = m;
3693630506bSToomas Soome 	}
3703630506bSToomas Soome 	v86.ctl = V86_FLAGS;
3713630506bSToomas Soome 	v86.addr = 0x10;
3723630506bSToomas Soome 	v86.eax = 0x4f02;
3733630506bSToomas Soome 	v86.ebx = mode | 0x4000;	/* set linear FB bit */
3743630506bSToomas Soome 	v86.es = VTOPSEG(ci);
3753630506bSToomas Soome 	v86.edi = VTOPOFF(ci);
3763630506bSToomas Soome 	v86int();
3773630506bSToomas Soome 	rv = v86.eax & 0xffff;
3783630506bSToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
3793630506bSToomas Soome 		int m;
3803630506bSToomas Soome 
3813630506bSToomas Soome 		/* Switch to 8-bits per primary color. */
3823630506bSToomas Soome 		m = 0x0800;
3833630506bSToomas Soome 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
3843630506bSToomas Soome 			palette_format = m;
3853630506bSToomas Soome 	}
386babda095SToomas Soome 	env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL);
3873630506bSToomas Soome 	return (rv);
3883630506bSToomas Soome }
3893630506bSToomas Soome 
3903630506bSToomas Soome /* Function 03h - Get VBE Mode */
3913630506bSToomas Soome static int
biosvbe_get_mode(int * mode)3923630506bSToomas Soome biosvbe_get_mode(int *mode)
3933630506bSToomas Soome {
3943630506bSToomas Soome 	v86.ctl = V86_FLAGS;
3953630506bSToomas Soome 	v86.addr = 0x10;
3963630506bSToomas Soome 	v86.eax = 0x4f03;
3973630506bSToomas Soome 	v86int();
3983630506bSToomas Soome 	*mode = v86.ebx & 0x3fff;	/* Bits 0-13 */
3993630506bSToomas Soome 	return (v86.eax & 0xffff);
4003630506bSToomas Soome }
4013630506bSToomas Soome 
4023630506bSToomas Soome /* Function 08h - Set/Get DAC Palette Format */
4033630506bSToomas Soome int
biosvbe_palette_format(int * format)4043630506bSToomas Soome biosvbe_palette_format(int *format)
4053630506bSToomas Soome {
4063630506bSToomas Soome 	v86.ctl = V86_FLAGS;
4073630506bSToomas Soome 	v86.addr = 0x10;
4083630506bSToomas Soome 	v86.eax = 0x4f08;
4093630506bSToomas Soome 	v86.ebx = *format;
4103630506bSToomas Soome 	v86int();
4113630506bSToomas Soome 	*format = (v86.ebx >> 8) & 0xff;
4123630506bSToomas Soome 	return (v86.eax & 0xffff);
4133630506bSToomas Soome }
4143630506bSToomas Soome 
4153630506bSToomas Soome /* Function 09h - Set/Get Palette Data */
4163630506bSToomas Soome static int
biosvbe_palette_data(int mode,int reg,struct paletteentry * pe)4173630506bSToomas Soome biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
4183630506bSToomas Soome {
4193630506bSToomas Soome 	v86.ctl = V86_FLAGS;
4203630506bSToomas Soome 	v86.addr = 0x10;
4213630506bSToomas Soome 	v86.eax = 0x4f09;
4223630506bSToomas Soome 	v86.ebx = mode;
4233630506bSToomas Soome 	v86.edx = reg;
4243630506bSToomas Soome 	v86.ecx = 1;
4253630506bSToomas Soome 	v86.es = VTOPSEG(pe);
4263630506bSToomas Soome 	v86.edi = VTOPOFF(pe);
4273630506bSToomas Soome 	v86int();
4283630506bSToomas Soome 	return (v86.eax & 0xffff);
4293630506bSToomas Soome }
4303630506bSToomas Soome 
4313630506bSToomas Soome /*
4323630506bSToomas Soome  * Function 15h BL=00h - Report VBE/DDC Capabilities
4333630506bSToomas Soome  *
4343630506bSToomas Soome  * int biosvbe_ddc_caps(void)
4353630506bSToomas Soome  * return: VBE/DDC capabilities
4363630506bSToomas Soome  */
4373630506bSToomas Soome static int
biosvbe_ddc_caps(void)4383630506bSToomas Soome biosvbe_ddc_caps(void)
4393630506bSToomas Soome {
4403630506bSToomas Soome 	v86.ctl = V86_FLAGS;
4413630506bSToomas Soome 	v86.addr = 0x10;
4423630506bSToomas Soome 	v86.eax = 0x4f15;	/* display identification extensions */
4433630506bSToomas Soome 	v86.ebx = 0;		/* report DDC capabilities */
4443630506bSToomas Soome 	v86.ecx = 0;		/* controller unit number (00h = primary) */
4453630506bSToomas Soome 	v86.es = 0;
4463630506bSToomas Soome 	v86.edi = 0;
4473630506bSToomas Soome 	v86int();
4483630506bSToomas Soome 	if (VBE_ERROR(v86.eax & 0xffff))
4493630506bSToomas Soome 		return (0);
4503630506bSToomas Soome 	return (v86.ebx & 0xffff);
4513630506bSToomas Soome }
4523630506bSToomas Soome 
4533630506bSToomas Soome /* Function 11h BL=01h - Flat Panel status */
4543630506bSToomas Soome static int
biosvbe_ddc_read_flat_panel_info(void * buf)4553630506bSToomas Soome biosvbe_ddc_read_flat_panel_info(void *buf)
4563630506bSToomas Soome {
4573630506bSToomas Soome 	v86.ctl = V86_FLAGS;
4583630506bSToomas Soome 	v86.addr = 0x10;
4593630506bSToomas Soome 	v86.eax = 0x4f11;	/* Flat Panel Interface extensions */
4603630506bSToomas Soome 	v86.ebx = 1;		/* Return Flat Panel Information */
4613630506bSToomas Soome 	v86.es = VTOPSEG(buf);
4623630506bSToomas Soome 	v86.edi = VTOPOFF(buf);
4633630506bSToomas Soome 	v86int();
4643630506bSToomas Soome 	return (v86.eax & 0xffff);
4653630506bSToomas Soome }
4663630506bSToomas Soome 
4673630506bSToomas Soome /* Function 15h BL=01h - Read EDID */
4683630506bSToomas Soome static int
biosvbe_ddc_read_edid(int blockno,void * buf)4693630506bSToomas Soome biosvbe_ddc_read_edid(int blockno, void *buf)
4703630506bSToomas Soome {
4713630506bSToomas Soome 	v86.ctl = V86_FLAGS;
4723630506bSToomas Soome 	v86.addr = 0x10;
4733630506bSToomas Soome 	v86.eax = 0x4f15;	/* display identification extensions */
4743630506bSToomas Soome 	v86.ebx = 1;		/* read EDID */
4753630506bSToomas Soome 	v86.ecx = 0;		/* controller unit number (00h = primary) */
4763630506bSToomas Soome 	v86.edx = blockno;
4773630506bSToomas Soome 	v86.es = VTOPSEG(buf);
4783630506bSToomas Soome 	v86.edi = VTOPOFF(buf);
4793630506bSToomas Soome 	v86int();
4803630506bSToomas Soome 	return (v86.eax & 0xffff);
4813630506bSToomas Soome }
4823630506bSToomas Soome 
4833630506bSToomas Soome static int
vbe_mode_is_supported(struct modeinfoblock * mi)4843630506bSToomas Soome vbe_mode_is_supported(struct modeinfoblock *mi)
4853630506bSToomas Soome {
4863630506bSToomas Soome 	if ((mi->ModeAttributes & 0x01) == 0)
4873630506bSToomas Soome 		return (0);	/* mode not supported by hardware */
4883630506bSToomas Soome 	if ((mi->ModeAttributes & 0x08) == 0)
4893630506bSToomas Soome 		return (0);	/* linear fb not available */
4903630506bSToomas Soome 	if ((mi->ModeAttributes & 0x10) == 0)
4913630506bSToomas Soome 		return (0);	/* text mode */
4923630506bSToomas Soome 	if (mi->NumberOfPlanes != 1)
4933630506bSToomas Soome 		return (0);	/* planar mode not supported */
4943630506bSToomas Soome 	if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
4953630506bSToomas Soome 	    mi->MemoryModel != 0x06 /* Direct Color */)
4963630506bSToomas Soome 		return (0);	/* unsupported pixel format */
4973630506bSToomas Soome 	return (1);
4983630506bSToomas Soome }
4993630506bSToomas Soome 
5003630506bSToomas Soome static bool
vbe_check(void)5013630506bSToomas Soome vbe_check(void)
5023630506bSToomas Soome {
5033630506bSToomas Soome 
5043630506bSToomas Soome 	if (vbe == NULL) {
5053630506bSToomas Soome 		printf("VBE not available\n");
5063630506bSToomas Soome 		return (false);
5073630506bSToomas Soome 	}
5083630506bSToomas Soome 	return (true);
5093630506bSToomas Soome }
5103630506bSToomas Soome 
5113630506bSToomas Soome static int
mode_set(struct env_var * ev,int flags __unused,const void * value)5123630506bSToomas Soome mode_set(struct env_var *ev, int flags __unused, const void *value)
5133630506bSToomas Soome {
5143630506bSToomas Soome 	int mode;
5153630506bSToomas Soome 
516babda095SToomas Soome 	if (strcmp(ev->ev_name, "screen.textmode") == 0) {
5173630506bSToomas Soome 		unsigned long v;
5183630506bSToomas Soome 		char *end;
5193630506bSToomas Soome 
5203630506bSToomas Soome 		if (value == NULL)
5213630506bSToomas Soome 			return (0);
5223630506bSToomas Soome 		errno = 0;
5233630506bSToomas Soome 		v = strtoul(value, &end, 0);
5243630506bSToomas Soome 		if (errno != 0 || *(char *)value == '\0' || *end != '\0' ||
5253630506bSToomas Soome 		    (v != 0 && v != 1))
5263630506bSToomas Soome 			return (EINVAL);
527babda095SToomas Soome 		env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK,
5283630506bSToomas Soome 		    value, NULL, NULL);
5293630506bSToomas Soome 		if (v == 1) {
5303630506bSToomas Soome 			reset_font_flags();
5313630506bSToomas Soome 			bios_text_font(true);
5323630506bSToomas Soome 			bios_set_text_mode(VGA_TEXT_MODE);
5333630506bSToomas Soome 			(void) cons_update_mode(false);
5343630506bSToomas Soome 			return (0);
5353630506bSToomas Soome 		}
5363630506bSToomas Soome 	} else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) {
5373630506bSToomas Soome 		env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
5383630506bSToomas Soome 		    value, NULL, NULL);
5393630506bSToomas Soome 	} else {
5403630506bSToomas Soome 		return (EINVAL);
5413630506bSToomas Soome 	}
5423630506bSToomas Soome 
5433630506bSToomas Soome 	mode = vbe_default_mode();
5443630506bSToomas Soome 	if (gfx_state.tg_mode != mode) {
5453630506bSToomas Soome 		reset_font_flags();
5463630506bSToomas Soome 		bios_text_font(false);
5473630506bSToomas Soome 		vbe_set_mode(mode);
5483630506bSToomas Soome 		cons_update_mode(true);
5493630506bSToomas Soome 	}
5503630506bSToomas Soome 	return (0);
5513630506bSToomas Soome }
5523630506bSToomas Soome 
553ad1ebbe5SToomas Soome static void *
vbe_farptr(uint32_t farptr)554ad1ebbe5SToomas Soome vbe_farptr(uint32_t farptr)
555ad1ebbe5SToomas Soome {
556ad1ebbe5SToomas Soome 	return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
557ad1ebbe5SToomas Soome }
558ad1ebbe5SToomas Soome 
5593630506bSToomas Soome void
vbe_init(void)5603630506bSToomas Soome vbe_init(void)
5613630506bSToomas Soome {
562ad1ebbe5SToomas Soome 	uint16_t *p, *ml;
563ad1ebbe5SToomas Soome 
5643630506bSToomas Soome 	/* First set FB for text mode. */
5653630506bSToomas Soome 	gfx_state.tg_fb_type = FB_TEXT;
5663630506bSToomas Soome 	gfx_state.tg_fb.fb_height = TEXT_ROWS;
5673630506bSToomas Soome 	gfx_state.tg_fb.fb_width = TEXT_COLS;
5683630506bSToomas Soome 	gfx_state.tg_ctype = CT_INDEXED;
5693630506bSToomas Soome 	gfx_state.tg_mode = 3;
5703630506bSToomas Soome 
57152e3a730SToomas Soome 	env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set,
57252e3a730SToomas Soome 	    env_nounset);
57352e3a730SToomas Soome 	env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
57452e3a730SToomas Soome 	    env_nounset);
57552e3a730SToomas Soome 
57652e3a730SToomas Soome 	if (vbe == NULL) {
5773630506bSToomas Soome 		vbe = malloc(sizeof(*vbe));
57852e3a730SToomas Soome 		if (vbe == NULL)
57952e3a730SToomas Soome 			return;
58052e3a730SToomas Soome 	}
5813630506bSToomas Soome 
5823630506bSToomas Soome 	if (vbe_mode == NULL) {
5833630506bSToomas Soome 		vbe_mode = malloc(sizeof(*vbe_mode));
5843630506bSToomas Soome 		if (vbe_mode == NULL) {
5853630506bSToomas Soome 			free(vbe);
5863630506bSToomas Soome 			vbe = NULL;
5873630506bSToomas Soome 		}
5883630506bSToomas Soome 	}
5893630506bSToomas Soome 
5903630506bSToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS) {
5913630506bSToomas Soome 		free(vbe);
5923630506bSToomas Soome 		vbe = NULL;
5933630506bSToomas Soome 		free(vbe_mode);
5943630506bSToomas Soome 		vbe_mode = NULL;
59552e3a730SToomas Soome 		return;
5963630506bSToomas Soome 	}
5973630506bSToomas Soome 
598ad1ebbe5SToomas Soome 	/*
599ad1ebbe5SToomas Soome 	 * Copy mode list. We must do this because some systems do
600ad1ebbe5SToomas Soome 	 * corrupt the provided list (vbox 6.1 is one example).
601ad1ebbe5SToomas Soome 	 */
602ad1ebbe5SToomas Soome 	p = ml = vbe_farptr(vbe->VideoModePtr);
603ad1ebbe5SToomas Soome 	while(*p++ != 0xFFFF)
604ad1ebbe5SToomas Soome 		;
605ad1ebbe5SToomas Soome 
606ad1ebbe5SToomas Soome 	vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
60752e3a730SToomas Soome 
60852e3a730SToomas Soome 	/*
60952e3a730SToomas Soome 	 * Since vbe_init() is used only once at very start of the loader,
61052e3a730SToomas Soome 	 * we assume malloc will not fail there, but in case it does,
61152e3a730SToomas Soome 	 * we point vbe_mode_list to memory pointed by VideoModePtr.
61252e3a730SToomas Soome 	 */
613ad1ebbe5SToomas Soome 	vbe_mode_list = malloc(vbe_mode_list_size);
61452e3a730SToomas Soome 	if (vbe_mode_list == NULL)
61552e3a730SToomas Soome 		vbe_mode_list = ml;
61652e3a730SToomas Soome 	else
617ad1ebbe5SToomas Soome 		bcopy(ml, vbe_mode_list, vbe_mode_list_size);
618ad1ebbe5SToomas Soome 
61952e3a730SToomas Soome 	/* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */
620ad1ebbe5SToomas Soome 	vbe->VideoModePtr = 0;
621ad1ebbe5SToomas Soome 
6223630506bSToomas Soome 	/* vbe_set_mode() will set up the rest. */
6233630506bSToomas Soome }
6243630506bSToomas Soome 
6253630506bSToomas Soome bool
vbe_available(void)6263630506bSToomas Soome vbe_available(void)
6273630506bSToomas Soome {
6283630506bSToomas Soome 	return (gfx_state.tg_fb_type == FB_VBE);
6293630506bSToomas Soome }
6303630506bSToomas Soome 
6313630506bSToomas Soome int
vbe_set_palette(const struct paletteentry * entry,size_t slot)6323630506bSToomas Soome vbe_set_palette(const struct paletteentry *entry, size_t slot)
6333630506bSToomas Soome {
6343630506bSToomas Soome 	struct paletteentry pe;
6353630506bSToomas Soome 	int mode, ret;
6363630506bSToomas Soome 
6373630506bSToomas Soome 	if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
6383630506bSToomas Soome 		return (1);
6393630506bSToomas Soome 
6403630506bSToomas Soome 	if (gfx_state.tg_ctype != CT_INDEXED) {
6413630506bSToomas Soome 		return (1);
6423630506bSToomas Soome 	}
6433630506bSToomas Soome 
6443630506bSToomas Soome 	pe.Blue = entry->Blue;
6453630506bSToomas Soome 	pe.Green = entry->Green;
6463630506bSToomas Soome 	pe.Red = entry->Red;
6473630506bSToomas Soome 	pe.Reserved = entry->Reserved;
6483630506bSToomas Soome 
6493630506bSToomas Soome 	if (vbe->Capabilities & VBE_CAP_SNOW)
6503630506bSToomas Soome 		mode = 0x80;
6513630506bSToomas Soome 	else
6523630506bSToomas Soome 		mode = 0;
6533630506bSToomas Soome 
6543630506bSToomas Soome 	ret = biosvbe_palette_data(mode, slot, &pe);
6553630506bSToomas Soome 
6563630506bSToomas Soome 	return (ret == VBE_SUCCESS ? 0 : 1);
6573630506bSToomas Soome }
6583630506bSToomas Soome 
6593630506bSToomas Soome int
vbe_get_mode(void)6603630506bSToomas Soome vbe_get_mode(void)
6613630506bSToomas Soome {
6623630506bSToomas Soome 	return (gfx_state.tg_mode);
6633630506bSToomas Soome }
6643630506bSToomas Soome 
6653630506bSToomas Soome int
vbe_set_mode(int modenum)6663630506bSToomas Soome vbe_set_mode(int modenum)
6673630506bSToomas Soome {
6683630506bSToomas Soome 	struct modeinfoblock mi;
6693630506bSToomas Soome 	int bpp, ret;
6703630506bSToomas Soome 
6713630506bSToomas Soome 	if (!vbe_check())
6723630506bSToomas Soome 		return (1);
6733630506bSToomas Soome 
6743630506bSToomas Soome 	ret = biosvbe_get_mode_info(modenum, &mi);
6753630506bSToomas Soome 	if (VBE_ERROR(ret)) {
6763630506bSToomas Soome 		printf("mode 0x%x invalid\n", modenum);
6773630506bSToomas Soome 		return (1);
6783630506bSToomas Soome 	}
6793630506bSToomas Soome 
6803630506bSToomas Soome 	if (!vbe_mode_is_supported(&mi)) {
6813630506bSToomas Soome 		printf("mode 0x%x not supported\n", modenum);
6823630506bSToomas Soome 		return (1);
6833630506bSToomas Soome 	}
6843630506bSToomas Soome 
6853630506bSToomas Soome 	/* calculate bytes per pixel */
6863630506bSToomas Soome 	switch (mi.BitsPerPixel) {
6873630506bSToomas Soome 	case 32:
6883630506bSToomas Soome 	case 24:
6893630506bSToomas Soome 	case 16:
6903630506bSToomas Soome 	case 15:
6913630506bSToomas Soome 	case 8:
6923630506bSToomas Soome 		break;
6933630506bSToomas Soome 	default:
6943630506bSToomas Soome 		printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
6953630506bSToomas Soome 		return (1);
6963630506bSToomas Soome 	}
6973630506bSToomas Soome 
6983630506bSToomas Soome 	ret = biosvbe_set_mode(modenum, NULL);
6993630506bSToomas Soome 	if (VBE_ERROR(ret)) {
7003630506bSToomas Soome 		printf("mode 0x%x could not be set\n", modenum);
7013630506bSToomas Soome 		return (1);
7023630506bSToomas Soome 	}
7033630506bSToomas Soome 
7043630506bSToomas Soome 	gfx_state.tg_mode = modenum;
7053630506bSToomas Soome 	gfx_state.tg_fb_type = FB_VBE;
7063630506bSToomas Soome 	/* make sure we have current MI in vbestate */
7073630506bSToomas Soome 	memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
7083630506bSToomas Soome 
7093630506bSToomas Soome 	gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
7103630506bSToomas Soome 	gfx_state.tg_fb.fb_height = mi.YResolution;
7113630506bSToomas Soome 	gfx_state.tg_fb.fb_width = mi.XResolution;
7123630506bSToomas Soome 	gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel;
7133630506bSToomas Soome 
7146102f43cSToomas Soome 	free(gfx_state.tg_shadow_fb);
7156102f43cSToomas Soome 	gfx_state.tg_shadow_fb = malloc(mi.YResolution * mi.XResolution *
7166102f43cSToomas Soome 	    sizeof(struct paletteentry));
7176102f43cSToomas Soome 
7183630506bSToomas Soome 	/* Bytes per pixel */
7193630506bSToomas Soome 	bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;
7203630506bSToomas Soome 
7213630506bSToomas Soome 	/* vbe_mode_is_supported() excludes the rest */
7223630506bSToomas Soome 	switch (mi.MemoryModel) {
7233630506bSToomas Soome 	case 0x4:
7243630506bSToomas Soome 		gfx_state.tg_ctype = CT_INDEXED;
7253630506bSToomas Soome 		break;
7263630506bSToomas Soome 	case 0x6:
7273630506bSToomas Soome 		gfx_state.tg_ctype = CT_RGB;
7283630506bSToomas Soome 		break;
7293630506bSToomas Soome 	}
7303630506bSToomas Soome 
7313630506bSToomas Soome #define	COLOR_MASK(size, pos) (((1 << size) - 1) << pos)
7323630506bSToomas Soome 	if (gfx_state.tg_ctype == CT_INDEXED) {
7333630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16);
7343630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8);
7353630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0);
7363630506bSToomas Soome 	} else if (vbe->VbeVersion >= 0x300) {
7373630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_red =
7383630506bSToomas Soome 		    COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition);
7393630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_green =
7403630506bSToomas Soome 		    COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition);
7413630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_blue =
7423630506bSToomas Soome 		    COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition);
7433630506bSToomas Soome 	} else {
7443630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_red =
7453630506bSToomas Soome 		    COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition);
7463630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_green =
7473630506bSToomas Soome 		    COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition);
7483630506bSToomas Soome 		gfx_state.tg_fb.fb_mask_blue =
7493630506bSToomas Soome 		    COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition);
7503630506bSToomas Soome 	}
7513630506bSToomas Soome 	gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red |
7523630506bSToomas Soome 	    gfx_state.tg_fb.fb_mask_green |
7533630506bSToomas Soome 	    gfx_state.tg_fb.fb_mask_blue);
7543630506bSToomas Soome 
7553630506bSToomas Soome 	if (vbe->VbeVersion >= 0x300)
7563630506bSToomas Soome 		gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp;
7573630506bSToomas Soome 	else
7583630506bSToomas Soome 		gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp;
7593630506bSToomas Soome 
7603630506bSToomas Soome 	gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride *
7613630506bSToomas Soome 	    bpp;
7623630506bSToomas Soome 
7633630506bSToomas Soome 	return (0);
7643630506bSToomas Soome }
7653630506bSToomas Soome 
7663630506bSToomas Soome /*
7679cd75b55SGordon Bergling  * Verify existence of mode number or find mode by
7683630506bSToomas Soome  * dimensions. If depth is not given, walk values 32, 24, 16, 8.
7693630506bSToomas Soome  */
7703630506bSToomas Soome static int
vbe_find_mode_xydm(int x,int y,int depth,int m)7713630506bSToomas Soome vbe_find_mode_xydm(int x, int y, int depth, int m)
7723630506bSToomas Soome {
7733630506bSToomas Soome 	struct modeinfoblock mi;
774ad1ebbe5SToomas Soome 	uint16_t *farptr;
7753630506bSToomas Soome 	uint16_t mode;
77652e3a730SToomas Soome 	int idx, nentries, i;
7773630506bSToomas Soome 
7783630506bSToomas Soome 	memset(vbe, 0, sizeof (*vbe));
7793630506bSToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
7803630506bSToomas Soome 		return (0);
7813630506bSToomas Soome 
7823630506bSToomas Soome 	if (m != -1)
7833630506bSToomas Soome 		i = 8;
7843630506bSToomas Soome 	else if (depth == -1)
7853630506bSToomas Soome 		i = 32;
7863630506bSToomas Soome 	else
7873630506bSToomas Soome 		i = depth;
7883630506bSToomas Soome 
78952e3a730SToomas Soome 	nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
7903630506bSToomas Soome 	while (i > 0) {
79152e3a730SToomas Soome 		for (idx = 0; idx < nentries; idx++) {
792ad1ebbe5SToomas Soome 			mode = vbe_mode_list[idx];
793ad1ebbe5SToomas Soome 			if (mode == 0xffff)
7943630506bSToomas Soome 				break;
795ad1ebbe5SToomas Soome 
7963630506bSToomas Soome 			if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
7973630506bSToomas Soome 				continue;
7983630506bSToomas Soome 			}
799ad1ebbe5SToomas Soome 
8003630506bSToomas Soome 			/* we only care about linear modes here */
8013630506bSToomas Soome 			if (vbe_mode_is_supported(&mi) == 0)
8023630506bSToomas Soome 				continue;
8033630506bSToomas Soome 
8043630506bSToomas Soome 			if (m != -1) {
8053630506bSToomas Soome 				if (m == mode)
8063630506bSToomas Soome 					return (mode);
8073630506bSToomas Soome 				else
8083630506bSToomas Soome 					continue;
8093630506bSToomas Soome 			}
8103630506bSToomas Soome 
8113630506bSToomas Soome 			if (mi.XResolution == x &&
8123630506bSToomas Soome 			    mi.YResolution == y &&
8133630506bSToomas Soome 			    mi.BitsPerPixel == i)
8143630506bSToomas Soome 				return (mode);
8153630506bSToomas Soome 		}
8163630506bSToomas Soome 		if (depth != -1)
8173630506bSToomas Soome 			break;
8183630506bSToomas Soome 
8193630506bSToomas Soome 		i -= 8;
8203630506bSToomas Soome 	}
8213630506bSToomas Soome 
8223630506bSToomas Soome 	return (0);
8233630506bSToomas Soome }
8243630506bSToomas Soome 
8253630506bSToomas Soome static int
vbe_find_mode(char * str)8263630506bSToomas Soome vbe_find_mode(char *str)
8273630506bSToomas Soome {
8283630506bSToomas Soome 	int x, y, depth;
8293630506bSToomas Soome 
8303630506bSToomas Soome 	if (!gfx_parse_mode_str(str, &x, &y, &depth))
8313630506bSToomas Soome 		return (0);
8323630506bSToomas Soome 
8333630506bSToomas Soome 	return (vbe_find_mode_xydm(x, y, depth, -1));
8343630506bSToomas Soome }
8353630506bSToomas Soome 
8363630506bSToomas Soome static void
vbe_dump_mode(int modenum,struct modeinfoblock * mi)8373630506bSToomas Soome vbe_dump_mode(int modenum, struct modeinfoblock *mi)
8383630506bSToomas Soome {
8393630506bSToomas Soome 	printf("0x%x=%dx%dx%d", modenum,
8403630506bSToomas Soome 	    mi->XResolution, mi->YResolution, mi->BitsPerPixel);
8413630506bSToomas Soome }
8423630506bSToomas Soome 
8433630506bSToomas Soome static bool
vbe_get_edid(edid_res_list_t * res)8443630506bSToomas Soome vbe_get_edid(edid_res_list_t *res)
8453630506bSToomas Soome {
846becaac39SToomas Soome 	struct vesa_edid_info *edidp;
8473630506bSToomas Soome 	const uint8_t magic[] = EDID_MAGIC;
8483630506bSToomas Soome 	int ddc_caps;
8493630506bSToomas Soome 	bool ret = false;
8503630506bSToomas Soome 
851becaac39SToomas Soome 	if (edid_info != NULL)
852becaac39SToomas Soome 		return (gfx_get_edid_resolution(edid_info, res));
853becaac39SToomas Soome 
8543630506bSToomas Soome 	ddc_caps = biosvbe_ddc_caps();
8553630506bSToomas Soome 	if (ddc_caps == 0) {
8563630506bSToomas Soome 		return (ret);
8573630506bSToomas Soome 	}
8583630506bSToomas Soome 
859becaac39SToomas Soome 	edidp = bio_alloc(sizeof(*edidp));
860becaac39SToomas Soome 	if (edidp == NULL)
8613630506bSToomas Soome 		return (ret);
862becaac39SToomas Soome 	memset(edidp, 0, sizeof(*edidp));
8633630506bSToomas Soome 
864becaac39SToomas Soome 	if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp)))
8653630506bSToomas Soome 		goto done;
8663630506bSToomas Soome 
867becaac39SToomas Soome 	if (memcmp(edidp, magic, sizeof(magic)) != 0)
8683630506bSToomas Soome 		goto done;
8693630506bSToomas Soome 
8703630506bSToomas Soome 	/* Unknown EDID version. */
871becaac39SToomas Soome 	if (edidp->header.version != 1)
8723630506bSToomas Soome 		goto done;
8733630506bSToomas Soome 
874becaac39SToomas Soome 	ret = gfx_get_edid_resolution(edidp, res);
875becaac39SToomas Soome 	edid_info = malloc(sizeof(*edid_info));
876becaac39SToomas Soome 	if (edid_info != NULL)
877becaac39SToomas Soome 		memcpy(edid_info, edidp, sizeof (*edid_info));
8783630506bSToomas Soome done:
879becaac39SToomas Soome 	bio_free(edidp, sizeof(*edidp));
8803630506bSToomas Soome 	return (ret);
8813630506bSToomas Soome }
8823630506bSToomas Soome 
8833630506bSToomas Soome static bool
vbe_get_flatpanel(uint32_t * pwidth,uint32_t * pheight)8843630506bSToomas Soome vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight)
8853630506bSToomas Soome {
8863630506bSToomas Soome 	struct vesa_flat_panel_info *fp_info;
8873630506bSToomas Soome 	bool ret = false;
8883630506bSToomas Soome 
8893630506bSToomas Soome 	fp_info = bio_alloc(sizeof (*fp_info));
8903630506bSToomas Soome 	if (fp_info == NULL)
8913630506bSToomas Soome 		return (ret);
8923630506bSToomas Soome 	memset(fp_info, 0, sizeof (*fp_info));
8933630506bSToomas Soome 
8943630506bSToomas Soome 	if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
8953630506bSToomas Soome 		goto done;
8963630506bSToomas Soome 
8973630506bSToomas Soome 	*pwidth = fp_info->HSize;
8983630506bSToomas Soome 	*pheight = fp_info->VSize;
8993630506bSToomas Soome 	ret = true;
9003630506bSToomas Soome 
9013630506bSToomas Soome done:
9023630506bSToomas Soome 	bio_free(fp_info, sizeof (*fp_info));
9033630506bSToomas Soome 	return (ret);
9043630506bSToomas Soome }
9053630506bSToomas Soome 
9063630506bSToomas Soome static void
vbe_print_memory(unsigned vmem)9073630506bSToomas Soome vbe_print_memory(unsigned vmem)
9083630506bSToomas Soome {
9093630506bSToomas Soome 	char unit = 'K';
9103630506bSToomas Soome 
9113630506bSToomas Soome 	vmem /= 1024;
9123630506bSToomas Soome 	if (vmem >= 10240000) {
9133630506bSToomas Soome 		vmem /= 1048576;
9143630506bSToomas Soome 		unit = 'G';
9153630506bSToomas Soome 	} else if (vmem >= 10000) {
9163630506bSToomas Soome 		vmem /= 1024;
9173630506bSToomas Soome 		unit = 'M';
9183630506bSToomas Soome 	}
9193630506bSToomas Soome 	printf("Total memory: %u%cB\n", vmem, unit);
9203630506bSToomas Soome }
9213630506bSToomas Soome 
9223630506bSToomas Soome static void
vbe_print_vbe_info(struct vbeinfoblock * vbep)9233630506bSToomas Soome vbe_print_vbe_info(struct vbeinfoblock *vbep)
9243630506bSToomas Soome {
9253630506bSToomas Soome 	char *oemstring = "";
9263630506bSToomas Soome 	char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
9273630506bSToomas Soome 
9283630506bSToomas Soome 	if (vbep->OemStringPtr != 0)
9293630506bSToomas Soome 		oemstring = vbe_farptr(vbep->OemStringPtr);
9303630506bSToomas Soome 
9313630506bSToomas Soome 	if (vbep->OemVendorNamePtr != 0)
9323630506bSToomas Soome 		oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
9333630506bSToomas Soome 
9343630506bSToomas Soome 	if (vbep->OemProductNamePtr != 0)
9353630506bSToomas Soome 		oemproductname = vbe_farptr(vbep->OemProductNamePtr);
9363630506bSToomas Soome 
9373630506bSToomas Soome 	if (vbep->OemProductRevPtr != 0)
9383630506bSToomas Soome 		oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
9393630506bSToomas Soome 
9403630506bSToomas Soome 	printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
9413630506bSToomas Soome 	    vbep->VbeVersion & 0xF, oemstring);
9423630506bSToomas Soome 
9433630506bSToomas Soome 	if (vbep->OemSoftwareRev != 0) {
9443630506bSToomas Soome 		printf("OEM Version %d.%d, %s (%s, %s)\n",
9453630506bSToomas Soome 		    vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
9463630506bSToomas Soome 		    oemvendor, oemproductname, oemproductrev);
9473630506bSToomas Soome 	}
9483630506bSToomas Soome 	vbe_print_memory(vbep->TotalMemory << 16);
9493630506bSToomas Soome 	printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages);
9503630506bSToomas Soome }
9513630506bSToomas Soome 
9523630506bSToomas Soome /* List available modes, filter by depth. If depth is -1, list all. */
9533630506bSToomas Soome void
vbe_modelist(int depth)9543630506bSToomas Soome vbe_modelist(int depth)
9553630506bSToomas Soome {
9563630506bSToomas Soome 	struct modeinfoblock mi;
9573630506bSToomas Soome 	uint16_t mode;
958ad1ebbe5SToomas Soome 	int nmodes, idx, nentries;
9593630506bSToomas Soome 	int ddc_caps;
9603630506bSToomas Soome 	uint32_t width, height;
9613630506bSToomas Soome 	bool edid = false;
9623630506bSToomas Soome 	edid_res_list_t res;
9633630506bSToomas Soome 	struct resolution *rp;
9643630506bSToomas Soome 
9653630506bSToomas Soome 	if (!vbe_check())
9663630506bSToomas Soome 		return;
9673630506bSToomas Soome 
9683630506bSToomas Soome 	ddc_caps = biosvbe_ddc_caps();
9693630506bSToomas Soome 	if (ddc_caps & 3) {
9703630506bSToomas Soome 		printf("DDC");
9713630506bSToomas Soome 		if (ddc_caps & 1)
9723630506bSToomas Soome 			printf(" [DDC1]");
9733630506bSToomas Soome 		if (ddc_caps & 2)
9743630506bSToomas Soome 			printf(" [DDC2]");
9753630506bSToomas Soome 
9763630506bSToomas Soome 		TAILQ_INIT(&res);
9773630506bSToomas Soome 		edid = vbe_get_edid(&res);
9783630506bSToomas Soome 		if (edid) {
9793630506bSToomas Soome 			printf(": EDID");
9803630506bSToomas Soome 			while ((rp = TAILQ_FIRST(&res)) != NULL) {
9813630506bSToomas Soome 				printf(" %dx%d", rp->width, rp->height);
9823630506bSToomas Soome 				TAILQ_REMOVE(&res, rp, next);
9833630506bSToomas Soome 				free(rp);
9843630506bSToomas Soome 			}
9853630506bSToomas Soome 			printf("\n");
9863630506bSToomas Soome 		} else {
9873630506bSToomas Soome 			printf(": no EDID information\n");
9883630506bSToomas Soome 		}
9893630506bSToomas Soome 	}
9903630506bSToomas Soome 	if (!edid)
9913630506bSToomas Soome 		if (vbe_get_flatpanel(&width, &height))
9923630506bSToomas Soome 			printf(": Panel %dx%d\n", width, height);
9933630506bSToomas Soome 
994ad1ebbe5SToomas Soome 	nmodes = 0;
9953630506bSToomas Soome 	memset(vbe, 0, sizeof (*vbe));
9963630506bSToomas Soome 	memcpy(vbe->VbeSignature, "VBE2", 4);
9973630506bSToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
9983630506bSToomas Soome 		goto done;
9993630506bSToomas Soome 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
10003630506bSToomas Soome 		goto done;
10013630506bSToomas Soome 
10023630506bSToomas Soome 	vbe_print_vbe_info(vbe);
10033630506bSToomas Soome 	printf("Modes: ");
10043630506bSToomas Soome 
1005ad1ebbe5SToomas Soome 	nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
1006ad1ebbe5SToomas Soome 	for (idx = 0; idx < nentries; idx++) {
1007ad1ebbe5SToomas Soome 		mode = vbe_mode_list[idx];
1008ad1ebbe5SToomas Soome 		if (mode == 0xffff)
10093630506bSToomas Soome 			break;
1010ad1ebbe5SToomas Soome 
10113630506bSToomas Soome 		if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
10123630506bSToomas Soome 			continue;
1013ad1ebbe5SToomas Soome 
10143630506bSToomas Soome 		/* we only care about linear modes here */
10153630506bSToomas Soome 		if (vbe_mode_is_supported(&mi) == 0)
10163630506bSToomas Soome 			continue;
10173630506bSToomas Soome 
10183630506bSToomas Soome 		/* apply requested filter */
10193630506bSToomas Soome 		if (depth != -1 && mi.BitsPerPixel != depth)
10203630506bSToomas Soome 			continue;
10213630506bSToomas Soome 
10223630506bSToomas Soome 		if (nmodes % 4 == 0)
10233630506bSToomas Soome 			printf("\n");
10243630506bSToomas Soome 		else
10253630506bSToomas Soome 			printf("  ");
10263630506bSToomas Soome 
10273630506bSToomas Soome 		vbe_dump_mode(mode, &mi);
10283630506bSToomas Soome 		nmodes++;
10293630506bSToomas Soome 	}
10303630506bSToomas Soome 
10313630506bSToomas Soome done:
10323630506bSToomas Soome 	if (nmodes == 0)
10333630506bSToomas Soome 		printf("none found");
10343630506bSToomas Soome 	printf("\n");
10353630506bSToomas Soome }
10363630506bSToomas Soome 
10373630506bSToomas Soome static void
vbe_print_mode(bool verbose __unused)10383630506bSToomas Soome vbe_print_mode(bool verbose __unused)
10393630506bSToomas Soome {
10403630506bSToomas Soome 	int nc, mode, i, rc;
10413630506bSToomas Soome 
10423630506bSToomas Soome 	nc = NCOLORS;
10433630506bSToomas Soome 
10443630506bSToomas Soome 	memset(vbe, 0, sizeof (*vbe));
10453630506bSToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
10463630506bSToomas Soome 		return;
10473630506bSToomas Soome 
10483630506bSToomas Soome 	vbe_print_vbe_info(vbe);
10493630506bSToomas Soome 
10503630506bSToomas Soome 	if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
10513630506bSToomas Soome 		printf("Error getting current VBE mode\n");
10523630506bSToomas Soome 		return;
10533630506bSToomas Soome 	}
10543630506bSToomas Soome 
10553630506bSToomas Soome 	if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
10563630506bSToomas Soome 	    vbe_mode_is_supported(vbe_mode) == 0) {
10573630506bSToomas Soome 		printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
10583630506bSToomas Soome 		return;
10593630506bSToomas Soome 	}
10603630506bSToomas Soome 
10613630506bSToomas Soome 	printf("\nCurrent VBE mode: ");
10623630506bSToomas Soome 	vbe_dump_mode(mode, vbe_mode);
10633630506bSToomas Soome 	printf("\n");
10643630506bSToomas Soome 
10653630506bSToomas Soome 	printf("%ux%ux%u, stride=%u\n",
10663630506bSToomas Soome 	    gfx_state.tg_fb.fb_width,
10673630506bSToomas Soome 	    gfx_state.tg_fb.fb_height,
10683630506bSToomas Soome 	    gfx_state.tg_fb.fb_bpp,
10693630506bSToomas Soome 	    gfx_state.tg_fb.fb_stride *
10703630506bSToomas Soome 	    (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY));
10713630506bSToomas Soome 	printf("    frame buffer: address=%jx, size=%jx\n",
10723630506bSToomas Soome 	    (uintmax_t)gfx_state.tg_fb.fb_addr,
10733630506bSToomas Soome 	    (uintmax_t)gfx_state.tg_fb.fb_size);
10743630506bSToomas Soome 
10753630506bSToomas Soome 	if (vbe_mode->MemoryModel == 0x6) {
10763630506bSToomas Soome 		printf("    color mask: R=%08x, G=%08x, B=%08x\n",
10773630506bSToomas Soome 		    gfx_state.tg_fb.fb_mask_red,
10783630506bSToomas Soome 		    gfx_state.tg_fb.fb_mask_green,
10793630506bSToomas Soome 		    gfx_state.tg_fb.fb_mask_blue);
10803630506bSToomas Soome 		pager_open();
10813630506bSToomas Soome 		for (i = 0; i < nc; i++) {
10823630506bSToomas Soome 			printf("%d: R=%02x, G=%02x, B=%02x %08x", i,
10833630506bSToomas Soome 			    (cmap[i] & gfx_state.tg_fb.fb_mask_red) >>
10843630506bSToomas Soome 			    ffs(gfx_state.tg_fb.fb_mask_red) - 1,
10853630506bSToomas Soome 			    (cmap[i] & gfx_state.tg_fb.fb_mask_green) >>
10863630506bSToomas Soome 			    ffs(gfx_state.tg_fb.fb_mask_green) - 1,
10873630506bSToomas Soome 			    (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >>
10883630506bSToomas Soome 			    ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]);
10893630506bSToomas Soome 			if (pager_output("\n") != 0)
10903630506bSToomas Soome 				break;
10913630506bSToomas Soome 		}
10923630506bSToomas Soome 		pager_close();
10933630506bSToomas Soome 		return;
10943630506bSToomas Soome 	}
10953630506bSToomas Soome 
10963630506bSToomas Soome 	mode = 1;	/* get DAC palette width */
10973630506bSToomas Soome 	rc = biosvbe_palette_format(&mode);
10983630506bSToomas Soome 	if (rc != VBE_SUCCESS)
10993630506bSToomas Soome 		return;
11003630506bSToomas Soome 
11013630506bSToomas Soome 	printf("    palette format: %x bits per primary\n", mode);
11023630506bSToomas Soome 	if (pe8 == NULL)
11033630506bSToomas Soome 		return;
11043630506bSToomas Soome 
11053630506bSToomas Soome 	pager_open();
11063630506bSToomas Soome 	for (i = 0; i < nc; i++) {
11073630506bSToomas Soome 		printf("%d: R=%02x, G=%02x, B=%02x", i,
11083630506bSToomas Soome 		    pe8[i].Red, pe8[i].Green, pe8[i].Blue);
11093630506bSToomas Soome 		if (pager_output("\n") != 0)
11103630506bSToomas Soome 			break;
11113630506bSToomas Soome 	}
11123630506bSToomas Soome 	pager_close();
11133630506bSToomas Soome }
11143630506bSToomas Soome 
11153630506bSToomas Soome /*
11163630506bSToomas Soome  * Try EDID preferred mode, if EDID or the suggested mode is not available,
11173630506bSToomas Soome  * then try flat panel information.
11183630506bSToomas Soome  * Fall back to VBE_DEFAULT_MODE.
11193630506bSToomas Soome  */
11203630506bSToomas Soome int
vbe_default_mode(void)11213630506bSToomas Soome vbe_default_mode(void)
11223630506bSToomas Soome {
11233630506bSToomas Soome 	edid_res_list_t res;
11243630506bSToomas Soome 	struct resolution *rp;
11253630506bSToomas Soome 	int modenum;
11263630506bSToomas Soome 	uint32_t width, height;
11273630506bSToomas Soome 
11283630506bSToomas Soome 	modenum = 0;
11293630506bSToomas Soome 	vbe_get_max_resolution(&width, &height);
11303630506bSToomas Soome 	if (width != 0 && height != 0)
11313630506bSToomas Soome 		modenum = vbe_find_mode_xydm(width, height, -1, -1);
11323630506bSToomas Soome 
11333630506bSToomas Soome 	TAILQ_INIT(&res);
11343630506bSToomas Soome 	if (vbe_get_edid(&res)) {
11353630506bSToomas Soome 		while ((rp = TAILQ_FIRST(&res)) != NULL) {
11363630506bSToomas Soome 			if (modenum == 0) {
11373630506bSToomas Soome 				modenum = vbe_find_mode_xydm(
11383630506bSToomas Soome 				    rp->width, rp->height, -1, -1);
11393630506bSToomas Soome 			}
11403630506bSToomas Soome 			TAILQ_REMOVE(&res, rp, next);
11413630506bSToomas Soome 			free(rp);
11423630506bSToomas Soome 		}
11433630506bSToomas Soome 	}
11443630506bSToomas Soome 
11453630506bSToomas Soome 	if (modenum == 0 &&
11463630506bSToomas Soome 	    vbe_get_flatpanel(&width, &height)) {
11473630506bSToomas Soome 		modenum = vbe_find_mode_xydm(width, height, -1, -1);
11483630506bSToomas Soome 	}
11493630506bSToomas Soome 
11503630506bSToomas Soome 	/* Still no mode? Fall back to default. */
11513630506bSToomas Soome 	if (modenum == 0)
11523630506bSToomas Soome 		modenum = vbe_find_mode(VBE_DEFAULT_MODE);
11533630506bSToomas Soome 	return (modenum);
11543630506bSToomas Soome }
11553630506bSToomas Soome 
11563630506bSToomas Soome COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa);
11573630506bSToomas Soome 
11583630506bSToomas Soome int
command_vesa(int argc,char * argv[])11593630506bSToomas Soome command_vesa(int argc, char *argv[])
11603630506bSToomas Soome {
11613630506bSToomas Soome 	char *arg, *cp;
11623630506bSToomas Soome 	int modenum = -1, n;
11633630506bSToomas Soome 
11643630506bSToomas Soome 	if (!vbe_check())
11653630506bSToomas Soome 		return (CMD_OK);
11663630506bSToomas Soome 
11673630506bSToomas Soome 	if (argc < 2)
11683630506bSToomas Soome 		goto usage;
11693630506bSToomas Soome 
11703630506bSToomas Soome 	if (strcmp(argv[1], "list") == 0) {
11713630506bSToomas Soome 		n = -1;
11723630506bSToomas Soome 		if (argc != 2 && argc != 3)
11733630506bSToomas Soome 			goto usage;
11743630506bSToomas Soome 
11753630506bSToomas Soome 		if (argc == 3) {
11763630506bSToomas Soome 			arg = argv[2];
11773630506bSToomas Soome 			errno = 0;
11783630506bSToomas Soome 			n = strtoul(arg, &cp, 0);
11793630506bSToomas Soome 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
11803630506bSToomas Soome 				snprintf(command_errbuf,
11813630506bSToomas Soome 				    sizeof (command_errbuf),
11823630506bSToomas Soome 				    "depth should be an integer");
11833630506bSToomas Soome 				return (CMD_ERROR);
11843630506bSToomas Soome 			}
11853630506bSToomas Soome 		}
11863630506bSToomas Soome 		vbe_modelist(n);
11873630506bSToomas Soome 		return (CMD_OK);
11883630506bSToomas Soome 	}
11893630506bSToomas Soome 
11903630506bSToomas Soome 	if (strcmp(argv[1], "get") == 0) {
11913630506bSToomas Soome 		bool verbose = false;
11923630506bSToomas Soome 
11933630506bSToomas Soome 		if (argc != 2) {
11943630506bSToomas Soome 			if (argc > 3 || strcmp(argv[2], "-v") != 0)
11953630506bSToomas Soome 				goto usage;
11963630506bSToomas Soome 			verbose = true;
11973630506bSToomas Soome 		}
11983630506bSToomas Soome 		vbe_print_mode(verbose);
11993630506bSToomas Soome 		return (CMD_OK);
12003630506bSToomas Soome 	}
12013630506bSToomas Soome 
12023630506bSToomas Soome 	if (strcmp(argv[1], "off") == 0) {
12033630506bSToomas Soome 		if (argc != 2)
12043630506bSToomas Soome 			goto usage;
12053630506bSToomas Soome 
12063630506bSToomas Soome 		if (gfx_state.tg_mode == VGA_TEXT_MODE)
12073630506bSToomas Soome 			return (CMD_OK);
12083630506bSToomas Soome 
12093630506bSToomas Soome 		reset_font_flags();
12103630506bSToomas Soome 		bios_text_font(true);
12113630506bSToomas Soome 		bios_set_text_mode(VGA_TEXT_MODE);
12123630506bSToomas Soome 		cons_update_mode(false);
12133630506bSToomas Soome 		return (CMD_OK);
12143630506bSToomas Soome 	}
12153630506bSToomas Soome 
12163630506bSToomas Soome 	if (strcmp(argv[1], "on") == 0) {
12173630506bSToomas Soome 		if (argc != 2)
12183630506bSToomas Soome 			goto usage;
12193630506bSToomas Soome 
12203630506bSToomas Soome 		modenum = vbe_default_mode();
12213630506bSToomas Soome 		if (modenum == 0) {
12223630506bSToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
12233630506bSToomas Soome 			    "%s: no suitable VBE mode number found", argv[0]);
12243630506bSToomas Soome 			return (CMD_ERROR);
12253630506bSToomas Soome 		}
12263630506bSToomas Soome 	} else if (strcmp(argv[1], "set") == 0) {
12273630506bSToomas Soome 		if (argc != 3)
12283630506bSToomas Soome 			goto usage;
12293630506bSToomas Soome 
12303630506bSToomas Soome 		if (strncmp(argv[2], "0x", 2) == 0) {
12313630506bSToomas Soome 			arg = argv[2];
12323630506bSToomas Soome 			errno = 0;
12333630506bSToomas Soome 			n = strtoul(arg, &cp, 0);
12343630506bSToomas Soome 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
12353630506bSToomas Soome 				snprintf(command_errbuf,
12363630506bSToomas Soome 				    sizeof (command_errbuf),
12373630506bSToomas Soome 				    "mode should be an integer");
12383630506bSToomas Soome 				return (CMD_ERROR);
12393630506bSToomas Soome 			}
12403630506bSToomas Soome 			modenum = vbe_find_mode_xydm(0, 0, 0, n);
12413630506bSToomas Soome 		} else if (strchr(argv[2], 'x') != NULL) {
12423630506bSToomas Soome 			modenum = vbe_find_mode(argv[2]);
12433630506bSToomas Soome 		}
12443630506bSToomas Soome 	} else {
12453630506bSToomas Soome 		goto usage;
12463630506bSToomas Soome 	}
12473630506bSToomas Soome 
12483630506bSToomas Soome 	if (modenum == 0) {
12493630506bSToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
12503630506bSToomas Soome 		    "%s: mode %s not supported by firmware\n",
12513630506bSToomas Soome 		    argv[0], argv[2]);
12523630506bSToomas Soome 		return (CMD_ERROR);
12533630506bSToomas Soome 	}
12543630506bSToomas Soome 
12553630506bSToomas Soome 	if (modenum >= VESA_MODE_BASE) {
12563630506bSToomas Soome 		if (gfx_state.tg_mode != modenum) {
12573630506bSToomas Soome 			reset_font_flags();
12583630506bSToomas Soome 			bios_text_font(false);
12593630506bSToomas Soome 			vbe_set_mode(modenum);
12603630506bSToomas Soome 			cons_update_mode(true);
12613630506bSToomas Soome 		}
12623630506bSToomas Soome 		return (CMD_OK);
12633630506bSToomas Soome 	} else {
12643630506bSToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
12653630506bSToomas Soome 		    "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
12663630506bSToomas Soome 		return (CMD_ERROR);
12673630506bSToomas Soome 	}
12683630506bSToomas Soome 
12693630506bSToomas Soome usage:
12703630506bSToomas Soome 	snprintf(command_errbuf, sizeof (command_errbuf),
12713630506bSToomas Soome 	    "usage: %s on | off | get | list [depth] | "
12723630506bSToomas Soome 	    "set <display or VBE mode number>", argv[0]);
12733630506bSToomas Soome 	return (CMD_ERROR);
12743630506bSToomas Soome }
1275