summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSherry Moore <Sherry.Moore@Sun.COM>2009-03-24 14:38:50 -0700
committerSherry Moore <Sherry.Moore@Sun.COM>2009-03-24 14:38:50 -0700
commit753a6d457b330b1b29b2d3eefcd0831116ce950d (patch)
treec18350d4e3f43265a05f156f9c9175474f3fc3e7
parent0c59ba075fc10821b752d32ee83a714261bca290 (diff)
downloadillumos-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()
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/Makefile.check1
-rw-r--r--usr/src/cmd/halt/Makefile44
-rw-r--r--usr/src/cmd/halt/halt.c244
-rw-r--r--usr/src/cmd/halt/smf/Makefile40
-rw-r--r--usr/src/cmd/halt/smf/boot-config.xml179
-rwxr-xr-xusr/src/cmd/halt/smf/svc-boot-config40
-rw-r--r--usr/src/cmd/initpkg/umountall.sh1
-rw-r--r--usr/src/cmd/svc/startd/Makefile5
-rw-r--r--usr/src/cmd/svc/startd/graph.c55
-rw-r--r--usr/src/cmd/uadmin/Makefile7
-rw-r--r--usr/src/cmd/uadmin/uadmin.c30
-rw-r--r--usr/src/lib/Makefile6
-rw-r--r--usr/src/lib/libbsm/audit_event.txt1
-rw-r--r--usr/src/lib/libbsm/common/adt.xml13
-rw-r--r--usr/src/lib/libc/amd64/sys/uadmin.c7
-rw-r--r--usr/src/lib/libc/i386/sys/uadmin.c7
-rw-r--r--usr/src/lib/libgrubmgmt/Makefile66
-rw-r--r--usr/src/lib/libgrubmgmt/Makefile.com55
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_bargs.c225
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_cmd.c354
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_cmd.def65
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_entry.c191
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_errno.c64
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_errno.def77
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_errno.h50
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_fs.c559
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_impl.h248
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_menu.c532
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrubmgmt.h98
-rw-r--r--usr/src/lib/libgrubmgmt/common/llib-lgrubmgmt29
-rw-r--r--usr/src/lib/libgrubmgmt/common/mapfile-vers34
-rw-r--r--usr/src/lib/libgrubmgmt/i386/Makefile29
-rw-r--r--usr/src/lib/libgrubmgmt/sparc/Makefile29
-rw-r--r--usr/src/lib/libscf/Makefile.com16
-rw-r--r--usr/src/lib/libscf/common/highlevel.c193
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers2
-rw-r--r--usr/src/lib/libscf/inc/libscf_priv.h6
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h3
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c154
-rw-r--r--usr/src/pkgdefs/Makefile1
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_i3864
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/depend53
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/pkginfo.tmpl50
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/prototype_com39
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/prototype_i38627
-rw-r--r--usr/src/pkgdefs/SUNWgrubu/prototype_sparc27
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com1
-rw-r--r--usr/src/uts/common/os/main.c4
-rw-r--r--usr/src/uts/common/os/mutex.c7
-rw-r--r--usr/src/uts/common/os/panic.c9
-rw-r--r--usr/src/uts/common/os/zone.c1
-rw-r--r--usr/src/uts/common/sys/mutex.h6
-rw-r--r--usr/src/uts/common/sys/systm.h3
-rw-r--r--usr/src/uts/common/sys/uadmin.h47
-rw-r--r--usr/src/uts/common/syscall/uadmin.c53
-rw-r--r--usr/src/uts/i86pc/os/fakebop.c157
-rw-r--r--usr/src/uts/i86pc/os/fastboot.c623
-rw-r--r--usr/src/uts/i86pc/os/machdep.c51
-rw-r--r--usr/src/uts/i86pc/sys/fastboot.h38
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s11
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