diff options
| author | Warner Losh <imp@FreeBSD.org> | 2017-11-14 23:02:19 +0000 |
|---|---|---|
| committer | Warner Losh <imp@FreeBSD.org> | 2017-11-14 23:02:19 +0000 |
| commit | ca987d4641cdcd7f27e153db17c5bf064934faf5 (patch) | |
| tree | 6c3860e3ba8949be9528d644fbb7fa88d8bbbb79 /stand/userboot | |
| parent | 6eac7115560381ce5c9e2939ab3fce82bb9b6a95 (diff) | |
Notes
Diffstat (limited to 'stand/userboot')
24 files changed, 3243 insertions, 0 deletions
diff --git a/stand/userboot/Makefile b/stand/userboot/Makefile new file mode 100644 index 000000000000..0a53a217dc15 --- /dev/null +++ b/stand/userboot/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +SUBDIR= test userboot + +.include <bsd.subdir.mk> + diff --git a/stand/userboot/Makefile.inc b/stand/userboot/Makefile.inc new file mode 100644 index 000000000000..265f86d1ed55 --- /dev/null +++ b/stand/userboot/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +.include "../Makefile.inc" diff --git a/stand/userboot/test/Makefile b/stand/userboot/test/Makefile new file mode 100644 index 000000000000..1bc66798aac8 --- /dev/null +++ b/stand/userboot/test/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + + +MAN= + +.include <bsd.init.mk> +MK_SSP= no + +PROG= test +INTERNALPROG= + +CFLAGS+= -I${BOOTSRC}/userboot + +.include <bsd.prog.mk> diff --git a/stand/userboot/test/Makefile.depend b/stand/userboot/test/Makefile.depend new file mode 100644 index 000000000000..6cfaab1c3644 --- /dev/null +++ b/stand/userboot/test/Makefile.depend @@ -0,0 +1,17 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/userboot/test/test.c b/stand/userboot/test/test.c new file mode 100644 index 000000000000..bacde565ec3b --- /dev/null +++ b/stand/userboot/test/test.c @@ -0,0 +1,475 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/disk.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <dirent.h> +#include <dlfcn.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include <userboot.h> + +char *host_base = NULL; +struct termios term, oldterm; +char *image; +size_t image_size; +int disk_fd = -1; + +uint64_t regs[16]; +uint64_t pc; + +void test_exit(void *arg, int v); + +/* + * Console i/o + */ + +void +test_putc(void *arg, int ch) +{ + char c = ch; + + write(1, &c, 1); +} + +int +test_getc(void *arg) +{ + char c; + + if (read(0, &c, 1) == 1) + return c; + return -1; +} + +int +test_poll(void *arg) +{ + int n; + + if (ioctl(0, FIONREAD, &n) >= 0) + return (n > 0); + return (0); +} + +/* + * Host filesystem i/o + */ + +struct test_file { + int tf_isdir; + size_t tf_size; + struct stat tf_stat; + union { + int fd; + DIR *dir; + } tf_u; +}; + +int +test_open(void *arg, const char *filename, void **h_return) +{ + struct stat st; + struct test_file *tf; + char path[PATH_MAX]; + + if (!host_base) + return (ENOENT); + + strlcpy(path, host_base, PATH_MAX); + if (path[strlen(path) - 1] == '/') + path[strlen(path) - 1] = 0; + strlcat(path, filename, PATH_MAX); + tf = malloc(sizeof(struct test_file)); + if (stat(path, &tf->tf_stat) < 0) { + free(tf); + return (errno); + } + + tf->tf_size = st.st_size; + if (S_ISDIR(tf->tf_stat.st_mode)) { + tf->tf_isdir = 1; + tf->tf_u.dir = opendir(path); + if (!tf->tf_u.dir) + goto out; + *h_return = tf; + return (0); + } + if (S_ISREG(tf->tf_stat.st_mode)) { + tf->tf_isdir = 0; + tf->tf_u.fd = open(path, O_RDONLY); + if (tf->tf_u.fd < 0) + goto out; + *h_return = tf; + return (0); + } + +out: + free(tf); + return (EINVAL); +} + +int +test_close(void *arg, void *h) +{ + struct test_file *tf = h; + + if (tf->tf_isdir) + closedir(tf->tf_u.dir); + else + close(tf->tf_u.fd); + free(tf); + + return (0); +} + +int +test_isdir(void *arg, void *h) +{ + struct test_file *tf = h; + + return (tf->tf_isdir); +} + +int +test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return) +{ + struct test_file *tf = h; + ssize_t sz; + + if (tf->tf_isdir) + return (EINVAL); + sz = read(tf->tf_u.fd, dst, size); + if (sz < 0) + return (EINVAL); + *resid_return = size - sz; + return (0); +} + +int +test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, + size_t *namelen_return, char *name) +{ + struct test_file *tf = h; + struct dirent *dp; + + if (!tf->tf_isdir) + return (EINVAL); + + dp = readdir(tf->tf_u.dir); + if (!dp) + return (ENOENT); + + /* + * Note: d_namlen is in the range 0..255 and therefore less + * than PATH_MAX so we don't need to test before copying. + */ + *fileno_return = dp->d_fileno; + *type_return = dp->d_type; + *namelen_return = dp->d_namlen; + memcpy(name, dp->d_name, dp->d_namlen); + name[dp->d_namlen] = 0; + + return (0); +} + +int +test_seek(void *arg, void *h, uint64_t offset, int whence) +{ + struct test_file *tf = h; + + if (tf->tf_isdir) + return (EINVAL); + if (lseek(tf->tf_u.fd, offset, whence) < 0) + return (errno); + return (0); +} + +int +test_stat(void *arg, void *h, int *mode_return, int *uid_return, int *gid_return, + uint64_t *size_return) +{ + struct test_file *tf = h; + + *mode_return = tf->tf_stat.st_mode; + *uid_return = tf->tf_stat.st_uid; + *gid_return = tf->tf_stat.st_gid; + *size_return = tf->tf_stat.st_size; + return (0); +} + +/* + * Disk image i/o + */ + +int +test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size, + size_t *resid_return) +{ + ssize_t n; + + if (unit != 0 || disk_fd == -1) + return (EIO); + n = pread(disk_fd, dst, size, offset); + if (n < 0) + return (errno); + *resid_return = size - n; + return (0); +} + +int +test_diskioctl(void *arg, int unit, u_long cmd, void *data) +{ + struct stat sb; + + if (unit != 0 || disk_fd == -1) + return (EBADF); + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = 512; + break; + case DIOCGMEDIASIZE: + if (fstat(disk_fd, &sb) == 0) + *(off_t *)data = sb.st_size; + else + return (ENOTTY); + break; + default: + return (ENOTTY); + } + return (0); +} + +/* + * Guest virtual machine i/o + * + * Note: guest addresses are kernel virtual + */ + +int +test_copyin(void *arg, const void *from, uint64_t to, size_t size) +{ + + to &= 0x7fffffff; + if (to > image_size) + return (EFAULT); + if (to + size > image_size) + size = image_size - to; + memcpy(&image[to], from, size); + return(0); +} + +int +test_copyout(void *arg, uint64_t from, void *to, size_t size) +{ + + from &= 0x7fffffff; + if (from > image_size) + return (EFAULT); + if (from + size > image_size) + size = image_size - from; + memcpy(to, &image[from], size); + return(0); +} + +void +test_setreg(void *arg, int r, uint64_t v) +{ + + if (r < 0 || r >= 16) + return; + regs[r] = v; +} + +void +test_setmsr(void *arg, int r, uint64_t v) +{ +} + +void +test_setcr(void *arg, int r, uint64_t v) +{ +} + +void +test_setgdt(void *arg, uint64_t v, size_t sz) +{ +} + +void +test_exec(void *arg, uint64_t pc) +{ + printf("Execute at 0x%"PRIu64"\n", pc); + test_exit(arg, 0); +} + +/* + * Misc + */ + +void +test_delay(void *arg, int usec) +{ + + usleep(usec); +} + +void +test_exit(void *arg, int v) +{ + + tcsetattr(0, TCSAFLUSH, &oldterm); + exit(v); +} + +void +test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem) +{ + + *lowmem = 128*1024*1024; + *highmem = 0; +} + +const char * +test_getenv(void *arg, int idx) +{ + static const char *vars[] = { + "foo=bar", + "bar=barbar", + NULL + }; + + return (vars[idx]); +} + +struct loader_callbacks cb = { + .putc = test_putc, + .getc = test_getc, + .poll = test_poll, + + .open = test_open, + .close = test_close, + .isdir = test_isdir, + .read = test_read, + .readdir = test_readdir, + .seek = test_seek, + .stat = test_stat, + + .diskread = test_diskread, + .diskioctl = test_diskioctl, + + .copyin = test_copyin, + .copyout = test_copyout, + .setreg = test_setreg, + .setmsr = test_setmsr, + .setcr = test_setcr, + .setgdt = test_setgdt, + .exec = test_exec, + + .delay = test_delay, + .exit = test_exit, + .getmem = test_getmem, + + .getenv = test_getenv, +}; + +void +usage() +{ + + printf("usage: [-b <userboot shared object>] [-d <disk image path>] [-h <host filesystem path>\n"); + exit(1); +} + +int +main(int argc, char** argv) +{ + void *h; + void (*func)(struct loader_callbacks *, void *, int, int); + int opt; + char *disk_image = NULL; + const char *userboot_obj = "/boot/userboot.so"; + + while ((opt = getopt(argc, argv, "b:d:h:")) != -1) { + switch (opt) { + case 'b': + userboot_obj = optarg; + break; + + case 'd': + disk_image = optarg; + break; + + case 'h': + host_base = optarg; + break; + + case '?': + usage(); + } + } + + h = dlopen(userboot_obj, RTLD_LOCAL); + if (!h) { + printf("%s\n", dlerror()); + return (1); + } + func = dlsym(h, "loader_main"); + if (!func) { + printf("%s\n", dlerror()); + return (1); + } + + image_size = 128*1024*1024; + image = malloc(image_size); + if (disk_image) { + disk_fd = open(disk_image, O_RDONLY); + if (disk_fd < 0) + err(1, "Can't open disk image '%s'", disk_image); + } + + tcgetattr(0, &term); + oldterm = term; + term.c_iflag &= ~(ICRNL); + term.c_lflag &= ~(ICANON|ECHO); + tcsetattr(0, TCSAFLUSH, &term); + + func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0); +} diff --git a/stand/userboot/userboot.h b/stand/userboot/userboot.h new file mode 100644 index 000000000000..7ffb06f652aa --- /dev/null +++ b/stand/userboot/userboot.h @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2011 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. + * + * $FreeBSD$ + */ + +/* + * USERBOOT interface versions + */ +#define USERBOOT_VERSION_1 1 +#define USERBOOT_VERSION_2 2 +#define USERBOOT_VERSION_3 3 + +/* + * Version 4 added more generic callbacks for setting up + * registers and descriptors. The callback structure is + * backward compatible (new callbacks have been added at + * the tail end). + */ +#define USERBOOT_VERSION_4 4 + +/* + * Exit codes from the loader + */ +#define USERBOOT_EXIT_QUIT 1 +#define USERBOOT_EXIT_REBOOT 2 + +struct loader_callbacks { + /* + * Console i/o + */ + + /* + * Wait until a key is pressed on the console and then return it + */ + int (*getc)(void *arg); + + /* + * Write the character ch to the console + */ + void (*putc)(void *arg, int ch); + + /* + * Return non-zero if a key can be read from the console + */ + int (*poll)(void *arg); + + /* + * Host filesystem i/o + */ + + /* + * Open a file in the host filesystem + */ + int (*open)(void *arg, const char *filename, void **h_return); + + /* + * Close a file + */ + int (*close)(void *arg, void *h); + + /* + * Return non-zero if the file is a directory + */ + int (*isdir)(void *arg, void *h); + + /* + * Read size bytes from a file. The number of bytes remaining + * in dst after reading is returned in *resid_return + */ + int (*read)(void *arg, void *h, void *dst, size_t size, + size_t *resid_return); + + /* + * Read an entry from a directory. The entry's inode number is + * returned in *fileno_return, its type in *type_return and + * the name length in *namelen_return. The name itself is + * copied to the buffer name which must be at least PATH_MAX + * in size. + */ + int (*readdir)(void *arg, void *h, uint32_t *fileno_return, + uint8_t *type_return, size_t *namelen_return, char *name); + + /* + * Seek to a location within an open file + */ + int (*seek)(void *arg, void *h, uint64_t offset, + int whence); + + /* + * Return some stat(2) related information about the file + */ + int (*stat)(void *arg, void *h, int *mode_return, + int *uid_return, int *gid_return, uint64_t *size_return); + + /* + * Disk image i/o + */ + + /* + * Read from a disk image at the given offset + */ + int (*diskread)(void *arg, int unit, uint64_t offset, + void *dst, size_t size, size_t *resid_return); + + /* + * Guest virtual machine i/o + */ + + /* + * Copy to the guest address space + */ + int (*copyin)(void *arg, const void *from, + uint64_t to, size_t size); + + /* + * Copy from the guest address space + */ + int (*copyout)(void *arg, uint64_t from, + void *to, size_t size); + + /* + * Set a guest register value + */ + void (*setreg)(void *arg, int, uint64_t); + + /* + * Set a guest MSR value + */ + void (*setmsr)(void *arg, int, uint64_t); + + /* + * Set a guest CR value + */ + void (*setcr)(void *arg, int, uint64_t); + + /* + * Set the guest GDT address + */ + void (*setgdt)(void *arg, uint64_t, size_t); + + /* + * Transfer control to the guest at the given address + */ + void (*exec)(void *arg, uint64_t pc); + + /* + * Misc + */ + + /* + * Sleep for usec microseconds + */ + void (*delay)(void *arg, int usec); + + /* + * Exit with the given exit code + */ + void (*exit)(void *arg, int v); + + /* + * Return guest physical memory map details + */ + void (*getmem)(void *arg, uint64_t *lowmem, + uint64_t *highmem); + + /* + * ioctl interface to the disk device + */ + int (*diskioctl)(void *arg, int unit, u_long cmd, + void *data); + + /* + * Returns an environment variable in the form "name=value". + * + * If there are no more variables that need to be set in the + * loader environment then return NULL. + * + * 'num' is used as a handle for the callback to identify which + * environment variable to return next. It will begin at 0 and + * each invocation will add 1 to the previous value of 'num'. + */ + const char * (*getenv)(void *arg, int num); + + /* + * Version 4 additions. + */ + 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); +}; diff --git a/stand/userboot/userboot/Makefile b/stand/userboot/userboot/Makefile new file mode 100644 index 000000000000..cbada8227ba0 --- /dev/null +++ b/stand/userboot/userboot/Makefile @@ -0,0 +1,59 @@ +# $FreeBSD$ + +MAN= + +LOADER_MSDOS_SUPPORT?= yes +LOADER_UFS_SUPPORT?= yes +LOADER_CD9660_SUPPORT?= no +LOADER_EXT2FS_SUPPORT?= no + +.include <bsd.init.mk> + +MK_SSP= no + +SHLIB_NAME= userboot.so +MK_CTF= no +STRIP= +LIBDIR= /boot + +SRCS= autoload.c +SRCS+= bcache.c +SRCS+= biossmap.c +SRCS+= bootinfo.c +SRCS+= bootinfo32.c +SRCS+= bootinfo64.c +SRCS+= conf.c +SRCS+= console.c +SRCS+= copy.c +SRCS+= devicename.c +SRCS+= elf32_freebsd.c +SRCS+= elf64_freebsd.c +SRCS+= host.c +SRCS+= main.c +SRCS+= userboot_cons.c +SRCS+= userboot_disk.c +SRCS+= vers.c + +CFLAGS+= -Wall +CFLAGS+= -I${BOOTSRC}/userboot +CFLAGS+= -ffreestanding + +CWARNFLAGS.main.c += -Wno-implicit-function-declaration + +LDFLAGS+= -nostdlib -Wl,-Bsymbolic + +NEWVERSWHAT= "User boot" ${MACHINE_CPUARCH} + +.if ${MK_ZFS} != "no" +CFLAGS+= -DUSERBOOT_ZFS_SUPPORT +LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a +.endif + +# Always add MI sources +HELP_FILES= # Disable +.include "${BOOTSRC}/loader.mk" +CFLAGS+= -I. +DPADD+= ${LIBFICL} ${LIBZFSBOOT} ${LIBSA} +LDADD+= ${LIBFICL} ${LIBZFSBOOT} ${LIBSA} + +.include <bsd.lib.mk> diff --git a/stand/userboot/userboot/Makefile.depend b/stand/userboot/userboot/Makefile.depend new file mode 100644 index 000000000000..87e0acc0f3b5 --- /dev/null +++ b/stand/userboot/userboot/Makefile.depend @@ -0,0 +1,16 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + stand/ficl \ + stand/libsa \ + stand/zfs \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/userboot/userboot/autoload.c b/stand/userboot/userboot/autoload.c new file mode 100644 index 000000000000..a86afcfa2b4b --- /dev/null +++ b/stand/userboot/userboot/autoload.c @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +int +userboot_autoload(void) +{ + + return (0); +} diff --git a/stand/userboot/userboot/biossmap.c b/stand/userboot/userboot/biossmap.c new file mode 100644 index 000000000000..9e556be373cc --- /dev/null +++ b/stand/userboot/userboot/biossmap.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <machine/pc/bios.h> +#include <machine/metadata.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +#define GB (1024UL * 1024 * 1024) + +void +bios_addsmapdata(struct preloaded_file *kfp) +{ + uint64_t lowmem, highmem; + int smapnum, len; + struct bios_smap smap[3], *sm; + + CALLBACK(getmem, &lowmem, &highmem); + + sm = &smap[0]; + + sm->base = 0; /* base memory */ + sm->length = 640 * 1024; + sm->type = SMAP_TYPE_MEMORY; + sm++; + + sm->base = 0x100000; /* extended memory */ + sm->length = lowmem - 0x100000; + sm->type = SMAP_TYPE_MEMORY; + sm++; + + smapnum = 2; + + if (highmem != 0) { + sm->base = 4 * GB; + sm->length = highmem; + sm->type = SMAP_TYPE_MEMORY; + smapnum++; + } + + len = smapnum * sizeof(struct bios_smap); + file_addmetadata(kfp, MODINFOMD_SMAP, len, &smap[0]); +} diff --git a/stand/userboot/userboot/bootinfo.c b/stand/userboot/userboot/bootinfo.c new file mode 100644 index 000000000000..289ca071fe89 --- /dev/null +++ b/stand/userboot/userboot/bootinfo.c @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <sys/boot.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +int +bi_getboothowto(char *kargs) +{ + char *cp; + char *curpos, *next, *string; + int howto; + int active; + int i; + int vidconsole; + + /* Parse kargs */ + howto = 0; + if (kargs != NULL) { + cp = kargs; + active = 0; + while (*cp != 0) { + if (!active && (*cp == '-')) { + active = 1; + } else if (active) + switch (*cp) { + case 'a': + howto |= RB_ASKNAME; + break; + case 'C': + howto |= RB_CDROM; + break; + case 'd': + howto |= RB_KDB; + break; + case 'D': + howto |= RB_MULTIPLE; + break; + case 'm': + howto |= RB_MUTE; + break; + case 'g': + howto |= RB_GDB; + break; + case 'h': + howto |= RB_SERIAL; + break; + case 'p': + howto |= RB_PAUSE; + break; + case 'r': + howto |= RB_DFLTROOT; + break; + case 's': + howto |= RB_SINGLE; + break; + case 'v': + howto |= RB_VERBOSE; + break; + default: + active = 0; + break; + } + cp++; + } + } + /* get equivalents from the environment */ + for (i = 0; howto_names[i].ev != NULL; i++) + if (getenv(howto_names[i].ev) != NULL) + howto |= howto_names[i].mask; + + /* Enable selected consoles */ + string = next = strdup(getenv("console")); + vidconsole = 0; + while (next != NULL) { + curpos = strsep(&next, " ,"); + if (*curpos == '\0') + continue; + if (!strcmp(curpos, "vidconsole")) + vidconsole = 1; + else if (!strcmp(curpos, "comconsole")) + howto |= RB_SERIAL; + else if (!strcmp(curpos, "nullconsole")) + howto |= RB_MUTE; + } + + if (vidconsole && (howto & RB_SERIAL)) + howto |= RB_MULTIPLE; + + /* + * XXX: Note that until the kernel is ready to respect multiple consoles + * for the boot messages, the first named console is the primary console + */ + if (!strcmp(string, "vidconsole")) + howto &= ~RB_SERIAL; + + free(string); + + return(howto); +} + +void +bi_setboothowto(int howto) +{ + int i; + + for (i = 0; howto_names[i].ev != NULL; i++) + if (howto & howto_names[i].mask) + setenv(howto_names[i].ev, "YES", 1); +} + +/* + * Copy the environment into the load area starting at (addr). + * Each variable is formatted as <name>=<value>, with a single nul + * separating each variable, and a double nul terminating the environment. + */ +vm_offset_t +bi_copyenv(vm_offset_t addr) +{ + struct env_var *ep; + + /* traverse the environment */ + for (ep = environ; ep != NULL; ep = ep->ev_next) { + CALLBACK(copyin, ep->ev_name, addr, strlen(ep->ev_name)); + addr += strlen(ep->ev_name); + CALLBACK(copyin, "=", addr, 1); + addr++; + if (ep->ev_value != NULL) { + CALLBACK(copyin, ep->ev_value, addr, strlen(ep->ev_value)); + addr += strlen(ep->ev_value); + } + CALLBACK(copyin, "", addr, 1); + addr++; + } + CALLBACK(copyin, "", addr, 1); + addr++; + return(addr); +} diff --git a/stand/userboot/userboot/bootinfo32.c b/stand/userboot/userboot/bootinfo32.c new file mode 100644 index 000000000000..6498461a740a --- /dev/null +++ b/stand/userboot/userboot/bootinfo32.c @@ -0,0 +1,262 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <i386/include/bootinfo.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +static struct bootinfo bi; + +/* + * Copy module-related data into the load area, where it can be + * used as a directory for loaded modules. + * + * Module data is presented in a self-describing format. Each datum + * is preceded by a 32-bit identifier and a 32-bit size field. + * + * Currently, the following data are saved: + * + * MOD_NAME (variable) module name (string) + * MOD_TYPE (variable) module type (string) + * MOD_ARGS (variable) module parameters (string) + * MOD_ADDR sizeof(vm_offset_t) module load address + * MOD_SIZE sizeof(size_t) module size + * MOD_METADATA (variable) type-specific metadata + */ +#define COPY32(v, a, c) { \ + u_int32_t x = (v); \ + if (c) \ + CALLBACK(copyin, &x, a, sizeof(x)); \ + a += sizeof(x); \ +} + +#define MOD_STR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(strlen(s) + 1, a, c); \ + if (c) \ + CALLBACK(copyin, s, a, strlen(s) + 1); \ + a += roundup(strlen(s) + 1, sizeof(uint32_t));\ +} + +#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) +#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) +#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) + +#define MOD_VAR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(sizeof(s), a, c); \ + if (c) \ + CALLBACK(copyin, &s, a, sizeof(s)); \ + a += roundup(sizeof(s), sizeof(uint32_t)); \ +} + +#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) +#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) + +#define MOD_METADATA(a, mm, c) { \ + COPY32(MODINFO_METADATA | mm->md_type, a, c); \ + COPY32(mm->md_size, a, c); \ + if (c) \ + CALLBACK(copyin, mm->md_data, a, mm->md_size); \ + a += roundup(mm->md_size, sizeof(uint32_t));\ +} + +#define MOD_END(a, c) { \ + COPY32(MODINFO_END, a, c); \ + COPY32(0, a, c); \ +} + +static vm_offset_t +bi_copymodules32(vm_offset_t addr) +{ + struct preloaded_file *fp; + struct file_metadata *md; + int c; + + c = addr != 0; + /* start with the first module on the list, should be the kernel */ + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + + MOD_NAME(addr, fp->f_name, c); /* this field must come first */ + MOD_TYPE(addr, fp->f_type, c); + if (fp->f_args) + MOD_ARGS(addr, fp->f_args, c); + MOD_ADDR(addr, fp->f_addr, c); + MOD_SIZE(addr, fp->f_size, c); + for (md = fp->f_metadata; md != NULL; md = md->md_next) + if (!(md->md_type & MODINFOMD_NOCOPY)) + MOD_METADATA(addr, md, c); + } + MOD_END(addr, c); + return(addr); +} + +/* + * Load the information expected by an i386 kernel. + * + * - The 'boothowto' argument is constructed + * - The 'bootdev' argument is constructed + * - The 'bootinfo' struct is constructed, and copied into the kernel space. + * - The kernel environment is copied into kernel space. + * - Module metadata are formatted and placed in kernel space. + */ +int +bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp) +{ + struct preloaded_file *xp, *kfp; + struct i386_devdesc *rootdev; + struct file_metadata *md; + vm_offset_t addr; + vm_offset_t kernend; + vm_offset_t envp; + vm_offset_t size; + vm_offset_t ssym, esym; + char *rootdevname; + int bootdevnr, howto; + char *kernelname; + const char *kernelpath; + uint64_t lowmem, highmem; + + howto = bi_getboothowto(args); + + /* + * Allow the environment variable 'rootdev' to override the supplied device + * This should perhaps go to MI code and/or have $rootdev tested/set by + * MI code before launching the kernel. + */ + rootdevname = getenv("rootdev"); + userboot_getdev((void **)(&rootdev), rootdevname, NULL); + if (rootdev == NULL) { /* bad $rootdev/$currdev */ + printf("can't determine root device\n"); + return(EINVAL); + } + + /* Try reading the /etc/fstab file to select the root device */ + getrootmount(userboot_fmtdev((void *)rootdev)); + + bootdevnr = 0; +#if 0 + if (bootdevnr == -1) { + printf("root device %s invalid\n", i386_fmtdev(rootdev)); + return (EINVAL); + } +#endif + free(rootdev); + + /* find the last module in the chain */ + addr = 0; + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + /* copy our environment */ + envp = addr; + addr = bi_copyenv(addr); + + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) + kfp = file_findfile(NULL, "elf32 kernel"); + if (kfp == NULL) + panic("can't find kernel file"); + kernend = 0; /* fill it in later */ + file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); + file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); + file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); + bios_addsmapdata(kfp); + + /* Figure out the size and location of the metadata */ + *modulep = addr; + size = bi_copymodules32(0); + kernend = roundup(addr + size, PAGE_SIZE); + *kernendp = kernend; + + /* patch MODINFOMD_KERNEND */ + md = file_findmetadata(kfp, MODINFOMD_KERNEND); + bcopy(&kernend, md->md_data, sizeof kernend); + + /* copy module list and metadata */ + (void)bi_copymodules32(addr); + + ssym = esym = 0; + md = file_findmetadata(kfp, MODINFOMD_SSYM); + if (md != NULL) + ssym = *((vm_offset_t *)&(md->md_data)); + md = file_findmetadata(kfp, MODINFOMD_ESYM); + if (md != NULL) + esym = *((vm_offset_t *)&(md->md_data)); + if (ssym == 0 || esym == 0) + ssym = esym = 0; /* sanity */ + + /* legacy bootinfo structure */ + kernelname = getenv("kernelname"); + userboot_getdev(NULL, kernelname, &kernelpath); + bi.bi_version = BOOTINFO_VERSION; + bi.bi_kernelname = 0; /* XXX char * -> kernel name */ + bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */ + bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */ +#if 0 + for (i = 0; i < N_BIOS_GEOM; i++) + bi.bi_bios_geom[i] = bd_getbigeom(i); +#endif + bi.bi_size = sizeof(bi); + CALLBACK(getmem, &lowmem, &highmem); + bi.bi_memsizes_valid = 1; + bi.bi_basemem = 640; + bi.bi_extmem = (lowmem - 0x100000) / 1024; + bi.bi_envp = envp; + bi.bi_modulep = *modulep; + bi.bi_kernend = kernend; + bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */ + bi.bi_esymtab = esym; + + /* + * Copy the legacy bootinfo and kernel name to the guest at 0x2000 + */ + bi.bi_kernelname = 0x2000 + sizeof(bi); + CALLBACK(copyin, &bi, 0x2000, sizeof(bi)); + CALLBACK(copyin, kernelname, 0x2000 + sizeof(bi), strlen(kernelname) + 1); + + /* legacy boot arguments */ + *howtop = howto | RB_BOOTINFO; + *bootdevp = bootdevnr; + *bip = 0x2000; + + return(0); +} diff --git a/stand/userboot/userboot/bootinfo64.c b/stand/userboot/userboot/bootinfo64.c new file mode 100644 index 000000000000..10ff133ce54c --- /dev/null +++ b/stand/userboot/userboot/bootinfo64.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <i386/include/bootinfo.h> +#include <machine/cpufunc.h> +#include <machine/psl.h> +#include <machine/specialreg.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +/* + * Copy module-related data into the load area, where it can be + * used as a directory for loaded modules. + * + * Module data is presented in a self-describing format. Each datum + * is preceded by a 32-bit identifier and a 32-bit size field. + * + * Currently, the following data are saved: + * + * MOD_NAME (variable) module name (string) + * MOD_TYPE (variable) module type (string) + * MOD_ARGS (variable) module parameters (string) + * MOD_ADDR sizeof(vm_offset_t) module load address + * MOD_SIZE sizeof(size_t) module size + * MOD_METADATA (variable) type-specific metadata + */ +#define COPY32(v, a, c) { \ + u_int32_t x = (v); \ + if (c) \ + CALLBACK(copyin, &x, a, sizeof(x)); \ + a += sizeof(x); \ +} + +#define MOD_STR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(strlen(s) + 1, a, c); \ + if (c) \ + CALLBACK(copyin, s, a, strlen(s) + 1); \ + a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ +} + +#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) +#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) +#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) + +#define MOD_VAR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(sizeof(s), a, c); \ + if (c) \ + CALLBACK(copyin, &s, a, sizeof(s)); \ + a += roundup(sizeof(s), sizeof(u_int64_t)); \ +} + +#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) +#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) + +#define MOD_METADATA(a, mm, c) { \ + COPY32(MODINFO_METADATA | mm->md_type, a, c); \ + COPY32(mm->md_size, a, c); \ + if (c) \ + CALLBACK(copyin, mm->md_data, a, mm->md_size); \ + a += roundup(mm->md_size, sizeof(u_int64_t));\ +} + +#define MOD_END(a, c) { \ + COPY32(MODINFO_END, a, c); \ + COPY32(0, a, c); \ +} + +static vm_offset_t +bi_copymodules64(vm_offset_t addr) +{ + struct preloaded_file *fp; + struct file_metadata *md; + int c; + u_int64_t v; + + c = addr != 0; + /* start with the first module on the list, should be the kernel */ + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + + MOD_NAME(addr, fp->f_name, c); /* this field must come first */ + MOD_TYPE(addr, fp->f_type, c); + if (fp->f_args) + MOD_ARGS(addr, fp->f_args, c); + v = fp->f_addr; + MOD_ADDR(addr, v, c); + v = fp->f_size; + MOD_SIZE(addr, v, c); + for (md = fp->f_metadata; md != NULL; md = md->md_next) + if (!(md->md_type & MODINFOMD_NOCOPY)) + MOD_METADATA(addr, md, c); + } + MOD_END(addr, c); + return(addr); +} + +/* + * Check to see if this CPU supports long mode. + */ +static int +bi_checkcpu(void) +{ +#if 0 + char *cpu_vendor; + int vendor[3]; + int eflags, regs[4]; + + /* Check for presence of "cpuid". */ + eflags = read_eflags(); + write_eflags(eflags ^ PSL_ID); + if (!((eflags ^ read_eflags()) & PSL_ID)) + return (0); + + /* Fetch the vendor string. */ + do_cpuid(0, regs); + vendor[0] = regs[1]; + vendor[1] = regs[3]; + vendor[2] = regs[2]; + cpu_vendor = (char *)vendor; + + /* Check for vendors that support AMD features. */ + if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 && + strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 && + strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0) + return (0); + + /* Has to support AMD features. */ + do_cpuid(0x80000000, regs); + if (!(regs[0] >= 0x80000001)) + return (0); + + /* Check for long mode. */ + do_cpuid(0x80000001, regs); + return (regs[3] & AMDID_LM); +#else + return (1); +#endif +} + +/* + * Load the information expected by an amd64 kernel. + * + * - The 'boothowto' argument is constructed + * - The 'bootdev' argument is constructed + * - The 'bootinfo' struct is constructed, and copied into the kernel space. + * - The kernel environment is copied into kernel space. + * - Module metadata are formatted and placed in kernel space. + */ +int +bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) +{ + struct preloaded_file *xp, *kfp; + struct userboot_devdesc *rootdev; + struct file_metadata *md; + vm_offset_t addr; + u_int64_t kernend; + u_int64_t envp; + vm_offset_t size; + char *rootdevname; + int howto; + + if (!bi_checkcpu()) { + printf("CPU doesn't support long mode\n"); + return (EINVAL); + } + + howto = bi_getboothowto(args); + + /* + * Allow the environment variable 'rootdev' to override the supplied device + * This should perhaps go to MI code and/or have $rootdev tested/set by + * MI code before launching the kernel. + */ + rootdevname = getenv("rootdev"); + userboot_getdev((void **)(&rootdev), rootdevname, NULL); + if (rootdev == NULL) { /* bad $rootdev/$currdev */ + printf("can't determine root device\n"); + return(EINVAL); + } + + /* Try reading the /etc/fstab file to select the root device */ + getrootmount(userboot_fmtdev((void *)rootdev)); + + /* find the last module in the chain */ + addr = 0; + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + /* copy our environment */ + envp = addr; + addr = bi_copyenv(addr); + + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) + kfp = file_findfile(NULL, "elf64 kernel"); + if (kfp == NULL) + panic("can't find kernel file"); + kernend = 0; /* fill it in later */ + file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); + file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); + file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); + bios_addsmapdata(kfp); + + /* Figure out the size and location of the metadata */ + *modulep = addr; + size = bi_copymodules64(0); + kernend = roundup(addr + size, PAGE_SIZE); + *kernendp = kernend; + + /* patch MODINFOMD_KERNEND */ + md = file_findmetadata(kfp, MODINFOMD_KERNEND); + bcopy(&kernend, md->md_data, sizeof kernend); + + /* copy module list and metadata */ + (void)bi_copymodules64(addr); + + return(0); +} diff --git a/stand/userboot/userboot/conf.c b/stand/userboot/userboot/conf.c new file mode 100644 index 000000000000..b8daa7396ca3 --- /dev/null +++ b/stand/userboot/userboot/conf.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1997 + * Matthias Drochner. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Matthias Drochner. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + * + * $NetBSD: conf.c,v 1.2 1997/03/22 09:03:29 thorpej Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> + +#include "libuserboot.h" + +#if defined(USERBOOT_ZFS_SUPPORT) +#include "../zfs/libzfs.h" +#endif + +/* + * We could use linker sets for some or all of these, but + * then we would have to control what ended up linked into + * the bootstrap. So it's easier to conditionalise things + * here. + * + * XXX rename these arrays to be consistent and less namespace-hostile + */ + +/* Exported for libstand */ +struct devsw *devsw[] = { + &host_dev, + &userboot_disk, +#if defined(USERBOOT_ZFS_SUPPORT) + &zfs_dev, +#endif + NULL +}; + +struct fs_ops *file_system[] = { + &host_fsops, + &ufs_fsops, + &cd9660_fsops, +#if defined(USERBOOT_ZFS_SUPPORT) + &zfs_fsops, +#endif + &gzipfs_fsops, + &bzipfs_fsops, + NULL +}; + +/* Exported for i386 only */ +/* + * Sort formats so that those that can detect based on arguments + * rather than reading the file go first. + */ +extern struct file_format i386_elf; +extern struct file_format i386_elf_obj; +extern struct file_format amd64_elf; +extern struct file_format amd64_elf_obj; + +struct file_format *file_formats[] = { + &i386_elf, + &i386_elf_obj, + &amd64_elf, + &amd64_elf_obj, + NULL +}; + +/* + * Consoles + * + * We don't prototype these in libuserboot.h because they require + * data structures from bootstrap.h as well. + */ +extern struct console userboot_console; +extern struct console userboot_comconsole; + +struct console *consoles[] = { + &userboot_console, + &userboot_comconsole, + NULL +}; diff --git a/stand/userboot/userboot/copy.c b/stand/userboot/userboot/copy.c new file mode 100644 index 000000000000..f8763e9b526a --- /dev/null +++ b/stand/userboot/userboot/copy.c @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> + +#include "libuserboot.h" + +ssize_t +userboot_copyin(const void *src, vm_offset_t va, size_t len) +{ + + CALLBACK(copyin, src, va, len); + return (len); +} + +ssize_t +userboot_copyout(vm_offset_t va, void *dst, size_t len) +{ + + CALLBACK(copyout, va, dst, len); + return (len); +} + +ssize_t +userboot_readin(int fd, vm_offset_t va, size_t len) +{ + ssize_t res, s; + size_t sz; + char buf[4096]; + + res = 0; + while (len > 0) { + sz = len; + if (sz > sizeof(buf)) + sz = sizeof(buf); + s = read(fd, buf, sz); + if (s == 0) + break; + if (s < 0) + return (s); + CALLBACK(copyin, buf, va, s); + len -= s; + res += s; + va += s; + } + return (res); +} diff --git a/stand/userboot/userboot/devicename.c b/stand/userboot/userboot/devicename.c new file mode 100644 index 000000000000..efa5634e541e --- /dev/null +++ b/stand/userboot/userboot/devicename.c @@ -0,0 +1,224 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" +#include "disk.h" +#include "libuserboot.h" + +#if defined(USERBOOT_ZFS_SUPPORT) +#include "../zfs/libzfs.h" +#endif + +static int userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path); + +/* + * Point (dev) at an allocated device specifier for the device matching the + * path in (devspec). If it contains an explicit device specification, + * use that. If not, use the default device. + */ +int +userboot_getdev(void **vdev, const char *devspec, const char **path) +{ + struct disk_devdesc **dev = (struct disk_devdesc **)vdev; + int rv; + + /* + * If it looks like this is just a path and no + * device, go with the current device. + */ + if ((devspec == NULL) || + (devspec[0] == '/') || + (strchr(devspec, ':') == NULL)) { + + if (((rv = userboot_parsedev(dev, getenv("currdev"), NULL)) == 0) && + (path != NULL)) + *path = devspec; + return(rv); + } + + /* + * Try to parse the device name off the beginning of the devspec + */ + return(userboot_parsedev(dev, devspec, path)); +} + +/* + * Point (dev) at an allocated device specifier matching the string version + * at the beginning of (devspec). Return a pointer to the remaining + * text in (path). + * + * In all cases, the beginning of (devspec) is compared to the names + * of known devices in the device switch, and then any following text + * is parsed according to the rules applied to the device type. + * + * For disk-type devices, the syntax is: + * + * disk<unit>[s<slice>][<partition>]: + * + */ +static int +userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path) +{ + struct disk_devdesc *idev; + struct devsw *dv; + int i, unit, err; + const char *cp; + const char *np; + + /* minimum length check */ + if (strlen(devspec) < 2) + return(EINVAL); + + /* look for a device that matches */ + for (i = 0, dv = NULL; devsw[i] != NULL; i++) { + if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { + dv = devsw[i]; + break; + } + } + if (dv == NULL) + return(ENOENT); + idev = malloc(sizeof(struct disk_devdesc)); + err = 0; + np = (devspec + strlen(dv->dv_name)); + + switch(dv->dv_type) { + case DEVT_NONE: /* XXX what to do here? Do we care? */ + break; + + case DEVT_DISK: + err = disk_parsedev(idev, np, path); + if (err != 0) + goto fail; + break; + + case DEVT_CD: + case DEVT_NET: + unit = 0; + + if (*np && (*np != ':')) { + unit = strtol(np, (char **)&cp, 0); /* get unit number if present */ + if (cp == np) { + err = EUNIT; + goto fail; + } + } else { + cp = np; + } + if (*cp && (*cp != ':')) { + err = EINVAL; + goto fail; + } + + idev->d_unit = unit; + if (path != NULL) + *path = (*cp == 0) ? cp : cp + 1; + break; + + case DEVT_ZFS: +#if defined(USERBOOT_ZFS_SUPPORT) + err = zfs_parsedev((struct zfs_devdesc *)idev, np, path); + if (err != 0) + goto fail; + break; +#else + /* FALLTHROUGH */ +#endif + + default: + err = EINVAL; + goto fail; + } + idev->d_dev = dv; + idev->d_type = dv->dv_type; + if (dev == NULL) { + free(idev); + } else { + *dev = idev; + } + return(0); + + fail: + free(idev); + return(err); +} + + +char * +userboot_fmtdev(void *vdev) +{ + struct disk_devdesc *dev = (struct disk_devdesc *)vdev; + static char buf[128]; /* XXX device length constant? */ + + switch(dev->d_type) { + case DEVT_NONE: + strcpy(buf, "(no device)"); + break; + + case DEVT_CD: + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); + break; + + case DEVT_DISK: + return (disk_fmtdev(vdev)); + + case DEVT_NET: + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); + break; + + case DEVT_ZFS: +#if defined(USERBOOT_ZFS_SUPPORT) + return (zfs_fmtdev(vdev)); +#else + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); +#endif + break; + } + return(buf); +} + + +/* + * Set currdev to suit the value being supplied in (value) + */ +int +userboot_setcurrdev(struct env_var *ev, int flags, const void *value) +{ + struct disk_devdesc *ncurr; + int rv; + + if ((rv = userboot_parsedev(&ncurr, value, NULL)) != 0) + return(rv); + free(ncurr); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return(0); +} diff --git a/stand/userboot/userboot/elf32_freebsd.c b/stand/userboot/userboot/elf32_freebsd.c new file mode 100644 index 000000000000..d8ccc3375cfd --- /dev/null +++ b/stand/userboot/userboot/elf32_freebsd.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <string.h> +#define _MACHINE_ELF_WANT_32BIT +#include <i386/include/bootinfo.h> +#include <i386/include/elf.h> +#include <stand.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +static int elf32_exec(struct preloaded_file *amp); +static int elf32_obj_exec(struct preloaded_file *amp); + +struct file_format i386_elf = { elf32_loadfile, elf32_exec }; +struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec }; + +#define GUEST_STACK 0x1000 /* Initial stack base */ +#define GUEST_GDT 0x3000 /* Address of initial GDT */ + +/* + * There is an ELF kernel and one or more ELF modules loaded. + * We wish to start executing the kernel image, so make such + * preparations as are required, and do so. + */ +static int +elf32_exec(struct preloaded_file *fp) +{ + struct file_metadata *md; + Elf_Ehdr *ehdr; + vm_offset_t entry, bootinfop, modulep, kernend; + int boothowto, err, bootdev; + uint32_t stack[1024], *sp; + + + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) + return(EFTYPE); + ehdr = (Elf_Ehdr *)&(md->md_data); + + err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); + if (err != 0) + return(err); + entry = ehdr->e_entry & 0xffffff; + +#ifdef DEBUG + printf("Start @ 0x%lx ...\n", entry); +#endif + + dev_cleanup(); + + /* + * Build a scratch stack at physical 0x1000 + */ + memset(stack, 0, sizeof(stack)); + sp = (uint32_t *)((char *)stack + sizeof(stack)); + *--sp = kernend; + *--sp = modulep; + *--sp = bootinfop; + *--sp = 0; + *--sp = 0; + *--sp = 0; + *--sp = bootdev; + *--sp = boothowto; + + /* + * Fake return address to mimic "new" boot blocks. For more + * details see recover_bootinfo in locore.S. + */ + *--sp = 0xbeefface; + CALLBACK(copyin, stack, GUEST_STACK, sizeof(stack)); + CALLBACK(setreg, 4, (char *)sp - (char *)stack + GUEST_STACK); + + CALLBACK(setgdt, GUEST_GDT, 8 * 4 - 1); + + CALLBACK(exec, entry); + + panic("exec returned"); +} + +static int +elf32_obj_exec(struct preloaded_file *fp) +{ + return (EFTYPE); +} diff --git a/stand/userboot/userboot/elf64_freebsd.c b/stand/userboot/userboot/elf64_freebsd.c new file mode 100644 index 000000000000..19d369d6d548 --- /dev/null +++ b/stand/userboot/userboot/elf64_freebsd.c @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <string.h> +#include <i386/include/bootinfo.h> +#include <machine/elf.h> +#include <stand.h> + +#include "bootstrap.h" +#include "libuserboot.h" + +static int elf64_exec(struct preloaded_file *amp); +static int elf64_obj_exec(struct preloaded_file *amp); + +struct file_format amd64_elf = { elf64_loadfile, elf64_exec }; +struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec }; + +#define MSR_EFER 0xc0000080 +#define EFER_LME 0x00000100 +#define EFER_LMA 0x00000400 /* Long mode active (R) */ +#define CR4_PAE 0x00000020 +#define CR4_VMXE (1UL << 13) +#define CR4_PSE 0x00000010 +#define CR0_PG 0x80000000 +#define CR0_PE 0x00000001 /* Protected mode Enable */ +#define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ + +#define PG_V 0x001 +#define PG_RW 0x002 +#define PG_U 0x004 +#define PG_PS 0x080 + +typedef u_int64_t p4_entry_t; +typedef u_int64_t p3_entry_t; +typedef u_int64_t p2_entry_t; + +#define GUEST_NULL_SEL 0 +#define GUEST_CODE_SEL 1 +#define GUEST_DATA_SEL 2 +#define GUEST_GDTR_LIMIT (3 * 8 - 1) + +static void +setup_freebsd_gdt(uint64_t *gdtr) +{ + gdtr[GUEST_NULL_SEL] = 0; + gdtr[GUEST_CODE_SEL] = 0x0020980000000000; + gdtr[GUEST_DATA_SEL] = 0x0000900000000000; +} + +/* + * There is an ELF kernel and one or more ELF modules loaded. + * We wish to start executing the kernel image, so make such + * preparations as are required, and do so. + */ +static int +elf64_exec(struct preloaded_file *fp) +{ + struct file_metadata *md; + Elf_Ehdr *ehdr; + vm_offset_t modulep, kernend; + int err; + int i; + uint32_t stack[1024]; + p4_entry_t PT4[512]; + p3_entry_t PT3[512]; + p2_entry_t PT2[512]; + uint64_t gdtr[3]; + + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) + return(EFTYPE); + ehdr = (Elf_Ehdr *)&(md->md_data); + + err = bi_load64(fp->f_args, &modulep, &kernend); + if (err != 0) + return(err); + + bzero(PT4, PAGE_SIZE); + bzero(PT3, PAGE_SIZE); + bzero(PT2, PAGE_SIZE); + + /* + * Build a scratch stack at physical 0x1000, page tables: + * PT4 at 0x2000, + * PT3 at 0x3000, + * PT2 at 0x4000, + * gdtr at 0x5000, + */ + + /* + * This is kinda brutal, but every single 1GB VM memory segment + * points to the same first 1GB of physical memory. But it is + * more than adequate. + */ + for (i = 0; i < 512; i++) { + /* Each slot of the level 4 pages points to the same level 3 page */ + PT4[i] = (p4_entry_t) 0x3000; + PT4[i] |= PG_V | PG_RW | PG_U; + + /* Each slot of the level 3 pages points to the same level 2 page */ + PT3[i] = (p3_entry_t) 0x4000; + PT3[i] |= PG_V | PG_RW | PG_U; + + /* The level 2 page slots are mapped with 2MB pages for 1GB. */ + PT2[i] = i * (2 * 1024 * 1024); + PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; + } + +#ifdef DEBUG + printf("Start @ %#llx ...\n", ehdr->e_entry); +#endif + + dev_cleanup(); + + stack[0] = 0; /* return address */ + stack[1] = modulep; + stack[2] = kernend; + CALLBACK(copyin, stack, 0x1000, sizeof(stack)); + CALLBACK(copyin, PT4, 0x2000, sizeof(PT4)); + CALLBACK(copyin, PT3, 0x3000, sizeof(PT3)); + CALLBACK(copyin, PT2, 0x4000, sizeof(PT2)); + CALLBACK(setreg, 4, 0x1000); + + CALLBACK(setmsr, MSR_EFER, EFER_LMA | EFER_LME); + CALLBACK(setcr, 4, CR4_PAE | CR4_VMXE); + CALLBACK(setcr, 3, 0x2000); + CALLBACK(setcr, 0, CR0_PG | CR0_PE | CR0_NE); + + setup_freebsd_gdt(gdtr); + CALLBACK(copyin, gdtr, 0x5000, sizeof(gdtr)); + CALLBACK(setgdt, 0x5000, sizeof(gdtr)); + + CALLBACK(exec, ehdr->e_entry); + + panic("exec returned"); +} + +static int +elf64_obj_exec(struct preloaded_file *fp) +{ + + return (EFTYPE); +} diff --git a/stand/userboot/userboot/host.c b/stand/userboot/userboot/host.c new file mode 100644 index 000000000000..94f8a3dca44c --- /dev/null +++ b/stand/userboot/userboot/host.c @@ -0,0 +1,202 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Read from the host filesystem + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <stand.h> +#include <bootstrap.h> + +#include "libuserboot.h" + +/* + * Open a file. + */ +static int +host_open(const char *upath, struct open_file *f) +{ + + if (f->f_dev != &host_dev) + return (EINVAL); + + return (CALLBACK(open, upath, &f->f_fsdata)); +} + +static int +host_close(struct open_file *f) +{ + + CALLBACK(close, f->f_fsdata); + f->f_fsdata = (void *)0; + + return (0); +} + +/* + * Copy a portion of a file into memory. + */ +static int +host_read(struct open_file *f, void *start, size_t size, size_t *resid) +{ + + return (CALLBACK(read, f->f_fsdata, start, size, resid)); +} + +/* + * Don't be silly - the bootstrap has no business writing anything. + */ +static int +host_write(struct open_file *f, void *start, size_t size, size_t *resid) +{ + + return (EROFS); +} + +static off_t +host_seek(struct open_file *f, off_t offset, int where) +{ + + return (CALLBACK(seek, f->f_fsdata, offset, where)); +} + +static int +host_stat(struct open_file *f, struct stat *sb) +{ + int mode; + int uid; + int gid; + uint64_t size; + + CALLBACK(stat, f->f_fsdata, &mode, &uid, &gid, &size); + sb->st_mode = mode; + sb->st_uid = uid; + sb->st_gid = gid; + sb->st_size = size; + return (0); +} + +static int +host_readdir(struct open_file *f, struct dirent *d) +{ + uint32_t fileno; + uint8_t type; + size_t namelen; + int rc; + + rc = CALLBACK(readdir, f->f_fsdata, &fileno, &type, &namelen, + d->d_name); + if (rc) + return (rc); + + d->d_fileno = fileno; + d->d_type = type; + d->d_namlen = namelen; + + return (0); +} + +static int +host_dev_init(void) +{ + + return (0); +} + +static int +host_dev_print(int verbose) +{ + char line[80]; + + printf("%s devices:", host_dev.dv_name); + if (pager_output("\n") != 0) + return (1); + + snprintf(line, sizeof(line), " host%d: Host filesystem\n", 0); + return (pager_output(line)); +} + +/* + * 'Open' the host device. + */ +static int +host_dev_open(struct open_file *f, ...) +{ + va_list args; + struct devdesc *dev; + + va_start(args, f); + dev = va_arg(args, struct devdesc*); + va_end(args); + + return (0); +} + +static int +host_dev_close(struct open_file *f) +{ + + return (0); +} + +static int +host_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + + return (ENOSYS); +} + +struct fs_ops host_fsops = { + "host", + host_open, + host_close, + host_read, + host_write, + host_seek, + host_stat, + host_readdir +}; + +struct devsw host_dev = { + .dv_name = "host", + .dv_type = DEVT_NET, + .dv_init = host_dev_init, + .dv_strategy = host_dev_strategy, + .dv_open = host_dev_open, + .dv_close = host_dev_close, + .dv_ioctl = noioctl, + .dv_print = host_dev_print, + .dv_cleanup = NULL +}; diff --git a/stand/userboot/userboot/libuserboot.h b/stand/userboot/userboot/libuserboot.h new file mode 100644 index 000000000000..e2048d5fef94 --- /dev/null +++ b/stand/userboot/userboot/libuserboot.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "userboot.h" + +extern struct loader_callbacks *callbacks; +extern void *callbacks_arg; + +#define CALLBACK(fn, args...) (callbacks->fn(callbacks_arg , ##args)) + +#define MAXDEV 31 /* maximum number of distinct devices */ + +typedef unsigned long physaddr_t; + +/* exported devices */ +extern struct devsw userboot_disk; +extern int userboot_disk_maxunit; +extern struct devsw host_dev; + +/* access to host filesystem */ +struct fs_ops host_fsops; + +struct bootinfo; +struct preloaded_file; +extern int bi_load(struct bootinfo *, struct preloaded_file *); + +extern void delay(int); + +extern int userboot_autoload(void); +extern ssize_t userboot_copyin(const void *, vm_offset_t, size_t); +extern ssize_t userboot_copyout(vm_offset_t, void *, size_t); +extern ssize_t userboot_readin(int, vm_offset_t, size_t); +extern int userboot_getdev(void **, const char *, const char **); +char *userboot_fmtdev(void *vdev); +int userboot_setcurrdev(struct env_var *ev, int flags, const void *value); + +int bi_getboothowto(char *kargs); +void bi_setboothowto(int howto); +vm_offset_t bi_copyenv(vm_offset_t addr); +int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, + vm_offset_t *modulep, vm_offset_t *kernend); +int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend); +void bios_addsmapdata(struct preloaded_file *kfp); diff --git a/stand/userboot/userboot/main.c b/stand/userboot/userboot/main.c new file mode 100644 index 000000000000..9e69724b17a9 --- /dev/null +++ b/stand/userboot/userboot/main.c @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> +#include <setjmp.h> +#include <sys/disk.h> + +#include "bootstrap.h" +#include "disk.h" +#include "libuserboot.h" + +#if defined(USERBOOT_ZFS_SUPPORT) +#include "../zfs/libzfs.h" + +static void userboot_zfs_probe(void); +static int userboot_zfs_found; +#endif + +/* Minimum version required */ +#define USERBOOT_VERSION USERBOOT_VERSION_3 + +#define MALLOCSZ (64*1024*1024) + +struct loader_callbacks *callbacks; +void *callbacks_arg; + +extern char bootprog_info[]; +static jmp_buf jb; + +struct arch_switch archsw; /* MI/MD interface boundary */ + +static void extract_currdev(void); + +void +delay(int usec) +{ + + CALLBACK(delay, usec); +} + +void +exit(int v) +{ + + CALLBACK(exit, v); + longjmp(jb, 1); +} + +void +loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks) +{ + static char mallocbuf[MALLOCSZ]; + const char *var; + int i; + + if (version < USERBOOT_VERSION) + abort(); + + callbacks = cb; + callbacks_arg = arg; + userboot_disk_maxunit = ndisks; + + /* + * initialise the heap as early as possible. Once this is done, + * alloc() is usable. + */ + setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf))); + + /* + * Hook up the console + */ + cons_probe(); + + printf("\n%s", bootprog_info); +#if 0 + printf("Memory: %ld k\n", memsize() / 1024); +#endif + + setenv("LINES", "24", 1); /* optional */ + + /* + * Set custom environment variables + */ + i = 0; + while (1) { + var = CALLBACK(getenv, i++); + if (var == NULL) + break; + putenv(var); + } + + archsw.arch_autoload = userboot_autoload; + archsw.arch_getdev = userboot_getdev; + archsw.arch_copyin = userboot_copyin; + archsw.arch_copyout = userboot_copyout; + archsw.arch_readin = userboot_readin; +#if defined(USERBOOT_ZFS_SUPPORT) + archsw.arch_zfs_probe = userboot_zfs_probe; +#endif + + /* + * Initialise the block cache. Set the upper limit. + */ + bcache_init(32768, 512); + /* + * March through the device switch probing for things. + */ + for (i = 0; devsw[i] != NULL; i++) + if (devsw[i]->dv_init != NULL) + (devsw[i]->dv_init)(); + + extract_currdev(); + + if (setjmp(jb)) + return; + + interact(NULL); /* doesn't return */ + + exit(0); +} + +/* + * Set the 'current device' by (if possible) recovering the boot device as + * supplied by the initial bootstrap. + */ +static void +extract_currdev(void) +{ + struct disk_devdesc dev; + + //bzero(&dev, sizeof(dev)); + +#if defined(USERBOOT_ZFS_SUPPORT) + if (userboot_zfs_found) { + struct zfs_devdesc zdev; + + /* Leave the pool/root guid's unassigned */ + bzero(&zdev, sizeof(zdev)); + zdev.d_dev = &zfs_dev; + zdev.d_type = zdev.d_dev->dv_type; + + dev = *(struct disk_devdesc *)&zdev; + init_zfs_bootenv(zfs_fmtdev(&dev)); + } else +#endif + + if (userboot_disk_maxunit > 0) { + dev.d_dev = &userboot_disk; + dev.d_type = dev.d_dev->dv_type; + dev.d_unit = 0; + dev.d_slice = 0; + dev.d_partition = 0; + /* + * If we cannot auto-detect the partition type then + * access the disk as a raw device. + */ + if (dev.d_dev->dv_open(NULL, &dev)) { + dev.d_slice = -1; + dev.d_partition = -1; + } + } else { + dev.d_dev = &host_dev; + dev.d_type = dev.d_dev->dv_type; + dev.d_unit = 0; + } + + env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(&dev), + userboot_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(&dev), + env_noset, env_nounset); +} + +#if defined(USERBOOT_ZFS_SUPPORT) +static void +userboot_zfs_probe(void) +{ + char devname[32]; + uint64_t pool_guid; + int unit; + + /* + * Open all the disks we can find and see if we can reconstruct + * ZFS pools from them. Record if any were found. + */ + for (unit = 0; unit < userboot_disk_maxunit; unit++) { + sprintf(devname, "disk%d:", unit); + pool_guid = 0; + zfs_probe_dev(devname, &pool_guid); + if (pool_guid != 0) + userboot_zfs_found = 1; + } +} + +COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", + command_lszfs); + +static int +command_lszfs(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "a single dataset must be supplied"; + return (CMD_ERROR); + } + + err = zfs_list(argv[1]); + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + return (CMD_OK); +} + +COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", + command_reloadbe); + +static int +command_reloadbe(int argc, char *argv[]) +{ + int err; + char *root; + + if (argc > 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + if (argc == 2) { + err = zfs_bootenv(argv[1]); + } else { + root = getenv("zfs_be_root"); + if (root == NULL) { + return (CMD_OK); + } + err = zfs_bootenv(root); + } + + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + + return (CMD_OK); +} + +uint64_t +ldi_get_size(void *priv) +{ + int fd = (uintptr_t) priv; + uint64_t size; + + ioctl(fd, DIOCGMEDIASIZE, &size); + return (size); +} +#endif /* USERBOOT_ZFS_SUPPORT */ + +COMMAND_SET(quit, "quit", "exit the loader", command_quit); + +static int +command_quit(int argc, char *argv[]) +{ + + exit(USERBOOT_EXIT_QUIT); + return (CMD_OK); +} + +COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); + +static int +command_reboot(int argc, char *argv[]) +{ + + exit(USERBOOT_EXIT_REBOOT); + return (CMD_OK); +} diff --git a/stand/userboot/userboot/userboot_cons.c b/stand/userboot/userboot/userboot_cons.c new file mode 100644 index 000000000000..6f73ad5633bb --- /dev/null +++ b/stand/userboot/userboot/userboot_cons.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include "bootstrap.h" +#include "libuserboot.h" + +int console; + +static struct console *userboot_comconsp; + +static void userboot_cons_probe(struct console *cp); +static int userboot_cons_init(int); +static void userboot_comcons_probe(struct console *cp); +static int userboot_comcons_init(int); +static void userboot_cons_putchar(int); +static int userboot_cons_getchar(void); +static int userboot_cons_poll(void); + +struct console userboot_console = { + "userboot", + "userboot", + 0, + userboot_cons_probe, + userboot_cons_init, + userboot_cons_putchar, + userboot_cons_getchar, + userboot_cons_poll, +}; + +/* + * Provide a simple alias to allow loader scripts to set the + * console to comconsole without resulting in an error + */ +struct console userboot_comconsole = { + "comconsole", + "comconsole", + 0, + userboot_comcons_probe, + userboot_comcons_init, + userboot_cons_putchar, + userboot_cons_getchar, + userboot_cons_poll, +}; + +static void +userboot_cons_probe(struct console *cp) +{ + + cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); +} + +static int +userboot_cons_init(int arg) +{ + + return (0); +} + +static void +userboot_comcons_probe(struct console *cp) +{ + + /* + * Save the console pointer so the comcons_init routine + * can set the C_PRESENT* flags. They are not set + * here to allow the existing userboot console to + * be elected the default. + */ + userboot_comconsp = cp; +} + +static int +userboot_comcons_init(int arg) +{ + + /* + * Set the C_PRESENT* flags to allow the comconsole + * to be selected as the active console + */ + userboot_comconsp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); + return (0); +} + +static void +userboot_cons_putchar(int c) +{ + + CALLBACK(putc, c); +} + +static int +userboot_cons_getchar() +{ + + return (CALLBACK(getc)); +} + +static int +userboot_cons_poll() +{ + + return (CALLBACK(poll)); +} diff --git a/stand/userboot/userboot/userboot_disk.c b/stand/userboot/userboot/userboot_disk.c new file mode 100644 index 000000000000..fc21b5f1792d --- /dev/null +++ b/stand/userboot/userboot/userboot_disk.c @@ -0,0 +1,242 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Userboot disk image handling. + */ + +#include <sys/disk.h> +#include <stand.h> +#include <stdarg.h> +#include <bootstrap.h> + +#include "disk.h" +#include "libuserboot.h" + +struct userdisk_info { + uint64_t mediasize; + uint16_t sectorsize; + int ud_open; /* reference counter */ + void *ud_bcache; /* buffer cache data */ +}; + +int userboot_disk_maxunit = 0; + +static int userdisk_maxunit = 0; +static struct userdisk_info *ud_info; + +static int userdisk_init(void); +static void userdisk_cleanup(void); +static int userdisk_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int userdisk_realstrategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int userdisk_open(struct open_file *f, ...); +static int userdisk_close(struct open_file *f); +static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data); +static int userdisk_print(int verbose); + +struct devsw userboot_disk = { + "disk", + DEVT_DISK, + userdisk_init, + userdisk_strategy, + userdisk_open, + userdisk_close, + userdisk_ioctl, + userdisk_print, + userdisk_cleanup +}; + +/* + * Initialize userdisk_info structure for each disk. + */ +static int +userdisk_init(void) +{ + off_t mediasize; + u_int sectorsize; + int i; + + userdisk_maxunit = userboot_disk_maxunit; + if (userdisk_maxunit > 0) { + ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit); + if (ud_info == NULL) + return (ENOMEM); + for (i = 0; i < userdisk_maxunit; i++) { + if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE, + §orsize) != 0 || CALLBACK(diskioctl, i, + DIOCGMEDIASIZE, &mediasize) != 0) + return (ENXIO); + ud_info[i].mediasize = mediasize; + ud_info[i].sectorsize = sectorsize; + ud_info[i].ud_open = 0; + ud_info[i].ud_bcache = NULL; + } + } + bcache_add_dev(userdisk_maxunit); + return(0); +} + +static void +userdisk_cleanup(void) +{ + + if (userdisk_maxunit > 0) + free(ud_info); +} + +/* + * Print information about disks + */ +static int +userdisk_print(int verbose) +{ + struct disk_devdesc dev; + char line[80]; + int i, ret = 0; + + if (userdisk_maxunit == 0) + return (0); + + printf("%s devices:", userboot_disk.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < userdisk_maxunit; i++) { + snprintf(line, sizeof(line), + " disk%d: Guest drive image\n", i); + ret = pager_output(line); + if (ret != 0) + break; + dev.d_dev = &userboot_disk; + dev.d_unit = i; + dev.d_slice = -1; + dev.d_partition = -1; + if (disk_open(&dev, ud_info[i].mediasize, + ud_info[i].sectorsize) == 0) { + snprintf(line, sizeof(line), " disk%d", i); + ret = disk_print(&dev, line, verbose); + disk_close(&dev); + if (ret != 0) + break; + } + } + return (ret); +} + +/* + * Attempt to open the disk described by (dev) for use by (f). + */ +static int +userdisk_open(struct open_file *f, ...) +{ + va_list ap; + struct disk_devdesc *dev; + + va_start(ap, f); + dev = va_arg(ap, struct disk_devdesc *); + va_end(ap); + + if (dev->d_unit < 0 || dev->d_unit >= userdisk_maxunit) + return (EIO); + ud_info[dev->d_unit].ud_open++; + if (ud_info[dev->d_unit].ud_bcache == NULL) + ud_info[dev->d_unit].ud_bcache = bcache_allocate(); + return (disk_open(dev, ud_info[dev->d_unit].mediasize, + ud_info[dev->d_unit].sectorsize)); +} + +static int +userdisk_close(struct open_file *f) +{ + struct disk_devdesc *dev; + + dev = (struct disk_devdesc *)f->f_devdata; + ud_info[dev->d_unit].ud_open--; + if (ud_info[dev->d_unit].ud_open == 0) { + bcache_free(ud_info[dev->d_unit].ud_bcache); + ud_info[dev->d_unit].ud_bcache = NULL; + } + return (disk_close(dev)); +} + +static int +userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct disk_devdesc *dev; + + dev = (struct disk_devdesc *)devdata; + bcd.dv_strategy = userdisk_realstrategy; + bcd.dv_devdata = devdata; + bcd.dv_cache = ud_info[dev->d_unit].ud_bcache; + return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, + size, buf, rsize)); +} + +static int +userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct disk_devdesc *dev = devdata; + uint64_t off; + size_t resid; + int rc; + + rw &= F_MASK; + if (rw == F_WRITE) + return (EROFS); + if (rw != F_READ) + return (EINVAL); + if (rsize) + *rsize = 0; + off = dblk * ud_info[dev->d_unit].sectorsize; + rc = CALLBACK(diskread, dev->d_unit, off, buf, size, &resid); + if (rc) + return (rc); + if (rsize) + *rsize = size - resid; + return (0); +} + +static int +userdisk_ioctl(struct open_file *f, u_long cmd, void *data) +{ + struct disk_devdesc *dev; + int rc; + + dev = (struct disk_devdesc *)f->f_devdata; + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + + return (CALLBACK(diskioctl, dev->d_unit, cmd, data)); +} diff --git a/stand/userboot/userboot/version b/stand/userboot/userboot/version new file mode 100644 index 000000000000..ce6e2701fdc3 --- /dev/null +++ b/stand/userboot/userboot/version @@ -0,0 +1,4 @@ +$FreeBSD$ + +1.1: Initial userland boot + |
