From ffe129a09a3ef0cba6b6de997ee409a3c764d0c7 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sat, 9 May 2020 20:32:44 +0200
Subject: [PATCH 3/5] WIP legacy loader VESA mode support

---
 sys/boot/pc32/libi386/Makefile     |   2 +-
 sys/boot/pc32/libi386/bootinfo64.c |   5 +
 sys/boot/pc32/libi386/libi386.h    |   4 +
 sys/boot/pc32/libi386/vbe.c        | 245 +++++++++++++++++++++++++++++
 sys/boot/pc32/libi386/vbe.h        | 137 ++++++++++++++++
 sys/boot/pc32/loader/help.i386     |  11 ++
 sys/boot/pc32/loader/main.c        |   3 +
 7 files changed, 406 insertions(+), 1 deletion(-)
 create mode 100644 sys/boot/pc32/libi386/vbe.c
 create mode 100644 sys/boot/pc32/libi386/vbe.h

diff --git a/sys/boot/pc32/libi386/Makefile b/sys/boot/pc32/libi386/Makefile
index 116b3e7c4a..6380fee70d 100644
--- a/sys/boot/pc32/libi386/Makefile
+++ b/sys/boot/pc32/libi386/Makefile
@@ -9,7 +9,7 @@ SRCS=	biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
 	biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \
 	comconsole.c devicename.c elf32_freebsd.c elf64_freebsd.c \
 	i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
-	smbios.c time.c vidconsole.c x86_64_tramp.S
+	smbios.c time.c vbe.c vidconsole.c x86_64_tramp.S
 
 CFLAGS+=	-ffreestanding
 CFLAGS+=	-fno-strict-aliasing
diff --git a/sys/boot/pc32/libi386/bootinfo64.c b/sys/boot/pc32/libi386/bootinfo64.c
index f48834908f..7df52595fa 100644
--- a/sys/boot/pc32/libi386/bootinfo64.c
+++ b/sys/boot/pc32/libi386/bootinfo64.c
@@ -32,6 +32,7 @@
 #include <sys/linker.h>
 #include <machine/bootinfo.h>
 #include <machine/cpufunc.h>
+#include <machine/metadata.h>
 #include <machine/psl.h>
 #include <machine/specialreg.h>
 #include "bootstrap.h"
@@ -179,6 +180,7 @@ bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
     struct preloaded_file	*xp, *kfp;
     struct i386_devdesc		*rootdev;
     struct file_metadata	*md;
+    struct efi_fb		*vbefb;
     vm_offset_t			addr;
     u_int64_t			kernend;
     u_int64_t			envp;
@@ -234,6 +236,9 @@ bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
     file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
     file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
     bios_addsmapdata(kfp);
+    vbefb = vbe_init();
+    if (vbefb != NULL)
+	file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(*vbefb), vbefb);
 
     /* Figure out the size and location of the metadata */
     *modulep = addr;
diff --git a/sys/boot/pc32/libi386/libi386.h b/sys/boot/pc32/libi386/libi386.h
index 6c71d89807..a86afb5a4b 100644
--- a/sys/boot/pc32/libi386/libi386.h
+++ b/sys/boot/pc32/libi386/libi386.h
@@ -125,6 +125,10 @@ void	biosacpi_detect(void);
 
 int	i386_autoload(void);
 
+void	vbe_detect(void);
+struct efi_fb;
+struct efi_fb *vbe_init(void);
+
 int	bi_getboothowto(char *kargs);
 vm_offset_t	bi_copyenv(vm_offset_t addr);
 int	bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip,
