xref: /titanic_50/usr/src/cmd/boot/bootadm/bootadm_hyper.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
144da779fSWilliam Kucharski /*
244da779fSWilliam Kucharski  * CDDL HEADER START
344da779fSWilliam Kucharski  *
444da779fSWilliam Kucharski  * The contents of this file are subject to the terms of the
544da779fSWilliam Kucharski  * Common Development and Distribution License (the "License").
644da779fSWilliam Kucharski  * You may not use this file except in compliance with the License.
744da779fSWilliam Kucharski  *
844da779fSWilliam Kucharski  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
944da779fSWilliam Kucharski  * or http://www.opensolaris.org/os/licensing.
1044da779fSWilliam Kucharski  * See the License for the specific language governing permissions
1144da779fSWilliam Kucharski  * and limitations under the License.
1244da779fSWilliam Kucharski  *
1344da779fSWilliam Kucharski  * When distributing Covered Code, include this CDDL HEADER in each
1444da779fSWilliam Kucharski  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1544da779fSWilliam Kucharski  * If applicable, add the following below this CDDL HEADER, with the
1644da779fSWilliam Kucharski  * fields enclosed by brackets "[]" replaced with your own identifying
1744da779fSWilliam Kucharski  * information: Portions Copyright [yyyy] [name of copyright owner]
1844da779fSWilliam Kucharski  *
1944da779fSWilliam Kucharski  * CDDL HEADER END
2044da779fSWilliam Kucharski  */
21*23a1cceaSRoger A. Faulkner 
2244da779fSWilliam Kucharski /*
23*23a1cceaSRoger A. Faulkner  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2444da779fSWilliam Kucharski  */
2544da779fSWilliam Kucharski 
2644da779fSWilliam Kucharski #include <stdio.h>
2744da779fSWilliam Kucharski #include <errno.h>
2844da779fSWilliam Kucharski #include <stdlib.h>
2944da779fSWilliam Kucharski #include <string.h>
3044da779fSWilliam Kucharski #include <unistd.h>
3144da779fSWilliam Kucharski #include <alloca.h>
3244da779fSWilliam Kucharski #include <ctype.h>
3344da779fSWilliam Kucharski #include <sys/types.h>
3444da779fSWilliam Kucharski 
3544da779fSWilliam Kucharski #include "message.h"
3644da779fSWilliam Kucharski #include "bootadm.h"
3744da779fSWilliam Kucharski 
3844da779fSWilliam Kucharski #define	HYPER_KERNEL_DIR 		"/platform/i86xpv/kernel"
3944da779fSWilliam Kucharski #define	METAL_KERNEL_DIR 		"/platform/i86pc/kernel"
4044da779fSWilliam Kucharski 
4144da779fSWilliam Kucharski #define	BOOTRC_FILE			"/boot/solaris/bootenv.rc"
4244da779fSWilliam Kucharski #define	ZFS_BOOTSTR			"$ZFS-BOOTFS"
4344da779fSWilliam Kucharski 
4444da779fSWilliam Kucharski #define	BFLAG				"-B"
4544da779fSWilliam Kucharski #define	DEFAULT_SERIAL			"9600,8,n,1"
4644da779fSWilliam Kucharski 
4744da779fSWilliam Kucharski #define	TTYXMODE_TO_COMNUM(ttyxmode)	((int)(*((ttyxmode) + 3) - '`'))
4844da779fSWilliam Kucharski #define	COMNAME_TO_COMNUM(comname)	((int)(*((comname) + 3) - '0'))
4944da779fSWilliam Kucharski 
5044da779fSWilliam Kucharski #define	WHITESPC(x)			(x)
5144da779fSWilliam Kucharski 
5244da779fSWilliam Kucharski static char *serial_config[2] = { NULL, NULL };
5344da779fSWilliam Kucharski static char *console_dev = NULL;
5444da779fSWilliam Kucharski 
55772d6a58SWilliam Kucharski static char *bootenv_rc_serial[2] = { NULL, NULL };
56772d6a58SWilliam Kucharski static char *bootenv_rc_console = NULL;
57772d6a58SWilliam Kucharski 
5844da779fSWilliam Kucharski static unsigned zfs_boot = 0;
5944da779fSWilliam Kucharski 
6044da779fSWilliam Kucharski /*
6144da779fSWilliam Kucharski  * Append the string pointed to by "str" to the string pointed to by "orig"
6244da779fSWilliam Kucharski  * adding the delimeter "delim" in between.
6344da779fSWilliam Kucharski  *
6466b6aef6SWilliam Kucharski  * Return a pointer to the new string or NULL, if we were passed a bad string.
6544da779fSWilliam Kucharski  */
6644da779fSWilliam Kucharski static char *
append_str(char * orig,char * str,char * delim)6744da779fSWilliam Kucharski append_str(char *orig, char *str, char *delim)
6844da779fSWilliam Kucharski {
6944da779fSWilliam Kucharski 	char *newstr;
7044da779fSWilliam Kucharski 	int len;
7144da779fSWilliam Kucharski 
7266b6aef6SWilliam Kucharski 	if ((str == NULL) || (delim == NULL))
7344da779fSWilliam Kucharski 		return (NULL);
7444da779fSWilliam Kucharski 
7566b6aef6SWilliam Kucharski 	if ((orig == NULL) || (*orig == NULL)) {
7666b6aef6SWilliam Kucharski 		/*
7766b6aef6SWilliam Kucharski 		 * Return a pointer to a copy of the path so a caller can
7866b6aef6SWilliam Kucharski 		 * always rely upon being able to free() a returned pointer.
7966b6aef6SWilliam Kucharski 		 */
8044da779fSWilliam Kucharski 		return (s_strdup(str));
8166b6aef6SWilliam Kucharski 	}
8244da779fSWilliam Kucharski 
8344da779fSWilliam Kucharski 	len = strlen(orig) + strlen(str) + strlen(delim) + 1;
8466b6aef6SWilliam Kucharski 	if ((newstr = malloc(len)) == NULL) {
8566b6aef6SWilliam Kucharski 		bam_error(NO_MEM, len);
8666b6aef6SWilliam Kucharski 		bam_exit(1);
8766b6aef6SWilliam Kucharski 	}
8844da779fSWilliam Kucharski 
8944da779fSWilliam Kucharski 	(void) snprintf(newstr, len, "%s%s%s", orig, delim, str);
9044da779fSWilliam Kucharski 	return (newstr);
9144da779fSWilliam Kucharski }
9244da779fSWilliam Kucharski 
9344da779fSWilliam Kucharski /*
9444da779fSWilliam Kucharski  * Replace the substring "old_str" in a path with the substring "new_str"
9544da779fSWilliam Kucharski  *
9666b6aef6SWilliam Kucharski  * Return a pointer to the modified string.
9744da779fSWilliam Kucharski  */
9844da779fSWilliam Kucharski static char *
modify_path(char * path,char * old_str,char * new_str)9944da779fSWilliam Kucharski modify_path(char *path, char *old_str, char *new_str)
10044da779fSWilliam Kucharski {
10144da779fSWilliam Kucharski 	char *newpath;
10244da779fSWilliam Kucharski 	char *pc;
10366b6aef6SWilliam Kucharski 	int len;
10444da779fSWilliam Kucharski 
10566b6aef6SWilliam Kucharski 	/*
10666b6aef6SWilliam Kucharski 	 * Return a pointer to a copy of the path so a caller can always rely
10766b6aef6SWilliam Kucharski 	 * upon being able to free() a returned pointer.
10866b6aef6SWilliam Kucharski 	 */
10966b6aef6SWilliam Kucharski 	if ((pc = strstr(path, old_str)) == NULL)
11066b6aef6SWilliam Kucharski 		return (s_strdup(path));
11144da779fSWilliam Kucharski 
11244da779fSWilliam Kucharski 	/*
11344da779fSWilliam Kucharski 	 * Allocate space for duplicate of path with name changes and
11444da779fSWilliam Kucharski 	 * NULL terminating byte
11544da779fSWilliam Kucharski 	 */
11666b6aef6SWilliam Kucharski 	len = strlen(path) - strlen(old_str) + strlen(new_str) + 1;
11766b6aef6SWilliam Kucharski 
11866b6aef6SWilliam Kucharski 	if ((newpath = malloc(len)) == NULL) {
11966b6aef6SWilliam Kucharski 		bam_error(NO_MEM, len);
12066b6aef6SWilliam Kucharski 		bam_exit(1);
12144da779fSWilliam Kucharski 	}
12244da779fSWilliam Kucharski 
12366b6aef6SWilliam Kucharski 	(void) strlcpy(newpath, path, (pc - path) + 1);
12466b6aef6SWilliam Kucharski 	pc += strlen(old_str);
12544da779fSWilliam Kucharski 
12644da779fSWilliam Kucharski 	(void) strcat(newpath, new_str);
12766b6aef6SWilliam Kucharski 	(void) strcat(newpath, pc);
12866b6aef6SWilliam Kucharski 	return (newpath);
12944da779fSWilliam Kucharski }
13044da779fSWilliam Kucharski 
13144da779fSWilliam Kucharski /*
13244da779fSWilliam Kucharski  * Set "token" to be the the string starting from the pointer "str" delimited
13344da779fSWilliam Kucharski  * by any character in the string "delim" or the end of the string, but IGNORE
13444da779fSWilliam Kucharski  * any characters between single or double quotes.
13544da779fSWilliam Kucharski  *
13644da779fSWilliam Kucharski  * Return a pointer to the next non-whitespace character after the delimiter
13744da779fSWilliam Kucharski  * or NULL if we hit the end of the string. Also return NULL upon failure to
13844da779fSWilliam Kucharski  * find any characters from the delimeter string or upon failure to allocate
13944da779fSWilliam Kucharski  * memory for the new token string.
14044da779fSWilliam Kucharski  */
14144da779fSWilliam Kucharski static char *
get_token(char ** token,char * str,char * delim)14244da779fSWilliam Kucharski get_token(char **token, char *str, char *delim)
14344da779fSWilliam Kucharski {
14444da779fSWilliam Kucharski 	char *dp;
14544da779fSWilliam Kucharski 	char *start = str;
14644da779fSWilliam Kucharski 	unsigned len;
14744da779fSWilliam Kucharski 
14844da779fSWilliam Kucharski 	*token = NULL;
14944da779fSWilliam Kucharski 
15044da779fSWilliam Kucharski 	if ((str == NULL) || (*str == NULL))
15144da779fSWilliam Kucharski 		return (NULL);
15244da779fSWilliam Kucharski 
15344da779fSWilliam Kucharski 	do {
15444da779fSWilliam Kucharski 		if ((*str == '\'') || (*str == '"')) {
15544da779fSWilliam Kucharski 			char quote = *str++;
15644da779fSWilliam Kucharski 
15744da779fSWilliam Kucharski 			while ((*str != NULL) && (*str != quote))
15844da779fSWilliam Kucharski 				str++;
15944da779fSWilliam Kucharski 
16044da779fSWilliam Kucharski 			/* no matching quote found in string */
16144da779fSWilliam Kucharski 			if (*str++ == NULL)
16244da779fSWilliam Kucharski 				return (NULL);
16344da779fSWilliam Kucharski 		}
16444da779fSWilliam Kucharski 
16544da779fSWilliam Kucharski 		/* look for a character from the delimiter string */
16644da779fSWilliam Kucharski 		for (dp = delim; ((*dp != NULL) && (*dp != *str)); dp++)
16744da779fSWilliam Kucharski 			;
16844da779fSWilliam Kucharski 
16944da779fSWilliam Kucharski 		if (*dp != NULL) {
17044da779fSWilliam Kucharski 			len = str - start + 1;
17144da779fSWilliam Kucharski 
17244da779fSWilliam Kucharski 			/* found a delimiter, so create a token string */
17344da779fSWilliam Kucharski 			if ((*token = malloc(len)) == NULL) {
17444da779fSWilliam Kucharski 				bam_error(NO_MEM, len);
17566b6aef6SWilliam Kucharski 				bam_exit(1);
17644da779fSWilliam Kucharski 			}
17744da779fSWilliam Kucharski 
17844da779fSWilliam Kucharski 			(void) strlcpy(*token, start, len);
17944da779fSWilliam Kucharski 
18044da779fSWilliam Kucharski 			while (isspace((int)*++str))
18144da779fSWilliam Kucharski 				;
18244da779fSWilliam Kucharski 
18344da779fSWilliam Kucharski 			return (str);
18444da779fSWilliam Kucharski 		}
18544da779fSWilliam Kucharski 	} while (*str++ != NULL);
18644da779fSWilliam Kucharski 
18744da779fSWilliam Kucharski 	/* if we hit the end of the string, the token is the whole string  */
18844da779fSWilliam Kucharski 	*token = s_strdup(start);
18944da779fSWilliam Kucharski 	return (NULL);
19044da779fSWilliam Kucharski }
19144da779fSWilliam Kucharski 
19244da779fSWilliam Kucharski /*
19344da779fSWilliam Kucharski  * Convert a metal "console" device name to an equivalent one suitable for
19444da779fSWilliam Kucharski  * use with the hypervisor.
19544da779fSWilliam Kucharski  *
19644da779fSWilliam Kucharski  * Default to "vga" if we can't parse the console device.
19744da779fSWilliam Kucharski  */
19844da779fSWilliam Kucharski static void
console_metal_to_hyper(char * console)19944da779fSWilliam Kucharski console_metal_to_hyper(char *console)
20044da779fSWilliam Kucharski {
20144da779fSWilliam Kucharski 	if ((*console == '\'') || (*console == '"'))
20244da779fSWilliam Kucharski 		console++;
20344da779fSWilliam Kucharski 
20444da779fSWilliam Kucharski 	if (strncmp(console, "ttya", 4) == 0)
20544da779fSWilliam Kucharski 		console_dev = "console=com1";
20644da779fSWilliam Kucharski 	else if (strncmp(console, "ttyb", 4) == 0)
20744da779fSWilliam Kucharski 		console_dev = "console=com2";
20844da779fSWilliam Kucharski 	else
20944da779fSWilliam Kucharski 		console_dev = "console=vga";
21044da779fSWilliam Kucharski }
21144da779fSWilliam Kucharski 
21244da779fSWilliam Kucharski static int
set_serial_rate(int com,char * rate)21344da779fSWilliam Kucharski set_serial_rate(int com, char *rate)
21444da779fSWilliam Kucharski {
21544da779fSWilliam Kucharski 	char **rp = &serial_config[com - 1];
21644da779fSWilliam Kucharski 
217772d6a58SWilliam Kucharski 	if ((com < 1) || (com > 2))
218772d6a58SWilliam Kucharski 		return (-1);
219772d6a58SWilliam Kucharski 
22044da779fSWilliam Kucharski 	/*
22144da779fSWilliam Kucharski 	 * If rate is a NULL pointer, erase any existing serial configuration
22244da779fSWilliam Kucharski 	 * for this serial port.
22344da779fSWilliam Kucharski 	 */
22444da779fSWilliam Kucharski 	if (rate == NULL) {
22544da779fSWilliam Kucharski 		if (*rp != NULL) {
22644da779fSWilliam Kucharski 			free(*rp);
22744da779fSWilliam Kucharski 			*rp = NULL;
22844da779fSWilliam Kucharski 		}
22944da779fSWilliam Kucharski 		return (0);
23044da779fSWilliam Kucharski 	}
23144da779fSWilliam Kucharski 
23266b6aef6SWilliam Kucharski 	*rp = s_realloc(*rp, strlen(rate) + 1);
23344da779fSWilliam Kucharski 	(void) strcpy(*rp, rate);
23444da779fSWilliam Kucharski 	return (0);
23544da779fSWilliam Kucharski }
23644da779fSWilliam Kucharski 
23744da779fSWilliam Kucharski /*
23844da779fSWilliam Kucharski  * Convert "metal" serial port parameters to values compatible with the
23944da779fSWilliam Kucharski  * hypervisor.
24044da779fSWilliam Kucharski  *
24144da779fSWilliam Kucharski  * Return 0 on success, otherwise -1.
24244da779fSWilliam Kucharski  */
24344da779fSWilliam Kucharski static int
serial_metal_to_hyper(char * metal_port,char * metal_serial)24444da779fSWilliam Kucharski serial_metal_to_hyper(char *metal_port, char *metal_serial)
24544da779fSWilliam Kucharski {
24644da779fSWilliam Kucharski #define	COM_RATE_LEN	16	/* strlen("com1=115200,8n1") */
24744da779fSWilliam Kucharski 
24844da779fSWilliam Kucharski 	char com_rate[COM_RATE_LEN];
24944da779fSWilliam Kucharski 
25044da779fSWilliam Kucharski 	unsigned com, baud, bits, stop;
25144da779fSWilliam Kucharski 	char parity, handshake;
25244da779fSWilliam Kucharski 
25344da779fSWilliam Kucharski 	if ((strcmp(metal_port, "ttya-mode") == 0) ||
25444da779fSWilliam Kucharski 	    (strcmp(metal_port, "ttyb-mode") == 0))
25544da779fSWilliam Kucharski 		com = TTYXMODE_TO_COMNUM(metal_port);
25644da779fSWilliam Kucharski 	else
25744da779fSWilliam Kucharski 		return (-1);
25844da779fSWilliam Kucharski 
25944da779fSWilliam Kucharski 	if ((*metal_serial == '\'') || (*metal_serial == '"'))
26044da779fSWilliam Kucharski 		metal_serial++;
26144da779fSWilliam Kucharski 
26244da779fSWilliam Kucharski 	/*
26344da779fSWilliam Kucharski 	 * Check if it's specified as the default rate; if so it defaults to
26444da779fSWilliam Kucharski 	 * "auto" and we need not set it for they hypervisor.
26544da779fSWilliam Kucharski 	 */
26644da779fSWilliam Kucharski 	if (strncmp(metal_serial, DEFAULT_SERIAL,
26744da779fSWilliam Kucharski 	    strlen(DEFAULT_SERIAL)) == 0) {
26844da779fSWilliam Kucharski 		(void) set_serial_rate(com, NULL);
26944da779fSWilliam Kucharski 		return (0);
27044da779fSWilliam Kucharski 	}
27144da779fSWilliam Kucharski 
27244da779fSWilliam Kucharski 	/* read the serial port format as set forth in common/io/asy.c */
27344da779fSWilliam Kucharski 	if (sscanf(metal_serial, "%u,%u,%c,%u,%c", &baud, &bits, &parity, &stop,
27444da779fSWilliam Kucharski 	    &handshake) != 5)
27544da779fSWilliam Kucharski 		return (-1);
27644da779fSWilliam Kucharski 
27744da779fSWilliam Kucharski 	/* validate serial port parameters */
27844da779fSWilliam Kucharski 	if (((bits < 5) || (bits > 8)) || (stop > 1) ||
27944da779fSWilliam Kucharski 	    ((parity != 'n') && (parity != 'e') && (parity != 'o')) ||
28044da779fSWilliam Kucharski 	    ((handshake != '-') && (handshake != 'h') && (handshake != 's')))
28144da779fSWilliam Kucharski 		return (-1);
28244da779fSWilliam Kucharski 
28344da779fSWilliam Kucharski 	/* validate baud rate */
28444da779fSWilliam Kucharski 	switch (baud) {
28544da779fSWilliam Kucharski 		case 150:
28644da779fSWilliam Kucharski 		case 300:
28744da779fSWilliam Kucharski 		case 600:
28844da779fSWilliam Kucharski 		case 1200:
28944da779fSWilliam Kucharski 		case 2400:
29044da779fSWilliam Kucharski 		case 4800:
29144da779fSWilliam Kucharski 		case 9600:
29244da779fSWilliam Kucharski 		case 19200:
29344da779fSWilliam Kucharski 		case 38400:
29444da779fSWilliam Kucharski 		case 57600:
29544da779fSWilliam Kucharski 		case 115200:
29644da779fSWilliam Kucharski 			break;
29744da779fSWilliam Kucharski 
29844da779fSWilliam Kucharski 		default:
29944da779fSWilliam Kucharski 			return (-1);
30044da779fSWilliam Kucharski 	}
30144da779fSWilliam Kucharski 
30244da779fSWilliam Kucharski 	/*
30344da779fSWilliam Kucharski 	 * The hypervisor has no way to specify a handshake method, so it gets
30444da779fSWilliam Kucharski 	 * quietly dropped in the conversion.
30544da779fSWilliam Kucharski 	 */
30644da779fSWilliam Kucharski 	(void) snprintf(com_rate, COM_RATE_LEN, "com%d=%u,%u%c%u", com, baud,
30744da779fSWilliam Kucharski 	    bits, parity, stop);
30844da779fSWilliam Kucharski 	(void) set_serial_rate(com, com_rate);
30944da779fSWilliam Kucharski 	return (0);
31044da779fSWilliam Kucharski }
31144da779fSWilliam Kucharski 
31244da779fSWilliam Kucharski /*
31344da779fSWilliam Kucharski  * Convert "name=value" metal options to values suitable for use with the
31444da779fSWilliam Kucharski  * hypervisor.
31544da779fSWilliam Kucharski  *
31644da779fSWilliam Kucharski  * Our main concerns are the console device and serial port settings.
31744da779fSWilliam Kucharski  *
31844da779fSWilliam Kucharski  * Return values:
31944da779fSWilliam Kucharski  *
32044da779fSWilliam Kucharski  *    -1:	Unparseable line
32144da779fSWilliam Kucharski  *    0:	Success
32244da779fSWilliam Kucharski  *    (n > 0):	A property unimportant to us
32344da779fSWilliam Kucharski  */
32444da779fSWilliam Kucharski static int
cvt_metal_option(char * optstr)32544da779fSWilliam Kucharski cvt_metal_option(char *optstr)
32644da779fSWilliam Kucharski {
32744da779fSWilliam Kucharski 	char *value;
32844da779fSWilliam Kucharski 	unsigned namlen;
32944da779fSWilliam Kucharski 
33044da779fSWilliam Kucharski 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
33144da779fSWilliam Kucharski 		zfs_boot = 1;
33244da779fSWilliam Kucharski 		return (0);
33344da779fSWilliam Kucharski 	}
33444da779fSWilliam Kucharski 
33544da779fSWilliam Kucharski 	if ((value = strchr(optstr, '=')) == NULL)
33644da779fSWilliam Kucharski 		return (-1);
33744da779fSWilliam Kucharski 
33844da779fSWilliam Kucharski 	namlen = value - optstr;
33944da779fSWilliam Kucharski 
34044da779fSWilliam Kucharski 	if (*++value == NULL)
34144da779fSWilliam Kucharski 		return (1);
34244da779fSWilliam Kucharski 
34344da779fSWilliam Kucharski 	if (strncmp(optstr, "console", namlen) == 0) {
34444da779fSWilliam Kucharski 		console_metal_to_hyper(value);
34544da779fSWilliam Kucharski 		return (0);
34644da779fSWilliam Kucharski 	}
34744da779fSWilliam Kucharski 
34844da779fSWilliam Kucharski 	if ((strncmp(optstr, "ttya-mode", namlen) == 0) ||
34944da779fSWilliam Kucharski 	    (strncmp(optstr, "ttyb-mode", namlen) == 0)) {
350*23a1cceaSRoger A. Faulkner 		char *port = strndupa(optstr, namlen);
35144da779fSWilliam Kucharski 
35244da779fSWilliam Kucharski 		return (serial_metal_to_hyper(port, value));
35344da779fSWilliam Kucharski 	}
35444da779fSWilliam Kucharski 
35544da779fSWilliam Kucharski 	return (1);
35644da779fSWilliam Kucharski }
35744da779fSWilliam Kucharski 
35844da779fSWilliam Kucharski /*
35944da779fSWilliam Kucharski  * Convert "name=value" properties for use with a bare metal kernel
36044da779fSWilliam Kucharski  *
36144da779fSWilliam Kucharski  * Our main concerns are the console setting and serial port modes.
36244da779fSWilliam Kucharski  *
36344da779fSWilliam Kucharski  * Return values:
36444da779fSWilliam Kucharski  *
36544da779fSWilliam Kucharski  *    -1:	Unparseable line
36644da779fSWilliam Kucharski  *    0:	Success
36744da779fSWilliam Kucharski  *    (n > 0):	A property unimportant to us
36844da779fSWilliam Kucharski  */
36944da779fSWilliam Kucharski static int
cvt_hyper_option(char * optstr)37044da779fSWilliam Kucharski cvt_hyper_option(char *optstr)
37144da779fSWilliam Kucharski {
372772d6a58SWilliam Kucharski #define	SER_LEN		15	/* strlen("115200,8,n,1,-") + 1 */
37344da779fSWilliam Kucharski 
37444da779fSWilliam Kucharski 	char ser[SER_LEN];
37544da779fSWilliam Kucharski 	char *value;
37644da779fSWilliam Kucharski 
37744da779fSWilliam Kucharski 	unsigned namlen;
37844da779fSWilliam Kucharski 
37944da779fSWilliam Kucharski 	unsigned baud;
38044da779fSWilliam Kucharski 	char bits, parity, stop;
38144da779fSWilliam Kucharski 
38244da779fSWilliam Kucharski 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
38344da779fSWilliam Kucharski 		zfs_boot = 1;
38444da779fSWilliam Kucharski 		return (0);
38544da779fSWilliam Kucharski 	}
38644da779fSWilliam Kucharski 
38744da779fSWilliam Kucharski 	/*
38844da779fSWilliam Kucharski 	 * If there's no "=" in the token, it's likely a standalone
38944da779fSWilliam Kucharski 	 * hypervisor token we don't care about (e.g. "noreboot" or
39044da779fSWilliam Kucharski 	 * "nosmp") so we ignore it.
39144da779fSWilliam Kucharski 	 */
39244da779fSWilliam Kucharski 	if ((value = strchr(optstr, '=')) == NULL)
39344da779fSWilliam Kucharski 		return (1);
39444da779fSWilliam Kucharski 
39544da779fSWilliam Kucharski 	namlen = value - optstr;
39644da779fSWilliam Kucharski 
39744da779fSWilliam Kucharski 	if (*++value == NULL)
39844da779fSWilliam Kucharski 		return (1);
39944da779fSWilliam Kucharski 
40044da779fSWilliam Kucharski 	/*
40144da779fSWilliam Kucharski 	 * Note that we use strncmp against the values because the
40244da779fSWilliam Kucharski 	 * hypervisor allows setting console parameters for both the
40344da779fSWilliam Kucharski 	 * console and debugger via the format:
40444da779fSWilliam Kucharski 	 *
40544da779fSWilliam Kucharski 	 *   console=cons_dev,debug_dev
40644da779fSWilliam Kucharski 	 *
40744da779fSWilliam Kucharski 	 * and we only care about "cons_dev."
40844da779fSWilliam Kucharski 	 *
40944da779fSWilliam Kucharski 	 * This also allows us to extract "comN" from hypervisor constructs
41044da779fSWilliam Kucharski 	 * like "com1H" or "com2L," concepts unsupported on bare metal kernels.
41144da779fSWilliam Kucharski 	 *
41244da779fSWilliam Kucharski 	 * Default the console device to "text" if it was "vga" or was
41344da779fSWilliam Kucharski 	 * unparseable.
41444da779fSWilliam Kucharski 	 */
41544da779fSWilliam Kucharski 	if (strncmp(optstr, "console", namlen) == 0) {
41644da779fSWilliam Kucharski 		/* ignore the "console=hypervisor" option */
41744da779fSWilliam Kucharski 		if (strcmp(value, "hypervisor") == 0)
41844da779fSWilliam Kucharski 			return (0);
41944da779fSWilliam Kucharski 
42044da779fSWilliam Kucharski 		if (strncmp(value, "com1", 4) == 0)
421772d6a58SWilliam Kucharski 			console_dev = "ttya";
42244da779fSWilliam Kucharski 		else if (strncmp(value, "com2", 4) == 0)
423772d6a58SWilliam Kucharski 			console_dev = "ttyb";
42444da779fSWilliam Kucharski 		else
425772d6a58SWilliam Kucharski 			console_dev = "text";
42644da779fSWilliam Kucharski 	}
42744da779fSWilliam Kucharski 
42844da779fSWilliam Kucharski 	/* serial port parameter conversion */
42944da779fSWilliam Kucharski 
43044da779fSWilliam Kucharski 	if ((strncmp(optstr, "com1", namlen) == 0) ||
43144da779fSWilliam Kucharski 	    (strncmp(optstr, "com2", namlen) == 0)) {
43244da779fSWilliam Kucharski 		unsigned com = COMNAME_TO_COMNUM(optstr);
43344da779fSWilliam Kucharski 
43444da779fSWilliam Kucharski 		/*
43544da779fSWilliam Kucharski 		 * Check if it's "auto" - if so, use the default setting
43644da779fSWilliam Kucharski 		 * of "9600,8,n,1,-".
43744da779fSWilliam Kucharski 		 *
43844da779fSWilliam Kucharski 		 * We can't just assume the serial port will default to
43944da779fSWilliam Kucharski 		 * "9600,8,n,1" as there could be a directive in bootenv.rc
44044da779fSWilliam Kucharski 		 * that would set it to some other value and we want the serial
44144da779fSWilliam Kucharski 		 * parameters to be the same as that used by the hypervisor.
44244da779fSWilliam Kucharski 		 */
44344da779fSWilliam Kucharski 		if (strcmp(value, "auto") == 0) {
444772d6a58SWilliam Kucharski 			(void) snprintf(ser, SER_LEN, "9600,8,n,1,-");
445772d6a58SWilliam Kucharski 		} else {
44644da779fSWilliam Kucharski 			/*
447772d6a58SWilliam Kucharski 			 * Extract the "B,PS" setting from the com line; ignore
448772d6a58SWilliam Kucharski 			 * other settings like io_base or IRQ.
44944da779fSWilliam Kucharski 			 */
45044da779fSWilliam Kucharski 			if (sscanf(value, "%u,%c%c%c", &baud, &bits, &parity,
45144da779fSWilliam Kucharski 			    &stop) != 4)
45244da779fSWilliam Kucharski 				return (-1);
45344da779fSWilliam Kucharski 
45444da779fSWilliam Kucharski 			/* validate serial port parameters */
45544da779fSWilliam Kucharski 			if (((stop != '0') && (stop != '1')) ||
45644da779fSWilliam Kucharski 			    ((bits < '5') && (bits > '8')) ||
457772d6a58SWilliam Kucharski 			    ((parity != 'n') && (parity != 'e') &&
458772d6a58SWilliam Kucharski 			    (parity != 'o')))
45944da779fSWilliam Kucharski 				return (-1);
46044da779fSWilliam Kucharski 
46144da779fSWilliam Kucharski 			/* validate baud rate */
46244da779fSWilliam Kucharski 			switch (baud) {
46344da779fSWilliam Kucharski 				case 150:
46444da779fSWilliam Kucharski 				case 300:
46544da779fSWilliam Kucharski 				case 600:
46644da779fSWilliam Kucharski 				case 1200:
46744da779fSWilliam Kucharski 				case 2400:
46844da779fSWilliam Kucharski 				case 4800:
46944da779fSWilliam Kucharski 				case 19200:
47044da779fSWilliam Kucharski 				case 38400:
47144da779fSWilliam Kucharski 				case 57600:
47244da779fSWilliam Kucharski 				case 115200:
47344da779fSWilliam Kucharski 					break;
47444da779fSWilliam Kucharski 
47544da779fSWilliam Kucharski 				default:
47644da779fSWilliam Kucharski 					return (-1);
47744da779fSWilliam Kucharski 			}
47844da779fSWilliam Kucharski 
47944da779fSWilliam Kucharski 			/*
480772d6a58SWilliam Kucharski 			 * As the hypervisor has no way to denote handshaking
481772d6a58SWilliam Kucharski 			 * in its serial port settings, emit a metal serial
482772d6a58SWilliam Kucharski 			 * port configuration with none as well.
48344da779fSWilliam Kucharski 			 */
484772d6a58SWilliam Kucharski 			(void) snprintf(ser, SER_LEN, "%u,%c,%c,%c,-", baud,
485772d6a58SWilliam Kucharski 			    bits, parity, stop);
486772d6a58SWilliam Kucharski 		}
48744da779fSWilliam Kucharski 
48844da779fSWilliam Kucharski 		if (set_serial_rate(com, ser) != 0)
48944da779fSWilliam Kucharski 			return (-1);
49044da779fSWilliam Kucharski 
49144da779fSWilliam Kucharski 		return (0);
49244da779fSWilliam Kucharski 	}
49344da779fSWilliam Kucharski 
49444da779fSWilliam Kucharski 	return (1);
49544da779fSWilliam Kucharski }
49644da779fSWilliam Kucharski 
49744da779fSWilliam Kucharski /*
49844da779fSWilliam Kucharski  * Parse a hardware kernel's "kernel$" specifier into parameters we can then
49944da779fSWilliam Kucharski  * use to construct an appropriate "module$" line that can be used to specify
50044da779fSWilliam Kucharski  * how to boot the hypervisor's dom0.
50144da779fSWilliam Kucharski  *
50266b6aef6SWilliam Kucharski  * Return values:
50366b6aef6SWilliam Kucharski  *
50466b6aef6SWilliam Kucharski  *	-1: error parsing kernel path
50566b6aef6SWilliam Kucharski  *	 0: success
50666b6aef6SWilliam Kucharski  *	 1: kernel already a hypervisor kernel
50744da779fSWilliam Kucharski  */
50844da779fSWilliam Kucharski static int
cvt_metal_kernel(char * kernstr,char ** path)50944da779fSWilliam Kucharski cvt_metal_kernel(char *kernstr, char **path)
51044da779fSWilliam Kucharski {
51144da779fSWilliam Kucharski 	char *token, *parsestr;
51244da779fSWilliam Kucharski 
51366b6aef6SWilliam Kucharski 	parsestr = get_token(path, kernstr, " \t,");
51466b6aef6SWilliam Kucharski 	if (*path == NULL)
51566b6aef6SWilliam Kucharski 		return (-1);
51644da779fSWilliam Kucharski 
51744da779fSWilliam Kucharski 	/*
51844da779fSWilliam Kucharski 	 * If the metal kernel specified contains the name of the hypervisor,
51944da779fSWilliam Kucharski 	 * we're probably trying to convert an entry already setup to run the
52044da779fSWilliam Kucharski 	 * hypervisor, so error out now.
52144da779fSWilliam Kucharski 	 */
52244da779fSWilliam Kucharski 	if (strstr(*path, XEN_MENU) != NULL) {
52344da779fSWilliam Kucharski 		bam_error(ALREADY_HYPER);
52466b6aef6SWilliam Kucharski 		free(*path);
52566b6aef6SWilliam Kucharski 		*path = NULL;
52666b6aef6SWilliam Kucharski 		return (1);
52744da779fSWilliam Kucharski 	}
52844da779fSWilliam Kucharski 
52944da779fSWilliam Kucharski 	/* if the path was the last item on the line, that's OK. */
53044da779fSWilliam Kucharski 	if ((parsestr = get_token(&token, parsestr, " \t,")) == NULL) {
53144da779fSWilliam Kucharski 		if (token != NULL)
53244da779fSWilliam Kucharski 			free(token);
53344da779fSWilliam Kucharski 		return (0);
53444da779fSWilliam Kucharski 	}
53544da779fSWilliam Kucharski 
53644da779fSWilliam Kucharski 	/* if the next token is "-B" process boot options */
53744da779fSWilliam Kucharski 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
53844da779fSWilliam Kucharski 		free(token);
53944da779fSWilliam Kucharski 		return (0);
54044da779fSWilliam Kucharski 	}
54144da779fSWilliam Kucharski 
54266b6aef6SWilliam Kucharski 	free(token);
54366b6aef6SWilliam Kucharski 
54444da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
54544da779fSWilliam Kucharski 		(void) cvt_metal_option(token);
54644da779fSWilliam Kucharski 		free(token);
54744da779fSWilliam Kucharski 	}
54844da779fSWilliam Kucharski 
54944da779fSWilliam Kucharski 	if (token != NULL) {
55044da779fSWilliam Kucharski 		(void) cvt_metal_option(token);
55144da779fSWilliam Kucharski 		free(token);
55244da779fSWilliam Kucharski 	}
55344da779fSWilliam Kucharski 
55444da779fSWilliam Kucharski 	return (0);
55544da779fSWilliam Kucharski }
55644da779fSWilliam Kucharski 
55744da779fSWilliam Kucharski /*
55844da779fSWilliam Kucharski  * Parse a hypervisor's "kernel$" line into parameters that can be used to
55944da779fSWilliam Kucharski  * help build an appropriate "kernel$" line for booting a bare metal kernel.
56044da779fSWilliam Kucharski  *
56144da779fSWilliam Kucharski  * Return 0 on success, non-zero on failure.
56244da779fSWilliam Kucharski  */
56344da779fSWilliam Kucharski static int
cvt_hyper_kernel(char * kernel)56444da779fSWilliam Kucharski cvt_hyper_kernel(char *kernel)
56544da779fSWilliam Kucharski {
56644da779fSWilliam Kucharski 	char *token, *parsestr;
56744da779fSWilliam Kucharski 
56866b6aef6SWilliam Kucharski 	parsestr = get_token(&token, kernel, " \t,");
56966b6aef6SWilliam Kucharski 
57066b6aef6SWilliam Kucharski 	if (token == NULL)
57166b6aef6SWilliam Kucharski 		return (-1);
57244da779fSWilliam Kucharski 
57344da779fSWilliam Kucharski 	/*
57444da779fSWilliam Kucharski 	 * If the hypervisor kernel specified lives in the metal kernel
57544da779fSWilliam Kucharski 	 * directory, we're probably trying to convert an entry already setup
57644da779fSWilliam Kucharski 	 * to run on bare metal, so error out now.
57744da779fSWilliam Kucharski 	 */
57844da779fSWilliam Kucharski 	if (strncmp(token, METAL_KERNEL_DIR, strlen(METAL_KERNEL_DIR)) == 0) {
57944da779fSWilliam Kucharski 		bam_error(ALREADY_METAL);
58066b6aef6SWilliam Kucharski 		free(token);
58144da779fSWilliam Kucharski 		return (-1);
58244da779fSWilliam Kucharski 	}
58344da779fSWilliam Kucharski 
58444da779fSWilliam Kucharski 	free(token);
58544da779fSWilliam Kucharski 
58644da779fSWilliam Kucharski 	/* check for kernel options */
58744da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, " ")) != NULL) {
58844da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
58944da779fSWilliam Kucharski 		free(token);
59044da779fSWilliam Kucharski 	}
59144da779fSWilliam Kucharski 
59244da779fSWilliam Kucharski 	if (token != NULL) {
59344da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
59444da779fSWilliam Kucharski 		free(token);
59544da779fSWilliam Kucharski 	}
59644da779fSWilliam Kucharski 
59744da779fSWilliam Kucharski 	return (0);
59844da779fSWilliam Kucharski }
59944da779fSWilliam Kucharski 
60044da779fSWilliam Kucharski /*
60144da779fSWilliam Kucharski  * Parse a hypervisor's "module$" line into parameters that can be used to
60244da779fSWilliam Kucharski  * help build an appropriate "kernel$" line for booting a bare metal kernel.
60344da779fSWilliam Kucharski  */
60444da779fSWilliam Kucharski static void
cvt_hyper_module(char * modstr,char ** path)60544da779fSWilliam Kucharski cvt_hyper_module(char *modstr, char **path)
60644da779fSWilliam Kucharski {
60766b6aef6SWilliam Kucharski 	char *token = NULL;
60844da779fSWilliam Kucharski 	char *parsestr = modstr;
60944da779fSWilliam Kucharski 
61044da779fSWilliam Kucharski 	/*
61144da779fSWilliam Kucharski 	 * If multiple pathnames exist on the module$ line, we just want
61244da779fSWilliam Kucharski 	 * the last one.
61344da779fSWilliam Kucharski 	 */
61444da779fSWilliam Kucharski 	while ((parsestr = get_token(path, parsestr, " \t,")) != NULL) {
61544da779fSWilliam Kucharski 		if (*parsestr != '/')
61644da779fSWilliam Kucharski 			break;
61766b6aef6SWilliam Kucharski 		else
61844da779fSWilliam Kucharski 			free(*path);
61944da779fSWilliam Kucharski 	}
62044da779fSWilliam Kucharski 
62144da779fSWilliam Kucharski 	/* if the path was the last item on the line, that's OK. */
62244da779fSWilliam Kucharski 	if ((parsestr == NULL) ||
62344da779fSWilliam Kucharski 	    ((parsestr = get_token(&token, parsestr, " \t,")) == NULL)) {
62444da779fSWilliam Kucharski 		if (token != NULL)
62544da779fSWilliam Kucharski 			free(token);
62644da779fSWilliam Kucharski 		return;
62744da779fSWilliam Kucharski 	}
62844da779fSWilliam Kucharski 
62966b6aef6SWilliam Kucharski 	if (token == NULL)
63066b6aef6SWilliam Kucharski 		return;
63166b6aef6SWilliam Kucharski 
63244da779fSWilliam Kucharski 	/* check for "-B" option */
63344da779fSWilliam Kucharski 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
63444da779fSWilliam Kucharski 		free(token);
63544da779fSWilliam Kucharski 		return;
63644da779fSWilliam Kucharski 	}
63744da779fSWilliam Kucharski 
63866b6aef6SWilliam Kucharski 	free(token);
63966b6aef6SWilliam Kucharski 
64044da779fSWilliam Kucharski 	/* check for kernel options */
64144da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
64244da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
64344da779fSWilliam Kucharski 		free(token);
64444da779fSWilliam Kucharski 	}
64544da779fSWilliam Kucharski 
64644da779fSWilliam Kucharski 	if (token != NULL) {
64744da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
64844da779fSWilliam Kucharski 		free(token);
64944da779fSWilliam Kucharski 	}
65044da779fSWilliam Kucharski }
65144da779fSWilliam Kucharski 
65244da779fSWilliam Kucharski static void
parse_bootenvrc(char * osroot)65344da779fSWilliam Kucharski parse_bootenvrc(char *osroot)
65444da779fSWilliam Kucharski {
65544da779fSWilliam Kucharski #define	LINEBUF_SZ	1024
65644da779fSWilliam Kucharski 
65744da779fSWilliam Kucharski 	FILE *fp;
65844da779fSWilliam Kucharski 	char *rcpath;
65944da779fSWilliam Kucharski 	char line[LINEBUF_SZ];	/* make line buffer large but not ridiculous */
66044da779fSWilliam Kucharski 	int len;
66144da779fSWilliam Kucharski 
66244da779fSWilliam Kucharski 	assert(osroot);
66344da779fSWilliam Kucharski 
66444da779fSWilliam Kucharski 	len = strlen(osroot) + strlen(BOOTRC_FILE) + 1;
66544da779fSWilliam Kucharski 	rcpath = alloca(len);
66666b6aef6SWilliam Kucharski 
66744da779fSWilliam Kucharski 	(void) snprintf(rcpath, len, "%s%s", osroot, BOOTRC_FILE);
66844da779fSWilliam Kucharski 
66944da779fSWilliam Kucharski 	/* if we couldn't open the bootenv.rc file, ignore the issue. */
67044da779fSWilliam Kucharski 	if ((fp = fopen(rcpath, "r")) == NULL) {
67144da779fSWilliam Kucharski 		BAM_DPRINTF((D_NO_BOOTENVRC, rcpath, strerror(errno)));
67244da779fSWilliam Kucharski 		return;
67344da779fSWilliam Kucharski 	}
67444da779fSWilliam Kucharski 
67544da779fSWilliam Kucharski 	while (s_fgets(line, LINEBUF_SZ, fp) != NULL) {
676772d6a58SWilliam Kucharski 		char *parsestr, *token;
677772d6a58SWilliam Kucharski 		int port = 0;
678772d6a58SWilliam Kucharski 
67944da779fSWilliam Kucharski 		/* we're only interested in parsing "setprop" directives. */
68044da779fSWilliam Kucharski 		if (strncmp(line, "setprop", 7) != NULL)
68144da779fSWilliam Kucharski 			continue;
68244da779fSWilliam Kucharski 
683772d6a58SWilliam Kucharski 		/* eat initial "setprop" */
684772d6a58SWilliam Kucharski 		if ((parsestr = get_token(&token, line, " \t")) == NULL) {
685772d6a58SWilliam Kucharski 			if (token != NULL)
686772d6a58SWilliam Kucharski 				free(token);
687772d6a58SWilliam Kucharski 
688772d6a58SWilliam Kucharski 			continue;
689772d6a58SWilliam Kucharski 		}
690772d6a58SWilliam Kucharski 
691772d6a58SWilliam Kucharski 		if (strcmp(token, "setprop") != 0) {
692772d6a58SWilliam Kucharski 			free(token);
693772d6a58SWilliam Kucharski 			continue;
694772d6a58SWilliam Kucharski 		}
695772d6a58SWilliam Kucharski 
696772d6a58SWilliam Kucharski 		free(token);
697772d6a58SWilliam Kucharski 
698772d6a58SWilliam Kucharski 		/* get property name */
699772d6a58SWilliam Kucharski 		if ((parsestr = get_token(&token, parsestr, " \t")) == NULL) {
700772d6a58SWilliam Kucharski 			if (token != NULL)
701772d6a58SWilliam Kucharski 				free(token);
702772d6a58SWilliam Kucharski 
703772d6a58SWilliam Kucharski 			continue;
704772d6a58SWilliam Kucharski 		}
705772d6a58SWilliam Kucharski 
706772d6a58SWilliam Kucharski 		if (strcmp(token, "console") == 0) {
707772d6a58SWilliam Kucharski 			free(token);
708772d6a58SWilliam Kucharski 
709772d6a58SWilliam Kucharski 			/* get console property value */
710772d6a58SWilliam Kucharski 			parsestr = get_token(&token, parsestr, " \t");
711772d6a58SWilliam Kucharski 			if (token == NULL)
712772d6a58SWilliam Kucharski 				continue;
713772d6a58SWilliam Kucharski 
714772d6a58SWilliam Kucharski 			if (bootenv_rc_console != NULL)
715772d6a58SWilliam Kucharski 				free(bootenv_rc_console);
716772d6a58SWilliam Kucharski 
717772d6a58SWilliam Kucharski 			bootenv_rc_console = s_strdup(token);
718772d6a58SWilliam Kucharski 			continue;
719772d6a58SWilliam Kucharski 		}
720772d6a58SWilliam Kucharski 
721772d6a58SWilliam Kucharski 		/* check if it's a serial port setting */
722772d6a58SWilliam Kucharski 		if (strcmp(token, "ttya-mode") == 0) {
723772d6a58SWilliam Kucharski 			free(token);
724772d6a58SWilliam Kucharski 			port = 0;
725772d6a58SWilliam Kucharski 		} else if (strcmp(token, "ttyb-mode") == 0) {
726772d6a58SWilliam Kucharski 			free(token);
727772d6a58SWilliam Kucharski 			port = 1;
728772d6a58SWilliam Kucharski 		} else {
729772d6a58SWilliam Kucharski 			/* nope, so check the next line */
730772d6a58SWilliam Kucharski 			free(token);
731772d6a58SWilliam Kucharski 			continue;
732772d6a58SWilliam Kucharski 		}
733772d6a58SWilliam Kucharski 
734772d6a58SWilliam Kucharski 		/* get serial port setting */
735772d6a58SWilliam Kucharski 		parsestr = get_token(&token, parsestr, " \t");
736772d6a58SWilliam Kucharski 
737772d6a58SWilliam Kucharski 		if (token == NULL)
738772d6a58SWilliam Kucharski 			continue;
739772d6a58SWilliam Kucharski 
740772d6a58SWilliam Kucharski 		if (bootenv_rc_serial[port] != NULL)
741772d6a58SWilliam Kucharski 			free(bootenv_rc_serial[port]);
742772d6a58SWilliam Kucharski 
743772d6a58SWilliam Kucharski 		bootenv_rc_serial[port] = s_strdup(token);
744772d6a58SWilliam Kucharski 		free(token);
74544da779fSWilliam Kucharski 	}
74644da779fSWilliam Kucharski 
74744da779fSWilliam Kucharski 	(void) fclose(fp);
74844da779fSWilliam Kucharski }
74944da779fSWilliam Kucharski 
75044da779fSWilliam Kucharski error_t
cvt_to_hyper(menu_t * mp,char * osroot,char * extra_args)75144da779fSWilliam Kucharski cvt_to_hyper(menu_t *mp, char *osroot, char *extra_args)
75244da779fSWilliam Kucharski {
75344da779fSWilliam Kucharski 	const char *fcn = "cvt_to_hyper()";
75444da779fSWilliam Kucharski 
75544da779fSWilliam Kucharski 	line_t *lp;
75644da779fSWilliam Kucharski 	entry_t *ent;
75744da779fSWilliam Kucharski 	size_t len, zfslen;
75844da779fSWilliam Kucharski 
75966b6aef6SWilliam Kucharski 	char *newstr;
76044da779fSWilliam Kucharski 	char *osdev;
76144da779fSWilliam Kucharski 
76244da779fSWilliam Kucharski 	char *title = NULL;
76344da779fSWilliam Kucharski 	char *findroot = NULL;
76444da779fSWilliam Kucharski 	char *bootfs = NULL;
76544da779fSWilliam Kucharski 	char *kernel = NULL;
76644da779fSWilliam Kucharski 	char *mod_kernel = NULL;
76744da779fSWilliam Kucharski 	char *module = NULL;
76844da779fSWilliam Kucharski 
76944da779fSWilliam Kucharski 	char *kern_path = NULL;
77044da779fSWilliam Kucharski 	char *kern_bargs = NULL;
77144da779fSWilliam Kucharski 
772772d6a58SWilliam Kucharski 	int curdef, newdef;
77366b6aef6SWilliam Kucharski 	int kp_allocated = 0;
77444da779fSWilliam Kucharski 	int ret = BAM_ERROR;
77544da779fSWilliam Kucharski 
77644da779fSWilliam Kucharski 	assert(osroot);
77744da779fSWilliam Kucharski 
77844da779fSWilliam Kucharski 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, extra_args));
77944da779fSWilliam Kucharski 
78044da779fSWilliam Kucharski 	/*
78144da779fSWilliam Kucharski 	 * First just check to verify osroot is a sane directory.
78244da779fSWilliam Kucharski 	 */
78344da779fSWilliam Kucharski 	if ((osdev = get_special(osroot)) == NULL) {
78444da779fSWilliam Kucharski 		bam_error(CANT_FIND_SPECIAL, osroot);
78544da779fSWilliam Kucharski 		return (BAM_ERROR);
78644da779fSWilliam Kucharski 	}
78744da779fSWilliam Kucharski 
78844da779fSWilliam Kucharski 	free(osdev);
78944da779fSWilliam Kucharski 
79044da779fSWilliam Kucharski 	/*
79144da779fSWilliam Kucharski 	 * While the effect is purely cosmetic, if osroot is "/" don't
79244da779fSWilliam Kucharski 	 * bother prepending it to any paths as they are constructed to
79344da779fSWilliam Kucharski 	 * begin with "/" anyway.
79444da779fSWilliam Kucharski 	 */
79544da779fSWilliam Kucharski 	if (strcmp(osroot, "/") == 0)
79644da779fSWilliam Kucharski 		osroot = "";
79744da779fSWilliam Kucharski 
79844da779fSWilliam Kucharski 	/*
79944da779fSWilliam Kucharski 	 * Found the GRUB signature on the target partitions, so now get the
80044da779fSWilliam Kucharski 	 * default GRUB boot entry number from the menu.lst file
80144da779fSWilliam Kucharski 	 */
80244da779fSWilliam Kucharski 	curdef = atoi(mp->curdefault->arg);
80344da779fSWilliam Kucharski 
80444da779fSWilliam Kucharski 	/* look for the first line of the matching boot entry */
80544da779fSWilliam Kucharski 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
80644da779fSWilliam Kucharski 	    ent = ent->next)
80744da779fSWilliam Kucharski 		;
80844da779fSWilliam Kucharski 
80944da779fSWilliam Kucharski 	/* couldn't find it, so error out */
81044da779fSWilliam Kucharski 	if (ent == NULL) {
81144da779fSWilliam Kucharski 		bam_error(CANT_FIND_DEFAULT, curdef);
81244da779fSWilliam Kucharski 		goto abort;
81344da779fSWilliam Kucharski 	}
81444da779fSWilliam Kucharski 
81544da779fSWilliam Kucharski 	/*
81644da779fSWilliam Kucharski 	 * We found the proper menu entry, so first we need to process the
81744da779fSWilliam Kucharski 	 * bootenv.rc file to look for boot options the hypervisor might need
81844da779fSWilliam Kucharski 	 * passed as kernel start options such as the console device and serial
81944da779fSWilliam Kucharski 	 * port parameters.
82044da779fSWilliam Kucharski 	 *
82144da779fSWilliam Kucharski 	 * If there's no bootenv.rc, it's not an issue.
82244da779fSWilliam Kucharski 	 */
82344da779fSWilliam Kucharski 	parse_bootenvrc(osroot);
82444da779fSWilliam Kucharski 
825772d6a58SWilliam Kucharski 	if (bootenv_rc_console != NULL)
826772d6a58SWilliam Kucharski 		console_metal_to_hyper(bootenv_rc_console);
827772d6a58SWilliam Kucharski 
828772d6a58SWilliam Kucharski 	if (bootenv_rc_serial[0] != NULL)
829772d6a58SWilliam Kucharski 		(void) serial_metal_to_hyper("ttya-mode", bootenv_rc_serial[0]);
830772d6a58SWilliam Kucharski 
831772d6a58SWilliam Kucharski 	if (bootenv_rc_serial[1] != NULL)
832772d6a58SWilliam Kucharski 		(void) serial_metal_to_hyper("ttyb-mode", bootenv_rc_serial[1]);
833772d6a58SWilliam Kucharski 
83444da779fSWilliam Kucharski 	/*
83544da779fSWilliam Kucharski 	 * Now process the entry itself.
83644da779fSWilliam Kucharski 	 */
83744da779fSWilliam Kucharski 	for (lp = ent->start; lp != NULL; lp = lp->next) {
83844da779fSWilliam Kucharski 		/*
83944da779fSWilliam Kucharski 		 * Process important lines from menu.lst boot entry.
84044da779fSWilliam Kucharski 		 */
84144da779fSWilliam Kucharski 		if (lp->flags == BAM_TITLE) {
842*23a1cceaSRoger A. Faulkner 			title = strdupa(lp->arg);
843eac223ccSWilliam Kucharski 		} else if (lp->cmd != NULL) {
844eac223ccSWilliam Kucharski 			if (strcmp(lp->cmd, "findroot") == 0) {
845*23a1cceaSRoger A. Faulkner 				findroot = strdupa(lp->arg);
84644da779fSWilliam Kucharski 			} else if (strcmp(lp->cmd, "bootfs") == 0) {
847*23a1cceaSRoger A. Faulkner 				bootfs = strdupa(lp->arg);
848eac223ccSWilliam Kucharski 			} else if (strcmp(lp->cmd,
849eac223ccSWilliam Kucharski 			    menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
850*23a1cceaSRoger A. Faulkner 				module = strdupa(lp->arg);
85144da779fSWilliam Kucharski 			} else if ((strcmp(lp->cmd,
85244da779fSWilliam Kucharski 			    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
853eac223ccSWilliam Kucharski 			    (ret = cvt_metal_kernel(lp->arg,
854eac223ccSWilliam Kucharski 			    &kern_path)) != 0) {
85566b6aef6SWilliam Kucharski 				if (ret < 0) {
85666b6aef6SWilliam Kucharski 					ret = BAM_ERROR;
85766b6aef6SWilliam Kucharski 					bam_error(KERNEL_NOT_PARSEABLE, curdef);
85866b6aef6SWilliam Kucharski 				} else
85944da779fSWilliam Kucharski 					ret = BAM_NOCHANGE;
86066b6aef6SWilliam Kucharski 
86144da779fSWilliam Kucharski 				goto abort;
86244da779fSWilliam Kucharski 			}
863eac223ccSWilliam Kucharski 		}
86444da779fSWilliam Kucharski 
86544da779fSWilliam Kucharski 		if (lp == ent->end)
86644da779fSWilliam Kucharski 			break;
86744da779fSWilliam Kucharski 	}
86844da779fSWilliam Kucharski 
86944da779fSWilliam Kucharski 	/*
870772d6a58SWilliam Kucharski 	 * If findroot, module or kern_path are NULL, the boot entry is
871772d6a58SWilliam Kucharski 	 * malformed.
87244da779fSWilliam Kucharski 	 */
87344da779fSWilliam Kucharski 	if (findroot == NULL) {
87444da779fSWilliam Kucharski 		bam_error(FINDROOT_NOT_FOUND, curdef);
87544da779fSWilliam Kucharski 		goto abort;
87644da779fSWilliam Kucharski 	}
87744da779fSWilliam Kucharski 
87844da779fSWilliam Kucharski 	if (module == NULL) {
87944da779fSWilliam Kucharski 		bam_error(MODULE_NOT_PARSEABLE, curdef);
88044da779fSWilliam Kucharski 		goto abort;
88144da779fSWilliam Kucharski 	}
88244da779fSWilliam Kucharski 
88344da779fSWilliam Kucharski 	if (kern_path == NULL) {
88444da779fSWilliam Kucharski 		bam_error(KERNEL_NOT_FOUND, curdef);
88544da779fSWilliam Kucharski 		goto abort;
88644da779fSWilliam Kucharski 	}
88744da779fSWilliam Kucharski 
88844da779fSWilliam Kucharski 	/* assemble new kernel and module arguments from parsed values */
88944da779fSWilliam Kucharski 	if (console_dev != NULL) {
89044da779fSWilliam Kucharski 		kern_bargs = s_strdup(console_dev);
89144da779fSWilliam Kucharski 
89266b6aef6SWilliam Kucharski 		if (serial_config[0] != NULL) {
89366b6aef6SWilliam Kucharski 			newstr = append_str(kern_bargs, serial_config[0], " ");
89466b6aef6SWilliam Kucharski 			free(kern_bargs);
89566b6aef6SWilliam Kucharski 			kern_bargs = newstr;
89644da779fSWilliam Kucharski 		}
89744da779fSWilliam Kucharski 
89866b6aef6SWilliam Kucharski 		if (serial_config[1] != NULL) {
89966b6aef6SWilliam Kucharski 			newstr = append_str(kern_bargs, serial_config[1], " ");
90066b6aef6SWilliam Kucharski 			free(kern_bargs);
90166b6aef6SWilliam Kucharski 			kern_bargs = newstr;
90266b6aef6SWilliam Kucharski 		}
90366b6aef6SWilliam Kucharski 	}
90466b6aef6SWilliam Kucharski 
90566b6aef6SWilliam Kucharski 	if ((extra_args != NULL) && (*extra_args != NULL)) {
90666b6aef6SWilliam Kucharski 		newstr = append_str(kern_bargs, extra_args, " ");
90766b6aef6SWilliam Kucharski 		free(kern_bargs);
90866b6aef6SWilliam Kucharski 		kern_bargs = newstr;
90966b6aef6SWilliam Kucharski 	}
91044da779fSWilliam Kucharski 
91144da779fSWilliam Kucharski 	len = strlen(osroot) + strlen(XEN_MENU) + strlen(kern_bargs) +
91244da779fSWilliam Kucharski 	    WHITESPC(1) + 1;
91344da779fSWilliam Kucharski 
91444da779fSWilliam Kucharski 	kernel = alloca(len);
91544da779fSWilliam Kucharski 
91666b6aef6SWilliam Kucharski 	if (kern_bargs != NULL) {
91766b6aef6SWilliam Kucharski 		if (*kern_bargs != NULL)
91866b6aef6SWilliam Kucharski 			(void) snprintf(kernel, len, "%s%s %s", osroot,
91966b6aef6SWilliam Kucharski 			    XEN_MENU, kern_bargs);
92066b6aef6SWilliam Kucharski 
92144da779fSWilliam Kucharski 		free(kern_bargs);
92244da779fSWilliam Kucharski 	} else {
92344da779fSWilliam Kucharski 		(void) snprintf(kernel, len, "%s%s", osroot, XEN_MENU);
92444da779fSWilliam Kucharski 	}
92544da779fSWilliam Kucharski 
92644da779fSWilliam Kucharski 	/*
92744da779fSWilliam Kucharski 	 * Change the kernel directory from the metal version to that needed for
92844da779fSWilliam Kucharski 	 * the hypervisor.  Convert either "direct boot" path to the default
92944da779fSWilliam Kucharski 	 * path.
93044da779fSWilliam Kucharski 	 */
93144da779fSWilliam Kucharski 	if ((strcmp(kern_path, DIRECT_BOOT_32) == 0) ||
93244da779fSWilliam Kucharski 	    (strcmp(kern_path, DIRECT_BOOT_64) == 0)) {
93344da779fSWilliam Kucharski 		kern_path = HYPERVISOR_KERNEL;
93444da779fSWilliam Kucharski 	} else {
93566b6aef6SWilliam Kucharski 		newstr = modify_path(kern_path, METAL_KERNEL_DIR,
93644da779fSWilliam Kucharski 		    HYPER_KERNEL_DIR);
93766b6aef6SWilliam Kucharski 		free(kern_path);
93866b6aef6SWilliam Kucharski 		kern_path = newstr;
93966b6aef6SWilliam Kucharski 		kp_allocated = 1;
94044da779fSWilliam Kucharski 	}
94144da779fSWilliam Kucharski 
94244da779fSWilliam Kucharski 	/*
94344da779fSWilliam Kucharski 	 * We need to allocate space for the kernel path (twice) plus an
94444da779fSWilliam Kucharski 	 * intervening space, possibly the ZFS boot string, and NULL,
94544da779fSWilliam Kucharski 	 * of course.
94644da779fSWilliam Kucharski 	 */
94744da779fSWilliam Kucharski 	len = (strlen(kern_path) * 2) + WHITESPC(1) + 1;
94844da779fSWilliam Kucharski 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
94944da779fSWilliam Kucharski 
95044da779fSWilliam Kucharski 	mod_kernel = alloca(len + zfslen);
95144da779fSWilliam Kucharski 	(void) snprintf(mod_kernel, len, "%s %s", kern_path, kern_path);
95244da779fSWilliam Kucharski 
95344da779fSWilliam Kucharski 	if (kp_allocated)
95444da779fSWilliam Kucharski 		free(kern_path);
95544da779fSWilliam Kucharski 
95644da779fSWilliam Kucharski 	if (zfs_boot) {
95744da779fSWilliam Kucharski 		char *zfsstr = alloca(zfslen + 1);
95844da779fSWilliam Kucharski 
95944da779fSWilliam Kucharski 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
96044da779fSWilliam Kucharski 		(void) strcat(mod_kernel, zfsstr);
96144da779fSWilliam Kucharski 	}
96244da779fSWilliam Kucharski 
96344da779fSWilliam Kucharski 	/* shut off warning messages from the entry line parser */
96444da779fSWilliam Kucharski 	if (ent->flags & BAM_ENTRY_BOOTADM)
96544da779fSWilliam Kucharski 		ent->flags &= ~BAM_ENTRY_BOOTADM;
96644da779fSWilliam Kucharski 
96744da779fSWilliam Kucharski 	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
96844da779fSWilliam Kucharski 	BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, mod_kernel));
96944da779fSWilliam Kucharski 
970772d6a58SWilliam Kucharski 	if ((newdef = add_boot_entry(mp, title, findroot, kernel, mod_kernel,
971772d6a58SWilliam Kucharski 	    module, bootfs)) == BAM_ERROR)
972772d6a58SWilliam Kucharski 		return (newdef);
973772d6a58SWilliam Kucharski 
97444da779fSWilliam Kucharski 	/*
97544da779fSWilliam Kucharski 	 * Now try to delete the current default entry from the menu and add
97644da779fSWilliam Kucharski 	 * the new hypervisor entry with the parameters we've setup.
97744da779fSWilliam Kucharski 	 */
978772d6a58SWilliam Kucharski 	if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS)
979772d6a58SWilliam Kucharski 		newdef--;
980772d6a58SWilliam Kucharski 	else
98144da779fSWilliam Kucharski 		bam_print(NEW_BOOT_ENTRY, title);
98244da779fSWilliam Kucharski 
98344da779fSWilliam Kucharski 	/*
98444da779fSWilliam Kucharski 	 * If we successfully created the new entry, set the default boot
98544da779fSWilliam Kucharski 	 * entry to that entry and let the caller know the new menu should
98644da779fSWilliam Kucharski 	 * be written out.
98744da779fSWilliam Kucharski 	 */
988772d6a58SWilliam Kucharski 	return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef));
98944da779fSWilliam Kucharski 
99044da779fSWilliam Kucharski abort:
99144da779fSWilliam Kucharski 	if (ret != BAM_NOCHANGE)
99244da779fSWilliam Kucharski 		bam_error(HYPER_ABORT, ((*osroot == NULL) ? "/" : osroot));
99344da779fSWilliam Kucharski 
99444da779fSWilliam Kucharski 	return (ret);
99544da779fSWilliam Kucharski }
99644da779fSWilliam Kucharski 
99744da779fSWilliam Kucharski /*ARGSUSED*/
99844da779fSWilliam Kucharski error_t
cvt_to_metal(menu_t * mp,char * osroot,char * menu_root)99944da779fSWilliam Kucharski cvt_to_metal(menu_t *mp, char *osroot, char *menu_root)
100044da779fSWilliam Kucharski {
100144da779fSWilliam Kucharski 	const char *fcn = "cvt_to_metal()";
100244da779fSWilliam Kucharski 
100344da779fSWilliam Kucharski 	line_t *lp;
100444da779fSWilliam Kucharski 	entry_t *ent;
100544da779fSWilliam Kucharski 	size_t len, zfslen;
100644da779fSWilliam Kucharski 
100744da779fSWilliam Kucharski 	char *delim = ",";
100866b6aef6SWilliam Kucharski 	char *newstr;
100944da779fSWilliam Kucharski 	char *osdev;
101044da779fSWilliam Kucharski 
101144da779fSWilliam Kucharski 	char *title = NULL;
101244da779fSWilliam Kucharski 	char *findroot = NULL;
101344da779fSWilliam Kucharski 	char *bootfs = NULL;
101444da779fSWilliam Kucharski 	char *kernel = NULL;
101544da779fSWilliam Kucharski 	char *module = NULL;
101644da779fSWilliam Kucharski 
101744da779fSWilliam Kucharski 	char *barchive_path = DIRECT_BOOT_ARCHIVE;
101844da779fSWilliam Kucharski 	char *kern_path = NULL;
101944da779fSWilliam Kucharski 
1020772d6a58SWilliam Kucharski 	int curdef, newdef;
102144da779fSWilliam Kucharski 	int emit_bflag = 1;
102244da779fSWilliam Kucharski 	int ret = BAM_ERROR;
102344da779fSWilliam Kucharski 
102444da779fSWilliam Kucharski 	assert(osroot);
102544da779fSWilliam Kucharski 
102644da779fSWilliam Kucharski 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, ""));
102744da779fSWilliam Kucharski 
102844da779fSWilliam Kucharski 	/*
102944da779fSWilliam Kucharski 	 * First just check to verify osroot is a sane directory.
103044da779fSWilliam Kucharski 	 */
103144da779fSWilliam Kucharski 	if ((osdev = get_special(osroot)) == NULL) {
103244da779fSWilliam Kucharski 		bam_error(CANT_FIND_SPECIAL, osroot);
103344da779fSWilliam Kucharski 		return (BAM_ERROR);
103444da779fSWilliam Kucharski 	}
103544da779fSWilliam Kucharski 
103644da779fSWilliam Kucharski 	free(osdev);
103744da779fSWilliam Kucharski 
103844da779fSWilliam Kucharski 	/*
103944da779fSWilliam Kucharski 	 * Found the GRUB signature on the target partitions, so now get the
104044da779fSWilliam Kucharski 	 * default GRUB boot entry number from the menu.lst file
104144da779fSWilliam Kucharski 	 */
104244da779fSWilliam Kucharski 	curdef = atoi(mp->curdefault->arg);
104344da779fSWilliam Kucharski 
104444da779fSWilliam Kucharski 	/* look for the first line of the matching boot entry */
104544da779fSWilliam Kucharski 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
104644da779fSWilliam Kucharski 	    ent = ent->next)
104744da779fSWilliam Kucharski 		;
104844da779fSWilliam Kucharski 
104944da779fSWilliam Kucharski 	/* couldn't find it, so error out */
105044da779fSWilliam Kucharski 	if (ent == NULL) {
105144da779fSWilliam Kucharski 		bam_error(CANT_FIND_DEFAULT, curdef);
105244da779fSWilliam Kucharski 		goto abort;
105344da779fSWilliam Kucharski 	}
105444da779fSWilliam Kucharski 
105544da779fSWilliam Kucharski 	/*
105644da779fSWilliam Kucharski 	 * Now process the entry itself.
105744da779fSWilliam Kucharski 	 */
105844da779fSWilliam Kucharski 	for (lp = ent->start; lp != NULL; lp = lp->next) {
105944da779fSWilliam Kucharski 		/*
106044da779fSWilliam Kucharski 		 * Process important lines from menu.lst boot entry.
106144da779fSWilliam Kucharski 		 */
106244da779fSWilliam Kucharski 		if (lp->flags == BAM_TITLE) {
1063*23a1cceaSRoger A. Faulkner 			title = strdupa(lp->arg);
1064eac223ccSWilliam Kucharski 		} else if (lp->cmd != NULL) {
1065eac223ccSWilliam Kucharski 			if (strcmp(lp->cmd, "findroot") == 0) {
1066*23a1cceaSRoger A. Faulkner 				findroot = strdupa(lp->arg);
106744da779fSWilliam Kucharski 			} else if (strcmp(lp->cmd, "bootfs") == 0) {
1068*23a1cceaSRoger A. Faulkner 				bootfs = strdupa(lp->arg);
1069eac223ccSWilliam Kucharski 			} else if (strcmp(lp->cmd,
1070eac223ccSWilliam Kucharski 			    menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
107144da779fSWilliam Kucharski 				if (strstr(lp->arg, "boot_archive") == NULL) {
1072*23a1cceaSRoger A. Faulkner 					module = strdupa(lp->arg);
107344da779fSWilliam Kucharski 					cvt_hyper_module(module, &kern_path);
107444da779fSWilliam Kucharski 				} else {
1075*23a1cceaSRoger A. Faulkner 					barchive_path = strdupa(lp->arg);
107644da779fSWilliam Kucharski 				}
107744da779fSWilliam Kucharski 			} else if ((strcmp(lp->cmd,
107844da779fSWilliam Kucharski 			    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
107944da779fSWilliam Kucharski 			    (cvt_hyper_kernel(lp->arg) < 0)) {
108044da779fSWilliam Kucharski 				ret = BAM_NOCHANGE;
108144da779fSWilliam Kucharski 				goto abort;
108244da779fSWilliam Kucharski 			}
1083eac223ccSWilliam Kucharski 		}
108444da779fSWilliam Kucharski 
108544da779fSWilliam Kucharski 		if (lp == ent->end)
108644da779fSWilliam Kucharski 			break;
108744da779fSWilliam Kucharski 	}
108844da779fSWilliam Kucharski 
108944da779fSWilliam Kucharski 	/*
1090772d6a58SWilliam Kucharski 	 * If findroot, module or kern_path are NULL, the boot entry is
1091772d6a58SWilliam Kucharski 	 * malformed.
109244da779fSWilliam Kucharski 	 */
109344da779fSWilliam Kucharski 	if (findroot == NULL) {
109444da779fSWilliam Kucharski 		bam_error(FINDROOT_NOT_FOUND, curdef);
109544da779fSWilliam Kucharski 		goto abort;
109644da779fSWilliam Kucharski 	}
109744da779fSWilliam Kucharski 
109844da779fSWilliam Kucharski 	if (module == NULL) {
109944da779fSWilliam Kucharski 		bam_error(MODULE_NOT_PARSEABLE, curdef);
110044da779fSWilliam Kucharski 		goto abort;
110144da779fSWilliam Kucharski 	}
110244da779fSWilliam Kucharski 
110344da779fSWilliam Kucharski 	if (kern_path == NULL) {
110444da779fSWilliam Kucharski 		bam_error(KERNEL_NOT_FOUND, curdef);
110544da779fSWilliam Kucharski 		goto abort;
110644da779fSWilliam Kucharski 	}
110744da779fSWilliam Kucharski 
110844da779fSWilliam Kucharski 	/*
110944da779fSWilliam Kucharski 	 * Assemble new kernel and module arguments from parsed values.
111044da779fSWilliam Kucharski 	 *
111144da779fSWilliam Kucharski 	 * First, change the kernel directory from the hypervisor version to
111244da779fSWilliam Kucharski 	 * that needed for a metal kernel.
111344da779fSWilliam Kucharski 	 */
111466b6aef6SWilliam Kucharski 	newstr = modify_path(kern_path, HYPER_KERNEL_DIR, METAL_KERNEL_DIR);
111566b6aef6SWilliam Kucharski 	free(kern_path);
111666b6aef6SWilliam Kucharski 	kern_path = newstr;
111744da779fSWilliam Kucharski 
111844da779fSWilliam Kucharski 	/* allocate initial space for the kernel path */
111944da779fSWilliam Kucharski 	len = strlen(kern_path) + 1;
112044da779fSWilliam Kucharski 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
112144da779fSWilliam Kucharski 
112244da779fSWilliam Kucharski 	if ((kernel = malloc(len + zfslen)) == NULL) {
112344da779fSWilliam Kucharski 		free(kern_path);
112466b6aef6SWilliam Kucharski 		bam_error(NO_MEM, len + zfslen);
112566b6aef6SWilliam Kucharski 		bam_exit(1);
112644da779fSWilliam Kucharski 	}
112744da779fSWilliam Kucharski 
112844da779fSWilliam Kucharski 	(void) snprintf(kernel, len, "%s", kern_path);
112944da779fSWilliam Kucharski 	free(kern_path);
113044da779fSWilliam Kucharski 
113144da779fSWilliam Kucharski 	if (zfs_boot) {
113244da779fSWilliam Kucharski 		char *zfsstr = alloca(zfslen + 1);
113344da779fSWilliam Kucharski 
113444da779fSWilliam Kucharski 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
113544da779fSWilliam Kucharski 		(void) strcat(kernel, zfsstr);
113644da779fSWilliam Kucharski 		emit_bflag = 0;
113744da779fSWilliam Kucharski 	}
113844da779fSWilliam Kucharski 
1139772d6a58SWilliam Kucharski 	/*
1140772d6a58SWilliam Kucharski 	 * Process the bootenv.rc file to look for boot options that would be
1141772d6a58SWilliam Kucharski 	 * the same as what the hypervisor had manually set, as we need not set
1142772d6a58SWilliam Kucharski 	 * those explicitly.
1143772d6a58SWilliam Kucharski 	 *
1144772d6a58SWilliam Kucharski 	 * If there's no bootenv.rc, it's not an issue.
1145772d6a58SWilliam Kucharski 	 */
1146772d6a58SWilliam Kucharski 	parse_bootenvrc(osroot);
1147772d6a58SWilliam Kucharski 
1148772d6a58SWilliam Kucharski 	/*
1149772d6a58SWilliam Kucharski 	 * Don't emit a console setting if it's the same as what would be
1150772d6a58SWilliam Kucharski 	 * set by bootenv.rc.
1151772d6a58SWilliam Kucharski 	 */
1152772d6a58SWilliam Kucharski 	if ((console_dev != NULL) && (bootenv_rc_console == NULL ||
1153772d6a58SWilliam Kucharski 	    (strcmp(console_dev, bootenv_rc_console) != 0))) {
115444da779fSWilliam Kucharski 		if (emit_bflag) {
115566b6aef6SWilliam Kucharski 			newstr = append_str(kernel, BFLAG, " ");
115666b6aef6SWilliam Kucharski 			free(kernel);
1157772d6a58SWilliam Kucharski 			kernel = append_str(newstr, "console=", " ");
115866b6aef6SWilliam Kucharski 			free(newstr);
1159772d6a58SWilliam Kucharski 			newstr = append_str(kernel, console_dev, "");
116066b6aef6SWilliam Kucharski 			free(kernel);
116166b6aef6SWilliam Kucharski 			kernel = newstr;
1162772d6a58SWilliam Kucharski 			emit_bflag = 0;
1163772d6a58SWilliam Kucharski 		} else {
1164772d6a58SWilliam Kucharski 			newstr = append_str(kernel, "console=", ",");
1165772d6a58SWilliam Kucharski 			free(kernel);
1166772d6a58SWilliam Kucharski 			kernel = append_str(newstr, console_dev, "");
1167772d6a58SWilliam Kucharski 			free(newstr);
116844da779fSWilliam Kucharski 		}
116944da779fSWilliam Kucharski 	}
117044da779fSWilliam Kucharski 
117144da779fSWilliam Kucharski 	/*
1172772d6a58SWilliam Kucharski 	 * We have to do some strange processing here because the hypervisor's
1173772d6a58SWilliam Kucharski 	 * serial ports default to "9600,8,n,1,-" if "comX=auto" is specified,
1174772d6a58SWilliam Kucharski 	 * or to "auto" if nothing is specified.
117544da779fSWilliam Kucharski 	 *
1176772d6a58SWilliam Kucharski 	 * This could result in a serial mode setting string being added when
1177772d6a58SWilliam Kucharski 	 * it would otherwise not be needed, but it's better to play it safe.
117844da779fSWilliam Kucharski 	 */
117944da779fSWilliam Kucharski 	if (emit_bflag) {
118066b6aef6SWilliam Kucharski 		newstr = append_str(kernel, BFLAG, " ");
118166b6aef6SWilliam Kucharski 		free(kernel);
118266b6aef6SWilliam Kucharski 		kernel = newstr;
118344da779fSWilliam Kucharski 		delim = " ";
118444da779fSWilliam Kucharski 		emit_bflag = 0;
118544da779fSWilliam Kucharski 	}
118644da779fSWilliam Kucharski 
1187772d6a58SWilliam Kucharski 	if ((serial_config[0] != NULL) && (bootenv_rc_serial[0] == NULL ||
1188772d6a58SWilliam Kucharski 	    (strcmp(serial_config[0], bootenv_rc_serial[0]) != 0))) {
1189772d6a58SWilliam Kucharski 		newstr = append_str(kernel, "ttya-mode='", delim);
119066b6aef6SWilliam Kucharski 		free(kernel);
119144da779fSWilliam Kucharski 
1192772d6a58SWilliam Kucharski 		/*
1193772d6a58SWilliam Kucharski 		 * Pass the serial configuration as the delimiter to
1194772d6a58SWilliam Kucharski 		 * append_str() as it will be inserted between the current
1195772d6a58SWilliam Kucharski 		 * string and the string we're appending, in this case the
1196772d6a58SWilliam Kucharski 		 * closing single quote.
1197772d6a58SWilliam Kucharski 		 */
1198772d6a58SWilliam Kucharski 		kernel = append_str(newstr, "'", serial_config[0]);
119966b6aef6SWilliam Kucharski 		free(newstr);
1200772d6a58SWilliam Kucharski 		delim = ",";
1201772d6a58SWilliam Kucharski 	}
1202772d6a58SWilliam Kucharski 
1203772d6a58SWilliam Kucharski 	if ((serial_config[1] != NULL) && (bootenv_rc_serial[1] == NULL ||
1204772d6a58SWilliam Kucharski 	    (strcmp(serial_config[1], bootenv_rc_serial[1]) != 0))) {
1205772d6a58SWilliam Kucharski 		newstr = append_str(kernel, "ttyb-mode='", delim);
1206772d6a58SWilliam Kucharski 		free(kernel);
1207772d6a58SWilliam Kucharski 
1208772d6a58SWilliam Kucharski 		/*
1209772d6a58SWilliam Kucharski 		 * Pass the serial configuration as the delimiter to
1210772d6a58SWilliam Kucharski 		 * append_str() as it will be inserted between the current
1211772d6a58SWilliam Kucharski 		 * string and the string we're appending, in this case the
1212772d6a58SWilliam Kucharski 		 * closing single quote.
1213772d6a58SWilliam Kucharski 		 */
1214772d6a58SWilliam Kucharski 		kernel = append_str(newstr, "'", serial_config[1]);
1215772d6a58SWilliam Kucharski 		free(newstr);
1216772d6a58SWilliam Kucharski 		delim = ",";
1217772d6a58SWilliam Kucharski 	}
121844da779fSWilliam Kucharski 
121944da779fSWilliam Kucharski 	/* shut off warning messages from the entry line parser */
122044da779fSWilliam Kucharski 	if (ent->flags & BAM_ENTRY_BOOTADM)
122144da779fSWilliam Kucharski 		ent->flags &= ~BAM_ENTRY_BOOTADM;
122244da779fSWilliam Kucharski 
122344da779fSWilliam Kucharski 	BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
122444da779fSWilliam Kucharski 	BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, module));
122544da779fSWilliam Kucharski 
1226772d6a58SWilliam Kucharski 	if ((newdef = add_boot_entry(mp, title, findroot, kernel, NULL,
1227772d6a58SWilliam Kucharski 	    barchive_path, bootfs)) == BAM_ERROR) {
1228772d6a58SWilliam Kucharski 		free(kernel);
1229772d6a58SWilliam Kucharski 		return (newdef);
1230772d6a58SWilliam Kucharski 	}
1231772d6a58SWilliam Kucharski 
123244da779fSWilliam Kucharski 	/*
123344da779fSWilliam Kucharski 	 * Now try to delete the current default entry from the menu and add
123444da779fSWilliam Kucharski 	 * the new hypervisor entry with the parameters we've setup.
123544da779fSWilliam Kucharski 	 */
1236772d6a58SWilliam Kucharski 	if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS)
1237772d6a58SWilliam Kucharski 		newdef--;
1238772d6a58SWilliam Kucharski 	else
123944da779fSWilliam Kucharski 		bam_print(NEW_BOOT_ENTRY, title);
124044da779fSWilliam Kucharski 
1241772d6a58SWilliam Kucharski 	free(kernel);
124244da779fSWilliam Kucharski 
124344da779fSWilliam Kucharski 	/*
124444da779fSWilliam Kucharski 	 * If we successfully created the new entry, set the default boot
124544da779fSWilliam Kucharski 	 * entry to that entry and let the caller know the new menu should
124644da779fSWilliam Kucharski 	 * be written out.
124744da779fSWilliam Kucharski 	 */
1248772d6a58SWilliam Kucharski 	return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef));
124944da779fSWilliam Kucharski 
125044da779fSWilliam Kucharski abort:
125144da779fSWilliam Kucharski 	if (ret != BAM_NOCHANGE)
125244da779fSWilliam Kucharski 		bam_error(METAL_ABORT, osroot);
125344da779fSWilliam Kucharski 
125444da779fSWilliam Kucharski 	return (ret);
125544da779fSWilliam Kucharski }
1256