/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2019 Joyent, Inc. */ /* * PICL plug-in that creates device tree nodes for all platforms */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "picldevtree.h" /* * Plugin registration entry points */ static void picldevtree_register(void); static void picldevtree_init(void); static void picldevtree_fini(void); static void picldevtree_evhandler(const char *ename, const void *earg, size_t size, void *cookie); #pragma init(picldevtree_register) /* * Log message texts */ #define DEVINFO_PLUGIN_INIT_FAILED gettext("SUNW_picldevtree failed!\n") #define PICL_EVENT_DROPPED \ gettext("SUNW_picldevtree '%s' event dropped.\n") /* * Macro to get PCI device id (from IEEE 1275 spec) */ #define PCI_DEVICE_ID(x) (((x) >> 11) & 0x1f) /* * Local variables */ static picld_plugin_reg_t my_reg_info = { PICLD_PLUGIN_VERSION_1, PICLD_PLUGIN_CRITICAL, "SUNW_picldevtree", picldevtree_init, picldevtree_fini }; /* * Debug enabling environment variable */ #define SUNW_PICLDEVTREE_PLUGIN_DEBUG "SUNW_PICLDEVTREE_PLUGIN_DEBUG" static int picldevtree_debug = 0; static conf_entries_t *conf_name_class_map = NULL; static builtin_map_t sun4u_map[] = { /* MAX_NAMEVAL_SIZE */ { "SUNW,bpp", PICL_CLASS_PARALLEL}, { "parallel", PICL_CLASS_PARALLEL}, { "floppy", PICL_CLASS_FLOPPY}, { "memory", PICL_CLASS_MEMORY}, { "ebus", PICL_CLASS_EBUS}, { "i2c", PICL_CLASS_I2C}, { "usb", PICL_CLASS_USB}, { "isa", PICL_CLASS_ISA}, { "dma", PICL_CLASS_DMA}, { "keyboard", PICL_CLASS_KEYBOARD}, { "mouse", PICL_CLASS_MOUSE}, { "fan-control", PICL_CLASS_FAN_CONTROL}, { "sc", PICL_CLASS_SYSTEM_CONTROLLER}, { "dimm", PICL_CLASS_SEEPROM}, { "dimm-fru", PICL_CLASS_SEEPROM}, { "cpu", PICL_CLASS_SEEPROM}, { "cpu-fru", PICL_CLASS_SEEPROM}, { "flashprom", PICL_CLASS_FLASHPROM}, { "temperature", PICL_CLASS_TEMPERATURE_DEVICE}, { "motherboard", PICL_CLASS_SEEPROM}, { "motherboard-fru", PICL_CLASS_SEEPROM}, { "motherboard-fru-prom", PICL_CLASS_SEEPROM}, { "pmu", PICL_CLASS_PMU}, { "sound", PICL_CLASS_SOUND}, { "firewire", PICL_CLASS_FIREWIRE}, { "i2c-at34c02", PICL_CLASS_SEEPROM}, { "hardware-monitor", PICL_CLASS_HARDWARE_MONITOR}, { "", ""} }; static builtin_map_t i86pc_map[] = { /* MAX_NAMEVAL_SIZE */ { "cpus", PICL_CLASS_I86CPUS}, { "cpu", PICL_CLASS_CPU}, { "memory", PICL_CLASS_MEMORY}, { "asy", PICL_CLASS_SERIAL}, { "", ""} }; static pname_type_map_t pname_type_map[] = { { "reg", PICL_PTYPE_BYTEARRAY}, { "device_type", PICL_PTYPE_CHARSTRING}, { "ranges", PICL_PTYPE_BYTEARRAY}, { "status", PICL_PTYPE_CHARSTRING}, { "compatible", PICL_PTYPE_CHARSTRING}, { "interrupts", PICL_PTYPE_BYTEARRAY}, { "model", PICL_PTYPE_CHARSTRING}, { "address", PICL_PTYPE_BYTEARRAY}, { "vendor-id", PICL_PTYPE_UNSIGNED_INT}, { "device-id", PICL_PTYPE_UNSIGNED_INT}, { "revision-id", PICL_PTYPE_UNSIGNED_INT}, { "class-code", PICL_PTYPE_UNSIGNED_INT}, { "min-grant", PICL_PTYPE_UNSIGNED_INT}, { "max-latency", PICL_PTYPE_UNSIGNED_INT}, { "devsel-speed", PICL_PTYPE_UNSIGNED_INT}, { "subsystem-id", PICL_PTYPE_UNSIGNED_INT}, { "subsystem-vendor-id", PICL_PTYPE_UNSIGNED_INT}, { "assigned-addresses", PICL_PTYPE_BYTEARRAY}, { "configuration#", PICL_PTYPE_UNSIGNED_INT}, { "assigned-address", PICL_PTYPE_UNSIGNED_INT}, { "#address-cells", PICL_PTYPE_UNSIGNED_INT}, { "#size-cells", PICL_PTYPE_UNSIGNED_INT}, { "clock-frequency", PICL_PTYPE_UNSIGNED_INT}, { "scsi-initiator-id", PICL_PTYPE_UNSIGNED_INT}, { "differential", PICL_PTYPE_UNSIGNED_INT}, { "idprom", PICL_PTYPE_BYTEARRAY}, { "bus-range", PICL_PTYPE_BYTEARRAY}, { "alternate-reg", PICL_PTYPE_BYTEARRAY}, { "power-consumption", PICL_PTYPE_BYTEARRAY}, { "slot-names", PICL_PTYPE_BYTEARRAY}, { "burst-sizes", PICL_PTYPE_UNSIGNED_INT}, { "up-burst-sizes", PICL_PTYPE_UNSIGNED_INT}, { "slot-address-bits", PICL_PTYPE_UNSIGNED_INT}, { "eisa-slots", PICL_PTYPE_BYTEARRAY}, { "dma", PICL_PTYPE_BYTEARRAY}, { "slot-names-index", PICL_PTYPE_UNSIGNED_INT}, { "pnp-csn", PICL_PTYPE_UNSIGNED_INT}, { "pnp-data", PICL_PTYPE_BYTEARRAY}, { "description", PICL_PTYPE_CHARSTRING}, { "pnp-id", PICL_PTYPE_CHARSTRING}, { "max-frame-size", PICL_PTYPE_UNSIGNED_INT}, { "address-bits", PICL_PTYPE_UNSIGNED_INT}, { "local-mac-address", PICL_PTYPE_BYTEARRAY}, { "mac-address", PICL_PTYPE_BYTEARRAY}, { "character-set", PICL_PTYPE_CHARSTRING}, { "available", PICL_PTYPE_BYTEARRAY}, { "port-wwn", PICL_PTYPE_BYTEARRAY}, { "node-wwn", PICL_PTYPE_BYTEARRAY}, { "width", PICL_PTYPE_UNSIGNED_INT}, { "linebytes", PICL_PTYPE_UNSIGNED_INT}, { "height", PICL_PTYPE_UNSIGNED_INT}, { "banner-name", PICL_PTYPE_CHARSTRING}, { "reset-reason", PICL_PTYPE_CHARSTRING}, { "implementation#", PICL_PTYPE_UNSIGNED_INT}, { "version#", PICL_PTYPE_UNSIGNED_INT}, { "icache-size", PICL_PTYPE_UNSIGNED_INT}, { "icache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "icache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "l1-icache-size", PICL_PTYPE_UNSIGNED_INT}, { "l1-icache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "l1-icache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "#itlb-entries", PICL_PTYPE_UNSIGNED_INT}, { "dcache-size", PICL_PTYPE_UNSIGNED_INT}, { "dcache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "dcache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "l1-dcache-size", PICL_PTYPE_UNSIGNED_INT}, { "l1-dcache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "l1-dcache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "#dtlb-entries", PICL_PTYPE_UNSIGNED_INT}, { "ecache-size", PICL_PTYPE_UNSIGNED_INT}, { "ecache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "ecache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "l2-cache-size", PICL_PTYPE_UNSIGNED_INT}, { "l2-cache-line-size", PICL_PTYPE_UNSIGNED_INT}, { "l2-cache-associativity", PICL_PTYPE_UNSIGNED_INT}, { "l2-cache-sharing", PICL_PTYPE_BYTEARRAY}, { "mask#", PICL_PTYPE_UNSIGNED_INT}, { "manufacturer#", PICL_PTYPE_UNSIGNED_INT}, { "sparc-version", PICL_PTYPE_UNSIGNED_INT}, { "version", PICL_PTYPE_CHARSTRING}, { "cpu-model", PICL_PTYPE_UNSIGNED_INT}, { "memory-layout", PICL_PTYPE_BYTEARRAY}, { "#interrupt-cells", PICL_PTYPE_UNSIGNED_INT}, { "interrupt-map", PICL_PTYPE_BYTEARRAY}, { "interrupt-map-mask", PICL_PTYPE_BYTEARRAY} }; #define PNAME_MAP_SIZE sizeof (pname_type_map) / sizeof (pname_type_map_t) static builtin_map_t *builtin_map_ptr = NULL; static int builtin_map_size = 0; static char mach_name[SYS_NMLN]; static di_prom_handle_t ph = DI_PROM_HANDLE_NIL; static int snapshot_stale; /* * UnitAddress mapping table */ static unitaddr_func_t encode_default_unitaddr; static unitaddr_func_t encode_optional_unitaddr; static unitaddr_func_t encode_scsi_unitaddr; static unitaddr_func_t encode_upa_unitaddr; static unitaddr_func_t encode_gptwo_jbus_unitaddr; static unitaddr_func_t encode_pci_unitaddr; static unitaddr_map_t unitaddr_map_table[] = { {PICL_CLASS_JBUS, encode_gptwo_jbus_unitaddr, 0}, {PICL_CLASS_GPTWO, encode_gptwo_jbus_unitaddr, 0}, {PICL_CLASS_PCI, encode_pci_unitaddr, 0}, {PICL_CLASS_PCIEX, encode_pci_unitaddr, 0}, {PICL_CLASS_UPA, encode_upa_unitaddr, 0}, {PICL_CLASS_SCSI, encode_scsi_unitaddr, 0}, {PICL_CLASS_SCSI2, encode_scsi_unitaddr, 0}, {PICL_CLASS_EBUS, encode_default_unitaddr, 2}, {PICL_CLASS_SBUS, encode_default_unitaddr, 2}, {PICL_CLASS_I2C, encode_default_unitaddr, 2}, {PICL_CLASS_USB, encode_default_unitaddr, 1}, {PICL_CLASS_PMU, encode_optional_unitaddr, 2}, {NULL, encode_default_unitaddr, 0} }; static int add_unitaddr_prop_to_subtree(picl_nodehdl_t nodeh); static int get_unitaddr(picl_nodehdl_t parh, picl_nodehdl_t nodeh, char *unitaddr, size_t ualen); static void set_pci_pciex_deviceid(picl_nodehdl_t plafh); /* * The mc event completion handler. * The arguments are event name buffer and a packed nvlist buffer * with the size specifying the size of unpacked nvlist. These * buffers are deallcoated here. * * Also, if a memory controller node is being removed then destroy the * PICL subtree associated with that memory controller. */ static void mc_completion_handler(char *ename, void *earg, size_t size) { picl_nodehdl_t mch; nvlist_t *unpack_nvl; if (strcmp(ename, PICLEVENT_MC_REMOVED) == 0 && nvlist_unpack(earg, size, &unpack_nvl, 0) == 0) { mch = 0; (void) nvlist_lookup_uint64(unpack_nvl, PICLEVENTARG_NODEHANDLE, &mch); if (mch != 0) { if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: destroying_node:%llx\n", mch); (void) ptree_destroy_node(mch); } nvlist_free(unpack_nvl); } free(ename); free(earg); } /* * Functions to post memory controller change event */ static int post_mc_event(char *ename, picl_nodehdl_t mch) { nvlist_t *nvl; size_t nvl_size; char *pack_buf; char *ev_name; ev_name = strdup(ename); if (ev_name == NULL) return (-1); if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) { free(ev_name); return (-1); } pack_buf = NULL; if (nvlist_add_uint64(nvl, PICLEVENTARG_NODEHANDLE, mch) || nvlist_pack(nvl, &pack_buf, &nvl_size, NV_ENCODE_NATIVE, 0)) { free(ev_name); nvlist_free(nvl); return (-1); } if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: posting MC event ename:%s nodeh:%llx\n", ev_name, mch); if (ptree_post_event(ev_name, pack_buf, nvl_size, mc_completion_handler) != PICL_SUCCESS) { free(ev_name); nvlist_free(nvl); return (-1); } nvlist_free(nvl); return (0); } /* * Lookup a name in the name to class map tables */ static int lookup_name_class_map(char *classbuf, const char *nm) { conf_entries_t *ptr; int i; /* * check name to class mapping in conf file */ ptr = conf_name_class_map; while (ptr != NULL) { if (strcmp(ptr->name, nm) == 0) { (void) strlcpy(classbuf, ptr->piclclass, PICL_CLASSNAMELEN_MAX); return (0); } ptr = ptr->next; } /* * check name to class mapping in builtin table */ if (builtin_map_ptr == NULL) return (-1); for (i = 0; i < builtin_map_size; ++i) if (strcmp(builtin_map_ptr[i].name, nm) == 0) { (void) strlcpy(classbuf, builtin_map_ptr[i].piclclass, PICL_CLASSNAMELEN_MAX); return (0); } return (-1); } /* * Lookup a prop name in the pname to class map table */ static int lookup_pname_type_map(const char *pname, picl_prop_type_t *type) { int i; for (i = 0; i < PNAME_MAP_SIZE; ++i) if (strcmp(pname_type_map[i].pname, pname) == 0) { *type = pname_type_map[i].type; return (0); } return (-1); } /* * Return the number of strings in the buffer */ static int get_string_count(char *strdat, int length) { int count; char *lastnull; char *nullptr; count = 1; for (lastnull = &strdat[length - 1], nullptr = strchr(strdat, '\0'); nullptr != lastnull; nullptr = strchr(nullptr+1, '\0')) count++; return (count); } /* * Return 1 if the node has a "reg" property */ static int has_reg_prop(di_node_t dn) { int *pdata; int dret; dret = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, OBP_REG, &pdata); if (dret > 0) return (1); if (!ph) return (0); dret = di_prom_prop_lookup_ints(ph, dn, OBP_REG, &pdata); return (dret < 0 ? 0 : 1); } /* * This function copies a PROM node's device_type property value into the * buffer given by outbuf. The buffer size is PICL_CLASSNAMELEN_MAX. * * We reclassify device_type 'fru-prom' to PICL class 'seeprom' * for FRUID support. */ static int get_device_type(char *outbuf, di_node_t dn) { char *pdata; char *pdatap; int dret; int i; dret = di_prop_lookup_strings(DDI_DEV_T_ANY, dn, OBP_DEVICETYPE, &pdata); if (dret <= 0) { if (!ph) return (-1); dret = di_prom_prop_lookup_strings(ph, dn, OBP_DEVICETYPE, &pdata); if (dret <= 0) { return (-1); } } if (dret != 1) { /* * multiple strings */ pdatap = pdata; for (i = 0; i < (dret - 1); ++i) { pdatap += strlen(pdatap); *pdatap = '-'; /* replace '\0' with '-' */ pdatap++; } } if (strcasecmp(pdata, "fru-prom") == 0) { /* * Use PICL 'seeprom' class for fru-prom device types */ (void) strlcpy(outbuf, PICL_CLASS_SEEPROM, PICL_CLASSNAMELEN_MAX); } else { (void) strlcpy(outbuf, pdata, PICL_CLASSNAMELEN_MAX); } return (0); } /* * Get the minor node name in the class buffer passed */ static int get_minor_class(char *classbuf, di_node_t dn) { di_minor_t mi_node; char *mi_nodetype; char *mi_name; /* get minor node type */ mi_node = di_minor_next(dn, DI_MINOR_NIL); if (mi_node == DI_MINOR_NIL) return (-1); mi_nodetype = di_minor_nodetype(mi_node); if (mi_nodetype == NULL) { /* no type info, return name */ mi_name = di_minor_name(mi_node); if (mi_name == NULL) return (-1); (void) strlcpy(classbuf, mi_name, PICL_CLASSNAMELEN_MAX); return (0); } #define DDI_NODETYPE(x, y) (strncmp(x, y, (sizeof (y) - 1)) == 0) /* * convert the string to the picl class for non-peudo nodes */ if (DDI_NODETYPE(mi_nodetype, DDI_PSEUDO)) return (-1); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_BLOCK_WWN)) (void) strcpy(classbuf, PICL_CLASS_BLOCK); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_BLOCK_CHAN)) (void) strcpy(classbuf, PICL_CLASS_BLOCK); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_CD)) (void) strcpy(classbuf, PICL_CLASS_CDROM); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_CD_CHAN)) (void) strcpy(classbuf, PICL_CLASS_CDROM); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_FD)) (void) strcpy(classbuf, PICL_CLASS_FLOPPY); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_BLOCK_FABRIC)) (void) strcpy(classbuf, PICL_CLASS_FABRIC); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_BLOCK_SAS)) (void) strcpy(classbuf, PICL_CLASS_SAS); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_BLOCK)) (void) strcpy(classbuf, PICL_CLASS_BLOCK); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_MOUSE)) (void) strcpy(classbuf, PICL_CLASS_MOUSE); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_KEYBOARD)) (void) strcpy(classbuf, PICL_CLASS_KEYBOARD); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_ATTACHMENT_POINT)) (void) strcpy(classbuf, PICL_CLASS_ATTACHMENT_POINT); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_TAPE)) (void) strcpy(classbuf, PICL_CLASS_TAPE); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_SCSI_ENCLOSURE)) (void) strcpy(classbuf, PICL_CLASS_SCSI); else if (DDI_NODETYPE(mi_nodetype, DDI_NT_ENCLOSURE)) { char *colon; if ((colon = strchr(mi_nodetype, ':')) == NULL) return (-1); ++colon; (void) strcpy(classbuf, colon); } else { /* unrecognized type, return name */ mi_name = di_minor_name(mi_node); if (mi_name == NULL) return (-1); (void) strlcpy(classbuf, mi_name, PICL_CLASSNAMELEN_MAX); } return (0); } /* * Derive PICL class using the compatible property of the node * We use the map table to map compatible property value to * class. */ static int get_compatible_class(char *outbuf, di_node_t dn) { char *pdata; char *pdatap; int dret; int i; dret = di_prop_lookup_strings(DDI_DEV_T_ANY, dn, OBP_COMPATIBLE, &pdata); if (dret <= 0) { if (!ph) return (-1); dret = di_prom_prop_lookup_strings(ph, dn, OBP_COMPATIBLE, &pdata); if (dret <= 0) { return (-1); } } pdatap = pdata; for (i = 0; i < dret; ++i) { if (lookup_name_class_map(outbuf, pdatap) == 0) return (0); pdatap += strlen(pdatap); pdatap++; } return (-1); } /* * For a given device node find the PICL class to use. Returns NULL * for non device node */ static int get_node_class(char *classbuf, di_node_t dn, const char *nodename) { if (get_device_type(classbuf, dn) == 0) { if (di_nodeid(dn) == DI_PROM_NODEID) { /* * discard place holder nodes */ if ((strcmp(classbuf, DEVICE_TYPE_BLOCK) == 0) || (strcmp(classbuf, DEVICE_TYPE_BYTE) == 0) || (strcmp(classbuf, DEVICE_TYPE_SES) == 0) || (strcmp(classbuf, DEVICE_TYPE_FP) == 0) || (strcmp(classbuf, DEVICE_TYPE_DISK) == 0)) return (-1); return (0); } return (0); /* return device_type value */ } if (get_compatible_class(classbuf, dn) == 0) { return (0); /* derive class using compatible prop */ } if (lookup_name_class_map(classbuf, nodename) == 0) return (0); /* derive class using name prop */ if (has_reg_prop(dn)) { /* use default obp-device */ (void) strcpy(classbuf, PICL_CLASS_OBP_DEVICE); return (0); } return (get_minor_class(classbuf, dn)); } /* * Add a table property containing nrows with one column */ static int add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist, unsigned int nrows) { ptree_propinfo_t propinfo; picl_prophdl_t proph; picl_prophdl_t tblh; int err; unsigned int i; unsigned int j; picl_prophdl_t *proprow; int len; #define NCOLS_IN_STRING_TABLE 1 err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_TABLE, PICL_READ, sizeof (picl_prophdl_t), name, NULL, NULL); if (err != PICL_SUCCESS) return (err); err = ptree_create_table(&tblh); if (err != PICL_SUCCESS) return (err); err = ptree_create_and_add_prop(nodeh, &propinfo, &tblh, &proph); if (err != PICL_SUCCESS) return (err); proprow = calloc(nrows, sizeof (picl_prophdl_t)); if (proprow == NULL) { (void) ptree_destroy_prop(proph); return (PICL_FAILURE); } for (j = 0; j < nrows; ++j) { len = strlen(strlist) + 1; err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, len, name, NULL, NULL); if (err != PICL_SUCCESS) break; err = ptree_create_prop(&propinfo, strlist, &proprow[j]); if (err != PICL_SUCCESS) break; strlist += len; err = ptree_add_row_to_table(tblh, NCOLS_IN_STRING_TABLE, &proprow[j]); if (err != PICL_SUCCESS) break; } if (err != PICL_SUCCESS) { for (i = 0; i < j; ++i) (void) ptree_destroy_prop(proprow[i]); (void) ptree_delete_prop(proph); (void) ptree_destroy_prop(proph); } free(proprow); return (err); } /* * return 1 if this node has this property with the given value */ static int compare_string_propval(picl_nodehdl_t nodeh, const char *pname, const char *pval) { char *pvalbuf; int err; int len; ptree_propinfo_t pinfo; picl_prophdl_t proph; int rv; err = ptree_get_prop_by_name(nodeh, pname, &proph); if (err != PICL_SUCCESS) /* prop doesn't exist */ return (0); err = ptree_get_propinfo(proph, &pinfo); if (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING) return (0); /* not string prop */ len = strlen(pval) + 1; pvalbuf = malloc(len); if (pvalbuf == NULL) return (0); err = ptree_get_propval(proph, pvalbuf, len); if ((err == PICL_SUCCESS) && (strcmp(pvalbuf, pval) == 0)) rv = 1; /* prop match */ else rv = 0; free(pvalbuf); return (rv); } /* * This function recursively searches the tree for a node that has * the specified string property name and value */ static int find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname, const char *pval, picl_nodehdl_t *nodeh) { picl_nodehdl_t childh; int err; for (err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND; err = ptree_get_propval_by_name(childh, PICL_PROP_PEER, &childh, sizeof (picl_nodehdl_t))) { if (err != PICL_SUCCESS) return (err); if (compare_string_propval(childh, pname, pval)) { *nodeh = childh; return (PICL_SUCCESS); } if (find_node_by_string_prop(childh, pname, pval, nodeh) == PICL_SUCCESS) return (PICL_SUCCESS); } return (PICL_FAILURE); } /* * check if this is a string prop * If the length is less than or equal to 4, assume it's not a string list. * If there is any non-ascii or non-print char, it's not a string prop * If \0 is in the first char or any two consecutive \0's exist, * it's a bytearray prop. * Return value: 0 means it's not a string prop, 1 means it's a string prop */ static int is_string_propval(unsigned char *pdata, int len) { int i; int lastindex; int prevnull = -1; switch (len) { case 1: if (!isascii(pdata[0]) || !isprint(pdata[0])) return (0); return (1); case 2: case 3: case 4: lastindex = len; if (pdata[len-1] == '\0') lastindex = len - 1; for (i = 0; i < lastindex; i++) if (!isascii(pdata[i]) || !isprint(pdata[i])) return (0); return (1); default: if (len <= 0) return (0); for (i = 0; i < len; i++) { if (!isascii(pdata[i]) || !isprint(pdata[i])) { if (pdata[i] != '\0') return (0); /* * if the null char is in the first char * or two consecutive nulls' exist, * it's a bytearray prop */ if ((i == 0) || ((i - prevnull) == 1)) return (0); prevnull = i; } } break; } return (1); } /* * This function counts the number of strings in the value buffer pdata * and creates a property. * If there is only one string in the buffer, pdata, a charstring property * type is created and added. * If there are more than one string in the buffer, pdata, then a table * of charstrings is added. */ static int process_charstring_data(picl_nodehdl_t nodeh, char *pname, unsigned char *pdata, int retval) { int err; int strcount; char *strdat; ptree_propinfo_t propinfo; /* * append the null char at the end of string when there is * no null terminator */ if (pdata[retval - 1] != '\0') { strdat = malloc(retval + 1); if (strdat != NULL) { (void) memcpy(strdat, pdata, retval); strdat[retval] = '\0'; retval++; } } else { strdat = malloc(retval); if (strdat != NULL) (void) memcpy(strdat, pdata, retval); } if (strdat == NULL) return (PICL_FAILURE); /* * If it's a string list, create a table prop */ strcount = get_string_count(strdat, retval); if (strcount > 1) { err = add_string_list_prop(nodeh, pname, strdat, strcount); if (err != PICL_SUCCESS) { free(strdat); return (err); } } else { err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(strdat) + 1, pname, NULL, NULL); if (err != PICL_SUCCESS) { free(strdat); return (err); } (void) ptree_create_and_add_prop(nodeh, &propinfo, strdat, NULL); } free(strdat); return (PICL_SUCCESS); } /* * Add the OBP properties as properties of the PICL node */ static int add_openprom_props(picl_nodehdl_t nodeh, di_node_t di_node) { di_prom_prop_t promp; char *pname; unsigned char *pdata; int retval; ptree_propinfo_t propinfo; int err; picl_prop_type_t type; if (!ph) return (PICL_FAILURE); for (promp = di_prom_prop_next(ph, di_node, DI_PROM_PROP_NIL); promp != DI_PROM_PROP_NIL; promp = di_prom_prop_next(ph, di_node, promp)) { pname = di_prom_prop_name(promp); retval = di_prom_prop_data(promp, &pdata); if (retval < 0) { return (PICL_SUCCESS); } if (retval == 0) { err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_VOID, PICL_READ, (size_t)0, pname, NULL, NULL); if (err != PICL_SUCCESS) { return (err); } (void) ptree_create_and_add_prop(nodeh, &propinfo, NULL, NULL); continue; } /* * Get the prop type from pname map table */ if (lookup_pname_type_map(pname, &type) == 0) { if (type == PICL_PTYPE_CHARSTRING) { err = process_charstring_data(nodeh, pname, pdata, retval); if (err != PICL_SUCCESS) { return (err); } continue; } err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, type, PICL_READ, retval, pname, NULL, NULL); if (err != PICL_SUCCESS) { return (err); } (void) ptree_create_and_add_prop(nodeh, &propinfo, pdata, NULL); } else if (!is_string_propval(pdata, retval)) { switch (retval) { case sizeof (uint8_t): /*FALLTHROUGH*/ case sizeof (uint16_t): /*FALLTHROUGH*/ case sizeof (uint32_t): type = PICL_PTYPE_UNSIGNED_INT; break; default: type = PICL_PTYPE_BYTEARRAY; break; } err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, type, PICL_READ, retval, pname, NULL, NULL); if (err != PICL_SUCCESS) { return (err); } (void) ptree_create_and_add_prop(nodeh, &propinfo, pdata, NULL); } else { err = process_charstring_data(nodeh, pname, pdata, retval); if (err != PICL_SUCCESS) { return (err); } } } return (PICL_SUCCESS); } static void add_boolean_prop(picl_nodehdl_t nodeh, ptree_propinfo_t propinfo, char *di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_VOID, PICL_READ, (size_t)0, di_val, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, NULL, NULL); } static void add_uints_prop(picl_nodehdl_t nodeh, ptree_propinfo_t propinfo, char *di_val, int *idata, int len) { if (len == 1) (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (int), di_val, NULL, NULL); else (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_BYTEARRAY, PICL_READ, len * sizeof (int), di_val, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, idata, NULL); } static void add_strings_prop(picl_nodehdl_t nodeh, ptree_propinfo_t propinfo, char *di_val, char *sdata, int len) { if (len == 1) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(sdata) + 1, di_val, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, sdata, NULL); } else { (void) add_string_list_prop(nodeh, di_val, sdata, len); } } static void add_bytes_prop(picl_nodehdl_t nodeh, ptree_propinfo_t propinfo, char *di_val, unsigned char *bdata, int len) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_BYTEARRAY, PICL_READ, len, di_val, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, bdata, NULL); } static const char * path_state_name(di_path_state_t st) { switch (st) { case DI_PATH_STATE_ONLINE: return ("online"); case DI_PATH_STATE_STANDBY: return ("standby"); case DI_PATH_STATE_OFFLINE: return ("offline"); case DI_PATH_STATE_FAULT: return ("faulted"); } return ("unknown"); } /* * This function is the volatile property handler for the multipath node * "State" property. It must locate the associated devinfo node in order to * determine the current state. Since the devinfo node can have multiple * paths the devfs_path is used to locate the correct path. */ static int get_path_state_name(ptree_rarg_t *rarg, void *vbuf) { int err; picl_nodehdl_t parh; char devfs_path[PATH_MAX]; di_node_t di_node; di_node_t di_root; di_path_t pi = DI_PATH_NIL; picl_nodehdl_t mpnode; (void) strlcpy(vbuf, "unknown", MAX_STATE_SIZE); mpnode = rarg->nodeh; /* * The parent node represents the vHCI. */ err = ptree_get_propval_by_name(mpnode, PICL_PROP_PARENT, &parh, sizeof (picl_nodehdl_t)); if (err != PICL_SUCCESS) { return (PICL_SUCCESS); } /* * The PICL_PROP_DEVFS_PATH property will be used to locate the * devinfo node for the vHCI driver. */ err = ptree_get_propval_by_name(parh, PICL_PROP_DEVFS_PATH, devfs_path, sizeof (devfs_path)); if (err != PICL_SUCCESS) { return (PICL_SUCCESS); } /* * Find the di_node for the vHCI driver. It will be used to scan * the path information nodes. */ di_root = di_init("/", DINFOCACHE); if (di_root == DI_NODE_NIL) { return (PICL_SUCCESS); } di_node = di_lookup_node(di_root, devfs_path); if (di_node == DI_NODE_NIL) { di_fini(di_root); return (PICL_SUCCESS); } /* * The devfs_path will be used below to match the * proper path information node. */ err = ptree_get_propval_by_name(mpnode, PICL_PROP_DEVFS_PATH, devfs_path, sizeof (devfs_path)); if (err != PICL_SUCCESS) { di_fini(di_root); return (PICL_SUCCESS); } /* * Scan the path information nodes looking for the matching devfs * path. When found obtain the state information. */ while ((pi = di_path_next_phci(di_node, pi)) != DI_PATH_NIL) { char *di_path; di_node_t phci_node = di_path_phci_node(pi); if (phci_node == DI_PATH_NIL) continue; di_path = di_devfs_path(phci_node); if (di_path) { if (strcmp(di_path, devfs_path) != 0) { di_devfs_path_free(di_path); continue; } (void) strlcpy(vbuf, path_state_name(di_path_state(pi)), MAX_STATE_SIZE); di_devfs_path_free(di_path); break; } } di_fini(di_root); return (PICL_SUCCESS); } static void add_di_path_prop(picl_nodehdl_t nodeh, di_path_prop_t di_path_prop) { int di_ptype; char *di_val; ptree_propinfo_t propinfo; int *idata; char *sdata; unsigned char *bdata; int len; di_ptype = di_path_prop_type(di_path_prop); di_val = di_path_prop_name(di_path_prop); switch (di_ptype) { case DI_PROP_TYPE_BOOLEAN: add_boolean_prop(nodeh, propinfo, di_val); break; case DI_PROP_TYPE_INT: case DI_PROP_TYPE_INT64: len = di_path_prop_ints(di_path_prop, &idata); if (len < 0) /* Received error, so ignore prop */ break; add_uints_prop(nodeh, propinfo, di_val, idata, len); break; case DI_PROP_TYPE_STRING: len = di_path_prop_strings(di_path_prop, &sdata); if (len <= 0) break; add_strings_prop(nodeh, propinfo, di_val, sdata, len); break; case DI_PROP_TYPE_BYTE: len = di_path_prop_bytes(di_path_prop, &bdata); if (len < 0) break; add_bytes_prop(nodeh, propinfo, di_val, bdata, len); break; case DI_PROP_TYPE_UNKNOWN: /* * Unknown type, we'll try and guess what it should be. */ len = di_path_prop_strings(di_path_prop, &sdata); if ((len > 0) && (sdata[0] != 0)) { add_strings_prop(nodeh, propinfo, di_val, sdata, len); break; } len = di_path_prop_ints(di_path_prop, &idata); if (len > 0) { add_uints_prop(nodeh, propinfo, di_val, idata, len); break; } len = di_path_prop_bytes(di_path_prop, &bdata); if (len > 0) add_bytes_prop(nodeh, propinfo, di_val, bdata, len); else if (len == 0) add_boolean_prop(nodeh, propinfo, di_val); break; case DI_PROP_TYPE_UNDEF_IT: break; default: break; } } /* * Add nodes for path information (PSARC/1999/647, PSARC/2008/437) */ static void construct_mpath_node(picl_nodehdl_t parh, di_node_t di_node) { di_path_t pi = DI_PATH_NIL; while ((pi = di_path_next_phci(di_node, pi)) != DI_PATH_NIL) { di_node_t phci_node = di_path_phci_node(pi); di_path_prop_t di_path_prop; picl_nodehdl_t nodeh; ptree_propinfo_t propinfo; int err; int instance; char *di_val; if (phci_node == DI_PATH_NIL) continue; err = ptree_create_and_add_node(parh, PICL_CLASS_MULTIPATH, PICL_CLASS_MULTIPATH, &nodeh); if (err != PICL_SUCCESS) continue; instance = di_instance(phci_node); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_INT, PICL_READ, sizeof (instance), PICL_PROP_INSTANCE, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, &instance, NULL); di_val = di_devfs_path(phci_node); if (di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, PICL_PROP_DEVFS_PATH, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, NULL); di_devfs_path_free(di_val); } (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, (PICL_READ|PICL_VOLATILE), MAX_STATE_SIZE, PICL_PROP_STATE, get_path_state_name, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, NULL, NULL); for (di_path_prop = di_path_prop_next(pi, DI_PROP_NIL); di_path_prop != DI_PROP_NIL; di_path_prop = di_path_prop_next(pi, di_path_prop)) { add_di_path_prop(nodeh, di_path_prop); } } } /* * Add properties provided by libdevinfo */ static void add_devinfo_props(picl_nodehdl_t nodeh, di_node_t di_node) { int instance; char *di_val; di_prop_t di_prop; int di_ptype; ptree_propinfo_t propinfo; char *sdata; unsigned char *bdata; int *idata; int len; instance = di_instance(di_node); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_INT, PICL_READ, sizeof (instance), PICL_PROP_INSTANCE, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, &instance, NULL); di_val = di_bus_addr(di_node); if (di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, PICL_PROP_BUS_ADDR, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, NULL); } di_val = di_binding_name(di_node); if (di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, PICL_PROP_BINDING_NAME, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, NULL); } di_val = di_driver_name(di_node); if (di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, PICL_PROP_DRIVER_NAME, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, NULL); } di_val = di_devfs_path(di_node); if (di_val) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, PICL_PROP_DEVFS_PATH, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, NULL); di_devfs_path_free(di_val); } for (di_prop = di_prop_next(di_node, DI_PROP_NIL); di_prop != DI_PROP_NIL; di_prop = di_prop_next(di_node, di_prop)) { di_val = di_prop_name(di_prop); di_ptype = di_prop_type(di_prop); switch (di_ptype) { case DI_PROP_TYPE_BOOLEAN: add_boolean_prop(nodeh, propinfo, di_val); break; case DI_PROP_TYPE_INT: len = di_prop_ints(di_prop, &idata); if (len < 0) /* Received error, so ignore prop */ break; add_uints_prop(nodeh, propinfo, di_val, idata, len); break; case DI_PROP_TYPE_STRING: len = di_prop_strings(di_prop, &sdata); if (len < 0) break; add_strings_prop(nodeh, propinfo, di_val, sdata, len); break; case DI_PROP_TYPE_BYTE: len = di_prop_bytes(di_prop, &bdata); if (len < 0) break; add_bytes_prop(nodeh, propinfo, di_val, bdata, len); break; case DI_PROP_TYPE_UNKNOWN: /* * Unknown type, we'll try and guess what it should be. */ len = di_prop_strings(di_prop, &sdata); if ((len > 0) && (sdata[0] != 0)) { add_strings_prop(nodeh, propinfo, di_val, sdata, len); break; } len = di_prop_ints(di_prop, &idata); if (len > 0) { add_uints_prop(nodeh, propinfo, di_val, idata, len); break; } len = di_prop_rawdata(di_prop, &bdata); if (len > 0) add_bytes_prop(nodeh, propinfo, di_val, bdata, len); else if (len == 0) add_boolean_prop(nodeh, propinfo, di_val); break; case DI_PROP_TYPE_UNDEF_IT: break; default: break; } } } /* * This function creates the /obp node in the PICL tree for OBP nodes * without a device type class. */ static int construct_picl_openprom(picl_nodehdl_t rooth, picl_nodehdl_t *obph) { picl_nodehdl_t tmph; int err; err = ptree_create_and_add_node(rooth, PICL_NODE_OBP, PICL_CLASS_PICL, &tmph); if (err != PICL_SUCCESS) return (err); *obph = tmph; return (PICL_SUCCESS); } /* * This function creates the /platform node in the PICL tree and * its properties. It sets the "platform-name" property to the * platform name */ static int construct_picl_platform(picl_nodehdl_t rooth, di_node_t di_root, picl_nodehdl_t *piclh) { int err; picl_nodehdl_t plafh; char *nodename; char nodeclass[PICL_CLASSNAMELEN_MAX]; ptree_propinfo_t propinfo; picl_prophdl_t proph; nodename = di_node_name(di_root); if (nodename == NULL) return (PICL_FAILURE); err = 0; if (di_nodeid(di_root) == DI_PROM_NODEID || di_nodeid(di_root) == DI_SID_NODEID) err = get_device_type(nodeclass, di_root); if (err < 0) (void) strcpy(nodeclass, PICL_CLASS_UPA); /* default */ err = ptree_create_and_add_node(rooth, PICL_NODE_PLATFORM, nodeclass, &plafh); if (err != PICL_SUCCESS) return (err); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(nodename) + 1, PICL_PROP_PLATFORM_NAME, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, nodename, &proph); if (err != PICL_SUCCESS) return (err); (void) add_devinfo_props(plafh, di_root); (void) add_openprom_props(plafh, di_root); *piclh = plafh; return (PICL_SUCCESS); } /* * This function creates a node in /obp tree for the libdevinfo handle. */ static int construct_obp_node(picl_nodehdl_t parh, di_node_t dn, picl_nodehdl_t *chdh) { int err; char *nodename; char nodeclass[PICL_CLASSNAMELEN_MAX]; picl_nodehdl_t anodeh; nodename = di_node_name(dn); /* PICL_PROP_NAME */ if (nodename == NULL) return (PICL_FAILURE); if (strcmp(nodename, "pseudo") == 0) return (PICL_FAILURE); if ((di_nodeid(dn) == DI_PROM_NODEID) && (get_device_type(nodeclass, dn) == 0)) return (PICL_FAILURE); err = ptree_create_and_add_node(parh, nodename, nodename, &anodeh); if (err != PICL_SUCCESS) return (err); add_devinfo_props(anodeh, dn); (void) add_openprom_props(anodeh, dn); *chdh = anodeh; return (PICL_SUCCESS); } /* * This function creates a PICL node in /platform tree for a device */ static int construct_devtype_node(picl_nodehdl_t parh, char *nodename, char *nodeclass, di_node_t dn, picl_nodehdl_t *chdh) { int err; picl_nodehdl_t anodeh; err = ptree_create_and_add_node(parh, nodename, nodeclass, &anodeh); if (err != PICL_SUCCESS) return (err); (void) add_devinfo_props(anodeh, dn); (void) add_openprom_props(anodeh, dn); construct_mpath_node(anodeh, dn); *chdh = anodeh; return (err); } /* * Create a subtree of "picl" class nodes in /obp for these nodes */ static int construct_openprom_tree(picl_nodehdl_t nodeh, di_node_t dinode) { di_node_t cnode; picl_nodehdl_t chdh; int err; err = construct_obp_node(nodeh, dinode, &chdh); if (err != PICL_SUCCESS) return (err); for (cnode = di_child_node(dinode); cnode != DI_NODE_NIL; cnode = di_sibling_node(cnode)) (void) construct_openprom_tree(chdh, cnode); return (PICL_SUCCESS); } /* * Process the libdevinfo device tree and create nodes in /platform or /obp * PICL tree. * * This routine traverses the immediate children of "dinode" device and * determines the node class for that child. If it finds a valid class * name, then it builds a PICL node under /platform subtree and calls itself * recursively to construct the subtree for that child node. Otherwise, if * the parent_class is NULL, then it constructs a node and subtree under /obp * subtree. * * Note that we skip the children nodes that don't have a valid class name * and the parent_class is non NULL to prevent creation of any placeholder * nodes (such as sd,...). */ static int construct_devinfo_tree(picl_nodehdl_t plafh, picl_nodehdl_t obph, di_node_t dinode, char *parent_class) { di_node_t cnode; picl_nodehdl_t chdh; char nodeclass[PICL_CLASSNAMELEN_MAX]; char *nodename; int err; err = PICL_SUCCESS; for (cnode = di_child_node(dinode); cnode != DI_NODE_NIL; cnode = di_sibling_node(cnode)) { nodename = di_node_name(cnode); /* PICL_PROP_NAME */ if (nodename == NULL) continue; err = get_node_class(nodeclass, cnode, nodename); if (err == 0) { err = construct_devtype_node(plafh, nodename, nodeclass, cnode, &chdh); if (err != PICL_SUCCESS) return (err); err = construct_devinfo_tree(chdh, obph, cnode, nodeclass); } else if (parent_class == NULL) err = construct_openprom_tree(obph, cnode); else continue; /* * if parent_class is non NULL, skip the children nodes * that don't have a valid device class - eliminates * placeholder nodes (sd,...) from being created. */ } return (err); } /* * This function is called from the event handler called from the daemon * on PICL events. * * This routine traverses the children of the "dinode" device and * creates a PICL node for each child not found in the PICL tree and * invokes itself recursively to create a subtree for the newly created * child node. It also checks if the node being created is a meory * controller. If so, it posts PICLEVENT_MC_ADDED PICL event to the PICL * framework. */ static int update_subtree(picl_nodehdl_t nodeh, di_node_t dinode) { di_node_t cnode; picl_nodehdl_t chdh; picl_nodehdl_t nh; char *nodename; char nodeclass[PICL_CLASSNAMELEN_MAX]; char *path_buf; char buf[MAX_UNIT_ADDRESS_LEN]; char unitaddr[MAX_UNIT_ADDRESS_LEN]; char path_w_ua[MAXPATHLEN]; char path_wo_ua[MAXPATHLEN]; char *strp; int gotit; int err; for (cnode = di_child_node(dinode); cnode != DI_NODE_NIL; cnode = di_sibling_node(cnode)) { path_buf = di_devfs_path(cnode); if (path_buf == NULL) continue; nodename = di_node_name(cnode); if (nodename == NULL) { di_devfs_path_free(path_buf); continue; } err = get_node_class(nodeclass, cnode, nodename); if (err < 0) { di_devfs_path_free(path_buf); continue; } /* * this is quite complicated - both path_buf and any nodes * already in the picl tree may, or may not, have the * @ at the end of their names. So we must * take path_buf and work out what the device path would * be both with and without the unit_address, then search * the picl tree for both forms. */ if (((strp = strrchr(path_buf, '/')) != NULL) && strchr(strp, '@') == NULL) { /* * This is an unattached node - so the path is not * unique. Need to find out which node it is. * Find the unit_address from the OBP or devinfo * properties. */ err = ptree_create_node(nodename, nodeclass, &chdh); if (err != PICL_SUCCESS) return (err); (void) add_devinfo_props(chdh, cnode); (void) add_openprom_props(chdh, cnode); err = get_unitaddr(nodeh, chdh, unitaddr, sizeof (unitaddr)); if (err != PICL_SUCCESS) return (err); (void) ptree_destroy_node(chdh); (void) snprintf(path_w_ua, sizeof (path_w_ua), "%s@%s", path_buf, unitaddr); (void) snprintf(path_wo_ua, sizeof (path_wo_ua), "%s", path_buf); } else { /* * this is an attached node - so the path is unique */ (void) snprintf(path_w_ua, sizeof (path_w_ua), "%s", path_buf); (void) snprintf(path_wo_ua, sizeof (path_wo_ua), "%s", path_buf); strp = strrchr(path_wo_ua, '@'); *strp++ = '\0'; (void) snprintf(unitaddr, sizeof (unitaddr), "%s", strp); } /* * first look for node with unit address in devfs_path */ if (ptree_find_node(nodeh, PICL_PROP_DEVFS_PATH, PICL_PTYPE_CHARSTRING, path_w_ua, strlen(path_w_ua) + 1, &nh) == PICL_SUCCESS) { /* * node already there - there's nothing we need to do */ if (picldevtree_debug > 1) syslog(LOG_INFO, "update_subtree: path:%s node exists\n", path_buf); di_devfs_path_free(path_buf); continue; } /* * now look for node without unit address in devfs_path. * This might be just one out of several * nodes - need to check all siblings */ err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh, sizeof (chdh)); if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND)) return (err); gotit = 0; while (err == PICL_SUCCESS) { err = ptree_get_propval_by_name(chdh, PICL_PROP_DEVFS_PATH, buf, sizeof (buf)); if (err != PICL_SUCCESS) return (err); if (strcmp(buf, path_wo_ua) == 0) { err = ptree_get_propval_by_name(chdh, PICL_PROP_UNIT_ADDRESS, buf, sizeof (buf)); if (err != PICL_SUCCESS) return (err); if (strcmp(buf, unitaddr) == 0) { gotit = 1; break; } } err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh, sizeof (chdh)); if (err != PICL_SUCCESS) break; } if (gotit) { /* * node already there - there's nothing we need to do */ if (picldevtree_debug > 1) syslog(LOG_INFO, "update_subtree: path:%s node exists\n", path_buf); di_devfs_path_free(path_buf); continue; } #define IS_MC(x) (strcmp(x, PICL_CLASS_MEMORY_CONTROLLER) == 0 ? 1 : 0) if (construct_devtype_node(nodeh, nodename, nodeclass, cnode, &chdh) == PICL_SUCCESS) { if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: added node:%s path:%s\n", nodename, path_buf); if (IS_MC(nodeclass)) { if (post_mc_event(PICLEVENT_MC_ADDED, chdh) != PICL_SUCCESS) syslog(LOG_WARNING, PICL_EVENT_DROPPED, PICLEVENT_MC_ADDED); } di_devfs_path_free(path_buf); (void) update_subtree(chdh, cnode); } } return (PICL_SUCCESS); } /* * Check for a stale OBP node. EINVAL is returned from the openprom(7D) driver * if the nodeid stored in the snapshot is not valid. */ static int check_stale_node(di_node_t node, void *arg) { di_prom_prop_t promp; errno = 0; promp = di_prom_prop_next(ph, node, DI_PROM_PROP_NIL); if (promp == DI_PROM_PROP_NIL && errno == EINVAL) { snapshot_stale = 1; return (DI_WALK_TERMINATE); } return (DI_WALK_CONTINUE); } /* * Walk the snapshot and check the OBP properties of each node. */ static int is_snapshot_stale(di_node_t root) { snapshot_stale = 0; (void) di_walk_node(root, DI_WALK_CLDFIRST, NULL, check_stale_node); return (snapshot_stale); } /* * This function processes the data from libdevinfo and creates nodes * in the PICL tree. */ static int libdevinfo_init(picl_nodehdl_t rooth) { di_node_t di_root; picl_nodehdl_t plafh; picl_nodehdl_t obph; int err; /* * Use DINFOCACHE so that we obtain all attributes for all * device instances (without necessarily doing a load/attach * of all drivers). Once the (on-disk) cache file is built, it * exists over a reboot and can be read into memory at a very * low cost. */ if ((di_root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) return (PICL_FAILURE); if ((ph = di_prom_init()) == NULL) return (PICL_FAILURE); /* * Check if the snapshot cache contains stale OBP nodeid references. * If it does release the snapshot and obtain a live snapshot from the * kernel. */ if (is_snapshot_stale(di_root)) { syslog(LOG_INFO, "picld detected stale snapshot cache"); di_fini(di_root); if ((di_root = di_init("/", DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { return (PICL_FAILURE); } } /* * create platform PICL node using di_root node */ err = construct_picl_platform(rooth, di_root, &plafh); if (err != PICL_SUCCESS) { di_fini(di_root); return (PICL_FAILURE); } err = construct_picl_openprom(rooth, &obph); if (err != PICL_SUCCESS) { di_fini(di_root); return (PICL_FAILURE); } (void) construct_devinfo_tree(plafh, obph, di_root, NULL); if (ph) { di_prom_fini(ph); ph = NULL; } di_fini(di_root); return (err); } /* * This function returns the integer property value */ static int get_int_propval_by_name(picl_nodehdl_t nodeh, char *pname, int *ival) { int err; err = ptree_get_propval_by_name(nodeh, pname, ival, sizeof (int)); return (err); } /* * This function returns the port ID (or CPU ID in the case of CMP cores) * of the specific CPU node handle. If upa_portid exists, return its value. * Otherwise, return portid/cpuid. */ static int get_cpu_portid(picl_nodehdl_t modh, int *id) { int err; if (strcmp(mach_name, "sun4u") == 0 || strcmp(mach_name, "sun4v") == 0) { err = get_int_propval_by_name(modh, OBP_PROP_UPA_PORTID, id); if (err == PICL_SUCCESS) return (err); err = get_int_propval_by_name(modh, OBP_PROP_PORTID, id); if (err == PICL_SUCCESS) return (err); return (get_int_propval_by_name(modh, OBP_PROP_CPUID, id)); } if (strcmp(mach_name, "i86pc") == 0) return (get_int_propval_by_name(modh, OBP_REG, id)); return (PICL_FAILURE); } /* * This function is the volatile read access function of CPU state * property */ static int get_pi_state(ptree_rarg_t *rarg, void *vbuf) { int id; int err; err = get_int_propval_by_name(rarg->nodeh, PICL_PROP_ID, &id); if (err != PICL_SUCCESS) return (err); switch (p_online(id, P_STATUS)) { case P_ONLINE: (void) strlcpy(vbuf, PS_ONLINE, MAX_STATE_SIZE); break; case P_OFFLINE: (void) strlcpy(vbuf, PS_OFFLINE, MAX_STATE_SIZE); break; case P_NOINTR: (void) strlcpy(vbuf, PS_NOINTR, MAX_STATE_SIZE); break; case P_SPARE: (void) strlcpy(vbuf, PS_SPARE, MAX_STATE_SIZE); break; case P_FAULTED: (void) strlcpy(vbuf, PS_FAULTED, MAX_STATE_SIZE); break; case P_POWEROFF: (void) strlcpy(vbuf, PS_POWEROFF, MAX_STATE_SIZE); break; case P_DISABLED: (void) strlcpy(vbuf, PS_DISABLED, MAX_STATE_SIZE); break; default: (void) strlcpy(vbuf, "unknown", MAX_STATE_SIZE); break; } return (PICL_SUCCESS); } /* * This function is the volatile read access function of CPU processor_type * property */ static int get_processor_type(ptree_rarg_t *rarg, void *vbuf) { processor_info_t cpu_info; int id; int err; err = get_int_propval_by_name(rarg->nodeh, PICL_PROP_ID, &id); if (err != PICL_SUCCESS) return (err); if (processor_info(id, &cpu_info) >= 0) { (void) strlcpy(vbuf, cpu_info.pi_processor_type, PI_TYPELEN); } return (PICL_SUCCESS); } /* * This function is the volatile read access function of CPU fputypes * property */ static int get_fputypes(ptree_rarg_t *rarg, void *vbuf) { processor_info_t cpu_info; int id; int err; err = get_int_propval_by_name(rarg->nodeh, PICL_PROP_ID, &id); if (err != PICL_SUCCESS) return (err); if (processor_info(id, &cpu_info) >= 0) { (void) strlcpy(vbuf, cpu_info.pi_fputypes, PI_FPUTYPE); } return (PICL_SUCCESS); } /* * This function is the volatile read access function of CPU StateBegin * property. To minimize overhead, use kstat_chain_update() to refresh * the kstat header info as opposed to invoking kstat_open() every time. */ static int get_pi_state_begin(ptree_rarg_t *rarg, void *vbuf) { int err; int cpu_id; static kstat_ctl_t *kc = NULL; static pthread_mutex_t kc_mutex = PTHREAD_MUTEX_INITIALIZER; kstat_t *kp; kstat_named_t *kn; err = get_int_propval_by_name(rarg->nodeh, PICL_PROP_ID, &cpu_id); if (err != PICL_SUCCESS) return (err); (void) pthread_mutex_lock(&kc_mutex); if (kc == NULL) kc = kstat_open(); else if (kstat_chain_update(kc) == -1) { (void) kstat_close(kc); kc = kstat_open(); } if (kc == NULL) { (void) pthread_mutex_unlock(&kc_mutex); return (PICL_FAILURE); } /* Get the state_begin from kstat */ if ((kp = kstat_lookup(kc, KSTAT_CPU_INFO, cpu_id, NULL)) == NULL || kp->ks_type != KSTAT_TYPE_NAMED || kstat_read(kc, kp, 0) < 0) { (void) pthread_mutex_unlock(&kc_mutex); return (PICL_FAILURE); } kn = kstat_data_lookup(kp, KSTAT_STATE_BEGIN); if (kn) { *(uint64_t *)vbuf = (uint64_t)kn->value.l; err = PICL_SUCCESS; } else err = PICL_FAILURE; (void) pthread_mutex_unlock(&kc_mutex); return (err); } /* * This function adds CPU information to the CPU nodes */ /* ARGSUSED */ static int add_processor_info(picl_nodehdl_t cpuh, void *args) { int err; int cpu_id; ptree_propinfo_t propinfo; ptree_propinfo_t pinfo; err = get_cpu_portid(cpuh, &cpu_id); if (err != PICL_SUCCESS) return (PICL_WALK_CONTINUE); /* * Check to make sure that the CPU is still present, i.e. that it * has not been DR'ed out of the system. */ if (p_online(cpu_id, P_STATUS) == -1) { if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: cpu %d (%llx) does not exist - " "deleting node\n", cpu_id, cpuh); if (ptree_delete_node(cpuh) == PICL_SUCCESS) (void) ptree_destroy_node(cpuh); return (PICL_WALK_CONTINUE); } (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_INT, PICL_READ, sizeof (int), PICL_PROP_ID, NULL, NULL); err = ptree_create_and_add_prop(cpuh, &propinfo, &cpu_id, NULL); if (err != PICL_SUCCESS) return (PICL_WALK_CONTINUE); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, (PICL_READ|PICL_VOLATILE), MAX_STATE_SIZE, PICL_PROP_STATE, get_pi_state, NULL); (void) ptree_create_and_add_prop(cpuh, &pinfo, NULL, NULL); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, (PICL_READ|PICL_VOLATILE), PI_TYPELEN, PICL_PROP_PROCESSOR_TYPE, get_processor_type, NULL); (void) ptree_create_and_add_prop(cpuh, &pinfo, NULL, NULL); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, (PICL_READ|PICL_VOLATILE), PI_FPUTYPE, PICL_PROP_FPUTYPE, get_fputypes, NULL); (void) ptree_create_and_add_prop(cpuh, &pinfo, NULL, NULL); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_TIMESTAMP, PICL_READ|PICL_VOLATILE, sizeof (uint64_t), PICL_PROP_STATE_BEGIN, get_pi_state_begin, NULL); (void) ptree_create_and_add_prop(cpuh, &pinfo, NULL, NULL); return (PICL_WALK_CONTINUE); } /* * This function sets up the "ID" property in every CPU nodes * and adds processor info */ static int setup_cpus(picl_nodehdl_t plafh) { int err; err = ptree_walk_tree_by_class(plafh, PICL_CLASS_CPU, NULL, add_processor_info); return (err); } /* * This function format's the manufacture's information for FFB display * devices */ static void fmt_manf_id(manuf_t manufid, int bufsz, char *outbuf) { /* * Format the manufacturer's info. Note a small inconsistency we * have to work around - Brooktree has it's part number in decimal, * while Mitsubishi has it's part number in hex. */ switch (manufid.fld.manf) { case MANF_BROOKTREE: (void) snprintf(outbuf, bufsz, "%s %d, version %d", "Brooktree", manufid.fld.partno, manufid.fld.version); break; case MANF_MITSUBISHI: (void) snprintf(outbuf, bufsz, "%s %x, version %d", "Mitsubishi", manufid.fld.partno, manufid.fld.version); break; default: (void) snprintf(outbuf, bufsz, "JED code %d, Part num 0x%x, version %d", manufid.fld.manf, manufid.fld.partno, manufid.fld.version); } } /* * If it's an ffb device, open ffb devices and return PICL_SUCCESS */ static int open_ffb_device(picl_nodehdl_t ffbh, int *fd) { DIR *dirp; char devfs_path[PATH_MAX]; char dev_path[PATH_MAX]; char *devp; struct dirent *direntp; int err; int tmpfd; /* Get the devfs_path of the ffb devices */ err = ptree_get_propval_by_name(ffbh, PICL_PROP_DEVFS_PATH, devfs_path, sizeof (devfs_path)); if (err != PICL_SUCCESS) return (err); /* Get the device node name */ devp = strrchr(devfs_path, '/'); if (devp == NULL) return (PICL_FAILURE); *devp = '\0'; ++devp; /* * Check if device node name has the ffb string * If not, assume it's not a ffb device. */ if (strstr(devp, FFB_NAME) == NULL) return (PICL_FAILURE); /* * Get the parent path of the ffb device node. */ (void) snprintf(dev_path, sizeof (dev_path), "%s/%s", "/devices", devfs_path); /* * Since we don't know ffb's minor nodename, * we need to search all the devices under its * parent dir by comparing the node name */ if ((dirp = opendir(dev_path)) == NULL) return (PICL_FAILURE); while ((direntp = readdir(dirp)) != NULL) { if (strstr(direntp->d_name, devp) != NULL) { (void) strcat(dev_path, "/"); (void) strcat(dev_path, direntp->d_name); tmpfd = open(dev_path, O_RDWR); if (tmpfd < 0) continue; *fd = tmpfd; (void) closedir(dirp); return (PICL_SUCCESS); } } (void) closedir(dirp); return (PICL_FAILURE); } /* * This function recursively searches the tree for ffb display devices * and add ffb config information */ static int add_ffb_config_info(picl_nodehdl_t rooth) { picl_nodehdl_t nodeh; int err; char piclclass[PICL_CLASSNAMELEN_MAX]; char manfidbuf[FFB_MANUF_BUFSIZE]; int fd; int board_rev; ffb_sys_info_t fsi; ptree_propinfo_t pinfo; for (err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &nodeh, sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND; err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, sizeof (picl_nodehdl_t))) { if (err != PICL_SUCCESS) return (err); err = ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, piclclass, PICL_CLASSNAMELEN_MAX); if ((err == PICL_SUCCESS) && (strcmp(piclclass, PICL_CLASS_DISPLAY) == 0)) { err = open_ffb_device(nodeh, &fd); if ((err == PICL_SUCCESS) && (ioctl(fd, FFB_SYS_INFO, &fsi) >= 0)) { (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (int), PICL_PROP_FFB_BOARD_REV, NULL, NULL); board_rev = fsi.ffb_strap_bits.fld.board_rev; (void) ptree_create_and_add_prop(nodeh, &pinfo, &board_rev, NULL); fmt_manf_id(fsi.dac_version, sizeof (manfidbuf), manfidbuf); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(manfidbuf) + 1, PICL_PROP_FFB_DAC_VER, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &pinfo, manfidbuf, NULL); fmt_manf_id(fsi.fbram_version, sizeof (manfidbuf), manfidbuf); (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(manfidbuf) + 1, PICL_PROP_FFB_FBRAM_VER, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &pinfo, manfidbuf, NULL); (void) close(fd); } } else if (add_ffb_config_info(nodeh) != PICL_SUCCESS) return (PICL_FAILURE); } return (PICL_SUCCESS); } static conf_entries_t * free_conf_entries(conf_entries_t *list) { conf_entries_t *el; conf_entries_t *del; if (list == NULL) return (NULL); el = list; while (el != NULL) { del = el; el = el->next; free(del->name); free(del->piclclass); free(del); } return (el); } /* * Reading config order: platform, common */ static conf_entries_t * read_conf_file(char *fname, conf_entries_t *list) { FILE *fp; char lbuf[CONFFILE_LINELEN_MAX]; char *nametok; char *classtok; conf_entries_t *el; conf_entries_t *ptr; if (fname == NULL) return (list); fp = fopen(fname, "r"); if (fp == NULL) return (list); while (fgets(lbuf, CONFFILE_LINELEN_MAX, fp) != NULL) { if ((lbuf[0] == CONFFILE_COMMENT_CHAR) || (lbuf[0] == '\n')) continue; nametok = strtok(lbuf, " \t\n"); if (nametok == NULL) continue; classtok = strtok(NULL, " \t\n"); if (classtok == NULL) continue; el = malloc(sizeof (conf_entries_t)); if (el == NULL) break; el->name = strdup(nametok); el->piclclass = strdup(classtok); if ((el->name == NULL) || (el->piclclass == NULL)) { free(el); return (list); } el->next = NULL; /* * Add it to the end of list */ if (list == NULL) list = el; else { ptr = list; while (ptr->next != NULL) ptr = ptr->next; ptr->next = el; } } (void) fclose(fp); return (list); } /* * Process the devtree conf file and set up the conf_name_class_map list */ static void process_devtree_conf_file(void) { char nmbuf[SYS_NMLN]; char pname[PATH_MAX]; conf_name_class_map = NULL; if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) { (void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf); (void) strlcat(pname, DEVTREE_CONFFILE_NAME, PATH_MAX); conf_name_class_map = read_conf_file(pname, conf_name_class_map); } if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) { (void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf); (void) strlcat(pname, DEVTREE_CONFFILE_NAME, PATH_MAX); conf_name_class_map = read_conf_file(pname, conf_name_class_map); } (void) snprintf(pname, PATH_MAX, "%s/%s", PICLD_COMMON_PLUGIN_DIR, DEVTREE_CONFFILE_NAME); conf_name_class_map = read_conf_file(pname, conf_name_class_map); } static asr_conf_entries_t *conf_name_asr_map = NULL; static void free_asr_conf_entries(asr_conf_entries_t *list) { asr_conf_entries_t *el; asr_conf_entries_t *del; el = list; while (el != NULL) { del = el; el = el->next; if (del->name) free(del->name); if (del->address) free(del->address); if (del->status) free(del->status); if (del->piclclass) free(del->piclclass); if (del->props) free(del->props); free(del); } } /* * Reading config order: platform, common */ static asr_conf_entries_t * read_asr_conf_file(char *fname, asr_conf_entries_t *list) { FILE *fp; char lbuf[CONFFILE_LINELEN_MAX]; char *nametok; char *classtok; char *statustok; char *addresstok; char *propstok; asr_conf_entries_t *el; asr_conf_entries_t *ptr; if (fname == NULL) return (list); fp = fopen(fname, "r"); if (fp == NULL) return (list); while (fgets(lbuf, CONFFILE_LINELEN_MAX, fp) != NULL) { if ((lbuf[0] == CONFFILE_COMMENT_CHAR) || (lbuf[0] == '\n')) continue; nametok = strtok(lbuf, " \t\n"); if (nametok == NULL) continue; classtok = strtok(NULL, " \t\n"); if (classtok == NULL) continue; statustok = strtok(NULL, " \t\n"); if (statustok == NULL) continue; addresstok = strtok(NULL, " \t\n"); if (addresstok == NULL) continue; /* * props are optional */ propstok = strtok(NULL, " \t\n"); el = malloc(sizeof (asr_conf_entries_t)); if (el == NULL) break; el->name = strdup(nametok); el->piclclass = strdup(classtok); el->status = strdup(statustok); el->address = strdup(addresstok); if (propstok != NULL) el->props = strdup(propstok); else el->props = NULL; if ((el->name == NULL) || (el->piclclass == NULL) || (el->address == NULL) || (el->status == NULL)) { if (el->name) free(el->name); if (el->address) free(el->address); if (el->status) free(el->status); if (el->piclclass) free(el->piclclass); if (el->props) free(el->props); free(el); break; } el->next = NULL; /* * Add it to the end of list */ if (list == NULL) list = el; else { ptr = list; while (ptr->next != NULL) ptr = ptr->next; ptr->next = el; } } (void) fclose(fp); return (list); } /* * Process the asr conf file */ static void process_asrtree_conf_file(void) { char nmbuf[SYS_NMLN]; char pname[PATH_MAX]; if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) { (void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf); (void) strlcat(pname, ASRTREE_CONFFILE_NAME, PATH_MAX); conf_name_asr_map = read_asr_conf_file(pname, conf_name_asr_map); } if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) { (void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf); (void) strlcat(pname, ASRTREE_CONFFILE_NAME, PATH_MAX); conf_name_asr_map = read_asr_conf_file(pname, conf_name_asr_map); } (void) snprintf(pname, PATH_MAX, "%s/%s", PICLD_COMMON_PLUGIN_DIR, ASRTREE_CONFFILE_NAME); conf_name_asr_map = read_asr_conf_file(pname, conf_name_asr_map); } /* * This function reads the export file list from ASR */ static int get_asr_export_list(char **exportlist, int *exportlistlen) { struct openpromio oppbuf; struct openpromio *opp = &oppbuf; int d; int listsize; d = open("/dev/openprom", O_RDWR); if (d < 0) return (0); if (ioctl(d, OPROMEXPORTLEN, opp) == -1) { (void) close(d); return (0); } listsize = opp->oprom_size; opp = (struct openpromio *)malloc(sizeof (struct openpromio) + listsize); if (opp == NULL) { (void) close(d); return (0); } (void) memset(opp, '\0', sizeof (struct openpromio) + listsize); opp->oprom_size = listsize; if (ioctl(d, OPROMEXPORT, opp) == -1) { free(opp); (void) close(d); return (0); } *exportlist = malloc(listsize); if (*exportlist == NULL) { free(opp); (void) close(d); return (0); } (void) memcpy(*exportlist, opp->oprom_array, opp->oprom_size); *exportlistlen = opp->oprom_size; free(opp); (void) close(d); return (1); } /* * Parses properties string, fills in triplet structure with first * type, name, val triplet and returns pointer to next property. * Returns NULL if no valid triplet found * CAUTION: drops \0 characters over separator characters: if you * want to parse the string twice, you'll have to take a copy. */ static char * parse_props_string(char *props, asr_prop_triplet_t *triplet) { char *prop_name; char *prop_val; char *prop_next; prop_name = strchr(props, '?'); if (prop_name == NULL) return (NULL); *prop_name++ = '\0'; prop_val = strchr(prop_name, '='); if (prop_val == NULL) return (NULL); *prop_val++ = '\0'; triplet->proptype = props; triplet->propname = prop_name; triplet->propval = prop_val; prop_next = strchr(prop_val, ':'); if (prop_next == NULL) return (prop_val - 1); *prop_next++ = '\0'; return (prop_next); } static int add_status_prop(picl_nodehdl_t chdh, char *status) { ptree_propinfo_t propinfo; picl_prophdl_t proph; int err; err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(status) + 1, PICL_PROP_STATUS, NULL, NULL); if (err != PICL_SUCCESS) return (err); err = ptree_create_and_add_prop(chdh, &propinfo, status, &proph); return (err); } static void create_asr_node(char *parent, char *child, char *unitaddr, char *class, char *status, char *props) { char ptreepath[PATH_MAX]; char nodename[PICL_PROPNAMELEN_MAX]; char ua[MAX_UNIT_ADDRESS_LEN]; char *props_copy = NULL; char *next; char *prop_string; boolean_t found = B_FALSE; picl_nodehdl_t nodeh; picl_nodehdl_t chdh; asr_prop_triplet_t triple; ptree_propinfo_t propinfo; picl_prophdl_t proph; int val; int err; (void) strlcpy(ptreepath, PLATFORM_PATH, PATH_MAX); (void) strlcat(ptreepath, parent, PATH_MAX); if (ptree_get_node_by_path(ptreepath, &nodeh) != PICL_SUCCESS) return; /* * see if the required child node already exists */ for (err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh, sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND; err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh, sizeof (picl_nodehdl_t))) { if (err != PICL_SUCCESS) break; err = ptree_get_propval_by_name(chdh, PICL_PROP_NAME, (void *)nodename, PICL_PROPNAMELEN_MAX); if (err != PICL_SUCCESS) break; if (strcmp(nodename, child) != 0) continue; /* * found a candidate child node */ if (unitaddr) { /* * does it match the required unit address? */ err = ptree_get_propval_by_name(chdh, PICL_PROP_UNIT_ADDRESS, ua, sizeof (ua)); if (err == PICL_PROPNOTFOUND) continue; if (err != PICL_SUCCESS) break; if (strcmp(unitaddr, ua) != 0) continue; } if (props == NULL) { next = ""; } else if (props_copy == NULL) { props_copy = strdup(props); if (props_copy == NULL) return; next = props_copy; } while ((next = parse_props_string(next, &triple)) != NULL) { err = ptree_get_prop_by_name(chdh, triple.propname, &proph); if (err != PICL_SUCCESS) break; err = ptree_get_propinfo(proph, &propinfo); if (err != PICL_SUCCESS) break; err = PICL_FAILURE; switch (propinfo.piclinfo.type) { case PICL_PTYPE_INT: case PICL_PTYPE_UNSIGNED_INT: if (strcmp(triple.proptype, "I") != 0) break; err = ptree_get_propval(proph, (void *)&val, sizeof (val)); if (err != PICL_SUCCESS) break; if (val != atoi(triple.propval)) err = PICL_FAILURE; break; case PICL_PTYPE_CHARSTRING: if (strcmp(triple.proptype, "S") != 0) break; prop_string = malloc(propinfo.piclinfo.size); if (prop_string == NULL) break; err = ptree_get_propval(proph, (void *)prop_string, propinfo.piclinfo.size); if (err != PICL_SUCCESS) { free(prop_string); break; } if (strcmp(prop_string, triple.propval) != 0) err = PICL_FAILURE; free(prop_string); break; default: break; } if (err != PICL_SUCCESS) { break; } } if (next == NULL) { found = B_TRUE; break; } } if (props_copy) free(props_copy); if (found) { /* * does the pre-existing node have a status property? */ err = ptree_get_propval_by_name(chdh, PICL_PROP_STATUS, ua, sizeof (ua)); if (err == PICL_PROPNOTFOUND) (void) add_status_prop(chdh, status); if (err != PICL_SUCCESS) return; if ((strcmp(ua, ASR_DISABLED) == 0) || (strcmp(ua, ASR_FAILED) == 0) || ((strcmp(status, ASR_DISABLED) != 0) && (strcmp(status, ASR_FAILED) != 0))) { return; } /* * more urgent status now, so replace existing value */ err = ptree_get_prop_by_name(chdh, PICL_PROP_STATUS, &proph); if (err != PICL_SUCCESS) return; (void) ptree_delete_prop(proph); (void) ptree_destroy_prop(proph); err = add_status_prop(chdh, status); if (err != PICL_SUCCESS) return; return; } /* * typical case, node needs adding together with a set of properties */ if (ptree_create_and_add_node(nodeh, child, class, &chdh) == PICL_SUCCESS) { (void) add_status_prop(chdh, status); if (unitaddr) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(unitaddr) + 1, PICL_PROP_UNIT_ADDRESS, NULL, NULL); (void) ptree_create_and_add_prop(chdh, &propinfo, unitaddr, &proph); (void) strlcpy(ptreepath, parent, PATH_MAX); (void) strlcat(ptreepath, "/", PATH_MAX); (void) strlcat(ptreepath, child, PATH_MAX); (void) strlcat(ptreepath, "@", PATH_MAX); (void) strlcat(ptreepath, unitaddr, PATH_MAX); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(ptreepath) + 1, PICL_PROP_DEVFS_PATH, NULL, NULL); (void) ptree_create_and_add_prop(chdh, &propinfo, ptreepath, &proph); } next = props; while ((next = parse_props_string(next, &triple)) != NULL) { /* * only handle int and string properties for * simplicity */ if (strcmp(triple.proptype, "I") == 0) { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_INT, PICL_READ, sizeof (int), triple.propname, NULL, NULL); val = atoi(triple.propval); (void) ptree_create_and_add_prop(chdh, &propinfo, &val, &proph); } else { (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(triple.propval) + 1, triple.propname, NULL, NULL); (void) ptree_create_and_add_prop(chdh, &propinfo, triple.propval, &proph); } } } } static void add_asr_nodes() { char *asrexport; int asrexportlen; asr_conf_entries_t *c = NULL; int i; char *key; char *child; char *unitaddr; uint16_t count; int disabled; if (get_asr_export_list(&asrexport, &asrexportlen) == 0) return; process_asrtree_conf_file(); if (conf_name_asr_map == NULL) return; i = 0; while (i < asrexportlen) { key = &asrexport[i]; i += strlen(key) + 1; if (i >= asrexportlen) break; /* * next byte tells us whether failed by diags or manually * disabled */ disabled = asrexport[i]; i++; if (i >= asrexportlen) break; /* * only type 1 supported */ if (asrexport[i] != 1) break; i++; if (i >= asrexportlen) break; /* * next two bytes give size of reason string */ count = (asrexport[i] << 8) | asrexport[i + 1]; i += count + 2; if (i > asrexportlen) break; /* * now look for key in conf file info */ c = conf_name_asr_map; while (c != NULL) { if (strcmp(key, c->name) == 0) { child = strrchr(c->address, '/'); *child++ = '\0'; unitaddr = strchr(child, '@'); if (unitaddr) *unitaddr++ = '\0'; if (strcmp(c->status, ASR_DISABLED) == 0) { create_asr_node(c->address, child, unitaddr, c->piclclass, disabled ? ASR_DISABLED : ASR_FAILED, c->props); } else { create_asr_node(c->address, child, unitaddr, c->piclclass, c->status, c->props); } } c = c->next; } } free_asr_conf_entries(conf_name_asr_map); free(asrexport); } /* * This function adds information to the /platform node */ static int add_platform_info(picl_nodehdl_t plafh) { struct utsname uts_info; int err; ptree_propinfo_t propinfo; picl_prophdl_t proph; if (uname(&uts_info) < 0) return (PICL_FAILURE); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(uts_info.sysname) + 1, PICL_PROP_SYSNAME, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, uts_info.sysname, &proph); if (err != PICL_SUCCESS) return (err); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(uts_info.nodename) + 1, PICL_PROP_NODENAME, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, uts_info.nodename, &proph); if (err != PICL_SUCCESS) return (err); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(uts_info.release) + 1, PICL_PROP_RELEASE, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, uts_info.release, &proph); if (err != PICL_SUCCESS) return (err); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(uts_info.version) + 1, PICL_PROP_VERSION, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, uts_info.version, &proph); if (err != PICL_SUCCESS) return (err); (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(uts_info.machine) + 1, PICL_PROP_MACHINE, NULL, NULL); err = ptree_create_and_add_prop(plafh, &propinfo, uts_info.machine, &proph); return (err); } /* * Get first 32-bit value from the reg property */ static int get_first_reg_word(picl_nodehdl_t nodeh, uint32_t *regval) { int err; uint32_t *regbuf; picl_prophdl_t regh; ptree_propinfo_t pinfo; err = ptree_get_prop_by_name(nodeh, OBP_REG, ®h); if (err != PICL_SUCCESS) /* no reg property */ return (err); err = ptree_get_propinfo(regh, &pinfo); if (err != PICL_SUCCESS) return (err); if (pinfo.piclinfo.size < sizeof (uint32_t)) /* too small */ return (PICL_FAILURE); regbuf = malloc(pinfo.piclinfo.size); if (regbuf == NULL) return (PICL_FAILURE); err = ptree_get_propval(regh, regbuf, pinfo.piclinfo.size); if (err == PICL_SUCCESS) *regval = *regbuf; /* get first 32-bit value */ free(regbuf); return (err); } /* * Get device ID from the reg property */ static int get_device_id(picl_nodehdl_t nodeh, uint32_t *dev_id) { int err; uint32_t regval; err = get_first_reg_word(nodeh, ®val); if (err != PICL_SUCCESS) return (err); *dev_id = PCI_DEVICE_ID(regval); return (PICL_SUCCESS); } /* * add Slot property for children of SBUS node */ /* ARGSUSED */ static int add_sbus_slots(picl_nodehdl_t pcih, void *args) { picl_nodehdl_t nodeh; uint32_t slot; int err; ptree_propinfo_t pinfo; for (err = ptree_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND; err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, sizeof (picl_nodehdl_t))) { if (err != PICL_SUCCESS) return (err); if (get_first_reg_word(nodeh, &slot) != 0) continue; (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (uint32_t), PICL_PROP_SLOT, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &pinfo, &slot, NULL); } return (PICL_WALK_CONTINUE); } /* * This function creates a Slot property for SBUS child nodes * which can be correlated with the slot they are plugged into * on the motherboard. */ static int set_sbus_slot(picl_nodehdl_t plafh) { int err; err = ptree_walk_tree_by_class(plafh, PICL_CLASS_SBUS, NULL, add_sbus_slots); return (err); } /* * add DeviceID property for children of PCI/PCIEX node */ /* ARGSUSED */ static int add_pci_deviceids(picl_nodehdl_t pcih, void *args) { picl_nodehdl_t nodeh; uint32_t dev_id; int err; ptree_propinfo_t pinfo; for (err = ptree_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND; err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, sizeof (picl_nodehdl_t))) { if (err != PICL_SUCCESS) return (err); if (get_device_id(nodeh, &dev_id) != 0) continue; (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (uint32_t), PICL_PROP_DEVICE_ID, NULL, NULL); (void) ptree_create_and_add_prop(nodeh, &pinfo, &dev_id, NULL); } return (PICL_WALK_CONTINUE); } /* * This function creates a DeviceID property for PCI/PCIEX child nodes * which can be correlated with the slot they are plugged into * on the motherboard. */ static void set_pci_pciex_deviceid(picl_nodehdl_t plafh) { (void) ptree_walk_tree_by_class(plafh, PICL_CLASS_PCI, NULL, add_pci_deviceids); (void) ptree_walk_tree_by_class(plafh, PICL_CLASS_PCIEX, NULL, add_pci_deviceids); } /* * Default UnitAddress encode function */ static int encode_default_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { int i, len; /* * Encode UnitAddress as %a,%b,%c,...,%n */ if (addrcells < 1) return (-1); len = snprintf(buf, sz, "%x", *regprop); for (i = 1; i < addrcells && len < sz; i++) len += snprintf(&buf[len], sz-len, ",%x", regprop[i]); return ((len >= sz) ? -1 : 0); } /* * UnitAddress encode function where the last component is not printed * unless non-zero. */ static int encode_optional_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { int retval; /* * Encode UnitAddress as %a,%b,%c,...,%n where the last component * is printed only if non-zero. */ if (addrcells > 1 && regprop[addrcells-1] == 0) retval = encode_default_unitaddr(buf, sz, regprop, addrcells-1); else retval = encode_default_unitaddr(buf, sz, regprop, addrcells); return (retval); } /* * UnitAddress encode function for SCSI class of devices */ static int encode_scsi_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { int len, retval; /* * #address-cells Format * 2 second component printed only if non-zero * * 4 regprop: phys_hi phys_lo lun_hi lun_lo * UnitAddr: w, */ if (addrcells == 2) { retval = encode_optional_unitaddr(buf, sz, regprop, addrcells); } else if (addrcells == 4) { len = snprintf(buf, sz, "w%08x%08x,%x", regprop[0], regprop[1], regprop[3]); retval = (len >= sz) ? -1 : 0; } else retval = -1; return (retval); } /* * UnitAddress encode function for UPA devices */ static int encode_upa_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { int len; if (addrcells != 2) return (-1); len = snprintf(buf, sz, "%x,%x", (regprop[0]/2)&0x1f, regprop[1]); return ((len >= sz) ? -1 : 0); } /* * UnitAddress encode function for GPTWO, JBUS devices */ static int encode_gptwo_jbus_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { uint32_t hi, lo; int len, id, off; if (addrcells != 2) return (-1); hi = regprop[0]; lo = regprop[1]; if (hi & 0x400) { id = ((hi & 0x1) << 9) | (lo >> 23); /* agent id */ off = lo & 0x7fffff; /* config offset */ len = snprintf(buf, sz, "%x,%x", id, off); } else { len = snprintf(buf, sz, "m%x,%x", hi, lo); } return ((len >= sz) ? -1 : 0); } /* * UnitAddress encode function for PCI devices */ static int encode_pci_unitaddr(char *buf, int sz, uint32_t *regprop, uint_t addrcells) { typedef struct { uint32_t n:1, /* relocatable */ p:1, /* prefetchable */ t:1, /* address region aliases */ zero:3, /* must be zero */ ss:2, /* address space type */ bus:8, /* bus number */ dev:5, /* device number */ fn:3, /* function number */ reg:8; /* register number */ uint32_t phys_hi; /* high physical address */ uint32_t phys_lo; /* low physical address */ } pci_addrcell_t; pci_addrcell_t *p; int len; if (addrcells != 3) return (-1); p = (pci_addrcell_t *)regprop; switch (p->ss) { case 0: /* Config */ if (p->fn) len = snprintf(buf, sz, "%x,%x", p->dev, p->fn); else len = snprintf(buf, sz, "%x", p->dev); break; case 1: /* IO */ len = snprintf(buf, sz, "i%x,%x,%x,%x", p->dev, p->fn, p->reg, p->phys_lo); break; case 2: /* Mem32 */ len = snprintf(buf, sz, "m%x,%x,%x,%x", p->dev, p->fn, p->reg, p->phys_lo); break; case 3: /* Mem64 */ len = snprintf(buf, sz, "x%x,%x,%x,%x%08x", p->dev, p->fn, p->reg, p->phys_hi, p->phys_lo); break; } return ((len >= sz) ? -1 : 0); } /* * Get #address-cells property value */ static uint_t get_addrcells_prop(picl_nodehdl_t nodeh) { int len, err; uint32_t addrcells; ptree_propinfo_t pinfo; picl_prophdl_t proph; /* * Get #address-cells property. If not present, use default value. */ err = ptree_get_prop_by_name(nodeh, OBP_PROP_ADDRESS_CELLS, &proph); if (err == PICL_SUCCESS) err = ptree_get_propinfo(proph, &pinfo); len = pinfo.piclinfo.size; if (err == PICL_SUCCESS && len >= sizeof (uint8_t) && len <= sizeof (addrcells)) { err = ptree_get_propval(proph, &addrcells, len); if (err == PICL_SUCCESS) { if (len == sizeof (uint8_t)) addrcells = *(uint8_t *)&addrcells; else if (len == sizeof (uint16_t)) addrcells = *(uint16_t *)&addrcells; } else addrcells = DEFAULT_ADDRESS_CELLS; } else addrcells = DEFAULT_ADDRESS_CELLS; return (addrcells); } /* * Get UnitAddress mapping entry for a node */ static unitaddr_map_t * get_unitaddr_mapping(picl_nodehdl_t nodeh) { int err; unitaddr_map_t *uamap; char clname[PICL_CLASSNAMELEN_MAX]; /* * Get my classname and locate a function to translate "reg" prop * into "UnitAddress" prop for my children. */ err = ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, clname, sizeof (clname)); if (err != PICL_SUCCESS) (void) strcpy(clname, ""); /* NULL class name */ for (uamap = &unitaddr_map_table[0]; uamap->class != NULL; uamap++) if (strcmp(clname, uamap->class) == 0) break; return (uamap); } /* * Add UnitAddress property to the specified node */ static int add_unitaddr_prop(picl_nodehdl_t nodeh, unitaddr_map_t *uamap, uint_t addrcells) { int regproplen, err; uint32_t *regbuf; picl_prophdl_t regh; ptree_propinfo_t pinfo; char unitaddr[MAX_UNIT_ADDRESS_LEN]; err = ptree_get_prop_by_name(nodeh, OBP_REG, ®h); if (err != PICL_SUCCESS) return (err); err = ptree_get_propinfo(regh, &pinfo); if (err != PICL_SUCCESS) return (PICL_FAILURE); if (pinfo.piclinfo.size < (addrcells * sizeof (uint32_t))) return (PICL_FAILURE); regproplen = pinfo.piclinfo.size; regbuf = malloc(regproplen); if (regbuf == NULL) return (PICL_FAILURE); err = ptree_get_propval(regh, regbuf, regproplen); if (err != PICL_SUCCESS || uamap->func == NULL || (uamap->addrcellcnt && uamap->addrcellcnt != addrcells) || (uamap->func)(unitaddr, sizeof (unitaddr), regbuf, addrcells) != 0) { free(regbuf); return (PICL_FAILURE); } err = ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, PICL_READ, strlen(unitaddr)+1, PICL_PROP_UNIT_ADDRESS, NULL, NULL); if (err == PICL_SUCCESS) err = ptree_create_and_add_prop(nodeh, &pinfo, unitaddr, NULL); free(regbuf); return (err); } /* * work out UnitAddress property of the specified node */ static int get_unitaddr(picl_nodehdl_t parh, picl_nodehdl_t nodeh, char *unitaddr, size_t ualen) { int regproplen, err; uint32_t *regbuf; picl_prophdl_t regh; ptree_propinfo_t pinfo; unitaddr_map_t *uamap; uint32_t addrcells; addrcells = get_addrcells_prop(parh); uamap = get_unitaddr_mapping(parh); err = ptree_get_prop_by_name(nodeh, OBP_REG, ®h); if (err != PICL_SUCCESS) return (err); err = ptree_get_propinfo(regh, &pinfo); if (err != PICL_SUCCESS) return (err); if (pinfo.piclinfo.size < (addrcells * sizeof (uint32_t))) return (PICL_FAILURE); regproplen = pinfo.piclinfo.size; regbuf = malloc(regproplen); if (regbuf == NULL) return (PICL_FAILURE); err = ptree_get_propval(regh, regbuf, regproplen); if (err != PICL_SUCCESS || uamap->func == NULL || (uamap->addrcellcnt && uamap->addrcellcnt != addrcells) || (uamap->func)(unitaddr, ualen, regbuf, addrcells) != 0) { free(regbuf); return (PICL_FAILURE); } free(regbuf); return (PICL_SUCCESS); } /* * Add UnitAddress property to all children of the specified node */ static int add_unitaddr_prop_to_subtree(picl_nodehdl_t nodeh) { int err; picl_nodehdl_t chdh; unitaddr_map_t *uamap; uint32_t addrcells; /* * Get #address-cells and unit address mapping entry for my * node's class */ addrcells = get_addrcells_prop(nodeh); uamap = get_unitaddr_mapping(nodeh); /* * Add UnitAddress property to my children and their subtree */ err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh, sizeof (picl_nodehdl_t)); while (err == PICL_SUCCESS) { (void) add_unitaddr_prop(chdh, uamap, addrcells); (void) add_unitaddr_prop_to_subtree(chdh); err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh, sizeof (picl_nodehdl_t)); } return (PICL_SUCCESS); } static int update_memory_size_prop(picl_nodehdl_t plafh) { picl_nodehdl_t memh; picl_prophdl_t proph; ptree_propinfo_t pinfo; int err, nspecs, snum, pval; char *regbuf; memspecs_t *mspecs; uint64_t memsize; /* * check if the #size-cells of the platform node is 2 */ err = ptree_get_propval_by_name(plafh, OBP_PROP_SIZE_CELLS, &pval, sizeof (pval)); if (err == PICL_PROPNOTFOUND) pval = SUPPORTED_NUM_CELL_SIZE; else if (err != PICL_SUCCESS) return (err); /* * don't know how to handle other vals */ if (pval != SUPPORTED_NUM_CELL_SIZE) return (PICL_FAILURE); err = ptree_get_node_by_path(MEMORY_PATH, &memh); if (err != PICL_SUCCESS) return (err); /* * Get the REG property to calculate the size of memory */ err = ptree_get_prop_by_name(memh, OBP_REG, &proph); if (err != PICL_SUCCESS) return (err); err = ptree_get_propinfo(proph, &pinfo); if (err != PICL_SUCCESS) return (err); regbuf = malloc(pinfo.piclinfo.size); if (regbuf == NULL) return (PICL_FAILURE); err = ptree_get_propval(proph, regbuf, pinfo.piclinfo.size); if (err != PICL_SUCCESS) { free(regbuf); return (err); } mspecs = (memspecs_t *)regbuf; nspecs = pinfo.piclinfo.size / sizeof (memspecs_t); memsize = 0; for (snum = 0; snum < nspecs; ++snum) memsize += mspecs[snum].size; err = ptree_get_prop_by_name(memh, PICL_PROP_SIZE, &proph); if (err == PICL_SUCCESS) { err = ptree_update_propval(proph, &memsize, sizeof (memsize)); free(regbuf); return (err); } /* * Add the size property */ (void) ptree_init_propinfo(&pinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (memsize), PICL_PROP_SIZE, NULL, NULL); err = ptree_create_and_add_prop(memh, &pinfo, &memsize, NULL); free(regbuf); return (err); } /* * This function is executed as part of .init when the plugin is * dlopen()ed */ static void picldevtree_register(void) { if (getenv(SUNW_PICLDEVTREE_PLUGIN_DEBUG)) picldevtree_debug = 1; (void) picld_plugin_register(&my_reg_info); } /* * This function is the init entry point of the plugin. * It initializes the /platform tree based on libdevinfo */ static void picldevtree_init(void) { picl_nodehdl_t rhdl; int err; struct utsname utsname; picl_nodehdl_t plafh; if (uname(&utsname) < 0) return; (void) strcpy(mach_name, utsname.machine); if (strcmp(mach_name, "sun4u") == 0) { builtin_map_ptr = sun4u_map; builtin_map_size = sizeof (sun4u_map) / sizeof (builtin_map_t); } else if (strcmp(mach_name, "sun4v") == 0) { builtin_map_ptr = sun4u_map; builtin_map_size = sizeof (sun4u_map) / sizeof (builtin_map_t); } else if (strcmp(mach_name, "i86pc") == 0) { builtin_map_ptr = i86pc_map; builtin_map_size = sizeof (i86pc_map) / sizeof (builtin_map_t); } else { builtin_map_ptr = NULL; builtin_map_size = 0; } err = ptree_get_root(&rhdl); if (err != PICL_SUCCESS) { syslog(LOG_ERR, DEVINFO_PLUGIN_INIT_FAILED); return; } process_devtree_conf_file(); if (libdevinfo_init(rhdl) != PICL_SUCCESS) { syslog(LOG_ERR, DEVINFO_PLUGIN_INIT_FAILED); return; } err = ptree_get_node_by_path(PLATFORM_PATH, &plafh); if (err != PICL_SUCCESS) return; (void) add_unitaddr_prop_to_subtree(plafh); add_asr_nodes(); (void) update_memory_size_prop(plafh); (void) setup_cpus(plafh); (void) add_ffb_config_info(plafh); (void) add_platform_info(plafh); set_pci_pciex_deviceid(plafh); (void) set_sbus_slot(plafh); (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, picldevtree_evhandler, NULL); (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, picldevtree_evhandler, NULL); (void) ptree_register_handler(PICLEVENT_CPU_STATE_CHANGE, picldevtree_evhandler, NULL); (void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE, picldevtree_evhandler, NULL); } /* * This function is the fini entry point of the plugin */ static void picldevtree_fini(void) { /* First unregister the event handlers */ (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, picldevtree_evhandler, NULL); (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, picldevtree_evhandler, NULL); (void) ptree_unregister_handler(PICLEVENT_CPU_STATE_CHANGE, picldevtree_evhandler, NULL); (void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE, picldevtree_evhandler, NULL); conf_name_class_map = free_conf_entries(conf_name_class_map); } /* * This function is the event handler of this plug-in. * * It processes the following events: * * PICLEVENT_SYSEVENT_DEVICE_ADDED * PICLEVENT_SYSEVENT_DEVICE_REMOVED * PICLEVENT_CPU_STATE_CHANGE * PICLEVENT_DR_AP_STATE_CHANGE */ /* ARGSUSED */ static void picldevtree_evhandler(const char *ename, const void *earg, size_t size, void *cookie) { char *devfs_path; char ptreepath[PATH_MAX]; char dipath[PATH_MAX]; picl_nodehdl_t plafh; picl_nodehdl_t nodeh; nvlist_t *nvlp; if ((earg == NULL) || (ptree_get_node_by_path(PLATFORM_PATH, &plafh) != PICL_SUCCESS)) return; if (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) == 0) { (void) setup_cpus(plafh); if (picldevtree_debug > 1) syslog(LOG_INFO, "picldevtree: event handler done\n"); return; } nvlp = NULL; if (nvlist_unpack((char *)earg, size, &nvlp, 0) || nvlist_lookup_string(nvlp, PICLEVENTARG_DEVFS_PATH, &devfs_path) || strlen(devfs_path) > (PATH_MAX - sizeof (PLATFORM_PATH))) { syslog(LOG_INFO, PICL_EVENT_DROPPED, ename); nvlist_free(nvlp); return; } (void) strlcpy(ptreepath, PLATFORM_PATH, PATH_MAX); (void) strlcat(ptreepath, devfs_path, PATH_MAX); (void) strlcpy(dipath, devfs_path, PATH_MAX); nvlist_free(nvlp); if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: event handler invoked ename:%s " "ptreepath:%s\n", ename, ptreepath); if (strcmp(ename, PICLEVENT_CPU_STATE_CHANGE) == 0) { goto done; } if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0) { di_node_t devnode; char *strp; picl_nodehdl_t parh; char nodeclass[PICL_CLASSNAMELEN_MAX]; char *nodename; int err; /* If the node already exist, then nothing else to do here */ if (ptree_get_node_by_path(ptreepath, &nodeh) == PICL_SUCCESS) return; /* Skip if unable to find parent PICL node handle */ parh = plafh; if (((strp = strrchr(ptreepath, '/')) != NULL) && (strp != strchr(ptreepath, '/'))) { *strp = '\0'; if (ptree_get_node_by_path(ptreepath, &parh) != PICL_SUCCESS) return; } /* * If parent is the root node */ if (parh == plafh) { ph = di_prom_init(); devnode = di_init(dipath, DINFOCPYALL); if (devnode == DI_NODE_NIL) { if (ph != NULL) { di_prom_fini(ph); ph = NULL; } return; } nodename = di_node_name(devnode); if (nodename == NULL) { di_fini(devnode); if (ph != NULL) { di_prom_fini(ph); ph = NULL; } return; } err = get_node_class(nodeclass, devnode, nodename); if (err < 0) { di_fini(devnode); if (ph != NULL) { di_prom_fini(ph); ph = NULL; } return; } err = construct_devtype_node(plafh, nodename, nodeclass, devnode, &nodeh); if (err != PICL_SUCCESS) { di_fini(devnode); if (ph != NULL) { di_prom_fini(ph); ph = NULL; } return; } (void) update_subtree(nodeh, devnode); (void) add_unitaddr_prop_to_subtree(nodeh); if (ph != NULL) { di_prom_fini(ph); ph = NULL; } di_fini(devnode); goto done; } /* kludge ... try without bus-addr first */ if ((strp = strrchr(dipath, '@')) != NULL) { char *p; p = strrchr(dipath, '/'); if (p != NULL && strp > p) { *strp = '\0'; devnode = di_init(dipath, DINFOCPYALL); if (devnode != DI_NODE_NIL) di_fini(devnode); *strp = '@'; } } /* Get parent devnode */ if ((strp = strrchr(dipath, '/')) != NULL) *++strp = '\0'; devnode = di_init(dipath, DINFOCPYALL); if (devnode == DI_NODE_NIL) return; ph = di_prom_init(); (void) update_subtree(parh, devnode); (void) add_unitaddr_prop_to_subtree(parh); if (ph) { di_prom_fini(ph); ph = NULL; } di_fini(devnode); } else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0) { char delclass[PICL_CLASSNAMELEN_MAX]; char *strp; /* * if final element of path doesn't have a unit address * then it is not uniquely identifiable - cannot remove */ if (((strp = strrchr(ptreepath, '/')) != NULL) && strchr(strp, '@') == NULL) return; /* skip if can't find the node */ if (ptree_get_node_by_path(ptreepath, &nodeh) != PICL_SUCCESS) return; if (ptree_delete_node(nodeh) != PICL_SUCCESS) return; if (picldevtree_debug) syslog(LOG_INFO, "picldevtree: deleted node nodeh:%llx\n", nodeh); if ((ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, delclass, PICL_CLASSNAMELEN_MAX) == PICL_SUCCESS) && IS_MC(delclass)) { if (post_mc_event(PICLEVENT_MC_REMOVED, nodeh) != PICL_SUCCESS) syslog(LOG_WARNING, PICL_EVENT_DROPPED, PICLEVENT_MC_REMOVED); } else (void) ptree_destroy_node(nodeh); } done: (void) setup_cpus(plafh); (void) add_ffb_config_info(plafh); set_pci_pciex_deviceid(plafh); (void) set_sbus_slot(plafh); if (picldevtree_debug > 1) syslog(LOG_INFO, "picldevtree: event handler done\n"); }