summaryrefslogtreecommitdiff
path: root/usr/src/cmd/picl/plugins
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 /usr/src/cmd/picl/plugins
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
Diffstat (limited to 'usr/src/cmd/picl/plugins')
-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
25 files changed, 7561 insertions, 59 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 */