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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <errno.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <limits.h> 36 #include <fcntl.h> 37 #include <strings.h> 38 39 #include <sys/mman.h> 40 #include <sys/elf.h> 41 #include <sys/multiboot.h> 42 43 #include "message.h" 44 #include "bootadm.h" 45 46 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET; 47 hv_t bam_is_hv = BAM_HV_UNKNOWN; 48 49 error_t 50 dboot_or_multiboot(const char *root) 51 { 52 char fname[PATH_MAX]; 53 char *image; 54 uchar_t *ident; 55 int fd, m; 56 multiboot_header_t *mbh; 57 struct stat sb; 58 59 if (!is_grub(root)) { 60 /* there is no non dboot sparc new-boot */ 61 bam_direct = BAM_DIRECT_DBOOT; 62 return (BAM_SUCCESS); 63 } 64 65 (void) snprintf(fname, PATH_MAX, "%s/%s", root, 66 "platform/i86pc/kernel/unix"); 67 fd = open(fname, O_RDONLY); 68 if (fd < 0) { 69 bam_error(OPEN_FAIL, fname, strerror(errno)); 70 return (BAM_ERROR); 71 } 72 73 /* 74 * mmap the first 8K 75 */ 76 image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0); 77 if (image == MAP_FAILED) { 78 bam_error(MMAP_FAIL, fname, strerror(errno)); 79 return (BAM_ERROR); 80 } 81 82 ident = (uchar_t *)image; 83 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || 84 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { 85 bam_error(NOT_ELF_FILE, fname); 86 return (BAM_ERROR); 87 } 88 if (ident[EI_CLASS] != ELFCLASS32) { 89 bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]); 90 return (BAM_ERROR); 91 } 92 93 /* 94 * The GRUB multiboot header must be 32-bit aligned and completely 95 * contained in the 1st 8K of the file. If the unix binary has 96 * a multiboot header, then it is a 'dboot' kernel. Otherwise, 97 * this kernel must be booted via multiboot -- we call this a 98 * 'multiboot' kernel. 99 */ 100 bam_direct = BAM_DIRECT_MULTIBOOT; 101 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) { 102 mbh = (void *)(image + m); 103 if (mbh->magic == MB_HEADER_MAGIC) { 104 bam_direct = BAM_DIRECT_DBOOT; 105 break; 106 } 107 } 108 (void) munmap(image, 8192); 109 (void) close(fd); 110 111 if (bam_direct == BAM_DIRECT_DBOOT) { 112 (void) snprintf(fname, PATH_MAX, "%s/%s", root, XEN_32); 113 if (stat(fname, &sb) == 0) { 114 bam_is_hv = BAM_HV_PRESENT; 115 } else { 116 bam_is_hv = BAM_HV_NO; 117 } 118 } 119 120 return (BAM_SUCCESS); 121 } 122 123 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE" 124 125 /* 126 * Return true if root has been bfu'ed. bfu will blow away 127 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can 128 * assume the system has not been bfu'ed. 129 */ 130 static int 131 is_bfu_system(const char *root) 132 { 133 static int is_bfu = -1; 134 char path[PATH_MAX]; 135 struct stat sb; 136 137 if (is_bfu != -1) 138 return (is_bfu); 139 140 (void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE); 141 if (stat(path, &sb) != 0) { 142 is_bfu = 1; 143 } else { 144 is_bfu = 0; 145 } 146 return (is_bfu); 147 } 148 149 #define MENU_URL(root) (is_bfu_system(root) ? \ 150 "http://www.sun.com/msg/SUNOS-8000-CF" : \ 151 "http://www.sun.com/msg/SUNOS-8000-AK") 152 153 /* 154 * Simply allocate a new line and copy in cmd + sep + arg 155 */ 156 void 157 update_line(line_t *linep) 158 { 159 size_t size; 160 161 free(linep->line); 162 size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1; 163 linep->line = s_calloc(1, size); 164 (void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep, 165 linep->arg); 166 } 167 168 /* 169 * The parse_kernel_line function examines a menu.lst kernel line. For 170 * multiboot, this is: 171 * 172 * kernel <multiboot path> <flags1> <kernel path> <flags2> 173 * 174 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot 175 * 176 * <kernel path> may be missing, or may be any full or relative path to unix. 177 * We check for it by looking for a word ending in "/unix". If it ends 178 * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in 179 * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise, 180 * it's a custom kernel, and we skip it. 181 * 182 * <flags*> are anything that doesn't fit either of the above - these will be 183 * copied over. 184 * 185 * For direct boot, the defaults are 186 * 187 * kernel$ <kernel path> <flags> 188 * 189 * <kernel path> is one of: 190 * /platform/i86pc/kernel/$ISADIR/unix 191 * /platform/i86pc/kernel/unix 192 * /platform/i86pc/kernel/amd64/unix 193 * /boot/platform/i86pc/kernel/unix 194 * 195 * If <kernel path> is any of the last three, the command may also be "kernel". 196 * 197 * <flags> is anything that isn't <kernel path>. 198 * 199 * This function is only called if it applies to our target boot environment. 200 * If we can't make any sense of the kernel line, an error is printed and 201 * BAM_ERROR is returned. 202 * 203 * The desired install type is given in the global variable bam_direct. 204 * If the kernel line is of a different install type, we change it to the 205 * preferred type. If the kernel line is already of the correct install 206 * type, we do nothing. Either way, BAM_SUCCESS is returned. 207 * 208 * For safety, we do one more check: if the kernel path starts with /boot, 209 * we verify that the new kernel exists before changing it. This is mainly 210 * done for bfu, as it may cause the failsafe archives to be a different 211 * boot architecture from the newly bfu'ed system. 212 */ 213 static error_t 214 parse_kernel_line(line_t *linep, const char *root, uint8_t *flags) 215 { 216 char path[PATH_MAX]; 217 int len, left, total_len; 218 struct stat sb; 219 char *new_ptr, *new_arg, *old_ptr; 220 menu_cmd_t which; 221 222 /* Used when changing a multiboot line to dboot */ 223 char *unix_ptr, *flags1_ptr, *flags2_ptr; 224 225 /* 226 * Note that BAM_ENTRY_DBOOT refers to the entry we're looking at, not 227 * necessarily the system type. 228 */ 229 if (strncmp(linep->arg, DIRECT_BOOT_32, 230 sizeof (DIRECT_BOOT_32) - 1) == 0) { 231 *flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 232 } else if ((strncmp(linep->arg, DIRECT_BOOT_KERNEL, 233 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) || 234 (strncmp(linep->arg, DIRECT_BOOT_64, 235 sizeof (DIRECT_BOOT_64) - 1) == 0) || 236 (strncmp(linep->arg, DIRECT_BOOT_FAILSAFE_KERNEL, 237 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0)) { 238 *flags |= BAM_ENTRY_DBOOT; 239 } else if ((strncmp(linep->arg, MULTI_BOOT, 240 sizeof (MULTI_BOOT) - 1) == 0) || 241 (strncmp(linep->arg, MULTI_BOOT_FAILSAFE, 242 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0)) { 243 *flags &= ~BAM_ENTRY_DBOOT; 244 } else { 245 bam_error(NO_KERNEL_MATCH, linep->lineNum, MENU_URL(root)); 246 return (BAM_ERROR); 247 } 248 249 if (((*flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) || 250 (((*flags & BAM_ENTRY_DBOOT) == 0) && 251 (bam_direct == BAM_DIRECT_MULTIBOOT))) { 252 253 /* No action needed */ 254 return (BAM_SUCCESS); 255 } 256 257 if (*flags & BAM_ENTRY_MINIROOT) { 258 /* 259 * We're changing boot architectures - make sure 260 * the multiboot failsafe still exists. 261 */ 262 (void) snprintf(path, PATH_MAX, "%s%s", root, 263 (*flags & BAM_ENTRY_DBOOT) ? MULTI_BOOT_FAILSAFE : 264 DIRECT_BOOT_FAILSAFE_KERNEL); 265 if (stat(path, &sb) != 0) { 266 if (bam_verbose) { 267 bam_error(FAILSAFE_MISSING, linep->lineNum); 268 } 269 return (BAM_SUCCESS); 270 } 271 } 272 273 /* 274 * Make sure we have the correct cmd - either kernel or kernel$ 275 * The failsafe entry should always be KERNEL_CMD. 276 */ 277 which = ((bam_direct == BAM_DIRECT_MULTIBOOT) || 278 (*flags & BAM_ENTRY_MINIROOT)) ? KERNEL_CMD : KERNEL_DOLLAR_CMD; 279 free(linep->cmd); 280 len = strlen(menu_cmds[which]) + 1; 281 linep->cmd = s_calloc(1, len); 282 (void) strncpy(linep->cmd, menu_cmds[which], len); 283 284 /* 285 * Since all arguments are copied, the new arg string should be close 286 * in size to the old one. Just add 32 to cover the difference in 287 * the boot path. 288 */ 289 total_len = strlen(linep->arg) + 32; 290 new_arg = s_calloc(1, total_len); 291 old_ptr = strchr(linep->arg, ' '); 292 if (old_ptr != NULL) 293 old_ptr++; 294 295 /* 296 * Transitioning from dboot to multiboot is pretty simple. We 297 * copy in multiboot and any args. 298 */ 299 if (bam_direct == BAM_DIRECT_MULTIBOOT) { 300 if (old_ptr == NULL) { 301 (void) snprintf(new_arg, total_len, "%s", 302 (*flags & BAM_ENTRY_MINIROOT) ? 303 MULTI_BOOT_FAILSAFE : MULTI_BOOT); 304 } else { 305 (void) snprintf(new_arg, total_len, "%s %s", 306 (*flags & BAM_ENTRY_MINIROOT) ? 307 MULTI_BOOT_FAILSAFE : MULTI_BOOT, old_ptr); 308 } 309 goto done; 310 } 311 312 /* 313 * Transitioning from multiboot to directboot is a bit more 314 * complicated, since we may have two sets of arguments to 315 * copy and a unix path to parse. 316 * 317 * First, figure out if there's a unix path. 318 */ 319 if ((old_ptr != NULL) && 320 ((unix_ptr = strstr(old_ptr, "/unix")) != NULL)) { 321 /* See if there's anything past unix */ 322 flags2_ptr = unix_ptr + sizeof ("/unix"); 323 if (*flags2_ptr == '\0') { 324 flags2_ptr = NULL; 325 } 326 327 while ((unix_ptr > old_ptr) && (*unix_ptr != ' ')) 328 unix_ptr--; 329 330 if (unix_ptr == old_ptr) { 331 flags1_ptr = NULL; 332 } else { 333 flags1_ptr = old_ptr; 334 } 335 336 if (strstr(unix_ptr, "kernel/unix") != NULL) { 337 *flags |= BAM_ENTRY_32BIT; 338 } else if ((strstr(unix_ptr, "kernel/amd64/unix") == NULL) && 339 (!bam_force)) { 340 /* 341 * If the above strstr returns NULL, but bam_force is 342 * set, we'll be upgrading an Install kernel. The 343 * result probably won't be what was intended, but we'll 344 * try it anyways. 345 */ 346 return (BAM_SKIP); 347 } 348 } else if (old_ptr != NULL) { 349 flags1_ptr = old_ptr; 350 unix_ptr = flags1_ptr + strlen(old_ptr); 351 flags2_ptr = NULL; 352 } else { 353 unix_ptr = flags1_ptr = flags2_ptr = NULL; 354 } 355 356 if (*flags & BAM_ENTRY_MINIROOT) { 357 (void) snprintf(new_arg, total_len, "%s", 358 DIRECT_BOOT_FAILSAFE_KERNEL); 359 } else if (*flags & BAM_ENTRY_32BIT) { 360 (void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_32); 361 } else { 362 (void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_KERNEL); 363 } 364 365 /* 366 * We now want to copy flags1_ptr through unix_ptr, and 367 * flags2_ptr through the end of the string 368 */ 369 if (flags1_ptr != NULL) { 370 len = strlcat(new_arg, " ", total_len); 371 left = total_len - len; 372 new_ptr = new_arg + len; 373 374 if ((unix_ptr - flags1_ptr) < left) 375 left = (unix_ptr - flags1_ptr) + 1; 376 (void) strlcpy(new_ptr, flags1_ptr, left); 377 } 378 if (flags2_ptr != NULL) { 379 (void) strlcat(new_arg, " ", total_len); 380 (void) strlcat(new_arg, flags2_ptr, total_len); 381 } 382 383 done: 384 free(linep->arg); 385 linep->arg = new_arg; 386 update_line(linep); 387 return (BAM_SUCCESS); 388 } 389 390 /* 391 * Similar to above, except this time we're looking at a module line, 392 * which is quite a bit simpler. 393 * 394 * Under multiboot, the archive line is: 395 * 396 * module /platform/i86pc/boot_archive 397 * 398 * Under directboot, the archive line is: 399 * 400 * module$ /platform/i86pc/$ISADIR/boot_archive 401 * 402 * which may be specified exactly as either of: 403 * 404 * module /platform/i86pc/boot_archive 405 * module /platform/i86pc/amd64/boot_archive 406 * 407 * For either dboot or multiboot, the failsafe is: 408 * 409 * module /boot/x86.miniroot-safe 410 */ 411 static error_t 412 parse_module_line(line_t *linep, const char *root, uint8_t flags) 413 { 414 int len; 415 menu_cmd_t which; 416 char *new; 417 418 /* 419 * If necessary, BAM_ENTRY_MINIROOT was already set in flags 420 * in upgrade_menu(). We re-check BAM_ENTRY_DBOOT here in here 421 * in case the kernel and module lines differ. 422 */ 423 if ((strcmp(linep->arg, DIRECT_BOOT_ARCHIVE) == 0) || 424 (strcmp(linep->arg, DIRECT_BOOT_ARCHIVE_64) == 0)) { 425 flags |= BAM_ENTRY_DBOOT; 426 } else if ((strcmp(linep->arg, MULTI_BOOT_ARCHIVE) == 0) || 427 (strcmp(linep->arg, MINIROOT) == 0)) { 428 flags &= ~BAM_ENTRY_DBOOT; 429 } else { 430 bam_error(NO_MODULE_MATCH, linep->lineNum, MENU_URL(root)); 431 return (BAM_ERROR); 432 } 433 434 if (((flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) || 435 (((flags & BAM_ENTRY_DBOOT) == 0) && 436 (bam_direct == BAM_DIRECT_MULTIBOOT)) || 437 ((flags & BAM_ENTRY_MINIROOT) && 438 (strcmp(linep->cmd, menu_cmds[MODULE_CMD]) == 0))) { 439 440 /* No action needed */ 441 return (BAM_SUCCESS); 442 } 443 444 /* 445 * Make sure we have the correct cmd - either module or module$ 446 * The failsafe entry should always be MODULE_CMD. 447 */ 448 which = ((bam_direct == BAM_DIRECT_MULTIBOOT) || 449 (flags & BAM_ENTRY_MINIROOT)) ? MODULE_CMD : MODULE_DOLLAR_CMD; 450 free(linep->cmd); 451 len = strlen(menu_cmds[which]) + 1; 452 linep->cmd = s_calloc(1, len); 453 (void) strncpy(linep->cmd, menu_cmds[which], len); 454 455 if (flags & BAM_ENTRY_MINIROOT) { 456 new = MINIROOT; 457 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 458 ((flags & BAM_ENTRY_32BIT) == 0)) { 459 new = DIRECT_BOOT_ARCHIVE; 460 } else { 461 new = MULTI_BOOT_ARCHIVE; 462 } 463 464 free(linep->arg); 465 len = strlen(new) + 1; 466 linep->arg = s_calloc(1, len); 467 (void) strncpy(linep->arg, new, len); 468 update_line(linep); 469 470 return (BAM_SUCCESS); 471 } 472 473 /*ARGSUSED*/ 474 error_t 475 upgrade_menu(menu_t *mp, char *root, char *opt) 476 { 477 entry_t *cur_entry; 478 line_t *cur_line; 479 int i, skipit, num_entries, found_hv; 480 int *hand_entries = NULL; 481 boolean_t found_kernel = B_FALSE; 482 error_t rv; 483 char *rootdev, *grubdisk = NULL; 484 485 skipit = num_entries = found_hv = 0; 486 487 rootdev = get_special(root); 488 if (rootdev) { 489 grubdisk = os_to_grubdisk(rootdev, strlen(root) == 1); 490 free(rootdev); 491 rootdev = NULL; 492 } 493 494 /* Loop through all OS entries in the menu.lst file */ 495 for (cur_entry = mp->entries; cur_entry != NULL; 496 cur_entry = cur_entry->next, skipit = 0) { 497 498 if ((cur_entry->flags & BAM_ENTRY_CHAINLOADER) || 499 ((cur_entry->flags & BAM_ENTRY_MINIROOT) && !bam_force)) 500 continue; 501 502 /* 503 * We only change entries added by bootadm and live upgrade, 504 * and warn on the rest, unless the -f flag was passed. 505 */ 506 if ((!(cur_entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) && 507 !bam_force) { 508 if (num_entries == 0) { 509 hand_entries = s_calloc(1, sizeof (int)); 510 } else { 511 hand_entries = s_realloc(hand_entries, 512 (num_entries + 1) * sizeof (int)); 513 } 514 hand_entries[num_entries++] = cur_entry->entryNum; 515 continue; 516 } 517 518 if (cur_entry->flags & BAM_ENTRY_HV) { 519 found_hv = 1; 520 continue; 521 } 522 523 /* 524 * We make two loops through the lines. First, we check if 525 * there is a root entry, and if so, whether we should be 526 * checking this entry. 527 */ 528 if ((grubdisk != NULL) && (cur_entry->flags & BAM_ENTRY_ROOT)) { 529 for (cur_line = cur_entry->start; cur_line != NULL; 530 cur_line = cur_line->next) { 531 if ((cur_line->cmd == NULL) || 532 (cur_line->arg == NULL)) 533 continue; 534 535 if (strcmp(cur_line->cmd, 536 menu_cmds[ROOT_CMD]) == 0) { 537 if (strcmp(cur_line->arg, 538 grubdisk) != 0) { 539 /* A different slice */ 540 skipit = 1; 541 } 542 break; 543 } 544 if (cur_line == cur_entry->end) 545 break; 546 } 547 } 548 if (skipit) 549 continue; 550 551 for (cur_line = cur_entry->start; cur_line != NULL; 552 cur_line = cur_line->next) { 553 554 /* 555 * We only compare for the length of KERNEL_CMD, 556 * so that KERNEL_DOLLAR_CMD will also match. 557 */ 558 if (strncmp(cur_line->cmd, menu_cmds[KERNEL_CMD], 559 strlen(menu_cmds[KERNEL_CMD])) == 0) { 560 rv = parse_kernel_line(cur_line, root, 561 &(cur_entry->flags)); 562 if (rv == BAM_SKIP) { 563 break; 564 } else if (rv != BAM_SUCCESS) { 565 return (rv); 566 } 567 found_kernel = B_TRUE; 568 } else if (strncmp(cur_line->cmd, 569 menu_cmds[MODULE_CMD], 570 strlen(menu_cmds[MODULE_CMD])) == 0) { 571 rv = parse_module_line(cur_line, root, 572 cur_entry->flags); 573 if (rv != BAM_SUCCESS) { 574 return (rv); 575 } 576 } 577 if (cur_line == cur_entry->end) 578 break; 579 } 580 } 581 582 /* 583 * If we're upgrading to a virtualized kernel and there are no 584 * hv entries in menu.lst, we need to add one. 585 */ 586 if ((bam_is_hv == BAM_HV_PRESENT) && (found_hv == 0)) { 587 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubdisk, 588 XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE); 589 } 590 591 /* 592 * We only want to output one error, to avoid confusing a user. We 593 * rank "No kernels changed" as a higher priority than "will not 594 * update hand-added entries", since the former implies the latter. 595 */ 596 if (found_kernel == B_FALSE) { 597 bam_error(NO_KERNELS_FOUND, MENU_URL(root)); 598 return (BAM_ERROR); 599 } else if (num_entries > 0) { 600 bam_error(HAND_ADDED_ENTRY, MENU_URL(root)); 601 bam_print_stderr("Entry Number%s: ", (num_entries > 1) ? 602 "s" : ""); 603 for (i = 0; i < num_entries; i++) { 604 bam_print_stderr("%d ", hand_entries[i]); 605 } 606 bam_print_stderr("\n"); 607 } 608 return (BAM_WRITE); 609 } 610