changeset 12959:f5f96e09bf49

PSARC/2010/113 Boot Block Downgrade Avoidance PSARC/2010/271 EOF and removal of installgrub(1m) floppy support 6944352 prevent downgrading the boot block on update on multi-BE environments 6972371 EOF and removal of installgrub(1m) floppy support [PSARC/2010/271]
author Enrico Perla - Sun Microsystems <Enrico.Perla@Sun.COM>
date Wed, 28 Jul 2010 15:51:57 -0700
parents ebf6da4d3fca
children 59a6f9b695ef
files usr/src/cmd/boot/Makefile usr/src/cmd/boot/common/bblk_einfo.c usr/src/cmd/boot/common/bblk_einfo.h usr/src/cmd/boot/common/boot_utils.c usr/src/cmd/boot/common/boot_utils.h usr/src/cmd/boot/common/mboot_extra.c usr/src/cmd/boot/common/mboot_extra.h usr/src/cmd/boot/installboot/Makefile usr/src/cmd/boot/installboot/installboot.c usr/src/cmd/boot/installboot/installboot.h usr/src/cmd/boot/installgrub/Makefile usr/src/cmd/boot/installgrub/floppy.c usr/src/cmd/boot/installgrub/installgrub.c usr/src/cmd/boot/installgrub/installgrub.h usr/src/cmd/boot/installgrub/pcfs_glue.c usr/src/grub/grub-0.97/grub/asmstub.c usr/src/grub/grub-0.97/stage2/asm.S usr/src/grub/grub-0.97/stage2/builtins.c usr/src/grub/grub-0.97/stage2/shared.h usr/src/psm/stand/bootblks/ufs/sparc/Makefile usr/src/psm/stand/bootblks/ufs/sparc/installboot.sh
diffstat 21 files changed, 3589 insertions(+), 1007 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/boot/Makefile	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/cmd/boot/Makefile	Wed Jul 28 15:51:57 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include $(SRC)/cmd/Makefile.cmd
@@ -38,6 +37,9 @@
 	mbr		\
 	symdef
 
+sparc_SUBDIRS=		\
+	installboot
+
 COMMON_LINTSUBDIRS=	\
 	bootadm	
 
@@ -46,6 +48,9 @@
 	installgrub	\
 	symdef
 
+sparc_LINTSUBDIRS=	\
+	installboot
+
 COMMON_MSGSUBDIRS=	\
 	bootadm		\
 	fiocompress
@@ -53,6 +58,9 @@
 i386_MSGSUBDIRS=	\
 	installgrub
 
+sparc_MSGSUBDIRS=	\
+	installboot
+
 
 all:=		TARGET= all
 install:=	TARGET= install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/bblk_einfo.c	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,414 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "bblk_einfo.h"
