From owner-dev-commits-src-all@freebsd.org Sat Jan 2 20:09:25 2021 Return-Path: Delivered-To: dev-commits-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id B267F4DDAAA; Sat, 2 Jan 2021 20:09:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4D7XzT4cQHz4tgY; Sat, 2 Jan 2021 20:09:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 906F31B4D2; Sat, 2 Jan 2021 20:09:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 102K9PoS075019; Sat, 2 Jan 2021 20:09:25 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 102K9PRl075016; Sat, 2 Jan 2021 20:09:25 GMT (envelope-from git) Date: Sat, 2 Jan 2021 20:09:25 GMT Message-Id: <202101022009.102K9PRl075016@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Toomas Soome Subject: git: 3630506b9dae - main - loader: implement framebuffer console MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: tsoome X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 3630506b9daec9167a89bc4525638ea45a00769e Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for all branches of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 02 Jan 2021 20:09:25 -0000 The branch main has been updated by tsoome: URL: https://cgit.FreeBSD.org/src/commit/?id=3630506b9daec9167a89bc4525638ea45a00769e commit 3630506b9daec9167a89bc4525638ea45a00769e Author: Toomas Soome AuthorDate: 2020-12-21 05:31:16 +0000 Commit: Toomas Soome CommitDate: 2021-01-02 19:41:36 +0000 loader: implement framebuffer console Draw console on efi. Add vbe framebuffer for BIOS loader (vbe off, vbe on, vbe list, vbe set xxx). autoload font (/boot/fonts) based on resolution and font size. Add command loadfont (set font by file) and variable screen.font (set font by size). Pass loaded font to kernel. Export variables: screen.height screen.width screen.depth Add gfx primitives to draw the screen and put png image on the screen. Rework menu draw to iterate list of consoles to enamble device specific output. Probably something else I forgot... Relnotes: yes Differential Revision: https://reviews.freebsd.org/D27420 --- stand/Makefile | 2 + stand/common/bootstrap.h | 5 + stand/common/gfx_fb.c | 2641 +++++++++++++++++++++++++++++++ stand/common/gfx_fb.h | 274 ++++ stand/common/module.c | 89 +- stand/defaults/loader.conf.5 | 4 +- stand/efi/include/efilib.h | 1 - stand/efi/libefi/Makefile | 2 +- stand/efi/libefi/efi_console.c | 484 ++++-- stand/efi/loader/Makefile | 14 +- stand/efi/loader/bootinfo.c | 17 +- stand/efi/loader/framebuffer.c | 15 +- stand/efi/loader/main.c | 3 +- stand/ficl.mk | 2 +- stand/ficl/Makefile | 2 + stand/ficl/loader.c | 185 +++ stand/fonts/INDEX.fonts | 66 + stand/fonts/Makefile | 81 + stand/forth/beastie.4th | 4 + stand/forth/brand-fbsd.4th | 12 + stand/forth/brand.4th | 4 + stand/forth/color.4th | 12 +- stand/forth/frames.4th | 14 + stand/forth/logo-orb.4th | 12 + stand/forth/menu.4th | 34 +- stand/forth/support.4th | 91 +- stand/i386/libi386/Makefile | 9 +- stand/i386/libi386/bootinfo.c | 11 + stand/i386/libi386/bootinfo32.c | 3 + stand/i386/libi386/bootinfo64.c | 3 + stand/i386/libi386/libi386.h | 1 + stand/i386/libi386/vbe.c | 1226 ++++++++++++++ stand/i386/libi386/vbe.h | 163 ++ stand/i386/libi386/vidconsole.c | 659 ++++++-- stand/i386/loader/Makefile | 13 +- stand/i386/loader/main.c | 10 +- stand/images/Makefile | 9 + stand/images/freebsd-brand-rev.png | Bin 0 -> 7709 bytes stand/images/freebsd-brand.png | Bin 0 -> 13788 bytes stand/images/freebsd-logo-rev.png | Bin 0 -> 43898 bytes stand/liblua/Makefile | 2 + stand/liblua/lutils.c | 192 +++ stand/loader.mk | 6 + stand/lua/color.lua | 2 +- stand/lua/core.lua | 13 + stand/lua/drawer.lua | 61 +- stand/lua/gfx-orb.lua | 4 +- stand/userboot/userboot/Makefile | 1 + stand/userboot/userboot/userboot_cons.c | 5 + sys/sys/font.h | 7 +- sys/teken/teken.h | 1 + 51 files changed, 6070 insertions(+), 401 deletions(-) diff --git a/stand/Makefile b/stand/Makefile index 46e0856e45fd..7b871d9164db 100644 --- a/stand/Makefile +++ b/stand/Makefile @@ -25,6 +25,8 @@ S.${MK_FORTH}+= forth S.${MK_LOADER_LUA}+= liblua S.${MK_LOADER_LUA}+= lua S.yes+= defaults +S.yes+= fonts +S.yes+= images S.yes+= man .if ${MK_FORTH} != "no" diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h index a0640c2bac2c..f671dba96e63 100644 --- a/stand/common/bootstrap.h +++ b/stand/common/bootstrap.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "readin.h" @@ -119,6 +120,8 @@ struct console }; extern struct console *consoles[]; void cons_probe(void); +bool cons_update_mode(bool); +void autoload_font(bool); /* * Plug-and-play enumerator/configurator interface. @@ -258,6 +261,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 /* Relocation types. */ diff --git a/stand/common/gfx_fb.c b/stand/common/gfx_fb.c new file mode 100644 index 000000000000..f73899c50525 --- /dev/null +++ b/stand/common/gfx_fb.c @@ -0,0 +1,2641 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(EFI) +#include +#include +#else +#include +#endif + +/* VGA text mode does use bold font. */ +#if !defined(VGA_8X16_FONT) +#define VGA_8X16_FONT "/boot/fonts/8x16v.fnt" +#endif +#if !defined(DEFAULT_8X16_FONT) +#define DEFAULT_8X16_FONT "/boot/fonts/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 }; + +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[NCMAP]; + +/* + * Between console's palette and VGA's one: + * - blue and red are swapped (1 <-> 4) + * - yellow and cyan are swapped (3 <-> 6) + */ +const int cons_to_vga_colors[NCOLORS] = { + 0, 4, 2, 6, 1, 5, 3, 7, + 8, 12, 10, 14, 9, 13, 11, 15 +}; + +static const int vga_to_cons_colors[NCOLORS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 +}; + +struct text_pixel *screen_buffer; +#if defined(EFI) +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer; +#else +static struct paletteentry *GlyphBuffer; +#endif +static size_t GlyphBufferSize; + +static bool insert_font(char *, FONT_FLAGS); +static int font_set(struct env_var *, int, const void *); +static void * allocate_glyphbuffer(uint32_t, uint32_t); +static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool); + +/* + * Initialize gfx framework. + */ +void +gfx_framework_init(void) +{ + /* + * Setup font list to have builtin font. + */ + (void) insert_font(NULL, FONT_BUILTIN); +} + +static uint8_t * +gfx_get_fb_address(void) +{ + return (ptov((uint32_t)gfx_state.tg_fb.fb_addr)); +} + +/* + * 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); +} + +static uint32_t +rgb_color_map(uint8_t index, uint32_t rmax, int roffset, + uint32_t gmax, int goffset, uint32_t bmax, int boffset) +{ + uint32_t color, code, gray, level; + + if (index < NCOLORS) { +#define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset) + return (CF(r, index) | CF(g, index) | CF(b, index)); +#undef CF + } + +#define CF(_f, _c) ((_f ## max & _c) << _f ## offset) + /* 6x6x6 color cube */ + if (index > 15 && index < 232) { + uint32_t red, green, blue; + + for (red = 0; red < 6; red++) { + for (green = 0; green < 6; green++) { + for (blue = 0; blue < 6; blue++) { + code = 16 + (red * 36) + + (green * 6) + blue; + if (code != index) + continue; + red = red ? (red * 40 + 55) : 0; + green = green ? (green * 40 + 55) : 0; + blue = blue ? (blue * 40 + 55) : 0; + color = CF(r, red); + color |= CF(g, green); + color |= CF(b, blue); + return (color); + } + } + } + } + + /* colors 232-255 are a grayscale ramp */ + for (gray = 0; gray < 24; gray++) { + level = (gray * 10) + 8; + code = 232 + gray; + if (code == index) + break; + } + return (CF(r, level) | CF(g, level) | CF(b, level)); +#undef CF +} + +/* + * Support for color mapping. + * For 8, 24 and 32 bit depth, use mask size 8. + * 15/16 bit depth needs to use mask size from mode, + * or we will lose color information from 32-bit to 15/16 bit translation. + */ +uint32_t +gfx_fb_color_map(uint8_t index) +{ + int rmask, gmask, bmask; + int roff, goff, boff, bpp; + + 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; + bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; + + if (bpp == 2) + rmask = gfx_state.tg_fb.fb_mask_red >> roff; + else + rmask = 0xff; + + if (bpp == 2) + gmask = gfx_state.tg_fb.fb_mask_green >> goff; + else + gmask = 0xff; + + if (bpp == 2) + bmask = gfx_state.tg_fb.fb_mask_blue >> boff; + else + bmask = 0xff; + + return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0)); +} + +/* Get indexed color */ +static uint8_t +rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b) +{ +#if !defined(EFI) + uint32_t color, best, dist, k; + int diff; + + color = 0; + best = NCMAP * NCMAP * NCMAP; + for (k = 0; k < NCMAP; k++) { + diff = r - pe8[k].Red; + dist = diff * diff; + diff = g - pe8[k].Green; + dist += diff * diff; + diff = b - pe8[k].Blue; + dist += diff * diff; + + if (dist == 0) + break; + if (dist < best) { + color = k; + best = dist; + } + } + if (k == NCMAP) + k = color; + return (k); +#else + (void) r; + (void) g; + (void) b; + return (0); +#endif +} + +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]; + for (; i < NCMAP; i++) + palette[i] = i; + break; + case COLOR_FORMAT_RGB: + for (i = 0; i < NCMAP; i++) + palette[i] = rgb_color_map(i, rmax, roffset, + gmax, goffset, bmax, boffset); + 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; +} + +/* Our GFX Block transfer toolkit. */ +static int gfxfb_blt_fill(void *BltBuffer, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; +#else + struct paletteentry *p; +#endif + uint32_t data, bpp, pitch, y, x; + int roff, goff, boff; + size_t size; + off_t off; + uint8_t *destination; + + if (BltBuffer == NULL) + return (EINVAL); + + if (DestinationY + Height > gfx_state.tg_fb.fb_height) + return (EINVAL); + + if (DestinationX + Width > gfx_state.tg_fb.fb_width) + return (EINVAL); + + if (Width == 0 || Height == 0) + return (EINVAL); + + p = BltBuffer; + 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; + + if (gfx_state.tg_fb.fb_bpp == 8) { + data = rgb_to_color_index(p->Red, p->Green, p->Blue); + } else { + data = (p->Red & + (gfx_state.tg_fb.fb_mask_red >> roff)) << roff; + data |= (p->Green & + (gfx_state.tg_fb.fb_mask_green >> goff)) << goff; + data |= (p->Blue & + (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff; + } + + bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; + pitch = gfx_state.tg_fb.fb_stride * bpp; + destination = gfx_get_fb_address(); + size = gfx_state.tg_fb.fb_size; + + for (y = DestinationY; y < Height + DestinationY; y++) { + off = y * pitch + DestinationX * bpp; + for (x = 0; x < Width; x++) { + switch (bpp) { + case 1: + gfx_mem_wr1(destination, size, off, + (data < NCOLORS) ? + cons_to_vga_colors[data] : data); + break; + case 2: + gfx_mem_wr2(destination, size, off, data); + break; + case 3: + gfx_mem_wr1(destination, size, off, + (data >> 16) & 0xff); + gfx_mem_wr1(destination, size, off + 1, + (data >> 8) & 0xff); + gfx_mem_wr1(destination, size, off + 2, + data & 0xff); + break; + case 4: + gfx_mem_wr4(destination, size, off, data); + break; + } + off += bpp; + } + } + + return (0); +} + +static int +gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height, uint32_t Delta) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; +#else + struct paletteentry *p; +#endif + uint32_t x, sy, dy; + uint32_t bpp, pitch, copybytes; + off_t off; + uint8_t *source, *destination, *buffer, *sb; + uint8_t rm, rp, gm, gp, bm, bp; + bool bgra; + + if (BltBuffer == NULL) + return (EINVAL); + + if (SourceY + Height > + gfx_state.tg_fb.fb_height) + return (EINVAL); + + if (SourceX + Width > gfx_state.tg_fb.fb_width) + return (EINVAL); + + if (Width == 0 || Height == 0) + return (EINVAL); + + if (Delta == 0) + Delta = Width * sizeof (*p); + + bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; + pitch = gfx_state.tg_fb.fb_stride * bpp; + + copybytes = Width * bpp; + + rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1; + gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1; + bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; + rm = gfx_state.tg_fb.fb_mask_red >> rp; + gm = gfx_state.tg_fb.fb_mask_green >> gp; + bm = gfx_state.tg_fb.fb_mask_blue >> bp; + + /* If FB pixel format is BGRA, we can use direct copy. */ + bgra = bpp == 4 && + ffs(rm) - 1 == 8 && rp == 16 && + ffs(gm) - 1 == 8 && gp == 8 && + ffs(bm) - 1 == 8 && bp == 0; + + if (bgra) { + buffer = NULL; + } else { + buffer = malloc(copybytes); + if (buffer == NULL) + return (ENOMEM); + } + + for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY; + sy++, dy++) { + off = sy * pitch + SourceX * bpp; + source = gfx_get_fb_address() + off; + + if (bgra) { + destination = (uint8_t *)BltBuffer + dy * Delta + + DestinationX * sizeof (*p); + } else { + destination = buffer; + } + + bcopy(source, destination, copybytes); + + if (!bgra) { + for (x = 0; x < Width; x++) { + uint32_t c = 0; + + p = (void *)((uint8_t *)BltBuffer + + dy * Delta + + (DestinationX + x) * sizeof (*p)); + sb = buffer + x * bpp; + switch (bpp) { + case 1: + c = *sb; + break; + case 2: + c = *(uint16_t *)sb; + break; + case 3: + c = sb[0] << 16 | sb[1] << 8 | sb[2]; + break; + case 4: + c = *(uint32_t *)sb; + break; + } + + if (bpp == 1) { + *(uint32_t *)p = gfx_fb_color_map( + (c < 16) ? + vga_to_cons_colors[c] : c); + } else { + p->Red = (c >> rp) & rm; + p->Green = (c >> gp) & gm; + p->Blue = (c >> bp) & bm; + p->Reserved = 0; + } + } + } + } + + free(buffer); + return (0); +} + +static int +gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height, uint32_t Delta) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; +#else + struct paletteentry *p; +#endif + uint32_t x, sy, dy; + uint32_t bpp, pitch, copybytes; + off_t off; + uint8_t *source, *destination, *buffer; + uint8_t rm, rp, gm, gp, bm, bp; + bool bgra; + + if (BltBuffer == NULL) + return (EINVAL); + + if (DestinationY + Height > + gfx_state.tg_fb.fb_height) + return (EINVAL); + + if (DestinationX + Width > gfx_state.tg_fb.fb_width) + return (EINVAL); + + if (Width == 0 || Height == 0) + return (EINVAL); + + if (Delta == 0) + Delta = Width * sizeof (*p); + + bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; + pitch = gfx_state.tg_fb.fb_stride * bpp; + + copybytes = Width * bpp; + + rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1; + gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1; + bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; + rm = gfx_state.tg_fb.fb_mask_red >> rp; + gm = gfx_state.tg_fb.fb_mask_green >> gp; + bm = gfx_state.tg_fb.fb_mask_blue >> bp; + + /* If FB pixel format is BGRA, we can use direct copy. */ + bgra = bpp == 4 && + ffs(rm) - 1 == 8 && rp == 16 && + ffs(gm) - 1 == 8 && gp == 8 && + ffs(bm) - 1 == 8 && bp == 0; + + if (bgra) { + buffer = NULL; + } else { + buffer = malloc(copybytes); + if (buffer == NULL) + return (ENOMEM); + } + for (sy = SourceY, dy = DestinationY; sy < Height + SourceY; + sy++, dy++) { + off = dy * pitch + DestinationX * bpp; + destination = gfx_get_fb_address() + off; + + if (bgra) { + source = (uint8_t *)BltBuffer + sy * Delta + + SourceX * sizeof (*p); + } else { + for (x = 0; x < Width; x++) { + uint32_t c; + + p = (void *)((uint8_t *)BltBuffer + + sy * Delta + + (SourceX + x) * sizeof (*p)); + if (bpp == 1) { + c = rgb_to_color_index(p->Red, + p->Green, p->Blue); + } else { + c = (p->Red & rm) << rp | + (p->Green & gm) << gp | + (p->Blue & bm) << bp; + } + off = x * bpp; + switch (bpp) { + case 1: + gfx_mem_wr1(buffer, copybytes, + off, (c < 16) ? + cons_to_vga_colors[c] : c); + break; + case 2: + gfx_mem_wr2(buffer, copybytes, + off, c); + break; + case 3: + gfx_mem_wr1(buffer, copybytes, + off, (c >> 16) & 0xff); + gfx_mem_wr1(buffer, copybytes, + off + 1, (c >> 8) & 0xff); + gfx_mem_wr1(buffer, copybytes, + off + 2, c & 0xff); + break; + case 4: + gfx_mem_wr4(buffer, copybytes, + x * bpp, c); + break; + } + } + source = buffer; + } + + bcopy(source, destination, copybytes); + } + + free(buffer); + return (0); +} + +static int +gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height) +{ + uint32_t bpp, copybytes; + int pitch; + uint8_t *source, *destination; + off_t off; + + if (SourceY + Height > + gfx_state.tg_fb.fb_height) + return (EINVAL); + + if (SourceX + Width > gfx_state.tg_fb.fb_width) + return (EINVAL); + + if (DestinationY + Height > + gfx_state.tg_fb.fb_height) + return (EINVAL); + + if (DestinationX + Width > gfx_state.tg_fb.fb_width) + return (EINVAL); + + if (Width == 0 || Height == 0) + return (EINVAL); + + bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; + pitch = gfx_state.tg_fb.fb_stride * bpp; + + copybytes = Width * bpp; + + off = SourceY * pitch + SourceX * bpp; + source = gfx_get_fb_address() + off; + off = DestinationY * pitch + DestinationX * bpp; + destination = gfx_get_fb_address() + off; + + if ((uintptr_t)destination > (uintptr_t)source) { + source += Height * pitch; + destination += Height * pitch; + pitch = -pitch; + } + + while (Height-- > 0) { + bcopy(source, destination, copybytes); + source += pitch; + destination += pitch; + } + + return (0); +} + +int +gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation, + uint32_t SourceX, uint32_t SourceY, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height, uint32_t Delta) +{ + int rv; +#if defined(EFI) + EFI_STATUS status; + EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private; + + if (gop != NULL && (gop->Mode->Info->PixelFormat == PixelBltOnly || + gfx_state.tg_fb.fb_addr == 0)) { + switch (BltOperation) { + case GfxFbBltVideoFill: + status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, + SourceX, SourceY, DestinationX, DestinationY, + Width, Height, Delta); + break; + + case GfxFbBltVideoToBltBuffer: + status = gop->Blt(gop, BltBuffer, + EfiBltVideoToBltBuffer, + SourceX, SourceY, DestinationX, DestinationY, + Width, Height, Delta); + break; + + case GfxFbBltBufferToVideo: + status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo, + SourceX, SourceY, DestinationX, DestinationY, + Width, Height, Delta); + break; + + case GfxFbBltVideoToVideo: + status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo, + SourceX, SourceY, DestinationX, DestinationY, + Width, Height, Delta); + break; + + default: + status = EFI_INVALID_PARAMETER; + break; + } + + switch (status) { + case EFI_SUCCESS: + rv = 0; + break; + + case EFI_INVALID_PARAMETER: + rv = EINVAL; + break; + + case EFI_DEVICE_ERROR: + default: + rv = EIO; + break; + } + + return (rv); + } +#endif + + switch (BltOperation) { + case GfxFbBltVideoFill: + rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY, + Width, Height); + break; + + case GfxFbBltVideoToBltBuffer: + rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY, + DestinationX, DestinationY, Width, Height, Delta); + break; + + case GfxFbBltBufferToVideo: + rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY, + DestinationX, DestinationY, Width, Height, Delta); + break; + + case GfxFbBltVideoToVideo: + rv = gfxfb_blt_video_to_video(SourceX, SourceY, + DestinationX, DestinationY, Width, Height); + break; + + default: + rv = EINVAL; + break; + } + return (rv); +} + +void +gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph, + const teken_attr_t *a, uint32_t alpha, bool cursor) +{ + uint32_t width, height; + uint32_t fgc, bgc, bpl, cc, o; + int bpp, bit, byte; + bool invert = false; + + bpp = 4; /* We only generate BGRA */ + width = state->tg_font.vf_width; + height = state->tg_font.vf_height; + bpl = (width + 7) / 8; /* Bytes per source line. */ + + fgc = a->ta_fgcolor; + bgc = a->ta_bgcolor; + if (a->ta_format & TF_BOLD) + fgc |= TC_LIGHT; + if (a->ta_format & TF_BLINK) + bgc |= TC_LIGHT; + + fgc = gfx_fb_color_map(fgc); + bgc = gfx_fb_color_map(bgc); + + if (a->ta_format & TF_REVERSE) + invert = !invert; + if (cursor) + invert = !invert; + if (invert) { + uint32_t tmp; + + tmp = fgc; + fgc = bgc; + bgc = tmp; + } + + alpha = alpha << 24; + 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; + + gfx_mem_wr4(state->tg_glyph, + state->tg_glyph_size, o, cc); + } + } +} + +/* + * Draw prepared glyph on terminal point p. + */ +static void +gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p) +{ + unsigned x, y, width, height; *** 6743 LINES SKIPPED ***