Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F133106369
D27420.id80147.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
198 KB
Referenced Files
None
Subscribers
None
D27420.id80147.diff
View Options
Index: contrib/pnglite/LICENSE
===================================================================
--- /dev/null
+++ contrib/pnglite/LICENSE
@@ -0,0 +1,25 @@
+pnglite.h - Interface for pnglite library
+
+Copyright (c) 2007 Daniel Karling
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+ distribution.
+
+Daniel Karling
+daniel.karling@gmail.com
Index: contrib/pnglite/README.md
===================================================================
--- /dev/null
+++ contrib/pnglite/README.md
@@ -0,0 +1,5 @@
+pnglite
+====
+#### A pretty small png library
+
+Currently all documentation resides in pnglite.h.
Index: contrib/pnglite/pnglite.h
===================================================================
--- /dev/null
+++ contrib/pnglite/pnglite.h
@@ -0,0 +1,169 @@
+/*
+ * pnglite.h - Interface for pnglite library
+ * Copyright (c) 2007 Daniel Karling
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * Daniel Karling
+ * daniel.karling@gmail.com
+ */
+
+
+#ifndef _PNGLITE_H_
+#define _PNGLITE_H_
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Enumerations for pnglite.
+ * Negative numbers are error codes and 0 and up are okay responses.
+ */
+
+enum {
+ PNG_DONE = 1,
+ PNG_NO_ERROR = 0,
+ PNG_FILE_ERROR = -1,
+ PNG_HEADER_ERROR = -2,
+ PNG_IO_ERROR = -3,
+ PNG_EOF_ERROR = -4,
+ PNG_CRC_ERROR = -5,
+ PNG_MEMORY_ERROR = -6,
+ PNG_ZLIB_ERROR = -7,
+ PNG_UNKNOWN_FILTER = -8,
+ PNG_NOT_SUPPORTED = -9,
+ PNG_WRONG_ARGUMENTS = -10
+};
+
+/*
+ * The five different kinds of color storage in PNG files.
+ */
+
+enum {
+ PNG_GREYSCALE = 0,
+ PNG_TRUECOLOR = 2,
+ PNG_INDEXED = 3,
+ PNG_GREYSCALE_ALPHA = 4,
+ PNG_TRUECOLOR_ALPHA = 6
+};
+
+typedef struct {
+ void *zs; /* pointer to z_stream */
+ int fd;
+ unsigned char *image;
+
+ unsigned char *png_data;
+ unsigned png_datalen;
+
+ unsigned width;
+ unsigned height;
+ unsigned char depth;
+ unsigned char color_type;
+ unsigned char compression_method;
+ unsigned char filter_method;
+ unsigned char interlace_method;
+ unsigned char bpp;
+
+ unsigned char *readbuf;
+ unsigned readbuflen;
+} png_t;
+
+
+/*
+ * Function: png_open
+ *
+ * This function is used to open a png file with the internal file
+ * IO system.
+ *
+ * Parameters:
+ * png - Empty png_t struct.
+ * filename - Filename of the file to be opened.
+ *
+ * Returns:
+ * PNG_NO_ERROR on success, otherwise an error code.
+ */
+
+int png_open(png_t *png, const char *filename);
+
+/*
+ * Function: png_print_info
+ *
+ * This function prints some info about the opened png file to stdout.
+ *
+ * Parameters:
+ * png - png struct to get info from.
+ */
+
+void png_print_info(png_t *png);
+
+/*
+ * Function: png_error_string
+ *
+ * This function translates an error code to a human readable string.
+ *
+ * Parameters:
+ * error - Error code.
+ *
+ * Returns:
+ * Pointer to string.
+ */
+
+char *png_error_string(int error);
+
+/*
+ * Function: png_get_data
+ *
+ * This function decodes the opened png file and stores the result in data.
+ * data should be big enough to hold the decoded png.
+ * Required size will be:
+ *
+ * > width*height*(bytes per pixel)
+ *
+ * Parameters:
+ * data - Where to store result.
+ *
+ * Returns:
+ * PNG_NO_ERROR on success, otherwise an error code.
+ */
+
+int png_get_data(png_t *png, uint8_t *data);
+
+/*
+ * Function: png_close
+ *
+ * Closes an open png file pointer.
+ *
+ * Parameters:
+ * png - png to close.
+ *
+ * Returns:
+ * PNG_NO_ERROR
+ */
+
+int png_close(png_t *png);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PNGLITE_H_ */
Index: contrib/pnglite/pnglite.c
===================================================================
--- /dev/null
+++ contrib/pnglite/pnglite.c
@@ -0,0 +1,628 @@
+/*
+ * pnglite.c - pnglite library
+ * For conditions of distribution and use, see copyright notice in pnglite.h
+ */
+
+/*
+ * Note: this source is updated to enable build for FreeBSD boot loader.
+ */
+
+#ifdef _STANDALONE
+#include <sys/cdefs.h>
+#include <stand.h>
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+#include <zlib.h>
+#include "pnglite.h"
+
+#ifndef abs
+#define abs(x) ((x) < 0? -(x):(x))
+#endif
+
+#define PNG_32b(b, s) ((uint32_t)(b) << (s))
+#define PNG_U32(b1, b2, b3, b4) \
+ (PNG_32b(b1, 24) | PNG_32b(b2, 16) | PNG_32b(b3, 8) | PNG_32b(b4, 0))
+
+#define png_IDAT PNG_U32(73, 68, 65, 84)
+#define png_IEND PNG_U32(73, 69, 78, 68)
+
+static ssize_t
+file_read(png_t *png, void *out, size_t size, size_t numel)
+{
+ ssize_t result;
+ off_t offset = (off_t)(size * numel);
+
+ if (offset < 0)
+ return (PNG_FILE_ERROR);
+
+ if (!out) {
+ result = lseek(png->fd, offset, SEEK_CUR);
+ } else {
+ result = read(png->fd, out, size * numel);
+ }
+
+ return (result);
+}
+
+static int
+file_read_ul(png_t *png, unsigned *out)
+{
+ uint8_t buf[4];
+
+ if (file_read(png, buf, 1, 4) != 4)
+ return (PNG_FILE_ERROR);
+
+ *out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
+
+ return (PNG_NO_ERROR);
+}
+
+static unsigned
+get_ul(uint8_t *buf)
+{
+ unsigned result;
+ uint8_t foo[4];
+
+ memcpy(foo, buf, 4);
+
+ result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
+
+ return (result);
+}
+
+static int
+png_get_bpp(png_t *png)
+{
+ int bpp;
+
+ switch (png->color_type) {
+ case PNG_GREYSCALE:
+ bpp = 1; break;
+ case PNG_TRUECOLOR:
+ bpp = 3; break;
+ case PNG_INDEXED:
+ bpp = 1; break;
+ case PNG_GREYSCALE_ALPHA:
+ bpp = 2; break;
+ case PNG_TRUECOLOR_ALPHA:
+ bpp = 4; break;
+ default:
+ return (PNG_FILE_ERROR);
+ }
+
+ bpp *= png->depth / 8;
+
+ return (bpp);
+}
+
+static int
+png_read_ihdr(png_t *png)
+{
+ unsigned length = 0;
+ unsigned orig_crc;
+ unsigned calc_crc;
+ uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
+
+ if (file_read_ul(png, &length) != PNG_NO_ERROR)
+ return (PNG_FILE_ERROR);
+
+ if (length != 13)
+ return (PNG_CRC_ERROR);
+
+ if (file_read(png, ihdr, 1, 13+4) != 13+4)
+ return (PNG_EOF_ERROR);
+
+ if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
+ return (PNG_FILE_ERROR);
+
+ calc_crc = crc32(0L, Z_NULL, 0);
+ calc_crc = crc32(calc_crc, ihdr, 13+4);
+
+ if (orig_crc != calc_crc) {
+ return (PNG_CRC_ERROR);
+ }
+
+ png->width = get_ul(ihdr+4);
+ png->height = get_ul(ihdr+8);
+ png->depth = ihdr[12];
+ png->color_type = ihdr[13];
+ png->compression_method = ihdr[14];
+ png->filter_method = ihdr[15];
+ png->interlace_method = ihdr[16];
+
+ if (png->color_type == PNG_INDEXED)
+ return (PNG_NOT_SUPPORTED);
+
+ if (png->depth != 8 && png->depth != 16)
+ return (PNG_NOT_SUPPORTED);
+
+ if (png->interlace_method)
+ return (PNG_NOT_SUPPORTED);
+
+ return (PNG_NO_ERROR);
+}
+
+void
+png_print_info(png_t *png)
+{
+ printf("PNG INFO:\n");
+ printf("\twidth:\t\t%d\n", png->width);
+ printf("\theight:\t\t%d\n", png->height);
+ printf("\tdepth:\t\t%d\n", png->depth);
+ printf("\tcolor:\t\t");
+
+ switch (png->color_type) {
+ case PNG_GREYSCALE:
+ printf("greyscale\n"); break;
+ case PNG_TRUECOLOR:
+ printf("truecolor\n"); break;
+ case PNG_INDEXED:
+ printf("palette\n"); break;
+ case PNG_GREYSCALE_ALPHA:
+ printf("greyscale with alpha\n"); break;
+ case PNG_TRUECOLOR_ALPHA:
+ printf("truecolor with alpha\n"); break;
+ default:
+ printf("unknown, this is not good\n"); break;
+ }
+
+ printf("\tcompression:\t%s\n",
+ png->compression_method?
+ "unknown, this is not good":"inflate/deflate");
+ printf("\tfilter:\t\t%s\n",
+ png->filter_method? "unknown, this is not good":"adaptive");
+ printf("\tinterlace:\t%s\n",
+ png->interlace_method? "interlace":"no interlace");
+}
+
+int
+png_open(png_t *png, const char *filename)
+{
+ char header[8];
+ int result;
+
+ png->image = NULL;
+ png->fd = open(filename, O_RDONLY);
+ if (png->fd == -1)
+ return (PNG_FILE_ERROR);
+
+ if (file_read(png, header, 1, 8) != 8) {
+ result = PNG_EOF_ERROR;
+ goto done;
+ }
+
+ if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
+ result = PNG_HEADER_ERROR;
+ goto done;
+ }
+
+ result = png_read_ihdr(png);
+ if (result == PNG_NO_ERROR) {
+ result = png_get_bpp(png);
+ if (result > 0) {
+ png->bpp = (uint8_t)result;
+ result = PNG_NO_ERROR;
+ }
+ }
+
+done:
+ if (result == PNG_NO_ERROR) {
+ uint64_t size = png->width * png->height * png->bpp;
+
+ if (size < UINT_MAX)
+ png->image = malloc(size);
+ if (png->image == NULL)
+ result = PNG_MEMORY_ERROR;
+ }
+
+ if (result == PNG_NO_ERROR)
+ result = png_get_data(png, png->image);
+
+ if (result != PNG_NO_ERROR) {
+ free(png->image);
+ (void) close(png->fd);
+ png->fd = -1;
+ return (result);
+ }
+
+ return (result);
+}
+
+int
+png_close(png_t *png)
+{
+ (void) close(png->fd);
+ png->fd = -1;
+ free(png->image);
+ png->image = NULL;
+
+ return (PNG_NO_ERROR);
+}
+
+static int
+png_init_inflate(png_t *png)
+{
+ z_stream *stream;
+ png->zs = calloc(1, sizeof (z_stream));
+
+ stream = png->zs;
+
+ if (!stream)
+ return (PNG_MEMORY_ERROR);
+
+ if (inflateInit(stream) != Z_OK) {
+ free(png->zs);
+ png->zs = NULL;
+ return (PNG_ZLIB_ERROR);
+ }
+
+ stream->next_out = png->png_data;
+ stream->avail_out = png->png_datalen;
+
+ return (PNG_NO_ERROR);
+}
+
+static int
+png_end_inflate(png_t *png)
+{
+ z_stream *stream = png->zs;
+ int rc = PNG_NO_ERROR;
+
+ if (!stream)
+ return (PNG_MEMORY_ERROR);
+
+ if (inflateEnd(stream) != Z_OK) {
+ printf("ZLIB says: %s\n", stream->msg);
+ rc = PNG_ZLIB_ERROR;
+ }
+
+ free(png->zs);
+ png->zs = NULL;
+
+ return (rc);
+}
+
+static int
+png_inflate(png_t *png, uint8_t *data, int len)
+{
+ int result;
+ z_stream *stream = png->zs;
+
+ if (!stream)
+ return (PNG_MEMORY_ERROR);
+
+ stream->next_in = data;
+ stream->avail_in = len;
+
+ result = inflate(stream, Z_SYNC_FLUSH);
+
+ if (result != Z_STREAM_END && result != Z_OK) {
+ printf("%s\n", stream->msg);
+ return (PNG_ZLIB_ERROR);
+ }
+
+ if (stream->avail_in != 0)
+ return (PNG_ZLIB_ERROR);
+
+ return (PNG_NO_ERROR);
+}
+
+static int
+png_read_idat(png_t *png, unsigned length)
+{
+ unsigned orig_crc;
+ unsigned calc_crc;
+ ssize_t len = length;
+
+ if (!png->readbuf || png->readbuflen < length) {
+ png->readbuf = realloc(png->readbuf, length);
+ png->readbuflen = length;
+ }
+
+ if (!png->readbuf)
+ return (PNG_MEMORY_ERROR);
+
+ if (file_read(png, png->readbuf, 1, length) != len)
+ return (PNG_FILE_ERROR);
+
+ calc_crc = crc32(0L, Z_NULL, 0);
+ calc_crc = crc32(calc_crc, (uint8_t *)"IDAT", 4);
+ calc_crc = crc32(calc_crc, (uint8_t *)png->readbuf, length);
+
+ if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
+ return (PNG_FILE_ERROR);
+
+ if (orig_crc != calc_crc)
+ return (PNG_CRC_ERROR);
+
+ return (png_inflate(png, png->readbuf, length));
+}
+
+static int
+png_process_chunk(png_t *png)
+{
+ int result = PNG_NO_ERROR;
+ unsigned type;
+ unsigned length;
+
+ if (file_read_ul(png, &length) != PNG_NO_ERROR)
+ return (PNG_FILE_ERROR);
+
+ if (file_read_ul(png, &type) != PNG_NO_ERROR)
+ return (PNG_FILE_ERROR);
+
+ /*
+ * if we found an idat, all other idats should be followed with no
+ * other chunks in between
+ */
+ if (type == png_IDAT) {
+ if (!png->png_data) { /* first IDAT */
+ png->png_datalen = png->width * png->height *
+ png->bpp + png->height;
+ png->png_data = malloc(png->png_datalen);
+ }
+
+ if (!png->png_data)
+ return (PNG_MEMORY_ERROR);
+
+ if (!png->zs) {
+ result = png_init_inflate(png);
+ if (result != PNG_NO_ERROR)
+ return (result);
+ }
+
+ return (png_read_idat(png, length));
+ } else if (type == png_IEND)
+ return (PNG_DONE);
+ else
+ (void) file_read(png, 0, 1, length + 4); /* unknown chunk */
+
+ return (result);
+}
+
+static void
+png_filter_sub(unsigned stride, uint8_t *in, uint8_t *out, unsigned len)
+{
+ unsigned i;
+ uint8_t a = 0;
+
+ for (i = 0; i < len; i++) {
+ if (i >= stride)
+ a = out[i - stride];
+
+ out[i] = in[i] + a;
+ }
+}
+
+static void
+png_filter_up(unsigned stride __unused, uint8_t *in, uint8_t *out,
+ uint8_t *prev_line, unsigned len)
+{
+ unsigned i;
+
+ if (prev_line) {
+ for (i = 0; i < len; i++)
+ out[i] = in[i] + prev_line[i];
+ } else
+ memcpy(out, in, len);
+}
+
+static void
+png_filter_average(unsigned stride, uint8_t *in, uint8_t *out,
+ uint8_t *prev_line, unsigned len)
+{
+ unsigned int i;
+ uint8_t a = 0;
+ uint8_t b = 0;
+ unsigned int sum = 0;
+
+ for (i = 0; i < len; i++) {
+ if (prev_line)
+ b = prev_line[i];
+
+ if (i >= stride)
+ a = out[i - stride];
+
+ sum = a;
+ sum += b;
+
+ out[i] = in[i] + sum/2;
+ }
+}
+
+static uint8_t
+png_paeth(uint8_t a, uint8_t b, uint8_t c)
+{
+ int p = (int)a + b - c;
+ int pa = abs(p - a);
+ int pb = abs(p - b);
+ int pc = abs(p - c);
+
+ int pr;
+
+ if (pa <= pb && pa <= pc)
+ pr = a;
+ else if (pb <= pc)
+ pr = b;
+ else
+ pr = c;
+
+ return (pr);
+}
+
+static void
+png_filter_paeth(unsigned stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
+ unsigned len)
+{
+ unsigned i;
+ uint8_t a;
+ uint8_t b;
+ uint8_t c;
+
+ for (i = 0; i < len; i++) {
+ if (prev_line && i >= stride) {
+ a = out[i - stride];
+ b = prev_line[i];
+ c = prev_line[i - stride];
+ } else {
+ if (prev_line)
+ b = prev_line[i];
+ else
+ b = 0;
+
+ if (i >= stride)
+ a = out[i - stride];
+ else
+ a = 0;
+
+ c = 0;
+ }
+
+ out[i] = in[i] + png_paeth(a, b, c);
+ }
+}
+
+static int
+png_unfilter(png_t *png, uint8_t *data)
+{
+ unsigned i;
+ unsigned pos = 0;
+ unsigned outpos = 0;
+ uint8_t *filtered = png->png_data;
+ unsigned stride = png->bpp;
+
+ while (pos < png->png_datalen) {
+ uint8_t filter = filtered[pos];
+
+ pos++;
+
+ if (png->depth == 16) {
+ for (i = 0; i < png->width * stride; i += 2) {
+ *(short *)(filtered+pos+i) =
+ (filtered[pos+i] << 8) | filtered[pos+i+1];
+ }
+ }
+
+ switch (filter) {
+ case 0: /* none */
+ memcpy(data+outpos, filtered+pos, png->width * stride);
+ break;
+ case 1: /* sub */
+ png_filter_sub(stride, filtered+pos, data+outpos,
+ png->width * stride);
+ break;
+ case 2: /* up */
+ if (outpos) {
+ png_filter_up(stride, filtered+pos, data+outpos,
+ data + outpos - (png->width*stride),
+ png->width*stride);
+ } else {
+ png_filter_up(stride, filtered+pos, data+outpos,
+ 0, png->width*stride);
+ }
+ break;
+ case 3: /* average */
+ if (outpos) {
+ png_filter_average(stride, filtered+pos,
+ data+outpos,
+ data + outpos - (png->width*stride),
+ png->width*stride);
+ } else {
+ png_filter_average(stride, filtered+pos,
+ data+outpos, 0, png->width*stride);
+ }
+ break;
+ case 4: /* paeth */
+ if (outpos) {
+ png_filter_paeth(stride, filtered+pos,
+ data+outpos,
+ data + outpos - (png->width*stride),
+ png->width*stride);
+ } else {
+ png_filter_paeth(stride, filtered+pos,
+ data+outpos, 0, png->width*stride);
+ }
+ break;
+ default:
+ return (PNG_UNKNOWN_FILTER);
+ }
+
+ outpos += png->width * stride;
+ pos += png->width * stride;
+ }
+
+ return (PNG_NO_ERROR);
+}
+
+int
+png_get_data(png_t *png, uint8_t *data)
+{
+ int result = PNG_NO_ERROR;
+
+ png->zs = NULL;
+ png->png_datalen = 0;
+ png->png_data = NULL;
+ png->readbuf = NULL;
+ png->readbuflen = 0;
+
+ while (result == PNG_NO_ERROR)
+ result = png_process_chunk(png);
+
+ if (png->readbuf) {
+ free(png->readbuf);
+ png->readbuflen = 0;
+ }
+ if (png->zs)
+ (void) png_end_inflate(png);
+
+ if (result != PNG_DONE) {
+ free(png->png_data);
+ return (result);
+ }
+
+ result = png_unfilter(png, data);
+
+ free(png->png_data);
+
+ return (result);
+}
+
+char *
+png_error_string(int error)
+{
+ switch (error) {
+ case PNG_NO_ERROR:
+ return ("No error");
+ case PNG_FILE_ERROR:
+ return ("Unknown file error.");
+ case PNG_HEADER_ERROR:
+ return ("No PNG header found. Are you sure this is a PNG?");
+ case PNG_IO_ERROR:
+ return ("Failure while reading file.");
+ case PNG_EOF_ERROR:
+ return ("Reached end of file.");
+ case PNG_CRC_ERROR:
+ return ("CRC or chunk length error.");
+ case PNG_MEMORY_ERROR:
+ return ("Could not allocate memory.");
+ case PNG_ZLIB_ERROR:
+ return ("zlib reported an error.");
+ case PNG_UNKNOWN_FILTER:
+ return ("Unknown filter method used in scanline.");
+ case PNG_DONE:
+ return ("PNG done");
+ case PNG_NOT_SUPPORTED:
+ return ("The PNG is unsupported by pnglite, too bad for you!");
+ case PNG_WRONG_ARGUMENTS:
+ return ("Wrong combination of arguments passed to png_open. "
+ "You must use either a read_function or supply a file "
+ "pointer to use.");
+ default:
+ return ("Unknown error.");
+ };
+}
Index: stand/Makefile
===================================================================
--- stand/Makefile
+++ stand/Makefile
@@ -18,6 +18,7 @@
S.${MK_FDT}+= fdt
S.${MK_LOADER_OFW}+= libofw
S.yes+= defaults
+S.yes+= images
S.yes+= man
.include <bsd.arch.inc.mk>
Index: stand/common/bootstrap.h
===================================================================
--- stand/common/bootstrap.h
+++ stand/common/bootstrap.h
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/linker_set.h>
+#include <stdbool.h>
#include "readin.h"
@@ -119,6 +120,8 @@
};
extern struct console *consoles[];
void cons_probe(void);
+bool cons_update_mode(bool);
+void autoload_font(bool);
/*
* Plug-and-play enumerator/configurator interface.
@@ -257,6 +260,8 @@
int file_addmodule(struct preloaded_file *, char *, int,
struct kernel_module **);
void file_removemetadata(struct preloaded_file *fp);
+
+vm_offset_t build_font_module(vm_offset_t);
/* MI module loaders */
#ifdef __elfN
Index: stand/common/console.c
===================================================================
--- stand/common/console.c
+++ stand/common/console.c
@@ -147,6 +147,26 @@
}
}
+void
+putchar_device(int c, void *name)
+{
+ int cons;
+
+ if (name == NULL)
+ return;
+
+ if (c == '\n')
+ putchar_device('\r', name);
+ cons = cons_find(name);
+ if (cons < 0)
+ return;
+
+ /* Expand newlines if not in raw mode */
+ if ((consoles[cons]->c_flags & C_PRESENTOUT) == C_PRESENTOUT) {
+ consoles[cons]->c_out(c);
+ }
+}
+
/*
* Find the console with the specified name.
*/
Index: stand/common/gfx_fb.h
===================================================================
--- /dev/null
+++ stand/common/gfx_fb.h
@@ -0,0 +1,249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2020 Toomas Soome
+ * Copyright 2020 RackTop Systems, Inc.
+ *
+ * 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$
+ */
+
+#ifndef _GFX_FB_H
+#define _GFX_FB_H
+
+#include <sys/font.h>
+#include <teken.h>
+#include <stdbool.h>
+#include <machine/metadata.h>
+#include <pnglite.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EDID_MAGIC { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }
+
+struct edid_header {
+ uint8_t header[8]; /* fixed header pattern */
+ uint16_t manufacturer_id;
+ uint16_t product_code;
+ uint32_t serial_number;
+ uint8_t week_of_manufacture;
+ uint8_t year_of_manufacture;
+ uint8_t version;
+ uint8_t revision;
+};
+
+struct edid_basic_display_parameters {
+ uint8_t video_input_parameters;
+ uint8_t max_horizontal_image_size;
+ uint8_t max_vertical_image_size;
+ uint8_t display_gamma;
+ uint8_t supported_features;
+};
+
+struct edid_chromaticity_coordinates {
+ uint8_t red_green_lo;
+ uint8_t blue_white_lo;
+ uint8_t red_x_hi;
+ uint8_t red_y_hi;
+ uint8_t green_x_hi;
+ uint8_t green_y_hi;
+ uint8_t blue_x_hi;
+ uint8_t blue_y_hi;
+ uint8_t white_x_hi;
+ uint8_t white_y_hi;
+};
+
+struct edid_detailed_timings {
+ uint16_t pixel_clock;
+ uint8_t horizontal_active_lo;
+ uint8_t horizontal_blanking_lo;
+ uint8_t horizontal_hi;
+ uint8_t vertical_active_lo;
+ uint8_t vertical_blanking_lo;
+ uint8_t vertical_hi;
+ uint8_t horizontal_sync_offset_lo;
+ uint8_t horizontal_sync_pulse_width_lo;
+ uint8_t vertical_sync_lo;
+ uint8_t sync_hi;
+ uint8_t horizontal_image_size_lo;
+ uint8_t vertical_image_size_lo;
+ uint8_t image_size_hi;
+ uint8_t horizontal_border;
+ uint8_t vertical_border;
+ uint8_t features;
+};
+
+struct vesa_edid_info {
+ struct edid_header header;
+ struct edid_basic_display_parameters display;
+#define EDID_FEATURE_PREFERRED_TIMING_MODE (1 << 1)
+ struct edid_chromaticity_coordinates chromaticity;
+ uint8_t established_timings_1;
+ uint8_t established_timings_2;
+ uint8_t manufacturer_reserved_timings;
+ uint16_t standard_timings[8];
+ struct edid_detailed_timings detailed_timings[4];
+ uint8_t number_of_extensions;
+ uint8_t checksum;
+} __packed;
+
+#define STD_TIMINGS 8
+#define DET_TIMINGS 4
+
+#define HSIZE(x) (((x & 0xff) + 31) * 8)
+#define RATIO(x) ((x & 0xC000) >> 14)
+#define RATIO1_1 0
+/* EDID Ver. 1.3 redefined this */
+#define RATIO16_10 RATIO1_1
+#define RATIO4_3 1
+#define RATIO5_4 2
+#define RATIO16_9 3
+
+/*
+ * Number of pixels and lines is 12-bit int, valid values 0-4095.
+ */
+#define EDID_MAX_PIXELS 4095
+#define EDID_MAX_LINES 4095
+
+#define GET_EDID_INFO_WIDTH(edid_info, timings_num) \
+ ((edid_info)->detailed_timings[(timings_num)].horizontal_active_lo | \
+ (((uint32_t)(edid_info)->detailed_timings[(timings_num)].horizontal_hi & \
+ 0xf0) << 4))
+
+#define GET_EDID_INFO_HEIGHT(edid_info, timings_num) \
+ ((edid_info)->detailed_timings[(timings_num)].vertical_active_lo | \
+ (((uint32_t)(edid_info)->detailed_timings[(timings_num)].vertical_hi & \
+ 0xf0) << 4))
+
+struct resolution {
+ uint32_t width;
+ uint32_t height;
+ TAILQ_ENTRY(resolution) next;
+};
+
+typedef TAILQ_HEAD(edid_resolution, resolution) edid_res_list_t;
+
+struct vesa_flat_panel_info {
+ uint16_t HSize; /* Horizontal Size in Pixels */
+ uint16_t VSize; /* Vertical Size in Lines */
+ uint16_t FPType; /* Flat Panel Type */
+ uint8_t RedBPP; /* Red Bits Per Primary */
+ uint8_t GreenBPP; /* Green Bits Per Primary */
+ uint8_t BlueBPP; /* Blue Bits Per Primary */
+ uint8_t ReservedBPP; /* Reserved Bits Per Primary */
+ uint32_t RsvdOffScrnMemSize; /* Size in KB of Offscreen Memory */
+ uint32_t RsvdOffScrnMemPtr; /* Pointer to reserved offscreen memory */
+ uint8_t Reserved[14]; /* remainder of FPInfo */
+} __packed;
+
+#define COLOR_FORMAT_VGA 0
+#define COLOR_FORMAT_RGB 1
+#define NCOLORS 16
+extern uint32_t cmap[NCOLORS];
+
+enum FB_TYPE {
+ FB_TEXT = -1,
+ FB_GOP,
+ FB_UGA,
+ FB_VBE
+};
+
+enum COLOR_TYPE {
+ CT_INDEXED,
+ CT_RGB
+};
+
+struct gen_fb {
+ uint64_t fb_addr;
+ uint64_t fb_size;
+ uint32_t fb_height;
+ uint32_t fb_width;
+ uint32_t fb_stride;
+ uint32_t fb_mask_red;
+ uint32_t fb_mask_green;
+ uint32_t fb_mask_blue;
+ uint32_t fb_mask_reserved;
+ uint32_t fb_bpp;
+};
+
+typedef struct teken_gfx {
+ enum FB_TYPE tg_fb_type;
+ enum COLOR_TYPE tg_ctype;
+ unsigned tg_mode;
+ teken_t tg_teken; /* Teken core */
+ teken_pos_t tg_cursor; /* Where cursor was drawn */
+ bool tg_cursor_visible;
+ teken_pos_t tg_tp; /* Terminal dimensions */
+ teken_pos_t tg_origin; /* Point of origin in pixels */
+ uint8_t *tg_glyph; /* Memory for glyph */
+ size_t tg_glyph_size;
+ struct vt_font tg_font;
+ struct gen_fb tg_fb;
+ teken_funcs_t *tg_functions;
+ void *tg_private;
+} teken_gfx_t;
+
+extern font_list_t fonts;
+extern teken_gfx_t gfx_state;
+
+int generate_cons_palette(uint32_t *, int, uint32_t, int, uint32_t, int,
+ uint32_t, int);
+bool console_update_mode(bool);
+void setup_font(teken_gfx_t *, teken_unit_t, teken_unit_t);
+uint8_t *font_lookup(const struct vt_font *, teken_char_t,
+ const teken_attr_t *);
+void gfx_bitblt_bitmap(teken_gfx_t *, const uint8_t *, teken_char_t,
+ const teken_attr_t *, uint32_t);
+
+void gfx_fb_cons_display(uint32_t, uint32_t, uint32_t, uint32_t, void *);
+
+void bios_text_font(bool);
+bool gfx_get_edid_resolution(struct vesa_edid_info *, edid_res_list_t *);
+void gfx_framework_init(void);
+void gfx_bm_cons_display(uint32_t, uint32_t, uint32_t, uint32_t, void *);
+void gfx_bm_cursor_draw(teken_gfx_t *, const teken_pos_t *, uint32_t, uint32_t);
+void gfx_fb_setpixel(uint32_t, uint32_t);
+void gfx_fb_drawrect(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_term_drawrect(uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_line(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_bezier(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
+ uint32_t);
+
+#define FL_PUTIMAGE_BORDER 0x1
+#define FL_PUTIMAGE_NOSCROLL 0x2
+#define FL_PUTIMAGE_DEBUG 0x80
+
+int gfx_fb_putimage(png_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+bool gfx_parse_mode_str(char *, int *, int *, int *);
+void term_image_display(teken_gfx_t *, const teken_rect_t *);
+void bitmap_cpy(uint8_t *, uint8_t *, uint32_t, int);
+
+void reset_font_flags(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GFX_FB_H */
Index: stand/common/gfx_fb.c
===================================================================
--- /dev/null
+++ stand/common/gfx_fb.c
@@ -0,0 +1,1845 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2020 Toomas Soome
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 RackTop Systems, Inc.
+ *
+ * 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 <sys/param.h>
+#include <stand.h>
+#include <teken.h>
+#include <gfx_fb.h>
+#include <sys/font.h>
+#include <sys/stdint.h>
+#include <sys/endian.h>
+#include <pnglite.h>
+#include <bootstrap.h>
+#include <lz4.h>
+
+/* VGA text mode does use bold font. */
+#if !defined(VGA_8X16_FONT)
+#define VGA_8X16_FONT "/boot/fonts/vgarom-8x16.fnt"
+#endif
+#if !defined(DEFAULT_8X16_FONT)
+#define DEFAULT_8X16_FONT "/boot/fonts/vgarom-8x16.fnt"
+#endif
+
+/*
+ * Must be sorted by font size in descending order
+ */
+font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
+
+#define DEFAULT_FONT_DATA font_data_8x16
+extern vt_font_bitmap_data_t font_data_8x16;
+teken_gfx_t gfx_state = { 0 };
+
+#define NCOLORS 16
+static struct {
+ unsigned char r; /* Red percentage value. */
+ unsigned char g; /* Green percentage value. */
+ unsigned char b; /* Blue percentage value. */
+} color_def[NCOLORS] = {
+ {0, 0, 0}, /* black */
+ {50, 0, 0}, /* dark red */
+ {0, 50, 0}, /* dark green */
+ {77, 63, 0}, /* dark yellow */
+ {20, 40, 64}, /* dark blue */
+ {50, 0, 50}, /* dark magenta */
+ {0, 50, 50}, /* dark cyan */
+ {75, 75, 75}, /* light gray */
+
+ {18, 20, 21}, /* dark gray */
+ {100, 0, 0}, /* light red */
+ {0, 100, 0}, /* light green */
+ {100, 100, 0}, /* light yellow */
+ {45, 62, 81}, /* light blue */
+ {100, 0, 100}, /* light magenta */
+ {0, 100, 100}, /* light cyan */
+ {100, 100, 100}, /* white */
+};
+uint32_t cmap[NCOLORS];
+
+/*
+ * 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 bool insert_font(char *, FONT_FLAGS);
+
+static int font_set(struct env_var *, int, const void *);
+
+/*
+ * Initialize gfx framework.
+ */
+void
+gfx_framework_init(void)
+{
+ /*
+ * Setup font list to have builtin font.
+ */
+ (void) insert_font(NULL, FONT_BUILTIN);
+}
+
+/*
+ * Utility function to parse gfx mode line strings.
+ */
+bool
+gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
+{
+ char *p, *end;
+
+ errno = 0;
+ p = str;
+ *x = strtoul(p, &end, 0);
+ if (*x == 0 || errno != 0)
+ return (false);
+ if (*end != 'x')
+ return (false);
+ p = end + 1;
+ *y = strtoul(p, &end, 0);
+ if (*y == 0 || errno != 0)
+ return (false);
+ if (*end != 'x') {
+ *depth = -1; /* auto select */
+ } else {
+ p = end + 1;
+ *depth = strtoul(p, &end, 0);
+ if (*depth == 0 || errno != 0 || *end != '\0')
+ return (false);
+ }
+
+ return (true);
+}
+
+int
+generate_cons_palette(uint32_t *palette, int format,
+ uint32_t rmax, int roffset, uint32_t gmax, int goffset,
+ uint32_t bmax, int boffset)
+{
+ int i;
+
+ switch (format) {
+ case COLOR_FORMAT_VGA:
+ for (i = 0; i < NCOLORS; i++)
+ palette[i] = cons_to_vga_colors[i];
+ break;
+ case COLOR_FORMAT_RGB:
+#define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
+ for (i = 0; i < NCOLORS; i++) {
+ palette[i] = CF(r, i) | CF(g, i) | CF(b, i);
+ }
+#undef CF
+ break;
+ default:
+ return (ENODEV);
+ }
+
+ return (0);
+}
+
+static void
+gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint8_t *)(base + o) = v;
+}
+
+static void
+gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint16_t *)(base + o) = v;
+}
+
+static void
+gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
+{
+
+ if (o >= size)
+ return;
+ *(uint32_t *)(base + o) = v;
+}
+
+void
+gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph, teken_char_t c,
+ const teken_attr_t *a, uint32_t alpha)
+{
+ uint32_t width, height;
+ uint32_t fgc, bgc, bpl, cc, o;
+ int bpp, bit, byte;
+
+ bpp = state->tg_fb.fb_bpp >> 3;
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+ bpl = (width + 7) / 8; /* Bytes per source line. */
+
+ fgc = teken_256to16(a->ta_fgcolor);
+ bgc = teken_256to16(a->ta_bgcolor);
+ if (a->ta_format & TF_BOLD)
+ fgc |= TC_LIGHT;
+ if (a->ta_format & TF_BLINK)
+ bgc |= TC_LIGHT;
+
+ fgc = cmap[fgc];
+ bgc = cmap[bgc];
+
+ if (a->ta_format & TF_REVERSE) {
+ uint32_t tmp;
+
+ tmp = fgc;
+ fgc = bgc;
+ bgc = tmp;
+ }
+
+ bit = ffs(gfx_state.tg_fb.fb_mask_reserved) - 1;
+ alpha = ((gfx_state.tg_fb.fb_mask_reserved >> bit) & alpha) << bit;
+ fgc |= alpha;
+ bgc |= alpha;
+
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ byte = y * bpl + x / 8;
+ bit = 0x80 >> (x % 8);
+ o = y * width * bpp + x * bpp;
+ cc = glyph[byte] & bit ? fgc : bgc;
+
+ switch (bpp) {
+ case 1:
+ gfx_mem_wr1(state->tg_glyph,
+ state->tg_glyph_size, o, cc);
+ break;
+ case 2:
+ gfx_mem_wr2(state->tg_glyph,
+ state->tg_glyph_size, o, cc);
+ break;
+ case 3:
+ gfx_mem_wr1(state->tg_glyph,
+ state->tg_glyph_size, o, (cc >> 16) & 0xff);
+ gfx_mem_wr1(state->tg_glyph,
+ state->tg_glyph_size, o + 1,
+ (cc >> 8) & 0xff);
+ gfx_mem_wr1(state->tg_glyph,
+ state->tg_glyph_size, o + 2, cc & 0xff);
+ break;
+ case 4:
+ gfx_mem_wr4(state->tg_glyph,
+ state->tg_glyph_size, o, cc);
+ break;
+ }
+ }
+ }
+}
+
+void
+gfx_bm_cursor_draw(teken_gfx_t *state, const teken_pos_t *p,
+ uint32_t fg, uint32_t bg)
+{
+ uint32_t offset, size, *fb32;
+ uint16_t *fb16;
+ uint8_t *fb8, *fb;
+ uint32_t bpp, pitch;
+
+ bpp = roundup2(state->tg_fb.fb_bpp, 8) >> 3;
+ fb = ptov((uint32_t)gfx_state.tg_fb.fb_addr & 0xffffffff);
+ pitch = state->tg_fb.fb_stride * bpp;
+ size = state->tg_font.vf_width * bpp;
+
+ if (fb == NULL)
+ return;
+
+ /*
+ * Build cursor image. We are building mirror image of data on
+ * frame buffer by (D xor FG) xor BG.
+ */
+ offset = state->tg_origin.tp_row + p->tp_row * state->tg_font.vf_height;
+ offset *= pitch;
+
+ offset += (state->tg_origin.tp_col +
+ p->tp_col * state->tg_font.vf_width) * bpp;
+
+ switch (state->tg_fb.fb_bpp) {
+ case 8: /* 8 bit */
+ for (int i = 0; i < state->tg_font.vf_height; i++) {
+ fb8 = fb + offset + i * pitch;
+ for (uint32_t j = 0; j < size; j += 1) {
+ fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
+ }
+ }
+ break;
+ case 15:
+ case 16: /* 16 bit */
+ for (int i = 0; i < state->tg_font.vf_height; i++) {
+ fb16 = (uint16_t *)(fb + offset + i * pitch);
+ for (int j = 0; j < state->tg_font.vf_width; j++) {
+ fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
+ (bg & 0xffff);
+ }
+ }
+ break;
+ case 24: /* 24 bit */
+ for (int i = 0; i < state->tg_font.vf_height; i++) {
+ fb8 = fb + offset + i * pitch;
+ for (uint32_t j = 0; j < size; j += 3) {
+ fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
+ ((bg >> 16) & 0xff);
+ fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
+ ((bg >> 8) & 0xff);
+ fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
+ (bg & 0xff);
+ }
+ }
+ break;
+ case 32:
+ for (int i = 0; i < state->tg_font.vf_height; i++) {
+ fb32 = (uint32_t *)(fb + offset + i * pitch);
+ for (int j = 0; j < state->tg_font.vf_width; j++)
+ fb32[j] = (fb32[j] ^ fg) ^ bg;
+ }
+ break;
+ }
+}
+
+/*
+ * Implements alpha blending for RGBA data, could use pixels for arguments,
+ * but byte stream seems more generic.
+ * The generic alpha blending is:
+ * blend = alpha * fg + (1.0 - alpha) * bg.
+ * Since our alpha is not from range [0..1], we scale appropriately.
+ */
+static uint8_t
+alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
+{
+ uint16_t blend, h, l;
+
+ /* trivial corner cases */
+ if (alpha == 0)
+ return (bg);
+ if (alpha == 0xFF)
+ return (fg);
+ blend = (alpha * fg + (0xFF - alpha) * bg);
+ /* Division by 0xFF */
+ h = blend >> 8;
+ l = blend & 0xFF;
+ if (h + l >= 0xFF)
+ h++;
+ return (h);
+}
+
+/* Copy memory to framebuffer or to memory. */
+void
+bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
+{
+ uint32_t i;
+ uint8_t a;
+
+ switch (bpp) {
+ case 4:
+ /*
+ * we only implement alpha blending for depth 32,
+ * use memcpy for other cases.
+ */
+ for (i = 0; i < len; i += bpp) {
+ a = src[i+3];
+ dst[i] = alpha_blend(src[i], dst[i], a);
+ dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
+ dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
+ dst[i+3] = 0;
+ }
+ break;
+ default:
+ (void) memcpy(dst, src, len);
+ break;
+ }
+}
+
+void
+gfx_bm_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ void *data)
+{
+ uint32_t size; /* write size per scanline */
+ uint8_t *fbp; /* fb + calculated offset */
+ uint32_t i, bpp, pitch;
+
+ fbp = ptov((uint32_t)gfx_state.tg_fb.fb_addr & 0xffffffff);
+ if (fbp == NULL)
+ return;
+
+ bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+ pitch = gfx_state.tg_fb.fb_stride * bpp;
+
+ size = width * bpp;
+ fbp += x * bpp + y * pitch;
+
+ /* write all scanlines in rectangle */
+ for (i = 0; i < height; i++) {
+ uint8_t *dest = fbp + i * pitch;
+ uint8_t *src = (uint8_t *)data + i * size;
+ bitmap_cpy(dest, src, size, bpp);
+ }
+}
+
+/*
+ * Public graphics primitives.
+ */
+
+static int
+isqrt(int num)
+{
+ int res = 0;
+ int bit = 1 << 30;
+
+ /* "bit" starts at the highest power of four <= the argument. */
+ while (bit > num)
+ bit >>= 2;
+
+ while (bit != 0) {
+ if (num >= res + bit) {
+ num -= res + bit;
+ res = (res >> 1) + bit;
+ } else {
+ res >>= 1;
+ }
+ bit >>= 2;
+ }
+ return (res);
+}
+
+/* set pixel in framebuffer using gfx coordinates */
+void
+gfx_fb_setpixel(uint32_t x, uint32_t y)
+{
+ uint32_t c;
+ const teken_attr_t *ap;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ ap = teken_get_defattr(&gfx_state.tg_teken);
+ if (ap->ta_format & TF_REVERSE) {
+ c = teken_256to16(ap->ta_bgcolor);
+ if (ap->ta_format & TF_BLINK)
+ c |= TC_LIGHT;
+ } else {
+ c = teken_256to16(ap->ta_fgcolor);
+ if (ap->ta_format & TF_BOLD)
+ c |= TC_LIGHT;
+ }
+
+ if (gfx_state.tg_fb.fb_bpp == 32)
+ c = gfx_state.tg_fb.fb_mask_reserved | cmap[c]; /* Set alpha */
+ else
+ c = cmap[c];
+
+ if (x >= gfx_state.tg_fb.fb_width ||
+ y >= gfx_state.tg_fb.fb_height)
+ return;
+
+ gfx_fb_cons_display(x, y, 1, 1, &c);
+}
+
+/*
+ * draw rectangle in framebuffer using gfx coordinates.
+ * The function is borrowed from vt_fb.c
+ */
+void
+gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
+ uint32_t fill)
+{
+ uint32_t x, y;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ for (y = y1; y <= y2; y++) {
+ if (fill || (y == y1) || (y == y2)) {
+ for (x = x1; x <= x2; x++)
+ gfx_fb_setpixel(x, y);
+ } else {
+ gfx_fb_setpixel(x1, y);
+ gfx_fb_setpixel(x2, y);
+ }
+ }
+}
+
+void
+gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
+{
+ int dx, sx, dy, sy;
+ int err, e2, x2, y2, ed, width;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ width = wd;
+ sx = x0 < x1? 1 : -1;
+ sy = y0 < y1? 1 : -1;
+ dx = x1 > x0? x1 - x0 : x0 - x1;
+ dy = y1 > y0? y1 - y0 : y0 - y1;
+ err = dx + dy;
+ ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
+
+ for (;;) {
+ gfx_fb_setpixel(x0, y0);
+ e2 = err;
+ x2 = x0;
+ if ((e2 << 1) >= -dx) { /* x step */
+ e2 += dy;
+ y2 = y0;
+ while (e2 < ed * width &&
+ (y1 != (uint32_t)y2 || dx > dy)) {
+ y2 += sy;
+ gfx_fb_setpixel(x0, y2);
+ e2 += dx;
+ }
+ if (x0 == x1)
+ break;
+ e2 = err;
+ err -= dy;
+ x0 += sx;
+ }
+ if ((e2 << 1) <= dy) { /* y step */
+ e2 = dx-e2;
+ while (e2 < ed * width &&
+ (x1 != (uint32_t)x2 || dx < dy)) {
+ x2 += sx;
+ gfx_fb_setpixel(x2, y0);
+ e2 += dy;
+ }
+ if (y0 == y1)
+ break;
+ err += dx;
+ y0 += sy;
+ }
+ }
+}
+
+/*
+ * quadratic Bézier curve limited to gradients without sign change.
+ */
+void
+gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
+ uint32_t y2, uint32_t wd)
+{
+ int sx, sy, xx, yy, xy, width;
+ int dx, dy, err, curvature;
+ int i;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ width = wd;
+ sx = x2 - x1;
+ sy = y2 - y1;
+ xx = x0 - x1;
+ yy = y0 - y1;
+ curvature = xx*sy - yy*sx;
+
+ if (sx*sx + sy*sy > xx*xx+yy*yy) {
+ x2 = x0;
+ x0 = sx + x1;
+ y2 = y0;
+ y0 = sy + y1;
+ curvature = -curvature;
+ }
+ if (curvature != 0) {
+ xx += sx;
+ sx = x0 < x2? 1 : -1;
+ xx *= sx;
+ yy += sy;
+ sy = y0 < y2? 1 : -1;
+ yy *= sy;
+ xy = (xx*yy) << 1;
+ xx *= xx;
+ yy *= yy;
+ if (curvature * sx * sy < 0) {
+ xx = -xx;
+ yy = -yy;
+ xy = -xy;
+ curvature = -curvature;
+ }
+ dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
+ dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
+ xx += xx;
+ yy += yy;
+ err = dx + dy + xy;
+ do {
+ for (i = 0; i <= width; i++)
+ gfx_fb_setpixel(x0 + i, y0);
+ if (x0 == x2 && y0 == y2)
+ return; /* last pixel -> curve finished */
+ y1 = 2 * err < dx;
+ if (2 * err > dy) {
+ x0 += sx;
+ dx -= xy;
+ dy += yy;
+ err += dy;
+ }
+ if (y1 != 0) {
+ y0 += sy;
+ dy -= xy;
+ dx += xx;
+ err += dx;
+ }
+ } while (dy < dx); /* gradient negates -> algorithm fails */
+ }
+ gfx_fb_line(x0, y0, x2, y2, width);
+}
+
+/*
+ * draw rectangle using terminal coordinates and current foreground color.
+ */
+void
+gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
+{
+ int x1, y1, x2, y2;
+ int xshift, yshift;
+ int width, i;
+ uint32_t vf_width, vf_height;
+ teken_rect_t r;
+
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ vf_width = gfx_state.tg_font.vf_width;
+ vf_height = gfx_state.tg_font.vf_height;
+ width = vf_width / 4; /* line width */
+ xshift = (vf_width - width) / 2;
+ yshift = (vf_height - width) / 2;
+
+ /* Shift coordinates */
+ if (ux1 != 0)
+ ux1--;
+ if (uy1 != 0)
+ uy1--;
+ ux2--;
+ uy2--;
+
+ /* mark area used in terminal */
+ r.tr_begin.tp_col = ux1;
+ r.tr_begin.tp_row = uy1;
+ r.tr_end.tp_col = ux2 + 1;
+ r.tr_end.tp_row = uy2 + 1;
+
+ term_image_display(&gfx_state, &r);
+
+ /*
+ * Draw horizontal lines width points thick, shifted from outer edge.
+ */
+ x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y2 += vf_height - yshift - width;
+ gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
+
+ /*
+ * Draw vertical lines width points thick, shifted from outer edge.
+ */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x1 += vf_width - xshift - width;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+
+ /* Draw upper left corner. */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height;
+
+ x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width;
+ y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
+
+ /* Draw lower left corner. */
+ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
+ x1 += vf_width;
+ y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height - yshift;
+ x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+
+ /* Draw upper right corner. */
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width - xshift - width;
+ y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
+ y2 += vf_height;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
+
+ /* Draw lower right corner. */
+ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ y1 += vf_height - yshift;
+ x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
+ x2 += vf_width - xshift - width;
+ y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+}
+
+int
+gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
+ uint32_t uy2, uint32_t flags)
+{
+ uint8_t *data;
+ uint32_t i, j, x, y, fheight, fwidth, color;
+ int fbpp;
+ uint8_t r, g, b, a, *p;
+ bool scale = false;
+ bool trace = false;
+ teken_rect_t rect;
+
+ trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
+
+ if (gfx_state.tg_fb_type == FB_TEXT) {
+ if (trace)
+ printf("Framebuffer not active.\n");
+ return (1);
+ }
+
+ if (png->color_type != PNG_TRUECOLOR_ALPHA) {
+ if (trace)
+ printf("Not truecolor image.\n");
+ return (1);
+ }
+
+ if (ux1 > gfx_state.tg_fb.fb_width ||
+ uy1 > gfx_state.tg_fb.fb_height) {
+ if (trace)
+ printf("Top left coordinate off screen.\n");
+ return (1);
+ }
+
+ if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
+ if (trace)
+ printf("Image too large.\n");
+ return (1);
+ }
+
+ if (png->width < 1 || png->height < 1) {
+ if (trace)
+ printf("Image too small.\n");
+ return (1);
+ }
+
+ /*
+ * If 0 was passed for either ux2 or uy2, then calculate the missing
+ * part of the bottom right coordinate.
+ */
+ scale = true;
+ if (ux2 == 0 && uy2 == 0) {
+ /* Both 0, use the native resolution of the image */
+ ux2 = ux1 + png->width;
+ uy2 = uy1 + png->height;
+ scale = false;
+ } else if (ux2 == 0) {
+ /* Set ux2 from uy2/uy1 to maintain aspect ratio */
+ ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
+ } else if (uy2 == 0) {
+ /* Set uy2 from ux2/ux1 to maintain aspect ratio */
+ uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
+ }
+
+ if (ux2 > gfx_state.tg_fb.fb_width ||
+ uy2 > gfx_state.tg_fb.fb_height) {
+ if (trace)
+ printf("Bottom right coordinate off screen.\n");
+ return (1);
+ }
+
+ fwidth = ux2 - ux1;
+ fheight = uy2 - uy1;
+
+ /*
+ * If the original image dimensions have been passed explicitly,
+ * disable scaling.
+ */
+ if (fwidth == png->width && fheight == png->height)
+ scale = false;
+
+ if (ux1 == 0) {
+ /*
+ * No top left X co-ordinate (real coordinates start at 1),
+ * place as far right as it will fit.
+ */
+ ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
+ ux1 = ux2 - fwidth;
+ }
+
+ if (uy1 == 0) {
+ /*
+ * No top left Y co-ordinate (real coordinates start at 1),
+ * place as far down as it will fit.
+ */
+ uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
+ uy1 = uy2 - fheight;
+ }
+
+ if (ux1 >= ux2 || uy1 >= uy2) {
+ if (trace)
+ printf("Image dimensions reversed.\n");
+ return (1);
+ }
+
+ if (fwidth < 2 || fheight < 2) {
+ if (trace)
+ printf("Target area too small\n");
+ return (1);
+ }
+
+ if (trace)
+ printf("Image %ux%u -> %ux%u @%ux%u\n",
+ png->width, png->height, fwidth, fheight, ux1, uy1);
+
+ rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
+ rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
+ rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
+ rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
+
+ /*
+ * mark area used in terminal
+ */
+ if (!(flags & FL_PUTIMAGE_NOSCROLL))
+ term_image_display(&gfx_state, &rect);
+
+ if ((flags & FL_PUTIMAGE_BORDER))
+ gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
+
+ fbpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
+
+ data = malloc(fwidth * fheight * fbpp);
+ if (data == NULL) {
+ if (trace)
+ printf("Out of memory.\n");
+ return (1);
+ }
+
+ /*
+ * Build image for our framebuffer.
+ */
+
+ /* Helper to calculate the pixel index from the source png */
+#define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
+
+ /*
+ * For each of the x and y directions, calculate the number of pixels
+ * in the source image that correspond to a single pixel in the target.
+ * Use fixed-point arithmetic with 16-bits for each of the integer and
+ * fractional parts.
+ */
+ const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
+ const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
+
+ uint32_t hc = 0;
+ for (y = 0; y < fheight; y++) {
+ uint32_t hc2 = (hc >> 9) & 0x7f;
+ uint32_t hc1 = 0x80 - hc2;
+
+ uint32_t offset_y = hc >> 16;
+ uint32_t offset_y1 = offset_y + 1;
+
+ uint32_t wc = 0;
+ for (x = 0; x < fwidth; x++) {
+ uint32_t wc2 = (wc >> 9) & 0x7f;
+ uint32_t wc1 = 0x80 - wc2;
+
+ uint32_t offset_x = wc >> 16;
+ uint32_t offset_x1 = offset_x + 1;
+
+ /* Target pixel index */
+ j = (y * fwidth + x) * fbpp;
+
+ if (!scale) {
+ i = GETPIXEL(x, y);
+ r = png->image[i];
+ g = png->image[i + 1];
+ b = png->image[i + 2];
+ a = png->image[i + 3];
+ } else {
+ uint8_t pixel[4];
+
+ uint32_t p00 = GETPIXEL(offset_x, offset_y);
+ uint32_t p01 = GETPIXEL(offset_x, offset_y1);
+ uint32_t p10 = GETPIXEL(offset_x1, offset_y);
+ uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
+
+ /*
+ * Given a 2x2 array of pixels in the source
+ * image, combine them to produce a single
+ * value for the pixel in the target image.
+ * Each column of pixels is combined using
+ * a weighted average where the top and bottom
+ * pixels contribute hc1 and hc2 respectively.
+ * The calculation for bottom pixel pB and
+ * top pixel pT is:
+ * (pT * hc1 + pB * hc2) / (hc1 + hc2)
+ * Once the values are determined for the two
+ * columns of pixels, then the columns are
+ * averaged together in the same way but using
+ * wc1 and wc2 for the weightings.
+ *
+ * Since hc1 and hc2 are chosen so that
+ * hc1 + hc2 == 128 (and same for wc1 + wc2),
+ * the >> 14 below is a quick way to divide by
+ * (hc1 + hc2) * (wc1 + wc2)
+ */
+ for (i = 0; i < 4; i++)
+ pixel[i] = (
+ (png->image[p00 + i] * hc1 +
+ png->image[p01 + i] * hc2) * wc1 +
+ (png->image[p10 + i] * hc1 +
+ png->image[p11 + i] * hc2) * wc2)
+ >> 14;
+
+ r = pixel[0];
+ g = pixel[1];
+ b = pixel[2];
+ a = pixel[3];
+ }
+
+ if (trace)
+ printf("r/g/b: %x/%x/%x\n", r, g, b);
+ color =
+ (r << ffs(gfx_state.tg_fb.fb_mask_red) - 1) &
+ gfx_state.tg_fb.fb_mask_red |
+ (g << ffs(gfx_state.tg_fb.fb_mask_green) - 1) &
+ gfx_state.tg_fb.fb_mask_green |
+ (b << ffs(gfx_state.tg_fb.fb_mask_blue) - 1) &
+ gfx_state.tg_fb.fb_mask_blue;
+
+ switch (gfx_state.tg_fb.fb_bpp) {
+ case 8: {
+ uint32_t best, dist, k;
+ int diff;
+
+ color = 0;
+ best = 256 * 256 * 256;
+#define CF(_c, _m) (((_c) & (_m)) >> ffs(_m) - 1)
+ for (k = 0; k < 16; k++) {
+ diff = r - CF(cmap[k],
+ gfx_state.tg_fb.fb_mask_red);
+ dist = diff * diff;
+ diff = g - CF(cmap[k],
+ gfx_state.tg_fb.fb_mask_green);
+ dist += diff * diff;
+ diff = b - CF(cmap[k],
+ gfx_state.tg_fb.fb_mask_green);
+ dist += diff * diff;
+
+ if (dist < best) {
+ color = k;
+ best = dist;
+ if (dist == 0)
+ break;
+ }
+ }
+#undef CF
+ data[j] = cons_to_vga_colors[color];
+ break;
+ }
+ case 15:
+ case 16:
+ *(uint16_t *)(data+j) = color;
+ break;
+ case 24:
+ p = (uint8_t *)&color;
+ data[j] = p[0];
+ data[j+1] = p[1];
+ data[j+2] = p[2];
+ break;
+ case 32:
+ color |= a << 24;
+ *(uint32_t *)(data+j) = color;
+ break;
+ }
+ wc += wcstep;
+ }
+ hc += hcstep;
+ }
+
+ gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
+ free(data);
+ return (0);
+}
+
+/*
+ * Reset font flags to FONT_AUTO.
+ */
+void
+reset_font_flags(void)
+{
+ struct fontlist *fl;
+
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ fl->font_flags = FONT_AUTO;
+ }
+}
+
+static vt_font_bitmap_data_t *
+set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
+{
+ vt_font_bitmap_data_t *font = NULL;
+ struct fontlist *fl;
+ unsigned height = h;
+ unsigned width = w;
+
+ /*
+ * First check for manually loaded font.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_flags == FONT_MANUAL) {
+ font = fl->font_data;
+ if (font->vfbd_font == NULL && fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL || font->vfbd_font == NULL)
+ font = NULL;
+ break;
+ }
+ }
+
+ if (font != NULL) {
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ return (font);
+ }
+
+ /*
+ * Find best font for these dimensions, or use default
+ *
+ * A 1 pixel border is the absolute minimum we could have
+ * as a border around the text window (BORDER_PIXELS = 2),
+ * however a slightly larger border not only looks better
+ * but for the fonts currently statically built into the
+ * emulator causes much better font selection for the
+ * normal range of screen resolutions.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ font = fl->font_data;
+ if ((((*rows * font->vfbd_height) + BORDER_PIXELS) <= height) &&
+ (((*cols * font->vfbd_width) + BORDER_PIXELS) <= width)) {
+ if (font->vfbd_font == NULL ||
+ fl->font_flags == FONT_RELOAD) {
+ if (fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL)
+ continue;
+ }
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ break;
+ }
+ font = NULL;
+ }
+
+ if (font == NULL) {
+ /*
+ * We have fonts sorted smallest last, try it before
+ * falling back to builtin.
+ */
+ fl = STAILQ_LAST(&fonts, fontlist, font_next);
+ if (fl != NULL && fl->font_load != NULL &&
+ fl->font_name != NULL) {
+ font = fl->font_load(fl->font_name);
+ }
+ if (font == NULL)
+ font = &DEFAULT_FONT_DATA;
+
+ *rows = (height - BORDER_PIXELS) / font->vfbd_height;
+ *cols = (width - BORDER_PIXELS) / font->vfbd_width;
+ }
+
+ return (font);
+}
+
+static void
+cons_clear(void)
+{
+ char clear[] = { '\033', 'c' };
+
+ /* Reset terminal */
+ teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
+ gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
+}
+
+void
+setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
+{
+ vt_font_bitmap_data_t *font_data;
+ teken_pos_t *tp = &state->tg_tp;
+ char env[8];
+ int i;
+
+ /*
+ * set_font() will select a appropriate sized font for
+ * the number of rows and columns selected. If we don't
+ * have a font that will fit, then it will use the
+ * default builtin font and adjust the rows and columns
+ * to fit on the screen.
+ */
+ font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
+
+ if (font_data == NULL)
+ panic("out of memory");
+
+ for (i = 0; i < VFNT_MAPS; i++) {
+ state->tg_font.vf_map[i] =
+ font_data->vfbd_font->vf_map[i];
+ state->tg_font.vf_map_count[i] =
+ font_data->vfbd_font->vf_map_count[i];
+ }
+
+ state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
+ state->tg_font.vf_height = font_data->vfbd_font->vf_height;
+ state->tg_font.vf_width = font_data->vfbd_font->vf_width;
+
+ snprintf(env, sizeof (env), "%ux%u",
+ state->tg_font.vf_width, state->tg_font.vf_height);
+ env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
+ env, font_set, env_nounset);
+}
+
+/* Binary search for the glyph. Return 0 if not found. */
+static uint16_t
+font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
+{
+ unsigned min, mid, max;
+
+ min = 0;
+ max = len - 1;
+
+ /* Empty font map. */
+ if (len == 0)
+ return (0);
+ /* Character below minimal entry. */
+ if (src < map[0].vfm_src)
+ return (0);
+ /* Optimization: ASCII characters occur very often. */
+ if (src <= map[0].vfm_src + map[0].vfm_len)
+ return (src - map[0].vfm_src + map[0].vfm_dst);
+ /* Character above maximum entry. */
+ if (src > map[max].vfm_src + map[max].vfm_len)
+ return (0);
+
+ /* Binary search. */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (src < map[mid].vfm_src)
+ max = mid - 1;
+ else if (src > map[mid].vfm_src + map[mid].vfm_len)
+ min = mid + 1;
+ else
+ return (src - map[mid].vfm_src + map[mid].vfm_dst);
+ }
+
+ return (0);
+}
+
+/*
+ * Return glyph bitmap. If glyph is not found, we will return bitmap
+ * for the first (offset 0) glyph.
+ */
+uint8_t *
+font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
+{
+ uint16_t dst;
+ size_t stride;
+
+ /* Substitute bold with normal if not found. */
+ if (a->ta_format & TF_BOLD) {
+ dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
+ vf->vf_map_count[VFNT_MAP_BOLD], c);
+ if (dst != 0)
+ goto found;
+ }
+ dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
+ vf->vf_map_count[VFNT_MAP_NORMAL], c);
+
+found:
+ stride = howmany(vf->vf_width, 8) * vf->vf_height;
+ return (&vf->vf_bytes[dst * stride]);
+}
+
+static int
+load_mapping(int fd, struct vt_font *fp, int n)
+{
+ size_t i, size;
+ ssize_t rv;
+ vfnt_map_t *mp;
+
+ if (fp->vf_map_count[n] == 0)
+ return (0);
+
+ size = fp->vf_map_count[n] * sizeof(*mp);
+ mp = malloc(size);
+ if (mp == NULL)
+ return (ENOMEM);
+ fp->vf_map[n] = mp;
+
+ rv = read(fd, mp, size);
+ if (rv < 0 || (size_t)rv != size) {
+ free(fp->vf_map[n]);
+ fp->vf_map[n] = NULL;
+ return (EIO);
+ }
+
+ for (i = 0; i < fp->vf_map_count[n]; i++) {
+ mp[i].vfm_src = be32toh(mp[i].vfm_src);
+ mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
+ mp[i].vfm_len = be16toh(mp[i].vfm_len);
+ }
+ return (0);
+}
+
+static int
+builtin_mapping(struct vt_font *fp, int n)
+{
+ size_t size;
+ struct vfnt_map *mp;
+
+ if (n >= VFNT_MAPS)
+ return (EINVAL);
+
+ if (fp->vf_map_count[n] == 0)
+ return (0);
+
+ size = fp->vf_map_count[n] * sizeof(*mp);
+ mp = malloc(size);
+ if (mp == NULL)
+ return (ENOMEM);
+ fp->vf_map[n] = mp;
+
+ memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
+ return (0);
+}
+
+/*
+ * Load font from builtin or from file.
+ * We do need special case for builtin because the builtin font glyphs
+ * are compressed and we do need to uncompress them.
+ * Having single load_font() for both cases will help us to simplify
+ * font switch handling.
+ */
+static vt_font_bitmap_data_t *
+load_font(char *path)
+{
+ int fd, i;
+ uint32_t glyphs;
+ struct font_header fh;
+ struct fontlist *fl;
+ vt_font_bitmap_data_t *bp;
+ struct vt_font *fp;
+ size_t size;
+ ssize_t rv;
+
+ /* Get our entry from the font list. */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (strcmp(fl->font_name, path) == 0)
+ break;
+ }
+ if (fl == NULL)
+ return (NULL); /* Should not happen. */
+
+ bp = fl->font_data;
+ if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
+ return (bp);
+
+ fd = -1;
+ /*
+ * Special case for builtin font.
+ * Builtin font is the very first font we load, we do not have
+ * previous loads to be released.
+ */
+ if (fl->font_flags == FONT_BUILTIN) {
+ if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
+ return (NULL);
+
+ fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
+ fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
+
+ fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
+ if (fp->vf_bytes == NULL) {
+ free(fp);
+ return (NULL);
+ }
+
+ bp->vfbd_uncompressed_size =
+ DEFAULT_FONT_DATA.vfbd_uncompressed_size;
+ bp->vfbd_compressed_size =
+ DEFAULT_FONT_DATA.vfbd_compressed_size;
+
+ if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
+ fp->vf_bytes,
+ DEFAULT_FONT_DATA.vfbd_compressed_size,
+ DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
+ free(fp->vf_bytes);
+ free(fp);
+ return (NULL);
+ }
+
+ for (i = 0; i < VFNT_MAPS; i++) {
+ fp->vf_map_count[i] =
+ DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
+ if (builtin_mapping(fp, i) != 0)
+ goto free_done;
+ }
+
+ bp->vfbd_font = fp;
+ return (bp);
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ size = sizeof(fh);
+ rv = read(fd, &fh, size);
+ if (rv < 0 || (size_t)rv != size) {
+ bp = NULL;
+ goto done;
+ }
+ if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
+ bp = NULL;
+ goto done;
+ }
+ if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
+ bp = NULL;
+ goto done;
+ }
+ for (i = 0; i < VFNT_MAPS; i++)
+ fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
+
+ glyphs = be32toh(fh.fh_glyph_count);
+ fp->vf_width = fh.fh_width;
+ fp->vf_height = fh.fh_height;
+
+ size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
+ bp->vfbd_uncompressed_size = size;
+ if ((fp->vf_bytes = malloc(size)) == NULL)
+ goto free_done;
+
+ rv = read(fd, fp->vf_bytes, size);
+ if (rv < 0 || (size_t)rv != size)
+ goto free_done;
+ for (i = 0; i < VFNT_MAPS; i++) {
+ if (load_mapping(fd, fp, i) != 0)
+ goto free_done;
+ }
+
+ /*
+ * Reset builtin flag now as we have full font loaded.
+ */
+ if (fl->font_flags == FONT_BUILTIN)
+ fl->font_flags = FONT_AUTO;
+
+ /*
+ * Release previously loaded entries. We can do this now, as
+ * the new font is loaded. Note, there can be no console
+ * output till the new font is in place and teken is notified.
+ * We do need to keep fl->font_data for glyph dimensions.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_font == NULL)
+ continue;
+
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(fl->font_data->vfbd_font->vf_map[i]);
+ free(fl->font_data->vfbd_font->vf_bytes);
+ free(fl->font_data->vfbd_font);
+ fl->font_data->vfbd_font = NULL;
+ }
+
+ bp->vfbd_font = fp;
+ bp->vfbd_compressed_size = 0;
+
+done:
+ if (fd != -1)
+ close(fd);
+ return (bp);
+
+free_done:
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(fp->vf_map[i]);
+ free(fp->vf_bytes);
+ free(fp);
+ bp = NULL;
+ goto done;
+}
+
+struct name_entry {
+ char *n_name;
+ SLIST_ENTRY(name_entry) n_entry;
+};
+
+SLIST_HEAD(name_list, name_entry);
+
+/* Read font names from index file. */
+static struct name_list *
+read_list(char *fonts)
+{
+ struct name_list *nl;
+ struct name_entry *np;
+ char *dir, *ptr;
+ char buf[PATH_MAX];
+ int fd, len;
+
+ dir = strdup(fonts);
+ if (dir == NULL)
+ return (NULL);
+
+ ptr = strrchr(dir, '/');
+ *ptr = '\0';
+
+ fd = open(fonts, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ nl = malloc(sizeof(*nl));
+ if (nl == NULL) {
+ close(fd);
+ return (nl);
+ }
+
+ SLIST_INIT(nl);
+ while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
+ if (*buf == '#' || *buf == '\0')
+ continue;
+
+ if (bcmp(buf, "MENU", 4) == 0)
+ continue;
+
+ if (bcmp(buf, "FONT", 4) == 0)
+ continue;
+
+ ptr = strchr(buf, ':');
+ if (ptr == NULL)
+ continue;
+ else
+ *ptr = '\0';
+
+ np = malloc(sizeof(*np));
+ if (np == NULL) {
+ close(fd);
+ return (nl); /* return what we have */
+ }
+ if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
+ free(np);
+ close(fd);
+ return (nl); /* return what we have */
+ }
+ SLIST_INSERT_HEAD(nl, np, n_entry);
+ }
+ close(fd);
+ return (nl);
+}
+
+/*
+ * Read the font properties and insert new entry into the list.
+ * The font list is built in descending order.
+ */
+static bool
+insert_font(char *name, FONT_FLAGS flags)
+{
+ struct font_header fh;
+ struct fontlist *fp, *previous, *entry, *next;
+ size_t size;
+ ssize_t rv;
+ int fd;
+ char *font_name;
+
+ font_name = NULL;
+ if (flags == FONT_BUILTIN) {
+ /*
+ * We only install builtin font once, while setting up
+ * initial console. Since this will happen very early,
+ * we assume asprintf will not fail. Once we have access to
+ * files, the builtin font will be replaced by font loaded
+ * from file.
+ */
+ if (!STAILQ_EMPTY(&fonts))
+ return (false);
+
+ fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
+ fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
+
+ (void) asprintf(&font_name, "%dx%d",
+ DEFAULT_FONT_DATA.vfbd_width,
+ DEFAULT_FONT_DATA.vfbd_height);
+ } else {
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return (false);
+ rv = read(fd, &fh, sizeof(fh));
+ close(fd);
+ if (rv < 0 || (size_t)rv != sizeof(fh))
+ return (false);
+
+ if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
+ sizeof(fh.fh_magic)) != 0)
+ return (false);
+ font_name = strdup(name);
+ }
+
+ if (font_name == NULL)
+ return (false);
+
+ /*
+ * If we have an entry with the same glyph dimensions, replace
+ * the file name and mark us. We only support unique dimensions.
+ */
+ STAILQ_FOREACH(entry, &fonts, font_next) {
+ if (fh.fh_width == entry->font_data->vfbd_width &&
+ fh.fh_height == entry->font_data->vfbd_height) {
+ free(entry->font_name);
+ entry->font_name = font_name;
+ entry->font_flags = FONT_RELOAD;
+ return (true);
+ }
+ }
+
+ fp = calloc(sizeof(*fp), 1);
+ if (fp == NULL) {
+ free(font_name);
+ return (false);
+ }
+ fp->font_data = calloc(sizeof(*fp->font_data), 1);
+ if (fp->font_data == NULL) {
+ free(font_name);
+ free(fp);
+ return (false);
+ }
+ fp->font_name = font_name;
+ fp->font_flags = flags;
+ fp->font_load = load_font;
+ fp->font_data->vfbd_width = fh.fh_width;
+ fp->font_data->vfbd_height = fh.fh_height;
+
+ if (STAILQ_EMPTY(&fonts)) {
+ STAILQ_INSERT_HEAD(&fonts, fp, font_next);
+ return (true);
+ }
+
+ previous = NULL;
+ size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
+
+ STAILQ_FOREACH(entry, &fonts, font_next) {
+ vt_font_bitmap_data_t *bd;
+
+ bd = entry->font_data;
+ /* Should fp be inserted before the entry? */
+ if (size > bd->vfbd_width * bd->vfbd_height) {
+ if (previous == NULL) {
+ STAILQ_INSERT_HEAD(&fonts, fp, font_next);
+ } else {
+ STAILQ_INSERT_AFTER(&fonts, previous, fp,
+ font_next);
+ }
+ return (true);
+ }
+ next = STAILQ_NEXT(entry, font_next);
+ if (next == NULL ||
+ size > next->font_data->vfbd_width *
+ next->font_data->vfbd_height) {
+ STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
+ return (true);
+ }
+ previous = entry;
+ }
+ return (true);
+}
+
+static int
+font_set(struct env_var *ev __unused, int flags __unused, const void *value)
+{
+ struct fontlist *fl;
+ char *eptr;
+ unsigned long x = 0, y = 0;
+
+ /*
+ * Attempt to extract values from "XxY" string. In case of error,
+ * we have unmaching glyph dimensions and will just output the
+ * available values.
+ */
+ if (value != NULL) {
+ x = strtoul(value, &eptr, 10);
+ if (*eptr == 'x')
+ y = strtoul(eptr + 1, &eptr, 10);
+ }
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_width == x &&
+ fl->font_data->vfbd_height == y)
+ break;
+ }
+ if (fl != NULL) {
+ /* Reset any FONT_MANUAL flag. */
+ reset_font_flags();
+
+ /* Mark this font manually loaded */
+ fl->font_flags = FONT_MANUAL;
+ cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ return (CMD_OK);
+ }
+
+ printf("Available fonts:\n");
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ printf(" %dx%d\n", fl->font_data->vfbd_width,
+ fl->font_data->vfbd_height);
+ }
+ return (CMD_OK);
+}
+
+void
+bios_text_font(bool use_vga_font)
+{
+ if (use_vga_font)
+ (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
+ else
+ (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
+}
+
+void
+autoload_font(bool bios)
+{
+ struct name_list *nl;
+ struct name_entry *np;
+
+ nl = read_list("/boot/fonts/INDEX.fonts");
+ if (nl == NULL)
+ return;
+
+ while (!SLIST_EMPTY(nl)) {
+ np = SLIST_FIRST(nl);
+ SLIST_REMOVE_HEAD(nl, n_entry);
+ if (insert_font(np->n_name, FONT_AUTO) == false)
+ printf("failed to add font: %s\n", np->n_name);
+ free(np->n_name);
+ free(np);
+ }
+
+ unsetenv("screen.font");
+ env_setenv("screen.font", EV_VOLATILE, NULL, font_set, env_nounset);
+
+ /*
+ * If vga text mode was requested, load vga.font (8x16 bold) font.
+ */
+ if (bios) {
+ bios_text_font(true);
+ }
+
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+}
+
+COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
+
+static int
+command_font(int argc, char *argv[])
+{
+ int i, c, rc;
+ struct fontlist *fl;
+ vt_font_bitmap_data_t *bd;
+ bool list;
+
+ list = false;
+ optind = 1;
+ optreset = 1;
+ rc = CMD_OK;
+
+ while ((c = getopt(argc, argv, "l")) != -1) {
+ switch (c) {
+ case 'l':
+ list = true;
+ break;
+ case '?':
+ default:
+ return (CMD_ERROR);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1 || (list && argc != 0)) {
+ printf("Usage: loadfont [-l] | [file.fnt]\n");
+ return (CMD_ERROR);
+ }
+
+ if (list) {
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ printf("font %s: %dx%d%s\n", fl->font_name,
+ fl->font_data->vfbd_width,
+ fl->font_data->vfbd_height,
+ fl->font_data->vfbd_font == NULL? "" : " loaded");
+ }
+ return (CMD_OK);
+ }
+
+ /* Clear scren */
+ cons_clear();
+
+ if (argc == 1) {
+ char *name = argv[0];
+
+ if (insert_font(name, FONT_MANUAL) == false) {
+ printf("loadfont error: failed to load: %s\n", name);
+ return (CMD_ERROR);
+ }
+
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ return (CMD_OK);
+ }
+
+ if (argc == 0) {
+ /*
+ * Walk entire font list, release any loaded font, and set
+ * autoload flag. The font list does have at least the builtin
+ * default font.
+ */
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (fl->font_data->vfbd_font != NULL) {
+
+ bd = fl->font_data;
+ /*
+ * Note the setup_font() is releasing
+ * font bytes.
+ */
+ for (i = 0; i < VFNT_MAPS; i++)
+ free(bd->vfbd_font->vf_map[i]);
+ free(fl->font_data->vfbd_font);
+ fl->font_data->vfbd_font = NULL;
+ fl->font_data->vfbd_uncompressed_size = 0;
+ fl->font_flags = FONT_AUTO;
+ }
+ }
+ (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
+ }
+ return (rc);
+}
+
+bool
+gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
+{
+ struct resolution *rp, *p;
+
+ /*
+ * Walk detailed timings tables (4).
+ */
+ if ((edid->display.supported_features
+ & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
+ /* Walk detailed timing descriptors (4) */
+ for (int i = 0; i < DET_TIMINGS; i++) {
+ /*
+ * Reserved value 0 is not used for display decriptor.
+ */
+ if (edid->detailed_timings[i].pixel_clock == 0)
+ continue;
+ if ((rp = malloc(sizeof(*rp))) == NULL)
+ continue;
+ rp->width = GET_EDID_INFO_WIDTH(edid, i);
+ rp->height = GET_EDID_INFO_HEIGHT(edid, i);
+ if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
+ rp->height > 0 && rp->height <= EDID_MAX_LINES)
+ TAILQ_INSERT_TAIL(res, rp, next);
+ else
+ free(rp);
+ }
+ }
+
+ /*
+ * Walk standard timings list (8).
+ */
+ for (int i = 0; i < STD_TIMINGS; i++) {
+ /* Is this field unused? */
+ if (edid->standard_timings[i] == 0x0101)
+ continue;
+
+ if ((rp = malloc(sizeof(*rp))) == NULL)
+ continue;
+
+ rp->width = HSIZE(edid->standard_timings[i]);
+ switch (RATIO(edid->standard_timings[i])) {
+ case RATIO1_1:
+ rp->height = HSIZE(edid->standard_timings[i]);
+ if (edid->header.version > 1 ||
+ edid->header.revision > 2) {
+ rp->height = rp->height * 10 / 16;
+ }
+ break;
+ case RATIO4_3:
+ rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
+ break;
+ case RATIO5_4:
+ rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
+ break;
+ case RATIO16_9:
+ rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
+ break;
+ }
+
+ /*
+ * Create resolution list in decreasing order, except keep
+ * first entry (preferred timing mode).
+ */
+ TAILQ_FOREACH(p, res, next) {
+ if (p->width * p->height < rp->width * rp->height) {
+ /* Keep preferred mode first */
+ if (TAILQ_FIRST(res) == p)
+ TAILQ_INSERT_AFTER(res, p, rp, next);
+ else
+ TAILQ_INSERT_BEFORE(p, rp, next);
+ break;
+ }
+ if (TAILQ_NEXT(p, next) == NULL) {
+ TAILQ_INSERT_TAIL(res, rp, next);
+ break;
+ }
+ }
+ }
+ return (!TAILQ_EMPTY(res));
+}
Index: stand/common/module.c
===================================================================
--- stand/common/module.c
+++ stand/common/module.c
@@ -38,6 +38,8 @@
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/stdint.h>
+#include <sys/font.h>
+#include <gfx_fb.h>
#if defined(LOADER_FDT_SUPPORT)
#include <fdt_platform.h>
@@ -91,7 +93,6 @@
NULL
};
-
/*
* load an object, either a disk file or code module.
*
@@ -616,6 +617,92 @@
return (error);
}
+vm_offset_t
+build_font_module(vm_offset_t addr)
+{
+ vt_font_bitmap_data_t *bd;
+ struct vt_font *fd;
+ struct preloaded_file *fp;
+ size_t size;
+ uint32_t checksum;
+ int i;
+ struct font_info fi;
+ struct fontlist *fl;
+ uint64_t fontp;
+
+ if (STAILQ_EMPTY(&fonts))
+ return (addr);
+
+ /* We can't load first */
+ if ((file_findfile(NULL, NULL)) == NULL) {
+ printf("Can not load font module: %s\n",
+ "the kernel is not loaded");
+ return (addr);
+ }
+
+ /* helper pointers */
+ bd = NULL;
+ STAILQ_FOREACH(fl, &fonts, font_next) {
+ if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
+ gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
+ /*
+ * Kernel does have better built in font.
+ */
+ if (fl->font_flags == FONT_BUILTIN)
+ return (addr);
+
+ bd = fl->font_data;
+ break;
+ }
+ }
+ if (bd == NULL)
+ return (addr);
+ fd = bd->vfbd_font;
+
+ fi.fi_width = fd->vf_width;
+ checksum = fi.fi_width;
+ fi.fi_height = fd->vf_height;
+ checksum += fi.fi_height;
+ fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
+ checksum += fi.fi_bitmap_size;
+
+ size = roundup2(sizeof (struct font_info), 8);
+ for (i = 0; i < VFNT_MAPS; i++) {
+ fi.fi_map_count[i] = fd->vf_map_count[i];
+ checksum += fi.fi_map_count[i];
+ size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
+ size += roundup2(size, 8);
+ }
+ size += bd->vfbd_uncompressed_size;
+
+ fi.fi_checksum = -checksum;
+
+ fp = file_findfile(NULL, "elf kernel");
+ if (fp == NULL)
+ fp = file_findfile(NULL, "elf64 kernel");
+ if (fp == NULL)
+ panic("can't find kernel file");
+
+ fontp = addr;
+ addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
+ addr = roundup2(addr, 8);
+
+ /* Copy maps. */
+ for (i = 0; i < VFNT_MAPS; i++) {
+ if (fd->vf_map_count[i] != 0) {
+ addr += archsw.arch_copyin(fd->vf_map[i], addr,
+ fd->vf_map_count[i] * sizeof (struct vfnt_map));
+ addr = roundup2(addr, 8);
+ }
+ }
+
+ /* Copy the bitmap. */
+ addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
+
+ /* Looks OK so far; populate control structure */
+ file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
+ return (addr);
+}
#ifdef LOADER_VERIEXEC_VECTX
#define VECTX_HANDLE(fd) vctx
Index: stand/defaults/loader.conf.5
===================================================================
--- stand/defaults/loader.conf.5
+++ stand/defaults/loader.conf.5
@@ -233,7 +233,9 @@
.Dq spinning
character (useful for embedded products and such).
.It Va efi_max_resolution
-Specify the maximum desired resolution for the EFI console.
+.It Va vbe_max_resolution
+Specify the maximum desired resolution for the EFI or VESA BIOS Extension (VBE)
+framebuffer console.
The following values are accepted:
.Bl -column "WidthxHeight"
.It Sy Value Ta Sy Resolution
Index: stand/efi/include/efilib.h
===================================================================
--- stand/efi/include/efilib.h
+++ stand/efi/include/efilib.h
@@ -108,7 +108,6 @@
void efi_time_fini(void);
int parse_uefi_con_out(void);
-bool efi_cons_update_mode(void);
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
EFI_STATUS main(int argc, CHAR16 *argv[]);
Index: stand/efi/libefi/Makefile
===================================================================
--- stand/efi/libefi/Makefile
+++ stand/efi/libefi/Makefile
@@ -49,7 +49,7 @@
.endif
CFLAGS+= -I${EFIINC}
CFLAGS+= -I${EFIINCMD}
-CFLAGS.efi_console.c+= -I${SRCTOP}/sys/teken
+CFLAGS.efi_console.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
CFLAGS.teken.c+= -I${SRCTOP}/sys/teken
.if ${MK_LOADER_ZFS} != "no"
CFLAGS+= -I${ZFSSRC}
Index: stand/efi/libefi/efi_console.c
===================================================================
--- stand/efi/libefi/efi_console.c
+++ stand/efi/libefi/efi_console.c
@@ -27,17 +27,22 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/param.h>
#include <efi.h>
#include <efilib.h>
#include <teken.h>
#include <sys/reboot.h>
-
+#include <machine/metadata.h>
+#include <gfx_fb.h>
#include "bootstrap.h"
+extern EFI_GUID gop_guid;
+extern int efi_find_framebuffer(struct efi_fb *efifb);
static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
static SIMPLE_INPUT_INTERFACE *conin;
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
+static bool efi_started;
static int mode; /* Does ConOut have serial console? */
@@ -59,12 +64,20 @@
void end_term(void);
#endif
+#define TEXT_ROWS 24
+#define TEXT_COLS 80
+
static tf_bell_t efi_cons_bell;
static tf_cursor_t efi_text_cursor;
+static tf_cursor_t efi_gfx_cursor;
static tf_putchar_t efi_text_putchar;
+static tf_putchar_t efi_gfx_putchar;
static tf_fill_t efi_text_fill;
+static tf_fill_t efi_gfx_fill;
static tf_copy_t efi_text_copy;
+static tf_copy_t efi_gfx_copy;
static tf_param_t efi_text_param;
+static tf_param_t efi_gfx_param;
static tf_respond_t efi_cons_respond;
static teken_funcs_t tf = {
@@ -77,8 +90,15 @@
.tf_respond = efi_cons_respond,
};
-teken_t teken;
-teken_pos_t tp;
+static teken_funcs_t tfx = {
+ .tf_bell = efi_cons_bell,
+ .tf_cursor = efi_gfx_cursor,
+ .tf_putchar = efi_gfx_putchar,
+ .tf_fill = efi_gfx_fill,
+ .tf_copy = efi_gfx_copy,
+ .tf_param = efi_gfx_param,
+ .tf_respond = efi_cons_respond,
+};
struct text_pixel {
teken_char_t c;
@@ -116,6 +136,7 @@
int efi_cons_getchar(void);
void efi_cons_efiputchar(int);
int efi_cons_poll(void);
+static void cons_draw_frame(teken_attr_t *);
struct console efi_console = {
"efi",
@@ -129,6 +150,28 @@
};
/*
+ * 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)
+{
+ 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;
+ buffer[idx].a.ta_format |= TF_IMAGE;
+ }
+ }
+}
+
+/*
* Not implemented.
*/
static void
@@ -137,33 +180,108 @@
}
static void
-efi_text_cursor(void *s __unused, const teken_pos_t *p)
+efi_text_cursor(void *arg, const teken_pos_t *p)
{
- UINTN row, col;
+ teken_gfx_t *state = arg;
+ UINTN col, row;
- (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
+ row = p->tp_row;
+ if (p->tp_row >= state->tg_tp.tp_row)
+ row = state->tg_tp.tp_row - 1;
- if (p->tp_col == col)
- col = p->tp_col - 1;
- else
- col = p->tp_col;
+ col = p->tp_col;
+ if (p->tp_col >= state->tg_tp.tp_col)
+ col = state->tg_tp.tp_col - 1;
- if (p->tp_row == row)
- row = p->tp_row - 1;
- else
- row = p->tp_row;
-
conout->SetCursorPosition(conout, col, row);
}
static void
-efi_text_printchar(const teken_pos_t *p, bool autoscroll)
+efi_gfx_cursor_draw(teken_gfx_t *state, const teken_pos_t *p)
{
+ union pixel {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
+ uint32_t p32;
+ } *buf, *row, fg, bg;
+ EFI_GRAPHICS_OUTPUT *gop = state->tg_private;
+ struct text_pixel *px;
+ UINTN x, y, width, height;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+
+ x = p->tp_col * width + state->tg_origin.tp_col;
+ y = p->tp_row * height + state->tg_origin.tp_row;
+
+ px = buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ fg.p32 = teken_256to16(px->a.ta_fgcolor);
+ bg.p32 = teken_256to16(px->a.ta_bgcolor);
+ if (px->a.ta_format & TF_BOLD)
+ fg.p32 |= TC_LIGHT;
+ if (px->a.ta_format & TF_BLINK)
+ bg.p32 |= TC_LIGHT;
+
+ fg.p32 = cmap[fg.p32];
+ bg.p32 = cmap[bg.p32];
+
+ if (px->a.ta_format & TF_REVERSE) {
+ uint32_t tmp;
+
+ tmp = fg.p32;
+ fg.p32 = bg.p32;
+ bg.p32 = tmp;
+ }
+
+ state->tg_cursor = *p;
+ if (state->tg_fb_type != FB_GOP) {
+ gfx_bm_cursor_draw(state, p, fg.p32, bg.p32);
+ return;
+ }
+
+ buf = (union pixel *)state->tg_glyph;
+ if (gop->Blt(gop, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)buf,
+ EfiBltVideoToBltBuffer, x, y, 0, 0,
+ width, height, 0) != EFI_SUCCESS) {
+ return;
+ }
+
+ /*
+ * Build cursor image. We are building mirror image of data on
+ * frame buffer by (D xor FG) xor BG.
+ */
+ for (unsigned i = 0; i < height; i++) {
+ row = buf + i * width;
+ for (unsigned j = 0; j < width; j++)
+ row[j].p32 = (row[j].p32 ^ fg.p32) ^ bg.p32;
+ }
+
+ (void) gop->Blt(gop, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)buf,
+ EfiBltBufferToVideo, 0, 0, x, y, width, height, 0);
+}
+
+static void
+efi_gfx_cursor(void *arg, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+ EFI_TPL tpl;
+
+ /* Switch cursor off in old location and back on in new. */
+ if (state->tg_cursor_visible) {
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+ efi_gfx_cursor_draw(state, &state->tg_cursor);
+ efi_gfx_cursor_draw(state, p);
+ BS->RestoreTPL(tpl);
+ }
+}
+
+static void
+efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
+{
UINTN a, attr;
struct text_pixel *px;
teken_color_t fg, bg, tmp;
- px = buffer + p->tp_col + p->tp_row * tp.tp_col;
+ px = buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
a = conout->Mode->Attribute;
fg = teken_256to16(px->a.ta_fgcolor);
@@ -184,10 +302,10 @@
conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
- /* to prvent autoscroll, skip print of lower right char */
+ /* to prevent autoscroll, skip print of lower right char */
if (!autoscroll &&
- p->tp_row == tp.tp_row - 1 &&
- p->tp_col == tp.tp_col - 1)
+ p->tp_row == state->tg_tp.tp_row - 1 &&
+ p->tp_col == state->tg_tp.tp_col - 1)
return;
(void) conout->SetAttribute(conout, attr);
@@ -196,42 +314,147 @@
}
static void
-efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
+efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = s;
EFI_STATUS status;
int idx;
- idx = p->tp_col + p->tp_row * tp.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;
+
buffer[idx].c = c;
buffer[idx].a = *a;
- efi_text_printchar(p, false);
+
+ efi_text_printchar(s, p, false);
}
+/*
+ * Draw prepared glyph on terminal point p.
+ */
static void
-efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
+efi_gfx_printchar(teken_gfx_t *state, const teken_pos_t *p)
+{
+ unsigned x, y, width, height;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+ x = state->tg_origin.tp_col + p->tp_col * width;
+ y = state->tg_origin.tp_row + p->tp_row * height;
+
+ gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
+}
+
+/*
+ * Store char with its attribute to buffer and put it on screen.
+ */
+static void
+efi_gfx_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
- teken_pos_t p;
- UINTN row, col;
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ EFI_TPL tpl;
+ int idx;
- (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
+ 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;
- conout->EnableCursor(conout, FALSE);
+ buffer[idx].c = c;
+ buffer[idx].a = *a;
+
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ efi_gfx_cursor_draw(arg, &state->tg_cursor);
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, c, a, 0xff);
+ efi_gfx_printchar(state, p);
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ efi_gfx_cursor_draw(state, c);
+ }
+ BS->RestoreTPL(tpl);
+}
+
+static void
+efi_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;
+
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, FALSE);
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++)
- efi_text_putchar(s, &p, c, a);
- conout->EnableCursor(conout, TRUE);
+ efi_text_putchar(state, &p, c, a);
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, TRUE);
}
+static void
+efi_gfx_fill(void *arg, const teken_rect_t *r, teken_char_t c,
+ const teken_attr_t *a)
+{
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ EFI_TPL tpl;
+ teken_pos_t p;
+ struct text_pixel *row;
+
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ efi_gfx_cursor_draw(state, &state->tg_cursor);
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, c, a, 0xff);
+
+ for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
+ p.tp_row++) {
+ row = &buffer[p.tp_row * state->tg_tp.tp_col];
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ row[p.tp_col].c = c;
+ row[p.tp_col].a = *a;
+ efi_gfx_printchar(state, &p);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ efi_gfx_cursor_draw(state, c);
+ }
+ BS->RestoreTPL(tpl);
+}
+
static bool
efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
{
if (px1->c != px2->c)
return (false);
+ /* Is there image stored? */
+ if ((px1->a.ta_format & TF_IMAGE) ||
+ (px2->a.ta_format & TF_IMAGE))
+ return (false);
+
if (px1->a.ta_format != px2->a.ta_format)
return (false);
if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
@@ -243,11 +466,44 @@
}
static void
-efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
+efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
+ teken_pos_t *d, bool scroll)
{
- int srow, drow;
- int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
+ unsigned soffset, doffset;
+ teken_pos_t sp, dp;
+ int x;
+
+ soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
+ doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
+
+ sp = *s;
+ dp = *d;
+ for (x = 0; x < ncol; x++) {
+ sp.tp_col = s->tp_col + x;
+ dp.tp_col = d->tp_col + x;
+ if (!efi_same_pixel(&buffer[soffset + x],
+ &buffer[doffset + x])) {
+ buffer[doffset + x] =
+ buffer[soffset + x];
+ if (!scroll)
+ efi_text_printchar(state, &dp, false);
+ } else if (scroll) {
+ /* Draw last char and trigger scroll. */
+ if (dp.tp_col + 1 == state->tg_tp.tp_col &&
+ dp.tp_row + 1 == state->tg_tp.tp_row) {
+ efi_text_printchar(state, &dp, true);
+ }
+ }
+ }
+}
+
+static void
+efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+ unsigned doffset, soffset;
teken_pos_t d, s;
+ int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
bool scroll = false;
/*
@@ -262,90 +518,189 @@
* Check if we do copy whole screen.
*/
if (p->tp_row == 0 && p->tp_col == 0 &&
- nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2)
+ nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
scroll = true;
- conout->EnableCursor(conout, FALSE);
- if (p->tp_row < r->tr_begin.tp_row) {
- /* Copy from bottom to top. */
+ soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
+ doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, FALSE);
+
+ /*
+ * Copy line by line.
+ */
+ if (doffset <= soffset) {
+ s = r->tr_begin;
+ d = *p;
for (y = 0; y < nrow; y++) {
+ s.tp_row = r->tr_begin.tp_row + y;
d.tp_row = p->tp_row + y;
+
+ efi_text_copy_line(state, ncol, &s, &d, scroll);
+ }
+ } else {
+ for (y = nrow - 1; y >= 0; y--) {
s.tp_row = r->tr_begin.tp_row + y;
- drow = d.tp_row * tp.tp_col;
- srow = s.tp_row * 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;
+ d.tp_row = p->tp_row + y;
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- if (!scroll)
- efi_text_printchar(&d, false);
- } else if (scroll) {
- /*
- * Draw last char and trigger
- * scroll.
- */
- if (y == nrow - 1 &&
- x == ncol - 1) {
- efi_text_printchar(&d, true);
- }
- }
- }
+ efi_text_copy_line(state, ncol, &s, &d, false);
}
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible)
+ conout->EnableCursor(conout, TRUE);
+}
+
+static void
+efi_gfx_copy_area(teken_gfx_t *state, const teken_rect_t *s,
+ const teken_pos_t *d)
+{
+ UINTN sx, sy, dx, dy, width, height;
+ EFI_GRAPHICS_OUTPUT *gop = state->tg_private;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+
+ sx = state->tg_origin.tp_col + s->tr_begin.tp_col * width;
+ sy = state->tg_origin.tp_row + s->tr_begin.tp_row * height;
+ dx = state->tg_origin.tp_col + d->tp_col * width;
+ dy = state->tg_origin.tp_row + d->tp_row * height;
+
+ width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
+
+ if (state->tg_fb_type == FB_GOP) {
+ (void) gop->Blt(gop, NULL, EfiBltVideoToVideo, sx, sy, dx, dy,
+ width, height, 0);
} else {
- /* Copy from top to bottom. */
- if (p->tp_col < r->tr_begin.tp_col) {
- /* Copy from right to left. */
- 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;
- for (x = 0; x < ncol; x++) {
- d.tp_col = p->tp_col + x;
- s.tp_col = r->tr_begin.tp_col + x;
+ unsigned bpp, pitch;
+ uint8_t *fb, *src, *dst;
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- efi_text_printchar(&d, false);
- }
- }
+ fb = (uint8_t *)gfx_state.tg_fb.fb_addr;
+ bpp = state->tg_fb.fb_bpp >> 3;
+ pitch = state->tg_fb.fb_stride * bpp;
+
+ src = fb + sx * bpp + sy * pitch;
+ dst = fb + dx * bpp + dy * pitch;
+ width *= bpp;
+
+ for (unsigned i = 0; i < height; i++) {
+ unsigned increment = i * pitch;
+
+ (void) memmove(dst + increment, src + increment, width);
+ }
+ }
+}
+
+static void
+efi_gfx_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
+{
+ struct text_pixel *src, *dst;
+ teken_rect_t sr;
+ teken_pos_t dp;
+ unsigned soffset, doffset;
+ bool mark = false;
+ int x;
+
+ soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
+ doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
+
+ for (x = 0; x < ncol; x++) {
+ if (efi_same_pixel(&buffer[soffset + x],
+ &buffer[doffset + x])) {
+ if (mark) {
+ efi_gfx_copy_area(state, &sr, &dp);
+ mark = false;
}
} else {
- /* Copy from left to right. */
- 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;
- for (x = ncol - 1; x >= 0; x--) {
- d.tp_col = p->tp_col + x;
- s.tp_col = r->tr_begin.tp_col + x;
-
- if (!efi_same_pixel(
- &buffer[d.tp_col + drow],
- &buffer[s.tp_col + srow])) {
- buffer[d.tp_col + drow] =
- buffer[s.tp_col + srow];
- efi_text_printchar(&d, false);
- }
- }
+ buffer[doffset + x] = buffer[soffset + x];
+ if (mark) {
+ /* update end point */
+ sr.tr_end.tp_col = s->tp_col + x;;
+ } else {
+ /* set up new rectangle */
+ mark = true;
+ sr.tr_begin.tp_col = s->tp_col + x;
+ sr.tr_begin.tp_row = s->tp_row;
+ sr.tr_end.tp_col = s->tp_col + x;
+ sr.tr_end.tp_row = s->tp_row;
+ dp.tp_col = d->tp_col + x;
+ dp.tp_row = d->tp_row;
}
}
}
- conout->EnableCursor(conout, TRUE);
+ if (mark) {
+ efi_gfx_copy_area(state, &sr, &dp);
+ }
}
static void
-efi_text_param(void *s __unused, int cmd, unsigned int value)
+efi_gfx_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
{
+ teken_gfx_t *state = arg;
+ unsigned doffset, soffset;
+ teken_pos_t d, s;
+ int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
+ EFI_TPL tpl;
+
+ /*
+ * Copying is a little tricky. We must make sure we do it in
+ * correct order, to make sure we don't overwrite our own data.
+ */
+
+ nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
+ ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
+
+ if (p->tp_row + nrow > state->tg_tp.tp_row ||
+ p->tp_col + ncol > state->tg_tp.tp_col)
+ return;
+
+ soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
+ doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ efi_gfx_cursor_draw(state, &state->tg_cursor);
+
+ /*
+ * Copy line by line.
+ */
+ if (doffset <= soffset) {
+ s = r->tr_begin;
+ d = *p;
+ for (y = 0; y < nrow; y++) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ efi_gfx_copy_line(state, ncol, &s, &d);
+ }
+ } else {
+ for (y = nrow - 1; y >= 0; y--) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ efi_gfx_copy_line(state, ncol, &s, &d);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ efi_gfx_cursor_draw(state, c);
+ }
+ BS->RestoreTPL(tpl);
+}
+
+static void
+efi_text_param(void *arg, int cmd, unsigned int value)
+{
+ teken_gfx_t *state = arg;
+
switch (cmd) {
case TP_SETLOCALCURSOR:
/*
@@ -357,10 +712,13 @@
value = (value == 1) ? 0 : 1;
/* FALLTHROUGH */
case TP_SHOWCURSOR:
- if (value == 1)
+ if (value != 0) {
conout->EnableCursor(conout, TRUE);
- else
+ state->tg_cursor_visible = true;
+ } else {
conout->EnableCursor(conout, FALSE);
+ state->tg_cursor_visible = false;
+ }
break;
default:
/* Not yet implemented */
@@ -368,6 +726,36 @@
}
}
+static void
+efi_gfx_param(void *arg, int cmd, unsigned int value)
+{
+ teken_gfx_t *state = arg;
+ const teken_pos_t *c;
+
+ switch (cmd) {
+ case TP_SETLOCALCURSOR:
+ /*
+ * 0 means normal (usually block), 1 means hidden, and
+ * 2 means blinking (always block) for compatibility with
+ * syscons. We don't support any changes except hiding,
+ * so must map 2 to 0.
+ */
+ value = (value == 1) ? 0 : 1;
+ /* FALLTHROUGH */
+ case TP_SHOWCURSOR:
+ c = teken_get_cursor(&state->tg_teken);
+ efi_gfx_cursor_draw(state, c);
+ if (value != 0)
+ state->tg_cursor_visible = true;
+ else
+ state->tg_cursor_visible = false;
+ break;
+ default:
+ /* Not yet implemented */
+ break;
+ }
+}
+
/*
* Not implemented.
*/
@@ -470,7 +858,7 @@
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? */
@@ -484,8 +872,15 @@
return (CMD_OK);
a.ta_bgcolor = val;
}
+
+ /* Improve visibility */
+ 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);
}
@@ -823,19 +1218,100 @@
#endif
}
+static int
+env_screen_nounset(struct env_var *ev __unused)
+{
+ if (gfx_state.tg_fb_type == FB_TEXT)
+ return (0);
+ return (EPERM);
+}
+
+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);
+}
+
bool
-efi_cons_update_mode(void)
+cons_update_mode(bool use_gfx_mode)
{
UINTN cols, rows;
const teken_attr_t *a;
teken_attr_t attr;
EFI_STATUS status;
- char env[8], *ptr;
+ EFI_GRAPHICS_OUTPUT *gop = NULL;
+ struct efi_fb efifb;
+ char env[10], *ptr;
+ if (use_gfx_mode == true) {
+ gfx_state.tg_fb_type = FB_GOP;
+ if (gfx_state.tg_private == NULL) {
+ (void) BS->LocateProtocol(&gop_guid, NULL,
+ (void **)&gop);
+ gfx_state.tg_private = gop;
+ } else {
+ gop = gfx_state.tg_private;
+ }
+
+ /*
+ * We have FB but no GOP - it must be UGA.
+ */
+ if (gop == NULL)
+ gfx_state.tg_fb_type = FB_UGA;
+
+ if (efi_find_framebuffer(&efifb) == 0) {
+ int roff, goff, boff;
+
+ gfx_state.tg_fb.fb_addr = efifb.fb_addr;
+ gfx_state.tg_fb.fb_size = efifb.fb_size;
+ gfx_state.tg_fb.fb_height = efifb.fb_height;
+ gfx_state.tg_fb.fb_width = efifb.fb_width;
+ gfx_state.tg_fb.fb_stride = efifb.fb_stride;
+ gfx_state.tg_fb.fb_mask_red = efifb.fb_mask_red;
+ gfx_state.tg_fb.fb_mask_green = efifb.fb_mask_green;
+ gfx_state.tg_fb.fb_mask_blue = efifb.fb_mask_blue;
+ gfx_state.tg_fb.fb_mask_reserved =
+ efifb.fb_mask_reserved;
+ roff = ffs(efifb.fb_mask_red) - 1;
+ goff = ffs(efifb.fb_mask_green) - 1;
+ boff = ffs(efifb.fb_mask_blue) - 1;
+
+ (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
+ efifb.fb_mask_red >> roff, roff,
+ efifb.fb_mask_green >> goff, goff,
+ efifb.fb_mask_blue >> boff, boff);
+ gfx_state.tg_fb.fb_bpp = fls(
+ efifb.fb_mask_red | efifb.fb_mask_green |
+ efifb.fb_mask_blue | efifb.fb_mask_reserved);
+ }
+ } else {
+ gfx_state.tg_fb_type = FB_TEXT;
+ }
+
status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
if (EFI_ERROR(status) || cols * rows == 0) {
- cols = 80;
- rows = 24;
+ cols = TEXT_COLS;
+ rows = TEXT_ROWS;
}
/*
@@ -853,20 +1329,79 @@
*/
mode = parse_uefi_con_out();
if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
- if (buffer != NULL) {
- if (tp.tp_row == rows && tp.tp_col == cols)
- return (true);
- free(buffer);
+ conout->EnableCursor(conout, FALSE);
+ gfx_state.tg_cursor_visible = false;
+
+ if (gfx_state.tg_fb_type == FB_TEXT) {
+
+ gfx_state.tg_functions = &tf;
+ /* ensure the following are not set for text mode */
+ unsetenv("screen.height");
+ unsetenv("screen.width");
+ unsetenv("screen.depth");
} else {
- teken_init(&teken, &tf, NULL);
+ uint32_t fb_height, fb_width;
+
+ fb_height = gfx_state.tg_fb.fb_height;
+ fb_width = gfx_state.tg_fb.fb_width;
+
+ /*
+ * setup_font() can adjust terminal size.
+ * Note, we assume 80x24 terminal first, this is
+ * because the font selection will attempt to achieve
+ * at least this terminal dimension and we do not
+ * end up with too small font.
+ */
+ gfx_state.tg_tp.tp_row = TEXT_ROWS;
+ gfx_state.tg_tp.tp_col = TEXT_COLS;
+ setup_font(&gfx_state, fb_height, fb_width);
+ rows = gfx_state.tg_tp.tp_row;
+ cols = gfx_state.tg_tp.tp_col;
+ /* Point of origin in pixels. */
+ gfx_state.tg_origin.tp_row = (fb_height -
+ (rows * gfx_state.tg_font.vf_height)) / 2;
+ gfx_state.tg_origin.tp_col = (fb_width -
+ (cols * gfx_state.tg_font.vf_width)) / 2;
+
+ /* UEFI gop has depth 32. */
+ 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), "%dx%d",
+ fb_width, fb_height);
+ env_setenv("kern.vt.fb.default_mode",
+ EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", fb_height);
+ env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", 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);
}
- tp.tp_row = rows;
- tp.tp_col = cols;
+ /* Record our terminal screen size. */
+ gfx_state.tg_tp.tp_row = rows;
+ gfx_state.tg_tp.tp_col = cols;
+
+ teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
+ &gfx_state);
+
+ free(buffer);
buffer = malloc(rows * cols * sizeof(*buffer));
if (buffer != NULL) {
- teken_set_winsize(&teken, &tp);
- a = teken_get_defattr(&teken);
+ teken_set_winsize(&gfx_state.tg_teken,
+ &gfx_state.tg_tp);
+ a = teken_get_defattr(&gfx_state.tg_teken);
attr = *a;
/*
@@ -880,7 +1415,7 @@
ptr = getenv("teken.bg_color");
attr.ta_bgcolor = strtol(ptr, NULL, 10);
- teken_set_defattr(&teken, &attr);
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
} else {
snprintf(env, sizeof(env), "%d",
attr.ta_fgcolor);
@@ -891,18 +1426,12 @@
env_setenv("teken.bg_color", EV_VOLATILE, env,
efi_set_colors, env_nounset);
}
-
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- buffer[col + row * tp.tp_col].c = ' ';
- buffer[col + row * tp.tp_col].a = attr;
- }
- }
}
}
-#ifdef TERM_EMU
if (buffer == NULL) {
+ conout->EnableCursor(conout, TRUE);
+#ifdef TERM_EMU
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
DEFAULT_BGCOLOR));
end_term();
@@ -910,9 +1439,24 @@
curs_move(&curx, &cury, curx, cury);
fg_c = DEFAULT_FGCOLOR;
bg_c = DEFAULT_BGCOLOR;
- }
#endif
+ } else {
+ /* Improve visibility */
+ if (attr.ta_bgcolor == TC_WHITE)
+ attr.ta_bgcolor |= TC_LIGHT;
+ teken_set_defattr(&gfx_state.tg_teken, &attr);
+ /* 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);
+ }
+
snprintf(env, sizeof (env), "%u", (unsigned)rows);
setenv("LINES", env, 1);
snprintf(env, sizeof (env), "%u", (unsigned)cols);
@@ -926,10 +1470,15 @@
{
EFI_STATUS status;
- conout->EnableCursor(conout, TRUE);
- if (efi_cons_update_mode())
+ if (efi_started)
return (0);
+ efi_started = true;
+
+ gfx_framework_init();
+ if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
+ return (0);
+
return (1);
}
@@ -1040,7 +1589,7 @@
return;
}
- teken_input(&teken, &ch, sizeof (ch));
+ teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
}
static int
@@ -1232,4 +1781,32 @@
if (EFI_ERROR(status))
buf[0] = '?';
conout->OutputString(conout, buf);
+}
+
+void
+gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ void *data)
+{
+ EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf;
+ size_t size;
+
+ if (gfx_state.tg_fb_type != FB_GOP) {
+ gfx_bm_cons_display(x, y, width, height, data);
+ return;
+ }
+
+ size = width * height * sizeof(*buf);
+ buf = malloc(size);
+ if (buf == NULL) {
+ gfx_bm_cons_display(x, y, width, height, data);
+ return;
+ }
+
+ (void) gop->Blt(gop, buf, EfiBltVideoToBltBuffer, x, y, 0, 0,
+ width, height, 0);
+ bitmap_cpy((uint8_t *)buf, data, size, sizeof(*buf));
+ (void) gop->Blt(gop, buf, EfiBltBufferToVideo, 0, 0, x, y,
+ width, height, 0);
+ free(buf);
}
Index: stand/efi/loader/Makefile
===================================================================
--- stand/efi/loader/Makefile
+++ stand/efi/loader/Makefile
@@ -22,7 +22,8 @@
framebuffer.c \
main.c \
self_reloc.c \
- vers.c
+ vers.c \
+ 8x16.c
CFLAGS+= -I${.CURDIR}/../loader
.if ${MK_LOADER_ZFS} != "no"
@@ -73,6 +74,11 @@
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
+
+CLEANFILES+= 8x16.c
+
+8x16.c: ${SRCTOP}/share/vt/fonts/vgarom-8x16.hex
+ vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC}
FILES+= ${LOADER}.efi
FILESMODE_${LOADER}.efi= ${BINMODE}
Index: stand/efi/loader/bootinfo.c
===================================================================
--- stand/efi/loader/bootinfo.c
+++ stand/efi/loader/bootinfo.c
@@ -447,7 +447,7 @@
*/
uint32_t mdt[] = {
MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
- MODINFOMD_ENVP,
+ MODINFOMD_ENVP, MODINFOMD_FONT,
#if defined(LOADER_FDT_SUPPORT)
MODINFOMD_DTBP
#endif
@@ -480,6 +480,11 @@
/* Pad to a page boundary. */
addr = roundup(addr, PAGE_SIZE);
+ addr = build_font_module(addr);
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
/* Copy our environment. */
envp = addr;
addr = bi_copyenv(addr);
@@ -503,17 +508,17 @@
if (kfp == NULL)
panic("can't find kernel file");
kernend = 0; /* fill it in later */
- file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
- file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof(howto), &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof(envp), &envp);
#if defined(LOADER_FDT_SUPPORT)
if (dtb_size)
- file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof(dtbp), &dtbp);
else
printf("WARNING! Trying to fire up the kernel, but no "
"device tree blob found!\n");
#endif
- file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
- file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof(kernend), &kernend);
+ file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(ST), &ST);
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
Index: stand/efi/loader/framebuffer.c
===================================================================
--- stand/efi/loader/framebuffer.c
+++ stand/efi/loader/framebuffer.c
@@ -40,9 +40,10 @@
#include <efipciio.h>
#include <machine/metadata.h>
+#include "bootstrap.h"
#include "framebuffer.h"
-static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
@@ -586,7 +587,7 @@
mode, EFI_ERROR_CODE(status));
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
}
return (CMD_OK);
}
@@ -611,7 +612,7 @@
}
if (max_dim > 0)
conout->SetMode(conout, best_mode);
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
return (CMD_OK);
}
@@ -699,8 +700,10 @@
argv[0], mode, EFI_ERROR_CODE(status));
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
- } else if (!strcmp(argv[1], "get")) {
+ (void) cons_update_mode(true);
+ } else if (strcmp(argv[1], "off") == 0) {
+ (void) cons_update_mode(false);
+ } else if (strcmp(argv[1], "get") == 0) {
if (argc != 2)
goto usage;
efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
@@ -728,7 +731,7 @@
usage:
snprintf(command_errbuf, sizeof(command_errbuf),
- "usage: %s [list | get | set <mode>]", argv[0]);
+ "usage: %s [list | get | set <mode> | off]", argv[0]);
return (CMD_ERROR);
}
Index: stand/efi/loader/main.c
===================================================================
--- stand/efi/loader/main.c
+++ stand/efi/loader/main.c
@@ -1155,6 +1155,7 @@
!interactive_interrupt("Failed to find bootable partition"))
return (EFI_NOT_FOUND);
+ autoload_font(false); /* Set up the font list for console. */
efi_init_environment();
#if !defined(__arm__)
@@ -1354,7 +1355,7 @@
printf("couldn't set mode %d\n", mode);
return (CMD_ERROR);
}
- (void) efi_cons_update_mode();
+ (void) cons_update_mode(true);
return (CMD_OK);
}
Index: stand/ficl.mk
===================================================================
--- stand/ficl.mk
+++ stand/ficl.mk
@@ -15,7 +15,7 @@
.endif
CFLAGS+= -I${FICLSRC} -I${FICLSRC}/${FICL_CPUARCH} -I${LDRSRC}
-CFLAGS+= -DBF_DICTSIZE=15000
+CFLAGS+= -DBF_DICTSIZE=30000
.if ${MK_LOADER_VERIEXEC} != "no"
CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h
Index: stand/ficl/Makefile
===================================================================
--- stand/ficl/Makefile
+++ stand/ficl/Makefile
@@ -12,6 +12,8 @@
SRCS= ${BASE_SRCS} sysdep.c softcore.c
CLEANFILES+= softcore.c testmain testmain.o
+CFLAGS.loader.c += -I${SRCTOP}/sys/teken
+CFLAGS.loader.c += -I${SRCTOP}/contrib/pnglite
.ifmake testmain
CFLAGS= -DTESTMAIN -D_TESTMAIN
CFLAGS+= -I${FICLSRC} -I${FICLSRC}/${FICL_CPUARCH} -I${LDRSRC}
Index: stand/ficl/loader.c
===================================================================
--- stand/ficl/loader.c
+++ stand/ficl/loader.c
@@ -46,6 +46,8 @@
#include "bootstrap.h"
#include <string.h>
#include <uuid.h>
+#include <gfx_fb.h>
+#include <pnglite.h>
#include "ficl.h"
/* FreeBSD's loader interaction words and extras
@@ -65,7 +67,181 @@
* .# ( value -- )
*/
+/* ( flags x1 y1 x2 y2 -- flag ) */
void
+ficl_term_putimage(FICL_VM *pVM)
+{
+ char *namep, *name;
+ int names;
+ unsigned long ret = FICL_FALSE;
+ uint32_t x1, y1, x2, y2, f;
+ png_t png;
+ int error;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 1);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char *) stackPopPtr(pVM->pStack);
+ y2 = stackPopINT(pVM->pStack);
+ x2 = stackPopINT(pVM->pStack);
+ y1 = stackPopINT(pVM->pStack);
+ x1 = stackPopINT(pVM->pStack);
+ f = stackPopINT(pVM->pStack);
+
+ x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
+ y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
+ if (x2 != 0) {
+ x2 = gfx_state.tg_origin.tp_col +
+ x2 * gfx_state.tg_font.vf_width;
+ }
+ if (y2 != 0) {
+ y2 = gfx_state.tg_origin.tp_row +
+ y2 * gfx_state.tg_font.vf_height;
+ }
+
+ name = ficlMalloc(names + 1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ (void) strncpy(name, namep, names);
+ name[names] = '\0';
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = FICL_TRUE; /* success */
+ (void) png_close(&png);
+ }
+ ficlFree(name);
+ stackPushUNS(pVM->pStack, ret);
+}
+
+/* ( flags x1 y1 x2 y2 -- flag ) */
+void
+ficl_fb_putimage(FICL_VM *pVM)
+{
+ char *namep, *name;
+ int names;
+ unsigned long ret = FICL_FALSE;
+ uint32_t x1, y1, x2, y2, f;
+ png_t png;
+ int error;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 1);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char *) stackPopPtr(pVM->pStack);
+ y2 = stackPopINT(pVM->pStack);
+ x2 = stackPopINT(pVM->pStack);
+ y1 = stackPopINT(pVM->pStack);
+ x1 = stackPopINT(pVM->pStack);
+ f = stackPopINT(pVM->pStack);
+
+ name = ficlMalloc(names + 1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ (void) strncpy(name, namep, names);
+ name[names] = '\0';
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = FICL_TRUE; /* success */
+ (void) png_close(&png);
+ }
+ ficlFree(name);
+ stackPushUNS(pVM->pStack, ret);
+}
+
+void
+ficl_fb_setpixel(FICL_VM *pVM)
+{
+ FICL_UNS x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ y = stackPopUNS(pVM->pStack);
+ x = stackPopUNS(pVM->pStack);
+ gfx_fb_setpixel(x, y);
+}
+
+void
+ficl_fb_line(FICL_VM *pVM)
+{
+ FICL_UNS x0, y0, x1, y1, wd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 5, 0);
+#endif
+
+ wd = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ y0 = stackPopUNS(pVM->pStack);
+ x0 = stackPopUNS(pVM->pStack);
+ gfx_fb_line(x0, y0, x1, y1, wd);
+}
+
+void
+ficl_fb_bezier(FICL_VM *pVM)
+{
+ FICL_UNS x0, y0, x1, y1, x2, y2, width;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 7, 0);
+#endif
+
+ width = stackPopUNS(pVM->pStack);
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ y0 = stackPopUNS(pVM->pStack);
+ x0 = stackPopUNS(pVM->pStack);
+ gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
+}
+
+void
+ficl_fb_drawrect(FICL_VM *pVM)
+{
+ FICL_UNS x1, x2, y1, y2, fill;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 5, 0);
+#endif
+
+ fill = stackPopUNS(pVM->pStack);
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ gfx_fb_drawrect(x1, y1, x2, y2, fill);
+}
+
+void
+ficl_term_drawrect(FICL_VM *pVM)
+{
+ FICL_UNS x1, x2, y1, y2;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 0);
+#endif
+
+ y2 = stackPopUNS(pVM->pStack);
+ x2 = stackPopUNS(pVM->pStack);
+ y1 = stackPopUNS(pVM->pStack);
+ x1 = stackPopUNS(pVM->pStack);
+ gfx_term_drawrect(x1, y1, x2, y2);
+}
+
+void
ficlSetenv(FICL_VM *pVM)
{
#ifndef TESTMAIN
@@ -866,6 +1042,13 @@
dictAppendWord(dp, "ccall", ficlCcall, FW_DEFAULT);
dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT);
dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT);
+ dictAppendWord(dp, "fb-setpixel", ficl_fb_setpixel, FW_DEFAULT);
+ dictAppendWord(dp, "fb-line", ficl_fb_line, FW_DEFAULT);
+ dictAppendWord(dp, "fb-bezier", ficl_fb_bezier, FW_DEFAULT);
+ dictAppendWord(dp, "fb-drawrect", ficl_fb_drawrect, FW_DEFAULT);
+ dictAppendWord(dp, "fb-putimage", ficl_fb_putimage, FW_DEFAULT);
+ dictAppendWord(dp, "term-drawrect", ficl_term_drawrect, FW_DEFAULT);
+ dictAppendWord(dp, "term-putimage", ficl_term_putimage, FW_DEFAULT);
#ifndef TESTMAIN
dictAppendWord(dp, "isvirtualized?",ficlIsvirtualizedQ, FW_DEFAULT);
#endif
Index: stand/ficl/softwords/softcore.fr
===================================================================
--- stand/ficl/softwords/softcore.fr
+++ stand/ficl/softwords/softcore.fr
@@ -67,6 +67,12 @@
: tuck ( y x -- x y x) swap over ;
: within ( test low high -- flag ) over - >r - r> u< ;
+: u.r ( n +n -- )
+ swap 0 <# #s #>
+ rot over - dup 0< if
+ drop else spaces
+ then
+ type space ;
\ ** LOCAL EXT word set
\ #if FICL_WANT_LOCALS
@@ -91,12 +97,32 @@
\ ** TOOLS word set...
: ? ( addr -- ) @ . ;
-: dump ( addr u -- )
- 0 ?do
- dup c@ . 1+
- i 7 and 7 = if cr endif
- loop drop
-;
+
+Variable /dump
+
+: i' ( R:w R:w2 -- R:w R:w2 w )
+ r> r> r> dup >r swap >r swap >r ;
+
+: .4 ( addr -- addr' )
+ 4 0 DO -1 /dump +! /dump @ 0<
+ IF 3 spaces ELSE dup c@ 0 <# # # #> type space THEN
+ char+ LOOP ;
+
+: .chars ( addr -- )
+ /dump @ over + swap
+ ?DO I c@ dup 127 bl within
+ IF drop [char] . THEN emit
+ LOOP ;
+
+: .line ( addr -- )
+ dup .4 space .4 ." - " .4 space .4 drop 16 /dump +! space .chars ;
+
+: dump ( addr u -- ) \ tools dump
+ cr base @ >r hex \ save base on return stack
+ 0 ?DO I' I - 16 min /dump !
+ dup 8 u.r ." : " dup .line cr 16 +
+ 16 +LOOP
+ drop r> base ! ;
\ ** SEARCH+EXT words and ficl helpers
\ BRAND-WORDLIST is a helper for ficl-named-wordlist. Usage idiom:
Index: stand/forth/beastie.4th
===================================================================
--- stand/forth/beastie.4th
+++ stand/forth/beastie.4th
@@ -82,6 +82,10 @@
then
;
+: draw-beastie
+ ['] draw-beastie console-iterate
+;
+
also support-functions
: beastie-start ( -- ) \ starts the menu
Index: stand/forth/brand-fbsd.4th
===================================================================
--- stand/forth/brand-fbsd.4th
+++ stand/forth/brand-fbsd.4th
@@ -34,6 +34,18 @@
: brand ( x y -- ) \ "FreeBSD" [wide] logo in B/W (7 rows x 42 columns)
+ framebuffer? if
+ s" term-putimage" sfind if
+ \ note, we use 0, 0 for image upper left as origin,
+ \ and 0, 7 for lower right to preserve aspect ratio
+ >r 0 0 0 0 7
+ s" /boot/images/freebsd-brand-rev.png"
+ r> execute if 2drop exit then
+ else
+ drop
+ then
+ then
+
s" ______ ____ _____ _____ " brand+
s" | ____| | _ \ / ____| __ \ " brand+
s" | |___ _ __ ___ ___ | |_) | (___ | | | |" brand+
Index: stand/forth/brand.4th
===================================================================
--- stand/forth/brand.4th
+++ stand/forth/brand.4th
@@ -72,3 +72,7 @@
else drop then
then
;
+
+: draw-brand
+ ['] draw-brand console-iterate
+;
Index: stand/forth/color.4th
===================================================================
--- stand/forth/color.4th
+++ stand/forth/color.4th
@@ -28,7 +28,7 @@
\ This function returns FALSE if the `loader_color' environment variable is set
\ to NO, no, or 0. It returns TRUE if `loader_color' is set to any other value.
-\ If `loader_color' is unset, TRUE is returned (unless booting serial).
+\ If `loader_color' is unset, TRUE is returned.
\
: loader_color? ( -- t )
@@ -44,12 +44,8 @@
FALSE exit
then
drop
- \ It is enabled.
- TRUE
- else
- \ `loader_color' is unset.
- \ Default to using color unless serial boot is active.
- drop
- boot_serial? 0=
then
+ drop
+ \ It is enabled.
+ TRUE
;
Index: stand/forth/frames.4th
===================================================================
--- stand/forth/frames.4th
+++ stand/forth/frames.4th
@@ -121,6 +121,20 @@
;
: box ( w h x y -- ) \ Draw a box
+ framebuffer? if
+ s" term-drawrect" sfind if
+ >R
+ rot ( w x y h )
+ over + >R ( w x y -- R: y+h )
+ swap rot ( y x w -- R: y+h )
+ over + >R ( y x -- R: y+h x+w )
+ swap R> R> R> execute
+ exit
+ else
+ drop
+ then
+ then
+ \ Non-framebuffer version
2dup 1+ 4 pick 1- -rot
vline \ Draw left vert line
2dup 1+ swap 5 pick + swap 4 pick 1- -rot
Index: stand/forth/logo-orb.4th
===================================================================
--- stand/forth/logo-orb.4th
+++ stand/forth/logo-orb.4th
@@ -35,6 +35,18 @@
: logo ( x y -- ) \ color Orb mascot (15 rows x 30 columns)
+ framebuffer? if
+ s" term-putimage" sfind if
+ >r 2dup ( x y x y )
+ >r 0 swap r> ( x y 0 x y )
+ dup 0 swap 15 + ( x y 0 x y 0 y+15 )
+ s" /boot/images/freebsd-logo-rev.png"
+ r> execute if 2drop exit then
+ else
+ drop
+ then
+ then
+
s" @[31m``` @[31;1m`@[31m" logo+
s" s` `.....---...@[31;1m....--.``` -/@[31m" logo+
s" +o .--` @[31;1m/y:` +.@[31m" logo+
Index: stand/forth/menu.4th
===================================================================
--- stand/forth/menu.4th
+++ stand/forth/menu.4th
@@ -991,6 +991,24 @@
menu-create
;
+: menu-box
+ f_double ( default frame type )
+ \ Interpret a custom frame type for the menu
+ TRUE ( draw a box? default yes, but might be altered below )
+ s" loader_menu_frame" getenv dup -1 = if ( 1 )
+ drop \ no custom frame type
+ else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
+ f_single ( see frames.4th )
+ else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
+ f_double ( see frames.4th )
+ else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
+ drop FALSE \ don't draw a box
+ ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
+ if
+ 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
+ then
+;
+
\ This function initializes the menu. Call this from your `loader.rc' file
\ before calling any other menu-related functions.
\
@@ -1021,21 +1039,7 @@
then
menuX !
- \ Interpret a custom frame type for the menu
- TRUE ( draw a box? default yes, but might be altered below )
- s" loader_menu_frame" getenv dup -1 = if ( 1 )
- drop \ no custom frame type
- else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
- f_single ( see frames.4th )
- else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
- f_double ( see frames.4th )
- else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
- drop FALSE \ don't draw a box
- ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
- if
- 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
- then
-
+ ['] menu-box console-iterate
0 25 at-xy \ Move cursor to the bottom for output
;
Index: stand/forth/support.4th
===================================================================
--- stand/forth/support.4th
+++ stand/forth/support.4th
@@ -190,6 +190,25 @@
0 0
;
+: strspn { addr len addr1 len1 | paddr plen -- addr' len' }
+ begin
+ len
+ while
+ addr1 to paddr
+ len1 to plen
+ begin
+ plen
+ while
+ addr c@ paddr c@ = if addr len exit then
+ paddr 1+ to paddr
+ plen 1- to plen
+ repeat
+ addr 1 + to addr
+ len 1 - to len
+ repeat
+ 0 0
+;
+
: s' \ same as s", allows " in the string
[char] ' parse
state @ if postpone sliteral then
@@ -201,6 +220,53 @@
: getenv? getenv -1 = if false else drop true then ;
+\ execute xt for each device listed in console variable.
+\ this allows us to have device specific output for logos, menu frames etc
+: console-iterate { xt | caddr clen taddr tlen -- }
+ \ get current console and save it
+ s" console" getenv
+ ['] strdup catch if 2drop exit then
+ to clen to caddr
+
+ clen to tlen
+ caddr to taddr
+ begin
+ tlen
+ while
+ taddr tlen s" , " strspn
+ \ we need to handle 3 cases for addr len pairs on stack:
+ \ addr len are 0 0 - there was no comma nor space
+ \ addr len are x 0 - the first char is either comma or space
+ \ addr len are x y.
+ 2dup + 0= if
+ \ there was no comma nor space.
+ 2drop
+ taddr tlen s" console" setenv
+ xt execute
+ 0 to tlen
+ else dup 0= if
+ 2drop
+ else
+ dup ( taddr' tlen' tlen' )
+ tlen swap - dup
+ 0= if \ sequence of comma and space?
+ drop
+ else
+ taddr swap s" console" setenv
+ xt execute
+ then
+ to tlen
+ to taddr
+ then then
+ tlen 0> if \ step over separator
+ tlen 1- to tlen
+ taddr 1+ to taddr
+ then
+ repeat
+ caddr clen s" console" setenv \ restore console setup
+ caddr free drop
+;
+
\ determine if a word appears in a string, case-insensitive
: contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
2 pick 0= if 2drop 2drop true exit then
@@ -231,14 +297,23 @@
s" console" getenv dup -1 <> if
s" comconsole" 2swap contains?
else drop false then
- s" boot_serial" getenv dup -1 <> if
- swap drop 0>
- else drop false then
- or \ console contains comconsole ( or ) boot_serial
- s" boot_multicons" getenv dup -1 <> if
- swap drop 0>
- else drop false then
- or \ previous boolean ( or ) boot_multicons
+\ s" boot_serial" getenv dup -1 <> if
+\ swap drop 0>
+\ else drop false then
+\ or \ console contains comconsole ( or ) boot_serial
+\ s" boot_multicons" getenv dup -1 <> if
+\ swap drop 0>
+\ else drop false then
+\ or \ previous boolean ( or ) boot_multicons
+;
+
+: framebuffer? ( -- t )
+ s" console" getenv
+ 2dup s" efi" compare 0<> >r
+ s" vidconsole" compare 0<> r> and if
+ FALSE exit
+ then
+ s" screen-width" getenv?
;
\ Private definitions
Index: stand/i386/libi386/Makefile
===================================================================
--- stand/i386/libi386/Makefile
+++ stand/i386/libi386/Makefile
@@ -9,7 +9,7 @@
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,10 @@
.endif
# terminal emulation
-CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken
+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
Index: stand/i386/libi386/bootinfo.c
===================================================================
--- stand/i386/libi386/bootinfo.c
+++ stand/i386/libi386/bootinfo.c
@@ -32,9 +32,20 @@
#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)
Index: stand/i386/libi386/bootinfo32.c
===================================================================
--- stand/i386/libi386/bootinfo32.c
+++ stand/i386/libi386/bootinfo32.c
@@ -205,6 +205,8 @@
/* 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 @@
#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;
Index: stand/i386/libi386/bootinfo64.c
===================================================================
--- stand/i386/libi386/bootinfo64.c
+++ stand/i386/libi386/bootinfo64.c
@@ -227,6 +227,8 @@
/* 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 @@
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
#endif
+ bi_load_vbe_data(kfp);
size = bi_copymodules64(0);
Index: stand/i386/libi386/libi386.h
===================================================================
--- stand/i386/libi386/libi386.h
+++ stand/i386/libi386/libi386.h
@@ -145,6 +145,7 @@
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);
Index: stand/i386/libi386/vbe.h
===================================================================
--- /dev/null
+++ stand/i386/libi386/vbe.h
@@ -0,0 +1,156 @@
+/*-
+ * 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 Alignment;
+} __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 */
+
+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);
+
+/* 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);
Index: stand/i386/libi386/vbe.c
===================================================================
--- /dev/null
+++ stand/i386/libi386/vbe.c
@@ -0,0 +1,1167 @@
+/*-
+ * 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;
+/*
+ * 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);
+}
+
+/* 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;
+}
+
+/* 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;
+ }
+ 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 __unused, int flags __unused, const void *value)
+{
+ int mode;
+
+ env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
+ value, NULL, NULL);
+
+ 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("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.Alignment = entry->Alignment;
+
+ 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;
+
+ 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 = 0, i;
+
+ memset(vbe, 0, sizeof (*vbe));
+ if (biosvbe_info(vbe) != VBE_SUCCESS)
+ return (0);
+ farptr = vbe->VideoModePtr;
+ if (farptr == 0)
+ return (0);
+
+ if (m != -1)
+ i = 8;
+ else if (depth == -1)
+ i = 32;
+ else
+ i = depth;
+
+ while (i > 0) {
+ while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
+ safety++;
+ farptr += 2;
+ if (safety == 100)
+ return (0);
+ 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;
+ safety = 0;
+
+ 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 == 100) {
+ 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 >= 0x100) {
+ 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);
+}
Index: stand/i386/libi386/vidconsole.c
===================================================================
--- stand/i386/libi386/vidconsole.c
+++ 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,16 +51,22 @@
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;
static tf_bell_t vidc_cons_bell;
static tf_cursor_t vidc_text_cursor;
+static tf_cursor_t vidc_gfx_cursor;
static tf_putchar_t vidc_text_putchar;
+static tf_putchar_t vidc_gfx_putchar;
static tf_fill_t vidc_text_fill;
+static tf_fill_t vidc_gfx_fill;
static tf_copy_t vidc_text_copy;
+static tf_copy_t vidc_gfx_copy;
static tf_param_t vidc_text_param;
+static tf_param_t vidc_gfx_param;
static tf_respond_t vidc_cons_respond;
static teken_funcs_t tf = {
@@ -72,8 +79,15 @@
.tf_respond = vidc_cons_respond,
};
-teken_t teken;
-teken_pos_t tp;
+static teken_funcs_t tfx = {
+ .tf_bell = vidc_cons_bell,
+ .tf_cursor = vidc_gfx_cursor,
+ .tf_putchar = vidc_gfx_putchar,
+ .tf_fill = vidc_gfx_fill,
+ .tf_copy = vidc_gfx_copy,
+ .tf_param = vidc_gfx_param,
+ .tf_respond = vidc_cons_respond,
+};
struct text_pixel {
teken_char_t c;
@@ -82,20 +96,6 @@
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
-};
-
-#define TEXT_COLS 80
-#define TEXT_ROWS 25
#define KEYBUFSZ 10
static uint8_t keybuf[KEYBUFSZ]; /* keybuf for extended codes */
@@ -111,63 +111,29 @@
.c_ready = vidc_ischar
};
-static int
-vga_get_reg(int reg, int index)
+/*
+ * 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)
{
- return (inb(reg + index));
-}
+ teken_pos_t p;
+ int idx;
-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);
+ 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;
+ buffer[idx].a.ta_format |= TF_IMAGE;
+ }
+ }
}
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)
-{
- vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
-}
-
-static void
vidc_text_set_cursor(teken_unit_t row, teken_unit_t col, bool visible)
{
uint16_t addr;
@@ -228,6 +194,47 @@
vidc_text_set_cursor(row, col, true);
}
+static void
+vidc_gfx_cursor_draw(teken_gfx_t *state, const teken_pos_t *p)
+{
+ struct text_pixel *px;
+ uint32_t fg, bg;
+
+ px = buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
+ fg = teken_256to16(px->a.ta_fgcolor);
+ bg = teken_256to16(px->a.ta_bgcolor);
+ if (px->a.ta_format & TF_BOLD)
+ fg |= TC_LIGHT;
+ if (px->a.ta_format & TF_BLINK)
+ bg |= TC_LIGHT;
+
+ fg = cmap[fg];
+ bg = cmap[bg];
+
+ if (px->a.ta_format & TF_REVERSE) {
+ uint32_t tmp;
+
+ tmp = fg;
+ fg = bg;
+ bg = tmp;
+ }
+
+ state->tg_cursor = *p;
+ gfx_bm_cursor_draw(state, p, fg, bg);
+}
+
+static void
+vidc_gfx_cursor(void *arg, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+
+ /* Switch cursor off in old location and back on in new. */
+ if (state->tg_cursor_visible) {
+ vidc_gfx_cursor_draw(state, &state->tg_cursor);
+ vidc_gfx_cursor_draw(state, p);
+ }
+}
+
/*
* Binary searchable table for Unicode to CP437 conversion.
*/
@@ -353,9 +360,9 @@
}
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 +371,8 @@
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 = &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 +386,85 @@
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;
+
+ buffer[idx].c = c;
+ buffer[idx].a = *a;
+
+ vidc_text_printchar(state, p);
}
+/*
+ * Draw prepared glyph on terminal point p.
+ */
static void
-vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
+vidc_gfx_printchar(teken_gfx_t *state, const teken_pos_t *p)
+{
+ unsigned x, y, width, height;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+ x = state->tg_origin.tp_col + p->tp_col * width;
+ y = state->tg_origin.tp_row + p->tp_row * height;
+
+ gfx_bm_cons_display(x, y, width, height, state->tg_glyph);
+}
+
+/*
+ * Store char with its attribute to buffer and put it on screen.
+ */
+static void
+vidc_gfx_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ int idx;
+
+ 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;
+
+ buffer[idx].c = c;
+ buffer[idx].a = *a;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ vidc_gfx_cursor_draw(arg, &state->tg_cursor);
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, c, a, 0xff);
+ vidc_gfx_printchar(state, p);
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ vidc_gfx_cursor_draw(state, c);
+ }
+}
+
+static void
+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,16 +474,57 @@
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 void
+vidc_gfx_fill(void *arg, const teken_rect_t *r, teken_char_t c,
+ const teken_attr_t *a)
+{
+ teken_gfx_t *state = arg;
+ const uint8_t *glyph;
+ teken_pos_t p;
+ struct text_pixel *row;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ vidc_gfx_cursor_draw(state, &state->tg_cursor);
+
+ glyph = font_lookup(&state->tg_font, c, a);
+ gfx_bitblt_bitmap(state, glyph, c, a, 0xff);
+
+ for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
+ p.tp_row++) {
+ row = &buffer[p.tp_row * state->tg_tp.tp_col];
+ for (p.tp_col = r->tr_begin.tp_col;
+ p.tp_col < r->tr_end.tp_col; p.tp_col++) {
+ row[p.tp_col].c = c;
+ row[p.tp_col].a = *a;
+ vidc_gfx_printchar(state, &p);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ vidc_gfx_cursor_draw(state, c);
+ }
+}
+
static bool
vidc_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
{
if (px1->c != px2->c)
return (false);
+ /* Is there image stored? */
+ if ((px1->a.ta_format & TF_IMAGE) ||
+ (px2->a.ta_format & TF_IMAGE))
+ return (false);
+
if (px1->a.ta_format != px2->a.ta_format)
return (false);
if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
@@ -431,8 +536,9 @@
}
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,8 +559,8 @@
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;
@@ -464,7 +570,7 @@
&buffer[s.tp_col + srow])) {
buffer[d.tp_col + drow] =
buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ vidc_text_printchar(state, &d);
}
}
}
@@ -475,8 +581,8 @@
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;
@@ -486,7 +592,7 @@
&buffer[s.tp_col + srow])) {
buffer[d.tp_col + drow] =
buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ vidc_text_printchar(state, &d);
}
}
}
@@ -495,8 +601,8 @@
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;
@@ -506,7 +612,7 @@
&buffer[s.tp_col + srow])) {
buffer[d.tp_col + drow] =
buffer[s.tp_col + srow];
- vidc_text_printchar(&d);
+ vidc_text_printchar(state, &d);
}
}
}
@@ -516,8 +622,141 @@
}
static void
-vidc_text_param(void *s __unused, int cmd, unsigned int value)
+vidc_gfx_copy_area(teken_gfx_t *state, const teken_rect_t *s,
+ const teken_pos_t *d)
{
+ unsigned sx, sy, dx, dy, width, height;
+ unsigned bpp, pitch;
+ uint8_t *fb, *src, *dst;
+
+ width = state->tg_font.vf_width;
+ height = state->tg_font.vf_height;
+
+ sx = state->tg_origin.tp_col + s->tr_begin.tp_col * width;
+ sy = state->tg_origin.tp_row + s->tr_begin.tp_row * height;
+ dx = state->tg_origin.tp_col + d->tp_col * width;
+ dy = state->tg_origin.tp_row + d->tp_row * height;
+
+ width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
+
+ fb = ptov((uint32_t)gfx_state.tg_fb.fb_addr & 0xffffffff);
+ bpp = roundup2(state->tg_fb.fb_bpp, 8) >> 3;
+ pitch = state->tg_fb.fb_stride * bpp;
+
+ src = fb + sx * bpp + sy * pitch;
+ dst = fb + dx * bpp + dy * pitch;
+ width *= bpp;
+
+ for (unsigned i = 0; i < height; i++) {
+ unsigned increment = i * pitch;
+
+ (void) memmove(dst + increment, src + increment, width);
+ }
+}
+
+static void
+vidc_gfx_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
+{
+ struct text_pixel *src, *dst;
+ teken_rect_t sr;
+ teken_pos_t dp;
+ unsigned soffset, doffset;
+ bool mark = false;
+ int x;
+
+ soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
+ doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
+
+ for (x = 0; x < ncol; x++) {
+ if (vidc_same_pixel(&buffer[soffset + x],
+ &buffer[doffset + x])) {
+ if (mark) {
+ vidc_gfx_copy_area(state, &sr, &dp);
+ mark = false;
+ }
+ } else {
+ buffer[doffset + x] = buffer[soffset + x];
+ if (mark) {
+ /* update end point */
+ sr.tr_end.tp_col = s->tp_col + x;;
+ } else {
+ /* set up new rectangle */
+ mark = true;
+ sr.tr_begin.tp_col = s->tp_col + x;
+ sr.tr_begin.tp_row = s->tp_row;
+ sr.tr_end.tp_col = s->tp_col + x;
+ sr.tr_end.tp_row = s->tp_row;
+ dp.tp_col = d->tp_col + x;
+ dp.tp_row = d->tp_row;
+ }
+ }
+ }
+ if (mark) {
+ vidc_gfx_copy_area(state, &sr, &dp);
+ }
+}
+
+static void
+vidc_gfx_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
+{
+ teken_gfx_t *state = arg;
+ unsigned doffset, soffset;
+ teken_pos_t d, s;
+ int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
+
+ /*
+ * Copying is a little tricky. We must make sure we do it in
+ * correct order, to make sure we don't overwrite our own data.
+ */
+
+ nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
+ ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
+
+ if (p->tp_row + nrow > state->tg_tp.tp_row ||
+ p->tp_col + ncol > state->tg_tp.tp_col)
+ return;
+
+ soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
+ doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
+
+ /* remove the cursor */
+ if (state->tg_cursor_visible)
+ vidc_gfx_cursor_draw(state, &state->tg_cursor);
+
+ /*
+ * Copy line by line.
+ */
+ if (doffset <= soffset) {
+ s = r->tr_begin;
+ d = *p;
+ for (y = 0; y < nrow; y++) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ vidc_gfx_copy_line(state, ncol, &s, &d);
+ }
+ } else {
+ for (y = nrow - 1; y >= 0; y--) {
+ s.tp_row = r->tr_begin.tp_row + y;
+ d.tp_row = p->tp_row + y;
+
+ vidc_gfx_copy_line(state, ncol, &s, &d);
+ }
+ }
+
+ /* display the cursor */
+ if (state->tg_cursor_visible) {
+ const teken_pos_t *c;
+
+ c = teken_get_cursor(&state->tg_teken);
+ vidc_gfx_cursor_draw(state, c);
+ }
+}
+
+static void
+vidc_text_param(void *arg, int cmd, unsigned int value)
+{
+ teken_gfx_t *state = arg;
teken_unit_t row, col;
switch (cmd) {
@@ -532,10 +771,13 @@
/* 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 */
@@ -543,6 +785,36 @@
}
}
+static void
+vidc_gfx_param(void *arg, int cmd, unsigned int value)
+{
+ teken_gfx_t *state = arg;
+ const teken_pos_t *c;
+
+ switch (cmd) {
+ case TP_SETLOCALCURSOR:
+ /*
+ * 0 means normal (usually block), 1 means hidden, and
+ * 2 means blinking (always block) for compatibility with
+ * syscons. We don't support any changes except hiding,
+ * so must map 2 to 0.
+ */
+ value = (value == 1) ? 0 : 1;
+ /* FALLTHROUGH */
+ case TP_SHOWCURSOR:
+ c = teken_get_cursor(&state->tg_teken);
+ vidc_gfx_cursor_draw(state, c);
+ if (value != 0)
+ state->tg_cursor_visible = true;
+ else
+ state->tg_cursor_visible = false;
+ break;
+ default:
+ /* Not yet implemented */
+ break;
+ }
+}
+
/*
* Not implemented.
*/
@@ -635,7 +907,7 @@
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 +926,201 @@
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;
+ extern struct paletteentry *pe8;
+
+ if (pe8 == NULL)
+ pe8 = calloc(sizeof(*pe8), NCOLORS);
+ 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 < NCOLORS; i++) {
+ rc = vbe_set_palette(&pe8[cmap[i]], i);
+ 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);
+}
+
+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 *
+ (roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3);
+ 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), "%dx%d", gfx_state.tg_fb.fb_width,
+ gfx_state.tg_fb.fb_height);
+ env_setenv("kern.vt.fb.default_mode", EV_VOLATILE | EV_NOHOOK,
+ env, env_noset, env_screen_nounset);
+ 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");
+ }
+
+ free(buffer);
+ buffer = malloc(gfx_state.tg_tp.tp_row * gfx_state.tg_tp.tp_col *
+ sizeof(*buffer));
+ if (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 +1131,7 @@
return (0);
vidc_started = 1;
+ vbe_init();
/*
* Check Miscellaneous Output Register (Read at 3CCh, Write at 3C2h)
@@ -687,31 +1149,18 @@
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);
+ 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);
+ }
- snprintf(env, sizeof (env), "%u", tp.tp_row);
- setenv("LINES", env, 1);
- snprintf(env, sizeof (env), "%u", tp.tp_col);
- setenv("COLUMNS", env, 1);
+ gfx_framework_init();
- teken_init(&teken, &tf, NULL);
- teken_set_winsize(&teken, &tp);
- a = teken_get_defattr(&teken);
+ if (!cons_update_mode(VBE_VALID_MODE(vbe_get_mode())))
+ return (1);
- 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);
-
- /* Erase display, this will also fill our screen buffer. */
- teken_input(&teken, "\e[J", 3);
-
for (int i = 0; i < 10 && vidc_ischar(); i++)
(void) vidc_getchar();
@@ -735,7 +1184,7 @@
unsigned char ch = c;
if (buffer != NULL)
- teken_input(&teken, &ch, sizeof (ch));
+ teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
else
vidc_biosputchar(c);
}
@@ -806,29 +1255,36 @@
return (!V86_ZR(v86.efl));
}
+void
+gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ void *data)
+{
+ gfx_bm_cons_display(x, y, width, height, data);
+}
+
#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 +1310,7 @@
(void) inb(0x84);
}
-/*
+/*
* We use the presence/absence of a keyboard to determine whether the internal
* console can be used for input.
*
Index: stand/i386/loader/Makefile
===================================================================
--- stand/i386/loader/Makefile
+++ stand/i386/loader/Makefile
@@ -23,7 +23,7 @@
.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 8x16.c
# Include bcache code.
HAVE_BCACHE= yes
@@ -49,7 +49,7 @@
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
-CLEANFILES+= ${LOADER} ${LOADER}.bin
+CLEANFILES+= ${LOADER} ${LOADER}.bin 8x16.c
ORG= 0x0
@@ -63,6 +63,9 @@
# Debug me!
#CFLAGS+= -g
#LDFLAGS+= -g
+
+8x16.c: ${SRCTOP}/share/vt/fonts/vgarom-8x16.hex
+ vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC}
${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
Index: stand/i386/loader/main.c
===================================================================
--- stand/i386/loader/main.c
+++ stand/i386/loader/main.c
@@ -131,6 +131,12 @@
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 @@
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 @@
printf("\n%s", bootprog_info);
extract_currdev(); /* set $currdev and $loaddev */
+ autoload_font(true);
bios_getsmap();
Index: stand/images/Makefile
===================================================================
--- /dev/null
+++ stand/images/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+FILES+= freebsd-brand-rev.png freebsd-brand.png freebsd-logo-rev.png
+
+FILESDIR= /boot/images
+
+.include <bsd.prog.mk>
Index: stand/liblua/Makefile
===================================================================
--- stand/liblua/Makefile
+++ stand/liblua/Makefile
@@ -35,6 +35,8 @@
CFLAGS+= -fno-stack-protector -D__BSD_VISIBLE
CFLAGS+= -I${BOOTSRC}/include -I${LIBLUASRC} -I${LUASRC} -I${LDRSRC}
+CFLAGS.lutils.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
+
.if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0
CFLAGS+= -fPIC
.endif
Index: stand/liblua/lutils.c
===================================================================
--- stand/liblua/lutils.c
+++ stand/liblua/lutils.c
@@ -35,6 +35,8 @@
#include "lstd.h"
#include "lutils.h"
#include "bootstrap.h"
+#include <gfx_fb.h>
+#include <pnglite.h>
/*
* Like loader.perform, except args are passed already parsed
@@ -346,6 +348,189 @@
return 1;
}
+/*
+ * put image using terminal coordinates.
+ */
+static int
+lua_term_putimage(lua_State *L)
+{
+ const char *name;
+ png_t png;
+ uint32_t x1, y1, x2, y2, f;
+ int nargs, ret = 0, error;
+
+ nargs = lua_gettop(L);
+ if (nargs != 6) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+
+ name = luaL_checkstring(L, 1);
+ x1 = luaL_checknumber(L, 2);
+ y1 = luaL_checknumber(L, 3);
+ x2 = luaL_checknumber(L, 4);
+ y2 = luaL_checknumber(L, 5);
+ f = luaL_checknumber(L, 6);
+
+ x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width;
+ y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height;
+ if (x2 != 0) {
+ x2 = gfx_state.tg_origin.tp_col +
+ x2 * gfx_state.tg_font.vf_width;
+ }
+ if (y2 != 0) {
+ y2 = gfx_state.tg_origin.tp_row +
+ y2 * gfx_state.tg_font.vf_height;
+ }
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = 1;
+ (void) png_close(&png);
+ }
+ lua_pushboolean(L, ret);
+ return 1;
+}
+
+static int
+lua_fb_putimage(lua_State *L)
+{
+ const char *name;
+ png_t png;
+ uint32_t x1, y1, x2, y2, f;
+ int nargs, ret = 0, error;
+
+ nargs = lua_gettop(L);
+ if (nargs != 6) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+
+ name = luaL_checkstring(L, 1);
+ x1 = luaL_checknumber(L, 2);
+ y1 = luaL_checknumber(L, 3);
+ x2 = luaL_checknumber(L, 4);
+ y2 = luaL_checknumber(L, 5);
+ f = luaL_checknumber(L, 6);
+
+ if ((error = png_open(&png, name)) != PNG_NO_ERROR) {
+ if (f & FL_PUTIMAGE_DEBUG)
+ printf("%s\n", png_error_string(error));
+ } else {
+ if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0)
+ ret = 1;
+ (void) png_close(&png);
+ }
+ lua_pushboolean(L, ret);
+ return 1;
+}
+
+static int
+lua_fb_setpixel(lua_State *L)
+{
+ uint32_t x, y;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 2) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x = luaL_checknumber(L, 1);
+ y = luaL_checknumber(L, 2);
+ gfx_fb_setpixel(x, y);
+ return 0;
+}
+
+static int
+lua_fb_line(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, wd;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 5) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ wd = luaL_checknumber(L, 5);
+ gfx_fb_line(x0, y0, x1, y1, wd);
+ return 0;
+}
+
+static int
+lua_fb_bezier(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, x2, y2, width;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 7) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ x2 = luaL_checknumber(L, 5);
+ y2 = luaL_checknumber(L, 6);
+ width = luaL_checknumber(L, 7);
+ gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width);
+ return 0;
+}
+
+static int
+lua_fb_drawrect(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1, fill;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 5) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ fill = luaL_checknumber(L, 5);
+ gfx_fb_drawrect(x0, y0, x1, y1, fill);
+ return 0;
+}
+
+static int
+lua_term_drawrect(lua_State *L)
+{
+ uint32_t x0, y0, x1, y1;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs != 4) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ x0 = luaL_checknumber(L, 1);
+ y0 = luaL_checknumber(L, 2);
+ x1 = luaL_checknumber(L, 3);
+ y1 = luaL_checknumber(L, 4);
+ gfx_term_drawrect(x0, y0, x1, y1);
+ return 0;
+}
+
#define REG_SIMPLE(n) { #n, lua_ ## n }
static const struct luaL_Reg loaderlib[] = {
REG_SIMPLE(delay),
@@ -360,6 +545,13 @@
REG_SIMPLE(setenv),
REG_SIMPLE(time),
REG_SIMPLE(unsetenv),
+ REG_SIMPLE(fb_bezier),
+ REG_SIMPLE(fb_drawrect),
+ REG_SIMPLE(fb_line),
+ REG_SIMPLE(fb_putimage),
+ REG_SIMPLE(fb_setpixel),
+ REG_SIMPLE(term_drawrect),
+ REG_SIMPLE(term_putimage),
{ NULL, NULL },
};
Index: stand/libsa/printf.c
===================================================================
--- stand/libsa/printf.c
+++ stand/libsa/printf.c
@@ -61,6 +61,25 @@
static char *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
static int kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
+__weak_symbol void
+putchar_device(int c __unused, void *ptr __unused)
+{
+}
+
+int
+diag_printf(const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+ char *diag;
+
+ diag = getenv("diag-device");
+ va_start(ap, fmt);
+ retval = kvprintf(fmt, putchar_device, diag, 10, ap);
+ va_end(ap);
+ return retval;
+}
+
static void
putchar_wrapper(int cc, void *arg)
{
Index: stand/libsa/stand.h
===================================================================
--- stand/libsa/stand.h
+++ stand/libsa/stand.h
@@ -264,6 +264,7 @@
extern void setheap(void *base, void *top);
extern char *sbrk(int incr);
+extern int diag_printf(const char *fmt, ...) __printflike(1, 2);
extern int printf(const char *fmt, ...) __printflike(1, 2);
extern int asprintf(char **buf, const char *cfmt, ...) __printflike(2, 3);
extern int sprintf(char *buf, const char *cfmt, ...) __printflike(2, 3);
@@ -399,6 +400,7 @@
extern int getchar(void);
extern int ischar(void);
extern void putchar(int);
+extern void putchar_device(int, void *);
extern int devopen(struct open_file *, const char *, const char **);
extern int devclose(struct open_file *f);
extern void panic(const char *, ...) __dead2 __printflike(1, 2);
Index: stand/loader.mk
===================================================================
--- stand/loader.mk
+++ stand/loader.mk
@@ -8,9 +8,19 @@
SRCS+= interp_backslash.c interp_parse.c ls.c misc.c
SRCS+= module.c nvstore.c
+CFLAGS.module.c += -I$(SRCTOP)/sys/teken -I${SRCTOP}/contrib/pnglite
+
.if ${MACHINE} == "i386" || ${MACHINE_CPUARCH} == "amd64"
SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c
SRCS+= load_elf64.c load_elf64_obj.c reloc_elf64.c
+SRCS+= gfx_fb.c pnglite.c
+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
+.PATH: ${SRCTOP}/contrib/pnglite
+CFLAGS.pnglite.c+= -I${SRCTOP}/contrib/pnglite
+CFLAGS.pnglite.c+= -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
.elif ${MACHINE_CPUARCH} == "aarch64"
SRCS+= load_elf64.c reloc_elf64.c
.elif ${MACHINE_CPUARCH} == "arm"
Index: stand/lua/color.lua
===================================================================
--- stand/lua/color.lua
+++ stand/lua/color.lua
@@ -56,7 +56,7 @@
if c ~= nil then
return c:lower() ~= "no" and c ~= "0"
end
- return not core.isSerialBoot()
+ return true
end
function color.escapefg(color_value)
Index: stand/lua/core.lua
===================================================================
--- stand/lua/core.lua
+++ stand/lua/core.lua
@@ -383,6 +383,19 @@
return false
end
+function core.isFramebufferConsole()
+ local c = loader.getenv("console")
+ if c ~= nil then
+ if c:find("efi") == nil and c:find("vidconsole") == nil then
+ return false
+ end
+ if loader.getenv("screen-width") ~= nil then
+ return true
+ end
+ end
+ return false
+end
+
function core.isSerialConsole()
local c = loader.getenv("console")
if c ~= nil then
Index: stand/lua/drawer.lua
===================================================================
--- stand/lua/drawer.lua
+++ stand/lua/drawer.lua
@@ -202,7 +202,7 @@
return "double"
end
-local function drawbox()
+local function drawframe()
local x = menu_position.x - 3
local y = menu_position.y - 1
local w = frame_size.w
@@ -213,7 +213,7 @@
-- If we don't have a framespec for the current frame style, just don't
-- draw a box.
if framespec == nil then
- return
+ return false
end
local hl = framespec.horizontal
@@ -227,6 +227,11 @@
x = x + shift.x
y = y + shift.y
+ if core.isFramebufferConsole() and loader.term_drawrect ~= nil then
+ loader.term_drawrect(x, y, x + w, y + h)
+ return true
+ end
+
screen.setcursor(x, y); printc(tl)
screen.setcursor(x, y + h); printc(bl)
screen.setcursor(x + w, y); printc(tr)
@@ -248,12 +253,25 @@
screen.setcursor(x + w, y + i)
printc(vl)
end
+ return true
+end
+local function drawbox()
+ local x = menu_position.x - 3
+ local y = menu_position.y - 1
+ local w = frame_size.w
local menu_header = loader.getenv("loader_menu_title") or
"Welcome to FreeBSD"
local menu_header_align = loader.getenv("loader_menu_title_align")
local menu_header_x
+ x = x + shift.x
+ y = y + shift.y
+
+ if drawframe(x, y, w) == false then
+ return
+ end
+
if menu_header_align ~= nil then
menu_header_align = menu_header_align:lower()
if menu_header_align == "left" then
@@ -287,6 +305,14 @@
x = x + shift.x
y = y + shift.y
+ if core.isFramebufferConsole() and
+ loader.term_putimage ~= nil and
+ branddef.image ~= nil then
+ if loader.term_putimage(branddef.image, 0, 0, 0, 7, 0)
+ then
+ return true
+ end
+ end
draw(x, y, graphic)
end
@@ -330,9 +356,33 @@
y = y + logodef.shift.y
end
+ if core.isFramebufferConsole() and
+ loader.term_putimage ~= nil and
+ logodef.image ~= nil then
+ local y1 = 15
+
+ if logodef.image_rl ~= nil then
+ y1 = logodef.image_rl
+ end
+ if loader.term_putimage(logodef.image, x, y, 0, y + y1, 0)
+ then
+ return true
+ end
+ end
draw(x, y, logodef.graphic)
end
+local function drawitem(func)
+ local console = loader.getenv("console")
+ local c
+
+ for c in string.gmatch(console, "%w+") do
+ loader.setenv("console", c)
+ func()
+ end
+ loader.setenv("console", console)
+end
+
fbsd_brand = {
" ______ ____ _____ _____ ",
" | ____| | _ \\ / ____| __ \\ ",
@@ -378,6 +428,7 @@
-- keys are: graphic (table depicting graphic)
["fbsd"] = {
graphic = fbsd_brand,
+ image = "/boot/images/freebsd-brand-rev.png",
},
["none"] = {
graphic = none,
@@ -458,9 +509,9 @@
function drawer.drawscreen(menudef)
-- drawlogo() must go first.
-- it determines the positions of other elements
- drawlogo()
- drawbrand()
- drawbox()
+ drawitem(drawlogo)
+ drawitem(drawbrand)
+ drawitem(drawbox)
return drawmenu(menudef)
end
Index: stand/lua/gfx-orb.lua
===================================================================
--- stand/lua/gfx-orb.lua
+++ stand/lua/gfx-orb.lua
@@ -47,6 +47,8 @@
" .---.....----.\027[m",
},
requires_color = true,
- shift = {x = 2, y = 4},
+ shift = {x = 2, y = 3},
+ image = "/boot/images/freebsd-logo-rev.png",
+ image_rl = 15
}
}
Index: sys/sys/font.h
===================================================================
--- sys/sys/font.h
+++ sys/sys/font.h
@@ -91,9 +91,10 @@
} vt_font_bitmap_data_t;
typedef enum {
- FONT_AUTO,
- FONT_MANUAL,
- FONT_BOOT
+ FONT_AUTO, /* This font is loaded by software */
+ FONT_MANUAL, /* This font is loaded manually by user */
+ FONT_BUILTIN, /* This font was built in at compile time */
+ FONT_RELOAD /* This font is marked to be re-read from file */
} FONT_FLAGS;
struct fontlist {
Index: sys/teken/teken.h
===================================================================
--- sys/teken/teken.h
+++ sys/teken/teken.h
@@ -48,6 +48,7 @@
#define TF_BLINK 0x04 /* Blinking character. */
#define TF_REVERSE 0x08 /* Reverse rendered character. */
#define TF_CJK_RIGHT 0x10 /* Right-hand side of CJK character. */
+#define TF_IMAGE 0x20 /* This character space has image. */
typedef unsigned char teken_color_t;
#define TC_BLACK 0
#define TC_RED 1
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Oct 24, 12:58 AM (5 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24110618
Default Alt Text
D27420.id80147.diff (198 KB)
Attached To
Mode
D27420: loader: implement framebuffer console
Attached
Detach File
Event Timeline
Log In to Comment