import less(1)
[unleashed/tickless.git] / usr / src / lib / libdevinfo / devfsinfo.c
blobefec48c694e43212121b26b54d35a666e399a1ea
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <thread.h>
33 #include <synch.h>
34 #include <sys/types.h>
35 #include <ctype.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <sys/modctl.h>
39 #include <errno.h>
40 #include <sys/openpromio.h>
41 #include <ftw.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <limits.h>
46 #include "device_info.h"
49 * #define's
52 /* alias node searching return values */
53 #define NO_MATCH -1
54 #define EXACT_MATCH 1
55 #define INEXACT_MATCH 2
57 /* for prom io operations */
58 #define BUFSIZE 4096
59 #define MAXPROPSIZE 256
60 #define MAXVALSIZE (BUFSIZE - MAXPROPSIZE - sizeof (uint_t))
62 /* prom_obp_vers() return values */
63 #define OBP_OF 0x4 /* versions OBP 3.x */
64 #define OBP_NO_ALIAS_NODE 0x8 /* No alias node */
66 /* for nftw call */
67 #define FT_DEPTH 15
69 /* default logical and physical device name space */
70 #define DEV "/dev"
71 #define DEVICES "/devices"
74 * internal structure declarations
77 /* for prom io functions */
78 typedef union {
79 char buf[BUFSIZE];
80 struct openpromio opp;
81 } Oppbuf;
83 /* used to manage lists of devices and aliases */
84 struct name_list {
85 char *name;
86 struct name_list *next;
90 * internal global data
93 /* global since nftw does not let you pass args to be updated */
94 static struct name_list **dev_list;
96 /* global since nftw does not let you pass args to be updated */
97 static struct boot_dev **bootdev_list;
99 /* mutex to protect bootdev_list and dev_list */
100 static mutex_t dev_lists_lk = DEFAULTMUTEX;
103 * internal function prototypes
106 static int prom_open(int);
107 static void prom_close(int);
108 static int is_openprom(int);
110 static int prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf);
111 static int alias_to_prom_dev(char *alias, char *ret_buf);
112 static int prom_srch_aliases_by_def(char *, struct name_list **,
113 struct name_list **, int);
114 static int prom_find_aliases_node(int fd);
115 static int prom_compare_devs(char *prom_dev1, char *prom_dev2);
116 static int _prom_strcmp(char *s1, char *s2);
117 static int prom_srch_node(int fd, char *prop_name, char *ret_buf);
118 static uint_t prom_next_node(int fd, uint_t node_id);
119 static uint_t prom_child_node(int fd, uint_t node_id);
121 static int prom_obp_vers(void);
123 static void parse_name(char *, char **, char **, char **);
124 static int process_bootdev(const char *, const char *, struct boot_dev ***);
125 static int process_minor_name(char *dev_path, const char *default_root);
126 static void options_override(char *prom_path, char *alias_name);
127 static int devfs_phys_to_logical(struct boot_dev **bootdev_array,
128 const int array_size, const char *default_root);
129 static int check_logical_dev(const char *, const struct stat *, int,
130 struct FTW *);
131 static struct boot_dev *alloc_bootdev(char *);
132 static void free_name_list(struct name_list *list, int free_name);
133 static int insert_alias_list(struct name_list **list,
134 char *alias_name);
135 static int get_boot_dev_var(struct openpromio *opp);
136 static int set_boot_dev_var(struct openpromio *opp, char *bootdev);
137 static int devfs_prom_to_dev_name(char *prom_path, char *dev_path);
138 static int devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len);
141 * frees a list of paths from devfs_get_prom_name_list
143 static void
144 prom_list_free(char **prom_list)
146 int i = 0;
148 if (!prom_list)
149 return;
151 while (prom_list[i]) {
152 free(prom_list[i]);
153 i++;
155 free(prom_list);
158 static int
159 devfs_get_prom_name_list(const char *dev_name, char ***prom_list)
161 char *prom_path = NULL;
162 int count = 0; /* # of slots we will need in prom_list */
163 int ret, i, len;
164 char **list;
165 char *ptr;
167 if (dev_name == NULL)
168 return (DEVFS_INVAL);
169 if (*dev_name != '/')
170 return (DEVFS_INVAL);
171 if (prom_list == NULL)
172 return (DEVFS_INVAL);
175 * make sure we are on a machine which supports a prom
176 * and we have permission to use /dev/openprom
178 if ((ret = prom_obp_vers()) < 0)
179 return (ret);
180 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
181 return (DEVFS_NOMEM);
183 * get the prom path name
185 ret = devfs_dev_to_prom_names((char *)dev_name, prom_path, MAXVALSIZE);
186 if (ret < 0) {
187 free(prom_path);
188 return (ret);
190 /* deal with list of names */
191 for (i = 0; i < ret; i++)
192 if (prom_path[i] == '\0')
193 count++;
195 if ((list = (char **)calloc(count + 1, sizeof (char *))) == NULL) {
196 free(prom_path);
197 return (DEVFS_NOMEM);
200 ptr = prom_path;
201 for (i = 0; i < count; i++) {
202 len = strlen(ptr) + 1;
203 if ((list[i] = (char *)malloc(len)) == NULL) {
204 free(prom_path);
205 free(list);
206 return (DEVFS_NOMEM);
208 (void) snprintf(list[i], len, "%s", ptr);
209 ptr += len;
212 free(prom_path);
214 *prom_list = list;
215 return (0);
219 * retrieve the list of prom representations for a given device name
220 * the list will be sorted in the following order: exact aliases,
221 * inexact aliases, prom device path name. If multiple matches occur
222 * for exact or inexact aliases, then these are sorted in collating
223 * order. The list is returned in prom_list
225 * the list may be restricted by specifying the correct flags in options.
228 devfs_get_prom_names(const char *dev_name, uint_t options, char ***prom_list)
230 char *prom_path = NULL;
231 int count = 0; /* # of slots we will need in prom_list */
232 char **alias_list = NULL;
233 char **list;
234 int ret;
236 if (dev_name == NULL) {
237 return (DEVFS_INVAL);
239 if (*dev_name != '/') {
240 return (DEVFS_INVAL);
242 if (prom_list == NULL) {
243 return (DEVFS_INVAL);
246 * make sure we are on a machine which supports a prom
247 * and we have permission to use /dev/openprom
249 if ((ret = prom_obp_vers()) < 0) {
250 return (ret);
252 if ((prom_path = (char *)malloc(MAXPATHLEN)) == NULL) {
253 return (DEVFS_NOMEM);
256 * get the prom path name
258 ret = devfs_dev_to_prom_name((char *)dev_name, prom_path);
259 if (ret < 0) {
260 free(prom_path);
261 return (ret);
263 /* get the list of aliases (exact and inexact) */
264 if ((ret = prom_dev_to_alias(prom_path, options, &alias_list)) < 0) {
265 free(prom_path);
266 return (ret);
268 /* now figure out how big the return array must be */
269 if (alias_list != NULL) {
270 while (alias_list[count] != NULL) {
271 count++;
274 if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
275 count++; /* # of slots we will need in prom_list */
277 count++; /* for the null terminator */
279 /* allocate space for the list */
280 if ((list = (char **)calloc(count, sizeof (char *))) == NULL) {
281 count = 0;
282 while ((alias_list) && (alias_list[count] != NULL)) {
283 free(alias_list[count]);
284 count++;
286 free(alias_list);
287 free(prom_path);
288 return (DEVFS_NOMEM);
290 /* fill in the array and free the name list of aliases. */
291 count = 0;
292 while ((alias_list) && (alias_list[count] != NULL)) {
293 list[count] = alias_list[count];
294 count++;
296 if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
297 list[count] = prom_path;
299 if (alias_list != NULL) {
300 free(alias_list);
302 *prom_list = list;
303 return (0);
307 * Get a list prom-path translations for a solaris device.
309 * Returns the number of and all OBP paths and alias variants that
310 * reference the Solaris device path passed in.
313 devfs_get_all_prom_names(const char *solaris_path, uint_t flags,
314 struct devfs_prom_path **paths)
316 int ret, len, i, count = 0;
317 char *ptr, *prom_path;
318 struct devfs_prom_path *cur = NULL, *new;
320 if ((ret = prom_obp_vers()) < 0)
321 return (ret);
322 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
323 return (DEVFS_NOMEM);
325 if ((ret = devfs_dev_to_prom_names((char *)solaris_path,
326 prom_path, MAXVALSIZE)) < 0) {
327 free(prom_path);
328 return (ret);
331 for (i = 0; i < ret; i++)
332 if (prom_path[i] == '\0')
333 count++;
335 *paths = NULL;
336 ptr = prom_path;
337 for (i = 0; i < count; i++) {
338 if ((new = (struct devfs_prom_path *)calloc(
339 sizeof (struct devfs_prom_path), 1)) == NULL) {
340 free(prom_path);
341 devfs_free_all_prom_names(*paths);
342 return (DEVFS_NOMEM);
345 if (cur == NULL)
346 *paths = new;
347 else
348 cur->next = new;
349 cur = new;
351 len = strlen(ptr) + 1;
352 if ((cur->obp_path = (char *)calloc(len, 1)) == NULL) {
353 free(prom_path);
354 devfs_free_all_prom_names(*paths);
355 return (DEVFS_NOMEM);
358 (void) snprintf(cur->obp_path, len, "%s", ptr);
359 ptr += len;
360 if ((ret = prom_dev_to_alias(cur->obp_path, flags,
361 &(cur->alias_list))) < 0) {
362 free(prom_path);
363 devfs_free_all_prom_names(*paths);
364 return (ret);
368 free(prom_path);
369 return (count);
372 void
373 devfs_free_all_prom_names(struct devfs_prom_path *paths)
375 int i;
377 if (paths == NULL)
378 return;
380 devfs_free_all_prom_names(paths->next);
382 free(paths->obp_path);
384 if (paths->alias_list != NULL) {
385 for (i = 0; paths->alias_list[i] != NULL; i++)
386 free(paths->alias_list[i]);
388 free(paths->alias_list);
391 free(paths);
395 * Accepts a device name as an input argument. Uses this to set the
396 * boot-device (or like) variable
398 * By default, this routine prepends to the list and converts the
399 * logical device name to its most compact prom representation.
400 * Available options include: converting the device name to a prom
401 * path name (but not an alias) or performing no conversion at all;
402 * overwriting the existing contents of boot-device rather than
403 * prepending.
406 devfs_bootdev_set_list(const char *dev_name, const uint_t options)
408 char *prom_path;
409 char *new_bootdev;
410 char *ptr;
411 char **alias_list = NULL;
412 char **prom_list = NULL;
413 Oppbuf oppbuf;
414 struct openpromio *opp = &(oppbuf.opp);
415 int ret, len, i, j;
417 if (devfs_bootdev_modifiable() != 0) {
418 return (DEVFS_NOTSUP);
420 if (dev_name == NULL) {
421 return (DEVFS_INVAL);
423 if (strlen(dev_name) >= MAXPATHLEN)
424 return (DEVFS_INVAL);
426 if ((*dev_name != '/') && !(options & BOOTDEV_LITERAL)) {
427 return (DEVFS_INVAL);
429 if ((options & BOOTDEV_LITERAL) && (options & BOOTDEV_PROMDEV)) {
430 return (DEVFS_INVAL);
433 * if we are prepending, make sure that this obp rev
434 * supports multiple boot device entries.
436 ret = prom_obp_vers();
437 if (ret < 0) {
438 return (ret);
441 if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) {
442 return (DEVFS_NOMEM);
444 if (options & BOOTDEV_LITERAL) {
445 (void) strcpy(prom_path, dev_name);
446 } else {
447 /* need to convert to prom representation */
448 ret = devfs_get_prom_name_list(dev_name, &prom_list);
449 if (ret < 0) {
450 free(prom_path);
451 return (ret);
454 len = MAXVALSIZE;
455 i = 0;
456 ptr = prom_path;
457 while (prom_list && prom_list[i]) {
458 if (!(options & BOOTDEV_PROMDEV)) {
459 ret = prom_dev_to_alias(prom_list[i], 0,
460 &alias_list);
461 if (ret < 0) {
462 free(prom_path);
463 prom_list_free(prom_list);
464 return (ret);
466 if ((alias_list != NULL) &&
467 (alias_list[0] != NULL)) {
468 (void) snprintf(ptr, len, "%s ",
469 alias_list[0]);
470 for (ret = 0; alias_list[ret] != NULL;
471 ret++)
472 free(alias_list[ret]);
473 } else {
474 (void) snprintf(ptr, len, "%s ",
475 prom_list[i]);
477 free(alias_list);
478 } else {
479 (void) snprintf(ptr, len, "%s ", prom_list[i]);
481 j = strlen(ptr);
482 len -= j;
483 ptr += j;
484 i++;
486 ptr--;
487 *ptr = '\0';
489 prom_list_free(prom_list);
491 if (options & BOOTDEV_OVERWRITE) {
492 new_bootdev = prom_path;
493 } else {
494 /* retrieve the current value of boot-device */
495 ret = get_boot_dev_var(opp);
496 if (ret < 0) {
497 free(prom_path);
498 return (ret);
500 /* prepend new entry - deal with duplicates */
501 new_bootdev = (char *)malloc(strlen(opp->oprom_array)
502 + strlen(prom_path) + 2);
503 if (new_bootdev == NULL) {
504 free(prom_path);
505 return (DEVFS_NOMEM);
507 (void) strcpy(new_bootdev, prom_path);
508 if (opp->oprom_size > 0) {
509 for (ptr = strtok(opp->oprom_array, " "); ptr != NULL;
510 ptr = strtok(NULL, " ")) {
511 /* we strip out duplicates */
512 if (strcmp(prom_path, ptr) == 0) {
513 continue;
515 (void) strcat(new_bootdev, " ");
516 (void) strcat(new_bootdev, ptr);
521 /* now set the new value */
522 ret = set_boot_dev_var(opp, new_bootdev);
524 if (options & BOOTDEV_OVERWRITE) {
525 free(prom_path);
526 } else {
527 free(new_bootdev);
528 free(prom_path);
531 return (ret);
535 * sets the string bootdev as the new value for boot-device
537 static int
538 set_boot_dev_var(struct openpromio *opp, char *bootdev)
540 int prom_fd;
541 int i;
542 int ret;
543 char *valbuf;
544 char *save_bootdev;
545 char *bootdev_variables[] = {
546 "boot-device",
547 "bootdev",
548 "boot-from",
549 NULL
551 int found = 0;
552 int *ip = (int *)((void *)opp->oprom_array);
554 /* query the prom */
555 prom_fd = prom_open(O_RDWR);
556 if (prom_fd < 0) {
557 return (prom_fd);
560 /* get the diagnostic-mode? property */
561 (void) strcpy(opp->oprom_array, "diagnostic-mode?");
562 opp->oprom_size = MAXVALSIZE;
563 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
564 if ((opp->oprom_size > 0) &&
565 (strcmp(opp->oprom_array, "true") == 0)) {
566 prom_close(prom_fd);
567 return (DEVFS_ERR);
570 /* get the diag-switch? property */
571 (void) strcpy(opp->oprom_array, "diag-switch?");
572 opp->oprom_size = MAXVALSIZE;
573 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
574 if ((opp->oprom_size > 0) &&
575 (strcmp(opp->oprom_array, "true") == 0)) {
576 prom_close(prom_fd);
577 return (DEVFS_ERR);
581 * look for one of the following properties in order:
582 * boot-device
583 * bootdev
584 * boot-from
586 * Use the first one that we find.
588 *ip = 0;
589 opp->oprom_size = MAXPROPSIZE;
590 while ((opp->oprom_size != 0) && (!found)) {
591 opp->oprom_size = MAXPROPSIZE;
592 if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
593 break;
595 for (i = 0; bootdev_variables[i] != NULL; i++) {
596 if (strcmp(opp->oprom_array, bootdev_variables[i])
597 == 0) {
598 found = 1;
599 break;
603 if (found) {
604 (void) strcpy(opp->oprom_array, bootdev_variables[i]);
605 opp->oprom_size = MAXVALSIZE;
606 if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
607 prom_close(prom_fd);
608 return (DEVFS_NOTSUP);
610 } else {
611 prom_close(prom_fd);
612 return (DEVFS_NOTSUP);
615 /* save the old copy in case we fail */
616 if ((save_bootdev = strdup(opp->oprom_array)) == NULL) {
617 prom_close(prom_fd);
618 return (DEVFS_NOMEM);
620 /* set up the new value of boot-device */
621 (void) strcpy(opp->oprom_array, bootdev_variables[i]);
622 valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
623 (void) strcpy(valbuf, bootdev);
625 opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
627 if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
628 free(save_bootdev);
629 prom_close(prom_fd);
630 return (DEVFS_ERR);
634 * now read it back to make sure it took
636 (void) strcpy(opp->oprom_array, bootdev_variables[i]);
637 opp->oprom_size = MAXVALSIZE;
638 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
639 if (_prom_strcmp(opp->oprom_array, bootdev) == 0) {
640 /* success */
641 free(save_bootdev);
642 prom_close(prom_fd);
643 return (0);
645 /* deal with setting it to "" */
646 if ((strlen(bootdev) == 0) && (opp->oprom_size == 0)) {
647 /* success */
648 free(save_bootdev);
649 prom_close(prom_fd);
650 return (0);
654 * something did not take - write out the old value and
655 * hope that we can restore things...
657 * unfortunately, there is no way for us to differentiate
658 * whether we exceeded the maximum number of characters
659 * allowable. The limit varies from prom rev to prom
660 * rev, and on some proms, when the limit is
661 * exceeded, whatever was in the
662 * boot-device variable becomes unreadable.
664 * so if we fail, we will assume we ran out of room. If we
665 * not able to restore the original setting, then we will
666 * return DEVFS_ERR instead.
668 ret = DEVFS_LIMIT;
669 (void) strcpy(opp->oprom_array, bootdev_variables[i]);
670 valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
671 (void) strcpy(valbuf, save_bootdev);
673 opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
675 if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
676 ret = DEVFS_ERR;
678 free(save_bootdev);
679 prom_close(prom_fd);
680 return (ret);
683 * retrieve the current value for boot-device
685 static int
686 get_boot_dev_var(struct openpromio *opp)
688 int prom_fd;
689 int i;
690 char *bootdev_variables[] = {
691 "boot-device",
692 "bootdev",
693 "boot-from",
694 NULL
696 int found = 0;
697 int *ip = (int *)((void *)opp->oprom_array);
699 /* query the prom */
700 prom_fd = prom_open(O_RDONLY);
701 if (prom_fd < 0) {
702 return (prom_fd);
705 /* get the diagnostic-mode? property */
706 (void) strcpy(opp->oprom_array, "diagnostic-mode?");
707 opp->oprom_size = MAXVALSIZE;
708 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
709 if ((opp->oprom_size > 0) &&
710 (strcmp(opp->oprom_array, "true") == 0)) {
711 prom_close(prom_fd);
712 return (DEVFS_ERR);
715 /* get the diag-switch? property */
716 (void) strcpy(opp->oprom_array, "diag-switch?");
717 opp->oprom_size = MAXVALSIZE;
718 if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
719 if ((opp->oprom_size > 0) &&
720 (strcmp(opp->oprom_array, "true") == 0)) {
721 prom_close(prom_fd);
722 return (DEVFS_ERR);
726 * look for one of the following properties in order:
727 * boot-device
728 * bootdev
729 * boot-from
731 * Use the first one that we find.
733 *ip = 0;
734 opp->oprom_size = MAXPROPSIZE;
735 while ((opp->oprom_size != 0) && (!found)) {
736 opp->oprom_size = MAXPROPSIZE;
737 if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
738 break;
740 for (i = 0; bootdev_variables[i] != NULL; i++) {
741 if (strcmp(opp->oprom_array, bootdev_variables[i])
742 == 0) {
743 found = 1;
744 break;
748 if (found) {
749 (void) strcpy(opp->oprom_array, bootdev_variables[i]);
750 opp->oprom_size = MAXVALSIZE;
751 if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
752 prom_close(prom_fd);
753 return (DEVFS_ERR);
755 /* boot-device exists but contains nothing */
756 if (opp->oprom_size == 0) {
757 *opp->oprom_array = '\0';
759 } else {
760 prom_close(prom_fd);
761 return (DEVFS_NOTSUP);
763 prom_close(prom_fd);
764 return (0);
768 * Just return error from it, at least for now.
769 * This is part of private interface, no consumers on gate.
771 static int
772 find_x86_boot_device(struct openpromio *opp)
774 return (DEVFS_ERR);
778 * retrieve the list of entries in the boot-device configuration
779 * variable. An array of boot_dev structs will be created, one entry
780 * for each device name in the boot-device variable. Each entry
781 * in the array will contain the logical device representation of the
782 * boot-device entry, if any.
784 * default_root. if set, is used to locate logical device entries in
785 * directories other than /dev
788 devfs_bootdev_get_list(const char *default_root,
789 struct boot_dev ***bootdev_list)
791 Oppbuf oppbuf;
792 struct openpromio *opp = &(oppbuf.opp);
793 int i;
794 struct boot_dev **tmp_list;
796 if (default_root == NULL) {
797 default_root = "";
798 } else if (*default_root != '/') {
799 return (DEVFS_INVAL);
802 if (bootdev_list == NULL) {
803 return (DEVFS_INVAL);
806 /* get the boot-device variable */
807 #if defined(sparc)
808 i = get_boot_dev_var(opp);
809 #else
810 i = find_x86_boot_device(opp);
811 #endif
812 if (i < 0) {
813 return (i);
815 /* now try to translate each entry to a logical device. */
816 i = process_bootdev(opp->oprom_array, default_root, &tmp_list);
817 if (i == 0) {
818 *bootdev_list = tmp_list;
819 return (0);
820 } else {
821 return (i);
826 * loop thru the list of entries in a boot-device configuration
827 * variable.
829 static int
830 process_bootdev(const char *bootdevice, const char *default_root,
831 struct boot_dev ***list)
833 int i;
834 char *entry, *ptr;
835 char prom_path[MAXPATHLEN];
836 char ret_buf[MAXPATHLEN];
837 struct boot_dev **bootdev_array;
838 int num_entries = 0;
839 int found = 0;
840 int vers;
842 if ((entry = (char *)malloc(strlen(bootdevice) + 1)) == NULL) {
843 return (DEVFS_NOMEM);
845 /* count the number of entries */
846 (void) strcpy(entry, bootdevice);
847 for (ptr = strtok(entry, " "); ptr != NULL;
848 ptr = strtok(NULL, " ")) {
849 num_entries++;
851 (void) strcpy(entry, bootdevice);
853 bootdev_array = (struct boot_dev **)
854 calloc((size_t)num_entries + 1, sizeof (struct boot_dev *));
856 if (bootdev_array == NULL) {
857 free(entry);
858 return (DEVFS_NOMEM);
861 vers = prom_obp_vers();
862 if (vers < 0) {
863 free(entry);
864 return (vers);
867 /* for each entry in boot-device, do... */
868 for (ptr = strtok(entry, " "), i = 0; ptr != NULL;
869 ptr = strtok(NULL, " "), i++) {
871 if ((bootdev_array[i] = alloc_bootdev(ptr)) == NULL) {
872 devfs_bootdev_free_list(bootdev_array);
873 free(entry);
874 return (DEVFS_NOMEM);
878 * prom boot-device may be aliased, so we need to do
879 * the necessary prom alias to dev translation.
881 if (*ptr != '/') {
882 if (alias_to_prom_dev(ptr, prom_path) < 0) {
883 continue;
885 } else {
886 (void) strcpy(prom_path, ptr);
889 /* now we have a prom device path - convert to a devfs name */
890 if (devfs_prom_to_dev_name(prom_path, ret_buf) < 0) {
891 continue;
894 /* append any default minor names necessary */
895 if (process_minor_name(ret_buf, default_root) < 0) {
896 continue;
898 found = 1;
901 * store the physical device path for now - when
902 * we are all done with the entries, we will convert
903 * these to their logical device name equivalents
905 bootdev_array[i]->bootdev_trans[0] = strdup(ret_buf);
908 * Convert all of the boot-device entries that translated to a
909 * physical device path in /devices to a logical device path
910 * in /dev (note that there may be several logical device paths
911 * associated with a single physical device path - return them all
913 if (found) {
914 if (devfs_phys_to_logical(bootdev_array, num_entries,
915 default_root) < 0) {
916 devfs_bootdev_free_list(bootdev_array);
917 bootdev_array = NULL;
920 free(entry);
921 *list = bootdev_array;
922 return (0);
926 * We may get a device path from the prom that has no minor name
927 * information included in it. Since this device name will not
928 * correspond directly to a physical device in /devices, we do our
929 * best to append what the default minor name should be and try this.
931 * For sparc: we append slice 0 (:a).
932 * For x86: we append fdisk partition 0 (:q).
934 static int
935 process_minor_name(char *dev_path, const char *root)
937 char *cp;
938 #if defined(sparc)
939 const char *default_minor_name = "a";
940 #else
941 const char *default_minor_name = "q";
942 #endif
943 int n;
944 struct stat stat_buf;
945 char path[MAXPATHLEN];
947 (void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
949 * if the device file already exists as given to us, there
950 * is nothing to do but return.
952 if (stat(path, &stat_buf) == 0) {
953 return (0);
956 * if there is no ':' after the last '/' character, or if there is
957 * a ':' with no specifier, append the default segment specifier
958 * ; if there is a ':' followed by a digit, this indicates
959 * a partition number (which does not map into the /devices name
960 * space), so strip the number and replace it with the letter
961 * that represents the partition index
963 if ((cp = strrchr(dev_path, '/')) != NULL) {
964 if ((cp = strchr(cp, ':')) == NULL) {
965 (void) strcat(dev_path, ":");
966 (void) strcat(dev_path, default_minor_name);
967 } else if (*++cp == '\0') {
968 (void) strcat(dev_path, default_minor_name);
969 } else if (isdigit(*cp)) {
970 n = atoi(cp);
971 /* make sure to squash the digit */
972 *cp = '\0';
973 switch (n) {
974 case 0: (void) strcat(dev_path, "q");
975 break;
976 case 1: (void) strcat(dev_path, "r");
977 break;
978 case 2: (void) strcat(dev_path, "s");
979 break;
980 case 3: (void) strcat(dev_path, "t");
981 break;
982 case 4: (void) strcat(dev_path, "u");
983 break;
984 default: (void) strcat(dev_path, "a");
985 break;
990 * see if we can find something now.
992 (void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
994 if (stat(path, &stat_buf) == 0) {
995 return (0);
996 } else {
997 return (-1);
1002 * for each entry in bootdev_array, convert the physical device
1003 * representation of the boot-device entry to one or more logical device
1004 * entries. We use the hammer method - walk through the logical device
1005 * name space looking for matches (/dev). We use nftw to do this.
1007 static int
1008 devfs_phys_to_logical(struct boot_dev **bootdev_array, const int array_size,
1009 const char *default_root)
1011 int walk_flags = FTW_PHYS | FTW_MOUNT;
1012 char *full_path;
1013 struct name_list *list;
1014 int count, i;
1015 char **dev_name_array;
1016 size_t default_root_len;
1017 char *dev_dir = DEV;
1018 int len;
1020 if (array_size < 0) {
1021 return (-1);
1024 if (bootdev_array == NULL) {
1025 return (-1);
1027 if (default_root == NULL) {
1028 return (-1);
1030 default_root_len = strlen(default_root);
1031 if ((default_root_len != 0) && (*default_root != '/')) {
1032 return (-1);
1035 /* short cut for an empty array */
1036 if (*bootdev_array == NULL) {
1037 return (0);
1040 /* tell nftw where to start (default: /dev) */
1041 len = default_root_len + strlen(dev_dir) + 1;
1042 if ((full_path = (char *)malloc(len)) == NULL) {
1043 return (-1);
1047 * if the default root path is terminated with a /, we have to
1048 * make sure we don't end up with one too many slashes in the
1049 * path we are building.
1051 if ((default_root_len > (size_t)0) &&
1052 (default_root[default_root_len - 1] == '/')) {
1053 (void) snprintf(full_path, len, "%s%s", default_root,
1054 &dev_dir[1]);
1055 } else {
1056 (void) snprintf(full_path, len, "%s%s", default_root, dev_dir);
1060 * we need to muck with global data to make nftw work
1061 * so single thread access
1063 (void) mutex_lock(&dev_lists_lk);
1066 * set the global vars bootdev_list and dev_list for use by nftw
1067 * dev_list is an array of lists - one for each boot-device
1068 * entry. The nftw function will create a list of logical device
1069 * entries for each boot-device and put all of the lists in
1070 * dev_list.
1072 dev_list = (struct name_list **)
1073 calloc(array_size, sizeof (struct name_list *));
1074 if (dev_list == NULL) {
1075 free(full_path);
1076 (void) mutex_unlock(&dev_lists_lk);
1077 return (-1);
1079 bootdev_list = bootdev_array;
1081 if (nftw(full_path, check_logical_dev, FT_DEPTH, walk_flags) == -1) {
1082 bootdev_list = NULL;
1083 free(full_path);
1084 for (i = 0; i < array_size; i++) {
1085 free_name_list(dev_list[i], 1);
1087 /* don't free dev_list here because it's been handed off */
1088 dev_list = NULL;
1089 (void) mutex_unlock(&dev_lists_lk);
1090 return (-1);
1094 * now we have a filled in dev_list. So for each logical device
1095 * list in dev_list, count the number of entries in the list,
1096 * create an array of strings of logical devices, and save in the
1097 * corresponding boot_dev structure.
1099 for (i = 0; i < array_size; i++) {
1100 /* get the next list */
1101 list = dev_list[i];
1102 count = 0;
1104 /* count the number of entries in the list */
1105 while (list != NULL) {
1106 count++;
1107 list = list->next;
1109 if ((dev_name_array =
1110 (char **)malloc((count + 1) * sizeof (char *)))
1111 == NULL) {
1112 continue;
1114 list = dev_list[i];
1115 count = 0;
1117 /* fill in the array */
1118 while (list != NULL) {
1119 dev_name_array[count] = list->name;
1120 count++;
1121 list = list->next;
1125 * null terminate the array
1127 dev_name_array[count] = NULL;
1128 if ((bootdev_array[i] != NULL) && (bootdev_array[i]->
1129 bootdev_trans[0] != NULL)) {
1130 free(bootdev_array[i]->bootdev_trans[0]);
1132 if (bootdev_array[i] != NULL) {
1133 free(bootdev_array[i]->bootdev_trans);
1134 bootdev_array[i]->bootdev_trans = dev_name_array;
1137 bootdev_list = NULL;
1138 free(full_path);
1139 for (i = 0; i < array_size; i++) {
1140 free_name_list(dev_list[i], 0);
1142 free(dev_list);
1143 dev_list = NULL;
1144 (void) mutex_unlock(&dev_lists_lk);
1145 return (0);
1148 * nftw function
1149 * for a logical dev entry, it walks the list of boot-devices and
1150 * sees if there are any matches. If so, it saves the logical device
1151 * name off in the appropriate list in dev_list
1153 /* ARGSUSED */
1154 static int
1155 check_logical_dev(const char *node, const struct stat *node_stat, int flags,
1156 struct FTW *ftw_info)
1158 char link_buf[MAXPATHLEN];
1159 int link_buf_len;
1160 char *name;
1161 struct name_list *dev;
1162 char *physdev;
1163 int i;
1165 if (flags != FTW_SL) {
1166 return (0);
1169 if ((link_buf_len = readlink(node, (void *)link_buf, MAXPATHLEN))
1170 == -1) {
1171 return (0);
1173 link_buf[link_buf_len] = '\0';
1174 if ((name = strstr(link_buf, DEVICES)) == NULL) {
1175 return (0);
1177 name = (char *)(name + strlen(DEVICES));
1179 for (i = 0; bootdev_list[i] != NULL; i++) {
1180 if (bootdev_list[i]->bootdev_trans[0] == NULL) {
1181 continue;
1184 * compare the contents of the link with the physical
1185 * device representation of this boot device
1187 physdev = bootdev_list[i]->bootdev_trans[0];
1188 if ((strcmp(name, physdev) == 0) &&
1189 (strlen(name) == strlen(physdev))) {
1190 if ((dev = (struct name_list *)
1191 malloc(sizeof (struct name_list))) == NULL) {
1192 return (-1);
1194 if ((dev->name = strdup(node)) == NULL) {
1195 free(dev);
1196 return (-1);
1198 if (dev_list[i] == NULL) {
1199 dev_list[i] = dev;
1200 dev_list[i]->next = NULL;
1201 } else {
1202 dev->next = dev_list[i];
1203 dev_list[i] = dev;
1207 return (0);
1211 * frees a list of boot_dev struct pointers
1213 void
1214 devfs_bootdev_free_list(struct boot_dev **array)
1216 int i = 0;
1217 int j;
1219 if (array == NULL) {
1220 return;
1223 while (array[i] != NULL) {
1224 free(array[i]->bootdev_element);
1225 j = 0;
1226 while (array[i]->bootdev_trans[j] != NULL) {
1227 free(array[i]->bootdev_trans[j++]);
1229 free(array[i]->bootdev_trans);
1230 free(array[i]);
1231 i++;
1233 free(array);
1236 * allocates a boot_dev struct and fills in the bootdev_element portion
1238 static struct boot_dev *
1239 alloc_bootdev(char *entry_name)
1241 struct boot_dev *entry;
1243 entry = (struct boot_dev *)calloc(1, sizeof (struct boot_dev));
1245 if (entry == NULL) {
1246 return (NULL);
1248 if ((entry->bootdev_element = strdup(entry_name)) == NULL) {
1249 free(entry);
1250 return (NULL);
1253 * Allocate room for 1 name and a null terminator - the caller of
1254 * this function will need the first slot right away.
1256 if ((entry->bootdev_trans = (char **)calloc(2, sizeof (char *)))
1257 == NULL) {
1258 free(entry->bootdev_element);
1259 free(entry);
1260 return (NULL);
1262 return (entry);
1266 * will come back with a concatenated list of paths
1269 devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len)
1271 Oppbuf oppbuf;
1272 struct openpromio *opp = &(oppbuf.opp);
1273 int prom_fd;
1274 int ret = DEVFS_INVAL;
1275 int i;
1277 if (prom_path == NULL) {
1278 return (DEVFS_INVAL);
1280 if (dev_path == NULL) {
1281 return (DEVFS_INVAL);
1283 if (strlen(dev_path) >= MAXPATHLEN)
1284 return (DEVFS_INVAL);
1286 if (*dev_path != '/')
1287 return (DEVFS_INVAL);
1289 prom_fd = prom_open(O_RDONLY);
1290 if (prom_fd < 0) {
1291 return (prom_fd);
1294 /* query the prom */
1295 (void) snprintf(opp->oprom_array, MAXVALSIZE, "%s", dev_path);
1296 opp->oprom_size = MAXVALSIZE;
1298 if (ioctl(prom_fd, OPROMDEV2PROMNAME, opp) == 0) {
1299 prom_close(prom_fd);
1301 /* return the prom path in prom_path */
1303 i = len - opp->oprom_size;
1304 if (i < 0) {
1305 bcopy(opp->oprom_array, prom_path, len);
1306 prom_path[len - 1] = '\0';
1307 return (len);
1308 } else {
1309 bcopy(opp->oprom_array, prom_path, len);
1310 return (opp->oprom_size);
1314 * either the prom does not support this ioctl or the argument
1315 * was invalid.
1317 if (errno == ENXIO) {
1318 ret = DEVFS_NOTSUP;
1320 prom_close(prom_fd);
1321 return (ret);
1325 * Convert a physical or logical device name to a name the prom would
1326 * understand. Fail if this platform does not support a prom or if
1327 * the device does not correspond to a valid prom device.
1328 * dev_path should be the name of a device in the logical or
1329 * physical device namespace.
1330 * prom_path is the prom version of the device name
1331 * prom_path must be large enough to contain the result and is
1332 * supplied by the user.
1334 * This routine only supports converting leaf device paths
1337 devfs_dev_to_prom_name(char *dev_path, char *prom_path)
1339 int rval;
1341 rval = devfs_dev_to_prom_names(dev_path, prom_path, MAXPATHLEN);
1343 if (rval < 0)
1344 return (rval);
1345 else
1346 return (0);
1350 * Use the openprom driver's OPROMPATH2DRV ioctl to convert a devfs
1351 * path to a driver name.
1352 * devfs_path - the pathname of interest. This must be the physcical device
1353 * path with the mount point prefix (ie. /devices) stripped off.
1354 * drv_buf - user supplied buffer - the driver name will be stored here.
1356 * If the prom lookup fails, we return the name of the last component in
1357 * the pathname. This routine is useful for looking up driver names
1358 * associated with generically named devices.
1360 * This routine returns driver names that have aliases resolved.
1363 devfs_path_to_drv(char *devfs_path, char *drv_buf)
1365 Oppbuf oppbuf;
1366 struct openpromio *opp = &(oppbuf.opp);
1367 char *slash, *colon, *dev_addr;
1368 char driver_path[MAXPATHLEN];
1369 int prom_fd;
1371 if (drv_buf == NULL) {
1372 return (-1);
1374 if (devfs_path == NULL) {
1375 return (-1);
1378 if (strlen(devfs_path) >= MAXPATHLEN)
1379 return (-1);
1381 if (*devfs_path != '/')
1382 return (-1);
1385 /* strip off any minor node info at the end of the path */
1386 (void) strcpy(driver_path, devfs_path);
1387 slash = strrchr(driver_path, '/');
1388 if (slash == NULL)
1389 return (-1);
1390 colon = strrchr(slash, ':');
1391 if (colon != NULL)
1392 *colon = '\0';
1394 /* query the prom */
1395 if ((prom_fd = prom_open(O_RDONLY)) >= 0) {
1396 (void) strcpy(opp->oprom_array, driver_path);
1397 opp->oprom_size = MAXVALSIZE;
1399 if (ioctl(prom_fd, OPROMPATH2DRV, opp) == 0) {
1400 prom_close(prom_fd);
1401 /* return the driver name in drv_buf */
1402 (void) strcpy(drv_buf, opp->oprom_array);
1403 return (0);
1405 prom_close(prom_fd);
1406 } else if (prom_fd != DEVFS_NOTSUP)
1407 return (-1);
1409 * If we get here, then either:
1410 * 1. this platform does not support an openprom driver
1411 * 2. we were asked to look up a device the prom does
1412 * not know about (e.g. a pseudo device)
1413 * In this case, we use the last component of the devfs path
1414 * name and try to derive the driver name
1417 /* use the last component of devfs_path as the driver name */
1418 if ((dev_addr = strrchr(slash, '@')) != NULL)
1419 *dev_addr = '\0';
1420 slash++;
1422 /* use opp->oprom_array as a buffer */
1423 (void) strcpy(opp->oprom_array, slash);
1424 if (devfs_resolve_aliases(opp->oprom_array) == NULL)
1425 return (-1);
1426 (void) strcpy(drv_buf, opp->oprom_array);
1427 return (0);
1431 * These modctl calls do the equivalent of:
1432 * ddi_name_to_major()
1433 * ddi_major_to_name()
1434 * This results in two things:
1435 * - the driver name must be a valid one
1436 * - any driver aliases are resolved.
1437 * drv is overwritten with the resulting name.
1439 char *
1440 devfs_resolve_aliases(char *drv)
1442 major_t maj;
1443 char driver_name[MAXNAMELEN + 1];
1445 if (drv == NULL) {
1446 return (NULL);
1449 if (modctl(MODGETMAJBIND, drv, strlen(drv) + 1, &maj) < 0)
1450 return (NULL);
1451 else if (modctl(MODGETNAME, driver_name, sizeof (driver_name), &maj)
1452 < 0) {
1453 return (NULL);
1454 } else {
1455 (void) strcpy(drv, driver_name);
1456 return (drv);
1461 * open the openprom device. and verify that we are on an
1462 * OBP/1275 OF machine. If the prom does not exist, then we
1463 * return an error
1465 static int
1466 prom_open(int oflag)
1468 int prom_fd = -1;
1469 char *promdev = "/dev/openprom";
1471 while (prom_fd < 0) {
1472 if ((prom_fd = open(promdev, oflag)) < 0) {
1473 if (errno == EAGAIN) {
1474 (void) sleep(5);
1475 continue;
1477 if ((errno == ENXIO) || (errno == ENOENT)) {
1478 return (DEVFS_NOTSUP);
1480 if ((errno == EPERM) || (errno == EACCES)) {
1481 return (DEVFS_PERM);
1483 return (DEVFS_ERR);
1484 } else
1485 break;
1487 if (is_openprom(prom_fd))
1488 return (prom_fd);
1489 else {
1490 prom_close(prom_fd);
1491 return (DEVFS_ERR);
1495 static void
1496 prom_close(int prom_fd)
1498 (void) close(prom_fd);
1502 * is this an OBP/1275 OF machine?
1504 static int
1505 is_openprom(int prom_fd)
1507 Oppbuf oppbuf;
1508 struct openpromio *opp = &(oppbuf.opp);
1509 unsigned int i;
1511 opp->oprom_size = MAXVALSIZE;
1512 if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
1513 return (0);
1515 i = (unsigned int)((unsigned char)opp->oprom_array[0]);
1516 return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
1520 * convert a prom device path name to an equivalent physical device
1521 * path in the kernel.
1523 static int
1524 devfs_prom_to_dev_name(char *prom_path, char *dev_path)
1526 Oppbuf oppbuf;
1527 struct openpromio *opp = &(oppbuf.opp);
1528 int prom_fd;
1529 int ret = DEVFS_INVAL;
1531 if (dev_path == NULL) {
1532 return (DEVFS_INVAL);
1534 if (prom_path == NULL) {
1535 return (DEVFS_INVAL);
1537 if (strlen(prom_path) >= MAXPATHLEN)
1538 return (DEVFS_INVAL);
1540 if (*prom_path != '/') {
1541 return (DEVFS_INVAL);
1544 /* query the prom */
1545 prom_fd = prom_open(O_RDONLY);
1546 if (prom_fd < 0) {
1547 return (prom_fd);
1549 (void) strcpy(opp->oprom_array, prom_path);
1550 opp->oprom_size = MAXVALSIZE;
1552 if (ioctl(prom_fd, OPROMPROM2DEVNAME, opp) == 0) {
1553 prom_close(prom_fd);
1555 * success
1556 * return the prom path in prom_path
1558 (void) strcpy(dev_path, opp->oprom_array);
1559 return (0);
1562 * either the argument was not a valid name or the openprom
1563 * driver does not support this ioctl.
1565 if (errno == ENXIO) {
1566 ret = DEVFS_NOTSUP;
1568 prom_close(prom_fd);
1569 return (ret);
1572 * convert a prom device path to a list of equivalent alias names
1573 * If there is no alias node, or there are no aliases that correspond
1574 * to dev, we return empty lists.
1576 static int
1577 prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf)
1579 struct name_list *exact_list;
1580 struct name_list *inexact_list;
1581 struct name_list *list;
1582 char *ptr;
1583 char **array;
1584 int prom_fd;
1585 int count;
1586 int vers;
1588 vers = prom_obp_vers();
1589 if (vers < 0) {
1590 return (vers);
1593 if (dev == NULL) {
1594 return (DEVFS_INVAL);
1597 if (*dev != '/')
1598 return (DEVFS_INVAL);
1600 if (strlen(dev) >= MAXPATHLEN)
1601 return (DEVFS_INVAL);
1603 if ((ptr = strchr(dev, ':')) != NULL) {
1604 if (strchr(ptr, '/') != NULL)
1605 return (DEVFS_INVAL);
1607 if (ret_buf == NULL) {
1608 return (DEVFS_INVAL);
1611 prom_fd = prom_open(O_RDONLY);
1612 if (prom_fd < 0) {
1613 return (prom_fd);
1616 (void) prom_srch_aliases_by_def(dev, &exact_list,
1617 &inexact_list, prom_fd);
1619 prom_close(prom_fd);
1621 if ((options & BOOTDEV_NO_EXACT_ALIAS) != 0) {
1622 free_name_list(exact_list, 1);
1623 exact_list = NULL;
1626 if ((options & BOOTDEV_NO_INEXACT_ALIAS) != 0) {
1627 free_name_list(inexact_list, 1);
1628 inexact_list = NULL;
1631 count = 0;
1632 list = exact_list;
1633 while (list != NULL) {
1634 list = list->next;
1635 count++;
1637 list = inexact_list;
1638 while (list != NULL) {
1639 list = list->next;
1640 count++;
1643 if ((*ret_buf = (char **)malloc((count + 1) * sizeof (char *)))
1644 == NULL) {
1645 free_name_list(inexact_list, 1);
1646 free_name_list(exact_list, 1);
1647 return (DEVFS_NOMEM);
1650 array = *ret_buf;
1651 count = 0;
1652 list = exact_list;
1653 while (list != NULL) {
1654 array[count] = list->name;
1655 list = list->next;
1656 count++;
1658 list = inexact_list;
1659 while (list != NULL) {
1660 array[count] = list->name;
1661 list = list->next;
1662 count++;
1664 array[count] = NULL;
1665 free_name_list(inexact_list, 0);
1666 free_name_list(exact_list, 0);
1668 return (0);
1672 * determine the version of prom we are running on.
1673 * Also include any prom revision specific information.
1675 static int
1676 prom_obp_vers(void)
1678 Oppbuf oppbuf;
1679 struct openpromio *opp = &(oppbuf.opp);
1680 int prom_fd;
1681 static int version = 0;
1683 /* cache version */
1684 if (version > 0) {
1685 return (version);
1688 prom_fd = prom_open(O_RDONLY);
1689 if (prom_fd < 0) {
1690 return (prom_fd);
1693 opp->oprom_size = MAXVALSIZE;
1695 if ((ioctl(prom_fd, OPROMGETVERSION, opp)) < 0) {
1696 prom_close(prom_fd);
1697 return (DEVFS_ERR);
1699 prom_close(prom_fd);
1701 version |= OBP_OF;
1703 return (version);
1706 * search the aliases node by definition - compile a list of
1707 * alias names that are both exact and inexact matches.
1709 static int
1710 prom_srch_aliases_by_def(char *promdev_def, struct name_list **exact_list,
1711 struct name_list **inexact_list, int prom_fd)
1713 Oppbuf oppbuf;
1714 Oppbuf propdef_oppbuf;
1715 struct openpromio *opp = &(oppbuf.opp);
1716 struct openpromio *propdef_opp = &(propdef_oppbuf.opp);
1717 int *ip = (int *)((void *)opp->oprom_array);
1718 int ret;
1719 struct name_list *inexact_match = *inexact_list = NULL;
1720 struct name_list *exact_match = *exact_list = NULL;
1721 char alias_buf[MAXNAMELEN];
1722 int found = 0;
1724 if ((ret = prom_find_aliases_node(prom_fd)) < 0)
1725 return (0);
1727 (void) memset(oppbuf.buf, 0, BUFSIZE);
1728 opp->oprom_size = MAXPROPSIZE;
1729 *ip = 0;
1731 if ((ret = ioctl(prom_fd, OPROMNXTPROP, opp)) < 0)
1732 return (0);
1733 if (opp->oprom_size == 0)
1734 return (0);
1736 while ((ret >= 0) && (opp->oprom_size > 0)) {
1737 (void) strcpy(propdef_opp->oprom_array, opp->oprom_array);
1738 opp->oprom_size = MAXPROPSIZE;
1739 propdef_opp->oprom_size = MAXVALSIZE;
1740 if ((ioctl(prom_fd, OPROMGETPROP, propdef_opp) < 0) ||
1741 (propdef_opp->oprom_size == 0)) {
1742 ret = ioctl(prom_fd, OPROMNXTPROP, opp);
1743 continue;
1745 ret = prom_compare_devs(promdev_def, propdef_opp->oprom_array);
1746 if (ret == EXACT_MATCH) {
1747 found++;
1748 if (insert_alias_list(exact_list, opp->oprom_array)
1749 != 0) {
1750 free_name_list(exact_match, 1);
1751 free_name_list(inexact_match, 1);
1752 return (-1);
1755 if (ret == INEXACT_MATCH) {
1756 found++;
1757 (void) strcpy(alias_buf, opp->oprom_array);
1758 options_override(promdev_def, alias_buf);
1759 if (insert_alias_list(inexact_list, alias_buf)
1760 != 0) {
1761 free_name_list(exact_match, 1);
1762 free_name_list(inexact_match, 1);
1763 return (-1);
1766 ret = ioctl(prom_fd, OPROMNXTPROP, opp);
1768 if (found) {
1769 return (0);
1770 } else {
1771 return (-1);
1776 * free a list of name_list structs and optionally
1777 * free the strings they contain.
1779 static void
1780 free_name_list(struct name_list *list, int free_name)
1782 struct name_list *next = list;
1784 while (next != NULL) {
1785 list = list->next;
1786 if (free_name)
1787 free(next->name);
1788 free(next);
1789 next = list;
1794 * insert a new alias in a list of aliases - the list is sorted
1795 * in collating order (ignoring anything that comes after the
1796 * ':' in the name).
1798 static int
1799 insert_alias_list(struct name_list **list, char *alias_name)
1801 struct name_list *entry = *list;
1802 struct name_list *new_entry, *prev_entry;
1803 int ret;
1804 char *colon1, *colon2;
1806 if ((new_entry =
1807 (struct name_list *)malloc(sizeof (struct name_list)))
1808 == NULL) {
1809 return (-1);
1811 if ((new_entry->name = strdup(alias_name)) == NULL) {
1812 free(new_entry);
1813 return (-1);
1815 new_entry->next = NULL;
1817 if (entry == NULL) {
1818 *list = new_entry;
1819 return (0);
1822 if ((colon1 = strchr(alias_name, ':')) != NULL) {
1823 *colon1 = '\0';
1825 prev_entry = NULL;
1826 while (entry != NULL) {
1827 if ((colon2 = strchr(entry->name, ':')) != NULL) {
1828 *colon2 = '\0';
1830 ret = strcmp(alias_name, entry->name);
1831 if (colon2 != NULL) {
1832 *colon2 = ':';
1834 /* duplicate */
1835 if (ret == 0) {
1836 free(new_entry->name);
1837 free(new_entry);
1838 if (colon1 != NULL) {
1839 *colon1 = ':';
1841 return (0);
1843 if (ret < 0) {
1844 new_entry->next = entry;
1845 if (prev_entry == NULL) {
1846 /* in beginning of list */
1847 *list = new_entry;
1848 } else {
1849 /* in middle of list */
1850 prev_entry->next = new_entry;
1852 if (colon1 != NULL) {
1853 *colon1 = ':';
1855 return (0);
1857 prev_entry = entry;
1858 entry = entry->next;
1860 /* at end of list */
1861 prev_entry->next = new_entry;
1862 new_entry->next = NULL;
1863 if (colon1 != NULL) {
1864 *colon1 = ':';
1866 return (0);
1869 * append :x to alias_name to override any default minor name options
1871 static void
1872 options_override(char *prom_path, char *alias_name)
1874 char *colon;
1876 if ((colon = strrchr(alias_name, ':')) != NULL) {
1878 * XXX - should alias names in /aliases ever have a
1879 * : embedded in them?
1880 * If so we ignore it.
1882 *colon = '\0';
1885 if ((colon = strrchr(prom_path, ':')) != NULL) {
1886 (void) strcat(alias_name, colon);
1891 * compare to prom device names.
1892 * if the device names are not fully qualified. we convert them -
1893 * we only do this as a last resort though since it requires
1894 * jumping into the kernel.
1896 static int
1897 prom_compare_devs(char *prom_dev1, char *prom_dev2)
1899 char *dev1, *dev2;
1900 char *ptr1, *ptr2;
1901 char *drvname1, *addrname1, *minorname1;
1902 char *drvname2, *addrname2, *minorname2;
1903 char component1[MAXNAMELEN], component2[MAXNAMELEN];
1904 char devname1[MAXPATHLEN], devname2[MAXPATHLEN];
1905 int unqualified_name = 0;
1906 int error = EXACT_MATCH;
1907 int len1, len2;
1908 char *wildcard = ",0";
1910 ptr1 = prom_dev1;
1911 ptr2 = prom_dev2;
1913 if ((ptr1 == NULL) || (*ptr1 != '/')) {
1914 return (NO_MATCH);
1916 if ((ptr2 == NULL) || (*ptr2 != '/')) {
1917 return (NO_MATCH);
1921 * compare device names one component at a time.
1923 while ((ptr1 != NULL) && (ptr2 != NULL)) {
1924 *ptr1 = *ptr2 = '/';
1925 dev1 = ptr1 + 1;
1926 dev2 = ptr2 + 1;
1927 if ((ptr1 = strchr(dev1, '/')) != NULL)
1928 *ptr1 = '\0';
1929 if ((ptr2 = strchr(dev2, '/')) != NULL)
1930 *ptr2 = '\0';
1932 (void) strcpy(component1, dev1);
1933 (void) strcpy(component2, dev2);
1935 parse_name(component1, &drvname1, &addrname1, &minorname1);
1936 parse_name(component2, &drvname2, &addrname2, &minorname2);
1938 if ((drvname1 == NULL) && (addrname1 == NULL)) {
1939 error = NO_MATCH;
1940 break;
1943 if ((drvname2 == NULL) && (addrname2 == NULL)) {
1944 error = NO_MATCH;
1945 break;
1948 if (_prom_strcmp(drvname1, drvname2) != 0) {
1949 error = NO_MATCH;
1950 break;
1954 * a possible name is driver_name@address. The address
1955 * portion is optional (i.e. the name is not fully
1956 * qualified.). We have to deal with the case where
1957 * the component name is either driver_name or
1958 * driver_name@address
1960 if ((addrname1 == NULL) ^ (addrname2 == NULL)) {
1961 unqualified_name = 1;
1962 } else if (addrname1 &&
1963 (_prom_strcmp(addrname1, addrname2) != 0)) {
1965 * check to see if appending a ",0" to the
1966 * shorter address causes a match to occur.
1967 * If so succeed.
1969 len1 = strlen(addrname1);
1970 len2 = strlen(addrname2);
1971 if ((len1 < len2) &&
1972 (strncmp(addrname1, addrname2, len1) == 0) &&
1973 (strcmp(wildcard, &addrname2[len1]) == 0)) {
1974 continue;
1975 } else if ((len2 < len1) &&
1976 (strncmp(addrname1, addrname2, len2) == 0) &&
1977 (strcmp(wildcard, &addrname1[len2]) == 0)) {
1978 continue;
1980 error = NO_MATCH;
1981 break;
1986 * if either of the two device paths still has more components,
1987 * then we do not have a match.
1989 if (ptr1 != NULL) {
1990 *ptr1 = '/';
1991 error = NO_MATCH;
1993 if (ptr2 != NULL) {
1994 *ptr2 = '/';
1995 error = NO_MATCH;
1997 if (error == NO_MATCH) {
1998 return (error);
2002 * OK - we found a possible match but one or more of the
2003 * path components was not fully qualified (did not have any
2004 * address information. So we need to convert it to a form
2005 * that is fully qualified and then compare the resulting
2006 * strings.
2008 if (unqualified_name != 0) {
2009 if ((devfs_prom_to_dev_name(prom_dev1, devname1) < 0) ||
2010 (devfs_prom_to_dev_name(prom_dev2, devname2) < 0)) {
2011 return (NO_MATCH);
2013 if ((dev1 = strrchr(devname1, ':')) != NULL) {
2014 *dev1 = '\0';
2016 if ((dev2 = strrchr(devname2, ':')) != NULL) {
2017 *dev2 = '\0';
2019 if (strcmp(devname1, devname2) != 0) {
2020 return (NO_MATCH);
2024 * the resulting strings matched. If the minorname information
2025 * matches, then we have an exact match, otherwise an inexact match
2027 if (_prom_strcmp(minorname1, minorname2) == 0) {
2028 return (EXACT_MATCH);
2029 } else {
2030 return (INEXACT_MATCH);
2035 * wrapper or strcmp - deals with null strings.
2037 static int
2038 _prom_strcmp(char *s1, char *s2)
2040 if ((s1 == NULL) && (s2 == NULL))
2041 return (0);
2042 if ((s1 == NULL) && (s2 != NULL)) {
2043 return (-1);
2045 if ((s1 != NULL) && (s2 == NULL)) {
2046 return (1);
2048 return (strcmp(s1, s2));
2051 * break device@a,b:minor into components
2053 static void
2054 parse_name(char *name, char **drvname, char **addrname, char **minorname)
2056 char *cp, ch;
2058 cp = *drvname = name;
2059 *addrname = *minorname = NULL;
2060 if (*name == '@')
2061 *drvname = NULL;
2063 while ((ch = *cp) != '\0') {
2064 if (ch == '@')
2065 *addrname = ++cp;
2066 else if (ch == ':')
2067 *minorname = ++cp;
2068 ++cp;
2070 if (*addrname) {
2071 *((*addrname)-1) = '\0';
2073 if (*minorname) {
2074 *((*minorname)-1) = '\0';
2079 * converts a prom alias to a prom device name.
2080 * if we find no matching device, then we fail since if were
2081 * given a valid alias, then by definition, there must be a
2082 * device pathname associated with it in the /aliases node.
2084 static int
2085 alias_to_prom_dev(char *alias, char *ret_buf)
2087 char *options_ptr;
2088 char alias_buf[MAXNAMELEN];
2089 char alias_def[MAXPATHLEN];
2090 char options[16] = "";
2091 int prom_fd = -1;
2092 int ret;
2093 int i;
2095 if (strchr(alias, '/') != NULL)
2096 return (DEVFS_INVAL);
2098 if (strlen(alias) > (MAXNAMELEN - 1))
2099 return (DEVFS_INVAL);
2101 if (ret_buf == NULL) {
2102 return (DEVFS_INVAL);
2105 prom_fd = prom_open(O_RDONLY);
2106 if (prom_fd < 0) {
2107 return (prom_fd);
2110 (void) strlcpy(alias_buf, alias, sizeof (alias_buf));
2113 * save off any options (minor name info) that is
2114 * explicitly called out in the alias name
2116 if ((options_ptr = strchr(alias_buf, ':')) != NULL) {
2117 *options_ptr = '\0';
2118 (void) strlcpy(options, ++options_ptr, sizeof (options));
2121 *alias_def = '\0';
2123 ret = prom_find_aliases_node(prom_fd);
2124 if (ret == 0) {
2126 * we loop because one alias may define another... we have
2127 * to work our way down to an actual device definition.
2129 for (i = 0; i <= 10; i++) {
2130 ret = prom_srch_node(prom_fd, alias_buf, alias_def);
2131 if (ret == -1) {
2132 break;
2134 (void) strlcpy(alias_buf, alias_def,
2135 sizeof (alias_buf));
2136 if (*alias_def == '/') {
2137 break;
2141 * save off any explicit options (minor name info)
2142 * if none has been encountered yet
2144 if (options_ptr == NULL) {
2145 options_ptr = strchr(alias_buf, ':');
2146 if (options_ptr != NULL) {
2147 *options_ptr = '\0';
2148 (void) strlcpy(options, ++options_ptr,
2149 sizeof (options));
2154 prom_close(prom_fd);
2156 /* error */
2157 if (ret == -1) {
2158 return (ret);
2161 (void) strlcpy(ret_buf, alias_def, MAXPATHLEN);
2163 /* override minor name information */
2164 if (options_ptr != NULL) {
2165 if ((options_ptr = strrchr(ret_buf, ':')) == NULL) {
2166 (void) strcat(ret_buf, ":");
2167 } else {
2168 *(++options_ptr) = '\0';
2170 (void) strcat(ret_buf, options);
2172 return (0);
2176 * search a prom node for a property name
2178 static int
2179 prom_srch_node(int fd, char *prop_name, char *ret_buf)
2181 Oppbuf oppbuf;
2182 struct openpromio *opp = &(oppbuf.opp);
2183 int *ip = (int *)((void *)opp->oprom_array);
2185 (void) memset(oppbuf.buf, 0, BUFSIZE);
2186 opp->oprom_size = MAXPROPSIZE;
2187 *ip = 0;
2189 if (ioctl(fd, OPROMNXTPROP, opp) < 0)
2190 return (-1);
2191 if (opp->oprom_size == 0)
2192 return (-1);
2194 while (strcmp(prop_name, opp->oprom_array) != 0) {
2195 opp->oprom_size = MAXPROPSIZE;
2196 if (ioctl(fd, OPROMNXTPROP, opp) < 0)
2197 return (-1);
2198 if (opp->oprom_size == 0)
2199 return (-1);
2201 opp->oprom_size = MAXVALSIZE;
2202 if (ioctl(fd, OPROMGETPROP, opp) < 0)
2203 return (-1);
2205 if (opp->oprom_size == 0)
2206 return (-1);
2207 (void) strlcpy(ret_buf, opp->oprom_array, MAXPATHLEN);
2208 return (0);
2212 * return the aliases node.
2214 static int
2215 prom_find_aliases_node(int fd)
2217 uint_t child_id;
2218 char buf[MAXPATHLEN];
2220 if ((child_id = prom_next_node(fd, 0)) == 0)
2221 return (-1);
2222 if ((child_id = prom_child_node(fd, child_id)) == 0)
2223 return (-1);
2225 while (child_id != 0) {
2226 if (prom_srch_node(fd, "name", buf) == 0) {
2227 if (strcmp(buf, "aliases") == 0) {
2228 return (0);
2231 child_id = prom_next_node(fd, child_id);
2233 return (-1);
2237 * get sibling
2239 static uint_t
2240 prom_next_node(int fd, uint_t node_id)
2242 Oppbuf oppbuf;
2243 struct openpromio *opp = &(oppbuf.opp);
2244 uint_t *ip = (uint_t *)((void *)opp->oprom_array);
2246 (void) memset(oppbuf.buf, 0, BUFSIZE);
2247 opp->oprom_size = MAXVALSIZE;
2248 *ip = node_id;
2250 if (ioctl(fd, OPROMNEXT, opp) < 0)
2251 return (0);
2253 return (*(uint_t *)((void *)opp->oprom_array));
2257 * get child
2259 static uint_t
2260 prom_child_node(int fd, uint_t node_id)
2262 Oppbuf oppbuf;
2263 struct openpromio *opp = &(oppbuf.opp);
2264 uint_t *ip = (uint_t *)((void *)opp->oprom_array);
2266 (void) memset(oppbuf.buf, 0, BUFSIZE);
2267 opp->oprom_size = MAXVALSIZE;
2268 *ip = node_id;
2270 if (ioctl(fd, OPROMCHILD, opp) < 0)
2271 return (0);
2273 return (*(uint_t *)((void *)opp->oprom_array));
2277 * only on sparc for now
2280 devfs_bootdev_modifiable(void)
2282 #if defined(sparc)
2283 return (0);
2284 #else
2285 return (DEVFS_NOTSUP);
2286 #endif