summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvenki <none@none>2007-03-31 18:24:05 -0700
committervenki <none@none>2007-03-31 18:24:05 -0700
commit0d63ce2b32a9e1cc8ed71d4d92536c44d66a530a (patch)
tree44032a0316e273c5597b6257f3510bd4dcb77e1f
parent1c02caff5ab8a73df8274cf66e0444b24cf4af10 (diff)
downloadillumos-joyent-0d63ce2b32a9e1cc8ed71d4d92536c44d66a530a.tar.gz
FWARC/2007/133 SNMP Domain Service
FWARC/2007/138 Updates to PRI structures 6438074 customer requests ability to query power/fan status info from OS 6526169 prtdiag output doesn't have Memory Configuration Information 6531453 sun4v picl needs device labels in the devtree 6534449 Unable to send a domain services message larger than 4K
-rw-r--r--usr/src/cmd/picl/plugins/common/devtree/picldevtree.c5
-rw-r--r--usr/src/cmd/picl/plugins/inc/picldefs.h42
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/Makefile19
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/include/libpiclsnmp.h60
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/include/picloids.h355
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/Makefile48
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/Makefile108
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c760
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.h154
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.c616
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.h144
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c668
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.h159
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.c1245
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.h76
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/mdesc/init.c82
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/Makefile120
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/init.c105
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c305
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c316
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c145
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h86
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/snmp/Makefile97
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c1690
-rw-r--r--usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.h215
-rw-r--r--usr/src/lib/libprtdiag/Makefile.com3
-rw-r--r--usr/src/lib/libprtdiag/common/display_sun4v.c1384
-rw-r--r--usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c15
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_sparc1
-rw-r--r--usr/src/pkgdefs/SUNWldomr.v/prototype_sparc2
-rw-r--r--usr/src/pkgdefs/SUNWpiclu/prototype_sparc8
-rw-r--r--usr/src/uts/sparc/os/name_to_major2
-rw-r--r--usr/src/uts/sun4v/Makefile.files1
-rw-r--r--usr/src/uts/sun4v/Makefile.sun4v.shared1
-rw-r--r--usr/src/uts/sun4v/ds_snmp/Makefile99
-rw-r--r--usr/src/uts/sun4v/io/ds.c114
-rw-r--r--usr/src/uts/sun4v/io/ds_pri.c62
-rw-r--r--usr/src/uts/sun4v/io/ds_snmp.c1014
-rw-r--r--usr/src/uts/sun4v/io/ds_snmp.conf28
-rw-r--r--usr/src/uts/sun4v/sys/Makefile1
-rw-r--r--usr/src/uts/sun4v/sys/ds_snmp.h60
41 files changed, 10216 insertions, 199 deletions
diff --git a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
index d962e0520c..a16766b3b8 100644
--- a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
+++ b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -3212,6 +3212,9 @@ picldevtree_init(void)
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);
diff --git a/usr/src/cmd/picl/plugins/inc/picldefs.h b/usr/src/cmd/picl/plugins/inc/picldefs.h
index 3a5cb31a61..f719f3e605 100644
--- a/usr/src/cmd/picl/plugins/inc/picldefs.h
+++ b/usr/src/cmd/picl/plugins/inc/picldefs.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -110,6 +110,27 @@ extern "C" {
#define PICL_CLASS_CHASSIS_SERIAL_NUM "chassis-serial-number"
/*
+ * Sun4v platforms do not create /frutree; instead they create
+ * the /physical-platform subtree. The following is the list of
+ * additional PICL classes that may be present in /physical-platform
+ */
+#define PICL_CLASS_ALARM "alarm"
+#define PICL_CLASS_BACKPLANE "backplane"
+#define PICL_CLASS_BATTERY "battery"
+#define PICL_CLASS_CHASSIS "chassis"
+#define PICL_CLASS_CONTAINER "container"
+#define PICL_CLASS_MODULE "module"
+#define PICL_CLASS_OTHER "other"
+#define PICL_CLASS_POWERSUPPLY "power-supply"
+#define PICL_CLASS_RPM_INDICATOR "rpm-indicator"
+#define PICL_CLASS_RPM_SENSOR "rpm-sensor"
+#define PICL_CLASS_PRESENCE_INDICATOR "presence-indicator"
+#define PICL_CLASS_INDICATOR "indicator"
+#define PICL_CLASS_SENSOR "sensor"
+#define PICL_CLASS_STACK "stack"
+#define PICL_CLASS_UNKNOWN "unknown"
+
+/*
* Solaris driver property names
*/
#define PICL_PROP_INSTANCE "instance"
@@ -203,6 +224,25 @@ extern "C" {
#define PICL_UNITADDR_LEN_MAX 256
/*
+ * Additional PICL properties for Sun4v platforms
+ */
+#define PICL_PROP_BATTERY_STATUS "BatteryStatus"
+#define PICL_PROP_EXPECTED "Expected"
+#define PICL_PROP_FW_REVISION "FW-version"
+#define PICL_PROP_HW_REVISION "HW-version"
+#define PICL_PROP_IS_REPLACEABLE "Replaceable"
+#define PICL_PROP_IS_HOT_SWAPPABLE "HotSwappable"
+#define PICL_PROP_IS_FRU "FRU"
+#define PICL_PROP_PHYS_DESCRIPTION "Description"
+#define PICL_PROP_SPEED "Speed"
+#define PICL_PROP_MFG_NAME "MfgName"
+#define PICL_PROP_MODEL_NAME "ModelName"
+#define PICL_PROP_SENSOR_VALUE "SensorValue"
+#define PICL_PROP_BASE_UNITS "BaseUnits"
+#define PICL_PROP_EXPONENT "Exponent"
+#define PICL_PROP_RATE_UNITS "RateUnits"
+
+/*
* Various threshold property names
*/
#define PICL_PROP_LOW_POWER_OFF "LowPowerOffThreshold"
diff --git a/usr/src/cmd/picl/plugins/sun4v/Makefile b/usr/src/cmd/picl/plugins/sun4v/Makefile
index 054da14128..8ce53cb911 100644
--- a/usr/src/cmd/picl/plugins/sun4v/Makefile
+++ b/usr/src/cmd/picl/plugins/sun4v/Makefile
@@ -2,9 +2,8 @@
# 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.
+# 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.
@@ -19,27 +18,35 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#pragma ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
+#
+
#
# cmd/picl/plugins/sun4v/Makefile
#
-SUBDIRS= mdesc ontario
+SUBDIRS= lib .WAIT snmp .WAIT mdesc pri ontario
+
+MSGSUBDIRS= snmp
all := TARGET= all
install := TARGET= install
clean := TARGET= clean
clobber := TARGET= clobber
lint := TARGET= lint
+_msg := TARGET= _msg
.KEEP_STATE:
all install clean clobber lint : $(SUBDIRS)
+_msg: $(MSGSUBDIRS)
+
$(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
diff --git a/usr/src/cmd/picl/plugins/sun4v/include/libpiclsnmp.h b/usr/src/cmd/picl/plugins/sun4v/include/libpiclsnmp.h
new file mode 100644
index 0000000000..4968628973
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/include/libpiclsnmp.h
@@ -0,0 +1,60 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBPICLSNMP_H
+#define _LIBPICLSNMP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Opaque picl snmp handle
+ */
+typedef void *picl_snmphdl_t;
+
+/*
+ * Exported interfaces
+ */
+extern picl_snmphdl_t snmp_init(void);
+extern void snmp_fini(picl_snmphdl_t);
+
+extern int snmp_reinit(picl_snmphdl_t hdl, int clr_linkreset);
+extern void snmp_register_group(picl_snmphdl_t, char *, int, int);
+
+extern int snmp_get_int(picl_snmphdl_t, char *, int, int *, int *);
+extern int snmp_get_str(picl_snmphdl_t, char *, int, char **, int *);
+extern int snmp_get_bitstr(picl_snmphdl_t, char *, int, uchar_t **,
+ uint_t *, int *);
+extern int snmp_get_nextrow(picl_snmphdl_t, char *, int, int *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBPICLSNMP_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/include/picloids.h b/usr/src/cmd/picl/plugins/sun4v/include/picloids.h
new file mode 100644
index 0000000000..3149cdcd4d
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/include/picloids.h
@@ -0,0 +1,355 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PICLOIDS_H
+#define _PICLOIDS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * IETF OIDs (not all are used by PICL)
+ */
+#define OID_ISO "1"
+#define OID_ORG OID_ISO ".3"
+#define OID_DOD OID_ORG ".6"
+#define OID_INTERNET OID_DOD ".1"
+
+#define OID_PRIVATE OID_INTERNET ".4"
+#define OID_ENTERPRISES OID_PRIVATE ".1"
+#define OID_SUN OID_ENTERPRISES ".42"
+
+#define OID_MGMT OID_INTERNET ".2"
+#define OID_MIB2 OID_MGMT ".1"
+#define OID_entityMIB OID_MIB2 ".47"
+#define OID_entityMIBObjects OID_entityMIB ".1"
+
+#define OID_entityPhysical OID_entityMIBObjects ".1"
+#define OID_entPhysicalTable OID_entityPhysical ".1"
+#define OID_entPhysicalEntry OID_entPhysicalTable ".1"
+
+#define OID_entPhysicalIndex OID_entPhysicalEntry ".1"
+#define OID_entPhysicalDescr OID_entPhysicalEntry ".2"
+#define OID_entPhysicalVendorType OID_entPhysicalEntry ".3"
+#define OID_entPhysicalContainedIn OID_entPhysicalEntry ".4"
+#define OID_entPhysicalClass OID_entPhysicalEntry ".5"
+#define OID_entPhysicalParentRelPos OID_entPhysicalEntry ".6"
+#define OID_entPhysicalName OID_entPhysicalEntry ".7"
+#define OID_entPhysicalHardwareRev OID_entPhysicalEntry ".8"
+#define OID_entPhysicalFirmwareRev OID_entPhysicalEntry ".9"
+#define OID_entPhysicalSoftwareRev OID_entPhysicalEntry ".10"
+#define OID_entPhysicalSerialNum OID_entPhysicalEntry ".11"
+#define OID_entPhysicalMfgName OID_entPhysicalEntry ".12"
+#define OID_entPhysicalModelName OID_entPhysicalEntry ".13"
+#define OID_entPhysicalAlias OID_entPhysicalEntry ".14"
+#define OID_entPhysicalAssetID OID_entPhysicalEntry ".15"
+#define OID_entPhysicalIsFRU OID_entPhysicalEntry ".16"
+
+/*
+ * Conceptual row change time for handling hotplug/hotswap events
+ */
+#define OID_entityGeneral OID_entityMIBObjects ".4"
+#define OID_entLastChangeTime OID_entityGeneral ".1"
+
+/*
+ * Sun Platform MIB OIDs used by PICL
+ */
+#define OID_products OID_SUN ".2"
+#define OID_sunFire OID_products ".70"
+#define OID_sunPlatMIB OID_sunFire ".101"
+#define OID_sunPlatMIBObjects OID_sunPlatMIB ".1"
+#define OID_sunPlatMIBPhysicalObjects OID_sunPlatMIBObjects ".1"
+
+/*
+ * Equipment Table
+ */
+#define OID_sunPlatEquipmentTable OID_sunPlatMIBPhysicalObjects ".2"
+#define OID_sunPlatEquipmentEntry OID_sunPlatEquipmentTable ".1"
+#define OID_sunPlatEquipmentOperationalState \
+ OID_sunPlatEquipmentEntry ".2"
+
+/*
+ * Equipment Holder Table
+ */
+#define OID_sunPlatEquipmentHolderTable OID_sunPlatMIBPhysicalObjects ".3"
+#define OID_sunPlatEquipmentHolderEntry OID_sunPlatEquipmentHolderTable ".1"
+#define OID_sunPlatEquipmentHolderAcceptableTypes \
+ OID_sunPlatEquipmentHolderEntry ".2"
+
+/*
+ * Circuit Pack Table
+ */
+#define OID_sunPlatCircuitPackTable OID_sunPlatMIBPhysicalObjects ".4"
+#define OID_sunPlatCircuitPackEntry OID_sunPlatCircuitPackTable ".1"
+#define OID_sunPlatCircuitPackReplaceable \
+ OID_sunPlatCircuitPackEntry ".3"
+#define OID_sunPlatCircuitPackHotSwappable \
+ OID_sunPlatCircuitPackEntry ".4"
+
+/*
+ * Physical Class Table
+ */
+#define OID_sunPlatPhysicalTable OID_sunPlatMIBPhysicalObjects ".5"
+#define OID_sunPlatPhysicalEntry OID_sunPlatPhysicalTable ".1"
+#define OID_sunPlatPhysicalClass OID_sunPlatPhysicalEntry ".1"
+
+/*
+ * Sensor Table
+ */
+#define OID_sunPlatSensorTable OID_sunPlatMIBPhysicalObjects ".6"
+#define OID_sunPlatSensorEntry OID_sunPlatSensorTable ".1"
+#define OID_sunPlatSensorClass OID_sunPlatSensorEntry ".1"
+#define OID_sunPlatSensorType OID_sunPlatSensorEntry ".2"
+
+/*
+ * Binary Sensor Table
+ */
+#define OID_sunPlatBinarySensorTable OID_sunPlatMIBPhysicalObjects ".7"
+#define OID_sunPlatBinarySensorEntry OID_sunPlatBinarySensorTable ".1"
+
+#define OID_sunPlatBinarySensorCurrent OID_sunPlatBinarySensorEntry ".1"
+#define OID_sunPlatBinarySensorExpected OID_sunPlatBinarySensorEntry ".2"
+#define OID_sunPlatBinarySensorInterpretTrue \
+ OID_sunPlatBinarySensorEntry ".3"
+#define OID_sunPlatBinarySensorInterpretFalse \
+ OID_sunPlatBinarySensorEntry ".4"
+
+/*
+ * Numeric Sensor Table
+ */
+#define OID_sunPlatNumericSensorTable OID_sunPlatMIBPhysicalObjects ".8"
+#define OID_sunPlatNumericSensorEntry OID_sunPlatNumericSensorTable ".1"
+#define OID_sunPlatNumericSensorCurrent OID_sunPlatNumericSensorEntry ".4"
+#define OID_sunPlatNumericSensorBaseUnits \
+ OID_sunPlatNumericSensorEntry ".1"
+#define OID_sunPlatNumericSensorExponent \
+ OID_sunPlatNumericSensorEntry ".2"
+#define OID_sunPlatNumericSensorRateUnits \
+ OID_sunPlatNumericSensorEntry ".3"
+#define OID_sunPlatNumericSensorLowerThresholdNonCritical \
+ OID_sunPlatNumericSensorEntry ".8"
+#define OID_sunPlatNumericSensorUpperThresholdNonCritical \
+ OID_sunPlatNumericSensorEntry ".9"
+#define OID_sunPlatNumericSensorLowerThresholdCritical \
+ OID_sunPlatNumericSensorEntry ".10"
+#define OID_sunPlatNumericSensorUpperThresholdCritical \
+ OID_sunPlatNumericSensorEntry ".11"
+#define OID_sunPlatNumericSensorLowerThresholdFatal \
+ OID_sunPlatNumericSensorEntry ".12"
+#define OID_sunPlatNumericSensorUpperThresholdFatal \
+ OID_sunPlatNumericSensorEntry ".13"
+#define OID_sunPlatNumericSensorEnabledThresholds \
+ OID_sunPlatNumericSensorEntry ".15"
+
+/*
+ * Alarm Table
+ */
+#define OID_sunPlatAlarmTable OID_sunPlatMIBPhysicalObjects ".12"
+#define OID_sunPlatAlarmEntry OID_sunPlatAlarmTable ".1"
+#define OID_sunPlatAlarmType OID_sunPlatAlarmEntry ".1"
+#define OID_sunPlatAlarmState OID_sunPlatAlarmEntry ".2"
+
+/*
+ * Power Supply Table
+ */
+#define OID_sunPlatPowerSupplyTable OID_sunPlatMIBPhysicalObjects ".14"
+#define OID_sunPlatPowerSupplyEntry OID_sunPlatPowerSupplyTable ".1"
+#define OID_sunPlatPowerSupplyClass OID_sunPlatPowerSupplyEntry ".1"
+
+/*
+ * Battery Table
+ */
+#define OID_sunPlatBatteryTable OID_sunPlatMIBPhysicalObjects ".15"
+#define OID_sunPlatBatteryEntry OID_sunPlatBatteryTable ".1"
+#define OID_sunPlatBatteryStatus OID_sunPlatBatteryEntry ".1"
+
+/*
+ * Integer enumeration classes used by PICL
+ */
+typedef enum {
+ ST_TRUE = 1,
+ ST_FALSE = 2
+} snmp_truthval_t;
+
+/*
+ * Note that the truth values could be much longer than the length
+ * of the strings "true" or "false", since we actuallly interpret them
+ * using InterpretTrue and InterpretFalse values in the MIB. Currently
+ * we limit them to be 32 (see MAX_TRUTHVAL_LEN definition below)
+ */
+#define STR_ST_TRUE "true"
+#define STR_ST_FALSE "false"
+
+/* entPhysicalClass */
+typedef enum {
+ SPC_OTHER = 1,
+ SPC_UNKNOWN = 2,
+ SPC_CHASSIS = 3,
+ SPC_BACKPLANE = 4,
+ SPC_CONTAINER = 5,
+ SPC_POWERSUPPLY = 6,
+ SPC_FAN = 7,
+ SPC_SENSOR = 8,
+ SPC_MODULE = 9,
+ SPC_PORT = 10,
+ SPC_STACK = 11
+} snmp_physical_class_t;
+
+/* sunPlatEquipmentOperationalState */
+typedef enum {
+ SSOS_DISABLED = 1,
+ SSOS_ENABLED = 2
+} snmp_sunplat_op_state_t;
+
+/*
+ * Update MAX_OPSTATE_LEN below if these strings are changed
+ */
+#define STR_SSOS_DISABLED "disabled"
+#define STR_SSOS_ENABLED "enabled"
+
+/* sunPlatPhysicalClass */
+typedef enum {
+ SSPC_OTHER = 1,
+ SSPC_ALARM = 2,
+ SSPC_WATCHDOG = 3
+} snmp_sunplat_phys_class_t;
+
+/* sunPlatSensorClass */
+typedef enum {
+ SSSC_BINARY = 1,
+ SSSC_NUMERIC = 2,
+ SSSC_DISCRETE = 3
+} snmp_sunplat_sensor_class_t;
+
+/* sunPlatSensorType */
+typedef enum {
+ SSST_OTHER = 1,
+ SSST_UNKNOWN = 2,
+ SSST_TEMPERATURE = 3,
+ SSST_VOLTAGE = 4,
+ SSST_CURRENT = 5,
+ SSST_TACHOMETER = 6,
+ SSST_COUNTER = 7,
+ SSST_SWITCH = 8,
+ SSST_LOCK = 9,
+ SSST_HUMIDITY = 10,
+ SSST_SMOKE_DETECTION = 11,
+ SSST_PRESENCE = 12,
+ SSST_AIRFLOW = 13
+} snmp_sunplat_sensor_type_t;
+
+/* sunPlatAlarmType */
+typedef enum {
+ SSAT_OTHER = 1,
+ SSAT_AUDIBLE = 2,
+ SSAT_VISIBLE = 3,
+ SSAT_MOTION = 4,
+ SSAT_SWITCH = 5
+} snmp_sunplat_alarm_type_t;
+
+/* sunPlatAlarmState */
+typedef enum {
+ SSAS_UNKNOWN = 1,
+ SSAS_OFF = 2,
+ SSAS_STEADY = 3,
+ SSAS_ALTERNATING = 4
+} snmp_sunplat_alarm_state_t;
+
+/*
+ * Update MAX_ALARMSTATE_LEN below if these strings are changed
+ */
+#define STR_SSAS_UNKNOWN "unknown"
+#define STR_SSAS_OFF "off"
+#define STR_SSAS_STEADY "steady"
+#define STR_SSAS_ALTERNATING "alternating"
+
+/*
+ * Bit masks for the sunPlatNumericSensorEnabledThresholds
+ */
+#define LOWER_NON_CRITICAL 0x80
+#define UPPER_NON_CRITICAL 0x40
+#define LOWER_CRITICAL 0x20
+#define UPPER_CRITICAL 0x10
+#define LOWER_FATAL 0x08
+#define UPPER_FATAL 0x04
+
+/*
+ * sunPlatPowerSupplyClass
+ */
+typedef enum {
+ SSPSC_OTHER = 1,
+ SSPSC_POWERSUPPLY = 2,
+ SSPSC_BATTERY = 3
+} snmp_sunplat_power_supply_class_t;
+
+/*
+ * sunPlatBatteryStatus
+ */
+typedef enum {
+ SSBS_OTHER = 1,
+ SSBS_UNKNOWN = 2,
+ SSBS_FULLYCHARGED = 3,
+ SSBS_LOW = 4,
+ SSBS_CRITICAL = 5,
+ SSBS_CHARGING = 6,
+ SSBS_CHARGING_AND_LOW = 7,
+ SSBS_CHARGING_AND_HIGH = 8,
+ SSBS_CHARGING_AND_CRITICAL = 9,
+ SSBS_UNDEFINED = 10,
+ SSBS_PARTIALLY_CHARGED = 11
+} snmp_sunplat_battery_status_t;
+
+/*
+ * Update MAX_BATTERYSTATUS_LEN below if these strings are changed
+ */
+#define STR_SSBS_OTHER "Other"
+#define STR_SSBS_UNKNOWN "Unknown"
+#define STR_SSBS_FULLYCHARGED "Fully Charged"
+#define STR_SSBS_LOW "Low"
+#define STR_SSBS_CRITICAL "Critical"
+#define STR_SSBS_CHARGING "Charging"
+#define STR_SSBS_CHARGING_AND_LOW "Charging and Low"
+#define STR_SSBS_CHARGING_AND_HIGH "Charging and High"
+#define STR_SSBS_CHARGING_AND_CRITICAL "Charging and Critical"
+#define STR_SSBS_UNDEFINED "Undefined"
+#define STR_SSBS_PARTIALLY_CHARGED "Partially Charged"
+
+/*
+ * Max limits of all volatiles
+ */
+#define MAX_OPSTATE_LEN 10
+#define MAX_ALARMSTATE_LEN 12
+#define MAX_TRUTHVAL_LEN 32
+#define MAX_BATTERYSTATUS_LEN 32
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PICLOIDS_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/Makefile b/usr/src/cmd/picl/plugins/sun4v/lib/Makefile
new file mode 100644
index 0000000000..1f157985a1
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/Makefile
@@ -0,0 +1,48 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# cmd/picl/plugins/sun4v/lib/Makefile
+#
+
+SUBDIRS= snmp .WAIT
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber lint : $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/Makefile b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/Makefile
new file mode 100644
index 0000000000..c73aad48cc
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/Makefile
@@ -0,0 +1,108 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# cmd/picl/plugins/sun4v/lib/snmp/Makefile
+#
+
+LIBRARY= libpiclsnmp.a
+VERS= .1
+OBJECTS= snmplib.o pdu.o asn1.o debug.o
+
+# include library definitions
+include $(SRC)/Makefile.psm
+include $(SRC)/lib/Makefile.lib
+
+ROOT_PLATFORM = $(USR_PLAT_DIR)/sun4v
+
+include $(SRC)/cmd/picl/plugins/Makefile.com
+
+SRCS= $(OBJECTS:%.o=%.c)
+
+LIBS= $(DYNLIB)
+
+ROOTLIBDIR = $(ROOT_PLATFORM)/lib
+ROOTLIBDIR := OWNER = root
+ROOTLIBDIR := GROUP = sys
+
+CLEANFILES= $(LINTOUT) $(LINTLIB)
+CLOBBERFILES += $(LIBLINKS)
+
+CPPFLAGS += -I. -I../../include -I$(SRC)/uts/sun4v
+CPPFLAGS += -D_REENTRANT
+
+#
+# Be careful when enabling SNMP_DEBUG; the debug log can quickly grow
+# very very large. Never run cycle stress test with SNMP_DEBUG enabled!
+#
+#CPPFLAGS += -DSNMP_DEBUG
+
+#
+# Do NOT uncomment the following two lines, unless you want to test
+# the behavior of the library with an SNMP agent over network.
+#
+#CPPFLAGS += -DUSE_SOCKETS
+#LDLIBS += -lsocket -lnsl
+
+CFLAGS += $(CCVERBOSE) -DBIG_ENDIAN
+LDLIBS += -lc -lnvpair
+
+# It's OK not to build debug.c except when SNMP_DEBUG is enabled.
+# Don't let lint complain about it.
+#
+ALWAYS_LINT_DEFS += -erroff=E_EMPTY_TRANSLATION_UNIT
+
+.KEEP_STATE:
+
+
+SUBDIRS=
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+all: $(DYNLIB) $(LIBLINKS)
+
+install: $(ROOTLIBDIR) all $(ROOTLIBS) $(ROOTLINKS)
+
+$(LIBLINKS): FRC
+ $(RM) $@; $(SYMLINK) $(DYNLIB) $@
+
+# include library targets
+include $(SRC)/cmd/picl/plugins/Makefile.targ
+include $(SRC)/lib/Makefile.targ
+
+lint :
+ $(LINT.c) -m $(SRCS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c
new file mode 100644
index 0000000000..4de8545981
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c
@@ -0,0 +1,760 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * ASN.1 encoding related routines
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "asn1.h"
+#include "pdu.h"
+#include "debug.h"
+
+/*
+ * This routine builds a 'SEQUENCE OF' ASN.1 object in the buffer
+ * using the 'id' and 'length' supplied. This is probably the place
+ * where using "reverse" asn encoding will help.
+ */
+uchar_t *
+asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
+{
+ /*
+ * When rebuilding sequence (which we do many times), we'll
+ * simply pass NULL to bufsz_p to skip the error check.
+ */
+ if ((bufsz_p) && (*bufsz_p < 4))
+ return (NULL);
+
+ buf[0] = id;
+ buf[1] = (uchar_t)(ASN_LONG_LEN | 0x02); /* following 2 octets */
+ buf[2] = (uchar_t)((length >> 8) & 0xff);
+ buf[3] = (uchar_t)(length & 0xff);
+
+ if (bufsz_p)
+ *bufsz_p -= 4;
+
+ LOGASNSEQ(buf, 4);
+
+ return (buf + 4);
+}
+
+/*
+ * The next two routines, asn_build_header() and asn_build_length(), build
+ * the header and length for an arbitrary object type into the buffer. The
+ * length of the object is encoded using as few length octets as possible.
+ */
+uchar_t *
+asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
+{
+ if (*bufsz_p < 1)
+ return (NULL);
+
+ buf[0] = id;
+ (*bufsz_p)--;
+
+ return (asn_build_length(buf + 1, bufsz_p, length));
+}
+uchar_t *
+asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
+{
+ if (length < 0x80) {
+ if (*bufsz_p < 1)
+ return (NULL);
+ buf[0] = (uchar_t)length;
+ (*bufsz_p)--;
+
+ LOGASNLENGTH(buf, 1);
+
+ return (buf + 1);
+
+ } else if (length <= 0xFF) {
+ if (*bufsz_p < 2)
+ return (NULL);
+ buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01);
+ buf[1] = (uchar_t)length;
+ *bufsz_p -= 2;
+
+ LOGASNLENGTH(buf, 2);
+
+ return (buf + 2);
+
+ } else {
+ if (*bufsz_p < 3)
+ return (NULL);
+
+ buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02);
+ buf[1] = (uchar_t)((length >> 8) & 0xff);
+ buf[2] = (uchar_t)(length & 0xff);
+ *bufsz_p -= 3;
+
+ LOGASNLENGTH(buf, 3);
+
+ return (buf + 3);
+ }
+}
+/*
+ * Builds an ASN.1 encoded integer in the buffer using as few octets
+ * as possible.
+ */
+uchar_t *
+asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
+{
+ uint_t uival;
+ int ival, i;
+ short sval;
+ char cval;
+
+ size_t valsz;
+ uchar_t *p, *valp;
+
+ /*
+ * We need to "pack" the integer before sending it, so determine
+ * the minimum number of bytes in which we can pack the integer
+ */
+ uival = ((uint_t)val >> BUILD_INT_SHIFT) & BUILD_INT_MASK;
+ ival = val;
+ sval = (short)val; /* yes, loss of data intended */
+ cval = (char)val; /* yes, loss of data intended */
+
+ if (val == (int)cval)
+ valsz = 1;
+ else if (val == (int)sval)
+ valsz = 2;
+ else if (uival == BUILD_INT_MASK || uival == 0)
+ valsz = 3;
+ else
+ valsz = 4;
+
+ /*
+ * Prepare the ASN.1 header for the integer
+ */
+ if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
+ return (NULL);
+
+ /*
+ * If we have enough space left, encode the integer
+ */
+ if (*bufsz_p < valsz)
+ return (NULL);
+ else {
+ valp = (uchar_t *)&ival;
+ for (i = 0; i < valsz; i++)
+ p[i] = valp[sizeof (int) - valsz + i];
+
+ *bufsz_p -= valsz;
+
+ LOGASNINT(buf, p + valsz - buf);
+
+ return (p + valsz);
+ }
+}
+/*
+ * Builds an ASN.1 encoded octet string in the buffer. The source string
+ * need not be null-terminated.
+ */
+uchar_t *
+asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
+ size_t slen)
+{
+ uchar_t *p;
+
+ if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
+ return (NULL);
+
+ if (*bufsz_p < slen)
+ return (NULL);
+ else {
+ if (str) {
+ (void) memcpy(p, str, slen);
+ } else {
+ (void) memset(p, 0, slen);
+ }
+
+ *bufsz_p -= slen;
+
+ LOGASNOCTSTR(buf, p + slen - buf);
+
+ return (p + slen);
+ }
+}
+
+/*
+ * Builds an Object Identifier into the buffer according to the OID
+ * packing and encoding rules.
+ */
+uchar_t *
+asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
+ size_t n_subids)
+{
+ oid *objid = oidp;
+ size_t oid_asnlen;
+ oid subid, first_subid;
+ uchar_t subid_len[MAX_SUBIDS_IN_OID];
+ uchar_t *p;
+ int i, ndx;
+
+ /*
+ * Eliminate invalid cases
+ */
+ if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID)
+ return (NULL);
+ if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40))
+ return (NULL);
+
+ /*
+ * The BER encoding rule for the ASN.1 Object Identifier states
+ * that after packing the first two subids into one, each subsequent
+ * component is considered as the next subid. Each subidentifier is
+ * then encoded as a non-negative integer using as few 7-bit blocks
+ * as possible. The blocks are packed in octets with the first bit of
+ * each octet equal to 1, except for the last octet of each subid.
+ */
+ oid_asnlen = 0;
+ for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
+ if (i == 0) {
+ /*
+ * The packing formula for the first two subids
+ * of an OID is given by Z = (X * 40) + Y
+ */
+ subid = objid[0] * 40 + objid[1];
+ first_subid = subid;
+ i++; /* done with both subids 0 and 1 */
+ } else {
+ subid = objid[i];
+ }
+
+ if (subid < (oid) 0x80)
+ subid_len[ndx] = 1;
+ else if (subid < (oid) 0x4000)
+ subid_len[ndx] = 2;
+ else if (subid < (oid) 0x200000)
+ subid_len[ndx] = 3;
+ else if (subid < (oid) 0x10000000)
+ subid_len[ndx] = 4;
+ else {
+ subid_len[ndx] = 5;
+ }
+
+ oid_asnlen += subid_len[ndx];
+ }
+
+ if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
+ return (NULL);
+
+ if (*bufsz_p < oid_asnlen)
+ return (NULL);
+
+ /*
+ * Store the encoded OID
+ */
+ for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
+ if (i == 0) {
+ subid = first_subid;
+ i++;
+ } else {
+ subid = objid[i];
+ }
+
+ switch (subid_len[ndx]) {
+ case 1:
+ *p++ = (uchar_t)subid;
+ break;
+
+ case 2:
+ *p++ = (uchar_t)((subid >> 7) | 0x80);
+ *p++ = (uchar_t)(subid & 0x7f);
+ break;
+
+ case 3:
+ *p++ = (uchar_t)((subid >> 14) | 0x80);
+ *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(subid & 0x7f);
+ break;
+
+ case 4:
+ *p++ = (uchar_t)((subid >> 21) | 0x80);
+ *p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(subid & 0x7f);
+ break;
+
+ case 5:
+ *p++ = (uchar_t)((subid >> 28) | 0x80);
+ *p++ = (uchar_t)(((subid >> 21) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
+ *p++ = (uchar_t)(subid & 0x7f);
+ break;
+ }
+ }
+
+ *bufsz_p -= oid_asnlen;
+
+ LOGASNOID(buf, p - buf);
+
+ return (p);
+}
+/*
+ * Build an ASN_NULL object val into the request packet
+ */
+uchar_t *
+asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
+{
+ uchar_t *p;
+
+ p = asn_build_header(buf, bufsz_p, id, 0);
+
+ LOGASNNULL(buf, p - buf);
+
+ return (p);
+}
+
+
+
+/*
+ * This routine parses a 'SEQUENCE OF' object header from the input
+ * buffer stream. If the identifier tag (made up of class, constructed
+ * type and data type tag) does not match the expected identifier tag,
+ * returns failure.
+ */
+uchar_t *
+asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
+{
+ uchar_t *p;
+ uchar_t id;
+
+ if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
+ return (NULL);
+
+ if (id != exp_id)
+ return (NULL);
+
+ return (p);
+}
+/*
+ * Return the type identifier of the ASN object via 'id'
+ */
+uchar_t *
+asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
+{
+ uchar_t *p;
+ size_t asnobj_len, hdrlen;
+
+ /*
+ * Objects with extension tag type are not supported
+ */
+ if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG)
+ return (NULL);
+
+ /*
+ * Parse the length field of the ASN object in the header
+ */
+ if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
+ return (NULL);
+
+ /*
+ * Check if the rest of the msg packet is big enough for the
+ * full length of the object
+ */
+ hdrlen = p - buf;
+ if (*bufsz_p < (asnobj_len + hdrlen))
+ return (NULL);
+
+ *id = buf[0];
+ *bufsz_p -= hdrlen;
+
+ return (p);
+}
+/*
+ * This routine parses the length of the object as specified in its
+ * header. The 'Indefinite' form of representing length is not supported.
+ */
+uchar_t *
+asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
+{
+ uchar_t *p;
+ int n_length_octets;
+
+ /*
+ * First, check for the short-definite form. Length of
+ * the object is simply the least significant 7-bits of
+ * the first byte.
+ */
+ if ((buf[0] & ASN_LONG_LEN) == 0) {
+ *asnobj_len_p = (size_t)buf[0];
+ return (buf + 1);
+ }
+
+ /*
+ * Then, eliminate the indefinite form. The ASN_LONG_LEN
+ * bit of the first byte will be set and the least significant
+ * 7-bites of that byte will be zeros.
+ */
+ if (buf[0] == (uchar_t)ASN_LONG_LEN)
+ return (NULL);
+
+ /*
+ * Then, eliminate the long-definite case when the number of
+ * follow-up octets is more than what the size var can hold.
+ */
+ n_length_octets = buf[0] & ~ASN_LONG_LEN;
+ if (n_length_octets > sizeof (*asnobj_len_p))
+ return (NULL);
+
+ /*
+ * Finally gather the length
+ */
+ p = buf + 1;
+ *asnobj_len_p = 0;
+ while (n_length_octets--) {
+ *asnobj_len_p <<= 8;
+ *asnobj_len_p |= *p++;
+ }
+
+ return (p);
+}
+/*
+ * Parses an integer out of the input buffer
+ */
+uchar_t *
+asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
+{
+ size_t asnobj_len, hdrlen;
+ uchar_t int_id;
+ uchar_t *p;
+
+ int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
+ if (buf[0] != int_id)
+ return (NULL);
+
+ /*
+ * Read in the length of the object; Note that integers are
+ * "packed" when sent from agent to manager and vice-versa,
+ * so the size of the object could be less than sizeof (int).
+ */
+ if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
+ return (NULL);
+
+ /*
+ * Is there sufficient space left in the packet to read the integer ?
+ */
+ hdrlen = p - buf;
+ if (*bufsz_p < (hdrlen + asnobj_len))
+ return (NULL);
+
+ /*
+ * Update space left in the buffer after the integer is read
+ */
+ *bufsz_p -= (hdrlen + asnobj_len);
+
+ /*
+ * Read in the integer value
+ */
+ *ival = (*p & ASN_BIT8) ? -1 : 0;
+ while (asnobj_len--) {
+ *ival <<= 8;
+ *ival |= *p++;
+ }
+
+ return (p);
+}
+/*
+ * Parses an unsigned integer out of the input buffer
+ */
+uchar_t *
+asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
+{
+ size_t asnobj_len, hdrlen;
+ uchar_t *p;
+
+ if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
+ return (NULL);
+
+ /*
+ * Read in the length of the object. Integers are sent the same
+ * way unsigned integers are sent. Except that, if the MSB was 1
+ * in the unsigned int value, a null-byte is attached to the front.
+ * Otherwise, packing rules are the same as for integer values.
+ */
+ if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
+ return (NULL);
+
+ /*
+ * Is there sufficient space left in the packet to read in the value ?
+ */
+ hdrlen = p - buf;
+ if (*bufsz_p < (hdrlen + asnobj_len))
+ return (NULL);
+
+ /*
+ * Update space left in the buffer after the uint is read
+ */
+ *bufsz_p -= (hdrlen + asnobj_len);
+
+ /*
+ * Read in the unsigned integer (this should never get
+ * initialized to ~0 if it was sent right)
+ */
+ *uival = (*p & ASN_BIT8) ? ~0 : 0;
+ while (asnobj_len--) {
+ *uival <<= 8;
+ *uival |= *p++;
+ }
+
+ return (p);
+}
+/*
+ * Parses a string (ASN_OCTET_STR or ASN_BIT_STR) out of the input buffer.
+ * The memory for the string is allocated inside the routine and must be
+ * freed by the caller when it is no longer needed. If the string type is
+ * ASN_OCTET_STR, the returned string is null-terminated, and the returned
+ * length indicates the strlen value. If the string type is ASN_BIT_STR,
+ * the returned string is not null-terminated, and the returned length
+ * indicates the number of bytes.
+ */
+uchar_t *
+asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
+{
+ uchar_t *p;
+ uchar_t id1, id2;
+ size_t asnobj_len, hdrlen;
+
+ /*
+ * Octet and bit strings are supported
+ */
+ id1 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
+ id2 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR;
+ if ((buf[0] != id1) && (buf[0] != id2))
+ return (NULL);
+
+ /*
+ * Parse out the length of the object and verify source buf sz
+ */
+ if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
+ return (NULL);
+
+ hdrlen = p - buf;
+ if (*bufsz_p < (hdrlen + asnobj_len))
+ return (NULL);
+
+ /*
+ * Allocate for and copy out the string
+ */
+ if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
+ return (NULL);
+
+ (void) memcpy(*str_p, p, asnobj_len);
+
+ /*
+ * Terminate the octet string with a null
+ */
+ if (buf[0] == id1) {
+ (*str_p)[asnobj_len] = 0;
+ }
+
+ /*
+ * Update pointers and return
+ */
+ *slen = asnobj_len;
+ *bufsz_p -= (hdrlen + asnobj_len);
+
+ return (p + asnobj_len);
+}
+/*
+ * Parses an object identifier out of the input packet buffer. Space for
+ * the oid object is allocated within this routine and must be freed by the
+ * caller when no longer needed.
+ */
+uchar_t *
+asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
+{
+ oid **objid_p = oidp;
+ oid *objid;
+ uchar_t *p;
+ size_t hdrlen, asnobj_len;
+ oid subid;
+ int i, ndx;
+ uchar_t exp_id;
+
+ /*
+ * Check id
+ */
+ exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
+ if (msg[0] != exp_id)
+ return (NULL);
+
+ /*
+ * Read object length
+ */
+ if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
+ return (NULL);
+
+ /*
+ * Check space in input message
+ */
+ hdrlen = p - msg;
+ if (*varsz_p < (hdrlen + asnobj_len))
+ return (NULL);
+
+ /*
+ * Since the OID subidentifiers are packed in 7-bit blocks with
+ * MSB set to 1 for all but the last octet, the number of subids
+ * is simply the number of octets with MSB equal to 0, plus 1
+ * (since the first two subids were packed into one subid and have
+ * to be expanded back to two).
+ */
+ *n_subids = 1;
+ for (i = 0; i < asnobj_len; i++) {
+ if ((p[i] & ASN_BIT8) == 0)
+ (*n_subids)++;
+ }
+
+ /*
+ * Now allocate for the oid and parse the OID into it
+ */
+ if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
+ return (NULL);
+
+ ndx = 1; /* start from 1 to allow for unpacking later */
+ subid = 0;
+ for (i = 0; i < asnobj_len; i++) {
+ subid = subid << 7;
+ subid |= (p[i] & ~ASN_BIT8);
+
+ if ((p[i] & ASN_BIT8) == 0) {
+ objid[ndx] = subid;
+ ndx++;
+ subid = 0;
+ }
+ }
+
+ /*
+ * Now unpack the first two subids from the subid at index 1.
+ */
+ if (objid[1] < 40) {
+ objid[0] = 0;
+ } else if (objid[1] < 80) {
+ objid[0] = 1;
+ objid[1] -= 40;
+ } else {
+ objid[0] = 2;
+ objid[1] -= 80;
+ }
+
+ *objid_p = objid;
+ *varsz_p -= (hdrlen + asnobj_len);
+
+ return (msg + hdrlen + asnobj_len);
+}
+/*
+ * Parses the value of an OID object out of the input message buffer.
+ * Only type tags less than ASN_EXT_TAG (0x1f) are supported.
+ */
+uchar_t *
+asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
+{
+ pdu_varlist_t *vp = varlistp;
+ uchar_t *p;
+ size_t n_subids;
+ size_t hdrlen, asnobj_len;
+
+ vp->type = msg[0] & ASN_EXT_TAG;
+ if (vp->type == ASN_EXT_TAG)
+ return (NULL);
+
+ /*
+ * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
+ * and ASN_TIMETICKS types.
+ */
+ switch (msg[0]) {
+ case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER:
+ vp->val.iptr = (int *)calloc(1, sizeof (int));
+ if (vp->val.iptr == NULL)
+ return (NULL);
+
+ if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
+ free(vp->val.iptr);
+ return (NULL);
+ }
+ vp->val_len = sizeof (int);
+ break;
+
+ case ASN_COUNTER:
+ case ASN_TIMETICKS:
+ vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t));
+ if (vp->val.uiptr == NULL)
+ return (NULL);
+
+ if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
+ free(vp->val.uiptr);
+ return (NULL);
+ }
+ vp->val_len = sizeof (uint_t);
+ vp->type = msg[0];
+ break;
+
+ case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR:
+ case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR:
+ p = asn_parse_string(msg, varsz_p, &vp->val.str, &vp->val_len);
+ if (p == NULL)
+ return (NULL);
+ break;
+
+ case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID:
+ p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids);
+ if (p == NULL)
+ return (NULL);
+ vp->val_len = n_subids * sizeof (oid);
+ break;
+
+ case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL:
+ case SNMP_NOSUCHOBJECT:
+ case SNMP_NOSUCHINSTANCE:
+ case SNMP_ENDOFMIBVIEW:
+ default:
+ p = asn_parse_length(msg + 1, &asnobj_len);
+ if (p == NULL)
+ return (NULL);
+
+ hdrlen = p - msg;
+ if (*varsz_p < (hdrlen + asnobj_len))
+ return (NULL);
+
+ vp->type = msg[0];
+ vp->val_len = asnobj_len;
+
+ *varsz_p -= (hdrlen + asnobj_len);
+ p += asnobj_len;
+ break;
+ }
+
+ return (p);
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.h b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.h
new file mode 100644
index 0000000000..22c8169fbb
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.h
@@ -0,0 +1,154 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ASN1_H
+#define _ASN1_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ASN.1 values are encoded as octet strings based on the use of a
+ * Type-Length-Value (TLV) structure. The Type indicates the ASN.1
+ * type, the class of the type, and whether the encoding is primitive
+ * or constructed. The Length indicates the length of the actual value
+ * representation and the Value represents the value as a string
+ * of octets.
+ *
+ * +------------+--------+----------+
+ * | Identifier | Length | Contents |
+ * +------------+--------+----------+
+ *
+ * The encoding of the Identifier field is shown below (for tags less than 31):
+ *
+ * +-------+-----+------------+
+ * | Class | P/C | Tag number |
+ * +-------+-----+------------+
+ * Bit 7 6 5 4 3 2 1 0
+ *
+ * The class field specifies one of four classes, the P/C bit specifies
+ * whether this is a primitive/constructed encoding and the tag number
+ * distinguishes one data type from another within the class.
+ */
+
+/*
+ * Identifier classes
+ */
+#define ASN_UNIVERSAL ((uchar_t)0x00)
+#define ASN_APPLICATION ((uchar_t)0x40)
+#define ASN_CONTEXT ((uchar_t)0x80)
+#define ASN_PRIVATE ((uchar_t)0xc0)
+
+/*
+ * Encoding type
+ */
+#define ASN_PRIMITIVE ((uchar_t)0x00)
+#define ASN_CONSTRUCTOR ((uchar_t)0x20)
+
+/*
+ * Tag numbers for the Universal class of ASN.1 values
+ */
+#define ASN_BOOLEAN ((uchar_t)0x01)
+#define ASN_INTEGER ((uchar_t)0x02)
+#define ASN_BIT_STR ((uchar_t)0x03)
+#define ASN_OCTET_STR ((uchar_t)0x04)
+#define ASN_NULL ((uchar_t)0x05)
+#define ASN_OBJECT_ID ((uchar_t)0x06)
+#define ASN_SEQUENCE ((uchar_t)0x10)
+#define ASN_SET ((uchar_t)0x11)
+
+/*
+ * ASN Extension Tag in the identifier
+ */
+#define ASN_EXT_TAG ((uchar_t)0x1f)
+
+/*
+ * Application class ASN.1 identifiers
+ */
+#define ASN_COUNTER (ASN_APPLICATION | ASN_PRIMITIVE | (uchar_t)0x01)
+#define ASN_TIMETICKS (ASN_APPLICATION | ASN_PRIMITIVE | (uchar_t)0x03)
+
+/*
+ * The Length field in the TLV structure described above is represented
+ * in many ways depending on the value.
+ *
+ * If the length is less than 128, the length field consists of a
+ * single octet beginning with a zero.
+ *
+ * +---+-----------+
+ * | 0 | Length(L) |
+ * +---+-----------+
+ *
+ * If the length is greater than 127, the first octet of the length field
+ * contains a seven-bit integer that specifies the number of additional
+ * length octets and the additional octets specify the actual length.
+ *
+ * <-- one octet --><----- K octets ----->
+ * +---------------+---------------------+
+ * | 1 | K | Length(L) |
+ * +---------------+---------------------+
+ *
+ */
+#define ASN_LONG_LEN ((uchar_t)0x80)
+#define ASN_BIT8 ((uchar_t)0x80)
+
+/*
+ * Some parts of the code assumes a few things -- big-endian ordering,
+ * sizeof int, etc. to simplify things.
+ */
+#define BUILD_INT_SHIFT 23
+#define BUILD_INT_MASK 0x1ff
+
+/*
+ * Exported ASN.1 encoding related interfaces (only exported within
+ * snmplib, we need to do ld versioning to limit the scope of these to
+ * within snmplib).
+ */
+uchar_t *asn_build_sequence(uchar_t *, size_t *, uchar_t, size_t);
+uchar_t *asn_build_header(uchar_t *, size_t *, uchar_t, size_t);
+uchar_t *asn_build_length(uchar_t *, size_t *, size_t);
+uchar_t *asn_build_int(uchar_t *, size_t *, uchar_t, int);
+uchar_t *asn_build_string(uchar_t *, size_t *, uchar_t, uchar_t *, size_t);
+uchar_t *asn_build_objid(uchar_t *, size_t *, uchar_t, void *, size_t);
+uchar_t *asn_build_null(uchar_t *, size_t *, uchar_t);
+
+uchar_t *asn_parse_sequence(uchar_t *, size_t *, uchar_t);
+uchar_t *asn_parse_header(uchar_t *, size_t *, uchar_t *);
+uchar_t *asn_parse_length(uchar_t *, size_t *);
+uchar_t *asn_parse_int(uchar_t *, size_t *, int *);
+uchar_t *asn_parse_uint(uchar_t *, size_t *, uint_t *);
+uchar_t *asn_parse_string(uchar_t *, size_t *, uchar_t **, size_t *);
+uchar_t *asn_parse_objid(uchar_t *, size_t *, void *, size_t *);
+uchar_t *asn_parse_objval(uchar_t *, size_t *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ASN1_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.c b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.c
new file mode 100644
index 0000000000..3a52599a63
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.c
@@ -0,0 +1,616 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef SNMP_DEBUG
+
+/*
+ * Debug routines
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <thread.h>
+#include <synch.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "asn1.h"
+#include "pdu.h"
+#include "snmplib.h"
+#include "debug.h"
+
+/*
+ * Buffer and line limits
+ */
+#define SNMP_DBLOCK_SZ 4096
+#define SNMP_DMAX_LINE 80
+#define SNMP_NCHARS_IN_A_ROW 16
+
+/*
+ * Debug flags
+ */
+#define SNMP_DEBUG_CMD 0x01
+#define SNMP_DEBUG_VAR 0x02
+#define SNMP_DEBUG_PDU 0x04
+#define SNMP_DEBUG_ASN 0x08
+#define SNMP_DEBUG_PKT 0x10
+#define SNMP_DEBUG_IO 0x20
+
+#define SNMP_DEBUG_DEFAULT 0x15 /* cmd, pdu, pkt */
+#define SNMP_DEBUG_EXTENDED 0x35 /* cmd, pdu, pkt, io */
+#define SNMP_DEBUG_ALL 0x3f
+
+/*
+ * Formatting aids
+ */
+#define SNMP_DCMD_INDENT 2
+#define SNMP_DVAR_INDENT 4
+#define SNMP_DPDU_INDENT 6
+#define SNMP_DASN_INDENT 8
+#define SNMP_DPKT_INDENT 10
+#define SNMP_DIO_INDENT 12
+
+#define SNMP_DHDR_PREFIX (const char *)" ___ "
+#define SNMP_DHDR_SUFFIX (const char *)" ___"
+#define SNMP_DTEXT_PREFIX (const char *)"| "
+
+/*
+ * All debug vars are protected by a single lock
+ */
+static mutex_t snmp_dbuf_lock; /* debug lock */
+static uint16_t snmp_debug_flag = SNMP_DEBUG_EXTENDED; /* debug flags */
+static char *snmp_dbuf = NULL; /* the debug buffer */
+static char *snmp_dbuf_curp = NULL; /* current dbuf index */
+static char *snmp_dbuf_tail = NULL; /* current dbuf tail */
+static int snmp_dbuf_sz = 0; /* current dbuf size */
+static int snmp_dbuf_overflow = 0; /* no more memory */
+static char snmp_lbuf[SNMP_DMAX_LINE]; /* scratch space */
+
+/*
+ * Key-to-string
+ */
+typedef struct {
+ int key;
+ char *str;
+} snmp_key_to_str_t;
+
+static snmp_key_to_str_t snmp_cmds[] = {
+ { SNMP_MSG_GET, "SNMP_MSG_GET" },
+ { SNMP_MSG_GETNEXT, "SNMP_MSG_GETNEXT" },
+ { SNMP_MSG_RESPONSE, "SNMP_MSG_RESPONSE" },
+ { SNMP_MSG_SET, "SNMP_MSG_SET" },
+ { SNMP_MSG_TRAP, "SNMP_MSG_TRAP" },
+ { SNMP_MSG_GETBULK, "SNMP_MSG_GETBULK" },
+ { SNMP_MSG_INFORM, "SNMP_MSG_INFORM" },
+ { SNMP_MSG_TRAP2, "SNMP_MSG_TRAP2" },
+ { SNMP_MSG_REPORT, "SNMP_MSG_REPORT" }
+};
+
+static snmp_key_to_str_t snmp_vartypes[] = {
+ { ASN_BOOLEAN, "ASN_BOOLEAN" },
+ { ASN_INTEGER, "ASN_INTEGER" },
+ { ASN_BIT_STR, "ASN_BIT_STR" },
+ { ASN_OCTET_STR, "ASN_OCTET_STR" },
+ { ASN_NULL, "ASN_NULL" },
+ { ASN_OBJECT_ID, "ASN_OBJECT_ID" },
+ { ASN_SEQUENCE, "ASN_SEQUENCE" }
+};
+
+static snmp_key_to_str_t snmp_asnencodings[] = {
+ { SNMP_DASN_SEQUENCE, "ASN SEQUENCE" },
+ { SNMP_DASN_LENGTH, "ASN LENGTH" },
+ { SNMP_DASN_INT, "ASN INT" },
+ { SNMP_DASN_OCTET_STR, "ASN OCTET STR" },
+ { SNMP_DASN_OID, "ASN OBJECT ID" },
+ { SNMP_DASN_NULL, "ASN NULL" }
+};
+
+static char *debug_tags[] = {
+ "SNMP Command Request",
+ "Null Var",
+ "Response Var",
+ "Request PDU",
+ "Response PDU",
+ "Request Packet",
+ "Response Packet",
+ "WRITE",
+ "IOCTL",
+ "READ",
+ "SENDTO",
+ "RECVFROM"
+};
+static const int n_tags = sizeof (debug_tags) / sizeof (char *);
+
+/*
+ * Helpers
+ */
+static char *snmp_cmdstr_lookup(int cmd);
+static char *snmp_vtypestr_lookup(int vtype);
+static char *snmp_asnencoding_lookup(int asnkey);
+static void snmp_get_dumpchars(uchar_t *abuf, uchar_t *p, int nchars);
+static void snmp_log_append(char *bufp);
+static void snmp_dbuf_realloc(void);
+
+void
+snmp_debug_init(void)
+{
+ (void) mutex_init(&snmp_dbuf_lock, USYNC_THREAD, NULL);
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+ snmp_dbuf_realloc();
+ if (snmp_dbuf == NULL)
+ snmp_debug_flag = 0; /* really tragic */
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_cmd(uint_t tag, int cmd, int n_oids, char *oidstr, int row)
+{
+ char *cmdstr;
+ int i;
+
+ if (oidstr == NULL)
+ return;
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_CMD) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ snmp_log_append("\n");
+
+ if (tag < n_tags) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s%s\n",
+ SNMP_DCMD_INDENT, ' ', SNMP_DHDR_PREFIX,
+ debug_tags[tag], SNMP_DHDR_SUFFIX);
+ snmp_log_append(snmp_lbuf);
+ }
+
+ if ((cmdstr = snmp_cmdstr_lookup(cmd)) == NULL) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sCMD=%#x\n",
+ SNMP_DCMD_INDENT, ' ', SNMP_DTEXT_PREFIX, cmd);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s\n",
+ SNMP_DCMD_INDENT, ' ', SNMP_DTEXT_PREFIX, cmdstr);
+ }
+ snmp_log_append(snmp_lbuf);
+
+ for (i = 0; i < n_oids; i++) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s %s.%d\n",
+ SNMP_DCMD_INDENT, ' ', SNMP_DTEXT_PREFIX,
+ oidstr, row);
+ snmp_log_append(snmp_lbuf);
+
+ oidstr += strlen(oidstr) + 1;
+ }
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_var(uint_t tag, pdu_varlist_t *vp)
+{
+ char *vts;
+
+ if (vp == NULL)
+ return;
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_VAR) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ snmp_log_append("\n");
+
+ if (tag < n_tags) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s%s\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DHDR_PREFIX,
+ debug_tags[tag], SNMP_DHDR_SUFFIX);
+ snmp_log_append(snmp_lbuf);
+ }
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%snextvar = %#x\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->nextvar);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sname = %#x\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->name);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sname_len = %u\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->name_len);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sval.ptr = %#x\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->val.str);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sval_len = %u\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->val_len);
+ snmp_log_append(snmp_lbuf);
+
+ if ((vts = snmp_vtypestr_lookup(vp->type)) == NULL) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%stype = %#x\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vp->type);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%stype = %s\n",
+ SNMP_DVAR_INDENT, ' ', SNMP_DTEXT_PREFIX, vts);
+ }
+ snmp_log_append(snmp_lbuf);
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_pdu(uint_t tag, snmp_pdu_t *pdu)
+{
+ char *cmdstr;
+
+ if (pdu == NULL)
+ return;
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_PDU) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ snmp_log_append("\n");
+
+ if (tag < n_tags) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s%s\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DHDR_PREFIX,
+ debug_tags[tag], SNMP_DHDR_SUFFIX);
+ snmp_log_append(snmp_lbuf);
+ }
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sversion = %d\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->version);
+ snmp_log_append(snmp_lbuf);
+
+ if (pdu->community) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%scommunity = %s\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, pdu->community);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%scommunity = %#x\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, pdu->community);
+ }
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%scommunity_len = %u\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->community_len);
+ snmp_log_append(snmp_lbuf);
+
+ if ((cmdstr = snmp_cmdstr_lookup(pdu->command)) == NULL) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%scommand = %#x\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, pdu->command);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%scommand = %s\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, cmdstr);
+ }
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sreqid = %d\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->reqid);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%serrstat = %#x (non-repeaters)\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, pdu->errstat);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%serrindex = %u (max-reps)\n", SNMP_DPDU_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, pdu->errindex);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%svars = %#x\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->vars);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sreq_pkt = %#x\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->req_pkt);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sreq_pktsz = %u\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->req_pktsz);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sreply_pkt = %#x\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->reply_pkt);
+ snmp_log_append(snmp_lbuf);
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sreply_pktsz = %u\n",
+ SNMP_DPDU_INDENT, ' ', SNMP_DTEXT_PREFIX, pdu->reply_pktsz);
+ snmp_log_append(snmp_lbuf);
+
+ snmp_log_append("\n");
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_asn(int key, uchar_t *pkt, size_t pktsz)
+{
+ char *p, *asnstr;
+ int i, len;
+ size_t nrows, nrem;
+
+ if (pkt == NULL)
+ return;
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_ASN) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ if ((asnstr = snmp_asnencoding_lookup(key)) == NULL) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%sASNKEY=%#x\n",
+ SNMP_DASN_INDENT, ' ', SNMP_DTEXT_PREFIX, key);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s\n",
+ SNMP_DASN_INDENT, ' ', SNMP_DTEXT_PREFIX, asnstr);
+ }
+ snmp_log_append(snmp_lbuf);
+
+ nrows = pktsz / 16;
+ for (i = 0; i < nrows; i++) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ SNMP_DASN_INDENT, ' ', SNMP_DTEXT_PREFIX,
+ pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5],
+ pkt[6], pkt[7], pkt[8], pkt[9], pkt[10], pkt[11],
+ pkt[12], pkt[13], pkt[14], pkt[15]);
+
+ pkt += 16;
+ snmp_log_append(snmp_lbuf);
+ }
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s ",
+ SNMP_DASN_INDENT, ' ', SNMP_DTEXT_PREFIX);
+
+ p = snmp_lbuf + SNMP_DASN_INDENT + strlen(SNMP_DTEXT_PREFIX) + 1;
+ len = SNMP_DMAX_LINE - SNMP_DASN_INDENT - strlen(SNMP_DTEXT_PREFIX) - 1;
+
+ nrem = pktsz % 16;
+ for (i = 0; i < nrem; i++) {
+ (void) snprintf(p, len, " %02x", pkt[i]);
+
+ p += 3;
+ len -= 3;
+ }
+ (void) snprintf(p, len, "\n");
+ snmp_log_append(snmp_lbuf);
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_pkt(uint_t tag, uchar_t *pkt, size_t pktsz)
+{
+ uchar_t ascii[SNMP_NCHARS_IN_A_ROW + 1];
+ uchar_t *p = pkt;
+ char *bufp;
+ int nrows, nrem;
+ int i, len;
+
+ if (pkt == NULL)
+ return;
+
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_PKT) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ snmp_log_append("\n");
+
+ if (tag < n_tags) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s%s%s\n",
+ SNMP_DPKT_INDENT, ' ',
+ SNMP_DHDR_PREFIX, debug_tags[tag], SNMP_DHDR_SUFFIX);
+ snmp_log_append(snmp_lbuf);
+ }
+
+ nrows = pktsz / SNMP_NCHARS_IN_A_ROW;
+ nrem = pktsz % SNMP_NCHARS_IN_A_ROW;
+
+ for (i = 0; i < nrows; i++) {
+ snmp_get_dumpchars(ascii, p, SNMP_NCHARS_IN_A_ROW);
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s"
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%s\n",
+ SNMP_DPKT_INDENT, ' ', SNMP_DTEXT_PREFIX,
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15],
+ ascii);
+ p += 16;
+
+ snmp_log_append(snmp_lbuf);
+ }
+
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE, "%*c%s",
+ SNMP_DPKT_INDENT, ' ', SNMP_DTEXT_PREFIX);
+
+ snmp_get_dumpchars(ascii, p, nrem);
+
+ bufp = snmp_lbuf + SNMP_DPKT_INDENT + strlen(SNMP_DTEXT_PREFIX);
+ len = SNMP_DMAX_LINE - SNMP_DPKT_INDENT + strlen(SNMP_DTEXT_PREFIX);
+ for (i = 0; i < 16; i++) {
+ if (i < nrem)
+ (void) snprintf(bufp, len, "%02x ", p[i]);
+ else
+ (void) snprintf(bufp, len, " ");
+
+ bufp += 3;
+ len -= 3;
+ }
+ (void) snprintf(bufp, len, "%s\n", ascii);
+ snmp_log_append(snmp_lbuf);
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+void
+snmp_log_io(uint_t tag, int a1, uint_t a2, uint_t a3)
+{
+ (void) mutex_lock(&snmp_dbuf_lock);
+
+ if ((snmp_debug_flag & SNMP_DEBUG_IO) == 0) {
+ (void) mutex_unlock(&snmp_dbuf_lock);
+ return;
+ }
+
+ snmp_log_append("\n");
+
+ if (tag < n_tags) {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%s%s(%d, %#x, %#x)\n", SNMP_DIO_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, debug_tags[tag], a1, a2, a3);
+ } else {
+ (void) snprintf(snmp_lbuf, SNMP_DMAX_LINE,
+ "%*c%s%#x(%d, %#x, %#x)\n", SNMP_DIO_INDENT, ' ',
+ SNMP_DTEXT_PREFIX, tag, a1, a2, a3);
+ }
+
+ snmp_log_append(snmp_lbuf);
+
+ (void) mutex_unlock(&snmp_dbuf_lock);
+}
+
+static char *
+snmp_cmdstr_lookup(int cmd)
+{
+ int nelem = sizeof (snmp_cmds) / sizeof (snmp_key_to_str_t);
+ int i;
+
+ for (i = 0; i < nelem; i++) {
+ if (snmp_cmds[i].key == cmd)
+ return (snmp_cmds[i].str);
+ }
+
+ return (NULL);
+}
+
+static char *
+snmp_vtypestr_lookup(int vtype)
+{
+ int nelem = sizeof (snmp_vartypes) / sizeof (snmp_key_to_str_t);
+ int i;
+
+ for (i = 0; i < nelem; i++) {
+ if (snmp_vartypes[i].key == vtype)
+ return (snmp_vartypes[i].str);
+ }
+
+ return (NULL);
+}
+
+static char *
+snmp_asnencoding_lookup(int asnkey)
+{
+ int nelem = sizeof (snmp_asnencodings) / sizeof (snmp_key_to_str_t);
+ int i;
+
+ for (i = 0; i < nelem; i++) {
+ if (snmp_asnencodings[i].key == asnkey)
+ return (snmp_asnencodings[i].str);
+ }
+
+ return (NULL);
+}
+
+static void
+snmp_get_dumpchars(uchar_t *abuf, uchar_t *p, int nchars)
+{
+ int i;
+
+ if (nchars > SNMP_NCHARS_IN_A_ROW)
+ nchars = SNMP_NCHARS_IN_A_ROW;
+
+ abuf[nchars] = 0;
+ for (i = 0; i < nchars; i++)
+ abuf[i] = isprint(p[i]) ? p[i] : '.';
+}
+
+static void
+snmp_log_append(char *bufp)
+{
+ int len;
+
+ len = strlen(bufp);
+ if ((snmp_dbuf_curp + len) >= snmp_dbuf_tail)
+ snmp_dbuf_realloc();
+
+ (void) strcpy(snmp_dbuf_curp, bufp);
+
+ snmp_dbuf_curp += len;
+}
+
+static void
+snmp_dbuf_realloc(void)
+{
+ char *p;
+ size_t offset = 0;
+ size_t count;
+
+ count = snmp_dbuf_sz + SNMP_DBLOCK_SZ;
+ if ((p = (char *)calloc(count, 1)) == NULL) {
+ snmp_dbuf_overflow++;
+ snmp_dbuf_curp = snmp_dbuf;
+ return;
+ }
+
+ if (snmp_dbuf) {
+ offset = snmp_dbuf_curp - snmp_dbuf;
+ (void) memcpy(p, snmp_dbuf, snmp_dbuf_sz);
+ free(snmp_dbuf);
+ }
+
+ snmp_dbuf = p;
+ snmp_dbuf_sz += SNMP_DBLOCK_SZ;
+
+ snmp_dbuf_curp = snmp_dbuf + offset;
+ snmp_dbuf_tail = snmp_dbuf + snmp_dbuf_sz;
+}
+
+#endif
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.h b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.h
new file mode 100644
index 0000000000..bc228a20d2
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.h
@@ -0,0 +1,144 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef SNMP_DEBUG
+
+/*
+ * ASN Debugging keys
+ */
+#define SNMP_DASN_SEQUENCE 1
+#define SNMP_DASN_LENGTH 2
+#define SNMP_DASN_INT 3
+#define SNMP_DASN_OCTET_STR 4
+#define SNMP_DASN_OID 5
+#define SNMP_DASN_NULL 6
+
+/*
+ * Debug tags
+ */
+#define TAG_CMD_REQUEST 0
+#define TAG_NULL_VAR 1
+#define TAG_RESPONSE_VAR 2
+#define TAG_REQUEST_PDU 3
+#define TAG_RESPONSE_PDU 4
+#define TAG_REQUEST_PKT 5
+#define TAG_RESPONSE_PKT 6
+#define TAG_WRITE 7
+#define TAG_IOCTL 8
+#define TAG_READ 9
+#define TAG_SENDTO 10
+#define TAG_RECVFROM 11
+
+/*
+ * Debug macros
+ */
+#define LOGINIT() \
+ snmp_debug_init()
+
+#define LOGGET(tag, prefix, row) \
+ snmp_log_cmd(tag, SNMP_MSG_GET, 1, prefix, row)
+
+#define LOGBULK(tag, n_oids, oidstrs, row) \
+ snmp_log_cmd(tag, SNMP_MSG_GETBULK, n_oids, oidstrs, row)
+
+#define LOGNEXT(tag, prefix, row) \
+ snmp_log_cmd(tag, SNMP_MSG_GETNEXT, 1, prefix, row)
+
+#define LOGVAR(tag, vp) \
+ snmp_log_var(tag, vp)
+
+#define LOGPDU(tag, pdu) \
+ snmp_log_pdu(tag, pdu)
+
+#define LOGASNSEQ(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_SEQUENCE, pkt, pktsz)
+
+#define LOGASNLENGTH(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_LENGTH, pkt, pktsz)
+
+#define LOGASNINT(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_INT, pkt, pktsz)
+
+#define LOGASNOCTSTR(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_OCTET_STR, pkt, pktsz)
+
+#define LOGASNOID(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_OID, pkt, pktsz)
+
+#define LOGASNNULL(pkt, pktsz) \
+ snmp_log_asn(SNMP_DASN_NULL, pkt, pktsz)
+
+#define LOGPKT(tag, pkt, sz) \
+ snmp_log_pkt(tag, pkt, sz)
+
+#define LOGIO(tag, a1, a2, a3) \
+ snmp_log_io(tag, (int)a1, (uint_t)a2, (uint_t)a3)
+
+/*
+ * Exported debug interfaces
+ */
+extern void snmp_debug_init(void);
+extern void snmp_log_cmd(uint_t tag, int cmd, int n_oids,
+ char *oidstr, int row);
+extern void snmp_log_var(uint_t tag, pdu_varlist_t *vp);
+extern void snmp_log_pdu(uint_t tag, snmp_pdu_t *pdu);
+extern void snmp_log_asn(int key, uchar_t *pkt, size_t pktsz);
+extern void snmp_log_pkt(uint_t tag, uchar_t *pkt, size_t pktsz);
+extern void snmp_log_io(uint_t tag, int a1, uint_t a2, uint_t a3);
+
+#else /* SNMP_DEBUG */
+
+#define LOGINIT()
+#define LOGGET(tag, prefix, row)
+#define LOGBULK(tag, n_oids, oidstrs, row)
+#define LOGNEXT(tag, prefix, row)
+#define LOGVAR(tag, vp)
+#define LOGPDU(tag, pdu)
+#define LOGASNSEQ(pkt, pktsz)
+#define LOGASNLENGTH(pkt, pktsz)
+#define LOGASNINT(pkt, pktsz)
+#define LOGASNOCTSTR(pkt, pktsz)
+#define LOGASNOID(pkt, pktsz)
+#define LOGASNNULL(pkt, pktsz)
+#define LOGPKT(tag, pkt, sz)
+#define LOGIO(tag, a1, a2, a3)
+
+#endif /* SNMP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEBUG_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c
new file mode 100644
index 0000000000..2485450d54
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c
@@ -0,0 +1,668 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SNMP PDU and packet transport related routines
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "asn1.h"
+#include "pdu.h"
+#include "debug.h"
+
+/*
+ * Static declarations
+ */
+static int snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
+static oid *snmp_oidstr_to_oid(int, char *, int, size_t *);
+static uchar_t *snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
+static uchar_t *snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
+ uchar_t, void *, size_t);
+static uchar_t *snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
+static uchar_t *snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
+
+/*
+ * Allocates and creates a PDU for the specified SNMP command. Currently
+ * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
+ */
+snmp_pdu_t *
+snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
+{
+ snmp_pdu_t *pdu;
+
+ if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
+ (cmd != SNMP_MSG_GETBULK)) {
+ return (NULL);
+ }
+
+ pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
+ if (pdu == NULL)
+ return (NULL);
+
+ if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
+ pdu->version = SNMP_VERSION_1;
+ pdu->errstat = 0;
+ pdu->errindex = 0;
+ } else if (cmd == SNMP_MSG_GETBULK) {
+ pdu->version = SNMP_VERSION_2c;
+ pdu->non_repeaters = 0;
+ pdu->max_repetitions = max_reps ?
+ max_reps : SNMP_DEF_MAX_REPETITIONS;
+ }
+
+ pdu->command = cmd;
+ pdu->reqid = snmp_get_reqid();
+ pdu->community = (uchar_t *)SNMP_DEF_COMMUNITY;
+ pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
+
+ if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
+ free((void *) pdu);
+ return (NULL);
+ }
+
+ pdu->req_pkt = NULL;
+ pdu->req_pktsz = 0;
+ pdu->reply_pkt = NULL;
+ pdu->reply_pktsz = 0;
+
+ return (pdu);
+}
+
+/*
+ * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
+ * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
+ * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
+ * as long as the number of bulk oids are not *too* many, we're safe with
+ * this limit (the typical packet size of a bulk request of 10 vars is
+ * around 250 bytes).
+ */
+int
+snmp_make_packet(snmp_pdu_t *pdu)
+{
+ uchar_t *buf, *p;
+ uchar_t *msg_seq_end;
+ uchar_t id;
+ size_t bufsz = SNMP_DEF_PKTBUF_SZ;
+ size_t seqlen;
+
+ if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
+ return (-1);
+
+ /*
+ * Let's start with the ASN sequence tag. Set the length
+ * to 0 initially and fill it up once the message packetizing
+ * is complete.
+ */
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
+ free((void *) buf);
+ return (-1);
+ }
+ msg_seq_end = p;
+
+ /*
+ * Store the version
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
+ if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
+ free((void *) buf);
+ return (-1);
+ }
+
+ /*
+ * Store the community string
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
+ p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
+ if (p == NULL) {
+ free((void *) buf);
+ return (-1);
+ }
+
+ /*
+ * Build the PDU
+ */
+ if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
+ free((void *) buf);
+ return (-1);
+ }
+
+ /*
+ * Complete the message pkt by updating the message sequence length
+ */
+ seqlen = p - msg_seq_end;
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ (void) asn_build_sequence(buf, NULL, id, seqlen);
+
+ /*
+ * Calculate packet size and return
+ */
+ pdu->req_pkt = buf;
+ pdu->req_pktsz = p - buf;
+
+ return (0);
+}
+
+/*
+ * Makes a PDU out of a reply packet. The reply message is parsed
+ * and if the reqid of the incoming packet does not match the reqid
+ * we're waiting for, an error is returned. The PDU is allocated
+ * inside this routine and must be freed by the caller once it is no
+ * longer needed.
+ */
+snmp_pdu_t *
+snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
+{
+ snmp_pdu_t *reply_pdu;
+ uchar_t *p;
+ size_t msgsz = reply_pktsz;
+ uchar_t exp_id;
+
+ reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
+ if (reply_pdu == NULL)
+ return (NULL);
+
+ /*
+ * Try to parse the ASN sequence out of the beginning of the reply
+ * packet. If we don't find a sequence at the beginning, something's
+ * wrong.
+ */
+ exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (NULL);
+ }
+
+ /*
+ * Now try to parse the version out of the packet
+ */
+ if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (NULL);
+ }
+ if ((reply_pdu->version != SNMP_VERSION_1) &&
+ (reply_pdu->version != SNMP_VERSION_2c)) {
+ snmp_free_pdu(reply_pdu);
+ return (NULL);
+ }
+
+ /*
+ * Parse the community string (space allocated by asn_parse_string)
+ */
+ p = asn_parse_string(p, &msgsz, &reply_pdu->community,
+ &reply_pdu->community_len);
+ if (p == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (NULL);
+ }
+
+ /*
+ * Parse the PDU part of the message
+ */
+ if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (NULL);
+ }
+
+ return (reply_pdu);
+}
+
+
+/*
+ * Convert the OID strings into the standard PDU oid form (sequence of
+ * integer subids) and add them to the PDU's variable list. Note that
+ * this is used only for preparing the request messages (GET, GETNEXT
+ * and GETBULK), so the values of the variables are always null.
+ */
+static int
+snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
+{
+ pdu_varlist_t *vp, *prev;
+ pdu_varlist_t *varblock_p;
+ char *p;
+ int i;
+
+ /*
+ * It's much easier to allocate for all variables in one go,
+ * so we can release it quickly if there's any failure.
+ */
+ varblock_p = (pdu_varlist_t *)calloc(n_oids, sizeof (pdu_varlist_t));
+ if (varblock_p == NULL)
+ return (-1);
+
+ prev = NULL;
+ p = oidstrs;
+ vp = varblock_p;
+ for (i = 0; i < n_oids; i++) {
+ vp->name = snmp_oidstr_to_oid(pdu->command,
+ p, row, &vp->name_len);
+ if (vp->name == NULL) {
+ free((void *) varblock_p);
+ return (-1);
+ }
+ vp->val.str = NULL;
+ vp->val_len = 0;
+ vp->type = ASN_NULL;
+ vp->nextvar = vp + 1;
+
+ LOGVAR(TAG_NULL_VAR, vp);
+
+ prev = vp;
+ p += strlen(p) + 1;
+ vp++;
+ }
+ prev->nextvar = NULL;
+
+ /*
+ * append the varlist to the PDU
+ */
+ if (pdu->vars == NULL)
+ pdu->vars = varblock_p;
+ else {
+ for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
+ ;
+ vp->nextvar = varblock_p;
+ }
+
+ return (0);
+}
+/*
+ * Some assumptions are in place here to eliminate unnecessary complexity.
+ * All OID strings passed are assumed to be in the numeric string form, have
+ * no leading/trailing '.' or spaces. Since PICL plugin is currently the
+ * only customer, this is quite reasonable.
+ */
+static oid *
+snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
+{
+ int i, count;
+ char *p, *q;
+ char *oidstr_dup;
+ oid *objid;
+
+ if ((oidstr == NULL) || (n_subids == NULL))
+ return (NULL);
+
+ for (count = 1, p = oidstr; p; count++, p++) {
+ if ((p = strchr(p, '.')) == NULL)
+ break;
+ }
+
+ /*
+ * Add one more to count for 'row'. Need special processing
+ * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
+ * comment below.
+ */
+ if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
+ (cmd == SNMP_MSG_GETNEXT && row >= 0)) {
+ count++;
+ }
+
+ if ((oidstr_dup = strdup(oidstr)) == NULL)
+ return (NULL);
+
+ objid = (oid *) calloc(count, sizeof (oid));
+ if (objid == NULL) {
+ free((void *) p);
+ return (NULL);
+ }
+
+ p = oidstr_dup;
+ for (i = 0; i < count - 1; i++) {
+ if (q = strchr(p, '.'))
+ *q = 0;
+ objid[i] = (oid) strtoul(p, NULL, 10);
+ p = q + 1;
+ }
+
+ /*
+ * For SNMP_MSG_GET, the leaf subid will simply be the row#.
+ *
+ * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
+ * we pass 'row-1' as the leaf subid, to include the item that
+ * is of interest to us. If the row# is less than or equal to 0,
+ * we will simply ignore it and pass only the prefix part of the
+ * oidstr. For this case, our count would have been 1 less than
+ * usual, and we are yet to save the last subid.
+ *
+ * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
+ * we'll simply ignore it and pass only the prefix part of the
+ * oidstr. For this case, our count would have been 1 less than
+ * usual, and we are yet to save the last subid. If the row#
+ * passed is greater than or equal to 0, we'll simply pass it
+ * verbatim, as the leaf subid.
+ */
+ switch (cmd) {
+ case SNMP_MSG_GET:
+ objid[i] = (oid) row;
+ break;
+
+ case SNMP_MSG_GETBULK:
+ if (row > 0)
+ objid[i] = (oid) (row - 1);
+ else
+ objid[i] = (oid) strtoul(p, NULL, 10);
+ break;
+
+ case SNMP_MSG_GETNEXT:
+ if (row < 0)
+ objid[i] = (oid) strtoul(p, NULL, 10);
+ else
+ objid[i] = (oid) row;
+ break;
+ }
+
+ *n_subids = count;
+
+ free((void *) oidstr_dup);
+
+ return (objid);
+}
+
+/*
+ * Builds the PDU part of the snmp message packet.
+ */
+static uchar_t *
+snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
+{
+ uchar_t *p;
+ uchar_t *pdu_seq_begin, *pdu_seq_end;
+ uchar_t *varlist_seq_begin, *varlist_seq_end;
+ uchar_t id;
+ size_t seqlen;
+ pdu_varlist_t *vp;
+
+ /*
+ * Build ASN sequence for the PDU command (length will be
+ * updated later once the entire command is completely formed)
+ */
+ pdu_seq_begin = buf;
+ p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
+ if (p == NULL)
+ return (NULL);
+ pdu_seq_end = p;
+
+ /*
+ * Build the request id
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
+ if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
+ return (NULL);
+
+ /*
+ * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
+ * (same as error status and error index for other message types)
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
+ if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
+ return (NULL);
+
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
+ if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
+ return (NULL);
+
+ /*
+ * Build ASN sequence for the variables list (update length
+ * after building the varlist)
+ */
+ varlist_seq_begin = p;
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
+ return (NULL);
+ varlist_seq_end = p;
+
+ /*
+ * Build the variables list
+ */
+ for (vp = pdu->vars; vp; vp = vp->nextvar) {
+ p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
+ vp->type, vp->val.str, vp->val_len);
+ if (p == NULL)
+ return (NULL);
+ }
+
+ /*
+ * Now update the varlist sequence length
+ */
+ seqlen = p - varlist_seq_end;
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ (void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
+
+ /*
+ * And finally, update the length for the PDU sequence
+ */
+ seqlen = p - pdu_seq_end;
+ (void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
+ seqlen);
+
+ return (p);
+}
+
+/*
+ * Builds an object variable into the snmp message packet. Although the
+ * code is here to build variables of basic types such as integer, object id
+ * and strings, the only type of variable we ever send via snmp request
+ * messages is the ASN_NULL type.
+ */
+static uchar_t *
+snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
+ uchar_t val_type, void *val, size_t val_len)
+{
+ uchar_t *p, *varseq_end;
+ size_t seqlen;
+ uchar_t id;
+
+ /*
+ * Each variable binding is in turn defined as a 'SEQUENCE of' by
+ * the SNMP PDU format, so we'll prepare the sequence and fill up
+ * the length later. Sigh!
+ */
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
+ return (NULL);
+ varseq_end = p;
+
+ /*
+ * Build the object id
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
+ if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
+ return (NULL);
+
+ /*
+ * Currently we only ever build ASN_NULL vars while sending requests,
+ * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
+ * SNMP_MSG_GETBULK.
+ */
+ id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
+ switch (val_type) {
+ case ASN_INTEGER:
+ p = asn_build_int(p, bufsz_p, id, *((int *)val));
+ if (p == NULL)
+ return (NULL);
+ break;
+
+ case ASN_OBJECT_ID:
+ p = asn_build_objid(p, bufsz_p, id, val,
+ val_len / sizeof (oid));
+ if (p == NULL)
+ return (NULL);
+ break;
+
+ case ASN_OCTET_STR:
+ p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
+ if (p == NULL)
+ return (NULL);
+ break;
+
+ case ASN_NULL:
+ if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
+ return (NULL);
+ break;
+
+ default:
+ return (NULL);
+ }
+
+ /*
+ * Rebuild the variable sequence length
+ */
+ seqlen = p - varseq_end;
+ id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ (void) asn_build_sequence(buf, NULL, id, seqlen);
+
+ return (p);
+}
+
+/*
+ * Parse the PDU portion of the incoming snmp message into the reply_pdu.
+ * Space for all structure members are allocated as needed and must be freed
+ * by the caller when these are no longer needed.
+ */
+static uchar_t *
+snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
+{
+ uchar_t *p;
+ uchar_t id, exp_id;
+ pdu_varlist_t *newvp, *vp = NULL;
+
+ /*
+ * Parse the PDU header out of the message
+ */
+ if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
+ return (NULL);
+ if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
+ return (NULL);
+ reply_pdu->command = (int)id;
+
+ /*
+ * Parse the request id and verify that this is the response
+ * we're expecting.
+ */
+ if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
+ return (NULL);
+ if (reply_pdu->reqid != reqid)
+ return (NULL);
+
+ /*
+ * Parse the error-status and error-index values
+ */
+ if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
+ return (NULL);
+ if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
+ return (NULL);
+
+ /*
+ * Parse the header for the variables list sequence.
+ */
+ exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
+ return (NULL);
+
+ while (((int)*msgsz_p) > 0) {
+ if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
+ return (NULL);
+
+ if (vp == NULL)
+ reply_pdu->vars = newvp;
+ else
+ vp->nextvar = newvp;
+
+ vp = newvp;
+ if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
+ return (NULL);
+
+ LOGVAR(TAG_RESPONSE_VAR, vp);
+ }
+
+ return (p);
+}
+
+/*
+ * Allocate and parse the next variable into the varlist
+ */
+static uchar_t *
+snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
+{
+ uchar_t *p;
+ uchar_t exp_id;
+
+ /*
+ * Parse this variable's sequence
+ */
+ exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
+ if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
+ return (NULL);
+
+ /*
+ * Parse the variable's object identifier
+ */
+ p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
+ if (p == NULL)
+ return (NULL);
+
+ /*
+ * Parse the object's value
+ */
+ if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
+ return (NULL);
+
+ return (p);
+}
+
+void
+snmp_free_pdu(snmp_pdu_t *pdu)
+{
+ pdu_varlist_t *vp, *nxt;
+
+ if (pdu) {
+ if (pdu->community)
+ free((void *) pdu->community);
+
+ for (vp = pdu->vars; vp; vp = nxt) {
+ nxt = vp->nextvar;
+
+ if (vp->name)
+ free((void *) vp->name);
+ if (vp->val.str)
+ free((void *) vp->val.str);
+ free((void *) vp);
+ }
+
+ if (pdu->req_pkt)
+ free((void *) pdu->req_pkt);
+
+ if (pdu->reply_pkt)
+ free((void *) pdu->reply_pkt);
+
+ free((void *) pdu);
+ }
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.h b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.h
new file mode 100644
index 0000000000..e0de368bbc
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.h
@@ -0,0 +1,159 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PDU_H
+#define _PDU_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint_t oid;
+
+/*
+ * SNMP PDU variable list
+ */
+typedef struct pdu_varlist {
+ struct pdu_varlist *nextvar;
+ oid *name;
+ size_t name_len; /* number of subids in the name */
+ union {
+ uint_t *uiptr; /* unused except while parsing */
+ int *iptr;
+ uchar_t *str;
+ oid *objid;
+ } val;
+ size_t val_len; /* in bytes even if val is objid */
+ uchar_t type;
+} pdu_varlist_t;
+
+/*
+ * Essential snmp message/PDU fields
+ */
+typedef struct snmp_pdu {
+ int version;
+ uchar_t *community;
+ size_t community_len;
+ int command;
+ int reqid;
+ int errstat; /* shared with non-repeaters for GETBULK */
+ int errindex; /* shared with max-repetitions for GETBULK */
+ pdu_varlist_t *vars;
+
+ uchar_t *req_pkt; /* not really part of PDU */
+ size_t req_pktsz; /* not really part of PDU */
+ uchar_t *reply_pkt; /* not really part of PDU */
+ size_t reply_pktsz; /* not really part of PDU */
+} snmp_pdu_t;
+#define non_repeaters errstat
+#define max_repetitions errindex
+
+/*
+ * Supported SNMP versions
+ */
+#define SNMP_VERSION_1 0
+#define SNMP_VERSION_2c 1
+
+/*
+ * Community strings for supported PDUs
+ */
+#define SNMP_DEF_COMMUNITY "public"
+#define SNMP_DEF_COMMUNITY_LEN 6
+
+/*
+ * PDU types (not all are supported)
+ */
+#define SNMP_MSG_GET (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x0)
+#define SNMP_MSG_GETNEXT (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x1)
+#define SNMP_MSG_RESPONSE (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x2)
+#define SNMP_MSG_SET (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x3)
+#define SNMP_MSG_TRAP (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x4)
+#define SNMP_MSG_GETBULK (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x5)
+#define SNMP_MSG_INFORM (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x6)
+#define SNMP_MSG_TRAP2 (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x7)
+#define SNMP_MSG_REPORT (ASN_CONTEXT | ASN_CONSTRUCTOR | (uchar_t)0x8)
+
+/*
+ * Exception values (not all are supported)
+ */
+#define SNMP_NOSUCHOBJECT (ASN_CONTEXT | ASN_PRIMITIVE | (uchar_t)0x0)
+#define SNMP_NOSUCHINSTANCE (ASN_CONTEXT | ASN_PRIMITIVE | (uchar_t)0x1)
+#define SNMP_ENDOFMIBVIEW (ASN_CONTEXT | ASN_PRIMITIVE | (uchar_t)0x2)
+
+/*
+ * Error codes (not all are supported)
+ */
+#define SNMP_ERR_NOERROR (0)
+#define SNMP_ERR_TOOBIG (1)
+#define SNMP_ERR_NOSUCHNAME (2)
+#define SNMP_ERR_BADVALUE (3)
+#define SNMP_ERR_READONLY (4)
+#define SNMP_ERR_GENERR (5)
+#define SNMP_ERR_NOACCESS (6)
+#define SNMP_ERR_WRONGTYPE (7)
+#define SNMP_ERR_WRONGLENGTH (8)
+#define SNMP_ERR_WRONGENCODING (9)
+#define SNMP_ERR_WRONGVALUE (10)
+#define SNMP_ERR_NOCREATION (11)
+#define SNMP_ERR_INCONSISTENTVALUE (12)
+#define SNMP_ERR_RESOURCEUNAVAILABLE (13)
+#define SNMP_ERR_COMMITFAILED (14)
+#define SNMP_ERR_UNDOFAILED (15)
+#define SNMP_ERR_AUTHORIZATIONERROR (16)
+#define SNMP_ERR_NOTWRITABLE (17)
+#define SNMP_ERR_INCONSISTENTNAME (18)
+
+/*
+ * Default values
+ */
+#define SNMP_DEF_NON_REPEATERS 0
+#define SNMP_DEF_MAX_REPETITIONS 25
+#define SNMP_DEF_PKTBUF_SZ 2048
+#define SNMP_PKTBUF_BLKSZ 1024
+#define SNMP_MAX_ERR 18
+#define MIN_SUBIDS_IN_OID 2
+#define MAX_SUBIDS_IN_OID 128
+
+/*
+ * Exported interfaces used by other parts of snmplib
+ */
+snmp_pdu_t *snmp_create_pdu(int, int, char *, int, int);
+int snmp_make_packet(snmp_pdu_t *);
+snmp_pdu_t *snmp_parse_reply(int, uchar_t *, size_t);
+void snmp_free_pdu(snmp_pdu_t *);
+
+/*
+ * Imported from elsewhere
+ */
+int snmp_get_reqid(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PDU_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.c b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.c
new file mode 100644
index 0000000000..fe5711868b
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.c
@@ -0,0 +1,1245 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The snmp library helps to prepare the PDUs and communicate with
+ * the snmp agent on the SP side via the ds_snmp driver.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <sys/ds_snmp.h>
+
+#include "libpiclsnmp.h"
+#include "snmplib.h"
+#include "asn1.h"
+#include "pdu.h"
+#include "debug.h"
+
+#pragma init(libpiclsnmp_init) /* need this in .init */
+
+/*
+ * Data from the MIB is fetched based on the hints about object
+ * groups received from (possibly many threads in) the application.
+ * However, the fetched data is kept in a common cache for use across
+ * all threads, so even a GETBULK is issued only when absolutely
+ * necessary.
+ *
+ * Note that locking is not fine grained (there's no locking per row)
+ * since we don't expect too many MT consumers right away.
+ *
+ */
+static mutex_t mibcache_lock;
+static nvlist_t **mibcache = NULL;
+static uint_t n_mibcache_rows = 0;
+
+static mutex_t snmp_reqid_lock;
+static int snmp_reqid = 1;
+
+#ifdef SNMP_DEBUG
+uint_t snmp_nsends = 0;
+uint_t snmp_sentbytes = 0;
+uint_t snmp_nrecvs = 0;
+uint_t snmp_rcvdbytes = 0;
+#endif
+
+#ifdef USE_SOCKETS
+#define SNMP_DEFAULT_PORT 161
+#define SNMP_MAX_RECV_PKTSZ (64 * 1024)
+#endif
+
+/*
+ * Static function declarations
+ */
+static void libpiclsnmp_init(void);
+
+static int lookup_int(char *, int, int *, int);
+static int lookup_str(char *, int, char **, int);
+static int lookup_bitstr(char *, int, uchar_t **, uint_t *, int);
+
+static oidgroup_t *locate_oid_group(struct picl_snmphdl *, char *);
+static int search_oid_in_group(char *, char *, int);
+
+static snmp_pdu_t *fetch_single(struct picl_snmphdl *, char *, int, int *);
+static snmp_pdu_t *fetch_next(struct picl_snmphdl *, char *, int, int *);
+static void fetch_bulk(struct picl_snmphdl *, char *, int, int, int, int *);
+static int fetch_single_str(struct picl_snmphdl *, char *, int,
+ char **, int *);
+static int fetch_single_int(struct picl_snmphdl *, char *, int,
+ int *, int *);
+static int fetch_single_bitstr(struct picl_snmphdl *, char *, int,
+ uchar_t **, uint_t *, int *);
+
+static int snmp_send_request(struct picl_snmphdl *, snmp_pdu_t *, int *);
+static int snmp_recv_reply(struct picl_snmphdl *, snmp_pdu_t *, int *);
+
+static int mibcache_realloc(int);
+static void mibcache_populate(snmp_pdu_t *, int);
+static char *oid_to_oidstr(oid *, size_t);
+
+
+static void
+libpiclsnmp_init(void)
+{
+ (void) mutex_init(&mibcache_lock, USYNC_THREAD, NULL);
+ if (mibcache_realloc(0) < 0)
+ (void) mutex_destroy(&mibcache_lock);
+
+ (void) mutex_init(&snmp_reqid_lock, USYNC_THREAD, NULL);
+
+ LOGINIT();
+}
+
+picl_snmphdl_t
+snmp_init()
+{
+ struct picl_snmphdl *smd;
+#ifdef USE_SOCKETS
+ int sbuf = (1 << 15); /* 16K */
+ int rbuf = (1 << 17); /* 64K */
+ char *snmp_agent_addr;
+#endif
+
+ smd = (struct picl_snmphdl *)calloc(1, sizeof (struct picl_snmphdl));
+ if (smd == NULL)
+ return (NULL);
+
+#ifdef USE_SOCKETS
+ if ((snmp_agent_addr = getenv("SNMP_AGENT_IPADDR")) == NULL)
+ return (NULL);
+
+ if ((smd->fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ return (NULL);
+
+ (void) setsockopt(smd->fd, SOL_SOCKET, SO_SNDBUF, &sbuf, sizeof (int));
+ (void) setsockopt(smd->fd, SOL_SOCKET, SO_RCVBUF, &rbuf, sizeof (int));
+
+ memset(&smd->agent_addr, 0, sizeof (struct sockaddr_in));
+ smd->agent_addr.sin_family = AF_INET;
+ smd->agent_addr.sin_port = htons(SNMP_DEFAULT_PORT);
+ smd->agent_addr.sin_addr.s_addr = inet_addr(snmp_agent_addr);
+#else
+ smd->fd = open(DS_SNMP_DRIVER, O_RDWR);
+ if (smd->fd < 0) {
+ free(smd);
+ return (NULL);
+ }
+#endif
+
+ return ((picl_snmphdl_t)smd);
+}
+
+void
+snmp_fini(picl_snmphdl_t hdl)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+
+ if (smd) {
+ if (smd->fd >= 0) {
+ (void) close(smd->fd);
+ }
+ free(smd);
+ }
+}
+
+int
+snmp_reinit(picl_snmphdl_t hdl, int clr_linkreset)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ nvlist_t *nvl;
+ int i;
+
+ (void) mutex_lock(&mibcache_lock);
+
+ for (i = 0; i < n_mibcache_rows; i++) {
+ if ((nvl = mibcache[i]) != NULL)
+ nvlist_free(nvl);
+ }
+
+ n_mibcache_rows = 0;
+ if (mibcache) {
+ free(mibcache);
+ mibcache = NULL;
+ }
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ if (clr_linkreset) {
+ if (smd == NULL || smd->fd < 0)
+ return (-1);
+ else
+ return (ioctl(smd->fd, DSSNMP_CLRLNKRESET, NULL));
+ }
+
+ return (0);
+}
+
+void
+snmp_register_group(picl_snmphdl_t hdl, char *oidstrs, int n_oids, int is_vol)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ oidgroup_t *oidg;
+ oidgroup_t *curr, *prev;
+ char *p;
+ int i, sz;
+
+ /*
+ * Allocate a new oidgroup_t
+ */
+ oidg = (oidgroup_t *)calloc(1, sizeof (struct oidgroup));
+ if (oidg == NULL)
+ return;
+
+ /*
+ * Determine how much space is required to register this group
+ */
+ sz = 0;
+ p = oidstrs;
+ for (i = 0; i < n_oids; i++) {
+ sz += strlen(p) + 1;
+ p = oidstrs + sz;
+ }
+
+ /*
+ * Create this oid group
+ */
+ if ((p = (char *)malloc(sz)) == NULL) {
+ free((void *) oidg);
+ return;
+ }
+
+ (void) memcpy(p, oidstrs, sz);
+
+ oidg->next = NULL;
+ oidg->oidstrs = p;
+ oidg->n_oids = n_oids;
+ oidg->is_volatile = is_vol;
+
+ /*
+ * Link it to the tail of the list of oid groups
+ */
+ for (prev = NULL, curr = smd->group; curr; curr = curr->next)
+ prev = curr;
+
+ if (prev == NULL)
+ smd->group = oidg;
+ else
+ prev->next = oidg;
+}
+
+/*
+ * snmp_get_int() takes in an OID and returns the integer value
+ * of the object referenced in the passed arg. It returns 0 on
+ * success and -1 on failure.
+ */
+int
+snmp_get_int(picl_snmphdl_t hdl, char *prefix, int row, int *val,
+ int *snmp_syserr)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ oidgroup_t *grp;
+ int ret;
+ int err = 0;
+
+ if (smd == NULL || prefix == NULL || val == NULL)
+ return (-1);
+
+ /*
+ * If this item should not be cached, fetch it directly from
+ * the agent using fetch_single_xxx()
+ */
+ if ((grp = locate_oid_group(smd, prefix)) == NULL) {
+ ret = fetch_single_int(smd, prefix, row, val, &err);
+
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ return (ret);
+ }
+
+ /*
+ * is it in the cache ?
+ */
+ if (lookup_int(prefix, row, val, grp->is_volatile) == 0)
+ return (0);
+
+ /*
+ * fetch it from the agent and populate the cache
+ */
+ fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err);
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ /*
+ * look it up again and return it
+ */
+ if (lookup_int(prefix, row, val, grp->is_volatile) < 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * snmp_get_str() takes in an OID and returns the string value
+ * of the object referenced in the passed arg. Memory for the string
+ * is allocated within snmp_get_str() and is expected to be freed by
+ * the caller when it is no longer needed. The function returns 0
+ * on success and -1 on failure.
+ */
+int
+snmp_get_str(picl_snmphdl_t hdl, char *prefix, int row, char **strp,
+ int *snmp_syserr)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ oidgroup_t *grp;
+ char *val;
+ int ret;
+ int err = 0;
+
+ if (smd == NULL || prefix == NULL || strp == NULL)
+ return (-1);
+
+ /*
+ * Check if this item is cacheable or not. If not, call
+ * fetch_single_* to get it directly from the agent
+ */
+ if ((grp = locate_oid_group(smd, prefix)) == NULL) {
+ ret = fetch_single_str(smd, prefix, row, strp, &err);
+
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ return (ret);
+ }
+
+ /*
+ * See if it's in the cache already
+ */
+ if (lookup_str(prefix, row, &val, grp->is_volatile) == 0) {
+ if ((*strp = strdup(val)) == NULL)
+ return (-1);
+ else
+ return (0);
+ }
+
+ /*
+ * Fetch it from the agent and populate cache
+ */
+ fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err);
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ /*
+ * Retry lookup
+ */
+ if (lookup_str(prefix, row, &val, grp->is_volatile) < 0)
+ return (-1);
+
+
+ if ((*strp = strdup(val)) == NULL)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * snmp_get_bitstr() takes in an OID and returns the bit string value
+ * of the object referenced in the passed args. Memory for the bitstring
+ * is allocated within the function and is expected to be freed by
+ * the caller when it is no longer needed. The function returns 0
+ * on success and -1 on failure.
+ */
+int
+snmp_get_bitstr(picl_snmphdl_t hdl, char *prefix, int row, uchar_t **bitstrp,
+ uint_t *nbytes, int *snmp_syserr)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ oidgroup_t *grp;
+ uchar_t *val;
+ int ret;
+ int err = 0;
+
+ if (smd == NULL || prefix == NULL || bitstrp == NULL || nbytes == NULL)
+ return (-1);
+
+ /*
+ * Check if this item is cacheable or not. If not, call
+ * fetch_single_* to get it directly from the agent
+ */
+ if ((grp = locate_oid_group(smd, prefix)) == NULL) {
+ ret = fetch_single_bitstr(smd, prefix, row, bitstrp,
+ nbytes, &err);
+
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ return (ret);
+ }
+
+ /*
+ * See if it's in the cache already
+ */
+ if (lookup_bitstr(prefix, row, &val, nbytes, grp->is_volatile) == 0) {
+ if ((*bitstrp = (uchar_t *)calloc(*nbytes, 1)) == NULL)
+ return (-1);
+ (void) memcpy(*bitstrp, (const void *)val, *nbytes);
+ return (0);
+ }
+
+ /*
+ * Fetch it from the agent and populate cache
+ */
+ fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err);
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ /*
+ * Retry lookup
+ */
+ if (lookup_bitstr(prefix, row, &val, nbytes, grp->is_volatile) < 0)
+ return (-1);
+
+ if ((*bitstrp = (uchar_t *)calloc(*nbytes, 1)) == NULL)
+ return (-1);
+ (void) memcpy(*bitstrp, (const void *)val, *nbytes);
+
+ return (0);
+}
+
+/*
+ * snmp_get_nextrow() is similar in operation to SNMP_GETNEXT, but
+ * only just. In particular, this is only expected to return the next
+ * valid row number for the same object, not its value. Since we don't
+ * have any other means, we use this to determine the number of rows
+ * in the table (and the valid ones). This function returns 0 on success
+ * and -1 on failure.
+ */
+int
+snmp_get_nextrow(picl_snmphdl_t hdl, char *prefix, int row, int *nextrow,
+ int *snmp_syserr)
+{
+ struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl;
+ snmp_pdu_t *reply_pdu;
+ pdu_varlist_t *vp;
+ char *nxt_oidstr;
+ int err = 0;
+
+ if (smd == NULL || prefix == NULL || nextrow == NULL)
+ return (-1);
+
+ /*
+ * The get_nextrow results should *never* go into any cache,
+ * since these relationships are dynamically discovered each time.
+ */
+ if ((reply_pdu = fetch_next(smd, prefix, row, &err)) == NULL) {
+ if (snmp_syserr)
+ *snmp_syserr = err;
+
+ return (-1);
+ }
+
+ /*
+ * We are not concerned about the "value" of the lexicographically
+ * next object; we only care about the name of that object and
+ * its row number (and whether such an object exists or not).
+ */
+ vp = reply_pdu->vars;
+ if (vp == NULL || vp->name == NULL || vp->type == SNMP_NOSUCHOBJECT ||
+ vp->type == SNMP_NOSUCHINSTANCE || vp->type == SNMP_ENDOFMIBVIEW) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+ if ((nxt_oidstr = oid_to_oidstr(vp->name, vp->name_len - 1)) == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+ if (strcmp(nxt_oidstr, prefix) != 0) {
+ free(nxt_oidstr);
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+
+ /*
+ * Ok, so we've got an oid that's simply the next valid row of the
+ * passed on object, return this row number.
+ */
+ *nextrow = (vp->name)[vp->name_len-1];
+
+ free(nxt_oidstr);
+ snmp_free_pdu(reply_pdu);
+
+ return (0);
+}
+
+/*
+ * Request ids for snmp messages to the agent are sequenced here.
+ */
+int
+snmp_get_reqid(void)
+{
+ int ret;
+
+ (void) mutex_lock(&snmp_reqid_lock);
+
+ ret = snmp_reqid++;
+
+ (void) mutex_unlock(&snmp_reqid_lock);
+
+ return (ret);
+}
+
+static int
+lookup_int(char *prefix, int row, int *valp, int is_vol)
+{
+ int32_t *val_arr;
+ uint_t nelem;
+ struct timeval tv;
+ int elapsed;
+
+ (void) mutex_lock(&mibcache_lock);
+
+ if (row >= n_mibcache_rows) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ if (mibcache[row] == NULL) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ /*
+ * If this is a volatile property, we should be searching
+ * for an integer-timestamp pair
+ */
+ if (is_vol) {
+ if (nvlist_lookup_int32_array(mibcache[row], prefix,
+ &val_arr, &nelem) != 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ if (nelem != 2 || val_arr[1] < 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ if (gettimeofday(&tv, NULL) < 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ elapsed = tv.tv_sec - val_arr[1];
+ if (elapsed < 0 || elapsed > MAX_INCACHE_TIME) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ *valp = (int)val_arr[0];
+ } else {
+ if (nvlist_lookup_int32(mibcache[row], prefix, valp) != 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ }
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ return (0);
+}
+
+static int
+lookup_str(char *prefix, int row, char **valp, int is_vol)
+{
+ char **val_arr;
+ uint_t nelem;
+ struct timeval tv;
+ int elapsed;
+
+ (void) mutex_lock(&mibcache_lock);
+
+ if (row >= n_mibcache_rows) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ if (mibcache[row] == NULL) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ /*
+ * If this is a volatile property, we should be searching
+ * for a string-timestamp pair
+ */
+ if (is_vol) {
+ if (nvlist_lookup_string_array(mibcache[row], prefix,
+ &val_arr, &nelem) != 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ if (nelem != 2 || atoi(val_arr[1]) <= 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ if (gettimeofday(&tv, NULL) < 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ elapsed = tv.tv_sec - atoi(val_arr[1]);
+ if (elapsed < 0 || elapsed > MAX_INCACHE_TIME) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ *valp = val_arr[0];
+ } else {
+ if (nvlist_lookup_string(mibcache[row], prefix, valp) != 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+ }
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ return (0);
+}
+
+static int
+lookup_bitstr(char *prefix, int row, uchar_t **valp, uint_t *nelem, int is_vol)
+{
+ (void) mutex_lock(&mibcache_lock);
+
+ if (row >= n_mibcache_rows) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ if (mibcache[row] == NULL) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ /*
+ * We don't support volatile bit string values yet. The nvlist
+ * functions don't support bitstring arrays like they do charstring
+ * arrays, so we would need to do things in a convoluted way,
+ * probably by attaching the timestamp as part of the byte array
+ * itself. However, the need for volatile bitstrings isn't there
+ * yet, to justify the effort.
+ */
+ if (is_vol) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ if (nvlist_lookup_byte_array(mibcache[row], prefix, valp, nelem) != 0) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ return (0);
+}
+
+static int
+search_oid_in_group(char *prefix, char *oidstrs, int n_oids)
+{
+ char *p;
+ int i;
+
+ p = oidstrs;
+ for (i = 0; i < n_oids; i++) {
+ if (strcmp(p, prefix) == 0)
+ return (0);
+
+ p += strlen(p) + 1;
+ }
+
+ return (-1);
+}
+
+static oidgroup_t *
+locate_oid_group(struct picl_snmphdl *smd, char *prefix)
+{
+ oidgroup_t *grp;
+
+ if (smd == NULL)
+ return (NULL);
+
+ if (smd->group == NULL)
+ return (NULL);
+
+ for (grp = smd->group; grp; grp = grp->next) {
+ if (search_oid_in_group(prefix, grp->oidstrs,
+ grp->n_oids) == 0) {
+ return (grp);
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+fetch_single_int(struct picl_snmphdl *smd, char *prefix, int row, int *ival,
+ int *snmp_syserr)
+{
+ snmp_pdu_t *reply_pdu;
+ pdu_varlist_t *vp;
+
+ if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL)
+ return (-1);
+
+ /*
+ * Note that we don't make any distinction between unsigned int
+ * value and signed int value at this point, since we provide
+ * only snmp_get_int() at the higher level. While it is possible
+ * to provide an entirely separate interface such as snmp_get_uint(),
+ * that's quite unnecessary, because we don't do any interpretation
+ * of the received value. Besides, the sizes of int and uint are
+ * the same and the sizes of all pointers are the same (so val.iptr
+ * would be the same as val.uiptr in pdu_varlist_t). If/when we
+ * violate any of these assumptions, it will be time to add
+ * snmp_get_uint().
+ */
+ vp = reply_pdu->vars;
+ if (vp == NULL || vp->val.iptr == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+
+ *ival = *(vp->val.iptr);
+
+ snmp_free_pdu(reply_pdu);
+
+ return (0);
+}
+
+static int
+fetch_single_str(struct picl_snmphdl *smd, char *prefix, int row, char **valp,
+ int *snmp_syserr)
+{
+ snmp_pdu_t *reply_pdu;
+ pdu_varlist_t *vp;
+
+ if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL)
+ return (-1);
+
+ vp = reply_pdu->vars;
+ if (vp == NULL || vp->val.str == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+
+ *valp = strdup((const char *)(vp->val.str));
+
+ snmp_free_pdu(reply_pdu);
+
+ return (0);
+}
+
+static int
+fetch_single_bitstr(struct picl_snmphdl *smd, char *prefix, int row,
+ uchar_t **valp, uint_t *nelem, int *snmp_syserr)
+{
+ snmp_pdu_t *reply_pdu;
+ pdu_varlist_t *vp;
+
+ if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL)
+ return (-1);
+
+ vp = reply_pdu->vars;
+ if (vp == NULL || vp->val.str == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+
+ if ((*valp = (uchar_t *)calloc(vp->val_len, 1)) == NULL) {
+ snmp_free_pdu(reply_pdu);
+ return (-1);
+ }
+
+ *nelem = vp->val_len;
+ (void) memcpy(*valp, (const void *)(vp->val.str),
+ (size_t)(vp->val_len));
+
+ snmp_free_pdu(reply_pdu);
+
+ return (0);
+}
+
+static snmp_pdu_t *
+fetch_single(struct picl_snmphdl *smd, char *prefix, int row, int *snmp_syserr)
+{
+ snmp_pdu_t *pdu, *reply_pdu;
+
+ LOGGET(TAG_CMD_REQUEST, prefix, row);
+
+ if ((pdu = snmp_create_pdu(SNMP_MSG_GET, 0, prefix, 1, row)) == NULL)
+ return (NULL);
+
+ LOGPDU(TAG_REQUEST_PDU, pdu);
+
+ if (snmp_make_packet(pdu) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz);
+
+ if (snmp_send_request(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz);
+
+ reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt,
+ pdu->reply_pktsz);
+
+ LOGPDU(TAG_RESPONSE_PDU, reply_pdu);
+
+ snmp_free_pdu(pdu);
+
+ return (reply_pdu);
+}
+
+static void
+fetch_bulk(struct picl_snmphdl *smd, char *oidstrs, int n_oids,
+ int row, int is_vol, int *snmp_syserr)
+{
+ snmp_pdu_t *pdu, *reply_pdu;
+ int max_reps;
+
+ LOGBULK(TAG_CMD_REQUEST, n_oids, oidstrs, row);
+
+ /*
+ * If we're fetching volatile properties using BULKGET, don't
+ * venture to get multiple rows (passing max_reps=0 will make
+ * snmp_create_pdu() fetch SNMP_DEF_MAX_REPETITIONS rows)
+ */
+ max_reps = is_vol ? 1 : 0;
+
+ pdu = snmp_create_pdu(SNMP_MSG_GETBULK, max_reps, oidstrs, n_oids, row);
+ if (pdu == NULL)
+ return;
+
+ LOGPDU(TAG_REQUEST_PDU, pdu);
+
+ /*
+ * Make an ASN.1 encoded packet from the PDU information
+ */
+ if (snmp_make_packet(pdu) < 0) {
+ snmp_free_pdu(pdu);
+ return;
+ }
+
+ LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz);
+
+ /*
+ * Send the request packet to the agent
+ */
+ if (snmp_send_request(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return;
+ }
+
+ /*
+ * Receive response from the agent into the reply packet buffer
+ * in the request PDU
+ */
+ if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return;
+ }
+
+ LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz);
+
+ /*
+ * Parse the reply, validate the response and create a
+ * reply-PDU out of the information. Populate the mibcache
+ * with the received values.
+ */
+ reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt,
+ pdu->reply_pktsz);
+ if (reply_pdu) {
+ LOGPDU(TAG_RESPONSE_PDU, reply_pdu);
+
+ if (reply_pdu->errstat == SNMP_ERR_NOERROR)
+ mibcache_populate(reply_pdu, is_vol);
+
+ snmp_free_pdu(reply_pdu);
+ }
+
+ snmp_free_pdu(pdu);
+}
+
+static snmp_pdu_t *
+fetch_next(struct picl_snmphdl *smd, char *prefix, int row, int *snmp_syserr)
+{
+ snmp_pdu_t *pdu, *reply_pdu;
+
+ LOGNEXT(TAG_CMD_REQUEST, prefix, row);
+
+ pdu = snmp_create_pdu(SNMP_MSG_GETNEXT, 0, prefix, 1, row);
+ if (pdu == NULL)
+ return (NULL);
+
+ LOGPDU(TAG_REQUEST_PDU, pdu);
+
+ if (snmp_make_packet(pdu) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz);
+
+ if (snmp_send_request(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) {
+ snmp_free_pdu(pdu);
+ return (NULL);
+ }
+
+ LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz);
+
+ reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt,
+ pdu->reply_pktsz);
+
+ LOGPDU(TAG_RESPONSE_PDU, reply_pdu);
+
+ snmp_free_pdu(pdu);
+
+ return (reply_pdu);
+}
+
+static int
+snmp_send_request(struct picl_snmphdl *smd, snmp_pdu_t *pdu, int *snmp_syserr)
+{
+ extern int errno;
+#ifdef USE_SOCKETS
+ int ret;
+#endif
+
+ if (smd->fd < 0)
+ return (-1);
+
+ if (pdu == NULL || pdu->req_pkt == NULL)
+ return (-1);
+
+#ifdef USE_SOCKETS
+ ret = -1;
+ while (ret < 0) {
+ LOGIO(TAG_SENDTO, smd->fd, pdu->req_pkt, pdu->req_pktsz);
+
+ ret = sendto(smd->fd, pdu->req_pkt, pdu->req_pktsz, 0,
+ (struct sockaddr *)&smd->agent_addr,
+ sizeof (struct sockaddr));
+ if (ret < 0 && errno != EINTR) {
+ return (-1);
+ }
+ }
+#else
+ LOGIO(TAG_WRITE, smd->fd, pdu->req_pkt, pdu->req_pktsz);
+
+ if (write(smd->fd, pdu->req_pkt, pdu->req_pktsz) < 0) {
+ if (snmp_syserr)
+ *snmp_syserr = errno;
+ return (-1);
+ }
+#endif
+
+#ifdef SNMP_DEBUG
+ snmp_nsends++;
+ snmp_sentbytes += pdu->req_pktsz;
+#endif
+
+ return (0);
+}
+
+static int
+snmp_recv_reply(struct picl_snmphdl *smd, snmp_pdu_t *pdu, int *snmp_syserr)
+{
+ struct dssnmp_info snmp_info;
+ size_t pktsz;
+ uchar_t *pkt;
+ extern int errno;
+#ifdef USE_SOCKETS
+ struct sockaddr_in from;
+ int fromlen;
+ ssize_t msgsz;
+#endif
+
+ if (smd->fd < 0 || pdu == NULL)
+ return (-1);
+
+#ifdef USE_SOCKETS
+ if ((pkt = (uchar_t *)calloc(1, SNMP_MAX_RECV_PKTSZ)) == NULL)
+ return (-1);
+
+ fromlen = sizeof (struct sockaddr_in);
+
+ LOGIO(TAG_RECVFROM, smd->fd, pkt, SNMP_MAX_RECV_PKTSZ);
+
+ msgsz = recvfrom(smd->fd, pkt, SNMP_MAX_RECV_PKTSZ, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (msgsz < 0 || msgsz >= SNMP_MAX_RECV_PKTSZ) {
+ free(pkt);
+ return (-1);
+ }
+
+ pktsz = (size_t)msgsz;
+#else
+ LOGIO(TAG_IOCTL, smd->fd, DSSNMP_GETINFO, &snmp_info);
+
+ /*
+ * The ioctl will block until we have snmp data available
+ */
+ if (ioctl(smd->fd, DSSNMP_GETINFO, &snmp_info) < 0) {
+ if (snmp_syserr)
+ *snmp_syserr = errno;
+ return (-1);
+ }
+
+ pktsz = snmp_info.size;
+ if ((pkt = (uchar_t *)calloc(1, pktsz)) == NULL)
+ return (-1);
+
+ LOGIO(TAG_READ, smd->fd, pkt, pktsz);
+
+ if (read(smd->fd, pkt, pktsz) < 0) {
+ free(pkt);
+ if (snmp_syserr)
+ *snmp_syserr = errno;
+ return (-1);
+ }
+#endif
+
+ pdu->reply_pkt = pkt;
+ pdu->reply_pktsz = pktsz;
+
+#ifdef SNMP_DEBUG
+ snmp_nrecvs++;
+ snmp_rcvdbytes += pktsz;
+#endif
+
+ return (0);
+}
+
+static int
+mibcache_realloc(int hint)
+{
+ uint_t count = (uint_t)hint;
+ nvlist_t **p;
+
+ if (hint < 0)
+ return (-1);
+
+ (void) mutex_lock(&mibcache_lock);
+
+ if (hint < n_mibcache_rows) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (0);
+ }
+
+ count = ((count >> MIBCACHE_BLK_SHIFT) + 1) << MIBCACHE_BLK_SHIFT;
+
+ p = (nvlist_t **)calloc(count, sizeof (nvlist_t *));
+ if (p == NULL) {
+ (void) mutex_unlock(&mibcache_lock);
+ return (-1);
+ }
+
+ if (mibcache) {
+ (void) memcpy((void *) p, (void *) mibcache,
+ n_mibcache_rows * sizeof (nvlist_t *));
+ free((void *) mibcache);
+ }
+
+ mibcache = p;
+ n_mibcache_rows = count;
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ return (0);
+}
+
+
+/*
+ * Scan each variable in the returned PDU's bindings and populate
+ * the cache appropriately
+ */
+static void
+mibcache_populate(snmp_pdu_t *pdu, int is_vol)
+{
+ pdu_varlist_t *vp;
+ int row, ret;
+ char *oidstr;
+ struct timeval tv;
+ int tod; /* in secs */
+ char tod_str[MAX_INT_LEN];
+ int ival_arr[2];
+ char *sval_arr[2];
+
+ /*
+ * If we're populating volatile properties, we also store a
+ * timestamp with each property value. When we lookup, we
+ * check the current time against this timestamp to determine
+ * if we need to refetch the value or not (refetch if it has
+ * been in for far too long).
+ */
+ if (is_vol) {
+ if (gettimeofday(&tv, NULL) < 0)
+ tod = -1;
+ else
+ tod = (int)tv.tv_sec;
+
+ tod_str[0] = 0;
+ (void) snprintf(tod_str, MAX_INT_LEN, "%d", tod);
+
+ ival_arr[1] = tod;
+ sval_arr[1] = (char *)tod_str;
+ }
+
+ for (vp = pdu->vars; vp; vp = vp->nextvar) {
+ if (vp->type != ASN_INTEGER && vp->type != ASN_OCTET_STR &&
+ vp->type != ASN_BIT_STR) {
+ continue;
+ }
+
+ if (vp->name == NULL || vp->val.str == NULL)
+ continue;
+
+ row = (vp->name)[vp->name_len-1];
+
+ (void) mutex_lock(&mibcache_lock);
+
+ if (row >= n_mibcache_rows) {
+ (void) mutex_unlock(&mibcache_lock);
+ if (mibcache_realloc(row) < 0)
+ continue;
+ (void) mutex_lock(&mibcache_lock);
+ }
+ ret = 0;
+ if (mibcache[row] == NULL)
+ ret = nvlist_alloc(&mibcache[row], NV_UNIQUE_NAME, 0);
+
+ (void) mutex_unlock(&mibcache_lock);
+
+ if (ret != 0)
+ continue;
+
+ /*
+ * Convert the standard OID form into an oid string that
+ * we can use as the key to lookup. Since we only search
+ * by the prefix (mibcache is really an array of nvlist_t
+ * pointers), ignore the leaf subid.
+ */
+ oidstr = oid_to_oidstr(vp->name, vp->name_len - 1);
+ if (oidstr == NULL)
+ continue;
+
+ (void) mutex_lock(&mibcache_lock);
+
+ if (vp->type == ASN_INTEGER) {
+ if (is_vol) {
+ ival_arr[0] = *(vp->val.iptr);
+ (void) nvlist_add_int32_array(mibcache[row],
+ oidstr, ival_arr, 2);
+ } else {
+ nvlist_add_int32(mibcache[row],
+ oidstr, *(vp->val.iptr));
+ }
+
+ } else if (vp->type == ASN_OCTET_STR) {
+ if (is_vol) {
+ sval_arr[0] = (char *)vp->val.str;
+ (void) nvlist_add_string_array(mibcache[row],
+ oidstr, sval_arr, 2);
+ } else {
+ (void) nvlist_add_string(mibcache[row],
+ oidstr, (const char *)(vp->val.str));
+ }
+ } else if (vp->type == ASN_BIT_STR) {
+ /*
+ * We don't support yet bit string objects that are
+ * volatile values.
+ */
+ if (!is_vol) {
+ (void) nvlist_add_byte_array(mibcache[row],
+ oidstr, (uchar_t *)(vp->val.str),
+ (uint_t)vp->val_len);
+ }
+ }
+ (void) mutex_unlock(&mibcache_lock);
+
+ free(oidstr);
+ }
+}
+
+static char *
+oid_to_oidstr(oid *objid, size_t n_subids)
+{
+ char *oidstr;
+ char subid_str[MAX_INT_LEN];
+ int i, isize;
+
+ /*
+ * ugly, but for now this will have to do.
+ */
+ oidstr = (char *)calloc(1, MAX_INT_LEN * n_subids);
+
+ for (i = 0; i < n_subids; i++) {
+ (void) memset(subid_str, 0, MAX_INT_LEN);
+ isize = snprintf(subid_str, MAX_INT_LEN, "%d", objid[i]);
+ if (isize >= MAX_INT_LEN)
+ return (NULL);
+
+ (void) strcat(oidstr, subid_str);
+ if (i < (n_subids - 1))
+ (void) strcat(oidstr, ".");
+ }
+
+ return (oidstr);
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.h b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.h
new file mode 100644
index 0000000000..339104bd2e
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.h
@@ -0,0 +1,76 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SNMPLIB_H
+#define _SNMPLIB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef USE_SOCKETS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+/*
+ * Groups of OIDs are registered with the picl snmp library to provide
+ * the library with a hint as to the set of OIDs to do GETBULK requests
+ */
+typedef struct oidgroup {
+ struct oidgroup *next;
+ char *oidstrs;
+ int n_oids;
+ int is_volatile;
+} oidgroup_t;
+
+/*
+ * Private (opaque to clients) handle to manage per-client snmp data
+ */
+struct picl_snmphdl {
+ oidgroup_t *group;
+#ifdef USE_SOCKETS
+ struct sockaddr_in agent_addr;
+#endif
+ int fd;
+};
+
+#define MIBCACHE_BLK_SZ 256
+#define MIBCACHE_BLK_SHIFT 8
+#define MAX_INCACHE_TIME 300 /* in secs */
+#define MAX_INT_LEN 16 /* #chars to print */
+
+#define DS_SNMP_DRIVER "/devices/pseudo/ds_snmp@0:ds_snmp"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNMPLIB_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c b/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c
index d9e52a293f..5c8af51d2a 100644
--- a/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c
+++ b/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -40,74 +40,54 @@
#include <errno.h>
#define MDESC_PATH "/devices/pseudo/mdesc@0:mdesc"
-#define SIZE 8192
static void mdesc_free(void *bufp, size_t size);
-uint8_t *md_bufp;
+uint64_t *md_bufp;
md_t *
mdesc_devinit(void)
{
- int fh;
- int res;
- int size;
- int offset;
+ int fd;
md_t *mdp;
+ size_t size;
+ /*
+ * We haven't finished using the previous MD/PRI info.
+ */
if (md_bufp != NULL)
return (NULL);
- fh = open(MDESC_PATH, O_RDONLY, 0);
- if (fh < 0) {
- return (NULL);
- }
-
- size = SIZE; /* initial size */
- offset = 0;
-
- md_bufp = malloc(size);
- if (NULL == md_bufp) {
- return (NULL);
- }
-
- /* OK read until we get a EOF */
-
do {
- int len;
-
- len = size - offset;
-
- while (len < SIZE) {
- size += SIZE;
- md_bufp = realloc(md_bufp, size);
- if (NULL == md_bufp)
- return (NULL);
- len = size - offset;
+ if ((fd = open(MDESC_PATH, O_RDONLY, 0)) < 0)
+ break;
+
+ if (ioctl(fd, MDESCIOCGSZ, &size) < 0)
+ break;
+ if ((md_bufp = (uint64_t *)malloc(size)) == NULL) {
+ (void) close(fd);
+ break;
}
- do {
- res = read(fh, md_bufp + offset, len);
- } while ((res < 0) && (errno == EAGAIN));
-
- if (res < 0) {
+ /*
+ * A partial read is as bad as a failed read.
+ */
+ if (read(fd, md_bufp, size) != size) {
free(md_bufp);
- return (NULL);
+ md_bufp = NULL;
}
- offset += res;
- } while (res > 0);
-
- (void) close(fh);
-
- md_bufp = realloc(md_bufp, offset);
- if (NULL == md_bufp)
- return (NULL);
+ (void) close(fd);
+ /*LINTED: E_CONSTANT_CONDITION */
+ } while (0);
- mdp = md_init_intern((uint64_t *)md_bufp, malloc, mdesc_free);
- if (NULL == mdp) {
- free(md_bufp);
- return (NULL);
- }
+ if (md_bufp) {
+ mdp = md_init_intern(md_bufp, malloc, mdesc_free);
+ if (mdp == NULL) {
+ free(md_bufp);
+ md_bufp = NULL;
+ }
+ } else
+ mdp = NULL;
return (mdp);
}
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/Makefile b/usr/src/cmd/picl/plugins/sun4v/pri/Makefile
new file mode 100644
index 0000000000..afec140c72
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/Makefile
@@ -0,0 +1,120 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/picl/plugins/sun4v/pri/Makefile
+#
+LIBRARY= libpriplugin.a
+VERS= .1
+
+OBJS_DIR= pics
+
+OBJECTS= priplugin.o init.o \
+ mem_prop_update.o io_dev_label.o \
+ mdesc_findname.o mdesc_findnodeprop.o \
+ mdesc_fini.o mdesc_getpropstr.o \
+ mdesc_getpropval.o mdesc_init_intern.o \
+ mdesc_nodecount.o mdesc_rootnode.o \
+ mdesc_scandag.o mdesc_getpropdata.o
+
+# include library definitions
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/Makefile.psm
+
+include $(SRC)/cmd/picl/plugins/Makefile.com
+
+SRCS= $(OBJECTS:%.o=%.c)
+
+LINT_SRC= ./priplugin.c ./init.c \
+ ./mem_prop_update.c io_dev_label.c \
+ $(SRC)/common/mdesc/mdesc_findname.c \
+ $(SRC)/common/mdesc/mdesc_findnodeprop.c \
+ $(SRC)/common/mdesc/mdesc_fini.c \
+ $(SRC)/common/mdesc/mdesc_getpropdata.c \
+ $(SRC)/common/mdesc/mdesc_getpropstr.c \
+ $(SRC)/common/mdesc/mdesc_getpropval.c \
+ $(SRC)/common/mdesc/mdesc_init_intern.c \
+ $(SRC)/common/mdesc/mdesc_nodecount.c \
+ $(SRC)/common/mdesc/mdesc_rootnode.c \
+ $(SRC)/common/mdesc/mdesc_scandag.c
+
+$(OBJS_DIR)/%.o: $(SRC)/common/mdesc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+LIBS = $(DYNLIB)
+
+ROOT_PLATFORM = $(USR_PLAT_DIR)/sun4v
+DYNFLAGS_PLAT = /usr/platform/\$$PLATFORM/lib/picl/plugins
+DYNFLAGS_SUN4V = /usr/platform/sun4v/lib/picl/plugins
+DYNFLAGS_COM = /usr/lib/picl/plugins
+
+ROOTLIBDIR = $(ROOT_PLAT_PLUGINDIR)
+
+CLEANFILES = $(LINTOUT) $(LINTLIB)
+
+CPPFLAGS += -I$(SRC)/common/mdesc
+CPPFLAGS += -I$(SRC)/uts/common/sys
+CPPFLAGS += -I$(SRC)/lib/libpri/common
+CPPFLAGS += -D_REENTRANT
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -L$(SRC)/lib/libpicl/$(MACH) -L$(SRC)/lib/libpicltree/$(MACH)
+LDLIBS += -L$(ROOT)/usr/lib/picl/plugins -L$(ROOT)/usr/lib/sparcv9
+LDLIBS += -L$(ROOT)/usr/lib/libpri
+LDLIBS += -L$(ROOT_PLATFORM)/lib -L$(ROOT_PLATFORM)/lib/picl/plugins
+
+LDLIBS += -lc -lpicl -lpicltree -lpicldevtree -lpri
+
+# No interfaces from libsnmpplugin.so directly used here, but we need the
+# snmp plugin to load and init before libpriplugin.so.
+#
+LDLIBS += -lsnmpplugin
+
+#DYNFLAGS += -R$(DYNFLAGS_COM)
+$(SPARC_BLD)LDLIBS += -R$(DYNFLAGS_PLAT) \
+ -R$(DYNFLAGS_SUN4V)
+LDLIBS += -R$(DYNFLAGS_COM)
+
+LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN -v
+
+.KEEP_STATE:
+
+all: $(LIBS) $(LIBLINKS)
+
+install: all $(ROOTLIBDIR) $(ROOTLIBS) $(ROOTLINKS)
+
+$(LIBLINKS): FRC
+ $(RM) $(LIBLINKS); $(SYMLINK) $(DYNLIB) $(LIBLINKS)
+
+# include library targets
+include $(SRC)/cmd/picl/plugins/Makefile.targ
+include $(SRC)/lib/Makefile.targ
+
+lint :
+ $(LINT.c) $(LINT_SRC)
+
+FRC:
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/init.c b/usr/src/cmd/picl/plugins/sun4v/pri/init.c
new file mode 100644
index 0000000000..eba606b744
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/init.c
@@ -0,0 +1,105 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mdesc.h>
+#include <pri.h>
+#include "priplugin.h"
+
+static void pri_free(void *bufp, size_t size);
+uint64_t *md_bufp;
+
+md_t *
+pri_devinit(void)
+{
+ md_t *mdp;
+ uint64_t tok;
+
+ md_bufp = NULL;
+ tok = 0;
+
+ if (pri_init() != -1) {
+ if (pri_get(PRI_GET, &tok, &md_bufp, malloc, pri_free) ==
+ (ssize_t)-1) {
+ pri_debug(LOG_NOTICE, "pri_devinit: can'r read from "
+ "the PRI: %d\n", errno);
+ }
+ if (md_bufp == NULL) {
+ pri_debug(LOG_NOTICE, "pri_devinit: pri_get returned"
+ "NULL buffer!\n");
+ }
+ } else {
+ pri_debug(LOG_NOTICE, "pri_devinit: pri_init failed!\n");
+ }
+ pri_fini();
+
+ pri_debug(LOG_NOTICE, "pri_devinit: done reading PRI\n");
+
+ /*
+ * The PRI and the MD use the same data format so they can be
+ * parsed by the same functions.
+ */
+ if (md_bufp) {
+ mdp = md_init_intern(md_bufp, malloc, pri_free);
+ if (mdp == NULL) {
+ pri_debug(LOG_NOTICE, "pri_devinit: md_init_intern "
+ "failed\n");
+ free(md_bufp);
+ md_bufp = NULL;
+ } else {
+ pri_debug(LOG_NOTICE, "pri_devinit: mdi_init_intern "
+ "completed successfully\n");
+ }
+ } else
+ mdp = NULL;
+
+ pri_debug(LOG_NOTICE, "pri_devinit: returning\n");
+
+ return (mdp);
+}
+
+/*ARGSUSED*/
+static void
+pri_free(void *bufp, size_t size)
+{
+ if (bufp)
+ free(bufp);
+}
+
+void
+pri_devfini(md_t *mdp)
+{
+ if (mdp)
+ (void) md_fini(mdp);
+
+ if (md_bufp)
+ free(md_bufp);
+ md_bufp = NULL;
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c b/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c
new file mode 100644
index 0000000000..87eba25068
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c
@@ -0,0 +1,305 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "priplugin.h"
+
+/*
+ * These 3 variable are defined and set in mdescplugin.c
+ */
+extern picl_nodehdl_t root_node;
+extern md_t *mdp;
+extern mde_cookie_t rootnode;
+
+static int
+find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
+ const char *pval, picl_nodehdl_t *nodeh);
+static int
+compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
+ const char *pval);
+
+/*
+ * Gather IO device nodes from the PRI and use the info to
+ * find the corresponding nodes in PICL's device tree, insert
+ * a Label into the devtree containing the "nac" from the PRI,
+ * and add a reference property to the corresponding fru tree node.
+ */
+void
+io_dev_addlabel(void)
+{
+ int status, substatus, i, node_count, component_count, busaddr_match;
+ int type_size, nac_size;
+ picl_nodehdl_t platnode, tpn;
+ char busaddr[PICL_PROPNAMELEN_MAX], *p, *q;
+ mde_cookie_t *components;
+ char *type, *nac, *path, *saved_path;
+
+ /*
+ * Find and remember the roots of the /frutree and /platform trees.
+ */
+ if ((status = ptree_get_node_by_path(PLATFORM_PATH, &platnode)) !=
+ PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE,
+ "io_dev_label: can't find platform node: %s\n",
+ picl_strerror(status));
+ return;
+ }
+
+ node_count = md_node_count(mdp);
+ if (node_count == 0) {
+ pri_debug(LOG_NOTICE, "io_dev_addlabel: no nodes to process\n");
+ return;
+ }
+ components = (mde_cookie_t *)malloc(node_count * sizeof (mde_cookie_t));
+ if (components == NULL) {
+ pri_debug(LOG_NOTICE,
+ "io_dev_addlabel: can't get memory for IO nodes\n");
+ return;
+ }
+
+ component_count = md_scan_dag(mdp, rootnode,
+ md_find_name(mdp, "component"),
+ md_find_name(mdp, "fwd"), components);
+
+ for (i = 0; i < component_count; ++i) {
+ tpn = platnode;
+
+ /*
+ * Try to fetch the "type" as a string or as "data" until we
+ * can agree on what its tag type should be.
+ */
+ if (md_get_prop_str(mdp, components[i], "type", &type) == -1) {
+ if (md_get_prop_data(mdp, components[i], "type",
+ (uint8_t **)&type, &type_size)) {
+ pri_debug(LOG_NOTICE, "io_add_devlabel: can't "
+ "get type for component %d\n", i);
+ continue;
+ }
+ }
+
+ /*
+ * Isolate components of type "io".
+ */
+ if (strcmp((const char *)type, "io")) {
+ pri_debug(LOG_NOTICE,
+ "io_add_devlabel: skipping component %d with "
+ "type %s\n", i, type);
+ continue;
+ }
+
+ /*
+ * Now get the nac and raw path from the PRI.
+ */
+ if (md_get_prop_str(mdp, components[i], "nac", &nac) == -1) {
+ pri_debug(LOG_NOTICE,
+ "io_add_devlabel: can't get nac value for device "
+ "<%s>\n", type);
+ continue;
+ } else
+ nac_size = strlen(nac) + 1;
+
+ if (md_get_prop_str(mdp, components[i], "path", &path) == -1) {
+ pri_debug(LOG_NOTICE,
+ "io_add_devlabel: can't get path value for "
+ "device <%s>\n", type);
+ continue;
+ }
+
+ pri_debug(LOG_NOTICE, "io_add_devlabel: processing component "
+ "%d, type <%s>, nac <%s>, path <%s>\n", i, type, nac,
+ path);
+
+ /*
+ * This loop visits each path component where those
+ * components are delimited with '/' and '@' characters.
+ * Each path component is a search key into the /platform
+ * tree; we're looking to match the bus-addr field of
+ * a node if that field is defined. If each path component
+ * matches up then we now have the corresponding device
+ * path for that IO device. Add a Label property to the
+ * leaf node.
+ */
+ for (busaddr_match = 1, p = q = (char *)path; q; p = q + 1) {
+
+ /*
+ * Isolate the bus address for this node by skipping
+ * over the first delimiter if present and writing
+ * a NUL character over the next '/'.
+ */
+ if (*p == '/')
+ ++p;
+ if (*p == '@')
+ ++p;
+ if ((q = strchr((const char *)p, '/')) != NULL)
+ *q = '\0';
+
+ /*
+ * See if there's a match, at this level only, in the
+ * device tree. We cannot skip generations in the
+ * device tree, which is why we're not doing a
+ * recursive search for bus-addr. bus-addr must
+ * be found at each node along the way. By doing
+ * this we'll stay in sync with the path components
+ * in the PRI.
+ */
+ if ((status = find_node_by_string_prop(tpn,
+ PICL_PROP_BUS_ADDR, (const char *)p, &tpn)) !=
+ PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE,
+ "can't find %s property of <%s> "
+ "for nac %s: %s\n",
+ PICL_PROP_BUS_ADDR, p, nac,
+ picl_strerror(status));
+ busaddr_match = 0;
+ break;
+ }
+
+ /*
+ * Note path component for the leaf so we can use
+ * it below.
+ */
+ saved_path = p;
+ }
+
+ /*
+ * We could not drill down through the bus-addrs, so skip this
+ * device and move on to the next.
+ */
+ if (busaddr_match == 0) {
+ pri_debug(LOG_NOTICE, "io_add_devlabel: no matching "
+ "bus-addr path for this nac - skipping\n");
+ continue;
+ }
+
+ nac_size = strlen((const char *)nac) + 1;
+
+ /*
+ * This loop adds a Label property to all the functions
+ * on the device we matched from the PRI path.
+ */
+ for (status = PICL_SUCCESS; status == PICL_SUCCESS;
+ status = ptree_get_propval_by_name(tpn, PICL_PROP_PEER,
+ &tpn, sizeof (picl_nodehdl_t))) {
+ /*
+ * Add Labels to peers that have the same bus-addr
+ * value (ignoring the function numbers.)
+ */
+ if ((substatus = ptree_get_propval_by_name(tpn,
+ PICL_PROP_BUS_ADDR,
+ busaddr, sizeof (busaddr))) != PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE,
+ "io_add_device: can't get %s "
+ "property from picl devtree: %s\n",
+ PICL_PROP_BUS_ADDR,
+ picl_strerror(substatus));
+ } else {
+ /*
+ * If the nac doesn't include a specific
+ * function number then don't look for one
+ * in the bus-addr. Devices on PCI-X bridges
+ * may have a function number in the path,
+ * so don't ignore that.
+ */
+ if (strchr(nac, ',') == NULL) {
+ if ((q = strchr(busaddr, ',')) !=
+ NULL) {
+ *q = '\0';
+ }
+ }
+ if (strncmp(busaddr, saved_path,
+ PICL_PROPNAMELEN_MAX) == 0) {
+ add_md_prop(tpn, nac_size,
+ PICL_PROP_LABEL, nac,
+ PICL_PTYPE_CHARSTRING);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * These two functions shamelessly stolen from picldevtree.c
+ */
+
+/*
+ * 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);
+}
+
+/*
+ * Search this node's children for the given property.
+ */
+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);
+ }
+ }
+ return (PICL_ENDOFLIST);
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c b/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c
new file mode 100644
index 0000000000..8632ff1d63
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c
@@ -0,0 +1,316 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * The PRI plug-in picks up memory configuration data from the PRI
+ * and injects this into PICL's /platform tree. It only populates
+ * the logical view of memory: memory, memory-segment, memory-bank.
+ * It does not populate the /device tree since there are no memory
+ * controller devices on sun4v.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "priplugin.h"
+#include "../../common/memcfg/piclmemcfg.h"
+
+/*
+ * These 3 variable are defined and set in mdescplugin.c
+ */
+extern picl_nodehdl_t root_node;
+extern md_t *mdp;
+extern mde_cookie_t rootnode;
+
+static void
+add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, uint64_t size);
+
+static void
+add_bank_props(picl_nodehdl_t node, mde_cookie_t banklistp,
+ uint64_t *size, uint64_t *mask, unsigned int id);
+static uint64_t countbits(uint64_t v);
+
+static void
+add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
+ uint64_t interleave, uint64_t *size, uint64_t base);
+
+/*
+ * Callback function for picl_walk_tree_by_class().
+ * NOTE: picl_walk_tree_by_class() maps the return codes PICL_WALK_CONTINUE
+ * and PICL_WALK_TERMINATE to PICL_SUCCESS.
+ */
+int
+add_mem_prop(picl_nodehdl_t node, void *args)
+{
+ mde_cookie_t *memorylistp, *segmentlistp, *banklistp;
+ picl_prophdl_t memh, segmenth, bankh;
+ mde_cookie_t *buf;
+ int j, k, num_nodes, interleave, err;
+ int nsegments, nbanks, nmemory;
+ uint64_t memsize, segsize, segbase;
+ uint64_t size, mask;
+
+ /*
+ * An absence of nodes or failure to obtain memory for searches
+ * or absence of the /memory node will cause this to fail.
+ * Return PICL_WALK_SUCCESS to allow the plug-in to continue.
+ */
+ num_nodes = md_node_count(mdp);
+ if (num_nodes == 0) {
+ pri_debug(LOG_NOTICE, "add_mem_prop: no nodes to walk\n");
+ return (PICL_SUCCESS);
+ }
+ buf = (mde_cookie_t *)malloc(sizeof (mde_cookie_t) * num_nodes * 3);
+ if (buf == NULL) {
+ pri_debug(LOG_NOTICE, "add_mem_prop: can't allocate memory\n");
+ return (PICL_SUCCESS);
+ }
+
+ memorylistp = &buf[0];
+ segmentlistp = &buf[num_nodes];
+ banklistp = &buf[num_nodes * 2];
+
+ if ((ptree_get_node_by_path(MEMORY_PATH, &memh)) != PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE,
+ "add_mem_prop: can't find /memory node in platform tree\n");
+ free(buf);
+ return (PICL_SUCCESS);
+ }
+
+ /*
+ * There should be only one memory node.
+ * If we can't find what we're looking for in the DAG then
+ * return PICL_PROPNOTFOUND to get the caller to re-try with
+ * a different property name.
+ */
+ nmemory = md_scan_dag(mdp, rootnode, md_find_name(mdp, args),
+ md_find_name(mdp, "fwd"), memorylistp);
+ if (nmemory != 1) {
+ pri_debug(LOG_NOTICE,
+ "add_mem_prop: wrong number of memory dags: expected "
+ "1, got %d\n", nmemory);
+ free(buf);
+ return (PICL_PROPNOTFOUND);
+ }
+
+ nsegments = md_scan_dag(mdp, memorylistp[0],
+ md_find_name(mdp, "memory-segment"),
+ md_find_name(mdp, "fwd"),
+ segmentlistp);
+
+ if (nsegments == 0) {
+ pri_debug(LOG_NOTICE, "add_mem_prop: wrong number of memory "
+ "segments: expected >0, got %d\n", nsegments);
+ free(buf);
+ return (PICL_PROPNOTFOUND);
+ }
+
+ /*
+ * Add memory segments, keep running total of system memory.
+ */
+ for (memsize = 0, segsize = 0, j = 0; j < nsegments;
+ ++j, memsize += segsize) {
+ nbanks = 0;
+ err = ptree_create_and_add_node(memh,
+ PICL_NAME_MEMORY_SEGMENT,
+ PICL_CLASS_MEMORY_SEGMENT, &segmenth);
+ if (err == PICL_SUCCESS) {
+ size = 0;
+ mask = 0;
+
+ /*
+ * Need to pull this out here since it's used for
+ * the ID.
+ */
+ if (md_get_prop_val(mdp, segmentlistp[j], "base",
+ &segbase))
+ segbase = 0ULL;
+
+ /*
+ * Add banks under each segment.
+ */
+ nbanks = md_scan_dag(mdp, segmentlistp[j],
+ md_find_name(mdp, "memory-bank"),
+ md_find_name(mdp, "fwd"),
+ banklistp);
+
+ if (nbanks <= 0) {
+ pri_debug(LOG_NOTICE, "add_mem_prop: no banks "
+ "found for segment %d\n", j);
+ } else {
+ for (k = 0; k < nbanks; ++k) {
+ err =
+ ptree_create_and_add_node(segmenth,
+ PICL_NAME_MEMORY_BANK,
+ PICL_CLASS_MEMORY_BANK, &bankh);
+ if (err == PICL_SUCCESS) {
+ /*
+ * Add AddressMatch,
+ * AddressMask, Size, and
+ * ID to each bank.
+ */
+ add_bank_props(bankh,
+ banklistp[k],
+ &size, &mask,
+ (segbase >> 32) * j + k);
+ }
+ }
+ }
+ }
+
+ /*
+ * Add Interleave, BaseAddress, and Size to each segment.
+ */
+ interleave = 2 << (countbits(mask & (size - 1)) - 1);
+ add_segment_props(segmenth, segmentlistp[j],
+ interleave, &segsize, segbase);
+ }
+
+ /*
+ * Add TransferSize and Size (total memory) to this node.
+ */
+ add_memory_props(memh, memorylistp[0], memsize);
+
+ free(buf);
+ return (PICL_WALK_CONTINUE);
+}
+
+static void
+add_bank_props(picl_nodehdl_t node, mde_cookie_t banklistp,
+ uint64_t *size, uint64_t *mask, unsigned int id)
+{
+ uint64_t int_value;
+ mde_cookie_t *dimmlistp;
+ int node_count, i, type_size, nac_size, status;
+ uint8_t *type;
+ char *pc, *nac;
+
+ *size = 0ULL;
+ *mask = 0ULL;
+
+ node_count = md_node_count(mdp);
+ dimmlistp = (mde_cookie_t *)malloc(node_count * sizeof (mde_cookie_t));
+
+ if (!md_get_prop_val(mdp, banklistp, "size", &int_value)) {
+ add_md_prop(node, sizeof (int_value), PICL_PROP_SIZE,
+ &int_value, PICL_PTYPE_UNSIGNED_INT);
+ *size = int_value;
+ }
+ if (!md_get_prop_val(mdp, banklistp, "mask",
+ &int_value)) {
+ add_md_prop(node, sizeof (int_value),
+ PICL_PROP_ADDRESSMASK,
+ &int_value, PICL_PTYPE_UNSIGNED_INT);
+ *mask = int_value;
+ }
+ if (!md_get_prop_val(mdp, banklistp, "match",
+ &int_value)) {
+ add_md_prop(node, sizeof (int_value),
+ PICL_PROP_ADDRESSMATCH,
+ &int_value, PICL_PTYPE_UNSIGNED_INT);
+ }
+
+ add_md_prop(node, sizeof (id), PICL_PROP_ID, &id,
+ PICL_PTYPE_INT);
+
+ node_count = md_scan_dag(mdp, banklistp, md_find_name(mdp, "component"),
+ md_find_name(mdp, "fwd"), dimmlistp);
+
+ for (i = 0; i < node_count; ++i) {
+ if ((status = md_get_prop_str(mdp, dimmlistp[i], "type",
+ (char **)&type)) == -1) {
+ status = md_get_prop_data(mdp, dimmlistp[i],
+ "type", &type, &type_size);
+ }
+ if (status == 0) {
+ if (strcmp((const char *)type, "dimm") == 0) {
+ if (!md_get_prop_str(mdp, dimmlistp[i], "nac",
+ (char **)&nac)) {
+ nac_size = strlen(nac) + 1;
+ add_md_prop(node, nac_size,
+ "nac", nac,
+ PICL_PTYPE_CHARSTRING);
+ if ((pc = strrchr(nac, '/')) != NULL)
+ nac = ++pc;
+ nac_size = strlen(nac) + 1;
+ add_md_prop(node, nac_size,
+ PICL_PROP_LABEL, nac,
+ PICL_PTYPE_CHARSTRING);
+ }
+ }
+ }
+ }
+}
+
+static uint64_t
+countbits(uint64_t v)
+{
+ uint64_t c; /* c accumulates the total bits set in v */
+
+ for (c = 0; v; c++)
+ v &= v - 1; /* clear the least significant bit set */
+ return (c);
+}
+
+static void
+add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
+ uint64_t interleave, uint64_t *size, uint64_t base)
+{
+ uint64_t int_value;
+
+ *size = 0;
+ if (!md_get_prop_val(mdp, segmentlistp, "size", &int_value)) {
+ add_md_prop(node, sizeof (int_value),
+ PICL_PROP_SIZE, &int_value,
+ PICL_PTYPE_UNSIGNED_INT);
+ *size = int_value;
+ }
+ add_md_prop(node, sizeof (base), PICL_PROP_BASEADDRESS,
+ &base, PICL_PTYPE_UNSIGNED_INT);
+
+ add_md_prop(node, sizeof (interleave), PICL_PROP_INTERLEAVE_FACTOR,
+ &interleave, PICL_PTYPE_UNSIGNED_INT);
+}
+
+static void
+add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, uint64_t size)
+{
+ uint64_t int_value;
+
+ /*
+ * If the top-level node has a size property then use that,
+ * otherwise use the size that was calculated by the caller
+ * and passed in.
+ */
+ if (md_get_prop_val(mdp, memorylistp, "size", &int_value))
+ int_value = size;
+ add_md_prop(node, sizeof (int_value), PICL_PROP_SIZE, &int_value,
+ PICL_PTYPE_UNSIGNED_INT);
+ if (!md_get_prop_val(mdp, memorylistp, "transfer_size",
+ &int_value)) {
+ add_md_prop(node, sizeof (int_value),
+ PICL_PROP_TRANSFER_SIZE,
+ &int_value, PICL_PTYPE_UNSIGNED_INT);
+ }
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c
new file mode 100644
index 0000000000..113bc64b8a
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c
@@ -0,0 +1,145 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "priplugin.h"
+
+#pragma init(priplugin_register) /* place in .init section */
+
+picl_nodehdl_t root_node;
+md_t *mdp;
+mde_cookie_t rootnode;
+
+void priplugin_init(void);
+void priplugin_fini(void);
+
+picld_plugin_reg_t priplugin_reg = {
+ PICLD_PLUGIN_VERSION_1,
+ PICLD_PLUGIN_CRITICAL,
+ "pri_plugin",
+ priplugin_init,
+ priplugin_fini
+};
+
+void
+set_prop_info(ptree_propinfo_t *propinfo, int size, char *name, int type)
+{
+ propinfo->version = PICLD_PLUGIN_VERSION_1;
+ propinfo->read = NULL;
+ propinfo->write = NULL;
+ propinfo->piclinfo.type = type;
+ propinfo->piclinfo.accessmode = PICL_READ;
+ propinfo->piclinfo.size = size;
+ (void) strncpy(propinfo->piclinfo.name, name,
+ sizeof (propinfo->piclinfo.name));
+}
+
+boolean_t
+prop_exists(picl_nodehdl_t node, char *name)
+{
+ int status;
+ picl_prophdl_t proph;
+
+ status = ptree_get_prop_by_name(node, name, &proph);
+ if (status == PICL_SUCCESS)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+void
+add_md_prop(picl_nodehdl_t node, int size, char *name, void* value, int type)
+{
+ ptree_propinfo_t propinfo;
+ picl_prophdl_t proph;
+
+ if (!prop_exists(node, name)) {
+ set_prop_info(&propinfo, size, name, type);
+
+ (void) ptree_create_and_add_prop(node, &propinfo,
+ value, &proph);
+ }
+}
+
+void
+priplugin_init(void)
+{
+ int status;
+
+ pri_debug(LOG_NOTICE, "priplugin: entered\n");
+ status = ptree_get_root(&root_node);
+ if (status != PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE, "priplugin: can't get picl root node\n");
+ return;
+ }
+
+ mdp = pri_devinit();
+ if (mdp == NULL) {
+ pri_debug(LOG_NOTICE, "priplugin: cannot init pri: %d\n",
+ errno);
+ return;
+ }
+
+ rootnode = md_root_node(mdp);
+
+ pri_debug(LOG_NOTICE, "priplugin: have root picl and PRI nodes\n");
+
+ status = ptree_walk_tree_by_class(root_node, "memory",
+ "memory-segments", add_mem_prop);
+ if (status != PICL_SUCCESS) {
+ pri_debug(LOG_NOTICE, "pri: memory-segments walk failed\n");
+ } else
+ pri_debug(LOG_NOTICE, "pri: success walking memory node\n");
+
+ io_dev_addlabel();
+
+ pri_devfini(mdp);
+}
+
+void
+priplugin_fini(void)
+{
+}
+
+void
+priplugin_register(void)
+{
+ picld_plugin_register(&priplugin_reg);
+}
+
+/*VARARGS2*/
+void
+pri_debug(int level, char *fmt, ...)
+{
+#if (PRI_DEBUG != 0)
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(level, fmt, ap);
+ va_end(ap);
+#endif
+}
diff --git a/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h
new file mode 100644
index 0000000000..1b172918db
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h
@@ -0,0 +1,86 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PRIPLUGIN_H
+#define _PRIPLUGIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <picl.h>
+#include <picltree.h>
+#include <picldefs.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <alloca.h>
+#include <sys/stat.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <mdesc.h>
+#include <string.h>
+#include <errno.h>
+#include <libnvpair.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <config_admin.h>
+#include <sys/param.h>
+#include <libdevinfo.h>
+#include <sys/systeminfo.h>
+#include <sys/sysevent/dr.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#define MAXSTRLEN 256
+
+#ifndef PRI_DEBUG
+#define PRI_DEBUG 0
+#endif
+
+/* These 3 variable are defined and set in mdescplugin.c */
+extern picl_nodehdl_t root_node;
+extern md_t *mdp;
+extern mde_cookie_t rootnode;
+
+int add_mem_prop(picl_nodehdl_t node, void *args);
+md_t *pri_devinit(void);
+void pri_devfini(md_t *mdp);
+void pri_debug(int level, char *fmt, ...);
+void add_md_prop(picl_nodehdl_t node, int size, char *name, void* value,
+ int type);
+void io_dev_addlabel(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PRIPLUGIN_H */
diff --git a/usr/src/cmd/picl/plugins/sun4v/snmp/Makefile b/usr/src/cmd/picl/plugins/sun4v/snmp/Makefile
new file mode 100644
index 0000000000..66f24fd4a8
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/snmp/Makefile
@@ -0,0 +1,97 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/picl/plugins/sun4v/snmp/Makefile
+#
+
+LIBRARY= libsnmpplugin.a
+VERS= .1
+
+OBJS_DIR= pics
+OBJECTS= snmpplugin.o
+
+# Include library definitions
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/Makefile.psm
+include $(SRC)/cmd/picl/plugins/Makefile.com
+
+SRCS= $(OBJECTS:%.o=%.c)
+LIBS = $(DYNLIB)
+
+ROOT_PLATFORM = $(USR_PLAT_DIR)/sun4v
+ROOTLIBDIR = $(ROOT_PLAT_PLUGINDIR)
+
+CLEANFILES= $(LINTOUT) $(LINTLIB)
+
+CPPFLAGS += -I. -I../include -I$(SRC)/uts/common/sys
+CPPFLAGS += -D_REENTRANT
+
+#
+# Be careful when enabling SNMPPLUGIN_DEBUG. The debug log can quickly
+# grow too large. NEVER stress/cycle test picl with SNMPPLUGIN_DEBUG
+# enabled
+#
+#CPPFLAGS += -DSNMPPLUGIN_DEBUG
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -L$(SRC)/lib/libpicltree/$(MACH)
+LDLIBS += -L$(SRC)/cmd/picl/plugins/sun4v/lib/snmp
+LDLIBS += -L$(ROOT)/usr/lib/sparcv9
+LDLIBS += -lc -lpicltree -lpiclsnmp
+DYNFLAGS += -R/usr/platform/sun4v/lib
+
+POFILE = snmpplugin_sun4v.po
+POFILES = $(SRCS:%.c=%.po)
+
+.KEEP_STATE:
+
+all: $(LIBS) $(LIBLINKS)
+
+install: all $(ROOTLIBDIR) $(ROOTLIBS) $(ROOTLINKS)
+
+$(LIBLINKS): FRC
+ $(RM) $(LIBLINKS); $(SYMLINK) $(DYNLIB) $(LIBLINKS)
+
+# Messages
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)/$(POFILE)
+
+$(MSGDOMAIN):
+ $(INS.dir)
+
+$(POFILE): $(POFILES)
+ $(CAT) $(POFILES) > $(POFILE)
+
+# Include library targets
+include $(SRC)/cmd/picl/plugins/Makefile.targ
+include $(SRC)/lib/Makefile.targ
+
+lint :
+ $(LINT.c) $(SRCS)
+
+FRC:
diff --git a/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c
new file mode 100644
index 0000000000..ced75d1569
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c
@@ -0,0 +1,1690 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The SNMP picl plugin connects to the agent on the SP and creates
+ * and populates the /physical-platform subtree in picl tree for use
+ * by picl consumers.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+
+#include <picldefs.h>
+#include <picl.h>
+#include <picltree.h>
+
+#include "picloids.h"
+#include "libpiclsnmp.h"
+#include "snmpplugin.h"
+
+#pragma init(snmpplugin_register) /* place in .init section */
+
+picld_plugin_reg_t snmpplugin_reg = {
+ PICLD_PLUGIN_VERSION_1,
+ PICLD_PLUGIN_NON_CRITICAL,
+ "snmp_plugin",
+ snmpplugin_init,
+ snmpplugin_fini
+};
+
+static picl_snmphdl_t hdl;
+
+/*
+ * The stale_tree_rwlp protects the stale_xxx vars. The 'stale_tree' flag
+ * and the 'rebuild_tree' flag below are both initialized to B_TRUE to
+ * let the tree_builder() thread build the initial tree without blocking.
+ */
+static rwlock_t stale_tree_rwlp;
+static boolean_t stale_tree = B_TRUE;
+
+/*
+ * vol_props, volprop_ndx and n_vol_props are protected by the stale_tree
+ * flag. They are read only when the stale_tree flag is B_FALSE and written
+ * to only when the flag is B_TRUE.
+ *
+ * The change_time (last changed time) is read by only one thread at a
+ * time when stale_tree is B_FALSE (protected by stale_tree_rwlp). It is
+ * written by only one thread (the tree builder) when stale_tree is B_TRUE.
+ *
+ * Note that strictly speaking, change_time should be uint_t (timeticks32).
+ * But keeping it as int is fine, since we don't do any arithmetic on it
+ * except equality check.
+ */
+static vol_prophdl_t *vol_props = NULL;
+static int volprop_ndx = 0, n_vol_props = 0;
+static int change_time = 0;
+
+/*
+ * The rebuild_tree_lock and cv are used by the tree builder thread.
+ * rebuild_tree has to be initialized to B_TRUE to let the tree_builder
+ * do the first build without blocking.
+ */
+static mutex_t rebuild_tree_lock;
+static cond_t rebuild_tree_cv;
+static boolean_t rebuild_tree = B_TRUE;
+
+/*
+ * These two should really not be global
+ */
+static picl_nodehdl_t *physplat_nodes = NULL;
+static int n_physplat_nodes = 0;
+
+static char *group1[] = {
+ OID_entPhysicalDescr,
+ OID_entPhysicalContainedIn,
+ OID_entPhysicalClass,
+ OID_entPhysicalName,
+ OID_entPhysicalHardwareRev,
+ OID_entPhysicalFirmwareRev,
+ OID_entPhysicalSerialNum,
+ OID_entPhysicalMfgName,
+ OID_entPhysicalModelName,
+ OID_entPhysicalIsFRU,
+ 0
+};
+
+static char *group2[] = {
+ OID_sunPlatEquipmentHolderAcceptableTypes,
+ OID_sunPlatCircuitPackReplaceable,
+ OID_sunPlatCircuitPackHotSwappable,
+ OID_sunPlatPhysicalClass,
+ OID_sunPlatSensorClass,
+ OID_sunPlatSensorType,
+ OID_sunPlatAlarmType,
+ OID_sunPlatPowerSupplyClass,
+ 0
+};
+
+static char *volgroup1[] = {
+ OID_sunPlatBinarySensorCurrent,
+ OID_sunPlatBinarySensorExpected,
+ OID_sunPlatBinarySensorInterpretTrue,
+ OID_sunPlatBinarySensorInterpretFalse,
+ 0
+};
+
+static char *volgroup2[] = {
+ OID_sunPlatNumericSensorBaseUnits,
+ OID_sunPlatNumericSensorExponent,
+ OID_sunPlatNumericSensorRateUnits,
+ OID_sunPlatNumericSensorCurrent,
+ OID_sunPlatNumericSensorLowerThresholdFatal,
+ OID_sunPlatNumericSensorLowerThresholdCritical,
+ OID_sunPlatNumericSensorLowerThresholdNonCritical,
+ OID_sunPlatNumericSensorUpperThresholdNonCritical,
+ OID_sunPlatNumericSensorUpperThresholdCritical,
+ OID_sunPlatNumericSensorUpperThresholdFatal,
+ 0
+};
+
+/*
+ * The following two items must match the Sun Platform MIB specification
+ * in their indices and values.
+ */
+static char *sensor_baseunits[] = {
+ "", "other", "unknown", "degC", "degF", "degK", "volts", "amps",
+ "watts", "joules", "coulombs", "va", "nits", "lumens", "lux",
+ "candelas", "kPa", "psi", "newtons", "cfm", "rpm", "hertz",
+ "seconds", "minutes", "hours", "days", "weeks", "mils", "inches",
+ "feet", "cubicInches", "cubicFeet", "meters", "cubicCentimeters",
+ "cubicMeters", "liters", "fluidOunces", "radians", "steradians",
+ "revolutions", "cycles", "gravities", "ounces", "pounds", "footPounds",
+ "ounceInches", "gauss", "gilberts", "henries", "farads", "ohms",
+ "siemens", "moles", "becquerels", "ppm", "decibels", "dBA", "dbC",
+ "grays", "sieverts", "colorTemperatureDegK", "bits", "bytes", "words",
+ "doubleWords", "quadWords", "percentage"
+};
+static const int n_baseunits = sizeof (sensor_baseunits) / sizeof (char *);
+
+static char *sensor_rateunits[] = {
+ "",
+ "none",
+ "perMicroSecond",
+ "perMilliSecond",
+ "perSecond",
+ "perMinute",
+ "perHour",
+ "perDay",
+ "perWeek",
+ "perMonth",
+ "perYear"
+};
+static const int n_rateunits = sizeof (sensor_rateunits) / sizeof (char *);
+
+/*
+ * Local declarations
+ */
+static void snmpplugin_register(void);
+static void register_group(char **g, int is_volatile);
+static void *tree_builder(void *arg);
+static int build_physplat(picl_nodehdl_t *subtree_rootp);
+static void free_resources(picl_nodehdl_t subtree_root);
+
+static picl_nodehdl_t make_node(picl_nodehdl_t subtree_root, int row,
+ int *snmp_syserr_p);
+static void save_nodeh(picl_nodehdl_t nodeh, int row);
+static picl_nodehdl_t lookup_nodeh(int row);
+
+static void save_volprop(picl_prophdl_t prop, char *oidstr, int row,
+ int proptype);
+static void check_for_stale_data(void);
+static int read_volprop(ptree_rarg_t *parg, void *buf);
+
+static void threshold(picl_nodehdl_t node, char *oidstr, int row,
+ char *propname, int *snmp_syserr_p);
+static void add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p);
+
+static char *get_slot_type(int row, int *snmp_syserr_p);
+static int add_volatile_prop(picl_nodehdl_t nodeh, char *name,
+ int type, int access, int size, int (*rdfunc)(ptree_rarg_t *, void *),
+ int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp);
+static int add_string_prop(picl_nodehdl_t node, char *propname, char *propval);
+static int add_void_prop(picl_nodehdl_t node, char *propname);
+static int add_int_prop(picl_nodehdl_t node, char *propname, int val);
+static void add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
+ int row, sp_propid_t pp, int *snmp_syserr_p);
+
+static void log_msg(int pri, const char *fmt, ...);
+
+#ifdef SNMPPLUGIN_DEBUG
+static mutex_t snmpplugin_dbuf_lock;
+static char *snmpplugin_dbuf = NULL;
+static char *snmpplugin_dbuf_curp = NULL;
+static int snmpplugin_dbuf_sz = 0;
+static int snmpplugin_dbuf_overflow = 0;
+static char snmpplugin_lbuf[SNMPPLUGIN_DMAX_LINE];
+
+static void snmpplugin_log_init(void);
+static void snmpplugin_log(const char *fmt, ...);
+static void snmpplugin_log_append(void);
+static void snmpplugin_dbuf_realloc(void);
+#endif
+
+static void
+snmpplugin_register(void)
+{
+ (void) picld_plugin_register(&snmpplugin_reg);
+}
+
+static void
+register_group(char **g, int is_volatile)
+{
+ int i, len = 0;
+ int n_oids;
+ char *p, *oidstrs;
+
+ for (i = 0; g[i]; i++)
+ len += strlen(g[i]) + 1;
+ n_oids = i;
+
+ if ((oidstrs = (char *)calloc(1, len)) == NULL)
+ return;
+
+ for (p = oidstrs, i = 0; g[i]; i++) {
+ (void) strcpy(p, g[i]);
+ p += strlen(g[i]) + 1;
+ }
+
+ snmp_register_group(hdl, oidstrs, n_oids, is_volatile);
+}
+
+void
+snmpplugin_init(void)
+{
+ int ret;
+
+ (void) mutex_init(&rebuild_tree_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&rebuild_tree_cv, USYNC_THREAD, NULL);
+ (void) rwlock_init(&stale_tree_rwlp, USYNC_THREAD, NULL);
+ LOGINIT();
+
+ /*
+ * Create the tree-builder thread and let it take over
+ */
+ LOGPRINTF("Tree-builder thread being created.\n");
+ if ((ret = thr_create(NULL, NULL, tree_builder, NULL,
+ THR_BOUND, NULL)) < 0) {
+ log_msg(LOG_ERR, SNMPP_CANT_CREATE_TREE_BUILDER, ret);
+ snmp_fini(hdl);
+ return;
+ }
+}
+
+void
+snmpplugin_fini(void)
+{
+ snmp_fini(hdl);
+
+ (void) rwlock_destroy(&stale_tree_rwlp);
+ (void) cond_destroy(&rebuild_tree_cv);
+ (void) mutex_destroy(&rebuild_tree_lock);
+}
+
+/*ARGSUSED*/
+static void *
+tree_builder(void *arg)
+{
+ int ret, rv;
+ picl_nodehdl_t root_node;
+ picl_nodehdl_t physplat_root;
+ picl_nodehdl_t old_physplat_root;
+
+ /*
+ * Initialize SNMP service
+ */
+ LOGPRINTF("Initializing SNMP service.\n");
+ if ((hdl = snmp_init()) == NULL) {
+ log_msg(LOG_ERR, SNMPP_CANT_INIT);
+ return ((void *)-1);
+ }
+
+ /*
+ * Register OID groupings for BULKGET optimizations
+ */
+ LOGPRINTF("Registering OID groups.\n");
+ register_group(group1, 0);
+ register_group(group2, 0);
+ register_group(volgroup1, 1);
+ register_group(volgroup2, 1);
+
+ (void) mutex_lock(&rebuild_tree_lock);
+
+ for (;;) {
+ LOGPRINTF("tree_builder: check whether to rebuild subtree\n");
+ while (rebuild_tree == B_FALSE)
+ (void) cond_wait(&rebuild_tree_cv, &rebuild_tree_lock);
+
+ LOGPRINTF("tree_builder: woke up\n");
+
+ old_physplat_root = NULL;
+ physplat_root = NULL;
+
+ LOGPRINTF("tree_builder: getting root node\n");
+ if ((ret = ptree_get_root(&root_node)) != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_NO_ROOT, ret);
+ return ((void *)-2);
+ }
+
+ LOGPRINTF("tree_builder: getting existing physplat node\n");
+ rv = ptree_find_node(root_node, PICL_PROP_NAME,
+ PICL_PTYPE_CHARSTRING, PICL_NODE_PHYSPLAT,
+ sizeof (PICL_NODE_PHYSPLAT), &old_physplat_root);
+
+ LOGPRINTF("tree_builder: building physical-platform\n");
+ if ((ret = build_physplat(&physplat_root)) < 0) {
+ log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
+ snmp_fini(hdl);
+ return ((void *)-3);
+ }
+
+ if (rv == PICL_SUCCESS && old_physplat_root != NULL) {
+ LOGPRINTF("tree_builder: destroying existing nodes\n");
+ ptree_delete_node(old_physplat_root);
+ ptree_destroy_node(old_physplat_root);
+ }
+
+ LOGPRINTF("tree_builder: attaching new subtree\n");
+ if ((ret = ptree_add_node(root_node, physplat_root)) < 0) {
+ free_resources(physplat_root);
+ log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
+ snmp_fini(hdl);
+ return ((void *)-4);
+ }
+
+ LOGPRINTF("tree_builder: setting stale_tree to FALSE\n");
+ (void) rw_wrlock(&stale_tree_rwlp);
+ stale_tree = B_FALSE;
+ (void) rw_unlock(&stale_tree_rwlp);
+
+ LOGPRINTF("tree_builder: setting rebuild_tree to FALSE\n");
+ rebuild_tree = B_FALSE;
+ }
+
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static int
+build_physplat(picl_nodehdl_t *subtree_rootp)
+{
+ int change_time1;
+ int row, nxtrow;
+ int clr_linkreset = 0;
+ int ret = 0;
+ int snmp_syserr = 0;
+
+retry:
+ (void) snmp_reinit(hdl, clr_linkreset);
+ clr_linkreset = 0;
+
+ /*
+ * Record LastChangeTime before we start building the tree
+ */
+ ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
+ &change_time1, &snmp_syserr);
+ if (ret < 0) {
+ if (snmp_syserr == ECANCELED) {
+ log_msg(LOG_WARNING, SNMPP_LINK_RESET);
+ clr_linkreset = 1;
+ goto retry;
+ } else
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ }
+
+ /*
+ * Create the physical-platform node
+ */
+ ret = ptree_create_node(PICL_NODE_PHYSPLAT, PICL_CLASS_PICL,
+ subtree_rootp);
+ if (ret != PICL_SUCCESS)
+ return (-1);
+
+ /*
+ * Scan entPhysicalTable and build the "physical-platform" subtree
+ */
+ ret = 0;
+ for (row = -1; ret == 0; row = nxtrow) {
+ ret = snmp_get_nextrow(hdl, OID_entPhysicalDescr,
+ row, &nxtrow, &snmp_syserr);
+ if (ret == 0) {
+ (void) make_node(*subtree_rootp, nxtrow, &snmp_syserr);
+ }
+
+ if (snmp_syserr == ECANCELED) {
+ /*
+ * If we get this error, a link reset must've
+ * happened and we need to throw away everything
+ * we have now and rebuild the tree again.
+ */
+ log_msg(LOG_WARNING, SNMPP_LINK_RESET);
+ free_resources(*subtree_rootp);
+ clr_linkreset = 1;
+ goto retry;
+ }
+ }
+
+ /*
+ * Record LastChangeTime after we're done building the tree
+ */
+ ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
+ &change_time, &snmp_syserr);
+ if (ret < 0) {
+ if (snmp_syserr == ECANCELED) {
+ log_msg(LOG_WARNING, SNMPP_LINK_RESET);
+ free_resources(*subtree_rootp);
+ clr_linkreset = 1;
+ goto retry;
+ } else
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ }
+
+ /*
+ * If they don't match, some hotplugging must've happened,
+ * free resources we've created and still holding, then go
+ * back and retry
+ */
+ if (change_time != change_time1) {
+ LOGPRINTF("build_physplat: entLastChangeTime has changed!\n");
+ free_resources(*subtree_rootp);
+ change_time1 = change_time;
+ goto retry;
+ }
+
+ /*
+ * The physplat_nodes table is no longer needed, free it
+ */
+ if (physplat_nodes) {
+ free(physplat_nodes);
+ physplat_nodes = NULL;
+ n_physplat_nodes = 0;
+ }
+
+ return (0);
+}
+
+/*
+ * Destroy all resources that were created during the building
+ * of the subtree
+ */
+static void
+free_resources(picl_nodehdl_t subtree_root)
+{
+ if (physplat_nodes) {
+ free(physplat_nodes);
+ physplat_nodes = NULL;
+ n_physplat_nodes = 0;
+ }
+
+ if (subtree_root) {
+ (void) ptree_delete_node(subtree_root);
+ (void) ptree_destroy_node(subtree_root);
+ }
+
+ if (vol_props) {
+ free(vol_props);
+ n_vol_props = 0;
+ volprop_ndx = 0;
+ }
+}
+
+static picl_nodehdl_t
+make_node(picl_nodehdl_t subtree_root, int row, int *snmp_syserr_p)
+{
+ picl_nodehdl_t nodeh, parenth;
+ picl_prophdl_t proph;
+ char *phys_name, *node_name;
+ int parent_row;
+ int ent_physclass, sunplat_physclass;
+ int sensor_class, sensor_type;
+ int alarm_type;
+ int ps_class;
+ int ret;
+
+ /*
+ * If we've already created this picl node, just return it
+ */
+ if ((nodeh = lookup_nodeh(row)) != NULL)
+ return (nodeh);
+
+ /*
+ * If we are creating it only now, make sure we have the parent
+ * created first; if there's no parent, then parent it to the
+ * subtree's root node
+ */
+ ret = snmp_get_int(hdl, OID_entPhysicalContainedIn, row,
+ &parent_row, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0 || parent_row <= 0)
+ parenth = subtree_root;
+ else {
+ parenth = make_node(subtree_root, parent_row, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (parenth == NULL)
+ parenth = subtree_root;
+ }
+
+ /*
+ * Figure out the physical-platform node name from entPhysicalName;
+ * all rows in the MIB that have a valid entPhysicalIndex should
+ * have a physical name.
+ */
+ ret = snmp_get_str(hdl, OID_entPhysicalName, row,
+ &phys_name, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0 || phys_name == NULL) {
+ log_msg(LOG_WARNING, SNMPP_NO_ENTPHYSNAME, row);
+ return (NULL);
+ }
+
+ node_name = basename(phys_name);
+
+ ret = snmp_get_int(hdl, OID_entPhysicalClass, row,
+ &ent_physclass, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ switch (ent_physclass) {
+ case SPC_OTHER:
+ ret = snmp_get_int(hdl, OID_sunPlatPhysicalClass, row,
+ &sunplat_physclass, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ if (sunplat_physclass == SSPC_ALARM) {
+ ret = snmp_get_int(hdl, OID_sunPlatAlarmType,
+ row, &alarm_type, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING,
+ SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ if (alarm_type == SSAT_VISIBLE) {
+ ADD_NODE(PICL_CLASS_LED)
+ } else {
+ ADD_NODE(PICL_CLASS_ALARM)
+ }
+
+ add_prop(nodeh, &proph, node_name, row, PP_STATE,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ } else {
+ ADD_NODE(PICL_CLASS_OTHER)
+ }
+
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_UNKNOWN:
+ ADD_NODE(PICL_CLASS_UNKNOWN)
+ break;
+
+ case SPC_CHASSIS:
+ ADD_NODE(PICL_CLASS_CHASSIS)
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_BACKPLANE:
+ ADD_NODE(PICL_CLASS_BACKPLANE)
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_CONTAINER:
+ ADD_NODE(PICL_CLASS_CONTAINER)
+
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_SLOT_TYPE,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_POWERSUPPLY:
+ ret = snmp_get_int(hdl, OID_sunPlatPowerSupplyClass,
+ row, &ps_class, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ if (ps_class == SSPSC_BATTERY) {
+ ADD_NODE(PICL_CLASS_BATTERY)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_BATT_STATUS, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ } else {
+ ADD_NODE(PICL_CLASS_POWERSUPPLY)
+ }
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_FAN:
+ ADD_NODE(PICL_CLASS_FAN)
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_SENSOR:
+ ret = snmp_get_int(hdl, OID_sunPlatSensorClass,
+ row, &sensor_class, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ ret = snmp_get_int(hdl, OID_sunPlatSensorType,
+ row, &sensor_type, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ if (ret < 0) {
+ log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ free(phys_name);
+ return (NULL);
+ }
+
+ if (sensor_class == SSSC_NUMERIC) {
+ if (sensor_type == SSST_TEMPERATURE) {
+ ADD_NODE(PICL_CLASS_TEMPERATURE_SENSOR)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_TEMPERATURE, snmp_syserr_p);
+ } else if (sensor_type == SSST_VOLTAGE) {
+ ADD_NODE(PICL_CLASS_VOLTAGE_SENSOR)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_VOLTAGE, snmp_syserr_p);
+ } else if (sensor_type == SSST_CURRENT) {
+ ADD_NODE(PICL_CLASS_CURRENT_SENSOR)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_CURRENT, snmp_syserr_p);
+ } else if (sensor_type == SSST_TACHOMETER) {
+ ADD_NODE(PICL_CLASS_RPM_SENSOR)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_SPEED, snmp_syserr_p);
+ } else {
+ ADD_NODE(PICL_CLASS_SENSOR)
+ add_prop(nodeh, &proph, node_name, row,
+ PP_SENSOR_VALUE, snmp_syserr_p);
+ }
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row,
+ PP_OPSTATUS, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row,
+ PP_BASE_UNITS, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row,
+ PP_EXPONENT, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row,
+ PP_RATE_UNITS, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_thresholds(nodeh, row, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ } else if (sensor_class == SSSC_BINARY) {
+ if (sensor_type == SSST_TEMPERATURE) {
+ ADD_NODE(PICL_CLASS_TEMPERATURE_INDICATOR)
+ } else if (sensor_type == SSST_VOLTAGE) {
+ ADD_NODE(PICL_CLASS_VOLTAGE_INDICATOR)
+ } else if (sensor_type == SSST_CURRENT) {
+ ADD_NODE(PICL_CLASS_CURRENT_INDICATOR)
+ } else if (sensor_type == SSST_TACHOMETER) {
+ ADD_NODE(PICL_CLASS_RPM_INDICATOR)
+ } else if (sensor_type == SSST_PRESENCE) {
+ ADD_NODE(PICL_CLASS_PRESENCE_INDICATOR)
+ } else {
+ ADD_NODE(PICL_CLASS_INDICATOR)
+ }
+
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_CONDITION,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_EXPECTED,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ } else {
+ log_msg(LOG_ERR,
+ SNMPP_UNSUPP_SENSOR_CLASS, sensor_class, row);
+ return (NULL);
+ }
+ break;
+
+ case SPC_MODULE:
+ ADD_NODE(PICL_CLASS_MODULE)
+
+ add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_REPLACEABLE,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_HOTSWAPPABLE,
+ snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+ break;
+
+ case SPC_PORT:
+ ADD_NODE(PICL_CLASS_PORT)
+ break;
+
+ case SPC_STACK:
+ ADD_NODE(PICL_CLASS_STACK)
+ break;
+
+ default:
+ log_msg(LOG_WARNING,
+ SNMPP_UNKNOWN_ENTPHYSCLASS, ent_physclass, row);
+ free(phys_name);
+ return (NULL);
+ }
+
+ add_prop(nodeh, &proph, node_name, row, PP_DESCRIPTION, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_LABEL, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_HW_REVISION, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_FW_REVISION, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_SERIAL_NUM, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_MFG_NAME, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_MODEL_NAME, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ add_prop(nodeh, &proph, node_name, row, PP_IS_FRU, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ free(phys_name);
+ save_nodeh(nodeh, row);
+
+ return (nodeh);
+}
+
+/*
+ * Saves the node handle and the row id into physplat_nodes[]. If we're
+ * doing this in response to a hotplug event, we should've freed the
+ * old physplat_nodes before entering here to save the first node of the
+ * new physplat subtree.
+ */
+static void
+save_nodeh(picl_nodehdl_t nodeh, int row)
+{
+ size_t sz, count;
+ picl_nodehdl_t *p;
+
+ if (row >= n_physplat_nodes) {
+ count = (((size_t)row >> NODE_BLOCK_SHIFT) + 1) *
+ N_ELEMS_IN_NODE_BLOCK;
+ sz = count * sizeof (picl_nodehdl_t);
+
+ p = (picl_nodehdl_t *)calloc(count, sizeof (picl_nodehdl_t));
+ if (p == NULL) {
+ log_msg(LOG_ERR, SNMPP_NO_MEM, sz);
+ return;
+ }
+
+ if (physplat_nodes) {
+ (void) memcpy((void *) p, (void *) physplat_nodes,
+ n_physplat_nodes * sizeof (picl_nodehdl_t));
+ free((void *) physplat_nodes);
+ }
+
+ physplat_nodes = p;
+ n_physplat_nodes = count;
+ }
+
+ physplat_nodes[row] = nodeh;
+}
+
+static picl_nodehdl_t
+lookup_nodeh(int row)
+{
+ if (row >= n_physplat_nodes)
+ return (NULL);
+
+ return (physplat_nodes[row]);
+}
+
+/*
+ * We enter this routine only when we are building the physical-platform
+ * subtree, whether for the first time or in response to a hotplug event.
+ * If we're here for rebuilding the tree, we have already set stale_tree
+ * to be B_TRUE, so no one else would be accessing vol_props, n_vol_props
+ * or volprop_ndx. If we're here to build the tree for the first time,
+ * picld hasn't yet created doors and is running single-threaded, so no
+ * one else would be accessing them anyway.
+ */
+static void
+save_volprop(picl_prophdl_t prop, char *oidstr, int row, int proptype)
+{
+ vol_prophdl_t *p;
+ int count;
+
+ if (volprop_ndx == n_vol_props) {
+ count = n_vol_props + N_ELEMS_IN_VOLPROP_BLOCK;
+ p = (vol_prophdl_t *)calloc(count, sizeof (vol_prophdl_t));
+ if (p == NULL) {
+ log_msg(LOG_ERR, SNMPP_NO_MEM,
+ count * sizeof (vol_prophdl_t));
+ return;
+ }
+
+ if (vol_props) {
+ (void) memcpy((void *) p, (void *) vol_props,
+ n_vol_props * sizeof (vol_prophdl_t));
+ free((void *) vol_props);
+ }
+
+ vol_props = p;
+ n_vol_props += N_ELEMS_IN_VOLPROP_BLOCK;
+ }
+
+ vol_props[volprop_ndx].prop = prop;
+ vol_props[volprop_ndx].oidstr = oidstr;
+ vol_props[volprop_ndx].row = row;
+ vol_props[volprop_ndx].proptype = proptype;
+
+ volprop_ndx++;
+}
+
+static void
+check_for_stale_data(void)
+{
+ int cur_change_time;
+ int ret;
+ int snmp_syserr;
+
+ (void) rw_wrlock(&stale_tree_rwlp);
+
+ /*
+ * Check if some other thread beat us to it
+ */
+ if (stale_tree == B_TRUE) {
+ (void) rw_unlock(&stale_tree_rwlp);
+ return;
+ }
+
+ /*
+ * Check if mib data has changed (hotplug? link-reset?)
+ */
+ ret = snmp_get_int(hdl, OID_entLastChangeTime, 0, &cur_change_time,
+ &snmp_syserr);
+ if ((ret == 0) && (cur_change_time == change_time)) {
+ (void) rw_unlock(&stale_tree_rwlp);
+ return;
+ }
+
+ /*
+ * If we can't read entLastChangeTime we assume we need to rebuild
+ * the tree. This will also cover the case when we need to rebuild
+ * the tree because a link reset had happened.
+ */
+ LOGPRINTF2("check_for_stale_data: LastChange times have changed, "
+ "(%#x != %#x)\n", change_time, cur_change_time);
+
+ /*
+ * If the mib data has changed, we need to rebuild the physical-platform
+ * subtree. To do this, we set a flag to mark the tree stale,
+ * so that any future reads to get value of volatile properties will
+ * return PICL_PROPVALUNAVAILABLE, until the stale_tree flag
+ * is reset by the tree builder thread.
+ */
+ stale_tree = B_TRUE;
+ if (vol_props) {
+ free(vol_props);
+ }
+ vol_props = NULL;
+ volprop_ndx = 0;
+ n_vol_props = 0;
+
+ (void) rw_unlock(&stale_tree_rwlp);
+
+ (void) mutex_lock(&rebuild_tree_lock);
+ rebuild_tree = B_TRUE;
+ (void) cond_signal(&rebuild_tree_cv);
+ LOGPRINTF("check_for_stale_data: signalled tree builder\n");
+ (void) mutex_unlock(&rebuild_tree_lock);
+}
+
+/*
+ * This is the critical routine. This callback is invoked by picl whenever
+ * it needs to fetch the value of a volatile property. The first thing we
+ * must do, however, is to see if there has been a hotplug or a link-reset
+ * event since the last time we built the tree and whether we need to
+ * rebuild the tree. If so, we do whatever is necessary to make that happen,
+ * but return PICL_PROPVALUNAVAILABLE for now, without making any further
+ * snmp requests or accessing any globals.
+ */
+static int
+read_volprop(ptree_rarg_t *parg, void *buf)
+{
+ char *pstr;
+ int propval;
+ int i, ndx;
+ int ret;
+ int snmp_syserr = 0;
+
+ /*
+ * First check for any event that would make us throw away
+ * the existing /physical-platform subtree and rebuild
+ * another one. If we are rebuilding the subtree, we just
+ * return the stale value until the tree is fully built.
+ */
+ check_for_stale_data();
+
+ (void) rw_rdlock(&stale_tree_rwlp);
+
+ if (stale_tree == B_TRUE) {
+ (void) rw_unlock(&stale_tree_rwlp);
+ return (PICL_PROPVALUNAVAILABLE);
+ }
+
+ for (i = 0; i < volprop_ndx; i++) {
+ if (vol_props[i].prop == parg->proph) {
+ ndx = i;
+ break;
+ }
+ }
+ if (i == volprop_ndx) {
+ log_msg(LOG_ERR, SNMPP_CANT_FIND_VOLPROP, parg->proph);
+ return (PICL_FAILURE);
+ }
+
+ /*
+ * If we can't read the value, return failure. Even if this was
+ * due to a link reset, between the check for stale data and now,
+ * the next volatile callback by picl will initiate a tree-rebuild.
+ */
+ ret = snmp_get_int(hdl, vol_props[ndx].oidstr, vol_props[ndx].row,
+ &propval, &snmp_syserr);
+ if (ret < 0) {
+ log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
+ return (PICL_FAILURE);
+ }
+
+ switch (vol_props[ndx].proptype) {
+ case VPT_PLATOPSTATE:
+ if (propval == SSOS_DISABLED) {
+ (void) strlcpy(buf, STR_SSOS_DISABLED, MAX_OPSTATE_LEN);
+ } else if (propval == SSOS_ENABLED) {
+ (void) strlcpy(buf, STR_SSOS_ENABLED, MAX_OPSTATE_LEN);
+ } else {
+ log_msg(LOG_ERR, SNMPP_INV_PLAT_EQUIP_OPSTATE,
+ propval, vol_props[ndx].row);
+ return (PICL_FAILURE);
+ }
+ break;
+
+ case VPT_NUMSENSOR:
+ (void) memcpy(buf, &propval, sizeof (propval));
+ break;
+
+ case VPT_BINSENSOR:
+ if (propval == ST_TRUE) {
+ ret = snmp_get_str(hdl,
+ OID_sunPlatBinarySensorInterpretTrue,
+ vol_props[ndx].row, &pstr, &snmp_syserr);
+ if (snmp_syserr == ECANCELED)
+ return (PICL_FAILURE);
+ if (ret < 0 || pstr == NULL) {
+ (void) strlcpy(buf, STR_ST_TRUE,
+ MAX_TRUTHVAL_LEN);
+ } else {
+ (void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
+ free(pstr);
+ }
+ } else if (propval == ST_FALSE) {
+ ret = snmp_get_str(hdl,
+ OID_sunPlatBinarySensorInterpretFalse,
+ vol_props[ndx].row, &pstr, &snmp_syserr);
+ if (snmp_syserr == ECANCELED)
+ return (PICL_FAILURE);
+ if (ret < 0 || pstr == NULL) {
+ (void) strlcpy(buf, STR_ST_FALSE,
+ MAX_TRUTHVAL_LEN);
+ } else {
+ (void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
+ free(pstr);
+ }
+ } else {
+ log_msg(LOG_ERR, SNMPP_INV_PLAT_BINSNSR_CURRENT,
+ propval, vol_props[ndx].row);
+ return (PICL_FAILURE);
+ }
+ break;
+
+ case VPT_ALARMSTATE:
+ if (propval == SSAS_OFF) {
+ (void) strlcpy(buf, STR_SSAS_OFF, MAX_ALARMSTATE_LEN);
+ } else if (propval == SSAS_STEADY) {
+ (void) strlcpy(buf, STR_SSAS_STEADY,
+ MAX_ALARMSTATE_LEN);
+ } else if (propval == SSAS_ALTERNATING) {
+ (void) strlcpy(buf, STR_SSAS_ALTERNATING,
+ MAX_ALARMSTATE_LEN);
+ } else {
+ (void) strlcpy(buf, STR_SSAS_UNKNOWN,
+ MAX_ALARMSTATE_LEN);
+ }
+ break;
+
+ case VPT_BATTERYSTATUS:
+ switch (propval) {
+ case SSBS_OTHER:
+ (void) strlcpy(buf, STR_SSBS_OTHER,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_FULLYCHARGED:
+ (void) strlcpy(buf, STR_SSBS_FULLYCHARGED,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_LOW:
+ (void) strlcpy(buf, STR_SSBS_LOW,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_CRITICAL:
+ (void) strlcpy(buf, STR_SSBS_CRITICAL,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_CHARGING:
+ (void) strlcpy(buf, STR_SSBS_CHARGING,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_CHARGING_AND_LOW:
+ (void) strlcpy(buf, STR_SSBS_CHARGING_AND_LOW,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_CHARGING_AND_HIGH:
+ (void) strlcpy(buf, STR_SSBS_CHARGING_AND_HIGH,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_CHARGING_AND_CRITICAL:
+ (void) strlcpy(buf, STR_SSBS_CHARGING_AND_CRITICAL,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_UNDEFINED:
+ (void) strlcpy(buf, STR_SSBS_UNDEFINED,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_PARTIALLY_CHARGED:
+ (void) strlcpy(buf, STR_SSBS_PARTIALLY_CHARGED,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ case SSBS_UNKNOWN:
+ default:
+ (void) strlcpy(buf, STR_SSBS_UNKNOWN,
+ MAX_BATTERYSTATUS_LEN);
+ break;
+ }
+ break;
+ }
+
+ (void) rw_unlock(&stale_tree_rwlp);
+
+ return (PICL_SUCCESS);
+}
+
+static void
+threshold(picl_nodehdl_t node, char *oidstr, int row, char *propname,
+ int *snmp_syserr_p)
+{
+ picl_prophdl_t prop;
+ int err;
+ int val;
+
+ if (snmp_get_int(hdl, oidstr, row, &val, snmp_syserr_p) != -1) {
+ err = add_volatile_prop(node, propname, PICL_PTYPE_INT,
+ PICL_READ, sizeof (int), read_volprop, NULL, &prop);
+ if (err == PICL_SUCCESS)
+ save_volprop(prop, oidstr, row, VPT_NUMSENSOR);
+ }
+}
+
+static void
+add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p)
+{
+ uchar_t *bitstr = NULL;
+ uchar_t enabled;
+ uint_t nbytes;
+ int ret;
+
+ ret = snmp_get_bitstr(hdl, OID_sunPlatNumericSensorEnabledThresholds,
+ row, &bitstr, &nbytes, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+
+ if (ret < 0 || bitstr == NULL || nbytes > 2)
+ enabled = 0xff;
+ else if (nbytes == 1) {
+ /*
+ * The ALOM snmp agent doesn't adhere to the BER rules for
+ * encoding bit strings. While the BER states that bitstrings
+ * must begin from the second octet after length, and the
+ * first octet after length must indicate the number of unused
+ * bits in the last octet, the snmp agent simply sends the
+ * bitstring data as if it were octet string -- that is, the
+ * "unused bits" octet is missing.
+ */
+ enabled = bitstr[0];
+ } else if (nbytes == 2)
+ enabled = bitstr[1];
+
+ if (bitstr) {
+ free(bitstr);
+ }
+
+ if (enabled & LOWER_FATAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorLowerThresholdFatal, row,
+ PICL_PROP_LOW_POWER_OFF, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+ if (enabled & LOWER_CRITICAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorLowerThresholdCritical, row,
+ PICL_PROP_LOW_SHUTDOWN, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+ if (enabled & LOWER_NON_CRITICAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorLowerThresholdNonCritical, row,
+ PICL_PROP_LOW_WARNING, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+ if (enabled & UPPER_NON_CRITICAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorUpperThresholdNonCritical, row,
+ PICL_PROP_HIGH_POWER_OFF, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+ if (enabled & UPPER_CRITICAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorUpperThresholdCritical, row,
+ PICL_PROP_HIGH_SHUTDOWN, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+ if (enabled & UPPER_FATAL) {
+ threshold(node,
+ OID_sunPlatNumericSensorUpperThresholdFatal, row,
+ PICL_PROP_HIGH_WARNING, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ }
+}
+
+static char *
+get_slot_type(int row, int *snmp_syserr_p)
+{
+ char *p;
+ char *slott = NULL;
+ int ret;
+
+ ret = snmp_get_str(hdl, OID_sunPlatEquipmentHolderAcceptableTypes,
+ row, &p, snmp_syserr_p);
+ CHECK_LINKRESET(snmp_syserr_p, NULL)
+
+ if ((ret == 0) && p && *p) {
+ slott = p;
+ if ((p = strchr(slott, '\n')) != NULL)
+ *p = 0;
+ } else {
+ log_msg(LOG_WARNING, SNMPP_NO_SLOT_TYPE, row);
+ if (p) {
+ free(p);
+ }
+ }
+
+ return (slott);
+}
+
+/*
+ * Create and add the specified volatile property
+ */
+static int
+add_volatile_prop(picl_nodehdl_t node, char *name, int type, int access,
+ int size, int (*rdfunc)(ptree_rarg_t *, void *),
+ int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp)
+{
+ ptree_propinfo_t propinfo;
+ picl_prophdl_t prop;
+ int err;
+
+ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
+ type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_INIT_PROPINFO, err);
+ return (err);
+ }
+
+ err = ptree_create_and_add_prop(node, &propinfo, NULL, &prop);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_ADD_PROP, err, node);
+ return (err);
+ }
+
+ if (propp)
+ *propp = prop;
+
+ return (PICL_SUCCESS);
+}
+
+/*
+ * Add the specified string property to the node
+ */
+static int
+add_string_prop(picl_nodehdl_t node, char *propname, char *propval)
+{
+ ptree_propinfo_t propinfo;
+ int err;
+
+ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
+ PICL_PTYPE_CHARSTRING, PICL_READ, strlen(propval) + 1,
+ propname, NULL, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_INIT_STR_PROPINFO, err);
+ return (err);
+ }
+
+ err = ptree_create_and_add_prop(node, &propinfo, propval, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_ADD_STR_PROP, err, node);
+ return (err);
+ }
+
+ return (PICL_SUCCESS);
+}
+
+/*
+ * Add the specified void property to the node
+ */
+static int
+add_void_prop(picl_nodehdl_t node, char *propname)
+{
+ ptree_propinfo_t propinfo;
+ int err;
+
+ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
+ PICL_PTYPE_VOID, PICL_READ, 0, propname, NULL, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_INIT_VOID_PROPINFO, err);
+ return (err);
+ }
+
+ err = ptree_create_and_add_prop(node, &propinfo, NULL, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_ADD_VOID_PROP, err, node);
+ return (err);
+ }
+
+ return (PICL_SUCCESS);
+}
+
+static int
+add_int_prop(picl_nodehdl_t node, char *propname, int val)
+{
+ ptree_propinfo_t propinfo;
+ int propval = val;
+ int err;
+
+ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
+ PICL_PTYPE_INT, PICL_READ, sizeof (int),
+ propname, NULL, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_INIT_INT_PROPINFO, err);
+ return (err);
+ }
+
+ err = ptree_create_and_add_prop(node, &propinfo, &propval, NULL);
+ if (err != PICL_SUCCESS) {
+ log_msg(LOG_ERR, SNMPP_CANT_ADD_INT_PROP, err, node);
+ return (err);
+ }
+
+ return (PICL_SUCCESS);
+}
+
+static void
+add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
+ int row, sp_propid_t pp, int *snmp_syserr_p)
+{
+ char *serial_num;
+ char *slot_type;
+ char *fw_revision, *hw_revision;
+ char *mfg_name, *model_name;
+ char *phys_descr;
+ int val;
+ int ret;
+
+ switch (pp) {
+ case PP_SERIAL_NUM:
+ ret = snmp_get_str(hdl, OID_entPhysicalSerialNum,
+ row, &serial_num, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && serial_num && *serial_num) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_SERIAL_NUMBER, serial_num);
+ free((void *) serial_num);
+ }
+ break;
+
+ case PP_SLOT_TYPE:
+ if ((slot_type = get_slot_type(row, snmp_syserr_p)) == NULL) {
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ (void) add_string_prop(nodeh,
+ PICL_PROP_SLOT_TYPE, DEFAULT_SLOT_TYPE);
+ } else {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_SLOT_TYPE, slot_type);
+ free((void *) slot_type);
+ }
+ break;
+
+ case PP_STATE:
+ ret = add_volatile_prop(nodeh, PICL_PROP_STATE,
+ PICL_PTYPE_CHARSTRING, PICL_READ, MAX_ALARMSTATE_LEN,
+ read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatAlarmState, row,
+ VPT_ALARMSTATE);
+ }
+ break;
+
+ case PP_OPSTATUS:
+ ret = add_volatile_prop(nodeh, PICL_PROP_OPERATIONAL_STATUS,
+ PICL_PTYPE_CHARSTRING, PICL_READ, MAX_OPSTATE_LEN,
+ read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php,
+ OID_sunPlatEquipmentOperationalState, row,
+ VPT_PLATOPSTATE);
+ }
+ break;
+
+ case PP_BATT_STATUS:
+ ret = add_volatile_prop(nodeh, PICL_PROP_BATTERY_STATUS,
+ PICL_PTYPE_CHARSTRING, PICL_READ, MAX_BATTERYSTATUS_LEN,
+ read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatBatteryStatus, row,
+ VPT_BATTERYSTATUS);
+ }
+ break;
+
+ case PP_TEMPERATURE:
+ ret = add_volatile_prop(nodeh, PICL_PROP_TEMPERATURE,
+ PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
+ NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatNumericSensorCurrent,
+ row, VPT_NUMSENSOR);
+ }
+ break;
+
+ case PP_VOLTAGE:
+ ret = add_volatile_prop(nodeh, PICL_PROP_VOLTAGE,
+ PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
+ NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatNumericSensorCurrent,
+ row, VPT_NUMSENSOR);
+ }
+ break;
+
+ case PP_CURRENT:
+ ret = add_volatile_prop(nodeh, PICL_PROP_CURRENT,
+ PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
+ NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatNumericSensorCurrent,
+ row, VPT_NUMSENSOR);
+ }
+ break;
+
+ case PP_SPEED:
+ ret = add_volatile_prop(nodeh, PICL_PROP_SPEED, PICL_PTYPE_INT,
+ PICL_READ, sizeof (int), read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatNumericSensorCurrent,
+ row, VPT_NUMSENSOR);
+ }
+ break;
+
+ case PP_SENSOR_VALUE:
+ ret = add_volatile_prop(nodeh, PICL_PROP_SENSOR_VALUE,
+ PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
+ NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatNumericSensorCurrent,
+ row, VPT_NUMSENSOR);
+ }
+ break;
+
+ case PP_CONDITION:
+ ret = add_volatile_prop(nodeh, PICL_PROP_CONDITION,
+ PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
+ read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatBinarySensorCurrent,
+ row, VPT_BINSENSOR);
+ }
+ break;
+
+ case PP_EXPECTED:
+ ret = add_volatile_prop(nodeh, PICL_PROP_EXPECTED,
+ PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
+ read_volprop, NULL, php);
+ if (ret == PICL_SUCCESS) {
+ save_volprop(*php, OID_sunPlatBinarySensorExpected,
+ row, VPT_BINSENSOR);
+ }
+ break;
+
+ case PP_REPLACEABLE:
+ ret = snmp_get_int(hdl, OID_sunPlatCircuitPackReplaceable,
+ row, &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && (val == ST_TRUE))
+ (void) add_void_prop(nodeh, PICL_PROP_IS_REPLACEABLE);
+ break;
+
+ case PP_HOTSWAPPABLE:
+ ret = snmp_get_int(hdl, OID_sunPlatCircuitPackHotSwappable,
+ row, &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && (val == ST_TRUE))
+ (void) add_void_prop(nodeh, PICL_PROP_IS_HOT_SWAPPABLE);
+ break;
+
+ case PP_IS_FRU:
+ ret = snmp_get_int(hdl, OID_entPhysicalIsFRU, row,
+ &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && (val == ST_TRUE))
+ (void) add_void_prop(nodeh, PICL_PROP_IS_FRU);
+ break;
+
+ case PP_HW_REVISION:
+ ret = snmp_get_str(hdl, OID_entPhysicalHardwareRev,
+ row, &hw_revision, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && hw_revision && *hw_revision) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_HW_REVISION, hw_revision);
+ free((void *) hw_revision);
+ }
+ break;
+
+ case PP_FW_REVISION:
+ ret = snmp_get_str(hdl, OID_entPhysicalFirmwareRev,
+ row, &fw_revision, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && fw_revision && *fw_revision) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_FW_REVISION, fw_revision);
+ free((void *) fw_revision);
+ }
+ break;
+
+ case PP_MFG_NAME:
+ ret = snmp_get_str(hdl, OID_entPhysicalMfgName,
+ row, &mfg_name, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && mfg_name && *mfg_name) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_MFG_NAME, mfg_name);
+ free((void *) mfg_name);
+ }
+ break;
+
+ case PP_MODEL_NAME:
+ ret = snmp_get_str(hdl, OID_entPhysicalModelName,
+ row, &model_name, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && model_name && *model_name) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_MODEL_NAME, model_name);
+ free((void *) model_name);
+ }
+ break;
+
+ case PP_DESCRIPTION:
+ ret = snmp_get_str(hdl, OID_entPhysicalDescr,
+ row, &phys_descr, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && phys_descr && *phys_descr) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_PHYS_DESCRIPTION, phys_descr);
+ free((void *) phys_descr);
+ }
+ break;
+
+ case PP_LABEL:
+ if (label && *label)
+ (void) add_string_prop(nodeh, PICL_PROP_LABEL, label);
+ break;
+
+ case PP_BASE_UNITS:
+ ret = snmp_get_int(hdl, OID_sunPlatNumericSensorBaseUnits,
+ row, &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && (val > 0) && (val < n_baseunits)) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_BASE_UNITS, sensor_baseunits[val]);
+ }
+ break;
+
+ case PP_RATE_UNITS:
+ ret = snmp_get_int(hdl, OID_sunPlatNumericSensorRateUnits,
+ row, &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if ((ret == 0) && (val > 0) && (val < n_rateunits)) {
+ (void) add_string_prop(nodeh,
+ PICL_PROP_RATE_UNITS, sensor_rateunits[val]);
+ }
+ break;
+
+ case PP_EXPONENT:
+ ret = snmp_get_int(hdl, OID_sunPlatNumericSensorExponent,
+ row, &val, snmp_syserr_p);
+ CHECK_LINKRESET_VOID(snmp_syserr_p)
+ if (ret == 0)
+ (void) add_int_prop(nodeh, PICL_PROP_EXPONENT, val);
+ break;
+ }
+}
+
+/*VARARGS2*/
+static void
+log_msg(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(pri, fmt, ap);
+ va_end(ap);
+}
+
+#ifdef SNMPPLUGIN_DEBUG
+
+static void
+snmpplugin_log_init(void)
+{
+ (void) mutex_init(&snmpplugin_dbuf_lock, USYNC_THREAD, NULL);
+}
+
+static void
+snmpplugin_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) mutex_lock(&snmpplugin_dbuf_lock);
+
+ va_start(ap, fmt);
+ (void) vsnprintf(snmpplugin_lbuf, SNMPPLUGIN_DMAX_LINE, fmt, ap);
+ snmpplugin_log_append();
+ va_end(ap);
+
+ (void) mutex_unlock(&snmpplugin_dbuf_lock);
+}
+
+static void
+snmpplugin_log_append(void)
+{
+ int len;
+
+ len = strlen(snmpplugin_lbuf);
+
+ if ((snmpplugin_dbuf_curp + len) >=
+ (snmpplugin_dbuf + snmpplugin_dbuf_sz)) {
+ snmpplugin_dbuf_realloc();
+ if (snmpplugin_dbuf == NULL) {
+ (void) mutex_unlock(&snmpplugin_dbuf_lock);
+ return;
+ }
+ }
+
+ (void) strcpy(snmpplugin_dbuf_curp, snmpplugin_lbuf);
+ snmpplugin_dbuf_curp += len;
+}
+
+static void
+snmpplugin_dbuf_realloc(void)
+{
+ char *p;
+ size_t offset = 0;
+ size_t count;
+
+ count = snmpplugin_dbuf_sz + SNMPPLUGIN_DBLOCK_SZ;
+ if ((p = (char *)calloc(count, 1)) == NULL) {
+ snmpplugin_dbuf_overflow++;
+ snmpplugin_dbuf_curp = snmpplugin_dbuf;
+ return;
+ }
+
+ if (snmpplugin_dbuf) {
+ offset = snmpplugin_dbuf_curp - snmpplugin_dbuf;
+ (void) memcpy(p, snmpplugin_dbuf, snmpplugin_dbuf_sz);
+ free(snmpplugin_dbuf);
+ }
+
+ snmpplugin_dbuf = p;
+ snmpplugin_dbuf_sz += SNMPPLUGIN_DBLOCK_SZ;
+
+ snmpplugin_dbuf_curp = snmpplugin_dbuf + offset;
+}
+#endif
diff --git a/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.h b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.h
new file mode 100644
index 0000000000..b0ac66192c
--- /dev/null
+++ b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.h
@@ -0,0 +1,215 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SNMPPLUGIN_H
+#define _SNMPPLUGIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The /physical-platform node
+ */
+#define PICL_NODE_PHYSPLAT "physical-platform"
+
+/*
+ * List of volatile property OIDs to lookup and update when needed
+ */
+typedef struct {
+ picl_prophdl_t prop;
+ char *oidstr;
+ int row;
+ int proptype;
+} vol_prophdl_t;
+
+/*
+ * Types of volatile properties (proptype values)
+ */
+#define VPT_PLATOPSTATE 1
+#define VPT_NUMSENSOR 2
+#define VPT_BINSENSOR 3
+#define VPT_ALARMSTATE 4
+#define VPT_BATTERYSTATUS 5
+
+/*
+ * Storage related and miscellaneous definitions
+ */
+#define N_ELEMS_IN_VOLPROP_BLOCK 512
+#define N_ELEMS_IN_NODE_BLOCK 256
+#define NODE_BLOCK_SHIFT 8
+#define DEFAULT_SLOT_TYPE "slot"
+
+/*
+ * Local macros and property ids
+ */
+#define ADD_NODE(cl) \
+{ \
+ if (ptree_create_and_add_node(parenth, node_name, cl, \
+ &nodeh) != PICL_SUCCESS) { \
+ log_msg(LOG_ERR, SNMPP_ADD_NODE_FAIL, node_name, cl); \
+ return (NULL); \
+ } \
+}
+
+#define CHECK_LINKRESET(errp, retval) \
+ if ((errp) && (*errp == ECANCELED)) { \
+ return (retval); \
+ }
+
+#define CHECK_LINKRESET_VOID(errp) \
+ if ((errp) && (*errp == ECANCELED)) { \
+ return; \
+ }
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+typedef enum {
+ PP_SERIAL_NUM = 1,
+ PP_SLOT_TYPE,
+ PP_STATE,
+ PP_OPSTATUS,
+ PP_BATT_STATUS,
+ PP_TEMPERATURE,
+ PP_VOLTAGE,
+ PP_CURRENT,
+ PP_SPEED,
+ PP_SENSOR_VALUE,
+ PP_BASE_UNITS,
+ PP_EXPONENT,
+ PP_RATE_UNITS,
+ PP_CONDITION,
+ PP_EXPECTED,
+ PP_REPLACEABLE,
+ PP_HOTSWAPPABLE,
+ PP_IS_FRU,
+ PP_HW_REVISION,
+ PP_FW_REVISION,
+ PP_MFG_NAME,
+ PP_MODEL_NAME,
+ PP_DESCRIPTION,
+ PP_LABEL
+} sp_propid_t;
+
+/*
+ * Plugin global routines
+ */
+void snmpplugin_init(void);
+void snmpplugin_fini(void);
+
+/*
+ * Plugin messages
+ */
+#define SNMPP_NO_ROOT \
+ gettext("PICL snmpplugin: cannot get picl tree root (ret=%d)\n")
+
+#define SNMPP_CANT_INIT \
+ gettext("PICL snmpplugin: cannot initialize snmp service\n")
+
+#define SNMPP_CANT_CREATE_PHYSPLAT \
+ gettext("PICL snmpplugin: cannot create physical-platform root (ret=%d)\n")
+
+#define SNMPP_CANT_CREATE_TREE_BUILDER \
+ gettext("PICL snmpplugin: cannot create thr to handle hotplugs (ret=%d)\n")
+
+#define SNMPP_NO_ENTPHYSNAME \
+ gettext("PICL snmpplugin: cannot get entPhysicalName (row=%d)\n")
+
+#define SNMPP_ADD_NODE_FAIL \
+ gettext("PICL snmpplugin: couldn't add node %s (class=%d)\n")
+
+#define SNMPP_UNSUPP_SENSOR_CLASS \
+ gettext("PICL snmpplugin: sunPlatSensorClass %d unsupported (row=%d)\n")
+
+#define SNMPP_UNKNOWN_ENTPHYSCLASS \
+ gettext("PICL snmpplugin: entPhysicalClass %d unknown (row=%d)\n")
+
+#define SNMPP_NO_MEM \
+ gettext("PICL snmpplugin: failed to allocate %d bytes\n")
+
+#define SNMPP_CANT_FIND_VOLPROP \
+ gettext("PICL snmpplugin: cannot find volatile property (proph=%lx)\n")
+
+#define SNMPP_INV_PLAT_EQUIP_OPSTATE \
+ gettext("PICL snmpplugin: invalid sunPlatEquipmentOpState %d (row=%d)\n")
+
+#define SNMPP_INV_PLAT_BINSNSR_CURRENT \
+ gettext("PICL snmpplugin: invalid sunPlatBinarySensorCurrent %d (row=%d)\n")
+
+#define SNMPP_NO_SLOT_TYPE \
+ gettext("PICL snmpplugin: no acceptable slot types (row=%d)\n")
+
+#define SNMPP_CANT_INIT_PROPINFO \
+ gettext("PICL snmpplugin: cannot init picl propinfo (err=%d)\n")
+
+#define SNMPP_CANT_ADD_PROP \
+ gettext("PICL snmpplugin: cannot add property, err=%d (node=%lx)\n")
+
+#define SNMPP_CANT_INIT_STR_PROPINFO \
+ gettext("PICL snmpplugin: cannot init picl str propinfo (err=%d)\n")
+
+#define SNMPP_CANT_ADD_STR_PROP \
+ gettext("PICL snmpplugin: cannot add string property (err=%d, node=%lx)\n")
+
+#define SNMPP_CANT_INIT_VOID_PROPINFO \
+ gettext("PICL snmpplugin: cannot init picl void propinfo (err=%d)\n")
+
+#define SNMPP_CANT_ADD_VOID_PROP \
+ gettext("PICL snmpplugin: cannot add void property (err=%d, node=%lx)\n")
+
+#define SNMPP_CANT_INIT_INT_PROPINFO \
+ gettext("PICL snmpplugin: cannot init picl int propinfo (err=%d)\n")
+
+#define SNMPP_CANT_ADD_INT_PROP \
+ gettext("PICL snmpplugin: cannot add int property (err=%d, node=%lx)\n")
+
+#define SNMPP_CANT_FETCH_OBJECT_VAL \
+ gettext("PICL snmpplugin: cannot fetch object value (err=%d)\n")
+
+#define SNMPP_LINK_RESET \
+ gettext("PICL snmpplugin: snmp ds reset happened, rebuilding tree\n")
+
+#ifdef SNMPPLUGIN_DEBUG
+#define SNMPPLUGIN_DBLOCK_SZ 4096
+#define SNMPPLUGIN_DMAX_LINE 80
+#define LOGINIT() snmpplugin_log_init()
+#define LOGPRINTF(s) snmpplugin_log(s)
+#define LOGPRINTF1(s, a1) snmpplugin_log(s, a1)
+#define LOGPRINTF2(s, a1, a2) snmpplugin_log(s, a1, a2)
+#else
+#define LOGINIT()
+#define LOGPRINTF(s)
+#define LOGPRINTF1(s, a1)
+#define LOGPRINTF2(s, a1, a2)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNMPPLUGIN_H */
diff --git a/usr/src/lib/libprtdiag/Makefile.com b/usr/src/lib/libprtdiag/Makefile.com
index 3f6762338e..0375fd3fe9 100644
--- a/usr/src/lib/libprtdiag/Makefile.com
+++ b/usr/src/lib/libprtdiag/Makefile.com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -40,6 +40,7 @@ include $(SRC)/Makefile.psm
LIBS = $(DYNLIB) $(LINTLIB)
IFLAGS = -I ../../inc -I $(USR_PSM_INCL_DIR)
+IFLAGS += -I $(SRC)/cmd/picl/plugins/inc
IFLAGS += -I $(UTSBASE)/sun4u
IFLAGS += -I $(UTSBASE)/sun4u/sunfire
IFLAGS += -I $(UTSBASE)/sun4u/serengeti
diff --git a/usr/src/lib/libprtdiag/common/display_sun4v.c b/usr/src/lib/libprtdiag/common/display_sun4v.c
index 42d998a12c..c017266300 100644
--- a/usr/src/lib/libprtdiag/common/display_sun4v.c
+++ b/usr/src/lib/libprtdiag/common/display_sun4v.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,26 +43,87 @@
#include <libintl.h>
#include <syslog.h>
#include <sys/dkio.h>
+#include <sys/systeminfo.h>
+#include <picldefs.h>
#include "pdevinfo.h"
#include "display.h"
#include "display_sun4v.h"
#include "libprtdiag.h"
-
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
-extern int sys_clk;
+#define IOBOARD "IOBD"
+#define NETWORK "network"
+#define PCIE_COMPATIBLE_STR "pciex"
+#define PCIX_COMPATIBLE_STR "pci"
+#define SUN4V_MACHINE "sun4v"
+#define PARENT_NAMES 10
+
+/*
+ * Additional OBP properties
+ */
+#define OBP_PROP_COMPATIBLE "compatible"
+#define OBP_PROP_MODEL "model"
+#define OBP_PROP_SLOT_NAMES "slot-names"
+
+#define PICL_NODE_PHYSICAL_PLATFORM "physical-platform"
+#define PICL_NODE_CHASSIS "chassis"
+#define MEMORY_SIZE_FIELD 11
+#define INVALID_THRESHOLD 1000000
+
+/*
+ * Additional picl classes
+ */
+#ifndef PICL_CLASS_SUN4V
+#define PICL_CLASS_SUN4V "sun4v"
+#endif
+
+#ifndef PICL_PROP_NAC
+#define PICL_PROP_NAC "nac"
+#endif
+
+extern int sys_clk;
+extern picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *,
+ picl_nodehdl_t *);
+
+static picl_nodehdl_t rooth = 0, phyplatformh = 0;
+static picl_nodehdl_t chassish = 0;
+static int class_node_found;
+static int syserrlog;
+static int all_status_ok;
+
+/* local functions */
+static int sun4v_get_first_compatible_value(picl_nodehdl_t, char **);
+static void sun4v_display_memory_conf(picl_nodehdl_t);
+static void sun4v_disp_env_status();
+static void sun4v_env_print_fan_sensors();
+static void sun4v_env_print_fan_indicators();
+static void sun4v_env_print_temp_sensors();
+static void sun4v_env_print_temp_indicators();
+static void sun4v_env_print_current_sensors();
+static void sun4v_env_print_current_indicators();
+static void sun4v_env_print_voltage_sensors();
+static void sun4v_env_print_voltage_indicators();
+static void sun4v_env_print_LEDs();
+static void sun4v_print_fru_status();
+static void sun4v_print_fw_rev();
+static void sun4v_print_chassis_serial_no();
int
-sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog,
- picl_nodehdl_t plafh)
+sun4v_display(Sys_tree *tree, Prom_node *root, int log,
+ picl_nodehdl_t plafh)
{
- int exit_code = 0; /* init to all OK */
void *value; /* used for opaque PROM data */
struct mem_total memory_total; /* Total memory in system */
struct grp_info grps; /* Info on all groups in system */
+ char machine[MAXSTRLEN];
+
+ if (sysinfo(SI_MACHINE, machine, sizeof (machine)) == -1)
+ return (1);
+ if (strncmp(machine, SUN4V_MACHINE, strlen(SUN4V_MACHINE)) != 0)
+ return (1);
sys_clk = -1; /* System clock freq. (in MHz) */
@@ -79,11 +139,9 @@ sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog,
*/
(void) uname(&uts_buf);
- log_printf(
- dgettext(TEXT_DOMAIN, "System Configuration: "
- "Sun Microsystems %s %s\n"), uts_buf.machine,
- get_prop_val(find_prop(root,
- "banner-name")), 0);
+ log_printf(dgettext(TEXT_DOMAIN, "System Configuration: "
+ "Sun Microsystems %s %s\n"), uts_buf.machine,
+ get_prop_val(find_prop(root, "banner-name")), 0);
/* display system clock frequency */
value = get_prop_val(find_prop(root, "clock-frequency"));
@@ -100,15 +158,286 @@ sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog,
sun4v_display_cpu_devices(plafh);
/* Display the Memory configuration */
- sun4v_display_memoryconf(plafh);
+ class_node_found = 0;
+ sun4v_display_memory_conf(plafh);
/* Display all the IO cards. */
(void) sun4v_display_pci(plafh);
+ sun4v_display_diaginfo((log || (logging)), root, plafh);
- sun4v_display_diaginfo((syserrlog || (logging)), root, plafh);
+ if (picl_get_root(&rooth) != PICL_SUCCESS)
+ return (1);
+ if (sun4v_get_node_by_name(rooth, PICL_NODE_PHYSICAL_PLATFORM,
+ &phyplatformh) != PICL_SUCCESS)
+ return (1);
+
+ if (picl_find_node(phyplatformh, PICL_PROP_CLASSNAME,
+ PICL_PTYPE_CHARSTRING, (void *)PICL_CLASS_CHASSIS,
+ strlen(PICL_CLASS_CHASSIS), &chassish) != PICL_SUCCESS)
+ return (1);
+
+ syserrlog = log;
+ sun4v_disp_env_status();
+ }
+ return (0);
+}
+
+static void
+get_bus_type(picl_nodehdl_t nodeh, struct io_card *card)
+{
+ char *compatible;
+
+ (void) strcpy(card->bus_type, "PCIX");
+ if (sun4v_get_first_compatible_value(nodeh, &compatible)
+ == PICL_SUCCESS) {
+ if (strncmp(compatible, PCIE_COMPATIBLE_STR,
+ strlen(PCIE_COMPATIBLE_STR)) == 0)
+ (void) strcpy(card->bus_type, "PCIE");
+ free(compatible);
+ }
+}
+
+static picl_errno_t
+get_slot_label(picl_nodehdl_t nodeh, struct io_card *card)
+{
+ char val[PICL_PROPNAMELEN_MAX];
+ picl_errno_t err;
+
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val,
+ sizeof (val));
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ (void) strcpy(card->slot_str, val);
+ card->slot = -1;
+ return (PICL_SUCCESS);
+}
+
+static void
+get_slot_number(picl_nodehdl_t nodeh, struct io_card *card)
+{
+ picl_errno_t err;
+ picl_prophdl_t proph;
+ picl_propinfo_t pinfo;
+ picl_nodehdl_t pnodeh;
+ uint8_t *pval;
+ uint32_t dev_mask;
+ char uaddr[MAXSTRLEN];
+ int i;
+
+ if (get_slot_label(nodeh, card) == PICL_SUCCESS)
+ return;
+ err = PICL_SUCCESS;
+ while (err == PICL_SUCCESS) {
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &pnodeh,
+ sizeof (pnodeh)) != PICL_SUCCESS) {
+ (void) strcpy(card->slot_str, IOBOARD);
+ card->slot = -1;
+ return;
+ }
+ if (picl_get_propinfo_by_name(pnodeh, OBP_PROP_SLOT_NAMES,
+ &pinfo, &proph) == PICL_SUCCESS) {
+ break;
+ }
+ nodeh = pnodeh;
+ }
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_UNIT_ADDRESS, uaddr,
+ sizeof (uaddr)) != PICL_SUCCESS) {
+ (void) strcpy(card->slot_str, IOBOARD);
+ card->slot = -1;
+ return;
+ }
+ pval = (uint8_t *)malloc(pinfo.size);
+ if (!pval) {
+ (void) strcpy(card->slot_str, IOBOARD);
+ card->slot = -1;
+ return;
+ }
+ if (picl_get_propval(proph, pval, pinfo.size) != PICL_SUCCESS) {
+ (void) strcpy(card->slot_str, IOBOARD);
+ card->slot = -1;
+ free(pval);
+ return;
}
- return (exit_code);
+ dev_mask = 0;
+ for (i = 0; i < sizeof (dev_mask); i++)
+ dev_mask |= (*(pval+i) << 8*(sizeof (dev_mask)-1-i));
+ for (i = 0; i < sizeof (uaddr) && uaddr[i] != '\0'; i++) {
+ if (uaddr[i] == ',') {
+ uaddr[i] = '\0';
+ break;
+ }
+ }
+ card->slot = atol(uaddr);
+ if (((1 << card->slot) & dev_mask) == 0) {
+ (void) strcpy(card->slot_str, IOBOARD);
+ card->slot = -1;
+ } else {
+ char *p = (char *)(pval+sizeof (dev_mask));
+ int shift = sizeof (uint32_t)*8-1-card->slot;
+ uint32_t x = (dev_mask << shift) >> shift;
+ int count = 0; /* count # of 1's in x */
+ int i = 0;
+ while (x != 0) {
+ count++;
+ x &= x-1;
+ }
+ while (count > 1) {
+ while (p[i++] != '\0');
+ count--;
+ }
+ (void) strcpy(card->slot_str, (char *)(p+i));
+ }
+ free(pval);
+}
+
+/*
+ * add all io devices under pci in io list
+ */
+/* ARGSUSED */
+static int
+sun4v_pci_callback(picl_nodehdl_t pcih, void *args)
+{
+ char path[PICL_PROPNAMELEN_MAX];
+ char class[PICL_CLASSNAMELEN_MAX];
+ char name[PICL_PROPNAMELEN_MAX];
+ char model[PICL_PROPNAMELEN_MAX];
+ char binding_name[PICL_PROPNAMELEN_MAX];
+ char val[PICL_PROPNAMELEN_MAX];
+ char *compatible;
+ picl_errno_t err;
+ picl_nodehdl_t nodeh;
+ struct io_card pci_card;
+
+ /* Walk through the children */
+
+ err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
+ sizeof (picl_nodehdl_t));
+
+ while (err == PICL_SUCCESS) {
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
+ class, sizeof (class));
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ if (args) {
+ char *val = args;
+ if (strcmp(class, val) == 0) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_PEER, &nodeh,
+ sizeof (picl_nodehdl_t));
+ continue;
+ } else if (strcmp(val, PICL_CLASS_PCIEX) == 0 &&
+ strcmp(class, PICL_CLASS_PCI) == 0) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_PEER, &nodeh,
+ sizeof (picl_nodehdl_t));
+ continue;
+ } else if (strcmp(val, PICL_CLASS_PCI) == 0 &&
+ strcmp(class, PICL_CLASS_PCIEX) == 0) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_PEER, &nodeh,
+ sizeof (picl_nodehdl_t));
+ continue;
+ }
+ }
+
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
+ path, sizeof (path));
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ (void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes));
+
+ get_bus_type(nodeh, &pci_card);
+ get_slot_number(nodeh, &pci_card);
+
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, name,
+ sizeof (name));
+ if (err == PICL_PROPNOTFOUND)
+ (void) strcpy(name, "");
+ else if (err != PICL_SUCCESS)
+ return (err);
+
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_STATUS, val,
+ sizeof (val));
+ if (err == PICL_PROPNOTFOUND)
+ (void) strcpy(val, "");
+ else if (err != PICL_SUCCESS)
+ return (err);
+
+ /* Figure NAC name */
+ if (pci_card.slot != -1)
+ (void) snprintf(pci_card.status,
+ sizeof (pci_card.status),
+ "%s%d", pci_card.slot_str,
+ pci_card.slot);
+ else
+ (void) snprintf(pci_card.status,
+ sizeof (pci_card.status),
+ "%s", pci_card.slot_str);
+
+ /*
+ * Get the name of this card. If binding_name is found,
+ * name will be <nodename>-<binding_name>.
+ */
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME,
+ binding_name, sizeof (binding_name));
+ if (err == PICL_SUCCESS) {
+ if (strcmp(name, binding_name) != 0) {
+ (void) strlcat(name, "-", sizeof (name));
+ (void) strlcat(name, binding_name,
+ sizeof (name));
+ }
+ } else if (err == PICL_PROPNOTFOUND) {
+ /*
+ * if compatible prop is not found, name will be
+ * <nodename>-<compatible>
+ */
+ err = sun4v_get_first_compatible_value(nodeh,
+ &compatible);
+ if (err == PICL_SUCCESS) {
+ (void) strlcat(name, "-", sizeof (name));
+ (void) strlcat(name, compatible, sizeof (name));
+ free(compatible);
+ }
+ } else
+ return (err);
+
+ (void) strlcpy(pci_card.name, name, sizeof (pci_card.name));
+
+ /* Get the model of this card */
+
+ err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL,
+ model, sizeof (model));
+ if (err == PICL_PROPNOTFOUND)
+ (void) strcpy(model, "");
+ else if (err != PICL_SUCCESS)
+ return (err);
+ (void) strlcpy(pci_card.model, model, sizeof (pci_card.model));
+
+ /* Print NAC name */
+ log_printf("%-12s", pci_card.status);
+ /* Print IO Type */
+ log_printf("%-6s", pci_card.bus_type);
+ /* Printf Card Name */
+ log_printf("%-46s", pci_card.name);
+ /* Print Card Model */
+ log_printf("%-8s", pci_card.model);
+ log_printf("\n");
+ /* Print Status */
+ log_printf("%-12s", val);
+ /* Print IO Type */
+ log_printf("%-6s", "");
+ /* Print Parent Path */
+ log_printf("%-46s", pci_card.notes);
+ log_printf("\n");
+
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh,
+ sizeof (picl_nodehdl_t));
+ }
+ return (PICL_WALK_CONTINUE);
}
/*
@@ -118,29 +447,229 @@ sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog,
void
sun4v_display_pci(picl_nodehdl_t plafh)
{
-#ifdef lint
- plafh = plafh;
-#endif
- /*
- * This function is intentionally empty
- */
+ char *fmt = "%-11s %-5s %-45s %-8s";
+ /* Have we printed the column headings? */
+ static int banner = FALSE;
+
+ if (banner == FALSE) {
+ log_printf("\n");
+ log_printf("============================");
+ log_printf(" IO Devices ");
+ log_printf("============================");
+ log_printf("\n");
+ log_printf(fmt, "Slot +", "Bus", "Name +", "Model", 0);
+ log_printf("\n");
+ log_printf(fmt, "Status", "Type", "Path", "", 0);
+ log_printf("\n");
+ log_printf("---------------------------------"
+ "------------------------------------\n");
+ banner = TRUE;
+ }
+
+ (void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCIEX,
+ PICL_CLASS_PCIEX, sun4v_pci_callback);
+ (void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCI,
+ PICL_CLASS_PCI, sun4v_pci_callback);
+ (void) picl_walk_tree_by_class(plafh, PICL_CLASS_SUN4V,
+ PICL_CLASS_SUN4V, sun4v_pci_callback);
}
+/*
+ * return the first compatible value
+ */
+static int
+sun4v_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
+{
+ picl_errno_t err;
+ picl_prophdl_t proph;
+ picl_propinfo_t pinfo;
+ picl_prophdl_t tblh;
+ picl_prophdl_t rowproph;
+ char *pval;
+
+ err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE,
+ &pinfo, &proph);
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ if (pinfo.type == PICL_PTYPE_CHARSTRING) {
+ pval = malloc(pinfo.size);
+ if (pval == NULL)
+ return (PICL_FAILURE);
+ err = picl_get_propval(proph, pval, pinfo.size);
+ if (err != PICL_SUCCESS) {
+ free(pval);
+ return (err);
+ }
+ *outbuf = pval;
+ return (PICL_SUCCESS);
+ }
+
+ if (pinfo.type != PICL_PTYPE_TABLE)
+ return (PICL_FAILURE);
+
+ /* get first string from table */
+ err = picl_get_propval(proph, &tblh, pinfo.size);
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ err = picl_get_next_by_row(tblh, &rowproph);
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ err = picl_get_propinfo(rowproph, &pinfo);
+ if (err != PICL_SUCCESS)
+ return (err);
+
+ pval = malloc(pinfo.size);
+ if (pval == NULL)
+ return (PICL_FAILURE);
+
+ err = picl_get_propval(rowproph, pval, pinfo.size);
+ if (err != PICL_SUCCESS) {
+ free(pval);
+ return (err);
+ }
+
+ *outbuf = pval;
+ return (PICL_SUCCESS);
+}
+
+/*
+ * print size of a memory segment
+ */
+static void
+print_memory_segment_size(uint64_t size)
+{
+ uint64_t kbyte = 1024;
+ uint64_t mbyte = kbyte * kbyte;
+ uint64_t gbyte = kbyte * mbyte;
+ char buf[MEMORY_SIZE_FIELD];
+
+ if (size >= gbyte) {
+ if (size % gbyte == 0)
+ (void) snprintf(buf, sizeof (buf), "%d GB",
+ (int)(size / gbyte));
+ else
+ (void) snprintf(buf, sizeof (buf), "%.2f GB",
+ (float)size / gbyte);
+ } else if (size >= mbyte) {
+ if (size % mbyte == 0)
+ (void) snprintf(buf, sizeof (buf), "%d MB",
+ (int)(size / mbyte));
+ else
+ (void) snprintf(buf, sizeof (buf), "%.2f MB",
+ (float)size / mbyte);
+ } else {
+ if (size % kbyte == 0)
+ (void) snprintf(buf, sizeof (buf), "%d KB",
+ (int)(size / kbyte));
+ else
+ (void) snprintf(buf, sizeof (buf), "%.2f KB",
+ (float)size / kbyte);
+ }
+ log_printf("%-7s ", buf);
+}
+
+/*
+ * print bank IDs of a memory segment
+ */
+static void
+print_memory_segment_contain(picl_nodehdl_t nodeh)
+{
+ char val[PICL_PROPNAMELEN_MAX];
+ picl_errno_t err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_NAC, val, sizeof (val));
+ if (err != PICL_SUCCESS)
+ return;
+ log_printf("%-30s", val);
+ while ((err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_PEER, &nodeh,
+ sizeof (nodeh))) == PICL_SUCCESS) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_NAC, val, sizeof (val));
+ if (err == PICL_SUCCESS) {
+ log_printf("\n");
+ log_printf("%-30s", val);
+ }
+ }
+}
+
+/*
+ * Search node where _class=="memory-segment"
+ * print "Base Address", "Size", etc
+ */
+/*ARGSUSED*/
+static int
+sun4v_memory_conf_callback(picl_nodehdl_t nodeh, void *args)
+{
+ uint64_t base;
+ uint64_t size;
+ uint64_t ifactor;
+ picl_errno_t err = PICL_SUCCESS;
+
+ if (class_node_found == 0) {
+ class_node_found = 1;
+ return (PICL_WALK_TERMINATE);
+ }
+ while (err == PICL_SUCCESS) {
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_BASEADDRESS,
+ &base, sizeof (base));
+ if (err != PICL_SUCCESS)
+ break;
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_SIZE,
+ &size, sizeof (size));
+ if (err != PICL_SUCCESS)
+ break;
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_INTERLEAVE_FACTOR, &ifactor,
+ sizeof (ifactor));
+ if (err != PICL_SUCCESS)
+ break;
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD,
+ &nodeh, sizeof (nodeh));
+ if (err != PICL_SUCCESS)
+ break;
+ log_printf("%-13llx", base);
+ print_memory_segment_size(size);
+ log_printf("%-18d", ifactor);
+ print_memory_segment_contain(nodeh);
+ log_printf("\n");
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh,
+ sizeof (picl_nodehdl_t));
+ }
+
+ return (PICL_WALK_CONTINUE);
+}
+
+/*ARGSUSED*/
void
-sun4v_display_memoryconf(picl_nodehdl_t plafh)
+sun4v_display_memory_conf(picl_nodehdl_t plafh)
{
-#ifdef lint
- plafh = plafh;
-#endif
- /*
- * This function is intentionally empty
- */
+ char *fmt = "%-12s %-7s %-9s %-20s";
+ (void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT,
+ NULL, sun4v_memory_conf_callback);
+ if (class_node_found == 0)
+ return;
+ log_printf("\n");
+ log_printf("============================");
+ log_printf(" Memory Configuration ");
+ log_printf("============================");
+ log_printf("\n");
+ log_printf("Segment Table:\n");
+ log_printf("-----------------------------------------------\n");
+ log_printf(fmt, "Base Address", "Size", "Interleave Factor",
+ "Contains", 0);
+ log_printf("\n");
+ log_printf("-----------------------------------------------\n");
+ (void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT,
+ NULL, sun4v_memory_conf_callback);
}
void
sun4v_display_cpu_devices(picl_nodehdl_t plafh)
{
- char *fmt = "%-12s %-5s %-8s %-19s %-5s";
+ char *fmt = "%-12s %-5s %-8s %-19s %-5s";
/*
* Display the table header for CPUs . Then display the CPU
@@ -156,15 +685,13 @@ sun4v_display_cpu_devices(picl_nodehdl_t plafh)
log_printf(fmt, "", "", "", "CPU", "CPU", 0);
log_printf("\n");
log_printf(fmt, "Location", "CPU", "Freq",
- "Implementation", "Mask", 0);
+ "Implementation", "Mask", 0);
log_printf("\n");
log_printf(fmt, "------------", "-----", "--------",
- "-------------------", "-----", 0);
+ "-------------------", "-----", 0);
log_printf("\n");
(void) picl_walk_tree_by_class(plafh, "cpu", "cpu", sun4v_display_cpus);
-
- log_printf("\n");
}
/*
@@ -175,16 +702,16 @@ int
sun4v_display_cpus(picl_nodehdl_t cpuh, void* args)
{
int status;
- picl_prophdl_t proph;
- picl_prophdl_t tblh;
- picl_prophdl_t rowproph;
+ picl_prophdl_t proph;
+ picl_prophdl_t tblh;
+ picl_prophdl_t rowproph;
picl_propinfo_t propinfo;
- int *int_value;
- uint64_t cpuid, mask_no;
- char *comp_value;
- char *no_prop_value = " ";
- char freq_str[MAXSTRLEN];
- char fru_name[MAXSTRLEN];
+ int *int_value;
+ uint64_t cpuid, mask_no;
+ char *comp_value;
+ char *no_prop_value = " ";
+ char freq_str[MAXSTRLEN];
+ char fru_name[MAXSTRLEN];
/*
* Get cpuid property and print it and the NAC name
@@ -193,44 +720,44 @@ sun4v_display_cpus(picl_nodehdl_t cpuh, void* args)
if (status == PICL_SUCCESS) {
status = picl_get_propval(proph, &cpuid, sizeof (cpuid));
if (status != PICL_SUCCESS) {
- log_printf("%-12s", no_prop_value);
- log_printf("%6s", no_prop_value);
+ log_printf("%-13s", no_prop_value);
+ log_printf("%-6s", no_prop_value);
} else {
(void) snprintf(fru_name, sizeof (fru_name), "%s%d",
CPU_STRAND_NAC, (int)cpuid);
- log_printf("%-12s", fru_name);
- log_printf("%6d", (int)cpuid);
+ log_printf("%-13s", fru_name);
+ log_printf("%-6d", (int)cpuid);
}
} else {
- log_printf("%-12s", no_prop_value);
- log_printf("%6s", no_prop_value);
+ log_printf("%-13s", no_prop_value);
+ log_printf("%-6s", no_prop_value);
}
clock_freq:
status = picl_get_propinfo_by_name(cpuh, "clock-frequency", &propinfo,
- &proph);
+ &proph);
if (status == PICL_SUCCESS) {
int_value = malloc(propinfo.size);
if (int_value == NULL) {
- log_printf("%9s", no_prop_value);
+ log_printf("%-9s", no_prop_value);
goto compatible;
}
status = picl_get_propval(proph, int_value, propinfo.size);
if (status != PICL_SUCCESS) {
- log_printf("%9s", no_prop_value);
+ log_printf("%-9s", no_prop_value);
} else {
/* Running frequency */
(void) snprintf(freq_str, sizeof (freq_str), "%d MHz",
CLK_FREQ_TO_MHZ(*int_value));
- log_printf("%9s", freq_str);
+ log_printf("%-9s", freq_str);
}
free(int_value);
} else
- log_printf("%9s", no_prop_value);
+ log_printf("%-9s", no_prop_value);
compatible:
status = picl_get_propinfo_by_name(cpuh, "compatible", &propinfo,
- &proph);
+ &proph);
if (status == PICL_SUCCESS) {
if (propinfo.type == PICL_PTYPE_CHARSTRING) {
/*
@@ -238,72 +765,65 @@ compatible:
*/
comp_value = malloc(propinfo.size);
if (comp_value == NULL) {
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
goto mask;
}
status = picl_get_propval(proph, comp_value,
- propinfo.size);
- if (status == PICL_SUCCESS) {
- log_printf("%20s", no_prop_value, 0);
- free(comp_value);
- }
+ propinfo.size);
+ if (status != PICL_SUCCESS)
+ log_printf("%-20s", no_prop_value, 0);
+ else
+ log_printf("%-20s", comp_value, 0);
+ free(comp_value);
} else if (propinfo.type == PICL_PTYPE_TABLE) {
/*
* Compatible Property has multiple values
*/
status = picl_get_propval(proph, &tblh, propinfo.size);
if (status != PICL_SUCCESS) {
- printf("Failed getting tblh\n");
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
goto mask;
}
status = picl_get_next_by_row(tblh, &rowproph);
if (status != PICL_SUCCESS) {
- printf("Failed getting next by row\n");
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
goto mask;
}
status = picl_get_propinfo(rowproph, &propinfo);
if (status != PICL_SUCCESS) {
- printf("Failed getting prop for rowproph\n");
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
goto mask;
}
comp_value = malloc(propinfo.size);
if (comp_value == NULL) {
- printf("Failed to get malloc value?\n");
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
goto mask;
}
-
status = picl_get_propval(rowproph, comp_value,
- propinfo.size);
- if (status != PICL_SUCCESS) {
- printf("Failed geting rowproph\n");
- log_printf("%20s", no_prop_value, 0);
- free(comp_value);
- goto mask;
- } else
- log_printf("%20s", comp_value, 0);
+ propinfo.size);
+ if (status != PICL_SUCCESS)
+ log_printf("%-20s", no_prop_value, 0);
+ else
+ log_printf("%-20s", comp_value, 0);
free(comp_value);
}
} else
- log_printf("%20s", no_prop_value, 0);
+ log_printf("%-20s", no_prop_value, 0);
mask:
status = picl_get_propinfo_by_name(cpuh, "mask#", &propinfo, &proph);
if (status == PICL_SUCCESS) {
status = picl_get_propval(proph, &mask_no, sizeof (mask_no));
if (status != PICL_SUCCESS) {
- log_printf("%9s", no_prop_value);
+ log_printf("%-9s", no_prop_value);
} else {
log_printf(dgettext(TEXT_DOMAIN, " %2d.%d"),
- (mask_no>> 4) & 0xf, mask_no & 0xf);
+ (mask_no>> 4) & 0xf, mask_no & 0xf);
}
} else
- log_printf("%9s", no_prop_value);
+ log_printf("%-9s", no_prop_value);
done:
log_printf("\n");
@@ -328,3 +848,701 @@ display_boardnum(int num)
{
log_printf("%2d ", num, 0);
}
+
+static void
+sun4v_disp_env_status()
+{
+ if (phyplatformh == 0)
+ return;
+ log_printf("\n");
+ log_printf("============================");
+ log_printf(" Environmental Status ");
+ log_printf("============================");
+ log_printf("\n");
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_fan_sensors();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_fan_indicators();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_temp_sensors();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_temp_indicators();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_current_sensors();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_current_indicators();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_voltage_sensors();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_env_print_voltage_indicators();
+
+ class_node_found = 0;
+ sun4v_env_print_LEDs();
+
+ class_node_found = 0;
+ all_status_ok = 1;
+ sun4v_print_fru_status();
+
+ class_node_found = 0;
+ sun4v_print_fw_rev();
+
+ sun4v_print_chassis_serial_no();
+}
+
+/*ARGSUSED*/
+static int
+sun4v_env_print_sensor_callback(picl_nodehdl_t nodeh, void *args)
+{
+ char val[PICL_PROPNAMELEN_MAX];
+ picl_nodehdl_t parenth;
+ char *names[PARENT_NAMES];
+ char *loc;
+ int i;
+ char *prop;
+ picl_errno_t err;
+ int32_t lo_warning, lo_shutdown;
+ int32_t hi_warning, hi_shutdown;
+ int32_t current_val;
+
+ if (class_node_found == 0) {
+ class_node_found = 1;
+ return (PICL_WALK_TERMINATE);
+ }
+
+ if (syserrlog == 0) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_OPERATIONAL_STATUS, val,
+ sizeof (val));
+ if (err == PICL_SUCCESS) {
+ if (strcmp(val, "disabled") == 0) {
+ if (all_status_ok) {
+ all_status_ok = 0;
+ return (PICL_WALK_TERMINATE);
+ }
+ } else
+ return (PICL_WALK_CONTINUE);
+ } else {
+ all_status_ok = 0;
+ return (PICL_WALK_TERMINATE);
+ }
+ }
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
+ sizeof (parenth));
+ if (err != PICL_SUCCESS) {
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+ }
+ if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL)
+ return (PICL_WALK_TERMINATE);
+ for (i = 0; i < PARENT_NAMES; i++)
+ if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) {
+ while (--i > -1)
+ free(names[i]);
+ free(loc);
+ return (PICL_WALK_TERMINATE);
+ }
+ i = 0;
+ while (err == PICL_SUCCESS) {
+ if (parenth == chassish || parenth == phyplatformh)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
+ names[i++], PICL_PROPNAMELEN_MAX);
+ if (err != PICL_SUCCESS) {
+ i--;
+ break;
+ }
+ if (i == PARENT_NAMES)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT,
+ &parenth, sizeof (parenth));
+ }
+ loc[0] = '\0';
+ if (--i > -1)
+ loc = strncat(loc, names[i], strlen(names[i]));
+ while (--i > -1) {
+ loc = strncat(loc, "/", 1);
+ loc = strncat(loc, names[i], strlen(names[i]));
+ }
+ log_printf("%-12s", loc);
+ for (i = 0; i < PARENT_NAMES; i++)
+ free(names[i]);
+ free(loc);
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val,
+ sizeof (val));
+ if (err == PICL_SUCCESS)
+ log_printf("%-15s", val);
+
+ prop = (char *)args;
+ if (!prop) {
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+ }
+ if (picl_get_propval_by_name(nodeh, prop, &current_val,
+ sizeof (current_val)) != PICL_SUCCESS) {
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+ }
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_WARNING,
+ &lo_warning, sizeof (lo_warning)) != PICL_SUCCESS)
+ lo_warning = INVALID_THRESHOLD;
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_SHUTDOWN,
+ &lo_shutdown, sizeof (lo_shutdown)) != PICL_SUCCESS)
+ lo_shutdown = INVALID_THRESHOLD;
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_WARNING,
+ &hi_warning, sizeof (hi_warning)) != PICL_SUCCESS)
+ hi_warning = INVALID_THRESHOLD;
+ if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_SHUTDOWN,
+ &hi_shutdown, sizeof (hi_shutdown)) != PICL_SUCCESS)
+ hi_shutdown = INVALID_THRESHOLD;
+
+ if ((lo_shutdown != INVALID_THRESHOLD &&
+ current_val <= lo_shutdown) ||
+ (hi_shutdown != INVALID_THRESHOLD &&
+ current_val >= hi_shutdown)) {
+ log_printf("%-s", "failed (");
+ log_printf("%-d", current_val);
+ log_printf("%-s", ")");
+ } else if ((lo_warning != INVALID_THRESHOLD &&
+ current_val <= lo_warning) ||
+ (hi_warning != INVALID_THRESHOLD &&
+ current_val >= hi_warning)) {
+ log_printf("%-s", "warning (");
+ log_printf("%-d", current_val);
+ log_printf("%-s", ")");
+ } else
+ log_printf("%-s", "ok");
+
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+sun4v_env_print_indicator_callback(picl_nodehdl_t nodeh, void *args)
+{
+ char val[PICL_PROPNAMELEN_MAX];
+ char status[PICL_PROPNAMELEN_MAX];
+ picl_nodehdl_t parenth;
+ char *names[PARENT_NAMES];
+ char *loc;
+ int i = 0;
+ char *prop = (char *)args;
+ picl_errno_t err = PICL_SUCCESS;
+
+ if (class_node_found == 0) {
+ class_node_found = 1;
+ return (PICL_WALK_TERMINATE);
+ }
+ if (syserrlog == 0) {
+ err = picl_get_propval_by_name(nodeh,
+ PICL_PROP_OPERATIONAL_STATUS, status,
+ sizeof (status));
+ if (err == PICL_SUCCESS) {
+ if (strcmp(status, "disabled") == 0) {
+ if (all_status_ok) {
+ all_status_ok = 0;
+ return (PICL_WALK_TERMINATE);
+ }
+ } else
+ return (PICL_WALK_CONTINUE);
+ } else {
+ all_status_ok = 0;
+ return (PICL_WALK_TERMINATE);
+ }
+ }
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
+ sizeof (parenth));
+ if (err != PICL_SUCCESS) {
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+ }
+ if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL)
+ return (PICL_WALK_TERMINATE);
+ for (i = 0; i < PARENT_NAMES; i++)
+ if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) {
+ while (--i > -1)
+ free(names[i]);
+ free(loc);
+ return (PICL_WALK_TERMINATE);
+ }
+ i = 0;
+ while (err == PICL_SUCCESS) {
+ if (parenth == chassish || parenth == phyplatformh)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
+ names[i++], PICL_PROPNAMELEN_MAX);
+ if (err != PICL_SUCCESS) {
+ i--;
+ break;
+ }
+ if (i == PARENT_NAMES)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT,
+ &parenth, sizeof (parenth));
+ }
+ loc[0] = '\0';
+ if (--i > -1)
+ loc = strncat(loc, names[i], strlen(names[i]));
+ while (--i > -1) {
+ loc = strncat(loc, "/", 1);
+ loc = strncat(loc, names[i], strlen(names[i]));
+ }
+ log_printf("%-12s", loc);
+ for (i = 0; i < PARENT_NAMES; i++)
+ free(names[i]);
+ free(loc);
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val,
+ sizeof (val));
+ if (err == PICL_SUCCESS)
+ log_printf("%-15s", val);
+ if (syserrlog == 0) {
+ log_printf("%-8s", status);
+ return (PICL_WALK_CONTINUE);
+ }
+ err = picl_get_propval_by_name(nodeh, prop, val,
+ sizeof (val));
+ if (err == PICL_SUCCESS)
+ log_printf("%-8s", val);
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+}
+
+static void
+sun4v_env_print_fan_sensors()
+{
+ char *fmt = "%-11s %-14s %-10s\n";
+ /*
+ * If there isn't any fan sensor node, return now.
+ */
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_RPM_SENSOR, (void *)PICL_CLASS_RPM_SENSOR,
+ sun4v_env_print_sensor_callback);
+ if (!class_node_found)
+ return;
+ log_printf("Fan sensors:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_RPM_SENSOR,
+ NULL, sun4v_env_print_sensor_callback);
+ if (all_status_ok) {
+ log_printf("All fan sensors are OK.\n");
+ return;
+ }
+ }
+ log_printf("---------------------------------\n");
+ log_printf(fmt, "Location", "Sensor", "Status", 0);
+ log_printf("---------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_SENSOR,
+ PICL_PROP_SPEED, sun4v_env_print_sensor_callback);
+}
+
+static void
+sun4v_env_print_fan_indicators()
+{
+ char *fmt = "%-11s %-14s %-10s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_RPM_INDICATOR, (void *)PICL_CLASS_RPM_INDICATOR,
+ sun4v_env_print_indicator_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nFan indicators:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_RPM_INDICATOR,
+ NULL, sun4v_env_print_indicator_callback);
+ if (all_status_ok) {
+ log_printf("All fan indicators are OK.\n");
+ return;
+ }
+ }
+ log_printf("------------------------------------\n");
+ log_printf(fmt, "Location", "Sensor", "Condition", 0);
+ log_printf("------------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_INDICATOR,
+ PICL_CLASS_RPM_INDICATOR, sun4v_env_print_indicator_callback);
+}
+
+static void
+sun4v_env_print_temp_sensors()
+{
+ char *fmt = "%-11s %-14s %-10s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_SENSOR,
+ (void *)PICL_PROP_TEMPERATURE,
+ sun4v_env_print_sensor_callback);
+ if (!class_node_found)
+ return;
+
+ log_printf("\nTemperature sensors:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_SENSOR,
+ NULL, sun4v_env_print_sensor_callback);
+ if (all_status_ok) {
+ log_printf("All temperature sensors are OK.\n");
+ return;
+ }
+ }
+ log_printf("---------------------------------\n");
+ log_printf(fmt, "Location", "Sensor", "Status", 0);
+ log_printf("---------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_SENSOR,
+ (void *)PICL_PROP_TEMPERATURE, sun4v_env_print_sensor_callback);
+}
+
+static void
+sun4v_env_print_temp_indicators()
+{
+ char *fmt = "%-11s %-14s %-8s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_INDICATOR, (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nTemperature indicators:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_INDICATOR, NULL,
+ sun4v_env_print_indicator_callback);
+ if (all_status_ok) {
+ log_printf("All temperature indicators are OK.\n");
+ return;
+ }
+ }
+ log_printf("------------------------------\n");
+ log_printf(fmt, "Location", "Indicator", "Condition", 0);
+ log_printf("------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_TEMPERATURE_INDICATOR,
+ (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+}
+
+static void
+sun4v_env_print_current_sensors()
+{
+ char *fmt = "%-11s %-14s %-10s\n";
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_CURRENT_SENSOR,
+ (void *)PICL_PROP_CURRENT, sun4v_env_print_sensor_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nCurrent sensors:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_CURRENT_SENSOR,
+ NULL, sun4v_env_print_sensor_callback);
+ if (all_status_ok) {
+ log_printf("All current sensors are OK.\n");
+ return;
+ }
+ }
+ log_printf("---------------------------------\n");
+ log_printf(fmt, "Location", "Sensor", "Status", 0);
+ log_printf("---------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_CURRENT_SENSOR, (void *)PICL_PROP_CURRENT,
+ sun4v_env_print_sensor_callback);
+}
+
+static void
+sun4v_env_print_current_indicators()
+{
+ char *fmt = "%-11s %-14s %-8s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_CURRENT_INDICATOR,
+ (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nCurrent indicators:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_CURRENT_INDICATOR, NULL,
+ sun4v_env_print_indicator_callback);
+ if (all_status_ok) {
+ log_printf("All current indicators are OK.\n");
+ return;
+ }
+ }
+ log_printf("------------------------------------\n");
+ log_printf(fmt, "Location", "Indicator", "Condition", 0);
+ log_printf("------------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_CURRENT_INDICATOR,
+ (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+}
+
+static void
+sun4v_env_print_voltage_sensors()
+{
+ char *fmt = "%-11s %-14s %-10s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_SENSOR,
+ PICL_PROP_VOLTAGE,
+ sun4v_env_print_sensor_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nVoltage sensors:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_SENSOR,
+ NULL, sun4v_env_print_sensor_callback);
+ if (all_status_ok) {
+ log_printf("All voltage sensors are OK.\n");
+ return;
+ }
+ }
+ log_printf("---------------------------------\n");
+ log_printf(fmt, "Location", "Sensor", "Status", 0);
+ log_printf("---------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_SENSOR,
+ (void *)PICL_PROP_VOLTAGE,
+ sun4v_env_print_sensor_callback);
+}
+
+static void
+sun4v_env_print_voltage_indicators()
+{
+ char *fmt = "%-11s %-14s %-8s\n";
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_INDICATOR,
+ (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nVoltage indicators:\n");
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_INDICATOR, NULL,
+ sun4v_env_print_indicator_callback);
+ if (all_status_ok) {
+ log_printf("All voltage indicators are OK.\n");
+ return;
+ }
+ }
+ log_printf("------------------------------------\n");
+ log_printf(fmt, "Location", "Indicator", "Condition", 0);
+ log_printf("------------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_VOLTAGE_INDICATOR,
+ (void *)PICL_PROP_CONDITION,
+ sun4v_env_print_indicator_callback);
+}
+
+static void
+sun4v_env_print_LEDs()
+{
+ char *fmt = "%-11s %-14s %-8s\n";
+ if (syserrlog == 0)
+ return;
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED,
+ (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\nLEDs:\n");
+ log_printf("--------------------------------\n");
+ log_printf(fmt, "Location", "LED", "State", 0);
+ log_printf("--------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED,
+ (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback);
+}
+
+/*ARGSUSED*/
+static int
+sun4v_print_fru_status_callback(picl_nodehdl_t nodeh, void *args)
+{
+ char label[PICL_PROPNAMELEN_MAX];
+ char status[PICL_PROPNAMELEN_MAX];
+ picl_errno_t err;
+ picl_prophdl_t proph;
+ picl_nodehdl_t parenth;
+ char *names[PARENT_NAMES];
+ char *loc;
+ int i;
+
+ if (!class_node_found) {
+ class_node_found = 1;
+ return (PICL_WALK_TERMINATE);
+ }
+ err = picl_get_prop_by_name(nodeh, PICL_PROP_IS_FRU, &proph);
+ if (err != PICL_SUCCESS)
+ return (PICL_WALK_CONTINUE);
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label,
+ sizeof (label));
+ if (err != PICL_SUCCESS)
+ return (PICL_WALK_CONTINUE);
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS,
+ status, sizeof (status));
+ if (err != PICL_SUCCESS)
+ return (PICL_WALK_CONTINUE);
+ if (syserrlog == 0) {
+ if (strcmp(status, "disabled") == 0) {
+ if (all_status_ok) {
+ all_status_ok = 0;
+ return (PICL_WALK_TERMINATE);
+ }
+ } else
+ return (PICL_WALK_CONTINUE);
+ }
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
+ sizeof (parenth));
+ if (err != PICL_SUCCESS) {
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+ }
+ if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL)
+ return (PICL_WALK_TERMINATE);
+ for (i = 0; i < PARENT_NAMES; i++)
+ if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) {
+ while (--i > -1)
+ free(names[i]);
+ free(loc);
+ return (PICL_WALK_TERMINATE);
+ }
+ i = 0;
+ while (err == PICL_SUCCESS) {
+ if (parenth == chassish || parenth == phyplatformh)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
+ names[i++], PICL_PROPNAMELEN_MAX);
+ if (err != PICL_SUCCESS) {
+ i--;
+ break;
+ }
+ if (i == PARENT_NAMES)
+ break;
+ err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT,
+ &parenth, sizeof (parenth));
+ }
+ loc[0] = '\0';
+ if (--i > -1)
+ loc = strncat(loc, names[i], strlen(names[i]));
+ while (--i > -1) {
+ loc = strncat(loc, "/", 1);
+ loc = strncat(loc, names[i], strlen(names[i]));
+ }
+ log_printf("%-21s", loc);
+ for (i = 0; i < PARENT_NAMES; i++)
+ free(names[i]);
+ free(loc);
+ log_printf("%-10s", label);
+ log_printf("%-9s", status);
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+}
+
+static void
+sun4v_print_fru_status()
+{
+ char *fmt = "%-20s %-9s %-8s\n";
+ (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
+ sun4v_print_fru_status_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\n");
+ log_printf("============================");
+ log_printf(" FRU Status ");
+ log_printf("============================");
+ log_printf("\n");
+
+ if (syserrlog == 0) {
+ (void) picl_walk_tree_by_class(phyplatformh,
+ PICL_CLASS_MODULE, NULL,
+ sun4v_print_fru_status_callback);
+ if (all_status_ok) {
+ log_printf("All FRUs are enabled.\n");
+ return;
+ }
+ }
+ log_printf(fmt, "Location", "Name", "Status", 0);
+ log_printf("-------------------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_MODULE, NULL,
+ sun4v_print_fru_status_callback);
+}
+
+/*ARGSUSED*/
+static int
+sun4v_print_fw_rev_callback(picl_nodehdl_t nodeh, void *args)
+{
+ char label[PICL_PROPNAMELEN_MAX];
+ char rev[PICL_PROPNAMELEN_MAX];
+ picl_errno_t err;
+
+ if (!class_node_found) {
+ class_node_found = 1;
+ return (PICL_WALK_TERMINATE);
+ }
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label,
+ sizeof (label));
+ if (err != PICL_SUCCESS)
+ return (PICL_WALK_CONTINUE);
+ err = picl_get_propval_by_name(nodeh, PICL_PROP_FW_REVISION, rev,
+ sizeof (rev));
+ if (err != PICL_SUCCESS)
+ return (PICL_WALK_CONTINUE);
+ if (strlen(rev) == 0)
+ return (PICL_WALK_CONTINUE);
+ log_printf("%-21s", label);
+ log_printf("%-40s", rev);
+ log_printf("\n");
+ return (PICL_WALK_CONTINUE);
+}
+
+static void
+sun4v_print_fw_rev()
+{
+ char *fmt = "%-20s %-10s\n";
+ if (syserrlog == 0)
+ return;
+ (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
+ sun4v_print_fw_rev_callback);
+ if (!class_node_found)
+ return;
+ log_printf("\n");
+ log_printf("============================");
+ log_printf(" FW Version ");
+ log_printf("============================");
+ log_printf("\n");
+ log_printf(fmt, "Name", "Version", 0);
+ log_printf("----------------------------\n");
+ (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
+ sun4v_print_fw_rev_callback);
+}
+
+static void
+sun4v_print_chassis_serial_no()
+{
+ char val[PICL_PROPNAMELEN_MAX];
+ picl_errno_t err;
+ if (syserrlog == 0 || chassish == 0)
+ return;
+
+ log_printf("\n");
+ log_printf("Chassis Serial Number");
+ log_printf("\n");
+ log_printf("---------------------\n");
+ err = picl_get_propval_by_name(chassish, PICL_PROP_SERIAL_NUMBER,
+ val, sizeof (val));
+ if (err == PICL_SUCCESS)
+ log_printf("%s", val);
+ log_printf("\n");
+}
diff --git a/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c b/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c
index 8b1f1914d2..41c3743e98 100644
--- a/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c
+++ b/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c
@@ -2,9 +2,8 @@
* 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.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -68,8 +67,8 @@ extern int get_id(Prom_node *);
/* Function prototypes */
Prom_node *sun4v_walk(Sys_tree *, Prom_node *, int);
-int sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name,
- picl_nodehdl_t *nodeh);
+picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *, picl_nodehdl_t *);
+
/*
* do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c
*
@@ -86,7 +85,7 @@ do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
picl_nodehdl_t rooth; /* root PICL node for IO display */
picl_nodehdl_t plafh; /* Platform PICL node for IO display */
- int err;
+ picl_errno_t err;
err = picl_initialize();
if (err != PICL_SUCCESS) {
@@ -238,7 +237,7 @@ sun4v_walk(Sys_tree *tree, Prom_node *root, int id)
/*
* search children to get the node by the nodename
*/
-int
+picl_errno_t
sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name,
picl_nodehdl_t *nodeh)
{
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_sparc b/usr/src/pkgdefs/SUNWhea/prototype_sparc
index e2b2ac1128..695d5e4516 100644
--- a/usr/src/pkgdefs/SUNWhea/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWhea/prototype_sparc
@@ -278,6 +278,7 @@ f none usr/platform/sun4v/include/sys/cpc_ultra.h 644 root bin
f none usr/platform/sun4v/include/sys/cpu_sgnblk_defs.h 644 root bin
f none usr/platform/sun4v/include/sys/ddi_subrdefs.h 644 root bin
f none usr/platform/sun4v/include/sys/ds_pri.h 644 root bin
+f none usr/platform/sun4v/include/sys/ds_snmp.h 644 root bin
f none usr/platform/sun4v/include/sys/dvma.h 644 root bin
f none usr/platform/sun4v/include/sys/eeprom.h 644 root bin
f none usr/platform/sun4v/include/sys/fcode.h 644 root bin
diff --git a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
index 9ffc1b9c84..6e7aac0b63 100644
--- a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
@@ -59,10 +59,12 @@ d none platform/sun4v/kernel 755 root sys
d none platform/sun4v/kernel/drv 755 root sys
f none platform/sun4v/kernel/drv/drctl.conf 0644 root sys
f none platform/sun4v/kernel/drv/ds_pri.conf 644 root sys
+f none platform/sun4v/kernel/drv/ds_snmp.conf 644 root sys
d none platform/sun4v/kernel/drv/sparcv9 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/cnex 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/drctl 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/ds_pri 755 root sys
+f none platform/sun4v/kernel/drv/sparcv9/ds_snmp 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vcc 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vdc 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vds 755 root sys
diff --git a/usr/src/pkgdefs/SUNWpiclu/prototype_sparc b/usr/src/pkgdefs/SUNWpiclu/prototype_sparc
index 445f212c68..1619d6a9e4 100644
--- a/usr/src/pkgdefs/SUNWpiclu/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWpiclu/prototype_sparc
@@ -20,7 +20,7 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -62,10 +62,16 @@ f none usr/platform/sun4u/lib/picl/plugins/libpiclenvmon.so.1 755 root sys
s none usr/platform/sun4u/lib/picl/plugins/libpiclenvmon.so=./libpiclenvmon.so.1
d none usr/platform/sun4v 755 root sys
d none usr/platform/sun4v/lib 755 root bin
+f none usr/platform/sun4v/lib/libpiclsnmp.so.1 755 root sys
+s none usr/platform/sun4v/lib/libpiclsnmp.so=./libpiclsnmp.so.1 755 root sys
d none usr/platform/sun4v/lib/picl 755 root sys
d none usr/platform/sun4v/lib/picl/plugins 755 root sys
f none usr/platform/sun4v/lib/picl/plugins/libmdescplugin.so.1 755 root sys
s none usr/platform/sun4v/lib/picl/plugins/libmdescplugin.so=./libmdescplugin.so.1
+f none usr/platform/sun4v/lib/picl/plugins/libpriplugin.so.1 755 root sys
+s none usr/platform/sun4v/lib/picl/plugins/libpriplugin.so=./libpriplugin.so.1
+f none usr/platform/sun4v/lib/picl/plugins/libsnmpplugin.so.1 755 root sys
+s none usr/platform/sun4v/lib/picl/plugins/libsnmpplugin.so=./libsnmpplugin.so.1
d none usr/platform/SUNW,Sun-Blade-100 755 root sys
d none usr/platform/SUNW,Sun-Blade-100/lib 755 root bin
d none usr/platform/SUNW,Sun-Blade-100/lib/picl 755 root sys
diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major
index 8cba43de17..28edfbc173 100644
--- a/usr/src/uts/sparc/os/name_to_major
+++ b/usr/src/uts/sparc/os/name_to_major
@@ -216,3 +216,5 @@ pxb_bcm 267
pxb_plx 268
n2rng 269
physmem 270
+ds_snmp 271
+ds_pri 272
diff --git a/usr/src/uts/sun4v/Makefile.files b/usr/src/uts/sun4v/Makefile.files
index 0a63ac33fb..8c04b8010a 100644
--- a/usr/src/uts/sun4v/Makefile.files
+++ b/usr/src/uts/sun4v/Makefile.files
@@ -147,6 +147,7 @@ VSW_OBJS = vsw.o
VDC_OBJS = vdc.o
VDS_OBJS = vds.o
DS_PRI_OBJS = ds_pri.o
+DS_SNMP_OBJS = ds_snmp.o
#
# Misc modules
diff --git a/usr/src/uts/sun4v/Makefile.sun4v.shared b/usr/src/uts/sun4v/Makefile.sun4v.shared
index 4b6356d0de..8b067938a9 100644
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared
+++ b/usr/src/uts/sun4v/Makefile.sun4v.shared
@@ -327,6 +327,7 @@ DRV_KMODS += cpc
DRV_KMODS += dma
DRV_KMODS += drctl
DRV_KMODS += ds_pri
+DRV_KMODS += ds_snmp
DRV_KMODS += ebus
DRV_KMODS += fpc
DRV_KMODS += glvc
diff --git a/usr/src/uts/sun4v/ds_snmp/Makefile b/usr/src/uts/sun4v/ds_snmp/Makefile
new file mode 100644
index 0000000000..ec79c5d9d5
--- /dev/null
+++ b/usr/src/uts/sun4v/ds_snmp/Makefile
@@ -0,0 +1,99 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the pseudo device
+# to access the sun4v SNMP
+#
+# sun4v implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = ds_snmp
+OBJECTS = $(DS_SNMP_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(DS_SNMP_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/sun4v/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4v/Makefile.sun4v
+
+#
+# Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+
+CLEANFILES += $(MODSTUBS_O)
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += -v
+
+#
+# Module Dependencies
+LDFLAGS += -dy -Nmisc/ds
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/$(PLATFORM)/Makefile.targ
diff --git a/usr/src/uts/sun4v/io/ds.c b/usr/src/uts/sun4v/io/ds.c
index 1b269ad276..2a8aea01dc 100644
--- a/usr/src/uts/sun4v/io/ds.c
+++ b/usr/src/uts/sun4v/io/ds.c
@@ -121,6 +121,15 @@ static boolean_t ds_enabled; /* enable/disable taskq processing */
#define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP)
/*
+ * Retry count and delay for LDC reads and writes
+ */
+#define DS_DEFAULT_RETRIES 10000 /* number of times to retry */
+#define DS_DEFAULT_DELAY 1000 /* usecs to wait between retries */
+
+static int ds_retries = DS_DEFAULT_RETRIES;
+static clock_t ds_delay = DS_DEFAULT_DELAY;
+
+/*
* Supported versions of the DS message protocol
*
* The version array must be sorted in order from the highest
@@ -205,22 +214,24 @@ static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL];
#define DS_DBG_FLAG_LDC 0x1
#define DS_DBG_FLAG_LOG 0x2
+#define DS_DBG_FLAG_MSG 0x4
#define DS_DBG_FLAG_ALL 0xf
#define DS_DBG if (ds_debug) printf
#define DS_DBG_LDC if (ds_debug & DS_DBG_FLAG_LDC) printf
#define DS_DBG_LOG if (ds_debug & DS_DBG_FLAG_LOG) printf
-#define DS_DUMP_LDC_MSG(buf, len) ds_dump_ldc_msg(buf, len)
+#define DS_DBG_MSG if (ds_debug & DS_DBG_FLAG_MSG) printf
+#define DS_DUMP_MSG(buf, len) ds_dump_msg(buf, len)
uint_t ds_debug = 0;
-static void ds_dump_ldc_msg(void *buf, size_t len);
+static void ds_dump_msg(void *buf, size_t len);
#else /* DEBUG */
#define DS_DBG _NOTE(CONSTCOND) if (0) printf
#define DS_DBG_LDC DS_DBG
#define DS_DBG_LOG DS_DBG
-#define DS_DUMP_LDC_MSG(buf, len)
+#define DS_DUMP_MSG(buf, len)
#endif /* DEBUG */
@@ -236,7 +247,7 @@ static int ds_ldc_fini(ds_port_t *port);
/* event processing functions */
static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
static void ds_dispatch_event(void *arg);
-static int ds_recv_msg(ldc_handle_t ldc_hdl, caddr_t msgp, size_t *sizep);
+static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
static void ds_handle_recv(void *arg);
/* message sending functions */
@@ -764,42 +775,64 @@ done:
* in the size parameter.
*/
static int
-ds_recv_msg(ldc_handle_t ldc_hdl, caddr_t msgp, size_t *sizep)
+ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
{
int rv = 0;
- size_t msglen = *sizep;
- size_t amt_left = msglen;
- int loopcnt = 0;
+ size_t bytes_req = *sizep;
+ size_t bytes_left = bytes_req;
+ size_t nbytes;
+ int retry_count = 0;
*sizep = 0;
- while (msglen > 0) {
- if ((rv = ldc_read(ldc_hdl, msgp, &amt_left)) != 0) {
- if ((rv == EAGAIN) && (loopcnt++ < 1000)) {
- /*
- * Try again, but don't try for more than
- * one second. Something is wrong with
- * the channel.
- */
- delay(drv_usectohz(10000)); /* 1/1000 sec */
- } else {
- /* fail */
- return (rv);
- }
+ DS_DBG_LDC("ds@%lx: attempting to read %ld bytes\n", port->id,
+ bytes_req);
+
+ while (bytes_left > 0) {
+
+ nbytes = bytes_left;
+
+ if ((rv = ldc_read(port->ldc.hdl, msgp, &nbytes)) != 0) {
+ if (rv != EAGAIN)
+ break;
} else {
+ if (nbytes != 0) {
+ DS_DBG_LDC("ds@%lx: read %ld bytes, %d "
+ "retries\n", port->id, nbytes, retry_count);
+
+ *sizep += nbytes;
+ msgp += nbytes;
+ bytes_left -= nbytes;
+
+ /* reset counter on a successful read */
+ retry_count = 0;
+ continue;
+ }
+
/*
- * Check for a zero length read. This
- * indicates that there is no more data
- * to read from the channel.
+ * No data was read. Check if this is the
+ * first attempt. If so, just return since
+ * nothing has been read yet.
*/
- if (amt_left == 0)
+ if (bytes_left == bytes_req) {
+ DS_DBG_LDC("ds@%lx: read zero bytes, no data "
+ "available\n", port->id);
break;
+ }
+ }
- *sizep += amt_left;
- msgp += amt_left;
- msglen -= amt_left;
- amt_left = msglen;
+ /*
+ * A retry is necessary because the read returned
+ * EAGAIN, or a zero length read occurred after
+ * reading a partial message.
+ */
+ if (retry_count++ >= ds_retries) {
+ DS_DBG_LDC("ds@%lx: timed out waiting for "
+ "message\n", port->id);
+ break;
}
+
+ drv_usecwait(ds_delay);
}
return (rv);
@@ -845,7 +878,7 @@ ds_handle_recv(void *arg)
currp = hbuf;
/* read in the message header */
- if ((rv = ds_recv_msg(ldc_hdl, currp, &read_size)) != 0) {
+ if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
cmn_err(CE_NOTE, "ds@%lx: ldc_read returned %d",
port->id, rv);
continue;
@@ -874,7 +907,7 @@ ds_handle_recv(void *arg)
currp = (char *)(msg) + DS_HDR_SZ;
/* read in the message body */
- if ((rv = ds_recv_msg(ldc_hdl, currp, &read_size)) != 0) {
+ if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
cmn_err(CE_NOTE, "ds@%lx: ldc_read returned %d",
port->id, rv);
kmem_free(msg, msglen);
@@ -890,7 +923,7 @@ ds_handle_recv(void *arg)
continue;
}
- DS_DUMP_LDC_MSG(msg, msglen);
+ DS_DUMP_MSG(msg, msglen);
/*
* Send the message for processing, and store it
@@ -1555,7 +1588,8 @@ ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
size_t amt_left = msglen;
int loopcnt = 0;
- DS_DUMP_LDC_MSG(msg, msglen);
+ DS_DUMP_MSG(msg, msglen);
+
(void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen);
/*
@@ -1568,12 +1602,8 @@ ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
/* send the message */
do {
if ((rv = ldc_write(port->ldc.hdl, currp, &msglen)) != 0) {
- if ((rv == EWOULDBLOCK) && (loopcnt++ < 1000)) {
- /*
- * don't try for more than a sec. Something
- * is wrong with the channel.
- */
- delay(drv_usectohz(10000)); /* 1/1000 sec */
+ if ((rv == EWOULDBLOCK) && (loopcnt++ < ds_retries)) {
+ drv_usecwait(ds_delay);
} else {
cmn_err(CE_WARN,
"ds@%lx: send_msg: ldc_write failed (%d)",
@@ -1879,7 +1909,7 @@ ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
* and '.' otherwise.
*/
static void
-ds_dump_ldc_msg(void *vbuf, size_t len)
+ds_dump_msg(void *vbuf, size_t len)
{
int i, j;
char *curr;
@@ -1888,7 +1918,7 @@ ds_dump_ldc_msg(void *vbuf, size_t len)
uint8_t *buf = vbuf;
/* abort if not debugging ldc */
- if (!(ds_debug & DS_DBG_FLAG_LDC)) {
+ if (!(ds_debug & DS_DBG_FLAG_MSG)) {
return;
}
@@ -1923,7 +1953,7 @@ ds_dump_ldc_msg(void *vbuf, size_t len)
while (curr != (line + ASCIIOFFSET))
*curr++ = ' ';
- DS_DBG_LDC("%s\n", line);
+ DS_DBG_MSG("%s\n", line);
}
}
#endif /* DEBUG */
diff --git a/usr/src/uts/sun4v/io/ds_pri.c b/usr/src/uts/sun4v/io/ds_pri.c
index 518749f0dc..1c63c68f3f 100644
--- a/usr/src/uts/sun4v/io/ds_pri.c
+++ b/usr/src/uts/sun4v/io/ds_pri.c
@@ -110,6 +110,7 @@ struct ds_pri_state {
size_t ds_pri_len;
uint64_t req_id;
uint64_t last_req_id;
+ int num_opens;
};
typedef struct ds_pri_state ds_pri_state_t;
@@ -329,6 +330,7 @@ ds_pri_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
sp->ds_pri = NULL;
sp->ds_pri_len = 0;
sp->req_id = 0;
+ sp->num_opens = 0;
if ((rv = ds_cap_init(&ds_pri_cap, &ds_pri_ops)) != 0) {
cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
@@ -403,6 +405,25 @@ ds_pri_open(dev_t *devp, int flag, int otyp, cred_t *credp)
if (sp == NULL)
return (ENXIO);
+ mutex_enter(&sp->lock);
+
+ /*
+ * If we're here and the state is DS_PRI_NO_SERVICE then this
+ * means that ds hasn't yet called the registration callback.
+ * Wait here and the callback will signal us when it has completed
+ * its work.
+ */
+ if (sp->state == DS_PRI_NO_SERVICE) {
+ if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
+ mutex_exit(&sp->lock);
+ return (EINTR);
+ }
+ }
+
+ sp->num_opens++;
+
+ mutex_exit(&sp->lock);
+
/*
* On open we dont fetch the PRI even if we have a valid service
* handle. PRI fetch is essentially lazy and on-demand.
@@ -419,6 +440,7 @@ static int
ds_pri_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
int instance;
+ ds_pri_state_t *sp;
if (otyp != OTYP_CHR)
return (EINVAL);
@@ -426,9 +448,35 @@ ds_pri_close(dev_t dev, int flag, int otyp, cred_t *credp)
DS_PRI_DBG("ds_pri_close\n");
instance = getminor(dev);
- if (ddi_get_soft_state(ds_pri_statep, instance) == NULL)
+ if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
return (ENXIO);
+ mutex_enter(&sp->lock);
+ if (!(sp->state & DS_PRI_HAS_SERVICE)) {
+ mutex_exit(&sp->lock);
+ return (0);
+ }
+
+ if (--sp->num_opens > 0) {
+ mutex_exit(&sp->lock);
+ return (0);
+ }
+
+ /* If we have an old PRI - remove it */
+ if (sp->state & DS_PRI_HAS_PRI) {
+ if (sp->ds_pri != NULL && sp->ds_pri_len > 0) {
+ /*
+ * remove the old data if we have an
+ * outstanding request
+ */
+ kmem_free(sp->ds_pri, sp->ds_pri_len);
+ sp->ds_pri_len = 0;
+ sp->ds_pri = NULL;
+ }
+ sp->state &= ~DS_PRI_HAS_PRI;
+ }
+ sp->state &= ~DS_PRI_REQUESTED;
+ mutex_exit(&sp->lock);
return (0);
}
@@ -674,10 +722,14 @@ ds_pri_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
/* have service, but no PRI */
sp->state |= DS_PRI_HAS_SERVICE;
- /*
- * Cannot request a PRI here, because the reg handler cannot
- * do a DS send operation - we take care of this later.
- */
+ /*
+ * Cannot request a PRI here, because the reg handler cannot
+ * do a DS send operation - we take care of this later.
+ */
+
+ /* Wake up anyone waiting in open() */
+ cv_broadcast(&sp->cv);
+
mutex_exit(&sp->lock);
}
diff --git a/usr/src/uts/sun4v/io/ds_snmp.c b/usr/src/uts/sun4v/io/ds_snmp.c
new file mode 100644
index 0000000000..a6e443cf7b
--- /dev/null
+++ b/usr/src/uts/sun4v/io/ds_snmp.c
@@ -0,0 +1,1014 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * sun4v domain services SNMP driver
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/ksynch.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/debug.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/ds.h>
+#include <sys/ds_snmp.h>
+
+#define DS_SNMP_NAME "ds_snmp"
+#define DS_SNMP_MAX_OPENS 256
+#define DS_BITS_IN_UINT64 64
+#define DS_MINOR_POOL_SZ (DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
+#define DS_SNMP_MINOR_SHIFT 56
+#define DS_SNMP_DBG if (ds_snmp_debug) printf
+
+typedef struct {
+ uint64_t seq_num;
+ uint64_t type;
+} ds_snmp_msg_t;
+
+typedef enum {
+ DS_SNMP_REQUEST = 0,
+ DS_SNMP_REPLY = 1,
+ DS_SNMP_ERROR = 2
+} ds_snmp_msg_type_t;
+
+typedef enum {
+ DS_SNMP_READY = 0x0,
+ DS_SNMP_REQUESTED = 0x1,
+ DS_SNMP_DATA_AVL = 0x2,
+ DS_SNMP_DATA_ERR = 0x3
+} ds_snmp_flags_t;
+
+/*
+ * The single mutex 'lock' protects all the SNMP/DS variables in the state
+ * structure.
+ *
+ * The condition variable 'state_cv' helps serialize write() calls for a
+ * single descriptor. When write() is called, it sets a flag to indicate
+ * that an SNMP request has been made to the agent. No more write()'s on
+ * the same open descriptor will be allowed until this flag is cleared via
+ * a matching read(), where the requested packet is consumed on arrival.
+ * Read() then wakes up any waiters blocked in write() for sending the next
+ * SNMP request to the agent.
+ */
+typedef struct ds_snmp_state {
+ dev_info_t *dip;
+ int instance;
+ dev_t dev;
+
+ /* SNMP/DS */
+ kmutex_t lock;
+ kcondvar_t state_cv;
+ ds_snmp_flags_t state;
+ void *data;
+ size_t data_len;
+ uint64_t req_id;
+ uint64_t last_req_id;
+ uint64_t gencount;
+ boolean_t sc_reset;
+} ds_snmp_state_t;
+
+
+static uint_t ds_snmp_debug = 0;
+static void *ds_snmp_statep = NULL;
+static int ds_snmp_instance = -1;
+static dev_info_t *ds_snmp_devi = NULL;
+
+/*
+ * The ds_snmp_lock mutex protects the following data global to the
+ * driver.
+ *
+ * The ds_snmp_service_cv condition variable is used to resolve the
+ * potential race between the registration of snmp service via a
+ * ds_cap_init() in attach(), the acknowledgement of this registration
+ * at a later time in ds_snmp_reg_handler(), and a possible open() at
+ * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
+ * used to indicate whether the registration acknowledgement has happened
+ * or not.
+ *
+ * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
+ * minor numbers dynamically.
+ */
+static kmutex_t ds_snmp_lock;
+static kcondvar_t ds_snmp_service_cv;
+static int ds_snmp_has_service = B_FALSE;
+static ds_svc_hdl_t ds_snmp_handle = DS_INVALID_HDL;
+static uint64_t ds_snmp_minor_pool[DS_MINOR_POOL_SZ]; /* bitmask */
+static int ds_snmp_num_opens = 0;
+
+static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
+static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
+static int ds_snmp_open(dev_t *, int, int, cred_t *);
+static int ds_snmp_close(dev_t, int, int, cred_t *);
+static int ds_snmp_read(dev_t, struct uio *, cred_t *);
+static int ds_snmp_write(dev_t, struct uio *, cred_t *);
+static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+/*
+ * DS Callbacks
+ */
+static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
+static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
+static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
+
+/*
+ * SNMP DS capability registration
+ */
+static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
+static ds_capability_t ds_snmp_cap = {
+ "snmp",
+ &ds_snmp_ver_1_0,
+ 1
+};
+
+/*
+ * SNMP DS Client callback vector
+ */
+static ds_clnt_ops_t ds_snmp_ops = {
+ ds_snmp_reg_handler, /* ds_reg_cb */
+ ds_snmp_unreg_handler, /* ds_unreg_cb */
+ ds_snmp_data_handler, /* ds_data_cb */
+ NULL /* cb_arg */
+};
+
+/*
+ * DS SNMP driver Ops Vector
+ */
+static struct cb_ops ds_snmp_cb_ops = {
+ ds_snmp_open, /* cb_open */
+ ds_snmp_close, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ ds_snmp_read, /* cb_read */
+ ds_snmp_write, /* cb_write */
+ ds_snmp_ioctl, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ (struct streamtab *)NULL, /* cb_str */
+ D_MP | D_64BIT, /* cb_flag */
+ CB_REV, /* cb_rev */
+ nodev, /* cb_aread */
+ nodev /* cb_awrite */
+};
+
+static struct dev_ops ds_snmp_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ ds_snmp_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ ds_snmp_attach, /* devo_attach */
+ ds_snmp_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &ds_snmp_cb_ops, /* devo_cb_ops */
+ (struct bus_ops *)NULL, /* devo_bus_ops */
+ nulldev /* devo_power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "Domain Services SNMP Driver 1.0",
+ &ds_snmp_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ int retval;
+
+ mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL);
+
+ retval = ddi_soft_state_init(&ds_snmp_statep,
+ sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
+ if (retval != 0) {
+ cv_destroy(&ds_snmp_service_cv);
+ mutex_destroy(&ds_snmp_lock);
+ return (retval);
+ }
+
+ retval = mod_install(&modlinkage);
+ if (retval != 0) {
+ ddi_soft_state_fini(&ds_snmp_statep);
+ cv_destroy(&ds_snmp_service_cv);
+ mutex_destroy(&ds_snmp_lock);
+ }
+
+ return (retval);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ return (retval);
+
+ ddi_soft_state_fini(&ds_snmp_statep);
+
+ cv_destroy(&ds_snmp_service_cv);
+ mutex_destroy(&ds_snmp_lock);
+
+ return (retval);
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+ ds_snmp_state_t *sp;
+ int retval = DDI_FAILURE;
+
+ ASSERT(resultp != NULL);
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg));
+ if (sp != NULL) {
+ *resultp = sp->dip;
+ retval = DDI_SUCCESS;
+ } else
+ *resultp = NULL;
+ break;
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
+ retval = DDI_SUCCESS;
+ break;
+ }
+
+ return (retval);
+}
+
+static int
+ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int rv;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ if (ds_snmp_instance != -1)
+ return (DDI_FAILURE);
+ break;
+
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ ds_snmp_instance = ddi_get_instance(dip);
+ if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance,
+ DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
+ DS_SNMP_NAME, ds_snmp_instance);
+ return (DDI_FAILURE);
+ }
+
+ bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
+
+ ds_snmp_ops.cb_arg = dip;
+ if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) {
+ cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
+ ddi_remove_minor_node(dip, NULL);
+ ds_snmp_instance = -1;
+ return (DDI_FAILURE);
+ }
+
+ ds_snmp_devi = dip;
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ if (ds_snmp_instance == -1)
+ return (DDI_FAILURE);
+ break;
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ (void) ds_cap_fini(&ds_snmp_cap);
+
+ ddi_remove_minor_node(ds_snmp_devi, NULL);
+ bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
+
+ ds_snmp_instance = -1;
+ ds_snmp_devi = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+static minor_t
+ds_snmp_get_minor(void)
+{
+ uint64_t val;
+ int i, ndx;
+ minor_t minor;
+
+ mutex_enter(&ds_snmp_lock);
+ for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) {
+ val = ds_snmp_minor_pool[ndx];
+ for (i = 0; i < DS_BITS_IN_UINT64; i++) {
+ if ((val & 0x1) == 0) {
+ ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i);
+ ds_snmp_num_opens++;
+ mutex_exit(&ds_snmp_lock);
+
+ minor = ndx * DS_BITS_IN_UINT64 + i + 1;
+
+ return (minor);
+ }
+ val >>= 1;
+ }
+ }
+ mutex_exit(&ds_snmp_lock);
+
+ return (0);
+}
+
+static void
+ds_snmp_rel_minor(minor_t minor)
+{
+ int i, ndx;
+
+ ndx = (minor - 1) / DS_BITS_IN_UINT64;
+ i = (minor - 1) % DS_BITS_IN_UINT64;
+
+ ASSERT(ndx < DS_MINOR_POOL_SZ);
+
+ mutex_enter(&ds_snmp_lock);
+
+ ds_snmp_num_opens--;
+ ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i);
+
+ mutex_exit(&ds_snmp_lock);
+}
+
+static boolean_t
+ds_snmp_is_open(minor_t minor)
+{
+ uint64_t val;
+ int i, ndx;
+
+ ndx = (minor - 1) / DS_BITS_IN_UINT64;
+ i = (minor - 1) % DS_BITS_IN_UINT64;
+
+ val = ((uint64_t)1 << i);
+ if (ds_snmp_minor_pool[ndx] & val)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+static int
+ds_snmp_create_state(dev_t *devp)
+{
+ major_t major;
+ minor_t minor;
+ ds_snmp_state_t *sp;
+
+ if ((minor = ds_snmp_get_minor()) == 0)
+ return (EMFILE);
+
+ if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
+ DS_SNMP_NAME, minor);
+ ds_snmp_rel_minor(minor);
+ return (ENOMEM);
+ }
+
+ sp = ddi_get_soft_state(ds_snmp_statep, minor);
+ if (devp != NULL)
+ major = getemajor(*devp);
+ else
+ major = ddi_driver_major(ds_snmp_devi);
+
+ sp->dev = makedevice(major, minor);
+ if (devp != NULL)
+ *devp = sp->dev;
+
+ sp->instance = minor;
+ sp->data = NULL;
+ sp->data_len = 0;
+ sp->req_id = 0;
+ sp->last_req_id = 0;
+ sp->state = DS_SNMP_READY;
+ sp->sc_reset = B_FALSE;
+
+ mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL);
+
+ return (0);
+}
+
+static int
+ds_snmp_destroy_state(dev_t dev)
+{
+ ds_snmp_state_t *sp;
+ minor_t minor;
+
+ minor = getminor(dev);
+
+ if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
+ return (ENXIO);
+
+ ASSERT(sp->instance == minor);
+
+ /*
+ * If the app has not exited cleanly, the data may not have been
+ * read/memory freed, hence take care of that here
+ */
+ if (sp->data) {
+ kmem_free(sp->data, sp->data_len);
+ }
+ cv_destroy(&sp->state_cv);
+ mutex_destroy(&sp->lock);
+
+ ddi_soft_state_free(ds_snmp_statep, minor);
+ ds_snmp_rel_minor(minor);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ if (ds_snmp_instance == -1)
+ return (ENXIO);
+
+ /*
+ * Avoid possible race condition - ds service may not be there yet
+ */
+ mutex_enter(&ds_snmp_lock);
+ while (ds_snmp_has_service == B_FALSE) {
+ if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
+ mutex_exit(&ds_snmp_lock);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&ds_snmp_lock);
+
+ return (ds_snmp_create_state(devp));
+}
+
+
+/*ARGSUSED*/
+static int
+ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ if (ds_snmp_instance == -1)
+ return (ENXIO);
+
+ if (ds_snmp_handle == DS_INVALID_HDL)
+ return (EIO);
+
+ return (ds_snmp_destroy_state(dev));
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ ds_snmp_state_t *sp;
+ minor_t minor;
+ size_t len;
+ int retval;
+ caddr_t tmpbufp = (caddr_t)NULL;
+
+ /*
+ * Given that now we can have sc resets happening at any
+ * time, it is possible that it happened since the last time
+ * we issued a read, write or ioctl. If so, we need to wait
+ * for the unreg-reg pair to complete before we can do
+ * anything.
+ */
+ mutex_enter(&ds_snmp_lock);
+ while (ds_snmp_has_service == B_FALSE) {
+ DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
+ if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
+ mutex_exit(&ds_snmp_lock);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&ds_snmp_lock);
+
+ if ((len = uiop->uio_resid) == 0)
+ return (0);
+
+ minor = getminor(dev);
+ if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
+ return (ENXIO);
+
+ mutex_enter(&sp->lock);
+
+ if (sp->sc_reset == B_TRUE) {
+ mutex_exit(&sp->lock);
+ return (ECANCELED);
+ }
+
+ /*
+ * Block or bail if there is no SNMP data
+ */
+ if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) {
+ DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
+ if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
+ mutex_exit(&sp->lock);
+ return (EAGAIN);
+ }
+ while (sp->state != DS_SNMP_DATA_AVL &&
+ sp->state != DS_SNMP_DATA_ERR) {
+ if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
+ mutex_exit(&sp->lock);
+ return (EINTR);
+ }
+ }
+ }
+
+ /*
+ * If there has been an error, it could be because the agent
+ * returned failure and there is no data to read, or an ldc-reset
+ * has happened. Figure out which and return appropriate
+ * error to the caller.
+ */
+ if (sp->state == DS_SNMP_DATA_ERR) {
+ if (sp->sc_reset == B_TRUE) {
+ mutex_exit(&sp->lock);
+ DS_SNMP_DBG("ds_snmp_read: sc got reset, "
+ "returning ECANCELED\n");
+ return (ECANCELED);
+ } else {
+ sp->state = DS_SNMP_READY;
+ cv_broadcast(&sp->state_cv);
+ mutex_exit(&sp->lock);
+ DS_SNMP_DBG("ds_snmp_read: data error, "
+ "returning EIO\n");
+ return (EIO);
+ }
+ }
+
+ if (len > sp->data_len)
+ len = sp->data_len;
+
+ tmpbufp = kmem_alloc(len, KM_SLEEP);
+
+ bcopy(sp->data, (void *)tmpbufp, len);
+ kmem_free(sp->data, sp->data_len);
+ sp->data = (caddr_t)NULL;
+ sp->data_len = 0;
+
+ /*
+ * SNMP data has been consumed, wake up anyone waiting to send
+ */
+ sp->state = DS_SNMP_READY;
+ cv_broadcast(&sp->state_cv);
+
+ mutex_exit(&sp->lock);
+
+ retval = uiomove(tmpbufp, len, UIO_READ, uiop);
+ kmem_free(tmpbufp, len);
+
+ return (retval);
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ ds_snmp_state_t *sp;
+ ds_snmp_msg_t hdr;
+ minor_t minor;
+ size_t len;
+ caddr_t tmpbufp;
+
+ /*
+ * Check if there was an sc reset; if yes, wait until we have the
+ * service back again.
+ */
+ mutex_enter(&ds_snmp_lock);
+ while (ds_snmp_has_service == B_FALSE) {
+ DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
+ if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
+ mutex_exit(&ds_snmp_lock);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&ds_snmp_lock);
+
+ minor = getminor(dev);
+ if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
+ return (ENXIO);
+
+ len = uiop->uio_resid + sizeof (ds_snmp_msg_t);
+ tmpbufp = kmem_alloc(len, KM_SLEEP);
+
+ if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t),
+ len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) {
+ kmem_free(tmpbufp, len);
+ return (EIO);
+ }
+
+ mutex_enter(&sp->lock);
+
+ if (sp->sc_reset == B_TRUE) {
+ mutex_exit(&sp->lock);
+ kmem_free(tmpbufp, len);
+ DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
+ "returning ECANCELD\n");
+ return (ECANCELED);
+ }
+
+ /*
+ * wait if earlier transaction is not yet completed
+ */
+ while (sp->state != DS_SNMP_READY) {
+ if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
+ mutex_exit(&sp->lock);
+ kmem_free(tmpbufp, len);
+ return (EINTR);
+ }
+ /*
+ * Normally, only a reader would ever wake us up. But if we
+ * did get signalled with an ERROR, it could only mean there
+ * was an sc reset and there's no point waiting; we need to
+ * fail this write().
+ */
+ if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) {
+ DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
+ "returning ECANCELED\n");
+ mutex_exit(&sp->lock);
+ kmem_free(tmpbufp, len);
+ return (ECANCELED);
+ }
+ }
+
+ if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1))
+ sp->req_id = 0; /* Reset */
+
+ hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id;
+ sp->last_req_id = hdr.seq_num;
+ (sp->req_id)++;
+
+ /*
+ * Set state to SNMP_REQUESTED, but don't wakeup anyone yet
+ */
+ sp->state = DS_SNMP_REQUESTED;
+
+ mutex_exit(&sp->lock);
+
+ hdr.type = DS_SNMP_REQUEST;
+ bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr));
+
+ /*
+ * If the service went away since the time we entered this
+ * routine and now, tough luck. Just ignore the current
+ * write() and return.
+ */
+ mutex_enter(&ds_snmp_lock);
+ if (ds_snmp_has_service == B_FALSE) {
+ DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
+ "write, returning ECANCELED\n");
+ mutex_exit(&ds_snmp_lock);
+ kmem_free(tmpbufp, len);
+ return (ECANCELED);
+ }
+ DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
+ ds_snmp_handle, len);
+ (void) ds_cap_send(ds_snmp_handle, tmpbufp, len);
+ mutex_exit(&ds_snmp_lock);
+
+ kmem_free(tmpbufp, len);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ ds_snmp_state_t *sp;
+ struct dssnmp_info info;
+ minor_t minor;
+
+ /*
+ * Check if there was an sc reset; if yes, wait until we have the
+ * service back again.
+ */
+ mutex_enter(&ds_snmp_lock);
+ while (ds_snmp_has_service == B_FALSE) {
+ DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
+ if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
+ mutex_exit(&ds_snmp_lock);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&ds_snmp_lock);
+
+ DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle);
+
+ minor = getminor(dev);
+ if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
+ return (ENXIO);
+
+ if (!(mode & FREAD))
+ return (EACCES);
+
+ switch (cmd) {
+ case DSSNMP_GETINFO:
+ mutex_enter(&sp->lock);
+
+ if (sp->sc_reset == B_TRUE) {
+ mutex_exit(&sp->lock);
+ DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
+ return (ECANCELED);
+ }
+
+ while (sp->state != DS_SNMP_DATA_AVL &&
+ sp->state != DS_SNMP_DATA_ERR) {
+ DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
+ "waiting for data\n", sp->state, sp->sc_reset);
+ if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
+ mutex_exit(&sp->lock);
+ return (EINTR);
+ }
+ }
+ DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
+ "out of wait!\n", sp->state, sp->sc_reset);
+
+ /*
+ * If there has been an error, it could be because the
+ * agent returned failure and there is no data to read,
+ * or an ldc-reset has happened. Figure out which and
+ * return appropriate error to the caller.
+ */
+ if (sp->state == DS_SNMP_DATA_ERR) {
+ if (sp->sc_reset == B_TRUE) {
+ mutex_exit(&sp->lock);
+ DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
+ "returning ECANCELED\n");
+ return (ECANCELED);
+ } else {
+ sp->state = DS_SNMP_READY;
+ cv_broadcast(&sp->state_cv);
+ mutex_exit(&sp->lock);
+ DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
+ "returning EIO\n");
+ return (EIO);
+ }
+ }
+
+ info.size = sp->data_len;
+ info.token = sp->gencount;
+
+ mutex_exit(&sp->lock);
+
+ if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
+ return (EFAULT);
+ break;
+
+ case DSSNMP_CLRLNKRESET:
+ mutex_enter(&sp->lock);
+
+ DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
+ DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
+
+ if (sp->sc_reset == B_TRUE) {
+ if (sp->data) {
+ DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
+ sp->data, sp->data_len);
+ kmem_free(sp->data, sp->data_len);
+ }
+ sp->data = NULL;
+ sp->data_len = 0;
+ sp->state = DS_SNMP_READY;
+ sp->req_id = 0;
+ sp->last_req_id = 0;
+ sp->sc_reset = B_FALSE;
+ }
+ mutex_exit(&sp->lock);
+ break;
+
+ default:
+ return (ENOTTY);
+ }
+
+ return (0);
+}
+
+/*
+ * DS Callbacks
+ */
+/*ARGSUSED*/
+static void
+ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
+{
+ DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
+ "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
+
+ mutex_enter(&ds_snmp_lock);
+
+ ASSERT(ds_snmp_handle == DS_INVALID_HDL);
+
+ ds_snmp_handle = hdl;
+ ds_snmp_has_service = B_TRUE;
+
+ cv_broadcast(&ds_snmp_service_cv);
+
+ mutex_exit(&ds_snmp_lock);
+
+}
+
+/*ARGSUSED*/
+static void
+ds_snmp_unreg_handler(ds_cb_arg_t arg)
+{
+ minor_t minor;
+ ds_snmp_state_t *sp;
+
+ DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
+
+ mutex_enter(&ds_snmp_lock);
+
+ if (ds_snmp_num_opens) {
+ DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
+ ds_snmp_num_opens);
+ for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
+ if (ds_snmp_is_open(minor)) {
+ DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
+ "open\n", minor);
+ sp = ddi_get_soft_state(ds_snmp_statep, minor);
+ if (sp == NULL)
+ continue;
+
+ /*
+ * Set the sc_reset flag and break any waiters
+ * out of their existing reads/writes/ioctls.
+ */
+ DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
+ "signal waiters\n");
+ mutex_enter(&sp->lock);
+ sp->sc_reset = B_TRUE;
+ sp->state = DS_SNMP_DATA_ERR;
+ cv_broadcast(&sp->state_cv);
+ mutex_exit(&sp->lock);
+ }
+ }
+ }
+
+ ds_snmp_handle = DS_INVALID_HDL;
+ ds_snmp_has_service = B_FALSE;
+
+ DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
+
+ mutex_exit(&ds_snmp_lock);
+}
+
+/*ARGSUSED*/
+static void
+ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
+{
+ ds_snmp_state_t *sp;
+ ds_snmp_msg_t hdr;
+ size_t snmp_size;
+ minor_t minor;
+
+ /*
+ * Make sure the header is at least valid
+ */
+ if (buflen < sizeof (hdr)) {
+ cmn_err(CE_WARN,
+ "ds_snmp_data_handler: buflen <%lu> too small", buflen);
+ return;
+ }
+
+ ASSERT(buf != NULL);
+ bcopy(buf, (void *)&hdr, sizeof (hdr));
+
+ DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
+ "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
+
+ minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
+ if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
+ return;
+
+ mutex_enter(&sp->lock);
+
+ /*
+ * If there is no pending SNMP request, then we've received
+ * bogus data or an SNMP trap. Since we don't yet support SNMP
+ * traps, ignore it.
+ */
+ if (sp->state != DS_SNMP_REQUESTED) {
+ cmn_err(CE_WARN, "Received SNMP data without request");
+ mutex_exit(&sp->lock);
+ return;
+ }
+
+ /*
+ * Response to a request therefore old SNMP must've been consumed
+ */
+ ASSERT(sp->data_len == 0);
+ ASSERT(sp->data == NULL);
+
+ /*
+ * Response seq_num should match our request seq_num
+ */
+ if (hdr.seq_num != sp->last_req_id) {
+ cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
+ "request");
+ mutex_exit(&sp->lock);
+ return;
+ }
+
+ if (hdr.type == DS_SNMP_ERROR) {
+ sp->state = DS_SNMP_DATA_ERR;
+ DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
+ } else {
+ snmp_size = buflen - sizeof (ds_snmp_msg_t);
+ sp->data = kmem_alloc(snmp_size, KM_SLEEP);
+ sp->data_len = snmp_size;
+ sp->state = DS_SNMP_DATA_AVL;
+
+ bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
+ sp->data, sp->data_len);
+ }
+
+ sp->gencount++;
+
+ /*
+ * Wake up any readers waiting for data
+ */
+ cv_broadcast(&sp->state_cv);
+ mutex_exit(&sp->lock);
+}
diff --git a/usr/src/uts/sun4v/io/ds_snmp.conf b/usr/src/uts/sun4v/io/ds_snmp.conf
new file mode 100644
index 0000000000..3979db29e4
--- /dev/null
+++ b/usr/src/uts/sun4v/io/ds_snmp.conf
@@ -0,0 +1,28 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+name="ds_snmp" parent="pseudo" instance=0;
diff --git a/usr/src/uts/sun4v/sys/Makefile b/usr/src/uts/sun4v/sys/Makefile
index 66d7605b9a..ed76d49bf4 100644
--- a/usr/src/uts/sun4v/sys/Makefile
+++ b/usr/src/uts/sun4v/sys/Makefile
@@ -72,6 +72,7 @@ CLOSED_SUN4_HDRS= \
HDRS= \
ds_pri.h \
+ ds_snmp.h \
hypervisor_api.h \
hsvc.h \
machasi.h \
diff --git a/usr/src/uts/sun4v/sys/ds_snmp.h b/usr/src/uts/sun4v/sys/ds_snmp.h
new file mode 100644
index 0000000000..ac1fc22aa4
--- /dev/null
+++ b/usr/src/uts/sun4v/sys/ds_snmp.h
@@ -0,0 +1,60 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DS_SNMP_H_
+#define _SYS_DS_SNMP_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ioctl info for ds_snmp device
+ */
+
+#define DSSNMPIOC ('d' << 24 | 's' << 16 | 'p' << 8)
+
+#define DSSNMP_GETINFO (DSSNMPIOC | 1) /* Get SNMP size */
+#define DSSNMP_CLRLNKRESET (DSSNMPIOC | 2) /* Clear link reset flag */
+
+/*
+ * DSSNMP_GETINFO
+ * Datamodel invariant.
+ */
+struct dssnmp_info {
+ uint64_t size;
+ uint64_t token;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DS_SNMP_H_ */