+#include "boot_utils.h"
+
+bblk_hash_t	bblk_no_hash = {BBLK_NO_HASH, 0, "(no hash)", NULL};
+bblk_hash_t	bblk_md5_hash = {BBLK_HASH_MD5, 0x10, "MD5", md5_calc};
+
+bblk_hash_t	*bblk_hash_list[BBLK_HASH_TOT] = {
+	&bblk_no_hash,
+	&bblk_md5_hash
+};
+
+/*
+ * einfo_compare_dotted_version()
+ * Compares two strings with an arbitrary long number of dot-separated numbers.
+ * Returns:	0  - if the version numbers are equal
+ *		1  - if str1 version number is more recent than str2
+ *		2  - if str2 version number is more recent than str1
+ *		-1 - if an error occurred
+ *
+ * Comparison is done field by field, by retrieving an unsigned integer value,
+ * (missing fields are assumed as 0, but explict zeroes take precedence) so:
+ *   4.1.2.11 > 4.1.2.2 > 4.1.2.0 > 4.1.2
+ *
+ * where ">" means "more recent than".
+ */
+static int
+einfo_compare_dotted_version(const char *str1, const char *str2)
+{
+	int		retval = 0;
+	char		*verstr1, *verstr2, *freeptr1, *freeptr2;
+	char		*parsep1, *parsep2;
+	unsigned int	val_str1, val_str2;
+
+	freeptr1 = verstr1 = strdup(str1);
+	freeptr2 = verstr2 = strdup(str2);
+	if (verstr1 == NULL || verstr2 == NULL) {
+		retval = -1;
+		goto out;
+	}
+
+	while (verstr1 != NULL && verstr2 != NULL) {
+		parsep1 = strsep(&verstr1, ".");
+		parsep2 = strsep(&verstr2, ".");
+
+		val_str1 = atoi(parsep1);
+		val_str2 = atoi(parsep2);
+
+		if (val_str1 > val_str2) {
+			retval = 1;
+			goto out;
+		}
+
+		if (val_str2 > val_str1) {
+			retval = 2;
+			goto out;
+		}
+	}
+
+	/* Common portion of the version string is equal. */
+	if (verstr1 == NULL && verstr2 != NULL)
+		retval = 2;
+	if (verstr2 == NULL && verstr1 != NULL)
+		retval = 1;
+
+out:
+	free(freeptr1);
+	free(freeptr2);
+	return (retval);
+}
+
+/*
+ * einfo_compare_timestamps()
+ * Currently, timestamp is in %Y%m%dT%H%M%SZ format in UTC, which means that
+ * we can simply do a lexicographic comparison to know which one is the most
+ * recent.
+ *
+ * Returns:   0  - if timestamps coincide
+ *            1  - if the timestamp in str1 is more recent
+ *            2  - if the timestamp in str2 is more recent
+ */
+static int
+einfo_compare_timestamps(const char *str1, const char *str2)
+{
+	int	retval;
+
+	retval = strcmp(str1, str2);
+	if (retval > 0)
+		retval = 1;
+	if (retval < 0)
+		retval = 2;
+
+	return (retval);
+}
+
+/*
+ * einfo_compare_version()
+ * Given two extended versions, compare the two and returns which one is more
+ * "recent". Comparison is based on dotted version number fields and a
+ * timestamp.
+ *
+ * Returns:    -1   - on error
+ *              0   - if the two versions coincide
+ *              1   - if the version in str1 is more recent
+ *              2   - if the version in str2 is more recent
+ */
+static int
+einfo_compare_version(const char *str1, const char *str2)
+{
+	int	retval = 0;
+	char	*verstr1, *verstr2, *freeptr1, *freeptr2;
+	char	*parsep1, *parsep2;
+
+	freeptr1 = verstr1 = strdup(str1);
+	freeptr2 = verstr2 = strdup(str2);
+	if (verstr1 == NULL || verstr2 == NULL) {
+		retval = -1;
+		goto out;
+	}
+
+	parsep1 = verstr1;
+	parsep2 = verstr2;
+
+	while (parsep1 != NULL && parsep2 != NULL) {
+		parsep1 = strsep(&verstr1, ",:-");
+		parsep2 = strsep(&verstr2, ",:-");
+
+		/* verstr1 or verstr2 will be NULL before parsep1 or parsep2. */
+		if (verstr1 == NULL || verstr2 == NULL) {
+			retval = einfo_compare_timestamps(parsep1, parsep2);
+			goto out;
+		}
+
+		retval = einfo_compare_dotted_version(parsep1, parsep2);
+		if (retval == 0)
+			continue;
+		else
+			goto out;
+	}
+out:
+	free(freeptr1);
+	free(freeptr2);
+	return (retval);
+}
+
+/*
+ * print_einfo()
+ *
+ * Print the extended information contained into the pointed structure.
+ * 'bufsize' specifies the real size of the structure, since str_off and
+ * hash_off need to point somewhere past the header.
+ */
+void
+print_einfo(uint8_t flags, bblk_einfo_t *einfo, unsigned long bufsize)
+{
+	int		i = 0;
+	char		*version;
+	boolean_t	has_hash = B_FALSE;
+	unsigned char	*hash;
+
+	if (einfo->str_off + einfo->str_size > bufsize) {
+		(void) fprintf(stdout, gettext("String offset %d is beyond the "
+		    "buffer size\n"), einfo->str_off);
+		return;
+	}
+
+	version = (char *)einfo + einfo->str_off;
+	if (einfo->hash_type != BBLK_NO_HASH &&
+	    einfo->hash_type < BBLK_HASH_TOT) {
+		if (einfo->hash_off + einfo->hash_size > bufsize) {
+			(void) fprintf(stdout, gettext("Warning: hashing "
+			    "present but hash offset %d is beyond the buffer "
+			    "size\n"), einfo->hash_off);
+			has_hash = B_FALSE;
+		} else {
+			hash = (unsigned char *)einfo + einfo->hash_off;
+			has_hash = B_TRUE;
+		}
+	}
+
+	if (flags & EINFO_PRINT_HEADER) {
+		(void) fprintf(stdout, "Boot Block Extended Info Header:\n");
+		(void) fprintf(stdout, "\tmagic: ");
+		for (i = 0; i < EINFO_MAGIC_SIZE; i++)
+			(void) fprintf(stdout, "%c", einfo->magic[i]);
+		(void) fprintf(stdout, "\n");
+		(void) fprintf(stdout, "\tversion: %d\n", einfo->version);
+		(void) fprintf(stdout, "\tflags: %x\n", einfo->flags);
+		(void) fprintf(stdout, "\textended version string offset: %d\n",
+		    einfo->str_off);
+		(void) fprintf(stdout, "\textended version string size: %d\n",
+		    einfo->str_size);
+		(void) fprintf(stdout, "\thashing type: %d (%s)\n",
+		    einfo->hash_type, has_hash ?
+		    bblk_hash_list[einfo->hash_type]->name : "nil");
+		(void) fprintf(stdout, "\thash offset: %d\n", einfo->hash_off);
+		(void) fprintf(stdout, "\thash size: %d\n", einfo->hash_size);
+	}
+
+	if (flags & EINFO_EASY_PARSE) {
+		(void) fprintf(stdout, "%s\n", version);
+	} else {
+		(void) fprintf(stdout, "Extended version string: %s\n",
+		    version);
+		if (has_hash) {
+			(void) fprintf(stdout, "%s hash: ",
+			    bblk_hash_list[einfo->hash_type]->name);
+		} else {
+			(void) fprintf(stdout, "No hashing available\n");
+		}
+	}
+
+	if (has_hash) {
+		for (i = 0; i < einfo->hash_size; i++) {
+			(void) fprintf(stdout, "%02x", hash[i]);
+		}
+		(void) fprintf(stdout, "\n");
+	}
+}
+
+static int
+compute_hash(bblk_hs_t *hs, unsigned char *dest, bblk_hash_t *hash)
+{
+	if (hs == NULL || dest == NULL || hash == NULL)
+		return (-1);
+
+	hash->compute_hash(dest, hs->src_buf, hs->src_size);
+	return (0);
+}
+
+int
+prepare_and_write_einfo(unsigned char *dest, char *infostr, bblk_hs_t *hs,
+    uint32_t maxsize, uint32_t *used_space)
+{
+	uint16_t	hash_size;
+	uint32_t	hash_off;
+	unsigned char	*data;
+	bblk_einfo_t	*einfo = (bblk_einfo_t *)dest;
+	bblk_hash_t	*hashinfo = bblk_hash_list[BBLK_DEFAULT_HASH];
+
+	/*
+	 * 'dest' might be both containing the buffer we want to hash and
+	 * containing our einfo structure: delay any update of it after the
+	 * hashing has been calculated.
+	 */
+	hash_size = hashinfo->size;
+	hash_off = sizeof (bblk_einfo_t);
+
+	if (hash_off + hash_size > maxsize) {
+		(void) fprintf(stderr, gettext("Unable to add extended info, "
+		    "not enough space\n"));
+		return (-1);
+	}
+
+	data = dest + hash_off;
+	if (compute_hash(hs, data, hashinfo) < 0) {
+		(void) fprintf(stderr, gettext("%s hash operation failed\n"),
+		    hashinfo->name);
+		einfo->hash_type = bblk_no_hash.type;
+		einfo->hash_size = bblk_no_hash.size;
+	} else {
+		einfo->hash_type = hashinfo->type;
+		einfo->hash_size = hashinfo->size;
+	}
+
+	(void) memcpy(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE);
+	einfo->version = BBLK_EINFO_VERSION;
+	einfo->flags = 0;
+	einfo->hash_off = hash_off;
+	einfo->hash_size = hash_size;
+	einfo->str_off = einfo->hash_off + einfo->hash_size + 1;
+
+	if (infostr == NULL) {
+		(void) fprintf(stderr, gettext("Unable to add extended info, "
+		    "string is empty\n"));
+		return (-1);
+	}
+	einfo->str_size = strlen(infostr);
+
+	if (einfo->str_off + einfo->str_size > maxsize) {
+		(void) fprintf(stderr, gettext("Unable to add extended info, "
+		    "not enough space\n"));
+		return (-1);
+	}
+
+	data = dest + einfo->str_off;
+	(void) memcpy(data, infostr, einfo->str_size);
+	*used_space = einfo->str_off + einfo->str_size;
+
+	return (0);
+}
+
+/*
+ * einfo_should_update()
+ * Given information on the boot block currently on disk (disk_einfo) and
+ * information on the supplied boot block (hs for hashing, verstr as the
+ * associated version string) decide if an update of the on-disk boot block
+ * is necessary or not.
+ */
+boolean_t
+einfo_should_update(bblk_einfo_t *disk_einfo, bblk_hs_t *hs, char *verstr)
+{
+	bblk_hash_t	*hashing;
+	unsigned char	*disk_hash;
+	unsigned char	*local_hash;
+	char		*disk_version;
+	int		retval;
+
+	if (disk_einfo == NULL)
+		return (B_TRUE);
+
+	if (memcmp(disk_einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0)
+		return (B_TRUE);
+
+	if (disk_einfo->version < BBLK_EINFO_VERSION)
+		return (B_TRUE);
+
+	disk_version = einfo_get_string(disk_einfo);
+	retval = einfo_compare_version(verstr, disk_version);
+	/*
+	 * If something goes wrong or if the on-disk version is more recent
+	 * do not update the bootblock.
+	 */
+	if (retval == -1 || retval == 2)
+		return (B_FALSE);
+
+	/*
+	 * If we got here it means that the two version strings are either
+	 * equal or the new bootblk binary is more recent. In order to save
+	 * some needless writes let's use the hash to determine if an update
+	 * is really necessary.
+	 */
+	if (disk_einfo->hash_type == bblk_no_hash.type)
+		return (B_TRUE);
+
+	if (disk_einfo->hash_type >= BBLK_HASH_TOT)
+		return (B_TRUE);
+
+	hashing = bblk_hash_list[disk_einfo->hash_type];
+
+	local_hash = malloc(hashing->size);
+	if (local_hash == NULL)
+		return (B_TRUE);
+
+	/*
+	 * Failure in computing the hash may mean something wrong
+	 * with the boot block file. Better be conservative here.
+	 */
+	if (compute_hash(hs, local_hash, hashing) < 0) {
+		free(local_hash);
+		return (B_FALSE);
+	}
+
+	disk_hash = (unsigned char *)einfo_get_hash(disk_einfo);
+
+	if (memcmp(local_hash, disk_hash, disk_einfo->hash_size) == 0) {
+		free(local_hash);
+		return (B_FALSE);
+	}
+
+	free(local_hash);
+	return (B_TRUE);
+}
+
+char *
+einfo_get_string(bblk_einfo_t *einfo)
+{
+	if (einfo == NULL)
+		return (NULL);
+
+	return ((char *)einfo + einfo->str_off);
+}
+
+char *
+einfo_get_hash(bblk_einfo_t *einfo)
+{
+	if (einfo == NULL)
+		return (NULL);
+
+	return ((char *)einfo + einfo->hash_off);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/bblk_einfo.h	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,94 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_BBLKEINFO_H
+#define	_BBLKEINFO_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <md5.h>
+
+#define	BBLK_EINFO_VERSION	(1)
+
+#define	EINFO_MAGIC		"EXTINFO"
+#define	EINFO_MAGIC_SIZE	(7)
+
+#pragma pack(1)
+typedef struct _extended_info {
+	char		magic[EINFO_MAGIC_SIZE];
+	uint8_t		version;
+	uint8_t		flags;
+	uint32_t	str_off;
+	uint16_t	str_size;
+	uint8_t		hash_type;
+	uint32_t	hash_off;
+	uint16_t	hash_size;
+	char		rsvd[32];
+} bblk_einfo_t;
+#pragma pack()
+
+enum bblk_hash_types_t {
+	BBLK_NO_HASH = 0,
+	BBLK_HASH_MD5,
+	BBLK_HASH_TOT
+};
+
+#define	EINFO_PRINT_HEADER	0x01
+#define	EINFO_EASY_PARSE	0x02
+
+typedef struct _hashing_function {
+	unsigned int	type;
+	unsigned int	size;
+	char		name[16];
+	void 		(*compute_hash)(void *, const void *, unsigned int);
+} bblk_hash_t;
+
+typedef struct _hashing_source {
+	unsigned char	*src_buf;
+	unsigned int	src_size;
+} bblk_hs_t;
+
+#define	BBLK_DEFAULT_HASH	BBLK_HASH_MD5
+
+extern bblk_hash_t	bblk_no_hash;
+extern bblk_hash_t	bblk_md5_hash;
+extern bblk_hash_t	*bblk_hash_list[BBLK_HASH_TOT];
+
+void print_einfo(uint8_t, bblk_einfo_t *, unsigned long);
+int prepare_and_write_einfo(unsigned char *, char *, bblk_hs_t *,
+    uint32_t, uint32_t *);
+boolean_t einfo_should_update(bblk_einfo_t *, bblk_hs_t *, char *);
+char *einfo_get_string(bblk_einfo_t *);
+char *einfo_get_hash(bblk_einfo_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _BBLKEINFO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/boot_utils.c	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,116 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <libintl.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include "bblk_einfo.h"
+#include "boot_utils.h"
+
+boolean_t boot_debug = B_FALSE;
+boolean_t nowrite = B_FALSE;
+
+void
+boot_gdebug(const char *funcname, char *format, ...)
+{
+	va_list ap;
+
+	if (boot_debug == B_FALSE)
+		return;
+
+	(void) fprintf(stdout, "%s(): ", funcname);
+
+	va_start(ap, format);
+	/* LINTED: E_SEC_PRINTF_VAR_FMT */
+	(void) vfprintf(stdout, format, ap);
+	va_end(ap);
+}
+
+/*
+ * Common functions to write out and read in block-sized data to a file
+ * descriptor.
+ */
+int
+write_out(int fd, void *buffer, size_t size, off_t off)
+{
+	int		ret;
+	char		*buf = buffer;
+
+	if (size % SECTOR_SIZE != 0)
+		BOOT_DEBUG("Expected block-sized data, got: %d\n", size);
+
+	/* Dry run. */
+	if (nowrite)
+		return (BC_SUCCESS);
+
+	for (;;) {
+	again:
+		ret = pwrite(fd, buf, size, off);
+		if (ret == -1) {
+			if (errno == EAGAIN)
+				goto again;
+			else
+				return (BC_ERROR);
+			}
+		if (ret < size) {
+			size -= ret;
+			off += ret;
+			buf += ret;
+		} else {
+			break;
+		}
+	}
+	return (BC_SUCCESS);
+}
+
+int
+read_in(int fd, void *buffer, size_t size, off_t off)
+{
+	int		ret;
+	char		*buf = buffer;
+
+	if (size % SECTOR_SIZE != 0)
+		BOOT_DEBUG("Expected block-sized data, got: %d\n", size);
+
+	for (;;) {
+	again:
+		ret = pread(fd, buf, size, off);
+		if (ret == -1) {
+			if (errno == EAGAIN)
+				goto again;
+			else
+				return (BC_ERROR);
+			}
+		if (ret < size) {
+			size -= ret;
+			off += ret;
+			buf += ret;
+		} else {
+			break;
+		}
+	}
+	return (BC_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/boot_utils.h	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,59 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_BOOT_UTILS_H
+#define	_BOOT_UTILS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include "bblk_einfo.h"
+
+/* Common return values for various operations. */
+#define	BC_SUCCESS		(0)
+#define	BC_ERROR		(1)
+#define	BC_NOUPDT		(4)
+#define	BC_NOEXTRA		(5)
+#define	BC_NOEINFO		(6)
+
+#define	SECTOR_SIZE		(512)
+
+extern boolean_t boot_debug;
+extern boolean_t nowrite;
+
+#define	BOOT_DEBUG(...)	boot_gdebug(__func__, __VA_ARGS__)
+
+void boot_gdebug(const char *, char *, ...);
+
+int write_out(int, void *, size_t, off_t);
+int read_in(int, void *, size_t, off_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _BOOT_UTILS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/mboot_extra.c	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,180 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <sys/multiboot.h>
+#include <sys/sysmacros.h>
+
+#include "bblk_einfo.h"
+#include "boot_utils.h"
+#include "mboot_extra.h"
+
+/*
+ * Common functions to deal with the fake-multiboot encapsulation of the
+ * bootblock and the location of the extra information area.
+ */
+
+/* mboot checksum routine. */
+uint32_t
+compute_checksum(char *data, uint32_t size)
+{
+	uint32_t	*ck_ptr;
+	uint32_t	cksum = 0;
+	int		i;
+
+	ck_ptr = (uint32_t *)data;
+	for (i = 0; i < size; i += sizeof (uint32_t))
+		cksum += *ck_ptr++;
+
+	return (-cksum);
+}
+
+/* Given a buffer, look for a multiboot header within it. */
+int
+find_multiboot(char *buffer, uint32_t buf_size, uint32_t *mboot_off)
+{
+	multiboot_header_t	*mboot;
+	uint32_t		*iter;
+	uint32_t		cksum;
+	uint32_t		boundary;
+	int			i = 0;
+
+	iter = (uint32_t *)buffer;
+	*mboot_off = 0;
+	/* multiboot header has to be within the first 32K. */
+	boundary = MBOOT_SCAN_SIZE;
+	if (boundary > buf_size)
+		boundary = buf_size;
+
+	boundary = boundary - sizeof (multiboot_header_t);
+
+	for (i = 0; i < boundary; i += 4, iter++) {
+
+		mboot = (multiboot_header_t *)iter;
+		if (mboot->magic != MB_HEADER_MAGIC)
+			continue;
+
+		/* Found magic signature -- check checksum. */
+		cksum = -(mboot->flags + mboot->magic);
+		if (mboot->checksum != cksum) {
+			BOOT_DEBUG("multiboot magic found at %p, but checksum "
+			    "mismatches (is %x, should be %x)\n", mboot,
+			    mboot->checksum, cksum);
+			continue;
+		} else {
+			if (!(mboot->flags & BB_MBOOT_AOUT_FLAG)) {
+				BOOT_DEBUG("multiboot structure found, but no "
+				    "AOUT kludge specified, skipping.\n");
+				continue;
+			} else {
+				/* proper multiboot structure found. */
+				*mboot_off = i;
+				return (BC_SUCCESS);
+			}
+		}
+	}
+
+	return (BC_ERROR);
+}
+
+/*
+ * Given a pointer to the extra information area (a sequence of bb_header_ext_t
+ * + payload chunks), find the extended information structure.
+ */
+bblk_einfo_t *
+find_einfo(char *extra)
+{
+	bb_header_ext_t		*ext_header;
+	bblk_einfo_t		*einfo;
+	uint32_t		cksum;
+
+	assert(extra != NULL);
+
+	ext_header = (bb_header_ext_t *)extra;
+	cksum = compute_checksum(extra + sizeof (bb_header_ext_t),
+	    ext_header->size);
+	BOOT_DEBUG("Extended information header checksum is %x\n", cksum);
+
+	if (cksum != ext_header->checksum) {
+		BOOT_DEBUG("Unable to find extended versioning information, "
+		    "data looks corrupted\n");
+		return (NULL);
+	}
+
+	/*
+	 * Currently we only have one extra header so it must be encapsulating
+	 * the extended information structure.
+	 */
+	einfo = (bblk_einfo_t *)(extra + sizeof (bb_header_ext_t));
+	if (memcmp(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0) {
+		BOOT_DEBUG("Unable to read stage2 extended versioning "
+		    "information, wrong magic identifier\n");
+		BOOT_DEBUG("Found %s, expected %s\n", einfo->magic,
+		    EINFO_MAGIC);
+		return (NULL);
+	}
+
+	return (einfo);
+}
+
+/*
+ * Given a pointer to the extra area, add the extended information structure
+ * encapsulated by a bb_header_ext_t structure.
+ */
+void
+add_einfo(char *extra, char *updt_str, bblk_hs_t *hs, uint32_t avail_space)
+{
+	bb_header_ext_t	*ext_hdr;
+	uint32_t	used_space;
+	unsigned char	*dest;
+	int		ret;
+
+	assert(extra != NULL);
+
+	if (updt_str == NULL) {
+		BOOT_DEBUG("WARNING: no update string passed to "
+		    "add_stage2_einfo()\n");
+		return;
+	}
+
+	/* Reserve space for the extra header. */
+	ext_hdr = (bb_header_ext_t *)extra;
+	dest = (unsigned char *)extra + sizeof (*ext_hdr);
+	/* Place the extended information structure. */
+	ret = prepare_and_write_einfo(dest, updt_str, hs, avail_space,
+	    &used_space);
+	if (ret != 0) {
+		(void) fprintf(stderr, gettext("Unable to write the extended "
+		    "versioning information\n"));
+		return;
+	}
+
+	/* Fill the extended information associated header. */
+	ext_hdr->size = P2ROUNDUP(used_space, 8);
+	ext_hdr->checksum = compute_checksum((char *)dest, ext_hdr->size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/common/mboot_extra.h	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,58 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_MBOOT_EXTRA_H
+#define	_MBOOT_EXTRA_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include "bblk_einfo.h"
+
+/* multiboot header needs to be located in the first 32KB. */
+#define	MBOOT_SCAN_SIZE		(32 * 1024 * 1024)
+
+/* multiboot header AOUT_KLUDGE flag. */
+#define	BB_MBOOT_AOUT_FLAG	(0x00010000)
+
+/* Extra header preceeding the payloads at the end of the bootblock. */
+typedef struct _bb_extra_header {
+	uint32_t	size;
+	uint32_t	checksum;
+} bb_header_ext_t;
+
+uint32_t compute_checksum(char *, uint32_t);
+bblk_einfo_t *find_einfo(char *);
+int find_multiboot(char *, uint32_t, uint32_t *);
+void add_einfo(char *, char *, bblk_hs_t *, uint32_t);
+int compare_bootblocks(char *, char *, char **);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _MBOOT_EXTRA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/installboot/Makefile	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,68 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# 
+
+PROG= installboot
+
+EINFO_SRC= ./../common/bblk_einfo.c
+UTILS_SRC =./../common/boot_utils.c
+EXTRA_SRC =./../common/mboot_extra.c
+
+OBJS= installboot.o bblk_einfo.o  boot_utils.o mboot_extra.o
+SRCS= installboot.c $(UTILS_SRC) $(EINFO_SRC) $(EXTRA_SRC)
+
+include ../Makefile.com
+
+CPPFLAGS += -I$(SRC)/uts/common
+
+LDLIBS += -lmd5
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
+LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+boot_utils.o:	$(UTILS_SRC)
+		$(COMPILE.c) -o $@ $(UTILS_SRC)
+
+mboot_extra.o:	$(EXTRA_SRC)
+		$(COMPILE.c) -o $@ $(EXTRA_SRC)
+
+bblk_einfo.o:	$(EINFO_SRC)
+		$(COMPILE.c) -o $@ $(EINFO_SRC)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+	$(RM) $(OBJS)
+
+lint:	lint_SRCS
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/installboot/installboot.c	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,949 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <locale.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/multiboot.h>
+#include <sys/sysmacros.h>
+
+#include "installboot.h"
+#include "./../common/bblk_einfo.h"
+#include "./../common/boot_utils.h"
+#include "./../common/mboot_extra.h"
+
+#ifndef	TEXT_DOMAIN
+#define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
+#endif
+
+/*
+ * SPARC bootblock installation:
+ *
+ * The bootblock resides in blocks 1 to 15 (disk label is at block 0).
+ * The ZFS boot block is larger than what will fit into these first 7.5K so we
+ * break it up and write the remaining portion into the ZFS provided boot block
+ * region at offset 512K. If versioning is requested, we add a multiboot
+ * header at the end of the bootblock, followed by the extra payload area and
+ * place the extended information structure within the latter.
+ */
+
+static boolean_t	force_update = B_FALSE;
+static boolean_t	do_getinfo = B_FALSE;
+static boolean_t	do_version = B_FALSE;
+static boolean_t	do_mirror_bblk = B_FALSE;
+static boolean_t	strip = B_FALSE;
+static boolean_t	verbose_dump = B_FALSE;
+
+static char		*update_str;
+static int		tgt_fs_type = TARGET_IS_UFS;
+char			mboot_scan[MBOOT_SCAN_SIZE];
+
+/* Function prototypes. */
+static int read_bootblock_from_file(char *, ib_data_t *data);
+static int read_bootblock_from_disk(int, ib_bootblock_t *);
+static void add_bootblock_einfo(ib_bootblock_t *, char *);
+static int prepare_bootblock(ib_data_t *, char *);
+static int write_zfs_bootblock(ib_data_t *);
+static int write_bootblock(ib_data_t *);
+static int open_device(ib_device_t *);
+static int init_device(ib_device_t *, char *);
+static void cleanup_device(ib_device_t *);
+static int commit_to_disk(ib_data_t *, char *);
+static int handle_install(char *, char **);
+static int handle_getinfo(char *, char **);
+static int handle_mirror(char *, char **);
+static boolean_t is_update_necessary(ib_data_t *, char *);
+static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
+static void usage(char *);
+
+static int
+read_bootblock_from_file(char *file, ib_data_t *data)
+{
+	ib_device_t	*device = &data->device;
+	ib_bootblock_t	*bblock = &data->bootblock;
+	struct stat 	sb;
+	uint32_t	buf_size;
+	int		fd = -1;
+	int		retval = BC_ERROR;
+
+	assert(data != NULL);
+	assert(file != NULL);
+
+	fd = open(file, O_RDONLY);
+	if (fd == -1) {
+		BOOT_DEBUG("Error opening %s\n", file);
+		perror("open");
+		goto out;
+	}
+
+	if (fstat(fd, &sb) == -1) {
+		BOOT_DEBUG("Error getting information (stat) about %s", file);
+		perror("stat");
+		goto outfd;
+	}
+
+	bblock->file_size = sb.st_size;
+	BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size);
+
+	/* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
+	if (!is_zfs(device->type)) {
+		buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE);
+		if (buf_size > BBLK_DATA_RSVD_SIZE) {
+			BOOT_DEBUG("boot block size is bigger than allowed\n");
+			goto outfd;
+		}
+	} else {
+		buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE,
+		    SECTOR_SIZE);
+		if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) {
+			(void) fprintf(stderr, gettext("WARNING, bootblock size"
+			    " does not allow to place extended versioning "
+			    "information.. skipping\n"));
+			do_version = B_FALSE;
+		}
+	}
+
+	bblock->buf_size = buf_size;
+	BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
+	    bblock->buf_size);
+
+	bblock->buf = malloc(buf_size);
+	if (bblock->buf == NULL) {
+		perror(gettext("Memory allocation failure"));
+		goto outbuf;
+	}
+	bblock->file = bblock->buf;
+
+	if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) {
+		BOOT_DEBUG("Read from %s failed\n", file);
+		perror("read");
+		goto outfd;
+	}
+
+	/* If not on ZFS, we are done here. */
+	if (!is_zfs(device->type)) {
+		BOOT_DEBUG("Reading of the bootblock done\n");
+		retval = BC_SUCCESS;
+		goto outfd;
+	}
+	/*
+	 * We place the multiboot header right after the file, followed by
+	 * the extended information structure.
+	 */
+	bblock->mboot = (multiboot_header_t *)(bblock->file +
+	    P2ROUNDUP(bblock->file_size, 8));
+	bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
+	BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
+	    bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size);
+
+	(void) close(fd);
+	return (BC_SUCCESS);
+
+outbuf:
+	(void) free(bblock->buf);
+	bblock->buf = NULL;
+outfd:
+	(void) close(fd);
+out:
+	return (retval);
+}
+
+static int
+read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock)
+{
+	char			*dest;
+	uint32_t		size;
+	uint32_t		buf_size;
+	uint32_t		mboot_off;
+	multiboot_header_t	*mboot;
+
+	assert(bblock != NULL);
+	assert(dev_fd != -1);
+
+	/*
+	 * The ZFS bootblock is divided in two parts, but the fake multiboot
+	 * header can only be in the second part (the one contained in the ZFS
+	 * reserved area).
+	 */
+	if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
+	    BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
+		BOOT_DEBUG("Error reading ZFS reserved area\n");
+		perror("read");
+		return (BC_ERROR);
+	}
+
+	/* No multiboot means no chance of knowing bootblock size */
+	if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
+	    != BC_SUCCESS) {
+		BOOT_DEBUG("Unable to find multiboot header\n");
+		return (BC_NOEXTRA);
+	}
+	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
+
+	/*
+	 * Currently, the amount of space reserved for extra information
+	 * is "fixed". We may have to scan for the terminating extra payload
+	 * in the future.
+	 */
+	size = mboot->load_end_addr - mboot->load_addr;
+	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
+	bblock->file_size = size;
+
+	bblock->buf = malloc(buf_size);
+	if (bblock->buf == NULL) {
+		BOOT_DEBUG("Unable to allocate enough memory to read"
+		    " the extra bootblock from the disk\n");
+		perror(gettext("Memory allocation failure"));
+		return (BC_ERROR);
+	}
+	bblock->buf_size = buf_size;
+
+	dest = bblock->buf;
+	size = BBLK_DATA_RSVD_SIZE;
+
+	if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) {
+		BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
+		    size);
+		(void) free(bblock->buf);
+		bblock->buf = NULL;
+		return (BC_ERROR);
+	}
+
+	dest += BBLK_DATA_RSVD_SIZE;
+	size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
+
+	if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
+		BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
+		(void) free(bblock->buf);
+		bblock->buf = NULL;
+		return (BC_ERROR);
+	}
+
+	/* Update pointers. */
+	bblock->file = bblock->buf;
+	bblock->mboot_off = mboot_off;
+	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off
+	    + BBLK_DATA_RSVD_SIZE);
+	bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
+	return (BC_SUCCESS);
+}
+
+static boolean_t
+is_update_necessary(ib_data_t *data, char *updt_str)
+{
+	bblk_einfo_t	*einfo;
+	bblk_hs_t	bblock_hs;
+	ib_bootblock_t	bblock_disk;
+	ib_bootblock_t	*bblock_file = &data->bootblock;
+	ib_device_t	*device = &data->device;
+	int		dev_fd = device->fd;
+
+	assert(data != NULL);
+	assert(device->fd != -1);
+
+	/* Nothing to do if we are not updating a ZFS bootblock. */
+	if (!is_zfs(device->type))
+		return (B_TRUE);
+
+	bzero(&bblock_disk, sizeof (ib_bootblock_t));
+
+	if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
+		BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
+		return (B_TRUE);
+	}
+
+	einfo = find_einfo(bblock_disk.extra);
+	if (einfo == NULL) {
+		BOOT_DEBUG("No extended information available\n");
+		return (B_TRUE);
+	}
+
+	if (!do_version || updt_str == NULL) {
+		(void) fprintf(stdout, "WARNING: target device %s has a "
+		    "versioned bootblock that is going to be overwritten by a "
+		    "non versioned one\n", device->path);
+		return (B_TRUE);
+	}
+
+	if (force_update) {
+		BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
+		return (B_TRUE);
+	}
+
+	BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
+
+	bblock_hs.src_buf = (unsigned char *)bblock_file->file;
+	bblock_hs.src_size = bblock_file->file_size;
+
+	return (einfo_should_update(einfo, &bblock_hs, updt_str));
+}
+
+static void
+add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
+{
+	bblk_hs_t	hs;
+	uint32_t	avail_space;
+
+	assert(bblock != NULL);
+
+	if (updt_str == NULL) {
+		BOOT_DEBUG("WARNING: no update string passed to "
+		    "add_bootblock_einfo()\n");
+		return;
+	}
+
+	/* Fill bootblock hashing source information. */
+	hs.src_buf = (unsigned char *)bblock->file;
+	hs.src_size = bblock->file_size;
+	/* How much space for the extended information structure? */
+	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
+	/* Place the extended information structure. */
+	add_einfo(bblock->extra, updt_str, &hs, avail_space);
+}
+
+
+static int
+prepare_bootblock(ib_data_t *data, char *updt_str)
+{
+	ib_device_t		*device = &data->device;
+	ib_bootblock_t		*bblock = &data->bootblock;
+	multiboot_header_t	*mboot;
+
+	assert(data != NULL);
+
+	/* Nothing to do if we are not on ZFS. */
+	if (!is_zfs(device->type))
+		return (BC_SUCCESS);
+
+	/*
+	 * Write the fake multiboot structure followed by the extra information
+	 * data. Both mboot and extra pointers have already been filled up to
+	 * point to the right location in the buffer. We prepare the fake
+	 * multiboot regardless if versioning was requested or not because
+	 * we need it for mirroring support.
+	 */
+	assert(bblock->mboot != NULL);
+	assert(bblock->extra != NULL);
+
+	mboot = bblock->mboot;
+
+	mboot->magic = MB_HEADER_MAGIC;
+	mboot->flags = MB_HEADER_FLAGS_64;
+	mboot->checksum = -(mboot->flags + mboot->magic);
+	/*
+	 * Flags include the AOUT_KLUDGE and we use the extra members to specify
+	 * the size of the bootblock.
+	 */
+	mboot->header_addr = bblock->mboot_off;
+	mboot->load_addr = 0;
+	mboot->load_end_addr = bblock->file_size;
+
+	/*
+	 * Now that we have the mboot header in place, we can add the extended
+	 * versioning information. Since the multiboot header has been placed
+	 * after the file image, the hashing will still reflect the one of the
+	 * file on the disk.
+	 */
+	if (do_version)
+		add_bootblock_einfo(bblock, updt_str);
+
+	return (BC_SUCCESS);
+}
+
+static int
+write_zfs_bootblock(ib_data_t *data)
+{
+	ib_device_t	*device = &data->device;
+	ib_bootblock_t	*bblock = &data->bootblock;
+	char		*bufptr;
+	uint32_t	size;
+
+	assert(data != NULL);
+	assert(device->fd != -1);
+
+	/*
+	 * In the ZFS case we actually perform two different steps:
+	 * - write the first 15 blocks of the bootblock to the reserved disk
+	 *   blocks.
+	 * - write the remaining blocks in the ZFS reserved area at offset
+	 *   512K.
+	 */
+	bufptr = bblock->buf;
+	size = BBLK_DATA_RSVD_SIZE;
+
+	if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
+		BOOT_DEBUG("Error writing first 15 blocks of %s\n",
+		    device->path);
+		perror("write");
+		return (BC_ERROR);
+	}
+
+	bufptr += BBLK_DATA_RSVD_SIZE;
+	size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
+
+	if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
+	    != BC_SUCCESS) {
+		BOOT_DEBUG("Error writing the second part of ZFS bootblock "
+		    "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
+		return (BC_ERROR);
+	}
+	return (BC_SUCCESS);
+}
+
+static int
+write_bootblock(ib_data_t *data)
+{
+	ib_device_t	*device = &data->device;
+	ib_bootblock_t	*bblock = &data->bootblock;
+	int		ret;
+
+	assert(data != NULL);
+
+	/*
+	 * If we are on UFS or HSFS we simply write out to the reserved
+	 * blocks (1 to 15) the boot block.
+	 */
+	if (!is_zfs(device->type)) {
+		if (write_out(device->fd, bblock->buf, bblock->buf_size,
+		    SECTOR_SIZE) != BC_SUCCESS) {
+			BOOT_DEBUG("Error writing bootblock to %s\n",
+			    device->path);
+			return (BC_ERROR);
+		} else {
+			return (BC_SUCCESS);
+		}
+	} else {
+		ret = write_zfs_bootblock(data);
+		return (ret);
+	}
+}
+
+static int
+open_device(ib_device_t *device)
+{
+	struct stat	statbuf;
+
+	device->fd = open(device->path, O_RDWR);
+	if (device->fd == -1) {
+		BOOT_DEBUG("Unable to open %s\n", device->path);
+		perror("open");
+		return (BC_ERROR);
+	}
+
+	if (fstat(device->fd, &statbuf) != 0) {
+		BOOT_DEBUG("Unable to stat %s\n", device->path);
+		perror("stat");
+		(void) close(device->fd);
+		return (BC_ERROR);
+	}
+
+	if (S_ISCHR(statbuf.st_mode) == 0) {
+		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
+		    device->path);
+		return (BC_ERROR);
+	}
+
+	return (BC_SUCCESS);
+}
+
+static int
+init_device(ib_device_t *device, char *path)
+{
+	bzero(device, sizeof (*device));
+	device->fd = -1;
+
+	device->path = strdup(path);
+	if (path == NULL) {
+		perror(gettext("Memory allocation failure"));
+		return (BC_ERROR);
+	}
+
+	device->type = tgt_fs_type;
+	if (open_device(device) != BC_SUCCESS)
+		return (BC_ERROR);
+
+	return (BC_SUCCESS);
+}
+
+static void
+cleanup_device(ib_device_t *device)
+{
+	free(device->path);
+	bzero(device, sizeof (*device));
+
+	if (device->fd != -1)
+		(void) close(device->fd);
+}
+
+static void
+cleanup_bootblock(ib_bootblock_t *bblock)
+{
+	free(bblock->buf);
+	bzero(bblock, sizeof (ib_bootblock_t));
+}
+
+/*
+ * Propagate the bootblock on the source disk to the destination disk and
+ * version it with 'updt_str' in the process. Since we cannot trust any data
+ * on the attaching disk, we do not perform any specific check on a potential
+ * target extended information structure and we just blindly update.
+ */
+static int
+propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
+{
+	ib_bootblock_t	*src_bblock = &src->bootblock;
+	ib_bootblock_t	*dest_bblock = &dest->bootblock;
+	uint32_t	buf_size;
+
+	assert(src != NULL);
+	assert(dest != NULL);
+
+	cleanup_bootblock(dest_bblock);
+
+	if (updt_str != NULL) {
+		do_version = B_TRUE;
+	} else {
+		do_version = B_FALSE;
+	}
+
+	buf_size = src_bblock->file_size + SECTOR_SIZE;
+
+	dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+	dest_bblock->buf = malloc(dest_bblock->buf_size);
+	if (dest_bblock->buf == NULL) {
+		perror(gettext("Memory Allocation Failure"));
+		return (BC_ERROR);
+	}
+	dest_bblock->file = dest_bblock->buf;
+	dest_bblock->file_size = src_bblock->file_size;
+	(void) memcpy(dest_bblock->file, src_bblock->file,
+	    dest_bblock->file_size);
+
+	dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
+	    P2ROUNDUP(dest_bblock->file_size, 8));
+	dest_bblock->extra = (char *)dest_bblock->mboot +
+	    sizeof (multiboot_header_t);
+
+	(void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
+	    src->device.path, dest->device.path);
+
+	return (commit_to_disk(dest, updt_str));
+}
+
+static int
+commit_to_disk(ib_data_t *data, char *update_str)
+{
+	assert(data != NULL);
+
+	if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error updating the bootblock "
+		    "image\n"));
+		return (BC_ERROR);
+	}
+
+	if (write_bootblock(data) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error writing bootblock to "
+		    "disk\n"));
+		return (BC_ERROR);
+	}
+
+	return (BC_SUCCESS);
+}
+
+
+/*
+ * Install a new bootblock on the given device. handle_install() expects argv
+ * to contain 2 parameters (the target device path and the path to the
+ * bootblock.
+ *
+ * Returns:	BC_SUCCESS - if the installation is successful
+ *		BC_ERROR   - if the installation failed
+ *		BC_NOUPDT  - if no installation was performed because the
+ *		             version currently installed is more recent than the
+ *			     supplied one.
+ *
+ */
+static int
+handle_install(char *progname, char **argv)
+{
+	ib_data_t	install_data;
+	char		*bootblock = NULL;
+	char		*device_path = NULL;
+	int		ret = BC_ERROR;
+
+	bootblock = strdup(argv[0]);
+	device_path = strdup(argv[1]);
+
+	if (!device_path || !bootblock) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+
+	BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
+	    bootblock);
+	bzero(&install_data, sizeof (ib_data_t));
+
+	if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to open device %s\n"),
+		    device_path);
+		goto out;
+	}
+
+	if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error reading %s\n"),
+		    bootblock);
+		goto out_dev;
+	}
+	/* Versioning is only supported for the ZFS bootblock. */
+	if (do_version && !is_zfs(install_data.device.type)) {
+		(void) fprintf(stderr, gettext("Versioning is only supported on"
+		    " ZFS... skipping.\n"));
+		do_version = B_FALSE;
+	}
+
+	/*
+	 * is_update_necessary() will take care of checking if versioning and/or
+	 * forcing the update have been specified. It will also emit a warning
+	 * if a non-versioned update is attempted over a versioned bootblock.
+	 */
+	if (!is_update_necessary(&install_data, update_str)) {
+		(void) fprintf(stderr, gettext("bootblock version installed "
+		    "on %s is more recent or identical\n"
+		    "Use -F to override or install without the -u option\n"),
+		    device_path);
+		ret = BC_NOUPDT;
+		goto out_dev;
+	}
+
+	BOOT_DEBUG("Ready to commit to disk\n");
+	ret = commit_to_disk(&install_data, update_str);
+
+out_dev:
+	cleanup_device(&install_data.device);
+out:
+	free(bootblock);
+	free(device_path);
+	return (ret);
+}
+
+/*
+ * Retrieves from a device the extended information (einfo) associated to the
+ * installed bootblock.
+ * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
+ * Returns:
+ *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
+ *	  - BC_ERROR (on error)
+ *        - BC_NOEINFO (no extended information available)
+ */
+static int
+handle_getinfo(char *progname, char **argv)
+{
+
+	ib_data_t	data;
+	ib_bootblock_t	*bblock = &data.bootblock;
+	ib_device_t	*device = &data.device;
+	bblk_einfo_t	*einfo;
+	uint8_t		flags = 0;
+	uint32_t	size;
+	char		*device_path;
+	int		retval = BC_ERROR;
+	int		ret;
+
+	device_path = strdup(argv[0]);
+	if (!device_path) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+
+	bzero(&data, sizeof (ib_data_t));
+	BOOT_DEBUG("device path: %s\n", device_path);
+
+	if (init_device(device, device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information from %s\n"), device_path);
+		goto out_dev;
+	}
+
+	if (!is_zfs(device->type)) {
+		(void) fprintf(stderr, gettext("Versioning only supported on "
+		    "ZFS\n"));
+		goto out_dev;
+	}
+
+	ret = read_bootblock_from_disk(device->fd, bblock);
+	if (ret == BC_ERROR) {
+		(void) fprintf(stderr, gettext("Error reading bootblock from "
+		    "%s\n"), device_path);
+		goto out_dev;
+	}
+
+	if (ret == BC_NOEXTRA) {
+		BOOT_DEBUG("No multiboot header found on %s, unable "
+		    "to locate extra information area (old/non versioned "
+		    "bootblock?) \n", device_path);
+		(void) fprintf(stderr, gettext("No extended information "
+		    "found\n"));
+		retval = BC_NOEINFO;
+		goto out_dev;
+	}
+
+	einfo = find_einfo(bblock->extra);
+	if (einfo == NULL) {
+		retval = BC_NOEINFO;
+		(void) fprintf(stderr, gettext("No extended information "
+		    "found\n"));
+		goto out_dev;
+	}
+
+	/* Print the extended information. */
+	if (strip)
+		flags |= EINFO_EASY_PARSE;
+	if (verbose_dump)
+		flags |= EINFO_PRINT_HEADER;
+
+	size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
+	    sizeof (multiboot_header_t);
+	print_einfo(flags, einfo, size);
+	retval = BC_SUCCESS;
+
+out_dev:
+	cleanup_device(&data.device);
+out:
+	free(device_path);
+	return (retval);
+
+}
+
+/*
+ * Attempt to mirror (propagate) the current bootblock over the attaching disk.
+ *
+ * Returns:
+ *	- BC_SUCCESS (a successful propagation happened)
+ *	- BC_ERROR (an error occurred)
+ *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
+ *			there is no multiboot information)
+ */
+static int
+handle_mirror(char *progname, char **argv)
+{
+	ib_data_t	curr_data;
+	ib_data_t	attach_data;
+	ib_device_t	*curr_device = &curr_data.device;
+	ib_device_t	*attach_device = &attach_data.device;
+	ib_bootblock_t	*bblock_curr = &curr_data.bootblock;
+	ib_bootblock_t	*bblock_attach = &attach_data.bootblock;
+	bblk_einfo_t	*einfo_curr = NULL;
+	char		*curr_device_path;
+	char		*attach_device_path;
+	char		*updt_str = NULL;
+	int		retval = BC_ERROR;
+	int		ret;
+
+	curr_device_path = strdup(argv[0]);
+	attach_device_path = strdup(argv[1]);
+
+	if (!curr_device_path || !attach_device_path) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
+	    " %s\n", curr_device_path, attach_device_path);
+
+	bzero(&curr_data, sizeof (ib_data_t));
+	bzero(&attach_data, sizeof (ib_data_t));
+
+	if (tgt_fs_type != TARGET_IS_ZFS) {
+		(void) fprintf(stderr, gettext("Mirroring is only supported on "
+		    "ZFS\n"));
+		return (BC_ERROR);
+	}
+
+	if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information from %s (current device)\n"),
+		    curr_device_path);
+		goto out_currdev;
+	}
+
+	if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information from %s (attaching device)\n"),
+		    attach_device_path);
+		goto out_devs;
+	}
+
+	ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
+	if (ret == BC_ERROR) {
+		BOOT_DEBUG("Error reading bootblock from %s\n",
+		    curr_device->path);
+		retval = BC_ERROR;
+		goto out_devs;
+	}
+
+	if (ret == BC_NOEXTRA) {
+		BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
+		    " the bootblock\n", curr_device->path);
+		retval = BC_NOEXTRA;
+		goto out_devs;
+	}
+
+	einfo_curr = find_einfo(bblock_curr->extra);
+	if (einfo_curr != NULL)
+		updt_str = einfo_get_string(einfo_curr);
+
+	retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
+	cleanup_bootblock(bblock_curr);
+	cleanup_bootblock(bblock_attach);
+out_devs:
+	cleanup_device(attach_device);
+out_currdev:
+	cleanup_device(curr_device);
+out:
+	free(curr_device_path);
+	free(attach_device_path);
+	return (retval);
+}
+
+#define	USAGE_STRING	"Usage: %s [-h|-f|-F fstype|-u verstr] bootblk "       \
+			"raw-device\n"					       \
+			"\t%s [-e|-V] -i -F zfs raw-device\n"	               \
+			"\t%s -M -F zfs raw-device attach-raw-device\n"        \
+			"\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
+
+#define	CANON_USAGE_STR	gettext(USAGE_STRING)
+
+static void
+usage(char *progname)
+{
+	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
+}
+
+int
+main(int argc, char **argv)
+{
+	int	opt;
+	int	params = 2;
+	int	ret;
+	char	*progname;
+	char	**handle_args;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
+		switch (opt) {
+		case 'F':
+			if (strcmp(optarg, "ufs") == 0) {
+				tgt_fs_type = TARGET_IS_UFS;
+			} else if (strcmp(optarg, "hsfs") == 0) {
+				tgt_fs_type = TARGET_IS_HSFS;
+			} else if (strcmp(optarg, "zfs") == 0) {
+				tgt_fs_type = TARGET_IS_ZFS;
+			} else {
+				(void) fprintf(stderr, gettext("Wrong "
+				    "filesystem specified\n\n"));
+				usage(argv[0]);
+				exit(BC_ERROR);
+			}
+			break;
+		case 'e':
+			strip = B_TRUE;
+			break;
+		case 'f':
+			force_update = B_TRUE;
+			break;
+		case 'V':
+			verbose_dump = B_TRUE;
+			break;
+		case 'i':
+			do_getinfo = B_TRUE;
+			params = 1;
+			break;
+		case 'u':
+			do_version = B_TRUE;
+
+			update_str = malloc(strlen(optarg) + 1);
+			if (update_str == NULL) {
+				perror(gettext("Memory allocation failure"));
+				exit(BC_ERROR);
+			}
+			(void) strlcpy(update_str, optarg, strlen(optarg) + 1);
+			break;
+		case 'M':
+			do_mirror_bblk = B_TRUE;
+			break;
+		case 'h':
+			usage(argv[0]);
+			exit(BC_SUCCESS);
+			break;
+		case 'd':
+			boot_debug = B_TRUE;
+			break;
+		case 'n':
+			nowrite = B_TRUE;
+			break;
+		default:
+			/* fall through to process non-optional args */
+			break;
+		}
+	}
+
+	/* check arguments */
+	if (argc != optind + params) {
+		usage(argv[0]);
+		exit(BC_ERROR);
+	}
+	progname = argv[0];
+	handle_args = argv + optind;
+
+	/* check options. */
+	if (do_getinfo && do_mirror_bblk) {
+		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
+		    "specified at the same time\n"));
+		usage(progname);
+		exit(BC_ERROR);
+	}
+
+	if (nowrite)
+		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
+		    " be written to disk.\n"));
+
+	if (do_getinfo) {
+		ret = handle_getinfo(progname, handle_args);
+	} else if (do_mirror_bblk) {
+		ret = handle_mirror(progname, handle_args);
+	} else {
+		ret = handle_install(progname, handle_args);
+	}
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/installboot/installboot.h	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,71 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_INSTALLBOOT_H
+#define	_INSTALLBOOT_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/multiboot.h>
+#include <sys/types.h>
+
+enum ib_fs_types {
+	TARGET_IS_UFS = 0,
+	TARGET_IS_HSFS,
+	TARGET_IS_ZFS
+};
+
+typedef struct _ib_device {
+	char	*path;
+	int	fd;
+	uint8_t	type;
+} ib_device_t;
+
+typedef struct _ib_bootblock {
+	char			*buf;
+	char			*file;
+	char			*extra;
+	multiboot_header_t	*mboot;
+	uint32_t		mboot_off;
+	uint32_t		buf_size;
+	uint32_t		file_size;
+} ib_bootblock_t;
+
+typedef struct _ib_data {
+	ib_device_t	device;
+	ib_bootblock_t	bootblock;
+} ib_data_t;
+
+#define	is_zfs(type)	(type == TARGET_IS_ZFS)
+
+#define	BBLK_DATA_RSVD_SIZE	(15 * SECTOR_SIZE)
+#define	BBLK_ZFS_EXTRA_OFF	(SECTOR_SIZE * 1024)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _INSTALLBOOT_H */
--- a/usr/src/cmd/boot/installgrub/Makefile	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/cmd/boot/installgrub/Makefile	Wed Jul 28 15:51:57 2010 -0700
@@ -18,17 +18,19 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
+# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 # 
 
 PROG= installgrub
 
 PCFS_SRC= $(SRC)/common/fs/pcfs.c
