xref: /titanic_51/usr/src/cmd/boot/bootadm/bootadm_hyper.c (revision f64ca10231919db05e806441ccd6186ea9c6e734)
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  */
2123a1cceaSRoger A. Faulkner 
2244da779fSWilliam Kucharski /*
23*f64ca102SToomas Soome  * Copyright 2016 Toomas Soome <tsoome@me.com>
2423a1cceaSRoger A. Faulkner  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2544da779fSWilliam Kucharski  */
2644da779fSWilliam Kucharski 
2744da779fSWilliam Kucharski #include <stdio.h>
2844da779fSWilliam Kucharski #include <errno.h>
2944da779fSWilliam Kucharski #include <stdlib.h>
3044da779fSWilliam Kucharski #include <string.h>
3144da779fSWilliam Kucharski #include <unistd.h>
3244da779fSWilliam Kucharski #include <alloca.h>
3344da779fSWilliam Kucharski #include <ctype.h>
3444da779fSWilliam Kucharski #include <sys/types.h>
3544da779fSWilliam Kucharski 
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 *
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) {
85*f64ca102SToomas Soome 		bam_error(_("could not allocate memory: size = %u\n"), 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 *
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) {
119*f64ca102SToomas Soome 		bam_error(_("could not allocate memory: size = %u\n"), 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 *
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) {
174*f64ca102SToomas Soome 				bam_error(_("could not allocate memory: "
175*f64ca102SToomas Soome 				    "size = %u\n"), len);
17666b6aef6SWilliam Kucharski 				bam_exit(1);
17744da779fSWilliam Kucharski 			}
17844da779fSWilliam Kucharski 
17944da779fSWilliam Kucharski 			(void) strlcpy(*token, start, len);
18044da779fSWilliam Kucharski 
18144da779fSWilliam Kucharski 			while (isspace((int)*++str))
18244da779fSWilliam Kucharski 				;
18344da779fSWilliam Kucharski 
18444da779fSWilliam Kucharski 			return (str);
18544da779fSWilliam Kucharski 		}
18644da779fSWilliam Kucharski 	} while (*str++ != NULL);
18744da779fSWilliam Kucharski 
18844da779fSWilliam Kucharski 	/* if we hit the end of the string, the token is the whole string  */
18944da779fSWilliam Kucharski 	*token = s_strdup(start);
19044da779fSWilliam Kucharski 	return (NULL);
19144da779fSWilliam Kucharski }
19244da779fSWilliam Kucharski 
19344da779fSWilliam Kucharski /*
19444da779fSWilliam Kucharski  * Convert a metal "console" device name to an equivalent one suitable for
19544da779fSWilliam Kucharski  * use with the hypervisor.
19644da779fSWilliam Kucharski  *
19744da779fSWilliam Kucharski  * Default to "vga" if we can't parse the console device.
19844da779fSWilliam Kucharski  */
19944da779fSWilliam Kucharski static void
20044da779fSWilliam Kucharski console_metal_to_hyper(char *console)
20144da779fSWilliam Kucharski {
20244da779fSWilliam Kucharski 	if ((*console == '\'') || (*console == '"'))
20344da779fSWilliam Kucharski 		console++;
20444da779fSWilliam Kucharski 
20544da779fSWilliam Kucharski 	if (strncmp(console, "ttya", 4) == 0)
20644da779fSWilliam Kucharski 		console_dev = "console=com1";
20744da779fSWilliam Kucharski 	else if (strncmp(console, "ttyb", 4) == 0)
20844da779fSWilliam Kucharski 		console_dev = "console=com2";
20944da779fSWilliam Kucharski 	else
21044da779fSWilliam Kucharski 		console_dev = "console=vga";
21144da779fSWilliam Kucharski }
21244da779fSWilliam Kucharski 
21344da779fSWilliam Kucharski static int
21444da779fSWilliam Kucharski set_serial_rate(int com, char *rate)
21544da779fSWilliam Kucharski {
21644da779fSWilliam Kucharski 	char **rp = &serial_config[com - 1];
21744da779fSWilliam Kucharski 
218772d6a58SWilliam Kucharski 	if ((com < 1) || (com > 2))
219772d6a58SWilliam Kucharski 		return (-1);
220772d6a58SWilliam Kucharski 
22144da779fSWilliam Kucharski 	/*
22244da779fSWilliam Kucharski 	 * If rate is a NULL pointer, erase any existing serial configuration
22344da779fSWilliam Kucharski 	 * for this serial port.
22444da779fSWilliam Kucharski 	 */
22544da779fSWilliam Kucharski 	if (rate == NULL) {
22644da779fSWilliam Kucharski 		if (*rp != NULL) {
22744da779fSWilliam Kucharski 			free(*rp);
22844da779fSWilliam Kucharski 			*rp = NULL;
22944da779fSWilliam Kucharski 		}
23044da779fSWilliam Kucharski 		return (0);
23144da779fSWilliam Kucharski 	}
23244da779fSWilliam Kucharski 
23366b6aef6SWilliam Kucharski 	*rp = s_realloc(*rp, strlen(rate) + 1);
23444da779fSWilliam Kucharski 	(void) strcpy(*rp, rate);
23544da779fSWilliam Kucharski 	return (0);
23644da779fSWilliam Kucharski }
23744da779fSWilliam Kucharski 
23844da779fSWilliam Kucharski /*
23944da779fSWilliam Kucharski  * Convert "metal" serial port parameters to values compatible with the
24044da779fSWilliam Kucharski  * hypervisor.
24144da779fSWilliam Kucharski  *
24244da779fSWilliam Kucharski  * Return 0 on success, otherwise -1.
24344da779fSWilliam Kucharski  */
24444da779fSWilliam Kucharski static int
24544da779fSWilliam Kucharski serial_metal_to_hyper(char *metal_port, char *metal_serial)
24644da779fSWilliam Kucharski {
24744da779fSWilliam Kucharski #define	COM_RATE_LEN	16	/* strlen("com1=115200,8n1") */
24844da779fSWilliam Kucharski 
24944da779fSWilliam Kucharski 	char com_rate[COM_RATE_LEN];
25044da779fSWilliam Kucharski 
25144da779fSWilliam Kucharski 	unsigned com, baud, bits, stop;
25244da779fSWilliam Kucharski 	char parity, handshake;
25344da779fSWilliam Kucharski 
25444da779fSWilliam Kucharski 	if ((strcmp(metal_port, "ttya-mode") == 0) ||
25544da779fSWilliam Kucharski 	    (strcmp(metal_port, "ttyb-mode") == 0))
25644da779fSWilliam Kucharski 		com = TTYXMODE_TO_COMNUM(metal_port);
25744da779fSWilliam Kucharski 	else
25844da779fSWilliam Kucharski 		return (-1);
25944da779fSWilliam Kucharski 
26044da779fSWilliam Kucharski 	if ((*metal_serial == '\'') || (*metal_serial == '"'))
26144da779fSWilliam Kucharski 		metal_serial++;
26244da779fSWilliam Kucharski 
26344da779fSWilliam Kucharski 	/*
26444da779fSWilliam Kucharski 	 * Check if it's specified as the default rate; if so it defaults to
26544da779fSWilliam Kucharski 	 * "auto" and we need not set it for they hypervisor.
26644da779fSWilliam Kucharski 	 */
26744da779fSWilliam Kucharski 	if (strncmp(metal_serial, DEFAULT_SERIAL,
26844da779fSWilliam Kucharski 	    strlen(DEFAULT_SERIAL)) == 0) {
26944da779fSWilliam Kucharski 		(void) set_serial_rate(com, NULL);
27044da779fSWilliam Kucharski 		return (0);
27144da779fSWilliam Kucharski 	}
27244da779fSWilliam Kucharski 
27344da779fSWilliam Kucharski 	/* read the serial port format as set forth in common/io/asy.c */
27444da779fSWilliam Kucharski 	if (sscanf(metal_serial, "%u,%u,%c,%u,%c", &baud, &bits, &parity, &stop,
27544da779fSWilliam Kucharski 	    &handshake) != 5)
27644da779fSWilliam Kucharski 		return (-1);
27744da779fSWilliam Kucharski 
27844da779fSWilliam Kucharski 	/* validate serial port parameters */
27944da779fSWilliam Kucharski 	if (((bits < 5) || (bits > 8)) || (stop > 1) ||
28044da779fSWilliam Kucharski 	    ((parity != 'n') && (parity != 'e') && (parity != 'o')) ||
28144da779fSWilliam Kucharski 	    ((handshake != '-') && (handshake != 'h') && (handshake != 's')))
28244da779fSWilliam Kucharski 		return (-1);
28344da779fSWilliam Kucharski 
28444da779fSWilliam Kucharski 	/* validate baud rate */
28544da779fSWilliam Kucharski 	switch (baud) {
28644da779fSWilliam Kucharski 		case 150:
28744da779fSWilliam Kucharski 		case 300:
28844da779fSWilliam Kucharski 		case 600:
28944da779fSWilliam Kucharski 		case 1200:
29044da779fSWilliam Kucharski 		case 2400:
29144da779fSWilliam Kucharski 		case 4800:
29244da779fSWilliam Kucharski 		case 9600:
29344da779fSWilliam Kucharski 		case 19200:
29444da779fSWilliam Kucharski 		case 38400:
29544da779fSWilliam Kucharski 		case 57600:
29644da779fSWilliam Kucharski 		case 115200:
29744da779fSWilliam Kucharski 			break;
29844da779fSWilliam Kucharski 
29944da779fSWilliam Kucharski 		default:
30044da779fSWilliam Kucharski 			return (-1);
30144da779fSWilliam Kucharski 	}
30244da779fSWilliam Kucharski 
30344da779fSWilliam Kucharski 	/*
30444da779fSWilliam Kucharski 	 * The hypervisor has no way to specify a handshake method, so it gets
30544da779fSWilliam Kucharski 	 * quietly dropped in the conversion.
30644da779fSWilliam Kucharski 	 */
30744da779fSWilliam Kucharski 	(void) snprintf(com_rate, COM_RATE_LEN, "com%d=%u,%u%c%u", com, baud,
30844da779fSWilliam Kucharski 	    bits, parity, stop);
30944da779fSWilliam Kucharski 	(void) set_serial_rate(com, com_rate);
31044da779fSWilliam Kucharski 	return (0);
31144da779fSWilliam Kucharski }
31244da779fSWilliam Kucharski 
31344da779fSWilliam Kucharski /*
31444da779fSWilliam Kucharski  * Convert "name=value" metal options to values suitable for use with the
31544da779fSWilliam Kucharski  * hypervisor.
31644da779fSWilliam Kucharski  *
31744da779fSWilliam Kucharski  * Our main concerns are the console device and serial port settings.
31844da779fSWilliam Kucharski  *
31944da779fSWilliam Kucharski  * Return values:
32044da779fSWilliam Kucharski  *
32144da779fSWilliam Kucharski  *    -1:	Unparseable line
32244da779fSWilliam Kucharski  *    0:	Success
32344da779fSWilliam Kucharski  *    (n > 0):	A property unimportant to us
32444da779fSWilliam Kucharski  */
32544da779fSWilliam Kucharski static int
32644da779fSWilliam Kucharski cvt_metal_option(char *optstr)
32744da779fSWilliam Kucharski {
32844da779fSWilliam Kucharski 	char *value;
32944da779fSWilliam Kucharski 	unsigned namlen;
33044da779fSWilliam Kucharski 
33144da779fSWilliam Kucharski 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
33244da779fSWilliam Kucharski 		zfs_boot = 1;
33344da779fSWilliam Kucharski 		return (0);
33444da779fSWilliam Kucharski 	}
33544da779fSWilliam Kucharski 
33644da779fSWilliam Kucharski 	if ((value = strchr(optstr, '=')) == NULL)
33744da779fSWilliam Kucharski 		return (-1);
33844da779fSWilliam Kucharski 
33944da779fSWilliam Kucharski 	namlen = value - optstr;
34044da779fSWilliam Kucharski 
34144da779fSWilliam Kucharski 	if (*++value == NULL)
34244da779fSWilliam Kucharski 		return (1);
34344da779fSWilliam Kucharski 
34444da779fSWilliam Kucharski 	if (strncmp(optstr, "console", namlen) == 0) {
34544da779fSWilliam Kucharski 		console_metal_to_hyper(value);
34644da779fSWilliam Kucharski 		return (0);
34744da779fSWilliam Kucharski 	}
34844da779fSWilliam Kucharski 
34944da779fSWilliam Kucharski 	if ((strncmp(optstr, "ttya-mode", namlen) == 0) ||
35044da779fSWilliam Kucharski 	    (strncmp(optstr, "ttyb-mode", namlen) == 0)) {
35123a1cceaSRoger A. Faulkner 		char *port = strndupa(optstr, namlen);
35244da779fSWilliam Kucharski 
35344da779fSWilliam Kucharski 		return (serial_metal_to_hyper(port, value));
35444da779fSWilliam Kucharski 	}
35544da779fSWilliam Kucharski 
35644da779fSWilliam Kucharski 	return (1);
35744da779fSWilliam Kucharski }
35844da779fSWilliam Kucharski 
35944da779fSWilliam Kucharski /*
36044da779fSWilliam Kucharski  * Convert "name=value" properties for use with a bare metal kernel
36144da779fSWilliam Kucharski  *
36244da779fSWilliam Kucharski  * Our main concerns are the console setting and serial port modes.
36344da779fSWilliam Kucharski  *
36444da779fSWilliam Kucharski  * Return values:
36544da779fSWilliam Kucharski  *
36644da779fSWilliam Kucharski  *    -1:	Unparseable line
36744da779fSWilliam Kucharski  *    0:	Success
36844da779fSWilliam Kucharski  *    (n > 0):	A property unimportant to us
36944da779fSWilliam Kucharski  */
37044da779fSWilliam Kucharski static int
37144da779fSWilliam Kucharski cvt_hyper_option(char *optstr)
37244da779fSWilliam Kucharski {
373772d6a58SWilliam Kucharski #define	SER_LEN		15	/* strlen("115200,8,n,1,-") + 1 */
37444da779fSWilliam Kucharski 
37544da779fSWilliam Kucharski 	char ser[SER_LEN];
37644da779fSWilliam Kucharski 	char *value;
37744da779fSWilliam Kucharski 
37844da779fSWilliam Kucharski 	unsigned namlen;
37944da779fSWilliam Kucharski 
38044da779fSWilliam Kucharski 	unsigned baud;
38144da779fSWilliam Kucharski 	char bits, parity, stop;
38244da779fSWilliam Kucharski 
38344da779fSWilliam Kucharski 	if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
38444da779fSWilliam Kucharski 		zfs_boot = 1;
38544da779fSWilliam Kucharski 		return (0);
38644da779fSWilliam Kucharski 	}
38744da779fSWilliam Kucharski 
38844da779fSWilliam Kucharski 	/*
38944da779fSWilliam Kucharski 	 * If there's no "=" in the token, it's likely a standalone
39044da779fSWilliam Kucharski 	 * hypervisor token we don't care about (e.g. "noreboot" or
39144da779fSWilliam Kucharski 	 * "nosmp") so we ignore it.
39244da779fSWilliam Kucharski 	 */
39344da779fSWilliam Kucharski 	if ((value = strchr(optstr, '=')) == NULL)
39444da779fSWilliam Kucharski 		return (1);
39544da779fSWilliam Kucharski 
39644da779fSWilliam Kucharski 	namlen = value - optstr;
39744da779fSWilliam Kucharski 
39844da779fSWilliam Kucharski 	if (*++value == NULL)
39944da779fSWilliam Kucharski 		return (1);
40044da779fSWilliam Kucharski 
40144da779fSWilliam Kucharski 	/*
40244da779fSWilliam Kucharski 	 * Note that we use strncmp against the values because the
40344da779fSWilliam Kucharski 	 * hypervisor allows setting console parameters for both the
40444da779fSWilliam Kucharski 	 * console and debugger via the format:
40544da779fSWilliam Kucharski 	 *
40644da779fSWilliam Kucharski 	 *   console=cons_dev,debug_dev
40744da779fSWilliam Kucharski 	 *
40844da779fSWilliam Kucharski 	 * and we only care about "cons_dev."
40944da779fSWilliam Kucharski 	 *
41044da779fSWilliam Kucharski 	 * This also allows us to extract "comN" from hypervisor constructs
41144da779fSWilliam Kucharski 	 * like "com1H" or "com2L," concepts unsupported on bare metal kernels.
41244da779fSWilliam Kucharski 	 *
41344da779fSWilliam Kucharski 	 * Default the console device to "text" if it was "vga" or was
41444da779fSWilliam Kucharski 	 * unparseable.
41544da779fSWilliam Kucharski 	 */
41644da779fSWilliam Kucharski 	if (strncmp(optstr, "console", namlen) == 0) {
41744da779fSWilliam Kucharski 		/* ignore the "console=hypervisor" option */
41844da779fSWilliam Kucharski 		if (strcmp(value, "hypervisor") == 0)
41944da779fSWilliam Kucharski 			return (0);
42044da779fSWilliam Kucharski 
42144da779fSWilliam Kucharski 		if (strncmp(value, "com1", 4) == 0)
422772d6a58SWilliam Kucharski 			console_dev = "ttya";
42344da779fSWilliam Kucharski 		else if (strncmp(value, "com2", 4) == 0)
424772d6a58SWilliam Kucharski 			console_dev = "ttyb";
42544da779fSWilliam Kucharski 		else
426772d6a58SWilliam Kucharski 			console_dev = "text";
42744da779fSWilliam Kucharski 	}
42844da779fSWilliam Kucharski 
42944da779fSWilliam Kucharski 	/* serial port parameter conversion */
43044da779fSWilliam Kucharski 
43144da779fSWilliam Kucharski 	if ((strncmp(optstr, "com1", namlen) == 0) ||
43244da779fSWilliam Kucharski 	    (strncmp(optstr, "com2", namlen) == 0)) {
43344da779fSWilliam Kucharski 		unsigned com = COMNAME_TO_COMNUM(optstr);
43444da779fSWilliam Kucharski 
43544da779fSWilliam Kucharski 		/*
43644da779fSWilliam Kucharski 		 * Check if it's "auto" - if so, use the default setting
43744da779fSWilliam Kucharski 		 * of "9600,8,n,1,-".
43844da779fSWilliam Kucharski 		 *
43944da779fSWilliam Kucharski 		 * We can't just assume the serial port will default to
44044da779fSWilliam Kucharski 		 * "9600,8,n,1" as there could be a directive in bootenv.rc
44144da779fSWilliam Kucharski 		 * that would set it to some other value and we want the serial
44244da779fSWilliam Kucharski 		 * parameters to be the same as that used by the hypervisor.
44344da779fSWilliam Kucharski 		 */
44444da779fSWilliam Kucharski 		if (strcmp(value, "auto") == 0) {
445772d6a58SWilliam Kucharski 			(void) snprintf(ser, SER_LEN, "9600,8,n,1,-");
446772d6a58SWilliam Kucharski 		} else {
44744da779fSWilliam Kucharski 			/*
448772d6a58SWilliam Kucharski 			 * Extract the "B,PS" setting from the com line; ignore
449772d6a58SWilliam Kucharski 			 * other settings like io_base or IRQ.
45044da779fSWilliam Kucharski 			 */
45144da779fSWilliam Kucharski 			if (sscanf(value, "%u,%c%c%c", &baud, &bits, &parity,
45244da779fSWilliam Kucharski 			    &stop) != 4)
45344da779fSWilliam Kucharski 				return (-1);
45444da779fSWilliam Kucharski 
45544da779fSWilliam Kucharski 			/* validate serial port parameters */
45644da779fSWilliam Kucharski 			if (((stop != '0') && (stop != '1')) ||
45744da779fSWilliam Kucharski 			    ((bits < '5') && (bits > '8')) ||
458772d6a58SWilliam Kucharski 			    ((parity != 'n') && (parity != 'e') &&
459772d6a58SWilliam Kucharski 			    (parity != 'o')))
46044da779fSWilliam Kucharski 				return (-1);
46144da779fSWilliam Kucharski 
46244da779fSWilliam Kucharski 			/* validate baud rate */
46344da779fSWilliam Kucharski 			switch (baud) {
46444da779fSWilliam Kucharski 				case 150:
46544da779fSWilliam Kucharski 				case 300:
46644da779fSWilliam Kucharski 				case 600:
46744da779fSWilliam Kucharski 				case 1200:
46844da779fSWilliam Kucharski 				case 2400:
46944da779fSWilliam Kucharski 				case 4800:
47044da779fSWilliam Kucharski 				case 19200:
47144da779fSWilliam Kucharski 				case 38400:
47244da779fSWilliam Kucharski 				case 57600:
47344da779fSWilliam Kucharski 				case 115200:
47444da779fSWilliam Kucharski 					break;
47544da779fSWilliam Kucharski 
47644da779fSWilliam Kucharski 				default:
47744da779fSWilliam Kucharski 					return (-1);
47844da779fSWilliam Kucharski 			}
47944da779fSWilliam Kucharski 
48044da779fSWilliam Kucharski 			/*
481772d6a58SWilliam Kucharski 			 * As the hypervisor has no way to denote handshaking
482772d6a58SWilliam Kucharski 			 * in its serial port settings, emit a metal serial
483772d6a58SWilliam Kucharski 			 * port configuration with none as well.
48444da779fSWilliam Kucharski 			 */
485772d6a58SWilliam Kucharski 			(void) snprintf(ser, SER_LEN, "%u,%c,%c,%c,-", baud,
486772d6a58SWilliam Kucharski 			    bits, parity, stop);
487772d6a58SWilliam Kucharski 		}
48844da779fSWilliam Kucharski 
48944da779fSWilliam Kucharski 		if (set_serial_rate(com, ser) != 0)
49044da779fSWilliam Kucharski 			return (-1);
49144da779fSWilliam Kucharski 
49244da779fSWilliam Kucharski 		return (0);
49344da779fSWilliam Kucharski 	}
49444da779fSWilliam Kucharski 
49544da779fSWilliam Kucharski 	return (1);
49644da779fSWilliam Kucharski }
49744da779fSWilliam Kucharski 
49844da779fSWilliam Kucharski /*
49944da779fSWilliam Kucharski  * Parse a hardware kernel's "kernel$" specifier into parameters we can then
50044da779fSWilliam Kucharski  * use to construct an appropriate "module$" line that can be used to specify
50144da779fSWilliam Kucharski  * how to boot the hypervisor's dom0.
50244da779fSWilliam Kucharski  *
50366b6aef6SWilliam Kucharski  * Return values:
50466b6aef6SWilliam Kucharski  *
50566b6aef6SWilliam Kucharski  *	-1: error parsing kernel path
50666b6aef6SWilliam Kucharski  *	 0: success
50766b6aef6SWilliam Kucharski  *	 1: kernel already a hypervisor kernel
50844da779fSWilliam Kucharski  */
50944da779fSWilliam Kucharski static int
51044da779fSWilliam Kucharski cvt_metal_kernel(char *kernstr, char **path)
51144da779fSWilliam Kucharski {
51244da779fSWilliam Kucharski 	char *token, *parsestr;
51344da779fSWilliam Kucharski 
51466b6aef6SWilliam Kucharski 	parsestr = get_token(path, kernstr, " \t,");
51566b6aef6SWilliam Kucharski 	if (*path == NULL)
51666b6aef6SWilliam Kucharski 		return (-1);
51744da779fSWilliam Kucharski 
51844da779fSWilliam Kucharski 	/*
51944da779fSWilliam Kucharski 	 * If the metal kernel specified contains the name of the hypervisor,
52044da779fSWilliam Kucharski 	 * we're probably trying to convert an entry already setup to run the
52144da779fSWilliam Kucharski 	 * hypervisor, so error out now.
52244da779fSWilliam Kucharski 	 */
52344da779fSWilliam Kucharski 	if (strstr(*path, XEN_MENU) != NULL) {
524*f64ca102SToomas Soome 		bam_error(_("default entry already setup for use with the "
525*f64ca102SToomas Soome 		    "hypervisor!\n"));
52666b6aef6SWilliam Kucharski 		free(*path);
52766b6aef6SWilliam Kucharski 		*path = NULL;
52866b6aef6SWilliam Kucharski 		return (1);
52944da779fSWilliam Kucharski 	}
53044da779fSWilliam Kucharski 
53144da779fSWilliam Kucharski 	/* if the path was the last item on the line, that's OK. */
53244da779fSWilliam Kucharski 	if ((parsestr = get_token(&token, parsestr, " \t,")) == NULL) {
53344da779fSWilliam Kucharski 		if (token != NULL)
53444da779fSWilliam Kucharski 			free(token);
53544da779fSWilliam Kucharski 		return (0);
53644da779fSWilliam Kucharski 	}
53744da779fSWilliam Kucharski 
53844da779fSWilliam Kucharski 	/* if the next token is "-B" process boot options */
53944da779fSWilliam Kucharski 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
54044da779fSWilliam Kucharski 		free(token);
54144da779fSWilliam Kucharski 		return (0);
54244da779fSWilliam Kucharski 	}
54344da779fSWilliam Kucharski 
54466b6aef6SWilliam Kucharski 	free(token);
54566b6aef6SWilliam Kucharski 
54644da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
54744da779fSWilliam Kucharski 		(void) cvt_metal_option(token);
54844da779fSWilliam Kucharski 		free(token);
54944da779fSWilliam Kucharski 	}
55044da779fSWilliam Kucharski 
55144da779fSWilliam Kucharski 	if (token != NULL) {
55244da779fSWilliam Kucharski 		(void) cvt_metal_option(token);
55344da779fSWilliam Kucharski 		free(token);
55444da779fSWilliam Kucharski 	}
55544da779fSWilliam Kucharski 
55644da779fSWilliam Kucharski 	return (0);
55744da779fSWilliam Kucharski }
55844da779fSWilliam Kucharski 
55944da779fSWilliam Kucharski /*
56044da779fSWilliam Kucharski  * Parse a hypervisor's "kernel$" line into parameters that can be used to
56144da779fSWilliam Kucharski  * help build an appropriate "kernel$" line for booting a bare metal kernel.
56244da779fSWilliam Kucharski  *
56344da779fSWilliam Kucharski  * Return 0 on success, non-zero on failure.
56444da779fSWilliam Kucharski  */
56544da779fSWilliam Kucharski static int
56644da779fSWilliam Kucharski cvt_hyper_kernel(char *kernel)
56744da779fSWilliam Kucharski {
56844da779fSWilliam Kucharski 	char *token, *parsestr;
56944da779fSWilliam Kucharski 
57066b6aef6SWilliam Kucharski 	parsestr = get_token(&token, kernel, " \t,");
57166b6aef6SWilliam Kucharski 
57266b6aef6SWilliam Kucharski 	if (token == NULL)
57366b6aef6SWilliam Kucharski 		return (-1);
57444da779fSWilliam Kucharski 
57544da779fSWilliam Kucharski 	/*
57644da779fSWilliam Kucharski 	 * If the hypervisor kernel specified lives in the metal kernel
57744da779fSWilliam Kucharski 	 * directory, we're probably trying to convert an entry already setup
57844da779fSWilliam Kucharski 	 * to run on bare metal, so error out now.
57944da779fSWilliam Kucharski 	 */
58044da779fSWilliam Kucharski 	if (strncmp(token, METAL_KERNEL_DIR, strlen(METAL_KERNEL_DIR)) == 0) {
581*f64ca102SToomas Soome 		bam_error(_("default entry already setup for use with a metal "
582*f64ca102SToomas Soome 		    "kernel!\n"));
58366b6aef6SWilliam Kucharski 		free(token);
58444da779fSWilliam Kucharski 		return (-1);
58544da779fSWilliam Kucharski 	}
58644da779fSWilliam Kucharski 
58744da779fSWilliam Kucharski 	free(token);
58844da779fSWilliam Kucharski 
58944da779fSWilliam Kucharski 	/* check for kernel options */
59044da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, " ")) != NULL) {
59144da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
59244da779fSWilliam Kucharski 		free(token);
59344da779fSWilliam Kucharski 	}
59444da779fSWilliam Kucharski 
59544da779fSWilliam Kucharski 	if (token != NULL) {
59644da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
59744da779fSWilliam Kucharski 		free(token);
59844da779fSWilliam Kucharski 	}
59944da779fSWilliam Kucharski 
60044da779fSWilliam Kucharski 	return (0);
60144da779fSWilliam Kucharski }
60244da779fSWilliam Kucharski 
60344da779fSWilliam Kucharski /*
60444da779fSWilliam Kucharski  * Parse a hypervisor's "module$" line into parameters that can be used to
60544da779fSWilliam Kucharski  * help build an appropriate "kernel$" line for booting a bare metal kernel.
60644da779fSWilliam Kucharski  */
60744da779fSWilliam Kucharski static void
60844da779fSWilliam Kucharski cvt_hyper_module(char *modstr, char **path)
60944da779fSWilliam Kucharski {
61066b6aef6SWilliam Kucharski 	char *token = NULL;
61144da779fSWilliam Kucharski 	char *parsestr = modstr;
61244da779fSWilliam Kucharski 
61344da779fSWilliam Kucharski 	/*
61444da779fSWilliam Kucharski 	 * If multiple pathnames exist on the module$ line, we just want
61544da779fSWilliam Kucharski 	 * the last one.
61644da779fSWilliam Kucharski 	 */
61744da779fSWilliam Kucharski 	while ((parsestr = get_token(path, parsestr, " \t,")) != NULL) {
61844da779fSWilliam Kucharski 		if (*parsestr != '/')
61944da779fSWilliam Kucharski 			break;
62066b6aef6SWilliam Kucharski 		else
62144da779fSWilliam Kucharski 			free(*path);
62244da779fSWilliam Kucharski 	}
62344da779fSWilliam Kucharski 
62444da779fSWilliam Kucharski 	/* if the path was the last item on the line, that's OK. */
62544da779fSWilliam Kucharski 	if ((parsestr == NULL) ||
62644da779fSWilliam Kucharski 	    ((parsestr = get_token(&token, parsestr, " \t,")) == NULL)) {
62744da779fSWilliam Kucharski 		if (token != NULL)
62844da779fSWilliam Kucharski 			free(token);
62944da779fSWilliam Kucharski 		return;
63044da779fSWilliam Kucharski 	}
63144da779fSWilliam Kucharski 
63266b6aef6SWilliam Kucharski 	if (token == NULL)
63366b6aef6SWilliam Kucharski 		return;
63466b6aef6SWilliam Kucharski 
63544da779fSWilliam Kucharski 	/* check for "-B" option */
63644da779fSWilliam Kucharski 	if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
63744da779fSWilliam Kucharski 		free(token);
63844da779fSWilliam Kucharski 		return;
63944da779fSWilliam Kucharski 	}
64044da779fSWilliam Kucharski 
64166b6aef6SWilliam Kucharski 	free(token);
64266b6aef6SWilliam Kucharski 
64344da779fSWilliam Kucharski 	/* check for kernel options */
64444da779fSWilliam Kucharski 	while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
64544da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
64644da779fSWilliam Kucharski 		free(token);
64744da779fSWilliam Kucharski 	}
64844da779fSWilliam Kucharski 
64944da779fSWilliam Kucharski 	if (token != NULL) {
65044da779fSWilliam Kucharski 		(void) cvt_hyper_option(token);
65144da779fSWilliam Kucharski 		free(token);
65244da779fSWilliam Kucharski 	}
65344da779fSWilliam Kucharski }
65444da779fSWilliam Kucharski 
65544da779fSWilliam Kucharski static void
65644da779fSWilliam Kucharski parse_bootenvrc(char *osroot)
65744da779fSWilliam Kucharski {
65844da779fSWilliam Kucharski #define	LINEBUF_SZ	1024
65944da779fSWilliam Kucharski 
66044da779fSWilliam Kucharski 	FILE *fp;
66144da779fSWilliam Kucharski 	char *rcpath;
66244da779fSWilliam Kucharski 	char line[LINEBUF_SZ];	/* make line buffer large but not ridiculous */
66344da779fSWilliam Kucharski 	int len;
66444da779fSWilliam Kucharski 
66544da779fSWilliam Kucharski 	assert(osroot);
66644da779fSWilliam Kucharski 
66744da779fSWilliam Kucharski 	len = strlen(osroot) + strlen(BOOTRC_FILE) + 1;
66844da779fSWilliam Kucharski 	rcpath = alloca(len);
66966b6aef6SWilliam Kucharski 
67044da779fSWilliam Kucharski 	(void) snprintf(rcpath, len, "%s%s", osroot, BOOTRC_FILE);
67144da779fSWilliam Kucharski 
67244da779fSWilliam Kucharski 	/* if we couldn't open the bootenv.rc file, ignore the issue. */
67344da779fSWilliam Kucharski 	if ((fp = fopen(rcpath, "r")) == NULL) {
674*f64ca102SToomas Soome 		BAM_DPRINTF(("could not open %s: %s\n", rcpath,
675*f64ca102SToomas Soome 		    strerror(errno)));
67644da779fSWilliam Kucharski 		return;
67744da779fSWilliam Kucharski 	}
67844da779fSWilliam Kucharski 
67944da779fSWilliam Kucharski 	while (s_fgets(line, LINEBUF_SZ, fp) != NULL) {
680772d6a58SWilliam Kucharski 		char *parsestr, *token;
681772d6a58SWilliam Kucharski 		int port = 0;
682772d6a58SWilliam Kucharski 
68344da779fSWilliam Kucharski 		/* we're only interested in parsing "setprop" directives. */
68444da779fSWilliam Kucharski 		if (strncmp(line, "setprop", 7) != NULL)
68544da779fSWilliam Kucharski 			continue;
68644da779fSWilliam Kucharski 
687772d6a58SWilliam Kucharski 		/* eat initial "setprop" */
688772d6a58SWilliam Kucharski 		if ((parsestr = get_token(&token, line, " \t")) == NULL) {
689772d6a58SWilliam Kucharski 			if (token != NULL)
690772d6a58SWilliam Kucharski 				free(token);
691772d6a58SWilliam Kucharski 
692772d6a58SWilliam Kucharski 			continue;
693772d6a58SWilliam Kucharski 		}
694772d6a58SWilliam Kucharski 
695772d6a58SWilliam Kucharski 		if (strcmp(token, "setprop") != 0) {
696772d6a58SWilliam Kucharski 			free(token);
697772d6a58SWilliam Kucharski 			continue;
698772d6a58SWilliam Kucharski 		}
699772d6a58SWilliam Kucharski 
700772d6a58SWilliam Kucharski 		free(token);
701772d6a58SWilliam Kucharski 
702772d6a58SWilliam Kucharski 		/* get property name */
703772d6a58SWilliam Kucharski 		if ((parsestr = get_token(&token, parsestr, " \t")) == NULL) {
704772d6a58SWilliam Kucharski 			if (token != NULL)
705772d6a58SWilliam Kucharski 				free(token);
706772d6a58SWilliam Kucharski 
707772d6a58SWilliam Kucharski 			continue;
708772d6a58SWilliam Kucharski 		}
709772d6a58SWilliam Kucharski 
710772d6a58SWilliam Kucharski 		if (strcmp(token, "console") == 0) {
711772d6a58SWilliam Kucharski 			free(token);
712772d6a58SWilliam Kucharski 
713772d6a58SWilliam Kucharski 			/* get console property value */
714772d6a58SWilliam Kucharski 			parsestr = get_token(&token, parsestr, " \t");
715772d6a58SWilliam Kucharski 			if (token == NULL)
716772d6a58SWilliam Kucharski 				continue;
717772d6a58SWilliam Kucharski 
718772d6a58SWilliam Kucharski 			if (bootenv_rc_console != NULL)
719772d6a58SWilliam Kucharski 				free(bootenv_rc_console);
720772d6a58SWilliam Kucharski 
721772d6a58SWilliam Kucharski 			bootenv_rc_console = s_strdup(token);
722772d6a58SWilliam Kucharski 			continue;
723772d6a58SWilliam Kucharski 		}
724772d6a58SWilliam Kucharski 
725772d6a58SWilliam Kucharski 		/* check if it's a serial port setting */
726772d6a58SWilliam Kucharski 		if (strcmp(token, "ttya-mode") == 0) {
727772d6a58SWilliam Kucharski 			free(token);
728772d6a58SWilliam Kucharski 			port = 0;
729772d6a58SWilliam Kucharski 		} else if (strcmp(token, "ttyb-mode") == 0) {
730772d6a58SWilliam Kucharski 			free(token);
731772d6a58SWilliam Kucharski 			port = 1;
732772d6a58SWilliam Kucharski 		} else {
733772d6a58SWilliam Kucharski 			/* nope, so check the next line */
734772d6a58SWilliam Kucharski 			free(token);
735772d6a58SWilliam Kucharski 			continue;
736772d6a58SWilliam Kucharski 		}
737772d6a58SWilliam Kucharski 
738772d6a58SWilliam Kucharski 		/* get serial port setting */
739772d6a58SWilliam Kucharski 		parsestr = get_token(&token, parsestr, " \t");
740772d6a58SWilliam Kucharski 
741772d6a58SWilliam Kucharski 		if (token == NULL)
742772d6a58SWilliam Kucharski 			continue;
743772d6a58SWilliam Kucharski 
744772d6a58SWilliam Kucharski 		if (bootenv_rc_serial[port] != NULL)
745772d6a58SWilliam Kucharski 			free(bootenv_rc_serial[port]);
746772d6a58SWilliam Kucharski 
747772d6a58SWilliam Kucharski 		bootenv_rc_serial[port] = s_strdup(token);
748772d6a58SWilliam Kucharski 		free(token);
74944da779fSWilliam Kucharski 	}
75044da779fSWilliam Kucharski 
75144da779fSWilliam Kucharski 	(void) fclose(fp);
75244da779fSWilliam Kucharski }
75344da779fSWilliam Kucharski 
75444da779fSWilliam Kucharski error_t
75544da779fSWilliam Kucharski cvt_to_hyper(menu_t *mp, char *osroot, char *extra_args)
75644da779fSWilliam Kucharski {
75744da779fSWilliam Kucharski 	const char *fcn = "cvt_to_hyper()";
75844da779fSWilliam Kucharski 
75944da779fSWilliam Kucharski 	line_t *lp;
76044da779fSWilliam Kucharski 	entry_t *ent;
76144da779fSWilliam Kucharski 	size_t len, zfslen;
76244da779fSWilliam Kucharski 
76366b6aef6SWilliam Kucharski 	char *newstr;
76444da779fSWilliam Kucharski 	char *osdev;
76544da779fSWilliam Kucharski 
76644da779fSWilliam Kucharski 	char *title = NULL;
76744da779fSWilliam Kucharski 	char *findroot = NULL;
76844da779fSWilliam Kucharski 	char *bootfs = NULL;
76944da779fSWilliam Kucharski 	char *kernel = NULL;
77044da779fSWilliam Kucharski 	char *mod_kernel = NULL;
77144da779fSWilliam Kucharski 	char *module = NULL;
77244da779fSWilliam Kucharski 
77344da779fSWilliam Kucharski 	char *kern_path = NULL;
77444da779fSWilliam Kucharski 	char *kern_bargs = NULL;
77544da779fSWilliam Kucharski 
776772d6a58SWilliam Kucharski 	int curdef, newdef;
77766b6aef6SWilliam Kucharski 	int kp_allocated = 0;
77844da779fSWilliam Kucharski 	int ret = BAM_ERROR;
77944da779fSWilliam Kucharski 
78044da779fSWilliam Kucharski 	assert(osroot);
78144da779fSWilliam Kucharski 
782*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, extra_args));
78344da779fSWilliam Kucharski 
78444da779fSWilliam Kucharski 	/*
78544da779fSWilliam Kucharski 	 * First just check to verify osroot is a sane directory.
78644da779fSWilliam Kucharski 	 */
78744da779fSWilliam Kucharski 	if ((osdev = get_special(osroot)) == NULL) {
788*f64ca102SToomas Soome 		bam_error(_("cant find special file for mount-point %s\n"),
789*f64ca102SToomas Soome 		    osroot);
79044da779fSWilliam Kucharski 		return (BAM_ERROR);
79144da779fSWilliam Kucharski 	}
79244da779fSWilliam Kucharski 
79344da779fSWilliam Kucharski 	free(osdev);
79444da779fSWilliam Kucharski 
79544da779fSWilliam Kucharski 	/*
79644da779fSWilliam Kucharski 	 * While the effect is purely cosmetic, if osroot is "/" don't
79744da779fSWilliam Kucharski 	 * bother prepending it to any paths as they are constructed to
79844da779fSWilliam Kucharski 	 * begin with "/" anyway.
79944da779fSWilliam Kucharski 	 */
80044da779fSWilliam Kucharski 	if (strcmp(osroot, "/") == 0)
80144da779fSWilliam Kucharski 		osroot = "";
80244da779fSWilliam Kucharski 
80344da779fSWilliam Kucharski 	/*
80444da779fSWilliam Kucharski 	 * Found the GRUB signature on the target partitions, so now get the
80544da779fSWilliam Kucharski 	 * default GRUB boot entry number from the menu.lst file
80644da779fSWilliam Kucharski 	 */
80744da779fSWilliam Kucharski 	curdef = atoi(mp->curdefault->arg);
80844da779fSWilliam Kucharski 
80944da779fSWilliam Kucharski 	/* look for the first line of the matching boot entry */
81044da779fSWilliam Kucharski 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
81144da779fSWilliam Kucharski 	    ent = ent->next)
81244da779fSWilliam Kucharski 		;
81344da779fSWilliam Kucharski 
81444da779fSWilliam Kucharski 	/* couldn't find it, so error out */
81544da779fSWilliam Kucharski 	if (ent == NULL) {
816*f64ca102SToomas Soome 		bam_error(_("unable to find default boot entry (%d) in "
817*f64ca102SToomas Soome 		    "menu.lst file.\n"), curdef);
81844da779fSWilliam Kucharski 		goto abort;
81944da779fSWilliam Kucharski 	}
82044da779fSWilliam Kucharski 
82144da779fSWilliam Kucharski 	/*
82244da779fSWilliam Kucharski 	 * We found the proper menu entry, so first we need to process the
82344da779fSWilliam Kucharski 	 * bootenv.rc file to look for boot options the hypervisor might need
82444da779fSWilliam Kucharski 	 * passed as kernel start options such as the console device and serial
82544da779fSWilliam Kucharski 	 * port parameters.
82644da779fSWilliam Kucharski 	 *
82744da779fSWilliam Kucharski 	 * If there's no bootenv.rc, it's not an issue.
82844da779fSWilliam Kucharski 	 */
82944da779fSWilliam Kucharski 	parse_bootenvrc(osroot);
83044da779fSWilliam Kucharski 
831772d6a58SWilliam Kucharski 	if (bootenv_rc_console != NULL)
832772d6a58SWilliam Kucharski 		console_metal_to_hyper(bootenv_rc_console);
833772d6a58SWilliam Kucharski 
834772d6a58SWilliam Kucharski 	if (bootenv_rc_serial[0] != NULL)
835772d6a58SWilliam Kucharski 		(void) serial_metal_to_hyper("ttya-mode", bootenv_rc_serial[0]);
836772d6a58SWilliam Kucharski 
837772d6a58SWilliam Kucharski 	if (bootenv_rc_serial[1] != NULL)
838772d6a58SWilliam Kucharski 		(void) serial_metal_to_hyper("ttyb-mode", bootenv_rc_serial[1]);
839772d6a58SWilliam Kucharski 
84044da779fSWilliam Kucharski 	/*
84144da779fSWilliam Kucharski 	 * Now process the entry itself.
84244da779fSWilliam Kucharski 	 */
84344da779fSWilliam Kucharski 	for (lp = ent->start; lp != NULL; lp = lp->next) {
84444da779fSWilliam Kucharski 		/*
84544da779fSWilliam Kucharski 		 * Process important lines from menu.lst boot entry.
84644da779fSWilliam Kucharski 		 */
84744da779fSWilliam Kucharski 		if (lp->flags == BAM_TITLE) {
84823a1cceaSRoger A. Faulkner 			title = strdupa(lp->arg);
849eac223ccSWilliam Kucharski 		} else if (lp->cmd != NULL) {
850eac223ccSWilliam Kucharski 			if (strcmp(lp->cmd, "findroot") == 0) {
85123a1cceaSRoger A. Faulkner 				findroot = strdupa(lp->arg);
85244da779fSWilliam Kucharski 			} else if (strcmp(lp->cmd, "bootfs") == 0) {
85323a1cceaSRoger A. Faulkner 				bootfs = strdupa(lp->arg);
854eac223ccSWilliam Kucharski 			} else if (strcmp(lp->cmd,
855eac223ccSWilliam Kucharski 			    menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
85623a1cceaSRoger A. Faulkner 				module = strdupa(lp->arg);
85744da779fSWilliam Kucharski 			} else if ((strcmp(lp->cmd,
85844da779fSWilliam Kucharski 			    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
859eac223ccSWilliam Kucharski 			    (ret = cvt_metal_kernel(lp->arg,
860eac223ccSWilliam Kucharski 			    &kern_path)) != 0) {
86166b6aef6SWilliam Kucharski 				if (ret < 0) {
86266b6aef6SWilliam Kucharski 					ret = BAM_ERROR;
863*f64ca102SToomas Soome 					bam_error(_("kernel$ in default boot "
864*f64ca102SToomas Soome 					    "entry (%d) missing or not "
865*f64ca102SToomas Soome 					    "parseable.\n"), curdef);
86666b6aef6SWilliam Kucharski 				} else
86744da779fSWilliam Kucharski 					ret = BAM_NOCHANGE;
86866b6aef6SWilliam Kucharski 
86944da779fSWilliam Kucharski 				goto abort;
87044da779fSWilliam Kucharski 			}
871eac223ccSWilliam Kucharski 		}
87244da779fSWilliam Kucharski 
87344da779fSWilliam Kucharski 		if (lp == ent->end)
87444da779fSWilliam Kucharski 			break;
87544da779fSWilliam Kucharski 	}
87644da779fSWilliam Kucharski 
87744da779fSWilliam Kucharski 	/*
878772d6a58SWilliam Kucharski 	 * If findroot, module or kern_path are NULL, the boot entry is
879772d6a58SWilliam Kucharski 	 * malformed.
88044da779fSWilliam Kucharski 	 */
88144da779fSWilliam Kucharski 	if (findroot == NULL) {
882*f64ca102SToomas Soome 		bam_error(_("findroot in default boot entry (%d) missing.\n"),
883*f64ca102SToomas Soome 		    curdef);
88444da779fSWilliam Kucharski 		goto abort;
88544da779fSWilliam Kucharski 	}
88644da779fSWilliam Kucharski 
88744da779fSWilliam Kucharski 	if (module == NULL) {
888*f64ca102SToomas Soome 		bam_error(_("module$ in default boot entry (%d) missing or "
889*f64ca102SToomas Soome 		    "not parseable.\n"), curdef);
89044da779fSWilliam Kucharski 		goto abort;
89144da779fSWilliam Kucharski 	}
89244da779fSWilliam Kucharski 
89344da779fSWilliam Kucharski 	if (kern_path == NULL) {
894*f64ca102SToomas Soome 		bam_error(_("kernel$ in default boot entry (%d) missing.\n"),
895*f64ca102SToomas Soome 		    curdef);
89644da779fSWilliam Kucharski 		goto abort;
89744da779fSWilliam Kucharski 	}
89844da779fSWilliam Kucharski 
89944da779fSWilliam Kucharski 	/* assemble new kernel and module arguments from parsed values */
90044da779fSWilliam Kucharski 	if (console_dev != NULL) {
90144da779fSWilliam Kucharski 		kern_bargs = s_strdup(console_dev);
90244da779fSWilliam Kucharski 
90366b6aef6SWilliam Kucharski 		if (serial_config[0] != NULL) {
90466b6aef6SWilliam Kucharski 			newstr = append_str(kern_bargs, serial_config[0], " ");
90566b6aef6SWilliam Kucharski 			free(kern_bargs);
90666b6aef6SWilliam Kucharski 			kern_bargs = newstr;
90744da779fSWilliam Kucharski 		}
90844da779fSWilliam Kucharski 
90966b6aef6SWilliam Kucharski 		if (serial_config[1] != NULL) {
91066b6aef6SWilliam Kucharski 			newstr = append_str(kern_bargs, serial_config[1], " ");
91166b6aef6SWilliam Kucharski 			free(kern_bargs);
91266b6aef6SWilliam Kucharski 			kern_bargs = newstr;
91366b6aef6SWilliam Kucharski 		}
91466b6aef6SWilliam Kucharski 	}
91566b6aef6SWilliam Kucharski 
91666b6aef6SWilliam Kucharski 	if ((extra_args != NULL) && (*extra_args != NULL)) {
91766b6aef6SWilliam Kucharski 		newstr = append_str(kern_bargs, extra_args, " ");
91866b6aef6SWilliam Kucharski 		free(kern_bargs);
91966b6aef6SWilliam Kucharski 		kern_bargs = newstr;
92066b6aef6SWilliam Kucharski 	}
92144da779fSWilliam Kucharski 
92244da779fSWilliam Kucharski 	len = strlen(osroot) + strlen(XEN_MENU) + strlen(kern_bargs) +
92344da779fSWilliam Kucharski 	    WHITESPC(1) + 1;
92444da779fSWilliam Kucharski 
92544da779fSWilliam Kucharski 	kernel = alloca(len);
92644da779fSWilliam Kucharski 
92766b6aef6SWilliam Kucharski 	if (kern_bargs != NULL) {
92866b6aef6SWilliam Kucharski 		if (*kern_bargs != NULL)
92966b6aef6SWilliam Kucharski 			(void) snprintf(kernel, len, "%s%s %s", osroot,
93066b6aef6SWilliam Kucharski 			    XEN_MENU, kern_bargs);
93166b6aef6SWilliam Kucharski 
93244da779fSWilliam Kucharski 		free(kern_bargs);
93344da779fSWilliam Kucharski 	} else {
93444da779fSWilliam Kucharski 		(void) snprintf(kernel, len, "%s%s", osroot, XEN_MENU);
93544da779fSWilliam Kucharski 	}
93644da779fSWilliam Kucharski 
93744da779fSWilliam Kucharski 	/*
93844da779fSWilliam Kucharski 	 * Change the kernel directory from the metal version to that needed for
93944da779fSWilliam Kucharski 	 * the hypervisor.  Convert either "direct boot" path to the default
94044da779fSWilliam Kucharski 	 * path.
94144da779fSWilliam Kucharski 	 */
94244da779fSWilliam Kucharski 	if ((strcmp(kern_path, DIRECT_BOOT_32) == 0) ||
94344da779fSWilliam Kucharski 	    (strcmp(kern_path, DIRECT_BOOT_64) == 0)) {
94444da779fSWilliam Kucharski 		kern_path = HYPERVISOR_KERNEL;
94544da779fSWilliam Kucharski 	} else {
94666b6aef6SWilliam Kucharski 		newstr = modify_path(kern_path, METAL_KERNEL_DIR,
94744da779fSWilliam Kucharski 		    HYPER_KERNEL_DIR);
94866b6aef6SWilliam Kucharski 		free(kern_path);
94966b6aef6SWilliam Kucharski 		kern_path = newstr;
95066b6aef6SWilliam Kucharski 		kp_allocated = 1;
95144da779fSWilliam Kucharski 	}
95244da779fSWilliam Kucharski 
95344da779fSWilliam Kucharski 	/*
95444da779fSWilliam Kucharski 	 * We need to allocate space for the kernel path (twice) plus an
95544da779fSWilliam Kucharski 	 * intervening space, possibly the ZFS boot string, and NULL,
95644da779fSWilliam Kucharski 	 * of course.
95744da779fSWilliam Kucharski 	 */
95844da779fSWilliam Kucharski 	len = (strlen(kern_path) * 2) + WHITESPC(1) + 1;
95944da779fSWilliam Kucharski 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
96044da779fSWilliam Kucharski 
96144da779fSWilliam Kucharski 	mod_kernel = alloca(len + zfslen);
96244da779fSWilliam Kucharski 	(void) snprintf(mod_kernel, len, "%s %s", kern_path, kern_path);
96344da779fSWilliam Kucharski 
96444da779fSWilliam Kucharski 	if (kp_allocated)
96544da779fSWilliam Kucharski 		free(kern_path);
96644da779fSWilliam Kucharski 
96744da779fSWilliam Kucharski 	if (zfs_boot) {
96844da779fSWilliam Kucharski 		char *zfsstr = alloca(zfslen + 1);
96944da779fSWilliam Kucharski 
97044da779fSWilliam Kucharski 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
97144da779fSWilliam Kucharski 		(void) strcat(mod_kernel, zfsstr);
97244da779fSWilliam Kucharski 	}
97344da779fSWilliam Kucharski 
97444da779fSWilliam Kucharski 	/* shut off warning messages from the entry line parser */
97544da779fSWilliam Kucharski 	if (ent->flags & BAM_ENTRY_BOOTADM)
97644da779fSWilliam Kucharski 		ent->flags &= ~BAM_ENTRY_BOOTADM;
97744da779fSWilliam Kucharski 
978*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: converted kernel cmd to %s\n", fcn, kernel));
979*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: converted module cmd to %s\n", fcn, mod_kernel));
98044da779fSWilliam Kucharski 
981772d6a58SWilliam Kucharski 	if ((newdef = add_boot_entry(mp, title, findroot, kernel, mod_kernel,
982772d6a58SWilliam Kucharski 	    module, bootfs)) == BAM_ERROR)
983772d6a58SWilliam Kucharski 		return (newdef);
984772d6a58SWilliam Kucharski 
98544da779fSWilliam Kucharski 	/*
98644da779fSWilliam Kucharski 	 * Now try to delete the current default entry from the menu and add
98744da779fSWilliam Kucharski 	 * the new hypervisor entry with the parameters we've setup.
98844da779fSWilliam Kucharski 	 */
989772d6a58SWilliam Kucharski 	if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS)
990772d6a58SWilliam Kucharski 		newdef--;
991772d6a58SWilliam Kucharski 	else
992*f64ca102SToomas Soome 		bam_print(_("unable to modify default entry; creating new "
993*f64ca102SToomas Soome 		    "boot entry for %s\n"), title);
99444da779fSWilliam Kucharski 
99544da779fSWilliam Kucharski 	/*
99644da779fSWilliam Kucharski 	 * If we successfully created the new entry, set the default boot
99744da779fSWilliam Kucharski 	 * entry to that entry and let the caller know the new menu should
99844da779fSWilliam Kucharski 	 * be written out.
99944da779fSWilliam Kucharski 	 */
1000772d6a58SWilliam Kucharski 	return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef));
100144da779fSWilliam Kucharski 
100244da779fSWilliam Kucharski abort:
100344da779fSWilliam Kucharski 	if (ret != BAM_NOCHANGE)
1004*f64ca102SToomas Soome 		bam_error(_("error converting GRUB menu entry on %s for use "
1005*f64ca102SToomas Soome 		    "with the hypervisor.\nAborting.\n"),
1006*f64ca102SToomas Soome 		    ((*osroot == NULL) ? "/" : osroot));
100744da779fSWilliam Kucharski 
100844da779fSWilliam Kucharski 	return (ret);
100944da779fSWilliam Kucharski }
101044da779fSWilliam Kucharski 
101144da779fSWilliam Kucharski /*ARGSUSED*/
101244da779fSWilliam Kucharski error_t
101344da779fSWilliam Kucharski cvt_to_metal(menu_t *mp, char *osroot, char *menu_root)
101444da779fSWilliam Kucharski {
101544da779fSWilliam Kucharski 	const char *fcn = "cvt_to_metal()";
101644da779fSWilliam Kucharski 
101744da779fSWilliam Kucharski 	line_t *lp;
101844da779fSWilliam Kucharski 	entry_t *ent;
101944da779fSWilliam Kucharski 	size_t len, zfslen;
102044da779fSWilliam Kucharski 
102144da779fSWilliam Kucharski 	char *delim = ",";
102266b6aef6SWilliam Kucharski 	char *newstr;
102344da779fSWilliam Kucharski 	char *osdev;
102444da779fSWilliam Kucharski 
102544da779fSWilliam Kucharski 	char *title = NULL;
102644da779fSWilliam Kucharski 	char *findroot = NULL;
102744da779fSWilliam Kucharski 	char *bootfs = NULL;
102844da779fSWilliam Kucharski 	char *kernel = NULL;
102944da779fSWilliam Kucharski 	char *module = NULL;
103044da779fSWilliam Kucharski 
103144da779fSWilliam Kucharski 	char *barchive_path = DIRECT_BOOT_ARCHIVE;
103244da779fSWilliam Kucharski 	char *kern_path = NULL;
103344da779fSWilliam Kucharski 
1034772d6a58SWilliam Kucharski 	int curdef, newdef;
103544da779fSWilliam Kucharski 	int emit_bflag = 1;
103644da779fSWilliam Kucharski 	int ret = BAM_ERROR;
103744da779fSWilliam Kucharski 
103844da779fSWilliam Kucharski 	assert(osroot);
103944da779fSWilliam Kucharski 
1040*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, ""));
104144da779fSWilliam Kucharski 
104244da779fSWilliam Kucharski 	/*
104344da779fSWilliam Kucharski 	 * First just check to verify osroot is a sane directory.
104444da779fSWilliam Kucharski 	 */
104544da779fSWilliam Kucharski 	if ((osdev = get_special(osroot)) == NULL) {
1046*f64ca102SToomas Soome 		bam_error(_("cant find special file for mount-point %s\n"),
1047*f64ca102SToomas Soome 		    osroot);
104844da779fSWilliam Kucharski 		return (BAM_ERROR);
104944da779fSWilliam Kucharski 	}
105044da779fSWilliam Kucharski 
105144da779fSWilliam Kucharski 	free(osdev);
105244da779fSWilliam Kucharski 
105344da779fSWilliam Kucharski 	/*
105444da779fSWilliam Kucharski 	 * Found the GRUB signature on the target partitions, so now get the
105544da779fSWilliam Kucharski 	 * default GRUB boot entry number from the menu.lst file
105644da779fSWilliam Kucharski 	 */
105744da779fSWilliam Kucharski 	curdef = atoi(mp->curdefault->arg);
105844da779fSWilliam Kucharski 
105944da779fSWilliam Kucharski 	/* look for the first line of the matching boot entry */
106044da779fSWilliam Kucharski 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
106144da779fSWilliam Kucharski 	    ent = ent->next)
106244da779fSWilliam Kucharski 		;
106344da779fSWilliam Kucharski 
106444da779fSWilliam Kucharski 	/* couldn't find it, so error out */
106544da779fSWilliam Kucharski 	if (ent == NULL) {
1066*f64ca102SToomas Soome 		bam_error(_("unable to find default boot entry (%d) in "
1067*f64ca102SToomas Soome 		    "menu.lst file.\n"), curdef);
106844da779fSWilliam Kucharski 		goto abort;
106944da779fSWilliam Kucharski 	}
107044da779fSWilliam Kucharski 
107144da779fSWilliam Kucharski 	/*
107244da779fSWilliam Kucharski 	 * Now process the entry itself.
107344da779fSWilliam Kucharski 	 */
107444da779fSWilliam Kucharski 	for (lp = ent->start; lp != NULL; lp = lp->next) {
107544da779fSWilliam Kucharski 		/*
107644da779fSWilliam Kucharski 		 * Process important lines from menu.lst boot entry.
107744da779fSWilliam Kucharski 		 */
107844da779fSWilliam Kucharski 		if (lp->flags == BAM_TITLE) {
107923a1cceaSRoger A. Faulkner 			title = strdupa(lp->arg);
1080eac223ccSWilliam Kucharski 		} else if (lp->cmd != NULL) {
1081eac223ccSWilliam Kucharski 			if (strcmp(lp->cmd, "findroot") == 0) {
108223a1cceaSRoger A. Faulkner 				findroot = strdupa(lp->arg);
108344da779fSWilliam Kucharski 			} else if (strcmp(lp->cmd, "bootfs") == 0) {
108423a1cceaSRoger A. Faulkner 				bootfs = strdupa(lp->arg);
1085eac223ccSWilliam Kucharski 			} else if (strcmp(lp->cmd,
1086eac223ccSWilliam Kucharski 			    menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
108744da779fSWilliam Kucharski 				if (strstr(lp->arg, "boot_archive") == NULL) {
108823a1cceaSRoger A. Faulkner 					module = strdupa(lp->arg);
108944da779fSWilliam Kucharski 					cvt_hyper_module(module, &kern_path);
109044da779fSWilliam Kucharski 				} else {
109123a1cceaSRoger A. Faulkner 					barchive_path = strdupa(lp->arg);
109244da779fSWilliam Kucharski 				}
109344da779fSWilliam Kucharski 			} else if ((strcmp(lp->cmd,
109444da779fSWilliam Kucharski 			    menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
109544da779fSWilliam Kucharski 			    (cvt_hyper_kernel(lp->arg) < 0)) {
109644da779fSWilliam Kucharski 				ret = BAM_NOCHANGE;
109744da779fSWilliam Kucharski 				goto abort;
109844da779fSWilliam Kucharski 			}
1099eac223ccSWilliam Kucharski 		}
110044da779fSWilliam Kucharski 
110144da779fSWilliam Kucharski 		if (lp == ent->end)
110244da779fSWilliam Kucharski 			break;
110344da779fSWilliam Kucharski 	}
110444da779fSWilliam Kucharski 
110544da779fSWilliam Kucharski 	/*
1106772d6a58SWilliam Kucharski 	 * If findroot, module or kern_path are NULL, the boot entry is
1107772d6a58SWilliam Kucharski 	 * malformed.
110844da779fSWilliam Kucharski 	 */
110944da779fSWilliam Kucharski 	if (findroot == NULL) {
1110*f64ca102SToomas Soome 		bam_error(_("findroot in default boot entry (%d) missing.\n"),
1111*f64ca102SToomas Soome 		    curdef);
111244da779fSWilliam Kucharski 		goto abort;
111344da779fSWilliam Kucharski 	}
111444da779fSWilliam Kucharski 
111544da779fSWilliam Kucharski 	if (module == NULL) {
1116*f64ca102SToomas Soome 		bam_error(_("module$ in default boot entry (%d) missing or "
1117*f64ca102SToomas Soome 		    "not parseable.\n"), curdef);
111844da779fSWilliam Kucharski 		goto abort;
111944da779fSWilliam Kucharski 	}
112044da779fSWilliam Kucharski 
112144da779fSWilliam Kucharski 	if (kern_path == NULL) {
1122*f64ca102SToomas Soome 		bam_error(_("kernel$ in default boot entry (%d) missing.\n"),
1123*f64ca102SToomas Soome 		    curdef);
112444da779fSWilliam Kucharski 		goto abort;
112544da779fSWilliam Kucharski 	}
112644da779fSWilliam Kucharski 
112744da779fSWilliam Kucharski 	/*
112844da779fSWilliam Kucharski 	 * Assemble new kernel and module arguments from parsed values.
112944da779fSWilliam Kucharski 	 *
113044da779fSWilliam Kucharski 	 * First, change the kernel directory from the hypervisor version to
113144da779fSWilliam Kucharski 	 * that needed for a metal kernel.
113244da779fSWilliam Kucharski 	 */
113366b6aef6SWilliam Kucharski 	newstr = modify_path(kern_path, HYPER_KERNEL_DIR, METAL_KERNEL_DIR);
113466b6aef6SWilliam Kucharski 	free(kern_path);
113566b6aef6SWilliam Kucharski 	kern_path = newstr;
113644da779fSWilliam Kucharski 
113744da779fSWilliam Kucharski 	/* allocate initial space for the kernel path */
113844da779fSWilliam Kucharski 	len = strlen(kern_path) + 1;
113944da779fSWilliam Kucharski 	zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
114044da779fSWilliam Kucharski 
114144da779fSWilliam Kucharski 	if ((kernel = malloc(len + zfslen)) == NULL) {
114244da779fSWilliam Kucharski 		free(kern_path);
1143*f64ca102SToomas Soome 		bam_error(_("could not allocate memory: size = %u\n"),
1144*f64ca102SToomas Soome 		    len + zfslen);
114566b6aef6SWilliam Kucharski 		bam_exit(1);
114644da779fSWilliam Kucharski 	}
114744da779fSWilliam Kucharski 
114844da779fSWilliam Kucharski 	(void) snprintf(kernel, len, "%s", kern_path);
114944da779fSWilliam Kucharski 	free(kern_path);
115044da779fSWilliam Kucharski 
115144da779fSWilliam Kucharski 	if (zfs_boot) {
115244da779fSWilliam Kucharski 		char *zfsstr = alloca(zfslen + 1);
115344da779fSWilliam Kucharski 
115444da779fSWilliam Kucharski 		(void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
115544da779fSWilliam Kucharski 		(void) strcat(kernel, zfsstr);
115644da779fSWilliam Kucharski 		emit_bflag = 0;
115744da779fSWilliam Kucharski 	}
115844da779fSWilliam Kucharski 
1159772d6a58SWilliam Kucharski 	/*
1160772d6a58SWilliam Kucharski 	 * Process the bootenv.rc file to look for boot options that would be
1161772d6a58SWilliam Kucharski 	 * the same as what the hypervisor had manually set, as we need not set
1162772d6a58SWilliam Kucharski 	 * those explicitly.
1163772d6a58SWilliam Kucharski 	 *
1164772d6a58SWilliam Kucharski 	 * If there's no bootenv.rc, it's not an issue.
1165772d6a58SWilliam Kucharski 	 */
1166772d6a58SWilliam Kucharski 	parse_bootenvrc(osroot);
1167772d6a58SWilliam Kucharski 
1168772d6a58SWilliam Kucharski 	/*
1169772d6a58SWilliam Kucharski 	 * Don't emit a console setting if it's the same as what would be
1170772d6a58SWilliam Kucharski 	 * set by bootenv.rc.
1171772d6a58SWilliam Kucharski 	 */
1172772d6a58SWilliam Kucharski 	if ((console_dev != NULL) && (bootenv_rc_console == NULL ||
1173772d6a58SWilliam Kucharski 	    (strcmp(console_dev, bootenv_rc_console) != 0))) {
117444da779fSWilliam Kucharski 		if (emit_bflag) {
117566b6aef6SWilliam Kucharski 			newstr = append_str(kernel, BFLAG, " ");
117666b6aef6SWilliam Kucharski 			free(kernel);
1177772d6a58SWilliam Kucharski 			kernel = append_str(newstr, "console=", " ");
117866b6aef6SWilliam Kucharski 			free(newstr);
1179772d6a58SWilliam Kucharski 			newstr = append_str(kernel, console_dev, "");
118066b6aef6SWilliam Kucharski 			free(kernel);
118166b6aef6SWilliam Kucharski 			kernel = newstr;
1182772d6a58SWilliam Kucharski 			emit_bflag = 0;
1183772d6a58SWilliam Kucharski 		} else {
1184772d6a58SWilliam Kucharski 			newstr = append_str(kernel, "console=", ",");
1185772d6a58SWilliam Kucharski 			free(kernel);
1186772d6a58SWilliam Kucharski 			kernel = append_str(newstr, console_dev, "");
1187772d6a58SWilliam Kucharski 			free(newstr);
118844da779fSWilliam Kucharski 		}
118944da779fSWilliam Kucharski 	}
119044da779fSWilliam Kucharski 
119144da779fSWilliam Kucharski 	/*
1192772d6a58SWilliam Kucharski 	 * We have to do some strange processing here because the hypervisor's
1193772d6a58SWilliam Kucharski 	 * serial ports default to "9600,8,n,1,-" if "comX=auto" is specified,
1194772d6a58SWilliam Kucharski 	 * or to "auto" if nothing is specified.
119544da779fSWilliam Kucharski 	 *
1196772d6a58SWilliam Kucharski 	 * This could result in a serial mode setting string being added when
1197772d6a58SWilliam Kucharski 	 * it would otherwise not be needed, but it's better to play it safe.
119844da779fSWilliam Kucharski 	 */
119944da779fSWilliam Kucharski 	if (emit_bflag) {
120066b6aef6SWilliam Kucharski 		newstr = append_str(kernel, BFLAG, " ");
120166b6aef6SWilliam Kucharski 		free(kernel);
120266b6aef6SWilliam Kucharski 		kernel = newstr;
120344da779fSWilliam Kucharski 		delim = " ";
120444da779fSWilliam Kucharski 		emit_bflag = 0;
120544da779fSWilliam Kucharski 	}
120644da779fSWilliam Kucharski 
1207772d6a58SWilliam Kucharski 	if ((serial_config[0] != NULL) && (bootenv_rc_serial[0] == NULL ||
1208772d6a58SWilliam Kucharski 	    (strcmp(serial_config[0], bootenv_rc_serial[0]) != 0))) {
1209772d6a58SWilliam Kucharski 		newstr = append_str(kernel, "ttya-mode='", delim);
121066b6aef6SWilliam Kucharski 		free(kernel);
121144da779fSWilliam Kucharski 
1212772d6a58SWilliam Kucharski 		/*
1213772d6a58SWilliam Kucharski 		 * Pass the serial configuration as the delimiter to
1214772d6a58SWilliam Kucharski 		 * append_str() as it will be inserted between the current
1215772d6a58SWilliam Kucharski 		 * string and the string we're appending, in this case the
1216772d6a58SWilliam Kucharski 		 * closing single quote.
1217772d6a58SWilliam Kucharski 		 */
1218772d6a58SWilliam Kucharski 		kernel = append_str(newstr, "'", serial_config[0]);
121966b6aef6SWilliam Kucharski 		free(newstr);
1220772d6a58SWilliam Kucharski 		delim = ",";
1221772d6a58SWilliam Kucharski 	}
1222772d6a58SWilliam Kucharski 
1223772d6a58SWilliam Kucharski 	if ((serial_config[1] != NULL) && (bootenv_rc_serial[1] == NULL ||
1224772d6a58SWilliam Kucharski 	    (strcmp(serial_config[1], bootenv_rc_serial[1]) != 0))) {
1225772d6a58SWilliam Kucharski 		newstr = append_str(kernel, "ttyb-mode='", delim);
1226772d6a58SWilliam Kucharski 		free(kernel);
1227772d6a58SWilliam Kucharski 
1228772d6a58SWilliam Kucharski 		/*
1229772d6a58SWilliam Kucharski 		 * Pass the serial configuration as the delimiter to
1230772d6a58SWilliam Kucharski 		 * append_str() as it will be inserted between the current
1231772d6a58SWilliam Kucharski 		 * string and the string we're appending, in this case the
1232772d6a58SWilliam Kucharski 		 * closing single quote.
1233772d6a58SWilliam Kucharski 		 */
1234772d6a58SWilliam Kucharski 		kernel = append_str(newstr, "'", serial_config[1]);
1235772d6a58SWilliam Kucharski 		free(newstr);
1236772d6a58SWilliam Kucharski 		delim = ",";
1237772d6a58SWilliam Kucharski 	}
123844da779fSWilliam Kucharski 
123944da779fSWilliam Kucharski 	/* shut off warning messages from the entry line parser */
124044da779fSWilliam Kucharski 	if (ent->flags & BAM_ENTRY_BOOTADM)
124144da779fSWilliam Kucharski 		ent->flags &= ~BAM_ENTRY_BOOTADM;
124244da779fSWilliam Kucharski 
1243*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: converted kernel cmd to %s\n", fcn, kernel));
1244*f64ca102SToomas Soome 	BAM_DPRINTF(("%s: converted module cmd to %s\n", fcn, module));
124544da779fSWilliam Kucharski 
1246772d6a58SWilliam Kucharski 	if ((newdef = add_boot_entry(mp, title, findroot, kernel, NULL,
1247772d6a58SWilliam Kucharski 	    barchive_path, bootfs)) == BAM_ERROR) {
1248772d6a58SWilliam Kucharski 		free(kernel);
1249772d6a58SWilliam Kucharski 		return (newdef);
1250772d6a58SWilliam Kucharski 	}
1251772d6a58SWilliam Kucharski 
125244da779fSWilliam Kucharski 	/*
125344da779fSWilliam Kucharski 	 * Now try to delete the current default entry from the menu and add
125444da779fSWilliam Kucharski 	 * the new hypervisor entry with the parameters we've setup.
125544da779fSWilliam Kucharski 	 */
1256772d6a58SWilliam Kucharski 	if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS)
1257772d6a58SWilliam Kucharski 		newdef--;
1258772d6a58SWilliam Kucharski 	else
1259*f64ca102SToomas Soome 		bam_print(_("unable to modify default entry; creating new "
1260*f64ca102SToomas Soome 		    "boot entry for %s\n"), title);
126144da779fSWilliam Kucharski 
1262772d6a58SWilliam Kucharski 	free(kernel);
126344da779fSWilliam Kucharski 
126444da779fSWilliam Kucharski 	/*
126544da779fSWilliam Kucharski 	 * If we successfully created the new entry, set the default boot
126644da779fSWilliam Kucharski 	 * entry to that entry and let the caller know the new menu should
126744da779fSWilliam Kucharski 	 * be written out.
126844da779fSWilliam Kucharski 	 */
1269772d6a58SWilliam Kucharski 	return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef));
127044da779fSWilliam Kucharski 
127144da779fSWilliam Kucharski abort:
127244da779fSWilliam Kucharski 	if (ret != BAM_NOCHANGE)
1273*f64ca102SToomas Soome 		bam_error(_("error converting GRUB menu entry on %s for use "
1274*f64ca102SToomas Soome 		    "with a metal kernel.\nAborting.\n"), osroot);
127544da779fSWilliam Kucharski 
127644da779fSWilliam Kucharski 	return (ret);
127744da779fSWilliam Kucharski }
1278