Index: head/stand/common/bootstrap.h =================================================================== --- head/stand/common/bootstrap.h +++ head/stand/common/bootstrap.h @@ -164,6 +164,14 @@ extern char bootprog_info[]; /* + * Interpreter information + */ +extern const char bootprog_interp[]; +#define INTERP_DEFINE(interpstr) \ +const char bootprog_interp[] = "$Interpreter:" interpstr + + +/* * Preloaded file metadata header. * * Metadata are allocated on our heap, and copied into kernel space Index: head/stand/common/interp_forth.c =================================================================== --- head/stand/common/interp_forth.c +++ head/stand/common/interp_forth.c @@ -34,6 +34,7 @@ #include "ficl.h" extern unsigned bootprog_rev; +INTERP_DEFINE("4th"); /* #define BFORTH_DEBUG */ Index: head/stand/common/interp_lua.c =================================================================== --- head/stand/common/interp_lua.c +++ head/stand/common/interp_lua.c @@ -60,6 +60,7 @@ #define LDBG(...) #endif +INTERP_DEFINE("lua"); static void * interp_lua_realloc(void *ud __unused, void *ptr, size_t osize __unused, size_t nsize) Index: head/stand/common/interp_simple.c =================================================================== --- head/stand/common/interp_simple.c +++ head/stand/common/interp_simple.c @@ -35,6 +35,8 @@ #include #include "bootstrap.h" +INTERP_DEFINE("simp"); + void interp_init(void) { Index: head/stand/userboot/Makefile =================================================================== --- head/stand/userboot/Makefile +++ head/stand/userboot/Makefile @@ -1,8 +1,10 @@ # $FreeBSD$ -.include +.include -SUBDIR= test userboot +SUBDIR.yes= test +SUBDIR.${MK_FORTH}+= userboot_4th +SUBDIR.${MK_LOADER_LUA}+= userboot_lua .include Index: head/stand/userboot/userboot.h =================================================================== --- head/stand/userboot/userboot.h +++ head/stand/userboot/userboot.h @@ -42,6 +42,13 @@ #define USERBOOT_VERSION_4 4 /* + * Version 5 added a callback for indicating that the guest + * should be restarted with a different interpreter. The callback + * structure is still backward compatible. + */ +#define USERBOOT_VERSION_5 5 + +/* * Exit codes from the loader */ #define USERBOOT_EXIT_QUIT 1 @@ -210,4 +217,9 @@ int (*vm_set_register)(void *arg, int vcpu, int reg, uint64_t val); int (*vm_set_desc)(void *arg, int vcpu, int reg, uint64_t base, u_int limit, u_int access); + + /* + * Version 5 addition. + */ + void (*swap_interpreter)(void *arg, const char *interp); }; Index: head/stand/userboot/userboot/Makefile =================================================================== --- head/stand/userboot/userboot/Makefile +++ head/stand/userboot/userboot/Makefile @@ -5,14 +5,14 @@ LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no PIC=yes -LOADER_INTERP=4th .include -SHLIB_NAME= userboot.so +SHLIB_NAME= userboot_${LOADER_INTERP}.so STRIP= LIBDIR= /boot +.PATH: ${.CURDIR}/../userboot SRCS= autoload.c SRCS+= bcache.c SRCS+= biossmap.c @@ -38,7 +38,12 @@ LDFLAGS+= -nostdlib -Wl,-Bsymbolic -NEWVERSWHAT= "User boot" ${MACHINE_CPUARCH} +NEWVERSWHAT= "User boot ${LOADER_INTERP}" ${MACHINE_CPUARCH} +VERSION_FILE= ${.CURDIR}/../userboot/version + +.if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} +LINKS+= ${BINDIR}/${SHLIB_NAME} ${BINDIR}/userboot.so +.endif .if ${MK_ZFS} != "no" CFLAGS+= -DUSERBOOT_ZFS_SUPPORT Index: head/stand/userboot/userboot/main.c =================================================================== --- head/stand/userboot/userboot/main.c +++ head/stand/userboot/userboot/main.c @@ -47,6 +47,9 @@ /* Minimum version required */ #define USERBOOT_VERSION USERBOOT_VERSION_3 +#define LOADER_PATH "/boot/loader" +#define INTERP_MARKER "$Interpreter:" + #define MALLOCSZ (64*1024*1024) struct loader_callbacks *callbacks; @@ -57,6 +60,7 @@ struct arch_switch archsw; /* MI/MD interface boundary */ static void extract_currdev(void); +static void check_interpreter(void); void delay(int usec) @@ -73,6 +77,62 @@ longjmp(jb, 1); } +static void +check_interpreter(void) +{ + struct stat st; + size_t marklen, rdsize; + const char *guest_interp, *my_interp; + char *buf; + int fd; + + /* + * If we can't stat(2) or open(2) LOADER_PATH, then we'll fail by + * simply letting us roll on with whatever interpreter we were compiled + * with. This is likely not going to be an issue in reality. + */ + buf = NULL; + if (stat(LOADER_PATH, &st) != 0) + return; + if ((fd = open(LOADER_PATH, O_RDONLY)) < 0) + return; + + rdsize = st.st_size; + buf = malloc(rdsize); + if (buf == NULL) + goto out; + if (read(fd, buf, rdsize) < rdsize) + goto out; + + marklen = strlen(INTERP_MARKER); + my_interp = bootprog_interp + marklen; + + /* + * Here we make the assumption that a loader binary without the + * interpreter marker is a 4th one. All loader binaries going forward + * should have this properly specified, so our assumption should always + * be a good one. + */ + if ((guest_interp = memmem(buf, rdsize, INTERP_MARKER, + marklen)) != NULL) + guest_interp += marklen; + else + guest_interp = "4th"; + + /* + * The guest interpreter may not have a version of loader that + * specifies the interpreter installed. If that's the case, we'll + * assume it's legacy (4th) and request a swap to that if we're + * a Lua-userboot. + */ + if (strcmp(my_interp, guest_interp) != 0) + CALLBACK(swap_interpreter, guest_interp); +out: + free(buf); + close(fd); + return; +} + void loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks) { @@ -137,6 +197,14 @@ (devsw[i]->dv_init)(); extract_currdev(); + + /* + * Checking the interpreter isn't worth the overhead unless we + * actually have the swap_interpreter callback, so we actually version + * check here rather than later on. + */ + if (version >= USERBOOT_VERSION_5) + check_interpreter(); if (setjmp(jb)) return; Index: head/stand/userboot/userboot/version =================================================================== --- head/stand/userboot/userboot/version +++ head/stand/userboot/userboot/version @@ -1,4 +1,5 @@ $FreeBSD$ +1.2: Userboot with lua or forth 1.1: Initial userland boot Index: head/stand/userboot/userboot_4th/Makefile =================================================================== --- head/stand/userboot/userboot_4th/Makefile +++ head/stand/userboot/userboot_4th/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +LOADER_INTERP=4th + +.include "../userboot/Makefile" + Index: head/stand/userboot/userboot_lua/Makefile =================================================================== --- head/stand/userboot/userboot_lua/Makefile +++ head/stand/userboot/userboot_lua/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +LOADER_INTERP=lua + +.include "../userboot/Makefile" + Index: head/usr.sbin/bhyveload/bhyveload.c =================================================================== --- head/usr.sbin/bhyveload/bhyveload.c +++ head/usr.sbin/bhyveload/bhyveload.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,13 @@ static int ndisks; static int consin_fd, consout_fd; +static int need_reinit; + +static void *loader_hdl; +static char *loader; +static int explicit_loader; +static jmp_buf jb; + static char *vmname, *progname; static struct vmctx *ctx; @@ -560,6 +568,30 @@ return (vm_set_desc(ctx, vcpu, reg, base, limit, access)); } +static void +cb_swap_interpreter(void *arg, const char *interp_req) +{ + + /* + * If the user specified a loader but we detected a mismatch, we should + * not try to pivot to a different loader on them. + */ + free(loader); + if (explicit_loader == 1) { + perror("requested loader interpreter does not match guest userboot"); + cb_exit(NULL, 1); + } + if (interp_req == NULL || *interp_req == '\0') { + perror("guest failed to request an interpreter"); + cb_exit(NULL, 1); + } + + if (asprintf(&loader, "/boot/userboot_%s.so", interp_req) == -1) + err(EX_OSERR, "malloc"); + need_reinit = 1; + longjmp(jb, 1); +} + static struct loader_callbacks cb = { .getc = cb_getc, .putc = cb_putc, @@ -593,6 +625,9 @@ /* Version 4 additions */ .vm_set_register = cb_vm_set_register, .vm_set_desc = cb_vm_set_desc, + + /* Version 5 additions */ + .swap_interpreter = cb_swap_interpreter, }; static int @@ -661,16 +696,12 @@ int main(int argc, char** argv) { - char *loader; - void *h; void (*func)(struct loader_callbacks *, void *, int, int); uint64_t mem_size; - int opt, error, need_reinit, memflags; + int opt, error, memflags; progname = basename(argv[0]); - loader = NULL; - memflags = 0; mem_size = 256 * MB; @@ -705,6 +736,7 @@ loader = strdup(optarg); if (loader == NULL) err(EX_OSERR, "malloc"); + explicit_loader = 1; break; case 'm': @@ -747,6 +779,13 @@ exit(1); } + /* + * setjmp in the case the guest wants to swap out interpreter, + * cb_swap_interpreter will swap out loader as appropriate and set + * need_reinit so that we end up in a clean state once again. + */ + setjmp(jb); + if (need_reinit) { error = vm_reinit(ctx); if (error) { @@ -767,13 +806,15 @@ if (loader == NULL) err(EX_OSERR, "malloc"); } - h = dlopen(loader, RTLD_LOCAL); - if (!h) { + if (loader_hdl != NULL) + dlclose(loader_hdl); + loader_hdl = dlopen(loader, RTLD_LOCAL); + if (!loader_hdl) { printf("%s\n", dlerror()); free(loader); return (1); } - func = dlsym(h, "loader_main"); + func = dlsym(loader_hdl, "loader_main"); if (!func) { printf("%s\n", dlerror()); free(loader); @@ -790,7 +831,7 @@ addenv("smbios.bios.vendor=BHYVE"); addenv("boot_serial=1"); - func(&cb, NULL, USERBOOT_VERSION_4, ndisks); + func(&cb, NULL, USERBOOT_VERSION_5, ndisks); free(loader); return (0);