bootadm_upgrade.c revision 5648:161f8007cab9
1251875Speter/*
2251875Speter * CDDL HEADER START
3251875Speter *
4251875Speter * The contents of this file are subject to the terms of the
5251875Speter * Common Development and Distribution License (the "License").
6251875Speter * You may not use this file except in compliance with the License.
7251875Speter *
8251875Speter * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9251875Speter * or http://www.opensolaris.org/os/licensing.
10251875Speter * See the License for the specific language governing permissions
11251875Speter * and limitations under the License.
12251875Speter *
13251875Speter * When distributing Covered Code, include this CDDL HEADER in each
14251875Speter * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15251875Speter * If applicable, add the following below this CDDL HEADER, with the
16251875Speter * fields enclosed by brackets "[]" replaced with your own identifying
17251875Speter * information: Portions Copyright [yyyy] [name of copyright owner]
18251875Speter *
19251875Speter * CDDL HEADER END
20251875Speter */
21251875Speter/*
22251875Speter * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23251875Speter * Use is subject to license terms.
24251875Speter */
25251875Speter
26251875Speter#pragma ident	"%Z%%M%	%I%	%E% SMI"
27251875Speter
28251875Speter#include <stdio.h>
29251875Speter#include <errno.h>
30251875Speter#include <stdlib.h>
31251875Speter#include <string.h>
32251875Speter#include <unistd.h>
33251875Speter#include <sys/types.h>
34251875Speter#include <sys/stat.h>
35251875Speter#include <limits.h>
36251875Speter#include <fcntl.h>
37251875Speter#include <strings.h>
38251875Speter
39251875Speter#include <sys/mman.h>
40251875Speter#include <sys/elf.h>
41251875Speter#include <sys/multiboot.h>
42251875Speter
43251875Speter#include "message.h"
44251875Speter#include "bootadm.h"
45251875Speter
46251875Speterdirect_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
47251875Speterhv_t bam_is_hv = BAM_HV_UNKNOWN;
48251875Speter
49251875Spetererror_t
50251875Speterdboot_or_multiboot(const char *root)
51251875Speter{
52251875Speter	char fname[PATH_MAX];
53251875Speter	char *image;
54251875Speter	uchar_t *ident;
55251875Speter	int fd, m;
56251875Speter	multiboot_header_t *mbh;
57251875Speter	struct stat sb;
58251875Speter
59251875Speter	if (!is_grub(root)) {
60251875Speter		/* there is no non dboot sparc new-boot */
61251875Speter		bam_direct = BAM_DIRECT_DBOOT;
62251875Speter		return (BAM_SUCCESS);
63251875Speter	}
64251875Speter
65251875Speter	(void) snprintf(fname, PATH_MAX, "%s/%s", root,
66251875Speter	    "platform/i86pc/kernel/unix");
67251875Speter	fd = open(fname, O_RDONLY);
68251875Speter	if (fd < 0) {
69251875Speter		bam_error(OPEN_FAIL, fname, strerror(errno));
70251875Speter		return (BAM_ERROR);
71251875Speter	}
72251875Speter
73251875Speter	/*
74251875Speter	 * mmap the first 8K
75251875Speter	 */
76251875Speter	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
77251875Speter	if (image == MAP_FAILED) {
78251875Speter		bam_error(MMAP_FAIL, fname, strerror(errno));
79251875Speter		return (BAM_ERROR);
80251875Speter	}
81251875Speter
82251875Speter	ident = (uchar_t *)image;
83251875Speter	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
84251875Speter	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
85251875Speter		bam_error(NOT_ELF_FILE, fname);
86251875Speter		return (BAM_ERROR);
87251875Speter	}
88251875Speter	if (ident[EI_CLASS] != ELFCLASS32) {
89251875Speter		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
90251875Speter		return (BAM_ERROR);
91251875Speter	}
92251875Speter
93251875Speter	/*
94251875Speter	 * The GRUB multiboot header must be 32-bit aligned and completely
95251875Speter	 * contained in the 1st 8K of the file.  If the unix binary has
96251875Speter	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
97251875Speter	 * this kernel must be booted via multiboot -- we call this a
98251875Speter	 * 'multiboot' kernel.
99251875Speter	 */
100251875Speter	bam_direct = BAM_DIRECT_MULTIBOOT;
101251875Speter	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
102251875Speter		mbh = (void *)(image + m);
103251875Speter		if (mbh->magic == MB_HEADER_MAGIC) {
104251875Speter			bam_direct = BAM_DIRECT_DBOOT;
105251875Speter			break;
106251875Speter		}
107251875Speter	}
108251875Speter	(void) munmap(image, 8192);
109251875Speter	(void) close(fd);
110251875Speter
111251875Speter	if (bam_direct == BAM_DIRECT_DBOOT) {
112251875Speter		(void) snprintf(fname, PATH_MAX, "%s/%s", root, XEN_32);
113251875Speter		if (stat(fname, &sb) == 0) {
114251875Speter			bam_is_hv = BAM_HV_PRESENT;
115251875Speter		} else {
116251875Speter			bam_is_hv = BAM_HV_NO;
117251875Speter		}
118251875Speter	}
119251875Speter
120251875Speter	return (BAM_SUCCESS);
121251875Speter}
122251875Speter
123251875Speter#define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
124251875Speter
125251875Speter/*
126251875Speter * Return true if root has been bfu'ed.  bfu will blow away
127251875Speter * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
128251875Speter * assume the system has not been bfu'ed.
129251875Speter */
130251875Speterstatic int
131251875Speteris_bfu_system(const char *root)
132251875Speter{
133251875Speter	static int is_bfu = -1;
134251875Speter	char path[PATH_MAX];
135251875Speter	struct stat sb;
136251875Speter
137251875Speter	if (is_bfu != -1)
138251875Speter		return (is_bfu);
139251875Speter
140251875Speter	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
141251875Speter	if (stat(path, &sb) != 0) {
142251875Speter		is_bfu = 1;
143251875Speter	} else {
144251875Speter		is_bfu = 0;
145251875Speter	}
146251875Speter	return (is_bfu);
147251875Speter}
148251875Speter
149251875Speter#define	MENU_URL(root)	(is_bfu_system(root) ?		\
150251875Speter	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
151251875Speter	"http://www.sun.com/msg/SUNOS-8000-AK")
152251875Speter
153251875Speter/*
154251875Speter * Simply allocate a new line and copy in cmd + sep + arg
155251875Speter */
156251875Spetervoid
157251875Speterupdate_line(line_t *linep)
158251875Speter{
159251875Speter	size_t size;
160251875Speter
161251875Speter	free(linep->line);
162251875Speter	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
163251875Speter	linep->line = s_calloc(1, size);
164251875Speter	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
165251875Speter	    linep->arg);
166251875Speter}
167251875Speter
168251875Speter/*
169251875Speter * The parse_kernel_line function examines a menu.lst kernel line.  For
170251875Speter * multiboot, this is:
171251875Speter *
172251875Speter * kernel <multiboot path> <flags1> <kernel path> <flags2>
173251875Speter *
174251875Speter * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
175251875Speter *
176251875Speter * <kernel path> may be missing, or may be any full or relative path to unix.
177251875Speter *	We check for it by looking for a word ending in "/unix".  If it ends
178251875Speter *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
179251875Speter *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
180251875Speter *	it's a custom kernel, and we skip it.
181251875Speter *
182251875Speter * <flags*> are anything that doesn't fit either of the above - these will be
183251875Speter *	copied over.
184251875Speter *
185251875Speter * For direct boot, the defaults are
186251875Speter *
187251875Speter * kernel$ <kernel path> <flags>
188251875Speter *
189251875Speter * <kernel path> is one of:
190251875Speter *	/platform/i86pc/kernel/$ISADIR/unix
191251875Speter *	/platform/i86pc/kernel/unix
192251875Speter *	/platform/i86pc/kernel/amd64/unix
193251875Speter *	/boot/platform/i86pc/kernel/unix
194251875Speter *
195251875Speter * If <kernel path> is any of the last three, the command may also be "kernel".
196251875Speter *
197251875Speter * <flags> is anything that isn't <kernel path>.
198251875Speter *
199251875Speter * This function is only called if it applies to our target boot environment.
200251875Speter * If we can't make any sense of the kernel line, an error is printed and
201251875Speter * BAM_ERROR is returned.
202251875Speter *
203251875Speter * The desired install type is given in the global variable bam_direct.
204251875Speter * If the kernel line is of a different install type, we change it to the
205251875Speter * preferred type.  If the kernel line is already of the correct install
206251875Speter * type, we do nothing.  Either way, BAM_SUCCESS is returned.
207251875Speter *
208251875Speter * For safety, we do one more check: if the kernel path starts with /boot,
209251875Speter * we verify that the new kernel exists before changing it.  This is mainly
210251875Speter * done for bfu, as it may cause the failsafe archives to be a different
211251875Speter * boot architecture from the newly bfu'ed system.
212251875Speter */
213251875Speterstatic error_t
214251875Speterparse_kernel_line(line_t *linep, const char *root, uint8_t *flags)
215251875Speter{
216251875Speter	char path[PATH_MAX];
217251875Speter	int len, left, total_len;
218251875Speter	struct stat sb;
219251875Speter	char *new_ptr, *new_arg, *old_ptr;
220251875Speter	menu_cmd_t which;
221251875Speter
222251875Speter	/* Used when changing a multiboot line to dboot */
223251875Speter	char *unix_ptr, *flags1_ptr, *flags2_ptr;
224251875Speter
225251875Speter	/*
226251875Speter	 * Note that BAM_ENTRY_DBOOT refers to the entry we're looking at, not
227251875Speter	 * necessarily the system type.
228251875Speter	 */
229251875Speter	if (strncmp(linep->arg, DIRECT_BOOT_32,
230251875Speter	    sizeof (DIRECT_BOOT_32) - 1) == 0) {
231251875Speter		*flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
232251875Speter	} else if ((strncmp(linep->arg, DIRECT_BOOT_KERNEL,
233251875Speter	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) ||
234251875Speter	    (strncmp(linep->arg, DIRECT_BOOT_64,
235251875Speter	    sizeof (DIRECT_BOOT_64) - 1) == 0) ||
236251875Speter	    (strncmp(linep->arg, DIRECT_BOOT_FAILSAFE_KERNEL,
237251875Speter	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0)) {
238251875Speter		*flags |= BAM_ENTRY_DBOOT;
239251875Speter	} else if ((strncmp(linep->arg, MULTI_BOOT,
240251875Speter	    sizeof (MULTI_BOOT) - 1) == 0) ||
241251875Speter	    (strncmp(linep->arg, MULTI_BOOT_FAILSAFE,
242251875Speter	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0)) {
243251875Speter		*flags &= ~BAM_ENTRY_DBOOT;
244251875Speter	} else {
245251875Speter		bam_error(NO_KERNEL_MATCH, linep->lineNum, MENU_URL(root));
246251875Speter		return (BAM_ERROR);
247251875Speter	}
248251875Speter
249251875Speter	if (((*flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
250251875Speter	    (((*flags & BAM_ENTRY_DBOOT) == 0) &&
251251875Speter	    (bam_direct == BAM_DIRECT_MULTIBOOT))) {
252
253		/* No action needed */
254		return (BAM_SUCCESS);
255	}
256
257	if (*flags & BAM_ENTRY_MINIROOT) {
258		/*
259		 * We're changing boot architectures - make sure
260		 * the multiboot failsafe still exists.
261		 */
262		(void) snprintf(path, PATH_MAX, "%s%s", root,
263		    (*flags & BAM_ENTRY_DBOOT) ? MULTI_BOOT_FAILSAFE :
264		    DIRECT_BOOT_FAILSAFE_KERNEL);
265		if (stat(path, &sb) != 0) {
266			if (bam_verbose) {
267				bam_error(FAILSAFE_MISSING, linep->lineNum);
268			}
269			return (BAM_SUCCESS);
270		}
271	}
272
273	/*
274	 * Make sure we have the correct cmd - either kernel or kernel$
275	 * The failsafe entry should always be KERNEL_CMD.
276	 */
277	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
278	    (*flags & BAM_ENTRY_MINIROOT)) ? KERNEL_CMD : KERNEL_DOLLAR_CMD;
279	free(linep->cmd);
280	len = strlen(menu_cmds[which]) + 1;
281	linep->cmd = s_calloc(1, len);
282	(void) strncpy(linep->cmd, menu_cmds[which], len);
283
284	/*
285	 * Since all arguments are copied, the new arg string should be close
286	 * in size to the old one.  Just add 32 to cover the difference in
287	 * the boot path.
288	 */
289	total_len = strlen(linep->arg) + 32;
290	new_arg = s_calloc(1, total_len);
291	old_ptr = strchr(linep->arg, ' ');
292	if (old_ptr != NULL)
293		old_ptr++;
294
295	/*
296	 * Transitioning from dboot to multiboot is pretty simple.  We
297	 * copy in multiboot and any args.
298	 */
299	if (bam_direct == BAM_DIRECT_MULTIBOOT) {
300		if (old_ptr == NULL) {
301			(void) snprintf(new_arg, total_len, "%s",
302			    (*flags & BAM_ENTRY_MINIROOT) ?
303			    MULTI_BOOT_FAILSAFE : MULTI_BOOT);
304		} else {
305			(void) snprintf(new_arg, total_len, "%s %s",
306			    (*flags & BAM_ENTRY_MINIROOT) ?
307			    MULTI_BOOT_FAILSAFE : MULTI_BOOT, old_ptr);
308		}
309		goto done;
310	}
311
312	/*
313	 * Transitioning from multiboot to directboot is a bit more
314	 * complicated, since we may have two sets of arguments to
315	 * copy and a unix path to parse.
316	 *
317	 * First, figure out if there's a unix path.
318	 */
319	if ((old_ptr != NULL) &&
320	    ((unix_ptr = strstr(old_ptr, "/unix")) != NULL)) {
321		/* See if there's anything past unix */
322		flags2_ptr = unix_ptr + sizeof ("/unix");
323		if (*flags2_ptr == '\0') {
324			flags2_ptr = NULL;
325		}
326
327		while ((unix_ptr > old_ptr) && (*unix_ptr != ' '))
328			unix_ptr--;
329
330		if (unix_ptr == old_ptr) {
331			flags1_ptr = NULL;
332		} else {
333			flags1_ptr = old_ptr;
334		}
335
336		if (strstr(unix_ptr, "kernel/unix") != NULL) {
337			*flags |= BAM_ENTRY_32BIT;
338		} else if ((strstr(unix_ptr, "kernel/amd64/unix") == NULL) &&
339		    (!bam_force)) {
340			/*
341			 * If the above strstr returns NULL, but bam_force is
342			 * set, we'll be upgrading an Install kernel.  The
343			 * result probably won't be what was intended, but we'll
344			 * try it anyways.
345			 */
346			return (BAM_SKIP);
347		}
348	} else if (old_ptr != NULL) {
349		flags1_ptr = old_ptr;
350		unix_ptr = flags1_ptr + strlen(old_ptr);
351		flags2_ptr = NULL;
352	} else {
353		unix_ptr = flags1_ptr = flags2_ptr = NULL;
354	}
355
356	if (*flags & BAM_ENTRY_MINIROOT) {
357		(void) snprintf(new_arg, total_len, "%s",
358		    DIRECT_BOOT_FAILSAFE_KERNEL);
359	} else if (*flags & BAM_ENTRY_32BIT) {
360		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_32);
361	} else {
362		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_KERNEL);
363	}
364
365	/*
366	 * We now want to copy flags1_ptr through unix_ptr, and
367	 * flags2_ptr through the end of the string
368	 */
369	if (flags1_ptr != NULL) {
370		len = strlcat(new_arg, " ", total_len);
371		left = total_len - len;
372		new_ptr = new_arg + len;
373
374		if ((unix_ptr - flags1_ptr) < left)
375			left = (unix_ptr - flags1_ptr) + 1;
376		(void) strlcpy(new_ptr, flags1_ptr, left);
377	}
378	if (flags2_ptr != NULL) {
379		(void) strlcat(new_arg, " ", total_len);
380		(void) strlcat(new_arg, flags2_ptr, total_len);
381	}
382
383done:
384	free(linep->arg);
385	linep->arg = new_arg;
386	update_line(linep);
387	return (BAM_SUCCESS);
388}
389
390/*
391 * Similar to above, except this time we're looking at a module line,
392 * which is quite a bit simpler.
393 *
394 * Under multiboot, the archive line is:
395 *
396 * module /platform/i86pc/boot_archive
397 *
398 * Under directboot, the archive line is:
399 *
400 * module$ /platform/i86pc/$ISADIR/boot_archive
401 *
402 * which may be specified exactly as either of:
403 *
404 * module /platform/i86pc/boot_archive
405 * module /platform/i86pc/amd64/boot_archive
406 *
407 * For either dboot or multiboot, the failsafe is:
408 *
409 * module /boot/x86.miniroot-safe
410 */
411static error_t
412parse_module_line(line_t *linep, const char *root, uint8_t flags)
413{
414	int len;
415	menu_cmd_t which;
416	char *new;
417
418	/*
419	 * If necessary, BAM_ENTRY_MINIROOT was already set in flags
420	 * in upgrade_menu().  We re-check BAM_ENTRY_DBOOT here in here
421	 * in case the kernel and module lines differ.
422	 */
423	if ((strcmp(linep->arg, DIRECT_BOOT_ARCHIVE) == 0) ||
424	    (strcmp(linep->arg, DIRECT_BOOT_ARCHIVE_64) == 0)) {
425		flags |= BAM_ENTRY_DBOOT;
426	} else if ((strcmp(linep->arg, MULTI_BOOT_ARCHIVE) == 0) ||
427	    (strcmp(linep->arg, MINIROOT) == 0)) {
428		flags &= ~BAM_ENTRY_DBOOT;
429	} else {
430		bam_error(NO_MODULE_MATCH, linep->lineNum, MENU_URL(root));
431		return (BAM_ERROR);
432	}
433
434	if (((flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
435	    (((flags & BAM_ENTRY_DBOOT) == 0) &&
436	    (bam_direct == BAM_DIRECT_MULTIBOOT)) ||
437	    ((flags & BAM_ENTRY_MINIROOT) &&
438	    (strcmp(linep->cmd, menu_cmds[MODULE_CMD]) == 0))) {
439
440		/* No action needed */
441		return (BAM_SUCCESS);
442	}
443
444	/*
445	 * Make sure we have the correct cmd - either module or module$
446	 * The failsafe entry should always be MODULE_CMD.
447	 */
448	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
449	    (flags & BAM_ENTRY_MINIROOT)) ? MODULE_CMD : MODULE_DOLLAR_CMD;
450	free(linep->cmd);
451	len = strlen(menu_cmds[which]) + 1;
452	linep->cmd = s_calloc(1, len);
453	(void) strncpy(linep->cmd, menu_cmds[which], len);
454
455	if (flags & BAM_ENTRY_MINIROOT) {
456		new = MINIROOT;
457	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
458	    ((flags & BAM_ENTRY_32BIT) == 0)) {
459		new = DIRECT_BOOT_ARCHIVE;
460	} else {
461		new = MULTI_BOOT_ARCHIVE;
462	}
463
464	free(linep->arg);
465	len = strlen(new) + 1;
466	linep->arg = s_calloc(1, len);
467	(void) strncpy(linep->arg, new, len);
468	update_line(linep);
469
470	return (BAM_SUCCESS);
471}
472
473/*ARGSUSED*/
474error_t
475upgrade_menu(menu_t *mp, char *root, char *opt)
476{
477	entry_t	*cur_entry;
478	line_t	*cur_line;
479	int	i, skipit, num_entries, found_hv;
480	int	*hand_entries = NULL;
481	boolean_t found_kernel = B_FALSE;
482	error_t	rv;
483	char	*rootdev, *grubdisk = NULL;
484
485	skipit = num_entries = found_hv = 0;
486
487	rootdev = get_special(root);
488	if (rootdev) {
489		grubdisk = os_to_grubdisk(rootdev, strlen(root) == 1);
490		free(rootdev);
491		rootdev = NULL;
492	}
493
494	/* Loop through all OS entries in the menu.lst file */
495	for (cur_entry = mp->entries; cur_entry != NULL;
496	    cur_entry = cur_entry->next, skipit = 0) {
497
498		if ((cur_entry->flags & BAM_ENTRY_CHAINLOADER) ||
499		    ((cur_entry->flags & BAM_ENTRY_MINIROOT) && !bam_force))
500			continue;
501
502		/*
503		 * We only change entries added by bootadm and live upgrade,
504		 * and warn on the rest, unless the -f flag was passed.
505		 */
506		if ((!(cur_entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) &&
507		    !bam_force) {
508			if (num_entries == 0) {
509				hand_entries = s_calloc(1, sizeof (int));
510			} else {
511				hand_entries = s_realloc(hand_entries,
512				    (num_entries + 1) * sizeof (int));
513			}
514			hand_entries[num_entries++] = cur_entry->entryNum;
515			continue;
516		}
517
518		if (cur_entry->flags & BAM_ENTRY_HV) {
519			found_hv = 1;
520			continue;
521		}
522
523		/*
524		 * We make two loops through the lines.  First, we check if
525		 * there is a root entry, and if so, whether we should be
526		 * checking this entry.
527		 */
528		if ((grubdisk != NULL) && (cur_entry->flags & BAM_ENTRY_ROOT)) {
529			for (cur_line = cur_entry->start; cur_line != NULL;
530			    cur_line = cur_line->next) {
531				if ((cur_line->cmd == NULL) ||
532				    (cur_line->arg == NULL))
533					continue;
534
535				if (strcmp(cur_line->cmd,
536				    menu_cmds[ROOT_CMD]) == 0) {
537					if (strcmp(cur_line->arg,
538					    grubdisk) != 0) {
539						/* A different slice */
540						skipit = 1;
541					}
542					break;
543				}
544				if (cur_line == cur_entry->end)
545					break;
546			}
547		}
548		if (skipit)
549			continue;
550
551		for (cur_line = cur_entry->start; cur_line != NULL;
552		    cur_line = cur_line->next) {
553
554			/*
555			 * We only compare for the length of KERNEL_CMD,
556			 * so that KERNEL_DOLLAR_CMD will also match.
557			 */
558			if (strncmp(cur_line->cmd, menu_cmds[KERNEL_CMD],
559			    strlen(menu_cmds[KERNEL_CMD])) == 0) {
560				rv = parse_kernel_line(cur_line, root,
561				    &(cur_entry->flags));
562				if (rv == BAM_SKIP) {
563					break;
564				} else if (rv != BAM_SUCCESS) {
565					return (rv);
566				}
567				found_kernel = B_TRUE;
568			} else if (strncmp(cur_line->cmd,
569			    menu_cmds[MODULE_CMD],
570			    strlen(menu_cmds[MODULE_CMD])) == 0) {
571				rv = parse_module_line(cur_line, root,
572				    cur_entry->flags);
573				if (rv != BAM_SUCCESS) {
574					return (rv);
575				}
576			}
577			if (cur_line == cur_entry->end)
578				break;
579		}
580	}
581
582	/*
583	 * If we're upgrading to a virtualized kernel and there are no
584	 * hv entries in menu.lst, we need to add one.
585	 */
586	if ((bam_is_hv == BAM_HV_PRESENT) && (found_hv == 0)) {
587		(void) add_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
588		    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE);
589	}
590
591	/*
592	 * We only want to output one error, to avoid confusing a user.  We
593	 * rank "No kernels changed" as a higher priority than "will not
594	 * update hand-added entries", since the former implies the latter.
595	 */
596	if (found_kernel == B_FALSE) {
597		bam_error(NO_KERNELS_FOUND, MENU_URL(root));
598		return (BAM_ERROR);
599	} else if (num_entries > 0) {
600		bam_error(HAND_ADDED_ENTRY, MENU_URL(root));
601		bam_print_stderr("Entry Number%s: ", (num_entries > 1) ?
602		    "s" : "");
603		for (i = 0; i < num_entries; i++) {
604			bam_print_stderr("%d ", hand_entries[i]);
605		}
606		bam_print_stderr("\n");
607	}
608	return (BAM_WRITE);
609}
610