diff --git a/sys/boot/pc32/libi386/vbe.c b/sys/boot/pc32/libi386/vbe.c
new file mode 100644
index 0000000000..23376bb186
--- /dev/null
+++ b/sys/boot/pc32/libi386/vbe.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sascha Wildner <swildner@dragonflybsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Specifications used:
+ *	VESA BIOS Extension Core Functions, Version 3.0
+ */
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <stand.h>
+
+#include <machine/metadata.h>
+#include <machine/pc/bios.h>
+
+#include "libi386.h"
+#include "vbe.h"
+
+static vbe_ctlr_info_t ctlr_info;
+static struct efi_fb vbefb;
+
+static int
+vbe_status(uint16_t ax)
+{
+	if ((ax & VBE_L_MASK) == (VBE_FUNC_BASE >> 8)) {
+		switch (ax & VBE_H_MASK) {
+		case 0x0000:
+			return (VBE_SUCCESS);
+			break;
+		case 0x0100:
+			return (VBE_FAILED);
+			break;
+		case 0x0200:
+			return (VBE_UNSUPPORTED_HW);
+			break;
+		case 0x0300:
+			return (VBE_INVALID);
+			break;
+		}
+		return (VBE_UNKNOWN);
+	} else {
+		return (VBE_UNSUPPORTED);
+	}
+}
+
+/*
+ * VBE Function 00h: Return VBE Controller Information
+ */
+static int
+vbefunc_ctlr_info(vbe_ctlr_info_t *ctlr_info)
+{
+	v86.ctl = 0;
+	v86.addr = 0x10;
+	v86.eax = VBE_FUNC_CTLR_INFO;
+	v86.es = VTOPSEG(ctlr_info);
+	v86.edi = VTOPOFF(ctlr_info);
+	v86int();
+	return (vbe_status(v86.eax));
+}
+
+/*
+ * VBE Function 01h: Return VBE Mode Information
+ */
+static int
+vbefunc_mode_info(uint16_t mode, vbe_mode_info_t *mode_info)
+{
+	v86.ctl = 0;
+	v86.addr = 0x10;
+	v86.eax = VBE_FUNC_MODE_INFO;
+	v86.ecx = mode;
+	v86.es = VTOPSEG(mode_info);
+	v86.edi = VTOPOFF(mode_info);
+	v86int();
+	return (vbe_status(v86.eax));
+}
+
+/*
+ * VBE Function 02h: Set VBE Mode
+ */
+static int
+vbefunc_set_mode(uint16_t mode)
+{
+	v86.ctl = 0;
+	v86.addr = 0x10;
+	v86.eax = VBE_FUNC_SET_MODE;
+	v86.ebx = mode;
+	v86.es = VTOPSEG(NULL);
+	v86.edi = VTOPOFF(NULL);
+	v86int();
+	return (vbe_status(v86.eax));
+}
+
+static void
+vbe_setup_framebuffer(vbe_mode_info_t *info)
+{
+	vbefb.fb_addr = (uint64_t)info->phys_base_ptr;
+	vbefb.fb_size = info->x_resolution * info->y_resolution;
+	vbefb.fb_height = info->y_resolution;
+	vbefb.fb_width = info->x_resolution;
+	vbefb.fb_stride =
+	    8 * (info->bytes_per_scan_line / info->bits_per_pixel);
+	vbefb.fb_mask_red = (((uint32_t)1 << info->red_mask_size) - 1) <<
+	    info->red_field_position;
+	vbefb.fb_mask_green = (((uint32_t)1 << info->green_mask_size) - 1) <<
+	    info->green_field_position;
+	vbefb.fb_mask_blue = (((uint32_t)1 << info->blue_mask_size) - 1) <<
+	    info->blue_field_position;
+	vbefb.fb_mask_reserved = (((uint32_t)1 << info->rsvd_mask_size) - 1) <<
+	    info->rsvd_field_position;
+}
+
+void
+vbe_detect(void)
+{
+	int status;
+
+	memcpy(ctlr_info.vbe_signature, "VBE2", 4);
+	status = vbefunc_ctlr_info(&ctlr_info);
+	if (status != VBE_SUCCESS)
+		return;
+	setenv("vbe.vendor.name",
+	    (char *)PTOV(BIOS_SADDRTOLADDR(ctlr_info.oem_vendor_name_ptr)), 1);
+	setenv("vbe.product.name",
+	    (char *)PTOV(BIOS_SADDRTOLADDR(ctlr_info.oem_product_name_ptr)), 1);
+	setenv("vbe.product.revision",
+	    (char *)PTOV(BIOS_SADDRTOLADDR(ctlr_info.oem_product_rev_ptr)), 1);
+}
+
+struct efi_fb *
+vbe_init(void)
+{
+	vbe_mode_info_t mode_info;
+	long width, height;
+	uint16_t *mp;
+	char *ev, *x;
+
+	if (memcmp(ctlr_info.vbe_signature, "VESA", 4) != 0 ||
+	    ctlr_info.vbe_version < 0x200)
+		return (NULL);
+	/* XXX improve checking */
+	ev = getenv("vbe.video");
+	if (ev == NULL || (x = strchr(ev, 'x')) == NULL)
+		return (NULL);
+	*x = '\0';
+	width = strtol(ev, NULL, 10);
+	height = strtol(x + 1, NULL, 10);
+	if (width <= 0 || height <= 0)
+		return (NULL);
+	mp = (uint16_t *)PTOV(BIOS_SADDRTOLADDR(ctlr_info.video_mode_ptr));
+	for (; *mp != 0xffff; mp++) {
+		if (vbefunc_mode_info(*mp, &mode_info) != VBE_SUCCESS)
+			continue;
+		if (mode_info.x_resolution == width &&
+		    mode_info.y_resolution == height &&
+		    VBE_MODE_SUPPORTED(mode_info))
+			break;
+	}
+	if (*mp == 0xffff)
+		return (NULL);
+	vbefunc_set_mode(*mp);
+	vbe_setup_framebuffer(&mode_info);
+	return (&vbefb);
+}
+
+COMMAND_SET(vbe, "vbe", "VESA BIOS extension", command_vbe);
+
+static int
+command_vbe(int argc, char *argv[])
+{
+	vbe_mode_info_t mode_info;
+	int listall = 0;
+	uint16_t *mp;
+	char buf[128];
+
+	if (argc < 2 || argc > 3)
+		goto usage;
+	if (!strcmp(argv[1], "list")) {
+		if (argc == 3) {
+			if (!strcmp(argv[2], "all"))
+				listall = 1;
+			else
+				goto usage;
+		}
+		if (memcmp(ctlr_info.vbe_signature, "VESA", 4) != 0 ||
+		    ctlr_info.vbe_version < 0x200) {
+			snprintf(command_errbuf, sizeof(command_errbuf),
+			    "error: vbe_signature:%c%c%c%c vbe_version:%#x",
+			    ctlr_info.vbe_signature[0],
+			    ctlr_info.vbe_signature[1],
+			    ctlr_info.vbe_signature[2],
+			    ctlr_info.vbe_signature[3],
+			    ctlr_info.vbe_version);
+			return (CMD_ERROR);
+		}
+		mp = (uint16_t *)PTOV(BIOS_SADDRTOLADDR(ctlr_info.video_mode_ptr));
+		pager_open();
+		for (; *mp != 0xffff; mp++) {
+			if (vbefunc_mode_info(*mp, &mode_info) != VBE_SUCCESS)
+				continue;
+			if (listall || VBE_MODE_SUPPORTED(mode_info)) {
+				snprintf(buf, sizeof(buf), "%dx%dx%d",
+				    mode_info.x_resolution,
+				    mode_info.y_resolution,
+				    mode_info.bits_per_pixel);
+				pager_output(buf);
+				pager_output("\n");
+			}
+		}
+		pager_close();
+		return (CMD_OK);
+	}
+usage:
+	snprintf(command_errbuf, sizeof(command_errbuf),
+	    "usage: %s list", argv[0]);
+	return (CMD_ERROR);
+}
diff --git a/sys/boot/pc32/libi386/vbe.h b/sys/boot/pc32/libi386/vbe.h
new file mode 100644
index 0000000000..94e2a0eeab
--- /dev/null
+++ b/sys/boot/pc32/libi386/vbe.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sascha Wildner <swildner@dragonflybsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+/*
+ * General definitions
+ */
+#define	VBE_H_MASK		0xff00
+#define	VBE_L_MASK		0x00ff
+#define	VBE_FUNC_BASE		0x4f00
+
+/*
+ * VBE function numbers
+ */
+#define	VBE_FUNC_CTLR_INFO	(VBE_FUNC_BASE + 0)
+#define	VBE_FUNC_MODE_INFO	(VBE_FUNC_BASE + 1)
+#define	VBE_FUNC_SET_MODE	(VBE_FUNC_BASE + 2)
+
+/*
+ * VBE return status
+ */
+#define	VBE_SUCCESS		0	/* function call successful */
+#define	VBE_UNSUPPORTED		1	/* function is not supported */
+#define	VBE_FAILED		2	/* function call failed */
+#define	VBE_UNSUPPORTED_HW	3	/* unsupported in current h/w conf */
+#define	VBE_INVALID		4	/* invalid in current video mode */
+#define	VBE_UNKNOWN		5	/* AL value unknown */
+
+/*
+ * A valid mode is one that has a depth of 24 or 32, uses the "direct"
+ * memory model, and provides us with a pointer to the framebuffer.
+ */
+#define	VBE_MODE_SUPPORTED(mi)	(((mi).bits_per_pixel == 24 ||		\
+				  (mi).bits_per_pixel == 32) &&		\
+				 (mi).memory_model == VBE_MM_DIRECT &&	\
+				 (mi).phys_base_ptr != 0)
+
+/*
+ * VBE Function 00h: Return VBE Controller Information
+ */
+typedef struct {
+	uint8_t		vbe_signature[4];	/* signature, e.g. "VESA" */
+	uint16_t	vbe_version;		/* VBE version */
+	uint32_t	oem_string_ptr;		/* far ptr to OEM string */
+	uint32_t	capabilities;
+	uint32_t	video_mode_ptr;		/* far ptr to mode list */
+	uint16_t	total_memory;		/* number of 64K blocks */
+	uint16_t	oem_software_rev;	/* VBE software revision */
+	uint32_t	oem_vendor_name_ptr;	/* far ptr to vendor name */
+	uint32_t	oem_product_name_ptr;	/* far ptr to product name */
+	uint32_t	oem_product_rev_ptr;	/* far ptr to product rev. */
+	uint8_t		reserved[222];
+	uint8_t		oem_data[256];		/* data area for OEM strings */
+} __packed vbe_ctlr_info_t;
+
+/*
+ * VBE Function 01h: Return VBE Mode Information
+ */
+typedef struct {
+	uint16_t	mode_attributes;
+	uint8_t		win_a_attributes;
+	uint8_t		win_b_attributes;
+	uint16_t	win_granularity;
+	uint16_t	win_size;
+	uint16_t	win_a_segment;
+	uint16_t	win_b_segment;
+	uint32_t	win_func_ptr;
+	uint16_t	bytes_per_scan_line;
+	uint16_t	x_resolution;
+	uint16_t	y_resolution;
+	uint8_t		x_char_size;
+	uint8_t		y_char_size;
+	uint8_t		number_of_planes;
+	uint8_t		bits_per_pixel;
+	uint8_t		number_of_banks;
+#define	VBE_MM_DIRECT	0x06
+	uint8_t		memory_model;
+	uint8_t		bank_size;
+	uint8_t		number_of_image_pages;
+	uint8_t		reserved;
+	uint8_t		red_mask_size;
+	uint8_t		red_field_position;
+	uint8_t		green_mask_size;
+	uint8_t		green_field_position;
+	uint8_t		blue_mask_size;
+	uint8_t		blue_field_position;
+	uint8_t		rsvd_mask_size;
+	uint8_t		rsvd_field_position;
+	uint8_t		direct_color_mode_info;
+	uint32_t	phys_base_ptr;
+	uint8_t		reserved2[6];
+	uint16_t	lin_bytes_per_scan_line;
+	uint8_t		bnk_number_of_image_pages;
+	uint8_t		lin_number_of_image_pages;
+	uint8_t		lin_red_mask_size;
+	uint8_t		lin_red_field_position;
+	uint8_t		lin_green_mask_size;
+	uint8_t		lin_green_field_position;
+	uint8_t		lin_blue_mask_size;
+	uint8_t		lin_blue_field_position;
+	uint8_t		lin_rsvd_mask_size;
+	uint8_t		lin_rsvd_field_position;
+	uint32_t	max_pixel_clock;
+	uint8_t		reserved3[189];
+} __packed vbe_mode_info_t;
diff --git a/sys/boot/pc32/loader/help.i386 b/sys/boot/pc32/loader/help.i386
index d629582f1f..ff8fe1ced8 100644
--- a/sys/boot/pc32/loader/help.i386
+++ b/sys/boot/pc32/loader/help.i386
@@ -67,3 +67,14 @@
 	See also vfs.root.mountfrom variable, under `set tunables' help item.
 
 ################################################################################
+# Tvbe Slist DList the supported VESA modes.
+
+	vbe list
+
+	Lists the VESA modes that are supported by the loader.
+
+	vbe list all
+
+	Lists all available VESA modes, supported or not.
+
+################################################################################
diff --git a/sys/boot/pc32/loader/main.c b/sys/boot/pc32/loader/main.c
index 4ead0a1d0c..2485b7d393 100644
--- a/sys/boot/pc32/loader/main.c
+++ b/sys/boot/pc32/loader/main.c
@@ -254,6 +254,9 @@ main(void)
     /* detect SMBIOS for future reference */
     smbios_detect(NULL);
 
+    /* detect VBE for future reference */
+    vbe_detect();
+
     /* enable EHCI */
     setenv("ehci_load", "YES", 1);
 
-- 
2.28.0