+EINFO_SRC= ./../common/bblk_einfo.c
+UTILS_SRC =./../common/boot_utils.c
+EXTRA_SRC =./../common/mboot_extra.c
 
-OBJS= installgrub.o floppy.o pcfs.o
-SRCS= installgrub.c floppy.c $(PCFS_SRC)
+OBJS= installgrub.o pcfs_glue.o pcfs.o bblk_einfo.o boot_utils.o mboot_extra.o
+SRCS= installgrub.c pcfs_glue.c $(PCFS_SRC) $(UTILS_SRC) $(EINFO_SRC) \
+	$(EXTRA_SRC)
 SBINLINKS= $(PROG)
 
 include ../Makefile.com
@@ -49,6 +51,9 @@
 	-erroff=E_FUNC_RET_MAYBE_IGNORED2 \
 	-xerroff=E_NAME_DEF_NOT_USED2
 
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
 .KEEP_STATE:
 
 all: $(PROG)
@@ -62,6 +67,15 @@
 pcfs.o:	$(PCFS_SRC)
 	$(COMPILE.c) -o $@ $(PCFS_SRC)
 
+boot_utils.o:	$(UTILS_SRC)
+		$(COMPILE.c) -o $@ $(UTILS_SRC)
+
+mboot_extra.o:	$(EXTRA_SRC)
+		$(COMPILE.c) -o $@ $(EXTRA_SRC)
+
+bblk_einfo.o:	$(EINFO_SRC)
+		$(COMPILE.c) -o $@ $(EINFO_SRC)
+
 install: all $(ROOTSBINPROG) .WAIT $(ROOTUSRSBINLINKS)
 
 clean:
--- a/usr/src/cmd/boot/installgrub/floppy.c	Wed Jul 28 17:47:31 2010 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-/*
- * 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 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/bootvfs.h>
-#include <sys/filep.h>
-
-#include <libintl.h>
-#include <locale.h>
-#include "message.h"
-
-/*
- * This file is glue layer to pcfs module in usr/src/common/fs/pcfs.c.
- * It's main functionality is to get the stage file blocklist. It's
- * used for installing grub on floppy and Solaris boot partition.
- */
-extern struct boot_fs_ops bpcfs_ops;
-struct boot_fs_ops *bfs_ops;
-struct boot_fs_ops *bfs_tab[] = {&bpcfs_ops, NULL};
-static int dev_fd;
-int bootrd_debug = 0;
-
-#define	DEV_BSIZE	512
-#define	MAX_CHUNK	64
-
-static unsigned int *blocklist;
-
-/* diskread_callback is set in filesytem module (pcfs.c) */
-int (*diskread_callback)(int, int);
-int (*fileread_callback)(int, int);
-
-static int
-add_stage2_block(int blocknum, int nblk)
-{
-	static int i = -2;
-
-	if (i >= 0 && (blocklist[i] + blocklist[i + 1] == blocknum)) {
-		blocklist[i + 1] += nblk;
-		return (0);
-	}
-
-	i += 2;
-	if (i >= DEV_BSIZE / 8) {
-		fprintf(stderr, PCFS_FRAGMENTED);
-		exit(-1);
-	}
-	blocklist[i] = blocknum;
-	blocklist[i + 1] = nblk;
-	return (0);
-}
-
-/*
- * This one reads the ramdisk. If fi_memp is set, we copy the
- * ramdisk content to the designated buffer. Otherwise, we
- * do a "cached" read (set fi_memp to the actual ramdisk buffer).
- */
-int
-diskread(fileid_t *filep)
-{
-	int ret;
-	uint_t blocknum, diskloc;
-
-	blocknum = filep->fi_blocknum;
-
-	if (diskread_callback) {
-		diskread_callback(blocknum, filep->fi_count / DEV_BSIZE);
-		return (0);
-	}
-
-	diskloc = blocknum * DEV_BSIZE;
-	if (filep->fi_memp == NULL) {
-		filep->fi_memp = malloc(filep->fi_count);
-	}
-	if (filep->fi_memp == NULL) {
-		fprintf(stderr, OUT_OF_MEMORY);
-		return (-1);
-	}
-
-	ret = pread(dev_fd, filep->fi_memp, filep->fi_count, diskloc);
-	if (ret < 0)
-		perror("diskread: pread");
-	return (ret >= 0 ? 0 : -1);
-}
-
-void *
-bkmem_alloc(size_t s)
-{
-	return (malloc(s));
-}
-
-/*ARGSUSED*/
-void
-bkmem_free(void *p, size_t s)
-{
-	free(p);
-}
-
-static int
-mountroot(char *name)
-{
-	int i;
-
-	/* try ops in bfs_tab and return the first successful one */
-	for (i = 0; bfs_tab[i] != NULL; i++) {
-		bfs_ops = bfs_tab[i];
-		if (BRD_MOUNTROOT(bfs_ops, name) == 0)
-			return (0);
-	}
-	return (-1);
-}
-
-static int
-unmountroot()
-{
-	return (BRD_UNMOUNTROOT(bfs_ops));
-}
-
-static int
-floppy_open(const char *filename, int flags)
-{
-	return (BRD_OPEN(bfs_ops, (char *)filename, flags));
-}
-
-static int
-floppy_close(int fd)
-{
-	return (BRD_CLOSE(bfs_ops, fd));
-}
-
-static ssize_t
-floppy_read(int fd, void *buf, size_t size)
-{
-	return (BRD_READ(bfs_ops, fd, buf, size));
-}
-
-static off_t
-floppy_lseek(int fd, off_t addr, int whence)
-{
-	return (BRD_SEEK(bfs_ops, fd, addr, whence));
-}
-
-/*
- * Get the blocklist for stage2
- */
-int
-read_stage2_blocklist(int device_fd, unsigned int *blkbuf)
-{
-	int i, fd, stage2_block;
-	char buf[DEV_BSIZE];
-	ssize_t size;
-
-	dev_fd = device_fd;
-	if (mountroot("dummy") != 0) {
-		fprintf(stderr, MOUNT_FAIL_PCFS);
-		return (-1);
-	}
-
-	if ((fd = floppy_open("/boot/grub/stage2", 0)) == -1) {
-		fprintf(stderr, OPEN_FAIL_PCFS);
-		return (-1);
-	}
-
-	if (bootrd_debug)
-		(void) printf("start reading stage2:\n");
-	stage2_block = 0;
-	blocklist = blkbuf;
-	fileread_callback = add_stage2_block;
-	for (;;) {
-		size = floppy_read(fd, buf, DEV_BSIZE);
-		if (size != DEV_BSIZE)
-			break;
-		stage2_block++;
-	}
-	fileread_callback = NULL;
-	(void) floppy_close(fd);
-
-	if (bootrd_debug) {
-		(void) printf("last block size = %d\n", size);
-		for (i = 0; blocklist[i] != 0; i += 2) {
-			(void) printf("sectors: %d-%d\n",
-			    blocklist[i],
-			    blocklist[i] + blocklist[i + 1] - 1);
-		}
-		(void) printf("total blocks in stage 2: %d\n", stage2_block);
-	}
-
-	(void) unmountroot();
-	return (0);
-}
--- a/usr/src/cmd/boot/installgrub/installgrub.c	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/cmd/boot/installgrub/installgrub.c	Wed Jul 28 15:51:57 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -28,122 +27,154 @@
 #include <libgen.h>
 #include <malloc.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <strings.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <libfdisk.h>
+#include <stdarg.h>
+#include <assert.h>
+
 #include <sys/mount.h>
 #include <sys/mnttab.h>
 #include <sys/dktp/fdisk.h>
 #include <sys/dkio.h>
 #include <sys/vtoc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/multiboot.h>
+#include <sys/sysmacros.h>
 
-#include <libintl.h>
-#include <locale.h>
 #include "message.h"
-#include <errno.h>
-#include <libfdisk.h>
-#include <md5.h>
+#include "installgrub.h"
+#include "./../common/bblk_einfo.h"
+#include "./../common/boot_utils.h"
+#include "./../common/mboot_extra.h"
 
 #ifndef	TEXT_DOMAIN
 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
 #endif
 
-#define	SECTOR_SIZE	0x200
-#define	HASH_SIZE	0x10
-#define	VERSION_SIZE	0x50
-#define	STAGE2_MEMADDR	0x8000	/* loading addr of stage2 */
+/*
+ * Variables to track installgrub desired mode of operation.
+ * 'nowrite' and 'boot_debug' come from boot_common.h.
+ */
+static boolean_t write_mbr = B_FALSE;
+static boolean_t force_mbr = B_FALSE;
+static boolean_t force_update = B_FALSE;
+static boolean_t do_getinfo = B_FALSE;
+static boolean_t do_version = B_FALSE;
+static boolean_t do_mirror_bblk = B_FALSE;
+static boolean_t strip = B_FALSE;
+static boolean_t verbose_dump = B_FALSE;
 
-#define	STAGE1_BPB_OFFSET	0x3
-#define	STAGE1_BPB_SIZE		0x3B
-#define	STAGE1_BOOT_DRIVE	0x40
-#define	STAGE1_FORCE_LBA	0x41
-#define	STAGE1_STAGE2_ADDRESS	0x42
-#define	STAGE1_STAGE2_SECTOR	0x44
-#define	STAGE1_STAGE2_SEGMENT	0x48
+/* Installing the bootblock is the default operation. */
+static boolean_t do_install = B_TRUE;
+
+/* Versioning string, if present. */
+static char *update_str;
 
-#define	STAGE2_BLOCKLIST	(SECTOR_SIZE - 0x8)
-#define	STAGE2_INSTALLPART	(SECTOR_SIZE + 0x8)
-#define	STAGE2_FORCE_LBA	(SECTOR_SIZE + 0x11)
-#define	STAGE2_VER_STRING	(SECTOR_SIZE + 0x12)
-#define	STAGE2_SIGN_OFFSET	(SECTOR_SIZE + 0x60)
-#define	STAGE2_PKG_VERSION	(SECTOR_SIZE + 0x70)
-#define	STAGE2_BLKOFF		50	/* offset from start of fdisk part */
-
-static char extended_sig[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB"
-"\xBB\xBB\xBB\xBB";
+/*
+ * Temporary buffer to store the first 32K of data looking for a multiboot
+ * signature.
+ */
+char	mboot_scan[MBOOT_SCAN_SIZE];
 
-static int nowrite = 0;
-static int write_mboot = 0;
-static int force_mboot = 0;
-static int getinfo = 0;
-static int do_version = 0;
-static int is_floppy = 0;
-static int is_bootpar = 0;
-static int strip = 0;
-static int stage2_fd;
-static int partition, slice = 0xff;
-static char *device_p0;
-static uint32_t stage2_first_sector, stage2_second_sector;
-
-
-static char bpb_sect[SECTOR_SIZE];
-static char boot_sect[SECTOR_SIZE];
-static char stage1_buffer[SECTOR_SIZE];
-static char stage2_buffer[2 * SECTOR_SIZE];
-static char signature[HASH_SIZE];
-static char verstring[VERSION_SIZE];
-static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)];
-
-static int open_device(char *);
-static void read_bpb_sect(int);
-static void read_boot_sect(char *);
-static void write_boot_sect(char *);
-static void read_stage1_stage2(char *, char *);
-static void modify_and_write_stage1(int);
-static void modify_and_write_stage2(int);
-static unsigned int get_start_sector(int);
-static void copy_stage2(int, char *);
-static char *get_raw_partition(char *);
+/* Function prototypes. */
+static void check_options(char *);
+static int handle_install(char *, char **);
+static int handle_mirror(char *, char **);
+static int handle_getinfo(char *, char **);
+static int commit_to_disk(ig_data_t *, char *);
+static int init_device(ig_device_t *, char *path);
+static void cleanup_device(ig_device_t *);
+static void cleanup_stage2(ig_stage2_t *);
+static int get_start_sector(ig_device_t *);
+static int get_disk_fd(ig_device_t *device);
+static int get_raw_partition_fd(ig_device_t *);
+static char *get_raw_partition_path(ig_device_t *);
+static boolean_t gather_stage2_from_dev(ig_data_t *);
+static int propagate_bootblock(ig_data_t *, ig_data_t *, char *);
+static int find_x86_bootpar(struct mboot *, int *, uint32_t *);
+static int copy_stage2_to_pcfs(ig_data_t *);
+static int write_stage2(ig_data_t *);
+static int write_stage1(ig_data_t *);
 static void usage(char *);
