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