summaryrefslogtreecommitdiff
path: root/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/picl/plugins/common/devtree/picldevtree.c')
-rw-r--r--usr/src/cmd/picl/plugins/common/devtree/picldevtree.c3433
1 files changed, 3433 insertions, 0 deletions
diff --git a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
new file mode 100644
index 0000000000..1dfe5f85b8
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
@@ -0,0 +1,3433 @@
+/*
+ * 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"
+
+/*
+ * PICL plug-in that creates device tree nodes for all platforms
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <syslog.h>
+#include <libdevinfo.h>
+#include <sys/dkio.h>
+#include <sys/vtoc.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <picl.h>
+#include <picltree.h>
+#include <sys/types.h>
+#include <sys/processor.h>
+#include <kstat.h>
+#include <sys/sysinfo.h>
+#include <dirent.h>
+#include <libintl.h>
+#include <pthread.h>
+#include <libnvpair.h>
+#include <sys/utsname.h>
+#include <sys/systeminfo.h>
+#include <sys/obpdefs.h>
+#include <sys/openpromio.h>
+#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;
+
+/*
+ * 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_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);
+
+
+/*
+ * 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, NULL) == 0) {
+ mch = NULL;
+ (void) nvlist_lookup_uint64(unpack_nvl,
+ PICLEVENTARG_NODEHANDLE, &mch);
+ if (mch != NULL) {
+ 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, NULL)) {
+ 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, NULL)) {
+ 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))
+ (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 = alloca(sizeof (picl_prophdl_t) * nrows);
+ 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);
+ return (err);
+ }
+
+ return (PICL_SUCCESS);
+}
+
+/*
+ * 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;
+
+ 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 = alloca(len);
+ if (pvalbuf == NULL)
+ return (0);
+
+ err = ptree_get_propval(proph, pvalbuf, len);
+ if ((err == PICL_SUCCESS) && (strcmp(pvalbuf, pval) == 0))
+ return (1); /* prop match */
+
+ return (0);
+}
+
+/*
+ * 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 = alloca(retval + 1);
+ (void) memcpy(strdat, pdata, retval);
+ strdat[retval] = '\0';
+ retval++;
+ } else {
+ strdat = alloca(retval);
+ (void) memcpy(strdat, pdata, retval);
+ }
+
+ /*
+ * 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)
+ 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)
+ return (err);
+ (void) ptree_create_and_add_prop(nodeh, &propinfo,
+ strdat, NULL);
+ }
+ 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);
+}
+
+/*
+ * 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;
+
+ 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:
+ (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);
+ break;
+ case DI_PROP_TYPE_INT: {
+ int *idata;
+ int len;
+
+ len = di_prop_ints(di_prop, &idata);
+ if (len < 0)
+ /* Recieved error, so ignore prop */
+ break;
+
+ if (len == 1)
+ (void) ptree_init_propinfo(&propinfo,
+ PTREE_PROPINFO_VERSION, PICL_PTYPE_INT,
+ PICL_READ, len * 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);
+ }
+ break;
+ case DI_PROP_TYPE_STRING: {
+ char *sdata;
+ int len;
+
+ len = di_prop_strings(di_prop, &sdata);
+ if (len < 0)
+ break;
+
+ 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);
+ }
+ }
+ break;
+ case DI_PROP_TYPE_BYTE: {
+ int len;
+ unsigned char *bdata;
+
+ len = di_prop_bytes(di_prop, &bdata);
+ if (len < 0)
+ break;
+ (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);
+ }
+ break;
+ case DI_PROP_TYPE_UNKNOWN:
+ 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);
+
+ *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
+ * @<unit_addr> 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 properties.
+ */
+ err = ptree_create_node(nodename, nodeclass, &chdh);
+ if (err != PICL_SUCCESS)
+ return (err);
+ (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);
+
+}
+
+/*
+ * 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;
+
+
+ if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
+ return (PICL_FAILURE);
+
+ if ((ph = di_prom_init()) == NULL)
+ 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) {
+ 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, PICL_PROP_INSTANCE, 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;
+ 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);
+ (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);
+ free(opp);
+ *exportlistlen = opp->oprom_size;
+ (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, &regh);
+ 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 = alloca(pinfo.piclinfo.size);
+ if (regbuf == NULL)
+ return (PICL_FAILURE);
+ err = ptree_get_propval(regh, regbuf, pinfo.piclinfo.size);
+ if (err != PICL_SUCCESS)
+ return (err);
+ *regval = *regbuf; /* get first 32-bit value */
+ return (PICL_SUCCESS);
+}
+
+/*
+ * 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, &regval);
+ 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 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 child nodes
+ * which can be correlated with the slot they are plugged into
+ * on the motherboard.
+ */
+static int
+set_pci_deviceid(picl_nodehdl_t plafh)
+{
+ int err;
+
+ err = ptree_walk_tree_by_class(plafh, PICL_CLASS_PCI, NULL,
+ add_pci_deviceids);
+
+ return (err);
+}
+
+/*
+ * 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<phys_hi><phys_lo>,<lun_lo>
+ */
+
+ 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, &regh);
+ 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 = alloca(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) {
+ 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);
+
+ 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, &regh);
+ 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 = alloca(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) {
+ return (PICL_FAILURE);
+ }
+ 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 = alloca(pinfo.piclinfo.size);
+ if (regbuf == NULL)
+ return (PICL_FAILURE);
+
+ err = ptree_get_propval(proph, regbuf, pinfo.piclinfo.size);
+ if (err != PICL_SUCCESS)
+ 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));
+ 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);
+ 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, "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);
+
+ (void) set_pci_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);
+}
+
+/*
+ * 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);
+
+ 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
+ */
+/* 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)
+ return;
+
+ nvlp = NULL;
+ if (ptree_get_node_by_path(PLATFORM_PATH, &plafh) != PICL_SUCCESS ||
+ nvlist_unpack((char *)earg, size, &nvlp, NULL) ||
+ nvlist_lookup_string(nvlp, PICLEVENTARG_DEVFS_PATH, &devfs_path) ||
+ strlen(devfs_path) > (PATH_MAX - sizeof (PLATFORM_PATH))) {
+ syslog(LOG_INFO, PICL_EVENT_DROPPED, ename);
+ if (nvlp)
+ 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_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);
+ (void) set_pci_deviceid(plafh);
+ (void) set_sbus_slot(plafh);
+ if (picldevtree_debug > 1)
+ syslog(LOG_INFO, "picldevtree: event handler done\n");
+}