xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm_upgrade.c (revision 5084:7d838c5c0eed)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
223446Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
273446Smrj 
283446Smrj #include <stdio.h>
293446Smrj #include <errno.h>
303446Smrj #include <stdlib.h>
313446Smrj #include <string.h>
323446Smrj #include <unistd.h>
333446Smrj #include <sys/types.h>
343446Smrj #include <sys/stat.h>
353446Smrj #include <limits.h>
363446Smrj #include <fcntl.h>
373446Smrj #include <strings.h>
383446Smrj 
393446Smrj #include <sys/mman.h>
403446Smrj #include <sys/elf.h>
413446Smrj #include <sys/multiboot.h>
423446Smrj 
433446Smrj #include "message.h"
443446Smrj #include "bootadm.h"
453446Smrj 
463446Smrj direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
47*5084Sjohnlev hv_t bam_is_hv = BAM_HV_UNKNOWN;
483446Smrj 
493446Smrj error_t
503446Smrj dboot_or_multiboot(const char *root)
513446Smrj {
523446Smrj 	char fname[PATH_MAX];
533446Smrj 	char *image;
543446Smrj 	uchar_t *ident;
553446Smrj 	int fd, m;
563446Smrj 	multiboot_header_t *mbh;
57*5084Sjohnlev 	struct stat sb;
583446Smrj 
593446Smrj 	(void) snprintf(fname, PATH_MAX, "%s/%s", root,
603446Smrj 	    "platform/i86pc/kernel/unix");
613446Smrj 	fd = open(fname, O_RDONLY);
623446Smrj 	if (fd < 0) {
633446Smrj 		bam_error(OPEN_FAIL, fname, strerror(errno));
643446Smrj 		return (BAM_ERROR);
653446Smrj 	}
663446Smrj 
673446Smrj 	/*
683446Smrj 	 * mmap the first 8K
693446Smrj 	 */
703446Smrj 	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
713446Smrj 	if (image == MAP_FAILED) {
723446Smrj 		bam_error(MMAP_FAIL, fname, strerror(errno));
733446Smrj 		return (BAM_ERROR);
743446Smrj 	}
753446Smrj 
763446Smrj 	ident = (uchar_t *)image;
773446Smrj 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
783446Smrj 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
793446Smrj 		bam_error(NOT_ELF_FILE, fname);
803446Smrj 		return (BAM_ERROR);
813446Smrj 	}
823446Smrj 	if (ident[EI_CLASS] != ELFCLASS32) {
833446Smrj 		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
843446Smrj 		return (BAM_ERROR);
853446Smrj 	}
863446Smrj 
873446Smrj 	/*
883446Smrj 	 * The GRUB multiboot header must be 32-bit aligned and completely
893446Smrj 	 * contained in the 1st 8K of the file.  If the unix binary has
903446Smrj 	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
913446Smrj 	 * this kernel must be booted via multiboot -- we call this a
923446Smrj 	 * 'multiboot' kernel.
933446Smrj 	 */
943446Smrj 	bam_direct = BAM_DIRECT_MULTIBOOT;
953446Smrj 	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
963446Smrj 		mbh = (void *)(image + m);
973446Smrj 		if (mbh->magic == MB_HEADER_MAGIC) {
983446Smrj 			bam_direct = BAM_DIRECT_DBOOT;
993446Smrj 			break;
1003446Smrj 		}
1013446Smrj 	}
1023446Smrj 	(void) munmap(image, 8192);
1033446Smrj 	(void) close(fd);
104*5084Sjohnlev 
105*5084Sjohnlev 	if (bam_direct == BAM_DIRECT_DBOOT) {
106*5084Sjohnlev 		(void) snprintf(fname, PATH_MAX, "%s/%s", root, XEN_32);
107*5084Sjohnlev 		if (stat(fname, &sb) == 0) {
108*5084Sjohnlev 			bam_is_hv = BAM_HV_PRESENT;
109*5084Sjohnlev 		} else {
110*5084Sjohnlev 			bam_is_hv = BAM_HV_NO;
111*5084Sjohnlev 		}
112*5084Sjohnlev 	}
113*5084Sjohnlev 
1143446Smrj 	return (BAM_SUCCESS);
1153446Smrj }
1163446Smrj 
1173446Smrj #define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
1183446Smrj 
1193446Smrj /*
1203446Smrj  * Return true if root has been bfu'ed.  bfu will blow away
1213446Smrj  * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
1223446Smrj  * assume the system has not been bfu'ed.
1233446Smrj  */
1243446Smrj static int
1253446Smrj is_bfu_system(const char *root)
1263446Smrj {
1273446Smrj 	static int is_bfu = -1;
1283446Smrj 	char path[PATH_MAX];
1293446Smrj 	struct stat sb;
1303446Smrj 
1313446Smrj 	if (is_bfu != -1)
1323446Smrj 		return (is_bfu);
1333446Smrj 
1343446Smrj 	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
1353446Smrj 	if (stat(path, &sb) != 0) {
1363446Smrj 		is_bfu = 1;
1373446Smrj 	} else {
1383446Smrj 		is_bfu = 0;
1393446Smrj 	}
1403446Smrj 	return (is_bfu);
1413446Smrj }
1423446Smrj 
1433446Smrj #define	MENU_URL(root)	(is_bfu_system(root) ?		\
1443446Smrj 	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
1453446Smrj 	"http://www.sun.com/msg/SUNOS-8000-AK")
1463446Smrj 
1473446Smrj /*
1483446Smrj  * Simply allocate a new line and copy in cmd + sep + arg
1493446Smrj  */
1503446Smrj void
1513446Smrj update_line(line_t *linep)
1523446Smrj {
1533446Smrj 	size_t size;
1543446Smrj 
1553446Smrj 	free(linep->line);
1563446Smrj 	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
1573446Smrj 	linep->line = s_calloc(1, size);
1583446Smrj 	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
1593446Smrj 	    linep->arg);
1603446Smrj }
1613446Smrj 
1623446Smrj /*
1633446Smrj  * The parse_kernel_line function examines a menu.lst kernel line.  For
1643446Smrj  * multiboot, this is:
1653446Smrj  *
1663446Smrj  * kernel <multiboot path> <flags1> <kernel path> <flags2>
1673446Smrj  *
1683446Smrj  * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
1693446Smrj  *
1703446Smrj  * <kernel path> may be missing, or may be any full or relative path to unix.
1713446Smrj  *	We check for it by looking for a word ending in "/unix".  If it ends
1723446Smrj  *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
1733446Smrj  *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
1743446Smrj  *	it's a custom kernel, and we skip it.
1753446Smrj  *
1763446Smrj  * <flags*> are anything that doesn't fit either of the above - these will be
1773446Smrj  *	copied over.
1783446Smrj  *
1793446Smrj  * For direct boot, the defaults are
1803446Smrj  *
1813446Smrj  * kernel$ <kernel path> <flags>
1823446Smrj  *
1833446Smrj  * <kernel path> is one of:
1843446Smrj  *	/platform/i86pc/kernel/$ISADIR/unix
1853446Smrj  *	/platform/i86pc/kernel/unix
1863446Smrj  *	/platform/i86pc/kernel/amd64/unix
1873446Smrj  *	/boot/platform/i86pc/kernel/unix
1883446Smrj  *
1893446Smrj  * If <kernel path> is any of the last three, the command may also be "kernel".
1903446Smrj  *
1913446Smrj  * <flags> is anything that isn't <kernel path>.
1923446Smrj  *
1933446Smrj  * This function is only called if it applies to our target boot environment.
1943446Smrj  * If we can't make any sense of the kernel line, an error is printed and
1953446Smrj  * BAM_ERROR is returned.
1963446Smrj  *
1973446Smrj  * The desired install type is given in the global variable bam_direct.
1983446Smrj  * If the kernel line is of a different install type, we change it to the
1993446Smrj  * preferred type.  If the kernel line is already of the correct install
2003446Smrj  * type, we do nothing.  Either way, BAM_SUCCESS is returned.
2013446Smrj  *
2023446Smrj  * For safety, we do one more check: if the kernel path starts with /boot,
2033446Smrj  * we verify that the new kernel exists before changing it.  This is mainly
2043446Smrj  * done for bfu, as it may cause the failsafe archives to be a different
2053446Smrj  * boot architecture from the newly bfu'ed system.
2063446Smrj  */
2073446Smrj static error_t
2083446Smrj parse_kernel_line(line_t *linep, const char *root, uint8_t *flags)
2093446Smrj {
2103446Smrj 	char path[PATH_MAX];
2113446Smrj 	int len, left, total_len;
2123446Smrj 	struct stat sb;
2133446Smrj 	char *new_ptr, *new_arg, *old_ptr;
2143446Smrj 	menu_cmd_t which;
2153446Smrj 
2163446Smrj 	/* Used when changing a multiboot line to dboot */
2173446Smrj 	char *unix_ptr, *flags1_ptr, *flags2_ptr;
2183446Smrj 
2193446Smrj 	/*
2203446Smrj 	 * Note that BAM_ENTRY_DBOOT refers to the entry we're looking at, not
2213446Smrj 	 * necessarily the system type.
2223446Smrj 	 */
2233446Smrj 	if (strncmp(linep->arg, DIRECT_BOOT_32,
2243446Smrj 	    sizeof (DIRECT_BOOT_32) - 1) == 0) {
2253446Smrj 		*flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
2263446Smrj 	} else if ((strncmp(linep->arg, DIRECT_BOOT_KERNEL,
2273446Smrj 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) ||
2283446Smrj 	    (strncmp(linep->arg, DIRECT_BOOT_64,
2293446Smrj 	    sizeof (DIRECT_BOOT_64) - 1) == 0) ||
2303446Smrj 	    (strncmp(linep->arg, DIRECT_BOOT_FAILSAFE_KERNEL,
2313446Smrj 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0)) {
2323446Smrj 		*flags |= BAM_ENTRY_DBOOT;
2333446Smrj 	} else if ((strncmp(linep->arg, MULTI_BOOT,
2343446Smrj 	    sizeof (MULTI_BOOT) - 1) == 0) ||
2353446Smrj 	    (strncmp(linep->arg, MULTI_BOOT_FAILSAFE,
2363446Smrj 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0)) {
2373446Smrj 		*flags &= ~BAM_ENTRY_DBOOT;
2383446Smrj 	} else {
2393446Smrj 		bam_error(NO_KERNEL_MATCH, linep->lineNum, MENU_URL(root));
2403446Smrj 		return (BAM_ERROR);
2413446Smrj 	}
2423446Smrj 
2433446Smrj 	if (((*flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
2443446Smrj 	    (((*flags & BAM_ENTRY_DBOOT) == 0) &&
2453446Smrj 	    (bam_direct == BAM_DIRECT_MULTIBOOT))) {
2463446Smrj 
2473446Smrj 		/* No action needed */
2483446Smrj 		return (BAM_SUCCESS);
2493446Smrj 	}
2503446Smrj 
2513446Smrj 	if (*flags & BAM_ENTRY_MINIROOT) {
2523446Smrj 		/*
2533446Smrj 		 * We're changing boot architectures - make sure
2543446Smrj 		 * the multiboot failsafe still exists.
2553446Smrj 		 */
2563446Smrj 		(void) snprintf(path, PATH_MAX, "%s%s", root,
2573446Smrj 		    (*flags & BAM_ENTRY_DBOOT) ? MULTI_BOOT_FAILSAFE :
2583446Smrj 		    DIRECT_BOOT_FAILSAFE_KERNEL);
2593446Smrj 		if (stat(path, &sb) != 0) {
2603446Smrj 			if (bam_verbose) {
2613446Smrj 				bam_error(FAILSAFE_MISSING, linep->lineNum);
2623446Smrj 			}
2633446Smrj 			return (BAM_SUCCESS);
2643446Smrj 		}
2653446Smrj 	}
2663446Smrj 
2673446Smrj 	/*
2683446Smrj 	 * Make sure we have the correct cmd - either kernel or kernel$
2693446Smrj 	 * The failsafe entry should always be KERNEL_CMD.
2703446Smrj 	 */
2713446Smrj 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
2723446Smrj 	    (*flags & BAM_ENTRY_MINIROOT)) ? KERNEL_CMD : KERNEL_DOLLAR_CMD;
2733446Smrj 	free(linep->cmd);
2743446Smrj 	len = strlen(menu_cmds[which]) + 1;
2753446Smrj 	linep->cmd = s_calloc(1, len);
2763446Smrj 	(void) strncpy(linep->cmd, menu_cmds[which], len);
2773446Smrj 
2783446Smrj 	/*
2793446Smrj 	 * Since all arguments are copied, the new arg string should be close
2803446Smrj 	 * in size to the old one.  Just add 32 to cover the difference in
2813446Smrj 	 * the boot path.
2823446Smrj 	 */
2833446Smrj 	total_len = strlen(linep->arg) + 32;
2843446Smrj 	new_arg = s_calloc(1, total_len);
2853446Smrj 	old_ptr = strchr(linep->arg, ' ');
2863446Smrj 	if (old_ptr != NULL)
2873446Smrj 		old_ptr++;
2883446Smrj 
2893446Smrj 	/*
2903446Smrj 	 * Transitioning from dboot to multiboot is pretty simple.  We
2913446Smrj 	 * copy in multiboot and any args.
2923446Smrj 	 */
2933446Smrj 	if (bam_direct == BAM_DIRECT_MULTIBOOT) {
2943446Smrj 		if (old_ptr == NULL) {
2953446Smrj 			(void) snprintf(new_arg, total_len, "%s",
2963446Smrj 			    (*flags & BAM_ENTRY_MINIROOT) ?
2973446Smrj 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT);
2983446Smrj 		} else {
2993446Smrj 			(void) snprintf(new_arg, total_len, "%s %s",
3003446Smrj 			    (*flags & BAM_ENTRY_MINIROOT) ?
3013446Smrj 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT, old_ptr);
3023446Smrj 		}
3033446Smrj 		goto done;
3043446Smrj 	}
3053446Smrj 
3063446Smrj 	/*
3073446Smrj 	 * Transitioning from multiboot to directboot is a bit more
3083446Smrj 	 * complicated, since we may have two sets of arguments to
3093446Smrj 	 * copy and a unix path to parse.
3103446Smrj 	 *
3113446Smrj 	 * First, figure out if there's a unix path.
3123446Smrj 	 */
3133446Smrj 	if ((old_ptr != NULL) &&
3143446Smrj 	    ((unix_ptr = strstr(old_ptr, "/unix")) != NULL)) {
3153446Smrj 		/* See if there's anything past unix */
3163446Smrj 		flags2_ptr = unix_ptr + sizeof ("/unix");
3173446Smrj 		if (*flags2_ptr == '\0') {
3183446Smrj 			flags2_ptr = NULL;
3193446Smrj 		}
3203446Smrj 
3213446Smrj 		while ((unix_ptr > old_ptr) && (*unix_ptr != ' '))
3223446Smrj 			unix_ptr--;
3233446Smrj 
3243446Smrj 		if (unix_ptr == old_ptr) {
3253446Smrj 			flags1_ptr = NULL;
3263446Smrj 		} else {
3273446Smrj 			flags1_ptr = old_ptr;
3283446Smrj 		}
3293446Smrj 
3303446Smrj 		if (strstr(unix_ptr, "kernel/unix") != NULL) {
3313446Smrj 			*flags |= BAM_ENTRY_32BIT;
3323446Smrj 		} else if ((strstr(unix_ptr, "kernel/amd64/unix") == NULL) &&
3333446Smrj 		    (!bam_force)) {
3343446Smrj 			/*
3353446Smrj 			 * If the above strstr returns NULL, but bam_force is
3363446Smrj 			 * set, we'll be upgrading an Install kernel.  The
3373446Smrj 			 * result probably won't be what was intended, but we'll
3383446Smrj 			 * try it anyways.
3393446Smrj 			 */
3403446Smrj 			return (BAM_SKIP);
3413446Smrj 		}
3423446Smrj 	} else if (old_ptr != NULL) {
3433446Smrj 		flags1_ptr = old_ptr;
3443446Smrj 		unix_ptr = flags1_ptr + strlen(old_ptr);
3453446Smrj 		flags2_ptr = NULL;
3463446Smrj 	} else {
3473446Smrj 		unix_ptr = flags1_ptr = flags2_ptr = NULL;
3483446Smrj 	}
3493446Smrj 
3503446Smrj 	if (*flags & BAM_ENTRY_MINIROOT) {
3513446Smrj 		(void) snprintf(new_arg, total_len, "%s",
3523446Smrj 		    DIRECT_BOOT_FAILSAFE_KERNEL);
3533446Smrj 	} else if (*flags & BAM_ENTRY_32BIT) {
3543446Smrj 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_32);
3553446Smrj 	} else {
3563446Smrj 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_KERNEL);
3573446Smrj 	}
3583446Smrj 
3593446Smrj 	/*
3603446Smrj 	 * We now want to copy flags1_ptr through unix_ptr, and
3613446Smrj 	 * flags2_ptr through the end of the string
3623446Smrj 	 */
3633446Smrj 	if (flags1_ptr != NULL) {
3643446Smrj 		len = strlcat(new_arg, " ", total_len);
3653446Smrj 		left = total_len - len;
3663446Smrj 		new_ptr = new_arg + len;
3673446Smrj 
3683446Smrj 		if ((unix_ptr - flags1_ptr) < left)
3693446Smrj 			left = (unix_ptr - flags1_ptr) + 1;
3703446Smrj 		(void) strlcpy(new_ptr, flags1_ptr, left);
3713446Smrj 	}
3723446Smrj 	if (flags2_ptr != NULL) {
3733446Smrj 		(void) strlcat(new_arg, " ", total_len);
3743446Smrj 		(void) strlcat(new_arg, flags2_ptr, total_len);
3753446Smrj 	}
3763446Smrj 
3773446Smrj done:
3783446Smrj 	free(linep->arg);
3793446Smrj 	linep->arg = new_arg;
3803446Smrj 	update_line(linep);
3813446Smrj 	return (BAM_SUCCESS);
3823446Smrj }
3833446Smrj 
3843446Smrj /*
3853446Smrj  * Similar to above, except this time we're looking at a module line,
3863446Smrj  * which is quite a bit simpler.
3873446Smrj  *
3883446Smrj  * Under multiboot, the archive line is:
3893446Smrj  *
3903446Smrj  * module /platform/i86pc/boot_archive
3913446Smrj  *
3923446Smrj  * Under directboot, the archive line is:
3933446Smrj  *
3943446Smrj  * module$ /platform/i86pc/$ISADIR/boot_archive
3953446Smrj  *
3963446Smrj  * which may be specified exactly as either of:
3973446Smrj  *
3983446Smrj  * module /platform/i86pc/boot_archive
3993446Smrj  * module /platform/i86pc/amd64/boot_archive
4003446Smrj  *
4013446Smrj  * For either dboot or multiboot, the failsafe is:
4023446Smrj  *
4033446Smrj  * module /boot/x86.miniroot-safe
4043446Smrj  */
4053446Smrj static error_t
4063446Smrj parse_module_line(line_t *linep, const char *root, uint8_t flags)
4073446Smrj {
4083446Smrj 	int len;
4093446Smrj 	menu_cmd_t which;
4103446Smrj 	char *new;
4113446Smrj 
4123446Smrj 	/*
4133446Smrj 	 * If necessary, BAM_ENTRY_MINIROOT was already set in flags
4143446Smrj 	 * in upgrade_menu().  We re-check BAM_ENTRY_DBOOT here in here
4153446Smrj 	 * in case the kernel and module lines differ.
4163446Smrj 	 */
4173446Smrj 	if ((strcmp(linep->arg, DIRECT_BOOT_ARCHIVE) == 0) ||
4183446Smrj 	    (strcmp(linep->arg, DIRECT_BOOT_ARCHIVE_64) == 0)) {
4193446Smrj 		flags |= BAM_ENTRY_DBOOT;
4203446Smrj 	} else if ((strcmp(linep->arg, MULTI_BOOT_ARCHIVE) == 0) ||
4213446Smrj 	    (strcmp(linep->arg, MINIROOT) == 0)) {
4223446Smrj 		flags &= ~BAM_ENTRY_DBOOT;
4233446Smrj 	} else {
4243446Smrj 		bam_error(NO_MODULE_MATCH, linep->lineNum, MENU_URL(root));
4253446Smrj 		return (BAM_ERROR);
4263446Smrj 	}
4273446Smrj 
4283446Smrj 	if (((flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
4293446Smrj 	    (((flags & BAM_ENTRY_DBOOT) == 0) &&
4303446Smrj 	    (bam_direct == BAM_DIRECT_MULTIBOOT)) ||
4313446Smrj 	    ((flags & BAM_ENTRY_MINIROOT) &&
4323446Smrj 	    (strcmp(linep->cmd, menu_cmds[MODULE_CMD]) == 0))) {
4333446Smrj 
4343446Smrj 		/* No action needed */
4353446Smrj 		return (BAM_SUCCESS);
4363446Smrj 	}
4373446Smrj 
4383446Smrj 	/*
4393446Smrj 	 * Make sure we have the correct cmd - either module or module$
4403446Smrj 	 * The failsafe entry should always be MODULE_CMD.
4413446Smrj 	 */
4423446Smrj 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
4433446Smrj 	    (flags & BAM_ENTRY_MINIROOT)) ? MODULE_CMD : MODULE_DOLLAR_CMD;
4443446Smrj 	free(linep->cmd);
4453446Smrj 	len = strlen(menu_cmds[which]) + 1;
4463446Smrj 	linep->cmd = s_calloc(1, len);
4473446Smrj 	(void) strncpy(linep->cmd, menu_cmds[which], len);
4483446Smrj 
4493446Smrj 	if (flags & BAM_ENTRY_MINIROOT) {
4503446Smrj 		new = MINIROOT;
4513446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
4523446Smrj 	    ((flags & BAM_ENTRY_32BIT) == 0)) {
4533446Smrj 		new = DIRECT_BOOT_ARCHIVE;
4543446Smrj 	} else {
4553446Smrj 		new = MULTI_BOOT_ARCHIVE;
4563446Smrj 	}
4573446Smrj 
4583446Smrj 	free(linep->arg);
4593446Smrj 	len = strlen(new) + 1;
4603446Smrj 	linep->arg = s_calloc(1, len);
4613446Smrj 	(void) strncpy(linep->arg, new, len);
4623446Smrj 	update_line(linep);
4633446Smrj 
4643446Smrj 	return (BAM_SUCCESS);
4653446Smrj }
4663446Smrj 
4673446Smrj /*ARGSUSED*/
4683446Smrj error_t
4693446Smrj upgrade_menu(menu_t *mp, char *root, char *opt)
4703446Smrj {
4713446Smrj 	entry_t	*cur_entry;
4723446Smrj 	line_t	*cur_line;
473*5084Sjohnlev 	int	i, skipit, num_entries, found_hv;
4743446Smrj 	int	*hand_entries = NULL;
4753446Smrj 	boolean_t found_kernel = B_FALSE;
4763446Smrj 	error_t	rv;
4773446Smrj 	char	*rootdev, *grubdisk = NULL;
4783446Smrj 
479*5084Sjohnlev 	skipit = num_entries = found_hv = 0;
480*5084Sjohnlev 
4813446Smrj 	rootdev = get_special(root);
4823446Smrj 	if (rootdev) {
4833446Smrj 		grubdisk = os_to_grubdisk(rootdev, strlen(root) == 1);
4843446Smrj 		free(rootdev);
4853446Smrj 		rootdev = NULL;
4863446Smrj 	}
4873446Smrj 
4883446Smrj 	/* Loop through all OS entries in the menu.lst file */
4893446Smrj 	for (cur_entry = mp->entries; cur_entry != NULL;
4903446Smrj 	    cur_entry = cur_entry->next, skipit = 0) {
4913446Smrj 
4923446Smrj 		if ((cur_entry->flags & BAM_ENTRY_CHAINLOADER) ||
4933446Smrj 		    ((cur_entry->flags & BAM_ENTRY_MINIROOT) && !bam_force))
4943446Smrj 			continue;
4953446Smrj 
4963446Smrj 		/*
4973446Smrj 		 * We only change entries added by bootadm and live upgrade,
4983446Smrj 		 * and warn on the rest, unless the -f flag was passed.
4993446Smrj 		 */
5003446Smrj 		if ((!(cur_entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) &&
5013446Smrj 		    !bam_force) {
5023446Smrj 			if (num_entries == 0) {
5033446Smrj 				hand_entries = s_calloc(1, sizeof (int));
5043446Smrj 			} else {
5053446Smrj 				hand_entries = s_realloc(hand_entries,
5063446Smrj 				    (num_entries + 1) * sizeof (int));
5073446Smrj 			}
5083446Smrj 			hand_entries[num_entries++] = cur_entry->entryNum;
5093446Smrj 			continue;
5103446Smrj 		}
5113446Smrj 
512*5084Sjohnlev 		if (cur_entry->flags & BAM_ENTRY_HV) {
513*5084Sjohnlev 			found_hv = 1;
514*5084Sjohnlev 			continue;
515*5084Sjohnlev 		}
516*5084Sjohnlev 
5173446Smrj 		/*
5183446Smrj 		 * We make two loops through the lines.  First, we check if
5193446Smrj 		 * there is a root entry, and if so, whether we should be
5203446Smrj 		 * checking this entry.
5213446Smrj 		 */
5223446Smrj 		if ((grubdisk != NULL) && (cur_entry->flags & BAM_ENTRY_ROOT)) {
5233446Smrj 			for (cur_line = cur_entry->start; cur_line != NULL;
5243446Smrj 			    cur_line = cur_line->next) {
5253446Smrj 				if ((cur_line->cmd == NULL) ||
5263446Smrj 				    (cur_line->arg == NULL))
5273446Smrj 					continue;
5283446Smrj 
5293446Smrj 				if (strcmp(cur_line->cmd,
5303446Smrj 				    menu_cmds[ROOT_CMD]) == 0) {
5313446Smrj 					if (strcmp(cur_line->arg,
5323446Smrj 					    grubdisk) != 0) {
5333446Smrj 						/* A different slice */
5343446Smrj 						skipit = 1;
5353446Smrj 					}
5363446Smrj 					break;
5373446Smrj 				}
5383446Smrj 				if (cur_line == cur_entry->end)
5393446Smrj 					break;
5403446Smrj 			}
5413446Smrj 		}
5423446Smrj 		if (skipit)
5433446Smrj 			continue;
5443446Smrj 
5453446Smrj 		for (cur_line = cur_entry->start; cur_line != NULL;
5463446Smrj 		    cur_line = cur_line->next) {
5473446Smrj 
5483446Smrj 			/*
5493446Smrj 			 * We only compare for the length of KERNEL_CMD,
5503446Smrj 			 * so that KERNEL_DOLLAR_CMD will also match.
5513446Smrj 			 */
5523446Smrj 			if (strncmp(cur_line->cmd, menu_cmds[KERNEL_CMD],
5533446Smrj 			    strlen(menu_cmds[KERNEL_CMD])) == 0) {
5543446Smrj 				rv = parse_kernel_line(cur_line, root,
5553446Smrj 				    &(cur_entry->flags));
5563446Smrj 				if (rv == BAM_SKIP) {
5573446Smrj 					break;
5583446Smrj 				} else if (rv != BAM_SUCCESS) {
5593446Smrj 					return (rv);
5603446Smrj 				}
5613446Smrj 				found_kernel = B_TRUE;
5623446Smrj 			} else if (strncmp(cur_line->cmd,
5633446Smrj 			    menu_cmds[MODULE_CMD],
5643446Smrj 			    strlen(menu_cmds[MODULE_CMD])) == 0) {
5653446Smrj 				rv = parse_module_line(cur_line, root,
5663446Smrj 				    cur_entry->flags);
5673446Smrj 				if (rv != BAM_SUCCESS) {
5683446Smrj 					return (rv);
5693446Smrj 				}
5703446Smrj 			}
5713446Smrj 			if (cur_line == cur_entry->end)
5723446Smrj 				break;
5733446Smrj 		}
5743446Smrj 	}
5753446Smrj 
5763446Smrj 	/*
577*5084Sjohnlev 	 * If we're upgrading to a virtualized kernel and there are no
578*5084Sjohnlev 	 * hv entries in menu.lst, we need to add one.
579*5084Sjohnlev 	 */
580*5084Sjohnlev 	if ((bam_is_hv == BAM_HV_PRESENT) && (found_hv == 0)) {
581*5084Sjohnlev 		(void) add_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
582*5084Sjohnlev 		    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE);
583*5084Sjohnlev 	}
584*5084Sjohnlev 
585*5084Sjohnlev 	/*
5863446Smrj 	 * We only want to output one error, to avoid confusing a user.  We
5873446Smrj 	 * rank "No kernels changed" as a higher priority than "will not
5883446Smrj 	 * update hand-added entries", since the former implies the latter.
5893446Smrj 	 */
5903446Smrj 	if (found_kernel == B_FALSE) {
5913446Smrj 		bam_error(NO_KERNELS_FOUND, MENU_URL(root));
5923446Smrj 		return (BAM_ERROR);
5933446Smrj 	} else if (num_entries > 0) {
5943446Smrj 		bam_error(HAND_ADDED_ENTRY, MENU_URL(root));
5953446Smrj 		bam_print_stderr("Entry Number%s: ", (num_entries > 1) ?
5963446Smrj 		    "s" : "");
5973446Smrj 		for (i = 0; i < num_entries; i++) {
5983446Smrj 			bam_print_stderr("%d ", hand_entries[i]);
5993446Smrj 		}
6003446Smrj 		bam_print_stderr("\n");
6013446Smrj 	}
6023446Smrj 	return (BAM_WRITE);
6033446Smrj }
604