Mercurial > illumos > illumos-gate
changeset 3941:328be6a20f20
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
line wrap: on
line diff
--- a/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/cmd/picl/plugins/common/devtree/picldevtree.c Sat Mar 31 18:24:05 2007 -0700 @@ -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 @@ 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);
--- a/usr/src/cmd/picl/plugins/inc/picldefs.h Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/cmd/picl/plugins/inc/picldefs.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 @@ #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 @@ #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"
--- a/usr/src/cmd/picl/plugins/sun4v/Makefile Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/cmd/picl/plugins/sun4v/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/include/libpiclsnmp.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/include/picloids.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -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:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -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:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.c Sat Mar 31 18:24:05 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/debug.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/lib/snmp/snmplib.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- a/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/cmd/picl/plugins/sun4v/mdesc/init.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); - } + do { + if ((fd = open(MDESC_PATH, O_RDONLY, 0)) < 0) + break; - /* 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 (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); + (void) close(fd); + /*LINTED: E_CONSTANT_CONDITION */ + } while (0); - md_bufp = realloc(md_bufp, offset); - if (NULL == md_bufp) - return (NULL); - - 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); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -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:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/init.c Sat Mar 31 18:24:05 2007 -0700 @@ -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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c Sat Mar 31 18:24:05 2007 -0700 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c Sat Mar 31 18:24:05 2007 -0700 @@ -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 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/snmp/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -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:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c Sat Mar 31 18:24:05 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.h Sat Mar 31 18:24:05 2007 -0700 @@ -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 */
--- a/usr/src/lib/libprtdiag/Makefile.com Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/lib/libprtdiag/Makefile.com Sat Mar 31 18:24:05 2007 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -40,6 +40,7 @@ LIBS = $(DYNLIB) $(LINTLIB) IFLAGS = -I ../../inc -I $(USR_PSM_INCL_DIR) +IFLAGS += -I $(SRC)/cmd/picl/plugins/inc IFLAGS += -I $(UTSBASE)/sun4u IFLAGS += -I $(UTSBASE)/sun4u/sunfire IFLAGS += -I $(UTSBASE)/sun4u/serengeti
--- a/usr/src/lib/libprtdiag/common/display_sun4v.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/lib/libprtdiag/common/display_sun4v.c Sat Mar 31 18:24:05 2007 -0700 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,26 +43,87 @@ #include <libintl.h> #include <syslog.h> #include <sys/dkio.h> +#include <sys/systeminfo.h> +#include <picldefs.h> #include "pdevinfo.h" #include "display.h" #include "display_sun4v.h" #include "libprtdiag.h" - #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif -extern int sys_clk; +#define IOBOARD "IOBD" +#define NETWORK "network" +#define PCIE_COMPATIBLE_STR "pciex" +#define PCIX_COMPATIBLE_STR "pci" +#define SUN4V_MACHINE "sun4v" +#define PARENT_NAMES 10 + +/* + * Additional OBP properties + */ +#define OBP_PROP_COMPATIBLE "compatible" +#define OBP_PROP_MODEL "model" +#define OBP_PROP_SLOT_NAMES "slot-names" + +#define PICL_NODE_PHYSICAL_PLATFORM "physical-platform" +#define PICL_NODE_CHASSIS "chassis" +#define MEMORY_SIZE_FIELD 11 +#define INVALID_THRESHOLD 1000000 + +/* + * Additional picl classes + */ +#ifndef PICL_CLASS_SUN4V +#define PICL_CLASS_SUN4V "sun4v" +#endif + +#ifndef PICL_PROP_NAC +#define PICL_PROP_NAC "nac" +#endif + +extern int sys_clk; +extern picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *, + picl_nodehdl_t *); + +static picl_nodehdl_t rooth = 0, phyplatformh = 0; +static picl_nodehdl_t chassish = 0; +static int class_node_found; +static int syserrlog; +static int all_status_ok; + +/* local functions */ +static int sun4v_get_first_compatible_value(picl_nodehdl_t, char **); +static void sun4v_display_memory_conf(picl_nodehdl_t); +static void sun4v_disp_env_status(); +static void sun4v_env_print_fan_sensors(); +static void sun4v_env_print_fan_indicators(); +static void sun4v_env_print_temp_sensors(); +static void sun4v_env_print_temp_indicators(); +static void sun4v_env_print_current_sensors(); +static void sun4v_env_print_current_indicators(); +static void sun4v_env_print_voltage_sensors(); +static void sun4v_env_print_voltage_indicators(); +static void sun4v_env_print_LEDs(); +static void sun4v_print_fru_status(); +static void sun4v_print_fw_rev(); +static void sun4v_print_chassis_serial_no(); int -sun4v_display(Sys_tree *tree, Prom_node *root, int syserrlog, - picl_nodehdl_t plafh) +sun4v_display(Sys_tree *tree, Prom_node *root, int log, + picl_nodehdl_t plafh) { - int exit_code = 0; /* init to all OK */ void *value; /* used for opaque PROM data */ struct mem_total memory_total; /* Total memory in system */ struct grp_info grps; /* Info on all groups in system */ + char machine[MAXSTRLEN]; + + if (sysinfo(SI_MACHINE, machine, sizeof (machine)) == -1) + return (1); + if (strncmp(machine, SUN4V_MACHINE, strlen(SUN4V_MACHINE)) != 0) + return (1); sys_clk = -1; /* System clock freq. (in MHz) */ @@ -79,11 +139,9 @@ */ (void) uname(&uts_buf); - log_printf( - dgettext(TEXT_DOMAIN, "System Configuration: " - "Sun Microsystems %s %s\n"), uts_buf.machine, - get_prop_val(find_prop(root, - "banner-name")), 0); + log_printf(dgettext(TEXT_DOMAIN, "System Configuration: " + "Sun Microsystems %s %s\n"), uts_buf.machine, + get_prop_val(find_prop(root, "banner-name")), 0); /* display system clock frequency */ value = get_prop_val(find_prop(root, "clock-frequency")); @@ -100,15 +158,286 @@ sun4v_display_cpu_devices(plafh); /* Display the Memory configuration */ - sun4v_display_memoryconf(plafh); + class_node_found = 0; + sun4v_display_memory_conf(plafh); /* Display all the IO cards. */ (void) sun4v_display_pci(plafh); + sun4v_display_diaginfo((log || (logging)), root, plafh); - sun4v_display_diaginfo((syserrlog || (logging)), root, plafh); + if (picl_get_root(&rooth) != PICL_SUCCESS) + return (1); + if (sun4v_get_node_by_name(rooth, PICL_NODE_PHYSICAL_PLATFORM, + &phyplatformh) != PICL_SUCCESS) + return (1); + + if (picl_find_node(phyplatformh, PICL_PROP_CLASSNAME, + PICL_PTYPE_CHARSTRING, (void *)PICL_CLASS_CHASSIS, + strlen(PICL_CLASS_CHASSIS), &chassish) != PICL_SUCCESS) + return (1); + + syserrlog = log; + sun4v_disp_env_status(); + } + return (0); +} + +static void +get_bus_type(picl_nodehdl_t nodeh, struct io_card *card) +{ + char *compatible; + + (void) strcpy(card->bus_type, "PCIX"); + if (sun4v_get_first_compatible_value(nodeh, &compatible) + == PICL_SUCCESS) { + if (strncmp(compatible, PCIE_COMPATIBLE_STR, + strlen(PCIE_COMPATIBLE_STR)) == 0) + (void) strcpy(card->bus_type, "PCIE"); + free(compatible); + } +} + +static picl_errno_t +get_slot_label(picl_nodehdl_t nodeh, struct io_card *card) +{ + char val[PICL_PROPNAMELEN_MAX]; + picl_errno_t err; + + err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val, + sizeof (val)); + if (err != PICL_SUCCESS) + return (err); + + (void) strcpy(card->slot_str, val); + card->slot = -1; + return (PICL_SUCCESS); +} + +static void +get_slot_number(picl_nodehdl_t nodeh, struct io_card *card) +{ + picl_errno_t err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_nodehdl_t pnodeh; + uint8_t *pval; + uint32_t dev_mask; + char uaddr[MAXSTRLEN]; + int i; + + if (get_slot_label(nodeh, card) == PICL_SUCCESS) + return; + err = PICL_SUCCESS; + while (err == PICL_SUCCESS) { + if (picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &pnodeh, + sizeof (pnodeh)) != PICL_SUCCESS) { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + return; + } + if (picl_get_propinfo_by_name(pnodeh, OBP_PROP_SLOT_NAMES, + &pinfo, &proph) == PICL_SUCCESS) { + break; + } + nodeh = pnodeh; + } + if (picl_get_propval_by_name(nodeh, PICL_PROP_UNIT_ADDRESS, uaddr, + sizeof (uaddr)) != PICL_SUCCESS) { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + return; + } + pval = (uint8_t *)malloc(pinfo.size); + if (!pval) { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + return; + } + if (picl_get_propval(proph, pval, pinfo.size) != PICL_SUCCESS) { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + free(pval); + return; } - return (exit_code); + dev_mask = 0; + for (i = 0; i < sizeof (dev_mask); i++) + dev_mask |= (*(pval+i) << 8*(sizeof (dev_mask)-1-i)); + for (i = 0; i < sizeof (uaddr) && uaddr[i] != '\0'; i++) { + if (uaddr[i] == ',') { + uaddr[i] = '\0'; + break; + } + } + card->slot = atol(uaddr); + if (((1 << card->slot) & dev_mask) == 0) { + (void) strcpy(card->slot_str, IOBOARD); + card->slot = -1; + } else { + char *p = (char *)(pval+sizeof (dev_mask)); + int shift = sizeof (uint32_t)*8-1-card->slot; + uint32_t x = (dev_mask << shift) >> shift; + int count = 0; /* count # of 1's in x */ + int i = 0; + while (x != 0) { + count++; + x &= x-1; + } + while (count > 1) { + while (p[i++] != '\0'); + count--; + } + (void) strcpy(card->slot_str, (char *)(p+i)); + } + free(pval); +} + +/* + * add all io devices under pci in io list + */ +/* ARGSUSED */ +static int +sun4v_pci_callback(picl_nodehdl_t pcih, void *args) +{ + char path[PICL_PROPNAMELEN_MAX]; + char class[PICL_CLASSNAMELEN_MAX]; + char name[PICL_PROPNAMELEN_MAX]; + char model[PICL_PROPNAMELEN_MAX]; + char binding_name[PICL_PROPNAMELEN_MAX]; + char val[PICL_PROPNAMELEN_MAX]; + char *compatible; + picl_errno_t err; + picl_nodehdl_t nodeh; + struct io_card pci_card; + + /* Walk through the children */ + + err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh, + sizeof (picl_nodehdl_t)); + + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, + class, sizeof (class)); + if (err != PICL_SUCCESS) + return (err); + + if (args) { + char *val = args; + if (strcmp(class, val) == 0) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + continue; + } else if (strcmp(val, PICL_CLASS_PCIEX) == 0 && + strcmp(class, PICL_CLASS_PCI) == 0) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + continue; + } else if (strcmp(val, PICL_CLASS_PCI) == 0 && + strcmp(class, PICL_CLASS_PCIEX) == 0) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + continue; + } + } + + err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH, + path, sizeof (path)); + if (err != PICL_SUCCESS) + return (err); + + (void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes)); + + get_bus_type(nodeh, &pci_card); + get_slot_number(nodeh, &pci_card); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, name, + sizeof (name)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(name, ""); + else if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_STATUS, val, + sizeof (val)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(val, ""); + else if (err != PICL_SUCCESS) + return (err); + + /* Figure NAC name */ + if (pci_card.slot != -1) + (void) snprintf(pci_card.status, + sizeof (pci_card.status), + "%s%d", pci_card.slot_str, + pci_card.slot); + else + (void) snprintf(pci_card.status, + sizeof (pci_card.status), + "%s", pci_card.slot_str); + + /* + * Get the name of this card. If binding_name is found, + * name will be <nodename>-<binding_name>. + */ + err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME, + binding_name, sizeof (binding_name)); + if (err == PICL_SUCCESS) { + if (strcmp(name, binding_name) != 0) { + (void) strlcat(name, "-", sizeof (name)); + (void) strlcat(name, binding_name, + sizeof (name)); + } + } else if (err == PICL_PROPNOTFOUND) { + /* + * if compatible prop is not found, name will be + * <nodename>-<compatible> + */ + err = sun4v_get_first_compatible_value(nodeh, + &compatible); + if (err == PICL_SUCCESS) { + (void) strlcat(name, "-", sizeof (name)); + (void) strlcat(name, compatible, sizeof (name)); + free(compatible); + } + } else + return (err); + + (void) strlcpy(pci_card.name, name, sizeof (pci_card.name)); + + /* Get the model of this card */ + + err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL, + model, sizeof (model)); + if (err == PICL_PROPNOTFOUND) + (void) strcpy(model, ""); + else if (err != PICL_SUCCESS) + return (err); + (void) strlcpy(pci_card.model, model, sizeof (pci_card.model)); + + /* Print NAC name */ + log_printf("%-12s", pci_card.status); + /* Print IO Type */ + log_printf("%-6s", pci_card.bus_type); + /* Printf Card Name */ + log_printf("%-46s", pci_card.name); + /* Print Card Model */ + log_printf("%-8s", pci_card.model); + log_printf("\n"); + /* Print Status */ + log_printf("%-12s", val); + /* Print IO Type */ + log_printf("%-6s", ""); + /* Print Parent Path */ + log_printf("%-46s", pci_card.notes); + log_printf("\n"); + + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + return (PICL_WALK_CONTINUE); } /* @@ -118,29 +447,229 @@ void sun4v_display_pci(picl_nodehdl_t plafh) { -#ifdef lint - plafh = plafh; -#endif - /* - * This function is intentionally empty - */ + char *fmt = "%-11s %-5s %-45s %-8s"; + /* Have we printed the column headings? */ + static int banner = FALSE; + + if (banner == FALSE) { + log_printf("\n"); + log_printf("============================"); + log_printf(" IO Devices "); + log_printf("============================"); + log_printf("\n"); + log_printf(fmt, "Slot +", "Bus", "Name +", "Model", 0); + log_printf("\n"); + log_printf(fmt, "Status", "Type", "Path", "", 0); + log_printf("\n"); + log_printf("---------------------------------" + "------------------------------------\n"); + banner = TRUE; + } + + (void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCIEX, + PICL_CLASS_PCIEX, sun4v_pci_callback); + (void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCI, + PICL_CLASS_PCI, sun4v_pci_callback); + (void) picl_walk_tree_by_class(plafh, PICL_CLASS_SUN4V, + PICL_CLASS_SUN4V, sun4v_pci_callback); +} + +/* + * return the first compatible value + */ +static int +sun4v_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) +{ + picl_errno_t err; + picl_prophdl_t proph; + picl_propinfo_t pinfo; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; + char *pval; + + err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, + &pinfo, &proph); + if (err != PICL_SUCCESS) + return (err); + + if (pinfo.type == PICL_PTYPE_CHARSTRING) { + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + err = picl_get_propval(proph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + *outbuf = pval; + return (PICL_SUCCESS); + } + + if (pinfo.type != PICL_PTYPE_TABLE) + return (PICL_FAILURE); + + /* get first string from table */ + err = picl_get_propval(proph, &tblh, pinfo.size); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_next_by_row(tblh, &rowproph); + if (err != PICL_SUCCESS) + return (err); + + err = picl_get_propinfo(rowproph, &pinfo); + if (err != PICL_SUCCESS) + return (err); + + pval = malloc(pinfo.size); + if (pval == NULL) + return (PICL_FAILURE); + + err = picl_get_propval(rowproph, pval, pinfo.size); + if (err != PICL_SUCCESS) { + free(pval); + return (err); + } + + *outbuf = pval; + return (PICL_SUCCESS); } -void -sun4v_display_memoryconf(picl_nodehdl_t plafh) +/* + * print size of a memory segment + */ +static void +print_memory_segment_size(uint64_t size) +{ + uint64_t kbyte = 1024; + uint64_t mbyte = kbyte * kbyte; + uint64_t gbyte = kbyte * mbyte; + char buf[MEMORY_SIZE_FIELD]; + + if (size >= gbyte) { + if (size % gbyte == 0) + (void) snprintf(buf, sizeof (buf), "%d GB", + (int)(size / gbyte)); + else + (void) snprintf(buf, sizeof (buf), "%.2f GB", + (float)size / gbyte); + } else if (size >= mbyte) { + if (size % mbyte == 0) + (void) snprintf(buf, sizeof (buf), "%d MB", + (int)(size / mbyte)); + else + (void) snprintf(buf, sizeof (buf), "%.2f MB", + (float)size / mbyte); + } else { + if (size % kbyte == 0) + (void) snprintf(buf, sizeof (buf), "%d KB", + (int)(size / kbyte)); + else + (void) snprintf(buf, sizeof (buf), "%.2f KB", + (float)size / kbyte); + } + log_printf("%-7s ", buf); +} + +/* + * print bank IDs of a memory segment + */ +static void +print_memory_segment_contain(picl_nodehdl_t nodeh) { -#ifdef lint - plafh = plafh; -#endif - /* - * This function is intentionally empty - */ + char val[PICL_PROPNAMELEN_MAX]; + picl_errno_t err = picl_get_propval_by_name(nodeh, + PICL_PROP_NAC, val, sizeof (val)); + if (err != PICL_SUCCESS) + return; + log_printf("%-30s", val); + while ((err = picl_get_propval_by_name(nodeh, + PICL_PROP_PEER, &nodeh, + sizeof (nodeh))) == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_NAC, val, sizeof (val)); + if (err == PICL_SUCCESS) { + log_printf("\n"); + log_printf("%-30s", val); + } + } +} + +/* + * Search node where _class=="memory-segment" + * print "Base Address", "Size", etc + */ +/*ARGSUSED*/ +static int +sun4v_memory_conf_callback(picl_nodehdl_t nodeh, void *args) +{ + uint64_t base; + uint64_t size; + uint64_t ifactor; + picl_errno_t err = PICL_SUCCESS; + + if (class_node_found == 0) { + class_node_found = 1; + return (PICL_WALK_TERMINATE); + } + while (err == PICL_SUCCESS) { + err = picl_get_propval_by_name(nodeh, PICL_PROP_BASEADDRESS, + &base, sizeof (base)); + if (err != PICL_SUCCESS) + break; + err = picl_get_propval_by_name(nodeh, PICL_PROP_SIZE, + &size, sizeof (size)); + if (err != PICL_SUCCESS) + break; + err = picl_get_propval_by_name(nodeh, + PICL_PROP_INTERLEAVE_FACTOR, &ifactor, + sizeof (ifactor)); + if (err != PICL_SUCCESS) + break; + err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, + &nodeh, sizeof (nodeh)); + if (err != PICL_SUCCESS) + break; + log_printf("%-13llx", base); + print_memory_segment_size(size); + log_printf("%-18d", ifactor); + print_memory_segment_contain(nodeh); + log_printf("\n"); + err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); + } + + return (PICL_WALK_CONTINUE); +} + +/*ARGSUSED*/ +void +sun4v_display_memory_conf(picl_nodehdl_t plafh) +{ + char *fmt = "%-12s %-7s %-9s %-20s"; + (void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT, + NULL, sun4v_memory_conf_callback); + if (class_node_found == 0) + return; + log_printf("\n"); + log_printf("============================"); + log_printf(" Memory Configuration "); + log_printf("============================"); + log_printf("\n"); + log_printf("Segment Table:\n"); + log_printf("-----------------------------------------------\n"); + log_printf(fmt, "Base Address", "Size", "Interleave Factor", + "Contains", 0); + log_printf("\n"); + log_printf("-----------------------------------------------\n"); + (void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT, + NULL, sun4v_memory_conf_callback); } void sun4v_display_cpu_devices(picl_nodehdl_t plafh) { - char *fmt = "%-12s %-5s %-8s %-19s %-5s"; + char *fmt = "%-12s %-5s %-8s %-19s %-5s"; /* * Display the table header for CPUs . Then display the CPU @@ -156,15 +685,13 @@ log_printf(fmt, "", "", "", "CPU", "CPU", 0); log_printf("\n"); log_printf(fmt, "Location", "CPU", "Freq", - "Implementation", "Mask", 0); + "Implementation", "Mask", 0); log_printf("\n"); log_printf(fmt, "------------", "-----", "--------", - "-------------------", "-----", 0); + "-------------------", "-----", 0); log_printf("\n"); (void) picl_walk_tree_by_class(plafh, "cpu", "cpu", sun4v_display_cpus); - - log_printf("\n"); } /* @@ -175,16 +702,16 @@ sun4v_display_cpus(picl_nodehdl_t cpuh, void* args) { int status; - picl_prophdl_t proph; - picl_prophdl_t tblh; - picl_prophdl_t rowproph; + picl_prophdl_t proph; + picl_prophdl_t tblh; + picl_prophdl_t rowproph; picl_propinfo_t propinfo; - int *int_value; - uint64_t cpuid, mask_no; - char *comp_value; - char *no_prop_value = " "; - char freq_str[MAXSTRLEN]; - char fru_name[MAXSTRLEN]; + int *int_value; + uint64_t cpuid, mask_no; + char *comp_value; + char *no_prop_value = " "; + char freq_str[MAXSTRLEN]; + char fru_name[MAXSTRLEN]; /* * Get cpuid property and print it and the NAC name @@ -193,44 +720,44 @@ if (status == PICL_SUCCESS) { status = picl_get_propval(proph, &cpuid, sizeof (cpuid)); if (status != PICL_SUCCESS) { - log_printf("%-12s", no_prop_value); - log_printf("%6s", no_prop_value); + log_printf("%-13s", no_prop_value); + log_printf("%-6s", no_prop_value); } else { (void) snprintf(fru_name, sizeof (fru_name), "%s%d", CPU_STRAND_NAC, (int)cpuid); - log_printf("%-12s", fru_name); - log_printf("%6d", (int)cpuid); + log_printf("%-13s", fru_name); + log_printf("%-6d", (int)cpuid); } } else { - log_printf("%-12s", no_prop_value); - log_printf("%6s", no_prop_value); + log_printf("%-13s", no_prop_value); + log_printf("%-6s", no_prop_value); } clock_freq: status = picl_get_propinfo_by_name(cpuh, "clock-frequency", &propinfo, - &proph); + &proph); if (status == PICL_SUCCESS) { int_value = malloc(propinfo.size); if (int_value == NULL) { - log_printf("%9s", no_prop_value); + log_printf("%-9s", no_prop_value); goto compatible; } status = picl_get_propval(proph, int_value, propinfo.size); if (status != PICL_SUCCESS) { - log_printf("%9s", no_prop_value); + log_printf("%-9s", no_prop_value); } else { /* Running frequency */ (void) snprintf(freq_str, sizeof (freq_str), "%d MHz", CLK_FREQ_TO_MHZ(*int_value)); - log_printf("%9s", freq_str); + log_printf("%-9s", freq_str); } free(int_value); } else - log_printf("%9s", no_prop_value); + log_printf("%-9s", no_prop_value); compatible: status = picl_get_propinfo_by_name(cpuh, "compatible", &propinfo, - &proph); + &proph); if (status == PICL_SUCCESS) { if (propinfo.type == PICL_PTYPE_CHARSTRING) { /* @@ -238,72 +765,65 @@ */ comp_value = malloc(propinfo.size); if (comp_value == NULL) { - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); goto mask; } status = picl_get_propval(proph, comp_value, - propinfo.size); - if (status == PICL_SUCCESS) { - log_printf("%20s", no_prop_value, 0); - free(comp_value); - } + propinfo.size); + if (status != PICL_SUCCESS) + log_printf("%-20s", no_prop_value, 0); + else + log_printf("%-20s", comp_value, 0); + free(comp_value); } else if (propinfo.type == PICL_PTYPE_TABLE) { /* * Compatible Property has multiple values */ status = picl_get_propval(proph, &tblh, propinfo.size); if (status != PICL_SUCCESS) { - printf("Failed getting tblh\n"); - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); goto mask; } status = picl_get_next_by_row(tblh, &rowproph); if (status != PICL_SUCCESS) { - printf("Failed getting next by row\n"); - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); goto mask; } status = picl_get_propinfo(rowproph, &propinfo); if (status != PICL_SUCCESS) { - printf("Failed getting prop for rowproph\n"); - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); goto mask; } comp_value = malloc(propinfo.size); if (comp_value == NULL) { - printf("Failed to get malloc value?\n"); - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); goto mask; } - status = picl_get_propval(rowproph, comp_value, - propinfo.size); - if (status != PICL_SUCCESS) { - printf("Failed geting rowproph\n"); - log_printf("%20s", no_prop_value, 0); - free(comp_value); - goto mask; - } else - log_printf("%20s", comp_value, 0); + propinfo.size); + if (status != PICL_SUCCESS) + log_printf("%-20s", no_prop_value, 0); + else + log_printf("%-20s", comp_value, 0); free(comp_value); } } else - log_printf("%20s", no_prop_value, 0); + log_printf("%-20s", no_prop_value, 0); mask: status = picl_get_propinfo_by_name(cpuh, "mask#", &propinfo, &proph); if (status == PICL_SUCCESS) { status = picl_get_propval(proph, &mask_no, sizeof (mask_no)); if (status != PICL_SUCCESS) { - log_printf("%9s", no_prop_value); + log_printf("%-9s", no_prop_value); } else { log_printf(dgettext(TEXT_DOMAIN, " %2d.%d"), - (mask_no>> 4) & 0xf, mask_no & 0xf); + (mask_no>> 4) & 0xf, mask_no & 0xf); } } else - log_printf("%9s", no_prop_value); + log_printf("%-9s", no_prop_value); done: log_printf("\n"); @@ -328,3 +848,701 @@ { log_printf("%2d ", num, 0); } + +static void +sun4v_disp_env_status() +{ + if (phyplatformh == 0) + return; + log_printf("\n"); + log_printf("============================"); + log_printf(" Environmental Status "); + log_printf("============================"); + log_printf("\n"); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_fan_sensors(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_fan_indicators(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_temp_sensors(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_temp_indicators(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_current_sensors(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_current_indicators(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_voltage_sensors(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_env_print_voltage_indicators(); + + class_node_found = 0; + sun4v_env_print_LEDs(); + + class_node_found = 0; + all_status_ok = 1; + sun4v_print_fru_status(); + + class_node_found = 0; + sun4v_print_fw_rev(); + + sun4v_print_chassis_serial_no(); +} + +/*ARGSUSED*/ +static int +sun4v_env_print_sensor_callback(picl_nodehdl_t nodeh, void *args) +{ + char val[PICL_PROPNAMELEN_MAX]; + picl_nodehdl_t parenth; + char *names[PARENT_NAMES]; + char *loc; + int i; + char *prop; + picl_errno_t err; + int32_t lo_warning, lo_shutdown; + int32_t hi_warning, hi_shutdown; + int32_t current_val; + + if (class_node_found == 0) { + class_node_found = 1; + return (PICL_WALK_TERMINATE); + } + + if (syserrlog == 0) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_OPERATIONAL_STATUS, val, + sizeof (val)); + if (err == PICL_SUCCESS) { + if (strcmp(val, "disabled") == 0) { + if (all_status_ok) { + all_status_ok = 0; + return (PICL_WALK_TERMINATE); + } + } else + return (PICL_WALK_CONTINUE); + } else { + all_status_ok = 0; + return (PICL_WALK_TERMINATE); + } + } + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth, + sizeof (parenth)); + if (err != PICL_SUCCESS) { + log_printf("\n"); + return (PICL_WALK_CONTINUE); + } + if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL) + return (PICL_WALK_TERMINATE); + for (i = 0; i < PARENT_NAMES; i++) + if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) { + while (--i > -1) + free(names[i]); + free(loc); + return (PICL_WALK_TERMINATE); + } + i = 0; + while (err == PICL_SUCCESS) { + if (parenth == chassish || parenth == phyplatformh) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_NAME, + names[i++], PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + i--; + break; + } + if (i == PARENT_NAMES) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + loc[0] = '\0'; + if (--i > -1) + loc = strncat(loc, names[i], strlen(names[i])); + while (--i > -1) { + loc = strncat(loc, "/", 1); + loc = strncat(loc, names[i], strlen(names[i])); + } + log_printf("%-12s", loc); + for (i = 0; i < PARENT_NAMES; i++) + free(names[i]); + free(loc); + err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val, + sizeof (val)); + if (err == PICL_SUCCESS) + log_printf("%-15s", val); + + prop = (char *)args; + if (!prop) { + log_printf("\n"); + return (PICL_WALK_CONTINUE); + } + if (picl_get_propval_by_name(nodeh, prop, ¤t_val, + sizeof (current_val)) != PICL_SUCCESS) { + log_printf("\n"); + return (PICL_WALK_CONTINUE); + } + if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_WARNING, + &lo_warning, sizeof (lo_warning)) != PICL_SUCCESS) + lo_warning = INVALID_THRESHOLD; + if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_SHUTDOWN, + &lo_shutdown, sizeof (lo_shutdown)) != PICL_SUCCESS) + lo_shutdown = INVALID_THRESHOLD; + if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_WARNING, + &hi_warning, sizeof (hi_warning)) != PICL_SUCCESS) + hi_warning = INVALID_THRESHOLD; + if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_SHUTDOWN, + &hi_shutdown, sizeof (hi_shutdown)) != PICL_SUCCESS) + hi_shutdown = INVALID_THRESHOLD; + + if ((lo_shutdown != INVALID_THRESHOLD && + current_val <= lo_shutdown) || + (hi_shutdown != INVALID_THRESHOLD && + current_val >= hi_shutdown)) { + log_printf("%-s", "failed ("); + log_printf("%-d", current_val); + log_printf("%-s", ")"); + } else if ((lo_warning != INVALID_THRESHOLD && + current_val <= lo_warning) || + (hi_warning != INVALID_THRESHOLD && + current_val >= hi_warning)) { + log_printf("%-s", "warning ("); + log_printf("%-d", current_val); + log_printf("%-s", ")"); + } else + log_printf("%-s", "ok"); + + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +/*ARGSUSED*/ +static int +sun4v_env_print_indicator_callback(picl_nodehdl_t nodeh, void *args) +{ + char val[PICL_PROPNAMELEN_MAX]; + char status[PICL_PROPNAMELEN_MAX]; + picl_nodehdl_t parenth; + char *names[PARENT_NAMES]; + char *loc; + int i = 0; + char *prop = (char *)args; + picl_errno_t err = PICL_SUCCESS; + + if (class_node_found == 0) { + class_node_found = 1; + return (PICL_WALK_TERMINATE); + } + if (syserrlog == 0) { + err = picl_get_propval_by_name(nodeh, + PICL_PROP_OPERATIONAL_STATUS, status, + sizeof (status)); + if (err == PICL_SUCCESS) { + if (strcmp(status, "disabled") == 0) { + if (all_status_ok) { + all_status_ok = 0; + return (PICL_WALK_TERMINATE); + } + } else + return (PICL_WALK_CONTINUE); + } else { + all_status_ok = 0; + return (PICL_WALK_TERMINATE); + } + } + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth, + sizeof (parenth)); + if (err != PICL_SUCCESS) { + log_printf("\n"); + return (PICL_WALK_CONTINUE); + } + if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL) + return (PICL_WALK_TERMINATE); + for (i = 0; i < PARENT_NAMES; i++) + if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) { + while (--i > -1) + free(names[i]); + free(loc); + return (PICL_WALK_TERMINATE); + } + i = 0; + while (err == PICL_SUCCESS) { + if (parenth == chassish || parenth == phyplatformh) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_NAME, + names[i++], PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + i--; + break; + } + if (i == PARENT_NAMES) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + loc[0] = '\0'; + if (--i > -1) + loc = strncat(loc, names[i], strlen(names[i])); + while (--i > -1) { + loc = strncat(loc, "/", 1); + loc = strncat(loc, names[i], strlen(names[i])); + } + log_printf("%-12s", loc); + for (i = 0; i < PARENT_NAMES; i++) + free(names[i]); + free(loc); + err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val, + sizeof (val)); + if (err == PICL_SUCCESS) + log_printf("%-15s", val); + if (syserrlog == 0) { + log_printf("%-8s", status); + return (PICL_WALK_CONTINUE); + } + err = picl_get_propval_by_name(nodeh, prop, val, + sizeof (val)); + if (err == PICL_SUCCESS) + log_printf("%-8s", val); + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +static void +sun4v_env_print_fan_sensors() +{ + char *fmt = "%-11s %-14s %-10s\n"; + /* + * If there isn't any fan sensor node, return now. + */ + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_RPM_SENSOR, (void *)PICL_CLASS_RPM_SENSOR, + sun4v_env_print_sensor_callback); + if (!class_node_found) + return; + log_printf("Fan sensors:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_RPM_SENSOR, + NULL, sun4v_env_print_sensor_callback); + if (all_status_ok) { + log_printf("All fan sensors are OK.\n"); + return; + } + } + log_printf("---------------------------------\n"); + log_printf(fmt, "Location", "Sensor", "Status", 0); + log_printf("---------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_SENSOR, + PICL_PROP_SPEED, sun4v_env_print_sensor_callback); +} + +static void +sun4v_env_print_fan_indicators() +{ + char *fmt = "%-11s %-14s %-10s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_RPM_INDICATOR, (void *)PICL_CLASS_RPM_INDICATOR, + sun4v_env_print_indicator_callback); + if (!class_node_found) + return; + log_printf("\nFan indicators:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_RPM_INDICATOR, + NULL, sun4v_env_print_indicator_callback); + if (all_status_ok) { + log_printf("All fan indicators are OK.\n"); + return; + } + } + log_printf("------------------------------------\n"); + log_printf(fmt, "Location", "Sensor", "Condition", 0); + log_printf("------------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_INDICATOR, + PICL_CLASS_RPM_INDICATOR, sun4v_env_print_indicator_callback); +} + +static void +sun4v_env_print_temp_sensors() +{ + char *fmt = "%-11s %-14s %-10s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_SENSOR, + (void *)PICL_PROP_TEMPERATURE, + sun4v_env_print_sensor_callback); + if (!class_node_found) + return; + + log_printf("\nTemperature sensors:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_SENSOR, + NULL, sun4v_env_print_sensor_callback); + if (all_status_ok) { + log_printf("All temperature sensors are OK.\n"); + return; + } + } + log_printf("---------------------------------\n"); + log_printf(fmt, "Location", "Sensor", "Status", 0); + log_printf("---------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_SENSOR, + (void *)PICL_PROP_TEMPERATURE, sun4v_env_print_sensor_callback); +} + +static void +sun4v_env_print_temp_indicators() +{ + char *fmt = "%-11s %-14s %-8s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_INDICATOR, (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); + if (!class_node_found) + return; + log_printf("\nTemperature indicators:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_INDICATOR, NULL, + sun4v_env_print_indicator_callback); + if (all_status_ok) { + log_printf("All temperature indicators are OK.\n"); + return; + } + } + log_printf("------------------------------\n"); + log_printf(fmt, "Location", "Indicator", "Condition", 0); + log_printf("------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_TEMPERATURE_INDICATOR, + (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); +} + +static void +sun4v_env_print_current_sensors() +{ + char *fmt = "%-11s %-14s %-10s\n"; + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_CURRENT_SENSOR, + (void *)PICL_PROP_CURRENT, sun4v_env_print_sensor_callback); + if (!class_node_found) + return; + log_printf("\nCurrent sensors:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_CURRENT_SENSOR, + NULL, sun4v_env_print_sensor_callback); + if (all_status_ok) { + log_printf("All current sensors are OK.\n"); + return; + } + } + log_printf("---------------------------------\n"); + log_printf(fmt, "Location", "Sensor", "Status", 0); + log_printf("---------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_CURRENT_SENSOR, (void *)PICL_PROP_CURRENT, + sun4v_env_print_sensor_callback); +} + +static void +sun4v_env_print_current_indicators() +{ + char *fmt = "%-11s %-14s %-8s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_CURRENT_INDICATOR, + (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); + if (!class_node_found) + return; + log_printf("\nCurrent indicators:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_CURRENT_INDICATOR, NULL, + sun4v_env_print_indicator_callback); + if (all_status_ok) { + log_printf("All current indicators are OK.\n"); + return; + } + } + log_printf("------------------------------------\n"); + log_printf(fmt, "Location", "Indicator", "Condition", 0); + log_printf("------------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_CURRENT_INDICATOR, + (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); +} + +static void +sun4v_env_print_voltage_sensors() +{ + char *fmt = "%-11s %-14s %-10s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_SENSOR, + PICL_PROP_VOLTAGE, + sun4v_env_print_sensor_callback); + if (!class_node_found) + return; + log_printf("\nVoltage sensors:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_SENSOR, + NULL, sun4v_env_print_sensor_callback); + if (all_status_ok) { + log_printf("All voltage sensors are OK.\n"); + return; + } + } + log_printf("---------------------------------\n"); + log_printf(fmt, "Location", "Sensor", "Status", 0); + log_printf("---------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_SENSOR, + (void *)PICL_PROP_VOLTAGE, + sun4v_env_print_sensor_callback); +} + +static void +sun4v_env_print_voltage_indicators() +{ + char *fmt = "%-11s %-14s %-8s\n"; + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_INDICATOR, + (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); + if (!class_node_found) + return; + log_printf("\nVoltage indicators:\n"); + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_INDICATOR, NULL, + sun4v_env_print_indicator_callback); + if (all_status_ok) { + log_printf("All voltage indicators are OK.\n"); + return; + } + } + log_printf("------------------------------------\n"); + log_printf(fmt, "Location", "Indicator", "Condition", 0); + log_printf("------------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_VOLTAGE_INDICATOR, + (void *)PICL_PROP_CONDITION, + sun4v_env_print_indicator_callback); +} + +static void +sun4v_env_print_LEDs() +{ + char *fmt = "%-11s %-14s %-8s\n"; + if (syserrlog == 0) + return; + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED, + (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback); + if (!class_node_found) + return; + log_printf("\nLEDs:\n"); + log_printf("--------------------------------\n"); + log_printf(fmt, "Location", "LED", "State", 0); + log_printf("--------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED, + (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback); +} + +/*ARGSUSED*/ +static int +sun4v_print_fru_status_callback(picl_nodehdl_t nodeh, void *args) +{ + char label[PICL_PROPNAMELEN_MAX]; + char status[PICL_PROPNAMELEN_MAX]; + picl_errno_t err; + picl_prophdl_t proph; + picl_nodehdl_t parenth; + char *names[PARENT_NAMES]; + char *loc; + int i; + + if (!class_node_found) { + class_node_found = 1; + return (PICL_WALK_TERMINATE); + } + err = picl_get_prop_by_name(nodeh, PICL_PROP_IS_FRU, &proph); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label, + sizeof (label)); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + err = picl_get_propval_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS, + status, sizeof (status)); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + if (syserrlog == 0) { + if (strcmp(status, "disabled") == 0) { + if (all_status_ok) { + all_status_ok = 0; + return (PICL_WALK_TERMINATE); + } + } else + return (PICL_WALK_CONTINUE); + } + err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth, + sizeof (parenth)); + if (err != PICL_SUCCESS) { + log_printf("\n"); + return (PICL_WALK_CONTINUE); + } + if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL) + return (PICL_WALK_TERMINATE); + for (i = 0; i < PARENT_NAMES; i++) + if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) { + while (--i > -1) + free(names[i]); + free(loc); + return (PICL_WALK_TERMINATE); + } + i = 0; + while (err == PICL_SUCCESS) { + if (parenth == chassish || parenth == phyplatformh) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_NAME, + names[i++], PICL_PROPNAMELEN_MAX); + if (err != PICL_SUCCESS) { + i--; + break; + } + if (i == PARENT_NAMES) + break; + err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT, + &parenth, sizeof (parenth)); + } + loc[0] = '\0'; + if (--i > -1) + loc = strncat(loc, names[i], strlen(names[i])); + while (--i > -1) { + loc = strncat(loc, "/", 1); + loc = strncat(loc, names[i], strlen(names[i])); + } + log_printf("%-21s", loc); + for (i = 0; i < PARENT_NAMES; i++) + free(names[i]); + free(loc); + log_printf("%-10s", label); + log_printf("%-9s", status); + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +static void +sun4v_print_fru_status() +{ + char *fmt = "%-20s %-9s %-8s\n"; + (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL, + sun4v_print_fru_status_callback); + if (!class_node_found) + return; + log_printf("\n"); + log_printf("============================"); + log_printf(" FRU Status "); + log_printf("============================"); + log_printf("\n"); + + if (syserrlog == 0) { + (void) picl_walk_tree_by_class(phyplatformh, + PICL_CLASS_MODULE, NULL, + sun4v_print_fru_status_callback); + if (all_status_ok) { + log_printf("All FRUs are enabled.\n"); + return; + } + } + log_printf(fmt, "Location", "Name", "Status", 0); + log_printf("-------------------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_MODULE, NULL, + sun4v_print_fru_status_callback); +} + +/*ARGSUSED*/ +static int +sun4v_print_fw_rev_callback(picl_nodehdl_t nodeh, void *args) +{ + char label[PICL_PROPNAMELEN_MAX]; + char rev[PICL_PROPNAMELEN_MAX]; + picl_errno_t err; + + if (!class_node_found) { + class_node_found = 1; + return (PICL_WALK_TERMINATE); + } + err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label, + sizeof (label)); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + err = picl_get_propval_by_name(nodeh, PICL_PROP_FW_REVISION, rev, + sizeof (rev)); + if (err != PICL_SUCCESS) + return (PICL_WALK_CONTINUE); + if (strlen(rev) == 0) + return (PICL_WALK_CONTINUE); + log_printf("%-21s", label); + log_printf("%-40s", rev); + log_printf("\n"); + return (PICL_WALK_CONTINUE); +} + +static void +sun4v_print_fw_rev() +{ + char *fmt = "%-20s %-10s\n"; + if (syserrlog == 0) + return; + (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL, + sun4v_print_fw_rev_callback); + if (!class_node_found) + return; + log_printf("\n"); + log_printf("============================"); + log_printf(" FW Version "); + log_printf("============================"); + log_printf("\n"); + log_printf(fmt, "Name", "Version", 0); + log_printf("----------------------------\n"); + (void) picl_walk_tree_by_class(phyplatformh, NULL, NULL, + sun4v_print_fw_rev_callback); +} + +static void +sun4v_print_chassis_serial_no() +{ + char val[PICL_PROPNAMELEN_MAX]; + picl_errno_t err; + if (syserrlog == 0 || chassish == 0) + return; + + log_printf("\n"); + log_printf("Chassis Serial Number"); + log_printf("\n"); + log_printf("---------------------\n"); + err = picl_get_propval_by_name(chassish, PICL_PROP_SERIAL_NUMBER, + val, sizeof (val)); + if (err == PICL_SUCCESS) + log_printf("%s", val); + log_printf("\n"); +}
--- a/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/lib/libprtdiag/common/pdevinfo_sun4v.c Sat Mar 31 18:24:05 2007 -0700 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -68,8 +67,8 @@ /* Function prototypes */ Prom_node *sun4v_walk(Sys_tree *, Prom_node *, int); -int sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name, - picl_nodehdl_t *nodeh); +picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *, picl_nodehdl_t *); + /* * do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c * @@ -86,7 +85,7 @@ picl_nodehdl_t rooth; /* root PICL node for IO display */ picl_nodehdl_t plafh; /* Platform PICL node for IO display */ - int err; + picl_errno_t err; err = picl_initialize(); if (err != PICL_SUCCESS) { @@ -238,7 +237,7 @@ /* * search children to get the node by the nodename */ -int +picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name, picl_nodehdl_t *nodeh) {
--- a/usr/src/pkgdefs/SUNWhea/prototype_sparc Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/pkgdefs/SUNWhea/prototype_sparc Sat Mar 31 18:24:05 2007 -0700 @@ -278,6 +278,7 @@ f none usr/platform/sun4v/include/sys/cpu_sgnblk_defs.h 644 root bin f none usr/platform/sun4v/include/sys/ddi_subrdefs.h 644 root bin f none usr/platform/sun4v/include/sys/ds_pri.h 644 root bin +f none usr/platform/sun4v/include/sys/ds_snmp.h 644 root bin f none usr/platform/sun4v/include/sys/dvma.h 644 root bin f none usr/platform/sun4v/include/sys/eeprom.h 644 root bin f none usr/platform/sun4v/include/sys/fcode.h 644 root bin
--- a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc Sat Mar 31 18:24:05 2007 -0700 @@ -59,10 +59,12 @@ d none platform/sun4v/kernel/drv 755 root sys f none platform/sun4v/kernel/drv/drctl.conf 0644 root sys f none platform/sun4v/kernel/drv/ds_pri.conf 644 root sys +f none platform/sun4v/kernel/drv/ds_snmp.conf 644 root sys d none platform/sun4v/kernel/drv/sparcv9 755 root sys f none platform/sun4v/kernel/drv/sparcv9/cnex 755 root sys f none platform/sun4v/kernel/drv/sparcv9/drctl 755 root sys f none platform/sun4v/kernel/drv/sparcv9/ds_pri 755 root sys +f none platform/sun4v/kernel/drv/sparcv9/ds_snmp 755 root sys f none platform/sun4v/kernel/drv/sparcv9/vcc 755 root sys f none platform/sun4v/kernel/drv/sparcv9/vdc 755 root sys f none platform/sun4v/kernel/drv/sparcv9/vds 755 root sys
--- a/usr/src/pkgdefs/SUNWpiclu/prototype_sparc Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/pkgdefs/SUNWpiclu/prototype_sparc Sat Mar 31 18:24:05 2007 -0700 @@ -20,7 +20,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -62,10 +62,16 @@ s none usr/platform/sun4u/lib/picl/plugins/libpiclenvmon.so=./libpiclenvmon.so.1 d none usr/platform/sun4v 755 root sys d none usr/platform/sun4v/lib 755 root bin +f none usr/platform/sun4v/lib/libpiclsnmp.so.1 755 root sys +s none usr/platform/sun4v/lib/libpiclsnmp.so=./libpiclsnmp.so.1 755 root sys d none usr/platform/sun4v/lib/picl 755 root sys d none usr/platform/sun4v/lib/picl/plugins 755 root sys f none usr/platform/sun4v/lib/picl/plugins/libmdescplugin.so.1 755 root sys s none usr/platform/sun4v/lib/picl/plugins/libmdescplugin.so=./libmdescplugin.so.1 +f none usr/platform/sun4v/lib/picl/plugins/libpriplugin.so.1 755 root sys +s none usr/platform/sun4v/lib/picl/plugins/libpriplugin.so=./libpriplugin.so.1 +f none usr/platform/sun4v/lib/picl/plugins/libsnmpplugin.so.1 755 root sys +s none usr/platform/sun4v/lib/picl/plugins/libsnmpplugin.so=./libsnmpplugin.so.1 d none usr/platform/SUNW,Sun-Blade-100 755 root sys d none usr/platform/SUNW,Sun-Blade-100/lib 755 root bin d none usr/platform/SUNW,Sun-Blade-100/lib/picl 755 root sys
--- a/usr/src/uts/sparc/os/name_to_major Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sparc/os/name_to_major Sat Mar 31 18:24:05 2007 -0700 @@ -216,3 +216,5 @@ pxb_plx 268 n2rng 269 physmem 270 +ds_snmp 271 +ds_pri 272
--- a/usr/src/uts/sun4v/Makefile.files Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sun4v/Makefile.files Sat Mar 31 18:24:05 2007 -0700 @@ -147,6 +147,7 @@ VDC_OBJS = vdc.o VDS_OBJS = vds.o DS_PRI_OBJS = ds_pri.o +DS_SNMP_OBJS = ds_snmp.o # # Misc modules
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sun4v/Makefile.sun4v.shared Sat Mar 31 18:24:05 2007 -0700 @@ -327,6 +327,7 @@ DRV_KMODS += dma DRV_KMODS += drctl DRV_KMODS += ds_pri +DRV_KMODS += ds_snmp DRV_KMODS += ebus DRV_KMODS += fpc DRV_KMODS += glvc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/ds_snmp/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -0,0 +1,99 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the pseudo device +# to access the sun4v SNMP +# +# sun4v implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = ds_snmp +OBJECTS = $(DS_SNMP_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(DS_SNMP_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/sun4v/io + +# +# Include common rules. +# +include $(UTSBASE)/sun4v/Makefile.sun4v + +# +# Override defaults to build a unique, local modstubs.o. +# +MODSTUBS_DIR = $(OBJS_DIR) + +CLEANFILES += $(MODSTUBS_O) + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# lint pass one enforcement +# +CFLAGS += -v + +# +# Module Dependencies +LDFLAGS += -dy -Nmisc/ds + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/$(PLATFORM)/Makefile.targ
--- a/usr/src/uts/sun4v/io/ds.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sun4v/io/ds.c Sat Mar 31 18:24:05 2007 -0700 @@ -121,6 +121,15 @@ #define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP) /* + * Retry count and delay for LDC reads and writes + */ +#define DS_DEFAULT_RETRIES 10000 /* number of times to retry */ +#define DS_DEFAULT_DELAY 1000 /* usecs to wait between retries */ + +static int ds_retries = DS_DEFAULT_RETRIES; +static clock_t ds_delay = DS_DEFAULT_DELAY; + +/* * Supported versions of the DS message protocol * * The version array must be sorted in order from the highest @@ -205,22 +214,24 @@ #define DS_DBG_FLAG_LDC 0x1 #define DS_DBG_FLAG_LOG 0x2 +#define DS_DBG_FLAG_MSG 0x4 #define DS_DBG_FLAG_ALL 0xf #define DS_DBG if (ds_debug) printf #define DS_DBG_LDC if (ds_debug & DS_DBG_FLAG_LDC) printf #define DS_DBG_LOG if (ds_debug & DS_DBG_FLAG_LOG) printf -#define DS_DUMP_LDC_MSG(buf, len) ds_dump_ldc_msg(buf, len) +#define DS_DBG_MSG if (ds_debug & DS_DBG_FLAG_MSG) printf +#define DS_DUMP_MSG(buf, len) ds_dump_msg(buf, len) uint_t ds_debug = 0; -static void ds_dump_ldc_msg(void *buf, size_t len); +static void ds_dump_msg(void *buf, size_t len); #else /* DEBUG */ #define DS_DBG _NOTE(CONSTCOND) if (0) printf #define DS_DBG_LDC DS_DBG #define DS_DBG_LOG DS_DBG -#define DS_DUMP_LDC_MSG(buf, len) +#define DS_DUMP_MSG(buf, len) #endif /* DEBUG */ @@ -236,7 +247,7 @@ /* event processing functions */ static uint_t ds_ldc_cb(uint64_t event, caddr_t arg); static void ds_dispatch_event(void *arg); -static int ds_recv_msg(ldc_handle_t ldc_hdl, caddr_t msgp, size_t *sizep); +static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep); static void ds_handle_recv(void *arg); /* message sending functions */ @@ -764,42 +775,64 @@ * in the size parameter. */ static int -ds_recv_msg(ldc_handle_t ldc_hdl, caddr_t msgp, size_t *sizep) +ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep) { int rv = 0; - size_t msglen = *sizep; - size_t amt_left = msglen; - int loopcnt = 0; + size_t bytes_req = *sizep; + size_t bytes_left = bytes_req; + size_t nbytes; + int retry_count = 0; *sizep = 0; - while (msglen > 0) { - if ((rv = ldc_read(ldc_hdl, msgp, &amt_left)) != 0) { - if ((rv == EAGAIN) && (loopcnt++ < 1000)) { - /* - * Try again, but don't try for more than - * one second. Something is wrong with - * the channel. - */ - delay(drv_usectohz(10000)); /* 1/1000 sec */ - } else { - /* fail */ - return (rv); - } + DS_DBG_LDC("ds@%lx: attempting to read %ld bytes\n", port->id, + bytes_req); + + while (bytes_left > 0) { + + nbytes = bytes_left; + + if ((rv = ldc_read(port->ldc.hdl, msgp, &nbytes)) != 0) { + if (rv != EAGAIN) + break; } else { + if (nbytes != 0) { + DS_DBG_LDC("ds@%lx: read %ld bytes, %d " + "retries\n", port->id, nbytes, retry_count); + + *sizep += nbytes; + msgp += nbytes; + bytes_left -= nbytes; + + /* reset counter on a successful read */ + retry_count = 0; + continue; + } + /* - * Check for a zero length read. This - * indicates that there is no more data - * to read from the channel. + * No data was read. Check if this is the + * first attempt. If so, just return since + * nothing has been read yet. */ - if (amt_left == 0) + if (bytes_left == bytes_req) { + DS_DBG_LDC("ds@%lx: read zero bytes, no data " + "available\n", port->id); break; - - *sizep += amt_left; - msgp += amt_left; - msglen -= amt_left; - amt_left = msglen; + } } + + /* + * A retry is necessary because the read returned + * EAGAIN, or a zero length read occurred after + * reading a partial message. + */ + if (retry_count++ >= ds_retries) { + DS_DBG_LDC("ds@%lx: timed out waiting for " + "message\n", port->id); + break; + } + + drv_usecwait(ds_delay); } return (rv); @@ -845,7 +878,7 @@ currp = hbuf; /* read in the message header */ - if ((rv = ds_recv_msg(ldc_hdl, currp, &read_size)) != 0) { + if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) { cmn_err(CE_NOTE, "ds@%lx: ldc_read returned %d", port->id, rv); continue; @@ -874,7 +907,7 @@ currp = (char *)(msg) + DS_HDR_SZ; /* read in the message body */ - if ((rv = ds_recv_msg(ldc_hdl, currp, &read_size)) != 0) { + if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) { cmn_err(CE_NOTE, "ds@%lx: ldc_read returned %d", port->id, rv); kmem_free(msg, msglen); @@ -890,7 +923,7 @@ continue; } - DS_DUMP_LDC_MSG(msg, msglen); + DS_DUMP_MSG(msg, msglen); /* * Send the message for processing, and store it @@ -1555,7 +1588,8 @@ size_t amt_left = msglen; int loopcnt = 0; - DS_DUMP_LDC_MSG(msg, msglen); + DS_DUMP_MSG(msg, msglen); + (void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen); /* @@ -1568,12 +1602,8 @@ /* send the message */ do { if ((rv = ldc_write(port->ldc.hdl, currp, &msglen)) != 0) { - if ((rv == EWOULDBLOCK) && (loopcnt++ < 1000)) { - /* - * don't try for more than a sec. Something - * is wrong with the channel. - */ - delay(drv_usectohz(10000)); /* 1/1000 sec */ + if ((rv == EWOULDBLOCK) && (loopcnt++ < ds_retries)) { + drv_usecwait(ds_delay); } else { cmn_err(CE_WARN, "ds@%lx: send_msg: ldc_write failed (%d)", @@ -1879,7 +1909,7 @@ * and '.' otherwise. */ static void -ds_dump_ldc_msg(void *vbuf, size_t len) +ds_dump_msg(void *vbuf, size_t len) { int i, j; char *curr; @@ -1888,7 +1918,7 @@ uint8_t *buf = vbuf; /* abort if not debugging ldc */ - if (!(ds_debug & DS_DBG_FLAG_LDC)) { + if (!(ds_debug & DS_DBG_FLAG_MSG)) { return; } @@ -1923,7 +1953,7 @@ while (curr != (line + ASCIIOFFSET)) *curr++ = ' '; - DS_DBG_LDC("%s\n", line); + DS_DBG_MSG("%s\n", line); } } #endif /* DEBUG */
--- a/usr/src/uts/sun4v/io/ds_pri.c Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sun4v/io/ds_pri.c Sat Mar 31 18:24:05 2007 -0700 @@ -110,6 +110,7 @@ size_t ds_pri_len; uint64_t req_id; uint64_t last_req_id; + int num_opens; }; typedef struct ds_pri_state ds_pri_state_t; @@ -329,6 +330,7 @@ sp->ds_pri = NULL; sp->ds_pri_len = 0; sp->req_id = 0; + sp->num_opens = 0; if ((rv = ds_cap_init(&ds_pri_cap, &ds_pri_ops)) != 0) { cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv); @@ -403,6 +405,25 @@ if (sp == NULL) return (ENXIO); + mutex_enter(&sp->lock); + + /* + * If we're here and the state is DS_PRI_NO_SERVICE then this + * means that ds hasn't yet called the registration callback. + * Wait here and the callback will signal us when it has completed + * its work. + */ + if (sp->state == DS_PRI_NO_SERVICE) { + if (cv_wait_sig(&sp->cv, &sp->lock) == 0) { + mutex_exit(&sp->lock); + return (EINTR); + } + } + + sp->num_opens++; + + mutex_exit(&sp->lock); + /* * On open we dont fetch the PRI even if we have a valid service * handle. PRI fetch is essentially lazy and on-demand. @@ -419,6 +440,7 @@ ds_pri_close(dev_t dev, int flag, int otyp, cred_t *credp) { int instance; + ds_pri_state_t *sp; if (otyp != OTYP_CHR) return (EINVAL); @@ -426,9 +448,35 @@ DS_PRI_DBG("ds_pri_close\n"); instance = getminor(dev); - if (ddi_get_soft_state(ds_pri_statep, instance) == NULL) + if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL) return (ENXIO); + mutex_enter(&sp->lock); + if (!(sp->state & DS_PRI_HAS_SERVICE)) { + mutex_exit(&sp->lock); + return (0); + } + + if (--sp->num_opens > 0) { + mutex_exit(&sp->lock); + return (0); + } + + /* If we have an old PRI - remove it */ + if (sp->state & DS_PRI_HAS_PRI) { + if (sp->ds_pri != NULL && sp->ds_pri_len > 0) { + /* + * remove the old data if we have an + * outstanding request + */ + kmem_free(sp->ds_pri, sp->ds_pri_len); + sp->ds_pri_len = 0; + sp->ds_pri = NULL; + } + sp->state &= ~DS_PRI_HAS_PRI; + } + sp->state &= ~DS_PRI_REQUESTED; + mutex_exit(&sp->lock); return (0); } @@ -674,10 +722,14 @@ /* have service, but no PRI */ sp->state |= DS_PRI_HAS_SERVICE; - /* - * Cannot request a PRI here, because the reg handler cannot - * do a DS send operation - we take care of this later. - */ + /* + * Cannot request a PRI here, because the reg handler cannot + * do a DS send operation - we take care of this later. + */ + + /* Wake up anyone waiting in open() */ + cv_broadcast(&sp->cv); + mutex_exit(&sp->lock); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/io/ds_snmp.c Sat Mar 31 18:24:05 2007 -0700 @@ -0,0 +1,1014 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * sun4v domain services SNMP driver + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/open.h> +#include <sys/cred.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/ksynch.h> +#include <sys/modctl.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/debug.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/ds.h> +#include <sys/ds_snmp.h> + +#define DS_SNMP_NAME "ds_snmp" +#define DS_SNMP_MAX_OPENS 256 +#define DS_BITS_IN_UINT64 64 +#define DS_MINOR_POOL_SZ (DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64) +#define DS_SNMP_MINOR_SHIFT 56 +#define DS_SNMP_DBG if (ds_snmp_debug) printf + +typedef struct { + uint64_t seq_num; + uint64_t type; +} ds_snmp_msg_t; + +typedef enum { + DS_SNMP_REQUEST = 0, + DS_SNMP_REPLY = 1, + DS_SNMP_ERROR = 2 +} ds_snmp_msg_type_t; + +typedef enum { + DS_SNMP_READY = 0x0, + DS_SNMP_REQUESTED = 0x1, + DS_SNMP_DATA_AVL = 0x2, + DS_SNMP_DATA_ERR = 0x3 +} ds_snmp_flags_t; + +/* + * The single mutex 'lock' protects all the SNMP/DS variables in the state + * structure. + * + * The condition variable 'state_cv' helps serialize write() calls for a + * single descriptor. When write() is called, it sets a flag to indicate + * that an SNMP request has been made to the agent. No more write()'s on + * the same open descriptor will be allowed until this flag is cleared via + * a matching read(), where the requested packet is consumed on arrival. + * Read() then wakes up any waiters blocked in write() for sending the next + * SNMP request to the agent. + */ +typedef struct ds_snmp_state { + dev_info_t *dip; + int instance; + dev_t dev; + + /* SNMP/DS */ + kmutex_t lock; + kcondvar_t state_cv; + ds_snmp_flags_t state; + void *data; + size_t data_len; + uint64_t req_id; + uint64_t last_req_id; + uint64_t gencount; + boolean_t sc_reset; +} ds_snmp_state_t; + + +static uint_t ds_snmp_debug = 0; +static void *ds_snmp_statep = NULL; +static int ds_snmp_instance = -1; +static dev_info_t *ds_snmp_devi = NULL; + +/* + * The ds_snmp_lock mutex protects the following data global to the + * driver. + * + * The ds_snmp_service_cv condition variable is used to resolve the + * potential race between the registration of snmp service via a + * ds_cap_init() in attach(), the acknowledgement of this registration + * at a later time in ds_snmp_reg_handler(), and a possible open() at + * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are + * used to indicate whether the registration acknowledgement has happened + * or not. + * + * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of + * minor numbers dynamically. + */ +static kmutex_t ds_snmp_lock; +static kcondvar_t ds_snmp_service_cv; +static int ds_snmp_has_service = B_FALSE; +static ds_svc_hdl_t ds_snmp_handle = DS_INVALID_HDL; +static uint64_t ds_snmp_minor_pool[DS_MINOR_POOL_SZ]; /* bitmask */ +static int ds_snmp_num_opens = 0; + +static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t); +static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t); +static int ds_snmp_open(dev_t *, int, int, cred_t *); +static int ds_snmp_close(dev_t, int, int, cred_t *); +static int ds_snmp_read(dev_t, struct uio *, cred_t *); +static int ds_snmp_write(dev_t, struct uio *, cred_t *); +static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +/* + * DS Callbacks + */ +static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t); +static void ds_snmp_unreg_handler(ds_cb_arg_t arg); +static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen); + +/* + * SNMP DS capability registration + */ +static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 }; +static ds_capability_t ds_snmp_cap = { + "snmp", + &ds_snmp_ver_1_0, + 1 +}; + +/* + * SNMP DS Client callback vector + */ +static ds_clnt_ops_t ds_snmp_ops = { + ds_snmp_reg_handler, /* ds_reg_cb */ + ds_snmp_unreg_handler, /* ds_unreg_cb */ + ds_snmp_data_handler, /* ds_data_cb */ + NULL /* cb_arg */ +}; + +/* + * DS SNMP driver Ops Vector + */ +static struct cb_ops ds_snmp_cb_ops = { + ds_snmp_open, /* cb_open */ + ds_snmp_close, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + ds_snmp_read, /* cb_read */ + ds_snmp_write, /* cb_write */ + ds_snmp_ioctl, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + (struct streamtab *)NULL, /* cb_str */ + D_MP | D_64BIT, /* cb_flag */ + CB_REV, /* cb_rev */ + nodev, /* cb_aread */ + nodev /* cb_awrite */ +}; + +static struct dev_ops ds_snmp_dev_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + ds_snmp_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + ds_snmp_attach, /* devo_attach */ + ds_snmp_detach, /* devo_detach */ + nodev, /* devo_reset */ + &ds_snmp_cb_ops, /* devo_cb_ops */ + (struct bus_ops *)NULL, /* devo_bus_ops */ + nulldev /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, + "Domain Services SNMP Driver 1.0", + &ds_snmp_dev_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +int +_init(void) +{ + int retval; + + mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL); + + retval = ddi_soft_state_init(&ds_snmp_statep, + sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS); + if (retval != 0) { + cv_destroy(&ds_snmp_service_cv); + mutex_destroy(&ds_snmp_lock); + return (retval); + } + + retval = mod_install(&modlinkage); + if (retval != 0) { + ddi_soft_state_fini(&ds_snmp_statep); + cv_destroy(&ds_snmp_service_cv); + mutex_destroy(&ds_snmp_lock); + } + + return (retval); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int retval; + + if ((retval = mod_remove(&modlinkage)) != 0) + return (retval); + + ddi_soft_state_fini(&ds_snmp_statep); + + cv_destroy(&ds_snmp_service_cv); + mutex_destroy(&ds_snmp_lock); + + return (retval); +} + +/*ARGSUSED*/ +static int +ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) +{ + ds_snmp_state_t *sp; + int retval = DDI_FAILURE; + + ASSERT(resultp != NULL); + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg)); + if (sp != NULL) { + *resultp = sp->dip; + retval = DDI_SUCCESS; + } else + *resultp = NULL; + break; + + case DDI_INFO_DEVT2INSTANCE: + *resultp = (void *)(uintptr_t)getminor((dev_t)arg); + retval = DDI_SUCCESS; + break; + } + + return (retval); +} + +static int +ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int rv; + + switch (cmd) { + case DDI_ATTACH: + if (ds_snmp_instance != -1) + return (DDI_FAILURE); + break; + + case DDI_RESUME: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + ds_snmp_instance = ddi_get_instance(dip); + if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance, + DDI_PSEUDO, 0) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s@%d: Unable to create minor node", + DS_SNMP_NAME, ds_snmp_instance); + return (DDI_FAILURE); + } + + bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t)); + + ds_snmp_ops.cb_arg = dip; + if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) { + cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv); + ddi_remove_minor_node(dip, NULL); + ds_snmp_instance = -1; + return (DDI_FAILURE); + } + + ds_snmp_devi = dip; + ddi_report_dev(dip); + + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +static int +ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + switch (cmd) { + case DDI_DETACH: + if (ds_snmp_instance == -1) + return (DDI_FAILURE); + break; + + case DDI_SUSPEND: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + (void) ds_cap_fini(&ds_snmp_cap); + + ddi_remove_minor_node(ds_snmp_devi, NULL); + bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t)); + + ds_snmp_instance = -1; + ds_snmp_devi = NULL; + + return (DDI_SUCCESS); +} + +static minor_t +ds_snmp_get_minor(void) +{ + uint64_t val; + int i, ndx; + minor_t minor; + + mutex_enter(&ds_snmp_lock); + for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) { + val = ds_snmp_minor_pool[ndx]; + for (i = 0; i < DS_BITS_IN_UINT64; i++) { + if ((val & 0x1) == 0) { + ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i); + ds_snmp_num_opens++; + mutex_exit(&ds_snmp_lock); + + minor = ndx * DS_BITS_IN_UINT64 + i + 1; + + return (minor); + } + val >>= 1; + } + } + mutex_exit(&ds_snmp_lock); + + return (0); +} + +static void +ds_snmp_rel_minor(minor_t minor) +{ + int i, ndx; + + ndx = (minor - 1) / DS_BITS_IN_UINT64; + i = (minor - 1) % DS_BITS_IN_UINT64; + + ASSERT(ndx < DS_MINOR_POOL_SZ); + + mutex_enter(&ds_snmp_lock); + + ds_snmp_num_opens--; + ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i); + + mutex_exit(&ds_snmp_lock); +} + +static boolean_t +ds_snmp_is_open(minor_t minor) +{ + uint64_t val; + int i, ndx; + + ndx = (minor - 1) / DS_BITS_IN_UINT64; + i = (minor - 1) % DS_BITS_IN_UINT64; + + val = ((uint64_t)1 << i); + if (ds_snmp_minor_pool[ndx] & val) + return (B_TRUE); + else + return (B_FALSE); +} + +static int +ds_snmp_create_state(dev_t *devp) +{ + major_t major; + minor_t minor; + ds_snmp_state_t *sp; + + if ((minor = ds_snmp_get_minor()) == 0) + return (EMFILE); + + if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s@%d: Unable to allocate state", + DS_SNMP_NAME, minor); + ds_snmp_rel_minor(minor); + return (ENOMEM); + } + + sp = ddi_get_soft_state(ds_snmp_statep, minor); + if (devp != NULL) + major = getemajor(*devp); + else + major = ddi_driver_major(ds_snmp_devi); + + sp->dev = makedevice(major, minor); + if (devp != NULL) + *devp = sp->dev; + + sp->instance = minor; + sp->data = NULL; + sp->data_len = 0; + sp->req_id = 0; + sp->last_req_id = 0; + sp->state = DS_SNMP_READY; + sp->sc_reset = B_FALSE; + + mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL); + + return (0); +} + +static int +ds_snmp_destroy_state(dev_t dev) +{ + ds_snmp_state_t *sp; + minor_t minor; + + minor = getminor(dev); + + if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL) + return (ENXIO); + + ASSERT(sp->instance == minor); + + /* + * If the app has not exited cleanly, the data may not have been + * read/memory freed, hence take care of that here + */ + if (sp->data) { + kmem_free(sp->data, sp->data_len); + } + cv_destroy(&sp->state_cv); + mutex_destroy(&sp->lock); + + ddi_soft_state_free(ds_snmp_statep, minor); + ds_snmp_rel_minor(minor); + + return (0); +} + +/*ARGSUSED*/ +static int +ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + + if (otyp != OTYP_CHR) + return (EINVAL); + + if (ds_snmp_instance == -1) + return (ENXIO); + + /* + * Avoid possible race condition - ds service may not be there yet + */ + mutex_enter(&ds_snmp_lock); + while (ds_snmp_has_service == B_FALSE) { + if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) { + mutex_exit(&ds_snmp_lock); + return (EINTR); + } + } + mutex_exit(&ds_snmp_lock); + + return (ds_snmp_create_state(devp)); +} + + +/*ARGSUSED*/ +static int +ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + if (otyp != OTYP_CHR) + return (EINVAL); + + if (ds_snmp_instance == -1) + return (ENXIO); + + if (ds_snmp_handle == DS_INVALID_HDL) + return (EIO); + + return (ds_snmp_destroy_state(dev)); +} + +/*ARGSUSED*/ +static int +ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp) +{ + ds_snmp_state_t *sp; + minor_t minor; + size_t len; + int retval; + caddr_t tmpbufp = (caddr_t)NULL; + + /* + * Given that now we can have sc resets happening at any + * time, it is possible that it happened since the last time + * we issued a read, write or ioctl. If so, we need to wait + * for the unreg-reg pair to complete before we can do + * anything. + */ + mutex_enter(&ds_snmp_lock); + while (ds_snmp_has_service == B_FALSE) { + DS_SNMP_DBG("ds_snmp_read: waiting for service\n"); + if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) { + mutex_exit(&ds_snmp_lock); + return (EINTR); + } + } + mutex_exit(&ds_snmp_lock); + + if ((len = uiop->uio_resid) == 0) + return (0); + + minor = getminor(dev); + if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL) + return (ENXIO); + + mutex_enter(&sp->lock); + + if (sp->sc_reset == B_TRUE) { + mutex_exit(&sp->lock); + return (ECANCELED); + } + + /* + * Block or bail if there is no SNMP data + */ + if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) { + DS_SNMP_DBG("ds_snmp_read: no SNMP data\n"); + if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) { + mutex_exit(&sp->lock); + return (EAGAIN); + } + while (sp->state != DS_SNMP_DATA_AVL && + sp->state != DS_SNMP_DATA_ERR) { + if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) { + mutex_exit(&sp->lock); + return (EINTR); + } + } + } + + /* + * If there has been an error, it could be because the agent + * returned failure and there is no data to read, or an ldc-reset + * has happened. Figure out which and return appropriate + * error to the caller. + */ + if (sp->state == DS_SNMP_DATA_ERR) { + if (sp->sc_reset == B_TRUE) { + mutex_exit(&sp->lock); + DS_SNMP_DBG("ds_snmp_read: sc got reset, " + "returning ECANCELED\n"); + return (ECANCELED); + } else { + sp->state = DS_SNMP_READY; + cv_broadcast(&sp->state_cv); + mutex_exit(&sp->lock); + DS_SNMP_DBG("ds_snmp_read: data error, " + "returning EIO\n"); + return (EIO); + } + } + + if (len > sp->data_len) + len = sp->data_len; + + tmpbufp = kmem_alloc(len, KM_SLEEP); + + bcopy(sp->data, (void *)tmpbufp, len); + kmem_free(sp->data, sp->data_len); + sp->data = (caddr_t)NULL; + sp->data_len = 0; + + /* + * SNMP data has been consumed, wake up anyone waiting to send + */ + sp->state = DS_SNMP_READY; + cv_broadcast(&sp->state_cv); + + mutex_exit(&sp->lock); + + retval = uiomove(tmpbufp, len, UIO_READ, uiop); + kmem_free(tmpbufp, len); + + return (retval); +} + +/*ARGSUSED*/ +static int +ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp) +{ + ds_snmp_state_t *sp; + ds_snmp_msg_t hdr; + minor_t minor; + size_t len; + caddr_t tmpbufp; + + /* + * Check if there was an sc reset; if yes, wait until we have the + * service back again. + */ + mutex_enter(&ds_snmp_lock); + while (ds_snmp_has_service == B_FALSE) { + DS_SNMP_DBG("ds_snmp_write: waiting for service\n"); + if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) { + mutex_exit(&ds_snmp_lock); + return (EINTR); + } + } + mutex_exit(&ds_snmp_lock); + + minor = getminor(dev); + if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL) + return (ENXIO); + + len = uiop->uio_resid + sizeof (ds_snmp_msg_t); + tmpbufp = kmem_alloc(len, KM_SLEEP); + + if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t), + len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) { + kmem_free(tmpbufp, len); + return (EIO); + } + + mutex_enter(&sp->lock); + + if (sp->sc_reset == B_TRUE) { + mutex_exit(&sp->lock); + kmem_free(tmpbufp, len); + DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, " + "returning ECANCELD\n"); + return (ECANCELED); + } + + /* + * wait if earlier transaction is not yet completed + */ + while (sp->state != DS_SNMP_READY) { + if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) { + mutex_exit(&sp->lock); + kmem_free(tmpbufp, len); + return (EINTR); + } + /* + * Normally, only a reader would ever wake us up. But if we + * did get signalled with an ERROR, it could only mean there + * was an sc reset and there's no point waiting; we need to + * fail this write(). + */ + if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) { + DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, " + "returning ECANCELED\n"); + mutex_exit(&sp->lock); + kmem_free(tmpbufp, len); + return (ECANCELED); + } + } + + if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1)) + sp->req_id = 0; /* Reset */ + + hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id; + sp->last_req_id = hdr.seq_num; + (sp->req_id)++; + + /* + * Set state to SNMP_REQUESTED, but don't wakeup anyone yet + */ + sp->state = DS_SNMP_REQUESTED; + + mutex_exit(&sp->lock); + + hdr.type = DS_SNMP_REQUEST; + bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr)); + + /* + * If the service went away since the time we entered this + * routine and now, tough luck. Just ignore the current + * write() and return. + */ + mutex_enter(&ds_snmp_lock); + if (ds_snmp_has_service == B_FALSE) { + DS_SNMP_DBG("ds_snmp_write: service went away, aborting " + "write, returning ECANCELED\n"); + mutex_exit(&ds_snmp_lock); + kmem_free(tmpbufp, len); + return (ECANCELED); + } + DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n", + ds_snmp_handle, len); + (void) ds_cap_send(ds_snmp_handle, tmpbufp, len); + mutex_exit(&ds_snmp_lock); + + kmem_free(tmpbufp, len); + + return (0); +} + +/*ARGSUSED*/ +static int +ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + ds_snmp_state_t *sp; + struct dssnmp_info info; + minor_t minor; + + /* + * Check if there was an sc reset; if yes, wait until we have the + * service back again. + */ + mutex_enter(&ds_snmp_lock); + while (ds_snmp_has_service == B_FALSE) { + DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n"); + if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) { + mutex_exit(&ds_snmp_lock); + return (EINTR); + } + } + mutex_exit(&ds_snmp_lock); + + DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle); + + minor = getminor(dev); + if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL) + return (ENXIO); + + if (!(mode & FREAD)) + return (EACCES); + + switch (cmd) { + case DSSNMP_GETINFO: + mutex_enter(&sp->lock); + + if (sp->sc_reset == B_TRUE) { + mutex_exit(&sp->lock); + DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n"); + return (ECANCELED); + } + + while (sp->state != DS_SNMP_DATA_AVL && + sp->state != DS_SNMP_DATA_ERR) { + DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, " + "waiting for data\n", sp->state, sp->sc_reset); + if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) { + mutex_exit(&sp->lock); + return (EINTR); + } + } + DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, " + "out of wait!\n", sp->state, sp->sc_reset); + + /* + * If there has been an error, it could be because the + * agent returned failure and there is no data to read, + * or an ldc-reset has happened. Figure out which and + * return appropriate error to the caller. + */ + if (sp->state == DS_SNMP_DATA_ERR) { + if (sp->sc_reset == B_TRUE) { + mutex_exit(&sp->lock); + DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE " + "returning ECANCELED\n"); + return (ECANCELED); + } else { + sp->state = DS_SNMP_READY; + cv_broadcast(&sp->state_cv); + mutex_exit(&sp->lock); + DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE " + "returning EIO\n"); + return (EIO); + } + } + + info.size = sp->data_len; + info.token = sp->gencount; + + mutex_exit(&sp->lock); + + if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0) + return (EFAULT); + break; + + case DSSNMP_CLRLNKRESET: + mutex_enter(&sp->lock); + + DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n"); + DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset); + + if (sp->sc_reset == B_TRUE) { + if (sp->data) { + DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n", + sp->data, sp->data_len); + kmem_free(sp->data, sp->data_len); + } + sp->data = NULL; + sp->data_len = 0; + sp->state = DS_SNMP_READY; + sp->req_id = 0; + sp->last_req_id = 0; + sp->sc_reset = B_FALSE; + } + mutex_exit(&sp->lock); + break; + + default: + return (ENOTTY); + } + + return (0); +} + +/* + * DS Callbacks + */ +/*ARGSUSED*/ +static void +ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl) +{ + DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version " + "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor); + + mutex_enter(&ds_snmp_lock); + + ASSERT(ds_snmp_handle == DS_INVALID_HDL); + + ds_snmp_handle = hdl; + ds_snmp_has_service = B_TRUE; + + cv_broadcast(&ds_snmp_service_cv); + + mutex_exit(&ds_snmp_lock); + +} + +/*ARGSUSED*/ +static void +ds_snmp_unreg_handler(ds_cb_arg_t arg) +{ + minor_t minor; + ds_snmp_state_t *sp; + + DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n"); + + mutex_enter(&ds_snmp_lock); + + if (ds_snmp_num_opens) { + DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n", + ds_snmp_num_opens); + for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) { + if (ds_snmp_is_open(minor)) { + DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d " + "open\n", minor); + sp = ddi_get_soft_state(ds_snmp_statep, minor); + if (sp == NULL) + continue; + + /* + * Set the sc_reset flag and break any waiters + * out of their existing reads/writes/ioctls. + */ + DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to " + "signal waiters\n"); + mutex_enter(&sp->lock); + sp->sc_reset = B_TRUE; + sp->state = DS_SNMP_DATA_ERR; + cv_broadcast(&sp->state_cv); + mutex_exit(&sp->lock); + } + } + } + + ds_snmp_handle = DS_INVALID_HDL; + ds_snmp_has_service = B_FALSE; + + DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n"); + + mutex_exit(&ds_snmp_lock); +} + +/*ARGSUSED*/ +static void +ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen) +{ + ds_snmp_state_t *sp; + ds_snmp_msg_t hdr; + size_t snmp_size; + minor_t minor; + + /* + * Make sure the header is at least valid + */ + if (buflen < sizeof (hdr)) { + cmn_err(CE_WARN, + "ds_snmp_data_handler: buflen <%lu> too small", buflen); + return; + } + + ASSERT(buf != NULL); + bcopy(buf, (void *)&hdr, sizeof (hdr)); + + DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, " + "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num); + + minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT); + if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL) + return; + + mutex_enter(&sp->lock); + + /* + * If there is no pending SNMP request, then we've received + * bogus data or an SNMP trap. Since we don't yet support SNMP + * traps, ignore it. + */ + if (sp->state != DS_SNMP_REQUESTED) { + cmn_err(CE_WARN, "Received SNMP data without request"); + mutex_exit(&sp->lock); + return; + } + + /* + * Response to a request therefore old SNMP must've been consumed + */ + ASSERT(sp->data_len == 0); + ASSERT(sp->data == NULL); + + /* + * Response seq_num should match our request seq_num + */ + if (hdr.seq_num != sp->last_req_id) { + cmn_err(CE_WARN, "Received DS snmp data out of sequence with " + "request"); + mutex_exit(&sp->lock); + return; + } + + if (hdr.type == DS_SNMP_ERROR) { + sp->state = DS_SNMP_DATA_ERR; + DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n"); + } else { + snmp_size = buflen - sizeof (ds_snmp_msg_t); + sp->data = kmem_alloc(snmp_size, KM_SLEEP); + sp->data_len = snmp_size; + sp->state = DS_SNMP_DATA_AVL; + + bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t), + sp->data, sp->data_len); + } + + sp->gencount++; + + /* + * Wake up any readers waiting for data + */ + cv_broadcast(&sp->state_cv); + mutex_exit(&sp->lock); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/io/ds_snmp.conf Sat Mar 31 18:24:05 2007 -0700 @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +name="ds_snmp" parent="pseudo" instance=0;
--- a/usr/src/uts/sun4v/sys/Makefile Sat Mar 31 14:17:25 2007 -0700 +++ b/usr/src/uts/sun4v/sys/Makefile Sat Mar 31 18:24:05 2007 -0700 @@ -72,6 +72,7 @@ HDRS= \ ds_pri.h \ + ds_snmp.h \ hypervisor_api.h \ hsvc.h \ machasi.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/sys/ds_snmp.h Sat Mar 31 18:24:05 2007 -0700 @@ -0,0 +1,60 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DS_SNMP_H_ +#define _SYS_DS_SNMP_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ioctl info for ds_snmp device + */ + +#define DSSNMPIOC ('d' << 24 | 's' << 16 | 'p' << 8) + +#define DSSNMP_GETINFO (DSSNMPIOC | 1) /* Get SNMP size */ +#define DSSNMP_CLRLNKRESET (DSSNMPIOC | 2) /* Clear link reset flag */ + +/* + * DSSNMP_GETINFO + * Datamodel invariant. + */ +struct dssnmp_info { + uint64_t size; + uint64_t token; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DS_SNMP_H_ */