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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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