aboutsummaryrefslogtreecommitdiff
path: root/stand/i386
diff options
context:
space:
mode:
authorToomas Soome <tsoome@FreeBSD.org>2020-12-21 05:31:16 +0000
committerToomas Soome <tsoome@FreeBSD.org>2021-01-02 19:41:36 +0000
commit3630506b9daec9167a89bc4525638ea45a00769e (patch)
tree8276b2e49eeaedbc1fb1806c5a9f64ee642bdc57 /stand/i386
parentbd03acedb804add1e22178d50eb2bfb703974ddf (diff)
downloadsrc-3630506b9daec9167a89bc4525638ea45a00769e.tar.gz
src-3630506b9daec9167a89bc4525638ea45a00769e.zip
loader: implement framebuffer console
Draw console on efi. Add vbe framebuffer for BIOS loader (vbe off, vbe on, vbe list, vbe set xxx). autoload font (/boot/fonts) based on resolution and font size. Add command loadfont (set font by file) and variable screen.font (set font by size). Pass loaded font to kernel. Export variables: screen.height screen.width screen.depth Add gfx primitives to draw the screen and put png image on the screen. Rework menu draw to iterate list of consoles to enamble device specific output. Probably something else I forgot... Relnotes: yes Differential Revision: https://reviews.freebsd.org/D27420
Diffstat (limited to 'stand/i386')
-rw-r--r--stand/i386/libi386/Makefile9
-rw-r--r--stand/i386/libi386/bootinfo.c11
-rw-r--r--stand/i386/libi386/bootinfo32.c3
-rw-r--r--stand/i386/libi386/bootinfo64.c3
-rw-r--r--stand/i386/libi386/libi386.h1
-rw-r--r--stand/i386/libi386/vbe.c1226
-rw-r--r--stand/i386/libi386/vbe.h163
-rw-r--r--stand/i386/libi386/vidconsole.c659
-rw-r--r--stand/i386/loader/Makefile13
-rw-r--r--stand/i386/loader/main.c10
10 files changed, 1916 insertions, 182 deletions
diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile
index 779575bff071..3dea78f16929 100644
--- a/stand/i386/libi386/Makefile
+++ b/stand/i386/libi386/Makefile
@@ -9,7 +9,7 @@ SRCS= bio.c biosacpi.c biosdisk.c biosmem.c biospnp.c \
comconsole.c devicename.c elf32_freebsd.c \
elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \
i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.S \
- time.c vidconsole.c amd64_tramp.S spinconsole.c
+ time.c vidconsole.c vbe.c amd64_tramp.S spinconsole.c
.PATH: ${ZFSSRC}
SRCS+= devicename_stubs.c
CFLAGS+= -I${ZFSSRC}
@@ -29,8 +29,13 @@ CFLAGS+= -DDISK_DEBUG
.endif
# terminal emulation
-CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken
+.if ${BOOT_FRAMEBUFFER_MODE:Uno} == "yes"
+CFLAGS.vidconsole.c+= -DFRAMEBUFFER_MODE
+.endif
+CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
CFLAGS.teken.c+= -I${SRCTOP}/sys/teken
+CFLAGS.bootinfo.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
+CFLAGS.vbe.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
# XXX: make alloca() useable
CFLAGS+= -Dalloca=__builtin_alloca
diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c
index 41901e5f928f..71e07cfb9702 100644
--- a/stand/i386/libi386/bootinfo.c
+++ b/stand/i386/libi386/bootinfo.c
@@ -32,10 +32,21 @@ __FBSDID("$FreeBSD$");
#include <sys/reboot.h>
#include <sys/boot.h>
#include <sys/linker.h>
+#include <gfx_fb.h>
#include "bootstrap.h"
#include "libi386.h"
+#include "vbe.h"
#include "btxv86.h"
+void
+bi_load_vbe_data(struct preloaded_file *kfp)
+{
+ if (vbe_available()) {
+ file_addmetadata(kfp, MODINFOMD_VBE_FB,
+ sizeof(gfx_state.tg_fb), &gfx_state.tg_fb);
+ }
+}
+
int
bi_getboothowto(char *kargs)
{
diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c
index e6a92a2164d7..23b02693cf48 100644
--- a/stand/i386/libi386/bootinfo32.c
+++ b/stand/i386/libi386/bootinfo32.c
@@ -205,6 +205,8 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
/* copy our environment */
envp = addr;
addr = bi_copyenv(addr);
@@ -225,6 +227,7 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
+ bi_load_vbe_data(kfp);
/* Figure out the size and location of the metadata */
*modulep = addr;
diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c
index 93723a6943e3..9f038f2a4f2b 100644
--- a/stand/i386/libi386/bootinfo64.c
+++ b/stand/i386/libi386/bootinfo64.c
@@ -227,6 +227,8 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
/* place the metadata before anything */
module = *modulep = addr;
@@ -245,6 +247,7 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
+ bi_load_vbe_data(kfp);
size = bi_copymodules64(0);
diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h
index d25df8fc44a5..dbd6565f8d1c 100644
--- a/stand/i386/libi386/libi386.h
+++ b/stand/i386/libi386/libi386.h
@@ -145,6 +145,7 @@ void biosacpi_detect(void);
int i386_autoload(void);
+void bi_load_vbe_data(struct preloaded_file *kfp);
int bi_getboothowto(char *kargs);
void bi_setboothowto(int howto);
vm_offset_t bi_copyenv(vm_offset_t addr);
diff --git a/stand/i386/libi386/vbe.c b/stand/i386/libi386/vbe.c
new file mode 100644
index 000000000000..7681eb633b85
--- /dev/null
+++ b/stand/i386/libi386/vbe.c
@@ -0,0 +1,1226 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ * Copyright 2020 Toomas Soome
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <stand.h>
+#include <sys/param.h>
+#include <machine/psl.h>
+#include <machine/cpufunc.h>
+#include <stdbool.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <gfx_fb.h>
+#include <dev/vt/hw/vga/vt_vga_reg.h>
+#include "libi386.h"
+#include "vbe.h"
+
+/*
+ * VESA BIOS Extensions routines
+ */
+
+static struct vbeinfoblock *vbe;
+static struct modeinfoblock *vbe_mode;
+/* The default VGA color palette format is 6 bits per primary color. */
+int palette_format = 6;
+
+#define VESA_MODE_BASE 0x100
+#define VESA_MODE_MAX 0x1ff
+#define VESA_MODE_COUNT (VESA_MODE_MAX - VESA_MODE_BASE + 1)
+
+/*
+ * palette array for 8-bit indexed colors. In this case, cmap does store
+ * index and pe8 does store actual RGB. This is needed because we may
+ * not be able to read palette data from hardware.
+ */
+struct paletteentry *pe8 = NULL;
+
+static struct named_resolution {
+ const char *name;
+ const char *alias;
+ unsigned int width;
+ unsigned int height;
+} resolutions[] = {
+ {
+ .name = "480p",
+ .width = 640,
+ .height = 480,
+ },
+ {
+ .name = "720p",
+ .width = 1280,
+ .height = 720,
+ },
+ {
+ .name = "1080p",
+ .width = 1920,
+ .height = 1080,
+ },
+ {
+ .name = "2160p",
+ .alias = "4k",
+ .width = 3840,
+ .height = 2160,
+ },
+ {
+ .name = "5k",
+ .width = 5120,
+ .height = 2880,
+ }
+};
+
+static bool
+vbe_resolution_compare(struct named_resolution *res, const char *cmp)
+{
+
+ if (strcasecmp(res->name, cmp) == 0)
+ return (true);
+ if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
+ return (true);
+ return (false);
+}
+
+static void
+vbe_get_max_resolution(int *width, int *height)
+{
+ struct named_resolution *res;
+ char *maxres;
+ char *height_start, *width_start;
+ int idx;
+
+ *width = *height = 0;
+ maxres = getenv("vbe_max_resolution");
+ /* No max_resolution set? Bail out; choose highest resolution */
+ if (maxres == NULL)
+ return;
+ /* See if it matches one of our known resolutions */
+ for (idx = 0; idx < nitems(resolutions); ++idx) {
+ res = &resolutions[idx];
+ if (vbe_resolution_compare(res, maxres)) {
+ *width = res->width;
+ *height = res->height;
+ return;
+ }
+ }
+ /* Not a known resolution, try to parse it; make a copy we can modify */
+ maxres = strdup(maxres);
+ if (maxres == NULL)
+ return;
+ height_start = strchr(maxres, 'x');
+ if (height_start == NULL) {
+ free(maxres);
+ return;
+ }
+ width_start = maxres;
+ *height_start++ = 0;
+ /* Errors from this will effectively mean "no max" */
+ *width = (int)strtol(width_start, NULL, 0);
+ *height = (int)strtol(height_start, NULL, 0);
+ free(maxres);
+}
+
+int
+vga_get_reg(int reg, int index)
+{
+ return (inb(reg + index));
+}
+
+int
+vga_get_atr(int reg, int i)
+{
+ int ret;
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+ outb(reg + VGA_AC_WRITE, i);
+ ret = inb(reg + VGA_AC_READ);
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+
+ return (ret);
+}
+
+void
+vga_set_atr(int reg, int i, int v)
+{
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+ outb(reg + VGA_AC_WRITE, i);
+ outb(reg + VGA_AC_WRITE, v);
+
+ (void) inb(reg + VGA_GEN_INPUT_STAT_1);
+}
+
+void
+vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
+{
+ outb(reg + indexreg, index);
+ outb(reg + datareg, val);
+}
+
+int
+vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
+{
+ outb(reg + indexreg, index);
+ return (inb(reg + datareg));
+}
+
+int
+vga_get_crtc(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
+}
+
+void
+vga_set_crtc(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
+}
+
+int
+vga_get_seq(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i));
+}
+
+void
+vga_set_seq(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v);
+}
+
+int
+vga_get_grc(int reg, int i)
+{
+ return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i));
+}
+
+void
+vga_set_grc(int reg, int i, int v)
+{
+ vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v);
+}
+
+/* Actually assuming mode 3. */
+void
+bios_set_text_mode(int mode)
+{
+ int atr;
+
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /*
+ * The mode change should reset the palette format to
+ * 6 bits, but apparently some systems do fail with 8-bit
+ * palette, so we switch to 6-bit here.
+ */
+ m = 0x0600;
+ (void) biosvbe_palette_format(&m);
+ palette_format = m;
+ }
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = mode; /* set VGA text mode */
+ v86int();
+ atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL);
+ atr &= ~VGA_AC_MC_BI;
+ atr &= ~VGA_AC_MC_ELG;
+ vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr);
+
+ gfx_state.tg_mode = mode;
+ gfx_state.tg_fb_type = FB_TEXT;
+ gfx_state.tg_fb.fb_height = TEXT_ROWS;
+ gfx_state.tg_fb.fb_width = TEXT_COLS;
+
+ gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16;
+ gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8;
+ gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0;
+ gfx_state.tg_ctype = CT_INDEXED;
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL);
+}
+
+/* Function 00h - Return VBE Controller Information */
+static int
+biosvbe_info(struct vbeinfoblock *vbep)
+{
+ struct vbeinfoblock *rvbe;
+ int ret;
+
+ if (vbep == NULL)
+ return (VBE_FAILED);
+
+ rvbe = bio_alloc(sizeof(*rvbe));
+ if (rvbe == NULL)
+ return (VBE_FAILED);
+
+ /* Now check if we have vesa. */
+ memset(rvbe, 0, sizeof (*vbe));
+ memcpy(rvbe->VbeSignature, "VBE2", 4);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f00;
+ v86.es = VTOPSEG(rvbe);
+ v86.edi = VTOPOFF(rvbe);
+ v86int();
+ ret = v86.eax & 0xffff;
+
+ if (ret != VBE_SUCCESS)
+ goto done;
+
+ if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) {
+ ret = VBE_NOTSUP;
+ goto done;
+ }
+ bcopy(rvbe, vbep, sizeof(*vbep));
+done:
+ bio_free(rvbe, sizeof(*rvbe));
+ return (ret);
+}
+
+/* Function 01h - Return VBE Mode Information */
+static int
+biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
+{
+ struct modeinfoblock *rmi;
+ int ret;
+
+ rmi = bio_alloc(sizeof(*rmi));
+ if (rmi == NULL)
+ return (VBE_FAILED);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f01;
+ v86.ecx = mode;
+ v86.es = VTOPSEG(rmi);
+ v86.edi = VTOPOFF(rmi);
+ v86int();
+
+ ret = v86.eax & 0xffff;
+ if (ret != VBE_SUCCESS)
+ goto done;
+ bcopy(rmi, mi, sizeof(*rmi));
+done:
+ bio_free(rmi, sizeof(*rmi));
+ return (ret);
+}
+
+/* Function 02h - Set VBE Mode */
+static int
+biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
+{
+ int rv;
+
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /*
+ * The mode change should reset the palette format to
+ * 6 bits, but apparently some systems do fail with 8-bit
+ * palette, so we switch to 6-bit here.
+ */
+ m = 0x0600;
+ if (biosvbe_palette_format(&m) == VBE_SUCCESS)
+ palette_format = m;
+ }
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f02;
+ v86.ebx = mode | 0x4000; /* set linear FB bit */
+ v86.es = VTOPSEG(ci);
+ v86.edi = VTOPOFF(ci);
+ v86int();
+ rv = v86.eax & 0xffff;
+ if (vbe->Capabilities & VBE_CAP_DAC8) {
+ int m;
+
+ /* Switch to 8-bits per primary color. */
+ m = 0x0800;
+ if (biosvbe_palette_format(&m) == VBE_SUCCESS)
+ palette_format = m;
+ }
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL);
+ return (rv);
+}
+
+/* Function 03h - Get VBE Mode */
+static int
+biosvbe_get_mode(int *mode)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f03;
+ v86int();
+ *mode = v86.ebx & 0x3fff; /* Bits 0-13 */
+ return (v86.eax & 0xffff);
+}
+
+/* Function 08h - Set/Get DAC Palette Format */
+int
+biosvbe_palette_format(int *format)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f08;
+ v86.ebx = *format;
+ v86int();
+ *format = (v86.ebx >> 8) & 0xff;
+ return (v86.eax & 0xffff);
+}
+
+/* Function 09h - Set/Get Palette Data */
+static int
+biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f09;
+ v86.ebx = mode;
+ v86.edx = reg;
+ v86.ecx = 1;
+ v86.es = VTOPSEG(pe);
+ v86.edi = VTOPOFF(pe);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+/*
+ * Function 15h BL=00h - Report VBE/DDC Capabilities
+ *
+ * int biosvbe_ddc_caps(void)
+ * return: VBE/DDC capabilities
+ */
+static int
+biosvbe_ddc_caps(void)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f15; /* display identification extensions */
+ v86.ebx = 0; /* report DDC capabilities */
+ v86.ecx = 0; /* controller unit number (00h = primary) */
+ v86.es = 0;
+ v86.edi = 0;
+ v86int();
+ if (VBE_ERROR(v86.eax & 0xffff))
+ return (0);
+ return (v86.ebx & 0xffff);
+}
+
+/* Function 11h BL=01h - Flat Panel status */
+static int
+biosvbe_ddc_read_flat_panel_info(void *buf)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f11; /* Flat Panel Interface extensions */
+ v86.ebx = 1; /* Return Flat Panel Information */
+ v86.es = VTOPSEG(buf);
+ v86.edi = VTOPOFF(buf);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+/* Function 15h BL=01h - Read EDID */
+static int
+biosvbe_ddc_read_edid(int blockno, void *buf)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0x4f15; /* display identification extensions */
+ v86.ebx = 1; /* read EDID */
+ v86.ecx = 0; /* controller unit number (00h = primary) */
+ v86.edx = blockno;
+ v86.es = VTOPSEG(buf);
+ v86.edi = VTOPOFF(buf);
+ v86int();
+ return (v86.eax & 0xffff);
+}
+
+static int
+vbe_mode_is_supported(struct modeinfoblock *mi)
+{
+ if ((mi->ModeAttributes & 0x01) == 0)
+ return (0); /* mode not supported by hardware */
+ if ((mi->ModeAttributes & 0x08) == 0)
+ return (0); /* linear fb not available */
+ if ((mi->ModeAttributes & 0x10) == 0)
+ return (0); /* text mode */
+ if (mi->NumberOfPlanes != 1)
+ return (0); /* planar mode not supported */
+ if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
+ mi->MemoryModel != 0x06 /* Direct Color */)
+ return (0); /* unsupported pixel format */
+ return (1);
+}
+
+static bool
+vbe_check(void)
+{
+
+ if (vbe == NULL) {
+ printf("VBE not available\n");
+ return (false);
+ }
+ return (true);
+}
+
+static int
+mode_set(struct env_var *ev, int flags __unused, const void *value)
+{
+ int mode;
+
+ if (strcmp(ev->ev_name, "hw.vga.textmode") == 0) {
+ unsigned long v;
+ char *end;
+
+ if (value == NULL)
+ return (0);
+ /* VT(4) describes hw.vga.textmode values 0 or 1. */
+ errno = 0;
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *(char *)value == '\0' || *end != '\0' ||
+ (v != 0 && v != 1))
+ return (EINVAL);
+ env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK,
+ value, NULL, NULL);
+ if (v == 1) {
+ reset_font_flags();
+ bios_text_font(true);
+ bios_set_text_mode(VGA_TEXT_MODE);
+ (void) cons_update_mode(false);
+ return (0);
+ }
+ } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) {
+ env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
+ value, NULL, NULL);
+ } else {
+ return (EINVAL);
+ }
+
+ mode = vbe_default_mode();
+ if (gfx_state.tg_mode != mode) {
+ reset_font_flags();
+ bios_text_font(false);
+ vbe_set_mode(mode);
+ cons_update_mode(true);
+ }
+ return (0);
+}
+
+void
+vbe_init(void)
+{
+ /* First set FB for text mode. */
+ gfx_state.tg_fb_type = FB_TEXT;
+ gfx_state.tg_fb.fb_height = TEXT_ROWS;
+ gfx_state.tg_fb.fb_width = TEXT_COLS;
+ gfx_state.tg_ctype = CT_INDEXED;
+ gfx_state.tg_mode = 3;
+
+ if (vbe == NULL)
+ vbe = malloc(sizeof(*vbe));
+
+ if (vbe_mode == NULL) {
+ vbe_mode = malloc(sizeof(*vbe_mode));
+ if (vbe_mode == NULL) {
+ free(vbe);
+ vbe = NULL;
+ }
+ }
+
+ if (biosvbe_info(vbe) != VBE_SUCCESS) {
+ free(vbe);
+ vbe = NULL;
+ free(vbe_mode);
+ vbe_mode = NULL;
+ }
+
+ env_setenv("hw.vga.textmode", EV_VOLATILE, "1", mode_set,
+ env_nounset);
+ env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
+ env_nounset);
+ /* vbe_set_mode() will set up the rest. */
+}
+
+bool
+vbe_available(void)
+{
+ return (gfx_state.tg_fb_type == FB_VBE);
+}
+
+int
+vbe_set_palette(const struct paletteentry *entry, size_t slot)
+{
+ struct paletteentry pe;
+ int mode, ret;
+
+ if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
+ return (1);
+
+ if (gfx_state.tg_ctype != CT_INDEXED) {
+ return (1);
+ }
+
+ pe.Blue = entry->Blue;
+ pe.Green = entry->Green;
+ pe.Red = entry->Red;
+ pe.Reserved = entry->Reserved;
+
+ if (vbe->Capabilities & VBE_CAP_SNOW)
+ mode = 0x80;
+ else
+ mode = 0;
+
+ ret = biosvbe_palette_data(mode, slot, &pe);
+
+ return (ret == VBE_SUCCESS ? 0 : 1);
+}
+
+int
+vbe_get_mode(void)
+{
+ return (gfx_state.tg_mode);
+}
+
+int
+vbe_set_mode(int modenum)
+{
+ struct modeinfoblock mi;
+ int bpp, ret;
+
+ if (!vbe_check())
+ return (1);
+
+ ret = biosvbe_get_mode_info(modenum, &mi);
+ if (VBE_ERROR(ret)) {
+ printf("mode 0x%x invalid\n", modenum);
+ return (1);
+ }
+
+ if (!vbe_mode_is_supported(&mi)) {
+ printf("mode 0x%x not supported\n", modenum);
+ return (1);
+ }
+
+ /* calculate bytes per pixel */
+ switch (mi.BitsPerPixel) {
+ case 32:
+ case 24:
+ case 16:
+ case 15:
+ case 8:
+ break;
+ default:
+ printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
+ return (1);
+ }
+
+ ret = biosvbe_set_mode(modenum, NULL);
+ if (VBE_ERROR(ret)) {
+ printf("mode 0x%x could not be set\n", modenum);
+ return (1);
+ }
+
+ gfx_state.tg_mode = modenum;
+ gfx_state.tg_fb_type = FB_VBE;
+ /* make sure we have current MI in vbestate */
+ memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
+
+ gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
+ gfx_state.tg_fb.fb_height = mi.YResolution;
+ gfx_state.tg_fb.fb_width = mi.XResolution;
+ gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel;
+
+ /* Bytes per pixel */
+ bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;
+
+ /* vbe_mode_is_supported() excludes the rest */
+ switch (mi.MemoryModel) {
+ case 0x4:
+ gfx_state.tg_ctype = CT_INDEXED;
+ break;
+ case 0x6:
+ gfx_state.tg_ctype = CT_RGB;
+ break;
+ }
+
+#define COLOR_MASK(size, pos) (((1 << size) - 1) << pos)
+ if (gfx_state.tg_ctype == CT_INDEXED) {
+ gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16);
+ gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8);
+ gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0);
+ } else if (vbe->VbeVersion >= 0x300) {
+ gfx_state.tg_fb.fb_mask_red =
+ COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition);
+ gfx_state.tg_fb.fb_mask_green =
+ COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition);
+ gfx_state.tg_fb.fb_mask_blue =
+ COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition);
+ } else {
+ gfx_state.tg_fb.fb_mask_red =
+ COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition);
+ gfx_state.tg_fb.fb_mask_green =
+ COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition);
+ gfx_state.tg_fb.fb_mask_blue =
+ COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition);
+ }
+ gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red |
+ gfx_state.tg_fb.fb_mask_green |
+ gfx_state.tg_fb.fb_mask_blue);
+
+ if (vbe->VbeVersion >= 0x300)
+ gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp;
+ else
+ gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp;
+
+ gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride *
+ bpp;
+
+ return (0);
+}
+
+static void *
+vbe_farptr(uint32_t farptr)
+{
+ return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
+}
+
+/*
+ * Verify existance of mode number or find mode by
+ * dimensions. If depth is not given, walk values 32, 24, 16, 8.
+ */
+static int
+vbe_find_mode_xydm(int x, int y, int depth, int m)
+{
+ struct modeinfoblock mi;
+ uint32_t farptr;
+ uint16_t mode;
+ int safety, i;
+
+ memset(vbe, 0, sizeof (*vbe));
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ return (0);
+ if (vbe->VideoModePtr == 0)
+ return (0);
+
+ if (m != -1)
+ i = 8;
+ else if (depth == -1)
+ i = 32;
+ else
+ i = depth;
+
+ while (i > 0) {
+ farptr = vbe->VideoModePtr;
+ safety = 0;
+ while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
+ safety++;
+ farptr += 2;
+ if (safety == VESA_MODE_COUNT)
+ break;
+ if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
+ continue;
+ }
+ /* we only care about linear modes here */
+ if (vbe_mode_is_supported(&mi) == 0)
+ continue;
+
+ if (m != -1) {
+ if (m == mode)
+ return (mode);
+ else
+ continue;
+ }
+
+ if (mi.XResolution == x &&
+ mi.YResolution == y &&
+ mi.BitsPerPixel == i)
+ return (mode);
+ }
+ if (depth != -1)
+ break;
+
+ i -= 8;
+ }
+
+ return (0);
+}
+
+static int
+vbe_find_mode(char *str)
+{
+ int x, y, depth;
+
+ if (!gfx_parse_mode_str(str, &x, &y, &depth))
+ return (0);
+
+ return (vbe_find_mode_xydm(x, y, depth, -1));
+}
+
+static void
+vbe_dump_mode(int modenum, struct modeinfoblock *mi)
+{
+ printf("0x%x=%dx%dx%d", modenum,
+ mi->XResolution, mi->YResolution, mi->BitsPerPixel);
+}
+
+static bool
+vbe_get_edid(edid_res_list_t *res)
+{
+ struct vesa_edid_info *edid_info;
+ const uint8_t magic[] = EDID_MAGIC;
+ int ddc_caps;
+ bool ret = false;
+
+ ddc_caps = biosvbe_ddc_caps();
+ if (ddc_caps == 0) {
+ return (ret);
+ }
+
+ edid_info = bio_alloc(sizeof (*edid_info));
+ if (edid_info == NULL)
+ return (ret);
+ memset(edid_info, 0, sizeof (*edid_info));
+
+ if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info)))
+ goto done;
+
+ if (memcmp(edid_info, magic, sizeof (magic)) != 0)
+ goto done;
+
+ /* Unknown EDID version. */
+ if (edid_info->header.version != 1)
+ goto done;
+
+ ret = gfx_get_edid_resolution(edid_info, res);
+done:
+ bio_free(edid_info, sizeof (*edid_info));
+ return (ret);
+}
+
+static bool
+vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight)
+{
+ struct vesa_flat_panel_info *fp_info;
+ bool ret = false;
+
+ fp_info = bio_alloc(sizeof (*fp_info));
+ if (fp_info == NULL)
+ return (ret);
+ memset(fp_info, 0, sizeof (*fp_info));
+
+ if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
+ goto done;
+
+ *pwidth = fp_info->HSize;
+ *pheight = fp_info->VSize;
+ ret = true;
+
+done:
+ bio_free(fp_info, sizeof (*fp_info));
+ return (ret);
+}
+
+static void
+vbe_print_memory(unsigned vmem)
+{
+ char unit = 'K';
+
+ vmem /= 1024;
+ if (vmem >= 10240000) {
+ vmem /= 1048576;
+ unit = 'G';
+ } else if (vmem >= 10000) {
+ vmem /= 1024;
+ unit = 'M';
+ }
+ printf("Total memory: %u%cB\n", vmem, unit);
+}
+
+static void
+vbe_print_vbe_info(struct vbeinfoblock *vbep)
+{
+ char *oemstring = "";
+ char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
+
+ if (vbep->OemStringPtr != 0)
+ oemstring = vbe_farptr(vbep->OemStringPtr);
+
+ if (vbep->OemVendorNamePtr != 0)
+ oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
+
+ if (vbep->OemProductNamePtr != 0)
+ oemproductname = vbe_farptr(vbep->OemProductNamePtr);
+
+ if (vbep->OemProductRevPtr != 0)
+ oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
+
+ printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
+ vbep->VbeVersion & 0xF, oemstring);
+
+ if (vbep->OemSoftwareRev != 0) {
+ printf("OEM Version %d.%d, %s (%s, %s)\n",
+ vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
+ oemvendor, oemproductname, oemproductrev);
+ }
+ vbe_print_memory(vbep->TotalMemory << 16);
+ printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages);
+}
+
+/* List available modes, filter by depth. If depth is -1, list all. */
+void
+vbe_modelist(int depth)
+{
+ struct modeinfoblock mi;
+ uint32_t farptr;
+ uint16_t mode;
+ int nmodes = 0, safety = 0;
+ int ddc_caps;
+ uint32_t width, height;
+ bool edid = false;
+ edid_res_list_t res;
+ struct resolution *rp;
+
+ if (!vbe_check())
+ return;
+
+ ddc_caps = biosvbe_ddc_caps();
+ if (ddc_caps & 3) {
+ printf("DDC");
+ if (ddc_caps & 1)
+ printf(" [DDC1]");
+ if (ddc_caps & 2)
+ printf(" [DDC2]");
+
+ TAILQ_INIT(&res);
+ edid = vbe_get_edid(&res);
+ if (edid) {
+ printf(": EDID");
+ while ((rp = TAILQ_FIRST(&res)) != NULL) {
+ printf(" %dx%d", rp->width, rp->height);
+ TAILQ_REMOVE(&res, rp, next);
+ free(rp);
+ }
+ printf("\n");
+ } else {
+ printf(": no EDID information\n");
+ }
+ }
+ if (!edid)
+ if (vbe_get_flatpanel(&width, &height))
+ printf(": Panel %dx%d\n", width, height);
+
+ memset(vbe, 0, sizeof (*vbe));
+ memcpy(vbe->VbeSignature, "VBE2", 4);
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ goto done;
+ if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
+ goto done;
+
+ vbe_print_vbe_info(vbe);
+ printf("Modes: ");
+
+ farptr = vbe->VideoModePtr;
+ if (farptr == 0)
+ goto done;
+
+ while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
+ safety++;
+ farptr += 2;
+ if (safety == VESA_MODE_COUNT) {
+ printf("[?] ");
+ break;
+ }
+ if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
+ continue;
+ /* we only care about linear modes here */
+ if (vbe_mode_is_supported(&mi) == 0)
+ continue;
+
+ /* we found some mode so reset safety counter */
+ safety = 0;
+
+ /* apply requested filter */
+ if (depth != -1 && mi.BitsPerPixel != depth)
+ continue;
+
+ if (nmodes % 4 == 0)
+ printf("\n");
+ else
+ printf(" ");
+
+ vbe_dump_mode(mode, &mi);
+ nmodes++;
+ }
+
+done:
+ if (nmodes == 0)
+ printf("none found");
+ printf("\n");
+}
+
+static void
+vbe_print_mode(bool verbose __unused)
+{
+ int nc, mode, i, rc;
+
+ nc = NCOLORS;
+
+ memset(vbe, 0, sizeof (*vbe));
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ return;
+
+ vbe_print_vbe_info(vbe);
+
+ if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
+ printf("Error getting current VBE mode\n");
+ return;
+ }
+
+ if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
+ vbe_mode_is_supported(vbe_mode) == 0) {
+ printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
+ return;
+ }
+
+ printf("\nCurrent VBE mode: ");
+ vbe_dump_mode(mode, vbe_mode);
+ printf("\n");
+
+ printf("%ux%ux%u, stride=%u\n",
+ gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height,
+ gfx_state.tg_fb.fb_bpp,
+ gfx_state.tg_fb.fb_stride *
+ (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY));
+ printf(" frame buffer: address=%jx, size=%jx\n",
+ (uintmax_t)gfx_state.tg_fb.fb_addr,
+ (uintmax_t)gfx_state.tg_fb.fb_size);
+
+ if (vbe_mode->MemoryModel == 0x6) {
+ printf(" color mask: R=%08x, G=%08x, B=%08x\n",
+ gfx_state.tg_fb.fb_mask_red,
+ gfx_state.tg_fb.fb_mask_green,
+ gfx_state.tg_fb.fb_mask_blue);
+ pager_open();
+ for (i = 0; i < nc; i++) {
+ printf("%d: R=%02x, G=%02x, B=%02x %08x", i,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_red) >>
+ ffs(gfx_state.tg_fb.fb_mask_red) - 1,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_green) >>
+ ffs(gfx_state.tg_fb.fb_mask_green) - 1,
+ (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >>
+ ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]);
+ if (pager_output("\n") != 0)
+ break;
+ }
+ pager_close();
+ return;
+ }
+
+ mode = 1; /* get DAC palette width */
+ rc = biosvbe_palette_format(&mode);
+ if (rc != VBE_SUCCESS)
+ return;
+
+ printf(" palette format: %x bits per primary\n", mode);
+ if (pe8 == NULL)
+ return;
+
+ pager_open();
+ for (i = 0; i < nc; i++) {
+ printf("%d: R=%02x, G=%02x, B=%02x", i,
+ pe8[i].Red, pe8[i].Green, pe8[i].Blue);
+ if (pager_output("\n") != 0)
+ break;
+ }
+ pager_close();
+}
+
+/*
+ * Try EDID preferred mode, if EDID or the suggested mode is not available,
+ * then try flat panel information.
+ * Fall back to VBE_DEFAULT_MODE.
+ */
+int
+vbe_default_mode(void)
+{
+ edid_res_list_t res;
+ struct resolution *rp;
+ int modenum;
+ uint32_t width, height;
+
+ modenum = 0;
+ vbe_get_max_resolution(&width, &height);
+ if (width != 0 && height != 0)
+ modenum = vbe_find_mode_xydm(width, height, -1, -1);
+
+ TAILQ_INIT(&res);
+ if (vbe_get_edid(&res)) {
+ while ((rp = TAILQ_FIRST(&res)) != NULL) {
+ if (modenum == 0) {
+ modenum = vbe_find_mode_xydm(
+ rp->width, rp->height, -1, -1);
+ }
+ TAILQ_REMOVE(&res, rp, next);
+ free(rp);
+ }
+ }
+
+ if (modenum == 0 &&
+ vbe_get_flatpanel(&width, &height)) {
+ modenum = vbe_find_mode_xydm(width, height, -1, -1);
+ }
+
+ /* Still no mode? Fall back to default. */
+ if (modenum == 0)
+ modenum = vbe_find_mode(VBE_DEFAULT_MODE);
+ return (modenum);
+}
+
+COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa);
+
+int
+command_vesa(int argc, char *argv[])
+{
+ char *arg, *cp;
+ int modenum = -1, n;
+
+ if (!vbe_check())
+ return (CMD_OK);
+
+ if (argc < 2)
+ goto usage;
+
+ if (strcmp(argv[1], "list") == 0) {
+ n = -1;
+ if (argc != 2 && argc != 3)
+ goto usage;
+
+ if (argc == 3) {
+ arg = argv[2];
+ errno = 0;
+ n = strtoul(arg, &cp, 0);
+ if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
+ snprintf(command_errbuf,
+ sizeof (command_errbuf),
+ "depth should be an integer");
+ return (CMD_ERROR);
+ }
+ }
+ vbe_modelist(n);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "get") == 0) {
+ bool verbose = false;
+
+ if (argc != 2) {
+ if (argc > 3 || strcmp(argv[2], "-v") != 0)
+ goto usage;
+ verbose = true;
+ }
+ vbe_print_mode(verbose);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "off") == 0) {
+ if (argc != 2)
+ goto usage;
+
+ if (gfx_state.tg_mode == VGA_TEXT_MODE)
+ return (CMD_OK);
+
+ reset_font_flags();
+ bios_text_font(true);
+ bios_set_text_mode(VGA_TEXT_MODE);
+ cons_update_mode(false);
+ return (CMD_OK);
+ }
+
+ if (strcmp(argv[1], "on") == 0) {
+ if (argc != 2)
+ goto usage;
+
+ modenum = vbe_default_mode();
+ if (modenum == 0) {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: no suitable VBE mode number found", argv[0]);
+ return (CMD_ERROR);
+ }
+ } else if (strcmp(argv[1], "set") == 0) {
+ if (argc != 3)
+ goto usage;
+
+ if (strncmp(argv[2], "0x", 2) == 0) {
+ arg = argv[2];
+ errno = 0;
+ n = strtoul(arg, &cp, 0);
+ if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
+ snprintf(command_errbuf,
+ sizeof (command_errbuf),
+ "mode should be an integer");
+ return (CMD_ERROR);
+ }
+ modenum = vbe_find_mode_xydm(0, 0, 0, n);
+ } else if (strchr(argv[2], 'x') != NULL) {
+ modenum = vbe_find_mode(argv[2]);
+ }
+ } else {
+ goto usage;
+ }
+
+ if (modenum == 0) {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: mode %s not supported by firmware\n",
+ argv[0], argv[2]);
+ return (CMD_ERROR);
+ }
+
+ if (modenum >= VESA_MODE_BASE) {
+ if (gfx_state.tg_mode != modenum) {
+ reset_font_flags();
+ bios_text_font(false);
+ vbe_set_mode(modenum);
+ cons_update_mode(true);
+ }
+ return (CMD_OK);
+ } else {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
+ return (CMD_ERROR);
+ }
+
+usage:
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "usage: %s on | off | get | list [depth] | "
+ "set <display or VBE mode number>", argv[0]);
+ return (CMD_ERROR);
+}
diff --git a/stand/i386/libi386/vbe.h b/stand/i386/libi386/vbe.h
new file mode 100644
index 000000000000..ff28b960df9c
--- /dev/null
+++ b/stand/i386/libi386/vbe.h
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * Default mode for VESA frame buffer.
+ * This mode is selected when there is no EDID inormation and
+ * mode is not provided by user.
+ * To provide consistent look with UEFI GOP, we use 800x600 here,
+ * and if this mode is not available, we fall back to text mode and
+ * VESA disabled.
+ */
+
+#define VBE_DEFAULT_MODE "800x600"
+
+struct vbeinfoblock {
+ char VbeSignature[4];
+ uint16_t VbeVersion;
+ uint32_t OemStringPtr;
+ uint32_t Capabilities;
+#define VBE_CAP_DAC8 (1 << 0) /* Can switch DAC */
+#define VBE_CAP_NONVGA (1 << 1) /* Controller is not VGA comp. */
+#define VBE_CAP_SNOW (1 << 2) /* Set data during Vertical Reterace */
+ uint32_t VideoModePtr;
+ uint16_t TotalMemory;
+ uint16_t OemSoftwareRev;
+ uint32_t OemVendorNamePtr, OemProductNamePtr, OemProductRevPtr;
+ /* data area, in total max 512 bytes for VBE 2.0 */
+ uint8_t Reserved[222];
+ uint8_t OemData[256];
+} __packed;
+
+struct modeinfoblock {
+ /* Mandatory information for all VBE revisions */
+ uint16_t ModeAttributes;
+ uint8_t WinAAttributes, WinBAttributes;
+ uint16_t WinGranularity, WinSize, WinASegment, WinBSegment;
+ uint32_t WinFuncPtr;
+ uint16_t BytesPerScanLine;
+ /* Mandatory information for VBE 1.2 and above */
+ uint16_t XResolution, YResolution;
+ uint8_t XCharSize, YCharSize, NumberOfPlanes, BitsPerPixel;
+ uint8_t NumberOfBanks, MemoryModel, BankSize, NumberOfImagePages;
+ uint8_t Reserved1;
+ /* Direct Color fields
+ (required for direct/6 and YUV/7 memory models) */
+ uint8_t RedMaskSize, RedFieldPosition;
+ uint8_t GreenMaskSize, GreenFieldPosition;
+ uint8_t BlueMaskSize, BlueFieldPosition;
+ uint8_t RsvdMaskSize, RsvdFieldPosition;
+ uint8_t DirectColorModeInfo;
+ /* Mandatory information for VBE 2.0 and above */
+ uint32_t PhysBasePtr;
+ uint32_t OffScreenMemOffset; /* reserved in VBE 3.0 and above */
+ uint16_t OffScreenMemSize; /* reserved in VBE 3.0 and above */
+
+ /* Mandatory information for VBE 3.0 and above */
+ uint16_t LinBytesPerScanLine;
+ uint8_t BnkNumberOfImagePages;
+ uint8_t LinNumberOfImagePages;
+ uint8_t LinRedMaskSize, LinRedFieldPosition;
+ uint8_t LinGreenMaskSize, LinGreenFieldPosition;
+ uint8_t LinBlueMaskSize, LinBlueFieldPosition;
+ uint8_t LinRsvdMaskSize, LinRsvdFieldPosition;
+ uint32_t MaxPixelClock;
+ /* + 1 will fix the size to 256 bytes */
+ uint8_t Reserved4[189 + 1];
+} __packed;
+
+struct crtciinfoblock {
+ uint16_t HorizontalTotal;
+ uint16_t HorizontalSyncStart;
+ uint16_t HorizontalSyncEnd;
+ uint16_t VerticalTotal;
+ uint16_t VerticalSyncStart;
+ uint16_t VerticalSyncEnd;
+ uint8_t Flags;
+ uint32_t PixelClock;
+ uint16_t RefreshRate;
+ uint8_t Reserved[40];
+} __packed;
+
+struct paletteentry {
+ uint8_t Blue;
+ uint8_t Green;
+ uint8_t Red;
+ uint8_t Reserved;
+} __packed;
+
+struct flatpanelinfo
+{
+ uint16_t HorizontalSize;
+ uint16_t VerticalSize;
+ uint16_t PanelType;
+ uint8_t RedBPP;
+ uint8_t GreenBPP;
+ uint8_t BlueBPP;
+ uint8_t ReservedBPP;
+ uint32_t ReservedOffScreenMemSize;
+ uint32_t ReservedOffScreenMemPtr;
+
+ uint8_t Reserved[14];
+} __packed;
+
+#define VBE_BASE_MODE (0x100) /* VBE 3.0 page 18 */
+#define VBE_VALID_MODE(a) ((a) >= VBE_BASE_MODE)
+#define VBE_ERROR(a) (((a) & 0xFF) != 0x4F || ((a) & 0xFF00) != 0)
+#define VBE_SUCCESS (0x004F)
+#define VBE_FAILED (0x014F)
+#define VBE_NOTSUP (0x024F)
+#define VBE_INVALID (0x034F)
+
+#define VGA_TEXT_MODE (3) /* 80x25 text mode */
+#define TEXT_ROWS (25) /* VGATEXT rows */
+#define TEXT_COLS (80) /* VGATEXT columns */
+
+extern struct paletteentry *pe8;
+extern int palette_format;
+
+int vga_get_reg(int, int);
+int vga_get_atr(int, int);
+void vga_set_atr(int, int, int);
+void vga_set_indexed(int, int, int, uint8_t, uint8_t);
+int vga_get_indexed(int, int, int, uint8_t);
+int vga_get_crtc(int, int);
+void vga_set_crtc(int, int, int);
+int vga_get_seq(int, int);
+void vga_set_seq(int, int, int);
+int vga_get_grc(int, int);
+void vga_set_grc(int, int, int);
+
+/* high-level VBE helpers, from vbe.c */
+void bios_set_text_mode(int);
+int biosvbe_palette_format(int *);
+void vbe_init(void);
+bool vbe_available(void);
+int vbe_default_mode(void);
+int vbe_set_mode(int);
+int vbe_get_mode(void);
+int vbe_set_palette(const struct paletteentry *, size_t);
+void vbe_modelist(int);
diff --git a/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c
index f965979d6565..b4829db1ea42 100644
--- a/stand/i386/libi386/vidconsole.c
+++ b/stand/i386/libi386/vidconsole.c
@@ -31,12 +31,13 @@
__FBSDID("$FreeBSD$");
#include <stand.h>
+#include <sys/param.h>
#include <bootstrap.h>
#include <btxv86.h>
-#include <machine/psl.h>
-#include <machine/cpufunc.h>
+#include <gfx_fb.h>
#include <teken.h>
#include <stdbool.h>
+#include "vbe.h"
#include <dev/vt/hw/vga/vt_vga_reg.h>
@@ -50,6 +51,7 @@ static int vidc_init(int arg);
static void vidc_putchar(int c);
static int vidc_getchar(void);
static int vidc_ischar(void);
+static void cons_draw_frame(teken_attr_t *);
static int vidc_started;
static uint16_t *vgatext;
@@ -72,30 +74,16 @@ static teken_funcs_t tf = {
.tf_respond = vidc_cons_respond,
};
-teken_t teken;
-teken_pos_t tp;
-
-struct text_pixel {
- teken_char_t c;
- teken_attr_t a;
-};
-
-static struct text_pixel *buffer;
-
-#define NCOLORS 16
-
-/*
- * Between console's palette and VGA's one:
- * - blue and red are swapped (1 <-> 4)
- * - yellow and cyan are swapped (3 <-> 6)
- */
-static const int cons_to_vga_colors[NCOLORS] = {
- 0, 4, 2, 6, 1, 5, 3, 7,
- 8, 12, 10, 14, 9, 13, 11, 15
+static teken_funcs_t tfx = {
+ .tf_bell = vidc_cons_bell,
+ .tf_cursor = gfx_fb_cursor,
+ .tf_putchar = gfx_fb_putchar,
+ .tf_fill = gfx_fb_fill,
+ .tf_copy = gfx_fb_copy,
+ .tf_param = gfx_fb_param,
+ .tf_respond = vidc_cons_respond,
};
-#define TEXT_COLS 80
-#define TEXT_ROWS 25
#define KEYBUFSZ 10
static uint8_t keybuf[KEYBUFSZ]; /* keybuf for extended codes */
@@ -111,60 +99,26 @@ struct console vidconsole = {
.c_ready = vidc_ischar
};
-static int
-vga_get_reg(int reg, int index)
-{
- return (inb(reg + index));
-}
-
-static int
-vga_get_atr(int reg, int i)
-{
- int ret;
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
- outb(reg + VGA_AC_WRITE, i);
- ret = inb(reg + VGA_AC_READ);
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
-
- return (ret);
-}
-
-static void
-vga_set_atr(int reg, int i, int v)
-{
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
- outb(reg + VGA_AC_WRITE, i);
- outb(reg + VGA_AC_WRITE, v);
-
- (void) inb(reg + VGA_GEN_INPUT_STAT_1);
-}
-
-static void
-vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
-{
- outb(reg + indexreg, index);
- outb(reg + datareg, val);
-}
-
-static int
-vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
-{
- outb(reg + indexreg, index);
- return (inb(reg + datareg));
-}
-
-static int
-vga_get_crtc(int reg, int i)
-{
- return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
-}
-
-static void
-vga_set_crtc(int reg, int i, int v)
+/*
+ * This function is used to mark a rectangular image area so the scrolling
+ * will know we need to copy the data from there.
+ */
+void
+term_image_display(teken_gfx_t *state, const teken_rect_t *r)
{
- vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
+ teken_pos_t p;
+ int idx;
+
+ for (p.tp_row = r->tr_begin.tp_row;
+ p.tp_row < r->tr_end.tp_row; p.tp_row++) {
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+ screen_buffer[idx].a.ta_format |= TF_IMAGE;
+ }
+ }
}
static void
@@ -353,9 +307,9 @@ vga_get_cp437(teken_char_t c)
}
static void
-vidc_text_printchar(const teken_pos_t *p)
+vidc_text_printchar(teken_gfx_t *state, const teken_pos_t *p)
{
- int i;
+ int idx;
uint8_t attr;
struct text_pixel *px;
teken_color_t fg, bg, tmp;
@@ -364,7 +318,8 @@ vidc_text_printchar(const teken_pos_t *p)
uint8_t attr;
} *addr;
- px = buffer + p->tp_col + p->tp_row * tp.tp_col;
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ px = &screen_buffer[idx];
fg = teken_256to16(px->a.ta_fgcolor);
bg = teken_256to16(px->a.ta_bgcolor);
if (px->a.ta_format & TF_BOLD)
@@ -378,29 +333,34 @@ vidc_text_printchar(const teken_pos_t *p)
bg = tmp;
}
- attr = (cons_to_vga_colors[bg & 0xf] << 4) |
- cons_to_vga_colors[fg & 0xf];
- addr = (struct cgatext *)vgatext + p->tp_col + p->tp_row * tp.tp_col;
- addr->ch = vga_get_cp437(px->c);
- addr->attr = attr;
+ attr = (cmap[bg & 0xf] << 4) | cmap[fg & 0xf];
+ addr = (struct cgatext *)vgatext;
+ addr[idx].ch = vga_get_cp437(px->c);
+ addr[idx].attr = attr;
}
static void
-vidc_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
+vidc_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
- int attr, idx;
+ teken_gfx_t *state = s;
+ int attr, idx;
- idx = p->tp_col + p->tp_row * tp.tp_col;
- buffer[idx].c = c;
- buffer[idx].a = *a;
- vidc_text_printchar(p);
+ idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
+ return;
+
+ screen_buffer[idx].c = c;
+ screen_buffer[idx].a = *a;
+
+ vidc_text_printchar(state, p);
}
static void
-vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
+vidc_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = arg;
teken_pos_t p;
teken_unit_t row, col;
@@ -410,29 +370,14 @@ vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
p.tp_row++)
for (p.tp_col = r->tr_begin.tp_col;
p.tp_col < r->tr_end.tp_col; p.tp_col++)
- vidc_text_putchar(s, &p, c, a);
+ vidc_text_putchar(state, &p, c, a);
vidc_text_set_cursor(row, col, true);
}
-static bool
-vidc_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
-{
- if (px1->c != px2->c)
- return (false);
-
- if (px1->a.ta_format != px2->a.ta_format)
- return (false);
- if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
- return (false);
- if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
- return (false);
-
- return (true);
-}
-
static void
-vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
+vidc_text_copy(void *ptr, const teken_rect_t *r, const teken_pos_t *p)
{
+ teken_gfx_t *state = ptr;
int srow, drow;
int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
teken_pos_t d, s;
@@ -453,18 +398,18 @@ vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
for (y = 0; y < nrow; y++) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = 0; x < ncol; x++) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -475,18 +420,18 @@ vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
for (y = nrow - 1; y >= 0; y--) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = 0; x < ncol; x++) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -495,18 +440,18 @@ vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
for (y = nrow - 1; y >= 0; y--) {
d.tp_row = p->tp_row + y;
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * tp.tp_col;
+ drow = d.tp_row * state->tg_tp.tp_col;
+ srow = s.tp_row * state->tg_tp.tp_col;
for (x = ncol - 1; x >= 0; x--) {
d.tp_col = p->tp_col + x;
s.tp_col = r->tr_begin.tp_col + x;
- if (!vidc_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ if (!is_same_pixel(
+ &screen_buffer[d.tp_col + drow],
+ &screen_buffer[s.tp_col + srow])) {
+ screen_buffer[d.tp_col + drow] =
+ screen_buffer[s.tp_col + srow];
+ vidc_text_printchar(state, &d);
}
}
}
@@ -516,8 +461,9 @@ vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
}
static void
-vidc_text_param(void *s __unused, int cmd, unsigned int value)
+vidc_text_param(void *arg, int cmd, unsigned int value)
{
+ teken_gfx_t *state = arg;
teken_unit_t row, col;
switch (cmd) {
@@ -532,10 +478,13 @@ vidc_text_param(void *s __unused, int cmd, unsigned int value)
/* FALLTHROUGH */
case TP_SHOWCURSOR:
vidc_text_get_cursor(&row, &col);
- if (value == 1)
+ if (value != 0) {
vidc_text_set_cursor(row, col, true);
- else
+ state->tg_cursor_visible = true;
+ } else {
vidc_text_set_cursor(row, col, false);
+ state->tg_cursor_visible = false;
+ }
break;
default:
/* Not yet implemented */
@@ -635,7 +584,7 @@ vidc_set_colors(struct env_var *ev, int flags, const void *value)
evalue = value;
}
- ap = teken_get_defattr(&teken);
+ ap = teken_get_defattr(&gfx_state.tg_teken);
a = *ap;
if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
/* is it already set? */
@@ -654,12 +603,382 @@ vidc_set_colors(struct env_var *ev, int flags, const void *value)
if (a.ta_bgcolor == TC_WHITE)
a.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &a);
+ cons_draw_frame(&a);
env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
- teken_set_defattr(&teken, &a);
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
+
return (CMD_OK);
}
static int
+env_screen_nounset(struct env_var *ev __unused)
+{
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return (0);
+ return (EPERM);
+}
+
+static int
+vidc_load_palette(uint32_t *cmap)
+{
+ int i, roff, goff, boff, rc;
+
+ if (pe8 == NULL)
+ pe8 = calloc(sizeof(*pe8), NCMAP);
+ if (pe8 == NULL)
+ return (ENOMEM);
+
+ /* Generate VGA colors */
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ rc = generate_cons_palette((uint32_t *)pe8, COLOR_FORMAT_RGB,
+ gfx_state.tg_fb.fb_mask_red >> roff, roff,
+ gfx_state.tg_fb.fb_mask_green >> goff, goff,
+ gfx_state.tg_fb.fb_mask_blue >> boff, boff);
+
+ if (rc == 0) {
+ for (i = 0; i < NCMAP; i++) {
+ int idx;
+
+ if (i < NCOLORS)
+ idx = cons_to_vga_colors[i];
+ else
+ idx = i;
+
+ rc = vbe_set_palette(&pe8[i], idx);
+ if (rc != 0)
+ break;
+ }
+ }
+ return (rc);
+}
+
+static void
+cons_draw_frame(teken_attr_t *a)
+{
+ teken_attr_t attr = *a;
+ teken_color_t fg = a->ta_fgcolor;
+
+ attr.ta_fgcolor = attr.ta_bgcolor;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_origin.tp_row, 1);
+ gfx_fb_drawrect(0,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
+ gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
+ gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
+ gfx_state.tg_origin.tp_col,
+ gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
+ gfx_fb_drawrect(
+ gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
+ gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height, 1);
+
+ attr.ta_fgcolor = fg;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+}
+
+/*
+ * Binary searchable table for CP437 to Unicode conversion.
+ */
+struct cp437uni {
+ uint8_t cp437_base;
+ uint16_t unicode_base;
+ uint8_t length;
+};
+
+static const struct cp437uni cp437unitable[] = {
+ { 0, 0x0000, 0 }, { 1, 0x263A, 1 }, { 3, 0x2665, 1 },
+ { 5, 0x2663, 0 }, { 6, 0x2660, 0 }, { 7, 0x2022, 0 },
+ { 8, 0x25D8, 0 }, { 9, 0x25CB, 0 }, { 10, 0x25D9, 0 },
+ { 11, 0x2642, 0 }, { 12, 0x2640, 0 }, { 13, 0x266A, 1 },
+ { 15, 0x263C, 0 }, { 16, 0x25BA, 0 }, { 17, 0x25C4, 0 },
+ { 18, 0x2195, 0 }, { 19, 0x203C, 0 }, { 20, 0x00B6, 0 },
+ { 21, 0x00A7, 0 }, { 22, 0x25AC, 0 }, { 23, 0x21A8, 0 },
+ { 24, 0x2191, 0 }, { 25, 0x2193, 0 }, { 26, 0x2192, 0 },
+ { 27, 0x2190, 0 }, { 28, 0x221F, 0 }, { 29, 0x2194, 0 },
+ { 30, 0x25B2, 0 }, { 31, 0x25BC, 0 }, { 32, 0x0020, 0x5e },
+ { 127, 0x2302, 0 }, { 128, 0x00C7, 0 }, { 129, 0x00FC, 0 },
+ { 130, 0x00E9, 0 }, { 131, 0x00E2, 0 }, { 132, 0x00E4, 0 },
+ { 133, 0x00E0, 0 }, { 134, 0x00E5, 0 }, { 135, 0x00E7, 0 },
+ { 136, 0x00EA, 1 }, { 138, 0x00E8, 0 }, { 139, 0x00EF, 0 },
+ { 140, 0x00EE, 0 }, { 141, 0x00EC, 0 }, { 142, 0x00C4, 1 },
+ { 144, 0x00C9, 0 }, { 145, 0x00E6, 0 }, { 146, 0x00C6, 0 },
+ { 147, 0x00F4, 0 }, { 148, 0x00F6, 0 }, { 149, 0x00F2, 0 },
+ { 150, 0x00FB, 0 }, { 151, 0x00F9, 0 }, { 152, 0x00FF, 0 },
+ { 153, 0x00D6, 0 }, { 154, 0x00DC, 0 }, { 155, 0x00A2, 1 },
+ { 157, 0x00A5, 0 }, { 158, 0x20A7, 0 }, { 159, 0x0192, 0 },
+ { 160, 0x00E1, 0 }, { 161, 0x00ED, 0 }, { 162, 0x00F3, 0 },
+ { 163, 0x00FA, 0 }, { 164, 0x00F1, 0 }, { 165, 0x00D1, 0 },
+ { 166, 0x00AA, 0 }, { 167, 0x00BA, 0 }, { 168, 0x00BF, 0 },
+ { 169, 0x2310, 0 }, { 170, 0x00AC, 0 }, { 171, 0x00BD, 0 },
+ { 172, 0x00BC, 0 }, { 173, 0x00A1, 0 }, { 174, 0x00AB, 0 },
+ { 175, 0x00BB, 0 }, { 176, 0x2591, 2 }, { 179, 0x2502, 0 },
+ { 180, 0x2524, 0 }, { 181, 0x2561, 1 }, { 183, 0x2556, 0 },
+ { 184, 0x2555, 0 }, { 185, 0x2563, 0 }, { 186, 0x2551, 0 },
+ { 187, 0x2557, 0 }, { 188, 0x255D, 0 }, { 189, 0x255C, 0 },
+ { 190, 0x255B, 0 }, { 191, 0x2510, 0 }, { 192, 0x2514, 0 },
+ { 193, 0x2534, 0 }, { 194, 0x252C, 0 }, { 195, 0x251C, 0 },
+ { 196, 0x2500, 0 }, { 197, 0x253C, 0 }, { 198, 0x255E, 1 },
+ { 200, 0x255A, 0 }, { 201, 0x2554, 0 }, { 202, 0x2569, 0 },
+ { 203, 0x2566, 0 }, { 204, 0x2560, 0 }, { 205, 0x2550, 0 },
+ { 206, 0x256C, 0 }, { 207, 0x2567, 1 }, { 209, 0x2564, 1 },
+ { 211, 0x2559, 0 }, { 212, 0x2558, 0 }, { 213, 0x2552, 1 },
+ { 215, 0x256B, 0 }, { 216, 0x256A, 0 }, { 217, 0x2518, 0 },
+ { 218, 0x250C, 0 }, { 219, 0x2588, 0 }, { 220, 0x2584, 0 },
+ { 221, 0x258C, 0 }, { 222, 0x2590, 0 }, { 223, 0x2580, 0 },
+ { 224, 0x03B1, 0 }, { 225, 0x00DF, 0 }, { 226, 0x0393, 0 },
+ { 227, 0x03C0, 0 }, { 228, 0x03A3, 0 }, { 229, 0x03C3, 0 },
+ { 230, 0x00B5, 0 }, { 231, 0x03C4, 0 }, { 232, 0x03A6, 0 },
+ { 233, 0x0398, 0 }, { 234, 0x03A9, 0 }, { 235, 0x03B4, 0 },
+ { 236, 0x221E, 0 }, { 237, 0x03C6, 0 }, { 238, 0x03B5, 0 },
+ { 239, 0x2229, 0 }, { 240, 0x2261, 0 }, { 241, 0x00B1, 0 },
+ { 242, 0x2265, 0 }, { 243, 0x2264, 0 }, { 244, 0x2320, 1 },
+ { 246, 0x00F7, 0 }, { 247, 0x2248, 0 }, { 248, 0x00B0, 0 },
+ { 249, 0x2219, 0 }, { 250, 0x00B7, 0 }, { 251, 0x221A, 0 },
+ { 252, 0x207F, 0 }, { 253, 0x00B2, 0 }, { 254, 0x25A0, 0 },
+ { 255, 0x00A0, 0 }
+};
+
+static uint16_t
+vga_cp437_to_uni(uint8_t c)
+{
+ int min, mid, max;
+
+ min = 0;
+ max = (sizeof(cp437unitable) / sizeof(struct cp437uni)) - 1;
+
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (c < cp437unitable[mid].cp437_base)
+ max = mid - 1;
+ else if (c > cp437unitable[mid].cp437_base +
+ cp437unitable[mid].length)
+ min = mid + 1;
+ else
+ return (c - cp437unitable[mid].cp437_base +
+ cp437unitable[mid].unicode_base);
+ }
+
+ return ('?');
+}
+
+/*
+ * install font for text mode
+ */
+static void
+vidc_install_font(void)
+{
+ static uint8_t fsreg[8] = {0x0, 0x30, 0x5, 0x35, 0xa, 0x3a, 0xf, 0x3f};
+ const uint8_t *from;
+ uint8_t volatile *to;
+ uint16_t c;
+ int i, j, s;
+ int bpc, f_offset;
+ teken_attr_t a = { 0 };
+
+ if (gfx_state.tg_fb_type != FB_TEXT)
+ return;
+
+ /* Sync-reset the sequencer registers */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x01);
+ /*
+ * enable write to plane2, since fonts
+ * could only be loaded into plane2
+ */
+ vga_set_seq(VGA_REG_BASE, 0x02, 0x04);
+ /*
+ * sequentially access data in the bit map being
+ * selected by MapMask register (index 0x02)
+ */
+ vga_set_seq(VGA_REG_BASE, 0x04, 0x07);
+ /* Sync-reset ended, and allow the sequencer to operate */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x03);
+
+ /*
+ * select plane 2 on Read Mode 0
+ */
+ vga_set_grc(VGA_REG_BASE, 0x04, 0x02);
+ /*
+ * system addresses sequentially access data, follow
+ * Memory Mode register bit 2 in the sequencer
+ */
+ vga_set_grc(VGA_REG_BASE, 0x05, 0x00);
+ /*
+ * set range of host memory addresses decoded by VGA
+ * hardware -- A0000h-BFFFFh (128K region)
+ */
+ vga_set_grc(VGA_REG_BASE, 0x06, 0x00);
+
+ /*
+ * This assumes 8x16 characters, which yield the traditional 80x25
+ * screen.
+ */
+ bpc = 16;
+ s = 0; /* font slot, maybe should use tunable there. */
+ f_offset = s * 8 * 1024;
+ for (i = 0; i < 256; i++) {
+ c = vga_cp437_to_uni(i);
+ from = font_lookup(&gfx_state.tg_font, c, &a);
+ to = (unsigned char *)ptov(VGA_MEM_BASE) + f_offset +
+ i * 0x20;
+ for (j = 0; j < bpc; j++)
+ *to++ = *from++;
+ }
+
+ /* Sync-reset the sequencer registers */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x01);
+ /* enable write to plane 0 and 1 */
+ vga_set_seq(VGA_REG_BASE, 0x02, 0x03);
+ /*
+ * enable character map selection
+ * and odd/even addressing
+ */
+ vga_set_seq(VGA_REG_BASE, 0x04, 0x03);
+ /*
+ * select font map
+ */
+ vga_set_seq(VGA_REG_BASE, 0x03, fsreg[s]);
+ /* Sync-reset ended, and allow the sequencer to operate */
+ vga_set_seq(VGA_REG_BASE, 0x00, 0x03);
+
+ /* restore graphic registers */
+
+ /* select plane 0 */
+ vga_set_grc(VGA_REG_BASE, 0x04, 0x00);
+ /* enable odd/even addressing mode */
+ vga_set_grc(VGA_REG_BASE, 0x05, 0x10);
+ /*
+ * range of host memory addresses decoded by VGA
+ * hardware -- B8000h-BFFFFh (32K region)
+ */
+ vga_set_grc(VGA_REG_BASE, 0x06, 0x0e);
+ /* enable all color plane */
+ vga_set_atr(VGA_REG_BASE, 0x12, 0x0f);
+}
+
+bool
+cons_update_mode(bool use_gfx_mode)
+{
+ const teken_attr_t *a;
+ teken_attr_t attr;
+ char env[10], *ptr;
+ int format, roff, goff, boff;
+
+ gfx_state.tg_tp.tp_row = TEXT_ROWS;
+ gfx_state.tg_tp.tp_col = TEXT_COLS;
+
+ if (use_gfx_mode) {
+ setup_font(&gfx_state, gfx_state.tg_fb.fb_height,
+ gfx_state.tg_fb.fb_width);
+ /* Point of origin in pixels. */
+ gfx_state.tg_origin.tp_row = (gfx_state.tg_fb.fb_height -
+ (gfx_state.tg_tp.tp_row * gfx_state.tg_font.vf_height)) / 2;
+ gfx_state.tg_origin.tp_col = (gfx_state.tg_fb.fb_width -
+ (gfx_state.tg_tp.tp_col * gfx_state.tg_font.vf_width)) / 2;
+
+ gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
+ gfx_state.tg_font.vf_width * 4;
+ free(gfx_state.tg_glyph);
+ gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
+ if (gfx_state.tg_glyph == NULL)
+ return (false);
+ gfx_state.tg_functions = &tfx;
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_height);
+ env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_width);
+ env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_bpp);
+ env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ } else {
+ /* Trigger loading of 8x16 font. */
+ setup_font(&gfx_state,
+ 16 * gfx_state.tg_fb.fb_height + BORDER_PIXELS,
+ 8 * gfx_state.tg_fb.fb_width + BORDER_PIXELS);
+ gfx_state.tg_functions = &tf;
+ /* ensure the following are not set for text mode */
+ unsetenv("screen.height");
+ unsetenv("screen.width");
+ unsetenv("screen.depth");
+ unsetenv("kern.vt.fb.default_mode");
+ vidc_install_font();
+ }
+
+ free(screen_buffer);
+ screen_buffer = malloc(gfx_state.tg_tp.tp_row * gfx_state.tg_tp.tp_col *
+ sizeof(*screen_buffer));
+ if (screen_buffer == NULL)
+ return (false);
+
+ teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, &gfx_state);
+
+ if (gfx_state.tg_ctype == CT_INDEXED)
+ format = COLOR_FORMAT_VGA;
+ else
+ format = COLOR_FORMAT_RGB;
+
+ roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
+ goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
+ boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
+ (void) generate_cons_palette(cmap, format,
+ gfx_state.tg_fb.fb_mask_red >> roff, roff,
+ gfx_state.tg_fb.fb_mask_green >> goff, goff,
+ gfx_state.tg_fb.fb_mask_blue >> boff, boff);
+
+ if (gfx_state.tg_ctype == CT_INDEXED)
+ vidc_load_palette(cmap);
+
+ teken_set_winsize(&gfx_state.tg_teken, &gfx_state.tg_tp);
+ a = teken_get_defattr(&gfx_state.tg_teken);
+ attr = *a;
+
+ /*
+ * On first run, we set up the vidc_set_colors()
+ * callback. If the env is already set, we
+ * pick up fg and bg color values from the environment.
+ */
+ ptr = getenv("teken.fg_color");
+ if (ptr != NULL) {
+ attr.ta_fgcolor = strtol(ptr, NULL, 10);
+ ptr = getenv("teken.bg_color");
+ attr.ta_bgcolor = strtol(ptr, NULL, 10);
+
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+ } else {
+ snprintf(env, sizeof(env), "%d", attr.ta_fgcolor);
+ env_setenv("teken.fg_color", EV_VOLATILE, env,
+ vidc_set_colors, env_nounset);
+ snprintf(env, sizeof(env), "%d", attr.ta_bgcolor);
+ env_setenv("teken.bg_color", EV_VOLATILE, env,
+ vidc_set_colors, env_nounset);
+ }
+
+ /* Improve visibility */
+ if (attr.ta_bgcolor == TC_WHITE)
+ attr.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+
+ snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_row);
+ setenv("LINES", env, 1);
+ snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_col);
+ setenv("COLUMNS", env, 1);
+
+ /* Draw frame around terminal area. */
+ cons_draw_frame(&attr);
+ /* Erase display, this will also fill our screen buffer. */
+ teken_input(&gfx_state.tg_teken, "\e[2J", 4);
+ gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 1);
+
+ return (true);
+}
+
+static int
vidc_init(int arg)
{
const teken_attr_t *a;
@@ -670,6 +989,7 @@ vidc_init(int arg)
return (0);
vidc_started = 1;
+ vbe_init();
/*
* Check Miscellaneous Output Register (Read at 3CCh, Write at 3C2h)
@@ -687,30 +1007,19 @@ vidc_init(int arg)
val &= ~VGA_AC_MC_ELG;
vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, val);
- tp.tp_row = TEXT_ROWS;
- tp.tp_col = TEXT_COLS;
- buffer = malloc(tp.tp_row * tp.tp_col * sizeof(*buffer));
- if (buffer == NULL)
- return (1);
-
- snprintf(env, sizeof (env), "%u", tp.tp_row);
- setenv("LINES", env, 1);
- snprintf(env, sizeof (env), "%u", tp.tp_col);
- setenv("COLUMNS", env, 1);
-
- teken_init(&teken, &tf, NULL);
- teken_set_winsize(&teken, &tp);
- a = teken_get_defattr(&teken);
+#if defined(FRAMEBUFFER_MODE)
+ val = vbe_default_mode();
+ /* if val is not legal VBE mode, use text mode */
+ if (VBE_VALID_MODE(val)) {
+ if (vbe_set_mode(val) != 0)
+ bios_set_text_mode(VGA_TEXT_MODE);
+ }
+#endif
- snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
- env_setenv("teken.fg_color", EV_VOLATILE, env, vidc_set_colors,
- env_nounset);
- snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
- env_setenv("teken.bg_color", EV_VOLATILE, env, vidc_set_colors,
- env_nounset);
+ gfx_framework_init();
- /* Erase display, this will also fill our screen buffer. */
- teken_input(&teken, "\e[J", 3);
+ if (!cons_update_mode(VBE_VALID_MODE(vbe_get_mode())))
+ return (1);
for (int i = 0; i < 10 && vidc_ischar(); i++)
(void) vidc_getchar();
@@ -734,8 +1043,8 @@ vidc_putchar(int c)
{
unsigned char ch = c;
- if (buffer != NULL)
- teken_input(&teken, &ch, sizeof (ch));
+ if (screen_buffer != NULL)
+ teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
else
vidc_biosputchar(c);
}
@@ -808,27 +1117,27 @@ vidc_ischar(void)
#if KEYBOARD_PROBE
-#define PROBE_MAXRETRY 5
-#define PROBE_MAXWAIT 400
-#define IO_DUMMY 0x84
-#define IO_KBD 0x060 /* 8042 Keyboard */
+#define PROBE_MAXRETRY 5
+#define PROBE_MAXWAIT 400
+#define IO_DUMMY 0x84
+#define IO_KBD 0x060 /* 8042 Keyboard */
/* selected defines from kbdio.h */
-#define KBD_STATUS_PORT 4 /* status port, read */
-#define KBD_DATA_PORT 0 /* data port, read/write
+#define KBD_STATUS_PORT 4 /* status port, read */
+#define KBD_DATA_PORT 0 /* data port, read/write
* also used as keyboard command
* and mouse command port
*/
-#define KBDC_ECHO 0x00ee
-#define KBDS_ANY_BUFFER_FULL 0x0001
-#define KBDS_INPUT_BUFFER_FULL 0x0002
-#define KBD_ECHO 0x00ee
+#define KBDC_ECHO 0x00ee
+#define KBDS_ANY_BUFFER_FULL 0x0001
+#define KBDS_INPUT_BUFFER_FULL 0x0002
+#define KBD_ECHO 0x00ee
/* 7 microsec delay necessary for some keyboard controllers */
static void
delay7(void)
{
- /*
+ /*
* I know this is broken, but no timer is available yet at this stage...
* See also comments in `delay1ms()'.
*/
@@ -854,7 +1163,7 @@ delay1ms(void)
(void) inb(0x84);
}
-/*
+/*
* We use the presence/absence of a keyboard to determine whether the internal
* console can be used for input.
*
diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile
index 74f39c078645..e80e19dfb9e6 100644
--- a/stand/i386/loader/Makefile
+++ b/stand/i386/loader/Makefile
@@ -23,7 +23,13 @@ VERSION_FILE= ${.CURDIR}/../loader/version
.PATH: ${BOOTSRC}/i386/loader
# architecture-specific loader code
-SRCS= main.c conf.c vers.c chain.c
+SRCS= main.c conf.c vers.c chain.c gfx_fb.c 8x16.c
+
+CFLAGS.gfx_fb.c += -I${.CURDIR}/../libi386
+CFLAGS.gfx_fb.c += -I$(SRCTOP)/sys/teken
+CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
+CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite
+CFLAGS.gfx_fb.c += -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
# Include bcache code.
HAVE_BCACHE= yes
@@ -49,7 +55,7 @@ HELP_FILES= ${.CURDIR}/help.i386
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
-CLEANFILES+= ${LOADER} ${LOADER}.bin
+CLEANFILES+= ${LOADER} ${LOADER}.bin 8x16.c
ORG= 0x0
@@ -64,6 +70,9 @@ CFLAGS+= -I${BOOTSRC}/i386
#CFLAGS+= -g
#LDFLAGS+= -g
+8x16.c: ${SRCTOP}/contrib/terminus/ter-u16v.bdf
+ vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC}
+
${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
-b ${BTXKERN} ${LOADER}.bin
diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c
index ceeaf7260acc..a595340d3a13 100644
--- a/stand/i386/loader/main.c
+++ b/stand/i386/loader/main.c
@@ -131,6 +131,12 @@ main(void)
setheap(heap_bottom, heap_top);
/*
+ * detect ACPI for future reference. This may set console to comconsole
+ * if we do have ACPI SPCR table.
+ */
+ biosacpi_detect();
+
+ /*
* XXX Chicken-and-egg problem; we want to have console output early,
* but some console attributes may depend on reading from eg. the boot
* device, which we can't do yet.
@@ -242,9 +248,6 @@ main(void)
initial_bootinfo->bi_extmem = bios_extmem / 1024;
}
- /* detect ACPI for future reference */
- biosacpi_detect();
-
/* detect SMBIOS for future reference */
smbios_detect(NULL);
@@ -254,6 +257,7 @@ main(void)
printf("\n%s", bootprog_info);
extract_currdev(); /* set $currdev and $loaddev */
+ autoload_font(true);
bios_getsmap();