/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Interfaces for getting device configuration data from kernel * through the devinfo driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NDEBUG 1 #include #include "libdevinfo.h" /* * Debug message levels */ typedef enum { DI_QUIET = 0, /* No debug messages - the default */ DI_ERR = 1, DI_INFO, DI_TRACE, DI_TRACE1, DI_TRACE2 } di_debug_t; int di_debug = DI_QUIET; #define DPRINTF(args) { if (di_debug != DI_QUIET) dprint args; } void dprint(di_debug_t msglevel, const char *fmt, ...); #pragma init(_libdevinfo_init) void _libdevinfo_init() { char *debug_str = getenv("_LIBDEVINFO_DEBUG"); if (debug_str) { errno = 0; di_debug = atoi(debug_str); if (errno || di_debug < DI_QUIET) di_debug = DI_QUIET; } } di_node_t di_init(const char *phys_path, uint_t flag) { return (di_init_impl(phys_path, flag, NULL)); } /* * We use blocking_open() to guarantee access to the devinfo device, if open() * is failing with EAGAIN. */ static int blocking_open(const char *path, int oflag) { int fd; while ((fd = open(path, oflag)) == -1 && errno == EAGAIN) (void) poll(NULL, 0, 1 * MILLISEC); return (fd); } /* private interface */ di_node_t di_init_driver(const char *drv_name, uint_t flag) { int fd; char driver[MAXPATHLEN]; /* * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023, * which should be sufficient for any sensible programmer. */ if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) { errno = EINVAL; return (DI_NODE_NIL); } (void) strcpy(driver, drv_name); /* * open the devinfo driver */ if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", O_RDONLY)) == -1) { DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); return (DI_NODE_NIL); } if (ioctl(fd, DINFOLODRV, driver) != 0) { DPRINTF((DI_ERR, "failed to load driver %s\n", driver)); (void) close(fd); errno = ENXIO; return (DI_NODE_NIL); } (void) close(fd); /* * Driver load succeeded, return a snapshot */ return (di_init("/", flag)); } di_node_t di_init_impl(const char *phys_path, uint_t flag, struct di_priv_data *priv) { caddr_t pa; int fd, map_size; struct di_all *dap; struct dinfo_io dinfo_io; uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1; uint_t pagemask = ~pageoffset; DPRINTF((DI_INFO, "di_init: taking a snapshot\n")); /* * Make sure there is no minor name in the path * and the path do not start with /devices.... */ if (strchr(phys_path, ':') || (strncmp(phys_path, "/devices", 8) == 0) || (strlen(phys_path) > MAXPATHLEN)) { errno = EINVAL; return (DI_NODE_NIL); } if (strlen(phys_path) == 0) (void) sprintf(dinfo_io.root_path, "/"); else if (*phys_path != '/') (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), "/%s", phys_path); else (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), "%s", phys_path); /* * If private data is requested, copy the format specification */ if (flag & DINFOPRIVDATA & 0xff) { if (priv) bcopy(priv, &dinfo_io.priv, sizeof (struct di_priv_data)); else { errno = EINVAL; return (DI_NODE_NIL); } } /* * Attempt to open the devinfo driver. Make a second attempt at the * read-only minor node if we don't have privileges to open the full * version _and_ if we're not requesting operations that the read-only * node can't perform. (Setgid processes would fail an access() test, * of course.) */ if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", O_RDONLY)) == -1) { if ((flag & DINFOFORCE) == DINFOFORCE || (flag & DINFOPRIVDATA) == DINFOPRIVDATA) { /* * We wanted to perform a privileged operation, but the * privileged node isn't available. Don't modify errno * on our way out (but display it if we're running with * di_debug set). */ DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); return (DI_NODE_NIL); } if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro", O_RDONLY)) == -1) { DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); return (DI_NODE_NIL); } } /* * Verify that there is no major conflict, i.e., we are indeed opening * the devinfo driver. */ if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) { DPRINTF((DI_ERR, "driver ID failed; check for major conflict\n")); (void) close(fd); return (DI_NODE_NIL); } /* * create snapshot */ if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) { DPRINTF((DI_ERR, "devinfo ioctl failed with " "error: %d\n", errno)); (void) close(fd); return (DI_NODE_NIL); } else if (map_size == 0) { DPRINTF((DI_ERR, "%s not found\n", phys_path)); errno = ENXIO; (void) close(fd); return (DI_NODE_NIL); } /* * copy snapshot to userland */ map_size = (map_size + pageoffset) & pagemask; if ((pa = valloc(map_size)) == NULL) { DPRINTF((DI_ERR, "valloc failed for snapshot\n")); (void) close(fd); return (DI_NODE_NIL); } if (ioctl(fd, DINFOUSRLD, pa) != map_size) { DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n")); (void) close(fd); free(pa); errno = EFAULT; return (DI_NODE_NIL); } (void) close(fd); dap = DI_ALL(pa); if (dap->top_devinfo == 0) { /* phys_path not found */ DPRINTF((DI_ERR, "%s not found\n", phys_path)); free(pa); errno = EINVAL; return (DI_NODE_NIL); } return (DI_NODE(pa + dap->top_devinfo)); } void di_fini(di_node_t root) { caddr_t pa; /* starting address of map */ DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n")); /* * paranoid checking */ if (root == DI_NODE_NIL) { DPRINTF((DI_ERR, "di_fini called with NIL arg\n")); return; } /* * The root contains its own offset--self. * Subtracting it from root address, we get the starting addr. * The map_size is stored at the beginning of snapshot. * Once we have starting address and size, we can free(). */ pa = (caddr_t)root - DI_NODE(root)->self; free(pa); } di_node_t di_parent_node(di_node_t node) { caddr_t pa; /* starting address of map */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_NODE_NIL); } DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node))); pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->parent) { return (DI_NODE(pa + DI_NODE(node)->parent)); } /* * Deal with error condition: * If parent doesn't exist and node is not the root, * set errno to ENOTSUP. Otherwise, set errno to ENXIO. */ if (strcmp(DI_ALL(pa)->root_path, "/") != 0) errno = ENOTSUP; else errno = ENXIO; return (DI_NODE_NIL); } di_node_t di_sibling_node(di_node_t node) { caddr_t pa; /* starting address of map */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_NODE_NIL); } DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node))); pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->sibling) { return (DI_NODE(pa + DI_NODE(node)->sibling)); } /* * Deal with error condition: * Sibling doesn't exist, figure out if ioctl command * has DINFOSUBTREE set. If it doesn't, set errno to * ENOTSUP. */ if (!(DI_ALL(pa)->command & DINFOSUBTREE)) errno = ENOTSUP; else errno = ENXIO; return (DI_NODE_NIL); } di_node_t di_child_node(di_node_t node) { caddr_t pa; /* starting address of map */ DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node))); if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_NODE_NIL); } pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->child) { return (DI_NODE(pa + DI_NODE(node)->child)); } /* * Deal with error condition: * Child doesn't exist, figure out if DINFOSUBTREE is set. * If it isn't, set errno to ENOTSUP. */ if (!(DI_ALL(pa)->command & DINFOSUBTREE)) errno = ENOTSUP; else errno = ENXIO; return (DI_NODE_NIL); } di_node_t di_drv_first_node(const char *drv_name, di_node_t root) { caddr_t pa; /* starting address of map */ int major, devcnt; struct di_devnm *devnm; DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name)); if (root == DI_NODE_NIL) { errno = EINVAL; return (DI_NODE_NIL); } /* * get major number of driver */ pa = (caddr_t)root - DI_NODE(root)->self; devcnt = DI_ALL(pa)->devcnt; devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); for (major = 0; major < devcnt; major++) if (devnm[major].name && (strcmp(drv_name, (char *)(pa + devnm[major].name)) == 0)) break; if (major >= devcnt) { errno = EINVAL; return (DI_NODE_NIL); } if (!(devnm[major].head)) { errno = ENXIO; return (DI_NODE_NIL); } return (DI_NODE(pa + devnm[major].head)); } di_node_t di_drv_next_node(di_node_t node) { caddr_t pa; /* starting address of map */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_NODE_NIL); } DPRINTF((DI_TRACE, "next node on per driver list:" " current=%s, driver=%s\n", di_node_name(node), di_driver_name(node))); if (DI_NODE(node)->next == (di_off_t)-1) { errno = ENOTSUP; return (DI_NODE_NIL); } pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->next == NULL) { errno = ENXIO; return (DI_NODE_NIL); } return (DI_NODE(pa + DI_NODE(node)->next)); } /* * Internal library interfaces: * node_list etc. for node walking */ struct node_list { struct node_list *next; di_node_t node; }; static void free_node_list(struct node_list **headp) { struct node_list *tmp; while (*headp) { tmp = *headp; *headp = (*headp)->next; free(tmp); } } static void append_node_list(struct node_list **headp, struct node_list *list) { struct node_list *tmp; if (*headp == NULL) { *headp = list; return; } if (list == NULL) /* a minor optimization */ return; tmp = *headp; while (tmp->next) tmp = tmp->next; tmp->next = list; } static void prepend_node_list(struct node_list **headp, struct node_list *list) { struct node_list *tmp; if (list == NULL) return; tmp = *headp; *headp = list; if (tmp == NULL) /* a minor optimization */ return; while (list->next) list = list->next; list->next = tmp; } /* * returns 1 if node is a descendant of parent, 0 otherwise */ static int is_descendant(di_node_t node, di_node_t parent) { /* * DI_NODE_NIL is parent of root, so it is * the parent of all nodes. */ if (parent == DI_NODE_NIL) { return (1); } do { node = di_parent_node(node); } while ((node != DI_NODE_NIL) && (node != parent)); return (node != DI_NODE_NIL); } /* * Insert list before the first node which is NOT a descendent of parent. * This is needed to reproduce the exact walking order of link generators. */ static void insert_node_list(struct node_list **headp, struct node_list *list, di_node_t parent) { struct node_list *tmp, *tmp1; if (list == NULL) return; tmp = *headp; if (tmp == NULL) { /* a minor optimization */ *headp = list; return; } if (!is_descendant(tmp->node, parent)) { prepend_node_list(headp, list); return; } /* * Find first node which is not a descendant */ while (tmp->next && is_descendant(tmp->next->node, parent)) { tmp = tmp->next; } tmp1 = tmp->next; tmp->next = list; append_node_list(headp, tmp1); } /* * Get a linked list of handles of all children */ static struct node_list * get_children(di_node_t node) { di_node_t child; struct node_list *result, *tmp; DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node))); if ((child = di_child_node(node)) == DI_NODE_NIL) { return (NULL); } if ((result = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); return (NULL); } result->node = child; tmp = result; while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) { if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); free_node_list(&result); return (NULL); } tmp = tmp->next; tmp->node = child; } tmp->next = NULL; return (result); } /* * Internal library interface: * Delete all siblings of the first node from the node_list, along with * the first node itself. */ static void prune_sib(struct node_list **headp) { di_node_t parent, curr_par, curr_gpar; struct node_list *curr, *prev; /* * get handle to parent of first node */ if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) { /* * This must be the root of the snapshot, so can't * have any siblings. * * XXX Put a check here just in case. */ if ((*headp)->next) DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n")); free(*headp); *headp = NULL; return; } /* * To be complete, we should also delete the children * of siblings that have already been visited. * This happens for DI_WALK_SIBFIRST when the first node * is NOT the first in the linked list of siblings. * * Hence, we compare parent with BOTH the parent and grandparent * of nodes, and delete node is a match is found. */ prev = *headp; curr = prev->next; while (curr) { if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) && ((curr_par == parent) || ((curr_gpar = di_parent_node(curr_par)) != DI_NODE_NIL) && (curr_gpar == parent))) { /* * match parent/grandparent: delete curr */ prev->next = curr->next; free(curr); curr = prev->next; } else curr = curr->next; } /* * delete the first node */ curr = *headp; *headp = curr->next; free(curr); } /* * Internal library function: * Update node list based on action (return code from callback) * and flag specifying walking behavior. */ static void update_node_list(int action, uint_t flag, struct node_list **headp) { struct node_list *children, *tmp; di_node_t parent = di_parent_node((*headp)->node); switch (action) { case DI_WALK_TERMINATE: /* * free the node list and be done */ children = NULL; free_node_list(headp); break; case DI_WALK_PRUNESIB: /* * Get list of children and prune siblings */ children = get_children((*headp)->node); prune_sib(headp); break; case DI_WALK_PRUNECHILD: /* * Set children to NULL and pop first node */ children = NULL; tmp = *headp; *headp = tmp->next; free(tmp); break; case DI_WALK_CONTINUE: default: /* * Get list of children and pop first node */ children = get_children((*headp)->node); tmp = *headp; *headp = tmp->next; free(tmp); break; } /* * insert the list of children */ switch (flag) { case DI_WALK_CLDFIRST: prepend_node_list(headp, children); break; case DI_WALK_SIBFIRST: append_node_list(headp, children); break; case DI_WALK_LINKGEN: default: insert_node_list(headp, children, parent); break; } } /* * Internal library function: * Invoke callback on one node and update the list of nodes to be walked * based on the flag and return code. */ static void walk_one_node(struct node_list **headp, uint_t flag, void *arg, int (*callback)(di_node_t, void *)) { DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node))); update_node_list(callback((*headp)->node, arg), flag & DI_WALK_MASK, headp); } int di_walk_node(di_node_t root, uint_t flag, void *arg, int (*node_callback)(di_node_t, void *)) { struct node_list *head; /* node_list for tree walk */ if (root == NULL) { errno = EINVAL; return (-1); } if ((head = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); return (-1); } head->next = NULL; head->node = root; DPRINTF((DI_INFO, "Start node walking from node %s\n", di_node_name(root))); while (head != NULL) walk_one_node(&head, flag, arg, node_callback); return (0); } /* * Internal library function: * Invoke callback for each minor on the minor list of first node * on node_list headp, and place children of first node on the list. * * This is similar to walk_one_node, except we only walk in child * first mode. */ static void walk_one_minor_list(struct node_list **headp, const char *desired_type, uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *)) { int ddm_type; int action = DI_WALK_CONTINUE; char *node_type; di_minor_t minor = DI_MINOR_NIL; di_node_t node = (*headp)->node; while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { ddm_type = di_minor_type(minor); if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS)) continue; if ((ddm_type == DDM_INTERNAL_PATH) && !(flag & DI_CHECK_INTERNAL_PATH)) continue; node_type = di_minor_nodetype(minor); if ((desired_type != NULL) && ((node_type == NULL) || strncmp(desired_type, node_type, strlen(desired_type)) != 0)) continue; if ((action = callback(node, minor, arg)) == DI_WALK_TERMINATE) { break; } } update_node_list(action, DI_WALK_LINKGEN, headp); } int di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg, int (*minor_callback)(di_node_t, di_minor_t, void *)) { struct node_list *head; /* node_list for tree walk */ #ifdef DEBUG char *path = di_devfs_path(root); DPRINTF((DI_INFO, "walking minor nodes under %s\n", path)); di_devfs_path_free(path); #endif if (root == NULL) { errno = EINVAL; return (-1); } if ((head = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); return (-1); } head->next = NULL; head->node = root; DPRINTF((DI_INFO, "Start minor walking from node %s\n", di_node_name(root))); while (head != NULL) walk_one_minor_list(&head, minor_type, flag, arg, minor_callback); return (0); } /* * generic node parameters * Calling these routines always succeeds. */ char * di_node_name(di_node_t node) { return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self); } /* returns NULL ptr or a valid ptr to non-NULL string */ char * di_bus_addr(di_node_t node) { caddr_t pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->address == 0) return (NULL); return ((char *)(pa + DI_NODE(node)->address)); } char * di_binding_name(di_node_t node) { caddr_t pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->bind_name == 0) return (NULL); return ((char *)(pa + DI_NODE(node)->bind_name)); } int di_compatible_names(di_node_t node, char **names) { char *c; int len, size, entries = 0; if (DI_NODE(node)->compat_names == 0) { *names = NULL; return (0); } *names = (caddr_t)node + DI_NODE(node)->compat_names - DI_NODE(node)->self; c = *names; len = DI_NODE(node)->compat_length; while (len > 0) { entries++; size = strlen(c) + 1; len -= size; c += size; } return (entries); } int di_instance(di_node_t node) { return (DI_NODE(node)->instance); } /* * XXX: emulate the return value of the old implementation * using info from devi_node_class and devi_node_attributes. */ int di_nodeid(di_node_t node) { if (DI_NODE(node)->node_class == DDI_NC_PROM) return (DI_PROM_NODEID); if (DI_NODE(node)->attributes & DDI_PERSISTENT) return (DI_SID_NODEID); return (DI_PSEUDO_NODEID); } uint_t di_state(di_node_t node) { uint_t result = 0; if (di_node_state(node) < DS_ATTACHED) result |= DI_DRIVER_DETACHED; if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE) result |= DI_DEVICE_OFFLINE; if (DI_NODE(node)->state & DEVI_DEVICE_DOWN) result |= DI_DEVICE_OFFLINE; if (DI_NODE(node)->state & DEVI_BUS_QUIESCED) result |= DI_BUS_QUIESCED; if (DI_NODE(node)->state & DEVI_BUS_DOWN) result |= DI_BUS_DOWN; return (result); } ddi_node_state_t di_node_state(di_node_t node) { return (DI_NODE(node)->node_state); } ddi_devid_t di_devid(di_node_t node) { if (DI_NODE(node)->devid == 0) return (NULL); return ((ddi_devid_t)((caddr_t)node + DI_NODE(node)->devid - DI_NODE(node)->self)); } int di_driver_major(di_node_t node) { int major; major = DI_NODE(node)->drv_major; if (major < 0) return (-1); return (major); } char * di_driver_name(di_node_t node) { int major; caddr_t pa; struct di_devnm *devnm; major = DI_NODE(node)->drv_major; if (major < 0) return (NULL); pa = (caddr_t)node - DI_NODE(node)->self; devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); if (devnm[major].name) return (pa + devnm[major].name); else return (NULL); } uint_t di_driver_ops(di_node_t node) { int major; caddr_t pa; struct di_devnm *devnm; major = DI_NODE(node)->drv_major; if (major < 0) return (0); pa = (caddr_t)node - DI_NODE(node)->self; devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); return (devnm[major].ops); } /* * returns the length of the path, caller must free memory */ char * di_devfs_path(di_node_t node) { caddr_t pa; di_node_t parent; int depth = 0, len = 0; char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH]; if (node == DI_NODE_NIL) { errno = EINVAL; return (NULL); } /* * trace back to root, note the node_name & address */ while ((parent = di_parent_node(node)) != DI_NODE_NIL) { name[depth] = di_node_name(node); len += strlen(name[depth]) + 1; /* 1 for '/' */ if ((addr[depth] = di_bus_addr(node)) != NULL) len += strlen(addr[depth]) + 1; /* 1 for '@' */ node = parent; depth++; } /* * get the path to the root of snapshot */ pa = (caddr_t)node - DI_NODE(node)->self; name[depth] = DI_ALL(pa)->root_path; len += strlen(name[depth]) + 1; /* * allocate buffer and assemble path */ if ((buf = malloc(len)) == NULL) { return (NULL); } (void) strcpy(buf, name[depth]); len = strlen(buf); if (buf[len - 1] == '/') len--; /* delete trailing '/' */ while (depth) { depth--; buf[len] = '/'; (void) strcpy(buf + len + 1, name[depth]); len += strlen(name[depth]) + 1; if (addr[depth] && addr[depth][0] != '\0') { buf[len] = '@'; (void) strcpy(buf + len + 1, addr[depth]); len += strlen(addr[depth]) + 1; } } return (buf); } char * di_devfs_minor_path(di_minor_t minor) { di_node_t node; char *full_path, *name, *path; int full_path_len; if (minor == DI_MINOR_NIL) { errno = EINVAL; return (NULL); } name = di_minor_name(minor); node = di_minor_devinfo(minor); path = di_devfs_path(node); if (path == NULL) return (NULL); /* make the full path to the device minor node */ full_path_len = strlen(path) + strlen(name) + 2; full_path = (char *)calloc(1, full_path_len); if (full_path != NULL) (void) snprintf(full_path, full_path_len, "%s:%s", path, name); di_devfs_path_free(path); return (full_path); } void di_devfs_path_free(char *buf) { if (buf == NULL) { DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n")); return; } free(buf); } /* minor data access */ di_minor_t di_minor_next(di_node_t node, di_minor_t minor) { caddr_t pa; /* * paranoid error checking */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_MINOR_NIL); } /* * minor is not NIL */ if (minor != DI_MINOR_NIL) { if (DI_MINOR(minor)->next != 0) return ((di_minor_t)((void *)((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->next))); else { errno = ENXIO; return (DI_MINOR_NIL); } } /* * minor is NIL-->caller asks for first minor node */ if (DI_NODE(node)->minor_data != 0) { return (DI_MINOR((caddr_t)node - DI_NODE(node)->self + DI_NODE(node)->minor_data)); } /* * no minor data-->check if snapshot includes minor data * in order to set the correct errno */ pa = (caddr_t)node - DI_NODE(node)->self; if (DINFOMINOR & DI_ALL(pa)->command) errno = ENXIO; else errno = ENOTSUP; return (DI_MINOR_NIL); } /* private interface for dealing with alias minor link generation */ di_node_t di_minor_devinfo(di_minor_t minor) { if (minor == DI_MINOR_NIL) { errno = EINVAL; return (DI_NODE_NIL); } return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->node)); } ddi_minor_type di_minor_type(di_minor_t minor) { return (DI_MINOR(minor)->type); } char * di_minor_name(di_minor_t minor) { if (DI_MINOR(minor)->name == 0) return (NULL); return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name); } dev_t di_minor_devt(di_minor_t minor) { return (makedev(DI_MINOR(minor)->dev_major, DI_MINOR(minor)->dev_minor)); } int di_minor_spectype(di_minor_t minor) { return (DI_MINOR(minor)->spec_type); } char * di_minor_nodetype(di_minor_t minor) { if (DI_MINOR(minor)->node_type == 0) return (NULL); return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->node_type); } /* * Single public interface for accessing software properties */ di_prop_t di_prop_next(di_node_t node, di_prop_t prop) { int list = DI_PROP_DRV_LIST; /* * paranoid check */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_PROP_NIL); } /* * Find which prop list we are at */ if (prop != DI_PROP_NIL) list = DI_PROP(prop)->prop_list; do { switch (list++) { case DI_PROP_DRV_LIST: prop = di_prop_drv_next(node, prop); break; case DI_PROP_SYS_LIST: prop = di_prop_sys_next(node, prop); break; case DI_PROP_GLB_LIST: prop = di_prop_global_next(node, prop); break; case DI_PROP_HW_LIST: prop = di_prop_hw_next(node, prop); break; default: /* shouldn't happen */ errno = EFAULT; return (DI_PROP_NIL); } } while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST)); return (prop); } dev_t di_prop_devt(di_prop_t prop) { return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor)); } char * di_prop_name(di_prop_t prop) { if (DI_PROP(prop)->prop_name == 0) return (NULL); return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name); } int di_prop_type(di_prop_t prop) { uint_t flags = DI_PROP(prop)->prop_flags; if (flags & DDI_PROP_UNDEF_IT) return (DI_PROP_TYPE_UNDEF_IT); if (DI_PROP(prop)->prop_len == 0) return (DI_PROP_TYPE_BOOLEAN); if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY) return (DI_PROP_TYPE_UNKNOWN); if (flags & DDI_PROP_TYPE_INT) return (DI_PROP_TYPE_INT); if (flags & DDI_PROP_TYPE_INT64) return (DI_PROP_TYPE_INT64); if (flags & DDI_PROP_TYPE_STRING) return (DI_PROP_TYPE_STRING); if (flags & DDI_PROP_TYPE_BYTE) return (DI_PROP_TYPE_BYTE); /* * Shouldn't get here. In case we do, return unknown type. * * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need * to add DI_PROP_TYPE_COMPOSITE. */ DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags)); return (DI_PROP_TYPE_UNKNOWN); } /* * Extract type-specific values of an property */ extern int di_prop_decode_common(void *prop_data, int len, int ddi_type, int prom); int di_prop_ints(di_prop_t prop, int **prop_data) { if (DI_PROP(prop)->prop_len == 0) return (0); /* boolean property */ if ((DI_PROP(prop)->prop_data == 0) || (DI_PROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_data)); return (di_prop_decode_common((void *)prop_data, DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); } int di_prop_int64(di_prop_t prop, int64_t **prop_data) { if (DI_PROP(prop)->prop_len == 0) return (0); /* boolean property */ if ((DI_PROP(prop)->prop_data == 0) || (DI_PROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_data)); return (di_prop_decode_common((void *)prop_data, DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); } int di_prop_strings(di_prop_t prop, char **prop_data) { if (DI_PROP(prop)->prop_len == 0) return (0); /* boolean property */ if ((DI_PROP(prop)->prop_data == 0) || (DI_PROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_data); return (di_prop_decode_common((void *)prop_data, DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); } int di_prop_bytes(di_prop_t prop, uchar_t **prop_data) { if (DI_PROP(prop)->prop_len == 0) return (0); /* boolean property */ if ((DI_PROP(prop)->prop_data == 0) || (DI_PROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_data); return (di_prop_decode_common((void *)prop_data, DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); } /* * returns 1 for match, 0 for no match */ static int match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type) { int prop_type; #ifdef DEBUG if (di_prop_name(prop) == NULL) { DPRINTF((DI_ERR, "libdevinfo: property has no name!\n")); return (0); } #endif /* DEBUG */ if (strcmp(name, di_prop_name(prop)) != 0) return (0); if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev)) return (0); /* * XXX prop_type is different from DDI_*. See PSARC 1997/127. */ prop_type = di_prop_type(prop); if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) && (prop_type != DI_PROP_TYPE_BOOLEAN)) return (0); return (1); } static di_prop_t di_prop_search(dev_t match_dev, di_node_t node, const char *name, int type) { di_prop_t prop = DI_PROP_NIL; /* * The check on match_dev follows ddi_prop_lookup_common(). * Other checks are libdevinfo specific implementation. */ if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) || (match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) { errno = EINVAL; return (DI_PROP_NIL); } while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n", di_prop_name(prop), di_prop_devt(prop), di_prop_type(prop))); if (match_prop(prop, match_dev, name, type)) return (prop); } return (DI_PROP_NIL); } int di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name, int **prop_data) { di_prop_t prop; if ((prop = di_prop_search(dev, node, prop_name, DI_PROP_TYPE_INT)) == DI_PROP_NIL) return (-1); return (di_prop_ints(prop, (void *)prop_data)); } int di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name, int64_t **prop_data) { di_prop_t prop; if ((prop = di_prop_search(dev, node, prop_name, DI_PROP_TYPE_INT64)) == DI_PROP_NIL) return (-1); return (di_prop_int64(prop, (void *)prop_data)); } int di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name, char **prop_data) { di_prop_t prop; if ((prop = di_prop_search(dev, node, prop_name, DI_PROP_TYPE_STRING)) == DI_PROP_NIL) return (-1); return (di_prop_strings(prop, (void *)prop_data)); } int di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name, uchar_t **prop_data) { di_prop_t prop; if ((prop = di_prop_search(dev, node, prop_name, DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) return (-1); return (di_prop_bytes(prop, (void *)prop_data)); } /* * Consolidation private property access functions */ enum prop_type { PROP_TYPE_DRV, PROP_TYPE_SYS, PROP_TYPE_GLOB, PROP_TYPE_HW }; static di_prop_t di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type) { caddr_t pa; di_off_t prop_off = 0; if (prop != DI_PROP_NIL) { if (DI_PROP(prop)->next) { return (DI_PROP((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->next)); } else { return (DI_PROP_NIL); } } /* * prop is NIL, caller asks for first property */ pa = (caddr_t)node - DI_NODE(node)->self; switch (prop_type) { case PROP_TYPE_DRV: prop_off = DI_NODE(node)->drv_prop; break; case PROP_TYPE_SYS: prop_off = DI_NODE(node)->sys_prop; break; case PROP_TYPE_HW: prop_off = DI_NODE(node)->hw_prop; break; case PROP_TYPE_GLOB: prop_off = DI_NODE(node)->glob_prop; if (prop_off == -1) { /* no global property */ prop_off = 0; } else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) { /* refer to devnames array */ struct di_devnm *devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major * sizeof (struct di_devnm))); prop_off = devnm->global_prop; } break; } if (prop_off) { return (DI_PROP(pa + prop_off)); } /* * no prop found. Check the reason for not found */ if (DINFOPROP & DI_ALL(pa)->command) errno = ENXIO; else errno = ENOTSUP; return (DI_PROP_NIL); } di_prop_t di_prop_drv_next(di_node_t node, di_prop_t prop) { return (di_prop_next_common(node, prop, PROP_TYPE_DRV)); } di_prop_t di_prop_sys_next(di_node_t node, di_prop_t prop) { return (di_prop_next_common(node, prop, PROP_TYPE_SYS)); } di_prop_t di_prop_global_next(di_node_t node, di_prop_t prop) { return (di_prop_next_common(node, prop, PROP_TYPE_GLOB)); } di_prop_t di_prop_hw_next(di_node_t node, di_prop_t prop) { return (di_prop_next_common(node, prop, PROP_TYPE_HW)); } int di_prop_rawdata(di_prop_t prop, uchar_t **prop_data) { #ifdef DEBUG if (prop == DI_PROP_NIL) { errno = EINVAL; return (-1); } #endif /* DEBUG */ if (DI_PROP(prop)->prop_len == 0) { *prop_data = NULL; return (0); } if ((DI_PROP(prop)->prop_data == 0) || (DI_PROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } /* * No memory allocation. */ *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_data); return (DI_PROP(prop)->prop_len); } /* * Consolidation private interfaces for accessing I/O multipathing data */ di_path_t di_path_next_client(di_node_t node, di_path_t path) { caddr_t pa; /* * path is not NIL */ if (path != DI_PATH_NIL) { if (DI_PATH(path)->path_p_link != 0) return (DI_PATH((void *)((caddr_t)path - DI_PATH(path)->self + DI_PATH(path)->path_p_link))); else { errno = ENXIO; return (DI_PATH_NIL); } } /* * Path is NIL; the caller is asking for the first path info node */ if (DI_NODE(node)->multipath_phci != 0) { DPRINTF((DI_INFO, "phci: returning %p\n", ((caddr_t)node - DI_NODE(node)->self + DI_NODE(node)->multipath_phci))); return (DI_PATH((caddr_t)node - DI_NODE(node)->self + DI_NODE(node)->multipath_phci)); } /* * No pathing data; check if the snapshot includes path data in order * to set errno properly. */ pa = (caddr_t)node - DI_NODE(node)->self; if (DINFOPATH & (DI_ALL(pa)->command)) errno = ENXIO; else errno = ENOTSUP; return (DI_PATH_NIL); } di_path_t di_path_next_phci(di_node_t node, di_path_t path) { caddr_t pa; /* * path is not NIL */ if (path != DI_PATH_NIL) { if (DI_PATH(path)->path_c_link != 0) return (DI_PATH((caddr_t)path - DI_PATH(path)->self + DI_PATH(path)->path_c_link)); else { errno = ENXIO; return (DI_PATH_NIL); } } /* * Path is NIL; the caller is asking for the first path info node */ if (DI_NODE(node)->multipath_client != 0) { DPRINTF((DI_INFO, "client: returning %p\n", ((caddr_t)node - DI_NODE(node)->self + DI_NODE(node)->multipath_client))); return (DI_PATH((caddr_t)node - DI_NODE(node)->self + DI_NODE(node)->multipath_client)); } /* * No pathing data; check if the snapshot includes path data in order * to set errno properly. */ pa = (caddr_t)node - DI_NODE(node)->self; if (DINFOPATH & (DI_ALL(pa)->command)) errno = ENXIO; else errno = ENOTSUP; return (DI_PATH_NIL); } /* * XXX Obsolete wrapper to be removed. Won't work under multilevel. */ di_path_t di_path_next(di_node_t node, di_path_t path) { if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_PATH_NIL); } if (DI_NODE(node)->multipath_client) { return (di_path_next_phci(node, path)); } else if (DI_NODE(node)->multipath_phci) { return (di_path_next_client(node, path)); } else { /* * The node had multipathing data but didn't appear to be a * phci *or* a client; probably a programmer error. */ errno = EINVAL; return (DI_PATH_NIL); } } di_path_state_t di_path_state(di_path_t path) { return ((di_path_state_t)DI_PATH(path)->path_state); } char * di_path_addr(di_path_t path, char *buf) { caddr_t pa; /* starting address of map */ pa = (caddr_t)path - DI_PATH(path)->self; (void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr), MAXPATHLEN); return (buf); } di_node_t di_path_client_node(di_path_t path) { caddr_t pa; /* starting address of map */ if (path == DI_PATH_NIL) { errno = EINVAL; return (DI_PATH_NIL); } DPRINTF((DI_TRACE, "Get client node for path %p\n", path)); pa = (caddr_t)path - DI_PATH(path)->self; if (DI_PATH(path)->path_client) { return (DI_NODE(pa + DI_PATH(path)->path_client)); } /* * Deal with error condition: * If parent doesn't exist and node is not the root, * set errno to ENOTSUP. Otherwise, set errno to ENXIO. */ if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0) errno = ENOTSUP; else errno = ENXIO; return (DI_NODE_NIL); } di_node_t di_path_phci_node(di_path_t path) { caddr_t pa; /* starting address of map */ if (path == DI_PATH_NIL) { errno = EINVAL; return (DI_PATH_NIL); } DPRINTF((DI_TRACE, "Get phci node for path %p\n", path)); pa = (caddr_t)path - DI_PATH(path)->self; if (DI_PATH(path)->path_phci) { return (DI_NODE(pa + DI_PATH(path)->path_phci)); } /* * Deal with error condition: * If parent doesn't exist and node is not the root, * set errno to ENOTSUP. Otherwise, set errno to ENXIO. */ if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0) errno = ENOTSUP; else errno = ENXIO; return (DI_NODE_NIL); } di_path_prop_t di_path_prop_next(di_path_t path, di_path_prop_t prop) { caddr_t pa; if (path == DI_PATH_NIL) { errno = EINVAL; return (DI_PROP_NIL); } /* * prop is not NIL */ if (prop != DI_PROP_NIL) { if (DI_PROP(prop)->next != 0) return (DI_PATHPROP((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->next)); else { errno = ENXIO; return (DI_PROP_NIL); } } /* * prop is NIL-->caller asks for first property */ pa = (caddr_t)path - DI_PATH(path)->self; if (DI_PATH(path)->path_prop != 0) { return (DI_PATHPROP(pa + DI_PATH(path)->path_prop)); } /* * no property data-->check if snapshot includes props * in order to set the correct errno */ if (DINFOPROP & (DI_ALL(pa)->command)) errno = ENXIO; else errno = ENOTSUP; return (DI_PROP_NIL); } char * di_path_prop_name(di_path_prop_t prop) { caddr_t pa; /* starting address of map */ pa = (caddr_t)prop - DI_PATHPROP(prop)->self; return ((char *)(pa + DI_PATHPROP(prop)->prop_name)); } int di_path_prop_len(di_path_prop_t prop) { return (DI_PATHPROP(prop)->prop_len); } int di_path_prop_type(di_path_prop_t prop) { switch (DI_PATHPROP(prop)->prop_type) { case DDI_PROP_TYPE_INT: return (DI_PROP_TYPE_INT); case DDI_PROP_TYPE_INT64: return (DI_PROP_TYPE_INT64); case DDI_PROP_TYPE_BYTE: return (DI_PROP_TYPE_BYTE); case DDI_PROP_TYPE_STRING: return (DI_PROP_TYPE_STRING); } return (DI_PROP_TYPE_UNKNOWN); } int di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data) { if ((DI_PATHPROP(prop)->prop_data == 0) || (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data); return (di_prop_decode_common((void *)prop_data, DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); } int di_path_prop_ints(di_path_prop_t prop, int **prop_data) { if (DI_PATHPROP(prop)->prop_len == 0) return (0); if ((DI_PATHPROP(prop)->prop_data == 0) || (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data)); return (di_prop_decode_common((void *)prop_data, DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); } int di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data) { if (DI_PATHPROP(prop)->prop_len == 0) return (0); if ((DI_PATHPROP(prop)->prop_data == 0) || (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data)); return (di_prop_decode_common((void *)prop_data, DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); } int di_path_prop_strings(di_path_prop_t prop, char **prop_data) { if (DI_PATHPROP(prop)->prop_len == 0) return (0); if ((DI_PATHPROP(prop)->prop_data == 0) || (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { errno = EFAULT; *prop_data = NULL; return (-1); } *prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data); return (di_prop_decode_common((void *)prop_data, DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); } static di_path_prop_t di_path_prop_search(di_path_t path, const char *name, int type) { di_path_prop_t prop = DI_PROP_NIL; /* * Sanity check arguments */ if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) || !DI_PROP_TYPE_VALID(type)) { errno = EINVAL; return (DI_PROP_NIL); } while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) { int prop_type = di_path_prop_type(prop); DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n", di_path_prop_name(prop), prop_type)); if (strcmp(name, di_path_prop_name(prop)) != 0) continue; if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type)) continue; return (prop); } return (DI_PROP_NIL); } int di_path_prop_lookup_bytes(di_path_t path, const char *prop_name, uchar_t **prop_data) { di_path_prop_t prop; if ((prop = di_path_prop_search(path, prop_name, DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) return (-1); return (di_path_prop_bytes(prop, prop_data)); } int di_path_prop_lookup_ints(di_path_t path, const char *prop_name, int **prop_data) { di_path_prop_t prop; if ((prop = di_path_prop_search(path, prop_name, DI_PROP_TYPE_INT)) == DI_PROP_NIL) return (-1); return (di_path_prop_ints(prop, prop_data)); } int di_path_prop_lookup_int64s(di_path_t path, const char *prop_name, int64_t **prop_data) { di_path_prop_t prop; if ((prop = di_path_prop_search(path, prop_name, DI_PROP_TYPE_INT64)) == DI_PROP_NIL) return (-1); return (di_path_prop_int64s(prop, prop_data)); } int di_path_prop_lookup_strings(di_path_t path, const char *prop_name, char **prop_data) { di_path_prop_t prop; if ((prop = di_path_prop_search(path, prop_name, DI_PROP_TYPE_STRING)) == DI_PROP_NIL) return (-1); return (di_path_prop_strings(prop, prop_data)); } /* * Consolidation private interfaces for private data */ void * di_parent_private_data(di_node_t node) { caddr_t pa; if (DI_NODE(node)->parent_data == 0) { errno = ENXIO; return (NULL); } if (DI_NODE(node)->parent_data == (di_off_t)-1) { /* * Private data requested, but not obtained due to a memory * error (e.g. wrong format specified) */ errno = EFAULT; return (NULL); } pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->parent_data) return (pa + DI_NODE(node)->parent_data); if (DI_ALL(pa)->command & DINFOPRIVDATA) errno = ENXIO; else errno = ENOTSUP; return (NULL); } void * di_driver_private_data(di_node_t node) { caddr_t pa; if (DI_NODE(node)->driver_data == 0) { errno = ENXIO; return (NULL); } if (DI_NODE(node)->driver_data == (di_off_t)-1) { /* * Private data requested, but not obtained due to a memory * error (e.g. wrong format specified) */ errno = EFAULT; return (NULL); } pa = (caddr_t)node - DI_NODE(node)->self; if (DI_NODE(node)->driver_data) return (pa + DI_NODE(node)->driver_data); if (DI_ALL(pa)->command & DINFOPRIVDATA) errno = ENXIO; else errno = ENOTSUP; return (NULL); } /* * PROM property access */ /* * openprom driver stuff: * The maximum property length depends on the buffer size. We use * OPROMMAXPARAM defined in * * MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275 * MAXVALSZ is maximum value size, which is whatever space left in buf */ #define OBP_MAXBUF OPROMMAXPARAM - sizeof (int) #define OBP_MAXPROPLEN OBP_MAXBUF - OBP_MAXPROPNAME; struct di_prom_prop { char *name; int len; uchar_t *data; struct di_prom_prop *next; /* form a linked list */ }; struct di_prom_handle { /* handle to prom */ mutex_t lock; /* synchronize access to openprom fd */ int fd; /* /dev/openprom file descriptor */ struct di_prom_prop *list; /* linked list of prop */ union { char buf[OPROMMAXPARAM]; struct openpromio opp; } oppbuf; }; di_prom_handle_t di_prom_init() { struct di_prom_handle *p; if ((p = malloc(sizeof (struct di_prom_handle))) == NULL) return (DI_PROM_HANDLE_NIL); DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p)); (void) mutex_init(&p->lock, USYNC_THREAD, NULL); if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) { free(p); return (DI_PROM_HANDLE_NIL); } p->list = NULL; return ((di_prom_handle_t)p); } static void di_prom_prop_free(struct di_prom_prop *list) { struct di_prom_prop *tmp = list; while (tmp != NULL) { list = tmp->next; if (tmp->name != NULL) { free(tmp->name); } if (tmp->data != NULL) { free(tmp->data); } free(tmp); tmp = list; } } void di_prom_fini(di_prom_handle_t ph) { struct di_prom_handle *p = (struct di_prom_handle *)ph; DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p)); (void) close(p->fd); (void) mutex_destroy(&p->lock); di_prom_prop_free(p->list); free(p); } /* * Internal library interface for locating the property * XXX: ph->lock must be held for the duration of call. */ static di_prom_prop_t di_prom_prop_found(di_prom_handle_t ph, int nodeid, di_prom_prop_t prom_prop) { struct di_prom_handle *p = (struct di_prom_handle *)ph; struct openpromio *opp = &p->oppbuf.opp; int *ip = (int *)((void *)opp->oprom_array); struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop; DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid)); /* * Set "current" nodeid in the openprom driver */ opp->oprom_size = sizeof (int); *ip = nodeid; if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid)); return (DI_PROM_PROP_NIL); } DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid)); bzero(opp, OBP_MAXBUF); opp->oprom_size = OBP_MAXPROPNAME; if (prom_prop != DI_PROM_PROP_NIL) (void) strcpy(opp->oprom_array, prop->name); if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0)) return (DI_PROM_PROP_NIL); /* * Prom property found. Allocate struct for storing prop * (reuse variable prop) */ if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) return (DI_PROM_PROP_NIL); /* * Get a copy of property name */ if ((prop->name = strdup(opp->oprom_array)) == NULL) { free(prop); return (DI_PROM_PROP_NIL); } /* * get property value and length */ opp->oprom_size = OBP_MAXPROPLEN; if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || (opp->oprom_size == (uint_t)-1)) { free(prop->name); free(prop); return (DI_PROM_PROP_NIL); } /* * make a copy of the property value */ prop->len = opp->oprom_size; if (prop->len == 0) prop->data = NULL; else if ((prop->data = malloc(prop->len)) == NULL) { free(prop->name); free(prop); return (DI_PROM_PROP_NIL); } bcopy(opp->oprom_array, prop->data, prop->len); /* * Prepend prop to list in prom handle */ prop->next = p->list; p->list = prop; return ((di_prom_prop_t)prop); } di_prom_prop_t di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop) { struct di_prom_handle *p = (struct di_prom_handle *)ph; DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n", node, p)); /* * paranoid check */ if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { errno = EINVAL; return (DI_PROM_PROP_NIL); } if (di_nodeid(node) != DI_PROM_NODEID) { errno = ENXIO; return (DI_PROM_PROP_NIL); } /* * synchronize access to prom file descriptor */ (void) mutex_lock(&p->lock); /* * look for next property */ prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop); (void) mutex_unlock(&p->lock); return (prom_prop); } char * di_prom_prop_name(di_prom_prop_t prom_prop) { /* * paranoid check */ if (prom_prop == DI_PROM_PROP_NIL) { errno = EINVAL; return (NULL); } return (((struct di_prom_prop *)prom_prop)->name); } int di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data) { /* * paranoid check */ if (prom_prop == DI_PROM_PROP_NIL) { errno = EINVAL; return (NULL); } *prom_prop_data = ((struct di_prom_prop *)prom_prop)->data; return (((struct di_prom_prop *)prom_prop)->len); } /* * Internal library interface for locating the property * Returns length if found, -1 if prop doesn't exist. */ static struct di_prom_prop * di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node, const char *prom_prop_name) { struct openpromio *opp; struct di_prom_prop *prop; struct di_prom_handle *p = (struct di_prom_handle *)ph; /* * paranoid check */ if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { errno = EINVAL; return (NULL); } if (di_nodeid(node) != DI_PROM_NODEID) { errno = ENXIO; return (NULL); } opp = &p->oppbuf.opp; (void) mutex_lock(&p->lock); opp->oprom_size = sizeof (int); opp->oprom_node = DI_NODE(node)->nodeid; if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { errno = ENXIO; DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", DI_NODE(node)->nodeid)); (void) mutex_unlock(&p->lock); return (NULL); } /* * get property length */ bzero(opp, OBP_MAXBUF); opp->oprom_size = OBP_MAXPROPLEN; (void) strcpy(opp->oprom_array, prom_prop_name); if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) || (opp->oprom_len == -1)) { /* no such property */ (void) mutex_unlock(&p->lock); return (NULL); } /* * Prom property found. Allocate struct for storing prop */ if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) { (void) mutex_unlock(&p->lock); return (NULL); } prop->name = NULL; /* we don't need the name */ prop->len = opp->oprom_len; if (prop->len == 0) { /* boolean property */ prop->data = NULL; prop->next = p->list; p->list = prop; (void) mutex_unlock(&p->lock); return (prop); } /* * retrieve the property value */ bzero(opp, OBP_MAXBUF); opp->oprom_size = OBP_MAXPROPLEN; (void) strcpy(opp->oprom_array, prom_prop_name); if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || (opp->oprom_size == (uint_t)-1)) { /* error retrieving property value */ (void) mutex_unlock(&p->lock); free(prop); return (NULL); } /* * make a copy of the property value, stick in ph->list */ if ((prop->data = malloc(prop->len)) == NULL) { (void) mutex_unlock(&p->lock); free(prop); return (NULL); } bcopy(opp->oprom_array, prop->data, prop->len); prop->next = p->list; p->list = prop; (void) mutex_unlock(&p->lock); return (prop); } int di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node, const char *prom_prop_name, int **prom_prop_data) { int len; struct di_prom_prop *prop; prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); if (prop == NULL) { *prom_prop_data = NULL; return (-1); } if (prop->len == 0) { /* boolean property */ *prom_prop_data = NULL; return (0); } len = di_prop_decode_common((void *)&prop->data, prop->len, DI_PROP_TYPE_INT, 1); *prom_prop_data = (int *)((void *)prop->data); return (len); } int di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node, const char *prom_prop_name, char **prom_prop_data) { int len; struct di_prom_prop *prop; prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); if (prop == NULL) { *prom_prop_data = NULL; return (-1); } if (prop->len == 0) { /* boolean property */ *prom_prop_data = NULL; return (0); } /* * Fix an openprom bug (OBP string not NULL terminated). * XXX This should really be fixed in promif. */ if (((char *)prop->data)[prop->len - 1] != '\0') { uchar_t *tmp; prop->len++; if ((tmp = realloc(prop->data, prop->len)) == NULL) return (-1); prop->data = tmp; ((char *)prop->data)[prop->len - 1] = '\0'; DPRINTF((DI_INFO, "OBP string not NULL terminated: " "node=%s, prop=%s, val=%s\n", di_node_name(node), prom_prop_name, prop->data)); } len = di_prop_decode_common((void *)&prop->data, prop->len, DI_PROP_TYPE_STRING, 1); *prom_prop_data = (char *)prop->data; return (len); } int di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node, const char *prom_prop_name, uchar_t **prom_prop_data) { int len; struct di_prom_prop *prop; prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); if (prop == NULL) { *prom_prop_data = NULL; return (-1); } if (prop->len == 0) { /* boolean property */ *prom_prop_data = NULL; return (0); } len = di_prop_decode_common((void *)&prop->data, prop->len, DI_PROP_TYPE_BYTE, 1); *prom_prop_data = prop->data; return (len); } di_lnode_t di_link_to_lnode(di_link_t link, uint_t endpoint) { struct di_all *di_all; if ((link == DI_LINK_NIL) || ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { errno = EINVAL; return (DI_LNODE_NIL); } di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self); if (endpoint == DI_LINK_SRC) { return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode)); } else { return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode)); } /* NOTREACHED */ } char * di_lnode_name(di_lnode_t lnode) { return (di_driver_name(di_lnode_devinfo(lnode))); } di_node_t di_lnode_devinfo(di_lnode_t lnode) { struct di_all *di_all; di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node)); } int di_lnode_devt(di_lnode_t lnode, dev_t *devt) { if ((lnode == DI_LNODE_NIL) || (devt == NULL)) { errno = EINVAL; return (-1); } if ((DI_LNODE(lnode)->dev_major == (major_t)-1) && (DI_LNODE(lnode)->dev_minor == (minor_t)-1)) return (-1); *devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor); return (0); } int di_link_spectype(di_link_t link) { return (DI_LINK(link)->spec_type); } void di_minor_private_set(di_minor_t minor, void *data) { DI_MINOR(minor)->user_private_data = (uintptr_t)data; } void * di_minor_private_get(di_minor_t minor) { return ((void *)(uintptr_t)DI_MINOR(minor)->user_private_data); } void di_node_private_set(di_node_t node, void *data) { DI_NODE(node)->user_private_data = (uintptr_t)data; } void * di_node_private_get(di_node_t node) { return ((void *)(uintptr_t)DI_NODE(node)->user_private_data); } void di_lnode_private_set(di_lnode_t lnode, void *data) { DI_LNODE(lnode)->user_private_data = (uintptr_t)data; } void * di_lnode_private_get(di_lnode_t lnode) { return ((void *)(uintptr_t)DI_LNODE(lnode)->user_private_data); } void di_link_private_set(di_link_t link, void *data) { DI_LINK(link)->user_private_data = (uintptr_t)data; } void * di_link_private_get(di_link_t link) { return ((void *)(uintptr_t)DI_LINK(link)->user_private_data); } di_lnode_t di_lnode_next(di_node_t node, di_lnode_t lnode) { struct di_all *di_all; /* * paranoid error checking */ if (node == DI_NODE_NIL) { errno = EINVAL; return (DI_LNODE_NIL); } di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); if (lnode == DI_NODE_NIL) { if (DI_NODE(node)->lnodes != NULL) return (DI_LNODE((caddr_t)di_all + DI_NODE(node)->lnodes)); } else { if (DI_LNODE(lnode)->node_next != NULL) return (DI_LNODE((caddr_t)di_all + DI_LNODE(lnode)->node_next)); } if (DINFOLYR & DI_ALL(di_all)->command) errno = ENXIO; else errno = ENOTSUP; return (DI_LNODE_NIL); } di_link_t di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint) { struct di_all *di_all; /* * paranoid error checking */ if ((node == DI_NODE_NIL) || ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { errno = EINVAL; return (DI_LINK_NIL); } di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); if (endpoint == DI_LINK_SRC) { if (link == DI_LINK_NIL) { if (DI_NODE(node)->src_links != NULL) return (DI_LINK((caddr_t)di_all + DI_NODE(node)->src_links)); } else { if (DI_LINK(link)->src_node_next != NULL) return (DI_LINK((caddr_t)di_all + DI_LINK(link)->src_node_next)); } } else { if (link == DI_LINK_NIL) { if (DI_NODE(node)->tgt_links != NULL) return (DI_LINK((caddr_t)di_all + DI_NODE(node)->tgt_links)); } else { if (DI_LINK(link)->tgt_node_next != NULL) return (DI_LINK((caddr_t)di_all + DI_LINK(link)->tgt_node_next)); } } if (DINFOLYR & DI_ALL(di_all)->command) errno = ENXIO; else errno = ENOTSUP; return (DI_LINK_NIL); } di_link_t di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint) { struct di_all *di_all; /* * paranoid error checking */ if ((lnode == DI_LNODE_NIL) || ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { errno = EINVAL; return (DI_LINK_NIL); } di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); if (endpoint == DI_LINK_SRC) { if (link == DI_LINK_NIL) { if (DI_LNODE(lnode)->link_out == NULL) return (DI_LINK_NIL); return (DI_LINK((caddr_t)di_all + DI_LNODE(lnode)->link_out)); } else { if (DI_LINK(link)->src_link_next == NULL) return (DI_LINK_NIL); return (DI_LINK((caddr_t)di_all + DI_LINK(link)->src_link_next)); } } else { if (link == DI_LINK_NIL) { if (DI_LNODE(lnode)->link_in == NULL) return (DI_LINK_NIL); return (DI_LINK((caddr_t)di_all + DI_LNODE(lnode)->link_in)); } else { if (DI_LINK(link)->tgt_link_next == NULL) return (DI_LINK_NIL); return (DI_LINK((caddr_t)di_all + DI_LINK(link)->tgt_link_next)); } } /* NOTREACHED */ } /* * Internal library function: * Invoke callback for each link data on the link list of first node * on node_list headp, and place children of first node on the list. * * This is similar to walk_one_node, except we only walk in child * first mode. */ static void walk_one_link(struct node_list **headp, uint_t ep, void *arg, int (*callback)(di_link_t link, void *arg)) { int action = DI_WALK_CONTINUE; di_link_t link = DI_LINK_NIL; di_node_t node = (*headp)->node; while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) { action = callback(link, arg); if (action == DI_WALK_TERMINATE) { break; } } update_node_list(action, DI_WALK_LINKGEN, headp); } int di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg, int (*link_callback)(di_link_t link, void *arg)) { struct node_list *head; /* node_list for tree walk */ #ifdef DEBUG char *path = di_devfs_path(root); DPRINTF((DI_INFO, "walking %s link data under %s\n", (endpoint == DI_LINK_SRC) ? "src" : "tgt", path)); di_devfs_path_free(path); #endif /* * paranoid error checking */ if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) || ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { errno = EINVAL; return (-1); } if ((head = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); return (-1); } head->next = NULL; head->node = root; DPRINTF((DI_INFO, "Start link data walking from node %s\n", di_node_name(root))); while (head != NULL) walk_one_link(&head, endpoint, arg, link_callback); return (0); } /* * Internal library function: * Invoke callback for each link data on the link list of first node * on node_list headp, and place children of first node on the list. * * This is similar to walk_one_node, except we only walk in child * first mode. */ static void walk_one_lnode(struct node_list **headp, void *arg, int (*callback)(di_lnode_t lnode, void *arg)) { int action = DI_WALK_CONTINUE; di_lnode_t lnode = DI_LNODE_NIL; di_node_t node = (*headp)->node; while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) { action = callback(lnode, arg); if (action == DI_WALK_TERMINATE) { break; } } update_node_list(action, DI_WALK_LINKGEN, headp); } int di_walk_lnode(di_node_t root, uint_t flag, void *arg, int (*lnode_callback)(di_lnode_t lnode, void *arg)) { struct node_list *head; /* node_list for tree walk */ #ifdef DEBUG char *path = di_devfs_path(root); DPRINTF((DI_INFO, "walking lnode data under %s\n", path)); di_devfs_path_free(path); #endif /* * paranoid error checking */ if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) { errno = EINVAL; return (-1); } if ((head = malloc(sizeof (struct node_list))) == NULL) { DPRINTF((DI_ERR, "malloc of node_list failed\n")); return (-1); } head->next = NULL; head->node = root; DPRINTF((DI_INFO, "Start lnode data walking from node %s\n", di_node_name(root))); while (head != NULL) walk_one_lnode(&head, arg, lnode_callback); return (0); } di_node_t di_lookup_node(di_node_t root, char *path) { struct di_all *dap; di_node_t node; char copy[MAXPATHLEN]; char *slash, *pname, *paddr; /* * Path must be absolute and musn't have duplicate slashes */ if (*path != '/' || strstr(path, "//")) { DPRINTF((DI_ERR, "Invalid path: %s\n", path)); return (DI_NODE_NIL); } if (root == DI_NODE_NIL) { DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n")); return (DI_NODE_NIL); } dap = DI_ALL((caddr_t)root - DI_NODE(root)->self); if (strcmp(dap->root_path, "/") != 0) { DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path)); return (DI_NODE_NIL); } if (strlcpy(copy, path, sizeof (copy)) >= sizeof (copy)) { DPRINTF((DI_ERR, "path too long: %s\n", path)); return (DI_NODE_NIL); } for (slash = copy, node = root; slash; ) { /* * Handle path = "/" case as well as trailing '/' */ if (*(slash + 1) == '\0') break; /* * More path-components exist. Deal with the next one */ pname = slash + 1; node = di_child_node(node); if (slash = strchr(pname, '/')) *slash = '\0'; if (paddr = strchr(pname, '@')) *paddr++ = '\0'; for (; node != DI_NODE_NIL; node = di_sibling_node(node)) { char *name, *baddr; name = di_node_name(node); baddr = di_bus_addr(node); if (strcmp(pname, name) != 0) continue; /* * Mappings between a "path-address" and bus-addr * * paddr baddr * --------------------- * NULL NULL * NULL "" * "" N/A (invalid paddr) */ if (paddr && baddr && strcmp(paddr, baddr) == 0) break; if (paddr == NULL && (baddr == NULL || *baddr == '\0')) break; } /* * No nodes in the sibling list or there was no match */ if (node == DI_NODE_NIL) { DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr)); return (DI_NODE_NIL); } } assert(node != DI_NODE_NIL); return (node); } static char * msglevel2str(di_debug_t msglevel) { switch (msglevel) { case DI_ERR: return ("ERROR"); case DI_INFO: return ("Info"); case DI_TRACE: return ("Trace"); case DI_TRACE1: return ("Trace1"); case DI_TRACE2: return ("Trace2"); default: return ("UNKNOWN"); } } void dprint(di_debug_t msglevel, const char *fmt, ...) { va_list ap; char *estr; if (di_debug <= DI_QUIET) return; if (di_debug < msglevel) return; estr = msglevel2str(msglevel); assert(estr); va_start(ap, fmt); (void) fprintf(stderr, "libdevinfo[%lu]: %s: ", (ulong_t)getpid(), estr); (void) vfprintf(stderr, fmt, ap); va_end(ap); } /* end of devinfo.c */