-static void print_info();
-static int read_stage2_info(int);
-static void check_extended_support();
+static int read_stage1_from_file(char *, ig_data_t *);
+static int read_stage2_from_file(char *, ig_data_t *);
+static int read_stage1_from_disk(int, char *);
+static int read_stage2_from_disk(int, ig_stage2_t *);
+static int prepare_stage1(ig_data_t *);
+static int prepare_stage2(ig_data_t *, char *);
+static void prepare_fake_multiboot(ig_stage2_t *);
+static void add_stage2_einfo(ig_stage2_t *, char *updt_str);
+static boolean_t is_update_necessary(ig_data_t *, char *);
 
 extern int read_stage2_blocklist(int, unsigned int *);
 
 int
 main(int argc, char *argv[])
 {
-	int dev_fd, opt, params = 3;
-	char *stage1, *stage2, *device;
+	int	opt;
+	int	params = 3;
+	int	ret;
+	char	**handle_args;
+	char	*progname;
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
 
-	while ((opt = getopt(argc, argv, "fmneis:")) != EOF) {
+	/*
+	 * retro-compatibility: installing the bootblock is the default
+	 * and there is no switch for it.
+	 */
+	do_install = B_TRUE;
+
+	while ((opt = getopt(argc, argv, "dVMFfmneiu:")) != EOF) {
 		switch (opt) {
 		case 'm':
-			write_mboot = 1;
+			write_mbr = B_TRUE;
 			break;
 		case 'n':
-			nowrite = 1;
+			nowrite = B_TRUE;
 			break;
 		case 'f':
-			force_mboot = 1;
+			force_mbr = B_TRUE;
 			break;
 		case 'i':
-			getinfo = 1;
+			do_getinfo = B_TRUE;
+			do_install = B_FALSE;
 			params = 1;
 			break;
+		case 'V':
+			verbose_dump = B_TRUE;
+			break;
+		case 'd':
+			boot_debug = B_TRUE;
+			break;
+		case 'F':
+			force_update = B_TRUE;
+			break;
 		case 'e':
-			strip = 1;
+			strip = B_TRUE;
+			break;
+		case 'M':
+			do_mirror_bblk = B_TRUE;
+			do_install = B_FALSE;
+			params = 2;
 			break;
-		case 's':
-			do_version = 1;
-			(void) snprintf(verstring, sizeof (verstring), "%s",
-			    optarg);
+		case 'u':
+			do_version = B_TRUE;
+
+			update_str = malloc(strlen(optarg) + 1);
+			if (update_str == NULL) {
+				(void) fprintf(stderr, gettext("Unable to "
+				    "allocate memory\n"));
+				exit(BC_ERROR);
+			}
+			(void) strlcpy(update_str, optarg, strlen(optarg) + 1);
 			break;
 		default:
 			/* fall through to process non-optional args */
@@ -154,121 +185,543 @@
 	/* check arguments */
 	if (argc != optind + params) {
 		usage(argv[0]);
-	}
-
-	if (nowrite) {
-		(void) fprintf(stdout, DRY_RUN);
+		exit(BC_ERROR);
 	}
 
-	if (params == 1) {
-		device = strdup(argv[optind]);
-		if (!device) {
-			usage(argv[0]);
+	/*
+	 * clean up options (and bail out if an unrecoverable combination is
+	 * requested.
+	 */
+	progname = argv[0];
+	check_options(progname);
+	handle_args = argv + optind;
+
+	if (nowrite)
+		(void) fprintf(stdout, DRY_RUN);
+
+	if (do_getinfo) {
+		ret = handle_getinfo(progname, handle_args);
+	} else if (do_mirror_bblk) {
+		ret = handle_mirror(progname, handle_args);
+	} else {
+		ret = handle_install(progname, handle_args);
+	}
+	return (ret);
+}
+
+#define	MEANINGLESS_OPT	gettext("%s specified but meaningless, ignoring\n")
+static void
+check_options(char *progname)
+{
+	if (do_getinfo && do_mirror_bblk) {
+		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
+		    "specified at the same time\n"));
+		usage(progname);
+		exit(BC_ERROR);
+	}
+
+	if (do_mirror_bblk) {
+		/*
+		 * -u and -F may actually reflect a user intent that is not
+		 * correct with this command (mirror can be interpreted
+		 * "similar" to install. Emit a message and continue.
+		 * -e and -V have no meaning, be quiet here and only report the
+		 * incongruence if a debug output is requested.
+		 */
+		if (do_version) {
+			(void) fprintf(stderr, MEANINGLESS_OPT, "-u");
+			do_version = B_FALSE;
 		}
-	} else if (params == 3) {
-		stage1 = strdup(argv[optind]);
-		stage2 = strdup(argv[optind + 1]);
-		device = strdup(argv[optind + 2]);
-
-		if (!stage1 || !stage2 || !device) {
-			usage(argv[0]);
+		if (force_update) {
+			(void) fprintf(stderr, MEANINGLESS_OPT, "-F");
+			force_update = B_FALSE;
+		}
+		if (strip || verbose_dump) {
+			BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
+			strip = B_FALSE;
+			verbose_dump = B_FALSE;
 		}
 	}
 
-	/* open and check device type */
-	dev_fd = open_device(device);
+	if (do_getinfo) {
+		if (write_mbr || force_mbr || do_version || force_update) {
+			BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
+			write_mbr = force_mbr = do_version = B_FALSE;
+			force_update = B_FALSE;
+		}
+	}
+}
+
+/*
+ * Install a new stage1/stage2 pair on the specified device. handle_install()
+ * expects argv to contain 3 parameters (the path to stage1, the path to stage2,
+ * the target device).
+ *
+ * Returns:	BC_SUCCESS - if the installation is successful
+ *		BC_ERROR   - if the installation failed
+ *		BC_NOUPDT  - if no installation was performed because the GRUB
+ *		             version currently installed is more recent than the
+ *			     supplied one.
+ *
+ */
+static int
+handle_install(char *progname, char **argv)
+{
+	ig_data_t	install_data;
+	char		*stage1_path = NULL;
+	char		*stage2_path = NULL;
+	char		*device_path = NULL;
+	int		ret = BC_ERROR;
 
-	if (getinfo) {
-		if (read_stage2_info(dev_fd) != 0) {
-			fprintf(stderr, "Unable to read extended information"
-			    " from %s\n", device);
-			exit(1);
-		}
-		print_info();
-		(void) free(device);
-		(void) close(dev_fd);
-		return (0);
+	stage1_path = strdup(argv[0]);
+	stage2_path = strdup(argv[1]);
+	device_path = strdup(argv[2]);
+
+	bzero(&install_data, sizeof (ig_data_t));
+
+	if (!stage1_path || !stage2_path || !device_path) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+
+	BOOT_DEBUG("stage1 path: %s, stage2 path: %s, device: %s\n",
+	    stage1_path, stage2_path, device_path);
+
+	if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information for %s\n"), device_path);
+		goto out;
+	}
+
+	/* read in stage1 and stage2. */
+	if (read_stage1_from_file(stage1_path, &install_data) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error opening %s\n"),
+		    stage1_path);
+		goto out_dev;
+	}
+
+	if (read_stage2_from_file(stage2_path, &install_data) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error opening %s\n"),
+		    stage2_path);
+		goto out_dev;
 	}
 
-	/* read in stage1 and stage2 into buffer */
-	read_stage1_stage2(stage1, stage2);
+	/* We do not support versioning on PCFS. */
+	if (is_bootpar(install_data.device.type) && do_version)
+		do_version = B_FALSE;
+
+	/*
+	 * is_update_necessary() will take care of checking if versioning and/or
+	 * forcing the update have been specified. It will also emit a warning
+	 * if a non-versioned update is attempted over a versioned bootblock.
+	 */
+	if (!is_update_necessary(&install_data, update_str)) {
+		(void) fprintf(stderr, gettext("GRUB version installed "
+		    "on %s is more recent or identical\n"
+		    "Use -F to override or install without the -u option\n"),
+		    device_path);
+		ret = BC_NOUPDT;
+		goto out_dev;
+	}
+	/*
+	 * We get here if:
+	 * - the installed GRUB version is older than the one about to be
+	 *   installed.
+	 * - no versioning string has been passed through the command line.
+	 * - a forced update is requested (-F).
+	 */
+	BOOT_DEBUG("Ready to commit to disk\n");
+	ret = commit_to_disk(&install_data, update_str);
+
+out_dev:
+	cleanup_device(&install_data.device);
+out:
+	free(stage1_path);
+	free(stage2_path);
+	free(device_path);
+	return (ret);
+}
+
+/*
+ * Retrieves from a device the extended information (einfo) associated to the
+ * installed stage2.
+ * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
+ * Returns:
+ *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
+ *	  - BC_ERROR (on error)
+ *        - BC_NOEINFO (no extended information available)
+ */
+static int
+handle_getinfo(char *progname, char **argv)
+{
+	ig_data_t	data;
+	ig_stage2_t	*stage2 = &data.stage2;
+	ig_device_t	*device = &data.device;
+	bblk_einfo_t	*einfo;
+	uint8_t		flags = 0;
+	uint32_t	size;
+	char		*device_path;
+	int		retval = BC_ERROR;
+	int		ret;
+
+	device_path = strdup(argv[0]);
+	if (!device_path) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+
+	bzero(&data, sizeof (ig_data_t));
+	BOOT_DEBUG("device path: %s\n", device_path);
+
+	if (init_device(device, device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information for %s\n"), device_path);
+		goto out_dev;
+	}
+
+	if (is_bootpar(device->type)) {
+		(void) fprintf(stderr, gettext("Versioning not supported on "
+		    "PCFS\n"));
+		goto out_dev;
+	}
 
-	/* check if stage2 supports extended versioning */
-	if (do_version)
-		check_extended_support(stage2);
+	ret = read_stage2_from_disk(device->part_fd, stage2);
+	if (ret == BC_ERROR) {
+		(void) fprintf(stderr, gettext("Error reading stage2 from "
+		    "%s\n"), device_path);
+		goto out_dev;
+	}
+
+	if (ret == BC_NOEXTRA) {
+		(void) fprintf(stdout, gettext("No multiboot header found on "
+		    "%s, unable to locate extra information area\n"),
+		    device_path);
+		retval = BC_NOEINFO;
+		goto out_dev;
+	}
+
+	einfo = find_einfo(stage2->extra);
+	if (einfo == NULL) {
+		retval = BC_NOEINFO;
+		(void) fprintf(stderr, gettext("No extended information "
+		    "found\n"));
+		goto out_dev;
+	}
+
+	/* Print the extended information. */
+	if (strip)
+		flags |= EINFO_EASY_PARSE;
+	if (verbose_dump)
+		flags |= EINFO_PRINT_HEADER;
+
+	size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
+	print_einfo(flags, einfo, size);
+	retval = BC_SUCCESS;
+
+out_dev:
+	cleanup_device(&data.device);
+out:
+	free(device_path);
+	return (retval);
+}
 
-	/* In the pcfs case, write a fresh stage2 */
-	if (is_floppy || is_bootpar) {
-		copy_stage2(dev_fd, device);
-		read_bpb_sect(dev_fd);
+/*
+ * Attempt to mirror (propagate) the current stage2 over the attaching disk.
+ *
+ * Returns:
+ *	- BC_SUCCESS (a successful propagation happened)
+ *	- BC_ERROR (an error occurred)
+ *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
+ *			there is no multiboot information)
+ */
+static int
+handle_mirror(char *progname, char **argv)
+{
+	ig_data_t	curr_data;
+	ig_data_t	attach_data;
+	ig_device_t	*curr_device = &curr_data.device;
+	ig_device_t	*attach_device = &attach_data.device;
+	ig_stage2_t	*stage2_curr = &curr_data.stage2;
+	ig_stage2_t	*stage2_attach = &attach_data.stage2;
+	bblk_einfo_t	*einfo_curr = NULL;
+	char		*curr_device_path;
+	char		*attach_device_path;
+	char		*updt_str = NULL;
+	int		retval = BC_ERROR;
+	int		ret;
+
+	curr_device_path = strdup(argv[0]);
+	attach_device_path = strdup(argv[1]);
+
+	if (!curr_device_path || !attach_device_path) {
+		(void) fprintf(stderr, gettext("Missing parameter"));
+		usage(progname);
+		goto out;
+	}
+	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
+	    " %s\n", curr_device_path, attach_device_path);
+
+	bzero(&curr_data, sizeof (ig_data_t));
+	bzero(&attach_data, sizeof (ig_data_t));
+
+	if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information for %s (current device)\n"), curr_device_path);
+		goto out_currdev;
 	}
 
-	/* read in boot sector */
-	if (!is_floppy)
-		read_boot_sect(device);
+	if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Unable to gather device "
+		    "information for %s (attaching device)\n"),
+		    attach_device_path);
+		goto out_devs;
+	}
+
+	if (is_bootpar(curr_device->type) || is_bootpar(attach_device->type)) {
+		(void) fprintf(stderr, gettext("boot block mirroring is not "
+		    "supported on PCFS\n"));
+		goto out_devs;
+	}
 
-	/* modify stage1 based on grub needs */
-	modify_and_write_stage1(dev_fd);
+	ret = read_stage2_from_disk(curr_device->part_fd, stage2_curr);
+	if (ret == BC_ERROR) {
+		BOOT_DEBUG("Error reading first stage2 blocks from %s\n",
+		    curr_device->path);
+		retval = BC_ERROR;
+		goto out_devs;
+	}
 
-	/* modify stage2 and write to media */
-	modify_and_write_stage2(dev_fd);
+	if (ret == BC_NOEXTRA) {
+		BOOT_DEBUG("No multiboot header found on %s, unable to grab "
+		    "stage2\n", curr_device->path);
+		retval = BC_NOEXTRA;
+		goto out_devs;
+	}
+
+	einfo_curr = find_einfo(stage2_curr->extra);
+	if (einfo_curr != NULL)
+		updt_str = einfo_get_string(einfo_curr);
+
+	write_mbr = B_TRUE;
+	force_mbr = B_TRUE;
+	retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
+	cleanup_stage2(stage2_curr);
+	cleanup_stage2(stage2_attach);
 
-	if (!is_floppy && write_mboot)
-		write_boot_sect(device);
+out_devs:
+	cleanup_device(attach_device);
+out_currdev:
+	cleanup_device(curr_device);
+out:
+	free(curr_device_path);
+	free(attach_device_path);
+	return (retval);
+}
 
-	(void) close(dev_fd);
-	free(device);
-	free(stage1);
-	free(stage2);
+static int
+commit_to_disk(ig_data_t *install, char *updt_str)
+{
+	assert(install != NULL);
+	/*
+	 * vanilla stage1 and stage2 need to be updated at runtime.
+	 * Update stage2 before stage1 because stage1 needs to know the first
+	 * sector stage2 will be written to.
+	 */
+	if (prepare_stage2(install, updt_str) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error building stage2\n"));
+		return (BC_ERROR);
+	}
+	if (prepare_stage1(install) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error building stage1\n"));
+		return (BC_ERROR);
+	}
 
-	return (0);
+	/* Write stage2 out to disk. */
+	if (write_stage2(install) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error writing stage2 to "
+		    "disk\n"));
+		return (BC_ERROR);
+	}
+
+	/* Write stage1 to disk and, if requested, to the MBR. */
+	if (write_stage1(install) != BC_SUCCESS) {
+		(void) fprintf(stderr, gettext("Error writing stage1 to "
+		    "disk\n"));
+		return (BC_ERROR);
+	}
+
+	return (BC_SUCCESS);
 }
 
