diff options
author | Sherry Moore <Sherry.Moore@Sun.COM> | 2009-03-24 14:38:50 -0700 |
---|---|---|
committer | Sherry Moore <Sherry.Moore@Sun.COM> | 2009-03-24 14:38:50 -0700 |
commit | 753a6d457b330b1b29b2d3eefcd0831116ce950d (patch) | |
tree | c18350d4e3f43265a05f156f9c9175474f3fc3e7 | |
parent | 0c59ba075fc10821b752d32ee83a714261bca290 (diff) | |
download | illumos-joyent-753a6d457b330b1b29b2d3eefcd0831116ce950d.tar.gz |
PSARC/2008/760 Boot configuration Service
PSARC/2009/091 Reboot to firmware
PSARC/2009/092 libgrubmgmt - library for GRUB menu management
6768468 Introducing svc:/system/boot-config service
6775160 reboot -f ignores active BE and resets zfs pool bootfs property
6760845 Add checksum verification when loading the new kernel and boot archive for fast reboot
6815215 quiesce_active should be added to MUTEX_NOT_HELD()
62 files changed, 4668 insertions, 339 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index cde949e045..f728cb1168 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -358,6 +358,7 @@ COMMON_SUBDIRS = \ lib/libexacct \ lib/libfcoe \ lib/libgen \ + lib/libgrubmgmt \ lib/libgss \ lib/libidmap \ lib/libinetcfg \ diff --git a/usr/src/cmd/Makefile.check b/usr/src/cmd/Makefile.check index 608a0467cd..3a63d88d33 100644 --- a/usr/src/cmd/Makefile.check +++ b/usr/src/cmd/Makefile.check @@ -107,6 +107,7 @@ MANIFEST_SUBDIRS= \ gss/gssd \ hal/addons/network-devices \ hal/hald/solaris \ + halt/smf \ hostid/smf \ idmap/idmapd \ ipf/svc \ diff --git a/usr/src/cmd/halt/Makefile b/usr/src/cmd/halt/Makefile index 135e8fee26..bc622af56c 100644 --- a/usr/src/cmd/halt/Makefile +++ b/usr/src/cmd/halt/Makefile @@ -19,31 +19,50 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # PROG = halt -ROOTLINKS = $(ROOTUSRSBIN)/poweroff $(ROOTUSRSBIN)/reboot -ROOTSYMLINKS= $(ROOTETC)/halt $(ROOTETC)/reboot - -lint := LINTFLAGS = -u include ../Makefile.cmd +# +# Currently Fast Reboot is only supported on x86. +# +sparc_SUBDIRS = +i386_SUBDIRS = smf +SUBDIRS = $($(MACH)_SUBDIRS) + +ROOTLINKS = $(ROOTUSRSBIN)/poweroff $(ROOTUSRSBIN)/reboot +ROOTSYMLINKS= $(ROOTETC)/halt $(ROOTETC)/reboot + FILEMODE = 0755 GROUP = bin .KEEP_STATE: -all: $(PROG) - CPPFLAGS += -I../../lib/libzpool/common +CPPFLAGS += -I../../lib/libscf/inc CPPFLAGS += -I../../uts/common/fs/zfs LDLIBS += -lbsm -lscf -lzfs -lgen +LDLIBS_i386 += -lgrubmgmt +LDLIBS += $(LDLIBS_$(MACH)) + +CLOBBERFILES += $(ROOTLINKS) $(ROOTSYMLINKS) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint +lint := LINTFLAGS = -u + + +all: $(PROG) -install: all $(ROOTUSRSBINPROG) $(ROOTLINKS) $(ROOTSYMLINKS) +install: all $(ROOTUSRSBINPROG) $(ROOTLINKS) $(ROOTSYMLINKS) $(SUBDIRS) $(ROOTLINKS): $(ROOTUSRSBINPROG) $(RM) $@ @@ -53,8 +72,17 @@ $(ROOTSYMLINKS): $(RM) $@ $(SYMLINK) ../usr/sbin/$(PROG) $@ +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + clean: +clobber: $(SUBDIRS) + lint: lint_PROG +check: $(CHKMANIFEST) + +FRC: + include ../Makefile.targ diff --git a/usr/src/cmd/halt/halt.c b/usr/src/cmd/halt/halt.c index bafce88e5b..51dfea7ddb 100644 --- a/usr/src/cmd/halt/halt.c +++ b/usr/src/cmd/halt/halt.c @@ -24,7 +24,7 @@ */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 @@ -60,6 +60,7 @@ #include <fcntl.h> #include <libgen.h> #include <libscf.h> +#include <libscf_priv.h> #include <limits.h> #include <locale.h> #include <libintl.h> @@ -79,6 +80,9 @@ #include <spawn.h> #include <libzfs.h> +#if defined(__i386) +#include <libgrubmgmt.h> +#endif #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" @@ -118,12 +122,21 @@ static ctid_t startdct = -1; #define LUUMOUNT_PROG "/usr/sbin/luumount" #define LUMOUNT_PROG "/usr/sbin/lumount" +#define BOOTADM_PROG "/sbin/bootadm" /* * The length of FASTBOOT_MOUNTPOINT must be less than MAXPATHLEN. */ #define FASTBOOT_MOUNTPOINT "/tmp/.fastboot.root" +/* + * Fast Reboot related variables + */ static char fastboot_mounted[MAXPATHLEN]; +#if defined(__i386) +static grub_boot_args_t fbarg; +static grub_boot_args_t *fbarg_used; +static int fbarg_entnum = GRUB_ENTRY_DEFAULT; +#endif /* __i386 */ static int validate_ufs_disk(char *, char *); static int validate_zfs_pool(char *, char *); @@ -542,29 +555,30 @@ validate_disk(char *arg, char *mountpoint) { static char root_dev_path[] = "/dev/dsk"; char kernpath[MAXPATHLEN]; - struct stat buf; struct stat64 statbuf; int rc = 0; if (strlen(arg) > MAXPATHLEN) { (void) fprintf(stderr, - gettext("%s: argument is too long\n"), cmdname); + gettext("%s: Argument is too long\n"), cmdname); return (-1); } bcopy(FASTBOOT_MOUNTPOINT, mountpoint, sizeof (FASTBOOT_MOUNTPOINT)); - /* - * Do a force umount just in case some other filesystem has - * been mounted there. - */ - (void) umount2(mountpoint, MS_FORCE); + if (strstr(arg, mountpoint) == NULL) { + /* + * Do a force umount just in case some other filesystem has + * been mounted there. + */ + (void) umount2(mountpoint, MS_FORCE); + } /* Create the directory if it doesn't already exist */ - if (lstat(mountpoint, &buf) != 0) { + if (lstat64(mountpoint, &statbuf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { (void) fprintf(stderr, - gettext("failed to create mountpoint %s\n"), + gettext("Failed to create mountpoint %s\n"), mountpoint); return (-1); } @@ -608,7 +622,7 @@ validate_ufs_disk(char *arg, char *mountpoint) mntopts, sizeof (mntopts)) != 0) { perror(cmdname); (void) fprintf(stderr, - gettext("%s: failed to mount %s\n"), cmdname, arg); + gettext("%s: Failed to mount %s\n"), cmdname, arg); return (-1); } @@ -623,7 +637,7 @@ validate_zfs_pool(char *arg, char *mountpoint) int rc = 0; if ((g_zfs = libzfs_init()) == NULL) { - (void) fprintf(stderr, gettext("internal error: failed to " + (void) fprintf(stderr, gettext("Internal error: failed to " "initialize ZFS library\n")); return (-1); } @@ -634,11 +648,11 @@ validate_zfs_pool(char *arg, char *mountpoint) return (-1); /* perform the mount */ - if (mount(zfs_get_name(zhp), mountpoint, MS_DATA|MS_OPTIONSTR, + if (mount(zfs_get_name(zhp), mountpoint, MS_DATA|MS_OPTIONSTR|MS_RDONLY, MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { perror(cmdname); (void) fprintf(stderr, - gettext("%s: failed to mount %s\n"), cmdname, arg); + gettext("%s: Failed to mount %s\n"), cmdname, arg); rc = -1; } @@ -665,12 +679,13 @@ get_zfs_bootfs_arg(const char *arg, const char ** fpth, int *is_zfs, FILE *mtabp = NULL; struct mnttab mnt; char *poolname = NULL; - char physpath[MAXNAMELEN]; + char physpath[MAXPATHLEN]; char mntsp[ZPOOL_MAXNAMELEN]; char bootfs[ZPOOL_MAXNAMELEN]; int rc = 0; size_t mntlen = 0; size_t msz; + static char fmt[] = "-B zfs-bootfs=%s,bootpath=\"%s\""; *fpth = arg; *is_zfs = 0; @@ -705,7 +720,7 @@ get_zfs_bootfs_arg(const char *arg, const char ** fpth, int *is_zfs, /* Try to open the dataset */ if ((zhp = zfs_open(g_zfs, mntsp, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_DATASET)) == NULL) { - (void) fprintf(stderr, gettext("cannot open %s\n"), mntsp); + (void) fprintf(stderr, gettext("Cannot open %s\n"), mntsp); rc = -1; goto validate_zfs_err_out; } @@ -718,27 +733,29 @@ get_zfs_bootfs_arg(const char *arg, const char ** fpth, int *is_zfs, } if ((zpoolp = zpool_open(g_zfs, poolname)) == NULL) { - (void) fprintf(stderr, gettext("cannot open %s\n"), poolname); + (void) fprintf(stderr, gettext("Cannot open %s\n"), poolname); rc = -1; goto validate_zfs_err_out; } - if (zpool_get_physpath(zpoolp, physpath) != 0) { - (void) fprintf(stderr, gettext("cannot find phys_path\n")); + if (zpool_get_physpath(zpoolp, physpath, sizeof (physpath)) != 0) { + (void) fprintf(stderr, gettext("Cannot find phys_path\n")); rc = -1; goto validate_zfs_err_out; } - if (zpool_set_prop(zpoolp, "bootfs", bootfs) != 0) { - (void) fprintf(stderr, gettext("cannot set bootfs to %s\n"), - bootfs); - rc = -1; - goto validate_zfs_err_out; + /* + * For the mirror physpath would contain the list of all + * bootable devices, pick up the first one. + */ + (void) strtok(physpath, " "); + if (snprintf(bootfs_arg, BOOTARGS_MAX, fmt, bootfs, physpath) >= + BOOTARGS_MAX) { + rc = E2BIG; + (void) fprintf(stderr, + gettext("Boot arguments are too long\n")); } - (void) snprintf(bootfs_arg, BOOTARGS_MAX, - "-B zfs-bootfs=%s,bootpath=\"%s\"", bootfs, physpath); - validate_zfs_err_out: if (zhp != NULL) zfs_close(zhp); @@ -768,7 +785,7 @@ validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg, if ((sz = resolvepath(arg, physpath, sizeof (physpath) - 1)) == (size_t)-1) { (void) fprintf(stderr, - gettext("cannot resolve path for %s: %s\n"), + gettext("Cannot resolve path for %s: %s\n"), arg, strerror(errno)); return (-1); } @@ -776,13 +793,13 @@ validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg, if (strlen(arg) > MAXPATHLEN) { (void) fprintf(stderr, - gettext("%s: new kernel name is too long\n"), cmdname); + gettext("%s: New kernel name is too long\n"), cmdname); return (-1); } if (strncmp(basename(arg), "unix", 4) != 0) { (void) fprintf(stderr, - gettext("%s: %s: kernel name must be unix\n"), + gettext("%s: %s: Kernel name must be unix\n"), cmdname, arg); return (-1); } @@ -798,7 +815,7 @@ validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg, *failsafe = 0; else { (void) fprintf(stderr, - gettext("%s: %s: no /boot/platform or /platform in" + gettext("%s: %s: No /boot/platform or /platform in" " file name\n"), cmdname, arg); goto err_out; } @@ -813,18 +830,16 @@ validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg, class = ident[EI_CLASS]; if ((class != ELFCLASS32 && class != ELFCLASS64) || - ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || - ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { + memcmp(&ident[EI_MAG0], ELFMAG, 4) != 0) { (void) fprintf(stderr, - gettext("%s: %s: not a valid ELF file\n"), - cmdname, arg); + gettext("%s: %s: Not a valid ELF file\n"), cmdname, arg); goto err_out; } format = ident[EI_DATA]; if (format != CUR_ELFDATA) { - (void) fprintf(stderr, gettext("%s: %s: invalid data format\n"), + (void) fprintf(stderr, gettext("%s: %s: Invalid data format\n"), cmdname, arg); goto err_out; } @@ -839,28 +854,6 @@ err_out: return (-1); } -#ifndef __i386 -/* ARGSUSED */ -#endif /* __i386 */ -static int -is_fastboot_default(uid_t uid) -{ -#if defined(__i386) - int ret; - struct stat st; - static const char fastboot_default[] = "/etc/fastreboot"; - - ret = (lstat(fastboot_default, &st) == 0 && - S_ISREG(st.st_mode) && - (st.st_mode & S_IRUSR) != 0 && - uid == st.st_uid); - - return (ret); -#else - return (0); -#endif /* __i386 */ -} - static int halt_exec(const char *path, ...) { @@ -891,7 +884,7 @@ halt_exec(const char *path, ...) va_end(vp); (void) execve(path, (char * const *)argv, NULL); - (void) fprintf(stderr, gettext("cannot execute %s: %s\n"), + (void) fprintf(stderr, gettext("Cannot execute %s: %s\n"), path, strerror(errno)); exit(-1); } else { @@ -918,7 +911,7 @@ fastboot_bename(const char *bename, char *mountpoint, size_t mpsz) if ((rc = halt_exec(LUMOUNT_PROG, "-n", bename, FASTBOOT_MOUNTPOINT, NULL)) != 0) - (void) fprintf(stderr, gettext("%s: cannot mount BE %s\n"), + (void) fprintf(stderr, gettext("%s: Cannot mount BE %s\n"), cmdname, bename); else (void) strlcpy(mountpoint, FASTBOOT_MOUNTPOINT, mpsz); @@ -928,11 +921,12 @@ fastboot_bename(const char *bename, char *mountpoint, size_t mpsz) /* * Returns 0 on successful parsing of the arguments; - * retuens non-zero on failure. + * returns EINVAL on parsing failures that should abort the reboot attempt; + * returns other error code to fall back to regular reboot. */ static int -parse_fastboot_args(char *bootargs_buf, int *is_dryrun, const char *bename, - int *failsafe) +parse_fastboot_args(char *bootargs_buf, size_t buf_size, + int *is_dryrun, const char *bename, int *failsafe) { char mountpoint[MAXPATHLEN]; char bootargs_saved[BOOTARGS_MAX]; @@ -965,11 +959,6 @@ parse_fastboot_args(char *bootargs_buf, int *is_dryrun, const char *bename, bzero(&bootargs_scratch[buflen], sizeof (bootargs_scratch) - buflen); head = &bootargs_scratch[0]; - /* Zero out the boot argument buffer as we will reconstruct it */ - bzero(bootargs_buf, BOOTARGS_MAX); - bzero(bootfs_arg, BOOTARGS_MAX); - bzero(unixfile, sizeof (unixfile)); - /* Get the first argument */ newarg = strtok(bootargs_scratch, " "); @@ -988,22 +977,72 @@ parse_fastboot_args(char *bootargs_buf, int *is_dryrun, const char *bename, */ if (uadmin(A_SHUTDOWN, AD_FASTREBOOT_DRYRUN, (uintptr_t)bootargs_saved) != 0) { - (void) fprintf(stderr, gettext("%s: not all drivers " - "have implemented quiesce(9E)\n"), cmdname); + (void) fprintf(stderr, gettext("%s: Not all drivers " + "have implemented quiesce(9E)\n" + "\tPlease see /var/adm/messages for drivers that haven't\n" + "\timplemented quiesce(9E).\n"), cmdname); } else if (*is_dryrun) { - (void) fprintf(stderr, gettext("%s: all drivers have " + (void) fprintf(stderr, gettext("%s: All drivers have " "implemented quiesce(9E)\n"), cmdname); } - /* - * Return if it is a true dry run. - */ + /* Return if it is a true dry run. */ if (*is_dryrun) return (rc); +#if defined(__i386) + /* Read boot args from GRUB menu */ + if ((bootargs_buf[0] == 0 || isdigit(bootargs_buf[0])) && + bename == NULL) { + /* + * If no boot arguments are given, or a GRUB menu entry + * number is provided, process the GRUB menu. + */ + int entnum; + if (bootargs_buf[0] == 0) + entnum = GRUB_ENTRY_DEFAULT; + else { + errno = 0; + entnum = strtoul(bootargs_buf, NULL, 10); + rc = errno; + } + + if (rc == 0 && (rc = grub_get_boot_args(&fbarg, NULL, + entnum)) == 0) { + if (strlcpy(bootargs_buf, fbarg.gba_bootargs, + buf_size) >= buf_size) { + grub_cleanup_boot_args(&fbarg); + bcopy(bootargs_saved, bootargs_buf, buf_size); + rc = E2BIG; + } + } + /* Failed to read GRUB menu, fall back to normal reboot */ + if (rc != 0) { + (void) fprintf(stderr, + gettext("%s: Failed to process GRUB menu " + "entry for fast reboot.\n\t%s\n"), + cmdname, grub_strerror(rc)); + (void) fprintf(stderr, + gettext("%s: Falling back to regular reboot.\n"), + cmdname); + return (-1); + } + /* No need to process further */ + fbarg_used = &fbarg; + fbarg_entnum = entnum; + return (0); + } +#endif /* __i386 */ + + /* Zero out the boot argument buffer as we will reconstruct it */ + bzero(bootargs_buf, buf_size); + bzero(bootfs_arg, sizeof (bootfs_arg)); + bzero(unixfile, sizeof (unixfile)); + if (bename && (rc = fastboot_bename(bename, mountpoint, sizeof (mountpoint))) != 0) - return (rc); + return (EINVAL); + /* * If BE is not specified, look for disk argument to construct @@ -1070,7 +1109,7 @@ parse_fastboot_args(char *bootargs_buf, int *is_dryrun, const char *bename, "/platform/i86pc/kernel/unix"); } else { (void) fprintf(stderr, - gettext("%s: unknown architecture"), cmdname); + gettext("%s: Unknown architecture"), cmdname); return (EINVAL); } } @@ -1163,9 +1202,9 @@ main(int argc, char *argv[]) { char *ttyn = ttyname(STDERR_FILENO); - uid_t euid; int qflag = 0, needlog = 1, nosync = 0; int fast_reboot = 0; + int prom_reboot = 0; uintptr_t mdep = NULL; int cmd, fcn, c, aval, r; const char *usage; @@ -1198,8 +1237,8 @@ main(int argc, char *argv[]) } else if (strcmp(cmdname, "reboot") == 0) { (void) audit_reboot_setup(); #if defined(__i386) - optstring = "dlnqfe:"; - usage = gettext("usage: %s [ -dlnqfe: ] [ boot args ]\n"); + optstring = "dlnqpfe:"; + usage = gettext("usage: %s [ -dlnq(p|fe:) ] [ boot args ]\n"); #else optstring = "dlnq"; usage = gettext("usage: %s [ -dlnq ] [ boot args ]\n"); @@ -1237,6 +1276,9 @@ main(int argc, char *argv[]) ttyn = NULL; break; #if defined(__i386) + case 'p': + prom_reboot = 1; + break; case 'f': fast_reboot = 1; break; @@ -1280,17 +1322,25 @@ main(int argc, char *argv[]) bzero(bootargs_buf, sizeof (bootargs_buf)); } - if ((euid = geteuid()) != 0) { + if (geteuid() != 0) { (void) fprintf(stderr, gettext("%s: permission denied\n"), cmdname); goto fail; } + if (fast_reboot && prom_reboot) { + (void) fprintf(stderr, + gettext("%s: -p and -f are mutually exclusive\n"), + cmdname); + return (EINVAL); + } + /* - * Check whether fast reboot is the default operating mode + * Check whether fast reboot is the default operating mode */ - if (fcn == AD_BOOT && !fast_reboot) - fast_reboot = is_fastboot_default(euid); + if (fcn == AD_BOOT && !fast_reboot && !prom_reboot && + zoneid == GLOBAL_ZONEID) + fast_reboot = scf_is_fastboot_default(); if (bename && !fast_reboot) { (void) fprintf(stderr, gettext("%s: -e only valid with -f\n"), @@ -1298,7 +1348,6 @@ main(int argc, char *argv[]) return (EINVAL); } - /* * If fast reboot, do some sanity check on the argument */ @@ -1308,25 +1357,28 @@ main(int argc, char *argv[]) if (zoneid != GLOBAL_ZONEID) { (void) fprintf(stderr, - gettext("%s: fast reboot only valid from global" + gettext("%s: Fast reboot only valid from global" " zone\n"), cmdname); return (EINVAL); } - rc = parse_fastboot_args(bootargs_buf, &is_dryrun, - bename, &failsafe); + rc = parse_fastboot_args(bootargs_buf, sizeof (bootargs_buf), + &is_dryrun, bename, &failsafe); /* * If dry run, or if arguments are invalid, return. */ if (is_dryrun) return (rc); - else if (rc != 0) + else if (rc == EINVAL) goto fail; + else if (rc != 0) + fast_reboot = 0; /* * For all the other errors, we continue on in case user - * user want to force fast reboot. + * user want to force fast reboot, or fall back to regular + * reboot. */ if (strlen(bootargs_buf) != 0) mdep = (uintptr_t)bootargs_buf; @@ -1395,8 +1447,16 @@ main(int argc, char *argv[]) need_check_zones = halt_zones(); } - /* if we're dumping, do the archive update here and don't defer it */ +#if defined(__i386) + /* set new default entry in the GRUB entry */ + if (fbarg_entnum != GRUB_ENTRY_DEFAULT) { + char buf[32]; + (void) snprintf(buf, sizeof (buf), "default=%u", fbarg_entnum); + (void) halt_exec(BOOTADM_PROG, "set-menu", buf, NULL); + } +#endif /* __i386 */ + /* if we're dumping, do the archive update here and don't defer it */ if (cmd == A_DUMP && zoneid == GLOBAL_ZONEID && !nosync) do_archives_update(fast_reboot); @@ -1539,6 +1599,10 @@ fail: } else if (strlen(fastboot_mounted) != 0) { (void) umount(fastboot_mounted); +#if defined(__i386) + } else if (fbarg_used != NULL) { + grub_cleanup_boot_args(fbarg_used); +#endif /* __i386 */ } } diff --git a/usr/src/cmd/halt/smf/Makefile b/usr/src/cmd/halt/smf/Makefile new file mode 100644 index 0000000000..c150613e8e --- /dev/null +++ b/usr/src/cmd/halt/smf/Makefile @@ -0,0 +1,40 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../../Makefile.cmd + +MANIFEST= boot-config.xml +SVCMETHOD= svc-boot-config + +ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) + +all clean clobber lint: + +install: all $(ROOTMANIFEST) $(ROOTSVCMETHOD) + +check: $(CHKMANIFEST) + +include ../../Makefile.targ diff --git a/usr/src/cmd/halt/smf/boot-config.xml b/usr/src/cmd/halt/smf/boot-config.xml new file mode 100644 index 0000000000..45dccefd57 --- /dev/null +++ b/usr/src/cmd/halt/smf/boot-config.xml @@ -0,0 +1,179 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2009 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWcsr:boot-config'> + +<service + name='system/boot-config' + type='service' + version='1'> + + <single_instance /> + + <dependency + name='manifest_import' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/manifest-import:default' /> + </dependency> + + <!-- The boot-config service is made to depend on milestone + multi-user to minimize the chance for panic reboot loop. --> + <dependency + name='boot_multi-user' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/milestone/multi-user' /> + </dependency> + + <instance name='default' enabled = 'true'> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/svc-boot-config' + timeout_seconds='60' /> + + <exec_method + type='method' + name='stop' + exec=':true' + timeout_seconds='60' /> + + <exec_method + type='method' + name='refresh' + exec='/lib/svc/method/svc-boot-config' + timeout_seconds='60' /> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' + value='transient' /> + </property_group> + + <property_group name='general' type='framework'> + <propval name='action_authorization' type='astring' + value='solaris.system.shutdown' /> + <propval name='value_authorization' type='astring' + value='solaris.system.shutdown' /> + </property_group> + + <property_group name='config' type='application'> + <stability value='Stable' /> + <propval name='fastreboot_default' type='boolean' + value='true' /> + <propval name='fastreboot_onpanic' type='boolean' + value='true' /> + <propval name='value_authorization' type='astring' + value='solaris.system.shutdown' /> + </property_group> + + <property_group name='fastreboot_blacklist' type='application'> + <stability value='Unstable' /> + <property name='platforms' type='astring'> + <astring_list> + <value_node value='VirtualBox' /> + <value_node value='VMware Virtual Platform' /> + <value_node value='MCP55' /> + </astring_list> + </property> + </property_group> + </instance> + + <stability value='Stable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + Boot Configuration Management + </loctext> + </common_name> + <description> + <loctext xml:lang='C'> +Apply the configuration defined in this service by uploading the configuration to the kernel. + </loctext> + </description> + <documentation> + <manpage title='reboot' section='1M' + manpath='/usr/share/man' /> + <manpage title='init' section='1M' + manpath='/usr/share/man' /> + <manpage title='uadmin' section='2' + manpath='/usr/share/man' /> + <manpage title='quiesce' section='9E' + manpath='/usr/share/man' /> + </documentation> + <pg_pattern name='config' type='application' + required='true'> + <common_name> + <loctext xml:lang='C'> + Boot Configuration Parameters + </loctext> + </common_name> + <description> + <loctext xml:lang='C'> +Parameters for controlling the reboot behavior. + </loctext> + </description> + <prop_pattern name='fastreboot_default' type='boolean' + required='true'> + <common_name> + <loctext xml:lang='C'> + Fast Reboot by Default + </loctext> + </common_name> + <description> + <loctext xml:lang='C'> +When set to true, reboot(1M) and init(1M) 6 will call uadmin(2) with AD_FASTREOOT, which will bypass firmware. + </loctext> + </description> + </prop_pattern> + <prop_pattern name='fastreboot_onpanic' type='boolean' + required='true'> + <common_name> + <loctext xml:lang='C'> + Fast Reboot on Panic + </loctext> + </common_name> + <description> + <loctext xml:lang='C'> +When set to true, the system will fast reboot on panic. + </loctext> + </description> + </prop_pattern> + </pg_pattern> + + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/halt/smf/svc-boot-config b/usr/src/cmd/halt/smf/svc-boot-config new file mode 100755 index 0000000000..e54214d754 --- /dev/null +++ b/usr/src/cmd/halt/smf/svc-boot-config @@ -0,0 +1,40 @@ +#!/sbin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Start and refresh method script for the boot-config service. +# + +. /lib/svc/share/smf_include.sh + +# +# This service is only valid in the global zone. +# +smf_is_globalzone || exit $SMF_EXIT_OK + +# +# uadmin A_CONFIG AD_UPDATE_BOOT_CONFIG +# +/usr/sbin/uadmin 23 1 +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/initpkg/umountall.sh b/usr/src/cmd/initpkg/umountall.sh index e589feebb9..e3e93ff98a 100644 --- a/usr/src/cmd/initpkg/umountall.sh +++ b/usr/src/cmd/initpkg/umountall.sh @@ -253,6 +253,7 @@ doumounts () { /system/contract | \ /system/object | \ /tmp | \ + /tmp/.libgrubmgmt* | \ /usr | \ /var | \ /var/adm | \ diff --git a/usr/src/cmd/svc/startd/Makefile b/usr/src/cmd/svc/startd/Makefile index f5b20e6e81..582cf512c1 100644 --- a/usr/src/cmd/svc/startd/Makefile +++ b/usr/src/cmd/svc/startd/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -75,6 +75,9 @@ LDLIBS += \ -lumem \ -luutil +LDLIBS_i386 += -lgrubmgmt +LDLIBS += $(LDLIBS_$(MACH)) + FILEMODE = 0555 OWNER = root GROUP = sys diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c index 455b14ab0c..40c5619fb4 100644 --- a/usr/src/cmd/svc/startd/graph.c +++ b/usr/src/cmd/svc/startd/graph.c @@ -126,6 +126,9 @@ #include <sys/statvfs.h> #include <sys/uadmin.h> #include <zone.h> +#if defined(__i386) +#include <libgrubmgmt.h> +#endif /* __i386 */ #include "startd.h" #include "protocol.h" @@ -3479,20 +3482,54 @@ kill_user_procs(void) static void do_uadmin(void) { + const char * const resetting = "/etc/svc/volatile/resetting"; int fd; struct statvfs vfs; time_t now; struct tm nowtm; char down_buf[256], time_buf[256]; + uintptr_t mdep; +#if defined(__i386) + grub_boot_args_t fbarg; +#endif /* __i386 */ - const char * const resetting = "/etc/svc/volatile/resetting"; - + mdep = NULL; fd = creat(resetting, 0777); if (fd >= 0) startd_close(fd); else uu_warn("Could not create \"%s\"", resetting); + /* + * Right now, fast reboot is supported only on i386. + * scf_is_fastboot_default() should take care of it. + * If somehow we got there on unsupported platform - + * print warning and fall back to regular reboot. + */ + if (halting == AD_FASTREBOOT) { +#if defined(__i386) + int rc; + + if ((rc = grub_get_boot_args(&fbarg, NULL, + GRUB_ENTRY_DEFAULT)) == 0) { + mdep = (uintptr_t)&fbarg.gba_bootargs; + } else { + /* + * Failed to read GRUB menu, fall back to normal reboot + */ + halting = AD_BOOT; + uu_warn("Failed to process GRUB menu entry " + "for fast reboot.\n\t%s\n" + "Falling back to regular reboot.\n", + grub_strerror(rc)); + } +#else /* __i386 */ + halting = AD_BOOT; + uu_warn("Fast reboot configured, but not supported by " + "this ISA\n"); +#endif /* __i386 */ + } + /* Kill dhcpagent if we're not using nfs for root */ if ((statvfs("/", &vfs) == 0) && (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) != 0)) @@ -3578,9 +3615,15 @@ do_uadmin(void) } (void) printf("%s%s\n", down_buf, time_buf); - (void) uadmin(A_SHUTDOWN, halting, NULL); + (void) uadmin(A_SHUTDOWN, halting, mdep); uu_warn("uadmin() failed"); +#if defined(__i386) + /* uadmin fail, cleanup grub_boot_args */ + if (halting == AD_FASTREBOOT) + grub_cleanup_boot_args(&fbarg); +#endif /* __i386 */ + if (remove(resetting) != 0 && errno != ENOENT) uu_warn("Could not remove \"%s\"", resetting); } @@ -5066,8 +5109,10 @@ dgraph_set_runlevel(scf_propertygroup_t *pg, scf_property_t *prop) case '6': halting_time = time(NULL); fork_rc_script(rl, stop, B_TRUE); - halting = AD_BOOT; - goto uadmin; + if (scf_is_fastboot_default() && getzoneid() == GLOBAL_ZONEID) + halting = AD_FASTREBOOT; + else + halting = AD_BOOT; uadmin: uu_warn("The system is coming down. Please wait.\n"); diff --git a/usr/src/cmd/uadmin/Makefile b/usr/src/cmd/uadmin/Makefile index d4a3c9b7b2..aad6970d8c 100644 --- a/usr/src/cmd/uadmin/Makefile +++ b/usr/src/cmd/uadmin/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# PROG= uadmin ROOTFS_PROG= $(PROG) @@ -32,7 +30,10 @@ include ../Makefile.cmd OWNER = root GROUP = sys + +i386_LDLIBS += -lscf LDLIBS += -lbsm +LDLIBS += $($(MACH)_LDLIBS) LINTFLAGS = -ux .KEEP_STATE: diff --git a/usr/src/cmd/uadmin/uadmin.c b/usr/src/cmd/uadmin/uadmin.c index 73d1014afb..8959e9c84b 100644 --- a/usr/src/cmd/uadmin/uadmin.c +++ b/usr/src/cmd/uadmin/uadmin.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,6 +36,9 @@ #include <strings.h> #include <signal.h> #include <unistd.h> +#ifdef __i386 +#include <libscf_priv.h> +#endif /* __i386 */ #include <bsm/adt.h> #include <bsm/adt_event.h> @@ -64,6 +67,10 @@ main(int argc, char *argv[]) adt_event_data_t *event = NULL; /* event to be generated */ au_event_t event_id; enum adt_uadmin_fcn fcn_id; +#ifdef __i386 + uint8_t boot_config = 0; +#endif /* __i386 */ + if (argc < 3 || argc > 4) { (void) fprintf(stderr, Usage, argv[0]); @@ -111,6 +118,9 @@ main(int argc, char *argv[]) case A_FTRACE: event_id = ADT_uadmin_ftrace; break; + case A_CONFIG: + event_id = ADT_uadmin_config; + break; case A_SWAPCTL: event_id = ADT_uadmin_swapctl; break; @@ -148,7 +158,7 @@ main(int argc, char *argv[]) #ifdef __i386 fcn_id = ADT_UADMIN_FCN_AD_FASTREBOOT; mdep = NULL; /* Ignore all arguments */ -#else +#else /* __i386 */ fcn = AD_BOOT; fcn_id = ADT_UADMIN_FCN_AD_BOOT; #endif /* __i386 */ @@ -196,6 +206,16 @@ main(int argc, char *argv[]) fcn_id = ADT_UADMIN_FCN_AD_FTRACE_STOP; break; } +#ifdef __i386 + } else if (cmd == A_CONFIG) { + switch (fcn) { + case AD_UPDATE_BOOT_CONFIG: + fcn_id = ADT_UADMIN_FCN_AD_UPDATE_BOOT_CONFIG; + scf_get_boot_config(&boot_config); + mdep = (uintptr_t)(&boot_config); + break; + } +#endif /* __i386 */ } if (geteuid() == 0) { @@ -222,6 +242,11 @@ main(int argc, char *argv[]) break; case A_FTRACE: event->adt_uadmin_ftrace.fcn = fcn_id; + event->adt_uadmin_ftrace.mdep = (char *)mdep; + break; + case A_CONFIG: + event->adt_uadmin_config.fcn = fcn_id; + event->adt_uadmin_config.mdep = (char *)mdep; break; case A_SWAPCTL: event->adt_uadmin_swapctl.fcn = fcn_id; @@ -302,6 +327,7 @@ closeout_audit(int cmd, int fcn) case A_REMOUNT: case A_SWAPCTL: case A_FTRACE: + case A_CONFIG: /* No system discontinuity, don't turn off auditd */ return (0); case A_FREEZE: diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 03a65a3c85..9ca22955f7 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -249,6 +249,7 @@ SUBDIRS += \ libipmi \ libexacct/demo \ libvscan \ + libgrubmgmt \ smbsrv \ scsi \ mms \ @@ -311,6 +312,7 @@ MSGSUBDIRS= \ libdiskmgt \ libdladm \ libdll \ + libgrubmgmt \ libgss \ libidmap \ libinetcfg \ @@ -464,6 +466,7 @@ HDRSUBDIRS= \ libshare \ libidmap \ libvscan \ + libgrubmgmt \ smbsrv \ scsi \ hbaapi \ @@ -590,7 +593,7 @@ libwanboot: libnvpair libresolv libnsl libsocket libdevinfo libinetutil \ libdhcputil openssl libwanbootutil: libnsl pam_modules: libproject passwdutil $(SMARTCARD) smbsrv -libscf: libuutil libmd libgen +libscf: libuutil libmd libgen libsmbios libinetsvc: libscf librestart: libuutil libscf ../cmd/sgs/libdl: ../cmd/sgs/libconv @@ -611,6 +614,7 @@ smbsrv: libsocket libnsl libmd libxnet libpthread librt \ libvscan: libscf scsi: libnvpair mpapi: libpthread libdevinfo libsysevent libnvpair +libgrubmgmt: libdevinfo libzfs libfstyp # # The reason this rule checks for the existence of the diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt index d4630fe5ae..448e788bb0 100644 --- a/usr/src/lib/libbsm/audit_event.txt +++ b/usr/src/lib/libbsm/audit_event.txt @@ -465,6 +465,7 @@ 6252:AUE_cpu_performance:set max CPU freq governor:ss 6253:AUE_cpu_threshold:set CPU freq threshold:ss 6254:AUE_uadmin_thaw:uadmin(1m) - thaw after freeze:ss,na +6255:AUE_uadmin_config:uadmin(1m) - config:ss # # SMF(5) svc.configd events (svcadm(1M) related) diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml index 76b9cfdc5f..93557fd877 100644 --- a/usr/src/lib/libbsm/common/adt.xml +++ b/usr/src/lib/libbsm/common/adt.xml @@ -1233,7 +1233,7 @@ Use is subject to license terms. </entry> </event> <!-- uadmin ftrace and swapctl are not documented in uadmin(2) --> - <event id="AUE_uadmin_ftrace" instance_of="AUE_uadmin_generic_fcn" + <event id="AUE_uadmin_ftrace" instance_of="AUE_uadmin_generic" header="0" idNo="56" omit="JNI"> <title>uadmin ftrace</title> <program>/sbin/uadmin</program> @@ -1266,6 +1266,14 @@ Use is subject to license terms. <external opt="none"/> </entry> </event> + <!-- uadmin config is not documented in uadmin(2) --> + <event id="AUE_uadmin_config" instance_of="AUE_uadmin_generic" + header="0" idNo="119" omit="JNI"> + <title>uadmin config</title> + <program>/sbin/uadmin</program> + <program>/usr/sbin/uadmin</program> + <see>uadmin(1M)</see> + </event> <!-- smbd service event; smbd session setup --> <event id="AUE_smbd_session" header="0" idNo="58" omit="JNI"> @@ -2005,7 +2013,7 @@ Use is subject to license terms. </event> <!-- add new events here with the next higher idNo --> -<!-- Highest idNo is 118, so next is 119, then fix this comment --> +<!-- Highest idNo is 119, so next is 120, then fix this comment --> <!-- end of C Only events --> <!-- @@ -2299,6 +2307,7 @@ Use is subject to license terms. <msg id="AD_NOSYNC">Do not sync filesystems on next A_DUMP</msg> <msg id="AD_FASTREBOOT">Reboot bypassing BIOS and boot loader</msg> <msg id="AD_FASTREBOOT_DRYRUN">Check if system supports reboot bypassing BIOS and boot loader</msg> + <msg id="AD_UPDATE_BOOT_CONFIG">Update boot configuration parameters</msg> <msg id="AD_REUSEINIT">Prepare for AD_REUSABLE</msg> <msg id="AD_REUSABLE">Create reusable statefile</msg> <msg id="AD_REUSEFINI">Revert to normal CPR mode (not reusable)</msg> diff --git a/usr/src/lib/libc/amd64/sys/uadmin.c b/usr/src/lib/libc/amd64/sys/uadmin.c index a705ead079..1038e81bb9 100644 --- a/usr/src/lib/libc/amd64/sys/uadmin.c +++ b/usr/src/lib/libc/amd64/sys/uadmin.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -126,12 +126,11 @@ uadmin(int cmd, int fcn, uintptr_t mdep) head = bargs_scratch; newarg = strtok(bargs_scratch, " "); - if (newarg == NULL) + if (newarg == NULL || newarg[0] == '-') break; /* First argument is rootdir */ - if (newarg[0] != '-' && - strncmp(&newarg[strlen(newarg)-4], + if (strncmp(&newarg[strlen(newarg)-4], "unix", 4) != 0) { newarg = strtok(NULL, " "); off = newarg - head; diff --git a/usr/src/lib/libc/i386/sys/uadmin.c b/usr/src/lib/libc/i386/sys/uadmin.c index a705ead079..1038e81bb9 100644 --- a/usr/src/lib/libc/i386/sys/uadmin.c +++ b/usr/src/lib/libc/i386/sys/uadmin.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -126,12 +126,11 @@ uadmin(int cmd, int fcn, uintptr_t mdep) head = bargs_scratch; newarg = strtok(bargs_scratch, " "); - if (newarg == NULL) + if (newarg == NULL || newarg[0] == '-') break; /* First argument is rootdir */ - if (newarg[0] != '-' && - strncmp(&newarg[strlen(newarg)-4], + if (strncmp(&newarg[strlen(newarg)-4], "unix", 4) != 0) { newarg = strtok(NULL, " "); off = newarg - head; diff --git a/usr/src/lib/libgrubmgmt/Makefile b/usr/src/lib/libgrubmgmt/Makefile new file mode 100644 index 0000000000..5a147360bd --- /dev/null +++ b/usr/src/lib/libgrubmgmt/Makefile @@ -0,0 +1,66 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +include ../Makefile.lib +HDRS= libgrubmgmt.h +HDRDIR= common + +POFILE = libgrubmgmt.po +POFILES = common/libgrub_errno.po + +SUBDIRS = $(MACH) + +# conditional assignments +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint +test := TARGET= test + +.KEEP_STATE: + +all install clean clobber lint: $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +include ../Makefile.targ +include ../../Makefile.msg.targ + +common/libgrub_errno.po := XGETFLAGS += -a + +FRC: diff --git a/usr/src/lib/libgrubmgmt/Makefile.com b/usr/src/lib/libgrubmgmt/Makefile.com new file mode 100644 index 0000000000..30b7bf4790 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/Makefile.com @@ -0,0 +1,55 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +LIBRARY = libgrubmgmt.a +VERS = .1 +OBJECTS = libgrub_cmd.o libgrub_entry.o libgrub_fs.o +OBJECTS += libgrub_menu.o libgrub_bargs.o libgrub_errno.o + +include ../../Makefile.lib +include ../../Makefile.rootfs + +LIBS = $(DYNLIB) $(LINTLIB) + +SRCDIR = ../common + +INCS += -I$(SRCDIR) + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) +# +# Libraries added to the next line must be present in miniroot +# +LDLIBS += -lc -lzfs -ldevinfo -lfstyp + +CFLAGS += $(CCVERBOSE) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_bargs.c b/usr/src/lib/libgrubmgmt/common/libgrub_bargs.c new file mode 100644 index 0000000000..b6c4e5f9a1 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_bargs.c @@ -0,0 +1,225 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains functions for constructing boot arguments + * from GRUB menu for Fast Reboot. + */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/elf.h> + +#include "libgrub_impl.h" + +#if defined(__sparc) +#define CUR_ELFDATA ELFDATA2MSB +#elif defined(__i386) +#define CUR_ELFDATA ELFDATA2LSB +#endif /* __i386 */ + +/* + * Open the kernel file. + * Return zero on sucess or error code otherwise. + * On success the kernel file descriptor is returned in fdp. + */ +static int +get_kernel_fd(const char *path, int *fdp) +{ + const char *bname; + int fd = -1, class, format; + char ident[EI_NIDENT]; + + /* kernel basename must be unix */ + if ((bname = strrchr(path, '/')) == NULL) + bname = path; + else + bname++; + + if (strcmp(bname, "unix") != 0) { + if (strcmp(bname, "xen.gz") == 0) + return (EG_XVMNOTSUP); + return (EG_NOTUNIX); + } + + if ((fd = open64(path, O_RDONLY)) >= 0 && + (pread64(fd, ident, sizeof (ident), 0) == sizeof (ident))) { + + class = ident[EI_CLASS]; + format = ident[EI_DATA]; + + if ((class == ELFCLASS32 || class == ELFCLASS64) && + (memcmp(&ident[EI_MAG0], ELFMAG, 4) == 0) && + format == CUR_ELFDATA) { + *fdp = fd; + return (0); + } + } + + if (fd >= 0) + (void) close(fd); + return (EG_OPENKERNFILE); +} + +/* + * Construct boot arguments for Fast Reboot from the ge_barg field of + * a GRUB menu entry. + * Return 0 on success, errno on failure. + */ +static int +barg2bootargs(const grub_barg_t *barg, grub_boot_args_t *fbarg) +{ + int rc = 0; + char path[BOOTARGS_MAX]; + char rpath[BOOTARGS_MAX]; + const grub_fsdesc_t *fsd; + + assert(fbarg); + bzero(fbarg, sizeof (*fbarg)); + fbarg->gba_kernel_fd = -1; + + if (!IS_BARG_VALID(barg)) + return (EINVAL); + if ((fsd = grub_get_rootfsd(&barg->gb_root)) == NULL) + return (EG_UNKNOWNFS); + + bcopy(fsd, &fbarg->gba_fsd, sizeof (fbarg->gba_fsd)); + bcopy(barg->gb_kernel, fbarg->gba_kernel, sizeof (fbarg->gba_kernel)); + bcopy(barg->gb_module, fbarg->gba_module, sizeof (fbarg->gba_module)); + + if (fbarg->gba_fsd.gfs_mountp[0] == 0 && + (rc = grub_fsd_mount_tmp(&fbarg->gba_fsd, + barg->gb_root.gr_fstyp)) != 0) + return (rc); + + if (snprintf(path, sizeof (path), "%s%s", fbarg->gba_fsd.gfs_mountp, + fbarg->gba_kernel) >= sizeof (path)) { + rc = E2BIG; + goto err_out; + } + (void) strtok(path, " \t"); + (void) clean_path(path); + + /* + * GRUB requires absolute path, no symlinks, so do we + */ + if ((rc = resolvepath(path, rpath, sizeof (rpath))) == -1) + rc = errno; + else { + rpath[rc] = 0; + if (strcmp(rpath, path) != 0) + rc = EG_NOTABSPATH; + else + rc = get_kernel_fd(rpath, &fbarg->gba_kernel_fd); + } + + /* construct bootargs command-line */ + if (rc == 0 && snprintf(fbarg->gba_bootargs, + sizeof (fbarg->gba_bootargs), "%s %s", fbarg->gba_fsd.gfs_mountp, + fbarg->gba_kernel) >= sizeof (fbarg->gba_bootargs)) + rc = E2BIG; + +err_out: + if (rc != 0) + grub_cleanup_boot_args(fbarg); + + return (rc); +} + +/* + * Construct boot arguments for Fast Reboot from grub_menu_t. + * Return 0 on success, errno on failure. + */ +static int +grub_entry_get_boot_args(grub_entry_t *ent, grub_boot_args_t *fbarg) +{ + int rc = EG_INVALIDENT; + + if (IS_ENTRY_VALID(ent) && (rc = grub_entry_construct_barg(ent)) == 0) + return (barg2bootargs(&ent->ge_barg, fbarg)); + else + return (rc); +} + +/* + * Construct boot arguments for Fast Reboot from grub_menu_t and the + * entry number. + * Return 0 on success, errno on failure. + */ +static int +grub_menu_get_boot_args(const grub_menu_t *mp, int num, + grub_boot_args_t *fbarg) +{ + grub_entry_t *ent; + + assert(mp); + assert(fbarg); + + if ((ent = grub_menu_get_entry(mp, num)) == NULL) + return (EG_NOENTRY); + + return (grub_entry_get_boot_args(ent, fbarg)); +} + +/* + * Construct boot arguments from the specified GRUB menu entry. + * Caller must allocate space for fbarg, and call grub_cleanup_boot_args() + * when it's done with fbarg to clean up. + * + * Return 0 on success, errno on failure. + */ +int +grub_get_boot_args(grub_boot_args_t *fbarg, const char *menupath, int num) +{ + int rc; + grub_menu_t *mp; + + assert(fbarg); + if ((rc = grub_menu_init(menupath, &mp)) == 0) { + rc = grub_menu_get_boot_args(mp, num, fbarg); + grub_menu_fini(mp); + } + return (rc); +} + +/* + * Clean up when done with fbarg: close file handle, unmount file + * systems. Must be safe to call even if not all the fields are + * set up. + */ +void +grub_cleanup_boot_args(grub_boot_args_t *fbarg) +{ + if (fbarg == NULL) + return; + + (void) close(fbarg->gba_kernel_fd); + grub_fsd_umount_tmp(&fbarg->gba_fsd); +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c b/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c new file mode 100644 index 0000000000..9b9c972736 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c @@ -0,0 +1,354 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains all the functions that implement the following + * GRUB commands: + * kernel, kernel$, module, module$, findroot, bootfs + * Return 0 on success, errno on failure. + */ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <alloca.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/fs/ufs_mount.h> +#include <sys/dktp/fdisk.h> +#if defined(__i386) +#include <sys/x86_archext.h> +#endif /* __i386 */ + +#include "libgrub_impl.h" + +#define RESET_MODULE(barg) ((barg)->gb_module[0] = 0) + +#if defined(__i386) +static const char cpuid_dev[] = "/dev/cpu/self/cpuid"; + +/* + * Return 1 if the system supports 64-bit mode, 0 if it doesn't, + * or -1 on failure. + */ +static int +cpuid_64bit_capable(void) +{ + int fd, ret = -1; + struct { + uint32_t cp_eax, cp_ebx, cp_ecx, cp_edx; + } cpuid_regs; + + if ((fd = open(cpuid_dev, O_RDONLY)) == -1) + return (ret); + + if (pread(fd, &cpuid_regs, sizeof (cpuid_regs), 0x80000001) == + sizeof (cpuid_regs)) + ret = ((CPUID_AMD_EDX_LM & cpuid_regs.cp_edx) != 0); + + (void) close(fd); + return (ret); +} +#endif /* __i386 */ + + +/* + * Expand $ISAIDR + */ +#if !defined(__i386) +/* ARGSUSED */ +#endif /* __i386 */ +static size_t +barg_isadir_var(char *var, int sz) +{ +#if defined(__i386) + if (cpuid_64bit_capable() == 1) + return (strlcpy(var, "amd64", sz)); +#endif /* __i386 */ + + var[0] = 0; + return (0); +} + +/* + * Expand $ZFS-BOOTFS + */ +static size_t +barg_bootfs_var(const grub_barg_t *barg, char *var, int sz) +{ + int n; + + assert(barg); + if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) == 0) { + n = snprintf(var, sz, "zfs-bootfs=%s,bootpath=\"%s\"", + barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, + barg->gb_root.gr_physpath); + } else { + var[0] = 0; + n = 0; + } + return (n); +} + +/* + * Expand all the variables without appending them more than once. + */ +static int +expand_var(char *arg, size_t argsz, const char *var, size_t varsz, + char *val, size_t valsz) +{ + char *sp = arg; + size_t sz = argsz, len; + char *buf, *dst, *src; + int ret = 0; + + buf = alloca(argsz); + dst = buf; + + while ((src = strstr(sp, var)) != NULL) { + + len = src - sp; + + if (len + valsz > sz) { + ret = E2BIG; + break; + } + + (void) bcopy(sp, dst, len); + (void) bcopy(val, dst + len, valsz); + dst += len + valsz; + sz -= len + valsz; + sp = src + varsz; + } + + if (strlcpy(dst, sp, sz) >= sz) + ret = E2BIG; + + if (ret == 0) + bcopy(buf, arg, argsz); + return (ret); +} + +static int +match_bootfs(zfs_handle_t *zfh, void *data) +{ + int ret; + const char *zfn; + grub_barg_t *barg = (grub_barg_t *)data; + + ret = (zfs_get_type(zfh) == ZFS_TYPE_FILESYSTEM && + (zfn = zfs_get_name(zfh)) != NULL && + strcmp(barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, zfn) == 0); + + if (ret != 0) + barg->gb_walkret = 0; + else + (void) zfs_iter_filesystems(zfh, match_bootfs, barg); + + zfs_close(zfh); + return (barg->gb_walkret == 0); +} + +static void +reset_root(grub_barg_t *barg) +{ + (void) memset(&barg->gb_root, 0, sizeof (barg->gb_root)); + barg->gb_bootsign[0] = 0; + barg->gb_kernel[0] = 0; + RESET_MODULE(barg); +} + +/* ARGSUSED */ +int +skip_line(const grub_line_t *lp, grub_barg_t *barg) +{ + return (0); +} + +/* ARGSUSED */ +int +error_line(const grub_line_t *lp, grub_barg_t *barg) +{ + return (EG_INVALIDLINE); +} + +int +kernel(const grub_line_t *lp, grub_barg_t *barg) +{ + RESET_MODULE(barg); + if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= + sizeof (barg->gb_kernel)) + return (E2BIG); + + return (0); +} + +int +module(const grub_line_t *lp, grub_barg_t *barg) +{ + if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= + sizeof (barg->gb_module)) + return (E2BIG); + + return (0); +} + +int +dollar_kernel(const grub_line_t *lp, grub_barg_t *barg) +{ + int ret; + size_t bfslen, isalen; + char isadir[32]; + char bootfs[BOOTARGS_MAX]; + + RESET_MODULE(barg); + if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= + sizeof (barg->gb_kernel)) + return (E2BIG); + + bfslen = barg_bootfs_var(barg, bootfs, sizeof (bootfs)); + isalen = barg_isadir_var(isadir, sizeof (isadir)); + + if (bfslen >= sizeof (bootfs) || isalen >= sizeof (isadir)) + return (EINVAL); + + if ((ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), + ZFS_BOOT_VAR, strlen(ZFS_BOOT_VAR), bootfs, bfslen)) != 0) + return (ret); + + ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), + ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); + + return (ret); +} + +int +dollar_module(const grub_line_t *lp, grub_barg_t *barg) +{ + int ret; + size_t isalen; + char isadir[32]; + + if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= + sizeof (barg->gb_module)) + return (E2BIG); + + if ((isalen = barg_isadir_var(isadir, sizeof (isadir))) >= sizeof + (isadir)) + return (EINVAL); + + ret = expand_var(barg->gb_module, sizeof (barg->gb_module), + ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); + + return (ret); +} + + +int +findroot(const grub_line_t *lp, grub_barg_t *barg) +{ + size_t sz, bsz; + const char *sign; + + reset_root(barg); + + sign = lp->gl_arg; + barg->gb_prtnum = (uint_t)PRTNUM_INVALID; + barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; + + if (sign[0] == '(') { + const char *pos; + + ++sign; + if ((pos = strchr(sign, ',')) == NULL || (sz = pos - sign) == 0) + return (EG_FINDROOTFMT); + + ++pos; + if (!IS_PRTNUM_VALID(barg->gb_prtnum = pos[0] - '0')) + return (EG_FINDROOTFMT); + + ++pos; + if (pos[0] != ',' || + !IS_SLCNUM_VALID(barg->gb_slcnum = pos[1]) || + pos[2] != ')') + return (EG_FINDROOTFMT); + } else { + sz = strlen(sign); + } + + bsz = strlen(BOOTSIGN_DIR "/"); + if (bsz + sz + 1 > sizeof (barg->gb_bootsign)) + return (E2BIG); + + bcopy(BOOTSIGN_DIR "/", barg->gb_bootsign, bsz); + bcopy(sign, barg->gb_bootsign + bsz, sz); + barg->gb_bootsign [bsz + sz] = 0; + + return (grub_find_bootsign(barg)); +} + +int +bootfs(const grub_line_t *lp, grub_barg_t *barg) +{ + zfs_handle_t *zfh; + grub_menu_t *mp = barg->gb_entry->ge_menu; + char *gfs_devp; + size_t gfs_dev_len; + + /* Check if root is zfs */ + if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) != 0) + return (EG_NOTZFS); + + gfs_devp = barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev; + gfs_dev_len = sizeof (barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev); + + /* + * If the bootfs value is the same as the bootfs for the pool, + * do nothing. + */ + if (strcmp(lp->gl_arg, gfs_devp) == 0) + return (0); + + if (strlcpy(gfs_devp, lp->gl_arg, gfs_dev_len) >= gfs_dev_len) + return (E2BIG); + + /* check if specified bootfs belongs to the root pool */ + if ((zfh = zfs_open(mp->gm_fs.gf_lzfh, + barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_dev, + ZFS_TYPE_FILESYSTEM)) == NULL) + return (EG_OPENZFS); + + barg->gb_walkret = EG_UNKBOOTFS; + (void) zfs_iter_filesystems(zfh, match_bootfs, barg); + zfs_close(zfh); + + if (barg->gb_walkret == 0) + (void) grub_fsd_get_mountp(barg->gb_root.gr_fs + + GRBM_ZFS_BOOTFS, MNTTYPE_ZFS); + + return (barg->gb_walkret); +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_cmd.def b/usr/src/lib/libgrubmgmt/common/libgrub_cmd.def new file mode 100644 index 0000000000..eea69c889c --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_cmd.def @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef menu_cmd +#define menu_cmd(cmd, num, flag, parsef) +#endif /* menu_cmd */ + +#ifndef menu_cmd_end +#define menu_cmd_end(num) +#endif /* menu_cmd */ + +/* + * Menu commands related info: + * first field - command, + * second field - enum for command + * third field - command flags + * forth field - parse function + */ + +/* + * NOTE: bootadm likes to have GRBM_KERNEL_DOLLAR_CMD == GRBM_KERNEL_CMD + 1 + * and GRBM_MODULE_DOLLAR_CMD == GRBM_MODULE_CMD + 1 + */ +menu_cmd("", GRBM_EMPTY_CMD, GRUB_LINE_EMPTY, skip_line) +menu_cmd(" ", GRBM_SEP_CMD, GRUB_LINE_INVALID, error_line) +menu_cmd("#", GRBM_COMMENT_CMD, GRUB_LINE_COMMENT, skip_line) +menu_cmd("default", GRBM_DEFAULT_CMD, GRUB_LINE_GLOBAL, error_line) +menu_cmd("timeout", GRBM_TIMEOUT_CMD, GRUB_LINE_GLOBAL, error_line) +menu_cmd("title", GRBM_TITLE_CMD, GRUB_LINE_TITLE, skip_line) +menu_cmd("root", GRBM_ROOT_CMD, GRUB_LINE_ENTRY, error_line) +menu_cmd("kernel", GRBM_KERNEL_CMD, GRUB_LINE_ENTRY, kernel) +menu_cmd("kernel$", GRBM_KERNEL_DOLLAR_CMD, GRUB_LINE_ENTRY, dollar_kernel) +menu_cmd("module", GRBM_MODULE_CMD, GRUB_LINE_ENTRY, module) +menu_cmd("module$", GRBM_MODULE_DOLLAR_CMD, GRUB_LINE_ENTRY, dollar_module) +menu_cmd("chainloader", GRBM_CHAINLOADER_CMD, GRUB_LINE_ENTRY, error_line) +menu_cmd("args", GRBM_ARGS_CMD, GRUB_LINE_ENTRY, error_line) +menu_cmd("findroot", GRBM_FINDROOT_CMD, GRUB_LINE_ENTRY, findroot) +menu_cmd("bootfs", GRBM_BOOTFS_CMD, GRUB_LINE_ENTRY, bootfs) + +menu_cmd_end(GRBM_CMD_NUM) /* Should be the last one */ + +#undef menu_cmd +#undef menu_cmd_end diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_entry.c b/usr/src/lib/libgrubmgmt/common/libgrub_entry.c new file mode 100644 index 0000000000..995b3a1c20 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_entry.c @@ -0,0 +1,191 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains all the functions that get/set fields + * in a GRUB menu entry. + */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <sys/types.h> +#include <assert.h> +#include <ctype.h> + +#include "libgrub_cmd.def" +#include "libgrub_impl.h" + +typedef int (*barg_parsef_t)(const grub_line_t *, grub_barg_t *); +static const barg_parsef_t barg_parse[] = { +#define menu_cmd(cmd, num, flag, parsef) parsef, +#include "libgrub_cmd.def" +}; + +/* + * Remove extra '/', stops at first isspace character. + * Return new string length. + */ +size_t +clean_path(char *path) +{ + int i, c; + size_t k, n; + + n = strlen(path) + 1; + + for (i = 0; (c = path[i]) != 0 && !isspace(c); i++) { + if (c == '/' && (k = strspn(path + i, "/") - 1) != 0) { + /* bcopy should deal with overlapping buffers */ + n -= k; + bcopy(path + i + k, path + i, n - i); + } + } + return (n - 1); +} + +/* + * Construct boot command line from the ge_barg field + */ +static size_t +barg_cmdline(const grub_barg_t *barg, char *cmd, size_t size) +{ + size_t n; + const grub_fsdesc_t *fsd; + + if (!IS_BARG_VALID(barg) || + (fsd = grub_get_rootfsd(&barg->gb_root)) == NULL) + return ((size_t)-1); + + /* if disk/top dataset is mounted, use mount point */ + if (fsd->gfs_mountp[0] != 0) { + if ((n = snprintf(cmd, size, "%s%s", fsd->gfs_mountp, + barg->gb_kernel)) >= size) + return (n); + return (clean_path(cmd)); + } else + return (snprintf(cmd, size, "%s %s", fsd->gfs_dev, + barg->gb_kernel)); +} + + +/* + * Construct ge_barg field based on the other fields of the entry. + * Return 0 on success, errno on failure. + */ +int +grub_entry_construct_barg(grub_entry_t *ent) +{ + int ret = 0; + grub_barg_t *barg; + grub_line_t *lp, *lend; + grub_menu_t *mp; + + assert(ent); + + barg = &ent->ge_barg; + mp = ent->ge_menu; + + assert(barg); + assert(mp); + + (void) memset(barg, 0, sizeof (*barg)); + barg->gb_entry = ent; + (void) bcopy(&mp->gm_root, &barg->gb_root, sizeof (barg->gb_root)); + + lend = ent->ge_end->gl_next; + for (lp = ent->ge_start; lp != lend; lp = lp->gl_next) { + if (lp->gl_cmdtp >= GRBM_CMD_NUM) + ret = EG_INVALIDCMD; + else + ret = barg_parse[lp->gl_cmdtp](lp, barg); + + if (ret != 0) + break; + } + + barg->gb_errline = lp; + if (ret == 0) { + /* at least kernel and module should be defined */ + if (barg->gb_kernel[0] != 0 && barg->gb_module[0] != 0) + barg->gb_flags |= GRBM_VALID_FLAG; + } + + return (ret); +} + +const char * +grub_entry_get_fstyp(const grub_entry_t *ent) +{ + if (IS_ENTRY_BARG_VALID(ent)) + return (ent->ge_barg.gb_root.gr_fstyp); + else + return (NULL); +} + +const char * +grub_entry_get_kernel(const grub_entry_t *ent) +{ + if (IS_ENTRY_BARG_VALID(ent)) + return (ent->ge_barg.gb_kernel); + else + return (NULL); +} + +const char * +grub_entry_get_module(const grub_entry_t *ent) +{ + if (IS_ENTRY_BARG_VALID(ent)) + return (ent->ge_barg.gb_module); + else + return (NULL); +} + +const char * +grub_entry_get_error_desc(const grub_entry_t *ent) +{ + assert(ent != NULL); + return ("Not implemented"); +} + +const grub_fsdesc_t * +grub_entry_get_rootfs(const grub_entry_t *ent) +{ + if (IS_ENTRY_BARG_VALID(ent)) + return (grub_get_rootfsd(&ent->ge_barg.gb_root)); + else + return (NULL); +} + +size_t +grub_entry_get_cmdline(grub_entry_t *ent, char *cmdline, size_t size) +{ + if (IS_ENTRY_VALID(ent) && (grub_entry_construct_barg(ent) == 0)) + return (barg_cmdline(&ent->ge_barg, cmdline, size)); + else + return ((size_t)-1); + +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_errno.c b/usr/src/lib/libgrubmgmt/common/libgrub_errno.c new file mode 100644 index 0000000000..3d09aa22e7 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_errno.c @@ -0,0 +1,64 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <libintl.h> +#include "libgrub_errno.h" + +#define MAKE_STRING(x) # x + +static const struct { + int ge_num; /* error number */ + char *ge_name; /* error name */ + char *ge_msg; /* error message */ +} _grub_errstr[] = { +/* + * TRANSLATION_NOTE + * The following message strings that begin with EG_ do not + * need to be translated. + */ +#define grub_errno_def(num, desc) { num, MAKE_STRING(num), desc}, +#include "libgrub_errno.def" +}; + +#define GRUB_ERRNO_INDEX(n) ((n) - (EG_START + 1)) + +const char * +grub_strerror(int err) +{ + return (err <= EG_START || err >= EG_END ? + strerror(err) : + dgettext(TEXT_DOMAIN, _grub_errstr[ GRUB_ERRNO_INDEX(err)].ge_msg)); +} + +const char * +grub_errname(int err) +{ + return (err <= EG_START || err >= EG_END ? + gettext("Not libgrubmgmt specific") : + gettext(_grub_errstr[ GRUB_ERRNO_INDEX(err)].ge_name)); +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_errno.def b/usr/src/lib/libgrubmgmt/common/libgrub_errno.def new file mode 100644 index 0000000000..99732da683 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_errno.def @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef grub_errno_start +#define grub_errno_start(num) +#endif /* grub_errno_start */ + +#ifndef grub_errno_def +#define grub_errno_def(num, desc) +#endif /* grub_errno_def */ + +#ifndef grub_errno_end +#define grub_errno_end(num) +#endif /* grub_errno_end */ + +/* + * !!! Should ALWAYS be the first one + */ +grub_errno_start(EG_START = -10000) + +grub_errno_def(EG_INVALIDCMD, "Invalid GRUB command") +grub_errno_def(EG_INVALIDMENU, "Invalid GRUB menu") +grub_errno_def(EG_INVALIDENT, "Invalid GRUB entry") +grub_errno_def(EG_INVALIDLINE, "Invalid GRUB line") +grub_errno_def(EG_INVALIDBARG, "Invalid GRUB boot arguments") +grub_errno_def(EG_FINDROOTFMT, "Invalid format for findroot") +grub_errno_def(EG_FINDROOTPRT, "Invalid partition number for findroot") +grub_errno_def(EG_FINDROOTSLC, "Invalid slice number for findroot") +grub_errno_def(EG_UNKNOWNFS, "Unknown file system") +grub_errno_def(EG_NOTZFS, "File system is not ZFS") +grub_errno_def(EG_OPENZFS, "Failed to open ZFS file system") +grub_errno_def(EG_INITFS, "Initialize file system") +grub_errno_def(EG_MOUNTFS, "Failed to mount file system") +grub_errno_def(EG_OPENFILE, "Failed to open file") +grub_errno_def(EG_NOTUNIX, "Kernel file is not unix") +grub_errno_def(EG_NOTABSPATH, "Kernel path is not absolute") +grub_errno_def(EG_OPENKERNFILE, "Failed to open kernel file") +grub_errno_def(EG_OPENMNTTAB, "Failed to open mnttab") +grub_errno_def(EG_GETMNTTAB, "Failed to get mnttab") +grub_errno_def(EG_CURROOT, "Failed to get current root info") +grub_errno_def(EG_NUMTOOBIG, "Requested entry number is too big") +grub_errno_def(EG_NOENTRY, "No such entry found") +grub_errno_def(EG_XVMNOTSUP, "xVM is not supported") +grub_errno_def(EG_BOOTSIGN, "Bootsign not found") +grub_errno_def(EG_UNKBOOTFS, "Unknown bootfs filesystem") + +/* + * !!! Should ALWAYS be the last one + */ +grub_errno_end(EG_END) + +#undef grub_errno_start +#undef grub_errno_def +#undef grub_errno_end diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_errno.h b/usr/src/lib/libgrubmgmt/common/libgrub_errno.h new file mode 100644 index 0000000000..d11676621c --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_errno.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBGRUB_ERRNO_H +#define _LIBGRUB_ERRNO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * libgrubmgmt specific error codes + */ +enum { +#define grub_errno_start(num) num, +#define grub_errno_def(num, desc) num, +#define grub_errno_end(num) num +#include "libgrub_errno.def" +}; + +extern const char *grub_errname(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBGRUB_ERRNO_H */ diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_fs.c b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c new file mode 100644 index 0000000000..4846877ab2 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c @@ -0,0 +1,559 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains all the functions that manipualte the file + * system where the GRUB menu resides. + */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/fs/ufs_mount.h> +#include <sys/dktp/fdisk.h> +#include <libfstyp.h> + +#include "libgrub_impl.h" + +static int +slice_match(const char *physpath, int slice) +{ + const char *pos; + + return ((pos = strrchr(physpath, slice)) == NULL || + pos[1] != 0 || pos[-1] != ':'); +} + +/* + * Returns zero if path contains ufs + */ +static int +slice_ufs(const char *path) +{ + int fd, ret; + const char *id; + fstyp_handle_t hdl; + + fd = open(path, O_RDONLY); + if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) { + ret = fstyp_ident(hdl, "ufs", &id); + fstyp_fini(hdl); + } + (void) close(fd); + return (ret); +} + + +static int +get_sol_prtnum(const char *physpath) +{ + int i, fd; + char *pos; + size_t sz; + struct mboot *mb; + struct ipart *ipart; + char boot_sect[512]; + char rdev[MAXNAMELEN]; + + (void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath); + + if ((pos = strrchr(rdev, ':')) == NULL) + return (PRTNUM_INVALID); + + pos[1] = SLCNUM_WHOLE_DISK; + + fd = open(rdev, O_RDONLY); + sz = read(fd, boot_sect, sizeof (boot_sect)); + (void) close(fd); + + if (sz != sizeof (boot_sect)) + return (PRTNUM_INVALID); + + /* parse fdisk table */ + mb = (struct mboot *)(uintptr_t)boot_sect; + ipart = (struct ipart *)(uintptr_t)mb->parts; + for (i = 0; i < FD_NUMPART; ++i) { + if (ipart[i].systid == SUNIXOS || ipart[i].systid == SUNIXOS2) + return (i); + } + return (PRTNUM_INVALID); +} + +/* + * Get physpath, topfs and bootfs for ZFS root dataset. + * Return 0 on success, non-zero (not errno) on failure. + */ +static int +get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root) +{ + int ret; + zpool_handle_t *zph; + const char *name; + + if (zfs_get_type(zfh) != ZFS_TYPE_FILESYSTEM || + (name = zfs_get_name(zfh)) == NULL || + (zph = zpool_open(fs->gf_lzfh, name)) == NULL) + return (-1); + + if ((ret = zpool_get_physpath(zph, root->gr_physpath, + sizeof (root->gr_physpath))) == 0 && + (ret = zpool_get_prop(zph, ZPOOL_PROP_BOOTFS, + root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, + sizeof (root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev), NULL)) == 0) { + + (void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name, + sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev)); + (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS, + MNTTYPE_ZFS); + (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS, + MNTTYPE_ZFS); + } + + zpool_close(zph); + return (ret); +} + +/* + * On entry physpath parameter supposed to contain: + * <disk_physpath>[<space><disk_physpath>]*. + * Retireives first <disk_physpath> that matches both partition and slice. + * If any partition and slice is acceptable, first <disk_physpath> is returned. + */ +static int +get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum) +{ + int ret; + char *tmp, *tok; + + if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) { + (void) strtok(physpath, " "); + return (0); + } + + if ((tmp = strdup(physpath)) == NULL) + return (errno); + + ret = ENODEV; + for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) { + if ((ret = (slice_match(tok, slcnum) != 0 || + get_sol_prtnum(tok) != prtnum)) == 0) { + (void) strcpy(physpath, tok); + break; + } + } + + free(tmp); + return (ret); +} + +static int +zfs_bootsign(zfs_handle_t *zfh, void *data) +{ + grub_barg_t *barg; + grub_menu_t *menu; + struct stat st; + char path[MAXPATHLEN]; + + barg = (grub_barg_t *)data; + menu = barg->gb_entry->ge_menu; + + do { + if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 || + get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum, + barg->gb_slcnum) != 0) + break; + + /* + * if top zfs dataset is not mounted, mount it now + */ + if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) { + if (grub_fsd_mount_tmp(barg->gb_root.gr_fs + + GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0) + break; + } + + /* check that bootsign exists and it is a regular file */ + (void) snprintf(path, sizeof (path), "%s%s", + barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp, + barg->gb_bootsign); + + if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 || + (st.st_mode & S_IRUSR) == 0) + break; + + (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS, + sizeof (barg->gb_root.gr_fstyp)); + barg->gb_walkret = 0; + /* LINTED: E_CONSTANT_CONDITION */ + } while (0); + + grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS); + zfs_close(zfh); + + /* return non-zero to terminate the walk */ + return (barg->gb_walkret == 0); +} + +static int +get_devlink(di_devlink_t dl, void *arg) +{ + const char *path; + grub_barg_t *barg; + + barg = (grub_barg_t *)arg; + if ((path = di_devlink_path(dl)) != NULL) + (void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path, + sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)); + return (DI_WALK_TERMINATE); +} + +static int +ufs_bootsign_check(grub_barg_t *barg) +{ + int ret; + struct stat st; + grub_menu_t *mp; + char path[MAXPATHLEN]; + + mp = barg->gb_entry->ge_menu; + + /* get /dev/dsk link */ + if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/", + barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0) + return (errno); + /* + * if disk is not mounted, mount it now + */ + if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS, + MNTTYPE_UFS) != 0) { + if ((ret = + slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 || + (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS, + MNTTYPE_UFS)) != 0) + return (ret); + } + + (void) snprintf(path, sizeof (path), "%s%s", + barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign); + + if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) && + (st.st_mode & S_IRUSR) != 0) { + barg->gb_walkret = 0; + (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS, + sizeof (barg->gb_root.gr_fstyp)); + } + + grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS); + return (barg->gb_walkret); +} + +static int +ufs_bootsign(di_node_t node, di_minor_t minor, void *arg) +{ + uint_t prtnum; + char *name, *path; + grub_barg_t *barg; + + barg = (grub_barg_t *)arg; + + if (di_minor_spectype(minor) != S_IFBLK) + return (DI_WALK_CONTINUE); + + name = di_minor_name(minor); + if (name[0] != barg->gb_slcnum || name[1] != 0) + return (DI_WALK_CONTINUE); + + path = di_devfs_path(node); + (void) snprintf(barg->gb_root.gr_physpath, + sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum); + di_devfs_path_free(path); + + prtnum = get_sol_prtnum(barg->gb_root.gr_physpath); + if (!IS_PRTNUM_VALID(prtnum)) + return (DI_WALK_CONTINUE); + + /* + * check only specified partition, slice + */ + + if (IS_PRTNUM_VALID(barg->gb_prtnum)) { + if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0) + return (DI_WALK_CONTINUE); + return (DI_WALK_TERMINATE); + } + + /* + * Walk through all slices in found solaris partition + */ + + barg->gb_prtnum = prtnum; + minor = DI_MINOR_NIL; + + while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { + + if (di_minor_spectype(minor) != S_IFBLK) + continue; + + name = di_minor_name(minor); + if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0) + continue; + + barg->gb_slcnum = name[0]; + path = strrchr(barg->gb_root.gr_physpath, ':'); + path[1] = barg->gb_slcnum; + + if (ufs_bootsign_check(barg) == 0) + return (DI_WALK_TERMINATE); + } + + barg->gb_prtnum = (uint_t)PRTNUM_INVALID; + barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; + return (DI_WALK_CONTINUE); +} + +/* + * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios + * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root, + * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found + * on other slices. + * That function first searches through all top datasets of active zpools, + * then if bootsign still not found walks through all disks and tries to + * find ufs slice with the bootsign. + */ +int +grub_find_bootsign(grub_barg_t *barg) +{ + grub_menu_t *mp; + mp = barg->gb_entry->ge_menu; + + /* try to find bootsign over zfs pools */ + barg->gb_walkret = EG_BOOTSIGN; + (void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg); + + /* try ufs now */ + if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot, + DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0) + return (errno); + + return (barg->gb_walkret); +} + +/* + * Get current root file system. + * Return 0 on success, errno code on failure. + */ +int +grub_current_root(grub_fs_t *fs, grub_root_t *root) +{ + int rc = 0; + FILE *fp = NULL; + char *name = NULL; + zfs_handle_t *zfh = NULL; + struct mnttab mp = {0}; + struct mnttab mpref = {0}; + char buf[MAXNAMELEN] = {0}; + + mpref.mnt_mountp = "/"; + + if ((fp = fopen(MNTTAB, "r")) == NULL) + return (errno); + + /* + * getmntany returns non-zero for failure, and sets errno + */ + rc = getmntany(fp, &mp, &mpref); + if (rc != 0) + rc = errno; + + (void) fclose(fp); + + if (rc != 0) + return (rc); + + (void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp)); + + if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) { + + (void) strlcpy(buf, mp.mnt_special, sizeof (buf)); + if ((name = strtok(buf, "/")) == NULL) + return (EG_CURROOT); + + if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) == + NULL) + return (EG_OPENZFS); + + /* + * get_zfs_root returns non-zero on failure, not + * errno. + */ + if (get_zfs_root(zfh, fs, root)) + rc = EG_CURROOT; + + zfs_close(zfh); + + } else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) { + (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special, + sizeof (root->gr_fs[GRBM_UFS].gfs_dev)); + (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp, + sizeof (root->gr_fs[GRBM_UFS].gfs_mountp)); + } else { + rc = EG_UNKNOWNFS; + } + + return (rc); +} + +grub_fsdesc_t * +grub_get_rootfsd(const grub_root_t *root) +{ + grub_fsdesc_t *fsd = NULL; + + assert(root); + if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0) + fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS; + else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0) + fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS; + + return (fsd); +} + +/* + * Gets file systems mount point if any. + * Return 0 if filesystem is mounted, errno on failure. + */ +int +grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp) +{ + int rc; + FILE *fp = NULL; + struct mnttab mp = {0}; + struct mnttab mpref = {0}; + + fsd->gfs_mountp[0] = 0; + + if ((fp = fopen(MNTTAB, "r")) == NULL) + return (errno); + + mpref.mnt_special = fsd->gfs_dev; + mpref.mnt_fstype = fstyp; + + if ((rc = getmntany(fp, &mp, &mpref)) == 0) + (void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp, + sizeof (fsd->gfs_mountp)); + else + rc = EG_GETMNTTAB; + + (void) fclose(fp); + return (rc); +} + +static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX"; + +/* + * Mount file system at tmp_mountp. + * Return 0 on success, errno on failure. + */ +int +grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp) +{ + const char *pos; + void *data = NULL; + int dtsz = 0; + struct ufs_args ufs_args = {UFSMNT_LARGEFILES}; + char mntopts[MNT_LINE_MAX] = ""; + int rc = 0; + + assert(fsd); + assert(!fsd->gfs_is_tmp_mounted); + + fsd->gfs_mountp[0] = 0; + + if (strcmp(fstyp, MNTTYPE_UFS) == 0) { + (void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts)); + data = &ufs_args; + dtsz = sizeof (ufs_args); + } else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) { + return (EG_UNKNOWNFS); + } + + /* construct name for temporary mount point */ + pos = strrchr(fsd->gfs_dev, '/'); + pos = (pos == NULL) ? fsd->gfs_dev : pos + 1; + + (void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp), + tmp_mountp, pos); + if (mkdtemp(fsd->gfs_mountp) != NULL) { + if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp, + MS_DATA | MS_OPTIONSTR | MS_RDONLY, + fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) { + /* + * mount failed, collect errno and remove temp dir + */ + rc = errno; + (void) rmdir(fsd->gfs_mountp); + } + } else { + rc = errno; + } + + if (rc != 0) + fsd->gfs_mountp[0] = 0; + + /* + * Note that valid values for gfs_is_tmp_mounted are 0,1. + * Any other value indicates that something bad happened. + * Probably grub_fsd_umount_tmp() wasn't called or didn't + * work as expected. + */ + fsd->gfs_is_tmp_mounted += (rc == 0); + return (rc); +} + +/* + * Unmount file system at tmp_mountp. + */ +void +grub_fsd_umount_tmp(grub_fsdesc_t *fsd) +{ + if (fsd == NULL) + return; + + if (fsd->gfs_is_tmp_mounted) { + if (fsd->gfs_mountp[0] != 0) { + (void) umount2(fsd->gfs_mountp, 0); + (void) rmdir(fsd->gfs_mountp); + fsd->gfs_mountp[0] = 0; + } + fsd->gfs_is_tmp_mounted = 0; + } +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_impl.h b/usr/src/lib/libgrubmgmt/common/libgrub_impl.h new file mode 100644 index 0000000000..0d33400e7d --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_impl.h @@ -0,0 +1,248 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _GRBMIMPL_H +#define _GRBMIMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mntent.h> +#include <sys/uadmin.h> +#include <libzfs.h> +#include <libdevinfo.h> +#include "libgrubmgmt.h" +#include "libgrub_errno.h" + +/* + * Macros for processing the GRUB menu. + */ +#define GRUB_MENU "/boot/grub/menu.lst" +#define BOOTSIGN_DIR "/boot/grub/bootsign" +#define BOOTSIGN_LEN (2 * MAXNAMELEN) +#define ZFS_BOOT_VAR "$ZFS-BOOTFS" /* ZFS boot option */ +#define ISADIR_VAR "$ISADIR" /* ISADIR option */ + +#define PRTNUM_INVALID -1 /* Partition number invlaid */ +#define SLCNUM_INVALID -1 /* Slice number invalid */ + +#define SLCNUM_FIRST 'a' +#define SLCNUM_WHOLE_DISK 'q' + +#define IS_SLCNUM_VALID(x) ((x) >= SLCNUM_FIRST && (x) < SLCNUM_WHOLE_DISK) +#define IS_PRTNUM_VALID(x) ((uint_t)(x) < FD_NUMPART) + +#define GRBM_VALID_FLAG ((uint_t)1 << 31) +#define GRBM_MAXLINE 8192 +#define IS_ENTRY_VALID(ent) ((ent) && ((ent)->ge_flags & GRBM_VALID_FLAG)) +#define IS_BARG_VALID(barg) ((barg)->gb_flags & GRBM_VALID_FLAG) +#define IS_ENTRY_BARG_VALID(ent) \ + (IS_ENTRY_VALID(ent) && IS_BARG_VALID(&(ent)->ge_barg)) +#define IS_LINE2BIG(buf, bfsz, len) \ + ((len = strlen(buf)) == (bfsz) - 1 && (buf)[len - 1] != '\n') +#define IS_STR_NULL(x) ((x) == NULL ? "NULL" : (x)) +#define GRUB_ENTRY_IS_XVM(fbarg) \ + (strstr(fbarg.gba_kernel, "xen.gz") != NULL) + +enum { +#define menu_cmd(cmd, num, flags, parsef) num, +#define menu_cmd_end(num) num +#include "libgrub_cmd.def" +}; + +typedef struct _grub_fs { + di_node_t gf_diroot; + di_devlink_handle_t gf_dvlh; + libzfs_handle_t *gf_lzfh; +} grub_fs_t; + + +typedef struct _grub_cmd_desc { + const char *gcd_cmd; + uint_t gcd_num; + int gcd_flags; +} grub_cmd_desc_t; + + +enum { + GRBM_UFS = 0, + GRBM_ZFS_TOPFS = 0, + GRBM_FS_TOP = 0, + GRBM_ZFS_BOOTFS, + GRBM_FS_MAX +}; + +typedef struct _grub_root { + char gr_fstyp[MNTMAXSTR]; + char gr_physpath[MAXPATHLEN]; + grub_fsdesc_t gr_fs[GRBM_FS_MAX]; +} grub_root_t; + +/* + * Data struct for the boot argument constructed from a GRUB menu entry + */ +typedef struct _grub_barg { + grub_entry_t *gb_entry; + grub_line_t *gb_errline; + int gb_walkret; /* set to 0 when match found */ + uint_t gb_flags; + uint_t gb_prtnum; + uint_t gb_slcnum; + grub_root_t gb_root; + char gb_bootsign[BOOTSIGN_LEN]; + char gb_kernel[BOOTARGS_MAX]; + char gb_module[BOOTARGS_MAX]; +} grub_barg_t; + + +/* GRUB menu per-line classification */ +enum { + GRUB_LINE_INVALID = 0, + GRUB_LINE_EMPTY, + GRUB_LINE_COMMENT, + GRUB_LINE_GLOBAL, + GRUB_LINE_ENTRY, + GRUB_LINE_TITLE +}; + +/* + * Data structures for menu.lst contents + */ +struct grub_line { + grub_line_t *gl_next; + grub_line_t *gl_prev; + int gl_line_num; /* Line number in menu.lst */ + int gl_entry_num; /* menu boot entry #. */ + /* GRUB_ENTRY_DEFAULT if none */ + int gl_flags; + uint_t gl_cmdtp; /* recognized command type */ + char *gl_cmd; + char *gl_sep; + char *gl_arg; + char *gl_line; +}; + +struct grub_entry { + grub_menu_t *ge_menu; /* grub_menu_t it belongs to */ + grub_entry_t *ge_next; + grub_entry_t *ge_prev; + grub_line_t *ge_start; + grub_line_t *ge_end; + int ge_entry_num; + uint_t ge_flags; + uint_t ge_emask; /* invalid lines mask */ + grub_barg_t ge_barg; +}; + +struct grub_menu { + grub_line_t *gm_start; + grub_line_t *gm_end; + grub_line_t *gm_curdefault; /* line containing default */ + grub_entry_t *gm_ent_start; /* os entries */ + grub_entry_t *gm_ent_end; + grub_entry_t *gm_ent_default; /* default entry */ + uint_t gm_line_num; /* number of lines processed */ + uint_t gm_entry_num; /* number of entries processed */ + char gm_path[MAXPATHLEN]; + grub_fs_t gm_fs; + grub_root_t gm_root; +}; + +/* File system helper functions */ +int grub_current_root(grub_fs_t *, grub_root_t *); +grub_fsdesc_t *grub_get_rootfsd(const grub_root_t *); +int grub_fsd_mount_tmp(grub_fsdesc_t *, const char *); +void grub_fsd_umount_tmp(grub_fsdesc_t *); +int grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp); +int grub_find_bootsign(grub_barg_t *barg); + + +/* GRUB menu parse functions */ +int skip_line(const grub_line_t *lp, grub_barg_t *barg); +int error_line(const grub_line_t *lp, grub_barg_t *barg); +int kernel(const grub_line_t *lp, grub_barg_t *barg); +int module(const grub_line_t *lp, grub_barg_t *barg); +int dollar_kernel(const grub_line_t *lp, grub_barg_t *barg); +int dollar_module(const grub_line_t *lp, grub_barg_t *barg); +int findroot(const grub_line_t *lp, grub_barg_t *barg); +int bootfs(const grub_line_t *lp, grub_barg_t *barg); +size_t clean_path(char *path); + + +/* GRUB entry functions */ +int grub_entry_construct_barg(grub_entry_t *ent); +const char *grub_entry_get_fstyp(const grub_entry_t *ent); +const char *grub_entry_get_kernel(const grub_entry_t *ent); +const char *grub_entry_get_module(const grub_entry_t *ent); +const grub_fsdesc_t *grub_entry_get_rootfs(const grub_entry_t *ent); +size_t grub_entry_get_cmdline(grub_entry_t *ent, char *cmdline, size_t size); + +/* + * GRUB menu parse/access funcions. + * + * Callers must call grub_menu_init() to to obtain a handle to the menu before + * calling any of the other functions, and call grub_menu_fini() to close. + * + * grub_menu_init: + * Reads and parses GRUB menu file into a grub_menu_t data structure. + * If grub_menu_path file path is NULL, will use 'currently active' + * GRUB menu file. + * grub_menu_fini: + * Frees all resources allocated by grub_menu_init(). + * + * grub_menu_get_entry: + * Returns a particular entry from the menu. + * grub_menu_next_entry: + * grub_menu_prev_entry: + * Returns next or previous entry in the menu. + * If current entry is NULL, return first or last entry. + * + * grub_menu_next_line: + * grub_menu_prev_line: + * Returns next/prev (to the current) line in the menu. + * If current line is NULL, returns first or last line. + * grub_menu_get_line: + * Returns the specified line in the menu (line counter starts from one). + */ +int grub_menu_init(const char *grub_menu_path, grub_menu_t **menup); +void grub_menu_fini(grub_menu_t *); +grub_entry_t *grub_menu_get_entry(const grub_menu_t *menu, int num); +grub_entry_t *grub_menu_next_entry(const grub_menu_t *menu, + const grub_entry_t *current); +grub_entry_t *grub_menu_prev_entry(const grub_menu_t *menu, + const grub_entry_t *current); +grub_line_t *grub_menu_next_line(const grub_menu_t *menu, + const grub_line_t *current); +grub_line_t *grub_menu_prev_line(const grub_menu_t *menu, + const grub_line_t *current); + +#ifdef __cplusplus +} +#endif + +#endif /* _GRBMIMPL_H */ diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_menu.c b/usr/src/lib/libgrubmgmt/common/libgrub_menu.c new file mode 100644 index 0000000000..913185eead --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrub_menu.c @@ -0,0 +1,532 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains functions for manipulating the GRUB menu. + */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> + +#include "libgrub_impl.h" + +static const grub_cmd_desc_t grub_cmd_descs[GRBM_CMD_NUM] = { +#define menu_cmd(cmd, num, flag, parsef) {cmd, num, flag}, +#include "libgrub_cmd.def" +}; + +static void +append_line(grub_menu_t *mp, grub_line_t *lp) +{ + if (mp->gm_start == NULL) { + mp->gm_start = lp; + } else { + mp->gm_end->gl_next = lp; + lp->gl_prev = mp->gm_end; + } + mp->gm_end = lp; + lp->gl_line_num = ++mp->gm_line_num; + lp->gl_entry_num = GRUB_ENTRY_DEFAULT; +} + +static void +process_line(grub_menu_t *mp) +{ + int n; + grub_line_t *lp; + + lp = mp->gm_end; + n = sizeof (grub_cmd_descs) / sizeof (grub_cmd_descs[0]); + + /* search through the table of known commands */ + while (n-- != 0 && strcmp(lp->gl_cmd, grub_cmd_descs[n].gcd_cmd) != 0) + ; + + /* unknown command */ + if (n < 0) + return; + + /* we found command, fill lp fields */ + lp->gl_flags = grub_cmd_descs[n].gcd_flags; + lp->gl_cmdtp = grub_cmd_descs[n].gcd_num; +} + + +static void +check_entry(grub_entry_t *ent) +{ + int i; + uint_t emask; + grub_line_t *lp; + const grub_line_t * const lend = ent->ge_end->gl_next; + + emask = 0; + for (i = 0, lp = ent->ge_start; lend != lp; lp = lp->gl_next, ++i) { + lp->gl_entry_num = ent->ge_entry_num; + if (lp->gl_flags == GRUB_LINE_INVALID || + lp->gl_flags == GRUB_LINE_GLOBAL) { + emask |= 1 << i; + lp->gl_flags = GRUB_LINE_INVALID; + } + } + + if ((ent->ge_emask = emask) == 0) + ent->ge_flags |= GRBM_VALID_FLAG; +} + +static int +add_entry(grub_menu_t *mp, grub_line_t *start, grub_line_t *end) +{ + grub_entry_t *ent; + + if ((ent = calloc(1, sizeof (*ent))) == NULL) + return (errno); + + ent->ge_start = start; + ent->ge_end = end; + + if (mp->gm_ent_end == NULL) { + mp->gm_ent_start = ent; + } else { + mp->gm_ent_end->ge_next = ent; + ent->ge_prev = mp->gm_ent_end; + } + mp->gm_ent_end = ent; + ent->ge_entry_num = mp->gm_entry_num++; + ent->ge_menu = mp; + return (0); +} + +static void +default_entry(grub_menu_t *mp) +{ + uint_t defent; + grub_line_t *lp; + grub_entry_t *ent; + + defent = 0; + lp = mp->gm_curdefault; + + if (lp != NULL && lp->gl_flags == GRUB_LINE_GLOBAL && + lp->gl_cmdtp == GRBM_DEFAULT_CMD) { + defent = strtoul(lp->gl_arg, NULL, 0); + if (defent >= mp->gm_entry_num) + defent = 0; + } + + for (ent = mp->gm_ent_start; ent != NULL && defent != ent->ge_entry_num; + ent = ent->ge_next) + ; + + mp->gm_ent_default = ent; +} + +static void +free_line(grub_line_t *lp) +{ + if (lp == NULL) + return; + + free(lp->gl_cmd); + free(lp->gl_sep); + free(lp->gl_arg); + free(lp->gl_line); + free(lp); +} + +static void +free_linelist(grub_line_t *line) +{ + grub_line_t *lp; + + if (line == NULL) + return; + + while (line) { + lp = line; + line = lp->gl_next; + free_line(lp); + } +} + +static void +free_entries(grub_menu_t *mp) +{ + grub_entry_t *ent, *tmp; + + if (mp == NULL) + return; + + for (ent = mp->gm_ent_start; (tmp = ent) != NULL; + ent = tmp->ge_next, free(tmp)) + ; + + mp->gm_ent_start = NULL; + mp->gm_ent_end = NULL; +} + +static int +grub_menu_append_line(grub_menu_t *mp, const char *line) +{ + int rc; + size_t n; + grub_line_t *lp; + + if (line == NULL) + return (EINVAL); + + rc = 0; + lp = NULL; + if ((lp = calloc(1, sizeof (*lp))) == NULL || + (lp->gl_line = strdup(line)) == NULL) { + free(lp); + return (errno); + } + + /* skip initial white space */ + line += strspn(line, " \t"); + + /* process comment line */ + if (line[0] == '#') { + if ((lp->gl_cmd = + strdup(grub_cmd_descs[GRBM_COMMENT_CMD].gcd_cmd)) == NULL || + (lp->gl_sep = + strdup(grub_cmd_descs[GRBM_EMPTY_CMD].gcd_cmd)) == NULL || + (lp->gl_arg = strdup(line + 1)) == NULL) + rc = errno; + } else { + /* get command */ + n = strcspn(line, " \t="); + if ((lp->gl_cmd = malloc(n + 1)) == NULL) + rc = errno; + else + (void) strlcpy(lp->gl_cmd, line, n + 1); + + line += n; + + /* get separator */ + n = strspn(line, " \t="); + if ((lp->gl_sep = malloc(n + 1)) == NULL) + rc = errno; + else + (void) strlcpy(lp->gl_sep, line, n + 1); + + line += n; + + /* get arguments */ + if ((lp->gl_arg = strdup(line)) == NULL) + rc = errno; + } + + if (rc != 0) { + free_line(lp); + return (rc); + } + + append_line(mp, lp); + process_line(mp); + return (0); +} + +static int +grub_menu_process(grub_menu_t *mp) +{ + int ret; + grub_entry_t *ent; + grub_line_t *line, *start; + + /* Free remaininig entries, if any */ + free_entries(mp); + + /* + * Walk through lines, till first 'title' command is encountered. + * Initialize globals. + */ + for (line = mp->gm_start; line != NULL; line = line->gl_next) { + if (line->gl_flags == GRUB_LINE_GLOBAL && + line->gl_cmdtp == GRBM_DEFAULT_CMD) + mp->gm_curdefault = line; + else if (line->gl_cmdtp == GRBM_TITLE_CMD) + break; + } + + /* + * Walk through remaining lines and recreate menu entries. + */ + for (start = NULL; line != NULL; line = line->gl_next) { + if (line->gl_cmdtp == GRBM_TITLE_CMD) { + /* is first entry */ + if (start != NULL && + (ret = add_entry(mp, start, line->gl_prev)) != 0) + return (ret); + start = line; + } + } + + /* Add last entry */ + if (start != NULL && (ret = add_entry(mp, start, mp->gm_end)) != 0) + return (ret); + + for (ent = mp->gm_ent_start; NULL != ent; ent = ent->ge_next) + check_entry(ent); + + default_entry(mp); + + return (0); +} + +static int +grub_fs_init(grub_fs_t *fs) +{ + assert(fs); + if ((fs->gf_lzfh = libzfs_init()) == NULL || + (fs->gf_diroot = di_init("/", DINFOCPYALL | DINFOPATH)) + == DI_NODE_NIL || + (fs->gf_dvlh = di_devlink_init(NULL, 0)) == DI_LINK_NIL) { + return (EG_INITFS); + } + return (0); +} + +static void +grub_fs_fini(grub_fs_t *fs) +{ + if (fs == NULL) + return; + + if (fs->gf_dvlh != DI_LINK_NIL) + (void) di_devlink_fini(&fs->gf_dvlh); + if (fs->gf_diroot != DI_NODE_NIL) + di_fini(fs->gf_diroot); + if (fs->gf_lzfh != NULL) + libzfs_fini(fs->gf_lzfh); + (void) memset(fs, 0, sizeof (*fs)); +} + +/* + * Reads and parses GRUB menu file into a grub_menu_t data structure. + * If grub_menu_path file path is NULL, will use 'currently active' + * GRUB menu file. + * + * Memory for the menu data structure is allocated within the routine. + * Caller must call grub_menu_fini() to release memory after calling + * grub_menu_init(). + */ +int +grub_menu_init(const char *path, grub_menu_t **menup) +{ + FILE *fp; + char *cp; + grub_menu_t *mp; + int len, n, ret; + char buf[GRBM_MAXLINE]; + + if (menup == NULL) + return (EINVAL); + + /* + * Allocate space, perform initialization + */ + if ((mp = calloc(1, sizeof (*mp))) == NULL) { + *menup = mp; + return (errno); + } + + if ((ret = grub_fs_init(&mp->gm_fs)) != 0 || + (ret = grub_current_root(&mp->gm_fs, &mp->gm_root)) != 0) + goto err_out1; + + if (path == NULL) { + /* + * Use default grub-menu. + * If top dataset is not mounted, mount it now. + */ + if (mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp[0] == 0) { + if ((ret = grub_fsd_mount_tmp(mp->gm_root.gr_fs + + GRBM_FS_TOP, mp->gm_root.gr_fstyp)) != 0) + goto err_out1; + } + (void) snprintf(mp->gm_path, sizeof (mp->gm_path), + "%s/%s", mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp, + GRUB_MENU); + } else { + (void) strlcpy(mp->gm_path, path, sizeof (mp->gm_path)); + } + + if ((fp = fopen(mp->gm_path, "r")) == NULL) { + ret = errno; + goto err_out1; + } + + cp = buf; + len = sizeof (buf); + + while (fgets(cp, len, fp) != NULL) { + + if (IS_LINE2BIG(cp, len, n)) { + ret = E2BIG; + break; + } + + /* remove white space at the end of line */ + for (; isspace(cp[n - 1]); --n) + ; + cp[n] = '\0'; + + if (cp[n - 1] == '\\') { + len -= n - 1; + assert(len >= 2); + cp += n - 1; + continue; + } + if ((ret = grub_menu_append_line(mp, buf)) != 0) + break; + + cp = buf; + len = sizeof (buf); + } + + if (fclose(fp) == EOF) + ret = errno; + else if (ret == 0) + ret = grub_menu_process(mp); + +err_out1: + grub_fsd_umount_tmp(mp->gm_root.gr_fs + GRBM_FS_TOP); + if (0 != ret) { + grub_menu_fini(mp); + mp = NULL; + } + *menup = mp; + return (ret); +} + +void +grub_menu_fini(grub_menu_t *mp) +{ + if (mp == NULL) + return; + + grub_fs_fini(&mp->gm_fs); + free_entries(mp); + free_linelist(mp->gm_start); + free(mp); +} + +grub_line_t * +grub_menu_next_line(const grub_menu_t *mp, const grub_line_t *lp) +{ + assert(mp); + if (lp == NULL) + return (mp->gm_start); + else + return (lp->gl_next); +} + +grub_line_t * +grub_menu_prev_line(const grub_menu_t *mp, const grub_line_t *lp) +{ + assert(mp); + if (lp == NULL) + return (mp->gm_end); + else + return (lp->gl_prev); +} + +grub_line_t * +grub_menu_get_line(const grub_menu_t *mp, int num) +{ + grub_line_t *lp; + + assert(mp); + if (num > mp->gm_line_num) + return (NULL); + for (lp = mp->gm_start; lp != NULL && num != lp->gl_line_num; + lp = lp->gl_next) + ; + return (lp); +} + +size_t +grub_menu_get_cmdline(const grub_menu_t *mp, int num, char *cmdl, size_t size) +{ + grub_entry_t *ent; + + assert(mp); + if ((ent = grub_menu_get_entry(mp, num)) == NULL) + return (size_t)(-1); + + return (grub_entry_get_cmdline(ent, cmdl, size)); +} + +grub_entry_t * +grub_menu_next_entry(const grub_menu_t *mp, const grub_entry_t *ent) +{ + assert(mp); + if (ent == NULL) { + return (mp->gm_ent_start); + } else { + assert(mp == ent->ge_menu); + return (ent->ge_next); + } +} + +grub_entry_t * +grub_menu_prev_entry(const grub_menu_t *mp, const grub_entry_t *ent) +{ + assert(mp); + if (ent == NULL) { + return (mp->gm_ent_end); + } else { + assert(mp == ent->ge_menu); + return (ent->ge_prev); + } +} + +grub_entry_t * +grub_menu_get_entry(const grub_menu_t *mp, int num) +{ + grub_entry_t *ent; + + assert(mp); + if (num == GRUB_ENTRY_DEFAULT) { + ent = mp->gm_ent_default; + } else if (num >= mp->gm_entry_num) { + ent = NULL; + } else { + for (ent = mp->gm_ent_start; + ent != NULL && num != ent->ge_entry_num; + ent = ent->ge_next) + ; + } + return (ent); +} diff --git a/usr/src/lib/libgrubmgmt/common/libgrubmgmt.h b/usr/src/lib/libgrubmgmt/common/libgrubmgmt.h new file mode 100644 index 0000000000..fb4d88768d --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/libgrubmgmt.h @@ -0,0 +1,98 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBGRUBMGMT_H +#define _LIBGRUBMGMT_H + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mntent.h> +#include <sys/uadmin.h> +#include <libzfs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRUB_ENTRY_DEFAULT -1 /* Use the default entry */ + +/* + * Data structure for describing the GRUB menu + */ +typedef struct grub_menu grub_menu_t; +typedef struct grub_line grub_line_t; +typedef struct grub_entry grub_entry_t; + +/* + * Data structure for describing the file system where the + * GRUB menu resides + */ +typedef struct grub_fsdesc { + int gfs_is_tmp_mounted; /* is temporary mounted */ + char gfs_dev[MAXNAMELEN]; /* device/zfs dataset to mount */ + char gfs_mountp[MAXPATHLEN]; /* mount point */ +} grub_fsdesc_t; + +/* + * Data structure for collecting data for Fast Reboot + */ +typedef struct grub_boot_args { + grub_fsdesc_t gba_fsd; + int gba_kernel_fd; + char gba_kernel[BOOTARGS_MAX]; + char gba_module[BOOTARGS_MAX]; + char gba_bootargs[BOOTARGS_MAX]; +} grub_boot_args_t; + +/* + * Wrapper functions for retriving boot arguments for Fast Reboot. + * grub_get_boot_args() calls grub_menu_init() and grub_menu_fini(). + * If menupath is NULL, it will use 'currently active' GRUB menu file. + * + * All _get_boot_args functions will mount the root file system for the + * given entry if not mounted, and open and validate the kernel file. + * Caller must allocate bargs, and call grub_cleanup_boot_args() to + * clean up mount points and open file handles when done. + * + * grub_get_boot_args: + * Collects boot argument from the specified GRUB menu entry. + * If entrynum == -1, default GRUB menu entry will be used. + * + * grub_cleanup_boot_args: + * Cleans up and releases all the resources allocated by + * grub_get_boot_args. Closes kernel file. Umounts root file + * system if temporarily mounted. + */ +extern int grub_get_boot_args(grub_boot_args_t *bargs, const char *menupath, + int entrynum); +extern void grub_cleanup_boot_args(grub_boot_args_t *bargs); + +extern const char *grub_strerror(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBGRUBMGMT_H */ diff --git a/usr/src/lib/libgrubmgmt/common/llib-lgrubmgmt b/usr/src/lib/libgrubmgmt/common/llib-lgrubmgmt new file mode 100644 index 0000000000..f3640f843b --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/llib-lgrubmgmt @@ -0,0 +1,29 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include "libgrubmgmt.h" diff --git a/usr/src/lib/libgrubmgmt/common/mapfile-vers b/usr/src/lib/libgrubmgmt/common/mapfile-vers new file mode 100644 index 0000000000..bc9afc111b --- /dev/null +++ b/usr/src/lib/libgrubmgmt/common/mapfile-vers @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +SUNWprivate_1.1 { + global: + grub_get_boot_args; + grub_cleanup_boot_args; + grub_strerror; + local: + *; +}; diff --git a/usr/src/lib/libgrubmgmt/i386/Makefile b/usr/src/lib/libgrubmgmt/i386/Makefile new file mode 100644 index 0000000000..0ef5233f44 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/i386/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libgrubmgmt/sparc/Makefile b/usr/src/lib/libgrubmgmt/sparc/Makefile new file mode 100644 index 0000000000..0ef5233f44 --- /dev/null +++ b/usr/src/lib/libgrubmgmt/sparc/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libscf/Makefile.com b/usr/src/lib/libscf/Makefile.com index b138032d66..95c522cb2a 100644 --- a/usr/src/lib/libscf/Makefile.com +++ b/usr/src/lib/libscf/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -30,6 +30,7 @@ OBJECTS = \ error.o \ lowlevel.o \ midlevel.o \ + highlevel.o \ scf_tmpl.o \ scf_type.o @@ -42,7 +43,9 @@ $(NOT_NATIVE)NATIVE_BUILD = $(POUND_SIGN) $(NATIVE_BUILD)VERS = $(NATIVE_BUILD)LIBS = $(DYNLIB) +LDLIBS_i386 += -lsmbios LDLIBS += -luutil -lc -lgen +LDLIBS += $(LDLIBS_$(MACH)) SRCDIR = ../common $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) @@ -50,7 +53,8 @@ $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) COMDIR = ../../../common/svc CFLAGS += $(CCVERBOSE) -Wp,-xc99=%all -CPPFLAGS += -I../inc -I../../common/inc -I$(COMDIR) +CPPFLAGS += -I../inc -I../../common/inc -I$(COMDIR) -I$(ROOTHDRDIR) +$(NOT_RELEASE_BUILD) CPPFLAGS += -DFASTREBOOT_DEBUG # # For native builds, we compile and link against the native version @@ -59,9 +63,11 @@ CPPFLAGS += -I../inc -I../../common/inc -I$(COMDIR) LIBUUTIL = $(SRC)/lib/libuutil MY_NATIVE_CPPFLAGS =\ -DNATIVE_BUILD $(DTEXTDOM) \ - -I../inc -I$(COMDIR) -I$(LIBUUTIL)/common -MY_NATIVE_LDLIBS = -L$(LIBUUTIL)/native -R$(LIBUUTIL)/native -luutil -ldoor -lc \ - -lgen + -I../inc -I$(COMDIR) -I$(LIBUUTIL)/common -I$(ROOTHDRDIR) +MY_NATIVE_LDLIBS = -L$(LIBUUTIL)/native -R$(LIBUUTIL)/native -luutil -ldoor \ + -lc -lgen +MY_NATIVE_LDLIBS_i386 = -lsmbios +MY_NATIVE_LDLIBS += $(MY_NATIVE_LDLIBS_$(MACH)) .KEEP_STATE: diff --git a/usr/src/lib/libscf/common/highlevel.c b/usr/src/lib/libscf/common/highlevel.c new file mode 100644 index 0000000000..6444d260c2 --- /dev/null +++ b/usr/src/lib/libscf/common/highlevel.c @@ -0,0 +1,193 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * This file contains high level functions used by multiple utilities. + */ + +#include "libscf_impl.h" + +#include <assert.h> +#include <libuutil.h> +#include <string.h> +#include <stdlib.h> +#include <sys/systeminfo.h> +#include <sys/uadmin.h> +#include <sys/utsname.h> + +#ifdef __x86 +#include <smbios.h> + +/* + * Check whether the platform is on the fastreboot_blacklist. + * Return 1 if the platform has been blacklisted, 0 otherwise. + */ +static int +scf_is_fb_blacklisted(void) +{ + smbios_hdl_t *shp; + smbios_system_t sys; + smbios_info_t info; + + id_t id; + int err; + int i; + + scf_simple_prop_t *prop = NULL; + ssize_t numvals; + char *platform_name; + + int blacklisted = 0; + + /* + * If there's no SMBIOS, assume it's blacklisted. + */ + if ((shp = smbios_open(NULL, SMB_VERSION, 0, &err)) == NULL) + return (1); + + /* + * If we can't read system info, assume it's blacklisted. + */ + if ((id = smbios_info_system(shp, &sys)) == SMB_ERR || + smbios_info_common(shp, id, &info) == SMB_ERR) { + blacklisted = 1; + goto fb_out; + } + + /* + * If we can't read the "platforms" property from property group + * BOOT_CONFIG_PG_FBBLACKLIST, assume no platforms have + * been blacklisted. + */ + if ((prop = scf_simple_prop_get(NULL, FMRI_BOOT_CONFIG, + BOOT_CONFIG_PG_FBBLACKLIST, "platforms")) == NULL) + goto fb_out; + + numvals = scf_simple_prop_numvalues(prop); + + for (i = 0; i < numvals; i++) { + platform_name = scf_simple_prop_next_astring(prop); + if (platform_name == NULL) + break; + if (strcmp(platform_name, info.smbi_product) == 0) { + blacklisted = 1; + break; + } + } + +fb_out: + smbios_close(shp); + scf_simple_prop_free(prop); + + return (blacklisted); +} +#endif /* __x86 */ + +/* + * Get config properties from svc:/system/boot-config:default. + * It prints errors with uu_warn(). + */ +void +scf_get_boot_config(uint8_t *boot_config) +{ + assert(boot_config); + *boot_config = 0; + +#ifndef __x86 + return; +#else + { + /* + * Property vector for BOOT_CONFIG_PG_PARAMS property group. + */ + scf_propvec_t ua_boot_config[] = { + { "fastreboot_default", NULL, SCF_TYPE_BOOLEAN, NULL, + UA_FASTREBOOT_DEFAULT }, + { FASTREBOOT_ONPANIC, NULL, SCF_TYPE_BOOLEAN, NULL, + UA_FASTREBOOT_ONPANIC }, + { NULL } + }; + scf_propvec_t *prop; + + for (prop = ua_boot_config; prop->pv_prop != NULL; prop++) + prop->pv_ptr = boot_config; + prop = NULL; + if (scf_read_propvec(FMRI_BOOT_CONFIG, BOOT_CONFIG_PG_PARAMS, + B_TRUE, ua_boot_config, &prop) != SCF_FAILED) { + /* + * Unset both flags if the platform has been + * blacklisted. + */ + if (scf_is_fb_blacklisted()) + *boot_config &= ~(UA_FASTREBOOT_DEFAULT | + UA_FASTREBOOT_ONPANIC); + return; + } +#if defined(FASTREBOOT_DEBUG) + if (prop != NULL) { + (void) uu_warn("Service %s property '%s/%s' " + "not found.\n", FMRI_BOOT_CONFIG, + BOOT_CONFIG_PG_PARAMS, prop->pv_prop); + } else { + (void) uu_warn("Unable to read service %s " + "property '%s': %s\n", FMRI_BOOT_CONFIG, + BOOT_CONFIG_PG_PARAMS, scf_strerror(scf_error())); + } +#endif /* FASTREBOOT_DEBUG */ + } +#endif /* __x86 */ +} + +/* + * Check whether Fast Reboot is the default operating mode. + * Return 0 if + * 1. the platform is xVM + * or + * 2. svc:/system/boot-config:default service doesn't exist, + * or + * 3. property "fastreboot_default" doesn't exist, + * or + * 4. value of property "fastreboot_default" is set to 0. + * or + * 5. the platform has been blacklisted. + * Return non-zero otherwise. + */ +int +scf_is_fastboot_default(void) +{ + uint8_t boot_config = 0; + char procbuf[SYS_NMLN]; + + /* + * If we are on xVM, do not fast reboot by default. + */ + if (sysinfo(SI_PLATFORM, procbuf, sizeof (procbuf)) == -1 || + strcmp(procbuf, "i86xpv") == 0) + return (0); + + scf_get_boot_config(&boot_config); + return (boot_config & UA_FASTREBOOT_DEFAULT); +} diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers index e7517768b1..7a88cb6f0f 100644 --- a/usr/src/lib/libscf/common/mapfile-vers +++ b/usr/src/lib/libscf/common/mapfile-vers @@ -313,6 +313,8 @@ SUNWprivate_1.1 { scf_write_propvec; scf_clean_propvec; scf_instance_delete_prop; + scf_get_boot_config; + scf_is_fastboot_default; local: *; }; diff --git a/usr/src/lib/libscf/inc/libscf_priv.h b/usr/src/lib/libscf/inc/libscf_priv.h index cfc965e084..f1df80093c 100644 --- a/usr/src/lib/libscf/inc/libscf_priv.h +++ b/usr/src/lib/libscf/inc/libscf_priv.h @@ -526,6 +526,12 @@ char *_scf_read_single_astring_from_pg(scf_propertygroup_t *, const char *); int scf_instance_delete_prop(scf_instance_t *, const char *, const char *); +/* + * Functions to extract boot config information from FMRI_BOOT_CONFIG + */ +void scf_get_boot_config(uint8_t *); +int scf_is_fastboot_default(void); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index ba702d2628..8c83d5d946 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -336,7 +336,8 @@ extern int zpool_stage_history(libzfs_handle_t *, const char *); extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *, size_t len); extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *); -extern int zpool_get_physpath(zpool_handle_t *, char *); +extern int zpool_get_physpath(zpool_handle_t *, char *, size_t); + /* * Basic handle manipulations. These functions do not create or destroy the * underlying datasets, only the references to them. diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 75ecc54e27..2996f834cc 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -1504,81 +1504,125 @@ vdev_online(nvlist_t *nv) } /* - * Get phys_path for a root pool - * Return 0 on success; non-zeron on failure. + * Helper function for zpool_get_config_physpath(). */ -int -zpool_get_physpath(zpool_handle_t *zhp, char *physpath) +static int +vdev_get_physpath(nvlist_t *config, char *physpath, size_t physpath_size, + size_t *bytes_written) +{ + size_t bytes_left, pos, rsz; + char *tmppath; + const char *format; + + if (nvlist_lookup_string(config, ZPOOL_CONFIG_PHYS_PATH, + &tmppath) != 0) + return (EZFS_NODEVICE); + + pos = *bytes_written; + bytes_left = physpath_size - pos; + format = (pos == 0) ? "%s" : " %s"; + + rsz = snprintf(physpath + pos, bytes_left, format, tmppath); + *bytes_written += rsz; + + if (rsz >= bytes_left) { + /* if physpath was not copied properly, clear it */ + if (bytes_left != 0) { + physpath[pos] = 0; + } + return (EZFS_NOSPC); + } + return (0); +} + +/* + * Get phys_path for a root pool config. + * Return 0 on success; non-zero on failure. + */ +static int +zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size) { + size_t rsz; nvlist_t *vdev_root; nvlist_t **child; + nvlist_t **child2; uint_t count; - int i; + char *type; + int j, ret; + + rsz = 0; + + if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &vdev_root) != 0) + return (EZFS_INVALCONFIG); + + if (nvlist_lookup_string(vdev_root, ZPOOL_CONFIG_TYPE, &type) != 0 || + nvlist_lookup_nvlist_array(vdev_root, ZPOOL_CONFIG_CHILDREN, + &child, &count) != 0) + return (EZFS_INVALCONFIG); /* - * Make sure this is a root pool, as phys_path doesn't mean - * anything to a non-root pool. + * root pool can not have EFI labeled disks and can only have + * a single top-level vdev. */ - if (!pool_is_bootable(zhp)) - return (-1); + if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1 || + pool_uses_efi(vdev_root)) + return (EZFS_POOL_INVALARG); - verify(nvlist_lookup_nvlist(zhp->zpool_config, - ZPOOL_CONFIG_VDEV_TREE, &vdev_root) == 0); + if (nvlist_lookup_string(child[0], ZPOOL_CONFIG_TYPE, &type) != 0) + return (EZFS_INVALCONFIG); - if (nvlist_lookup_nvlist_array(vdev_root, ZPOOL_CONFIG_CHILDREN, - &child, &count) != 0) - return (-2); + if (strcmp(type, VDEV_TYPE_DISK) == 0) { + if (vdev_online(child[0])) { + if ((ret = vdev_get_physpath(child[0], physpath, + phypath_size, &rsz)) != 0) + return (ret); + } + } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) { - for (i = 0; i < count; i++) { - nvlist_t **child2; - uint_t count2; - char *type; - char *tmppath; - int j; + if (nvlist_lookup_nvlist_array(child[0], + ZPOOL_CONFIG_CHILDREN, &child2, &count) != 0) + return (EZFS_INVALCONFIG); - if (nvlist_lookup_string(child[i], ZPOOL_CONFIG_TYPE, &type) - != 0) - return (-3); - - if (strcmp(type, VDEV_TYPE_DISK) == 0) { - if (!vdev_online(child[i])) - return (-8); - verify(nvlist_lookup_string(child[i], - ZPOOL_CONFIG_PHYS_PATH, &tmppath) == 0); - (void) strncpy(physpath, tmppath, strlen(tmppath)); - } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) { - if (nvlist_lookup_nvlist_array(child[i], - ZPOOL_CONFIG_CHILDREN, &child2, &count2) != 0) - return (-4); - - for (j = 0; j < count2; j++) { - if (!vdev_online(child2[j])) - return (-8); - if (nvlist_lookup_string(child2[j], - ZPOOL_CONFIG_PHYS_PATH, &tmppath) != 0) - return (-5); - - if ((strlen(physpath) + strlen(tmppath)) > - MAXNAMELEN) - return (-6); - - if (strlen(physpath) == 0) { - (void) strncpy(physpath, tmppath, - strlen(tmppath)); - } else { - (void) strcat(physpath, " "); - (void) strcat(physpath, tmppath); - } + for (j = 0; j < count; j++) { + if (nvlist_lookup_string(child2[j], ZPOOL_CONFIG_TYPE, + &type) != 0) + return (EZFS_INVALCONFIG); + + if (strcmp(type, VDEV_TYPE_DISK) != 0) + return (EZFS_POOL_INVALARG); + + if (vdev_online(child2[j])) { + ret = vdev_get_physpath(child2[j], + physpath, phypath_size, &rsz); + + if (ret == EZFS_NOSPC) + return (ret); } - } else { - return (-7); } + } else { + return (EZFS_POOL_INVALARG); } + /* No online devices */ + if (rsz == 0) + return (EZFS_NODEVICE); + return (0); } /* + * Get phys_path for a root pool + * Return 0 on success; non-zero on failure. + */ +int +zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size) +{ + return (zpool_get_config_physpath(zhp->zpool_config, physpath, + phypath_size)); +} + +/* * Returns TRUE if the given guid corresponds to the given type. * This is used to check for hot spares (INUSE or not), and level 2 cache * devices. diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile index b6558b1f63..b3c233545b 100644 --- a/usr/src/pkgdefs/Makefile +++ b/usr/src/pkgdefs/Makefile @@ -128,6 +128,7 @@ i386_SUBDIRS= \ SUNWdrmr \ SUNWgrub \ SUNWgrubS \ + SUNWgrubu \ SUNWdcopy \ SUNWipw \ SUNWiwh \ diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_i386 b/usr/src/pkgdefs/SUNWcsr/prototype_i386 index 8a4c090ff1..d6de6ce08a 100644 --- a/usr/src/pkgdefs/SUNWcsr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWcsr/prototype_i386 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -44,9 +44,11 @@ # # SUNWcsr # +f none lib/svc/method/svc-boot-config 0555 root bin f none lib/svc/method/svc-hostid 0555 root bin f none sbin/biosdev 555 root bin f none sbin/installgrub 0555 root sys d none var/ld/amd64 755 root bin s none var/ld/64=amd64 f manifest var/svc/manifest/system/hostid.xml 0444 root sys +f manifest var/svc/manifest/system/boot-config.xml 0444 root sys diff --git a/usr/src/pkgdefs/SUNWgrubu/Makefile b/usr/src/pkgdefs/SUNWgrubu/Makefile new file mode 100644 index 0000000000..ba37f5f806 --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +.KEEP_STATE: + +all: $(FILES) depend + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWgrubu/depend b/usr/src/pkgdefs/SUNWgrubu/depend new file mode 100644 index 0000000000..cd1e48a8db --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/depend @@ -0,0 +1,53 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# This package information file defines software dependencies associated +# with the pkg. You can define three types of pkg dependencies with this file: +# P indicates a prerequisite for installation +# I indicates an incompatible package +# R indicates a reverse dependency +# <pkg.abbr> see pkginfo(4), PKG parameter +# <name> see pkginfo(4), NAME parameter +# <version> see pkginfo(4), VERSION parameter +# <arch> see pkginfo(4), ARCH parameter +# <type> <pkg.abbr> <name> +# (<arch>)<version> +# (<arch>)<version> +# ... +# <type> <pkg.abbr> <name> +# ... +# + +P SUNWcar Core Architecture, (Root) +P SUNWcakr Core Solaris Kernel Architecture (Root) +P SUNWkvm Core Architecture, (Kvm) +P SUNWcsr Core Solaris, (Root) +P SUNWckr Core Solaris Kernel (Root) +P SUNWcsu Core Solaris, (Usr) +P SUNWcsd Core Solaris Devices +P SUNWcsl Core Solaris Libraries +P SUNWcslr Core Solaris Libraries (Root) +P SUNWzfsr ZFS (Root) +P SUNWzfsu ZFS (Usr) diff --git a/usr/src/pkgdefs/SUNWgrubu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWgrubu/pkginfo.tmpl new file mode 100644 index 0000000000..3f59c5bb1e --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/pkginfo.tmpl @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWgrubu" +NAME="GRUB (Usr)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="usr" +MAXINST="1000" +CATEGORY="system" +DESC="GRUB libraries" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWgrubu/prototype_com b/usr/src/pkgdefs/SUNWgrubu/prototype_com new file mode 100644 index 0000000000..0928f26cbd --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/prototype_com @@ -0,0 +1,39 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# packaging files +i copyright +i depend +i pkginfo + +# +# SUNWgrubu +# +d none lib 755 root bin +s none lib/libgrubmgmt.so=libgrubmgmt.so.1 +f none lib/libgrubmgmt.so.1 755 root bin +f none lib/llib-lgrubmgmt.ln 644 root bin +f none lib/llib-lgrubmgmt 644 root bin diff --git a/usr/src/pkgdefs/SUNWgrubu/prototype_i386 b/usr/src/pkgdefs/SUNWgrubu/prototype_i386 new file mode 100644 index 0000000000..88137b169e --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/prototype_i386 @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +!include prototype_com diff --git a/usr/src/pkgdefs/SUNWgrubu/prototype_sparc b/usr/src/pkgdefs/SUNWgrubu/prototype_sparc new file mode 100644 index 0000000000..88137b169e --- /dev/null +++ b/usr/src/pkgdefs/SUNWgrubu/prototype_sparc @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +!include prototype_com diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index ad2d51eb1b..45f2ea9c0c 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -304,6 +304,7 @@ f none usr/include/libelf.h 644 root bin f none usr/include/libfstyp.h 644 root bin f none usr/include/libfstyp_module.h 644 root bin f none usr/include/libgen.h 644 root bin +f none usr/include/libgrubmgmt.h 644 root bin f none usr/include/libintl.h 644 root bin f none usr/include/libipmi.h 644 root bin f none usr/include/libnvpair.h 644 root bin diff --git a/usr/src/uts/common/os/main.c b/usr/src/uts/common/os/main.c index e69e241129..7c5cd30915 100644 --- a/usr/src/uts/common/os/main.c +++ b/usr/src/uts/common/os/main.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -378,6 +378,7 @@ main(void) extern void lgrp_main_mp_init(void); #if defined(__x86) extern void cpupm_post_startup(void); + extern void fastboot_post_startup(void); #endif /* * In the horrible world of x86 in-lines, you can't get symbolic @@ -574,6 +575,7 @@ main(void) pm_cfb_setup_intr(); #if defined(__x86) cpupm_post_startup(); + fastboot_post_startup(); #endif /* diff --git a/usr/src/uts/common/os/mutex.c b/usr/src/uts/common/os/mutex.c index 9a2559b09e..0bdb5fdfe7 100644 --- a/usr/src/uts/common/os/mutex.c +++ b/usr/src/uts/common/os/mutex.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -532,12 +532,7 @@ mutex_owned(const kmutex_t *mp) { const mutex_impl_t *lp = (const mutex_impl_t *)mp; -#ifdef __x86 - extern int quiesce_active; if (panicstr || quiesce_active) -#else - if (panicstr) -#endif /* __x86 */ return (1); if (MUTEX_TYPE_ADAPTIVE(lp)) diff --git a/usr/src/uts/common/os/panic.c b/usr/src/uts/common/os/panic.c index c35f9528d7..26d4b98e82 100644 --- a/usr/src/uts/common/os/panic.c +++ b/usr/src/uts/common/os/panic.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * When the operating system detects that it is in an invalid state, a panic * is initiated in order to minimize potential damage to user data and to @@ -197,6 +195,11 @@ int panic_quiesce; /* trigger for CALM -> QUIESCE */ int panic_sync; /* trigger for QUIESCE -> SYNC */ int panic_dump; /* trigger for SYNC -> DUMP */ +/* + * Variable signifying quiesce(9E) is in progress. + */ +volatile int quiesce_active = 0; + void panicsys(const char *format, va_list alist, struct regs *rp, int on_panic_stack) { diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c index 6135b02e52..6d5f7a9696 100644 --- a/usr/src/uts/common/os/zone.c +++ b/usr/src/uts/common/os/zone.c @@ -5761,6 +5761,7 @@ zone_kadmin(int cmd, int fcn, const char *mdep, cred_t *credp) case A_REMOUNT: case A_FREEZE: case A_DUMP: + case A_CONFIG: return (ENOTSUP); default: ASSERT(cmd != A_SWAPCTL); /* handled by uadmin() */ diff --git a/usr/src/uts/common/sys/mutex.h b/usr/src/uts/common/sys/mutex.h index b271efced3..5c11b4afe0 100644 --- a/usr/src/uts/common/sys/mutex.h +++ b/usr/src/uts/common/sys/mutex.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_MUTEX_H #define _SYS_MUTEX_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _ASM #include <sys/types.h> #endif @@ -74,7 +72,7 @@ typedef struct mutex { #ifdef _KERNEL #define MUTEX_HELD(x) (mutex_owned(x)) -#define MUTEX_NOT_HELD(x) (!mutex_owned(x) || panicstr) +#define MUTEX_NOT_HELD(x) (!mutex_owned(x) || panicstr || quiesce_active) extern void mutex_init(kmutex_t *, char *, kmutex_type_t, void *); extern void mutex_destroy(kmutex_t *); diff --git a/usr/src/uts/common/sys/systm.h b/usr/src/uts/common/sys/systm.h index 017555524b..c7421047cf 100644 --- a/usr/src/uts/common/sys/systm.h +++ b/usr/src/uts/common/sys/systm.h @@ -23,7 +23,7 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -93,6 +93,7 @@ extern boolean_t root_is_ramdisk; /* root is boot_archive ramdisk */ extern uint32_t ramdisk_size; /* (KB) set only for sparc netboots */ extern char *volatile panicstr; /* panic string pointer */ extern va_list panicargs; /* panic arguments */ +extern volatile int quiesce_active; /* quiesce(9E) is in progress */ extern int rstchown; /* 1 ==> restrictive chown(2) semantics */ extern int klustsize; diff --git a/usr/src/uts/common/sys/uadmin.h b/usr/src/uts/common/sys/uadmin.h index 05fbcf6f33..8fad240cf3 100644 --- a/usr/src/uts/common/sys/uadmin.h +++ b/usr/src/uts/common/sys/uadmin.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -49,6 +49,7 @@ extern "C" { #define A_SWAPCTL 16 /* 17-21 reserved for obsolete interface */ #define A_SDTTEST 22 /* DTrace sdt:::test */ +#define A_CONFIG 23 /* For system configuration */ #define AD_UNKNOWN -1 /* no method */ #define AD_HALT 0 /* halt the processor */ @@ -61,7 +62,6 @@ extern "C" { #define AD_FASTREBOOT 8 /* bypass firmware and boot loader */ #define AD_FASTREBOOT_DRYRUN 9 /* Fast reboot Dry run */ - /* * Functions reserved for A_FREEZE (may not be available on all platforms) * Note: AD_COMPRESS, AD_CHECK and AD_FORCE are now obsolete @@ -94,12 +94,55 @@ extern "C" { #define AD_FTRACE_STOP 2 /* + * Functions of A_CONFIG. Unstable interface. + */ +#define AD_UPDATE_BOOT_CONFIG 1 /* Update boot config variables */ + +/* * When 'mdep' (the second argument to uadmin(2)) is initialized for A_REBOOT, * A_SHUTDOWN or A_DUMP, it represents the boot arguments string of at most * 256 characters. */ #define BOOTARGS_MAX 256 +#if !defined(_KERNEL) +/* + * FMRI for boot-config service. + */ +#define FMRI_BOOT_CONFIG \ + "svc:/system/boot-config:default" + +/* + * Property group that contains all Fast Reboot configuration properties. + */ +#define BOOT_CONFIG_PG_PARAMS "config" + +/* + * Property group that contains all Fast Reboot blacklisting information. + */ +#define BOOT_CONFIG_PG_FBBLACKLIST "fastreboot_blacklist" + +#endif /* _KERNEL */ + +/* + * Flag representations of fastboot configuration. + */ +#define UA_FASTREBOOT_DEFAULT 0x01 +#define UA_FASTREBOOT_ONPANIC 0x02 + +#define FASTREBOOT_ONPANIC "fastreboot_onpanic" +#define FASTREBOOT_ONPANIC_CMDLINE "fastreboot_onpanic_cmdline" + +#define FASTREBOOT_ONPANIC_NOTSET(p) \ + (strcmp((p), "false") == 0 || \ + strcmp((p), "no") == 0 || \ + strcmp((p), "0") == 0) + +#define FASTREBOOT_ONPANIC_ISSET(p) \ + (strcmp((p), "true") == 0 || \ + strcmp((p), "yes") == 0 || \ + strcmp((p), "1") == 0) + #if !defined(_ASM) #if defined(_KERNEL) diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c index a93ac589ad..c86a64724d 100644 --- a/usr/src/uts/common/syscall/uadmin.c +++ b/usr/src/uts/common/syscall/uadmin.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -149,6 +149,7 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) case A_FREEZE: case A_DUMP: case A_SDTTEST: + case A_CONFIG: if (secpolicy_sys_config(credp, B_FALSE) != 0) return (EPERM); break; @@ -164,10 +165,11 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) * functioning of A_REBOOT relies on being able to interrupt blocked * userland callers. * - * We only clear ua_shutdown_thread after A_REMOUNT, because A_SHUTDOWN - * and A_REBOOT should never return. + * We only clear ua_shutdown_thread after A_REMOUNT or A_CONFIG. + * Other commands should never return. */ - if (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_REMOUNT) { + if (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_REMOUNT || + cmd == A_CONFIG) { mutex_enter(&ualock); while (ua_shutdown_thread != NULL) { if (cv_wait_sig(&uacond, &ualock) == 0) { @@ -300,6 +302,28 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) /* no return expected */ break; + case A_CONFIG: + switch (fcn) { + case AD_UPDATE_BOOT_CONFIG: +#ifndef __sparc + { + extern int fastreboot_capable; + extern void fastboot_update_config(const char *); + + if (fastreboot_capable) + fastboot_update_config(mdep); + } +#endif + + break; + } + /* Let other threads enter the shutdown path now */ + mutex_enter(&ualock); + ua_shutdown_thread = NULL; + cv_signal(&uacond); + mutex_exit(&ualock); + break; + case A_REMOUNT: (void) VFS_MOUNTROOT(rootvfs, ROOT_REMOUNT); /* Let other threads enter the shutdown path now */ @@ -354,6 +378,25 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) } else panic_bootstr = mdep; +#ifndef __sparc + extern int fastreboot_onpanic; + if (fcn != AD_FASTREBOOT) { + extern void fastboot_update_config(const char *); + /* + * If user has explicitly requested reboot to prom, + * or uadmin(1M) was invoked with other functions, + * don't try to fast reboot after dumping. + */ + fastreboot_onpanic = 0; + fastboot_update_config((char *)&fastreboot_onpanic); + } + + if (fastreboot_onpanic) { + extern void fastboot_load_kernel(char *); + fastboot_load_kernel(mdep); + } +#endif + panic("forced crash dump initiated at user request"); /*NOTREACHED*/ } @@ -410,7 +453,7 @@ uadmin(int cmd, int fcn, uintptr_t mdep) */ if (mdep != NULL && (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_DUMP || - cmd == A_FREEZE)) { + cmd == A_FREEZE || cmd == A_CONFIG)) { bootargs = kmem_zalloc(BOOTARGS_MAX, KM_SLEEP); if ((error = copyinstr((const char *)mdep, bootargs, BOOTARGS_MAX, &nbytes)) != 0) { diff --git a/usr/src/uts/i86pc/os/fakebop.c b/usr/src/uts/i86pc/os/fakebop.c index 8e38adaf18..dd42ce5b1e 100644 --- a/usr/src/uts/i86pc/os/fakebop.c +++ b/usr/src/uts/i86pc/os/fakebop.c @@ -123,14 +123,13 @@ static void build_firmware_properties(void); static int early_allocation = 1; -#ifdef __xpv -int fastreboot_capable = 0; int force_fastreboot = 0; +int fastreboot_onpanic = 0; int post_fastreboot = 0; +#ifdef __xpv +int fastreboot_capable = 0; #else int fastreboot_capable = 1; -int force_fastreboot = 0; -int post_fastreboot = 0; #endif /* @@ -143,6 +142,13 @@ mb_memory_map_t saved_mmap[FASTBOOT_SAVED_MMAP_COUNT]; struct sol_netinfo saved_drives[FASTBOOT_SAVED_DRIVES_COUNT]; char saved_cmdline[FASTBOOT_SAVED_CMDLINE_LEN]; int saved_cmdline_len = 0; +size_t saved_file_size[FASTBOOT_MAX_FILES_MAP]; + +/* + * Turn off fastreboot_onpanic to avoid panic loop. + */ +char fastreboot_onpanic_cmdline[FASTBOOT_SAVED_CMDLINE_LEN]; +static const char fastreboot_onpanic_args[] = " -B fastreboot_onpanic=0"; /* * Pointers to where System Resource Affinity Table (SRAT) and @@ -1019,6 +1025,108 @@ setup_rarp_props(struct sol_netinfo *sip) #endif /* __xpv */ +static void +build_panic_cmdline(const char *cmd, int cmdlen) +{ + int proplen; + size_t arglen; + + arglen = sizeof (fastreboot_onpanic_args); + /* + * If we allready have fastreboot-onpanic set to zero, + * don't add them again. + */ + if ((proplen = do_bsys_getproplen(NULL, FASTREBOOT_ONPANIC)) > 0 && + proplen <= sizeof (fastreboot_onpanic_cmdline)) { + (void) do_bsys_getprop(NULL, FASTREBOOT_ONPANIC, + fastreboot_onpanic_cmdline); + if (FASTREBOOT_ONPANIC_NOTSET(fastreboot_onpanic_cmdline)) + arglen = 1; + } + + /* + * construct fastreboot_onpanic_cmdline + */ + if (cmdlen + arglen > sizeof (fastreboot_onpanic_cmdline)) { + DBG_MSG("Command line too long: clearing " + FASTREBOOT_ONPANIC "\n"); + fastreboot_onpanic = 0; + } else { + bcopy(cmd, fastreboot_onpanic_cmdline, cmdlen); + if (arglen != 1) + bcopy(fastreboot_onpanic_args, + fastreboot_onpanic_cmdline + cmdlen, arglen); + else + fastreboot_onpanic_cmdline[cmdlen] = 0; + } +} + + +#ifndef __xpv +/* + * Construct boot command line for Fast Reboot + */ +static void +build_fastboot_cmdline(void) +{ + saved_cmdline_len = strlen(xbootp->bi_cmdline) + 1; + if (saved_cmdline_len > FASTBOOT_SAVED_CMDLINE_LEN) { + DBG(saved_cmdline_len); + DBG_MSG("Command line too long: clearing fastreboot_capable\n"); + fastreboot_capable = 0; + } else { + bcopy((void *)(xbootp->bi_cmdline), (void *)saved_cmdline, + saved_cmdline_len); + saved_cmdline[saved_cmdline_len - 1] = '\0'; + build_panic_cmdline(saved_cmdline, saved_cmdline_len - 1); + } +} + +/* + * Save memory layout, disk drive information, unix and boot archive sizes for + * Fast Reboot. + */ +static void +save_boot_info(multiboot_info_t *mbi) +{ + mb_module_t *mbp; + int i; + + bcopy(mbi, &saved_mbi, sizeof (multiboot_info_t)); + if (mbi->mmap_length > sizeof (saved_mmap)) { + DBG_MSG("mbi->mmap_length too big: clearing " + "fastreboot_capable\n"); + fastreboot_capable = 0; + } else { + bcopy((void *)(uintptr_t)mbi->mmap_addr, (void *)saved_mmap, + mbi->mmap_length); + } + + if (mbi->drives_length > sizeof (saved_drives)) { + DBG(mbi->drives_length); + DBG_MSG("mbi->drives_length too big: clearing " + "fastreboot_capable\n"); + fastreboot_capable = 0; + } else { + bcopy((void *)(uintptr_t)mbi->drives_addr, (void *)saved_drives, + mbi->drives_length); + } + + /* + * Current file sizes. Used by fastboot.c to figure out how much + * memory to reserve for panic reboot. + */ + saved_file_size[FASTBOOT_NAME_UNIX] = FOUR_MEG - PAGESIZE; + for (i = 0, mbp = (mb_module_t *)(uintptr_t)mbi->mods_addr; + i < mbi->mods_count; i++, mbp += sizeof (mb_module_t)) { + saved_file_size[FASTBOOT_NAME_BOOTARCHIVE] += + mbp->mod_end - mbp->mod_start; + } + +} +#endif /* __xpv */ + + /* * 1st pass at building the table of boot properties. This includes: * - values set on the command line: -B a=x,b=y,c=z .... @@ -1074,20 +1182,6 @@ build_boot_properties(void) boot_args[0] = 0; boot_arg_len = 0; -#ifndef __xpv - saved_cmdline_len = strlen(xbootp->bi_cmdline) + 1; - if (saved_cmdline_len > FASTBOOT_SAVED_CMDLINE_LEN) { - DBG(saved_cmdline_len); - DBG_MSG("Command line too long: clearing fastreboot_capable\n"); - fastreboot_capable = 0; - } else { - bcopy((void *)(xbootp->bi_cmdline), (void *)saved_cmdline, - saved_cmdline_len); - saved_cmdline[saved_cmdline_len - 1] = '\0'; - } -#endif - - #ifdef __xpv /* * Xen puts a lot of device information in front of the kernel name @@ -1276,25 +1370,16 @@ build_boot_properties(void) */ netboot = 0; mbi = xbootp->bi_mb_info; - bcopy(mbi, &saved_mbi, sizeof (multiboot_info_t)); - if (mbi->mmap_length > sizeof (saved_mmap)) { - DBG_MSG("mbi->mmap_length too big: clearing " - "fastreboot_capable\n"); - fastreboot_capable = 0; - } else { - bcopy((void *)(uintptr_t)mbi->mmap_addr, (void *)saved_mmap, - mbi->mmap_length); - } - if (mbi->drives_length > sizeof (saved_drives)) { - DBG(mbi->drives_length); - DBG_MSG("mbi->drives_length too big: clearing " - "fastreboot_capable\n"); - fastreboot_capable = 0; - } else { - bcopy((void *)(uintptr_t)mbi->drives_addr, (void *)saved_drives, - mbi->drives_length); - } + /* + * Build boot command line for Fast Reboot + */ + build_fastboot_cmdline(); + + /* + * Save various boot information for Fast Reboot + */ + save_boot_info(mbi); if (mbi != NULL && mbi->flags & 0x2) { boot_device = mbi->boot_device >> 24; diff --git a/usr/src/uts/i86pc/os/fastboot.c b/usr/src/uts/i86pc/os/fastboot.c index 6595abea32..b383566dae 100644 --- a/usr/src/uts/i86pc/os/fastboot.c +++ b/usr/src/uts/i86pc/os/fastboot.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,8 +29,8 @@ * reboot which bypasses the firmware and bootloader, considerably * reducing downtime. * - * load_kernel(): This function is invoked by mdpreboot() in the reboot - * path. It loads the new kernel and boot archive into memory, builds + * fastboot_load_kernel(): This function is invoked by mdpreboot() in the + * reboot path. It loads the new kernel and boot archive into memory, builds * the data structure containing sufficient information about the new * kernel and boot archive to be passed to the fast reboot switcher * (see fb_swtch_src.s for details). When invoked the switcher relocates @@ -38,6 +38,10 @@ * similar to where the boot loader would have loaded them, and jumps to * the new kernel. * + * If fastreboot_onpanic is enabled, fastboot_load_kernel() is called + * by fastreboot_post_startup() to load the back up kernel in case of + * panic. + * * The physical addresses of the memory allocated for the new kernel, boot * archive and their page tables must be above where the boot archive ends * after it has been relocated by the switcher, otherwise the new files @@ -45,7 +49,8 @@ * * fast_reboot(): This function is invoked by mdboot() once it's determined * that the system is capable of fast reboot. It jumps to the fast reboot - * switcher with the data structure built by load_kernel() as the argument. + * switcher with the data structure built by fastboot_load_kernel() as the + * argument. */ #include <sys/types.h> @@ -83,17 +88,28 @@ #include <sys/machsystm.h> #include <sys/mman.h> #include <sys/x86_archext.h> +#include <sys/smp_impldefs.h> +#include <sys/spl.h> #include <sys/fastboot.h> #include <sys/machelf.h> #include <sys/kobj.h> #include <sys/multiboot.h> +#include <sys/kobj_lex.h> + +/* + * Macro to determine how many pages are needed for PTEs to map a particular + * file. Allocate one extra page table entry for terminating the list. + */ +#define FASTBOOT_PTE_LIST_SIZE(fsize) \ + P2ROUNDUP((((fsize) >> PAGESHIFT) + 1) * sizeof (x86pte_t), PAGESIZE) /* * Data structure containing necessary information for the fast reboot * switcher to jump to the new kernel. */ fastboot_info_t newkernel = { 0 }; +char fastboot_args[OBP_MAXPATHLEN]; static char fastboot_filename[2][OBP_MAXPATHLEN] = { { 0 }, { 0 }}; static x86pte_t ptp_bits = PT_VALID | PT_REF | PT_USER | PT_WRITABLE; @@ -110,8 +126,28 @@ int fastboot_contig = 0; static uintptr_t fake_va = FASTBOOT_FAKE_VA; /* - * Below 1G for page tables as we are using 2G as the fake virtual address for - * the new kernel and boot archive. + * Reserve memory below PA 1G in preparation of fast reboot. + * + * This variable is only checked when fastreboot_capable is set, but + * fastreboot_onpanic is not set. The amount of memory reserved + * is negligible, but just in case we are really short of low memory, + * this variable will give us a backdoor to not consume memory at all. + */ +int reserve_mem_enabled = 1; + +/* + * Amount of memory below PA 1G to reserve for constructing the multiboot + * data structure and the page tables as we tend to run out of those + * when more drivers are loaded. + */ +static size_t fastboot_mbi_size = 0x2000; /* 8K */ +static size_t fastboot_pagetable_size = 0x5000; /* 20K */ + +/* + * Use below 1G for page tables as + * 1. we are only doing 1:1 mapping of the bottom 1G of physical memory. + * 2. we are using 2G as the fake virtual address for the new kernel and + * boot archive. */ static ddi_dma_attr_t fastboot_below_1G_dma_attr = { DMA_ATTR_V0, @@ -156,6 +192,7 @@ extern mb_memory_map_t saved_mmap[FASTBOOT_SAVED_MMAP_COUNT]; extern struct sol_netinfo saved_drives[FASTBOOT_SAVED_DRIVES_COUNT]; extern char saved_cmdline[FASTBOOT_SAVED_CMDLINE_LEN]; extern int saved_cmdline_len; +extern size_t saved_file_size[]; extern void* contig_alloc(size_t size, ddi_dma_attr_t *attr, uintptr_t align, int cansleep); @@ -171,14 +208,17 @@ extern void vprintf(const char *, va_list); */ #define BOOTARCHIVE64 "/platform/i86pc/amd64/boot_archive" #define BOOTARCHIVE32 "/platform/i86pc/boot_archive" -#define BOOTARCHIVE_FAILSAFE "/boot/x86.miniroot-safe" -#define FAILSAFE_BOOTFILE "/boot/platform/i86pc/kernel/unix" +#define BOOTARCHIVE32_FAILSAFE "/boot/x86.miniroot-safe" +#define BOOTARCHIVE64_FAILSAFE "/boot/amd64/x86.miniroot-safe" +#define FAILSAFE_BOOTFILE32 "/boot/platform/i86pc/kernel/unix" +#define FAILSAFE_BOOTFILE64 "/boot/platform/i86pc/kernel/amd64/unix" static uint_t fastboot_vatoindex(fastboot_info_t *, uintptr_t, int); static void fastboot_map_with_size(fastboot_info_t *, uintptr_t, paddr_t, size_t, int); static void fastboot_build_pagetables(fastboot_info_t *); static int fastboot_build_mbi(char *, fastboot_info_t *); +static void fastboot_free_file(fastboot_file_t *); static const char fastboot_enomem_msg[] = "Fastboot: Couldn't allocate 0x%" PRIx64" bytes below %s to do fast reboot"; @@ -395,10 +435,9 @@ fastboot_build_mbi(char *mdep, fastboot_info_t *nk) mb_module_t *mbp; uintptr_t next_addr; uintptr_t new_mbi_pa; - size_t size; - void *buf = NULL; size_t arglen; char bootargs[OBP_MAXPATHLEN]; + size_t size; bzero(bootargs, OBP_MAXPATHLEN); @@ -409,18 +448,50 @@ fastboot_build_mbi(char *mdep, fastboot_info_t *nk) } size = PAGESIZE + P2ROUNDUP(arglen, PAGESIZE); - buf = contig_alloc(size, &fastboot_below_1G_dma_attr, PAGESIZE, 0); - if (buf == NULL) { - cmn_err(CE_WARN, fastboot_enomem_msg, (uint64_t)size, "1G"); - return (-1); + if (nk->fi_mbi_size && nk->fi_mbi_size < size) { + contig_free((void *)nk->fi_new_mbi_va, nk->fi_mbi_size); + nk->fi_mbi_size = 0; } - bzero(buf, size); + if (nk->fi_mbi_size == 0) { + if ((nk->fi_new_mbi_va = + (uintptr_t)contig_alloc(size, &fastboot_below_1G_dma_attr, + PAGESIZE, 0)) == NULL) { + cmn_err(CE_WARN, fastboot_enomem_msg, + (uint64_t)size, "1G"); + return (-1); + } + /* + * fi_mbi_size must be set after the allocation succeeds + * as it's used to determine how much memory to free. + */ + nk->fi_mbi_size = size; + } - new_mbi_pa = mmu_ptob((uint64_t)hat_getpfnum(kas.a_hat, (caddr_t)buf)); + bzero((void *)nk->fi_new_mbi_va, nk->fi_mbi_size); - hat_devload(kas.a_hat, (caddr_t)new_mbi_pa, size, - mmu_btop(new_mbi_pa), PROT_READ | PROT_WRITE, HAT_LOAD_NOCONSIST); + new_mbi_pa = mmu_ptob((uint64_t)hat_getpfnum(kas.a_hat, + (caddr_t)nk->fi_new_mbi_va)); + + /* + * Map the address into both the current proc's address + * space and the kernel's address space in case the panic + * is forced by kmdb. + */ + AS_LOCK_ENTER(&kas, &kas.a_lock, RW_WRITER); + hat_devload(kas.a_hat, (caddr_t)new_mbi_pa, nk->fi_mbi_size, + mmu_btop(new_mbi_pa), PROT_READ | PROT_WRITE, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); + AS_LOCK_EXIT(&kas, &kas.a_lock); + + if (&kas != curproc->p_as) { + struct as *asp = curproc->p_as; + AS_LOCK_ENTER(asp, &asp->a_lock, RW_WRITER); + hat_devload(asp->a_hat, (caddr_t)new_mbi_pa, nk->fi_mbi_size, + mmu_btop(new_mbi_pa), PROT_READ | PROT_WRITE, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); + AS_LOCK_EXIT(asp, &asp->a_lock); + } nk->fi_new_mbi_pa = (paddr_t)new_mbi_pa; @@ -429,8 +500,8 @@ fastboot_build_mbi(char *mdep, fastboot_info_t *nk) next_addr = new_mbi_pa + sizeof (multiboot_info_t); ((multiboot_info_t *)new_mbi_pa)->mods_addr = next_addr; mbp = (mb_module_t *)(uintptr_t)next_addr; - mbp->mod_start = newkernel.fi_files[FASTBOOT_BOOTARCHIVE].fb_dest_pa; - mbp->mod_end = newkernel.fi_files[FASTBOOT_BOOTARCHIVE].fb_next_pa; + mbp->mod_start = nk->fi_files[FASTBOOT_BOOTARCHIVE].fb_dest_pa; + mbp->mod_end = nk->fi_files[FASTBOOT_BOOTARCHIVE].fb_next_pa; next_addr += sizeof (mb_module_t); bcopy(fastboot_filename[FASTBOOT_NAME_BOOTARCHIVE], (void *)next_addr, @@ -542,20 +613,202 @@ fastboot_parse_mdep(char *mdep, char *kern_bootpath, int *bootpath_len, } /* - * Free up the memory we have allocated for this file + * Reserve memory under PA 1G for mapping the new kernel and boot archive. + * This function is only called if fastreboot_onpanic is *not* set. + */ +static void +fastboot_reserve_mem(fastboot_info_t *nk) +{ + int i; + + /* + * A valid kernel is in place. No need to reserve any memory. + */ + if (nk->fi_valid) + return; + + /* + * Reserve memory under PA 1G for PTE lists. + */ + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + fastboot_file_t *fb = &nk->fi_files[i]; + size_t fsize_roundup, size; + + fsize_roundup = P2ROUNDUP_TYPED(saved_file_size[i], + PAGESIZE, size_t); + size = FASTBOOT_PTE_LIST_SIZE(fsize_roundup); + if ((fb->fb_pte_list_va = contig_alloc(size, + &fastboot_below_1G_dma_attr, PAGESIZE, 0)) == NULL) { + return; + } + fb->fb_pte_list_size = size; + } + + /* + * Reserve memory under PA 1G for page tables. + */ + if ((nk->fi_pagetable_va = + (uintptr_t)contig_alloc(fastboot_pagetable_size, + &fastboot_below_1G_dma_attr, PAGESIZE, 0)) == NULL) { + return; + } + nk->fi_pagetable_size = fastboot_pagetable_size; + + /* + * Reserve memory under PA 1G for multiboot structure. + */ + if ((nk->fi_new_mbi_va = (uintptr_t)contig_alloc(fastboot_mbi_size, + &fastboot_below_1G_dma_attr, PAGESIZE, 0)) == NULL) { + return; + } + nk->fi_mbi_size = fastboot_mbi_size; +} + +/* + * Calculate MD5 digest for the given fastboot_file. + * Assumes that the file is allready loaded properly. + */ +static void +fastboot_cksum_file(fastboot_file_t *fb, uchar_t *md5_hash) +{ + MD5_CTX md5_ctx; + + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, (void *)fb->fb_va, fb->fb_size); + MD5Final(md5_hash, &md5_ctx); +} + +/* + * Free up the memory we have allocated for a file */ static void fastboot_free_file(fastboot_file_t *fb) { - size_t fsize_roundup, pt_size; - int pt_entry_count; + size_t fsize_roundup; fsize_roundup = P2ROUNDUP_TYPED(fb->fb_size, PAGESIZE, size_t); - contig_free((void *)fb->fb_va, fsize_roundup); + if (fsize_roundup) { + contig_free((void *)fb->fb_va, fsize_roundup); + fb->fb_va = NULL; + fb->fb_size = 0; + } +} + +/* + * Free up memory used by the PTEs for a file. + */ +static void +fastboot_free_file_pte(fastboot_file_t *fb, uint64_t endaddr) +{ + if (fb->fb_pte_list_size && fb->fb_pte_list_pa < endaddr) { + contig_free((void *)fb->fb_pte_list_va, fb->fb_pte_list_size); + fb->fb_pte_list_va = 0; + fb->fb_pte_list_pa = 0; + fb->fb_pte_list_size = 0; + } +} + +/* + * Free up all the memory used for representing a kernel with + * fastboot_info_t. + */ +static void +fastboot_free_mem(fastboot_info_t *nk, uint64_t endaddr) +{ + int i; + + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + fastboot_free_file(nk->fi_files + i); + fastboot_free_file_pte(nk->fi_files + i, endaddr); + } + + if (nk->fi_pagetable_size && nk->fi_pagetable_pa < endaddr) { + contig_free((void *)nk->fi_pagetable_va, nk->fi_pagetable_size); + nk->fi_pagetable_va = 0; + nk->fi_pagetable_pa = 0; + nk->fi_pagetable_size = 0; + } + + if (nk->fi_mbi_size && nk->fi_new_mbi_pa < endaddr) { + contig_free((void *)nk->fi_new_mbi_va, nk->fi_mbi_size); + nk->fi_new_mbi_va = 0; + nk->fi_new_mbi_pa = 0; + nk->fi_mbi_size = 0; + } +} + +/* + * Only free up the memory allocated for the kernel and boot archive, + * but not for the page tables. + */ +void +fastboot_free_newkernel(fastboot_info_t *nk) +{ + int i; + + nk->fi_valid = 0; + /* + * Free the memory we have allocated + */ + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + fastboot_free_file(&(nk->fi_files[i])); + } +} + +static void +fastboot_cksum_cdata(fastboot_info_t *nk, uchar_t *md5_hash) +{ + int i; + MD5_CTX md5_ctx; + + MD5Init(&md5_ctx); + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + MD5Update(&md5_ctx, nk->fi_files[i].fb_pte_list_va, + nk->fi_files[i].fb_pte_list_size); + } + MD5Update(&md5_ctx, (void *)nk->fi_pagetable_va, nk->fi_pagetable_size); + MD5Update(&md5_ctx, (void *)nk->fi_new_mbi_va, nk->fi_mbi_size); + + MD5Final(md5_hash, &md5_ctx); +} + +/* + * Generate MD5 checksum of the given kernel. + */ +static void +fastboot_cksum_generate(fastboot_info_t *nk) +{ + int i; + + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + fastboot_cksum_file(nk->fi_files + i, nk->fi_md5_hash[i]); + } + fastboot_cksum_cdata(nk, nk->fi_md5_hash[i]); +} + +/* + * Calculate MD5 checksum of the given kernel and verify that + * it matches with what was calculated before. + */ +int +fastboot_cksum_verify(fastboot_info_t *nk) +{ + int i; + uchar_t md5_hash[MD5_DIGEST_LENGTH]; + + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + fastboot_cksum_file(nk->fi_files + i, md5_hash); + if (bcmp(nk->fi_md5_hash[i], md5_hash, + sizeof (nk->fi_md5_hash[i])) != 0) + return (i + 1); + } + + fastboot_cksum_cdata(nk, md5_hash); + if (bcmp(nk->fi_md5_hash[i], md5_hash, + sizeof (nk->fi_md5_hash[i])) != 0) + return (i + 1); - pt_entry_count = (fsize_roundup >> PAGESHIFT) + 1; - pt_size = P2ROUNDUP(pt_entry_count * 8, PAGESIZE); - contig_free((void *)fb->fb_pte_list_va, pt_size); + return (0); } /* @@ -572,16 +825,15 @@ fastboot_free_file(fastboot_file_t *fb) * - Mark the data structure as valid if all steps have succeeded. */ void -load_kernel(char *mdep) +fastboot_load_kernel(char *mdep) { void *buf = NULL; int i; fastboot_file_t *fb; uint32_t dboot_start_offset; char kern_bootpath[OBP_MAXPATHLEN]; - char bootargs[OBP_MAXPATHLEN]; extern uintptr_t postbootkernelbase; - extern char fb_swtch_image[]; + uintptr_t saved_kernelbase; int bootpath_len = 0; int is_failsafe = 0; int is_retry = 0; @@ -589,6 +841,11 @@ load_kernel(char *mdep) ASSERT(fastreboot_capable); + if (newkernel.fi_valid) + fastboot_free_newkernel(&newkernel); + + saved_kernelbase = postbootkernelbase; + postbootkernelbase = 0; /* @@ -601,8 +858,8 @@ load_kernel(char *mdep) /* * Process the boot argument */ - bzero(bootargs, OBP_MAXPATHLEN); - fastboot_parse_mdep(mdep, kern_bootpath, &bootpath_len, bootargs); + bzero(fastboot_args, OBP_MAXPATHLEN); + fastboot_parse_mdep(mdep, kern_bootpath, &bootpath_len, fastboot_args); /* * Make sure we get the null character @@ -616,8 +873,10 @@ load_kernel(char *mdep) bcopy(kern_bootpath, fastboot_filename[FASTBOOT_NAME_BOOTARCHIVE], bootpath_len); - if (bcmp(kern_bootfile, FAILSAFE_BOOTFILE, - (sizeof (FAILSAFE_BOOTFILE) - 1)) == 0) { + if (bcmp(kern_bootfile, FAILSAFE_BOOTFILE32, + (sizeof (FAILSAFE_BOOTFILE32) - 1)) == 0 || + bcmp(kern_bootfile, FAILSAFE_BOOTFILE64, + (sizeof (FAILSAFE_BOOTFILE64) - 1)) == 0) { is_failsafe = 1; } @@ -633,7 +892,6 @@ load_kernel_retry: size_t fsize_roundup, pt_size; int page_index; uintptr_t offset; - int pt_entry_count; ddi_dma_attr_t dma_attr = fastboot_dma_attr; @@ -682,8 +940,6 @@ load_kernel_retry: "Fastboot: boot archive is too big"); goto err_out; } else { - int j; - /* Set the flag so we don't keep retrying */ is_retry++; @@ -697,9 +953,7 @@ load_kernel_retry: * whose physical addresses might not fit * the new lo and hi constraints. */ - for (j = 0; j < i; j++) - fastboot_free_file( - &newkernel.fi_files[j]); + fastboot_free_mem(&newkernel, end_addr); goto load_kernel_retry; } } @@ -728,22 +982,35 @@ load_kernel_retry: fb->fb_size = fsize; fb->fb_sectcnt = 0; + pt_size = FASTBOOT_PTE_LIST_SIZE(fsize_roundup); + /* - * Allocate one extra page table entry for terminating - * the list. + * If we have reserved memory but it not enough, free it. */ - pt_entry_count = (fsize_roundup >> PAGESHIFT) + 1; - pt_size = P2ROUNDUP(pt_entry_count * 8, PAGESIZE); + if (fb->fb_pte_list_size && fb->fb_pte_list_size < pt_size) { + contig_free((void *)fb->fb_pte_list_va, + fb->fb_pte_list_size); + fb->fb_pte_list_size = 0; + } - if ((fb->fb_pte_list_va = - (x86pte_t *)contig_alloc(pt_size, - &fastboot_below_1G_dma_attr, PAGESIZE, 0)) == NULL) { - cmn_err(CE_WARN, fastboot_enomem_msg, - (uint64_t)pt_size, "1G"); - goto err_out; + if (fb->fb_pte_list_size == 0) { + if ((fb->fb_pte_list_va = + (x86pte_t *)contig_alloc(pt_size, + &fastboot_below_1G_dma_attr, PAGESIZE, 0)) + == NULL) { + cmn_err(CE_WARN, fastboot_enomem_msg, + (uint64_t)pt_size, "1G"); + goto err_out; + } + /* + * fb_pte_list_size must be set after the allocation + * succeeds as it's used to determine how much memory to + * free. + */ + fb->fb_pte_list_size = pt_size; } - bzero((void *)(fb->fb_pte_list_va), pt_size); + bzero((void *)(fb->fb_pte_list_va), fb->fb_pte_list_size); fb->fb_pte_list_pa = mmu_ptob((uint64_t)hat_getpfnum(kas.a_hat, (caddr_t)fb->fb_pte_list_va)); @@ -805,11 +1072,11 @@ load_kernel_retry: if (is_failsafe) { /* Failsafe boot_archive */ - bcopy(BOOTARCHIVE_FAILSAFE, + bcopy(BOOTARCHIVE32_FAILSAFE, &fastboot_filename [FASTBOOT_NAME_BOOTARCHIVE] [bootpath_len], - sizeof (BOOTARCHIVE_FAILSAFE)); + sizeof (BOOTARCHIVE32_FAILSAFE)); } else { bcopy(BOOTARCHIVE32, &fastboot_filename @@ -839,10 +1106,20 @@ load_kernel_retry: goto err_out; } - bcopy(BOOTARCHIVE64, - &fastboot_filename - [FASTBOOT_NAME_BOOTARCHIVE][bootpath_len], - sizeof (BOOTARCHIVE64)); + if (is_failsafe) { + /* Failsafe boot_archive */ + bcopy(BOOTARCHIVE64_FAILSAFE, + &fastboot_filename + [FASTBOOT_NAME_BOOTARCHIVE] + [bootpath_len], + sizeof (BOOTARCHIVE64_FAILSAFE)); + } else { + bcopy(BOOTARCHIVE64, + &fastboot_filename + [FASTBOOT_NAME_BOOTARCHIVE] + [bootpath_len], + sizeof (BOOTARCHIVE64)); + } } else { cmn_err(CE_WARN, "Fastboot: Unknown ELF type"); goto err_out; @@ -862,33 +1139,21 @@ load_kernel_retry: } /* - * Set fb_va to fake_va - */ - for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { - newkernel.fi_files[i].fb_va = fake_va; - - } - - /* * Add the function that will switch us to 32-bit protected mode */ fb = &newkernel.fi_files[FASTBOOT_SWTCH]; fb->fb_va = fb->fb_dest_pa = FASTBOOT_SWTCH_PA; fb->fb_size = MMU_PAGESIZE; - /* - * Map in FASTBOOT_SWTCH_PA - */ - hat_devload(kas.a_hat, (caddr_t)fb->fb_va, MMU_PAGESIZE, - mmu_btop(fb->fb_dest_pa), - PROT_READ | PROT_WRITE | PROT_EXEC, HAT_LOAD_NOCONSIST); - - bcopy((void *)fb_swtch_image, (void *)fb->fb_va, fb->fb_size); + hat_devload(kas.a_hat, (caddr_t)fb->fb_va, + MMU_PAGESIZE, mmu_btop(fb->fb_dest_pa), + PROT_READ | PROT_WRITE | PROT_EXEC, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); /* * Build the new multiboot_info structure */ - if (fastboot_build_mbi(bootargs, &newkernel) != 0) { + if (fastboot_build_mbi(fastboot_args, &newkernel) != 0) { goto err_out; } @@ -909,12 +1174,27 @@ load_kernel_retry: size_t size = MMU_PAGESIZE * 4; #endif /* __amd64 */ - if ((newkernel.fi_pagetable_va = (uintptr_t) - contig_alloc(size, &fastboot_below_1G_dma_attr, - MMU_PAGESIZE, 0)) == NULL) { - cmn_err(CE_WARN, fastboot_enomem_msg, - (uint64_t)size, "1G"); - goto err_out; + if (newkernel.fi_pagetable_size && newkernel.fi_pagetable_size + < size) { + contig_free((void *)newkernel.fi_pagetable_va, + newkernel.fi_pagetable_size); + newkernel.fi_pagetable_size = 0; + } + + if (newkernel.fi_pagetable_size == 0) { + if ((newkernel.fi_pagetable_va = (uintptr_t) + contig_alloc(size, &fastboot_below_1G_dma_attr, + MMU_PAGESIZE, 0)) == NULL) { + cmn_err(CE_WARN, fastboot_enomem_msg, + (uint64_t)size, "1G"); + goto err_out; + } + /* + * fi_pagetable_size must be set after the allocation + * succeeds as it's used to determine how much memory to + * free. + */ + newkernel.fi_pagetable_size = size; } bzero((void *)(newkernel.fi_pagetable_va), size); @@ -935,14 +1215,55 @@ load_kernel_retry: } + /* Generate MD5 checksums */ + fastboot_cksum_generate(&newkernel); + /* Mark it as valid */ newkernel.fi_valid = 1; newkernel.fi_magic = FASTBOOT_MAGIC; + postbootkernelbase = saved_kernelbase; return; err_out: + postbootkernelbase = saved_kernelbase; newkernel.fi_valid = 0; + fastboot_free_newkernel(&newkernel); +} + + +/* ARGSUSED */ +static int +fastboot_xc_func(fastboot_info_t *nk, xc_arg_t unused2, xc_arg_t unused3) +{ + void (*fastboot_func)(fastboot_info_t *); + fastboot_file_t *fb = &nk->fi_files[FASTBOOT_SWTCH]; + fastboot_func = (void (*)())(fb->fb_va); + kthread_t *t_intr = curthread->t_intr; + + if (&kas != curproc->p_as) { + hat_devload(curproc->p_as->a_hat, (caddr_t)fb->fb_va, + MMU_PAGESIZE, mmu_btop(fb->fb_dest_pa), + PROT_READ | PROT_WRITE | PROT_EXEC, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); + } + + /* + * If we have pinned a thread, make sure the address is mapped + * in the address space of the pinned thread. + */ + if (t_intr && t_intr->t_procp->p_as->a_hat != curproc->p_as->a_hat && + t_intr->t_procp->p_as != &kas) + hat_devload(t_intr->t_procp->p_as->a_hat, (caddr_t)fb->fb_va, + MMU_PAGESIZE, mmu_btop(fb->fb_dest_pa), + PROT_READ | PROT_WRITE | PROT_EXEC, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); + + (*psm_shutdownf)(A_SHUTDOWN, AD_FASTREBOOT); + (*fastboot_func)(nk); + + /*NOTREACHED*/ + return (0); } /* @@ -951,8 +1272,144 @@ err_out: void fast_reboot() { - void (*fastboot_func)(fastboot_info_t *); + processorid_t bootcpuid = 0; + extern uintptr_t postbootkernelbase; + extern char fb_swtch_image[]; + fastboot_file_t *fb; + int i; + + postbootkernelbase = 0; + + fb = &newkernel.fi_files[FASTBOOT_SWTCH]; + + /* + * Map the address into both the current proc's address + * space and the kernel's address space in case the panic + * is forced by kmdb. + */ + if (&kas != curproc->p_as) { + hat_devload(curproc->p_as->a_hat, (caddr_t)fb->fb_va, + MMU_PAGESIZE, mmu_btop(fb->fb_dest_pa), + PROT_READ | PROT_WRITE | PROT_EXEC, + HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK); + } + + bcopy((void *)fb_swtch_image, (void *)fb->fb_va, fb->fb_size); + + + /* + * Set fb_va to fake_va + */ + for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) { + newkernel.fi_files[i].fb_va = fake_va; + + } + + if (panicstr && CPU->cpu_id != bootcpuid && + CPU_ACTIVE(cpu_get(bootcpuid))) { + cpuset_t cpuset; + + CPUSET_ZERO(cpuset); + CPUSET_ADD(cpuset, bootcpuid); + xc_trycall((xc_arg_t)&newkernel, 0, 0, cpuset, + (xc_func_t)fastboot_xc_func); + + /* Do what panic_idle() does */ + splx(ipltospl(CLOCK_LEVEL)); + (void) setjmp(&curthread->t_pcb); + for (;;) + ; + } else + (void) fastboot_xc_func(&newkernel, 0, 0); +} + + +/* + * Get boot property value for fastreboot_onpanic. + * + * NOTE: If fastreboot_onpanic is set to non-zero in /etc/system, + * new setting passed in via "-B fastreboot_onpanic" is ignored. + * This order of precedence is to enable developers debugging panics + * that occur early in boot to utilize Fast Reboot on panic. + */ +static void +fastboot_get_bootprop(void) +{ + int val = 0xaa, len, ret; + dev_info_t *devi; + char *propstr = NULL; + + devi = ddi_root_node(); + + ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + FASTREBOOT_ONPANIC, &propstr); + + if (ret == DDI_PROP_SUCCESS) { + if (FASTREBOOT_ONPANIC_NOTSET(propstr)) + val = 0; + else if (FASTREBOOT_ONPANIC_ISSET(propstr)) + val = UA_FASTREBOOT_ONPANIC; + + /* + * Only set fastreboot_onpanic to the value passed in + * if it's not already set to non-zero, and the value + * has indeed been passed in via command line. + */ + if (!fastreboot_onpanic && val != 0xaa) + fastreboot_onpanic = val; + ddi_prop_free(propstr); + } else if (ret != DDI_PROP_NOT_FOUND && ret != DDI_PROP_UNDEFINED) { + cmn_err(CE_WARN, "%s value is invalid, will be ignored", + FASTREBOOT_ONPANIC); + } + + len = sizeof (fastreboot_onpanic_cmdline); + ret = ddi_getlongprop_buf(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + FASTREBOOT_ONPANIC_CMDLINE, fastreboot_onpanic_cmdline, &len); + + if (ret == DDI_PROP_BUF_TOO_SMALL) + cmn_err(CE_WARN, "%s value is too long, will be ignored", + FASTREBOOT_ONPANIC_CMDLINE); +} + +/* + * This function is called by main() to either load the backup kernel for panic + * fast reboot, or to reserve low physical memory for fast reboot. + */ +void +fastboot_post_startup() +{ + if (!fastreboot_capable) + return; + + fastboot_get_bootprop(); + + if (fastreboot_onpanic) + fastboot_load_kernel(fastreboot_onpanic_cmdline); + else if (reserve_mem_enabled) + fastboot_reserve_mem(&newkernel); +} + +/* + * Update boot configuration settings. + * If the new fastreboot_onpanic setting is false, and a kernel has + * been preloaded, free the memory; + * if the new fastreboot_onpanic setting is true and newkernel is + * not valid, load the new kernel. + */ +void +fastboot_update_config(const char *mdep) +{ + uint8_t boot_config = (uint8_t)*mdep; + int cur_fastreboot_onpanic = fastreboot_onpanic; + + if (!fastreboot_capable) + return; - fastboot_func = (void (*)())(newkernel.fi_files[FASTBOOT_SWTCH].fb_va); - (*fastboot_func)(&newkernel); + fastreboot_onpanic = boot_config & UA_FASTREBOOT_ONPANIC; + if (fastreboot_onpanic && (!cur_fastreboot_onpanic || + !newkernel.fi_valid)) + fastboot_load_kernel(fastreboot_onpanic_cmdline); + if (cur_fastreboot_onpanic && !fastreboot_onpanic) + fastboot_free_newkernel(&newkernel); } diff --git a/usr/src/uts/i86pc/os/machdep.c b/usr/src/uts/i86pc/os/machdep.c index 6ead84ab84..c944c3bcfe 100644 --- a/usr/src/uts/i86pc/os/machdep.c +++ b/usr/src/uts/i86pc/os/machdep.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -166,8 +166,6 @@ extern void pm_cfb_rele(void); extern fastboot_info_t newkernel; -int quiesce_active = 0; - /* * Machine dependent code to reboot. * "mdep" is interpreted as a character pointer; if non-null, it is a pointer @@ -186,6 +184,7 @@ mdboot(int cmd, int fcn, char *mdep, boolean_t invoke_cb) static int is_first_quiesce = 1; static int is_first_reset = 1; int reset_status = 0; + static char fallback_str[] = "Falling back to regular reboot.\n"; if (fcn == AD_FASTREBOOT && !newkernel.fi_valid) fcn = AD_BOOT; @@ -267,6 +266,14 @@ mdboot(int cmd, int fcn, char *mdep, boolean_t invoke_cb) } /* + * If the system is panicking, the preloaded kernel is valid, + * and fastreboot_onpanic has been set, choose Fast Reboot. + */ + if (fcn == AD_BOOT && panicstr && newkernel.fi_valid && + fastreboot_onpanic) + fcn = AD_FASTREBOOT; + + /* * Try to quiesce devices. */ if (is_first_quiesce) { @@ -282,8 +289,10 @@ mdboot(int cmd, int fcn, char *mdep, boolean_t invoke_cb) if (reset_status == -1) { if (fcn == AD_FASTREBOOT && !force_fastreboot) { prom_printf("Driver(s) not capable of fast " - "reboot. Fall back to regular reboot.\n"); + "reboot.\n"); + prom_printf(fallback_str); fastreboot_capable = 0; + fcn = AD_BOOT; } else if (fcn != AD_FASTREBOOT) fastreboot_capable = 0; } @@ -308,15 +317,30 @@ mdboot(int cmd, int fcn, char *mdep, boolean_t invoke_cb) reset_leaves(); } + /* Verify newkernel checksum */ + if (fastreboot_capable && fcn == AD_FASTREBOOT && + fastboot_cksum_verify(&newkernel) != 0) { + fastreboot_capable = 0; + prom_printf("Fast reboot: checksum failed for the new " + "kernel.\n"); + prom_printf(fallback_str); + } + (void) spl8(); - (*psm_shutdownf)(cmd, fcn); - if (fcn == AD_FASTREBOOT && !panicstr && fastreboot_capable) + if (fastreboot_capable && fcn == AD_FASTREBOOT) { + /* + * psm_shutdown is called within fast_reboot() + */ fast_reboot(); - else if (fcn == AD_HALT || fcn == AD_POWEROFF) - halt((char *)NULL); - else - prom_reboot(""); + } else { + (*psm_shutdownf)(cmd, fcn); + + if (fcn == AD_HALT || fcn == AD_POWEROFF) + halt((char *)NULL); + else + prom_reboot(""); + } /*NOTREACHED*/ } @@ -328,14 +352,15 @@ mdpreboot(int cmd, int fcn, char *mdep) if (fcn == AD_FASTREBOOT && !fastreboot_capable) { fcn = AD_BOOT; #ifdef __xpv - cmn_err(CE_WARN, "Fast reboot not supported on xVM"); + cmn_err(CE_WARN, "Fast reboot is not supported on xVM"); #else - cmn_err(CE_WARN, "Fast reboot not supported on this platform"); + cmn_err(CE_WARN, + "Fast reboot is not supported on this platform"); #endif } if (fcn == AD_FASTREBOOT) { - load_kernel(mdep); + fastboot_load_kernel(mdep); if (!newkernel.fi_valid) fcn = AD_BOOT; } diff --git a/usr/src/uts/i86pc/sys/fastboot.h b/usr/src/uts/i86pc/sys/fastboot.h index 5eb2cf55c4..45c74d9052 100644 --- a/usr/src/uts/i86pc/sys/fastboot.h +++ b/usr/src/uts/i86pc/sys/fastboot.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,6 +39,7 @@ extern "C" { #ifndef _ASM #include <sys/types.h> #include <sys/mach_mmu.h> +#include <sys/md5.h> #endif /* _ASM */ #define FASTBOOT_NAME_UNIX 0 @@ -47,6 +48,8 @@ extern "C" { #define FASTBOOT_MAX_FILES_MAP 2 /* max number of files that needs mapping */ #define FASTBOOT_MAX_FILES_TOTAL 3 /* max number of files */ +#define FASTBOOT_MAX_MD5_HASH (FASTBOOT_MAX_FILES_MAP + 1) + #define FASTBOOT_SWTCH_PA 0x5000 /* low memory */ #define FASTBOOT_STACK_OFFSET 0xe00 /* where the stack starts */ #define FASTBOOT_MAGIC ('F' << 24 | 'A' << 16 | 'S' << 8 | 'T') @@ -103,6 +106,7 @@ typedef struct _fastboot_file { uintptr_t fb_va; /* virtual address */ x86pte_t *fb_pte_list_va; /* VA for PTE list */ paddr_t fb_pte_list_pa; /* PA for PTE list */ + size_t fb_pte_list_size; /* size of PTE list */ uintptr_t fb_dest_pa; /* destination PA */ size_t fb_size; /* file size */ uintptr_t fb_next_pa; @@ -113,6 +117,10 @@ typedef struct _fastboot_file { /* * Data structure containing all the information the switching routine needs * for fast rebooting to the new kernel. + * + * NOTE: There is limited stack space (0x200 bytes) in the switcher to + * copy in the data structure. Fields that are not absolutely necessary for + * the switcher should be added after the fi_valid field. */ typedef struct _fastboot_info { uint32_t fi_magic; /* magic for fast reboot */ @@ -129,14 +137,38 @@ typedef struct _fastboot_info { uint_t fi_ptes_per_table; uint_t fi_lpagesize; int fi_top_level; /* top level of page tables */ + size_t fi_pagetable_size; /* size allocated for pt */ + uintptr_t fi_new_mbi_va; /* new multiboot info VA */ + size_t fi_mbi_size; /* size allocated for mbi */ + uchar_t fi_md5_hash[FASTBOOT_MAX_MD5_HASH][MD5_DIGEST_LENGTH]; } fastboot_info_t; -extern void fast_reboot(); -extern void load_kernel(char *); +/* + * Fast reboot core functions + */ +extern void fast_reboot(); /* Entry point for fb_switch */ +extern void fastboot_load_kernel(char *); /* Load a new kernel */ + +extern int fastboot_cksum_verify(fastboot_info_t *); + +/* + * Fast reboot tunables + */ + +/* If set, the system is capable of fast reboot */ extern int fastreboot_capable; + +/* + * If set, force fast reboot even if the system has + * drivers without quiesce(9E) implementation. + */ extern int force_fastreboot; +/* If set, fast reboot after panic. */ +extern int fastreboot_onpanic; +extern char fastreboot_onpanic_cmdline[FASTBOOT_SAVED_CMDLINE_LEN]; + #endif /* _ASM */ #ifdef __cplusplus diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s index 4ee5540a8c..2f73b8673a 100644 --- a/usr/src/uts/intel/ia32/ml/modstubs.s +++ b/usr/src/uts/intel/ia32/ml/modstubs.s @@ -905,6 +905,17 @@ fcnname/**/_info: \ #endif /* + * Stubs for MD5 + */ +#ifndef MD5_MODULE + MODULE(md5,misc); + WSTUB(md5, MD5Init, nomod_zero); + WSTUB(md5, MD5Update, nomod_zero); + WSTUB(md5, MD5Final, nomod_zero); + END_MODULE(md5); +#endif + +/* * Stubs for idmap */ #ifndef IDMAP_MODULE |