diff options
author | Keith M Wesolowski <wesolows@foobazco.org> | 2013-11-19 01:12:04 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2015-11-06 22:06:29 -0800 |
commit | 0181461b79a0991f2269dad3ef978086e6c70257 (patch) | |
tree | 55f870e02664e992272fc66180a908de61a68180 | |
parent | 99141ac7e1fddf3c8d7a32b9d945448af73e0039 (diff) | |
download | illumos-joyent-0181461b79a0991f2269dad3ef978086e6c70257.tar.gz |
5886 want ability to provide additional objects at boot
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
-rw-r--r-- | usr/src/common/fs/bootfsops.c | 329 | ||||
-rw-r--r-- | usr/src/common/util/string.h | 3 | ||||
-rw-r--r-- | usr/src/common/util/strtolctype.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/fs/vfs.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/krtld/bootrd.c | 83 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sunddi.h | 1 | ||||
-rw-r--r-- | usr/src/uts/i86pc/Makefile.rules | 2 | ||||
-rw-r--r-- | usr/src/uts/i86pc/dboot/dboot_startkern.c | 261 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/fakebop.c | 49 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/mlsetup.c | 5 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/startup.c | 51 | ||||
-rw-r--r-- | usr/src/uts/i86pc/sys/fastboot_msg.h | 1 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.files | 3 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/bootinfo.h | 21 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/bootvfs.h | 19 |
15 files changed, 756 insertions, 78 deletions
diff --git a/usr/src/common/fs/bootfsops.c b/usr/src/common/fs/bootfsops.c new file mode 100644 index 0000000000..5a693b80e5 --- /dev/null +++ b/usr/src/common/fs/bootfsops.c @@ -0,0 +1,329 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 Joyent, Inc. All rights reserved. + */ + +#include <sys/bootconf.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/vnode.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_fs.h> +#include <sys/fs/ufs_inode.h> +#include <sys/sysmacros.h> +#include <sys/bootvfs.h> +#include <sys/bootinfo.h> +#include <sys/filep.h> + +#ifdef _BOOT +#include "../common/util.h" +#else +#include <sys/sunddi.h> +#endif + +#define MAX_FILES MAX_BOOT_MODULES +#define MAX_FDS 256 + +extern void *bkmem_alloc(size_t); +extern void bkmem_free(void *, size_t); + +/* + * TODO: Replace these declarations with inclusion of the ordinary userland + * bootfs headers once they're available. + */ +typedef struct bfile { + char bf_name[MAXPATHLEN]; + caddr_t bf_addr; + size_t bf_size; + struct bfile *bf_next; + uint64_t bf_ino; +} bfile_t; + +typedef struct bf_fd { + bfile_t *fd_file; + off_t fd_pos; +} bf_fd_t; + +static bfile_t *head; +static uint_t init_done; +static bf_fd_t fds[MAX_FDS]; + +static char cpath[MAXPATHLEN]; /* For canonicalising filenames */ + +static void bbootfs_closeall(int); + +static void +canonicalise(const char *fn, char *out) +{ + const char *p; + char *q, *s; + char *last; + char *oc; + int is_slash = 0; + static char scratch[MAXPATHLEN]; + + if (fn == NULL) { + *out = '\0'; + return; + } + + /* + * Remove leading slashes and condense all multiple slashes into one. + */ + p = fn; + while (*p == '/') + ++p; + + for (q = scratch; *p != '\0'; p++) { + if (*p == '/' && !is_slash) { + *q++ = '/'; + is_slash = 1; + } else if (*p != '/') { + *q++ = *p; + is_slash = 0; + } + } + *q = '\0'; + + if (strncmp(scratch, "system/boot/", 12) == 0 || + strcmp(scratch, "system/boot") == 0) { + s = scratch + 12; + } else { + s = scratch; + } + + for (last = strsep(&s, "/"), q = oc = out; last != NULL; + last = strsep(&s, "/")) { + if (strcmp(last, ".") == 0) + continue; + if (strcmp(last, "..") == 0) { + for (oc = q; oc > out && *oc != '/'; oc--) + ; + q = oc; + continue; + } + if (q > out) + *q++ = '/'; + q += snprintf(q, MAXPATHLEN - (q - out), "%s", last); + } + + *q = '\0'; +} + +/* ARGSUSED */ +static int +bbootfs_mountroot(char *str) +{ + return (-1); +} + +static int +bbootfs_unmountroot(void) +{ + return (-1); +} + +static int +bbootfs_init(void) +{ + bfile_t *fp; + char propname[32]; + uint64_t propval; + uint_t i; + + for (i = 0; i < MAX_FILES; i++) { + (void) snprintf(propname, sizeof (propname), + "module-name-%u", i); + if (do_bsys_getproplen(NULL, propname) < 0) + break; + + if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) { + bbootfs_closeall(1); + return (-1); + } + + (void) do_bsys_getprop(NULL, propname, cpath); + canonicalise(cpath, fp->bf_name); + + (void) snprintf(propname, sizeof (propname), + "module-addr-%u", i); + if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { + bkmem_free(fp, sizeof (bfile_t)); + continue; + } + (void) do_bsys_getprop(NULL, propname, &propval); + fp->bf_addr = (void *)(uintptr_t)propval; + + (void) snprintf(propname, sizeof (propname), + "module-size-%u", i); + if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { + bkmem_free(fp, sizeof (bfile_t)); + continue; + } + (void) do_bsys_getprop(NULL, propname, &propval); + fp->bf_size = (size_t)propval; + fp->bf_ino = i; + + fp->bf_next = head; + head = fp; + } + + return (0); +} + +/*ARGSUSED*/ +static int +bbootfs_open(char *fn, int flags) +{ + uint_t i; + bfile_t *fp; + + if (!init_done) { + if (bbootfs_init() != 0) + return (-1); + + init_done = 1; + } + + canonicalise(fn, cpath); + + for (fp = head; fp != NULL; fp = fp->bf_next) { + if (strcmp(fp->bf_name, cpath) == 0) + break; + } + + if (fp == NULL) + return (-1); + + for (i = 0; i < MAX_FDS; i++) { + if (fds[i].fd_file == NULL) { + fds[i].fd_file = fp; + fds[i].fd_pos = 0; + return (i); + } + } + + return (-1); +} + +static int +bbootfs_close(int fd) +{ + if (fds[fd].fd_file == NULL) + return (-1); + + fds[fd].fd_file = NULL; + fds[fd].fd_pos = 0; + + return (0); +} + +static ssize_t +bbootfs_read(int fd, caddr_t buf, size_t size) +{ + ssize_t len; + bf_fd_t *fdp = &fds[fd]; + + if (fdp->fd_file == NULL) + return (-1); + + if (fdp->fd_pos >= fdp->fd_file->bf_size) + return (-1); + + if (fdp->fd_pos + size > fdp->fd_file->bf_size) + len = fdp->fd_file->bf_size - fdp->fd_pos; + else + len = size; + + bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len); + + fdp->fd_pos += len; + + return (len); +} + +static off_t +bbootfs_lseek(int fd, off_t addr, int whence) +{ + bf_fd_t *fdp = &fds[fd]; + + if (fdp->fd_file == NULL) + return (-1); + + switch (whence) { + case SEEK_CUR: + fdp->fd_pos += addr; + break; + case SEEK_SET: + fdp->fd_pos = addr; + break; + case SEEK_END: + fdp->fd_pos = fdp->fd_file->bf_size; + break; + default: + return (-1); + } + + return (0); +} + +static int +bbootfs_fstat(int fd, struct bootstat *bsp) +{ + bf_fd_t *fdp = &fds[fd]; + + if (fdp->fd_file == NULL) + return (-1); + + bsp->st_dev = 1; + bsp->st_ino = fdp->fd_file->bf_ino; + bsp->st_mode = 0444; + bsp->st_nlink = 1; + bsp->st_uid = bsp->st_gid = 0; + bsp->st_rdev = 0; + bsp->st_size = fdp->fd_file->bf_size; + bsp->st_blksize = 1; + bsp->st_blocks = fdp->fd_file->bf_size; + (void) strcpy(bsp->st_fstype, "bootfs"); + + return (0); +} + +/* ARGSUSED */ +static void +bbootfs_closeall(int flag) +{ + bfile_t *fp; + + while (head != NULL) { + fp = head; + head = head->bf_next; + + bkmem_free(fp, sizeof (bfile_t)); + } + + init_done = 0; +} + +struct boot_fs_ops bbootfs_ops = { + "bootfs", + bbootfs_mountroot, + bbootfs_unmountroot, + bbootfs_open, + bbootfs_close, + bbootfs_read, + bbootfs_lseek, + bbootfs_fstat, + bbootfs_closeall, + NULL +}; diff --git a/usr/src/common/util/string.h b/usr/src/common/util/string.h index 052eeab4a4..6a109bf930 100644 --- a/usr/src/common/util/string.h +++ b/usr/src/common/util/string.h @@ -27,8 +27,6 @@ #ifndef _COMMON_UTIL_STRING_H #define _COMMON_UTIL_STRING_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #ifdef __cplusplus @@ -61,6 +59,7 @@ extern char *strncpy(char *, const char *, size_t); extern char *strrchr(const char *, int c); extern char *strstr(const char *, const char *); extern char *strpbrk(const char *, const char *); +extern char *strsep(char **, const char *); extern char *strncat(char *, const char *, size_t); extern size_t strlcat(char *, const char *, size_t); extern size_t strlcpy(char *, const char *, size_t); diff --git a/usr/src/common/util/strtolctype.h b/usr/src/common/util/strtolctype.h index 5675e42be7..535c014d1f 100644 --- a/usr/src/common/util/strtolctype.h +++ b/usr/src/common/util/strtolctype.h @@ -44,7 +44,7 @@ extern "C" { * safe in probe context. */ -#if defined(_KERNEL) && !defined(_BOOT) +#if defined(_KERNEL) || defined(_BOOT) #define isalnum(ch) (isalpha(ch) || isdigit(ch)) #define isalpha(ch) (isupper(ch) || islower(ch)) @@ -56,7 +56,7 @@ extern "C" { #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \ ((ch) >= 'A' && (ch) <= 'F')) -#endif /* _KERNEL && !_BOOT */ +#endif /* _KERNEL || _BOOT */ #define DIGIT(x) \ (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c index 1c19c8bfe2..794ec23968 100644 --- a/usr/src/uts/common/fs/vfs.c +++ b/usr/src/uts/common/fs/vfs.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ diff --git a/usr/src/uts/common/krtld/bootrd.c b/usr/src/uts/common/krtld/bootrd.c index 08e5d98c09..35ad67da96 100644 --- a/usr/src/uts/common/krtld/bootrd.c +++ b/usr/src/uts/common/krtld/bootrd.c @@ -21,6 +21,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2013 Joyent, Inc. All rights reserved. */ @@ -36,17 +37,95 @@ extern void (*_kobj_printf)(void *, const char *fmt, ...); extern int get_weakish_int(int *); extern struct bootops *ops; -extern struct boot_fs_ops bufs_ops, bhsfs_ops; +extern struct boot_fs_ops bufs_ops, bhsfs_ops, bbootfs_ops; extern int kmem_ready; static uint64_t rd_start, rd_end; struct boot_fs_ops *bfs_ops; -struct boot_fs_ops *bfs_tab[] = {&bufs_ops, &bhsfs_ops, NULL}; +struct boot_fs_ops *bfs_tab[] = {&bufs_ops, &bhsfs_ops, &bbootfs_ops, NULL}; static uintptr_t scratch_max = 0; #define _kmem_ready get_weakish_int(&kmem_ready) +int +BRD_MOUNTROOT(struct boot_fs_ops *ops, char *str) +{ + return (ops->fsw_mountroot(str)); +} + +int +BRD_UNMOUNTROOT(struct boot_fs_ops *ops) +{ + if (bfs_ops != &bbootfs_ops) + bbootfs_ops.fsw_closeall(1); + + return (ops->fsw_unmountroot()); +} + +int +BRD_OPEN(struct boot_fs_ops *ops, char *file, int flags) +{ + int len = strlen(SYSTEM_BOOT_PATH); + int fd; + + /* + * Our policy is that we try bootfs first. If bootfs is the only + * filesystem, that's the end of it. Otherwise we will fall back to + * the normal root (i.e., ramdisk) filesystem at this point and try + * again if the file does not exist in bootfs. + */ + fd = bbootfs_ops.fsw_open(file, flags); + + if (bfs_ops == &bbootfs_ops) + return (fd); + + if (strncmp(file, SYSTEM_BOOT_PATH, len) == 0 || fd >= 0) + return ((fd < 0) ? fd : (fd | BFD_F_SYSTEM_BOOT)); + + return (ops->fsw_open(file, flags)); +} + +int +BRD_CLOSE(struct boot_fs_ops *ops, int fd) +{ + if (fd & BFD_F_SYSTEM_BOOT) + return (bbootfs_ops.fsw_close(fd & ~BFD_F_SYSTEM_BOOT)); + + return (ops->fsw_close(fd)); +} + +ssize_t +BRD_READ(struct boot_fs_ops *ops, int fd, caddr_t buf, size_t len) +{ + if (fd & BFD_F_SYSTEM_BOOT) { + return (bbootfs_ops.fsw_read(fd & ~BFD_F_SYSTEM_BOOT, + buf, len)); + } + + return (ops->fsw_read(fd, buf, len)); +} + +off_t +BRD_SEEK(struct boot_fs_ops *ops, int fd, off_t addr, int whence) +{ + if (fd & BFD_F_SYSTEM_BOOT) { + return (bbootfs_ops.fsw_lseek(fd & ~BFD_F_SYSTEM_BOOT, + addr, whence)); + } + + return (ops->fsw_lseek(fd, addr, whence)); +} + +int +BRD_FSTAT(struct boot_fs_ops *ops, int fd, struct bootstat *bsp) +{ + if (fd & BFD_F_SYSTEM_BOOT) + return (bbootfs_ops.fsw_fstat(fd & ~BFD_F_SYSTEM_BOOT, bsp)); + + return (ops->fsw_fstat(fd, bsp)); +} + /* * This one reads the ramdisk. If fi_memp is set, we copy the * ramdisk content to the designated buffer. Otherwise, we diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h index bb063c23db..3338ce7a60 100644 --- a/usr/src/uts/common/sys/sunddi.h +++ b/usr/src/uts/common/sys/sunddi.h @@ -462,6 +462,7 @@ extern size_t strlcat(char *, const char *, size_t); extern size_t strlcpy(char *, const char *, size_t); extern size_t strspn(const char *, const char *); extern size_t strcspn(const char *, const char *); +extern char *strsep(char **, const char *); extern int bcmp(const void *, const void *, size_t) __PURE; extern int stoi(char **); extern void numtos(ulong_t, char *); diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules index ff68c02eba..cc30fd647a 100644 --- a/usr/src/uts/i86pc/Makefile.rules +++ b/usr/src/uts/i86pc/Makefile.rules @@ -221,7 +221,7 @@ DBOOT_DEFS = -D_BOOT $(DBOOT_MACH_$(CLASS)) DBOOT_DEFS += -D_MACHDEP -D_KMEMUSER -U_KERNEL -D_I32LPx DBOOT_FLAGS = $(CCVERBOSE) $(CERRWARN) $(CCNOAUTOINLINE) -DBOOT_CC_INCL = -I$(SRC)/common $(INCLUDE_PATH) +DBOOT_CC_INCL = -I$(SRC)/common -I$(SRC)/common/util $(INCLUDE_PATH) DBOOT_AS_INCL = $(AS_INC_PATH) DBOOT_AS = $(ONBLD_TOOLS)/bin/$(MACH)/aw diff --git a/usr/src/uts/i86pc/dboot/dboot_startkern.c b/usr/src/uts/i86pc/dboot/dboot_startkern.c index f5f8f95682..7fc43e27bd 100644 --- a/usr/src/uts/i86pc/dboot/dboot_startkern.c +++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c @@ -23,7 +23,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2012 Joyent, Inc. All rights reserved. + * Copyright 2013 Joyent, Inc. All rights reserved. */ @@ -34,6 +34,8 @@ #include <sys/mach_mmu.h> #include <sys/multiboot.h> #include <sys/sha1.h> +#include <util/string.h> +#include <util/strtolctype.h> #if defined(__xpv) @@ -162,8 +164,12 @@ uint_t pcimemlists_used = 0; struct boot_memlist rsvdmemlists[MAX_MEMLIST]; uint_t rsvdmemlists_used = 0; -#define MAX_MODULES (10) -struct boot_modules modules[MAX_MODULES]; +/* + * This should match what's in the bootloader. It's arbitrary, but GRUB + * in particular has limitations on how much space it can use before it + * stops working properly. This should be enough. + */ +struct boot_modules modules[MAX_BOOT_MODULES]; uint_t modules_used = 0; /* @@ -172,6 +178,8 @@ uint_t modules_used = 0; uint_t prom_debug = 0; uint_t map_debug = 0; +static char noname[2] = "-"; + /* * Either hypervisor-specific or grub-specific code builds the initial * memlists. This code does the sort/merge/link for final use. @@ -805,13 +813,20 @@ digest_a2h(const char *ascii, uint8_t *digest) * 4 GB, which should not be a problem. */ static int -check_image_hash(const char *ascii, const void *image, size_t len) +check_image_hash(uint_t midx) { + const char *ascii; + const void *image; + size_t len; SHA1_CTX ctx; uint8_t digest[SHA1_DIGEST_LENGTH]; uint8_t baseline[SHA1_DIGEST_LENGTH]; unsigned int i; + ascii = (const char *)(uintptr_t)modules[midx].bm_hash; + image = (const void *)(uintptr_t)modules[midx].bm_addr; + len = (size_t)modules[midx].bm_size; + digest_a2h(ascii, baseline); SHA1Init(&ctx); @@ -826,16 +841,80 @@ check_image_hash(const char *ascii, const void *image, size_t len) return (0); } +static const char * +type_to_str(boot_module_type_t type) +{ + switch (type) { + case BMT_ROOTFS: + return ("rootfs"); + case BMT_FILE: + return ("file"); + case BMT_HASH: + return ("hash"); + default: + return ("unknown"); + } +} + static void check_images(void) { - int i; - char *hashes; - mb_module_t *mod, *hashmod; - char *hash; + uint_t i; char displayhash[SHA1_ASCII_LENGTH + 1]; - size_t hashlen; - size_t len; + + for (i = 0; i < modules_used; i++) { + if (prom_debug) { + dboot_printf("module #%d: name %s type %s " + "addr %lx size %lx\n", + i, (char *)(uintptr_t)modules[i].bm_name, + type_to_str(modules[i].bm_type), + (ulong_t)modules[i].bm_addr, + (ulong_t)modules[i].bm_size); + } + + if (modules[i].bm_type == BMT_HASH || + modules[i].bm_hash == NULL) { + DBG_MSG("module has no hash; skipping check\n"); + continue; + } + (void) memcpy(displayhash, + (void *)(uintptr_t)modules[i].bm_hash, + SHA1_ASCII_LENGTH); + displayhash[SHA1_ASCII_LENGTH] = '\0'; + if (prom_debug) { + dboot_printf("checking expected hash [%s]: ", + displayhash); + } + + if (check_image_hash(i) != 0) + dboot_panic("hash mismatch!\n"); + else + DBG_MSG("OK\n"); + } +} + +/* + * Determine the module's starting address, size, name, and type, and fill the + * boot_modules structure. This structure is used by the bop code, except for + * hashes which are checked prior to transferring control to the kernel. + */ +static void +process_module(mb_module_t *mod) +{ + int midx = modules_used++; + char *p, *q; + + if (prom_debug) { + dboot_printf("\tmodule #%d: '%s' at 0x%lx, end 0x%lx\n", + midx, (char *)(mod->mod_name), + (ulong_t)mod->mod_start, (ulong_t)mod->mod_end); + } + + if (mod->mod_start > mod->mod_end) { + dboot_panic("module #%d: module start address 0x%lx greater " + "than end address 0x%lx", midx, + (ulong_t)mod->mod_start, (ulong_t)mod->mod_end); + } /* * A brief note on lengths and sizes: GRUB, for reasons unknown, passes @@ -851,47 +930,128 @@ check_images(void) * we'll just cope with the bug. That means we won't actually hash the * byte at mod_end, and we will expect that mod_end for the hash file * itself is one greater than some multiple of 41 (40 bytes of ASCII - * hash plus a newline for each module). + * hash plus a newline for each module). We set bm_size to the true + * correct number of bytes in each module, achieving exactly this. */ - if (mb_info->mods_count > 1) { - mod = (mb_module_t *)mb_info->mods_addr; - hashmod = mod + (mb_info->mods_count - 1); - hashes = (char *)hashmod->mod_start; - hashlen = (size_t)(hashmod->mod_end - hashmod->mod_start); - hash = hashes; - if (prom_debug) { - dboot_printf("Hash module found at %lx size %lx\n", - (ulong_t)hashes, (ulong_t)hashlen); - } - } else { - DBG_MSG("Skipping hash check; no hash module found.\n"); + modules[midx].bm_addr = mod->mod_start; + modules[midx].bm_size = mod->mod_end - mod->mod_start; + modules[midx].bm_name = mod->mod_name; + modules[midx].bm_hash = NULL; + modules[midx].bm_type = BMT_FILE; + + if (mod->mod_name == NULL) { + modules[midx].bm_name = (native_ptr_t)(uintptr_t)noname; return; } - for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0; - i < mb_info->mods_count - 1; ++mod, ++i) { - if ((hash - hashes) + SHA1_ASCII_LENGTH + 1 > hashlen) { - dboot_printf("Short hash module of length 0x%lx bytes; " - "skipping hash checks\n", (ulong_t)hashlen); - break; + p = (char *)(uintptr_t)mod->mod_name; + modules[midx].bm_name = + (native_ptr_t)(uintptr_t)strsep(&p, " \t\f\n\r"); + + while (p != NULL) { + q = strsep(&p, " \t\f\n\r"); + if (strncmp(q, "name=", 5) == 0) { + if (q[5] != '\0' && !isspace(q[5])) { + modules[midx].bm_name = + (native_ptr_t)(uintptr_t)(q + 5); + } + continue; } - (void) memcpy(displayhash, hash, SHA1_ASCII_LENGTH); - displayhash[SHA1_ASCII_LENGTH] = '\0'; - if (prom_debug) { - dboot_printf("Checking hash for module %d [%s]: ", - i, displayhash); + if (strncmp(q, "type=", 5) == 0) { + if (q[5] == '\0' || isspace(q[5])) + continue; + q += 5; + if (strcmp(q, "rootfs") == 0) { + modules[midx].bm_type = BMT_ROOTFS; + } else if (strcmp(q, "hash") == 0) { + modules[midx].bm_type = BMT_HASH; + } else if (strcmp(q, "file") != 0) { + dboot_printf("\tmodule #%d: unknown module " + "type '%s'; defaulting to 'file'", + midx, q); + } + continue; } - len = mod->mod_end - mod->mod_start; /* see above */ - if (check_image_hash(hash, (void *)mod->mod_start, len) != 0) { - dboot_panic("SHA-1 hash mismatch on %s; expected %s\n", - (char *)mod->mod_name, displayhash); - } else { - DBG_MSG("OK\n"); + if (strncmp(q, "hash=", 5) == 0) { + if (q[5] != '\0' && !isspace(q[5])) { + modules[midx].bm_hash = + (native_ptr_t)(uintptr_t)(q + 5); + } + continue; + } + + dboot_printf("ignoring unknown option '%s'\n", q); + } +} + +/* + * Backward compatibility: if there are exactly one or two modules, both + * of type 'file' and neither with an embedded hash value, we have been + * given the legacy style modules. In this case we need to treat the first + * module as a rootfs and the second as a hash referencing that module. + * Otherwise, even if the configuration is invalid, we assume that the + * operator knows what he's doing or at least isn't being bitten by this + * interface change. + */ +static void +fixup_modules(void) +{ + if (modules_used == 0 || modules_used > 2) + return; + + if (modules[0].bm_type != BMT_FILE || + modules_used > 1 && modules[1].bm_type != BMT_FILE) { + return; + } + + if (modules[0].bm_hash != NULL || + modules_used > 1 && modules[1].bm_hash != NULL) { + return; + } + + modules[0].bm_type = BMT_ROOTFS; + if (modules_used > 1) { + modules[1].bm_type = BMT_HASH; + modules[1].bm_name = modules[0].bm_name; + } +} + +/* + * For modules that do not have assigned hashes but have a separate hash module, + * find the assigned hash module and set the primary module's bm_hash to point + * to the hash data from that module. We will then ignore modules of type + * BMT_HASH from this point forward. + */ +static void +assign_module_hashes(void) +{ + uint_t i, j; + + for (i = 0; i < modules_used; i++) { + if (modules[i].bm_type == BMT_HASH || + modules[i].bm_hash != NULL) { + continue; + } + + for (j = 0; j < modules_used; j++) { + if (modules[j].bm_type != BMT_HASH || + strcmp((char *)(uintptr_t)modules[j].bm_name, + (char *)(uintptr_t)modules[i].bm_name) != 0) { + continue; + } + + if (modules[j].bm_size < SHA1_ASCII_LENGTH) { + dboot_printf("Short hash module of length " + "0x%lx bytes; ignoring\n", + (ulong_t)modules[j].bm_size); + } else { + modules[i].bm_hash = modules[j].bm_addr; + } + break; } - hash += SHA1_ASCII_LENGTH + 1; } } @@ -927,9 +1087,9 @@ init_mem_alloc(void) DBG_MSG("Entered init_mem_alloc()\n"); DBG((uintptr_t)mb_info); - if (mb_info->mods_count > MAX_MODULES) { + if (mb_info->mods_count > MAX_BOOT_MODULES) { dboot_panic("Too many modules (%d) -- the maximum is %d.", - mb_info->mods_count, MAX_MODULES); + mb_info->mods_count, MAX_BOOT_MODULES); } /* * search the modules to find the last used address @@ -940,18 +1100,7 @@ init_mem_alloc(void) for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0; i < mb_info->mods_count; ++mod, ++i) { - if (prom_debug) { - dboot_printf("\tmodule #%d: %s at: 0x%lx, end 0x%lx\n", - i, (char *)(mod->mod_name), - (ulong_t)mod->mod_start, (ulong_t)mod->mod_end); - } - modules[i].bm_addr = mod->mod_start; - if (mod->mod_start > mod->mod_end) { - dboot_panic("module[%d]: Invalid module start address " - "(0x%llx)", i, (uint64_t)mod->mod_start); - } - modules[i].bm_size = mod->mod_end - mod->mod_start; - + process_module(mod); check_higher(mod->mod_end); } bi->bi_modules = (native_ptr_t)(uintptr_t)modules; @@ -959,6 +1108,8 @@ init_mem_alloc(void) bi->bi_module_cnt = mb_info->mods_count; DBG(bi->bi_module_cnt); + fixup_modules(); + assign_module_hashes(); check_images(); /* diff --git a/usr/src/uts/i86pc/os/fakebop.c b/usr/src/uts/i86pc/os/fakebop.c index d38bcb046f..50690ae67d 100644 --- a/usr/src/uts/i86pc/os/fakebop.c +++ b/usr/src/uts/i86pc/os/fakebop.c @@ -22,10 +22,11 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - */ -/* + * * Copyright (c) 2010, Intel Corporation. * All rights reserved. + * + * Copyright 2013 Joyent, Inc. All rights reserved. */ /* @@ -66,6 +67,7 @@ #include <sys/kobj.h> #include <sys/kobj_lex.h> #include <sys/pci_cfgspace_impl.h> +#include <sys/fastboot_impl.h> #include "acpi_fw.h" static int have_console = 0; /* set once primitive console is initialized */ @@ -664,7 +666,7 @@ boot_prop_finish(void) } done: if (fd >= 0) - BRD_CLOSE(bfs_ops, fd); + (void) BRD_CLOSE(bfs_ops, fd); /* * Check if we have to limit the boot time allocator @@ -1164,10 +1166,12 @@ build_boot_properties(void) int name_len; char *value; int value_len; - struct boot_modules *bm; + struct boot_modules *bm, *rdbm; char *propbuf; int quoted = 0; int boot_arg_len; + uint_t i, midx; + char modid[32]; #ifndef __xpv static int stdout_val = 0; uchar_t boot_device; @@ -1185,9 +1189,40 @@ build_boot_properties(void) DBG((uintptr_t)propbuf); if (xbootp->bi_module_cnt > 0) { bm = xbootp->bi_modules; - bsetprop64("ramdisk_start", (uint64_t)(uintptr_t)bm->bm_addr); - bsetprop64("ramdisk_end", (uint64_t)(uintptr_t)bm->bm_addr + - bm->bm_size); + rdbm = NULL; + for (midx = i = 0; i < xbootp->bi_module_cnt; i++) { + if (bm[i].bm_type == BMT_ROOTFS) { + rdbm = &bm[i]; + continue; + } + if (bm[i].bm_type == BMT_HASH || bm[i].bm_name == NULL) + continue; + + (void) snprintf(modid, sizeof (modid), + "module-name-%u", midx); + bsetprops(modid, (char *)bm[i].bm_name); + (void) snprintf(modid, sizeof (modid), + "module-addr-%u", midx); + bsetprop64(modid, (uint64_t)(uintptr_t)bm[i].bm_addr); + (void) snprintf(modid, sizeof (modid), + "module-size-%u", midx); + bsetprop64(modid, (uint64_t)bm[i].bm_size); + ++midx; + } + if (rdbm != NULL) { + bsetprop64("ramdisk_start", + (uint64_t)(uintptr_t)rdbm->bm_addr); + bsetprop64("ramdisk_end", + (uint64_t)(uintptr_t)rdbm->bm_addr + rdbm->bm_size); + } + } + + /* + * If there are any boot time modules or hashes present, then disable + * fast reboot. + */ + if (xbootp->bi_module_cnt > 1) { + fastreboot_disable(FBNS_BOOTMOD); } DBG_MSG("Parsing command line for boot properties\n"); diff --git a/usr/src/uts/i86pc/os/mlsetup.c b/usr/src/uts/i86pc/os/mlsetup.c index 0fd3ec3dfb..8cb56d9682 100644 --- a/usr/src/uts/i86pc/os/mlsetup.c +++ b/usr/src/uts/i86pc/os/mlsetup.c @@ -60,6 +60,7 @@ #include <sys/archsystm.h> #include <sys/promif.h> #include <sys/pci_cfgspace.h> +#include <sys/bootvfs.h> #ifdef __xpv #include <sys/hypervisor.h> #else @@ -479,6 +480,10 @@ mach_modpath(char *path, const char *filename) const char isastr[] = "/amd64"; size_t isalen = strlen(isastr); + len = strlen(SYSTEM_BOOT_PATH "/kernel"); + (void) strcpy(path, SYSTEM_BOOT_PATH "/kernel "); + path += len + 1; + if ((p = strrchr(filename, '/')) == NULL) return; diff --git a/usr/src/uts/i86pc/os/startup.c b/usr/src/uts/i86pc/os/startup.c index 277614b536..700b3456ae 100644 --- a/usr/src/uts/i86pc/os/startup.c +++ b/usr/src/uts/i86pc/os/startup.c @@ -22,6 +22,7 @@ * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2013 Joyent, Inc. All rights reserved. */ /* * Copyright (c) 2010, Intel Corporation. @@ -121,6 +122,7 @@ #include <sys/ddi_periodic.h> #include <sys/systeminfo.h> #include <sys/multiboot.h> +#include <sys/ramdisk.h> #ifdef __xpv @@ -2348,6 +2350,20 @@ pp_in_range(page_t *pp, uint64_t low_addr, uint64_t high_addr) (pp->p_pagenum < btopr(high_addr))); } +static int +pp_in_module(page_t *pp, const rd_existing_t *modranges) +{ + uint_t i; + + for (i = 0; modranges[i].phys != 0; i++) { + if (pp_in_range(pp, modranges[i].phys, + modranges[i].phys + modranges[i].size)) + return (1); + } + + return (0); +} + void release_bootstrap(void) { @@ -2355,10 +2371,40 @@ release_bootstrap(void) page_t *pp; extern void kobj_boot_unmountroot(void); extern dev_t rootdev; + uint_t i; + char propname[32]; + rd_existing_t *modranges; #if !defined(__xpv) pfn_t pfn; #endif + /* + * Save the bootfs module ranges so that we can reserve them below + * for the real bootfs. + */ + modranges = kmem_alloc(sizeof (rd_existing_t) * MAX_BOOT_MODULES, + KM_SLEEP); + for (i = 0; ; i++) { + uint64_t start, size; + + modranges[i].phys = 0; + + (void) snprintf(propname, sizeof (propname), + "module-addr-%u", i); + if (do_bsys_getproplen(NULL, propname) <= 0) + break; + (void) do_bsys_getprop(NULL, propname, &start); + + (void) snprintf(propname, sizeof (propname), + "module-size-%u", i); + if (do_bsys_getproplen(NULL, propname) <= 0) + break; + (void) do_bsys_getprop(NULL, propname, &size); + + modranges[i].phys = start; + modranges[i].size = size; + } + /* unmount boot ramdisk and release kmem usage */ kobj_boot_unmountroot(); @@ -2399,9 +2445,8 @@ release_bootstrap(void) continue; } - if (root_is_ramdisk && pp_in_range(pp, ramdisk_start, - ramdisk_end)) { + ramdisk_end) || pp_in_module(pp, modranges)) { pp->p_next = rd_pages; rd_pages = pp; continue; @@ -2413,6 +2458,8 @@ release_bootstrap(void) } PRM_POINT("Boot pages released"); + kmem_free(modranges, sizeof (rd_existing_t) * 99); + #if !defined(__xpv) /* XXPV -- note this following bunch of code needs to be revisited in Xen 3.0 */ /* diff --git a/usr/src/uts/i86pc/sys/fastboot_msg.h b/usr/src/uts/i86pc/sys/fastboot_msg.h index 4c7ee7f8b5..9a1c9bd878 100644 --- a/usr/src/uts/i86pc/sys/fastboot_msg.h +++ b/usr/src/uts/i86pc/sys/fastboot_msg.h @@ -46,6 +46,7 @@ fastboot_nosup_msg(FBNS_DEFAULT, "") fastboot_nosup_msg(FBNS_SUSPEND, " after suspend/resume") fastboot_nosup_msg(FBNS_FMAHWERR, " due to FMA recovery from hardware error") fastboot_nosup_msg(FBNS_HOTPLUG, " after DR operations") +fastboot_nosup_msg(FBNS_BOOTMOD, " due to presence of boot-time modules") /* * Should ALWAYS be the last one. diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files index 01147a5314..b1f80b2409 100644 --- a/usr/src/uts/intel/Makefile.files +++ b/usr/src/uts/intel/Makefile.files @@ -21,7 +21,7 @@ # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# Copyright (c) 2013, Joyent, Inc. All rights reserved. # # @@ -167,6 +167,7 @@ VGATEXT_OBJS += vgatext.o vgasubr.o # Kernel linker # KRTLD_OBJS += \ + bootfsops.o \ bootrd.o \ ufsops.o \ hsfs.o \ diff --git a/usr/src/uts/intel/sys/bootinfo.h b/usr/src/uts/intel/sys/bootinfo.h index 08ff9c3ca0..3adce64fc4 100644 --- a/usr/src/uts/intel/sys/bootinfo.h +++ b/usr/src/uts/intel/sys/bootinfo.h @@ -32,6 +32,13 @@ extern "C" { #endif /* + * This is used by bootfs and dboot. It should be at least as large as the + * number of modules that bootloaders (e.g., grub) can support. This figure + * has been chosen to match grub's value exactly. + */ +#define MAX_BOOT_MODULES 99 + +/* * The 32-bit kernel loader code needs to build several structures that the * kernel is expecting. They will contain native sized pointers for the * target kernel. @@ -51,6 +58,12 @@ typedef void *native_ptr_t; #endif +typedef enum boot_module_type { + BMT_ROOTFS, + BMT_FILE, + BMT_HASH +} boot_module_type_t; + struct boot_memlist { uint64_t addr; uint64_t size; @@ -62,9 +75,11 @@ struct boot_memlist { * The kernel needs to know how to find its modules. */ struct boot_modules { - native_ptr_t bm_addr; - uint32_t bm_size; - uint32_t bm_padding; + native_ptr_t bm_addr; + native_ptr_t bm_name; + native_ptr_t bm_hash; + uint32_t bm_size; + boot_module_type_t bm_type; }; /* diff --git a/usr/src/uts/intel/sys/bootvfs.h b/usr/src/uts/intel/sys/bootvfs.h index 63696395da..5120a6733e 100644 --- a/usr/src/uts/intel/sys/bootvfs.h +++ b/usr/src/uts/intel/sys/bootvfs.h @@ -26,8 +26,6 @@ #ifndef _SYS_BOOTVFS_H #define _SYS_BOOTVFS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -69,6 +67,18 @@ struct boot_fs_ops { extern struct boot_fs_ops *bfs_ops; +#ifdef _KERNEL + +extern int BRD_MOUNTROOT(struct boot_fs_ops *, char *); +extern int BRD_UNMOUNTROOT(struct boot_fs_ops *); +extern int BRD_OPEN(struct boot_fs_ops *, char *, int); +extern int BRD_CLOSE(struct boot_fs_ops *, int); +extern ssize_t BRD_READ(struct boot_fs_ops *, int, caddr_t, size_t); +extern off_t BRD_SEEK(struct boot_fs_ops *, int, off_t, int); +extern int BRD_FSTAT(struct boot_fs_ops *, int, struct bootstat *); + +#else + #define BRD_MOUNTROOT(ops, str) ((ops)->fsw_mountroot)(str) #define BRD_UNMOUNTROOT(ops) ((ops)->fsw_unmountroot)() #define BRD_OPEN(ops, file, flag) ((ops)->fsw_open)(file, flag) @@ -77,6 +87,11 @@ extern struct boot_fs_ops *bfs_ops; #define BRD_SEEK(ops, fd, addr, w) ((ops)->fsw_lseek)(fd, addr, w) #define BRD_FSTAT(ops, fd, stp) ((ops)->fsw_fstat)(fd, stp) +#endif + +#define SYSTEM_BOOT_PATH "/system/boot" +#define BFD_F_SYSTEM_BOOT 0x40000000 + #ifdef _BOOT extern int mountroot(char *str); |