-static unsigned int
-get_start_sector(int fd)
+/*
+ * Propagate the bootblock on the source disk to the destination disk and
+ * version it with 'updt_str' in the process. Since we cannot trust any data
+ * on the attaching disk, we do not perform any specific check on a potential
+ * target extended information structure and we just blindly update.
+ */
+static int
+propagate_bootblock(ig_data_t *source, ig_data_t *target, char *updt_str)
 {
-	static unsigned int start_sect = 0;
-	uint32_t secnum = 0, numsec = 0;
-	int i, pno, rval, log_part = 0;
-	struct mboot *mboot;
-	struct ipart *part;
-	ext_part_t *epp;
-	struct part_info dkpi;
-	struct extpart_info edkpi;
+	ig_device_t	*src_device = &source->device;
+	ig_device_t	*dest_device = &target->device;
+	ig_stage2_t	*src_stage2 = &source->stage2;
+	ig_stage2_t	*dest_stage2 = &target->stage2;
+	uint32_t	buf_size;
+	int		retval;
+
+	assert(source != NULL);
+	assert(target != NULL);
+
+	/* read in stage1 from the source disk. */
+	if (read_stage1_from_disk(src_device->part_fd, target->stage1_buf)
+	    != BC_SUCCESS)
+		return (BC_ERROR);
+
+	/* Prepare target stage2 for commit_to_disk. */
+	cleanup_stage2(dest_stage2);
+
+	if (updt_str != NULL)
+		do_version = B_TRUE;
+	else
+		do_version = B_FALSE;
+
+	buf_size = src_stage2->file_size + SECTOR_SIZE;
+
+	dest_stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+	dest_stage2->buf = malloc(dest_stage2->buf_size);
+	if (dest_stage2->buf == NULL) {
+		perror(gettext("Memory allocation failed"));
+		return (BC_ERROR);
+	}
+	dest_stage2->file = dest_stage2->buf;
+	dest_stage2->file_size = src_stage2->file_size;
+	memcpy(dest_stage2->file, src_stage2->file, dest_stage2->file_size);
+	dest_stage2->extra = dest_stage2->buf +
+	    P2ROUNDUP(dest_stage2->file_size, 8);
+
+	/* If we get down here we do have a mboot structure. */
+	assert(src_stage2->mboot);
+
+	dest_stage2->mboot_off = src_stage2->mboot_off;
+	dest_stage2->mboot = (multiboot_header_t *)(dest_stage2->buf +
+	    dest_stage2->mboot_off);
+
+	(void) fprintf(stdout, gettext("Propagating %s stage1/stage2 to %s\n"),
+	    src_device->path, dest_device->path);
+	retval = commit_to_disk(target, updt_str);
+
+	return (retval);
+}
+
+/*
+ * open the device and fill the various members of ig_device_t.
+ */
+static int
+init_device(ig_device_t *device, char *path)
+{
+	bzero(device, sizeof (*device));
+	device->part_fd = -1;
+	device->disk_fd = -1;
+	device->path_p0 = NULL;
+
+	device->path = strdup(path);
+	if (device->path == NULL) {
+		perror(gettext("Memory allocation failed"));
+		return (BC_ERROR);
+	}
 
-	if (start_sect)
-		return (start_sect);
+	if (strstr(device->path, "diskette")) {
+		(void) fprintf(stderr, gettext("installing GRUB to a floppy "
+		    "disk is no longer supported\n"));
+		return (BC_ERROR);
+	}
+
+	/* Detect if the target device is a pcfs partition. */
+	if (strstr(device->path, "p0:boot"))
+		device->type = IG_DEV_X86BOOTPAR;
+
+	if (get_disk_fd(device) != BC_SUCCESS)
+		return (BC_ERROR);
+
+	/* read in the device boot sector. */
+	if (read(device->disk_fd, device->boot_sector, SECTOR_SIZE)
+	    != SECTOR_SIZE) {
+		(void) fprintf(stderr, gettext("Error reading boot sector\n"));
+		perror("read");
+		return (BC_ERROR);
+	}
+
+	if (get_raw_partition_fd(device) != BC_SUCCESS)
+		return (BC_ERROR);
+
+	if (get_start_sector(device) != BC_SUCCESS)
+		return (BC_ERROR);
+
+	return (BC_SUCCESS);
+}
+
+static void
+cleanup_device(ig_device_t *device)
+{
+	if (device->path)
+		free(device->path);
+	if (device->path_p0)
+		free(device->path_p0);
 
-	mboot = (struct mboot *)boot_sect;
-	for (i = 0; i < FD_NUMPART; i++) {
-		part = (struct ipart *)mboot->parts + i;
-		if (is_bootpar) {
-			if (part->systid == 0xbe) {
-				start_sect = part->relsect;
-				partition = i;
-				goto found_part;
-			}
+	if (device->part_fd != -1)
+		(void) close(device->part_fd);
+	if (device->disk_fd != -1)
+		(void) close(device->disk_fd);
+
+	bzero(device, sizeof (ig_device_t));
+	device->part_fd = -1;
+	device->disk_fd = -1;
+}
+
+static void
+cleanup_stage2(ig_stage2_t *stage2)
+{
+	if (stage2->buf)
+		free(stage2->buf);
+	bzero(stage2, sizeof (ig_stage2_t));
+}
+
+static int
+get_start_sector(ig_device_t *device)
+{
+	uint32_t		secnum = 0, numsec = 0;
+	int			i, pno, rval, log_part = 0;
+	struct mboot		*mboot;
+	struct ipart		*part;
+	ext_part_t		*epp;
+	struct part_info	dkpi;
+	struct extpart_info	edkpi;
+
+	mboot = (struct mboot *)device->boot_sector;
+
+	if (is_bootpar(device->type)) {
+		if (find_x86_bootpar(mboot, &pno, &secnum) != BC_SUCCESS) {
+			(void) fprintf(stderr, NOBOOTPAR);
+			return (BC_ERROR);
+		} else {
+			device->start_sector = secnum;
+			device->partition = pno;
+			goto found_part;
 		}
 	}
 
 	/*
-	 * We will not support x86 boot partition on extended partitions
-	 */
-	if (is_bootpar) {
-		(void) fprintf(stderr, NOBOOTPAR);
-		exit(-1);
-	}
-
-	/*
-	 * Not an x86 boot partition. Search for Solaris fdisk partition
+	 * Search for Solaris fdisk partition
 	 * Get the solaris partition information from the device
 	 * and compare the offset of S2 with offset of solaris partition
 	 * from fdisk partition table.
 	 */
-	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
-		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
+	if (ioctl(device->part_fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
+		if (ioctl(device->part_fd, DKIOCPARTINFO, &dkpi) < 0) {
 			(void) fprintf(stderr, PART_FAIL);
-			exit(-1);
+			return (BC_ERROR);
 		} else {
 			edkpi.p_start = dkpi.p_start;
 		}
@@ -279,7 +732,7 @@
 
 		if (part->relsect == 0) {
 			(void) fprintf(stderr, BAD_PART, i);
-			exit(-1);
+			return (BC_ERROR);
 		}
 
 		if (edkpi.p_start >= part->relsect &&
@@ -292,7 +745,7 @@
 	if (i == FD_NUMPART) {
 		/* No solaris fdisk partitions (primary or logical) */
 		(void) fprintf(stderr, NOSOLPAR);
-		exit(-1);
+		return (BC_ERROR);
 	}
 
 	/*
@@ -300,8 +753,8 @@
 	 * Handle the simple case first: Solaris in a primary partition
 	 */
 	if (!fdisk_is_dos_extended(part->systid)) {
-		start_sect = part->relsect;
-		partition = i;
+		device->start_sector = part->relsect;
+		device->partition = i;
 		goto found_part;
 	}
 
@@ -309,7 +762,7 @@
 	 * Solaris in a logical partition. Find that partition in the
 	 * extended part.
 	 */
-	if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK))
+	if ((rval = libfdisk_init(&epp, device->path_p0, NULL, FDISK_READ_DISK))
 	    != FDISK_SUCCESS) {
 		switch (rval) {
 			/*
@@ -320,23 +773,19 @@
 			case FDISK_ENOLOGDRIVE:
 			case FDISK_EBADMAGIC:
 				(void) fprintf(stderr, NOSOLPAR);
-				exit(-1);
-				/*NOTREACHED*/
+				return (BC_ERROR);
 			case FDISK_ENOVGEOM:
 				(void) fprintf(stderr, NO_VIRT_GEOM);
-				exit(1);
-				break;
+				return (BC_ERROR);
 			case FDISK_ENOPGEOM:
 				(void) fprintf(stderr, NO_PHYS_GEOM);
-				exit(1);
-				break;
+				return (BC_ERROR);
 			case FDISK_ENOLGEOM:
 				(void) fprintf(stderr, NO_LABEL_GEOM);
-				exit(1);
-				break;
+				return (BC_ERROR);
 			default:
 				(void) fprintf(stderr, LIBFDISK_INIT_FAIL);
-				exit(1);
+				return (BC_ERROR);
 				break;
 		}
 	}
@@ -346,21 +795,21 @@
 	if (rval != FDISK_SUCCESS) {
 		/* No solaris logical partition */
 		(void) fprintf(stderr, NOSOLPAR);
-		exit(-1);
+		return (BC_ERROR);
 	}
 
-	start_sect = secnum;
-	partition = pno - 1;
+	device->start_sector = secnum;
+	device->partition = pno - 1;
 	log_part = 1;
 
 found_part:
 	/* get confirmation for -m */
-	if (write_mboot && !force_mboot) {
+	if (write_mbr && !force_mbr) {
 		(void) fprintf(stdout, MBOOT_PROMPT);
 		if (getchar() != 'y') {
-			write_mboot = 0;
+			write_mbr = 0;
 			(void) fprintf(stdout, MBOOT_NOT_UPDATED);
-			exit(-1);
+			return (BC_ERROR);
 		}
 	}
 
@@ -368,318 +817,463 @@
 	 * Currently if Solaris is in an extended partition we need to
 	 * write GRUB to the MBR. Check for this.
 	 */
-	if (log_part && !write_mboot) {
-		write_mboot = 1;
+	if (log_part && !write_mbr) {
+		(void) fprintf(stdout, gettext("Installing Solaris on an "
+		    "extended partition... forcing MBR update\n"));
+		write_mbr = 1;
 	}
 
 	/*
 	 * warn, if Solaris in primary partition and GRUB not in MBR and
 	 * partition is not active
 	 */
-	if (!log_part && part->bootid != 128 && !write_mboot) {
-		(void) fprintf(stdout, SOLPAR_INACTIVE, partition + 1);
+	if (!log_part && part->bootid != 128 && !write_mbr) {
+		(void) fprintf(stdout, SOLPAR_INACTIVE, device->partition + 1);
+	}
+
+	return (BC_SUCCESS);
+}
+
+static int
+get_disk_fd(ig_device_t *device)
+{
+	int	i;
+	char	save[2];
+	char	*end = NULL;
+
+	assert(device != NULL);
+	assert(device->path != NULL);
+
+	if (is_bootpar(device->type)) {
+		end = strstr(device->path, "p0:boot");
+		/* tested at the start of init_device() */
+		assert(end != NULL);
+		/* chop off :boot */
+		save[0] = end[2];
+		end[2] = '\0';
+	} else {
+		i = strlen(device->path);
+		save[0] = device->path[i - 2];
+		save[1] = device->path[i - 1];
+		device->path[i - 2] = 'p';
+		device->path[i - 1] = '0';
+	}
+
+	if (nowrite)
+		device->disk_fd = open(device->path, O_RDONLY);
+	else
+		device->disk_fd = open(device->path, O_RDWR);
+
+	device->path_p0 = strdup(device->path);
+	if (device->path_p0 == NULL) {
+		perror("strdup");
+		return (BC_ERROR);
+	}
+
+	if (is_bootpar(device->type)) {
+		end[2] = save[0];
+	} else {
+		device->path[i - 2] = save[0];
+		device->path[i - 1] = save[1];
+	}
+
+	if (device->disk_fd == -1) {
+		perror("open");
+		return (BC_ERROR);
 	}
 
-	return (start_sect);
+	return (BC_SUCCESS);
+}
+
+static void
+prepare_fake_multiboot(ig_stage2_t *stage2)
+{
+	multiboot_header_t	*mboot;
+
+	assert(stage2 != NULL);
+	assert(stage2->mboot != NULL);
+	assert(stage2->buf != NULL);
+
+	mboot = stage2->mboot;
+
+	/*
+	 * Currently we expect find_multiboot() to have located a multiboot
+	 * header with the AOUT kludge flag set.
+	 */
+	assert(mboot->flags & BB_MBOOT_AOUT_FLAG);
+
+	/* Insert the information necessary to locate stage2. */
+	mboot->header_addr = stage2->mboot_off;
+	mboot->load_addr = 0;
+	mboot->load_end_addr = stage2->file_size;
+}
+
+static void
+add_stage2_einfo(ig_stage2_t *stage2, char *updt_str)
+{
+	bblk_hs_t	hs;
+	uint32_t	avail_space;
+
+	assert(stage2 != NULL);
+
+	/* Fill bootblock hashing source information. */
+	hs.src_buf = (unsigned char *)stage2->file;
+	hs.src_size = stage2->file_size;
+	/* How much space for the extended information structure? */
+	avail_space = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
+	add_einfo(stage2->extra, updt_str, &hs, avail_space);
 }
 
+
+static int
+write_stage2(ig_data_t *install)
+{
+	ig_device_t		*device = &install->device;
+	ig_stage2_t		*stage2 = &install->stage2;
+	off_t			offset;
+
+	assert(install != NULL);
+
+	if (is_bootpar(device->type)) {
+		/*
+		 * stage2 is already on the filesystem, we only need to update
+		 * the first two blocks (that we have modified during
+		 * prepare_stage2())
+		 */
+		if (write_out(device->part_fd, stage2->file, SECTOR_SIZE,
+		    stage2->pcfs_first_sectors[0] * SECTOR_SIZE)
+		    != BC_SUCCESS ||
+		    write_out(device->part_fd, stage2->file + SECTOR_SIZE,
+		    SECTOR_SIZE, stage2->pcfs_first_sectors[1] * SECTOR_SIZE)
+		    != BC_SUCCESS) {
+			(void) fprintf(stderr, WRITE_FAIL_STAGE2);
+			return (BC_ERROR);
+		}
+		(void) fprintf(stdout, WRITE_STAGE2_PCFS);
+		return (BC_SUCCESS);
+	}
+
+	/*
+	 * For disk, write stage2 starting at STAGE2_BLKOFF sector.
+	 * Note that we use stage2->buf rather than stage2->file, because we
+	 * may have extended information after the latter.
+	 */
+	offset = STAGE2_BLKOFF * SECTOR_SIZE;
+	if (write_out(device->part_fd, stage2->buf, stage2->buf_size,
+	    offset) != BC_SUCCESS) {
+		perror("write");
+		return (BC_ERROR);
+	}
+
+	/* Simulate the "old" installgrub output. */
+	(void) fprintf(stdout, WRITE_STAGE2_DISK, device->partition,
+	    (stage2->buf_size / SECTOR_SIZE) + 1, STAGE2_BLKOFF,
+	    stage2->first_sector);
+
+	return (BC_SUCCESS);
+}
+
+static int
+write_stage1(ig_data_t *install)
+{
+	ig_device_t	*device = &install->device;
+
+	assert(install != NULL);
+
+	if (write_out(device->part_fd, install->stage1_buf,
+	    sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
+		(void) fprintf(stdout, WRITE_FAIL_PBOOT);
+		perror("write");
+		return (BC_ERROR);
+	}
+
+	/* Simulate "old" installgrub output. */
+	(void) fprintf(stdout, WRITE_PBOOT, device->partition,
+	    device->start_sector);
+
+	if (write_mbr) {
+		if (write_out(device->disk_fd, install->stage1_buf,
+		    sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
+			(void) fprintf(stdout, WRITE_FAIL_BOOTSEC);
+			perror("write");
+			return (BC_ERROR);
+		}
+		/* Simulate "old" installgrub output. */
+		(void) fprintf(stdout, WRITE_MBOOT);
+	}
+
+	return (BC_SUCCESS);
+}
+
+#define	USAGE_STRING	"%s [-m|-f|-n|-F|-u verstr] stage1 stage2 device\n"    \
+			"%s -M [-n] device1 device2\n"			       \
+			"%s [-V|-e] -i device\n"			       \
+
+#define	CANON_USAGE_STR	gettext(USAGE_STRING)
+
 static void
 usage(char *progname)
 {
-	(void) fprintf(stderr, USAGE, basename(progname));
-	exit(-1);
-}
-
-static int
-open_device(char *device)
-{
-	int dev_fd;
-	struct stat stat;
-	char *raw_part;
-
-	is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) &&
-	    strncmp(device, "/dev/dsk", strlen("/dev/dsk"));
-
-	/* handle boot partition specification */
-	if (!is_floppy && strstr(device, "p0:boot")) {
-		is_bootpar = 1;
-	}
-
-	raw_part = get_raw_partition(device);
-
-	if (nowrite)
-		dev_fd = open(raw_part, O_RDONLY);
-	else
-		dev_fd = open(raw_part, O_RDWR);
-
-	if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) {
-		(void) fprintf(stderr, OPEN_FAIL, raw_part);
-		exit(-1);
-	}
-	if (S_ISCHR(stat.st_mode) == 0) {
-		(void) fprintf(stderr, NOT_RAW_DEVICE, raw_part);
-		exit(-1);
-	}
-
-	return (dev_fd);
-}
-
-static void
-read_stage1_stage2(char *stage1, char *stage2)
-{
-	int fd;
-
-	/* read the stage1 file from filesystem */
-	fd = open(stage1, O_RDONLY);
-	if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
-		(void) fprintf(stderr, READ_FAIL_STAGE1, stage1);
-		exit(-1);
-	}
-	(void) close(fd);
-
-	/* read first two blocks of stage 2 from filesystem */
-	stage2_fd = open(stage2, O_RDONLY);
-	if (stage2_fd == -1 ||
-	    read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE)
-	    != 2 * SECTOR_SIZE) {
-		(void) fprintf(stderr, READ_FAIL_STAGE2, stage2);
-		exit(-1);
-	}
-	/* leave the stage2 file open for later */
-}
-
-static void
-read_bpb_sect(int dev_fd)
-{
-	if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) {
-		(void) fprintf(stderr, READ_FAIL_BPB);
-		exit(-1);
-	}
-}
-
-static void
-read_boot_sect(char *device)
-{
-	static int read_mbr = 0;
-	int i, fd;
-	char save[2];
-
-	if (read_mbr)
-		return;
-	read_mbr = 1;
-
-	/* get the whole disk (p0) */
-	i = strlen(device);
-	save[0] = device[i - 2];
-	save[1] = device[i - 1];
-	device[i - 2] = 'p';
-	device[i - 1] = '0';
-
-	device_p0 = strdup(device);
-	fd = open(device, O_RDONLY);
-	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
-		(void) fprintf(stderr, READ_FAIL_MBR, device);
-		if (fd == -1)
-			perror("open");
-		else
-			perror("read");
-		exit(-1);
-	}
-	(void) close(fd);
-	device[i - 2] = save[0];
-	device[i - 1] = save[1];
-}
-
-static void
-write_boot_sect(char *device)
-{
-	int fd, len;
-	char *raw, *end;
-	struct stat stat;
-
-	/* make a copy and chop off ":boot" */
-	raw = strdup(device);
-	end = strstr(raw, "p0:boot");
-	if (end)
-		end[2] = 0;
-
-	/* open p0 (whole disk) */
-	len = strlen(raw);
-	raw[len - 2] = 'p';
-	raw[len - 1] = '0';
-	fd = open(raw, O_WRONLY);
-	if (fd == -1 || fstat(fd, &stat) != 0) {
-		(void) fprintf(stderr, OPEN_FAIL, raw);
-		exit(-1);
-	}
-	if (!nowrite &&
-	    pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
-		(void) fprintf(stderr, WRITE_FAIL_BOOTSEC);
-		exit(-1);
-	}
-	(void) fprintf(stdout, WRITE_MBOOT);
-	(void) close(fd);
-}
-
-static void
-modify_and_write_stage1(int dev_fd)
-{
-	if (is_floppy) {
-		stage2_first_sector = blocklist[0];
-		/* copy bios parameter block (for fat fs) */
-		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
-		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
-	} else if (is_bootpar) {
-		stage2_first_sector = get_start_sector(dev_fd) + blocklist[0];
-		/* copy bios parameter block (for fat fs) and MBR */
-		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
-		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
-		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
-		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
-	} else {
-		stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF;
-		/* copy MBR to stage1 in case of overwriting MBR sector */
-		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
-		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
-	}
-
-	/* modify default stage1 file generated by GRUB */
-	*((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR))
-	    = stage2_first_sector;
-	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS))
-	    = STAGE2_MEMADDR;
-	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT))
-	    = STAGE2_MEMADDR >> 4;
-
-	/*
-	 * XXX the default grub distribution also:
-	 * - Copy the possible MBR/extended part table
-	 * - Set the boot drive of stage1
-	 */
-
-	/* write stage1/pboot to 1st sector */
-	if (!nowrite &&
-	    pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
-		(void) fprintf(stderr, WRITE_FAIL_PBOOT);
-		exit(-1);
-	}
-
-	if (is_floppy) {
-		(void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY);
-	} else {
-		(void) fprintf(stdout, WRITE_PBOOT,
-		    partition, get_start_sector(dev_fd));
-	}
-}
-
-static void check_extended_support(char *stage2)
-{
-	char	*cmp = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
-
-	if ((*cmp++ != '\xEE') && memcmp(cmp, extended_sig, HASH_SIZE) != 0) {
-		fprintf(stderr, "%s does not support extended versioning\n",
-		    stage2);
-		do_version = 0;
-	}
-}
-
-
-static void print_info()
-{
-	int	i;
-
-	if (strip) {
-		fprintf(stdout, "%s\n", verstring);
-	} else {
-		fprintf(stdout, "Grub extended version information : %s\n",
-		    verstring);
-		fprintf(stdout, "Grub stage2 (MD5) signature : ");
-	}
-
-	for (i = 0; i < HASH_SIZE; i++)
-		fprintf(stdout, "%02x", (unsigned char)signature[i]);
-
-	fprintf(stdout, "\n");
-}
-
-static int
-read_stage2_info(int dev_fd)
-{
-	int 	ret;
-	int	first_offset, second_offset;
-	char	*sign;
-
-	if (is_floppy || is_bootpar) {
-
-		ret = pread(dev_fd, stage1_buffer, SECTOR_SIZE, 0);
-		if (ret != SECTOR_SIZE) {
-			perror("Error reading stage1 sector");
-			return (1);
-		}
-
-		first_offset = *((ulong_t *)(stage1_buffer +
-		    STAGE1_STAGE2_SECTOR));
-
-		/* Start reading in the first sector of stage 2 */
-
-		ret = pread(dev_fd, stage2_buffer, SECTOR_SIZE, first_offset *
-		    SECTOR_SIZE);
-		if (ret != SECTOR_SIZE) {
-			perror("Error reading stage2 first sector");
-			return (1);
-		}
-
-		/* From the block list section grab stage2 second sector */
-
-		second_offset = *((ulong_t *)(stage2_buffer +
-		    STAGE2_BLOCKLIST));
-
-		ret = pread(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
-		    second_offset * SECTOR_SIZE);
-		if (ret != SECTOR_SIZE) {
-			perror("Error reading stage2 second sector");
-			return (1);
-		}
-	} else {
-		ret = pread(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
-		    STAGE2_BLKOFF * SECTOR_SIZE);
-		if (ret != 2 * SECTOR_SIZE) {
-			perror("Error reading stage2 sectors");
-			return (1);
-		}
-	}
-
-	sign = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
-	if (*sign++ != '\xEE')
-		return (1);
-	(void) memcpy(signature, sign, HASH_SIZE);
-	sign = stage2_buffer + STAGE2_PKG_VERSION;
-	(void) strncpy(verstring, sign, VERSION_SIZE);
-	return (0);
+	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
 }
 
 
 static int
-compute_and_write_md5hash(char *dest)
+read_stage1_from_file(char *path, ig_data_t *dest)
 {
+	int	fd;
+
+	assert(dest);
+
+	/* read the stage1 file from filesystem */
+	fd = open(path, O_RDONLY);
+	if (fd == -1 ||
+	    read(fd, dest->stage1_buf, SECTOR_SIZE) != SECTOR_SIZE) {
+		(void) fprintf(stderr, READ_FAIL_STAGE1, path);
+		return (BC_ERROR);
+	}
+	(void) close(fd);
+	return (BC_SUCCESS);
+}
+
+static int
+read_stage2_from_file(char *path, ig_data_t *dest)
+{
+	int		fd;
 	struct stat	sb;
-	char		*buffer;
+	ig_stage2_t	*stage2 = &dest->stage2;
+	ig_device_t	*device = &dest->device;
+	uint32_t	buf_size;
+
+	assert(dest);
+	assert(stage2->buf == NULL);
+
+	fd = open(path, O_RDONLY);
+	if (fstat(fd, &sb) == -1) {
+		perror("fstat");
+		goto out;
+	}
+
+	stage2->file_size = sb.st_size;
+
+	if (!is_bootpar(device->type)) {
+		/*
+		 * buffer size needs to account for stage2 plus the extra
+		 * versioning information at the end of it. We reserve one
+		 * extra sector (plus we round up to the next sector boundary).
+		 */
+		buf_size = stage2->file_size + SECTOR_SIZE;
+	} else {
+		/* In the PCFS case we only need to read in stage2. */
+		buf_size = stage2->file_size;
+	}
+
+	stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+
+	BOOT_DEBUG("stage2 buffer size = %d (%d sectors)\n", stage2->buf_size,
+	    stage2->buf_size / SECTOR_SIZE);
+
+	stage2->buf = malloc(stage2->buf_size);
+	if (stage2->buf == NULL) {
+		perror(gettext("Memory allocation failed"));
+		goto out_fd;
+	}
+
+	stage2->file = stage2->buf;
+
+	/*
+	 * Extra information (e.g. the versioning structure) is placed at the
+	 * end of stage2, aligned on a 8-byte boundary.
+	 */
+	if (!(is_bootpar(device->type)))
+		stage2->extra = stage2->file + P2ROUNDUP(stage2->file_size, 8);
+
+	if (lseek(fd, 0, SEEK_SET) == -1) {
+		perror("lseek");
+		goto out_alloc;
+	}
 
-	if (fstat(stage2_fd, &sb) == -1)
-		return (-1);
+	if (read(fd, stage2->file, stage2->file_size) < 0) {
+		perror(gettext("unable to read stage2"));
+		goto out_alloc;
+	}
+
+	(void) close(fd);
+	return (BC_SUCCESS);
+
+out_alloc:
+	free(stage2->buf);
+	stage2->buf = NULL;
+out_fd:
+	(void) close(fd);
+out:
+	return (BC_ERROR);
+}
+
+static int
+prepare_stage1(ig_data_t *install)
+{
+	ig_device_t	*device = &install->device;
+
+	assert(install != NULL);
+
+	/* If PCFS add the BIOS Parameter Block. */
+	if (is_bootpar(device->type)) {
+		char	bpb_sect[SECTOR_SIZE];
+
+		if (pread(device->part_fd, bpb_sect, SECTOR_SIZE, 0)
+		    != SECTOR_SIZE) {
+			(void) fprintf(stderr, READ_FAIL_BPB);
+			return (BC_ERROR);
+		}
+		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
+		    install->stage1_buf + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
+	}
+
+	/* copy MBR to stage1 in case of overwriting MBR sector. */
+	bcopy(device->boot_sector + BOOTSZ, install->stage1_buf + BOOTSZ,
+	    SECTOR_SIZE - BOOTSZ);
+	/* modify default stage1 file generated by GRUB. */
+	*((unsigned char *)(install->stage1_buf + STAGE1_FORCE_LBA)) = 1;
+	*((ulong_t *)(install->stage1_buf + STAGE1_STAGE2_SECTOR))
+	    = install->stage2.first_sector;
+	*((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_ADDRESS))
+	    = STAGE2_MEMADDR;
+	*((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_SEGMENT))
+	    = STAGE2_MEMADDR >> 4;
+
+	return (BC_SUCCESS);
+}
 
