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 <string.h> 30 #include <strings.h> 31 #include <unistd.h> 32 #include <stdlib.h> 33 #include <thread.h> 34 #include <synch.h> 35 #include <sys/types.h> 36 #include <ctype.h> 37 #include <sys/stat.h> 38 #include <fcntl.h> 39 #include <sys/modctl.h> 40 #include <errno.h> 41 #include <sys/openpromio.h> 42 #include <ftw.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <limits.h> 46 47 #include "device_info.h" 48 49 /* 50 * #define's 51 */ 52 53 /* alias node searching return values */ 54 #define NO_MATCH -1 55 #define EXACT_MATCH 1 56 #define INEXACT_MATCH 2 57 58 /* for prom io operations */ 59 #define BUFSIZE 4096 60 #define MAXPROPSIZE 256 61 #define MAXVALSIZE (BUFSIZE - MAXPROPSIZE - sizeof (uint_t)) 62 63 /* prom_obp_vers() return values */ 64 #define OBP_OF 0x4 /* versions OBP 3.x */ 65 #define OBP_NO_ALIAS_NODE 0x8 /* No alias node */ 66 67 /* for nftw call */ 68 #define FT_DEPTH 15 69 70 /* default logical and physical device name space */ 71 #define DEV "/dev" 72 #define DEVICES "/devices" 73 74 /* for boot device identification on x86 */ 75 #define CREATE_DISKMAP "/boot/solaris/bin/create_diskmap" 76 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map" 77 78 /* 79 * internal structure declarations 80 */ 81 82 /* for prom io functions */ 83 typedef union { 84 char buf[BUFSIZE]; 85 struct openpromio opp; 86 } Oppbuf; 87 88 /* used to manage lists of devices and aliases */ 89 struct name_list { 90 char *name; 91 struct name_list *next; 92 }; 93 94 /* 95 * internal global data 96 */ 97 98 /* global since nftw does not let you pass args to be updated */ 99 static struct name_list **dev_list; 100 101 /* global since nftw does not let you pass args to be updated */ 102 static struct boot_dev **bootdev_list; 103 104 /* mutex to protect bootdev_list and dev_list */ 105 static mutex_t dev_lists_lk = DEFAULTMUTEX; 106 107 /* 108 * internal function prototypes 109 */ 110 111 static int prom_open(int); 112 static void prom_close(int); 113 static int is_openprom(int); 114 115 static int prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf); 116 static int alias_to_prom_dev(char *alias, char *ret_buf); 117 static int prom_srch_aliases_by_def(char *, struct name_list **, 118 struct name_list **, int); 119 static int prom_find_aliases_node(int fd); 120 static int prom_compare_devs(char *prom_dev1, char *prom_dev2); 121 static int _prom_strcmp(char *s1, char *s2); 122 static int prom_srch_node(int fd, char *prop_name, char *ret_buf); 123 static uint_t prom_next_node(int fd, uint_t node_id); 124 static uint_t prom_child_node(int fd, uint_t node_id); 125 126 static int prom_obp_vers(void); 127 128 static void parse_name(char *, char **, char **, char **); 129 static int process_bootdev(const char *, const char *, struct boot_dev ***); 130 static int process_minor_name(char *dev_path, const char *default_root); 131 static void options_override(char *prom_path, char *alias_name); 132 static int devfs_phys_to_logical(struct boot_dev **bootdev_array, 133 const int array_size, const char *default_root); 134 static int check_logical_dev(const char *, const struct stat *, int, 135 struct FTW *); 136 static struct boot_dev *alloc_bootdev(char *); 137 static void free_name_list(struct name_list *list, int free_name); 138 static int insert_alias_list(struct name_list **list, 139 char *alias_name); 140 static int get_boot_dev_var(struct openpromio *opp); 141 static int set_boot_dev_var(struct openpromio *opp, char *bootdev); 142 static int devfs_prom_to_dev_name(char *prom_path, char *dev_path); 143 static int devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len); 144 145 /* 146 * frees a list of paths from devfs_get_prom_name_list 147 */ 148 static void 149 prom_list_free(char **prom_list) 150 { 151 int i = 0; 152 153 if (!prom_list) 154 return; 155 156 while (prom_list[i]) { 157 free(prom_list[i]); 158 i++; 159 } 160 free(prom_list); 161 } 162 163 static int 164 devfs_get_prom_name_list(const char *dev_name, char ***prom_list) 165 { 166 char *prom_path = NULL; 167 int count = 0; /* # of slots we will need in prom_list */ 168 int ret, i, len; 169 char **list; 170 char *ptr; 171 172 if (dev_name == NULL) 173 return (DEVFS_INVAL); 174 if (*dev_name != '/') 175 return (DEVFS_INVAL); 176 if (prom_list == NULL) 177 return (DEVFS_INVAL); 178 179 /* 180 * make sure we are on a machine which supports a prom 181 * and we have permission to use /dev/openprom 182 */ 183 if ((ret = prom_obp_vers()) < 0) 184 return (ret); 185 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) 186 return (DEVFS_NOMEM); 187 /* 188 * get the prom path name 189 */ 190 ret = devfs_dev_to_prom_names((char *)dev_name, prom_path, MAXVALSIZE); 191 if (ret < 0) { 192 free(prom_path); 193 return (ret); 194 } 195 /* deal with list of names */ 196 for (i = 0; i < ret; i++) 197 if (prom_path[i] == '\0') 198 count++; 199 200 if ((list = (char **)calloc(count + 1, sizeof (char *))) == NULL) { 201 free(prom_path); 202 return (DEVFS_NOMEM); 203 } 204 205 ptr = prom_path; 206 for (i = 0; i < count; i++) { 207 len = strlen(ptr) + 1; 208 if ((list[i] = (char *)malloc(len)) == NULL) { 209 free(prom_path); 210 free(list); 211 return (DEVFS_NOMEM); 212 } 213 (void) snprintf(list[i], len, "%s", ptr); 214 ptr += len; 215 } 216 217 free(prom_path); 218 219 *prom_list = list; 220 return (0); 221 } 222 223 /* 224 * retrieve the list of prom representations for a given device name 225 * the list will be sorted in the following order: exact aliases, 226 * inexact aliases, prom device path name. If multiple matches occur 227 * for exact or inexact aliases, then these are sorted in collating 228 * order. The list is returned in prom_list 229 * 230 * the list may be restricted by specifying the correct flags in options. 231 */ 232 int 233 devfs_get_prom_names(const char *dev_name, uint_t options, char ***prom_list) 234 { 235 char *prom_path = NULL; 236 int count = 0; /* # of slots we will need in prom_list */ 237 char **alias_list = NULL; 238 char **list; 239 int ret; 240 241 if (dev_name == NULL) { 242 return (DEVFS_INVAL); 243 } 244 if (*dev_name != '/') { 245 return (DEVFS_INVAL); 246 } 247 if (prom_list == NULL) { 248 return (DEVFS_INVAL); 249 } 250 /* 251 * make sure we are on a machine which supports a prom 252 * and we have permission to use /dev/openprom 253 */ 254 if ((ret = prom_obp_vers()) < 0) { 255 return (ret); 256 } 257 if ((prom_path = (char *)malloc(MAXPATHLEN)) == NULL) { 258 return (DEVFS_NOMEM); 259 } 260 /* 261 * get the prom path name 262 */ 263 ret = devfs_dev_to_prom_name((char *)dev_name, prom_path); 264 if (ret < 0) { 265 free(prom_path); 266 return (ret); 267 } 268 /* get the list of aliases (exact and inexact) */ 269 if ((ret = prom_dev_to_alias(prom_path, options, &alias_list)) < 0) { 270 free(prom_path); 271 return (ret); 272 } 273 /* now figure out how big the return array must be */ 274 if (alias_list != NULL) { 275 while (alias_list[count] != NULL) { 276 count++; 277 } 278 } 279 if ((options & BOOTDEV_NO_PROM_PATH) == 0) { 280 count++; /* # of slots we will need in prom_list */ 281 } 282 count++; /* for the null terminator */ 283 284 /* allocate space for the list */ 285 if ((list = (char **)calloc(count, sizeof (char *))) == NULL) { 286 count = 0; 287 while ((alias_list) && (alias_list[count] != NULL)) { 288 free(alias_list[count]); 289 count++; 290 } 291 free(alias_list); 292 free(prom_path); 293 return (DEVFS_NOMEM); 294 } 295 /* fill in the array and free the name list of aliases. */ 296 count = 0; 297 while ((alias_list) && (alias_list[count] != NULL)) { 298 list[count] = alias_list[count]; 299 count++; 300 } 301 if ((options & BOOTDEV_NO_PROM_PATH) == 0) { 302 list[count] = prom_path; 303 } 304 if (alias_list != NULL) { 305 free(alias_list); 306 } 307 *prom_list = list; 308 return (0); 309 } 310 311 /* 312 * Get a list prom-path translations for a solaris device. 313 * 314 * Returns the number of and all OBP paths and alias variants that 315 * reference the Solaris device path passed in. 316 */ 317 int 318 devfs_get_all_prom_names(const char *solaris_path, uint_t flags, 319 struct devfs_prom_path **paths) 320 { 321 int ret, len, i, count = 0; 322 char *ptr, *prom_path; 323 struct devfs_prom_path *cur = NULL, *new; 324 325 if ((ret = prom_obp_vers()) < 0) 326 return (ret); 327 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) 328 return (DEVFS_NOMEM); 329 330 if ((ret = devfs_dev_to_prom_names((char *)solaris_path, 331 prom_path, MAXVALSIZE)) < 0) { 332 free(prom_path); 333 return (ret); 334 } 335 336 for (i = 0; i < ret; i++) 337 if (prom_path[i] == '\0') 338 count++; 339 340 *paths = NULL; 341 ptr = prom_path; 342 for (i = 0; i < count; i++) { 343 if ((new = (struct devfs_prom_path *)calloc( 344 sizeof (struct devfs_prom_path), 1)) == NULL) { 345 free(prom_path); 346 devfs_free_all_prom_names(*paths); 347 return (DEVFS_NOMEM); 348 } 349 350 if (cur == NULL) 351 *paths = new; 352 else 353 cur->next = new; 354 cur = new; 355 356 len = strlen(ptr) + 1; 357 if ((cur->obp_path = (char *)calloc(len, 1)) == NULL) { 358 free(prom_path); 359 devfs_free_all_prom_names(*paths); 360 return (DEVFS_NOMEM); 361 } 362 363 (void) snprintf(cur->obp_path, len, "%s", ptr); 364 ptr += len; 365 if ((ret = prom_dev_to_alias(cur->obp_path, flags, 366 &(cur->alias_list))) < 0) { 367 free(prom_path); 368 devfs_free_all_prom_names(*paths); 369 return (ret); 370 } 371 } 372 373 free(prom_path); 374 return (count); 375 } 376 377 void 378 devfs_free_all_prom_names(struct devfs_prom_path *paths) 379 { 380 int i; 381 382 if (paths == NULL) 383 return; 384 385 devfs_free_all_prom_names(paths->next); 386 387 if (paths->obp_path != NULL) 388 free(paths->obp_path); 389 390 if (paths->alias_list != NULL) { 391 for (i = 0; paths->alias_list[i] != NULL; i++) 392 if (paths->alias_list[i] != NULL) 393 free(paths->alias_list[i]); 394 395 free(paths->alias_list); 396 } 397 398 free(paths); 399 } 400 401 /* 402 * Accepts a device name as an input argument. Uses this to set the 403 * boot-device (or like) variable 404 * 405 * By default, this routine prepends to the list and converts the 406 * logical device name to its most compact prom representation. 407 * Available options include: converting the device name to a prom 408 * path name (but not an alias) or performing no conversion at all; 409 * overwriting the existing contents of boot-device rather than 410 * prepending. 411 */ 412 int 413 devfs_bootdev_set_list(const char *dev_name, const uint_t options) 414 { 415 char *prom_path; 416 char *new_bootdev; 417 char *ptr; 418 char **alias_list = NULL; 419 char **prom_list = NULL; 420 Oppbuf oppbuf; 421 struct openpromio *opp = &(oppbuf.opp); 422 int ret, len, i, j; 423 424 if (devfs_bootdev_modifiable() != 0) { 425 return (DEVFS_NOTSUP); 426 } 427 if (dev_name == NULL) { 428 return (DEVFS_INVAL); 429 } 430 if (strlen(dev_name) >= MAXPATHLEN) 431 return (DEVFS_INVAL); 432 433 if ((*dev_name != '/') && !(options & BOOTDEV_LITERAL)) { 434 return (DEVFS_INVAL); 435 } 436 if ((options & BOOTDEV_LITERAL) && (options & BOOTDEV_PROMDEV)) { 437 return (DEVFS_INVAL); 438 } 439 /* 440 * if we are prepending, make sure that this obp rev 441 * supports multiple boot device entries. 442 */ 443 ret = prom_obp_vers(); 444 if (ret < 0) { 445 return (ret); 446 } 447 448 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) { 449 return (DEVFS_NOMEM); 450 } 451 if (options & BOOTDEV_LITERAL) { 452 (void) strcpy(prom_path, dev_name); 453 } else { 454 /* need to convert to prom representation */ 455 ret = devfs_get_prom_name_list(dev_name, &prom_list); 456 if (ret < 0) { 457 free(prom_path); 458 return (ret); 459 } 460 461 len = MAXVALSIZE; 462 i = 0; 463 ptr = prom_path; 464 while (prom_list && prom_list[i]) { 465 if (!(options & BOOTDEV_PROMDEV)) { 466 ret = prom_dev_to_alias(prom_list[i], 0, 467 &alias_list); 468 if (ret < 0) { 469 free(prom_path); 470 prom_list_free(prom_list); 471 return (ret); 472 } 473 if ((alias_list != NULL) && 474 (alias_list[0] != NULL)) { 475 (void) snprintf(ptr, len, "%s ", 476 alias_list[0]); 477 for (ret = 0; alias_list[ret] != NULL; 478 ret++) 479 free(alias_list[ret]); 480 } else { 481 (void) snprintf(ptr, len, "%s ", 482 prom_list[i]); 483 } 484 if (alias_list != NULL) 485 free(alias_list); 486 } else { 487 (void) snprintf(ptr, len, "%s ", prom_list[i]); 488 } 489 j = strlen(ptr); 490 len -= j; 491 ptr += j; 492 i++; 493 } 494 ptr--; 495 *ptr = NULL; 496 497 prom_list_free(prom_list); 498 } 499 if (options & BOOTDEV_OVERWRITE) { 500 new_bootdev = prom_path; 501 } else { 502 /* retrieve the current value of boot-device */ 503 ret = get_boot_dev_var(opp); 504 if (ret < 0) { 505 free(prom_path); 506 return (ret); 507 } 508 /* prepend new entry - deal with duplicates */ 509 new_bootdev = (char *)malloc(strlen(opp->oprom_array) 510 + strlen(prom_path) + 2); 511 if (new_bootdev == NULL) { 512 free(prom_path); 513 return (DEVFS_NOMEM); 514 } 515 (void) strcpy(new_bootdev, prom_path); 516 if (opp->oprom_size > 0) { 517 for (ptr = strtok(opp->oprom_array, " "); ptr != NULL; 518 ptr = strtok(NULL, " ")) { 519 /* we strip out duplicates */ 520 if (strcmp(prom_path, ptr) == 0) { 521 continue; 522 } 523 (void) strcat(new_bootdev, " "); 524 (void) strcat(new_bootdev, ptr); 525 } 526 } 527 } 528 529 /* now set the new value */ 530 ret = set_boot_dev_var(opp, new_bootdev); 531 532 if (options & BOOTDEV_OVERWRITE) { 533 free(prom_path); 534 } else { 535 free(new_bootdev); 536 free(prom_path); 537 } 538 539 return (ret); 540 } 541 542 /* 543 * sets the string bootdev as the new value for boot-device 544 */ 545 static int 546 set_boot_dev_var(struct openpromio *opp, char *bootdev) 547 { 548 int prom_fd; 549 int i; 550 int ret; 551 char *valbuf; 552 char *save_bootdev; 553 char *bootdev_variables[] = { 554 "boot-device", 555 "bootdev", 556 "boot-from", 557 NULL 558 }; 559 int found = 0; 560 int *ip = (int *)((void *)opp->oprom_array); 561 562 /* query the prom */ 563 prom_fd = prom_open(O_RDWR); 564 if (prom_fd < 0) { 565 return (prom_fd); 566 } 567 568 /* get the diagnostic-mode? property */ 569 (void) strcpy(opp->oprom_array, "diagnostic-mode?"); 570 opp->oprom_size = MAXVALSIZE; 571 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) { 572 if ((opp->oprom_size > 0) && 573 (strcmp(opp->oprom_array, "true") == 0)) { 574 prom_close(prom_fd); 575 return (DEVFS_ERR); 576 } 577 } 578 /* get the diag-switch? property */ 579 (void) strcpy(opp->oprom_array, "diag-switch?"); 580 opp->oprom_size = MAXVALSIZE; 581 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) { 582 if ((opp->oprom_size > 0) && 583 (strcmp(opp->oprom_array, "true") == 0)) { 584 prom_close(prom_fd); 585 return (DEVFS_ERR); 586 } 587 } 588 /* 589 * look for one of the following properties in order: 590 * boot-device 591 * bootdev 592 * boot-from 593 * 594 * Use the first one that we find. 595 */ 596 *ip = 0; 597 opp->oprom_size = MAXPROPSIZE; 598 while ((opp->oprom_size != 0) && (!found)) { 599 opp->oprom_size = MAXPROPSIZE; 600 if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) { 601 break; 602 } 603 for (i = 0; bootdev_variables[i] != NULL; i++) { 604 if (strcmp(opp->oprom_array, bootdev_variables[i]) 605 == 0) { 606 found = 1; 607 break; 608 } 609 } 610 } 611 if (found) { 612 (void) strcpy(opp->oprom_array, bootdev_variables[i]); 613 opp->oprom_size = MAXVALSIZE; 614 if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) { 615 prom_close(prom_fd); 616 return (DEVFS_NOTSUP); 617 } 618 } else { 619 prom_close(prom_fd); 620 return (DEVFS_NOTSUP); 621 } 622 623 /* save the old copy in case we fail */ 624 if ((save_bootdev = strdup(opp->oprom_array)) == NULL) { 625 prom_close(prom_fd); 626 return (DEVFS_NOMEM); 627 } 628 /* set up the new value of boot-device */ 629 (void) strcpy(opp->oprom_array, bootdev_variables[i]); 630 valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1; 631 (void) strcpy(valbuf, bootdev); 632 633 opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2; 634 635 if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) { 636 free(save_bootdev); 637 prom_close(prom_fd); 638 return (DEVFS_ERR); 639 } 640 641 /* 642 * now read it back to make sure it took 643 */ 644 (void) strcpy(opp->oprom_array, bootdev_variables[i]); 645 opp->oprom_size = MAXVALSIZE; 646 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) { 647 if (_prom_strcmp(opp->oprom_array, bootdev) == 0) { 648 /* success */ 649 free(save_bootdev); 650 prom_close(prom_fd); 651 return (0); 652 } 653 /* deal with setting it to "" */ 654 if ((strlen(bootdev) == 0) && (opp->oprom_size == 0)) { 655 /* success */ 656 free(save_bootdev); 657 prom_close(prom_fd); 658 return (0); 659 } 660 } 661 /* 662 * something did not take - write out the old value and 663 * hope that we can restore things... 664 * 665 * unfortunately, there is no way for us to differentiate 666 * whether we exceeded the maximum number of characters 667 * allowable. The limit varies from prom rev to prom 668 * rev, and on some proms, when the limit is 669 * exceeded, whatever was in the 670 * boot-device variable becomes unreadable. 671 * 672 * so if we fail, we will assume we ran out of room. If we 673 * not able to restore the original setting, then we will 674 * return DEVFS_ERR instead. 675 */ 676 ret = DEVFS_LIMIT; 677 (void) strcpy(opp->oprom_array, bootdev_variables[i]); 678 valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1; 679 (void) strcpy(valbuf, save_bootdev); 680 681 opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2; 682 683 if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) { 684 ret = DEVFS_ERR; 685 } 686 free(save_bootdev); 687 prom_close(prom_fd); 688 return (ret); 689 } 690 /* 691 * retrieve the current value for boot-device 692 */ 693 static int 694 get_boot_dev_var(struct openpromio *opp) 695 { 696 int prom_fd; 697 int i; 698 char *bootdev_variables[] = { 699 "boot-device", 700 "bootdev", 701 "boot-from", 702 NULL 703 }; 704 int found = 0; 705 int *ip = (int *)((void *)opp->oprom_array); 706 707 /* query the prom */ 708 prom_fd = prom_open(O_RDONLY); 709 if (prom_fd < 0) { 710 return (prom_fd); 711 } 712 713 /* get the diagnostic-mode? property */ 714 (void) strcpy(opp->oprom_array, "diagnostic-mode?"); 715 opp->oprom_size = MAXVALSIZE; 716 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) { 717 if ((opp->oprom_size > 0) && 718 (strcmp(opp->oprom_array, "true") == 0)) { 719 prom_close(prom_fd); 720 return (DEVFS_ERR); 721 } 722 } 723 /* get the diag-switch? property */ 724 (void) strcpy(opp->oprom_array, "diag-switch?"); 725 opp->oprom_size = MAXVALSIZE; 726 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) { 727 if ((opp->oprom_size > 0) && 728 (strcmp(opp->oprom_array, "true") == 0)) { 729 prom_close(prom_fd); 730 return (DEVFS_ERR); 731 } 732 } 733 /* 734 * look for one of the following properties in order: 735 * boot-device 736 * bootdev 737 * boot-from 738 * 739 * Use the first one that we find. 740 */ 741 *ip = 0; 742 opp->oprom_size = MAXPROPSIZE; 743 while ((opp->oprom_size != 0) && (!found)) { 744 opp->oprom_size = MAXPROPSIZE; 745 if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) { 746 break; 747 } 748 for (i = 0; bootdev_variables[i] != NULL; i++) { 749 if (strcmp(opp->oprom_array, bootdev_variables[i]) 750 == 0) { 751 found = 1; 752 break; 753 } 754 } 755 } 756 if (found) { 757 (void) strcpy(opp->oprom_array, bootdev_variables[i]); 758 opp->oprom_size = MAXVALSIZE; 759 if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) { 760 prom_close(prom_fd); 761 return (DEVFS_ERR); 762 } 763 /* boot-device exists but contains nothing */ 764 if (opp->oprom_size == 0) { 765 *opp->oprom_array = '\0'; 766 } 767 } else { 768 prom_close(prom_fd); 769 return (DEVFS_NOTSUP); 770 } 771 prom_close(prom_fd); 772 return (0); 773 } 774 775 #ifndef __sparc 776 static FILE * 777 open_diskmap(void) 778 { 779 FILE *fp; 780 char cmd[PATH_MAX]; 781 782 /* make sure we have a map file */ 783 fp = fopen(GRUBDISK_MAP, "r"); 784 if (fp == NULL) { 785 (void) snprintf(cmd, sizeof (cmd), 786 "%s > /dev/null", CREATE_DISKMAP); 787 (void) system(cmd); 788 fp = fopen(GRUBDISK_MAP, "r"); 789 } 790 return (fp); 791 } 792 793 static int 794 find_x86_boot_device(struct openpromio *opp) 795 { 796 int ret = DEVFS_ERR; 797 char *cp, line[MAXVALSIZE + 6]; 798 FILE *file; 799 800 file = open_diskmap(); 801 if (file == NULL) 802 return (DEVFS_ERR); 803 804 while (fgets(line, MAXVALSIZE + 6, file)) { 805 if (strncmp(line, "0 ", 2) != 0) 806 continue; 807 /* drop new-line */ 808 line[strlen(line) - 1] = '\0'; 809 /* 810 * an x86 BIOS only boots a disk, not a partition 811 * or a slice, so hard-code :q (p0) 812 */ 813 cp = strchr(line + 2, ' '); 814 if (cp == NULL) 815 break; 816 (void) snprintf(opp->oprom_array, MAXVALSIZE, 817 "%s:q", cp + 1); 818 opp->oprom_size = MAXVALSIZE; 819 ret = 0; 820 break; 821 } 822 (void) fclose(file); 823 return (ret); 824 } 825 #endif /* ndef __sparc */ 826 827 /* 828 * retrieve the list of entries in the boot-device configuration 829 * variable. An array of boot_dev structs will be created, one entry 830 * for each device name in the boot-device variable. Each entry 831 * in the array will contain the logical device representation of the 832 * boot-device entry, if any. 833 * 834 * default_root. if set, is used to locate logical device entries in 835 * directories other than /dev 836 */ 837 int 838 devfs_bootdev_get_list(const char *default_root, 839 struct boot_dev ***bootdev_list) 840 { 841 Oppbuf oppbuf; 842 struct openpromio *opp = &(oppbuf.opp); 843 int i; 844 struct boot_dev **tmp_list; 845 846 if (default_root == NULL) { 847 default_root = ""; 848 } else if (*default_root != '/') { 849 return (DEVFS_INVAL); 850 } 851 852 if (bootdev_list == NULL) { 853 return (DEVFS_INVAL); 854 } 855 856 /* get the boot-device variable */ 857 #if defined(sparc) 858 i = get_boot_dev_var(opp); 859 #else 860 i = find_x86_boot_device(opp); 861 #endif 862 if (i < 0) { 863 return (i); 864 } 865 /* now try to translate each entry to a logical device. */ 866 i = process_bootdev(opp->oprom_array, default_root, &tmp_list); 867 if (i == 0) { 868 *bootdev_list = tmp_list; 869 return (0); 870 } else { 871 return (i); 872 } 873 } 874 875 /* 876 * loop thru the list of entries in a boot-device configuration 877 * variable. 878 */ 879 static int 880 process_bootdev(const char *bootdevice, const char *default_root, 881 struct boot_dev ***list) 882 { 883 int i; 884 char *entry, *ptr; 885 char prom_path[MAXPATHLEN]; 886 char ret_buf[MAXPATHLEN]; 887 struct boot_dev **bootdev_array; 888 int num_entries = 0; 889 int found = 0; 890 int vers; 891 892 if ((entry = (char *)malloc(strlen(bootdevice) + 1)) == NULL) { 893 return (DEVFS_NOMEM); 894 } 895 /* count the number of entries */ 896 (void) strcpy(entry, bootdevice); 897 for (ptr = strtok(entry, " "); ptr != NULL; 898 ptr = strtok(NULL, " ")) { 899 num_entries++; 900 } 901 (void) strcpy(entry, bootdevice); 902 903 bootdev_array = (struct boot_dev **) 904 calloc((size_t)num_entries + 1, sizeof (struct boot_dev *)); 905 906 if (bootdev_array == NULL) { 907 free(entry); 908 return (DEVFS_NOMEM); 909 } 910 911 vers = prom_obp_vers(); 912 if (vers < 0) { 913 free(entry); 914 return (vers); 915 } 916 917 /* for each entry in boot-device, do... */ 918 for (ptr = strtok(entry, " "), i = 0; ptr != NULL; 919 ptr = strtok(NULL, " "), i++) { 920 921 if ((bootdev_array[i] = alloc_bootdev(ptr)) == NULL) { 922 devfs_bootdev_free_list(bootdev_array); 923 free(entry); 924 return (DEVFS_NOMEM); 925 } 926 927 /* 928 * prom boot-device may be aliased, so we need to do 929 * the necessary prom alias to dev translation. 930 */ 931 if (*ptr != '/') { 932 if (alias_to_prom_dev(ptr, prom_path) < 0) { 933 continue; 934 } 935 } else { 936 (void) strcpy(prom_path, ptr); 937 } 938 939 /* now we have a prom device path - convert to a devfs name */ 940 if (devfs_prom_to_dev_name(prom_path, ret_buf) < 0) { 941 continue; 942 } 943 944 /* append any default minor names necessary */ 945 if (process_minor_name(ret_buf, default_root) < 0) { 946 continue; 947 } 948 found = 1; 949 950 /* 951 * store the physical device path for now - when 952 * we are all done with the entries, we will convert 953 * these to their logical device name equivalents 954 */ 955 bootdev_array[i]->bootdev_trans[0] = strdup(ret_buf); 956 } 957 /* 958 * Convert all of the boot-device entries that translated to a 959 * physical device path in /devices to a logical device path 960 * in /dev (note that there may be several logical device paths 961 * associated with a single physical device path - return them all 962 */ 963 if (found) { 964 if (devfs_phys_to_logical(bootdev_array, num_entries, 965 default_root) < 0) { 966 devfs_bootdev_free_list(bootdev_array); 967 bootdev_array = NULL; 968 } 969 } 970 free(entry); 971 *list = bootdev_array; 972 return (0); 973 } 974 975 /* 976 * We may get a device path from the prom that has no minor name 977 * information included in it. Since this device name will not 978 * correspond directly to a physical device in /devices, we do our 979 * best to append what the default minor name should be and try this. 980 * 981 * For sparc: we append slice 0 (:a). 982 * For x86: we append fdisk partition 0 (:q). 983 */ 984 static int 985 process_minor_name(char *dev_path, const char *root) 986 { 987 char *cp; 988 #if defined(sparc) 989 const char *default_minor_name = "a"; 990 #else 991 const char *default_minor_name = "q"; 992 #endif 993 int n; 994 struct stat stat_buf; 995 char path[MAXPATHLEN]; 996 997 (void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path); 998 /* 999 * if the device file already exists as given to us, there 1000 * is nothing to do but return. 1001 */ 1002 if (stat(path, &stat_buf) == 0) { 1003 return (0); 1004 } 1005 /* 1006 * if there is no ':' after the last '/' character, or if there is 1007 * a ':' with no specifier, append the default segment specifier 1008 * ; if there is a ':' followed by a digit, this indicates 1009 * a partition number (which does not map into the /devices name 1010 * space), so strip the number and replace it with the letter 1011 * that represents the partition index 1012 */ 1013 if ((cp = strrchr(dev_path, '/')) != NULL) { 1014 if ((cp = strchr(cp, ':')) == NULL) { 1015 (void) strcat(dev_path, ":"); 1016 (void) strcat(dev_path, default_minor_name); 1017 } else if (*++cp == '\0') { 1018 (void) strcat(dev_path, default_minor_name); 1019 } else if (isdigit(*cp)) { 1020 n = atoi(cp); 1021 /* make sure to squash the digit */ 1022 *cp = '\0'; 1023 switch (n) { 1024 case 0: (void) strcat(dev_path, "q"); 1025 break; 1026 case 1: (void) strcat(dev_path, "r"); 1027 break; 1028 case 2: (void) strcat(dev_path, "s"); 1029 break; 1030 case 3: (void) strcat(dev_path, "t"); 1031 break; 1032 case 4: (void) strcat(dev_path, "u"); 1033 break; 1034 default: (void) strcat(dev_path, "a"); 1035 break; 1036 } 1037 } 1038 } 1039 /* 1040 * see if we can find something now. 1041 */ 1042 (void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path); 1043 1044 if (stat(path, &stat_buf) == 0) { 1045 return (0); 1046 } else { 1047 return (-1); 1048 } 1049 } 1050 1051 /* 1052 * for each entry in bootdev_array, convert the physical device 1053 * representation of the boot-device entry to one or more logical device 1054 * entries. We use the hammer method - walk through the logical device 1055 * name space looking for matches (/dev). We use nftw to do this. 1056 */ 1057 static int 1058 devfs_phys_to_logical(struct boot_dev **bootdev_array, const int array_size, 1059 const char *default_root) 1060 { 1061 int walk_flags = FTW_PHYS | FTW_MOUNT; 1062 char *full_path; 1063 struct name_list *list; 1064 int count, i; 1065 char **dev_name_array; 1066 size_t default_root_len; 1067 char *dev_dir = DEV; 1068 int len; 1069 1070 if (array_size < 0) { 1071 return (-1); 1072 } 1073 1074 if (bootdev_array == NULL) { 1075 return (-1); 1076 } 1077 if (default_root == NULL) { 1078 return (-1); 1079 } 1080 default_root_len = strlen(default_root); 1081 if ((default_root_len != 0) && (*default_root != '/')) { 1082 return (-1); 1083 } 1084 1085 /* short cut for an empty array */ 1086 if (*bootdev_array == NULL) { 1087 return (0); 1088 } 1089 1090 /* tell nftw where to start (default: /dev) */ 1091 len = default_root_len + strlen(dev_dir) + 1; 1092 if ((full_path = (char *)malloc(len)) == NULL) { 1093 return (-1); 1094 } 1095 1096 /* 1097 * if the default root path is terminated with a /, we have to 1098 * make sure we don't end up with one too many slashes in the 1099 * path we are building. 1100 */ 1101 if ((default_root_len > (size_t)0) && 1102 (default_root[default_root_len - 1] == '/')) { 1103 (void) snprintf(full_path, len, "%s%s", default_root, 1104 &dev_dir[1]); 1105 } else { 1106 (void) snprintf(full_path, len, "%s%s", default_root, dev_dir); 1107 } 1108 1109 /* 1110 * we need to muck with global data to make nftw work 1111 * so single thread access 1112 */ 1113 (void) mutex_lock(&dev_lists_lk); 1114 1115 /* 1116 * set the global vars bootdev_list and dev_list for use by nftw 1117 * dev_list is an array of lists - one for each boot-device 1118 * entry. The nftw function will create a list of logical device 1119 * entries for each boot-device and put all of the lists in 1120 * dev_list. 1121 */ 1122 dev_list = (struct name_list **) 1123 calloc(array_size, sizeof (struct name_list *)); 1124 if (dev_list == NULL) { 1125 free(full_path); 1126 (void) mutex_unlock(&dev_lists_lk); 1127 return (-1); 1128 } 1129 bootdev_list = bootdev_array; 1130 1131 if (nftw(full_path, check_logical_dev, FT_DEPTH, walk_flags) == -1) { 1132 bootdev_list = NULL; 1133 free(full_path); 1134 for (i = 0; i < array_size; i++) { 1135 free_name_list(dev_list[i], 1); 1136 } 1137 /* don't free dev_list here because it's been handed off */ 1138 dev_list = NULL; 1139 (void) mutex_unlock(&dev_lists_lk); 1140 return (-1); 1141 } 1142 1143 /* 1144 * now we