diff --git a/stand/Makefile b/stand/Makefile index 46e0856e45fd..7b871d9164db 100644 --- a/stand/Makefile +++ b/stand/Makefile @@ -1,83 +1,85 @@ # $FreeBSD$ .include <src.opts.mk> # For amd64 we have to build 32 and 64 bit versions of things. For # others we don't. LIB32LIST is a list of libraries, which if # included, need to be built 32-bit as well. .if ${MACHINE_ARCH} == "amd64" LIB32LIST=libsa .if ${MK_FORTH} != "no" LIB32LIST+= ficl .endif .if ${MK_LOADER_LUA} != "no" LIB32LIST+= liblua .endif .endif S.yes+= libsa S.${MK_LOADER_OFW}+= libofw S.${MK_FDT}+= fdt S.${MK_FORTH}+= ficl 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" INTERP_DEPENDS+= forth .endif .if ${MK_LOADER_LUA} != "no" INTERP_DEPENDS+= lua .endif .include <bsd.arch.inc.mk> S.${MK_EFI}+= efi S.${MK_LOADER_UBOOT}+= uboot .if defined(LIB32LIST) LIB32DEPENDS= ${LIB32LIST:S/$/32/} .endif .if exists(${.CURDIR}/${MACHINE}/.) S.yes+= ${MACHINE} SUBDIR_DEPEND_${MACHINE}+= ${INTERP_DEPENDS} .if ${MK_FDT} != "no" SUBDIR_DEPEND_${MACHINE}+= fdt .endif .if ${MK_LOADER_UBOOT} != "no" SUBDIR_DEPEND_${MACHINE}+= uboot .endif .if ${MK_LOADER_OFW} != "no" SUBDIR_DEPEND_${MACHINE}+= libofw .endif .endif # Build the actual subdir list from S.yes, adding in the 32-bit # variant if necessary. .for _x in ${S.yes} SUBDIR+=${_x} .if defined(LIB32LIST) && ${LIB32LIST:M${_x}} SUBDIR+=${_x}32 .endif .if ${_x} != "libsa" SUBDIR_DEPEND_${_x}+= libsa SUBDIR_DEPEND_${_x}32+= libsa32 .endif .endfor # Remaining dependencies SUBDIR_DEPEND_libsa32+= libsa SUBDIR_DEPEND_forth+= ficl SUBDIR_DEPEND_lua+= liblua SUBDIR_DEPEND_efi+= fdt SUBDIR_PARALLEL= yes .include <bsd.subdir.mk> 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 @@ -1,396 +1,401 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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 _BOOTSTRAP_H_ #define _BOOTSTRAP_H_ #include <sys/types.h> #include <sys/queue.h> #include <sys/linker_set.h> +#include <stdbool.h> #include "readin.h" /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); #define COMMAND_ERRBUFSZ (256) extern const char *command_errmsg; extern char command_errbuf[COMMAND_ERRBUFSZ]; #define CMD_OK 0 #define CMD_WARN 1 #define CMD_ERROR 2 #define CMD_CRIT 3 #define CMD_FATAL 4 /* interp.c */ void interact(void); void interp_emit_prompt(void); int interp_builtin_cmd(int argc, char *argv[]); /* Called by interp.c for interp_*.c embedded interpreters */ int interp_include(const char *); /* Execute commands from filename */ void interp_init(void); /* Initialize interpreater */ int interp_run(const char *); /* Run a single command */ /* interp_backslash.c */ char *backslash(const char *str); /* interp_parse.c */ int parse(int *argc, char ***argv, const char *str); /* boot.c */ void autoboot_maybe(void); int getrootmount(char *rootdev); /* misc.c */ char *unargv(int argc, char *argv[]); size_t strlenout(vm_offset_t str); char *strdupout(vm_offset_t str); void kern_bzero(vm_offset_t dest, size_t len); int kern_pread(readin_handle_t fd, vm_offset_t dest, size_t len, off_t off); void *alloc_pread(readin_handle_t fd, off_t off, size_t len); /* bcache.c */ void bcache_init(size_t nblks, size_t bsize); void bcache_add_dev(int); void *bcache_allocate(void); void bcache_free(void *); int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *, int, daddr_t, size_t, char *, size_t *); void *dv_devdata; void *dv_cache; }; /* * Modular console support. */ struct console { const char *c_name; const char *c_desc; int c_flags; #define C_PRESENTIN (1<<0) /* console can provide input */ #define C_PRESENTOUT (1<<1) /* console can provide output */ #define C_ACTIVEIN (1<<2) /* user wants input from console */ #define C_ACTIVEOUT (1<<3) /* user wants output to console */ #define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */ /* set c_flags to match hardware */ void (* c_probe)(struct console *cp); /* reinit XXX may need more args */ int (* c_init)(int arg); /* emit c */ void (* c_out)(int c); /* wait for and return input */ int (* c_in)(void); /* return nonzero if input waiting */ int (* c_ready)(void); }; extern struct console *consoles[]; void cons_probe(void); +bool cons_update_mode(bool); +void autoload_font(bool); /* * Plug-and-play enumerator/configurator interface. */ struct pnphandler { const char *pp_name; /* handler/bus name */ void (*pp_enumerate)(void); /* enumerate PnP devices, add to chain */ }; struct pnpident { /* ASCII identifier, actual format varies with bus/handler */ char *id_ident; STAILQ_ENTRY(pnpident) id_link; }; struct pnpinfo { char *pi_desc; /* ASCII description, optional */ int pi_revision; /* optional revision (or -1) if not supported */ char *pi_module; /* module/args nominated to handle device */ int pi_argc; /* module arguments */ char **pi_argv; struct pnphandler *pi_handler; /* handler which detected this device */ STAILQ_HEAD(, pnpident) pi_ident; /* list of identifiers */ STAILQ_ENTRY(pnpinfo) pi_link; }; STAILQ_HEAD(pnpinfo_stql, pnpinfo); extern struct pnphandler *pnphandlers[]; /* provided by MD code */ void pnp_addident(struct pnpinfo *pi, char *ident); struct pnpinfo *pnp_allocinfo(void); void pnp_freeinfo(struct pnpinfo *pi); void pnp_addinfo(struct pnpinfo *pi); char *pnp_eisaformat(uint8_t *data); /* * < 0 - No ISA in system * == 0 - Maybe ISA, search for read data port * > 0 - ISA in system, value is read data port address */ extern int isapnp_readport; /* * Version information */ extern char bootprog_info[]; /* * Interpreter information */ extern const char bootprog_interp[]; #define INTERP_DEFINE(interpstr) \ const char bootprog_interp[] = "$Interpreter:" interpstr /* * Preloaded file metadata header. * * Metadata are allocated on our heap, and copied into kernel space * before executing the kernel. */ struct file_metadata { size_t md_size; uint16_t md_type; struct file_metadata *md_next; char md_data[1]; /* data are immediately appended */ }; struct preloaded_file; struct mod_depend; struct kernel_module { char *m_name; /* module name */ int m_version; /* module version */ /* char *m_args; */ /* arguments for the module */ struct preloaded_file *m_fp; struct kernel_module *m_next; }; /* * Preloaded file information. Depending on type, file can contain * additional units called 'modules'. * * At least one file (the kernel) must be loaded in order to boot. * The kernel is always loaded first. * * String fields (m_name, m_type) should be dynamically allocated. */ struct preloaded_file { char *f_name; /* file name */ char *f_type; /* verbose file type, eg 'ELF kernel', 'pnptable', etc. */ char *f_args; /* arguments for the file */ /* metadata that will be placed in the module directory */ struct file_metadata *f_metadata; int f_loader; /* index of the loader that read the file */ vm_offset_t f_addr; /* load address */ size_t f_size; /* file size */ struct kernel_module *f_modules; /* list of modules if any */ struct preloaded_file *f_next; /* next file */ }; struct file_format { /* * Load function must return EFTYPE if it can't handle * the module supplied */ int (*l_load)(char *, uint64_t, struct preloaded_file **); /* * Only a loader that will load a kernel (first module) * should have an exec handler */ int (*l_exec)(struct preloaded_file *); }; extern struct file_format *file_formats[]; /* supplied by consumer */ extern struct preloaded_file *preloaded_files; int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]); int mod_loadkld(const char *name, int argc, char *argv[]); void unload(void); struct preloaded_file *file_alloc(void); struct preloaded_file *file_findfile(const char *name, const char *type); struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); struct preloaded_file *file_loadraw(const char *name, char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *, int, size_t, void *); 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. */ #define ELF_RELOC_REL 1 #define ELF_RELOC_RELA 2 /* Relocation offset for some architectures */ extern uint64_t __elfN(relocation_offset); struct elf_file; typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *, uint64_t, struct preloaded_file **); int __elfN(obj_loadfile)(char *, uint64_t, struct preloaded_file **); int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len); int __elfN(loadfile_raw)(char *, uint64_t, struct preloaded_file **, int); int __elfN(load_modmetadata)(struct preloaded_file *, uint64_t); #endif /* * Support for commands */ struct bootblk_command { const char *c_name; const char *c_desc; bootblk_cmd_t *c_fn; }; #define COMMAND_SET(tag, key, desc, func) \ static bootblk_cmd_t func; \ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ DATA_SET(Xcommand_set, _cmd_ ## tag) SET_DECLARE(Xcommand_set, struct bootblk_command); /* * The intention of the architecture switch is to provide a convenient * encapsulation of the interface between the bootstrap MI and MD code. * MD code may selectively populate the switch at runtime based on the * actual configuration of the target system. */ struct arch_switch { /* Automatically load modules as required by detected hardware */ int (*arch_autoload)(void); /* Locate the device for (name), return pointer to tail in (*path) */ int (*arch_getdev)(void **dev, const char *name, const char **path); /* * Copy from local address space to module address space, * similar to bcopy() */ ssize_t (*arch_copyin)(const void *, vm_offset_t, const size_t); /* * Copy to local address space from module address space, * similar to bcopy() */ ssize_t (*arch_copyout)(const vm_offset_t, void *, const size_t); /* Read from file to module address space, same semantics as read() */ ssize_t (*arch_readin)(readin_handle_t, vm_offset_t, const size_t); /* Perform ISA byte port I/O (only for systems with ISA) */ int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); /* * Interface to adjust the load address according to the "object" * being loaded. */ uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); #define LOAD_ELF 1 /* data points to the ELF header. */ #define LOAD_RAW 2 /* data points to the file name. */ /* * Interface to inform MD code about a loaded (ELF) segment. This * can be used to flush caches and/or set up translations. */ #ifdef __elfN void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta); #else void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); #endif /* Probe ZFS pool(s), if needed. */ void (*arch_zfs_probe)(void); /* Return the hypervisor name/type or NULL if not virtualized. */ const char *(*arch_hypervisor)(void); /* For kexec-type loaders, get ksegment structure */ void (*arch_kexec_kseg_get)(int *nseg, void **kseg); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ void delay(int delay); void dev_cleanup(void); /* * nvstore API. */ typedef int (nvstore_getter_cb_t)(void *, const char *, void **); typedef int (nvstore_setter_cb_t)(void *, int, const char *, const void *, size_t); typedef int (nvstore_setter_str_cb_t)(void *, const char *, const char *, const char *); typedef int (nvstore_unset_cb_t)(void *, const char *); typedef int (nvstore_print_cb_t)(void *, void *); typedef int (nvstore_iterate_cb_t)(void *, int (*)(void *, void *)); typedef struct nvs_callbacks { nvstore_getter_cb_t *nvs_getter; nvstore_setter_cb_t *nvs_setter; nvstore_setter_str_cb_t *nvs_setter_str; nvstore_unset_cb_t *nvs_unset; nvstore_print_cb_t *nvs_print; nvstore_iterate_cb_t *nvs_iterate; } nvs_callbacks_t; int nvstore_init(const char *, nvs_callbacks_t *, void *); int nvstore_fini(const char *); void *nvstore_get_store(const char *); int nvstore_print(void *); int nvstore_get_var(void *, const char *, void **); int nvstore_set_var(void *, int, const char *, void *, size_t); int nvstore_set_var_from_string(void *, const char *, const char *, const char *); int nvstore_unset_var(void *, const char *); #ifndef CTASSERT #define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") #endif #endif /* !_BOOTSTRAP_H_ */ 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 <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> +#if defined(EFI) +#include <efi.h> +#include <efilib.h> +#else +#include <vbe.h> +#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; + + 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. + */ +void +gfx_fb_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; + + /* remove the cursor */ + if (state->tg_cursor_visible) + gfx_fb_cursor_draw(state, &state->tg_cursor, false); + + screen_buffer[idx].c = c; + screen_buffer[idx].a = *a; + + glyph = font_lookup(&state->tg_font, c, a); + gfx_bitblt_bitmap(state, glyph, a, 0xff, false); + gfx_fb_printchar(state, p); + + /* display the cursor */ + if (state->tg_cursor_visible) { + const teken_pos_t *c; + + c = teken_get_cursor(&state->tg_teken); + gfx_fb_cursor_draw(state, c, true); + } +} + +void +gfx_fb_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) + gfx_fb_cursor_draw(state, &state->tg_cursor, false); + + glyph = font_lookup(&state->tg_font, c, a); + gfx_bitblt_bitmap(state, glyph, a, 0xff, false); + + for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; + p.tp_row++) { + row = &screen_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; + gfx_fb_printchar(state, &p); + } + } + + /* display the cursor */ + if (state->tg_cursor_visible) { + const teken_pos_t *c; + + c = teken_get_cursor(&state->tg_teken); + gfx_fb_cursor_draw(state, c, true); + } +} + +static void +gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *p, bool on) +{ + 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; + + glyph = font_lookup(&state->tg_font, screen_buffer[idx].c, + &screen_buffer[idx].a); + gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on); + gfx_fb_printchar(state, p); + state->tg_cursor = *p; +} + +void +gfx_fb_cursor(void *arg, const teken_pos_t *p) +{ + teken_gfx_t *state = arg; +#if defined(EFI) + EFI_TPL tpl; + + tpl = BS->RaiseTPL(TPL_NOTIFY); +#endif + + /* Switch cursor off in old location and back on in new. */ + if (state->tg_cursor_visible) { + gfx_fb_cursor_draw(state, &state->tg_cursor, false); + gfx_fb_cursor_draw(state, p, true); + } +#if defined(EFI) + BS->RestoreTPL(tpl); +#endif +} + +void +gfx_fb_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); + gfx_fb_cursor_draw(state, c, true); + if (value != 0) + state->tg_cursor_visible = true; + else + state->tg_cursor_visible = false; + break; + default: + /* Not yet implemented */ + break; + } +} + +bool +is_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) + return (false); + if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) + return (false); + + return (true); +} + +static void +gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s, + const teken_pos_t *d) +{ + uint32_t sx, sy, dx, dy, width, height; + + 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); + + (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, sx, sy, dx, dy, + width, height, 0); +} + +static void +gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d) +{ + 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 (is_same_pixel(&screen_buffer[soffset + x], + &screen_buffer[doffset + x])) { + if (mark) { + gfx_fb_copy_area(state, &sr, &dp); + mark = false; + } + } else { + screen_buffer[doffset + x] = screen_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) { + gfx_fb_copy_area(state, &sr, &dp); + } +} + +void +gfx_fb_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, 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) + gfx_fb_cursor_draw(state, &state->tg_cursor, 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; + + gfx_fb_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; + + gfx_fb_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); + gfx_fb_cursor_draw(state, c, true); + } +} + +/* + * 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); +} + +/* + * 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 void +bitmap_cpy(void *dst, void *src, uint32_t size) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd; +#else + struct paletteentry *ps, *pd; +#endif + uint32_t i; + uint8_t a; + + ps = src; + pd = dst; + + /* + * we only implement alpha blending for depth 32. + */ + for (i = 0; i < size; i ++) { + a = ps[i].Reserved; + pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a); + pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a); + pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a); + pd[i].Reserved = a; + } +} + +static void * +allocate_glyphbuffer(uint32_t width, uint32_t height) +{ + size_t size; + + size = sizeof (*GlyphBuffer) * width * height; + if (size != GlyphBufferSize) { + free(GlyphBuffer); + GlyphBuffer = malloc(size); + if (GlyphBuffer == NULL) + return (NULL); + GlyphBufferSize = size; + } + return (GlyphBuffer); +} + +void +gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height, + void *data) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf; +#else + struct paletteentry *buf; +#endif + size_t size; + + size = width * height * sizeof(*buf); + + /* + * Common data to display is glyph, use preallocated + * glyph buffer. + */ + if (gfx_state.tg_glyph_size != GlyphBufferSize) + (void) allocate_glyphbuffer(width, height); + + if (size == GlyphBufferSize) + buf = GlyphBuffer; + else + buf = malloc(size); + if (buf == NULL) + return; + + if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0, + width, height, 0) == 0) { + bitmap_cpy(buf, data, width * height); + (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y, + width, height, 0); + } + if (buf != GlyphBuffer) + free(buf); +} + +/* + * 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_curattr(&gfx_state.tg_teken); + if (ap->ta_format & TF_REVERSE) { + c = ap->ta_bgcolor; + if (ap->ta_format & TF_BLINK) + c |= TC_LIGHT; + } else { + c = ap->ta_fgcolor; + if (ap->ta_format & TF_BOLD) + c |= TC_LIGHT; + } + + c = gfx_fb_color_map(c); + + if (x >= gfx_state.tg_fb.fb_width || + y >= gfx_state.tg_fb.fb_height) + return; + + gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0); +} + +/* + * 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) +{ +#if defined(EFI) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; +#else + struct paletteentry *p; +#endif + uint8_t *data; + uint32_t i, j, x, y, fheight, fwidth; + int rs, gs, bs; + uint8_t r, g, b, a; + 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); + + data = malloc(fwidth * fheight * sizeof(*p)); + p = (void *)data; + 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); + + rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) - + ffs(gfx_state.tg_fb.fb_mask_red) + 1); + gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) - + ffs(gfx_state.tg_fb.fb_mask_green) + 1); + bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) - + ffs(gfx_state.tg_fb.fb_mask_blue) + 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; + + 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); + /* + * Rough colorspace reduction for 15/16 bit colors. + */ + p[j].Red = r >> rs; + p[j].Green = g >> gs; + p[j].Blue = b >> bs; + p[j].Reserved = a; + + 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); + } + + /* + * 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)); +} diff --git a/stand/common/gfx_fb.h b/stand/common/gfx_fb.h new file mode 100644 index 000000000000..1424b8223136 --- /dev/null +++ b/stand/common/gfx_fb.h @@ -0,0 +1,274 @@ +/*- + * 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 +#define NCMAP 256 +extern uint32_t cmap[NCMAP]; + +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; + +typedef enum { + GfxFbBltVideoFill, + GfxFbBltVideoToBltBuffer, + GfxFbBltBufferToVideo, + GfxFbBltVideoToVideo, + GfxFbBltOperationMax, +} GFXFB_BLT_OPERATION; + +int gfxfb_blt(void *, GFXFB_BLT_OPERATION, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); + +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 bios_text_font(bool); + +/* teken callbacks. */ +tf_cursor_t gfx_fb_cursor; +tf_putchar_t gfx_fb_putchar; +tf_fill_t gfx_fb_fill; +tf_copy_t gfx_fb_copy; +tf_param_t gfx_fb_param; + +/* Screen buffer element */ +struct text_pixel { + teken_char_t c; + teken_attr_t a; +}; + +extern const int cons_to_vga_colors[NCOLORS]; + +/* Screen buffer to track changes on the terminal screen. */ +extern struct text_pixel *screen_buffer; +bool is_same_pixel(struct text_pixel *, struct text_pixel *); + +bool gfx_get_edid_resolution(struct vesa_edid_info *, edid_res_list_t *); +void gfx_framework_init(void); +void gfx_fb_cons_display(uint32_t, uint32_t, uint32_t, uint32_t, void *); +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 reset_font_flags(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _GFX_FB_H */ diff --git a/stand/common/module.c b/stand/common/module.c index bd8d091d91c9..247fc54b6021 100644 --- a/stand/common/module.c +++ b/stand/common/module.c @@ -1,1683 +1,1770 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); /* * file/module function dispatcher, support, etc. */ #include <stand.h> #include <string.h> #include <sys/param.h> #include <sys/linker.h> #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> #endif #include "bootstrap.h" #define MDIR_REMOVED 0x0001 #define MDIR_NOHINTS 0x0002 struct moduledir { char *d_path; /* path of modules directory */ u_char *d_hints; /* content of linker.hints file */ int d_hintsz; /* size of hints data */ int d_flags; STAILQ_ENTRY(moduledir) d_link; }; static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); static int file_load_dependencies(struct preloaded_file *base_mod); static char * file_search(const char *name, char **extlist); static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); static int file_havepath(const char *name); static char *mod_searchmodule(char *name, struct mod_depend *verinfo); static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo); static void file_insert_tail(struct preloaded_file *mp); static void file_remove(struct preloaded_file *fp); struct file_metadata* metadata_next(struct file_metadata *base_mp, int type); static void moduledir_readhints(struct moduledir *mdp); static void moduledir_rebuild(void); /* load address should be tweaked by first module loaded (kernel) */ static vm_offset_t loadaddr = 0; #if defined(LOADER_FDT_SUPPORT) static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb"; #else static const char *default_searchpath = "/boot/kernel;/boot/modules"; #endif static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); struct preloaded_file *preloaded_files = NULL; static char *kld_ext_list[] = { ".ko", "", ".debug", NULL }; - /* * load an object, either a disk file or code module. * * To load a file, the syntax is: * * load -t <type> <path> * * code modules are loaded as: * * load <path> <options> */ COMMAND_SET(load, "load", "load a kernel or module", command_load); static int command_load(int argc, char *argv[]) { struct preloaded_file *fp; char *typestr; char *prefix; char *skip; int dflag, dofile, dokld, ch, error; dflag = dokld = dofile = 0; optind = 1; optreset = 1; typestr = NULL; if (argc == 1) { command_errmsg = "no filename specified"; return (CMD_CRIT); } prefix = skip = NULL; while ((ch = getopt(argc, argv, "dkp:s:t:")) != -1) { switch(ch) { case 'd': dflag++; break; case 'k': dokld = 1; break; case 'p': prefix = optarg; break; case 's': skip = optarg; break; case 't': typestr = optarg; dofile = 1; break; case '?': default: /* getopt has already reported an error */ return (CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); /* * Request to load a raw file? */ if (dofile) { if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { command_errmsg = "invalid load type"; return (CMD_CRIT); } #ifdef LOADER_VERIEXEC if (strncmp(typestr, "manifest", 8) == 0) { if (dflag > 0) ve_debug_set(dflag); return (load_manifest(argv[1], prefix, skip, NULL)); } #ifdef LOADER_VERIEXEC_PASS_MANIFEST if (strncmp(typestr, "pass_manifest", 13) == 0) { if (dflag > 0) ve_debug_set(dflag); return (pass_manifest(argv[1], prefix)); } #endif #endif fp = file_findfile(argv[1], typestr); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: file '%s' already loaded", argv[1]); return (CMD_WARN); } if (file_loadraw(argv[1], typestr, 1) != NULL) return (CMD_OK); /* Failing to load mfs_root is never going to end well! */ if (strcmp("mfs_root", typestr) == 0) return (CMD_FATAL); return (CMD_ERROR); } /* * Do we have explicit KLD load ? */ if (dokld || file_havepath(argv[1])) { error = mod_loadkld(argv[1], argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } /* * Looks like a request for a module. */ error = mod_load(argv[1], NULL, argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #ifdef LOADER_GELI_SUPPORT COMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); static int command_load_geli(int argc, char *argv[]) { char typestr[80]; char *cp; int ch, num; if (argc < 3) { command_errmsg = "usage is [-n key#] <prov> <file>"; return(CMD_ERROR); } num = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "n:")) != -1) { switch(ch) { case 'n': num = strtol(optarg, &cp, 0); if (cp == optarg) { snprintf(command_errbuf, sizeof(command_errbuf), "bad key index '%s'", optarg); return(CMD_ERROR); } break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); } #endif void unload(void) { struct preloaded_file *fp; while (preloaded_files != NULL) { fp = preloaded_files; preloaded_files = preloaded_files->f_next; file_discard(fp); } loadaddr = 0; unsetenv("kernelname"); } COMMAND_SET(unload, "unload", "unload all modules", command_unload); static int command_unload(int argc, char *argv[]) { unload(); return(CMD_OK); } COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); static int command_lsmod(int argc, char *argv[]) { struct preloaded_file *fp; struct kernel_module *mp; struct file_metadata *md; char lbuf[80]; int ch, verbose, ret = 0; verbose = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } pager_open(); for (fp = preloaded_files; fp; fp = fp->f_next) { snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr); pager_output(lbuf); pager_output(fp->f_name); snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type, (long)fp->f_size); if (pager_output(lbuf)) break; if (fp->f_args != NULL) { pager_output(" args: "); pager_output(fp->f_args); if (pager_output("\n")) break; } if (fp->f_modules) { pager_output(" modules: "); for (mp = fp->f_modules; mp; mp = mp->m_next) { snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name, mp->m_version); pager_output(lbuf); } if (pager_output("\n")) break; } if (verbose) { /* XXX could add some formatting smarts here to display some better */ for (md = fp->f_metadata; md != NULL; md = md->md_next) { snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); if (pager_output(lbuf)) break; } } if (ret) break; } pager_close(); return(CMD_OK); } COMMAND_SET(pnpmatch, "pnpmatch", "list matched modules based on pnpinfo", command_pnpmatch); static int pnp_dump_flag = 0; static int pnp_unbound_flag = 0; static int pnp_verbose_flag = 0; static int command_pnpmatch(int argc, char *argv[]) { char *module; int ch; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); module = mod_searchmodule_pnpinfo(argv[1], argv[2]); if (module) printf("Matched module: %s\n", module); else if(argv[1]) printf("No module matches %s\n", argv[1]); return (CMD_OK); } COMMAND_SET(pnpload, "pnpload", "load matched modules based on pnpinfo", command_pnpload); static int command_pnpload(int argc, char *argv[]) { char *module; int ch, error; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); if (argc != 2) return (CMD_ERROR); module = mod_searchmodule_pnpinfo(argv[1], argv[2]); error = mod_load(module, NULL, 0, NULL); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #if defined(LOADER_FDT_SUPPORT) static void pnpautoload_simplebus(void) { const char *pnpstring; const char *compatstr; char *pnpinfo = NULL; char *module = NULL; int tag = 0, len, pnplen; int error; while (1) { pnpstring = fdt_devmatch_next(&tag, &len); if (pnpstring == NULL) return; compatstr = pnpstring; for (pnplen = 0; pnplen != len; compatstr = pnpstring + pnplen) { pnplen += strlen(compatstr) + 1; asprintf(&pnpinfo, "compat=%s", compatstr); module = mod_searchmodule_pnpinfo("simplebus", pnpinfo); if (module) { error = mod_loadkld(module, 0, NULL); if (error) printf("Cannot load module %s\n", module); break; } } free(pnpinfo); free(module); } } #endif struct pnp_bus { const char *name; void (*load)(void); }; struct pnp_bus pnp_buses[] = { #if defined(LOADER_FDT_SUPPORT) {"simplebus", pnpautoload_simplebus}, #endif }; COMMAND_SET(pnpautoload, "pnpautoload", "auto load modules based on pnpinfo", command_pnpautoload); static int command_pnpautoload(int argc, char *argv[]) { int i; int verbose; int ch, match; pnp_verbose_flag = 0; pnp_dump_flag = 0; verbose = 0; optind = 1; optreset = 1; match = 0; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); if (argc > 2) return (CMD_ERROR); for (i = 0; i < nitems(pnp_buses); i++) { if (argc == 2 && strcmp(argv[1], pnp_buses[i].name) != 0) { if (verbose) printf("Skipping bus %s\n", pnp_buses[i].name); continue; } if (verbose) printf("Autoloading modules for simplebus\n"); pnp_buses[i].load(); match = 1; } if (match == 0) printf("Unsupported bus %s\n", argv[1]); return (CMD_OK); } /* * File level interface, functions file_* */ int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) { static int last_file_format = 0; struct preloaded_file *fp; int error; int i; if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); error = EFTYPE; for (i = last_file_format, fp = NULL; file_formats[i] && fp == NULL; i++) { error = (file_formats[i]->l_load)(filename, dest, &fp); if (error == 0) { fp->f_loader = last_file_format = i; /* remember the loader */ *result = fp; break; } else if (last_file_format == i && i != 0) { /* Restart from the beginning */ i = -1; last_file_format = 0; fp = NULL; continue; } if (error == EFTYPE) continue; /* Unknown to this handler? */ if (error) { snprintf(command_errbuf, sizeof(command_errbuf), "can't load file '%s': %s", filename, strerror(error)); break; } } return (error); } static int file_load_dependencies(struct preloaded_file *base_file) { struct file_metadata *md; struct preloaded_file *fp; struct mod_depend *verinfo; struct kernel_module *mp; char *dmodname; int error; md = file_findmetadata(base_file, MODINFOMD_DEPLIST); if (md == NULL) return (0); error = 0; do { verinfo = (struct mod_depend*)md->md_data; dmodname = (char *)(verinfo + 1); if (file_findmodule(NULL, dmodname, verinfo) == NULL) { printf("loading required module '%s'\n", dmodname); error = mod_load(dmodname, verinfo, 0, NULL); if (error) break; /* * If module loaded via kld name which isn't listed * in the linker.hints file, we should check if it have * required version. */ mp = file_findmodule(NULL, dmodname, verinfo); if (mp == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "module '%s' exists but with wrong version", dmodname); error = ENOENT; break; } } md = metadata_next(md, MODINFOMD_DEPLIST); } while (md); if (!error) return (0); /* Load failed; discard everything */ while (base_file != NULL) { fp = base_file; base_file = base_file->f_next; file_discard(fp); } 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 #else #define VECTX_HANDLE(fd) fd #endif /* * We've been asked to load (fname) as (type), so just suck it in, * no arguments or anything. */ struct preloaded_file * file_loadraw(const char *fname, char *type, int insert) { struct preloaded_file *fp; char *name; int fd, got; vm_offset_t laddr; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; int verror; #endif /* We can't load first */ if ((file_findfile(NULL, NULL)) == NULL) { command_errmsg = "can't load file before kernel"; return(NULL); } /* locate the file on the load path */ name = file_search(fname, NULL); if (name == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", fname); return(NULL); } if ((fd = open(name, O_RDONLY)) < 0) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", name, strerror(errno)); free(name); return(NULL); } #ifdef LOADER_VERIEXEC_VECTX vctx = vectx_open(fd, name, 0L, NULL, &verror, __func__); if (verror) { sprintf(command_errbuf, "can't verify '%s': %s", name, ve_error_get()); free(name); free(vctx); close(fd); return(NULL); } #else #ifdef LOADER_VERIEXEC if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) { sprintf(command_errbuf, "can't verify '%s': %s", name, ve_error_get()); free(name); close(fd); return(NULL); } #endif #endif if (archsw.arch_loadaddr != NULL) loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); printf("%s ", name); laddr = loadaddr; for (;;) { /* read in 4k chunks; size is not really important */ got = archsw.arch_readin(VECTX_HANDLE(fd), laddr, 4096); if (got == 0) /* end of file */ break; if (got < 0) { /* error */ snprintf(command_errbuf, sizeof(command_errbuf), "error reading '%s': %s", name, strerror(errno)); free(name); close(fd); #ifdef LOADER_VERIEXEC_VECTX free(vctx); #endif return(NULL); } laddr += got; } printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr)); #ifdef LOADER_VERIEXEC_VECTX verror = vectx_close(vctx, VE_MUST, __func__); if (verror) { free(name); close(fd); free(vctx); return(NULL); } #endif /* Looks OK so far; create & populate control structure */ fp = file_alloc(); if (fp == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); return (NULL); } fp->f_name = name; fp->f_type = strdup(type); fp->f_args = NULL; fp->f_metadata = NULL; fp->f_loader = -1; fp->f_addr = loadaddr; fp->f_size = laddr - loadaddr; if (fp->f_type == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); return (NULL); } /* recognise space consumption */ loadaddr = laddr; /* Add to the list of loaded files */ if (insert != 0) file_insert_tail(fp); close(fd); return(fp); } /* * Load the module (name), pass it (argc),(argv), add container file * to the list of loaded files. * If module is already loaded just assign new argc/argv. */ int mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) { struct kernel_module *mp; int err; char *filename; if (file_havepath(modname)) { printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); return (mod_loadkld(modname, argc, argv)); } /* see if module is already loaded */ mp = file_findmodule(NULL, modname, verinfo); if (mp) { #ifdef moduleargs free(mp->m_args); mp->m_args = unargv(argc, argv); #endif snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", mp->m_name); return (0); } /* locate file with the module on the search path */ filename = mod_searchmodule(modname, verinfo); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", modname); return (ENOENT); } err = mod_loadkld(filename, argc, argv); free(filename); return (err); } /* * Load specified KLD. If path is omitted, then try to locate it via * search path. */ int mod_loadkld(const char *kldname, int argc, char *argv[]) { struct preloaded_file *fp; int err; char *filename; vm_offset_t loadaddr_saved; /* * Get fully qualified KLD name */ filename = file_search(kldname, kld_ext_list); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", kldname); return (ENOENT); } /* * Check if KLD already loaded */ fp = file_findfile(filename, NULL); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", filename); free(filename); return (0); } do { err = file_load(filename, loadaddr, &fp); if (err) break; fp->f_args = unargv(argc, argv); loadaddr_saved = loadaddr; loadaddr = fp->f_addr + fp->f_size; file_insert_tail(fp); /* Add to the list of loaded files */ if (file_load_dependencies(fp) != 0) { err = ENOENT; file_remove(fp); loadaddr = loadaddr_saved; fp = NULL; break; } } while(0); if (err == EFTYPE) { snprintf(command_errbuf, sizeof(command_errbuf), "don't know how to load module '%s'", filename); } if (err) file_discard(fp); free(filename); return (err); } /* * Find a file matching (name) and (type). * NULL may be passed as a wildcard to either. */ struct preloaded_file * file_findfile(const char *name, const char *type) { struct preloaded_file *fp; for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { if (((name == NULL) || !strcmp(name, fp->f_name)) && ((type == NULL) || !strcmp(type, fp->f_type))) break; } return (fp); } /* * Find a module matching (name) inside of given file. * NULL may be passed as a wildcard. */ struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo) { struct kernel_module *mp, *best; int bestver, mver; if (fp == NULL) { for (fp = preloaded_files; fp; fp = fp->f_next) { mp = file_findmodule(fp, modname, verinfo); if (mp) return (mp); } return (NULL); } best = NULL; bestver = 0; for (mp = fp->f_modules; mp; mp = mp->m_next) { if (strcmp(modname, mp->m_name) == 0) { if (verinfo == NULL) return (mp); mver = mp->m_version; if (mver == verinfo->md_ver_preferred) return (mp); if (mver >= verinfo->md_ver_minimum && mver <= verinfo->md_ver_maximum && mver > bestver) { best = mp; bestver = mver; } } } return (best); } /* * Make a copy of (size) bytes of data from (p), and associate them as * metadata of (type) to the module (mp). */ void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) { struct file_metadata *md; md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); if (md != NULL) { md->md_size = size; md->md_type = type; bcopy(p, md->md_data, size); md->md_next = fp->f_metadata; } fp->f_metadata = md; } /* * Find a metadata object of (type) associated with the file (fp) */ struct file_metadata * file_findmetadata(struct preloaded_file *fp, int type) { struct file_metadata *md; for (md = fp->f_metadata; md != NULL; md = md->md_next) if (md->md_type == type) break; return(md); } /* * Remove all metadata from the file. */ void file_removemetadata(struct preloaded_file *fp) { struct file_metadata *md, *next; for (md = fp->f_metadata; md != NULL; md = next) { next = md->md_next; free(md); } fp->f_metadata = NULL; } struct file_metadata * metadata_next(struct file_metadata *md, int type) { if (md == NULL) return (NULL); while((md = md->md_next) != NULL) if (md->md_type == type) break; return (md); } static char *emptyextlist[] = { "", NULL }; /* * Check if the given file is in place and return full path to it. */ static char * file_lookup(const char *path, const char *name, int namelen, char **extlist) { struct stat st; char *result, *cp, **cpp; int pathlen, extlen, len; pathlen = strlen(path); extlen = 0; if (extlist == NULL) extlist = emptyextlist; for (cpp = extlist; *cpp; cpp++) { len = strlen(*cpp); if (len > extlen) extlen = len; } result = malloc(pathlen + namelen + extlen + 2); if (result == NULL) return (NULL); bcopy(path, result, pathlen); if (pathlen > 0 && result[pathlen - 1] != '/') result[pathlen++] = '/'; cp = result + pathlen; bcopy(name, cp, namelen); cp += namelen; for (cpp = extlist; *cpp; cpp++) { strcpy(cp, *cpp); if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) return result; } free(result); return NULL; } /* * Check if file name have any qualifiers */ static int file_havepath(const char *name) { const char *cp; archsw.arch_getdev(NULL, name, &cp); return (cp != name || strchr(name, '/') != NULL); } /* * Attempt to find the file (name) on the module searchpath. * If (name) is qualified in any way, we simply check it and * return it or NULL. If it is not qualified, then we attempt * to construct a path using entries in the environment variable * module_path. * * The path we return a pointer to need never be freed, as we manage * it internally. */ static char * file_search(const char *name, char **extlist) { struct moduledir *mdp; struct stat sb; char *result; int namelen; /* Don't look for nothing */ if (name == NULL) return(NULL); if (*name == 0) return(strdup(name)); if (file_havepath(name)) { /* Qualified, so just see if it exists */ if (stat(name, &sb) == 0) return(strdup(name)); return(NULL); } moduledir_rebuild(); result = NULL; namelen = strlen(name); STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = file_lookup(mdp->d_path, name, namelen, extlist); if (result) break; } return(result); } #define INT_ALIGN(base, ptr) ptr = \ (base) + roundup2((ptr) - (base), sizeof(int)) static char * mod_search_hints(struct moduledir *mdp, const char *modname, struct mod_depend *verinfo) { u_char *cp, *recptr, *bufend, *best; char *result; int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; moduledir_readhints(mdp); modnamelen = strlen(modname); found = 0; result = NULL; bestver = 0; if (mdp->d_hints == NULL) goto bad; recptr = mdp->d_hints; bufend = recptr + mdp->d_hintsz; clen = blen = 0; best = cp = NULL; while (recptr < bufend && !found) { intp = (int*)recptr; reclen = *intp++; ival = *intp++; cp = (u_char*)intp; switch (ival) { case MDT_VERSION: clen = *cp++; if (clen != modnamelen || bcmp(cp, modname, clen) != 0) break; cp += clen; INT_ALIGN(mdp->d_hints, cp); ival = *(int*)cp; cp += sizeof(int); clen = *cp++; if (verinfo == NULL || ival == verinfo->md_ver_preferred) { found = 1; break; } if (ival >= verinfo->md_ver_minimum && ival <= verinfo->md_ver_maximum && ival > bestver) { bestver = ival; best = cp; blen = clen; } break; default: break; } recptr += reclen + sizeof(int); } /* * Finally check if KLD is in the place */ if (found) result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL); else if (best) result = file_lookup(mdp->d_path, (const char *)best, blen, NULL); bad: /* * If nothing found or hints is absent - fallback to the old way * by using "kldname[.ko]" as module name. */ if (!found && !bestver && result == NULL) result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); return result; } static int getint(void **ptr) { int *p = *ptr; int rv; p = (int *)roundup2((intptr_t)p, sizeof(int)); rv = *p++; *ptr = p; return rv; } static void getstr(void **ptr, char *val) { int *p = *ptr; char *c = (char *)p; int len = *(uint8_t *)c; memcpy(val, c + 1, len); val[len] = 0; c += len + 1; *ptr = (void *)c; } static int pnpval_as_int(const char *val, const char *pnpinfo) { int rv; char key[256]; char *cp; if (pnpinfo == NULL) return -1; cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) rv = strtol(pnpinfo + strlen(key + 1), NULL, 0); else { cp = strstr(pnpinfo, key); if (cp == NULL) rv = -1; else rv = strtol(cp + strlen(key), NULL, 0); } return rv; } static void quoted_strcpy(char *dst, const char *src) { char q = ' '; if (*src == '\'' || *src == '"') q = *src++; while (*src && *src != q) *dst++ = *src++; // XXX backtick quoting *dst++ = '\0'; // XXX overflow } static char * pnpval_as_str(const char *val, const char *pnpinfo) { static char retval[256]; char key[256]; char *cp; if (pnpinfo == NULL) { *retval = '\0'; return retval; } cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) quoted_strcpy(retval, pnpinfo + strlen(key + 1)); else { cp = strstr(pnpinfo, key); if (cp == NULL) strcpy(retval, "MISSING"); else quoted_strcpy(retval, cp + strlen(key)); } return retval; } static char * devmatch_search_hints(struct moduledir *mdp, const char *bus, const char *dev, const char *pnpinfo) { char val1[256], val2[256]; int ival, len, ents, i, notme, mask, bit, v, found; void *ptr, *walker, *hints_end; char *lastmod = NULL, *cp, *s; moduledir_readhints(mdp); found = 0; if (mdp->d_hints == NULL) goto bad; walker = mdp->d_hints; hints_end = walker + mdp->d_hintsz; while (walker < hints_end && !found) { len = getint(&walker); ival = getint(&walker); ptr = walker; switch (ival) { case MDT_VERSION: getstr(&ptr, val1); ival = getint(&ptr); getstr(&ptr, val2); if (pnp_dump_flag || pnp_verbose_flag) printf("Version: if %s.%d kmod %s\n", val1, ival, val2); break; case MDT_MODULE: getstr(&ptr, val1); getstr(&ptr, val2); if (lastmod) free(lastmod); lastmod = strdup(val2); if (pnp_dump_flag || pnp_verbose_flag) printf("module %s in %s\n", val1, val1); break; case MDT_PNP_INFO: if (!pnp_dump_flag && !pnp_unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0) break; getstr(&ptr, val1); getstr(&ptr, val2); ents = getint(&ptr); if (pnp_dump_flag || pnp_verbose_flag) printf("PNP info for bus %s format %s %d entries (%s)\n", val1, val2, ents, lastmod); if (strcmp(val1, "usb") == 0) { if (pnp_verbose_flag) printf("Treating usb as uhub -- bug in source table still?\n"); strcpy(val1, "uhub"); } if (bus && strcmp(val1, bus) != 0) { if (pnp_verbose_flag) printf("Skipped because table for bus %s, looking for %s\n", val1, bus); break; } for (i = 0; i < ents; i++) { if (pnp_verbose_flag) printf("---------- Entry %d ----------\n", i); if (pnp_dump_flag) printf(" "); cp = val2; notme = 0; mask = -1; bit = -1; do { switch (*cp) { /* All integer fields */ case 'I': case 'J': case 'G': case 'L': case 'M': ival = getint(&ptr); if (pnp_dump_flag) { printf("%#x:", ival); break; } if (bit >= 0 && ((1 << bit) & mask) == 0) break; v = pnpval_as_int(cp + 2, pnpinfo); if (pnp_verbose_flag) printf("Matching %s (%c) table=%#x tomatch=%#x\n", cp + 2, *cp, v, ival); switch (*cp) { case 'J': if (ival == -1) break; /*FALLTHROUGH*/ case 'I': if (v != ival) notme++; break; case 'G': if (v < ival) notme++; break; case 'L': if (v > ival) notme++; break; case 'M': mask = ival; break; } break; /* String fields */ case 'D': case 'Z': getstr(&ptr, val1); if (pnp_dump_flag) { printf("'%s':", val1); break; } if (*cp == 'D') break; s = pnpval_as_str(cp + 2, pnpinfo); if (strcmp(s, val1) != 0) notme++; break; /* Key override fields, required to be last in the string */ case 'T': /* * This is imperfect and only does one key and will be redone * to be more general for multiple keys. Currently, nothing * does that. */ if (pnp_dump_flag) /* No per-row data stored */ break; if (cp[strlen(cp) - 1] == ';') /* Skip required ; at end */ cp[strlen(cp) - 1] = '\0'; /* in case it's not there */ if ((s = strstr(pnpinfo, cp + 2)) == NULL) notme++; else if (s > pnpinfo && s[-1] != ' ') notme++; break; default: printf("Unknown field type %c\n:", *cp); break; } bit++; cp = strchr(cp, ';'); if (cp) cp++; } while (cp && *cp); if (pnp_dump_flag) printf("\n"); else if (!notme) { if (!pnp_unbound_flag) { if (pnp_verbose_flag) printf("Matches --- %s ---\n", lastmod); } found++; } } break; default: break; } walker = (void *)(len - sizeof(int) + (intptr_t)walker); } if (pnp_unbound_flag && found == 0 && *pnpinfo) { if (pnp_verbose_flag) printf("------------------------- "); printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo); if (pnp_verbose_flag) printf(" -------------------------"); printf("\n"); } if (found != 0) return (lastmod); free(lastmod); bad: return (NULL); } /* * Attempt to locate the file containing the module (name) */ static char * mod_searchmodule(char *name, struct mod_depend *verinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = mod_search_hints(mdp, name, verinfo); if (result) break; } return(result); } static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = devmatch_search_hints(mdp, bus, NULL, pnpinfo); if (result) break; } return(result); } int file_addmodule(struct preloaded_file *fp, char *modname, int version, struct kernel_module **newmp) { struct kernel_module *mp; struct mod_depend mdepend; bzero(&mdepend, sizeof(mdepend)); mdepend.md_ver_preferred = version; mp = file_findmodule(fp, modname, &mdepend); if (mp) return (EEXIST); mp = calloc(1, sizeof(struct kernel_module)); if (mp == NULL) return (ENOMEM); mp->m_name = strdup(modname); if (mp->m_name == NULL) { free(mp); return (ENOMEM); } mp->m_version = version; mp->m_fp = fp; mp->m_next = fp->f_modules; fp->f_modules = mp; if (newmp) *newmp = mp; return (0); } /* * Throw a file away */ void file_discard(struct preloaded_file *fp) { struct file_metadata *md, *md1; struct kernel_module *mp, *mp1; if (fp == NULL) return; md = fp->f_metadata; while (md) { md1 = md; md = md->md_next; free(md1); } mp = fp->f_modules; while (mp) { free(mp->m_name); mp1 = mp; mp = mp->m_next; free(mp1); } free(fp->f_name); free(fp->f_type); free(fp->f_args); free(fp); } /* * Allocate a new file; must be used instead of malloc() * to ensure safe initialisation. */ struct preloaded_file * file_alloc(void) { return (calloc(1, sizeof(struct preloaded_file))); } /* * Add a module to the chain */ static void file_insert_tail(struct preloaded_file *fp) { struct preloaded_file *cm; /* Append to list of loaded file */ fp->f_next = NULL; if (preloaded_files == NULL) { preloaded_files = fp; } else { for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) ; cm->f_next = fp; } } /* * Remove module from the chain */ static void file_remove(struct preloaded_file *fp) { struct preloaded_file *cm; if (preloaded_files == NULL) return; if (preloaded_files == fp) { preloaded_files = fp->f_next; return; } for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) { if (cm->f_next == fp) { cm->f_next = fp->f_next; return; } } } static char * moduledir_fullpath(struct moduledir *mdp, const char *fname) { char *cp; cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); if (cp == NULL) return NULL; strcpy(cp, mdp->d_path); strcat(cp, "/"); strcat(cp, fname); return (cp); } /* * Read linker.hints file into memory performing some sanity checks. */ static void moduledir_readhints(struct moduledir *mdp) { struct stat st; char *path; int fd, size, version; if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) return; path = moduledir_fullpath(mdp, "linker.hints"); if (stat(path, &st) != 0 || st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) { free(path); mdp->d_flags |= MDIR_NOHINTS; return; } free(path); size = read(fd, &version, sizeof(version)); if (size != sizeof(version) || version != LINKER_HINTS_VERSION) goto bad; size = st.st_size - size; mdp->d_hints = malloc(size); if (mdp->d_hints == NULL) goto bad; if (read(fd, mdp->d_hints, size) != size) goto bad; mdp->d_hintsz = size; close(fd); return; bad: close(fd); free(mdp->d_hints); mdp->d_hints = NULL; mdp->d_flags |= MDIR_NOHINTS; return; } /* * Extract directories from the ';' separated list, remove duplicates. */ static void moduledir_rebuild(void) { struct moduledir *mdp, *mtmp; const char *path, *cp, *ep; size_t cplen; path = getenv("module_path"); if (path == NULL) path = default_searchpath; /* * Rebuild list of module directories if it changed */ STAILQ_FOREACH(mdp, &moduledir_list, d_link) mdp->d_flags |= MDIR_REMOVED; for (ep = path; *ep != 0; ep++) { cp = ep; for (; *ep != 0 && *ep != ';'; ep++) ; /* * Ignore trailing slashes */ for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) ; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) continue; mdp->d_flags &= ~MDIR_REMOVED; break; } if (mdp == NULL) { mdp = malloc(sizeof(*mdp) + cplen + 1); if (mdp == NULL) return; mdp->d_path = (char*)(mdp + 1); bcopy(cp, mdp->d_path, cplen); mdp->d_path[cplen] = 0; mdp->d_hints = NULL; mdp->d_flags = 0; STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); } if (*ep == 0) break; } /* * Delete unused directories if any */ mdp = STAILQ_FIRST(&moduledir_list); while (mdp) { if ((mdp->d_flags & MDIR_REMOVED) == 0) { mdp = STAILQ_NEXT(mdp, d_link); } else { free(mdp->d_hints); mtmp = mdp; mdp = STAILQ_NEXT(mdp, d_link); STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); free(mtmp); } } return; } diff --git a/stand/defaults/loader.conf.5 b/stand/defaults/loader.conf.5 index 1053191ec181..22fe9812a33a 100644 --- a/stand/defaults/loader.conf.5 +++ b/stand/defaults/loader.conf.5 @@ -1,384 +1,386 @@ .\" Copyright (c) 1999 Daniel C. Sobral .\" 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 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$ .Dd December 31, 2020 .Dt LOADER.CONF 5 .Os .Sh NAME .Nm loader.conf .Nd "system bootstrap configuration information" .Sh DESCRIPTION The file .Nm contains descriptive information on bootstrapping the system. Through it you can specify the kernel to be booted, parameters to be passed to it, and additional modules to be loaded; and generally set all variables described in .Xr loader 8 . .Sh SYNTAX Though .Nm Ns 's format was defined explicitly to resemble .Xr rc.conf 5 , and can be sourced by .Xr sh 1 , some settings are treated in a special fashion. Also, the behavior of some settings is defined by the setting's suffix; the prefix identifies which module the setting controls. .Pp The general parsing rules are: .Bl -bullet .It Spaces and empty lines are ignored. .It A # sign will mark the remainder of the line as a comment. .It Only one setting can be present on each line. .El .Pp All settings have the following format: .Pp .Dl variable="value" .Pp Unless it belongs to one of the classes of settings that receive special treatment, a setting will set the value of a .Xr loader 8 environment variable. The settings that receive special treatment are listed below. Settings beginning with .Qq * below define the modules to be loaded and may have any prefix; the prefix identifies a module. All such settings sharing a common prefix refer to the same module. .Bl -tag -width Ar .It Ar exec Immediately executes a .Xr loader 8 command. This type of setting cannot be processed by programs other than .Xr loader 8 , so its use should be avoided. Multiple instances of it will be processed independently. .It Ar loader_conf_dirs Space separated list of directories to process for configuration files. The lua-based loader will process files with a .Dq .conf suffix that are placed in these directories. .It Ar loader_conf_files Defines additional configuration files to be processed right after the present file. .Ar loader_conf_files should be treated as write-only. One cannot depend on any value remaining in the loader environment or carried over into the kernel environment. .It Ar kernel Name of the kernel to be loaded. If no kernel name is set, no additional modules will be loaded. The name must be a subdirectory of .Pa /boot that contains a kernel. .It Ar kernel_options Flags to be passed to the kernel. .It Ar vfs.root.mountfrom Specify the root partition to mount. For example: .Pp .Dl vfs.root.mountfrom="ufs:/dev/da0s1a" .Pp .Xr loader 8 automatically calculates the value of this tunable from .Pa /etc/fstab from the partition the kernel was loaded from. The calculated value might be calculated incorrectly when .Pa /etc/fstab is not available during .Xr loader 8 startup (as during diskless booting from NFS), or if a different device is desired by the user. The preferred value can be set in .Pa /loader.conf . .Pp The value can also be overridden from the .Xr loader 8 command line. This is useful for system recovery when .Pa /etc/fstab is damaged, lost, or read from the wrong partition. .It Ar password Protect boot menu with a password without interrupting .Ic autoboot process. The password should be in clear text format. If a password is set, boot menu will not appear until any key is pressed during countdown period specified by .Va autoboot_delay variable or .Ic autoboot process fails. In both cases user should provide specified password to be able to access boot menu. .It Ar bootlock_password Provides a password to be required by check-password before execution is allowed to continue. The password should be in clear text format. If a password is set, the user must provide specified password to boot. .It Ar verbose_loading If set to .Dq YES , module names will be displayed as they are loaded. .It Ar module_blacklist Blacklist of modules. Modules specified in the blacklist may not be loaded automatically with a .Ar *_load directive, but they may be loaded directly at the .Xr loader 8 prompt. Blacklisted modules may still be loaded indirectly as dependencies of other modules. .It Ar *_load If set to .Dq YES , that module will be loaded. If no name is defined (see below), the module's name is taken to be the same as the prefix. .It Ar *_name Defines the name of the module. .It Ar *_type Defines the module's type. If none is given, it defaults to a kld module. .It Ar *_flags Flags and parameters to be passed to the module. .It Ar *_before Commands to be executed before the module is loaded. Use of this setting should be avoided. .It Ar *_after Commands to be executed after the module is loaded. Use of this setting should be avoided. .It Ar *_error Commands to be executed if the loading of a module fails. Except for the special value .Dq abort , which aborts the bootstrap process, use of this setting should be avoided. .El .Pp .Em WARNING: developers should never use these suffixes for any kernel environment variables (tunables) or conflicts will result. .Sh DEFAULT SETTINGS Most of .Nm Ns 's default settings can be ignored. The few of them which are important or useful are: .Bl -tag -width bootfile -offset indent .It Va bitmap_load .Pq Dq NO If set to .Dq YES , a bitmap will be loaded to be displayed on screen while booting. .It Va bitmap_name .Pq Dq Pa /boot/splash.bmp Name of the bitmap to be loaded. Any other name can be used. .It Va comconsole_speed .Dq ( 9600 or the value of the .Va BOOT_COMCONSOLE_SPEED variable when .Xr loader 8 was compiled). Sets the speed of the serial console. If the previous boot loader stage specified that a serial console is in use then the default speed is determined from the current serial port speed setting. .It Va console .Pq Dq vidconsole .Dq comconsole selects serial console, .Dq vidconsole selects the video console, .Dq nullconsole selects a mute console (useful for systems with neither a video console nor a serial port), and .Dq spinconsole selects the video console which prevents any input and hides all output replacing it with .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 .It 480p Ta 640x480 .It 720p Ta 1280x720 .It 1080p Ta 1920x1080 .It 2160p Ta 3840x2160 .It 4k Ta 3840x2160 .It 5k Ta 5120x2880 .It Va Width Ns x Ns Va Height Ta Va Width Ns x Ns Va Height .El .It Va kernel .Pq Dq kernel .It Va kernels .Pq Dq kernel kernel.old Space or comma separated list of kernels to present in the boot menu. .It Va loader_conf_files .Pq Dq Pa /boot/loader.conf /boot/loader.conf.local .It Va loader_conf_dirs .Pq Dq Pa /boot/loader.conf.d .It Va splash_bmp_load .Pq Dq NO If set to .Dq YES , will load the splash screen module, making it possible to display a bmp image on the screen while booting. .It Va splash_pcx_load .Pq Dq NO If set to .Dq YES , will load the splash screen module, making it possible to display a pcx image on the screen while booting. .It Va vesa_load .Pq Dq NO If set to .Dq YES , the vesa module will be loaded, enabling bitmaps above VGA resolution to be displayed. .It Va beastie_disable If set to .Dq YES , the beastie boot menu will be skipped. .It Va loader_logo Pq Dq Li orbbw Selects a desired logo in the beastie boot menu. Possible values are: .Dq Li orbbw , .Dq Li orb , .Dq Li fbsdbw , .Dq Li beastiebw , .Dq Li beastie , and .Dq Li none . .It Va loader_color If set to .Dq NO , the beastie boot menu will be displayed without ANSI coloring. .It Va entropy_cache_load .Pq Dq YES If set to .Dq NO , the very early boot-time entropy file will not be loaded. See the entropy entries in .Xr rc.conf 5 . .It Va entropy_cache_name .Pq Dq /boot/entropy The name of the very early boot-time entropy cache file. .It Va cpu_microcode_load .Pq Dq NO If set to .Dq YES , the microcode update file specified by .Va cpu_microcode_name will be loaded and applied very early during boot. This provides functionality similar to .Xr cpucontrol 8 but ensures that CPU features enabled by microcode updates can be used by the kernel. The update will be re-applied automatically when resuming from an ACPI sleep state. If the update file contains updates for multiple processor models, the kernel will search for and extract a matching update. Currently this setting is supported only on Intel .Dv i386 and .Dv amd64 processors. It has no effect on other processor types. .It Va cpu_microcode_name A path to a microcode update file. .El .Sh OTHER SETTINGS Other settings that may be used in .Nm that have no default value: .Bl -tag -width bootfile -offset indent .It Va fdt_overlays Specifies a comma-delimited list of FDT overlays to apply. .Pa /boot/dtb/overlays is created by default for overlays to be placed in. .It Va kernels_autodetect If set to .Dq YES , attempt to auto-detect kernels installed in .Pa /boot . This is an option specific to the Lua-based loader. It is not available in the default Forth-based loader. .El .Sh FILES .Bl -tag -width /boot/defaults/loader.conf -compact .It Pa /boot/defaults/loader.conf default settings -- do not change this file. .It Pa /boot/loader.conf user defined settings. .It Pa /boot/loader.conf.local machine-specific settings for sites with a common loader.conf. .El .Sh SEE ALSO .Xr rc.conf 5 , .Xr boot 8 , .Xr cpucontrol 8 , .Xr loader 8 , .Xr loader.4th 8 .Sh HISTORY The file .Nm first appeared in .Fx 3.2 . .Sh AUTHORS This manual page was written by .An Daniel C. Sobral Aq dcs@FreeBSD.org . .Sh BUGS The .Xr loader 8 stops reading .Nm when it encounters a syntax error, so any options which are vital for booting a particular system (i.e.\& .Dq Va hw.ata.ata_dma Ns "=0" ) should precede any experimental additions to .Nm . diff --git a/stand/efi/include/efilib.h b/stand/efi/include/efilib.h index 6f1dbac98730..b9b9030afd7f 100644 --- a/stand/efi/include/efilib.h +++ b/stand/efi/include/efilib.h @@ -1,151 +1,150 @@ /*- * Copyright (c) 2000 Doug Rabson * Copyright (c) 2006 Marcel Moolenaar * 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 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 _LOADER_EFILIB_H #define _LOADER_EFILIB_H #include <stand.h> #include <stdbool.h> #include <sys/queue.h> extern EFI_HANDLE IH; extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RS; extern struct devsw efipart_fddev; extern struct devsw efipart_cddev; extern struct devsw efipart_hddev; extern struct devsw efihttp_dev; extern struct devsw efinet_dev; extern struct netif_driver efinetif; /* EFI block device data, included here to help efi_zfs_probe() */ typedef STAILQ_HEAD(pdinfo_list, pdinfo) pdinfo_list_t; typedef struct pdinfo { STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */ pdinfo_list_t pd_part; /* list of partitions */ EFI_HANDLE pd_handle; EFI_HANDLE pd_alias; EFI_DEVICE_PATH *pd_devpath; EFI_BLOCK_IO *pd_blkio; uint32_t pd_unit; /* unit number */ uint32_t pd_open; /* reference counter */ void *pd_bcache; /* buffer cache data */ struct pdinfo *pd_parent; /* Linked items (eg partitions) */ struct devsw *pd_devsw; /* Back pointer to devsw */ } pdinfo_t; pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev); pdinfo_t *efiblk_get_pdinfo(struct devdesc *dev); pdinfo_t *efiblk_get_pdinfo_by_handle(EFI_HANDLE h); pdinfo_t *efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path); void *efi_get_table(EFI_GUID *tbl); EFI_STATUS OpenProtocolByHandle(EFI_HANDLE, EFI_GUID *, void **); int efi_getdev(void **vdev, const char *devspec, const char **path); char *efi_fmtdev(void *vdev); int efi_setcurrdev(struct env_var *ev, int flags, const void *value); int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int); EFI_HANDLE efi_find_handle(struct devsw *, int); int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE); EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE); void efi_close_devpath(EFI_HANDLE); EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *); bool efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_match_node(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_is_prefix(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *); void efi_free_devpath_name(CHAR16 *); bool efi_devpath_same_disk(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_to_media_path(EFI_DEVICE_PATH *); UINTN efi_devpath_length(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_name_to_devpath(const char *path); EFI_DEVICE_PATH *efi_name_to_devpath16(CHAR16 *path); void efi_devpath_free(EFI_DEVICE_PATH *dp); EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles); int efi_status_to_errno(EFI_STATUS); EFI_STATUS errno_to_efi_status(int errno); void efi_time_init(void); 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[]); void efi_exit(EFI_STATUS status) __dead2; /* EFI environment initialization. */ void efi_init_environment(void); /* EFI Memory type strings. */ const char *efi_memory_type(EFI_MEMORY_TYPE); /* CHAR16 utility functions. */ int wcscmp(CHAR16 *, CHAR16 *); void cpy8to16(const char *, CHAR16 *, size_t); void cpy16to8(const CHAR16 *, char *, size_t); /* * Routines for interacting with EFI's env vars in a more unix-like * way than the standard APIs. In addition, convenience routines for * the loader setting / getting FreeBSD specific variables. */ EFI_STATUS efi_delenv(EFI_GUID *guid, const char *varname); EFI_STATUS efi_freebsd_delenv(const char *varname); EFI_STATUS efi_freebsd_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_getenv(EFI_GUID *g, const char *v, void *data, __size_t *len); EFI_STATUS efi_global_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_setenv(EFI_GUID *guid, const char *varname, UINT32 attr, void *data, __size_t len); EFI_STATUS efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr); /* guids and names */ bool efi_guid_to_str(const EFI_GUID *, char **); bool efi_str_to_guid(const char *, EFI_GUID *); bool efi_name_to_guid(const char *, EFI_GUID *); bool efi_guid_to_name(EFI_GUID *, char **); /* efipart.c */ int efipart_inithandles(void); #endif /* _LOADER_EFILIB_H */ diff --git a/stand/efi/libefi/Makefile b/stand/efi/libefi/Makefile index 8006893d0cbe..010754b5238a 100644 --- a/stand/efi/libefi/Makefile +++ b/stand/efi/libefi/Makefile @@ -1,72 +1,72 @@ # $FreeBSD$ .include <bsd.init.mk> LIB= efi WARNS?= 2 SRCS= delay.c \ devicename.c \ devpath.c \ efi_console.c \ efi_driver_utils.c \ efichar.c \ efienv.c \ efihttp.c \ efinet.c \ efipart.c \ efizfs.c \ env.c \ errno.c \ handles.c \ libefi.c \ wchar.c .PATH: ${SYSDIR}/teken SRCS+= teken.c .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SRCS+= time.c .elif ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" || \ ${MACHINE_CPUARCH} == "riscv" SRCS+= time_event.c .endif # We implement a slightly non-standard %S in that it always takes a # CHAR16 that's common in UEFI-land instead of a wchar_t. This only # seems to matter on arm64 where wchar_t defaults to an int instead # of a short. There's no good cast to use here so just ignore the # warnings for now. CWARNFLAGS.efinet.c+= -Wno-format CWARNFLAGS.efipart.c+= -Wno-format CWARNFLAGS.env.c+= -Wno-format .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -mgeneral-regs-only .endif .if ${MACHINE_ARCH} == "amd64" CFLAGS+= -fPIC -mno-red-zone .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} CFLAGS+= -I${SYSDIR}/cddl/boot/zfs CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common CFLAGS+= -DEFI_ZFS_BOOT .endif # Pick up the bootstrap header for some interface items CFLAGS+= -I${LDRSRC} # Handle FreeBSD specific %b and %D printf format specifiers CFLAGS+= ${FORMAT_EXTENSIONS} # Do not use TERM_EMU on arm and arm64 as it doesn't behave well with serial console .if ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "aarch64" CFLAGS+= -DTERM_EMU .endif .include <bsd.lib.mk> diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c index a72fb4d561da..22adcd3a00e2 100644 --- a/stand/efi/libefi/efi_console.c +++ b/stand/efi/libefi/efi_console.c @@ -1,1235 +1,1401 @@ /*- * Copyright (c) 2000 Doug Rabson * 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 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. */ #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? */ static uint32_t utf8_left; static uint32_t utf8_partial; #ifdef TERM_EMU #define DEFAULT_FGCOLOR EFI_LIGHTGRAY #define DEFAULT_BGCOLOR EFI_BLACK #define MAXARGS 8 static int args[MAXARGS], argc; static int fg_c, bg_c, curx, cury; static int esc; void get_pos(int *x, int *y); void curs_move(int *_x, int *_y, int x, int y); static void CL(int); void HO(void); 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_putchar_t efi_text_putchar; static tf_fill_t efi_text_fill; static tf_copy_t efi_text_copy; static tf_param_t efi_text_param; static tf_respond_t efi_cons_respond; static teken_funcs_t tf = { .tf_bell = efi_cons_bell, .tf_cursor = efi_text_cursor, .tf_putchar = efi_text_putchar, .tf_fill = efi_text_fill, .tf_copy = efi_text_copy, .tf_param = efi_text_param, .tf_respond = efi_cons_respond, }; -teken_t teken; -teken_pos_t tp; - -struct text_pixel { - teken_char_t c; - teken_attr_t a; +static teken_funcs_t tfx = { + .tf_bell = efi_cons_bell, + .tf_cursor = gfx_fb_cursor, + .tf_putchar = gfx_fb_putchar, + .tf_fill = gfx_fb_fill, + .tf_copy = gfx_fb_copy, + .tf_param = gfx_fb_param, + .tf_respond = efi_cons_respond, }; -static struct text_pixel *buffer; - #define KEYBUFSZ 10 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ static int key_pending; static const unsigned char teken_color_to_efi_color[16] = { EFI_BLACK, EFI_RED, EFI_GREEN, EFI_BROWN, EFI_BLUE, EFI_MAGENTA, EFI_CYAN, EFI_LIGHTGRAY, EFI_DARKGRAY, EFI_LIGHTRED, EFI_LIGHTGREEN, EFI_YELLOW, EFI_LIGHTBLUE, EFI_LIGHTMAGENTA, EFI_LIGHTCYAN, EFI_WHITE }; static void efi_cons_probe(struct console *); static int efi_cons_init(int); void efi_cons_putchar(int); 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", "EFI console", C_WIDEOUT, efi_cons_probe, efi_cons_init, efi_cons_putchar, efi_cons_getchar, efi_cons_poll }; +/* + * 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; + screen_buffer[idx].a.ta_format |= TF_IMAGE; + } + } +} + /* * Not implemented. */ static void efi_cons_bell(void *s __unused) { } 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; - - if (p->tp_row == row) - row = p->tp_row - 1; - else - row = p->tp_row; + col = p->tp_col; + if (p->tp_col >= state->tg_tp.tp_col) + col = state->tg_tp.tp_col - 1; conout->SetCursorPosition(conout, col, row); } static void -efi_text_printchar(const teken_pos_t *p, bool autoscroll) +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 = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col; a = conout->Mode->Attribute; 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; if (px->a.ta_format & TF_REVERSE) { tmp = fg; fg = bg; bg = tmp; } attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], teken_color_to_efi_color[bg] & 0x7); 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); efi_cons_efiputchar(px->c); (void) conout->SetAttribute(conout, a); } 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; - buffer[idx].c = c; - buffer[idx].a = *a; - efi_text_printchar(p, false); + idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; + if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) + return; + + screen_buffer[idx].c = c; + screen_buffer[idx].a = *a; + + efi_text_printchar(s, p, false); } static void -efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, +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; - UINTN row, col; - - (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); - conout->EnableCursor(conout, FALSE); + 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 bool -efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) +static void +efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, + teken_pos_t *d, bool scroll) { - if (px1->c != px2->c) - return (false); - - if (px1->a.ta_format != px2->a.ta_format) - return (false); - if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) - return (false); - if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) - return (false); - - return (true); + 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 (!is_same_pixel(&screen_buffer[soffset + x], + &screen_buffer[doffset + x])) { + screen_buffer[doffset + x] = + screen_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 *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) +efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) { - int srow, drow; - int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ + 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; /* * 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; /* * 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++) { - 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; - - 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); - } - } - } + d.tp_row = p->tp_row + y; + + efi_text_copy_line(state, ncol, &s, &d, scroll); } } 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; - - 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); - } - } - } - } 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); - } - } - } + for (y = nrow - 1; y >= 0; 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, false); } } - conout->EnableCursor(conout, TRUE); + + /* display the cursor */ + if (state->tg_cursor_visible) + conout->EnableCursor(conout, TRUE); } static void -efi_text_param(void *s __unused, int cmd, unsigned int value) +efi_text_param(void *arg, int cmd, unsigned int value) { + teken_gfx_t *state = arg; + 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: - 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 */ break; } } /* * Not implemented. */ static void efi_cons_respond(void *s __unused, const void *buf __unused, size_t len __unused) { } /* * Set up conin/conout/coninex to make sure we have input ready. */ static void efi_cons_probe(struct console *cp) { EFI_STATUS status; conout = ST->ConOut; conin = ST->ConIn; /* * Call SetMode to work around buggy firmware. */ status = conout->SetMode(conout, conout->Mode->Mode); if (coninex == NULL) { status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (status != EFI_SUCCESS) coninex = NULL; } cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; } static bool color_name_to_teken(const char *name, int *val) { if (strcasecmp(name, "black") == 0) { *val = TC_BLACK; return (true); } if (strcasecmp(name, "red") == 0) { *val = TC_RED; return (true); } if (strcasecmp(name, "green") == 0) { *val = TC_GREEN; return (true); } if (strcasecmp(name, "brown") == 0) { *val = TC_BROWN; return (true); } if (strcasecmp(name, "blue") == 0) { *val = TC_BLUE; return (true); } if (strcasecmp(name, "magenta") == 0) { *val = TC_MAGENTA; return (true); } if (strcasecmp(name, "cyan") == 0) { *val = TC_CYAN; return (true); } if (strcasecmp(name, "white") == 0) { *val = TC_WHITE; return (true); } return (false); } static int efi_set_colors(struct env_var *ev, int flags, const void *value) { int val = 0; char buf[2]; const void *evalue; const teken_attr_t *ap; teken_attr_t a; if (value == NULL) return (CMD_OK); if (color_name_to_teken(value, &val)) { snprintf(buf, sizeof (buf), "%d", val); evalue = buf; } else { char *end; errno = 0; val = (int)strtol(value, &end, 0); if (errno != 0 || *end != '\0') { printf("Allowed values are either ansi color name or " "number from range [0-7].\n"); return (CMD_OK); } 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? */ if (ap->ta_fgcolor == val) return (CMD_OK); a.ta_fgcolor = val; } if (strcmp(ev->ev_name, "teken.bg_color") == 0) { /* is it already set? */ if (ap->ta_bgcolor == val) 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); } #ifdef TERM_EMU /* Get cursor position. */ void get_pos(int *x, int *y) { *x = conout->Mode->CursorColumn; *y = conout->Mode->CursorRow; } /* Move cursor to x rows and y cols (0-based). */ void curs_move(int *_x, int *_y, int x, int y) { conout->SetCursorPosition(conout, x, y); if (_x != NULL) *_x = conout->Mode->CursorColumn; if (_y != NULL) *_y = conout->Mode->CursorRow; } /* Clear internal state of the terminal emulation code. */ void end_term(void) { esc = 0; argc = -1; } #endif static void efi_cons_rawputchar(int c) { int i; UINTN x, y; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); if (c == '\t') { int n; n = 8 - ((conout->Mode->CursorColumn + 8) % 8); for (i = 0; i < n; i++) efi_cons_rawputchar(' '); } else { #ifndef TERM_EMU if (c == '\n') efi_cons_efiputchar('\r'); efi_cons_efiputchar(c); #else switch (c) { case '\r': curx = 0; efi_cons_efiputchar('\r'); return; case '\n': efi_cons_efiputchar('\n'); efi_cons_efiputchar('\r'); cury++; if (cury >= y) cury--; curx = 0; return; case '\b': if (curx > 0) { efi_cons_efiputchar('\b'); curx--; } return; default: efi_cons_efiputchar(c); curx++; if (curx > x-1) { curx = 0; cury++; } if (cury > y-1) { curx = 0; cury--; } } #endif } conout->EnableCursor(conout, TRUE); } #ifdef TERM_EMU /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ static void bail_out(int c) { char buf[16], *ch; int i; if (esc) { efi_cons_rawputchar('\033'); if (esc != '\033') efi_cons_rawputchar(esc); for (i = 0; i <= argc; ++i) { sprintf(buf, "%d", args[i]); ch = buf; while (*ch) efi_cons_rawputchar(*ch++); } } efi_cons_rawputchar(c); end_term(); } /* Clear display from current position to end of screen. */ static void CD(void) { int i; UINTN x, y; get_pos(&curx, &cury); if (curx == 0 && cury == 0) { conout->ClearScreen(conout); end_term(); return; } conout->QueryMode(conout, conout->Mode->Mode, &x, &y); CL(0); /* clear current line from cursor to end */ for (i = cury + 1; i < y-1; i++) { curs_move(NULL, NULL, 0, i); CL(0); } curs_move(NULL, NULL, curx, cury); end_term(); } /* * Absolute cursor move to args[0] rows and args[1] columns * (the coordinates are 1-based). */ static void CM(void) { if (args[0] > 0) args[0]--; if (args[1] > 0) args[1]--; curs_move(&curx, &cury, args[1], args[0]); end_term(); } /* Home cursor (left top corner), also called from mode command. */ void HO(void) { argc = 1; args[0] = args[1] = 1; CM(); } /* Clear line from current position to end of line */ static void CL(int direction) { int i, len; UINTN x, y; CHAR16 *line; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); switch (direction) { case 0: /* from cursor to end */ len = x - curx + 1; break; case 1: /* from beginning to cursor */ len = curx; break; case 2: /* entire line */ len = x; break; default: /* NOTREACHED */ __unreachable(); } if (cury == y - 1) len--; line = malloc(len * sizeof (CHAR16)); if (line == NULL) { printf("out of memory\n"); return; } for (i = 0; i < len; i++) line[i] = ' '; line[len-1] = 0; if (direction != 0) curs_move(NULL, NULL, 0, cury); conout->OutputString(conout, line); /* restore cursor position */ curs_move(NULL, NULL, curx, cury); free(line); end_term(); } static void get_arg(int c) { if (argc < 0) argc = 0; args[argc] *= 10; args[argc] += c - '0'; } #endif /* Emulate basic capabilities of cons25 terminal */ static void efi_term_emu(int c) { #ifdef TERM_EMU static int ansi_col[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; int t, i; EFI_STATUS status; switch (esc) { case 0: switch (c) { case '\033': esc = c; break; default: efi_cons_rawputchar(c); break; } break; case '\033': switch (c) { case '[': esc = c; args[0] = 0; argc = -1; break; default: bail_out(c); break; } break; case '[': switch (c) { case ';': if (argc < 0) argc = 0; else if (argc + 1 >= MAXARGS) bail_out(c); else args[++argc] = 0; break; case 'H': /* ho = \E[H */ if (argc < 0) HO(); else if (argc == 1) CM(); else bail_out(c); break; case 'J': /* cd = \E[J */ if (argc < 0) CD(); else bail_out(c); break; case 'm': if (argc < 0) { fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; } for (i = 0; i <= argc; ++i) { switch (args[i]) { case 0: /* back to normal */ fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; break; case 1: /* bold */ fg_c |= 0x8; break; case 4: /* underline */ case 5: /* blink */ bg_c |= 0x8; break; case 7: /* reverse */ t = fg_c; fg_c = bg_c; bg_c = t; break; case 22: /* normal intensity */ fg_c &= ~0x8; break; case 24: /* not underline */ case 25: /* not blinking */ bg_c &= ~0x8; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: fg_c = ansi_col[args[i] - 30]; break; case 39: /* normal */ fg_c = DEFAULT_FGCOLOR; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: bg_c = ansi_col[args[i] - 40]; break; case 49: /* normal */ bg_c = DEFAULT_BGCOLOR; break; } } conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); end_term(); break; default: if (isdigit(c)) get_arg(c); else bail_out(c); break; } break; default: bail_out(c); break; } #else efi_cons_rawputchar(c); #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; } /* * When we have serial port listed in ConOut, use pre-teken emulator, * if built with. * The problem is, we can not output text on efi and comconsole when * efi also has comconsole bound. But then again, we need to have * terminal emulator for efi text mode to support the menu. * While teken is too expensive to be used on serial console, the * pre-teken emulator is light enough to be used on serial console. * * When doing multiple consoles (both serial and video), * also just use the old emulator. RB_MULTIPLE also implies * we're using a serial console. */ 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), "%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; - buffer = malloc(rows * cols * sizeof(*buffer)); - if (buffer != NULL) { - teken_set_winsize(&teken, &tp); - a = teken_get_defattr(&teken); + /* 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(screen_buffer); + screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); + if (screen_buffer != NULL) { + 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 efi_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(&teken, &attr); + 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, efi_set_colors, env_nounset); snprintf(env, sizeof(env), "%d", attr.ta_bgcolor); 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; - } - } } } + if (screen_buffer == NULL) { + conout->EnableCursor(conout, TRUE); #ifdef TERM_EMU - if (buffer == NULL) { conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, DEFAULT_BGCOLOR)); end_term(); get_pos(&curx, &cury); 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); setenv("COLUMNS", env, 1); return (true); } static int efi_cons_init(int arg) { 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); } static void input_partial(void) { unsigned i; uint32_t c; if (utf8_left == 0) return; for (i = 0; i < sizeof(utf8_partial); i++) { c = (utf8_partial >> (24 - (i << 3))) & 0xff; if (c != 0) efi_term_emu(c); } utf8_left = 0; utf8_partial = 0; } static void input_byte(uint8_t c) { if ((c & 0x80) == 0x00) { /* One-byte sequence. */ input_partial(); efi_term_emu(c); return; } if ((c & 0xe0) == 0xc0) { /* Two-byte sequence. */ input_partial(); utf8_left = 1; utf8_partial = c; return; } if ((c & 0xf0) == 0xe0) { /* Three-byte sequence. */ input_partial(); utf8_left = 2; utf8_partial = c; return; } if ((c & 0xf8) == 0xf0) { /* Four-byte sequence. */ input_partial(); utf8_left = 3; utf8_partial = c; return; } if ((c & 0xc0) == 0x80) { /* Invalid state? */ if (utf8_left == 0) { efi_term_emu(c); return; } utf8_left--; utf8_partial = (utf8_partial << 8) | c; if (utf8_left == 0) { uint32_t v, u; uint8_t b; v = 0; u = utf8_partial; b = (u >> 24) & 0xff; if (b != 0) { /* Four-byte sequence */ v = b & 0x07; b = (u >> 16) & 0xff; v = (v << 6) | (b & 0x3f); b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 16) & 0xff) != 0) { v = b & 0x0f; /* Three-byte sequence */ b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 8) & 0xff) != 0) { v = b & 0x1f; /* Two-byte sequence */ b = u & 0xff; v = (v << 6) | (b & 0x3f); } /* Send unicode char directly to console. */ efi_cons_efiputchar(v); utf8_partial = 0; } return; } /* Anything left is illegal in UTF-8 sequence. */ input_partial(); efi_term_emu(c); } void efi_cons_putchar(int c) { unsigned char ch = c; /* * Don't use Teken when we're doing pure serial, or a multiple console * with video "primary" because that's also serial. */ - if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) { + if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { input_byte(ch); return; } - teken_input(&teken, &ch, sizeof (ch)); + teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); } static int keybuf_getchar(void) { int i, c = 0; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) { c = keybuf[i]; keybuf[i] = 0; break; } } return (c); } static bool keybuf_ischar(void) { int i; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) return (true); } return (false); } /* * We are not reading input before keybuf is empty, so we are safe * just to fill keybuf from the beginning. */ static void keybuf_inschar(EFI_INPUT_KEY *key) { switch (key->ScanCode) { case SCAN_UP: /* UP */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'A'; break; case SCAN_DOWN: /* DOWN */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'B'; break; case SCAN_RIGHT: /* RIGHT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'C'; break; case SCAN_LEFT: /* LEFT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'D'; break; case SCAN_DELETE: keybuf[0] = CHAR_BACKSPACE; break; case SCAN_ESC: keybuf[0] = 0x1b; /* esc */ break; default: keybuf[0] = key->UnicodeChar; break; } } static bool efi_readkey(void) { EFI_STATUS status; EFI_INPUT_KEY key; status = conin->ReadKeyStroke(conin, &key); if (status == EFI_SUCCESS) { keybuf_inschar(&key); return (true); } return (false); } static bool efi_readkey_ex(void) { EFI_STATUS status; EFI_INPUT_KEY *kp; EFI_KEY_DATA key_data; uint32_t kss; status = coninex->ReadKeyStrokeEx(coninex, &key_data); if (status == EFI_SUCCESS) { kss = key_data.KeyState.KeyShiftState; kp = &key_data.Key; if (kss & EFI_SHIFT_STATE_VALID) { /* * quick mapping to control chars, replace with * map lookup later. */ if (kss & EFI_RIGHT_CONTROL_PRESSED || kss & EFI_LEFT_CONTROL_PRESSED) { if (kp->UnicodeChar >= 'a' && kp->UnicodeChar <= 'z') { kp->UnicodeChar -= 'a'; kp->UnicodeChar++; } } } /* * The shift state and/or toggle state may not be valid, * but we still can have ScanCode or UnicodeChar. */ if (kp->ScanCode == 0 && kp->UnicodeChar == 0) return (false); keybuf_inschar(kp); return (true); } return (false); } int efi_cons_getchar(void) { int c; if ((c = keybuf_getchar()) != 0) return (c); key_pending = 0; if (coninex == NULL) { if (efi_readkey()) return (keybuf_getchar()); } else { if (efi_readkey_ex()) return (keybuf_getchar()); } return (-1); } int efi_cons_poll(void) { EFI_STATUS status; if (keybuf_ischar() || key_pending) return (1); /* * Some EFI implementation (u-boot for example) do not support * WaitForKey(). * CheckEvent() can clear the signaled state. */ if (coninex != NULL) { if (coninex->WaitForKeyEx == NULL) { key_pending = efi_readkey_ex(); } else { status = BS->CheckEvent(coninex->WaitForKeyEx); key_pending = status == EFI_SUCCESS; } } else { if (conin->WaitForKey == NULL) { key_pending = efi_readkey(); } else { status = BS->CheckEvent(conin->WaitForKey); key_pending = status == EFI_SUCCESS; } } return (key_pending); } /* Plain direct access to EFI OutputString(). */ void efi_cons_efiputchar(int c) { CHAR16 buf[2]; EFI_STATUS status; buf[0] = c; buf[1] = 0; /* terminate string */ status = conout->TestString(conout, buf); if (EFI_ERROR(status)) buf[0] = '?'; conout->OutputString(conout, buf); } diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile index a0e7f95b8200..3cf2df933cc3 100644 --- a/stand/efi/loader/Makefile +++ b/stand/efi/loader/Makefile @@ -1,106 +1,118 @@ # $FreeBSD$ LOADER_NET_SUPPORT?= yes LOADER_MSDOS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no .include <bsd.init.mk> LOADER?= loader_${LOADER_INTERP} PROG= ${LOADER}.sym INTERNALPROG= WARNS?= 3 # architecture-specific loader code SRCS= autoload.c \ bootinfo.c \ conf.c \ copy.c \ efi_main.c \ framebuffer.c \ main.c \ self_reloc.c \ - vers.c + vers.c \ + gfx_fb.c \ + 8x16.c CFLAGS+= -I${.CURDIR}/../loader .if ${MK_LOADER_ZFS} != "no" CFLAGS+= -I${ZFSSRC} CFLAGS+= -I${SYSDIR}/contrib/openzfs/include CFLAGS+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs CFLAGS+= -DEFI_ZFS_BOOT HAVE_ZFS= yes .endif +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 + # We implement a slightly non-standard %S in that it always takes a # CHAR16 that's common in UEFI-land instead of a wchar_t. This only # seems to matter on arm64 where wchar_t defaults to an int instead # of a short. There's no good cast to use here so just ignore the # warnings for now. CWARNFLAGS.main.c+= -Wno-format .PATH: ${.CURDIR}/../loader .PATH: ${.CURDIR}/../loader/arch/${MACHINE} .include "${.CURDIR}/../loader/arch/${MACHINE}/Makefile.inc" CFLAGS+= -I${.CURDIR} CFLAGS+= -I${.CURDIR}/arch/${MACHINE} CFLAGS+= -I${EFISRC}/include CFLAGS+= -I${EFISRC}/include/${MACHINE} CFLAGS+= -I${SYSDIR}/contrib/dev/acpica/include CFLAGS+= -I${BOOTSRC}/i386/libi386 CFLAGS+= -DEFI .if defined(HAVE_FDT) && ${MK_FDT} != "no" .include "${BOOTSRC}/fdt.mk" LIBEFI_FDT= ${BOOTOBJ}/efi/fdt/libefi_fdt.a .endif # Include bcache code. HAVE_BCACHE= yes .if defined(EFI_STAGING_SIZE) CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE} .endif .if ${MK_LOADER_EFI_SECUREBOOT} != "no" CFLAGS+= -DEFI_SECUREBOOT .endif NEWVERSWHAT= "EFI loader" ${MACHINE} VERSION_FILE= ${.CURDIR}/../loader/version # Always add MI sources .include "${BOOTSRC}/loader.mk" +CLEANFILES+= 8x16.c + +8x16.c: ${SRCTOP}/contrib/terminus/ter-u16v.bdf + vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC} + FILES+= ${LOADER}.efi FILESMODE_${LOADER}.efi= ${BINMODE} .if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} LINKS+= ${BINDIR}/${LOADER}.efi ${BINDIR}/loader.efi .endif LDSCRIPT= ${.CURDIR}/../loader/arch/${MACHINE}/ldscript.${MACHINE} LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -pie CLEANFILES+= loader.efi ${LOADER}.efi: ${PROG} if ${NM} ${.ALLSRC} | grep ' U '; then \ echo "Undefined symbols in ${.ALLSRC}"; \ exit 1; \ fi SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel.dyn \ -j .rela.dyn -j .reloc -j .eh_frame -j set_Xcommand_set \ -j set_Xficl_compile_set \ --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET} LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a DPADD= ${LDR_INTERP} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSA} ${LDSCRIPT} LDADD= ${LDR_INTERP} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSA} .include <bsd.prog.mk> diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c index b1df11acd53f..108f46c5f9c4 100644 --- a/stand/efi/loader/bootinfo.c +++ b/stand/efi/loader/bootinfo.c @@ -1,552 +1,557 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * Copyright (c) 2004, 2006 Marcel Moolenaar * Copyright (c) 2014 The FreeBSD Foundation * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> #include <string.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/reboot.h> #include <sys/boot.h> #include <machine/cpufunc.h> #include <machine/elf.h> #include <machine/metadata.h> #include <machine/psl.h> #include <efi.h> #include <efilib.h> #include "bootstrap.h" #include "loader_efi.h" #if defined(__amd64__) #include <machine/specialreg.h> #endif #include "framebuffer.h" #if defined(LOADER_FDT_SUPPORT) #include <fdt_platform.h> #endif #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" #endif int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp); extern EFI_SYSTEM_TABLE *ST; static int bi_getboothowto(char *kargs) { const char *sw, *tmp; char *opts; char *console; int howto, speed, port; char buf[50]; howto = boot_parse_cmdline(kargs); howto |= boot_env_to_howto(); console = getenv("console"); if (console != NULL) { if (strcmp(console, "comconsole") == 0) howto |= RB_SERIAL; if (strcmp(console, "nullconsole") == 0) howto |= RB_MUTE; #if defined(__i386__) || defined(__amd64__) if (strcmp(console, "efi") == 0 && getenv("efi_8250_uid") != NULL && getenv("hw.uart.console") == NULL) { /* * If we found a 8250 com port and com speed, we need to * tell the kernel where the serial port is, and how * fast. Ideally, we'd get the port from ACPI, but that * isn't running in the loader. Do the next best thing * by allowing it to be set by a loader.conf variable, * either a EFI specific one, or the compatible * comconsole_port if not. PCI support is needed, but * for that we'd ideally refactor the * libi386/comconsole.c code to have identical behavior. * We only try to set the port for cases where we saw * the Serial(x) node when parsing, otherwise * specialized hardware that has Uart nodes will have a * bogus address set. * But if someone specifically setup hw.uart.console, * don't override that. */ speed = -1; port = -1; tmp = getenv("efi_com_speed"); if (tmp != NULL) speed = strtol(tmp, NULL, 0); tmp = getenv("efi_com_port"); if (tmp == NULL) tmp = getenv("comconsole_port"); if (tmp != NULL) port = strtol(tmp, NULL, 0); if (speed != -1 && port != -1) { snprintf(buf, sizeof(buf), "io:%d,br:%d", port, speed); env_setenv("hw.uart.console", EV_VOLATILE, buf, NULL, NULL); } } #endif } return (howto); } /* * Copy the environment into the load area starting at (addr). * Each variable is formatted as <name>=<value>, with a single nul * separating each variable, and a double nul terminating the environment. */ static vm_offset_t bi_copyenv(vm_offset_t start) { struct env_var *ep; vm_offset_t addr, last; size_t len; addr = last = start; /* Traverse the environment. */ for (ep = environ; ep != NULL; ep = ep->ev_next) { len = strlen(ep->ev_name); if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len) break; addr += len; if (archsw.arch_copyin("=", addr, 1) != 1) break; addr++; if (ep->ev_value != NULL) { len = strlen(ep->ev_value); if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len) break; addr += len; } if (archsw.arch_copyin("", addr, 1) != 1) break; last = ++addr; } if (archsw.arch_copyin("", last++, 1) != 1) last = start; return(last); } /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ archsw.arch_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c); \ if (c) \ archsw.arch_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(u_long)); \ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ archsw.arch_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(u_long)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c); \ COPY32(mm->md_size, a, c); \ if (c) \ archsw.arch_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(u_long)); \ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; uint64_t v; c = addr != 0; /* Start with the first module on the list, should be the kernel. */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* This must come first. */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); v = fp->f_addr; #if defined(__arm__) v -= __elfN(relocation_offset); #endif MOD_ADDR(addr, v, c); v = fp->f_size; MOD_SIZE(addr, v, c); for (md = fp->f_metadata; md != NULL; md = md->md_next) if (!(md->md_type & MODINFOMD_NOCOPY)) MOD_METADATA(addr, md, c); } MOD_END(addr, c); return(addr); } static EFI_STATUS efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver) { EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap; EFI_STATUS ret; int curr, ndesc, nset; nset = 0; desc = mm; ndesc = sz / mmsz; vmap = malloc(sz); if (vmap == NULL) /* This isn't really an EFI error case, but pretend it is */ return (EFI_OUT_OF_RESOURCES); viter = vmap; for (curr = 0; curr < ndesc; curr++, desc = NextMemoryDescriptor(desc, mmsz)) { if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) { ++nset; desc->VirtualStart = desc->PhysicalStart; *viter = *desc; viter = NextMemoryDescriptor(viter, mmsz); } } ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap); free(vmap); return (ret); } static int bi_load_efi_data(struct preloaded_file *kfp) { EFI_MEMORY_DESCRIPTOR *mm; EFI_PHYSICAL_ADDRESS addr = 0; EFI_STATUS status; const char *efi_novmap; size_t efisz; UINTN efi_mapkey; UINTN dsz, pages, retry, sz; UINT32 mmver; struct efi_map_header *efihdr; bool do_vmap; #if defined(__amd64__) || defined(__aarch64__) struct efi_fb efifb; if (efi_find_framebuffer(&efifb) == 0) { printf("EFI framebuffer information:\n"); printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr, efifb.fb_size); printf("dimensions %d x %d\n", efifb.fb_width, efifb.fb_height); printf("stride %d\n", efifb.fb_stride); printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue, efifb.fb_mask_reserved); file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb); } #endif do_vmap = true; efi_novmap = getenv("efi_disable_vmap"); if (efi_novmap != NULL) do_vmap = strcasecmp(efi_novmap, "YES") != 0; efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; /* * Assign size of EFI_MEMORY_DESCRIPTOR to keep compatible with * u-boot which doesn't fill this value when buffer for memory * descriptors is too small (eg. 0 to obtain memory map size) */ dsz = sizeof(EFI_MEMORY_DESCRIPTOR); /* * Allocate enough pages to hold the bootinfo block and the * memory map EFI will return to us. The memory map has an * unknown size, so we have to determine that first. Note that * the AllocatePages call can itself modify the memory map, so * we have to take that into account as well. The changes to * the memory map are caused by splitting a range of free * memory into two, so that one is marked as being loader * data. */ sz = 0; /* * Matthew Garrett has observed at least one system changing the * memory map when calling ExitBootServices, causing it to return an * error, probably because callbacks are allocating memory. * So we need to retry calling it at least once. */ for (retry = 2; retry > 0; retry--) { for (;;) { status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &dsz, &mmver); if (!EFI_ERROR(status)) break; if (status != EFI_BUFFER_TOO_SMALL) { printf("%s: GetMemoryMap error %lu\n", __func__, EFI_ERROR_CODE(status)); return (EINVAL); } if (addr != 0) BS->FreePages(addr, pages); /* Add 10 descriptors to the size to allow for * fragmentation caused by calling AllocatePages */ sz += (10 * dsz); pages = EFI_SIZE_TO_PAGES(sz + efisz); status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, &addr); if (EFI_ERROR(status)) { printf("%s: AllocatePages error %lu\n", __func__, EFI_ERROR_CODE(status)); return (ENOMEM); } /* * Read the memory map and stash it after bootinfo. Align the * memory map on a 16-byte boundary (the bootinfo block is page * aligned). */ efihdr = (struct efi_map_header *)(uintptr_t)addr; mm = (void *)((uint8_t *)efihdr + efisz); sz = (EFI_PAGE_SIZE * pages) - efisz; } status = BS->ExitBootServices(IH, efi_mapkey); if (!EFI_ERROR(status)) break; } if (retry == 0) { BS->FreePages(addr, pages); printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); return (EINVAL); } /* * This may be disabled by setting efi_disable_vmap in * loader.conf(5). By default we will setup the virtual * map entries. */ if (do_vmap) efi_do_vmap(mm, sz, dsz, mmver); efihdr->memory_size = sz; efihdr->descriptor_size = dsz; efihdr->descriptor_version = mmver; file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, efihdr); return (0); } /* * Load the information expected by an amd64 kernel. * * - The 'boothowto' argument is constructed. * - The 'bootdev' argument is constructed. * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) { struct preloaded_file *xp, *kfp; struct devdesc *rootdev; struct file_metadata *md; vm_offset_t addr; uint64_t kernend; uint64_t envp; vm_offset_t size; char *rootdevname; int howto; #if defined(LOADER_FDT_SUPPORT) vm_offset_t dtbp; int dtb_size; #endif #if defined(__arm__) vm_offset_t vaddr; size_t i; /* * These metadata addreses must be converted for kernel after * relocation. */ uint32_t mdt[] = { MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, - MODINFOMD_ENVP, + MODINFOMD_ENVP, MODINFOMD_FONT, #if defined(LOADER_FDT_SUPPORT) MODINFOMD_DTBP #endif }; #endif howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied * device. This should perhaps go to MI code and/or have $rootdev * tested/set by MI code before launching the kernel. */ rootdevname = getenv("rootdev"); archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { printf("Can't determine root device.\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(efi_fmtdev((void *)rootdev)); addr = 0; for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { if (addr < (xp->f_addr + xp->f_size)) addr = xp->f_addr + xp->f_size; } /* 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); /* Pad to a page boundary. */ addr = roundup(addr, PAGE_SIZE); #if defined(LOADER_FDT_SUPPORT) /* Handle device tree blob */ dtbp = addr; dtb_size = fdt_copy(addr); /* Pad to a page boundary */ if (dtb_size) addr += roundup(dtb_size, PAGE_SIZE); #endif kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); 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 bi_load_efi_data(kfp); /* Figure out the size and location of the metadata. */ *modulep = addr; size = bi_copymodules(0); kernend = roundup(addr + size, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); #if defined(__arm__) *modulep -= __elfN(relocation_offset); /* Do relocation fixup on metadata of each module. */ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { for (i = 0; i < nitems(mdt); i++) { md = file_findmetadata(xp, mdt[i]); if (md) { bcopy(md->md_data, &vaddr, sizeof vaddr); vaddr -= __elfN(relocation_offset); bcopy(&vaddr, md->md_data, sizeof vaddr); } } } #endif /* Copy module list and metadata. */ (void)bi_copymodules(addr); return (0); } diff --git a/stand/efi/loader/framebuffer.c b/stand/efi/loader/framebuffer.c index dce49c69ab0e..fccbd82a09f7 100644 --- a/stand/efi/loader/framebuffer.c +++ b/stand/efi/loader/framebuffer.c @@ -1,768 +1,771 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Benno Rice under sponsorship from * the FreeBSD Foundation. * 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <bootstrap.h> #include <sys/endian.h> #include <sys/param.h> #include <stand.h> #include <efi.h> #include <efilib.h> #include <efiuga.h> #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; 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 u_int efifb_color_depth(struct efi_fb *efifb) { uint32_t mask; u_int depth; mask = efifb->fb_mask_red | efifb->fb_mask_green | efifb->fb_mask_blue | efifb->fb_mask_reserved; if (mask == 0) return (0); for (depth = 1; mask != 1; depth++) mask >>= 1; return (depth); } static int efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, EFI_PIXEL_BITMASK *pixinfo) { int result; result = 0; switch (pixfmt) { case PixelRedGreenBlueReserved8BitPerColor: efifb->fb_mask_red = 0x000000ff; efifb->fb_mask_green = 0x0000ff00; efifb->fb_mask_blue = 0x00ff0000; efifb->fb_mask_reserved = 0xff000000; break; case PixelBlueGreenRedReserved8BitPerColor: efifb->fb_mask_red = 0x00ff0000; efifb->fb_mask_green = 0x0000ff00; efifb->fb_mask_blue = 0x000000ff; efifb->fb_mask_reserved = 0xff000000; break; case PixelBitMask: efifb->fb_mask_red = pixinfo->RedMask; efifb->fb_mask_green = pixinfo->GreenMask; efifb->fb_mask_blue = pixinfo->BlueMask; efifb->fb_mask_reserved = pixinfo->ReservedMask; break; default: result = 1; break; } return (result); } static int efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) { int result; efifb->fb_addr = mode->FrameBufferBase; efifb->fb_size = mode->FrameBufferSize; efifb->fb_height = info->VerticalResolution; efifb->fb_width = info->HorizontalResolution; efifb->fb_stride = info->PixelsPerScanLine; result = efifb_mask_from_pixfmt(efifb, info->PixelFormat, &info->PixelInformation); return (result); } static ssize_t efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line, EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) { EFI_UGA_PIXEL pix0, pix1; uint8_t *data1, *data2; size_t count, maxcount = 1024; ssize_t ofs; EFI_STATUS status; u_int idx; status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, 0, line, 0, 0, 1, 1, 0); if (EFI_ERROR(status)) { printf("UGA BLT operation failed (video->buffer)"); return (-1); } pix1.Red = ~pix0.Red; pix1.Green = ~pix0.Green; pix1.Blue = ~pix0.Blue; pix1.Reserved = 0; data1 = calloc(maxcount, 2); if (data1 == NULL) { printf("Unable to allocate memory"); return (-1); } data2 = data1 + maxcount; ofs = 0; while (size > 0) { count = min(size, maxcount); status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, data1); if (EFI_ERROR(status)) { printf("Error reading frame buffer (before)"); goto fail; } status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, 0, 0, 0, line, 1, 1, 0); if (EFI_ERROR(status)) { printf("UGA BLT operation failed (modify)"); goto fail; } status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, data2); if (EFI_ERROR(status)) { printf("Error reading frame buffer (after)"); goto fail; } status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, 0, 0, 0, line, 1, 1, 0); if (EFI_ERROR(status)) { printf("UGA BLT operation failed (restore)"); goto fail; } for (idx = 0; idx < count; idx++) { if (data1[idx] != data2[idx]) { free(data1); return (ofs + (idx & ~3)); } } ofs += count; size -= count; } printf("No change detected in frame buffer"); fail: printf(" -- error %lu\n", EFI_ERROR_CODE(status)); free(data1); return (-1); } static EFI_PCI_IO_PROTOCOL * efifb_uga_get_pciio(void) { EFI_PCI_IO_PROTOCOL *pciio; EFI_HANDLE *buf, *hp; EFI_STATUS status; UINTN bufsz; /* Get all handles that support the UGA protocol. */ bufsz = 0; status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL); if (status != EFI_BUFFER_TOO_SMALL) return (NULL); buf = malloc(bufsz); status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf); if (status != EFI_SUCCESS) { free(buf); return (NULL); } bufsz /= sizeof(EFI_HANDLE); /* Get the PCI I/O interface of the first handle that supports it. */ pciio = NULL; for (hp = buf; hp < buf + bufsz; hp++) { status = OpenProtocolByHandle(*hp, &pciio_guid, (void **)&pciio); if (status == EFI_SUCCESS) { free(buf); return (pciio); } } free(buf); return (NULL); } static EFI_STATUS efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp, uint64_t *sizep) { uint8_t *resattr; uint64_t addr, size; EFI_STATUS status; u_int bar; if (pciio == NULL) return (EFI_DEVICE_ERROR); /* Attempt to get the frame buffer address (imprecise). */ *addrp = 0; *sizep = 0; for (bar = 0; bar < 6; bar++) { status = pciio->GetBarAttributes(pciio, bar, NULL, (void **)&resattr); if (status != EFI_SUCCESS) continue; /* XXX magic offsets and constants. */ if (resattr[0] == 0x87 && resattr[3] == 0) { /* 32-bit address space descriptor (MEMIO) */ addr = le32dec(resattr + 10); size = le32dec(resattr + 22); } else if (resattr[0] == 0x8a && resattr[3] == 0) { /* 64-bit address space descriptor (MEMIO) */ addr = le64dec(resattr + 14); size = le64dec(resattr + 38); } else { addr = 0; size = 0; } BS->FreePool(resattr); if (addr == 0 || size == 0) continue; /* We assume the largest BAR is the frame buffer. */ if (size > *sizep) { *addrp = addr; *sizep = size; } } return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0); } static int efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) { EFI_PCI_IO_PROTOCOL *pciio; char *ev, *p; EFI_STATUS status; ssize_t offset; uint64_t fbaddr; uint32_t horiz, vert, stride; uint32_t np, depth, refresh; status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); if (EFI_ERROR(status)) return (1); efifb->fb_height = vert; efifb->fb_width = horiz; /* Paranoia... */ if (efifb->fb_height == 0 || efifb->fb_width == 0) return (1); /* The color masks are fixed AFAICT. */ efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, NULL); /* pciio can be NULL on return! */ pciio = efifb_uga_get_pciio(); /* Try to find the frame buffer. */ status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr, &efifb->fb_size); if (EFI_ERROR(status)) { efifb->fb_addr = 0; efifb->fb_size = 0; } /* * There's no reliable way to detect the frame buffer or the * offset within the frame buffer of the visible region, nor * the stride. Our only option is to look at the system and * fill in the blanks based on that. Luckily, UGA was mostly * only used on Apple hardware. */ offset = -1; ev = getenv("smbios.system.maker"); if (ev != NULL && !strcmp(ev, "Apple Inc.")) { ev = getenv("smbios.system.product"); if (ev != NULL && !strcmp(ev, "iMac7,1")) { /* These are the expected values we should have. */ horiz = 1680; vert = 1050; fbaddr = 0xc0000000; /* These are the missing bits. */ offset = 0x10000; stride = 1728; } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) { /* These are the expected values we should have. */ horiz = 1280; vert = 800; fbaddr = 0xc0000000; /* These are the missing bits. */ offset = 0x0; stride = 2048; } } /* * If this is hardware we know, make sure that it looks familiar * before we accept our hardcoded values. */ if (offset >= 0 && efifb->fb_width == horiz && efifb->fb_height == vert && efifb->fb_addr == fbaddr) { efifb->fb_addr += offset; efifb->fb_size -= offset; efifb->fb_stride = stride; return (0); } else if (offset >= 0) { printf("Hardware make/model known, but graphics not " "as expected.\n"); printf("Console may not work!\n"); } /* * The stride is equal or larger to the width. Often it's the * next larger power of two. We'll start with that... */ efifb->fb_stride = efifb->fb_width; do { np = efifb->fb_stride & (efifb->fb_stride - 1); if (np) { efifb->fb_stride |= (np - 1); efifb->fb_stride++; } } while (np); ev = getenv("hw.efifb.address"); if (ev == NULL) { if (efifb->fb_addr == 0) { printf("Please set hw.efifb.address and " "hw.efifb.stride.\n"); return (1); } /* * The visible part of the frame buffer may not start at * offset 0, so try to detect it. Note that we may not * always be able to read from the frame buffer, which * means that we may not be able to detect anything. In * that case, we would take a long time scanning for a * pixel change in the frame buffer, which would have it * appear that we're hanging, so we limit the scan to * 1/256th of the frame buffer. This number is mostly * based on PR 202730 and the fact that on a MacBoook, * where we can't read from the frame buffer the offset * of the visible region is 0. In short: we want to scan * enough to handle all adapters that have an offset * larger than 0 and we want to scan as little as we can * to not appear to hang when we can't read from the * frame buffer. */ offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr, efifb->fb_size >> 8); if (offset == -1) { printf("Unable to reliably detect frame buffer.\n"); } else if (offset > 0) { efifb->fb_addr += offset; efifb->fb_size -= offset; } } else { offset = 0; efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; efifb->fb_addr = strtoul(ev, &p, 0); if (*p != '\0') return (1); } ev = getenv("hw.efifb.stride"); if (ev == NULL) { if (pciio != NULL && offset != -1) { /* Determine the stride. */ offset = efifb_uga_find_pixel(uga, 1, pciio, efifb->fb_addr, horiz * 8); if (offset != -1) efifb->fb_stride = offset >> 2; } else { printf("Unable to reliably detect the stride.\n"); } } else { efifb->fb_stride = strtoul(ev, &p, 0); if (*p != '\0') return (1); } /* * We finalized on the stride, so recalculate the size of the * frame buffer. */ efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; return (0); } int efi_find_framebuffer(struct efi_fb *efifb) { EFI_GRAPHICS_OUTPUT *gop; EFI_UGA_DRAW_PROTOCOL *uga; EFI_STATUS status; status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); if (status == EFI_SUCCESS) return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); if (status == EFI_SUCCESS) return (efifb_from_uga(efifb, uga)); return (1); } static void print_efifb(int mode, struct efi_fb *efifb, int verbose) { u_int depth; if (mode >= 0) printf("mode %d: ", mode); depth = efifb_color_depth(efifb); printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height, depth, efifb->fb_stride); if (verbose) { printf("\n frame buffer: address=%jx, size=%jx", (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size); printf("\n color mask: R=%08x, G=%08x, B=%08x\n", efifb->fb_mask_red, efifb->fb_mask_green, efifb->fb_mask_blue); } } static bool efi_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 efi_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("efi_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 (efi_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); } static int gop_autoresize(EFI_GRAPHICS_OUTPUT *gop) { struct efi_fb efifb; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; EFI_STATUS status; UINTN infosz; UINT32 best_mode, currdim, maxdim, mode; int height, max_height, max_width, width; best_mode = maxdim = 0; efi_get_max_resolution(&max_width, &max_height); for (mode = 0; mode < gop->Mode->MaxMode; mode++) { status = gop->QueryMode(gop, mode, &infosz, &info); if (EFI_ERROR(status)) continue; efifb_from_gop(&efifb, gop->Mode, info); width = info->HorizontalResolution; height = info->VerticalResolution; currdim = width * height; if (currdim > maxdim) { if ((max_width != 0 && width > max_width) || (max_height != 0 && height > max_height)) continue; maxdim = currdim; best_mode = mode; } } if (maxdim != 0) { status = gop->SetMode(gop, best_mode); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof(command_errbuf), "gop_autoresize: Unable to set mode to %u (error=%lu)", mode, EFI_ERROR_CODE(status)); return (CMD_ERROR); } - (void) efi_cons_update_mode(); + (void) cons_update_mode(true); } return (CMD_OK); } static int text_autoresize() { SIMPLE_TEXT_OUTPUT_INTERFACE *conout; EFI_STATUS status; UINTN i, max_dim, best_mode, cols, rows; conout = ST->ConOut; max_dim = best_mode = 0; for (i = 0; i < conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; if (cols * rows > max_dim) { max_dim = cols * rows; best_mode = i; } } if (max_dim > 0) conout->SetMode(conout, best_mode); - (void) efi_cons_update_mode(); + (void) cons_update_mode(true); return (CMD_OK); } static int uga_autoresize(EFI_UGA_DRAW_PROTOCOL *uga) { return (text_autoresize()); } COMMAND_SET(efi_autoresize, "efi-autoresizecons", "EFI Auto-resize Console", command_autoresize); static int command_autoresize(int argc, char *argv[]) { EFI_GRAPHICS_OUTPUT *gop; EFI_UGA_DRAW_PROTOCOL *uga; char *textmode; EFI_STATUS status; u_int mode; textmode = getenv("hw.vga.textmode"); /* If it's set and non-zero, we'll select a console mode instead */ if (textmode != NULL && strcmp(textmode, "0") != 0) return (text_autoresize()); gop = NULL; uga = NULL; status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); if (EFI_ERROR(status) == 0) return (gop_autoresize(gop)); status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); if (EFI_ERROR(status) == 0) return (uga_autoresize(uga)); snprintf(command_errbuf, sizeof(command_errbuf), "%s: Neither Graphics Output Protocol nor Universal Graphics Adapter present", argv[0]); /* * Default to text_autoresize if we have neither GOP or UGA. This won't * give us the most ideal resolution, but it will at least leave us * functional rather than failing the boot for an objectively bad * reason. */ return (text_autoresize()); } COMMAND_SET(gop, "gop", "graphics output protocol", command_gop); static int command_gop(int argc, char *argv[]) { struct efi_fb efifb; EFI_GRAPHICS_OUTPUT *gop; EFI_STATUS status; u_int mode; status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof(command_errbuf), "%s: Graphics Output Protocol not present (error=%lu)", argv[0], EFI_ERROR_CODE(status)); return (CMD_ERROR); } if (argc < 2) goto usage; if (!strcmp(argv[1], "set")) { char *cp; if (argc != 3) goto usage; mode = strtol(argv[2], &cp, 0); if (cp[0] != '\0') { sprintf(command_errbuf, "mode is an integer"); return (CMD_ERROR); } status = gop->SetMode(gop, mode); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof(command_errbuf), "%s: Unable to set mode to %u (error=%lu)", 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); print_efifb(gop->Mode->Mode, &efifb, 1); printf("\n"); } else if (!strcmp(argv[1], "list")) { EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; UINTN infosz; if (argc != 2) goto usage; pager_open(); for (mode = 0; mode < gop->Mode->MaxMode; mode++) { status = gop->QueryMode(gop, mode, &infosz, &info); if (EFI_ERROR(status)) continue; efifb_from_gop(&efifb, gop->Mode, info); print_efifb(mode, &efifb, 0); if (pager_output("\n")) break; } pager_close(); } return (CMD_OK); 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); } COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga); static int command_uga(int argc, char *argv[]) { struct efi_fb efifb; EFI_UGA_DRAW_PROTOCOL *uga; EFI_STATUS status; status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof(command_errbuf), "%s: UGA Protocol not present (error=%lu)", argv[0], EFI_ERROR_CODE(status)); return (CMD_ERROR); } if (argc != 1) goto usage; if (efifb_from_uga(&efifb, uga) != CMD_OK) { snprintf(command_errbuf, sizeof(command_errbuf), "%s: Unable to get UGA information", argv[0]); return (CMD_ERROR); } print_efifb(-1, &efifb, 1); printf("\n"); return (CMD_OK); usage: snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]); return (CMD_ERROR); } diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c index aad285f59092..ca41cd4a2610 100644 --- a/stand/efi/loader/main.c +++ b/stand/efi/loader/main.c @@ -1,1631 +1,1632 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Copyright (c) 2016-2019 Netflix, Inc. written by M. Warner Losh * * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> #include <sys/disk.h> #include <sys/param.h> #include <sys/reboot.h> #include <sys/boot.h> #ifdef EFI_ZFS_BOOT #include <sys/zfs_bootenv.h> #endif #include <paths.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <stdint.h> #include <string.h> #include <setjmp.h> #include <disk.h> #include <dev_net.h> #include <net.h> #include <efi.h> #include <efilib.h> #include <efichar.h> #include <uuid.h> #include <bootstrap.h> #include <smbios.h> #include "efizfs.h" #include "loader_efi.h" struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID lzmadecomp = LZMA_DECOMPRESSION_GUID; EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID; EFI_GUID esrt = ESRT_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; /* * Number of seconds to wait for a keystroke before exiting with failure * in the event no currdev is found. -2 means always break, -1 means * never break, 0 means poll once and then reboot, > 0 means wait for * that many seconds. "fail_timeout" can be set in the environment as * well. */ static int fail_timeout = 5; /* * Current boot variable */ UINT16 boot_current; /* * Image that we booted from. */ EFI_LOADED_IMAGE *boot_img; static bool has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; bool retval = false; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = OpenProtocolByHandle(*walker, &devid, (void **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = true; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = true; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } static void set_currdev(const char *devname) { /* * Don't execute hooks here; we may need to try setting these more than * once here if we're probing for the ZFS pool we're supposed to boot. * The currdev hook is intended to just validate user input anyways, * while the loaddev hook makes it immutable once we've determined what * the proper currdev is. */ env_setenv("currdev", EV_VOLATILE | EV_NOHOOK, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE | EV_NOHOOK, devname, env_noset, env_nounset); } static void set_currdev_devdesc(struct devdesc *currdev) { const char *devname; devname = efi_fmtdev(currdev); printf("Setting currdev to %s\n", devname); set_currdev(devname); } static void set_currdev_devsw(struct devsw *dev, int unit) { struct devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; set_currdev_devdesc(&currdev); } static void set_currdev_pdinfo(pdinfo_t *dp) { /* * Disks are special: they have partitions. if the parent * pointer is non-null, we're a partition not a full disk * and we need to adjust currdev appropriately. */ if (dp->pd_devsw->dv_type == DEVT_DISK) { struct disk_devdesc currdev; currdev.dd.d_dev = dp->pd_devsw; if (dp->pd_parent == NULL) { currdev.dd.d_unit = dp->pd_unit; currdev.d_slice = D_SLICENONE; currdev.d_partition = D_PARTNONE; } else { currdev.dd.d_unit = dp->pd_parent->pd_unit; currdev.d_slice = dp->pd_unit; currdev.d_partition = D_PARTISGPT; /* XXX Assumes GPT */ } set_currdev_devdesc((struct devdesc *)&currdev); } else { set_currdev_devsw(dp->pd_devsw, dp->pd_unit); } } static bool sanity_check_currdev(void) { struct stat st; return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 || #ifdef PATH_BOOTABLE_TOKEN stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */ #endif stat(PATH_KERNEL, &st) == 0); } #ifdef EFI_ZFS_BOOT static bool probe_zfs_currdev(uint64_t guid) { char *devname; struct zfs_devdesc currdev; char *buf = NULL; bool rv; currdev.dd.d_dev = &zfs_dev; currdev.dd.d_unit = 0; currdev.pool_guid = guid; currdev.root_guid = 0; set_currdev_devdesc((struct devdesc *)&currdev); devname = efi_fmtdev(&currdev); init_zfs_boot_options(devname); rv = sanity_check_currdev(); if (rv) { buf = malloc(VDEV_PAD_SIZE); if (buf != NULL) { if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf, VDEV_PAD_SIZE) == 0) { printf("zfs bootonce: %s\n", buf); set_currdev(buf); setenv("zfs-bootonce", buf, 1); } free(buf); (void) zfs_attach_nvstore(&currdev); } } return (rv); } #endif static bool try_as_currdev(pdinfo_t *hd, pdinfo_t *pp) { uint64_t guid; #ifdef EFI_ZFS_BOOT /* * If there's a zpool on this device, try it as a ZFS * filesystem, which has somewhat different setup than all * other types of fs due to imperfect loader integration. * This all stems from ZFS being both a device (zpool) and * a filesystem, plus the boot env feature. */ if (efizfs_get_guid_by_handle(pp->pd_handle, &guid)) return (probe_zfs_currdev(guid)); #endif /* * All other filesystems just need the pdinfo * initialized in the standard way. */ set_currdev_pdinfo(pp); return (sanity_check_currdev()); } /* * Sometimes we get filenames that are all upper case * and/or have backslashes in them. Filter all this out * if it looks like we need to do so. */ static void fix_dosisms(char *p) { while (*p) { if (isupper(*p)) *p = tolower(*p); else if (*p == '\\') *p = '/'; p++; } } #define SIZE(dp, edp) (size_t)((intptr_t)(void *)edp - (intptr_t)(void *)dp) enum { BOOT_INFO_OK = 0, BAD_CHOICE = 1, NOT_SPECIFIC = 2 }; static int match_boot_info(char *boot_info, size_t bisz) { uint32_t attr; uint16_t fplen; size_t len; char *walker, *ep; EFI_DEVICE_PATH *dp, *edp, *first_dp, *last_dp; pdinfo_t *pp; CHAR16 *descr; char *kernel = NULL; FILEPATH_DEVICE_PATH *fp; struct stat st; CHAR16 *text; /* * FreeBSD encodes its boot loading path into the boot loader * BootXXXX variable. We look for the last one in the path * and use that to load the kernel. However, if we only find * one DEVICE_PATH, then there's nothing specific and we should * fall back. * * In an ideal world, we'd look at the image handle we were * passed, match up with the loader we are and then return the * next one in the path. This would be most flexible and cover * many chain booting scenarios where you need to use this * boot loader to get to the next boot loader. However, that * doesn't work. We rarely have the path to the image booted * (just the device) so we can't count on that. So, we do the * next best thing: we look through the device path(s) passed * in the BootXXXX variable. If there's only one, we return * NOT_SPECIFIC. Otherwise, we look at the last one and try to * load that. If we can, we return BOOT_INFO_OK. Otherwise we * return BAD_CHOICE for the caller to sort out. */ if (bisz < sizeof(attr) + sizeof(fplen) + sizeof(CHAR16)) return NOT_SPECIFIC; walker = boot_info; ep = walker + bisz; memcpy(&attr, walker, sizeof(attr)); walker += sizeof(attr); memcpy(&fplen, walker, sizeof(fplen)); walker += sizeof(fplen); descr = (CHAR16 *)(intptr_t)walker; len = ucs2len(descr); walker += (len + 1) * sizeof(CHAR16); last_dp = first_dp = dp = (EFI_DEVICE_PATH *)walker; edp = (EFI_DEVICE_PATH *)(walker + fplen); if ((char *)edp > ep) return NOT_SPECIFIC; while (dp < edp && SIZE(dp, edp) > sizeof(EFI_DEVICE_PATH)) { text = efi_devpath_name(dp); if (text != NULL) { printf(" BootInfo Path: %S\n", text); efi_free_devpath_name(text); } last_dp = dp; dp = (EFI_DEVICE_PATH *)((char *)dp + efi_devpath_length(dp)); } /* * If there's only one item in the list, then nothing was * specified. Or if the last path doesn't have a media * path in it. Those show up as various VenHw() nodes * which are basically opaque to us. Don't count those * as something specifc. */ if (last_dp == first_dp) { printf("Ignoring Boot%04x: Only one DP found\n", boot_current); return NOT_SPECIFIC; } if (efi_devpath_to_media_path(last_dp) == NULL) { printf("Ignoring Boot%04x: No Media Path\n", boot_current); return NOT_SPECIFIC; } /* * OK. At this point we either have a good path or a bad one. * Let's check. */ pp = efiblk_get_pdinfo_by_device_path(last_dp); if (pp == NULL) { printf("Ignoring Boot%04x: Device Path not found\n", boot_current); return BAD_CHOICE; } set_currdev_pdinfo(pp); if (!sanity_check_currdev()) { printf("Ignoring Boot%04x: sanity check failed\n", boot_current); return BAD_CHOICE; } /* * OK. We've found a device that matches, next we need to check the last * component of the path. If it's a file, then we set the default kernel * to that. Otherwise, just use this as the default root. * * Reminder: we're running very early, before we've parsed the defaults * file, so we may need to have a hack override. */ dp = efi_devpath_last_node(last_dp); if (DevicePathType(dp) != MEDIA_DEVICE_PATH || DevicePathSubType(dp) != MEDIA_FILEPATH_DP) { printf("Using Boot%04x for root partition\n", boot_current); return (BOOT_INFO_OK); /* use currdir, default kernel */ } fp = (FILEPATH_DEVICE_PATH *)dp; ucs2_to_utf8(fp->PathName, &kernel); if (kernel == NULL) { printf("Not using Boot%04x: can't decode kernel\n", boot_current); return (BAD_CHOICE); } if (*kernel == '\\' || isupper(*kernel)) fix_dosisms(kernel); if (stat(kernel, &st) != 0) { free(kernel); printf("Not using Boot%04x: can't find %s\n", boot_current, kernel); return (BAD_CHOICE); } setenv("kernel", kernel, 1); free(kernel); text = efi_devpath_name(last_dp); if (text) { printf("Using Boot%04x %S + %s\n", boot_current, text, kernel); efi_free_devpath_name(text); } return (BOOT_INFO_OK); } /* * Look at the passed-in boot_info, if any. If we find it then we need * to see if we can find ourselves in the boot chain. If we can, and * there's another specified thing to boot next, assume that the file * is loaded from / and use that for the root filesystem. If can't * find the specified thing, we must fail the boot. If we're last on * the list, then we fallback to looking for the first available / * candidate (ZFS, if there's a bootable zpool, otherwise a UFS * partition that has either /boot/defaults/loader.conf on it or * /boot/kernel/kernel (the default kernel) that we can use. * * We always fail if we can't find the right thing. However, as * a concession to buggy UEFI implementations, like u-boot, if * we have determined that the host is violating the UEFI boot * manager protocol, we'll signal the rest of the program that * a drop to the OK boot loader prompt is possible. */ static int find_currdev(bool do_bootmgr, bool is_last, char *boot_info, size_t boot_info_sz) { pdinfo_t *dp, *pp; EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; CHAR16 *text; struct devsw *dev; int unit; uint64_t extra; int rv; char *rootdev; /* * First choice: if rootdev is already set, use that, even if * it's wrong. */ rootdev = getenv("rootdev"); if (rootdev != NULL) { printf(" Setting currdev to configured rootdev %s\n", rootdev); set_currdev(rootdev); return (0); } /* * Second choice: If uefi_rootdev is set, translate that UEFI device * path to the loader's internal name and use that. */ do { rootdev = getenv("uefi_rootdev"); if (rootdev == NULL) break; devpath = efi_name_to_devpath(rootdev); if (devpath == NULL) break; dp = efiblk_get_pdinfo_by_device_path(devpath); efi_devpath_free(devpath); if (dp == NULL) break; printf(" Setting currdev to UEFI path %s\n", rootdev); set_currdev_pdinfo(dp); return (0); } while (0); /* * Third choice: If we can find out image boot_info, and there's * a follow-on boot image in that boot_info, use that. In this * case root will be the partition specified in that image and * we'll load the kernel specified by the file path. Should there * not be a filepath, we use the default. This filepath overrides * loader.conf. */ if (do_bootmgr) { rv = match_boot_info(boot_info, boot_info_sz); switch (rv) { case BOOT_INFO_OK: /* We found it */ return (0); case BAD_CHOICE: /* specified file not found -> error */ /* XXX do we want to have an escape hatch for last in boot order? */ return (ENOENT); } /* Nothing specified, try normal match */ } #ifdef EFI_ZFS_BOOT /* * Did efi_zfs_probe() detect the boot pool? If so, use the zpool * it found, if it's sane. ZFS is the only thing that looks for * disks and pools to boot. This may change in the future, however, * if we allow specifying which pool to boot from via UEFI variables * rather than the bootenv stuff that FreeBSD uses today. */ if (pool_guid != 0) { printf("Trying ZFS pool\n"); if (probe_zfs_currdev(pool_guid)) return (0); } #endif /* EFI_ZFS_BOOT */ /* * Try to find the block device by its handle based on the * image we're booting. If we can't find a sane partition, * search all the other partitions of the disk. We do not * search other disks because it's a violation of the UEFI * boot protocol to do so. We fail and let UEFI go on to * the next candidate. */ dp = efiblk_get_pdinfo_by_handle(boot_img->DeviceHandle); if (dp != NULL) { text = efi_devpath_name(dp->pd_devpath); if (text != NULL) { printf("Trying ESP: %S\n", text); efi_free_devpath_name(text); } set_currdev_pdinfo(dp); if (sanity_check_currdev()) return (0); if (dp->pd_parent != NULL) { pdinfo_t *espdp = dp; dp = dp->pd_parent; STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { /* Already tried the ESP */ if (espdp == pp) continue; /* * Roll up the ZFS special case * for those partitions that have * zpools on them. */ text = efi_devpath_name(pp->pd_devpath); if (text != NULL) { printf("Trying: %S\n", text); efi_free_devpath_name(text); } if (try_as_currdev(dp, pp)) return (0); } } } /* * Try the device handle from our loaded image first. If that * fails, use the device path from the loaded image and see if * any of the nodes in that path match one of the enumerated * handles. Currently, this handle list is only for netboot. */ if (efi_handle_lookup(boot_img->DeviceHandle, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); return (ENOENT); } static bool interactive_interrupt(const char *msg) { time_t now, then, last; last = 0; now = then = getsecs(); printf("%s\n", msg); if (fail_timeout == -2) /* Always break to OK */ return (true); if (fail_timeout == -1) /* Never break to OK */ return (false); do { if (last != now) { printf("press any key to interrupt reboot in %d seconds\r", fail_timeout - (int)(now - then)); last = now; } /* XXX no pause or timeout wait for char */ if (ischar()) return (true); now = getsecs(); } while (now - then < fail_timeout); return (false); } static int parse_args(int argc, CHAR16 *argv[]) { int i, j, howto; bool vargood; char var[128]; /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. Or the optional argument in the * boot environment was used to pass these arguments in (in which case * neither /boot.config nor /boot/config are consulted). * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this * method is flawed for non-ASCII characters). */ howto = 0; for (i = 1; i < argc; i++) { cpy16to8(argv[i], var, sizeof(var)); howto |= boot_parse_arg(var); } return (howto); } static void setenv_int(const char *key, int val) { char buf[20]; snprintf(buf, sizeof(buf), "%d", val); setenv(key, buf, 1); } /* * Parse ConOut (the list of consoles active) and see if we can find a * serial port and/or a video port. It would be nice to also walk the * ACPI name space to map the UID for the serial port to a port. The * latter is especially hard. */ int parse_uefi_con_out(void) { int how, rv; int vid_seen = 0, com_seen = 0, seen = 0; size_t sz; char buf[4096], *ep; EFI_DEVICE_PATH *node; ACPI_HID_DEVICE_PATH *acpi; UART_DEVICE_PATH *uart; bool pci_pending; how = 0; sz = sizeof(buf); rv = efi_global_getenv("ConOut", buf, &sz); if (rv != EFI_SUCCESS) { /* If we don't have any ConOut default to serial */ how = RB_SERIAL; goto out; } ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { if (IsDevicePathEndType(node)) { if (pci_pending && vid_seen == 0) vid_seen = ++seen; } pci_pending = false; if (DevicePathType(node) == ACPI_DEVICE_PATH && (DevicePathSubType(node) == ACPI_DP || DevicePathSubType(node) == ACPI_EXTENDED_DP)) { /* Check for Serial node */ acpi = (void *)node; if (EISA_ID_TO_NUM(acpi->HID) == 0x501) { setenv_int("efi_8250_uid", acpi->UID); com_seen = ++seen; } } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH && DevicePathSubType(node) == MSG_UART_DP) { com_seen = ++seen; uart = (void *)node; setenv_int("efi_com_speed", uart->BaudRate); } else if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_ADR_DP) { /* Check for AcpiAdr() Node for video */ vid_seen = ++seen; } else if (DevicePathType(node) == HARDWARE_DEVICE_PATH && DevicePathSubType(node) == HW_PCI_DP) { /* * Note, vmware fusion has a funky console device * PciRoot(0x0)/Pci(0xf,0x0) * which we can only detect at the end since we also * have to cope with: * PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1) * so only match it if it's last. */ pci_pending = true; } node = NextDevicePathNode(node); } /* * Truth table for RB_MULTIPLE | RB_SERIAL * Value Result * 0 Use only video console * RB_SERIAL Use only serial console * RB_MULTIPLE Use both video and serial console * (but video is primary so gets rc messages) * both Use both video and serial console * (but serial is primary so gets rc messages) * * Try to honor this as best we can. If only one of serial / video * found, then use that. Otherwise, use the first one we found. * This also implies if we found nothing, default to video. */ how = 0; if (vid_seen && com_seen) { how |= RB_MULTIPLE; if (com_seen < vid_seen) how |= RB_SERIAL; } else if (com_seen) how |= RB_SERIAL; out: return (how); } void parse_loader_efi_config(EFI_HANDLE h, const char *env_fn) { pdinfo_t *dp; struct stat st; int fd = -1; char *env = NULL; dp = efiblk_get_pdinfo_by_handle(h); if (dp == NULL) return; set_currdev_pdinfo(dp); if (stat(env_fn, &st) != 0) return; fd = open(env_fn, O_RDONLY); if (fd == -1) return; env = malloc(st.st_size + 1); if (env == NULL) goto out; if (read(fd, env, st.st_size) != st.st_size) goto out; env[st.st_size] = '\0'; boot_parse_cmdline(env); out: free(env); close(fd); } static void read_loader_env(const char *name, char *def_fn, bool once) { UINTN len; char *fn, *freeme = NULL; len = 0; fn = def_fn; if (efi_freebsd_getenv(name, NULL, &len) == EFI_BUFFER_TOO_SMALL) { freeme = fn = malloc(len + 1); if (fn != NULL) { if (efi_freebsd_getenv(name, fn, &len) != EFI_SUCCESS) { free(fn); fn = NULL; printf( "Can't fetch FreeBSD::%s we know is there\n", name); } else { /* * if tagged as 'once' delete the env variable so we * only use it once. */ if (once) efi_freebsd_delenv(name); /* * We malloced 1 more than len above, then redid the call. * so now we have room at the end of the string to NUL terminate * it here, even if the typical idium would have '- 1' here to * not overflow. len should be the same on return both times. */ fn[len] = '\0'; } } else { printf( "Can't allocate %d bytes to fetch FreeBSD::%s env var\n", len, name); } } if (fn) { printf(" Reading loader env vars from %s\n", fn); parse_loader_efi_config(boot_img->DeviceHandle, fn); } } caddr_t ptov(uintptr_t x) { return ((caddr_t)x); } EFI_STATUS main(int argc, CHAR16 *argv[]) { EFI_GUID *guid; int howto, i, uhowto; UINTN k; bool has_kbd, is_last; char *s; EFI_DEVICE_PATH *imgpath; CHAR16 *text; EFI_STATUS rv; size_t sz, bosz = 0, bisz = 0; UINT16 boot_order[100]; char boot_info[4096]; char buf[32]; bool uefi_boot_mgr; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; #ifdef __amd64__ archsw.arch_hypervisor = x86_hypervisor; #endif archsw.arch_readin = efi_readin; archsw.arch_zfs_probe = efi_zfs_probe; /* Get our loaded image protocol interface structure. */ (void) OpenProtocolByHandle(IH, &imgid, (void **)&boot_img); /* * 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. We can use printf() etc. once this is * done. So, we set it to the efi console, then call console init. This * gets us printf early, but also primes the pump for all future console * changes to take effect, regardless of where they come from. */ setenv("console", "efi", 1); uhowto = parse_uefi_con_out(); #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) if ((uhowto & RB_SERIAL) != 0) setenv("console", "comconsole", 1); #endif cons_probe(); /* Init the time source */ efi_time_init(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Scan the BLOCK IO MEDIA handles then * march through the device switch probing for things. */ i = efipart_inithandles(); if (i != 0 && i != ENOENT) { printf("efipart_inithandles failed with ERRNO %d, expect " "failures\n", i); } for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); /* * Detect console settings two different ways: one via the command * args (eg -h) or via the UEFI ConOut variable. */ has_kbd = has_keyboard(); howto = parse_args(argc, argv); if (!has_kbd && (howto & RB_PROBE)) howto |= RB_SERIAL | RB_MULTIPLE; howto &= ~RB_PROBE; /* * Read additional environment variables from the boot device's * "LoaderEnv" file. Any boot loader environment variable may be set * there, which are subtly different than loader.conf variables. Only * the 'simple' ones may be set so things like foo_load="YES" won't work * for two reasons. First, the parser is simplistic and doesn't grok * quotes. Second, because the variables that cause an action to happen * are parsed by the lua, 4th or whatever code that's not yet * loaded. This is relative to the root directory when loader.efi is * loaded off the UFS root drive (when chain booted), or from the ESP * when directly loaded by the BIOS. * * We also read in NextLoaderEnv if it was specified. This allows next boot * functionality to be implemented and to override anything in LoaderEnv. */ read_loader_env("LoaderEnv", "/efi/freebsd/loader.env", false); read_loader_env("NextLoaderEnv", NULL, true); /* * We now have two notions of console. howto should be viewed as * overrides. If console is already set, don't set it again. */ #define VIDEO_ONLY 0 #define SERIAL_ONLY RB_SERIAL #define VID_SER_BOTH RB_MULTIPLE #define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE) #define CON_MASK (RB_SERIAL | RB_MULTIPLE) if (strcmp(getenv("console"), "efi") == 0) { if ((howto & CON_MASK) == 0) { /* No override, uhowto is controlling and efi cons is perfect */ howto = howto | (uhowto & CON_MASK); } else if ((howto & CON_MASK) == (uhowto & CON_MASK)) { /* override matches what UEFI told us, efi console is perfect */ } else if ((uhowto & (CON_MASK)) != 0) { /* * We detected a serial console on ConOut. All possible * overrides include serial. We can't really override what efi * gives us, so we use it knowing it's the best choice. */ /* Do nothing */ } else { /* * We detected some kind of serial in the override, but ConOut * has no serial, so we have to sort out which case it really is. */ switch (howto & CON_MASK) { case SERIAL_ONLY: setenv("console", "comconsole", 1); break; case VID_SER_BOTH: setenv("console", "efi comconsole", 1); break; case SER_VID_BOTH: setenv("console", "comconsole efi", 1); break; /* case VIDEO_ONLY can't happen -- it's the first if above */ } } } /* * howto is set now how we want to export the flags to the kernel, so * set the env based on it. */ boot_howto_to_env(howto); if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } if ((s = getenv("fail_timeout")) != NULL) fail_timeout = strtol(s, NULL, 10); printf("%s\n", bootprog_info); printf(" Command line arguments:"); for (i = 0; i < argc; i++) printf(" %S", argv[i]); printf("\n"); printf(" Image base: 0x%lx\n", (unsigned long)boot_img->ImageBase); printf(" EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf(" EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf(" Console: %s (%#x)\n", getenv("console"), howto); /* Determine the devpath of our image so we can prefer it. */ text = efi_devpath_name(boot_img->FilePath); if (text != NULL) { printf(" Load Path: %S\n", text); efi_setenv_freebsd_wcs("LoaderPath", text); efi_free_devpath_name(text); } rv = OpenProtocolByHandle(boot_img->DeviceHandle, &devid, (void **)&imgpath); if (rv == EFI_SUCCESS) { text = efi_devpath_name(imgpath); if (text != NULL) { printf(" Load Device: %S\n", text); efi_setenv_freebsd_wcs("LoaderDev", text); efi_free_devpath_name(text); } } if (getenv("uefi_ignore_boot_mgr") != NULL) { printf(" Ignoring UEFI boot manager\n"); uefi_boot_mgr = false; } else { uefi_boot_mgr = true; boot_current = 0; sz = sizeof(boot_current); rv = efi_global_getenv("BootCurrent", &boot_current, &sz); if (rv == EFI_SUCCESS) printf(" BootCurrent: %04x\n", boot_current); else { boot_current = 0xffff; uefi_boot_mgr = false; } sz = sizeof(boot_order); rv = efi_global_getenv("BootOrder", &boot_order, &sz); if (rv == EFI_SUCCESS) { printf(" BootOrder:"); for (i = 0; i < sz / sizeof(boot_order[0]); i++) printf(" %04x%s", boot_order[i], boot_order[i] == boot_current ? "[*]" : ""); printf("\n"); is_last = boot_order[(sz / sizeof(boot_order[0])) - 1] == boot_current; bosz = sz; } else if (uefi_boot_mgr) { /* * u-boot doesn't set BootOrder, but otherwise participates in the * boot manager protocol. So we fake it here and don't consider it * a failure. */ bosz = sizeof(boot_order[0]); boot_order[0] = boot_current; is_last = true; } } /* * Next, find the boot info structure the UEFI boot manager is * supposed to setup. We need this so we can walk through it to * find where we are in the booting process and what to try to * boot next. */ if (uefi_boot_mgr) { snprintf(buf, sizeof(buf), "Boot%04X", boot_current); sz = sizeof(boot_info); rv = efi_global_getenv(buf, &boot_info, &sz); if (rv == EFI_SUCCESS) bisz = sz; else uefi_boot_mgr = false; } /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); /* * Initialize the trusted/forbidden certificates from UEFI. * They will be later used to verify the manifest(s), * which should contain hashes of verified files. * This needs to be initialized before any configuration files * are loaded. */ #ifdef EFI_SECUREBOOT ve_efi_init(); #endif /* * Try and find a good currdev based on the image that was booted. * It might be desirable here to have a short pause to allow falling * through to the boot loader instead of returning instantly to follow * the boot protocol and also allow an escape hatch for users wishing * to try something different. */ if (find_currdev(uefi_boot_mgr, is_last, boot_info, bisz) != 0) if (uefi_boot_mgr && !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__) for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { char buf[40]; snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } #endif interact(); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff); static int command_poweroff(int argc __unused, char *argv[] __unused) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc __unused, char *argv[] __unused) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { snprintf(line, sizeof(line), "%23s %012jx %012jx %08jx ", efi_memory_type(p->Type), (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (pager_output(line)) break; if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); if (p->Attribute & EFI_MEMORY_NV) printf("NV "); if (p->Attribute & EFI_MEMORY_MORE_RELIABLE) printf("MR "); if (p->Attribute & EFI_MEMORY_RO) printf("RO "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static int command_configuration(int argc, char *argv[]) { UINTN i; char *name; printf("NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (efi_guid_to_name(guid, &name) == true) { printf(name); free(name); } else { printf("Error while translating UUID to name"); } printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); } return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } - (void) efi_cons_update_mode(); + (void) cons_update_mode(true); return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode <number>\"\n"); return (CMD_OK); } COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi); static int command_lsefi(int argc __unused, char *argv[] __unused) { char *name; EFI_HANDLE *buffer = NULL; EFI_HANDLE handle; UINTN bufsz = 0, i, j; EFI_STATUS status; int ret = 0; status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (status != EFI_BUFFER_TOO_SMALL) { snprintf(command_errbuf, sizeof (command_errbuf), "unexpected error: %lld", (long long)status); return (CMD_ERROR); } if ((buffer = malloc(bufsz)) == NULL) { sprintf(command_errbuf, "out of memory"); return (CMD_ERROR); } status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (EFI_ERROR(status)) { free(buffer); snprintf(command_errbuf, sizeof (command_errbuf), "LocateHandle() error: %lld", (long long)status); return (CMD_ERROR); } pager_open(); for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) { UINTN nproto = 0; EFI_GUID **protocols = NULL; handle = buffer[i]; printf("Handle %p", handle); if (pager_output("\n")) break; /* device path */ status = BS->ProtocolsPerHandle(handle, &protocols, &nproto); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof (command_errbuf), "ProtocolsPerHandle() error: %lld", (long long)status); continue; } for (j = 0; j < nproto; j++) { if (efi_guid_to_name(protocols[j], &name) == true) { printf(" %s", name); free(name); } else { printf("Error while translating UUID to name"); } if ((ret = pager_output("\n")) != 0) break; } BS->FreePool(protocols); if (ret != 0) break; } pager_close(); free(buffer); return (CMD_OK); } #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif /* * Chain load another efi loader. */ static int command_chain(int argc, char *argv[]) { EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; struct stat st; struct devdesc *dev; char *name, *path; void *buf; int fd; if (argc < 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } name = argv[1]; if ((fd = open(name, O_RDONLY)) < 0) { command_errmsg = "no such file"; return (CMD_ERROR); } #ifdef LOADER_VERIEXEC if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) { sprintf(command_errbuf, "can't verify: %s", name); close(fd); return (CMD_ERROR); } #endif if (fstat(fd, &st) < -1) { command_errmsg = "stat failed"; close(fd); return (CMD_ERROR); } status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf); if (status != EFI_SUCCESS) { command_errmsg = "failed to allocate buffer"; close(fd); return (CMD_ERROR); } if (read(fd, buf, st.st_size) != st.st_size) { command_errmsg = "error while reading the file"; (void)BS->FreePool(buf); close(fd); return (CMD_ERROR); } close(fd); status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle); (void)BS->FreePool(buf); if (status != EFI_SUCCESS) { command_errmsg = "LoadImage failed"; return (CMD_ERROR); } status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, (void **)&loaded_image); if (argc > 2) { int i, len = 0; CHAR16 *argp; for (i = 2; i < argc; i++) len += strlen(argv[i]) + 1; len *= sizeof (*argp); loaded_image->LoadOptions = argp = malloc (len); loaded_image->LoadOptionsSize = len; for (i = 2; i < argc; i++) { char *ptr = argv[i]; while (*ptr) *(argp++) = *(ptr++); *(argp++) = ' '; } *(--argv) = 0; } if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) { #ifdef EFI_ZFS_BOOT struct zfs_devdesc *z_dev; #endif struct disk_devdesc *d_dev; pdinfo_t *hd, *pd; switch (dev->d_dev->dv_type) { #ifdef EFI_ZFS_BOOT case DEVT_ZFS: z_dev = (struct zfs_devdesc *)dev; loaded_image->DeviceHandle = efizfs_get_handle_by_guid(z_dev->pool_guid); break; #endif case DEVT_NET: loaded_image->DeviceHandle = efi_find_handle(dev->d_dev, dev->d_unit); break; default: hd = efiblk_get_pdinfo(dev); if (STAILQ_EMPTY(&hd->pd_part)) { loaded_image->DeviceHandle = hd->pd_handle; break; } d_dev = (struct disk_devdesc *)dev; STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { /* * d_partition should be 255 */ if (pd->pd_unit == (uint32_t)d_dev->d_slice) { loaded_image->DeviceHandle = pd->pd_handle; break; } } break; } } dev_cleanup(); status = BS->StartImage(loaderhandle, NULL, NULL); if (status != EFI_SUCCESS) { command_errmsg = "StartImage failed"; free(loaded_image->LoadOptions); loaded_image->LoadOptions = NULL; status = BS->UnloadImage(loaded_image); return (CMD_ERROR); } return (CMD_ERROR); /* not reached */ } COMMAND_SET(chain, "chain", "chain load file", command_chain); extern struct in_addr servip; static int command_netserver(int argc, char *argv[]) { char *proto; n_long rootaddr; if (argc > 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } if (argc < 2) { proto = netproto == NET_TFTP ? "tftp://" : "nfs://"; printf("Netserver URI: %s%s%s\n", proto, intoa(rootip.s_addr), rootpath); return (CMD_OK); } if (argc == 2) { strncpy(rootpath, argv[1], sizeof(rootpath)); rootpath[sizeof(rootpath) -1] = '\0'; if ((rootaddr = net_parse_rootpath()) != INADDR_NONE) servip.s_addr = rootip.s_addr = rootaddr; return (CMD_OK); } return (CMD_ERROR); /* not reached */ } COMMAND_SET(netserver, "netserver", "change or display netserver URI", command_netserver); diff --git a/stand/ficl.mk b/stand/ficl.mk index 0f96532cc65b..caa4aab951c3 100644 --- a/stand/ficl.mk +++ b/stand/ficl.mk @@ -1,22 +1,22 @@ # $FreeBSD$ # Common flags to build FICL related files .if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 1 FICL_CPUARCH= i386 .elif ${MACHINE_ARCH:Mmips64*} != "" FICL_CPUARCH= mips64 .else FICL_CPUARCH= ${MACHINE_CPUARCH} .endif .if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0 CFLAGS+= -fPIC .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 .endif diff --git a/stand/ficl/Makefile b/stand/ficl/Makefile index 35730851d884..0f70286ff7df 100644 --- a/stand/ficl/Makefile +++ b/stand/ficl/Makefile @@ -1,33 +1,35 @@ # $FreeBSD$ # .include <bsd.init.mk> .include "${BOOTSRC}/ficl.mk" .PATH: ${FICLSRC} ${FICLSRC}/${FICL_CPUARCH} BASE_SRCS= dict.c ficl.c fileaccess.c float.c loader.c math64.c \ prefix.c search.c stack.c tools.c vm.c words.c 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} SRCS+= testmain.c PROG= testmain .include <bsd.prog.mk> .else LIB= ficl .include <bsd.lib.mk> .endif # Standard softwords .PATH: ${FICLSRC}/softwords SOFTWORDS= softcore.fr jhlocal.fr marker.fr freebsd.fr ficllocal.fr \ ifbrack.fr softcore.c: ${SOFTWORDS} softcore.awk (cd ${FICLSRC}/softwords; cat ${SOFTWORDS} \ | awk -f softcore.awk -v datestamp="`LC_ALL=C date`") > ${.TARGET} diff --git a/stand/ficl/loader.c b/stand/ficl/loader.c index 2d1e1b908e9d..fca2b7421ffb 100644 --- a/stand/ficl/loader.c +++ b/stand/ficl/loader.c @@ -1,885 +1,1070 @@ /*- * Copyright (c) 2000 Daniel Capo Sobral * 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 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$ */ /******************************************************************* ** l o a d e r . c ** Additional FICL words designed for FreeBSD's loader ** *******************************************************************/ #ifdef TESTMAIN #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #else #include <stand.h> #endif #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 * * setenv ( value n name n' -- ) * setenv? ( value n name n' flag -- ) * getenv ( addr n -- addr' n' | -1 ) * unsetenv ( addr n -- ) * copyin ( addr addr' len -- ) * copyout ( addr addr' len -- ) * findfile ( name len type len' -- addr ) * pnpdevices ( -- addr ) * pnphandlers ( -- addr ) * ccall ( [[...[p10] p9] ... p1] n addr -- result ) * uuid-from-string ( addr n -- addr' ) * uuid-to-string ( addr' -- addr n ) * .# ( value -- ) */ +#ifndef TESTMAIN +/* ( 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); +} +#endif /* TESTMAIN */ + void ficlSetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep, *valuep; int names, values; #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 0); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); values = stackPopINT(pVM->pStack); valuep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = (char*) ficlMalloc(values+1); if (!value) vmThrowErr(pVM, "Error: out of memory"); strncpy(value, valuep, values); value[values] = '\0'; setenv(name, value, 1); ficlFree(name); ficlFree(value); #endif return; } void ficlSetenvq(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep, *valuep; int names, values, overwrite; #if FICL_ROBUST > 1 vmCheckStack(pVM, 5, 0); #endif overwrite = stackPopINT(pVM->pStack); names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); values = stackPopINT(pVM->pStack); valuep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = (char*) ficlMalloc(values+1); if (!value) vmThrowErr(pVM, "Error: out of memory"); strncpy(value, valuep, values); value[values] = '\0'; setenv(name, value, overwrite); ficlFree(name); ficlFree(value); #endif return; } void ficlGetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = getenv(name); ficlFree(name); if(value != NULL) { stackPushPtr(pVM->pStack, value); stackPushINT(pVM->pStack, strlen(value)); } else #endif stackPushINT(pVM->pStack, -1); return; } void ficlUnsetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name; #endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; unsetenv(name); ficlFree(name); #endif return; } void ficlCopyin(FICL_VM *pVM) { void* src; vm_offset_t dest; size_t len; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 0); #endif len = stackPopINT(pVM->pStack); dest = stackPopINT(pVM->pStack); src = stackPopPtr(pVM->pStack); #ifndef TESTMAIN archsw.arch_copyin(src, dest, len); #endif return; } void ficlCopyout(FICL_VM *pVM) { void* dest; vm_offset_t src; size_t len; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 0); #endif len = stackPopINT(pVM->pStack); dest = stackPopPtr(pVM->pStack); src = stackPopINT(pVM->pStack); #ifndef TESTMAIN archsw.arch_copyout(src, dest, len); #endif return; } void ficlFindfile(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *type; #endif char *namep, *typep; struct preloaded_file* fp; int names, types; #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 1); #endif types = stackPopINT(pVM->pStack); typep = (char*) stackPopPtr(pVM->pStack); names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; type = (char*) ficlMalloc(types+1); if (!type) vmThrowErr(pVM, "Error: out of memory"); strncpy(type, typep, types); type[types] = '\0'; fp = file_findfile(name, type); #else fp = NULL; #endif stackPushPtr(pVM->pStack, fp); return; } #ifndef TESTMAIN /* isvirtualized? - Return whether the loader runs under a * hypervisor. * * isvirtualized? ( -- flag ) */ static void ficlIsvirtualizedQ(FICL_VM *pVM) { FICL_INT flag; const char *hv; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif hv = (archsw.arch_hypervisor != NULL) ? (*archsw.arch_hypervisor)() : NULL; flag = (hv != NULL) ? FICL_TRUE : FICL_FALSE; stackPushINT(pVM->pStack, flag); } #endif /* ndef TESTMAIN */ void ficlCcall(FICL_VM *pVM) { int (*func)(int, ...); int result, p[10]; int nparam, i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif func = stackPopPtr(pVM->pStack); nparam = stackPopINT(pVM->pStack); #if FICL_ROBUST > 1 vmCheckStack(pVM, nparam, 1); #endif for (i = 0; i < nparam; i++) p[i] = stackPopINT(pVM->pStack); result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); stackPushINT(pVM->pStack, result); return; } void ficlUuidFromString(FICL_VM *pVM) { #ifndef TESTMAIN char *uuid; uint32_t status; #endif char *uuidp; int uuids; uuid_t *u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif uuids = stackPopINT(pVM->pStack); uuidp = (char *) stackPopPtr(pVM->pStack); #ifndef TESTMAIN uuid = (char *)ficlMalloc(uuids + 1); if (!uuid) vmThrowErr(pVM, "Error: out of memory"); strncpy(uuid, uuidp, uuids); uuid[uuids] = '\0'; u = (uuid_t *)ficlMalloc(sizeof (*u)); uuid_from_string(uuid, u, &status); ficlFree(uuid); if (status != uuid_s_ok) { ficlFree(u); u = NULL; } #else u = NULL; #endif stackPushPtr(pVM->pStack, u); return; } void ficlUuidToString(FICL_VM *pVM) { #ifndef TESTMAIN char *uuid; uint32_t status; #endif uuid_t *u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif u = (uuid_t *)stackPopPtr(pVM->pStack); #ifndef TESTMAIN uuid_to_string(u, &uuid, &status); if (status != uuid_s_ok) { stackPushPtr(pVM->pStack, uuid); stackPushINT(pVM->pStack, strlen(uuid)); } else #endif stackPushINT(pVM->pStack, -1); return; } /************************************************************************** f i c l E x e c F D ** reads in text from file fd and passes it to ficlExec() * returns VM_OUTOFTEXT on success or the ficlExec() error code on * failure. */ #define nLINEBUF 256 int ficlExecFD(FICL_VM *pVM, int fd) { char cp[nLINEBUF]; int nLine = 0, rval = VM_OUTOFTEXT; char ch; CELL id; id = pVM->sourceID; pVM->sourceID.i = fd; /* feed each line to ficlExec */ while (1) { int status, i; i = 0; while ((status = read(fd, &ch, 1)) > 0 && ch != '\n') cp[i++] = ch; nLine++; if (!i) { if (status < 1) break; continue; } rval = ficlExecC(pVM, cp, i); if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT) { pVM->sourceID = id; return rval; } } /* ** Pass an empty line with SOURCE-ID == -1 to flush ** any pending REFILLs (as required by FILE wordset) */ pVM->sourceID.i = -1; ficlExec(pVM, ""); pVM->sourceID = id; return rval; } static void displayCellNoPad(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif c = stackPop(pVM->pStack); ltoa((c).i, pVM->pad, pVM->base); vmTextOut(pVM, pVM->pad, 0); return; } /* isdir? - Return whether an fd corresponds to a directory. * * isdir? ( fd -- bool ) */ static void isdirQuestion(FICL_VM *pVM) { struct stat sb; FICL_INT flag; int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif fd = stackPopINT(pVM->pStack); flag = FICL_FALSE; do { if (fd < 0) break; if (fstat(fd, &sb) < 0) break; if (!S_ISDIR(sb.st_mode)) break; flag = FICL_TRUE; } while (0); stackPushINT(pVM->pStack, flag); } /* fopen - open a file and return new fd on stack. * * fopen ( ptr count mode -- fd ) */ static void pfopen(FICL_VM *pVM) { int mode, fd, count; char *ptr, *name; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif mode = stackPopINT(pVM->pStack); /* get mode */ count = stackPopINT(pVM->pStack); /* get count */ ptr = stackPopPtr(pVM->pStack); /* get ptr */ if ((count < 0) || (ptr == NULL)) { stackPushINT(pVM->pStack, -1); return; } /* ensure that the string is null terminated */ name = (char *)malloc(count+1); bcopy(ptr,name,count); name[count] = 0; /* open the file */ fd = open(name, mode); #ifdef LOADER_VERIEXEC if (fd >= 0) { if (verify_file(fd, name, 0, VE_GUESS, __func__) < 0) { /* not verified writing ok but reading is not */ if ((mode & O_ACCMODE) != O_WRONLY) { close(fd); fd = -1; } } else { /* verified reading ok but writing is not */ if ((mode & O_ACCMODE) != O_RDONLY) { close(fd); fd = -1; } } } #endif free(name); stackPushINT(pVM->pStack, fd); return; } /* fclose - close a file who's fd is on stack. * * fclose ( fd -- ) */ static void pfclose(FICL_VM *pVM) { int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif fd = stackPopINT(pVM->pStack); /* get fd */ if (fd != -1) close(fd); return; } /* fread - read file contents * * fread ( fd buf nbytes -- nread ) */ static void pfread(FICL_VM *pVM) { int fd, len; char *buf; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif len = stackPopINT(pVM->pStack); /* get number of bytes to read */ buf = stackPopPtr(pVM->pStack); /* get buffer */ fd = stackPopINT(pVM->pStack); /* get fd */ if (len > 0 && buf && fd != -1) stackPushINT(pVM->pStack, read(fd, buf, len)); else stackPushINT(pVM->pStack, -1); return; } /* freaddir - read directory contents * * freaddir ( fd -- ptr len TRUE | FALSE ) */ static void pfreaddir(FICL_VM *pVM) { #ifdef TESTMAIN static struct dirent dirent; struct stat sb; char *buf; off_t off, ptr; u_int blksz; int bufsz; #endif struct dirent *d; int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 3); #endif fd = stackPopINT(pVM->pStack); #if TESTMAIN /* * The readdirfd() function is specific to the loader environment. * We do the best we can to make freaddir work, but it's not at * all guaranteed. */ d = NULL; buf = NULL; do { if (fd == -1) break; if (fstat(fd, &sb) == -1) break; blksz = (sb.st_blksize) ? sb.st_blksize : getpagesize(); if ((blksz & (blksz - 1)) != 0) break; buf = malloc(blksz); if (buf == NULL) break; off = lseek(fd, 0LL, SEEK_CUR); if (off == -1) break; ptr = off; if (lseek(fd, 0, SEEK_SET) == -1) break; bufsz = getdents(fd, buf, blksz); while (bufsz > 0 && bufsz <= ptr) { ptr -= bufsz; bufsz = getdents(fd, buf, blksz); } if (bufsz <= 0) break; d = (void *)(buf + ptr); dirent = *d; off += d->d_reclen; d = (lseek(fd, off, SEEK_SET) != off) ? NULL : &dirent; } while (0); if (buf != NULL) free(buf); #else d = readdirfd(fd); #endif if (d != NULL) { stackPushPtr(pVM->pStack, d->d_name); stackPushINT(pVM->pStack, strlen(d->d_name)); stackPushINT(pVM->pStack, FICL_TRUE); } else { stackPushINT(pVM->pStack, FICL_FALSE); } } /* fload - interpret file contents * * fload ( fd -- ) */ static void pfload(FICL_VM *pVM) { int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif fd = stackPopINT(pVM->pStack); /* get fd */ if (fd != -1) ficlExecFD(pVM, fd); return; } /* fwrite - write file contents * * fwrite ( fd buf nbytes -- nwritten ) */ static void pfwrite(FICL_VM *pVM) { int fd, len; char *buf; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif len = stackPopINT(pVM->pStack); /* get number of bytes to read */ buf = stackPopPtr(pVM->pStack); /* get buffer */ fd = stackPopINT(pVM->pStack); /* get fd */ if (len > 0 && buf && fd != -1) stackPushINT(pVM->pStack, write(fd, buf, len)); else stackPushINT(pVM->pStack, -1); return; } /* fseek - seek to a new position in a file * * fseek ( fd ofs whence -- pos ) */ static void pfseek(FICL_VM *pVM) { int fd, pos, whence; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif whence = stackPopINT(pVM->pStack); pos = stackPopINT(pVM->pStack); fd = stackPopINT(pVM->pStack); stackPushINT(pVM->pStack, lseek(fd, pos, whence)); return; } /* key - get a character from stdin * * key ( -- char ) */ static void key(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif stackPushINT(pVM->pStack, getchar()); return; } /* key? - check for a character from stdin (FACILITY) * * key? ( -- flag ) */ static void keyQuestion(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif #ifdef TESTMAIN /* XXX Since we don't fiddle with termios, let it always succeed... */ stackPushINT(pVM->pStack, FICL_TRUE); #else /* But here do the right thing. */ stackPushINT(pVM->pStack, ischar()? FICL_TRUE : FICL_FALSE); #endif return; } /* seconds - gives number of seconds since beginning of time * * beginning of time is defined as: * * BTX - number of seconds since midnight * FreeBSD - number of seconds since Jan 1 1970 * * seconds ( -- u ) */ static void pseconds(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,0,1); #endif stackPushUNS(pVM->pStack, (FICL_UNS) time(NULL)); return; } /* ms - wait at least that many milliseconds (FACILITY) * * ms ( u -- ) * */ static void ms(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,1,0); #endif #ifdef TESTMAIN usleep(stackPopUNS(pVM->pStack)*1000); #else delay(stackPopUNS(pVM->pStack)*1000); #endif return; } /* fkey - get a character from a file * * fkey ( file -- char ) */ static void fkey(FICL_VM *pVM) { int i, fd; char ch; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif fd = stackPopINT(pVM->pStack); i = read(fd, &ch, 1); stackPushINT(pVM->pStack, i > 0 ? ch : -1); return; } /* ** Retrieves free space remaining on the dictionary */ static void freeHeap(FICL_VM *pVM) { stackPushINT(pVM->pStack, dictCellsAvail(ficlGetDict(pVM->pSys))); } /******************* Increase dictionary size on-demand ******************/ static void ficlDictThreshold(FICL_VM *pVM) { stackPushPtr(pVM->pStack, &dictThreshold); } static void ficlDictIncrease(FICL_VM *pVM) { stackPushPtr(pVM->pStack, &dictIncrease); } /************************************************************************** f i c l C o m p i l e P l a t f o r m ** Build FreeBSD platform extensions into the system dictionary **************************************************************************/ void ficlCompilePlatform(FICL_SYSTEM *pSys) { ficlCompileFcn **fnpp; FICL_DICT *dp = pSys->dp; assert (dp); dictAppendWord(dp, ".#", displayCellNoPad, FW_DEFAULT); dictAppendWord(dp, "isdir?", isdirQuestion, FW_DEFAULT); dictAppendWord(dp, "fopen", pfopen, FW_DEFAULT); dictAppendWord(dp, "fclose", pfclose, FW_DEFAULT); dictAppendWord(dp, "fread", pfread, FW_DEFAULT); dictAppendWord(dp, "freaddir", pfreaddir, FW_DEFAULT); dictAppendWord(dp, "fload", pfload, FW_DEFAULT); dictAppendWord(dp, "fkey", fkey, FW_DEFAULT); dictAppendWord(dp, "fseek", pfseek, FW_DEFAULT); dictAppendWord(dp, "fwrite", pfwrite, FW_DEFAULT); dictAppendWord(dp, "key", key, FW_DEFAULT); dictAppendWord(dp, "key?", keyQuestion, FW_DEFAULT); dictAppendWord(dp, "ms", ms, FW_DEFAULT); dictAppendWord(dp, "seconds", pseconds, FW_DEFAULT); dictAppendWord(dp, "heap?", freeHeap, FW_DEFAULT); dictAppendWord(dp, "dictthreshold", ficlDictThreshold, FW_DEFAULT); dictAppendWord(dp, "dictincrease", ficlDictIncrease, FW_DEFAULT); dictAppendWord(dp, "setenv", ficlSetenv, FW_DEFAULT); dictAppendWord(dp, "setenv?", ficlSetenvq, FW_DEFAULT); dictAppendWord(dp, "getenv", ficlGetenv, FW_DEFAULT); dictAppendWord(dp, "unsetenv", ficlUnsetenv, FW_DEFAULT); dictAppendWord(dp, "copyin", ficlCopyin, FW_DEFAULT); dictAppendWord(dp, "copyout", ficlCopyout, FW_DEFAULT); dictAppendWord(dp, "findfile", ficlFindfile, FW_DEFAULT); dictAppendWord(dp, "ccall", ficlCcall, FW_DEFAULT); dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT); dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT); #ifndef TESTMAIN + 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); dictAppendWord(dp, "isvirtualized?",ficlIsvirtualizedQ, FW_DEFAULT); #endif SET_FOREACH(fnpp, Xficl_compile_set) (*fnpp)(pSys); #if defined(__i386__) ficlSetEnv(pSys, "arch-i386", FICL_TRUE); ficlSetEnv(pSys, "arch-powerpc", FICL_FALSE); #elif defined(__powerpc__) ficlSetEnv(pSys, "arch-i386", FICL_FALSE); ficlSetEnv(pSys, "arch-powerpc", FICL_TRUE); #endif return; } diff --git a/stand/fonts/INDEX.fonts b/stand/fonts/INDEX.fonts new file mode 100644 index 000000000000..44aa6a3c81cc --- /dev/null +++ b/stand/fonts/INDEX.fonts @@ -0,0 +1,66 @@ +# +# $FreeBSD$ +# +# database for vidfont(8) +# +# Format <file>:<lang>:<description> +# +# lang: ar bg cs da de el en es fi fr hr hu hy is it iw ja ko nl no pl +# pt ro ru sh sk sl sv tr uk zh +# lang: lang,lang +# +# Example: +# terminus-b32.fnt:de:Terminus Schriftart +# terminus-b32.fnt:en:Terminus font +# +# If lang is empty use 'en' (us-english) as default. +# +# See also setlocale(3), +# /usr/share/locale, /usr/X11/lib/X11/locale/locale.alias +# +################################ +# Language support: MENU, FONT +# +MENU:en:Choose your terminal font +MENU:da:Vælg skrifttypen til din terminal +MENU:de:Wählen Sie Ihre Schrift +MENU:fr:Choisissez votre fonte écran + +# +# The font definition for "en" is the fall-back font for +# all languages. +# Add language specific font definitions only where required! +# +FONT:en:8x16v.fnt +# + +6x12.fnt:en:Terminus BSD Console, size 12 +6x12.fnt:da:Terminus BSD-konsol, størrelse 12 +6x12.fnt:de:Terminus BSD Console, Größe 12 +8x14.fnt:en:Terminus BSD Console, size 14 +8x14.fnt:da:Terminus BSD-konsol, størrelse 14 +8x14.fnt:de:Terminus BSD Console, Größe 14 +8x16.fnt:en:Terminus BSD Console, size 16 +8x16.fnt:da:Terminus BSD-konsol, størrelse 16 +8x16.fnt:de:Terminus BSD Console, Größe 16 +10x18.fnt:en:Terminus BSD Console, size 18 +10x18.fnt:da:Terminus BSD-konsol, størrelse 18 +10x18.fnt:de:Terminus BSD Console, Größe 18 +10x20.fnt:en:Terminus BSD Console, size 20 +10x20.fnt:da:Terminus BSD-konsol, størrelse 20 +10x20.fnt:de:Terminus BSD Console, Größe 20 +11x22.fnt:en:Terminus BSD Console, size 22 +11x22.fnt:da:Terminus BSD-konsol, størrelse 22 +11x22.fnt:de:Terminus BSD Console, Größe 22 +12x24.fnt:en:Terminus BSD Console, size 24 +12x24.fnt:da:Terminus BSD-konsol, størrelse 24 +12x24.fnt:de:Terminus BSD Console, Größe 24 +14x28.fnt:en:Terminus BSD Console, size 28 +14x28.fnt:da:Terminus BSD-konsol, størrelse 28 +14x28.fnt:de:Terminus BSD Console, Größe 28 +16x32.fnt:en:Terminus BSD Console, size 32 +16x32.fnt:da:Terminus BSD-konsol, størrelse 32 +16x32.fnt:de:Terminus BSD Console, Größe 32 + +# (fset 'langnew +# "\M-}\C-p\C-k\C-y\C-m\C-y\M-}") diff --git a/stand/fonts/Makefile b/stand/fonts/Makefile new file mode 100644 index 000000000000..b7b2ea7a0f0d --- /dev/null +++ b/stand/fonts/Makefile @@ -0,0 +1,81 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +.PATH: ${SRCTOP}/contrib/terminus + +FONTS= \ + 6x12.fnt.gz \ + 8x14.fnt.gz \ + 8x14v.fnt.gz \ + 8x16.fnt.gz \ + 8x16v.fnt.gz \ + 10x18.fnt.gz \ + 10x20.fnt.gz \ + 11x22.fnt.gz \ + 12x24.fnt.gz \ + 14x28.fnt.gz \ + 16x32.fnt.gz \ + +FILES= ${FONTS} INDEX.fonts +FILESDIR= /boot/fonts + +CLEANFILES+= ${FONTS} ${FONTS:T:S/${COMPRESS_EXT}//g} + +6x12.fnt.gz: 6x12.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +8x14.fnt.gz: 8x14.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +8x14v.fnt.gz: 8x14v.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +8x16.fnt.gz: 8x16.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +8x16v.fnt.gz: 8x16v.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +10x18.fnt.gz: 10x18.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +10x20.fnt.gz: 10x20.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +11x22.fnt.gz: 11x22.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +12x24.fnt.gz: 12x24.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +14x28.fnt.gz: 14x28.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} +16x32.fnt.gz: 16x32.fnt + ${COMPRESS_CMD} ${.ALLSRC} > ${.TARGET} + +6x12.fnt: ter-u12n.bdf ter-u12b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +8x14.fnt: ter-u14n.bdf ter-u14b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +8x14v.fnt: ter-u14v.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +8x16.fnt: ter-u16n.bdf ter-u16b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +8x16v.fnt: ter-u16v.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +10x18.fnt: ter-u18n.bdf ter-u18b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +10x20.fnt: ter-u20n.bdf ter-u20b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +11x22.fnt: ter-u22n.bdf ter-u22b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +12x24.fnt: ter-u24n.bdf ter-u24b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +14x28.fnt: ter-u28n.bdf ter-u28b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +16x32.fnt: ter-u32n.bdf ter-u32b.bdf + vtfontcvt -o ${.TARGET} ${.ALLSRC} + +.include <bsd.prog.mk> diff --git a/stand/forth/beastie.4th b/stand/forth/beastie.4th index 752cce22a4ff..f64cf9f8720b 100644 --- a/stand/forth/beastie.4th +++ b/stand/forth/beastie.4th @@ -1,110 +1,114 @@ \ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org> \ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> \ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ marker task-beastie.4th only forth definitions variable logoX variable logoY \ Initialize logo placement to defaults 46 logoX ! 4 logoY ! \ This function draws any number of beastie logos at (loader_logo_x, \ loader_logo_y) if defined, else (46,4) (to the right of the menu). To choose \ your beastie, set the variable `loader_logo' to the respective logo name. \ \ NOTE: Each is defined as a logo function in /boot/logo-${loader_logo}.4th \ NOTE: If `/boot/logo-${loader_logo}.4th' does not exist or does not define \ a `logo' function, no beastie is drawn. \ : draw-beastie ( -- ) \ at (loader_logo_x,loader_logo_y), else (46,4) s" loader_logo_x" getenv dup -1 <> if ?number 1 = if logoX ! then else drop then s" loader_logo_y" getenv dup -1 <> if ?number 1 = if logoY ! then else drop then \ If `logo' is defined, execute it s" logo" sfind ( -- xt|0 bool ) if logoX @ logoY @ rot execute else \ Not defined; try-include desired logo file drop ( xt = 0 ) \ cruft s" loader_logo" getenv dup -1 = over 0= or if dup 0= if 2drop else drop then \ getenv result unused loader_color? if s" try-include /boot/logo-orb.4th" else s" try-include /boot/logo-orbbw.4th" then else 2drop ( c-addr/u -- ) \ getenv result unused s" try-include /boot/logo-${loader_logo}.4th" then evaluate 1 spaces \ Execute `logo' if defined now s" logo" sfind if logoX @ logoY @ rot execute else drop then then ; +: draw-beastie + ['] draw-beastie console-iterate +; + also support-functions : beastie-start ( -- ) \ starts the menu s" beastie_disable" getenv dup -1 <> if s" YES" compare-insensitive 0= if any_conf_read? if load_xen_throw load_kernel load_modules then exit \ to autoboot (default) then else drop then s" loader_delay" getenv -1 = if s" include /boot/menu.rc" evaluate else drop ." Loading Menu (Ctrl-C to Abort)" cr s" set delay_command='include /boot/menu.rc'" evaluate s" set delay_showdots" evaluate delay_execute then ; only forth definitions diff --git a/stand/forth/brand-fbsd.4th b/stand/forth/brand-fbsd.4th index 9cd017f84a3f..b8e43e601c96 100644 --- a/stand/forth/brand-fbsd.4th +++ b/stand/forth/brand-fbsd.4th @@ -1,46 +1,58 @@ \ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ 2 brandX ! 1 brandY ! \ Initialize brand placement defaults : brand+ ( x y c-addr/u -- x y' ) 2swap 2dup at-xy 2swap \ position the cursor type \ print to the screen 1+ \ increase y for next time we're called ; : 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+ s" | ___| '__/ _ \/ _ \| _ < \___ \| | | |" brand+ s" | | | | | __/ __/| |_) |____) | |__| |" brand+ s" | | | | | | || | | |" brand+ s" |_| |_| \___|\___||____/|_____/|_____/ " brand+ 2drop ; diff --git a/stand/forth/brand.4th b/stand/forth/brand.4th index 39a9bfae753c..034e4eb40445 100644 --- a/stand/forth/brand.4th +++ b/stand/forth/brand.4th @@ -1,74 +1,78 @@ \ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ marker task-brand.4th variable brandX variable brandY \ Initialize brand placement to defaults 2 brandX ! 1 brandY ! \ This function draws any number of company brands at (loader_brand_x, \ loader_brand_y) if defined, or (2,1) (top-left). To choose your brand, set \ the variable `loader_brand' to the respective brand name. \ \ NOTE: Each is defined as a brand function in /boot/brand-${loader_brand}.4th \ NOTE: If `/boot/brand-${loader_brand}.4th' does not exist or does not define \ a `brand' function, no brand is drawn. \ : draw-brand ( -- ) \ at (loader_brand_x,loader_brand_y), else (2,1) s" loader_brand_x" getenv dup -1 <> if ?number 1 = if brandX ! then else drop then s" loader_brand_y" getenv dup -1 <> if ?number 1 = if brandY ! then else drop then \ If `brand' is defined, execute it s" brand" sfind ( -- xt|0 bool ) if brandX @ brandY @ rot execute else \ Not defined; try-include desired brand file drop ( xt = 0 ) \ cruft s" loader_brand" getenv dup -1 = over 0= or if dup 0= if 2drop else drop then \ getenv result unused s" try-include /boot/brand-fbsd.4th" else 2drop ( c-addr/u -- ) \ getenv result unused s" try-include /boot/brand-${loader_brand}.4th" then evaluate 1 spaces \ Execute `brand' if defined now s" brand" sfind if brandX @ brandY @ rot execute else drop then then ; + +: draw-brand + ['] draw-brand console-iterate +; diff --git a/stand/forth/color.4th b/stand/forth/color.4th index fc5a4d564d0a..e73e8b725c31 100644 --- a/stand/forth/color.4th +++ b/stand/forth/color.4th @@ -1,55 +1,51 @@ \ Copyright (c) 2011-2013 Devin Teske <dteske@FreeBSD.org> \ 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 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$ marker task-color.4th \ 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 ) s" loader_color" getenv dup -1 <> if \ `loader_color' is set. \ Check if it is explicitly disabled. 2dup s" NO" compare-insensitive 0= if 2drop FALSE exit then 2dup s" 0" compare 0= if 2drop 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 ; diff --git a/stand/forth/frames.4th b/stand/forth/frames.4th index ba356cee4acb..8d2421c758d9 100644 --- a/stand/forth/frames.4th +++ b/stand/forth/frames.4th @@ -1,142 +1,156 @@ \ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org> \ Copyright (c) 2012-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ marker task-frames.4th vocabulary frame-drawing only forth also frame-drawing definitions \ XXX Filled boxes are left as an exercise for the reader... ;-/ variable h_el variable v_el variable lt_el variable lb_el variable rt_el variable rb_el variable fill \ ASCII frames (used when serial console is detected) 45 constant ascii_dash 61 constant ascii_equal 124 constant ascii_pipe 43 constant ascii_plus \ Single frames 0x2500 constant sh_el 0x2502 constant sv_el 0x250c constant slt_el 0x2514 constant slb_el 0x2510 constant srt_el 0x2518 constant srb_el \ Double frames 0x2550 constant dh_el 0x2551 constant dv_el 0x2554 constant dlt_el 0x255a constant dlb_el 0x2557 constant drt_el 0x255d constant drb_el \ Fillings 0 constant fill_none 32 constant fill_blank 0x2591 constant fill_dark 0x2592 constant fill_med 0x2593 constant fill_bright only forth definitions also frame-drawing : hline ( len x y -- ) \ Draw horizontal single line at-xy \ move cursor 0 do h_el @ xemit loop ; : f_ascii ( -- ) ( -- ) \ set frames to ascii ascii_dash h_el ! ascii_pipe v_el ! ascii_plus lt_el ! ascii_plus lb_el ! ascii_plus rt_el ! ascii_plus rb_el ! ; : f_single ( -- ) \ set frames to single boot_serial? if f_ascii exit then sh_el h_el ! sv_el v_el ! slt_el lt_el ! slb_el lb_el ! srt_el rt_el ! srb_el rb_el ! ; : f_double ( -- ) \ set frames to double boot_serial? if f_ascii ascii_equal h_el ! exit then dh_el h_el ! dv_el v_el ! dlt_el lt_el ! dlb_el lb_el ! drt_el rt_el ! drb_el rb_el ! ; : vline ( len x y -- ) \ Draw vertical single line 2dup 4 pick 0 do at-xy v_el @ xemit 1+ 2dup loop 2drop 2drop drop ; : 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 vline \ Draw right vert line 2dup swap 1+ swap 5 pick 1- -rot hline \ Draw top horiz line 2dup swap 1+ swap 4 pick + 5 pick 1- -rot hline \ Draw bottom horiz line 2dup at-xy lt_el @ xemit \ Draw left-top corner 2dup 4 pick + at-xy lb_el @ xemit \ Draw left bottom corner 2dup swap 5 pick + swap at-xy rt_el @ xemit \ Draw right top corner 2 pick + swap 3 pick + swap at-xy rb_el @ xemit 2drop ; f_single fill_none fill ! only forth definitions diff --git a/stand/forth/logo-orb.4th b/stand/forth/logo-orb.4th index c2a504d1ddf5..289353a46926 100644 --- a/stand/forth/logo-orb.4th +++ b/stand/forth/logo-orb.4th @@ -1,55 +1,67 @@ \ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ 46 logoX ! 7 logoY ! \ Initialize logo placement defaults : logo+ ( x y c-addr/u -- x y' ) 2swap 2dup at-xy 2swap \ position the cursor [char] @ escc! \ replace @ with Esc type \ print to the screen 1+ \ increase y for next time we're called ; : 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+ s" yo`:. @[31;1m:o `+-@[31m" logo+ s" y/ @[31;1m-/` -o/@[31m" logo+ s" .- @[31;1m::/sy+:.@[31m" logo+ s" / @[31;1m`-- /@[31m" logo+ s" `: @[31;1m:`@[31m" logo+ s" `: @[31;1m:`@[31m" logo+ s" / @[31;1m/@[31m" logo+ s" .- @[31;1m-.@[31m" logo+ s" -- @[31;1m-.@[31m" logo+ s" `:` @[31;1m`:`" logo+ s" @[31;1m.-- `--." logo+ s" .---.....----.@[m" logo+ 2drop ; diff --git a/stand/forth/menu.4th b/stand/forth/menu.4th index ca166c6d934b..6b0869cfe3b9 100644 --- a/stand/forth/menu.4th +++ b/stand/forth/menu.4th @@ -1,1319 +1,1323 @@ \ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org> \ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> \ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> \ 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 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$ marker task-menu.4th \ Frame drawing include /boot/frames.4th vocabulary menu-infrastructure vocabulary menu-namespace vocabulary menu-command-helpers only forth also menu-infrastructure definitions f_double \ Set frames to double (see frames.4th). Replace with \ f_single if you want single frames. 46 constant dot \ ASCII definition of a period (in decimal) 5 constant menu_default_x \ default column position of timeout 10 constant menu_default_y \ default row position of timeout msg 4 constant menu_timeout_default_x \ default column position of timeout 23 constant menu_timeout_default_y \ default row position of timeout msg 10 constant menu_timeout_default \ default timeout (in seconds) \ Customize the following values with care 1 constant menu_start \ Numerical prefix of first menu item dot constant bullet \ Menu bullet (appears after numerical prefix) 5 constant menu_x \ Row position of the menu (from the top) 10 constant menu_y \ Column position of the menu (from left side) \ Menu Appearance variable menuidx \ Menu item stack for number prefixes variable menurow \ Menu item stack for positioning variable menubllt \ Menu item bullet \ Menu Positioning variable menuX \ Menu X offset (columns) variable menuY \ Menu Y offset (rows) \ Menu-item elements variable menurebootadded \ Parsing of kernels into menu-items variable kernidx variable kernlen variable kernmenuidx \ Menu timer [count-down] variables variable menu_timeout_enabled \ timeout state (internal use only) variable menu_time \ variable for tracking the passage of time variable menu_timeout \ determined configurable delay duration variable menu_timeout_x \ column position of timeout message variable menu_timeout_y \ row position of timeout message \ Containers for parsing kernels into menu-items create kerncapbuf 64 allot create kerndefault 64 allot create kernelsbuf 256 allot only forth also menu-namespace definitions \ Menu-item key association/detection variable menukey1 variable menukey2 variable menukey3 variable menukey4 variable menukey5 variable menukey6 variable menukey7 variable menukey8 variable menureboot variable menuacpi variable menuoptions variable menukernel \ Menu initialization status variables variable init_state1 variable init_state2 variable init_state3 variable init_state4 variable init_state5 variable init_state6 variable init_state7 variable init_state8 \ Boolean option status variables variable toggle_state1 variable toggle_state2 variable toggle_state3 variable toggle_state4 variable toggle_state5 variable toggle_state6 variable toggle_state7 variable toggle_state8 \ Array option status variables variable cycle_state1 variable cycle_state2 variable cycle_state3 variable cycle_state4 variable cycle_state5 variable cycle_state6 variable cycle_state7 variable cycle_state8 \ Containers for storing the initial caption text create init_text1 64 allot create init_text2 64 allot create init_text3 64 allot create init_text4 64 allot create init_text5 64 allot create init_text6 64 allot create init_text7 64 allot create init_text8 64 allot only forth definitions : arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. s" arch-i386" environment? dup if drop then ; : acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise s" hint.acpi.0.rsdp" getenv dup -1 = if drop false exit then 2drop true ; : acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise s" hint.acpi.0.disabled" getenv dup -1 <> if s" 0" compare 0<> if false exit then else drop then true ; : +c! ( N C-ADDR/U K -- C-ADDR/U ) 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr ) rot + c! ( n c-addr/u k n c-addr -- n c-addr/u ) rot drop ( n c-addr/u -- c-addr/u ) ; only forth also menu-namespace definitions \ Forth variables : namespace ( C-ADDR/U N -- ) also menu-namespace +c! evaluate previous ; : menukeyN ( N -- ADDR ) s" menukeyN" 7 namespace ; : init_stateN ( N -- ADDR ) s" init_stateN" 10 namespace ; : toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 namespace ; : cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 namespace ; : init_textN ( N -- C-ADDR ) s" init_textN" 9 namespace ; \ Environment variables : kernel[x] ( N -- C-ADDR/U ) s" kernel[x]" 7 +c! ; : menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ; : menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ; : menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ; : ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ; : menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ; : toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ; : toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ; : menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ; : ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ; also menu-infrastructure definitions \ This function prints a menu item at menuX (row) and menuY (column), returns \ the incremental decimal ASCII value associated with the menu item, and \ increments the cursor position to the next row for the creation of the next \ menu item. This function is called by the menu-create function. You need not \ call it directly. \ : printmenuitem ( menu_item_str -- ascii_keycode ) loader_color? if [char] ^ escc! then menurow dup @ 1+ swap ! ( increment menurow ) menuidx dup @ 1+ swap ! ( increment menuidx ) \ Calculate the menuitem row position menurow @ menuY @ + \ Position the cursor at the menuitem position dup menuX @ swap at-xy \ Print the value of menuidx loader_color? dup ( -- bool bool ) if b then menuidx @ . if me then \ Move the cursor forward 1 column dup menuX @ 1+ swap at-xy menubllt @ emit \ Print the menu bullet using the emit function \ Move the cursor to the 3rd column from the current position \ to allow for a space between the numerical prefix and the \ text caption menuX @ 3 + swap at-xy \ Print the menu caption (we expect a string to be on the stack \ prior to invoking this function) type \ Here we will add the ASCII decimal of the numerical prefix \ to the stack (decimal ASCII for `1' is 49) as a "return value" menuidx @ 48 + ; \ This function prints the appropriate menuitem basename to the stack if an \ ACPI option is to be presented to the user, otherwise returns -1. Used \ internally by menu-create, you need not (nor should you) call this directly. \ : acpimenuitem ( -- C-Addr/U | -1 ) arch-i386? if acpipresent? if acpienabled? if loader_color? if s" toggled_ansi[x]" else s" toggled_text[x]" then else loader_color? if s" ansi_caption[x]" else s" menu_caption[x]" then then else menuidx dup @ 1+ swap ! ( increment menuidx ) -1 then else -1 then ; : delim? ( C -- BOOL ) dup 32 = ( c -- c bool ) \ [sp] space over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab over 10 = or ( c bool -- c bool ) \ [nl] newline over 13 = or ( c bool -- c bool ) \ [cr] carriage return over [char] , = or ( c bool -- c bool ) \ comma swap drop ( c bool -- bool ) \ return boolean ; \ This function parses $kernels into variables that are used by the menu to \ display which kernel to boot when the [overloaded] `boot' word is interpreted. \ Used internally by menu-create, you need not (nor should you) call this \ directly. \ : parse-kernels ( N -- ) \ kernidx kernidx ! ( n -- ) \ store provided `x' value [char] 0 kernmenuidx ! \ initialize `y' value for menu_caption[x][y] \ Attempt to get a list of kernels, fall back to sensible default s" kernels" getenv dup -1 = if drop ( cruft ) s" kernel kernel.old" then ( -- c-addr/u ) \ Check to see if the user has altered $kernel by comparing it against \ $kernel[N] where N is kernel_state (the actively displayed kernel). s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv dup -1 <> if s" kernel" getenv dup -1 = if drop ( cruft ) s" " then 2swap 2over compare 0= if 2drop FALSE ( skip below conditional ) else \ User has changed $kernel TRUE ( slurp in new value ) then else \ We haven't yet parsed $kernels into $kernel[N] drop ( getenv cruft ) s" kernel" getenv dup -1 = if drop ( cruft ) s" " then TRUE ( slurp in initial value ) then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 ) if \ slurp new value into kerndefault kerndefault 1+ 0 2swap strcat swap 1- c! then \ Clear out existing parsed-kernels kernidx @ [char] 0 begin dup kernel[x] unsetenv 2dup menu_caption[x][y] unsetenv 2dup ansi_caption[x][y] unsetenv 1+ dup [char] 8 > until 2drop \ Step through the string until we find the end begin 0 kernlen ! \ initialize length of value \ Skip leading whitespace and/or comma delimiters begin dup 0<> if over c@ delim? ( c-addr/u -- c-addr/u bool ) else false ( c-addr/u -- c-addr/u bool ) then while 1- swap 1+ swap ( c-addr/u -- c-addr'/u' ) repeat ( c-addr/u -- c-addr'/u' ) dup 0= if \ end of string while eating whitespace 2drop ( c-addr/u -- ) kernmenuidx @ [char] 0 <> if \ found at least one exit \ all done then \ No entries in $kernels; use $kernel instead s" kernel" getenv dup -1 = if drop ( cruft ) s" " then ( -- c-addr/u ) dup kernlen ! \ store entire value length as kernlen else \ We're still within $kernels parsing toward the end; \ find delimiter/end to determine kernlen 2dup ( c-addr/u -- c-addr/u c-addr/u ) begin dup 0<> while over c@ delim? if drop 0 ( break ) \ found delimiter else kernlen @ 1+ kernlen ! \ incrememnt 1- swap 1+ swap \ c-addr++ u-- then repeat 2drop ( c-addr/u c-addr'/u' -- c-addr/u ) \ If this is the first entry, compare it to $kernel \ If different, then insert $kernel beforehand kernmenuidx @ [char] 0 = if over kernlen @ kerndefault count compare if kernelsbuf 0 kerndefault count strcat s" ," strcat 2swap strcat kerndefault count swap drop kernlen ! then then then ( c-addr/u -- c-addr'/u' ) \ At this point, we should have something on the stack to store \ as the next kernel menu option; start assembling variables over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 ) \ Assign first to kernel[x] 2dup kernmenuidx @ kernel[x] setenv \ Assign second to menu_caption[x][y] kerncapbuf 0 s" [K]ernel: " strcat 2over strcat kernidx @ kernmenuidx @ menu_caption[x][y] setenv \ Assign third to ansi_caption[x][y] kerncapbuf 0 s" @[1mK@[mernel: " [char] @ escc! strcat kernmenuidx @ [char] 0 = if s" default/@[32m" else s" @[34;1m" then [char] @ escc! strcat 2over strcat s" @[m" [char] @ escc! strcat kernidx @ kernmenuidx @ ansi_caption[x][y] setenv 2drop ( c-addr/u c-addr/u2 -- c-addr/u ) kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if 2drop ( c-addr/u -- ) exit then kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' ) again ; \ This function goes through the kernels that were discovered by the \ parse-kernels function [above], adding " (# of #)" text to the end of each \ caption. \ : tag-kernels ( -- ) kernidx @ ( -- x ) dup 0= if exit then [char] 0 s" (Y of Z)" ( x -- x y c-addr/u ) kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed begin 2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num 2over menu_caption[x][y] getenv dup -1 <> if 2dup + 1- c@ [char] ) = if 2drop \ Already tagged else kerncapbuf 0 2swap strcat 2over strcat 5 pick 5 pick menu_caption[x][y] setenv then else drop ( getenv cruft ) then 2over ansi_caption[x][y] getenv dup -1 <> if 2dup + 1- c@ [char] ) = if 2drop \ Already tagged else kerncapbuf 0 2swap strcat 2over strcat 5 pick 5 pick ansi_caption[x][y] setenv then else drop ( getenv cruft ) then rot 1+ dup [char] 8 > if -rot 2drop TRUE ( break ) else -rot FALSE then until 2drop ( x y -- ) ; \ This function creates the list of menu items. This function is called by the \ menu-display function. You need not call it directly. \ : menu-create ( -- ) \ Print the frame caption at (x,y) s" loader_menu_title" getenv dup -1 = if drop s" Welcome to FreeBSD" then TRUE ( use default alignment ) s" loader_menu_title_align" getenv dup -1 <> if 2dup s" left" compare-insensitive 0= if ( 1 ) 2drop ( c-addr/u ) drop ( bool ) menuX @ menuY @ 1- FALSE ( don't use default alignment ) else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 ) 2drop ( c-addr/u ) drop ( bool ) menuX @ 42 + 4 - over - menuY @ 1- FALSE ( don't use default alignment ) else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then else drop ( getenv cruft ) then if ( use default center alignement? ) menuX @ 19 + over 2 / - menuY @ 1- then at-xy type \ If $menu_init is set, evaluate it (allowing for whole menus to be \ constructed dynamically -- as this function could conceivably set \ the remaining environment variables to construct the menu entirely). \ s" menu_init" getenv dup -1 <> if evaluate else drop then \ Print our menu options with respective key/variable associations. \ `printmenuitem' ends by adding the decimal ASCII value for the \ numerical prefix to the stack. We store the value left on the stack \ to the key binding variable for later testing against a character \ captured by the `getkey' function. \ Note that any menu item beyond 9 will have a numerical prefix on the \ screen consisting of the first digit (ie. 1 for the tenth menu item) \ and the key required to activate that menu item will be the decimal \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') \ which is misleading and not desirable. \ \ Thus, we do not allow more than 8 configurable items on the menu \ (with "Reboot" as the optional ninth and highest numbered item). \ \ Initialize the ACPI option status. \ 0 menuacpi ! s" menu_acpi" getenv -1 <> if c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) menuacpi ! arch-i386? if acpipresent? if \ \ Set menu toggle state to active state \ (required by generic toggle_menuitem) \ acpienabled? menuacpi @ toggle_stateN ! then then else drop then then \ \ Initialize kernel captions after parsing $kernels \ 0 menukernel ! s" menu_kernel" getenv -1 <> if c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) dup menukernel ! dup parse-kernels tag-kernels \ Get the current cycle state (entry to use) s" kernel_state" evaluate @ 48 + ( n -- n y ) \ If state is invalid, reset dup kernmenuidx @ 1- > if drop [char] 0 ( n y -- n 48 ) 0 s" kernel_state" evaluate ! over s" init_kernel" evaluate drop then \ Set the current non-ANSI caption 2dup swap dup ( n y -- n y y n n ) s" set menu_caption[x]=$menu_caption[x][y]" 17 +c! 34 +c! 37 +c! evaluate ( n y y n n c-addr/u -- n y ) \ Set the current ANSI caption 2dup swap dup ( n y -- n y y n n ) s" set ansi_caption[x]=$ansi_caption[x][y]" 17 +c! 34 +c! 37 +c! evaluate ( n y y n n c-addr/u -- n y ) \ Initialize cycle state from stored value 48 - ( n y -- n k ) s" init_cyclestate" evaluate ( n k -- n ) \ Set $kernel to $kernel[y] s" activate_kernel" evaluate ( n -- n ) then drop then \ \ Initialize the menu_options visual separator. \ 0 menuoptions ! s" menu_options" getenv -1 <> if c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) menuoptions ! else drop then then \ Initialize "Reboot" menu state variable (prevents double-entry) false menurebootadded ! menu_start 1- menuidx ! \ Initialize the starting index for the menu 0 menurow ! \ Initialize the starting position for the menu 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') begin \ If the "Options:" separator, print it. dup menuoptions @ = if \ Optionally add a reboot option to the menu s" menu_reboot" getenv -1 <> if drop s" Reboot" printmenuitem menureboot ! true menurebootadded ! then menuX @ menurow @ 2 + menurow ! menurow @ menuY @ + at-xy s" menu_optionstext" getenv dup -1 <> if type else drop ." Options:" then then \ If this is the ACPI menu option, act accordingly. dup menuacpi @ = if dup acpimenuitem ( n -- n n c-addr/u | n n -1 ) dup -1 <> if 13 +c! ( n n c-addr/u -- n c-addr/u ) \ replace 'x' with n else swap drop ( n n -1 -- n -1 ) over menu_command[x] unsetenv then else \ make sure we have not already initialized this item dup init_stateN dup @ 0= if 1 swap ! \ If this menuitem has an initializer, run it dup menu_init[x] getenv dup -1 <> if evaluate else drop then else drop then dup loader_color? if ansi_caption[x] else menu_caption[x] then then dup -1 <> if \ test for environment variable getenv dup -1 <> if printmenuitem ( c-addr/u -- n ) dup menukeyN ! else drop then else drop then 1+ dup 56 > \ add 1 to iterator, continue if less than 57 until drop \ iterator \ Optionally add a reboot option to the menu menurebootadded @ true <> if s" menu_reboot" getenv -1 <> if drop \ no need for the value s" Reboot" \ menu caption (required by printmenuitem) printmenuitem menureboot ! else 0 menureboot ! then then ; \ Takes a single integer on the stack and updates the timeout display. The \ integer must be between 0 and 9 (we will only update a single digit in the \ source message). \ : menu-timeout-update ( N -- ) \ Enforce minimum/maximum dup 9 > if drop 9 then dup 0 < if drop 0 then s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u ) 2 pick 0> if rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor type ( c-addr/u -- ) \ print message else menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor spaces ( n c-addr/u -- n c-addr ) \ erase message 2drop ( n c-addr -- ) then 0 25 at-xy ( position cursor back at bottom-left ) ; \ This function blocks program flow (loops forever) until a key is pressed. \ The key that was pressed is added to the top of the stack in the form of its \ decimal ASCII representation. This function is called by the menu-display \ function. You need not call it directly. \ : getkey ( -- ascii_keycode ) begin \ loop forever menu_timeout_enabled @ 1 = if ( -- ) seconds ( get current time: -- N ) dup menu_time @ <> if ( has time elapsed?: N N N -- N ) \ At least 1 second has elapsed since last loop \ so we will decrement our "timeout" (really a \ counter, insuring that we do not proceed too \ fast) and update our timeout display. menu_time ! ( update time record: N -- ) menu_timeout @ ( "time" remaining: -- N ) dup 0> if ( greater than 0?: N N 0 -- N ) 1- ( decrement counter: N -- N ) dup menu_timeout ! ( re-assign: N N Addr -- N ) then ( -- N ) dup 0= swap 0< or if ( N <= 0?: N N -- ) \ halt the timer 0 menu_timeout ! ( 0 Addr -- ) 0 menu_timeout_enabled ! ( 0 Addr -- ) then \ update the timer display ( N -- ) menu_timeout @ menu-timeout-update menu_timeout @ 0= if \ We've reached the end of the timeout \ (user did not cancel by pressing ANY \ key) s" menu_timeout_command" getenv dup -1 = if drop \ clean-up else evaluate then then else ( -- N ) \ No [detectable] time has elapsed (in seconds) drop ( N -- ) then ( -- ) then key? if \ Was a key pressed? (see loader(8)) \ An actual key was pressed (if the timeout is running, \ kill it regardless of which key was pressed) menu_timeout @ 0<> if 0 menu_timeout ! 0 menu_timeout_enabled ! \ clear screen of timeout message 0 menu-timeout-update then \ get the key that was pressed and exit (if we \ get a non-zero ASCII code) key dup 0<> if exit else drop then then 50 ms \ sleep for 50 milliseconds (see loader(8)) again ; : menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1. \ Clear the screen area associated with the interactive menu menuX @ menuY @ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 2drop \ Reset the starting index and position for the menu menu_start 1- menuidx ! 0 menurow ! ; only forth also menu-infrastructure also menu-namespace also menu-command-helpers definitions : toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state \ ASCII numeral equal to user-selected menu item must be on the stack. \ We do not modify the stack, so the ASCII numeral is left on top. dup init_textN c@ 0= if \ NOTE: no need to check toggle_stateN since the first time we \ are called, we will populate init_textN. Further, we don't \ need to test whether menu_caption[x] (ansi_caption[x] when \ loader_color?=1) is available since we would not have been \ called if the caption was NULL. \ base name of environment variable dup ( n -- n n ) \ key pressed loader_color? if ansi_caption[x] else menu_caption[x] then getenv dup -1 <> if 2 pick ( n c-addr/u -- n c-addr/u n ) init_textN ( n c-addr/u n -- n c-addr/u c-addr ) \ now we have the buffer c-addr on top \ ( followed by c-addr/u of current caption ) \ Copy the current caption into our buffer 2dup c! -rot \ store strlen at first byte begin rot 1+ \ bring alt addr to top and increment -rot -rot \ bring buffer addr to top 2dup c@ swap c! \ copy current character 1+ \ increment buffer addr rot 1- \ bring buffer len to top and decrement dup 0= \ exit loop if buffer len is zero until 2drop \ buffer len/addr drop \ alt addr else drop then then \ Now we are certain to have init_textN populated with the initial \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). \ We can now use init_textN as the untoggled caption and \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the \ toggled caption and store the appropriate value into menu_caption[x] \ (again, ansi_caption[x] with loader_color enabled). Last, we'll \ negate the toggled state so that we reverse the flow on subsequent \ calls. dup toggle_stateN @ 0= if \ state is OFF, toggle to ON dup ( n -- n n ) \ key pressed loader_color? if toggled_ansi[x] else toggled_text[x] then getenv dup -1 <> if \ Assign toggled text to menu caption 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed loader_color? if ansi_caption[x] else menu_caption[x] then setenv else \ No toggled text, keep the same caption drop ( n -1 -- n ) \ getenv cruft then true \ new value of toggle state var (to be stored later) else \ state is ON, toggle to OFF dup init_textN count ( n -- n c-addr/u ) \ Assign init_textN text to menu caption 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed loader_color? if ansi_caption[x] else menu_caption[x] then setenv false \ new value of toggle state var (to be stored below) then \ now we'll store the new toggle state (on top of stack) over toggle_stateN ! ; : cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem \ ASCII numeral equal to user-selected menu item must be on the stack. \ We do not modify the stack, so the ASCII numeral is left on top. dup cycle_stateN dup @ 1+ \ get value and increment \ Before assigning the (incremented) value back to the pointer, \ let's test for the existence of this particular array element. \ If the element exists, we'll store index value and move on. \ Otherwise, we'll loop around to zero and store that. dup 48 + ( n addr k -- n addr k k' ) \ duplicate array index and convert to ASCII numeral 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y) loader_color? if ansi_caption[x][y] else menu_caption[x][y] then ( n addr k n k' -- n addr k c-addr/u ) \ Now test for the existence of our incremented array index in the \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color \ enabled) as set in loader.rc(5), et. al. getenv dup -1 = if \ No caption set for this array index. Loop back to zero. drop ( n addr k -1 -- n addr k ) \ getenv cruft drop 0 ( n addr k -- n addr 0 ) \ new value to store later 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y) loader_color? if ansi_caption[x][y] else menu_caption[x][y] then ( n addr 0 n 48 -- n addr 0 c-addr/u ) getenv dup -1 = if \ Highly unlikely to occur, but to ensure things move \ along smoothly, allocate a temporary NULL string drop ( cruft ) s" " then then \ At this point, we should have the following on the stack (in order, \ from bottom to top): \ \ n - Ascii numeral representing the menu choice (inherited) \ addr - address of our internal cycle_stateN variable \ k - zero-based number we intend to store to the above \ c-addr/u - string value we intend to store to menu_caption[x] \ (or ansi_caption[x] with loader_color enabled) \ \ Let's perform what we need to with the above. \ Assign array value text to menu caption 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n ) loader_color? if ansi_caption[x] else menu_caption[x] then setenv swap ! ( n addr k -- n ) \ update array state variable ; only forth definitions also menu-infrastructure \ Erase and redraw the menu. Useful if you change a caption and want to \ update the menu to reflect the new value. \ : menu-redraw ( -- ) menu-erase 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. \ : menu-init ( -- ) menu_start 1- menuidx ! \ Initialize the starting index for the menu 0 menurow ! \ Initialize the starting position for the menu \ Assign configuration values s" loader_menu_y" getenv dup -1 = if drop \ no custom row position menu_default_y else \ make sure custom position is a number ?number 0= if menu_default_y \ or use default then then menuY ! s" loader_menu_x" getenv dup -1 = if drop \ no custom column position menu_default_x else \ make sure custom position is a number ?number 0= if menu_default_x \ or use default then 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 ; also menu-namespace \ Main function. Call this from your `loader.rc' file. \ : menu-display ( -- ) 0 menu_timeout_enabled ! \ start with automatic timeout disabled \ check indication that automatic execution after delay is requested s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) drop ( just testing existence right now: Addr -- ) \ initialize state variables seconds menu_time ! ( store the time we started ) 1 menu_timeout_enabled ! ( enable automatic timeout ) \ read custom time-duration (if set) s" autoboot_delay" getenv dup -1 = if drop \ no custom duration (remove dup'd bunk -1) menu_timeout_default \ use default setting else 2dup ?number 0= if ( if not a number ) \ disable timeout if "NO", else use default s" NO" compare-insensitive 0= if 0 menu_timeout_enabled ! 0 ( assigned to menu_timeout below ) else menu_timeout_default then else -rot 2drop \ boot immediately if less than zero dup 0< if drop menu-create 0 25 at-xy 0 boot then then then menu_timeout ! ( store value on stack from above ) menu_timeout_enabled @ 1 = if \ read custom column position (if set) s" loader_menu_timeout_x" getenv dup -1 = if drop \ no custom column position menu_timeout_default_x \ use default setting else \ make sure custom position is a number ?number 0= if menu_timeout_default_x \ or use default then then menu_timeout_x ! ( store value on stack from above ) \ read custom row position (if set) s" loader_menu_timeout_y" getenv dup -1 = if drop \ no custom row position menu_timeout_default_y \ use default setting else \ make sure custom position is a number ?number 0= if menu_timeout_default_y \ or use default then then menu_timeout_y ! ( store value on stack from above ) then then menu-create begin \ Loop forever 0 25 at-xy \ Move cursor to the bottom for output getkey \ Block here, waiting for a key to be pressed dup -1 = if drop exit \ Caught abort (abnormal return) then \ Boot if the user pressed Enter/Ctrl-M (13) or \ Ctrl-Enter/Ctrl-J (10) dup over 13 = swap 10 = or if drop ( no longer needed ) s" boot" evaluate exit ( pedantic; never reached ) then dup menureboot @ = if 0 reboot then \ Evaluate the decimal ASCII value against known menu item \ key associations and act accordingly 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') begin dup menukeyN @ rot tuck = if \ Adjust for missing ACPI menuitem on non-i386 arch-i386? true <> menuacpi @ 0<> and if menuacpi @ over 2dup < -rot = or over 58 < and if ( key >= menuacpi && key < 58: N -- N ) 1+ then then \ Test for the environment variable dup menu_command[x] getenv dup -1 <> if \ Execute the stored procedure evaluate \ We expect there to be a non-zero \ value left on the stack after \ executing the stored procedure. \ If so, continue to run, else exit. 0= if drop \ key pressed drop \ loop iterator exit else swap \ need iterator on top then then \ Re-adjust for missing ACPI menuitem arch-i386? true <> menuacpi @ 0<> and if swap menuacpi @ 1+ over 2dup < -rot = or over 59 < and if 1- then swap then else swap \ need iterator on top then \ \ Check for menu keycode shortcut(s) \ dup menu_keycode[x] getenv dup -1 = if drop else ?number 0<> if rot tuck = if swap dup menu_command[x] getenv dup -1 <> if evaluate 0= if 2drop exit then else drop then else swap then then then 1+ dup 56 > \ increment iterator \ continue if less than 57 until drop \ loop iterator drop \ key pressed again \ Non-operational key was pressed; repeat ; \ This function unsets all the possible environment variables associated with \ creating the interactive menu. \ : menu-unset ( -- ) 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') begin dup menu_init[x] unsetenv \ menu initializer dup menu_command[x] unsetenv \ menu command dup menu_caption[x] unsetenv \ menu caption dup ansi_caption[x] unsetenv \ ANSI caption dup menu_keycode[x] unsetenv \ menu keycode dup toggled_text[x] unsetenv \ toggle_menuitem caption dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9') begin \ cycle_menuitem caption and ANSI caption 2dup menu_caption[x][y] unsetenv 2dup ansi_caption[x][y] unsetenv 1+ dup 57 > until drop \ inner iterator 0 over menukeyN ! \ used by menu-create, menu-display 0 over init_stateN ! \ used by menu-create 0 over toggle_stateN ! \ used by toggle_menuitem 0 over init_textN c! \ used by toggle_menuitem 0 over cycle_stateN ! \ used by cycle_menuitem 1+ dup 56 > \ increment, continue if less than 57 until drop \ iterator s" menu_timeout_command" unsetenv \ menu timeout command s" menu_reboot" unsetenv \ Reboot menu option flag s" menu_acpi" unsetenv \ ACPI menu option flag s" menu_kernel" unsetenv \ Kernel menu option flag s" menu_options" unsetenv \ Options separator flag s" menu_optionstext" unsetenv \ separator display text s" menu_init" unsetenv \ menu initializer 0 menureboot ! 0 menuacpi ! 0 menuoptions ! ; only forth definitions also menu-infrastructure \ This function both unsets menu variables and visually erases the menu area \ in-preparation for another menu. \ : menu-clear ( -- ) menu-unset menu-erase ; bullet menubllt ! also menu-namespace \ Initialize our menu initialization state variables 0 init_state1 ! 0 init_state2 ! 0 init_state3 ! 0 init_state4 ! 0 init_state5 ! 0 init_state6 ! 0 init_state7 ! 0 init_state8 ! \ Initialize our boolean state variables 0 toggle_state1 ! 0 toggle_state2 ! 0 toggle_state3 ! 0 toggle_state4 ! 0 toggle_state5 ! 0 toggle_state6 ! 0 toggle_state7 ! 0 toggle_state8 ! \ Initialize our array state variables 0 cycle_state1 ! 0 cycle_state2 ! 0 cycle_state3 ! 0 cycle_state4 ! 0 cycle_state5 ! 0 cycle_state6 ! 0 cycle_state7 ! 0 cycle_state8 ! \ Initialize string containers 0 init_text1 c! 0 init_text2 c! 0 init_text3 c! 0 init_text4 c! 0 init_text5 c! 0 init_text6 c! 0 init_text7 c! 0 init_text8 c! only forth definitions diff --git a/stand/forth/support.4th b/stand/forth/support.4th index e6addfbdf7bb..d87cf16a16dd 100644 --- a/stand/forth/support.4th +++ b/stand/forth/support.4th @@ -1,1617 +1,1692 @@ \ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org> \ 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 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$ \ Loader.rc support functions: \ \ initialize ( addr len -- ) as above, plus load_conf_files \ load_conf ( addr len -- ) load conf file given \ include_conf_files ( -- ) load all conf files in load_conf_files \ print_syntax_error ( -- ) print line and marker of where a syntax \ error was detected \ print_line ( -- ) print last line processed \ load_kernel ( -- ) load kernel \ load_modules ( -- ) load modules flagged \ \ Exported structures: \ \ string counted string structure \ cell .addr string address \ cell .len string length \ module module loading information structure \ cell module.flag should we load it? \ string module.name module's name \ string module.loadname name to be used in loading the module \ string module.type module's type \ string module.args flags to be passed during load \ string module.beforeload command to be executed before load \ string module.afterload command to be executed after load \ string module.loaderror command to be executed if load fails \ cell module.next list chain \ \ Exported global variables; \ \ string conf_files configuration files to be loaded \ cell modules_options pointer to first module information \ value verbose? indicates if user wants a verbose loading \ value any_conf_read? indicates if a conf file was successfully read \ \ Other exported words: \ note, strlen is internal \ strdup ( addr len -- addr' len) similar to strdup(3) \ strcat ( addr len addr' len' -- addr len+len' ) similar to strcat(3) \ s' ( | string' -- addr len | ) similar to s" \ rudimentary structure support \ Exception values 1 constant ESYNTAX 2 constant ENOMEM 3 constant EFREE 4 constant ESETERROR \ error setting environment variable 5 constant EREAD \ error reading 6 constant EOPEN 7 constant EEXEC \ XXX never catched 8 constant EBEFORELOAD 9 constant EAFTERLOAD \ I/O constants 0 constant SEEK_SET 1 constant SEEK_CUR 2 constant SEEK_END 0 constant O_RDONLY 1 constant O_WRONLY 2 constant O_RDWR \ Crude structure support : structure: create here 0 , ['] drop , 0 does> create here swap dup @ allot cell+ @ execute ; : member: create dup , over , + does> cell+ @ + ; : ;structure swap ! ; : constructor! >body cell+ ! ; : constructor: over :noname ; : ;constructor postpone ; swap cell+ ! ; immediate : sizeof ' >body @ state @ if postpone literal then ; immediate : offsetof ' >body cell+ @ state @ if postpone literal then ; immediate : ptr 1 cells member: ; : int 1 cells member: ; \ String structure structure: string ptr .addr int .len constructor: 0 over .addr ! 0 swap .len ! ;constructor ;structure \ Module options linked list structure: module int module.flag sizeof string member: module.name sizeof string member: module.loadname sizeof string member: module.type sizeof string member: module.args sizeof string member: module.beforeload sizeof string member: module.afterload sizeof string member: module.loaderror ptr module.next ;structure \ Internal loader structures (preloaded_file, kernel_module, file_metadata) \ must be in sync with the C struct in stand/common/bootstrap.h structure: preloaded_file ptr pf.name ptr pf.type ptr pf.args ptr pf.metadata \ file_metadata int pf.loader int pf.addr int pf.size ptr pf.modules \ kernel_module ptr pf.next \ preloaded_file ;structure structure: kernel_module ptr km.name \ ptr km.args ptr km.fp \ preloaded_file ptr km.next \ kernel_module ;structure structure: file_metadata int md.size 2 member: md.type \ this is not ANS Forth compatible (XXX) ptr md.next \ file_metadata 0 member: md.data \ variable size ;structure \ end of structures \ Global variables string conf_files string nextboot_conf_file create module_options sizeof module.next allot 0 module_options ! create last_module_option sizeof module.next allot 0 last_module_option ! 0 value verbose? 0 value nextboot? \ Support string functions : strdup { addr len -- addr' len' } len allocate if ENOMEM throw then addr over len move len ; : strcat { addr len addr' len' -- addr len+len' } addr' addr len + len' move addr len len' + ; : strchr { addr len c -- addr' len' } begin len while addr c@ c = if addr len exit then addr 1 + to addr len 1 - to len repeat 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 ; immediate : 2>r postpone >r postpone >r ; immediate : 2r> postpone r> postpone r> ; immediate : 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate : 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 dup 0= if 2drop 2drop false exit then begin begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop while 1+ swap 1- repeat swap 2 pick 1- over < while 2over 2over drop over compare-insensitive 0= if 2 pick over = if 2drop 2drop true exit then 2 pick tuck - -rot + swap over c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if 2drop 2drop true exit then then begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if false else true then 2 pick 0> and while 1+ swap 1- repeat swap repeat 2drop 2drop false ; : boot_serial? ( -- 0 | -1 ) 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.depth" getenv? ; \ Private definitions vocabulary support-functions only forth also support-functions definitions \ Some control characters constants 7 constant bell 8 constant backspace 9 constant tab 10 constant lf 13 constant <cr> \ Read buffer size 80 constant read_buffer_size \ Standard suffixes : load_module_suffix s" _load" ; : module_loadname_suffix s" _name" ; : module_type_suffix s" _type" ; : module_args_suffix s" _flags" ; : module_beforeload_suffix s" _before" ; : module_afterload_suffix s" _after" ; : module_loaderror_suffix s" _error" ; \ Support operators : >= < 0= ; : <= > 0= ; \ Assorted support functions : free-memory free if EFREE throw then ; : strget { var -- addr len } var .addr @ var .len @ ; \ assign addr len to variable. : strset { addr len var -- } addr var .addr ! len var .len ! ; \ free memory and reset fields : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ; \ free old content, make a copy of the string and assign to variable : string= { addr len var -- } var strfree addr len strdup var strset ; : strtype ( str -- ) strget type ; \ assign a reference to what is on the stack : strref { addr len var -- addr len } addr var .addr ! len var .len ! addr len ; \ unquote a string : unquote ( addr len -- addr len ) over c@ [char] " = if 2 chars - swap char+ swap then ; \ Assignment data temporary storage string name_buffer string value_buffer \ Line by line file reading functions \ \ exported: \ line_buffer \ end_of_file? \ fd \ read_line \ reset_line_reading vocabulary line-reading also line-reading definitions \ File data temporary storage string read_buffer 0 value read_buffer_ptr \ File's line reading function get-current ( -- wid ) previous definitions string line_buffer 0 value end_of_file? variable fd >search ( wid -- ) definitions : skip_newlines begin read_buffer .len @ read_buffer_ptr > while read_buffer .addr @ read_buffer_ptr + c@ lf = if read_buffer_ptr char+ to read_buffer_ptr else exit then repeat ; : scan_buffer ( -- addr len ) read_buffer_ptr >r begin read_buffer .len @ r@ > while read_buffer .addr @ r@ + c@ lf = if read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr exit then r> char+ >r repeat read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr ; : line_buffer_resize ( len -- len ) dup 0= if exit then >r line_buffer .len @ if line_buffer .addr @ line_buffer .len @ r@ + resize if ENOMEM throw then else r@ allocate if ENOMEM throw then then line_buffer .addr ! r> ; : append_to_line_buffer ( addr len -- ) dup 0= if 2drop exit then line_buffer strget 2swap strcat line_buffer .len ! drop ; : read_from_buffer scan_buffer ( -- addr len ) line_buffer_resize ( len -- len ) append_to_line_buffer ( addr len -- ) ; : refill_required? read_buffer .len @ read_buffer_ptr = end_of_file? 0= and ; : refill_buffer 0 to read_buffer_ptr read_buffer .addr @ 0= if read_buffer_size allocate if ENOMEM throw then read_buffer .addr ! then fd @ read_buffer .addr @ read_buffer_size fread dup -1 = if EREAD throw then dup 0= if true to end_of_file? then read_buffer .len ! ; get-current ( -- wid ) previous definitions >search ( wid -- ) : reset_line_reading 0 to read_buffer_ptr ; : read_line line_buffer strfree skip_newlines begin read_from_buffer refill_required? while refill_buffer repeat ; only forth also support-functions definitions \ Conf file line parser: \ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] | \ <spaces>[<comment>] \ <name> ::= <letter>{<letter>|<digit>|'_'} \ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <name> \ <character_set> ::= ASCII 32 to 126, except '\' and '"' \ <comment> ::= '#'{<anything>} \ \ exported: \ line_pointer \ process_conf 0 value line_pointer vocabulary file-processing also file-processing definitions \ parser functions \ \ exported: \ get_assignment vocabulary parser also parser definitions 0 value parsing_function 0 value end_of_line : end_of_line? line_pointer end_of_line = ; \ classifiers for various character classes in the input line : letter? line_pointer c@ >r r@ [char] A >= r@ [char] Z <= and r@ [char] a >= r> [char] z <= and or ; : digit? line_pointer c@ >r r@ [char] - = r@ [char] 0 >= r> [char] 9 <= and or ; : quote? line_pointer c@ [char] " = ; : assignment_sign? line_pointer c@ [char] = = ; : comment? line_pointer c@ [char] # = ; : space? line_pointer c@ bl = line_pointer c@ tab = or ; : backslash? line_pointer c@ [char] \ = ; : underscore? line_pointer c@ [char] _ = ; : dot? line_pointer c@ [char] . = ; \ manipulation of input line : skip_character line_pointer char+ to line_pointer ; : skip_to_end_of_line end_of_line to line_pointer ; : eat_space begin end_of_line? if 0 else space? then while skip_character repeat ; : parse_name ( -- addr len ) line_pointer begin end_of_line? if 0 else letter? digit? underscore? dot? or or or then while skip_character repeat line_pointer over - strdup ; : remove_backslashes { addr len | addr' len' -- addr' len' } len allocate if ENOMEM throw then to addr' addr >r begin addr c@ [char] \ <> if addr c@ addr' len' + c! len' char+ to len' then addr char+ to addr r@ len + addr = until r> drop addr' len' ; : parse_quote ( -- addr len ) line_pointer skip_character end_of_line? if ESYNTAX throw then begin quote? 0= while backslash? if skip_character end_of_line? if ESYNTAX throw then then skip_character end_of_line? if ESYNTAX throw then repeat skip_character line_pointer over - remove_backslashes ; : read_name parse_name ( -- addr len ) name_buffer strset ; : read_value quote? if parse_quote ( -- addr len ) else parse_name ( -- addr len ) then value_buffer strset ; : comment skip_to_end_of_line ; : white_space_4 eat_space comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; : variable_value read_value ['] white_space_4 to parsing_function ; : white_space_3 eat_space letter? digit? quote? or or if ['] variable_value to parsing_function exit then ESYNTAX throw ; : assignment_sign skip_character ['] white_space_3 to parsing_function ; : white_space_2 eat_space assignment_sign? if ['] assignment_sign to parsing_function exit then ESYNTAX throw ; : variable_name read_name ['] white_space_2 to parsing_function ; : white_space_1 eat_space letter? if ['] variable_name to parsing_function exit then comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; get-current ( -- wid ) previous definitions >search ( wid -- ) : get_assignment line_buffer strget + to end_of_line line_buffer .addr @ to line_pointer ['] white_space_1 to parsing_function begin end_of_line? 0= while parsing_function execute repeat parsing_function ['] comment = parsing_function ['] white_space_1 = parsing_function ['] white_space_4 = or or 0= if ESYNTAX throw then ; only forth also support-functions also file-processing definitions \ Process line : assignment_type? ( addr len -- flag ) name_buffer strget compare 0= ; : suffix_type? ( addr len -- flag ) name_buffer .len @ over <= if 2drop false exit then name_buffer .len @ over - name_buffer .addr @ + over compare 0= ; : loader_conf_files? s" loader_conf_files" assignment_type? ; : nextboot_flag? s" nextboot_enable" assignment_type? ; : nextboot_conf? s" nextboot_conf" assignment_type? ; : verbose_flag? s" verbose_loading" assignment_type? ; : execute? s" exec" assignment_type? ; : module_load? load_module_suffix suffix_type? ; : module_loadname? module_loadname_suffix suffix_type? ; : module_type? module_type_suffix suffix_type? ; : module_args? module_args_suffix suffix_type? ; : module_beforeload? module_beforeload_suffix suffix_type? ; : module_afterload? module_afterload_suffix suffix_type? ; : module_loaderror? module_loaderror_suffix suffix_type? ; \ build a 'set' statement and execute it : set_environment_variable name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string allocate if ENOMEM throw then dup 0 \ start with an empty string and append the pieces s" set " strcat name_buffer strget strcat s" =" strcat value_buffer strget strcat ['] evaluate catch if 2drop free drop ESETERROR throw else free-memory then ; : set_conf_files set_environment_variable s" loader_conf_files" getenv conf_files string= ; : set_nextboot_conf value_buffer strget unquote nextboot_conf_file string= ; : append_to_module_options_list ( addr -- ) module_options @ 0= if dup module_options ! last_module_option ! else dup last_module_option @ module.next ! last_module_option ! then ; : set_module_name { addr -- } \ check leaks name_buffer strget addr module.name string= ; : yes_value? value_buffer strget \ XXX could use unquote 2dup s' "YES"' compare >r 2dup s' "yes"' compare >r 2dup s" YES" compare >r s" yes" compare r> r> r> and and and 0= ; : find_module_option ( -- addr | 0 ) \ return ptr to entry matching name_buffer module_options @ begin dup while dup module.name strget name_buffer strget compare 0= if exit then module.next @ repeat ; : new_module_option ( -- addr ) sizeof module allocate if ENOMEM throw then dup sizeof module erase dup append_to_module_options_list dup set_module_name ; : get_module_option ( -- addr ) find_module_option ?dup 0= if new_module_option then ; : set_module_flag name_buffer .len @ load_module_suffix nip - name_buffer .len ! yes_value? get_module_option module.flag ! ; : set_module_args name_buffer .len @ module_args_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.args string= ; : set_module_loadname name_buffer .len @ module_loadname_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loadname string= ; : set_module_type name_buffer .len @ module_type_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.type string= ; : set_module_beforeload name_buffer .len @ module_beforeload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.beforeload string= ; : set_module_afterload name_buffer .len @ module_afterload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.afterload string= ; : set_module_loaderror name_buffer .len @ module_loaderror_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loaderror string= ; : set_nextboot_flag yes_value? to nextboot? ; : set_verbose yes_value? to verbose? ; : execute_command value_buffer strget unquote ['] evaluate catch if EEXEC throw then ; : process_assignment name_buffer .len @ 0= if exit then loader_conf_files? if set_conf_files exit then nextboot_flag? if set_nextboot_flag exit then nextboot_conf? if set_nextboot_conf exit then verbose_flag? if set_verbose exit then execute? if execute_command exit then module_load? if set_module_flag exit then module_loadname? if set_module_loadname exit then module_type? if set_module_type exit then module_args? if set_module_args exit then module_beforeload? if set_module_beforeload exit then module_afterload? if set_module_afterload exit then module_loaderror? if set_module_loaderror exit then set_environment_variable ; \ free_buffer ( -- ) \ \ Free some pointers if needed. The code then tests for errors \ in freeing, and throws an exception if needed. If a pointer is \ not allocated, it's value (0) is used as flag. : free_buffers name_buffer strfree value_buffer strfree ; \ Higher level file processing get-current ( -- wid ) previous definitions >search ( wid -- ) : process_conf begin end_of_file? 0= while free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch swap throw throw repeat ; : peek_file ( addr len -- ) 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch fd @ fclose swap throw throw ; only forth also support-functions definitions \ Interface to loading conf files : load_conf ( addr len -- ) 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then ['] process_conf catch fd @ fclose throw ; : print_line line_buffer strtype cr ; : print_syntax_error line_buffer strtype cr line_buffer .addr @ begin line_pointer over <> while bl emit char+ repeat drop ." ^" cr ; \ Debugging support functions only forth definitions also support-functions : test-file ['] load_conf catch dup . ESYNTAX = if cr print_syntax_error then ; \ find a module name, leave addr on the stack (0 if not found) : find-module ( <module> -- ptr | 0 ) bl parse ( addr len ) module_options @ >r ( store current pointer ) begin r@ while 2dup ( addr len addr len ) r@ module.name strget compare 0= if drop drop r> exit then ( found it ) r> module.next @ >r repeat type ." was not found" cr r> ; : show-nonempty ( addr len mod -- ) strget dup verbose? or if 2swap type type cr else drop drop drop drop then ; : show-one-module { addr -- addr } ." Name: " addr module.name strtype cr s" Path: " addr module.loadname show-nonempty s" Type: " addr module.type show-nonempty s" Flags: " addr module.args show-nonempty s" Before load: " addr module.beforeload show-nonempty s" After load: " addr module.afterload show-nonempty s" Error: " addr module.loaderror show-nonempty ." Status: " addr module.flag @ if ." Load" else ." Don't load" then cr cr addr ; : show-module-options module_options @ begin ?dup while show-one-module module.next @ repeat ; : free-one-module { addr -- addr } addr module.name strfree addr module.loadname strfree addr module.type strfree addr module.args strfree addr module.beforeload strfree addr module.afterload strfree addr module.loaderror strfree addr ; : free-module-options module_options @ begin ?dup while free-one-module dup module.next @ swap free-memory repeat 0 module_options ! 0 last_module_option ! ; only forth also support-functions definitions \ Variables used for processing multiple conf files string current_file_name_ref \ used to print the file name \ Indicates if any conf file was successfully read 0 value any_conf_read? \ loader_conf_files processing support functions : get_conf_files ( -- addr len ) \ put addr/len on stack, reset var conf_files strget 0 0 conf_files strset ; : skip_leading_spaces { addr len pos -- addr len pos' } begin pos len = if 0 else addr pos + c@ bl = then while pos char+ to pos repeat addr len pos ; \ return the file name at pos, or free the string if nothing left : get_file_name { addr len pos -- addr len pos' addr' len' || 0 } pos len = if addr free abort" Fatal error freeing memory" 0 exit then pos >r begin \ stay in the loop until have chars and they are not blank pos len = if 0 else addr pos + c@ bl <> then while pos char+ to pos repeat addr len pos addr r@ + pos r> - ; : get_next_file ( addr len ptr -- addr len ptr' addr' len' | 0 ) skip_leading_spaces get_file_name ; : print_current_file current_file_name_ref strtype ; : process_conf_errors dup 0= if true to any_conf_read? drop exit then >r 2drop r> dup ESYNTAX = if ." Warning: syntax error on file " print_current_file cr print_syntax_error drop exit then dup ESETERROR = if ." Warning: bad definition on file " print_current_file cr print_line drop exit then dup EREAD = if ." Warning: error reading file " print_current_file cr drop exit then dup EOPEN = if verbose? if ." Warning: unable to open file " print_current_file cr then drop exit then dup EFREE = abort" Fatal error freeing memory" dup ENOMEM = abort" Out of memory" throw \ Unknown error -- pass ahead ; \ Process loader_conf_files recursively \ Interface to loader_conf_files processing : include_conf_files get_conf_files 0 ( addr len offset ) begin get_next_file ?dup ( addr len 1 | 0 ) while current_file_name_ref strref ['] load_conf catch process_conf_errors conf_files .addr @ if recurse then repeat ; : get_nextboot_conf_file ( -- addr len ) nextboot_conf_file strget ; : rewrite_nextboot_file ( -- ) get_nextboot_conf_file O_WRONLY fopen fd ! fd @ -1 = if EOPEN throw then fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop fd @ fclose ; : include_nextboot_file ( -- ) s" nextboot_enable" getenv dup -1 <> if 2dup s' "YES"' compare >r 2dup s' "yes"' compare >r 2dup s" YES" compare >r 2dup s" yes" compare r> r> r> and and and 0= to nextboot? else drop get_nextboot_conf_file ['] peek_file catch if 2drop then then nextboot? if get_nextboot_conf_file current_file_name_ref strref ['] load_conf catch process_conf_errors ['] rewrite_nextboot_file catch if 2drop then then s' "NO"' s" nextboot_enable" setenv ; \ Module loading functions : load_parameters { addr -- addr addrN lenN ... addr1 len1 N } addr addr module.args strget addr module.loadname .len @ if addr module.loadname strget else addr module.name strget then addr module.type .len @ if addr module.type strget s" -t " 4 ( -t type name flags ) else 2 ( name flags ) then ; : before_load ( addr -- addr ) dup module.beforeload .len @ if dup module.beforeload strget ['] evaluate catch if EBEFORELOAD throw then then ; : after_load ( addr -- addr ) dup module.afterload .len @ if dup module.afterload strget ['] evaluate catch if EAFTERLOAD throw then then ; : load_error ( addr -- addr ) dup module.loaderror .len @ if dup module.loaderror strget evaluate \ This we do not intercept so it can throw errors then ; : pre_load_message ( addr -- addr ) verbose? if dup module.name strtype ." ..." then ; : load_error_message verbose? if ." failed!" cr then ; : load_successful_message verbose? if ." ok" cr then ; : load_module load_parameters load ; : process_module ( addr -- addr ) pre_load_message before_load begin ['] load_module catch if dup module.loaderror .len @ if load_error \ Command should return a flag! else load_error_message true \ Do not retry then else after_load load_successful_message true \ Successful, do not retry then until ; : process_module_errors ( addr ior -- ) dup EBEFORELOAD = if drop ." Module " dup module.name strtype dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.beforeload strtype cr \ XXX there was a typo here abort then dup EAFTERLOAD = if drop ." Module " dup module.name .addr @ over module.name .len @ type dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.afterload strtype cr abort then throw \ Don't know what it is all about -- pass ahead ; \ Module loading interface \ scan the list of modules, load enabled ones. : load_modules ( -- ) ( throws: abort & user-defined ) module_options @ ( list_head ) begin ?dup while dup module.flag @ if ['] process_module catch process_module_errors then module.next @ repeat ; \ h00h00 magic used to try loading either a kernel with a given name, \ or a kernel with the default name in a directory of a given name \ (the pain!) : bootpath s" /boot/" ; : modulepath s" module_path" ; \ Functions used to save and restore module_path's value. : saveenv ( addr len | -1 -- addr' len | 0 -1 ) dup -1 = if 0 swap exit then strdup ; : freeenv ( addr len | 0 -1 ) -1 = if drop else free abort" Freeing error" then ; : restoreenv ( addr len | 0 -1 -- ) dup -1 = if ( it wasn't set ) 2drop modulepath unsetenv else over >r modulepath setenv r> free abort" Freeing error" then ; : clip_args \ Drop second string if only one argument is passed 1 = if 2swap 2drop 1 else 2 then ; also builtins \ Parse filename from a semicolon-separated list \ replacement, not working yet : newparse-; { addr len | a1 -- a' len-x addr x } addr len [char] ; strchr dup if ( a1 len1 ) swap to a1 ( store address ) 1 - a1 @ 1 + swap ( remove match ) addr a1 addr - else 0 0 addr len then ; : parse-; ( addr len -- addr' len-x addr x ) over 0 2swap ( addr 0 addr len ) begin dup 0 <> ( addr 0 addr len ) while over c@ [char] ; <> ( addr 0 addr len flag ) while 1- swap 1+ swap 2swap 1+ 2swap repeat then dup 0 <> if 1- swap 1+ swap then 2swap ; \ Try loading one of multiple kernels specified : try_multiple_kernels ( addr len addr' len' args -- flag ) >r begin parse-; 2>r 2over 2r> r@ clip_args s" DEBUG" getenv? if s" echo Module_path: ${module_path}" evaluate ." Kernel : " >r 2dup type r> cr dup 2 = if ." Flags : " >r 2over type r> cr then then 1 load while dup 0= until 1 >r \ Failure else 0 >r \ Success then 2drop 2drop r> r> drop ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if available. If not, dummy values must be given. \ \ The kernel gets loaded from the current module_path. : load_a_kernel ( flags len 1 | x x 0 -- flag ) local args 2local flags 0 0 2local kernel end-locals \ Check if a default kernel name exists at all, exits if not s" bootfile" getenv dup -1 <> if to kernel flags kernel args 1+ try_multiple_kernels dup 0= if exit then then drop s" kernel" getenv dup -1 <> if to kernel else drop 1 exit \ Failure then \ Try all default kernel names flags kernel args 1+ try_multiple_kernels ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ The module_path variable is overridden if load is successful, by \ prepending the successful path. : load_from_directory ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags 0 0 2local oldmodulepath \ like a string 0 0 2local newmodulepath \ like a string end-locals \ Set the environment variable module_path, and try loading \ the kernel again. modulepath getenv saveenv to oldmodulepath \ Try prepending /boot/ first bootpath nip path nip + \ total length oldmodulepath nip dup -1 = if drop else 1+ + \ add oldpath -- XXX why the 1+ ? then allocate if ( out of memory ) 1 exit then \ XXX throw ? 0 bootpath strcat path strcat 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel 0= if ( success ) oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 exit then \ Well, try without the prepended /boot/ path newmodulepath drop swap move newmodulepath drop path nip 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel if ( failed once more ) oldmodulepath restoreenv newmodulepath drop free-memory 1 else oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 then ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ 3. The "path" argument \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ Unless "path" is meant to be kernel name itself. In that case, it \ will first be tried as a full path, and, next, search on the \ directories pointed by module_path. \ \ The module_path variable is overridden if load is successful, by \ prepending the successful path. : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags end-locals \ First, assume path is an absolute path to a directory flags path args clip_args load_from_directory dup 0= if exit else drop then \ Next, assume path points to the kernel flags path args try_multiple_kernels ; : initialize ( addr len -- ) strdup conf_files strset ; : kernel_options ( -- addr len 1 | 0 ) s" kernel_options" getenv dup -1 = if drop 0 else 1 then ; : standard_kernel_search ( flags 1 | 0 -- flag ) local args args 0= if 0 0 then 2local flags s" kernel" getenv dup -1 = if 0 swap then 2local path end-locals path nip -1 = if ( there isn't a "kernel" environment variable ) flags args load_a_kernel else flags path args 1+ clip_args load_directory_or_file then ; : load_kernel ( -- ) ( throws: abort ) kernel_options standard_kernel_search abort" Unable to load a kernel!" ; : load_xen ( -- flag ) s" xen_kernel" getenv dup -1 <> if 1 1 load ( c-addr/u flag N -- flag ) else drop 0 ( -1 -- flag ) then ; : load_xen_throw ( -- ) ( throws: abort ) load_xen abort" Unable to load Xen!" ; : set_defaultoptions ( -- ) s" kernel_options" getenv dup -1 = if drop else s" temp_options" setenv then ; \ pick the i-th argument, i starts at 0 : argv[] ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 ) 2dup = if 0 0 exit then \ out of range dup >r 1+ 2* ( skip N and ui ) pick r> 1+ 2* ( skip N and ai ) pick ; : drop_args ( aN uN ... a1 u1 N -- ) 0 ?do 2drop loop ; : argc dup ; : queue_argv ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 ) >r over 2* 1+ -roll r> over 2* 1+ -roll 1+ ; : unqueue_argv ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 ) 1- -rot ; \ compute the length of the buffer including the spaces between words : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len ) dup 0= if 0 exit then 0 >r \ Size 0 >r \ Index begin argc r@ <> while r@ argv[] nip r> r> rot + 1+ >r 1+ >r repeat r> drop r> ; : concat_argv ( aN uN ... a1 u1 N -- a u ) strlen(argv) allocate if ENOMEM throw then 0 2>r ( save addr 0 on return stack ) begin dup while unqueue_argv ( ... N a1 u1 ) 2r> 2swap ( old a1 u1 ) strcat s" " strcat ( append one space ) \ XXX this gives a trailing space 2>r ( store string on the result stack ) repeat drop_args 2r> ; : set_tempoptions ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 ) \ Save the first argument, if it exists and is not a flag argc if 0 argv[] drop c@ [char] - <> if unqueue_argv 2>r \ Filename 1 >r \ Filename present else 0 >r \ Filename not present then else 0 >r \ Filename not present then \ If there are other arguments, assume they are flags ?dup if concat_argv 2dup s" temp_options" setenv drop free if EFREE throw then else set_defaultoptions then \ Bring back the filename, if one was provided r> if 2r> 1 else 0 then ; : get_arguments ( -- addrN lenN ... addr1 len1 N ) 0 begin \ Get next word on the command line parse-word ?dup while queue_argv repeat drop ( empty string ) ; : load_kernel_and_modules ( args -- flag ) set_tempoptions argc >r s" temp_options" getenv dup -1 <> if queue_argv else drop then load_xen ?dup 0= if ( success ) r> if ( a path was passed ) load_directory_or_file else standard_kernel_search then ?dup 0= if ['] load_modules catch then then ; only forth definitions diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile index 779575bff071..3dea78f16929 100644 --- a/stand/i386/libi386/Makefile +++ b/stand/i386/libi386/Makefile @@ -1,47 +1,52 @@ # $FreeBSD$ .include <bsd.init.mk> LIB= i386 SRCS= bio.c biosacpi.c biosdisk.c biosmem.c biospnp.c \ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ comconsole.c devicename.c elf32_freebsd.c \ elf64_freebsd.c 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} .PATH: ${SYSDIR}/teken SRCS+= teken.c BOOT_COMCONSOLE_PORT?= 0x3f8 CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} BOOT_COMCONSOLE_SPEED?= 9600 CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} .ifdef(BOOT_BIOSDISK_DEBUG) # Make the disk code more talkative CFLAGS+= -DDISK_DEBUG .endif # terminal emulation -CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken +.if ${BOOT_FRAMEBUFFER_MODE:Uno} == "yes" +CFLAGS.vidconsole.c+= -DFRAMEBUFFER_MODE +.endif +CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite CFLAGS.teken.c+= -I${SRCTOP}/sys/teken +CFLAGS.bootinfo.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite +CFLAGS.vbe.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite # XXX: make alloca() useable CFLAGS+= -Dalloca=__builtin_alloca CFLAGS+= -I${BOOTSRC}/ficl -I${BOOTSRC}/ficl/i386 \ -I${LDRSRC} -I${BOOTSRC}/i386/common \ -I${SYSDIR}/cddl/boot/zfs \ -I${SYSDIR}/cddl/contrib/opensolaris/uts/common \ -I${SYSDIR}/contrib/dev/acpica/include # Handle FreeBSD specific %b and %D printf format specifiers CFLAGS+= ${FORMAT_EXTENSIONS} .include <bsd.lib.mk> diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c index 41901e5f928f..71e07cfb9702 100644 --- a/stand/i386/libi386/bootinfo.c +++ b/stand/i386/libi386/bootinfo.c @@ -1,112 +1,123 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> #include <sys/param.h> #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) { char *curpos, *next, *string; int howto; int vidconsole; howto = boot_parse_cmdline(kargs); howto |= boot_env_to_howto(); /* Enable selected consoles */ string = next = strdup(getenv("console")); vidconsole = 0; while (next != NULL) { curpos = strsep(&next, " ,"); if (*curpos == '\0') continue; if (!strcmp(curpos, "vidconsole")) vidconsole = 1; else if (!strcmp(curpos, "comconsole")) howto |= RB_SERIAL; else if (!strcmp(curpos, "nullconsole")) howto |= RB_MUTE; } if (vidconsole && (howto & RB_SERIAL)) howto |= RB_MULTIPLE; /* * XXX: Note that until the kernel is ready to respect multiple consoles * for the boot messages, the first named console is the primary console */ if (!strcmp(string, "vidconsole")) howto &= ~RB_SERIAL; free(string); return(howto); } void bi_setboothowto(int howto) { boot_howto_to_env(howto); } /* * Copy the environment into the load area starting at (addr). * Each variable is formatted as <name>=<value>, with a single nul * separating each variable, and a double nul terminating the environment. */ vm_offset_t bi_copyenv(vm_offset_t addr) { struct env_var *ep; /* traverse the environment */ for (ep = environ; ep != NULL; ep = ep->ev_next) { i386_copyin(ep->ev_name, addr, strlen(ep->ev_name)); addr += strlen(ep->ev_name); i386_copyin("=", addr, 1); addr++; if (ep->ev_value != NULL) { i386_copyin(ep->ev_value, addr, strlen(ep->ev_value)); addr += strlen(ep->ev_value); } i386_copyin("", addr, 1); addr++; } i386_copyin("", addr, 1); addr++; return(addr); } diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c index e6a92a2164d7..23b02693cf48 100644 --- a/stand/i386/libi386/bootinfo32.c +++ b/stand/i386/libi386/bootinfo32.c @@ -1,278 +1,281 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> #include <sys/param.h> #include <sys/reboot.h> #include <sys/linker.h> #include <machine/bootinfo.h> #include <machine/metadata.h> #include "bootstrap.h" #include "libi386.h" #include "btxv86.h" #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" #endif static struct bootinfo bi; /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ i386_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c); \ if (c) \ i386_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(u_long));\ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ i386_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(u_long)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c); \ COPY32(mm->md_size, a, c); \ if (c) \ i386_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(u_long));\ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules32(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; c = addr != 0; /* start with the first module on the list, should be the kernel */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* this field must come first */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); MOD_ADDR(addr, fp->f_addr, c); MOD_SIZE(addr, fp->f_size, c); for (md = fp->f_metadata; md != NULL; md = md->md_next) if (!(md->md_type & MODINFOMD_NOCOPY)) MOD_METADATA(addr, md, c); } MOD_END(addr, c); return(addr); } /* * Load the information expected by an i386 kernel. * * - The 'boothowto' argument is constructed * - The 'bootdev' argument is constructed * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp) { struct preloaded_file *xp, *kfp; struct i386_devdesc *rootdev; struct file_metadata *md; vm_offset_t addr; vm_offset_t kernend; vm_offset_t envp; vm_offset_t size; vm_offset_t ssym, esym; char *rootdevname; int bootdevnr, i, howto; char *kernelname; const char *kernelpath; howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied device * This should perhaps go to MI code and/or have $rootdev tested/set by * MI code before launching the kernel. */ rootdevname = getenv("rootdev"); i386_getdev((void **)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { /* bad $rootdev/$currdev */ printf("can't determine root device\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(i386_fmtdev((void *)rootdev)); /* Do legacy rootdev guessing */ /* XXX - use a default bootdev of 0. Is this ok??? */ bootdevnr = 0; switch(rootdev->dd.d_dev->dv_type) { case DEVT_CD: case DEVT_DISK: /* pass in the BIOS device number of the current disk */ bi.bi_bios_dev = bd_unit2bios(rootdev); bootdevnr = bd_getdev(rootdev); break; case DEVT_NET: case DEVT_ZFS: break; default: printf("WARNING - don't know how to boot from device type %d\n", rootdev->dd.d_dev->dv_type); } if (bootdevnr == -1) { printf("root device %s invalid\n", i386_fmtdev(rootdev)); return (EINVAL); } free(rootdev); /* find the last module in the chain */ addr = 0; for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { if (addr < (xp->f_addr + xp->f_size)) addr = xp->f_addr + xp->f_size; } /* pad to a page boundary */ addr = roundup(addr, PAGE_SIZE); + addr = build_font_module(addr); + /* copy our environment */ envp = addr; addr = bi_copyenv(addr); /* pad to a page boundary */ addr = roundup(addr, PAGE_SIZE); kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf32 kernel"); 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_KERNEND, sizeof kernend, &kernend); bios_addsmapdata(kfp); #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; size = bi_copymodules32(0); kernend = roundup(addr + size, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); /* copy module list and metadata */ (void)bi_copymodules32(addr); ssym = esym = 0; md = file_findmetadata(kfp, MODINFOMD_SSYM); if (md != NULL) ssym = *((vm_offset_t *)&(md->md_data)); md = file_findmetadata(kfp, MODINFOMD_ESYM); if (md != NULL) esym = *((vm_offset_t *)&(md->md_data)); if (ssym == 0 || esym == 0) ssym = esym = 0; /* sanity */ /* legacy bootinfo structure */ kernelname = getenv("kernelname"); i386_getdev(NULL, kernelname, &kernelpath); bi.bi_version = BOOTINFO_VERSION; bi.bi_kernelname = 0; /* XXX char * -> kernel name */ bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */ bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */ for (i = 0; i < N_BIOS_GEOM; i++) bi.bi_bios_geom[i] = bd_getbigeom(i); bi.bi_size = sizeof(bi); bi.bi_memsizes_valid = 1; bi.bi_basemem = bios_basemem / 1024; bi.bi_extmem = bios_extmem / 1024; bi.bi_envp = envp; bi.bi_modulep = *modulep; bi.bi_kernend = kernend; bi.bi_kernelname = VTOP(kernelpath); bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */ bi.bi_esymtab = esym; /* legacy boot arguments */ *howtop = howto | RB_BOOTINFO; *bootdevp = bootdevnr; *bip = VTOP(&bi); return(0); } diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c index 93723a6943e3..9f038f2a4f2b 100644 --- a/stand/i386/libi386/bootinfo64.c +++ b/stand/i386/libi386/bootinfo64.c @@ -1,271 +1,274 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> #include <sys/param.h> #include <sys/reboot.h> #include <sys/linker.h> #include <machine/bootinfo.h> #include <machine/cpufunc.h> #include <machine/metadata.h> #include <machine/psl.h> #include <machine/specialreg.h> #include "bootstrap.h" #include "libi386.h" #include "btxv86.h" #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" #endif /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ i386_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c); \ if (c) \ i386_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(uint64_t));\ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ i386_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(uint64_t)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c); \ COPY32(mm->md_size, a, c); \ if (c) \ i386_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(uint64_t));\ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules64(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; uint64_t v; c = addr != 0; /* start with the first module on the list, should be the kernel */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* this field must come first */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); v = fp->f_addr; MOD_ADDR(addr, v, c); v = fp->f_size; MOD_SIZE(addr, v, c); for (md = fp->f_metadata; md != NULL; md = md->md_next) if (!(md->md_type & MODINFOMD_NOCOPY)) MOD_METADATA(addr, md, c); } MOD_END(addr, c); return(addr); } /* * Check to see if this CPU supports long mode. */ static int bi_checkcpu(void) { char *cpu_vendor; int vendor[3]; int eflags; unsigned int regs[4]; /* Check for presence of "cpuid". */ eflags = read_eflags(); write_eflags(eflags ^ PSL_ID); if (!((eflags ^ read_eflags()) & PSL_ID)) return (0); /* Fetch the vendor string. */ do_cpuid(0, regs); vendor[0] = regs[1]; vendor[1] = regs[3]; vendor[2] = regs[2]; cpu_vendor = (char *)vendor; /* Check for vendors that support AMD features. */ if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 && strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 && strncmp(cpu_vendor, HYGON_VENDOR_ID, 12) != 0 && strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0) return (0); /* Has to support AMD features. */ do_cpuid(0x80000000, regs); if (!(regs[0] >= 0x80000001)) return (0); /* Check for long mode. */ do_cpuid(0x80000001, regs); return (regs[3] & AMDID_LM); } /* * Load the information expected by an amd64 kernel. * * - The 'boothowto' argument is constructed * - The 'bootdev' argument is constructed * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, vm_offset_t *kernendp, int add_smap) { struct preloaded_file *xp, *kfp; struct i386_devdesc *rootdev; struct file_metadata *md; uint64_t kernend; uint64_t envp; uint64_t module; vm_offset_t size; char *rootdevname; int howto; if (!bi_checkcpu()) { printf("CPU doesn't support long mode\n"); return (EINVAL); } howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied device * This should perhaps go to MI code and/or have $rootdev tested/set by * MI code before launching the kernel. */ rootdevname = getenv("rootdev"); i386_getdev((void **)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { /* bad $rootdev/$currdev */ printf("can't determine root device\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(i386_fmtdev((void *)rootdev)); if (addr == 0) { /* find the last module in the chain */ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { if (addr < (xp->f_addr + xp->f_size)) addr = xp->f_addr + xp->f_size; } } /* pad to a page boundary */ addr = roundup(addr, PAGE_SIZE); + addr = build_font_module(addr); + /* place the metadata before anything */ module = *modulep = addr; kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); 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_KERNEND, sizeof kernend, &kernend); file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module); if (add_smap != 0) bios_addsmapdata(kfp); #ifdef LOADER_GELI_SUPPORT geli_export_key_metadata(kfp); #endif + bi_load_vbe_data(kfp); size = bi_copymodules64(0); /* copy our environment */ envp = roundup(addr + size, PAGE_SIZE); addr = bi_copyenv(envp); /* set kernend */ kernend = roundup(addr, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); /* patch MODINFOMD_ENVP */ md = file_findmetadata(kfp, MODINFOMD_ENVP); bcopy(&envp, md->md_data, sizeof envp); /* copy module list and metadata */ (void)bi_copymodules64(*modulep); return(0); } diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h index d25df8fc44a5..dbd6565f8d1c 100644 --- a/stand/i386/libi386/libi386.h +++ b/stand/i386/libi386/libi386.h @@ -1,156 +1,157 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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$ */ /* * i386 fully-qualified device descriptor. */ struct i386_devdesc { struct devdesc dd; /* Must be first. */ union { struct { int slice; int partition; off_t offset; } biosdisk; struct { uint64_t pool_guid; uint64_t root_guid; } zfs; } d_kind; }; /* * relocater trampoline support. */ struct relocate_data { uint32_t src; uint32_t dest; uint32_t size; }; extern void relocater(void); /* * The relocater_data[] is fixed size array allocated in relocater_tramp.S */ extern struct relocate_data relocater_data[]; extern uint32_t relocater_size; extern uint16_t relocator_ip; extern uint16_t relocator_cs; extern uint16_t relocator_ds; extern uint16_t relocator_es; extern uint16_t relocator_fs; extern uint16_t relocator_gs; extern uint16_t relocator_ss; extern uint16_t relocator_sp; extern uint32_t relocator_esi; extern uint32_t relocator_eax; extern uint32_t relocator_ebx; extern uint32_t relocator_edx; extern uint32_t relocator_ebp; extern uint16_t relocator_a20_enabled; int i386_getdev(void **vdev, const char *devspec, const char **path); char *i386_fmtdev(void *vdev); int i386_setcurrdev(struct env_var *ev, int flags, const void *value); extern struct devdesc currdev; /* our current device */ #define MAXDEV 31 /* maximum number of distinct devices */ #define MAXBDDEV MAXDEV #include <readin.h> /* exported devices XXX rename? */ extern struct devsw bioscd; extern struct devsw biosfd; extern struct devsw bioshd; extern struct devsw pxedisk; extern struct fs_ops pxe_fsops; int bc_add(int biosdev); /* Register CD booted from. */ uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */ int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */ int bd_unit2bios(struct i386_devdesc *); /* xlate biosdisk -> BIOS device */ int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t i386_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); struct preloaded_file; void bios_addsmapdata(struct preloaded_file *); void bios_getsmap(void); void bios_getmem(void); extern uint32_t bios_basemem; /* base memory in bytes */ extern uint32_t bios_extmem; /* extended memory in bytes */ extern vm_offset_t memtop; /* last address of physical memory + 1 */ extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */ /* when heap is at the top of */ /* extended memory; for other cases */ /* just the same as memtop */ extern uint32_t high_heap_size; /* extended memory region available */ extern vm_offset_t high_heap_base; /* for use as the heap */ /* 16KB buffer space for real mode data transfers. */ #define BIO_BUFFER_SIZE 0x4000 void *bio_alloc(size_t size); void bio_free(void *ptr, size_t size); /* * Values for width parameter to biospci_{read,write}_config */ #define BIOSPCI_8BITS 0 #define BIOSPCI_16BITS 1 #define BIOSPCI_32BITS 2 void biospci_detect(void); int biospci_find_devclass(uint32_t class, int index, uint32_t *locator); int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val); uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function); int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val); void biosacpi_detect(void); int i386_autoload(void); +void bi_load_vbe_data(struct preloaded_file *kfp); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, vm_offset_t *kernend, int add_smap); void pxe_enable(void *pxeinfo); diff --git a/stand/i386/libi386/vbe.c b/stand/i386/libi386/vbe.c new file mode 100644 index 000000000000..7681eb633b85 --- /dev/null +++ b/stand/i386/libi386/vbe.c @@ -0,0 +1,1226 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * Copyright 2020 Toomas Soome + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +#include <stand.h> +#include <sys/param.h> +#include <machine/psl.h> +#include <machine/cpufunc.h> +#include <stdbool.h> +#include <bootstrap.h> +#include <btxv86.h> +#include <gfx_fb.h> +#include <dev/vt/hw/vga/vt_vga_reg.h> +#include "libi386.h" +#include "vbe.h" + +/* + * VESA BIOS Extensions routines + */ + +static struct vbeinfoblock *vbe; +static struct modeinfoblock *vbe_mode; +/* The default VGA color palette format is 6 bits per primary color. */ +int palette_format = 6; + +#define VESA_MODE_BASE 0x100 +#define VESA_MODE_MAX 0x1ff +#define VESA_MODE_COUNT (VESA_MODE_MAX - VESA_MODE_BASE + 1) + +/* + * palette array for 8-bit indexed colors. In this case, cmap does store + * index and pe8 does store actual RGB. This is needed because we may + * not be able to read palette data from hardware. + */ +struct paletteentry *pe8 = NULL; + +static struct named_resolution { + const char *name; + const char *alias; + unsigned int width; + unsigned int height; +} resolutions[] = { + { + .name = "480p", + .width = 640, + .height = 480, + }, + { + .name = "720p", + .width = 1280, + .height = 720, + }, + { + .name = "1080p", + .width = 1920, + .height = 1080, + }, + { + .name = "2160p", + .alias = "4k", + .width = 3840, + .height = 2160, + }, + { + .name = "5k", + .width = 5120, + .height = 2880, + } +}; + +static bool +vbe_resolution_compare(struct named_resolution *res, const char *cmp) +{ + + if (strcasecmp(res->name, cmp) == 0) + return (true); + if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0) + return (true); + return (false); +} + +static void +vbe_get_max_resolution(int *width, int *height) +{ + struct named_resolution *res; + char *maxres; + char *height_start, *width_start; + int idx; + + *width = *height = 0; + maxres = getenv("vbe_max_resolution"); + /* No max_resolution set? Bail out; choose highest resolution */ + if (maxres == NULL) + return; + /* See if it matches one of our known resolutions */ + for (idx = 0; idx < nitems(resolutions); ++idx) { + res = &resolutions[idx]; + if (vbe_resolution_compare(res, maxres)) { + *width = res->width; + *height = res->height; + return; + } + } + /* Not a known resolution, try to parse it; make a copy we can modify */ + maxres = strdup(maxres); + if (maxres == NULL) + return; + height_start = strchr(maxres, 'x'); + if (height_start == NULL) { + free(maxres); + return; + } + width_start = maxres; + *height_start++ = 0; + /* Errors from this will effectively mean "no max" */ + *width = (int)strtol(width_start, NULL, 0); + *height = (int)strtol(height_start, NULL, 0); + free(maxres); +} + +int +vga_get_reg(int reg, int index) +{ + return (inb(reg + index)); +} + +int +vga_get_atr(int reg, int i) +{ + int ret; + + (void) inb(reg + VGA_GEN_INPUT_STAT_1); + outb(reg + VGA_AC_WRITE, i); + ret = inb(reg + VGA_AC_READ); + + (void) inb(reg + VGA_GEN_INPUT_STAT_1); + + return (ret); +} + +void +vga_set_atr(int reg, int i, int v) +{ + (void) inb(reg + VGA_GEN_INPUT_STAT_1); + outb(reg + VGA_AC_WRITE, i); + outb(reg + VGA_AC_WRITE, v); + + (void) inb(reg + VGA_GEN_INPUT_STAT_1); +} + +void +vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val) +{ + outb(reg + indexreg, index); + outb(reg + datareg, val); +} + +int +vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index) +{ + outb(reg + indexreg, index); + return (inb(reg + datareg)); +} + +int +vga_get_crtc(int reg, int i) +{ + return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i)); +} + +void +vga_set_crtc(int reg, int i, int v) +{ + vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v); +} + +int +vga_get_seq(int reg, int i) +{ + return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i)); +} + +void +vga_set_seq(int reg, int i, int v) +{ + vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v); +} + +int +vga_get_grc(int reg, int i) +{ + return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i)); +} + +void +vga_set_grc(int reg, int i, int v) +{ + vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v); +} + +/* Actually assuming mode 3. */ +void +bios_set_text_mode(int mode) +{ + int atr; + + if (vbe->Capabilities & VBE_CAP_DAC8) { + int m; + + /* + * The mode change should reset the palette format to + * 6 bits, but apparently some systems do fail with 8-bit + * palette, so we switch to 6-bit here. + */ + m = 0x0600; + (void) biosvbe_palette_format(&m); + palette_format = m; + } + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = mode; /* set VGA text mode */ + v86int(); + atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL); + atr &= ~VGA_AC_MC_BI; + atr &= ~VGA_AC_MC_ELG; + vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr); + + gfx_state.tg_mode = mode; + gfx_state.tg_fb_type = FB_TEXT; + gfx_state.tg_fb.fb_height = TEXT_ROWS; + gfx_state.tg_fb.fb_width = TEXT_COLS; + + gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16; + gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8; + gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0; + gfx_state.tg_ctype = CT_INDEXED; + env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL); +} + +/* Function 00h - Return VBE Controller Information */ +static int +biosvbe_info(struct vbeinfoblock *vbep) +{ + struct vbeinfoblock *rvbe; + int ret; + + if (vbep == NULL) + return (VBE_FAILED); + + rvbe = bio_alloc(sizeof(*rvbe)); + if (rvbe == NULL) + return (VBE_FAILED); + + /* Now check if we have vesa. */ + memset(rvbe, 0, sizeof (*vbe)); + memcpy(rvbe->VbeSignature, "VBE2", 4); + + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f00; + v86.es = VTOPSEG(rvbe); + v86.edi = VTOPOFF(rvbe); + v86int(); + ret = v86.eax & 0xffff; + + if (ret != VBE_SUCCESS) + goto done; + + if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) { + ret = VBE_NOTSUP; + goto done; + } + bcopy(rvbe, vbep, sizeof(*vbep)); +done: + bio_free(rvbe, sizeof(*rvbe)); + return (ret); +} + +/* Function 01h - Return VBE Mode Information */ +static int +biosvbe_get_mode_info(int mode, struct modeinfoblock *mi) +{ + struct modeinfoblock *rmi; + int ret; + + rmi = bio_alloc(sizeof(*rmi)); + if (rmi == NULL) + return (VBE_FAILED); + + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f01; + v86.ecx = mode; + v86.es = VTOPSEG(rmi); + v86.edi = VTOPOFF(rmi); + v86int(); + + ret = v86.eax & 0xffff; + if (ret != VBE_SUCCESS) + goto done; + bcopy(rmi, mi, sizeof(*rmi)); +done: + bio_free(rmi, sizeof(*rmi)); + return (ret); +} + +/* Function 02h - Set VBE Mode */ +static int +biosvbe_set_mode(int mode, struct crtciinfoblock *ci) +{ + int rv; + + if (vbe->Capabilities & VBE_CAP_DAC8) { + int m; + + /* + * The mode change should reset the palette format to + * 6 bits, but apparently some systems do fail with 8-bit + * palette, so we switch to 6-bit here. + */ + m = 0x0600; + if (biosvbe_palette_format(&m) == VBE_SUCCESS) + palette_format = m; + } + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f02; + v86.ebx = mode | 0x4000; /* set linear FB bit */ + v86.es = VTOPSEG(ci); + v86.edi = VTOPOFF(ci); + v86int(); + rv = v86.eax & 0xffff; + if (vbe->Capabilities & VBE_CAP_DAC8) { + int m; + + /* Switch to 8-bits per primary color. */ + m = 0x0800; + if (biosvbe_palette_format(&m) == VBE_SUCCESS) + palette_format = m; + } + env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL); + return (rv); +} + +/* Function 03h - Get VBE Mode */ +static int +biosvbe_get_mode(int *mode) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f03; + v86int(); + *mode = v86.ebx & 0x3fff; /* Bits 0-13 */ + return (v86.eax & 0xffff); +} + +/* Function 08h - Set/Get DAC Palette Format */ +int +biosvbe_palette_format(int *format) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f08; + v86.ebx = *format; + v86int(); + *format = (v86.ebx >> 8) & 0xff; + return (v86.eax & 0xffff); +} + +/* Function 09h - Set/Get Palette Data */ +static int +biosvbe_palette_data(int mode, int reg, struct paletteentry *pe) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f09; + v86.ebx = mode; + v86.edx = reg; + v86.ecx = 1; + v86.es = VTOPSEG(pe); + v86.edi = VTOPOFF(pe); + v86int(); + return (v86.eax & 0xffff); +} + +/* + * Function 15h BL=00h - Report VBE/DDC Capabilities + * + * int biosvbe_ddc_caps(void) + * return: VBE/DDC capabilities + */ +static int +biosvbe_ddc_caps(void) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f15; /* display identification extensions */ + v86.ebx = 0; /* report DDC capabilities */ + v86.ecx = 0; /* controller unit number (00h = primary) */ + v86.es = 0; + v86.edi = 0; + v86int(); + if (VBE_ERROR(v86.eax & 0xffff)) + return (0); + return (v86.ebx & 0xffff); +} + +/* Function 11h BL=01h - Flat Panel status */ +static int +biosvbe_ddc_read_flat_panel_info(void *buf) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f11; /* Flat Panel Interface extensions */ + v86.ebx = 1; /* Return Flat Panel Information */ + v86.es = VTOPSEG(buf); + v86.edi = VTOPOFF(buf); + v86int(); + return (v86.eax & 0xffff); +} + +/* Function 15h BL=01h - Read EDID */ +static int +biosvbe_ddc_read_edid(int blockno, void *buf) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0x4f15; /* display identification extensions */ + v86.ebx = 1; /* read EDID */ + v86.ecx = 0; /* controller unit number (00h = primary) */ + v86.edx = blockno; + v86.es = VTOPSEG(buf); + v86.edi = VTOPOFF(buf); + v86int(); + return (v86.eax & 0xffff); +} + +static int +vbe_mode_is_supported(struct modeinfoblock *mi) +{ + if ((mi->ModeAttributes & 0x01) == 0) + return (0); /* mode not supported by hardware */ + if ((mi->ModeAttributes & 0x08) == 0) + return (0); /* linear fb not available */ + if ((mi->ModeAttributes & 0x10) == 0) + return (0); /* text mode */ + if (mi->NumberOfPlanes != 1) + return (0); /* planar mode not supported */ + if (mi->MemoryModel != 0x04 /* Packed pixel */ && + mi->MemoryModel != 0x06 /* Direct Color */) + return (0); /* unsupported pixel format */ + return (1); +} + +static bool +vbe_check(void) +{ + + if (vbe == NULL) { + printf("VBE not available\n"); + return (false); + } + return (true); +} + +static int +mode_set(struct env_var *ev, int flags __unused, const void *value) +{ + int mode; + + if (strcmp(ev->ev_name, "hw.vga.textmode") == 0) { + unsigned long v; + char *end; + + if (value == NULL) + return (0); + /* VT(4) describes hw.vga.textmode values 0 or 1. */ + errno = 0; + v = strtoul(value, &end, 0); + if (errno != 0 || *(char *)value == '\0' || *end != '\0' || + (v != 0 && v != 1)) + return (EINVAL); + env_setenv("hw.vga.textmode", EV_VOLATILE | EV_NOHOOK, + value, NULL, NULL); + if (v == 1) { + reset_font_flags(); + bios_text_font(true); + bios_set_text_mode(VGA_TEXT_MODE); + (void) cons_update_mode(false); + return (0); + } + } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) { + env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK, + value, NULL, NULL); + } else { + return (EINVAL); + } + + mode = vbe_default_mode(); + if (gfx_state.tg_mode != mode) { + reset_font_flags(); + bios_text_font(false); + vbe_set_mode(mode); + cons_update_mode(true); + } + return (0); +} + +void +vbe_init(void) +{ + /* First set FB for text mode. */ + gfx_state.tg_fb_type = FB_TEXT; + gfx_state.tg_fb.fb_height = TEXT_ROWS; + gfx_state.tg_fb.fb_width = TEXT_COLS; + gfx_state.tg_ctype = CT_INDEXED; + gfx_state.tg_mode = 3; + + if (vbe == NULL) + vbe = malloc(sizeof(*vbe)); + + if (vbe_mode == NULL) { + vbe_mode = malloc(sizeof(*vbe_mode)); + if (vbe_mode == NULL) { + free(vbe); + vbe = NULL; + } + } + + if (biosvbe_info(vbe) != VBE_SUCCESS) { + free(vbe); + vbe = NULL; + free(vbe_mode); + vbe_mode = NULL; + } + + env_setenv("hw.vga.textmode", EV_VOLATILE, "1", mode_set, + env_nounset); + env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set, + env_nounset); + /* vbe_set_mode() will set up the rest. */ +} + +bool +vbe_available(void) +{ + return (gfx_state.tg_fb_type == FB_VBE); +} + +int +vbe_set_palette(const struct paletteentry *entry, size_t slot) +{ + struct paletteentry pe; + int mode, ret; + + if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0) + return (1); + + if (gfx_state.tg_ctype != CT_INDEXED) { + return (1); + } + + pe.Blue = entry->Blue; + pe.Green = entry->Green; + pe.Red = entry->Red; + pe.Reserved = entry->Reserved; + + if (vbe->Capabilities & VBE_CAP_SNOW) + mode = 0x80; + else + mode = 0; + + ret = biosvbe_palette_data(mode, slot, &pe); + + return (ret == VBE_SUCCESS ? 0 : 1); +} + +int +vbe_get_mode(void) +{ + return (gfx_state.tg_mode); +} + +int +vbe_set_mode(int modenum) +{ + struct modeinfoblock mi; + int bpp, ret; + + if (!vbe_check()) + return (1); + + ret = biosvbe_get_mode_info(modenum, &mi); + if (VBE_ERROR(ret)) { + printf("mode 0x%x invalid\n", modenum); + return (1); + } + + if (!vbe_mode_is_supported(&mi)) { + printf("mode 0x%x not supported\n", modenum); + return (1); + } + + /* calculate bytes per pixel */ + switch (mi.BitsPerPixel) { + case 32: + case 24: + case 16: + case 15: + case 8: + break; + default: + printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel); + return (1); + } + + ret = biosvbe_set_mode(modenum, NULL); + if (VBE_ERROR(ret)) { + printf("mode 0x%x could not be set\n", modenum); + return (1); + } + + gfx_state.tg_mode = modenum; + gfx_state.tg_fb_type = FB_VBE; + /* make sure we have current MI in vbestate */ + memcpy(vbe_mode, &mi, sizeof (*vbe_mode)); + + gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff; + gfx_state.tg_fb.fb_height = mi.YResolution; + gfx_state.tg_fb.fb_width = mi.XResolution; + gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel; + + /* Bytes per pixel */ + bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY; + + /* vbe_mode_is_supported() excludes the rest */ + switch (mi.MemoryModel) { + case 0x4: + gfx_state.tg_ctype = CT_INDEXED; + break; + case 0x6: + gfx_state.tg_ctype = CT_RGB; + break; + } + +#define COLOR_MASK(size, pos) (((1 << size) - 1) << pos) + if (gfx_state.tg_ctype == CT_INDEXED) { + gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16); + gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8); + gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0); + } else if (vbe->VbeVersion >= 0x300) { + gfx_state.tg_fb.fb_mask_red = + COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition); + gfx_state.tg_fb.fb_mask_green = + COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition); + gfx_state.tg_fb.fb_mask_blue = + COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition); + } else { + gfx_state.tg_fb.fb_mask_red = + COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition); + gfx_state.tg_fb.fb_mask_green = + COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition); + gfx_state.tg_fb.fb_mask_blue = + COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition); + } + gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red | + gfx_state.tg_fb.fb_mask_green | + gfx_state.tg_fb.fb_mask_blue); + + if (vbe->VbeVersion >= 0x300) + gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp; + else + gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp; + + gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride * + bpp; + + return (0); +} + +static void * +vbe_farptr(uint32_t farptr) +{ + return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)))); +} + +/* + * Verify existance of mode number or find mode by + * dimensions. If depth is not given, walk values 32, 24, 16, 8. + */ +static int +vbe_find_mode_xydm(int x, int y, int depth, int m) +{ + struct modeinfoblock mi; + uint32_t farptr; + uint16_t mode; + int safety, i; + + memset(vbe, 0, sizeof (*vbe)); + if (biosvbe_info(vbe) != VBE_SUCCESS) + return (0); + if (vbe->VideoModePtr == 0) + return (0); + + if (m != -1) + i = 8; + else if (depth == -1) + i = 32; + else + i = depth; + + while (i > 0) { + farptr = vbe->VideoModePtr; + safety = 0; + while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { + safety++; + farptr += 2; + if (safety == VESA_MODE_COUNT) + break; + if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { + continue; + } + /* we only care about linear modes here */ + if (vbe_mode_is_supported(&mi) == 0) + continue; + + if (m != -1) { + if (m == mode) + return (mode); + else + continue; + } + + if (mi.XResolution == x && + mi.YResolution == y && + mi.BitsPerPixel == i) + return (mode); + } + if (depth != -1) + break; + + i -= 8; + } + + return (0); +} + +static int +vbe_find_mode(char *str) +{ + int x, y, depth; + + if (!gfx_parse_mode_str(str, &x, &y, &depth)) + return (0); + + return (vbe_find_mode_xydm(x, y, depth, -1)); +} + +static void +vbe_dump_mode(int modenum, struct modeinfoblock *mi) +{ + printf("0x%x=%dx%dx%d", modenum, + mi->XResolution, mi->YResolution, mi->BitsPerPixel); +} + +static bool +vbe_get_edid(edid_res_list_t *res) +{ + struct vesa_edid_info *edid_info; + const uint8_t magic[] = EDID_MAGIC; + int ddc_caps; + bool ret = false; + + ddc_caps = biosvbe_ddc_caps(); + if (ddc_caps == 0) { + return (ret); + } + + edid_info = bio_alloc(sizeof (*edid_info)); + if (edid_info == NULL) + return (ret); + memset(edid_info, 0, sizeof (*edid_info)); + + if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info))) + goto done; + + if (memcmp(edid_info, magic, sizeof (magic)) != 0) + goto done; + + /* Unknown EDID version. */ + if (edid_info->header.version != 1) + goto done; + + ret = gfx_get_edid_resolution(edid_info, res); +done: + bio_free(edid_info, sizeof (*edid_info)); + return (ret); +} + +static bool +vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight) +{ + struct vesa_flat_panel_info *fp_info; + bool ret = false; + + fp_info = bio_alloc(sizeof (*fp_info)); + if (fp_info == NULL) + return (ret); + memset(fp_info, 0, sizeof (*fp_info)); + + if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info))) + goto done; + + *pwidth = fp_info->HSize; + *pheight = fp_info->VSize; + ret = true; + +done: + bio_free(fp_info, sizeof (*fp_info)); + return (ret); +} + +static void +vbe_print_memory(unsigned vmem) +{ + char unit = 'K'; + + vmem /= 1024; + if (vmem >= 10240000) { + vmem /= 1048576; + unit = 'G'; + } else if (vmem >= 10000) { + vmem /= 1024; + unit = 'M'; + } + printf("Total memory: %u%cB\n", vmem, unit); +} + +static void +vbe_print_vbe_info(struct vbeinfoblock *vbep) +{ + char *oemstring = ""; + char *oemvendor = "", *oemproductname = "", *oemproductrev = ""; + + if (vbep->OemStringPtr != 0) + oemstring = vbe_farptr(vbep->OemStringPtr); + + if (vbep->OemVendorNamePtr != 0) + oemvendor = vbe_farptr(vbep->OemVendorNamePtr); + + if (vbep->OemProductNamePtr != 0) + oemproductname = vbe_farptr(vbep->OemProductNamePtr); + + if (vbep->OemProductRevPtr != 0) + oemproductrev = vbe_farptr(vbep->OemProductRevPtr); + + printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8, + vbep->VbeVersion & 0xF, oemstring); + + if (vbep->OemSoftwareRev != 0) { + printf("OEM Version %d.%d, %s (%s, %s)\n", + vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF, + oemvendor, oemproductname, oemproductrev); + } + vbe_print_memory(vbep->TotalMemory << 16); + printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages); +} + +/* List available modes, filter by depth. If depth is -1, list all. */ +void +vbe_modelist(int depth) +{ + struct modeinfoblock mi; + uint32_t farptr; + uint16_t mode; + int nmodes = 0, safety = 0; + int ddc_caps; + uint32_t width, height; + bool edid = false; + edid_res_list_t res; + struct resolution *rp; + + if (!vbe_check()) + return; + + ddc_caps = biosvbe_ddc_caps(); + if (ddc_caps & 3) { + printf("DDC"); + if (ddc_caps & 1) + printf(" [DDC1]"); + if (ddc_caps & 2) + printf(" [DDC2]"); + + TAILQ_INIT(&res); + edid = vbe_get_edid(&res); + if (edid) { + printf(": EDID"); + while ((rp = TAILQ_FIRST(&res)) != NULL) { + printf(" %dx%d", rp->width, rp->height); + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + printf("\n"); + } else { + printf(": no EDID information\n"); + } + } + if (!edid) + if (vbe_get_flatpanel(&width, &height)) + printf(": Panel %dx%d\n", width, height); + + memset(vbe, 0, sizeof (*vbe)); + memcpy(vbe->VbeSignature, "VBE2", 4); + if (biosvbe_info(vbe) != VBE_SUCCESS) + goto done; + if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) + goto done; + + vbe_print_vbe_info(vbe); + printf("Modes: "); + + farptr = vbe->VideoModePtr; + if (farptr == 0) + goto done; + + while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { + safety++; + farptr += 2; + if (safety == VESA_MODE_COUNT) { + printf("[?] "); + break; + } + if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) + continue; + /* we only care about linear modes here */ + if (vbe_mode_is_supported(&mi) == 0) + continue; + + /* we found some mode so reset safety counter */ + safety = 0; + + /* apply requested filter */ + if (depth != -1 && mi.BitsPerPixel != depth) + continue; + + if (nmodes % 4 == 0) + printf("\n"); + else + printf(" "); + + vbe_dump_mode(mode, &mi); + nmodes++; + } + +done: + if (nmodes == 0) + printf("none found"); + printf("\n"); +} + +static void +vbe_print_mode(bool verbose __unused) +{ + int nc, mode, i, rc; + + nc = NCOLORS; + + memset(vbe, 0, sizeof (*vbe)); + if (biosvbe_info(vbe) != VBE_SUCCESS) + return; + + vbe_print_vbe_info(vbe); + + if (biosvbe_get_mode(&mode) != VBE_SUCCESS) { + printf("Error getting current VBE mode\n"); + return; + } + + if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS || + vbe_mode_is_supported(vbe_mode) == 0) { + printf("VBE mode (0x%x) is not framebuffer mode\n", mode); + return; + } + + printf("\nCurrent VBE mode: "); + vbe_dump_mode(mode, vbe_mode); + printf("\n"); + + printf("%ux%ux%u, stride=%u\n", + gfx_state.tg_fb.fb_width, + gfx_state.tg_fb.fb_height, + gfx_state.tg_fb.fb_bpp, + gfx_state.tg_fb.fb_stride * + (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY)); + printf(" frame buffer: address=%jx, size=%jx\n", + (uintmax_t)gfx_state.tg_fb.fb_addr, + (uintmax_t)gfx_state.tg_fb.fb_size); + + if (vbe_mode->MemoryModel == 0x6) { + printf(" color mask: R=%08x, G=%08x, B=%08x\n", + gfx_state.tg_fb.fb_mask_red, + gfx_state.tg_fb.fb_mask_green, + gfx_state.tg_fb.fb_mask_blue); + pager_open(); + for (i = 0; i < nc; i++) { + printf("%d: R=%02x, G=%02x, B=%02x %08x", i, + (cmap[i] & gfx_state.tg_fb.fb_mask_red) >> + ffs(gfx_state.tg_fb.fb_mask_red) - 1, + (cmap[i] & gfx_state.tg_fb.fb_mask_green) >> + ffs(gfx_state.tg_fb.fb_mask_green) - 1, + (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >> + ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]); + if (pager_output("\n") != 0) + break; + } + pager_close(); + return; + } + + mode = 1; /* get DAC palette width */ + rc = biosvbe_palette_format(&mode); + if (rc != VBE_SUCCESS) + return; + + printf(" palette format: %x bits per primary\n", mode); + if (pe8 == NULL) + return; + + pager_open(); + for (i = 0; i < nc; i++) { + printf("%d: R=%02x, G=%02x, B=%02x", i, + pe8[i].Red, pe8[i].Green, pe8[i].Blue); + if (pager_output("\n") != 0) + break; + } + pager_close(); +} + +/* + * Try EDID preferred mode, if EDID or the suggested mode is not available, + * then try flat panel information. + * Fall back to VBE_DEFAULT_MODE. + */ +int +vbe_default_mode(void) +{ + edid_res_list_t res; + struct resolution *rp; + int modenum; + uint32_t width, height; + + modenum = 0; + vbe_get_max_resolution(&width, &height); + if (width != 0 && height != 0) + modenum = vbe_find_mode_xydm(width, height, -1, -1); + + TAILQ_INIT(&res); + if (vbe_get_edid(&res)) { + while ((rp = TAILQ_FIRST(&res)) != NULL) { + if (modenum == 0) { + modenum = vbe_find_mode_xydm( + rp->width, rp->height, -1, -1); + } + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + } + + if (modenum == 0 && + vbe_get_flatpanel(&width, &height)) { + modenum = vbe_find_mode_xydm(width, height, -1, -1); + } + + /* Still no mode? Fall back to default. */ + if (modenum == 0) + modenum = vbe_find_mode(VBE_DEFAULT_MODE); + return (modenum); +} + +COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa); + +int +command_vesa(int argc, char *argv[]) +{ + char *arg, *cp; + int modenum = -1, n; + + if (!vbe_check()) + return (CMD_OK); + + if (argc < 2) + goto usage; + + if (strcmp(argv[1], "list") == 0) { + n = -1; + if (argc != 2 && argc != 3) + goto usage; + + if (argc == 3) { + arg = argv[2]; + errno = 0; + n = strtoul(arg, &cp, 0); + if (errno != 0 || *arg == '\0' || cp[0] != '\0') { + snprintf(command_errbuf, + sizeof (command_errbuf), + "depth should be an integer"); + return (CMD_ERROR); + } + } + vbe_modelist(n); + return (CMD_OK); + } + + if (strcmp(argv[1], "get") == 0) { + bool verbose = false; + + if (argc != 2) { + if (argc > 3 || strcmp(argv[2], "-v") != 0) + goto usage; + verbose = true; + } + vbe_print_mode(verbose); + return (CMD_OK); + } + + if (strcmp(argv[1], "off") == 0) { + if (argc != 2) + goto usage; + + if (gfx_state.tg_mode == VGA_TEXT_MODE) + return (CMD_OK); + + reset_font_flags(); + bios_text_font(true); + bios_set_text_mode(VGA_TEXT_MODE); + cons_update_mode(false); + return (CMD_OK); + } + + if (strcmp(argv[1], "on") == 0) { + if (argc != 2) + goto usage; + + modenum = vbe_default_mode(); + if (modenum == 0) { + snprintf(command_errbuf, sizeof (command_errbuf), + "%s: no suitable VBE mode number found", argv[0]); + return (CMD_ERROR); + } + } else if (strcmp(argv[1], "set") == 0) { + if (argc != 3) + goto usage; + + if (strncmp(argv[2], "0x", 2) == 0) { + arg = argv[2]; + errno = 0; + n = strtoul(arg, &cp, 0); + if (errno != 0 || *arg == '\0' || cp[0] != '\0') { + snprintf(command_errbuf, + sizeof (command_errbuf), + "mode should be an integer"); + return (CMD_ERROR); + } + modenum = vbe_find_mode_xydm(0, 0, 0, n); + } else if (strchr(argv[2], 'x') != NULL) { + modenum = vbe_find_mode(argv[2]); + } + } else { + goto usage; + } + + if (modenum == 0) { + snprintf(command_errbuf, sizeof (command_errbuf), + "%s: mode %s not supported by firmware\n", + argv[0], argv[2]); + return (CMD_ERROR); + } + + if (modenum >= VESA_MODE_BASE) { + if (gfx_state.tg_mode != modenum) { + reset_font_flags(); + bios_text_font(false); + vbe_set_mode(modenum); + cons_update_mode(true); + } + return (CMD_OK); + } else { + snprintf(command_errbuf, sizeof (command_errbuf), + "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]); + return (CMD_ERROR); + } + +usage: + snprintf(command_errbuf, sizeof (command_errbuf), + "usage: %s on | off | get | list [depth] | " + "set <display or VBE mode number>", argv[0]); + return (CMD_ERROR); +} diff --git a/stand/i386/libi386/vbe.h b/stand/i386/libi386/vbe.h new file mode 100644 index 000000000000..ff28b960df9c --- /dev/null +++ b/stand/i386/libi386/vbe.h @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Default mode for VESA frame buffer. + * This mode is selected when there is no EDID inormation and + * mode is not provided by user. + * To provide consistent look with UEFI GOP, we use 800x600 here, + * and if this mode is not available, we fall back to text mode and + * VESA disabled. + */ + +#define VBE_DEFAULT_MODE "800x600" + +struct vbeinfoblock { + char VbeSignature[4]; + uint16_t VbeVersion; + uint32_t OemStringPtr; + uint32_t Capabilities; +#define VBE_CAP_DAC8 (1 << 0) /* Can switch DAC */ +#define VBE_CAP_NONVGA (1 << 1) /* Controller is not VGA comp. */ +#define VBE_CAP_SNOW (1 << 2) /* Set data during Vertical Reterace */ + uint32_t VideoModePtr; + uint16_t TotalMemory; + uint16_t OemSoftwareRev; + uint32_t OemVendorNamePtr, OemProductNamePtr, OemProductRevPtr; + /* data area, in total max 512 bytes for VBE 2.0 */ + uint8_t Reserved[222]; + uint8_t OemData[256]; +} __packed; + +struct modeinfoblock { + /* Mandatory information for all VBE revisions */ + uint16_t ModeAttributes; + uint8_t WinAAttributes, WinBAttributes; + uint16_t WinGranularity, WinSize, WinASegment, WinBSegment; + uint32_t WinFuncPtr; + uint16_t BytesPerScanLine; + /* Mandatory information for VBE 1.2 and above */ + uint16_t XResolution, YResolution; + uint8_t XCharSize, YCharSize, NumberOfPlanes, BitsPerPixel; + uint8_t NumberOfBanks, MemoryModel, BankSize, NumberOfImagePages; + uint8_t Reserved1; + /* Direct Color fields + (required for direct/6 and YUV/7 memory models) */ + uint8_t RedMaskSize, RedFieldPosition; + uint8_t GreenMaskSize, GreenFieldPosition; + uint8_t BlueMaskSize, BlueFieldPosition; + uint8_t RsvdMaskSize, RsvdFieldPosition; + uint8_t DirectColorModeInfo; + /* Mandatory information for VBE 2.0 and above */ + uint32_t PhysBasePtr; + uint32_t OffScreenMemOffset; /* reserved in VBE 3.0 and above */ + uint16_t OffScreenMemSize; /* reserved in VBE 3.0 and above */ + + /* Mandatory information for VBE 3.0 and above */ + uint16_t LinBytesPerScanLine; + uint8_t BnkNumberOfImagePages; + uint8_t LinNumberOfImagePages; + uint8_t LinRedMaskSize, LinRedFieldPosition; + uint8_t LinGreenMaskSize, LinGreenFieldPosition; + uint8_t LinBlueMaskSize, LinBlueFieldPosition; + uint8_t LinRsvdMaskSize, LinRsvdFieldPosition; + uint32_t MaxPixelClock; + /* + 1 will fix the size to 256 bytes */ + uint8_t Reserved4[189 + 1]; +} __packed; + +struct crtciinfoblock { + uint16_t HorizontalTotal; + uint16_t HorizontalSyncStart; + uint16_t HorizontalSyncEnd; + uint16_t VerticalTotal; + uint16_t VerticalSyncStart; + uint16_t VerticalSyncEnd; + uint8_t Flags; + uint32_t PixelClock; + uint16_t RefreshRate; + uint8_t Reserved[40]; +} __packed; + +struct paletteentry { + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Reserved; +} __packed; + +struct flatpanelinfo +{ + uint16_t HorizontalSize; + uint16_t VerticalSize; + uint16_t PanelType; + uint8_t RedBPP; + uint8_t GreenBPP; + uint8_t BlueBPP; + uint8_t ReservedBPP; + uint32_t ReservedOffScreenMemSize; + uint32_t ReservedOffScreenMemPtr; + + uint8_t Reserved[14]; +} __packed; + +#define VBE_BASE_MODE (0x100) /* VBE 3.0 page 18 */ +#define VBE_VALID_MODE(a) ((a) >= VBE_BASE_MODE) +#define VBE_ERROR(a) (((a) & 0xFF) != 0x4F || ((a) & 0xFF00) != 0) +#define VBE_SUCCESS (0x004F) +#define VBE_FAILED (0x014F) +#define VBE_NOTSUP (0x024F) +#define VBE_INVALID (0x034F) + +#define VGA_TEXT_MODE (3) /* 80x25 text mode */ +#define TEXT_ROWS (25) /* VGATEXT rows */ +#define TEXT_COLS (80) /* VGATEXT columns */ + +extern struct paletteentry *pe8; +extern int palette_format; + +int vga_get_reg(int, int); +int vga_get_atr(int, int); +void vga_set_atr(int, int, int); +void vga_set_indexed(int, int, int, uint8_t, uint8_t); +int vga_get_indexed(int, int, int, uint8_t); +int vga_get_crtc(int, int); +void vga_set_crtc(int, int, int); +int vga_get_seq(int, int); +void vga_set_seq(int, int, int); +int vga_get_grc(int, int); +void vga_set_grc(int, int, int); + +/* high-level VBE helpers, from vbe.c */ +void bios_set_text_mode(int); +int biosvbe_palette_format(int *); +void vbe_init(void); +bool vbe_available(void); +int vbe_default_mode(void); +int vbe_set_mode(int); +int vbe_get_mode(void); +int vbe_set_palette(const struct paletteentry *, size_t); +void vbe_modelist(int); diff --git a/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c index f965979d6565..b4829db1ea42 100644 --- a/stand/i386/libi386/vidconsole.c +++ b/stand/i386/libi386/vidconsole.c @@ -1,921 +1,1230 @@ /*- * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) * 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 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. * * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp */ #include <sys/cdefs.h> __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> #include "libi386.h" #if KEYBOARD_PROBE static int probe_keyboard(void); #endif static void vidc_probe(struct console *cp); static int vidc_init(int arg); static void vidc_putchar(int c); static int vidc_getchar(void); static int vidc_ischar(void); +static void cons_draw_frame(teken_attr_t *); static int vidc_started; static uint16_t *vgatext; static tf_bell_t vidc_cons_bell; static tf_cursor_t vidc_text_cursor; static tf_putchar_t vidc_text_putchar; static tf_fill_t vidc_text_fill; static tf_copy_t vidc_text_copy; static tf_param_t vidc_text_param; static tf_respond_t vidc_cons_respond; static teken_funcs_t tf = { .tf_bell = vidc_cons_bell, .tf_cursor = vidc_text_cursor, .tf_putchar = vidc_text_putchar, .tf_fill = vidc_text_fill, .tf_copy = vidc_text_copy, .tf_param = vidc_text_param, .tf_respond = vidc_cons_respond, }; -teken_t teken; -teken_pos_t tp; - -struct text_pixel { - teken_char_t c; - teken_attr_t a; -}; - -static struct text_pixel *buffer; - -#define NCOLORS 16 - -/* - * Between console's palette and VGA's one: - * - blue and red are swapped (1 <-> 4) - * - yellow and cyan are swapped (3 <-> 6) - */ -static const int cons_to_vga_colors[NCOLORS] = { - 0, 4, 2, 6, 1, 5, 3, 7, - 8, 12, 10, 14, 9, 13, 11, 15 +static teken_funcs_t tfx = { + .tf_bell = vidc_cons_bell, + .tf_cursor = gfx_fb_cursor, + .tf_putchar = gfx_fb_putchar, + .tf_fill = gfx_fb_fill, + .tf_copy = gfx_fb_copy, + .tf_param = gfx_fb_param, + .tf_respond = vidc_cons_respond, }; -#define TEXT_COLS 80 -#define TEXT_ROWS 25 #define KEYBUFSZ 10 static uint8_t keybuf[KEYBUFSZ]; /* keybuf for extended codes */ struct console vidconsole = { .c_name = "vidconsole", .c_desc = "internal video/keyboard", .c_flags = 0, .c_probe = vidc_probe, .c_init = vidc_init, .c_out = vidc_putchar, .c_in = vidc_getchar, .c_ready = vidc_ischar }; -static int -vga_get_reg(int reg, int index) -{ - return (inb(reg + index)); -} - -static int -vga_get_atr(int reg, int i) -{ - int ret; - - (void) inb(reg + VGA_GEN_INPUT_STAT_1); - outb(reg + VGA_AC_WRITE, i); - ret = inb(reg + VGA_AC_READ); - - (void) inb(reg + VGA_GEN_INPUT_STAT_1); - - return (ret); -} - -static void -vga_set_atr(int reg, int i, int v) -{ - (void) inb(reg + VGA_GEN_INPUT_STAT_1); - outb(reg + VGA_AC_WRITE, i); - outb(reg + VGA_AC_WRITE, v); - - (void) inb(reg + VGA_GEN_INPUT_STAT_1); -} - -static void -vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val) -{ - outb(reg + indexreg, index); - outb(reg + datareg, val); -} - -static int -vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index) -{ - outb(reg + indexreg, index); - return (inb(reg + datareg)); -} - -static int -vga_get_crtc(int reg, int i) -{ - return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i)); -} - -static void -vga_set_crtc(int reg, int i, int v) +/* + * This function is used to mark a rectangular image area so the scrolling + * will know we need to copy the data from there. + */ +void +term_image_display(teken_gfx_t *state, const teken_rect_t *r) { - vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v); + teken_pos_t p; + int idx; + + for (p.tp_row = r->tr_begin.tp_row; + p.tp_row < r->tr_end.tp_row; p.tp_row++) { + for (p.tp_col = r->tr_begin.tp_col; + p.tp_col < r->tr_end.tp_col; p.tp_col++) { + idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; + if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) + return; + screen_buffer[idx].a.ta_format |= TF_IMAGE; + } + } } static void vidc_text_set_cursor(teken_unit_t row, teken_unit_t col, bool visible) { uint16_t addr; uint8_t msl, s, e; msl = vga_get_crtc(VGA_REG_BASE, VGA_CRTC_MAX_SCAN_LINE) & 0x1f; s = vga_get_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_START) & 0xC0; e = vga_get_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_END); if (visible == true) { addr = row * TEXT_COLS + col; vga_set_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_LOC_HIGH, addr >> 8); vga_set_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_LOC_LOW, addr & 0xff); e = msl; } else { s |= (1<<5); } vga_set_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_START, s); vga_set_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_END, e); } static void vidc_text_get_cursor(teken_unit_t *row, teken_unit_t *col) { uint16_t addr; addr = (vga_get_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_LOC_HIGH) << 8) + vga_get_crtc(VGA_REG_BASE, VGA_CRTC_CURSOR_LOC_LOW); *row = addr / TEXT_COLS; *col = addr % TEXT_COLS; } /* * Not implemented. */ static void vidc_cons_bell(void *s __unused) { } static void vidc_text_cursor(void *s __unused, const teken_pos_t *p) { teken_unit_t row, col; if (p->tp_col == TEXT_COLS) col = p->tp_col - 1; else col = p->tp_col; if (p->tp_row == TEXT_ROWS) row = p->tp_row - 1; else row = p->tp_row; vidc_text_set_cursor(row, col, true); } /* * Binary searchable table for Unicode to CP437 conversion. */ struct unicp437 { uint16_t unicode_base; uint8_t cp437_base; uint8_t length; }; static const struct unicp437 cp437table[] = { { 0x0020, 0x20, 0x5e }, { 0x00a0, 0x20, 0x00 }, { 0x00a1, 0xad, 0x00 }, { 0x00a2, 0x9b, 0x00 }, { 0x00a3, 0x9c, 0x00 }, { 0x00a5, 0x9d, 0x00 }, { 0x00a7, 0x15, 0x00 }, { 0x00aa, 0xa6, 0x00 }, { 0x00ab, 0xae, 0x00 }, { 0x00ac, 0xaa, 0x00 }, { 0x00b0, 0xf8, 0x00 }, { 0x00b1, 0xf1, 0x00 }, { 0x00b2, 0xfd, 0x00 }, { 0x00b5, 0xe6, 0x00 }, { 0x00b6, 0x14, 0x00 }, { 0x00b7, 0xfa, 0x00 }, { 0x00ba, 0xa7, 0x00 }, { 0x00bb, 0xaf, 0x00 }, { 0x00bc, 0xac, 0x00 }, { 0x00bd, 0xab, 0x00 }, { 0x00bf, 0xa8, 0x00 }, { 0x00c4, 0x8e, 0x01 }, { 0x00c6, 0x92, 0x00 }, { 0x00c7, 0x80, 0x00 }, { 0x00c9, 0x90, 0x00 }, { 0x00d1, 0xa5, 0x00 }, { 0x00d6, 0x99, 0x00 }, { 0x00dc, 0x9a, 0x00 }, { 0x00df, 0xe1, 0x00 }, { 0x00e0, 0x85, 0x00 }, { 0x00e1, 0xa0, 0x00 }, { 0x00e2, 0x83, 0x00 }, { 0x00e4, 0x84, 0x00 }, { 0x00e5, 0x86, 0x00 }, { 0x00e6, 0x91, 0x00 }, { 0x00e7, 0x87, 0x00 }, { 0x00e8, 0x8a, 0x00 }, { 0x00e9, 0x82, 0x00 }, { 0x00ea, 0x88, 0x01 }, { 0x00ec, 0x8d, 0x00 }, { 0x00ed, 0xa1, 0x00 }, { 0x00ee, 0x8c, 0x00 }, { 0x00ef, 0x8b, 0x00 }, { 0x00f0, 0xeb, 0x00 }, { 0x00f1, 0xa4, 0x00 }, { 0x00f2, 0x95, 0x00 }, { 0x00f3, 0xa2, 0x00 }, { 0x00f4, 0x93, 0x00 }, { 0x00f6, 0x94, 0x00 }, { 0x00f7, 0xf6, 0x00 }, { 0x00f8, 0xed, 0x00 }, { 0x00f9, 0x97, 0x00 }, { 0x00fa, 0xa3, 0x00 }, { 0x00fb, 0x96, 0x00 }, { 0x00fc, 0x81, 0x00 }, { 0x00ff, 0x98, 0x00 }, { 0x0192, 0x9f, 0x00 }, { 0x0393, 0xe2, 0x00 }, { 0x0398, 0xe9, 0x00 }, { 0x03a3, 0xe4, 0x00 }, { 0x03a6, 0xe8, 0x00 }, { 0x03a9, 0xea, 0x00 }, { 0x03b1, 0xe0, 0x01 }, { 0x03b4, 0xeb, 0x00 }, { 0x03b5, 0xee, 0x00 }, { 0x03bc, 0xe6, 0x00 }, { 0x03c0, 0xe3, 0x00 }, { 0x03c3, 0xe5, 0x00 }, { 0x03c4, 0xe7, 0x00 }, { 0x03c6, 0xed, 0x00 }, { 0x03d5, 0xed, 0x00 }, { 0x2010, 0x2d, 0x00 }, { 0x2014, 0x2d, 0x00 }, { 0x2018, 0x60, 0x00 }, { 0x2019, 0x27, 0x00 }, { 0x201c, 0x22, 0x00 }, { 0x201d, 0x22, 0x00 }, { 0x2022, 0x07, 0x00 }, { 0x203c, 0x13, 0x00 }, { 0x207f, 0xfc, 0x00 }, { 0x20a7, 0x9e, 0x00 }, { 0x20ac, 0xee, 0x00 }, { 0x2126, 0xea, 0x00 }, { 0x2190, 0x1b, 0x00 }, { 0x2191, 0x18, 0x00 }, { 0x2192, 0x1a, 0x00 }, { 0x2193, 0x19, 0x00 }, { 0x2194, 0x1d, 0x00 }, { 0x2195, 0x12, 0x00 }, { 0x21a8, 0x17, 0x00 }, { 0x2202, 0xeb, 0x00 }, { 0x2208, 0xee, 0x00 }, { 0x2211, 0xe4, 0x00 }, { 0x2212, 0x2d, 0x00 }, { 0x2219, 0xf9, 0x00 }, { 0x221a, 0xfb, 0x00 }, { 0x221e, 0xec, 0x00 }, { 0x221f, 0x1c, 0x00 }, { 0x2229, 0xef, 0x00 }, { 0x2248, 0xf7, 0x00 }, { 0x2261, 0xf0, 0x00 }, { 0x2264, 0xf3, 0x00 }, { 0x2265, 0xf2, 0x00 }, { 0x2302, 0x7f, 0x00 }, { 0x2310, 0xa9, 0x00 }, { 0x2320, 0xf4, 0x00 }, { 0x2321, 0xf5, 0x00 }, { 0x2500, 0xc4, 0x00 }, { 0x2502, 0xb3, 0x00 }, { 0x250c, 0xda, 0x00 }, { 0x2510, 0xbf, 0x00 }, { 0x2514, 0xc0, 0x00 }, { 0x2518, 0xd9, 0x00 }, { 0x251c, 0xc3, 0x00 }, { 0x2524, 0xb4, 0x00 }, { 0x252c, 0xc2, 0x00 }, { 0x2534, 0xc1, 0x00 }, { 0x253c, 0xc5, 0x00 }, { 0x2550, 0xcd, 0x00 }, { 0x2551, 0xba, 0x00 }, { 0x2552, 0xd5, 0x00 }, { 0x2553, 0xd6, 0x00 }, { 0x2554, 0xc9, 0x00 }, { 0x2555, 0xb8, 0x00 }, { 0x2556, 0xb7, 0x00 }, { 0x2557, 0xbb, 0x00 }, { 0x2558, 0xd4, 0x00 }, { 0x2559, 0xd3, 0x00 }, { 0x255a, 0xc8, 0x00 }, { 0x255b, 0xbe, 0x00 }, { 0x255c, 0xbd, 0x00 }, { 0x255d, 0xbc, 0x00 }, { 0x255e, 0xc6, 0x01 }, { 0x2560, 0xcc, 0x00 }, { 0x2561, 0xb5, 0x00 }, { 0x2562, 0xb6, 0x00 }, { 0x2563, 0xb9, 0x00 }, { 0x2564, 0xd1, 0x01 }, { 0x2566, 0xcb, 0x00 }, { 0x2567, 0xcf, 0x00 }, { 0x2568, 0xd0, 0x00 }, { 0x2569, 0xca, 0x00 }, { 0x256a, 0xd8, 0x00 }, { 0x256b, 0xd7, 0x00 }, { 0x256c, 0xce, 0x00 }, { 0x2580, 0xdf, 0x00 }, { 0x2584, 0xdc, 0x00 }, { 0x2588, 0xdb, 0x00 }, { 0x258c, 0xdd, 0x00 }, { 0x2590, 0xde, 0x00 }, { 0x2591, 0xb0, 0x02 }, { 0x25a0, 0xfe, 0x00 }, { 0x25ac, 0x16, 0x00 }, { 0x25b2, 0x1e, 0x00 }, { 0x25ba, 0x10, 0x00 }, { 0x25bc, 0x1f, 0x00 }, { 0x25c4, 0x11, 0x00 }, { 0x25cb, 0x09, 0x00 }, { 0x25d8, 0x08, 0x00 }, { 0x25d9, 0x0a, 0x00 }, { 0x263a, 0x01, 0x01 }, { 0x263c, 0x0f, 0x00 }, { 0x2640, 0x0c, 0x00 }, { 0x2642, 0x0b, 0x00 }, { 0x2660, 0x06, 0x00 }, { 0x2663, 0x05, 0x00 }, { 0x2665, 0x03, 0x01 }, { 0x266a, 0x0d, 0x00 }, { 0x266c, 0x0e, 0x00 } }; static uint8_t vga_get_cp437(teken_char_t c) { int min, mid, max; min = 0; max = (sizeof(cp437table) / sizeof(struct unicp437)) - 1; if (c < cp437table[0].unicode_base || c > cp437table[max].unicode_base + cp437table[max].length) return ('?'); while (max >= min) { mid = (min + max) / 2; if (c < cp437table[mid].unicode_base) max = mid - 1; else if (c > cp437table[mid].unicode_base + cp437table[mid].length) min = mid + 1; else return (c - cp437table[mid].unicode_base + cp437table[mid].cp437_base); } return ('?'); } 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; struct cgatext { uint8_t ch; uint8_t attr; } *addr; - px = buffer + p->tp_col + p->tp_row * tp.tp_col; + idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; + px = &screen_buffer[idx]; fg = teken_256to16(px->a.ta_fgcolor); bg = teken_256to16(px->a.ta_bgcolor); if (px->a.ta_format & TF_BOLD) fg |= TC_LIGHT; if (px->a.ta_format & TF_BLINK) bg |= TC_LIGHT; if (px->a.ta_format & TF_REVERSE) { tmp = fg; fg = bg; bg = tmp; } - attr = (cons_to_vga_colors[bg & 0xf] << 4) | - cons_to_vga_colors[fg & 0xf]; - addr = (struct cgatext *)vgatext + p->tp_col + p->tp_row * tp.tp_col; - addr->ch = vga_get_cp437(px->c); - addr->attr = attr; + attr = (cmap[bg & 0xf] << 4) | cmap[fg & 0xf]; + addr = (struct cgatext *)vgatext; + addr[idx].ch = vga_get_cp437(px->c); + addr[idx].attr = attr; } static void -vidc_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, +vidc_text_putchar(void *s, const teken_pos_t *p, teken_char_t c, const teken_attr_t *a) { - int attr, idx; + teken_gfx_t *state = s; + int attr, idx; - idx = p->tp_col + p->tp_row * tp.tp_col; - buffer[idx].c = c; - buffer[idx].a = *a; - vidc_text_printchar(p); + idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; + if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) + return; + + screen_buffer[idx].c = c; + screen_buffer[idx].a = *a; + + vidc_text_printchar(state, p); } static void -vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c, +vidc_text_fill(void *arg, const teken_rect_t *r, teken_char_t c, const teken_attr_t *a) { + teken_gfx_t *state = arg; teken_pos_t p; teken_unit_t row, col; vidc_text_get_cursor(&row, &col); vidc_text_set_cursor(row, col, 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++) - vidc_text_putchar(s, &p, c, a); + vidc_text_putchar(state, &p, c, a); vidc_text_set_cursor(row, col, true); } -static bool -vidc_same_pixel(struct text_pixel *px1, struct text_pixel *px2) -{ - if (px1->c != px2->c) - return (false); - - if (px1->a.ta_format != px2->a.ta_format) - return (false); - if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) - return (false); - if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) - return (false); - - return (true); -} - static void -vidc_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) +vidc_text_copy(void *ptr, const teken_rect_t *r, const teken_pos_t *p) { + teken_gfx_t *state = ptr; int srow, drow; int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ teken_pos_t d, s; teken_unit_t row, col; /* * 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; vidc_text_get_cursor(&row, &col); vidc_text_set_cursor(row, col, false); if (p->tp_row < r->tr_begin.tp_row) { /* Copy from bottom to top. */ for (y = 0; y < nrow; y++) { d.tp_row = p->tp_row + y; s.tp_row = r->tr_begin.tp_row + y; - drow = d.tp_row * tp.tp_col; - srow = s.tp_row * tp.tp_col; + drow = d.tp_row * state->tg_tp.tp_col; + srow = s.tp_row * state->tg_tp.tp_col; for (x = 0; x < ncol; x++) { d.tp_col = p->tp_col + x; s.tp_col = r->tr_begin.tp_col + x; - if (!vidc_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - vidc_text_printchar(&d); + if (!is_same_pixel( + &screen_buffer[d.tp_col + drow], + &screen_buffer[s.tp_col + srow])) { + screen_buffer[d.tp_col + drow] = + screen_buffer[s.tp_col + srow]; + vidc_text_printchar(state, &d); } } } } 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; + drow = d.tp_row * state->tg_tp.tp_col; + srow = s.tp_row * state->tg_tp.tp_col; for (x = 0; x < ncol; x++) { d.tp_col = p->tp_col + x; s.tp_col = r->tr_begin.tp_col + x; - if (!vidc_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - vidc_text_printchar(&d); + if (!is_same_pixel( + &screen_buffer[d.tp_col + drow], + &screen_buffer[s.tp_col + srow])) { + screen_buffer[d.tp_col + drow] = + screen_buffer[s.tp_col + srow]; + vidc_text_printchar(state, &d); } } } } 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; + drow = d.tp_row * state->tg_tp.tp_col; + srow = s.tp_row * state->tg_tp.tp_col; for (x = ncol - 1; x >= 0; x--) { d.tp_col = p->tp_col + x; s.tp_col = r->tr_begin.tp_col + x; - if (!vidc_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - vidc_text_printchar(&d); + if (!is_same_pixel( + &screen_buffer[d.tp_col + drow], + &screen_buffer[s.tp_col + srow])) { + screen_buffer[d.tp_col + drow] = + screen_buffer[s.tp_col + srow]; + vidc_text_printchar(state, &d); } } } } } vidc_text_set_cursor(row, col, true); } static void -vidc_text_param(void *s __unused, int cmd, unsigned int value) +vidc_text_param(void *arg, int cmd, unsigned int value) { + teken_gfx_t *state = arg; teken_unit_t row, col; switch (cmd) { 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: 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 */ break; } } /* * Not implemented. */ static void vidc_cons_respond(void *s __unused, const void *buf __unused, size_t len __unused) { } static void vidc_probe(struct console *cp) { /* look for a keyboard */ #if KEYBOARD_PROBE if (probe_keyboard()) #endif { cp->c_flags |= C_PRESENTIN; } /* XXX for now, always assume we can do BIOS screen output */ cp->c_flags |= C_PRESENTOUT; } static bool color_name_to_teken(const char *name, int *val) { if (strcasecmp(name, "black") == 0) { *val = TC_BLACK; return (true); } if (strcasecmp(name, "red") == 0) { *val = TC_RED; return (true); } if (strcasecmp(name, "green") == 0) { *val = TC_GREEN; return (true); } if (strcasecmp(name, "brown") == 0) { *val = TC_BROWN; return (true); } if (strcasecmp(name, "blue") == 0) { *val = TC_BLUE; return (true); } if (strcasecmp(name, "magenta") == 0) { *val = TC_MAGENTA; return (true); } if (strcasecmp(name, "cyan") == 0) { *val = TC_CYAN; return (true); } if (strcasecmp(name, "white") == 0) { *val = TC_WHITE; return (true); } return (false); } static int vidc_set_colors(struct env_var *ev, int flags, const void *value) { int val = 0; char buf[2]; const void *evalue; const teken_attr_t *ap; teken_attr_t a; if (value == NULL) return (CMD_OK); if (color_name_to_teken(value, &val)) { snprintf(buf, sizeof (buf), "%d", val); evalue = buf; } else { char *end; errno = 0; val = (int)strtol(value, &end, 0); if (errno != 0 || *end != '\0') { printf("Allowed values are either ansi color name or " "number from range [0-7].\n"); return (CMD_OK); } 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? */ if (ap->ta_fgcolor == val) return (CMD_OK); a.ta_fgcolor = val; } if (strcmp(ev->ev_name, "teken.bg_color") == 0) { /* is it already set? */ if (ap->ta_bgcolor == val) 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); } +static int +env_screen_nounset(struct env_var *ev __unused) +{ + if (gfx_state.tg_fb_type == FB_TEXT) + return (0); + return (EPERM); +} + +static int +vidc_load_palette(uint32_t *cmap) +{ + int i, roff, goff, boff, rc; + + if (pe8 == NULL) + pe8 = calloc(sizeof(*pe8), NCMAP); + if (pe8 == NULL) + return (ENOMEM); + + /* Generate VGA colors */ + roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; + goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; + boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; + rc = generate_cons_palette((uint32_t *)pe8, COLOR_FORMAT_RGB, + gfx_state.tg_fb.fb_mask_red >> roff, roff, + gfx_state.tg_fb.fb_mask_green >> goff, goff, + gfx_state.tg_fb.fb_mask_blue >> boff, boff); + + if (rc == 0) { + for (i = 0; i < NCMAP; i++) { + int idx; + + if (i < NCOLORS) + idx = cons_to_vga_colors[i]; + else + idx = i; + + rc = vbe_set_palette(&pe8[i], idx); + if (rc != 0) + break; + } + } + return (rc); +} + +static void +cons_draw_frame(teken_attr_t *a) +{ + teken_attr_t attr = *a; + teken_color_t fg = a->ta_fgcolor; + + attr.ta_fgcolor = attr.ta_bgcolor; + teken_set_defattr(&gfx_state.tg_teken, &attr); + + gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, + gfx_state.tg_origin.tp_row, 1); + gfx_fb_drawrect(0, + gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, + gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); + gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, + gfx_state.tg_origin.tp_col, + gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); + gfx_fb_drawrect( + gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, + gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, + gfx_state.tg_fb.fb_height, 1); + + attr.ta_fgcolor = fg; + teken_set_defattr(&gfx_state.tg_teken, &attr); +} + +/* + * Binary searchable table for CP437 to Unicode conversion. + */ +struct cp437uni { + uint8_t cp437_base; + uint16_t unicode_base; + uint8_t length; +}; + +static const struct cp437uni cp437unitable[] = { + { 0, 0x0000, 0 }, { 1, 0x263A, 1 }, { 3, 0x2665, 1 }, + { 5, 0x2663, 0 }, { 6, 0x2660, 0 }, { 7, 0x2022, 0 }, + { 8, 0x25D8, 0 }, { 9, 0x25CB, 0 }, { 10, 0x25D9, 0 }, + { 11, 0x2642, 0 }, { 12, 0x2640, 0 }, { 13, 0x266A, 1 }, + { 15, 0x263C, 0 }, { 16, 0x25BA, 0 }, { 17, 0x25C4, 0 }, + { 18, 0x2195, 0 }, { 19, 0x203C, 0 }, { 20, 0x00B6, 0 }, + { 21, 0x00A7, 0 }, { 22, 0x25AC, 0 }, { 23, 0x21A8, 0 }, + { 24, 0x2191, 0 }, { 25, 0x2193, 0 }, { 26, 0x2192, 0 }, + { 27, 0x2190, 0 }, { 28, 0x221F, 0 }, { 29, 0x2194, 0 }, + { 30, 0x25B2, 0 }, { 31, 0x25BC, 0 }, { 32, 0x0020, 0x5e }, + { 127, 0x2302, 0 }, { 128, 0x00C7, 0 }, { 129, 0x00FC, 0 }, + { 130, 0x00E9, 0 }, { 131, 0x00E2, 0 }, { 132, 0x00E4, 0 }, + { 133, 0x00E0, 0 }, { 134, 0x00E5, 0 }, { 135, 0x00E7, 0 }, + { 136, 0x00EA, 1 }, { 138, 0x00E8, 0 }, { 139, 0x00EF, 0 }, + { 140, 0x00EE, 0 }, { 141, 0x00EC, 0 }, { 142, 0x00C4, 1 }, + { 144, 0x00C9, 0 }, { 145, 0x00E6, 0 }, { 146, 0x00C6, 0 }, + { 147, 0x00F4, 0 }, { 148, 0x00F6, 0 }, { 149, 0x00F2, 0 }, + { 150, 0x00FB, 0 }, { 151, 0x00F9, 0 }, { 152, 0x00FF, 0 }, + { 153, 0x00D6, 0 }, { 154, 0x00DC, 0 }, { 155, 0x00A2, 1 }, + { 157, 0x00A5, 0 }, { 158, 0x20A7, 0 }, { 159, 0x0192, 0 }, + { 160, 0x00E1, 0 }, { 161, 0x00ED, 0 }, { 162, 0x00F3, 0 }, + { 163, 0x00FA, 0 }, { 164, 0x00F1, 0 }, { 165, 0x00D1, 0 }, + { 166, 0x00AA, 0 }, { 167, 0x00BA, 0 }, { 168, 0x00BF, 0 }, + { 169, 0x2310, 0 }, { 170, 0x00AC, 0 }, { 171, 0x00BD, 0 }, + { 172, 0x00BC, 0 }, { 173, 0x00A1, 0 }, { 174, 0x00AB, 0 }, + { 175, 0x00BB, 0 }, { 176, 0x2591, 2 }, { 179, 0x2502, 0 }, + { 180, 0x2524, 0 }, { 181, 0x2561, 1 }, { 183, 0x2556, 0 }, + { 184, 0x2555, 0 }, { 185, 0x2563, 0 }, { 186, 0x2551, 0 }, + { 187, 0x2557, 0 }, { 188, 0x255D, 0 }, { 189, 0x255C, 0 }, + { 190, 0x255B, 0 }, { 191, 0x2510, 0 }, { 192, 0x2514, 0 }, + { 193, 0x2534, 0 }, { 194, 0x252C, 0 }, { 195, 0x251C, 0 }, + { 196, 0x2500, 0 }, { 197, 0x253C, 0 }, { 198, 0x255E, 1 }, + { 200, 0x255A, 0 }, { 201, 0x2554, 0 }, { 202, 0x2569, 0 }, + { 203, 0x2566, 0 }, { 204, 0x2560, 0 }, { 205, 0x2550, 0 }, + { 206, 0x256C, 0 }, { 207, 0x2567, 1 }, { 209, 0x2564, 1 }, + { 211, 0x2559, 0 }, { 212, 0x2558, 0 }, { 213, 0x2552, 1 }, + { 215, 0x256B, 0 }, { 216, 0x256A, 0 }, { 217, 0x2518, 0 }, + { 218, 0x250C, 0 }, { 219, 0x2588, 0 }, { 220, 0x2584, 0 }, + { 221, 0x258C, 0 }, { 222, 0x2590, 0 }, { 223, 0x2580, 0 }, + { 224, 0x03B1, 0 }, { 225, 0x00DF, 0 }, { 226, 0x0393, 0 }, + { 227, 0x03C0, 0 }, { 228, 0x03A3, 0 }, { 229, 0x03C3, 0 }, + { 230, 0x00B5, 0 }, { 231, 0x03C4, 0 }, { 232, 0x03A6, 0 }, + { 233, 0x0398, 0 }, { 234, 0x03A9, 0 }, { 235, 0x03B4, 0 }, + { 236, 0x221E, 0 }, { 237, 0x03C6, 0 }, { 238, 0x03B5, 0 }, + { 239, 0x2229, 0 }, { 240, 0x2261, 0 }, { 241, 0x00B1, 0 }, + { 242, 0x2265, 0 }, { 243, 0x2264, 0 }, { 244, 0x2320, 1 }, + { 246, 0x00F7, 0 }, { 247, 0x2248, 0 }, { 248, 0x00B0, 0 }, + { 249, 0x2219, 0 }, { 250, 0x00B7, 0 }, { 251, 0x221A, 0 }, + { 252, 0x207F, 0 }, { 253, 0x00B2, 0 }, { 254, 0x25A0, 0 }, + { 255, 0x00A0, 0 } +}; + +static uint16_t +vga_cp437_to_uni(uint8_t c) +{ + int min, mid, max; + + min = 0; + max = (sizeof(cp437unitable) / sizeof(struct cp437uni)) - 1; + + while (max >= min) { + mid = (min + max) / 2; + if (c < cp437unitable[mid].cp437_base) + max = mid - 1; + else if (c > cp437unitable[mid].cp437_base + + cp437unitable[mid].length) + min = mid + 1; + else + return (c - cp437unitable[mid].cp437_base + + cp437unitable[mid].unicode_base); + } + + return ('?'); +} + +/* + * install font for text mode + */ +static void +vidc_install_font(void) +{ + static uint8_t fsreg[8] = {0x0, 0x30, 0x5, 0x35, 0xa, 0x3a, 0xf, 0x3f}; + const uint8_t *from; + uint8_t volatile *to; + uint16_t c; + int i, j, s; + int bpc, f_offset; + teken_attr_t a = { 0 }; + + if (gfx_state.tg_fb_type != FB_TEXT) + return; + + /* Sync-reset the sequencer registers */ + vga_set_seq(VGA_REG_BASE, 0x00, 0x01); + /* + * enable write to plane2, since fonts + * could only be loaded into plane2 + */ + vga_set_seq(VGA_REG_BASE, 0x02, 0x04); + /* + * sequentially access data in the bit map being + * selected by MapMask register (index 0x02) + */ + vga_set_seq(VGA_REG_BASE, 0x04, 0x07); + /* Sync-reset ended, and allow the sequencer to operate */ + vga_set_seq(VGA_REG_BASE, 0x00, 0x03); + + /* + * select plane 2 on Read Mode 0 + */ + vga_set_grc(VGA_REG_BASE, 0x04, 0x02); + /* + * system addresses sequentially access data, follow + * Memory Mode register bit 2 in the sequencer + */ + vga_set_grc(VGA_REG_BASE, 0x05, 0x00); + /* + * set range of host memory addresses decoded by VGA + * hardware -- A0000h-BFFFFh (128K region) + */ + vga_set_grc(VGA_REG_BASE, 0x06, 0x00); + + /* + * This assumes 8x16 characters, which yield the traditional 80x25 + * screen. + */ + bpc = 16; + s = 0; /* font slot, maybe should use tunable there. */ + f_offset = s * 8 * 1024; + for (i = 0; i < 256; i++) { + c = vga_cp437_to_uni(i); + from = font_lookup(&gfx_state.tg_font, c, &a); + to = (unsigned char *)ptov(VGA_MEM_BASE) + f_offset + + i * 0x20; + for (j = 0; j < bpc; j++) + *to++ = *from++; + } + + /* Sync-reset the sequencer registers */ + vga_set_seq(VGA_REG_BASE, 0x00, 0x01); + /* enable write to plane 0 and 1 */ + vga_set_seq(VGA_REG_BASE, 0x02, 0x03); + /* + * enable character map selection + * and odd/even addressing + */ + vga_set_seq(VGA_REG_BASE, 0x04, 0x03); + /* + * select font map + */ + vga_set_seq(VGA_REG_BASE, 0x03, fsreg[s]); + /* Sync-reset ended, and allow the sequencer to operate */ + vga_set_seq(VGA_REG_BASE, 0x00, 0x03); + + /* restore graphic registers */ + + /* select plane 0 */ + vga_set_grc(VGA_REG_BASE, 0x04, 0x00); + /* enable odd/even addressing mode */ + vga_set_grc(VGA_REG_BASE, 0x05, 0x10); + /* + * range of host memory addresses decoded by VGA + * hardware -- B8000h-BFFFFh (32K region) + */ + vga_set_grc(VGA_REG_BASE, 0x06, 0x0e); + /* enable all color plane */ + vga_set_atr(VGA_REG_BASE, 0x12, 0x0f); +} + +bool +cons_update_mode(bool use_gfx_mode) +{ + const teken_attr_t *a; + teken_attr_t attr; + char env[10], *ptr; + int format, roff, goff, boff; + + gfx_state.tg_tp.tp_row = TEXT_ROWS; + gfx_state.tg_tp.tp_col = TEXT_COLS; + + if (use_gfx_mode) { + setup_font(&gfx_state, gfx_state.tg_fb.fb_height, + gfx_state.tg_fb.fb_width); + /* Point of origin in pixels. */ + gfx_state.tg_origin.tp_row = (gfx_state.tg_fb.fb_height - + (gfx_state.tg_tp.tp_row * gfx_state.tg_font.vf_height)) / 2; + gfx_state.tg_origin.tp_col = (gfx_state.tg_fb.fb_width - + (gfx_state.tg_tp.tp_col * gfx_state.tg_font.vf_width)) / 2; + + gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * + gfx_state.tg_font.vf_width * 4; + free(gfx_state.tg_glyph); + gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); + if (gfx_state.tg_glyph == NULL) + return (false); + gfx_state.tg_functions = &tfx; + snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_height); + env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, env, + env_noset, env_screen_nounset); + snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_width); + env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, env, + env_noset, env_screen_nounset); + snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_bpp); + env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, env, + env_noset, env_screen_nounset); + } else { + /* Trigger loading of 8x16 font. */ + setup_font(&gfx_state, + 16 * gfx_state.tg_fb.fb_height + BORDER_PIXELS, + 8 * gfx_state.tg_fb.fb_width + BORDER_PIXELS); + gfx_state.tg_functions = &tf; + /* ensure the following are not set for text mode */ + unsetenv("screen.height"); + unsetenv("screen.width"); + unsetenv("screen.depth"); + unsetenv("kern.vt.fb.default_mode"); + vidc_install_font(); + } + + free(screen_buffer); + screen_buffer = malloc(gfx_state.tg_tp.tp_row * gfx_state.tg_tp.tp_col * + sizeof(*screen_buffer)); + if (screen_buffer == NULL) + return (false); + + teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, &gfx_state); + + if (gfx_state.tg_ctype == CT_INDEXED) + format = COLOR_FORMAT_VGA; + else + format = COLOR_FORMAT_RGB; + + roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; + goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; + boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; + (void) generate_cons_palette(cmap, format, + gfx_state.tg_fb.fb_mask_red >> roff, roff, + gfx_state.tg_fb.fb_mask_green >> goff, goff, + gfx_state.tg_fb.fb_mask_blue >> boff, boff); + + if (gfx_state.tg_ctype == CT_INDEXED) + vidc_load_palette(cmap); + + teken_set_winsize(&gfx_state.tg_teken, &gfx_state.tg_tp); + a = teken_get_defattr(&gfx_state.tg_teken); + attr = *a; + + /* + * On first run, we set up the vidc_set_colors() + * callback. If the env is already set, we + * pick up fg and bg color values from the environment. + */ + ptr = getenv("teken.fg_color"); + if (ptr != NULL) { + attr.ta_fgcolor = strtol(ptr, NULL, 10); + ptr = getenv("teken.bg_color"); + attr.ta_bgcolor = strtol(ptr, NULL, 10); + + teken_set_defattr(&gfx_state.tg_teken, &attr); + } else { + snprintf(env, sizeof(env), "%d", attr.ta_fgcolor); + env_setenv("teken.fg_color", EV_VOLATILE, env, + vidc_set_colors, env_nounset); + snprintf(env, sizeof(env), "%d", attr.ta_bgcolor); + env_setenv("teken.bg_color", EV_VOLATILE, env, + vidc_set_colors, env_nounset); + } + + /* Improve visibility */ + if (attr.ta_bgcolor == TC_WHITE) + attr.ta_bgcolor |= TC_LIGHT; + teken_set_defattr(&gfx_state.tg_teken, &attr); + + snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_row); + setenv("LINES", env, 1); + snprintf(env, sizeof (env), "%u", (unsigned)gfx_state.tg_tp.tp_col); + setenv("COLUMNS", env, 1); + + /* Draw frame around terminal area. */ + cons_draw_frame(&attr); + /* Erase display, this will also fill our screen buffer. */ + teken_input(&gfx_state.tg_teken, "\e[2J", 4); + gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 1); + + return (true); +} + static int vidc_init(int arg) { const teken_attr_t *a; int val; char env[8]; if (vidc_started && arg == 0) return (0); vidc_started = 1; + vbe_init(); /* * Check Miscellaneous Output Register (Read at 3CCh, Write at 3C2h) * for bit 1 (Input/Output Address Select), which means * color/graphics adapter. */ if (vga_get_reg(VGA_REG_BASE, VGA_GEN_MISC_OUTPUT_R) & VGA_GEN_MO_IOA) vgatext = (uint16_t *)PTOV(VGA_TXT_BASE); else vgatext = (uint16_t *)PTOV(VGA_MEM_BASE + VGA_MEM_SIZE); /* set 16bit colors */ val = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL); val &= ~VGA_AC_MC_BI; val &= ~VGA_AC_MC_ELG; vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, val); - tp.tp_row = TEXT_ROWS; - tp.tp_col = TEXT_COLS; - buffer = malloc(tp.tp_row * tp.tp_col * sizeof(*buffer)); - if (buffer == NULL) - return (1); - - snprintf(env, sizeof (env), "%u", tp.tp_row); - setenv("LINES", env, 1); - snprintf(env, sizeof (env), "%u", tp.tp_col); - setenv("COLUMNS", env, 1); - - teken_init(&teken, &tf, NULL); - teken_set_winsize(&teken, &tp); - a = teken_get_defattr(&teken); +#if defined(FRAMEBUFFER_MODE) + val = vbe_default_mode(); + /* if val is not legal VBE mode, use text mode */ + if (VBE_VALID_MODE(val)) { + if (vbe_set_mode(val) != 0) + bios_set_text_mode(VGA_TEXT_MODE); + } +#endif - snprintf(env, sizeof(env), "%d", a->ta_fgcolor); - env_setenv("teken.fg_color", EV_VOLATILE, env, vidc_set_colors, - env_nounset); - snprintf(env, sizeof(env), "%d", a->ta_bgcolor); - env_setenv("teken.bg_color", EV_VOLATILE, env, vidc_set_colors, - env_nounset); + gfx_framework_init(); - /* Erase display, this will also fill our screen buffer. */ - teken_input(&teken, "\e[J", 3); + if (!cons_update_mode(VBE_VALID_MODE(vbe_get_mode()))) + return (1); for (int i = 0; i < 10 && vidc_ischar(); i++) (void) vidc_getchar(); return (0); /* XXX reinit? */ } void vidc_biosputchar(int c) { v86.ctl = 0; v86.addr = 0x10; v86.eax = 0xe00 | (c & 0xff); v86.ebx = 0x7; v86int(); } static void vidc_putchar(int c) { unsigned char ch = c; - if (buffer != NULL) - teken_input(&teken, &ch, sizeof (ch)); + if (screen_buffer != NULL) + teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); else vidc_biosputchar(c); } static int vidc_getchar(void) { int i, c; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) { c = keybuf[i]; keybuf[i] = 0; return (c); } } if (vidc_ischar()) { v86.ctl = 0; v86.addr = 0x16; v86.eax = 0x0; v86int(); if ((v86.eax & 0xff) != 0) { return (v86.eax & 0xff); } /* extended keys */ switch (v86.eax & 0xff00) { case 0x4800: /* up */ keybuf[0] = '['; keybuf[1] = 'A'; return (0x1b); /* esc */ case 0x4b00: /* left */ keybuf[0] = '['; keybuf[1] = 'D'; return (0x1b); /* esc */ case 0x4d00: /* right */ keybuf[0] = '['; keybuf[1] = 'C'; return (0x1b); /* esc */ case 0x5000: /* down */ keybuf[0] = '['; keybuf[1] = 'B'; return (0x1b); /* esc */ default: return (-1); } } else { return (-1); } } static int vidc_ischar(void) { int i; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) { return (1); } } v86.ctl = V86_FLAGS; v86.addr = 0x16; v86.eax = 0x100; v86int(); return (!V86_ZR(v86.efl)); } #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()'. */ inb(IO_DUMMY); inb(IO_DUMMY); inb(IO_DUMMY); inb(IO_DUMMY); inb(IO_DUMMY); inb(IO_DUMMY); } /* * This routine uses an inb to an unused port, the time to execute that * inb is approximately 1.25uS. This value is pretty constant across * all CPU's and all buses, with the exception of some PCI implentations * that do not forward this I/O address to the ISA bus as they know it * is not a valid ISA bus address, those machines execute this inb in * 60 nS :-(. * */ static void delay1ms(void) { int i = 800; while (--i >= 0) (void) inb(0x84); } -/* +/* * We use the presence/absence of a keyboard to determine whether the internal * console can be used for input. * * Perform a simple test on the keyboard; issue the ECHO command and see * if the right answer is returned. We don't do anything as drastic as * full keyboard reset; it will be too troublesome and take too much time. */ static int probe_keyboard(void) { int retry = PROBE_MAXRETRY; int wait; int i; while (--retry >= 0) { /* flush any noise */ while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { delay7(); inb(IO_KBD + KBD_DATA_PORT); delay1ms(); } /* wait until the controller can accept a command */ for (wait = PROBE_MAXWAIT; wait > 0; --wait) { if (((i = inb(IO_KBD + KBD_STATUS_PORT)) & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) break; if (i & KBDS_ANY_BUFFER_FULL) { delay7(); inb(IO_KBD + KBD_DATA_PORT); } delay1ms(); } if (wait <= 0) continue; /* send the ECHO command */ outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); /* wait for a response */ for (wait = PROBE_MAXWAIT; wait > 0; --wait) { if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) break; delay1ms(); } if (wait <= 0) continue; delay7(); i = inb(IO_KBD + KBD_DATA_PORT); #ifdef PROBE_KBD_BEBUG printf("probe_keyboard: got 0x%x.\n", i); #endif if (i == KBD_ECHO) { /* got the right answer */ return (1); } } return (0); } #endif /* KEYBOARD_PROBE */ diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile index 74f39c078645..e80e19dfb9e6 100644 --- a/stand/i386/loader/Makefile +++ b/stand/i386/loader/Makefile @@ -1,93 +1,102 @@ # $FreeBSD$ HAVE_ZFS= ${MK_LOADER_ZFS} LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= yes LOADER_CD9660_SUPPORT?= yes LOADER_EXT2FS_SUPPORT?= yes LOADER_MSDOS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_GZIP_SUPPORT?= yes LOADER_BZIP2_SUPPORT?= yes .include <bsd.init.mk> LOADER?= loader_${LOADER_INTERP} PROG= ${LOADER}.sym INTERNALPROG= NEWVERSWHAT?= "bootstrap loader" x86 VERSION_FILE= ${.CURDIR}/../loader/version .PATH: ${BOOTSRC}/i386/loader # architecture-specific loader code -SRCS= main.c conf.c vers.c chain.c +SRCS= main.c conf.c vers.c chain.c gfx_fb.c 8x16.c + +CFLAGS.gfx_fb.c += -I${.CURDIR}/../libi386 +CFLAGS.gfx_fb.c += -I$(SRCTOP)/sys/teken +CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4 +CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite +CFLAGS.gfx_fb.c += -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib # Include bcache code. HAVE_BCACHE= yes # Enable PnP and ISA-PnP code. HAVE_PNP= yes HAVE_ISABUS= yes .if ${MK_LOADER_FIREWIRE} == "yes" CFLAGS+= -DLOADER_FIREWIRE_SUPPORT LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a .endif .if ${MK_LOADER_ZFS} == "yes" CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs .endif .if exists(${.CURDIR}/help.i386) HELP_FILES= ${.CURDIR}/help.i386 .endif # Always add MI sources .include "${BOOTSRC}/loader.mk" -CLEANFILES+= ${LOADER} ${LOADER}.bin +CLEANFILES+= ${LOADER} ${LOADER}.bin 8x16.c ORG= 0x0 CFLAGS+= -Wall LDFLAGS+= -static ${LDFLAGS_ORG} -Wl,--gc-sections # i386 standalone support library LIBI386= ${BOOTOBJ}/i386/libi386/libi386.a CFLAGS+= -I${BOOTSRC}/i386 # Debug me! #CFLAGS+= -g #LDFLAGS+= -g +8x16.c: ${SRCTOP}/contrib/terminus/ter-u16v.bdf + vtfontcvt -f compressed-source -o ${.TARGET} ${.ALLSRC} + ${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN} btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \ -b ${BTXKERN} ${LOADER}.bin ${LOADER}.bin: ${LOADER}.sym ${STRIPBIN} -R .comment -R .note -o ${.TARGET} ${.ALLSRC} .if ${MK_LOADER_ZFS} == "yes" && ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} LINKS+= ${BINDIR}/${LOADER} ${BINDIR}/zfsloader .endif .if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} LINKS+= ${BINDIR}/${LOADER} ${BINDIR}/loader .endif FILES+= ${LOADER} FILESMODE_${LOADER}= ${BINMODE} -b # XXX crt0.o needs to be first for pxeboot(8) to work OBJS= ${BTXCRT} DPADD= ${LDR_INTERP32} ${LIBFIREWIRE} ${LIBI386} ${LIBSA32} LDADD= ${LDR_INTERP32} ${LIBFIREWIRE} ${LIBI386} ${LIBSA32} .if ${MACHINE_CPUARCH} == "amd64" CFLAGS+= -DLOADER_PREFER_AMD64 .endif .include <bsd.prog.mk> diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c index ceeaf7260acc..a595340d3a13 100644 --- a/stand/i386/loader/main.c +++ b/stand/i386/loader/main.c @@ -1,454 +1,458 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); /* * MD bootstrap main() and assorted miscellaneous * commands. */ #include <stand.h> #include <stddef.h> #include <string.h> #include <machine/bootinfo.h> #include <machine/cpufunc.h> #include <machine/psl.h> #include <sys/disk.h> #include <sys/reboot.h> #include <common/drv.h> #include "bootstrap.h" #include "common/bootargs.h" #include "libi386/libi386.h" #include <smbios.h> #include "btxv86.h" #ifdef LOADER_ZFS_SUPPORT #include <sys/zfs_bootenv.h> #include "libzfs.h" #endif CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE); CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO); CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS); CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE); /* Arguments passed in from the boot1/boot2 loader */ static struct bootargs *kargs; static uint32_t initial_howto; static uint32_t initial_bootdev; static struct bootinfo *initial_bootinfo; struct arch_switch archsw; /* MI/MD interface boundary */ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" struct geli_boot_args *gargs; struct geli_boot_data *gbdata; #endif #ifdef LOADER_ZFS_SUPPORT struct zfs_boot_args *zargs; static void i386_zfs_probe(void); #endif /* XXX debugging */ extern char end[]; static void *heap_top; static void *heap_bottom; caddr_t ptov(uintptr_t x) { return (PTOV(x)); } int main(void) { int i; /* Pick up arguments */ kargs = (void *)__args; initial_howto = kargs->howto; initial_bootdev = kargs->bootdev; initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL; /* Initialize the v86 register set to a known-good state. */ bzero(&v86, sizeof(v86)); v86.efl = PSL_RESERVED_DEFAULT | PSL_I; /* * Initialise the heap as early as possible. * Once this is done, malloc() is usable. */ bios_getmem(); #if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \ defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT) if (high_heap_size > 0) { heap_top = PTOV(high_heap_base + high_heap_size); heap_bottom = PTOV(high_heap_base); if (high_heap_base < memtop_copyin) memtop_copyin = high_heap_base; } else #endif { heap_top = (void *)PTOV(bios_basemem); heap_bottom = (void *)end; } 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. * * We can use printf() etc. once this is done. * If the previous boot stage has requested a serial console, * prefer that. */ bi_setboothowto(initial_howto); if (initial_howto & RB_MULTIPLE) { if (initial_howto & RB_SERIAL) setenv("console", "comconsole vidconsole", 1); else setenv("console", "vidconsole comconsole", 1); } else if (initial_howto & RB_SERIAL) { setenv("console", "comconsole", 1); } else if (initial_howto & RB_MUTE) { setenv("console", "nullconsole", 1); } cons_probe(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Special handling for PXE and CD booting. */ if (kargs->bootinfo == 0) { /* * We only want the PXE disk to try to init itself in the below * walk through devsw if we actually booted off of PXE. */ if (kargs->bootflags & KARGS_FLAGS_PXE) pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL); else if (kargs->bootflags & KARGS_FLAGS_CD) bc_add(initial_bootdev); } archsw.arch_autoload = i386_autoload; archsw.arch_getdev = i386_getdev; archsw.arch_copyin = i386_copyin; archsw.arch_copyout = i386_copyout; archsw.arch_readin = i386_readin; archsw.arch_isainb = isa_inb; archsw.arch_isaoutb = isa_outb; archsw.arch_hypervisor = x86_hypervisor; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; /* * zfsboot and gptzfsboot have always passed KARGS_FLAGS_ZFS, * so if that is set along with KARGS_FLAGS_EXTARG we know we * can interpret the extarg data as a struct zfs_boot_args. */ #define KARGS_EXTARGS_ZFS (KARGS_FLAGS_EXTARG | KARGS_FLAGS_ZFS) if ((kargs->bootflags & KARGS_EXTARGS_ZFS) == KARGS_EXTARGS_ZFS) { zargs = (struct zfs_boot_args *)(kargs + 1); } #endif /* LOADER_ZFS_SUPPORT */ #ifdef LOADER_GELI_SUPPORT /* * If we decided earlier that we have zfs_boot_args extarg data, * and it is big enough to contain the embedded geli data * (the early zfs_boot_args structs weren't), then init the gbdata * pointer accordingly. If there is extarg data which isn't * zfs_boot_args data, determine whether it is geli_boot_args data. * Recent versions of gptboot set KARGS_FLAGS_GELI to indicate that. * Earlier versions didn't, but we presume that's what we * have if the extarg size exactly matches the size of the * geli_boot_args struct during that pre-flag era. */ #define LEGACY_GELI_ARGS_SIZE 260 /* This can never change */ #ifdef LOADER_ZFS_SUPPORT if (zargs != NULL) { if (zargs->size > offsetof(struct zfs_boot_args, gelidata)) { gbdata = &zargs->gelidata; } } else #endif /* LOADER_ZFS_SUPPORT */ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { gargs = (struct geli_boot_args *)(kargs + 1); if ((kargs->bootflags & KARGS_FLAGS_GELI) || gargs->size == LEGACY_GELI_ARGS_SIZE) { gbdata = &gargs->gelidata; } } if (gbdata != NULL) import_geli_boot_data(gbdata); #endif /* LOADER_GELI_SUPPORT */ /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024); if (initial_bootinfo != NULL) { initial_bootinfo->bi_basemem = bios_basemem / 1024; initial_bootinfo->bi_extmem = bios_extmem / 1024; } - /* detect ACPI for future reference */ - biosacpi_detect(); - /* detect SMBIOS for future reference */ smbios_detect(NULL); /* detect PCI BIOS for future reference */ biospci_detect(); printf("\n%s", bootprog_info); extract_currdev(); /* set $currdev and $loaddev */ + autoload_font(true); bios_getsmap(); interact(); /* if we ever get here, it is an error */ return (1); } /* * Set the 'current device' by (if possible) recovering the boot device as * supplied by the initial bootstrap. * * XXX should be extended for netbooting. */ static void extract_currdev(void) { struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; char *bootonce; #endif int biosdev = -1; /* Assume we are booting from a BIOS disk by default */ new_currdev.dd.d_dev = &bioshd; /* new-style boot loaders such as pxeldr and cdldr */ if (kargs->bootinfo == 0) { if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) { /* we are booting from a CD with cdboot */ new_currdev.dd.d_dev = &bioscd; new_currdev.dd.d_unit = bd_bios2unit(initial_bootdev); } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) { /* we are booting from pxeldr */ new_currdev.dd.d_dev = &pxedisk; new_currdev.dd.d_unit = 0; } else { /* we don't know what our boot device is */ new_currdev.d_kind.biosdisk.slice = -1; new_currdev.d_kind.biosdisk.partition = 0; biosdev = -1; } #ifdef LOADER_ZFS_SUPPORT } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { /* * zargs was set in main() if we have new style extended * argument */ if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) { /* sufficient data is provided */ new_currdev.d_kind.zfs.pool_guid = zargs->pool; new_currdev.d_kind.zfs.root_guid = zargs->root; if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) { sprintf(buf, "%llu", zargs->primary_pool); setenv("vfs.zfs.boot.primary_pool", buf, 1); sprintf(buf, "%llu", zargs->primary_vdev); setenv("vfs.zfs.boot.primary_vdev", buf, 1); } } else { /* old style zfsboot block */ new_currdev.d_kind.zfs.pool_guid = kargs->zfspool; new_currdev.d_kind.zfs.root_guid = 0; } new_currdev.dd.d_dev = &zfs_dev; if ((bootonce = malloc(VDEV_PAD_SIZE)) != NULL) { if (zfs_get_bootonce(&new_currdev, OS_BOOTONCE_USED, bootonce, VDEV_PAD_SIZE) == 0) { setenv("zfs-bootonce", bootonce, 1); } free(bootonce); (void) zfs_attach_nvstore(&new_currdev); } #endif } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) { /* The passed-in boot device is bad */ new_currdev.d_kind.biosdisk.slice = -1; new_currdev.d_kind.biosdisk.partition = 0; biosdev = -1; } else { new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1; new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev); biosdev = initial_bootinfo->bi_bios_dev; /* * If we are booted by an old bootstrap, we have to guess at * the BIOS unit number. We will lose if there is more than * one disk type and we are not booting from the * lowest-numbered disk type (ie. SCSI when IDE also exists). */ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) { /* * biosdev doesn't match major, assume harddisk */ biosdev = 0x80 + B_UNIT(initial_bootdev); } } /* * If we are booting off of a BIOS disk and we didn't succeed * in determining which one we booted off of, just use disk0: * as a reasonable default. */ if ((new_currdev.dd.d_dev->dv_type == bioshd.dv_type) && ((new_currdev.dd.d_unit = bd_bios2unit(biosdev)) == -1)) { printf("Can't work out which disk we are booting " "from.\nGuessed BIOS device 0x%x not found by " "probes, defaulting to disk0:\n", biosdev); new_currdev.dd.d_unit = 0; } #ifdef LOADER_ZFS_SUPPORT if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS) init_zfs_boot_options(zfs_fmtdev(&new_currdev)); #endif env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), i386_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, env_nounset); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); printf("Rebooting...\n"); delay(1000000); __exit(0); } /* provide this for panic, as it's not in the startup code */ void exit(int code) { __exit(code); } COMMAND_SET(heap, "heap", "show heap usage", command_heap); static int command_heap(int argc, char *argv[]) { mallocstats(); printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom, sbrk(0), heap_top); return (CMD_OK); } /* ISA bus access functions for PnP. */ static int isa_inb(int port) { return (inb(port)); } static void isa_outb(int port, int value) { outb(port, value); } #ifdef LOADER_ZFS_SUPPORT static void i386_zfs_probe(void) { char devname[32]; struct i386_devdesc dev; /* * Open all the disks we can find and see if we can reconstruct * ZFS pools from them. */ dev.dd.d_dev = &bioshd; for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) { snprintf(devname, sizeof(devname), "%s%d:", bioshd.dv_name, dev.dd.d_unit); zfs_probe_dev(devname, NULL); } } #endif diff --git a/stand/images/Makefile b/stand/images/Makefile new file mode 100644 index 000000000000..432b1daa851b --- /dev/null +++ b/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> diff --git a/stand/images/freebsd-brand-rev.png b/stand/images/freebsd-brand-rev.png new file mode 100644 index 000000000000..945c12da0556 Binary files /dev/null and b/stand/images/freebsd-brand-rev.png differ diff --git a/stand/images/freebsd-brand.png b/stand/images/freebsd-brand.png new file mode 100644 index 000000000000..8ce3091bd83f Binary files /dev/null and b/stand/images/freebsd-brand.png differ diff --git a/stand/images/freebsd-logo-rev.png b/stand/images/freebsd-logo-rev.png new file mode 100644 index 000000000000..5279b718218c Binary files /dev/null and b/stand/images/freebsd-logo-rev.png differ diff --git a/stand/liblua/Makefile b/stand/liblua/Makefile index 434268063158..68fa2da64fdb 100644 --- a/stand/liblua/Makefile +++ b/stand/liblua/Makefile @@ -1,45 +1,47 @@ # $FreeBSD$ .include <bsd.init.mk> .PATH: ${LUASRC} .PATH: ${LIBLUASRC} .include "${BOOTSRC}/lua.mk" LIB= lua INTERNALLIB= # Core Lua. SRCS= lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c \ lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c \ ltm.c lundump.c lvm.c lzio.c SRCS+= lauxlib.c lbaselib.c lstrlib.c loadlib.c # These aren't yet included, but link now, omitting them saves 15k #SRCS+= lcorolib.c ldblib.c lutf8lib.c # These aren't yet compatible with the boot environment, and some may never be #SRCS+= lbitlib.c liolib.c lmathlib.c loslib.c ltablib.c # Our utilities. SRCS+= lerrno.c lpager.c lstd.c lutils.c .PATH: ${FLUASRC}/modules SRCS+= lfs.c WARNS?= 3 CFLAGS+= -DLUA_PATH=\"${LUAPATH}\" -DLUA_PATH_DEFAULT=\"${LUAPATH}/\?.lua\" CFLAGS+= -ffreestanding -nostdlib -DLUA_USE_POSIX 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 .if ${MK_LOADER_VERIEXEC} == "yes" CFLAGS+= -I${SRCTOP}/lib/libsecureboot/h -DLOADER_VERIEXEC .endif .include <bsd.lib.mk> diff --git a/stand/liblua/lutils.c b/stand/liblua/lutils.c index 7c4dcf01f7cc..9243edd07e8f 100644 --- a/stand/liblua/lutils.c +++ b/stand/liblua/lutils.c @@ -1,399 +1,591 @@ /*- * Copyright (c) 2014 Pedro Souza <pedrosouza@freebsd.org> * 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 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. * */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/param.h> #include "lua.h" #include "lauxlib.h" #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 * on the stack. */ static int lua_command(lua_State *L) { int i; int res = 1; int argc = lua_gettop(L); char **argv; argv = malloc(sizeof(char *) * (argc + 1)); if (argv == NULL) return 0; for (i = 0; i < argc; i++) argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1); argv[argc] = NULL; res = interp_builtin_cmd(argc, argv); free(argv); lua_pushinteger(L, res); return 1; } static int lua_perform(lua_State *L) { int argc; char **argv; int res = 1; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { res = interp_builtin_cmd(argc, argv); free(argv); } lua_pushinteger(L, res); return 1; } static int lua_command_error(lua_State *L) { lua_pushstring(L, command_errbuf); return 1; } /* * Accepts a space-delimited loader command and runs it through the standard * loader parsing, as if it were executed at the loader prompt by the user. */ static int lua_interpret(lua_State *L) { const char *interp_string; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } interp_string = luaL_checkstring(L, 1); lua_pushinteger(L, interp_run(interp_string)); return 1; } static int lua_parse(lua_State *L) { int argc, nargc; char **argv; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { for (nargc = 0; nargc < argc; ++nargc) { lua_pushstring(L, argv[nargc]); } free(argv); return nargc; } lua_pushnil(L); return 1; } static int lua_getchar(lua_State *L) { lua_pushinteger(L, getchar()); return 1; } static int lua_ischar(lua_State *L) { lua_pushboolean(L, ischar()); return 1; } static int lua_gets(lua_State *L) { char buf[129]; ngets(buf, 128); lua_pushstring(L, buf); return 1; } static int lua_time(lua_State *L) { lua_pushinteger(L, time(NULL)); return 1; } static int lua_delay(lua_State *L) { delay((int)luaL_checknumber(L, 1)); return 0; } static int lua_getenv(lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); return 1; } static int lua_setenv(lua_State *L) { const char *key, *val; key = luaL_checkstring(L, 1); val = luaL_checkstring(L, 2); lua_pushinteger(L, setenv(key, val, 1)); return 1; } static int lua_unsetenv(lua_State *L) { const char *ev; ev = luaL_checkstring(L, 1); lua_pushinteger(L, unsetenv(ev)); return 1; } static int lua_printc(lua_State *L) { ssize_t cur, l; const char *s = luaL_checklstring(L, 1, &l); for (cur = 0; cur < l; ++cur) putchar((unsigned char)*(s++)); return 1; } static int lua_openfile(lua_State *L) { const char *mode, *str; int nargs; nargs = lua_gettop(L); if (nargs < 1 || nargs > 2) { lua_pushnil(L); return 1; } str = lua_tostring(L, 1); mode = "r"; if (nargs > 1) { mode = lua_tostring(L, 2); if (mode == NULL) { lua_pushnil(L); return 1; } } FILE * f = fopen(str, mode); if (f != NULL) { FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**)); *ptr = f; } else lua_pushnil(L); return 1; } static int lua_closefile(lua_State *L) { FILE ** f; if (lua_gettop(L) != 1) { lua_pushboolean(L, 0); return 1; } f = (FILE**)lua_touserdata(L, 1); if (f != NULL && *f != NULL) { lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0); *f = NULL; } else lua_pushboolean(L, 0); return 1; } static int lua_readfile(lua_State *L) { FILE **f; size_t size, r; char * buf; if (lua_gettop(L) < 1 || lua_gettop(L) > 2) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } if (lua_gettop(L) == 2) size = (size_t)lua_tonumber(L, 2); else size = (*f)->size; buf = (char*)malloc(size); r = fread(buf, 1, size, *f); lua_pushlstring(L, buf, r); free(buf); lua_pushinteger(L, r); return 2; } /* * Implements io.write(file, ...) * Any number of string and number arguments may be passed to it, * and it will return the number of bytes written, or nil, an error string, and * the errno. */ static int lua_writefile(lua_State *L) { FILE **f; const char *buf; int i, nargs; size_t bufsz, w, wrsz; buf = NULL; bufsz = 0; w = 0; wrsz = 0; nargs = lua_gettop(L); if (nargs < 2) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } /* Do a validation pass first */ for (i = 0; i < nargs - 1; i++) { /* * With Lua's API, lua_isstring really checks if the argument * is a string or a number. The latter will be implicitly * converted to a string by our later call to lua_tolstring. */ if (!lua_isstring(L, i + 2)) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } } for (i = 0; i < nargs - 1; i++) { /* We've already validated; there's no chance of failure */ buf = lua_tolstring(L, i + 2, &bufsz); wrsz = fwrite(buf, 1, bufsz, *f); if (wrsz < bufsz) return luaL_fileresult(L, 0, NULL); w += wrsz; } lua_pushinteger(L, w); 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), REG_SIMPLE(command_error), REG_SIMPLE(command), REG_SIMPLE(interpret), REG_SIMPLE(parse), REG_SIMPLE(getenv), REG_SIMPLE(perform), /* Also registered as the global 'printc' */ REG_SIMPLE(printc), 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 }, }; static const struct luaL_Reg iolib[] = { { "close", lua_closefile }, REG_SIMPLE(getchar), REG_SIMPLE(gets), REG_SIMPLE(ischar), { "open", lua_openfile }, { "read", lua_readfile }, { "write", lua_writefile }, { NULL, NULL }, }; #undef REG_SIMPLE int luaopen_loader(lua_State *L) { luaL_newlib(L, loaderlib); /* Add loader.machine and loader.machine_arch properties */ lua_pushstring(L, MACHINE); lua_setfield(L, -2, "machine"); lua_pushstring(L, MACHINE_ARCH); lua_setfield(L, -2, "machine_arch"); lua_pushstring(L, LUA_PATH); lua_setfield(L, -2, "lua_path"); /* Set global printc to loader.printc */ lua_register(L, "printc", lua_printc); return 1; } int luaopen_io(lua_State *L) { luaL_newlib(L, iolib); return 1; } diff --git a/stand/loader.mk b/stand/loader.mk index f6341052ab71..9ed8b65a2d06 100644 --- a/stand/loader.mk +++ b/stand/loader.mk @@ -1,181 +1,187 @@ # $FreeBSD$ .PATH: ${LDRSRC} ${BOOTSRC}/libsa CFLAGS+=-I${LDRSRC} SRCS+= boot.c commands.c console.c devopen.c interp.c 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+= pnglite.c +.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" SRCS+= load_elf32.c reloc_elf32.c .elif ${MACHINE_CPUARCH} == "powerpc" SRCS+= load_elf32.c reloc_elf32.c SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .elif ${MACHINE_ARCH:Mmips64*} != "" SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .elif ${MACHINE} == "mips" SRCS+= load_elf32.c reloc_elf32.c SRCS+= metadata.c .elif ${MACHINE_CPUARCH} == "riscv" SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .endif .if ${LOADER_DISK_SUPPORT:Uyes} == "yes" CFLAGS.part.c+= -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib SRCS+= disk.c part.c vdisk.c .endif .if ${LOADER_NET_SUPPORT:Uno} == "yes" SRCS+= dev_net.c .endif .if defined(HAVE_BCACHE) SRCS+= bcache.c .endif .if defined(MD_IMAGE_SIZE) CFLAGS+= -DMD_IMAGE_SIZE=${MD_IMAGE_SIZE} SRCS+= md.c .else CLEANFILES+= md.o .endif # Machine-independant ISA PnP .if defined(HAVE_ISABUS) SRCS+= isapnp.c .endif .if defined(HAVE_PNP) SRCS+= pnp.c .endif .if ${LOADER_INTERP} == "lua" SRCS+= interp_lua.c .include "${BOOTSRC}/lua.mk" LDR_INTERP= ${LIBLUA} LDR_INTERP32= ${LIBLUA32} CFLAGS.interp_lua.c= -DLUA_PATH=\"${LUAPATH}\" -I${FLUASRC}/modules .elif ${LOADER_INTERP} == "4th" SRCS+= interp_forth.c .include "${BOOTSRC}/ficl.mk" LDR_INTERP= ${LIBFICL} LDR_INTERP32= ${LIBFICL32} .elif ${LOADER_INTERP} == "simp" SRCS+= interp_simple.c .else .error Unknown interpreter ${LOADER_INTERP} .endif .if ${MK_LOADER_VERIEXEC} != "no" CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h .if ${MK_LOADER_VERIEXEC_VECTX} != "no" CFLAGS+= -DLOADER_VERIEXEC_VECTX .endif .endif .if ${MK_LOADER_VERIEXEC_PASS_MANIFEST} != "no" CFLAGS+= -DLOADER_VERIEXEC_PASS_MANIFEST -I${SRCTOP}/lib/libsecureboot/h .endif .if defined(BOOT_PROMPT_123) CFLAGS+= -DBOOT_PROMPT_123 .endif .if defined(LOADER_INSTALL_SUPPORT) SRCS+= install.c .endif # Filesystem support .if ${LOADER_CD9660_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_CD9660_SUPPORT .endif .if ${LOADER_EXT2FS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_EXT2FS_SUPPORT .endif .if ${LOADER_MSDOS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_MSDOS_SUPPORT .endif .if ${LOADER_UFS_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_UFS_SUPPORT .endif # Compression .if ${LOADER_GZIP_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_GZIP_SUPPORT .endif .if ${LOADER_BZIP2_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_BZIP2_SUPPORT .endif # Network related things .if ${LOADER_NET_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_NET_SUPPORT .endif .if ${LOADER_NFS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_NFS_SUPPORT .endif .if ${LOADER_TFTP_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_TFTP_SUPPORT .endif # Partition support .if ${LOADER_GPT_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_GPT_SUPPORT .endif .if ${LOADER_MBR_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_MBR_SUPPORT .endif .if ${HAVE_ZFS:Uno} == "yes" CFLAGS+= -DLOADER_ZFS_SUPPORT CFLAGS+= -I${ZFSSRC} CFLAGS+= -I${SYSDIR}/cddl/boot/zfs CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common SRCS+= zfs_cmd.c .endif LIBFICL= ${BOOTOBJ}/ficl/libficl.a .if ${MACHINE} == "i386" LIBFICL32= ${LIBFICL} .else LIBFICL32= ${BOOTOBJ}/ficl32/libficl.a .endif LIBLUA= ${BOOTOBJ}/liblua/liblua.a .if ${MACHINE} == "i386" LIBLUA32= ${LIBLUA} .else LIBLUA32= ${BOOTOBJ}/liblua32/liblua.a .endif CLEANFILES+= vers.c VERSION_FILE?= ${.CURDIR}/version .if ${MK_REPRODUCIBLE_BUILD} != no REPRO_FLAG= -r .endif vers.c: ${LDRSRC}/newvers.sh ${VERSION_FILE} sh ${LDRSRC}/newvers.sh ${REPRO_FLAG} ${VERSION_FILE} \ ${NEWVERSWHAT} .if ${MK_LOADER_VERBOSE} != "no" CFLAGS+= -DELF_VERBOSE .endif .if !empty(HELP_FILES) HELP_FILES+= ${LDRSRC}/help.common CLEANFILES+= loader.help FILES+= loader.help loader.help: ${HELP_FILES} cat ${HELP_FILES} | awk -f ${LDRSRC}/merge_help.awk > ${.TARGET} .endif diff --git a/stand/lua/color.lua b/stand/lua/color.lua index 364d548ca3dc..4dc16cacb97e 100644 --- a/stand/lua/color.lua +++ b/stand/lua/color.lua @@ -1,122 +1,122 @@ -- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> -- 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 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$ -- local core = require("core") local hook = require("hook") local color = {} local function recalcDisabled() color.disabled = not color.isEnabled() end -- Module exports color.BLACK = 0 color.RED = 1 color.GREEN = 2 color.YELLOW = 3 color.BLUE = 4 color.MAGENTA = 5 color.CYAN = 6 color.WHITE = 7 color.DEFAULT = 9 color.BRIGHT = 1 color.DIM = 2 function color.isEnabled() local c = loader.getenv("loader_color") if c ~= nil then return c:lower() ~= "no" and c ~= "0" end - return not core.isSerialBoot() + return true end function color.escapefg(color_value) if color.disabled then return '' end return core.KEYSTR_CSI .. "3" .. color_value .. "m" end function color.resetfg() if color.disabled then return '' end return color.escapefg(color.DEFAULT) end function color.escapebg(color_value) if color.disabled then return '' end return core.KEYSTR_CSI .. "4" .. color_value .. "m" end function color.resetbg() if color.disabled then return '' end return color.escapebg(color.DEFAULT) end function color.escape(fg_color, bg_color, attribute) if color.disabled then return "" end if attribute == nil then attribute = "" else attribute = attribute .. ";" end return core.KEYSTR_CSI .. attribute .. "3" .. fg_color .. ";4" .. bg_color .. "m" end function color.default() if color.disabled then return "" end return color.escape(color.DEFAULT, color.DEFAULT) end function color.highlight(str) if color.disabled then return str end -- We need to reset attributes as well as color scheme here, just in -- case the terminal defaults don't match what we're expecting. return core.KEYSTR_CSI .. "1m" .. str .. core.KEYSTR_CSI .. "22m" end recalcDisabled() hook.register("config.loaded", recalcDisabled) return color diff --git a/stand/lua/core.lua b/stand/lua/core.lua index 441a032dc51a..9d331bc0ad3a 100644 --- a/stand/lua/core.lua +++ b/stand/lua/core.lua @@ -1,490 +1,503 @@ -- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> -- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> -- 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 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$ -- local config = require("config") local hook = require("hook") local core = {} local default_safe_mode = false local default_single_user = false local default_verbose = false local bootenv_list = "bootenvs" local function composeLoaderCmd(cmd_name, argstr) if argstr ~= nil then cmd_name = cmd_name .. " " .. argstr end return cmd_name end local function recordDefaults() -- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, -- it will generally be set upon execution of the kernel. Because of -- this, we can't (or don't really want to) detect/disable ACPI on !i386 -- reliably. Just set it enabled if we detect it and leave well enough -- alone if we don't. local boot_acpi = core.isSystem386() and core.getACPIPresent(false) local boot_single = loader.getenv("boot_single") or "no" local boot_verbose = loader.getenv("boot_verbose") or "no" default_single_user = boot_single:lower() ~= "no" default_verbose = boot_verbose:lower() ~= "no" if boot_acpi then core.setACPI(true) end core.setSingleUser(default_single_user) core.setVerbose(default_verbose) end -- Globals -- try_include will return the loaded module on success, or false and the error -- message on failure. function try_include(module) if module:sub(1, 1) ~= "/" then local lua_path = loader.lua_path -- XXX Temporary compat shim; this should be removed once the -- loader.lua_path export has sufficiently spread. if lua_path == nil then lua_path = "/boot/lua" end module = lua_path .. "/" .. module -- We only attempt to append an extension if an absolute path -- wasn't specified. This assumes that the caller either wants -- to treat this like it would require() and specify just the -- base filename, or they know what they're doing as they've -- specified an absolute path and we shouldn't impede. if module:match(".lua$") == nil then module = module .. ".lua" end end if lfs.attributes(module, "mode") ~= "file" then return end return dofile(module) end -- Module exports -- Commonly appearing constants core.KEY_BACKSPACE = 8 core.KEY_ENTER = 13 core.KEY_DELETE = 127 -- Note that this is a decimal representation, despite the leading 0 that in -- other contexts (outside of Lua) may mean 'octal' core.KEYSTR_ESCAPE = "\027" core.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" core.KEYSTR_RESET = core.KEYSTR_ESCAPE .. "c" core.MENU_RETURN = "return" core.MENU_ENTRY = "entry" core.MENU_SEPARATOR = "separator" core.MENU_SUBMENU = "submenu" core.MENU_CAROUSEL_ENTRY = "carousel_entry" function core.setVerbose(verbose) if verbose == nil then verbose = not core.verbose end if verbose then loader.setenv("boot_verbose", "YES") else loader.unsetenv("boot_verbose") end core.verbose = verbose end function core.setSingleUser(single_user) if single_user == nil then single_user = not core.su end if single_user then loader.setenv("boot_single", "YES") else loader.unsetenv("boot_single") end core.su = single_user end function core.getACPIPresent(checking_system_defaults) local c = loader.getenv("hint.acpi.0.rsdp") if c ~= nil then if checking_system_defaults then return true end -- Otherwise, respect disabled if it's set c = loader.getenv("hint.acpi.0.disabled") return c == nil or tonumber(c) ~= 1 end return false end function core.setACPI(acpi) if acpi == nil then acpi = not core.acpi end if acpi then loader.setenv("acpi_load", "YES") loader.setenv("hint.acpi.0.disabled", "0") loader.unsetenv("loader.acpi_disabled_by_user") else loader.unsetenv("acpi_load") loader.setenv("hint.acpi.0.disabled", "1") loader.setenv("loader.acpi_disabled_by_user", "1") end core.acpi = acpi end function core.setSafeMode(safe_mode) if safe_mode == nil then safe_mode = not core.sm end if safe_mode then loader.setenv("kern.smp.disabled", "1") loader.setenv("hw.ata.ata_dma", "0") loader.setenv("hw.ata.atapi_dma", "0") loader.setenv("hw.ata.wc", "0") loader.setenv("hw.eisa_slots", "0") loader.setenv("kern.eventtimer.periodic", "1") loader.setenv("kern.geom.part.check_integrity", "0") else loader.unsetenv("kern.smp.disabled") loader.unsetenv("hw.ata.ata_dma") loader.unsetenv("hw.ata.atapi_dma") loader.unsetenv("hw.ata.wc") loader.unsetenv("hw.eisa_slots") loader.unsetenv("kern.eventtimer.periodic") loader.unsetenv("kern.geom.part.check_integrity") end core.sm = safe_mode end function core.clearCachedKernels() -- Clear the kernel cache on config changes, autodetect might have -- changed or if we've switched boot environments then we could have -- a new kernel set. core.cached_kernels = nil end function core.kernelList() if core.cached_kernels ~= nil then return core.cached_kernels end local k = loader.getenv("kernel") local v = loader.getenv("kernels") local autodetect = loader.getenv("kernels_autodetect") or "" local kernels = {} local unique = {} local i = 0 if k ~= nil then i = i + 1 kernels[i] = k unique[k] = true end if v ~= nil then for n in v:gmatch("([^;, ]+)[;, ]?") do if unique[n] == nil then i = i + 1 kernels[i] = n unique[n] = true end end end -- Base whether we autodetect kernels or not on a loader.conf(5) -- setting, kernels_autodetect. If it's set to 'yes', we'll add -- any kernels we detect based on the criteria described. if autodetect:lower() ~= "yes" then core.cached_kernels = kernels return core.cached_kernels end -- Automatically detect other bootable kernel directories using a -- heuristic. Any directory in /boot that contains an ordinary file -- named "kernel" is considered eligible. for file in lfs.dir("/boot") do local fname = "/boot/" .. file if file == "." or file == ".." then goto continue end if lfs.attributes(fname, "mode") ~= "directory" then goto continue end if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then goto continue end if unique[file] == nil then i = i + 1 kernels[i] = file unique[file] = true end ::continue:: end core.cached_kernels = kernels return core.cached_kernels end function core.bootenvDefault() return loader.getenv("zfs_be_active") end function core.bootenvList() local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count")) local bootenvs = {} local curenv local envcount = 0 local unique = {} if bootenv_count == nil or bootenv_count <= 0 then return bootenvs end -- Currently selected bootenv is always first/default -- On the rewinded list the bootenv may not exists if core.isRewinded() then curenv = core.bootenvDefaultRewinded() else curenv = core.bootenvDefault() end if curenv ~= nil then envcount = envcount + 1 bootenvs[envcount] = curenv unique[curenv] = true end for curenv_idx = 0, bootenv_count - 1 do curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]") if curenv ~= nil and unique[curenv] == nil then envcount = envcount + 1 bootenvs[envcount] = curenv unique[curenv] = true end end return bootenvs end function core.isCheckpointed() return loader.getenv("zpool_checkpoint") ~= nil end function core.bootenvDefaultRewinded() local defname = "zfs:!" .. string.sub(core.bootenvDefault(), 5) local bootenv_count = tonumber("bootenvs_check_count") if bootenv_count == nil or bootenv_count <= 0 then return defname end for curenv_idx = 0, bootenv_count - 1 do local curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]") if curenv == defname then return defname end end return loader.getenv("bootenvs_check[0]") end function core.isRewinded() return bootenv_list == "bootenvs_check" end function core.changeRewindCheckpoint() if core.isRewinded() then bootenv_list = "bootenvs" else bootenv_list = "bootenvs_check" end end function core.setDefaults() core.setACPI(core.getACPIPresent(true)) core.setSafeMode(default_safe_mode) core.setSingleUser(default_single_user) core.setVerbose(default_verbose) end function core.autoboot(argstr) -- loadelf() only if we've not already loaded a kernel if loader.getenv("kernelname") == nil then config.loadelf() end loader.perform(composeLoaderCmd("autoboot", argstr)) end function core.boot(argstr) -- loadelf() only if we've not already loaded a kernel if loader.getenv("kernelname") == nil then config.loadelf() end loader.perform(composeLoaderCmd("boot", argstr)) end function core.isSingleUserBoot() local single_user = loader.getenv("boot_single") return single_user ~= nil and single_user:lower() == "yes" end function core.isUEFIBoot() local efiver = loader.getenv("efi-version") return efiver ~= nil end function core.isZFSBoot() local c = loader.getenv("currdev") if c ~= nil then return c:match("^zfs:") ~= nil end 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.depth") ~= nil then + return true + end + end + return false +end + function core.isSerialConsole() local c = loader.getenv("console") if c ~= nil then if c:find("comconsole") ~= nil then return true end end return false end function core.isSerialBoot() local s = loader.getenv("boot_serial") if s ~= nil then return true end local m = loader.getenv("boot_multicons") if m ~= nil then return true end return false end function core.isSystem386() return loader.machine_arch == "i386" end -- Is the menu skipped in the environment in which we've booted? function core.isMenuSkipped() return string.lower(loader.getenv("beastie_disable") or "") == "yes" end -- This may be a better candidate for a 'utility' module. function core.deepCopyTable(tbl) local new_tbl = {} for k, v in pairs(tbl) do if type(v) == "table" then new_tbl[k] = core.deepCopyTable(v) else new_tbl[k] = v end end return new_tbl end -- XXX This should go away if we get the table lib into shape for importing. -- As of now, it requires some 'os' functions, so we'll implement this in lua -- for our uses function core.popFrontTable(tbl) -- Shouldn't reasonably happen if #tbl == 0 then return nil, nil elseif #tbl == 1 then return tbl[1], {} end local first_value = tbl[1] local new_tbl = {} -- This is not a cheap operation for k, v in ipairs(tbl) do if k > 1 then new_tbl[k - 1] = v end end return first_value, new_tbl end function core.getConsoleName() if loader.getenv("boot_multicons") ~= nil then if loader.getenv("boot_serial") ~= nil then return "Dual (Serial primary)" else return "Dual (Video primary)" end else if loader.getenv("boot_serial") ~= nil then return "Serial" else return "Video" end end end function core.nextConsoleChoice() if loader.getenv("boot_multicons") ~= nil then if loader.getenv("boot_serial") ~= nil then loader.unsetenv("boot_serial") else loader.unsetenv("boot_multicons") loader.setenv("boot_serial", "YES") end else if loader.getenv("boot_serial") ~= nil then loader.unsetenv("boot_serial") else loader.setenv("boot_multicons", "YES") loader.setenv("boot_serial", "YES") end end end recordDefaults() hook.register("config.reloaded", core.clearCachedKernels) return core diff --git a/stand/lua/drawer.lua b/stand/lua/drawer.lua index 32483f1b424e..3ace3884ff8a 100644 --- a/stand/lua/drawer.lua +++ b/stand/lua/drawer.lua @@ -1,467 +1,518 @@ -- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> -- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> -- 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 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$ -- local color = require("color") local config = require("config") local core = require("core") local screen = require("screen") local drawer = {} local fbsd_brand local none local menu_name_handlers local branddefs local logodefs local brand_position local logo_position local menu_position local frame_size local default_shift local shift local function menuEntryName(drawing_menu, entry) local name_handler = menu_name_handlers[entry.entry_type] if name_handler ~= nil then return name_handler(drawing_menu, entry) end if type(entry.name) == "function" then return entry.name() end return entry.name end local function processFile(gfxname) if gfxname == nil then return false, "Missing filename" end local ret = try_include('gfx-' .. gfxname) if ret == nil then return false, "Failed to include gfx-" .. gfxname end -- Legacy format if type(ret) ~= "table" then return true end for gfxtype, def in pairs(ret) do if gfxtype == "brand" then drawer.addBrand(gfxname, def) elseif gfxtype == "logo" then drawer.addLogo(gfxname, def) else return false, "Unknown graphics type '" .. gfxtype .. "'" end end return true end local function getBranddef(brand) if brand == nil then return nil end -- Look it up local branddef = branddefs[brand] -- Try to pull it in if branddef == nil then local res, err = processFile(brand) if not res then -- This fallback should go away after FreeBSD 13. try_include('brand-' .. brand) -- If the fallback also failed, print whatever error -- we encountered in the original processing. if branddefs[brand] == nil then print(err) return nil end end branddef = branddefs[brand] end return branddef end local function getLogodef(logo) if logo == nil then return nil end -- Look it up local logodef = logodefs[logo] -- Try to pull it in if logodef == nil then local res, err = processFile(logo) if not res then -- This fallback should go away after FreeBSD 13. try_include('logo-' .. logo) -- If the fallback also failed, print whatever error -- we encountered in the original processing. if logodefs[logo] == nil then print(err) return nil end end logodef = logodefs[logo] end return logodef end local function draw(x, y, logo) for i = 1, #logo do screen.setcursor(x, y + i - 1) printc(logo[i]) end end local function drawmenu(menudef) local x = menu_position.x local y = menu_position.y x = x + shift.x y = y + shift.y -- print the menu and build the alias table local alias_table = {} local entry_num = 0 local menu_entries = menudef.entries local effective_line_num = 0 if type(menu_entries) == "function" then menu_entries = menu_entries() end for _, e in ipairs(menu_entries) do -- Allow menu items to be conditionally visible by specifying -- a visible function. if e.visible ~= nil and not e.visible() then goto continue end effective_line_num = effective_line_num + 1 if e.entry_type ~= core.MENU_SEPARATOR then entry_num = entry_num + 1 screen.setcursor(x, y + effective_line_num) printc(entry_num .. ". " .. menuEntryName(menudef, e)) -- fill the alias table alias_table[tostring(entry_num)] = e if e.alias ~= nil then for _, a in ipairs(e.alias) do alias_table[a] = e end end else screen.setcursor(x, y + effective_line_num) printc(menuEntryName(menudef, e)) end ::continue:: end return alias_table end local function defaultframe() if core.isSerialConsole() then return "ascii" end 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 local h = frame_size.h local framestyle = loader.getenv("loader_menu_frame") or defaultframe() local framespec = drawer.frame_styles[framestyle] -- 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 local vl = framespec.vertical local tl = framespec.top_left local bl = framespec.bottom_left local tr = framespec.top_right local br = framespec.bottom_right 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) screen.setcursor(x + w, y + h); printc(br) screen.setcursor(x + 1, y) for _ = 1, w - 1 do printc(hl) end screen.setcursor(x + 1, y + h) for _ = 1, w - 1 do printc(hl) end for i = 1, h - 1 do screen.setcursor(x, y + i) printc(vl) 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 -- Just inside the left border on top menu_header_x = x + 1 elseif menu_header_align == "right" then -- Just inside the right border on top menu_header_x = x + w - #menu_header end end if menu_header_x == nil then menu_header_x = x + (w / 2) - (#menu_header / 2) end screen.setcursor(menu_header_x, y) printc(menu_header) end local function drawbrand() local x = tonumber(loader.getenv("loader_brand_x")) or brand_position.x local y = tonumber(loader.getenv("loader_brand_y")) or brand_position.y local branddef = getBranddef(loader.getenv("loader_brand")) if branddef == nil then branddef = getBranddef(drawer.default_brand) end local graphic = branddef.graphic 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 local function drawlogo() local x = tonumber(loader.getenv("loader_logo_x")) or logo_position.x local y = tonumber(loader.getenv("loader_logo_y")) or logo_position.y local logo = loader.getenv("loader_logo") local colored = color.isEnabled() local logodef = getLogodef(logo) if logodef == nil or logodef.graphic == nil or (not colored and logodef.requires_color) then -- Choose a sensible default if colored then logodef = getLogodef(drawer.default_color_logodef) else logodef = getLogodef(drawer.default_bw_logodef) end -- Something has gone terribly wrong. if logodef == nil then logodef = getLogodef(drawer.default_fallback_logodef) end end if logodef ~= nil and logodef.graphic == none then shift = logodef.shift else shift = default_shift end x = x + shift.x y = y + shift.y if logodef ~= nil and logodef.shift ~= nil then x = x + logodef.shift.x 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 = { " ______ ____ _____ _____ ", " | ____| | _ \\ / ____| __ \\ ", " | |___ _ __ ___ ___ | |_) | (___ | | | |", " | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", " | | | | | __/ __/| |_) |____) | |__| |", " | | | | | | || | | |", " |_| |_| \\___|\\___||____/|_____/|_____/ " } none = {""} menu_name_handlers = { -- Menu name handlers should take the menu being drawn and entry being -- drawn as parameters, and return the name of the item. -- This is designed so that everything, including menu separators, may -- have their names derived differently. The default action for entry -- types not specified here is to use entry.name directly. [core.MENU_SEPARATOR] = function(_, entry) if entry.name ~= nil then if type(entry.name) == "function" then return entry.name() end return entry.name end return "" end, [core.MENU_CAROUSEL_ENTRY] = function(_, entry) local carid = entry.carousel_id local caridx = config.getCarouselIndex(carid) local choices = entry.items if type(choices) == "function" then choices = choices() end if #choices < caridx then caridx = 1 end return entry.name(caridx, choices[caridx], choices) end, } branddefs = { -- Indexed by valid values for loader_brand in loader.conf(5). Valid -- keys are: graphic (table depicting graphic) ["fbsd"] = { graphic = fbsd_brand, + image = "/boot/images/freebsd-brand-rev.png", }, ["none"] = { graphic = none, }, } logodefs = { -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys -- are: requires_color (boolean), graphic (table depicting graphic), and -- shift (table containing x and y). ["tribute"] = { graphic = fbsd_brand, }, ["tributebw"] = { graphic = fbsd_brand, }, ["none"] = { graphic = none, shift = {x = 17, y = 0}, }, } brand_position = {x = 2, y = 1} logo_position = {x = 46, y = 4} menu_position = {x = 5, y = 10} frame_size = {w = 42, h = 13} default_shift = {x = 0, y = 0} shift = default_shift -- Module exports drawer.default_brand = 'fbsd' drawer.default_color_logodef = 'orb' drawer.default_bw_logodef = 'orbbw' -- For when things go terribly wrong; this def should be present here in the -- drawer module in case it's a filesystem issue. drawer.default_fallback_logodef = 'none' -- These should go away after FreeBSD 13; only available for backwards -- compatibility with old logo- files. function drawer.addBrand(name, def) branddefs[name] = def end function drawer.addLogo(name, def) logodefs[name] = def end drawer.frame_styles = { -- Indexed by valid values for loader_menu_frame in loader.conf(5). -- All of the keys appearing below must be set for any menu frame style -- added to drawer.frame_styles. ["ascii"] = { horizontal = "-", vertical = "|", top_left = "+", bottom_left = "+", top_right = "+", bottom_right = "+", }, ["single"] = { horizontal = "\xE2\x94\x80", vertical = "\xE2\x94\x82", top_left = "\xE2\x94\x8C", bottom_left = "\xE2\x94\x94", top_right = "\xE2\x94\x90", bottom_right = "\xE2\x94\x98", }, ["double"] = { horizontal = "\xE2\x95\x90", vertical = "\xE2\x95\x91", top_left = "\xE2\x95\x94", bottom_left = "\xE2\x95\x9A", top_right = "\xE2\x95\x97", bottom_right = "\xE2\x95\x9D", }, } 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 return drawer diff --git a/stand/lua/gfx-orb.lua b/stand/lua/gfx-orb.lua index 1af8f9f96072..2702618948d6 100644 --- a/stand/lua/gfx-orb.lua +++ b/stand/lua/gfx-orb.lua @@ -1,52 +1,54 @@ -- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- 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$ -- return { logo = { graphic = { " \027[31m``` \027[31;1m`\027[31m", " s` `.....---...\027[31;1m....--.``` -/\027[31m", " +o .--` \027[31;1m/y:` +.\027[31m", " yo`:. \027[31;1m:o `+-\027[31m", " y/ \027[31;1m-/` -o/\027[31m", " .- \027[31;1m::/sy+:.\027[31m", " / \027[31;1m`-- /\027[31m", " `: \027[31;1m:`\027[31m", " `: \027[31;1m:`\027[31m", " / \027[31;1m/\027[31m", " .- \027[31;1m-.\027[31m", " -- \027[31;1m-.\027[31m", " `:` \027[31;1m`:`", " \027[31;1m.-- `--.", " .---.....----.\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 } } diff --git a/stand/userboot/userboot/Makefile b/stand/userboot/userboot/Makefile index f256ac2769e5..49026375328e 100644 --- a/stand/userboot/userboot/Makefile +++ b/stand/userboot/userboot/Makefile @@ -1,60 +1,61 @@ # $FreeBSD$ LOADER_MSDOS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no PIC=yes .include <bsd.init.mk> SHLIB_NAME= userboot_${LOADER_INTERP}.so STRIP= LIBDIR= /boot .PATH: ${.CURDIR}/../userboot SRCS= autoload.c SRCS+= bcache.c SRCS+= biossmap.c SRCS+= bootinfo.c SRCS+= bootinfo32.c SRCS+= bootinfo64.c SRCS+= conf.c SRCS+= copy.c SRCS+= devicename.c SRCS+= elf32_freebsd.c SRCS+= elf64_freebsd.c SRCS+= host.c SRCS+= main.c SRCS+= userboot_cons.c SRCS+= userboot_disk.c SRCS+= vers.c CFLAGS+= -Wall CFLAGS+= -I${BOOTSRC}/userboot CFLAGS.main.c+= -I${BOOTSRC}/libsa/zfs CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs +CFLAGS.userboot_cons.c+= -I$(SRCTOP)/sys/teken -I${SRCTOP}/contrib/pnglite CWARNFLAGS.main.c += -Wno-implicit-function-declaration LDFLAGS+= -nostdlib -Wl,-Bsymbolic NEWVERSWHAT= "User boot ${LOADER_INTERP}" ${MACHINE_CPUARCH} VERSION_FILE= ${.CURDIR}/../userboot/version .if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} LINKS+= ${BINDIR}/${SHLIB_NAME} ${BINDIR}/userboot.so .endif .if ${MK_LOADER_ZFS} != "no" CFLAGS+= -DUSERBOOT_ZFS_SUPPORT HAVE_ZFS=yes .endif # Always add MI sources .include "${BOOTSRC}/loader.mk" CFLAGS+= -I. DPADD+= ${LDR_INTERP} ${LIBSA} LDADD+= ${LDR_INTERP} ${LIBSA} .include <bsd.lib.mk> diff --git a/stand/userboot/userboot/userboot_cons.c b/stand/userboot/userboot/userboot_cons.c index 6f73ad5633bb..528f9ac40af9 100644 --- a/stand/userboot/userboot/userboot_cons.c +++ b/stand/userboot/userboot/userboot_cons.c @@ -1,130 +1,135 @@ /*- * Copyright (c) 2011 Google, Inc. * 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 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. */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <stand.h> +#include <sys/font.h> +#include "gfx_fb.h" #include "bootstrap.h" #include "libuserboot.h" +font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts); +teken_gfx_t gfx_state = { 0 }; + int console; static struct console *userboot_comconsp; static void userboot_cons_probe(struct console *cp); static int userboot_cons_init(int); static void userboot_comcons_probe(struct console *cp); static int userboot_comcons_init(int); static void userboot_cons_putchar(int); static int userboot_cons_getchar(void); static int userboot_cons_poll(void); struct console userboot_console = { "userboot", "userboot", 0, userboot_cons_probe, userboot_cons_init, userboot_cons_putchar, userboot_cons_getchar, userboot_cons_poll, }; /* * Provide a simple alias to allow loader scripts to set the * console to comconsole without resulting in an error */ struct console userboot_comconsole = { "comconsole", "comconsole", 0, userboot_comcons_probe, userboot_comcons_init, userboot_cons_putchar, userboot_cons_getchar, userboot_cons_poll, }; static void userboot_cons_probe(struct console *cp) { cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); } static int userboot_cons_init(int arg) { return (0); } static void userboot_comcons_probe(struct console *cp) { /* * Save the console pointer so the comcons_init routine * can set the C_PRESENT* flags. They are not set * here to allow the existing userboot console to * be elected the default. */ userboot_comconsp = cp; } static int userboot_comcons_init(int arg) { /* * Set the C_PRESENT* flags to allow the comconsole * to be selected as the active console */ userboot_comconsp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); return (0); } static void userboot_cons_putchar(int c) { CALLBACK(putc, c); } static int userboot_cons_getchar() { return (CALLBACK(getc)); } static int userboot_cons_poll() { return (CALLBACK(poll)); } diff --git a/sys/sys/font.h b/sys/sys/font.h index f8bf151c5f52..e09b2112959d 100644 --- a/sys/sys/font.h +++ b/sys/sys/font.h @@ -1,122 +1,123 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Ed Schouten under sponsorship from the * FreeBSD Foundation. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * 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 _SYS_FONT_H_ #define _SYS_FONT_H_ #include <sys/queue.h> /* * Fonts. * * Fonts support normal and bold weights, and single and double width glyphs. * Mapping tables are used to map Unicode points to glyphs. They are sorted by * code point, and vtfont_lookup() uses this to perform a binary search. Each * font has four mapping tables: two weights times two halves (left/single, * right). When a character is not present in a bold map the glyph from the * normal map is used. When no glyph is available, it uses glyph 0, which is * normally equal to U+FFFD. */ enum vfnt_map_type { VFNT_MAP_NORMAL = 0, /* Normal font. */ VFNT_MAP_NORMAL_RIGHT, /* Normal font right hand. */ VFNT_MAP_BOLD, /* Bold font. */ VFNT_MAP_BOLD_RIGHT, /* Bold font right hand. */ VFNT_MAPS /* Number of maps. */ }; struct font_info { int32_t fi_checksum; uint32_t fi_width; uint32_t fi_height; uint32_t fi_bitmap_size; uint32_t fi_map_count[VFNT_MAPS]; }; struct vfnt_map { uint32_t vfm_src; uint16_t vfm_dst; uint16_t vfm_len; } __packed; typedef struct vfnt_map vfnt_map_t; struct vt_font { vfnt_map_t *vf_map[VFNT_MAPS]; uint8_t *vf_bytes; uint32_t vf_height; uint32_t vf_width; uint32_t vf_map_count[VFNT_MAPS]; uint32_t vf_refcount; }; typedef struct vt_font_bitmap_data { uint32_t vfbd_width; uint32_t vfbd_height; uint32_t vfbd_compressed_size; uint32_t vfbd_uncompressed_size; uint8_t *vfbd_compressed_data; struct vt_font *vfbd_font; } 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 { char *font_name; FONT_FLAGS font_flags; vt_font_bitmap_data_t *font_data; vt_font_bitmap_data_t *(*font_load)(char *); STAILQ_ENTRY(fontlist) font_next; }; #define BORDER_PIXELS 10 /* space from screen border */ typedef STAILQ_HEAD(font_list, fontlist) font_list_t; #define FONT_HEADER_MAGIC "VFNT0002" struct font_header { uint8_t fh_magic[8]; uint8_t fh_width; uint8_t fh_height; uint16_t fh_pad; uint32_t fh_glyph_count; uint32_t fh_map_count[VFNT_MAPS]; } __packed; #endif /* !_SYS_FONT_H_ */ diff --git a/sys/teken/teken.h b/sys/teken/teken.h index 0a3928a9ffd1..994298b0dbc6 100644 --- a/sys/teken/teken.h +++ b/sys/teken/teken.h @@ -1,221 +1,222 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org> * 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 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 _TEKEN_H_ #define _TEKEN_H_ #include <sys/types.h> /* * libteken: terminal emulation library. * * This library converts an UTF-8 stream of bytes to terminal drawing * commands. */ typedef uint32_t teken_char_t; typedef unsigned short teken_unit_t; typedef unsigned char teken_format_t; #define TF_BOLD 0x01 /* Bold character. */ #define TF_UNDERLINE 0x02 /* Underline character. */ #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 #define TC_GREEN 2 #define TC_BROWN 3 #define TC_BLUE 4 #define TC_MAGENTA 5 #define TC_CYAN 6 #define TC_WHITE 7 #define TC_NCOLORS 8 #define TC_LIGHT 8 /* ORed with the others. */ typedef struct { teken_unit_t tp_row; teken_unit_t tp_col; } teken_pos_t; typedef struct { teken_pos_t tr_begin; teken_pos_t tr_end; } teken_rect_t; typedef struct { teken_format_t ta_format; teken_color_t ta_fgcolor; teken_color_t ta_bgcolor; } teken_attr_t; typedef struct { teken_unit_t ts_begin; teken_unit_t ts_end; } teken_span_t; typedef struct __teken teken_t; typedef void teken_state_t(teken_t *, teken_char_t); /* * Drawing routines supplied by the user. */ typedef void tf_bell_t(void *); typedef void tf_cursor_t(void *, const teken_pos_t *); typedef void tf_putchar_t(void *, const teken_pos_t *, teken_char_t, const teken_attr_t *); typedef void tf_fill_t(void *, const teken_rect_t *, teken_char_t, const teken_attr_t *); typedef void tf_copy_t(void *, const teken_rect_t *, const teken_pos_t *); typedef void tf_pre_input_t(void *); typedef void tf_post_input_t(void *); typedef void tf_param_t(void *, int, unsigned int); #define TP_SHOWCURSOR 0 #define TP_KEYPADAPP 1 #define TP_AUTOREPEAT 2 #define TP_SWITCHVT 3 #define TP_132COLS 4 #define TP_SETBELLPD 5 #define TP_SETBELLPD_PITCH(pd) ((pd) >> 16) #define TP_SETBELLPD_DURATION(pd) ((pd) & 0xffff) #define TP_MOUSE 6 #define TP_SETBORDER 7 #define TP_SETLOCALCURSOR 8 #define TP_SETGLOBALCURSOR 9 typedef void tf_respond_t(void *, const void *, size_t); typedef struct { tf_bell_t *tf_bell; tf_cursor_t *tf_cursor; tf_putchar_t *tf_putchar; tf_fill_t *tf_fill; tf_copy_t *tf_copy; tf_pre_input_t *tf_pre_input; tf_post_input_t *tf_post_input; tf_param_t *tf_param; tf_respond_t *tf_respond; } teken_funcs_t; typedef teken_char_t teken_scs_t(const teken_t *, teken_char_t); /* * Terminal state. */ struct __teken { const teken_funcs_t *t_funcs; void *t_softc; teken_state_t *t_nextstate; unsigned int t_stateflags; #define T_NUMSIZE 8 unsigned int t_nums[T_NUMSIZE]; unsigned int t_curnum; teken_pos_t t_cursor; teken_attr_t t_curattr; teken_pos_t t_saved_cursor; teken_attr_t t_saved_curattr; teken_attr_t t_defattr; teken_pos_t t_winsize; /* For DECSTBM. */ teken_span_t t_scrollreg; /* For DECOM. */ teken_span_t t_originreg; #define T_NUMCOL 160 unsigned int t_tabstops[T_NUMCOL / (sizeof(unsigned int) * 8)]; unsigned int t_utf8_left; teken_char_t t_utf8_partial; teken_char_t t_last; unsigned int t_curscs; teken_scs_t *t_saved_curscs; teken_scs_t *t_scs[2]; }; /* Initialize teken structure. */ void teken_init(teken_t *, const teken_funcs_t *, void *); /* Deliver character input. */ void teken_input(teken_t *, const void *, size_t); /* Get/set teken attributes. */ const teken_pos_t *teken_get_cursor(const teken_t *); const teken_attr_t *teken_get_curattr(const teken_t *); const teken_attr_t *teken_get_defattr(const teken_t *); void teken_get_defattr_cons25(const teken_t *, int *, int *); const teken_pos_t *teken_get_winsize(const teken_t *); void teken_set_cursor(teken_t *, const teken_pos_t *); void teken_set_curattr(teken_t *, const teken_attr_t *); void teken_set_defattr(teken_t *, const teken_attr_t *); void teken_set_winsize(teken_t *, const teken_pos_t *); void teken_set_winsize_noreset(teken_t *, const teken_pos_t *); /* Key input escape sequences. */ #define TKEY_UP 0x00 #define TKEY_DOWN 0x01 #define TKEY_LEFT 0x02 #define TKEY_RIGHT 0x03 #define TKEY_HOME 0x04 #define TKEY_END 0x05 #define TKEY_INSERT 0x06 #define TKEY_DELETE 0x07 #define TKEY_PAGE_UP 0x08 #define TKEY_PAGE_DOWN 0x09 #define TKEY_F1 0x0a #define TKEY_F2 0x0b #define TKEY_F3 0x0c #define TKEY_F4 0x0d #define TKEY_F5 0x0e #define TKEY_F6 0x0f #define TKEY_F7 0x10 #define TKEY_F8 0x11 #define TKEY_F9 0x12 #define TKEY_F10 0x13 #define TKEY_F11 0x14 #define TKEY_F12 0x15 const char *teken_get_sequence(const teken_t *, unsigned int); /* Legacy features. */ void teken_set_8bit(teken_t *); void teken_set_cons25(teken_t *); void teken_set_cons25keys(teken_t *); /* Color conversion. */ teken_color_t teken_256to16(teken_color_t); teken_color_t teken_256to8(teken_color_t); #endif /* !_TEKEN_H_ */