-	buffer = malloc(sb.st_size);
-	if (buffer == NULL)
-		return (-1);
+/*
+ * Grab stage1 from the specified device file descriptor.
+ */
+static int
+read_stage1_from_disk(int dev_fd, char *stage1_buf)
+{
+	assert(stage1_buf != NULL);
+
+	if (read_in(dev_fd, stage1_buf, SECTOR_SIZE, 0) != BC_SUCCESS) {
+		perror(gettext("Unable to read stage1 from disk"));
+		return (BC_ERROR);
+	}
+	return (BC_SUCCESS);
+}
+
+static int
+read_stage2_from_disk(int dev_fd, ig_stage2_t *stage2)
+{
+	uint32_t		size;
+	uint32_t		buf_size;
+	uint32_t		mboot_off;
+	multiboot_header_t	*mboot;
+
+	assert(stage2 != NULL);
+	assert(dev_fd != -1);
+
+	if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
+	    STAGE2_BLKOFF * SECTOR_SIZE) != BC_SUCCESS) {
+		perror(gettext("Error reading stage2 sectors"));
+		return (BC_ERROR);
+	}
+
+	/* No multiboot means no chance of knowing stage2 size */
+	if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
+	    != BC_SUCCESS) {
+		BOOT_DEBUG("Unable to find multiboot header\n");
+		return (BC_NOEXTRA);
+	}
+	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
+
+	/*
+	 * Unfilled mboot values mean an older version of installgrub installed
+	 * the stage2. Again we have no chance of knowing stage2 size.
+	 */
+	if (mboot->load_end_addr == 0 ||
+	    mboot->load_end_addr < mboot->load_addr)
+		return (BC_NOEXTRA);
+
+	/*
+	 * Currently, the amount of space reserved for extra information
+	 * is "fixed". We may have to scan for the terminating extra payload
+	 * in the future.
+	 */
+	size = mboot->load_end_addr - mboot->load_addr;
+	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
+
+	stage2->buf = malloc(buf_size);
+	if (stage2->buf == NULL) {
+		perror(gettext("Memory allocation failed"));
+		return (BC_ERROR);
+	}
+	stage2->buf_size = buf_size;
 
-	if (lseek(stage2_fd, 0, SEEK_SET) == -1)
-		return (-1);
-	if (read(stage2_fd, buffer, sb.st_size) < 0)
-		return (-1);
+	if (read_in(dev_fd, stage2->buf, buf_size, STAGE2_BLKOFF *
+	    SECTOR_SIZE) != BC_SUCCESS) {
+		perror("read");
+		free(stage2->buf);
+		return (BC_ERROR);
+	}
+
+	/* Update pointers. */
+	stage2->file = stage2->buf;
+	stage2->file_size = size;
+	stage2->mboot_off = mboot_off;
+	stage2->mboot = (multiboot_header_t *)(stage2->buf + stage2->mboot_off);
+	stage2->extra = stage2->buf + P2ROUNDUP(stage2->file_size, 8);
+
+	return (BC_SUCCESS);
+}
+
+static boolean_t
+is_update_necessary(ig_data_t *data, char *updt_str)
+{
+	bblk_einfo_t	*einfo;
+	bblk_hs_t	stage2_hs;
+	ig_stage2_t	stage2_disk;
+	ig_stage2_t	*stage2_file = &data->stage2;
+	ig_device_t	*device = &data->device;
+	int		dev_fd = device->part_fd;
+
+	assert(data != NULL);
+	assert(device->part_fd != -1);
+
+	bzero(&stage2_disk, sizeof (ig_stage2_t));
 
-	md5_calc(dest, buffer, sb.st_size);
-	free(buffer);
-	return (0);
+	/* Gather stage2 (if present) from the target device. */
+	if (read_stage2_from_disk(dev_fd, &stage2_disk) != BC_SUCCESS) {
+		BOOT_DEBUG("Unable to read stage2 from %s\n", device->path);
+		BOOT_DEBUG("No multiboot wrapped stage2 on %s\n", device->path);
+		return (B_TRUE);
+	}
+
+	/*
+	 * Look for the extended information structure in the extra payload
+	 * area.
+	 */
+	einfo = find_einfo(stage2_disk.extra);
+	if (einfo == NULL) {
+		BOOT_DEBUG("No extended information available\n");
+		return (B_TRUE);
+	}
+
+	if (!do_version || updt_str == NULL) {
+		(void) fprintf(stdout, "WARNING: target device %s has a "
+		    "versioned stage2 that is going to be overwritten by a non "
+		    "versioned one\n", device->path);
+		return (B_TRUE);
+	}
+
+	if (force_update) {
+		BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
+		return (B_TRUE);
+	}
+
+	/* Compare the two extended information structures. */
+	stage2_hs.src_buf = (unsigned char *)stage2_file->file;
+	stage2_hs.src_size = stage2_file->file_size;
+
+	return (einfo_should_update(einfo, &stage2_hs, updt_str));
 }
 
 
@@ -687,198 +1281,230 @@
 #define	NUM_BLOCK(pos)		(*(ushort_t *)((pos) + 4))
 #define	START_SEG(pos)		(*(ushort_t *)((pos) + 6))
 
