diff --git a/stand/liblua/lutils.c b/stand/liblua/lutils.c index 1792d0c8c620..274d9a39da21 100644 --- a/stand/liblua/lutils.c +++ b/stand/liblua/lutils.c @@ -1,605 +1,653 @@ /*- * Copyright (c) 2014 Pedro Souza * 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 #include "lua.h" #include "lauxlib.h" #include "lstd.h" #include "lutils.h" #include "bootstrap.h" #include #include /* * Like loader.perform, except args are passed already parsed * on the stack. */ static int lua_command(lua_State *L) { int i; int res = 1; int argc = lua_gettop(L); char **argv; argv = malloc(sizeof(char *) * (argc + 1)); if (argv == NULL) return 0; for (i = 0; i < argc; i++) argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1); argv[argc] = NULL; res = interp_builtin_cmd(argc, argv); free(argv); lua_pushinteger(L, res); return 1; } static int lua_has_command(lua_State *L) { const char *cmd; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } cmd = luaL_checkstring(L, 1); lua_pushinteger(L, interp_has_builtin_cmd(cmd)); return 1; } +static int +lua_has_feature(lua_State *L) +{ + const char *feature; + char *msg; + + feature = luaL_checkstring(L, 1); + + if (feature_name_is_enabled(feature)) { + lua_pushboolean(L, 1); + return 1; + } + + lua_pushnil(L); + lua_pushstring(L, "Feature not enabled"); + return 2; +} + + static int lua_perform(lua_State *L) { int argc; char **argv; int res = 1; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { res = interp_builtin_cmd(argc, argv); free(argv); } lua_pushinteger(L, res); return 1; } static int lua_command_error(lua_State *L) { lua_pushstring(L, command_errbuf); return 1; } /* * Accepts a space-delimited loader command and runs it through the standard * loader parsing, as if it were executed at the loader prompt by the user. */ static int lua_interpret(lua_State *L) { const char *interp_string; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } interp_string = luaL_checkstring(L, 1); lua_pushinteger(L, interp_run(interp_string)); return 1; } static int lua_parse(lua_State *L) { int argc, nargc; char **argv; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { for (nargc = 0; nargc < argc; ++nargc) { lua_pushstring(L, argv[nargc]); } free(argv); return nargc; } lua_pushnil(L); return 1; } static int lua_getchar(lua_State *L) { lua_pushinteger(L, getchar()); return 1; } static int lua_ischar(lua_State *L) { lua_pushboolean(L, ischar()); return 1; } static int lua_gets(lua_State *L) { char buf[129]; ngets(buf, 128); lua_pushstring(L, buf); return 1; } static int lua_time(lua_State *L) { lua_pushinteger(L, time(NULL)); return 1; } static int lua_delay(lua_State *L) { delay((int)luaL_checknumber(L, 1)); return 0; } static int lua_getenv(lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); return 1; } static int lua_setenv(lua_State *L) { const char *key, *val; key = luaL_checkstring(L, 1); val = luaL_checkstring(L, 2); lua_pushinteger(L, setenv(key, val, 1)); return 1; } static int lua_unsetenv(lua_State *L) { const char *ev; ev = luaL_checkstring(L, 1); lua_pushinteger(L, unsetenv(ev)); return 1; } static int lua_printc(lua_State *L) { ssize_t cur, l; const char *s = luaL_checklstring(L, 1, &l); for (cur = 0; cur < l; ++cur) putchar((unsigned char)*(s++)); return 1; } static int lua_openfile(lua_State *L) { const char *mode, *str; int nargs; nargs = lua_gettop(L); if (nargs < 1 || nargs > 2) { lua_pushnil(L); return 1; } str = lua_tostring(L, 1); mode = "r"; if (nargs > 1) { mode = lua_tostring(L, 2); if (mode == NULL) { lua_pushnil(L); return 1; } } FILE * f = fopen(str, mode); if (f != NULL) { FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**)); *ptr = f; } else lua_pushnil(L); return 1; } static int lua_closefile(lua_State *L) { FILE ** f; if (lua_gettop(L) != 1) { lua_pushboolean(L, 0); return 1; } f = (FILE**)lua_touserdata(L, 1); if (f != NULL && *f != NULL) { lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0); *f = NULL; } else lua_pushboolean(L, 0); return 1; } static int lua_readfile(lua_State *L) { FILE **f; size_t size, r; char * buf; if (lua_gettop(L) < 1 || lua_gettop(L) > 2) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } if (lua_gettop(L) == 2) size = (size_t)lua_tonumber(L, 2); else size = (*f)->size; buf = (char*)malloc(size); r = fread(buf, 1, size, *f); lua_pushlstring(L, buf, r); free(buf); lua_pushinteger(L, r); return 2; } /* * Implements io.write(file, ...) * Any number of string and number arguments may be passed to it, * and it will return the number of bytes written, or nil, an error string, and * the errno. */ static int lua_writefile(lua_State *L) { FILE **f; const char *buf; int i, nargs; size_t bufsz, w, wrsz; buf = NULL; bufsz = 0; w = 0; wrsz = 0; nargs = lua_gettop(L); if (nargs < 2) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } /* Do a validation pass first */ for (i = 0; i < nargs - 1; i++) { /* * With Lua's API, lua_isstring really checks if the argument * is a string or a number. The latter will be implicitly * converted to a string by our later call to lua_tolstring. */ if (!lua_isstring(L, i + 2)) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } } for (i = 0; i < nargs - 1; i++) { /* We've already validated; there's no chance of failure */ buf = lua_tolstring(L, i + 2, &bufsz); wrsz = fwrite(buf, 1, bufsz, *f); if (wrsz < bufsz) return luaL_fileresult(L, 0, NULL); w += wrsz; } lua_pushinteger(L, w); return 1; } /* * put image using terminal coordinates. */ static int lua_term_putimage(lua_State *L) { const char *name; png_t png; uint32_t x1, y1, x2, y2, f; int nargs, ret = 0, error; nargs = lua_gettop(L); if (nargs != 6) { lua_pushboolean(L, 0); return 1; } name = luaL_checkstring(L, 1); x1 = luaL_checknumber(L, 2); y1 = luaL_checknumber(L, 3); x2 = luaL_checknumber(L, 4); y2 = luaL_checknumber(L, 5); f = luaL_checknumber(L, 6); x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width; y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height; if (x2 != 0) { x2 = gfx_state.tg_origin.tp_col + x2 * gfx_state.tg_font.vf_width; } if (y2 != 0) { y2 = gfx_state.tg_origin.tp_row + y2 * gfx_state.tg_font.vf_height; } if ((error = png_open(&png, name)) != PNG_NO_ERROR) { if (f & FL_PUTIMAGE_DEBUG) printf("%s\n", png_error_string(error)); } else { if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0) ret = 1; (void) png_close(&png); } lua_pushboolean(L, ret); return 1; } static int lua_fb_putimage(lua_State *L) { const char *name; png_t png; uint32_t x1, y1, x2, y2, f; int nargs, ret = 0, error; nargs = lua_gettop(L); if (nargs != 6) { lua_pushboolean(L, 0); return 1; } name = luaL_checkstring(L, 1); x1 = luaL_checknumber(L, 2); y1 = luaL_checknumber(L, 3); x2 = luaL_checknumber(L, 4); y2 = luaL_checknumber(L, 5); f = luaL_checknumber(L, 6); if ((error = png_open(&png, name)) != PNG_NO_ERROR) { if (f & FL_PUTIMAGE_DEBUG) printf("%s\n", png_error_string(error)); } else { if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0) ret = 1; (void) png_close(&png); } lua_pushboolean(L, ret); return 1; } static int lua_fb_setpixel(lua_State *L) { uint32_t x, y; int nargs; nargs = lua_gettop(L); if (nargs != 2) { lua_pushnil(L); return 1; } x = luaL_checknumber(L, 1); y = luaL_checknumber(L, 2); gfx_fb_setpixel(x, y); return 0; } static int lua_fb_line(lua_State *L) { uint32_t x0, y0, x1, y1, wd; int nargs; nargs = lua_gettop(L); if (nargs != 5) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); wd = luaL_checknumber(L, 5); gfx_fb_line(x0, y0, x1, y1, wd); return 0; } static int lua_fb_bezier(lua_State *L) { uint32_t x0, y0, x1, y1, x2, y2, width; int nargs; nargs = lua_gettop(L); if (nargs != 7) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); x2 = luaL_checknumber(L, 5); y2 = luaL_checknumber(L, 6); width = luaL_checknumber(L, 7); gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width); return 0; } static int lua_fb_drawrect(lua_State *L) { uint32_t x0, y0, x1, y1, fill; int nargs; nargs = lua_gettop(L); if (nargs != 5) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); fill = luaL_checknumber(L, 5); gfx_fb_drawrect(x0, y0, x1, y1, fill); return 0; } static int lua_term_drawrect(lua_State *L) { uint32_t x0, y0, x1, y1; int nargs; nargs = lua_gettop(L); if (nargs != 4) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); gfx_term_drawrect(x0, y0, x1, y1); return 0; } #define REG_SIMPLE(n) { #n, lua_ ## n } static const struct luaL_Reg loaderlib[] = { REG_SIMPLE(delay), REG_SIMPLE(command_error), REG_SIMPLE(command), REG_SIMPLE(interpret), REG_SIMPLE(parse), REG_SIMPLE(getenv), REG_SIMPLE(has_command), + REG_SIMPLE(has_feature), REG_SIMPLE(perform), REG_SIMPLE(printc), /* Also registered as the global 'printc' */ REG_SIMPLE(setenv), REG_SIMPLE(time), REG_SIMPLE(unsetenv), REG_SIMPLE(fb_bezier), REG_SIMPLE(fb_drawrect), REG_SIMPLE(fb_line), REG_SIMPLE(fb_putimage), REG_SIMPLE(fb_setpixel), REG_SIMPLE(term_drawrect), REG_SIMPLE(term_putimage), { NULL, NULL }, }; static const struct luaL_Reg iolib[] = { { "close", lua_closefile }, REG_SIMPLE(getchar), REG_SIMPLE(gets), REG_SIMPLE(ischar), { "open", lua_openfile }, { "read", lua_readfile }, { "write", lua_writefile }, { NULL, NULL }, }; #undef REG_SIMPLE +static void +lua_add_feature(void *cookie, const char *name, const char *desc, bool enabled) +{ + lua_State *L = cookie; + + /* + * The feature table consists solely of features that are enabled, and + * their associated descriptions for debugging purposes. + */ + lua_pushstring(L, desc); + lua_setfield(L, -2, name); +} + +static void +lua_add_features(lua_State *L) +{ + + lua_newtable(L); + feature_iter(&lua_add_feature, L); + + /* + * We should still have just the table on the stack after we're done + * iterating. + */ + lua_setfield(L, -2, "features"); +} + int luaopen_loader(lua_State *L) { luaL_newlib(L, loaderlib); /* Add loader.machine and loader.machine_arch properties */ lua_pushstring(L, MACHINE); lua_setfield(L, -2, "machine"); lua_pushstring(L, MACHINE_ARCH); lua_setfield(L, -2, "machine_arch"); lua_pushstring(L, LUA_PATH); lua_setfield(L, -2, "lua_path"); lua_pushinteger(L, bootprog_rev); lua_setfield(L, -2, "version"); + lua_add_features(L); /* Set global printc to loader.printc */ lua_register(L, "printc", lua_printc); return 1; } int luaopen_io(lua_State *L) { luaL_newlib(L, iolib); return 1; } diff --git a/stand/libsa/Makefile b/stand/libsa/Makefile index 57340709873b..a1b9bc32e025 100644 --- a/stand/libsa/Makefile +++ b/stand/libsa/Makefile @@ -1,213 +1,213 @@ # Originally from $NetBSD: Makefile,v 1.21 1997/10/26 22:08:38 lukem Exp $ # # Notes: # - We don't use the libc strerror/sys_errlist because the string table is # quite large. # .include LIBSA_CPUARCH?=${MACHINE_CPUARCH} LIB?= sa # standalone components and stuff we have modified locally SRCS+= gzguts.h zutil.h __main.c abort.c assert.c bcd.c environment.c \ - getopt.c gets.c globals.c \ + features.c getopt.c gets.c globals.c \ hexdump.c nvstore.c pager.c panic.c printf.c strdup.c strerror.c \ random.c sbrk.c tslog.c twiddle.c zalloc.c zalloc_malloc.c # private (pruned) versions of libc string functions SRCS+= strcasecmp.c .PATH: ${LIBCSRC}/net SRCS+= ntoh.c # string functions from libc .PATH: ${LIBCSRC}/string SRCS+= bcmp.c bcopy.c bzero.c ffs.c fls.c \ memccpy.c memchr.c memcmp.c memcpy.c memmove.c memset.c \ strcat.c strchr.c strchrnul.c strcmp.c strcpy.c stpcpy.c stpncpy.c \ strcspn.c strlcat.c strlcpy.c strlen.c strncat.c strncmp.c strncpy.c \ strnlen.c strpbrk.c strrchr.c strsep.c strspn.c strstr.c strtok.c swab.c # stdlib functions from libc .PATH: ${LIBCSRC}/stdlib SRCS+= abs.c strtol.c strtoll.c strtoul.c strtoull.c # common boot code .PATH: ${SYSDIR}/kern SRCS+= subr_boot.c .if ${MACHINE_CPUARCH} == "arm" .PATH: ${LIBCSRC}/arm/gen # 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. CFLAGS.clang+= -mno-movt CFLAGS.clang+= -mfpu=none .PATH: ${SRCTOP}/contrib/llvm-project/compiler-rt/lib/builtins/arm/ SRCS+= aeabi_idivmod.S aeabi_ldivmod.S aeabi_uidivmod.S aeabi_uldivmod.S SRCS+= aeabi_memcmp.S aeabi_memcpy.S aeabi_memmove.S aeabi_memset.S .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv" .PATH: ${LIBCSRC}/${MACHINE_CPUARCH}/gen .endif # Compiler support functions .PATH: ${SRCTOP}/contrib/llvm-project/compiler-rt/lib/builtins/ # __clzsi2 and ctzsi2 for various builtin functions SRCS+= clzsi2.c ctzsi2.c # Divide and modulus functions called by the compiler SRCS+= divmoddi4.c divmodsi4.c divdi3.c divsi3.c moddi3.c modsi3.c SRCS+= udivmoddi4.c udivmodsi4.c udivdi3.c udivsi3.c umoddi3.c umodsi3.c SRCS+= ashldi3.c ashrdi3.c lshrdi3.c .if ${MACHINE_CPUARCH:Namd64:Ni386} == "" .PATH: ${SASRC}/x86 SRCS+= hypervisor.c .endif .if ${MACHINE_CPUARCH} == "powerpc" SRCS+= syncicache.c .endif # uuid functions from libc .PATH: ${LIBCSRC}/uuid SRCS+= uuid_create_nil.c uuid_equal.c uuid_from_string.c uuid_is_nil.c uuid_to_string.c # _setjmp/_longjmp .PATH: ${SASRC}/${LIBSA_CPUARCH} SRCS+= _setjmp.S # decompression functionality from libbz2 # NOTE: to actually test this functionality after libbz2 upgrade compile # loader(8) with LOADER_BZIP2_SUPPORT defined .PATH: ${SRCTOP}/contrib/bzip2 .for i in bzlib.c crctable.c decompress.c huffman.c randtable.c CFLAGS.${i}+= -DBZ_NO_STDIO -DBZ_NO_COMPRESS SRCS+=${i} .endfor # decompression functionality from zlib # https://github.com/madler/zlib/issues/633 documents why we suppress deprecated # prototype warnings. .PATH: ${SRCTOP}/sys/contrib/zlib ZLIB_CFLAGS=-DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib ${NO_WDEPRECATED_NON_PROTOTYPE} .for i in adler32.c crc32.c infback.c inffast.c inflate.c inftrees.c zutil.c CFLAGS.${i}+=${ZLIB_CFLAGS} SRCS+= ${i} .endfor # lz4 decompression functionality .PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4 SRCS+= lz4.c CFLAGS.lz4.c+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4 # Create a subset of includes that are safe, as well as adjusting those that aren't # The lists may drive people nuts, but they are explicitly opt-in FAKE_DIRS=xlocale arpa SAFE_INCS=a.out.h assert.h elf.h inttypes.h limits.h nlist.h setjmp.h stddef.h stdbool.h string.h strings.h time.h unistd.h uuid.h STAND_H_INC=ctype.h fcntl.h signal.h stdio.h stdlib.h OTHER_INC=stdarg.h errno.h stdint.h beforedepend: mkdir -p ${FAKE_DIRS}; \ for i in ${SAFE_INCS}; do \ ln -sf ${SRCTOP}/include/$$i $$i; \ done; \ ln -sf ${SYSDIR}/${MACHINE}/include/stdarg.h stdarg.h; \ ln -sf ${SYSDIR}/sys/errno.h errno.h; \ ln -sf ${SYSDIR}/sys/stdint.h stdint.h; \ ln -sf ${SRCTOP}/include/arpa/inet.h arpa/inet.h; \ ln -sf ${SRCTOP}/include/arpa/tftp.h arpa/tftp.h; \ for i in _time.h _strings.h _string.h; do \ [ -f xlocale/$$i ] || :> xlocale/$$i; \ done; \ for i in ${STAND_H_INC}; do \ ln -sf ${SASRC}/stand.h $$i; \ done CLEANDIRS+=${FAKE_DIRS} CLEANFILES+= ${SAFE_INCS} ${STAND_H_INC} ${OTHER_INC} # io routines SRCS+= closeall.c dev.c ioctl.c nullfs.c stat.c mount.c \ fstat.c close.c lseek.c open.c read.c write.c readdir.c preload.c # SMBios routines SRCS+= smbios.c .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS.smbios.c+= -DSMBIOS_SERIAL_NUMBERS .if defined(BOOT_LITTLE_ENDIAN_UUID) # Use little-endian UUID format as defined in SMBIOS 2.6. CFLAGS.smbios.c+= -DSMBIOS_LITTLE_ENDIAN_UUID .elif defined(BOOT_NETWORK_ENDIAN_UUID) # Use network-endian UUID format for backward compatibility. CFLAGS.smbios.c+= -DSMBIOS_NETWORK_ENDIAN_UUID .endif .endif # network routines SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c # network info services: SRCS+= bootp.c rarp.c bootparam.c # boot filesystems SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c SRCS+= dosfs.c ext2fs.c SRCS+= splitfs.c SRCS+= pkgfs.c # Time support SRCS+= time.c # kernel ufs support .PATH: ${SRCTOP}/sys/ufs/ffs SRCS+=ffs_subr.c ffs_tables.c # # i386 has a constrained space for its /boot/loader, so compile out the # extensive messages diagnosing bad superblocks. i386 doesn't support UEFI # booting, so doing it always makes sense natively there. When we compile # for 32-bit on amd64, LIBSA_CPUARCH is also i386 and we use libsa32 only # for the BIOS /boot/loader which has the same constraints. # .if ${LIBSA_CPUARCH} == "i386" CFLAGS.ffs_subr.c+= -DSTANDALONE_SMALL .endif CFLAGS.gzipfs.c+= ${ZLIB_CFLAGS} CFLAGS.pkgfs.c+= ${ZLIB_CFLAGS} CFLAGS.bzipfs.c+= -I${SRCTOP}/contrib/bzip2 -DBZ_NO_STDIO -DBZ_NO_COMPRESS # explicit_bzero and calculate_crc32c .PATH: ${SYSDIR}/libkern SRCS+= explicit_bzero.c crc32_libkern.c # Maybe GELI .if ${MK_LOADER_GELI} == "yes" .include "${SASRC}/geli/Makefile.inc" .endif .if ${MK_LOADER_VERIEXEC} == "yes" && ${MK_BEARSSL} == "yes" # XXX Note that these pollutes CFLAGS in a way that's not easy to fix .include "${SRCTOP}/lib/libbearssl/Makefile.libsa.inc" .include "${SRCTOP}/lib/libsecureboot/Makefile.libsa.inc" .endif # Maybe ZFS .if ${MK_LOADER_ZFS} == "yes" .include "${SASRC}/zfs/Makefile.inc" .endif .if ${DO32:U0} == 0 MAN=libsa.3 .endif .include diff --git a/stand/libsa/features.c b/stand/libsa/features.c new file mode 100644 index 000000000000..23dce2b13b60 --- /dev/null +++ b/stand/libsa/features.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2023 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + * + */ + +#include + +#include "stand.h" + +static uint32_t loader_features; + +#define FEATURE_ENTRY(name, desc) { FEATURE_##name, #name, desc } +static const struct feature_entry { + uint32_t value; + const char *name; + const char *desc; +} feature_map[] = { + FEATURE_ENTRY(EARLY_ACPI, "Loader probes ACPI in early startup"), +}; + +void +feature_enable(uint32_t mask) +{ + + loader_features |= mask; +} + +bool +feature_name_is_enabled(const char *name) +{ + const struct feature_entry *entry; + + for (size_t i = 0; i < nitems(feature_map); i++) { + entry = &feature_map[i]; + + if (strcmp(entry->name, name) == 0) + return ((loader_features & entry->value) != 0); + } + + return (false); +} + +void +feature_iter(feature_iter_fn *iter_fn, void *cookie) +{ + const struct feature_entry *entry; + + for (size_t i = 0; i < nitems(feature_map); i++) { + entry = &feature_map[i]; + + (*iter_fn)(cookie, entry->name, entry->desc, + (loader_features & entry->value) != 0); + } +} diff --git a/stand/libsa/libsa.3 b/stand/libsa/libsa.3 index a6f30051c8df..7643423b342a 100644 --- a/stand/libsa/libsa.3 +++ b/stand/libsa/libsa.3 @@ -1,863 +1,923 @@ .\" Copyright (c) Michael Smith .\" 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. .\" .Dd September 9, 2022 .Dt LIBSA 3 .Os .Sh NAME .Nm libsa .Nd support library for standalone executables .Sh SYNOPSIS .In stand.h .Sh DESCRIPTION The .Nm library provides a set of supporting functions for standalone applications, mimicking where possible the standard .Bx programming environment. The following sections group these functions by kind. Unless specifically described here, see the corresponding section 3 manpages for the given functions. .Sh STRING FUNCTIONS String functions are available as documented in .Xr string 3 and .Xr bstring 3 . .Sh MEMORY ALLOCATION .Bl -hang -width 10n .It Xo .Ft "void *" .Fn malloc "size_t size" .Xc .Pp Allocate .Fa size bytes of memory from the heap using a best-fit algorithm. .It Xo .Ft void .Fn free "void *ptr" .Xc .Pp Free the allocated object at .Fa ptr . .It Xo .Ft void .Fn setheap "void *start" "void *limit" .Xc .Pp Initialise the heap. This function must be called before calling .Fn alloc for the first time. The region between .Fa start and .Fa limit will be used for the heap; attempting to allocate beyond this will result in a panic. .It Xo .Ft "char *" .Fn sbrk "int junk" .Xc .Pp Provides the behaviour of .Fn sbrk 0 , i.e., returns the highest point that the heap has reached. This value can be used during testing to determine the actual heap usage. The .Fa junk argument is ignored. .El .Sh ENVIRONMENT A set of functions are provided for manipulating a flat variable space similar to the traditional shell-supported environment. Major enhancements are support for set/unset hook functions. .Bl -hang -width 10n .It Xo .Ft "char *" .Fn getenv "const char *name" .Xc .It Xo .Ft int .Fn setenv "const char *name" "const char *value" "int overwrite" .Xc .It Xo .Ft int .Fn putenv "char *string" .Xc .It Xo .Ft int .Fn unsetenv "const char *name" .Xc .Pp These functions behave similarly to their standard library counterparts. .It Xo .Ft "struct env_var *" .Fn env_getenv "const char *name" .Xc .Pp Looks up a variable in the environment and returns its entire data structure. .It Xo .Ft int .Fn env_setenv "const char *name" "int flags" "const void *value" "ev_sethook_t sethook" "ev_unsethook_t unsethook" .Xc .Pp Creates a new or sets an existing environment variable called .Fa name . If creating a new variable, the .Fa sethook and .Fa unsethook arguments may be specified. .Pp The set hook is invoked whenever an attempt is made to set the variable, unless the EV_NOHOOK flag is set. Typically a set hook will validate the .Fa value argument, and then call .Fn env_setenv again with EV_NOHOOK set to actually save the value. The predefined function .Fn env_noset may be specified to refuse all attempts to set a variable. .Pp The unset hook is invoked when an attempt is made to unset a variable. If it returns zero, the variable will be unset. The predefined function .Fa env_nounset may be used to prevent a variable being unset. .El .Sh STANDARD LIBRARY SUPPORT .Bl -hang -width 10n .It Xo .Ft int .Fn abs "int i" .Xc .It Xo .Ft int .Fn getopt "int argc" "char * const *argv" "const char *optstring" .Xc .It Xo .Ft long .Fn strtol "const char *nptr" "char **endptr" "int base" .Xc .It Xo .Ft long long .Fn strtoll "const char *nptr" "char **endptr" "int base" .Xc .It Xo .Ft long .Fn strtoul "const char *nptr" "char **endptr" "int base" .Xc .It Xo .Ft long long .Fn strtoull "const char *nptr" "char **endptr" "int base" .Xc .It Xo .Ft void .Fn srandom "unsigned int seed" .Xc .It Xo .Ft "long" .Fn random void .Xc .It Xo .Ft "char *" .Fn strerror "int error" .Xc .Pp Returns error messages for the subset of errno values supported by .Nm . .It Fn assert expression .Pp Requires .In assert.h . .It Xo .Ft int .Fn setjmp "jmp_buf env" .Xc .It Xo .Ft void .Fn longjmp "jmp_buf env" "int val" .Xc .Pp Defined as .Fn _setjmp and .Fn _longjmp respectively as there is no signal state to manipulate. Requires .In setjmp.h . .El .Sh CHARACTER I/O .Bl -hang -width 10n .It Xo .Ft void .Fn gets "char *buf" .Xc .Pp Read characters from the console into .Fa buf . All of the standard cautions apply to this function. .It Xo .Ft void .Fn ngets "char *buf" "int size" .Xc .Pp Read at most .Fa size - 1 characters from the console into .Fa buf . If .Fa size is less than 1, the function's behaviour is as for .Fn gets . .It Xo .Ft int .Fn fgetstr "char *buf" "int size" "int fd" .Xc .Pp Read a line of at most .Fa size characters into .Fa buf . Line terminating characters are stripped, and the buffer is always .Dv NUL terminated. Returns the number of characters in .Fa buf if successful, or -1 if a read error occurs. .It Xo .Ft int .Fn printf "const char *fmt" "..." .Xc .It Xo .Ft void .Fn vprintf "const char *fmt" "va_list ap" .Xc .It Xo .Ft int .Fn sprintf "char *buf" "const char *fmt" "..." .Xc .It Xo .Ft void .Fn vsprintf "char *buf" "const char *fmt" "va_list ap" .Xc .Pp The *printf functions implement a subset of the standard .Fn printf family functionality and some extensions. The following standard conversions are supported: c,d,n,o,p,s,u,x. The following modifiers are supported: +,-,#,*,0,field width,precision,l. .Pp The .Li b conversion is provided to decode error registers. Its usage is: .Bd -ragged -offset indent printf( .Qq reg=%b\en , regval, .Qq * ); .Ed .Pp where is the output expressed as a control character, e.g.\& \e10 gives octal, \e20 gives hex. Each is a sequence of characters, the first of which gives the bit number to be inspected (origin 1) and the next characters (up to a character less than 32) give the text to be displayed if the bit is set. Thus .Bd -ragged -offset indent printf( .Qq reg=%b\en , 3, .Qq \e10\e2BITTWO\e1BITONE ); .Ed .Pp would give the output .Bd -ragged -offset indent reg=3 .Ed .Pp The .Li D conversion provides a hexdump facility, e.g. .Bd -ragged -offset indent printf( .Qq %6D , ptr, .Qq \&: ); gives .Qq XX:XX:XX:XX:XX:XX .Ed .Bd -ragged -offset indent printf( .Qq %*D , len, ptr, .Qq "\ " ); gives .Qq XX XX XX ... .Ed .El .Sh CHARACTER TESTS AND CONVERSIONS .Bl -hang -width 10n .It Xo .Ft int .Fn isupper "int c" .Xc .It Xo .Ft int .Fn islower "int c" .Xc .It Xo .Ft int .Fn isspace "int c" .Xc .It Xo .Ft int .Fn isdigit "int c" .Xc .It Xo .Ft int .Fn isxdigit "int c" .Xc .It Xo .Ft int .Fn isascii "int c" .Xc .It Xo .Ft int .Fn isalpha "int c" .Xc .It Xo .Ft int .Fn isalnum "int c" .Xc .It Xo .Ft int .Fn iscntrl "int c" .Xc .It Xo .Ft int .Fn isgraph "int c" .Xc .It Xo .Ft int .Fn ispunct "int c" .Xc .It Xo .Ft int .Fn toupper "int c" .Xc .It Xo .Ft int .Fn tolower "int c" .Xc .El .Sh FILE I/O .Bl -hang -width 10n .It Xo .Ft int .Fn open "const char *path" "int flags" .Xc .Pp Similar to the behaviour as specified in .Xr open 2 , except that file creation is not supported, so the mode parameter is not required. The .Fa flags argument may be one of O_RDONLY, O_WRONLY and O_RDWR. Only UFS currently supports writing. .It Xo .Ft int .Fn close "int fd" .Xc .It Xo .Ft void .Fn closeall void .Xc .Pp Close all open files. .It Xo .Ft ssize_t .Fn read "int fd" "void *buf" "size_t len" .Xc .It Xo .Ft ssize_t .Fn write "int fd" "void *buf" "size_t len" .Xc .Pp (No file systems currently support writing.) .It Xo .Ft off_t .Fn lseek "int fd" "off_t offset" "int whence" .Xc .Pp Files being automatically uncompressed during reading cannot seek backwards from the current point. .It Xo .Ft int .Fn stat "const char *path" "struct stat *sb" .Xc .It Xo .Ft int .Fn fstat "int fd" "struct stat *sb" .Xc .Pp The .Fn stat and .Fn fstat functions only fill out the following fields in the .Fa sb structure: st_mode,st_nlink,st_uid,st_gid,st_size. The .Nm tftp file system cannot provide meaningful values for this call, and the .Nm cd9660 file system always reports files having uid/gid of zero. .El .Sh PAGER The .Nm library supplies a simple internal pager to ease reading the output of large commands. .Bl -hang -width 10n .It Xo .Ft void .Fn pager_open .Xc .Pp Initialises the pager and tells it that the next line output will be the top of the display. The environment variable LINES is consulted to determine the number of lines to be displayed before pausing. .It Xo .Ft void .Fn pager_close void .Xc .Pp Closes the pager. .It Xo .Ft int .Fn pager_output "const char *lines" .Xc .Pp Sends the lines in the .Dv NUL Ns -terminated buffer at .Fa lines to the pager. Newline characters are counted in order to determine the number of lines being output (wrapped lines are not accounted for). The .Fn pager_output function will return zero when all of the lines have been output, or nonzero if the display was paused and the user elected to quit. .It Xo .Ft int .Fn pager_file "const char *fname" .Xc .Pp Attempts to open and display the file .Fa fname . Returns -1 on error, 0 at EOF, or 1 if the user elects to quit while reading. .El +.Sh FEATURE SUPPORT +A set of functions are provided to communicate support of features from the +loader binary to the interpreter. +These are used to do something sensible if we are still operating with a loader +binary that behaves differently than expected. +.Bl -hang -width 10n +.It Xo +.Ft void +.Fn feature_enable "uint32_t mask" +.Xc +.Pp +Enable the referenced +.Fa mask +feature, which should be one of the +.Li FEATURE_* +macros defined in +.In stand.h . +.It Xo +.Ft bool +.Fn feature_name_is_enabled "const char *name" +.Xc +.Pp +Check if the referenced +.Fa name +feature is enabled. +The +.Fa name +is usually the same name as the +.Li FEATURE_* +macro, but with the FEATURE_ prefix stripped off. +The authoritative source of feature names is the mapping table near the top in +.Pa stand/libsa/features.c . +.It Xo +.Ft void +.Fn "(feature_iter_fn)" "void *cookie" "const char *name" "const char *desc" "bool enabled" +.Xc +.Pp +The +.Fa cookie +argument is passed as-is from the argument of the same name to +.Fn feature_iter . +The +.Fa name +and +.Fa desc +arguments are defined in the mapping table in +.Pa stand/libsa/features.c . +The +.Fa enabled +argument indicates the current status of the feature, though one could +theoretically turn a feature off in later execution. +As such, this should likely not be trusted if it is needed after the iteration +has finished. +.It Xo +.Ft void +.Fn "feature_iter" "feature_iter_fn *iter_fn" "void *cookie" +.Xc +.Pp +Iterate over the current set of features. +.El .Sh MISC .Bl -hang -width 10n .It Xo .Ft char * .Fn devformat "struct devdesc *" .Xc .Pp Format the specified device as a string. .It Xo .Ft int .Fn devparse "struct devdesc **dev" "const char *devdesc" "const char **path" .Xc .Pp Parse the .Dv devdesc string of the form .Sq device:[/path/to/file] . The .Dv devsw table is used to match the start of the .Sq device string with .Fa dv_name . If .Fa dv_parsedev is non-NULL, then it will be called to parse the rest of the string and allocate the .Dv struct devdesc for this path. If NULL, then a default routine will be called that will allocate a simple .Dv struct devdesc , parse a unit number and ensure there's no trailing characters. If .Dv path is non-NULL, then a pointer to the remainder of the .Dv devdesc string after the device specification is written. .It Xo .Ft int .Fn devinit void Calls all the .Fa dv_init routines in the .Dv devsw array, returning the number of routines that returned an error. .It Xo .Ft void .Fn twiddle void .Xc .Pp Successive calls emit the characters in the sequence |,/,-,\\ followed by a backspace in order to provide reassurance to the user. .El .Sh REQUIRED LOW-LEVEL SUPPORT The following resources are consumed by .Nm - stack, heap, console and devices. .Pp The stack must be established before .Nm functions can be invoked. Stack requirements vary depending on the functions and file systems used by the consumer and the support layer functions detailed below. .Pp The heap must be established before calling .Fn alloc or .Fn open by calling .Fn setheap . Heap usage will vary depending on the number of simultaneously open files, as well as client behaviour. Automatic decompression will allocate more than 64K of data per open file. .Pp Console access is performed via the .Fn getchar , .Fn putchar and .Fn ischar functions detailed below. .Pp Device access is initiated via .Fn devopen and is performed through the .Fn dv_strategy , .Fn dv_ioctl and .Fn dv_close functions in the device switch structure that .Fn devopen returns. .Pp The consumer must provide the following support functions: .Bl -hang -width 10n .It Xo .Ft int .Fn getchar void .Xc .Pp Return a character from the console, used by .Fn gets , .Fn ngets and pager functions. .It Xo .Ft int .Fn ischar void .Xc .Pp Returns nonzero if a character is waiting from the console. .It Xo .Ft void .Fn putchar int .Xc .Pp Write a character to the console, used by .Fn gets , .Fn ngets , .Fn *printf , .Fn panic and .Fn twiddle and thus by many other functions for debugging and informational output. .It Xo .Ft int .Fn devopen "struct open_file *of" "const char *name" "const char **file" .Xc .Pp Open the appropriate device for the file named in .Fa name , returning in .Fa file a pointer to the remaining body of .Fa name which does not refer to the device. The .Va f_dev field in .Fa of will be set to point to the .Vt devsw structure for the opened device if successful. Device identifiers must always precede the path component, but may otherwise be arbitrarily formatted. Used by .Fn open and thus for all device-related I/O. .It Xo .Ft int .Fn devclose "struct open_file *of" .Xc .Pp Close the device allocated for .Fa of . The device driver itself will already have been called for the close; this call should clean up any allocation made by devopen only. .It Xo .Ft void .Fn __abort .Xc .Pp Calls .Fn panic with a fixed string. .It Xo .Ft void .Fn panic "const char *msg" "..." .Xc .Pp Signal a fatal and unrecoverable error condition. The .Fa msg ... arguments are as for .Fn printf . .El .Sh INTERNAL FILE SYSTEMS Internal file systems are enabled by the consumer exporting the array .Vt struct fs_ops *file_system[] , which should be initialised with pointers to .Vt struct fs_ops structures. The following file system handlers are supplied by .Nm , the consumer may supply other file systems of their own: .Bl -hang -width ".Va cd9660_fsops" .It Va ufs_fsops The .Bx UFS. .It Va ext2fs_fsops Linux ext2fs file system. .It Va tftp_fsops File access via TFTP. .It Va nfs_fsops File access via NFS. .It Va cd9660_fsops ISO 9660 (CD-ROM) file system. .It Va gzipfs_fsops Stacked file system supporting gzipped files. When trying the gzipfs file system, .Nm appends .Li .gz to the end of the filename, and then tries to locate the file using the other file systems. Placement of this file system in the .Va file_system[] array determines whether gzipped files will be opened in preference to non-gzipped files. It is only possible to seek a gzipped file forwards, and .Fn stat and .Fn fstat on gzipped files will report an invalid length. .It Va bzipfs_fsops The same as .Va gzipfs_fsops , but for .Xr bzip2 1 Ns -compressed files. .El .Pp The array of .Vt struct fs_ops pointers should be terminated with a NULL. .Sh DEVICES Devices are exported by the supporting code via the array .Vt struct devsw *devsw[] which is a NULL terminated array of pointers to device switch structures. .Sh DRIVER INTERFACE The driver needs to provide a common set of entry points that are used by .Nm libsa to interface with the device. .Bd -literal struct devsw { const char dv_name[DEV_NAMLEN]; int dv_type; int (*dv_init)(void); int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); int (*dv_open)(struct open_file *f, ...); int (*dv_close)(struct open_file *f); int (*dv_ioctl)(struct open_file *f, u_long cmd, void *data); int (*dv_print)(int verbose); void (*dv_cleanup)(void); char * (*dv_fmtdev)(struct devdesc *); int (*dv_parsedev)(struct devdesc **dev, const char *devpart, const char **path); bool (*dv_match)(struct devsw *dv, const char *devspec); }; .Ed .Bl -tag -width ".Fn dv_strategy" .It Fn dv_name The device's name. .It Fn dv_type Type of device. The supported types are: .Bl -tag -width "DEVT_NONE" .It DEVT_NONE .It DEVT_DISK .It DEVT_NET .It DEVT_CD .It DEVT_ZFS .It DEVT_FD .El Each type may have its own associated (struct type_devdesc), which has the generic (struct devdesc) as its first member. .It Fn dv_init Driver initialization routine. This routine should probe for available units. Drivers are responsible for maintaining lists of units for later enumeration. No other driver routines may be called before .Fn dv_init returns. .It Fn dv_open The driver open routine. .It Fn dv_close The driver close routine. .It Fn dv_ioctl The driver ioctl routine. .It Fn dv_print Prints information about the available devices. Information should be presented with .Fn pager_output . .It Fn dv_cleanup Cleans up any memory used by the device before the next stage is run. .It Fn dv_fmtdev Converts the specified devdesc to the canonical string representation for that device. .It Fn dv_parsedev Parses the device portion of a file path. The .Dv devpart will point to the .Sq tail of device name, possibly followed by a colon and a path within the device. The .Sq tail is, by convention, the part of the device specification that follows the .Fa dv_name part of the string. So when .Fa devparse is parsing the string .Dq disk3p5:/xxx , .Dv devpart will point to the .Sq 3 in that string. The parsing routine is expected to allocate a new .Dv struct devdesc or subclass and return it in .Dv dev when successful. This routine should set .Dv path to point to the portion of the string after device specification, or .Dq /xxx in the earlier example. Generally, code needing to parse a path will use .Fa devparse instead of calling this routine directly. .It Fn dv_match .Dv NULL to specify that all device paths starting with .Fa dv_name match. Otherwise, this function returns 0 for a match and a non-zero .Dv errno to indicate why it didn't match. This is helpful when you claim the device path after using it to query properties on systems that have uniform naming for different types of devices. .El .Sh HISTORY The .Nm library contains contributions from many sources, including: .Bl -bullet -compact .It .Nm libsa from .Nx .It .Nm libc and .Nm libkern from .Fx 3.0 . .It .Nm zalloc from .An Matthew Dillon Aq Mt dillon@backplane.com .El .Pp The reorganisation and port to .Fx 3.0 , the environment functions and this manpage were written by .An Mike Smith Aq Mt msmith@FreeBSD.org . .Sh BUGS The lack of detailed memory usage data is unhelpful. diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h index b6ddebb82fd7..f500a8f47847 100644 --- a/stand/libsa/stand.h +++ b/stand/libsa/stand.h @@ -1,543 +1,558 @@ /* * Copyright (c) 1998 Michael Smith. * 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. * From $NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ #ifndef STAND_H #define STAND_H #include #include #include #include #include /* this header intentionally exports NULL from */ #include #define strcoll(a, b) strcmp((a), (b)) #define CHK(fmt, args...) printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args) #define PCHK(fmt, args...) {printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args); getchar();} #include /* special stand error codes */ #define EADAPT (ELAST+1) /* bad adaptor */ #define ECTLR (ELAST+2) /* bad controller */ #define EUNIT (ELAST+3) /* bad unit */ #define ESLICE (ELAST+4) /* bad slice */ #define EPART (ELAST+5) /* bad partition */ #define ERDLAB (ELAST+6) /* can't read disk label */ #define EUNLAB (ELAST+7) /* unlabeled disk */ #define EOFFSET (ELAST+8) /* relative seek not supported */ #define ESALAST (ELAST+8) /* */ /* Partial signal emulation for sig_atomic_t */ #include __BEGIN_DECLS struct open_file; /* * This structure is used to define file system operations in a file system * independent way. * * XXX note that filesystem providers should export a pointer to their fs_ops * struct, so that consumers can reference this and thus include the * filesystems that they require. */ struct fs_ops { const char *fs_name; int (*fo_open)(const char *path, struct open_file *f); int (*fo_close)(struct open_file *f); int (*fo_read)(struct open_file *f, void *buf, size_t size, size_t *resid); int (*fo_write)(struct open_file *f, const void *buf, size_t size, size_t *resid); off_t (*fo_seek)(struct open_file *f, off_t offset, int where); int (*fo_stat)(struct open_file *f, struct stat *sb); int (*fo_readdir)(struct open_file *f, struct dirent *d); int (*fo_preload)(struct open_file *f); int (*fo_mount)(const char *, const char *, void **); int (*fo_unmount)(const char *, void *); }; /* * libsa-supplied filesystems */ extern struct fs_ops ufs_fsops; extern struct fs_ops tftp_fsops; extern struct fs_ops nfs_fsops; extern struct fs_ops cd9660_fsops; extern struct fs_ops gzipfs_fsops; extern struct fs_ops bzipfs_fsops; extern struct fs_ops dosfs_fsops; extern struct fs_ops ext2fs_fsops; extern struct fs_ops splitfs_fsops; extern struct fs_ops pkgfs_fsops; extern struct fs_ops efihttp_fsops; /* where values for lseek(2) */ #define SEEK_SET 0 /* set file offset to offset */ #define SEEK_CUR 1 /* set file offset to current plus offset */ #define SEEK_END 2 /* set file offset to EOF plus offset */ /* * Device switch */ #define DEV_NAMLEN 8 /* Length of name of device class */ #define DEV_DEVLEN 128 /* Length of longest device instance name */ struct devdesc; struct devsw { const char dv_name[DEV_NAMLEN]; int dv_type; /* opaque type constant */ #define DEVT_NONE 0 #define DEVT_DISK 1 #define DEVT_NET 2 #define DEVT_CD 3 #define DEVT_ZFS 4 #define DEVT_FD 5 int (*dv_init)(void); /* early probe call */ int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); int (*dv_open)(struct open_file *f, ...); int (*dv_close)(struct open_file *f); int (*dv_ioctl)(struct open_file *f, u_long cmd, void *data); int (*dv_print)(int verbose); /* print device information */ void (*dv_cleanup)(void); char * (*dv_fmtdev)(struct devdesc *); int (*dv_parsedev)(struct devdesc **, const char *, const char **); bool (*dv_match)(struct devsw *, const char *); }; /* * libsa-supplied device switch */ extern struct devsw netdev; extern int errno; /* * Generic device specifier; architecture-dependent versions may be larger, but * should be allowed to overlap. The larger device specifiers store more data * than can fit in the generic one that's gleaned after parsing the device * string, or used in some cases to indicate wildcards that match a variety of * situations based on what's on the drive itself rather than what the progammer * might know in advance. Information about open files is stored in d_opendata, * though what's passed into the open routine may differ from what's present * after the open on some configurations. */ struct devdesc { struct devsw *d_dev; int d_unit; void *d_opendata; }; char *devformat(struct devdesc *d); int devparse(struct devdesc **, const char *, const char **); int devinit(void); void dev_cleanup(void); struct open_file { int f_flags; /* see F_* below */ struct devsw *f_dev; /* pointer to device operations */ void *f_devdata; /* device specific data */ struct fs_ops *f_ops; /* pointer to file system operations */ void *f_fsdata; /* file system specific data */ off_t f_offset; /* current file offset */ char *f_rabuf; /* readahead buffer pointer */ size_t f_ralen; /* valid data in readahead buffer */ off_t f_raoffset; /* consumer offset in readahead buffer */ int f_id; /* file number */ TAILQ_ENTRY(open_file) f_link; /* next entry */ #define SOPEN_RASIZE 512 }; typedef TAILQ_HEAD(file_list, open_file) file_list_t; extern file_list_t files; extern struct open_file *fd2open_file(int); /* f_flags values */ #define F_READ 0x0001 /* file opened for reading */ #define F_WRITE 0x0002 /* file opened for writing */ #define F_RAW 0x0004 /* raw device open - no file system */ #define F_NODEV 0x0008 /* network open - no device */ #define F_MASK 0xFFFF /* Mode modifier for strategy() */ #define F_NORA (0x01 << 16) /* Disable Read-Ahead */ #define isascii(c) (((c) & ~0x7F) == 0) static __inline int isupper(int c) { return c >= 'A' && c <= 'Z'; } static __inline int islower(int c) { return c >= 'a' && c <= 'z'; } static __inline int isspace(int c) { return c == ' ' || (c >= 0x9 && c <= 0xd); } static __inline int isdigit(int c) { return c >= '0' && c <= '9'; } static __inline int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } static __inline int isalpha(int c) { return isupper(c) || islower(c); } static __inline int isalnum(int c) { return isalpha(c) || isdigit(c); } static __inline int iscntrl(int c) { return (c >= 0 && c < ' ') || c == 127; } static __inline int isgraph(int c) { return c >= '!' && c <= '~'; } static __inline int ispunct(int c) { return (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~'); } static __inline int toupper(int c) { return islower(c) ? c - 'a' + 'A' : c; } static __inline int tolower(int c) { return isupper(c) ? c - 'A' + 'a' : c; } /* sbrk emulation */ extern void setheap(void *base, void *top); extern char *sbrk(int incr); extern int printf(const char *fmt, ...) __printflike(1, 2); extern int asprintf(char **buf, const char *cfmt, ...) __printflike(2, 3); extern int sprintf(char *buf, const char *cfmt, ...) __printflike(2, 3); extern int snprintf(char *buf, size_t size, const char *cfmt, ...) __printflike(3, 4); extern int vprintf(const char *fmt, __va_list); extern int vsprintf(char *buf, const char *cfmt, __va_list); extern int vsnprintf(char *buf, size_t size, const char *cfmt, __va_list); extern void twiddle(u_int callerdiv); extern void twiddle_divisor(u_int globaldiv); extern void ngets(char *, int); #define gets(x) ngets((x), 0) extern int fgetstr(char *buf, int size, int fd); extern int mount(const char *dev, const char *path, int flags, void *data); extern int unmount(const char *dev, int flags); extern int open(const char *, int); #define O_RDONLY 0x0 #define O_WRONLY 0x1 #define O_RDWR 0x2 #define O_ACCMODE 0x3 /* NOT IMPLEMENTED */ #define O_CREAT 0x0200 /* create if nonexistent */ #define O_TRUNC 0x0400 /* truncate to zero length */ extern int close(int); extern void closeall(void); extern ssize_t read(int, void *, size_t); extern ssize_t write(int, const void *, size_t); extern int ioctl(int, u_long, void *); extern struct dirent *readdirfd(int); extern void preload(int); extern void srandom(unsigned int); extern long random(void); /* imports from stdlib, locally modified */ extern char *optarg; /* getopt(3) external variables */ extern int optind, opterr, optopt, optreset; extern int getopt(int, char * const [], const char *); /* pager.c */ extern void pager_open(void); extern void pager_close(void); extern int pager_output(const char *lines); extern int pager_file(const char *fname); /* No signal state to preserve */ #define setjmp _setjmp #define longjmp _longjmp /* environment.c */ #define EV_DYNAMIC (1<<0) /* value was dynamically allocated, free if changed/unset */ #define EV_VOLATILE (1<<1) /* value is volatile, make a copy of it */ #define EV_NOHOOK (1<<2) /* don't call hook when setting */ struct env_var; typedef char *(ev_format_t)(struct env_var *ev); typedef int (ev_sethook_t)(struct env_var *ev, int flags, const void *value); typedef int (ev_unsethook_t)(struct env_var *ev); struct env_var { char *ev_name; int ev_flags; void *ev_value; ev_sethook_t *ev_sethook; ev_unsethook_t *ev_unsethook; struct env_var *ev_next, *ev_prev; }; extern struct env_var *environ; extern struct env_var *env_getenv(const char *name); extern int env_setenv(const char *name, int flags, const void *value, ev_sethook_t sethook, ev_unsethook_t unsethook); extern void env_discard(struct env_var *); extern char *getenv(const char *name); extern int setenv(const char *name, const char *value, int overwrite); extern int putenv(char *string); extern int unsetenv(const char *name); extern ev_sethook_t env_noset; /* refuse set operation */ extern ev_unsethook_t env_nounset; /* refuse unset operation */ /* stdlib.h routines */ extern int abs(int a); extern void abort(void) __dead2; extern long strtol(const char * __restrict, char ** __restrict, int); extern long long strtoll(const char * __restrict, char ** __restrict, int); extern unsigned long strtoul(const char * __restrict, char ** __restrict, int); extern unsigned long long strtoull(const char * __restrict, char ** __restrict, int); /* BCD conversions (undocumented) */ extern u_char const bcd2bin_data[]; extern u_char const bin2bcd_data[]; extern char const hex2ascii_data[]; #define bcd2bin(bcd) (bcd2bin_data[bcd]) #define bin2bcd(bin) (bin2bcd_data[bin]) #define hex2ascii(hex) (hex2ascii_data[hex]) #define validbcd(bcd) (bcd == 0 || (bcd > 0 && bcd <= 0x99 && bcd2bin_data[bcd] != 0)) /* min/max (undocumented) */ static __inline int imax(int a, int b) { return (a > b ? a : b); } static __inline int imin(int a, int b) { return (a < b ? a : b); } static __inline long lmax(long a, long b) { return (a > b ? a : b); } static __inline long lmin(long a, long b) { return (a < b ? a : b); } static __inline u_int max(u_int a, u_int b) { return (a > b ? a : b); } static __inline u_int min(u_int a, u_int b) { return (a < b ? a : b); } static __inline quad_t qmax(quad_t a, quad_t b) { return (a > b ? a : b); } static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); } static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); } static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); } /* null functions for device/filesystem switches (undocumented) */ extern int nodev(void); extern int noioctl(struct open_file *, u_long, void *); extern void nullsys(void); extern int null_open(const char *path, struct open_file *f); extern int null_close(struct open_file *f); extern int null_read(struct open_file *f, void *buf, size_t size, size_t *resid); extern int null_write(struct open_file *f, const void *buf, size_t size, size_t *resid); extern off_t null_seek(struct open_file *f, off_t offset, int where); extern int null_stat(struct open_file *f, struct stat *sb); extern int null_readdir(struct open_file *f, struct dirent *d); /* * Machine dependent functions and data, must be provided or stubbed by * the consumer */ extern void exit(int) __dead2; extern int getchar(void); extern int ischar(void); extern void putchar(int); extern int devopen(struct open_file *, const char *, const char **); extern int devclose(struct open_file *f); extern void panic(const char *, ...) __dead2 __printflike(1, 2); extern void panic_action(void) __weak_symbol __dead2; extern time_t getsecs(void); extern struct fs_ops *file_system[]; extern struct fs_ops *exclusive_file_system; extern struct devsw *devsw[]; /* * Time routines */ time_t time(time_t *); /* * Expose byteorder(3) functions. */ #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED extern uint32_t htonl(uint32_t); extern uint16_t htons(uint16_t); extern uint32_t ntohl(uint32_t); extern uint16_t ntohs(uint16_t); #endif #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif void *Malloc(size_t, const char *, int); void *Memalign(size_t, size_t, const char *, int); void *Calloc(size_t, size_t, const char *, int); void *Realloc(void *, size_t, const char *, int); void *Reallocf(void *, size_t, const char *, int); void Free(void *, const char *, int); extern void mallocstats(void); const char *x86_hypervisor(void); #ifdef USER_MALLOC extern void *malloc(size_t); extern void *memalign(size_t, size_t); extern void *calloc(size_t, size_t); extern void free(void *); extern void *realloc(void *, size_t); extern void *reallocf(void *, size_t); #elif defined(DEBUG_MALLOC) #define malloc(x) Malloc(x, __FILE__, __LINE__) #define memalign(x, y) Memalign(x, y, __FILE__, __LINE__) #define calloc(x, y) Calloc(x, y, __FILE__, __LINE__) #define free(x) Free(x, __FILE__, __LINE__) #define realloc(x, y) Realloc(x, y, __FILE__, __LINE__) #define reallocf(x, y) Reallocf(x, y, __FILE__, __LINE__) #else #define malloc(x) Malloc(x, NULL, 0) #define memalign(x, y) Memalign(x, y, NULL, 0) #define calloc(x, y) Calloc(x, y, NULL, 0) #define free(x) Free(x, NULL, 0) #define realloc(x, y) Realloc(x, y, NULL, 0) #define reallocf(x, y) Reallocf(x, y, NULL, 0) #endif /* * va <-> pa routines. MD code must supply. */ caddr_t ptov(uintptr_t); +/* features.c */ +typedef void (feature_iter_fn)(void *, const char *, const char *, bool); + +extern void feature_enable(uint32_t); +extern bool feature_name_is_enabled(const char *); +extern void feature_iter(feature_iter_fn *, void *); + +/* + * Note that these should also be added to the mapping table in features.c, + * which the interpreter may query to provide details from. The name with + * FEATURE_ removed is assumed to be the name we'll provide in the loader + * features table, just to simplify reasoning about these. + */ +#define FEATURE_EARLY_ACPI 0x0001 + /* hexdump.c */ void hexdump(caddr_t region, size_t len); /* nvstore.c */ typedef int (nvstore_getter_cb_t)(void *, const char *, void **); typedef int (nvstore_setter_cb_t)(void *, int, const char *, const void *, size_t); typedef int (nvstore_setter_str_cb_t)(void *, const char *, const char *, const char *); typedef int (nvstore_unset_cb_t)(void *, const char *); typedef int (nvstore_print_cb_t)(void *, void *); typedef int (nvstore_iterate_cb_t)(void *, int (*)(void *, void *)); typedef struct nvs_callbacks { nvstore_getter_cb_t *nvs_getter; nvstore_setter_cb_t *nvs_setter; nvstore_setter_str_cb_t *nvs_setter_str; nvstore_unset_cb_t *nvs_unset; nvstore_print_cb_t *nvs_print; nvstore_iterate_cb_t *nvs_iterate; } nvs_callbacks_t; int nvstore_init(const char *, nvs_callbacks_t *, void *); int nvstore_fini(const char *); void *nvstore_get_store(const char *); int nvstore_print(void *); int nvstore_get_var(void *, const char *, void **); int nvstore_set_var(void *, int, const char *, void *, size_t); int nvstore_set_var_from_string(void *, const char *, const char *, const char *); int nvstore_unset_var(void *, const char *); /* tslog.c */ #define TSRAW(a, b, c) tslog(a, b, c) #define TSENTER() TSRAW("ENTER", __func__, NULL) #define TSENTER2(x) TSRAW("ENTER", __func__, x) #define TSEXIT() TSRAW("EXIT", __func__, NULL) #define TSLINE() TSRAW("EVENT", __FILE__, __XSTRING(__LINE__)) void tslog(const char *, const char *, const char *); void tslog_setbuf(void * buf, size_t len); void tslog_getbuf(void ** buf, size_t * len); __END_DECLS #endif /* STAND_H */ diff --git a/stand/lua/core.lua b/stand/lua/core.lua index 5c3c65463e51..3a80b0822ca6 100644 --- a/stand/lua/core.lua +++ b/stand/lua/core.lua @@ -1,517 +1,526 @@ -- -- SPDX-License-Identifier: BSD-2-Clause -- -- 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. -- local config = require("config") local hook = require("hook") local core = {} local default_acpi = false local default_safe_mode = false local default_single_user = false local default_verbose = false local bootenv_list = "bootenvs" local function composeLoaderCmd(cmd_name, argstr) if argstr ~= nil then cmd_name = cmd_name .. " " .. argstr end return cmd_name end local function recordDefaults() local boot_single = loader.getenv("boot_single") or "no" local boot_verbose = loader.getenv("boot_verbose") or "no" default_acpi = core.getACPI() default_single_user = boot_single:lower() ~= "no" default_verbose = boot_verbose:lower() ~= "no" core.setACPI(default_acpi) core.setSingleUser(default_single_user) core.setVerbose(default_verbose) end -- Globals -- try_include will return the loaded module on success, or false and the error -- message on failure. function try_include(module) if module:sub(1, 1) ~= "/" then local lua_path = loader.lua_path -- XXX Temporary compat shim; this should be removed once the -- loader.lua_path export has sufficiently spread. if lua_path == nil then lua_path = "/boot/lua" end module = lua_path .. "/" .. module -- We only attempt to append an extension if an absolute path -- wasn't specified. This assumes that the caller either wants -- to treat this like it would require() and specify just the -- base filename, or they know what they're doing as they've -- specified an absolute path and we shouldn't impede. if module:match(".lua$") == nil then module = module .. ".lua" end end if lfs.attributes(module, "mode") ~= "file" then return end return dofile(module) end -- Module exports -- Commonly appearing constants core.KEY_BACKSPACE = 8 core.KEY_ENTER = 13 core.KEY_DELETE = 127 -- Note that this is a decimal representation, despite the leading 0 that in -- other contexts (outside of Lua) may mean 'octal' core.KEYSTR_ESCAPE = "\027" core.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" core.KEYSTR_RESET = core.KEYSTR_ESCAPE .. "c" core.MENU_RETURN = "return" core.MENU_ENTRY = "entry" core.MENU_SEPARATOR = "separator" core.MENU_SUBMENU = "submenu" core.MENU_CAROUSEL_ENTRY = "carousel_entry" function core.setVerbose(verbose) if verbose == nil then verbose = not core.verbose end if verbose then loader.setenv("boot_verbose", "YES") else loader.unsetenv("boot_verbose") end core.verbose = verbose end function core.setSingleUser(single_user) if single_user == nil then single_user = not core.su end if single_user then loader.setenv("boot_single", "YES") else loader.unsetenv("boot_single") end core.su = single_user end function core.hasACPI() return loader.getenv("acpi.rsdp") ~= nil end function core.isX86() return loader.machine_arch == "i386" or loader.machine_arch == "amd64" end function core.getACPI() if not core.hasACPI() then -- x86 requires ACPI pretty much return false or core.isX86() end -- Otherwise, respect disabled if it's set local c = loader.getenv("hint.acpi.0.disabled") return c == nil or tonumber(c) ~= 1 end function core.setACPI(acpi) if acpi == nil then acpi = not core.acpi end if acpi then loader.setenv("acpi_load", "YES") loader.setenv("hint.acpi.0.disabled", "0") loader.unsetenv("loader.acpi_disabled_by_user") else loader.unsetenv("acpi_load") loader.setenv("hint.acpi.0.disabled", "1") loader.setenv("loader.acpi_disabled_by_user", "1") end core.acpi = acpi end function core.setSafeMode(safe_mode) if safe_mode == nil then safe_mode = not core.sm end if safe_mode then loader.setenv("kern.smp.disabled", "1") loader.setenv("hw.ata.ata_dma", "0") loader.setenv("hw.ata.atapi_dma", "0") loader.setenv("kern.eventtimer.periodic", "1") loader.setenv("kern.geom.part.check_integrity", "0") else loader.unsetenv("kern.smp.disabled") loader.unsetenv("hw.ata.ata_dma") loader.unsetenv("hw.ata.atapi_dma") loader.unsetenv("kern.eventtimer.periodic") loader.unsetenv("kern.geom.part.check_integrity") end core.sm = safe_mode end function core.clearCachedKernels() -- Clear the kernel cache on config changes, autodetect might have -- changed or if we've switched boot environments then we could have -- a new kernel set. core.cached_kernels = nil end function core.kernelList() if core.cached_kernels ~= nil then return core.cached_kernels end local k = loader.getenv("kernel") local v = loader.getenv("kernels") local autodetect = loader.getenv("kernels_autodetect") or "" local kernels = {} local unique = {} local i = 0 if k ~= nil then i = i + 1 kernels[i] = k unique[k] = true end if v ~= nil then for n in v:gmatch("([^;, ]+)[;, ]?") do if unique[n] == nil then i = i + 1 kernels[i] = n unique[n] = true end end end -- Do not attempt to autodetect if underlying filesystem -- do not support directory listing (e.g. tftp, http) if not lfs.attributes("/boot", "mode") then autodetect = "no" loader.setenv("kernels_autodetect", "NO") end -- Base whether we autodetect kernels or not on a loader.conf(5) -- setting, kernels_autodetect. If it's set to 'yes', we'll add -- any kernels we detect based on the criteria described. if autodetect:lower() ~= "yes" then core.cached_kernels = kernels return core.cached_kernels end -- Automatically detect other bootable kernel directories using a -- heuristic. Any directory in /boot that contains an ordinary file -- named "kernel" is considered eligible. for file, ftype in lfs.dir("/boot") do local fname = "/boot/" .. file if file == "." or file == ".." then goto continue end if ftype then if ftype ~= lfs.DT_DIR then goto continue end elseif lfs.attributes(fname, "mode") ~= "directory" then goto continue end if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then goto continue end if unique[file] == nil then i = i + 1 kernels[i] = file unique[file] = true end ::continue:: end core.cached_kernels = kernels return core.cached_kernels end function core.bootenvDefault() return loader.getenv("zfs_be_active") end function core.bootenvList() local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count")) local bootenvs = {} local curenv local envcount = 0 local unique = {} if bootenv_count == nil or bootenv_count <= 0 then return bootenvs end -- Currently selected bootenv is always first/default -- On the rewinded list the bootenv may not exists if core.isRewinded() then curenv = core.bootenvDefaultRewinded() else curenv = core.bootenvDefault() end if curenv ~= nil then envcount = envcount + 1 bootenvs[envcount] = curenv unique[curenv] = true end for curenv_idx = 0, bootenv_count - 1 do curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]") if curenv ~= nil and unique[curenv] == nil then envcount = envcount + 1 bootenvs[envcount] = curenv unique[curenv] = true end end return bootenvs end function core.isCheckpointed() return loader.getenv("zpool_checkpoint") ~= nil end function core.bootenvDefaultRewinded() local defname = "zfs:!" .. string.sub(core.bootenvDefault(), 5) local bootenv_count = tonumber("bootenvs_check_count") if bootenv_count == nil or bootenv_count <= 0 then return defname end for curenv_idx = 0, bootenv_count - 1 do local curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]") if curenv == defname then return defname end end return loader.getenv("bootenvs_check[0]") end function core.isRewinded() return bootenv_list == "bootenvs_check" end function core.changeRewindCheckpoint() if core.isRewinded() then bootenv_list = "bootenvs" else bootenv_list = "bootenvs_check" end end function core.loadEntropy() if core.isUEFIBoot() then if (loader.getenv("entropy_efi_seed") or "no"):lower() == "yes" then loader.perform("efi-seed-entropy") end end end function core.setDefaults() core.setACPI(default_acpi) core.setSafeMode(default_safe_mode) core.setSingleUser(default_single_user) core.setVerbose(default_verbose) end function core.autoboot(argstr) -- loadelf() only if we've not already loaded a kernel if loader.getenv("kernelname") == nil then config.loadelf() end core.loadEntropy() loader.perform(composeLoaderCmd("autoboot", argstr)) end function core.boot(argstr) -- loadelf() only if we've not already loaded a kernel if loader.getenv("kernelname") == nil then config.loadelf() end core.loadEntropy() loader.perform(composeLoaderCmd("boot", argstr)) end +function core.hasFeature(name) + if not loader.has_feature then + -- Loader too old, no feature support + return nil, "No feature support in loaded loader" + end + + return loader.has_feature(name) +end + function core.isSingleUserBoot() local single_user = loader.getenv("boot_single") return single_user ~= nil and single_user:lower() == "yes" end function core.isUEFIBoot() local efiver = loader.getenv("efi-version") return efiver ~= nil end function core.isZFSBoot() local c = loader.getenv("currdev") if c ~= nil then return c:match("^zfs:") ~= nil end return false end function core.isFramebufferConsole() local c = loader.getenv("console") if c ~= nil then if c:find("efi") == nil and c:find("vidconsole") == nil then return false end if loader.getenv("screen.depth") ~= nil then return true end end return false end function core.isSerialConsole() local c = loader.getenv("console") if c ~= nil then -- serial console is comconsole, but also userboot. -- userboot is there, because we have no way to know -- if the user terminal can draw unicode box chars or not. if c:find("comconsole") ~= nil or c:find("userboot") ~= nil then return true end end return false end function core.isSerialBoot() local s = loader.getenv("boot_serial") if s ~= nil then return true end local m = loader.getenv("boot_multicons") if m ~= nil then return true end return false end -- Is the menu skipped in the environment in which we've booted? function core.isMenuSkipped() return string.lower(loader.getenv("beastie_disable") or "") == "yes" end -- This may be a better candidate for a 'utility' module. function core.deepCopyTable(tbl) local new_tbl = {} for k, v in pairs(tbl) do if type(v) == "table" then new_tbl[k] = core.deepCopyTable(v) else new_tbl[k] = v end end return new_tbl end -- XXX This should go away if we get the table lib into shape for importing. -- As of now, it requires some 'os' functions, so we'll implement this in lua -- for our uses function core.popFrontTable(tbl) -- Shouldn't reasonably happen if #tbl == 0 then return nil, nil elseif #tbl == 1 then return tbl[1], {} end local first_value = tbl[1] local new_tbl = {} -- This is not a cheap operation for k, v in ipairs(tbl) do if k > 1 then new_tbl[k - 1] = v end end return first_value, new_tbl end function core.getConsoleName() if loader.getenv("boot_multicons") ~= nil then if loader.getenv("boot_serial") ~= nil then return "Dual (Serial primary)" else return "Dual (Video primary)" end else if loader.getenv("boot_serial") ~= nil then return "Serial" else return "Video" end end end function core.nextConsoleChoice() if loader.getenv("boot_multicons") ~= nil then if loader.getenv("boot_serial") ~= nil then loader.unsetenv("boot_serial") else loader.unsetenv("boot_multicons") loader.setenv("boot_serial", "YES") end else if loader.getenv("boot_serial") ~= nil then loader.unsetenv("boot_serial") else loader.setenv("boot_multicons", "YES") loader.setenv("boot_serial", "YES") end end end recordDefaults() hook.register("config.reloaded", core.clearCachedKernels) return core diff --git a/stand/lua/core.lua.8 b/stand/lua/core.lua.8 index 5274ba4c2143..84447d7632b9 100644 --- a/stand/lua/core.lua.8 +++ b/stand/lua/core.lua.8 @@ -1,225 +1,232 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .\" Copyright (c) 2018 Kyle Evans .\" .\" 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. .\" -.Dd November 20, 2023 +.Dd December 8, 2023 .Dt CORE.LUA 8 .Os .Sh NAME .Nm core.lua .Nd FreeBSD core module .Sh DESCRIPTION .Nm contains core functionality that does not have a more fitting module. .Pp Before hooking into or using the functionality provided by .Nm , it must be included with a statement such as the following: .Pp .Dl local core = require("core") .Ss CONSTANTS The following raw key code constants are defined in .Nm : .Bl -tag -width KEY_BACKSPACE -compact -offset indent .It Ic KEY_BACKSPACE The backspace code. Should generally be checked along with .Ic KEY_DELETE for backspace functionality. .It Ic KEY_ENTER The enter key, or hard return. .It Ic KEY_DELETE The delete code. Should generally be checked along with .Ic KEY_BACKSPACE for backspace functionality. .El .Pp The following key-string constants are defined in .Nm : .Bl -tag -width KEYSTR_ESCAPE -compact -offset indent .It Ic KEYSTR_ESCAPE The escape key. .It Ic KEYSTR_CSI The ANSI CSI sequence. .El .Pp The following menu entry type constants are defined in .Nm : .Bl -tag -width MENU_CAROUSEL_ENTRY -compact -offset indent .It Ic MENU_RETURN Return to the parent menu. .It Ic MENU_ENTRY A normal menu entry. .It Ic MENU_SEPARATOR A menu entry that serves as a separator. .It Ic MENU_SUBMENU A menu entry that opens a submenu when selected. .It Ic MENU_CAROUSEL_ENTRY A menu entry that rotates through items like a carousel upon selection of the menu entry. .El .Pp Please see .Xr menu.lua 8 for extended descriptions and usage of the .Ic MENU_* constants. .Ss Exported functions The following functions are exported from .Nm : .Bl -tag -width core.setSingleUser -offset indent .It Fn core.setVerbose verbose Sets or unsets .Ev boot_verbose . If .Fa verbose is omitted, toggle the current verbose setting. .It Fn core.setSingleUser singleUser Sets or unsets .Ev boot_single . If .Fa singleUser is omitted, toggle the current single user setting. .It Fn core.getACPI Return true if ACPI is both present and not explicitly disabled by .Ev hints.acpi.0.disabled . .It Fn core.hasACPI Checks whether ACPI support is present. This requires the loader to probe the system and set .Ev acpi.rsdp early before starting the interpreter. .It Fn core.setACPI acpi Sets or unsets .Ev acpi_load , .Ev hint.acpi.0.disabled , and .Ev loader.acpi_disabled_by_user . If .Fa acpi is omitted, toggle the current ACPI setting. .It Fn core.setSafeMode safeMode Set the safe mode setting. Sets or unsets .Ev kern.smp.disabled , .Ev hw.ata.ata_dma , .Ev hw.ata.atapi_dma , .Ev kern.eventtimer.periodic , and .Ev kern.geom.part.check_integrity . If .Fa safeMode is omitted, toggle the current safe mode setting. .It Fn core.clearCachedKernels Clears out the cache of kernels to be displayed on the boot menu. This function is registered as a .Ev config.reloaded hook. It is used to invalidate the kernel list whenever it may have changed, either due to a boot environment change or a potential change in either .Ic kernel or .Ic kernels . +.It Fn core.hasFeature featureName +Checks whether the named +.Fa featureName +is enabled in the current loader. +This is specifically used for detecting capabilities of the loaded loader +binary, so that one can reasonably implement backwards compatibility shims if +needed. .It Fn core.kernelList Returns a table of kernels to display on the boot menu. This will combine .Ic kernel and .Ic kernels from .Xr loader.conf 5 . If .Ic kernels_autodetect is set in .Xr loader.conf 5 , kernels will be autodetected from the current system. .It Fn core.bootenvDefault Returns the default boot environment, nil if unset. .It Fn core.bootenvList Returns a table of boot environments, or an empty table. These will be picked up using the .Ev bootenvs and .Ev bootenvs_count variables set by .Xr loader 8 . .It Fn core.setDefaults Resets ACPI, safe mode, single user, and verbose settings to their system defauilts. .It Fn core.autoboot argstr Loads the kernel and specified modules, then invokes the .Ic autoboot .Xr loader 8 command with .Fa argstr as-is. .It Fn core.boot argstr Loads the kernel and specified modules, then invokes the .Ic boot .Xr loader 8 command with .Fa argstr as-is. .It Fn core.isSingleUserBoot Returns true if .Ev boot_single is set to yes. .It Fn core.isZFSBoot Returns true if .Ev currdev is set to a .Xr zfs 8 dataset. .It Fn core.isSerialBoot Returns true if we are booting over serial. This checks .Ev console , .Ev boot_serial , and .Ev boot_multicons . .It Fn core.deepCopyTable tbl Recursively deep copies .Fa tbl and returns the result. .It Fn core.popFrontTable tbl Pops the front element off of .Fa tbl , and returns two return values: the front element, and the rest of the table. If there are no elements, this returns nil and nil. If there is one element, this returns the front element and an empty table. This will not operate on truly associative tables; numeric indices are required. .El .Sh SEE ALSO .Xr loader.conf 5 , .Xr loader 8 , .Xr menu.lua 8 .Sh AUTHORS The .Nm file was originally written by .An Pedro Souza Aq Mt pedrosouza@FreeBSD.org . Later work and this manual page was done by .An Kyle Evans Aq Mt kevans@FreeBSD.org .