xref: /titanic_52/usr/src/cmd/boot/bootadm/bootadm_hyper.c (revision 807e8f10a5bd74064a2c6ed98b73c845df2f834e)
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 <alloca.h>
32 #include <ctype.h>
33 #include <sys/types.h>
34 
35 #include "message.h"
36 #include "bootadm.h"
37 
38 #define	HYPER_KERNEL_DIR 		"/platform/i86xpv/kernel"
39 #define	METAL_KERNEL_DIR 		"/platform/i86pc/kernel"
40 
41 #define	BOOTRC_FILE			"/boot/solaris/bootenv.rc"
42 #define	ZFS_BOOTSTR			"$ZFS-BOOTFS"
43 
44 #define	BFLAG				"-B"
45 #define	DEFAULT_SERIAL			"9600,8,n,1"
46 
47 #define	TTYXMODE_TO_COMNUM(ttyxmode)	((int)(*((ttyxmode) + 3) - '`'))
48 #define	COMNAME_TO_COMNUM(comname)	((int)(*((comname) + 3) - '0'))
49 
50 #define	WHITESPC(x)			(x)
51 
52 static char *serial_config[2] = { NULL, NULL };
53 
54 static char *console_dev = NULL;
55 
56 static unsigned zfs_boot = 0;
57 
58 /*
59  * Append the string pointed to by "str" to the string pointed to by "orig"
60  * adding the delimeter "delim" in between.
61  *
62  * Return a pointer to the new string or NULL, if we were passed a bad string.
63  */
64 static char *
65 append_str(char *orig, char *str, char *delim)
66 {
67 	char *newstr;
68 	int len;
69 
70 	if ((str == NULL) || (delim == NULL))
71 		return (NULL);
72 
73 	if ((orig == NULL) || (*orig == NULL)) {
74 		/*
75 		 * Return a pointer to a copy of the path so a caller can
76 		 * always rely upon being able to free() a returned pointer.
77 		 */
78 		return (s_strdup(str));
79 	}
80 
81 	len = strlen(orig) + strlen(str) + strlen(delim) + 1;
82 	if ((newstr = malloc(len)) == NULL) {
83 		bam_error(NO_MEM, len);
84 		bam_exit(1);
85 	}
86 
87 	(void) snprintf(newstr, len, "%s%s%s", orig, delim, str);
88 	return (newstr);
89 }
90 
91 /*
92  * Replace the substring "old_str" in a path with the substring "new_str"
93  *
94  * Return a pointer to the modified string.
95  */
96 static char *
97 modify_path(char *path, char *old_str, char *new_str)
98 {
99 	char *newpath;
100 	char *pc;
101 	int len;
102 
103 	/*
104 	 * Return a pointer to a copy of the path so a caller can always rely
105 	 * upon being able to free() a returned pointer.
106 	 */
107 	if ((pc = strstr(path, old_str)) == NULL)
108 		return (s_strdup(path));
109 
110 	/*
111 	 * Allocate space for duplicate of path with name changes and
112 	 * NULL terminating byte
113 	 */
114 	len = strlen(path) - strlen(old_str) + strlen(new_str) + 1;
115 
116 	if ((newpath = malloc(len)) == NULL) {
117 		bam_error(NO_MEM, len);
118 		bam_exit(1);
119 	}
120 
121 	(void) strlcpy(newpath, path, (pc - path) + 1);
122 	pc += strlen(old_str);
123 
124 	(void) strcat(newpath, new_str);
125 	(void) strcat(newpath, pc);
126 	return (newpath);
127 }
128 
129 /*
130  * Set "token" to be the the string starting from the pointer "str" delimited
131  * by any character in the string "delim" or the end of the string, but IGNORE
132  * any characters between single or double quotes.
133  *
134  * Return a pointer to the next non-whitespace character after the delimiter
135  * or NULL if we hit the end of the string. Also return NULL upon failure to
136  * find any characters from the delimeter string or upon failure to allocate
137  * memory for the new token string.
138  */
139 static char *
140 get_token(char **token, char *str, char *delim)
141 {
142 	char *dp;
143 	char *start = str;
144 	unsigned len;
145 
146 	*token = NULL;
147 
148 	if ((str == NULL) || (*str == NULL))
149 		return (NULL);
150 
151 	do {
152 		if ((*str == '\'') || (*str == '"')) {
153 			char quote = *str++;
154 
155 			while ((*str != NULL) && (*str != quote))
156 				str++;
157 
158 			/* no matching quote found in string */
159 			if (*str++ == NULL)
160 				return (NULL);
161 		}
162 
163 		/* look for a character from the delimiter string */
164 		for (dp = delim; ((*dp != NULL) && (*dp != *str)); dp++)
165 			;
166 
167 		if (*dp != NULL) {
168 			len = str - start + 1;
169 
170 			/* found a delimiter, so create a token string */
171 			if ((*token = malloc(len)) == NULL) {
172 				bam_error(NO_MEM, len);
173 				bam_exit(1);
174 			}
175 
176 			(void) strlcpy(*token, start, len);
177 
178 			while (isspace((int)*++str))
179 				;
180 
181 			return (str);
182 		}
183 	} while (*str++ != NULL);
184 
185 	/* if we hit the end of the string, the token is the whole string  */
186 	*token = s_strdup(start);
187 	return (NULL);
188 }
189 
190 /*
191  * Convert a metal "console" device name to an equivalent one suitable for
192  * use with the hypervisor.
193  *
194  * Default to "vga" if we can't parse the console device.
195  */
196 static void
197 console_metal_to_hyper(char *console)
198 {
199 	if ((*console == '\'') || (*console == '"'))
200 		console++;
201 
202 	if (strncmp(console, "ttya", 4) == 0)
203 		console_dev = "console=com1";
204 	else if (strncmp(console, "ttyb", 4) == 0)
205 		console_dev = "console=com2";
206 	else
207 		console_dev = "console=vga";
208 }
209 
210 static int
211 set_serial_rate(int com, char *rate)
212 {
213 	char **rp = &serial_config[com - 1];
214 
215 	/*
216 	 * If rate is a NULL pointer, erase any existing serial configuration
217 	 * for this serial port.
218 	 */
219 	if (rate == NULL) {
220 		if (*rp != NULL) {
221 			free(*rp);
222 			*rp = NULL;
223 		}
224 		return (0);
225 	}
226 
227 	*rp = s_realloc(*rp, strlen(rate) + 1);
228 	(void) strcpy(*rp, rate);
229 	return (0);
230 }
231 
232 /*
233  * Convert "metal" serial port parameters to values compatible with the
234  * hypervisor.
235  *
236  * Return 0 on success, otherwise -1.
237  */
238 static int
239 serial_metal_to_hyper(char *metal_port, char *metal_serial)
240 {
241 #define	COM_RATE_LEN	16	/* strlen("com1=115200,8n1") */
242 
243 	char com_rate[COM_RATE_LEN];
244 
245 	unsigned com, baud, bits, stop;
246 	char parity, handshake;
247 
248 	if ((strcmp(metal_port, "ttya-mode") == 0) ||
249 	    (strcmp(metal_port, "ttyb-mode") == 0))
250 		com = TTYXMODE_TO_COMNUM(metal_port);
251 	else
252 		return (-1);
253 
254 	if ((*metal_serial == '\'') || (*metal_serial == '"'))
255 		metal_serial++;
256 
257 	/*
258 	 * Check if it's specified as the default rate; if so it defaults to
259 	 * "auto" and we need not set it for they hypervisor.
260 	 */
261 	if (strncmp(metal_serial, DEFAULT_SERIAL,
262 	    strlen(DEFAULT_SERIAL)) == 0) {
263 		(void) set_serial_rate(com, NULL);
264 		return (0);
265 	}
266 
267 	/* read the serial port format as set forth in common/io/asy.c */
268 	if (sscanf(metal_serial, "%u,%u,%c,%u,%c", &baud, &bits, &parity, &stop,
269 	    &handshake) != 5)
270 		return (-1);
271 
272 	/* validate serial port parameters */
273 	if (((bits < 5) || (bits > 8)) || (stop > 1) ||
274 	    ((parity != 'n') && (parity != 'e') && (parity != 'o')) ||
275 	    ((handshake != '-') && (handshake != 'h') && (handshake != 's')))
276 		return (-1);
277 
278 	/* validate baud rate */
279 	switch (baud) {
280 		case 150:
281 		case 300:
282 		case 600:
283 		case 1200:
284 		case 2400:
285 		case 4800:
286 		case 9600:
287 		case 19200:
288 		case 38400:
289 		case 57600:
290 		case 115200:
291 			break;
292 
293 		default:
294 			return (-1);
295 	}
296 
297 	/*
298 	 * The hypervisor has no way to specify a handshake method, so it gets
299 	 * quietly dropped in the conversion.
300 	 */
301 	(void) snprintf(com_rate, COM_RATE_LEN, "com%d=%u,%u%c%u", com, baud,
302 	    bits, parity, stop);
303 	(void) set_serial_rate(com, com_rate);
304 	return (0);
305 }
306 
307 /*
308  * Convert bootenv.rc boot property strings of the form:
309  *
310  *     setprop property value
311  *
312  * into boot option lines suitable for use with the hypervisor.
313  *
314  * Our main concerns are the console device and serial port settings.
315  *
316  * Return values:
317  *
318  *    -1:	Unparseable line
319  *    0:	Success
320  *    (n > 0):	A property unimportant to us
321  */
322 static int
323 cvt_bootprop(char *propstr)
324 {
325 	char *parsestr, *port, *token;
326 
327 	int retval = 0;
328 
329 	/* get initial "setprop" */
330 	if ((parsestr = get_token(&token, propstr, " \t")) == NULL) {
331 		if (token != NULL)
332 			free(token);
333 
334 		return (-1);
335 	}
336 
337 	if (strcmp(token, "setprop")) {
338 		free(token);
339 		return (1);
340 	}
341 
342 	free(token);
343 
344 	/* get property name */
345 	if ((parsestr = get_token(&token, parsestr, " \t")) == NULL) {
346 		if (token != NULL)
347 			free(token);
348 
349 		return (-2);
350 	}
351 
352 	if (strcmp(token, "console") == 0) {
353 		free(token);
354 
355 		/* get console property value */
356 		parsestr = get_token(&token, parsestr, " \t");
357 		if (token == NULL)
358 			return (-3);
359 
360 		console_metal_to_hyper(token);
361 		free(token);
362 		return (0);
363 	}
364 
365 	/* check if it's a serial port setting */
366 	if ((strcmp(token, "ttya-mode") == 0) ||
367 	    (strcmp(token, "ttyb-mode") == 0)) {
368 		port = token;
369 	} else {
370 		free(token);
371 		return (3);
372 	}
373 
374 	/* get serial port setting */
375 	parsestr = get_token(&token, parsestr, " \t");
376 
377 	if (token == NULL) {
378 		free(port);
379 		return (-1);
380 	}
381 
382 	retval = serial_metal_to_hyper(port, token);
383 
384 	free(port);
385 	free(token);
386 	return (retval);
387 }
388 
389 /*
390  * Convert "name=value" metal options to values suitable for use with the
391  * hypervisor.
392  *
393  * Our main concerns are the console device and serial port settings.
394  *
395  * Return values:
396  *
397  *    -1:	Unparseable line
398  *    0:	Success
399  *    (n > 0):	A property unimportant to us
400  */
401 static int
402 cvt_metal_option(char *optstr)
403 {
404 	char *value;
405 	unsigned namlen;
406 
407 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
408 		zfs_boot = 1;
409 		return (0);
410 	}
411 
412 	if ((value = strchr(optstr, '=')) == NULL)
413 		return (-1);
414 
415 	namlen = value - optstr;
416 
417 	if (*++value == NULL)
418 		return (1);
419 
420 	if (strncmp(optstr, "console", namlen) == 0) {
421 		console_metal_to_hyper(value);
422 		return (0);
423 	}
424 
425 	if ((strncmp(optstr, "ttya-mode", namlen) == 0) ||
426 	    (strncmp(optstr, "ttyb-mode", namlen) == 0)) {
427 		char *port = alloca(namlen + 1);
428 
429 		(void) strlcpy(port, optstr, namlen + 1);
430 		return (serial_metal_to_hyper(port, value));
431 	}
432 
433 	return (1);
434 }
435 
436 /*
437  * Convert "name=value" properties for use with a bare metal kernel
438  *
439  * Our main concerns are the console setting and serial port modes.
440  *
441  * Return values:
442  *
443  *    -1:	Unparseable line
444  *    0:	Success
445  *    (n > 0):	A property unimportant to us
446  */
447 static int
448 cvt_hyper_option(char *optstr)
449 {
450 #define	SER_LEN		27	/* strlen("ttyb-mode='115200,8,n,1,-'") */
451 
452 	char ser[SER_LEN];
453 	char *value;
454 
455 	unsigned namlen;
456 
457 	unsigned baud;
458 	char bits, parity, stop;
459 
460 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
461 		zfs_boot = 1;
462 		return (0);
463 	}
464 
465 	/*
466 	 * If there's no "=" in the token, it's likely a standalone
467 	 * hypervisor token we don't care about (e.g. "noreboot" or
468 	 * "nosmp") so we ignore it.
469 	 */
470 	if ((value = strchr(optstr, '=')) == NULL)
471 		return (1);
472 
473 	namlen = value - optstr;
474 
475 	if (*++value == NULL)
476 		return (1);
477 
478 	/*
479 	 * Note that we use strncmp against the values because the
480 	 * hypervisor allows setting console parameters for both the
481 	 * console and debugger via the format:
482 	 *
483 	 *   console=cons_dev,debug_dev
484 	 *
485 	 * and we only care about "cons_dev."
486 	 *
487 	 * This also allows us to extract "comN" from hypervisor constructs
488 	 * like "com1H" or "com2L," concepts unsupported on bare metal kernels.
489 	 *
490 	 * Default the console device to "text" if it was "vga" or was
491 	 * unparseable.
492 	 */
493 	if (strncmp(optstr, "console", namlen) == 0) {
494 		/* ignore the "console=hypervisor" option */
495 		if (strcmp(value, "hypervisor") == 0)
496 			return (0);
497 
498 		if (strncmp(value, "com1", 4) == 0)
499 			console_dev = "console=ttya";
500 		else if (strncmp(value, "com2", 4) == 0)
501 			console_dev = "console=ttyb";
502 		else
503 			console_dev = "console=text";
504 	}
505 
506 	/* serial port parameter conversion */
507 
508 	if ((strncmp(optstr, "com1", namlen) == 0) ||
509 	    (strncmp(optstr, "com2", namlen) == 0)) {
510 		unsigned com = COMNAME_TO_COMNUM(optstr);
511 
512 		/*
513 		 * Check if it's "auto" - if so, use the default setting
514 		 * of "9600,8,n,1,-".
515 		 *
516 		 * We can't just assume the serial port will default to
517 		 * "9600,8,n,1" as there could be a directive in bootenv.rc
518 		 * that would set it to some other value and we want the serial
519 		 * parameters to be the same as that used by the hypervisor.
520 		 */
521 		if (strcmp(value, "auto") == 0) {
522 			(void) snprintf(ser, SER_LEN,
523 			    "tty%c-mode='9600,8,n,1,-'", '`' + com);
524 
525 			if (set_serial_rate(com, ser) != 0)
526 				return (-1);
527 
528 			return (0);
529 		}
530 
531 		/*
532 		 * Extract the "B,PS" setting from the com line; ignore other
533 		 * settings like io_base or IRQ.
534 		 */
535 		if (sscanf(value, "%u,%c%c%c", &baud, &bits, &parity,
536 		    &stop) != 4)
537 			return (-1);
538 
539 		/* validate serial port parameters */
540 		if (((stop != '0') && (stop != '1')) ||
541 		    ((bits < '5') && (bits > '8')) ||
542 		    ((parity != 'n') && (parity != 'e') && (parity != 'o')))
543 			return (-1);
544 
545 		/* validate baud rate */
546 		switch (baud) {
547 			case 150:
548 			case 300:
549 			case 600:
550 			case 1200:
551 			case 2400:
552 			case 4800:
553 			case 19200:
554 			case 38400:
555 			case 57600:
556 			case 115200:
557 				break;
558 
559 			default:
560 				return (-1);
561 		}
562 
563 		/*
564 		 * As the hypervisor has no way to denote handshaking in its
565 		 * serial port settings, emit a metal serial port configuration
566 		 * with none as well.
567 		 */
568 		(void) snprintf(ser, SER_LEN, "tty%c-mode='%u,%c,%c,%c,-'",
569 		    '`' + com, baud, bits, parity, stop);
570 
571 		if (set_serial_rate(com, ser) != 0)
572 			return (-1);
573 
574 		return (0);
575 	}
576 
577 	return (1);
578 }
579 
580 /*
581  * Parse a hardware kernel's "kernel$" specifier into parameters we can then
582  * use to construct an appropriate "module$" line that can be used to specify
583  * how to boot the hypervisor's dom0.
584  *
585  * Return values:
586  *
587  *	-1: error parsing kernel path
588  *	 0: success
589  *	 1: kernel already a hypervisor kernel
590  */
591 static int
592 cvt_metal_kernel(char *kernstr, char **path)
593 {
594 	char *token, *parsestr;
595 
596 	parsestr = get_token(path, kernstr, " \t,");
597 	if (*path == NULL)
598 		return (-1);
599 
600 	/*
601 	 * If the metal kernel specified contains the name of the hypervisor,
602 	 * we're probably trying to convert an entry already setup to run the
603 	 * hypervisor, so error out now.
604 	 */
605 	if (strstr(*path, XEN_MENU) != NULL) {
606 		bam_error(ALREADY_HYPER);
607 		free(*path);
608 		*path = NULL;
609 		return (1);
610 	}
611 
612 	/* if the path was the last item on the line, that's OK. */
613 	if ((parsestr = get_token(&token, parsestr, " \t,")) == NULL) {
614 		if (token != NULL)
615 			free(token);
616 		return (0);
617 	}
618 
619 	/* if the next token is "-B" process boot options */
620 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
621 		free(token);
622 		return (0);
623 	}
624 
625 	free(token);
626 
627 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
628 		(void) cvt_metal_option(token);
629 		free(token);
630 	}
631 
632 	if (token != NULL) {
633 		(void) cvt_metal_option(token);
634 		free(token);
635 	}
636 
637 	return (0);
638 }
639 
640 /*
641  * Parse a hypervisor's "kernel$" line into parameters that can be used to
642  * help build an appropriate "kernel$" line for booting a bare metal kernel.
643  *
644  * Return 0 on success, non-zero on failure.
645  */
646 static int
647 cvt_hyper_kernel(char *kernel)
648 {
649 	char *token, *parsestr;
650 
651 	parsestr = get_token(&token, kernel, " \t,");
652 
653 	if (token == NULL)
654 		return (-1);
655 
656 	/*
657 	 * If the hypervisor kernel specified lives in the metal kernel
658 	 * directory, we're probably trying to convert an entry already setup
659 	 * to run on bare metal, so error out now.
660 	 */
661 	if (strncmp(token, METAL_KERNEL_DIR, strlen(METAL_KERNEL_DIR)) == 0) {
662 		bam_error(ALREADY_METAL);
663 		free(token);
664 		return (-1);
665 	}
666 
667 	free(token);
668 
669 	/* check for kernel options */
670 	while ((parsestr = get_token(&token, parsestr, " ")) != NULL) {
671 		(void) cvt_hyper_option(token);
672 		free(token);
673 	}
674 
675 	if (token != NULL) {
676 		(void) cvt_hyper_option(token);
677 		free(token);
678 	}
679 
680 	return (0);
681 }
682 
683 /*
684  * Parse a hypervisor's "module$" line into parameters that can be used to
685  * help build an appropriate "kernel$" line for booting a bare metal kernel.
686  */
687 static void
688 cvt_hyper_module(char *modstr, char **path)
689 {
690 	char *token = NULL;
691 	char *parsestr = modstr;
692 
693 	/*
694 	 * If multiple pathnames exist on the module$ line, we just want
695 	 * the last one.
696 	 */
697 	while ((parsestr = get_token(path, parsestr, " \t,")) != NULL) {
698 		if (*parsestr != '/')
699 			break;
700 		else
701 			free(*path);
702 	}
703 
704 	/* if the path was the last item on the line, that's OK. */
705 	if ((parsestr == NULL) ||
706 	    ((parsestr = get_token(&token, parsestr, " \t,")) == NULL)) {
707 		if (token != NULL)
708 			free(token);
709 		return;
710 	}
711 
712 	if (token == NULL)
713 		return;
714 
715 	/* check for "-B" option */
716 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
717 		free(token);
718 		return;
719 	}
720 
721 	free(token);
722 
723 	/* check for kernel options */
724 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
725 		(void) cvt_hyper_option(token);
726 		free(token);
727 	}
728 
729 	if (token != NULL) {
730 		(void) cvt_hyper_option(token);
731 		free(token);
732 	}
733 }
734 
735 static void
736 parse_bootenvrc(char *osroot)
737 {
738 #define	LINEBUF_SZ	1024
739 
740 	FILE *fp;
741 	char *rcpath;
742 	char line[LINEBUF_SZ];	/* make line buffer large but not ridiculous */
743 	int len;
744 
745 	assert(osroot);
746 
747 	len = strlen(osroot) + strlen(BOOTRC_FILE) + 1;
748 	rcpath = alloca(len);
749 
750 	(void) snprintf(rcpath, len, "%s%s", osroot, BOOTRC_FILE);
751 
752 	/* if we couldn't open the bootenv.rc file, ignore the issue. */
753 	if ((fp = fopen(rcpath, "r")) == NULL) {
754 		BAM_DPRINTF((D_NO_BOOTENVRC, rcpath, strerror(errno)));
755 		return;
756 	}
757 
758 	while (s_fgets(line, LINEBUF_SZ, fp) != NULL) {
759 		/* we're only interested in parsing "setprop" directives. */
760 		if (strncmp(line, "setprop", 7) != NULL)
761 			continue;
762 
763 		(void) cvt_bootprop(line);
764 	}
765 
766 	(void) fclose(fp);
767 }
768 
769 error_t
770 cvt_to_hyper(menu_t *mp, char *osroot, char *extra_args)
771 {
772 	const char *fcn = "cvt_to_hyper()";
773 
774 	line_t *lp;
775 	entry_t *ent;
776 	size_t len, zfslen;
777 
778 	char *newstr;
779 	char *osdev;
780 
781 	char *title = NULL;
782 	char *findroot = NULL;
783 	char *bootfs = NULL;
784 	char *kernel = NULL;
785 	char *mod_kernel = NULL;
786 	char *module = NULL;
787 
788 	char *kern_path = NULL;
789 	char *kern_bargs = NULL;
790 
791 	int curdef;
792 	int kp_allocated = 0;
793 	int ret = BAM_ERROR;
794 
795 	assert(osroot);
796 
797 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, extra_args));
798 
799 	/*
800 	 * First just check to verify osroot is a sane directory.
801 	 */
802 	if ((osdev = get_special(osroot)) == NULL) {
803 		bam_error(CANT_FIND_SPECIAL, osroot);
804 		return (BAM_ERROR);
805 	}
806 
807 	free(osdev);
808 
809 	/*
810 	 * While the effect is purely cosmetic, if osroot is "/" don't
811 	 * bother prepending it to any paths as they are constructed to
812 	 * begin with "/" anyway.
813 	 */
814 	if (strcmp(osroot, "/") == 0)
815 		osroot = "";
816 
817 	/*
818 	 * Found the GRUB signature on the target partitions, so now get the
819 	 * default GRUB boot entry number from the menu.lst file
820 	 */
821 	curdef = atoi(mp->curdefault->arg);
822 
823 	/* look for the first line of the matching boot entry */
824 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
825 	    ent = ent->next)
826 		;
827 
828 	/* couldn't find it, so error out */
829 	if (ent == NULL) {
830 		bam_error(CANT_FIND_DEFAULT, curdef);
831 		goto abort;
832 	}
833 
834 	/*
835 	 * We found the proper menu entry, so first we need to process the
836 	 * bootenv.rc file to look for boot options the hypervisor might need
837 	 * passed as kernel start options such as the console device and serial
838 	 * port parameters.
839 	 *
840 	 * If there's no bootenv.rc, it's not an issue.
841 	 */
842 	parse_bootenvrc(osroot);
843 
844 	/*
845 	 * Now process the entry itself.
846 	 */
847 	for (lp = ent->start; lp != NULL; lp = lp->next) {
848 		/*
849 		 * Process important lines from menu.lst boot entry.
850 		 */
851 		if (lp->flags == BAM_TITLE) {
852 			title = alloca(strlen(lp->arg) + 1);
853 			(void) strcpy(title, lp->arg);
854 		} else if (strcmp(lp->cmd, "findroot") == 0) {
855 			findroot = alloca(strlen(lp->arg) + 1);
856 			(void) strcpy(findroot, lp->arg);
857 		} else if (strcmp(lp->cmd, "bootfs") == 0) {
858 			bootfs = alloca(strlen(lp->arg) + 1);
859 			(void) strcpy(bootfs, lp->arg);
860 		} else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
861 			module = alloca(strlen(lp->arg) + 1);
862 			(void) strcpy(module, lp->arg);
863 		} else if ((strcmp(lp->cmd,
864 		    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
865 		    (ret = cvt_metal_kernel(lp->arg, &kern_path)) != 0) {
866 			if (ret < 0) {
867 				ret = BAM_ERROR;
868 				bam_error(KERNEL_NOT_PARSEABLE, curdef);
869 			} else
870 				ret = BAM_NOCHANGE;
871 
872 			goto abort;
873 		}
874 
875 		if (lp == ent->end)
876 			break;
877 	}
878 
879 	/*
880 	 * If findroot, module or kern_path are NULL, boot entry was malformed
881 	 */
882 	if (findroot == NULL) {
883 		bam_error(FINDROOT_NOT_FOUND, curdef);
884 		goto abort;
885 	}
886 
887 	if (module == NULL) {
888 		bam_error(MODULE_NOT_PARSEABLE, curdef);
889 		goto abort;
890 	}
891 
892 	if (kern_path == NULL) {
893 		bam_error(KERNEL_NOT_FOUND, curdef);
894 		goto abort;
895 	}
896 
897 	/* assemble new kernel and module arguments from parsed values */
898 	if (console_dev != NULL) {
899 		kern_bargs = s_strdup(console_dev);
900 
901 		if (serial_config[0] != NULL) {
902 			newstr = append_str(kern_bargs, serial_config[0], " ");
903 			free(kern_bargs);
904 			kern_bargs = newstr;
905 		}
906 
907 		if (serial_config[1] != NULL) {
908 			newstr = append_str(kern_bargs, serial_config[1], " ");
909 			free(kern_bargs);
910 			kern_bargs = newstr;
911 		}
912 	}
913 
914 	if ((extra_args != NULL) && (*extra_args != NULL)) {
915 		newstr = append_str(kern_bargs, extra_args, " ");
916 		free(kern_bargs);
917 		kern_bargs = newstr;
918 	}
919 
920 	len = strlen(osroot) + strlen(XEN_MENU) + strlen(kern_bargs) +
921 	    WHITESPC(1) + 1;
922 
923 	kernel = alloca(len);
924 
925 	if (kern_bargs != NULL) {
926 		if (*kern_bargs != NULL)
927 			(void) snprintf(kernel, len, "%s%s %s", osroot,
928 			    XEN_MENU, kern_bargs);
929 
930 		free(kern_bargs);
931 	} else {
932 		(void) snprintf(kernel, len, "%s%s", osroot, XEN_MENU);
933 	}
934 
935 	/*
936 	 * Change the kernel directory from the metal version to that needed for
937 	 * the hypervisor.  Convert either "direct boot" path to the default
938 	 * path.
939 	 */
940 	if ((strcmp(kern_path, DIRECT_BOOT_32) == 0) ||
941 	    (strcmp(kern_path, DIRECT_BOOT_64) == 0)) {
942 		kern_path = HYPERVISOR_KERNEL;
943 	} else {
944 		newstr = modify_path(kern_path, METAL_KERNEL_DIR,
945 		    HYPER_KERNEL_DIR);
946 		free(kern_path);
947 		kern_path = newstr;
948 		kp_allocated = 1;
949 	}
950 
951 	/*
952 	 * We need to allocate space for the kernel path (twice) plus an
953 	 * intervening space, possibly the ZFS boot string, and NULL,
954 	 * of course.
955 	 */
956 	len = (strlen(kern_path) * 2) + WHITESPC(1) + 1;
957 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
958 
959 	mod_kernel = alloca(len + zfslen);
960 	(void) snprintf(mod_kernel, len, "%s %s", kern_path, kern_path);
961 
962 	if (kp_allocated)
963 		free(kern_path);
964 
965 	if (zfs_boot) {
966 		char *zfsstr = alloca(zfslen + 1);
967 
968 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
969 		(void) strcat(mod_kernel, zfsstr);
970 	}
971 
972 	/* shut off warning messages from the entry line parser */
973 	if (ent->flags & BAM_ENTRY_BOOTADM)
974 		ent->flags &= ~BAM_ENTRY_BOOTADM;
975 
976 	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
977 	BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, mod_kernel));
978 
979 	/*
980 	 * Now try to delete the current default entry from the menu and add
981 	 * the new hypervisor entry with the parameters we've setup.
982 	 */
983 	if (delete_boot_entry(mp, curdef, DBE_QUIET) != BAM_SUCCESS)
984 		bam_print(NEW_BOOT_ENTRY, title);
985 
986 	curdef = add_boot_entry(mp, title, findroot, kernel, mod_kernel,
987 	    module, bootfs);
988 
989 	/*
990 	 * If we successfully created the new entry, set the default boot
991 	 * entry to that entry and let the caller know the new menu should
992 	 * be written out.
993 	 */
994 	if (curdef != BAM_ERROR)
995 		return (set_global(mp, menu_cmds[DEFAULT_CMD], curdef));
996 
997 	return (BAM_ERROR);
998 
999 abort:
1000 	if (ret != BAM_NOCHANGE)
1001 		bam_error(HYPER_ABORT, ((*osroot == NULL) ? "/" : osroot));
1002 
1003 	return (ret);
1004 }
1005 
1006 /*ARGSUSED*/
1007 error_t
1008 cvt_to_metal(menu_t *mp, char *osroot, char *menu_root)
1009 {
1010 	const char *fcn = "cvt_to_metal()";
1011 
1012 	line_t *lp;
1013 	entry_t *ent;
1014 	size_t len, zfslen;
1015 
1016 	char *delim = ",";
1017 	char *newstr;
1018 	char *osdev;
1019 
1020 	char *title = NULL;
1021 	char *findroot = NULL;
1022 	char *bootfs = NULL;
1023 	char *kernel = NULL;
1024 	char *module = NULL;
1025 
1026 	char *barchive_path = DIRECT_BOOT_ARCHIVE;
1027 	char *kern_path = NULL;
1028 
1029 	int curdef;
1030 	int emit_bflag = 1;
1031 	int ret = BAM_ERROR;
1032 
1033 	assert(osroot);
1034 
1035 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, ""));
1036 
1037 	/*
1038 	 * First just check to verify osroot is a sane directory.
1039 	 */
1040 	if ((osdev = get_special(osroot)) == NULL) {
1041 		bam_error(CANT_FIND_SPECIAL, osroot);
1042 		return (BAM_ERROR);
1043 	}
1044 
1045 	free(osdev);
1046 
1047 	/*
1048 	 * Found the GRUB signature on the target partitions, so now get the
1049 	 * default GRUB boot entry number from the menu.lst file
1050 	 */
1051 	curdef = atoi(mp->curdefault->arg);
1052 
1053 	/* look for the first line of the matching boot entry */
1054 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
1055 	    ent = ent->next)
1056 		;
1057 
1058 	/* couldn't find it, so error out */
1059 	if (ent == NULL) {
1060 		bam_error(CANT_FIND_DEFAULT, curdef);
1061 		goto abort;
1062 	}
1063 
1064 	/*
1065 	 * Now process the entry itself.
1066 	 */
1067 	for (lp = ent->start; lp != NULL; lp = lp->next) {
1068 		/*
1069 		 * Process important lines from menu.lst boot entry.
1070 		 */
1071 		if (lp->flags == BAM_TITLE) {
1072 			title = alloca(strlen(lp->arg) + 1);
1073 			(void) strcpy(title, lp->arg);
1074 		} else if (strcmp(lp->cmd, "findroot") == 0) {
1075 			findroot = alloca(strlen(lp->arg) + 1);
1076 			(void) strcpy(findroot, lp->arg);
1077 		} else if (strcmp(lp->cmd, "bootfs") == 0) {
1078 			bootfs = alloca(strlen(lp->arg) + 1);
1079 			(void) strcpy(bootfs, lp->arg);
1080 		} else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
1081 			if (strstr(lp->arg, "boot_archive") == NULL) {
1082 				module = alloca(strlen(lp->arg) + 1);
1083 				(void) strcpy(module, lp->arg);
1084 				cvt_hyper_module(module, &kern_path);
1085 			} else {
1086 				barchive_path = alloca(strlen(lp->arg) + 1);
1087 				(void) strcpy(barchive_path, lp->arg);
1088 			}
1089 		} else if ((strcmp(lp->cmd,
1090 		    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
1091 		    (cvt_hyper_kernel(lp->arg) < 0)) {
1092 			ret = BAM_NOCHANGE;
1093 			goto abort;
1094 		}
1095 
1096 		if (lp == ent->end)
1097 			break;
1098 	}
1099 
1100 	/*
1101 	 * If findroot, module or kern_path are NULL, boot entry was malformed
1102 	 */
1103 	if (findroot == NULL) {
1104 		bam_error(FINDROOT_NOT_FOUND, curdef);
1105 		goto abort;
1106 	}
1107 
1108 	if (module == NULL) {
1109 		bam_error(MODULE_NOT_PARSEABLE, curdef);
1110 		goto abort;
1111 	}
1112 
1113 	if (kern_path == NULL) {
1114 		bam_error(KERNEL_NOT_FOUND, curdef);
1115 		goto abort;
1116 	}
1117 
1118 	/*
1119 	 * Assemble new kernel and module arguments from parsed values.
1120 	 *
1121 	 * First, change the kernel directory from the hypervisor version to
1122 	 * that needed for a metal kernel.
1123 	 */
1124 	newstr = modify_path(kern_path, HYPER_KERNEL_DIR, METAL_KERNEL_DIR);
1125 	free(kern_path);
1126 	kern_path = newstr;
1127 
1128 	/* allocate initial space for the kernel path */
1129 	len = strlen(kern_path) + 1;
1130 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
1131 
1132 	if ((kernel = malloc(len + zfslen)) == NULL) {
1133 		free(kern_path);
1134 		bam_error(NO_MEM, len + zfslen);
1135 		bam_exit(1);
1136 	}
1137 
1138 	(void) snprintf(kernel, len, "%s", kern_path);
1139 	free(kern_path);
1140 
1141 	if (zfs_boot) {
1142 		char *zfsstr = alloca(zfslen + 1);
1143 
1144 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
1145 		(void) strcat(kernel, zfsstr);
1146 		emit_bflag = 0;
1147 	}
1148 
1149 	if (console_dev != NULL) {
1150 		if (emit_bflag) {
1151 			newstr = append_str(kernel, BFLAG, " ");
1152 			free(kernel);
1153 			kernel = append_str(newstr, console_dev, " ");
1154 			free(newstr);
1155 			emit_bflag = 0;
1156 		} else {
1157 			newstr = append_str(kernel, console_dev, ",");
1158 			free(kernel);
1159 			kernel = newstr;
1160 		}
1161 	}
1162 
1163 	/*
1164 	 * We have to do some strange processing here because the
1165 	 * hypervisor's serial ports default to "9600,8,n,1,-" if
1166 	 * "comX=auto" is specified, or to "auto" if nothing is
1167 	 * specified.
1168 	 *
1169 	 * Since there could be entries in the bootenv.rc file that
1170 	 * set the serial port to some other setting, when converting
1171 	 * a hypervisor entry to a metal entry we must force the
1172 	 * serial ports to their defaults.
1173 	 */
1174 
1175 	if (emit_bflag) {
1176 		newstr = append_str(kernel, BFLAG, " ");
1177 		free(kernel);
1178 		kernel = newstr;
1179 		delim = " ";
1180 		emit_bflag = 0;
1181 	}
1182 
1183 	if (serial_config[0] != NULL)
1184 		newstr = append_str(kernel, serial_config[0], delim);
1185 	else
1186 		newstr = append_str(kernel, "ttya-mode='9600,8,n,1,-'", delim);
1187 
1188 	free(kernel);
1189 
1190 	if (serial_config[1] != NULL)
1191 		kernel = append_str(newstr, serial_config[1], ",");
1192 	else
1193 		kernel = append_str(newstr, "ttyb-mode='9600,8,n,1,-'", ",");
1194 
1195 	free(newstr);
1196 
1197 	/* shut off warning messages from the entry line parser */
1198 	if (ent->flags & BAM_ENTRY_BOOTADM)
1199 		ent->flags &= ~BAM_ENTRY_BOOTADM;
1200 
1201 	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
1202 	BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, module));
1203 
1204 	/*
1205 	 * Now try to delete the current default entry from the menu and add
1206 	 * the new hypervisor entry with the parameters we've setup.
1207 	 */
1208 	if (delete_boot_entry(mp, curdef, DBE_QUIET) != BAM_SUCCESS)
1209 		bam_print(NEW_BOOT_ENTRY, title);
1210 
1211 	curdef = add_boot_entry(mp, title, findroot, kernel, NULL,
1212 	    barchive_path, bootfs);
1213 
1214 	/*
1215 	 * If we successfully created the new entry, set the default boot
1216 	 * entry to that entry and let the caller know the new menu should
1217 	 * be written out.
1218 	 */
1219 	if (curdef != BAM_ERROR)
1220 		return (set_global(mp, menu_cmds[DEFAULT_CMD], curdef));
1221 
1222 	return (BAM_ERROR);
1223 
1224 abort:
1225 	if (ret != BAM_NOCHANGE)
1226 		bam_error(METAL_ABORT, osroot);
1227 
1228 	return (ret);
1229 }
1230