-static void
-modify_and_write_stage2(int dev_fd)
+static int
+prepare_stage2(ig_data_t *install, char *updt_str)
 {
-	int 	nrecord;
-	off_t 	offset;
-	char	*dest;
+	ig_device_t	*device = &install->device;
+	ig_stage2_t	*stage2 = &install->stage2;
+	uint32_t	mboot_off = 0;
+
+	assert(install != NULL);
+	assert(stage2->file != NULL);
 
-	if (do_version) {
-		dest = stage2_buffer + STAGE2_SIGN_OFFSET;
-		if (compute_and_write_md5hash(dest) < 0)
-			perror("MD5 operation");
-		dest = stage2_buffer + STAGE2_PKG_VERSION;
-		(void) strncpy(dest, verstring, VERSION_SIZE);
+	/* New stage2 files come with an embedded stage2. */
+	if (find_multiboot(stage2->file, stage2->file_size, &mboot_off)
+	    != BC_SUCCESS) {
+		BOOT_DEBUG("WARNING: no multiboot structure found in stage2, "
+		    "are you using an old GRUB stage2?\n");
+		if (do_version == B_TRUE) {
+			(void) fprintf(stderr, gettext("Versioning requested "
+			    "but stage2 does not support it.. skipping.\n"));
+			do_version = B_FALSE;
+		}
+	} else {
+		/* Keep track of where the multiboot header is. */
+		stage2->mboot_off = mboot_off;
+		stage2->mboot = (multiboot_header_t *)(stage2->file +
+		    mboot_off);
+		if (do_version) {
+			/*
+			 * Adding stage2 information needs to happen before
+			 * we modify the copy of stage2 we have in memory, so
+			 * that the hashing reflects the one of the file.
+			 * An error here is not fatal.
+			 */
+			add_stage2_einfo(stage2, updt_str);
+		}
+		/*
+		 * Fill multiboot information. We add them even without
+		 * versioning to support as much as possible mirroring.
+		 */
+		prepare_fake_multiboot(stage2);
 	}
 
-	if (is_floppy || is_bootpar) {
-		int i = 0;
-		uint32_t partition_offset;
-		uint32_t install_addr = 0x8200;
-		uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST;
+	if (is_bootpar(device->type)) {
+		uint32_t	blocklist[SECTOR_SIZE / sizeof (uint32_t)];
+		uint32_t	install_addr = STAGE2_MEMADDR + SECTOR_SIZE;
+		int		i = 0;
+		uchar_t		*pos;
 
-		stage2_first_sector = blocklist[0];
+		bzero(blocklist, sizeof (blocklist));
+		if (read_stage2_blocklist(device->part_fd, blocklist) != 0) {
+			(void) fprintf(stderr, gettext("Error reading pcfs "
+			    "stage2 blocklist\n"));
+			return (BC_ERROR);
+		}
 
-		/* figure out the second sector */
+		pos = (uchar_t *)stage2->file + STAGE2_BLOCKLIST;
+		stage2->first_sector = device->start_sector + blocklist[0];
+		stage2->pcfs_first_sectors[0] = blocklist[0];
+		BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
+
+
 		if (blocklist[1] > 1) {
 			blocklist[0]++;
 			blocklist[1]--;
 		} else {
 			i += 2;
 		}
-		stage2_second_sector = blocklist[i];
 
-		if (is_floppy)
-			partition_offset = 0;
-		else	/* solaris boot partition */
-			partition_offset = get_start_sector(dev_fd);
+		stage2->pcfs_first_sectors[1] = blocklist[i];
 
-		/* install the blocklist at the end of stage2_buffer */
 		while (blocklist[i]) {
 			if (START_BLOCK(pos - 8) != 0 &&
 			    START_BLOCK(pos - 8) != blocklist[i + 2]) {
 				(void) fprintf(stderr, PCFS_FRAGMENTED);
-				exit(-1);
+				return (BC_ERROR);
 			}
-			START_BLOCK(pos) = blocklist[i] + partition_offset;
+			START_BLOCK(pos) = blocklist[i] + device->start_sector;
 			START_SEG(pos) = (ushort_t)(install_addr >> 4);
 			NUM_BLOCK(pos) = blocklist[i + 1];
 			install_addr += blocklist[i + 1] * SECTOR_SIZE;
 			pos -= 8;
 			i += 2;
 		}
-
 	} else {
+		/* Solaris VTOC */
+		stage2->first_sector = device->start_sector + STAGE2_BLKOFF;
+		BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
 		/*
 		 * In a solaris partition, stage2 is written to contiguous
 		 * blocks. So we update the starting block only.
 		 */
-		*((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) =
-		    stage2_first_sector + 1;
-	}
-
-	if (is_floppy) {
-		/* modify the config file to add (fd0) */
-		char *config_file = stage2_buffer + STAGE2_VER_STRING;
-		while (*config_file++)
-			;
-		strcpy(config_file, "(fd0)/boot/grub/menu.lst");
-	} else {
-		/* force lba and set disk partition */
-		*((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1;
-		*((long *)(stage2_buffer + STAGE2_INSTALLPART))
-		    = (partition << 16) | (slice << 8) | 0xff;
-	}
-
-	/* modification done, now do the writing */
-	if (is_floppy || is_bootpar) {
-		/* we rewrite block 0 and 1 and that's it */
-		if (!nowrite &&
-		    (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
-		    stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE ||
-		    pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
-		    stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) {
-			(void) fprintf(stderr, WRITE_FAIL_STAGE2);
-			exit(-1);
-		}
-		(void) fprintf(stdout, WRITE_STAGE2_PCFS);
-		return;
+		*((ulong_t *)(stage2->file + STAGE2_BLOCKLIST)) =
+		    stage2->first_sector + 1;
 	}
 
-	/* for disk, write stage2 starting at STAGE2_BLKOFF sector */
-	offset = STAGE2_BLKOFF;
+	/* force lba and set disk partition */
+	*((unsigned char *) (stage2->file + STAGE2_FORCE_LBA)) = 1;
+	*((long *)(stage2->file + STAGE2_INSTALLPART))
+	    = (device->partition << 16) | (device->slice << 8) | 0xff;
 
-	/* write the modified first two sectors */
-	if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
-	    offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) {
-		(void) fprintf(stderr, WRITE_FAIL_STAGE2);
-		exit(-1);
-	}
+	return (BC_SUCCESS);
+}
+
+static int
+find_x86_bootpar(struct mboot *mboot, int *part_num, uint32_t *start_sect)
+{
+	int	i;
 
-	/* write the remaining sectors */
-	nrecord = 2;
-	offset += 2;
-	for (;;) {
-		int nread, nwrite;
-		nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE,
-		    nrecord * SECTOR_SIZE);
-		if (nread > 0 && !nowrite)
-			nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
-			    offset * SECTOR_SIZE);
-		else
-			nwrite = SECTOR_SIZE;
-		if (nread < 0 || nwrite != SECTOR_SIZE) {
-			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
-			    nread, nwrite);
-			break;
+	for (i = 0; i < FD_NUMPART; i++) {
+		struct ipart	*part;
+
+		part = (struct ipart *)mboot->parts + i;
+		if (part->systid == 0xbe) {
+			if (start_sect)
+				*start_sect = part->relsect;
+			if (part_num)
+				*part_num = i;
+			/* solaris boot part */
+			return (BC_SUCCESS);
 		}
-		if (nread > 0) {
-			nrecord ++;
-			offset ++;
-		}
-		if (nread < SECTOR_SIZE)
-			break;	/* end of file */
 	}
-	(void) fprintf(stdout, WRITE_STAGE2_DISK,
-	    partition, nrecord, STAGE2_BLKOFF, stage2_first_sector);
+	return (BC_ERROR);
 }
 
 static char *
-get_raw_partition(char *device)
+get_raw_partition_path(ig_device_t *device)
 {
-	int len;
-	struct mboot *mboot;
-	static char *raw = NULL;
-
-	if (raw)
-		return (raw);
-	raw = strdup(device);
+	char	*raw;
+	int	len;
 
-	if (is_floppy)
-		return (raw);
-
-	if (is_bootpar) {
-		int i;
-		char *end = strstr(raw, "p0:boot");
+	if (is_bootpar(device->type)) {
+		int		part;
+		struct mboot	*mboot;
 
-		end[2] = 0;		/* chop off :boot */
-		read_boot_sect(raw);
-		mboot = (struct mboot *)boot_sect;
-		for (i = 0; i < FD_NUMPART; i++) {
-			struct ipart *part = (struct ipart *)mboot->parts + i;
-			if (part->systid == 0xbe)	/* solaris boot part */
-				break;
+		mboot = (struct mboot *)device->boot_sector;
+		if (find_x86_bootpar(mboot, &part, NULL) != BC_SUCCESS) {
+			(void) fprintf(stderr, BOOTPAR_NOTFOUND,
+			    device->path_p0);
+			return (NULL);
 		}
 
-		if (i == FD_NUMPART) {
-			(void) fprintf(stderr, BOOTPAR_NOTFOUND, device);
-			exit(-1);
+		raw = strdup(device->path_p0);
+		if (raw == NULL) {
+			perror(gettext("Memory allocation failed"));
+			return (NULL);
 		}
-		end[1] = '1' + i;	/* set partition name */
+
+		raw[strlen(raw) - 2] = '1' + part;
 		return (raw);
 	}
 
 	/* For disk, remember slice and return whole fdisk partition  */
+	raw = strdup(device->path);
+	if (raw == NULL) {
+		perror(gettext("Memory allocation failed"));
+		return (NULL);
+	}
+
 	len = strlen(raw);
 	if (raw[len - 2] != 's' || raw[len - 1] == '2') {
 		(void) fprintf(stderr, NOT_ROOT_SLICE);
-		exit(-1);
+		free(raw);
+		return (NULL);
 	}
-	slice = atoi(&raw[len - 1]);
+	device->slice = atoi(&raw[len - 1]);
 
 	raw[len - 2] = 's';
 	raw[len - 1] = '2';
+
 	return (raw);
 }
 
+static int
+get_raw_partition_fd(ig_device_t *device)
+{
+	struct stat	stat = {0};
+	char		*raw;
+
+	raw = get_raw_partition_path(device);
+	if (raw == NULL)
+		return (BC_ERROR);
+
+	if (nowrite)
+		device->part_fd = open(raw, O_RDONLY);
+	else
+		device->part_fd = open(raw, O_RDWR);
+
+	if (device->part_fd < 0 || fstat(device->part_fd, &stat) != 0) {
+		(void) fprintf(stderr, OPEN_FAIL, raw);
+		free(raw);
+		return (BC_ERROR);
+	}
+
+	if (S_ISCHR(stat.st_mode) == 0) {
+		(void) fprintf(stderr, NOT_RAW_DEVICE, raw);
+		(void) close(device->part_fd);
+		device->part_fd = -1;
+		free(raw);
+		return (BC_ERROR);
+	}
+
+	free(raw);
+	return (BC_SUCCESS);
+}
+
 #define	TMP_MNTPT	"/tmp/installgrub_pcfs"
-static void
-copy_stage2(int dev_fd, char *device)
+static int
+copy_stage2_to_pcfs(ig_data_t *install)
 {
-	FILE *mntfp;
-	int i, pcfs_fp;
-	char buf[SECTOR_SIZE];
-	char *cp;
-	struct mnttab mp = {0}, mpref = {0};
+	FILE		*mntfp;
+	int		pcfs_fp;
+	int		status = BC_ERROR;
+	char		buf[SECTOR_SIZE];
+	char		*cp;
+	struct mnttab	mp = {0}, mpref = {0};
+	ig_device_t	*device = &install->device;
+	ig_stage2_t	*stage2 = &install->stage2;
 
 	/* convert raw to block device name by removing the first 'r' */
-	(void) strncpy(buf, device, sizeof (buf));
+	(void) strncpy(buf, device->path, sizeof (buf));
 	buf[sizeof (buf) - 1] = 0;
 	cp = strchr(buf, 'r');
 	if (cp == NULL) {
-		(void) fprintf(stderr, CONVERT_FAIL, device);
-		exit(-1);
+		(void) fprintf(stderr, CONVERT_FAIL, device->path);
+		return (BC_ERROR);
 	}
 	do {
 		*cp = *(cp + 1);
@@ -888,7 +1514,7 @@
 	mntfp = fopen("/etc/mnttab", "r");
 	if (mntfp == NULL) {
 		(void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
-		exit(-1);
+		return (BC_ERROR);
 	}
 
 	mpref.mnt_special = buf;
@@ -904,7 +1530,7 @@
 		bzero(&mp, sizeof (mp));
 		if (getmntany(mntfp, &mp, &mpref) != 0) {
 			(void) fprintf(stderr, MOUNT_FAIL, buf);
-			exit(-1);
+			return (BC_ERROR);
 		}
 	}
 
@@ -919,33 +1545,21 @@
 	if (pcfs_fp == -1) {
 		(void) fprintf(stderr, OPEN_FAIL_FILE, buf);
 		perror("open:");
-		(void) umount(TMP_MNTPT);
-		exit(-1);
+		goto out;
 	}
 
-	/* write stage2 to pcfs */
-	for (i = 0; ; i++) {
-		int nread, nwrite;
-		nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE);
-		if (nowrite)
-			nwrite = nread;
-		else
-			nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE);
-		if (nread < 0 || nwrite != nread) {
-			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
-			    nread, nwrite);
-			break;
-		}
-		if (nread < SECTOR_SIZE)
-			break;	/* end of file */
+	/* write stage2 to the pcfs mounted filesystem. */
+	if (write(pcfs_fp, stage2->file, stage2->file_size)
+	    != stage2->file_size) {
+		perror(gettext("Error writing stage2"));
+		goto out;
 	}
+
+	status = BC_SUCCESS;
+out_fd:
 	(void) close(pcfs_fp);
+out:
 	(void) umount(TMP_MNTPT);
-
-	/*
-	 * Now, get the blocklist from the device.
-	 */
-	bzero(blocklist, sizeof (blocklist));
-	if (read_stage2_blocklist(dev_fd, blocklist) != 0)
-		exit(-1);
+	(void) rmdir(TMP_MNTPT);
+	return (status);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/installgrub/installgrub.h	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,93 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_INSTALLGRUB_H
+#define	_INSTALLGRUB_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/multiboot.h>
+#include "./../common/bblk_einfo.h"
+
+#define	SECTOR_SIZE	(512)
+
+typedef struct _device_data {
+	char		*path;
+	char		*path_p0;
+	uint8_t		type;
+	int		part_fd;
+	int		disk_fd;
+	int		slice;
+	int		partition;
+	uint32_t	start_sector;
+	char		boot_sector[SECTOR_SIZE];
+} ig_device_t;
+
+typedef struct _stage2_data {
+	char			*buf;
+	char			*file;
+	char			*extra;
+	multiboot_header_t	*mboot;
+	uint32_t		mboot_off;
+	uint32_t		file_size;
+	uint32_t		buf_size;
+	uint32_t		first_sector;
+	uint32_t		pcfs_first_sectors[2];
+} ig_stage2_t;
+
+typedef struct _ig_data {
+	char		stage1_buf[SECTOR_SIZE];
+	ig_stage2_t	stage2;
+	ig_device_t	device;
+} ig_data_t;
+
+enum ig_devtype_t {
+	IG_DEV_X86BOOTPAR = 1,
+	IG_DEV_SOLVTOC
+};
+
+#define	is_bootpar(type)	(type == IG_DEV_X86BOOTPAR)
+
+#define	STAGE2_MEMADDR		(0x8000)	/* loading addr of stage2 */
+
+#define	STAGE1_BPB_OFFSET	(0x3)
+#define	STAGE1_BPB_SIZE		(0x3B)
+#define	STAGE1_BOOT_DRIVE	(0x40)
+#define	STAGE1_FORCE_LBA	(0x41)
+#define	STAGE1_STAGE2_ADDRESS	(0x42)
+#define	STAGE1_STAGE2_SECTOR	(0x44)
+#define	STAGE1_STAGE2_SEGMENT	(0x48)
+
+#define	STAGE2_BLOCKLIST	(SECTOR_SIZE - 0x8)
+#define	STAGE2_INSTALLPART	(SECTOR_SIZE + 0x8)
+#define	STAGE2_FORCE_LBA	(SECTOR_SIZE + 0x11)
+#define	STAGE2_BLKOFF		(50)	/* offset from start of fdisk part */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _INSTALLGRUB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/boot/installgrub/pcfs_glue.c	Wed Jul 28 15:51:57 2010 -0700
@@ -0,0 +1,213 @@
+/*
+ * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/bootvfs.h>
+#include <sys/filep.h>
+
+#include <libintl.h>
+#include <locale.h>
+#include "message.h"
+
+/*
+ * This file is glue layer to pcfs module in usr/src/common/fs/pcfs.c.
+ * It's main functionality is to get the stage file blocklist. It's
+ * used for installing grub on a Solaris boot partition.
+ */
+extern struct boot_fs_ops bpcfs_ops;
+struct boot_fs_ops *bfs_ops;
+struct boot_fs_ops *bfs_tab[] = {&bpcfs_ops, NULL};
+static int dev_fd;
+int bootrd_debug = 0;
+
+#define	DEV_BSIZE	512
+#define	MAX_CHUNK	64
+
+static unsigned int *blocklist;
+
+/* diskread_callback is set in filesytem module (pcfs.c) */
+int (*diskread_callback)(int, int);
+int (*fileread_callback)(int, int);
+
+static int
+add_stage2_block(int blocknum, int nblk)
+{
+	static int i = -2;
+
+	if (i >= 0 && (blocklist[i] + blocklist[i + 1] == blocknum)) {
+		blocklist[i + 1] += nblk;
+		return (0);
+	}
+
+	i += 2;
+	if (i >= DEV_BSIZE / 8) {
+		fprintf(stderr, PCFS_FRAGMENTED);
+		exit(-1);
+	}
+	blocklist[i] = blocknum;
+	blocklist[i + 1] = nblk;
+	return (0);
+}
+
+/*
+ * This one reads the ramdisk. If fi_memp is set, we copy the
+ * ramdisk content to the designated buffer. Otherwise, we
+ * do a "cached" read (set fi_memp to the actual ramdisk buffer).
+ */
+int
+diskread(fileid_t *filep)
+{
+	int ret;
+	uint_t blocknum, diskloc;
+
+	blocknum = filep->fi_blocknum;
+
+	if (diskread_callback) {
+		diskread_callback(blocknum, filep->fi_count / DEV_BSIZE);
+		return (0);
+	}
+
+	diskloc = blocknum * DEV_BSIZE;
+	if (filep->fi_memp == NULL) {
+		filep->fi_memp = malloc(filep->fi_count);
+	}
+	if (filep->fi_memp == NULL) {
+		fprintf(stderr, OUT_OF_MEMORY);
+		return (-1);
+	}
+
+	ret = pread(dev_fd, filep->fi_memp, filep->fi_count, diskloc);
+	if (ret < 0)
+		perror("diskread: pread");
+	return (ret >= 0 ? 0 : -1);
+}
+
+void *
+bkmem_alloc(size_t s)
+{
+	return (malloc(s));
+}
+
+/*ARGSUSED*/
+void
+bkmem_free(void *p, size_t s)
+{
+	free(p);
+}
+
+static int
+mountroot(char *name)
+{
+	int i;
+
+	/* try ops in bfs_tab and return the first successful one */
+	for (i = 0; bfs_tab[i] != NULL; i++) {
+		bfs_ops = bfs_tab[i];
+		if (BRD_MOUNTROOT(bfs_ops, name) == 0)
+			return (0);
+	}
+	return (-1);
+}
+
+static int
+unmountroot()
+{
+	return (BRD_UNMOUNTROOT(bfs_ops));
+}
+
+static int
+pcfs_glue_open(const char *filename, int flags)
+{
+	return (BRD_OPEN(bfs_ops, (char *)filename, flags));
+}
+
+static int
+pcfs_glue_close(int fd)
+{
+	return (BRD_CLOSE(bfs_ops, fd));
+}
+
+static ssize_t
+pcfs_glue_read(int fd, void *buf, size_t size)
+{
+	return (BRD_READ(bfs_ops, fd, buf, size));
+}
+
+static off_t
+pcfs_glue_lseek(int fd, off_t addr, int whence)
+{
+	return (BRD_SEEK(bfs_ops, fd, addr, whence));
+}
+
+/*
+ * Get the blocklist for stage2
+ */
+int
+read_stage2_blocklist(int device_fd, unsigned int *blkbuf)
+{
+	int i, fd, stage2_block;
+	char buf[DEV_BSIZE];
+	ssize_t size;
+
+	dev_fd = device_fd;
+	if (mountroot("dummy") != 0) {
+		fprintf(stderr, MOUNT_FAIL_PCFS);
+		return (-1);
+	}
+
+	if ((fd = pcfs_glue_open("/boot/grub/stage2", 0)) == -1) {
+		fprintf(stderr, OPEN_FAIL_PCFS);
+		return (-1);
+	}
+
+	if (bootrd_debug)
+		(void) printf("start reading stage2:\n");
+	stage2_block = 0;
+	blocklist = blkbuf;
+	fileread_callback = add_stage2_block;
+	for (;;) {
+		size = pcfs_glue_read(fd, buf, DEV_BSIZE);
+		if (size != DEV_BSIZE)
+			break;
+		stage2_block++;
+	}
+	fileread_callback = NULL;
+	(void) pcfs_glue_close(fd);
+
+	if (bootrd_debug) {
+		(void) printf("last block size = %d\n", size);
+		for (i = 0; blocklist[i] != 0; i += 2) {
+			(void) printf("sectors: %d-%d\n",
+			    blocklist[i],
+			    blocklist[i] + blocklist[i + 1] - 1);
+		}
+		(void) printf("total blocks in stage 2: %d\n", stage2_block);
+	}
+
+	(void) unmountroot();
+	return (0);
+}
--- a/usr/src/grub/grub-0.97/grub/asmstub.c	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/grub/grub-0.97/grub/asmstub.c	Wed Jul 28 15:51:57 2010 -0700
@@ -67,9 +67,6 @@
 #define EXTENDED_MEMSIZE (64 * 1024 * 1024)	/* 64MB */
 #define CONVENTIONAL_MEMSIZE (640 * 1024)	/* 640kB */
 
-unsigned char md5hash[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB"
-    "\xBB\xBB\xBB\xBB";
-char pkg_version[] = "empty";
 unsigned long install_partition = 0x20000;
 unsigned long boot_drive = 0;
 int saved_entryno = 0;
--- a/usr/src/grub/grub-0.97/stage2/asm.S	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/grub/grub-0.97/stage2/asm.S	Wed Jul 28 15:51:57 2010 -0700
@@ -108,17 +108,16 @@
 	 *  Leave some breathing room for the config file name.
 	 */
 
-	. = EXT_C(main) + 0x5f
-VARIABLE(ext_info)
-	.byte 0xEE
-VARIABLE(md5hash)
-	.quad	0xAAAAAAAACCCCCCCC, 0xBBBBBBBBBBBBBBBB
-VARIABLE(pkg_version)
-	.byte 0
+	. = EXT_C(main) + 0x60
+VARIABLE(fake_mboot)
+	.long	0x1BADB002
+	.long   0x00010003 
+	.long	-0x1BAEB005	
 	/* 
-	 * installgrub will place the pkg_version here
+	 * installgrub will place the rest of the fake 
+	 * multiboot header here.
 	 */
-	.= EXT_C(main) + 0x110
+	.= EXT_C(main) + 0x140
 /* the real mode code continues... */
 codestart:
 	cli		/* we're not safe here! */
--- a/usr/src/grub/grub-0.97/stage2/builtins.c	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/grub/grub-0.97/stage2/builtins.c	Wed Jul 28 15:51:57 2010 -0700
@@ -18,11 +18,6 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-/*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
 /* Include stdio.h before shared.h, because we can't define
    WITHOUT_LIBC_STUBS here.  */
 #ifdef GRUB_UTIL
@@ -2175,32 +2170,6 @@
   " a tight loop."
 };
 
-/* extended info */
-static int
-info_func (char *arg, int flags)
-{
-  int  i;
-
-  grub_printf("Extended version information : %s\n", pkg_version);
-  grub_printf("stage2 (MD5) signature : ");
-
-  for (i = 0; i < 0x10; i++)
-    grub_printf("%x", md5hash[i]);
-
-  grub_printf("\n");
-}
-
-static struct builtin builtin_info =
-{
-  "info",
-  info_func,
-  BUILTIN_CMDLINE | BUILTIN_HELP_LIST | BUILTIN_SCRIPT,
-  "info",
-  "Read Grub extended version and stage2 MD5 hash"
-};
-
-
-
 /* initrd */
 static int
 initrd_func (char *arg, int flags)
@@ -5896,7 +5865,6 @@
   &builtin_ifconfig,
 #endif /* SUPPORT_NETBOOT */
   &builtin_impsprobe,
-  &builtin_info,
   &builtin_initrd,
   &builtin_install,
   &builtin_ioprobe,
--- a/usr/src/grub/grub-0.97/stage2/shared.h	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/grub/grub-0.97/stage2/shared.h	Wed Jul 28 15:51:57 2010 -0700
@@ -17,10 +17,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
 
 /*
  *  Generic defines to use anywhere
@@ -534,7 +530,6 @@
   unsigned char reserved3[189];
 } __attribute__ ((packed));
 
-
 #undef NULL
 #define NULL         ((void *) 0)
 
@@ -607,8 +602,6 @@
 extern char config_file[];
 extern char *bootfile;
 extern configfile_origin_t configfile_origin;
-extern unsigned char md5hash[];
-extern char pkg_version[];
 extern unsigned long linux_text_len;
 extern char *linux_data_tmp_addr;
 extern char *linux_data_real_addr;
--- a/usr/src/psm/stand/bootblks/ufs/sparc/Makefile	Wed Jul 28 17:47:31 2010 -0500
+++ b/usr/src/psm/stand/bootblks/ufs/sparc/Makefile	Wed Jul 28 15:51:57 2010 -0700
@@ -18,31 +18,13 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # psm/stand/bootblks/ufs/sparc/Makefile
 #
 
 BASEDIR = ../..
 
-include $(BASEDIR)/Makefile.com
-
-#
-# This program is used to install the boot block
-#
-INSTALLBOOT		= installboot
-
-USR			= $(ROOT)/usr
-USR_SBIN		= $(USR)/sbin
-USR_SBIN_INSTALLBOOT	= $(USR_SBIN)/$(INSTALLBOOT)
-
-#
-# Overrides for installing installboot.
-#
-INS.file.555		= $(RM) $@; $(INS) -s -m 555 -f $(@D) $<
-
-
 SUBDIRS	= sun4u sun4v
 
 all	:=	TARGET= all
@@ -53,30 +35,9 @@
 
 .KEEP_STATE:
 
-all: $(INSTALLBOOT) $(SUBDIRS)
-
-install: $(USR_SBIN_INSTALLBOOT) $(SUBDIRS)
-
-lint clean: $(SUBDIRS)
-
-clobber: $(SUBDIRS)
-	-$(RM) $(INSTALLBOOT)
+all install lint clean clobber: $(SUBDIRS)
 
 $(SUBDIRS): FRC
 	@cd $@; pwd; $(MAKE) $(TARGET)
 
 FRC:
-
-#
-# install rules
-#
-$(USR_SBIN)/%:	% $(USR_SBIN)
-	$(INS.file.555)
-
-#
-# Pattern matching rules for source in this directory
-#
-%: %.sh
-	$(RM) $@
-	cat $< > $@
-	chmod +x $@
--- a/usr/src/psm/stand/bootblks/ufs/sparc/installboot.sh	Wed Jul 28 17:47:31 2010 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-#!/bin/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.
-#
-
-PATH=/usr/bin
-
-away() {
-	echo $2 1>&2
-	exit $1
-}
-
-COUNT=15
-FSTYPE=ufs
-
-while getopts F: a; do
-
-	case $a in
-	F) case $OPTARG in
-	   ufs|hsfs|zfs) FSTYPE=$OPTARG;;
-	   *) away 1 "$OPTARG: Unknown fstype";;
-	   esac;;
-	?) away 1 "unknown fstype: $fs"
-	esac
-done
-shift `expr $OPTIND - 1`
-
-Usage="Usage: `basename $0` [-F fstype] bootblk raw-device"
-
-test $# -ne 2 && away 1 "$Usage"
-
-BOOTBLK=$1
-DEVICE=$2
-test ! -f $BOOTBLK && away 1 "$BOOTBLK: File not found"
-test ! -c $DEVICE && away 1 "$DEVICE: Not a character device"
-test ! -w $DEVICE && away 1 "$DEVICE: Not writeable"
-
-# label at block 0, bootblk from block 1 through 15
-stderr=`dd if=$BOOTBLK of=$DEVICE bs=1b oseek=1 count=$COUNT conv=sync 2>&1`
-err=$? ; test $err -ne 0 && away $err "$stderr"
-
-#
-# The ZFS boot block is larger than what will fit into the first 7.5K so
-# we break it up and write the remaining portion into the ZFS provided boot
-# block region at offset 512K
-#
-if [ $FSTYPE = "zfs" ]; then
-	stderr=`dd if=$BOOTBLK of=$DEVICE bs=1b iseek=$COUNT oseek=1024 \
-	    count=16 conv=sync 2>&1`
-	err=$? ; test $err -ne 0 && away $err "$stderr"
-fi
-exit 0