bootadm_upgrade.c revision 10405:505c385fdfdc
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <errno.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <limits.h>
34#include <fcntl.h>
35#include <strings.h>
36
37#include <sys/mman.h>
38#include <sys/elf.h>
39#include <sys/multiboot.h>
40
41#include "message.h"
42#include "bootadm.h"
43
44direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
45hv_t bam_is_hv = BAM_HV_UNKNOWN;
46findroot_t bam_is_findroot = BAM_FINDROOT_UNKNOWN;
47
48static void
49get_findroot_cap(const char *osroot)
50{
51	FILE		*fp;
52	char		path[PATH_MAX];
53	char		buf[BAM_MAXLINE];
54	struct stat	sb;
55	int		dboot;
56	int		error;
57	int		ret;
58	const char	*fcn = "get_findroot_cap()";
59
60	assert(is_grub(osroot));
61
62	(void) snprintf(path, sizeof (path), "%s/%s",
63	    osroot, "boot/grub/capability");
64
65	if (stat(path, &sb) == -1) {
66		bam_is_findroot = BAM_FINDROOT_ABSENT;
67		BAM_DPRINTF((D_FINDROOT_ABSENT, fcn));
68		return;
69	}
70
71	fp = fopen(path, "r");
72	error = errno;
73	INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL);
74	if (fp == NULL) {
75		bam_error(OPEN_FAIL, path, strerror(error));
76		return;
77	}
78
79	dboot = 0;
80	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
81		if (strcmp(buf, "findroot") == 0) {
82			BAM_DPRINTF((D_FINDROOT_PRESENT, fcn));
83			bam_is_findroot = BAM_FINDROOT_PRESENT;
84		}
85		if (strcmp(buf, "dboot") == 0) {
86			BAM_DPRINTF((D_DBOOT_PRESENT, fcn));
87			dboot = 1;
88		}
89	}
90
91	assert(dboot);
92
93	if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) {
94		bam_is_findroot = BAM_FINDROOT_ABSENT;
95		BAM_DPRINTF((D_FINDROOT_ABSENT, fcn));
96	}
97out:
98	ret = fclose(fp);
99	error = errno;
100	INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1);
101	if (ret != 0) {
102		bam_error(CLOSE_FAIL, path, strerror(error));
103	}
104}
105
106error_t
107get_boot_cap(const char *osroot)
108{
109	char		fname[PATH_MAX];
110	char		*image;
111	uchar_t		*ident;
112	int		fd;
113	int		m;
114	multiboot_header_t *mbh;
115	struct stat	sb;
116	int		error;
117	const char	*fcn = "get_boot_cap()";
118
119	if (is_sparc()) {
120		/* there is no non dboot sparc new-boot */
121		bam_direct = BAM_DIRECT_DBOOT;
122		BAM_DPRINTF((D_IS_SPARC_DBOOT, fcn));
123		return (BAM_SUCCESS);
124	}
125
126	if (!is_grub(osroot)) {
127		bam_error(NOT_GRUB_ROOT, osroot);
128		return (BAM_ERROR);
129	}
130
131	(void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
132	    "platform/i86pc/kernel/unix");
133	fd = open(fname, O_RDONLY);
134	error = errno;
135	INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1);
136	if (fd < 0) {
137		bam_error(OPEN_FAIL, fname, strerror(error));
138		return (BAM_ERROR);
139	}
140
141	/*
142	 * Verify that this is a sane unix at least 8192 bytes in length
143	 */
144	if (fstat(fd, &sb) == -1 || sb.st_size < 8192) {
145		(void) close(fd);
146		bam_error(INVALID_BINARY, fname);
147		return (BAM_ERROR);
148	}
149
150	/*
151	 * mmap the first 8K
152	 */
153	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
154	error = errno;
155	INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED);
156	if (image == MAP_FAILED) {
157		bam_error(MMAP_FAIL, fname, strerror(error));
158		return (BAM_ERROR);
159	}
160
161	ident = (uchar_t *)image;
162	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
163	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
164		bam_error(NOT_ELF_FILE, fname);
165		return (BAM_ERROR);
166	}
167	if (ident[EI_CLASS] != ELFCLASS32) {
168		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
169		return (BAM_ERROR);
170	}
171
172	/*
173	 * The GRUB multiboot header must be 32-bit aligned and completely
174	 * contained in the 1st 8K of the file.  If the unix binary has
175	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
176	 * this kernel must be booted via multiboot -- we call this a
177	 * 'multiboot' kernel.
178	 */
179	bam_direct = BAM_DIRECT_MULTIBOOT;
180	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
181		mbh = (void *)(image + m);
182		if (mbh->magic == MB_HEADER_MAGIC) {
183			BAM_DPRINTF((D_IS_DBOOT, fcn));
184			bam_direct = BAM_DIRECT_DBOOT;
185			break;
186		}
187	}
188	(void) munmap(image, 8192);
189	(void) close(fd);
190
191	INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
192	if (bam_direct == BAM_DIRECT_DBOOT) {
193		if (bam_is_hv == BAM_HV_PRESENT) {
194			BAM_DPRINTF((D_IS_XVM, fcn));
195		} else {
196			BAM_DPRINTF((D_IS_NOT_XVM, fcn));
197		}
198	} else {
199		BAM_DPRINTF((D_IS_MULTIBOOT, fcn));
200	}
201
202	/* Not a fatal error if this fails */
203	get_findroot_cap(osroot);
204
205	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
206	return (BAM_SUCCESS);
207}
208
209#define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
210
211/*
212 * Return true if root has been bfu'ed.  bfu will blow away
213 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
214 * assume the system has not been bfu'ed.
215 */
216static int
217is_bfu_system(const char *root)
218{
219	static int		is_bfu = -1;
220	char			path[PATH_MAX];
221	struct stat		sb;
222	const char		*fcn = "is_bfu_system()";
223
224	if (is_bfu != -1) {
225		BAM_DPRINTF((D_ALREADY_BFU_TEST, fcn, is_bfu ? "" : "NOT"));
226		return (is_bfu);
227	}
228
229	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
230	if (stat(path, &sb) != 0) {
231		is_bfu = 1;
232		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
233	} else {
234		is_bfu = 0;
235		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
236	}
237	return (is_bfu);
238}
239
240#define	MENU_URL(root)	(is_bfu_system(root) ?		\
241	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
242	"http://www.sun.com/msg/SUNOS-8000-AK")
243
244/*
245 * Simply allocate a new line and copy in cmd + sep + arg
246 */
247void
248update_line(line_t *linep)
249{
250	size_t		size;
251	const char	*fcn = "update_line()";
252
253	BAM_DPRINTF((D_UPDATE_LINE_BEFORE, fcn, linep->line));
254	free(linep->line);
255	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
256	linep->line = s_calloc(1, size);
257	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
258	    linep->arg);
259	BAM_DPRINTF((D_UPDATE_LINE_AFTER, fcn, linep->line));
260}
261
262static char *
263skip_wspace(char *ptr)
264{
265	const char		*fcn = "skip_wspace()";
266
267	INJECT_ERROR1("SKIP_WSPACE", ptr = NULL);
268	if (ptr == NULL) {
269		BAM_DPRINTF((D_SKIP_WSPACE_PTR_NULL, fcn));
270		return (NULL);
271	}
272
273	BAM_DPRINTF((D_SKIP_WSPACE_ENTRY_PTR, fcn, ptr));
274	for (; *ptr != '\0'; ptr++) {
275		if ((*ptr != ' ') && (*ptr != '\t') &&
276		    (*ptr != '\n'))
277			break;
278	}
279
280	ptr = (*ptr == '\0' ? NULL : ptr);
281
282	BAM_DPRINTF((D_SKIP_WSPACE_EXIT_PTR, fcn, ptr ? ptr : "NULL"));
283
284	return (ptr);
285}
286
287static char *
288rskip_bspace(char *bound, char *ptr)
289{
290	const char		*fcn = "rskip_bspace()";
291	assert(bound);
292	assert(ptr);
293	assert(bound <= ptr);
294	assert(*bound != ' ' && *bound != '\t' && *bound != '\n');
295
296	BAM_DPRINTF((D_RSKIP_BSPACE_ENTRY, fcn, ptr));
297	for (; ptr > bound; ptr--) {
298		if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
299			break;
300	}
301
302	BAM_DPRINTF((D_RSKIP_BSPACE_EXIT, fcn, ptr));
303	return (ptr);
304}
305
306/*
307 * The parse_kernel_line function examines a menu.lst kernel line.  For
308 * multiboot, this is:
309 *
310 * kernel <multiboot path> <flags1> <kernel path> <flags2>
311 *
312 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
313 *
314 * <kernel path> may be missing, or may be any full or relative path to unix.
315 *	We check for it by looking for a word ending in "/unix".  If it ends
316 *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
317 *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
318 *	it's a custom kernel, and we skip it.
319 *
320 * <flags*> are anything that doesn't fit either of the above - these will be
321 *	copied over.
322 *
323 * For direct boot, the defaults are
324 *
325 * kernel$ <kernel path> <flags>
326 *
327 * <kernel path> is one of:
328 *	/platform/i86pc/kernel/$ISADIR/unix
329 *	/boot/platform/i86pc/kernel/$ISADIR/unix
330 *	/platform/i86pc/kernel/unix
331 *	/platform/i86pc/kernel/amd64/unix
332 *	/boot/platform/i86pc/kernel/unix
333 *	/boot/platform/i86pc/kernel/amd64/unix
334 *
335 * If <kernel path> is any of the last four, the command may also be "kernel".
336 *
337 * <flags> is anything that isn't <kernel path>.
338 *
339 * This function is only called to convert a multiboot entry to a dboot entry
340 *
341 * For safety, we do one more check: if the kernel path starts with /boot,
342 * we verify that the new kernel exists before changing it.  This is mainly
343 * done for bfu, as it may cause the failsafe archives to be a different
344 * boot architecture from the newly bfu'ed system.
345 */
346static error_t
347cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry)
348{
349	char		path[PATH_MAX], path_64[PATH_MAX];
350	char		linebuf[PATH_MAX];
351	char		new_arg[PATH_MAX];
352	struct stat	sb, sb_64;
353	char		*old_ptr;
354	char		*unix_ptr;
355	char		*flags1_ptr;
356	char		*flags2_ptr;
357	const char	*fcn = "cvt_kernel_line()";
358
359	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, line->line, osroot));
360
361	/*
362	 * We only convert multiboot to dboot and nothing else.
363	 */
364	if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
365		BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn));
366		return (BAM_SUCCESS);
367	}
368
369	if (entry->flags & BAM_ENTRY_FAILSAFE) {
370		/*
371		 * We're attempting to change failsafe to dboot.
372		 * In the bfu case, we may not have a dboot failsafe
373		 * kernel i.e. a "unix" under the "/boot" hierarchy.
374		 * If so, just emit a message in verbose mode and
375		 * return success.
376		 */
377		BAM_DPRINTF((D_TRYING_FAILSAFE_CVT_TO_DBOOT, fcn));
378		(void) snprintf(path, PATH_MAX, "%s%s", osroot,
379		    DIRECT_BOOT_FAILSAFE_32);
380		(void) snprintf(path_64, PATH_MAX, "%s%s", osroot,
381		    DIRECT_BOOT_FAILSAFE_64);
382		if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) {
383			if (bam_verbose) {
384				bam_error(FAILSAFE_MISSING, line->lineNum);
385			}
386			BAM_DPRINTF((D_NO_FAILSAFE_UNIX_CONVERT, fcn));
387			return (BAM_SUCCESS);
388		}
389	}
390
391	/*
392	 * Make sure we have the correct cmd
393	 */
394
395	free(line->cmd);
396	line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
397	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, line->cmd));
398
399	assert(sizeof (linebuf) > strlen(line->arg) + 32);
400	(void) strlcpy(linebuf, line->arg, sizeof (linebuf));
401
402	old_ptr = strpbrk(linebuf, " \t\n");
403	old_ptr = skip_wspace(old_ptr);
404	if (old_ptr == NULL) {
405		/*
406		 * only multiboot and nothing else
407		 * i.e. flags1 = unix = flags2 = NULL
408		 */
409		flags1_ptr = unix_ptr = flags2_ptr = NULL;
410		BAM_DPRINTF((D_FLAGS1_UNIX_FLAGS2_NULL, fcn))
411		goto create;
412	}
413
414	/*
415	 *
416	 * old_ptr is either at "flags1" or "unix"
417	 */
418	if (unix_ptr = strstr(old_ptr, "/unix")) {
419
420		/*
421		 * There is a  unix.
422		 */
423		BAM_DPRINTF((D_UNIX_PRESENT, fcn));
424
425		/* See if there's a flags2 past unix */
426		flags2_ptr = unix_ptr + strlen("/unix");
427		flags2_ptr = skip_wspace(flags2_ptr);
428		if (flags2_ptr) {
429			BAM_DPRINTF((D_FLAGS2_PRESENT, fcn, flags2_ptr));
430		} else {
431			BAM_DPRINTF((D_FLAGS2_ABSENT, fcn));
432		}
433
434		/* see if there is a flags1 before unix */
435		unix_ptr = rskip_bspace(old_ptr, unix_ptr);
436
437		if (unix_ptr == old_ptr) {
438			flags1_ptr = NULL;
439			BAM_DPRINTF((D_FLAGS1_ABSENT, fcn));
440		} else {
441			flags1_ptr = old_ptr;
442			*unix_ptr = '\0';
443			unix_ptr++;
444			BAM_DPRINTF((D_FLAGS1_PRESENT, fcn, flags1_ptr));
445		}
446
447	} else  {
448		/* There is no unix, there is only a bunch of flags */
449		flags1_ptr = old_ptr;
450		unix_ptr = flags2_ptr = NULL;
451		BAM_DPRINTF((D_FLAGS1_ONLY, fcn, flags1_ptr));
452	}
453
454	/*
455	 * With dboot, unix is fixed and is at the beginning. We need to
456	 * migrate flags1 and flags2
457	 */
458create:
459	if (entry->flags & BAM_ENTRY_FAILSAFE) {
460		(void) snprintf(new_arg, sizeof (new_arg), "%s",
461		    DIRECT_BOOT_FAILSAFE_KERNEL);
462	} else {
463		(void) snprintf(new_arg, sizeof (new_arg), "%s",
464		    DIRECT_BOOT_KERNEL);
465	}
466	BAM_DPRINTF((D_CVTED_UNIX, fcn, new_arg));
467
468	if (flags1_ptr != NULL) {
469		(void) strlcat(new_arg, " ", sizeof (new_arg));
470		(void) strlcat(new_arg, flags1_ptr, sizeof (new_arg));
471	}
472
473	if (flags2_ptr != NULL) {
474		(void) strlcat(new_arg, " ", sizeof (new_arg));
475		(void) strlcat(new_arg, flags2_ptr, sizeof (new_arg));
476	}
477
478	BAM_DPRINTF((D_CVTED_UNIX_AND_FLAGS, fcn, new_arg));
479
480	free(line->arg);
481	line->arg = s_strdup(new_arg);
482	update_line(line);
483	BAM_DPRINTF((D_CVTED_KERNEL_LINE, fcn, line->line));
484	return (BAM_SUCCESS);
485}
486
487/*
488 * Similar to above, except this time we're looking at a module line,
489 * which is quite a bit simpler.
490 *
491 * Under multiboot, the archive line is:
492 *
493 * module /platform/i86pc/boot_archive
494 *
495 * Under directboot, the archive line is:
496 *
497 * module$ /platform/i86pc/$ISADIR/boot_archive
498 *
499 * which may be specified exactly as either of:
500 *
501 * module /platform/i86pc/boot_archive
502 * module /platform/i86pc/amd64/boot_archive
503 *
504 * Under multiboot, the failsafe is:
505 *
506 * module /boot/x86.miniroot-safe
507 *
508 * Under dboot, the failsafe is:
509 *
510 * module$ /boot/$ISADIR/x86.miniroot-safe
511 *
512 * which may be specified exactly as either of:
513 *
514 * module /boot/x86.miniroot-safe
515 * module /boot/amd64/x86.miniroot-safe
516 */
517static error_t
518cvt_module_line(line_t *line, entry_t *entry)
519{
520	const char		*fcn = "cvt_module_line()";
521
522	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, line->line));
523
524	/*
525	 * We only convert multiboot to dboot and nothing else
526	 */
527	if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
528		BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn));
529		return (BAM_SUCCESS);
530	}
531
532	if (entry->flags & BAM_ENTRY_FAILSAFE) {
533		if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) {
534			BAM_DPRINTF((D_FAILSAFE_NO_CVT_NEEDED, fcn, line->arg));
535			BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
536			return (BAM_SUCCESS);
537		}
538	} else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) {
539		bam_error(UNKNOWN_MODULE_LINE, line->lineNum);
540		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
541		return (BAM_MSG);
542	}
543
544	free(line->cmd);
545	free(line->arg);
546	line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
547
548	line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ?
549	    FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE);
550
551	update_line(line);
552	BAM_DPRINTF((D_CVTED_MODULE, fcn, line->line));
553	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
554	return (BAM_SUCCESS);
555}
556
557static void
558bam_warn_hand_entries(menu_t *mp, char *osroot)
559{
560	int		hand_num;
561	int		hand_max;
562	int		*hand_list;
563	int		i;
564	entry_t		*entry;
565	const char	*fcn = "bam_warn_hand_entries()";
566
567	if (bam_force) {
568		/*
569		 * No warning needed, we are automatically converting
570		 * the "hand" entries
571		 */
572		BAM_DPRINTF((D_FORCE_HAND_CVT,  fcn));
573		return;
574	}
575
576	hand_num = 0;
577	hand_max = BAM_ENTRY_NUM;
578	hand_list = s_calloc(1, hand_max);
579
580	for (entry = mp->entries; entry; entry = entry->next) {
581		if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))
582			continue;
583		BAM_DPRINTF((D_FOUND_HAND, fcn, entry->entryNum));
584		if (++hand_num > hand_max) {
585			hand_max *= 2;
586			hand_list = s_realloc(hand_list,
587			    hand_max * sizeof (int));
588		}
589		hand_list[hand_num - 1] = entry->entryNum;
590	}
591
592	bam_error(HAND_ADDED_ENTRIES, osroot, MENU_URL(osroot));
593	bam_print_stderr("Entry Number%s: ", (hand_num > 1) ?
594	    "s" : "");
595	for (i = 0; i < hand_num; i++) {
596		bam_print_stderr("%d ", hand_list[i]);
597	}
598	bam_print_stderr("\n");
599}
600
601static entry_t *
602find_matching_entry(
603	entry_t *estart,
604	char *grubsign,
605	char *grubroot,
606	int root_opt)
607{
608	entry_t		*entry;
609	line_t		*line;
610	char		opt[10];
611	const char	*fcn = "find_matching_entry()";
612
613	assert(grubsign);
614	assert(root_opt == 0 || root_opt == 1);
615
616	(void) snprintf(opt, sizeof (opt), "%d", root_opt);
617	BAM_DPRINTF((D_FUNC_ENTRY3, fcn, grubsign, grubroot, opt));
618
619	for (entry = estart; entry; entry = entry->next) {
620
621		if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) &&
622		    !bam_force) {
623			BAM_DPRINTF((D_SKIP_ENTRY, fcn, entry->entryNum));
624			continue;
625		}
626
627		if (entry->flags & BAM_ENTRY_ROOT) {
628			for (line = entry->start; line; line = line->next) {
629				if (line->cmd == NULL || line->arg == NULL) {
630					if (line == entry->end) {
631						BAM_DPRINTF((D_ENTRY_END, fcn));
632						break;
633					} else {
634						BAM_DPRINTF((D_SKIP_NULL, fcn));
635						continue;
636					}
637				}
638				if (strcmp(line->cmd, menu_cmds[ROOT_CMD])
639				    == 0 && strcmp(line->arg, grubroot) == 0) {
640					BAM_DPRINTF((D_ROOT_MATCH, fcn,
641					    line->line, grubsign));
642					return (entry);
643				}
644				if (line == entry->end) {
645					BAM_DPRINTF((D_ENTRY_END, fcn));
646					break;
647				}
648			}
649		} else if (entry->flags & BAM_ENTRY_FINDROOT) {
650			for (line = entry->start; line; line = line->next) {
651				if (line->cmd == NULL || line->arg == NULL) {
652					if (line == entry->end) {
653						BAM_DPRINTF((D_ENTRY_END, fcn));
654						break;
655					} else {
656						BAM_DPRINTF((D_SKIP_NULL, fcn));
657						continue;
658					}
659				}
660				if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD])
661				    == 0 && strcmp(line->arg, grubsign) == 0) {
662					BAM_DPRINTF((D_FINDROOT_MATCH, fcn,
663					    line->line, grubsign));
664					return (entry);
665				}
666				if (line == entry->end) {
667					BAM_DPRINTF((D_ENTRY_END, fcn));
668					break;
669				}
670			}
671		} else if (root_opt) {
672			/* Neither root nor findroot */
673			BAM_DPRINTF((D_NO_ROOT_FINDROOT, fcn, entry->entryNum));
674			return (entry);
675		}
676	}
677
678	BAM_DPRINTF((D_NO_MATCH, fcn));
679	return (NULL);
680}
681
682/*
683 * The following is a set of routines that attempt to convert the
684 * menu entries for the supplied osroot into a format compatible
685 * with the GRUB installation on osroot.
686 *
687 * Each of these conversion routines make no assumptions about
688 * the current state of the menu entry, it does its best to
689 * convert the menu entry to the new state. In the process
690 * we may either upgrade or downgrade.
691 *
692 * We don't make any heroic efforts at conversion. It is better
693 * to be conservative and bail out at the first sign of error. We will
694 * in such cases, point the user at the knowledge-base article
695 * so that they can upgrade manually.
696 */
697static error_t
698bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
699{
700	entry_t		*entry;
701	line_t		*line;
702	line_t		*newlp;
703	int		update_num;
704	char		linebuf[PATH_MAX];
705	const char	*fcn = "bam_add_findroot()";
706
707	update_num = 0;
708
709	bam_print(CVT_FINDROOT);
710
711	entry = mp->entries;
712	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
713	    entry = entry->next) {
714		if (entry->flags & BAM_ENTRY_FINDROOT) {
715			/* already converted */
716			BAM_DPRINTF((D_ALREADY_FINDROOT, fcn, entry->entryNum));
717			continue;
718		}
719		for (line = entry->start; line; line = line->next) {
720			if (line->cmd == NULL || line->arg == NULL) {
721				if (line == entry->end) {
722					BAM_DPRINTF((D_ENTRY_END, fcn));
723					break;
724				} else {
725					BAM_DPRINTF((D_SKIP_NULL, fcn));
726					continue;
727				}
728			}
729			if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) {
730				newlp = s_calloc(1, sizeof (line_t));
731				newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
732				newlp->sep = s_strdup(" ");
733				newlp->arg = s_strdup(grubsign);
734				(void) snprintf(linebuf, sizeof (linebuf),
735				    "%s%s%s", newlp->cmd, newlp->sep,
736				    newlp->arg);
737				newlp->line = s_strdup(linebuf);
738				bam_add_line(mp, entry, line, newlp);
739				update_num = 1;
740				entry->flags &= ~BAM_ENTRY_ROOT;
741				entry->flags |= BAM_ENTRY_FINDROOT;
742				BAM_DPRINTF((D_ADDED_FINDROOT, fcn,
743				    newlp->line));
744				line = newlp;
745			}
746			if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) {
747				BAM_DPRINTF((D_FREEING_ROOT, fcn, line->line));
748				unlink_line(mp, line);
749				line_free(line);
750			}
751			if (line == entry->end) {
752				BAM_DPRINTF((D_ENTRY_END, fcn));
753				break;
754			}
755		}
756	}
757
758	if (update_num) {
759		BAM_DPRINTF((D_UPDATED_NUMBERING, fcn));
760		update_numbering(mp);
761	}
762
763	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
764	return (BAM_SUCCESS);
765}
766
767static error_t
768bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
769{
770	entry_t		*entry;
771	const char	*fcn = "bam_add_hv()";
772
773	bam_print(CVT_HV);
774
775	entry = mp->entries;
776	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
777	    entry = entry->next) {
778		if (entry->flags & BAM_ENTRY_HV) {
779			BAM_DPRINTF((D_ALREADY_HV, fcn, entry->entryNum));
780			return (BAM_SUCCESS);
781		}
782	}
783
784	(void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU,
785	    XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE);
786
787	BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn));
788
789	update_numbering(mp);
790
791	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
792
793	return (BAM_SUCCESS);
794}
795
796static error_t
797bam_add_dboot(
798	menu_t *mp,
799	char *osroot,
800	char *grubsign,
801	char *grubroot,
802	int root_opt)
803{
804	int		msg = 0;
805	entry_t		*entry;
806	line_t		*line;
807	error_t		ret;
808	const char 	*fcn = "bam_add_dboot()";
809
810	bam_print(CVT_DBOOT);
811
812	entry = mp->entries;
813	for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt);
814	    entry = entry->next) {
815		for (line = entry->start; line; line = line->next) {
816			if (line->cmd == NULL || line->arg == NULL) {
817				if (line == entry->end) {
818					BAM_DPRINTF((D_ENTRY_END, fcn));
819					break;
820				} else {
821					BAM_DPRINTF((D_SKIP_NULL, fcn));
822					continue;
823				}
824			}
825
826			/*
827			 * If we have a kernel$ command, assume it
828			 * is dboot already.  If it is not a dboot
829			 * entry, something funny is going on and
830			 * we will leave it alone
831			 */
832			if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) {
833				ret = cvt_kernel_line(line, osroot, entry);
834				INJECT_ERROR1("ADD_DBOOT_KERN_ERR",
835				    ret = BAM_ERROR);
836				INJECT_ERROR1("ADD_DBOOT_KERN_MSG",
837				    ret = BAM_MSG);
838				if (ret == BAM_ERROR) {
839					BAM_DPRINTF((D_CVT_KERNEL_FAIL, fcn));
840					return (ret);
841				} else if (ret == BAM_MSG) {
842					msg = 1;
843					BAM_DPRINTF((D_CVT_KERNEL_MSG, fcn));
844				}
845			}
846			if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) {
847				ret = cvt_module_line(line, entry);
848				INJECT_ERROR1("ADD_DBOOT_MOD_ERR",
849				    ret = BAM_ERROR);
850				INJECT_ERROR1("ADD_DBOOT_MOD_MSG",
851				    ret = BAM_MSG);
852				if (ret == BAM_ERROR) {
853					BAM_DPRINTF((D_CVT_MODULE_FAIL, fcn));
854					return (ret);
855				} else if (ret == BAM_MSG) {
856					BAM_DPRINTF((D_CVT_MODULE_MSG, fcn));
857					msg = 1;
858				}
859			}
860
861			if (line == entry->end) {
862				BAM_DPRINTF((D_ENTRY_END, fcn));
863				break;
864			}
865		}
866	}
867
868	ret = msg ? BAM_MSG : BAM_SUCCESS;
869	BAM_DPRINTF((D_RETURN_RET, fcn, ret));
870	return (ret);
871}
872
873/*ARGSUSED*/
874error_t
875upgrade_menu(menu_t *mp, char *osroot, char *menu_root)
876{
877	char		*osdev;
878	char		*grubsign;
879	char		*grubroot;
880	int		ret1;
881	int		ret2;
882	int		ret3;
883	const char	*fcn = "upgrade_menu()";
884
885	assert(osroot);
886	assert(menu_root);
887
888	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
889
890	/*
891	 * We only support upgrades. Xen may not be present
892	 * on smaller metaclusters so we don't check for that.
893	 */
894	if (bam_is_findroot != BAM_FINDROOT_PRESENT ||
895	    bam_direct != BAM_DIRECT_DBOOT) {
896		bam_error(DOWNGRADE_NOTSUP, osroot);
897		return (BAM_ERROR);
898	}
899
900	/*
901	 * First get the GRUB signature
902	 */
903	osdev = get_special(osroot);
904	INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL);
905	if (osdev == NULL) {
906		bam_error(CANT_FIND_SPECIAL, osroot);
907		return (BAM_ERROR);
908	}
909
910	grubsign = get_grubsign(osroot, osdev);
911	INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL);
912	if (grubsign == NULL) {
913		free(osdev);
914		bam_error(CANT_FIND_GRUBSIGN, osroot);
915		return (BAM_ERROR);
916	}
917
918	/* not fatal if we can't get grubroot */
919	grubroot = get_grubroot(osroot, osdev, menu_root);
920	INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL);
921
922	free(osdev);
923
924	ret1 = bam_add_findroot(mp, grubsign,
925	    grubroot, root_optional(osroot, menu_root));
926	INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR);
927	if (ret1 == BAM_ERROR)
928		goto abort;
929
930	if (bam_is_hv == BAM_HV_PRESENT) {
931		ret2 = bam_add_hv(mp, grubsign, grubroot,
932		    root_optional(osroot, menu_root));
933		INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR);
934		if (ret2 == BAM_ERROR)
935			goto abort;
936	} else
937		ret2 = BAM_SUCCESS;
938
939	ret3 = bam_add_dboot(mp, osroot, grubsign,
940	    grubroot, root_optional(osroot, menu_root));
941	INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR);
942	if (ret3 == BAM_ERROR)
943		goto abort;
944
945	if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) {
946		bam_error(CVT_TODO, MENU_URL(osroot));
947	} else {
948		bam_warn_hand_entries(mp, osroot);
949	}
950
951	free(grubsign);
952
953	BAM_DPRINTF((D_RETURN_RET, fcn, BAM_WRITE));
954	return (BAM_WRITE);
955
956abort:
957	free(grubsign);
958	bam_error(CVT_ABORT, osroot, MENU_URL(osroot));
959	return (BAM_ERROR);
960}
961