Index: head/stand/defs.mk =================================================================== --- head/stand/defs.mk (revision 351899) +++ head/stand/defs.mk (revision 351900) @@ -1,221 +1,228 @@ # $FreeBSD$ .if !defined(__BOOT_DEFS_MK__) __BOOT_DEFS_MK__=${MFILE} # We need to define all the MK_ options before including src.opts.mk # because it includes bsd.own.mk which needs the right MK_ values, # espeically MK_CTF. MK_CTF= no MK_SSP= no MK_PROFILE= no MAN= .if !defined(PIC) NO_PIC= INTERNALLIB= .endif .include WARNS?= 1 BOOTSRC= ${SRCTOP}/stand EFISRC= ${BOOTSRC}/efi EFIINC= ${EFISRC}/include EFIINCMD= ${EFIINC}/${MACHINE} FDTSRC= ${BOOTSRC}/fdt FICLSRC= ${BOOTSRC}/ficl LDRSRC= ${BOOTSRC}/common LIBLUASRC= ${BOOTSRC}/liblua LUASRC= ${SRCTOP}/contrib/lua/src SASRC= ${BOOTSRC}/libsa SYSDIR= ${SRCTOP}/sys UBOOTSRC= ${BOOTSRC}/uboot ZFSSRC= ${SASRC}/zfs LIBCSRC= ${SRCTOP}/lib/libc BOOTOBJ= ${OBJTOP}/stand # BINDIR is where we install BINDIR?= /boot LIBSA= ${BOOTOBJ}/libsa/libsa.a .if ${MACHINE} == "i386" LIBSA32= ${LIBSA} .else LIBSA32= ${BOOTOBJ}/libsa32/libsa32.a .endif # Standard options: CFLAGS+= -nostdinc .if ${MACHINE_ARCH} == "amd64" && ${DO32:U0} == 1 CFLAGS+= -I${BOOTOBJ}/libsa32 .else CFLAGS+= -I${BOOTOBJ}/libsa .endif CFLAGS+= -I${SASRC} -D_STANDALONE CFLAGS+= -I${SYSDIR} # Spike the floating point interfaces CFLAGS+= -Ddouble=jagged-little-pill -Dfloat=floaty-mcfloatface .if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" # Slim down the image. This saves about 15% in size with clang 6 on x86 # Our most constrained /boot/loader env is BIOS booting on x86, where # our text + data + BTX have to fit into 640k below the ISA hole. # Experience has shown that problems arise between ~520k to ~530k. CFLAGS.clang+= -Oz CFLAGS.gcc+= -Os .endif # GELI Support, with backward compat hooks (mostly) .if defined(LOADER_NO_GELI_SUPPORT) MK_LOADER_GELI=no .warning "Please move from LOADER_NO_GELI_SUPPORT to WITHOUT_LOADER_GELI" .endif .if defined(LOADER_GELI_SUPPORT) MK_LOADER_GELI=yes .warning "Please move from LOADER_GELI_SUPPORT to WITH_LOADER_GELI" .endif .if ${MK_LOADER_GELI} == "yes" CFLAGS+= -DLOADER_GELI_SUPPORT CFLAGS+= -I${SASRC}/geli .endif # MK_LOADER_GELI # These should be confined to loader.mk, but can't because uboot/lib # also uses it. It's part of loader, but isn't a loader so we can't # just include loader.mk .if ${LOADER_DISK_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_DISK_SUPPORT .endif # Machine specific flags for all builds here # All PowerPC builds are 32 bit. We have no 64-bit loaders on powerpc # or powerpc64. .if ${MACHINE_ARCH} == "powerpc64" CFLAGS+= -m32 -mcpu=powerpc .endif # For amd64, there's a bit of mixed bag. Some of the tree (i386, lib*32) is # build 32-bit and some 64-bit (lib*, efi). Centralize all the 32-bit magic here # and activate it when DO32 is explicitly defined to be 1. .if ${MACHINE_ARCH} == "amd64" && ${DO32:U0} == 1 CFLAGS+= -m32 # LD_FLAGS is passed directly to ${LD}, not via ${CC}: LD_FLAGS+= -m elf_i386_fbsd AFLAGS+= --32 .endif SSP_CFLAGS= # Add in the no float / no SIMD stuff and announce we're freestanding # aarch64 and riscv don't have -msoft-float, but all others do. riscv # currently has no /boot/loader, but may soon. CFLAGS+= -ffreestanding ${CFLAGS_NO_SIMD} .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -mgeneral-regs-only -fPIC .elif ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -march=rv64imac -mabi=lp64 .else CFLAGS+= -msoft-float .endif # -msoft-float seems to be insufficient for powerpcspe .if ${MACHINE_ARCH} == "powerpcspe" CFLAGS+= -mno-spe .endif .if ${MACHINE_CPUARCH} == "i386" || (${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 1) CFLAGS+= -march=i386 CFLAGS.gcc+= -mpreferred-stack-boundary=2 .endif .if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0 CFLAGS+= -fPIC -mno-red-zone .endif .if ${MACHINE_CPUARCH} == "arm" # Do not generate movt/movw, because the relocation fixup for them does not # translate to the -Bsymbolic -pie format required by self_reloc() in loader(8). # Also, the fpu is not available in a standalone environment. .if ${COMPILER_VERSION} < 30800 CFLAGS.clang+= -mllvm -arm-use-movt=0 .else CFLAGS.clang+= -mno-movt .endif CFLAGS.clang+= -mfpu=none CFLAGS+= -fPIC .endif # The boot loader build uses dd status=none, where possible, for reproducible # build output (since performance varies from run to run). Trouble is that # option was recently (10.3) added to FreeBSD and is non-standard. Only use it # when this test succeeds rather than require dd to be a bootstrap tool. DD_NOSTATUS!=(dd status=none count=0 2> /dev/null && echo status=none) || true DD=dd ${DD_NOSTATUS} .if ${MACHINE_CPUARCH} == "mips" CFLAGS+= -G0 -fno-pic -mno-abicalls .endif .if ${MK_LOADER_FORCE_LE} != "no" .if ${MACHINE_ARCH} == "powerpc64" CFLAGS+= -mlittle-endian .endif .endif # # Have a sensible default # .if ${MK_LOADER_LUA} == "yes" LOADER_DEFAULT_INTERP?=lua .elif ${MK_FORTH} == "yes" LOADER_DEFAULT_INTERP?=4th .else LOADER_DEFAULT_INTERP?=simp .endif LOADER_INTERP?=${LOADER_DEFAULT_INTERP} # Make sure we use the machine link we're about to create CFLAGS+=-I. all: ${PROG} +CLEANFILES+= teken_state.h +teken.c: teken_state.h + +teken_state.h: ${SYSDIR}/teken/sequences + awk -f ${SYSDIR}/teken/gensequences \ + ${SYSDIR}/teken/sequences > teken_state.h + .if !defined(NO_OBJ) _ILINKS=machine .if ${MACHINE} != ${MACHINE_CPUARCH} && ${MACHINE} != "arm64" _ILINKS+=${MACHINE_CPUARCH} .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _ILINKS+=x86 .endif CLEANFILES+=${_ILINKS} beforedepend: ${_ILINKS} beforebuild: ${_ILINKS} # Ensure that the links exist without depending on it when it exists which # causes all the modules to be rebuilt when the directory pointed to changes. .for _link in ${_ILINKS} .if !exists(${.OBJDIR}/${_link}) ${OBJS}: ${_link} .endif # _link exists .endfor .NOPATH: ${_ILINKS} ${_ILINKS}: @case ${.TARGET} in \ machine) \ if [ ${DO32:U0} -eq 0 ]; then \ path=${SYSDIR}/${MACHINE}/include ; \ else \ path=${SYSDIR}/${MACHINE:C/amd64/i386/}/include ; \ fi ;; \ *) \ path=${SYSDIR}/${.TARGET:T}/include ;; \ esac ; \ path=`(cd $$path && /bin/pwd)` ; \ ${ECHO} ${.TARGET:T} "->" $$path ; \ ln -fhs $$path ${.TARGET:T} .endif # !NO_OBJ .endif # __BOOT_DEFS_MK__ Index: head/stand/efi/include/efilib.h =================================================================== --- head/stand/efi/include/efilib.h (revision 351899) +++ head/stand/efi/include/efilib.h (revision 351900) @@ -1,149 +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 #include #include 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); 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); +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; void delay(int usecs); /* 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 */ Index: head/stand/efi/libefi/Makefile =================================================================== --- head/stand/efi/libefi/Makefile (revision 351899) +++ head/stand/efi/libefi/Makefile (revision 351900) @@ -1,64 +1,64 @@ # $FreeBSD$ .include 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" 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.teken.c+= -I${SRCTOP}/sys/teken .if ${MK_LOADER_ZFS} != "no" CFLAGS+= -I${ZFSSRC} 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 Index: head/stand/efi/libefi/efi_console.c =================================================================== --- head/stand/efi/libefi/efi_console.c (revision 351899) +++ head/stand/efi/libefi/efi_console.c (revision 351900) @@ -1,654 +1,609 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include +#include #include "bootstrap.h" 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; -#ifdef TERM_EMU -#define DEFAULT_FGCOLOR EFI_LIGHTGRAY -#define DEFAULT_BGCOLOR EFI_BLACK +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; -#define MAXARGS 8 -static int args[MAXARGS], argc; -static int fg_c, bg_c, curx, cury; -static int esc; +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, +}; -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 +teken_t teken; +teken_pos_t tp; +struct text_pixel { + teken_char_t c; + teken_attr_t a; +}; + +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); struct console efi_console = { "efi", "EFI console", C_WIDEOUT, efi_cons_probe, efi_cons_init, efi_cons_putchar, efi_cons_getchar, efi_cons_poll }; -#ifdef TERM_EMU - -/* Get cursor position. */ -void -get_pos(int *x, int *y) +/* + * Not implemented. + */ +static void +efi_cons_bell(void *s __unused) { - *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) +static void +efi_text_cursor(void *s __unused, const teken_pos_t *p) { - conout->SetCursorPosition(conout, x, y); - if (_x != NULL) - *_x = conout->Mode->CursorColumn; - if (_y != NULL) - *_y = conout->Mode->CursorRow; + UINTN row, col; + + (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); + + 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; + + conout->SetCursorPosition(conout, col, row); } -/* Clear internal state of the terminal emulation code. */ -void -end_term(void) +static void +efi_text_printchar(const teken_pos_t *p) { - esc = 0; - argc = -1; -} + UINTN a, attr; + struct text_pixel *px; + teken_color_t fg, bg, tmp; -#endif + px = buffer + p->tp_col + p->tp_row * 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]); + + conout->SetCursorPosition(conout, p->tp_col, p->tp_row); + + /* to prvent autoscroll, skip print of lower right char */ + if (p->tp_row == tp.tp_row - 1 && + p->tp_col == tp.tp_col - 1) + return; + + (void) conout->SetAttribute(conout, attr); + efi_cons_efiputchar(px->c); + (void) conout->SetAttribute(conout, a); +} + static void -efi_cons_probe(struct console *cp) +efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, + const teken_attr_t *a) { - conout = ST->ConOut; - conin = ST->ConIn; - cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; + 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); } -static int -efi_cons_init(int arg) +static void +efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, + const teken_attr_t *a) { - EFI_STATUS status; + teken_pos_t p; + UINTN row, col; -#ifdef TERM_EMU - 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 + (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); + + 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); - status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, - (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (status != EFI_SUCCESS) - coninex = NULL; - return (0); } +static bool +efi_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 -efi_cons_rawputchar(int c) +efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) { - int i; - UINTN x, y; - conout->QueryMode(conout, conout->Mode->Mode, &x, &y); + int srow, drow; + int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ + teken_pos_t d, s; - if (c == '\t') { - int n; + /* + * 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. + */ - n = 8 - ((conout->Mode->CursorColumn + 8) % 8); - for (i = 0; i < n; i++) - efi_cons_rawputchar(' '); + nrow = r->tr_end.tp_row - r->tr_begin.tp_row; + ncol = r->tr_end.tp_col - r->tr_begin.tp_col; + + conout->EnableCursor(conout, 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; + 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); + } + } + } } 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--; + /* 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); + } + } } - return; - default: - efi_cons_efiputchar(c); - curx++; - if (curx > x-1) { - curx = 0; - cury++; + } 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); + } + } } - 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) +efi_text_param(void *s __unused, int cmd, unsigned int value) { - 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++); - } + 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) + conout->EnableCursor(conout, TRUE); + else + conout->EnableCursor(conout, FALSE); + break; + default: + /* Not yet implemented */ + break; } - 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). + * Not implemented. */ static void -CM(void) +efi_cons_respond(void *s __unused, const void *buf __unused, + size_t len __unused) { - 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) +static void +efi_cons_probe(struct console *cp) { - argc = 1; - args[0] = args[1] = 1; - CM(); + cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; } -/* Clear line from current position to end of line */ -static void -CL(int direction) +bool +efi_cons_update_mode(void) { - int i, len; - UINTN x, y; - CHAR16 *line; + UINTN cols, rows; + const teken_attr_t *a; + EFI_STATUS status; + char env[8]; - 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(); + status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); + if (EFI_ERROR(status)) { + cols = 80; + rows = 24; } - if (cury == y - 1) - len--; - - line = malloc(len * sizeof (CHAR16)); - if (line == NULL) { - printf("out of memory\n"); - return; + if (buffer != NULL) { + if (tp.tp_row == rows && tp.tp_col == cols) + return (true); + free(buffer); + } else { + teken_init(&teken, &tf, NULL); } - for (i = 0; i < len; i++) - line[i] = ' '; - line[len-1] = 0; - if (direction != 0) - curs_move(NULL, NULL, 0, cury); + tp.tp_row = rows; + tp.tp_col = cols; + buffer = malloc(rows * cols * sizeof(*buffer)); + if (buffer == NULL) + return (false); - conout->OutputString(conout, line); - /* restore cursor position */ - curs_move(NULL, NULL, curx, cury); - free(line); - end_term(); -} + teken_set_winsize(&teken, &tp); + a = teken_get_defattr(&teken); -static void -get_arg(int c) -{ - if (argc < 0) - argc = 0; - args[argc] *= 10; - args[argc] += c - '0'; + 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 = *a; + } + + snprintf(env, sizeof (env), "%u", (unsigned)rows); + setenv("LINES", env, 1); + snprintf(env, sizeof (env), "%u", (unsigned)cols); + setenv("COLUMNS", env, 1); + + return (true); } -/* Emulate basic capabilities of cons25 terminal */ -static void -efi_term_emu(int c) +static int +efi_cons_init(int arg) { - 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; - } + if (conin != NULL) + return (0); + + conout = ST->ConOut; + conin = ST->ConIn; + + conout->EnableCursor(conout, TRUE); + status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, + (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (status != EFI_SUCCESS) + coninex = NULL; + + if (efi_cons_update_mode()) + return (0); + + return (1); } -#else -void -HO(void) -{ -} -#endif void efi_cons_putchar(int c) { -#ifdef TERM_EMU - efi_term_emu(c); -#else - efi_cons_rawputchar(c); -#endif + unsigned char ch = c; + + if (buffer != NULL) + teken_input(&teken, &ch, sizeof (ch)); + else + efi_cons_efiputchar(c); } 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++; } } } 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; - /* - * translate box chars to unicode - */ - switch (c) { - /* single frame */ - case 0xb3: buf[0] = BOXDRAW_VERTICAL; break; - case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break; - case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break; - case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break; - case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break; - case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break; - - /* double frame */ - case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break; - case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break; - case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break; - case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break; - case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break; - case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break; - - default: - buf[0] = c; - } + buf[0] = c; buf[1] = 0; /* terminate string */ + status = conout->TestString(conout, buf); + if (EFI_ERROR(status)) + buf[0] = '?'; conout->OutputString(conout, buf); } Index: head/stand/efi/loader/arch/amd64/Makefile.inc =================================================================== --- head/stand/efi/loader/arch/amd64/Makefile.inc (revision 351899) +++ head/stand/efi/loader/arch/amd64/Makefile.inc (revision 351900) @@ -1,15 +1,15 @@ # $FreeBSD$ SRCS+= amd64_tramp.S \ start.S \ elf64_freebsd.c \ trap.c \ exc.S .PATH: ${BOOTSRC}/i386/libi386 SRCS+= nullconsole.c \ comconsole.c \ spinconsole.c -CFLAGS+= -fPIC -DTERM_EMU +CFLAGS+= -fPIC LDFLAGS+= -Wl,-znocombreloc Index: head/stand/efi/loader/arch/i386/Makefile.inc =================================================================== --- head/stand/efi/loader/arch/i386/Makefile.inc (revision 351899) +++ head/stand/efi/loader/arch/i386/Makefile.inc (revision 351900) @@ -1,13 +1,13 @@ # $FreeBSD$ SRCS+= start.S \ elf32_freebsd.c \ exec.c .PATH: ${BOOTSRC}/i386/libi386 SRCS+= nullconsole.c \ comconsole.c \ spinconsole.c -CFLAGS+= -fPIC -DTERM_EMU +CFLAGS+= -fPIC LDFLAGS+= -Wl,-znocombreloc Index: head/stand/efi/loader/main.c =================================================================== --- head/stand/efi/loader/main.c (revision 351899) +++ head/stand/efi/loader/main.c (revision 351900) @@ -1,1551 +1,1547 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, 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 || stat(PATH_KERNEL, &st) == 0); } #ifdef EFI_ZFS_BOOT static bool probe_zfs_currdev(uint64_t guid) { char *devname; struct zfs_devdesc currdev; 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_bootenv(devname); return (sanity_check_currdev()); } #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 it's 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 fine * 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 * enxt best thing, we look through the device path(s) passed * in the BootXXXX varaible. 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. */ static 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) goto out; ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { pci_pending = false; if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_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) { 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); /* Skip the end node */ } if (pci_pending && vid_seen == 0) vid_seen = ++seen; /* * 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); } } 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; 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); 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; uhowto = parse_uefi_con_out(); /* * 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(" 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); 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; - char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; - extern void HO(void); 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); } - sprintf(rowenv, "%u", (unsigned)rows); - setenv("LINES", rowenv, 1); - HO(); /* set cursor */ + (void) efi_cons_update_mode(); 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 \"\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) < 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); Index: head/stand/forth/frames.4th =================================================================== --- head/stand/forth/frames.4th (revision 351899) +++ head/stand/forth/frames.4th (revision 351900) @@ -1,142 +1,142 @@ \ Copyright (c) 2003 Scott Long \ Copyright (c) 2012-2015 Devin Teske \ 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 -196 constant sh_el -179 constant sv_el -218 constant slt_el -192 constant slb_el -191 constant srt_el -217 constant srb_el +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 -205 constant dh_el -186 constant dv_el -201 constant dlt_el -200 constant dlb_el -187 constant drt_el -188 constant drb_el +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 -176 constant fill_dark -177 constant fill_med -178 constant fill_bright +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 @ emit + 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 @ emit + v_el @ xemit 1+ 2dup loop 2drop 2drop drop ; : box ( w h x y -- ) \ Draw a box 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 @ emit \ Draw left-top corner - 2dup 4 pick + at-xy lb_el @ emit \ Draw left bottom corner - 2dup swap 5 pick + swap at-xy rt_el @ emit \ Draw right top corner - 2 pick + swap 3 pick + swap at-xy rb_el @ emit + 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 Index: head/stand/i386/libi386/Makefile =================================================================== --- head/stand/i386/libi386/Makefile (revision 351899) +++ head/stand/i386/libi386/Makefile (revision 351900) @@ -1,53 +1,57 @@ # $FreeBSD$ .include 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 \ smbios.c time.c vidconsole.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 .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS .if defined(BOOT_LITTLE_ENDIAN_UUID) # Use little-endian UUID format as defined in SMBIOS 2.6. CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID .elif defined(BOOT_NETWORK_ENDIAN_UUID) # Use network-endian UUID format for backward compatibility. CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID .endif .endif -# Include simple terminal emulation (cons25-compatible) -CFLAGS+= -DTERM_EMU +# terminal emulation +CFLAGS.vidconsole.c+= -I${SRCTOP}/sys/teken +CFLAGS.teken.c+= -I${SRCTOP}/sys/teken # XXX: make alloca() useable CFLAGS+= -Dalloca=__builtin_alloca CFLAGS+= -I${BOOTSRC}/ficl -I${BOOTSRC}/ficl/i386 \ -I${LDRSRC} -I${BOOTSRC}/i386/common \ -I${SYSDIR}/contrib/dev/acpica/include # Handle FreeBSD specific %b and %D printf format specifiers CFLAGS+= ${FORMAT_EXTENSIONS} .include Index: head/stand/i386/libi386/vidconsole.c =================================================================== --- head/stand/i386/libi386/vidconsole.c (revision 351899) +++ head/stand/i386/libi386/vidconsole.c (revision 351900) @@ -1,647 +1,821 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include +#include +#include +#include + +#include + #include "libi386.h" #if KEYBOARD_PROBE -#include - 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 int vidc_started; +static uint16_t *vgatext; -void get_pos(int *x, int *y); +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, +}; -#ifdef TERM_EMU -#define MAXARGS 8 -#define DEFAULT_FGCOLOR 7 -#define DEFAULT_BGCOLOR 0 +teken_t teken; +teken_pos_t tp; -void end_term(void); -void bail_out(int c); -void vidc_term_emu(int c); -void curs_move(int *_x, int *_y, int x, int y); -void write_char(int c, int fg, int bg); -void scroll_up(int rows, int fg, int bg); -void CD(void); -void CM(void); -void HO(void); +struct text_pixel { + teken_char_t c; + teken_attr_t a; +}; -static int args[MAXARGS], argc; -static int fg_c, bg_c, curx, cury; -static int esc; -#endif +static struct text_pixel *buffer; +#define NCOLORS 16 +/* + * Between console's palette and VGA's one: + * - blue and red are swapped (1 <-> 4) + * - yellow and cyan are swapped (3 <-> 6) + */ +static const int cons_to_vga_colors[NCOLORS] = { + 0, 4, 2, 6, 1, 5, 3, 7, + 8, 12, 10, 14, 9, 13, 11, 15 +}; + +#define TEXT_COLS 80 +#define TEXT_ROWS 25 +#define KEYBUFSZ 10 + +static uint8_t keybuf[KEYBUFSZ]; /* keybuf for extended codes */ + struct console vidconsole = { - "vidconsole", - "internal video/keyboard", - 0, - vidc_probe, - vidc_init, - vidc_putchar, - vidc_getchar, - vidc_ischar + .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 void -vidc_probe(struct console *cp) +static int +vga_get_reg(int reg, int index) { - - /* 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; + return (inb(reg + index)); } static int -vidc_init(int arg) +vga_get_atr(int reg, int i) { - int i; + int ret; - if (vidc_started && arg == 0) - return (0); - vidc_started = 1; -#ifdef TERM_EMU - /* Init terminal emulator */ - end_term(); - get_pos(&curx, &cury); - curs_move(&curx, &cury, curx, cury); - fg_c = DEFAULT_FGCOLOR; - bg_c = DEFAULT_BGCOLOR; -#endif - for (i = 0; i < 10 && vidc_ischar(); i++) - (void)vidc_getchar(); - return (0); /* XXX reinit? */ + (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 -vidc_biosputchar(int c) +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); - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0xe00 | (c & 0xff); - v86.ebx = 0x7; - v86int(); + (void) inb(reg + VGA_GEN_INPUT_STAT_1); } static void -vidc_rawputchar(int c) +vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val) { - int i; + outb(reg + indexreg, index); + outb(reg + datareg, val); +} - if (c == '\t') { - int n; -#ifndef TERM_EMU - int curx, cury; +static int +vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index) +{ + outb(reg + indexreg, index); + return (inb(reg + datareg)); +} - get_pos(&curx, %cury); -#endif +static int +vga_get_crtc(int reg, int i) +{ + return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i)); +} - n = 8 - ((curx + 8) % 8); - for (i = 0; i < n; i++) - vidc_rawputchar(' '); - } else { -#ifndef TERM_EMU - vidc_biosputchar(c); -#else - /* Emulate AH=0eh (teletype output) */ - switch(c) { - case '\a': - vidc_biosputchar(c); - return; - case '\r': - curx = 0; - curs_move(&curx, &cury, curx, cury); - return; - case '\n': - cury++; - if (cury > 24) { - scroll_up(1, fg_c, bg_c); - cury--; - } else { - curs_move(&curx, &cury, curx, cury); - } - return; - case '\b': - if (curx > 0) { - curx--; - curs_move(&curx, &cury, curx, cury); - /* write_char(' ', fg_c, bg_c); XXX destructive(!) */ - return; - } - return; - default: - write_char(c, fg_c, bg_c); - curx++; - if (curx > 79) { - curx = 0; - cury++; - } - if (cury > 24) { - curx = 0; - scroll_up(1, fg_c, bg_c); - cury--; - } - } - curs_move(&curx, &cury, curx, cury); -#endif - } +static void +vga_set_crtc(int reg, int i, int v) +{ + vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v); } -/* Get cursor position on the screen. Result is in edx. Sets - * curx and cury appropriately. - */ -void -get_pos(int *x, int *y) +static void +vidc_text_set_cursor(teken_unit_t row, teken_unit_t col, bool visible) { + uint16_t addr; + uint8_t msl, s, e; - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0300; - v86.ebx = 0x0; - v86int(); - *x = v86.edx & 0x00ff; - *y = (v86.edx & 0xff00) >> 8; + 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); } -#ifdef TERM_EMU - -/* Move cursor to x rows and y cols (0-based). */ -void -curs_move(int *_x, int *_y, int x, int y) +static void +vidc_text_get_cursor(teken_unit_t *row, teken_unit_t *col) { + uint16_t addr; - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0200; - v86.ebx = 0x0; - v86.edx = ((0x00ff & y) << 8) + (0x00ff & x); - v86int(); - *_x = x; - *_y = y; - /* If there is ctrl char at this position, cursor would be invisible. - * Make it a space instead. - */ - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0800; - v86.ebx = 0x0; - v86int(); -#define isvisible(c) (((c) >= 32) && ((c) < 255)) - if (!isvisible(v86.eax & 0x00ff)) { - write_char(' ', fg_c, bg_c); - } + 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; } -/* Scroll up the whole window by a number of rows. If rows==0, - * clear the window. fg and bg are attributes for the new lines - * inserted in the window. +/* + * Not implemented. */ -void -scroll_up(int rows, int fgcol, int bgcol) +static void +vidc_cons_bell(void *s __unused) { - - if (rows == 0) - rows = 25; - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0600 + (0x00ff & rows); - v86.ebx = (bgcol << 12) + (fgcol << 8); - v86.ecx = 0x0; - v86.edx = 0x184f; - v86int(); } -/* Write character and attribute at cursor position. */ -void -write_char(int c, int fgcol, int bgcol) +static void +vidc_text_cursor(void *s __unused, const teken_pos_t *p) { + teken_unit_t row, col; - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0900 + (0x00ff & c); - v86.ebx = (bgcol << 4) + fgcol; - v86.ecx = 0x1; - v86int(); + 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); } -/**************************************************************/ /* - * Screen manipulation functions. They use accumulated data in - * args[] and argc variables. - * + * Binary searchable table for Unicode to CP437 conversion. */ +struct unicp437 { + uint16_t unicode_base; + uint8_t cp437_base; + uint8_t length; +}; -/* Clear display from current position to end of screen */ -void -CD(void) +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; - get_pos(&curx, &cury); - if (curx > 0) { - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0600; - v86.ebx = (bg_c << 4) + fg_c; - v86.ecx = (cury << 8) + curx; - v86.edx = (cury << 8) + 79; - v86int(); - if (++cury > 24) { - end_term(); - return; + 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); } - } - v86.ctl = 0; - v86.addr = 0x10; - v86.eax = 0x0600; - v86.ebx = (bg_c << 4) + fg_c; - v86.ecx = (cury << 8) + 0; - v86.edx = (24 << 8) + 79; - v86int(); - end_term(); + + return ('?'); } -/* Absolute cursor move to args[0] rows and args[1] columns - * (the coordinates are 1-based). - */ -void -CM(void) +static void +vidc_text_printchar(const teken_pos_t *p) { + int i; + uint8_t attr; + struct text_pixel *px; + teken_color_t fg, bg, tmp; + struct cgatext { + uint8_t ch; + uint8_t attr; + } *addr; - if (args[0] > 0) - args[0]--; - if (args[1] > 0) - args[1]--; - curs_move(&curx, &cury, args[1], args[0]); - end_term(); + px = buffer + p->tp_col + p->tp_row * tp.tp_col; + fg = teken_256to16(px->a.ta_fgcolor); + bg = teken_256to16(px->a.ta_bgcolor); + if (px->a.ta_format & TF_BOLD) + fg |= TC_LIGHT; + if (px->a.ta_format & TF_BLINK) + bg |= TC_LIGHT; + + 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; } -/* Home cursor (left top corner) */ -void -HO(void) +static void +vidc_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, + const teken_attr_t *a) { + int attr, idx; - argc = 1; - args[0] = args[1] = 1; - CM(); + idx = p->tp_col + p->tp_row * tp.tp_col; + buffer[idx].c = c; + buffer[idx].a = *a; + vidc_text_printchar(p); } -/* Clear internal state of the terminal emulation code */ -void -end_term(void) +static void +vidc_text_fill(void *s, const teken_rect_t *r, teken_char_t c, + const teken_attr_t *a) { + teken_pos_t p; + teken_unit_t row, col; - esc = 0; - argc = -1; + 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_set_cursor(row, col, true); } -/* Gracefully exit ESC-sequence processing in case of misunderstanding */ -void -bail_out(int c) +static bool +vidc_same_pixel(struct text_pixel *px1, struct text_pixel *px2) { - char buf[16], *ch; - int i; + if (px1->c != px2->c) + return (false); - if (esc) { - vidc_rawputchar('\033'); - if (esc != '\033') - vidc_rawputchar(esc); - for (i = 0; i <= argc; ++i) { - sprintf(buf, "%d", args[i]); - ch = buf; - while (*ch) - vidc_rawputchar(*ch++); - } - } - vidc_rawputchar(c); - end_term(); + 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 -get_arg(int c) +vidc_text_copy(void *ptr __unused, 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_pos_t d, s; + teken_unit_t row, col; - if (argc < 0) - argc = 0; - args[argc] *= 10; - args[argc] += c - '0'; + /* + * 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; + 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); + } + } + } + } 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 (!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); + } + } + } + } 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 (!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); + } + } + } + } + } + vidc_text_set_cursor(row, col, true); } -/* Emulate basic capabilities of cons25 terminal */ -void -vidc_term_emu(int c) +static void +vidc_text_param(void *s __unused, int cmd, unsigned int value) { - static int ansi_col[] = { - 0, 4, 2, 6, 1, 5, 3, 7, - }; - int t; - int i; + teken_unit_t row, col; - switch (esc) { - case 0: - switch (c) { - case '\033': - esc = c; - break; + 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) + vidc_text_set_cursor(row, col, true); + else + vidc_text_set_cursor(row, col, false); + break; default: - vidc_rawputchar(c); - break; + /* Not yet implemented */ + break; } - break; +} - case '\033': - switch (c) { - case '[': - esc = c; - args[0] = 0; - argc = -1; - break; - default: - bail_out(c); - break; - } - break; +/* + * Not implemented. + */ +static void +vidc_cons_respond(void *s __unused, const void *buf __unused, + size_t len __unused) +{ +} - case '[': - switch (c) { - case ';': - if (argc < 0) /* XXX */ - argc = 0; - else if (argc + 1 >= MAXARGS) - bail_out(c); - else - args[++argc] = 0; - break; - case 'H': - if (argc < 0) - HO(); - else if (argc == 1) - CM(); - else - bail_out(c); - break; - case '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; +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 int +vidc_init(int arg) +{ + const teken_attr_t *a; + int val; + + if (vidc_started && arg == 0) + return (0); + + vidc_started = 1; + + /* + * 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); + + teken_init(&teken, &tf, NULL); + teken_set_winsize(&teken, &tp); + a = teken_get_defattr(&teken); + + for (int row = 0; row < tp.tp_row; row++) + for (int col = 0; col < tp.tp_col; col++) { + buffer[col + row * tp.tp_col].c = ' '; + buffer[col + row * tp.tp_col].a = *a; } - } - end_term(); - break; - default: - if (isdigit(c)) - get_arg(c); - else - bail_out(c); - break; - } - break; - default: - bail_out(c); - break; - } + for (int i = 0; i < 10 && vidc_ischar(); i++) + (void) vidc_getchar(); + + return (0); /* XXX reinit? */ } -#endif +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) { -#ifdef TERM_EMU - vidc_term_emu(c); -#else - vidc_rawputchar(c); -#endif + unsigned char ch = c; + + if (buffer != NULL) + teken_input(&teken, &ch, sizeof (ch)); + else + vidc_biosputchar(c); } static int vidc_getchar(void) { + int i, c; - if (vidc_ischar()) { - v86.ctl = 0; - v86.addr = 0x16; - v86.eax = 0x0; - v86int(); - return (v86.eax & 0xff); - } else { - return (-1); - } + 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; - v86.ctl = V86_FLAGS; - v86.addr = 0x16; - v86.eax = 0x100; - v86int(); - return (!V86_ZR(v86.efl)); + 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 */ /* selected defines from kbdio.h */ #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 /* 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); + /* + * 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); + 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; + 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(); - } + 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; + /* 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); + /* 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; + /* 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); + delay7(); + i = inb(IO_KBD + KBD_DATA_PORT); #ifdef PROBE_KBD_BEBUG - printf("probe_keyboard: got 0x%x.\n", i); + printf("probe_keyboard: got 0x%x.\n", i); #endif - if (i == KBD_ECHO) { - /* got the right answer */ - return (1); + if (i == KBD_ECHO) { + /* got the right answer */ + return (1); + } } - } - return (0); + return (0); } #endif /* KEYBOARD_PROBE */ Index: head/stand/lua/drawer.lua =================================================================== --- head/stand/lua/drawer.lua (revision 351899) +++ head/stand/lua/drawer.lua (revision 351900) @@ -1,399 +1,399 @@ -- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright (c) 2015 Pedro Souza -- Copyright (c) 2018 Kyle Evans -- 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 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 try_include('brand-' .. brand) 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 try_include('logo-' .. logo) 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 drawbox() 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 "double" 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 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 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 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 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 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 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 draw(x, y, logodef.graphic) 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, }, ["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' 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 = "\xC4", - vertical = "\xB3", - top_left = "\xDA", - bottom_left = "\xC0", - top_right = "\xBF", - bottom_right = "\xD9", + 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 = "\xCD", - vertical = "\xBA", - top_left = "\xC9", - bottom_left = "\xC8", - top_right = "\xBB", - bottom_right = "\xBC", + 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() return drawmenu(menudef) end return drawer