xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm_upgrade.c (revision 3446:5903aece022d)
1*3446Smrj /*
2*3446Smrj  * CDDL HEADER START
3*3446Smrj  *
4*3446Smrj  * The contents of this file are subject to the terms of the
5*3446Smrj  * Common Development and Distribution License (the "License").
6*3446Smrj  * You may not use this file except in compliance with the License.
7*3446Smrj  *
8*3446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3446Smrj  * or http://www.opensolaris.org/os/licensing.
10*3446Smrj  * See the License for the specific language governing permissions
11*3446Smrj  * and limitations under the License.
12*3446Smrj  *
13*3446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14*3446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3446Smrj  * If applicable, add the following below this CDDL HEADER, with the
16*3446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17*3446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3446Smrj  *
19*3446Smrj  * CDDL HEADER END
20*3446Smrj  */
21*3446Smrj /*
22*3446Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*3446Smrj  * Use is subject to license terms.
24*3446Smrj  */
25*3446Smrj 
26*3446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*3446Smrj 
28*3446Smrj #include <stdio.h>
29*3446Smrj #include <errno.h>
30*3446Smrj #include <stdlib.h>
31*3446Smrj #include <string.h>
32*3446Smrj #include <unistd.h>
33*3446Smrj #include <sys/types.h>
34*3446Smrj #include <sys/stat.h>
35*3446Smrj #include <limits.h>
36*3446Smrj #include <fcntl.h>
37*3446Smrj #include <strings.h>
38*3446Smrj 
39*3446Smrj #include <sys/mman.h>
40*3446Smrj #include <sys/elf.h>
41*3446Smrj #include <sys/multiboot.h>
42*3446Smrj 
43*3446Smrj #include "message.h"
44*3446Smrj #include "bootadm.h"
45*3446Smrj 
46*3446Smrj direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
47*3446Smrj 
48*3446Smrj error_t
49*3446Smrj dboot_or_multiboot(const char *root)
50*3446Smrj {
51*3446Smrj 	char fname[PATH_MAX];
52*3446Smrj 	char *image;
53*3446Smrj 	uchar_t *ident;
54*3446Smrj 	int fd, m;
55*3446Smrj 	multiboot_header_t *mbh;
56*3446Smrj 
57*3446Smrj 	(void) snprintf(fname, PATH_MAX, "%s/%s", root,
58*3446Smrj 	    "platform/i86pc/kernel/unix");
59*3446Smrj 	fd = open(fname, O_RDONLY);
60*3446Smrj 	if (fd < 0) {
61*3446Smrj 		bam_error(OPEN_FAIL, fname, strerror(errno));
62*3446Smrj 		return (BAM_ERROR);
63*3446Smrj 	}
64*3446Smrj 
65*3446Smrj 	/*
66*3446Smrj 	 * mmap the first 8K
67*3446Smrj 	 */
68*3446Smrj 	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
69*3446Smrj 	if (image == MAP_FAILED) {
70*3446Smrj 		bam_error(MMAP_FAIL, fname, strerror(errno));
71*3446Smrj 		return (BAM_ERROR);
72*3446Smrj 	}
73*3446Smrj 
74*3446Smrj 	ident = (uchar_t *)image;
75*3446Smrj 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
76*3446Smrj 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
77*3446Smrj 		bam_error(NOT_ELF_FILE, fname);
78*3446Smrj 		return (BAM_ERROR);
79*3446Smrj 	}
80*3446Smrj 	if (ident[EI_CLASS] != ELFCLASS32) {
81*3446Smrj 		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
82*3446Smrj 		return (BAM_ERROR);
83*3446Smrj 	}
84*3446Smrj 
85*3446Smrj 	/*
86*3446Smrj 	 * The GRUB multiboot header must be 32-bit aligned and completely
87*3446Smrj 	 * contained in the 1st 8K of the file.  If the unix binary has
88*3446Smrj 	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
89*3446Smrj 	 * this kernel must be booted via multiboot -- we call this a
90*3446Smrj 	 * 'multiboot' kernel.
91*3446Smrj 	 */
92*3446Smrj 	bam_direct = BAM_DIRECT_MULTIBOOT;
93*3446Smrj 	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
94*3446Smrj 		mbh = (void *)(image + m);
95*3446Smrj 		if (mbh->magic == MB_HEADER_MAGIC) {
96*3446Smrj 			bam_direct = BAM_DIRECT_DBOOT;
97*3446Smrj 			break;
98*3446Smrj 		}
99*3446Smrj 	}
100*3446Smrj 	(void) munmap(image, 8192);
101*3446Smrj 	(void) close(fd);
102*3446Smrj 	return (BAM_SUCCESS);
103*3446Smrj }
104*3446Smrj 
105*3446Smrj #define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
106*3446Smrj 
107*3446Smrj /*
108*3446Smrj  * Return true if root has been bfu'ed.  bfu will blow away
109*3446Smrj  * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
110*3446Smrj  * assume the system has not been bfu'ed.
111*3446Smrj  */
112*3446Smrj static int
113*3446Smrj is_bfu_system(const char *root)
114*3446Smrj {
115*3446Smrj 	static int is_bfu = -1;
116*3446Smrj 	char path[PATH_MAX];
117*3446Smrj 	struct stat sb;
118*3446Smrj 
119*3446Smrj 	if (is_bfu != -1)
120*3446Smrj 		return (is_bfu);
121*3446Smrj 
122*3446Smrj 	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
123*3446Smrj 	if (stat(path, &sb) != 0) {
124*3446Smrj 		is_bfu = 1;
125*3446Smrj 	} else {
126*3446Smrj 		is_bfu = 0;
127*3446Smrj 	}
128*3446Smrj 	return (is_bfu);
129*3446Smrj }
130*3446Smrj 
131*3446Smrj #define	MENU_URL(root)	(is_bfu_system(root) ?		\
132*3446Smrj 	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
133*3446Smrj 	"http://www.sun.com/msg/SUNOS-8000-AK")
134*3446Smrj 
135*3446Smrj /*
136*3446Smrj  * Simply allocate a new line and copy in cmd + sep + arg
137*3446Smrj  */
138*3446Smrj void
139*3446Smrj update_line(line_t *linep)
140*3446Smrj {
141*3446Smrj 	size_t size;
142*3446Smrj 
143*3446Smrj 	free(linep->line);
144*3446Smrj 	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
145*3446Smrj 	linep->line = s_calloc(1, size);
146*3446Smrj 	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
147*3446Smrj 	    linep->arg);
148*3446Smrj }
149*3446Smrj 
150*3446Smrj /*
151*3446Smrj  * The parse_kernel_line function examines a menu.lst kernel line.  For
152*3446Smrj  * multiboot, this is:
153*3446Smrj  *
154*3446Smrj  * kernel <multiboot path> <flags1> <kernel path> <flags2>
155*3446Smrj  *
156*3446Smrj  * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
157*3446Smrj  *
158*3446Smrj  * <kernel path> may be missing, or may be any full or relative path to unix.
159*3446Smrj  *	We check for it by looking for a word ending in "/unix".  If it ends
160*3446Smrj  *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
161*3446Smrj  *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
162*3446Smrj  *	it's a custom kernel, and we skip it.
163*3446Smrj  *
164*3446Smrj  * <flags*> are anything that doesn't fit either of the above - these will be
165*3446Smrj  *	copied over.
166*3446Smrj  *
167*3446Smrj  * For direct boot, the defaults are
168*3446Smrj  *
169*3446Smrj  * kernel$ <kernel path> <flags>
170*3446Smrj  *
171*3446Smrj  * <kernel path> is one of:
172*3446Smrj  *	/platform/i86pc/kernel/$ISADIR/unix
173*3446Smrj  *	/platform/i86pc/kernel/unix
174*3446Smrj  *	/platform/i86pc/kernel/amd64/unix
175*3446Smrj  *	/boot/platform/i86pc/kernel/unix
176*3446Smrj  *
177*3446Smrj  * If <kernel path> is any of the last three, the command may also be "kernel".
178*3446Smrj  *
179*3446Smrj  * <flags> is anything that isn't <kernel path>.
180*3446Smrj  *
181*3446Smrj  * This function is only called if it applies to our target boot environment.
182*3446Smrj  * If we can't make any sense of the kernel line, an error is printed and
183*3446Smrj  * BAM_ERROR is returned.
184*3446Smrj  *
185*3446Smrj  * The desired install type is given in the global variable bam_direct.
186*3446Smrj  * If the kernel line is of a different install type, we change it to the
187*3446Smrj  * preferred type.  If the kernel line is already of the correct install
188*3446Smrj  * type, we do nothing.  Either way, BAM_SUCCESS is returned.
189*3446Smrj  *
190*3446Smrj  * For safety, we do one more check: if the kernel path starts with /boot,
191*3446Smrj  * we verify that the new kernel exists before changing it.  This is mainly
192*3446Smrj  * done for bfu, as it may cause the failsafe archives to be a different
193*3446Smrj  * boot architecture from the newly bfu'ed system.
194*3446Smrj  */
195*3446Smrj static error_t
196*3446Smrj parse_kernel_line(line_t *linep, const char *root, uint8_t *flags)
197*3446Smrj {
198*3446Smrj 	char path[PATH_MAX];
199*3446Smrj 	int len, left, total_len;
200*3446Smrj 	struct stat sb;
201*3446Smrj 	char *new_ptr, *new_arg, *old_ptr;
202*3446Smrj 	menu_cmd_t which;
203*3446Smrj 
204*3446Smrj 	/* Used when changing a multiboot line to dboot */
205*3446Smrj 	char *unix_ptr, *flags1_ptr, *flags2_ptr;
206*3446Smrj 
207*3446Smrj 	/*
208*3446Smrj 	 * Note that BAM_ENTRY_DBOOT refers to the entry we're looking at, not
209*3446Smrj 	 * necessarily the system type.
210*3446Smrj 	 */
211*3446Smrj 	if (strncmp(linep->arg, DIRECT_BOOT_32,
212*3446Smrj 	    sizeof (DIRECT_BOOT_32) - 1) == 0) {
213*3446Smrj 		*flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
214*3446Smrj 	} else if ((strncmp(linep->arg, DIRECT_BOOT_KERNEL,
215*3446Smrj 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) ||
216*3446Smrj 	    (strncmp(linep->arg, DIRECT_BOOT_64,
217*3446Smrj 	    sizeof (DIRECT_BOOT_64) - 1) == 0) ||
218*3446Smrj 	    (strncmp(linep->arg, DIRECT_BOOT_FAILSAFE_KERNEL,
219*3446Smrj 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0)) {
220*3446Smrj 		*flags |= BAM_ENTRY_DBOOT;
221*3446Smrj 	} else if ((strncmp(linep->arg, MULTI_BOOT,
222*3446Smrj 	    sizeof (MULTI_BOOT) - 1) == 0) ||
223*3446Smrj 	    (strncmp(linep->arg, MULTI_BOOT_FAILSAFE,
224*3446Smrj 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0)) {
225*3446Smrj 		*flags &= ~BAM_ENTRY_DBOOT;
226*3446Smrj 	} else {
227*3446Smrj 		bam_error(NO_KERNEL_MATCH, linep->lineNum, MENU_URL(root));
228*3446Smrj 		return (BAM_ERROR);
229*3446Smrj 	}
230*3446Smrj 
231*3446Smrj 	if (((*flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
232*3446Smrj 	    (((*flags & BAM_ENTRY_DBOOT) == 0) &&
233*3446Smrj 	    (bam_direct == BAM_DIRECT_MULTIBOOT))) {
234*3446Smrj 
235*3446Smrj 		/* No action needed */
236*3446Smrj 		return (BAM_SUCCESS);
237*3446Smrj 	}
238*3446Smrj 
239*3446Smrj 	if (*flags & BAM_ENTRY_MINIROOT) {
240*3446Smrj 		/*
241*3446Smrj 		 * We're changing boot architectures - make sure
242*3446Smrj 		 * the multiboot failsafe still exists.
243*3446Smrj 		 */
244*3446Smrj 		(void) snprintf(path, PATH_MAX, "%s%s", root,
245*3446Smrj 		    (*flags & BAM_ENTRY_DBOOT) ? MULTI_BOOT_FAILSAFE :
246*3446Smrj 		    DIRECT_BOOT_FAILSAFE_KERNEL);
247*3446Smrj 		if (stat(path, &sb) != 0) {
248*3446Smrj 			if (bam_verbose) {
249*3446Smrj 				bam_error(FAILSAFE_MISSING, linep->lineNum);
250*3446Smrj 			}
251*3446Smrj 			return (BAM_SUCCESS);
252*3446Smrj 		}
253*3446Smrj 	}
254*3446Smrj 
255*3446Smrj 	/*
256*3446Smrj 	 * Make sure we have the correct cmd - either kernel or kernel$
257*3446Smrj 	 * The failsafe entry should always be KERNEL_CMD.
258*3446Smrj 	 */
259*3446Smrj 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
260*3446Smrj 	    (*flags & BAM_ENTRY_MINIROOT)) ? KERNEL_CMD : KERNEL_DOLLAR_CMD;
261*3446Smrj 	free(linep->cmd);
262*3446Smrj 	len = strlen(menu_cmds[which]) + 1;
263*3446Smrj 	linep->cmd = s_calloc(1, len);
264*3446Smrj 	(void) strncpy(linep->cmd, menu_cmds[which], len);
265*3446Smrj 
266*3446Smrj 	/*
267*3446Smrj 	 * Since all arguments are copied, the new arg string should be close
268*3446Smrj 	 * in size to the old one.  Just add 32 to cover the difference in
269*3446Smrj 	 * the boot path.
270*3446Smrj 	 */
271*3446Smrj 	total_len = strlen(linep->arg) + 32;
272*3446Smrj 	new_arg = s_calloc(1, total_len);
273*3446Smrj 	old_ptr = strchr(linep->arg, ' ');
274*3446Smrj 	if (old_ptr != NULL)
275*3446Smrj 		old_ptr++;
276*3446Smrj 
277*3446Smrj 	/*
278*3446Smrj 	 * Transitioning from dboot to multiboot is pretty simple.  We
279*3446Smrj 	 * copy in multiboot and any args.
280*3446Smrj 	 */
281*3446Smrj 	if (bam_direct == BAM_DIRECT_MULTIBOOT) {
282*3446Smrj 		if (old_ptr == NULL) {
283*3446Smrj 			(void) snprintf(new_arg, total_len, "%s",
284*3446Smrj 			    (*flags & BAM_ENTRY_MINIROOT) ?
285*3446Smrj 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT);
286*3446Smrj 		} else {
287*3446Smrj 			(void) snprintf(new_arg, total_len, "%s %s",
288*3446Smrj 			    (*flags & BAM_ENTRY_MINIROOT) ?
289*3446Smrj 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT, old_ptr);
290*3446Smrj 		}
291*3446Smrj 		goto done;
292*3446Smrj 	}
293*3446Smrj 
294*3446Smrj 	/*
295*3446Smrj 	 * Transitioning from multiboot to directboot is a bit more
296*3446Smrj 	 * complicated, since we may have two sets of arguments to
297*3446Smrj 	 * copy and a unix path to parse.
298*3446Smrj 	 *
299*3446Smrj 	 * First, figure out if there's a unix path.
300*3446Smrj 	 */
301*3446Smrj 	if ((old_ptr != NULL) &&
302*3446Smrj 	    ((unix_ptr = strstr(old_ptr, "/unix")) != NULL)) {
303*3446Smrj 		/* See if there's anything past unix */
304*3446Smrj 		flags2_ptr = unix_ptr + sizeof ("/unix");
305*3446Smrj 		if (*flags2_ptr == '\0') {
306*3446Smrj 			flags2_ptr = NULL;
307*3446Smrj 		}
308*3446Smrj 
309*3446Smrj 		while ((unix_ptr > old_ptr) && (*unix_ptr != ' '))
310*3446Smrj 			unix_ptr--;
311*3446Smrj 
312*3446Smrj 		if (unix_ptr == old_ptr) {
313*3446Smrj 			flags1_ptr = NULL;
314*3446Smrj 		} else {
315*3446Smrj 			flags1_ptr = old_ptr;
316*3446Smrj 		}
317*3446Smrj 
318*3446Smrj 		if (strstr(unix_ptr, "kernel/unix") != NULL) {
319*3446Smrj 			*flags |= BAM_ENTRY_32BIT;
320*3446Smrj 		} else if ((strstr(unix_ptr, "kernel/amd64/unix") == NULL) &&
321*3446Smrj 		    (!bam_force)) {
322*3446Smrj 			/*
323*3446Smrj 			 * If the above strstr returns NULL, but bam_force is
324*3446Smrj 			 * set, we'll be upgrading an Install kernel.  The
325*3446Smrj 			 * result probably won't be what was intended, but we'll
326*3446Smrj 			 * try it anyways.
327*3446Smrj 			 */
328*3446Smrj 			return (BAM_SKIP);
329*3446Smrj 		}
330*3446Smrj 	} else if (old_ptr != NULL) {
331*3446Smrj 		flags1_ptr = old_ptr;
332*3446Smrj 		unix_ptr = flags1_ptr + strlen(old_ptr);
333*3446Smrj 		flags2_ptr = NULL;
334*3446Smrj 	} else {
335*3446Smrj 		unix_ptr = flags1_ptr = flags2_ptr = NULL;
336*3446Smrj 	}
337*3446Smrj 
338*3446Smrj 	if (*flags & BAM_ENTRY_MINIROOT) {
339*3446Smrj 		(void) snprintf(new_arg, total_len, "%s",
340*3446Smrj 		    DIRECT_BOOT_FAILSAFE_KERNEL);
341*3446Smrj 	} else if (*flags & BAM_ENTRY_32BIT) {
342*3446Smrj 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_32);
343*3446Smrj 	} else {
344*3446Smrj 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_KERNEL);
345*3446Smrj 	}
346*3446Smrj 
347*3446Smrj 	/*
348*3446Smrj 	 * We now want to copy flags1_ptr through unix_ptr, and
349*3446Smrj 	 * flags2_ptr through the end of the string
350*3446Smrj 	 */
351*3446Smrj 	if (flags1_ptr != NULL) {
352*3446Smrj 		len = strlcat(new_arg, " ", total_len);
353*3446Smrj 		left = total_len - len;
354*3446Smrj 		new_ptr = new_arg + len;
355*3446Smrj 
356*3446Smrj 		if ((unix_ptr - flags1_ptr) < left)
357*3446Smrj 			left = (unix_ptr - flags1_ptr) + 1;
358*3446Smrj 		(void) strlcpy(new_ptr, flags1_ptr, left);
359*3446Smrj 	}
360*3446Smrj 	if (flags2_ptr != NULL) {
361*3446Smrj 		(void) strlcat(new_arg, " ", total_len);
362*3446Smrj 		(void) strlcat(new_arg, flags2_ptr, total_len);
363*3446Smrj 	}
364*3446Smrj 
365*3446Smrj done:
366*3446Smrj 	free(linep->arg);
367*3446Smrj 	linep->arg = new_arg;
368*3446Smrj 	update_line(linep);
369*3446Smrj 	return (BAM_SUCCESS);
370*3446Smrj }
371*3446Smrj 
372*3446Smrj /*
373*3446Smrj  * Similar to above, except this time we're looking at a module line,
374*3446Smrj  * which is quite a bit simpler.
375*3446Smrj  *
376*3446Smrj  * Under multiboot, the archive line is:
377*3446Smrj  *
378*3446Smrj  * module /platform/i86pc/boot_archive
379*3446Smrj  *
380*3446Smrj  * Under directboot, the archive line is:
381*3446Smrj  *
382*3446Smrj  * module$ /platform/i86pc/$ISADIR/boot_archive
383*3446Smrj  *
384*3446Smrj  * which may be specified exactly as either of:
385*3446Smrj  *
386*3446Smrj  * module /platform/i86pc/boot_archive
387*3446Smrj  * module /platform/i86pc/amd64/boot_archive
388*3446Smrj  *
389*3446Smrj  * For either dboot or multiboot, the failsafe is:
390*3446Smrj  *
391*3446Smrj  * module /boot/x86.miniroot-safe
392*3446Smrj  */
393*3446Smrj static error_t
394*3446Smrj parse_module_line(line_t *linep, const char *root, uint8_t flags)
395*3446Smrj {
396*3446Smrj 	int len;
397*3446Smrj 	menu_cmd_t which;
398*3446Smrj 	char *new;
399*3446Smrj 
400*3446Smrj 	/*
401*3446Smrj 	 * If necessary, BAM_ENTRY_MINIROOT was already set in flags
402*3446Smrj 	 * in upgrade_menu().  We re-check BAM_ENTRY_DBOOT here in here
403*3446Smrj 	 * in case the kernel and module lines differ.
404*3446Smrj 	 */
405*3446Smrj 	if ((strcmp(linep->arg, DIRECT_BOOT_ARCHIVE) == 0) ||
406*3446Smrj 	    (strcmp(linep->arg, DIRECT_BOOT_ARCHIVE_64) == 0)) {
407*3446Smrj 		flags |= BAM_ENTRY_DBOOT;
408*3446Smrj 	} else if ((strcmp(linep->arg, MULTI_BOOT_ARCHIVE) == 0) ||
409*3446Smrj 	    (strcmp(linep->arg, MINIROOT) == 0)) {
410*3446Smrj 		flags &= ~BAM_ENTRY_DBOOT;
411*3446Smrj 	} else {
412*3446Smrj 		bam_error(NO_MODULE_MATCH, linep->lineNum, MENU_URL(root));
413*3446Smrj 		return (BAM_ERROR);
414*3446Smrj 	}
415*3446Smrj 
416*3446Smrj 	if (((flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
417*3446Smrj 	    (((flags & BAM_ENTRY_DBOOT) == 0) &&
418*3446Smrj 	    (bam_direct == BAM_DIRECT_MULTIBOOT)) ||
419*3446Smrj 	    ((flags & BAM_ENTRY_MINIROOT) &&
420*3446Smrj 	    (strcmp(linep->cmd, menu_cmds[MODULE_CMD]) == 0))) {
421*3446Smrj 
422*3446Smrj 		/* No action needed */
423*3446Smrj 		return (BAM_SUCCESS);
424*3446Smrj 	}
425*3446Smrj 
426*3446Smrj 	/*
427*3446Smrj 	 * Make sure we have the correct cmd - either module or module$
428*3446Smrj 	 * The failsafe entry should always be MODULE_CMD.
429*3446Smrj 	 */
430*3446Smrj 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
431*3446Smrj 	    (flags & BAM_ENTRY_MINIROOT)) ? MODULE_CMD : MODULE_DOLLAR_CMD;
432*3446Smrj 	free(linep->cmd);
433*3446Smrj 	len = strlen(menu_cmds[which]) + 1;
434*3446Smrj 	linep->cmd = s_calloc(1, len);
435*3446Smrj 	(void) strncpy(linep->cmd, menu_cmds[which], len);
436*3446Smrj 
437*3446Smrj 	if (flags & BAM_ENTRY_MINIROOT) {
438*3446Smrj 		new = MINIROOT;
439*3446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
440*3446Smrj 	    ((flags & BAM_ENTRY_32BIT) == 0)) {
441*3446Smrj 		new = DIRECT_BOOT_ARCHIVE;
442*3446Smrj 	} else {
443*3446Smrj 		new = MULTI_BOOT_ARCHIVE;
444*3446Smrj 	}
445*3446Smrj 
446*3446Smrj 	free(linep->arg);
447*3446Smrj 	len = strlen(new) + 1;
448*3446Smrj 	linep->arg = s_calloc(1, len);
449*3446Smrj 	(void) strncpy(linep->arg, new, len);
450*3446Smrj 	update_line(linep);
451*3446Smrj 
452*3446Smrj 	return (BAM_SUCCESS);
453*3446Smrj }
454*3446Smrj 
455*3446Smrj /*ARGSUSED*/
456*3446Smrj error_t
457*3446Smrj upgrade_menu(menu_t *mp, char *root, char *opt)
458*3446Smrj {
459*3446Smrj 	entry_t	*cur_entry;
460*3446Smrj 	line_t	*cur_line;
461*3446Smrj 	int	i, skipit = 0, num_entries = 0;
462*3446Smrj 	int	*hand_entries = NULL;
463*3446Smrj 	boolean_t found_kernel = B_FALSE;
464*3446Smrj 	error_t	rv;
465*3446Smrj 	char	*rootdev, *grubdisk = NULL;
466*3446Smrj 
467*3446Smrj 	rootdev = get_special(root);
468*3446Smrj 	if (rootdev) {
469*3446Smrj 		grubdisk = os_to_grubdisk(rootdev, strlen(root) == 1);
470*3446Smrj 		free(rootdev);
471*3446Smrj 		rootdev = NULL;
472*3446Smrj 	}
473*3446Smrj 
474*3446Smrj 	/* Loop through all OS entries in the menu.lst file */
475*3446Smrj 	for (cur_entry = mp->entries; cur_entry != NULL;
476*3446Smrj 	    cur_entry = cur_entry->next, skipit = 0) {
477*3446Smrj 
478*3446Smrj 		if ((cur_entry->flags & BAM_ENTRY_CHAINLOADER) ||
479*3446Smrj 		    ((cur_entry->flags & BAM_ENTRY_MINIROOT) && !bam_force))
480*3446Smrj 			continue;
481*3446Smrj 
482*3446Smrj 		/*
483*3446Smrj 		 * We only change entries added by bootadm and live upgrade,
484*3446Smrj 		 * and warn on the rest, unless the -f flag was passed.
485*3446Smrj 		 */
486*3446Smrj 		if ((!(cur_entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) &&
487*3446Smrj 		    !bam_force) {
488*3446Smrj 			if (num_entries == 0) {
489*3446Smrj 				hand_entries = s_calloc(1, sizeof (int));
490*3446Smrj 			} else {
491*3446Smrj 				hand_entries = s_realloc(hand_entries,
492*3446Smrj 				    (num_entries + 1) * sizeof (int));
493*3446Smrj 			}
494*3446Smrj 			hand_entries[num_entries++] = cur_entry->entryNum;
495*3446Smrj 			continue;
496*3446Smrj 		}
497*3446Smrj 
498*3446Smrj 		/*
499*3446Smrj 		 * We make two loops through the lines.  First, we check if
500*3446Smrj 		 * there is a root entry, and if so, whether we should be
501*3446Smrj 		 * checking this entry.
502*3446Smrj 		 */
503*3446Smrj 		if ((grubdisk != NULL) && (cur_entry->flags & BAM_ENTRY_ROOT)) {
504*3446Smrj 			for (cur_line = cur_entry->start; cur_line != NULL;
505*3446Smrj 			    cur_line = cur_line->next) {
506*3446Smrj 				if ((cur_line->cmd == NULL) ||
507*3446Smrj 				    (cur_line->arg == NULL))
508*3446Smrj 					continue;
509*3446Smrj 
510*3446Smrj 				if (strcmp(cur_line->cmd,
511*3446Smrj 				    menu_cmds[ROOT_CMD]) == 0) {
512*3446Smrj 					if (strcmp(cur_line->arg,
513*3446Smrj 					    grubdisk) != 0) {
514*3446Smrj 						/* A different slice */
515*3446Smrj 						skipit = 1;
516*3446Smrj 					}
517*3446Smrj 					break;
518*3446Smrj 				}
519*3446Smrj 				if (cur_line == cur_entry->end)
520*3446Smrj 					break;
521*3446Smrj 			}
522*3446Smrj 		}
523*3446Smrj 		if (skipit)
524*3446Smrj 			continue;
525*3446Smrj 
526*3446Smrj 		for (cur_line = cur_entry->start; cur_line != NULL;
527*3446Smrj 		    cur_line = cur_line->next) {
528*3446Smrj 
529*3446Smrj 			/*
530*3446Smrj 			 * We only compare for the length of KERNEL_CMD,
531*3446Smrj 			 * so that KERNEL_DOLLAR_CMD will also match.
532*3446Smrj 			 */
533*3446Smrj 			if (strncmp(cur_line->cmd, menu_cmds[KERNEL_CMD],
534*3446Smrj 			    strlen(menu_cmds[KERNEL_CMD])) == 0) {
535*3446Smrj 				rv = parse_kernel_line(cur_line, root,
536*3446Smrj 				    &(cur_entry->flags));
537*3446Smrj 				if (rv == BAM_SKIP) {
538*3446Smrj 					break;
539*3446Smrj 				} else if (rv != BAM_SUCCESS) {
540*3446Smrj 					return (rv);
541*3446Smrj 				}
542*3446Smrj 				found_kernel = B_TRUE;
543*3446Smrj 			} else if (strncmp(cur_line->cmd,
544*3446Smrj 			    menu_cmds[MODULE_CMD],
545*3446Smrj 			    strlen(menu_cmds[MODULE_CMD])) == 0) {
546*3446Smrj 				rv = parse_module_line(cur_line, root,
547*3446Smrj 				    cur_entry->flags);
548*3446Smrj 				if (rv != BAM_SUCCESS) {
549*3446Smrj 					return (rv);
550*3446Smrj 				}
551*3446Smrj 			}
552*3446Smrj 			if (cur_line == cur_entry->end)
553*3446Smrj 				break;
554*3446Smrj 		}
555*3446Smrj 	}
556*3446Smrj 
557*3446Smrj 	/*
558*3446Smrj 	 * We only want to output one error, to avoid confusing a user.  We
559*3446Smrj 	 * rank "No kernels changed" as a higher priority than "will not
560*3446Smrj 	 * update hand-added entries", since the former implies the latter.
561*3446Smrj 	 */
562*3446Smrj 	if (found_kernel == B_FALSE) {
563*3446Smrj 		bam_error(NO_KERNELS_FOUND, MENU_URL(root));
564*3446Smrj 		return (BAM_ERROR);
565*3446Smrj 	} else if (num_entries > 0) {
566*3446Smrj 		bam_error(HAND_ADDED_ENTRY, MENU_URL(root));
567*3446Smrj 		bam_print_stderr("Entry Number%s: ", (num_entries > 1) ?
568*3446Smrj 		    "s" : "");
569*3446Smrj 		for (i = 0; i < num_entries; i++) {
570*3446Smrj 			bam_print_stderr("%d ", hand_entries[i]);
571*3446Smrj 		}
572*3446Smrj 		bam_print_stderr("\n");
573*3446Smrj 	}
574*3446Smrj 	return (BAM_WRITE);
575*3446Smrj }
576