summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Kucharski <William.Kucharski@Sun.COM>2009-09-11 08:43:34 -0600
committerWilliam Kucharski <William.Kucharski@Sun.COM>2009-09-11 08:43:34 -0600
commit44da779fd55c337eb9877e490e494037fc1ddf06 (patch)
treefb2144482700b57024b74cb38c6dcdd08db2ff13
parentfbd8dcf988ac639217a2bdb4287206afb8b347b6 (diff)
downloadillumos-joyent-44da779fd55c337eb9877e490e494037fc1ddf06.tar.gz
6874337 Add private commands to bootadm to enable/disable hypervisor
-rw-r--r--usr/src/cmd/boot/bootadm/Makefile2
-rw-r--r--usr/src/cmd/boot/bootadm/bootadm.c216
-rw-r--r--usr/src/cmd/boot/bootadm/bootadm.h17
-rw-r--r--usr/src/cmd/boot/bootadm/bootadm_hyper.c1183
-rw-r--r--usr/src/cmd/boot/bootadm/bootadm_upgrade.c2
-rw-r--r--usr/src/cmd/boot/bootadm/message.h37
6 files changed, 1415 insertions, 42 deletions
diff --git a/usr/src/cmd/boot/bootadm/Makefile b/usr/src/cmd/boot/bootadm/Makefile
index e0aa385f27..f89afb9ccd 100644
--- a/usr/src/cmd/boot/bootadm/Makefile
+++ b/usr/src/cmd/boot/bootadm/Makefile
@@ -27,7 +27,7 @@ PROG= bootadm
SBINLINKS= $(PROG)
-OBJS= bootadm.o bootadm_upgrade.o
+OBJS= bootadm.o bootadm_upgrade.o bootadm_hyper.o
SRCS = $(OBJS:.o=.c)
include ../Makefile.com
diff --git a/usr/src/cmd/boot/bootadm/bootadm.c b/usr/src/cmd/boot/bootadm/bootadm.c
index aa67099f6e..cc3faa2784 100644
--- a/usr/src/cmd/boot/bootadm/bootadm.c
+++ b/usr/src/cmd/boot/bootadm/bootadm.c
@@ -38,6 +38,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <alloca.h>
#include <stdarg.h>
#include <limits.h>
#include <signal.h>
@@ -192,6 +193,7 @@ char *menu_cmds[] = {
"chainloader", /* CHAINLOADER_CMD */
"args", /* ARGS_CMD */
"findroot", /* FINDROOT_CMD */
+ "bootfs", /* BOOTFS_CMD */
NULL
};
@@ -262,6 +264,7 @@ static void menu_free(menu_t *);
static void filelist_free(filelist_t *);
static error_t list2file(char *, char *, char *, line_t *);
static error_t list_entry(menu_t *, char *, char *);
+static error_t list_setting(menu_t *, char *, char *);
static error_t delete_all_entries(menu_t *, char *, char *);
static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
static error_t update_temp(menu_t *mp, char *dummy, char *opt);
@@ -270,7 +273,6 @@ static error_t update_archive(char *, char *);
static error_t list_archive(char *, char *);
static error_t update_all(char *, char *);
static error_t read_list(char *, filelist_t *);
-static error_t set_global(menu_t *, char *, int);
static error_t set_option(menu_t *, char *, char *);
static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
@@ -302,6 +304,9 @@ static subcmd_defn_t menu_subcmds[] = {
"update_entry", OPT_REQ, update_entry, 0, /* menu */
"update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
"upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
+ "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
+ "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
+ "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
NULL, 0, NULL, 0 /* must be last */
};
@@ -530,7 +535,7 @@ main(int argc, char *argv[])
}
if (ret != BAM_SUCCESS)
- bam_exit(1);
+ bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
bam_unlock();
return (0);
@@ -592,13 +597,17 @@ parse_args(int argc, char *argv[])
/*
* A combination of public and private commands are parsed here.
* The internal syntax and the corresponding functionality are:
- * -a update -- update-archive
- * -a list -- list-archive
- * -a update-all -- (reboot to sync all mounted OS archive)
- * -m update_entry -- update-menu
- * -m list_entry -- list-menu
- * -m update_temp -- (reboot -- [boot-args])
- * -m delete_all_entries -- (called from install)
+ * -a update -- update-archive
+ * -a list -- list-archive
+ * -a update-all -- (reboot to sync all mnted OS archive)
+ * -m update_entry -- update-menu
+ * -m list_entry -- list-menu
+ * -m update_temp -- (reboot -- [boot-args])
+ * -m delete_all_entries -- (called from install)
+ * -m enable_hypervisor [args] -- cvt_to_hyper
+ * -m disable_hypervisor -- cvt_to_metal
+ * -m list_setting [entry] [value] -- list_setting
+ *
* A set of private flags is there too:
* -F -- purge the cache directories and rebuild them
* -e -- use the (faster) archive update approach (used by
@@ -795,7 +804,12 @@ check_subcmd_and_options(
}
if (bam_argc == 1)
sync_menu = 0;
- } else if (bam_argc || bam_argv) {
+ } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
+ (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
+ /*
+ * Of the remaining subcommands, only "enable_hypervisor" and
+ * "list_setting" take trailing arguments.
+ */
bam_error(TRAILING_ARGS);
usage();
return (BAM_ERROR);
@@ -893,6 +907,69 @@ is_safe_exec(char *path)
}
static error_t
+list_setting(menu_t *mp, char *which, char *setting)
+{
+ line_t *lp;
+ entry_t *ent;
+
+ char *p = which;
+ int entry;
+
+ int found;
+
+ assert(which);
+ assert(setting);
+
+ if (*which != NULL) {
+ /*
+ * If "which" is not a number, assume it's a setting we want
+ * to look for and so set up the routine to look for "which"
+ * in the default entry.
+ */
+ while (*p != NULL)
+ if (!(isdigit((int)*p++))) {
+ setting = which;
+ which = mp->curdefault->arg;
+ break;
+ }
+ } else {
+ which = mp->curdefault->arg;
+ }
+
+ entry = atoi(which);
+
+ for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
+ ent = ent->next)
+ ;
+
+ if (!ent) {
+ bam_error(NO_MATCH_ENTRY);
+ return (BAM_ERROR);
+ }
+
+ found = (*setting == NULL);
+
+ for (lp = ent->start; lp != NULL; lp = lp->next) {
+ if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
+ bam_print(PRINT, lp->line);
+ else if (strcmp(setting, lp->cmd) == 0) {
+ bam_print(PRINT, lp->arg);
+ found = 1;
+ }
+
+ if (lp == ent->end)
+ break;
+ }
+
+ if (!found) {
+ bam_error(NO_MATCH_ENTRY);
+ return (BAM_ERROR);
+ }
+
+ return (BAM_SUCCESS);
+}
+
+static error_t
bam_menu(char *subcmd, char *opt, int largc, char *largv[])
{
error_t ret;
@@ -1027,13 +1104,16 @@ bam_menu(char *subcmd, char *opt, int largc, char *largv[])
/*
* If listing the menu, display the menu location
*/
- if (strcmp(subcmd, "list_entry") == 0) {
+ if (strcmp(subcmd, "list_entry") == 0)
bam_print(GRUB_MENU_PATH, menu_path);
- }
+ if ((menu = menu_read(menu_path)) == NULL) {
+ bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path);
+ if (special != NULL)
+ free(special);
- menu = menu_read(menu_path);
- assert(menu);
+ return (BAM_ERROR);
+ }
/*
* We already checked the following case in
@@ -1043,7 +1123,8 @@ bam_menu(char *subcmd, char *opt, int largc, char *largv[])
if (strcmp(subcmd, "set_option") == 0) {
assert(largc == 1 && largv[0] && largv[1] == NULL);
opt = largv[0];
- } else {
+ } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
+ (strcmp(subcmd, "list_setting") != 0)) {
assert(largc == 0 && largv == NULL);
}
@@ -1057,13 +1138,68 @@ bam_menu(char *subcmd, char *opt, int largc, char *largv[])
* Once the sub-cmd handler has run
* only the line field is guaranteed to have valid values
*/
- if (strcmp(subcmd, "update_entry") == 0)
+ if (strcmp(subcmd, "update_entry") == 0) {
ret = f(menu, menu_root, osdev);
- else if (strcmp(subcmd, "upgrade") == 0)
+ } else if (strcmp(subcmd, "upgrade") == 0) {
ret = f(menu, bam_root, menu_root);
- else if (strcmp(subcmd, "list_entry") == 0)
+ } else if (strcmp(subcmd, "list_entry") == 0) {
ret = f(menu, menu_path, opt);
- else
+ } else if (strcmp(subcmd, "list_setting") == 0) {
+ ret = f(menu, ((largc > 0) ? largv[0] : ""),
+ ((largc > 1) ? largv[1] : ""));
+ } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
+ if (is_sparc()) {
+ bam_error(NO_SPARC, subcmd);
+ ret = BAM_ERROR;
+ } else {
+ ret = f(menu, bam_root, NULL);
+ }
+ } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
+ if (is_sparc()) {
+ bam_error(NO_SPARC, subcmd);
+ ret = BAM_ERROR;
+ } else {
+ char *extra_args = NULL;
+
+ /*
+ * Compress all arguments passed in the largv[] array
+ * into one string that can then be appended to the
+ * end of the kernel$ string the routine to enable the
+ * hypervisor will build.
+ *
+ * This allows the caller to supply arbitrary unparsed
+ * arguments, such as dom0 memory settings or APIC
+ * options.
+ *
+ * This concatenation will be done without ANY syntax
+ * checking whatsoever, so it's the responsibility of
+ * the caller to make sure the arguments are valid and
+ * do not duplicate arguments the conversion routines
+ * may create.
+ */
+ if (largc > 0) {
+ int extra_len, i;
+
+ for (extra_len = 0, i = 0; i < largc; i++)
+ extra_len += strlen(largv[i]);
+
+ /*
+ * Allocate space for argument strings,
+ * intervening spaces and terminating NULL.
+ */
+ extra_args = alloca(extra_len + largc);
+
+ (void) strcpy(extra_args, largv[0]);
+
+ for (i = 1; i < largc; i++) {
+ (void) strcat(extra_args, " ");
+ (void) strcat(extra_args, largv[i]);
+ }
+ }
+
+ ret = f(menu, bam_root, extra_args);
+ }
+ } else
ret = f(menu, NULL, opt);
if (ret == BAM_WRITE) {
@@ -1420,7 +1556,6 @@ list2file(char *root, char *tmp, char *final, line_t *start)
return (BAM_ERROR);
}
-
/*
* Do an atomic rename
*/
@@ -4332,10 +4467,10 @@ menu_read(char *menu_path)
fp = fopen(menu_path, "r");
if (fp == NULL) { /* Let the caller handle this error */
- return (mp);
+ free(mp);
+ return (NULL);
}
-
/* Note: GRUB boot entry number starts with 0 */
line = LINE_INIT;
entry = ENTRY_INIT;
@@ -4490,7 +4625,8 @@ add_boot_entry(menu_t *mp,
char *findroot,
char *kernel,
char *mod_kernel,
- char *module)
+ char *module,
+ char *bootfs)
{
int lineNum;
int entryNum;
@@ -4572,6 +4708,12 @@ add_boot_entry(menu_t *mp,
line_parser(mp, linebuf, &lineNum, &entryNum);
BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
+ if (bootfs != NULL) {
+ (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
+ menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
+ line_parser(mp, linebuf, &lineNum, &entryNum);
+ }
+
(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
line_parser(mp, linebuf, &lineNum, &entryNum);
@@ -4593,15 +4735,15 @@ add_boot_entry(menu_t *mp,
return (entryNum);
}
-static error_t
-do_delete(menu_t *mp, int entryNum)
+error_t
+delete_boot_entry(menu_t *mp, int entryNum, int quiet)
{
line_t *lp;
line_t *freed;
entry_t *ent;
entry_t *tmp;
- int deleted;
- const char *fcn = "do_delete()";
+ int deleted = 0;
+ const char *fcn = "delete_boot_entry()";
assert(entryNum != ENTRY_INIT);
@@ -4646,7 +4788,8 @@ do_delete(menu_t *mp, int entryNum)
assert(tmp == NULL);
if (!deleted && entryNum != ALL_ENTRIES) {
- bam_error(NO_BOOTADM_MATCH);
+ if (quiet == DBE_PRINTERR)
+ bam_error(NO_BOOTADM_MATCH);
return (BAM_ERROR);
}
@@ -4673,7 +4816,7 @@ delete_all_entries(menu_t *mp, char *dummy, char *opt)
return (BAM_SUCCESS);
}
- if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
+ if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
return (BAM_ERROR);
}
@@ -6819,7 +6962,6 @@ get_grubsign(char *osroot, char *osdev)
const char *fcn = "get_grubsign()";
BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
-
fstype = get_fstype(osroot);
INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
if (fstype == NULL) {
@@ -7684,7 +7826,7 @@ update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
if (ent == NULL) {
BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
return (add_boot_entry(mp, title, findroot,
- kernel, mod_kernel, module));
+ kernel, mod_kernel, module, NULL));
}
/* replace title of existing entry and update findroot line */
@@ -8110,7 +8252,7 @@ update_temp(menu_t *mp, char *dummy, char *opt)
BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
return (BAM_SUCCESS);
}
- (void) do_delete(mp, entry);
+ (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
mp->olddefault = NULL;
BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
@@ -8294,7 +8436,7 @@ update_temp(menu_t *mp, char *dummy, char *opt)
}
free(fstype);
entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
- NULL, NULL);
+ NULL, NULL, NULL);
INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
if (entry == BAM_ERROR) {
bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
@@ -8311,7 +8453,7 @@ update_temp(menu_t *mp, char *dummy, char *opt)
return (BAM_WRITE);
}
-static error_t
+error_t
set_global(menu_t *mp, char *globalcmd, int val)
{
line_t *lp;
@@ -8671,7 +8813,7 @@ get_set_kernel(
(strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
kernelp = NULL;
- (void) do_delete(mp, entryNum);
+ (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
restore_default_entry(mp, BAM_OLD_RC_DEF,
mp->old_rc_default);
mp->old_rc_default = NULL;
@@ -8779,7 +8921,7 @@ get_set_kernel(
if (optnum == KERNEL_CMD) {
BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path));
entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
- signbuf, new_path, NULL, NULL);
+ signbuf, new_path, NULL, NULL, NULL);
} else {
new_str_len = strlen(path) + 8;
if (strcmp(fstype, "zfs") == 0) {
@@ -8796,7 +8938,7 @@ get_set_kernel(
BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
- signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
+ signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
free(new_arg);
}
free(fstype);
diff --git a/usr/src/cmd/boot/bootadm/bootadm.h b/usr/src/cmd/boot/bootadm/bootadm.h
index 2d0c867cbb..ab54f6cbe8 100644
--- a/usr/src/cmd/boot/bootadm/bootadm.h
+++ b/usr/src/cmd/boot/bootadm/bootadm.h
@@ -99,7 +99,8 @@ typedef enum {
BAM_ERROR = -1, /* Must be negative. add_boot_entry() depends on it */
BAM_SUCCESS = 0,
BAM_WRITE = 2,
- BAM_MSG /* Used by upgrade_menu() */
+ BAM_MSG, /* Used by upgrade_menu() */
+ BAM_NOCHANGE /* Used by cvt_to_hyper()/cvt_to_metal() */
} error_t;
/*
@@ -122,7 +123,8 @@ typedef enum {
COMMENT_CMD,
CHAINLOADER_CMD,
ARGS_CMD,
- FINDROOT_CMD
+ FINDROOT_CMD,
+ BOOTFS_CMD
} menu_cmd_t;
extern char *menu_cmds[];
@@ -157,7 +159,10 @@ extern int bam_debug;
extern void bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp);
extern void update_numbering(menu_t *mp);
+extern error_t set_global(menu_t *, char *, int);
extern error_t upgrade_menu(menu_t *, char *, char *);
+extern error_t cvt_to_hyper(menu_t *, char *, char *);
+extern error_t cvt_to_metal(menu_t *, char *, char *);
extern void *s_calloc(size_t, size_t);
extern void *s_realloc(void *, size_t);
extern char *s_fgets(char *buf, int n, FILE *fp);
@@ -169,7 +174,9 @@ extern error_t get_boot_cap(const char *osroot);
extern char *get_special(char *);
extern char *os_to_grubdisk(char *, int);
extern void update_line(line_t *);
-extern int add_boot_entry(menu_t *, char *, char *, char *, char *, char *);
+extern int add_boot_entry(menu_t *, char *, char *, char *, char *, char *,
+ char *);
+extern error_t delete_boot_entry(menu_t *, int, int);
extern int is_grub(const char *);
extern char *get_grubsign(char *osroot, char *osdev);
extern char *get_grubroot(char *osroot, char *osdev, char *menu_root);
@@ -261,6 +268,10 @@ extern int is_sparc(void);
/* A first guess at the number of entries in a menu */
#define BAM_ENTRY_NUM 10
+/* toggle for whether delete_boot_entry prints an error message or not */
+#define DBE_PRINTERR 0
+#define DBE_QUIET 1
+
/*
* Debugging defines
*/
diff --git a/usr/src/cmd/boot/bootadm/bootadm_hyper.c b/usr/src/cmd/boot/bootadm/bootadm_hyper.c
new file mode 100644
index 0000000000..4b11fda9c7
--- /dev/null
+++ b/usr/src/cmd/boot/bootadm/bootadm_hyper.c
@@ -0,0 +1,1183 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#include "message.h"
+#include "bootadm.h"
+
+#define HYPER_KERNEL_DIR "/platform/i86xpv/kernel"
+#define METAL_KERNEL_DIR "/platform/i86pc/kernel"
+
+#define BOOTRC_FILE "/boot/solaris/bootenv.rc"
+#define ZFS_BOOTSTR "$ZFS-BOOTFS"
+
+#define BFLAG "-B"
+#define DEFAULT_SERIAL "9600,8,n,1"
+
+#define TTYXMODE_TO_COMNUM(ttyxmode) ((int)(*((ttyxmode) + 3) - '`'))
+#define COMNAME_TO_COMNUM(comname) ((int)(*((comname) + 3) - '0'))
+
+#define WHITESPC(x) (x)
+
+static char *serial_config[2] = { NULL, NULL };
+
+static char *console_dev = NULL;
+
+static unsigned zfs_boot = 0;
+
+/*
+ * Append the string pointed to by "str" to the string pointed to by "orig"
+ * adding the delimeter "delim" in between.
+ *
+ * Return a pointer to the new string or NULL, if we were passed a bad string
+ * or we couldn't allocate memory for the new one.
+ */
+static char *
+append_str(char *orig, char *str, char *delim)
+{
+ char *newstr;
+ int len;
+
+ if ((orig == NULL) || (str == NULL) || (delim == NULL))
+ return (NULL);
+
+ if (*orig == NULL)
+ return (s_strdup(str));
+
+ len = strlen(orig) + strlen(str) + strlen(delim) + 1;
+ newstr = s_realloc(orig, len);
+
+ if (newstr != NULL)
+ (void) snprintf(newstr, len, "%s%s%s", orig, delim, str);
+
+ return (newstr);
+}
+
+/*
+ * Replace the substring "old_str" in a path with the substring "new_str"
+ *
+ * Return a pointer to the modified string or NULL, if we couldn't allocate
+ * memory for the new string.
+ */
+static char *
+modify_path(char *path, char *old_str, char *new_str)
+{
+ char *newpath;
+ char *oldpath;
+ char *pc;
+
+ oldpath = s_strdup(path);
+
+ if ((pc = strstr(oldpath, old_str)) == NULL) {
+ free(oldpath);
+ return (path);
+ }
+
+ /*
+ * Allocate space for duplicate of path with name changes and
+ * NULL terminating byte
+ */
+ if ((newpath = s_realloc(path,
+ strlen(oldpath) - strlen(old_str) + strlen(new_str) + 1)) == NULL) {
+ free(oldpath);
+ return (NULL);
+ }
+
+ (void) strlcpy(newpath, oldpath, (pc - oldpath) + 1);
+ free(oldpath);
+
+ (void) strcat(newpath, new_str);
+ pc += strlen(old_str);
+ return (strcat(newpath, pc));
+}
+
+/*
+ * Set "token" to be the the string starting from the pointer "str" delimited
+ * by any character in the string "delim" or the end of the string, but IGNORE
+ * any characters between single or double quotes.
+ *
+ * Return a pointer to the next non-whitespace character after the delimiter
+ * or NULL if we hit the end of the string. Also return NULL upon failure to
+ * find any characters from the delimeter string or upon failure to allocate
+ * memory for the new token string.
+ */
+static char *
+get_token(char **token, char *str, char *delim)
+{
+ char *dp;
+ char *start = str;
+ unsigned len;
+
+ *token = NULL;
+
+ if ((str == NULL) || (*str == NULL))
+ return (NULL);
+
+ do {
+ if ((*str == '\'') || (*str == '"')) {
+ char quote = *str++;
+
+ while ((*str != NULL) && (*str != quote))
+ str++;
+
+ /* no matching quote found in string */
+ if (*str++ == NULL)
+ return (NULL);
+ }
+
+ /* look for a character from the delimiter string */
+ for (dp = delim; ((*dp != NULL) && (*dp != *str)); dp++)
+ ;
+
+ if (*dp != NULL) {
+ len = str - start + 1;
+
+ /* found a delimiter, so create a token string */
+ if ((*token = malloc(len)) == NULL) {
+ bam_error(NO_MEM, len);
+ return (NULL);
+ }
+
+ (void) strlcpy(*token, start, len);
+
+ while (isspace((int)*++str))
+ ;
+
+ return (str);
+ }
+ } while (*str++ != NULL);
+
+ /* if we hit the end of the string, the token is the whole string */
+ *token = s_strdup(start);
+ return (NULL);
+}
+
+/*
+ * Convert a metal "console" device name to an equivalent one suitable for
+ * use with the hypervisor.
+ *
+ * Default to "vga" if we can't parse the console device.
+ */
+static void
+console_metal_to_hyper(char *console)
+{
+ if ((*console == '\'') || (*console == '"'))
+ console++;
+
+ if (strncmp(console, "ttya", 4) == 0)
+ console_dev = "console=com1";
+ else if (strncmp(console, "ttyb", 4) == 0)
+ console_dev = "console=com2";
+ else
+ console_dev = "console=vga";
+}
+
+static int
+set_serial_rate(int com, char *rate)
+{
+ char **rp = &serial_config[com - 1];
+
+ /*
+ * If rate is a NULL pointer, erase any existing serial configuration
+ * for this serial port.
+ */
+ if (rate == NULL) {
+ if (*rp != NULL) {
+ free(*rp);
+ *rp = NULL;
+ }
+ return (0);
+ }
+
+ *rp = s_realloc(*rp, strlen(rate));
+ (void) strcpy(*rp, rate);
+ return (0);
+}
+
+/*
+ * Convert "metal" serial port parameters to values compatible with the
+ * hypervisor.
+ *
+ * Return 0 on success, otherwise -1.
+ */
+static int
+serial_metal_to_hyper(char *metal_port, char *metal_serial)
+{
+#define COM_RATE_LEN 16 /* strlen("com1=115200,8n1") */
+
+ char com_rate[COM_RATE_LEN];
+
+ unsigned com, baud, bits, stop;
+ char parity, handshake;
+
+ if ((strcmp(metal_port, "ttya-mode") == 0) ||
+ (strcmp(metal_port, "ttyb-mode") == 0))
+ com = TTYXMODE_TO_COMNUM(metal_port);
+ else
+ return (-1);
+
+ if ((*metal_serial == '\'') || (*metal_serial == '"'))
+ metal_serial++;
+
+ /*
+ * Check if it's specified as the default rate; if so it defaults to
+ * "auto" and we need not set it for they hypervisor.
+ */
+ if (strncmp(metal_serial, DEFAULT_SERIAL,
+ strlen(DEFAULT_SERIAL)) == 0) {
+ (void) set_serial_rate(com, NULL);
+ return (0);
+ }
+
+ /* read the serial port format as set forth in common/io/asy.c */
+ if (sscanf(metal_serial, "%u,%u,%c,%u,%c", &baud, &bits, &parity, &stop,
+ &handshake) != 5)
+ return (-1);
+
+ /* validate serial port parameters */
+ if (((bits < 5) || (bits > 8)) || (stop > 1) ||
+ ((parity != 'n') && (parity != 'e') && (parity != 'o')) ||
+ ((handshake != '-') && (handshake != 'h') && (handshake != 's')))
+ return (-1);
+
+ /* validate baud rate */
+ switch (baud) {
+ case 150:
+ case 300:
+ case 600:
+ case 1200:
+ case 2400:
+ case 4800:
+ case 9600:
+ case 19200:
+ case 38400:
+ case 57600:
+ case 115200:
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /*
+ * The hypervisor has no way to specify a handshake method, so it gets
+ * quietly dropped in the conversion.
+ */
+ (void) snprintf(com_rate, COM_RATE_LEN, "com%d=%u,%u%c%u", com, baud,
+ bits, parity, stop);
+ (void) set_serial_rate(com, com_rate);
+ return (0);
+}
+
+/*
+ * Convert bootenv.rc boot property strings of the form:
+ *
+ * setprop property value
+ *
+ * into boot option lines suitable for use with the hypervisor.
+ *
+ * Our main concerns are the console device and serial port settings.
+ *
+ * Return values:
+ *
+ * -1: Unparseable line
+ * 0: Success
+ * (n > 0): A property unimportant to us
+ */
+static int
+cvt_bootprop(char *propstr)
+{
+ char *parsestr, *port, *token;
+
+ int retval = 0;
+
+ /* get initial "setprop" */
+ if ((parsestr = get_token(&token, propstr, " \t")) == NULL) {
+ if (token != NULL)
+ free(token);
+
+ return (-1);
+ }
+
+ if (strcmp(token, "setprop")) {
+ free(token);
+ return (1);
+ }
+
+ free(token);
+
+ /* get property name */
+ if ((parsestr = get_token(&token, parsestr, " \t")) == NULL) {
+ if (token != NULL)
+ free(token);
+
+ return (-2);
+ }
+
+ if (strcmp(token, "console") == 0) {
+ free(token);
+
+ /* get console property value */
+ parsestr = get_token(&token, parsestr, " \t");
+ if (token == NULL)
+ return (-3);
+
+ console_metal_to_hyper(token);
+ free(token);
+ return (0);
+ }
+
+ /* check if it's a serial port setting */
+ if ((strcmp(token, "ttya-mode") == 0) ||
+ (strcmp(token, "ttyb-mode") == 0)) {
+ port = token;
+ } else {
+ free(token);
+ return (3);
+ }
+
+ /* get serial port setting */
+ parsestr = get_token(&token, parsestr, " \t");
+
+ if (token == NULL) {
+ free(port);
+ return (-1);
+ }
+
+ retval = serial_metal_to_hyper(port, token);
+
+ free(port);
+ free(token);
+ return (retval);
+}
+
+/*
+ * Convert "name=value" metal options to values suitable for use with the
+ * hypervisor.
+ *
+ * Our main concerns are the console device and serial port settings.
+ *
+ * Return values:
+ *
+ * -1: Unparseable line
+ * 0: Success
+ * (n > 0): A property unimportant to us
+ */
+static int
+cvt_metal_option(char *optstr)
+{
+ char *value;
+ unsigned namlen;
+
+ if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
+ zfs_boot = 1;
+ return (0);
+ }
+
+ if ((value = strchr(optstr, '=')) == NULL)
+ return (-1);
+
+ namlen = value - optstr;
+
+ if (*++value == NULL)
+ return (1);
+
+ if (strncmp(optstr, "console", namlen) == 0) {
+ console_metal_to_hyper(value);
+ return (0);
+ }
+
+ if ((strncmp(optstr, "ttya-mode", namlen) == 0) ||
+ (strncmp(optstr, "ttyb-mode", namlen) == 0)) {
+ char *port = alloca(namlen + 1);
+
+ (void) strlcpy(port, optstr, namlen);
+ return (serial_metal_to_hyper(port, value));
+ }
+
+ return (1);
+}
+
+/*
+ * Convert "name=value" properties for use with a bare metal kernel
+ *
+ * Our main concerns are the console setting and serial port modes.
+ *
+ * Return values:
+ *
+ * -1: Unparseable line
+ * 0: Success
+ * (n > 0): A property unimportant to us
+ */
+static int
+cvt_hyper_option(char *optstr)
+{
+#define SER_LEN 27 /* strlen("ttyb-mode='115200,8,n,1,-'") */
+
+ char ser[SER_LEN];
+ char *value;
+
+ unsigned namlen;
+
+ unsigned baud;
+ char bits, parity, stop;
+
+ if (strcmp(optstr, ZFS_BOOTSTR) == 0) {
+ zfs_boot = 1;
+ return (0);
+ }
+
+ /*
+ * If there's no "=" in the token, it's likely a standalone
+ * hypervisor token we don't care about (e.g. "noreboot" or
+ * "nosmp") so we ignore it.
+ */
+ if ((value = strchr(optstr, '=')) == NULL)
+ return (1);
+
+ namlen = value - optstr;
+
+ if (*++value == NULL)
+ return (1);
+
+ /*
+ * Note that we use strncmp against the values because the
+ * hypervisor allows setting console parameters for both the
+ * console and debugger via the format:
+ *
+ * console=cons_dev,debug_dev
+ *
+ * and we only care about "cons_dev."
+ *
+ * This also allows us to extract "comN" from hypervisor constructs
+ * like "com1H" or "com2L," concepts unsupported on bare metal kernels.
+ *
+ * Default the console device to "text" if it was "vga" or was
+ * unparseable.
+ */
+ if (strncmp(optstr, "console", namlen) == 0) {
+ /* ignore the "console=hypervisor" option */
+ if (strcmp(value, "hypervisor") == 0)
+ return (0);
+
+ if (strncmp(value, "com1", 4) == 0)
+ console_dev = "console=ttya";
+ else if (strncmp(value, "com2", 4) == 0)
+ console_dev = "console=ttyb";
+ else
+ console_dev = "console=text";
+ }
+
+ /* serial port parameter conversion */
+
+ if ((strncmp(optstr, "com1", namlen) == 0) ||
+ (strncmp(optstr, "com2", namlen) == 0)) {
+ unsigned com = COMNAME_TO_COMNUM(optstr);
+
+ /*
+ * Check if it's "auto" - if so, use the default setting
+ * of "9600,8,n,1,-".
+ *
+ * We can't just assume the serial port will default to
+ * "9600,8,n,1" as there could be a directive in bootenv.rc
+ * that would set it to some other value and we want the serial
+ * parameters to be the same as that used by the hypervisor.
+ */
+ if (strcmp(value, "auto") == 0) {
+ (void) snprintf(ser, SER_LEN,
+ "tty%c-mode='9600,8,n,1,-'", '`' + com);
+
+ if (set_serial_rate(com, ser) != 0)
+ return (-1);
+
+ return (0);
+ }
+
+ /*
+ * Extract the "B,PS" setting from the com line; ignore other
+ * settings like io_base or IRQ.
+ */
+ if (sscanf(value, "%u,%c%c%c", &baud, &bits, &parity,
+ &stop) != 4)
+ return (-1);
+
+ /* validate serial port parameters */
+ if (((stop != '0') && (stop != '1')) ||
+ ((bits < '5') && (bits > '8')) ||
+ ((parity != 'n') && (parity != 'e') && (parity != 'o')))
+ return (-1);
+
+ /* validate baud rate */
+ switch (baud) {
+ case 150:
+ case 300:
+ case 600:
+ case 1200:
+ case 2400:
+ case 4800:
+ case 19200:
+ case 38400:
+ case 57600:
+ case 115200:
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /*
+ * As the hypervisor has no way to denote handshaking in its
+ * serial port settings, emit a metal serial port configuration
+ * with none as well.
+ */
+ (void) snprintf(ser, SER_LEN, "tty%c-mode='%u,%c,%c,%c,-'",
+ '`' + com, baud, bits, parity, stop);
+
+ if (set_serial_rate(com, ser) != 0)
+ return (-1);
+
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Parse a hardware kernel's "kernel$" specifier into parameters we can then
+ * use to construct an appropriate "module$" line that can be used to specify
+ * how to boot the hypervisor's dom0.
+ *
+ * Return 0 on success, non-zero on failure.
+ */
+static int
+cvt_metal_kernel(char *kernstr, char **path)
+{
+ char *token, *parsestr;
+
+ if ((parsestr = get_token(path, kernstr, " \t,")) == NULL)
+ return (*path == NULL);
+
+ /*
+ * If the metal kernel specified contains the name of the hypervisor,
+ * we're probably trying to convert an entry already setup to run the
+ * hypervisor, so error out now.
+ */
+ if (strstr(*path, XEN_MENU) != NULL) {
+ bam_error(ALREADY_HYPER);
+ return (-1);
+ }
+
+ /* if the path was the last item on the line, that's OK. */
+ if ((parsestr = get_token(&token, parsestr, " \t,")) == NULL) {
+ if (token != NULL)
+ free(token);
+ return (0);
+ }
+
+ /* if the next token is "-B" process boot options */
+ if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
+ free(token);
+ return (0);
+ }
+
+ while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
+ (void) cvt_metal_option(token);
+ free(token);
+ }
+
+ if (token != NULL) {
+ (void) cvt_metal_option(token);
+ free(token);
+ }
+
+ return (0);
+}
+
+/*
+ * Parse a hypervisor's "kernel$" line into parameters that can be used to
+ * help build an appropriate "kernel$" line for booting a bare metal kernel.
+ *
+ * Return 0 on success, non-zero on failure.
+ */
+static int
+cvt_hyper_kernel(char *kernel)
+{
+ char *token, *parsestr;
+
+ /* eat the kernel path, which should be the first token */
+ if ((parsestr = get_token(&token, kernel, " \t,")) == NULL)
+ return (0);
+
+ /*
+ * If the hypervisor kernel specified lives in the metal kernel
+ * directory, we're probably trying to convert an entry already setup
+ * to run on bare metal, so error out now.
+ */
+ if (strncmp(token, METAL_KERNEL_DIR, strlen(METAL_KERNEL_DIR)) == 0) {
+ bam_error(ALREADY_METAL);
+ (void) free(token);
+ return (-1);
+ }
+
+ /* if the path was the last item on the line, that's OK. */
+ free(token);
+
+ /* check for kernel options */
+ while ((parsestr = get_token(&token, parsestr, " ")) != NULL) {
+ (void) cvt_hyper_option(token);
+ free(token);
+ }
+
+ if (token != NULL) {
+ (void) cvt_hyper_option(token);
+ free(token);
+ }
+
+ return (0);
+}
+
+/*
+ * Parse a hypervisor's "module$" line into parameters that can be used to
+ * help build an appropriate "kernel$" line for booting a bare metal kernel.
+ */
+static void
+cvt_hyper_module(char *modstr, char **path)
+{
+ char *token;
+ char *parsestr = modstr;
+
+ /*
+ * If multiple pathnames exist on the module$ line, we just want
+ * the last one.
+ */
+ while ((parsestr = get_token(path, parsestr, " \t,")) != NULL) {
+ if (*parsestr != '/')
+ break;
+
+ free(*path);
+ }
+
+ /* if the path was the last item on the line, that's OK. */
+ if ((parsestr == NULL) ||
+ ((parsestr = get_token(&token, parsestr, " \t,")) == NULL)) {
+ if (token != NULL)
+ free(token);
+ return;
+ }
+
+ /* check for "-B" option */
+ if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) {
+ free(token);
+ return;
+ }
+
+ /* check for kernel options */
+ while ((parsestr = get_token(&token, parsestr, ",")) != NULL) {
+ (void) cvt_hyper_option(token);
+ free(token);
+ }
+
+ if (token != NULL) {
+ (void) cvt_hyper_option(token);
+ free(token);
+ }
+}
+
+static void
+parse_bootenvrc(char *osroot)
+{
+#define LINEBUF_SZ 1024
+
+ FILE *fp;
+ char *rcpath;
+ char line[LINEBUF_SZ]; /* make line buffer large but not ridiculous */
+ int len;
+
+ assert(osroot);
+
+ len = strlen(osroot) + strlen(BOOTRC_FILE) + 1;
+ rcpath = alloca(len);
+ (void) snprintf(rcpath, len, "%s%s", osroot, BOOTRC_FILE);
+
+ /* if we couldn't open the bootenv.rc file, ignore the issue. */
+ if ((fp = fopen(rcpath, "r")) == NULL) {
+ BAM_DPRINTF((D_NO_BOOTENVRC, rcpath, strerror(errno)));
+ return;
+ }
+
+ while (s_fgets(line, LINEBUF_SZ, fp) != NULL) {
+ /* we're only interested in parsing "setprop" directives. */
+ if (strncmp(line, "setprop", 7) != NULL)
+ continue;
+
+ (void) cvt_bootprop(line);
+ }
+
+ (void) fclose(fp);
+}
+
+error_t
+cvt_to_hyper(menu_t *mp, char *osroot, char *extra_args)
+{
+ const char *fcn = "cvt_to_hyper()";
+
+ line_t *lp;
+ entry_t *ent;
+ size_t len, zfslen;
+
+ char *osdev;
+
+ char *title = NULL;
+ char *findroot = NULL;
+ char *bootfs = NULL;
+ char *kernel = NULL;
+ char *mod_kernel = NULL;
+ char *module = NULL;
+
+ char *kern_path = NULL;
+ char *kern_bargs = NULL;
+
+ int curdef;
+ int kp_allocated = 1;
+ int ret = BAM_ERROR;
+
+ assert(osroot);
+
+ BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, extra_args));
+
+ /*
+ * First just check to verify osroot is a sane directory.
+ */
+ if ((osdev = get_special(osroot)) == NULL) {
+ bam_error(CANT_FIND_SPECIAL, osroot);
+ return (BAM_ERROR);
+ }
+
+ free(osdev);
+
+ /*
+ * While the effect is purely cosmetic, if osroot is "/" don't
+ * bother prepending it to any paths as they are constructed to
+ * begin with "/" anyway.
+ */
+ if (strcmp(osroot, "/") == 0)
+ osroot = "";
+
+ /*
+ * Found the GRUB signature on the target partitions, so now get the
+ * default GRUB boot entry number from the menu.lst file
+ */
+ curdef = atoi(mp->curdefault->arg);
+
+ /* look for the first line of the matching boot entry */
+ for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
+ ent = ent->next)
+ ;
+
+ /* couldn't find it, so error out */
+ if (ent == NULL) {
+ bam_error(CANT_FIND_DEFAULT, curdef);
+ goto abort;
+ }
+
+ /*
+ * We found the proper menu entry, so first we need to process the
+ * bootenv.rc file to look for boot options the hypervisor might need
+ * passed as kernel start options such as the console device and serial
+ * port parameters.
+ *
+ * If there's no bootenv.rc, it's not an issue.
+ */
+ parse_bootenvrc(osroot);
+
+ /*
+ * Now process the entry itself.
+ */
+ for (lp = ent->start; lp != NULL; lp = lp->next) {
+ /*
+ * Process important lines from menu.lst boot entry.
+ */
+ if (lp->flags == BAM_TITLE) {
+ title = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(title, lp->arg);
+ } else if (strcmp(lp->cmd, "findroot") == 0) {
+ findroot = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(findroot, lp->arg);
+ } else if (strcmp(lp->cmd, "bootfs") == 0) {
+ bootfs = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(bootfs, lp->arg);
+ } else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
+ module = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(module, lp->arg);
+ } else if ((strcmp(lp->cmd,
+ menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
+ (cvt_metal_kernel(lp->arg, &kern_path) < 0)) {
+ ret = BAM_NOCHANGE;
+ goto abort;
+ }
+
+ if (lp == ent->end)
+ break;
+ }
+
+ /*
+ * If findroot, module or kern_path are NULL, boot entry was malformed
+ */
+ if (findroot == NULL) {
+ bam_error(FINDROOT_NOT_FOUND, curdef);
+ goto abort;
+ }
+
+ if (module == NULL) {
+ bam_error(MODULE_NOT_PARSEABLE, curdef);
+ goto abort;
+ }
+
+ if (kern_path == NULL) {
+ bam_error(KERNEL_NOT_FOUND, curdef);
+ goto abort;
+ }
+
+ /* assemble new kernel and module arguments from parsed values */
+ if (console_dev != NULL) {
+ kern_bargs = s_strdup(console_dev);
+
+ if (serial_config[0] != NULL)
+ kern_bargs = append_str(kern_bargs, serial_config[0],
+ " ");
+
+ if (serial_config[1] != NULL)
+ kern_bargs = append_str(kern_bargs, serial_config[1],
+ " ");
+ }
+
+ if ((extra_args != NULL) && (*extra_args != NULL))
+ kern_bargs = append_str(kern_bargs, extra_args, " ");
+
+ len = strlen(osroot) + strlen(XEN_MENU) + strlen(kern_bargs) +
+ WHITESPC(1) + 1;
+
+ kernel = alloca(len);
+
+ if ((kern_bargs != NULL) && (*kern_bargs != NULL)) {
+ (void) snprintf(kernel, len, "%s%s %s", osroot, XEN_MENU,
+ kern_bargs);
+ free(kern_bargs);
+ } else {
+ (void) snprintf(kernel, len, "%s%s", osroot, XEN_MENU);
+ }
+
+ /*
+ * Change the kernel directory from the metal version to that needed for
+ * the hypervisor. Convert either "direct boot" path to the default
+ * path.
+ */
+ if ((strcmp(kern_path, DIRECT_BOOT_32) == 0) ||
+ (strcmp(kern_path, DIRECT_BOOT_64) == 0)) {
+ kern_path = HYPERVISOR_KERNEL;
+ kp_allocated = 0;
+ } else {
+ kern_path = modify_path(kern_path, METAL_KERNEL_DIR,
+ HYPER_KERNEL_DIR);
+ }
+
+ /*
+ * We need to allocate space for the kernel path (twice) plus an
+ * intervening space, possibly the ZFS boot string, and NULL,
+ * of course.
+ */
+ len = (strlen(kern_path) * 2) + WHITESPC(1) + 1;
+ zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
+
+ mod_kernel = alloca(len + zfslen);
+
+ (void) snprintf(mod_kernel, len, "%s %s", kern_path, kern_path);
+
+ if (kp_allocated)
+ free(kern_path);
+
+ if (zfs_boot) {
+ char *zfsstr = alloca(zfslen + 1);
+
+ (void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
+ (void) strcat(mod_kernel, zfsstr);
+ }
+
+ /* shut off warning messages from the entry line parser */
+ if (ent->flags & BAM_ENTRY_BOOTADM)
+ ent->flags &= ~BAM_ENTRY_BOOTADM;
+
+ BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
+ BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, mod_kernel));
+
+ /*
+ * Now try to delete the current default entry from the menu and add
+ * the new hypervisor entry with the parameters we've setup.
+ */
+ if (delete_boot_entry(mp, curdef, DBE_QUIET) != BAM_SUCCESS)
+ bam_print(NEW_BOOT_ENTRY, title);
+
+ curdef = add_boot_entry(mp, title, findroot, kernel, mod_kernel,
+ module, bootfs);
+
+ /*
+ * If we successfully created the new entry, set the default boot
+ * entry to that entry and let the caller know the new menu should
+ * be written out.
+ */
+ if (curdef != BAM_ERROR)
+ return (set_global(mp, menu_cmds[DEFAULT_CMD], curdef));
+
+ return (BAM_ERROR);
+
+abort:
+ if ((kp_allocated) && (kern_path != NULL))
+ free(kern_path);
+
+ if (ret != BAM_NOCHANGE)
+ bam_error(HYPER_ABORT, ((*osroot == NULL) ? "/" : osroot));
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+error_t
+cvt_to_metal(menu_t *mp, char *osroot, char *menu_root)
+{
+ const char *fcn = "cvt_to_metal()";
+
+ line_t *lp;
+ entry_t *ent;
+ size_t len, zfslen;
+
+ char *delim = ", ";
+ char *osdev;
+
+ char *title = NULL;
+ char *findroot = NULL;
+ char *bootfs = NULL;
+ char *kernel = NULL;
+ char *module = NULL;
+
+ char *barchive_path = DIRECT_BOOT_ARCHIVE;
+ char *kern_path = NULL;
+
+ int curdef;
+ int emit_bflag = 1;
+ int ret = BAM_ERROR;
+
+ assert(osroot);
+
+ BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, ""));
+
+ /*
+ * First just check to verify osroot is a sane directory.
+ */
+ if ((osdev = get_special(osroot)) == NULL) {
+ bam_error(CANT_FIND_SPECIAL, osroot);
+ return (BAM_ERROR);
+ }
+
+ free(osdev);
+
+ /*
+ * Found the GRUB signature on the target partitions, so now get the
+ * default GRUB boot entry number from the menu.lst file
+ */
+ curdef = atoi(mp->curdefault->arg);
+
+ /* look for the first line of the matching boot entry */
+ for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef));
+ ent = ent->next)
+ ;
+
+ /* couldn't find it, so error out */
+ if (ent == NULL) {
+ bam_error(CANT_FIND_DEFAULT, curdef);
+ goto abort;
+ }
+
+ /*
+ * Now process the entry itself.
+ */
+ for (lp = ent->start; lp != NULL; lp = lp->next) {
+ /*
+ * Process important lines from menu.lst boot entry.
+ */
+ if (lp->flags == BAM_TITLE) {
+ title = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(title, lp->arg);
+ } else if (strcmp(lp->cmd, "findroot") == 0) {
+ findroot = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(findroot, lp->arg);
+ } else if (strcmp(lp->cmd, "bootfs") == 0) {
+ bootfs = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(bootfs, lp->arg);
+ } else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) {
+ if (strstr(lp->arg, "boot_archive") == NULL) {
+ module = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(module, lp->arg);
+ cvt_hyper_module(module, &kern_path);
+ } else {
+ barchive_path = alloca(strlen(lp->arg) + 1);
+ (void) strcpy(barchive_path, lp->arg);
+ }
+ } else if ((strcmp(lp->cmd,
+ menu_cmds[KERNEL_DOLLAR_CMD]) == 0) &&
+ (cvt_hyper_kernel(lp->arg) < 0)) {
+ ret = BAM_NOCHANGE;
+ goto abort;
+ }
+
+ if (lp == ent->end)
+ break;
+ }
+
+ /*
+ * If findroot, module or kern_path are NULL, boot entry was malformed
+ */
+ if (findroot == NULL) {
+ bam_error(FINDROOT_NOT_FOUND, curdef);
+ goto abort;
+ }
+
+ if (module == NULL) {
+ bam_error(MODULE_NOT_PARSEABLE, curdef);
+ goto abort;
+ }
+
+ if (kern_path == NULL) {
+ bam_error(KERNEL_NOT_FOUND, curdef);
+ goto abort;
+ }
+
+ /*
+ * Assemble new kernel and module arguments from parsed values.
+ *
+ * First, change the kernel directory from the hypervisor version to
+ * that needed for a metal kernel.
+ */
+ kern_path = modify_path(kern_path, HYPER_KERNEL_DIR, METAL_KERNEL_DIR);
+
+ /* allocate initial space for the kernel path */
+ len = strlen(kern_path) + 1;
+ zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0);
+
+ if ((kernel = malloc(len + zfslen)) == NULL) {
+ free(kern_path);
+ bam_error(NO_MEM, len);
+ goto abort;
+ }
+
+ (void) snprintf(kernel, len, "%s", kern_path);
+ free(kern_path);
+
+ if (zfs_boot) {
+ char *zfsstr = alloca(zfslen + 1);
+
+ (void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT);
+ (void) strcat(kernel, zfsstr);
+ emit_bflag = 0;
+ }
+
+ if (console_dev != NULL) {
+ if (emit_bflag) {
+ kernel = append_str(kernel, BFLAG, " ");
+ kernel = append_str(kernel, console_dev, " ");
+ emit_bflag = 0;
+ } else {
+ kernel = append_str(kernel, console_dev, ", ");
+ }
+ }
+
+ /*
+ * We have to do some strange processing here because the
+ * hypervisor's serial ports default to "9600,8,n,1,-" if
+ * "comX=auto" is specified, or to "auto" if nothing is
+ * specified.
+ *
+ * Since there could be entries in the bootenv.rc file that
+ * set the serial port to some other setting, when converting
+ * a hypervisor entry to a metal entry we must force the
+ * serial ports to their defaults.
+ */
+
+ if (emit_bflag) {
+ kernel = append_str(kernel, BFLAG, " ");
+ delim = " ";
+ emit_bflag = 0;
+ }
+
+ if (serial_config[0] != NULL)
+ kernel = append_str(kernel, serial_config[0], delim);
+ else
+ kernel = append_str(kernel, "ttya-mode='9600,8,n,1,-'", delim);
+
+ if (serial_config[1] != NULL)
+ kernel = append_str(kernel, serial_config[1], ", ");
+ else
+ kernel = append_str(kernel, "ttyb-mode='9600,8,n,1,-'", ", ");
+
+ /* shut off warning messages from the entry line parser */
+ if (ent->flags & BAM_ENTRY_BOOTADM)
+ ent->flags &= ~BAM_ENTRY_BOOTADM;
+
+ BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel));
+ BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, module));
+
+ /*
+ * Now try to delete the current default entry from the menu and add
+ * the new hypervisor entry with the parameters we've setup.
+ */
+ if (delete_boot_entry(mp, curdef, DBE_QUIET) != BAM_SUCCESS)
+ bam_print(NEW_BOOT_ENTRY, title);
+
+ curdef = add_boot_entry(mp, title, findroot, kernel, NULL,
+ barchive_path, bootfs);
+
+ /*
+ * If we successfully created the new entry, set the default boot
+ * entry to that entry and let the caller know the new menu should
+ * be written out.
+ */
+ if (curdef != BAM_ERROR)
+ return (set_global(mp, menu_cmds[DEFAULT_CMD], curdef));
+
+ return (BAM_ERROR);
+
+abort:
+ if (ret != BAM_NOCHANGE)
+ bam_error(METAL_ABORT, osroot);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/boot/bootadm/bootadm_upgrade.c b/usr/src/cmd/boot/bootadm/bootadm_upgrade.c
index 0caaa43db7..ddf28c4a25 100644
--- a/usr/src/cmd/boot/bootadm/bootadm_upgrade.c
+++ b/usr/src/cmd/boot/bootadm/bootadm_upgrade.c
@@ -782,7 +782,7 @@ bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
}
(void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU,
- XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE);
+ XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL);
BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn));
diff --git a/usr/src/cmd/boot/bootadm/message.h b/usr/src/cmd/boot/bootadm/message.h
index 79f8fc652b..2c14f63753 100644
--- a/usr/src/cmd/boot/bootadm/message.h
+++ b/usr/src/cmd/boot/bootadm/message.h
@@ -88,8 +88,12 @@ extern "C" {
#define NO_MEM gettext("could not allocate memory: size = %u\n")
+#define NO_SPARC gettext("%s operation unsupported on SPARC machines\n")
+
#define CANNOT_LOCATE_GRUB_MENU gettext("cannot find GRUB menu\n")
+#define CANNOT_LOCATE_GRUB_MENU_FILE gettext("cannot find GRUB menu file: %s\n")
+
#define GRUB_MENU_PATH gettext("the location for the active GRUB menu is: %s\n")
#define STUBBOOT_DIR_NOT_FOUND gettext("cannot find stubboot directory\n")
@@ -285,12 +289,24 @@ extern "C" {
#define DEFAULT_NOT_BAM gettext( \
"Default /boot/grub/menu.lst entry is not controlled by bootadm. Exiting\n")
+#define CANT_FIND_DEFAULT \
+gettext("unable to find default boot entry (%d) in menu.lst file.\n")
+
#define UNKNOWN_KERNEL_LINE \
gettext("kernel command on line %d not recognized.\n")
#define UNKNOWN_MODULE_LINE \
gettext("module command on line %d not recognized.\n")
+#define FINDROOT_NOT_FOUND \
+gettext("findroot in default boot entry (%d) missing.\n")
+
+#define KERNEL_NOT_FOUND \
+gettext("kernel$ in default boot entry (%d) missing.\n")
+
+#define MODULE_NOT_PARSEABLE \
+gettext("module$ in default boot entry (%d) missing or not parseable.\n")
+
#define NOT_ELF_FILE gettext("%s is not an ELF file.\n")
#define WRONG_ELF_CLASS gettext("%s is wrong ELF class 0x%x\n")
@@ -419,6 +435,20 @@ For details on manually updating entries, see %s\n")
gettext("error upgrading GRUB menu entries on %s. Aborting.\n\
For details on manually updating entries, see %s\n")
+#define ALREADY_HYPER \
+gettext("default entry already setup for use with the hypervisor!\n")
+
+#define HYPER_ABORT \
+gettext("error converting GRUB menu entry on %s for use with the hypervisor.\n\
+Aborting.\n")
+
+#define ALREADY_METAL \
+gettext("default entry already setup for use with a metal kernel!\n")
+
+#define METAL_ABORT \
+gettext("error converting GRUB menu entry on %s for use with a metal kernel.\n\
+Aborting.\n")
+
#define HAND_ADDED_ENTRIES \
gettext("bootadm(1M) will only upgrade GRUB menu entries added by \n\
bootadm(1M) or lu(1M). The following entries on %s will not be upgraded.\n\
@@ -663,6 +693,9 @@ gettext("failed to get special file for menu_root: %s\n")
#define UPDT_CACHE_FAIL gettext("directory cache update failed for %s\n")
+#define NEW_BOOT_ENTRY \
+ gettext("unable to modify default entry; creating new boot entry for %s\n")
+
/*
* NOTE: The following are debug messages and not I18Ned
*/
@@ -966,6 +999,8 @@ gettext("failed to get special file for menu_root: %s\n")
#define D_CVT_CMD_KERN_DOLLAR "%s: converted kernel cmd to %s\n"
+#define D_CVT_CMD_MOD_DOLLAR "%s: converted module cmd to %s\n"
+
#define D_FLAGS1_UNIX_FLAGS2_NULL "%s: NULL flags1, unix, flags2\n"
#define D_UNIX_PRESENT "%s: unix present\n"
@@ -1114,6 +1149,8 @@ gettext("failed to get special file for menu_root: %s\n")
#define D_IS_CHAINLOADER_CMD "%s: setting CHAINLOADER: %s\n"
+#define D_NO_BOOTENVRC "could not open %s: %s\n"
+
#define D_ADD_FINDROOT_NUM "%s: findroot added: line#: %d: entry#: %d\n"
#define D_FREEING_LINE "%s: freeing line: %d\n"