diff options
Diffstat (limited to 'lib')
37 files changed, 54815 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..d878b11 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,48 @@ +# Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Sun Microsystems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +INCLUDES = -I$(top_srcdir)/include +MAINTAINERCLEANFILES = Makefile.in + +noinst_LTLIBRARIES = libipmitool.la +libipmitool_la_SOURCES = helper.c ipmi_sdr.c ipmi_sel.c ipmi_sol.c ipmi_pef.c \ + ipmi_lanp.c ipmi_fru.c ipmi_chassis.c ipmi_mc.c log.c \ + dimm_spd.c ipmi_sensor.c ipmi_channel.c ipmi_event.c \ + ipmi_session.c ipmi_strings.c ipmi_user.c ipmi_raw.c \ + ipmi_oem.c ipmi_isol.c ipmi_sunoem.c ipmi_fwum.c ipmi_picmg.c \ + ipmi_main.c ipmi_tsol.c ipmi_firewall.c ipmi_kontronoem.c \ + ipmi_hpmfwupg.c ipmi_sdradd.c ipmi_ekanalyzer.c ipmi_gendev.c \ + ipmi_ime.c ipmi_delloem.c ipmi_dcmi.c hpm2.c \ + ../src/plugins/lan/md5.c ../src/plugins/lan/md5.h + +libipmitool_la_LDFLAGS = -export-dynamic +libipmitool_la_LIBADD = -lm +libipmitool_la_DEPENDENCIES = + diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..0925a1f --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,601 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Sun Microsystems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am_libipmitool_la_OBJECTS = helper.lo ipmi_sdr.lo ipmi_sel.lo \ + ipmi_sol.lo ipmi_pef.lo ipmi_lanp.lo ipmi_fru.lo \ + ipmi_chassis.lo ipmi_mc.lo log.lo dimm_spd.lo ipmi_sensor.lo \ + ipmi_channel.lo ipmi_event.lo ipmi_session.lo ipmi_strings.lo \ + ipmi_user.lo ipmi_raw.lo ipmi_oem.lo ipmi_isol.lo \ + ipmi_sunoem.lo ipmi_fwum.lo ipmi_picmg.lo ipmi_main.lo \ + ipmi_tsol.lo ipmi_firewall.lo ipmi_kontronoem.lo \ + ipmi_hpmfwupg.lo ipmi_sdradd.lo ipmi_ekanalyzer.lo \ + ipmi_gendev.lo ipmi_ime.lo ipmi_delloem.lo ipmi_dcmi.lo \ + hpm2.lo md5.lo +libipmitool_la_OBJECTS = $(am_libipmitool_la_OBJECTS) +libipmitool_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libipmitool_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libipmitool_la_SOURCES) +DIST_SOURCES = $(libipmitool_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +ARCH = @ARCH@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASEDIR = @BASEDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTRO = @DISTRO@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTF_BMC = @INTF_BMC@ +INTF_BMC_LIB = @INTF_BMC_LIB@ +INTF_DUMMY = @INTF_DUMMY@ +INTF_DUMMY_LIB = @INTF_DUMMY_LIB@ +INTF_FREE = @INTF_FREE@ +INTF_FREE_LIB = @INTF_FREE_LIB@ +INTF_IMB = @INTF_IMB@ +INTF_IMB_LIB = @INTF_IMB_LIB@ +INTF_LAN = @INTF_LAN@ +INTF_LANPLUS = @INTF_LANPLUS@ +INTF_LANPLUS_LIB = @INTF_LANPLUS_LIB@ +INTF_LAN_LIB = @INTF_LAN_LIB@ +INTF_LIPMI = @INTF_LIPMI@ +INTF_LIPMI_LIB = @INTF_LIPMI_LIB@ +INTF_OPEN = @INTF_OPEN@ +INTF_OPEN_LIB = @INTF_OPEN_LIB@ +INTF_SERIAL = @INTF_SERIAL@ +INTF_SERIAL_LIB = @INTF_SERIAL_LIB@ +IPMITOOL_INTF_LIB = @IPMITOOL_INTF_LIB@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS = @OS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POW_LIB = @POW_LIB@ +PSTAMP = @PSTAMP@ +RANLIB = @RANLIB@ +RPMBUILD = @RPMBUILD@ +RPM_RELEASE = @RPM_RELEASE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_configure_args = @ac_configure_args@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I$(top_srcdir)/include +MAINTAINERCLEANFILES = Makefile.in +noinst_LTLIBRARIES = libipmitool.la +libipmitool_la_SOURCES = helper.c ipmi_sdr.c ipmi_sel.c ipmi_sol.c ipmi_pef.c \ + ipmi_lanp.c ipmi_fru.c ipmi_chassis.c ipmi_mc.c log.c \ + dimm_spd.c ipmi_sensor.c ipmi_channel.c ipmi_event.c \ + ipmi_session.c ipmi_strings.c ipmi_user.c ipmi_raw.c \ + ipmi_oem.c ipmi_isol.c ipmi_sunoem.c ipmi_fwum.c ipmi_picmg.c \ + ipmi_main.c ipmi_tsol.c ipmi_firewall.c ipmi_kontronoem.c \ + ipmi_hpmfwupg.c ipmi_sdradd.c ipmi_ekanalyzer.c ipmi_gendev.c \ + ipmi_ime.c ipmi_delloem.c ipmi_dcmi.c hpm2.c \ + ../src/plugins/lan/md5.c ../src/plugins/lan/md5.h + +libipmitool_la_LDFLAGS = -export-dynamic +libipmitool_la_LIBADD = -lm +libipmitool_la_DEPENDENCIES = +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libipmitool.la: $(libipmitool_la_OBJECTS) $(libipmitool_la_DEPENDENCIES) $(EXTRA_libipmitool_la_DEPENDENCIES) + $(libipmitool_la_LINK) $(libipmitool_la_OBJECTS) $(libipmitool_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dimm_spd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hpm2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_channel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_chassis.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_dcmi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_delloem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_ekanalyzer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_event.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_firewall.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_fru.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_fwum.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_gendev.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_hpmfwupg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_ime.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_isol.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_kontronoem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_lanp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_main.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_mc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_oem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_pef.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_picmg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_raw.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sdr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sdradd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sensor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sol.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_strings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sunoem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_tsol.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_user.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +md5.lo: ../src/plugins/lan/md5.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5.lo -MD -MP -MF $(DEPDIR)/md5.Tpo -c -o md5.lo `test -f '../src/plugins/lan/md5.c' || echo '$(srcdir)/'`../src/plugins/lan/md5.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/md5.Tpo $(DEPDIR)/md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../src/plugins/lan/md5.c' object='md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5.lo `test -f '../src/plugins/lan/md5.c' || echo '$(srcdir)/'`../src/plugins/lan/md5.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/dimm_spd.c b/lib/dimm_spd.c new file mode 100644 index 0000000..1f27de2 --- /dev/null +++ b/lib/dimm_spd.c @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_fru.h> + +#include <stdlib.h> +#include <string.h> + +extern int verbose; + +/* + * Also, see ipmi_fru.c. + * + * Apparently some systems have problems with FRU access greater than 16 bytes + * at a time, even when using byte (not word) access. In order to ensure we + * work with the widest variety of hardware request size is capped at 16 bytes. + * Since this may result in slowdowns on some systems with lots of FRU data you + * can change this define to enable larger (up to 32 bytes at a time) access. + */ +#define FRU_DATA_RQST_SIZE 16; + +const struct valstr spd_memtype_vals[] = { + { 0x01, "STD FPM DRAM" }, + { 0x02, "EDO" }, + { 0x04, "SDRAM" }, + { 0x05, "ROM" }, + { 0x06, "DDR SGRAM" }, + { 0x07, "DDR SDRAM" }, + { 0x08, "DDR2 SDRAM" }, + { 0x09, "DDR2 SDRAM FB-DIMM" }, + { 0x0A, "DDR2 SDRAM FB-DIMM Probe" }, + { 0x0B, "DDR3 SDRAM" }, + { 0x00, NULL }, +}; + +const struct valstr ddr3_density_vals[] = +{ + { 0, "256 Mb" }, + { 1, "512 Mb" }, + { 2, "1 Gb" }, + { 3, "2 Gb" }, + { 4, "4 Gb" }, + { 5, "8 Gb" }, + { 6, "16 Gb" }, + { 0x00, NULL }, +}; + +const struct valstr ddr3_banks_vals[] = +{ + { 0, "3 (8 Banks)" }, + { 1, "4 (16 Banks)" }, + { 2, "5 (32 Banks)" }, + { 3, "6 (64 Banks)" }, + { 0x00, NULL }, +}; + +const struct valstr ddr3_ecc_vals[] = +{ + { 0, "0 bits" }, + { 1, "8 bits" }, + { 0x00, NULL }, +}; + +const struct valstr spd_config_vals[] = { + { 0x00, "None" }, + { 0x01, "Parity" }, + { 0x02, "ECC" }, + { 0x04, "Addr Cmd Parity" }, + { 0x00, NULL }, +}; + +const struct valstr spd_voltage_vals[] = { + { 0x00, "5.0V TTL" }, + { 0x01, "LVTTL" }, + { 0x02, "HSTL 1.5V" }, + { 0x03, "SSTL 3.3V" }, + { 0x04, "SSTL 2.5V" }, + { 0x05, "SSTL 1.8V" }, + { 0x00, NULL }, +}; + +/* + * JEDEC Standard Manufacturers Identification Code + * publication JEP106N, December 2003 + */ + +const struct valstr jedec_id1_vals[] = { + { 0x01, "AMD" }, + { 0x02, "AMI" }, + { 0x83, "Fairchild" }, + { 0x04, "Fujitsu" }, + { 0x85, "GTE" }, + { 0x86, "Harris" }, + { 0x07, "Hitachi" }, + { 0x08, "Inmos" }, + { 0x89, "Intel" }, + { 0x8a, "I.T.T." }, + { 0x0b, "Intersil" }, + { 0x8c, "Monolithic Memories" }, + { 0x0d, "Mostek" }, + { 0x0e, "Motorola" }, + { 0x8f, "National" }, + { 0x10, "NEC" }, + { 0x91, "RCA" }, + { 0x92, "Raytheon" }, + { 0x13, "Conexant (Rockwell)" }, + { 0x94, "Seeq" }, + { 0x15, "Philips Semi. (Signetics)" }, + { 0x16, "Synertek" }, + { 0x97, "Texas Instruments" }, + { 0x98, "Toshiba" }, + { 0x19, "Xicor" }, + { 0x1a, "Zilog" }, + { 0x9b, "Eurotechnique" }, + { 0x1c, "Mitsubishi" }, + { 0x9d, "Lucent (AT&T)" }, + { 0x9e, "Exel" }, + { 0x1f, "Atmel" }, + { 0x20, "SGS/Thomson" }, + { 0xa1, "Lattice Semi." }, + { 0xa2, "NCR" }, + { 0x23, "Wafer Scale Integration" }, + { 0xa4, "IBM" }, + { 0x25, "Tristar" }, + { 0x26, "Visic" }, + { 0xa7, "Intl. CMOS Technology" }, + { 0xa8, "SSSI" }, + { 0x29, "Microchip Technology" }, + { 0x2a, "Ricoh Ltd." }, + { 0xab, "VLSI" }, + { 0x2c, "Micron Technology" }, + { 0xad, "Hyundai Electronics" }, + { 0xae, "OKI Semiconductor" }, + { 0x2f, "ACTEL" }, + { 0xb0, "Sharp" }, + { 0x31, "Catalyst" }, + { 0x32, "Panasonic" }, + { 0xb3, "IDT" }, + { 0x34, "Cypress" }, + { 0xb5, "DEC" }, + { 0xb6, "LSI Logic" }, + { 0x37, "Zarlink" }, + { 0x38, "UTMC" }, + { 0xb9, "Thinking Machine" }, + { 0xba, "Thomson CSF" }, + { 0x3b, "Integrated CMOS(Vertex)" }, + { 0xbc, "Honeywell" }, + { 0x3d, "Tektronix" }, + { 0x3e, "Sun Microsystems" }, + { 0xbf, "SST" }, + { 0x40, "MOSEL" }, + { 0xc1, "Infineon" }, + { 0xc2, "Macronix" }, + { 0x43, "Xerox" }, + { 0xc4, "Plus Logic" }, + { 0x45, "SunDisk" }, + { 0x46, "Elan Circuit Tech." }, + { 0xc7, "European Silicon Str." }, + { 0xc8, "Apple Computer" }, + { 0xc9, "Xilinx" }, + { 0x4a, "Compaq" }, + { 0xcb, "Protocol Engines" }, + { 0x4c, "SCI" }, + { 0xcd, "Seiko Instruments" }, + { 0xce, "Samsung" }, + { 0x4f, "I3 Design System" }, + { 0xd0, "Klic" }, + { 0x51, "Crosspoint Solutions" }, + { 0x52, "Alliance Semiconductor" }, + { 0xd3, "Tandem" }, + { 0x54, "Hewlett-Packard" }, + { 0xd5, "Intg. Silicon Solutions" }, + { 0xd6, "Brooktree" }, + { 0x57, "New Media" }, + { 0x58, "MHS Electronic" }, + { 0xd9, "Performance Semi." }, + { 0xda, "Winbond Electronic" }, + { 0x5b, "Kawasaki Steel" }, + { 0xdc, "Bright Micro" }, + { 0x5d, "TECMAR" }, + { 0x5e, "Exar" }, + { 0xdf, "PCMCIA" }, + { 0xe0, "LG Semiconductor" }, + { 0x61, "Northern Telecom" }, + { 0x62, "Sanyo" }, + { 0xe3, "Array Microsystems" }, + { 0x64, "Crystal Semiconductor" }, + { 0xe5, "Analog Devices" }, + { 0xe6, "PMC-Sierra" }, + { 0x67, "Asparix" }, + { 0x68, "Convex Computer" }, + { 0xe9, "Quality Semiconductor" }, + { 0xea, "Nimbus Technology" }, + { 0x6b, "Transwitch" }, + { 0xec, "Micronas (ITT Intermetall)" }, + { 0x6d, "Cannon" }, + { 0x6e, "Altera" }, + { 0xef, "NEXCOM" }, + { 0x70, "QUALCOMM" }, + { 0xf1, "Sony" }, + { 0xf2, "Cray Research" }, + { 0x73, "AMS (Austria Micro)" }, + { 0xf4, "Vitesse" }, + { 0x75, "Aster Electronics" }, + { 0x76, "Bay Networks (Synoptic)" }, + { 0xf7, "Zentrum" }, + { 0xf8, "TRW" }, + { 0x79, "Thesys" }, + { 0x7a, "Solbourne Computer" }, + { 0xfb, "Allied-Signal" }, + { 0x7c, "Dialog" }, + { 0xfd, "Media Vision" }, + { 0xfe, "Level One Communication" }, + { 0x00, NULL }, +}; + +const struct valstr jedec_id2_vals[] = { + { 0x01, "Cirrus Logic" }, + { 0x02, "National Instruments" }, + { 0x83, "ILC Data Device" }, + { 0x04, "Alcatel Mietec" }, + { 0x85, "Micro Linear" }, + { 0x86, "Univ. of NC" }, + { 0x07, "JTAG Technologies" }, + { 0x08, "Loral" }, + { 0x89, "Nchip" }, + { 0x8A, "Galileo Tech" }, + { 0x0B, "Bestlink Systems" }, + { 0x8C, "Graychip" }, + { 0x0D, "GENNUM" }, + { 0x0E, "VideoLogic" }, + { 0x8F, "Robert Bosch" }, + { 0x10, "Chip Express" }, + { 0x91, "DATARAM" }, + { 0x92, "United Microelec Corp." }, + { 0x13, "TCSI" }, + { 0x94, "Smart Modular" }, + { 0x15, "Hughes Aircraft" }, + { 0x16, "Lanstar Semiconductor" }, + { 0x97, "Qlogic" }, + { 0x98, "Kingston" }, + { 0x19, "Music Semi" }, + { 0x1A, "Ericsson Components" }, + { 0x9B, "SpaSE" }, + { 0x1C, "Eon Silicon Devices" }, + { 0x9D, "Programmable Micro Corp" }, + { 0x9E, "DoD" }, + { 0x1F, "Integ. Memories Tech." }, + { 0x20, "Corollary Inc." }, + { 0xA1, "Dallas Semiconductor" }, + { 0xA2, "Omnivision" }, + { 0x23, "EIV(Switzerland)" }, + { 0xA4, "Novatel Wireless" }, + { 0x25, "Zarlink (formerly Mitel)" }, + { 0x26, "Clearpoint" }, + { 0xA7, "Cabletron" }, + { 0xA8, "Silicon Technology" }, + { 0x29, "Vanguard" }, + { 0x2A, "Hagiwara Sys-Com" }, + { 0xAB, "Vantis" }, + { 0x2C, "Celestica" }, + { 0xAD, "Century" }, + { 0xAE, "Hal Computers" }, + { 0x2F, "Rohm Company Ltd." }, + { 0xB0, "Juniper Networks" }, + { 0x31, "Libit Signal Processing" }, + { 0x32, "Enhanced Memories Inc." }, + { 0xB3, "Tundra Semiconductor" }, + { 0x34, "Adaptec Inc." }, + { 0xB5, "LightSpeed Semi." }, + { 0xB6, "ZSP Corp." }, + { 0x37, "AMIC Technology" }, + { 0x38, "Adobe Systems" }, + { 0xB9, "Dynachip" }, + { 0xBA, "PNY Electronics" }, + { 0x3B, "Newport Digital" }, + { 0xBC, "MMC Networks" }, + { 0x3D, "T Square" }, + { 0x3E, "Seiko Epson" }, + { 0xBF, "Broadcom" }, + { 0x40, "Viking Components" }, + { 0xC1, "V3 Semiconductor" }, + { 0xC2, "Flextronics (formerly Orbit)" }, + { 0x43, "Suwa Electronics" }, + { 0xC4, "Transmeta" }, + { 0x45, "Micron CMS" }, + { 0x46, "American Computer & Digital Components Inc" }, + { 0xC7, "Enhance 3000 Inc" }, + { 0xC8, "Tower Semiconductor" }, + { 0x49, "CPU Design" }, + { 0x4A, "Price Point" }, + { 0xCB, "Maxim Integrated Product" }, + { 0x4C, "Tellabs" }, + { 0xCD, "Centaur Technology" }, + { 0xCE, "Unigen Corporation" }, + { 0x4F, "Transcend Information" }, + { 0xD0, "Memory Card Technology" }, + { 0x51, "CKD Corporation Ltd." }, + { 0x52, "Capital Instruments, Inc." }, + { 0xD3, "Aica Kogyo, Ltd." }, + { 0x54, "Linvex Technology" }, + { 0xD5, "MSC Vertriebs GmbH" }, + { 0xD6, "AKM Company, Ltd." }, + { 0x57, "Dynamem, Inc." }, + { 0x58, "NERA ASA" }, + { 0xD9, "GSI Technology" }, + { 0xDA, "Dane-Elec (C Memory)" }, + { 0x5B, "Acorn Computers" }, + { 0xDC, "Lara Technology" }, + { 0x5D, "Oak Technology, Inc." }, + { 0x5E, "Itec Memory" }, + { 0xDF, "Tanisys Technology" }, + { 0xE0, "Truevision" }, + { 0x61, "Wintec Industries" }, + { 0x62, "Super PC Memory" }, + { 0xE3, "MGV Memory" }, + { 0x64, "Galvantech" }, + { 0xE5, "Gadzoox Nteworks" }, + { 0xE6, "Multi Dimensional Cons." }, + { 0x67, "GateField" }, + { 0x68, "Integrated Memory System" }, + { 0xE9, "Triscend" }, + { 0xEA, "XaQti" }, + { 0x6B, "Goldenram" }, + { 0xEC, "Clear Logic" }, + { 0x6D, "Cimaron Communications" }, + { 0x6E, "Nippon Steel Semi. Corp." }, + { 0xEF, "Advantage Memory" }, + { 0x70, "AMCC" }, + { 0xF1, "LeCroy" }, + { 0xF2, "Yamaha Corporation" }, + { 0x73, "Digital Microwave" }, + { 0xF4, "NetLogic Microsystems" }, + { 0x75, "MIMOS Semiconductor" }, + { 0x76, "Advanced Fibre" }, + { 0xF7, "BF Goodrich Data." }, + { 0xF8, "Epigram" }, + { 0x79, "Acbel Polytech Inc." }, + { 0x7A, "Apacer Technology" }, + { 0xFB, "Admor Memory" }, + { 0x7C, "FOXCONN" }, + { 0xFD, "Quadratics Superconductor" }, + { 0xFE, "3COM" }, + { 0x00, NULL }, +}; + +const struct valstr jedec_id3_vals[] = { + { 0x01, "Camintonn Corporation" }, + { 0x02, "ISOA Incorporated" }, + { 0x83, "Agate Semiconductor" }, + { 0x04, "ADMtek Incorporated" }, + { 0x85, "HYPERTEC" }, + { 0x86, "Adhoc Technologies" }, + { 0x07, "MOSAID Technologies" }, + { 0x08, "Ardent Technologies" }, + { 0x89, "Switchcore" }, + { 0x8A, "Cisco Systems, Inc." }, + { 0x0B, "Allayer Technologies" }, + { 0x8C, "WorkX AG" }, + { 0x0D, "Oasis Semiconductor" }, + { 0x0E, "Novanet Semiconductor" }, + { 0x8F, "E-M Solutions" }, + { 0x10, "Power General" }, + { 0x91, "Advanced Hardware Arch." }, + { 0x92, "Inova Semiconductors GmbH" }, + { 0x13, "Telocity" }, + { 0x94, "Delkin Devices" }, + { 0x15, "Symagery Microsystems" }, + { 0x16, "C-Port Corporation" }, + { 0x97, "SiberCore Technologies" }, + { 0x98, "Southland Microsystems" }, + { 0x19, "Malleable Technologies" }, + { 0x1A, "Kendin Communications" }, + { 0x9B, "Great Technology Microcomputer" }, + { 0x1C, "Sanmina Corporation" }, + { 0x9D, "HADCO Corporation" }, + { 0x9E, "Corsair" }, + { 0x1F, "Actrans System Inc." }, + { 0x20, "ALPHA Technologies" }, + { 0xA1, "Cygnal Integrated Products Incorporated" }, + { 0xA2, "Artesyn Technologies" }, + { 0x23, "Align Manufacturing" }, + { 0xA4, "Peregrine Semiconductor" }, + { 0x25, "Chameleon Systems" }, + { 0x26, "Aplus Flash Technology" }, + { 0xA7, "MIPS Technologies" }, + { 0xA8, "Chrysalis ITS" }, + { 0x29, "ADTEC Corporation" }, + { 0x2A, "Kentron Technologies" }, + { 0xAB, "Win Technologies" }, + { 0x2C, "ASIC Designs Inc" }, + { 0xAD, "Extreme Packet Devices" }, + { 0xAE, "RF Micro Devices" }, + { 0x2F, "Siemens AG" }, + { 0xB0, "Sarnoff Corporation" }, + { 0x31, "Itautec Philco SA" }, + { 0x32, "Radiata Inc." }, + { 0xB3, "Benchmark Elect. (AVEX)" }, + { 0x34, "Legend" }, + { 0xB5, "SpecTek Incorporated" }, + { 0xB6, "Hi/fn" }, + { 0x37, "Enikia Incorporated" }, + { 0x38, "SwitchOn Networks" }, + { 0xB9, "AANetcom Incorporated" }, + { 0xBA, "Micro Memory Bank" }, + { 0x3B, "ESS Technology" }, + { 0xBC, "Virata Corporation" }, + { 0x3D, "Excess Bandwidth" }, + { 0x3E, "West Bay Semiconductor" }, + { 0xBF, "DSP Group" }, + { 0x40, "Newport Communications" }, + { 0xC1, "Chip2Chip Incorporated" }, + { 0xC2, "Phobos Corporation" }, + { 0x43, "Intellitech Corporation" }, + { 0xC4, "Nordic VLSI ASA" }, + { 0x45, "Ishoni Networks" }, + { 0x46, "Silicon Spice" }, + { 0xC7, "Alchemy Semiconductor" }, + { 0xC8, "Agilent Technologies" }, + { 0x49, "Centillium Communications" }, + { 0x4A, "W.L. Gore" }, + { 0xCB, "HanBit Electronics" }, + { 0x4C, "GlobeSpan" }, + { 0xCD, "Element 14" }, + { 0xCE, "Pycon" }, + { 0x4F, "Saifun Semiconductors" }, + { 0xD0, "Sibyte, Incorporated" }, + { 0x51, "MetaLink Technologies" }, + { 0x52, "Feiya Technology" }, + { 0xD3, "I & C Technology" }, + { 0x54, "Shikatronics" }, + { 0xD5, "Elektrobit" }, + { 0xD6, "Megic" }, + { 0x57, "Com-Tier" }, + { 0x58, "Malaysia Micro Solutions" }, + { 0xD9, "Hyperchip" }, + { 0xDA, "Gemstone Communications" }, + { 0x5B, "Anadyne Microelectronics" }, + { 0xDC, "3ParData" }, + { 0x5D, "Mellanox Technologies" }, + { 0x5E, "Tenx Technologies" }, + { 0xDF, "Helix AG" }, + { 0xE0, "Domosys" }, + { 0x61, "Skyup Technology" }, + { 0x62, "HiNT Corporation" }, + { 0xE3, "Chiaro" }, + { 0x64, "MCI Computer GMBH" }, + { 0xE5, "Exbit Technology A/S" }, + { 0xE6, "Integrated Technology Express" }, + { 0x67, "AVED Memory" }, + { 0x68, "Legerity" }, + { 0xE9, "Jasmine Networks" }, + { 0xEA, "Caspian Networks" }, + { 0x6B, "nCUBE" }, + { 0xEC, "Silicon Access Networks" }, + { 0x6D, "FDK Corporation" }, + { 0x6E, "High Bandwidth Access" }, + { 0xEF, "MultiLink Technology" }, + { 0x70, "BRECIS" }, + { 0xF1, "World Wide Packets" }, + { 0xF2, "APW" }, + { 0x73, "Chicory Systems" }, + { 0xF4, "Xstream Logic" }, + { 0x75, "Fast-Chip" }, + { 0x76, "Zucotto Wireless" }, + { 0xF7, "Realchip" }, + { 0xF8, "Galaxy Power" }, + { 0x79, "eSilicon" }, + { 0x7A, "Morphics Technology" }, + { 0xFB, "Accelerant Networks" }, + { 0x7C, "Silicon Wave" }, + { 0xFD, "SandCraft" }, + { 0xFE, "Elpida" }, + { 0x00, NULL }, +}; + +const struct valstr jedec_id4_vals[] = { + { 0x01, "Solectron" }, + { 0x02, "Optosys Technologies" }, + { 0x83, "Buffalo (Formerly Melco)" }, + { 0x04, "TriMedia Technologies" }, + { 0x85, "Cyan Technologies" }, + { 0x86, "Global Locate" }, + { 0x07, "Optillion" }, + { 0x08, "Terago Communications" }, + { 0x89, "Ikanos Communications" }, + { 0x8A, "Princeton Technology" }, + { 0x0B, "Nanya Technology" }, + { 0x8C, "Elite Flash Storage" }, + { 0x0D, "Mysticom" }, + { 0x0E, "LightSand Communications" }, + { 0x8F, "ATI Technologies" }, + { 0x10, "Agere Systems" }, + { 0x91, "NeoMagic" }, + { 0x92, "AuroraNetics" }, + { 0x13, "Golden Empire" }, + { 0x94, "Muskin" }, + { 0x15, "Tioga Technologies" }, + { 0x16, "Netlist" }, + { 0x97, "TeraLogic" }, + { 0x98, "Cicada Semiconductor" }, + { 0x19, "Centon Electronics" }, + { 0x1A, "Tyco Electronics" }, + { 0x9B, "Magis Works" }, + { 0x1C, "Zettacom" }, + { 0x9D, "Cogency Semiconductor" }, + { 0x9E, "Chipcon AS" }, + { 0x1F, "Aspex Technology" }, + { 0x20, "F5 Networks" }, + { 0xA1, "Programmable Silicon Solutions" }, + { 0xA2, "ChipWrights" }, + { 0x23, "Acorn Networks" }, + { 0xA4, "Quicklogic" }, + { 0x25, "Kingmax Semiconductor" }, + { 0x26, "BOPS" }, + { 0xA7, "Flasys" }, + { 0xA8, "BitBlitz Communications" }, + { 0x29, "eMemory Technology" }, + { 0x2A, "Procket Networks" }, + { 0xAB, "Purple Ray" }, + { 0x2C, "Trebia Networks" }, + { 0xAD, "Delta Electronics" }, + { 0xAE, "Onex Communications" }, + { 0x2F, "Ample Communications" }, + { 0xB0, "Memory Experts Intl" }, + { 0x31, "Astute Networks" }, + { 0x32, "Azanda Network Devices" }, + { 0xB3, "Dibcom" }, + { 0x34, "Tekmos" }, + { 0xB5, "API NetWorks" }, + { 0xB6, "Bay Microsystems" }, + { 0x37, "Firecron Ltd" }, + { 0x38, "Resonext Communications" }, + { 0xB9, "Tachys Technologies" }, + { 0xBA, "Equator Technology" }, + { 0x3B, "Concept Computer" }, + { 0xBC, "SILCOM" }, + { 0x3D, "3Dlabs" }, + { 0x3E, "ct Magazine" }, + { 0xBF, "Sanera Systems" }, + { 0x40, "Silicon Packets" }, + { 0xC1, "Viasystems Group" }, + { 0xC2, "Simtek" }, + { 0x43, "Semicon Devices Singapore" }, + { 0xC4, "Satron Handelsges" }, + { 0x45, "Improv Systems" }, + { 0x46, "INDUSYS GmbH" }, + { 0xC7, "Corrent" }, + { 0xC8, "Infrant Technologies" }, + { 0x49, "Ritek Corp" }, + { 0x4A, "empowerTel Networks" }, + { 0xCB, "Hypertec" }, + { 0x4C, "Cavium Networks" }, + { 0xCD, "PLX Technology" }, + { 0xCE, "Massana Design" }, + { 0x4F, "Intrinsity" }, + { 0xD0, "Valence Semiconductor" }, + { 0x51, "Terawave Communications" }, + { 0x52, "IceFyre Semiconductor" }, + { 0xD3, "Primarion" }, + { 0x54, "Picochip Designs Ltd" }, + { 0xD5, "Silverback Systems" }, + { 0xD6, "Jade Star Technologies" }, + { 0x57, "Pijnenburg Securealink" }, + { 0x58, "MemorySolutioN" }, + { 0xD9, "Cambridge Silicon Radio" }, + { 0xDA, "Swissbit" }, + { 0x5B, "Nazomi Communications" }, + { 0xDC, "eWave System" }, + { 0x5D, "Rockwell Collins" }, + { 0x5E, "PAION" }, + { 0xDF, "Alphamosaic Ltd" }, + { 0xE0, "Sandburst" }, + { 0x61, "SiCon Video" }, + { 0x62, "NanoAmp Solutions" }, + { 0xE3, "Ericsson Technology" }, + { 0x64, "PrairieComm" }, + { 0xE5, "Mitac International" }, + { 0xE6, "Layer N Networks" }, + { 0x67, "Atsana Semiconductor" }, + { 0x68, "Allegro Networks" }, + { 0xE9, "Marvell Semiconductors" }, + { 0xEA, "Netergy Microelectronic" }, + { 0x6B, "NVIDIA" }, + { 0xEC, "Internet Machines" }, + { 0x6D, "Peak Electronics" }, + { 0xEF, "Accton Technology" }, + { 0x70, "Teradiant Networks" }, + { 0xF1, "Europe Technologies" }, + { 0xF2, "Cortina Systems" }, + { 0x73, "RAM Components" }, + { 0xF4, "Raqia Networks" }, + { 0x75, "ClearSpeed" }, + { 0x76, "Matsushita Battery" }, + { 0xF7, "Xelerated" }, + { 0xF8, "SimpleTech" }, + { 0x79, "Utron Technology" }, + { 0x7A, "Astec International" }, + { 0xFB, "AVM gmbH" }, + { 0x7C, "Redux Communications" }, + { 0xFD, "Dot Hill Systems" }, + { 0xFE, "TeraChip" }, + { 0x00, NULL }, +}; + +const struct valstr jedec_id5_vals[] = { + { 0x01, "T-RAM Incorporated" }, + { 0x02, "Innovics Wireless" }, + { 0x83, "Teknovus" }, + { 0x04, "KeyEye Communications" }, + { 0x85, "Runcom Technologies" }, + { 0x86, "RedSwitch" }, + { 0x07, "Dotcast" }, + { 0x08, "Silicon Mountain Memory" }, + { 0x89, "Signia Technologies" }, + { 0x8A, "Pixim" }, + { 0x0B, "Galazar Networks" }, + { 0x8C, "White Electronic Designs" }, + { 0x0D, "Patriot Scientific" }, + { 0x0E, "Neoaxiom Corporation" }, + { 0x8F, "3Y Power Technology" }, + { 0x10, "Europe Technologies" }, + { 0x91, "Potentia Power Systems" }, + { 0x92, "C-guys Incorporated" }, + { 0x13, "Digital Communications Technology Incorporated" }, + { 0x94, "Silicon-Based Technology" }, + { 0x15, "Fulcrum Microsystems" }, + { 0x16, "Positivo Informatica Ltd" }, + { 0x97, "XIOtech Corporation" }, + { 0x98, "PortalPlayer" }, + { 0x19, "Zhiying Software" }, + { 0x1A, "Direct2Data" }, + { 0x9B, "Phonex Broadband" }, + { 0x1C, "Skyworks Solutions" }, + { 0x9D, "Entropic Communications" }, + { 0x9E, "Pacific Force Technology" }, + { 0x1F, "Zensys A/S" }, + { 0x20, "Legend Silicon Corp." }, + { 0xA1, "sci-worx GmbH" }, + { 0xA2, "Oasis Silicon Systems" }, + { 0x23, "Renesas Technology" }, + { 0xA4, "Raza Microelectronics" }, + { 0x25, "Phyworks" }, + { 0x26, "MediaTek" }, + { 0xA7, "Non-cents Productions" }, + { 0xA8, "US Modular" }, + { 0x29, "Wintegra Ltd" }, + { 0x2A, "Mathstar" }, + { 0xAB, "StarCore" }, + { 0x2C, "Oplus Technologies" }, + { 0xAD, "Mindspeed" }, + { 0xAE, "Just Young Computer" }, + { 0x2F, "Radia Communications" }, + { 0xB0, "OCZ" }, + { 0x31, "Emuzed" }, + { 0x32, "LOGIC Devices" }, + { 0xB3, "Inphi Corporation" }, + { 0x34, "Quake Technologies" }, + { 0xB5, "Vixel" }, + { 0xB6, "SolusTek" }, + { 0x37, "Kongsberg Maritime" }, + { 0x38, "Faraday Technology" }, + { 0xB9, "Altium Ltd." }, + { 0xBA, "Insyte" }, + { 0x3B, "ARM Ltd." }, + { 0xBC, "DigiVision" }, + { 0x3D, "Vativ Technologies" }, + { 0x3E, "Endicott Interconnect Technologies" }, + { 0xBF, "Pericom" }, + { 0x40, "Bandspeed" }, + { 0xC1, "LeWiz Communications" }, + { 0xC2, "CPU Technology" }, + { 0x43, "Ramaxel Technology" }, + { 0xC4, "DSP Group" }, + { 0x45, "Axis Communications" }, + { 0x46, "Legacy Electronics" }, + { 0xC7, "Chrontel" }, + { 0xC8, "Powerchip Semiconductor" }, + { 0x49, "MobilEye Technologies" }, + { 0x4A, "Excel Semiconductor" }, + { 0xCB, "A-DATA Technology" }, + { 0x4C, "VirtualDigm" }, + { 0x00, NULL }, +}; + +int +ipmi_spd_print(uint8_t *spd_data, int len) +{ + int k = 0; + int ii = 0; + + if (len < 92) + return -1; /* we need first 91 bytes to do our thing */ + + printf(" Memory Type : %s\n", + val2str(spd_data[2], spd_memtype_vals)); + + if (spd_data[2] == 0x0B) /* DDR3 SDRAM */ + { + int iPN; + char *pchPN = spd_data+128; + int sdram_cap = 0; + int pri_bus_width = 0; + int sdram_width = 0; + int ranks = 0; + int mem_size = 0; + + if (len < 148) + return -1; /* we need first 91 bytes to do our thing */ + + + sdram_cap = ldexp(256,(spd_data[4]&15)); + pri_bus_width = ldexp(8,(spd_data[8]&7)); + sdram_width = ldexp(4,(spd_data[7]&7)); + ranks = ldexp(1,((spd_data[7]&0x3F)>>3)); + mem_size = (sdram_cap/8) * (pri_bus_width/sdram_width) * ranks; + printf(" SDRAM Capacity : %d MB\n", sdram_cap ); + printf(" Memory Banks : %s\n", val2str(spd_data[4]>>4, ddr3_banks_vals)); + printf(" Primary Bus Width : %d bits\n", pri_bus_width ); + printf(" SDRAM Device Width : %d bits\n", sdram_width ); + printf(" Number of Ranks : %d\n", ranks ); + printf(" Memory size : %d MB\n", mem_size ); + + /* printf(" Memory Density : %s\n", val2str(spd_data[4]&15, ddr3_density_vals)); */ + printf(" 1.5 V Nominal Op : %s\n", (((spd_data[6]&1) != 0) ? "No":"Yes" ) ); + printf(" 1.35 V Nominal Op : %s\n", (((spd_data[6]&2) != 0) ? "No":"Yes" ) ); + printf(" 1.2X V Nominal Op : %s\n", (((spd_data[6]&4) != 0) ? "No":"Yes" ) ); + printf(" Error Detect/Cor : %s\n", val2str(spd_data[8]>>3, ddr3_ecc_vals)); + + printf(" Manufacturer : "); + switch (spd_data[117]&127) + { + case 0: + printf("%s\n", val2str(spd_data[118], jedec_id1_vals)); + break; + + case 1: + printf("%s\n", val2str(spd_data[118], jedec_id2_vals)); + break; + + case 2: + printf("%s\n", val2str(spd_data[118], jedec_id3_vals)); + break; + + case 3: + printf("%s\n", val2str(spd_data[118], jedec_id4_vals)); + break; + + case 4: + printf("%s\n", val2str(spd_data[118], jedec_id5_vals)); + break; + + default: + printf("%s\n", "JEDEC JEP106 update required" ); + + } + + printf(" Manufacture Date : year %c%c week %c%c\n", + '0'+(spd_data[120]>>4), '0'+(spd_data[120]&15), '0'+(spd_data[121]>>4), '0'+(spd_data[121]&15) ); + + printf(" Serial Number : %02x%02x%02x%02x\n", + spd_data[122], spd_data[123], spd_data[124], spd_data[125]); + + printf(" Part Number : "); + for (iPN=0; iPN < 19; iPN++) + { + printf( "%c", *pchPN++ ); + } + printf("\n"); + } + else + { + ii = (spd_data[3] & 0x0f) + (spd_data[4] & 0x0f) - 17; + k = ((spd_data[5] & 0x7) + 1) * spd_data[17]; + + if(ii > 0 && ii <= 12 && k > 0) { + printf(" Memory Size : %d MB\n", ((1 << ii) * k)); + } else { + printf(" Memory Size INVALID: %d, %d, %d, %d\n", spd_data[3], + spd_data[4], spd_data[5], spd_data[17]); + } + printf(" Voltage Intf : %s\n", + val2str(spd_data[8], spd_voltage_vals)); + printf(" Error Detect/Cor : %s\n", + val2str(spd_data[11], spd_config_vals)); + + /* handle jedec table bank continuation values */ + printf(" Manufacturer : "); + if (spd_data[64] != 0x7f) + printf("%s\n", + val2str(spd_data[64], jedec_id1_vals)); + else { + if (spd_data[65] != 0x7f) + printf("%s\n", + val2str(spd_data[65], jedec_id2_vals)); + else { + if (spd_data[66] != 0x7f) + printf("%s\n", + val2str(spd_data[66], jedec_id3_vals)); + else { + if (spd_data[67] != 0x7f) + printf("%s\n", + val2str(spd_data[67], + jedec_id4_vals)); + else + printf("%s\n", + val2str(spd_data[68], + jedec_id5_vals)); + } + } + } + + if (spd_data[73]) { + char part[19]; + memcpy(part, spd_data+73, 18); + part[18] = 0; + printf(" Part Number : %s\n", part); + } + + printf(" Serial Number : %02x%02x%02x%02x\n", + spd_data[95], spd_data[96], spd_data[97], spd_data[98]); + } + + if (verbose) { + printf("\n"); + printbuf(spd_data, len, "SPD DATA"); + } + + return 0; +} + +int +ipmi_spd_print_fru(struct ipmi_intf * intf, uint8_t id) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_info fru; + uint8_t spd_data[256], msg_data[4]; + int len, offset; + + msg_data[0] = id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", + fru.size, fru.access ? "words" : "bytes"); + + if (fru.size < 1) { + lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + offset = 0; + memset(spd_data, 0, 256); + do { + msg_data[0] = id; + msg_data[1] = offset; + msg_data[2] = 0; + msg_data[3] = FRU_DATA_RQST_SIZE; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + + /* Timeouts are acceptable. No DIMM in the socket */ + if (rsp->ccode == 0xc3) + return 1; + + return -1; + } + + len = rsp->data[0]; + memcpy(&spd_data[offset], rsp->data + 1, len); + offset += len; + } while (offset < fru.size); + + /* now print spd info */ + ipmi_spd_print(spd_data, offset); + + return 0; +} diff --git a/lib/helper.c b/lib/helper.c new file mode 100644 index 0000000..4b903b0 --- /dev/null +++ b/lib/helper.c @@ -0,0 +1,789 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> /* For TIOCNOTTY */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <inttypes.h> +#include <signal.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_PATHS_H +# include <paths.h> +#else +# define _PATH_VARRUN "/var/run/" +#endif + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> + +extern int verbose; + +uint32_t buf2long(uint8_t * buf) +{ + return (uint32_t)(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]); +} + +uint16_t buf2short(uint8_t * buf) +{ + return (uint16_t)(buf[1] << 8 | buf[0]); +} + +const char * buf2str(uint8_t * buf, int len) +{ + static char str[2049]; + int i; + + if (len <= 0 || len > 1024) + return NULL; + + memset(str, 0, 2049); + + for (i=0; i<len; i++) + sprintf(str+i+i, "%2.2x", buf[i]); + + str[len*2] = '\0'; + + return (const char *)str; +} + +void printbuf(const uint8_t * buf, int len, const char * desc) +{ + int i; + + if (len <= 0) + return; + + if (verbose < 1) + return; + + fprintf(stderr, "%s (%d bytes)\n", desc, len); + for (i=0; i<len; i++) { + if (((i%16) == 0) && (i != 0)) + fprintf(stderr, "\n"); + fprintf(stderr, " %2.2x", buf[i]); + } + fprintf(stderr, "\n"); +} + +const char * val2str(uint16_t val, const struct valstr *vs) +{ + static char un_str[32]; + int i; + + for (i = 0; vs[i].str != NULL; i++) { + if (vs[i].val == val) + return vs[i].str; + } + + memset(un_str, 0, 32); + snprintf(un_str, 32, "Unknown (0x%02X)", val); + + return un_str; +} + +const char * oemval2str(uint32_t oem, uint16_t val, + const struct oemvalstr *vs) +{ + static char un_str[32]; + int i; + + for (i = 0; vs[i].oem != 0xffffff && vs[i].str != NULL; i++) { + /* FIXME: for now on we assume PICMG capability on all IANAs */ + if ( (vs[i].oem == oem || vs[i].oem == IPMI_OEM_PICMG) && + vs[i].val == val ) { + return vs[i].str; + } + } + + memset(un_str, 0, 32); + snprintf(un_str, 32, "Unknown (0x%X)", val); + + return un_str; +} + +/* str2double - safely convert string to double + * + * @str: source string to convert from + * @double_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2double(const char * str, double * double_ptr) +{ + char * end_ptr = 0; + if (!str || !double_ptr) + return (-1); + + *double_ptr = 0; + errno = 0; + *double_ptr = strtod(str, &end_ptr); + + if (*end_ptr != '\0') + return (-2); + + if (errno != 0) + return (-3); + + return 0; +} /* str2double(...) */ + +/* str2long - safely convert string to int64_t + * + * @str: source string to convert from + * @lng_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2long(const char * str, int64_t * lng_ptr) +{ + char * end_ptr = 0; + if (!str || !lng_ptr) + return (-1); + + *lng_ptr = 0; + errno = 0; + *lng_ptr = strtol(str, &end_ptr, 0); + + if (*end_ptr != '\0') + return (-2); + + if (errno != 0) + return (-3); + + return 0; +} /* str2long(...) */ + +/* str2ulong - safely convert string to uint64_t + * + * @str: source string to convert from + * @ulng_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2ulong(const char * str, uint64_t * ulng_ptr) +{ + char * end_ptr = 0; + if (!str || !ulng_ptr) + return (-1); + + *ulng_ptr = 0; + errno = 0; + *ulng_ptr = strtoul(str, &end_ptr, 0); + + if (*end_ptr != '\0') + return (-2); + + if (errno != 0) + return (-3); + + return 0; +} /* str2ulong(...) */ + +/* str2int - safely convert string to int32_t + * + * @str: source string to convert from + * @int_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2int(const char * str, int32_t * int_ptr) +{ + int rc = 0; + int64_t arg_long = 0; + if (!str || !int_ptr) + return (-1); + + if ( (rc = str2long(str, &arg_long)) != 0 ) { + *int_ptr = 0; + return rc; + } + + if (arg_long < INT32_MIN || arg_long > INT32_MAX) + return (-3); + + *int_ptr = (int32_t)arg_long; + return 0; +} /* str2int(...) */ + +/* str2uint - safely convert string to uint32_t + * + * @str: source string to convert from + * @uint_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2uint(const char * str, uint32_t * uint_ptr) +{ + int rc = 0; + uint64_t arg_ulong = 0; + if (!str || !uint_ptr) + return (-1); + + if ( (rc = str2ulong(str, &arg_ulong)) != 0) { + *uint_ptr = 0; + return rc; + } + + if (arg_ulong > UINT32_MAX) + return (-3); + + *uint_ptr = (uint32_t)arg_ulong; + return 0; +} /* str2uint(...) */ + +/* str2short - safely convert string to int16_t + * + * @str: source string to convert from + * @shrt_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2short(const char * str, int16_t * shrt_ptr) +{ + int rc = (-3); + int64_t arg_long = 0; + if (!str || !shrt_ptr) + return (-1); + + if ( (rc = str2long(str, &arg_long)) != 0 ) { + *shrt_ptr = 0; + return rc; + } + + if (arg_long < INT16_MIN || arg_long > INT16_MAX) + return (-3); + + *shrt_ptr = (int16_t)arg_long; + return 0; +} /* str2short(...) */ + +/* str2ushort - safely convert string to uint16_t + * + * @str: source string to convert from + * @ushrt_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) invalid input, (-3) for *flow + */ +int str2ushort(const char * str, uint16_t * ushrt_ptr) +{ + int rc = (-3); + uint64_t arg_ulong = 0; + if (!str || !ushrt_ptr) + return (-1); + + if ( (rc = str2ulong(str, &arg_ulong)) != 0 ) { + *ushrt_ptr = 0; + return rc; + } + + if (arg_ulong > UINT16_MAX) + return (-3); + + *ushrt_ptr = (uint16_t)arg_ulong; + return 0; +} /* str2ushort(...) */ + +/* str2char - safely convert string to int8 + * + * @str: source string to convert from + * @chr_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) or (-3) if conversion fails + */ +int str2char(const char *str, int8_t * chr_ptr) +{ + int rc = (-3); + int64_t arg_long = 0; + if (!str || !chr_ptr) { + return (-1); + } + if ((rc = str2long(str, &arg_long)) != 0) { + *chr_ptr = 0; + return rc; + } + if (arg_long < INT8_MIN || arg_long > INT8_MAX) { + return (-3); + } + return 0; +} /* str2char(...) */ + +/* str2uchar - safely convert string to uint8 + * + * @str: source string to convert from + * @uchr_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) if one of args is NULL, (-2) or (-3) if conversion fails + */ +int str2uchar(const char * str, uint8_t * uchr_ptr) +{ + int rc = (-3); + uint64_t arg_ulong = 0; + if (!str || !uchr_ptr) + return (-1); + + if ( (rc = str2ulong(str, &arg_ulong)) != 0 ) { + *uchr_ptr = 0; + return rc; + } + + if (arg_ulong > UINT8_MAX) + return (-3); + + *uchr_ptr = (uint8_t)arg_ulong; + return 0; +} /* str2uchar(...) */ + +uint16_t str2val(const char *str, const struct valstr *vs) +{ + int i; + + for (i = 0; vs[i].str != NULL; i++) { + if (strncasecmp(vs[i].str, str, __maxlen(str, vs[i].str)) == 0) + return vs[i].val; + } + + return vs[i].val; +} + +/* print_valstr - print value string list to log or stdout + * + * @vs: value string list to print + * @title: name of this value string list + * @loglevel: what log level to print, -1 for stdout + */ +void +print_valstr(const struct valstr * vs, const char * title, int loglevel) +{ + int i; + + if (vs == NULL) + return; + + if (title != NULL) { + if (loglevel < 0) + printf("\n%s:\n\n", title); + else + lprintf(loglevel, "\n%s:\n", title); + } + + if (loglevel < 0) { + printf(" VALUE\tHEX\tSTRING\n"); + printf("==============================================\n"); + } else { + lprintf(loglevel, " VAL\tHEX\tSTRING"); + lprintf(loglevel, "=============================================="); + } + + for (i = 0; vs[i].str != NULL; i++) { + if (loglevel < 0) { + if (vs[i].val < 256) + printf(" %d\t0x%02x\t%s\n", vs[i].val, vs[i].val, vs[i].str); + else + printf(" %d\t0x%04x\t%s\n", vs[i].val, vs[i].val, vs[i].str); + } else { + if (vs[i].val < 256) + lprintf(loglevel, " %d\t0x%02x\t%s", vs[i].val, vs[i].val, vs[i].str); + else + lprintf(loglevel, " %d\t0x%04x\t%s", vs[i].val, vs[i].val, vs[i].str); + } + } + + if (loglevel < 0) + printf("\n"); + else + lprintf(loglevel, ""); +} + +/* print_valstr_2col - print value string list in two columns to log or stdout + * + * @vs: value string list to print + * @title: name of this value string list + * @loglevel: what log level to print, -1 for stdout + */ +void +print_valstr_2col(const struct valstr * vs, const char * title, int loglevel) +{ + int i; + + if (vs == NULL) + return; + + if (title != NULL) { + if (loglevel < 0) + printf("\n%s:\n\n", title); + else + lprintf(loglevel, "\n%s:\n", title); + } + + for (i = 0; vs[i].str != NULL; i++) { + if (vs[i+1].str == NULL) { + /* last one */ + if (loglevel < 0) { + printf(" %4d %-32s\n", vs[i].val, vs[i].str); + } else { + lprintf(loglevel, " %4d %-32s\n", vs[i].val, vs[i].str); + } + } + else { + if (loglevel < 0) { + printf(" %4d %-32s %4d %-32s\n", + vs[i].val, vs[i].str, vs[i+1].val, vs[i+1].str); + } else { + lprintf(loglevel, " %4d %-32s %4d %-32s\n", + vs[i].val, vs[i].str, vs[i+1].val, vs[i+1].str); + } + i++; + } + } + + if (loglevel < 0) + printf("\n"); + else + lprintf(loglevel, ""); +} + +/* ipmi_csum - calculate an ipmi checksum + * + * @d: buffer to check + * @s: position in buffer to start checksum from + */ +uint8_t +ipmi_csum(uint8_t * d, int s) +{ + uint8_t c = 0; + for (; s > 0; s--, d++) + c += *d; + return -c; +} + +/* ipmi_open_file - safely open a file for reading or writing + * + * @file: filename + * @rw: read-write flag, 1=write + * + * returns pointer to file handler on success + * returns NULL on error + */ +FILE * +ipmi_open_file(const char * file, int rw) +{ + struct stat st1, st2; + FILE * fp; + + /* verify existance */ + if (lstat(file, &st1) < 0) { + if (rw) { + /* does not exist, ok to create */ + fp = fopen(file, "w"); + if (fp == NULL) { + lperror(LOG_ERR, "Unable to open file %s " + "for write", file); + return NULL; + } + /* created ok, now return the descriptor */ + return fp; + } else { + lprintf(LOG_ERR, "File %s does not exist", file); + return NULL; + } + } + +#ifndef ENABLE_FILE_SECURITY + if (!rw) { + /* on read skip the extra checks */ + fp = fopen(file, "r"); + if (fp == NULL) { + lperror(LOG_ERR, "Unable to open file %s", file); + return NULL; + } + return fp; + } +#endif + + /* it exists - only regular files, not links */ + if (S_ISREG(st1.st_mode) == 0) { + lprintf(LOG_ERR, "File %s has invalid mode: %d", + file, st1.st_mode); + return NULL; + } + + /* allow only files with 1 link (itself) */ + if (st1.st_nlink != 1) { + lprintf(LOG_ERR, "File %s has invalid link count: %d != 1", + file, (int)st1.st_nlink); + return NULL; + } + + fp = fopen(file, rw ? "w+" : "r"); + if (fp == NULL) { + lperror(LOG_ERR, "Unable to open file %s", file); + return NULL; + } + + /* stat again */ + if (fstat(fileno(fp), &st2) < 0) { + lperror(LOG_ERR, "Unable to stat file %s", file); + fclose(fp); + return NULL; + } + + /* verify inode */ + if (st1.st_ino != st2.st_ino) { + lprintf(LOG_ERR, "File %s has invalid inode: %d != %d", + file, st1.st_ino, st2.st_ino); + fclose(fp); + return NULL; + } + + /* verify owner */ + if (st1.st_uid != st2.st_uid) { + lprintf(LOG_ERR, "File %s has invalid user id: %d != %d", + file, st1.st_uid, st2.st_uid); + fclose(fp); + return NULL; + } + + /* verify inode */ + if (st2.st_nlink != 1) { + lprintf(LOG_ERR, "File %s has invalid link count: %d != 1", + file, st2.st_nlink); + fclose(fp); + return NULL; + } + + return fp; +} + +void +ipmi_start_daemon(struct ipmi_intf *intf) +{ + pid_t pid; + int fd; +#ifdef SIGHUP + sigset_t sighup; +#endif + +#ifdef SIGHUP + sigemptyset(&sighup); + sigaddset(&sighup, SIGHUP); + if (sigprocmask(SIG_UNBLOCK, &sighup, NULL) < 0) + fprintf(stderr, "ERROR: could not unblock SIGHUP signal\n"); + signal(SIGHUP, SIG_IGN); +#endif +#ifdef SIGTTOU + signal(SIGTTOU, SIG_IGN); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_IGN); +#endif +#ifdef SIGQUIT + signal(SIGQUIT, SIG_IGN); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + + pid = (pid_t) fork(); + if (pid < 0 || pid > 0) + exit(0); + +#if defined(SIGTSTP) && defined(TIOCNOTTY) + if (setpgid(0, getpid()) == -1) + exit(1); + if ((fd = open(_PATH_TTY, O_RDWR)) >= 0) { + ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#else + if (setpgid(0, 0) == -1) + exit(1); + pid = (pid_t) fork(); + if (pid < 0 || pid > 0) + exit(0); +#endif + + chdir("/"); + umask(0); + + for (fd=0; fd<64; fd++) { + if (fd != intf->fd) + close(fd); + } + + fd = open("/dev/null", O_RDWR); + assert(0 == fd); + dup(fd); + dup(fd); +} + +/* is_fru_id - wrapper for str-2-int FRU ID conversion. Message is printed + * on error. + * FRU ID range: <0..255> + * + * @argv_ptr: source string to convert from; usually argv + * @fru_id_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) on error and message is printed on STDERR + */ +int +is_fru_id(const char *argv_ptr, uint8_t *fru_id_ptr) +{ + if (!argv_ptr || !fru_id_ptr) { + lprintf(LOG_ERR, "is_fru_id(): invalid argument(s)."); + return (-1); + } + + if (str2uchar(argv_ptr, fru_id_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "FRU ID '%s' is either invalid or out of range.", + argv_ptr); + return (-1); +} /* is_fru_id(...) */ + +/* is_ipmi_channel_num - wrapper for str-2-int Channel conversion. Message is + * printed on error. + * + * 6.3 Channel Numbers, p. 45, IPMIv2 spec. + * Valid channel numbers are: <0..7>, <E-F> + * Reserved channel numbers: <8-D> + * + * @argv_ptr: source string to convert from; usually argv + * @channel_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) on error and message is printed on STDERR + */ +int +is_ipmi_channel_num(const char *argv_ptr, uint8_t *channel_ptr) +{ + if (!argv_ptr || !channel_ptr) { + lprintf(LOG_ERR, + "is_ipmi_channel_num(): invalid argument(s)."); + return (-1); + } + if ((str2uchar(argv_ptr, channel_ptr) == 0) + && ((*channel_ptr >= 0x0 && *channel_ptr <= 0x7) + || (*channel_ptr >= 0xE && *channel_ptr <= 0xF))) { + return 0; + } + lprintf(LOG_ERR, + "Given Channel number '%s' is either invalid or out of range.", + argv_ptr); + lprintf(LOG_ERR, "Channel number must be from ranges: <0..7>, <0xE..0xF>"); + return (-1); +} + +/* is_ipmi_user_id() - wrapper for str-2-uint IPMI UID conversion. Message is + * printed on error. + * + * @argv_ptr: source string to convert from; usually argv + * @ipmi_uid_ptr: pointer where to store result + * + * returns zero on success + * returns (-1) on error and message is printed on STDERR + */ +int +is_ipmi_user_id(const char *argv_ptr, uint8_t *ipmi_uid_ptr) +{ + if (!argv_ptr || !ipmi_uid_ptr) { + lprintf(LOG_ERR, + "is_ipmi_user_id(): invalid argument(s)."); + return (-1); + } + if ((str2uchar(argv_ptr, ipmi_uid_ptr) == 0) + && *ipmi_uid_ptr >= IPMI_UID_MIN + && *ipmi_uid_ptr <= IPMI_UID_MAX) { + return 0; + } + lprintf(LOG_ERR, + "Given User ID '%s' is either invalid or out of range.", + argv_ptr); + lprintf(LOG_ERR, "User ID is limited to range <%i..%i>.", + IPMI_UID_MIN, IPMI_UID_MAX); + return (-1); +} + +uint16_t +ipmi_get_oem_id(struct ipmi_intf *intf) +{ + /* Execute a Get Board ID command to determine the board */ + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint16_t oem_id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TSOL; + req.msg.cmd = 0x21; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Board ID command failed"); + return 0; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Board ID command failed: %#x %s", + rsp->ccode, val2str(rsp->ccode, completion_code_vals)); + return 0; + } + oem_id = rsp->data[0] | (rsp->data[1] << 8); + lprintf(LOG_DEBUG,"Board ID: %x", oem_id); + + return oem_id; +} diff --git a/lib/hpm2.c b/lib/hpm2.c new file mode 100644 index 0000000..e7d6c03 --- /dev/null +++ b/lib/hpm2.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pigeon Point Systems nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <ipmitool/hpm2.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/log.h> +#include <ipmitool/bswap.h> + +#if HAVE_PRAGMA_PACK +# pragma pack(push, 1) +#endif + +/* HPM.x Get Capabilities request */ +struct hpmx_cmd_get_capabilities_rq { + uint8_t picmg_id; + uint8_t hpmx_id; +} ATTRIBUTE_PACKING; + +/* HPM.2 Get Capabilities response */ +struct hpm2_cmd_get_capabilities_rp { + uint8_t picmg_id; + struct hpm2_lan_attach_capabilities caps; +} ATTRIBUTE_PACKING; + +#if HAVE_PRAGMA_PACK +# pragma pack(pop) +#endif + +/* IPMI Get LAN Configuration Parameters command */ +#define IPMI_LAN_GET_CONFIG 0x02 + +int hpm2_get_capabilities(struct ipmi_intf * intf, + struct hpm2_lan_attach_capabilities * caps) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + struct hpmx_cmd_get_capabilities_rq rq; + + /* reset result */ + memset(caps, 0, sizeof(struct hpm2_lan_attach_capabilities)); + + /* prepare request */ + rq.picmg_id = 0; + rq.hpmx_id = 2; + + /* prepare request */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPM2_GET_LAN_ATTACH_CAPABILITIES; + req.msg.data = (uint8_t *)&rq; + req.msg.data_len = sizeof(rq); + + + /* send */ + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_NOTICE, "Error sending request."); + return -1; + } + + if (rsp->ccode == 0xC1) { + lprintf(LOG_DEBUG, "IPM Controller is not HPM.2 compatible"); + return rsp->ccode; + } else if (rsp->ccode) { + lprintf(LOG_NOTICE, "Get HPM.x Capabilities request failed," + " compcode = %x", rsp->ccode); + return rsp->ccode; + } + + /* check response length */ + if (rsp->data_len < 2 || rsp->data_len > 10) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.x identifier */ + if (rsp->data[1] != 2) { + lprintf(LOG_NOTICE, "Bad HPM.x ID, id=%d", rsp->data[1]); + return rsp->ccode; + } + + /* + * this hardly can happen, since completion code is already checked. + * but check for safety + */ + if (rsp->data_len < 4) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* copy HPM.2 capabilities */ + memcpy(caps, rsp->data + 2, rsp->data_len - 2); + +#if WORDS_BIGENDIAN + /* swap bytes to convert from little-endian format */ + caps->lan_channel_mask = BSWAP_16(caps->lan_channel_mask); +#endif + + /* check HPM.2 revision */ + if (caps->hpm2_revision_id != HPM2_REVISION) { + lprintf(LOG_NOTICE, "Bad HPM.2 revision, rev=%d", + caps->hpm2_revision_id); + return -1; + } + + if (!caps->lan_channel_mask) { + return -1; + } + + /* check response length */ + if (rsp->data_len < 8) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.2 LAN parameters start */ + if (caps->hpm2_lan_params_start < 0xC0) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN params start, start=%x", + caps->hpm2_lan_params_start); + return -1; + } + + /* check HPM.2 LAN parameters revision */ + if (caps->hpm2_lan_params_rev != HPM2_LAN_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN params revision, rev=%d", + caps->hpm2_lan_params_rev); + return -1; + } + + /* check for HPM.2 SOL extension */ + if (!(caps->hpm2_caps & HPM2_CAPS_SOL_EXTENSION)) { + /* no further checks */ + return 0; + } + + /* check response length */ + if (rsp->data_len < 10) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.2 SOL parameters start */ + if (caps->hpm2_sol_params_start < 0xC0) { + lprintf(LOG_NOTICE, "Bad HPM.2 SOL params start, start=%x", + caps->hpm2_sol_params_start); + return -1; + } + + /* check HPM.2 SOL parameters revision */ + if (caps->hpm2_sol_params_rev != HPM2_SOL_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 SOL params revision, rev=%d", + caps->hpm2_sol_params_rev); + return -1; + } + + return 0; +} + +int hpm2_get_lan_channel_capabilities(struct ipmi_intf * intf, + uint8_t hpm2_lan_params_start, + struct hpm2_lan_channel_capabilities * caps) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + uint8_t rq[4]; + + /* reset result */ + memset(caps, 0, sizeof(struct hpm2_lan_channel_capabilities)); + + /* prepare request */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_GET_CONFIG; + req.msg.data = (uint8_t *)&rq; + req.msg.data_len = sizeof(rq); + + /* prepare request data */ + rq[0] = 0xE; /* sending channel */ + rq[1] = hpm2_lan_params_start; /* HPM.2 Channel Caps */ + rq[2] = rq[3] = 0; + + /* send */ + rsp = intf->sendrecv(intf, &req); + + if (rsp) { + lprintf(LOG_NOTICE, "Error sending request"); + return -1; + } + + if (rsp->ccode == 0x80) { + lprintf(LOG_DEBUG, "HPM.2 Channel Caps parameter is not supported"); + return rsp->ccode; + } else if (rsp->ccode) { + lprintf(LOG_NOTICE, "Get LAN Configuration Parameters request failed," + " compcode = %x", rsp->ccode); + return rsp->ccode; + } + + /* check response length */ + if (rsp->data_len != sizeof (struct hpm2_lan_channel_capabilities) + 1) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check parameter revision */ + if (rsp->data[0] != HPM2_LAN_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN parameter revision, rev=%d", + rsp->data[0]); + return -1; + } + + /* copy parameter data */ + memcpy(caps, &rsp->data[1], sizeof (struct hpm2_lan_channel_capabilities)); + +#if WORDS_BIGENDIAN + /* swap bytes to convert from little-endian format */ + caps->max_inbound_pld_size = BSWAP_16(caps->max_inbound_pld_size); + caps->max_outbound_pld_size = BSWAP_16(caps->max_outbound_pld_size); +#endif + + return 0; +} + +int hpm2_detect_max_payload_size(struct ipmi_intf * intf) +{ + struct hpm2_lan_attach_capabilities attach_caps; + struct hpm2_lan_channel_capabilities channel_caps; + int err; + + /* query HPM.2 support */ + err = hpm2_get_capabilities(intf, &attach_caps); + + /* check if HPM.2 is supported */ + if (err != 0 || !attach_caps.lan_channel_mask) { + return err; + } + + /* query channel capabilities */ + err = hpm2_get_lan_channel_capabilities(intf, + attach_caps.hpm2_lan_params_start, &channel_caps); + + /* check if succeeded */ + if (err != 0) { + return err; + } + + /* update request and response sizes */ + ipmi_intf_set_max_request_data_size(intf, + channel_caps.max_inbound_pld_size - 7); + ipmi_intf_set_max_response_data_size(intf, + channel_caps.max_outbound_pld_size - 8); + + /* print debug info */ + lprintf(LOG_DEBUG, "Set maximum request size to %d\n" + "Set maximum response size to %d", + intf->max_request_data_size, intf->max_response_data_size); + + return 0; +} diff --git a/lib/ipmi_channel.c b/lib/ipmi_channel.c new file mode 100644 index 0000000..43db338 --- /dev/null +++ b/lib/ipmi_channel.c @@ -0,0 +1,903 @@ +/* -*-mode: C; indent-tabs-mode: t; -*- + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_constants.h> + +extern int csv_output; +extern int verbose; + +void printf_channel_usage (void); + +/** + * ipmi_1_5_authtypes + * + * Create a string describing the supported authentication types as + * specificed by the parameter n + */ +static const char * +ipmi_1_5_authtypes(uint8_t n) +{ + uint32_t i; + static char supportedTypes[128]; + + bzero(supportedTypes, 128); + + for (i = 0; ipmi_authtype_vals[i].val != 0; i++) { + if (n & ipmi_authtype_vals[i].val) { + strcat(supportedTypes, ipmi_authtype_vals[i].str); + strcat(supportedTypes, " "); + } + } + + return supportedTypes; +} + + + +/** + * ipmi_get_channel_auth_cap + * + * return 0 on success + * -1 on failure + */ +int +ipmi_get_channel_auth_cap(struct ipmi_intf * intf, + uint8_t channel, + uint8_t priv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct get_channel_auth_cap_rsp auth_cap; + uint8_t msg_data[2]; + + msg_data[0] = channel | 0x80; // Ask for IPMI v2 data as well + msg_data[1] = priv; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_AUTH_CAP; // 0x38 + req.msg.data = msg_data; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + + if ((rsp == NULL) || (rsp->ccode > 0)) { + /* + * It's very possible that this failed because we asked for IPMI v2 data + * Ask again, without requesting IPMI v2 data + */ + msg_data[0] &= 0x7F; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Authentication Capabilities"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Authentication Capabilities failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } + + memcpy(&auth_cap, rsp->data, sizeof(struct get_channel_auth_cap_rsp)); + + printf("Channel number : %d\n", + auth_cap.channel_number); + printf("IPMI v1.5 auth types : %s\n", + ipmi_1_5_authtypes(auth_cap.enabled_auth_types)); + + if (auth_cap.v20_data_available) + printf("KG status : %s\n", + (auth_cap.kg_status) ? "non-zero" : "default (all zeroes)"); + + printf("Per message authentication : %sabled\n", + (auth_cap.per_message_auth) ? "dis" : "en"); + printf("User level authentication : %sabled\n", + (auth_cap.user_level_auth) ? "dis" : "en"); + + printf("Non-null user names exist : %s\n", + (auth_cap.non_null_usernames) ? "yes" : "no"); + printf("Null user names exist : %s\n", + (auth_cap.null_usernames) ? "yes" : "no"); + printf("Anonymous login enabled : %s\n", + (auth_cap.anon_login_enabled) ? "yes" : "no"); + + if (auth_cap.v20_data_available) { + printf("Channel supports IPMI v1.5 : %s\n", + (auth_cap.ipmiv15_support) ? "yes" : "no"); + printf("Channel supports IPMI v2.0 : %s\n", + (auth_cap.ipmiv20_support) ? "yes" : "no"); + } + + /* + * If there is support for an OEM authentication type, there is some + * information. + */ + if (auth_cap.enabled_auth_types & IPMI_1_5_AUTH_TYPE_BIT_OEM) { + printf("IANA Number for OEM : %d\n", + auth_cap.oem_id[0] | + auth_cap.oem_id[1] << 8 | + auth_cap.oem_id[2] << 16); + printf("OEM Auxiliary Data : 0x%x\n", + auth_cap.oem_aux_data); + } + + return 0; +} + + + +/** + * ipmi_get_channel_info + * + * returns 0 on success + * -1 on failure + * + */ +int +ipmi_get_channel_info(struct ipmi_intf * intf, uint8_t channel) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[2]; + uint8_t medium; + struct get_channel_info_rsp channel_info; + struct get_channel_access_rsp channel_access; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_INFO; // 0x42 + req.msg.data = &channel; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Info"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Info failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(&channel_info, rsp->data, sizeof(struct get_channel_info_rsp)); + + printf("Channel 0x%x info:\n", channel_info.channel_number); + + printf(" Channel Medium Type : %s\n", + val2str(channel_info.channel_medium, ipmi_channel_medium_vals)); + + printf(" Channel Protocol Type : %s\n", + val2str(channel_info.channel_protocol, ipmi_channel_protocol_vals)); + + printf(" Session Support : "); + switch (channel_info.session_support) { + case 0x0: + printf("session-less\n"); + break; + case 0x1: + printf("single-session\n"); + break; + case 0x2: + printf("multi-session\n"); + break; + case 0x3: + default: + printf("session-based\n"); + break; + } + + printf(" Active Session Count : %d\n", + channel_info.active_sessions); + + printf(" Protocol Vendor ID : %d\n", + channel_info.vendor_id[0] | + channel_info.vendor_id[1] << 8 | + channel_info.vendor_id[2] << 16); + + + /* only proceed if this is LAN channel */ + medium = ipmi_get_channel_medium(intf, channel); + if (medium != IPMI_CHANNEL_MEDIUM_LAN && + medium != IPMI_CHANNEL_MEDIUM_LAN_OTHER) { + return 0; + } + + memset(&req, 0, sizeof(req)); + rqdata[0] = channel & 0xf; + + /* get volatile settings */ + + rqdata[1] = 0x80; /* 0x80=active */ + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_ACCESS; // 0x41 + req.msg.data = rqdata; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Access (volatile)"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Access (volatile) failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(&channel_access, rsp->data, sizeof(struct get_channel_access_rsp)); + + + printf(" Volatile(active) Settings\n"); + printf(" Alerting : %sabled\n", + (channel_access.alerting) ? "dis" : "en"); + printf(" Per-message Auth : %sabled\n", + (channel_access.per_message_auth) ? "dis" : "en"); + printf(" User Level Auth : %sabled\n", + (channel_access.user_level_auth) ? "dis" : "en"); + + printf(" Access Mode : "); + switch (channel_access.access_mode) { + case 0: + printf("disabled\n"); + break; + case 1: + printf("pre-boot only\n"); + break; + case 2: + printf("always available\n"); + break; + case 3: + printf("shared\n"); + break; + default: + printf("unknown\n"); + break; + } + + /* get non-volatile settings */ + + rqdata[1] = 0x40; /* 0x40=non-volatile */ + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Access (non-volatile)"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Access (non-volatile) failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(&channel_access, rsp->data, sizeof(struct get_channel_access_rsp)); + + printf(" Non-Volatile Settings\n"); + printf(" Alerting : %sabled\n", + (channel_access.alerting) ? "dis" : "en"); + printf(" Per-message Auth : %sabled\n", + (channel_access.per_message_auth) ? "dis" : "en"); + printf(" User Level Auth : %sabled\n", + (channel_access.user_level_auth) ? "dis" : "en"); + + printf(" Access Mode : "); + switch (channel_access.access_mode) { + case 0: + printf("disabled\n"); + break; + case 1: + printf("pre-boot only\n"); + break; + case 2: + printf("always available\n"); + break; + case 3: + printf("shared\n"); + break; + default: + printf("unknown\n"); + break; + } + + return 0; +} + +static int +ipmi_get_user_access(struct ipmi_intf * intf, uint8_t channel, uint8_t userid) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req1, req2; + uint8_t rqdata[2]; + struct get_user_access_rsp user_access; + int curr_uid, max_uid = 0, init = 1; + + curr_uid = userid ? : 1; + + memset(&req1, 0, sizeof(req1)); + req1.msg.netfn = IPMI_NETFN_APP; + req1.msg.cmd = IPMI_GET_USER_ACCESS; + req1.msg.data = rqdata; + req1.msg.data_len = 2; + + memset(&req2, 0, sizeof(req2)); + req2.msg.netfn = IPMI_NETFN_APP; + req2.msg.cmd = IPMI_GET_USER_NAME; + req2.msg.data = rqdata; + req2.msg.data_len = 1; + + do + { + rqdata[0] = channel & 0xf; + rqdata[1] = curr_uid & 0x3f; + + rsp = intf->sendrecv(intf, &req1); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get User Access (channel %d id %d)", + rqdata[0], rqdata[1]); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get User Access (channel %d id %d) failed: %s", + rqdata[0], rqdata[1], + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(&user_access, rsp->data, sizeof(struct get_user_access_rsp)); + + rqdata[0] = curr_uid & 0x3f; + + rsp = intf->sendrecv(intf, &req2); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get User Name (id %d)", rqdata[0]); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get User Name (id %d) failed: %s", + rqdata[0], val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (init) { + printf("Maximum User IDs : %d\n", user_access.max_user_ids); + printf("Enabled User IDs : %d\n", user_access.enabled_user_ids); + max_uid = user_access.max_user_ids; + init = 0; + } + + printf("\n"); + printf("User ID : %d\n", curr_uid); + printf("User Name : %s\n", rsp->data); + printf("Fixed Name : %s\n", + (curr_uid <= user_access.fixed_user_ids) ? "Yes" : "No"); + printf("Access Available : %s\n", + (user_access.callin_callback) ? "callback" : "call-in / callback"); + printf("Link Authentication : %sabled\n", + (user_access.link_auth) ? "en" : "dis"); + printf("IPMI Messaging : %sabled\n", + (user_access.ipmi_messaging) ? "en" : "dis"); + printf("Privilege Level : %s\n", + val2str(user_access.privilege_limit, ipmi_privlvl_vals)); + + curr_uid ++; + + } while (!userid && curr_uid <= max_uid); + + return 0; +} + +static int +ipmi_set_user_access(struct ipmi_intf * intf, int argc, char ** argv) +{ + uint8_t channel, privilege_limit, userid; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[2]; + struct get_user_access_rsp user_access; + struct set_user_access_data set_access; + int i; + + if ((argc < 3) || (strncmp(argv[0], "help", 4) == 0)) { + printf_channel_usage(); + return 0; + } + + if (str2uchar(argv[0], &channel) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[0]); + return (-1); + } + if (str2uchar(argv[1], &userid) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[1]); + return (-1); + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_GET_USER_ACCESS; + req.msg.data = rqdata; + req.msg.data_len = 2; + + rqdata[0] = channel & 0xf; + rqdata[1] = userid & 0x3f; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get User Access (channel %d id %d)", + rqdata[0], rqdata[1]); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get User Access (channel %d id %d) failed: %s", + rqdata[0], rqdata[1], + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(&user_access, rsp->data, sizeof(struct get_user_access_rsp)); + + memset(&set_access, 0, sizeof(set_access)); + set_access.change_bits = 1; + set_access.callin_callback = user_access.callin_callback; + set_access.link_auth = user_access.link_auth; + set_access.ipmi_messaging = user_access.ipmi_messaging; + set_access.channel = channel; + set_access.user_id = userid; + set_access.privilege_limit = user_access.privilege_limit; + set_access.session_limit = 0; + + for (i = 2; i < argc; i ++) + { + if (strncmp(argv[i], "callin=", 7) == 0) { + set_access.callin_callback = !(strncmp (argv[i]+7, "off", 3)); + } + else if (strncmp(argv[i], "link=", 5) == 0) { + set_access.link_auth = strncmp (argv[i]+5, "off", 3); + } + else if (strncmp(argv[i], "ipmi=", 5) == 0) { + set_access.ipmi_messaging = strncmp (argv[i]+5, "off", 3); + } + else if (strncmp(argv[i], "privilege=", 10) == 0) { + if (str2uchar(argv[i]+10, &privilege_limit) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[i]+10); + return (-1); + } + set_access.privilege_limit = privilege_limit; + } + else { + printf ("Invalid option: %s\n", argv [i]); + return -1; + } + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_SET_USER_ACCESS; + req.msg.data = (uint8_t *) &set_access; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set User Access (channel %d id %d)", + set_access.channel, set_access.user_id); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set User Access (channel %d id %d) failed: %s", + set_access.channel, set_access.user_id, + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + + +static const char * +iana_string(uint32_t iana) +{ + static char s[10]; + + if (iana) + { + sprintf(s, "%06x", iana); + return s; + } + else + return "N/A"; +} + + +static int +ipmi_get_channel_cipher_suites(struct ipmi_intf * intf, + const char * payload_type, + uint8_t channel) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + uint8_t oem_record; + uint8_t rqdata[3]; + uint32_t iana; + uint8_t auth_alg, integrity_alg, crypt_alg; + uint8_t cipher_suite_id; + uint8_t list_index = 0; + uint8_t cipher_suite_data[1024]; // 0x40 sets * 16 bytes per set + uint16_t offset = 0; + uint16_t cipher_suite_data_length = 0; // how much was returned, total + + memset(cipher_suite_data, 0, sizeof(cipher_suite_data)); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_CIPHER_SUITES; // 0x54 + req.msg.data = rqdata; + req.msg.data_len = 3; + + rqdata[0] = channel; + rqdata[1] = ((strncmp(payload_type, "ipmi", 4) == 0)? 0: 1); + rqdata[2] = 0x80; // Always ask for cipher suite format + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + + // Grab the returned channel number once. We assume it's the same + // in future calls. + if (rsp->data_len >= 1) + channel = rsp->data[0]; + + while ((rsp->data_len > 1) && (rsp->data_len == 17) && (list_index < 0x3F)) + { + // + // We got back cipher suite data -- store it. + //printf("copying data to offset %d\n", offset); + //printbuf(rsp->data + 1, rsp->data_len - 1, "this is the data"); + memcpy(cipher_suite_data + offset, rsp->data + 1, rsp->data_len - 1); + offset += rsp->data_len - 1; + + // + // Increment our list for the next call + // + ++list_index; + rqdata[2] = (rqdata[2] & 0x80) + list_index; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } + + /* Copy last chunk */ + if(rsp->data_len > 1) + { + // + // We got back cipher suite data -- store it. + //printf("copying data to offset %d\n", offset); + //printbuf(rsp->data + 1, rsp->data_len - 1, "this is the data"); + memcpy(cipher_suite_data + offset, rsp->data + 1, rsp->data_len - 1); + offset += rsp->data_len - 1; + } + + // + // We can chomp on all our data now. + // + cipher_suite_data_length = offset; + offset = 0; + + if (! csv_output) + printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n"); + + while (offset < cipher_suite_data_length) + { + if (cipher_suite_data[offset++] == 0xC0) + { + oem_record = 0; // standard type + iana = 0; + + // Verify that we have at least a full record left + if ((cipher_suite_data_length - offset) < 4) // id + 3 algs + { + lprintf(LOG_ERR, "Incomplete data record in cipher suite data"); + return -1; + } + + cipher_suite_id = cipher_suite_data[offset++]; + + } + else if (cipher_suite_data[offset++] == 0xC1) + { + oem_record = 1; // OEM record type + + // Verify that we have at least a full record left + if ((cipher_suite_data_length - offset) < 4) // id + iana + 3 algs + { + lprintf(LOG_ERR, "Incomplete data record in cipher suite data"); + return -1; + } + + cipher_suite_id = cipher_suite_data[offset++]; + + // + // Grab the IANA + // + iana = + cipher_suite_data[offset] | + (cipher_suite_data[offset + 1] << 8) | + (cipher_suite_data[offset + 2] << 16); + offset += 3; + } + else + { + lprintf(LOG_ERR, "Bad start of record byte in cipher suite data"); + return -1; + } + + // + // Grab the algorithms for this cipher suite. I guess we can't be + // sure of what order they'll come in. Also, I suppose we default + // to the NONE algorithm if one were absent. This part of the spec is + // poorly written -- I have read the errata document. For now, I'm only + // allowing one algorithm per type (auth, integrity, crypt) because I + // don't I understand how it could be otherwise. + // + auth_alg = IPMI_AUTH_RAKP_NONE; + integrity_alg = IPMI_INTEGRITY_NONE; + crypt_alg = IPMI_CRYPT_NONE; + + while (((cipher_suite_data[offset] & 0xC0) != 0xC0) && + ((cipher_suite_data_length - offset) > 0)) + { + switch (cipher_suite_data[offset] & 0xC0) + { + case 0x00: + // Authentication algorithm specifier + auth_alg = cipher_suite_data[offset++] & 0x3F; + break; + case 0x40: + // Interity algorithm specifier + integrity_alg = cipher_suite_data[offset++] & 0x3F; + break; + case 0x80: + // Confidentiality algorithm specifier + crypt_alg = cipher_suite_data[offset++] & 0x3F; + break; + } + } + + + // + // We have everything we need to spit out a cipher suite record + // + printf((csv_output? "%d,%s,%s,%s,%s\n" : + "%-4d %-7s %-15s %-15s %-15s\n"), + cipher_suite_id, + iana_string(iana), + val2str(auth_alg, ipmi_auth_algorithms), + val2str(integrity_alg, ipmi_integrity_algorithms), + val2str(crypt_alg, ipmi_encryption_algorithms)); + } + + + return 0; +} + + + +uint8_t +ipmi_get_channel_medium(struct ipmi_intf * intf, uint8_t channel) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct get_channel_info_rsp info; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_GET_CHANNEL_INFO; + req.msg.data = &channel; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Channel Info command failed"); + return 0; + } + if (rsp->ccode > 0) { + if (rsp->ccode == 0xcc) + return IPMI_CHANNEL_MEDIUM_RESERVED; + lprintf(LOG_INFO, "Get Channel Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IPMI_CHANNEL_MEDIUM_RESERVED; + } + + memcpy(&info, rsp->data, sizeof(struct get_channel_info_rsp)); + + lprintf(LOG_DEBUG, "Channel type: %s", + val2str(info.channel_medium, ipmi_channel_medium_vals)); + + return info.channel_medium; +} + +uint8_t +ipmi_current_channel_medium(struct ipmi_intf * intf) +{ + return ipmi_get_channel_medium(intf, 0xE); +} + +void +printf_channel_usage() +{ + lprintf(LOG_NOTICE, "Channel Commands: authcap <channel number> <max privilege>"); + lprintf(LOG_NOTICE, " getaccess <channel number> [user id]"); + lprintf(LOG_NOTICE, " setaccess <channel number> " + "<user id> [callin=on|off] [ipmi=on|off] [link=on|off] [privilege=level]"); + lprintf(LOG_NOTICE, " info [channel number]"); + lprintf(LOG_NOTICE, " getciphers <ipmi | sol> [channel]\n"); + lprintf(LOG_NOTICE, "Possible privilege levels are:"); + lprintf(LOG_NOTICE, " 1 Callback level"); + lprintf(LOG_NOTICE, " 2 User level"); + lprintf(LOG_NOTICE, " 3 Operator level"); + lprintf(LOG_NOTICE, " 4 Administrator level"); + lprintf(LOG_NOTICE, " 5 OEM Proprietary level"); + lprintf(LOG_NOTICE, " 15 No access"); +} + + +int +ipmi_channel_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int retval = 0; + uint8_t channel, priv = 0; + + if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) + { + printf_channel_usage(); + } + else if (strncmp(argv[0], "authcap", 7) == 0) + { + if (argc != 3) { + printf_channel_usage(); + return (-1); + } else { + if (str2uchar(argv[1], &channel) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[1]); + return (-1); + } + if (str2uchar(argv[2], &priv) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[2]); + return (-1); + } + retval = ipmi_get_channel_auth_cap(intf, channel, priv); + } + } + else if (strncmp(argv[0], "getaccess", 10) == 0) + { + if ((argc < 2) || (argc > 3)) + printf_channel_usage(); + else { + uint8_t ch = 0; + uint8_t id = 0; + if (str2uchar(argv[1], &ch) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[1]); + return (-1); + } + if (argc == 3) { + if (str2uchar(argv[2], &id) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[2]); + return (-1); + } + } + retval = ipmi_get_user_access(intf, ch, id); + } + } + else if (strncmp(argv[0], "setaccess", 9) == 0) + { + retval = ipmi_set_user_access(intf, argc-1, &(argv[1])); + } + else if (strncmp(argv[0], "info", 4) == 0) + { + if (argc > 2) + printf_channel_usage(); + else { + uint8_t ch = 0xe; + if (argc == 2) { + if (str2uchar(argv[1], &ch) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[1]); + return (-1); + } + } + retval = ipmi_get_channel_info(intf, ch); + } + } + + // it channel getciphers <ipmi | sol> [channel] + else if (strncmp(argv[0], "getciphers", 10) == 0) + { + if ((argc < 2) || (argc > 3) || + (strncmp(argv[1], "ipmi", 4) && strncmp(argv[1], "sol", 3))) + printf_channel_usage(); + else + { + uint8_t ch = 0xe; + if (argc == 3) { + if (str2uchar(argv[2], &ch) != 0) { + lprintf(LOG_ERR, "Numeric value expected, but '%s' given.", argv[2]); + return (-1); + } + } + retval = ipmi_get_channel_cipher_suites(intf, + argv[1], // ipmi | sol + ch); + } + } + else + { + printf("Invalid CHANNEL command: %s\n", argv[0]); + printf_channel_usage(); + retval = -1; + } + + return retval; +} diff --git a/lib/ipmi_chassis.c b/lib/ipmi_chassis.c new file mode 100644 index 0000000..d4e88ee --- /dev/null +++ b/lib/ipmi_chassis.c @@ -0,0 +1,1393 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> + +#include <ipmitool/bswap.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_chassis.h> + +extern int verbose; + +int +ipmi_chassis_power_status(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x1; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to get Chassis Power Status"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Chassis Power Status failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return rsp->data[0] & 1; +} + +static int +ipmi_chassis_print_power_status(struct ipmi_intf * intf) +{ + int ps = ipmi_chassis_power_status(intf); + + if (ps < 0) + return -1; + + printf("Chassis Power is %s\n", ps ? "on" : "off"); + + return 0; +} + +int +ipmi_chassis_power_control(struct ipmi_intf * intf, uint8_t ctl) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x2; + req.msg.data = &ctl; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to set Chassis Power Control to %s", + val2str(ctl, ipmi_chassis_power_control_vals)); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Chassis Power Control to %s failed: %s", + val2str(ctl, ipmi_chassis_power_control_vals), + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("Chassis Power Control: %s\n", + val2str(ctl, ipmi_chassis_power_control_vals)); + +#if 0 /* this can cause sessions to hang around after power commands */ + /* sessions often get lost when changing chassis power */ + intf->abort = 1; +#endif + + return 0; +} + +static int +ipmi_chassis_identify(struct ipmi_intf * intf, char * arg) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + int rc = (-3); + + struct { + uint8_t interval; + uint8_t force_on; + } identify_data = { .interval = 0, .force_on = 0 }; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x4; + + if (arg != NULL) { + if (strncmp(arg, "force", 5) == 0) { + identify_data.force_on = 1; + } else { + if ( (rc = str2uchar(arg, &identify_data.interval)) != 0) { + if (rc == (-2)) { + lprintf(LOG_ERR, "Invalid interval given."); + } else { + lprintf(LOG_ERR, "Given interval is too big."); + } + return (-1); + } + } + req.msg.data = (uint8_t *)&identify_data; + /* The Force Identify On byte is optional and not + * supported by all devices-- if force is not specified, + * we pass only one data byte; if specified, we pass two + * data bytes and check for an error completion code + */ + req.msg.data_len = (identify_data.force_on) ? 2 : 1; + } + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to set Chassis Identify"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Chassis Identify failed: %s", + val2str(rsp->ccode, completion_code_vals)); + if (identify_data.force_on != 0) { + /* Intel SE7501WV2 F/W 1.2 returns CC 0xC7, but + * the IPMI v1.5 spec does not standardize a CC + * if unsupported, so we warn + */ + lprintf(LOG_WARNING, "Chassis may not support Force Identify On\n"); + } + return -1; + } + + printf("Chassis identify interval: "); + if (arg == NULL) { + printf("default (15 seconds)\n"); + } else { + if (identify_data.force_on != 0) { + printf("indefinite\n"); + } else { + if (identify_data.interval == 0) + printf("off\n"); + else + printf("%i seconds\n", identify_data.interval); + } + } + return 0; +} + +static int +ipmi_chassis_poh(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t mins_per_count; + uint32_t count; + float minutes; + uint32_t days, hours; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0xf; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to get Chassis Power-On-Hours"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Chassis Power-On-Hours failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + mins_per_count = rsp->data[0]; + memcpy(&count, rsp->data+1, 4); +#if WORDS_BIGENDIAN + count = BSWAP_32(count); +#endif + + minutes = (float)count * mins_per_count; + days = minutes / 1440; + minutes -= (float)days * 1440; + hours = minutes / 60; + minutes -= hours * 60; + + if (mins_per_count < 60) { + printf("POH Counter : %i days, %i hours, %li minutes\n", + days, hours, (long)minutes); + } else { + printf("POH Counter : %i days, %i hours\n", days, hours); + } + + return 0; +} + +static int +ipmi_chassis_restart_cause(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x7; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to get Chassis Restart Cause"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Chassis Restart Cause failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("System restart cause: "); + + switch (rsp->data[0] & 0xf) { + case 0: + printf("unknown\n"); + break; + case 1: + printf("chassis power control command\n"); + break; + case 2: + printf("reset via pushbutton\n"); + break; + case 3: + printf("power-up via pushbutton\n"); + break; + case 4: + printf("watchdog expired\n"); + break; + case 5: + printf("OEM\n"); + break; + case 6: + printf("power-up due to always-restore power policy\n"); + break; + case 7: + printf("power-up due to restore-previous power policy\n"); + break; + case 8: + printf("reset via PEF\n"); + break; + case 9: + printf("power-cycle via PEF\n"); + break; + default: + printf("invalid\n"); + } + + return 0; +} + +int +ipmi_chassis_status(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error sending Chassis Status command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error sending Chassis Status command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* byte 1 */ + printf("System Power : %s\n", (rsp->data[0] & 0x1) ? "on" : "off"); + printf("Power Overload : %s\n", (rsp->data[0] & 0x2) ? "true" : "false"); + printf("Power Interlock : %s\n", (rsp->data[0] & 0x4) ? "active" : "inactive"); + printf("Main Power Fault : %s\n", (rsp->data[0] & 0x8) ? "true" : "false"); + printf("Power Control Fault : %s\n", (rsp->data[0] & 0x10) ? "true" : "false"); + printf("Power Restore Policy : "); + switch ((rsp->data[0] & 0x60) >> 5) { + case 0x0: + printf("always-off\n"); + break; + case 0x1: + printf("previous\n"); + break; + case 0x2: + printf("always-on\n"); + break; + case 0x3: + default: + printf("unknown\n"); + } + + /* byte 2 */ + printf("Last Power Event : "); + if (rsp->data[1] & 0x1) + printf("ac-failed "); + if (rsp->data[1] & 0x2) + printf("overload "); + if (rsp->data[1] & 0x4) + printf("interlock "); + if (rsp->data[1] & 0x8) + printf("fault "); + if (rsp->data[1] & 0x10) + printf("command"); + printf("\n"); + + /* byte 3 */ + printf("Chassis Intrusion : %s\n", (rsp->data[2] & 0x1) ? "active" : "inactive"); + printf("Front-Panel Lockout : %s\n", (rsp->data[2] & 0x2) ? "active" : "inactive"); + printf("Drive Fault : %s\n", (rsp->data[2] & 0x4) ? "true" : "false"); + printf("Cooling/Fan Fault : %s\n", (rsp->data[2] & 0x8) ? "true" : "false"); + + if (rsp->data_len > 3) { + /* optional byte 4 */ + if (rsp->data[3] == 0) { + printf("Front Panel Control : none\n"); + } else { + printf("Sleep Button Disable : %s\n", (rsp->data[3] & 0x80) ? "allowed" : "not allowed"); + printf("Diag Button Disable : %s\n", (rsp->data[3] & 0x40) ? "allowed" : "not allowed"); + printf("Reset Button Disable : %s\n", (rsp->data[3] & 0x20) ? "allowed" : "not allowed"); + printf("Power Button Disable : %s\n", (rsp->data[3] & 0x10) ? "allowed" : "not allowed"); + printf("Sleep Button Disabled: %s\n", (rsp->data[3] & 0x08) ? "true" : "false"); + printf("Diag Button Disabled : %s\n", (rsp->data[3] & 0x04) ? "true" : "false"); + printf("Reset Button Disabled: %s\n", (rsp->data[3] & 0x02) ? "true" : "false"); + printf("Power Button Disabled: %s\n", (rsp->data[3] & 0x01) ? "true" : "false"); + } + } + + return 0; +} + + +static int +ipmi_chassis_selftest(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error sending Get Self Test command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error sending Get Self Test command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("Self Test Results : "); + switch (rsp->data[0]) { + case 0x55: + printf("passed\n"); + break; + + case 0x56: + printf("not implemented\n"); + break; + + case 0x57: + { + int i; + const struct valstr broken_dev_vals[] = { + { 0, "firmware corrupted" }, + { 1, "boot block corrupted" }, + { 2, "FRU Internal Use Area corrupted" }, + { 3, "SDR Repository empty" }, + { 4, "IPMB not responding" }, + { 5, "cannot access BMC FRU" }, + { 6, "cannot access SDR Repository" }, + { 7, "cannot access SEL Device" }, + { 0xff, NULL }, + }; + printf("device error\n"); + for (i=0; i<8; i++) { + if (rsp->data[1] & (1<<i)) { + printf(" [%s]\n", + val2str(i, broken_dev_vals)); + } + } + } + break; + + case 0x58: + printf("Fatal hardware error: %02xh\n", rsp->data[1]); + break; + + default: + printf("Device-specific failure %02xh:%02xh\n", + rsp->data[0], rsp->data[1]); + break; + } + + return 0; +} + +static int +ipmi_chassis_set_bootparam(struct ipmi_intf * intf, uint8_t param, uint8_t * data, int len) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[16]; + + memset(msg_data, 0, 16); + msg_data[0] = param & 0x7f; + memcpy(msg_data+1, data, len); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x8; + req.msg.data = msg_data; + req.msg.data_len = len + 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting Chassis Boot Parameter %d", param); + return -1; + } + if (rsp->ccode > 0) { + if (param != 0) { + lprintf(LOG_ERR, "Set Chassis Boot Parameter %d failed: %s", + param, val2str(rsp->ccode, completion_code_vals)); + } + return -1; + } + + lprintf(LOG_DEBUG, "Chassis Set Boot Parameter %d to %s", param, buf2str(data, len)); + return 0; +} + +static int +ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[3]; + uint8_t param_id = 0; + + if (arg == NULL) + return -1; + + if (str2uchar(arg, ¶m_id) != 0) { + lprintf(LOG_ERR, "Invalid parameter '%s' given instead of bootparam.", + arg); + return (-1); + } + + memset(msg_data, 0, 3); + + msg_data[0] = param_id & 0x7f; + msg_data[1] = 0; + msg_data[2] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x9; + req.msg.data = msg_data; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error Getting Chassis Boot Parameter %s", arg); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Chassis Boot Parameter %s failed: %s", + arg, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "Boot Option"); + + param_id = 0; + param_id = (rsp->data[1] & 0x7f); + + printf("Boot parameter version: %d\n", rsp->data[0]); + printf("Boot parameter %d is %s\n", rsp->data[1] & 0x7f, + (rsp->data[1] & 0x80) ? "invalid/locked" : "valid/unlocked"); + printf("Boot parameter data: %s\n", buf2str(rsp->data+2, rsp->data_len - 2)); + + switch(param_id) + { + case 0: + { + printf(" Set In Progress : "); + switch((rsp->data[2]) &0x03) + { + case 0: printf("set complete\n"); break; + case 1: printf("set in progress\n"); break; + case 2: printf("commit write\n"); break; + default: printf("error, reserved bit\n"); break; + } + } + break; + case 1: + { + printf(" Service Partition Selector : "); + if((rsp->data[2]) == 0) + { + printf("unspecified\n"); + } + else + { + printf("%d\n",(rsp->data[2])); + } + } + break; + case 2: + { + printf( " Service Partition Scan :\n"); + if((rsp->data[2]&0x03) != 0) + { + if((rsp->data[2]&0x01) == 0x01) + printf(" - Request BIOS to scan\n"); + if((rsp->data[2]&0x02) == 0x02) + printf(" - Service Partition Discovered\n"); + } + else + { + printf(" No flag set\n"); + } + } + break; + case 3: + { + printf( " BMC boot flag valid bit clearing :\n"); + if((rsp->data[2]&0x1f) != 0) + { + if((rsp->data[2]&0x10) == 0x10) + printf(" - Don't clear valid bit on reset/power cycle cause by PEF\n"); + if((rsp->data[2]&0x08) == 0x08) + printf(" - Don't automatically clear boot flag valid bit on timeout\n"); + if((rsp->data[2]&0x04) == 0x04) + printf(" - Don't clear valid bit on reset/power cycle cause by watchdog\n"); + if((rsp->data[2]&0x02) == 0x02) + printf(" - Don't clear valid bit on push button reset // soft reset\n"); + if((rsp->data[2]&0x01) == 0x01) + printf(" - Don't clear valid bit on power up via power push button or wake event\n"); + } + else + { + printf(" No flag set\n"); + } + } + break; + case 4: + { + printf( " Boot Info Acknowledge :\n"); + if((rsp->data[3]&0x1f) != 0) + { + if((rsp->data[3]&0x10) == 0x10) + printf(" - OEM has handled boot info\n"); + if((rsp->data[3]&0x08) == 0x08) + printf(" - SMS has handled boot info\n"); + if((rsp->data[3]&0x04) == 0x04) + printf(" - OS // service partition has handled boot info\n"); + if((rsp->data[3]&0x02) == 0x02) + printf(" - OS Loader has handled boot info\n"); + if((rsp->data[3]&0x01) == 0x01) + printf(" - BIOS/POST has handled boot info\n"); + } + else + { + printf(" No flag set\n"); + } + } + break; + case 5: + { + printf( " Boot Flags :\n"); + + if((rsp->data[2]&0x80) == 0x80) + printf(" - Boot Flag Valid\n"); + else + printf(" - Boot Flag Invalid\n"); + + if((rsp->data[2]&0x40) == 0x40) + printf(" - Options apply to all future boots\n"); + else + printf(" - Options apply to only next boot\n"); + + if((rsp->data[2]&0x20) == 0x20) + printf(" - BIOS EFI boot \n"); + else + printf(" - BIOS PC Compatible (legacy) boot \n"); + + if((rsp->data[3]&0x80) == 0x80) + printf(" - CMOS Clear\n"); + if((rsp->data[3]&0x40) == 0x40) + printf(" - Lock Keyboard\n"); + printf(" - Boot Device Selector : "); + switch( ((rsp->data[3]>>2)&0x0f)) + { + case 0: printf("No override\n"); break; + case 1: printf("Force PXE\n"); break; + case 2: printf("Force Boot from default Hard-Drive\n"); break; + case 3: printf("Force Boot from default Hard-Drive, request Safe-Mode\n"); break; + case 4: printf("Force Boot from Diagnostic Partition\n"); break; + case 5: printf("Force Boot from CD/DVD\n"); break; + case 6: printf("Force Boot into BIOS Setup\n"); break; + case 15: printf("Force Boot from Floppy/primary removable media\n"); break; + default: printf("Flag error\n"); break; + } + if((rsp->data[3]&0x02) == 0x02) + printf(" - Screen blank\n"); + if((rsp->data[3]&0x01) == 0x01) + printf(" - Lock out Reset buttons\n"); + + if((rsp->data[4]&0x80) == 0x80) + printf(" - Lock out (power off/sleep request) vi Power Button\n"); + printf(" - Console Redirection control : "); + switch( ((rsp->data[4]>>5)&0x03)) + { + case 0: printf("System Default\n"); break; + case 1: printf("Request Quiet Display\n"); break; + case 2: printf("Request Verbose Display\n"); break; + default: printf("Flag error\n"); break; + } + if((rsp->data[4]&0x10) == 0x10) + printf(" - Force progress event traps\n"); + if((rsp->data[4]&0x08) == 0x08) + printf(" - User password bypass\n"); + if((rsp->data[4]&0x04) == 0x04) + printf(" - Lock Out Sleep Button\n"); + if((rsp->data[4]&0x02) == 0x02) + printf(" - Lock Out Sleep Button\n"); + printf(" - BIOS verbosity : "); + switch( ((rsp->data[4]>>0)&0x03)) + { + case 0: printf("Console redirection occurs per BIOS configuration setting (default)\n"); break; + case 1: printf("Suppress (skip) console redirection if enabled\n"); break; + case 2: printf("Request console redirection be enabled\n"); break; + default: printf("Flag error\n"); break; + } + + if((rsp->data[5]&0x08) == 0x08) + printf(" - BIOS Shared Mode Override\n"); + printf(" - BIOS Mux Control Override : "); + switch( ((rsp->data[5]>>0)&0x07)) + { + case 0: printf("BIOS uses recommended setting of the mux at the end of POST\n"); break; + case 1: printf("Requests BIOS to force mux to BMC at conclusion of POST/start of OS boot\n"); break; + case 2: printf("Requests BIOS to force mux to system at conclusion of POST/start of OS boot\n"); break; + default: printf("Flag error\n"); break; + } + } + break; + case 6: + { + unsigned long session_id; + unsigned long timestamp; + char time_buf[40]; + time_t out_time; + + session_id = ((unsigned long) rsp->data[3]); + session_id |= (((unsigned long) rsp->data[4])<<8); + session_id |= (((unsigned long) rsp->data[5])<<16); + session_id |= (((unsigned long) rsp->data[6])<<24); + + timestamp = ((unsigned long) rsp->data[7]); + timestamp |= (((unsigned long) rsp->data[8])<<8); + timestamp |= (((unsigned long) rsp->data[9])<<16); + timestamp |= (((unsigned long) rsp->data[10])<<24); + + memset(time_buf, 0, 40); + strftime( + time_buf, + sizeof(time_buf), + "%m/%d/%Y %H:%M:%S", localtime(&out_time) + ); + + printf(" Boot Initiator Info :\n"); + printf(" Channel Number : %d\n", (rsp->data[2] & 0x0f)); + printf(" Session Id : %08lXh\n",session_id); + if(timestamp != 0) + { + printf(" Timestamp : %08lXh, %s\n",timestamp,time_buf); + } + else + { + printf(" Timestamp : %08lXh, undefined\n",timestamp); + } + + } + break; + case 7: + { + printf(" Selector : %d\n", rsp->data[2] ); + printf(" Block Data : %s\n", buf2str(rsp->data+3, rsp->data_len - 2)); + } + break; + default: + printf(" Undefined byte\n"); + break; + } + + return 0; +} + +static int +get_bootparam_options(char *optstring, + unsigned char *set_flag, unsigned char *clr_flag) +{ + char *token; + char *saveptr = NULL; + int optionError = 0; + *set_flag = 0; + *clr_flag = 0; + static struct { + char *name; + unsigned char value; + char *desc; + } options[] = { + {"PEF", 0x10, + "Clear valid bit on reset/power cycle cause by PEF"}, + {"timeout", 0x08, + "Automatically clear boot flag valid bit on timeout"}, + {"watchdog", 0x04, + "Clear valid bit on reset/power cycle cause by watchdog"}, + {"reset", 0x02, + "Clear valid bit on push button reset/soft reset"}, + {"power", 0x01, + "Clear valid bit on power up via power push button or wake event"}, + + {NULL} /* End marker */ + }, *op; + + if (strncmp(optstring, "options=", 8) != 0) { + lprintf(LOG_ERR, "No options= keyword found \"%s\"", optstring); + return -1; + } + token = strtok_r(optstring + 8, ",", &saveptr); + while (token != NULL) { + int setbit = 0; + if (strcmp(token, "help") == 0) { + optionError = 1; + break; + } + if (strncmp(token, "no-", 3) == 0) { + setbit = 1; + token += 3; + } + for (op = options; op->name != NULL; ++op) { + if (strncmp(token, op->name, strlen(op->name)) == 0) { + if (setbit) { + *set_flag |= op->value; + } else { + *clr_flag |= op->value; + } + break; + } + } + if (op->name == NULL) { + /* Option not found */ + optionError = 1; + if (setbit) { + token -=3; + } + lprintf(LOG_ERR, "Invalid option: %s", token); + } + token = strtok_r(NULL, ",", &saveptr); + } + if (optionError) { + lprintf(LOG_NOTICE, " Legal options are:"); + lprintf(LOG_NOTICE, " %-8s: print this message", "help"); + for (op = options; op->name != NULL; ++op) { + lprintf(LOG_NOTICE, " %-8s: %s", op->name, op->desc); + } + lprintf(LOG_NOTICE, " Any Option may be prepended with no-" + " to invert sense of operation\n"); + return (-1); + } + return (0); +} + +static int +ipmi_chassis_get_bootvalid(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[3]; + uint8_t param_id = IPMI_CHASSIS_BOOTPARAM_FLAG_VALID; + memset(msg_data, 0, 3); + + msg_data[0] = param_id & 0x7f; + msg_data[1] = 0; + msg_data[2] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x9; + req.msg.data = msg_data; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error Getting Chassis Boot Parameter %d", param_id); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Chassis Boot Parameter %d failed: %s", + param_id, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "Boot Option"); + + return(rsp->data[2]); +} + +static int +ipmi_chassis_set_bootvalid(struct ipmi_intf *intf, uint8_t set_flag, uint8_t clr_flag) +{ + int bootvalid; + uint8_t flags[5]; + int rc = 0; + int use_progress = 1; + uint8_t param_id = IPMI_CHASSIS_BOOTPARAM_FLAG_VALID; + + if (use_progress) { + /* set set-in-progress flag */ + memset(flags, 0, 5); + flags[0] = 0x01; + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, flags, 1); + if (rc < 0) + use_progress = 0; + } + + memset(flags, 0, 5); + flags[0] = 0x01; + flags[1] = 0x01; + rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_INFO_ACK, + flags, 2); + + if (rc < 0) { + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + return -1; + } + + bootvalid = ipmi_chassis_get_bootvalid(intf); + + if (bootvalid < 0) { + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + return -1; + } + flags[0] = (bootvalid & ~clr_flag) | set_flag; + + rc = ipmi_chassis_set_bootparam(intf, param_id, flags, 1); + + if (rc == 0) { + if (use_progress) { + /* set-in-progress = commit-write */ + memset(flags, 0, 5); + flags[0] = 0x02; + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + } + + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + + return rc; +} + +static int +ipmi_chassis_set_bootdev(struct ipmi_intf * intf, char * arg, uint8_t *iflags) +{ + uint8_t flags[5]; + int rc = 0; + int use_progress = 1; + + if (use_progress) { + /* set set-in-progress flag */ + memset(flags, 0, 5); + flags[0] = 0x01; + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, flags, 1); + if (rc < 0) + use_progress = 0; + } + + memset(flags, 0, 5); + flags[0] = 0x01; + flags[1] = 0x01; + rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_INFO_ACK, + flags, 2); + + if (rc < 0) { + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + return -1; + } + + if (iflags == NULL) + memset(flags, 0, 5); + else + memcpy(flags, iflags, sizeof (flags)); + + if (arg == NULL) + flags[1] |= 0x00; + else if (strncmp(arg, "none", 4) == 0) + flags[1] |= 0x00; + else if (strncmp(arg, "pxe", 3) == 0 || + strncmp(arg, "force_pxe", 9) == 0) + flags[1] |= 0x04; + else if (strncmp(arg, "disk", 4) == 0 || + strncmp(arg, "force_disk", 10) == 0) + flags[1] |= 0x08; + else if (strncmp(arg, "safe", 4) == 0 || + strncmp(arg, "force_safe", 10) == 0) + flags[1] |= 0x0c; + else if (strncmp(arg, "diag", 4) == 0 || + strncmp(arg, "force_diag", 10) == 0) + flags[1] |= 0x10; + else if (strncmp(arg, "cdrom", 5) == 0 || + strncmp(arg, "force_cdrom", 11) == 0) + flags[1] |= 0x14; + else if (strncmp(arg, "floppy", 6) == 0 || + strncmp(arg, "force_floppy", 12) == 0) + flags[1] |= 0x3c; + else if (strncmp(arg, "bios", 4) == 0 || + strncmp(arg, "force_bios", 10) == 0) + flags[1] |= 0x18; + else { + lprintf(LOG_ERR, "Invalid argument: %s", arg); + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + return -1; + } + + /* set flag valid bit */ + flags[0] |= 0x80; + + rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_BOOT_FLAGS, + flags, 5); + if (rc == 0) { + if (use_progress) { + /* set-in-progress = commit-write */ + memset(flags, 0, 5); + flags[0] = 0x02; + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + + printf("Set Boot Device to %s\n", arg); + } + + if (use_progress) { + /* set-in-progress = set-complete */ + memset(flags, 0, 5); + ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + flags, 1); + } + + return rc; +} + +static int +ipmi_chassis_power_policy(struct ipmi_intf * intf, uint8_t policy) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_CHASSIS; + req.msg.cmd = 0x6; + req.msg.data = &policy; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Power Restore Policy command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Power Restore Policy command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (policy == IPMI_CHASSIS_POLICY_NO_CHANGE) { + printf("Supported chassis power policy: "); + if (rsp->data[0] & (1<<IPMI_CHASSIS_POLICY_ALWAYS_OFF)) + printf("always-off "); + if (rsp->data[0] & (1<<IPMI_CHASSIS_POLICY_ALWAYS_ON)) + printf("always-on "); + if (rsp->data[0] & (1<<IPMI_CHASSIS_POLICY_PREVIOUS)) + printf("previous"); + printf("\n"); + } + else { + printf("Set chassis power restore policy to "); + switch (policy) { + case IPMI_CHASSIS_POLICY_ALWAYS_ON: + printf("always-on\n"); + break; + case IPMI_CHASSIS_POLICY_ALWAYS_OFF: + printf("always-off\n"); + break; + case IPMI_CHASSIS_POLICY_PREVIOUS: + printf("previous\n"); + break; + default: + printf("unknown\n"); + } + } + return 0; +} + +int +ipmi_power_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + uint8_t ctl = 0; + + if ((argc < 1) || (strncmp(argv[0], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "chassis power Commands: status, on, off, cycle, reset, diag, soft"); + return 0; + } + if (strncmp(argv[0], "status", 6) == 0) { + rc = ipmi_chassis_print_power_status(intf); + return rc; + } + if ((strncmp(argv[0], "up", 2) == 0) || (strncmp(argv[0], "on", 2) == 0)) + ctl = IPMI_CHASSIS_CTL_POWER_UP; + else if ((strncmp(argv[0], "down", 4) == 0) || (strncmp(argv[0], "off", 3) == 0)) + ctl = IPMI_CHASSIS_CTL_POWER_DOWN; + else if (strncmp(argv[0], "cycle", 5) == 0) + ctl = IPMI_CHASSIS_CTL_POWER_CYCLE; + else if (strncmp(argv[0], "reset", 5) == 0) + ctl = IPMI_CHASSIS_CTL_HARD_RESET; + else if (strncmp(argv[0], "diag", 4) == 0) + ctl = IPMI_CHASSIS_CTL_PULSE_DIAG; + else if ((strncmp(argv[0], "acpi", 4) == 0) || (strncmp(argv[0], "soft", 4) == 0)) + ctl = IPMI_CHASSIS_CTL_ACPI_SOFT; + else { + lprintf(LOG_ERR, "Invalid chassis power command: %s", argv[0]); + return -1; + } + + rc = ipmi_chassis_power_control(intf, ctl); + return rc; +} + +void +ipmi_chassis_set_bootflag_help() +{ + unsigned char set_flag; + unsigned char clr_flag; + lprintf(LOG_NOTICE, "bootparam set bootflag <device> [options=...]"); + lprintf(LOG_NOTICE, " Legal devices are:"); + lprintf(LOG_NOTICE, " none : No override"); + lprintf(LOG_NOTICE, " force_pxe : Force PXE boot"); + lprintf(LOG_NOTICE, " force_disk : Force boot from default Hard-drive"); + lprintf(LOG_NOTICE, " force_safe : Force boot from default Hard-drive, request Safe Mode"); + lprintf(LOG_NOTICE, " force_diag : Force boot from Diagnostic Partition"); + lprintf(LOG_NOTICE, " force_cdrom : Force boot from CD/DVD"); + lprintf(LOG_NOTICE, " force_bios : Force boot into BIOS Setup"); + get_bootparam_options("options=help", &set_flag, &clr_flag); +} + +int +ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "Chassis Commands: status, power, identify, policy, restart_cause, poh, bootdev, bootparam, selftest"); + } + else if (strncmp(argv[0], "status", 6) == 0) { + rc = ipmi_chassis_status(intf); + } + else if (strncmp(argv[0], "selftest", 8) == 0) { + rc = ipmi_chassis_selftest(intf); + } + else if (strncmp(argv[0], "power", 5) == 0) { + uint8_t ctl = 0; + + if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "chassis power Commands: status, on, off, cycle, reset, diag, soft"); + return 0; + } + if (strncmp(argv[1], "status", 6) == 0) { + rc = ipmi_chassis_print_power_status(intf); + return rc; + } + if ((strncmp(argv[1], "up", 2) == 0) || (strncmp(argv[1], "on", 2) == 0)) + ctl = IPMI_CHASSIS_CTL_POWER_UP; + else if ((strncmp(argv[1], "down", 4) == 0) || (strncmp(argv[1], "off", 3) == 0)) + ctl = IPMI_CHASSIS_CTL_POWER_DOWN; + else if (strncmp(argv[1], "cycle", 5) == 0) + ctl = IPMI_CHASSIS_CTL_POWER_CYCLE; + else if (strncmp(argv[1], "reset", 5) == 0) + ctl = IPMI_CHASSIS_CTL_HARD_RESET; + else if (strncmp(argv[1], "diag", 4) == 0) + ctl = IPMI_CHASSIS_CTL_PULSE_DIAG; + else if ((strncmp(argv[1], "acpi", 4) == 0) || (strncmp(argv[1], "soft", 4) == 0)) + ctl = IPMI_CHASSIS_CTL_ACPI_SOFT; + else { + lprintf(LOG_ERR, "Invalid chassis power command: %s", argv[1]); + return -1; + } + + rc = ipmi_chassis_power_control(intf, ctl); + } + else if (strncmp(argv[0], "identify", 8) == 0) { + if (argc < 2) { + rc = ipmi_chassis_identify(intf, NULL); + } + else if (strncmp(argv[1], "help", 4) == 0) { + lprintf(LOG_NOTICE, "chassis identify <interval>"); + lprintf(LOG_NOTICE, " default is 15 seconds"); + lprintf(LOG_NOTICE, " 0 to turn off"); + lprintf(LOG_NOTICE, " force to turn on indefinitely"); + } else { + rc = ipmi_chassis_identify(intf, argv[1]); + } + } + else if (strncmp(argv[0], "poh", 3) == 0) { + rc = ipmi_chassis_poh(intf); + } + else if (strncmp(argv[0], "restart_cause", 13) == 0) { + rc = ipmi_chassis_restart_cause(intf); + } + else if (strncmp(argv[0], "policy", 4) == 0) { + if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "chassis policy <state>"); + lprintf(LOG_NOTICE, " list : return supported policies"); + lprintf(LOG_NOTICE, " always-on : turn on when power is restored"); + lprintf(LOG_NOTICE, " previous : return to previous state when power is restored"); + lprintf(LOG_NOTICE, " always-off : stay off after power is restored"); + } else { + uint8_t ctl; + if (strncmp(argv[1], "list", 4) == 0) + ctl = IPMI_CHASSIS_POLICY_NO_CHANGE; + else if (strncmp(argv[1], "always-on", 9) == 0) + ctl = IPMI_CHASSIS_POLICY_ALWAYS_ON; + else if (strncmp(argv[1], "previous", 8) == 0) + ctl = IPMI_CHASSIS_POLICY_PREVIOUS; + else if (strncmp(argv[1], "always-off", 10) == 0) + ctl = IPMI_CHASSIS_POLICY_ALWAYS_OFF; + else { + lprintf(LOG_ERR, "Invalid chassis policy: %s", argv[1]); + return -1; + } + rc = ipmi_chassis_power_policy(intf, ctl); + } + } + else if (strncmp(argv[0], "bootparam", 9) == 0) { + if ((argc < 3) || (strncmp(argv[1], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "bootparam get <param #>"); + ipmi_chassis_set_bootflag_help(); + } + else { + if (strncmp(argv[1], "get", 3) == 0) { + rc = ipmi_chassis_get_bootparam(intf, argv[2]); + } + else if (strncmp(argv[1], "set", 3) == 0) { + unsigned char set_flag=0; + unsigned char clr_flag=0; + if (strncmp(argv[2], "help", 4) == 0 || + argc < 4 || (argc >= 4 && + strncmp(argv[2], "bootflag", 8) != 0)) { + ipmi_chassis_set_bootflag_help(); + } else { + if (argc == 5) { + get_bootparam_options(argv[4], &set_flag, &clr_flag); + } + rc = ipmi_chassis_set_bootdev(intf, argv[3], NULL); + if (argc == 5 && (set_flag != 0 || clr_flag != 0)) { + rc = ipmi_chassis_set_bootvalid(intf, set_flag, clr_flag); + } + } + } + else + lprintf(LOG_NOTICE, "bootparam get|set <option> [value ...]"); + } + } + else if (strncmp(argv[0], "bootdev", 7) == 0) { + if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + lprintf(LOG_NOTICE, "bootdev <device> [clear-cmos=yes|no]"); + lprintf(LOG_NOTICE, "bootdev <device> [options=help,...]"); + lprintf(LOG_NOTICE, " none : Do not change boot device order"); + lprintf(LOG_NOTICE, " pxe : Force PXE boot"); + lprintf(LOG_NOTICE, " disk : Force boot from default Hard-drive"); + lprintf(LOG_NOTICE, " safe : Force boot from default Hard-drive, request Safe Mode"); + lprintf(LOG_NOTICE, " diag : Force boot from Diagnostic Partition"); + lprintf(LOG_NOTICE, " cdrom : Force boot from CD/DVD"); + lprintf(LOG_NOTICE, " bios : Force boot into BIOS Setup"); + lprintf(LOG_NOTICE, " floppy: Force boot from Floppy/primary removable media"); + } else { + if (argc < 3) + rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); + else if (strncmp(argv[2], "clear-cmos=", 11) == 0) { + if (strncmp(argv[2]+11, "yes", 3) == 0) { + uint8_t flags[5] = {0, (1<<7), 0, 0, 0}; + rc = ipmi_chassis_set_bootdev(intf, argv[1], flags); + } else + rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); + } + else if (strncmp(argv[2], "options=", 8) == 0) { + char *token; + char *saveptr = NULL; + int optionError = 0; + unsigned char flags[5]; + static struct { + char *name; + int i; + unsigned char mask; + unsigned char value; + char *desc; + } options[] = { + /* data 1 */ + {"valid", 0, (1<<7), (1<<7), + "Boot flags valid"}, + {"persistent", 0, (1<<6), (1<<6), + "Changes are persistent for all future boots"}, + {"efiboot", 0, (1<<5), (1<<5), + "Extensible Firmware Interface Boot (EFI)"}, + /* data 2 */ + {"clear-cmos", 1, (1<<7), (1<<7), + "CMOS clear"}, + {"lockkbd", 1, (1<<6), (1<<6), + "Lock Keyboard"}, + /* data2[5:2] is parsed elsewhere */ + {"screenblank", 1, (1<<1), (1<<1), + "Screen Blank"}, + {"lockoutreset", 1, (1<<0), (1<<0), + "Lock out Resetbuttons"}, + /* data 3 */ + {"lockout_power", 2, (1<<7), (1<<7), + "Lock out (power off/sleep request) via Power Button"}, + {"verbose=default", 2, (3<<5), (0<<5), + "Request quiet BIOS display"}, + {"verbose=no", 2, (3<<5), (1<<5), + "Request quiet BIOS display"}, + {"verbose=yes", 2, (3<<5), (2<<5), + "Request verbose BIOS display"}, + {"force_pet", 2, (1<<4), (1<<4), + "Force progress event traps"}, + {"upw_bypass", 2, (1<<3), (1<<3), + "User password bypass"}, + {"lockout_sleep", 2, (1<<2), (1<<2), + "Log Out Sleep Button"}, + {"cons_redirect=default", 2, (3<<0), (0<<0), + "Console redirection occurs per BIOS configuration setting"}, + {"cons_redirect=skip", 2, (3<<0), (1<<0), + "Suppress (skip) console redirection if enabled"}, + {"cons_redirect=enable", 2, (3<<0), (2<<0), + "Suppress (skip) console redirection if enabled"}, + /* data 4 */ + /* data4[7:4] reserved */ + /* data4[3] BIOS Shared Mode Override, not implemented here */ + /* data4[2:0] BIOS Mux Control Override, not implemented here */ + + /* data5 reserved */ + + {NULL} /* End marker */ + }, *op; + + memset(&flags[0], 0, sizeof(flags)); + token = strtok_r(argv[2] + 8, ",", &saveptr); + while (token != NULL) { + if (strcmp(token, "help") == 0) { + optionError = 1; + break; + } + for (op = options; op->name != NULL; ++op) { + if (strcmp(token, op->name) == 0) { + flags[op->i] &= op->mask; + flags[op->i] |= op->value; + break; + } + } + if (op->name == NULL) { + /* Option not found */ + optionError = 1; + lprintf(LOG_ERR, "Invalid option: %s", token); + } + token = strtok_r(NULL, ",", &saveptr); + } + if (optionError) { + lprintf(LOG_NOTICE, "Legal options settings are:"); + lprintf(LOG_NOTICE, "\thelp:\tprint this message"); + for (op = options; op->name != NULL; ++op) { + lprintf(LOG_NOTICE, "\t%s:\t%s", op->name, op->desc); + } + return (-1); + } + rc = ipmi_chassis_set_bootdev(intf, argv[1], flags); + } + else + rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); + } + } + else { + lprintf(LOG_ERR, "Invalid Chassis command: %s", argv[0]); + return -1; + } + + return rc; +} diff --git a/lib/ipmi_dcmi.c b/lib/ipmi_dcmi.c new file mode 100755 index 0000000..6e030f9 --- /dev/null +++ b/lib/ipmi_dcmi.c @@ -0,0 +1,2193 @@ +/* + * Copyright (C) 2008 Intel Corporation. + * All rights reserved + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Theory of operation + * + * DCMI is the Data Center Management Interface which is a subset of IPMI v2.0. + * DCMI incorporates the ability to locate a system with DCMI functionality, + * its available temperature sensors, and power limiting control. + * + * All of the available DCMI commands are contained in a struct with a numeric + * value and a string. When the user specifies a command the string is + * compared to one of several structs and is then given a numeric value based + * on the matched string. A case statement is used to select the desired + * action from the user. If an invalid string is entered, or a string that is + * not a command option is entered, the available commands are printed to the + * screen. This allows the strings to be changed quickly with the DCMI spec. + * + * Each called function usually executes whichever command was requested to + * keep the main() from being overly complicated. + * + * This code conforms to the 1.0 DCMI Specification + * released by Hari Ramachandran of the Intel Corporation + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <unistd.h> +#include <sys/types.h> +#include <time.h> +#include <netdb.h> + +#include <ipmitool/ipmi_dcmi.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_entity.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_sensor.h> + +#include "../src/plugins/lanplus/lanplus.h" + +#define IPMI_LAN_PORT 0x26f + +extern int verbose; + +static int ipmi_print_sensor_info(struct ipmi_intf *intf, uint16_t rec_id); + +/******************************************************************************* + * The structs below are the DCMI command option strings. They are printed * + * when the user does not issue enough options or the wrong ones. The reason * + * that the DMCI command strings are in a struct is so that when the * + * specification changes, the strings can be changed quickly with out having * + * to change a lot of the code in the main(). * + ******************************************************************************/ + +/* Main set of DCMI commands */ +const struct dcmi_cmd dcmi_cmd_vals[] = { + { 0x00, "discover", " Used to discover supported DCMI capabilities" }, + { 0x01, "power", " Platform power limit command options" }, + { 0x02, "sensors", " Prints the available DCMI sensors" }, + { 0x03, "asset_tag", " Prints the platform's asset tag" }, + { 0x04, "set_asset_tag", " Sets the platform's asset tag" }, + { 0x05, "get_mc_id_string", " Get management controller ID string" }, + { 0x06, "set_mc_id_string", " Set management controller ID string" }, + { 0x07, "thermalpolicy", " Thermal policy get/set" }, + { 0x08, "get_temp_reading", " Get Temperature Readings" }, + { 0x09, "get_conf_param", " Get DCMI Config Parameters" }, + { 0x0A, "set_conf_param", " Set DCMI Config Parameters" }, + { 0x0B, "oob_discover", " Ping/Pong Message for DCMI Discovery" }, + { 0xFF, NULL, NULL } +}; + +/* get capabilites */ +const struct dcmi_cmd dcmi_capable_vals[] = { + { 0x01, "platform", " Lists the system capabilities" }, + { 0x02, "mandatory_attributes", "Lists SEL, identification and" + "temperature attributes" }, + { 0x03, "optional_attributes", " Lists power capabilities" }, + { 0x04, "managebility access", " Lists OOB channel information" }, + { 0xFF, NULL, NULL } +}; + +/* platform capabilities + * Since they are actually in two bytes, we need three structs to make this + * human readable... + */ +const struct dcmi_cmd dcmi_mandatory_platform_capabilities[] = { + { 0x01, "Identification support available", "" }, + { 0x02, "SEL logging available", "" }, + { 0x03, "Chassis power available", "" }, + { 0x04, "Temperature monitor available", "" }, + { 0xFF, NULL, NULL } +}; + +/* optional capabilities */ +const struct dcmi_cmd dcmi_optional_platform_capabilities[] = { + { 0x01, "Power management available", "" }, + { 0xFF, NULL, NULL } +}; + +/* access capabilties */ +const struct dcmi_cmd dcmi_management_access_capabilities[] = { + { 0x01, "In-band KCS channel available", "" }, + { 0x02, "Out-of-band serial TMODE available", "" }, + { 0x03, "Out-of-band secondary LAN channel available", "" }, + { 0x04, "Out-of-band primary LAN channel available", "" }, + { 0x05, "SOL enabled", "" }, + { 0x06, "VLAN capable", "" }, + { 0xFF, NULL, NULL } +}; + +/* identification capabilities */ +const struct dcmi_cmd dcmi_id_capabilities_vals[] = { + { 0x01, "GUID", "" }, + { 0x02, "DHCP hostname", "" }, + { 0x03, "Asset tag", "" }, + { 0xFF, NULL, NULL } +}; + +/* Configuration parameters*/ +const struct dcmi_cmd dcmi_conf_param_vals[] = { + { 0x01, "activate_dhcp", "\tActivate DHCP"}, + { 0x02, "dhcp_config", "\tDHCP Configuration" }, + { 0x03, "init", "\t\tInitial timeout interval" }, + { 0x04, "timeout", "\t\tServer contact timeout interval" }, + { 0x05, "retry", "\t\tServer contact retry interval" }, + { 0xFF, NULL, NULL } +}; + + +/* temperature monitoring capabilities */ +const struct dcmi_cmd dcmi_temp_monitoring_vals[] = { + { 0x01, "inlet", " Inlet air temperature sensors" }, + { 0x02, "cpu", " CPU temperature sensors" }, + { 0x03, "baseboard", "Baseboard temperature sensors" }, + { 0xff, NULL, NULL } +}; + +/* These are not comands. These are the DCMI temp sensors and their numbers + * If new sensors are added, they need to be added to this list with their + * sensor number + */ +const struct dcmi_cmd dcmi_discvry_snsr_vals[] = { + { 0x40, "Inlet", " Inlet air temperature sensors" }, + { 0x41, "CPU", " CPU temperature sensors" }, + { 0x42, "Baseboard", "Baseboard temperature sensors" }, + { 0xff, NULL, NULL } +}; + +/* Temperature Readings */ +const struct dcmi_cmd dcmi_temp_read_vals[] = { + { 0x40, "Inlet", "Inlet air temperature(40h) " }, + { 0x41, "CPU", "CPU temperature sensors(41h) " }, + { 0x42, "Baseboard", "Baseboard temperature sensors(42h) " }, + { 0xff, NULL, NULL } +}; + +/* power management/control commands */ +const struct dcmi_cmd dcmi_pwrmgmt_vals[] = { + { 0x00, "reading", " Get power related readings from the system" }, + { 0x01, "get_limit", " Get the configured power limits" }, + { 0x02, "set_limit", " Set a power limit option" }, + { 0x03, "activate", " Activate the set power limit" }, + { 0x04, "deactivate", "Deactivate the set power limit" }, + { 0xFF, NULL, NULL } +}; + +/* set power limit commands */ +const struct dcmi_cmd dcmi_pwrmgmt_set_usage_vals[] = { + { 0x00, "action", " <no_action | sel_logging | power_off>" }, + { 0x01, "limit", " <number in Watts>" }, + { 0x02, "correction", "<number in milliseconds>" }, + { 0x03, "sample", " <number in seconds>" }, + { 0xFF, NULL, NULL } +}; + +/* power management/get action commands */ +const struct dcmi_cmd dcmi_pwrmgmt_get_action_vals[] = { + { 0x00, "No Action", ""}, + { 0x01, "Hard Power Off & Log Event to SEL", ""}, + + { 0x02, "OEM reserved (02h)", ""}, + { 0x03, "OEM reserved (03h)", ""}, + { 0x04, "OEM reserved (04h)", ""}, + { 0x05, "OEM reserved (05h)", ""}, + { 0x06, "OEM reserved (06h)", ""}, + { 0x07, "OEM reserved (07h)", ""}, + { 0x08, "OEM reserved (08h)", ""}, + { 0x09, "OEM reserved (09h)", ""}, + { 0x0a, "OEM reserved (0ah)", ""}, + { 0x0b, "OEM reserved (0bh)", ""}, + { 0x0c, "OEM reserved (0ch)", ""}, + { 0x0d, "OEM reserved (0dh)", ""}, + { 0x0e, "OEM reserved (0eh)", ""}, + { 0x0f, "OEM reserved (0fh)", ""}, + { 0x10, "OEM reserved (10h)", ""}, + + { 0x11, "Log Event to SEL", ""}, + { 0xFF, NULL, NULL } +}; + +/* power management/set action commands */ +const struct dcmi_cmd dcmi_pwrmgmt_action_vals[] = { + { 0x00, "no_action", "No Action"}, + { 0x01, "power_off", "Hard Power Off & Log Event to SEL"}, + { 0x11, "sel_logging", "Log Event to SEL"}, + + { 0x02, "oem_02", "OEM reserved (02h)"}, + { 0x03, "oem_03", "OEM reserved (03h)"}, + { 0x04, "oem_04", "OEM reserved (04h)"}, + { 0x05, "oem_05", "OEM reserved (05h)"}, + { 0x06, "oem_06", "OEM reserved (06h)"}, + { 0x07, "oem_07", "OEM reserved (07h)"}, + { 0x08, "oem_08", "OEM reserved (08h)"}, + { 0x09, "oem_09", "OEM reserved (09h)"}, + { 0x0a, "oem_0a", "OEM reserved (0ah)"}, + { 0x0b, "oem_0b", "OEM reserved (0bh)"}, + { 0x0c, "oem_0c", "OEM reserved (0ch)"}, + { 0x0d, "oem_0d", "OEM reserved (0dh)"}, + { 0x0e, "oem_0e", "OEM reserved (0eh)"}, + { 0x0f, "oem_0f", "OEM reserved (0fh)"}, + { 0x10, "oem_10", "OEM reserved (10h)"}, + + { 0xFF, NULL, NULL } +}; + +/* thermal policy action commands */ +const struct dcmi_cmd dcmi_thermalpolicy_vals[] = { + { 0x00, "get", "Get thermal policy" }, + { 0x01, "set", "Set thermal policy" }, + { 0xFF, NULL, NULL } +}; + +/* thermal policy action commands */ +const struct dcmi_cmd dcmi_confparameters_vals[] = { + { 0x00, "get", "Get configuration parameters" }, + { 0x01, "set", "Set configuration parameters" }, + { 0xFF, NULL, NULL } +}; + +/* entityIDs used in thermap policy */ +const struct dcmi_cmd dcmi_thermalpolicy_set_parameters_vals[] = { + { 0x00, "volatile", " Current Power Cycle" }, + { 0x01, "nonvolatile", "Set across power cycles" }, + { 0x01, "poweroff", " Hard Power Off system" }, + { 0x00, "nopoweroff", " No 'Hard Power Off' action" }, + { 0x01, "sel", " Log event to SEL" }, + { 0x00, "nosel", " No 'Log event to SEL' action" }, + { 0x00, "disabled", " Disabled" }, + { 0x00, NULL, NULL } +}; + + +/* DCMI command specific completion code results per 1.0 spec + * 80h - parameter not supported. + * 81h - attempt to set the ‘set in progress’ value (in parameter #0) when not + * in the ‘set complete’ state. (This completion code provides a way to + * recognize that another party has already ‘claimed’ the parameters) + * 82h - attempt to write read-only parameter + * 82h - set not supported on selected channel (e.g. channel is session-less.) + * 83h - access mode not supported + * 84h – Power Limit out of range + * 85h – Correction Time out of range + * 89h – Statistics Reporting Period out of range + */ +const struct valstr dcmi_ccode_vals[] = { + { 0x80, "Parameter not supported" }, + { 0x81, "Something else has already claimed these parameters" }, + { 0x82, "Not supported or failed to write a read-only parameter" }, + { 0x83, "Access mode is not supported" }, + { 0x84, "Power/Thermal limit out of range" }, + { 0x85, "Correction/Exception time out of range" }, + { 0x89, "Sample/Statistics Reporting period out of range" }, + { 0x8A, "Power limit already active" }, + { 0xFF, NULL } +}; + +/* End strings */ + +/* This was taken from print_valstr() from helper.c. It serves the same + * purpose but with out the extra formatting. This function simply prints + * the dcmi_cmd struct provided. verthorz specifies to print vertically or + * horizontally. If the string is printed horizontally then a | will be + * printed between each instance of vs[i].str until it is NULL + * + * @vs: value string list to print + * @title: name of this value string list + * @loglevel: what log level to print, -1 for stdout + * @verthorz: printed vertically or horizontally, 0 or 1 + */ +void +print_strs(const struct dcmi_cmd * vs, const char * title, int loglevel, + int verthorz) +{ + int i; + + if (vs == NULL) + return; + + if (title != NULL) { + if (loglevel < 0) + printf("\n%s\n", title); + else + lprintf(loglevel, "\n%s", title); + } + for (i = 0; vs[i].str != NULL; i++) { + if (loglevel < 0) { + if (vs[i].val < 256) + if (verthorz == 0) + printf(" %s %s\n", vs[i].str, vs[i].desc); + else + printf("%s", vs[i].str); + else if (verthorz == 0) + printf(" %s %s\n", vs[i].str, vs[i].desc); + else + printf("%s", vs[i].str); + } else { + if (vs[i].val < 256) + lprintf(loglevel, " %s %s", vs[i].str, vs[i].desc); + else + lprintf(loglevel, " %s %s", vs[i].str, vs[i].desc); + } + /* Check to see if this is NOT the last element in vs.str if true + * print the | else don't print anything. + */ + if ((verthorz == 1) && (vs[i+1].str != NULL)) + printf(" | "); + } + if (verthorz == 0) { + if (loglevel < 0) { + printf("\n"); + } else { + lprintf(loglevel, ""); + } + } +} + +/* This was taken from str2val() from helper.c. It serves the same + * purpose but with the addition of a desc field from the structure. + * This function converts the str from the dcmi_cmd struct provided to the + * value associated to the compared string in the struct. + * + * @str: string to compare against + * @vs: dcmi_cmd structure + */ +uint16_t +str2val2(const char *str, const struct dcmi_cmd *vs) +{ + int i; + if (vs == NULL || str == NULL) { + return 0; + } + for (i = 0; vs[i].str != NULL; i++) { + if (strncasecmp(vs[i].str, str, + __maxlen(str, vs[i].str)) == 0) { + return vs[i].val; + } + } + return vs[i].val; +} + +/* This was taken from val2str() from helper.c. It serves the same + * purpose but with the addition of a desc field from the structure. + * This function converts the val and returns a string from the dcmi_cmd + * struct provided in the struct. + * + * @val: value to compare against + * @vs: dcmi_cmd structure + */ +const char * +val2str2(uint16_t val, const struct dcmi_cmd *vs) +{ + static char un_str[32]; + int i; + + if (vs == NULL) + return NULL; + + for (i = 0; vs[i].str != NULL; i++) { + if (vs[i].val == val) + return vs[i].str; + } + memset(un_str, 0, 32); + snprintf(un_str, 32, "Unknown (0x%x)", val); + return un_str; +} + +/* check the DCMI response from the BMC + * @rsp: Response data structure + */ +static int +chk_rsp(struct ipmi_rs * rsp) +{ + /* if the response from the intf is NULL then the BMC is experiencing + * some issue and cannot complete the command + */ + if (rsp == NULL) { + lprintf(LOG_ERR, "\n Unable to get DCMI information"); + return 1; + } + /* if the completion code is greater than zero there was an error. We'll + * use val2str from helper.c to print the error from either the DCMI + * completion code struct or the generic IPMI completion_code_vals struct + */ + if ((rsp->ccode >= 0x80) && (rsp->ccode <= 0x8F)) { + lprintf(LOG_ERR, "\n DCMI request failed because: %s (%x)", + val2str(rsp->ccode, dcmi_ccode_vals), rsp->ccode); + return 1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "\n DCMI request failed because: %s (%x)", + val2str(rsp->ccode, completion_code_vals), rsp->ccode); + return 1; + } + /* check to make sure this is a DCMI firmware */ + if(rsp->data[0] != IPMI_DCMI) { + printf("\n A valid DCMI command was not returned! (%x)", rsp->data[0]); + return 1; + } + return 0; +} + +/* Get capabilities ipmi response + * + * This function returns the available capabilities of the platform. + * The reason it returns in the rsp struct is so that it can be used for other + * purposes. + * + * returns ipmi response structure + * + * @intf: ipmi interface handler + * @selector: Parameter selector + */ +struct ipmi_rs * +ipmi_dcmi_getcapabilities(struct ipmi_intf * intf, uint8_t selector) +{ + struct ipmi_rq req; /* request data to send to the BMC */ + uint8_t msg_data[2]; /* 'raw' data to be sent to the BMC */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = selector; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.0 spec */ + req.msg.cmd = IPMI_DCMI_COMPAT; /* 0x01 per 1.0 spec */ + req.msg.data = msg_data; /* 0xDC 0x01 or the msg_data above */ + req.msg.data_len = 2; /* How many times does req.msg.data need to read */ + + return intf->sendrecv(intf, &req); +} +/* end capabilities struct */ + +/* Displays capabilities from structure + * returns void + * + * @cmd: dcmi_cmd structure + * @data_val: holds value of what to display + */ +void +display_capabilities_attributes(const struct dcmi_cmd *cmd, uint8_t data_val) +{ + uint8_t i; + for (i = 0x01; cmd[i-1].val != 0xFF; i++) { + if (data_val & (1<<(i-1))) { + printf(" %s\n", val2str2(i, cmd)); + } + } +} + +static int +ipmi_dcmi_prnt_oobDiscover(struct ipmi_intf * intf) +{ +# ifndef IPMI_INTF_LANPLUS + lprintf(LOG_ERR, + "DCMI Discovery is available only when LANplus(IPMI v2.0) is enabled."); + return (-1); +# else + int rc; + struct ipmi_session *s; + + if (intf->opened == 0 && intf->open != NULL) { + if (intf->open(intf) < 0) + return (-1); + } + if (intf == NULL || intf->session == NULL) + return -1; + + s = intf->session; + + if (s->port == 0) + s->port = IPMI_LAN_PORT; + if (s->privlvl == 0) + s->privlvl = IPMI_SESSION_PRIV_ADMIN; + if (s->timeout == 0) + s->timeout = IPMI_LAN_TIMEOUT; + if (s->retry == 0) + s->retry = IPMI_LAN_RETRY; + + if (s->hostname == NULL || strlen((const char *)s->hostname) == 0) { + lprintf(LOG_ERR, "No hostname specified!"); + return -1; + } + + intf->abort = 1; + intf->session->sol_data.sequence_number = 1; + + if (ipmi_intf_socket_connect (intf) == -1) { + lprintf(LOG_ERR, "Could not open socket!"); + return -1; + } + + if (intf->fd < 0) { + lperror(LOG_ERR, "Connect to %s failed", + s->hostname); + intf->close(intf); + return -1; + } + + intf->opened = 1; + + /* Lets ping/pong */ + return ipmiv2_lan_ping(intf); +# endif +} + +/* This is the get DCMI Capabilities function to see what the BMC supports. + * + * returns 0 with out error -1 with any errors + * + * @intf: ipmi interface handler + * @selector: selection parameter + */ +static int +ipmi_dcmi_prnt_getcapabilities(struct ipmi_intf * intf, uint8_t selector) +{ + uint8_t i; + uint8_t bit_shifter = 0; + struct capabilities cape; + struct ipmi_rs * rsp; + rsp = ipmi_dcmi_getcapabilities(intf, selector); + + if(chk_rsp(rsp)) + return -1; + + /* if there were no errors, the command worked! */ + memcpy(&cape, rsp->data, sizeof (cape)); + /* check to make sure that this is a 1.0/1.1/1.5 command */ + if ((cape.conformance != IPMI_DCMI_CONFORM) + && (cape.conformance != IPMI_DCMI_1_1_CONFORM) + && (cape.conformance != IPMI_DCMI_1_5_CONFORM)) { + lprintf(LOG_ERR, + "ERROR! This command is not available on this platform"); + return -1; + } + /* check to make sure that this is a rev .01 or .02 */ + if (cape.revision != 0x01 && cape.revision != 0x02) { + lprintf(LOG_ERR, + "ERROR! This command is not compatible with this version"); + return -1; + } + /* 0x01 - platform capabilities + * 0x02 - Manageability Access Capabilities + * 0x03 - SEL Capability + * 0x04 - Identification Capability + * 0x05 - LAN Out-Of-Band Capability + * 0x06 - Serial Out-Of-Band TMODE Capability + */ + switch (selector) { + case 0x01: + printf(" Supported DCMI capabilities:\n"); + /* loop through each of the entries in the first byte from the + * struct + */ + printf("\n Mandatory platform capabilties\n"); + display_capabilities_attributes( + dcmi_mandatory_platform_capabilities, cape.data_byte1); + /* loop through each of the entries in the second byte from the + * struct + */ + printf("\n Optional platform capabilties\n"); + display_capabilities_attributes( + dcmi_optional_platform_capabilities, cape.data_byte2); + /* loop through each of the entries in the third byte from the + * struct + */ + printf("\n Managebility access capabilties\n"); + display_capabilities_attributes( + dcmi_management_access_capabilities, cape.data_byte3); + break; + case 0x02: + printf("\n Mandatory platform attributes:\n"); + /* byte 1 & 2 data */ + printf("\n SEL Attributes: "); + printf("\n SEL automatic rollover is "); + /* mask the 2nd byte of the data response with 10000000b or 0x80 + * because of the endian-ness the 15th bit is in the second byte + */ + if ((cape.data_byte2 & 0x80)) + printf("enabled"); + else + printf("not present"); + /* since the number of SEL entries is split across the two data + * bytes we will need to bit shift and append them together again + */ + /* cast cape.data_byte1 as 16 bits */ + uint16_t sel_entries = (uint16_t)cape.data_byte1; + /* or sel_entries with byte 2 and shift it 8 places */ + sel_entries |= (uint16_t)cape.data_byte2 << 8; + printf("\n %d SEL entries\n", sel_entries & 0xFFF); + /* byte 3 data */ + printf("\n Identification Attributes: \n"); + display_capabilities_attributes( + dcmi_id_capabilities_vals, cape.data_byte3); + /* byte 4 data */ + printf("\n Temperature Monitoring Attributes: \n"); + display_capabilities_attributes(dcmi_temp_monitoring_vals, + cape.data_byte4); + break; + case 0x03: + printf("\n Optional Platform Attributes: \n"); + /* Power Management */ + printf("\n Power Management:\n"); + if (cape.data_byte1 == 0x40) { + printf(" Slave address of device: 20h (BMC)\n" ); + } else { + printf(" Slave address of device: %xh (8bits)" + "(Satellite/External controller)\n", + cape.data_byte1); + } + /* Controller channel number (4-7) bits */ + if ((cape.data_byte2>>4) == 0x00) { + printf(" Channel number is 0h (Primary BMC)\n"); + } else { + printf(" Channel number is %xh \n", + (cape.data_byte2>>4)); + } + /* Device revision (0-3) */ + printf(" Device revision is %d \n", + cape.data_byte2 &0xf); + break; + case 0x04: + /* LAN */ + printf("\n Manageability Access Attributes: \n"); + if (cape.data_byte1 == 0xFF) { + printf(" Primary LAN channel is not available for OOB\n"); + } else { + printf(" Primary LAN channel number: %d is available\n", + cape.data_byte1); + } + if (cape.data_byte2 == 0xFF) { + printf(" Secondary LAN channel is not available for OOB\n"); + } else { + printf(" Secondary LAN channel number: %d is available\n", + cape.data_byte2); + } + /* serial */ + if (cape.data_byte3 == 0xFF) { + printf(" No serial channel is available\n"); + } else { + printf(" Serial channel number: %d is available\n", + cape.data_byte3); + } + break; + default: + return -1; + } + return 0; + /* return intf->sendrecv(intf, &req); */ +} + +/* This is the get asset tag command. This checks the length of the asset tag + * with the first read, then reads n number of bytes thereafter to get the + * complete asset tag. + * + * @intf: ipmi interface handler + * @offset: where to start reading the asset tag + * @length: how much to read + * + * returns ipmi_rs structure + */ +struct ipmi_rs * +ipmi_dcmi_getassettag(struct ipmi_intf * intf, uint8_t offset, uint8_t length) +{ + struct ipmi_rq req; /* request data to send to the BMC */ + uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = offset; /* offset 0 */ + msg_data[2] = length; /* read one byte */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */ + req.msg.cmd = IPMI_DCMI_GETASSET; /* 0x01 per 1.1 spec */ + req.msg.data = msg_data; /* msg_data above */ + req.msg.data_len = 3; /* How many times does req.msg.data need to read */ + return intf->sendrecv(intf, &req); +} + +/* This is the get asset tag command. The function first checks to see if the + * platform is capable of getting the asset tag by calling the getcapabilities + * function and checking the response. Then it checks the length of the asset + * tag with the first read, then x number of reads thereafter to get the asset + * complete asset tag then print it. + * + * @intf: ipmi interface handler + * + * returns 0 if no failure, -1 with a failure + */ +static int +ipmi_dcmi_prnt_getassettag(struct ipmi_intf * intf) +{ + uint8_t data_byte2; + struct ipmi_rs * rsp; /* ipmi response */ + uint8_t taglength = 0; + uint8_t getlength = 0; + uint8_t offset = 0; + uint8_t i; + /* now let's get the asset tag length */ + rsp = ipmi_dcmi_getassettag(intf, 0, 0); + if (chk_rsp(rsp)) { + return -1; + } + taglength = rsp->data[1]; + printf("\n Asset tag: "); + while (taglength) { + getlength = taglength / DCMI_MAX_BYTE_SIZE ? + DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE; + rsp = ipmi_dcmi_getassettag(intf, offset, getlength); + /* macro has no effect here where can generate sig segv + * if rsp occurs with null + */ + if (rsp != NULL) { + GOOD_ASSET_TAG_CCODE(rsp->ccode); + } + if (chk_rsp(rsp)) { + return -1; + } + for (i=0; i<getlength; i++) { + printf("%c", rsp->data[i+2]); + } + offset += getlength; + taglength -= getlength; + } + printf("\n"); + return 0; +} + +/* This is the set asset tag command. This checks the length of the asset tag + * with the first read, then reads n number of bytes thereafter to set the + * complete asset tag. + * + * @intf: ipmi interface handler + * @offset: offset to write + * @length: number of bytes to write (16 bytes maximum) + * @data: data to write + * + * returns ipmi_rs structure + */ +struct ipmi_rs * +ipmi_dcmi_setassettag(struct ipmi_intf * intf, uint8_t offset, uint8_t length, + uint8_t *data) +{ + struct ipmi_rq req; /* request data to send to the BMC */ + uint8_t msg_data[3+length]; /* 'raw' data to be sent to the BMC */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = offset; /* offset 0 */ + msg_data[2] = length; /* read one byte */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */ + req.msg.cmd = IPMI_DCMI_SETASSET; /* 0x08 per 1.1 spec */ + req.msg.data = msg_data; /* msg_data above */ + /* How many times does req.msg.data need to read */ + req.msg.data_len = length + 3; + memcpy(req.msg.data + 3, data, length); + + return intf->sendrecv(intf, &req); +} + +static int +ipmi_dcmi_prnt_setassettag(struct ipmi_intf * intf, uint8_t * data) +{ + uint8_t data_byte2; + struct ipmi_rs * rsp; /* ipmi response */ + uint8_t tmpData[DCMI_MAX_BYTE_SIZE]; + uint8_t taglength = 0; + uint8_t getlength = 0; + uint8_t offset = 0; + uint8_t i; + + /* now let's get the asset tag length */ + taglength = strlen(data); + if (taglength > 64){ + lprintf(LOG_ERR, "\nValue is too long."); + return -1; + } + printf("\n Set Asset Tag: "); + while (taglength) { + getlength = taglength / DCMI_MAX_BYTE_SIZE ? + DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE; + memcpy(tmpData, data + offset, getlength); + rsp = ipmi_dcmi_setassettag(intf, offset, getlength, tmpData); + if (chk_rsp(rsp)) { + return -1; + } + for (i=0; i<getlength; i++) { + printf("%c", tmpData[i]); + } + offset += getlength; + taglength -= getlength; + } + printf("\n"); + return 0; +} + +/* Management Controller Identifier String is provided in order to accommodate + * the requirement for the management controllers to identify themselves. + * + * @intf: ipmi interface handler + * @offset: offset to read + * @length: number of bytes to read (16 bytes maximum) + * + * returns ipmi_rs structure + */ +struct ipmi_rs * +ipmi_dcmi_getmngctrlids(struct ipmi_intf * intf, uint8_t offset, uint8_t length) +{ + struct ipmi_rq req; /* request data to send to the BMC */ + uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = offset; /* offset 0 */ + msg_data[2] = length; /* read one byte */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */ + req.msg.cmd = IPMI_DCMI_GETMNGCTRLIDS; /* 0x09 per 1.1 spec */ + req.msg.data = msg_data; /* msg_data above */ + /* How many times does req.msg.data need to read */ + req.msg.data_len = 3; + return intf->sendrecv(intf, &req); +} + +static int +ipmi_dcmi_prnt_getmngctrlids(struct ipmi_intf * intf) +{ + uint8_t data_byte2; + struct ipmi_rs * rsp; /* ipmi response */ + uint8_t taglength = 0; + uint8_t getlength = 0; + uint8_t offset = 0; + uint8_t i; + + /* now let's get the asset tag length */ + rsp = ipmi_dcmi_getmngctrlids(intf, 0, 1); + + if (chk_rsp(rsp)) { + return -1; + } + + taglength = rsp->data[1]; + + printf("\n Get Management Controller Identifier String: "); + while (taglength) { + getlength = taglength / DCMI_MAX_BYTE_SIZE ? + DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE; + rsp = ipmi_dcmi_getmngctrlids(intf, offset, getlength); + + if (chk_rsp(rsp)) { + return -1; + } + for (i=0; i<getlength; i++) { + printf("%c", rsp->data[i+2]); + } + offset += getlength; + taglength -= getlength; + } + printf("\n"); + return 0; +} + +/* Management Controller Identifier String is provided in order to accommodate + * the requirement for the management controllers to identify themselves. + * + * @intf: ipmi interface handler + * @offset: offset to write + * @length: number of bytes to write (16 bytes maximum) + * @data: data to write + * + * returns ipmi_rs structure + */ +struct ipmi_rs * +ipmi_dcmi_setmngctrlids(struct ipmi_intf * intf, uint8_t offset, uint8_t length, + uint8_t *data) +{ + struct ipmi_rq req; /* request data to send to the BMC */ + uint8_t msg_data[3+length]; /* 'raw' data to be sent to the BMC */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = offset; /* offset 0 */ + msg_data[2] = length; /* read one byte */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */ + req.msg.cmd = IPMI_DCMI_SETMNGCTRLIDS; /* 0x0A per 1.1 spec */ + req.msg.data = msg_data; /* msg_data above */ + /* How many times does req.msg.data need to read */ + req.msg.data_len = 3 + length; + memcpy(req.msg.data + 3, data, length); + + return intf->sendrecv(intf, &req); +} + +/* Set Asset Tag command provides ability for the management console to set the + * asset tag as appropriate. Management controller is not responsible for the + * data format used for the Asset Tag once modified by IPDC. + * + * @intf: ipmi interface handler + * + * returns 0 if no failure, -1 with a failure + */ +static int +ipmi_dcmi_prnt_setmngctrlids(struct ipmi_intf * intf, uint8_t * data) +{ + uint8_t data_byte2; + struct ipmi_rs * rsp; /* ipmi response */ + uint8_t tmpData[DCMI_MAX_BYTE_SIZE]; + uint8_t taglength = 0; + uint8_t getlength = 0; + uint8_t offset = 0; + uint8_t i; + + data += '\0'; + taglength = strlen(data) +1; + + if (taglength > 64) { + lprintf(LOG_ERR, "\nValue is too long."); + return -1; + } + + printf("\n Set Management Controller Identifier String Command: "); + while (taglength) { + getlength = taglength / DCMI_MAX_BYTE_SIZE ? + DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE; + memcpy(tmpData, data + offset, getlength); + rsp = ipmi_dcmi_setmngctrlids(intf, offset, getlength, tmpData); + /* because after call "Set mc id string" RMCP+ will go down + * we have no "rsp" + */ + if (strncmp(intf->name, "lanplus", 7)) { + if (chk_rsp(rsp)) { + return -1; + } + } + for (i=0; i<getlength; i++) { + printf("%c", tmpData[i]); + } + offset += getlength; + taglength -= getlength; + } + printf("\n"); + return 0; +} + +/* Issues a discovery command to see what sensors are available on the target. + * system. + * + * @intf: ipmi interface handler + * @isnsr: entity ID + * @offset: offset (Entity instace start) + * + * returns ipmi_rs structure + */ +struct ipmi_rs * +ipmi_dcmi_discvry_snsr(struct ipmi_intf * intf, uint8_t isnsr, uint8_t offset) +{ + struct ipmi_rq req; /* ipmi request struct */ + uint8_t msg_data[5]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = 0x01; /* Senser Type = Temp (01h) */ + msg_data[2] = isnsr; /* Sensor Number */ + msg_data[3] = 0x00; /* Entity Instance, set to read all instances */ + msg_data[4] = offset; /* Entity instace start */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETSNSR; + req.msg.data = msg_data; /* Contents above */ + req.msg.data_len = 5; /* how many times does req.msg.data need to read */ + + return intf->sendrecv(intf, &req); +} + +/* DCMI sensor discovery + * Uses the dcmi_discvry_snsr_vals struct to print its string and + * uses the numeric values to request the sensor sdr record id. + * + * @intf: ipmi interface handler + * @isnsr: entity ID + * @ient: sensor entity id + */ +static int +ipmi_dcmi_prnt_discvry_snsr(struct ipmi_intf * intf, uint8_t isnsr) +{ + int i = 0; + struct ipmi_rs * rsp; /* ipmi response */ + uint8_t records = 0; + int8_t instances = 0; + uint8_t offset = 0; + uint16_t record_id = 0; + uint8_t id_buff[16]; /* enough for 8 record IDs */ + rsp = ipmi_dcmi_discvry_snsr(intf, isnsr, 0); + if (chk_rsp(rsp)) { + return -1; + } + instances = rsp->data[1]; + printf("\n%s: %d temperature sensor%s found:\n", + val2str2(isnsr, dcmi_discvry_snsr_vals), + instances, + (instances > 1) ? "s" : ""); + while(instances > 0) { + ipmi_dcmi_discvry_snsr(intf, isnsr, offset); + if (chk_rsp(rsp)) { + return -1; + } + records = rsp->data[2]; + /* cache the data since it may be destroyed by subsequent + * ipmi_xxx calls + */ + memcpy(id_buff, &rsp->data[3], 16); + for (i=0; i<records; i++) { + /* Record ID is in little endian format */ + record_id = (id_buff[2*i + 1] << 8) + id_buff[2*i]; + printf("Record ID 0x%04x: ", record_id); + ipmi_print_sensor_info(intf, record_id); + } + offset += 8; + instances -= records; + } + return 0; +} +/* end sensor discovery */ + +/* Power Management get power reading + * + * @intf: ipmi interface handler + */ +static int +ipmi_dcmi_pwr_rd(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct power_reading val; + struct tm tm_t; + time_t t; + uint8_t msg_data[4]; /* number of request data bytes */ + memset(&tm_t, 0, sizeof(tm_t)); + memset(&t, 0, sizeof(t)); + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = 0x01; /* Mode Power Status */ + msg_data[2] = 0x00; /* reserved */ + msg_data[3] = 0x00; /* reserved */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETRED; /* Get power reading */ + req.msg.data = msg_data; /* msg_data above */ + req.msg.data_len = 4; /* how many times does req.msg.data need to read */ + + rsp = intf->sendrecv(intf, &req); + + if (chk_rsp(rsp)) { + return -1; + } + /* rsp->data[0] is equal to response data byte 2 in spec */ + /* printf("Group Extension Identification: %02x\n", rsp->data[0]); */ + memcpy(&val, rsp->data, sizeof (val)); + t = val.time_stamp; + gmtime_r(&t, &tm_t); + printf("\n"); + printf(" Instantaneous power reading: %8d Watts\n", + val.curr_pwr); + printf(" Minimum during sampling period: %8d Watts\n", + val.min_sample); + printf(" Maximum during sampling period: %8d Watts\n", + val.max_sample); + printf(" Average power reading over sample period: %8d Watts\n", + val.avg_pwr); + printf(" IPMI timestamp: %s", + asctime(&tm_t)); + printf(" Sampling period: %08d Milliseconds\n", + val.sample); + printf(" Power reading state is: "); + /* mask the rsp->data so that we only care about bit 6 */ + if((val.state & 0x40) == 0x40) { + printf("activated"); + } else { + printf("deactivated"); + } + printf("\n\n"); + return 0; +} +/* end Power Management get reading */ + + +/* This is the get thermalpolicy command. + * + * @intf: ipmi interface handler + */ +int +ipmi_dcmi_getthermalpolicy(struct ipmi_intf * intf, uint8_t entityID, + uint8_t entityInstance) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct thermal_limit val; + uint8_t msg_data[3]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = entityID; /* Inlet Temperature DCMI ID*/ + msg_data[2] = entityInstance; /* Entity Instance */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETTERMALLIMIT; /* Get thermal policy reading */ + req.msg.data = msg_data; /* msg_data above */ + req.msg.data_len = 3; /* how many times does req.msg.data need to read */ + + rsp = intf->sendrecv(intf, &req); + + if (chk_rsp(rsp)) { + return -1; + } + /* rsp->data[0] is equal to response data byte 2 in spec */ + memcpy(&val, rsp->data, sizeof (val)); + printf("\n"); + printf(" Persistance flag is: %s\n", + ((val.exceptionActions & 0x80) ? "set" : "notset")); + printf(" Exception Actions, taken if the Temperature Limit exceeded:\n"); + printf(" Hard Power Off system and log event: %s\n", + ((val.exceptionActions & 0x40) ? "active":"inactive")); + printf(" Log event to SEL only: %s\n", + ((val.exceptionActions & 0x20) ? "active":"inactive")); + printf(" Temperature Limit %d degrees\n", + val.tempLimit); + printf(" Exception Time %d seconds\n", + val.exceptionTime); + printf("\n\n"); + return 0; +} + +/* This is the set thermalpolicy command. + * + * @intf: ipmi interface handler + */ +int +ipmi_dcmi_setthermalpolicy(struct ipmi_intf * intf, + uint8_t entityID, + uint8_t entityInst, + uint8_t persistanceFlag, + uint8_t actionHardPowerOff, + uint8_t actionLogToSEL, + uint8_t tempLimit, + uint8_t samplingTimeLSB, + uint8_t samplingTimeMSB) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[7]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = entityID; /* Inlet Temperature DCMI ID*/ + msg_data[2] = entityInst; /* Entity Instance */ + /* persistance and actions or disabled if no actions */ + msg_data[3] = (((persistanceFlag ? 1 : 0) << 7) | + ((actionHardPowerOff? 1 : 0) << 6) | + ((actionLogToSEL ? 1 : 0) << 5)); + msg_data[4] = tempLimit; + msg_data[5] = samplingTimeLSB; + msg_data[6] = samplingTimeMSB; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + /* Get thermal policy reading */ + req.msg.cmd = IPMI_DCMI_SETTERMALLIMIT; + req.msg.data = msg_data; /* msg_data above */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 7; + + rsp = intf->sendrecv(intf, &req); + if (chk_rsp(rsp)) { + return -1; + } + /* rsp->data[0] is equal to response data byte 2 in spec */ + printf("\nThermal policy %d for %0Xh entity successfully set.\n\n", + entityInst, entityID); + return 0; +} + +/* This is Get Temperature Readings Command + * + * returns ipmi response structure + * + * @intf: ipmi interface handler + */ +struct ipmi_rs * +ipmi_dcmi_get_temp_readings(struct ipmi_intf * intf, + uint8_t entityID, + uint8_t entityInst, + uint8_t entityInstStart) +{ + struct ipmi_rq req; + uint8_t msg_data[5]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = 0x01; /* Sensor type */ + msg_data[2] = entityID; /* Entity Instance */ + msg_data[3] = entityInst; + msg_data[4] = entityInstStart; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETTEMPRED; /* Get thermal policy reading */ + req.msg.data = msg_data; /* msg_data above */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 5; + return intf->sendrecv(intf, &req); +} + +static int +ipmi_dcmi_prnt_get_temp_readings(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + int i,j, tota_inst, get_inst, offset = 0; + /* Print sensor description */ + printf("\n\tEntity ID\t\t\tEntity Instance\t Temp. Readings"); + for (i = 0; dcmi_temp_read_vals[i].str != NULL; i++) { + /* get all of the information about this sensor */ + rsp = ipmi_dcmi_get_temp_readings(intf, + dcmi_temp_read_vals[i].val, 0, 0); + if (chk_rsp(rsp)) { + continue; + } + /* Total number of available instances for the Entity ID */ + offset = 0; + tota_inst = rsp->data[1]; + while (tota_inst > 0) { + get_inst = ((tota_inst / DCMI_MAX_BYTE_TEMP_READ_SIZE) ? + DCMI_MAX_BYTE_TEMP_READ_SIZE : + (tota_inst % DCMI_MAX_BYTE_TEMP_READ_SIZE)); + rsp = ipmi_dcmi_get_temp_readings(intf, + dcmi_temp_read_vals[i].val, offset, 0); + if (chk_rsp(rsp)) { + continue; + } + /* Number of sets of Temperature Data in this + * response (Max 8 per response) + */ + for (j=0; j < rsp->data[2]*2; j=j+2) { + /* Print Instance temperature info */ + printf("\n%s",dcmi_temp_read_vals[i].desc); + printf("\t\t%i\t\t%c%i C", rsp->data[j+4], + ((rsp->data[j+3]) >> 7) ? + '-' : '+', (rsp->data[j+3] & 127)); + } + offset += get_inst; + tota_inst -= get_inst; + } + } + return 0; +} + +/* This is Get DCMI Config Parameters Command + * + * returns ipmi response structure + * + * @intf: ipmi interface handler + */ +struct ipmi_rs * +ipmi_dcmi_getconfparam(struct ipmi_intf * intf, int param_selector) +{ + struct ipmi_rq req; + uint8_t msg_data[3]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = param_selector; /* Parameter selector */ + /* Set Selector. Selects a given set of parameters under a given Parameter + * selector value. 00h if parameter doesn't use a Set Selector. + */ + msg_data[2] = 0x00; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETCONFPARAM; /* Get DCMI Config Parameters */ + req.msg.data = msg_data; /* Contents above */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 3; + return intf->sendrecv(intf, &req); +} + +static int +ipmi_dcmi_prnt_getconfparam(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + const int dcmi_conf_params = 5; + int param_selector; + uint16_t tmp_value = 0; + /* We are not interested in parameter 1 which always will return 0 */ + for (param_selector = 2 ; param_selector <= dcmi_conf_params; + param_selector++) { + rsp = ipmi_dcmi_getconfparam(intf, param_selector); + if (chk_rsp(rsp)) { + return -1; + } + /* Time to print what we have got */ + switch(param_selector) { + case 2: + tmp_value = (rsp->data[4])& 1; + printf("\n\tDHCP Discovery method\t: "); + printf("\n\t\tManagement Controller ID String is %s", + tmp_value ? "enabled" : "disabled"); + printf("\n\t\tVendor class identifier DCMI IANA and Vendor class-specific Informationa are %s", + ((rsp->data[4])& 2) ? "enabled" : "disabled" ); + break; + case 3: + printf("\n\tInitial timeout interval\t: %i seconds", + rsp->data[4]); + break; + case 4: + printf("\n\tServer contact timeout interval\t: %i seconds", + rsp->data[4] + (rsp->data[5]<<8)); + break; + case 5: + printf("\n\tServer contact retry interval\t: %i seconds", + rsp->data[4] + (rsp->data[5] << 8)); + break; + default: + printf("\n\tConfiguration Parameter not supported."); + } + } + return 0; +} + +/* This is Set DCMI Config Parameters Command + * + * returns ipmi response structure + * + * @intf: ipmi interface handler + */ +struct ipmi_rs * +ipmi_dcmi_setconfparam(struct ipmi_intf * intf, uint8_t param_selector, + uint16_t value) +{ + struct ipmi_rq req; + uint8_t msg_data[5]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = param_selector; /* Parameter selector */ + /* Set Selector (use 00h for parameters that only have one set). */ + msg_data[2] = 0x00; + + if (param_selector > 3) { + /* One bite more */ + msg_data[3] = value & 0xFF; + msg_data[4] = value >> 8; + } else { + msg_data[3] = value; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_SETCONFPARAM; /* Set DCMI Config Parameters */ + req.msg.data = msg_data; /* Contents above */ + if (param_selector > 3) { + /* One bite more */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 5; + } else { + /* how many times does req.msg.data need to read */ + req.msg.data_len = 4; + } + return intf->sendrecv(intf, &req); +} + +/* Power Management get limit ipmi response + * + * This function returns the currently set power management settings as an + * ipmi response structure. The reason it returns in the rsp struct is so + * that it can be used in the set limit [slimit()] function to populate + * un-changed or un-edited values. + * + * returns ipmi response structure + * + * @intf: ipmi interface handler + */ +struct ipmi_rs * ipmi_dcmi_pwr_glimit(struct ipmi_intf * intf) +{ + struct ipmi_rq req; + uint8_t msg_data[3]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = 0x00; /* reserved */ + msg_data[2] = 0x00; /* reserved */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_GETLMT; /* Get power limit */ + req.msg.data = msg_data; /* Contents above */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 3; + + return intf->sendrecv(intf, &req); +} +/* end Power Management get limit response */ + +/* Power Management print the get limit command + * + * This function calls the get limit function that returns an ipmi response. + * + * returns 0 else 1 with error + * @intf: ipmi interface handler + */ +static int +ipmi_dcmi_pwr_prnt_glimit(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct power_limit val; + uint8_t realCc = 0xff; + + rsp = ipmi_dcmi_pwr_glimit(intf); + /* rsp can be a null so check response before any operation + * on it to avoid sig segv + */ + if (rsp != NULL) { + realCc = rsp->ccode; + GOOD_PWR_GLIMIT_CCODE(rsp->ccode); + } + if (chk_rsp(rsp)) { + return -1; + } + /* rsp->data[0] is equal to response data byte 2 in spec */ + /* printf("Group Extension Identification: %02x\n", rsp->data[0]); */ + memcpy(&val, rsp->data, sizeof (val)); + printf("\n Current Limit State: %s\n", + (realCc == 0) ? + "Power Limit Active" : "No Active Power Limit"); + printf(" Exception actions: %s\n", + val2str2(val.action, dcmi_pwrmgmt_get_action_vals)); + printf(" Power Limit: %i Watts\n", val.limit); + printf(" Correction time: %i milliseconds\n", val.correction); + printf(" Sampling period: %i seconds\n", val.sample); + printf("\n"); + return 0; +} +/* end print get limit */ + +/* Power Management set limit + * + * Undocumented bounds: + * Power limit: 0 - 0xFFFF + * Correction period 5750ms to 28751ms or 0x1676 to 0x704F + * sample period: 3 sec to 65 sec and 69+ + * + * @intf: ipmi interface handler + * @option: Power option to change + * @value: Value of the desired change + */ +static int +ipmi_dcmi_pwr_slimit(struct ipmi_intf * intf, const char * option, + const char * value) +{ + struct ipmi_rs * rsp; /* ipmi response */ + struct ipmi_rq req; /* ipmi request (to send) */ + struct power_limit val; + uint8_t msg_data[15]; /* number of request data bytes */ + uint32_t lvalue = 0; + int i; + + rsp = ipmi_dcmi_pwr_glimit(intf); /* get the power limit settings */ +# if 0 + { + unsigned char counter = 0; + printf("DATA (%d): ", rsp->data_len); + for(counter = 0; counter < rsp->data_len; counter ++) { + printf("%02X ", rsp->data[counter]); + } + printf("\n"); + } +# endif + /* rsp can be a null so check response before any operation on it to + * avoid sig segv + */ + if (rsp != NULL) { + GOOD_PWR_GLIMIT_CCODE(rsp->ccode); + } + if (chk_rsp(rsp)) { + return -1; + } + memcpy(&val, rsp->data, sizeof (val)); + /* same as above; sets the values of the val struct + * DCMI group ID * + * val.grp_id = rsp->data[0]; + * exception action * + * val.action = rsp->data[3]; * + * + * power limit in Watts * + * store 16 bits of the rsp from the 4th entity * + * val.limit = *(uint16_t*)(&rsp->data[4]); + * correction period in mS * + * store 32 bits of the rsp from the 6th entity * + * val.correction = *(uint32_t*)(&rsp->data[6]); + * store 16 bits of the rsp from the 12th entity * + * sample period in seconds * + * val.sample = *(uint16_t*)(&rsp->data[12]); + */ + lprintf(LOG_INFO, + "DCMI IN Limit=%d Correction=%d Action=%d Sample=%d\n", + val.limit, val.correction, val.action, val.sample); + switch (str2val2(option, dcmi_pwrmgmt_set_usage_vals)) { + case 0x00: + /* action */ + switch (str2val2(value, dcmi_pwrmgmt_action_vals)) { + case 0x00: + /* no_action */ + val.action = 0; + break; + case 0x01: + /* power_off */ + val.action = 1; + break; + case 0x02: + /* OEM reserved action */ + val.action = 0x02; + break; + case 0x03: + /* OEM reserved action */ + val.action = 0x03; + break; + case 0x04: + /* OEM reserved action */ + val.action = 0x04; + break; + case 0x05: + /* OEM reserved action */ + val.action = 0x05; + break; + case 0x06: + /* OEM reserved action */ + val.action = 0x06; + break; + case 0x07: + /* OEM reserved action */ + val.action = 0x07; + break; + case 0x08: + /* OEM reserved action */ + val.action = 0x08; + break; + case 0x09: + /* OEM reserved action */ + val.action = 0x09; + break; + case 0x0a: + /* OEM reserved action */ + val.action = 0x0a; + break; + case 0x0b: + /* OEM reserved action */ + val.action = 0x0b; + break; + case 0x0c: + /* OEM reserved action */ + val.action = 0x0c; + break; + case 0x0d: + /* OEM reserved action */ + val.action = 0x0d; + break; + case 0x0e: + /* OEM reserved action */ + val.action = 0x0e; + break; + case 0x0f: + /* OEM reserved action */ + val.action = 0x0f; + break; + case 0x10: + /* OEM reserved action */ + val.action = 0x10; + break; + case 0x11: + /* sel_logging*/ + val.action = 0x11; + break; + case 0xFF: + /* error - not a string we knew what to do with */ + lprintf(LOG_ERR, "Given %s '%s' is invalid.", + option, value); + return -1; + } + break; + case 0x01: + /* limit */ + if (str2uint(value, &lvalue) != 0) { + lprintf(LOG_ERR, "Given %s '%s' is invalid.", + option, value); + return (-1); + } + val.limit = *(uint16_t*)(&lvalue); + break; + case 0x02: + /* correction */ + if (str2uint(value, &lvalue) != 0) { + lprintf(LOG_ERR, "Given %s '%s' is invalid.", + option, value); + return (-1); + } + val.correction = *(uint32_t*)(&lvalue); + break; + case 0x03: + /* sample */ + if (str2uint(value, &lvalue) != 0) { + lprintf(LOG_ERR, "Given %s '%s' is invalid.", + option, value); + return (-1); + } + val.sample = *(uint16_t*)(&lvalue); + break; + case 0xff: + /* no valid options */ + return -1; + } + lprintf(LOG_INFO, "DCMI OUT Limit=%d Correction=%d Action=%d Sample=%d\n", val.limit, val.correction, val.action, val.sample); + + msg_data[0] = val.grp_id; /* Group Extension Identification */ + msg_data[1] = 0x00; /* reserved */ + msg_data[2] = 0x00; /* reserved */ + msg_data[3] = 0x00; /* reserved */ + msg_data[4] = val.action; /* exception action; 0x00 disables it */ + + /* fill msg_data[5] with the first 16 bits of val.limit */ + *(uint16_t*)(&msg_data[5]) = val.limit; + /* msg_data[5] = 0xFF; + * msg_data[6] = 0xFF; + */ + /* fill msg_data[7] with the first 32 bits of val.correction */ + *(uint32_t*)(&msg_data[7]) = val.correction; + /* msg_data[7] = 0x76; + * msg_data[8] = 0x16; + * msg_data[9] = 0x00; + * msg_data[10] = 0x00; + */ + msg_data[11] = 0x00; /* reserved */ + msg_data[12] = 0x00; /* reserved */ + /* fill msg_data[7] with the first 16 bits of val.sample */ + *(uint16_t*)(&msg_data[13]) = val.sample; + /* msg_data[13] = 0x03; */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_SETLMT; /* Set power limit */ + req.msg.data = msg_data; /* Contents above */ + /* how many times does req.msg.data need to read */ + req.msg.data_len = 15; + + rsp = intf->sendrecv(intf, &req); + + if (chk_rsp(rsp)) { + return -1; + } + return 0; +} +/* end Power Management set limit */ + +/* Power Management activate deactivate + * + * @intf: ipmi interface handler + * @option: uint8_t - 0 to deactivate or 1 to activate + */ +static int +ipmi_dcmi_pwr_actdeact(struct ipmi_intf * intf, uint8_t option) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[4]; /* number of request data bytes */ + + msg_data[0] = IPMI_DCMI; /* Group Extension Identification */ + msg_data[1] = option; /* 0 = Deactivate 1 = Activate */ + msg_data[2] = 0x00; /* reserved */ + msg_data[3] = 0x00; /* reserved */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.cmd = IPMI_DCMI_PWRACT; /* Act-deactivate power limit */ + req.msg.data = msg_data; /* Contents above */ + req.msg.data_len = 4; /* how mant times does req.msg.data need to read */ + + rsp = intf->sendrecv(intf, &req); + if (chk_rsp(rsp)) { + return -1; + } + printf("\n Power limit successfully "); + if (option == 0x00) { + printf("deactivated"); + } else { + printf("activated"); + } + printf("\n"); + return 0; +} +/* end power management activate/deactivate */ + +/* main + * + * @intf: dcmi interface handler + * @argc: argument count + * @argv: argument vector + */ +int +ipmi_dcmi_main(struct ipmi_intf * intf, int argc, char **argv) +{ + int rc = 0; + uint8_t ctl = 0; + int i, ii, instances; + struct ipmi_rs *rsp; + + if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) { + print_strs(dcmi_cmd_vals, + "Data Center Management Interface commands", + -1, 0); + return -1; + } + /* start the cmd requested */ + switch (str2val2(argv[0], dcmi_cmd_vals)) { + case 0x00: + /* discover capabilities*/ + for (i = 1; dcmi_capable_vals[i-1].str != NULL; i++) { + if (ipmi_dcmi_prnt_getcapabilities(intf, i) < 0) { + printf("Error discovering %s capabilities!\n", + val2str2(i, dcmi_capable_vals)); + return -1; + } + } + break; + case 0x01: + /* power */ + argv++; + if (argv[0] == NULL) { + print_strs(dcmi_pwrmgmt_vals, "power <command>", + -1, 0); + return -1; + } + /* power management */ + switch (str2val2(argv[0], dcmi_pwrmgmt_vals)) { + case 0x00: + /* get reading */ + rc = ipmi_dcmi_pwr_rd(intf); + break; + case 0x01: + /* get limit */ + /* because the get limit function is also used to + * populate unchanged values for the set limit + * command it returns an ipmi response structure + */ + rc = ipmi_dcmi_pwr_prnt_glimit(intf); + break; + case 0x02: + /* set limit */ + if (argc < 4) { + print_strs(dcmi_pwrmgmt_set_usage_vals, + "set_limit <parameter> <value>", + -1, 0); + return -1; + } + if ( argc == 10) { + /* Let`s initialize dcmi power parameters */ + struct ipmi_rq req; + uint8_t data[256]; + uint16_t sample = 0; + uint16_t limit = 0; + uint32_t correction = 0; + + memset(data, 0, sizeof(data)); + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_DCGRP; + req.msg.lun = 0x00; + req.msg.cmd = IPMI_DCMI_SETLMT; /* Set power limit */ + req.msg.data = data; /* Contents above */ + req.msg.data_len = 15; + + data[0] = IPMI_DCMI; /* Group Extension Identification */ + data[1] = 0x0; /* reserved */ + data[2] = 0x0; /* reserved */ + data[3] = 0x0; /* reserved */ + + /* action */ + switch (str2val2(argv[2], dcmi_pwrmgmt_action_vals)) { + case 0x00: + /* no_action */ + data[4] = 0x00; + break; + case 0x01: + /* power_off */ + data[4] = 0x01; + break; + case 0x11: + /* sel_logging*/ + data[4] = 0x11; + break; + case 0xFF: + /* error - not a string we knew what to do with */ + lprintf(LOG_ERR, "Given Action '%s' is invalid.", + argv[2]); + return -1; + } + /* limit */ + if (str2ushort(argv[4], &limit) != 0) { + lprintf(LOG_ERR, + "Given Limit '%s' is invalid.", + argv[4]); + return (-1); + } + data[5] = limit >> 0; + data[6] = limit >> 8; + /* correction */ + if (str2uint(argv[6], &correction) != 0) { + lprintf(LOG_ERR, + "Given Correction '%s' is invalid.", + argv[6]); + return (-1); + } + data[7] = correction >> 0; + data[8] = correction >> 8; + data[9] = correction >> 16; + data[10] = correction >> 24; + data[11] = 0x00; /* reserved */ + data[12] = 0x00; /* reserved */ + /* sample */ + if (str2ushort(argv[8], &sample) != 0) { + lprintf(LOG_ERR, + "Given Sample '%s' is invalid.", + argv[8]); + return (-1); + } + data[13] = sample >> 0; + data[14] = sample >> 8; + + rsp = intf->sendrecv(intf, &req); + if (chk_rsp(rsp)) { + return -1; + } + } else { + /* loop through each parameter and value until we have neither */ + while ((argv[1] != NULL) && (argv[2] != NULL)) { + rc = ipmi_dcmi_pwr_slimit(intf, argv[1], argv[2]); + /* catch any error that the set limit function returned */ + if (rc > 0) { + print_strs(dcmi_pwrmgmt_set_usage_vals, + "set_limit <parameter> <value>", -1, 0); + return -1; + } + /* the first argument is the command and the second is the + * value. Move argv two places; what is now 3 will be 1 + */ + argv+=2; + } + } + rc = ipmi_dcmi_pwr_prnt_glimit(intf); + break; + case 0x03: + /* activate */ + rc = ipmi_dcmi_pwr_actdeact(intf, 1); + break; + case 0x04: + /* deactivate */ + rc = ipmi_dcmi_pwr_actdeact(intf, 0); + break; + default: + /* no valid options */ + print_strs(dcmi_pwrmgmt_vals, + "power <command>", -1, 0); + break; + } + /* power mgmt end */ + break; + /* end power command */ + case 0x02: + /* sensor print */ + /* Look for each item in the dcmi_discvry_snsr_vals struct + * and if it exists, print the sdr record id(s) for it. + * Use the val from each one as the sensor number. + */ + for (i = 0; dcmi_discvry_snsr_vals[i].str != NULL; i++) { + /* get all of the information about this sensor */ + rc = ipmi_dcmi_prnt_discvry_snsr(intf, + dcmi_discvry_snsr_vals[i].val); + } + break; + /* end sensor print */ + case 0x03: + /* asset tag */ + if(ipmi_dcmi_prnt_getassettag(intf) < 0) { + lprintf(LOG_ERR, "Error getting asset tag!"); + return -1; + } + break; + /* end asset tag */ + case 0x04: + { + /* set asset tag */ + if (argc == 1 ) { + print_strs(dcmi_cmd_vals, + "Data Center Management Interface commands", + -1, 0); + return -1; + } + if (ipmi_dcmi_prnt_setassettag(intf, argv[1]) < 0) { + lprintf(LOG_ERR, "\nError setting asset tag!"); + return -1; + } + break; + } + /* end set asset tag */ + case 0x05: + /* get management controller identifier string */ + if (ipmi_dcmi_prnt_getmngctrlids(intf) < 0) { + lprintf(LOG_ERR, + "Error getting management controller identifier string!"); + return -1; + } + break; + /* end get management controller identifier string */ + case 0x06: + { + /* set management controller identifier string */ + if (argc == 1 ) { + print_strs(dcmi_cmd_vals, + "Data Center Management Interface commands", + -1, 0); + return -1; + } + if (ipmi_dcmi_prnt_setmngctrlids(intf, argv[1]) < 0) { + lprintf(LOG_ERR, + "Error setting management controller identifier string!"); + return -1; + } + break; + } + /* end set management controller identifier string */ + case 0x07: + { + uint8_t entityID = 0; + uint8_t entityInst = 0; + uint8_t persistanceFlag; + uint8_t actionHardPowerOff; + uint8_t actionLogToSEL; + uint8_t tempLimit = 0; + uint8_t samplingTimeLSB; + uint8_t samplingTimeMSB; + uint16_t samplingTime = 0; + /* Thermal policy get/set */ + /* dcmitool dcmi thermalpolicy get */ + switch (str2val2(argv[1], dcmi_thermalpolicy_vals)) { + case 0x00: + if (argc < 4) { + lprintf(LOG_NOTICE, "Get <entityID> <instanceID>"); + return -1; + } + if (str2uchar(argv[2], &entityID) != 0) { + lprintf(LOG_ERR, + "Given Entity ID '%s' is invalid.", + argv[2]); + return (-1); + } + if (str2uchar(argv[3], &entityInst) != 0) { + lprintf(LOG_ERR, + "Given Instance ID '%s' is invalid.", + argv[3]); + return (-1); + } + rc = ipmi_dcmi_getthermalpolicy(intf, entityID, entityInst); + break; + case 0x01: + if (argc < 4) { + lprintf(LOG_NOTICE, "Set <entityID> <instanceID>"); + return -1; + } else if (argc < 9) { + print_strs(dcmi_thermalpolicy_set_parameters_vals, + "Set thermalpolicy instance parameters: " + "<volatile/nonvolatile/disabled> " + "<poweroff/nopoweroff/disabled> " + "<sel/nosel/disabled> <templimitByte> <exceptionTime>", + -1, 0); + return -1; + } + if (str2uchar(argv[2], &entityID) != 0) { + lprintf(LOG_ERR, + "Given Entity ID '%s' is invalid.", + argv[2]); + return (-1); + } + if (str2uchar(argv[3], &entityInst) != 0) { + lprintf(LOG_ERR, + "Given Instance ID '%s' is invalid.", + argv[3]); + return (-1); + } + persistanceFlag = (uint8_t) str2val2(argv[4], dcmi_thermalpolicy_set_parameters_vals); + actionHardPowerOff = (uint8_t) str2val2(argv[5], dcmi_thermalpolicy_set_parameters_vals); + actionLogToSEL = (uint8_t) str2val2(argv[6], dcmi_thermalpolicy_set_parameters_vals); + if (str2uchar(argv[7], &tempLimit) != 0) { + lprintf(LOG_ERR, + "Given Temp Limit '%s' is invalid.", + argv[7]); + return (-1); + } + if (str2ushort(argv[8], &samplingTime) != 0) { + lprintf(LOG_ERR, + "Given Sampling Time '%s' is invalid.", + argv[8]); + return (-1); + } + samplingTimeLSB = (samplingTime & 0xFF); + samplingTimeMSB = ((samplingTime & 0xFF00) >> 8); + + rc = ipmi_dcmi_setthermalpolicy(intf, + entityID, + entityInst, + persistanceFlag, + actionHardPowerOff, + actionLogToSEL, + tempLimit, + samplingTimeLSB, + samplingTimeMSB); + + break; + default: + print_strs(dcmi_thermalpolicy_vals, + "thermalpolicy <command>", + -1, 0); + return -1; + } + break; + } + case 0x08: + if(ipmi_dcmi_prnt_get_temp_readings(intf) < 0 ) { + lprintf(LOG_ERR, + "Error get temperature readings!"); + } + break; + case 0x09: + if(ipmi_dcmi_prnt_getconfparam(intf) < 0 ) { + lprintf(LOG_ERR, + "Error Get DCMI Configuration Parameters!"); + }; + break; + case 0x0A: + { + switch (argc) { + case 2: + if (strncmp(argv[1], "activate_dhcp", 13) != 0) { + print_strs( dcmi_conf_param_vals, + "DCMI Configuration Parameters", + -1, 0); + return -1; + } + break; + default: + if (argc != 3 || strncmp(argv[1], "help", 4) == 0) { + print_strs(dcmi_conf_param_vals, + "DCMI Configuration Parameters", + -1, 0); + return -1; + } + } + if (strncmp(argv[1], "activate_dhcp", 13) == 0) { + rsp = ipmi_dcmi_setconfparam(intf, 1, 1); + } else { + uint16_t tmp_val = 0; + if (str2ushort(argv[2], &tmp_val) != 0) { + lprintf(LOG_ERR, + "Given %s '%s' is invalid.", + argv[1], argv[2]); + return (-1); + } + rsp = ipmi_dcmi_setconfparam(intf, + str2val2(argv[1], dcmi_conf_param_vals), + tmp_val); + } + if (chk_rsp(rsp)) { + lprintf(LOG_ERR, + "Error Set DCMI Configuration Parameters!"); + } + break; + } + case 0x0B: + { + if (intf->session == NULL) { + lprintf(LOG_ERR, + "\nOOB discovery is available only via RMCP interface."); + return -1; + } + if(ipmi_dcmi_prnt_oobDiscover(intf) < 0) { + lprintf(LOG_ERR, "\nOOB discovering capabilities failed."); + return -1; + } + break; + } + default: + /* couldn't detect what the user entered */ + print_strs(dcmi_cmd_vals, + "Data Center Management Interface commands", + -1, 0); + return -1; + break; + } + printf("\n"); + return 0; +} + +/* Display DCMI sensor information + * Uses the ipmi_sdr_get_next_header to read SDR header and compare to the + * target Record ID. Then either ipmi_sensor_print_full or + * ipmi_sensor_print_compact is called to print the data + * + * @intf: ipmi interface handler + * @rec_id: target Record ID + */ +static int +ipmi_print_sensor_info(struct ipmi_intf *intf, uint16_t rec_id) +{ + struct sdr_get_rs *header; + struct ipmi_sdr_iterator *itr; + int rc = 0; + uint8_t *rec = NULL; + + itr = ipmi_sdr_start(intf, 0); + if (itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return (-1); + } + + while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) { + if (header->id == rec_id) { + break; + } + } + if (header == NULL) { + lprintf(LOG_DEBUG, "header == NULL"); + ipmi_sdr_end(intf, itr); + return (-1); + } + /* yes, we found the SDR for this record ID, now get full record */ + rec = ipmi_sdr_get_record(intf, header, itr); + if (rec == NULL) { + lprintf(LOG_DEBUG, "rec == NULL"); + ipmi_sdr_end(intf, itr); + return (-1); + } + if ((header->type == SDR_RECORD_TYPE_FULL_SENSOR) || + (header->type == SDR_RECORD_TYPE_COMPACT_SENSOR)) { + rc = ipmi_sdr_print_rawentry(intf, header->type, + rec, header->length); + } else { + rc = (-1); + } + free(rec); + rec = NULL; + ipmi_sdr_end(intf, itr); + return rc; +} diff --git a/lib/ipmi_delloem.c b/lib/ipmi_delloem.c new file mode 100644 index 0000000..e190cd4 --- /dev/null +++ b/lib/ipmi_delloem.c @@ -0,0 +1,4225 @@ +/* + * Copyright (c) 2008, Dell Inc + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Dell Inc nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Thursday Oct 7 17:30:12 2009 + * <deepaganesh_paulraj@dell.com> + * + * This code implements a dell OEM proprietary commands. + * This Code is edited and Implemented the License feature for Delloem + * Author Harsha S <Harsha_S1@dell.com> + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> +#include <limits.h> +#include <time.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_delloem.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_sensor.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/bswap.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_entity.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_sensor.h> + +#define DELL_OEM_NETFN (uint8_t)(0x30) +#define GET_IDRAC_VIRTUAL_MAC (uint8_t)(0xC9) +/* 11g Support Macros */ +#define INVALID (-1) +#define SHARED 0 +#define SHARED_WITH_FAILOVER_LOM2 1 +#define DEDICATED 2 +#define SHARED_WITH_FAILOVER_ALL_LOMS 3 +/* 11g Support Macros */ +#define SHARED 0 +#define SHARED_WITH_FAILOVER_LOM2 1 +#define DEDICATED 2 +#define SHARED_WITH_FAILOVER_ALL_LOMS 3 +/* 12g Support Strings for nic selection */ +#define INVAILD_FAILOVER_MODE -2 +#define INVAILD_FAILOVER_MODE_SETTINGS -3 +#define INVAILD_SHARED_MODE -4 + +#define INVAILD_FAILOVER_MODE_STRING "ERROR: Cannot set shared with failover lom same as current shared lom." +#define INVAILD_FAILOVER_MODE_SET "ERROR: Cannot set shared with failover loms when NIC is set to dedicated Mode." +#define INVAILD_SHARED_MODE_SET_STRING "ERROR: Cannot set shared Mode for Blades." + +char AciveLOM_String [6] [10] = { + "None", + "LOM1", + "LOM2", + "LOM3", + "LOM4", + "dedicated" +}; +/* 11g Support Strings for nic selection */ +char NIC_Selection_Mode_String [4] [50] = { + "shared", + "shared with failover lom2", + "dedicated", + "shared with Failover all loms" +}; + +char NIC_Selection_Mode_String_12g[] [50] = { + "dedicated", + "shared with lom1", + "shared with lom2", + "shared with lom3", + "shared with lom4", + "shared with failover lom1", + "shared with failover lom2", + "shared with failover lom3", + "shared with failover lom4", + "shared with failover all loms" +}; + +const struct vFlashstr vFlash_completion_code_vals[] = { + {0x00, "SUCCESS"}, + {0x01, "NO_SD_CARD"}, + {0x63, "UNKNOWN_ERROR"}, + {0x00, NULL} +}; + +static int current_arg =0; +uint8_t iDRAC_FLAG=0; +LCD_MODE lcd_mode; +static uint8_t LcdSupported=0; +static uint8_t SetLEDSupported=0; + +volatile uint8_t IMC_Type = IMC_IDRAC_10G; + +POWER_HEADROOM powerheadroom; + +uint8_t PowercapSetable_flag=0; +uint8_t PowercapstatusFlag=0; + +static void usage(void); +/* LCD Function prototypes */ +static int ipmi_delloem_lcd_main(struct ipmi_intf *intf, int argc, + char **argv); +int ipmi_lcd_get_platform_model_name(struct ipmi_intf *intf, char *lcdstring, + uint8_t max_length, uint8_t field_type); +static int ipmi_idracvalidator_command(struct ipmi_intf *intf); +static int ipmi_lcd_get_configure_command_wh(struct ipmi_intf *intf); +static int ipmi_lcd_get_configure_command(struct ipmi_intf *intf, + uint8_t *command); +static int ipmi_lcd_set_configure_command(struct ipmi_intf *intf, int command); +static int ipmi_lcd_set_configure_command_wh(struct ipmi_intf *intf, uint32_t mode, + uint16_t lcdquallifier,uint8_t errordisp); +static int ipmi_lcd_get_single_line_text(struct ipmi_intf *intf, + char *lcdstring, uint8_t max_length); +static int ipmi_lcd_get_info_wh(struct ipmi_intf *intf); +static int ipmi_lcd_get_info(struct ipmi_intf *intf); +static int ipmi_lcd_get_status_val(struct ipmi_intf *intf, + LCD_STATUS *lcdstatus); +static int IsLCDSupported(); +static void CheckLCDSupport(struct ipmi_intf *intf); +static void ipmi_lcd_status_print(LCD_STATUS lcdstatus); +static int ipmi_lcd_get_status(struct ipmi_intf *intf); +static int ipmi_lcd_set_kvm(struct ipmi_intf *intf, char status); +static int ipmi_lcd_set_lock(struct ipmi_intf *intf, char lock); +static int ipmi_lcd_set_single_line_text(struct ipmi_intf *intf, char *text); +static int ipmi_lcd_set_text(struct ipmi_intf *intf, char *text, + int line_number); +static int ipmi_lcd_configure_wh(struct ipmi_intf *intf, uint32_t mode, + uint16_t lcdquallifier, uint8_t errordisp, int8_t line_number, char *text); +static int ipmi_lcd_configure(struct ipmi_intf *intf, int command, + int8_t line_number, char *text); +static void ipmi_lcd_usage(void); +/* MAC Function prototypes */ +static int ipmi_delloem_mac_main(struct ipmi_intf *intf, int argc, char **argv); +static void InitEmbeddedNICMacAddressValues(); +static int ipmi_macinfo_drac_idrac_virtual_mac(struct ipmi_intf *intf, + uint8_t NicNum); +static int ipmi_macinfo_drac_idrac_mac(struct ipmi_intf *intf,uint8_t NicNum); +static int ipmi_macinfo_10g(struct ipmi_intf *intf, uint8_t NicNum); +static int ipmi_macinfo_11g(struct ipmi_intf *intf, uint8_t NicNum); +static int ipmi_macinfo(struct ipmi_intf *intf, uint8_t NicNum); +static void ipmi_mac_usage(void); +/* LAN Function prototypes */ +static int ipmi_delloem_lan_main(struct ipmi_intf *intf, int argc, char **argv); +static int IsLANSupported(); +static int get_nic_selection_mode(int current_arg, char **argv); +static int ipmi_lan_set_nic_selection(struct ipmi_intf *intf, + uint8_t nic_selection); +static int ipmi_lan_get_nic_selection(struct ipmi_intf *intf); +static int ipmi_lan_get_active_nic(struct ipmi_intf *intf); +static void ipmi_lan_usage(void); +static int ipmi_lan_set_nic_selection_12g(struct ipmi_intf *intf, + uint8_t *nic_selection); +/* Power monitor Function prototypes */ +static int ipmi_delloem_powermonitor_main(struct ipmi_intf *intf, int argc, + char **argv); +static void ipmi_time_to_str(time_t rawTime, char *strTime); +static int ipmi_get_sensor_reading(struct ipmi_intf *intf, + unsigned char sensorNumber, SensorReadingType *pSensorReadingData); +static int ipmi_get_power_capstatus_command(struct ipmi_intf *intf); +static int ipmi_set_power_capstatus_command(struct ipmi_intf *intf, + uint8_t val); +static int ipmi_powermgmt(struct ipmi_intf *intf); +static int ipmi_powermgmt_clear(struct ipmi_intf *intf, uint8_t clearValue); +static uint64_t watt_to_btuphr_conversion(uint32_t powerinwatt); +static uint32_t btuphr_to_watt_conversion(uint64_t powerinbtuphr); +static int ipmi_get_power_headroom_command(struct ipmi_intf *intf, uint8_t unit); +static int ipmi_get_power_consumption_data(struct ipmi_intf *intf, uint8_t unit); +static int ipmi_get_instan_power_consmpt_data(struct ipmi_intf *intf, + IPMI_INST_POWER_CONSUMPTION_DATA *instpowerconsumptiondata); +static void ipmi_print_get_instan_power_Amps_data( + IPMI_INST_POWER_CONSUMPTION_DATA instpowerconsumptiondata); +static int ipmi_print_get_power_consmpt_data(struct ipmi_intf *intf, + uint8_t unit); +static int ipmi_get_avgpower_consmpt_history(struct ipmi_intf *intf, + IPMI_AVGPOWER_CONSUMP_HISTORY *pavgpower); +static int ipmi_get_peakpower_consmpt_history(struct ipmi_intf *intf, + IPMI_POWER_CONSUMP_HISTORY *pstPeakpower); +static int ipmi_get_minpower_consmpt_history(struct ipmi_intf *intf, + IPMI_POWER_CONSUMP_HISTORY *pstMinpower); +static int ipmi_print_power_consmpt_history(struct ipmi_intf *intf, int unit); +static int ipmi_get_power_cap(struct ipmi_intf *intf, + IPMI_POWER_CAP *ipmipowercap); +static int ipmi_print_power_cap(struct ipmi_intf *intf, uint8_t unit); +static int ipmi_set_power_cap(struct ipmi_intf *intf, int unit, int val); +static void ipmi_powermonitor_usage(void); +/* vFlash Function prototypes */ +static int ipmi_delloem_vFlash_main(struct ipmi_intf *intf, int argc, + char **argv); +const char *get_vFlash_compcode_str(uint8_t vflashcompcode, + const struct vFlashstr *vs); +static int ipmi_get_sd_card_info(struct ipmi_intf *intf); +static int ipmi_delloem_vFlash_process(struct ipmi_intf *intf, int current_arg, + char **argv); +static void ipmi_vFlash_usage(void); +/* LED Function prototypes */ +static int ipmi_getsesmask(int, char **argv); +static void CheckSetLEDSupport(struct ipmi_intf *intf); +static int IsSetLEDSupported(void); +static void ipmi_setled_usage(void); +static int ipmi_delloem_setled_main(struct ipmi_intf *intf, int argc, + char **argv); +static int ipmi_setled_state(struct ipmi_intf *intf, int bayId, int slotId, + int state); +static int ipmi_getdrivemap(struct ipmi_intf *intf, int b, int d, int f, + int *bayId, int *slotId); + +/* Function Name: ipmi_delloem_main + * + * Description: This function processes the delloem command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +int +ipmi_delloem_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + current_arg = 0; + if (argc == 0 || strncmp(argv[0], "help\0", 5) == 0) { + usage(); + return 0; + } + if (0 ==strncmp(argv[current_arg], "lcd\0", 4)) { + ipmi_delloem_lcd_main(intf,argc,argv); + } else if (strncmp(argv[current_arg], "mac\0", 4) == 0) { + /* mac address*/ + ipmi_delloem_mac_main(intf,argc,argv); + } else if (strncmp(argv[current_arg], "lan\0", 4) == 0) { + /* lan address*/ + ipmi_delloem_lan_main(intf,argc,argv); + } else if (strncmp(argv[current_arg], "setled\0", 7) == 0) { + /* SetLED support */ + ipmi_delloem_setled_main(intf,argc,argv); + } else if (strncmp(argv[current_arg], "powermonitor\0", 13) == 0) { + /*Powermanagement report processing*/ + ipmi_delloem_powermonitor_main(intf,argc,argv); + } else if (strncmp(argv[current_arg], "vFlash\0", 7) == 0) { + /* vFlash Support */ + ipmi_delloem_vFlash_main(intf,argc,argv); + } else { + usage(); + return -1; + } + return rc; +} +/* + * Function Name: usage + * + * Description: This function prints help message for delloem command + * Input: + * Output: + * + * Return: + * + */ +static void +usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"usage: delloem <command> [option...]"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"commands:"); + lprintf(LOG_NOTICE, +" lcd"); + lprintf(LOG_NOTICE, +" mac"); + lprintf(LOG_NOTICE, +" lan"); + lprintf(LOG_NOTICE, +" setled"); + lprintf(LOG_NOTICE, +" powermonitor"); + lprintf(LOG_NOTICE, +" vFlash"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"For help on individual commands type:"); + lprintf(LOG_NOTICE, +"delloem <command> help"); +} +/* + * Function Name: ipmi_delloem_lcd_main + * + * Description: This function processes the delloem lcd command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + * + */ +static int +ipmi_delloem_lcd_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + current_arg++; + if (argc < current_arg) { + usage(); + return -1; + } + /* ipmitool delloem lcd info*/ + if (argc == 1 || strcmp(argv[current_arg], "help") == 0) { + ipmi_lcd_usage(); + return 0; + } + CheckLCDSupport(intf); + ipmi_idracvalidator_command(intf); + if (!IsLCDSupported()) { + lprintf(LOG_ERR, "lcd is not supported on this system."); + return -1; + } else if (strncmp(argv[current_arg], "info\0", 5) == 0) { + if ((iDRAC_FLAG==IDRAC_11G) || (iDRAC_FLAG==IDRAC_12G)) { + rc = ipmi_lcd_get_info_wh(intf); + } else { + rc = ipmi_lcd_get_info(intf); + } + } else if (strncmp(argv[current_arg], "status\0", 7) == 0) { + rc = ipmi_lcd_get_status(intf); + } else if (strncmp(argv[current_arg], "set\0", 4) == 0) { + /* ipmitool delloem lcd set*/ + uint8_t line_number = 0; + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "line\0", 5) == 0) { + current_arg++; + if (argc <= current_arg) { + usage(); + return -1; + } + if (str2uchar(argv[current_arg], &line_number) != 0) { + lprintf(LOG_ERR, + "Argument '%s' is either not a number or out of range.", + argv[current_arg]); + return (-1); + } + current_arg++; + if (argc <= current_arg) { + usage(); + return -1; + } + } + if ((strncmp(argv[current_arg], "mode\0", 5) == 0) + && ((iDRAC_FLAG==IDRAC_11G) || (iDRAC_FLAG==IDRAC_12G))) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (argv[current_arg] == NULL) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "none\0", 5) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_CONFIG_NONE, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "modelname\0", 10) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_CONFIG_DEFAULT, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "userdefined\0", 12) == 0) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_CONFIG_USER_DEFINED, + 0xFF, 0XFF, line_number, argv[current_arg]); + } else if (strncmp(argv[current_arg], "ipv4address\0", 12) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_iDRAC_IPV4ADRESS, + 0xFF, 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "macaddress\0", 11) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_IDRAC_MAC_ADDRESS, + 0xFF, 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "systemname\0", 11) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_OS_SYSTEM_NAME, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "servicetag\0", 11) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_SERVICE_TAG, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "ipv6address\0", 12) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_iDRAC_IPV6ADRESS, + 0xFF, 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "ambienttemp\0", 12) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_AMBEINT_TEMP, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "systemwatt\0", 11) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_SYSTEM_WATTS, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "assettag\0", 9) == 0) { + rc = ipmi_lcd_configure_wh(intf, IPMI_DELL_LCD_ASSET_TAG, 0xFF, + 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "help\0", 5) == 0) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + } + } else if ((strncmp(argv[current_arg], "lcdqualifier\0", 13) == 0) + && ((iDRAC_FLAG==IDRAC_11G) || (iDRAC_FLAG==IDRAC_12G))) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (argv[current_arg] == NULL) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "watt\0", 5) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0x00, 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "btuphr\0",7) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0x01, 0XFF, 0, NULL); + } else if (strncmp(argv[current_arg], "celsius\0", 8) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0x02, 0xFF, 0, NULL); + } else if (strncmp(argv[current_arg], "fahrenheit", 11) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0x03, 0xFF, 0, NULL); + } else if (strncmp(argv[current_arg], "help\0", 5) == 0) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + } + } else if ((strncmp(argv[current_arg], "errordisplay\0", 13) == 0) + && ((iDRAC_FLAG==IDRAC_11G) || (iDRAC_FLAG==IDRAC_12G))) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (argv[current_arg] == NULL) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "sel\0", 4) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0xFF, + IPMI_DELL_LCD_ERROR_DISP_SEL, 0, NULL); + } else if (strncmp(argv[current_arg], "simple\0", 7) == 0) { + rc = ipmi_lcd_configure_wh(intf, 0xFF, 0xFF, + IPMI_DELL_LCD_ERROR_DISP_VERBOSE, 0, NULL); + } else if (strncmp(argv[current_arg], "help\0", 5) == 0) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + } + } else if ((strncmp(argv[current_arg], "none\0", 5) == 0) + && (iDRAC_FLAG==0)) { + rc = ipmi_lcd_configure(intf, IPMI_DELL_LCD_CONFIG_NONE, 0, NULL); + } else if ((strncmp(argv[current_arg], "default\0", 8) == 0) + && (iDRAC_FLAG==0)) { + rc = ipmi_lcd_configure(intf, IPMI_DELL_LCD_CONFIG_DEFAULT, 0, NULL); + } else if ((strncmp(argv[current_arg], "custom\0", 7) == 0) + && (iDRAC_FLAG==0)) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + rc = ipmi_lcd_configure(intf, IPMI_DELL_LCD_CONFIG_USER_DEFINED, + line_number, argv[current_arg]); + } else if (strncmp(argv[current_arg], "vkvm\0", 5) == 0) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "active\0", 7) == 0) { + rc = ipmi_lcd_set_kvm(intf, 1); + } else if (strncmp(argv[current_arg], "inactive\0", 9) == 0) { + rc = ipmi_lcd_set_kvm(intf, 0); + } else if (strncmp(argv[current_arg], "help\0", 5) == 0) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + } + } else if (strncmp(argv[current_arg], "frontpanelaccess\0", 17) == 0) { + current_arg++; + if (argc <= current_arg) { + ipmi_lcd_usage(); + return -1; + } + if (strncmp(argv[current_arg], "viewandmodify\0", 14) == 0) { + rc = ipmi_lcd_set_lock(intf, 0); + } else if (strncmp(argv[current_arg], "viewonly\0", 9)==0) { + rc = ipmi_lcd_set_lock(intf, 1); + } else if (strncmp(argv[current_arg], "disabled\0", 9)==0) { + rc = ipmi_lcd_set_lock(intf, 2); + } else if (strncmp(argv[current_arg], "help\0", 5) == 0) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + } + } else if( (strncmp(argv[current_arg], "help\0", 5) == 0) + && (iDRAC_FLAG==0)) { + ipmi_lcd_usage(); + } else { + ipmi_lcd_usage(); + return -1; + } + } else { + ipmi_lcd_usage(); + return -1; + } + return rc; +} +/* ipmi_lcd_get_platform_model_name - This function retrieves the platform model + * name, or any other parameter which stores data in the same format + * + * @intf: pointer to interface + * @lcdstring: hostname/platform model string(output) + * @max_length: length of the platform model string + * @field_type: either hostname/platform model + * + * returns: 0 => success, other value means error + */ +int +ipmi_lcd_get_platform_model_name(struct ipmi_intf * intf, char* lcdstring, + uint8_t max_length, uint8_t field_type) +{ + uint8_t data[4]; + int bytes_copied = 0; + int ii = 0; + int lcdstring_len = 0; + int rc = 0; + IPMI_DELL_LCD_STRING lcdstringblock; + + for (ii = 0; ii < 4; ii++) { + int bytes_to_copy; + rc = ipmi_mc_getsysinfo(intf, field_type, ii, 0, sizeof(lcdstringblock), + &lcdstringblock); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting platform model name"); + break; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting platform model name: %s", + val2str(rc, completion_code_vals)); + break; + } + /* first block is different - 14 bytes*/ + if (ii == 0) { + lcdstring_len = lcdstringblock.lcd_string.selector_0_string.length; + lcdstring_len = MIN(lcdstring_len,max_length); + bytes_to_copy = MIN(lcdstring_len, IPMI_DELL_LCD_STRING1_SIZE); + memcpy(lcdstring, lcdstringblock.lcd_string.selector_0_string.data, + bytes_to_copy); + } else { + int string_offset; + bytes_to_copy = MIN(lcdstring_len - bytes_copied, + IPMI_DELL_LCD_STRINGN_SIZE); + if (bytes_to_copy < 1) { + break; + } + string_offset = IPMI_DELL_LCD_STRING1_SIZE + IPMI_DELL_LCD_STRINGN_SIZE + * (ii-1); + memcpy(lcdstring + string_offset, + lcdstringblock.lcd_string.selector_n_data, bytes_to_copy); + } + bytes_copied += bytes_to_copy; + if (bytes_copied >= lcdstring_len) { + break; + } + } + return rc; +} +/* + * Function Name: ipmi_idracvalidator_command + * + * Description: This function returns the iDRAC6 type + * Input: intf - ipmi interface + * Output: + * + * Return: iDRAC6 type 1 - whoville + * 0 - others + */ +static int +ipmi_idracvalidator_command(struct ipmi_intf * intf) +{ + int rc; + uint8_t data[11]; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_IDRAC_VALIDATOR, 2, 0, sizeof(data), + data); + if (rc < 0) { + /*lprintf(LOG_ERR, " Error getting IMC type"); */ + return -1; + } else if (rc > 0) { + /*lprintf(LOG_ERR, " Error getting IMC type: %s", + val2str(rsp->ccode, completion_code_vals)); */ + return -1; + } + /* Support the 11G Monolithic, modular, Maisy and Coaster */ + if ((IMC_IDRAC_11G_MONOLITHIC == data[10]) + || (IMC_IDRAC_11G_MODULAR == data[10]) + || (IMC_MASER_LITE_BMC == data[10]) + || (IMC_MASER_LITE_NU == data[10])) { + iDRAC_FLAG=IDRAC_11G; + } else if((IMC_IDRAC_12G_MONOLITHIC == data[10]) + || (IMC_IDRAC_12G_MODULAR == data[10])) { + iDRAC_FLAG = IDRAC_12G; + } else { + iDRAC_FLAG = 0; + } + IMC_Type = data[10]; + return 0; +} +/* + * Function Name: ipmi_lcd_get_configure_command_wh + * + * Description: This function returns current lcd configuration for Dell OEM LCD command + * Input: intf - ipmi interface + * Global: lcd_mode - lcd mode setting + * Output: + * + * Return: returns the current lcd configuration + * 0 = User defined + * 1 = Default + * 2 = None + */ +static int +ipmi_lcd_get_configure_command_wh(struct ipmi_intf * intf) +{ + uint8_t data[4]; + int rc; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_CONFIG_SELECTOR, 0, 0, + sizeof(lcd_mode), &lcd_mode); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD configuration"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)){ + lprintf(LOG_ERR, "Error getting LCD configuration: " + "Command not supported on this system."); + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD configuration: %s", + val2str(rc, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_lcd_get_configure_command + * + * Description: This function returns current lcd configuration for Dell OEM + * LCD command + * Input: intf - ipmi interface + * Output: command - user defined / default / none / ipv4 / mac address / + * system name / service tag / ipv6 / temp / system watt / asset tag + * + * Return: + */ +static int +ipmi_lcd_get_configure_command(struct ipmi_intf * intf, uint8_t *command) +{ + uint8_t data[4]; + int rc; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_CONFIG_SELECTOR, 0, 0, + sizeof(data), data); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD configuration"); + return -1; + } else if ((rc == 0xc1)||(rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD configuration: " + "Command not supported on this system."); + return -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD configuration: %s", + val2str(rc, completion_code_vals)); + return -1; + } + /* rsp->data[0] is the rev */ + *command = data[1]; + return 0; +} +/* + * Function Name: ipmi_lcd_set_configure_command + * + * Description: This function updates current lcd configuration + * Input: intf - ipmi interface + * command - user defined / default / none / ipv4 / mac address / + * system name / service tag / ipv6 / temp / system watt / asset tag + * Output: + * Return: + */ +static int +ipmi_lcd_set_configure_command(struct ipmi_intf * intf, int command) +{ + #define LSCC_DATA_LEN 2 + uint8_t data[2]; + int rc; + data[0] = IPMI_DELL_LCD_CONFIG_SELECTOR; + data[1] = command; /* command - custom, default, none */ + rc = ipmi_mc_setsysinfo(intf, 2, data); + if (rc < 0) { + lprintf(LOG_ERR, "Error setting LCD configuration"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error setting LCD configuration: " + "Command not supported on this system."); + } else if (rc > 0) { + lprintf(LOG_ERR, "Error setting LCD configuration: %s", + val2str(rc, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_lcd_set_configure_command + * + * Description: This function updates current lcd configuration + * Input: intf - ipmi interface + * mode - user defined / default / none + * lcdquallifier - lcd quallifier id + * errordisp - error number + * Output: + * Return: + */ +static int +ipmi_lcd_set_configure_command_wh(struct ipmi_intf * intf, uint32_t mode, + uint16_t lcdquallifier, uint8_t errordisp) +{ + #define LSCC_DATA_LEN 2 + uint8_t data[13]; + int rc; + ipmi_lcd_get_configure_command_wh(intf); + data[0] = IPMI_DELL_LCD_CONFIG_SELECTOR; + if (mode != 0xFF) { + data[1] = mode & 0xFF; /* command - custom, default, none*/ + data[2] = (mode & 0xFF00) >> 8; + data[3] = (mode & 0xFF0000) >> 16; + data[4] = (mode & 0xFF000000) >> 24; + } else { + data[1] = (lcd_mode.lcdmode) & 0xFF; /* command - custom, default, none*/ + data[2] = ((lcd_mode.lcdmode) & 0xFF00) >> 8; + data[3] = ((lcd_mode.lcdmode) & 0xFF0000) >> 16; + data[4] = ((lcd_mode.lcdmode) & 0xFF000000) >> 24; + } + if (lcdquallifier != 0xFF) { + if(lcdquallifier == 0x01) { + data[5] = (lcd_mode.lcdquallifier) | 0x01; /* command - custom, default, none*/ + } else if (lcdquallifier == 0x00) { + data[5] = (lcd_mode.lcdquallifier) & 0xFE; /* command - custom, default, none*/ + } else if (lcdquallifier == 0x03) { + data[5] = (lcd_mode.lcdquallifier) | 0x02; /* command - custom, default, none*/ + } else if (lcdquallifier == 0x02) { + data[5] = (lcd_mode.lcdquallifier) & 0xFD; + } + } else { + data[5] = lcd_mode.lcdquallifier; + } + if (errordisp != 0xFF) { + data[11] = errordisp; + } else { + data[11] = lcd_mode.error_display; + } + rc = ipmi_mc_setsysinfo(intf, 13, data); + if (rc < 0) { + lprintf(LOG_ERR, "Error setting LCD configuration"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error setting LCD configuration: " + "Command not supported on this system."); + } else if (rc > 0) { + lprintf(LOG_ERR, "Error setting LCD configuration: %s", + val2str(rc, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_lcd_get_single_line_text + * + * Description: This function updates current lcd configuration + * Input: intf - ipmi interface + * lcdstring - new string to be updated + * max_length - length of the string + * Output: + * Return: + */ +static int +ipmi_lcd_get_single_line_text(struct ipmi_intf * intf, char* lcdstring, + uint8_t max_length) +{ + IPMI_DELL_LCD_STRING lcdstringblock; + int lcdstring_len = 0; + int bytes_copied = 0; + int ii, rc; + for (ii = 0; ii < 4; ii++) { + int bytes_to_copy; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_STRING_SELECTOR, ii, 0, + sizeof(lcdstringblock), &lcdstringblock); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting text data"); + return -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting text data: %s", + val2str(rc, completion_code_vals)); + return -1; + } + /* first block is different - 14 bytes*/ + if (0 == ii) { + lcdstring_len = lcdstringblock.lcd_string.selector_0_string.length; + if (lcdstring_len < 1 || lcdstring_len > max_length) { + break; + } + bytes_to_copy = MIN(lcdstring_len, IPMI_DELL_LCD_STRING1_SIZE); + memcpy(lcdstring, lcdstringblock.lcd_string.selector_0_string.data, + bytes_to_copy); + } else { + int string_offset; + bytes_to_copy = MIN(lcdstring_len - bytes_copied, + IPMI_DELL_LCD_STRINGN_SIZE); + if (bytes_to_copy < 1) { + break; + } + string_offset = IPMI_DELL_LCD_STRING1_SIZE + IPMI_DELL_LCD_STRINGN_SIZE + * (ii-1); + memcpy(lcdstring+string_offset, + lcdstringblock.lcd_string.selector_n_data, bytes_to_copy); + } + bytes_copied += bytes_to_copy; + if (bytes_copied >= lcdstring_len) { + break; + } + } + return 0; +} +/* + * Function Name: ipmi_lcd_get_info_wh + * + * Description: This function prints current lcd configuration for whoville platform + * Input: intf - ipmi interface + * Output: + * Return: + */ +static int +ipmi_lcd_get_info_wh(struct ipmi_intf * intf) +{ + IPMI_DELL_LCD_CAPS lcd_caps; + char lcdstring[IPMI_DELL_LCD_STRING_LENGTH_MAX+1] = {0}; + int rc; + printf("LCD info\n"); + if (ipmi_lcd_get_configure_command_wh(intf) != 0) { + return -1; + } + if (lcd_mode.lcdmode== IPMI_DELL_LCD_CONFIG_DEFAULT) { + char text[IPMI_DELL_LCD_STRING_LENGTH_MAX+1] = {0}; + if (ipmi_lcd_get_platform_model_name(intf, text, + IPMI_DELL_LCD_STRING_LENGTH_MAX, + IPMI_DELL_PLATFORM_MODEL_NAME_SELECTOR) != 0) { + return (-1); + } + printf(" Setting:Model name\n"); + printf(" Line 1: %s\n", text); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_CONFIG_NONE) { + printf(" Setting: none\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_CONFIG_USER_DEFINED) { + printf(" Setting: User defined\n"); + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_GET_CAPS_SELECTOR, 0, 0, + sizeof(lcd_caps), &lcd_caps); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities."); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD capabilities: " + "Command not supported on this system."); + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (lcd_caps.number_lines > 0) { + memset(lcdstring, 0, IPMI_DELL_LCD_STRING_LENGTH_MAX + 1); + rc = ipmi_lcd_get_single_line_text(intf, lcdstring, + lcd_caps.max_chars[0]); + printf(" Text: %s\n", lcdstring); + } else { + printf(" No lines to show\n"); + } + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_iDRAC_IPV4ADRESS) { + printf(" Setting: IPV4 Address\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_IDRAC_MAC_ADDRESS) { + printf(" Setting: MAC Address\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_OS_SYSTEM_NAME) { + printf(" Setting: OS System Name\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_SERVICE_TAG) { + printf(" Setting: System Tag\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_iDRAC_IPV6ADRESS) { + printf(" Setting: IPV6 Address\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_ASSET_TAG) { + printf(" Setting: Asset Tag\n"); + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_AMBEINT_TEMP) { + printf(" Setting: Ambient Temp\n"); + if (lcd_mode.lcdquallifier & 0x02) { + printf(" Unit: F\n"); + } else { + printf(" Unit: C\n"); + } + } else if (lcd_mode.lcdmode == IPMI_DELL_LCD_SYSTEM_WATTS) { + printf(" Setting: System Watts\n"); + if (lcd_mode.lcdquallifier & 0x01) { + printf(" Unit: BTU/hr\n"); + } else { + printf(" Unit: Watt\n"); + } + } + if (lcd_mode.error_display == IPMI_DELL_LCD_ERROR_DISP_SEL) { + printf(" Error Display: SEL\n"); + } else if (lcd_mode.error_display == IPMI_DELL_LCD_ERROR_DISP_VERBOSE) { + printf(" Error Display: Simple\n"); + } + return 0; +} +/* + * Function Name: ipmi_lcd_get_info + * + * Description: This function prints current lcd configuration for platform other than whoville + * Input: intf - ipmi interface + * Output: + * Return: + */ +static int +ipmi_lcd_get_info(struct ipmi_intf * intf) +{ + IPMI_DELL_LCD_CAPS lcd_caps; + uint8_t command = 0; + char lcdstring[IPMI_DELL_LCD_STRING_LENGTH_MAX+1] = {0}; + int rc; + + printf("LCD info\n"); + + if (ipmi_lcd_get_configure_command(intf, &command) != 0) { + return -1; + } + if (command == IPMI_DELL_LCD_CONFIG_DEFAULT) { + memset(lcdstring,0,IPMI_DELL_LCD_STRING_LENGTH_MAX+1); + if (ipmi_lcd_get_platform_model_name(intf, lcdstring, + IPMI_DELL_LCD_STRING_LENGTH_MAX, + IPMI_DELL_PLATFORM_MODEL_NAME_SELECTOR) != 0) { + return (-1); + } + printf(" Setting: default\n"); + printf(" Line 1: %s\n", lcdstring); + } else if (command == IPMI_DELL_LCD_CONFIG_NONE) { + printf(" Setting: none\n"); + } else if (command == IPMI_DELL_LCD_CONFIG_USER_DEFINED) { + printf(" Setting: custom\n"); + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_GET_CAPS_SELECTOR, 0, 0, + sizeof(lcd_caps), &lcd_caps); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities."); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD capabilities: " + "Command not supported on this system."); + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (lcd_caps.number_lines > 0) { + memset(lcdstring, 0, IPMI_DELL_LCD_STRING_LENGTH_MAX + 1); + rc = ipmi_lcd_get_single_line_text(intf, lcdstring, + lcd_caps.max_chars[0]); + printf(" Text: %s\n", lcdstring); + } else { + printf(" No lines to show\n"); + } + } + return 0; +} +/* + * Function Name: ipmi_lcd_get_status_val + * + * Description: This function gets current lcd configuration + * Input: intf - ipmi interface + * Output: lcdstatus - KVM Status & Lock Status + * Return: + */ +static int +ipmi_lcd_get_status_val(struct ipmi_intf * intf, LCD_STATUS* lcdstatus) +{ + int rc; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_STATUS_SELECTOR, 0, 0, + sizeof(*lcdstatus), lcdstatus); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD Status"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD status: " + "Command not supported on this system."); + return -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD Status: %s", + val2str(rc, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: IsLCDSupported + * + * Description: This function returns whether lcd supported or not + * Input: + * Output: + * Return: + */ +static int +IsLCDSupported() +{ + return LcdSupported; +} +/* + * Function Name: CheckLCDSupport + * + * Description: This function checks whether lcd supported or not + * Input: intf - ipmi interface + * Output: + * Return: + */ +static void +CheckLCDSupport(struct ipmi_intf * intf) +{ + int rc; + LcdSupported = 0; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_STATUS_SELECTOR, 0, 0, 0, NULL); + if (rc == 0) { + LcdSupported = 1; + } +} +/* + * Function Name: ipmi_lcd_status_print + * + * Description: This function prints current lcd configuration KVM Status & Lock Status + * Input: lcdstatus - KVM Status & Lock Status + * Output: + * Return: + */ +static void +ipmi_lcd_status_print(LCD_STATUS lcdstatus) +{ + switch (lcdstatus.vKVM_status) { + case 0x00: + printf("LCD KVM Status :Inactive\n"); + break; + case 0x01: + printf("LCD KVM Status :Active\n"); + break; + default: + printf("LCD KVM Status :Invalid Status\n"); + break; + } + switch (lcdstatus.lock_status) { + case 0x00: + printf("LCD lock Status :View and modify\n"); + break; + case 0x01: + printf("LCD lock Status :View only\n"); + break; + case 0x02: + printf("LCD lock Status :disabled\n"); + break; + default: + printf("LCD lock Status :Invalid\n"); + break; + } +} +/* + * Function Name: ipmi_lcd_get_status + * + * Description: This function gets current lcd KVM active status & lcd access mode + * Input: intf - ipmi interface + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_get_status(struct ipmi_intf * intf) +{ + int rc=0; + LCD_STATUS lcdstatus; + rc =ipmi_lcd_get_status_val( intf, &lcdstatus); + if (rc < 0) { + return -1; + } + ipmi_lcd_status_print(lcdstatus); + return rc; +} +/* + * Function Name: ipmi_lcd_set_kvm + * + * Description: This function sets lcd KVM active status + * Input: intf - ipmi interface + * status - Inactive / Active + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_set_kvm(struct ipmi_intf * intf, char status) +{ + #define LSCC_DATA_LEN 2 + LCD_STATUS lcdstatus; + int rc=0; + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[5]; + rc = ipmi_lcd_get_status_val(intf,&lcdstatus); + if (rc < 0) { + return -1; + } + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_SET_SYS_INFO; + req.msg.data_len = 5; + req.msg.data = data; + data[0] = IPMI_DELL_LCD_STATUS_SELECTOR; + data[1] = status; /* active- incative*/ + data[2] = lcdstatus.lock_status; /* full-veiw-locked */ + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting LCD status"); + rc= -1; + } else if ((rsp->ccode == 0xc1) || (rsp->ccode == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD status: " + "Command not supported on this system."); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error setting LCD status: %s", + val2str(rsp->ccode, completion_code_vals)); + rc= -1; + } + return rc; +} +/* + * Function Name: ipmi_lcd_set_lock + * + * Description: This function sets lcd access mode + * Input: intf - ipmi interface + * lock - View and modify / View only / Diabled + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_set_lock(struct ipmi_intf * intf, char lock) +{ + #define LSCC_DATA_LEN 2 + LCD_STATUS lcdstatus; + int rc =0; + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[5]; + rc = ipmi_lcd_get_status_val(intf,&lcdstatus); + if (rc < 0) { + return -1; + } + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_SET_SYS_INFO; + req.msg.data_len = 5; + req.msg.data = data; + data[0] = IPMI_DELL_LCD_STATUS_SELECTOR; + data[1] = lcdstatus.vKVM_status; /* active- incative */ + data[2] = lock; /* full- veiw-locked */ + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting LCD status"); + rc = -1; + } else if ((rsp->ccode == 0xc1) || (rsp->ccode == 0xcb)) { + lprintf(LOG_ERR, "Error getting LCD status: " + "Command not supported on this system."); + rc = -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error setting LCD status: %s", + val2str(rsp->ccode, completion_code_vals)); + rc= -1; + } + return rc; +} +/* + * Function Name: ipmi_lcd_set_single_line_text + * + * Description: This function sets lcd line text + * Input: intf - ipmi interface + * text - lcd string + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_set_single_line_text(struct ipmi_intf * intf, char * text) +{ + uint8_t data[18]; + int bytes_to_store = strlen(text); + int bytes_stored = 0; + int ii; + int rc = 0; + if (bytes_to_store > IPMI_DELL_LCD_STRING_LENGTH_MAX) { + lprintf(LOG_ERR, "Out of range Max limit is 62 characters"); + return (-1); + } else { + bytes_to_store = MIN(bytes_to_store, IPMI_DELL_LCD_STRING_LENGTH_MAX); + for (ii = 0; ii < 4; ii++) { + /*first block, 2 bytes parms and 14 bytes data*/ + if (0 == ii) { + int size_of_copy = MIN((bytes_to_store - bytes_stored), + IPMI_DELL_LCD_STRING1_SIZE); + if (size_of_copy < 0) { + /* allow 0 string length*/ + break; + } + data[0] = IPMI_DELL_LCD_STRING_SELECTOR; + data[1] = ii; /* block number to use (0)*/ + data[2] = 0; /*string encoding*/ + data[3] = bytes_to_store; /* total string length*/ + memcpy(data + 4, text+bytes_stored, size_of_copy); + bytes_stored += size_of_copy; + } else { + int size_of_copy = MIN((bytes_to_store - bytes_stored), + IPMI_DELL_LCD_STRINGN_SIZE); + if (size_of_copy <= 0) { + break; + } + data[0] = IPMI_DELL_LCD_STRING_SELECTOR; + data[1] = ii; /* block number to use (1,2,3)*/ + memcpy(data + 2, text+bytes_stored, size_of_copy); + bytes_stored += size_of_copy; + } + rc = ipmi_mc_setsysinfo(intf, 18, data); + if (rc < 0) { + lprintf(LOG_ERR, "Error setting text data"); + rc = -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error setting text data: %s", + val2str(rc, completion_code_vals)); + rc = -1; + } + } + } + return rc; +} +/* + * Function Name: ipmi_lcd_set_text + * + * Description: This function sets lcd line text + * Input: intf - ipmi interface + * text - lcd string + * line_number- line number + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_set_text(struct ipmi_intf * intf, char * text, int line_number) +{ + int rc = 0; + IPMI_DELL_LCD_CAPS lcd_caps; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_LCD_GET_CAPS_SELECTOR, 0, 0, + sizeof(lcd_caps), &lcd_caps); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities"); + return -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error getting LCD capabilities: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (lcd_caps.number_lines > 0) { + rc = ipmi_lcd_set_single_line_text(intf, text); + } else { + lprintf(LOG_ERR, "LCD does not have any lines that can be set"); + rc = -1; + } + return rc; +} +/* + * Function Name: ipmi_lcd_configure_wh + * + * Description: This function updates the current lcd configuration + * Input: intf - ipmi interface + * lcdquallifier- lcd quallifier + * errordisp - error number + * line_number-line number + * text - lcd string + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_configure_wh(struct ipmi_intf * intf, uint32_t mode, + uint16_t lcdquallifier, uint8_t errordisp, int8_t line_number, char * text) +{ + int rc = 0; + if (IPMI_DELL_LCD_CONFIG_USER_DEFINED == mode) { + /* Any error was reported earlier. */ + rc = ipmi_lcd_set_text(intf, text, line_number); + } + if (rc == 0) { + rc = ipmi_lcd_set_configure_command_wh(intf, mode ,lcdquallifier,errordisp); + } + return rc; +} +/* + * Function Name: ipmi_lcd_configure + * + * Description: This function updates the current lcd configuration + * Input: intf - ipmi interface + * command- lcd command + * line_number-line number + * text - lcd string + * Output: + * Return: -1 on error + * 0 if successful + */ +static int +ipmi_lcd_configure(struct ipmi_intf * intf, int command, + int8_t line_number, char * text) +{ + int rc = 0; + if (IPMI_DELL_LCD_CONFIG_USER_DEFINED == command) { + rc = ipmi_lcd_set_text(intf, text, line_number); + } + if (rc == 0) { + rc = ipmi_lcd_set_configure_command(intf, command); + } + return rc; +} +/* + * Function Name: ipmi_lcd_usage + * + * Description: This function prints help message for lcd command + * Input: + * Output: + * + * Return: + */ +static void +ipmi_lcd_usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"Generic DELL HW:"); + lprintf(LOG_NOTICE, +" lcd set {none}|{default}|{custom <text>}"); + lprintf(LOG_NOTICE, +" Set LCD text displayed during non-fault conditions"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"iDRAC 11g or iDRAC 12g:"); + lprintf(LOG_NOTICE, +" lcd set {mode}|{lcdqualifier}|{errordisplay}"); + lprintf(LOG_NOTICE, +" Allows you to set the LCD mode and user-defined string."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd set mode {none}|{modelname}|{ipv4address}|{macaddress}|"); + lprintf(LOG_NOTICE, +" {systemname}|{servicetag}|{ipv6address}|{ambienttemp}"); + lprintf(LOG_NOTICE, +" {systemwatt }|{assettag}|{userdefined}<text>"); + lprintf(LOG_NOTICE, +" Allows you to set the LCD display mode to any of the preceding"); + lprintf(LOG_NOTICE, +" parameters"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd set lcdqualifier {watt}|{btuphr}|{celsius}|{fahrenheit}"); + lprintf(LOG_NOTICE, +" Allows you to set the unit for the system ambient temperature mode."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd set errordisplay {sel}|{simple}"); + lprintf(LOG_NOTICE, +" Allows you to set the error display."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd info"); + lprintf(LOG_NOTICE, +" Show LCD text that is displayed during non-fault conditions"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd set vkvm{active}|{inactive}"); + lprintf(LOG_NOTICE, +" Set vKVM active and inactive, message will be displayed on lcd"); + lprintf(LOG_NOTICE, +" when vKVM is active and vKVM session is in progress"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd set frontpanelaccess {viewandmodify}|{viewonly}|{disabled}"); + lprintf(LOG_NOTICE, +" Set LCD mode to view and modify, view only or disabled "); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lcd status"); + lprintf(LOG_NOTICE, +" Show LCD Status for vKVM display<active|inactive>"); + lprintf(LOG_NOTICE, +" and Front Panel access mode {viewandmodify}|{viewonly}|{disabled}"); + lprintf(LOG_NOTICE, +""); +} +/* + * Function Name: ipmi_delloem_mac_main + * + * Description: This function processes the delloem mac command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +static int +ipmi_delloem_mac_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + int currIdInt = -1; + current_arg++; + if (argc > 1 && strcmp(argv[current_arg], "help") == 0) { + ipmi_mac_usage(); + return 0; + } + ipmi_idracvalidator_command(intf); + if (argc == 1) { + rc = ipmi_macinfo(intf, 0xff); + } else if (strncmp(argv[current_arg], "list\0", 5) == 0) { + rc = ipmi_macinfo(intf, 0xff); + } else if (strncmp(argv[current_arg], "get\0", 4) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_mac_usage(); + return -1; + } + if (str2int(argv[current_arg],&currIdInt) != 0) { + lprintf(LOG_ERR, + "Invalid NIC number. The NIC number should be between 0-8"); + return -1; + } + if ((currIdInt > 8) || (currIdInt < 0)) { + lprintf(LOG_ERR, + "Invalid NIC number. The NIC number should be between 0-8"); + return -1; + } + rc = ipmi_macinfo(intf, currIdInt); + } else { + ipmi_mac_usage(); + } + return rc; +} + +EmbeddedNICMacAddressType EmbeddedNICMacAddress; + +EmbeddedNICMacAddressType_10G EmbeddedNICMacAddress_10G; + +static void +InitEmbeddedNICMacAddressValues() +{ + uint8_t i; + uint8_t j; + for (i = 0; i < MAX_LOM; i++) { + EmbeddedNICMacAddress.LOMMacAddress[i].BladSlotNumber = 0; + EmbeddedNICMacAddress.LOMMacAddress[i].MacType = LOM_MACTYPE_RESERVED; + EmbeddedNICMacAddress.LOMMacAddress[i].EthernetStatus = + LOM_ETHERNET_RESERVED; + EmbeddedNICMacAddress.LOMMacAddress[i].NICNumber = 0; + EmbeddedNICMacAddress.LOMMacAddress[i].Reserved = 0; + for (j = 0; j < MACADDRESSLENGH; j++) { + EmbeddedNICMacAddress.LOMMacAddress[i].MacAddressByte[j] = 0; + EmbeddedNICMacAddress_10G.MacAddress[i].MacAddressByte[j] = 0; + } + } +} + +uint8_t UseVirtualMacAddress = 0; +static int +ipmi_macinfo_drac_idrac_virtual_mac(struct ipmi_intf* intf,uint8_t NicNum) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[30]; + uint8_t VirtualMacAddress [MACADDRESSLENGH]; + uint8_t input_length=0; + uint8_t j; + uint8_t i; + if (NicNum != 0xff && NicNum != IDRAC_NIC_NUMBER) { + return 0; + } + UseVirtualMacAddress = 0; + input_length = 0; + msg_data[input_length++] = 1; /*Get*/ + + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_IDRAC_VIRTUAL_MAC; + req.msg.data = msg_data; + req.msg.data_len = input_length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + return -1; + } + if (rsp->ccode > 0) { + return -1; + } + if ((IMC_IDRAC_12G_MODULAR == IMC_Type) + || (IMC_IDRAC_12G_MONOLITHIC== IMC_Type)) { + /* Get the Chasiss Assigned MAC Addresss for 12g Only */ + memcpy(VirtualMacAddress, ((rsp->data) + 1), MACADDRESSLENGH); + for (i = 0; i < MACADDRESSLENGH; i++) { + if (VirtualMacAddress[i] != 0) { + UseVirtualMacAddress = 1; + } + } + /* Get the Server Assigned MAC Addresss for 12g Only */ + if (!UseVirtualMacAddress) { + memcpy(VirtualMacAddress, ((rsp->data) + 1 + MACADDRESSLENGH), + MACADDRESSLENGH); + for (i = 0; i < MACADDRESSLENGH; i++) { + if (VirtualMacAddress[i] != 0) { + UseVirtualMacAddress = 1; + } + } + } + } else { + memcpy(VirtualMacAddress, ((rsp->data) + VIRTUAL_MAC_OFFSET), + MACADDRESSLENGH); + for (i = 0; i < MACADDRESSLENGH; i++) { + if (VirtualMacAddress[i] != 0) { + UseVirtualMacAddress = 1; + } + } + } + if (UseVirtualMacAddress == 0) { + return -1; + } + if (IMC_IDRAC_10G == IMC_Type) { + printf("\nDRAC MAC Address "); + } else if ((IMC_IDRAC_11G_MODULAR == IMC_Type) + || (IMC_IDRAC_11G_MONOLITHIC== IMC_Type)) { + printf("\niDRAC6 MAC Address "); + } else if ((IMC_IDRAC_12G_MODULAR == IMC_Type) + || (IMC_IDRAC_12G_MONOLITHIC== IMC_Type)) { + printf("\niDRAC7 MAC Address "); + } else if ((IMC_MASER_LITE_BMC== IMC_Type) + || (IMC_MASER_LITE_NU== IMC_Type)) { + printf("\nBMC MAC Address "); + } else { + printf("\niDRAC6 MAC Address "); + } + + for (j = 0; j < 5; j++) { + printf("%02x:", VirtualMacAddress[j]); + } + printf("%02x", VirtualMacAddress[j]); + printf("\n"); + return 0; +} +/* + * Function Name: ipmi_macinfo_drac_idrac_mac + * + * Description: This function retrieves the mac address of DRAC or iDRAC + * Input: NicNum + * Output: + * Return: + */ +static int +ipmi_macinfo_drac_idrac_mac(struct ipmi_intf* intf,uint8_t NicNum) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[30]; + uint8_t input_length=0; + uint8_t iDRAC6MacAddressByte[MACADDRESSLENGH]; + uint8_t j; + ipmi_macinfo_drac_idrac_virtual_mac(intf,NicNum); + if ((NicNum != 0xff && NicNum != IDRAC_NIC_NUMBER) + || UseVirtualMacAddress != 0) { + return 0; + } + input_length = 0; + msg_data[input_length++] = LAN_CHANNEL_NUMBER; + msg_data[input_length++] = MAC_ADDR_PARAM; + msg_data[input_length++] = 0x00; + msg_data[input_length++] = 0x00; + + req.msg.netfn = TRANSPORT_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_LAN_PARAM_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting MAC Address"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting MAC Address (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + memcpy(iDRAC6MacAddressByte, ((rsp->data) + PARAM_REV_OFFSET), + MACADDRESSLENGH); + + if (IMC_IDRAC_10G == IMC_Type) { + printf("\nDRAC MAC Address "); + } else if ((IMC_IDRAC_11G_MODULAR == IMC_Type) + || (IMC_IDRAC_11G_MONOLITHIC== IMC_Type)) { + printf("\niDRAC6 MAC Address "); + } else if ((IMC_IDRAC_12G_MODULAR == IMC_Type) + || (IMC_IDRAC_12G_MONOLITHIC== IMC_Type)) { + printf("\niDRAC7 MAC Address "); + } else if ((IMC_MASER_LITE_BMC== IMC_Type) + || (IMC_MASER_LITE_NU== IMC_Type)) { + printf("\n\rBMC MAC Address "); + } else { + printf("\niDRAC6 MAC Address "); + } + + for (j = 0; j < 5; j++) { + printf("%02x:", iDRAC6MacAddressByte[j]); + } + printf("%02x", iDRAC6MacAddressByte[j]); + printf("\n"); + return 0; +} +/* + * Function Name: ipmi_macinfo_10g + * + * Description: This function retrieves the mac address of LOMs + * Input: intf - ipmi interface + * NicNum - NIC number + * Output: + * Return: + */ +static int +ipmi_macinfo_10g(struct ipmi_intf* intf, uint8_t NicNum) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[30]; + uint8_t input_length=0; + uint8_t j; + uint8_t i; + uint8_t Total_No_NICs = 0; + InitEmbeddedNICMacAddressValues(); + memset(msg_data, 0, sizeof(msg_data)); + input_length = 0; + msg_data[input_length++] = 0x00; /* Get Parameter Command */ + msg_data[input_length++] = EMB_NIC_MAC_ADDRESS_9G_10G; /* OEM Param */ + msg_data[input_length++] = 0x00; + msg_data[input_length++] = 0x00; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_GET_SYS_INFO; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting MAC Address"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting MAC Address (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + Total_No_NICs = (uint8_t)rsp->data[0 + PARAM_REV_OFFSET]; /* Byte 1: Total Number of Embedded NICs */ + if (IDRAC_NIC_NUMBER != NicNum) { + if (0xff == NicNum) { + printf("\nSystem LOMs"); + } + printf("\nNIC Number\tMAC Address\n"); + memcpy(&EmbeddedNICMacAddress_10G, + ((rsp->data) + PARAM_REV_OFFSET+TOTAL_N0_NICS_INDEX), + Total_No_NICs* MACADDRESSLENGH); + /*Read the LOM type and Mac Addresses */ + for (i = 0; i < Total_No_NICs; i++) { + if ((0xff == NicNum) || (i == NicNum)) { + printf("\n%d",i); + printf("\t\t"); + for (j = 0 ; j < 5; j++) { + printf("%02x:", + EmbeddedNICMacAddress_10G.MacAddress[i].MacAddressByte[j]); + } + printf("%02x", + EmbeddedNICMacAddress_10G.MacAddress[i].MacAddressByte[j]); + } + } + printf("\n"); + } + ipmi_macinfo_drac_idrac_mac(intf,NicNum); + return 0; +} +/* + * Function Name: ipmi_macinfo_11g + * + * Description: This function retrieves the mac address of LOMs + * Input: intf - ipmi interface + * Output: + * Return: + */ +static int +ipmi_macinfo_11g(struct ipmi_intf* intf, uint8_t NicNum) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t input_length = 0; + uint8_t i; + uint8_t j; + uint8_t len; + uint8_t loop_count; + uint8_t maxlen; + uint8_t msg_data[30]; + uint8_t offset; + offset = 0; + len = 8; /*eigher 8 or 16 */ + maxlen = 64; + loop_count = maxlen / len; + InitEmbeddedNICMacAddressValues(); + memset(msg_data, 0, sizeof(msg_data)); + input_length = 0; + msg_data[input_length++] = 0x00; /* Get Parameter Command */ + msg_data[input_length++] = EMB_NIC_MAC_ADDRESS_11G; /* OEM Param */ + msg_data[input_length++] = 0x00; + msg_data[input_length++] = 0x00; + msg_data[input_length++] = 0x00; + msg_data[input_length++] = 0x00; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_GET_SYS_INFO; + req.msg.data = msg_data; + req.msg.data_len = input_length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting MAC Address"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting MAC Address (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + len = 8; /*eigher 8 or 16 */ + maxlen = (uint8_t)rsp->data[0 + PARAM_REV_OFFSET]; + loop_count = maxlen / len; + if (IDRAC_NIC_NUMBER != NicNum) { + if (0xff == NicNum) { + printf("\nSystem LOMs"); + } + printf("\nNIC Number\tMAC Address\t\tStatus\n"); + /*Read the LOM type and Mac Addresses */ + offset=0; + for (i = 0; i < loop_count; i++, offset = offset + len) { + input_length = 4; + msg_data[input_length++] = offset; + msg_data[input_length++] = len; + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_GET_SYS_INFO; + req.msg.data = msg_data; + req.msg.data_len = input_length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting MAC Address"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting MAC Address (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + memcpy(&(EmbeddedNICMacAddress.LOMMacAddress[i]), + ((rsp->data)+PARAM_REV_OFFSET), len); + if (LOM_MACTYPE_ETHERNET == EmbeddedNICMacAddress.LOMMacAddress[i].MacType) { + if ((0xff==NicNum) + || (NicNum == EmbeddedNICMacAddress.LOMMacAddress[i].NICNumber)) { + printf("\n%d",EmbeddedNICMacAddress.LOMMacAddress[i].NICNumber); + printf("\t\t"); + for (j = 0; j < 5; j++) { + printf("%02x:", + EmbeddedNICMacAddress.LOMMacAddress[i].MacAddressByte[j]); + } + printf("%02x", + EmbeddedNICMacAddress.LOMMacAddress[i].MacAddressByte[j]); + + if (LOM_ETHERNET_ENABLED + == EmbeddedNICMacAddress.LOMMacAddress[i].EthernetStatus) { + printf("\tEnabled"); + } else { + printf("\tDisabled"); + } + } + } + } + printf("\n"); + } + ipmi_macinfo_drac_idrac_mac(intf,NicNum); + return 0; +} +/* + * Function Name: ipmi_macinfo + * + * Description: This function retrieves the mac address of LOMs + * Input: intf - ipmi interface + * Output: + * Return: + */ +static int +ipmi_macinfo(struct ipmi_intf* intf, uint8_t NicNum) +{ + if (IMC_IDRAC_10G == IMC_Type) { + return ipmi_macinfo_10g(intf,NicNum); + } else if ((IMC_IDRAC_11G_MODULAR == IMC_Type + || IMC_IDRAC_11G_MONOLITHIC == IMC_Type) + || (IMC_IDRAC_12G_MODULAR == IMC_Type + || IMC_IDRAC_12G_MONOLITHIC == IMC_Type) + || (IMC_MASER_LITE_NU == IMC_Type || IMC_MASER_LITE_BMC== IMC_Type)) { + return ipmi_macinfo_11g(intf,NicNum); + } else { + lprintf(LOG_ERR, "Error in getting MAC Address : Not supported platform"); + return (-1); + } +} +/* + * Function Name: ipmi_mac_usage + * + * Description: This function prints help message for mac command + * Input: + * Output: + * + * Return: + */ +static void +ipmi_mac_usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" mac list"); + lprintf(LOG_NOTICE, +" Lists the MAC address of LOMs"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" mac get <NIC number>"); + lprintf(LOG_NOTICE, +" Shows the MAC address of specified LOM. 0-7 System LOM, 8- DRAC/iDRAC."); + lprintf(LOG_NOTICE, +""); +} +/* + * Function Name: ipmi_delloem_lan_main + * + * Description: This function processes the delloem lan command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +static int +ipmi_delloem_lan_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + int nic_selection = 0; + char nic_set[2] = {0}; + current_arg++; + if (argv[current_arg] == NULL || strcmp(argv[current_arg], "help") == 0) { + ipmi_lan_usage(); + return 0; + } + ipmi_idracvalidator_command(intf); + if (!IsLANSupported()) { + lprintf(LOG_ERR, "lan is not supported on this system."); + return -1; + } else if (strncmp(argv[current_arg], "set\0", 4) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_lan_usage(); + return -1; + } + if (iDRAC_FLAG == IDRAC_12G) { + nic_selection = get_nic_selection_mode_12g(intf, current_arg, argv, + nic_set); + if (INVALID == nic_selection) { + ipmi_lan_usage(); + return -1; + } else if (INVAILD_FAILOVER_MODE == nic_selection) { + lprintf(LOG_ERR, INVAILD_FAILOVER_MODE_STRING); + return (-1); + } else if (INVAILD_FAILOVER_MODE_SETTINGS == nic_selection) { + lprintf(LOG_ERR, INVAILD_FAILOVER_MODE_SET); + return (-1); + } else if (INVAILD_SHARED_MODE == nic_selection) { + lprintf(LOG_ERR, INVAILD_SHARED_MODE_SET_STRING); + return (-1); + } + rc = ipmi_lan_set_nic_selection_12g(intf,nic_set); + } else { + nic_selection = get_nic_selection_mode(current_arg, argv); + if (INVALID == nic_selection) { + ipmi_lan_usage(); + return -1; + } + if (IMC_IDRAC_11G_MODULAR == IMC_Type) { + lprintf(LOG_ERR, INVAILD_SHARED_MODE_SET_STRING); + return (-1); + } + rc = ipmi_lan_set_nic_selection(intf,nic_selection); + } + return 0; + } else if (strncmp(argv[current_arg], "get\0", 4) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + rc = ipmi_lan_get_nic_selection(intf); + return rc; + } else if (strncmp(argv[current_arg], "active\0", 7) == 0) { + rc = ipmi_lan_get_active_nic(intf); + return rc; + } else { + ipmi_lan_usage(); + } + } else { + ipmi_lan_usage(); + return -1; + } + return rc; +} + +static int +IsLANSupported() +{ + if (IMC_IDRAC_11G_MODULAR == IMC_Type) { + return 0; + } + return 1; +} + +int +get_nic_selection_mode_12g(struct ipmi_intf* intf,int current_arg, + char ** argv, char *nic_set) +{ + /* First get the current settings. */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + int failover = 0; + int nic_selection_mode = 0; + uint8_t input_length = 0; + uint8_t msg_data[30]; + + input_length = 0; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_NIC_SELECTION_12G_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting nic selection"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting nic selection (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + nic_set[0] = rsp->data[0]; + nic_set[1] = rsp->data[1]; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "dedicated\0", 10) == 0) { + nic_set[0] = 1; + nic_set[1] = 0; + return 0; + } + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "shared\0", 7) == 0) { + /* placeholder */ + } else { + return INVALID; + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "with\0", 5) == 0) { + /* placeholder */ + } else { + return INVALID; + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "failover\0", 9) == 0) { + failover = 1; + } + if (failover) { + current_arg++; + } + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "lom1\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (failover) { + if (nic_set[0] == 2) { + return INVAILD_FAILOVER_MODE; + } else if (nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 2; + } else { + nic_set[0] = 2; + if (nic_set[1] == 2) { + nic_set[1] = 0; + } + } + return 0; + } else if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "lom2\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (failover) { + if (nic_set[0] == 3) { + return INVAILD_FAILOVER_MODE; + } else if(nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 3; + } else { + nic_set[0] = 3; + if (nic_set[1] == 3) { + nic_set[1] = 0; + } + } + return 0; + } else if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "lom3\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (failover) { + if (nic_set[0] == 4) { + return INVAILD_FAILOVER_MODE; + } else if(nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 4; + } else { + nic_set[0] = 4; + if (nic_set[1] == 4) { + nic_set[1] = 0; + } + } + return 0; + } else if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "lom4\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (failover) { + if (nic_set[0] == 5) { + return INVAILD_FAILOVER_MODE; + } else if(nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 5; + } else { + nic_set[0] = 5; + if (nic_set[1] == 5) { + nic_set[1] = 0; + } + } + return 0; + } else if (failover && argv[current_arg] != NULL + && strncmp(argv[current_arg], "none\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (failover) { + if (nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 0; + } + return 0; + } else if (failover && argv[current_arg] != NULL + && strncmp(argv[current_arg], "all\0", 4) == 0) { + /* placeholder */ + } else { + return INVALID; + } + + current_arg++; + if (failover && argv[current_arg] != NULL + && strncmp(argv[current_arg], "loms\0", 5) == 0) { + if (IMC_IDRAC_12G_MODULAR == IMC_Type) { + return INVAILD_SHARED_MODE; + } + if (nic_set[0] == 1) { + return INVAILD_FAILOVER_MODE_SETTINGS; + } + nic_set[1] = 6; + return 0; + } + return INVALID; +} + +static int +get_nic_selection_mode(int current_arg, char ** argv) +{ + int nic_selection_mode = 0; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "dedicated\0", 10) == 0) { + return DEDICATED; + } + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "shared\0", 7) == 0) { + if (argv[current_arg+1] == NULL) { + return SHARED; + } + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "with\0", 5) == 0) { + /* place holder */ + } else { + return INVALID; + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "failover\0", 9) == 0) { + /* place holder */ + } else { + return INVALID; + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "lom2\0", 5) == 0) { + return SHARED_WITH_FAILOVER_LOM2; + } else if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "all\0", 4) == 0) { + /* place holder */ + } else { + return INVALID; + } + + current_arg++; + if (argv[current_arg] != NULL + && strncmp(argv[current_arg], "loms\0", 5) == 0) { + return SHARED_WITH_FAILOVER_ALL_LOMS; + } + return INVALID; +} + +static int +ipmi_lan_set_nic_selection_12g(struct ipmi_intf * intf, uint8_t * nic_selection) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t input_length = 0; + uint8_t msg_data[30]; + + input_length = 0; + msg_data[input_length++] = nic_selection[0]; + msg_data[input_length++] = nic_selection[1]; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = SET_NIC_SELECTION_12G_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in setting nic selection"); + return -1; + } else if( (nic_selection[0] == 1) + && ((iDRAC_FLAG == IDRAC_12G) && (rsp->ccode == LICENSE_NOT_SUPPORTED))) { + /* Check license only for setting the dedicated nic. */ + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in setting nic selection (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + printf("configured successfully"); + return 0; +} + +static int +ipmi_lan_set_nic_selection(struct ipmi_intf * intf, uint8_t nic_selection) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t input_length = 0; + uint8_t msg_data[30]; + + input_length = 0; + msg_data[input_length++] = nic_selection; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = SET_NIC_SELECTION_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in setting nic selection"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in setting nic selection (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + printf("configured successfully"); + return 0; +} + +static int +ipmi_lan_get_nic_selection(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t input_length=0; + uint8_t msg_data[30]; + uint8_t nic_selection=-1; + uint8_t nic_selection_failover = 0; + + input_length = 0; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + if (iDRAC_FLAG == IDRAC_12G) { + req.msg.cmd = GET_NIC_SELECTION_12G_CMD; + } else { + req.msg.cmd = GET_NIC_SELECTION_CMD; + } + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting nic selection"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting nic selection (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + nic_selection = rsp->data[0]; + if (iDRAC_FLAG == IDRAC_12G) { + nic_selection_failover = rsp->data[1]; + if ((nic_selection < 6) && (nic_selection > 0) + && (nic_selection_failover < 7)) { + if(nic_selection == 1) { + printf("%s\n",NIC_Selection_Mode_String_12g[nic_selection-1]); + } else if(nic_selection) { + printf("Shared LOM : %s\n", + NIC_Selection_Mode_String_12g[nic_selection-1]); + if(nic_selection_failover == 0) { + printf("Failover LOM : None\n"); + } else if(nic_selection_failover >= 2 && nic_selection_failover <= 6) { + printf("Failover LOM : %s\n", + NIC_Selection_Mode_String_12g[nic_selection_failover + 3]); + } + } + } else { + lprintf(LOG_ERR, "Error Outof bond Value received (%d) (%d)", + nic_selection,nic_selection_failover); + return -1; + } + } else { + printf("%s\n",NIC_Selection_Mode_String[nic_selection]); + } + return 0; +} + +static int +ipmi_lan_get_active_nic(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t active_nic=0; + uint8_t current_lom =0; + uint8_t input_length=0; + uint8_t msg_data[30]; + + input_length = 0; + msg_data[input_length++] = 0; /* Get Status */ + msg_data[input_length++] = 0; /* Reserved */ + msg_data[input_length++] = 0; /* Reserved */ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_ACTIVE_NIC_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting Active LOM Status"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting Active LOM Status (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + current_lom = rsp->data[0]; + input_length = 0; + msg_data[input_length++] = 1; /* Get Link status */ + msg_data[input_length++] = 0; /* Reserved */ + msg_data[input_length++] = 0; /* Reserved */ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_ACTIVE_NIC_CMD; + req.msg.data = msg_data; + req.msg.data_len = input_length; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting Active LOM Status"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting Active LOM Status (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + active_nic = rsp->data[1]; + if (current_lom < 6 && active_nic) { + printf("\n%s\n", AciveLOM_String[current_lom]); + } else { + printf("\n%s\n", AciveLOM_String[0]); + } + return 0; +} + +static void +ipmi_lan_usage(void) +{ + /* TODO: + * - rewrite + * - review + * - make it fit into 80 chars per line + * - this ``shared with Failover None).'' seems like a typo + */ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lan set <Mode>"); + lprintf(LOG_NOTICE, +" sets the NIC Selection Mode :"); + lprintf(LOG_NOTICE, +" on iDRAC12g :"); + lprintf(LOG_NOTICE, +" dedicated, shared with lom1, shared with lom2,shared with lom3,shared"); + lprintf(LOG_NOTICE, +" with lom4,shared with failover lom1,shared with failover lom2,shared"); + lprintf(LOG_NOTICE, +" with failover lom3,shared with failover lom4,shared with Failover all"); + lprintf(LOG_NOTICE, +" loms, shared with Failover None)."); + lprintf(LOG_NOTICE, +" on other systems :"); + lprintf(LOG_NOTICE, +" dedicated, shared, shared with failover lom2,"); + lprintf(LOG_NOTICE, +" shared with Failover all loms."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lan get "); + lprintf(LOG_NOTICE, +" on iDRAC12g :"); + lprintf(LOG_NOTICE, +" returns the current NIC Selection Mode (dedicated, shared with lom1, shared"); + lprintf(LOG_NOTICE, +" with lom2, shared with lom3, shared with lom4,shared with failover lom1,"); + lprintf(LOG_NOTICE, +" shared with failover lom2,shared with failover lom3,shared with failover"); + lprintf(LOG_NOTICE, +" lom4,shared with Failover all loms,shared with Failover None)."); + lprintf(LOG_NOTICE, +" on other systems :"); + lprintf(LOG_NOTICE, +" dedicated, shared, shared with failover,"); + lprintf(LOG_NOTICE, +" lom2, shared with Failover all loms."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" lan get active"); + lprintf(LOG_NOTICE, +" returns the current active NIC (dedicated, LOM1, LOM2, LOM3, LOM4)."); + lprintf(LOG_NOTICE, +""); +} +/* + * Function Name: ipmi_delloem_powermonitor_main + * + * Description: This function processes the delloem powermonitor command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +static int +ipmi_delloem_powermonitor_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + current_arg++; + if (argc > 1 && strcmp(argv[current_arg], "help") == 0) { + ipmi_powermonitor_usage(); + return 0; + } + ipmi_idracvalidator_command(intf); + if (argc == 1) { + rc = ipmi_powermgmt(intf); + } else if (strncmp(argv[current_arg], "status\0", 7) == 0) { + rc = ipmi_powermgmt(intf); + } else if (strncmp(argv[current_arg], "clear\0", 6) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_powermonitor_usage(); + return -1; + } else if (strncmp(argv[current_arg], "peakpower\0", 10) == 0) { + rc = ipmi_powermgmt_clear(intf, 1); + } else if (strncmp(argv[current_arg], "cumulativepower\0", 16) == 0) { + rc = ipmi_powermgmt_clear(intf, 0); + } else { + ipmi_powermonitor_usage(); + return -1; + } + } else if (strncmp(argv[current_arg], "powerconsumption\0", 17) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + rc = ipmi_print_get_power_consmpt_data(intf,watt); + } else if (strncmp(argv[current_arg], "watt\0", 5) == 0) { + rc = ipmi_print_get_power_consmpt_data(intf, watt); + } else if (strncmp(argv[current_arg], "btuphr\0", 7) == 0) { + rc = ipmi_print_get_power_consmpt_data(intf, btuphr); + } else { + ipmi_powermonitor_usage(); + return -1; + } + } else if (strncmp(argv[current_arg], "powerconsumptionhistory\0", 23) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + rc = ipmi_print_power_consmpt_history(intf,watt); + } else if (strncmp(argv[current_arg], "watt\0", 5) == 0) { + rc = ipmi_print_power_consmpt_history(intf, watt); + } else if (strncmp(argv[current_arg], "btuphr\0", 7) == 0) { + rc = ipmi_print_power_consmpt_history(intf, btuphr); + } else { + ipmi_powermonitor_usage(); + return -1; + } + } else if (strncmp(argv[current_arg], "getpowerbudget\0", 15) == 0) { + current_arg++; + if (argv[current_arg] == NULL) { + rc=ipmi_print_power_cap(intf,watt); + } else if (strncmp(argv[current_arg], "watt\0", 5) == 0) { + rc = ipmi_print_power_cap(intf, watt); + } else if (strncmp(argv[current_arg], "btuphr\0", 7) == 0) { + rc = ipmi_print_power_cap(intf, btuphr); + } else { + ipmi_powermonitor_usage(); + return -1; + } + } else if (strncmp(argv[current_arg], "setpowerbudget\0", 15) == 0) { + int val; + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_powermonitor_usage(); + return -1; + } + if (strchr(argv[current_arg], '.')) { + lprintf(LOG_ERR, + "Cap value in Watts, Btu/hr or percent should be whole number"); + return -1; + } + if (str2int(argv[current_arg], &val) != 0) { + lprintf(LOG_ERR, "Given capacity value '%s' is invalid.", + argv[current_arg]); + return (-1); + } + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_powermonitor_usage(); + } else if (strncmp(argv[current_arg], "watt\0", 5) == 0) { + rc = ipmi_set_power_cap(intf,watt,val); + } else if (strncmp(argv[current_arg], "btuphr\0", 7) == 0) { + rc = ipmi_set_power_cap(intf, btuphr,val); + } else if (strncmp(argv[current_arg], "percent\0", 8) == 0) { + rc = ipmi_set_power_cap(intf,percent,val); + } else { + ipmi_powermonitor_usage(); + return -1; + } + } else if (strncmp(argv[current_arg], "enablepowercap\0", 15) == 0) { + ipmi_set_power_capstatus_command(intf,1); + } else if (strncmp(argv[current_arg], "disablepowercap\0", 16) == 0) { + ipmi_set_power_capstatus_command(intf,0); + } else { + ipmi_powermonitor_usage(); + return -1; + } + return rc; +} +/* + * Function Name: ipmi_time_to_str + * + * Description: This function converts ipmi time format into gmtime format + * Input: rawTime - ipmi time format + * Output: strTime - gmtime format + * + * Return: + */ +static void +ipmi_time_to_str(time_t rawTime, char * strTime) +{ + struct tm *tm; + char *temp; + tm = gmtime(&rawTime); + temp = asctime(tm); + strcpy(strTime,temp); +} +/* + * Function Name: ipmi_get_sensor_reading + * + * Description: This function retrieves a raw sensor reading + * Input: sensorOwner - sensor owner id + * sensorNumber - sensor id + * intf - ipmi interface + * Output: sensorReadingData - ipmi response structure + * Return: 1 on error + * 0 if successful + */ +static int +ipmi_get_sensor_reading(struct ipmi_intf *intf, unsigned char sensorNumber, + SensorReadingType* pSensorReadingData) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + int rc = 0; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = 0; + req.msg.cmd = GET_SENSOR_READING; + req.msg.data = &sensorNumber; + req.msg.data_len = 1; + if (pSensorReadingData == NULL) { + return -1; + } + memset(pSensorReadingData, 0, sizeof(SensorReadingType)); + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + return 1; + } else if (rsp->ccode > 0) { + return 1; + } + memcpy(pSensorReadingData, rsp->data, sizeof(SensorReadingType)); + /* if there is an error transmitting ipmi command, return error */ + if (rsp->ccode != 0) { + rc = 1; + } + /* if sensor messages are disabled, return error*/ + if ((!(rsp->data[1]& 0xC0)) || ((rsp->data[1] & 0x20))) { + rc =1; + } + return rc; +} +/* + * Function Name: ipmi_get_power_capstatus_command + * + * Description: This function gets the power cap status + * Input: intf - ipmi interface + * Global: PowercapSetable_flag - power cap status + * Output: + * + * Return: + */ +static int +ipmi_get_power_capstatus_command(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[2]; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = IPMI_DELL_POWER_CAP_STATUS; + req.msg.data_len = 2; + req.msg.data = data; + data[0] = 01; + data[1] = 0xFF; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting powercap status"); + return -1; + } else if((iDRAC_FLAG == IDRAC_12G) && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; /* Return Error as unlicensed */ + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error getting powercap statusr: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if (rsp->data[0] & 0x02) { + PowercapSetable_flag=1; + } + if (rsp->data[0] & 0x01) { + PowercapstatusFlag=1; + } + return 0; +} +/* + * Function Name: ipmi_set_power_capstatus_command + * + * Description: This function sets the power cap status + * Input: intf - ipmi interface + * val - power cap status + * Output: + * + * Return: + */ +static int +ipmi_set_power_capstatus_command(struct ipmi_intf * intf, uint8_t val) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[2]; + if (ipmi_get_power_capstatus_command(intf) < 0) { + return -1; + } + if (PowercapSetable_flag != 1) { + lprintf(LOG_ERR, "Can not set powercap on this system"); + return -1; + } + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = IPMI_DELL_POWER_CAP_STATUS; + req.msg.data_len = 2; + req.msg.data = data; + data[0] = 00; + data[1] = val; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting powercap status"); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; /* return unlicensed Error code */ + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error setting powercap statusr: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_powermgmt + * + * Description: This function print the powermonitor details + * Input: intf - ipmi interface + * Output: + * + * Return: + */ +static int +ipmi_powermgmt(struct ipmi_intf * intf) +{ + time_t now; + struct tm* tm; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + uint32_t cumStartTimeConv; + uint32_t cumReadingConv; + uint32_t maxPeakStartTimeConv; + uint32_t ampPeakTimeConv; + uint16_t ampReadingConv; + uint32_t wattPeakTimeConv; + uint32_t wattReadingConv; + uint32_t bmctimeconv; + uint32_t * bmctimeconvval; + + IPMI_POWER_MONITOR * pwrMonitorInfo; + + char cumStartTime[26]; + char maxPeakStartTime[26]; + char ampPeakTime[26]; + char wattPeakTime[26]; + char bmctime[26]; + + int ampReading; + int ampReadingRemainder; + int remainder; + int wattReading; + + now = time(0); + tm = gmtime(&now); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.lun = 0; + req.msg.cmd = IPMI_CMD_GET_SEL_TIME; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting BMC time info."); + return -1; + } + if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "Error getting power management information, return code %x", + rsp->ccode); + return -1; + } + bmctimeconvval=(uint32_t*)rsp->data; +# if WORDS_BIGENDIAN + bmctimeconv=BSWAP_32(*bmctimeconvval); +# else + bmctimeconv=*bmctimeconvval; +# endif + + /* get powermanagement info*/ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0x0; + req.msg.cmd = GET_PWRMGMT_INFO_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + + memset(msg_data, 0, 2); + msg_data[0] = 0x07; + msg_data[1] = 0x01; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting power management information."); + return -1; + } + + if((iDRAC_FLAG == IDRAC_12G) && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rsp->ccode == 0xc1)||(rsp->ccode == 0xcb)) { + lprintf(LOG_ERR, "Error getting power management information: " + "Command not supported on this system."); + return -1; + }else if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "Error getting power management information, return code %x", + rsp->ccode); + return -1; + } + + pwrMonitorInfo = (IPMI_POWER_MONITOR*)rsp->data; +# if WORDS_BIGENDIAN + cumStartTimeConv = BSWAP_32(pwrMonitorInfo->cumStartTime); + cumReadingConv = BSWAP_32(pwrMonitorInfo->cumReading); + maxPeakStartTimeConv = BSWAP_32(pwrMonitorInfo->maxPeakStartTime); + ampPeakTimeConv = BSWAP_32(pwrMonitorInfo->ampPeakTime); + ampReadingConv = BSWAP_16(pwrMonitorInfo->ampReading); + wattPeakTimeConv = BSWAP_32(pwrMonitorInfo->wattPeakTime); + wattReadingConv = BSWAP_16(pwrMonitorInfo->wattReading); +# else + cumStartTimeConv = pwrMonitorInfo->cumStartTime; + cumReadingConv = pwrMonitorInfo->cumReading; + maxPeakStartTimeConv = pwrMonitorInfo->maxPeakStartTime; + ampPeakTimeConv = pwrMonitorInfo->ampPeakTime; + ampReadingConv = pwrMonitorInfo->ampReading; + wattPeakTimeConv = pwrMonitorInfo->wattPeakTime; + wattReadingConv = pwrMonitorInfo->wattReading; +# endif + + ipmi_time_to_str(cumStartTimeConv, cumStartTime); + ipmi_time_to_str(maxPeakStartTimeConv, maxPeakStartTime); + ipmi_time_to_str(ampPeakTimeConv, ampPeakTime); + ipmi_time_to_str(wattPeakTimeConv, wattPeakTime); + ipmi_time_to_str(bmctimeconv, bmctime); + now = time(0); + + remainder = (cumReadingConv % 1000); + cumReadingConv = cumReadingConv / 1000; + remainder = (remainder + 50) / 100; + + ampReading = ampReadingConv; + ampReadingRemainder = ampReading%10; + ampReading = ampReading/10; + + wattReading = wattReadingConv; + + printf("Power Tracking Statistics\n"); + printf("Statistic : Cumulative Energy Consumption\n"); + printf("Start Time : %s", cumStartTime); + printf("Finish Time : %s", bmctime); + printf("Reading : %d.%d kWh\n\n", cumReadingConv, remainder); + + printf("Statistic : System Peak Power\n"); + printf("Start Time : %s", maxPeakStartTime); + printf("Peak Time : %s", wattPeakTime); + printf("Peak Reading : %d W\n\n", wattReading); + + printf("Statistic : System Peak Amperage\n"); + printf("Start Time : %s", maxPeakStartTime); + printf("Peak Time : %s", ampPeakTime); + printf("Peak Reading : %d.%d A\n", ampReading, ampReadingRemainder); + return 0; +} +/* + * Function Name: ipmi_powermgmt_clear + * + * Description: This function clears peakpower / cumulativepower value + * Input: intf - ipmi interface + * clearValue - peakpower / cumulativepower + * Output: + * + * Return: + */ +static int +ipmi_powermgmt_clear(struct ipmi_intf * intf, uint8_t clearValue) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t clearType = 1; + uint8_t msg_data[3]; + if (clearValue) { + clearType = 2; + } + /* clear powermanagement info*/ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = CLEAR_PWRMGMT_INFO_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + memset(msg_data, 0, 3); + msg_data[0] = 0x07; + msg_data[1] = 0x01; + msg_data[2] = clearType; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error clearing power values."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) + && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if (rsp->ccode == 0xc1) { + lprintf(LOG_ERR, + "Error clearing power values, command not supported on this system."); + return -1; + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Error clearing power values: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: watt_to_btuphr_conversion + * + * Description: This function converts the power value in watt to btuphr + * Input: powerinwatt - power in watt + * + * Output: power in btuphr + * + * Return: + */ +static uint64_t +watt_to_btuphr_conversion(uint32_t powerinwatt) +{ + uint64_t powerinbtuphr; + powerinbtuphr=(3.413 * powerinwatt); + return(powerinbtuphr); +} +/* + * Function Name: btuphr_to_watt_conversion + * + * Description: This function converts the power value in btuphr to watt + * Input: powerinbtuphr - power in btuphr + * + * Output: power in watt + * + * Return: + */ +static uint32_t +btuphr_to_watt_conversion(uint64_t powerinbtuphr) +{ + uint32_t powerinwatt; + /*returning the floor value*/ + powerinwatt= (powerinbtuphr / 3.413); + return (powerinwatt); +} +/* + * Function Name: ipmi_get_power_headroom_command + * + * Description: This function prints the Power consumption information + * Input: intf - ipmi interface + * unit - watt / btuphr + * Output: + * + * Return: + */ +static int +ipmi_get_power_headroom_command(struct ipmi_intf * intf,uint8_t unit) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint64_t peakpowerheadroombtuphr; + uint64_t instantpowerhearoom; + + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_PWR_HEADROOM_CMD; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting power headroom status"); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) + && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rsp->ccode == 0xc1) || (rsp->ccode == 0xcb)) { + lprintf(LOG_ERR, "Error getting power headroom status: " + "Command not supported on this system "); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error getting power headroom status: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if (verbose > 1) { + /* need to look into */ + printf("power headroom Data : %x %x %x %x ", rsp->data[0], + rsp->data[1], rsp->data[2], rsp->data[3]); + } + powerheadroom= *(( POWER_HEADROOM *)rsp->data); +# if WORDS_BIGENDIAN + powerheadroom.instheadroom = BSWAP_16(powerheadroom.instheadroom); + powerheadroom.peakheadroom = BSWAP_16(powerheadroom.peakheadroom); +# endif + printf("Headroom\n"); + printf("Statistic Reading\n"); + if (unit == btuphr) { + peakpowerheadroombtuphr = watt_to_btuphr_conversion(powerheadroom.peakheadroom); + instantpowerhearoom = watt_to_btuphr_conversion(powerheadroom.instheadroom); + printf("System Instantaneous Headroom : %lld BTU/hr\n", + instantpowerhearoom); + printf("System Peak Headroom : %lld BTU/hr\n", + peakpowerheadroombtuphr); + } else { + printf("System Instantaneous Headroom : %d W\n", + powerheadroom.instheadroom); + printf("System Peak Headroom : %d W\n", + powerheadroom.peakheadroom); + } + return 0; +} +/* + * Function Name: ipmi_get_power_consumption_data + * + * Description: This function updates the instant Power consumption information + * Input: intf - ipmi interface + * Output: power consumption current reading + * Assumption value will be in Watt. + * + * Return: + */ +static int +ipmi_get_power_consumption_data(struct ipmi_intf * intf,uint8_t unit) +{ + SensorReadingType sensorReadingData; + + struct ipmi_rs * rsp=NULL; + struct sdr_record_list *sdr; + int readingbtuphr = 0; + int warning_threshbtuphr = 0; + int failure_threshbtuphr = 0; + int status = 0; + int sensor_number = 0; + sdr = ipmi_sdr_find_sdr_byid(intf, "System Level"); + if (sdr == NULL) { + lprintf(LOG_ERR, + "Error : Can not access the System Level sensor data"); + return -1; + } + sensor_number = sdr->record.common->keys.sensor_num; + ipmi_get_sensor_reading(intf,sensor_number,&sensorReadingData); + rsp = ipmi_sdr_get_sensor_thresholds(intf, + sdr->record.common->keys.sensor_num, + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + if (rsp == NULL || rsp->ccode != 0) { + lprintf(LOG_ERR, + "Error : Can not access the System Level sensor data"); + return -1; + } + readingbtuphr = sdr_convert_sensor_reading(sdr->record.full, + sensorReadingData.sensorReading); + warning_threshbtuphr = sdr_convert_sensor_reading(sdr->record.full, + rsp->data[4]); + failure_threshbtuphr = sdr_convert_sensor_reading(sdr->record.full, + rsp->data[5]); + + printf("System Board System Level\n"); + if (unit == btuphr) { + readingbtuphr = watt_to_btuphr_conversion(readingbtuphr); + warning_threshbtuphr = watt_to_btuphr_conversion(warning_threshbtuphr); + failure_threshbtuphr = watt_to_btuphr_conversion( failure_threshbtuphr); + + printf("Reading : %d BTU/hr\n", readingbtuphr); + printf("Warning threshold : %d BTU/hr\n", warning_threshbtuphr); + printf("Failure threshold : %d BTU/hr\n", failure_threshbtuphr); + } else { + printf("Reading : %d W \n",readingbtuphr); + printf("Warning threshold : %d W \n",(warning_threshbtuphr)); + printf("Failure threshold : %d W \n",(failure_threshbtuphr)); + } + return status; +} +/* + * Function Name: ipmi_get_instan_power_consmpt_data + * + * Description: This function updates the instant Power consumption information + * Input: intf - ipmi interface + * Output: instpowerconsumptiondata - instant Power consumption information + * + * Return: + */ +static int +ipmi_get_instan_power_consmpt_data(struct ipmi_intf * intf, + IPMI_INST_POWER_CONSUMPTION_DATA * instpowerconsumptiondata) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req={0}; + uint8_t msg_data[2]; + /*get instantaneous power consumption command*/ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = GET_PWR_CONSUMPTION_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + memset(msg_data, 0, 2); + msg_data[0] = 0x0A; + msg_data[1] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting instantaneous power consumption data ."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) + && (rsp->ccode == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rsp->ccode == 0xc1) || (rsp->ccode == 0xcb)) { + lprintf(LOG_ERR, "Error getting instantaneous power consumption data: " + "Command not supported on this system."); + return -1; + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Error getting instantaneous power consumption data: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + *instpowerconsumptiondata = *((IPMI_INST_POWER_CONSUMPTION_DATA *)(rsp->data)); +#if WORDS_BIGENDIAN + instpowerconsumptiondata->instanpowerconsumption = BSWAP_16(instpowerconsumptiondata->instanpowerconsumption); + instpowerconsumptiondata->instanApms = BSWAP_16(instpowerconsumptiondata->instanApms); + instpowerconsumptiondata->resv1 = BSWAP_16(instpowerconsumptiondata->resv1); +#endif + return 0; +} +/* + * Function Name: ipmi_print_get_instan_power_Amps_data + * + * Description: This function prints the instant Power consumption information + * Input: instpowerconsumptiondata - instant Power consumption information + * Output: + * + * Return: + */ +static void +ipmi_print_get_instan_power_Amps_data(IPMI_INST_POWER_CONSUMPTION_DATA instpowerconsumptiondata) +{ + uint16_t intampsval=0; + uint16_t decimalampsval=0; + if (instpowerconsumptiondata.instanApms > 0) { + decimalampsval = (instpowerconsumptiondata.instanApms % 10); + intampsval = instpowerconsumptiondata.instanApms / 10; + } + printf("\nAmperage value: %d.%d A \n", intampsval, decimalampsval); +} +/* + * Function Name: ipmi_print_get_power_consmpt_data + * + * Description: This function prints the Power consumption information + * Input: intf - ipmi interface + * unit - watt / btuphr + * Output: + * + * Return: + */ +static int +ipmi_print_get_power_consmpt_data(struct ipmi_intf * intf, uint8_t unit) +{ + int rc = 0; + IPMI_INST_POWER_CONSUMPTION_DATA instpowerconsumptiondata = {0,0,0,0}; + printf("\nPower consumption information\n"); + rc = ipmi_get_power_consumption_data(intf, unit); + if (rc == (-1)) { + return rc; + } + rc = ipmi_get_instan_power_consmpt_data(intf, &instpowerconsumptiondata); + if (rc == (-1)) { + return rc; + } + ipmi_print_get_instan_power_Amps_data(instpowerconsumptiondata); + rc = ipmi_get_power_headroom_command(intf, unit); + if (rc == (-1)) { + return rc; + } + return rc; +} +/* + * Function Name: ipmi_get_avgpower_consmpt_history + * + * Description: This function updates the average power consumption information + * Input: intf - ipmi interface + * Output: pavgpower- average power consumption information + * + * Return: + */ +static int +ipmi_get_avgpower_consmpt_history(struct ipmi_intf * intf, + IPMI_AVGPOWER_CONSUMP_HISTORY * pavgpower) +{ + int rc; + uint8_t *rdata; + rc = ipmi_mc_getsysinfo(intf, 0xeb, 0, 0, sizeof(*pavgpower), pavgpower); + if (rc < 0) { + lprintf(LOG_ERR, + "Error getting average power consumption history data."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting average power consumption history data: " + "Command not supported on this system."); + return -1; + } else if (rc != 0) { + lprintf(LOG_ERR, + "Error getting average power consumption history data: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + rdata = (void *)pavgpower; + printf("Average power consumption history data" + " :%x %x %x %x %x %x %x %x\n\n", + rdata[0], rdata[1], rdata[2], rdata[3], + rdata[4], rdata[5], rdata[6], rdata[7]); + } +# if WORDS_BIGENDIAN + pavgpower->lastminutepower = BSWAP_16(pavgpower->lastminutepower); + pavgpower->lasthourpower = BSWAP_16(pavgpower->lasthourpower); + pavgpower->lastdaypower = BSWAP_16(pavgpower->lastdaypower); + pavgpower->lastweakpower = BSWAP_16(pavgpower->lastweakpower); +# endif + return 0; +} +/* + * Function Name: ipmi_get_peakpower_consmpt_history + * + * Description: This function updates the peak power consumption information + * Input: intf - ipmi interface + * Output: pavgpower- peak power consumption information + * + * Return: + */ +static int +ipmi_get_peakpower_consmpt_history(struct ipmi_intf * intf, + IPMI_POWER_CONSUMP_HISTORY * pstPeakpower) +{ + uint8_t *rdata; + int rc; + rc = ipmi_mc_getsysinfo(intf, 0xec, 0, 0, sizeof(*pstPeakpower), + pstPeakpower); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting peak power consumption history data."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting peak power consumption history data: " + "Command not supported on this system."); + return -1; + } else if (rc != 0) { + lprintf(LOG_ERR, "Error getting peak power consumption history data: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + rdata = (void *)pstPeakpower; + printf("Peak power consmhistory Data : " + "%x %x %x %x %x %x %x %x %x %x\n " + "%x %x %x %x %x %x %x %x %x %x %x %x %x %x\n\n", + rdata[0], rdata[1], rdata[2], rdata[3], + rdata[4], rdata[5], rdata[6], rdata[7], + rdata[8], rdata[9], rdata[10], rdata[11], + rdata[12], rdata[13], rdata[14], rdata[15], + rdata[16], rdata[17], rdata[18], rdata[19], + rdata[20], rdata[21], rdata[22], rdata[23]); + } +# if WORDS_BIGENDIAN + pstPeakpower->lastminutepower = BSWAP_16(pstPeakpower->lastminutepower); + pstPeakpower->lasthourpower = BSWAP_16(pstPeakpower->lasthourpower); + pstPeakpower->lastdaypower = BSWAP_16(pstPeakpower->lastdaypower); + pstPeakpower->lastweakpower = BSWAP_16(pstPeakpower->lastweakpower); + pstPeakpower->lastminutepowertime = BSWAP_32(pstPeakpower->lastminutepowertime); + pstPeakpower->lasthourpowertime = BSWAP_32(pstPeakpower->lasthourpowertime); + pstPeakpower->lastdaypowertime = BSWAP_32(pstPeakpower->lastdaypowertime); + pstPeakpower->lastweekpowertime = BSWAP_32(pstPeakpower->lastweekpowertime); +#endif + return 0; +} +/* + * Function Name: ipmi_get_minpower_consmpt_history + * + * Description: This function updates the peak power consumption information + * Input: intf - ipmi interface + * Output: pavgpower- peak power consumption information + * + * Return: + */ +static int +ipmi_get_minpower_consmpt_history(struct ipmi_intf * intf, + IPMI_POWER_CONSUMP_HISTORY * pstMinpower) +{ + uint8_t *rdata; + int rc; + rc = ipmi_mc_getsysinfo(intf, 0xed, 0, 0, sizeof(*pstMinpower), + pstMinpower); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting peak power consumption history data ."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting peak power consumption history data: " + "Command not supported on this system."); + return -1; + } else if (rc != 0) { + lprintf(LOG_ERR, "Error getting peak power consumption history data: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + rdata = (void *)pstMinpower; + printf("Peak power consmhistory Data : " + "%x %x %x %x %x %x %x %x %x %x\n " + "%x %x %x %x %x %x %x %x %x %x %x %x %x\n\n", + rdata[0], rdata[1], rdata[2], rdata[3], + rdata[4], rdata[5], rdata[6], rdata[7], + rdata[8], rdata[9], rdata[10], rdata[11], + rdata[12], rdata[13], rdata[14], rdata[15], + rdata[16], rdata[17], rdata[18], rdata[19], + rdata[20], rdata[21], rdata[22], rdata[23]); + } +# if WORDS_BIGENDIAN + pstMinpower->lastminutepower = BSWAP_16(pstMinpower->lastminutepower); + pstMinpower->lasthourpower = BSWAP_16(pstMinpower->lasthourpower); + pstMinpower->lastdaypower = BSWAP_16(pstMinpower->lastdaypower); + pstMinpower->lastweakpower = BSWAP_16(pstMinpower->lastweakpower); + pstMinpower->lastminutepowertime = BSWAP_32(pstMinpower->lastminutepowertime); + pstMinpower->lasthourpowertime = BSWAP_32(pstMinpower->lasthourpowertime); + pstMinpower->lastdaypowertime = BSWAP_32(pstMinpower->lastdaypowertime); + pstMinpower->lastweekpowertime = BSWAP_32(pstMinpower->lastweekpowertime); +# endif + return 0; +} +/* + * Function Name: ipmi_print_power_consmpt_history + * + * Description: This function print the average and peak power consumption information + * Input: intf - ipmi interface + * unit - watt / btuphr + * Output: + * + * Return: + */ +static int +ipmi_print_power_consmpt_history(struct ipmi_intf * intf, int unit) +{ + char timestr[30]; + uint32_t lastminutepeakpower; + uint32_t lasthourpeakpower; + uint32_t lastdaypeakpower; + uint32_t lastweekpeakpower; + uint64_t tempbtuphrconv; + int rc = 0; + + IPMI_AVGPOWER_CONSUMP_HISTORY avgpower; + IPMI_POWER_CONSUMP_HISTORY stMinpower; + IPMI_POWER_CONSUMP_HISTORY stPeakpower; + + rc = ipmi_get_avgpower_consmpt_history(intf, &avgpower); + if (rc == (-1)) { + return rc; + } + + rc = ipmi_get_peakpower_consmpt_history(intf, &stPeakpower); + if (rc == (-1)) { + return rc; + } + + rc = ipmi_get_minpower_consmpt_history(intf, &stMinpower); + if (rc == (-1)) { + return rc; + } + if (rc != 0) { + return rc; + } + printf("Power Consumption History\n\n"); + /* The fields are alligned manually changing the spaces will alter + * the alignment*/ + printf("Statistic Last Minute Last Hour " + "Last Day Last Week\n\n"); + if (unit == btuphr) { + printf("Average Power Consumption "); + tempbtuphrconv = watt_to_btuphr_conversion(avgpower.lastminutepower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(avgpower.lasthourpower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(avgpower.lastdaypower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(avgpower.lastweakpower); + printf("%4lld BTU/hr\n", tempbtuphrconv); + + printf("Max Power Consumption "); + tempbtuphrconv = watt_to_btuphr_conversion(stPeakpower.lastminutepower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stPeakpower.lasthourpower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stPeakpower.lastdaypower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stPeakpower.lastweakpower); + printf("%4lld BTU/hr\n", tempbtuphrconv); + + printf("Min Power Consumption "); + tempbtuphrconv = watt_to_btuphr_conversion(stMinpower.lastminutepower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stMinpower.lasthourpower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stMinpower.lastdaypower); + printf("%4lld BTU/hr ", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(stMinpower.lastweakpower); + printf("%4lld BTU/hr\n\n", tempbtuphrconv); + } else { + printf("Average Power Consumption "); + tempbtuphrconv = (avgpower.lastminutepower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (avgpower.lasthourpower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (avgpower.lastdaypower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv=(avgpower.lastweakpower); + printf("%4lld W \n", tempbtuphrconv); + + printf("Max Power Consumption "); + tempbtuphrconv = (stPeakpower.lastminutepower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stPeakpower.lasthourpower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stPeakpower.lastdaypower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stPeakpower.lastweakpower); + printf("%4lld W \n", tempbtuphrconv); + + printf("Min Power Consumption "); + tempbtuphrconv = (stMinpower.lastminutepower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stMinpower.lasthourpower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stMinpower.lastdaypower); + printf("%4lld W ", tempbtuphrconv); + tempbtuphrconv = (stMinpower.lastweakpower); + printf("%4lld W \n\n", tempbtuphrconv); + } + + lastminutepeakpower = stPeakpower.lastminutepowertime; + lasthourpeakpower = stPeakpower.lasthourpowertime; + lastdaypeakpower = stPeakpower.lastdaypowertime; + lastweekpeakpower = stPeakpower.lastweekpowertime; + + printf("Max Power Time\n"); + ipmi_time_to_str(lastminutepeakpower, timestr); + printf("Last Minute : %s",timestr); + ipmi_time_to_str(lasthourpeakpower, timestr); + printf("Last Hour : %s",timestr); + ipmi_time_to_str(lastdaypeakpower, timestr); + printf("Last Day : %s",timestr); + ipmi_time_to_str(lastweekpeakpower, timestr); + printf("Last Week : %s",timestr); + + lastminutepeakpower=stMinpower.lastminutepowertime; + lasthourpeakpower=stMinpower.lasthourpowertime; + lastdaypeakpower=stMinpower.lastdaypowertime; + lastweekpeakpower=stMinpower.lastweekpowertime; + + printf("Min Power Time\n"); + ipmi_time_to_str(lastminutepeakpower, timestr); + printf("Last Minute : %s", timestr); + ipmi_time_to_str(lasthourpeakpower, timestr); + printf("Last Hour : %s", timestr); + ipmi_time_to_str(lastdaypeakpower, timestr); + printf("Last Day : %s", timestr); + ipmi_time_to_str(lastweekpeakpower, timestr); + printf("Last Week : %s", timestr); + return rc; +} +/* + * Function Name: ipmi_get_power_cap + * + * Description: This function updates the power cap information + * Input: intf - ipmi interface + * Output: ipmipowercap - power cap information + * + * Return: + */ +static int +ipmi_get_power_cap(struct ipmi_intf * intf, IPMI_POWER_CAP * ipmipowercap) +{ + uint64_t tempbtuphrconv; + uint8_t *rdata; + int rc; + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_POWER_CAP, 0, 0, + sizeof(*ipmipowercap), ipmipowercap); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting power cap."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if ((rc == 0xc1) || (rc == 0xcb)) { + lprintf(LOG_ERR, "Error getting power cap: " + "Command not supported on this system."); + return -1; + } else if (rc != 0) { + lprintf(LOG_ERR, "Error getting power cap: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + rdata = (void*)ipmipowercap; + printf("power cap Data :%x %x %x %x %x %x %x %x %x %x ", + rdata[1], rdata[2], rdata[3], + rdata[4], rdata[5], rdata[6], rdata[7], + rdata[8], rdata[9], rdata[10],rdata[11]); + } +# if WORDS_BIGENDIAN + ipmipowercap->PowerCap = BSWAP_16(ipmipowercap->PowerCap); + ipmipowercap->MaximumPowerConsmp = BSWAP_16(ipmipowercap->MaximumPowerConsmp); + ipmipowercap->MinimumPowerConsmp = BSWAP_16(ipmipowercap->MinimumPowerConsmp); + ipmipowercap->totalnumpowersupp = BSWAP_16(ipmipowercap->totalnumpowersupp); + ipmipowercap->AvailablePower = BSWAP_16(ipmipowercap->AvailablePower); + ipmipowercap->SystemThrottling = BSWAP_16(ipmipowercap->SystemThrottling); + ipmipowercap->Resv = BSWAP_16(ipmipowercap->Resv); +# endif + return 0; +} +/* + * Function Name: ipmi_print_power_cap + * + * Description: This function print the power cap information + * Input: intf - ipmi interface + * unit - watt / btuphr + * Output: + * Return: + */ +static int +ipmi_print_power_cap(struct ipmi_intf * intf,uint8_t unit) +{ + uint64_t tempbtuphrconv; + int rc; + IPMI_POWER_CAP ipmipowercap; + memset(&ipmipowercap, 0, sizeof(ipmipowercap)); + rc = ipmi_get_power_cap(intf, &ipmipowercap); + if (rc == 0) { + if (unit == btuphr) { + tempbtuphrconv = watt_to_btuphr_conversion(ipmipowercap.MaximumPowerConsmp); + printf("Maximum power: %lld BTU/hr\n", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(ipmipowercap.MinimumPowerConsmp); + printf("Minimum power: %lld BTU/hr\n", tempbtuphrconv); + tempbtuphrconv = watt_to_btuphr_conversion(ipmipowercap.PowerCap); + printf("Power cap : %lld BTU/hr\n", tempbtuphrconv); + } else { + printf("Maximum power: %d Watt\n", ipmipowercap.MaximumPowerConsmp); + printf("Minimum power: %d Watt\n", ipmipowercap.MinimumPowerConsmp); + printf("Power cap : %d Watt\n", ipmipowercap.PowerCap); + } + } + return rc; +} +/* + * Function Name: ipmi_set_power_cap + * + * Description: This function updates the power cap information + * Input: intf - ipmi interface + * unit - watt / btuphr + * val - new power cap value + * Output: + * Return: + */ +static int +ipmi_set_power_cap(struct ipmi_intf * intf, int unit, int val) +{ + int rc; + uint8_t data[13], *rdata; + uint16_t powercapval; + uint64_t maxpowerbtuphr; + uint64_t maxpowerbtuphr1; + uint64_t minpowerbtuphr; + IPMI_POWER_CAP ipmipowercap; + + if (ipmi_get_power_capstatus_command(intf) < 0) { + return -1; /* Adding the failed condition check */ + } + if (PowercapSetable_flag != 1) { + lprintf(LOG_ERR, "Can not set powercap on this system"); + return -1; + } else if (PowercapstatusFlag != 1) { + lprintf(LOG_ERR, "Power cap set feature is not enabled"); + return -1; + } + rc = ipmi_mc_getsysinfo(intf, IPMI_DELL_POWER_CAP, 0, 0, + sizeof(ipmipowercap), &ipmipowercap); + if (rc < 0) { + lprintf(LOG_ERR, "Error getting power cap."); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if (rc == 0xc1) { + lprintf(LOG_ERR, "Error getting power cap, command not supported on " + "this system."); + return -1; + } else if (rc != 0) { + lprintf(LOG_ERR, "Error getting power cap: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + rdata = (void *)&ipmipowercap; + printf("power cap Data :%x %x %x %x %x %x %x %x %x %x %x ", + rdata[1], rdata[2], rdata[3], + rdata[4], rdata[5], rdata[6], rdata[7], + rdata[8], rdata[9], rdata[10],rdata[11]); + } +# if WORDS_BIGENDIAN + ipmipowercap.PowerCap = BSWAP_16(ipmipowercap.PowerCap); + ipmipowercap.MaximumPowerConsmp = BSWAP_16(ipmipowercap.MaximumPowerConsmp); + ipmipowercap.MinimumPowerConsmp = BSWAP_16(ipmipowercap.MinimumPowerConsmp); + ipmipowercap.AvailablePower = BSWAP_16(ipmipowercap.AvailablePower); + ipmipowercap.totalnumpowersupp = BSWAP_16(ipmipowercap.totalnumpowersupp); +# endif + memset(data, 0, 13); + data[0] = IPMI_DELL_POWER_CAP; + powercapval = val; + data[1] = (powercapval & 0XFF); + data[2] = ((powercapval & 0XFF00) >> 8); + data[3] = unit; + data[4] = ((ipmipowercap.MaximumPowerConsmp & 0xFF)); + data[5] = ((ipmipowercap.MaximumPowerConsmp & 0xFF00) >> 8); + data[6] = ((ipmipowercap.MinimumPowerConsmp & 0xFF)); + data[7] = ((ipmipowercap.MinimumPowerConsmp & 0xFF00) >> 8); + data[8] = (ipmipowercap.totalnumpowersupp); + data[9] = ((ipmipowercap.AvailablePower & 0xFF)); + data[10] = ((ipmipowercap.AvailablePower & 0xFF00) >> 8); + data[11] = (ipmipowercap.SystemThrottling); + data[12] = 0x00; + + if (unit == btuphr) { + val = btuphr_to_watt_conversion(val); + } else if (unit == percent) { + if ((val < 0) || (val > 100)) { + lprintf(LOG_ERR, "Cap value is out of boundary conditon it " + "should be between 0 - 100"); + return -1; + } + val = ((val*(ipmipowercap.MaximumPowerConsmp + - ipmipowercap.MinimumPowerConsmp)) / 100) + + ipmipowercap.MinimumPowerConsmp; + lprintf(LOG_ERR, "Cap value in percentage is %d ", val); + data[1] = (val & 0XFF); + data[2] = ((val & 0XFF00) >> 8); + data[3] = watt; + } + if (((val < ipmipowercap.MinimumPowerConsmp) + || (val > ipmipowercap.MaximumPowerConsmp)) && (unit == watt)) { + lprintf(LOG_ERR, + "Cap value is out of boundary conditon it should be between %d - %d", + ipmipowercap.MinimumPowerConsmp, ipmipowercap.MaximumPowerConsmp); + return -1; + } else if (((val < ipmipowercap.MinimumPowerConsmp) + || (val > ipmipowercap.MaximumPowerConsmp)) && (unit == btuphr)) { + minpowerbtuphr = watt_to_btuphr_conversion(ipmipowercap.MinimumPowerConsmp); + maxpowerbtuphr = watt_to_btuphr_conversion(ipmipowercap.MaximumPowerConsmp); + maxpowerbtuphr1 = watt_to_btuphr_conversion(ipmipowercap.MaximumPowerConsmp); + lprintf(LOG_ERR, + "Cap value is out of boundary conditon it should be between %d", + minpowerbtuphr); + lprintf(LOG_ERR, " -%d", maxpowerbtuphr1); + return -1; + } + rc = ipmi_mc_setsysinfo(intf, 13, data); + if (rc < 0) { + lprintf(LOG_ERR, "Error setting power cap"); + return -1; + } else if ((iDRAC_FLAG == IDRAC_12G) && (rc == LICENSE_NOT_SUPPORTED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if (rc > 0) { + lprintf(LOG_ERR, "Error setting power cap: %s", + val2str(rc, completion_code_vals)); + return -1; + } + if (verbose > 1) { + printf("CC for setpowercap :%d ", rc); + } + return 0; +} +/* + * Function Name: ipmi_powermonitor_usage + * + * Description: This function prints help message for powermonitor command + * Input: + * Output: + * + * Return: + */ +static void +ipmi_powermonitor_usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor"); + lprintf(LOG_NOTICE, +" Shows power tracking statistics "); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor clear cumulativepower"); + lprintf(LOG_NOTICE, +" Reset cumulative power reading"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor clear peakpower"); + lprintf(LOG_NOTICE, +" Reset peak power reading"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor powerconsumption"); + lprintf(LOG_NOTICE, +" Displays power consumption in <watt|btuphr>"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor powerconsumptionhistory <watt|btuphr>"); + lprintf(LOG_NOTICE, +" Displays power consumption history "); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor getpowerbudget"); + lprintf(LOG_NOTICE, +" Displays power cap in <watt|btuphr>"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor setpowerbudget <val><watt|btuphr|percent>"); + lprintf(LOG_NOTICE, +" Allows user to set the power cap in <watt|BTU/hr|percentage>"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor enablepowercap "); + lprintf(LOG_NOTICE, +" To enable set power cap"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" powermonitor disablepowercap "); + lprintf(LOG_NOTICE, +" To disable set power cap"); + lprintf(LOG_NOTICE, +""); +} +/* + * Function Name: ipmi_delloem_vFlash_main + * + * Description: This function processes the delloem vFlash command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +static int +ipmi_delloem_vFlash_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + current_arg++; + rc = ipmi_delloem_vFlash_process(intf, current_arg, argv); + return rc; +} +/* + * Function Name: get_vFlash_compcode_str + * + * Description: This function maps the vFlash completion code + * to a string + * Input : vFlash completion code and static array of codes vs strings + * Output: - + * Return: returns the mapped string + */ +const char * +get_vFlash_compcode_str(uint8_t vflashcompcode, const struct vFlashstr *vs) +{ + static char un_str[32]; + int i; + for (i = 0; vs[i].str != NULL; i++) { + if (vs[i].val == vflashcompcode) + return vs[i].str; + } + memset(un_str, 0, 32); + snprintf(un_str, 32, "Unknown (0x%02X)", vflashcompcode); + return un_str; +} +/* + * Function Name: ipmi_get_sd_card_info + * + * Description: This function prints the vFlash Extended SD card info + * Input : ipmi interface + * Output: prints the sd card extended info + * Return: 0 - success -1 - failure + */ +static int +ipmi_get_sd_card_info(struct ipmi_intf * intf) { + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + uint8_t input_length=0; + uint8_t cardstatus=0x00; + IPMI_DELL_SDCARD_INFO * sdcardinfoblock; + + input_length = 2; + msg_data[0] = msg_data[1] = 0x00; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = IPMI_GET_EXT_SD_CARD_INFO; + req.msg.data = msg_data; + req.msg.data_len = input_length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in getting SD Card Extended Information"); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in getting SD Card Extended Information (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + sdcardinfoblock = (IPMI_DELL_SDCARD_INFO *) (void *) rsp->data; + + if ((iDRAC_FLAG == IDRAC_12G) + && (sdcardinfoblock->vflashcompcode == VFL_NOT_LICENSED)) { + lprintf(LOG_ERR, + "FM001 : A required license is missing or expired"); + return -1; + } else if (sdcardinfoblock->vflashcompcode != 0x00) { + lprintf(LOG_ERR, "Error in getting SD Card Extended Information (%s)", + get_vFlash_compcode_str(sdcardinfoblock->vflashcompcode, + vFlash_completion_code_vals)); + return -1; + } + + if (!(sdcardinfoblock->sdcardstatus & 0x04)) { + lprintf(LOG_ERR, + "vFlash SD card is unavailable, please insert the card of"); + lprintf(LOG_ERR, + "size 256MB or greater"); + return (-1); + } + + printf("vFlash SD Card Properties\n"); + printf("SD Card size : %8dMB\n", sdcardinfoblock->sdcardsize); + printf("Available size : %8dMB\n", sdcardinfoblock->sdcardavailsize); + printf("Initialized : %10s\n", + (sdcardinfoblock->sdcardstatus & 0x80) ? "Yes" : "No"); + printf("Licensed : %10s\n", + (sdcardinfoblock->sdcardstatus & 0x40) ? "Yes" : "No"); + printf("Attached : %10s\n", + (sdcardinfoblock->sdcardstatus & 0x20) ? "Yes" : "No"); + printf("Enabled : %10s\n", + (sdcardinfoblock->sdcardstatus & 0x10) ? "Yes" : "No"); + printf("Write Protected : %10s\n", + (sdcardinfoblock->sdcardstatus & 0x08) ? "Yes" : "No"); + cardstatus = sdcardinfoblock->sdcardstatus & 0x03; + printf("Health : %10s\n", + ((0x00 == cardstatus) ? "OK" : ( + (cardstatus == 0x03) ? "Undefined" : ( + (cardstatus == 0x02) ? "Critical" : "Warning")))); + printf("Bootable partition : %10d\n", sdcardinfoblock->bootpartion); + return 0; +} +/* + * Function Name: ipmi_delloem_vFlash_process + * + * Description: This function processes the args for vFlash subcmd + * Input : intf - ipmi interface, arg index, argv array + * Output: prints help or error with help + * Return: 0 - Success -1 - failure + */ +static int +ipmi_delloem_vFlash_process(struct ipmi_intf * intf, int current_arg, char ** argv) +{ + int rc; + if (strncmp(intf->name,"wmi\0",4) && strncmp(intf->name, "open\0",5)) { + lprintf(LOG_ERR, + "vFlash support is enabled only for wmi and open interface."); + lprintf(LOG_ERR, "Its not enabled for lan and lanplus interface."); + return -1; + } + + if (argv[current_arg] == NULL || strcmp(argv[current_arg], "help") == 0) { + ipmi_vFlash_usage(); + return 0; + } + ipmi_idracvalidator_command(intf); + if (!strncmp(argv[current_arg], "info\0", 5)) { + current_arg++; + if (argv[current_arg] == NULL) { + ipmi_vFlash_usage(); + return -1; + } else if (strncmp(argv[current_arg], "Card\0", 5) == 0) { + current_arg++; + if (argv[current_arg] != NULL) { + ipmi_vFlash_usage(); + return -1; + } + rc = ipmi_get_sd_card_info(intf); + return rc; + } else { + /* TBD: many sub commands are present */ + ipmi_vFlash_usage(); + return -1; + } + } else { + /* TBD other vFlash subcommands */ + ipmi_vFlash_usage(); + return -1; + } +} +/* + * Function Name: ipmi_vFlash_usage + * + * Description: This function displays the usage for using vFlash + * Input : void + * Output: prints help + * Return: void + */ +static void +ipmi_vFlash_usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" vFlash info Card"); + lprintf(LOG_NOTICE, +" Shows Extended SD Card information"); + lprintf(LOG_NOTICE, +""); +} +/* + * Function Name: ipmi_setled_usage + * + * Description: This function prints help message for setled command + * Input: + * Output: + * + * Return: + */ +static void +ipmi_setled_usage(void) +{ + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +" setled <b:d.f> <state..>"); + lprintf(LOG_NOTICE, +" Set backplane LED state"); + lprintf(LOG_NOTICE, +" b:d.f = PCI Bus:Device.Function of drive (lspci format)"); + lprintf(LOG_NOTICE, +" state = present|online|hotspare|identify|rebuilding|"); + lprintf(LOG_NOTICE, +" fault|predict|critical|failed"); + lprintf(LOG_NOTICE, +""); +} + +static int +IsSetLEDSupported(void) +{ + return SetLEDSupported; +} + +static void +CheckSetLEDSupport(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[10]; + + SetLEDSupported = 0; + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = 0xD5; /* Storage */ + req.msg.data_len = 10; + req.msg.data = data; + + memset(data, 0, sizeof(data)); + data[0] = 0x01; /* get */ + data[1] = 0x00; /* subcmd:get firmware version */ + data[2] = 0x08; /* length lsb */ + data[3] = 0x00; /* length msb */ + data[4] = 0x00; /* offset lsb */ + data[5] = 0x00; /* offset msb */ + data[6] = 0x00; /* bay id */ + data[7] = 0x00; + data[8] = 0x00; + data[9] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL || rsp->ccode != 0) { + return; + } + SetLEDSupported = 1; +} +/* + * Function Name: ipmi_getdrivemap + * + * Description: This function returns mapping of BDF to Bay:Slot + * Input: intf - ipmi interface + * bdf - PCI Address of drive + * *bay - Returns bay ID + * *slot - Returns slot ID + * Output: + * + * Return: + */ +static int +ipmi_getdrivemap(struct ipmi_intf * intf, int b, int d, int f, int *bay, + int *slot) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[8]; + /* Get mapping of BDF to bay:slot */ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = 0xD5; + req.msg.data_len = 8; + req.msg.data = data; + + memset(data, 0, sizeof(data)); + data[0] = 0x01; /* get */ + data[1] = 0x07; /* storage map */ + data[2] = 0x06; /* length lsb */ + data[3] = 0x00; /* length msb */ + data[4] = 0x00; /* offset lsb */ + data[5] = 0x00; /* offset msb */ + data[6] = b; /* bus */ + data[7] = (d << 3) + f; /* devfn */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error issuing getdrivemap command."); + return -1; + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Error issuing getdrivemap command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + *bay = rsp->data[7]; + *slot = rsp->data[8]; + if (*bay == 0xFF || *slot == 0xFF) { + lprintf(LOG_ERR, "Error could not get drive bay:slot mapping"); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_setled_state + * + * Description: This function updates the LED on the backplane + * Input: intf - ipmi interface + * bdf - PCI Address of drive + * state - SES Flags state of drive + * Output: + * + * Return: + */ +static int +ipmi_setled_state(struct ipmi_intf * intf, int bayId, int slotId, int state) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req = {0}; + uint8_t data[20]; + /* Issue Drive Status Update to bay:slot */ + req.msg.netfn = DELL_OEM_NETFN; + req.msg.lun = 0; + req.msg.cmd = 0xD5; + req.msg.data_len = 20; + req.msg.data = data; + + memset(data, 0, sizeof(data)); + data[0] = 0x00; /* set */ + data[1] = 0x04; /* set drive status */ + data[2] = 0x0e; /* length lsb */ + data[3] = 0x00; /* length msb */ + data[4] = 0x00; /* offset lsb */ + data[5] = 0x00; /* offset msb */ + data[6] = 0x0e; /* length lsb */ + data[7] = 0x00; /* length msb */ + data[8] = bayId; /* bayid */ + data[9] = slotId; /* slotid */ + data[10] = state & 0xff; /* state LSB */ + data[11] = state >> 8; /* state MSB; */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error issuing setled command."); + return -1; + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Error issuing setled command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + return 0; +} +/* + * Function Name: ipmi_getsesmask + * + * Description: This function calculates bits in SES drive update + * Return: Mask set with bits for SES backplane update + */ +static int +ipmi_getsesmask(int argc, char **argv) +{ + int mask = 0; + while (current_arg < argc) { + if (!strcmp(argv[current_arg], "present")) + mask |= (1L << 0); + if (!strcmp(argv[current_arg], "online")) + mask |= (1L << 1); + if (!strcmp(argv[current_arg], "hotspare")) + mask |= (1L << 2); + if (!strcmp(argv[current_arg], "identify")) + mask |= (1L << 3); + if (!strcmp(argv[current_arg], "rebuilding")) + mask |= (1L << 4); + if (!strcmp(argv[current_arg], "fault")) + mask |= (1L << 5); + if (!strcmp(argv[current_arg], "predict")) + mask |= (1L << 6); + if (!strcmp(argv[current_arg], "critical")) + mask |= (1L << 9); + if (!strcmp(argv[current_arg], "failed")) + mask |= (1L << 10); + current_arg++; + } + return mask; +} +/* + * Function Name: ipmi_delloem_setled_main + * + * Description: This function processes the delloem setled command + * Input: intf - ipmi interface + * argc - no of arguments + * argv - argument string array + * Output: + * + * Return: return code 0 - success + * -1 - failure + */ +static int +ipmi_delloem_setled_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int b,d,f, mask; + int bayId, slotId; + bayId = 0xFF; + slotId = 0xFF; + current_arg++; + if (argc < current_arg) { + usage(); + return -1; + } + /* ipmitool delloem setled info*/ + if (argc == 1 || strcmp(argv[current_arg], "help") == 0) { + ipmi_setled_usage(); + return 0; + } + CheckSetLEDSupport(intf); + if (!IsSetLEDSupported()) { + lprintf(LOG_ERR, "'setled' is not supported on this system."); + return -1; + } else if (sscanf(argv[current_arg], "%*x:%x:%x.%x", &b,&d,&f) == 3) { + /* We have bus/dev/function of drive */ + current_arg++; + ipmi_getdrivemap (intf, b, d, f, &bayId, &slotId); + } else if (sscanf(argv[current_arg], "%x:%x.%x", &b,&d,&f) == 3) { + /* We have bus/dev/function of drive */ + current_arg++; + } else { + ipmi_setled_usage(); + return -1; + } + /* Get mask of SES flags */ + mask = ipmi_getsesmask(argc, argv); + /* Get drive mapping */ + if (ipmi_getdrivemap (intf, b, d, f, &bayId, &slotId)) { + return -1; + } + /* Set drive LEDs */ + return ipmi_setled_state (intf, bayId, slotId, mask); +} diff --git a/lib/ipmi_ekanalyzer.c b/lib/ipmi_ekanalyzer.c new file mode 100644 index 0000000..2ac1012 --- /dev/null +++ b/lib/ipmi_ekanalyzer.c @@ -0,0 +1,4195 @@ +/* + * Copyright (c) 2007 Kontron Canada, Inc. All Rights Reserved. + * + * Base on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <ipmitool/ipmi_ekanalyzer.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_strings.h> + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#define NO_MORE_INFO_FIELD 0xc1 +#define TYPE_CODE 0xc0 /*Language code*/ + +/***************************************************************** +* CONSTANT +*****************************************************************/ +const int ERROR_STATUS = -1; +const int OK_STATUS = 0; + +const char * STAR_LINE_LIMITER = + "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"; +const char * EQUAL_LINE_LIMITER = + "================================================================="; +const int SIZE_OF_FILE_TYPE = 3; +const unsigned char AMC_MODULE = 0x80; +const int PICMG_ID_OFFSET = 3; +const unsigned int COMPARE_CANDIDATE = 2; +/*In AMC.0 or PICMG 3.0 specification offset start from 0 with 3 bytes of +* Mfg.ID, 1 byte of Picmg record Id, and +* 1 byte of format version, so the data offset start from 5 +*/ +const int START_DATA_OFFSET = 5; +const int LOWER_OEM_TYPE = 0xf0; +const int UPPER_OEM_TYPE = 0xfe; +const unsigned char DISABLE_PORT = 0x1f; + +const struct valstr ipmi_ekanalyzer_module_type[] = { + { ON_CARRIER_FRU_FILE, "On-Carrier Device" }, + { A1_AMC_FRU_FILE, "AMC slot A1" }, + { A2_AMC_FRU_FILE, "AMC slot A2" }, + { A3_AMC_FRU_FILE, "AMC slot A3" }, + { A4_AMC_FRU_FILE, "AMC slot A4" }, + { B1_AMC_FRU_FILE, "AMC slot B1" }, + { B2_AMC_FRU_FILE, "AMC slot B2" }, + { B3_AMC_FRU_FILE, "AMC slot B3" }, + { B4_AMC_FRU_FILE, "AMC slot B4" }, + { RTM_FRU_FILE, "RTM" }, /*This is OEM specific module*/ + { CONFIG_FILE, "Configuration file" }, + { SHELF_MANAGER_FRU_FILE, "Shelf Manager" }, + { 0xffff , NULL }, +}; + +const struct valstr ipmi_ekanalyzer_IPMBL_addr[] = { + { 0x72, "AMC slot A1" }, + { 0x74, "AMC slot A2" }, + { 0x76, "AMC slot A3" }, + { 0x78, "AMC slot A4" }, + { 0x7a, "AMC slot B1" }, + { 0x7c, "AMC slot B2" }, + { 0x7e, "AMC slot B3" }, + { 0x80, "AMC slot B4" }, + { 0x90, "RTM"}, /*This is OEM specific module*/ + { 0xffff , NULL }, +}; + +const struct valstr ipmi_ekanalyzer_link_type[] = { + { 0x00, "Reserved" }, + { 0x01, "Reserved" }, + { 0x02, "AMC.1 PCI Express" }, + { 0x03, "AMC.1 PCI Express Advanced Switching" }, + { 0x04, "AMC.1 PCI Express Advanced Switching" }, + { 0x05, "AMC.2 Ethernet" }, + { 0x06, "AMC.4 Serial RapidIO" }, + { 0x07, "AMC.3 Storage" }, + /*This is OEM specific module*/ + { 0xf0, "OEM Type 0"}, + { 0xf1, "OEM Type 1"}, + { 0xf2, "OEM Type 2"}, + { 0xf3, "OEM Type 3"}, + { 0xf4, "OEM Type 4"}, + { 0xf5, "OEM Type 5"}, + { 0xf6, "OEM Type 6"}, + { 0xf7, "OEM Type 7"}, + { 0xf8, "OEM Type 8"}, + { 0xf9, "OEM Type 9"}, + { 0xfa, "OEM Type 10"}, + { 0xfb, "OEM Type 11"}, + { 0xfc, "OEM Type 12"}, + { 0xfd, "OEM Type 13"}, + { 0xfe, "OEM Type 14"}, + { 0xff , "Reserved" }, +}; + +/*Reference: AMC.1 specification*/ +const struct valstr ipmi_ekanalyzer_extension_PCIE[] = { + { 0x00, "Gen 1 capable - non SSC" }, + { 0x01, "Gen 1 capable - SSC" }, + { 0x02, "Gen 2 capable - non SSC" }, + { 0x03, "Gen 3 capable - SSC" }, + { 0x0f, "Reserved"}, +}; +/*Reference: AMC.2 specification*/ +const struct valstr ipmi_ekanalyzer_extension_ETHERNET[] = { + { 0x00, "1000BASE-BX (SerDES Gigabit) Ethernet link" }, + { 0x01, "10GBASE-BX4 10 Gigabit Ethernet link" }, +}; +/*Reference: AMC.3 specification*/ +const struct valstr ipmi_ekanalyzer_extension_STORAGE[] = { + { 0x00, "Fibre Channel (FC)" }, + { 0x01, "Serial ATA (SATA)" }, + { 0x02, "Serial Attached SCSI (SAS/SATA)" }, +}; + +const struct valstr ipmi_ekanalyzer_asym_PCIE[] = { + { 0x00, "exact match"}, + { 0x01, "provides a Primary PCI Express Port" }, + { 0x02, "provides a Secondary PCI Express Port" }, +}; + +const struct valstr ipmi_ekanalyzer_asym_STORAGE[] = { + { 0x00, "FC or SAS interface {exact match}" }, + { 0x01, "SATA Server interface" }, + { 0x02, "SATA Client interface" }, + { 0x03, "Reserved" }, +}; + +const struct valstr ipmi_ekanalyzer_picmg_record_id[] = { + { 0x04, "Backplane Point to Point Connectivity Record" }, + { 0x10, "Address Table Record" }, + { 0x11, "Shelf Power Distribution Record" }, + { 0x12, "Shelf Activation and Power Management Record" }, + { 0x13, "Shelf Manager IP Connection Record" }, + { 0x14, "Board Point to Point Connectivity Record" }, + { 0x15, "Radial IPMB-0 Link Mapping Record" }, + { 0x16, "Module Current Requirements Record" }, + { 0x17, "Carrier Activation and Power Management Record" }, + { 0x18, "Carrier Point-to-Point Connectivity Record" }, + { 0x19, "AdvancedMC Point-to-Point Connectivity Record" }, + { 0x1a, "Carrier Information Table" }, + { 0x1b, "Shelf Fan Geography Record" }, + { 0x2c, "Carrier Clock Point-to-Point Connectivity Record" }, + { 0x2d, "Clock Configuration Record" }, +}; + +extern int verbose; + +struct ipmi_ek_multi_header { + struct fru_multirec_header header; + unsigned char * data; + struct ipmi_ek_multi_header * prev; + struct ipmi_ek_multi_header * next; +}; + +struct ipmi_ek_amc_p2p_connectivity_record{ + unsigned char guid_count; + struct fru_picmgext_guid * oem_guid; + unsigned char rsc_id; + unsigned char ch_count; + struct fru_picmgext_amc_channel_desc_record * ch_desc; + unsigned char link_desc_count; + struct fru_picmgext_amc_link_desc_record * link_desc; + int * matching_result; /*For link descriptor comparision*/ +}; + +/***************************************************************************** +* Function prototype +******************************************************************************/ +/**************************************************************************** +* command Functions +*****************************************************************************/ +static int ipmi_ekanalyzer_print( int argc, char * opt, + char ** filename, int * file_type ); + +static tboolean ipmi_ekanalyzer_ekeying_match( int argc, char * opt, + char ** filename, int * file_type ); + +/**************************************************************************** +* Linked list Functions +*****************************************************************************/ +static void ipmi_ek_add_record2list( struct ipmi_ek_multi_header ** record, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last ); + +static void ipmi_ek_display_record( struct ipmi_ek_multi_header * record, + struct ipmi_ek_multi_header * list_head, + struct ipmi_ek_multi_header * list_last ); + +static void ipmi_ek_remove_record_from_list( + struct ipmi_ek_multi_header * record, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last ); + +static int ipmi_ekanalyzer_fru_file2structure( char * filename, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_record, + struct ipmi_ek_multi_header ** list_last ); + +/**************************************************************************** +* Ekeying match Functions +*****************************************************************************/ +static int ipmi_ek_matching_process( int * file_type, int index1, int index2, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last, char * opt, + struct ipmi_ek_multi_header * pphysical ); + +static int ipmi_ek_get_resource_descriptor( int port_count, int index, + struct fru_picmgext_carrier_p2p_descriptor * port_desc, + struct ipmi_ek_multi_header * record ); + +static int ipmi_ek_create_amc_p2p_record( struct ipmi_ek_multi_header * record, + struct ipmi_ek_amc_p2p_connectivity_record * amc_record ); + +static int ipmi_ek_compare_link( struct ipmi_ek_multi_header * physic_record, + struct ipmi_ek_amc_p2p_connectivity_record record1, + struct ipmi_ek_amc_p2p_connectivity_record record2, + char * opt, int file_type1, int file_type2 ); + +static tboolean ipmi_ek_compare_channel_descriptor( + struct fru_picmgext_amc_channel_desc_record ch_desc1, + struct fru_picmgext_amc_channel_desc_record ch_desc2, + struct fru_picmgext_carrier_p2p_descriptor * port_desc, + int index_port, unsigned char rsc_id ); + +static int ipmi_ek_compare_link_descriptor( + struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, + struct ipmi_ek_amc_p2p_connectivity_record record2, int index2 ); + +static int ipmi_ek_compare_asym( unsigned char asym[COMPARE_CANDIDATE] ); + +static int ipmi_ek_compare_number_of_enable_port( + struct fru_picmgext_amc_link_desc_record link_desc[COMPARE_CANDIDATE] ); + +static int ipmi_ek_check_physical_connectivity( + struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, + struct ipmi_ek_amc_p2p_connectivity_record record2, int index2, + struct ipmi_ek_multi_header * record, + int filetype1, int filetype2, char * option ); + +/**************************************************************************** +* Display Functions +*****************************************************************************/ +static int ipmi_ek_display_fru_header( char * filename ); + +static int ipmi_ek_display_fru_header_detail(char * filename); + +static int ipmi_ek_display_chassis_info_area(FILE * input_file, long offset); + +static size_t ipmi_ek_display_board_info_area( FILE * input_file, + char * board_type, unsigned int * board_length ); + +static int ipmi_ek_display_product_info_area(FILE * input_file, long offset); + +static tboolean ipmi_ek_display_link_descriptor( int file_type, + unsigned char rsc_id, char * str, + struct fru_picmgext_amc_link_desc_record link_desc ); + +static void ipmi_ek_display_oem_guid( + struct ipmi_ek_amc_p2p_connectivity_record amc_record1 ); + +static int ipmi_ek_display_carrier_connectivity( + struct ipmi_ek_multi_header * record ); + +static int ipmi_ek_display_power( int argc, char * opt, + char ** filename, int * file_type ); + +static void ipmi_ek_display_current_descriptor( + struct fru_picmgext_carrier_activation_record car, + struct fru_picmgext_activation_record * cur_desc, char * filename ); + +static void ipmi_ek_display_backplane_p2p_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_address_table_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_shelf_power_distribution_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_shelf_activation_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_shelf_ip_connection_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_shelf_fan_geography_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_board_p2p_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_radial_ipmb0_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_amc_current_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_amc_activation_record ( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_amc_p2p_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_amc_carrier_info_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_clock_carrier_p2p_record( + struct ipmi_ek_multi_header * record ); + +static void ipmi_ek_display_clock_config_record( + struct ipmi_ek_multi_header * record ); + +/************************************************************************** +* +* Function name: ipmi_ekanalyzer_usage +* +* Description : Print the usage (help menu) of ekeying analyzer tool +* +* Restriction : None +* +* Input : None +* +* Output : None +* +* Global : None +* +* Return : None +* +***************************************************************************/ +static void +ipmi_ekanalyzer_usage( void ) +{ + lprintf(LOG_NOTICE, "Ekeying analyzer tool version 1.00"); + lprintf(LOG_NOTICE, "ekanalyzer Commands:"); + lprintf(LOG_NOTICE, + " print [carrier | power | all] <oc=filename1> <b1=filename2>..."); + lprintf(LOG_NOTICE, + " frushow <b2=filename>"); + lprintf(LOG_NOTICE, + " summary [match | unmatch | all] <oc=filename1> <b1=filename2>..."); +} + +/************************************************************************** +* +* Function name: ipmi_ek_get_file_type +* +* Description: this function takes an argument, then xtract the file type and +* convert into module type (on carrier, AMC,...) value. +* +* +* Restriction: None +* +* Input: argument: strings contain the type and the name of the file +* together +* +* Output: None +* +* Global: None +* +* Return: Return value of module type: On carrier FRU file, A1 FRUM file... +* if the file type is invalid, it return -1. See structure +* ipmi_ekanalyzer_module_type for a list of valid type. +***************************************************************************/ +static int +ipmi_ek_get_file_type( char * argument ) +{ + int index_name=0; + int filetype = ERROR_STATUS; + + if( strlen (argument) > MIN_ARGUMENT ){ + if( strncmp( argument, "oc=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = ON_CARRIER_FRU_FILE; + } + else if( strncmp( argument, "a1=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = A1_AMC_FRU_FILE; + } + else if( strncmp( argument, "a2=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = A2_AMC_FRU_FILE; + } + else if( strncmp( argument, "a3=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = A3_AMC_FRU_FILE; + } + else if( strncmp( argument, "a4=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = A4_AMC_FRU_FILE; + } + else if( strncmp( argument, "b1=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = B1_AMC_FRU_FILE; + } + else if( strncmp( argument, "b2=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = B2_AMC_FRU_FILE; + } + else if( strncmp( argument, "b3=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = B3_AMC_FRU_FILE; + } + else if( strncmp( argument, "b4=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = B4_AMC_FRU_FILE; + } + else if( strncmp( argument, "rt=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = RTM_FRU_FILE; + } + else if( strncmp( argument, "rc=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = CONFIG_FILE; + } + else if( strncmp( argument, "sm=", SIZE_OF_FILE_TYPE ) == 0 ) { + filetype = SHELF_MANAGER_FRU_FILE; + } + else{ + filetype = ERROR_STATUS; + } + } + return filetype; +} + +/************************************************************************** +* +* Function name: ipmi_ekanalyzer_main +* +* Description: Main program of ekeying analyzer. It calls the appropriate +* function according to the command received. +* +* Restriction: None +* +* Input: ipmi_intf * intf: ? +* int argc : number of argument received +* int ** argv: argument strings +* +* Output: None +* +* Global: None +* +* Return: OK_STATUS as succes or ERROR_STATUS as error +* +***************************************************************************/ +int +ipmi_ekanalyzer_main( struct ipmi_intf * intf, int argc, char ** argv ) +{ + int rc = ERROR_STATUS; + int file_type[MAX_FILE_NUMBER]; + int tmp_ret = 0; + char * filename[MAX_FILE_NUMBER]; + unsigned int argument_offset = 0; + unsigned int type_offset = 0; + /*list des multi record*/ + struct ipmi_ek_multi_header * list_head = NULL; + struct ipmi_ek_multi_header * list_record = NULL; + struct ipmi_ek_multi_header * list_last = NULL; + + if ( (argc == 0) || ( (argc - 1) > MAX_FILE_NUMBER ) ){ + lprintf(LOG_ERR, "Too few or too many arguments!"); + ipmi_ekanalyzer_usage(); + rc = ERROR_STATUS; + } + else if ( strcmp(argv[argument_offset], "help") == 0) { + ipmi_ekanalyzer_usage(); + rc = 0; + } + else if ( (strcmp(argv[argument_offset], "frushow") == 0) + && (argc > (MIN_ARGUMENT-1) ) + ){ + for ( type_offset = 0; type_offset < (argc-1); type_offset++ ){ + argument_offset++; + file_type[type_offset] = ipmi_ek_get_file_type (argv[argument_offset]); + if ( file_type[type_offset] != ERROR_STATUS ){ + if ( file_type[type_offset] != CONFIG_FILE ){ + /* because of strlen doesn't count '\0', we need to add 1 byte for + * this character to filename size + */ + filename[type_offset] = malloc( strlen(argv[argument_offset]) + 1 + - SIZE_OF_FILE_TYPE + ); + if( filename[type_offset] != NULL ){ + strcpy(filename[type_offset], + &argv[argument_offset][SIZE_OF_FILE_TYPE]); + printf("Start converting file '%s'...\n", filename[type_offset]); + /* Display FRU header offset */ + rc = ipmi_ek_display_fru_header (filename[type_offset]); + + if ( rc != ERROR_STATUS ){ + /* Display FRU header info in detail record */ + tmp_ret = ipmi_ek_display_fru_header_detail(filename[type_offset]); + /* Convert from binary data into multi record structure */ + rc = ipmi_ekanalyzer_fru_file2structure ( filename[type_offset], + &list_head, &list_record, &list_last ); + + ipmi_ek_display_record ( list_record, list_head, list_last ); + /* Remove record of list */ + while ( list_head != NULL ){ + ipmi_ek_remove_record_from_list( list_head, + &list_head,&list_last ); + if (verbose > 1) + printf("record has been removed!\n"); + } + } + free(filename[type_offset]); + filename[type_offset] = NULL; + } + } + } + else{ + lprintf(LOG_ERR, "Invalid file type!"); + lprintf(LOG_ERR, " ekanalyzer frushow <xx=frufile> ..."); + } + } + } + else if ( (strcmp(argv[argument_offset], "print") == 0) + || (strcmp(argv[argument_offset], "summary") == 0) + ){ + /*Display help of the correspond command if there is not enought argument + * passing in command line + */ + if ( argc < MIN_ARGUMENT ){ + lprintf(LOG_ERR, "Not enough parameters given."); + if ( strcmp(argv[argument_offset], "print") == 0 ){ + lprintf(LOG_ERR, " ekanalyzer print [carrier/power/all]" + " <xx=frufile> <xx=frufile> [xx=frufile]" + ); + } + else{ + lprintf(LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]" + " <xx=frufile> <xx=frufile> [xx=frufile]" + ); + } + } + else{ + char * option; + /*index=1 indicates start position of first file name in command line*/ + int index = 1; + int filename_size=0; + + argument_offset++; + if ( (strcmp(argv[argument_offset], "carrier") == 0) + || (strcmp(argv[argument_offset], "power") == 0) + || (strcmp(argv[argument_offset], "all") == 0) + ){ + option = argv[argument_offset]; + index ++; + argc--; + } + else if ( ( strcmp(argv[argument_offset], "match") == 0 ) + || ( strcmp(argv[argument_offset], "unmatch") == 0 ) + ){ + option = argv[argument_offset]; + index ++; + argc--; + } + /*since the command line must receive xx=filename, so the position of + * "=" sign is 2 + */ + else if ( strncmp(&argv[argument_offset][2], "=", 1) == 0 ){ + option = "default"; + /* Since there is no option from user, the first argument + * becomes first file type */ + index = 1; /* index of argument */ + } + else{ + option = "invalid"; + printf("Invalid option '%s'\n", argv[argument_offset]); + argument_offset--; + if (strcmp(argv[0], "print") == 0){ + lprintf (LOG_ERR, " ekanalyzer print [carrier/power/all]" + " <xx=frufile> <xx=frufile> [xx=frufile]" + ); + } + else{ + lprintf (LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]" + " <xx=frufile> <xx=frufile> [xx=frufile]" + ); + } + rc = ERROR_STATUS; + } + if ( strcmp(option, "invalid") != 0 ){ + int i=0; + + for ( i = 0; i < (argc-1); i++){ + file_type[i] = ipmi_ek_get_file_type (argv[index]); + if ( file_type[i] == ERROR_STATUS ){ + /* display the first 2 charactors (file type) of argument */ + lprintf(LOG_ERR, "Invalid file type: %c%c\n", argv[index][0], + argv[index][1]); + ipmi_ekanalyzer_usage(); + rc = ERROR_STATUS; + break; + } + /*size is equal to string size minus 3 bytes of file type plus + * 1 byte of '\0' since the strlen doesn't count the '\0' + */ + filename_size = strlen( argv[index] ) - SIZE_OF_FILE_TYPE + 1; + if ( filename_size > 0 ){ + filename[i] = malloc( filename_size ); + if (filename[i] != NULL) + strcpy( filename[i], &argv[index][SIZE_OF_FILE_TYPE] ); + } + rc = OK_STATUS; + index++; + } + if ( rc != ERROR_STATUS ){ + if (verbose > 0){ + for (i = 0; i < (argc-1); i++){ + printf ("Type: %s, ", + val2str(file_type[i], ipmi_ekanalyzer_module_type)); + printf("file name: %s\n", filename[i]); + } + } + if (strcmp(argv[0], "print") == 0){ + rc = ipmi_ekanalyzer_print( + (argc-1), option, filename, file_type); + } + else{ + rc = ipmi_ekanalyzer_ekeying_match( + (argc-1), option, filename, file_type); + } + for (i = 0; i < (argc-1); i++){ + if (filename[i] != NULL){ + free(filename[i]); + filename[i] = NULL; + } + } + } /* End of ERROR_STATUS */ + } /* End of comparison of invalid option */ + } /* End of else MIN_ARGUMENT */ + } /* End of print or summary option */ + else{ + lprintf(LOG_ERR, "Invalid ekanalyzer command: %s", argv[0]); + ipmi_ekanalyzer_usage(); + rc = ERROR_STATUS; + } + + return rc; +} + +/************************************************************************** +* +* Function name: ipmi_ekanalyzer_print +* +* Description: this function will display the topology, power or both +* information together according to the option that it received. +* +* Restriction: None +* +* Input: int argc: number of the argument received +* char* opt: option string that will tell what to display +* char** filename: strings that contained filename of FRU data binary file +* int* file_type: a pointer that contain file type (on carrier file, +* a1 file, b1 file...). See structure +* ipmi_ekanalyzer_module_type for a list of valid type +* +* Output: None +* +* Global: None +* +* Return: return 0 as success and -1 as error. +* +***************************************************************************/ +static int +ipmi_ekanalyzer_print( int argc, char * opt, char ** filename, int * file_type ) +{ + int return_value = OK_STATUS; + + /*Display carrier topology*/ + if ( (strcmp(opt, "carrier") == 0) || (strcmp(opt, "default") == 0) ){ + tboolean found_flag = FALSE; + int index = 0; + int index_name[argc]; + int list = 0; + /*list of multi record*/ + struct ipmi_ek_multi_header * list_head[argc]; + struct ipmi_ek_multi_header * list_record[argc]; + struct ipmi_ek_multi_header * list_last[argc]; + + for ( list=0; list < argc; list++ ){ + list_head[list] = NULL; + list_record[list] = NULL; + list_last[list] = NULL; + } + + list=0; /* reset list count */ + for ( index = 0; index < argc; index++ ){ + if ( file_type[index] == ON_CARRIER_FRU_FILE ){ + index_name[list] = index; + return_value = ipmi_ekanalyzer_fru_file2structure( filename[index], + &list_head[list], &list_record[list], &list_last[list] ); + list++; + found_flag = TRUE; + } + } + if ( !found_flag ){ + printf("No carrier file has been found\n"); + return_value = ERROR_STATUS; + } + else{ + int i = 0; + for ( i = 0; i < argc; i++ ){ + /*this is a flag to advoid displaying the same data multiple time*/ + tboolean first_data = TRUE; + for ( list_record[i] = list_head[i]; + list_record[i] != NULL; + list_record[i] = list_record[i]->next ){ + if ( list_record[i]->data[PICMG_ID_OFFSET] + == + FRU_AMC_CARRIER_P2P ){ + if ( first_data ){ + printf("%s\n", STAR_LINE_LIMITER); + printf("From Carrier file: %s\n", filename[index_name[i]]); + first_data = FALSE; + } + return_value = ipmi_ek_display_carrier_connectivity( + list_record[i] ); + } + else if ( list_record[i]->data[PICMG_ID_OFFSET] + == + FRU_AMC_CARRIER_INFO ){ + /*See AMC.0 specification Table3-3 for mor detail*/ + #define COUNT_OFFSET 6 + if ( first_data ){ + printf("From Carrier file: %s\n", filename[index_name[i]]); + first_data = FALSE; + } + printf(" Number of AMC bays supported by Carrier: %d\n", + list_record[i]->data[COUNT_OFFSET] ); + } + } + } + /*Destroy the list of record*/ + for ( i = 0; i < argc; i++ ){ + while ( list_head[i] != NULL ){ + ipmi_ek_remove_record_from_list( list_head[i], + &list_head[i], &list_last[i] ); + } + /* display deleted result when we reach the last record */ + if ( ( i == (list-1) ) && verbose ) + printf("Record list has been removed successfully\n"); + } + } + } + else if ( (strcmp(opt, "power") == 0) ){ + printf("Print power information\n"); + return_value = ipmi_ek_display_power(argc, opt, filename, file_type); + } + else if ( strcmp(opt, "all") == 0 ){ + printf("Print all information\n"); + return_value = ipmi_ek_display_power(argc, opt, filename, file_type); + } + else{ + lprintf(LOG_ERR, "Invalid option %s", opt); + return_value = ERROR_STATUS; + } + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_carrier_connectivity +* +* Description: Display the topology between a Carrier and all AMC modules by +* using carrier p2p connectivity record +* +* Restriction: Ref: AMC.0 Specification: Table 3-13 and Table 3-14 +* +* Input: struct ipmi_ek_multi_header* record: a pointer to the carrier p2p +* connectivity record. +* +* Output: None +* +* Global: None +* +* Return: return 0 on success and -1 if the record doesn't exist. +* +***************************************************************************/ +static int +ipmi_ek_display_carrier_connectivity( struct ipmi_ek_multi_header * record ) +{ + int return_value = ERROR_STATUS; + struct fru_picmgext_carrier_p2p_record rsc_desc; + struct fru_picmgext_carrier_p2p_descriptor *port_desc; + + if ( record == NULL ){ + lprintf(LOG_ERR, "P2P connectivity record is invalid\n"); + return_value = ERROR_STATUS; + } + else{ + int offset = START_DATA_OFFSET; + if ( verbose > 1 ){ + int k = 0; + printf("Binary data of Carrier p2p connectivity"\ + " record starting from mfg id\n"); + for ( k = 0; k < ( record->header.len ); k++ ){ + printf("%02x ", record->data[k]); + } + printf("\n"); + } + while ( offset <= (record->header.len - START_DATA_OFFSET) ){ + rsc_desc.resource_id = record->data[offset++]; + rsc_desc.p2p_count = record->data[offset++]; + if ( verbose > 0 ){ + printf("resource id= %02x port count= %d\n", + rsc_desc.resource_id,rsc_desc.p2p_count); + } + /*check if it is an AMC Module*/ + if ( ( (rsc_desc.resource_id & AMC_MODULE) ) == AMC_MODULE ) { + /*check if it is an RTM module*/ + if ((rsc_desc.resource_id == AMC_MODULE)){ + printf(" %s topology:\n", val2str( RTM_IPMB_L, + ipmi_ekanalyzer_IPMBL_addr)); + } + else{ + /*The last four bits of resource ID represent site number + * (mask = 0x0f) + */ + printf(" %s topology:\n", + val2str( (rsc_desc.resource_id & 0x0f), + ipmi_ekanalyzer_module_type)); + } + } + else{ + printf(" On Carrier Device ID %d topology: \n", + (rsc_desc.resource_id & 0x0f)); + } + while ( rsc_desc.p2p_count > 0 ){ + unsigned char data[3]; +#ifndef WORDS_BIGENDIAN + data[0] = record->data[offset+0]; + data[1] = record->data[offset+1]; + data[2] = record->data[offset+2]; +#else + data[0] = record->data[offset+2]; + data[1] = record->data[offset+1]; + data[2] = record->data[offset+0]; +#endif + port_desc = (struct fru_picmgext_carrier_p2p_descriptor*)data; + offset += sizeof (struct fru_picmgext_carrier_p2p_descriptor); + if ((port_desc->remote_resource_id & AMC_MODULE) == AMC_MODULE) { + printf("\tPort %d =====> %s, Port %d\n", + port_desc->local_port, + val2str( (port_desc->remote_resource_id & 0x0f), + ipmi_ekanalyzer_module_type), + port_desc->remote_port); + } + else { + printf("\tPort %d =====> On Carrier Device ID %d, Port %d\n", + port_desc->local_port, + (port_desc->remote_resource_id & 0x0f), + port_desc->remote_port); + } + rsc_desc.p2p_count--; + } + } + return_value = OK_STATUS; + } + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_power +* +* Description: Display the power management of the Carrier and AMC module by +* using current management record. If the display option equal to all, +* it will display power and carrier topology together. +* +* Restriction: Reference: AMC.0 Specification, Table 3-11 +* +* Input: int argc: number of the argument received +* char* opt: option string that will tell what to display +* char** filename: strings that contained filename of FRU data binary file +* int* file_type: a pointer that contain file type (on carrier file, +* a1 file, b1 file...) +* +* Output: None +* +* Global: None +* +* Return: return 0 on success and -1 if the record doesn't exist. +* +***************************************************************************/ +static int +ipmi_ek_display_power( int argc, char * opt, char ** filename, int * file_type ) +{ + int num_file=0; + int return_value = ERROR_STATUS; + int index = 0; + + /*list des multi record*/ + struct ipmi_ek_multi_header * list_head[argc]; + struct ipmi_ek_multi_header * list_record[argc]; + struct ipmi_ek_multi_header * list_last[argc]; + + for ( num_file = 0; num_file < argc; num_file++ ){ + list_head[num_file] = NULL; + list_record[num_file] = NULL; + list_last[num_file] = NULL; + } + + for ( num_file = 0; num_file < argc; num_file++ ){ + tboolean is_first_data = TRUE; + if ( file_type[num_file] == CONFIG_FILE ){ + num_file++; + } + + if ( is_first_data ){ + printf("%s\n", STAR_LINE_LIMITER); + printf("\nFrom %s file '%s'\n", + val2str( file_type[num_file], ipmi_ekanalyzer_module_type), + filename[num_file]); + is_first_data = FALSE; + } + + return_value = ipmi_ekanalyzer_fru_file2structure( filename[num_file], + &list_head[num_file], &list_record[num_file], &list_last[num_file]); + + if ( list_head[num_file] != NULL ){ + for ( list_record[num_file] = list_head[num_file]; + list_record[num_file] != NULL; + list_record[num_file] = list_record[num_file]->next + ){ + if ( ( strcmp(opt, "all") == 0 ) + && ( file_type[num_file] == ON_CARRIER_FRU_FILE ) + ){ + if ( list_record[num_file]->data[PICMG_ID_OFFSET] + == + FRU_AMC_CARRIER_P2P + ){ + return_value = ipmi_ek_display_carrier_connectivity( + list_record[num_file] ); + } + else if ( list_record[num_file]->data[PICMG_ID_OFFSET] + == + FRU_AMC_CARRIER_INFO + ){ + /*Ref: See AMC.0 Specification Table 3-3: Carrier Information + * Table about offset value + */ + printf( " Number of AMC bays supported by Carrier: %d\n", + list_record[num_file]->data[START_DATA_OFFSET+1] ); + } + } + /*Ref: AMC.0 Specification: Table 3-11 + * Carrier Activation and Current Management Record + */ + if ( list_record[num_file]->data[PICMG_ID_OFFSET] + == + FRU_AMC_ACTIVATION + ){ + int index_data = START_DATA_OFFSET; + struct fru_picmgext_carrier_activation_record car; + struct fru_picmgext_activation_record * cur_desc; + + memcpy ( &car, &list_record[num_file]->data[index_data], + sizeof (struct fru_picmgext_carrier_activation_record) ); + index_data += + sizeof (struct fru_picmgext_carrier_activation_record); + cur_desc = malloc (car.module_activation_record_count * \ + sizeof (struct fru_picmgext_activation_record) ); + for(index=0; index<car.module_activation_record_count; index++){ + memcpy( &cur_desc[index], + &list_record[num_file]->data[index_data], + sizeof (struct fru_picmgext_activation_record) ); + + index_data += sizeof (struct fru_picmgext_activation_record); + } + /*Display the current*/ + ipmi_ek_display_current_descriptor( car, + cur_desc, filename[num_file] ); + free(cur_desc); + cur_desc = NULL; + } + /*Ref: AMC.0 specification, Table 3-10: Module Current Requirement*/ + else if ( list_record[num_file]->data[PICMG_ID_OFFSET] + == FRU_AMC_CURRENT + ){ + float power_in_watt = 0; + float current_in_amp = 0; + + printf(" %s power required (Current Draw): ", + val2str ( file_type[num_file], ipmi_ekanalyzer_module_type) ); + current_in_amp = + list_record[num_file]->data[START_DATA_OFFSET]*0.1; + power_in_watt = current_in_amp * AMC_VOLTAGE; + printf("%.2f Watts (%.2f Amps)\n",power_in_watt, current_in_amp); + } + } + return_value = OK_STATUS; + /*Destroy the list of record*/ + for ( index = 0; index < argc; index++ ){ + while ( list_head[index] != NULL ){ + ipmi_ek_remove_record_from_list ( list_head[index], + &list_head[index],&list_last[index] ); + } + if ( verbose > 1 ) + printf("Record list has been removed successfully\n"); + } + } + } + printf("%s\n", STAR_LINE_LIMITER); + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_current_descriptor +* +* Description: Display the current descriptor under format xx Watts (xx Amps) +* +* Restriction: None +* +* Input: struct fru_picmgext_carrier_activation_record car: contain binary data +* of carrier activation record +* struct fru_picmgext_activation_record * cur_desc: contain current +* descriptor +* char* filename: strings that contained filename of FRU data binary file +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_current_descriptor( + struct fru_picmgext_carrier_activation_record car, + struct fru_picmgext_activation_record * cur_desc, char * filename ) +{ + int index = 0; + float power_in_watt = 0.0; + float current_in_amp = 0.0; + + for ( index = 0; index < car.module_activation_record_count; index++ ){ + /*See AMC.0 specification, Table 3-12 for detail about calculation*/ + current_in_amp = (float) cur_desc[index].max_module_curr * 0.1; + power_in_watt = (float) current_in_amp * AMC_VOLTAGE; + + printf(" Carrier AMC power available on %s:\n", + val2str( cur_desc[index].ibmb_addr, ipmi_ekanalyzer_IPMBL_addr ) ); + printf("\t- Local IPMB Address \t: %02x\n", cur_desc[index].ibmb_addr); + printf("\t- Maximum module Current\t: %.2f Watts (%.2f Amps)\n", + power_in_watt, current_in_amp ); + } + /*Display total power on Carrier*/ + current_in_amp = (float) car.max_internal_curr * 0.1; + power_in_watt = (float) current_in_amp * AMC_VOLTAGE; + printf(" Carrier AMC total power available for all bays from file '%s':", + filename); + printf(" %.2f Watts (%.2f Amps)\n", power_in_watt, current_in_amp ); +} + +/************************************************************************** +* +* Function name: ipmi_ekanalyzer_ekeying_match +* +* Description: Check for possible Ekeying match between two FRU files +* +* Restriction: None +* +* Input: argc: number of the argument received +* opt: string that contains display option received from user. +* filename: strings that contained filename of FRU data binary file +* file_type: a pointer that contain file type (on carrier file, +* a1 file, b1 file...) +* +* Output: None +* +* Global: None +* +* Return: return TRUE on success and FALSE if the record doesn't exist. +* +***************************************************************************/ +static tboolean +ipmi_ekanalyzer_ekeying_match( int argc, char * opt, + char ** filename, int * file_type ) +{ + tboolean return_value = FALSE; + + if ( (strcmp(opt, "carrier") == 0 ) || (strcmp(opt, "power") == 0) ){ + lprintf(LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]"\ + " <xx=frufile> <xx=frufile> [xx=frufile]"); + return_value = ERROR_STATUS; + } + else{ + int num_file=0; + tboolean amc_file = FALSE; /*used to indicate the present of AMC file*/ + tboolean oc_file = FALSE; /*used to indicate the present of Carrier file*/ + + /*Check for possible ekeying match between files*/ + for ( num_file=0; num_file < argc; num_file++ ){ + if ( ( file_type[num_file] == ON_CARRIER_FRU_FILE ) + || ( file_type[num_file] == CONFIG_FILE ) + || ( file_type[num_file] == SHELF_MANAGER_FRU_FILE ) + ){ + amc_file = FALSE; + } + else { /*there is an amc file*/ + amc_file = TRUE; + break; + } + } + if ( amc_file == FALSE ){ + printf("\nNo AMC FRU file is provided --->" \ + " No possible ekeying match!\n"); + return_value = ERROR_STATUS; + } + else{ + /*If no carrier file is provided, return error*/ + for ( num_file=0; num_file < argc; num_file++ ){ + if ( (file_type[num_file] == ON_CARRIER_FRU_FILE ) + || ( file_type[num_file] == CONFIG_FILE ) + || ( file_type[num_file] == SHELF_MANAGER_FRU_FILE ) + ){ + oc_file = TRUE; + break; + } + } + if ( !oc_file ){ + printf("\nNo Carrier FRU file is provided" \ + " ---> No possible ekeying match!\n"); + return_value = ERROR_STATUS; + } + else{ + /*list des multi record*/ + struct ipmi_ek_multi_header * list_head[argc]; + struct ipmi_ek_multi_header * list_record[argc]; + struct ipmi_ek_multi_header * list_last[argc]; + struct ipmi_ek_multi_header * pcarrier_p2p; + int list = 0; + int match_pair = 0; + + /*Create an empty list*/ + for ( list=0; list<argc; list++ ){ + list_head[list] = NULL; + list_record[list] = NULL; + list_last[list] = NULL; + } + list=0; + + for ( num_file=0; num_file < argc; num_file++ ){ + if (file_type[num_file] != CONFIG_FILE){ + return_value = ipmi_ekanalyzer_fru_file2structure( + filename[num_file], &list_head[num_file], + &list_record[num_file], &list_last[num_file]); + } + } + /*Get Carrier p2p connectivity record for physical check*/ + for (num_file=0; num_file < argc; num_file++){ + if (file_type[num_file] == ON_CARRIER_FRU_FILE ){ + for ( pcarrier_p2p=list_head[num_file]; + pcarrier_p2p != NULL ; + pcarrier_p2p = pcarrier_p2p->next + ){ + if ( pcarrier_p2p->data[PICMG_ID_OFFSET] + == FRU_AMC_CARRIER_P2P + ){ + break; + } + } + break; + } + } + /*Determine the match making pair*/ + while ( match_pair < argc ){ + for ( num_file = (match_pair+1); num_file<argc; num_file++ ){ + if ( ( file_type[match_pair] != CONFIG_FILE ) + && ( file_type[num_file] != CONFIG_FILE ) + ){ + if ( ( file_type[match_pair] != ON_CARRIER_FRU_FILE ) + || ( file_type[num_file] != ON_CARRIER_FRU_FILE ) + ){ + printf("%s vs %s\n", + val2str(file_type[match_pair], + ipmi_ekanalyzer_module_type), + val2str(file_type[num_file], + ipmi_ekanalyzer_module_type)); + /*Ekeying match between 2 files*/ + if (verbose>0){ + printf("Start matching process\n"); + } + return_value = ipmi_ek_matching_process( file_type, + match_pair, num_file, list_head, + list_last, opt, pcarrier_p2p); + } + } + } + match_pair ++; + } + for( num_file=0; num_file < argc; num_file++ ){ + if (list_head[num_file] != NULL ){ + ipmi_ek_remove_record_from_list( list_head[num_file], + &list_record[num_file], &list_last[num_file]); + } + if ( ( num_file == argc-1 ) && verbose ) + printf("Record list has been removed successfully\n"); + } + return_value = OK_STATUS; + } + } + } + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_matching_process +* +* Description: This function process the OEM check, Physical Connectivity check, +* and Link Descriptor comparison to do Ekeying match +* +* Restriction: None +* +* Input: file_type: a pointer that contain file type (on carrier file, +* a1 file, b1 file...) +* index1: position of the first record in the list of the record +* index2: position of the second record in the list of the record +* ipmi_ek_multi_header ** list_head: pointer to the header of a +* linked list that contain FRU multi record +* ipmi_ek_multi_header ** list_last: pointer to the tale of a +* linked list that contain FRU multi record +* opt: string that contain display option such as "match", "unmatch", or +* "all". +* pphysical: a pointer that contain a carrier p2p connectivity record +* to perform physical check +* +* Output: None +* +* Global: None +* +* Return: return OK_STATUS on success and ERROR_STATUS if the record doesn't +* exist. +* +***************************************************************************/ +static int ipmi_ek_matching_process( int * file_type, int index1, int index2, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last, char * opt, + struct ipmi_ek_multi_header * pphysical ) +{ + int result = ERROR_STATUS; + struct ipmi_ek_multi_header * record; + int num_amc_record1 = 0;/*Number of AMC records in the first module*/ + int num_amc_record2 = 0;/*Number of AMC records in the second module*/ + + /* Comparison between an On-Carrier and an AMC*/ + if ( file_type[index2] == ON_CARRIER_FRU_FILE ){ + int index_temp = 0; + index_temp = index1; + index1 = index2; /*index1 indicate on carrier*/ + index2 = index_temp; /*index2 indcate an AMC*/ + } + /*Calculate record size for Carrier file*/ + for ( record=list_head[index1]; record != NULL;record = record->next ){ + if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ + num_amc_record2++; + } + } + /*Calculate record size for amc file*/ + for ( record=list_head[index2]; record != NULL;record = record->next){ + if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ + num_amc_record1++; + } + } + if ( (num_amc_record1 > 0) && (num_amc_record2 > 0) ){ + int index_record1 = 0; + int index_record2 = 0; + /* Multi records of AMC module */ + struct ipmi_ek_amc_p2p_connectivity_record * amc_record1 = NULL; + /* Multi records of Carrier or an AMC module */ + struct ipmi_ek_amc_p2p_connectivity_record * amc_record2 = NULL; + + amc_record1 = malloc ( num_amc_record1 * \ + sizeof(struct ipmi_ek_amc_p2p_connectivity_record)); + amc_record2 = malloc ( num_amc_record2 * \ + sizeof(struct ipmi_ek_amc_p2p_connectivity_record)); + + for (record=list_head[index2]; record != NULL;record = record->next){ + if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ + result = ipmi_ek_create_amc_p2p_record( record, + &amc_record1[index_record1] ); + if (result != ERROR_STATUS){ + struct ipmi_ek_multi_header * current_record = NULL; + + for ( current_record=list_head[index1]; + current_record != NULL ; + current_record = current_record->next + ){ + if ( current_record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ + result = ipmi_ek_create_amc_p2p_record( current_record, + &amc_record2[index_record2] ); + if ( result != ERROR_STATUS ){ + if ( result == OK_STATUS ){ + /*Compare Link descriptor*/ + result = ipmi_ek_compare_link ( pphysical, + amc_record1[index_record1], + amc_record2[index_record2], + opt, file_type[index1], file_type[index2]); + } + index_record2++; + } + } /*end of FRU_AMC_P2P */ + } /* end of for loop */ + index_record1++; + } + } + } + free(amc_record1) ; + amc_record1 = NULL; + free(amc_record2) ; + amc_record2 = NULL; + } + else{ + printf("No amc record is found!\n"); + } + + return result; +} + +/************************************************************************** +* +* Function name: ipmi_ek_check_physical_connectivity +* +* Description: This function check for point to point connectivity between +* two modules by comparing each enable port in link descriptor +* with local and remote ports of port descriptor in +* carrier point-to-point connectivity record according to the +* corresponding file type ( a1, b1, b2...). +* +* Restriction: In order to perform physical check connectivity, it needs to +* compare between 2 AMC Modules, so the use of index ( 1 and 2 ) +* can facilitate the comparison in this case. +* +* Input: record1: is an AMC p2p record for an AMC module +* record2 is an AMC p2p record for an On-Carrier record or an AMC module +* char* opt: option string that will tell if a matching result, unmatched +* result or all the results will be displayed. +* file_type1: indicates type of the first module +* file_type2: indicates type of the second module +* +* Output: None +* +* Global: None +* +* Return: return OK_STATUS if both link are matched, otherwise +* return ERROR_STATUS +* +***************************************************************************/ +static int +ipmi_ek_check_physical_connectivity( + struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, + struct ipmi_ek_amc_p2p_connectivity_record record2, int index2, + struct ipmi_ek_multi_header * record, + int filetype1, int filetype2, char * option ) +{ + int return_status = OK_STATUS; + + if ( record == NULL ){ + printf("NO Carrier p2p connectivity !\n"); + return_status = ERROR_STATUS; + } + else{ + #define INVALID_AMC_SITE_NUMBER -1 + int index = START_DATA_OFFSET; + int amc_site = INVALID_AMC_SITE_NUMBER; + struct fru_picmgext_carrier_p2p_record rsc_desc; + struct fru_picmgext_carrier_p2p_descriptor * port_desc = NULL; + + /* Get the physical connectivity record */ + while ( index < record->header.len ) { + rsc_desc.resource_id = record->data[index++]; + rsc_desc.p2p_count = record->data[index++]; + /* carrier p2p record starts with on-carrier device */ + if ( (rsc_desc.resource_id == record1.rsc_id) + || + (rsc_desc.resource_id == record2.rsc_id) + ){ + if (rsc_desc.p2p_count <= 0){ + printf("No p2p count\n"); + return_status = ERROR_STATUS; + } + else{ + port_desc = malloc ( rsc_desc.p2p_count * + sizeof(struct fru_picmgext_carrier_p2p_descriptor) ); + index = ipmi_ek_get_resource_descriptor( rsc_desc.p2p_count, + index, port_desc, record ); + amc_site = INVALID_AMC_SITE_NUMBER; + break; + } + } + else{ /* carrier p2p record starts with AMC module */ + if (rsc_desc.resource_id == AMC_MODULE){ + if (filetype1 != ON_CARRIER_FRU_FILE){ + amc_site = filetype1; + } + else{ + amc_site = filetype2; + } + } + else{ + amc_site = rsc_desc.resource_id & 0x0f; + } + if ( amc_site > 0 ){ + if ( (amc_site == filetype1) || (amc_site == filetype2) ){ + port_desc = malloc ( rsc_desc.p2p_count * + sizeof(struct fru_picmgext_carrier_p2p_descriptor) ); + index = ipmi_ek_get_resource_descriptor( rsc_desc.p2p_count, + index, port_desc, record ); + break; + } + } + else{ + return_status = ERROR_STATUS; + } + } + /*If the record doesn't contain the same AMC site number in command + * line, go to the next record + */ + index += ( sizeof(struct fru_picmgext_carrier_p2p_descriptor) * + rsc_desc.p2p_count ); + } + + if ( (port_desc != NULL) && (return_status != ERROR_STATUS) ){ + int j=0; + + for ( j = 0; j < rsc_desc.p2p_count; j++ ){ + /* Compare only enable channel descriptor */ + if ( record1.ch_desc[index1].lane0port != DISABLE_PORT ){ + /* matching result from channel descriptor comparison */ + tboolean match_lane = FALSE; + + match_lane = ipmi_ek_compare_channel_descriptor ( + record1.ch_desc[index1], record2.ch_desc[index2], + port_desc, j, rsc_desc.resource_id ); + + if ( match_lane ){ + if ( filetype1 != ON_CARRIER_FRU_FILE ){ + if ( ( + (filetype1 == (rsc_desc.resource_id & 0x0f)) + && + (filetype2 ==(port_desc[j].remote_resource_id &0x0f)) + ) + || + ( + (filetype2 == (rsc_desc.resource_id & 0x0f)) + && + (filetype1 ==(port_desc[j].remote_resource_id &0x0f)) + ) + ){ + if ( ! (strcmp(option, "unmatch") == 0) ){ + printf("%s port %d ==> %s port %d\n", + val2str(filetype2, ipmi_ekanalyzer_module_type), + record1.ch_desc[index1].lane0port, + val2str(filetype1, ipmi_ekanalyzer_module_type), + record2.ch_desc[index2].lane0port); + } + return_status = OK_STATUS; + + break; + } + else{ + if (verbose == LOG_DEBUG){ + printf("No point 2 point connectivity\n"); + } + return_status = ERROR_STATUS; + } + } + else{ + if ( (record2.rsc_id == (rsc_desc.resource_id) ) + && + (filetype2 == (port_desc[j].remote_resource_id & 0x0f)) + ){ + if ( ! (strcmp(option, "unmatch") == 0) ){ + printf("%s port %d ==> %s port %d\n", + val2str(filetype2, ipmi_ekanalyzer_module_type), + record1.ch_desc[index1].lane0port, + val2str(filetype1, ipmi_ekanalyzer_module_type), + record2.ch_desc[index2].lane0port); + } + return_status = OK_STATUS; + break; + } + else if ( (filetype2 == (rsc_desc.resource_id & 0x0f) ) + && + (record2.rsc_id == (port_desc[j].remote_resource_id)) + ){ + if ( ! (strcmp(option, "unmatch") == 0) ){ + printf("%s port %d ==> %s %x port %d\n", + val2str(filetype2, ipmi_ekanalyzer_module_type), + record1.ch_desc[index1].lane0port, + val2str(filetype1, ipmi_ekanalyzer_module_type), + record2.rsc_id,record2.ch_desc[index2].lane0port); + } + return_status = OK_STATUS; + break; + } + else{ + if (verbose == LOG_DEBUG){ + printf("No point 2 point connectivity\n"); + } + return_status = ERROR_STATUS; + } + } + } + else{ + if (verbose == LOG_DEBUG){ + printf("No point 2 point connectivity\n"); + } + return_status = ERROR_STATUS; + } + } + else{ /*If the link is disable, the result is always true*/ + return_status = OK_STATUS; + } + } + } + else{ + if (verbose == LOG_WARN){ + printf("Invalid Carrier p2p connectivity record\n"); + } + return_status = ERROR_STATUS; + } + if (port_desc != NULL){ + free(port_desc); + port_desc = NULL; + } + } + return return_status; +} + +/************************************************************************** +* +* Function name: ipmi_ek_compare_link +* +* Description: This function compares link grouping id of each +* amc p2p connectiviy record +* +* Restriction: None +* +* Input: record1: is an AMC p2p record for an AMC module +* record2 is an AMC p2p record for an On-Carrier record or an AMC module +* char* opt: option string that will tell if a matching result, unmatched +* result or all the results will be displayed. +* file_type1: indicates type of the first module +* file_type2: indicates type of the second module +* +* Output: None +* +* Global: None +* +* Return: return 0 if both link are matched, otherwise return -1 +* +***************************************************************************/ +static int +ipmi_ek_compare_link( struct ipmi_ek_multi_header * physic_record, + struct ipmi_ek_amc_p2p_connectivity_record record1, + struct ipmi_ek_amc_p2p_connectivity_record record2, char * opt, + int file_type1, int file_type2 ) +{ + int result = ERROR_STATUS; + int index1 = 0; /*index for AMC module*/ + int index2 = 0; /*index for On-carrier type*/ + + record1.matching_result = malloc ( record1.link_desc_count * sizeof(int) ); + record2.matching_result = malloc ( record2.link_desc_count * sizeof(int) ); + /*Initialize all the matching_result to false*/ + for( index2 = 0; index2 < record2.link_desc_count; index2++ ){ + record2.matching_result[index2] = FALSE; + } + for( index1 = 0; index1 < record1.link_desc_count; index1++ ){ + for( index2 = 0; index2 < record2.link_desc_count; index2++ ){ + if( record1.link_desc[index1].group_id == 0 ){ + if( record2.link_desc[index2].group_id == 0 ){ + result = ipmi_ek_compare_link_descriptor( + record1, index1, record2, index2 ); + if ( result == OK_STATUS ){ + /*Calculate the index for Channel descriptor in function of + * link designator channel ID + */ + /*first channel_id in the AMC Link descriptor of record1*/ + static int flag_first_link1; + int index_ch_desc1; /*index of channel descriptor */ + /*first channel_id in the AMC Link descriptor of record2*/ + static int flag_first_link2; + int index_ch_desc2; /*index of channel descriptor*/ + + if (index1==0){ /*this indicate the first link is encounter*/ + flag_first_link1 = record1.link_desc[index1].channel_id; + } + index_ch_desc1 = record1.link_desc[index1].channel_id - + flag_first_link1; + if (index2==0){ + flag_first_link2 = record2.link_desc[index2].channel_id; + } + index_ch_desc2 = record2.link_desc[index2].channel_id - + flag_first_link2; + /*Check for physical connectivity for each link*/ + result = ipmi_ek_check_physical_connectivity ( record1, + index_ch_desc1, record2, index_ch_desc2, + physic_record, file_type1, file_type2, opt ); + if ( result == OK_STATUS ){ + /*Display the result if option = match or all*/ + if ( (strcmp( opt, "match" ) == 0) + || (strcmp( opt, "all" ) == 0) + || (strcmp( opt, "default" ) == 0) + ){ + tboolean isOEMtype = FALSE; + printf(" Matching Result\n"); + isOEMtype = ipmi_ek_display_link_descriptor( file_type1, + record2.rsc_id, + "From", record2.link_desc[index2]); + if (isOEMtype){ + ipmi_ek_display_oem_guid (record2); + } + isOEMtype = ipmi_ek_display_link_descriptor( file_type2, + record1.rsc_id, + "To", record1.link_desc[index1] ); + if (isOEMtype){ + ipmi_ek_display_oem_guid (record1); + } + printf(" %s\n", STAR_LINE_LIMITER); + } + record2.matching_result[index2] = TRUE; + record1.matching_result[index1] = TRUE; + /*quit the fist loop since the match is found*/ + index2 = record2.link_desc_count; + } + } + } + } + else { /*Link Grouping ID is non zero, Compare all link descriptor + * that has non-zero link grouping id together + */ + if (record2.link_desc[index2].group_id != 0 ){ + result = ipmi_ek_compare_link_descriptor( + record1, index1, record2, index2 ); + if ( result == OK_STATUS ){ + /*Calculate the index for Channel descriptor in function of + * link designator channel ID + */ + /*first channel_id in the AMC Link descriptor of record1*/ + static int flag_first_link1; + int index_ch_desc1; /*index of channel descriptor */ + /*first channel_id in the AMC Link descriptor of record2*/ + static int flag_first_link2; + int index_ch_desc2; /*index of channel descriptor*/ + + if (index1==0){ /*this indicate the first link is encounter*/ + flag_first_link1 = record1.link_desc[index1].channel_id; + } + index_ch_desc1 = record1.link_desc[index1].channel_id - + flag_first_link1; + if (index2==0){ + flag_first_link2 = record2.link_desc[index2].channel_id; + } + index_ch_desc2 = record2.link_desc[index2].channel_id - + flag_first_link2; + /*Check for physical connectivity for each link*/ + result = ipmi_ek_check_physical_connectivity ( + record1, index_ch_desc1, record2, index_ch_desc2, + physic_record, file_type1, file_type2, opt ); + if ( result == OK_STATUS ){ + if ( (strcmp( opt, "match" ) == 0) + || (strcmp( opt, "all" ) == 0) + || (strcmp( opt, "default" ) == 0) + ){ + tboolean isOEMtype = FALSE; + printf(" Matching Result\n"); + isOEMtype = ipmi_ek_display_link_descriptor( file_type1, + record2.rsc_id, + "From", record2.link_desc[index2] ); + if ( isOEMtype ){ + ipmi_ek_display_oem_guid (record2); + } + isOEMtype = ipmi_ek_display_link_descriptor( file_type2, + record1.rsc_id, + "To", record1.link_desc[index1] ); + if (isOEMtype){ + ipmi_ek_display_oem_guid (record1); + } + printf(" %s\n", STAR_LINE_LIMITER); + } + record2.matching_result[index2] = TRUE; + record1.matching_result[index1] = TRUE; + /*leave the fist loop since the match is found*/ + index2 = record2.link_desc_count; + } + } + } + } + } + } + + if ( (strcmp(opt, "unmatch") == 0) || (strcmp(opt, "all") == 0) ){ + int isOEMtype = FALSE; + printf(" Unmatching result\n"); + for (index1 = 0; index1 < record1.link_desc_count; index1++){ + isOEMtype = ipmi_ek_display_link_descriptor( file_type2, + record1.rsc_id, "", record1.link_desc[index1] ); + if ( isOEMtype ){ + ipmi_ek_display_oem_guid (record1); + } + printf(" %s\n", STAR_LINE_LIMITER); + } + for ( index2 = 0; index2 < record2.link_desc_count; index2++){ + if ( !record2.matching_result[index2] ){ + isOEMtype = ipmi_ek_display_link_descriptor( file_type1, + record2.rsc_id, "", record2.link_desc[index2] ); + if ( isOEMtype ){ + ipmi_ek_display_oem_guid (record2); + } + printf(" %s\n", STAR_LINE_LIMITER); + } + } + } + + free(record1.matching_result); + record1.matching_result = NULL; + free(record2.matching_result); + record2.matching_result = NULL; + + return result; +} + +/************************************************************************** +* +* Function name: ipmi_ek_compare_channel_descriptor +* +* Description: This function compares 2 channel descriptors of 2 AMC +* point-to-point connectivity records with port descriptor of +* carrier point-to-point connectivity record. The comparison is +* made between each enable port only. +* +* Restriction: Reference: AMC.0 specification: +* - Table 3-14 for port descriptor +* - Table 3-17 for channel descriptor +* +* Input: ch_desc1: first channel descriptor +* ch_desc2: second channel descriptor +* port_desc: a pointer that contain a list of port descriptor +* index_port: index of the port descriptor +* rsc_id: resource id that represents as local resource id in the +* resource descriptor table. +* +* Output: None +* +* Global: None +* +* Return: return TRUE if both channel descriptor are matched, +* or FALSE otherwise +* +***************************************************************************/ +static tboolean +ipmi_ek_compare_channel_descriptor( + struct fru_picmgext_amc_channel_desc_record ch_desc1, + struct fru_picmgext_amc_channel_desc_record ch_desc2, + struct fru_picmgext_carrier_p2p_descriptor * port_desc, + int index_port, unsigned char rsc_id ) +{ + tboolean match_lane = FALSE; + + /* carrier p2p record start with AMC_MODULE as local port */ + if ( (rsc_id & AMC_MODULE) == AMC_MODULE ){ + if ( (ch_desc1.lane0port == port_desc[index_port].local_port) + && + (ch_desc2.lane0port == port_desc[index_port].remote_port) + ){ + /*check if the port is enable*/ + if (ch_desc1.lane1port != DISABLE_PORT){ + index_port ++; + if ( (ch_desc1.lane1port == port_desc[index_port].local_port) + && + (ch_desc2.lane1port == port_desc[index_port].remote_port) + ){ + if (ch_desc1.lane2port != DISABLE_PORT){ + index_port++; + if ( (ch_desc1.lane2port == port_desc[index_port].local_port) + && + (ch_desc2.lane2port == port_desc[index_port].remote_port) + ){ + if (ch_desc1.lane3port != DISABLE_PORT){ + index_port++; + if ( (ch_desc1.lane3port == + port_desc[index_port].local_port) + && + (ch_desc2.lane3port == + port_desc[index_port].remote_port) + ){ + match_lane = TRUE; + } + } + else{ + match_lane = TRUE; + } + } /* end of if lane2port */ + } + else{ + match_lane = TRUE; + } + } /* end of if lane1port */ + } + else{ /*if the port is disable, the compare result is always true*/ + match_lane = TRUE; + } + }/* end of if lane0port */ + } + /* carrier p2p record start with Carrier as local port */ + else{ + if ( (ch_desc1.lane0port == port_desc[index_port].remote_port) + && + (ch_desc2.lane0port == port_desc[index_port].local_port) + ){ + if (ch_desc1.lane1port != DISABLE_PORT){ + index_port ++; + if ( (ch_desc1.lane1port == port_desc[index_port].remote_port) + && + (ch_desc2.lane1port == port_desc[index_port].local_port) + ){ + if (ch_desc1.lane2port != DISABLE_PORT){ + index_port++; + if ( (ch_desc1.lane2port == port_desc[index_port].remote_port) + && + (ch_desc2.lane2port == port_desc[index_port].local_port) + ){ + if (ch_desc1.lane3port != DISABLE_PORT){ + index_port++; + if ( (ch_desc1.lane3port == + port_desc[index_port].remote_port) + && + (ch_desc2.lane3port == + port_desc[index_port].local_port) + ){ + match_lane = TRUE; + } + } + else{ + match_lane = TRUE; + } + } /* end of if lane2port */ + } + else{ + match_lane = TRUE; + } + } /* end of if lane1port */ + } + else{ + match_lane = TRUE; + } + } /* end of if lane0port */ + } + + return match_lane; +} + +/************************************************************************** +* +* Function name: ipmi_ek_compare_link_descriptor +* +* Description: This function compares 2 link descriptors of 2 +* amc p2p connectiviy record +* +* Restriction: None +* +* Input: record1: AMC p2p connectivity record of the 1rst AMC or Carrier Module +* index1: index of AMC link descriptor in 1rst record +* record2: AMC p2p connectivity record of the 2nd AMC or Carrier Module +* index1: index of AMC link descriptor in 2nd record +* +* Output: None +* +* Global: None +* +* Return: return OK_STATUS if both link are matched, +* otherwise return ERROR_STATUS +* +***************************************************************************/ +static int +ipmi_ek_compare_link_descriptor( + struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, + struct ipmi_ek_amc_p2p_connectivity_record record2, int index2 ) +{ + int result = ERROR_STATUS; + + if (record1.link_desc[index1].type == record2.link_desc[index2].type){ + /*if it is an OEM type, we compare the OEM GUID*/ + if ( (record1.link_desc[index1].type >= LOWER_OEM_TYPE) + && (record1.link_desc[index1].type <= UPPER_OEM_TYPE) + ){ + if ( (record1.guid_count == 0) && (record2.guid_count == 0) ){ + /*there is no GUID for comparison, so the result is always OK*/ + result = OK_STATUS; + } + else{ + int i=0; + int j=0; + + for( i=0; i<record1.guid_count; i++){ + for( j=0; j < record2.guid_count; j++){ + if( memcmp (&record1.oem_guid[i], &record2.oem_guid[j], + SIZE_OF_GUID ) + == 0 + ){ + result = OK_STATUS; + break; + } + } + } + } + } + else{ + result = OK_STATUS; + } + if (result == OK_STATUS){ + if (record1.link_desc[index1].type_ext + == record2.link_desc[index2].type_ext + ){ + unsigned char asym[COMPARE_CANDIDATE]; + int offset = 0; + + asym[offset++] = record1.link_desc[index1].asym_match; + asym[offset] = record2.link_desc[index2].asym_match; + result = ipmi_ek_compare_asym ( asym ); + if (result == OK_STATUS){ + struct fru_picmgext_amc_link_desc_record link[COMPARE_CANDIDATE]; + int index = 0; + + link[index++] = record1.link_desc[index1]; + link[index] = record2.link_desc[index2]; + result = ipmi_ek_compare_number_of_enable_port( link ); + } + else{ + result = ERROR_STATUS; + } + } + else{ + result = ERROR_STATUS; + } + } + } + else{ + result = ERROR_STATUS; + } + + return result; +} + +/************************************************************************** +* +* Function name: ipmi_ek_compare_asym +* +* Description: This function compares 2 asymetric match of 2 +* amc link descriptors +* +* Restriction: None +* +* Input: asym[COMPARE_CANDIDATE]: Contain 2 asymetric match for comparison +* +* Output: None +* +* Global: None +* +* Return: return 0 if both asym. match are matched, otherwise return -1 +* +***************************************************************************/ + +static int +ipmi_ek_compare_asym( unsigned char asym[COMPARE_CANDIDATE] ) +{ + int return_value = ERROR_STATUS; + int first_index = 0; + int second_index = 1; + + if ( (asym[first_index] == 0) && (asym[second_index] == 0) ){ + return_value = OK_STATUS; + } + else if ( (asym[first_index] & asym[second_index]) == 0 ){ + return_value = OK_STATUS; + } + else{ + return_value = ERROR_STATUS; + } + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_compare_link_descriptor +* +* Description: This function compare number of enble port of Link designator +* +* Restriction: None +* +* Input: link_designator1: first link designator +* link_designator2: second link designator +* +* Output: None +* +* Global: None +* +* Return: return 0 if both link are matched, otherwise return -1 +* +***************************************************************************/ +static int +ipmi_ek_compare_number_of_enable_port( + struct fru_picmgext_amc_link_desc_record link_desc[COMPARE_CANDIDATE] ) +{ + int amc_port_count = 0; + int carrier_port_count = 0; + int return_value = ERROR_STATUS; + int index = 0; + + if (link_desc[index].port_flag_0){ /*bit 0 indicates port 0*/ + amc_port_count++; + } + if (link_desc[index].port_flag_1){ /*bit 1 indicates port 1*/ + amc_port_count++; + } + if (link_desc[index].port_flag_2){ /*bit 2 indicates port 2*/ + amc_port_count++; + } + if (link_desc[index++].port_flag_3){ /*bit 3 indicates port 3*/ + amc_port_count++; + } + + /*2nd link designator*/ + if (link_desc[index].port_flag_0){ /*bit 0 indicates port 0*/ + carrier_port_count++; + } + if (link_desc[index].port_flag_1){ /*bit 1 indicates port 1*/ + carrier_port_count++; + } + if (link_desc[index].port_flag_2){ /*bit 2 indicates port 2*/ + carrier_port_count++; + } + if (link_desc[index].port_flag_3){ /*bit 3 indicates port 3*/ + carrier_port_count++; + } + + if(carrier_port_count == amc_port_count){ + + return_value = OK_STATUS; + } + else{ + return_value = ERROR_STATUS; + } + + return return_value; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_link_descriptor +* +* Description: Display the link descriptor of an AMC p2p connectivity record +* +* Restriction: See AMC.0 or PICMG 3.0 specification for detail about bit masks +* +* Input: file_type: module type. +* rsc_id: resource id +* char* str: indicates if it is a source (its value= "From") or a +* destination (its value = "To"). ( it is set to "" if it is not +* a source nor destination +* link_desc: AMC link descriptor +* asym: asymetric match +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static tboolean +ipmi_ek_display_link_descriptor( int file_type, unsigned char rsc_id, + char * str, struct fru_picmgext_amc_link_desc_record link_desc ) +{ + tboolean isOEMtype = FALSE; + + if (file_type == ON_CARRIER_FRU_FILE){ + printf(" - %s On-Carrier Device ID %d\n", str, (rsc_id & 0x0f) ); + } + else{ + printf(" - %s %s\n", str, + val2str(file_type,ipmi_ekanalyzer_module_type)); + } + + printf(" - Channel ID %d || ", link_desc.channel_id ); + printf("%s", link_desc.port_flag_0 ? "Lane 0: enable" : ""); + printf("%s", link_desc.port_flag_1 ? ", Lane 1: enable" : ""); + printf("%s", link_desc.port_flag_2 ? ", Lane 2: enable" : ""); + printf("%s", link_desc.port_flag_3 ? ", Lane 3: enable" : ""); + + printf("\n"); + printf(" - Link Type: %s \n", + val2str (link_desc.type, ipmi_ekanalyzer_link_type) ); + switch ( link_desc.type ){ + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: + printf(" - Link Type extension: %s\n", + val2str (link_desc.type_ext, ipmi_ekanalyzer_extension_PCIE) ); + printf(" - Link Group ID: %d || ", link_desc.group_id ); + printf("Link Asym. Match: %d - %s\n", + link_desc.asym_match, + val2str (link_desc.asym_match, ipmi_ekanalyzer_asym_PCIE) ); + break; + case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: + printf(" - Link Type extension: %s\n", + val2str (link_desc.type_ext, ipmi_ekanalyzer_extension_ETHERNET) ); + printf(" - Link Group ID: %d || ", link_desc.group_id ); + printf("Link Asym. Match: %d - %s\n", + link_desc.asym_match, + val2str (link_desc.asym_match, ipmi_ekanalyzer_asym_PCIE) ); + break; + case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: + printf(" - Link Type extension: %s\n", + val2str (link_desc.type_ext, ipmi_ekanalyzer_extension_STORAGE) ); + printf(" - Link Group ID: %d || ", link_desc.group_id ); + printf("Link Asym. Match: %d - %s\n", + link_desc.asym_match, + val2str (link_desc.asym_match, ipmi_ekanalyzer_asym_STORAGE) ); + break; + default: + printf(" - Link Type extension: %i\n", link_desc.type_ext ); + printf(" - Link Group ID: %d || ", link_desc.group_id ); + printf("Link Asym. Match: %i\n", link_desc.asym_match); + break; + } + /*return as OEM type if link type indicates OEM*/ + if ( (link_desc.type >= LOWER_OEM_TYPE) + && + (link_desc.type <= UPPER_OEM_TYPE) + ){ + isOEMtype = TRUE; + } + + return isOEMtype; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_oem_guid +* +* Description: Display the oem guid of an AMC p2p connectivity record +* +* Restriction: None +* +* Input: amc_record: AMC p2p connectivity record +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_oem_guid( + struct ipmi_ek_amc_p2p_connectivity_record amc_record ) +{ + int index_oem = 0; + int index = 0; + + if ( amc_record.guid_count == 0 ){ + printf("\tThere is no OEM GUID for this module\n"); + } + for (index_oem = 0; index_oem < amc_record.guid_count; index_oem++){ + printf(" - GUID: "); + for(index = 0; index < SIZE_OF_GUID; index++){ + printf("%02x", amc_record.oem_guid[index_oem].guid[index]); + /*For a better look: putting a "-" after displaying four bytes of GUID*/ + if (!(index % 4)){ + printf("-"); + } + } + printf("\n"); + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_create_amc_p2p_record +* +* Description: this function create an AMC point 2 point connectivity record +* that contain link descriptor, channel descriptor, oem guid +* +* Restriction: Reference: AMC.0 Specification Table 3-16 +* +* Input: record: a pointer to FRU multi record +* +* Output: amc_record: a pointer to the created AMC p2p record +* +* Global: None +* +* Return: Return OK_STATUS on success, or ERROR_STATUS if no record has been +* created. +* +***************************************************************************/ +static int +ipmi_ek_create_amc_p2p_record(struct ipmi_ek_multi_header * record, + struct ipmi_ek_amc_p2p_connectivity_record * amc_record) +{ + int index_data = START_DATA_OFFSET; + int return_status = OK_STATUS; + + amc_record->guid_count = record->data[index_data++]; + if (amc_record->guid_count > 0) { + int index_oem = 0; + amc_record->oem_guid = malloc(amc_record->guid_count * \ + sizeof(struct fru_picmgext_guid)); + for (index_oem = 0; index_oem < amc_record->guid_count; + index_oem++) { + memcpy(&amc_record->oem_guid[index_oem].guid, + &record->data[index_data], + SIZE_OF_GUID); + index_data += (int)SIZE_OF_GUID; + } + amc_record->rsc_id = record->data[index_data++]; + amc_record->ch_count = record->data[index_data++]; + /* Calculate link descriptor count */ + amc_record->link_desc_count = ((record->header.len) - 8 - + (SIZE_OF_GUID*amc_record->guid_count) - + (FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE * + amc_record->ch_count)) / 5 ; + } else { + amc_record->rsc_id = record->data[index_data++]; + amc_record->ch_count = record->data[index_data++]; + /* Calculate link descriptor count see spec AMC.0 for detail */ + amc_record->link_desc_count = ((record->header.len) - 8 - + (FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE * + amc_record->ch_count)) / 5; + } + + if (amc_record->ch_count > 0) { + int ch_index = 0; + amc_record->ch_desc = malloc((amc_record->ch_count) * \ + sizeof(struct fru_picmgext_amc_channel_desc_record)); + for (ch_index = 0; ch_index < amc_record->ch_count; + ch_index++) { + unsigned int data; + struct fru_picmgext_amc_channel_desc_record *src, *dst; + data = record->data[index_data] | + (record->data[index_data + 1] << 8) | + (record->data[index_data + 2] << 16); + + src = (struct fru_picmgext_amc_channel_desc_record *)&data; + dst = (struct fru_picmgext_amc_channel_desc_record *) + &amc_record->ch_desc[ch_index]; + + dst->lane0port = src->lane0port; + dst->lane1port = src->lane1port; + dst->lane2port = src->lane2port; + dst->lane3port = src->lane3port; + index_data += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; + } + } + if (amc_record->link_desc_count > 0) { + int i=0; + amc_record->link_desc = malloc(amc_record->link_desc_count * \ + sizeof(struct fru_picmgext_amc_link_desc_record)); + for (i = 0; i< amc_record->link_desc_count; i++) { + unsigned int data[2]; + struct fru_picmgext_amc_link_desc_record *src, *dst; + data[0] = record->data[index_data] | + (record->data[index_data + 1] << 8) | + (record->data[index_data + 2] << 16) | + (record->data[index_data + 3] << 24); + + data[1] = record->data[index_data + 4]; + src = (struct fru_picmgext_amc_link_desc_record*)&data; + dst = (struct fru_picmgext_amc_link_desc_record*) + &amc_record->link_desc[i]; + + dst->channel_id = src->channel_id; + dst->port_flag_0 = src->port_flag_0; + dst->port_flag_1 = src->port_flag_1; + dst->port_flag_2 = src->port_flag_2; + dst->port_flag_3 = src->port_flag_3; + dst->type = src->type; + dst->type_ext = src->type_ext; + dst->group_id = src->group_id; + dst->asym_match = src->asym_match; + index_data += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; + } + } else { + return_status = ERROR_STATUS; + } + return return_status; +} + +/************************************************************************** +* +* Function name: ipmi_ek_get_resource_descriptor +* +* Description: this function create the resource descriptor of Carrier p2p +* connectivity record. +* +* Restriction: None +* +* Input: port_count: number of port count +* index: index to the position of data start offset +* record: a pointer to FRU multi record +* +* Output: port_desc: a pointer to the created resource descriptor +* +* Global: None +* +* Return: Return index that indicates the current position of data in record. +* +***************************************************************************/ +static int +ipmi_ek_get_resource_descriptor( int port_count, int index, + struct fru_picmgext_carrier_p2p_descriptor * port_desc, + struct ipmi_ek_multi_header * record ) +{ + int num_port = 0; + + while ( num_port < port_count ){ + memcpy ( &port_desc[num_port], &record->data[index], + sizeof (struct fru_picmgext_carrier_p2p_descriptor) ); + index += sizeof (struct fru_picmgext_carrier_p2p_descriptor); + num_port++; + } + + return index; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_fru_header +* +* Description: this function display FRU header offset from a FRU binary file +* +* Restriction: Reference: IPMI Platform Management FRU Information Storage +* Definition V1.0, Section 8 +* +* Input: filename: name of FRU binary file +* +* Output: None +* +* Global: None +* +* Return: Return OK_STATUS on sucess, ERROR_STATUS on error +* +***************************************************************************/ +static int +ipmi_ek_display_fru_header(char * filename) +{ + FILE * input_file; + struct fru_header header; + int ret = 0; + + input_file = fopen(filename, "r"); + if (input_file == NULL) { + lprintf(LOG_ERR, "File '%s' not found.", filename); + return (ERROR_STATUS); + } + ret = fread(&header, sizeof (struct fru_header), 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Failed to read FRU header!"); + fclose(input_file); + return (ERROR_STATUS); + } + printf("%s\n", EQUAL_LINE_LIMITER); + printf("FRU Header Info\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + printf("Format Version :0x%02x %s\n", + (header.version & 0x0f), + ((header.version & 0x0f) == 1) ? "" : "{unsupported}"); + printf("Internal Use Offset :0x%02x\n", header.offset.internal); + printf("Chassis Info Offset :0x%02x\n", header.offset.chassis); + printf("Board Info Offset :0x%02x\n", header.offset.board); + printf("Product Info Offset :0x%02x\n", header.offset.product); + printf("MultiRecord Offset :0x%02x\n", header.offset.multi); + printf("Common header Checksum :0x%02x\n", header.checksum); + + fclose(input_file); + return OK_STATUS; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_fru_header_detail +* +* Description: this function display detail FRU header information +* from a FRU binary file. + +* +* Restriction: Reference: IPMI Platform Management FRU Information Storage +* Definition V1.0, Section 8 +* +* Input: filename: name of FRU binary file +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static int +ipmi_ek_display_fru_header_detail(char * filename) +{ +# define FACTOR_OFFSET 8 +# define SIZE_MFG_DATE 3 + FILE * input_file; + size_t file_offset = 0; + struct fru_header header; + time_t tval; + int ret = 0; + unsigned char data = 0; + unsigned char lan_code = 0; + unsigned char mfg_date[SIZE_MFG_DATE]; + unsigned int board_length = 0; + + input_file = fopen(filename, "r"); + if (input_file == NULL) { + lprintf(LOG_ERR, "File '%s' not found.", filename); + return (-1); + } + /* The offset in each fru is in multiple of 8 bytes + * See IPMI Platform Management FRU Information Storage Definition + * for detail + */ + ret = fread(&header, sizeof(struct fru_header), 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Failed to read FRU header!"); + fclose(input_file); + return (-1); + } + /*** Display FRU Internal Use Info ***/ + if (!feof(input_file)) { + unsigned char format_version; + unsigned long len = 0; + + printf("%s\n", EQUAL_LINE_LIMITER); + printf("FRU Internal Use Info\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + + ret = fread(&format_version, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid format version!"); + fclose(input_file); + return (-1); + } + printf("Format Version: %d\n", (format_version & 0x0f)); + + if (header.offset.chassis > 0) { + len = (header.offset.chassis * FACTOR_OFFSET) + - (header.offset.internal * FACTOR_OFFSET); + } else { + len = (header.offset.board * FACTOR_OFFSET) + - (header.offset.internal * FACTOR_OFFSET); + } + printf("Length: %ld\n", len); + printf("Data dump:\n"); + while ((len > 0) && (!feof(input_file))) { + unsigned char data; + ret = fread(&data, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid data!"); + fclose(input_file); + return (-1); + } + printf("0x%02x ", data); + len--; + } + printf("\n"); + } + /*** Chassis Info Area ***/ + if (header.offset.chassis != 0) { + long offset = 0; + offset = header.offset.chassis * FACTOR_OFFSET; + ret = ipmi_ek_display_chassis_info_area(input_file, offset); + } + /*** Display FRU Board Info Area ***/ + while (1) { + if (header.offset.board == 0) { + break; + } + ret = fseek(input_file, + (header.offset.board * FACTOR_OFFSET), + SEEK_SET); + if (feof(input_file)) { + break; + } + file_offset = ftell(input_file); + printf("%s\n", EQUAL_LINE_LIMITER); + printf("FRU Board Info Area\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + + ret = fread(&data, 1, 1, input_file); /* Format version */ + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid FRU Format Version!"); + fclose(input_file); + return (-1); + } + printf("Format Version: %d\n", (data & 0x0f)); + if (feof(input_file)) { + break; + } + ret = fread(&data, 1, 1, input_file); /* Board Area Length */ + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Board Area Length!"); + fclose(input_file); + return (-1); + } + board_length = (data * FACTOR_OFFSET); + printf("Area Length: %d\n", board_length); + /* Decrease the length of board area by 1 byte of format version + * and 1 byte for area length itself. the rest of this length will + * be used to check for additional custom mfg. byte + */ + board_length -= 2; + if (feof(input_file)) { + break; + } + ret = fread(&lan_code, 1, 1, input_file); /* Language Code */ + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Language Code in input"); + fclose(input_file); + return (-1); + } + printf("Language Code: %d\n", lan_code); + board_length--; + /* Board Mfg Date */ + if (feof(input_file)) { + break; + } + + ret = fread(mfg_date, SIZE_MFG_DATE, 1, input_file); + if (ret != 1) { + lprintf(LOG_ERR, "Invalid Board Data."); + fclose(input_file); + return (-1); + } + tval = ((mfg_date[2] << 16) + (mfg_date[1] << 8) + + (mfg_date[0])); + tval = tval * 60; + tval = tval + secs_from_1970_1996; + printf("Board Mfg Date: %ld, %s", tval, + asctime(localtime(&tval))); + board_length -= SIZE_MFG_DATE; + /* Board Mfg */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "Board Manufacture Data", &board_length); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Board Product */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "Board Product Name", &board_length); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Board Serial */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "Board Serial Number", &board_length); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Board Part */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "Board Part Number", &board_length); + ret = fseek(input_file, file_offset, SEEK_SET); + /* FRU file ID */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "FRU File ID", &board_length); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Additional Custom Mfg. */ + file_offset = ipmi_ek_display_board_info_area( + input_file, "Custom", &board_length); + break; + } + /* Product Info Area */ + if (header.offset.product && (!feof(input_file))) { + long offset = 0; + offset = header.offset.product * FACTOR_OFFSET; + ret = ipmi_ek_display_product_info_area(input_file, + offset); + } + fclose(input_file); + return 0; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_chassis_info_area +* +* Description: this function displays detail format of product info area record +* into humain readable text format +* +* Restriction: Reference: IPMI Platform Management FRU Information Storage +* Definition V1.0, Section 10 +* +* Input: input_file: pointer to file stream +* offset: start offset of chassis info area +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static int +ipmi_ek_display_chassis_info_area(FILE * input_file, long offset) +{ + size_t file_offset; + int ret = 0; + unsigned char data = 0; + unsigned char ch_len = 0; + unsigned char ch_type = 0; + unsigned int len; + + if (input_file == NULL) { + lprintf(LOG_ERR, "No file stream to read."); + return (-1); + } + printf("%s\n", EQUAL_LINE_LIMITER); + printf("Chassis Info Area\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + ret = fseek(input_file, offset, SEEK_SET); + if (feof(input_file)) { + lprintf(LOG_ERR, "Invalid Chassis Info Area!"); + return (-1); + } + ret = fread(&data, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Version Number!"); + return (-1); + } + printf("Format Version Number: %d\n", (data & 0x0f)); + ret = fread(&ch_len, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid length!"); + return (-1); + } + /* len is in factor of 8 bytes */ + len = ch_len * 8; + printf("Area Length: %d\n", len); + len -= 2; + if (feof(input_file)) { + return (-1); + } + /* Chassis Type*/ + ret = fread(&ch_type, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Chassis Type!"); + return (-1); + } + printf("Chassis Type: %d\n", ch_type); + len--; + /* Chassis Part Number*/ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Chassis Part Number", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Chassis Serial */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Chassis Serial Number", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Custom product info area */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Custom", &len); + return 0; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_board_info_area +* +* Description: this function displays board info area depending on board type +* that pass in argument. Board type can be: +* Manufacturer, Serial, Product or Part... +* +* Restriction: IPMI Platform Management FRU Information Storage +* Definition V1.0, Section 11 +* +* Input: input_file: pointer to file stream +* board_type: a string that contain board type +* board_length: length of info area +* +* Output: None +* +* Global: None +* +* Return: the current position of input_file +* +***************************************************************************/ +static size_t +ipmi_ek_display_board_info_area(FILE * input_file, char * board_type, + unsigned int * board_length) +{ + size_t file_offset; + int ret = 0; + unsigned char len = 0; + unsigned int size_board = 0; + if (input_file == NULL || board_type == NULL + || board_length == NULL) { + return (size_t)(-1); + } + file_offset = ftell(input_file); + + /* Board length*/ + ret = fread(&len, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Length!"); + goto out; + } + (*board_length)--; + + /* Bit 5:0 of Board Mfg type represent legnth */ + size_board = (len & 0x3f); + if (size_board == 0) { + printf("%s: None\n", board_type); + goto out; + } + if (strncmp(board_type, "Custom", 6 ) != 0) { + unsigned char *data; + unsigned int i = 0; + data = malloc(size_board); + if (data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return (size_t)(-1); + } + ret = fread(data, size_board, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid board type size!"); + free(data); + data = NULL; + goto out; + } + printf("%s type: 0x%02x\n", board_type, len); + printf("%s: ", board_type); + for (i = 0; i < size_board; i++) { + if ((len & TYPE_CODE) == TYPE_CODE) { + printf("%c", data[i]); + } else { + /* other than language code (binary, BCD, + * ASCII 6 bit...) is not supported + */ + printf("%02x", data[i]); + } + } + printf("\n"); + free(data); + data = NULL; + (*board_length) -= size_board; + goto out; + } + while (!feof(input_file)) { + if (len == NO_MORE_INFO_FIELD) { + unsigned char padding; + unsigned char checksum = 0; + /* take the rest of data in the area minus 1 byte of + * checksum + */ + printf("Additional Custom Mfg. length: 0x%02x\n", len); + padding = (*board_length) - 1; + if ((padding > 0) && (!feof(input_file))) { + printf("Unused space: %d (bytes)\n", padding); + fseek(input_file, padding, SEEK_CUR); + } + ret = fread(&checksum, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Checksum!"); + goto out; + } + printf("Checksum: 0x%02x\n", checksum); + goto out; + } + printf("Additional Custom Mfg. length: 0x%02x\n", len); + if ((size_board > 0) && (size_board < (*board_length))) { + unsigned char * additional_data = NULL; + unsigned int i = 0; + additional_data = malloc(size_board); + if (additional_data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return (size_t)(-1); + } + + ret = fread(additional_data, size_board, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Additional Data!"); + goto out; + } + printf("Additional Custom Mfg. Data: %02x", + additional_data[0]); + for (i = 1; i < size_board; i++) { + printf("-%02x", additional_data[i]); + } + printf("\n"); + free(additional_data); + additional_data = NULL; + (*board_length) -= size_board; + } + else { + printf("No Additional Custom Mfg. %d\n", *board_length); + goto out; + } + } + +out: + file_offset = ftell(input_file); + return file_offset; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_product_info_area +* +* Description: this function displays detail format of product info area record +* into humain readable text format +* +* Restriction: Reference: IPMI Platform Management FRU Information Storage +* Definition V1.0, Section 12 +* +* Input: input_file: pointer to file stream +* offset: start offset of product info area +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static int +ipmi_ek_display_product_info_area(FILE * input_file, long offset) +{ + size_t file_offset; + int ret = 0; + unsigned char ch_len = 0; + unsigned char data = 0; + unsigned int len = 0; + + if (input_file == NULL) { + lprintf(LOG_ERR, "No file stream to read."); + return (-1); + } + file_offset = ftell(input_file); + printf("%s\n", EQUAL_LINE_LIMITER); + printf("Product Info Area\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + ret = fseek(input_file, offset, SEEK_SET); + if (feof(input_file)) { + lprintf(LOG_ERR, "Invalid Product Info Area!"); + return (-1); + } + ret = fread(&data, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Data!"); + return (-1); + } + printf("Format Version Number: %d\n", (data & 0x0f)); + if (feof(input_file)) { + return (-1); + } + /* Have to read this into a char or else + * it ends up byte swapped on big endian machines */ + ret = fread(&ch_len, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Length!"); + return (-1); + } + /* length is in factor of 8 bytes */ + len = ch_len * 8; + printf("Area Length: %d\n", len); + len -= 2; /* -1 byte of format version and -1 byte itself */ + + ret = fread(&data, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Length!"); + return (-1); + } + + fread(&data, 1, 1, input_file); + printf("Language Code: %d\n", data); + len--; + /* Product Mfg */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Product Manufacture Data", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Product Name */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Product Name", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Product Part */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Product Part/Model Number", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Product Version */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Product Version", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Product Serial */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Product Serial Number", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Product Asset Tag */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Asset Tag", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* FRU file ID */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "FRU File ID", &len); + ret = fseek(input_file, file_offset, SEEK_SET); + /* Custom product info area */ + file_offset = ipmi_ek_display_board_info_area(input_file, + "Custom", &len); + return 0; +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_record +* +* Description: this function displays FRU multi record information. +* +* Restriction: None +* +* Input: record: a pointer to current record +* list_head: a pointer to header of the list +* list_last: a pointer to tale of the list +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_record( struct ipmi_ek_multi_header * record, + struct ipmi_ek_multi_header * list_head, + struct ipmi_ek_multi_header * list_last ) +{ + if ( list_head == NULL ){ + printf("***empty list***\n"); + } + else{ + printf("%s\n", EQUAL_LINE_LIMITER); + printf("FRU Multi Info area\n"); + printf("%s\n", EQUAL_LINE_LIMITER); + for ( record = list_head; record != NULL; record = record->next ){ + printf("Record Type ID: 0x%02x\n", record->header.type); + printf("Record Format version: 0x%02x\n", record->header.format); + if (record->header.len > PICMG_ID_OFFSET){ + /* In picmg3.0 specification, picmg record id lower than 4 or + * greater than 0x2d is not supported + */ + #define PICMG_ID_LOWER_LIMIT 0x04 + #define PICMG_ID_UPPER_LIMIT 0x2d + unsigned char picmg_id; + + picmg_id = record->data[PICMG_ID_OFFSET]; + printf("Manufacturer ID: %02x%02x%02x h\n", record->data[2], + record->data[1], record->data[0] ); + if( ( picmg_id < PICMG_ID_LOWER_LIMIT ) + || + ( picmg_id > PICMG_ID_UPPER_LIMIT ) ){ + printf("Picmg record ID: Unsupported {0x%02x}\n", picmg_id ); + } + else{ + printf("Picmg record ID: %s {0x%02x}\n", + val2str(picmg_id, ipmi_ekanalyzer_picmg_record_id), + picmg_id ); + } + switch (picmg_id){ + case FRU_PICMG_BACKPLANE_P2P: /*0x04*/ + ipmi_ek_display_backplane_p2p_record (record); + break; + case FRU_PICMG_ADDRESS_TABLE: /*0x10*/ + ipmi_ek_display_address_table_record (record); + break; + case FRU_PICMG_SHELF_POWER_DIST: /*0x11*/ + ipmi_ek_display_shelf_power_distribution_record (record); + break; + case FRU_PICMG_SHELF_ACTIVATION: /*/0x12*/ + ipmi_ek_display_shelf_activation_record (record); + break; + case FRU_PICMG_SHMC_IP_CONN: /*0x13*/ + ipmi_ek_display_shelf_ip_connection_record (record); + break; + case FRU_PICMG_BOARD_P2P: /*0x14*/ + ipmi_ek_display_board_p2p_record (record); + break; + case FRU_RADIAL_IPMB0_LINK_MAPPING: /*0x15*/ + ipmi_ek_display_radial_ipmb0_record (record); + break; + case FRU_AMC_CURRENT: /*0x16*/ + ipmi_ek_display_amc_current_record (record); + break; + case FRU_AMC_ACTIVATION: /*0x17*/ + ipmi_ek_display_amc_activation_record (record); + break; + case FRU_AMC_CARRIER_P2P: /*0x18*/ + ipmi_ek_display_carrier_connectivity (record); + break; + case FRU_AMC_P2P: /*0x19*/ + ipmi_ek_display_amc_p2p_record (record); + break; + case FRU_AMC_CARRIER_INFO: /*0x1a*/ + ipmi_ek_display_amc_carrier_info_record (record); + break; + case FRU_PICMG_CLK_CARRIER_P2P: /*0x2c*/ + ipmi_ek_display_clock_carrier_p2p_record (record); + break; + case FRU_PICMG_CLK_CONFIG: /*0x2d*/ + ipmi_ek_display_clock_config_record (record); + break; + default: + if (verbose > 0){ + int i; + printf("%02x %02x %02x %02x %02x ", record->header.type, + record->header.format, record->header.len, + record->header.record_checksum, + record->header.header_checksum ); + for ( i = 0; i < record->header.len; i++ ){ + printf("%02x ", record->data[i]); + } + printf("\n"); + } + break; + } + printf("%s\n", STAR_LINE_LIMITER); + } + } + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_backplane_p2p_record +* +* Description: this function displays backplane p2p record. +* +* Restriction: Reference: PICMG 3.0 Specification Table 3-40 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_backplane_p2p_record( struct ipmi_ek_multi_header * record ) +{ + uint8_t index; + int offset = START_DATA_OFFSET; + struct fru_picmgext_slot_desc * slot_d + = (struct fru_picmgext_slot_desc*) &record->data[offset]; + + offset += sizeof(struct fru_picmgext_slot_desc); + + while ( offset <= record->header.len ) { + printf(" Channel Type: "); + switch ( slot_d -> chan_type ) + { + case 0x00: + case 0x07: + printf("PICMG 2.9\n"); + break; + case 0x08: + printf("Single Port Fabric IF\n"); + break; + case 0x09: + printf("Double Port Fabric IF\n"); + break; + case 0x0a: + printf("Full Channel Fabric IF\n"); + break; + case 0x0b: + printf("Base IF\n"); + break; + case 0x0c: + printf("Update Channel IF\n"); + break; + default: + printf("Unknown IF\n"); + break; + } + printf(" Slot Address: %02x\n", slot_d -> slot_addr); + printf(" Channel Count: %i\n", slot_d -> chn_count); + + for ( index = 0; index < (slot_d -> chn_count); index++ ) { + struct fru_picmgext_chn_desc * d + = (struct fru_picmgext_chn_desc *) &record->data[offset]; + + if ( verbose ){ + printf( "\t" + "Chn: %02x --> " + "Chn: %02x in " + "Slot: %02x\n", + d->local_chn, d->remote_chn, d->remote_slot + ); + } + offset += sizeof(struct fru_picmgext_chn_desc); + } + slot_d = (struct fru_picmgext_slot_desc*) &record->data[offset]; + offset += sizeof(struct fru_picmgext_slot_desc); + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_address_table_record +* +* Description: this function displays address table record. +* +* Restriction: Reference: PICMG 3.0 Specification Table 3-6 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_address_table_record( struct ipmi_ek_multi_header * record ) +{ + unsigned char entries = 0; + unsigned char i; + int offset = START_DATA_OFFSET; + #define SIZE_SHELF_ADDRESS_BYTE 20 + + printf(" Type/Len: 0x%02x\n", record->data[offset++]); + printf(" Shelf Addr: "); + for ( i = 0; i < SIZE_SHELF_ADDRESS_BYTE; i++ ){ + printf("0x%02x ", record->data[offset++]); + } + printf("\n"); + + entries = record->data[offset++]; + printf(" Addr Table Entries count: 0x%02x\n", entries); + + for ( i = 0; i < entries; i++ ){ + printf("\tHWAddr: 0x%02x - SiteNum: 0x%02x - SiteType: 0x%02x \n", + record->data[offset+0], + record->data[offset+1], + record->data[offset+2]); + offset += 3; + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_shelf_power_distribution_record +* +* Description: this function displays shelf power distribution record. +* +* Restriction: Reference: PICMG 3.0 Specification Table 3-70 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_shelf_power_distribution_record( + struct ipmi_ek_multi_header * record ) +{ + int offset = START_DATA_OFFSET; + unsigned char i,j; + unsigned char feeds = 0; + + feeds = record->data[offset++]; + printf(" Number of Power Feeds: 0x%02x\n", feeds); + + for (i=0; i<feeds; i++) { + unsigned char entries; + unsigned long max_ext = 0; + unsigned long max_int = 0; + max_ext = record->data[offset+0] | (record->data[offset+1]<<8); + printf(" Max External Available Current: %ld Amps\n", (max_ext*10) ); + + offset += 2; + + max_int = record->data[offset+0] | (record->data[offset+1]<<8); + printf(" Max Internal Current:\t %ld Amps\n", (max_int*10)); + offset += 2; + printf(" Min Expected Operating Voltage: %d Volts\n", + (record->data[offset++]/2)); + entries = record->data[offset++]; + printf(" Feed to FRU count: 0x%02x\n", entries); + for (j=0; j<entries; j++) { + printf("\tHW: 0x%02x", record->data[offset++]); + printf("\tFRU ID: 0x%02x\n", record->data[offset++]); + } + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_shelf_activation_record +* +* Description: this function displays shelf activation record. +* +* Restriction: Reference: PICMG 3.0 Specification Table 3-73 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_shelf_activation_record( + struct ipmi_ek_multi_header * record ) +{ + unsigned char count = 0; + int offset = START_DATA_OFFSET; + + printf(" Allowance for FRU Act Readiness: 0x%02x\n", + record->data[offset++]); + count = record->data[offset++]; + printf(" FRU activation and Power Desc Cnt: 0x%02x\n", count); + + while ( count > 0 ) { + printf(" FRU activation and Power descriptor:\n"); + printf("\tHardware Address:\t\t0x%02x\n", record->data[offset++]); + printf("\tFRU Device ID:\t\t\t0x%02x\n", record->data[offset++]); + printf("\tMax FRU Power Capability:\t0x%04x Watts\n", + ( record->data[offset+0] | (record->data[offset+1]<<8) )); + offset += 2; + printf("\tConfiguration parameter:\t0x%02x\n", record->data[offset++]); + count --; + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_shelf_ip_connection_record +* +* Description: this function displays shelf ip connection record. +* +* Restriction: Fix me: Don't test yet +* Reference: PICMG 3.0 Specification Table 3-31 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_shelf_ip_connection_record( + struct ipmi_ek_multi_header * record ) +{ + int ioffset = START_DATA_OFFSET; + if (ioffset > record->header.len) { + printf(" Shelf Manager IP Address: %d.%d.%d.%d\n", + record->data[ioffset+0], record->data[ioffset+1], + record->data[ioffset+2], record->data[ioffset+3]); + ioffset += 4; + } + if (ioffset > record->header.len) { + printf(" Default Gateway Address: %d.%d.%d.%d\n", + record->data[ioffset+0], record->data[ioffset+1], + record->data[ioffset+2], record->data[ioffset+3]); + ioffset += 4; + } + if (ioffset > record->header.len) { + printf(" Subnet Mask: %d.%d.%d.%d\n", + record->data[ioffset+0], record->data[ioffset+1], + record->data[ioffset+2], record->data[ioffset+3]); + ioffset += 4; + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_shelf_fan_geography_record +* +* Description: this function displays shelf fan geography record. +* +* Restriction: Fix me: Don't test yet +* Reference: PICMG 3.0 Specification Table 3-75 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_shelf_fan_geography_record( + struct ipmi_ek_multi_header * record ) +{ + int ioffset = START_DATA_OFFSET; + unsigned char fan_count = 0; + + fan_count = record->data[ioffset]; + ioffset++; + printf(" Fan-to-FRU Entry Count: 0x%02x\n", fan_count); + + while ( (fan_count > 0) && (ioffset <= record->header.len) ) { + printf(" Fan-to-FRU Mapping Entry: {%2x%2x%2x%2x}\n", + record->data[ioffset], record->data[ioffset+1], + record->data[ioffset+2], record->data[ioffset+3] + ); + printf(" Hardware Address: 0x%02x\n", record->data[ioffset++]); + printf(" FRU device ID: 0x%02x\n", record->data[ioffset++]); + printf(" Site Number: 0x%02x\n", record->data[ioffset++]); + printf(" Site Type: 0x%02x\n", record->data[ioffset++]); + fan_count --; + } + +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_board_p2p_record +* +* Description: this function displays board pont-to-point record. +* +* Restriction: Reference: PICMG 3.0 Specification Table 3-44 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_board_p2p_record( struct ipmi_ek_multi_header * record ) +{ + unsigned char guid_count; + int offset = START_DATA_OFFSET; + int i = 0; + + guid_count = record->data[offset++]; + printf(" GUID count: %2d\n", guid_count); + + for (i = 0 ; i < guid_count; i++ ) { + int j; + printf("\tGUID: "); + for (j=0; j < sizeof(struct fru_picmgext_guid); j++) { + printf("%02x", record->data[offset+j]); + } + printf("\n"); + offset += sizeof(struct fru_picmgext_guid); + } + + for ( offset; + offset < record->header.len; + offset += sizeof(struct fru_picmgext_link_desc) + ) { + /* to solve little endian /big endian problem */ + unsigned long data; + struct fru_picmgext_link_desc * d; + + data = (record->data[offset+0]) | (record->data[offset+1] << 8)\ + | (record->data[offset+2] << 16)\ + | (record->data[offset+3] << 24); + + d = (struct fru_picmgext_link_desc *) &data; + + printf(" Link Descriptor\n"); + printf("\tLink Grouping ID:\t0x%02x\n", d->grouping); + printf("\tLink Type Extension:\t0x%02x - ", d->ext); + + if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE){ + switch (d->ext){ + case 0: + printf("10/100/1000BASE-T Link (four-pair)\n"); + break; + case 1: + printf("ShMC Cross-connect (two-pair)\n"); + break; + default: + printf("Unknwon\n"); + break; + } + } + else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET){ + switch (d->ext){ + case 0: + printf("Fixed 1000Base-BX\n"); + break; + case 1: + printf("Fixed 10GBASE-BX4 [XAUI]\n"); + break; + case 2: + printf("FC-PI\n"); + break; + default: + printf("Unknwon\n"); + break; + } + } + else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND){ + printf("Unknwon\n"); + } + else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR){ + printf("Unknwon\n"); + } + else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE){ + printf("Unknwon\n"); + } + else{ + printf("Unknwon\n"); + } + + printf("\tLink Type:\t\t0x%02x - ",d->type); + if (d->type == 0 || d->type == 0xff){ + printf("Reserved\n"); + } + else if (d->type >= 0x06 && d->type <= 0xef) { + printf("Reserved\n"); + } + else if (d->type >= LOWER_OEM_TYPE && d->type <= UPPER_OEM_TYPE) { + printf("OEM GUID Definition\n"); + } + else { + switch (d->type){ + case FRU_PICMGEXT_LINK_TYPE_BASE: + printf("PICMG 3.0 Base Interface 10/100/1000\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET: + printf("PICMG 3.1 Ethernet Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND: + printf("PICMG 3.2 Infiniband Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR: + printf("PICMG 3.3 Star Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_PCIE: + printf("PICMG 3.4 PCI Express Fabric Interface\n"); + break; + default: + printf("Invalid\n"); + break; + } + } + printf("\tLink Designator: \n"); + printf("\t Port 0 Flag: %s\n", + (d->desig_port & 0x01) ? "enable" : "disable"); + printf("\t Port 1 Flag: %s\n", + (d->desig_port & 0x02) ? "enable" : "disable"); + printf("\t Port 2 Flag: %s\n", + (d->desig_port & 0x04) ? "enable" : "disable"); + printf("\t Port 3 Flag: %s\n", + (d->desig_port & 0x08) ? "enable" : "disable"); + + printf("\t Interface: 0x%02x - ", d->desig_if); + switch (d->desig_if){ + case FRU_PICMGEXT_DESIGN_IF_BASE: + printf("Base Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_FABRIC: + printf("Fabric Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL: + printf("Update Channel\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_RESERVED: + printf("Reserved\n"); + break; + default: + printf("Invalid"); + break; + } + printf("\t Channel Number: 0x%02x\n", d->desig_channel); + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_radial_ipmb0_record +* +* Description: this function displays radial IPMB-0 record. +* +* Restriction: Fix me: Don't test yet +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_radial_ipmb0_record( struct ipmi_ek_multi_header * record ) +{ + int offset = START_DATA_OFFSET; + #define SIZE_OF_CONNECTOR_DEFINER 3; /*bytes*/ + + /*Ref: PICMG 3.0 Specification Revision 2.0, Table 3-59*/ + printf(" IPMB-0 Connector Definer: "); + #ifndef WORDS_BIGENDIAN + printf("%02x %02x %02x h\n", record->data[offset], + record->data[offset+1], record->data[offset+2]); + #else + printf("%02x %02x %02x h\n", record->data[offset+2], + record->data[offset+1], record->data[offset]); + #endif + /*3 bytes of connector definer was used*/ + offset += SIZE_OF_CONNECTOR_DEFINER; + + printf (" IPMB-0 Connector version ID: "); + #ifndef WORDS_BIGENDIAN + printf("%02x %02x h\n", record->data[offset], record->data[offset+1]); + #else + printf("%02x %02x h\n", record->data[offset+1], record->data[offset]); + #endif + offset += 2; + + printf(" IPMB-0 Hub Descriptor Count: 0x%02x", record->data[offset++]); + if (record->data[offset] > 0){ + for (offset; offset < record->header.len;){ + unsigned char entry_count = 0; + printf(" IPMB-0 Hub Descriptor\n"); + printf("\tHardware Address: 0x%02x\n", record->data[offset++]); + printf("\tHub Info {0x%02x}: ", record->data[offset]); + /* Bit mask specified in Table 3-59 of PICMG 3.0 Specification */ + if ( (record->data[offset] & 0x01) == 0x01 ){ + printf("IPMB-A only\n"); + } + else if ( (record->data[offset] & 0x02) == 0x02 ){ + printf("IPMB-B only\n"); + } + else if ( (record->data[offset] & 0x03) == 0x03 ){ + printf("IPMB-A and IPMB-B\n"); + } + else{ + printf("Reserved.\n"); + } + offset ++; + + entry_count = record->data[offset++]; + printf("\tAddress Entry count: 0x%02x", entry_count); + while (entry_count > 0){ + printf("\t Hardware Address: 0x%02x\n", record->data[offset++]); + printf("\t IPMB-0 Link Entry: 0x%02x\n",record->data[offset++]); + entry_count --; + } + } + } + +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_amc_current_record +* +* Description: this function displays AMC current record. +* +* Restriction: None +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_amc_current_record( struct ipmi_ek_multi_header * record ) +{ + unsigned char current; + current = record->data[START_DATA_OFFSET]; + printf(" Current draw: %.1f A @ 12V => %.2f Watt\n", + (float) current/10.0, ((float)current/10.0)*12.0 ); + printf("\n"); +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_amc_activation_record +* +* Description: this function displays carrier activation and current management +* record. +* +* Restriction: Reference: AMC.0 Specification Table 3-11 and Table 3-12 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_amc_activation_record( struct ipmi_ek_multi_header * record ) +{ + uint16_t max_current; + int offset = START_DATA_OFFSET; + + max_current = record->data[offset]; + max_current |= record->data[++offset] << 8; + printf(" Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n", + (float) max_current / 10, + (float) max_current / 10 * 12); + printf(" Module Activation Readiness: %i sec.\n", + record->data[++offset]); + + printf(" Descriptor Count: %i\n", record->data[++offset]); + for(++offset; (offset < record->header.len); offset += 3 ) + { + struct fru_picmgext_activation_record * a = + (struct fru_picmgext_activation_record *) &record->data[offset]; + + printf("\tIPMB-Address:\t\t0x%x\n", a->ibmb_addr); + printf("\tMax. Module Current:\t%.2f A\n", (float)a->max_module_curr/10); + printf("\n"); + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_amc_p2p_record +* +* Description: this function display amc p2p connectivity record in humain +* readable text format +* +* Restriction: Reference: AMC.0 Specification Table 3-16 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_amc_p2p_record( struct ipmi_ek_multi_header * record ) +{ + int index_data = START_DATA_OFFSET; + int oem_count = 0; + int ch_count = 0; + int index=0; + + oem_count = record->data[index_data++]; + printf("OEM GUID count: %02x\n", oem_count); + + if ( oem_count > 0 ){ + while ( oem_count > 0 ){ + printf("OEM GUID: "); + for ( index = 1; index <= SIZE_OF_GUID; index++ ){ + printf("%02x", record->data[index_data++]); + /* For a better look, display a "-" character after each 5 bytes + * of OEM GUID */ + if ( !(index % 5) ){ + printf("-"); + } + } + printf("\n"); + oem_count--; + } + } + if ( ( record->data[index_data] & AMC_MODULE ) == AMC_MODULE ){ + printf("AMC module connection\n"); + } + else{ + printf("On-Carrier Device %02x h\n", ( record->data[index_data] & 0x0f )); + } + index_data ++; + ch_count = record->data[index_data++]; + printf("AMC Channel Descriptor count: %02x h\n", ch_count); + + if ( ch_count > 0 ){ + for ( index = 0; index < ch_count; index++ ){ + unsigned int data; + struct fru_picmgext_amc_channel_desc_record * ch_desc; + printf(" AMC Channel Descriptor {%02x%02x%02x}\n", + record->data[index_data+2], record->data[index_data+1], + record->data[index_data] + ); + data = record->data[index_data] | + (record->data[index_data + 1] << 8) | + (record->data[index_data + 2] << 16); + ch_desc = ( struct fru_picmgext_amc_channel_desc_record * ) &data; + printf(" Lane 0 Port: %d\n", ch_desc->lane0port); + printf(" Lane 1 Port: %d\n", ch_desc->lane1port); + printf(" Lane 2 Port: %d\n", ch_desc->lane2port); + printf(" Lane 3 Port: %d\n\n", ch_desc->lane3port); + index_data += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; + } + } + while ( index_data < record->header.len ){ + /*Warning: For gcc version between 4.0 and 4.3 this code doesnt work*/ + unsigned int data[2]; + struct fru_picmgext_amc_link_desc_record *link_desc; + data[0] = record->data[index_data] | + (record->data[index_data + 1] << 8) | + (record->data[index_data + 2] << 16) | + (record->data[index_data + 3] << 24); + data[1] = record->data[index_data + 4]; + + link_desc = (struct fru_picmgext_amc_link_desc_record *) &data[0]; + + printf(" AMC Link Descriptor:\n" ); + + printf("\t- Link Type: %s \n", + val2str (link_desc->type, ipmi_ekanalyzer_link_type)); + switch ( link_desc->type ) { + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: + printf("\t- Link Type extension: %s\n", + val2str (link_desc->type_ext, ipmi_ekanalyzer_extension_PCIE)); + printf("\t- Link Group ID: %d\n ", link_desc->group_id ); + printf("\t- Link Asym. Match: %d - %s\n", + link_desc->asym_match, + val2str (link_desc->asym_match, ipmi_ekanalyzer_asym_PCIE)); + break; + case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: + printf("\t- Link Type extension: %s\n", + val2str (link_desc->type_ext, + ipmi_ekanalyzer_extension_ETHERNET)); + printf("\t- Link Group ID: %d \n", link_desc->group_id ); + printf("\t- Link Asym. Match: %d - %s\n", + link_desc->asym_match, + val2str (link_desc->asym_match, ipmi_ekanalyzer_asym_PCIE)); + break; + case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: + printf("\t- Link Type extension: %s\n", + val2str (link_desc->type_ext, + ipmi_ekanalyzer_extension_STORAGE)); + printf("\t- Link Group ID: %d \n", link_desc->group_id ); + printf("\t- Link Asym. Match: %d - %s\n", + link_desc->asym_match, + val2str (link_desc->asym_match, ipmi_ekanalyzer_asym_STORAGE)); + break; + default: + printf("\t- Link Type extension: %i (Unknown)\n", link_desc->type_ext ); + printf("\t- Link Group ID: %d \n", link_desc->group_id ); + printf("\t- Link Asym. Match: %i\n", link_desc->asym_match); + break; + } + printf("\t- AMC Link Designator:\n"); + printf("\t Channel ID: %i\n", link_desc->channel_id); + printf("\t\t Lane 0: %s\n", (link_desc->port_flag_0)?"enable":"disable"); + printf("\t\t Lane 1: %s\n", (link_desc->port_flag_1)?"enable":"disable"); + printf("\t\t Lane 2: %s\n", (link_desc->port_flag_2)?"enable":"disable"); + printf("\t\t Lane 3: %s\n", (link_desc->port_flag_3)?"enable":"disable"); + index_data += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; + } +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_amc_carrier_info_record +* +* Description: this function displays Carrier information table. +* +* Restriction: Reference: AMC.0 Specification Table 3-3 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: START_DATA_OFFSET +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_amc_carrier_info_record( struct ipmi_ek_multi_header * record ) +{ + unsigned char extVersion; + unsigned char siteCount; + int offset = START_DATA_OFFSET; + + extVersion = record->data[offset++]; + siteCount = record->data[offset++]; + + printf(" AMC.0 extension version: R%d.%d\n", (extVersion >> 0)& 0x0F, + (extVersion >> 4)& 0x0F ); + printf(" Carrier Sie Number Count: %d\n", siteCount); + + while (siteCount > 0){ + printf("\tSite ID (%d): %s \n", record->data[offset], + val2str(record->data[offset], ipmi_ekanalyzer_module_type) ); + offset++; + siteCount--; + } + printf("\n"); +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_clock_carrier_p2p_record +* +* Description: this function displays Carrier clock point-to-pont +* connectivity record. +* +* Restriction: the following code is copy from ipmi_fru.c with modification in +* reference to AMC.0 Specification Table 3-29 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_display_clock_carrier_p2p_record( + struct ipmi_ek_multi_header * record ) +{ + unsigned char desc_count; + int i,j; + int offset = START_DATA_OFFSET; + + desc_count = record->data[offset++]; + + for(i=0; i<desc_count; i++){ + unsigned char resource_id; + unsigned char channel_count; + + resource_id = record->data[offset++]; + channel_count = record->data[offset++]; + + printf(" Clock Resource ID: 0x%02x\n", resource_id); + printf(" Type: "); + if((resource_id & 0xC0)>>6 == 0) { + printf("On-Carrier-Device\n"); + } + else if((resource_id & 0xC0)>>6 == 1) { + printf("AMC slot\n"); + } + else if((resource_id & 0xC0)>>6 == 2) { + printf("Backplane\n"); + } + else{ + printf("reserved\n"); + } + printf(" Channel Count: 0x%02x\n", channel_count); + + for(j=0; j<channel_count; j++){ + unsigned char loc_channel, rem_channel, rem_resource; + + loc_channel = record->data[offset++]; + rem_channel = record->data[offset++]; + rem_resource = record->data[offset++]; + + printf("\tCLK-ID: 0x%02x ---> ", loc_channel); + printf(" remote CLKID: 0x%02x ", rem_channel); + if((rem_resource & 0xC0)>>6 == 0) { + printf("[ Carrier-Dev"); + } + else if((rem_resource & 0xC0)>>6 == 1) { + printf("[ AMC slot "); + } + else if((rem_resource & 0xC0)>>6 == 2) { + printf("[ Backplane "); + } + else{ + printf("reserved "); + } + printf(" 0x%02x ]\n", rem_resource&0xF); + } + } + printf("\n"); +} + +/************************************************************************** +* +* Function name: ipmi_ek_display_clock_config_record +* +* Description: this function displays clock configuration record. +* +* Restriction: the following codes are copy from ipmi_fru.c with modification +* in reference to AMC.0 Specification Table 3-35 and Table 3-36 +* +* Input: record: a pointer to current record to be displayed +* +* Output: None +* +* Global: START_DATA_OFFSET +* +* Return: None +* +***************************************************************************/ +void +ipmi_ek_display_clock_config_record( struct ipmi_ek_multi_header * record ) +{ + unsigned char resource_id, descr_count; + int i; + int offset = START_DATA_OFFSET; + + resource_id = record->data[offset++]; + descr_count = record->data[offset++]; + printf(" Clock Resource ID: 0x%02x\n", resource_id); + printf(" Clock Configuration Descriptor Count: 0x%02x\n", descr_count); + + for(i=0; i<descr_count; i++){ + unsigned char channel_id, control; + unsigned char indirect_cnt, direct_cnt; + int j=0; + + channel_id = record->data[offset++]; + control = record->data[offset++]; + printf("\tCLK-ID: 0x%02x - ", channel_id); + printf("CTRL 0x%02x [ %12s ]\n", control, + ((control&0x1)==0)?"Carrier IPMC":"Application"); + + indirect_cnt = record->data[offset++]; + direct_cnt = record->data[offset++]; + printf("\t Count: Indirect 0x%02x / Direct 0x%02x\n", indirect_cnt, + direct_cnt ); + + /* indirect desc */ + for(j=0; j<indirect_cnt; j++){ + unsigned char feature; + unsigned char dep_chn_id; + + feature = record->data[offset++]; + dep_chn_id = record->data[offset++]; + printf("\t\tFeature: 0x%02x [%8s] - ", feature, + (feature&0x1)==1?"Source":"Receiver"); + printf(" Dep. CLK-ID: 0x%02x\n", dep_chn_id); + } + + /* direct desc */ + for(j=0; j<direct_cnt; j++){ + unsigned char feature, family, accuracy; + unsigned long freq, min_freq, max_freq; + + feature = record->data[offset++]; + family = record->data[offset++]; + accuracy = record->data[offset++]; + freq = (record->data[offset+0] << 0 ) + | (record->data[offset+1] << 8 ) + | (record->data[offset+2] << 16) + | (record->data[offset+3] << 24); + offset += 4; + min_freq = (record->data[offset+0] << 0 ) + | (record->data[offset+1] << 8 ) + | (record->data[offset+2] << 16) + | (record->data[offset+3] << 24); + offset += 4; + max_freq = (record->data[offset+0] << 0 ) + | (record->data[offset+1] << 8 ) + | (record->data[offset+2] << 16) + | (record->data[offset+3] << 24); + offset += 4; + + printf("\t- Feature: 0x%02x - PLL: %x / Asym: %s\n", + feature, + (feature > 1) & 1, + (feature&1)?"Source":"Receiver"); + printf("\tFamily: 0x%02x - AccLVL: 0x%02x\n", family, accuracy); + printf("\tFRQ: %-9ld - min: %-9ld - max: %-9ld\n", + freq, min_freq, max_freq); + } + printf("\n"); + } +} + +/************************************************************************** +* +* Function name: ipmi_ekanalyzer_fru_file2structure +* +* Description: this function convert a FRU binary file into a linked list of +* FRU multi record +* +* Restriction: None +* +* Input/Ouput: filename1: name of the file that contain FRU binary data +* record: a pointer to current record +* list_head: a pointer to header of the list +* list_last: a pointer to tale of the list +* +* Global: None +* +* Return: return -1 as Error status, and 0 as Ok status +* +***************************************************************************/ +static int +ipmi_ekanalyzer_fru_file2structure(char * filename, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_record, + struct ipmi_ek_multi_header ** list_last) +{ + FILE * input_file; + unsigned char data; + unsigned char last_record = 0; + unsigned int multi_offset = 0; + int record_count = 0; + int ret = 0; + + input_file = fopen(filename, "r"); + if (input_file == NULL) { + lprintf(LOG_ERR, "File: '%s' is not found", filename); + return ERROR_STATUS; + } + + fseek(input_file, START_DATA_OFFSET, SEEK_SET); + data = 0; + ret = fread(&data, 1, 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Offset!"); + fclose(input_file); + return ERROR_STATUS; + } + if (data == 0) { + lprintf(LOG_ERR, "There is no multi record in the file '%s'", + filename); + fclose(input_file); + return ERROR_STATUS; + } + /* the offset value is in multiple of 8 bytes. */ + multi_offset = data * 8; + lprintf(LOG_DEBUG, "start multi offset = 0x%02x", + multi_offset ); + + fseek(input_file, multi_offset, SEEK_SET); + while (!feof(input_file)) { + *list_record = malloc(sizeof(struct ipmi_ek_multi_header)); + ret = fread(&(*list_record)->header, START_DATA_OFFSET, 1, + input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Header!"); + fclose(input_file); + return ERROR_STATUS; + } + if ((*list_record)->header.len == 0) { + record_count++; + continue; + } + (*list_record)->data = malloc((*list_record)->header.len); + if ((*list_record)->data == NULL) { + lprintf(LOG_ERR, "Failed to allocation memory size %d\n", + (*list_record)->header.len); + record_count++; + continue; + } + + ret = fread((*list_record)->data, ((*list_record)->header.len), + 1, input_file); + if ((ret != 1) || ferror(input_file)) { + lprintf(LOG_ERR, "Invalid Record Data!"); + fclose(input_file); + return ERROR_STATUS; + } + if (verbose > 0) + printf("Record %d has length = %02x\n", record_count, + (*list_record)->header.len); + if (verbose > 1) { + int i; + printf("Type: %02x", (*list_record)->header.type); + for (i = 0; i < ((*list_record)->header.len); i++) { + if (!(i % 8)) { + printf("\n0x%02x: ", i); + } + printf("%02x ", + (*list_record)->data[i]); + } + printf("\n\n"); + } + ipmi_ek_add_record2list(list_record, list_head, list_last); + /* mask the 8th bits to see if it is the last record */ + last_record = ((*list_record)->header.format) & 0x80; + if (last_record) { + break; + } + record_count++; + } + fclose(input_file); + return OK_STATUS; +} + + +/************************************************************************** +* +* Function name: ipmi_ek_add_record2list +* +* Description: this function adds a sigle FRU multi record to a linked list of +* FRU multi record. +* +* Restriction: None +* +* Input/Output: record: a pointer to current record +* list_head: a pointer to header of the list +* list_last: a pointer to tale of the list +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_add_record2list( struct ipmi_ek_multi_header ** record, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last ) +{ + if (*list_head == NULL) { + *list_head = *record; + (*record)->prev = NULL; + if (verbose > 2) + printf("Adding first record to list\n"); + } + else { + (*list_last)->next = *record; + (*record)->prev = *list_last; + if (verbose > 2) + printf("Add 1 record to list\n"); + } + *list_last = *record; + (*record)->next = NULL; +} + +/************************************************************************** +* +* Function name: ipmi_ek_remove_record_from_list +* +* Description: this function removes a sigle FRU multi record from a linked +* list of FRU multi record. +* +* Restriction: None +* +* Input/Output: record: a pointer to record to be deleted +* list_head: a pointer to header of the list +* list_last: a pointer to tale of the list +* +* Global: None +* +* Return: None +* +***************************************************************************/ +static void +ipmi_ek_remove_record_from_list( struct ipmi_ek_multi_header * record, + struct ipmi_ek_multi_header ** list_head, + struct ipmi_ek_multi_header ** list_last ) +{ + if (record->prev == NULL) + *list_head = record->next; + else + record->prev->next = record->next; + if ( record->next == NULL ) + (*list_last) = record->prev; + else + record->next->prev = record->prev; + free(record); + record = NULL; +} + + + + diff --git a/lib/ipmi_event.c b/lib/ipmi_event.c new file mode 100644 index 0000000..2f1032e --- /dev/null +++ b/lib/ipmi_event.c @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_event.h> +#include <ipmitool/ipmi_sdr.h> + + +static void +ipmi_event_msg_print(struct ipmi_intf * intf, struct platform_event_msg * pmsg) +{ + struct sel_event_record sel_event; + + memset(&sel_event, 0, sizeof(struct sel_event_record)); + + sel_event.record_id = 0; + sel_event.sel_type.standard_type.gen_id = 2; + + sel_event.sel_type.standard_type.evm_rev = pmsg->evm_rev; + sel_event.sel_type.standard_type.sensor_type = pmsg->sensor_type; + sel_event.sel_type.standard_type.sensor_num = pmsg->sensor_num; + sel_event.sel_type.standard_type.event_type = pmsg->event_type; + sel_event.sel_type.standard_type.event_dir = pmsg->event_dir; + sel_event.sel_type.standard_type.event_data[0] = pmsg->event_data[0]; + sel_event.sel_type.standard_type.event_data[1] = pmsg->event_data[1]; + sel_event.sel_type.standard_type.event_data[2] = pmsg->event_data[2]; + + if (verbose) + ipmi_sel_print_extended_entry_verbose(intf, &sel_event); + else + ipmi_sel_print_extended_entry(intf, &sel_event); +} + +static int +ipmi_send_platform_event(struct ipmi_intf * intf, struct platform_event_msg * emsg) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[8]; + uint8_t chmed; + + memset(&req, 0, sizeof(req)); + memset(rqdata, 0, 8); + + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = 0x02; + req.msg.data = rqdata; + + chmed = ipmi_current_channel_medium(intf); + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) { + /* system interface, need extra generator ID */ + req.msg.data_len = 8; + rqdata[0] = 0x41; // As per Fig. 29-2 and Table 5-4 + memcpy(rqdata+1, emsg, sizeof(struct platform_event_msg)); + } + else { + req.msg.data_len = 7; + memcpy(rqdata, emsg, sizeof(struct platform_event_msg)); + } + + ipmi_event_msg_print(intf, emsg); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Platform Event Message command failed"); + return -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Platform Event Message command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +#define EVENT_THRESH_STATE_LNC_LO 0 +#define EVENT_THRESH_STATE_LNC_HI 1 +#define EVENT_THRESH_STATE_LCR_LO 2 +#define EVENT_THRESH_STATE_LCR_HI 3 +#define EVENT_THRESH_STATE_LNR_LO 4 +#define EVENT_THRESH_STATE_LNR_HI 5 +#define EVENT_THRESH_STATE_UNC_LO 6 +#define EVENT_THRESH_STATE_UNC_HI 7 +#define EVENT_THRESH_STATE_UCR_LO 8 +#define EVENT_THRESH_STATE_UCR_HI 9 +#define EVENT_THRESH_STATE_UNR_LO 10 +#define EVENT_THRESH_STATE_UNR_HI 11 + +static const struct valstr ipmi_event_thresh_lo[] = { + { EVENT_THRESH_STATE_LNC_LO, "lnc" }, + { EVENT_THRESH_STATE_LCR_LO, "lcr" }, + { EVENT_THRESH_STATE_LNR_LO, "lnr" }, + { EVENT_THRESH_STATE_UNC_LO, "unc" }, + { EVENT_THRESH_STATE_UCR_LO, "ucr" }, + { EVENT_THRESH_STATE_UNR_LO, "unr" }, + { 0, NULL }, +}; +static const struct valstr ipmi_event_thresh_hi[] = { + { EVENT_THRESH_STATE_LNC_HI, "lnc" }, + { EVENT_THRESH_STATE_LCR_HI, "lcr" }, + { EVENT_THRESH_STATE_LNR_HI, "lnr" }, + { EVENT_THRESH_STATE_UNC_HI, "unc" }, + { EVENT_THRESH_STATE_UCR_HI, "ucr" }, + { EVENT_THRESH_STATE_UNR_HI, "unr" }, + { 0, NULL }, +}; + +static int +ipmi_send_platform_event_num(struct ipmi_intf * intf, int num) +{ + struct platform_event_msg emsg; + + memset(&emsg, 0, sizeof(struct platform_event_msg)); + + /* IPMB/LAN/etc */ + switch (num) { + case 1: /* temperature */ + printf("Sending SAMPLE event: Temperature - " + "Upper Critical - Going High\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x01; + emsg.sensor_num = 0x30; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x01; + emsg.event_data[0] = EVENT_THRESH_STATE_UCR_HI; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + case 2: /* voltage error */ + printf("Sending SAMPLE event: Voltage Threshold - " + "Lower Critical - Going Low\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x02; + emsg.sensor_num = 0x60; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x01; + emsg.event_data[0] = EVENT_THRESH_STATE_LCR_LO; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + case 3: /* correctable ECC */ + printf("Sending SAMPLE event: Memory - Correctable ECC\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x0c; + emsg.sensor_num = 0x53; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x6f; + emsg.event_data[0] = 0x00; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + default: + lprintf(LOG_ERR, "Invalid event number: %d", num); + return -1; + } + + return ipmi_send_platform_event(intf, &emsg); +} + +static int +ipmi_event_find_offset(uint8_t code, + struct ipmi_event_sensor_types * evt, + char * desc) +{ + if (desc == NULL || code == 0) + return 0x00; + + while (evt->type) { + if (evt->code == code && evt->desc != NULL && + strncasecmp(desc, evt->desc, __maxlen(desc, evt->desc)) == 0) + return evt->offset; + evt++; + } + + lprintf(LOG_WARN, "Unable to find matching event offset for '%s'", desc); + return -1; +} + +static void +print_sensor_states(uint8_t sensor_type, uint8_t event_type) +{ + ipmi_sdr_print_discrete_state_mini( + "Sensor States: \n ", "\n ", sensor_type, + event_type, 0xff, 0xff); + printf("\n"); +} + + +static int +ipmi_event_fromsensor(struct ipmi_intf * intf, char * id, char * state, char * evdir) +{ + struct ipmi_rs * rsp; + struct sdr_record_list * sdr; + struct platform_event_msg emsg; + int off; + uint8_t target, lun, channel; + + if (id == NULL) { + lprintf(LOG_ERR, "No sensor ID supplied"); + return -1; + } + + memset(&emsg, 0, sizeof(struct platform_event_msg)); + emsg.evm_rev = 0x04; + + if (evdir == NULL) + emsg.event_dir = EVENT_DIR_ASSERT; + else if (strncasecmp(evdir, "assert", 6) == 0) + emsg.event_dir = EVENT_DIR_ASSERT; + else if (strncasecmp(evdir, "deassert", 8) == 0) + emsg.event_dir = EVENT_DIR_DEASSERT; + else { + lprintf(LOG_ERR, "Invalid event direction %s. Must be 'assert' or 'deassert'", evdir); + return -1; + } + + printf("Finding sensor %s... ", id); + sdr = ipmi_sdr_find_sdr_byid(intf, id); + if (sdr == NULL) { + printf("not found!\n"); + return -1; + } + printf("ok\n"); + + switch (sdr->type) + { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + + emsg.sensor_type = sdr->record.common->sensor.type; + emsg.sensor_num = sdr->record.common->keys.sensor_num; + emsg.event_type = sdr->record.common->event_type; + target = sdr->record.common->keys.owner_id; + lun = sdr->record.common->keys.lun; + channel = sdr->record.common->keys.channel; + break; + default: + lprintf(LOG_ERR, "Unknown sensor type for id '%s'", id); + return -1; + } + + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + + switch (emsg.event_type) + { + /* + * Threshold Class + */ + case 1: + { + int dir = 0; + int hilo = 0; + off = 1; + + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + printf("Sensor States:\n"); + printf(" lnr : Lower Non-Recoverable \n"); + printf(" lcr : Lower Critical\n"); + printf(" lnc : Lower Non-Critical\n"); + printf(" unc : Upper Non-Critical\n"); + printf(" ucr : Upper Critical\n"); + printf(" unr : Upper Non-Recoverable\n"); + return -1; + } + + if (0 != strncasecmp(state, "lnr", 3) && + 0 != strncasecmp(state, "lcr", 3) && + 0 != strncasecmp(state, "lnc", 3) && + 0 != strncasecmp(state, "unc", 3) && + 0 != strncasecmp(state, "ucr", 3) && + 0 != strncasecmp(state, "unr", 3)) + { + lprintf(LOG_ERR, "Invalid threshold identifier %s", state); + return -1; + } + + if (state[0] == 'u') + hilo = 1; + else + hilo = 0; + + if (emsg.event_dir == EVENT_DIR_ASSERT) + dir = hilo; + else + dir = !hilo; + + if ((emsg.event_dir == EVENT_DIR_ASSERT && hilo == 1) || + (emsg.event_dir == EVENT_DIR_DEASSERT && hilo == 0)) + emsg.event_data[0] = (uint8_t)(str2val(state, ipmi_event_thresh_hi) & 0xf); + else if ((emsg.event_dir == EVENT_DIR_ASSERT && hilo == 0) || + (emsg.event_dir == EVENT_DIR_DEASSERT && hilo == 1)) + emsg.event_data[0] = (uint8_t)(str2val(state, ipmi_event_thresh_lo) & 0xf); + else { + lprintf(LOG_ERR, "Invalid Event"); + return -1; + } + + rsp = ipmi_sdr_get_sensor_thresholds(intf, emsg.sensor_num, + target, lun, channel); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Command Get Sensor Thresholds failed: invalid response."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Command Get Sensor Thresholds failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* threshold reading */ + emsg.event_data[2] = rsp->data[(emsg.event_data[0] / 2) + 1]; + + rsp = ipmi_sdr_get_sensor_hysteresis(intf, emsg.sensor_num, + target, lun, channel); + if (rsp != NULL && rsp->ccode == 0) + off = dir ? rsp->data[0] : rsp->data[1]; + if (off <= 0) + off = 1; + + /* trigger reading */ + if (dir) { + if ((emsg.event_data[2] + off) > 0xff) + emsg.event_data[1] = 0xff; + else + emsg.event_data[1] = emsg.event_data[2] + off; + } + else { + if ((emsg.event_data[2] - off) < 0) + emsg.event_data[1] = 0; + else + emsg.event_data[1] = emsg.event_data[2] - off; + } + + /* trigger in byte 2, threshold in byte 3 */ + emsg.event_data[0] |= 0x50; + } + break; + + /* + * Digital Discrete + */ + case 3: case 4: case 5: case 6: case 8: case 9: + { + int x; + const char * digi_on[] = { "present", "assert", "limit", + "fail", "yes", "on", "up" }; + const char * digi_off[] = { "absent", "deassert", "nolimit", + "nofail", "no", "off", "down" }; + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + printf("Sensor State Shortcuts:\n"); + for (x = 0; x < sizeof(digi_on)/sizeof(*digi_on); x++) { + printf(" %-9s %-9s\n", digi_on[x], digi_off[x]); + } + return 0; + } + + off = 0; + for (x = 0; x < sizeof(digi_on)/sizeof(*digi_on); x++) { + if (strncasecmp(state, digi_on[x], strlen(digi_on[x])) == 0) { + emsg.event_data[0] = 1; + off = 1; + break; + } + else if (strncasecmp(state, digi_off[x], strlen(digi_off[x])) == 0) { + emsg.event_data[0] = 0; + off = 1; + break; + } + } + if (off == 0) { + off = ipmi_event_find_offset( + emsg.event_type, generic_event_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + } + break; + + /* + * Generic Discrete + */ + case 2: case 7: case 10: case 11: case 12: + { + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + return 0; + } + off = ipmi_event_find_offset( + emsg.event_type, generic_event_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + break; + + /* + * Sensor-Specific Discrete + */ + case 0x6f: + { + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + return 0; + } + off = ipmi_event_find_offset( + emsg.sensor_type, sensor_specific_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + break; + + default: + return -1; + + } + + return ipmi_send_platform_event(intf, &emsg); +} + +static int +ipmi_event_fromfile(struct ipmi_intf * intf, char * file) +{ + FILE * fp; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct sel_event_record sel_event; + uint8_t rqdata[8]; + char buf[1024]; + char * ptr, * tok; + int i, j; + uint8_t chmed; + int rc = 0; + + if (file == NULL) + return -1; + + memset(rqdata, 0, 8); + + /* setup Platform Event Message command */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = 0x02; + req.msg.data = rqdata; + req.msg.data_len = 7; + + chmed = ipmi_current_channel_medium(intf); + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) { + /* system interface, need extra generator ID */ + rqdata[0] = 0x41; // As per Fig. 29-2 and Table 5-4 + req.msg.data_len = 8; + } + + fp = ipmi_open_file_read(file); + if (fp == NULL) + return -1; + + while (feof(fp) == 0) { + if (fgets(buf, 1024, fp) == NULL) + continue; + + /* clip off optional comment tail indicated by # */ + ptr = strchr(buf, '#'); + if (ptr) + *ptr = '\0'; + else + ptr = buf + strlen(buf); + + /* clip off trailing and leading whitespace */ + ptr--; + while (isspace((int)*ptr) && ptr >= buf) + *ptr-- = '\0'; + ptr = buf; + while (isspace((int)*ptr)) + ptr++; + if (strlen(ptr) == 0) + continue; + + /* parse the event, 7 bytes with optional comment */ + /* 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # event */ + i = 0; + tok = strtok(ptr, " "); + while (tok) { + if (i == 7) + break; + j = i++; + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) + j++; + rqdata[j] = (uint8_t)strtol(tok, NULL, 0); + tok = strtok(NULL, " "); + } + if (i < 7) { + lprintf(LOG_ERR, "Invalid Event: %s", + buf2str(rqdata, sizeof(rqdata))); + continue; + } + + memset(&sel_event, 0, sizeof(struct sel_event_record)); + sel_event.record_id = 0; + sel_event.sel_type.standard_type.gen_id = 2; + + j = (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) ? 1 : 0; + sel_event.sel_type.standard_type.evm_rev = rqdata[j++]; + sel_event.sel_type.standard_type.sensor_type = rqdata[j++]; + sel_event.sel_type.standard_type.sensor_num = rqdata[j++]; + sel_event.sel_type.standard_type.event_type = rqdata[j] & 0x7f; + sel_event.sel_type.standard_type.event_dir = (rqdata[j++] & 0x80) >> 7; + sel_event.sel_type.standard_type.event_data[0] = rqdata[j++]; + sel_event.sel_type.standard_type.event_data[1] = rqdata[j++]; + sel_event.sel_type.standard_type.event_data[2] = rqdata[j++]; + + ipmi_sel_print_std_entry(intf, &sel_event); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Platform Event Message command failed"); + rc = -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Platform Event Message command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + rc = -1; + } + } + + fclose(fp); + return rc; +} + +static void +ipmi_event_usage(void) +{ + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event <num>"); + lprintf(LOG_NOTICE, " Send generic test events"); + lprintf(LOG_NOTICE, " 1 : Temperature - Upper Critical - Going High"); + lprintf(LOG_NOTICE, " 2 : Voltage Threshold - Lower Critical - Going Low"); + lprintf(LOG_NOTICE, " 3 : Memory - Correctable ECC"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event file <filename>"); + lprintf(LOG_NOTICE, " Read and generate events from file"); + lprintf(LOG_NOTICE, " Use the 'sel save' command to generate from SEL"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event <sensorid> <state> [event_dir]"); + lprintf(LOG_NOTICE, " sensorid : Sensor ID string to use for event data"); + lprintf(LOG_NOTICE, " state : Sensor state, use 'list' to see possible states for sensor"); + lprintf(LOG_NOTICE, " event_dir : assert, deassert [default=assert]"); + lprintf(LOG_NOTICE, ""); +} + +int +ipmi_event_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { + ipmi_event_usage(); + return 0; + } + if (strncmp(argv[0], "file", 4) == 0) { + if (argc < 2) { + ipmi_event_usage(); + return 0; + } + return ipmi_event_fromfile(intf, argv[1]); + } + if (strlen(argv[0]) == 1) { + switch (argv[0][0]) { + case '1': return ipmi_send_platform_event_num(intf, 1); + case '2': return ipmi_send_platform_event_num(intf, 2); + case '3': return ipmi_send_platform_event_num(intf, 3); + } + } + if (argc < 2) + rc = ipmi_event_fromsensor(intf, argv[0], NULL, NULL); + else if (argc < 3) + rc = ipmi_event_fromsensor(intf, argv[0], argv[1], NULL); + else + rc = ipmi_event_fromsensor(intf, argv[0], argv[1], argv[2]); + + return rc; +} diff --git a/lib/ipmi_firewall.c b/lib/ipmi_firewall.c new file mode 100644 index 0000000..8bda398 --- /dev/null +++ b/lib/ipmi_firewall.c @@ -0,0 +1,1191 @@ +/* + * Copyright (c) 2005 International Business Machines, Inc. All Rights Reserved. + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/bswap.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_firewall.h> +#include <ipmitool/ipmi_strings.h> + +static void +printf_firewall_usage(void) +{ + lprintf(LOG_NOTICE, +"Firmware Firewall Commands:"); + lprintf(LOG_NOTICE, +"\tinfo [channel H] [lun L]"); + lprintf(LOG_NOTICE, +"\tinfo [channel H] [lun L [netfn N [command C [subfn S]]]]"); + lprintf(LOG_NOTICE, +"\tenable [channel H] [lun L [netfn N [command C [subfn S]]]]"); + lprintf(LOG_NOTICE, +"\tdisable [channel H] [lun L [netfn N [command C [subfn S]]]] [force])"); + lprintf(LOG_NOTICE, +"\treset [channel H]"); + lprintf(LOG_NOTICE, +"\t\twhere H is a Channel, L is a LUN, N is a NetFn,"); + lprintf(LOG_NOTICE, +"\t\tC is a Command and S is a Sub-Function"); +} + +void +printf_firewall_info_usage(void) +{ + lprintf(LOG_NOTICE, +"info [channel H]"); + lprintf(LOG_NOTICE, +"\tList all of the firewall information for all LUNs, NetFns"); + lprintf(LOG_NOTICE, +"\tand Commands, This is a long list and is not very human readable."); + lprintf(LOG_NOTICE, +"info [channel H] lun L"); + lprintf(LOG_NOTICE, +"\tThis also prints a long list that is not very human readable."); + lprintf(LOG_NOTICE, +"info [channel H] lun L netfn N"); + lprintf(LOG_NOTICE, +"\tThis prints out information for a single LUN/NetFn pair."); + lprintf(LOG_NOTICE, +"\tThat is not really very usable, but at least it is short."); + lprintf(LOG_NOTICE, +"info [channel H] lun L netfn N command C"); + lprintf(LOG_NOTICE, +"\tThis is the one you want -- it prints out detailed human"); + lprintf(LOG_NOTICE, +"\treadable information. It shows the support, configurable, and"); + lprintf(LOG_NOTICE, +"\tenabled bits for the Command C on LUN/NetFn pair L,N and the"); + lprintf(LOG_NOTICE, +"\tsame information about each of its Sub-functions."); +} + +// print n bytes of bit field bf (if invert, print ~bf) +static void print_bitfield(const unsigned char * bf, int n, int invert, int loglevel) { + int i = 0; + if (loglevel < 0) { + while (i<n) { + printf("%02x", (unsigned char) (invert?~bf[i]:bf[i])); + if (++i % 4 == 0) + printf(" "); + } + printf("\n"); + } else { + while (i<n) { + lprintf(loglevel, "%02x", (unsigned char) (invert?~bf[i]:bf[i])); + if (++i % 4 == 0) + lprintf(loglevel, " "); + } + lprintf(loglevel, "\n"); + } + +} + +static int +ipmi_firewall_parse_args(int argc, char ** argv, struct ipmi_function_params * p) +{ + int i; + uint8_t conv_err = 0; + + if (!p) { + lprintf(LOG_ERR, "ipmi_firewall_parse_args: p is NULL"); + return -1; + } + for (i=0; i<argc; i++) { + if (strncmp(argv[i], "channel", 7) == 0 && (++i < argc)) { + uint8_t channel_tmp = 0; + if (is_ipmi_channel_num(argv[i], &channel_tmp) != 0) { + conv_err = 1; + break; + } else { + p->channel = channel_tmp; + } + } + else if (strncmp(argv[i], "lun", 3) == 0 && (++i < argc)) { + if (str2int(argv[i], &(p->lun)) != 0) { + lprintf(LOG_ERR, "Given lun '%s' is invalid.", argv[i]); + conv_err = 1; + break; + } + } + else if (strncmp(argv[i], "force", 5) == 0) { + p->force = 1; + } + else if (strncmp(argv[i], "netfn", 5) == 0 && (++i < argc)) { + if (str2int(argv[i], &(p->netfn)) != 0) { + lprintf(LOG_ERR, "Given netfn '%s' is invalid.", argv[i]); + conv_err = 1; + break; + } + } + else if (strncmp(argv[i], "command", 7) == 0 && (++i < argc)) { + if (str2int(argv[i], &(p->command)) != 0) { + lprintf(LOG_ERR, "Given command '%s' is invalid.", argv[i]); + conv_err = 1; + break; + } + } + else if (strncmp(argv[i], "subfn", 5) == 0 && (++i < argc)) { + if (str2int(argv[i], &(p->subfn)) != 0) { + lprintf(LOG_ERR, "Given subfn '%s' is invalid.", argv[i]); + conv_err = 1; + break; + } + } + } + if (conv_err != 0) { + return (-1); + } + if (p->subfn >= MAX_SUBFN) { + lprintf(LOG_ERR, "subfn is out of range (0-%d)", MAX_SUBFN-1); + return -1; + } + if (p->command >= MAX_COMMAND) { + lprintf(LOG_ERR, "command is out of range (0-%d)", MAX_COMMAND-1); + return -1; + } + if (p->netfn >= MAX_NETFN) { + lprintf(LOG_ERR, "netfn is out of range (0-%d)", MAX_NETFN-1); + return -1; + } + if (p->lun >= MAX_LUN) { + lprintf(LOG_ERR, "lun is out of range (0-%d)", MAX_LUN-1); + return -1; + } + if (p->netfn >= 0 && p->lun < 0) { + lprintf(LOG_ERR, "if netfn is set, so must be lun"); + return -1; + } + if (p->command >= 0 && p->netfn < 0) { + lprintf(LOG_ERR, "if command is set, so must be netfn"); + return -1; + } + if (p->subfn >= 0 && p->command < 0) { + lprintf(LOG_ERR, "if subfn is set, so must be command"); + return -1; + } + return 0; +} + +/* _get_netfn_suport + * + * @intf: ipmi interface + * @channel: ipmi channel + * @lun: a pointer to a 4 byte field + * @netfn: a pointer to a 128-bit bitfield (16 bytes) + * + * returns 0 on success and fills in the bitfield for + * the 32 netfn * 4 LUN pairs that support commands + * returns -1 on error + */ +static int +_get_netfn_support(struct ipmi_intf * intf, int channel, unsigned char * lun, unsigned char * netfn) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char * d, rqdata; + unsigned int l; + + if (!lun || !netfn) { + lprintf(LOG_ERR, "_get_netfn_suport: lun or netfn is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_NETFN_SUPPORT; + rqdata = (unsigned char) channel; + req.msg.data = &rqdata; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get NetFn Support command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get NetFn Support command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (l=0; l<4; l++) { + lun[l] = (*d)>>(2*l) & 0x3; + } + d++; + + memcpy(netfn, d, 16); + + return 0; +} + +/* _get_command_suport + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @lnfn: a pointer to a struct lun_netfn_support + * + * returns 0 on success and fills in lnfn according to the request in p + * returns -1 on error + */ +static int +_get_command_support(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct lun_netfn_support * lnfn) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char * d, rqdata[3]; + unsigned int c; + + if (!p || !lnfn) { + lprintf(LOG_ERR, "_get_netfn_suport: p or lnfn is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_SUPPORT; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Support (LUN=%d, NetFn=%d, op=0) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Support (LUN=%d, NetFn=%d, op=0) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (!(d[c>>3] & (1<<(c%8)))) + lnfn->command[c].support |= BIT_AVAILABLE; + } + memcpy(lnfn->command_mask, d, MAX_COMMAND_BYTES/2); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_SUPPORT; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = 0x40 | p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Support (LUN=%d, NetFn=%d, op=1) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Support (LUN=%d, NetFn=%d, op=1) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (!(d[c>>3] & (1<<(c%8)))) + lnfn->command[128+c].support |= BIT_AVAILABLE; + } + memcpy(lnfn->command_mask+MAX_COMMAND_BYTES/2, d, MAX_COMMAND_BYTES/2); + return 0; +} + +/* _get_command_configurable + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @lnfn: a pointer to a struct lun_netfn_support + * + * returns 0 on success and fills in lnfn according to the request in p + * returns -1 on error + */ +static int +_get_command_configurable(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct lun_netfn_support * lnfn) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char * d, rqdata[3]; + unsigned int c; + + if (!p || !lnfn) { + lprintf(LOG_ERR, "_get_command_configurable: p or lnfn is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_CONFIGURABLE_COMMANDS; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Configurable Command (LUN=%d, NetFn=%d, op=0) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Configurable Command (LUN=%d, NetFn=%d, op=0) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (d[c>>3] & (1<<(c%8))) + lnfn->command[c].support |= BIT_CONFIGURABLE; + } + memcpy(lnfn->config_mask, d, MAX_COMMAND_BYTES/2); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_CONFIGURABLE_COMMANDS; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = 0x40 | p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Configurable Command (LUN=%d, NetFn=%d, op=1) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Configurable Command (LUN=%d, NetFn=%d, op=1) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (d[c>>3] & (1<<(c%8))) + lnfn->command[128+c].support |= BIT_CONFIGURABLE; + } + memcpy(lnfn->config_mask+MAX_COMMAND_BYTES/2, d, MAX_COMMAND_BYTES/2); + return 0; +} + +/* _get_command_enables + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @lnfn: a pointer to a struct lun_netfn_support + * + * returns 0 on success and fills in lnfn according to the request in p + * returns -1 on error + */ +static int +_get_command_enables(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct lun_netfn_support * lnfn) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char * d, rqdata[3]; + unsigned int c; + + if (!p || !lnfn) { + lprintf(LOG_ERR, "_get_command_enables: p or lnfn is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Enables (LUN=%d, NetFn=%d, op=0) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Enables (LUN=%d, NetFn=%d, op=0) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (d[c>>3] & (1<<(c%8))) + lnfn->command[c].support |= BIT_ENABLED; + } + memcpy(lnfn->enable_mask, d, MAX_COMMAND_BYTES/2); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = 0x40 | p->netfn; + rqdata[2] = p->lun; + req.msg.data = rqdata; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Enables (LUN=%d, NetFn=%d, op=1) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Enables (LUN=%d, NetFn=%d, op=1) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + for (c=0; c<128; c++) { + if (d[c>>3] & (1<<(c%8))) + lnfn->command[128+c].support |= BIT_ENABLED; + } + memcpy(lnfn->enable_mask+MAX_COMMAND_BYTES/2, d, MAX_COMMAND_BYTES/2); + return 0; +} + +/* _set_command_enables + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @lnfn: a pointer to a struct lun_netfn_support that contains current info + * @enable: a pointer to a 32 byte bitfield that contains the desired enable state + * @gun: here is a gun to shoot yourself in the foot. If this is true + * you are allowed to disable this command + * + * returns 0 on success + * returns -1 on error + */ +static int +_set_command_enables(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct lun_netfn_support * lnfn, + unsigned char * enable, int gun) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char * d, rqdata[19]; + unsigned int c; + + if (!p || !lnfn) { + lprintf(LOG_ERR, "_set_command_enables: p or lnfn is NULL"); + return -1; + } + + lprintf(LOG_INFO, "support: "); + print_bitfield(lnfn->command_mask, MAX_COMMAND_BYTES, 1, LOG_INFO); + lprintf(LOG_INFO, "configurable: "); + print_bitfield(lnfn->config_mask, MAX_COMMAND_BYTES, 0, LOG_INFO); + lprintf(LOG_INFO, "enabled: "); + print_bitfield(lnfn->enable_mask, MAX_COMMAND_BYTES, 0, LOG_INFO); + lprintf(LOG_INFO, "enable mask before: "); + print_bitfield(enable, MAX_COMMAND_BYTES, 0, LOG_INFO); + + // mask off the appropriate bits (if not configurable, set enable bit + // must be the same as the current enable bit) + for (c=0; c<(MAX_COMMAND_BYTES); c++) { + enable[c] = (lnfn->config_mask[c] & enable[c]) | + (~lnfn->config_mask[c] & lnfn->enable_mask[c]); + } + + // take the gun out of their hand if they are not supposed to have it + if (!gun) { + enable[SET_COMMAND_ENABLE_BYTE] = + (lnfn->config_mask[SET_COMMAND_ENABLE_BYTE] + & SET_COMMAND_ENABLE_BIT) | + (~lnfn->config_mask[SET_COMMAND_ENABLE_BYTE] + & lnfn->enable_mask[SET_COMMAND_ENABLE_BYTE]); + } + lprintf(LOG_INFO, "enable mask after: "); + print_bitfield(enable, MAX_COMMAND_BYTES, 0, LOG_INFO); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_SET_COMMAND_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + memcpy(&rqdata[3], enable, MAX_COMMAND_BYTES/2); + req.msg.data = rqdata; + req.msg.data_len = 19; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Command Enables (LUN=%d, NetFn=%d, op=0) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Command Enables (LUN=%d, NetFn=%d, op=0) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_SET_COMMAND_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = 0x40 | p->netfn; + rqdata[2] = p->lun; + memcpy(&rqdata[3], enable+MAX_COMMAND_BYTES/2, MAX_COMMAND_BYTES/2); + req.msg.data = rqdata; + req.msg.data_len = 19; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Command Enables (LUN=%d, NetFn=%d, op=1) command failed", p->lun, p->netfn); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Command Enables (LUN=%d, NetFn=%d, op=1) command failed: %s", + p->lun, p->netfn, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + d = rsp->data; + return 0; +} + +/* _get_subfn_support + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @cmd: a pointer to a struct command_support + * + * returns 0 on success and fills in cmd according to the request in p + * returns -1 on error + */ +static int +_get_subfn_support(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct command_support * cmd) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char rqdata[4]; + + if (!p || !cmd) { + lprintf(LOG_ERR, "_get_subfn_support: p or cmd is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_SUBFUNCTION_SUPPORT; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + rqdata[3] = p->command; + req.msg.data = rqdata; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Sub-function Support (LUN=%d, NetFn=%d, command=%d) command failed", p->lun, p->netfn, p->command); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Sub-function Support (LUN=%d, NetFn=%d, command=%d) command failed: %s", + p->lun, p->netfn, p->command, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(cmd->subfn_support, rsp->data, sizeof(cmd->subfn_support)); + return 0; +} + +/* _get_subfn_configurable + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @cmd: a pointer to a struct command_support + * + * returns 0 on success and fills in cmd according to the request in p + * returns -1 on error + */ +static int +_get_subfn_configurable(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct command_support * cmd) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char rqdata[4]; + + if (!p || !cmd) { + lprintf(LOG_ERR, "_get_subfn_configurable: p or cmd is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_CONFIGURABLE_COMMAND_SUBFUNCTIONS; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + rqdata[3] = p->command; + req.msg.data = rqdata; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Configurable Command Sub-function (LUN=%d, NetFn=%d, command=%d) command failed", p->lun, p->netfn, p->command); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Configurable Command Sub-function (LUN=%d, NetFn=%d, command=%d) command failed: %s", + p->lun, p->netfn, p->command, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(cmd->subfn_config, rsp->data, sizeof(cmd->subfn_config)); + return 0; +} + +/* _get_subfn_enables + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @cmd: a pointer to a struct command_support + * + * returns 0 on success and fills in cmd according to the request in p + * returns -1 on error + */ +static int +_get_subfn_enables(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct command_support * cmd) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char rqdata[4]; + + if (!p || !cmd) { + lprintf(LOG_ERR, "_get_subfn_enables: p or cmd is NULL"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_COMMAND_SUBFUNCTION_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + rqdata[3] = p->command; + req.msg.data = rqdata; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Command Sub-function Enables (LUN=%d, NetFn=%d, command=%d) command failed", p->lun, p->netfn, p->command); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Command Sub-function Enables (LUN=%d, NetFn=%d, command=%d) command failed: %s", + p->lun, p->netfn, p->command, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(cmd->subfn_enable, rsp->data, sizeof(cmd->subfn_enable)); + return 0; +} + +/* _set_subfn_enables + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @cmd: a pointer to a struct command_support + * @enable: a pointer to a 4 byte bitfield that contains the desired enable state + * + * returns 0 on success (and modifies enable to be the bits it actually set) + * returns -1 on error + */ +static int +_set_subfn_enables(struct ipmi_intf * intf, + struct ipmi_function_params * p, struct command_support * cmd, + unsigned char * enable) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char rqdata[8]; + unsigned int c; + + if (!p || !cmd) { + lprintf(LOG_ERR, "_set_subfn_enables: p or cmd is NULL"); + return -1; + } + + lprintf(LOG_INFO, "support: "); + print_bitfield(cmd->subfn_support, MAX_SUBFN_BYTES, 1, LOG_INFO); + lprintf(LOG_INFO, "configurable: "); + print_bitfield(cmd->subfn_config, MAX_SUBFN_BYTES, 0, LOG_INFO); + lprintf(LOG_INFO, "enabled: "); + print_bitfield(cmd->subfn_enable, MAX_SUBFN_BYTES, 0, LOG_INFO); + lprintf(LOG_INFO, "enable mask before: "); + print_bitfield(enable, MAX_SUBFN_BYTES, 0, LOG_INFO); + // mask off the appropriate bits (if not configurable, set enable bit + // must be the same as the current enable bit) + for (c=0; c<sizeof(cmd->subfn_enable); c++) { + enable[c] = (cmd->subfn_config[c] & enable[c]) | + (~cmd->subfn_config[c] & cmd->subfn_enable[c]); + } + lprintf(LOG_INFO, "enable mask after: "); + print_bitfield(enable, MAX_SUBFN_BYTES, 0, LOG_INFO); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_SET_COMMAND_SUBFUNCTION_ENABLES; + rqdata[0] = (unsigned char) p->channel; + rqdata[1] = p->netfn; + rqdata[2] = p->lun; + rqdata[3] = p->command; + memcpy(&rqdata[4], enable, MAX_SUBFN_BYTES); + req.msg.data = rqdata; + req.msg.data_len = 8; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Command Sub-function Enables (LUN=%d, NetFn=%d, command=%d) command failed", p->lun, p->netfn, p->command); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Command Sub-function Enables (LUN=%d, NetFn=%d, command=%d) command failed: %s", + p->lun, p->netfn, p->command, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +/* _gather_info + * + * @intf: ipmi interface + * @p: a pointer to a struct ipmi_function_params + * @bmc: a pointer to a struct bmc_fn_support + * @enable: a pointer to a 4 byte bitfield that contains the desired enable state + * + * returns 0 on success and fills in bmc according to request p + * returns -1 on error + */ +static int _gather_info(struct ipmi_intf * intf, struct ipmi_function_params * p, struct bmc_fn_support * bmc) +{ + int ret, l, n; + unsigned char lun[MAX_LUN], netfn[16]; + + ret = _get_netfn_support(intf, p->channel, lun, netfn); + if (!ret) { + for (l=0; l<MAX_LUN; l++) { + if (p->lun >= 0 && p->lun != l) + continue; + bmc->lun[l].support = lun[l]; + if (lun[l]) { + for (n=0; n<MAX_NETFN_PAIR; n++) { + int offset = l*MAX_NETFN_PAIR+n; + bmc->lun[l].netfn[n].support = + !!(netfn[offset>>3] & (1<<(offset%8))); + } + } + } + } + if (p->netfn >= 0) { + if (!((p->lun < 0 || bmc->lun[p->lun].support) && + (p->netfn < 0 || bmc->lun[p->lun].netfn[p->netfn>>1].support))) { + lprintf(LOG_ERR, "LUN or LUN/NetFn pair %d,%d not supported", p->lun, p->netfn); + return 0; + } + ret = _get_command_support(intf, p, &(bmc->lun[p->lun].netfn[p->netfn>>1])); + ret |= _get_command_configurable(intf, p, &(bmc->lun[p->lun].netfn[p->netfn>>1])); + ret |= _get_command_enables(intf, p, &(bmc->lun[p->lun].netfn[p->netfn>>1])); + if (!ret && p->command >= 0) { + ret = _get_subfn_support(intf, p, + &(bmc->lun[p->lun].netfn[p->netfn>>1].command[p->command])); + ret |= _get_subfn_configurable(intf, p, + &(bmc->lun[p->lun].netfn[p->netfn>>1].command[p->command])); + ret |= _get_subfn_enables(intf, p, + &(bmc->lun[p->lun].netfn[p->netfn>>1].command[p->command])); + } + } + else if (p->lun >= 0) { + l = p->lun; + if (bmc->lun[l].support) { + for (n=0; n<MAX_NETFN_PAIR; n++) { + p->netfn = n*2; + if (bmc->lun[l].netfn[n].support) { + ret = _get_command_support(intf, p, &(bmc->lun[l].netfn[n])); + ret |= _get_command_configurable(intf, p, &(bmc->lun[l].netfn[n])); + ret |= _get_command_enables(intf, p, &(bmc->lun[l].netfn[n])); + } + if (ret) + bmc->lun[l].netfn[n].support = 0; + } + } + p->netfn = -1; + } else { + for (l=0; l<4; l++) { + p->lun = l; + if (bmc->lun[l].support) { + for (n=0; n<MAX_NETFN_PAIR; n++) { + p->netfn = n*2; + if (bmc->lun[l].netfn[n].support) { + ret = _get_command_support(intf, p, &(bmc->lun[l].netfn[n])); + ret |= _get_command_configurable(intf, p, &(bmc->lun[l].netfn[n])); + ret |= _get_command_enables(intf, p, &(bmc->lun[l].netfn[n])); + } + if (ret) + bmc->lun[l].netfn[n].support = 0; + } + } + } + p->lun = -1; + p->netfn = -1; + } + + return 0; +} + +/* ipmi_firewall_info - print out info for firewall functions + * + * @intf: ipmi inteface + * @argc: argument count + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_firewall_info(struct ipmi_intf * intf, int argc, char ** argv) +{ + int ret = 0; + struct ipmi_function_params p = {0xe, -1, -1, -1, -1}; + struct bmc_fn_support * bmc_fn_support; + unsigned int l, n, c; + + if ((argc > 0 && strncmp(argv[0], "help", 4) == 0) || ipmi_firewall_parse_args(argc, argv, &p) < 0) + { + printf_firewall_info_usage(); + return 0; + } + + bmc_fn_support = malloc(sizeof(struct bmc_fn_support)); + if (!bmc_fn_support) { + lprintf(LOG_ERR, "malloc struct bmc_fn_support failed"); + return -1; + } + + ret = _gather_info(intf, &p, bmc_fn_support); + + if (p.command >= 0) { + struct command_support * cmd; + if (!((p.lun < 0 || bmc_fn_support->lun[p.lun].support) && + (p.netfn < 0 || bmc_fn_support->lun[p.lun].netfn[p.netfn>>1].support) && + bmc_fn_support->lun[p.lun].netfn[p.netfn>>1].command[p.command].support)) + { + lprintf(LOG_ERR, "Command 0x%02x not supported on LUN/NetFn pair %02x,%02x", + p.command, p.lun, p.netfn); + free(bmc_fn_support); + bmc_fn_support = NULL; + return 0; + } + cmd = + &bmc_fn_support->lun[p.lun].netfn[p.netfn>>1].command[p.command]; + c = cmd->support; + printf("(A)vailable, (C)onfigurable, (E)nabled: | A | C | E |\n"); + printf("-----------------------------------------------------\n"); + printf("LUN %01d, NetFn 0x%02x, Command 0x%02x: | %c | %c | %c |\n", + p.lun, p.netfn, p.command, + (c & BIT_AVAILABLE) ? 'X' : ' ', + (c & BIT_CONFIGURABLE) ? 'X' : ' ', + (c & BIT_ENABLED) ? 'X': ' '); + + for (n=0; n<MAX_SUBFN; n++) { + printf("sub-function 0x%02x: | %c | %c | %c |\n", n, + (!bit_test(cmd->subfn_support, n)) ? 'X' : ' ', + (bit_test(cmd->subfn_config, n)) ? 'X' : ' ', + (bit_test(cmd->subfn_enable, n)) ? 'X' : ' '); + } + } + else if (p.netfn >= 0) { + if (!((p.lun < 0 || bmc_fn_support->lun[p.lun].support) && + (bmc_fn_support->lun[p.lun].netfn[p.netfn>>1].support))) + { + lprintf(LOG_ERR, "LUN or LUN/NetFn pair %02x,%02x not supported", + p.lun, p.netfn); + free(bmc_fn_support); + bmc_fn_support = NULL; + return 0; + } + n = p.netfn >> 1; + l = p.lun; + printf("Commands on LUN 0x%02x, NetFn 0x%02x\n", p.lun, p.netfn); + printf("support: "); + print_bitfield(bmc_fn_support->lun[l].netfn[n].command_mask, + MAX_COMMAND_BYTES, 1, -1); + printf("configurable: "); + print_bitfield(bmc_fn_support->lun[l].netfn[n].config_mask, + MAX_COMMAND_BYTES, 0, -1); + printf("enabled: "); + print_bitfield(bmc_fn_support->lun[l].netfn[n].enable_mask, + MAX_COMMAND_BYTES, 0, -1); + } + else { + for (l=0; l<4; l++) { + p.lun = l; + if (bmc_fn_support->lun[l].support) { + for (n=0; n<MAX_NETFN_PAIR; n++) { + p.netfn = n*2; + if (bmc_fn_support->lun[l].netfn[n].support) { + printf("%02x,%02x support: ", p.lun, p.netfn); + print_bitfield(bmc_fn_support->lun[l].netfn[n].command_mask, + MAX_COMMAND_BYTES, 1, -1); + printf("%02x,%02x configurable: ", p.lun, p.netfn); + print_bitfield(bmc_fn_support->lun[l].netfn[n].config_mask, + MAX_COMMAND_BYTES, 0, -1); + printf("%02x,%02x enabled: ", p.lun, p.netfn); + print_bitfield(bmc_fn_support->lun[l].netfn[n].enable_mask, + MAX_COMMAND_BYTES, 0, -1); + } + } + } + } + p.lun = -1; + p.netfn = -1; + } + + free(bmc_fn_support); + bmc_fn_support = NULL; + return ret; +} + +/* ipmi_firewall_enable_disable - enable/disable BMC functions + * + * @intf: ipmi inteface + * @enable: whether to enable or disable + * @argc: argument count + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_firewall_enable_disable(struct ipmi_intf * intf, int enable, int argc, char ** argv) +{ + struct ipmi_function_params p = {0xe, -1, -1, -1, -1}; + struct bmc_fn_support * bmc_fn_support; + unsigned int l, n, c, ret; + unsigned char enables[MAX_COMMAND_BYTES]; + + if (argc < 1 || strncmp(argv[0], "help", 4) == 0) { + char * s1 = enable?"en":"dis"; + char * s2 = enable?"":" [force]"; + printf("%sable [channel H] lun L netfn N%s\n", s1, s2); + printf("\t%sable all commands on this LUN/NetFn pair\n", s1); + printf("%sable [channel H] lun L netfn N command C%s\n", s1, s2); + printf("\t%sable Command C and all its Sub-functions for this LUN/NetFn pair\n", s1); + printf("%sable [channel H] lun L netfn N command C subfn S\n", s1); + printf("\t%sable Sub-function S for Command C for this LUN/NetFn pair\n", s1); + if (!enable) { + printf("* force will allow you to disable the \"Command Set Enable\" command\n"); + printf("\tthereby letting you shoot yourself in the foot\n"); + printf("\tthis is only recommended for advanced users\n"); + } + return 0; + } + if (ipmi_firewall_parse_args(argc, argv, &p) < 0) + return -1; + + bmc_fn_support = malloc(sizeof(struct bmc_fn_support)); + if (!bmc_fn_support) { + lprintf(LOG_ERR, "malloc struct bmc_fn_support failed"); + return -1; + } + + ret = _gather_info(intf, &p, bmc_fn_support); + if (ret < 0) { + free(bmc_fn_support); + bmc_fn_support = NULL; + return ret; + } + + l = p.lun; + n = p.netfn>>1; + c = p.command; + if (p.subfn >= 0) { + // firewall (en|dis)able [channel c] lun l netfn n command m subfn s + // (en|dis)able this sub-function for this commnad on this lun/netfn pair + memcpy(enables, + bmc_fn_support->lun[l].netfn[n].command[c].subfn_enable, + MAX_SUBFN_BYTES); + bit_set(enables, p.subfn, enable); + ret = _set_subfn_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n].command[c], enables); + + } else if (p.command >= 0) { + // firewall (en|dis)able [channel c] lun l netfn n command m + // (en|dis)able all subfn and command for this commnad on this lun/netfn pair + memset(enables, enable?0xff:0, MAX_SUBFN_BYTES); + ret = _set_subfn_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n].command[c], enables); + memcpy(enables, + &bmc_fn_support->lun[l].netfn[n].enable_mask, sizeof(enables)); + bit_set(enables, p.command, enable); + ret |= _set_command_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n], enables, p.force); + } else if (p.netfn >= 0) { + // firewall (en|dis)able [channel c] lun l netfn n + // (en|dis)able all commnads on this lun/netfn pair + memset(enables, enable?0xff:0, sizeof(enables)); + ret = _set_command_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n], enables, p.force); + /* + } else if (p.lun >= 0) { + // firewall (en|dis)able [channel c] lun l + // (en|dis)able all commnads on all netfn pairs for this lun + */ + } + free(bmc_fn_support); + bmc_fn_support = NULL; + return ret; +} + +/* ipmi_firewall_reset - reset firmware firewall to enable everything + * + * @intf: ipmi inteface + * @argc: argument count + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_firewall_reset(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_function_params p = {0xe, -1, -1, -1, -1}; + struct bmc_fn_support * bmc_fn_support; + unsigned int l, n, c, ret; + unsigned char enables[MAX_COMMAND_BYTES]; + + if (argc > 0 || (argc > 0 && strncmp(argv[0], "help", 4) == 0)) { + printf_firewall_usage(); + return 0; + } + if (ipmi_firewall_parse_args(argc, argv, &p) < 0) + return -1; + + bmc_fn_support = malloc(sizeof(struct bmc_fn_support)); + if (!bmc_fn_support) { + lprintf(LOG_ERR, "malloc struct bmc_fn_support failed"); + return -1; + } + + ret = _gather_info(intf, &p, bmc_fn_support); + if (ret < 0) { + free(bmc_fn_support); + bmc_fn_support = NULL; + return ret; + } + + for (l=0; l<MAX_LUN; l++) { + p.lun = l; + for (n=0; n<MAX_NETFN; n+=2) { + p.netfn = n; + for (c=0; c<MAX_COMMAND; c++) { + p.command = c; + printf("reset lun %d, netfn %d, command %d, subfn\n", l, n, c); + memset(enables, 0xff, MAX_SUBFN_BYTES); + ret = _set_subfn_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n].command[c], enables); + } + printf("reset lun %d, netfn %d, command\n", l, n); + memset(enables, 0xff, sizeof(enables)); + ret = _set_command_enables(intf, &p, + &bmc_fn_support->lun[l].netfn[n], enables, 0); + } + } + + free(bmc_fn_support); + bmc_fn_support = NULL; + return ret; +} + + +/* ipmi_firewall_main - top-level handler for firmware firewall functions + * + * @intf: ipmi interface + * @argc: number of arguments + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_firewall_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc < 1 || strncmp(argv[0], "help", 4) == 0) { + printf_firewall_usage(); + } + else if (strncmp(argv[0], "info", 4) == 0) { + rc = ipmi_firewall_info(intf, argc-1, &(argv[1])); + } + else if (strncmp(argv[0], "enable", 6) == 0) { + rc = ipmi_firewall_enable_disable(intf, 1, argc-1, &(argv[1])); + } + else if (strncmp(argv[0], "disable", 7) == 0) { + rc = ipmi_firewall_enable_disable(intf, 0, argc-1, &(argv[1])); + } + else if (strncmp(argv[0], "reset", 5) == 0) { + rc = ipmi_firewall_reset(intf, argc-1, &(argv[1])); + } + else { + printf_firewall_usage(); + } + + return rc; +} diff --git a/lib/ipmi_fru.c b/lib/ipmi_fru.c new file mode 100644 index 0000000..1b2e0cd --- /dev/null +++ b/lib/ipmi_fru.c @@ -0,0 +1,5209 @@ +/* +* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistribution of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistribution in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of Sun Microsystems, Inc. or the names of +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* This software is provided "AS IS," without a warranty of any kind. +* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +* SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +* OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +* OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +*/ + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_strings.h> /* IANA id strings */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#define FRU_MULTIREC_CHUNK_SIZE (255 + sizeof(struct fru_multirec_header)) + +extern int verbose; + +static void ipmi_fru_read_to_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static void ipmi_fru_write_from_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static int ipmi_fru_upg_ekeying(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static int ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, uint8_t fruId, + struct fru_info *pFruInfo, uint32_t * pRetLocation, + uint32_t * pRetSize); +static int ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea, + uint32_t size, uint32_t offset); +static int ipmi_fru_get_multirec_size_from_file(char * pFileName, uint32_t * pSize, uint32_t * pOffset); +int ipmi_fru_get_adjust_size_from_buffer(uint8_t *pBufArea, uint32_t *pSize); +static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length); + +static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned + char fruId, uint8_t f_type, uint8_t f_index, char *f_string); +static int +ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId, + struct fru_info fru, struct fru_header header, + uint8_t f_type, uint8_t f_index, char *f_string); + +static void +fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset); +int +read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, + uint32_t offset, uint32_t length, uint8_t *frubuf); +void free_fru_bloc(t_ipmi_fru_bloc *bloc); + +/* get_fru_area_str - Parse FRU area string from raw data +* +* @data: raw FRU data +* @offset: offset into data for area +* +* returns pointer to FRU area string +*/ +char * get_fru_area_str(uint8_t * data, uint32_t * offset) +{ + static const char bcd_plus[] = "0123456789 -.:,_"; + char * str; + int len, off, size, i, j, k, typecode; + union { + uint32_t bits; + char chars[4]; + } u; + + size = 0; + off = *offset; + + /* bits 6:7 contain format */ + typecode = ((data[off] & 0xC0) >> 6); + + // printf("Typecode:%i\n", typecode); + /* bits 0:5 contain length */ + len = data[off++]; + len &= 0x3f; + + switch (typecode) { + case 0: /* 00b: binary/unspecified */ + /* hex dump -> 2x length */ + size = (len*2); + break; + case 2: /* 10b: 6-bit ASCII */ + /* 4 chars per group of 1-3 bytes */ + size = ((((len+2)*4)/3) & ~3); + break; + case 3: /* 11b: 8-bit ASCII */ + case 1: /* 01b: BCD plus */ + /* no length adjustment */ + size = len; + break; + } + + if (size < 1) { + *offset = off; + return NULL; + } + str = malloc(size+1); + if (str == NULL) + return NULL; + memset(str, 0, size+1); + + if (len == 0) { + str[0] = '\0'; + *offset = off; + return str; + } + + switch (typecode) { + case 0: /* Binary */ + strncpy(str, buf2str(&data[off], len), len*2); + break; + + case 1: /* BCD plus */ + for (k=0; k<len; k++) + str[k] = bcd_plus[(data[off+k] & 0x0f)]; + str[k] = '\0'; + break; + + case 2: /* 6-bit ASCII */ + for (i=j=0; i<len; i+=3) { + u.bits = 0; + k = ((len-i) < 3 ? (len-i) : 3); +#if WORDS_BIGENDIAN + u.chars[3] = data[off+i]; + u.chars[2] = (k > 1 ? data[off+i+1] : 0); + u.chars[1] = (k > 2 ? data[off+i+2] : 0); +#define CHAR_IDX 3 +#else + memcpy((void *)&u.bits, &data[off+i], k); +#define CHAR_IDX 0 +#endif + for (k=0; k<4; k++) { + str[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20); + u.bits >>= 6; + } + } + str[j] = '\0'; + break; + + case 3: + memcpy(str, &data[off], len); + str[len] = '\0'; + break; + } + + off += len; + *offset = off; + + return str; +} + +/* is_valid_filename - checks file/path supplied by user + * + * input_filename - user input string + * + * returns 0 if path is ok + * returns (-1) if path is NULL + * returns (-2) if path is too short + * returns (-3) if path is too long + */ +int +is_valid_filename(const char *input_filename) +{ + if (input_filename == NULL) { + lprintf(LOG_ERR, "ERROR: NULL pointer passed."); + return (-1); + } + + if (strlen(input_filename) < 1) { + lprintf(LOG_ERR, "File/path is invalid."); + return (-2); + } + + if (strlen(input_filename) >= 512) { + lprintf(LOG_ERR, "File/path must be shorter than 512 bytes."); + return (-3); + } + + return 0; +} /* is_valid_filename() */ + +/* build_fru_bloc - build fru bloc for write protection +* +* @intf: ipmi interface +* @fru_info: information about FRU device +* @id : Fru id +* @soffset : Source offset (from buffer) +* @doffset : Destination offset (in device) +* @length : Size of data to write (in bytes) +* @pFrubuf : Pointer on data to write +* +* returns 0 on success +* returns -1 on error +*/ +#define FRU_NUM_BLOC_COMMON_HEADER 6 +t_ipmi_fru_bloc * +build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id) +{ + t_ipmi_fru_bloc * p_first, * p_bloc, * p_new; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_header header; + struct fru_multirec_header rec_hdr; + uint8_t msg_data[4]; + uint32_t off; + uint16_t i; + + /* + * get COMMON Header format + */ + msg_data[0] = id; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, " Device not present (No Response)"); + return NULL; + } + + if (rsp->ccode > 0) { + lprintf(LOG_ERR," Device not present (%s)", + val2str(rsp->ccode, completion_code_vals)); + return NULL; + } + + if (verbose > 1) { + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + } + + memcpy(&header, rsp->data + 1, 8); + + /* verify header checksum */ + if (ipmi_csum((uint8_t *)&header, 8)) { + lprintf(LOG_ERR, " Bad header checksum"); + return NULL; + } + + if (header.version != 1) { + lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", header.version); + return NULL; + } + + /****************************************** + Malloc and fill up the bloc contents + *******************************************/ + + // Common header + p_first = malloc(sizeof(struct ipmi_fru_bloc)); + if (!p_first) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + + p_bloc = p_first; + p_bloc->next = NULL; + p_bloc->start= 0; + p_bloc->size = fru->size; + strcpy((char *)p_bloc->blocId, "Common Header Section"); + + for (i = 0; i < 4; i++) { + if (header.offsets[i]) { + p_new = malloc(sizeof(struct ipmi_fru_bloc)); + if (!p_new) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + free_fru_bloc(p_first); + return NULL; + } + + p_new->next = NULL; + p_new->start = header.offsets[i] * 8; + p_new->size = fru->size - p_new->start; + + strncpy((char *)p_new->blocId, section_id[i], sizeof(p_new->blocId)); + /* Make sure string is null terminated */ + p_new->blocId[sizeof(p_new->blocId)-1] = 0; + + p_bloc->next = p_new; + p_bloc->size = p_new->start - p_bloc->start; + p_bloc = p_new; + } + } + + // Multi + if (header.offset.multi) { + off = header.offset.multi * 8; + + do { + /* + * check for odd offset for the case of fru devices + * accessed by words + */ + if (fru->access && (off & 1)) { + lprintf(LOG_ERR, " Unaligned offset for a block: %d", off); + /* increment offset */ + off++; + break; + } + + if (read_fru_area(intf, fru, id, off, 5, + (uint8_t *) &rec_hdr) < 0) { + break; + } + + p_new = malloc(sizeof(struct ipmi_fru_bloc)); + if (!p_new) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + free_fru_bloc(p_first); + return NULL; + } + + p_new->next = NULL; + p_new->start = off; + p_new->size = fru->size - p_new->start; + sprintf((char *)p_new->blocId, "Multi-Rec Area: Type %i", + rec_hdr.type); + + p_bloc->next = p_new; + p_bloc->size = p_new->start - p_bloc->start; + p_bloc = p_new; + + off += rec_hdr.len + sizeof(struct fru_multirec_header); + + /* verify record header */ + if (ipmi_csum((uint8_t *)&rec_hdr, + sizeof(struct fru_multirec_header))) { + /* can't reliably judge for the rest space */ + break; + } + } while (!(rec_hdr.format & 0x80) && (off < fru->size)); + + lprintf(LOG_DEBUG,"Multi-Record area ends at: %i (%xh)", off, off); + + if (fru->size > off) { + // Bloc for remaining space + p_new = malloc(sizeof(struct ipmi_fru_bloc)); + if (!p_new) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + free_fru_bloc(p_first); + return NULL; + } + + p_new->next = NULL; + p_new->start = off; + p_new->size = fru->size - p_new->start; + strcpy((char *)p_new->blocId, "Unused space"); + + p_bloc->next = p_new; + p_bloc->size = p_new->start - p_bloc->start; + } + } + + /* Dump blocs */ + for(p_bloc = p_first, i = 0; p_bloc; p_bloc = p_bloc->next) { + lprintf(LOG_DEBUG ,"Bloc Numb : %i", i++); + lprintf(LOG_DEBUG ,"Bloc Id : %s", p_bloc->blocId); + lprintf(LOG_DEBUG ,"Bloc Start: %i", p_bloc->start); + lprintf(LOG_DEBUG ,"Bloc Size : %i", p_bloc->size); + lprintf(LOG_DEBUG ,""); + } + + return p_first; +} + +void +free_fru_bloc(t_ipmi_fru_bloc *bloc) +{ + t_ipmi_fru_bloc * del; + + while (bloc) { + del = bloc; + bloc = bloc->next; + free(del); + del = NULL; + } +} + +/* + * write FRU[doffset:length] from the pFrubuf[soffset:length] + * rc=1 on success +**/ +int +write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, + uint16_t soffset, uint16_t doffset, + uint16_t length, uint8_t *pFrubuf) +{ + uint16_t tmp, finish; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[255+3]; + uint16_t writeLength; + uint16_t found_bloc = 0; + + finish = doffset + length; /* destination offset */ + if (finish > fru->size) + { + lprintf(LOG_ERROR, "Return error"); + return -1; + } + + if (fru->access && ((doffset & 1) || (length & 1))) { + lprintf(LOG_ERROR, "Odd offset or length specified"); + return (-1); + } + + t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id); + t_ipmi_fru_bloc * saved_fru_bloc = fru_bloc; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = SET_FRU_DATA; + req.msg.data = msg_data; + + /* initialize request size only once */ + if (fru->max_write_size == 0) { + uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf); + + /* validate lower bound of the maximum request data size */ + if (max_rq_size <= 3) { + lprintf(LOG_ERROR, "Maximum request size is too small to send " + "a write request"); + return -1; + } + + /* + * Write FRU Info command returns the number of written bytes in + * a single byte field. + */ + if (max_rq_size - 3 > 255) { + /* Limit the max write size with 255 bytes. */ + fru->max_write_size = 255; + } else { + /* subtract 1 byte for FRU ID an 2 bytes for offset */ + fru->max_write_size = max_rq_size - 3; + } + + /* check word access */ + if (fru->access) { + fru->max_write_size &= ~1; + } + } + + do { + uint16_t end_bloc; + uint8_t protected_bloc = 0; + + /* Write per bloc, try to find the end of a bloc*/ + while (fru_bloc && fru_bloc->start + fru_bloc->size <= doffset) { + fru_bloc = fru_bloc->next; + found_bloc++; + } + + if (fru_bloc && fru_bloc->start + fru_bloc->size < finish) { + end_bloc = fru_bloc->start + fru_bloc->size; + } else { + end_bloc = finish; + } + + /* calculate write length */ + tmp = end_bloc - doffset; + + /* check that write length is more than maximum request size */ + if (tmp > fru->max_write_size) { + writeLength = fru->max_write_size; + } else { + writeLength = tmp; + } + + /* copy fru data */ + memcpy(&msg_data[3], pFrubuf + soffset, writeLength); + + /* check word access */ + if (fru->access) { + writeLength &= ~1; + } + + tmp = doffset; + if (fru->access) { + tmp >>= 1; + } + + msg_data[0] = id; + msg_data[1] = (uint8_t)tmp; + msg_data[2] = (uint8_t)(tmp >> 8); + req.msg.data_len = writeLength + 3; + + if(fru_bloc) { + lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)", + writeLength, found_bloc, fru_bloc->blocId); + } else { + lprintf(LOG_INFO,"Writing %d bytes", writeLength); + } + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + break; + } + + if (rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) { + if (fru->max_write_size > 8) { + fru->max_write_size -= 8; + lprintf(LOG_INFO, "Retrying FRU write with request size %d", + fru->max_write_size); + continue; + } + } else if(rsp->ccode == 0x80) { + rsp->ccode = 0; + // Write protected section + protected_bloc = 1; + } + + if (rsp->ccode > 0) + break; + + if (protected_bloc == 0) { + // Write OK, bloc not protected, continue + lprintf(LOG_INFO,"Wrote %d bytes", writeLength); + doffset += writeLength; + soffset += writeLength; + } else { + if(fru_bloc) { + // Bloc protected, advise user and jump over protected bloc + lprintf(LOG_INFO, + "Bloc [%s] protected at offset: %i (size %i bytes)", + fru_bloc->blocId, fru_bloc->start, fru_bloc->size); + lprintf(LOG_INFO,"Jumping over this bloc"); + } else { + lprintf(LOG_INFO, + "Remaining FRU is protected following offset: %i", + doffset); + } + soffset += end_bloc - doffset; + doffset = end_bloc; + } + } while (doffset < finish); + + if (saved_fru_bloc) { + free_fru_bloc(saved_fru_bloc); + } + + return doffset >= finish; +} + +/* read_fru_area - fill in frubuf[offset:length] from the FRU[offset:length] +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset into buffer +* @length: how much to read +* @frubuf: buffer read into +* +* returns -1 on error +* returns 0 if successful +*/ +int +read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, + uint32_t offset, uint32_t length, uint8_t *frubuf) +{ + uint32_t off = offset, tmp, finish; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[4]; + + if (offset > fru->size) { + lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d", + offset, fru->size); + return -1; + } + + finish = offset + length; + if (finish > fru->size) { + finish = fru->size; + lprintf(LOG_NOTICE, "Read FRU Area length %d too large, " + "Adjusting to %d", + offset + length, finish - offset); + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + if (fru->max_read_size == 0) { + uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1; + + /* validate lower bound of the maximum response data size */ + if (max_rs_size <= 1) { + lprintf(LOG_ERROR, "Maximum response size is too small to send " + "a read request"); + return -1; + } + + /* + * Read FRU Info command may read up to 255 bytes of data. + */ + if (max_rs_size - 1 > 255) { + /* Limit the max read size with 255 bytes. */ + fru->max_read_size = 255; + } else { + /* subtract 1 byte for bytes count */ + fru->max_read_size = max_rs_size - 1; + } + + /* check word access */ + if (fru->access) { + fru->max_read_size &= ~1; + } + } + + do { + tmp = fru->access ? off >> 1 : off; + msg_data[0] = id; + msg_data[1] = (uint8_t)(tmp & 0xff); + msg_data[2] = (uint8_t)(tmp >> 8); + tmp = finish - off; + if (tmp > fru->max_read_size) + msg_data[3] = (uint8_t)fru->max_read_size; + else + msg_data[3] = (uint8_t)tmp; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_NOTICE, "FRU Read failed"); + break; + } + if (rsp->ccode > 0) { + /* if we get C8h or CAh completion code then we requested too + * many bytes at once so try again with smaller size */ + if ((rsp->ccode == 0xc8 || rsp->ccode == 0xca) + && fru->max_read_size > 8) { + if (fru->max_read_size > 32) { + /* subtract read length more aggressively */ + fru->max_read_size -= 8; + } else { + /* subtract length less aggressively */ + fru->max_read_size--; + } + + lprintf(LOG_INFO, "Retrying FRU read with request size %d", + fru->max_read_size); + continue; + } + + lprintf(LOG_NOTICE, "FRU Read failed: %s", + val2str(rsp->ccode, completion_code_vals)); + break; + } + + tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0]; + memcpy(frubuf, rsp->data + 1, tmp); + off += tmp; + frubuf += tmp; + /* sometimes the size returned in the Info command + * is too large. return 0 so higher level function + * still attempts to parse what was returned */ + if (tmp == 0 && off < finish) { + return 0; + } + } while (off < finish); + + if (off < finish) { + return -1; + } + + return 0; +} + +/* read_fru_area - fill in frubuf[offset:length] from the FRU[offset:length] +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset into buffer +* @length: how much to read +* @frubuf: buffer read into +* +* returns -1 on error +* returns 0 if successful +*/ +int +read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, + uint32_t offset, uint32_t length, uint8_t *frubuf) +{ + static uint32_t fru_data_rqst_size = 20; + uint32_t off = offset, tmp, finish; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[4]; + + if (offset > fru->size) { + lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d", + offset, fru->size); + return -1; + } + + finish = offset + length; + if (finish > fru->size) { + finish = fru->size; + lprintf(LOG_NOTICE, "Read FRU Area length %d too large, " + "Adjusting to %d", + offset + length, finish - offset); + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + +#ifdef LIMIT_ALL_REQUEST_SIZE + if (fru_data_rqst_size > 16) +#else + if (fru->access && fru_data_rqst_size > 16) +#endif + fru_data_rqst_size = 16; + do { + tmp = fru->access ? off >> 1 : off; + msg_data[0] = id; + msg_data[1] = (uint8_t)(tmp & 0xff); + msg_data[2] = (uint8_t)(tmp >> 8); + tmp = finish - off; + if (tmp > fru_data_rqst_size) + msg_data[3] = (uint8_t)fru_data_rqst_size; + else + msg_data[3] = (uint8_t)tmp; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_NOTICE, "FRU Read failed"); + break; + } + if (rsp->ccode > 0) { + /* if we get C7 or C8 or CA return code then we requested too + * many bytes at once so try again with smaller size */ + if ((rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) && + (--fru_data_rqst_size > 8)) { + lprintf(LOG_INFO, "Retrying FRU read with request size %d", + fru_data_rqst_size); + continue; + } + lprintf(LOG_NOTICE, "FRU Read failed: %s", + val2str(rsp->ccode, completion_code_vals)); + break; + } + + tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0]; + memcpy((frubuf + off)-offset, rsp->data + 1, tmp); + off += tmp; + + /* sometimes the size returned in the Info command + * is too large. return 0 so higher level function + * still attempts to parse what was returned */ + if (tmp == 0 && off < finish) + return 0; + + } while (off < finish); + + if (off < finish) + return -1; + + return 0; +} + + +static void +fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset) +{ + uint8_t * fru_data = NULL; + uint32_t fru_len, i; + struct fru_multirec_header * h; + uint32_t last_off, len; + + i = last_off = offset; + fru_len = 0; + + fru_data = malloc(fru->size + 1); + if (fru_data == NULL) { + lprintf(LOG_ERR, " Out of memory!"); + return; + } + + memset(fru_data, 0, fru->size + 1); + + do { + h = (struct fru_multirec_header *) (fru_data + i); + + // read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time + if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) + { + len = fru->size - last_off; + if (len > FRU_MULTIREC_CHUNK_SIZE) + len = FRU_MULTIREC_CHUNK_SIZE; + + if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0) + break; + + last_off += len; + } + + //printf("Bloc Numb : %i\n", counter); + printf("Bloc Start: %i\n", i); + printf("Bloc Size : %i\n", h->len); + printf("\n"); + + i += h->len + sizeof (struct fru_multirec_header); + } while (!(h->format & 0x80)); + + i = offset; + do { + h = (struct fru_multirec_header *) (fru_data + i); + + printf("Bloc Start: %i\n", i); + printf("Bloc Size : %i\n", h->len); + printf("\n"); + + i += h->len + sizeof (struct fru_multirec_header); + } while (!(h->format & 0x80)); + + lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i); + + free(fru_data); + fru_data = NULL; +} + + +/* fru_area_print_chassis - Print FRU Chassis Area +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset pointer +*/ +static void +fru_area_print_chassis(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset) +{ + char * fru_area; + uint8_t * fru_data; + uint32_t fru_len, i; + uint8_t tmp[2]; + + fru_len = 0; + + /* read enough to check length field */ + if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { + fru_len = 8 * tmp[1]; + } + + if (fru_len == 0) { + return; + } + + fru_data = malloc(fru_len); + if (fru_data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + + memset(fru_data, 0, fru_len); + + /* read in the full fru */ + if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { + free(fru_data); + fru_data = NULL; + return; + } + + /* + * skip first two bytes which specify + * fru area version and fru area length + */ + i = 2; + + printf(" Chassis Type : %s\n", + chassis_type_desc[fru_data[i] > + (sizeof(chassis_type_desc)/sizeof(chassis_type_desc[0])) - 1 ? + 2 : fru_data[i]]); + + i++; + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Chassis Part Number : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Chassis Serial : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + /* read any extra fields */ + while ((fru_data[i] != 0xc1) && (i < fru_len)) + { + int j = i; + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Chassis Extra : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + if (i == j) { + break; + } + } + + if (fru_area != NULL) { + free(fru_data); + fru_data = NULL; + } +} + +/* fru_area_print_board - Print FRU Board Area +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset pointer +*/ +static void +fru_area_print_board(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset) +{ + char * fru_area; + uint8_t * fru_data; + uint32_t fru_len; + uint32_t i; + time_t tval; + uint8_t tmp[2]; + + fru_len = 0; + + /* read enough to check length field */ + if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { + fru_len = 8 * tmp[1]; + } + + if (fru_len <= 0) { + return; + } + + fru_data = malloc(fru_len); + if (fru_data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + + memset(fru_data, 0, fru_len); + + /* read in the full fru */ + if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { + free(fru_data); + fru_data = NULL; + return; + } + + /* + * skip first three bytes which specify + * fru area version, fru area length + * and fru board language + */ + i = 3; + + tval=((fru_data[i+2] << 16) + (fru_data[i+1] << 8) + (fru_data[i])); + tval=tval * 60; + tval=tval + secs_from_1970_1996; + printf(" Board Mfg Date : %s", asctime(localtime(&tval))); + i += 3; /* skip mfg. date time */ + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Board Mfg : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Board Product : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Board Serial : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Board Part Number : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0 && verbose > 0) { + printf(" Board FRU ID : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + /* read any extra fields */ + while ((fru_data[i] != 0xc1) && (i < fru_len)) + { + int j = i; + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Board Extra : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + if (i == j) + break; + } + + if (fru_area != NULL) { + free(fru_data); + fru_data = NULL; + } +} + +/* fru_area_print_product - Print FRU Product Area +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset pointer +*/ +static void +fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset) +{ + char * fru_area; + uint8_t * fru_data; + uint32_t fru_len, i; + uint8_t tmp[2]; + + fru_len = 0; + + /* read enough to check length field */ + if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { + fru_len = 8 * tmp[1]; + } + + if (fru_len == 0) { + return; + } + + fru_data = malloc(fru_len); + if (fru_data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + + memset(fru_data, 0, fru_len); + + + /* read in the full fru */ + if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { + free(fru_data); + fru_data = NULL; + return; + } + + /* + * skip first three bytes which specify + * fru area version, fru area length + * and fru board language + */ + i = 3; + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Manufacturer : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Name : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Part Number : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Version : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Serial : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Asset Tag : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0 && verbose > 0) { + printf(" Product FRU ID : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + + /* read any extra fields */ + while ((fru_data[i] != 0xc1) && (i < fru_len)) + { + int j = i; + fru_area = get_fru_area_str(fru_data, &i); + if (fru_area != NULL) { + if (strlen(fru_area) > 0) { + printf(" Product Extra : %s\n", fru_area); + } + free(fru_area); + fru_area = NULL; + } + if (i == j) + break; + } + + if (fru_area != NULL) { + free(fru_data); + fru_data = NULL; + } +} + +/* fru_area_print_multirec - Print FRU Multi Record Area +* +* @intf: ipmi interface +* @fru: fru info +* @id: fru id +* @offset: offset pointer +*/ +static void +fru_area_print_multirec(struct ipmi_intf * intf, struct fru_info * fru, + uint8_t id, uint32_t offset) +{ + uint8_t * fru_data; + struct fru_multirec_header * h; + struct fru_multirec_powersupply * ps; + struct fru_multirec_dcoutput * dc; + struct fru_multirec_dcload * dl; + uint16_t peak_capacity; + uint8_t peak_hold_up_time; + uint32_t last_off; + + last_off = offset; + + fru_data = malloc(FRU_MULTIREC_CHUNK_SIZE); + if (fru_data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + + memset(fru_data, 0, FRU_MULTIREC_CHUNK_SIZE); + + h = (struct fru_multirec_header *) (fru_data); + + do { + if (read_fru_area(intf, fru, id, last_off, sizeof(*h), fru_data) < 0) { + break; + } + + if (h->len && read_fru_area(intf, fru, id, + last_off + sizeof(*h), h->len, fru_data + sizeof(*h)) < 0) { + break; + } + + last_off += h->len + sizeof(*h); + + switch (h->type) { + case FRU_RECORD_TYPE_POWER_SUPPLY_INFORMATION: + ps = (struct fru_multirec_powersupply *) + (fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN + ps->capacity = BSWAP_16(ps->capacity); + ps->peak_va = BSWAP_16(ps->peak_va); + ps->lowend_input1 = BSWAP_16(ps->lowend_input1); + ps->highend_input1 = BSWAP_16(ps->highend_input1); + ps->lowend_input2 = BSWAP_16(ps->lowend_input2); + ps->highend_input2 = BSWAP_16(ps->highend_input2); + ps->combined_capacity = BSWAP_16(ps->combined_capacity); + ps->peak_cap_ht = BSWAP_16(ps->peak_cap_ht); +#endif + peak_hold_up_time = (ps->peak_cap_ht & 0xf000) >> 12; + peak_capacity = ps->peak_cap_ht & 0x0fff; + + printf (" Power Supply Record\n"); + printf (" Capacity : %d W\n", + ps->capacity); + printf (" Peak VA : %d VA\n", + ps->peak_va); + printf (" Inrush Current : %d A\n", + ps->inrush_current); + printf (" Inrush Interval : %d ms\n", + ps->inrush_interval); + printf (" Input Voltage Range 1 : %d-%d V\n", + ps->lowend_input1 / 100, ps->highend_input1 / 100); + printf (" Input Voltage Range 2 : %d-%d V\n", + ps->lowend_input2 / 100, ps->highend_input2 / 100); + printf (" Input Frequency Range : %d-%d Hz\n", + ps->lowend_freq, ps->highend_freq); + printf (" A/C Dropout Tolerance : %d ms\n", + ps->dropout_tolerance); + printf (" Flags : %s%s%s%s%s\n", + ps->predictive_fail ? "'Predictive fail' " : "", + ps->pfc ? "'Power factor correction' " : "", + ps->autoswitch ? "'Autoswitch voltage' " : "", + ps->hotswap ? "'Hot swap' " : "", + ps->predictive_fail ? ps->rps_threshold ? + ps->tach ? "'Two pulses per rotation'" : "'One pulse per rotation'" : + ps->tach ? "'Failure on pin de-assertion'" : "'Failure on pin assertion'" : ""); + printf (" Peak capacity : %d W\n", + peak_capacity); + printf (" Peak capacity holdup : %d s\n", + peak_hold_up_time); + if (ps->combined_capacity == 0) + printf (" Combined capacity : not specified\n"); + else + printf (" Combined capacity : %d W (%s and %s)\n", + ps->combined_capacity, + combined_voltage_desc [ps->combined_voltage1], + combined_voltage_desc [ps->combined_voltage2]); + if (ps->predictive_fail) + printf (" Fan lower threshold : %d RPS\n", + ps->rps_threshold); + break; + + case FRU_RECORD_TYPE_DC_OUTPUT: + dc = (struct fru_multirec_dcoutput *) + (fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN + dc->nominal_voltage = BSWAP_16(dc->nominal_voltage); + dc->max_neg_dev = BSWAP_16(dc->max_neg_dev); + dc->max_pos_dev = BSWAP_16(dc->max_pos_dev); + dc->ripple_and_noise = BSWAP_16(dc->ripple_and_noise); + dc->min_current = BSWAP_16(dc->min_current); + dc->max_current = BSWAP_16(dc->max_current); +#endif + + printf (" DC Output Record\n"); + printf (" Output Number : %d\n", + dc->output_number); + printf (" Standby power : %s\n", + dc->standby ? "Yes" : "No"); + printf (" Nominal voltage : %.2f V\n", + (double) dc->nominal_voltage / 100); + printf (" Max negative deviation : %.2f V\n", + (double) dc->max_neg_dev / 100); + printf (" Max positive deviation : %.2f V\n", + (double) dc->max_pos_dev / 100); + printf (" Ripple and noise pk-pk : %d mV\n", + dc->ripple_and_noise); + printf (" Minimum current draw : %.3f A\n", + (double) dc->min_current / 1000); + printf (" Maximum current draw : %.3f A\n", + (double) dc->max_current / 1000); + break; + + case FRU_RECORD_TYPE_DC_LOAD: + dl = (struct fru_multirec_dcload *) + (fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN + dl->nominal_voltage = BSWAP_16(dl->nominal_voltage); + dl->min_voltage = BSWAP_16(dl->min_voltage); + dl->max_voltage = BSWAP_16(dl->max_voltage); + dl->ripple_and_noise = BSWAP_16(dl->ripple_and_noise); + dl->min_current = BSWAP_16(dl->min_current); + dl->max_current = BSWAP_16(dl->max_current); +#endif + + printf (" DC Load Record\n"); + printf (" Output Number : %d\n", + dl->output_number); + printf (" Nominal voltage : %.2f V\n", + (double) dl->nominal_voltage / 100); + printf (" Min voltage allowed : %.2f V\n", + (double) dl->min_voltage / 100); + printf (" Max voltage allowed : %.2f V\n", + (double) dl->max_voltage / 100); + printf (" Ripple and noise pk-pk : %d mV\n", + dl->ripple_and_noise); + printf (" Minimum current load : %.3f A\n", + (double) dl->min_current / 1000); + printf (" Maximum current load : %.3f A\n", + (double) dl->max_current / 1000); + break; + case FRU_RECORD_TYPE_OEM_EXTENSION: + { + struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) + &fru_data[sizeof(struct fru_multirec_header)]; + uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + + /* Now makes sure this is really PICMG record */ + + if( iana == IPMI_OEM_PICMG ){ + printf(" PICMG Extension Record\n"); + ipmi_fru_picmg_ext_print(fru_data, + sizeof(struct fru_multirec_header), + h->len); + } + /* FIXME: Add OEM record support here */ + else{ + printf(" OEM (%s) Record\n", val2str( iana, ipmi_oem_info)); + } + } + break; + } + } while (!(h->format & 0x80)); + + lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)", last_off, last_off); + + free(fru_data); +} + +/* ipmi_fru_query_new_value - Query new values to replace original FRU content +* +* @data: FRU data +* @offset: offset of the bytes to be modified in data +* @len: size of the modified data +* +* returns : TRUE if data changed +* returns : FALSE if data not changed +*/ +int ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len) +{ + int status=FALSE; + int ret; + char answer; + + printf("Would you like to change this value <y/n> ? "); + ret = scanf("%c", &answer); + if (ret != 1) { + return FALSE; + } + + if( answer == 'y' || answer == 'Y' ){ + int i; + unsigned int *holder; + + holder = malloc(len); + printf( + "Enter hex values for each of the %d entries (lsb first), " + "hit <enter> between entries\n", (int)len); + + /* I can't assign scanf' %x into a single char */ + for( i=0;i<len;i++ ){ + ret = scanf("%x", holder+i); + if (ret != 1) { + free(holder); + return FALSE; + } + } + for( i=0;i<len;i++ ){ + data[offset++] = (unsigned char) *(holder+i); + } + /* &data[offset++] */ + free(holder); + holder = NULL; + status = TRUE; + } + else{ + printf("Entered %c\n",answer); + } + + return status; +} + +/* ipmi_fru_oemkontron_edit - +* Query new values to replace original FRU content +* This is a generic enough to support any type of 'OEM' record +* because the user supplies 'IANA number' , 'record Id' and 'record' version' +* +* However, the parser must have 'apriori' knowledge of the record format +* The currently supported record is : +* +* IANA : 15000 (Kontron) +* RECORD ID : 3 +* RECORD VERSION: 0 (or 1) +* +* I would have like to put that stuff in an OEM specific file, but apart for +* the record format information, all commands are really standard 'FRU' command +* +* +* @data: FRU data +* @offset: start of the current multi record (start of header) +* @len: len of the current record (excluding header) +* @h: pointer to record header +* @oh: pointer to OEM /PICMG header +* +* returns: TRUE if data changed +* returns: FALSE if data not changed +*/ +#define OEM_KONTRON_INFORMATION_RECORD 3 + +#define EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT 12 +#define GET_OEM_KONTRON_COMPLETE_ARG_COUNT 5 +/* +./src/ipmitool fru edit 0 +oem 15000 3 0 name instance FIELD1 FIELD2 FIELD3 crc32 +*/ + +#define OEM_KONTRON_SUBCOMMAND_ARG_POS 2 +#define OEM_KONTRON_IANA_ARG_POS 3 +#define OEM_KONTRON_RECORDID_ARG_POS 4 +#define OEM_KONTRON_FORMAT_ARG_POS 5 +#define OEM_KONTRON_NAME_ARG_POS 6 +#define OEM_KONTRON_INSTANCE_ARG_POS 7 +#define OEM_KONTRON_VERSION_ARG_POS 8 +#define OEM_KONTRON_BUILDDATE_ARG_POS 9 +#define OEM_KONTRON_UPDATEDATE_ARG_POS 10 +#define OEM_KONTRON_CRC32_ARG_POS 11 + +#define OEM_KONTRON_FIELD_SIZE 8 +#define OEM_KONTRON_VERSION_FIELD_SIZE 10 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct OemKontronInformationRecordV0{ + uint8_t field1TypeLength; + uint8_t field1[OEM_KONTRON_FIELD_SIZE]; + uint8_t field2TypeLength; + uint8_t field2[OEM_KONTRON_FIELD_SIZE]; + uint8_t field3TypeLength; + uint8_t field3[OEM_KONTRON_FIELD_SIZE]; + uint8_t crcTypeLength; + uint8_t crc32[OEM_KONTRON_FIELD_SIZE]; +}tOemKontronInformationRecordV0; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct OemKontronInformationRecordV1{ + uint8_t field1TypeLength; + uint8_t field1[OEM_KONTRON_VERSION_FIELD_SIZE]; + uint8_t field2TypeLength; + uint8_t field2[OEM_KONTRON_FIELD_SIZE]; + uint8_t field3TypeLength; + uint8_t field3[OEM_KONTRON_FIELD_SIZE]; + uint8_t crcTypeLength; + uint8_t crc32[OEM_KONTRON_FIELD_SIZE]; +}tOemKontronInformationRecordV1; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +/* +./src/ipmitool fru get 0 oem iana 3 + +*/ + +static void ipmi_fru_oemkontron_get( int argc, char ** argv,uint8_t * fru_data, + int off,int len, + struct fru_multirec_header *h, + struct fru_multirec_oem_header *oh) +{ + static int badParams=FALSE; + int start = off; + int offset = start; + int length = len; + int i; + offset += sizeof(struct fru_multirec_oem_header); + + if(!badParams){ + /* the 'OEM' field is already checked in caller */ + if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){ + if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){ + printf("usage: fru get <id> <oem>\n"); + badParams = TRUE; + return; + } + } + if( argc<GET_OEM_KONTRON_COMPLETE_ARG_COUNT ){ + printf("usage: oem <iana> <recordid>\n"); + printf("usage: oem 15000 3\n"); + badParams = TRUE; + return; + } + } + + if(!badParams){ + + if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) { + + uint8_t version; + + printf("Kontron OEM Information Record\n"); + version = oh->record_version; + + int blockstart; + uint8_t blockCount; + uint8_t blockIndex=0; + + unsigned int matchInstance = 0; + uint8_t instance = 0; + + if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) { + lprintf(LOG_ERR, + "Instance argument '%s' is either invalid or out of range.", + argv[OEM_KONTRON_INSTANCE_ARG_POS]); + badParams = TRUE; + return; + } + + blockCount = fru_data[offset++]; + + for(blockIndex=0;blockIndex<blockCount;blockIndex++){ + void * pRecordData; + uint8_t nameLen; + + blockstart = offset; + nameLen = ( fru_data[offset++] &= 0x3F ); + printf(" Name: %*.*s\n",nameLen, nameLen, (const char *)(fru_data+offset)); + + offset+=nameLen; + + pRecordData = &fru_data[offset]; + + printf(" Record Version: %d\n", version); + if( version == 0 ) + { + printf(" Version: %*.*s\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV0 *) pRecordData)->field1); + printf(" Build Date: %*.*s\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV0 *) pRecordData)->field2); + printf(" Update Date: %*.*s\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV0 *) pRecordData)->field3); + printf(" Checksum: %*.*s\n\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV0 *) pRecordData)->crc32); + matchInstance++; + offset+= sizeof(tOemKontronInformationRecordV0); + offset++; + } + else if ( version == 1 ) + { + printf(" Version: %*.*s\n", + OEM_KONTRON_VERSION_FIELD_SIZE, + OEM_KONTRON_VERSION_FIELD_SIZE, + ((tOemKontronInformationRecordV1 *) pRecordData)->field1); + printf(" Build Date: %*.*s\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV1 *) pRecordData)->field2); + printf(" Update Date: %*.*s\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV1 *) pRecordData)->field3); + printf(" Checksum: %*.*s\n\n", + OEM_KONTRON_FIELD_SIZE, + OEM_KONTRON_FIELD_SIZE, + ((tOemKontronInformationRecordV1 *) pRecordData)->crc32); + matchInstance++; + offset+= sizeof(tOemKontronInformationRecordV1); + offset++; + } + else + { + printf (" Unsupported version %d\n",version); + } + } + } + } +} + +static int ipmi_fru_oemkontron_edit( int argc, char ** argv,uint8_t * fru_data, + int off,int len, + struct fru_multirec_header *h, + struct fru_multirec_oem_header *oh) +{ + static int badParams=FALSE; + int hasChanged = FALSE; + int start = off; + int offset = start; + int length = len; + int i; + uint8_t record_id = 0; + offset += sizeof(struct fru_multirec_oem_header); + + if(!badParams){ + /* the 'OEM' field is already checked in caller */ + if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){ + if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){ + printf("usage: fru edit <id> <oem> <args...>\n"); + badParams = TRUE; + return hasChanged; + } + } + if( argc<EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT ){ + printf("usage: oem <iana> <recordid> <format> <args...>\n"); + printf("usage: oem 15000 3 0 <name> <instance> <field1>"\ + " <field2> <field3> <crc32>\n"); + badParams = TRUE; + return hasChanged; + } + if (str2uchar(argv[OEM_KONTRON_RECORDID_ARG_POS], &record_id) != 0) { + lprintf(LOG_ERR, + "Record ID argument '%s' is either invalid or out of range.", + argv[OEM_KONTRON_RECORDID_ARG_POS]); + badParams = TRUE; + return hasChanged; + } + if (record_id == OEM_KONTRON_INFORMATION_RECORD) { + for(i=OEM_KONTRON_VERSION_ARG_POS;i<=OEM_KONTRON_CRC32_ARG_POS;i++){ + if( (strlen(argv[i]) != OEM_KONTRON_FIELD_SIZE) && + (strlen(argv[i]) != OEM_KONTRON_VERSION_FIELD_SIZE)) { + printf("error: version fields must have %d characters\n", + OEM_KONTRON_FIELD_SIZE); + badParams = TRUE; + return hasChanged; + } + } + } + } + + if(!badParams){ + + if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) { + uint8_t formatVersion = 0; + uint8_t version; + + if (str2uchar(argv[OEM_KONTRON_FORMAT_ARG_POS], &formatVersion) != 0) { + lprintf(LOG_ERR, + "Format argument '%s' is either invalid or out of range.", + argv[OEM_KONTRON_FORMAT_ARG_POS]); + badParams = TRUE; + return hasChanged; + } + + printf(" Kontron OEM Information Record\n"); + version = oh->record_version; + + if( version == formatVersion ){ + int blockstart; + uint8_t blockCount; + uint8_t blockIndex=0; + + uint8_t matchInstance = 0; + uint8_t instance = 0; + + if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) { + lprintf(LOG_ERR, + "Instance argument '%s' is either invalid or out of range.", + argv[OEM_KONTRON_INSTANCE_ARG_POS]); + badParams = TRUE; + return hasChanged; + } + + blockCount = fru_data[offset++]; + printf(" blockCount: %d\n",blockCount); + + for(blockIndex=0;blockIndex<blockCount;blockIndex++){ + void * pRecordData; + uint8_t nameLen; + + blockstart = offset; + + nameLen = ( fru_data[offset++] & 0x3F ); + + if( version == 0 || version == 1 ) + { + if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS], + (const char *)(fru_data+offset),nameLen)&& (matchInstance == instance)){ + + printf ("Found : %s\n",argv[OEM_KONTRON_NAME_ARG_POS]); + offset+=nameLen; + + pRecordData = &fru_data[offset]; + + if( version == 0 ) + { + memcpy( ((tOemKontronInformationRecordV0 *) + pRecordData)->field1 , + argv[OEM_KONTRON_VERSION_ARG_POS] , + OEM_KONTRON_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV0 *) + pRecordData)->field2 , + argv[OEM_KONTRON_BUILDDATE_ARG_POS], + OEM_KONTRON_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV0 *) + pRecordData)->field3 , + argv[OEM_KONTRON_UPDATEDATE_ARG_POS], + OEM_KONTRON_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV0 *) + pRecordData)->crc32 , + argv[OEM_KONTRON_CRC32_ARG_POS] , + OEM_KONTRON_FIELD_SIZE); + } + else + { + memcpy( ((tOemKontronInformationRecordV1 *) + pRecordData)->field1 , + argv[OEM_KONTRON_VERSION_ARG_POS] , + OEM_KONTRON_VERSION_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV1 *) + pRecordData)->field2 , + argv[OEM_KONTRON_BUILDDATE_ARG_POS], + OEM_KONTRON_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV1 *) + pRecordData)->field3 , + argv[OEM_KONTRON_UPDATEDATE_ARG_POS], + OEM_KONTRON_FIELD_SIZE); + memcpy( ((tOemKontronInformationRecordV1 *) + pRecordData)->crc32 , + argv[OEM_KONTRON_CRC32_ARG_POS] , + OEM_KONTRON_FIELD_SIZE); + } + + matchInstance++; + hasChanged = TRUE; + } + else if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS], + (const char *)(fru_data+offset), nameLen)){ + printf ("Skipped : %s [instance %d]\n",argv[OEM_KONTRON_NAME_ARG_POS], + (unsigned int)matchInstance); + matchInstance++; + offset+=nameLen; + } + else { + offset+=nameLen; + } + + if( version == 0 ) + { + offset+= sizeof(tOemKontronInformationRecordV0); + } + else + { + offset+= sizeof(tOemKontronInformationRecordV1); + } + offset++; + } + else + { + printf (" Unsupported version %d\n",version); + } + } + } + else{ + printf(" Version: %d\n",version); + } + } + if( hasChanged ){ + + uint8_t record_checksum =0; + uint8_t header_checksum =0; + int index; + + lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum); + lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum); + for(index=0;index<length;index++){ + record_checksum+= fru_data[start+index]; + } + /* Update Record checksum */ + h->record_checksum = ~record_checksum + 1; + + + for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){ + uint8_t data= *( (uint8_t *)h+ index); + header_checksum+=data; + } + /* Update header checksum */ + h->header_checksum = ~header_checksum + 1; + + lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum); + lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum); + + /* write back data */ + } + } + + return hasChanged; +} + +/* ipmi_fru_picmg_ext_edit - Query new values to replace original FRU content +* +* @data: FRU data +* @offset: start of the current multi record (start of header) +* @len: len of the current record (excluding header) +* @h: pointer to record header +* @oh: pointer to OEM /PICMG header +* +* returns: TRUE if data changed +* returns: FALSE if data not changed +*/ +static int ipmi_fru_picmg_ext_edit(uint8_t * fru_data, + int off,int len, + struct fru_multirec_header *h, + struct fru_multirec_oem_header *oh) +{ + int hasChanged = FALSE; + int start = off; + int offset = start; + int length = len; + offset += sizeof(struct fru_multirec_oem_header); + + switch (oh->record_id) + { + case FRU_AMC_ACTIVATION: + printf(" FRU_AMC_ACTIVATION\n"); + { + int index=offset; + uint16_t max_current; + + max_current = fru_data[offset]; + max_current |= fru_data[++offset]<<8; + + printf(" Maximum Internal Current(@12V): %.2f A (0x%02x)\n", + (float)max_current / 10.0f, max_current); + + if( ipmi_fru_query_new_value(fru_data,index,2) ){ + max_current = fru_data[index]; + max_current |= fru_data[++index]<<8; + printf(" New Maximum Internal Current(@12V): %.2f A (0x%02x)\n", + (float)max_current / 10.0f, max_current); + hasChanged = TRUE; + + } + + printf(" Module Activation Readiness: %i sec.\n", fru_data[++offset]); + printf(" Descriptor Count: %i\n", fru_data[++offset]); + printf("\n"); + + for (++offset; + offset < (off + length); + offset += sizeof(struct fru_picmgext_activation_record)) { + struct fru_picmgext_activation_record * a = + (struct fru_picmgext_activation_record *) &fru_data[offset]; + + printf(" IPMB-Address: 0x%x\n", a->ibmb_addr); + printf(" Max. Module Current: %.2f A\n", (float)a->max_module_curr / 10.0f); + + printf("\n"); + } + } + break; + + case FRU_AMC_CURRENT: + printf(" FRU_AMC_CURRENT\n"); + { + int index=offset; + unsigned char current; + + current = fru_data[index]; + + printf(" Current draw(@12V): %.2f A (0x%02x)\n", + (float)current / 10.0f, current); + + if( ipmi_fru_query_new_value(fru_data, index, 1) ){ + current = fru_data[index]; + + printf(" New Current draw(@12V): %.2f A (0x%02x)\n", + (float)current / 10.0f, current); + hasChanged = TRUE; + } + } + break; + } + + if( hasChanged ){ + + uint8_t record_checksum =0; + uint8_t header_checksum =0; + int index; + + lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum); + lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum); + for(index=0;index<length;index++){ + record_checksum+= fru_data[start+index]; + } + /* Update Record checksum */ + h->record_checksum = ~record_checksum + 1; + + + for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){ + uint8_t data= *( (uint8_t *)h+ index); + header_checksum+=data; + } + /* Update header checksum */ + h->header_checksum = ~header_checksum + 1; + + lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum); + lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum); + + /* write back data */ + } + + return hasChanged; +} + +/* ipmi_fru_picmg_ext_print - prints OEM fru record (PICMG) +* +* @fru_data: FRU data +* @offset: offset of the bytes to be modified in data +* @length: size of the record +* +* returns : n/a +*/ +static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length) +{ + struct fru_multirec_oem_header *h; + int guid_count; + int offset = off; + int start_offset = off; + int i; + + h = (struct fru_multirec_oem_header *) &fru_data[offset]; + offset += sizeof(struct fru_multirec_oem_header); + + switch (h->record_id) + { + case FRU_PICMG_BACKPLANE_P2P: + { + uint8_t index; + unsigned int data; + struct fru_picmgext_slot_desc *slot_d; + + slot_d = + (struct fru_picmgext_slot_desc*)&fru_data[offset]; + offset += sizeof(struct fru_picmgext_slot_desc); + printf(" FRU_PICMG_BACKPLANE_P2P\n"); + + while (offset <= (start_offset+length)) { + printf("\n"); + printf(" Channel Type: "); + switch (slot_d->chan_type) + { + case 0x00: + case 0x07: + printf("PICMG 2.9\n"); + break; + case 0x08: + printf("Single Port Fabric IF\n"); + break; + case 0x09: + printf("Double Port Fabric IF\n"); + break; + case 0x0a: + printf("Full Channel Fabric IF\n"); + break; + case 0x0b: + printf("Base IF\n"); + break; + case 0x0c: + printf("Update Channel IF\n"); + break; + case 0x0d: + printf("ShMC Cross Connect\n"); + break; + default: + printf("Unknown IF (0x%x)\n", + slot_d->chan_type); + break; + } + printf(" Slot Addr. : %02x\n", + slot_d->slot_addr ); + printf(" Channel Count: %i\n", + slot_d->chn_count); + + for (index = 0; + index < (slot_d->chn_count); + index++) { + struct fru_picmgext_chn_desc *d; + data = (fru_data[offset+0]) | + (fru_data[offset+1] << 8) | + (fru_data[offset+2] << 16); + d = (struct fru_picmgext_chn_desc *)&data; + if (verbose) { + printf( " " + "Chn: %02x -> " + "Chn: %02x in " + "Slot: %02x\n", + d->local_chn, + d->remote_chn, + d->remote_slot); + } + offset += FRU_PICMGEXT_CHN_DESC_RECORD_SIZE; + } + slot_d = (struct fru_picmgext_slot_desc*)&fru_data[offset]; + offset += sizeof(struct fru_picmgext_slot_desc); + } + } + break; + + case FRU_PICMG_ADDRESS_TABLE: + { + unsigned int hwaddr; + unsigned int sitetype; + unsigned int sitenum; + unsigned int entries; + unsigned int i; + char *picmg_site_type_strings[] = { + "AdvancedTCA Board", + "Power Entry", + "Shelf FRU Information", + "Dedicated ShMC", + "Fan Tray", + "Fan Filter Tray", + "Alarm", + "AdvancedMC Module", + "PMC", + "Rear Transition Module"}; + + + printf(" FRU_PICMG_ADDRESS_TABLE\n"); + printf(" Type/Len: 0x%02x\n", fru_data[offset++]); + printf(" Shelf Addr: "); + for (i=0;i<20;i++) { + printf("0x%02x ", fru_data[offset++]); + } + printf("\n"); + + entries = fru_data[offset++]; + printf(" Addr Table Entries: 0x%02x\n", entries); + + for (i=0; i<entries; i++) { + hwaddr = fru_data[offset]; + sitenum = fru_data[offset + 1]; + sitetype = fru_data[offset + 2]; + printf( + " HWAddr: 0x%02x (0x%02x) SiteNum: %d SiteType: 0x%02x %s\n", + hwaddr, hwaddr * 2, + sitenum, sitetype, + (sitetype < 0xa) ? + picmg_site_type_strings[sitetype] : + "Reserved"); + offset += 3; + } + } + break; + + case FRU_PICMG_SHELF_POWER_DIST: + { + unsigned int entries; + unsigned int feeds; + unsigned int feedcnt; + unsigned int hwaddr; + unsigned int i; + unsigned int id; + unsigned int j; + unsigned int maxext; + unsigned int maxint; + unsigned int minexp; + + printf(" FRU_PICMG_SHELF_POWER_DIST\n"); + + feeds = fru_data[offset++]; + printf(" Number of Power Feeds: 0x%02x\n", + feeds); + + for (i=0; i<feeds; i++) { + printf(" Feed %d:\n", i); + maxext = fru_data[offset] | + (fru_data[offset+1] << 8); + offset += 2; + maxint = fru_data[offset] | + (fru_data[offset+1] << 8); + offset += 2; + minexp = fru_data[offset]; + offset += 1; + entries = fru_data[offset]; + offset += 1; + + printf( + " Max External Current: %d.%d Amps (0x%04x)\n", + maxext / 10, maxext % 10, maxext); + if (maxint < 0xffff) { + printf( + " Max Internal Current: %d.%d Amps (0x%04x)\n", + maxint / 10, maxint % 10, + maxint); + } else { + printf( + " Max Internal Current: Not Specified\n"); + } + + if (minexp >= 0x48 && minexp <= 0x90) { + printf( + " Min Expected Voltage: -%02d.%dV\n", + minexp / 2, (minexp % 2) * 5); + } else { + printf( + " Min Expected Voltage: -%dV (actual invalid value 0x%x)\n", + 36, minexp); + } + for (j=0; j < entries; j++) { + hwaddr = fru_data[offset++]; + id = fru_data[offset++]; + printf( + " FRU HW Addr: 0x%02x (0x%02x)", + hwaddr, hwaddr * 2); + printf( + " FRU ID: 0x%02x\n", + id); + } + } + } + break; + + case FRU_PICMG_SHELF_ACTIVATION: + { + unsigned int i; + unsigned int count = 0; + + printf(" FRU_PICMG_SHELF_ACTIVATION\n"); + printf( + " Allowance for FRU Act Readiness: 0x%02x\n", + fru_data[offset++]); + + count = fru_data[offset++]; + printf( + " FRU activation and Power Desc Cnt: 0x%02x\n", + count); + + for (i=0; i<count; i++) { + printf(" HW Addr: 0x%02x ", + fru_data[offset++]); + printf(" FRU ID: 0x%02x ", + fru_data[offset++]); + printf(" Max FRU Power: 0x%04x ", + fru_data[offset+0] | + (fru_data[offset+1]<<8)); + offset += 2; + printf(" Config: 0x%02x \n", + fru_data[offset++]); + } + } + break; + + case FRU_PICMG_SHMC_IP_CONN: + printf(" FRU_PICMG_SHMC_IP_CONN\n"); + break; + + case FRU_PICMG_BOARD_P2P: + printf(" FRU_PICMG_BOARD_P2P\n"); + + guid_count = fru_data[offset++]; + printf(" GUID count: %2d\n", guid_count); + for (i = 0 ; i < guid_count; i++ ) { + int j; + printf(" GUID [%2d]: 0x", i); + + for (j=0; j < sizeof(struct fru_picmgext_guid); + j++) { + printf("%02x", fru_data[offset+j]); + } + + printf("\n"); + offset += sizeof(struct fru_picmgext_guid); + } + printf("\n"); + + for (; offset < off + length; + offset += sizeof(struct fru_picmgext_link_desc)) { + + /* to solve little endian /big endian problem */ + struct fru_picmgext_link_desc *d; + unsigned int data = (fru_data[offset+0]) | + (fru_data[offset+1] << 8) | + (fru_data[offset+2] << 16) | + (fru_data[offset+3] << 24); + d = (struct fru_picmgext_link_desc *) &data; + + printf(" Link Grouping ID: 0x%02x\n", + d->grouping); + printf(" Link Type Extension: 0x%02x - ", + d->ext); + if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) { + switch (d->ext) + { + case 0: + printf("10/100/1000BASE-T Link (four-pair)\n"); + break; + case 1: + printf("ShMC Cross-connect (two-pair)\n"); + break; + default: + printf("Unknwon\n"); + break; + } + } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) { + switch (d->ext) + { + case 0: + printf("Fixed 1000Base-BX\n"); + break; + case 1: + printf("Fixed 10GBASE-BX4 [XAUI]\n"); + break; + case 2: + printf("FC-PI\n"); + break; + default: + printf("Unknwon\n"); + break; + } + } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) { + printf("Unknwon\n"); + } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) { + printf("Unknwon\n"); + } else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) { + printf("Unknwon\n"); + } else { + printf("Unknwon\n"); + } + + printf(" Link Type: 0x%02x - ", + d->type); + if (d->type == 0 || d->type == 0xff) { + printf("Reserved\n"); + } + else if (d->type >= 0x06 && d->type <= 0xef) { + printf("Reserved\n"); + } + else if (d->type >= 0xf0 && d->type <= 0xfe) { + printf("OEM GUID Definition\n"); + } + else { + switch (d->type) + { + case FRU_PICMGEXT_LINK_TYPE_BASE: + printf("PICMG 3.0 Base Interface 10/100/1000\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET: + printf("PICMG 3.1 Ethernet Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND: + printf("PICMG 3.2 Infiniband Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR: + printf("PICMG 3.3 Star Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_PCIE: + printf("PICMG 3.4 PCI Express Fabric Interface\n"); + break; + default: + printf("Invalid\n"); + break; + } + } + printf(" Link Designator: \n"); + printf(" Port Flag: 0x%02x\n", + d->desig_port); + printf(" Interface: 0x%02x - ", + d->desig_if); + switch (d->desig_if) + { + case FRU_PICMGEXT_DESIGN_IF_BASE: + printf("Base Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_FABRIC: + printf("Fabric Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL: + printf("Update Channel\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_RESERVED: + printf("Reserved\n"); + break; + default: + printf("Invalid"); + break; + } + printf(" Channel Number: 0x%02x\n", + d->desig_channel); + printf("\n"); + } + + break; + + case FRU_AMC_CURRENT: + { + unsigned char current; + printf(" FRU_AMC_CURRENT\n"); + + current = fru_data[offset]; + printf(" Current draw(@12V): %.2f A [ %.2f Watt ]\n", + (float)current / 10.0f, + (float)current / 10.0f * 12.0f); + printf("\n"); + } + break; + + case FRU_AMC_ACTIVATION: + printf(" FRU_AMC_ACTIVATION\n"); + { + uint16_t max_current; + + max_current = fru_data[offset]; + max_current |= fru_data[++offset]<<8; + printf(" Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n", + (float)max_current / 10.0f, + (float)max_current / 10.0f * 12.0f); + + printf(" Module Activation Readiness: %i sec.\n", fru_data[++offset]); + printf(" Descriptor Count: %i\n", fru_data[++offset]); + printf("\n"); + + for(++offset; offset < off + length; + offset += sizeof(struct fru_picmgext_activation_record)) + { + struct fru_picmgext_activation_record *a; + a = (struct fru_picmgext_activation_record *)&fru_data[offset]; + printf(" IPMB-Address: 0x%x\n", + a->ibmb_addr); + printf(" Max. Module Current: %.2f A\n", + (float)a->max_module_curr / 10.0f); + printf("\n"); + } + } + break; + + case FRU_AMC_CARRIER_P2P: + { + uint16_t index; + printf(" FRU_CARRIER_P2P\n"); + for(; offset < off + length; ) { + struct fru_picmgext_carrier_p2p_record * h = + (struct fru_picmgext_carrier_p2p_record *)&fru_data[offset]; + printf("\n"); + printf(" Resource ID: %i", + (h->resource_id & 0x07)); + printf(" Type: "); + if ((h->resource_id>>7) == 1) { + printf("AMC\n"); + } else { + printf("Local\n"); + } + printf(" Descriptor Count: %i\n", + h->p2p_count); + offset += sizeof(struct fru_picmgext_carrier_p2p_record); + for (index = 0; index < h->p2p_count; index++) { + /* to solve little endian /big endian problem */ + unsigned char data[3]; + struct fru_picmgext_carrier_p2p_descriptor * desc; +# ifndef WORDS_BIGENDIAN + data[0] = fru_data[offset+0]; + data[1] = fru_data[offset+1]; + data[2] = fru_data[offset+2]; +# else + data[0] = fru_data[offset+2]; + data[1] = fru_data[offset+1]; + data[2] = fru_data[offset+0]; +# endif + desc = (struct fru_picmgext_carrier_p2p_descriptor*)&data; + printf(" Port: %02d\t-> Remote Port: %02d\t", + desc->local_port, desc->remote_port); + if ((desc->remote_resource_id >> 7) == 1) { + printf("[ AMC ID: %02d ]\n", + desc->remote_resource_id & 0x0F); + } else { + printf("[ local ID: %02d ]\n", + desc->remote_resource_id & 0x0F); + } + offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor); + } + } + } + break; + + case FRU_AMC_P2P: + { + unsigned int index; + unsigned char channel_count; + struct fru_picmgext_amc_p2p_record * h; + printf(" FRU_AMC_P2P\n"); + guid_count = fru_data[offset]; + printf(" GUID count: %2d\n", guid_count); + for (i = 0 ; i < guid_count; i++) { + int j; + printf(" GUID %2d: ", i); + for (j=0; j < sizeof(struct fru_picmgext_guid); + j++) { + printf("%02x", fru_data[offset+j]); + offset += sizeof(struct fru_picmgext_guid); + printf("\n"); + } + h = (struct fru_picmgext_amc_p2p_record *)&fru_data[++offset]; + printf(" %s", + (h->record_type ? + "AMC Module:" : "On-Carrier Device")); + printf(" Resource ID: %i\n", h->resource_id); + offset += sizeof(struct fru_picmgext_amc_p2p_record); + channel_count = fru_data[offset++]; + printf(" Descriptor Count: %i\n", + channel_count); + for (index = 0; index < channel_count; index++) { + unsigned int data; + struct fru_picmgext_amc_channel_desc_record *d; + /* pack the data in little endian format. + * Stupid intel... + */ + data = fru_data[offset] | + (fru_data[offset + 1] << 8) | + (fru_data[offset + 2] << 16); + d = (struct fru_picmgext_amc_channel_desc_record *)&data; + printf(" Lane 0 Port: %i\n", + d->lane0port); + printf(" Lane 1 Port: %i\n", + d->lane1port); + printf(" Lane 2 Port: %i\n", + d->lane2port); + printf(" Lane 3 Port: %i\n\n", + d->lane3port); + offset += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; + } + for (; offset < off + length;) { + unsigned int data[2]; + struct fru_picmgext_amc_link_desc_record *l; + l = (struct fru_picmgext_amc_link_desc_record *)&data[0]; + data[0] = fru_data[offset] | + (fru_data[offset + 1] << 8) | + (fru_data[offset + 2] << 16) | + (fru_data[offset + 3] << 24); + data[1] = fru_data[offset + 4]; + printf( " Link Designator: Channel ID: %i\n" + " Port Flag 0: %s%s%s%s\n", + l->channel_id, + (l->port_flag_0)?"o":"-", + (l->port_flag_1)?"o":"-", + (l->port_flag_2)?"o":"-", + (l->port_flag_3)?"o":"-" ); + switch (l->type) { + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: + /* AMC.1 */ + printf( " Link Type: %02x - " + "AMC.1 PCI Express\n", l->type); + switch (l->type_ext) { + case AMC_LINK_TYPE_EXT_PCIE_G1_NSSC: + printf( " Link Type Ext: %i - " + " Gen 1 capable - non SSC\n", + l->type_ext); + break; + case AMC_LINK_TYPE_EXT_PCIE_G1_SSC: + printf( " Link Type Ext: %i - " + " Gen 1 capable - SSC\n", + l->type_ext); + break; + case AMC_LINK_TYPE_EXT_PCIE_G2_NSSC: + printf( " Link Type Ext: %i - " + " Gen 2 capable - non SSC\n", + l->type_ext); + break; + case AMC_LINK_TYPE_EXT_PCIE_G2_SSC: + printf( " Link Type Ext: %i - " + " Gen 2 capable - SSC\n", + l->type_ext); + break; + default: + printf( " Link Type Ext: %i - " + " Invalid\n", + l->type_ext); + break; + } + break; + + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: + case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: + /* AMC.1 */ + printf( " Link Type: %02x - " + "AMC.1 PCI Express Advanced Switching\n", + l->type); + printf(" Link Type Ext: %i\n", + l->type_ext); + break; + + case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: + /* AMC.2 */ + printf( " Link Type: %02x - " + "AMC.2 Ethernet\n", + l->type); + switch (l->type_ext) { + case AMC_LINK_TYPE_EXT_ETH_1000_BX: + printf( " Link Type Ext: %i - " + " 1000Base-Bx (SerDES Gigabit) Ethernet Link\n", + l->type_ext); + break; + + case AMC_LINK_TYPE_EXT_ETH_10G_XAUI: + printf( " Link Type Ext: %i - " + " 10Gbit XAUI Ethernet Link\n", + l->type_ext); + break; + + default: + printf( " Link Type Ext: %i - " + " Invalid\n", + l->type_ext); + break; + } + break; + + case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: + /* AMC.3 */ + printf( " Link Type: %02x - " + "AMC.3 Storage\n", + l->type); + switch (l->type_ext) { + case AMC_LINK_TYPE_EXT_STORAGE_FC: + printf( " Link Type Ext: %i - " + " Fibre Channel\n", + l->type_ext); + break; + + case AMC_LINK_TYPE_EXT_STORAGE_SATA: + printf( " Link Type Ext: %i - " + " Serial ATA\n", + l->type_ext); + break; + + case AMC_LINK_TYPE_EXT_STORAGE_SAS: + printf( " Link Type Ext: %i - " + " Serial Attached SCSI\n", + l->type_ext); + break; + + default: + printf( " Link Type Ext: %i - " + " Invalid\n", + l->type_ext); + break; + } + break; + + case FRU_PICMGEXT_AMC_LINK_TYPE_RAPIDIO: + /* AMC.4 */ + printf( " Link Type: %02x - " + "AMC.4 Serial Rapid IO\n", + l->type); + printf(" Link Type Ext: %i\n", + l->type_ext); + break; + + default: + printf( " Link Type: %02x - " + "reserved or OEM GUID", + l->type); + printf(" Link Type Ext: %i\n", + l->type_ext); + break; + } + + printf(" Link group Id: %i\n", + l->group_id); + printf(" Link Asym Match: %i\n\n", + l->asym_match); + offset += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; + } + } + } + break; + + case FRU_AMC_CARRIER_INFO: + { + unsigned char extVersion; + unsigned char siteCount; + + printf(" FRU_CARRIER_INFO\n"); + + extVersion = fru_data[offset++]; + siteCount = fru_data[offset++]; + + printf(" AMC.0 extension version: R%d.%d\n", + (extVersion >> 0)& 0x0F, + (extVersion >> 4)& 0x0F ); + printf(" Carrier Sie Number Cnt: %d\n", siteCount); + + for (i = 0 ; i < siteCount; i++ ){ + printf(" Site ID: %i \n", fru_data[offset++]); + } + printf("\n"); + } + break; + case FRU_PICMG_CLK_CARRIER_P2P: + { + unsigned char desc_count; + int i,j; + + printf(" FRU_PICMG_CLK_CARRIER_P2P\n"); + + desc_count = fru_data[offset++]; + + for(i=0; i<desc_count; i++){ + unsigned char resource_id; + unsigned char channel_count; + + resource_id = fru_data[offset++]; + channel_count = fru_data[offset++]; + + printf("\n"); + printf(" Clock Resource ID: 0x%02x Type: ", resource_id); + if((resource_id & 0xC0)>>6 == 0) {printf("On-Carrier-Device\n");} + else if((resource_id & 0xC0)>>6 == 1) {printf("AMC slot\n");} + else if((resource_id & 0xC0)>>6 == 2) {printf("Backplane\n");} + else{ printf("reserved\n");} + printf(" Channel Count: 0x%02x\n", channel_count); + + for(j=0; j<channel_count; j++){ + unsigned char loc_channel, rem_channel, rem_resource; + + loc_channel = fru_data[offset++]; + rem_channel = fru_data[offset++]; + rem_resource = fru_data[offset++]; + + printf(" CLK-ID: 0x%02x ->", loc_channel); + printf(" remote CLKID: 0x%02x ", rem_channel); + if((rem_resource & 0xC0)>>6 == 0) {printf("[ Carrier-Dev");} + else if((rem_resource & 0xC0)>>6 == 1) {printf("[ AMC slot ");} + else if((rem_resource & 0xC0)>>6 == 2) {printf("[ Backplane ");} + else{ printf("reserved ");} + printf(" 0x%02x ]\n", rem_resource&0xF); + } + } + printf("\n"); + } + break; + case FRU_PICMG_CLK_CONFIG: + { + unsigned char resource_id, descr_count; + int i,j; + + printf(" FRU_PICMG_CLK_CONFIG\n"); + + resource_id = fru_data[offset++]; + descr_count = fru_data[offset++]; + + printf("\n"); + printf(" Clock Resource ID: 0x%02x\n", resource_id); + printf(" Descr. Count: 0x%02x\n", descr_count); + + for(i=0; i<descr_count; i++){ + unsigned char channel_id, control; + unsigned char indirect_cnt, direct_cnt; + + channel_id = fru_data[offset++]; + control = fru_data[offset++]; + printf(" CLK-ID: 0x%02x - ", channel_id); + printf("CTRL 0x%02x [ %12s ]\n", + control, + ((control&0x1)==0)?"Carrier IPMC":"Application"); + + indirect_cnt = fru_data[offset++]; + direct_cnt = fru_data[offset++]; + printf(" Cnt: Indirect 0x%02x / Direct 0x%02x\n", + indirect_cnt, + direct_cnt); + + /* indirect desc */ + for(j=0; j<indirect_cnt; j++){ + unsigned char feature; + unsigned char dep_chn_id; + + feature = fru_data[offset++]; + dep_chn_id = fru_data[offset++]; + + printf(" Feature: 0x%02x [%8s] - ", feature, (feature&0x1)==1?"Source":"Receiver"); + printf(" Dep. CLK-ID: 0x%02x\n", dep_chn_id); + } + + /* direct desc */ + for(j=0; j<direct_cnt; j++){ + unsigned char feature, family, accuracy; + unsigned int freq, min_freq, max_freq; + + feature = fru_data[offset++]; + family = fru_data[offset++]; + accuracy = fru_data[offset++]; + freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) + | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); + offset += 4; + min_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) + | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); + offset += 4; + max_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) + | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); + offset += 4; + + printf(" - Feature: 0x%02x - PLL: %x / Asym: %s\n", + feature, + (feature > 1) & 1, + (feature&1)?"Source":"Receiver"); + printf(" Family: 0x%02x - AccLVL: 0x%02x\n", family, accuracy); + printf(" FRQ: %-9ld - min: %-9ld - max: %-9ld\n", + freq, min_freq, max_freq); + } + printf("\n"); + } + printf("\n"); + } + break; + + case FRU_UTCA_FRU_INFO_TABLE: + case FRU_UTCA_CARRIER_MNG_IP: + case FRU_UTCA_CARRIER_INFO: + case FRU_UTCA_CARRIER_LOCATION: + case FRU_UTCA_SHMC_IP_LINK: + case FRU_UTCA_POWER_POLICY: + case FRU_UTCA_ACTIVATION: + case FRU_UTCA_PM_CAPABILTY: + case FRU_UTCA_FAN_GEOGRAPHY: + case FRU_UTCA_CLOCK_MAPPING: + case FRU_UTCA_MSG_BRIDGE_POLICY: + case FRU_UTCA_OEM_MODULE_DESC: + printf(" Not implemented yet. uTCA specific record found!!\n"); + printf(" - Record ID: 0x%02x\n", h->record_id); + break; + + default: + printf(" Unknown OEM Extension Record ID: %x\n", h->record_id); + break; + + } +} + + +/* __ipmi_fru_print - Do actual work to print a FRU by its ID +* +* @intf: ipmi interface +* @id: fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +__ipmi_fru_print(struct ipmi_intf * intf, uint8_t id) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + + memset(&fru, 0, sizeof(struct fru_info)); + memset(&header, 0, sizeof(struct fru_header)); + + /* + * get info about this FRU + */ + memset(msg_data, 0, 4); + msg_data[0] = id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", + fru.size, fru.access ? "words" : "bytes"); + + if (fru.size < 1) { + lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); + return -1; + } + + /* + * retrieve the FRU header + */ + msg_data[0] = id; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return 1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return 1; + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 1) { + lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", + header.version); + return -1; + } + + /* offsets need converted to bytes + * but that conversion is not done to the structure + * because we may end up with offset > 255 + * which would overflow our 1-byte offset field */ + + lprintf(LOG_DEBUG, "fru.header.version: 0x%x", + header.version); + lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x", + header.offset.internal * 8); + lprintf(LOG_DEBUG, "fru.header.offset.chassis: 0x%x", + header.offset.chassis * 8); + lprintf(LOG_DEBUG, "fru.header.offset.board: 0x%x", + header.offset.board * 8); + lprintf(LOG_DEBUG, "fru.header.offset.product: 0x%x", + header.offset.product * 8); + lprintf(LOG_DEBUG, "fru.header.offset.multi: 0x%x", + header.offset.multi * 8); + + /* + * rather than reading the entire part + * only read the areas we'll format + */ + /* chassis area */ + if ((header.offset.chassis*8) >= sizeof(struct fru_header)) + fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8); + + /* board area */ + if ((header.offset.board*8) >= sizeof(struct fru_header)) + fru_area_print_board(intf, &fru, id, header.offset.board*8); + + /* product area */ + if ((header.offset.product*8) >= sizeof(struct fru_header)) + fru_area_print_product(intf, &fru, id, header.offset.product*8); + + /* multirecord area */ + if( verbose==0 ) /* scipp parsing multirecord */ + return 0; + + if ((header.offset.multi*8) >= sizeof(struct fru_header)) + fru_area_print_multirec(intf, &fru, id, header.offset.multi*8); + + return 0; +} + +/* ipmi_fru_print - Print a FRU from its SDR locator record +* +* @intf: ipmi interface +* @fru: SDR FRU Locator Record +* +* returns -1 on error +*/ +int +ipmi_fru_print(struct ipmi_intf * intf, struct sdr_record_fru_locator * fru) +{ + char desc[17]; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + int rc = 0; + + if (fru == NULL) + return __ipmi_fru_print(intf, 0); + + /* Logical FRU Device + * dev_type == 0x10 + * modifier + * 0x00 = IPMI FRU Inventory + * 0x01 = DIMM Memory ID + * 0x02 = IPMI FRU Inventory + * 0x03 = System Processor FRU + * 0xff = unspecified + * + * EEPROM 24C01 or equivalent + * dev_type >= 0x08 && dev_type <= 0x0f + * modifier + * 0x00 = unspecified + * 0x01 = DIMM Memory ID + * 0x02 = IPMI FRU Inventory + * 0x03 = System Processor Cartridge + */ + if (fru->dev_type != 0x10 && + (fru->dev_type_modifier != 0x02 || + fru->dev_type < 0x08 || fru->dev_type > 0x0f)) + return -1; + + if (fru->dev_slave_addr == IPMI_BMC_SLAVE_ADDR && + fru->device_id == 0) + return 0; + + memset(desc, 0, sizeof(desc)); + memcpy(desc, fru->id_string, fru->id_code & 0x01f); + desc[fru->id_code & 0x01f] = 0; + printf("FRU Device Description : %s (ID %d)\n", desc, fru->device_id); + + switch (fru->dev_type_modifier) { + case 0x00: + case 0x02: + if (BRIDGE_TO_SENSOR(intf, fru->dev_slave_addr, + fru->channel_num)) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = fru->dev_slave_addr; + save_channel = intf->target_channel; + intf->target_channel = fru->channel_num; + } + /* print FRU */ + rc = __ipmi_fru_print(intf, fru->device_id); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + break; + case 0x01: + rc = ipmi_spd_print_fru(intf, fru->device_id); + break; + default: + if (verbose) + printf(" Unsupported device 0x%02x " + "type 0x%02x with modifier 0x%02x\n", + fru->device_id, fru->dev_type, + fru->dev_type_modifier); + else + printf(" Unsupported device\n"); + } + printf("\n"); + + return rc; +} + +/* ipmi_fru_print_all - Print builtin FRU + SDR FRU Locator records +* +* @intf: ipmi interface +* +* returns -1 on error +*/ +static int +ipmi_fru_print_all(struct ipmi_intf * intf) +{ + struct ipmi_sdr_iterator * itr; + struct sdr_get_rs * header; + struct sdr_record_fru_locator * fru; + int rc; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_devid_rsp *devid; + struct sdr_record_mc_locator * mc; + uint32_t save_addr; + + printf("FRU Device Description : Builtin FRU Device (ID 0)\n"); + /* TODO: Figure out if FRU device 0 may show up in SDR records. */ + + /* Do a Get Device ID command to determine device support */ + memset (&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Device ID command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Device ID command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + devid = (struct ipm_devid_rsp *) rsp->data; + + /* Check the FRU inventory device bit to decide whether various */ + /* FRU commands can be issued to FRU device #0 LUN 0 */ + + if (devid->adtl_device_support & 0x08) { /* FRU Inventory Device bit? */ + rc = ipmi_fru_print(intf, NULL); + printf("\n"); + } + + if ((itr = ipmi_sdr_start(intf, 0)) == NULL) + return -1; + + /* Walk the SDRs looking for FRU Devices and Management Controller Devices. */ + /* For FRU devices, print the FRU from the SDR locator record. */ + /* For MC devices, issue FRU commands to the satellite controller to print */ + /* FRU data. */ + while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) + { + if (header->type == SDR_RECORD_TYPE_MC_DEVICE_LOCATOR ) { + /* Check the capabilities of the Management Controller Device */ + mc = (struct sdr_record_mc_locator *) + ipmi_sdr_get_record(intf, header, itr); + /* Does this MC device support FRU inventory device? */ + if (mc && (mc->dev_support & 0x08)) { /* FRU inventory device? */ + /* Yes. Prepare to issue FRU commands to FRU device #0 LUN 0 */ + /* using the slave address specified in the MC record. */ + + /* save current target address */ + save_addr = intf->target_addr; + + /* set new target address to satellite controller */ + intf->target_addr = mc->dev_slave_addr; + + printf("FRU Device Description : %-16s\n", mc->id_string); + + /* print the FRU by issuing FRU commands to the satellite */ + /* controller. */ + rc = __ipmi_fru_print(intf, 0); + + printf("\n"); + + /* restore previous target */ + intf->target_addr = save_addr; + } + + if (mc) { + free(mc); + mc = NULL; + } + + continue; + } + + if (header->type != SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR) + continue; + + /* Print the FRU from the SDR locator record. */ + fru = (struct sdr_record_fru_locator *) + ipmi_sdr_get_record(intf, header, itr); + if (fru == NULL || !fru->logical) { + if (fru) { + free(fru); + fru = NULL; + } + continue; + } + rc = ipmi_fru_print(intf, fru); + free(fru); + fru = NULL; + } + + ipmi_sdr_end(intf, itr); + + return rc; +} + +/* ipmi_fru_read_help() - print help text for 'read' + * + * returns void + */ +void +ipmi_fru_read_help() +{ + lprintf(LOG_NOTICE, "fru read <fru id> <fru file>"); + lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); + lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin"); +} /* ipmi_fru_read_help() */ + +static void +ipmi_fru_read_to_bin(struct ipmi_intf * intf, + char * pFileName, + uint8_t fruId) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_info fru; + uint8_t msg_data[4]; + uint8_t * pFruBuf; + + msg_data[0] = fruId; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) + return; + + if (rsp->ccode > 0) { + if (rsp->ccode == 0xc3) + printf (" Timeout accessing FRU info. (Device not present?)\n"); + return; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + if (verbose) { + printf("Fru Size = %d bytes\n",fru.size); + printf("Fru Access = %xh\n", fru.access); + } + + pFruBuf = malloc(fru.size); + if (pFruBuf != NULL) { + printf("Fru Size : %d bytes\n",fru.size); + read_fru_area(intf, &fru, fruId, 0, fru.size, pFruBuf); + } else { + lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size); + return; + } + + if(pFruBuf != NULL) + { + FILE * pFile; + pFile = fopen(pFileName,"wb"); + if (pFile) { + fwrite(pFruBuf, fru.size, 1, pFile); + printf("Done\n"); + } else { + lprintf(LOG_ERR, "Error opening file %s\n", pFileName); + free(pFruBuf); + pFruBuf = NULL; + return; + } + fclose(pFile); + } + free(pFruBuf); + pFruBuf = NULL; +} + +static void +ipmi_fru_write_from_bin(struct ipmi_intf * intf, + char * pFileName, + uint8_t fruId) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct fru_info fru; + uint8_t msg_data[4]; + uint8_t *pFruBuf; + uint16_t len = 0; + FILE *pFile; + + msg_data[0] = fruId; + + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) + return; + + if (rsp->ccode) { + if (rsp->ccode == 0xc3) + printf(" Timeout accessing FRU info. (Device not present?)\n"); + return; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + if (verbose) { + printf("Fru Size = %d bytes\n", fru.size); + printf("Fru Access = %xh\n", fru.access); + } + + pFruBuf = malloc(fru.size); + if (pFruBuf == NULL) { + lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size); + return; + } + + pFile = fopen(pFileName, "rb"); + if (pFile != NULL) { + len = fread(pFruBuf, 1, fru.size, pFile); + printf("Fru Size : %d bytes\n", fru.size); + printf("Size to Write : %d bytes\n", len); + fclose(pFile); + } else { + lprintf(LOG_ERR, "Error opening file %s\n", pFileName); + } + + if (len != 0) { + write_fru_area(intf, &fru, fruId,0, 0, len, pFruBuf); + lprintf(LOG_INFO,"Done"); + } + + free(pFruBuf); + pFruBuf = NULL; +} + +/* ipmi_fru_write_help() - print help text for 'write' + * + * retruns void + */ +void +ipmi_fru_write_help() +{ + lprintf(LOG_NOTICE, "fru write <fru id> <fru file>"); + lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); + lprintf(LOG_NOTICE, "Example: ipmitool fru write 0 /root/fru.bin"); +} /* ipmi_fru_write_help() */ + +/* ipmi_fru_edit_help - print help text for 'fru edit' command + * + * returns void + */ +void +ipmi_fru_edit_help() +{ + lprintf(LOG_NOTICE, + "fru edit <fruid> field <section> <index> <string> - edit FRU string"); + lprintf(LOG_NOTICE, + "fru edit <fruid> oem iana <record> <format> <args> - limited OEM support"); +} /* ipmi_fru_edit_help() */ + +/* ipmi_fru_edit_multirec - Query new values to replace original FRU content +* +* @intf: interface to use +* @id: FRU id to work on +* +* returns: nothing +*/ +/* Work in progress, copy paste most of the stuff for other functions in this + file ... not elegant yet */ +static int +ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id , + int argc, char ** argv) +{ + + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + + uint16_t retStatus = 0; + uint32_t offFruMultiRec; + uint32_t fruMultiRecSize = 0; + struct fru_info fruInfo; + retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo, + &offFruMultiRec, + &fruMultiRecSize); + + + lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize); + lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); + + { + + + memset(&fru, 0, sizeof(struct fru_info)); + memset(&header, 0, sizeof(struct fru_header)); + + /* + * get info about this FRU + */ + memset(msg_data, 0, 4); + msg_data[0] = id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", + fru.size, fru.access ? "words" : "bytes"); + + if (fru.size < 1) { + lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); + return -1; + } + } + + { + uint8_t * fru_data; + uint32_t fru_len, i; + uint32_t offset= offFruMultiRec; + struct fru_multirec_header * h; + uint32_t last_off, len; + uint8_t error=0; + + i = last_off = offset; + fru_len = 0; + + memset(&fru, 0, sizeof(fru)); + fru_data = malloc(fru.size + 1); + if (fru_data == NULL) { + lprintf(LOG_ERR, " Out of memory!"); + return -1; + } + memset(fru_data, 0, fru.size + 1); + + do { + h = (struct fru_multirec_header *) (fru_data + i); + + /* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */ + if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) + { + len = fru.size - last_off; + if (len > FRU_MULTIREC_CHUNK_SIZE) + len = FRU_MULTIREC_CHUNK_SIZE; + + if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0) + break; + + last_off += len; + } + if( h->type == FRU_RECORD_TYPE_OEM_EXTENSION ){ + + struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) + &fru_data[i + sizeof(struct fru_multirec_header)]; + uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + + uint32_t suppliedIana = 0 ; + /* Now makes sure this is really PICMG record */ + + /* Default to PICMG for backward compatibility */ + if( argc <=2 ) { + suppliedIana = IPMI_OEM_PICMG; + } else { + if( !strncmp( argv[2] , "oem" , 3 )) { + /* Expect IANA number next */ + if( argc <= 3 ) { + lprintf(LOG_ERR, "oem iana <record> <format> [<args>]"); + error = 1; + } else { + if (str2uint(argv[3], &suppliedIana) == 0) { + lprintf(LOG_DEBUG, + "using iana: %d", + suppliedIana); + } else { + lprintf(LOG_ERR, + "Given IANA '%s' is invalid.", + argv[3]); + error = 1; + } + } + } + } + + if( suppliedIana == iana ) { + lprintf(LOG_DEBUG, "Matching record found" ); + + if( iana == IPMI_OEM_PICMG ){ + if( ipmi_fru_picmg_ext_edit(fru_data, + i + sizeof(struct fru_multirec_header), + h->len, h, oh )){ + /* The fru changed */ + write_fru_area(intf,&fru,id, i,i, + h->len+ sizeof(struct fru_multirec_header), fru_data); + } + } + else if( iana == IPMI_OEM_KONTRON ) { + if( ipmi_fru_oemkontron_edit( argc,argv,fru_data, + i + sizeof(struct fru_multirec_header), + h->len, h, oh )){ + /* The fru changed */ + write_fru_area(intf,&fru,id, i,i, + h->len+ sizeof(struct fru_multirec_header), fru_data); + } + } + /* FIXME: Add OEM record support here */ + else{ + printf(" OEM IANA (%s) Record not support in this mode\n", + val2str( iana, ipmi_oem_info)); + error = 1; + } + } + } + i += h->len + sizeof (struct fru_multirec_header); + } while (!(h->format & 0x80) && (error != 1)); + + free(fru_data); + fru_data = NULL; + } + return 0; +} + +/* ipmi_fru_get_help - print help text for 'fru get' + * + * returns void + */ +void +ipmi_fru_get_help() +{ + lprintf(LOG_NOTICE, + "fru get <fruid> oem iana <record> <format> <args> - limited OEM support"); +} /* ipmi_fru_get_help() */ + +void +ipmi_fru_internaluse_help() +{ + lprintf(LOG_NOTICE, + "fru internaluse <fru id> info - get internal use area size"); + lprintf(LOG_NOTICE, + "fru internaluse <fru id> print - print internal use area in hex"); + lprintf(LOG_NOTICE, + "fru internaluse <fru id> read <fru file> - read internal use area to file"); + lprintf(LOG_NOTICE, + "fru internaluse <fru id> write <fru file> - write internal use area from file"); +} /* void ipmi_fru_internaluse_help() */ + +/* ipmi_fru_get_multirec - Query new values to replace original FRU content +* +* @intf: interface to use +* @id: FRU id to work on +* +* returns: nothing +*/ +/* Work in progress, copy paste most of the stuff for other functions in this + file ... not elegant yet */ +static int +ipmi_fru_get_multirec(struct ipmi_intf * intf, uint8_t id , + int argc, char ** argv) +{ + + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + + uint16_t retStatus = 0; + uint32_t offFruMultiRec; + uint32_t fruMultiRecSize = 0; + struct fru_info fruInfo; + retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo, + &offFruMultiRec, + &fruMultiRecSize); + + + lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize); + lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); + + { + + + memset(&fru, 0, sizeof(struct fru_info)); + memset(&header, 0, sizeof(struct fru_header)); + + /* + * get info about this FRU + */ + memset(msg_data, 0, 4); + msg_data[0] = id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", + fru.size, fru.access ? "words" : "bytes"); + + if (fru.size < 1) { + lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); + return -1; + } + } + + { + uint8_t * fru_data; + uint32_t fru_len, i; + uint32_t offset= offFruMultiRec; + struct fru_multirec_header * h; + uint32_t last_off, len; + uint8_t error=0; + + i = last_off = offset; + fru_len = 0; + + fru_data = malloc(fru.size + 1); + if (fru_data == NULL) { + lprintf(LOG_ERR, " Out of memory!"); + return -1; + } + memset(fru_data, 0, fru.size + 1); + + do { + h = (struct fru_multirec_header *) (fru_data + i); + + /* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */ + if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) + { + len = fru.size - last_off; + if (len > FRU_MULTIREC_CHUNK_SIZE) + len = FRU_MULTIREC_CHUNK_SIZE; + + if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0) + break; + + last_off += len; + } + if( h->type == FRU_RECORD_TYPE_OEM_EXTENSION ){ + + struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) + &fru_data[i + sizeof(struct fru_multirec_header)]; + uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + + uint32_t suppliedIana = 0 ; + /* Now makes sure this is really PICMG record */ + if( !strncmp( argv[2] , "oem" , 3 )) { + /* Expect IANA number next */ + if( argc <= 3 ) { + lprintf(LOG_ERR, "oem iana <record> <format>"); + error = 1; + } else { + if (str2uint(argv[3], &suppliedIana) == 0) { + lprintf(LOG_DEBUG, + "using iana: %d", + suppliedIana); + } else { + lprintf(LOG_ERR, + "Given IANA '%s' is invalid.", + argv[3]); + error = 1; + } + } + } + + if( suppliedIana == iana ) { + lprintf(LOG_DEBUG, "Matching record found" ); + + if( iana == IPMI_OEM_KONTRON ) { + ipmi_fru_oemkontron_get( argc,argv,fru_data, + i + sizeof(struct fru_multirec_header), + h->len, h, oh ); + } + /* FIXME: Add OEM record support here */ + else{ + printf(" OEM IANA (%s) Record not supported in this mode\n", + val2str( iana, ipmi_oem_info)); + error = 1; + } + } + } + i += h->len + sizeof (struct fru_multirec_header); + } while (!(h->format & 0x80) && (error != 1)); + + free(fru_data); + fru_data = NULL; + } + return 0; +} + +static int +ipmi_fru_upg_ekeying(struct ipmi_intf * intf, + char * pFileName, + uint8_t fruId) +{ + struct fru_info fruInfo; + uint8_t *buf = NULL; + uint32_t offFruMultiRec = 0; + uint32_t fruMultiRecSize = 0; + uint32_t offFileMultiRec = 0; + uint32_t fileMultiRecSize = 0; + if (pFileName == NULL) { + lprintf(LOG_ERR, "File expected, but none given."); + return (-1); + } + if (ipmi_fru_get_multirec_location_from_fru(intf, fruId, &fruInfo, + &offFruMultiRec, &fruMultiRecSize) != 0) { + lprintf(LOG_ERR, "Failed to get multirec location from FRU."); + return (-1); + } + lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize); + lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); + if (ipmi_fru_get_multirec_size_from_file(pFileName, &fileMultiRecSize, + &offFileMultiRec) != 0) { + lprintf(LOG_ERR, "Failed to get multirec size from file '%s'.", pFileName); + return (-1); + } + buf = malloc(fileMultiRecSize); + if (buf == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return (-1); + } + if (ipmi_fru_get_multirec_from_file(pFileName, buf, fileMultiRecSize, + offFileMultiRec) != 0) { + lprintf(LOG_ERR, "Failed to get multirec from file '%s'.", pFileName); + if (buf != NULL) { + free(buf); + buf = NULL; + } + return (-1); + } + if (ipmi_fru_get_adjust_size_from_buffer(buf, &fileMultiRecSize) != 0) { + lprintf(LOG_ERR, "Failed to adjust size from buffer."); + if (buf != NULL) { + free(buf); + buf = NULL; + } + return (-1); + } + if (write_fru_area(intf, &fruInfo, fruId, 0, offFruMultiRec, + fileMultiRecSize, buf) != 0) { + lprintf(LOG_ERR, "Failed to write FRU area."); + if (buf != NULL) { + free(buf); + buf = NULL; + } + return (-1); + } + if (buf != NULL) { + free(buf); + buf = NULL; + } + lprintf(LOG_INFO, "Done upgrading Ekey."); + return 0; +} + +/* ipmi_fru_upgekey_help - print help text for 'upgEkey' + * + * returns void + */ +void +ipmi_fru_upgekey_help() +{ + lprintf(LOG_NOTICE, "fru upgEkey <fru id> <fru file>"); + lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); + lprintf(LOG_NOTICE, "Example: ipmitool fru upgEkey 0 /root/fru.bin"); +} /* ipmi_fru_upgekey_help() */ + +static int +ipmi_fru_get_multirec_size_from_file(char * pFileName, + uint32_t * pSize, + uint32_t * pOffset) +{ + struct fru_header header; + FILE * pFile; + uint8_t len = 0; + uint32_t end = 0; + *pSize = 0; + + pFile = fopen(pFileName,"rb"); + if (pFile) { + rewind(pFile); + len = fread(&header, 1, 8, pFile); + fseek(pFile, 0, SEEK_END); + end = ftell(pFile); + fclose(pFile); + } + + lprintf(LOG_DEBUG, "File Size = %lu\n", end); + lprintf(LOG_DEBUG, "Len = %u\n", len); + + if (len != 8) { + printf("Error with file %s in getting size\n", pFileName); + return -1; + } + + if (header.version != 0x01) { + printf ("Unknown FRU header version %02x.\n", header.version); + return -1; + } + + /* Retreive length */ + if (((header.offset.internal * 8) > (header.offset.internal * 8)) && + ((header.offset.internal * 8) < end)) + end = (header.offset.internal * 8); + + if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) && + ((header.offset.chassis * 8) < end)) + end = (header.offset.chassis * 8); + + if (((header.offset.board * 8) > (header.offset.board * 8)) && + ((header.offset.board * 8) < end)) + end = (header.offset.board * 8); + + if (((header.offset.product * 8) > (header.offset.product * 8)) && + ((header.offset.product * 8) < end)) + end = (header.offset.product * 8); + + *pSize = end - (header.offset.multi * 8); + *pOffset = (header.offset.multi * 8); + + return 0; +} + +int +ipmi_fru_get_adjust_size_from_buffer(uint8_t * fru_data, uint32_t *pSize) +{ + struct fru_multirec_header * head; + int status = 0; + uint8_t checksum = 0; + uint8_t counter = 0; + uint16_t count = 0; + do { + checksum = 0; + head = (struct fru_multirec_header *) (fru_data + count); + if (verbose) { + printf("Adding ("); + } + for (counter = 0; counter < sizeof(struct fru_multirec_header); counter++) { + if (verbose) { + printf(" %02X", *(fru_data + count + counter)); + } + checksum += *(fru_data + count + counter); + } + if (verbose) { + printf(")"); + } + if (checksum != 0) { + lprintf(LOG_ERR, "Bad checksum in Multi Records"); + status = (-1); + if (verbose) { + printf("--> FAIL"); + } + } else if (verbose) { + printf("--> OK"); + } + if (verbose > 1 && checksum == 0) { + for (counter = 0; counter < head->len; counter++) { + printf(" %02X", *(fru_data + count + counter + + sizeof(struct fru_multirec_header))); + } + } + if (verbose) { + printf("\n"); + } + count += head->len + sizeof (struct fru_multirec_header); + } while ((!(head->format & 0x80)) && (status == 0)); + + *pSize = count; + lprintf(LOG_DEBUG, "Size of multirec: %lu\n", *pSize); + return status; +} + +static int +ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea, + uint32_t size, uint32_t offset) +{ + FILE * pFile; + uint32_t len = 0; + if (pFileName == NULL) { + lprintf(LOG_ERR, "Invalid file name given."); + return (-1); + } + + errno = 0; + pFile = fopen(pFileName, "rb"); + if (!pFile) { + lprintf(LOG_ERR, "Error opening file '%s': %i -> %s.", pFileName, errno, + strerror(errno)); + return (-1); + } + errno = 0; + if (fseek(pFile, offset, SEEK_SET) != 0) { + lprintf(LOG_ERR, "Failed to seek in file '%s': %i -> %s.", pFileName, errno, + strerror(errno)); + fclose(pFile); + return (-1); + } + len = fread(pBufArea, size, 1, pFile); + fclose(pFile); + + if (len != 1) { + lprintf(LOG_ERR, "Error in file '%s'.", pFileName); + return (-1); + } + return 0; +} + +static int +ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, + uint8_t fruId, + struct fru_info *pFruInfo, + uint32_t * pRetLocation, + uint32_t * pRetSize) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[4]; + uint32_t end; + struct fru_header header; + + *pRetLocation = 0; + + msg_data[0] = fruId; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + if (verbose > 1) + printf("no response\n"); + return -1; + } + + if (rsp->ccode > 0) { + if (rsp->ccode == 0xc3) + printf (" Timeout accessing FRU info. (Device not present?)\n"); + else + printf (" CCODE = 0x%02x\n", rsp->ccode); + return -1; + } + pFruInfo->size = (rsp->data[1] << 8) | rsp->data[0]; + pFruInfo->access = rsp->data[2] & 0x1; + + if (verbose > 1) + printf("pFruInfo->size = %d bytes (accessed by %s)\n", + pFruInfo->size, pFruInfo->access ? "words" : "bytes"); + + if (!pFruInfo->size) + return -1; + + msg_data[0] = fruId; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) + return -1; + if (rsp->ccode > 0) { + if (rsp->ccode == 0xc3) + printf (" Timeout while reading FRU data. (Device not present?)\n"); + return -1; + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 0x01) { + printf (" Unknown FRU header version %02x.\n", header.version); + return -1; + } + + end = pFruInfo->size; + + /* Retreive length */ + if (((header.offset.internal * 8) > (header.offset.internal * 8)) && + ((header.offset.internal * 8) < end)) + end = (header.offset.internal * 8); + + if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) && + ((header.offset.chassis * 8) < end)) + end = (header.offset.chassis * 8); + + if (((header.offset.board * 8) > (header.offset.board * 8)) && + ((header.offset.board * 8) < end)) + end = (header.offset.board * 8); + + if (((header.offset.product * 8) > (header.offset.product * 8)) && + ((header.offset.product * 8) < end)) + end = (header.offset.product * 8); + + *pRetSize = end; + *pRetLocation = 8 * header.offset.multi; + + return 0; +} + +/* ipmi_fru_get_internal_use_offset - Retreive internal use offset +* +* @intf: ipmi interface +* @id: fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_get_internal_use_info( struct ipmi_intf * intf, + uint8_t id, + struct fru_info * fru, + uint16_t * size, + uint16_t * offset) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct fru_header header; + uint8_t msg_data[4]; + + // Init output value + * offset = 0; + * size = 0; + + memset(fru, 0, sizeof(struct fru_info)); + memset(&header, 0, sizeof(struct fru_header)); + + /* + * get info about this FRU + */ + memset(msg_data, 0, 4); + msg_data[0] = id; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memset(&fru, 0, sizeof(fru)); + fru->size = (rsp->data[1] << 8) | rsp->data[0]; + fru->access = rsp->data[2] & 0x1; + + lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", + fru->size, fru->access ? "words" : "bytes"); + + if (fru->size < 1) { + lprintf(LOG_ERR, " Invalid FRU size %d", fru->size); + return -1; + } + + /* + * retrieve the FRU header + */ + msg_data[0] = id; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return 1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return 1; + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 1) { + lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", + header.version); + return -1; + } + + lprintf(LOG_DEBUG, "fru.header.version: 0x%x", + header.version); + lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x", + header.offset.internal * 8); + lprintf(LOG_DEBUG, "fru.header.offset.chassis: 0x%x", + header.offset.chassis * 8); + lprintf(LOG_DEBUG, "fru.header.offset.board: 0x%x", + header.offset.board * 8); + lprintf(LOG_DEBUG, "fru.header.offset.product: 0x%x", + header.offset.product * 8); + lprintf(LOG_DEBUG, "fru.header.offset.multi: 0x%x", + header.offset.multi * 8); + + if((header.offset.internal*8) == 0) + { + * size = 0; + * offset = 0; + } + else + { + (* offset) = (header.offset.internal*8); + + if(header.offset.chassis != 0) + { + (* size) = ((header.offset.chassis*8)-(* offset)); + } + else if(header.offset.board != 0) + { + (* size) = ((header.offset.board*8)-(* offset)); + } + else if(header.offset.product != 0) + { + (* size) = ((header.offset.product*8)-(* offset)); + } + else if(header.offset.multi != 0) + { + (* size) = ((header.offset.multi*8)-(* offset)); + } + else + { + (* size) = (fru->size - (* offset)); + } + } + return 0; +} + +/* ipmi_fru_info_internal_use - print internal use info +* +* @intf: ipmi interface +* @id: fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_info_internal_use(struct ipmi_intf * intf, uint8_t id) +{ + struct fru_info fru; + uint16_t size; + uint16_t offset; + int rc = 0; + + rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + + if(rc == 0) + { + lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); + printf( "Internal Use Area Size : %i\n", size); + } + else + { + lprintf(LOG_ERR, "Cannot access internal use area"); + return -1; + } + return 0; +} + +/* ipmi_fru_help - print help text for FRU subcommand + * + * returns void + */ +void +ipmi_fru_help() +{ + lprintf(LOG_NOTICE, + "FRU Commands: print read write upgEkey edit internaluse get"); +} /* ipmi_fru_help() */ + +/* ipmi_fru_read_internal_use - print internal use are in hex or file +* +* @intf: ipmi interface +* @id: fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName) +{ + struct fru_info fru; + uint16_t size; + uint16_t offset; + int rc = 0; + + rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + + if(rc == 0) + { + uint8_t * frubuf; + + lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); + printf( "Internal Use Area Size : %i\n", size); + + frubuf = malloc( size ); + if(frubuf) + { + rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf); + + if(rc == 0) + { + if(pFileName == NULL) + { + uint16_t counter; + for(counter = 0; counter < size; counter ++) + { + if((counter % 16) == 0) + printf("\n%02i- ", (counter / 16)); + printf("%02X ", frubuf[counter]); + } + } + else + { + FILE * pFile; + pFile = fopen(pFileName,"wb"); + if (pFile) + { + fwrite(frubuf, size, 1, pFile); + printf("Done\n"); + } + else + { + lprintf(LOG_ERR, "Error opening file %s\n", pFileName); + free(frubuf); + frubuf = NULL; + return -1; + } + fclose(pFile); + } + } + printf("\n"); + + free(frubuf); + frubuf = NULL; + } + + } + else + { + lprintf(LOG_ERR, "Cannot access internal use area"); + } + return 0; +} + +/* ipmi_fru_write_internal_use - print internal use are in hex or file +* +* @intf: ipmi interface +* @id: fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_write_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName) +{ + struct fru_info fru; + uint16_t size; + uint16_t offset; + int rc = 0; + + rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + + if(rc == 0) + { + uint8_t * frubuf; + FILE * fp; + uint32_t fileLength = 0; + + lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); + printf( "Internal Use Area Size : %i\n", size); + + fp = fopen(pFileName, "r"); + + if(fp) + { + /* Retreive file length, check if it's fits the Eeprom Size */ + fseek(fp, 0 ,SEEK_END); + fileLength = ftell(fp); + + lprintf(LOG_ERR, "File Size: %i", fileLength); + lprintf(LOG_ERR, "Area Size: %i", size); + if(fileLength != size) + { + lprintf(LOG_ERR, "File size does not fit Eeprom Size"); + fclose(fp); + fp = NULL; + } + else + { + fseek(fp, 0 ,SEEK_SET); + } + } + + if(fp) + { + frubuf = malloc( size ); + if(frubuf) + { + uint16_t fru_read_size; + fru_read_size = fread(frubuf, 1, size, fp); + + if(fru_read_size == size) + { + rc = write_fru_area(intf, &fru, id, 0, offset, size, frubuf); + + if(rc == 0) + { + lprintf(LOG_INFO, "Done\n"); + } + } + else + { + lprintf(LOG_ERR, "Unable to read file: %i\n", fru_read_size); + } + + free(frubuf); + frubuf = NULL; + } + fclose(fp); + fp = NULL; + } + } + else + { + lprintf(LOG_ERR, "Cannot access internal use area"); + } + return 0; +} + +int +ipmi_fru_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + uint8_t fru_id = 0; + + if (argc < 1) { + rc = ipmi_fru_print_all(intf); + } + else if (strncmp(argv[0], "help", 4) == 0) { + ipmi_fru_help(); + return 0; + } + else if (strncmp(argv[0], "print", 5) == 0 || + strncmp(argv[0], "list", 4) == 0) { + if (argc > 1) { + if (strcmp(argv[1], "help") == 0) { + lprintf(LOG_NOTICE, "fru print [fru id] - print information about FRU(s)"); + return 0; + } + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + rc = __ipmi_fru_print(intf, fru_id); + } else { + rc = ipmi_fru_print_all(intf); + } + } + else if (!strncmp(argv[0], "read", 5)) { + if (argc > 1 && strcmp(argv[1], "help") == 0) { + ipmi_fru_read_help(); + return 0; + } else if (argc < 3) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_read_help(); + return (-1); + } + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + /* There is a file name in the parameters */ + if (is_valid_filename(argv[2]) != 0) + return (-1); + + if (verbose) { + printf("FRU ID : %d\n", fru_id); + printf("FRU File : %s\n", argv[2]); + } + /* TODO - rc is missing */ + ipmi_fru_read_to_bin(intf, argv[2], fru_id); + } + else if (!strncmp(argv[0], "write", 5)) { + if (argc > 1 && strcmp(argv[1], "help") == 0) { + ipmi_fru_write_help(); + return 0; + } else if (argc < 3) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_write_help(); + return (-1); + } + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + /* There is a file name in the parameters */ + if (is_valid_filename(argv[2]) != 0) + return (-1); + + if (verbose) { + printf("FRU ID : %d\n", fru_id); + printf("FRU File : %s\n", argv[2]); + } + /* TODO - rc is missing */ + ipmi_fru_write_from_bin(intf, argv[2], fru_id); + } + else if (!strncmp(argv[0], "upgEkey", 7)) { + if (argc > 1 && strcmp(argv[1], "help") == 0) { + ipmi_fru_upgekey_help(); + return 0; + } else if (argc < 3) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_upgekey_help(); + return (-1); + } + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + /* There is a file name in the parameters */ + if (is_valid_filename(argv[2]) != 0) + return (-1); + + rc = ipmi_fru_upg_ekeying(intf, argv[2], fru_id); + } + else if (!strncmp(argv[0], "internaluse", 11)) { + if (argc > 1 && strcmp(argv[1], "help") == 0) { + ipmi_fru_internaluse_help(); + return 0; + } + + if ( (argc >= 3) && (!strncmp(argv[2], "info", 4)) ) { + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + rc = ipmi_fru_info_internal_use(intf, fru_id); + } + else if ( (argc >= 3) && (!strncmp(argv[2], "print", 5)) ) { + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + rc = ipmi_fru_read_internal_use(intf, fru_id, NULL); + } + else if ( (argc >= 4) && (!strncmp(argv[2], "read", 4)) ) { + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + /* There is a file name in the parameters */ + if (is_valid_filename(argv[3]) != 0) + return (-1); + + lprintf(LOG_DEBUG, "FRU ID : %d", fru_id); + lprintf(LOG_DEBUG, "FRU File : %s", argv[3]); + + rc = ipmi_fru_read_internal_use(intf, fru_id, argv[3]); + } + else if ( (argc >= 4) && (!strncmp(argv[2], "write", 5)) ) { + + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + /* There is a file name in the parameters */ + if (is_valid_filename(argv[3]) != 0) + return (-1); + + lprintf(LOG_DEBUG, "FRU ID : %d", fru_id); + lprintf(LOG_DEBUG, "FRU File : %s", argv[3]); + + rc = ipmi_fru_write_internal_use(intf, fru_id, argv[3]); + } else { + lprintf(LOG_ERR, + "Either unknown command or not enough parameters given."); + ipmi_fru_internaluse_help(); + return (-1); + } + } + else if (!strncmp(argv[0], "edit", 4)) { + if (argc > 1 && strcmp(argv[1], "help") == 0) { + ipmi_fru_edit_help(); + return 0; + } else if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_edit_help(); + return (-1); + } + + if (argc >= 2) { + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + if (verbose) { + printf("FRU ID : %d\n", fru_id); + } + } else { + printf("Using default FRU ID: %d\n", fru_id); + } + + if (argc >= 3) { + if (!strncmp(argv[2], "field", 5)) { + if (argc != 6) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_edit_help(); + return (-1); + } + rc = ipmi_fru_set_field_string(intf, fru_id, *argv[3], *argv[4], + (char *) argv[5]); + } else if (!strncmp(argv[2], "oem", 3)) { + rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv); + } else { + lprintf(LOG_ERR, "Invalid command: %s", argv[2]); + ipmi_fru_edit_help(); + return (-1); + } + } else { + rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv); + } + } + else if (!strncmp(argv[0], "get", 4)) { + if (argc > 1 && (strncmp(argv[1], "help", 4) == 0)) { + ipmi_fru_get_help(); + return 0; + } else if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_fru_get_help(); + return (-1); + } + + if (argc >= 2) { + if (is_fru_id(argv[1], &fru_id) != 0) + return (-1); + + if (verbose) { + printf("FRU ID : %d\n", fru_id); + } + } else { + printf("Using default FRU ID: %d\n", fru_id); + } + + if (argc >= 3) { + if (!strncmp(argv[2], "oem", 3)) { + rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv); + } else { + lprintf(LOG_ERR, "Invalid command: %s", argv[2]); + ipmi_fru_get_help(); + return (-1); + } + } else { + rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv); + } + } + else { + lprintf(LOG_ERR, "Invalid FRU command: %s", argv[0]); + ipmi_fru_help(); + return (-1); + } + + return rc; +} + +/* ipmi_fru_set_field_string - Set a field string to a new value, Need to be the same size. If +* size if not equal, the function ipmi_fru_set_field_string_rebuild +* will be called. +* +* @intf: ipmi interface +* @id: fru id +* @f_type: Type of the Field : c=Chassis b=Board p=Product +* @f_index: findex of the field, zero indexed. +* @f_string: NULL terminated string +* +* returns -1 on error +* returns 1 if successful +*/ +static int +ipmi_fru_set_field_string(struct ipmi_intf * intf, uint8_t fruId, uint8_t +f_type, uint8_t f_index, char *f_string) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + uint8_t checksum; + int i = 0; + int rc = 1; + uint8_t *fru_data = NULL; + uint8_t *fru_area = NULL; + uint32_t fru_field_offset, fru_field_offset_tmp; + uint32_t fru_section_len, header_offset; + + memset(msg_data, 0, 4); + msg_data[0] = fruId; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + if (fru.size < 1) { + printf(" Invalid FRU size %d", fru.size); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + /* + * retrieve the FRU header + */ + msg_data[0] = fruId; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf(" Device not present (No Response)\n"); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + if (rsp->ccode > 0) + { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 1) + { + printf(" Unknown FRU header version 0x%02x", + header.version); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + + fru_data = malloc( fru.size ); + + if( fru_data == NULL ) + { + printf("Out of memory!\n"); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + + /* Setup offset from the field type */ + + /* Chassis type field */ + if (f_type == 'c' ) { + header_offset = (header.offset.chassis * 8); + read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); + fru_field_offset = 3; + fru_section_len = *(fru_data + 1) * 8; + } + /* Board type field */ + else if (f_type == 'b' ) { + header_offset = (header.offset.board * 8); + read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); + fru_field_offset = 6; + fru_section_len = *(fru_data + 1) * 8; + } + /* Product type field */ + else if (f_type == 'p' ) { + header_offset = (header.offset.product * 8); + read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); + fru_field_offset = 3; + fru_section_len = *(fru_data + 1) * 8; + } + else + { + printf("Wrong field type."); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + memset(fru_data, 0, fru.size); + if( read_fru_area(intf ,&fru, fruId, header_offset , + fru_section_len , fru_data) < 0 ) + { + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + /* Convert index from character to decimal */ + f_index= f_index - 0x30; + + /*Seek to field index */ + for (i=0; i <= f_index; i++) { + fru_field_offset_tmp = fru_field_offset; + if (fru_area != NULL) { + free(fru_area); + fru_area = NULL; + } + fru_area = (uint8_t *) get_fru_area_str(fru_data, &fru_field_offset); + } + + if( (fru_area == NULL ) || strlen((const char *)fru_area) == 0 ) { + printf("Field not found !\n"); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + + if ( strlen((const char *)fru_area) == strlen((const char *)f_string) ) + { + printf("Updating Field '%s' with '%s' ...\n", fru_area, f_string ); + memcpy(fru_data + fru_field_offset_tmp + 1, + f_string, strlen(f_string)); + + checksum = 0; + /* Calculate Header Checksum */ + for( i = header_offset; i < header_offset + + fru_section_len - 1; i ++ ) + { + checksum += fru_data[i]; + } + checksum = (~checksum) + 1; + fru_data[header_offset + fru_section_len - 1] = checksum; + + /* Write the updated section to the FRU data; source offset => 0 */ + if( write_fru_area(intf, &fru, fruId, 0, + header_offset, fru_section_len, fru_data) < 0 ) + { + printf("Write to FRU data failed.\n"); + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + } + else { + printf("String size are not equal, resizing fru to fit new string\n"); + if( + ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string) + ) + { + rc = (-1); + goto ipmi_fru_set_field_string_out; + } + } + + ipmi_fru_set_field_string_out: + if (fru_data != NULL) { + free(fru_data); + fru_data = NULL; + } + if (fru_area != NULL) { + free(fru_area); + fru_area = NULL; + } + + return rc; +} + +/* + This function can update a string within of the following section when the size is not equal: + + Chassis + Product + Board +*/ +/* ipmi_fru_set_field_string_rebuild - Set a field string to a new value, When size are not +* the same size. +* +* This function can update a string within of the following section when the size is not equal: +* +* - Chassis +* - Product +* - Board +* +* @intf: ipmi interface +* @fruId: fru id +* @fru: info about fru +* @header: contain the header of the FRU +* @f_type: Type of the Field : c=Chassis b=Board p=Product +* @f_index: findex of the field, zero indexed. +* @f_string: NULL terminated string +* +* returns -1 on error +* returns 1 if successful +*/ + +#define DBG_RESIZE_FRU +static int +ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId, + struct fru_info fru, struct fru_header header, + uint8_t f_type, uint8_t f_index, char *f_string) +{ + uint8_t msg_data[4]; + uint8_t checksum; + int i = 0; + uint8_t *fru_data_old = NULL; + uint8_t *fru_data_new = NULL; + uint8_t *fru_area = NULL; + uint32_t fru_field_offset, fru_field_offset_tmp; + uint32_t fru_section_len, old_section_len, header_offset; + uint32_t chassis_offset, board_offset, product_offset; + uint32_t chassis_len, board_len, product_len, product_len_new; + int num_byte_change = 0, padding_len = 0; + uint32_t counter; + unsigned char cksum; + int rc = 1; + + fru_data_old = calloc( fru.size, sizeof(uint8_t) ); + + fru_data_new = malloc( fru.size ); + + if( fru_data_old == NULL || fru_data_new == NULL ) + { + printf("Out of memory!\n"); + rc = (-1); + goto ipmi_fru_set_field_string_rebuild_out; + } + + /************************* + 1) Read ALL FRU */ + printf("Read All FRU area\n"); + printf("Fru Size : %u bytes\n", fru.size); + + /* Read current fru data */ + read_fru_area(intf ,&fru, fruId, 0, fru.size , fru_data_old); + + #ifdef DBG_RESIZE_FRU + printf("Copy to new FRU\n"); + #endif + + /************************* + 2) Copy all FRU to new FRU */ + memcpy(fru_data_new, fru_data_old, fru.size); + + /* Build location of all modifiable components */ + chassis_offset = (header.offset.chassis * 8); + board_offset = (header.offset.board * 8); + product_offset = (header.offset.product * 8); + + /* Retrieve length of all modifiable components */ + chassis_len = *(fru_data_old + chassis_offset + 1) * 8; + board_len = *(fru_data_old + board_offset + 1) * 8; + product_len = *(fru_data_old + product_offset + 1) * 8; + product_len_new = product_len; + + /* Chassis type field */ + if (f_type == 'c' ) + { + header_offset = chassis_offset; + fru_field_offset = chassis_offset + 3; + fru_section_len = chassis_len; + } + /* Board type field */ + else if (f_type == 'b' ) + { + header_offset = board_offset; + fru_field_offset = board_offset + 6; + fru_section_len = board_len; + } + /* Product type field */ + else if (f_type == 'p' ) + { + header_offset = product_offset; + fru_field_offset = product_offset + 3; + fru_section_len = product_len; + } + else + { + printf("Wrong field type."); + rc = (-1); + goto ipmi_fru_set_field_string_rebuild_out; + } + + /* Keep length for future old section display */ + old_section_len = fru_section_len; + + /************************* + 3) Seek to field index */ + for (i = 0;i <= f_index; i++) { + fru_field_offset_tmp = fru_field_offset; + if (fru_area != NULL) { + free(fru_area); + fru_area = NULL; + } + fru_area = (uint8_t *) get_fru_area_str(fru_data_old, &fru_field_offset); + } + + if( (fru_area == NULL ) || strlen((const char *)fru_area) == 0 ) { + printf("Field not found (1)!\n"); + rc = (-1); + goto ipmi_fru_set_field_string_rebuild_out; + } + + #ifdef DBG_RESIZE_FRU + printf("Section Length: %u\n", fru_section_len); + #endif + + /************************* + 4) Check number of padding bytes and bytes changed */ + for(counter = 2; counter < fru_section_len; counter ++) + { + if(*(fru_data_old + (header_offset + fru_section_len - counter)) == 0) + padding_len ++; + else + break; + } + num_byte_change = strlen(f_string) - strlen(fru_area); + + #ifdef DBG_RESIZE_FRU + printf("Padding Length: %u\n", padding_len); + printf("NumByte Change: %i\n", num_byte_change); + printf("Start SecChnge: %x\n", *(fru_data_old + fru_field_offset_tmp)); + printf("End SecChnge : %x\n", *(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)); + + printf("Start Section : %x\n", *(fru_data_old + header_offset)); + printf("End Sec wo Pad: %x\n", *(fru_data_old + header_offset + fru_section_len - 2 - padding_len)); + printf("End Section : %x\n", *(fru_data_old + header_offset + fru_section_len - 1)); + #endif + + /* Calculate New Padding Length */ + padding_len -= num_byte_change; + + #ifdef DBG_RESIZE_FRU + printf("New Padding Length: %i\n", padding_len); + #endif + + /************************* + 5) Check if section must be resize. This occur when padding length is not between 0 and 7 */ + if( (padding_len < 0) || (padding_len >= 8)) + { + uint32_t remaining_offset = ((header.offset.product * 8) + product_len); + int change_size_by_8; + + if(padding_len >= 8) + { + /* Section must be set smaller */ + change_size_by_8 = ((padding_len) / 8) * (-1); + } + else + { + /* Section must be set bigger */ + change_size_by_8 = 1 + (((padding_len+1) / 8) * (-1)); + } + + /* Recalculate padding and section length base on the section changes */ + fru_section_len += (change_size_by_8 * 8); + padding_len += (change_size_by_8 * 8); + + #ifdef DBG_RESIZE_FRU + printf("change_size_by_8: %i\n", change_size_by_8); + printf("New Padding Length: %i\n", padding_len); + printf("change_size_by_8: %i\n", change_size_by_8); + printf("header.offset.board: %i\n", header.offset.board); + #endif + + /* Must move sections */ + /* Section that can be modified are as follow + Chassis + Board + product */ + + /* Chassis type field */ + if (f_type == 'c' ) + { + printf("Moving Section Chassis, from %i to %i\n", + ((header.offset.board) * 8), + ((header.offset.board + change_size_by_8) * 8) + ); + memcpy( + (fru_data_new + ((header.offset.board + change_size_by_8) * 8)), + (fru_data_old + (header.offset.board) * 8), + board_len + ); + header.offset.board += change_size_by_8; + } + /* Board type field */ + if ((f_type == 'c' ) || (f_type == 'b' )) + { + printf("Moving Section Product, from %i to %i\n", + ((header.offset.product) * 8), + ((header.offset.product + change_size_by_8) * 8) + ); + memcpy( + (fru_data_new + ((header.offset.product + change_size_by_8) * 8)), + (fru_data_old + (header.offset.product) * 8), + product_len + ); + header.offset.product += change_size_by_8; + } + + /* Adjust length of the section */ + if (f_type == 'c') + { + *(fru_data_new + chassis_offset + 1) += change_size_by_8; + } + else if( f_type == 'b') + { + *(fru_data_new + board_offset + 1) += change_size_by_8; + } + else if( f_type == 'p') + { + *(fru_data_new + product_offset + 1) += change_size_by_8; + product_len_new = *(fru_data_new + product_offset + 1) * 8; + } + + /* Rebuild Header checksum */ + { + unsigned char * pfru_header = (unsigned char *) &header; + header.checksum = 0; + for(counter = 0; counter < (sizeof(struct fru_header) -1); counter ++) + { + header.checksum += pfru_header[counter]; + } + header.checksum = (0 - header.checksum); + memcpy(fru_data_new, pfru_header, sizeof(struct fru_header)); + } + + /* Move remaining sections in 1 copy */ + printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n", + remaining_offset, + ((header.offset.product) * 8) + product_len_new + ); + if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0) + { + memcpy( + fru_data_new + (header.offset.product * 8) + product_len_new, + fru_data_old + remaining_offset, + fru.size - remaining_offset + ); + } + else + { + memcpy( + fru_data_new + (header.offset.product * 8) + product_len_new, + fru_data_old + remaining_offset, + fru.size - ((header.offset.product * 8) + product_len_new) + ); + } + } + + /* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal + error */ + /************************* + 6) Update Field and sections */ + if( (padding_len >=0) && (padding_len < 8)) + { + /* Do not requires any change in other section */ + + /* Change field length */ + printf( + "Updating Field : '%s' with '%s' ... (Length from '%d' to '%d')\n", + fru_area, f_string, + (int)*(fru_data_old + fru_field_offset_tmp), + (int)(0xc0 + strlen(f_string))); + *(fru_data_new + fru_field_offset_tmp) = (0xc0 + strlen(f_string)); + memcpy(fru_data_new + fru_field_offset_tmp + 1, f_string, strlen(f_string)); + + /* Copy remaing bytes in section */ +#ifdef DBG_RESIZE_FRU + printf("Copying remaining of sections: %d \n", + (int)((fru_data_old + header_offset + fru_section_len - 1) - + (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1))); +#endif + + memcpy((fru_data_new + fru_field_offset_tmp + 1 + + strlen(f_string)), + (fru_data_old + fru_field_offset_tmp + 1 + + strlen(fru_area)), + ((fru_data_old + header_offset + fru_section_len - 1) - + (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1))); + + /* Add Padding if required */ + for(counter = 0; counter < padding_len; counter ++) + { + *(fru_data_new + header_offset + fru_section_len - 1 - + padding_len + counter) = 0; + } + + /* Calculate New Checksum */ + cksum = 0; + for( counter = 0; counter <fru_section_len-1; counter ++ ) + { + cksum += *(fru_data_new + header_offset + counter); + } + *(fru_data_new + header_offset + fru_section_len - 1) = (0 - cksum); + + #ifdef DBG_RESIZE_FRU + printf("Calculate New Checksum: %x\n", (0 - cksum)); + #endif + + /****** ENABLE to show section modified before and after ********/ + #if 0 + printf("Section: "); + for( counter = 0; counter <old_section_len; counter ++ ) + { + if((counter %16) == 0) + { + printf("\n"); + } + printf( "%02X ", *(fru_data_old + header_offset + counter) ); + } + printf("\n"); + + printf("Section: "); + for( counter = 0; counter <fru_section_len; counter ++ ) + { + if((counter %16) == 0) + { + printf("\n"); + } + printf( "%02X ", *(fru_data_new + header_offset + counter) ); + } + printf("\n"); + #endif + } + else + { + printf( "Internal error, padding length %i (must be from 0 to 7) ", padding_len ); + rc = (-1); + goto ipmi_fru_set_field_string_rebuild_out; + } + + /************************* + 7) Finally, write new FRU */ + printf("Writing new FRU.\n"); + if( write_fru_area( intf, &fru, fruId, 0, 0, fru.size, fru_data_new ) < 0 ) + { + printf("Write to FRU data failed.\n"); + rc = (-1); + goto ipmi_fru_set_field_string_rebuild_out; + } + + printf("Done.\n"); + + ipmi_fru_set_field_string_rebuild_out: + if (fru_area != NULL) { + free(fru_area); + fru_area = NULL; + } + if (fru_data_new != NULL) { + free(fru_data_new); + fru_data_new = NULL; + } + if (fru_data_old != NULL) { + free(fru_data_old); + fru_data_old = NULL; + } + + return rc; +} diff --git a/lib/ipmi_fwum.c b/lib/ipmi_fwum.c new file mode 100644 index 0000000..b666a2b --- /dev/null +++ b/lib/ipmi_fwum.c @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2004 Kontron Canada, Inc. All Rights Reserved. + * + * Base on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <math.h> +#include <time.h> +#include <unistd.h> + +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_fwum.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> + +extern int verbose; +unsigned char firmBuf[1024*512]; +tKFWUM_SaveFirmwareInfo save_fw_nfo; + +int KfwumGetFileSize(const char *pFileName, + unsigned long *pFileSize); +int KfwumSetupBuffersFromFile(const char *pFileName, + unsigned long fileSize); +void KfwumShowProgress(const char *task, unsigned long current, + unsigned long total); +unsigned short KfwumCalculateChecksumPadding(unsigned char *pBuffer, + unsigned long totalSize); +int KfwumGetInfo(struct ipmi_intf *intf, unsigned char output, + unsigned char *pNumBank); +int KfwumGetDeviceInfo(struct ipmi_intf *intf, + unsigned char output, tKFWUM_BoardInfo *pBoardInfo); +int KfwumGetStatus(struct ipmi_intf *intf); +int KfwumManualRollback(struct ipmi_intf *intf); +int KfwumStartFirmwareImage(struct ipmi_intf *intf, + unsigned long length, unsigned short padding); +int KfwumSaveFirmwareImage(struct ipmi_intf *intf, + unsigned char sequenceNumber, unsigned long address, + unsigned char *pFirmBuf, unsigned char *pInBufLength); +int KfwumFinishFirmwareImage(struct ipmi_intf *intf, + tKFWUM_InFirmwareInfo firmInfo); +int KfwumUploadFirmware(struct ipmi_intf *intf, + unsigned char *pBuffer, unsigned long totalSize); +int KfwumStartFirmwareUpgrade(struct ipmi_intf *intf); +int KfwumGetInfoFromFirmware(unsigned char *pBuf, + unsigned long bufSize, tKFWUM_InFirmwareInfo *pInfo); +void KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo *pInfo); +int KfwumGetTraceLog(struct ipmi_intf *intf); +int ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo); + +int ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action); +int ipmi_fwum_info(struct ipmi_intf *intf); +int ipmi_fwum_status(struct ipmi_intf *intf); +void printf_kfwum_help(void); +void printf_kfwum_info(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo); + +/* String table */ +/* Must match eFWUM_CmdId */ +const char *CMD_ID_STRING[] = { + "GetFwInfo", + "KickWatchdog", + "GetLastAnswer", + "BootHandshake", + "ReportStatus", + "CtrlIPMBLine", + "SetFwState", + "GetFwStatus", + "GetSpiMemStatus", + "StartFwUpdate", + "StartFwImage", + "SaveFwImage", + "FinishFwImage", + "ReadFwImage", + "ManualRollback", + "GetTraceLog" +}; + +const char *EXT_CMD_ID_STRING[] = { + "FwUpgradeLock", + "ProcessFwUpg", + "ProcessFwRb", + "WaitHSAfterUpg", + "WaitFirstHSUpg", + "FwInfoStateChange" +}; + +const char *CMD_STATE_STRING[] = { + "Invalid", + "Begin", + "Progress", + "Completed" +}; + +const struct valstr bankStateValS[] = { + { 0x00, "Not programmed" }, + { 0x01, "New firmware" }, + { 0x02, "Wait for validation" }, + { 0x03, "Last Known Good" }, + { 0x04, "Previous Good" } +}; + +/* ipmi_fwum_main - entry point for this ipmitool mode + * + * @intf: ipmi interface + * @arc: number of arguments + * @argv: point to argument array + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_fwum_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = 0; + printf("FWUM extension Version %d.%d\n", VER_MAJOR, VER_MINOR); + if (argc < 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_kfwum_help(); + return (-1); + } + if (strncmp(argv[0], "help", 4) == 0) { + printf_kfwum_help(); + rc = 0; + } else if (strncmp(argv[0], "info", 4) == 0) { + rc = ipmi_fwum_info(intf); + } else if (strncmp(argv[0], "status", 6) == 0) { + rc = ipmi_fwum_status(intf); + } else if (strncmp(argv[0], "rollback", 8) == 0) { + rc = KfwumManualRollback(intf); + } else if (strncmp(argv[0], "download", 8) == 0) { + if ((argc < 2) || (strlen(argv[1]) < 1)) { + lprintf(LOG_ERR, + "Path and file name must be specified."); + return (-1); + } + printf("Firmware File Name : %s\n", argv[1]); + rc = ipmi_fwum_fwupgrade(intf, argv[1], 0); + } else if (strncmp(argv[0], "upgrade", 7) == 0) { + if ((argc >= 2) && (strlen(argv[1]) > 0)) { + printf("Upgrading using file name %s\n", argv[1]); + rc = ipmi_fwum_fwupgrade(intf, argv[1], 1); + } else { + rc = KfwumStartFirmwareUpgrade(intf); + } + } else if (strncmp(argv[0], "tracelog", 8) == 0) { + rc = KfwumGetTraceLog(intf); + } else { + lprintf(LOG_ERR, "Invalid KFWUM command: %s", argv[0]); + printf_kfwum_help(); + rc = (-1); + } + return rc; +} + +void +printf_kfwum_help(void) +{ + lprintf(LOG_NOTICE, +"KFWUM Commands: info status download upgrade rollback tracelog"); +} + +/* private definitions and macros */ +typedef enum eFWUM_CmdId +{ + KFWUM_CMD_ID_GET_FIRMWARE_INFO = 0, + KFWUM_CMD_ID_KICK_IPMC_WATCHDOG = 1, + KFWUM_CMD_ID_GET_LAST_ANSWER = 2, + KFWUM_CMD_ID_BOOT_HANDSHAKE = 3, + KFWUM_CMD_ID_REPORT_STATUS = 4, + KFWUM_CMD_ID_GET_FIRMWARE_STATUS = 7, + KFWUM_CMD_ID_START_FIRMWARE_UPDATE = 9, + KFWUM_CMD_ID_START_FIRMWARE_IMAGE = 0x0a, + KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE = 0x0b, + KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE = 0x0c, + KFWUM_CMD_ID_READ_FIRMWARE_IMAGE = 0x0d, + KFWUM_CMD_ID_MANUAL_ROLLBACK = 0x0e, + KFWUM_CMD_ID_GET_TRACE_LOG = 0x0f, + KFWUM_CMD_ID_STD_MAX_CMD, + KFWUM_CMD_ID_EXTENDED_CMD = 0xC0 +} tKFWUM_CmdId; + +int +ipmi_fwum_info(struct ipmi_intf *intf) +{ + tKFWUM_BoardInfo b_info; + int rc = 0; + unsigned char not_used; + if (verbose) { + printf("Getting Kontron FWUM Info\n"); + } + if (KfwumGetDeviceInfo(intf, 1, &b_info) != 0) { + rc = (-1); + } + if (KfwumGetInfo(intf, 1, ¬_used) != 0) { + rc = (-1); + } + return rc; +} + +int +ipmi_fwum_status(struct ipmi_intf *intf) +{ + if (verbose) { + printf("Getting Kontron FWUM Status\n"); + } + if (KfwumGetStatus(intf) != 0) { + return (-1); + } + return 0; +} + +/* ipmi_fwum_fwupgrade - function implements download/upload of the firmware + * data received as parameters + * + * @file: fw file + * @action: 0 = download, 1 = upload/start upload + * + * returns 0 on success, otherwise (-1) + */ +int +ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action) +{ + tKFWUM_BoardInfo b_info; + tKFWUM_InFirmwareInfo fw_info = { 0 }; + unsigned short padding; + unsigned long fsize = 0; + unsigned char not_used; + if (file == NULL) { + lprintf(LOG_ERR, "No file given."); + return (-1); + } + if (KfwumGetFileSize(file, &fsize) != 0) { + return (-1); + } + if (KfwumSetupBuffersFromFile(file, fsize) != 0) { + return (-1); + } + padding = KfwumCalculateChecksumPadding(firmBuf, fsize); + if (KfwumGetInfoFromFirmware(firmBuf, fsize, &fw_info) != 0) { + return (-1); + } + if (KfwumGetDeviceInfo(intf, 0, &b_info) != 0) { + return (-1); + } + if (ipmi_kfwum_checkfwcompat(b_info, fw_info) != 0) { + return (-1); + } + KfwumGetInfo(intf, 0, ¬_used); + printf_kfwum_info(b_info, fw_info); + if (KfwumStartFirmwareImage(intf, fsize, padding) != 0) { + return (-1); + } + if (KfwumUploadFirmware(intf, firmBuf, fsize) != 0) { + return (-1); + } + if (KfwumFinishFirmwareImage(intf, fw_info) != 0) { + return (-1); + } + if (KfwumGetStatus(intf) != 0) { + return (-1); + } + if (action != 0) { + if (KfwumStartFirmwareUpgrade(intf) != 0) { + return (-1); + } + } + return 0; +} + +/* KfwumGetFileSize - gets the file size + * + * @pFileName : filename ptr + * @pFileSize : output ptr for filesize + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetFileSize(const char *pFileName, unsigned long *pFileSize) +{ + FILE *pFileHandle = NULL; + pFileHandle = fopen(pFileName, "rb"); + if (pFileHandle == NULL) { + return (-1); + } + if (fseek(pFileHandle, 0L , SEEK_END) == 0) { + *pFileSize = ftell(pFileHandle); + } + fclose(pFileHandle); + if (*pFileSize != 0) { + return 0; + } + return (-1); +} + +/* KfwumSetupBuffersFromFile - small buffers are used to store the file data + * + * @pFileName : filename ptr + * unsigned long : filesize + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumSetupBuffersFromFile(const char *pFileName, unsigned long fileSize) +{ + int rc = (-1); + FILE *pFileHandle = NULL; + int count; + int modulus; + int qty = 0; + + pFileHandle = fopen(pFileName, "rb"); + if (pFileHandle == NULL) { + lprintf(LOG_ERR, "Failed to open '%s' for reading.", + pFileName); + return (-1); + } + count = fileSize / MAX_BUFFER_SIZE; + modulus = fileSize % MAX_BUFFER_SIZE; + + rewind(pFileHandle); + for (qty = 0; qty < count; qty++) { + KfwumShowProgress("Reading Firmware from File", + qty, count); + if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1, + MAX_BUFFER_SIZE, + pFileHandle) == MAX_BUFFER_SIZE) { + rc = 0; + } + } + if (modulus) { + if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1, + modulus, pFileHandle) == modulus) { + rc = 0; + } + } + if (rc == 0) { + KfwumShowProgress("Reading Firmware from File", 100, 100); + } + fclose(pFileHandle); + return rc; +} + +/* KfwumShowProgress - helper routine to display progress bar + * + * Converts current/total in percent + * + * *task : string identifying current operation + * current: progress + * total : limit + */ +void +KfwumShowProgress(const char *task, unsigned long current, unsigned long total) +{ +# define PROG_LENGTH 42 + static unsigned long staticProgress=0xffffffff; + unsigned char spaces[PROG_LENGTH + 1]; + unsigned short hash; + float percent = ((float)current / total); + unsigned long progress = 100 * (percent); + + if (staticProgress == progress) { + /* We displayed the same last time.. so don't do it */ + return; + } + staticProgress = progress; + printf("%-25s : ", task); /* total 20 bytes */ + hash = (percent * PROG_LENGTH); + memset(spaces, '#', hash); + spaces[hash] = '\0'; + + printf("%s", spaces); + memset(spaces, ' ', (PROG_LENGTH - hash)); + spaces[(PROG_LENGTH - hash)] = '\0'; + printf("%s", spaces ); + + printf(" %3ld %%\r", progress); /* total 7 bytes */ + if (progress == 100) { + printf("\n"); + } + fflush(stdout); +} + +/* KfwumCalculateChecksumPadding - TBD + */ +unsigned short +KfwumCalculateChecksumPadding(unsigned char *pBuffer, unsigned long totalSize) +{ + unsigned short sumOfBytes = 0; + unsigned short padding; + unsigned long counter; + for (counter = 0; counter < totalSize; counter ++) { + sumOfBytes += pBuffer[counter]; + } + padding = 0 - sumOfBytes; + return padding; +} + +/* KfwumGetInfo - Get Firmware Update Manager (FWUM) information + * + * *intf : IPMI interface + * output : when set to non zero, queried information is displayed + * pNumBank: output ptr for number of banks + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetInfo(struct ipmi_intf *intf, unsigned char output, + unsigned char *pNumBank) +{ + int rc = 0; + static struct KfwumGetInfoResp *pGetInfo; + struct ipmi_rs *rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_INFO; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + lprintf(LOG_ERR, "Error in FWUM Firmware Get Info Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "FWUM Firmware Get Info returned %x", + rsp->ccode); + return (-1); + } + + pGetInfo = (struct KfwumGetInfoResp *)rsp->data; + if (output) { + printf("\nFWUM info\n"); + printf("=========\n"); + printf("Protocol Revision : %02Xh\n", + pGetInfo->protocolRevision); + printf("Controller Device Id : %02Xh\n", + pGetInfo->controllerDeviceId); + printf("Firmware Revision : %u.%u%u", + pGetInfo->firmRev1, pGetInfo->firmRev2 >> 4, + pGetInfo->firmRev2 & 0x0f); + if (pGetInfo->byte.mode != 0) { + printf(" - DEBUG BUILD\n"); + } else { + printf("\n"); + } + printf("Number Of Memory Bank : %u\n", pGetInfo->numBank); + } + *pNumBank = pGetInfo->numBank; + /* Determine wich type of download to use: */ + /* Old FWUM or Old IPMC fw (data_len < 7) + * --> Address with small buffer size + */ + if ((pGetInfo->protocolRevision) <= 0x05 || (rsp->data_len < 7 )) { + save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_ADDRESS; + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + save_fw_nfo.overheadSize = KFWUM_OLD_CMD_OVERHEAD; + if (verbose) { + printf("Protocol Revision :"); + printf(" <= 5 detected, adjusting buffers\n"); + } + } else { + /* Both fw are using the new protocol */ + save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_SEQUENCE; + save_fw_nfo.overheadSize = KFWUM_NEW_CMD_OVERHEAD; + /* Buffer size depending on access type (Local or remote) */ + /* Look if we run remote or locally */ + if (verbose) { + printf("Protocol Revision :"); + printf(" > 5 optimizing buffers\n"); + } + if (strstr(intf->name,"lan") != NULL) { + /* also covers lanplus */ + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + if (verbose) { + printf("IOL payload size : %d\n", + save_fw_nfo.bufferSize); + } + } else if ((strstr(intf->name,"open")!= NULL) + && intf->target_addr != IPMI_BMC_SLAVE_ADDR + && (intf->target_addr != intf->my_addr)) { + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + if (verbose) { + printf("IPMB payload size : %d\n", + save_fw_nfo.bufferSize); + } + } else { + save_fw_nfo.bufferSize = KFWUM_BIG_BUFFER; + if (verbose) { + printf("SMI payload size : %d\n", + save_fw_nfo.bufferSize); + } + } + } + return rc; +} + +/* KfwumGetDeviceInfo - Get IPMC/Board information + * + * *intf: IPMI interface + * output: when set to non zero, queried information is displayed + * tKFWUM_BoardInfo: output ptr for IPMC/Board information + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetDeviceInfo(struct ipmi_intf *intf, unsigned char output, + tKFWUM_BoardInfo *pBoardInfo) +{ + struct ipm_devid_rsp *pGetDevId; + struct ipmi_rs *rsp; + struct ipmi_rq req; + /* Send Get Device Id */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get Device Id Command"); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Get Device Id returned %x", + rsp->ccode); + return (-1); + } + pGetDevId = (struct ipm_devid_rsp *)rsp->data; + pBoardInfo->iana = IPM_DEV_MANUFACTURER_ID(pGetDevId->manufacturer_id); + pBoardInfo->boardId = buf2short(pGetDevId->product_id); + if (output) { + printf("\nIPMC Info\n"); + printf("=========\n"); + printf("Manufacturer Id : %u\n", + pBoardInfo->iana); + printf("Board Id : %u\n", + pBoardInfo->boardId); + printf("Firmware Revision : %u.%u%u", + pGetDevId->fw_rev1, pGetDevId->fw_rev2 >> 4, + pGetDevId->fw_rev2 & 0x0f); + if (((pBoardInfo->iana == IPMI_OEM_KONTRON) + && (pBoardInfo->boardId = KFWUM_BOARD_KONTRON_5002))) { + printf(" SDR %u", pGetDevId->aux_fw_rev[0]); + } + printf("\n"); + } + return 0; +} + +/* KfwumGetStatus - Get (and prints) FWUM banks information + * + * *intf : IPMI interface + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetStatus(struct ipmi_intf * intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumGetStatusResp *pGetStatus; + unsigned char numBank; + unsigned char counter; + unsigned long firmLength; + if (verbose) { + printf(" Getting Status!\n"); + } + /* Retreive the number of bank */ + rc = KfwumGetInfo(intf, 0, &numBank); + for(counter = 0; + (counter < numBank) && (rc == 0); + counter ++) { + /* Retreive the status of each bank */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_STATUS; + req.msg.data = &counter; + req.msg.data_len = 1; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Get Status Command."); + rc = (-1); + break; + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Get Status returned %x", + rsp->ccode); + rc = (-1); + break; + } + pGetStatus = (struct KfwumGetStatusResp *) rsp->data; + printf("\nBank State %d : %s\n", + counter, + val2str(pGetStatus->bankState, bankStateValS)); + if (!pGetStatus->bankState) { + continue; + } + firmLength = pGetStatus->firmLengthMSB; + firmLength = firmLength << 8; + firmLength |= pGetStatus->firmLengthMid; + firmLength = firmLength << 8; + firmLength |= pGetStatus->firmLengthLSB; + printf("Firmware Length : %ld bytes\n", + firmLength); + printf("Firmware Revision : %u.%u%u SDR %u\n", + pGetStatus->firmRev1, + pGetStatus->firmRev2 >> 4, + pGetStatus->firmRev2 & 0x0f, + pGetStatus->firmRev3); + } + printf("\n"); + return rc; +} + +/* KfwumManualRollback - Ask IPMC to rollback to previous version + * + * *intf : IPMI interface + * + * returns 0 on success + * returns (-1) on error + */ +int +KfwumManualRollback(struct ipmi_intf *intf) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumManualRollbackReq thisReq; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_MANUAL_ROLLBACK; + thisReq.type = 0; /* Wait BMC shutdown */ + req.msg.data = (unsigned char *)&thisReq; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in FWUM Manual Rollback Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "Error in FWUM Manual Rollback Command returned %x", + rsp->ccode); + return (-1); + } + printf("FWUM Starting Manual Rollback \n"); + return 0; +} + +int +KfwumStartFirmwareImage(struct ipmi_intf *intf, unsigned long length, + unsigned short padding) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumStartFirmwareDownloadResp *pResp; + struct KfwumStartFirmwareDownloadReq thisReq; + + thisReq.lengthLSB = length & 0x000000ff; + thisReq.lengthMid = (length >> 8) & 0x000000ff; + thisReq.lengthMSB = (length >> 16) & 0x000000ff; + thisReq.paddingLSB = padding & 0x00ff; + thisReq.paddingMSB = (padding>> 8) & 0x00ff; + thisReq.useSequence = 0x01; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_IMAGE; + req.msg.data = (unsigned char *) &thisReq; + /* Look for download type */ + if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) { + req.msg.data_len = 5; + } else { + req.msg.data_len = 6; + } + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Start Firmware Image Download Command."); + return (-1); + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Start Firmware Image Download returned %x", + rsp->ccode); + return (-1); + } + pResp = (struct KfwumStartFirmwareDownloadResp *)rsp->data; + printf("Bank holding new firmware : %d\n", pResp->bank); + sleep(5); + return 0; +} + +int +KfwumSaveFirmwareImage(struct ipmi_intf *intf, unsigned char sequenceNumber, + unsigned long address, unsigned char *pFirmBuf, + unsigned char *pInBufLength) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumSaveFirmwareAddressReq addr_req; + struct KfwumSaveFirmwareSequenceReq seq_req; + int retry = 0; + int no_rsp = 0; + do { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE; + if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) { + addr_req.addressLSB = address & 0x000000ff; + addr_req.addressMid = (address >> 8) & 0x000000ff; + addr_req.addressMSB = (address >> 16) & 0x000000ff; + addr_req.numBytes = *pInBufLength; + memcpy(addr_req.txBuf, pFirmBuf, *pInBufLength); + req.msg.data = (unsigned char *)&addr_req; + req.msg.data_len = *pInBufLength + 4; + } else { + seq_req.sequenceNumber = sequenceNumber; + memcpy(seq_req.txBuf, pFirmBuf, *pInBufLength); + req.msg.data = (unsigned char *)&seq_req; + req.msg.data_len = *pInBufLength + sizeof(unsigned char); + /* + 1 => sequenceNumber*/ + } + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Save Firmware Image Download Command."); + /* We don't receive "C7" on errors with IOL, + * instead we receive nothing + */ + if (strstr(intf->name, "lan") != NULL) { + no_rsp++; + if (no_rsp < FWUM_SAVE_FIRMWARE_NO_RESPONSE_LIMIT) { + *pInBufLength -= 1; + continue; + } + lprintf(LOG_ERR, + "Error, too many commands without response."); + *pInBufLength = 0; + break; + } /* For other interface keep trying */ + } else if (rsp->ccode != 0) { + if (rsp->ccode == 0xc0) { + sleep(1); + } else if ((rsp->ccode == 0xc7) + || ((rsp->ccode == 0xc3) + && (sequenceNumber == 0))) { + *pInBufLength -= 1; + retry = 1; + } else if (rsp->ccode == 0x82) { + /* Double sent, continue */ + rc = 0; + break; + } else if (rsp->ccode == 0x83) { + if (retry == 0) { + retry = 1; + continue; + } + rc = (-1); + break; + } else if (rsp->ccode == 0xcf) { + /* Ok if receive duplicated request */ + retry = 1; + } else if (rsp->ccode == 0xc3) { + if (retry == 0) { + retry = 1; + continue; + } + rc = (-1); + break; + } else { + lprintf(LOG_ERR, + "FWUM Firmware Save Firmware Image Download returned %x", + rsp->ccode); + rc = (-1); + break; + } + } else { + break; + } + } while (1); + return rc; +} + +int +KfwumFinishFirmwareImage(struct ipmi_intf *intf, tKFWUM_InFirmwareInfo firmInfo) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumFinishFirmwareDownloadReq thisReq; + + thisReq.versionMaj = firmInfo.versMajor; + thisReq.versionMinSub = ((firmInfo.versMinor <<4) + | firmInfo.versSubMinor); + thisReq.versionSdr = firmInfo.sdrRev; + thisReq.reserved = 0; + /* Byte 4 reserved, write 0 */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE; + req.msg.data = (unsigned char *)&thisReq; + req.msg.data_len = 4; + /* Infinite loop if BMC doesn't reply or replies 0xc0 every time. */ + do { + rsp = intf->sendrecv(intf, &req); + } while (rsp == NULL || rsp->ccode == 0xc0); + if (!rsp) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Finish Firmware Image Download Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "FWUM Firmware Finish Firmware Image Download returned %x", + rsp->ccode); + return (-1); + } + return 0; +} + +int +KfwumUploadFirmware(struct ipmi_intf *intf, unsigned char *pBuffer, + unsigned long totalSize) +{ + int rc = (-1); + unsigned long address = 0x0; + unsigned char writeSize; + unsigned char oldWriteSize; + unsigned long lastAddress = 0; + unsigned char sequenceNumber = 0; + unsigned char retry = FWUM_MAX_UPLOAD_RETRY; + unsigned char isLengthValid = 1; + do { + writeSize = save_fw_nfo.bufferSize - save_fw_nfo.overheadSize; + /* Reach the end */ + if (address + writeSize > totalSize) { + writeSize = (totalSize - address); + } else if (((address % KFWUM_PAGE_SIZE) + + writeSize) > KFWUM_PAGE_SIZE) { + /* Reach boundary end */ + writeSize = (KFWUM_PAGE_SIZE - (address % KFWUM_PAGE_SIZE)); + } + oldWriteSize = writeSize; + rc = KfwumSaveFirmwareImage(intf, sequenceNumber, + address, &pBuffer[address], &writeSize); + if ((rc != 0) && (retry-- != 0)) { + address = lastAddress; + rc = 0; + } else if ( writeSize == 0) { + rc = (-1); + } else { + if (writeSize != oldWriteSize) { + printf("Adjusting length to %d bytes \n", + writeSize); + save_fw_nfo.bufferSize -= (oldWriteSize - writeSize); + } + retry = FWUM_MAX_UPLOAD_RETRY; + lastAddress = address; + address+= writeSize; + } + if (rc == 0) { + if ((address % 1024) == 0) { + KfwumShowProgress("Writting Firmware in Flash", + address, totalSize); + } + sequenceNumber++; + } + } while ((rc == 0) && (address < totalSize)); + if (rc == 0) { + KfwumShowProgress("Writting Firmware in Flash", + 100, 100); + } + return rc; +} + +int +KfwumStartFirmwareUpgrade(struct ipmi_intf *intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + /* Upgrade type, wait BMC shutdown */ + unsigned char upgType = 0 ; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_UPDATE; + req.msg.data = (unsigned char *) &upgType; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Start Firmware Upgrade Command"); + rc = (-1); + } else if (rsp->ccode) { + if (rsp->ccode == 0xd5) { + lprintf(LOG_ERR, + "No firmware available for upgrade. Download Firmware first."); + } else { + lprintf(LOG_ERR, + "FWUM Firmware Start Firmware Upgrade returned %x", + rsp->ccode); + } + rc = (-1); + } + return rc; +} + +int +KfwumGetTraceLog(struct ipmi_intf *intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + unsigned char chunkIdx; + unsigned char cmdIdx; + if (verbose) { + printf(" Getting Trace Log!\n"); + } + for (chunkIdx = 0; + (chunkIdx < TRACE_LOG_CHUNK_COUNT) + && (rc == 0); + chunkIdx++) { + /* Retreive each log chunk and print it */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_TRACE_LOG; + req.msg.data = &chunkIdx; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Get Trace Log Command"); + rc = (-1); + break; + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Get Trace Log returned %x", + rsp->ccode); + rc = (-1); + break; + } + for (cmdIdx=0; cmdIdx < TRACE_LOG_CHUNK_SIZE; cmdIdx++) { + /* Don't diplay commands with an invalid state */ + if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0) + && (rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] < KFWUM_CMD_ID_STD_MAX_CMD)) { + printf(" Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n", + CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx]], + CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]], + rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]); + } else if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0) + && (rsp->data[TRACE_LOG_ATT_COUNT*cmdIdx] >= KFWUM_CMD_ID_EXTENDED_CMD)) { + printf(" Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n", + EXT_CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] - KFWUM_CMD_ID_EXTENDED_CMD], + CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]], + rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]); + } + } + } + printf("\n"); + return rc; +} + +int +KfwumGetInfoFromFirmware(unsigned char *pBuf, unsigned long bufSize, + tKFWUM_InFirmwareInfo *pInfo) +{ + unsigned long offset = 0; + if (bufSize < (IN_FIRMWARE_INFO_OFFSET_LOCATION + IN_FIRMWARE_INFO_SIZE)) { + return (-1); + } + offset = IN_FIRMWARE_INFO_OFFSET_LOCATION; + + /* Now, fill the structure with read informations */ + pInfo->checksum = (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + 0 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM ) << 8; + + pInfo->checksum|= (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + 1 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM); + + pInfo->sumToRemoveFromChecksum = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM); + + pInfo->sumToRemoveFromChecksum+= KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM + 1); + + pInfo->fileSize = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 0) << 24; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 1) << 16; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 2) << 8; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 3); + + pInfo->boardId = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 0) << 8; + + pInfo->boardId|= KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 1); + + pInfo->deviceId = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_DEVICE_ID); + + pInfo->tableVers = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_TABLE_VERSION); + + pInfo->implRev = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IMPLEMENT_REV); + + pInfo->versMajor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + + IN_FIRMWARE_INFO_OFFSET_VER_MAJOROR)) & 0x0f; + + pInfo->versMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + + IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB) >> 4) & 0x0f; + + pInfo->versSubMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB)) & 0x0f; + + pInfo->sdrRev = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_SDR_REV); + + pInfo->iana = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA2) << 16; + + pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA1) << 8; + + pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA0); + + KfwumFixTableVersionForOldFirmware(pInfo); + return 0; +} + +void +KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo * pInfo) +{ + switch(pInfo->boardId) { + case KFWUM_BOARD_KONTRON_UNKNOWN: + pInfo->tableVers = 0xff; + break; + default: + /* pInfo->tableVers is already set for + * the right version + */ + break; + } +} + +/* ipmi_kfwum_checkfwcompat - check whether firmware we're about to upload is + * compatible with board. + * + * @boardInfo: + * @firmInfo: + * + * returns 0 if compatible, otherwise (-1) + */ +int +ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo) +{ + int compatible = 0; + if (boardInfo.iana != firmInfo.iana) { + lprintf(LOG_ERR, + "Board IANA does not match firmware IANA."); + compatible = (-1); + } + if (boardInfo.boardId != firmInfo.boardId) { + lprintf(LOG_ERR, + "Board IANA does not match firmware IANA."); + compatible = (-1); + } + if (compatible != 0) { + lprintf(LOG_ERR, + "Firmware invalid for target board. Download of upgrade aborted."); + } + return compatible; +} + +void +printf_kfwum_info(tKFWUM_BoardInfo boardInfo, tKFWUM_InFirmwareInfo firmInfo) +{ + printf( +"Target Board Id : %u\n", boardInfo.boardId); + printf( +"Target IANA number : %u\n", boardInfo.iana); + printf( +"File Size : %lu bytes\n", firmInfo.fileSize); + printf( +"Firmware Version : %d.%d%d SDR %d\n", firmInfo.versMajor, +firmInfo.versMinor, firmInfo.versSubMinor, firmInfo.sdrRev); +} diff --git a/lib/ipmi_gendev.c b/lib/ipmi_gendev.c new file mode 100644 index 0000000..7a4cf08 --- /dev/null +++ b/lib/ipmi_gendev.c @@ -0,0 +1,640 @@ +/* + * Copyright (c) 2003 Kontron Canada, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> + +#include <math.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <time.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_gendev.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_entity.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_raw.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +extern int verbose; + + +#define GENDEV_RETRY_COUNT 5 +#define GENDEV_MAX_SIZE 16 + +typedef struct gendev_eeprom_info +{ + uint32_t size; + uint16_t page_size; + uint8_t address_span; + uint8_t address_length; +}t_gendev_eeprom_info; + + +static int +ipmi_gendev_get_eeprom_size( + struct ipmi_intf *intf, + struct sdr_record_generic_locator *dev, + t_gendev_eeprom_info *info + ) +{ + int eeprom_size = 0; + /* + lprintf(LOG_ERR, "Gen Device : %s", dev->id_string); + lprintf(LOG_ERR, "Access Addr: %x", dev->dev_access_addr); + lprintf(LOG_ERR, "Slave Addr : %x", dev->dev_slave_addr); + lprintf(LOG_ERR, "Channel Num: %x", dev->channel_num); + lprintf(LOG_ERR, "Lun : %x", dev->lun); + lprintf(LOG_ERR, "Bus : %x", dev->bus); + lprintf(LOG_ERR, "Addr Span : %x", dev->addr_span); + lprintf(LOG_ERR, "DevType : %x", dev->dev_type); + lprintf(LOG_ERR, "DevType Mod: %x", dev->dev_type_modifier); + */ + if( info != NULL) + { + switch(dev->dev_type) + { + case 0x08: // 24C01 + info->size = 128; + info->page_size = 8; + info->address_span = dev->addr_span; + info->address_length = 1; + break; + case 0x09: // 24C02 + info->size = 256; + info->page_size = 8; + info->address_span = dev->addr_span; + info->address_length = 1; + break; + case 0x0A: // 24C04 + info->size = 512; + info->page_size = 8; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0x0B: // 24C08 + info->size = 1024; + info->page_size = 8; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0x0C: // 24C16 + info->size = 2048; + info->page_size = 256; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0x0D: // 24C17 + info->size = 2048; + info->page_size = 256; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0x0E: // 24C32 + info->size = 4096; + info->page_size = 8; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0x0F: // 24C64 + info->size = 8192; + info->page_size = 32; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0xC0: // Proposed OEM Code for 24C128 + info->size = 16384; + info->page_size = 64; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0xC1: // Proposed OEM Code for 24C256 + info->size = 32748; + info->page_size = 64; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0xC2: // Proposed OEM Code for 24C512 + info->size = 65536; + info->page_size = 128; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + case 0xC3: // Proposed OEM Code for 24C1024 + info->size = 131072; + info->page_size = 128; + info->address_span = dev->addr_span; + info->address_length = 2; + break; + /* Please reserved up to CFh for future update */ + default: // Not a eeprom, return size = 0; + info->size = 0; + info->page_size = 0; + info->address_span = 0; + info->address_length = 0; + break; + } + + eeprom_size = info->size; + } + + return eeprom_size; +} + + + +static int +ipmi_gendev_read_file( + struct ipmi_intf *intf, + struct sdr_record_generic_locator *dev, + const char *ofile + ) +{ + int rc = 0; + int eeprom_size; + t_gendev_eeprom_info eeprom_info; + + eeprom_size = ipmi_gendev_get_eeprom_size(intf, dev, &eeprom_info); + + if(eeprom_size > 0) + { + FILE *fp; + + /* now write to file */ + fp = ipmi_open_file_write(ofile); + + if(fp) + { + struct ipmi_rs *rsp; + int numWrite; + uint32_t counter; + uint8_t msize; + uint8_t channel = dev->channel_num; + uint8_t i2cbus = dev->bus; + uint8_t i2caddr = dev->dev_slave_addr; + uint8_t privatebus = 1; + uint32_t address_span_size; + uint8_t percentCompleted = 0; + + + /* Handle Address Span */ + if( eeprom_info.address_span != 0) + { + address_span_size = + (eeprom_info.size / (eeprom_info.address_span+1)); + } + else + { + address_span_size = eeprom_info.size; + } + + /* Setup read/write size */ + if( eeprom_info.page_size < GENDEV_MAX_SIZE) + { + msize = eeprom_info.page_size; + } + else + { + msize = GENDEV_MAX_SIZE; + // All eeprom with page higher than 32 is on the + // 16 bytes boundary + } + + /* Setup i2c bus byte */ + i2cbus = ((channel & 0xF) << 4) | ((i2cbus & 7) << 1) | privatebus; + +/* + lprintf(LOG_ERR, "Generic device: %s", dev->id_string); + lprintf(LOG_ERR, "I2C Chnl: %x", channel); + lprintf(LOG_ERR, "I2C Bus : %x", i2cbus); + lprintf(LOG_ERR, "I2C Addr: %x", i2caddr); */ + + for ( + counter = 0; + (counter < (eeprom_info.size)) && (rc == 0); + counter+= msize + ) + { + uint8_t retryCounter; + + for( + retryCounter = 0; + retryCounter<GENDEV_RETRY_COUNT; + retryCounter ++ + ) + { + uint8_t wrByte[GENDEV_MAX_SIZE+2]; + + wrByte[0] = (uint8_t) (counter>>0); + if(eeprom_info.address_length > 1) + { + wrByte[1] = (uint8_t) (counter>>8); + } + + i2caddr+= (((eeprom_info.size) % address_span_size) * 2); + + rsp = ipmi_master_write_read( + intf, + i2cbus, + i2caddr, + (uint8_t *) wrByte, + eeprom_info.address_length, + msize + ); + + if (rsp != NULL) + { + retryCounter = GENDEV_RETRY_COUNT; + rc = 0; + } + else if(retryCounter < GENDEV_RETRY_COUNT) + { + retryCounter ++; + lprintf(LOG_ERR, "Retry"); + sleep(1); + rc = -1; + } + else + { + lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read"); + rc = -1; + } + } + + if( rc == 0 ) + { + static uint8_t previousCompleted = 101; + numWrite = fwrite(rsp->data, 1, msize, fp); + if (numWrite != msize) + { + lprintf(LOG_ERR, "Error writing file %s", ofile); + rc = -1; + break; + } + + percentCompleted = ((counter * 100) / eeprom_info.size ); + + if(percentCompleted != previousCompleted) + { + printf("\r%i percent completed", percentCompleted); + previousCompleted = percentCompleted; + } + + + } + } + if(counter == (eeprom_info.size)) + { + printf("\r%%100 percent completed\n"); + } + else + { + printf("\rError: %i percent completed, read not completed \n", percentCompleted); + } + + fclose(fp); + } + } + else + { + lprintf(LOG_ERR, "The selected generic device is not an eeprom"); + } + + return rc; +} + + +/* ipmi_gendev_write_file - Read raw SDR from binary file + * + * used for writing generic locator device Eeprom type + * + * @intf: ipmi interface + * @dev: generic device to read + * @ofile: output filename + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_gendev_write_file( + struct ipmi_intf *intf, + struct sdr_record_generic_locator *dev, + const char *ofile + ) +{ + int rc = 0; + int eeprom_size; + t_gendev_eeprom_info eeprom_info; + + eeprom_size = ipmi_gendev_get_eeprom_size(intf, dev, &eeprom_info); + + if(eeprom_size > 0) + { + FILE *fp; + uint32_t fileLength = 0; + + /* now write to file */ + fp = ipmi_open_file_read(ofile); + + if(fp) + { + /* Retreive file length, check if it's fits the Eeprom Size */ + fseek(fp, 0 ,SEEK_END); + fileLength = ftell(fp); + + lprintf(LOG_ERR, "File Size: %i", fileLength); + lprintf(LOG_ERR, "Eeprom Size: %i", eeprom_size); + if(fileLength != eeprom_size) + { + lprintf(LOG_ERR, "File size does not fit Eeprom Size"); + fclose(fp); + fp = NULL; + } + else + { + fseek(fp, 0 ,SEEK_SET); + } + } + + if(fp) + { + struct ipmi_rs *rsp; + int numRead; + uint32_t counter; + uint8_t msize; + uint8_t channel = dev->channel_num; + uint8_t i2cbus = dev->bus; + uint8_t i2caddr = dev->dev_slave_addr; + uint8_t privatebus = 1; + uint32_t address_span_size; + uint8_t percentCompleted = 0; + + + /* Handle Address Span */ + if( eeprom_info.address_span != 0) + { + address_span_size = + (eeprom_info.size / (eeprom_info.address_span+1)); + } + else + { + address_span_size = eeprom_info.size; + } + + /* Setup read/write size */ + if( eeprom_info.page_size < GENDEV_MAX_SIZE) + { + msize = eeprom_info.page_size; + } + else + { + msize = GENDEV_MAX_SIZE; + // All eeprom with page higher than 32 is on the + // 16 bytes boundary + } + + /* Setup i2c bus byte */ + i2cbus = ((channel & 0xF) << 4) | ((i2cbus & 7) << 1) | privatebus; + +/* + lprintf(LOG_ERR, "Generic device: %s", dev->id_string); + lprintf(LOG_ERR, "I2C Chnl: %x", channel); + lprintf(LOG_ERR, "I2C Bus : %x", i2cbus); + lprintf(LOG_ERR, "I2C Addr: %x", i2caddr); */ + + for ( + counter = 0; + (counter < (eeprom_info.size)) && (rc == 0); + counter+= msize + ) + { + uint8_t retryCounter; + uint8_t readByte[GENDEV_MAX_SIZE]; + + numRead = fread(readByte, 1, msize, fp); + if (numRead != msize) + { + lprintf(LOG_ERR, "Error reading file %s", ofile); + rc = -1; + break; + } + + + + for( + retryCounter = 0; + retryCounter<GENDEV_RETRY_COUNT; + retryCounter ++ + ) + { + uint8_t wrByte[GENDEV_MAX_SIZE+2]; + wrByte[0] = (uint8_t) (counter>>0); + if(eeprom_info.address_length > 1) + { + wrByte[1] = (uint8_t) (counter>>8); + } + memcpy(&wrByte[eeprom_info.address_length], readByte, msize); + + i2caddr+= (((eeprom_info.size) % address_span_size) * 2); + + rsp = ipmi_master_write_read(intf, i2cbus, i2caddr, (uint8_t *) wrByte, eeprom_info.address_length+msize, 0); + if (rsp != NULL) + { + retryCounter = GENDEV_RETRY_COUNT; + rc = 0; + } + else if(retryCounter < GENDEV_RETRY_COUNT) + { + retryCounter ++; + lprintf(LOG_ERR, "Retry"); + sleep(1); + rc = -1; + } + else + { + lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read"); + rc = -1; + } + } + + if( rc == 0 ) + { + static uint8_t previousCompleted = 101; + percentCompleted = ((counter * 100) / eeprom_info.size ); + + if(percentCompleted != previousCompleted) + { + printf("\r%i percent completed", percentCompleted); + previousCompleted = percentCompleted; + } + + } + } + if(counter == (eeprom_info.size)) + { + printf("\r%%100 percent completed\n"); + } + else + { + printf("\rError: %i percent completed, read not completed \n", percentCompleted); + } + + fclose(fp); + } + } + else + { + lprintf(LOG_ERR, "The selected generic device is not an eeprom"); + } + + return rc; +} + + +/* ipmi_gendev_main - top-level handler for generic device + * + * @intf: ipmi interface + * @argc: number of arguments + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_gendev_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = 0; + + /* initialize random numbers used later */ + srand(time(NULL)); + + lprintf(LOG_ERR, "Rx gendev command: %s", argv[0]); + + if ( + (argc == 0) + || + (strncmp(argv[0], "help", 4) == 0) + ) + { + lprintf(LOG_ERR, + "SDR Commands: list read write"); + lprintf(LOG_ERR, + " list List All Generic Device Locators"); + lprintf(LOG_ERR, + " read <sdr name> <file> Read to file eeprom specify by Generic Device Locators"); + lprintf(LOG_ERR, + " write <sdr name> <file> Write from file eeprom specify by Generic Device Locators"); + } + else if ( strncmp(argv[0], "list", 4) == 0) + { + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + } + else if (strncmp(argv[0], "read", 4) == 0) + { + if (argc < 3) + lprintf(LOG_ERR, "usage: gendev read <gendev> <filename>"); + else + { + struct sdr_record_list *sdr; + + lprintf(LOG_ERR, "Gendev read sdr name : %s", argv[1]); + + printf("Locating sensor record '%s'...\n", argv[1]); + + /* lookup by sensor name */ + sdr = ipmi_sdr_find_sdr_byid(intf, argv[1]); + if (sdr == NULL) + { + lprintf(LOG_ERR, "Sensor data record not found!"); + return -1; + } + + if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + { + lprintf(LOG_ERR, "Target SDR is not a generic device locator"); + return -1; + } + + lprintf(LOG_ERR, "Gendev read file name: %s", argv[2]); + ipmi_gendev_read_file(intf, sdr->record.genloc, argv[2]); + + } + } + else if (strncmp(argv[0], "write", 5) == 0) + { + if (argc < 3) + lprintf(LOG_ERR, "usage: gendev write <gendev> <filename>"); + else + { + struct sdr_record_list *sdr; + + lprintf(LOG_ERR, "Gendev write sdr name : %s", argv[1]); + + printf("Locating sensor record '%s'...\n", argv[1]); + + /* lookup by sensor name */ + sdr = ipmi_sdr_find_sdr_byid(intf, argv[1]); + if (sdr == NULL) + { + lprintf(LOG_ERR, "Sensor data record not found!"); + return -1; + } + + if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + { + lprintf(LOG_ERR, "Target SDR is not a generic device locator"); + return -1; + } + + lprintf(LOG_ERR, "Gendev write file name: %s", argv[2]); + ipmi_gendev_write_file(intf, sdr->record.genloc, argv[2]); + + } + } + else + { + lprintf(LOG_ERR, "Invalid gendev command: %s", argv[0]); + rc = -1; + } + + return rc; +} diff --git a/lib/ipmi_hpmfwupg.c b/lib/ipmi_hpmfwupg.c new file mode 100644 index 0000000..69950b7 --- /dev/null +++ b/lib/ipmi_hpmfwupg.c @@ -0,0 +1,2624 @@ +/* + * Copyright (c) 2006 Kontron Canada, Inc. All Rights Reserved. + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_hpmfwupg.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/log.h> +#include "../src/plugins/lan/md5.h" +#include <stdio.h> +#include <time.h> +#include <sys/param.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +extern int verbose; + +int HpmfwupgUpgrade(struct ipmi_intf *intf, char *imageFilename, + int activate, int, int); +int HpmfwupgValidateImageIntegrity(struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgPreparationStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option); +int HpmfwupgUpgradeStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option); +int HpmfwupgActivationStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgGetTargetUpgCapabilities(struct ipmi_intf *intf, + struct HpmfwupgGetTargetUpgCapabilitiesCtx *pCtx); +int HpmfwupgGetComponentProperties(struct ipmi_intf *intf, + struct HpmfwupgGetComponentPropertiesCtx *pCtx); +int HpmfwupgQuerySelftestResult(struct ipmi_intf *intf, + struct HpmfwupgQuerySelftestResultCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgQueryRollbackStatus(struct ipmi_intf *intf, + struct HpmfwupgQueryRollbackStatusCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgAbortUpgrade(struct ipmi_intf *intf, + struct HpmfwupgAbortUpgradeCtx *pCtx); +int HpmfwupgInitiateUpgradeAction(struct ipmi_intf *intf, + struct HpmfwupgInitiateUpgradeActionCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgUploadFirmwareBlock(struct ipmi_intf *intf, + struct HpmfwupgUploadFirmwareBlockCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int count, + unsigned int *pOffset, unsigned int *blockLen); +int HpmfwupgFinishFirmwareUpload(struct ipmi_intf *intf, + struct HpmfwupgFinishFirmwareUploadCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option); +int HpmfwupgActivateFirmware(struct ipmi_intf *intf, + struct HpmfwupgActivateFirmwareCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgGetUpgradeStatus(struct ipmi_intf *intf, + struct HpmfwupgGetUpgradeStatusCtx *pCtxstruct, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int silent); +int HpmfwupgManualFirmwareRollback(struct ipmi_intf *intf, + struct HpmfwupgManualFirmwareRollbackCtx *pCtx); +void HpmfwupgPrintUsage(void); +unsigned char HpmfwupgCalculateChecksum(unsigned char *pData, + unsigned int length); +int HpmfwupgGetDeviceId(struct ipmi_intf *intf, + struct ipm_devid_rsp *pGetDevId); +int HpmfwupgGetBufferFromFile(char *imageFilename, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +int HpmfwupgWaitLongDurationCmd(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx); +struct ipmi_rs *HpmfwupgSendCmd(struct ipmi_intf *intf, + struct ipmi_rq req, struct HpmfwupgUpgradeCtx* pFwupgCtx); + + +int HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, + struct HpmfwupgUpgradeCtx* pFwupgCtx, + unsigned char **pImagePtr, + struct ipmi_intf *intf, + int option, + int *pFlagColdReset); + +/* HpmGetuserInput - get input from user + * + * returns TRUE if its Yes or FALSE if its No + */ +int +HpmGetUserInput(char *str) +{ + char userInput[2]; + int ret; + + printf("%s", str); + ret = scanf("%s", userInput); + if (!ret) { + return 1; + } + if (toupper(userInput[0]) == 'Y') { + return 1; + } + return 0; +} + +/* HpmDisplayLine - display the line with the given character + */ +void +HpmDisplayLine(char *s, int n) +{ + while (n--) { + printf ("%c", *s); + } + printf("\n"); +} + +/* HpmDisplayUpgradeHeader - display the Upgrade header information + */ +void +HpmDisplayUpgradeHeader(void) +{ + printf("\n"); + HpmDisplayLine("-", 79); + printf( +"|ID | Name | Versions | %% |\n"); + printf( +"| | | Active | Backup | File | |\n"); + printf( +"|----|-------------|-----------------|-----------------|-----------------|----|\n"); +} + +/* HpmDisplayUpgrade - display the progress of the upgrade; prints the "." + * for every 5% of its completion. + */ +void +HpmDisplayUpgrade(int skip, unsigned int totalSent, + unsigned int displayFWLength, time_t timeElapsed) +{ + int percent; + static int old_percent = -1; + if (skip) { + printf("Skip|\n"); + return; + } + fflush(stdout); + + percent = ((float)totalSent / displayFWLength) * 100; + if (percent != old_percent) { + if (old_percent != -1) { + printf("\b\b\b\b\b"); + } + printf("%3d%%|", percent); + old_percent = percent; + } + if (totalSent == displayFWLength) { + /* Display the time taken to complete the upgrade */ + printf( +"\n| |Upload Time: %02ld:%02ld | Image Size: %7d bytes |\n", + timeElapsed / 60, timeElapsed % 60, totalSent); + old_percent = -1; + } +} + +/* HpmDisplayVersionHeader - display the information about version header + */ +void +HpmDisplayVersionHeader(int mode) +{ + if (mode & IMAGE_VER) { + HpmDisplayLine("-", 74); + printf( +"|ID | Name | Versions |\n"); + printf( +"| | | Active | Backup | File |\n"); + HpmDisplayLine("-", 74); + } else { + HpmDisplayLine("-",74 ); + printf( +"|ID | Name | Versions |\n"); + printf( +"| | | Active | Backup | Deferred |\n"); + HpmDisplayLine("-", 74); + } +} + +/* HpmDisplayVersion - display the version of the image and target + */ +void +HpmDisplayVersion(int mode, VERSIONINFO *pVersion, int upgradable) +{ + /* + * If the cold reset is required then we can display * on it + * so that user is aware that he needs to do payload power + * cycle after upgrade + */ + printf("|%c%c%2d|%-13s|", + pVersion->coldResetRequired ? '*' : ' ', + upgradable ? '^' : ' ', + pVersion->componentId, pVersion->descString); + + if (mode & TARGET_VER) { + if ((pVersion->targetMajor == 0xFF + || (pVersion->targetMajor == 0x7F)) + && pVersion->targetMinor == 0xFF) { + printf(" ---.-- -------- |"); + } else { + printf(" %3d.%02x %02X%02X%02X%02X |", + pVersion->targetMajor, + pVersion->targetMinor, + pVersion->targetAux[0], + pVersion->targetAux[1], + pVersion->targetAux[2], + pVersion->targetAux[3]); + } + if (mode & ROLLBACK_VER) { + if ((pVersion->rollbackMajor == 0xFF + || (pVersion->rollbackMajor == 0x7F)) + && pVersion->rollbackMinor == 0xFF) { + printf(" ---.-- -------- |"); + } else { + printf(" %3d.%02x %02X%02X%02X%02X |", + pVersion->rollbackMajor, + pVersion->rollbackMinor, + pVersion->rollbackAux[0], + pVersion->rollbackAux[1], + pVersion->rollbackAux[2], + pVersion->rollbackAux[3]); + } + } else { + printf(" ---.-- -------- |"); + } + } + if (mode & IMAGE_VER) { + if ((pVersion->imageMajor == 0xFF + || (pVersion->imageMajor == 0x7F)) + && pVersion->imageMinor == 0xFF) { + printf(" ---.-- |"); + } else { + printf(" %3d.%02x %02X%02X%02X%02X |", + pVersion->imageMajor, + pVersion->imageMinor, + pVersion->imageAux[0], + pVersion->imageAux[1], + pVersion->imageAux[2], + pVersion->imageAux[3]); + } + } else { + if ((pVersion->deferredMajor == 0xFF + || (pVersion->deferredMajor == 0x7F)) + && pVersion->deferredMinor == 0xFF) { + printf(" ---.-- -------- |"); + } else { + printf(" %3d.%02x %02X%02X%02X%02X |", + pVersion->deferredMajor, + pVersion->deferredMinor, + pVersion->deferredAux[0], + pVersion->deferredAux[1], + pVersion->deferredAux[2], + pVersion->deferredAux[3]); + } + } +} + +/* HpmfwupgTargerCheck - get target information and displays it on the screen + */ +int +HpmfwupgTargetCheck(struct ipmi_intf *intf, int option) +{ + struct HpmfwupgGetTargetUpgCapabilitiesCtx targetCapCmd; + int rc = HPMFWUPG_SUCCESS; + int componentId = 0; + int flagColdReset = FALSE; + struct ipm_devid_rsp devIdrsp; + struct HpmfwupgGetComponentPropertiesCtx getCompProp; + int mode = 0; + rc = HpmfwupgGetDeviceId(intf, &devIdrsp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, + "Verify whether the Target board is present \n"); + return HPMFWUPG_ERROR; + } + rc = HpmfwupgGetTargetUpgCapabilities(intf, &targetCapCmd); + if (rc != HPMFWUPG_SUCCESS) { + /* That indicates the target is not responding to the command + * May be that there is no HPM support + */ + lprintf(LOG_NOTICE, + "Board might not be supporting the HPM.1 Standards\n"); + return rc; + } + if (option & VIEW_MODE) { + lprintf(LOG_NOTICE, "-------Target Information-------"); + lprintf(LOG_NOTICE, "Device Id : 0x%x", + devIdrsp.device_id); + lprintf(LOG_NOTICE, "Device Revision : 0x%x", + devIdrsp.device_revision); + lprintf(LOG_NOTICE, "Product Id : 0x%04x", + buf2short(devIdrsp.product_id)); + lprintf(LOG_NOTICE, "Manufacturer Id : 0x%04x (%s)\n\n", + buf2short(devIdrsp.manufacturer_id), + val2str(buf2short(devIdrsp.manufacturer_id),ipmi_oem_info)); + HpmDisplayVersionHeader(TARGET_VER|ROLLBACK_VER); + } + for (componentId = HPMFWUPG_COMPONENT_ID_0; + componentId < HPMFWUPG_COMPONENT_ID_MAX; + componentId++ ) { + /* If the component is supported */ + if (((1 << componentId) & targetCapCmd.resp.componentsPresent.ComponentBits.byte)) { + memset((PVERSIONINFO)&gVersionInfo[componentId], 0x00, sizeof(VERSIONINFO)); + getCompProp.req.componentId = componentId; + getCompProp.req.selector = HPMFWUPG_COMP_GEN_PROPERTIES; + rc = HpmfwupgGetComponentProperties(intf, &getCompProp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, "Get CompGenProp Failed for component Id %d\n", + componentId); + return rc; + } + gVersionInfo[componentId].rollbackSupported = getCompProp.resp.Response. + generalPropResp.GeneralCompProperties.bitfield.rollbackBackup; + gVersionInfo[componentId].coldResetRequired = getCompProp.resp.Response. + generalPropResp.GeneralCompProperties.bitfield.payloadColdReset; + getCompProp.req.selector = HPMFWUPG_COMP_DESCRIPTION_STRING; + rc = HpmfwupgGetComponentProperties(intf, &getCompProp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, + "Get CompDescString Failed for component Id %d\n", + componentId); + return rc; + } + memcpy(gVersionInfo[componentId].descString, + getCompProp.resp.Response.descStringResp.descString, + HPMFWUPG_DESC_STRING_LENGTH); + gVersionInfo[componentId].descString[HPMFWUPG_DESC_STRING_LENGTH] = '\0'; + getCompProp.req.selector = HPMFWUPG_COMP_CURRENT_VERSION; + rc = HpmfwupgGetComponentProperties(intf, &getCompProp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, + "Get CompCurrentVersion Failed for component Id %d\n", + componentId); + return rc; + } + gVersionInfo[componentId].componentId = componentId; + gVersionInfo[componentId].targetMajor = getCompProp.resp.Response. + currentVersionResp.currentVersion[0]; + gVersionInfo[componentId].targetMinor = getCompProp.resp.Response. + currentVersionResp.currentVersion[1]; + gVersionInfo[componentId].targetAux[0] = getCompProp.resp.Response. + currentVersionResp.currentVersion[2]; + gVersionInfo[componentId].targetAux[1] = getCompProp.resp.Response. + currentVersionResp.currentVersion[3]; + gVersionInfo[componentId].targetAux[2] = getCompProp.resp.Response. + currentVersionResp.currentVersion[4]; + gVersionInfo[componentId].targetAux[3] = getCompProp.resp.Response. + currentVersionResp.currentVersion[5]; + mode = TARGET_VER; + if (gVersionInfo[componentId].rollbackSupported) { + getCompProp.req.selector = HPMFWUPG_COMP_ROLLBACK_FIRMWARE_VERSION; + rc = HpmfwupgGetComponentProperties(intf, &getCompProp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, + "Get CompRollbackVersion Failed for component Id %d\n", + componentId); + } else { + gVersionInfo[componentId].rollbackMajor = getCompProp.resp + .Response.rollbackFwVersionResp.rollbackFwVersion[0]; + gVersionInfo[componentId].rollbackMinor = getCompProp.resp + .Response.rollbackFwVersionResp.rollbackFwVersion[1]; + gVersionInfo[componentId].rollbackAux[0] = getCompProp.resp.Response.rollbackFwVersionResp.rollbackFwVersion[2]; + gVersionInfo[componentId].rollbackAux[1] = getCompProp.resp.Response.rollbackFwVersionResp.rollbackFwVersion[3]; + gVersionInfo[componentId].rollbackAux[2] = getCompProp.resp.Response.rollbackFwVersionResp.rollbackFwVersion[4]; + gVersionInfo[componentId].rollbackAux[3] = getCompProp.resp.Response.rollbackFwVersionResp.rollbackFwVersion[5]; + } + getCompProp.req.selector = HPMFWUPG_COMP_DEFERRED_FIRMWARE_VERSION; + rc = HpmfwupgGetComponentProperties(intf, &getCompProp); + if (rc != HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, + "Get CompRollbackVersion Failed for component Id %d\n", + componentId); + } else { + gVersionInfo[componentId].deferredMajor = getCompProp.resp + .Response.deferredFwVersionResp.deferredFwVersion[0]; + gVersionInfo[componentId].deferredMinor = getCompProp.resp + .Response.deferredFwVersionResp.deferredFwVersion[1]; + gVersionInfo[componentId].deferredAux[0] = getCompProp.resp.Response.deferredFwVersionResp.deferredFwVersion[2]; + gVersionInfo[componentId].deferredAux[1] = getCompProp.resp.Response.deferredFwVersionResp.deferredFwVersion[3]; + gVersionInfo[componentId].deferredAux[2] = getCompProp.resp.Response.deferredFwVersionResp.deferredFwVersion[4]; + gVersionInfo[componentId].deferredAux[3] = getCompProp.resp.Response.deferredFwVersionResp.deferredFwVersion[5]; + } + mode |= ROLLBACK_VER; + } else { + gVersionInfo[componentId].rollbackMajor = 0xff; + gVersionInfo[componentId].rollbackMinor = 0xff; + gVersionInfo[componentId].rollbackAux[0] = 0xff; + gVersionInfo[componentId].rollbackAux[1] = 0xff; + gVersionInfo[componentId].rollbackAux[2] = 0xff; + gVersionInfo[componentId].rollbackAux[3] = 0xff; + gVersionInfo[componentId].deferredMajor = 0xff; + gVersionInfo[componentId].deferredMinor = 0xff; + gVersionInfo[componentId].deferredAux[0] = 0xff; + gVersionInfo[componentId].deferredAux[1] = 0xff; + gVersionInfo[componentId].deferredAux[2] = 0xff; + gVersionInfo[componentId].deferredAux[3] = 0xff; + } + if (gVersionInfo[componentId].coldResetRequired) { + /* + * If any of the component indicates that the Payload Cold reset is required + * then set the flag + */ + flagColdReset = TRUE; + } + if (option & VIEW_MODE) { + HpmDisplayVersion(mode, + &gVersionInfo[componentId], + 0); + printf("\n"); + } + } + } + if (option & VIEW_MODE) { + HpmDisplayLine("-",74 ); + fflush(stdout); + lprintf(LOG_NOTICE, + "(*) Component requires Payload Cold Reset"); + printf("\n\n"); + } + return HPMFWUPG_SUCCESS; +} + +/* HpmfwupgUpgrade - perform the HPM.1 firmware upgrade procedure as defined + * the IPM Controller Firmware Upgrade Specification version 1.0 + */ +int +HpmfwupgUpgrade(struct ipmi_intf *intf, char *imageFilename, int activate, + int componentMask, int option) +{ + int rc = HPMFWUPG_SUCCESS; + struct HpmfwupgUpgradeCtx fwupgCtx; + /* INITIALIZE UPGRADE CONTEXT */ + memset(&fwupgCtx, 0, sizeof (fwupgCtx)); + /* GET IMAGE BUFFER FROM FILE */ + rc = HpmfwupgGetBufferFromFile(imageFilename, &fwupgCtx); + /* VALIDATE IMAGE INTEGRITY */ + if (rc == HPMFWUPG_SUCCESS) { + printf("Validating firmware image integrity..."); + fflush(stdout); + rc = HpmfwupgValidateImageIntegrity(&fwupgCtx); + if (rc == HPMFWUPG_SUCCESS) { + printf("OK\n"); + fflush(stdout); + } + } + /* PREPARATION STAGE */ + if (rc == HPMFWUPG_SUCCESS) { + printf("Performing preparation stage..."); + fflush(stdout); + rc = HpmfwupgPreparationStage(intf, &fwupgCtx, option); + if (rc == HPMFWUPG_SUCCESS) { + printf("OK\n"); + fflush(stdout); + } + } + /* UPGRADE STAGE */ + if (rc == HPMFWUPG_SUCCESS) { + if (option & VIEW_MODE) { + lprintf(LOG_NOTICE, + "\nComparing Target & Image File version"); + } else if (option & COMPARE_MODE) { + lprintf(LOG_NOTICE, + "\nPerforming upload for compare stage:"); + } else { + lprintf(LOG_NOTICE, "\nPerforming upgrade stage:"); + } + if (option & VIEW_MODE) { + rc = HpmfwupgPreUpgradeCheck(intf, + &fwupgCtx,componentMask, VIEW_MODE); + } else { + rc = HpmfwupgPreUpgradeCheck(intf, &fwupgCtx, + componentMask, option); + if (rc == HPMFWUPG_SUCCESS) { + if (verbose) { + printf("Component update mask : 0x%02x\n", + fwupgCtx.compUpdateMask.ComponentBits.byte); + } + rc = HpmfwupgUpgradeStage(intf, &fwupgCtx, option); + } + } + } + /* ACTIVATION STAGE */ + if (rc == HPMFWUPG_SUCCESS && activate) { + /* check if upgrade components mask is non-zero */ + if (fwupgCtx.compUpdateMask.ComponentBits.byte) { + lprintf(LOG_NOTICE, "Performing activation stage: "); + rc = HpmfwupgActivationStage(intf, &fwupgCtx); + } else { + lprintf(LOG_NOTICE, + "No components updated. Skipping activation stage.\n"); + } + } + if (rc == HPMFWUPG_SUCCESS) { + if (option & VIEW_MODE) { + /* Dont display anything here in case we are just viewing it */ + lprintf(LOG_NOTICE," "); + } else if (option & COMPARE_MODE) { + lprintf(LOG_NOTICE, + "\nFirmware comparison procedure complete\n"); + } else { + lprintf(LOG_NOTICE, + "\nFirmware upgrade procedure successful\n"); + } + } else if (option & VIEW_MODE) { + /* Dont display anything here in case we are just viewing it */ + lprintf(LOG_NOTICE," "); + } else if (option & COMPARE_MODE) { + lprintf(LOG_NOTICE, + "Firmware comparison procedure failed\n"); + } else { + lprintf(LOG_NOTICE, "Firmware upgrade procedure failed\n"); + } + if (fwupgCtx.pImageData) { + free(fwupgCtx.pImageData); + fwupgCtx.pImageData = NULL; + } + return rc; +} + +/* HpmfwupgValidateImageIntegrity - validate a HPM.1 firmware image file as + * defined in section 4 of the IPM Controller Firmware Upgrade Specification + * version 1.0 + */ +int +HpmfwupgValidateImageIntegrity(struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + struct HpmfwupgImageHeader *pImageHeader = (struct HpmfwupgImageHeader*)pFwupgCtx->pImageData; + md5_state_t ctx; + static unsigned char md[HPMFWUPG_MD5_SIGNATURE_LENGTH]; + unsigned char *pMd5Sig = pFwupgCtx->pImageData + + (pFwupgCtx->imageSize - HPMFWUPG_MD5_SIGNATURE_LENGTH); + /* Validate MD5 checksum */ + memset(md, 0, HPMFWUPG_MD5_SIGNATURE_LENGTH); + memset(&ctx, 0, sizeof(md5_state_t)); + md5_init(&ctx); + md5_append(&ctx, pFwupgCtx->pImageData, + pFwupgCtx->imageSize - HPMFWUPG_MD5_SIGNATURE_LENGTH); + md5_finish(&ctx, md); + if (memcmp(md, pMd5Sig, HPMFWUPG_MD5_SIGNATURE_LENGTH) != 0) { + lprintf(LOG_NOTICE, "\n Invalid MD5 signature"); + return HPMFWUPG_ERROR; + } + /* Validate Header signature */ + if(strncmp(pImageHeader->signature, + HPMFWUPG_IMAGE_SIGNATURE, + HPMFWUPG_HEADER_SIGNATURE_LENGTH) != 0) { + lprintf(LOG_NOTICE,"\n Invalid image signature"); + return HPMFWUPG_ERROR; + } + /* Validate Header image format version */ + if (pImageHeader->formatVersion != HPMFWUPG_IMAGE_HEADER_VERSION) { + lprintf(LOG_NOTICE,"\n Unrecognized image version"); + return HPMFWUPG_ERROR; + } + /* Validate header checksum */ + if (HpmfwupgCalculateChecksum((unsigned char*)pImageHeader, + sizeof(struct HpmfwupgImageHeader) + + pImageHeader->oemDataLength + + sizeof(unsigned char)/*checksum*/) != 0) { + lprintf(LOG_NOTICE,"\n Invalid header checksum"); + return HPMFWUPG_ERROR; + } + return HPMFWUPG_SUCCESS; +} + +/* HpmfwupgPreparationStage - prepere stage of a firmware upgrade procedure as + * defined in section 3.2 of the IPM Controller Firmware Upgrade Specification + * version 1.0 + */ +int +HpmfwupgPreparationStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option) +{ + int componentId; + int rc = HPMFWUPG_SUCCESS; + struct HpmfwupgGetTargetUpgCapabilitiesCtx targetCapCmd; + struct HpmfwupgImageHeader *pImageHeader = (struct HpmfwupgImageHeader*) + pFwupgCtx->pImageData; + /* Get device ID */ + rc = HpmfwupgGetDeviceId(intf, &pFwupgCtx->devId); + /* Match current IPMC IDs with upgrade image */ + if (rc != HPMFWUPG_SUCCESS) { + return HPMFWUPG_ERROR; + } + /* Validate device ID */ + if (pImageHeader->deviceId == pFwupgCtx->devId.device_id) { + /* Validate product ID */ + if (memcmp(pImageHeader->prodId, + pFwupgCtx->devId.product_id, + HPMFWUPG_PRODUCT_ID_LENGTH ) == 0) { + /* Validate man ID */ + if (memcmp(pImageHeader->manId, + pFwupgCtx->devId.manufacturer_id, + HPMFWUPG_MANUFATURER_ID_LENGTH) != 0) { + lprintf(LOG_NOTICE, + "\n Invalid image file for manufacturer %u", + buf2short(pFwupgCtx->devId.manufacturer_id)); + rc = HPMFWUPG_ERROR; + } + } else { + lprintf(LOG_NOTICE, + "\n Invalid image file for product %u", + buf2short(pFwupgCtx->devId.product_id)); + rc = HPMFWUPG_ERROR; + } + } else { + lprintf(LOG_NOTICE, "\n Invalid device ID %x", + pFwupgCtx->devId.device_id); + rc = HPMFWUPG_ERROR; + } + if (rc != HPMFWUPG_SUCCESS) { + /* Giving one more chance to user to check whether its OK to continue even if the + * product ID does not match. This is helpful as sometimes we just want to update + * and dont care whether we have a different product Id. If the user says NO then + * we need to just bail out from here + */ + if (!((option & FORCE_MODE) || (option & VIEW_MODE))) { + printf("\n\n Use \"force\" option for copying all the components\n"); + return HPMFWUPG_ERROR; + } + printf("\n Image Information"); + printf("\n Device Id : 0x%x", pImageHeader->deviceId); + printf("\n Prod Id : 0x%02x%02x", + pImageHeader->prodId[1], pImageHeader->prodId[0]); + printf("\n Manuf Id : 0x%02x%02x%02x", + pImageHeader->manId[2], + pImageHeader->manId[1], + pImageHeader->manId[0]); + printf("\n Board Information"); + printf("\n Device Id : 0x%x", pFwupgCtx->devId.device_id); + printf("\n Prod Id : 0x%02x%02x", + pFwupgCtx->devId.product_id[1], pFwupgCtx->devId.product_id[0]); + printf("\n Manuf Id : 0x%02x%02x%02x", + pFwupgCtx->devId.manufacturer_id[2], + pFwupgCtx->devId.manufacturer_id[1], + pFwupgCtx->devId.manufacturer_id[0]); + if (HpmGetUserInput("\n Continue ignoring DeviceID/ProductID/ManufacturingID (Y/N): ")) { + rc = HPMFWUPG_SUCCESS; + } else { + return HPMFWUPG_ERROR; + } + } + /* Validate earliest compatible revision */ + /* Validate major & minor revision */ + if (pImageHeader->compRevision[0] > pFwupgCtx->devId.fw_rev1 + || (pImageHeader->compRevision[0] == pFwupgCtx->devId.fw_rev1 + && pImageHeader->compRevision[1] > pFwupgCtx->devId.fw_rev2)) { + /* Version not compatible for upgrade */ + lprintf(LOG_NOTICE, "\n Version: Major: %d", pImageHeader->compRevision[0]); + lprintf(LOG_NOTICE, " Minor: %x", pImageHeader->compRevision[1]); + lprintf(LOG_NOTICE, " Not compatible with "); + lprintf(LOG_NOTICE, " Version: Major: %d", pFwupgCtx->devId.fw_rev1); + lprintf(LOG_NOTICE, " Minor: %x", pFwupgCtx->devId.fw_rev2); + /* Confirming it once again */ + if (!((option & FORCE_MODE) || (option & VIEW_MODE))) { + return HPMFWUPG_ERROR; + } + if (HpmGetUserInput("\n Continue IGNORING Earliest compatibility (Y/N): ")) { + rc = HPMFWUPG_SUCCESS; + } else { + return HPMFWUPG_ERROR; + } + } + /* Get target upgrade capabilities */ + rc = HpmfwupgGetTargetUpgCapabilities(intf, &targetCapCmd); + if (rc != HPMFWUPG_SUCCESS) { + return HPMFWUPG_ERROR; + } + /* Copy response to context */ + memcpy(&pFwupgCtx->targetCap, + &targetCapCmd.resp, + sizeof(struct HpmfwupgGetTargetUpgCapabilitiesResp)); + if (option & VIEW_MODE) { + /* do nothing */ + } else { + /* Make sure all component IDs defined in the + * upgrade image are supported by the IPMC + */ + if ((pImageHeader->components.ComponentBits.byte & + pFwupgCtx->targetCap.componentsPresent.ComponentBits.byte) != + pImageHeader->components.ComponentBits.byte) { + lprintf(LOG_NOTICE, + "\n Some components present in the image file are not supported by the IPMC"); + return HPMFWUPG_ERROR; + } + /* Make sure the upgrade is desirable rigth now */ + if (pFwupgCtx->targetCap.GlobalCapabilities.bitField.fwUpgUndesirable == 1) { + lprintf(LOG_NOTICE, "\n Upgrade undesirable at this moment"); + return HPMFWUPG_ERROR; + } + /* Get confimation from the user if he wants to continue when + * service affected during upgrade + */ + if (!(option & COMPARE_MODE) + && (pFwupgCtx->targetCap.GlobalCapabilities.bitField.servAffectDuringUpg == 1 + || pImageHeader->imageCapabilities.bitField.servAffected == 1)) { + if (HpmGetUserInput("\nServices may be affected during upgrade. Do you wish to continue? (y/n): ")) { + rc = HPMFWUPG_SUCCESS; + } else { + return HPMFWUPG_ERROR; + } + } + } + /* Get the general properties of each component present in image */ + for (componentId = HPMFWUPG_COMPONENT_ID_0; + componentId < HPMFWUPG_COMPONENT_ID_MAX; + componentId++) { + /* Reset component properties */ + memset(&pFwupgCtx->genCompProp[componentId], 0, + sizeof (struct HpmfwupgGetGeneralPropResp)); + if ((1 << componentId & pImageHeader->components.ComponentBits.byte)) { + struct HpmfwupgGetComponentPropertiesCtx getCompPropCmd; + /* Get general component properties */ + getCompPropCmd.req.componentId = componentId; + getCompPropCmd.req.selector = HPMFWUPG_COMP_GEN_PROPERTIES; + rc = HpmfwupgGetComponentProperties(intf, &getCompPropCmd); + if (rc == HPMFWUPG_SUCCESS) { + /* Copy response to context */ + memcpy(&pFwupgCtx->genCompProp[componentId], + &getCompPropCmd.resp, + sizeof(struct HpmfwupgGetGeneralPropResp)); + } + } + } + return rc; +} + +int +image_version_upgradable(VERSIONINFO *pVersionInfo) +{ + /* If the image and active target versions are different, then + * upgrade */ + if ((pVersionInfo->imageMajor != pVersionInfo->targetMajor) + || (pVersionInfo->imageMinor != pVersionInfo->targetMinor) + || (pVersionInfo->imageAux[0] != pVersionInfo->targetAux[0]) + || (pVersionInfo->imageAux[1] != pVersionInfo->targetAux[1]) + || (pVersionInfo->imageAux[2] != pVersionInfo->targetAux[2]) + || (pVersionInfo->imageAux[3] != pVersionInfo->targetAux[3])) { + return (1); + } + /* If the image and active target versions are the same and rollback + * is not supported, then there's nothing to do, skip the upgrade + */ + if (!pVersionInfo->rollbackSupported) { + return (0); + } + /* If the image and rollback target versions are different, then + * go ahead and upgrade + */ + if ((pVersionInfo->imageMajor != pVersionInfo->rollbackMajor) + || (pVersionInfo->imageMinor != pVersionInfo->rollbackMinor) + || (pVersionInfo->imageAux[0] != pVersionInfo->rollbackAux[0]) + || (pVersionInfo->imageAux[1] != pVersionInfo->rollbackAux[1]) + || (pVersionInfo->imageAux[2] != pVersionInfo->rollbackAux[2]) + || (pVersionInfo->imageAux[3] != pVersionInfo->rollbackAux[3])) { + return (1); + } + /* Image and rollback target versions are the same too, skip it */ + return (0); +} + +/* HpmfwupgValidateActionRecordChecksum - validate checksum of the specified + * action record header. + */ +int +HpmfwupgValidateActionRecordChecksum(struct HpmfwupgActionRecord *pActionRecord) +{ + int rc = HPMFWUPG_SUCCESS; + /* Validate action record checksum */ + if (HpmfwupgCalculateChecksum((unsigned char*)pActionRecord, + sizeof(struct HpmfwupgActionRecord)) != 0) { + /* Due to ambiguity in the HPM.1 specification, for the case of + * the Upload Firmware Image action type, the record header length + * might be thought as either the first 3 bytes, or the first 34 bytes + * which precede the firmware image data. + * For the latter case we re-calculate the Upload Firmware Image + * record checksum for the 34 byte header length. + */ + if (pActionRecord->actionType != HPMFWUPG_ACTION_UPLOAD_FIRMWARE + || HpmfwupgCalculateChecksum((unsigned char*)pActionRecord, + sizeof(struct HpmfwupgActionRecord) + + sizeof(struct HpmfwupgFirmwareImage))) { + lprintf(LOG_NOTICE, " Invalid Action record."); + rc = HPMFWUPG_ERROR; + } + } + return rc; +} + +/* HpmfwupgPreUpgradeCheck - make pre-Upgrade check, this mainly helps in + * checking which all version upgrade is skippable because the image version + * is same as target version. + */ +int +HpmfwupgPreUpgradeCheck(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx, + int componentMask, int option) +{ + unsigned char *pImagePtr; + struct HpmfwupgActionRecord *pActionRecord; + struct HpmfwupgImageHeader *pImageHeader; + int componentId; + pImageHeader = (struct HpmfwupgImageHeader*)pFwupgCtx->pImageData; + /* Put pointer after image header */ + pImagePtr = (unsigned char*)(pFwupgCtx->pImageData + + sizeof(struct HpmfwupgImageHeader) + + pImageHeader->oemDataLength + + sizeof(unsigned char)/*chksum*/); + if (option & VIEW_MODE) { + HpmDisplayVersionHeader(TARGET_VER|ROLLBACK_VER|IMAGE_VER); + } + /* Perform actions defined in the image */ + while (pImagePtr < (pFwupgCtx->pImageData + pFwupgCtx->imageSize + - HPMFWUPG_MD5_SIGNATURE_LENGTH)) { + /* Get action record */ + pActionRecord = (struct HpmfwupgActionRecord*)pImagePtr; + /* Validate action record checksum */ + if (HpmfwupgValidateActionRecordChecksum(pActionRecord) != HPMFWUPG_SUCCESS) { + return HPMFWUPG_ERROR; + } + /* Validate affected components */ + if (pActionRecord->components.ComponentBits.byte + && !pFwupgCtx->targetCap.componentsPresent.ComponentBits.byte) { + lprintf(LOG_NOTICE, + " Invalid action record. One or more affected components is not supported"); + return HPMFWUPG_ERROR; + } + switch (pActionRecord->actionType) { + case HPMFWUPG_ACTION_BACKUP_COMPONENTS: + { + /* Make sure every component specified by + * this action record + * supports the backup operation + */ + for (componentId = HPMFWUPG_COMPONENT_ID_0; + componentId < HPMFWUPG_COMPONENT_ID_MAX; + componentId++) { + if (((1 << componentId) & pActionRecord->components.ComponentBits.byte) + && pFwupgCtx->genCompProp[componentId].GeneralCompProperties.bitfield.rollbackBackup == 0) { + lprintf(LOG_NOTICE, + " Component ID %d does not support backup", + componentId); + return HPMFWUPG_ERROR; + } + } + pImagePtr += sizeof(struct HpmfwupgActionRecord); + } + break; + case HPMFWUPG_ACTION_PREPARE_COMPONENTS: + { + /* Make sure every components specified by + * this action + * supports the prepare operation + */ + for (componentId = HPMFWUPG_COMPONENT_ID_0; + componentId < HPMFWUPG_COMPONENT_ID_MAX; + componentId++) { + if (((1 << componentId) & pActionRecord->components.ComponentBits.byte) + && pFwupgCtx->genCompProp[componentId].GeneralCompProperties.bitfield.preparationSupport == 0) { + lprintf(LOG_NOTICE, + " Component ID %d does not support preparation", + componentId); + return HPMFWUPG_ERROR; + } + } + pImagePtr += sizeof(struct HpmfwupgActionRecord); + } + break; + case HPMFWUPG_ACTION_UPLOAD_FIRMWARE: + /* Upload all firmware blocks */ + { + struct HpmfwupgFirmwareImage *pFwImage; + unsigned char *pData; + unsigned int firmwareLength; + unsigned char mode; + unsigned char componentId; + unsigned char componentIdByte; + unsigned int upgrade_comp; + VERSIONINFO *pVersionInfo; + /* Save component ID on which the upload is done */ + componentIdByte = pActionRecord->components.ComponentBits.byte; + componentId = 0; + while ((componentIdByte >>= 1) != 0) { + componentId++; + } + pFwImage = (struct HpmfwupgFirmwareImage*)(pImagePtr + + sizeof(struct HpmfwupgActionRecord)); + pData = ((unsigned char*)pFwImage + + sizeof(struct HpmfwupgFirmwareImage)); + /* Get firmware length */ + firmwareLength = pFwImage->length[0]; + firmwareLength |= (pFwImage->length[1] << 8) & 0xff00; + firmwareLength |= (pFwImage->length[2] << 16) & 0xff0000; + firmwareLength |= (pFwImage->length[3] << 24) & 0xff000000; + + pVersionInfo = &gVersionInfo[componentId]; + + pVersionInfo->imageMajor = pFwImage->version[0]; + pVersionInfo->imageMinor = pFwImage->version[1]; + pVersionInfo->imageAux[0] = pFwImage->version[2]; + pVersionInfo->imageAux[1] = pFwImage->version[3]; + pVersionInfo->imageAux[2] = pFwImage->version[4]; + pVersionInfo->imageAux[3] = pFwImage->version[5]; + + mode = TARGET_VER | IMAGE_VER; + /* check if component is selected for upgrade */ + upgrade_comp = !componentMask + || (componentMask & pActionRecord->components.ComponentBits.byte); + /* check if current component version requires upgrade */ + if (upgrade_comp && !(option & (FORCE_MODE|COMPARE_MODE))) { + upgrade_comp = image_version_upgradable(pVersionInfo); + } + if (verbose) { + lprintf(LOG_NOTICE, + "%s component %d", + (upgrade_comp ? "Updating" : "Skipping"), + componentId); + } + if (upgrade_comp) { + pFwupgCtx->compUpdateMask.ComponentBits.byte|= 1 << componentId; + } + if (option & VIEW_MODE) { + if (pVersionInfo->rollbackSupported) { + mode|= ROLLBACK_VER; + } + HpmDisplayVersion(mode,pVersionInfo, upgrade_comp); + printf("\n"); + } + pImagePtr = pData + firmwareLength; + } + break; + default: + lprintf(LOG_NOTICE, + " Invalid Action type. Cannot continue"); + return HPMFWUPG_ERROR; + break; + } + } + if (option & VIEW_MODE) { + HpmDisplayLine("-",74); + fflush(stdout); + lprintf(LOG_NOTICE, + "(*) Component requires Payload Cold Reset"); + lprintf(LOG_NOTICE, + "(^) Indicates component would be upgraded"); + } + return HPMFWUPG_SUCCESS; +} + +/* HpmfwupgUpgradeStage - upgrade stage of a firmware upgrade procedure as + * defined in section 3.3 of the IPM Controller Firmware Upgrade Specification + * version 1.0 + */ +int +HpmfwupgUpgradeStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option) +{ + struct HpmfwupgImageHeader *pImageHeader = (struct HpmfwupgImageHeader*) + pFwupgCtx->pImageData; + struct HpmfwupgActionRecord* pActionRecord; + int rc = HPMFWUPG_SUCCESS; + unsigned char *pImagePtr; + unsigned int actionsSize; + int flagColdReset = FALSE; + time_t start,end; + /* Put pointer after image header */ + pImagePtr = (unsigned char*) + (pFwupgCtx->pImageData + sizeof(struct HpmfwupgImageHeader) + + pImageHeader->oemDataLength + sizeof(unsigned char)/*checksum*/); + /* Deternime actions size */ + actionsSize = pFwupgCtx->imageSize - sizeof(struct HpmfwupgImageHeader); + if (!(option & VIEW_MODE)) { + HpmDisplayUpgradeHeader(); + } + /* Perform actions defined in the image */ + while (( pImagePtr < (pFwupgCtx->pImageData + pFwupgCtx->imageSize - + HPMFWUPG_MD5_SIGNATURE_LENGTH)) + && (rc == HPMFWUPG_SUCCESS)) { + /* Get action record */ + pActionRecord = (struct HpmfwupgActionRecord*)pImagePtr; + /* Validate action record checksum */ + rc = HpmfwupgValidateActionRecordChecksum(pActionRecord); + if (rc != HPMFWUPG_SUCCESS) { + continue; + } + switch(pActionRecord->actionType) { + case HPMFWUPG_ACTION_BACKUP_COMPONENTS: + { + if (!(option & COMPARE_MODE)) { + /* Send Upgrade Action command */ + struct HpmfwupgInitiateUpgradeActionCtx initUpgActionCmd; + /* Affect only selected components */ + initUpgActionCmd.req.componentsMask.ComponentBits.byte = + pFwupgCtx->compUpdateMask.ComponentBits.byte & + pActionRecord->components.ComponentBits.byte; + /* Action is prepare components */ + if (initUpgActionCmd.req.componentsMask.ComponentBits.byte) { + initUpgActionCmd.req.upgradeAction = HPMFWUPG_UPGRADE_ACTION_BACKUP; + rc = HpmfwupgInitiateUpgradeAction(intf, &initUpgActionCmd, pFwupgCtx); + } + } + pImagePtr+= sizeof(struct HpmfwupgActionRecord); + } + break; + case HPMFWUPG_ACTION_PREPARE_COMPONENTS: + { + if (!(option & COMPARE_MODE)) { + /* Send prepare components command */ + struct HpmfwupgInitiateUpgradeActionCtx initUpgActionCmd; + /* Affect only selected components */ + initUpgActionCmd.req.componentsMask.ComponentBits.byte = + pFwupgCtx->compUpdateMask.ComponentBits.byte & + pActionRecord->components.ComponentBits.byte; + if (initUpgActionCmd.req.componentsMask.ComponentBits.byte) { + /* Action is prepare components */ + initUpgActionCmd.req.upgradeAction = HPMFWUPG_UPGRADE_ACTION_PREPARE; + rc = HpmfwupgInitiateUpgradeAction(intf, &initUpgActionCmd, pFwupgCtx); + } + } + pImagePtr+= sizeof(struct HpmfwupgActionRecord); + } + break; + case HPMFWUPG_ACTION_UPLOAD_FIRMWARE: + /* Upload all firmware blocks */ + rc = HpmFwupgActionUploadFirmware(pActionRecord->components, + pFwupgCtx, + &pImagePtr, + intf, + option, + &flagColdReset); + break; + default: + lprintf(LOG_NOTICE, " Invalid Action type. Cannot continue"); + rc = HPMFWUPG_ERROR; + break; + } + } + HpmDisplayLine("-", 79); + fflush(stdout); + lprintf(LOG_NOTICE, "(*) Component requires Payload Cold Reset"); + return rc; +} + +int +HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, + struct HpmfwupgUpgradeCtx *pFwupgCtx, + unsigned char **pImagePtr, + struct ipmi_intf *intf, + int option, + int *pFlagColdReset) +{ + struct HpmfwupgFirmwareImage *pFwImage; + struct HpmfwupgInitiateUpgradeActionCtx initUpgActionCmd; + struct HpmfwupgUploadFirmwareBlockCtx uploadCmd; + struct HpmfwupgFinishFirmwareUploadCtx finishCmd; + VERSIONINFO *pVersionInfo; + time_t start,end; + + int rc = HPMFWUPG_SUCCESS; + int skip = TRUE; + unsigned char *pData, *pDataInitial; + unsigned short count; + unsigned int totalSent = 0; + unsigned short bufLength = 0; + unsigned short bufLengthIsSet = 0; + unsigned int firmwareLength = 0; + + unsigned int displayFWLength = 0; + unsigned char *pDataTemp; + unsigned int imageOffset = 0x00; + unsigned int blockLength = 0x00; + unsigned int lengthOfBlock = 0x00; + unsigned int numTxPkts = 0; + unsigned int numRxPkts = 0; + unsigned char mode = 0; + unsigned char componentId = 0x00; + unsigned char componentIdByte = 0x00; + uint16_t max_rq_size; + + /* Save component ID on which the upload is done */ + componentIdByte = components.ComponentBits.byte; + while ((componentIdByte>>= 1) != 0) { + componentId++; + } + pFwupgCtx->componentId = componentId; + pVersionInfo = (VERSIONINFO *)&gVersionInfo[componentId]; + pFwImage = (struct HpmfwupgFirmwareImage*)((*pImagePtr) + + sizeof(struct HpmfwupgActionRecord)); + pDataInitial = ((unsigned char *)pFwImage + + sizeof(struct HpmfwupgFirmwareImage)); + pData = pDataInitial; + + /* Find max buffer length according the connection parameters */ + max_rq_size = ipmi_intf_get_max_request_data_size(intf); + + /* validate lower bound of max request size */ + if (max_rq_size <= sizeof(struct HpmfwupgUploadFirmwareBlockReq)) { + lprintf(LOG_ERROR, "Maximum request size is too small to " + "send a upload request."); + return HPMFWUPG_ERROR; + } + + bufLength = max_rq_size - sizeof(struct HpmfwupgUploadFirmwareBlockReq); + + /* Get firmware length */ + firmwareLength = pFwImage->length[0]; + firmwareLength|= (pFwImage->length[1] << 8) & 0xff00; + firmwareLength|= (pFwImage->length[2] << 16) & 0xff0000; + firmwareLength|= (pFwImage->length[3] << 24) & 0xff000000; + mode = TARGET_VER | IMAGE_VER; + if (pVersionInfo->rollbackSupported) { + mode |= ROLLBACK_VER; + } + if ((option & DEBUG_MODE)) { + printf("\n\n Comp ID : %d [%-20s]\n", + pVersionInfo->componentId, + pFwImage->desc); + } else { + HpmDisplayVersion(mode, pVersionInfo, 0); + } + if ((1 << componentId) & pFwupgCtx->compUpdateMask.ComponentBits.byte) { + if (verbose) { + lprintf(LOG_NOTICE, "Do not skip %d", + componentId); + } + skip = FALSE; + } + if (!skip) { + HpmDisplayUpgrade(0,0,1,0); + /* Initialize parameters */ + uploadCmd.req = malloc(max_rq_size); + if (!uploadCmd.req) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return HPMFWUPG_ERROR; + } + uploadCmd.req->blockNumber = 0; + /* Send Initiate Upgrade Action */ + initUpgActionCmd.req.componentsMask = components; + if (option & COMPARE_MODE) { + /* Action is compare */ + initUpgActionCmd.req.upgradeAction = HPMFWUPG_UPGRADE_ACTION_COMPARE; + } else { + /* Action is upgrade */ + initUpgActionCmd.req.upgradeAction = HPMFWUPG_UPGRADE_ACTION_UPGRADE; + } + rc = HpmfwupgInitiateUpgradeAction(intf, &initUpgActionCmd, pFwupgCtx); + if (rc != HPMFWUPG_SUCCESS) { + skip = TRUE; + } + if ((pVersionInfo->coldResetRequired) && (!skip)) { + *pFlagColdReset = TRUE; + } + /* pDataInitial is the starting pointer of the image data */ + /* pDataTemp is one which we will move across */ + pData = pDataInitial; + pDataTemp = pDataInitial; + lengthOfBlock = firmwareLength; + totalSent = 0x00; + displayFWLength= firmwareLength; + time(&start); + while ((pData < (pDataTemp+lengthOfBlock)) && (rc == HPMFWUPG_SUCCESS)) { + if ((pData+bufLength) <= (pDataTemp+lengthOfBlock)) { + count = bufLength; + } else { + count = (unsigned short)((pDataTemp+lengthOfBlock) - pData); + } + memcpy(&uploadCmd.req->data, pData, bufLength); + imageOffset = 0x00; + blockLength = 0x00; + numTxPkts++; + rc = HpmfwupgUploadFirmwareBlock(intf, &uploadCmd, + pFwupgCtx, count, &imageOffset,&blockLength); + numRxPkts++; + if (rc != HPMFWUPG_SUCCESS) { + if (rc == HPMFWUPG_UPLOAD_BLOCK_LENGTH && !bufLengthIsSet) { + rc = HPMFWUPG_SUCCESS; + /* Retry with a smaller buffer length */ + if (strstr(intf->name,"lan") != NULL && bufLength > 8) { + bufLength-= 8; + lprintf(LOG_INFO, + "Trying reduced buffer length: %d", + bufLength); + } else if (bufLength) { + bufLength-= 1; + lprintf(LOG_INFO, + "Trying reduced buffer length: %d", + bufLength); + } else { + rc = HPMFWUPG_ERROR; + } + } else if (rc == HPMFWUPG_UPLOAD_RETRY) { + rc = HPMFWUPG_SUCCESS; + } else { + fflush(stdout); + lprintf(LOG_NOTICE, + "\n Error in Upload FIRMWARE command [rc=%d]\n", + rc); + lprintf(LOG_NOTICE, + "\n TotalSent:0x%x ", + totalSent); + /* Exiting from the function */ + rc = HPMFWUPG_ERROR; + } + } else { + /* success, buf length is valid */ + bufLengthIsSet = 1; + if (blockLength > firmwareLength) { + /* + * blockLength is the remaining length of the firmware to upload so + * if its greater than the firmware length then its kind of error + */ + lprintf(LOG_NOTICE, + "\n Error in Upload FIRMWARE command [rc=%d]\n", + rc); + lprintf(LOG_NOTICE, + "\n TotalSent:0x%x Img offset:0x%x Blk length:0x%x Fwlen:0x%x\n", + totalSent,imageOffset,blockLength,firmwareLength); + rc = HPMFWUPG_ERROR; + } + totalSent += count; + if (imageOffset != 0x00) { + /* block Length is valid */ + lengthOfBlock = blockLength; + pDataTemp = pDataInitial + imageOffset; + pData = pDataTemp; + if (displayFWLength == firmwareLength) { + /* This is basically used only to make sure that we display uptil 100% */ + displayFWLength = blockLength + totalSent; + } + } else { + pData += count; + } + time(&end); + /* + * Just added debug mode in case we need to see exactly how many bytes have + * gone through - Its a hidden option used mainly should be used for debugging + */ + if (option & DEBUG_MODE) { + fflush(stdout); + printf(" Blk Num : %02x Bytes : %05x ", + uploadCmd.req->blockNumber,totalSent); + if (imageOffset || blockLength) { + printf("\n--> ImgOff : %x BlkLen : %x\n", + imageOffset,blockLength); + } + if (displayFWLength == totalSent) { + printf("\n Time Taken %02ld:%02ld", + (end-start)/60, (end-start)%60); + printf("\n\n"); + } + } else { + HpmDisplayUpgrade(0, totalSent, + displayFWLength, (end-start)); + } + uploadCmd.req->blockNumber++; + } + } + /* free buffer */ + free(uploadCmd.req); + uploadCmd.req = NULL; + } + if (skip) { + HpmDisplayUpgrade(1,0,0,0); + if ((option & COMPARE_MODE) + && !pFwupgCtx->genCompProp[pFwupgCtx->componentId].GeneralCompProperties.bitfield.comparisonSupport) { + printf("| |Comparison isn't supported for given compenent. |\n"); + } + *pImagePtr = pDataInitial + firmwareLength; + } + if (rc == HPMFWUPG_SUCCESS && !skip) { + /* Send finish component */ + /* Set image length */ + finishCmd.req.componentId = componentId; + /* We need to send the actual data that is sent + * not the comlete firmware image length + */ + finishCmd.req.imageLength[0] = totalSent & 0xFF; + finishCmd.req.imageLength[1] = (totalSent >> 8) & 0xFF; + finishCmd.req.imageLength[2] = (totalSent >> 16) & 0xFF; + finishCmd.req.imageLength[3] = (totalSent >> 24) & 0xFF; + rc = HpmfwupgFinishFirmwareUpload(intf, &finishCmd, + pFwupgCtx, option); + *pImagePtr = pDataInitial + firmwareLength; + } + return rc; +} + +/* HpmfwupgActivationStage - validate stage of a firmware upgrade procedure as + * defined in section 3.4 of the IPM Controller Firmware Upgrade Specification + * version 1.0 + */ +int +HpmfwupgActivationStage(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct HpmfwupgActivateFirmwareCtx activateCmd; + struct HpmfwupgImageHeader *pImageHeader = (struct HpmfwupgImageHeader*) + pFwupgCtx->pImageData; + /* Print out stuf...*/ + printf(" "); + fflush(stdout); + /* Activate new firmware */ + activateCmd.req.rollback_override = 0; + rc = HpmfwupgActivateFirmware(intf, &activateCmd, pFwupgCtx); + if (rc == HPMFWUPG_SUCCESS) { + /* Query self test result if supported by target and new image */ + if ((pFwupgCtx->targetCap.GlobalCapabilities.bitField.ipmcSelftestCap == 1) + || (pImageHeader->imageCapabilities.bitField.imageSelfTest == 1)) { + struct HpmfwupgQuerySelftestResultCtx selfTestCmd; + rc = HpmfwupgQuerySelftestResult(intf, &selfTestCmd, + pFwupgCtx); + if (rc == HPMFWUPG_SUCCESS) { + /* Get the self test result */ + if (selfTestCmd.resp.result1 != 0x55) { + /* Perform manual rollback if necessary */ + /* BACKUP/ MANUAL ROLLBACK not supported by this UA */ + lprintf(LOG_NOTICE, " Self test failed:"); + lprintf(LOG_NOTICE, " Result1 = %x", + selfTestCmd.resp.result1); + lprintf(LOG_NOTICE, " Result2 = %x", + selfTestCmd.resp.result2); + rc = HPMFWUPG_ERROR; + } + } else { + /* Perform manual rollback if necessary */ + /* BACKUP / MANUAL ROLLBACK not supported by this UA */ + lprintf(LOG_NOTICE," Self test failed."); + } + } + } + /* If activation / self test failed, query rollback + * status if automatic rollback supported + */ + if (rc == HPMFWUPG_ERROR) { + if ((pFwupgCtx->targetCap.GlobalCapabilities.bitField.autRollback == 1) + && (pFwupgCtx->genCompProp[pFwupgCtx->componentId].GeneralCompProperties.bitfield.rollbackBackup != 0x00)) { + struct HpmfwupgQueryRollbackStatusCtx rollCmd; + lprintf(LOG_NOTICE," Getting rollback status..."); + fflush(stdout); + rc = HpmfwupgQueryRollbackStatus(intf, + &rollCmd, pFwupgCtx); + } + } + return rc; +} + +int +HpmfwupgGetBufferFromFile(char *imageFilename, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + int ret = 0; + FILE *pImageFile = fopen(imageFilename, "rb"); + if (pImageFile == NULL) { + lprintf(LOG_ERR, "Cannot open image file '%s'", + imageFilename); + return HPMFWUPG_ERROR; + } + /* Get the raw data in file */ + fseek(pImageFile, 0, SEEK_END); + pFwupgCtx->imageSize = ftell(pImageFile); + pFwupgCtx->pImageData = malloc(sizeof(unsigned char)*pFwupgCtx->imageSize); + if (pFwupgCtx->pImageData == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + fclose(pImageFile); + return HPMFWUPG_ERROR; + } + rewind(pImageFile); + ret = fread(pFwupgCtx->pImageData, + sizeof(unsigned char), + pFwupgCtx->imageSize, + pImageFile); + if (ret != pFwupgCtx->imageSize) { + lprintf(LOG_ERR, + "Failed to read file %s size %d", + imageFilename, + pFwupgCtx->imageSize); + rc = HPMFWUPG_ERROR; + } + fclose(pImageFile); + return rc; +} + +int +HpmfwupgGetDeviceId(struct ipmi_intf *intf, struct ipm_devid_rsp *pGetDevId) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + rsp = HpmfwupgSendCmd(intf, req, NULL); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting device ID."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode != 0x00) { + lprintf(LOG_ERR, "Error getting device ID."); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + return HPMFWUPG_ERROR; + } + memcpy(pGetDevId, rsp->data, sizeof(struct ipm_devid_rsp)); + return HPMFWUPG_SUCCESS; +} + +int +HpmfwupgGetTargetUpgCapabilities(struct ipmi_intf *intf, + struct HpmfwupgGetTargetUpgCapabilitiesCtx *pCtx) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_GET_TARGET_UPG_CAPABILITIES; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgGetTargetUpgCapabilitiesReq); + rsp = HpmfwupgSendCmd(intf, req, NULL); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error getting target upgrade capabilities."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode != 0x00) { + lprintf(LOG_ERR, + "Error getting target upgrade capabilities, ccode: 0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + return HPMFWUPG_ERROR; + } + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgGetTargetUpgCapabilitiesResp)); + if (verbose) { + lprintf(LOG_NOTICE, "TARGET UPGRADE CAPABILITIES"); + lprintf(LOG_NOTICE, "-------------------------------"); + lprintf(LOG_NOTICE, "HPM.1 version............%d ", + pCtx->resp.hpmVersion); + lprintf(LOG_NOTICE, "Component 0 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component0 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 1 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component1 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 2 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component2 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 3 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component3 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 4 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component4 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 5 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component5 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 6 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component6 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Component 7 presence....[%c] ", + pCtx->resp.componentsPresent.ComponentBits.bitField.component7 ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Upgrade undesirable.....[%c] ", + pCtx->resp.GlobalCapabilities.bitField.fwUpgUndesirable ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Aut rollback override...[%c] ", + pCtx->resp.GlobalCapabilities.bitField.autRollbackOverride ? 'y' : 'n'); + lprintf(LOG_NOTICE, "IPMC degraded...........[%c] ", + pCtx->resp.GlobalCapabilities.bitField.ipmcDegradedDurinUpg ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Defered activation......[%c] ", + pCtx->resp.GlobalCapabilities.bitField.deferActivation ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Service affected........[%c] ", + pCtx->resp.GlobalCapabilities.bitField.servAffectDuringUpg ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Manual rollback.........[%c] ", + pCtx->resp.GlobalCapabilities.bitField.manualRollback ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Automatic rollback......[%c] ", + pCtx->resp.GlobalCapabilities.bitField.autRollback ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Self test...............[%c] ", + pCtx->resp.GlobalCapabilities.bitField.ipmcSelftestCap ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Upgrade timeout.........[%d sec] ", + pCtx->resp.upgradeTimeout*5); + lprintf(LOG_NOTICE, "Self test timeout.......[%d sec] ", + pCtx->resp.selftestTimeout*5); + lprintf(LOG_NOTICE, "Rollback timeout........[%d sec] ", + pCtx->resp.rollbackTimeout*5); + lprintf(LOG_NOTICE, "Inaccessibility timeout.[%d sec] \n", + pCtx->resp.inaccessTimeout*5); + } + return HPMFWUPG_SUCCESS; +} + +int +HpmfwupgGetComponentProperties(struct ipmi_intf *intf, + struct HpmfwupgGetComponentPropertiesCtx *pCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs * rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_GET_COMPONENT_PROPERTIES; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgGetComponentPropertiesReq); + rsp = HpmfwupgSendCmd(intf, req, NULL); + if (rsp == NULL) { + lprintf(LOG_NOTICE, + "Error getting component properties\n"); + return HPMFWUPG_ERROR; + } + if (rsp->ccode != 0x00) { + lprintf(LOG_NOTICE, + "Error getting component properties"); + lprintf(LOG_NOTICE, + "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + return HPMFWUPG_ERROR; + } + switch (pCtx->req.selector) { + case HPMFWUPG_COMP_GEN_PROPERTIES: + memcpy(&pCtx->resp, rsp->data, sizeof(struct HpmfwupgGetGeneralPropResp)); + if (verbose) { + lprintf(LOG_NOTICE, "GENERAL PROPERTIES"); + lprintf(LOG_NOTICE, "-------------------------------"); + lprintf(LOG_NOTICE, "Payload cold reset req....[%c] ", + pCtx->resp.Response.generalPropResp.GeneralCompProperties.bitfield.payloadColdReset ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Def. activation supported.[%c] ", + pCtx->resp.Response.generalPropResp.GeneralCompProperties.bitfield.deferredActivation ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Comparison supported......[%c] ", + pCtx->resp.Response.generalPropResp.GeneralCompProperties.bitfield.comparisonSupport ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Preparation supported.....[%c] ", + pCtx->resp.Response.generalPropResp.GeneralCompProperties.bitfield.preparationSupport ? 'y' : 'n'); + lprintf(LOG_NOTICE, "Rollback supported........[%c] \n", + pCtx->resp.Response.generalPropResp.GeneralCompProperties.bitfield.rollbackBackup ? 'y' : 'n'); + } + break; + case HPMFWUPG_COMP_CURRENT_VERSION: + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgGetCurrentVersionResp)); + if (verbose) { + lprintf(LOG_NOTICE, "Current Version: "); + lprintf(LOG_NOTICE, " Major: %d", + pCtx->resp.Response.currentVersionResp.currentVersion[0]); + lprintf(LOG_NOTICE, " Minor: %x", + pCtx->resp.Response.currentVersionResp.currentVersion[1]); + lprintf(LOG_NOTICE, " Aux : %03d %03d %03d %03d\n", + pCtx->resp.Response.currentVersionResp.currentVersion[2], + pCtx->resp.Response.currentVersionResp.currentVersion[3], + pCtx->resp.Response.currentVersionResp.currentVersion[4], + pCtx->resp.Response.currentVersionResp.currentVersion[5]); + } + break; + case HPMFWUPG_COMP_DESCRIPTION_STRING: + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgGetDescStringResp)); + if (verbose) { + char descString[HPMFWUPG_DESC_STRING_LENGTH + 1]; + memcpy(descString, + pCtx->resp.Response.descStringResp.descString, + HPMFWUPG_DESC_STRING_LENGTH); + descString[HPMFWUPG_DESC_STRING_LENGTH] = '\0'; + lprintf(LOG_NOTICE, + "Description string: %s\n", + descString); + } + break; + case HPMFWUPG_COMP_ROLLBACK_FIRMWARE_VERSION: + memcpy(&pCtx->resp, rsp->data, sizeof(struct HpmfwupgGetRollbackFwVersionResp)); + if (verbose) { + lprintf(LOG_NOTICE, "Rollback FW Version: "); + lprintf(LOG_NOTICE, " Major: %d", + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[0]); + lprintf(LOG_NOTICE, " Minor: %x", + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[1]); + lprintf(LOG_NOTICE, " Aux : %03d %03d %03d %03d\n", + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[2], + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[3], + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[4], + pCtx->resp.Response.rollbackFwVersionResp.rollbackFwVersion[5]); + } + break; + case HPMFWUPG_COMP_DEFERRED_FIRMWARE_VERSION: + memcpy(&pCtx->resp, rsp->data, sizeof(struct HpmfwupgGetDeferredFwVersionResp)); + if (verbose) { + lprintf(LOG_NOTICE, "Deferred FW Version: "); + lprintf(LOG_NOTICE, " Major: %d", + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[0]); + lprintf(LOG_NOTICE, " Minor: %x", + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[1]); + lprintf(LOG_NOTICE, " Aux : %03d %03d %03d %03d\n", + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[2], + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[3], + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[4], + pCtx->resp.Response.deferredFwVersionResp.deferredFwVersion[5]); + } + break; + case HPMFWUPG_COMP_OEM_PROPERTIES: + /* OEM Properties command */ + memcpy(&pCtx->resp, rsp->data, sizeof(struct HpmfwupgGetOemProperties)); + if (verbose) { + unsigned char i = 0; + lprintf(LOG_NOTICE,"OEM Properties: "); + for (i=0; i < HPMFWUPG_OEM_LENGTH; i++) { + lprintf(LOG_NOTICE, " 0x%x ", + pCtx->resp.Response.oemProperties.oemRspData[i]); + } + } + break; + default: + lprintf(LOG_NOTICE,"Unsupported component selector"); + rc = HPMFWUPG_ERROR; + break; + } + return rc; +} + +int +HpmfwupgAbortUpgrade(struct ipmi_intf *intf, + struct HpmfwupgAbortUpgradeCtx *pCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_ABORT_UPGRADE; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgAbortUpgradeReq); + rsp = HpmfwupgSendCmd(intf, req, NULL); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error - aborting upgrade."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode != 0x00) { + lprintf(LOG_ERR, "Error aborting upgrade"); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgInitiateUpgradeAction(struct ipmi_intf *intf, + struct HpmfwupgInitiateUpgradeActionCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_INITIATE_UPGRADE_ACTION; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgInitiateUpgradeActionReq); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error initiating upgrade action."); + return HPMFWUPG_ERROR; + } + /* Long duration command handling */ + if (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) { + rc = HpmfwupgWaitLongDurationCmd(intf, pFwupgCtx); + } else if (rsp->ccode != 0x00) { + lprintf(LOG_NOTICE,"Error initiating upgrade action"); + lprintf(LOG_NOTICE, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgUploadFirmwareBlock(struct ipmi_intf *intf, + struct HpmfwupgUploadFirmwareBlockCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int count, + unsigned int *imageOffset, unsigned int *blockLength) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req->picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_UPLOAD_FIRMWARE_BLOCK; + req.msg.data = (unsigned char *)pCtx->req; + /* 2 is the size of the upload struct - data */ + req.msg.data_len = 2 + count; + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + if (rsp == NULL) { + lprintf(LOG_NOTICE, "Error uploading firmware block."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS + || rsp->ccode == 0x00) { + /* + * We need to check if the response also contains the next upload firmware offset + * and the firmware length in its response - These are optional but very vital + */ + if (rsp->data_len > 1) { + /* + * If the response data length is greater than 1 it should contain both the + * the Section offset and section length. Because we cannot just have + * Section offset without section length so the length should be 9 + */ + if (rsp->data_len == 9) { + /* rsp->data[1] - LSB rsp->data[2] - rsp->data[3] = MSB */ + *imageOffset = (rsp->data[4] << 24) + (rsp->data[3] << 16) + (rsp->data[2] << 8) + rsp->data[1]; + *blockLength = (rsp->data[8] << 24) + (rsp->data[7] << 16) + (rsp->data[6] << 8) + rsp->data[5]; + } else { + /* + * The Spec does not say much for this kind of errors where the + * firmware returned only offset and length so currently returning it + * as 0x82 - Internal CheckSum Error + */ + lprintf(LOG_NOTICE, + "Error wrong rsp->datalen %d for Upload Firmware block command\n", + rsp->data_len); + rsp->ccode = HPMFWUPG_INT_CHECKSUM_ERROR; + } + } + } + /* Long duration command handling */ + if (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) { + rc = HpmfwupgWaitLongDurationCmd(intf, pFwupgCtx); + } else if (rsp->ccode != 0x00) { + /* PATCH --> This validation is to handle retryables errors codes on IPMB bus. + * This will be fixed in the next release of open ipmi and this + * check will have to be removed. (Buggy version = 39) + */ + if (HPMFWUPG_IS_RETRYABLE(rsp->ccode)) { + lprintf(LOG_DEBUG, "HPM: [PATCH]Retryable error detected"); + rc = HPMFWUPG_UPLOAD_RETRY; + } else if (rsp->ccode == IPMI_CC_REQ_DATA_INV_LENGTH || + rsp->ccode == IPMI_CC_REQ_DATA_FIELD_EXCEED) { + /* If completion code = 0xc7(0xc8), we will retry with a reduced buffer length. + * Do not print error. + */ + rc = HPMFWUPG_UPLOAD_BLOCK_LENGTH; + } else { + lprintf(LOG_ERR, "Error uploading firmware block"); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, + completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + } + return rc; +} + +int +HpmfwupgFinishFirmwareUpload(struct ipmi_intf *intf, + struct HpmfwupgFinishFirmwareUploadCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx, int option) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_FINISH_FIRMWARE_UPLOAD; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgFinishFirmwareUploadReq); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error fininshing firmware upload."); + return HPMFWUPG_ERROR; + } + /* Long duration command handling */ + if (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) { + rc = HpmfwupgWaitLongDurationCmd(intf, pFwupgCtx); + } else if ((option & COMPARE_MODE) && rsp->ccode == 0x83) { + printf("| |Component's active copy doesn't match the upgrade image |\n"); + } else if ((option & COMPARE_MODE) && rsp->ccode == IPMI_CC_OK) { + printf("| |Comparison passed |\n"); + } else if ( rsp->ccode != IPMI_CC_OK ) { + lprintf(LOG_ERR, "Error finishing firmware upload"); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgActivateFirmware(struct ipmi_intf *intf, + struct HpmfwupgActivateFirmwareCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_ACTIVATE_FIRMWARE; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgActivateFirmwareReq) + - (!pCtx->req.rollback_override ? 1 : 0); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error activating firmware."); + return HPMFWUPG_ERROR; + } + /* Long duration command handling */ + if (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) { + printf("Waiting firmware activation..."); + fflush(stdout); + rc = HpmfwupgWaitLongDurationCmd(intf, pFwupgCtx); + if (rc == HPMFWUPG_SUCCESS) { + lprintf(LOG_NOTICE, "OK"); + } else { + lprintf(LOG_NOTICE, "Failed"); + } + } else if (rsp->ccode != IPMI_CC_OK) { + lprintf(LOG_ERR, "Error activating firmware"); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgGetUpgradeStatus(struct ipmi_intf *intf, + struct HpmfwupgGetUpgradeStatusCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx, + int silent) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_GET_UPGRADE_STATUS; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgGetUpgradeStatusReq); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + if (!rsp) { + lprintf(LOG_NOTICE, + "Error getting upgrade status. Failed to get response."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode == 0x00) { + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgGetUpgradeStatusResp)); + if (!silent) { + lprintf(LOG_NOTICE, "Upgrade status:"); + lprintf(LOG_NOTICE, + " Command in progress: %x", + pCtx->resp.cmdInProcess); + lprintf(LOG_NOTICE, + " Last command completion code: %x", + pCtx->resp.lastCmdCompCode); + } + } else if (HPMFWUPG_IS_RETRYABLE(rsp->ccode)) { + /* PATCH --> This validation is to handle retryable errors + * codes on the IPMB bus. + * This will be fixed in the next release of + * open ipmi and this check can be removed. + * (Buggy version = 39) + */ + if (!silent) { + lprintf(LOG_DEBUG, "HPM: Retryable error detected"); + } + pCtx->resp.lastCmdCompCode = HPMFWUPG_COMMAND_IN_PROGRESS; + } else { + lprintf(LOG_NOTICE, "Error getting upgrade status"); + lprintf(LOG_NOTICE, "compcode=0x%x: %s", rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + return HPMFWUPG_ERROR; + } + return HPMFWUPG_SUCCESS; +} + +int +HpmfwupgManualFirmwareRollback(struct ipmi_intf *intf, + struct HpmfwupgManualFirmwareRollbackCtx *pCtx) +{ + struct HpmfwupgUpgradeCtx fwupgCtx; + struct HpmfwupgGetTargetUpgCapabilitiesCtx targetCapCmd; + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + /* prepare fake upgrade context */ + memset(&fwupgCtx, 0, sizeof (fwupgCtx)); + verbose--; + rc = HpmfwupgGetTargetUpgCapabilities(intf, &targetCapCmd); + verbose++; + if (rc != HPMFWUPG_SUCCESS) { + return rc; + } + memcpy(&fwupgCtx.targetCap, &targetCapCmd.resp, sizeof(targetCapCmd.resp)); + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_MANUAL_FIRMWARE_ROLLBACK; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgManualFirmwareRollbackReq); + rsp = HpmfwupgSendCmd(intf, req, &fwupgCtx); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error sending manual rollback."); + return HPMFWUPG_ERROR; + } + /* Long duration command handling */ + if (rsp->ccode == IPMI_CC_OK + || rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) { + struct HpmfwupgQueryRollbackStatusCtx resCmd; + printf("Waiting firmware rollback..."); + fflush(stdout); + rc = HpmfwupgQueryRollbackStatus(intf, &resCmd, &fwupgCtx); + } else if ( rsp->ccode != 0x00 ) { + lprintf(LOG_ERR, "Error sending manual rollback"); + lprintf(LOG_ERR, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgQueryRollbackStatus(struct ipmi_intf *intf, + struct HpmfwupgQueryRollbackStatusCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + unsigned int rollbackTimeout = 0; + unsigned int timeoutSec1, timeoutSec2; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_QUERY_ROLLBACK_STATUS; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgQueryRollbackStatusReq); + /* If we are not in upgrade context, we use default timeout values */ + if (pFwupgCtx != NULL) { + struct HpmfwupgImageHeader *pImageHeader; + if (pFwupgCtx->pImageData) { + pImageHeader = (struct HpmfwupgImageHeader*)pFwupgCtx->pImageData; + rollbackTimeout = pImageHeader->rollbackTimeout; + } else { + rollbackTimeout = 0; + } + /* Use the greater of the two timeouts (header and target caps) */ + rollbackTimeout = MAX(rollbackTimeout, + pFwupgCtx->targetCap.rollbackTimeout) * 5; + } else { + rollbackTimeout = HPMFWUPG_DEFAULT_UPGRADE_TIMEOUT; + } + /* Poll rollback status until completion or timeout */ + timeoutSec1 = time(NULL); + timeoutSec2 = time(NULL); + do { + /* Must wait at least 100 ms between status requests */ + usleep(100000); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + /* PATCH --> This validation is to handle retryables errors codes on IPMB bus. + * This will be fixed in the next release of open ipmi and this + * check will have to be removed. (Buggy version = 39) + */ + if (rsp) { + if (HPMFWUPG_IS_RETRYABLE(rsp->ccode)) { + lprintf(LOG_DEBUG,"HPM: [PATCH]Retryable error detected"); + rsp->ccode = HPMFWUPG_COMMAND_IN_PROGRESS; + } + } + timeoutSec2 = time(NULL); + } while (rsp + && ((rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) + || (rsp->ccode == IPMI_CC_TIMEOUT)) + && (timeoutSec2 - timeoutSec1 < rollbackTimeout)); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error getting upgrade status."); + return HPMFWUPG_ERROR; + } + if (rsp->ccode == 0x00) { + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgQueryRollbackStatusResp)); + if (pCtx->resp.rollbackComp.ComponentBits.byte != 0) { + /* Rollback occured */ + lprintf(LOG_NOTICE, + "Rollback occured on component mask: 0x%02x", + pCtx->resp.rollbackComp.ComponentBits.byte); + } else { + lprintf(LOG_NOTICE, + "No Firmware rollback occured"); + } + } else if (rsp->ccode == 0x81) { + lprintf(LOG_ERR, + "Rollback failed on component mask: 0x%02x", + pCtx->resp.rollbackComp.ComponentBits.byte); + rc = HPMFWUPG_ERROR; + } else { + lprintf(LOG_ERR, + "Error getting rollback status"); + lprintf(LOG_ERR, + "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +int +HpmfwupgQuerySelftestResult(struct ipmi_intf *intf, struct HpmfwupgQuerySelftestResultCtx *pCtx, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + struct ipmi_rs *rsp; + struct ipmi_rq req; + unsigned char selfTestTimeout = 0; + unsigned int timeoutSec1, timeoutSec2; + pCtx->req.picmgId = HPMFWUPG_PICMG_IDENTIFIER; + /* If we are not in upgrade context, we use default timeout values */ + if (pFwupgCtx != NULL) { + /* Getting selftest timeout from new image */ + struct HpmfwupgImageHeader *pImageHeader = (struct HpmfwupgImageHeader*) + pFwupgCtx->pImageData; + selfTestTimeout = MAX(pImageHeader->selfTestTimeout, + pFwupgCtx->targetCap.selftestTimeout) * 5; + } else { + selfTestTimeout = HPMFWUPG_DEFAULT_UPGRADE_TIMEOUT; + } + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPMFWUPG_QUERY_SELFTEST_RESULT; + req.msg.data = (unsigned char*)&pCtx->req; + req.msg.data_len = sizeof(struct HpmfwupgQuerySelftestResultReq); + /* Poll rollback status until completion or timeout */ + timeoutSec1 = time(NULL); + timeoutSec2 = time(NULL); + do { + /* Must wait at least 100 ms between status requests */ + usleep(100000); + rsp = HpmfwupgSendCmd(intf, req, pFwupgCtx); + /* PATCH --> This validation is to handle retryables errors codes on IPMB bus. + * This will be fixed in the next release of open ipmi and this + * check will have to be removed. (Buggy version = 39) + */ + if (rsp) { + if (HPMFWUPG_IS_RETRYABLE(rsp->ccode)) { + lprintf(LOG_DEBUG, + "HPM: [PATCH]Retryable error detected"); + rsp->ccode = HPMFWUPG_COMMAND_IN_PROGRESS; + } + } + timeoutSec2 = time(NULL); + } while (rsp + && (rsp->ccode == HPMFWUPG_COMMAND_IN_PROGRESS) + && (timeoutSec2 - timeoutSec1 < selfTestTimeout)); + if (rsp == NULL) { + lprintf(LOG_NOTICE, "Error getting upgrade status\n"); + return HPMFWUPG_ERROR; + } + if (rsp->ccode == 0x00) { + memcpy(&pCtx->resp, rsp->data, + sizeof(struct HpmfwupgQuerySelftestResultResp)); + if (verbose) { + lprintf(LOG_NOTICE, "Self test results:"); + lprintf(LOG_NOTICE, "Result1 = %x", + pCtx->resp.result1); + lprintf(LOG_NOTICE, "Result2 = %x", + pCtx->resp.result2); + } + } else { + lprintf(LOG_NOTICE, "Error getting self test results"); + lprintf(LOG_NOTICE, "compcode=0x%x: %s", + rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + rc = HPMFWUPG_ERROR; + } + return rc; +} + +struct ipmi_rs * +HpmfwupgSendCmd(struct ipmi_intf *intf, struct ipmi_rq req, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + struct ipmi_rs *rsp; + unsigned int inaccessTimeout = 0, inaccessTimeoutCounter = 0; + unsigned int upgradeTimeout = 0, upgradeTimeoutCounter = 0; + unsigned int timeoutSec1, timeoutSec2; + unsigned char retry = 0; + /* If we are not in upgrade context, we use default timeout values */ + if (pFwupgCtx != NULL) { + inaccessTimeout = pFwupgCtx->targetCap.inaccessTimeout*5; + upgradeTimeout = pFwupgCtx->targetCap.upgradeTimeout*5; + } else { + /* keeping the inaccessTimeout to 60 seconds results in almost 2900 retries + * So if the target is not available it will be retrying the command for 2900 + * times which is not effecient -So reducing the Timout to 5 seconds which is + * almost 200 retries if it continuously recieves 0xC3 as completion code. + */ + inaccessTimeout = HPMFWUPG_DEFAULT_UPGRADE_TIMEOUT; + upgradeTimeout = HPMFWUPG_DEFAULT_UPGRADE_TIMEOUT; + } + timeoutSec1 = time(NULL); + do { + static unsigned char isValidSize = FALSE; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + #define HPM_LAN_PACKET_RESIZE_LIMIT 6 + /* also covers lanplus */ + if (strstr(intf->name, "lan") != NULL) { + static int errorCount=0; + static struct ipmi_rs fakeRsp; + lprintf(LOG_DEBUG, + "HPM: no response available"); + lprintf(LOG_DEBUG, + "HPM: the command may be rejected for security reasons"); + if (req.msg.netfn == IPMI_NETFN_PICMG + && req.msg.cmd == HPMFWUPG_UPLOAD_FIRMWARE_BLOCK + && errorCount < HPM_LAN_PACKET_RESIZE_LIMIT + && (!isValidSize)) { + lprintf(LOG_DEBUG, + "HPM: upload firmware block API called"); + lprintf(LOG_DEBUG, + "HPM: returning length error to force resize"); + fakeRsp.ccode = IPMI_CC_REQ_DATA_INV_LENGTH; + rsp = &fakeRsp; + errorCount++; + } else if (req.msg.netfn == IPMI_NETFN_PICMG + && (req.msg.cmd == HPMFWUPG_ACTIVATE_FIRMWARE + || req.msg.cmd == HPMFWUPG_MANUAL_FIRMWARE_ROLLBACK)) { + /* + * rsp == NULL and command activate firmware or manual firmware + * rollback most likely occurs when we have sent a firmware activation + * request. Fake a command in progress response. + */ + lprintf(LOG_DEBUG, + "HPM: activate/rollback firmware API called"); + lprintf(LOG_DEBUG, + "HPM: returning in progress to handle IOL session lost"); + fakeRsp.ccode = HPMFWUPG_COMMAND_IN_PROGRESS; + rsp = &fakeRsp; + } else if (req.msg.netfn == IPMI_NETFN_PICMG + && (req.msg.cmd == HPMFWUPG_QUERY_ROLLBACK_STATUS + || req.msg.cmd == HPMFWUPG_GET_UPGRADE_STATUS + || req.msg.cmd == HPMFWUPG_QUERY_SELFTEST_RESULT) + && ( !intf->target_addr || intf->target_addr == intf->my_addr)) { + /* reopen session only if target IPMC is directly accessed */ + /* + * rsp == NULL and command get upgrade status or query rollback + * status most likely occurs when we are waiting for firmware + * activation. Try to re-open the IOL session (re-open will work + * once the IPMC recovers from firmware activation. + */ + lprintf(LOG_DEBUG, "HPM: upg/rollback status firmware API called"); + lprintf(LOG_DEBUG, "HPM: try to re-open IOL session"); + { + /* force session re-open */ + intf->opened = 0; + intf->session->authtype = IPMI_SESSION_AUTHTYPE_NONE; + intf->session->session_id = 0; + intf->session->in_seq = 0; + intf->session->out_seq = 0; + intf->session->active = 0; + intf->session->retry = 10; + while (intf->open(intf) == HPMFWUPG_ERROR + && inaccessTimeoutCounter < inaccessTimeout) { + inaccessTimeoutCounter += (time(NULL) - timeoutSec1); + timeoutSec1 = time(NULL); + } + /* Fake timeout to retry command */ + fakeRsp.ccode = 0xc3; + rsp = &fakeRsp; + } + } + } + } + /* Handle inaccessibility timeout (rsp = NULL if IOL) */ + if (rsp == NULL || rsp->ccode == 0xff || rsp->ccode == 0xc3 || rsp->ccode == 0xd3) { + if (inaccessTimeoutCounter < inaccessTimeout) { + timeoutSec2 = time(NULL); + if (timeoutSec2 > timeoutSec1) { + inaccessTimeoutCounter += timeoutSec2 - timeoutSec1; + timeoutSec1 = time(NULL); + } + usleep(100000); + retry = 1; + } else { + retry = 0; + } + } else if ( rsp->ccode == 0xc0 ) { + /* Handle node busy timeout */ + if (upgradeTimeoutCounter < upgradeTimeout) { + timeoutSec2 = time(NULL); + if (timeoutSec2 > timeoutSec1) { + timeoutSec1 = time(NULL); + upgradeTimeoutCounter += timeoutSec2 - timeoutSec1; + } + usleep(100000); + retry = 1; + } else { + retry = 0; + } + } else { +# ifdef ENABLE_OPENIPMI_V39_PATCH + if (rsp->ccode == IPMI_CC_OK) { + errorCount = 0 ; + } +# endif + retry = 0; + if (req.msg.netfn == IPMI_NETFN_PICMG + && req.msg.cmd == HPMFWUPG_UPLOAD_FIRMWARE_BLOCK + && (!isValidSize)) { + lprintf(LOG_INFO, + "Buffer length is now considered valid"); + isValidSize = TRUE; + } + } + } while (retry); + return rsp; +} + +int +HpmfwupgWaitLongDurationCmd(struct ipmi_intf *intf, + struct HpmfwupgUpgradeCtx *pFwupgCtx) +{ + int rc = HPMFWUPG_SUCCESS; + unsigned int upgradeTimeout = 0; + unsigned int timeoutSec1, timeoutSec2; + struct HpmfwupgGetUpgradeStatusCtx upgStatusCmd; + /* If we are not in upgrade context, we use default timeout values */ + if (pFwupgCtx != NULL) { + upgradeTimeout = (unsigned int)(pFwupgCtx->targetCap.upgradeTimeout*5); + if (verbose) { + printf("Use File Upgrade Capabilities: %i seconds\n", + upgradeTimeout); + } + } else { + /* Try to retreive from Caps */ + struct HpmfwupgGetTargetUpgCapabilitiesCtx targetCapCmd; + if(HpmfwupgGetTargetUpgCapabilities(intf, &targetCapCmd) != HPMFWUPG_SUCCESS) { + upgradeTimeout = HPMFWUPG_DEFAULT_UPGRADE_TIMEOUT; + if (verbose) { + printf("Use default timeout: %i seconds\n", + upgradeTimeout); + } + } else { + upgradeTimeout = (unsigned int)(targetCapCmd.resp.upgradeTimeout * 5); + if (verbose) { + printf("Use Command Upgrade Capabilities Timeout: %i seconds\n", + upgradeTimeout); + } + } + } + if (rc == HPMFWUPG_SUCCESS) { + /* Poll upgrade status until completion or timeout*/ + timeoutSec1 = time(NULL); + timeoutSec2 = time(NULL); + rc = HpmfwupgGetUpgradeStatus(intf, &upgStatusCmd, + pFwupgCtx, 1); + } + while ( + /* With KCS: Cover the case where we sometime + * receive d5 (on the first get status) from + * the ipmi driver. + */ + (upgStatusCmd.resp.lastCmdCompCode != 0x00 ) + && ((timeoutSec2 - timeoutSec1) < upgradeTimeout ) + && (rc == HPMFWUPG_SUCCESS)) { + /* Must wait at least 1000 ms between status requests */ + usleep(1000000); + timeoutSec2 = time(NULL); + rc = HpmfwupgGetUpgradeStatus(intf, &upgStatusCmd, pFwupgCtx, 1); +/* + * printf("Get Status: %x - %x = %x _ %x [%x]\n", + ( timeoutSec2, timeoutSec1, + * (timeoutSec2 - timeoutSec1), + * upgradeTimeout, rc); + */ + } + if (upgStatusCmd.resp.lastCmdCompCode != 0x00) { + if (verbose) { + lprintf(LOG_NOTICE, + "Error waiting for command %x, compcode = %x", + upgStatusCmd.resp.cmdInProcess, + upgStatusCmd.resp.lastCmdCompCode); + } + rc = HPMFWUPG_ERROR; + } + return rc; +} + +unsigned char +HpmfwupgCalculateChecksum(unsigned char *pData, unsigned int length) +{ + unsigned char checksum = 0; + int dataIdx = 0; + for (dataIdx = 0; dataIdx < length; dataIdx++) { + checksum += pData[dataIdx]; + } + return checksum; +} + +void +HpmfwupgPrintUsage(void) +{ + lprintf(LOG_NOTICE, +"help - This help menu."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"check - Check the target information."); + lprintf(LOG_NOTICE, +"check <file> - If the user is unsure of what update is going to be "); + lprintf(LOG_NOTICE, +" This will display the existing target version and"); + lprintf(LOG_NOTICE, +" image version on the screen"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"upgrade <file> [component x...] [force] [activate]"); + lprintf(LOG_NOTICE, +" - Copies components from a valid HPM.1 image to the target."); + lprintf(LOG_NOTICE, +" If one or more components specified by \"component\","); + lprintf(LOG_NOTICE, +" only the specified components are copied."); + lprintf(LOG_NOTICE, +" Otherwise, all the image components are copied."); + lprintf(LOG_NOTICE, +" Before copy, each image component undergoes a version check"); + lprintf(LOG_NOTICE, +" and can be skipped if the target component version"); + lprintf(LOG_NOTICE, +" is the same or more recent."); + lprintf(LOG_NOTICE, +" Use \"force\" to bypass the version check results."); + lprintf(LOG_NOTICE, +" Make sure to check the versions first using the"); + lprintf(LOG_NOTICE, +" \"check <file>\" command."); + lprintf(LOG_NOTICE, +" If \"activate\" is specified, the newly uploaded firmware"); + lprintf(LOG_NOTICE, +" is activated."); + lprintf(LOG_NOTICE, +"upgstatus - Returns the status of the last long duration command."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"compare <file> - Perform \"Comparison of the Active Copy\" action for all the"); + lprintf(LOG_NOTICE, +" components present in the file."); + lprintf(LOG_NOTICE, +"compare <file> component x - Compare only component <x> from the given <file>"); + lprintf(LOG_NOTICE, +"activate - Activate the newly uploaded firmware."); + lprintf(LOG_NOTICE, +"activate norollback - Activate the newly uploaded firmware but inform"); + lprintf(LOG_NOTICE, +" the target to not automatically rollback if "); + lprintf(LOG_NOTICE, +" the upgrade fails."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"targetcap - Get the target upgrade capabilities."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"compprop <id> <prop> - Get specified component properties from the target."); + lprintf(LOG_NOTICE, +" Valid component <id>: 0-7 "); + lprintf(LOG_NOTICE, +" Properties <prop> can be one of the following: "); + lprintf(LOG_NOTICE, +" 0- General properties"); + lprintf(LOG_NOTICE, +" 1- Current firmware version"); + lprintf(LOG_NOTICE, +" 2- Description string"); + lprintf(LOG_NOTICE, +" 3- Rollback firmware version"); + lprintf(LOG_NOTICE, +" 4- Deferred firmware version"); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"abort - Abort the on-going firmware upgrade."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"rollback - Performs a manual rollback on the IPM Controller."); + lprintf(LOG_NOTICE, +" firmware"); + lprintf(LOG_NOTICE, +"rollbackstatus - Query the rollback status."); + lprintf(LOG_NOTICE, +""); + lprintf(LOG_NOTICE, +"selftestresult - Query the self test results.\n"); +} + +int +ipmi_hpmfwupg_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = HPMFWUPG_SUCCESS; + int activateFlag = 0x00; + int componentMask = 0; + int componentId = 0; + int option = 0; + + lprintf(LOG_DEBUG,"ipmi_hpmfwupg_main()"); + lprintf(LOG_NOTICE, "\nPICMG HPM.1 Upgrade Agent %d.%d.%d: \n", + HPMFWUPG_VERSION_MAJOR, HPMFWUPG_VERSION_MINOR, + HPMFWUPG_VERSION_SUBMINOR); + if (argc < 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + HpmfwupgPrintUsage(); + return HPMFWUPG_ERROR; + } + if (strcmp(argv[0], "help") == 0) { + HpmfwupgPrintUsage(); + return HPMFWUPG_SUCCESS; + } else if ((strcmp(argv[0], "check") == 0)) { + /* hpm check */ + if (argv[1] == NULL) { + rc = HpmfwupgTargetCheck(intf,VIEW_MODE); + } else { + /* hpm check <filename> */ + rc = HpmfwupgTargetCheck(intf,0); + if (rc == HPMFWUPG_SUCCESS) { + rc = HpmfwupgUpgrade(intf, argv[1], 0, + 0, VIEW_MODE); + } + } + } else if (strcmp(argv[0], "upgrade") == 0) { + int i =0; + for (i=1; i< argc ; i++) { + if (strcmp(argv[i],"activate") == 0) { + activateFlag = 1; + } + /* hpm upgrade <filename> force */ + if (strcmp(argv[i],"force") == 0) { + option |= FORCE_MODE; + } + /* hpm upgrade <filename> component <comp Id> */ + if (strcmp(argv[i],"component") == 0) { + if (i+1 < argc) { + /* Error Checking */ + if (str2int(argv[i+1], &componentId) != 0 + || componentId < 0 + || componentId > HPMFWUPG_COMPONENT_ID_MAX) { + lprintf(LOG_ERR, + "Given Component ID '%s' is invalid.", + argv[i+1]); + lprintf(LOG_ERR, + "Valid Compoment ID is: <0..7>"); + return HPMFWUPG_ERROR; + } + if( verbose ) { + lprintf(LOG_NOTICE, + "Component Id %d provided", + componentId ); + } + componentMask |= 1 << componentId; + } else { + /* That indicates the user has + * given component on console but + * not given any ID + */ + lprintf(LOG_NOTICE, + "No component Id provided\n"); + return HPMFWUPG_ERROR; + } + } + if (strcmp(argv[i],"debug") == 0) { + option |= DEBUG_MODE; + } + } + rc = HpmfwupgTargetCheck(intf, 0); + if (rc == HPMFWUPG_SUCCESS) { + /* Call the Upgrade function to start the upgrade */ + rc = HpmfwupgUpgrade(intf, argv[1], activateFlag, + componentMask, option); + } + } else if (strcmp(argv[0], "compare") == 0) { + int i = 0; + for (i=1; i< argc; i++) { + /* hpm compare <file> [component x...] */ + if (strcmp(argv[i],"component") == 0) { + if (i+1 < argc) { + /* Error Checking */ + if (str2int(argv[i+1], &componentId) != 0 + || componentId < 0 + || componentId > HPMFWUPG_COMPONENT_ID_MAX) { + lprintf(LOG_ERR, + "Given Component ID '%s' is invalid.", + argv[i+1]); + lprintf(LOG_ERR, + "Valid Compoment ID is: <0..7>"); + return HPMFWUPG_ERROR; + } + if( verbose ) { + lprintf(LOG_NOTICE, + "Component Id %d provided", + componentId); + } + componentMask|= 1 << componentId; + } else { + /* That indicates the user + * has given component on + * console but not + * given any ID + */ + lprintf(LOG_NOTICE, + "No component Id provided\n"); + return HPMFWUPG_ERROR; + } + } else if (strcmp(argv[i],"debug") == 0) { + option|= DEBUG_MODE; + } + } + option|= (COMPARE_MODE); + rc = HpmfwupgTargetCheck(intf, 0); + if (rc == HPMFWUPG_SUCCESS) { + rc = HpmfwupgUpgrade(intf, argv[1], 0, + componentMask, option); + } + } else if ((argc >= 1) && (strcmp(argv[0], "activate") == 0)) { + struct HpmfwupgActivateFirmwareCtx cmdCtx; + if ((argc == 2) && (strcmp(argv[1], "norollback") == 0)) { + cmdCtx.req.rollback_override = 1; + } else { + cmdCtx.req.rollback_override = 0; + } + rc = HpmfwupgActivateFirmware(intf, &cmdCtx, NULL); + } else if ((argc == 1) && (strcmp(argv[0], "targetcap") == 0)) { + struct HpmfwupgGetTargetUpgCapabilitiesCtx cmdCtx; + verbose++; + rc = HpmfwupgGetTargetUpgCapabilities(intf, &cmdCtx); + } else if ((argc == 3) && (strcmp(argv[0], "compprop") == 0)) { + struct HpmfwupgGetComponentPropertiesCtx cmdCtx; + if (str2uchar(argv[1], &(cmdCtx.req.componentId)) != 0 + || cmdCtx.req.componentId > 7) { + lprintf(LOG_ERR, + "Given Component ID '%s' is invalid.", + argv[1]); + lprintf(LOG_ERR, + "Valid Compoment ID is: <0..7>"); + return (-1); + } + if (str2uchar(argv[2], &(cmdCtx.req.selector)) != 0 + || cmdCtx.req.selector > 4) { + lprintf(LOG_ERR, + "Given Properties selector '%s' is invalid.", + argv[2]); + lprintf(LOG_ERR, + "Valid Properties selector is: <0..4>"); + return (-1); + } + verbose++; + rc = HpmfwupgGetComponentProperties(intf, &cmdCtx); + } else if ((argc == 1) && (strcmp(argv[0], "abort") == 0)) { + struct HpmfwupgAbortUpgradeCtx cmdCtx; + verbose++; + rc = HpmfwupgAbortUpgrade(intf, &cmdCtx); + } else if ((argc == 1) && (strcmp(argv[0], "upgstatus") == 0)) { + struct HpmfwupgGetUpgradeStatusCtx cmdCtx; + verbose++; + rc = HpmfwupgGetUpgradeStatus(intf, &cmdCtx, NULL, 0); + } else if ((argc == 1) && (strcmp(argv[0], "rollback") == 0)) { + struct HpmfwupgManualFirmwareRollbackCtx cmdCtx; + verbose++; + rc = HpmfwupgManualFirmwareRollback(intf, &cmdCtx); + } else if ((argc == 1) && (strcmp(argv[0], "rollbackstatus") == 0)) { + struct HpmfwupgQueryRollbackStatusCtx cmdCtx; + verbose++; + rc = HpmfwupgQueryRollbackStatus(intf, &cmdCtx, NULL); + } else if ((argc == 1) && (strcmp(argv[0], "selftestresult") == 0)) { + struct HpmfwupgQuerySelftestResultCtx cmdCtx; + verbose++; + rc = HpmfwupgQuerySelftestResult(intf, &cmdCtx, NULL); + } else { + lprintf(LOG_ERR, "Invalid HPM command: %s", argv[0]); + HpmfwupgPrintUsage(); + rc = HPMFWUPG_ERROR; + } + return rc; +} + diff --git a/lib/ipmi_ime.c b/lib/ipmi_ime.c new file mode 100755 index 0000000..b520ce5 --- /dev/null +++ b/lib/ipmi_ime.c @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2007 Kontron Canada, Inc. All Rights Reserved. + * + * Base on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/**************************************************************************** +* +* Copyright (c) 2009 Kontron Canada, Inc. All Rights Reserved. +* +* IME +* Intel Manageability Engine +* Firmware Update Agent +* +* The ME is an IPMI-enabled component included in Intel(R) Next Generation +* Server Chipset Nehalem-EP platforms. +* +* These are a few synonyms for the ME : +* +* - Dynamic Power Node Manager +* - Intelligent Power Node Manager +* +* Consult Intel litterature for more information on this technology. +* +* The ME firmware resides on the platform boot flash and contains read only +* boot code for the ME as well as boot image redundancy support. +* +* This module implements an Upgrade Agent for the ME firwmare. Because the ME +* implements IPMI command handling, the agent speaks directly to the ME. In other +* words, in order the reach the ME, the BMC must implement IPMB bridging. +* +* The update is done through IPMI (this is IPMITOOL right !), not HECI. +* +* Example: ME available at address 0x88 on IPMI channel 8: +* ipmitool -m 0x20 -t 0x88 -b 8 ime info +* +* !! WARNING - You MUST use an image provided by your board vendor. - WARNING !! +* +* author: +* Jean-Michel.Audet@ca.kontron.com +* Francois.Isabelle@ca.kontron.com +* +*****************************************************************************/ +/* + * HISTORY + * =========================================================================== + * 2009-04-20 + * + * First public release of Kontron + * +*/ +#include <ipmitool/ipmi_ime.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_strings.h> + + +#undef OUTPUT_DEBUG + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +static const int IME_SUCCESS = 0; +static const int IME_ERROR = -1; +static const int IME_RESTART = -2; + +#define IME_UPGRADE_BUFFER_SIZE 22 +#define IME_RETRY_COUNT 5 + +typedef struct ImeUpdateImageCtx +{ + uint32_t size; + uint8_t * pData; + uint8_t crc8; +}tImeUpdateImageCtx; + +typedef enum eImeState +{ + IME_STATE_IDLE = 0, + IME_STATE_UPDATE_REQUESTED = 1, + IME_STATE_UPDATE_IN_PROGRESS = 2, + IME_STATE_SUCCESS = 3, + IME_STATE_FAILED = 4, + IME_STATE_ROLLED_BACK = 5, + IME_STATE_ABORTED = 6, + IME_STATE_INIT_FAILED = 7 +} tImeStateEnum; + + +typedef enum tImeUpdateType +{ + IME_UPDTYPE_NORMAL = 1, + IME_UPDTYPE_MANUAL_ROLLBACK = 3, + IME_UPDTYPE_ABORT = 4 +} tImeUpdateType; + + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct sImeStatus { + uint8_t image_status; + tImeStateEnum update_state; + uint8_t update_attempt_status; + uint8_t rollback_attempt_status; + uint8_t update_type; + uint8_t dependent_flag; + uint8_t free_area_size[4]; +} ATTRIBUTE_PACKING tImeStatus ; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct sImeCaps { + uint8_t area_supported; + uint8_t special_caps; +} ATTRIBUTE_PACKING tImeCaps ; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + + +static void ImePrintUsage(void); +static int ImeGetInfo(struct ipmi_intf *intf); +static int ImeUpgrade(struct ipmi_intf *intf, char* imageFilename); +static int ImeManualRollback(struct ipmi_intf *intf); +static int ImeUpdatePrepare(struct ipmi_intf *intf); +static int ImeUpdateOpenArea(struct ipmi_intf *intf); +static int ImeUpdateWriteArea( + struct ipmi_intf *intf, + uint8_t sequence, + uint8_t length, + uint8_t * pBuf + ); +static int ImeUpdateCloseArea( + struct ipmi_intf *intf, + uint32_t size, + uint16_t checksum + ); + +static int ImeUpdateGetStatus(struct ipmi_intf *intf, tImeStatus *pStatus); +static int ImeUpdateGetCapabilities(struct ipmi_intf *intf, tImeCaps *pCaps ); +static int ImeUpdateRegisterUpdate(struct ipmi_intf *intf, tImeUpdateType type); + +static int ImeImageCtxFromFile( + char * imageFilename, + tImeUpdateImageCtx * pImageCtx); +static int ImeUpdateShowStatus(struct ipmi_intf *intf); + +static uint8_t ImeCrc8( uint32_t length, uint8_t * pBuf ); + + +static int ImeGetInfo(struct ipmi_intf *intf) +{ + int rc = IME_ERROR; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_devid_rsp *devid; + const char *product=NULL; + tImeStatus status; + tImeCaps caps; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Device ID command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Device ID command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + devid = (struct ipm_devid_rsp *) rsp->data; + + lprintf(LOG_DEBUG,"Device ID : %i", devid->device_id); + lprintf(LOG_DEBUG,"Device Revision : %i", + devid->device_revision & IPM_DEV_DEVICE_ID_REV_MASK); + + if( + (devid->device_id == 0) + && + ((devid->device_revision & IPM_DEV_DEVICE_ID_REV_MASK) == 0) + && + ( + (devid->manufacturer_id[0] == 0x57) // Intel + && + (devid->manufacturer_id[1] == 0x01) // Intel + && + (devid->manufacturer_id[2] == 0x00) // Intel + ) + && + ( + (devid->product_id[1] == 0x0b) + && + (devid->product_id[0] == 0x00) + ) + ) + { + rc = IME_SUCCESS; + printf("Manufacturer Name : %s\n", + val2str( (long)IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id), + ipmi_oem_info) ); + + printf("Product ID : %u (0x%02x%02x)\n", + buf2short((uint8_t *)(devid->product_id)), + devid->product_id[1], devid->product_id[0]); + + product=oemval2str(IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id), + (devid->product_id[1]<<8)+devid->product_id[0], + ipmi_oem_product_info); + + if (product!=NULL) + { + printf("Product Name : %s\n", product); + } + + printf("Intel ME Firmware Revision : %x.%02x.%02x.%x%x%x.%x\n", + ((devid->fw_rev1 & IPM_DEV_FWREV1_MAJOR_MASK ) ), + ((devid->fw_rev2 ) >> 4), + ((devid->fw_rev2 ) & 0x0f), + ((devid->aux_fw_rev[1] ) >> 4), + ((devid->aux_fw_rev[1] ) & 0x0f), + ((devid->aux_fw_rev[2] ) >> 4), + ((devid->aux_fw_rev[2] ) & 0x0f) + ); + + printf("SPS FW IPMI cmd version : %x.%x\n", + devid->aux_fw_rev[0] >> 4, + devid->aux_fw_rev[0] & 0x0f); + + lprintf(LOG_DEBUG,"Flags: %xh", devid->aux_fw_rev[3]); + + printf("Current Image Type : "); + switch( (devid->aux_fw_rev[3] & 0x03) ) + { + case 0: + printf("Recovery\n"); + break; + + case 1: + printf("Operational Image 1\n"); + break; + + case 2: + printf("Operational Image 2\n"); + break; + + case 3: + default: + printf("Unknown\n"); + break; + } + } + else + { + printf("Supported ME not found\n"); + } + + if(rc == IME_SUCCESS) + { + rc = ImeUpdateGetStatus(intf, &status); + + if(rc == IME_SUCCESS) + { + rc = ImeUpdateGetCapabilities(intf, &caps); + } + + } + + if(rc == IME_SUCCESS) + { + uint8_t newImage = ((status.image_status >> 1) & 0x01); + uint8_t rollImage = ((status.image_status >> 2) & 0x01); + uint8_t runArea = ((status.image_status >> 3) & 0x03); + uint8_t rollSup = ((caps.special_caps >> 0) & 0x01); + uint8_t recovSup = ((caps.special_caps >> 1) & 0x01); + + uint8_t operSup = ((caps.area_supported >> 1) & 0x01); + uint8_t piaSup = ((caps.area_supported >> 2) & 0x01); + uint8_t sdrSup = ((caps.area_supported >> 3) & 0x01); + + printf("\nSupported Area\n"); + printf(" Operation Code : %s\n", (operSup ? "Supported" : "Unsupported")); + printf(" PIA : %s\n", (piaSup ? "Supported" : "Unsupported")); + printf(" SDR : %s\n", (sdrSup ? "Supported" : "Unsupported")); + + printf("\nSpecial Capabilities\n"); + printf(" Rollback : %s\n", (rollSup ? "Supported" : "Unsupported")); + printf(" Recovery : %s\n", (recovSup ? "Supported" : "Unsupported")); + + printf("\nImage Status\n"); + printf(" Staging (new) : %s\n", (newImage ? "Valid" : "Invalid")); + printf(" Rollback : %s\n", (rollImage ? "Valid" : "Invalid")); + if(runArea == 0) + printf(" Running Image Area : CODE\n"); + else + printf(" Running Image Area : CODE%d\n", runArea); + + } + + return rc; +} + + +static int ImeUpgrade(struct ipmi_intf *intf, char* imageFilename) +{ + int rc = IME_SUCCESS; + tImeUpdateImageCtx imgCtx; + tImeStatus imeStatus; + time_t start,end,current; + + time(&start); + + memset(&imgCtx, 0, sizeof(tImeUpdateImageCtx)); + + rc = ImeImageCtxFromFile(imageFilename, &imgCtx); + + if( + (rc == IME_ERROR) || + (imgCtx.pData == NULL) || + (imgCtx.size == 0) + ) + { + return IME_ERROR; + } + + ImeUpdateGetStatus(intf,&imeStatus); + + if(rc == IME_SUCCESS) + { + rc = ImeUpdatePrepare(intf); + ImeUpdateGetStatus(intf,&imeStatus); + } + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_UPDATE_REQUESTED) + ) + { + rc = ImeUpdateOpenArea(intf); + ImeUpdateGetStatus(intf,&imeStatus); + } + else if(rc == IME_SUCCESS) + { + lprintf(LOG_ERROR,"ME state error (%i), aborting", imeStatus.update_state); + rc = IME_ERROR; + } + + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_UPDATE_IN_PROGRESS) + ) + { + uint8_t sequence = 0; + uint32_t counter = 0; + uint8_t retry = 0; + uint8_t shownPercent = 0xff; + + while( + (counter < imgCtx.size) && + (rc == IME_SUCCESS) && + (retry < IME_RETRY_COUNT) + ) + { + uint8_t length = IME_UPGRADE_BUFFER_SIZE; + uint8_t currentPercent; + + if( (imgCtx.size - counter) < IME_UPGRADE_BUFFER_SIZE ) + { + length = (imgCtx.size - counter); + } + + rc = ImeUpdateWriteArea(intf,sequence,length,&imgCtx.pData[counter]); + + /* + As per the flowchart Intel Dynamic Power Node Manager 1.5 IPMI Iface + page 65 + We shall send the GetStatus command each time following a write area + but this add too much time to the upgrade + */ + /* ImeUpdateGetStatus(intf,&imeStatus); */ + counter += length; + sequence ++; + + + currentPercent = ((float)counter/imgCtx.size)*100; + + if(currentPercent != shownPercent) + { + uint16_t timeElapsedSecond; + uint16_t timeRemainingSecond; + shownPercent = currentPercent; + printf("Percent: %02i, ", shownPercent); + time(¤t); + timeElapsedSecond = (current-start) + ((current-start)%60); + printf("Elapsed time %02ld:%02ld\r",((current-start)/60), ((current-start)%60)); + fflush(stdout); + + } + } + ImeUpdateGetStatus(intf,&imeStatus); + printf("\n"); + } + else if(rc == IME_SUCCESS) + { + lprintf(LOG_ERROR,"ME state error (%i), aborting", imeStatus.update_state); + rc = IME_ERROR; + } + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_UPDATE_IN_PROGRESS) + ) + { + rc = ImeUpdateCloseArea(intf, imgCtx.size, imgCtx.crc8); + ImeUpdateGetStatus(intf,&imeStatus); + } + else if(rc == IME_SUCCESS) + { + lprintf(LOG_ERROR,"ME state error, aborting"); + rc = IME_ERROR; + } + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_UPDATE_REQUESTED) + ) + { + printf("UpdateCompleted, Activate now\n"); + rc = ImeUpdateRegisterUpdate(intf, IME_UPDTYPE_NORMAL); + ImeUpdateGetStatus(intf,&imeStatus); + } + else if(rc == IME_SUCCESS) + { + lprintf(LOG_ERROR,"ME state error, aborting"); + rc = IME_ERROR; + } + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_SUCCESS) + ) + { + time(&end); + printf("Update Completed in %02ld:%02ld\n",(end-start)/60, (end-start)%60); + } + else + { + time(&end); + printf("Update Error\n"); + printf("\nTime Taken %02ld:%02ld\n",(end-start)/60, (end-start)%60); + } + + return rc; +} + + +static int ImeUpdatePrepare(struct ipmi_intf *intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + #ifdef OUTPUT_DEBUG + printf("ImeUpdatePrepare\n"); + #endif + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA0; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdatePrepare command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdatePrepare command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdatePrepare command succeed"); + return IME_SUCCESS; +} + +static int ImeUpdateOpenArea(struct ipmi_intf *intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t buffer[ 2 ]; + + #ifdef OUTPUT_DEBUG + printf("ImeUpdateOpenArea\n"); + #endif + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA1; + + buffer[0] = 0x01; // Area Type : Operational code + buffer[1] = 0x00; // Reserved : 0 + req.msg.data = buffer; + + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdateOpenArea command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdateOpenArea command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdateOpenArea command succeed"); + return IME_SUCCESS; +} + +static int ImeUpdateWriteArea( + struct ipmi_intf *intf, + uint8_t sequence, + uint8_t length, + uint8_t * pBuf + ) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t buffer[ IME_UPGRADE_BUFFER_SIZE + 1 ]; + +// printf("ImeUpdateWriteArea %i\n", sequence); + + if(length > IME_UPGRADE_BUFFER_SIZE) + return IME_ERROR; + + buffer[0] = sequence; + memcpy(&buffer[1], pBuf, length); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA2; + req.msg.data = buffer; + req.msg.data_len = length + 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdateWriteArea command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdateWriteArea command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + if( rsp->ccode == 0x80) // restart operation + return IME_RESTART; + else + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdateWriteArea command succeed"); + return IME_SUCCESS; +} + +static int ImeUpdateCloseArea( + struct ipmi_intf *intf, + uint32_t size, + uint16_t checksum + ) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t length = sizeof( uint32_t ) + sizeof( uint16_t ); + uint8_t buffer[ sizeof( uint32_t ) + sizeof( uint16_t ) ]; + + #ifdef OUTPUT_DEBUG + printf( "ImeUpdateCloseArea\n"); + #endif + + buffer[0] = (uint8_t)((size & 0x000000ff) >> 0); + buffer[1] = (uint8_t)((size & 0x0000ff00) >> 8); + buffer[2] = (uint8_t)((size & 0x00ff0000) >> 16); + buffer[3] = (uint8_t)((size & 0xff000000) >> 24); + + buffer[4] = (uint8_t)((checksum & 0x00ff) >> 0); + buffer[5] = (uint8_t)((checksum & 0xff00) >> 8); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA3; + req.msg.data = buffer; + req.msg.data_len = length; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdateCloseArea command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdateCloseArea command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdateCloseArea command succeed"); + return IME_SUCCESS; +} + +static int ImeUpdateGetStatus(struct ipmi_intf *intf, tImeStatus *pStatus ) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + tImeStatus *pGetStatus; + + memset(pStatus, 0, sizeof(tImeStatus)); + pStatus->update_state = IME_STATE_ABORTED; + + + #ifdef OUTPUT_DEBUG + printf("ImeUpdateGetStatus: "); + #endif + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA6; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdatePrepare command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdatePrepare command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdatePrepare command succeed"); + + pGetStatus = (tImeStatus *) rsp->data; + + memcpy( pStatus, pGetStatus, sizeof(tImeStatus)); + + #ifdef OUTPUT_DEBUG + printf("%x - ", pStatus->updateState); + + switch( pStatus->update_state ) + { + case IME_STATE_IDLE: + printf("IDLE\n"); + break; + case IME_STATE_UPDATE_REQUESTED: + printf("Update Requested\n"); + break; + case IME_STATE_UPDATE_IN_PROGRESS: + printf("Update in Progress\n"); + break; + case IME_STATE_SUCCESS: + printf("Update Success\n"); + break; + case IME_STATE_FAILED: + printf("Update Failed\n"); + break; + case IME_STATE_ROLLED_BACK: + printf("Update Rolled Back\n"); + break; + case IME_STATE_ABORTED: + printf("Update Aborted\n"); + break; + case IME_STATE_INIT_FAILED: + printf("Update Init Failed\n"); + break; + default: + printf("Unknown, reserved\n"); + break; + } + #endif + + return IME_SUCCESS; +} + +static int ImeUpdateGetCapabilities(struct ipmi_intf *intf, tImeCaps *pCaps ) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + tImeCaps * pGetCaps; + + memset(pCaps, 0, sizeof(tImeCaps)); + + + #ifdef OUTPUT_DEBUG + printf("ImeUpdateGetStatus: "); + #endif + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA7; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdatePrepare command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdatePrepare command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdatePrepare command succeed"); + + pGetCaps = (tImeCaps *) rsp->data; + + memcpy( pCaps, pGetCaps, sizeof(tImeCaps)); + + return IME_SUCCESS; +} + + +static int ImeUpdateRegisterUpdate(struct ipmi_intf *intf, tImeUpdateType type) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t buffer[ 2 ]; + + #ifdef OUTPUT_DEBUG + printf( "ImeUpdateRegisterUpdate\n"); + #endif + + buffer[0] = type; // Normal Update + buffer[1] = 0; // Flags, reserved + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA4; + req.msg.data = buffer; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "ImeUpdateRegisterUpdate command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "ImeUpdateRegisterUpdate command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "ImeUpdateRegisterUpdate command succeed"); + return IME_SUCCESS; +} + + + + +static int ImeUpdateShowStatus(struct ipmi_intf *intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + tImeStatus *pStatus; + + printf("ImeUpdateGetStatus: "); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x30; // OEM NetFn + req.msg.cmd = 0xA6; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "UpdatePrepare command failed"); + return IME_ERROR; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "UpdatePrepare command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return IME_ERROR; + } + + lprintf(LOG_DEBUG, "UpdatePrepare command succeed"); + + pStatus = (tImeStatus *) rsp->data ; + + + printf("image_status: %x - ", pStatus->image_status); + + printf("update_state: %x - ", pStatus->update_state); + + switch( pStatus->update_state ) + { + case IME_STATE_IDLE: + printf("IDLE\n"); + break; + case IME_STATE_UPDATE_REQUESTED: + printf("Update Requested\n"); + break; + case IME_STATE_UPDATE_IN_PROGRESS: + printf("Update in Progress\n"); + break; + case IME_STATE_SUCCESS: + printf("Update Success\n"); + break; + case IME_STATE_FAILED: + printf("Update Failed\n"); + break; + case IME_STATE_ROLLED_BACK: + printf("Update Rolled Back\n"); + break; + case IME_STATE_ABORTED: + printf("Update Aborted\n"); + break; + case IME_STATE_INIT_FAILED: + printf("Update Init Failed\n"); + break; + default: + printf("Unknown, reserved\n"); + break; + } + printf("update_attempt_status : %x\n", pStatus->update_attempt_status); + printf("rollback_attempt_status: %x\n", pStatus->rollback_attempt_status); + printf("update_type : %x\n", pStatus->update_type); + printf("dependent_flag : %x\n", pStatus->dependent_flag); + printf("free_area_size : %x\n", pStatus->free_area_size[0]); + printf(" : %x\n", pStatus->free_area_size[1]); + printf(" : %x\n", pStatus->free_area_size[2]); + printf(" : %x\n", pStatus->free_area_size[3]); + + return IME_SUCCESS; +} + + +static int ImeImageCtxFromFile( + char* imageFilename, + tImeUpdateImageCtx * pImageCtx + ) +{ + int rc = IME_SUCCESS; + FILE* pImageFile = fopen(imageFilename, "rb"); + + if ( pImageFile == NULL ) + { + lprintf(LOG_NOTICE,"Cannot open image file %s", imageFilename); + rc = IME_ERROR; + } + + if ( rc == IME_SUCCESS ) + { + /* Get the raw data in file */ + fseek(pImageFile, 0, SEEK_END); + pImageCtx->size = ftell(pImageFile); + if (pImageCtx->size <= 0) { + if (pImageCtx->size < 0) + lprintf(LOG_ERR, "Error seeking %s. %s\n", imageFilename, strerror(errno)); + rc = IME_ERROR; + fclose(pImageFile); + return rc; + } + pImageCtx->pData = malloc(sizeof(unsigned char)*pImageCtx->size); + rewind(pImageFile); + + if ( pImageCtx->pData != NULL ) + { + if (pImageCtx->size < fread(pImageCtx->pData, sizeof(unsigned char), + pImageCtx->size, pImageFile)) + rc = IME_ERROR; + } + else + { + rc = IME_ERROR; + } + } + + // Calculate checksum CRC8 + if ( rc == IME_SUCCESS ) + { + pImageCtx->crc8 = ImeCrc8(pImageCtx->size, pImageCtx->pData); + } + + + if( pImageFile != NULL) + { + fclose(pImageFile); + } + + return rc; +} + +static uint8_t ImeCrc8( uint32_t length, uint8_t * pBuf ) +{ + uint8_t crc = 0; + uint32_t bufCount; + + for ( bufCount = 0; bufCount < length; bufCount++ ) + { + uint8_t count; + + crc = crc ^ pBuf[bufCount]; + + for ( count = 0; count < 8; count++ ) + { + if (( crc & 0x80 ) != 0 ) + { + crc <<= 1; + crc ^= 0x07; + } + else + { + crc <<= 1; + } + } + } + + lprintf(LOG_DEBUG,"CRC8: %02xh\n", crc); + return crc; +} + + +static int ImeManualRollback(struct ipmi_intf *intf) +{ + int rc = IME_SUCCESS; + tImeStatus imeStatus; + time_t start,end,current; + + + rc = ImeUpdateRegisterUpdate(intf, IME_UPDTYPE_MANUAL_ROLLBACK); + ImeUpdateGetStatus(intf,&imeStatus); + + + if( + (rc == IME_SUCCESS) && + (imeStatus.update_state == IME_STATE_ROLLED_BACK) + ) + { + printf("Manual Rollback Succeed\n"); + return IME_SUCCESS; + } + else + { + printf("Manual Rollback Completed With Error\n"); + return IME_ERROR; + } +} + + + +static void ImePrintUsage(void) +{ + lprintf(LOG_NOTICE,"help - This help menu"); + lprintf(LOG_NOTICE,"info - Information about the present Intel ME"); + lprintf(LOG_NOTICE,"update <file> - Upgrade the ME firmware from received image <file>"); + lprintf(LOG_NOTICE,"rollback - Manual Rollback ME"); +// lprintf(LOG_NOTICE,"rollback - Rollback ME Firmware"); +} + + + +int ipmi_ime_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = IME_SUCCESS; + + lprintf(LOG_DEBUG,"ipmi_ime_main()"); + + + if ( (argc == 0) || (strcmp(argv[0], "help") == 0) ) + { + ImePrintUsage(); + } + else if ( (argc == 0) || (strcmp(argv[0], "info") == 0) ) + { + rc = ImeGetInfo(intf); + } + else if ( strcmp(argv[0], "update") == 0) + { + if(argc == 2) + { + lprintf(LOG_NOTICE,"Update using file: %s", argv[1]); + rc = ImeUpgrade(intf, argv[1]); + } + else + { + lprintf(LOG_ERROR,"File must be provided with this option, see help\n"); + rc = IME_ERROR; + } + } + else if ( (argc == 0) || (strcmp(argv[0], "rollback") == 0) ) + { + rc = ImeManualRollback(intf); + } + else + { + ImePrintUsage(); + } + + return rc; +} + + + diff --git a/lib/ipmi_isol.c b/lib/ipmi_isol.c new file mode 100644 index 0000000..0338e36 --- /dev/null +++ b/lib/ipmi_isol.c @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <sys/time.h> +#include <signal.h> +#include <unistd.h> + + +#include <termios.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_isol.h> + +static struct termios _saved_tio; +static int _in_raw_mode = 0; + +extern int verbose; + +#define ISOL_ESCAPE_CHARACTER '~' + +/* + * ipmi_get_isol_info + */ +static int ipmi_get_isol_info(struct ipmi_intf * intf, + struct isol_config_parameters * params) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[6]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = GET_ISOL_CONFIG; + req.msg.data = data; + req.msg.data_len = 4; + + /* GET ISOL ENABLED CONFIG */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_ENABLE_PARAM; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); + return -1; + } + if (rsp->ccode == 0xc1) { + lprintf(LOG_ERR, "IPMI v1.5 Serial Over Lan (ISOL) not supported!"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + params->enabled = rsp->data[1]; + + /* GET ISOL AUTHENTICATON CONFIG */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_AUTHENTICATION_PARAM; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + params->privilege_level = rsp->data[1]; + + /* GET ISOL BAUD RATE CONFIG */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_BAUD_RATE_PARAM; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + params->bit_rate = rsp->data[1]; + + return 0; +} + +static int ipmi_print_isol_info(struct ipmi_intf * intf) +{ + struct isol_config_parameters params = {0}; + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + if (csv_output) + { + printf("%s,", (params.enabled & 0x1)?"true": "false"); + printf("%s,", + val2str((params.privilege_level & 0xf), ipmi_privlvl_vals)); + printf("%s,", + val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals)); + } + else + { + printf("Enabled : %s\n", + (params.enabled & 0x1)?"true": "false"); + printf("Privilege Level : %s\n", + val2str((params.privilege_level & 0xf), ipmi_privlvl_vals)); + printf("Bit Rate (kbps) : %s\n", + val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals)); + } + + return 0; +} + +static int ipmi_isol_set_param(struct ipmi_intf * intf, + const char *param, + const char *value) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[6]; + struct isol_config_parameters params = {0}; + + /* We need other values to complete the request */ + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = SET_ISOL_CONFIG; + req.msg.data = data; + req.msg.data_len = 3; + + memset(data, 0, 6); + + /* + * enabled + */ + if (strcmp(param, "enabled") == 0) + { + data[1] = ISOL_ENABLE_PARAM; + if (strcmp(value, "true") == 0) + data[2] = 0x01; + else if (strcmp(value, "false") == 0) + data[2] = 0x00; + else { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are true and false"); + return -1; + } + } + + /* + * privilege-level + */ + else if (strcmp(param, "privilege-level") == 0) + { + data[1] = ISOL_AUTHENTICATION_PARAM; + if (! strcmp(value, "user")) + data[2] = 0x02; + else if (! strcmp(value, "operator")) + data[2] = 0x03; + else if (! strcmp(value, "admin")) + data[2] = 0x04; + else if (! strcmp(value, "oem")) + data[2] = 0x05; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem"); + return -1; + } + /* We need to mask bit7 from the fetched value */ + data[2] |= (params.privilege_level & 0x80) ? 0x80 : 0x00; + } + + /* + * bit-rate + */ + else if (strcmp(param, "bit-rate") == 0) + { + data[1] = ISOL_BAUD_RATE_PARAM; + if (strncmp(value, "9.6", 3) == 0) { + data[2] = 0x06; + } + else if (strncmp(value, "19.2", 4) == 0) { + data[2] = 0x07; + } + else if (strncmp(value, "38.4", 4) == 0) { + data[2] = 0x08; + } + else if (strncmp(value, "57.6", 4) == 0) { + data[2] = 0x09; + } + else if (strncmp(value, "115.2", 5) == 0) { + data[2] = 0x0A; + } + else { + lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", value); + lprintf(LOG_ERR, "Valid values are 9.6, 19.2, 38.4, 57.6 and 115.2"); + return -1; + } + } + else + { + lprintf(LOG_ERR, "Error: invalid ISOL parameter %s", param); + return -1; + } + + + /* + * Execute the request + */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting ISOL parameter '%s'", param); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error setting ISOL parameter '%s': %s", + param, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static void +leave_raw_mode(void) +{ + if (!_in_raw_mode) + return; + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 0; +} + + + +static void +enter_raw_mode(void) +{ + struct termios tio; + if (tcgetattr(fileno(stdin), &tio) == -1) { + perror("tcgetattr"); + return; + } + _saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF)\ + ; + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); + // #ifdef IEXTEN + tio.c_lflag &= ~IEXTEN; + // #endif + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 1; +} + + +static void +sendBreak(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + v2_payload.payload.sol_packet.character_count = 0; + v2_payload.payload.sol_packet.generate_break = 1; + + intf->send_sol(intf, &v2_payload); +} + +/* + * suspendSelf + * + * Put ourself in the background + * + * param bRestoreTty specifies whether we will put our self back + * in raw mode when we resume + */ +static void +suspendSelf(int bRestoreTty) +{ + leave_raw_mode(); + kill(getpid(), SIGTSTP); + + if (bRestoreTty) + enter_raw_mode(); +} + + + +/* + * printiSolEscapeSequences + * + * Send some useful documentation to the user + */ +static void +printiSolEscapeSequences(void) +{ + printf( + "%c?\n\ + Supported escape sequences:\n\ + %c. - terminate connection\n\ + %c^Z - suspend ipmitool\n\ + %c^X - suspend ipmitool, but don't restore tty on restart\n\ + %cB - send break\n\ + %c? - this message\n\ + %c%c - send the escape character by typing it twice\n\ + (Note that escapes are only recognized immediately after newline.)\n", + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER); +} + + + +/* + * output + * + * Send the specified data to stdout + */ +static void +output(struct ipmi_rs * rsp) +{ + if (rsp) + { + int i; + for (i = 0; i < rsp->data_len; ++i) + putc(rsp->data[i], stdout); + + fflush(stdout); + } +} + +/* + * ipmi_isol_deactivate + */ +static int +ipmi_isol_deactivate(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[6]; + struct isol_config_parameters params; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = ACTIVATE_ISOL; + req.msg.data = data; + req.msg.data_len = 5; + + memset(data, 0, 6); + data[0] = 0x00; /* Deactivate */ + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[5] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error deactivating ISOL"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error deactivating ISOL: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + /* response contain 4 additional bytes : 80 00 32 ff + Don't know what to use them for yet... */ + return 0; +} + +/* + * processiSolUserInput + * + * Act on user input into the ISOL session. The only reason this + * is complicated is that we have to process escape sequences. + * + * return 0 on success + * 1 if we should exit + * < 0 on error (BMC probably closed the session) + */ +static int +processiSolUserInput(struct ipmi_intf * intf, + uint8_t * input, + uint16_t buffer_length) +{ + static int escape_pending = 0; + static int last_was_cr = 1; + struct ipmi_v2_payload v2_payload; + int length = 0; + int retval = 0; + char ch; + int i; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + /* + * Our first order of business is to check the input for escape + * sequences to act on. + */ + for (i = 0; i < buffer_length; ++i) + { + ch = input[i]; + + if (escape_pending){ + escape_pending = 0; + + /* + * Process a possible escape sequence. + */ + switch (ch) { + case '.': + printf("%c. [terminated ipmitool]\n", ISOL_ESCAPE_CHARACTER); + retval = 1; + break; + case 'Z' - 64: + printf("%c^Z [suspend ipmitool]\n", ISOL_ESCAPE_CHARACTER); + suspendSelf(1); /* Restore tty back to raw */ + continue; + + case 'X' - 64: + printf("%c^X [suspend ipmitool]\n", ISOL_ESCAPE_CHARACTER); + suspendSelf(0); /* Don't restore to raw mode */ + continue; + + case 'B': + printf("%cb [send break]\n", ISOL_ESCAPE_CHARACTER); + sendBreak(intf); + continue; + + case '?': + printiSolEscapeSequences(); + continue; + default: + if (ch != ISOL_ESCAPE_CHARACTER) + v2_payload.payload.sol_packet.data[length++] = + ISOL_ESCAPE_CHARACTER; + v2_payload.payload.sol_packet.data[length++] = ch; + } + } + + else + { + if (last_was_cr && (ch == ISOL_ESCAPE_CHARACTER)) { + escape_pending = 1; + continue; + } + + v2_payload.payload.sol_packet.data[length++] = ch; + } + + + /* + * Normal character. Record whether it was a newline. + */ + last_was_cr = (ch == '\r' || ch == '\n'); + } + + /* + * If there is anything left to process we dispatch it to the BMC, + * send intf->session->sol_data.max_outbound_payload_size bytes + * at a time. + */ + if (length) + { + struct ipmi_rs * rsp; + + v2_payload.payload.sol_packet.flush_outbound = 1; /* Not sure if necessary ? */ + v2_payload.payload.sol_packet.character_count = length; + rsp = intf->send_sol(intf, &v2_payload); + + if (! rsp) { + lprintf(LOG_ERR, "Error sending SOL data"); + retval = -1; + } + + /* If the sequence number is set we know we have new data */ + else if ((rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + output(rsp); + } + return retval; +} + +/* + * ipmi_isol_red_pill + */ +static int +ipmi_isol_red_pill(struct ipmi_intf * intf) +{ + char * buffer; + int numRead; + int bShouldExit = 0; + int bBmcClosedSession = 0; + fd_set read_fds; + struct timeval tv; + int retval; + int buffer_size = 255; + int timedout = 0; + + buffer = (char*)malloc(buffer_size); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + + enter_raw_mode(); + + while (! bShouldExit) + { + FD_ZERO(&read_fds); + FD_SET(0, &read_fds); + FD_SET(intf->fd, &read_fds); + + /* Wait up to half a second */ + tv.tv_sec = 0; + tv.tv_usec = 500000; + + retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv); + + if (retval) + { + if (retval == -1) + { + /* ERROR */ + perror("select"); + return -1; + } + + timedout = 0; + + /* + * Process input from the user + */ + if (FD_ISSET(0, &read_fds)) + { + memset(buffer, 0, buffer_size); + numRead = read(fileno(stdin), + buffer, + buffer_size); + + if (numRead > 0) + { + int rc = processiSolUserInput(intf, buffer, numRead); + + if (rc) + { + if (rc < 0) + bShouldExit = bBmcClosedSession = 1; + else + bShouldExit = 1; + } + } + else + { + bShouldExit = 1; + } + } + + + /* + * Process input from the BMC + */ + else if (FD_ISSET(intf->fd, &read_fds)) + { + struct ipmi_rs * rs = intf->recv_sol(intf); + if (! rs) + { + bShouldExit = bBmcClosedSession = 1; + } + else + output(rs); + } + + + /* + * ERROR in select + */ + else + { + lprintf(LOG_ERR, "Error: Select returned with nothing to read"); + bShouldExit = 1; + } + } + else + { + if ((++timedout) == 20) /* Every 10 seconds we send a keepalive */ + { + intf->keepalive(intf); + timedout = 0; + } + } + } + + leave_raw_mode(); + + if (bBmcClosedSession) + { + lprintf(LOG_ERR, "SOL session closed by BMC"); + } + else + ipmi_isol_deactivate(intf); + + return 0; +} + +/* + * ipmi_isol_activate + */ +static int +ipmi_isol_activate(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[6]; + struct isol_config_parameters params; + + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + if (!(params.enabled & 0x1)) { + lprintf(LOG_ERR, "ISOL is not enabled!"); + return -1; + } + + /* + * Setup a callback so that the lanplus processing knows what + * to do with packets that come unexpectedly (while waiting for + * an ACK, perhaps. + */ + intf->session->sol_data.sol_input_handler = output; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = ACTIVATE_ISOL; + req.msg.data = data; + req.msg.data_len = 5; + + memset(data, 0, 6); + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[5] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (NULL != rsp) { + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 4) { + break; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "in ISOL activation response", + rsp->data_len); + return -1; + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: ISOL already active on another session"); + return -1; + case 0x81: + lprintf(LOG_ERR, "Info: ISOL disabled"); + return -1; + case 0x82: + lprintf(LOG_ERR, "Info: ISOL activation limit reached"); + return -1; + default: + lprintf(LOG_ERR, "Error activating ISOL: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } else { + lprintf(LOG_ERR, "Error: No response activating ISOL"); + return -1; + } + + /* response contain 4 additional bytes : 80 01 32 ff + Don't know what to use them for yet... */ + + printf("[SOL Session operational. Use %c? for help]\n", + ISOL_ESCAPE_CHARACTER); + + /* + * At this point we are good to go with our SOL session. We + * need to listen to + * 1) STDIN for user input + * 2) The FD for incoming SOL packets + */ + if (ipmi_isol_red_pill(intf)) { + lprintf(LOG_ERR, "Error in SOL session"); + return -1; + } + + return 0; +} + +static void print_isol_set_usage(void) { + lprintf(LOG_NOTICE, "\nISOL set parameters and values: \n"); + lprintf(LOG_NOTICE, " enabled true | false"); + lprintf(LOG_NOTICE, " privilege-level user | operator | admin | oem"); + lprintf(LOG_NOTICE, " bit-rate " + "9.6 | 19.2 | 38.4 | 57.6 | 115.2"); + lprintf(LOG_NOTICE, ""); +} + +static void print_isol_usage(void) { + lprintf(LOG_NOTICE, "ISOL Commands: info"); + lprintf(LOG_NOTICE, " set <parameter> <setting>"); + lprintf(LOG_NOTICE, " activate"); +} + +int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int ret = 0; + + /* + * Help + */ + if (!argc || !strncmp(argv[0], "help", 4)) + print_isol_usage(); + + /* + * Info + */ + else if (!strncmp(argv[0], "info", 4)) { + ret = ipmi_print_isol_info(intf); + } + + /* + * Set a parameter value + */ + else if (!strncmp(argv[0], "set", 3)) { + if (argc < 3) { + print_isol_set_usage(); + return -1; + } + ret = ipmi_isol_set_param(intf, argv[1], argv[2]); + } + + /* + * Activate + */ + else if (!strncmp(argv[0], "activate", 8)) { + ret = ipmi_isol_activate(intf); + } + + else { + print_isol_usage(); + ret = -1; + } + + return ret; +} diff --git a/lib/ipmi_kontronoem.c b/lib/ipmi_kontronoem.c new file mode 100644 index 0000000..c154eda --- /dev/null +++ b/lib/ipmi_kontronoem.c @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2004 Kontron Canada, Inc. All Rights Reserved. + * + * Base on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Tue Mar 7 14:36:12 2006 + * <stephane.filion@ca.kontron.com> + * + * This code implements an Kontron OEM proprietary commands. + */ + + +#include <string.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_fru.h> + + +extern int verbose; +extern int read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, + uint8_t id, uint32_t offset, uint32_t length, + uint8_t *frubuf); +extern int write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, + unsigned char id, unsigned int soffset, + unsigned int doffset, unsigned int length, + unsigned char *pFrubuf); + +extern char * get_fru_area_str(uint8_t * data, uint32_t * offset); + + + +static void ipmi_kontron_help(void); +static int ipmi_kontron_set_serial_number(struct ipmi_intf * intf); +static int ipmi_kontron_set_mfg_date (struct ipmi_intf * intf); + +static void ipmi_kontron_nextboot_help(void); +static int ipmi_kontron_nextboot_set(struct ipmi_intf * intf, + int argc, char **argv); + +static int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf * intf, + unsigned char channel, + unsigned char size); + +int +ipmi_kontronoem_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc == 0) + ipmi_kontron_help(); + else if (strncmp(argv[0], "help", 4) == 0) + ipmi_kontron_help(); + + else if (!strncmp(argv[0], "setsn", 5)) + { + if(argc >= 1) + { + if(ipmi_kontron_set_serial_number(intf) > 0) + { + printf("FRU serial number setted successfully\n"); + } + else + { + printf("FRU serial number set failed\n"); + } + } + else + { + printf("fru setsn\n"); + } + } + else if (!strncmp(argv[0], "setmfgdate", 5)) + { + if(argc >= 1) + { + if(ipmi_kontron_set_mfg_date(intf) > 0) + { + printf("FRU manufacturing date setted successfully\n"); + } + else + { + printf("FRU manufacturing date set failed\n"); + } + } + else + { + printf("fru setmfgdate\n"); + } + } + else if (!strncmp(argv[0], "nextboot", sizeof("nextboot")-1)) + { + if (argc > 1) + { + if ((rc = ipmi_kontron_nextboot_set(intf, argc-1, argv+1)) == 0) + { + printf("Nextboot set successfully\n"); + } + else + { + printf("Nextboot set failed\n"); + } + } + else + { + ipmi_kontron_nextboot_help(); + } + } + + else + { + printf("Invalid Kontron command: %s", argv[0]); + ipmi_kontron_help(); + rc = -1; + } + + return rc; +} + + +static void ipmi_kontron_help(void) +{ + printf("Kontron Commands: setsn setmfgdate nextboot\n"); +} + + +int ipmi_kontronoem_set_large_buffer(struct ipmi_intf * intf, unsigned char size) +{ + uint8_t error_occurs = 0; + uint32_t prev_target_addr = intf->target_addr ; + + if ( intf->target_addr > 0 && (intf->target_addr != intf->my_addr) ) + { + intf->target_addr = intf->my_addr; + + printf("Set local big buffer\n"); + if(ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, size ) == 0) + { + printf("Set local big buffer:success\n"); + } + else + { + error_occurs = 1; + } + + if (error_occurs == 0) + { + if(ipmi_kontronoem_send_set_large_buffer( intf, 0x00, size ) == 0) + { + printf("IPMB was set\n"); + } + else + { + /* Revert back the previous set large buffer */ + error_occurs = 1; + ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 ); + } + } + + /* Restore target address */ + intf->target_addr = prev_target_addr; + } + + if (error_occurs == 0) + { + if(ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, size ) == 0) + { + //printf("Set remote big buffer\n"); + } + else + { + if ( intf->target_addr > 0 && (intf->target_addr != intf->my_addr) ) + { + /* Error occurs revert back the previous set large buffer*/ + intf->target_addr = intf->my_addr; + + //ipmi_kontronoem_send_set_large_buffer( intf, 0x00, 0 ); + ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 ); + + intf->target_addr = prev_target_addr; + } + } + } + return error_occurs; +} + + +int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf * intf, unsigned char channel,unsigned char size) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + int i; + + memset(msg_data, 0, sizeof(msg_data)); + msg_data[0] = channel/*0x0e*/; // Currently running interface + msg_data[1] = size; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x3E; /* OEM NetFn */ + req.msg.cmd = 0x82; /* Set Channel Buffer Length - OEM */ + req.msg.data = msg_data; + req.msg.data_len = 2; + + req.msg.lun = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf("Cannot send large buffer command\n"); + return(-1); + } + if (rsp->ccode > 0) + { + printf("Invalid length for the selected interface (%s) %d\n", + val2str(rsp->ccode, completion_code_vals), rsp->ccode); + return(-1); + } + return 0; +} + + +/* ipmi_fru_set_serial_number - Set the Serial Number in FRU + * + * @intf: ipmi interface + * @id: fru id + * + * returns -1 on error + * returns 1 if successful + */ +static int +ipmi_kontron_set_serial_number(struct ipmi_intf * intf) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + char *sn; + uint8_t sn_size, checksum; + uint8_t *fru_data; + char *fru_area; + uint32_t fru_data_offset, fru_data_offset_tmp, board_sec_len, prod_sec_len, i; + + sn = NULL; + fru_data = NULL; + + memset(msg_data, 0, 4); + msg_data[0] = 0xb4; + msg_data[1] = 0x90; + msg_data[2] = 0x91; + msg_data[3] = 0x8b; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x3E; + req.msg.cmd = 0x0C; + req.msg.data = msg_data; + req.msg.data_len = 4; + + + /* Set Lun, necessary for this oem command */ + req.msg.lun = 0x03; + + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf(" Device not present (No Response)\n"); + return -11; + } + + if (rsp->ccode > 0) + { + printf(" This option is not implemented for this board\n"); + return -1; + } + + sn_size = rsp->data_len; + + sn = malloc(sn_size + 1); + + if(sn == NULL) + { + printf("Out of memory!"); + return -1; + } + + memset(sn, 0, sn_size + 1); + memcpy(sn, rsp->data, sn_size); + + if(verbose >= 1) + { + printf("Original serial number is : [%s]\n", sn); + } + + + + memset(msg_data, 0, 4); + msg_data[0] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + free(sn); + sn = NULL; + return -1; + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + free(sn); + sn = NULL; + return(-1); + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + if (fru.size < 1) { + printf(" Invalid FRU size %d", fru.size); + free(sn); + sn = NULL; + return -1; + } + + /* + * retrieve the FRU header + */ + msg_data[0] = 0; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf(" Device not present (No Response)\n"); + free(sn); + sn = NULL; + return(-1); + } + if (rsp->ccode > 0) + { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + free(sn); + sn = NULL; + return(-1); + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 1) + { + printf(" Unknown FRU header version 0x%02x", + header.version); + free(sn); + sn = NULL; + return(-1); + } + + /* Set the Board Section */ + board_sec_len = (header.offset.product * 8) - (header.offset.board * 8); + + + fru_data = malloc( fru.size); + + if(fru_data == NULL) + { + printf("Out of memory!"); + free(sn); + sn = NULL; + return(-1); + } + + memset(fru_data, 0, fru.size); + if(read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8) ,board_sec_len , fru_data) < 0) + { + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + return(-1); + } + + /*Position at Board Manufacturer*/ + fru_data_offset = (header.offset.board * 8) + 6; + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + /*Position at Board Product Name*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + fru_data_offset_tmp = fru_data_offset; + + /*Position at Serial Number*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp); + + fru_data_offset ++; + + if(strlen(fru_area) != sn_size) + { + printf("The length of the serial number in the FRU Board Area is wrong.\n"); + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + return(-1); + } + + /* Copy the new serial number in the board section saved in memory*/ + memcpy(fru_data + fru_data_offset, sn, sn_size); + + checksum = 0; + /* Calculate Header Checksum */ + for( i = (header.offset.board * 8); i < (((header.offset.board * 8)+board_sec_len) - 2) ; i ++ ) + { + checksum += fru_data[i]; + } + checksum = (~checksum) + 1; + + + fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum; + + /* Write the new FRU Board section */ + if(write_fru_area(intf, &fru, 0, (header.offset.board * 8), (header.offset.board * 8), board_sec_len, fru_data) < 0) + { + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + return(-1); + } + + /* Set the Product Section */ + prod_sec_len = (header.offset.multi * 8) - (header.offset.product * 8); + + if(read_fru_area(intf ,&fru ,0 ,(header.offset.product * 8) ,prod_sec_len , fru_data) < 0) + { + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + return(-1); + } + + /*Position at Product Manufacturer*/ + fru_data_offset = (header.offset.product * 8) + 3; + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + /*Position at Product Name*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + /*Position at Product Part*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + /*Position at Product Version*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset); + + + + fru_data_offset_tmp = fru_data_offset; + + /*Position at Serial Number*/ + fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp); + + fru_data_offset ++; + + if(strlen(fru_area) != sn_size) + { + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + printf("The length of the serial number in the FRU Product Area is wrong.\n"); + return(-1); + + } + + /* Copy the new serial number in the product section saved in memory*/ + memcpy(fru_data + fru_data_offset, sn, sn_size); + + checksum = 0; + /* Calculate Header Checksum */ + for( i = (header.offset.product * 8); i < (((header.offset.product * 8)+prod_sec_len) - 2) ; i ++ ) + { + checksum += fru_data[i]; + } + checksum = (~checksum) + 1; + + + fru_data[(header.offset.product * 8)+prod_sec_len - 1] = checksum; + + /* Write the new FRU Board section */ + if(write_fru_area(intf, &fru, 0, (header.offset.product * 8), (header.offset.product * 8), prod_sec_len, fru_data) < 0) + { + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + return -1; + } + + free(sn); + sn = NULL; + free(fru_data); + fru_data = NULL; + + return(1); + +} + + +/* ipmi_fru_set_mfg_date - Set the Manufacturing Date in FRU + * + * @intf: ipmi interface + * @id: fru id + * + * returns -1 on error + * returns 1 if successful + */ +static int +ipmi_kontron_set_mfg_date (struct ipmi_intf * intf) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct fru_info fru; + struct fru_header header; + uint8_t msg_data[4]; + uint8_t mfg_date[3]; + + uint32_t board_sec_len, i; + uint8_t *fru_data, checksum; + + + + + memset(msg_data, 0, 4); + msg_data[0] = 0xb4; + msg_data[1] = 0x90; + msg_data[2] = 0x91; + msg_data[3] = 0x8b; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x3E; + req.msg.cmd = 0x0E; + req.msg.data = msg_data; + req.msg.data_len = 4; + + /* Set Lun temporary, necessary for this oem command */ + req.msg.lun = 0x03; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf("Device not present (No Response)\n"); + return(-1); + } + + if (rsp->ccode > 0) + { + printf("This option is not implemented for this board\n"); + return(-1); + } + + if(rsp->data_len != 3) + { + printf("Invalid response for the Manufacturing date\n"); + return(-1); + } + + memset(mfg_date, 0, 3); + memcpy(mfg_date, rsp->data, 3); + + memset(msg_data, 0, 4); + msg_data[0] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_INFO; + req.msg.data = msg_data; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf(" Device not present (No Response)\n"); + return(-1); + } + if (rsp->ccode > 0) { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return(-1); + } + + memset(&fru, 0, sizeof(fru)); + fru.size = (rsp->data[1] << 8) | rsp->data[0]; + fru.access = rsp->data[2] & 0x1; + + if (fru.size < 1) { + printf(" Invalid FRU size %d", fru.size); + return(-1); + } + + /* + * retrieve the FRU header + */ + msg_data[0] = 0; + msg_data[1] = 0; + msg_data[2] = 0; + msg_data[3] = 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_FRU_DATA; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf(" Device not present (No Response)\n"); + return(-1); + } + if (rsp->ccode > 0) + { + printf(" Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return(-1); + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "FRU DATA"); + + memcpy(&header, rsp->data + 1, 8); + + if (header.version != 1) + { + printf(" Unknown FRU header version 0x%02x", + header.version); + return(-1); + } + + + board_sec_len = (header.offset.product * 8) - (header.offset.board * 8); + + fru_data = malloc( fru.size); + + if(fru_data == NULL) + { + printf("Out of memory!"); + return(-1); + } + + memset(fru_data, 0, fru.size); + if(read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8) ,board_sec_len , fru_data) < 0) + { + free(fru_data); + fru_data = NULL; + return(-1); + } + + /* Copy the new manufacturing date in the board section saved in memory*/ + memcpy(fru_data + (header.offset.board * 8) + 3, mfg_date, 3); + + checksum = 0; + /* Calculate Header Checksum */ + for( i = (header.offset.board * 8); i < (((header.offset.board * 8)+board_sec_len) - 2) ; i ++ ) + { + checksum += fru_data[i]; + } + checksum = (~checksum) + 1; + + fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum; + + /* Write the new FRU Board section */ + if(write_fru_area(intf, &fru, 0, (header.offset.board * 8), (header.offset.board * 8), board_sec_len, fru_data) < 0) + { + free(fru_data); + fru_data = NULL; + return(-1); + } + + free(fru_data); + fru_data = NULL; + return(1); +} + + +static char *bootdev[] = {"BIOS", "FDD", "HDD", "CDROM", "network", 0}; + +static void +ipmi_kontron_nextboot_help(void) +{ + int i; + printf("nextboot <device>\n" + "Supported devices:\n"); + for (i = 0; bootdev[i] != 0; i++) { + printf("- %s\n", bootdev[i]); + } +} + +/* ipmi_kontron_next_boot_set - Select the next boot order on CP6012 + * + * @intf: ipmi interface + * @id: fru id + * + * returns -1 on error + * returns 1 if successful + */ +static int +ipmi_kontron_nextboot_set(struct ipmi_intf * intf, int argc, char **argv) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint8_t msg_data[8]; + int i; + + memset(msg_data, 0, sizeof(msg_data)); + msg_data[0] = 0xb4; + msg_data[1] = 0x90; + msg_data[2] = 0x91; + msg_data[3] = 0x8b; + msg_data[4] = 0x9d; + msg_data[5] = 0xFF; + msg_data[6] = 0xFF; /* any */ + + for (i = 0; bootdev[i] != 0; i++) { + if (strcmp(argv[0], bootdev[i]) == 0) { + msg_data[5] = i; + break; + } + } + + /* Invalid device selected? */ + if (msg_data[5] == 0xFF) { + printf("Unknown boot device: %s\n", argv[0]); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x3E; + req.msg.cmd = 0x02; + req.msg.data = msg_data; + req.msg.data_len = 7; + + /* Set Lun temporary, necessary for this oem command */ + req.msg.lun = 0x03; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + printf("Device not present (No Response)\n"); + return(-1); + } + if (rsp->ccode > 0) { + printf("Device not present (%s)\n", + val2str(rsp->ccode, completion_code_vals)); + return(-1); + } + return 0; +} + diff --git a/lib/ipmi_lanp.c b/lib/ipmi_lanp.c new file mode 100644 index 0000000..060e753 --- /dev/null +++ b/lib/ipmi_lanp.c @@ -0,0 +1,2352 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <setjmp.h> +#include <netdb.h> +#include <limits.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_channel.h> + +extern int verbose; + +/* is_lan_channel - Check if channel is LAN medium + * + * return 1 if channel is LAN + * return 0 if channel is not LAN + * + * @intf: ipmi interface handle + * @chan: channel number to check + */ +static int +is_lan_channel(struct ipmi_intf * intf, uint8_t chan) +{ + uint8_t medium; + + if (chan < 1 || chan > IPMI_CHANNEL_NUMBER_MAX) + return 0; + + medium = ipmi_get_channel_medium(intf, chan); + + if (medium == IPMI_CHANNEL_MEDIUM_LAN || + medium == IPMI_CHANNEL_MEDIUM_LAN_OTHER) + return 1; + + return 0; +} + +/* find_lan_channel - Find first channel that is LAN + * + * return channel number if successful + * return 0 if no lan channel found, which is not a valid LAN channel + * + * @intf: ipmi interface handle + * @start: channel number to start searching from + */ +static uint8_t +find_lan_channel(struct ipmi_intf * intf, uint8_t start) +{ + uint8_t chan = 0; + + for (chan = start; chan < IPMI_CHANNEL_NUMBER_MAX; chan++) { + if (is_lan_channel(intf, chan)) { + return chan; + } + } + return 0; +} + +/* get_lan_param_select - Query BMC for LAN parameter data + * + * return pointer to lan_param if successful + * if parameter not supported then + * return pointer to lan_param with + * lan_param->data == NULL and lan_param->data_len == 0 + * return NULL on error + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + * @select: lan parameter set selector + */ +static struct lan_param * +get_lan_param_select(struct ipmi_intf * intf, uint8_t chan, int param, int select) +{ + struct lan_param * p = NULL; + struct ipmi_rs * rsp; + struct ipmi_rq req; + int i = 0; + uint8_t msg_data[4]; + + for (i = 0; ipmi_lan_params[i].cmd != (-1); i++) { + if (ipmi_lan_params[i].cmd == param) { + p = &ipmi_lan_params[i]; + break; + } + } + + if (p == NULL) { + lprintf(LOG_INFO, "Get LAN Parameter failed: Unknown parameter."); + return NULL; + } + + msg_data[0] = chan; + msg_data[1] = p->cmd; + msg_data[2] = select; + msg_data[3] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_GET_CONFIG; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_INFO, "Get LAN Parameter '%s' command failed", p->desc); + return NULL; + } + + switch (rsp->ccode) + { + case 0x00: /* successful */ + break; + + case 0x80: /* parameter not supported */ + case 0xc9: /* parameter out of range */ + case 0xcc: /* invalid data field in request */ + + /* these completion codes usually mean parameter not supported */ + lprintf(LOG_INFO, "Get LAN Parameter '%s' command failed: %s", + p->desc, val2str(rsp->ccode, completion_code_vals)); + p->data = NULL; + p->data_len = 0; + return p; + + default: + + /* other completion codes are treated as error */ + lprintf(LOG_INFO, "Get LAN Parameter '%s' command failed: %s", + p->desc, val2str(rsp->ccode, completion_code_vals)); + return NULL; + } + + p->data = rsp->data + 1; + p->data_len = rsp->data_len - 1; + + return p; +} + +/* get_lan_param - Query BMC for LAN parameter data + * + * return pointer to lan_param if successful + * if parameter not supported then + * return pointer to lan_param with + * lan_param->data == NULL and lan_param->data_len == 0 + * return NULL on error + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + */ +static struct lan_param * +get_lan_param(struct ipmi_intf * intf, uint8_t chan, int param) +{ + return get_lan_param_select(intf, chan, param, 0); +} + +/* set_lan_param_wait - Wait for Set LAN Parameter command to complete + * + * On some systems this can take unusually long so we wait for the write + * to take effect and verify that the data was written successfully + * before continuing or retrying. + * + * returns 0 on success + * returns -1 on error + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + * @data: lan parameter data + * @len: length of lan parameter data + */ +static int +set_lan_param_wait(struct ipmi_intf * intf, uint8_t chan, + int param, uint8_t * data, int len) +{ + struct lan_param * p; + int retry = 10; /* 10 retries */ + + lprintf(LOG_DEBUG, "Waiting for Set LAN Parameter to complete..."); + if (verbose > 1) + printbuf(data, len, "SET DATA"); + + for (;;) { + p = get_lan_param(intf, chan, param); + if (p == NULL) { + sleep(IPMI_LANP_TIMEOUT); + if (retry-- == 0) + return -1; + continue; + } + if (verbose > 1) + printbuf(p->data, p->data_len, "READ DATA"); + if (p->data_len != len) { + sleep(IPMI_LANP_TIMEOUT); + if (retry-- == 0) { + lprintf(LOG_WARNING, "Mismatched data lengths: %d != %d", + p->data_len, len); + return -1; + } + continue; + } + if (memcmp(data, p->data, len) != 0) { + sleep(IPMI_LANP_TIMEOUT); + if (retry-- == 0) { + lprintf(LOG_WARNING, "LAN Parameter Data does not match! " + "Write may have failed."); + return -1; + } + continue; + } + break; + } + return 0; +} + +/* __set_lan_param - Write LAN Parameter data to BMC + * + * This function does the actual work of writing the LAN parameter + * to the BMC and calls set_lan_param_wait() if requested. + * + * returns 0 on success + * returns -1 on error + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + * @data: lan parameter data + * @len: length of lan parameter data + * @wait: whether to wait for write completion + */ +static int +__set_lan_param(struct ipmi_intf * intf, uint8_t chan, + int param, uint8_t * data, int len, int wait) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[32]; + + if (param < 0) + return -1; + + msg_data[0] = chan; + msg_data[1] = param; + + memcpy(&msg_data[2], data, len); + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_SET_CONFIG; + req.msg.data = msg_data; + req.msg.data_len = len+2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set LAN Parameter failed"); + return -1; + } + if ((rsp->ccode > 0) && (wait != 0)) { + lprintf(LOG_DEBUG, "Warning: Set LAN Parameter failed: %s", + val2str(rsp->ccode, completion_code_vals)); + if (rsp->ccode == 0xcc) { + /* retry hack for invalid data field ccode */ + int retry = 10; /* 10 retries */ + lprintf(LOG_DEBUG, "Retrying..."); + for (;;) { + if (retry-- == 0) + break; + sleep(IPMI_LANP_TIMEOUT); + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + continue; + if (rsp->ccode > 0) + continue; + return set_lan_param_wait(intf, chan, param, data, len); + } + } + else if (rsp->ccode != 0xff) { + /* let 0xff ccode continue */ + return -1; + } + } + + if (wait == 0) + return 0; + return set_lan_param_wait(intf, chan, param, data, len); +} + +/* ipmi_lanp_lock_state - Retrieve set-in-progress status + * + * returns one of: + * IPMI_LANP_WRITE_UNLOCK + * IPMI_LANP_WRITE_LOCK + * IPMI_LANP_WRITE_COMMIT + * -1 on error/if not supported + * + * @intf: ipmi interface handle + * @chan: ipmi channel + */ +static int +ipmi_lanp_lock_state(struct ipmi_intf * intf, uint8_t chan) +{ + struct lan_param * p; + p = get_lan_param(intf, chan, IPMI_LANP_SET_IN_PROGRESS); + if (p == NULL) + return -1; + if (p->data == NULL) + return -1; + return (p->data[0] & 3); +} + +/* ipmi_lanp_lock - Lock set-in-progress bits for our use + * + * Write to the Set-In-Progress LAN parameter to indicate + * to other management software that we are modifying parameters. + * + * No meaningful return value because this is an optional + * requirement in IPMI spec and not found on many BMCs. + * + * @intf: ipmi interface handle + * @chan: ipmi channel + */ +static void +ipmi_lanp_lock(struct ipmi_intf * intf, uint8_t chan) +{ + uint8_t val = IPMI_LANP_WRITE_LOCK; + int retry = 3; + + for (;;) { + int state = ipmi_lanp_lock_state(intf, chan); + if (state == -1) + break; + if (state == val) + break; + if (retry-- == 0) + break; + __set_lan_param(intf, chan, IPMI_LANP_SET_IN_PROGRESS, + &val, 1, 0); + } +} + +/* ipmi_lanp_unlock - Unlock set-in-progress bits + * + * Write to the Set-In-Progress LAN parameter, first with + * a "commit" instruction and then unlocking it. + * + * No meaningful return value because this is an optional + * requirement in IPMI spec and not found on many BMCs. + * + * @intf: ipmi interface handle + * @chan: ipmi channel + */ +static void +ipmi_lanp_unlock(struct ipmi_intf * intf, uint8_t chan) +{ + uint8_t val = IPMI_LANP_WRITE_COMMIT; + int rc; + + rc = __set_lan_param(intf, chan, IPMI_LANP_SET_IN_PROGRESS, &val, 1, 0); + if (rc < 0) { + lprintf(LOG_DEBUG, "LAN Parameter Commit not supported"); + } + + val = IPMI_LANP_WRITE_UNLOCK; + __set_lan_param(intf, chan, IPMI_LANP_SET_IN_PROGRESS, &val, 1, 0); +} + +/* set_lan_param - Wrap LAN parameter write with set-in-progress lock + * + * Returns value from __set_lan_param() + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + * @data: lan parameter data + * @len: length of lan parameter data + */ +static int +set_lan_param(struct ipmi_intf * intf, uint8_t chan, + int param, uint8_t * data, int len) +{ + int rc; + ipmi_lanp_lock(intf, chan); + rc = __set_lan_param(intf, chan, param, data, len, 1); + ipmi_lanp_unlock(intf, chan); + return rc; +} + +/* set_lan_param_nowait - Wrap LAN parameter write without set-in-progress lock + * + * Returns value from __set_lan_param() + * + * @intf: ipmi interface handle + * @chan: ipmi channel + * @param: lan parameter id + * @data: lan parameter data + * @len: length of lan parameter data + */ +static int +set_lan_param_nowait(struct ipmi_intf * intf, uint8_t chan, + int param, uint8_t * data, int len) +{ + int rc; + ipmi_lanp_lock(intf, chan); + rc = __set_lan_param(intf, chan, param, data, len, 0); + ipmi_lanp_unlock(intf, chan); + return rc; +} + +static int +lan_set_arp_interval(struct ipmi_intf * intf, uint8_t chan, uint8_t ival) +{ + struct lan_param *lp; + uint8_t interval = 0; + int rc = 0; + + lp = get_lan_param(intf, chan, IPMI_LANP_GRAT_ARP); + if (lp == NULL) + return -1; + if (lp->data == NULL) + return -1; + + if (ival != 0) { + if (((UINT8_MAX - 1) / 2) < ival) { + lprintf(LOG_ERR, "Given ARP interval '%u' is too big.", ival); + return (-1); + } + interval = (ival * 2) - 1; + rc = set_lan_param(intf, chan, IPMI_LANP_GRAT_ARP, &interval, 1); + } else { + interval = lp->data[0]; + } + + printf("BMC-generated Gratuitous ARP interval: %.1f seconds\n", + (float)((interval + 1) / 2)); + + return rc; +} + +static int +lan_set_arp_generate(struct ipmi_intf * intf, + uint8_t chan, uint8_t ctl) +{ + struct lan_param *lp; + uint8_t data; + + lp = get_lan_param(intf, chan, IPMI_LANP_BMC_ARP); + if (lp == NULL) + return -1; + if (lp->data == NULL) + return -1; + data = lp->data[0]; + + /* set arp generate bitflag */ + if (ctl == 0) + data &= ~0x1; + else + data |= 0x1; + + printf("%sabling BMC-generated Gratuitous ARPs\n", ctl ? "En" : "Dis"); + return set_lan_param(intf, chan, IPMI_LANP_BMC_ARP, &data, 1); +} + +static int +lan_set_arp_respond(struct ipmi_intf * intf, + uint8_t chan, uint8_t ctl) +{ + struct lan_param *lp; + uint8_t data; + + lp = get_lan_param(intf, chan, IPMI_LANP_BMC_ARP); + if (lp == NULL) + return -1; + if (lp->data == NULL) + return -1; + data = lp->data[0]; + + /* set arp response bitflag */ + if (ctl == 0) + data &= ~0x2; + else + data |= 0x2; + + printf("%sabling BMC-generated ARP responses\n", ctl ? "En" : "Dis"); + return set_lan_param(intf, chan, IPMI_LANP_BMC_ARP, &data, 1); +} + + +static char priv_level_to_char(unsigned char priv_level) +{ + char ret = 'X'; + + switch (priv_level) + { + case IPMI_SESSION_PRIV_CALLBACK: + ret = 'c'; + break; + case IPMI_SESSION_PRIV_USER: + ret = 'u'; + break; + case IPMI_SESSION_PRIV_OPERATOR: + ret = 'o'; + break; + case IPMI_SESSION_PRIV_ADMIN: + ret = 'a'; + break; + case IPMI_SESSION_PRIV_OEM: + ret = 'O'; + break; + } + + return ret; +} + + +static int +ipmi_lan_print(struct ipmi_intf * intf, uint8_t chan) +{ + struct lan_param * p; + int rc = 0; + + if (chan < 1 || chan > IPMI_CHANNEL_NUMBER_MAX) { + lprintf(LOG_ERR, "Invalid Channel %d", chan); + return -1; + } + + /* find type of channel and only accept 802.3 LAN */ + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", chan); + return -1; + } + + p = get_lan_param(intf, chan, IPMI_LANP_SET_IN_PROGRESS); + if (p == NULL) + return -1; + if (p->data != NULL) { + printf("%-24s: ", p->desc); + p->data[0] &= 3; + switch (p->data[0]) { + case 0: + printf("Set Complete\n"); + break; + case 1: + printf("Set In Progress\n"); + break; + case 2: + printf("Commit Write\n"); + break; + case 3: + printf("Reserved\n"); + break; + default: + printf("Unknown\n"); + } + } + + p = get_lan_param(intf, chan, IPMI_LANP_AUTH_TYPE); + if (p == NULL) + return -1; + if (p->data != NULL) { + printf("%-24s: %s%s%s%s%s\n", p->desc, + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + } + + p = get_lan_param(intf, chan, IPMI_LANP_AUTH_TYPE_ENABLE); + if (p == NULL) + return -1; + if (p->data != NULL) { + printf("%-24s: Callback : %s%s%s%s%s\n", p->desc, + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[0] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + printf("%-24s: User : %s%s%s%s%s\n", "", + (p->data[1] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[1] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[1] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[1] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[1] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + printf("%-24s: Operator : %s%s%s%s%s\n", "", + (p->data[2] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[2] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[2] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[2] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[2] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + printf("%-24s: Admin : %s%s%s%s%s\n", "", + (p->data[3] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[3] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[3] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[3] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[3] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + printf("%-24s: OEM : %s%s%s%s%s\n", "", + (p->data[4] & 1<<IPMI_SESSION_AUTHTYPE_NONE) ? "NONE " : "", + (p->data[4] & 1<<IPMI_SESSION_AUTHTYPE_MD2) ? "MD2 " : "", + (p->data[4] & 1<<IPMI_SESSION_AUTHTYPE_MD5) ? "MD5 " : "", + (p->data[4] & 1<<IPMI_SESSION_AUTHTYPE_PASSWORD) ? "PASSWORD " : "", + (p->data[4] & 1<<IPMI_SESSION_AUTHTYPE_OEM) ? "OEM " : ""); + } + + p = get_lan_param(intf, chan, IPMI_LANP_IP_ADDR_SRC); + if (p == NULL) + return -1; + if (p->data != NULL) { + printf("%-24s: ", p->desc); + p->data[0] &= 0xf; + switch (p->data[0]) { + case 0: + printf("Unspecified\n"); + break; + case 1: + printf("Static Address\n"); + break; + case 2: + printf("DHCP Address\n"); + break; + case 3: + printf("BIOS Assigned Address\n"); + break; + default: + printf("Other\n"); + break; + } + } + + p = get_lan_param(intf, chan, IPMI_LANP_IP_ADDR); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %d.%d.%d.%d\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3]); + + p = get_lan_param(intf, chan, IPMI_LANP_SUBNET_MASK); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %d.%d.%d.%d\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3]); + + p = get_lan_param(intf, chan, IPMI_LANP_MAC_ADDR); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %02x:%02x:%02x:%02x:%02x:%02x\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5]); + + p = get_lan_param(intf, chan, IPMI_LANP_SNMP_STRING); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %s\n", p->desc, p->data); + + p = get_lan_param(intf, chan, IPMI_LANP_IP_HEADER); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: TTL=0x%02x Flags=0x%02x Precedence=0x%02x TOS=0x%02x\n", + p->desc, p->data[0], p->data[1] & 0xe0, p->data[2] & 0xe0, p->data[2] & 0x1e); + + p = get_lan_param(intf, chan, IPMI_LANP_BMC_ARP); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: ARP Responses %sabled, Gratuitous ARP %sabled\n", p->desc, + (p->data[0] & 2) ? "En" : "Dis", (p->data[0] & 1) ? "En" : "Dis"); + + p = get_lan_param(intf, chan, IPMI_LANP_GRAT_ARP); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %.1f seconds\n", p->desc, (float)((p->data[0] + 1) / 2)); + + p = get_lan_param(intf, chan, IPMI_LANP_DEF_GATEWAY_IP); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %d.%d.%d.%d\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3]); + + p = get_lan_param(intf, chan, IPMI_LANP_DEF_GATEWAY_MAC); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %02x:%02x:%02x:%02x:%02x:%02x\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5]); + + p = get_lan_param(intf, chan, IPMI_LANP_BAK_GATEWAY_IP); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %d.%d.%d.%d\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3]); + + p = get_lan_param(intf, chan, IPMI_LANP_BAK_GATEWAY_MAC); + if (p == NULL) + return -1; + if (p->data != NULL) + printf("%-24s: %02x:%02x:%02x:%02x:%02x:%02x\n", p->desc, + p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5]); + + p = get_lan_param(intf, chan, IPMI_LANP_VLAN_ID); + if (p != NULL && p->data != NULL) { + int id = ((p->data[1] & 0x0f) << 8) + p->data[0]; + if (p->data[1] & 0x80) + printf("%-24s: %d\n", p->desc, id); + else + printf("%-24s: Disabled\n", p->desc); + } + + p = get_lan_param(intf, chan, IPMI_LANP_VLAN_PRIORITY); + if (p != NULL && p->data != NULL) + printf("%-24s: %d\n", p->desc, p->data[0] & 0x07); + + /* Determine supported Cipher Suites -- Requires two calls */ + p = get_lan_param(intf, chan, IPMI_LANP_RMCP_CIPHER_SUPPORT); + if (p == NULL) + return -1; + else if (p->data != NULL) + { + unsigned char cipher_suite_count = p->data[0]; + p = get_lan_param(intf, chan, IPMI_LANP_RMCP_CIPHERS); + if (p == NULL) + return -1; + + printf("%-24s: ", p->desc); + + /* Now we're dangerous. There are only 15 fixed cipher + suite IDs, but the spec allows for 16 in the return data.*/ + if ((p->data != NULL) && (p->data_len <= 17)) + { + unsigned int i; + for (i = 0; (i < 16) && (i < cipher_suite_count); ++i) + { + printf("%s%d", + (i > 0? ",": ""), + p->data[i + 1]); + } + printf("\n"); + } + else + { + printf("None\n"); + } + } + + /* RMCP+ Messaging Cipher Suite Privilege Levels */ + /* These are the privilege levels for the 15 fixed cipher suites */ + p = get_lan_param(intf, chan, IPMI_LANP_RMCP_PRIV_LEVELS); + if (p == NULL) + return -1; + if ((p->data != NULL) && (p->data_len == 9)) + { + printf("%-24s: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", p->desc, + priv_level_to_char(p->data[1] & 0x0F), + priv_level_to_char(p->data[1] >> 4), + priv_level_to_char(p->data[2] & 0x0F), + priv_level_to_char(p->data[2] >> 4), + priv_level_to_char(p->data[3] & 0x0F), + priv_level_to_char(p->data[3] >> 4), + priv_level_to_char(p->data[4] & 0x0F), + priv_level_to_char(p->data[4] >> 4), + priv_level_to_char(p->data[5] & 0x0F), + priv_level_to_char(p->data[5] >> 4), + priv_level_to_char(p->data[6] & 0x0F), + priv_level_to_char(p->data[6] >> 4), + priv_level_to_char(p->data[7] & 0x0F), + priv_level_to_char(p->data[7] >> 4), + priv_level_to_char(p->data[8] & 0x0F)); + + /* Now print a legend */ + printf("%-24s: %s\n", "", " X=Cipher Suite Unused"); + printf("%-24s: %s\n", "", " c=CALLBACK"); + printf("%-24s: %s\n", "", " u=USER"); + printf("%-24s: %s\n", "", " o=OPERATOR"); + printf("%-24s: %s\n", "", " a=ADMIN"); + printf("%-24s: %s\n", "", " O=OEM"); + } + else + printf("%-24s: Not Available\n", p->desc); + + return rc; +} + +/* Configure Authentication Types */ +static int +ipmi_lan_set_auth(struct ipmi_intf * intf, uint8_t chan, char * level, char * types) +{ + uint8_t data[5]; + uint8_t authtype = 0; + char * p; + struct lan_param * lp; + + if (level == NULL || types == NULL) + return -1; + + lp = get_lan_param(intf, chan, IPMI_LANP_AUTH_TYPE_ENABLE); + if (lp == NULL) + return -1; + if (lp->data == NULL) + return -1; + + lprintf(LOG_DEBUG, "%-24s: callback=0x%02x user=0x%02x operator=0x%02x admin=0x%02x oem=0x%02x", + lp->desc, lp->data[0], lp->data[1], lp->data[2], lp->data[3], lp->data[4]); + + memset(data, 0, 5); + memcpy(data, lp->data, 5); + + p = types; + while (p) { + if (strncasecmp(p, "none", 4) == 0) + authtype |= 1 << IPMI_SESSION_AUTHTYPE_NONE; + else if (strncasecmp(p, "md2", 3) == 0) + authtype |= 1 << IPMI_SESSION_AUTHTYPE_MD2; + else if (strncasecmp(p, "md5", 3) == 0) + authtype |= 1 << IPMI_SESSION_AUTHTYPE_MD5; + else if ((strncasecmp(p, "password", 8) == 0) || + (strncasecmp(p, "key", 3) == 0)) + authtype |= 1 << IPMI_SESSION_AUTHTYPE_KEY; + else if (strncasecmp(p, "oem", 3) == 0) + authtype |= 1 << IPMI_SESSION_AUTHTYPE_OEM; + else + lprintf(LOG_WARNING, "Invalid authentication type: %s", p); + p = strchr(p, ','); + if (p) + p++; + } + + p = level; + while (p) { + if (strncasecmp(p, "callback", 8) == 0) + data[0] = authtype; + else if (strncasecmp(p, "user", 4) == 0) + data[1] = authtype; + else if (strncasecmp(p, "operator", 8) == 0) + data[2] = authtype; + else if (strncasecmp(p, "admin", 5) == 0) + data[3] = authtype; + else + lprintf(LOG_WARNING, "Invalid authentication level: %s", p); + p = strchr(p, ','); + if (p) + p++; + } + + if (verbose > 1) + printbuf(data, 5, "authtype data"); + + return set_lan_param(intf, chan, IPMI_LANP_AUTH_TYPE_ENABLE, data, 5); +} + +static int +ipmi_lan_set_password(struct ipmi_intf * intf, + uint8_t userid, uint8_t * password) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[18]; + + memset(&data, 0, sizeof(data)); + data[0] = userid & 0x3f;/* user ID */ + data[1] = 0x02; /* set password */ + + if (password != NULL) + memcpy(data+2, password, __min(strlen((const char *)password), 16)); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x47; + req.msg.data = data; + req.msg.data_len = 18; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set LAN Password for user %d", userid); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set LAN Password for user %d failed: %s", + userid, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* adjust our session password + * or we will no longer be able to communicate with BMC + */ + ipmi_intf_session_set_password(intf, (char *)password); + printf("Password %s for user %d\n", + (password == NULL) ? "cleared" : "set", userid); + + return 0; +} + +static int +ipmi_set_alert_enable(struct ipmi_intf * intf, uint8_t channel, uint8_t enable) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[3]; + + memset(&req, 0, sizeof(req)); + + /* update non-volatile access */ + rqdata[0] = channel; + rqdata[1] = 0x40; + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x41; + req.msg.data = rqdata; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Access for channel %d", channel); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* SAVE TO NVRAM */ + memset(rqdata, 0, 3); + rqdata[0] = channel & 0xf; + rqdata[1] = rsp->data[0]; + if (enable != 0) + rqdata[1] &= ~0x20; + else + rqdata[1] |= 0x20; + rqdata[1] |= 0x40; + rqdata[2] = 0; + + req.msg.cmd = 0x40; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set Channel Access for channel %d", channel); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* SAVE TO CURRENT */ + rqdata[1] &= 0xc0; + rqdata[1] |= 0x80; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set Channel Access for channel %d", channel); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static int +ipmi_set_channel_access(struct ipmi_intf * intf, uint8_t channel, uint8_t enable) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[3]; + uint8_t byteEnable; + + memset(&req, 0, sizeof(req)); + + /* RETREIVE VALUE IN NVRAM */ + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x41; /* Get Channel Access Command */ + req.msg.data = rqdata; + req.msg.data_len = 2; + + memset(rqdata, 0, 2); + rqdata[0] = channel & 0xf; + rqdata[1] = 0x40; /* retreive NV */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Access for channel %d", channel); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } else { + byteEnable = *(rsp->data + 0); + } + + /* SAVE TO NVRAM */ + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x40; /* Set Channel Access Command */ + req.msg.data = rqdata; + req.msg.data_len = 3; + + memset(rqdata, 0, 3); + rqdata[0] = channel & 0xf; + rqdata[1] = 0x40 | (byteEnable & 0x38); /* use previously set values */ + if (enable != 0) + rqdata[1] |= 0x2; /* set always available if enable is set */ + rqdata[2] = 0x44; /* set channel privilege limit to ADMIN */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set Channel Access for channel %d", channel); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* RETREIVE VALUE IN NVRAM */ + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x41; /* Get Channel Access Command */ + req.msg.data = rqdata; + req.msg.data_len = 2; + + memset(rqdata, 0, 2); + rqdata[0] = channel & 0xf; + rqdata[1] = 0x80; /* retreive NV */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Get Channel Access for channel %d", channel); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } else { + byteEnable = *(rsp->data + 0); + } + + /* SAVE TO CURRENT */ + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x40; /* Set Channel Access Command */ + req.msg.data = rqdata; + req.msg.data_len = 3; + + memset(rqdata, 0, 3); + rqdata[0] = channel & 0xf; + rqdata[1] = 0x80 | (byteEnable & 0x38); /* use previously set values */ + if (enable != 0) + rqdata[1] |= 0x2; /* set always available if enable is set */ + rqdata[2] = 0x84; /* set channel privilege limit to ADMIN */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set Channel Access for channel %d", channel); + return -1; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Channel Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* can't send close session if access off so abort instead */ + if (enable == 0) + intf->abort = 1; + + return 0; +} + +static int +ipmi_set_user_access(struct ipmi_intf * intf, uint8_t channel, uint8_t userid) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[4]; + + memset(rqdata, 0, 4); + rqdata[0] = 0x90 | (channel & 0xf); + rqdata[1] = userid & 0x3f; + rqdata[2] = 0x4; + rqdata[3] = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x43; + req.msg.data = rqdata; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to Set User Access for channel %d", channel); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set User Access for channel %d failed: %s", + channel, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static int +get_cmdline_macaddr(char * arg, uint8_t * buf) +{ + uint32_t m1, m2, m3, m4, m5, m6; + if (sscanf(arg, "%02x:%02x:%02x:%02x:%02x:%02x", + &m1, &m2, &m3, &m4, &m5, &m6) != 6) { + lprintf(LOG_ERR, "Invalid MAC address: %s", arg); + return -1; + } + buf[0] = (uint8_t)m1; + buf[1] = (uint8_t)m2; + buf[2] = (uint8_t)m3; + buf[3] = (uint8_t)m4; + buf[4] = (uint8_t)m5; + buf[5] = (uint8_t)m6; + return 0; +} + + +static int +get_cmdline_cipher_suite_priv_data(char * arg, uint8_t * buf) +{ + int i, ret = 0; + + if (strlen(arg) != 15) + { + lprintf(LOG_ERR, "Invalid privilege specification length: %d", + strlen(arg)); + return -1; + } + + /* + * The first byte is reserved (0). The rest of the buffer is setup + * so that each nibble holds the maximum privilege level available for + * that cipher suite number. The number of nibbles (15) matches the number + * of fixed cipher suite IDs. This command documentation mentions 16 IDs + * but table 22-19 shows that there are only 15 (0-14). + * + * data 1 - reserved + * data 2 - maximum priv level for first (LSN) and second (MSN) ciphers + * data 3 - maximum priv level for third (LSN) and fourth (MSN) ciphers + * data 9 - maximum priv level for 15th (LSN) cipher. + */ + bzero(buf, 9); + + for (i = 0; i < 15; ++i) + { + unsigned char priv_level = IPMI_SESSION_PRIV_ADMIN; + + switch (arg[i]) + { + case 'X': + priv_level = IPMI_SESSION_PRIV_UNSPECIFIED; /* 0 */ + break; + case 'c': + priv_level = IPMI_SESSION_PRIV_CALLBACK; /* 1 */ + break; + case 'u': + priv_level = IPMI_SESSION_PRIV_USER; /* 2 */ + break; + case 'o': + priv_level = IPMI_SESSION_PRIV_OPERATOR; /* 3 */ + break; + case 'a': + priv_level = IPMI_SESSION_PRIV_ADMIN; /* 4 */ + break; + case 'O': + priv_level = IPMI_SESSION_PRIV_OEM; /* 5 */ + break; + default: + lprintf(LOG_ERR, "Invalid privilege specification char: %c", + arg[i]); + ret = -1; + break; + } + + if (ret != 0) + break; + else + { + if ((i + 1) % 2) + { + // Odd number cipher suites will be in the LSN + buf[1 + (i / 2)] += priv_level; + } + else + { + // Even number cipher suites will be in the MSN + buf[1 + (i / 2)] += (priv_level << 4); + } + } + } + + return ret; +} + + +static int +get_cmdline_ipaddr(char * arg, uint8_t * buf) +{ + uint32_t ip1, ip2, ip3, ip4; + if (sscanf(arg, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) { + lprintf(LOG_ERR, "Invalid IP address: %s", arg); + return -1; + } + buf[0] = (uint8_t)ip1; + buf[1] = (uint8_t)ip2; + buf[2] = (uint8_t)ip3; + buf[3] = (uint8_t)ip4; + return 0; +} + +static void ipmi_lan_set_usage(void) +{ + lprintf(LOG_NOTICE, "\nusage: lan set <channel> <command> <parameter>\n"); + lprintf(LOG_NOTICE, "LAN set command/parameter options:"); + lprintf(LOG_NOTICE, " ipaddr <x.x.x.x> Set channel IP address"); + lprintf(LOG_NOTICE, " netmask <x.x.x.x> Set channel IP netmask"); + lprintf(LOG_NOTICE, " macaddr <x:x:x:x:x:x> Set channel MAC address"); + lprintf(LOG_NOTICE, " defgw ipaddr <x.x.x.x> Set default gateway IP address"); + lprintf(LOG_NOTICE, " defgw macaddr <x:x:x:x:x:x> Set default gateway MAC address"); + lprintf(LOG_NOTICE, " bakgw ipaddr <x.x.x.x> Set backup gateway IP address"); + lprintf(LOG_NOTICE, " bakgw macaddr <x:x:x:x:x:x> Set backup gateway MAC address"); + lprintf(LOG_NOTICE, " password <password> Set session password for this channel"); + lprintf(LOG_NOTICE, " snmp <community string> Set SNMP public community string"); + lprintf(LOG_NOTICE, " user Enable default user for this channel"); + lprintf(LOG_NOTICE, " access <on|off> Enable or disable access to this channel"); + lprintf(LOG_NOTICE, " alert <on|off> Enable or disable PEF alerting for this channel"); + lprintf(LOG_NOTICE, " arp respond <on|off> Enable or disable BMC ARP responding"); + lprintf(LOG_NOTICE, " arp generate <on|off> Enable or disable BMC gratuitous ARP generation"); + lprintf(LOG_NOTICE, " arp interval <seconds> Set gratuitous ARP generation interval"); + lprintf(LOG_NOTICE, " vlan id <off|<id>> Disable or enable VLAN and set ID (1-4094)"); + lprintf(LOG_NOTICE, " vlan priority <priority> Set vlan priority (0-7)"); + lprintf(LOG_NOTICE, " auth <level> <type,..> Set channel authentication types"); + lprintf(LOG_NOTICE, " level = CALLBACK, USER, OPERATOR, ADMIN"); + lprintf(LOG_NOTICE, " type = NONE, MD2, MD5, PASSWORD, OEM"); + lprintf(LOG_NOTICE, " ipsrc <source> Set IP Address source"); + lprintf(LOG_NOTICE, " none = unspecified source"); + lprintf(LOG_NOTICE, " static = address manually configured to be static"); + lprintf(LOG_NOTICE, " dhcp = address obtained by BMC running DHCP"); + lprintf(LOG_NOTICE, " bios = address loaded by BIOS or system software"); + lprintf(LOG_NOTICE, " cipher_privs XXXXXXXXXXXXXXX Set RMCP+ cipher suite privilege levels"); + lprintf(LOG_NOTICE, " X = Cipher Suite Unused"); + lprintf(LOG_NOTICE, " c = CALLBACK"); + lprintf(LOG_NOTICE, " u = USER"); + lprintf(LOG_NOTICE, " o = OPERATOR"); + lprintf(LOG_NOTICE, " a = ADMIN"); + lprintf(LOG_NOTICE, " O = OEM\n"); +} + +static void +ipmi_lan_set_vlan_usage(void) +{ + lprintf(LOG_NOTICE, + "lan set <channel> vlan id <id>\n" + "lan set <channel> vlan id off\n" + "lan set <channel> vlan priority <priority>\n"); +} + +static int +ipmi_lan_set_vlan_id(struct ipmi_intf * intf, uint8_t chan, char *string) +{ + uint8_t data[2]; + int rc; + + if (string == NULL) { + data[0] = 0; + data[1] = 0; + } + else { + int id = 0; + if (str2int(string, &id) != 0) { + lprintf(LOG_ERR, "Given VLAN ID '%s' is invalid.", string); + return (-1); + } + + if (id < 1 || id > 4094) { + lprintf(LOG_NOTICE, "vlan id must be between 1 and 4094."); + return -1; + } + else { + data[0] = (uint8_t)id; + data[1] = (uint8_t)(id >> 8) | 0x80; + } + } + rc = set_lan_param(intf, chan, IPMI_LANP_VLAN_ID, data, 2); + return rc; +} + +static int +ipmi_lan_set_vlan_priority(struct ipmi_intf * intf, uint8_t chan, char *string) +{ + uint8_t data; + int rc; + int priority = 0; + if (str2int(string, &priority) != 0) { + lprintf(LOG_ERR, "Given VLAN priority '%s' is invalid.", string); + return (-1); + } + + if (priority < 0 || priority > 7) { + lprintf(LOG_NOTICE, "vlan priority must be between 0 and 7."); + return -1; + } + data = (uint8_t)priority; + rc = set_lan_param(intf, chan, IPMI_LANP_VLAN_PRIORITY, &data, 1); + return rc; +} + +static int +ipmi_lan_set(struct ipmi_intf * intf, int argc, char ** argv) +{ + uint8_t data[32]; + uint8_t chan; + int rc = 0; + + if (argc < 2) { + ipmi_lan_set_usage(); + return (-1); + } + + if (strncmp(argv[0], "help", 4) == 0 || + strncmp(argv[1], "help", 4) == 0) { + ipmi_lan_set_usage(); + return 0; + } + + if (str2uchar(argv[0], &chan) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[0]); + return (-1); + } + + /* find type of channel and only accept 802.3 LAN */ + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel!", chan); + ipmi_lan_set_usage(); + return -1; + } + + memset(&data, 0, sizeof(data)); + + /* set user access */ + if (strncmp(argv[1], "user", 4) == 0) { + rc = ipmi_set_user_access(intf, chan, 1); + } + /* set channel access mode */ + else if (strncmp(argv[1], "access", 6) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, "lan set access <on|off>"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, "lan set access <on|off>"); + return 0; + } + else if (strncmp(argv[2], "on", 2) == 0) { + rc = ipmi_set_channel_access(intf, chan, 1); + } + else if (strncmp(argv[2], "off", 3) == 0) { + rc = ipmi_set_channel_access(intf, chan, 0); + } + else { + lprintf(LOG_NOTICE, "lan set access <on|off>"); + return (-1); + } + } + /* set ARP control */ + else if (strncmp(argv[1], "arp", 3) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, + "lan set <channel> arp respond <on|off>\n" + "lan set <channel> arp generate <on|off>\n" + "lan set <channel> arp interval <seconds>\n\n" + "example: lan set 7 arp gratuitous off\n"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, + "lan set <channel> arp respond <on|off>\n" + "lan set <channel> arp generate <on|off>\n" + "lan set <channel> arp interval <seconds>\n\n" + "example: lan set 7 arp gratuitous off\n"); + return 0; + } + else if (strncmp(argv[2], "interval", 8) == 0) { + uint8_t interval = 0; + if (str2uchar(argv[3], &interval) != 0) { + lprintf(LOG_ERR, "Given ARP interval '%s' is invalid.", argv[3]); + return (-1); + } + rc = lan_set_arp_interval(intf, chan, interval); + } + else if (strncmp(argv[2], "generate", 8) == 0) { + if (argc < 4) { + lprintf(LOG_NOTICE, "lan set <channel> arp generate <on|off>"); + return (-1); + } + else if (strncmp(argv[3], "on", 2) == 0) + rc = lan_set_arp_generate(intf, chan, 1); + else if (strncmp(argv[3], "off", 3) == 0) + rc = lan_set_arp_generate(intf, chan, 0); + else { + lprintf(LOG_NOTICE, "lan set <channel> arp generate <on|off>"); + return (-1); + } + } + else if (strncmp(argv[2], "respond", 7) == 0) { + if (argc < 4) { + lprintf(LOG_NOTICE, "lan set <channel> arp respond <on|off>"); + return (-1); + } + else if (strncmp(argv[3], "on", 2) == 0) + rc = lan_set_arp_respond(intf, chan, 1); + else if (strncmp(argv[3], "off", 3) == 0) + rc = lan_set_arp_respond(intf, chan, 0); + else { + lprintf(LOG_NOTICE, "lan set <channel> arp respond <on|off>"); + return (-1); + } + } + else { + lprintf(LOG_NOTICE, + "lan set <channel> arp respond <on|off>\n" + "lan set <channel> arp generate <on|off>\n" + "lan set <channel> arp interval <seconds>\n"); + return (-1); + } + } + /* set authentication types */ + else if (strncmp(argv[1], "auth", 4) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, + "lan set <channel> auth <level> <type,type,...>\n" + " level = CALLBACK, USER, OPERATOR, ADMIN\n" + " types = NONE, MD2, MD5, PASSWORD, OEM\n" + "example: lan set 7 auth ADMIN PASSWORD,MD5\n"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, + "lan set <channel> auth <level> <type,type,...>\n" + " level = CALLBACK, USER, OPERATOR, ADMIN\n" + " types = NONE, MD2, MD5, PASSWORD, OEM\n" + "example: lan set 7 auth ADMIN PASSWORD,MD5\n"); + return 0; + } else { + rc = ipmi_lan_set_auth(intf, chan, argv[2], argv[3]); + } + } + /* ip address source */ + else if (strncmp(argv[1], "ipsrc", 5) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, + "lan set <channel> ipsrc <source>\n" + " none = unspecified\n" + " static = static address (manually configured)\n" + " dhcp = address obtained by BMC running DHCP\n" + " bios = address loaded by BIOS or system software\n"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, + "lan set <channel> ipsrc <source>\n" + " none = unspecified\n" + " static = static address (manually configured)\n" + " dhcp = address obtained by BMC running DHCP\n" + " bios = address loaded by BIOS or system software\n"); + return 0; + } + else if (strncmp(argv[2], "none", 4) == 0) + data[0] = 0; + else if (strncmp(argv[2], "static", 5) == 0) + data[0] = 1; + else if (strncmp(argv[2], "dhcp", 4) == 0) + data[0] = 2; + else if (strncmp(argv[2], "bios", 4) == 0) + data[0] = 3; + else { + lprintf(LOG_NOTICE, + "lan set <channel> ipsrc <source>\n" + " none = unspecified\n" + " static = static address (manually configured)\n" + " dhcp = address obtained by BMC running DHCP\n" + " bios = address loaded by BIOS or system software\n"); + return -1; + } + rc = set_lan_param(intf, chan, IPMI_LANP_IP_ADDR_SRC, data, 1); + } + /* session password + * not strictly a lan setting, but its used for lan connections */ + else if (strncmp(argv[1], "password", 8) == 0) { + rc = ipmi_lan_set_password(intf, 1, (uint8_t *)argv[2]); + } + /* snmp community string */ + else if (strncmp(argv[1], "snmp", 4) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, "lan set <channel> snmp <community string>"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, "lan set <channel> snmp <community string>"); + return 0; + } else { + memcpy(data, argv[2], __min(strlen(argv[2]), 18)); + printf("Setting LAN %s to %s\n", + ipmi_lan_params[IPMI_LANP_SNMP_STRING].desc, data); + rc = set_lan_param(intf, chan, IPMI_LANP_SNMP_STRING, data, 18); + } + } + /* ip address */ + else if (strncmp(argv[1], "ipaddr", 6) == 0) { + if(argc != 3) + { + ipmi_lan_set_usage(); + return -1; + } + rc = get_cmdline_ipaddr(argv[2], data); + if (rc == 0) { + printf("Setting LAN %s to %d.%d.%d.%d\n", + ipmi_lan_params[IPMI_LANP_IP_ADDR].desc, + data[0], data[1], data[2], data[3]); + rc = set_lan_param(intf, chan, IPMI_LANP_IP_ADDR, data, 4); + } + } + /* network mask */ + else if (strncmp(argv[1], "netmask", 7) == 0) { + if(argc != 3) + { + ipmi_lan_set_usage(); + return -1; + } + rc = get_cmdline_ipaddr(argv[2], data); + if (rc == 0) { + printf("Setting LAN %s to %d.%d.%d.%d\n", + ipmi_lan_params[IPMI_LANP_SUBNET_MASK].desc, + data[0], data[1], data[2], data[3]); + rc = set_lan_param(intf, chan, IPMI_LANP_SUBNET_MASK, data, 4); + } + } + /* mac address */ + else if (strncmp(argv[1], "macaddr", 7) == 0) { + if(argc != 3) + { + ipmi_lan_set_usage(); + return -1; + } + rc = get_cmdline_macaddr(argv[2], data); + if (rc == 0) { + printf("Setting LAN %s to %02x:%02x:%02x:%02x:%02x:%02x\n", + ipmi_lan_params[IPMI_LANP_MAC_ADDR].desc, + data[0], data[1], data[2], data[3], data[4], data[5]); + rc = set_lan_param(intf, chan, IPMI_LANP_MAC_ADDR, data, 6); + } + } + /* default gateway settings */ + else if (strncmp(argv[1], "defgw", 5) == 0) { + if (argc < 4) { + lprintf(LOG_NOTICE, "LAN set default gateway Commands: ipaddr, macaddr"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, "LAN set default gateway Commands: ipaddr, macaddr"); + return 0; + } + else if ((strncmp(argv[2], "ipaddr", 5) == 0) && + (get_cmdline_ipaddr(argv[3], data) == 0)) { + printf("Setting LAN %s to %d.%d.%d.%d\n", + ipmi_lan_params[IPMI_LANP_DEF_GATEWAY_IP].desc, + data[0], data[1], data[2], data[3]); + rc = set_lan_param(intf, chan, IPMI_LANP_DEF_GATEWAY_IP, data, 4); + } + else if ((strncmp(argv[2], "macaddr", 7) == 0) && + (get_cmdline_macaddr(argv[3], data) == 0)) { + printf("Setting LAN %s to %02x:%02x:%02x:%02x:%02x:%02x\n", + ipmi_lan_params[IPMI_LANP_DEF_GATEWAY_MAC].desc, + data[0], data[1], data[2], data[3], data[4], data[5]); + rc = set_lan_param(intf, chan, IPMI_LANP_DEF_GATEWAY_MAC, data, 6); + } + else { + ipmi_lan_set_usage(); + return -1; + } + } + /* backup gateway settings */ + else if (strncmp(argv[1], "bakgw", 5) == 0) { + if (argc < 4) { + lprintf(LOG_NOTICE, "LAN set backup gateway commands: ipaddr, macaddr"); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + lprintf(LOG_NOTICE, "LAN set backup gateway commands: ipaddr, macaddr"); + return 0; + } + else if ((strncmp(argv[2], "ipaddr", 5) == 0) && + (get_cmdline_ipaddr(argv[3], data) == 0)) { + printf("Setting LAN %s to %d.%d.%d.%d\n", + ipmi_lan_params[IPMI_LANP_BAK_GATEWAY_IP].desc, + data[0], data[1], data[2], data[3]); + rc = set_lan_param(intf, chan, IPMI_LANP_BAK_GATEWAY_IP, data, 4); + } + else if ((strncmp(argv[2], "macaddr", 7) == 0) && + (get_cmdline_macaddr(argv[3], data) == 0)) { + printf("Setting LAN %s to %02x:%02x:%02x:%02x:%02x:%02x\n", + ipmi_lan_params[IPMI_LANP_BAK_GATEWAY_MAC].desc, + data[0], data[1], data[2], data[3], data[4], data[5]); + rc = set_lan_param(intf, chan, IPMI_LANP_BAK_GATEWAY_MAC, data, 6); + } + else { + ipmi_lan_set_usage(); + return -1; + } + } + else if (strncasecmp(argv[1], "vlan", 4) == 0) { + if (argc < 4) { + ipmi_lan_set_vlan_usage(); + return (-1); + } + else if (strncmp(argv[2], "help", 4) == 0) { + ipmi_lan_set_vlan_usage(); + return 0; + } + else if (strncasecmp(argv[2], "id", 2) == 0) { + if (strncasecmp(argv[3], "off", 3) == 0) { + ipmi_lan_set_vlan_id(intf, chan, NULL); + } + else { + ipmi_lan_set_vlan_id(intf, chan, argv[3]); + } + } + else if (strncasecmp(argv[2], "priority", 8) == 0) { + ipmi_lan_set_vlan_priority(intf, chan, argv[3]); + } + else { + ipmi_lan_set_vlan_usage(); + return (-1); + } + } + /* set PEF alerting on or off */ + else if (strncasecmp(argv[1], "alert", 5) == 0) { + if (argc < 3) { + lprintf(LOG_NOTICE, "LAN set alert must be 'on' or 'off'"); + return (-1); + } + else if (strncasecmp(argv[2], "on", 2) == 0 || + strncasecmp(argv[2], "enable", 6) == 0) { + printf("Enabling PEF alerts for LAN channel %d\n", chan); + rc = ipmi_set_alert_enable(intf, chan, 1); + } + else if (strncasecmp(argv[2], "off", 3) == 0 || + strncasecmp(argv[2], "disable", 7) == 0) { + printf("Disabling PEF alerts for LAN channel %d\n", chan); + rc = ipmi_set_alert_enable(intf, chan, 0); + } + else { + lprintf(LOG_NOTICE, "LAN set alert must be 'on' or 'off'"); + return 0; + } + } + /* RMCP+ cipher suite privilege levels */ + else if (strncmp(argv[1], "cipher_privs", 12) == 0) + { + if (argc != 3) { + lprintf(LOG_NOTICE, "lan set <channel> cipher_privs XXXXXXXXXXXXXXX"); + lprintf(LOG_NOTICE, " X = Cipher Suite Unused"); + lprintf(LOG_NOTICE, " c = CALLBACK"); + lprintf(LOG_NOTICE, " u = USER"); + lprintf(LOG_NOTICE, " o = OPERATOR"); + lprintf(LOG_NOTICE, " a = ADMIN"); + lprintf(LOG_NOTICE, " O = OEM\n"); + return (-1); + } + else if ((strncmp(argv[2], "help", 4) == 0) || + get_cmdline_cipher_suite_priv_data(argv[2], data)) + { + lprintf(LOG_NOTICE, "lan set <channel> cipher_privs XXXXXXXXXXXXXXX"); + lprintf(LOG_NOTICE, " X = Cipher Suite Unused"); + lprintf(LOG_NOTICE, " c = CALLBACK"); + lprintf(LOG_NOTICE, " u = USER"); + lprintf(LOG_NOTICE, " o = OPERATOR"); + lprintf(LOG_NOTICE, " a = ADMIN"); + lprintf(LOG_NOTICE, " O = OEM\n"); + return 0; + } + else + { + rc = set_lan_param(intf, chan, IPMI_LANP_RMCP_PRIV_LEVELS, data, 9); + } + } + else { + ipmi_lan_set_usage(); + return (-1); + } + + return rc; +} + + +static int +is_alert_destination(struct ipmi_intf * intf, uint8_t channel, uint8_t alert) +{ + struct lan_param * p; + + p = get_lan_param(intf, channel, IPMI_LANP_NUM_DEST); + if (p == NULL) + return 0; + if (p->data == NULL) + return 0; + + if (alert <= (p->data[0] & 0xf)) + return 1; + else + return 0; +} + +static int +ipmi_lan_alert_print(struct ipmi_intf * intf, uint8_t channel, uint8_t alert) +{ +# define PTYPE_LEN 4 +# define PADDR_LEN 13 + struct lan_param *lp_ptr = NULL; + int isack = 0; + uint8_t ptype[PTYPE_LEN]; + uint8_t paddr[PADDR_LEN]; + + lp_ptr = get_lan_param_select(intf, channel, IPMI_LANP_DEST_TYPE, alert); + if (lp_ptr == NULL || lp_ptr->data == NULL + || lp_ptr->data_len < PTYPE_LEN) { + return (-1); + } + memcpy(ptype, lp_ptr->data, PTYPE_LEN); + + lp_ptr = get_lan_param_select(intf, channel, IPMI_LANP_DEST_ADDR, alert); + if (lp_ptr == NULL || lp_ptr->data == NULL + || lp_ptr->data_len < PADDR_LEN) { + return (-1); + } + memcpy(paddr, lp_ptr->data, PADDR_LEN); + + printf("%-24s: %d\n", "Alert Destination", + ptype[0]); + + if (ptype[1] & 0x80) { + isack = 1; + } + printf("%-24s: %s\n", "Alert Acknowledge", + isack ? "Acknowledged" : "Unacknowledged"); + + printf("%-24s: ", "Destination Type"); + switch (ptype[1] & 0x7) { + case 0: + printf("PET Trap\n"); + break; + case 6: + printf("OEM 1\n"); + break; + case 7: + printf("OEM 2\n"); + break; + default: + printf("Unknown\n"); + break; + } + + printf("%-24s: %d\n", + isack ? "Acknowledge Timeout" : "Retry Interval", + ptype[2]); + + printf("%-24s: %d\n", "Number of Retries", + ptype[3] & 0x7); + + if ((paddr[1] & 0xf0) != 0) { + /* unknown address format */ + printf("\n"); + return 0; + } + + printf("%-24s: %s\n", "Alert Gateway", + (paddr[2] & 1) ? "Backup" : "Default"); + + printf("%-24s: %d.%d.%d.%d\n", "Alert IP Address", + paddr[3], paddr[4], paddr[5], paddr[6]); + + printf("%-24s: %02x:%02x:%02x:%02x:%02x:%02x\n", "Alert MAC Address", + paddr[7], paddr[8], paddr[9], + paddr[10], paddr[11], paddr[12]); + + printf("\n"); + return 0; +} + +static int +ipmi_lan_alert_print_all(struct ipmi_intf * intf, uint8_t channel) +{ + int j, ndest; + struct lan_param * p; + + p = get_lan_param(intf, channel, IPMI_LANP_NUM_DEST); + if (p == NULL) + return -1; + if (p->data == NULL) + return -1; + ndest = p->data[0] & 0xf; + + for (j=0; j<=ndest; j++) { + ipmi_lan_alert_print(intf, channel, j); + } + + return 0; +} + +static void +ipmi_lan_alert_print_usage(void) +{ + lprintf(LOG_NOTICE, "\nusage: lan alert print [channel number] [alert destination]\n"); + lprintf(LOG_NOTICE, "Default will print all alerts for the first found LAN channel"); +} + +static void +ipmi_lan_alert_set_usage(void) +{ + lprintf(LOG_NOTICE, "\nusage: lan alert set <channel number> <alert destination> <command> <parameter>\n"); + lprintf(LOG_NOTICE, " Command/parameter options:\n"); + lprintf(LOG_NOTICE, " ipaddr <x.x.x.x> Set alert IP address"); + lprintf(LOG_NOTICE, " macaddr <x:x:x:x:x:x> Set alert MAC address"); + lprintf(LOG_NOTICE, " gateway <default|backup> Set channel gateway to use for alerts"); + lprintf(LOG_NOTICE, " ack <on|off> Set Alert Acknowledge on or off"); + lprintf(LOG_NOTICE, " type <pet|oem1|oem2> Set destination type as PET or OEM"); + lprintf(LOG_NOTICE, " time <seconds> Set ack timeout or unack retry interval"); + lprintf(LOG_NOTICE, " retry <number> Set number of alert retries"); + lprintf(LOG_NOTICE, ""); +} + +static int +ipmi_lan_alert_set(struct ipmi_intf * intf, uint8_t chan, uint8_t alert, + int argc, char ** argv) +{ + struct lan_param * p; + uint8_t data[32], temp[32]; + int rc = 0; + + if (argc < 2) { + ipmi_lan_alert_set_usage(); + return (-1); + } + + if (strncmp(argv[0], "help", 4) == 0 || + strncmp(argv[1], "help", 4) == 0) { + ipmi_lan_alert_set_usage(); + return 0; + } + + memset(data, 0, sizeof(data)); + memset(temp, 0, sizeof(temp)); + + /* alert destination ip address */ + if (strncasecmp(argv[0], "ipaddr", 6) == 0 && + (get_cmdline_ipaddr(argv[1], temp) == 0)) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + /* set new ipaddr */ + memcpy(data+3, temp, 4); + printf("Setting LAN Alert %d IP Address to %d.%d.%d.%d\n", alert, + data[3], data[4], data[5], data[6]); + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len); + } + /* alert destination mac address */ + else if (strncasecmp(argv[0], "macaddr", 7) == 0 && + (get_cmdline_macaddr(argv[1], temp) == 0)) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + /* set new macaddr */ + memcpy(data+7, temp, 6); + printf("Setting LAN Alert %d MAC Address to " + "%02x:%02x:%02x:%02x:%02x:%02x\n", alert, + data[7], data[8], data[9], data[10], data[11], data[12]); + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len); + } + /* alert destination gateway selector */ + else if (strncasecmp(argv[0], "gateway", 7) == 0) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + + if (strncasecmp(argv[1], "def", 3) == 0 || + strncasecmp(argv[1], "default", 7) == 0) { + printf("Setting LAN Alert %d to use Default Gateway\n", alert); + data[2] = 0; + } + else if (strncasecmp(argv[1], "bak", 3) == 0 || + strncasecmp(argv[1], "backup", 6) == 0) { + printf("Setting LAN Alert %d to use Backup Gateway\n", alert); + data[2] = 1; + } + else { + ipmi_lan_alert_set_usage(); + return -1; + } + + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len); + } + /* alert acknowledgement */ + else if (strncasecmp(argv[0], "ack", 3) == 0) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + + if (strncasecmp(argv[1], "on", 2) == 0 || + strncasecmp(argv[1], "yes", 3) == 0) { + printf("Setting LAN Alert %d to Acknowledged\n", alert); + data[1] |= 0x80; + } + else if (strncasecmp(argv[1], "off", 3) == 0 || + strncasecmp(argv[1], "no", 2) == 0) { + printf("Setting LAN Alert %d to Unacknowledged\n", alert); + data[1] &= ~0x80; + } + else { + ipmi_lan_alert_set_usage(); + return -1; + } + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len); + } + /* alert destination type */ + else if (strncasecmp(argv[0], "type", 4) == 0) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + + if (strncasecmp(argv[1], "pet", 3) == 0) { + printf("Setting LAN Alert %d destination to PET Trap\n", alert); + data[1] &= ~0x07; + } + else if (strncasecmp(argv[1], "oem1", 4) == 0) { + printf("Setting LAN Alert %d destination to OEM 1\n", alert); + data[1] &= ~0x07; + data[1] |= 0x06; + } + else if (strncasecmp(argv[1], "oem2", 4) == 0) { + printf("Setting LAN Alert %d destination to OEM 2\n", alert); + data[1] |= 0x07; + } + else { + ipmi_lan_alert_set_usage(); + return -1; + } + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len); + } + /* alert acknowledge timeout or retry interval */ + else if (strncasecmp(argv[0], "time", 4) == 0) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + + if (str2uchar(argv[1], &data[2]) != 0) { + lprintf(LOG_ERR, "Invalid time: %s", argv[1]); + return (-1); + } + printf("Setting LAN Alert %d timeout/retry to %d seconds\n", alert, data[2]); + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len); + } + /* number of retries */ + else if (strncasecmp(argv[0], "retry", 5) == 0) { + /* get current parameter */ + p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert); + if (p == NULL) { + return (-1); + } + memcpy(data, p->data, p->data_len); + + if (str2uchar(argv[1], &data[3]) != 0) { + lprintf(LOG_ERR, "Invalid retry: %s", argv[1]); + return (-1); + } + data[3] = data[3] & 0x7; + printf("Setting LAN Alert %d number of retries to %d\n", alert, data[3]); + rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len); + } + else { + ipmi_lan_alert_set_usage(); + return -1; + } + + return rc; +} + +static int +ipmi_lan_alert(struct ipmi_intf * intf, int argc, char ** argv) +{ + uint8_t alert; + uint8_t channel = 1; + + if (argc < 1) { + ipmi_lan_alert_print_usage(); + ipmi_lan_alert_set_usage(); + return (-1); + } + else if (strncasecmp(argv[0], "help", 4) == 0) { + ipmi_lan_alert_print_usage(); + ipmi_lan_alert_set_usage(); + return 0; + } + + /* alert print [channel] [alert] */ + if (strncasecmp(argv[0], "print", 5) == 0) { + if (argc < 2) { + channel = find_lan_channel(intf, 1); + if (!is_lan_channel(intf, channel)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", channel); + return -1; + } + return ipmi_lan_alert_print_all(intf, channel); + } + + if (strncasecmp(argv[1], "help", 4) == 0) { + ipmi_lan_alert_print_usage(); + return 0; + } + + if (str2uchar(argv[1], &channel) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return (-1); + } + if (!is_lan_channel(intf, channel)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", channel); + return -1; + } + + if (argc < 3) + return ipmi_lan_alert_print_all(intf, channel); + + if (str2uchar(argv[2], &alert) != 0) { + lprintf(LOG_ERR, "Invalid alert: %s", argv[2]); + return (-1); + } + if (is_alert_destination(intf, channel, alert) == 0) { + lprintf(LOG_ERR, "Alert %d is not a valid destination", alert); + return -1; + } + return ipmi_lan_alert_print(intf, channel, alert); + } + + /* alert set <channel> <alert> [option] */ + if (strncasecmp(argv[0], "set", 3) == 0) { + if (argc < 5) { + ipmi_lan_alert_set_usage(); + return (-1); + } + else if (strncasecmp(argv[1], "help", 4) == 0) { + ipmi_lan_alert_set_usage(); + return 0; + } + + if (str2uchar(argv[1], &channel) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return (-1); + } + if (!is_lan_channel(intf, channel)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", channel); + return -1; + } + + if (str2uchar(argv[2], &alert) != 0) { + lprintf(LOG_ERR, "Invalid alert: %s", argv[2]); + return (-1); + } + if (is_alert_destination(intf, channel, alert) == 0) { + lprintf(LOG_ERR, "Alert %d is not a valid destination", alert); + return -1; + } + + return ipmi_lan_alert_set(intf, channel, alert, argc-3, &(argv[3])); + } + + return 0; +} + + +static int +ipmi_lan_stats_get(struct ipmi_intf * intf, uint8_t chan) +{ + int rc = 0; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + uint16_t statsTemp; + + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", chan); + return -1; + } + + /* From here, we are ready to get the stats */ + + msg_data[0] = chan; + msg_data[1] = 0; /* Don't clear */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_GET_STAT; + req.msg.data = msg_data; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get LAN Stats command failed"); + return (-1); + } + + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get LAN Stats command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + if (verbose > 1) { + uint8_t counter; + printf("--- Rx Stats ---\n"); + for (counter=0; counter<18; counter+=2) { + printf("%02X", *(rsp->data + counter)); + printf(" %02X - ", *(rsp->data + counter+1)); + } + printf("\n"); + } + + statsTemp = ((*(rsp->data + 0)) << 8) | (*(rsp->data + 1)); + printf("IP Rx Packet : %d\n", statsTemp); + + statsTemp = ((*(rsp->data + 2)) << 8) | (*(rsp->data + 3)); + printf("IP Rx Header Errors : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 4)) << 8) | (*(rsp->data + 5)); + printf("IP Rx Address Errors : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 6)) << 8) | (*(rsp->data + 7)); + printf("IP Rx Fragmented : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 8)) << 8) | (*(rsp->data + 9)); + printf("IP Tx Packet : %u\n", statsTemp); + + statsTemp = ((*(rsp->data +10)) << 8) | (*(rsp->data +11)); + printf("UDP Rx Packet : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 12)) << 8) | (*(rsp->data + 13)); + printf("RMCP Rx Valid : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 14)) << 8) | (*(rsp->data + 15)); + printf("UDP Proxy Packet Received : %u\n", statsTemp); + + statsTemp = ((*(rsp->data + 16)) << 8) | (*(rsp->data + 17)); + printf("UDP Proxy Packet Dropped : %u\n", statsTemp); + + return rc; +} + + +static int +ipmi_lan_stats_clear(struct ipmi_intf * intf, uint8_t chan) +{ + int rc = 0; + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Channel %d is not a LAN channel", chan); + return -1; + } + + /* From here, we are ready to get the stats */ + msg_data[0] = chan; + msg_data[1] = 1; /* Clear */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_GET_STAT; + req.msg.data = msg_data; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_INFO, "Get LAN Stats command failed"); + return (-1); + } + + if (rsp->ccode > 0) { + lprintf(LOG_INFO, "Get LAN Stats command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + return rc; +} + + +/* + * print_lan_usage + */ +static void +print_lan_usage(void) +{ + lprintf(LOG_NOTICE, "LAN Commands:"); + lprintf(LOG_NOTICE, " print [<channel number>]"); + lprintf(LOG_NOTICE, " set <channel number> <command> <parameter>"); + lprintf(LOG_NOTICE, " alert print <channel number> <alert destination>"); + lprintf(LOG_NOTICE, " alert set <channel number> <alert destination> <command> <parameter>"); + lprintf(LOG_NOTICE, " stats get [<channel number>]"); + lprintf(LOG_NOTICE, " stats clear [<channel number>]"); +} + + +int +ipmi_lanp_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + uint8_t chan = 0; + + if (argc == 0) { + print_lan_usage(); + return (-1); + } else if (strncmp(argv[0], "help", 4) == 0) { + print_lan_usage(); + return 0; + } + + chan = find_lan_channel(intf, 1); + + if (strncmp(argv[0], "printconf", 9) == 0 || + strncmp(argv[0], "print", 5) == 0) + { + if (argc > 2) { + print_lan_usage(); + return (-1); + } else if (argc == 2) { + if (str2uchar(argv[1], &chan) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return (-1); + } + } + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Invalid channel: %d", chan); + return (-1); + } + rc = ipmi_lan_print(intf, chan); + } else if (strncmp(argv[0], "set", 3) == 0) { + rc = ipmi_lan_set(intf, argc-1, &(argv[1])); + } else if (strncmp(argv[0], "alert", 5) == 0) { + rc = ipmi_lan_alert(intf, argc-1, &(argv[1])); + } else if (strncmp(argv[0], "stats", 5) == 0) { + if (argc < 2) { + print_lan_usage(); + return (-1); + } else if (argc == 3) { + if (str2uchar(argv[2], &chan) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[2]); + return (-1); + } + } + if (!is_lan_channel(intf, chan)) { + lprintf(LOG_ERR, "Invalid channel: %d", chan); + return (-1); + } + if (strncmp(argv[1], "get", 3) == 0) { + rc = ipmi_lan_stats_get(intf, chan); + } else if (strncmp(argv[1], "clear", 5) == 0) { + rc = ipmi_lan_stats_clear(intf, chan); + } else { + print_lan_usage(); + return (-1); + } + } else { + lprintf(LOG_NOTICE, "Invalid LAN command: %s", argv[0]); + return (-1); + } + return rc; +} diff --git a/lib/ipmi_main.c b/lib/ipmi_main.c new file mode 100644 index 0000000..14ca183 --- /dev/null +++ b/lib/ipmi_main.c @@ -0,0 +1,1063 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <signal.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_session.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_gendev.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_sol.h> +#include <ipmitool/ipmi_isol.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_chassis.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_firewall.h> +#include <ipmitool/ipmi_sensor.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_session.h> +#include <ipmitool/ipmi_event.h> +#include <ipmitool/ipmi_user.h> +#include <ipmitool/ipmi_raw.h> +#include <ipmitool/ipmi_pef.h> +#include <ipmitool/ipmi_oem.h> +#include <ipmitool/ipmi_ekanalyzer.h> +#include <ipmitool/ipmi_picmg.h> + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef ENABLE_ALL_OPTIONS +# define OPTION_STRING "I:hVvcgsEKYao:H:d:P:f:U:p:C:L:A:t:T:m:z:S:l:b:B:e:k:y:O:R:N:D:" +#else +# define OPTION_STRING "I:hVvcH:f:U:p:d:S:D:" +#endif + +extern int verbose; +extern int csv_output; +extern const struct valstr ipmi_privlvl_vals[]; +extern const struct valstr ipmi_authtype_session_vals[]; + +static struct ipmi_intf * ipmi_main_intf = NULL; + +/* ipmi_password_file_read - Open file and read password from it + * + * @filename: file name to read from + * + * returns pointer to allocated buffer containing password + * (caller is expected to free when finished) + * returns NULL on error + */ +static char * +ipmi_password_file_read(char * filename) +{ + FILE * fp; + char * pass = NULL; + int l; + + pass = malloc(21); + if (pass == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + + memset(pass, 0, 21); + fp = ipmi_open_file_read((const char *)filename); + if (fp == NULL) { + lprintf(LOG_ERR, "Unable to open password file %s", + filename); + free(pass); + return NULL; + } + + /* read in id */ + if (fgets(pass, 21, fp) == NULL) { + lprintf(LOG_ERR, "Unable to read password from file %s", + filename); + free(pass); + fclose(fp); + return NULL; + } + + /* remove trailing whitespace */ + l = strcspn(pass, " \r\n\t"); + if (l > 0) { + pass[l] = '\0'; + } + + fclose(fp); + return pass; +} + + +/* + * Print all the commands in the above table to stderr + * used for help text on command line and shell + */ +void +ipmi_cmd_print(struct ipmi_cmd * cmdlist) +{ + struct ipmi_cmd * cmd; + int hdr = 0; + + if (cmdlist == NULL) + return; + for (cmd=cmdlist; cmd->func != NULL; cmd++) { + if (cmd->desc == NULL) + continue; + if (hdr == 0) { + lprintf(LOG_NOTICE, "Commands:"); + hdr = 1; + } + lprintf(LOG_NOTICE, "\t%-12s %s", cmd->name, cmd->desc); + } + lprintf(LOG_NOTICE, ""); +} + +/* ipmi_cmd_run - run a command from list based on parameters + * called from main() + * + * 1. iterate through ipmi_cmd_list matching on name + * 2. call func() for that command + * + * @intf: ipmi interface + * @name: command name + * @argc: command argument count + * @argv: command argument list + * + * returns value from func() of that commnad if found + * returns -1 if command is not found + */ +int +ipmi_cmd_run(struct ipmi_intf * intf, char * name, int argc, char ** argv) +{ + struct ipmi_cmd * cmd = intf->cmdlist; + + /* hook to run a default command if nothing specified */ + if (name == NULL) { + if (cmd->func == NULL || cmd->name == NULL) + return -1; + else if (strncmp(cmd->name, "default", 7) == 0) + return cmd->func(intf, 0, NULL); + else { + lprintf(LOG_ERR, "No command provided!"); + ipmi_cmd_print(intf->cmdlist); + return -1; + } + } + + for (cmd=intf->cmdlist; cmd->func != NULL; cmd++) { + if (strncmp(name, cmd->name, __maxlen(cmd->name, name)) == 0) + break; + } + if (cmd->func == NULL) { + cmd = intf->cmdlist; + if (strncmp(cmd->name, "default", 7) == 0) + return cmd->func(intf, argc+1, argv-1); + + lprintf(LOG_ERR, "Invalid command: %s", name); + ipmi_cmd_print(intf->cmdlist); + return -1; + } + return cmd->func(intf, argc, argv); +} + +static void +ipmi_option_usage(const char * progname, struct ipmi_cmd * cmdlist, struct ipmi_intf_support * intflist) +{ + lprintf(LOG_NOTICE, "%s version %s\n", progname, VERSION); + lprintf(LOG_NOTICE, "usage: %s [options...] <command>\n", progname); + lprintf(LOG_NOTICE, " -h This help"); + lprintf(LOG_NOTICE, " -V Show version information"); + lprintf(LOG_NOTICE, " -v Verbose (can use multiple times)"); + lprintf(LOG_NOTICE, " -c Display output in comma separated format"); + lprintf(LOG_NOTICE, " -d N Specify a /dev/ipmiN device to use (default=0)"); + lprintf(LOG_NOTICE, " -I intf Interface to use"); + lprintf(LOG_NOTICE, " -H hostname Remote host name for LAN interface"); + lprintf(LOG_NOTICE, " -p port Remote RMCP port [default=623]"); + lprintf(LOG_NOTICE, " -U username Remote session username"); + lprintf(LOG_NOTICE, " -f file Read remote session password from file"); + lprintf(LOG_NOTICE, " -z size Change Size of Communication Channel (OEM)"); + lprintf(LOG_NOTICE, " -S sdr Use local file for remote SDR cache"); + lprintf(LOG_NOTICE, " -D tty:b[:s] Specify the serial device, baud rate to use"); + lprintf(LOG_NOTICE, " and, optionally, specify that interface is the system one"); +#ifdef ENABLE_ALL_OPTIONS + lprintf(LOG_NOTICE, " -a Prompt for remote password"); + lprintf(LOG_NOTICE, " -Y Prompt for the Kg key for IPMIv2 authentication"); + lprintf(LOG_NOTICE, " -e char Set SOL escape character"); + lprintf(LOG_NOTICE, " -C ciphersuite Cipher suite to be used by lanplus interface"); + lprintf(LOG_NOTICE, " -k key Use Kg key for IPMIv2 authentication"); + lprintf(LOG_NOTICE, " -y hex_key Use hexadecimal-encoded Kg key for IPMIv2 authentication"); + lprintf(LOG_NOTICE, " -L level Remote session privilege level [default=ADMINISTRATOR]"); + lprintf(LOG_NOTICE, " Append a '+' to use name/privilege lookup in RAKP1"); + lprintf(LOG_NOTICE, " -A authtype Force use of auth type NONE, PASSWORD, MD2, MD5 or OEM"); + lprintf(LOG_NOTICE, " -P password Remote session password"); + lprintf(LOG_NOTICE, " -E Read password from IPMI_PASSWORD environment variable"); + lprintf(LOG_NOTICE, " -K Read kgkey from IPMI_KGKEY environment variable"); + lprintf(LOG_NOTICE, " -m address Set local IPMB address"); + lprintf(LOG_NOTICE, " -b channel Set destination channel for bridged request"); + lprintf(LOG_NOTICE, " -t address Bridge request to remote target address"); + lprintf(LOG_NOTICE, " -B channel Set transit channel for bridged request (dual bridge)"); + lprintf(LOG_NOTICE, " -T address Set transit address for bridge request (dual bridge)"); + lprintf(LOG_NOTICE, " -l lun Set destination lun for raw commands"); + lprintf(LOG_NOTICE, " -o oemtype Setup for OEM (use 'list' to see available OEM types)"); + lprintf(LOG_NOTICE, " -O seloem Use file for OEM SEL event descriptions"); + lprintf(LOG_NOTICE, " -N seconds Specify timeout for lan [default=2] / lanplus [default=1] interface"); + lprintf(LOG_NOTICE, " -R retry Set the number of retries for lan/lanplus interface [default=4]"); +#endif + lprintf(LOG_NOTICE, ""); + + ipmi_intf_print(intflist); + + if (cmdlist != NULL) + ipmi_cmd_print(cmdlist); +} +/* ipmi_catch_sigint - Handle the interrupt signal (Ctrl-C), close the + * interface, and exit ipmitool with error (-1) + * + * This insures that the IOL session gets freed + * for other callers. + * + * returns -1 + */ +void ipmi_catch_sigint() +{ + if (ipmi_main_intf != NULL) { + printf("\nSIGN INT: Close Interface %s\n",ipmi_main_intf->desc); + ipmi_main_intf->close(ipmi_main_intf); + } + exit(-1); +} + +/* ipmi_parse_hex - convert hexadecimal numbers to ascii string + * Input string must be composed of two-characer hexadecimal numbers. + * There is no separator between the numbers. Each number results in one character + * of the converted string. + * + * Example: ipmi_parse_hex("50415353574F5244") returns 'PASSWORD' + * + * @param str: input string. It must contain only even number of '0'-'9','a'-'f' and 'A-F' characters. + * @returns converted ascii string + * @returns NULL on error + */ +static unsigned char * +ipmi_parse_hex(const char *str) +{ + const char * p; + unsigned char * out, *q; + unsigned char b = 0; + int shift = 4; + + if (strlen(str) == 0) + return NULL; + + if (strlen(str) % 2 != 0) { + lprintf(LOG_ERR, "Number of hex_kg characters is not even"); + return NULL; + } + + if (strlen(str) > (IPMI_KG_BUFFER_SIZE-1)*2) { + lprintf(LOG_ERR, "Kg key is too long"); + return NULL; + } + + out = calloc(IPMI_KG_BUFFER_SIZE, sizeof(unsigned char)); + if (out == NULL) { + lprintf(LOG_ERR, "malloc failure"); + return NULL; + } + + for (p = str, q = out; *p; p++) { + if (!isxdigit(*p)) { + lprintf(LOG_ERR, "Kg_hex is not hexadecimal number"); + free(out); + out = NULL; + return NULL; + } + + if (*p < 'A') /* it must be 0-9 */ + b = *p - '0'; + else /* it's A-F or a-f */ + b = (*p | 0x20) - 'a' + 10; /* convert to lowercase and to 10-15 */ + + *q = *q + b << shift; + if (shift) + shift = 0; + else { + shift = 4; + q++; + } + } + + return out; +} + +/* ipmi_parse_options - helper function to handle parsing command line options + * + * @argc: count of options + * @argv: list of options + * @cmdlist: list of supported commands + * @intflist: list of supported interfaces + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_main(int argc, char ** argv, + struct ipmi_cmd * cmdlist, + struct ipmi_intf_support * intflist) +{ + struct ipmi_intf_support * sup; + int privlvl = 0; + uint8_t target_addr = 0; + uint8_t target_channel = 0; + + uint8_t transit_addr = 0; + uint8_t transit_channel = 0; + uint8_t target_lun = 0; + uint8_t arg_addr = 0; + uint8_t addr = 0; + uint16_t my_long_packet_size=0; + uint8_t my_long_packet_set=0; + uint8_t lookupbit = 0x10; /* use name-only lookup by default */ + int retry = 0; + uint32_t timeout = 0; + int authtype = -1; + char * tmp_pass = NULL; + char * tmp_env = NULL; + char * hostname = NULL; + char * username = NULL; + char * password = NULL; + char * intfname = NULL; + char * progname = NULL; + char * oemtype = NULL; + char * sdrcache = NULL; + unsigned char * kgkey = NULL; + char * seloem = NULL; + int port = 0; + int devnum = 0; + int cipher_suite_id = 3; /* See table 22-19 of the IPMIv2 spec */ + int argflag, i, found; + int rc = -1; + char sol_escape_char = SOL_ESCAPE_CHARACTER_DEFAULT; + char * devfile = NULL; + + /* save program name */ + progname = strrchr(argv[0], '/'); + progname = ((progname == NULL) ? argv[0] : progname+1); + signal(SIGINT, ipmi_catch_sigint); + + while ((argflag = getopt(argc, (char **)argv, OPTION_STRING)) != -1) + { + switch (argflag) { + case 'I': + if (intfname) { + free(intfname); + intfname = NULL; + } + intfname = strdup(optarg); + if (intfname == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + if (intflist != NULL) { + found = 0; + for (sup=intflist; sup->name != NULL; sup++) { + if (strncmp(sup->name, intfname, strlen(intfname)) == 0 && + strncmp(sup->name, intfname, strlen(sup->name)) == 0 && + sup->supported == 1) + found = 1; + } + if (!found) { + lprintf(LOG_ERR, "Interface %s not supported", intfname); + goto out_free; + } + } + break; + case 'h': + ipmi_option_usage(progname, cmdlist, intflist); + rc = 0; + goto out_free; + break; + case 'V': + printf("%s version %s\n", progname, VERSION); + rc = 0; + goto out_free; + break; + case 'd': + if (str2int(optarg, &devnum) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-d'."); + rc = -1; + goto out_free; + } + /* Check if device number is -gt 0; I couldn't find limit for + * kernels > 2.6, thus right side is unlimited. + */ + if (devnum < 0) { + lprintf(LOG_ERR, "Device number %i is out of range.", devnum); + rc = -1; + goto out_free; + } + break; + case 'p': + if (str2int(optarg, &port) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-p'."); + rc = -1; + goto out_free; + } + /* Check if port is -gt 0 && port is -lt 65535 */ + if (port < 0 || port > 65535) { + lprintf(LOG_ERR, "Port number %i is out of range.", port); + rc = -1; + goto out_free; + } + break; + case 'C': + if (str2int(optarg, &cipher_suite_id) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-C'."); + rc = -1; + goto out_free; + } + /* add check Cipher is -gt 0 */ + if (cipher_suite_id < 0) { + lprintf(LOG_ERR, "Cipher suite ID %i is invalid.", cipher_suite_id); + rc = -1; + goto out_free; + } + break; + case 'v': + verbose++; + break; + case 'c': + csv_output = 1; + break; + case 'H': + if (hostname) { + free(hostname); + hostname = NULL; + } + hostname = strdup(optarg); + if (hostname == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; + case 'f': + if (password) { + free(password); + password = NULL; + } + password = ipmi_password_file_read(optarg); + if (password == NULL) + lprintf(LOG_ERR, "Unable to read password " + "from file %s", optarg); + break; + case 'a': +#ifdef HAVE_GETPASSPHRASE + tmp_pass = getpassphrase("Password: "); +#else + tmp_pass = getpass("Password: "); +#endif + if (tmp_pass != NULL) { + if (password) { + free(password); + password = NULL; + } + password = strdup(tmp_pass); + tmp_pass = NULL; + if (password == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + break; + case 'k': + if (kgkey) { + free(kgkey); + kgkey = NULL; + } + kgkey = strdup(optarg); + if (kgkey == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; + case 'K': + if ((tmp_env = getenv("IPMI_KGKEY"))) { + if (kgkey) { + free(kgkey); + kgkey = NULL; + } + kgkey = strdup(tmp_env); + if (kgkey == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } else { + lprintf(LOG_WARN, "Unable to read kgkey from environment"); + } + break; + case 'y': + if (kgkey) { + free(kgkey); + kgkey = NULL; + } + kgkey = ipmi_parse_hex(optarg); + if (kgkey == NULL) { + goto out_free; + } + break; + case 'Y': +#ifdef HAVE_GETPASSPHRASE + tmp_pass = getpassphrase("Key: "); +#else + tmp_pass = getpass("Key: "); +#endif + if (tmp_pass != NULL) { + if (kgkey) { + free(kgkey); + kgkey = NULL; + } + kgkey = strdup(tmp_pass); + tmp_pass = NULL; + if (kgkey == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + break; + case 'U': + if (username) { + free(username); + username = NULL; + } + if (strlen(optarg) > 16) { + lprintf(LOG_ERR, "Username is too long (> 16 bytes)"); + goto out_free; + } + username = strdup(optarg); + if (username == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; + case 'S': + if (sdrcache) { + free(sdrcache); + sdrcache = NULL; + } + sdrcache = strdup(optarg); + if (sdrcache == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; + case 'D': + /* check for subsequent instance of -D */ + if (devfile) { + /* free memory for previous string */ + free(devfile); + } + devfile = strdup(optarg); + if (devfile == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; +#ifdef ENABLE_ALL_OPTIONS + case 'o': + if (oemtype) { + free(oemtype); + oemtype = NULL; + } + oemtype = strdup(optarg); + if (oemtype == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + if (strncmp(oemtype, "list", 4) == 0 || + strncmp(oemtype, "help", 4) == 0) { + ipmi_oem_print(); + rc = 0; + goto out_free; + } + break; + case 'g': + /* backwards compatible oem hack */ + if (oemtype) { + free(oemtype); + oemtype = NULL; + } + oemtype = strdup("intelwv2"); + break; + case 's': + /* backwards compatible oem hack */ + if (oemtype) { + free(oemtype); + oemtype = NULL; + } + oemtype = strdup("supermicro"); + break; + case 'P': + if (password) { + free(password); + password = NULL; + } + password = strdup(optarg); + if (password == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + + /* Prevent password snooping with ps */ + i = strlen(optarg); + memset(optarg, 'X', i); + break; + case 'E': + if ((tmp_env = getenv("IPMITOOL_PASSWORD"))) { + if (password) { + free(password); + password = NULL; + } + password = strdup(tmp_env); + if (password == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + else if ((tmp_env = getenv("IPMI_PASSWORD"))) { + if (password) { + free(password); + password = NULL; + } + password = strdup(tmp_env); + if (password == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + else { + lprintf(LOG_WARN, "Unable to read password from environment"); + } + break; + case 'L': + i = strlen(optarg); + if ((i > 0) && (optarg[i-1] == '+')) { + lookupbit = 0; + optarg[i-1] = 0; + } + privlvl = str2val(optarg, ipmi_privlvl_vals); + if (privlvl == 0xFF) { + lprintf(LOG_WARN, "Invalid privilege level %s", optarg); + } + break; + case 'A': + authtype = str2val(optarg, ipmi_authtype_session_vals); + break; + case 't': + if (str2uchar(optarg, &target_addr) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-t'."); + rc = -1; + goto out_free; + } + break; + case 'b': + if (str2uchar(optarg, &target_channel) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-b'."); + rc = -1; + goto out_free; + } + break; + case 'T': + if (str2uchar(optarg, &transit_addr) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-T'."); + rc = -1; + goto out_free; + } + break; + case 'B': + if (str2uchar(optarg, &transit_channel) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-B'."); + rc = -1; + goto out_free; + } + break; + case 'l': + if (str2uchar(optarg, &target_lun) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-l'."); + rc = 1; + goto out_free; + } + break; + case 'm': + if (str2uchar(optarg, &arg_addr) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-m'."); + rc = -1; + goto out_free; + } + break; + case 'e': + sol_escape_char = optarg[0]; + break; + case 'O': + if (seloem) { + free(seloem); + seloem = NULL; + } + seloem = strdup(optarg); + if (seloem == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + break; + case 'z': + if (str2ushort(optarg, &my_long_packet_size) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-z'."); + rc = -1; + goto out_free; + } + break; + /* Retry and Timeout */ + case 'R': + if (str2int(optarg, &retry) != 0 || retry < 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-R'."); + rc = -1; + goto out_free; + } + break; + case 'N': + if (str2uint(optarg, &timeout) != 0) { + lprintf(LOG_ERR, "Invalid parameter given or out of range for '-N'."); + rc = -1; + goto out_free; + } + break; +#endif + default: + ipmi_option_usage(progname, cmdlist, intflist); + goto out_free; + } + } + + /* check for command before doing anything */ + if (argc-optind > 0 && + strncmp(argv[optind], "help", 4) == 0) { + ipmi_cmd_print(cmdlist); + rc = 0; + goto out_free; + } + + /* + * If the user has specified a hostname (-H option) + * then this is a remote access session. + * + * If no password was specified by any other method + * and the authtype was not explicitly set to NONE + * then prompt the user. + */ + if (hostname != NULL && password == NULL && + (authtype != IPMI_SESSION_AUTHTYPE_NONE || authtype < 0)) { +#ifdef HAVE_GETPASSPHRASE + tmp_pass = getpassphrase("Password: "); +#else + tmp_pass = getpass("Password: "); +#endif + if (tmp_pass != NULL) { + password = strdup(tmp_pass); + tmp_pass = NULL; + if (password == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + } + + /* if no interface was specified but a + * hostname was then use LAN by default + * otherwise the default is hardcoded + * to use the first entry in the list + */ + if (intfname == NULL && hostname != NULL) { + intfname = strdup("lan"); + if (intfname == NULL) { + lprintf(LOG_ERR, "%s: malloc failure", progname); + goto out_free; + } + } + + if (password != NULL && intfname != NULL) { + if (strcmp(intfname, "lan") == 0 && strlen(password) > 16) { + lprintf(LOG_ERR, "%s: password is longer than 16 bytes.", intfname); + rc = -1; + goto out_free; + } else if (strcmp(intfname, "lanplus") == 0 && strlen(password) > 20) { + lprintf(LOG_ERR, "%s: password is longer than 20 bytes.", intfname); + rc = -1; + goto out_free; + } + } /* if (password != NULL && intfname != NULL) */ + + /* load interface */ + ipmi_main_intf = ipmi_intf_load(intfname); + if (ipmi_main_intf == NULL) { + lprintf(LOG_ERR, "Error loading interface %s", intfname); + goto out_free; + } + + /* setup log */ + log_init(progname, 0, verbose); + + /* run OEM setup if found */ + if (oemtype != NULL && + ipmi_oem_setup(ipmi_main_intf, oemtype) < 0) { + lprintf(LOG_ERR, "OEM setup for \"%s\" failed", oemtype); + goto out_free; + } + + /* set session variables */ + if (hostname != NULL) + ipmi_intf_session_set_hostname(ipmi_main_intf, hostname); + if (username != NULL) + ipmi_intf_session_set_username(ipmi_main_intf, username); + if (password != NULL) + ipmi_intf_session_set_password(ipmi_main_intf, password); + if (kgkey != NULL) + ipmi_intf_session_set_kgkey(ipmi_main_intf, kgkey); + if (port > 0) + ipmi_intf_session_set_port(ipmi_main_intf, port); + if (authtype >= 0) + ipmi_intf_session_set_authtype(ipmi_main_intf, (uint8_t)authtype); + if (privlvl > 0) + ipmi_intf_session_set_privlvl(ipmi_main_intf, (uint8_t)privlvl); + else + ipmi_intf_session_set_privlvl(ipmi_main_intf, + IPMI_SESSION_PRIV_ADMIN); /* default */ + /* Adding retry and timeout for interface that support it */ + if (retry > 0) + ipmi_intf_session_set_retry(ipmi_main_intf, retry); + if (timeout > 0) + ipmi_intf_session_set_timeout(ipmi_main_intf, timeout); + + ipmi_intf_session_set_lookupbit(ipmi_main_intf, lookupbit); + ipmi_intf_session_set_sol_escape_char(ipmi_main_intf, sol_escape_char); + ipmi_intf_session_set_cipher_suite_id(ipmi_main_intf, cipher_suite_id); + + ipmi_main_intf->devnum = devnum; + + /* setup device file if given */ + ipmi_main_intf->devfile = devfile; + + /* Open the interface with the specified or default IPMB address */ + ipmi_main_intf->my_addr = arg_addr ? arg_addr : IPMI_BMC_SLAVE_ADDR; + if (ipmi_main_intf->open != NULL) { + if (ipmi_main_intf->open(ipmi_main_intf) < 0) { + goto out_free; + } + } + /* + * Attempt picmg discovery of the actual interface address unless + * the users specified an address. + * Address specification always overrides discovery + */ + if (picmg_discover(ipmi_main_intf) && !arg_addr) { + lprintf(LOG_DEBUG, "Running PICMG Get Address Info"); + addr = ipmi_picmg_ipmb_address(ipmi_main_intf); + lprintf(LOG_INFO, "Discovered IPMB-0 address 0x%x", addr); + } + + /* + * If we discovered the ipmb address and it is not the same as what we + * used for open, Set the discovered IPMB address as my address if the + * interface supports it. + */ + if (addr != 0 && addr != ipmi_main_intf->my_addr && + ipmi_main_intf->set_my_addr) { + /* + * Only set the interface address on interfaces which support + * it + */ + (void) ipmi_main_intf->set_my_addr(ipmi_main_intf, addr); + } + + /* If bridging addresses are specified, handle them */ + if (target_addr > 0) { + ipmi_main_intf->target_addr = target_addr; + ipmi_main_intf->target_lun = target_lun ; + ipmi_main_intf->target_channel = target_channel ; + } + if (transit_addr > 0) { + /* sanity check, transit makes no sense without a target */ + if ((transit_addr != 0 || transit_channel != 0) && + ipmi_main_intf->target_addr == 0) { + lprintf(LOG_ERR, + "Transit address/channel %#x/%#x ignored. " + "Target address must be specified!", + transit_addr, transit_channel); + goto out_free; + } + + ipmi_main_intf->transit_addr = transit_addr; + ipmi_main_intf->transit_channel = transit_channel; + } + if (ipmi_main_intf->target_addr > 0) { + /* must be admin level to do this over lan */ + ipmi_intf_session_set_privlvl(ipmi_main_intf, IPMI_SESSION_PRIV_ADMIN); + /* Get the ipmb address of the targeted entity */ + ipmi_main_intf->target_ipmb_addr = + ipmi_picmg_ipmb_address(ipmi_main_intf); + lprintf(LOG_DEBUG, "Specified addressing Target %#x:%#x Transit %#x:%#x", + ipmi_main_intf->target_addr, + ipmi_main_intf->target_channel, + ipmi_main_intf->transit_addr, + ipmi_main_intf->transit_channel); + if (ipmi_main_intf->target_ipmb_addr) { + lprintf(LOG_INFO, "Discovered Target IPMB-0 address %#x", + ipmi_main_intf->target_ipmb_addr); + } + } + + lprintf(LOG_DEBUG, "Interface address: my_addr %#x " + "transit %#x:%#x target %#x:%#x " + "ipmb_target %#x\n", + ipmi_main_intf->my_addr, + ipmi_main_intf->transit_addr, + ipmi_main_intf->transit_channel, + ipmi_main_intf->target_addr, + ipmi_main_intf->target_channel, + ipmi_main_intf->target_ipmb_addr); + + /* parse local SDR cache if given */ + if (sdrcache != NULL) { + ipmi_sdr_list_cache_fromfile(ipmi_main_intf, sdrcache); + } + /* Parse SEL OEM file if given */ + if (seloem != NULL) { + ipmi_sel_oem_init(seloem); + } + + /* Enable Big Buffer when requested */ + if ( my_long_packet_size != 0 ) { + /* Enable Big Buffer when requested */ + if (!ipmi_oem_active(ipmi_main_intf, "kontron") || + ipmi_kontronoem_set_large_buffer(ipmi_main_intf, + my_long_packet_size ) == 0) { + printf("Setting large buffer to %i\n", my_long_packet_size); + my_long_packet_set = 1; + ipmi_intf_set_max_request_data_size(ipmi_main_intf, + my_long_packet_size); + } + } + + ipmi_main_intf->cmdlist = cmdlist; + + /* now we finally run the command */ + if (argc-optind > 0) + rc = ipmi_cmd_run(ipmi_main_intf, argv[optind], argc-optind-1, + &(argv[optind+1])); + else + rc = ipmi_cmd_run(ipmi_main_intf, NULL, 0, NULL); + + if (my_long_packet_set == 1) { + if (ipmi_oem_active(ipmi_main_intf, "kontron")) { + /* Restore defaults */ + ipmi_kontronoem_set_large_buffer( ipmi_main_intf, 0 ); + } + } + + /* clean repository caches */ + ipmi_cleanup(ipmi_main_intf); + + /* call interface close function if available */ + if (ipmi_main_intf->opened > 0 && ipmi_main_intf->close != NULL) + ipmi_main_intf->close(ipmi_main_intf); + + out_free: + log_halt(); + + if (intfname != NULL) { + free(intfname); + intfname = NULL; + } + if (hostname != NULL) { + free(hostname); + hostname = NULL; + } + if (username != NULL) { + free(username); + username = NULL; + } + if (password != NULL) { + free(password); + password = NULL; + } + if (oemtype != NULL) { + free(oemtype); + oemtype = NULL; + } + if (seloem != NULL) { + free(seloem); + seloem = NULL; + } + if (kgkey != NULL) { + free(kgkey); + kgkey = NULL; + } + if (sdrcache != NULL) { + free(sdrcache); + sdrcache = NULL; + } + if (devfile) { + free(devfile); + devfile = NULL; + } + + return rc; +} + + diff --git a/lib/ipmi_mc.c b/lib/ipmi_mc.c new file mode 100644 index 0000000..2890c90 --- /dev/null +++ b/lib/ipmi_mc.c @@ -0,0 +1,1112 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/bswap.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_strings.h> + +extern int verbose; + +static int ipmi_sysinfo_main(struct ipmi_intf *intf, int argc, char ** argv, + int is_set); +static void printf_sysinfo_usage(int full_help); + +/* ipmi_mc_reset - attempt to reset an MC + * + * @intf: ipmi interface + * @cmd: reset command to send + * BMC_WARM_RESET or + * BMC_COLD_RESET + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_reset(struct ipmi_intf * intf, int cmd) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + if( !intf->opened ) + intf->open(intf); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = cmd; + req.msg.data_len = 0; + + if (cmd == BMC_COLD_RESET) + intf->noanswer = 1; + + rsp = intf->sendrecv(intf, &req); + + if (cmd == BMC_COLD_RESET) + intf->abort = 1; + + if (cmd == BMC_COLD_RESET && rsp == NULL) { + /* This is expected. See 20.2 Cold Reset Command, p.243, IPMIv2.0 rev1.0 */ + } else if (rsp == NULL) { + lprintf(LOG_ERR, "MC reset command failed."); + return (-1); + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "MC reset command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + printf("Sent %s reset command to MC\n", + (cmd == BMC_WARM_RESET) ? "warm" : "cold"); + + return 0; +} + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +struct bmc_enables_data { +#if WORDS_BIGENDIAN + uint8_t oem2 : 1; + uint8_t oem1 : 1; + uint8_t oem0 : 1; + uint8_t __reserved : 1; + uint8_t system_event_log : 1; + uint8_t event_msgbuf : 1; + uint8_t event_msgbuf_intr : 1; + uint8_t receive_msg_intr : 1; +#else + uint8_t receive_msg_intr : 1; + uint8_t event_msgbuf_intr : 1; + uint8_t event_msgbuf : 1; + uint8_t system_event_log : 1; + uint8_t __reserved : 1; + uint8_t oem0 : 1; + uint8_t oem1 : 1; + uint8_t oem2 : 1; +#endif +} ATTRIBUTE_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +struct bitfield_data { + const char * name; + const char * desc; + uint32_t mask; +}; + +struct bitfield_data mc_enables_bf[] = { + { + name: "recv_msg_intr", + desc: "Receive Message Queue Interrupt", + mask: 1<<0, + }, + { + name: "event_msg_intr", + desc: "Event Message Buffer Full Interrupt", + mask: 1<<1, + }, + { + name: "event_msg", + desc: "Event Message Buffer", + mask: 1<<2, + }, + { + name: "system_event_log", + desc: "System Event Logging", + mask: 1<<3, + }, + { + name: "oem0", + desc: "OEM 0", + mask: 1<<5, + }, + { + name: "oem1", + desc: "OEM 1", + mask: 1<<6, + }, + { + name: "oem2", + desc: "OEM 2", + mask: 1<<7, + }, + { NULL }, +}; + +static void +printf_mc_reset_usage(void) +{ + lprintf(LOG_NOTICE, "usage: mc reset <warm|cold>"); +} /* printf_mc_reset_usage(void) */ + +static void +printf_mc_usage(void) +{ + struct bitfield_data * bf; + lprintf(LOG_NOTICE, "MC Commands:"); + lprintf(LOG_NOTICE, " reset <warm|cold>"); + lprintf(LOG_NOTICE, " guid"); + lprintf(LOG_NOTICE, " info"); + lprintf(LOG_NOTICE, " watchdog <get|reset|off>"); + lprintf(LOG_NOTICE, " selftest"); + lprintf(LOG_NOTICE, " getenables"); + lprintf(LOG_NOTICE, " setenables <option=on|off> ..."); + for (bf = mc_enables_bf; bf->name != NULL; bf++) { + lprintf(LOG_NOTICE, " %-20s %s", bf->name, bf->desc); + } + printf_sysinfo_usage(0); +} + +static void +printf_sysinfo_usage(int full_help) +{ + if (full_help != 0) + lprintf(LOG_NOTICE, "usage:"); + + lprintf(LOG_NOTICE, " getsysinfo <argument>"); + + if (full_help != 0) { + lprintf(LOG_NOTICE, + " Retrieves system info from BMC for given argument"); + } + + lprintf(LOG_NOTICE, " setsysinfo <argument> <string>"); + + if (full_help != 0) { + lprintf(LOG_NOTICE, + " Stores system info string for given argument to BMC"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " Valid arguments are:"); + } + lprintf(LOG_NOTICE, + " primary_os_name Primary operating system name"); + lprintf(LOG_NOTICE, " os_name Operating system name"); + lprintf(LOG_NOTICE, + " system_name System Name of server(vendor dependent)"); + lprintf(LOG_NOTICE, + " delloem_os_version Running version of operating system"); + lprintf(LOG_NOTICE, " delloem_url URL of BMC webserver"); + lprintf(LOG_NOTICE, ""); +} + +static void +print_watchdog_usage(void) +{ + lprintf(LOG_NOTICE, "usage: watchdog <command>:"); + lprintf(LOG_NOTICE, " get : Get Current Watchdog settings"); + lprintf(LOG_NOTICE, " reset : Restart Watchdog timer based on most recent settings"); + lprintf(LOG_NOTICE, " off : Shut off a running Watchdog timer"); +} + +/* ipmi_mc_get_enables - print out MC enables + * + * @intf: ipmi inteface + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_get_enables(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct bitfield_data * bf; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_GLOBAL_ENABLES; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Global Enables command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Global Enables command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + for (bf = mc_enables_bf; bf->name != NULL; bf++) { + printf("%-40s : %sabled\n", bf->desc, + rsp->data[0] & bf->mask ? "en" : "dis"); + } + + return 0; +} + +/* ipmi_mc_set_enables - set MC enable flags + * + * @intf: ipmi inteface + * @argc: argument count + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_set_enables(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct bitfield_data * bf; + uint8_t en; + int i; + + if (argc < 1) { + printf_mc_usage(); + return (-1); + } + else if (strncmp(argv[0], "help", 4) == 0) { + printf_mc_usage(); + return 0; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_GLOBAL_ENABLES; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Global Enables command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Global Enables command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + en = rsp->data[0]; + + for (i = 0; i < argc; i++) { + for (bf = mc_enables_bf; bf->name != NULL; bf++) { + int nl = strlen(bf->name); + if (strncmp(argv[i], bf->name, nl) != 0) + continue; + if (strncmp(argv[i]+nl+1, "off", 3) == 0) { + printf("Disabling %s\n", bf->desc); + en &= ~bf->mask; + } + else if (strncmp(argv[i]+nl+1, "on", 2) == 0) { + printf("Enabling %s\n", bf->desc); + en |= bf->mask; + } + else { + lprintf(LOG_ERR, "Unrecognized option: %s", argv[i]); + } + } + } + + if (en == rsp->data[0]) { + printf("\nNothing to change...\n"); + ipmi_mc_get_enables(intf); + return 0; + } + + req.msg.cmd = BMC_SET_GLOBAL_ENABLES; + req.msg.data = &en; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Global Enables command failed"); + return -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Global Enables command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("\nVerifying...\n"); + ipmi_mc_get_enables(intf); + + return 0; +} + +/* IPM Device, Get Device ID Command - Additional Device Support */ +const char *ipm_dev_adtl_dev_support[8] = { + "Sensor Device", /* bit 0 */ + "SDR Repository Device", /* bit 1 */ + "SEL Device", /* bit 2 */ + "FRU Inventory Device", /* ... */ + "IPMB Event Receiver", + "IPMB Event Generator", + "Bridge", + "Chassis Device" /* bit 7 */ +}; + +/* ipmi_mc_get_deviceid - print information about this MC + * + * @intf: ipmi interface + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_get_deviceid(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_devid_rsp *devid; + int i; + const char *product=NULL; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Device ID command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Device ID command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + devid = (struct ipm_devid_rsp *) rsp->data; + printf("Device ID : %i\n", + devid->device_id); + printf("Device Revision : %i\n", + devid->device_revision & IPM_DEV_DEVICE_ID_REV_MASK); + printf("Firmware Revision : %u.%02x\n", + devid->fw_rev1 & IPM_DEV_FWREV1_MAJOR_MASK, + devid->fw_rev2); + printf("IPMI Version : %x.%x\n", + IPM_DEV_IPMI_VERSION_MAJOR(devid->ipmi_version), + IPM_DEV_IPMI_VERSION_MINOR(devid->ipmi_version)); + printf("Manufacturer ID : %lu\n", + (long)IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id)); + printf("Manufacturer Name : %s\n", + val2str( (long)IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id), + ipmi_oem_info) ); + + printf("Product ID : %u (0x%02x%02x)\n", + buf2short((uint8_t *)(devid->product_id)), + devid->product_id[1], devid->product_id[0]); + + product=oemval2str(IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id), + (devid->product_id[1]<<8)+devid->product_id[0], + ipmi_oem_product_info); + + if (product!=NULL) { + printf("Product Name : %s\n", product); + } + + printf("Device Available : %s\n", + (devid->fw_rev1 & IPM_DEV_FWREV1_AVAIL_MASK) ? + "no" : "yes"); + printf("Provides Device SDRs : %s\n", + (devid->device_revision & IPM_DEV_DEVICE_ID_SDR_MASK) ? + "yes" : "no"); + printf("Additional Device Support :\n"); + for (i = 0; i < IPM_DEV_ADTL_SUPPORT_BITS; i++) { + if (devid->adtl_device_support & (1 << i)) { + printf(" %s\n", ipm_dev_adtl_dev_support[i]); + } + } + if (rsp->data_len == sizeof(*devid)) { + printf("Aux Firmware Rev Info : \n"); + /* These values could be looked-up by vendor if documented, + * so we put them on individual lines for better treatment later + */ + printf(" 0x%02x\n 0x%02x\n 0x%02x\n 0x%02x\n", + devid->aux_fw_rev[0], + devid->aux_fw_rev[1], + devid->aux_fw_rev[2], + devid->aux_fw_rev[3]); + } + return 0; +} + +/* Structure follow the IPMI V.2 Rev 1.0 + * See Table 20-10 */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif + +struct ipmi_guid { + uint32_t time_low; /* timestamp low field */ + uint16_t time_mid; /* timestamp middle field */ + uint16_t time_hi_and_version; /* timestamp high field and version number */ + uint8_t clock_seq_hi_variant;/* clock sequence high field and variant */ + uint8_t clock_seq_low; /* clock sequence low field */ + uint8_t node[6]; /* node */ +} ATTRIBUTE_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +/* ipmi_mc_get_guid - print this MC GUID + * + * @intf: ipmi interface + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_get_guid(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipmi_guid guid; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_GUID; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get GUID command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get GUID command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (rsp->data_len == sizeof(struct ipmi_guid)) { + char tbuf[40]; + time_t s; + memset(tbuf, 0, 40); + memset(&guid, 0, sizeof(struct ipmi_guid)); + memcpy(&guid, rsp->data, rsp->data_len); + + /* Kipp - changed order of last field (node) to follow specification */ + printf("System GUID : %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\n", + guid.time_low, guid.time_mid, guid.time_hi_and_version, + guid.clock_seq_hi_variant << 8 | guid.clock_seq_low, + guid.node[0], guid.node[1], guid.node[2], + guid.node[3], guid.node[4], guid.node[5]); + + s = (time_t)guid.time_low; /* Kipp - removed the BSWAP_32, it was not needed here */ + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", localtime(&s)); + printf("Timestamp : %s\n", tbuf); + } + else { + lprintf(LOG_ERR, "Invalid GUID length %d", rsp->data_len); + } + + return 0; +} + +/* ipmi_mc_get_selftest - returns and print selftest results + * + * @intf: ipmi interface + */ +static int ipmi_mc_get_selftest(struct ipmi_intf * intf) +{ + int rv = 0; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_selftest_rsp *sft_res; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_SELF_TEST; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + lprintf(LOG_ERR, "No response from devices\n"); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Bad response: (%s)", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + sft_res = (struct ipm_selftest_rsp *) rsp->data; + + if (sft_res->code == IPM_SFT_CODE_OK) { + printf("Selftest: passed\n"); + rv = 0; + } + + else if (sft_res->code == IPM_SFT_CODE_NOT_IMPLEMENTED) { + printf("Selftest: not implemented\n"); + rv = -1; + } + + else if (sft_res->code == IPM_SFT_CODE_DEV_CORRUPTED) { + printf("Selftest: device corrupted\n"); + rv = -1; + + if (sft_res->test & IPM_SELFTEST_SEL_ERROR) { + printf(" -> SEL device not accessible\n"); + } + if (sft_res->test & IPM_SELFTEST_SDR_ERROR) { + printf(" -> SDR repository not accesible\n"); + } + if (sft_res->test & IPM_SELFTEST_FRU_ERROR) { + printf("FRU device not accessible\n"); + } + if (sft_res->test & IPM_SELFTEST_IPMB_ERROR) { + printf("IPMB signal lines do not respond\n"); + } + if (sft_res->test & IPM_SELFTEST_SDRR_EMPTY) { + printf("SDR repository empty\n"); + } + if (sft_res->test & IPM_SELFTEST_INTERNAL_USE) { + printf("Internal Use Area corrupted\n"); + } + if (sft_res->test & IPM_SELFTEST_FW_BOOTBLOCK) { + printf("Controller update boot block corrupted\n"); + } + if (sft_res->test & IPM_SELFTEST_FW_CORRUPTED) { + printf("controller operational firmware corrupted\n"); + } + } + + else if (sft_res->code == IPM_SFT_CODE_FATAL_ERROR) { + printf("Selftest : fatal error\n"); + printf("Failure code : %02x\n", sft_res->test); + rv = -1; + } + + else if (sft_res->code == IPM_SFT_CODE_RESERVED) { + printf("Selftest: N/A"); + rv = -1; + } + + else { + printf("Selftest : device specific (%02Xh)\n", sft_res->code); + printf("Failure code : %02Xh\n", sft_res->test); + rv = 0; + } + + return rv; +} + +/* ipmi_mc_get_watchdog + * + * @intf: ipmi interface + * + * returns 0 on success + * returns -1 on error + */ + +const char *wdt_use_string[8] = { + "Reserved", + "BIOS FRB2", + "BIOS/POST", + "OS Load", + "SMS/OS", + "OEM", + "Reserved", + "Reserved" +}; + +const char *wdt_action_string[8] = { + "No action", + "Hard Reset", + "Power Down", + "Power Cycle", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +static int +ipmi_mc_get_watchdog(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_get_watchdog_rsp * wdt_res; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_WATCHDOG_TIMER; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Watchdog Timer command failed"); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Get Watchdog Timer command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + wdt_res = (struct ipm_get_watchdog_rsp *) rsp->data; + + printf("Watchdog Timer Use: %s (0x%02x)\n", + wdt_use_string[(wdt_res->timer_use & 0x07 )], wdt_res->timer_use); + printf("Watchdog Timer Is: %s\n", + wdt_res->timer_use & 0x40 ? "Started/Running" : "Stopped"); + printf("Watchdog Timer Actions: %s (0x%02x)\n", + wdt_action_string[(wdt_res->timer_actions&0x07)], wdt_res->timer_actions); + printf("Pre-timeout interval: %d seconds\n", wdt_res->pre_timeout); + printf("Timer Expiration Flags: 0x%02x\n", wdt_res->timer_use_exp); + printf("Initial Countdown: %i sec\n", + ((wdt_res->initial_countdown_msb << 8) | wdt_res->initial_countdown_lsb)/10); + printf("Present Countdown: %i sec\n", + (((wdt_res->present_countdown_msb << 8) | wdt_res->present_countdown_lsb)) / 10); + + return 0; +} + +/* ipmi_mc_shutoff_watchdog + * + * @intf: ipmi interface + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_shutoff_watchdog(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_SET_WATCHDOG_TIMER; + req.msg.data = msg_data; + req.msg.data_len = 6; + + /* + * The only set cmd we're allowing is to shut off the timer. + * Turning on the timer should be the job of the ipmi watchdog driver. + * See 'modinfo ipmi_watchdog' for more info. (NOTE: the reset + * command will restart the timer if it's already been initialized.) + * + * Out-of-band watchdog set commands can still be sent via the raw + * command interface but this is a very dangerous thing to do since + * a periodic "poke"/reset over a network is unreliable. This is + * not a recommended way to use the IPMI watchdog commands. + */ + + msg_data[0] = IPM_WATCHDOG_SMS_OS; + msg_data[1] = IPM_WATCHDOG_NO_ACTION; + msg_data[2] = 0x00; /* pretimeout interval */ + msg_data[3] = IPM_WATCHDOG_CLEAR_SMS_OS; + msg_data[4] = 0xb8; /* countdown lsb (100 ms/count) */ + msg_data[5] = 0x0b; /* countdown msb - 5 mins */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Watchdog Timer Shutoff command failed!"); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Watchdog Timer Shutoff command failed! %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("Watchdog Timer Shutoff successful -- timer stopped\n"); + return 0; +} + + +/* ipmi_mc_rst_watchdog + * + * @intf: ipmi interface + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_mc_rst_watchdog(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_RESET_WATCHDOG_TIMER; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Reset Watchdog Timer command failed!"); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Reset Watchdog Timer command failed: %s", + (rsp->ccode == IPM_WATCHDOG_RESET_ERROR) ? + "Attempt to reset unitialized watchdog" : + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("IPMI Watchdog Timer Reset - countdown restarted!\n"); + return 0; +} + +/* ipmi_mc_main - top-level handler for MC functions + * + * @intf: ipmi interface + * @argc: number of arguments + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_mc_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc < 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_mc_usage(); + rc = (-1); + } + else if (strncmp(argv[0], "help", 4) == 0) { + printf_mc_usage(); + rc = 0; + } + else if (strncmp(argv[0], "reset", 5) == 0) { + if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_mc_reset_usage(); + rc = (-1); + } + else if (strncmp(argv[1], "help", 4) == 0) { + printf_mc_reset_usage(); + rc = 0; + } + else if (strncmp(argv[1], "cold", 4) == 0) { + rc = ipmi_mc_reset(intf, BMC_COLD_RESET); + } + else if (strncmp(argv[1], "warm", 4) == 0) { + rc = ipmi_mc_reset(intf, BMC_WARM_RESET); + } + else { + lprintf(LOG_ERR, "Invalid mc/bmc %s command: %s", argv[0], argv[1]); + printf_mc_reset_usage(); + rc = (-1); + } + } + else if (strncmp(argv[0], "info", 4) == 0) { + rc = ipmi_mc_get_deviceid(intf); + } + else if (strncmp(argv[0], "guid", 4) == 0) { + rc = ipmi_mc_get_guid(intf); + } + else if (strncmp(argv[0], "getenables", 10) == 0) { + rc = ipmi_mc_get_enables(intf); + } + else if (strncmp(argv[0], "setenables", 10) == 0) { + rc = ipmi_mc_set_enables(intf, argc-1, &(argv[1])); + } + else if (!strncmp(argv[0], "selftest", 8)) { + rc = ipmi_mc_get_selftest(intf); + } + else if (!strncmp(argv[0], "watchdog", 8)) { + if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + print_watchdog_usage(); + rc = (-1); + } + else if (strncmp(argv[1], "help", 4) == 0) { + print_watchdog_usage(); + rc = 0; + } + else if (strncmp(argv[1], "get", 3) == 0) { + rc = ipmi_mc_get_watchdog(intf); + } + else if(strncmp(argv[1], "off", 3) == 0) { + rc = ipmi_mc_shutoff_watchdog(intf); + } + else if(strncmp(argv[1], "reset", 5) == 0) { + rc = ipmi_mc_rst_watchdog(intf); + } + else { + lprintf(LOG_ERR, "Invalid mc/bmc %s command: %s", argv[0], argv[1]); + print_watchdog_usage(); + rc = (-1); + } + } + else if (strncmp(argv[0], "getsysinfo", 10) == 0) { + rc = ipmi_sysinfo_main(intf, argc, argv, 0); + } + else if (strncmp(argv[0], "setsysinfo", 10) == 0) { + rc = ipmi_sysinfo_main(intf, argc, argv, 1); + } + else { + lprintf(LOG_ERR, "Invalid mc/bmc command: %s", argv[0]); + printf_mc_usage(); + rc = (-1); + } + return rc; +} + +/* + * sysinfo_param() - function converts sysinfo param to int + * + * @str - user input string + * @maxset - ? + * + * returns (-1) on error + * returns > 0 on success + */ +static int +sysinfo_param(const char *str, int *maxset) +{ + if (!str || !maxset) + return (-1); + + *maxset = 4; + if (!strcmp(str, "system_name")) + return IPMI_SYSINFO_HOSTNAME; + else if (!strcmp(str, "primary_os_name")) + return IPMI_SYSINFO_PRIMARY_OS_NAME; + else if (!strcmp(str, "os_name")) + return IPMI_SYSINFO_OS_NAME; + else if (!strcmp(str, "delloem_os_version")) + return IPMI_SYSINFO_DELL_OS_VERSION; + else if (!strcmp(str, "delloem_url")) { + *maxset = 2; + return IPMI_SYSINFO_DELL_URL; + } + + return (-1); +} + +/* + * ipmi_mc_getsysinfo() - function processes the IPMI Get System Info command + * + * @intf - ipmi interface + * @param - parameter eg. 0xC0..0xFF = OEM + * @block - number of block parameters + * @set - number of set parameters + * @len - length of buffer + * @buffer - pointer to buffer + * + * returns (-1) on failure + * returns 0 on success + * returns > 0 IPMI code + */ +int +ipmi_mc_getsysinfo(struct ipmi_intf * intf, int param, int block, int set, + int len, void *buffer) +{ + uint8_t data[4]; + struct ipmi_rs *rsp = NULL; + struct ipmi_rq req = {0}; + + memset(buffer, 0, len); + memset(data, 0, 4); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_GET_SYS_INFO; + req.msg.data_len = 4; + req.msg.data = data; + + if (verbose > 1) + printf("getsysinfo: %.2x/%.2x/%.2x\n", param, block, set); + + data[0] = 0; /* get/set */ + data[1] = param; + data[2] = block; + data[3] = set; + + /* + * Format of get output is: + * u8 param_rev + * u8 selector + * u8 encoding bit[0-3]; + * u8 length + * u8 data0[14] + */ + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + return (-1); + + if (rsp->ccode == 0) { + if (len > rsp->data_len) + len = rsp->data_len; + if (len && buffer) + memcpy(buffer, rsp->data, len); + } + return rsp->ccode; +} + +/* + * ipmi_mc_setsysinfo() - function processes the IPMI Set System Info command + * + * @intf - ipmi interface + * @len - length of buffer + * @buffer - pointer to buffer + * + * returns (-1) on failure + * returns 0 on success + * returns > 0 IPMI code + */ +int +ipmi_mc_setsysinfo(struct ipmi_intf * intf, int len, void *buffer) +{ + struct ipmi_rs *rsp = NULL; + struct ipmi_rq req = {0}; + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = IPMI_SET_SYS_INFO; + req.msg.data_len = len; + req.msg.data = buffer; + + /* + * Format of set input: + * u8 param rev + * u8 selector + * u8 data1[16] + */ + rsp = intf->sendrecv(intf, &req); + if (rsp != NULL) { + return rsp->ccode; + } + return -1; +} + +static int +ipmi_sysinfo_main(struct ipmi_intf *intf, int argc, char ** argv, int is_set) +{ + char *str; + unsigned char infostr[256]; + unsigned char paramdata[18]; + int len, maxset, param, pos, rc, set; + + if (argc == 2 && strcmp(argv[1], "help") == 0) { + printf_sysinfo_usage(1); + return 0; + } + else if (argc < 2 || (is_set == 1 && argc < 3)) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_sysinfo_usage(1); + return (-1); + } + + /* Get Parameters */ + if ((param = sysinfo_param(argv[1], &maxset)) < 0) { + lprintf(LOG_ERR, "Invalid mc/bmc %s command: %s", argv[0], argv[1]); + printf_sysinfo_usage(1); + return (-1); + } + + rc = 0; + if (is_set != 0) { + str = argv[2]; + set = pos = 0; + len = strlen(str); + + /* first block holds 14 bytes, all others hold 16 */ + if ((len + 2 + 15) / 16 >= maxset) + len = (maxset * 16) - 2; + + do { + memset(paramdata, 0, sizeof(paramdata)); + paramdata[0] = param; + paramdata[1] = set; + if (set == 0) { + /* First block is special case */ + paramdata[2] = 0; /* ascii encoding */ + paramdata[3] = len; /* length */ + strncpy(paramdata + 4, str + pos, IPMI_SYSINFO_SET0_SIZE); + pos += IPMI_SYSINFO_SET0_SIZE; + } + else { + strncpy(paramdata + 2, str + pos, IPMI_SYSINFO_SETN_SIZE); + pos += IPMI_SYSINFO_SETN_SIZE; + } + rc = ipmi_mc_setsysinfo(intf, 18, paramdata); + + if (rc) + break; + + set++; + } while (pos < len); + } + else { + memset(infostr, 0, sizeof(infostr)); + /* Read blocks of data */ + pos = 0; + for (set = 0; set < maxset; set++) { + rc = ipmi_mc_getsysinfo(intf, param, set, 0, 18, paramdata); + + if (rc) + break; + + if (set == 0) { + /* First block is special case */ + if ((paramdata[2] & 0xF) == 0) { + /* Determine max number of blocks to read */ + maxset = ((paramdata[3] + 2) + 15) / 16; + } + memcpy(infostr + pos, paramdata + 4, IPMI_SYSINFO_SET0_SIZE); + pos += IPMI_SYSINFO_SET0_SIZE; + } + else { + memcpy(infostr + pos, paramdata + 2, IPMI_SYSINFO_SETN_SIZE); + pos += IPMI_SYSINFO_SETN_SIZE; + } + } + printf("%s\n", infostr); + } + if (rc < 0) { + lprintf(LOG_ERR, "%s %s set %d command failed", argv[0], argv[1], set); + } + else if (rc == 0x80) { + lprintf(LOG_ERR, "%s %s parameter not supported", argv[0], argv[1]); + } + else if (rc > 0) { + lprintf(LOG_ERR, "%s command failed: %s", argv[0], + val2str(rc, completion_code_vals)); + } + return rc; +} diff --git a/lib/ipmi_oem.c b/lib/ipmi_oem.c new file mode 100644 index 0000000..89495c0 --- /dev/null +++ b/lib/ipmi_oem.c @@ -0,0 +1,168 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_sel.h> + +static int ipmi_oem_supermicro(struct ipmi_intf * intf); +static int ipmi_oem_ibm(struct ipmi_intf * intf); + +static struct ipmi_oem_handle ipmi_oem_list[] = { + { + name: "supermicro", + desc: "Supermicro IPMIv1.5 BMC with OEM LAN authentication support", + setup: ipmi_oem_supermicro, + }, + { + name: "intelwv2", + desc: "Intel SE7501WV2 IPMIv1.5 BMC with extra LAN communication support", + }, + { + name: "intelplus", + desc: "Intel IPMI 2.0 BMC with RMCP+ communication support", + }, + { + name: "icts", + desc: "IPMI 2.0 ICTS compliance support", + }, + { + name: "ibm", + desc: "IBM OEM support", + setup: ipmi_oem_ibm, + }, + { + name: "i82571spt", + desc: "Intel 82571 MAC with integrated RMCP+ support in super pass-through mode", + }, + { + name: "kontron", + desc: "Kontron OEM big buffer support" + }, + { 0 } +}; + +/* Supermicro IPMIv2 BMCs use OEM authtype */ +static int +ipmi_oem_supermicro(struct ipmi_intf * intf) +{ + ipmi_intf_session_set_authtype(intf, IPMI_SESSION_AUTHTYPE_OEM); + return 0; +} + +static int +ipmi_oem_ibm(struct ipmi_intf * intf) +{ + char * filename; + if ((filename = getenv("IPMI_OEM_IBM_DATAFILE")) == NULL) { + lprintf(LOG_ERR, "Unable to read IPMI_OEM_IBM_DATAFILE from environment"); + return -1; + } + return ipmi_sel_oem_init((const char *)filename); +} + +/* ipmi_oem_print - print list of OEM handles + */ +void +ipmi_oem_print(void) +{ + struct ipmi_oem_handle * oem; + lprintf(LOG_NOTICE, "\nOEM Support:"); + for (oem=ipmi_oem_list; oem->name != NULL && oem->desc != NULL; oem++) { + lprintf(LOG_NOTICE, "\t%-12s %s", oem->name, oem->desc); + } + lprintf(LOG_NOTICE, ""); +} + +/* ipmi_oem_setup - do initial setup of OEM handle + * + * @intf: ipmi interface + * @oemtype: OEM handle name + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_oem_setup(struct ipmi_intf * intf, char * oemtype) +{ + struct ipmi_oem_handle * oem; + int rc = 0; + + if (oemtype == NULL || + strncmp(oemtype, "help", 4) == 0 || + strncmp(oemtype, "list", 4) == 0) { + ipmi_oem_print(); + return -1; + } + + for (oem=ipmi_oem_list; oem->name != NULL; oem++) { + if (strncmp(oemtype, oem->name, strlen(oem->name)) == 0) + break; + } + + if (oem->name == NULL) + return -1; + + /* save pointer for later use */ + intf->oem = oem; + + /* run optional setup function if it is defined */ + if (oem->setup != NULL) { + lprintf(LOG_DEBUG, "Running OEM setup for \"%s\"", oem->desc); + rc = oem->setup(intf); + } + + return rc; +} + +/* ipmi_oem_active - used to determine if a particular OEM type is set + * + * @intf: ipmi interface + * @oemtype: string containing name of ipmi handle to check + * + * returns 1 if requested ipmi handle is active + * returns 0 otherwise + */ +int +ipmi_oem_active(struct ipmi_intf * intf, const char * oemtype) +{ + if (intf->oem == NULL) + return 0; + + if (strncmp(intf->oem->name, oemtype, strlen(oemtype)) == 0) + return 1; + + return 0; +} diff --git a/lib/ipmi_pef.c b/lib/ipmi_pef.c new file mode 100644 index 0000000..154bf40 --- /dev/null +++ b/lib/ipmi_pef.c @@ -0,0 +1,890 @@ +/* + * Copyright (c) 2004 Dell Computers. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Dell Computers, or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * DELL COMPUTERS ("DELL") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * DELL OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF DELL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <math.h> +#include <time.h> + +#include <ipmitool/bswap.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_pef.h> + +extern int verbose; +/* +// common kywd/value printf() templates +*/ +static const char * pef_fld_fmts[][2] = { + {"%-*s : %u\n", " | %u"}, /* F_DEC: unsigned value */ + {"%-*s : %d\n", " | %d"}, /* F_INT: signed value */ + {"%-*s : %s\n", " | %s"}, /* F_STR: string value */ + {"%-*s : 0x%x\n", " | 0x%x"}, /* F_HEX: "N hex digits" */ + {"%-*s : 0x%04x\n", " | 0x%04x"}, /* F_2XD: "2 hex digits" */ + {"%-*s : 0x%02x\n", " | 0x%02x"}, /* F_1XD: "1 hex digit" */ + {"%-*s : %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + " | %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"}, +}; +typedef enum { + F_DEC, + F_INT, + F_STR, + F_HEX, + F_2XD, + F_1XD, + F_UID, +} fmt_e; +#define KYWD_LENGTH 24 +static int first_field = 1; + +static const char * pef_flag_fmts[][3] = { + {"", "false", "true"}, + {"supported", "un", ""}, + {"active", "in", ""}, + {"abled", "dis", "en"}, +}; +static const char * listitem[] = {" | %s", ",%s", "%s"}; + +const char * +ipmi_pef_bit_desc(struct bit_desc_map * map, uint32_t value) +{ /* + // return description/text label(s) for the given value. + // NB: uses a static buffer + */ + static char buf[128]; + char * p; + struct desc_map * pmap; + uint32_t match, index; + + *(p = buf) = '\0'; + index = 2; + for (pmap=map->desc_maps; pmap && pmap->desc; pmap++) { + if (map->desc_map_type == BIT_DESC_MAP_LIST) + match = (value == pmap->mask); + else + match = ((value & pmap->mask) == pmap->mask); + + if (match) { + sprintf(p, listitem[index], pmap->desc); + p = strchr(p, '\0'); + if (map->desc_map_type != BIT_DESC_MAP_ALL) + break; + index = 1; + } + } + if (p == buf) + return("None"); + + return((const char *)buf); +} + +void +ipmi_pef_print_flags(struct bit_desc_map * map, flg_e type, uint32_t val) +{ /* + // print features/flags, using val (a bitmask), according to map. + // observe the verbose flag, and print any labels, etc. based on type + */ + struct desc_map * pmap; + uint32_t maskval, index; + + index = 0; + for (pmap=map->desc_maps; pmap && pmap->desc; pmap++) { + maskval = (val & pmap->mask); + if (verbose) + printf("%-*s : %s%s\n", KYWD_LENGTH, + ipmi_pef_bit_desc(map, pmap->mask), + pef_flag_fmts[type][1 + (maskval != 0)], + pef_flag_fmts[type][0]); + else if (maskval != 0) { + printf(listitem[index], ipmi_pef_bit_desc(map, maskval)); + index = 1; + } + } +} + +static void +ipmi_pef_print_field(const char * fmt[2], const char * label, unsigned long val) +{ /* + // print a 'field' (observes 'verbose' flag) + */ + if (verbose) + printf(fmt[0], KYWD_LENGTH, label, val); + else if (first_field) + printf(&fmt[1][2], val); /* skip field separator */ + else + printf(fmt[1], val); + + first_field = 0; +} + +void +ipmi_pef_print_dec(const char * text, uint32_t val) +{ /* unsigned */ + ipmi_pef_print_field(pef_fld_fmts[F_DEC], text, val); +} + +void +ipmi_pef_print_int(const char * text, uint32_t val) +{ /* signed */ + ipmi_pef_print_field(pef_fld_fmts[F_INT], text, val); +} + +void +ipmi_pef_print_hex(const char * text, uint32_t val) +{ /* hex */ + ipmi_pef_print_field(pef_fld_fmts[F_HEX], text, val); +} + +void +ipmi_pef_print_str(const char * text, const char * val) +{ /* string */ + ipmi_pef_print_field(pef_fld_fmts[F_STR], text, (unsigned long)val); +} + +void +ipmi_pef_print_2xd(const char * text, uint8_t u1, uint8_t u2) +{ /* 2 hex digits */ + uint32_t val = ((u1 << 8) + u2) & 0xffff; + ipmi_pef_print_field(pef_fld_fmts[F_2XD], text, val); +} + +void +ipmi_pef_print_1xd(const char * text, uint32_t val) +{ /* 1 hex digit */ + ipmi_pef_print_field(pef_fld_fmts[F_1XD], text, val); +} + +static struct ipmi_rs * +ipmi_pef_msg_exchange(struct ipmi_intf * intf, struct ipmi_rq * req, char * txt) +{ /* + // common IPMItool rqst/resp handling + */ + struct ipmi_rs * rsp = intf->sendrecv(intf, req); + if (!rsp) { + return(NULL); + } else if (rsp->ccode == 0x80) { + return(NULL); /* Do not output error, just unsupported parameters */ + } else if (rsp->ccode) { + lprintf(LOG_ERR, " **Error %x in '%s' command", rsp->ccode, txt); + return(NULL); + } + if (verbose > 2) { + printbuf(rsp->data, rsp->data_len, txt); + } + return(rsp); +} + +static uint8_t +ipmi_pef_get_policy_table(struct ipmi_intf * intf, + struct pef_cfgparm_policy_table_entry ** table) +{ /* + // get the PEF policy table: allocate space, fillin, and return its size + // NB: the caller must free the returned area (when returned size > 0) + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_cfgparm_selector psel; + struct pef_cfgparm_policy_table_entry * ptbl, * ptmp; + uint32_t i; + uint8_t tbl_size; + + memset(&psel, 0, sizeof(psel)); + psel.id = PEF_CFGPARM_ID_PEF_ALERT_POLICY_TABLE_SIZE; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CONFIG_PARMS; + req.msg.data = (uint8_t *)&psel; + req.msg.data_len = sizeof(psel); + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert policy table size"); + if (!rsp) + return(0); + tbl_size = (rsp->data[1] & PEF_POLICY_TABLE_SIZE_MASK); + i = (tbl_size * sizeof(struct pef_cfgparm_policy_table_entry)); + if (!i + || (ptbl = (struct pef_cfgparm_policy_table_entry *)malloc(i)) == NULL) + return(0); + + memset(&psel, 0, sizeof(psel)); + psel.id = PEF_CFGPARM_ID_PEF_ALERT_POLICY_TABLE_ENTRY; + for (ptmp=ptbl, i=1; i<=tbl_size; i++) { + psel.set = (i & PEF_POLICY_TABLE_ID_MASK); + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert policy table entry"); + if (!rsp + || i != (rsp->data[1] & PEF_POLICY_TABLE_ID_MASK)) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert policy table entry"); + free(ptbl); + ptbl = NULL; + tbl_size = 0; + break; + } + memcpy(ptmp, &rsp->data[1], sizeof(*ptmp)); + ptmp++; + } + + *table = ptbl; + return(tbl_size); +} + +static void +ipmi_pef_print_lan_dest(struct ipmi_intf * intf, uint8_t ch, uint8_t dest) +{ /* + // print LAN alert destination info + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_lan_cfgparm_selector lsel; + struct pef_lan_cfgparm_dest_type * ptype; + struct pef_lan_cfgparm_dest_info * pinfo; + char buf[32]; + uint8_t tbl_size, dsttype, timeout, retries; + + memset(&lsel, 0, sizeof(lsel)); + lsel.id = PEF_LAN_CFGPARM_ID_DEST_COUNT; + lsel.ch = ch; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_CMD_LAN_GET_CONFIG; + req.msg.data = (uint8_t *)&lsel; + req.msg.data_len = sizeof(lsel); + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert destination count"); + if (!rsp) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert destination count"); + return; + } + tbl_size = (rsp->data[1] & PEF_LAN_DEST_TABLE_SIZE_MASK); + //if (tbl_size == 0 || dest == 0) /* LAN alerting not supported */ + // return; + + lsel.id = PEF_LAN_CFGPARM_ID_DESTTYPE; + lsel.set = dest; + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert destination type"); + if (!rsp || rsp->data[1] != lsel.set) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert destination type"); + return; + } + ptype = (struct pef_lan_cfgparm_dest_type *)&rsp->data[1]; + dsttype = (ptype->dest_type & PEF_LAN_DEST_TYPE_MASK); + timeout = ptype->alert_timeout; + retries = (ptype->retries & PEF_LAN_RETRIES_MASK); + ipmi_pef_print_str("Alert destination type", + ipmi_pef_bit_desc(&pef_b2s_lan_desttype, dsttype)); + if (dsttype == PEF_LAN_DEST_TYPE_PET) { + lsel.id = PEF_LAN_CFGPARM_ID_PET_COMMUNITY; + lsel.set = 0; + rsp = ipmi_pef_msg_exchange(intf, &req, "PET community"); + if (!rsp) + lprintf(LOG_ERR, " **Error retrieving %s", + "PET community"); + else { + rsp->data[19] = '\0'; + ipmi_pef_print_str("PET Community", (const char *)&rsp->data[1]); + } + } + ipmi_pef_print_dec("ACK timeout/retry (secs)", timeout); + ipmi_pef_print_dec("Retries", retries); + + lsel.id = PEF_LAN_CFGPARM_ID_DESTADDR; + lsel.set = dest; + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert destination info"); + if (!rsp || rsp->data[1] != lsel.set) + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert destination info"); + else { + pinfo = (struct pef_lan_cfgparm_dest_info *)&rsp->data[1]; + sprintf(buf, "%u.%u.%u.%u", + pinfo->ip[0], pinfo->ip[1], pinfo->ip[2], pinfo->ip[3]); + ipmi_pef_print_str("IP address", buf); + + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + pinfo->mac[0], pinfo->mac[1], pinfo->mac[2], + pinfo->mac[3], pinfo->mac[4], pinfo->mac[5]); + ipmi_pef_print_str("MAC address", buf); + } +} + +static void +ipmi_pef_print_serial_dest_dial(struct ipmi_intf * intf, char * label, + struct pef_serial_cfgparm_selector * ssel) +{ /* + // print a dial string + */ +#define BLOCK_SIZE 16 + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_serial_cfgparm_selector tmp; + char * p, strval[(6 * BLOCK_SIZE) + 1]; + + memset(&tmp, 0, sizeof(tmp)); + tmp.id = PEF_SERIAL_CFGPARM_ID_DEST_DIAL_STRING_COUNT; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_CMD_SERIAL_GET_CONFIG; + req.msg.data = (uint8_t *)&tmp; + req.msg.data_len = sizeof(tmp); + rsp = ipmi_pef_msg_exchange(intf, &req, "Dial string count"); + if (!rsp || (rsp->data[1] & PEF_SERIAL_DIAL_STRING_COUNT_MASK) == 0) + return; /* sssh, not supported */ + + memcpy(&tmp, ssel, sizeof(tmp)); + tmp.id = PEF_SERIAL_CFGPARM_ID_DEST_DIAL_STRING; + tmp.block = 1; + memset(strval, 0, sizeof(strval)); + p = strval; + for (;;) { + rsp = ipmi_pef_msg_exchange(intf, &req, label); + if (!rsp + || (rsp->data[1] != ssel->id) + || (rsp->data[2] != tmp.block)) { + lprintf(LOG_ERR, " **Error retrieving %s", label); + return; + } + memcpy(p, &rsp->data[3], BLOCK_SIZE); + if (strchr(p, '\0') <= (p + BLOCK_SIZE)) + break; + if ((p += BLOCK_SIZE) >= &strval[sizeof(strval)-1]) + break; + tmp.block++; + } + + ipmi_pef_print_str(label, strval); +#undef BLOCK_SIZE +} + +static void +ipmi_pef_print_serial_dest_tap(struct ipmi_intf * intf, + struct pef_serial_cfgparm_selector * ssel) +{ /* + // print TAP destination info + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_serial_cfgparm_selector tmp; + struct pef_serial_cfgparm_tap_svc_settings * pset; + uint8_t dialstr_id, setting_id; + + memset(&tmp, 0, sizeof(tmp)); + tmp.id = PEF_SERIAL_CFGPARM_ID_TAP_ACCT_COUNT; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_CMD_SERIAL_GET_CONFIG; + req.msg.data = (uint8_t *)&tmp; + req.msg.data_len = sizeof(tmp); + rsp = ipmi_pef_msg_exchange(intf, &req, "Number of TAP accounts"); + if (!rsp || (rsp->data[1] & PEF_SERIAL_TAP_ACCT_COUNT_MASK) == 0) + return; /* sssh, not supported */ + + memcpy(&tmp, ssel, sizeof(tmp)); + tmp.id = PEF_SERIAL_CFGPARM_ID_TAP_ACCT_INFO; + rsp = ipmi_pef_msg_exchange(intf, &req, "TAP account info"); + if (!rsp || (rsp->data[1] != tmp.set)) { + lprintf(LOG_ERR, " **Error retrieving %s", + "TAP account info"); + return; + } + dialstr_id = (rsp->data[2] & PEF_SERIAL_TAP_ACCT_INFO_DIAL_STRING_ID_MASK); + dialstr_id >>= PEF_SERIAL_TAP_ACCT_INFO_DIAL_STRING_ID_SHIFT; + setting_id = (rsp->data[2] & PEF_SERIAL_TAP_ACCT_INFO_SVC_SETTINGS_ID_MASK); + tmp.set = dialstr_id; + ipmi_pef_print_serial_dest_dial(intf, "TAP Dial string", &tmp); + + tmp.set = setting_id; + rsp = ipmi_pef_msg_exchange(intf, &req, "TAP service settings"); + if (!rsp || (rsp->data[1] != tmp.set)) { + lprintf(LOG_ERR, " **Error retrieving %s", + "TAP service settings"); + return; + } + pset = (struct pef_serial_cfgparm_tap_svc_settings *)&rsp->data[1]; + ipmi_pef_print_str("TAP confirmation", + ipmi_pef_bit_desc(&pef_b2s_tap_svc_confirm, pset->confirmation_flags)); + + /* TODO : additional TAP settings? */ +} + +static void +ipmi_pef_print_serial_dest_ppp(struct ipmi_intf * intf, + struct pef_serial_cfgparm_selector * ssel) +{ /* + // print PPP destination info + */ + + /* TODO */ +} + +static void +ipmi_pef_print_serial_dest_callback(struct ipmi_intf * intf, + struct pef_serial_cfgparm_selector * ssel) +{ /* + // print callback destination info + */ + + /* TODO */ +} + +static void +ipmi_pef_print_serial_dest(struct ipmi_intf * intf, uint8_t ch, uint8_t dest) +{ /* + // print Serial/PPP alert destination info + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_serial_cfgparm_selector ssel; + uint8_t tbl_size, wrk; + struct pef_serial_cfgparm_dest_info * pinfo; + + memset(&ssel, 0, sizeof(ssel)); + ssel.id = PEF_SERIAL_CFGPARM_ID_DEST_COUNT; + ssel.ch = ch; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_CMD_SERIAL_GET_CONFIG; + req.msg.data = (uint8_t *)&ssel; + req.msg.data_len = sizeof(ssel); + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert destination count"); + if (!rsp) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert destination count"); + return; + } + tbl_size = (rsp->data[1] & PEF_SERIAL_DEST_TABLE_SIZE_MASK); + if (!dest || tbl_size == 0) /* Page alerting not supported */ + return; + + ssel.id = PEF_SERIAL_CFGPARM_ID_DESTINFO; + ssel.set = dest; + rsp = ipmi_pef_msg_exchange(intf, &req, "Alert destination info"); + if (!rsp || rsp->data[1] != ssel.set) + lprintf(LOG_ERR, " **Error retrieving %s", + "Alert destination info"); + else { + pinfo = (struct pef_serial_cfgparm_dest_info *)rsp->data; + wrk = (pinfo->dest_type & PEF_SERIAL_DEST_TYPE_MASK); + ipmi_pef_print_str("Alert destination type", + ipmi_pef_bit_desc(&pef_b2s_serial_desttype, wrk)); + ipmi_pef_print_dec("ACK timeout (secs)", + pinfo->alert_timeout); + ipmi_pef_print_dec("Retries", + (pinfo->retries & PEF_SERIAL_RETRIES_MASK)); + switch (wrk) { + case PEF_SERIAL_DEST_TYPE_DIAL: + ipmi_pef_print_serial_dest_dial(intf, "Serial dial string", &ssel); + break; + case PEF_SERIAL_DEST_TYPE_TAP: + ipmi_pef_print_serial_dest_tap(intf, &ssel); + break; + case PEF_SERIAL_DEST_TYPE_PPP: + ipmi_pef_print_serial_dest_ppp(intf, &ssel); + break; + case PEF_SERIAL_DEST_TYPE_BASIC_CALLBACK: + case PEF_SERIAL_DEST_TYPE_PPP_CALLBACK: + ipmi_pef_print_serial_dest_callback(intf, &ssel); + break; + } + } +} + +static void +ipmi_pef_print_dest(struct ipmi_intf * intf, uint8_t ch, uint8_t dest) +{ /* + // print generic alert destination info + */ + ipmi_pef_print_dec("Destination ID", dest); +} + +void +ipmi_pef_print_event_info(struct pef_cfgparm_filter_table_entry * pef, char * buf) +{ /* + // print PEF entry Event info: class, severity, trigger, etc. + */ + static char * classes[] = {"Discrete", "Threshold", "OEM"}; + uint16_t offmask; + char * p; + int i; + uint8_t t; + + ipmi_pef_print_str("Event severity", + ipmi_pef_bit_desc(&pef_b2s_severities, pef->entry.severity)); + + t = pef->entry.event_trigger; + if (t == PEF_EVENT_TRIGGER_THRESHOLD) + i = 1; + else if (t > PEF_EVENT_TRIGGER_SENSOR_SPECIFIC) + i = 2; + else + i = 0; + ipmi_pef_print_str("Event class", classes[i]); + + offmask = ((pef->entry.event_data_1_offset_mask[1] << 8) + + pef->entry.event_data_1_offset_mask[0]); + + if (offmask == 0xffff || t == PEF_EVENT_TRIGGER_MATCH_ANY) + strcpy(buf, "Any"); + else if (t == PEF_EVENT_TRIGGER_UNSPECIFIED) + strcpy(buf, "Unspecified"); + else if (t == PEF_EVENT_TRIGGER_SENSOR_SPECIFIC) + strcpy(buf, "Sensor-specific"); + else if (t > PEF_EVENT_TRIGGER_SENSOR_SPECIFIC) + strcpy(buf, "OEM"); + else { + sprintf(buf, "(0x%02x/0x%04x)", t, offmask); + p = strchr(buf, '\0'); + for (i=0; i<PEF_B2S_GENERIC_ER_ENTRIES; i++) { + if (offmask & 1) { + if ((t-1) >= PEF_B2S_GENERIC_ER_ENTRIES) { + sprintf(p, ", Unrecognized event trigger"); + } else { + sprintf(p, ",%s", ipmi_pef_bit_desc(pef_b2s_generic_ER[t-1], i)); + } + p = strchr(p, '\0'); + } + offmask >>= 1; + } + } + + ipmi_pef_print_str("Event trigger(s)", buf); +} + +static void +ipmi_pef_print_entry(struct ipmi_rs * rsp, uint8_t id, + struct pef_cfgparm_filter_table_entry * pef) +{ /* + // print a PEF table entry + */ + uint8_t wrk, set; + char buf[128]; + + ipmi_pef_print_dec("PEF table entry", id); + + wrk = !!(pef->entry.config & PEF_CONFIG_ENABLED); + sprintf(buf, "%sactive", (wrk ? "" : "in")); + if (pef->entry.config & PEF_CONFIG_PRECONFIGURED) + strcat(buf, ", pre-configured"); + + ipmi_pef_print_str("Status", buf); + + if (wrk != 0) { + ipmi_pef_print_1xd("Version", rsp->data[0]); + ipmi_pef_print_str("Sensor type", + ipmi_pef_bit_desc(&pef_b2s_sensortypes, pef->entry.sensor_type)); + + if (pef->entry.sensor_number == PEF_SENSOR_NUMBER_MATCH_ANY) + ipmi_pef_print_str("Sensor number", "Any"); + else + ipmi_pef_print_dec("Sensor number", pef->entry.sensor_number); + + ipmi_pef_print_event_info(pef, buf); + ipmi_pef_print_str("Action", + ipmi_pef_bit_desc(&pef_b2s_actions, pef->entry.action)); + + if (pef->entry.action & PEF_ACTION_ALERT) { + set = (pef->entry.policy_number & PEF_POLICY_NUMBER_MASK); + ipmi_pef_print_int("Policy set", set); + } + } +} + +static void +ipmi_pef_list_entries(struct ipmi_intf * intf) +{ /* + // list all PEF table entries + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_cfgparm_selector psel; + struct pef_cfgparm_filter_table_entry * pcfg; + uint32_t i; + uint8_t max_filters; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CAPABILITIES; + rsp = ipmi_pef_msg_exchange(intf, &req, "PEF capabilities"); + if (!rsp + || (max_filters = ((struct pef_capabilities *)rsp->data)->tblsize) == 0) + return; /* sssh, not supported */ + + memset(&psel, 0, sizeof(psel)); + psel.id = PEF_CFGPARM_ID_PEF_FILTER_TABLE_ENTRY; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CONFIG_PARMS; + req.msg.data = (uint8_t *)&psel; + req.msg.data_len = sizeof(psel); + for (i=1; i<=max_filters; i++) { + if (i > 1) + printf("\n"); + psel.set = (i & PEF_FILTER_TABLE_ID_MASK); + rsp = ipmi_pef_msg_exchange(intf, &req, "PEF table entry"); + if (!rsp + || (psel.set != (rsp->data[1] & PEF_FILTER_TABLE_ID_MASK))) { + lprintf(LOG_ERR, " **Error retrieving %s", + "PEF table entry"); + continue; + } + pcfg = (struct pef_cfgparm_filter_table_entry *)&rsp->data[1]; + first_field = 1; + ipmi_pef_print_entry(rsp, psel.set, pcfg); + } +} + +static void +ipmi_pef_list_policies(struct ipmi_intf * intf) +{ /* + // list PEF alert policies + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_cfgparm_policy_table_entry * ptbl = NULL; + struct pef_cfgparm_policy_table_entry * ptmp = NULL; + uint32_t i; + uint8_t wrk, ch, medium, tbl_size; + + tbl_size = ipmi_pef_get_policy_table(intf, &ptbl); + if (!tbl_size) { + if (!ptbl) { + free(ptbl); + ptbl = NULL; + } + return; + } + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_CMD_GET_CHANNEL_INFO; + req.msg.data = &ch; + req.msg.data_len = sizeof(ch); + for (ptmp=ptbl, i=1; i<=tbl_size; i++, ptmp++) { + if ((ptmp->entry.policy & PEF_POLICY_ENABLED) == PEF_POLICY_ENABLED) { + if (i > 1) + printf("\n"); + first_field = 1; + ipmi_pef_print_dec("Alert policy table entry", + (ptmp->data1 & PEF_POLICY_TABLE_ID_MASK)); + ipmi_pef_print_dec("Policy set", + (ptmp->entry.policy & PEF_POLICY_ID_MASK) >> PEF_POLICY_ID_SHIFT); + ipmi_pef_print_str("Policy entry rule", + ipmi_pef_bit_desc(&pef_b2s_policies, (ptmp->entry.policy & PEF_POLICY_FLAGS_MASK))); + + if (ptmp->entry.alert_string_key & PEF_POLICY_EVENT_SPECIFIC) { + ipmi_pef_print_str("Event-specific", "true"); +// continue; + } + wrk = ptmp->entry.chan_dest; + + /* channel/description */ + ch = (wrk & PEF_POLICY_CHANNEL_MASK) >> PEF_POLICY_CHANNEL_SHIFT; + rsp = ipmi_pef_msg_exchange(intf, &req, "Channel info"); + if (!rsp || rsp->data[0] != ch) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Channel info"); + continue; + } + medium = rsp->data[1]; + ipmi_pef_print_dec("Channel number", ch); + ipmi_pef_print_str("Channel medium", + ipmi_pef_bit_desc(&pef_b2s_ch_medium, medium)); + + /* destination/description */ + wrk &= PEF_POLICY_DESTINATION_MASK; + switch (medium) { + case PEF_CH_MEDIUM_TYPE_LAN: + ipmi_pef_print_lan_dest(intf, ch, wrk); + break; + case PEF_CH_MEDIUM_TYPE_SERIAL: + ipmi_pef_print_serial_dest(intf, ch, wrk); + break; + default: + ipmi_pef_print_dest(intf, ch, wrk); + break; + } + } + } + free(ptbl); + ptbl = NULL; +} + +static void +ipmi_pef_get_status(struct ipmi_intf * intf) +{ /* + // report the PEF status + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_cfgparm_selector psel; + char tbuf[40]; + uint32_t timei; + time_t ts; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_LAST_PROCESSED_EVT_ID; + rsp = ipmi_pef_msg_exchange(intf, &req, "Last S/W processed ID"); + if (!rsp) { + lprintf(LOG_ERR, " **Error retrieving %s", + "Last S/W processed ID"); + return; + } + memcpy(&timei, rsp->data, sizeof(timei)); +#if WORDS_BIGENDIAN + timei = BSWAP_32(timei); +#endif + ts = (time_t)timei; + + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&ts)); + + ipmi_pef_print_str("Last SEL addition", tbuf); + ipmi_pef_print_2xd("Last SEL record ID", rsp->data[5], rsp->data[4]); + ipmi_pef_print_2xd("Last S/W processed ID", rsp->data[7], rsp->data[6]); + ipmi_pef_print_2xd("Last BMC processed ID", rsp->data[9], rsp->data[8]); + + memset(&psel, 0, sizeof(psel)); + psel.id = PEF_CFGPARM_ID_PEF_CONTROL; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CONFIG_PARMS; + req.msg.data = (uint8_t *)&psel; + req.msg.data_len = sizeof(psel); + rsp = ipmi_pef_msg_exchange(intf, &req, "PEF control"); + if (!rsp) { + lprintf(LOG_ERR, " **Error retrieving %s", + "PEF control"); + return; + } + ipmi_pef_print_flags(&pef_b2s_control, P_ABLE, rsp->data[1]); + + psel.id = PEF_CFGPARM_ID_PEF_ACTION; + rsp = ipmi_pef_msg_exchange(intf, &req, "PEF action"); + if (!rsp) { + lprintf(LOG_ERR, " **Error retrieving %s", + "PEF action"); + return; + } + ipmi_pef_print_flags(&pef_b2s_actions, P_ACTV, rsp->data[1]); +} + +static void +ipmi_pef_get_info(struct ipmi_intf * intf) +{ /* + // report PEF capabilities + System GUID + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct pef_capabilities * pcap; + struct pef_cfgparm_selector psel; + struct pef_cfgparm_policy_table_entry * ptbl = NULL; + uint8_t * uid; + uint8_t actions, tbl_size; + + tbl_size = ipmi_pef_get_policy_table(intf, &ptbl); + if (!ptbl) { + free(ptbl); + ptbl = NULL; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CAPABILITIES; + rsp = ipmi_pef_msg_exchange(intf, &req, "PEF capabilities"); + if (!rsp) + return; + pcap = (struct pef_capabilities *)rsp->data; + + ipmi_pef_print_1xd("Version", pcap->version); + ipmi_pef_print_dec("PEF table size", pcap->tblsize); + ipmi_pef_print_dec("Alert policy table size", tbl_size); + actions = pcap->actions; + + memset(&psel, 0, sizeof(psel)); + psel.id = PEF_CFGPARM_ID_SYSTEM_GUID; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = IPMI_CMD_GET_PEF_CONFIG_PARMS; + req.msg.data = (uint8_t *)&psel; + req.msg.data_len = sizeof(psel); + rsp = ipmi_pef_msg_exchange(intf, &req, "System GUID"); + uid = NULL; + if (rsp && (rsp->data[1] & PEF_SYSTEM_GUID_USED_IN_PET)) + uid = &rsp->data[2]; + else { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_CMD_GET_SYSTEM_GUID; + rsp = ipmi_pef_msg_exchange(intf, &req, "System GUID"); + if (rsp) + uid = &rsp->data[0]; + } + if (uid) { /* got GUID? */ + if (verbose) + printf(pef_fld_fmts[F_UID][0], KYWD_LENGTH, "System GUID", + uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7], + uid[8], uid[9], uid[10],uid[11],uid[12],uid[13],uid[14],uid[15]); + else + printf(pef_fld_fmts[F_UID][1], + uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7], + uid[8], uid[9], uid[10],uid[11],uid[12],uid[13],uid[14],uid[15]); + } + ipmi_pef_print_flags(&pef_b2s_actions, P_SUPP, actions); +} + +int ipmi_pef_main(struct ipmi_intf * intf, int argc, char ** argv) +{ /* + // PEF subcommand handling + */ + int help = 0; + int rc = 0; + + if (!argc || !strncmp(argv[0], "info", 4)) + ipmi_pef_get_info(intf); + else if (!strncmp(argv[0], "help", 4)) + help = 1; + else if (!strncmp(argv[0], "status", 6)) + ipmi_pef_get_status(intf); + else if (!strncmp(argv[0], "policy", 6)) + ipmi_pef_list_policies(intf); + else if (!strncmp(argv[0], "list", 4)) + ipmi_pef_list_entries(intf); + else { + help = 1; + rc = -1; + lprintf(LOG_ERR, "Invalid PEF command: '%s'\n", argv[0]); + } + + if (help) + lprintf(LOG_NOTICE, "PEF commands: info status policy list"); + else if (!verbose) + printf("\n"); + + return rc; +} diff --git a/lib/ipmi_picmg.c b/lib/ipmi_picmg.c new file mode 100644 index 0000000..d1c82b2 --- /dev/null +++ b/lib/ipmi_picmg.c @@ -0,0 +1,2371 @@ +/* + Copyright (c) Kontron. All right reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Kontron, or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * DELL COMPUTERS ("DELL") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * DELL OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF DELL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + + +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_picmg.h> +#include <ipmitool/ipmi_fru.h> /* for access to link descriptor defines */ +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/log.h> + +#define PICMG_EXTENSION_ATCA_MAJOR_VERSION 2 +#define PICMG_EXTENSION_AMC0_MAJOR_VERSION 4 +#define PICMG_EXTENSION_UTCA_MAJOR_VERSION 5 + + +#define PICMG_EKEY_MODE_QUERY 0 +#define PICMG_EKEY_MODE_PRINT_ALL 1 +#define PICMG_EKEY_MODE_PRINT_ENABLED 2 +#define PICMG_EKEY_MODE_PRINT_DISABLED 3 + +#define PICMG_EKEY_MAX_CHANNEL 16 +#define PICMG_EKEY_MAX_FABRIC_CHANNEL 15 +#define PICMG_EKEY_MAX_INTERFACE 3 + +#define PICMG_EKEY_AMC_MAX_CHANNEL 16 +#define PICMG_EKEY_AMC_MAX_DEVICE 15 /* 4 bits */ + + +typedef enum picmg_bused_resource_mode { + PICMG_BUSED_RESOURCE_SUMMARY, +} t_picmg_bused_resource_mode ; + + +typedef enum picmg_card_type { + PICMG_CARD_TYPE_CPCI, + PICMG_CARD_TYPE_ATCA, + PICMG_CARD_TYPE_AMC, + PICMG_CARD_TYPE_RESERVED +} t_picmg_card_type ; + +/* This is the version of the PICMG Extenstion */ +static t_picmg_card_type PicmgCardType = PICMG_CARD_TYPE_RESERVED; + +void +ipmi_picmg_help (void) +{ + lprintf(LOG_NOTICE, "PICMG commands:"); + lprintf(LOG_NOTICE, " properties - get PICMG properties"); + lprintf(LOG_NOTICE, " frucontrol - FRU control"); + lprintf(LOG_NOTICE, " addrinfo - get address information"); + lprintf(LOG_NOTICE, " activate - activate a FRU"); + lprintf(LOG_NOTICE, " deactivate - deactivate a FRU"); + lprintf(LOG_NOTICE, " policy get - get the FRU activation policy"); + lprintf(LOG_NOTICE, " policy set - set the FRU activation policy"); + lprintf(LOG_NOTICE, " portstate get - get port state"); + lprintf(LOG_NOTICE, + " portstate getdenied - get all denied[disabled] port description"); + lprintf(LOG_NOTICE, + " portstate getgranted - get all granted[enabled] port description"); + lprintf(LOG_NOTICE, + " portstate getall - get all port state description"); + lprintf(LOG_NOTICE, " portstate set - set port state"); + lprintf(LOG_NOTICE, " amcportstate get - get port state"); + lprintf(LOG_NOTICE, " amcportstate set - set port state"); + lprintf(LOG_NOTICE, " led prop - get led properties"); + lprintf(LOG_NOTICE, " led cap - get led color capabilities"); + lprintf(LOG_NOTICE, " led get - get led state"); + lprintf(LOG_NOTICE, " led set - set led state"); + lprintf(LOG_NOTICE, " power get - get power level info"); + lprintf(LOG_NOTICE, " power set - set power level"); + lprintf(LOG_NOTICE, " clk get - get clk state"); + lprintf(LOG_NOTICE, + " clk getdenied - get all(up to 16) denied[disabled] clock descriptions"); + lprintf(LOG_NOTICE, + " clk getgranted - get all(up to 16) granted[enabled] clock descriptions"); + lprintf(LOG_NOTICE, + " clk getall - get all(up to 16) clock descriptions"); + lprintf(LOG_NOTICE, " clk set - set clk state"); + lprintf(LOG_NOTICE, + " busres summary - display brief bused resource status info"); +} + + +struct sAmcAddrMap { + unsigned char ipmbLAddr; + char* amcBayId; + unsigned char siteNum; +} amcAddrMap[] = { + {0xFF, "reserved", 0}, + {0x72, "A1" , 1}, + {0x74, "A2" , 2}, + {0x76, "A3" , 3}, + {0x78, "A4" , 4}, + {0x7A, "B1" , 5}, + {0x7C, "B2" , 6}, + {0x7E, "B3" , 7}, + {0x80, "B4" , 8}, + {0x82, "reserved", 0}, + {0x84, "reserved", 0}, + {0x86, "reserved", 0}, + {0x88, "reserved", 0}, +}; + +/* is_amc_channel - wrapper to convert user input into integer + * AMC Channel range seems to be <0..255>, bits [7:0] + * + * @argv_ptr: source string to convert from; usually argv + * @amc_chan_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_amc_channel(const char *argv_ptr, uint8_t *amc_chan_ptr) +{ + if (!argv_ptr || !amc_chan_ptr) { + lprintf(LOG_ERR, "is_amc_channel(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, amc_chan_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given AMC Channel '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_amc_dev - wrapper to convert user input into integer. + * AMC Dev ID limits are uknown. + * + * @argv_ptr: source string to convert from; usually argv + * @amc_dev_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_amc_dev(const char *argv_ptr, int32_t *amc_dev_ptr) +{ + if (!argv_ptr || !amc_dev_ptr) { + lprintf(LOG_ERR, "is_amc_dev(): invalid argument(s)."); + return (-1); + } + if (str2int(argv_ptr, amc_dev_ptr) == 0 && *amc_dev_ptr >= 0) { + return 0; + } + lprintf(LOG_ERR, "Given PICMG Device '%s' is invalid.", + argv_ptr); + return (-1); +} +/* is_amc_intf - wrapper to convert user input into integer. + * AMC Interface (ID) limits are uknown. + * + * @argv_ptr: source string to convert from; usually argv + * @amc_intf_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_amc_intf(const char *argv_ptr, int32_t *amc_intf_ptr) +{ + if (!argv_ptr || !amc_intf_ptr) { + lprintf(LOG_ERR, "is_amc_intf(): invalid argument(s)."); + return (-1); + } + if (str2int(argv_ptr, amc_intf_ptr) == 0 && *amc_intf_ptr >= 0) { + return 0; + } + lprintf(LOG_ERR, "Given PICMG Interface '%s' is invalid.", + argv_ptr); + return (-1); +} +/* is_amc_port - wrapper to convert user input into integer. + * AMC Port limits are uknown. + * + * @argv_ptr: source string to convert from; usually argv + * @amc_port_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_amc_port(const char *argv_ptr, int32_t *amc_port_ptr) +{ + if (!argv_ptr || !amc_port_ptr) { + lprintf(LOG_ERR, "is_amc_port(): invalid argument(s)."); + return (-1); + } + if (str2int(argv_ptr, amc_port_ptr) == 0 && *amc_port_ptr >= 0) { + return 0; + } + lprintf(LOG_ERR, "Given PICMG Port '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_clk_acc - wrapper to convert user input into integer. + * Clock Accuracy limits are uknown[1byte by spec]. + * + * @argv_ptr: source string to convert from; usually argv + * @clk_acc_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_acc(const char *argv_ptr, uint8_t *clk_acc_ptr) +{ + if (!argv_ptr || !clk_acc_ptr) { + lprintf(LOG_ERR, "is_clk_acc(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, clk_acc_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Clock Accuracy '%s' is invalid.", + argv_ptr); + return (-1); +} +/* is_clk_family - wrapper to convert user input into integer. + * Clock Family limits are uknown[1byte by spec]. + * + * @argv_ptr: source string to convert from; usually argv + * @clk_family_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_family(const char *argv_ptr, uint8_t *clk_family_ptr) +{ + if (!argv_ptr || !clk_family_ptr) { + lprintf(LOG_ERR, "is_clk_family(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, clk_family_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Clock Family '%s' is invalid.", + argv_ptr); + return (-1); +} +/* is_clk_freq - wrapper to convert user input into integer. + * Clock Frequency limits are uknown, but specification says + * 3Bytes + 1B checksum + * + * @argv_ptr: source string to convert from; usually argv + * @clk_freq_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_freq(const char *argv_ptr, uint32_t *clk_freq_ptr) +{ + if (!argv_ptr || !clk_freq_ptr) { + lprintf(LOG_ERR, "is_clk_freq(): invalid argument(s)."); + return (-1); + } + if (str2uint(argv_ptr, clk_freq_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Clock Frequency '%s' is invalid.", + argv_ptr); + return (-1); +} +/* is_clk_id - wrapper to convert user input into integer. + * Clock ID limits are uknown, however it's 1B by specification and I've + * found two ranges: <1..5> or <0..15> + * + * @argv_ptr: source string to convert from; usually argv + * @clk_id_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_id(const char *argv_ptr, uint8_t *clk_id_ptr) +{ + if (!argv_ptr || !clk_id_ptr) { + lprintf(LOG_ERR, "is_clk_id(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, clk_id_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Clock ID '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_clk_index - wrapper to convert user input into integer. + * Clock Index limits are uknown[1B by spec] + * + * @argv_ptr: source string to convert from; usually argv + * @clk_index_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_index(const char *argv_ptr, uint8_t *clk_index_ptr) +{ + if (!argv_ptr || !clk_index_ptr) { + lprintf(LOG_ERR, "is_clk_index(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, clk_index_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Clock Index '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_clk_resid - wrapper to convert user input into integer. + * Clock Resource Index(?) limits are uknown, but maximum seems to be 15. + * + * @argv_ptr: source string to convert from; usually argv + * @clk_resid_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_resid(const char *argv_ptr, int8_t *clk_resid_ptr) +{ + if (!argv_ptr || !clk_resid_ptr) { + lprintf(LOG_ERR, "is_clk_resid(): invalid argument(s)."); + return (-1); + } + if (str2char(argv_ptr, clk_resid_ptr) == 0 + && *clk_resid_ptr > (-1)) { + return 0; + } + lprintf(LOG_ERR, "Given Resource ID '%s' is invalid.", + clk_resid_ptr); + return (-1); +} +/* is_clk_setting - wrapper to convert user input into integer. + * Clock Setting is a 1B bitfield: + * x [7:4] - reserved + * x [3] - state - 0/1 + * x [2] - direction - 0/1 + * x [1:0] - PLL ctrl - 00/01/10/11[Reserved] + * + * @argv_ptr: source string to convert from; usually argv + * @clk_setting_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_clk_setting(const char *argv_ptr, uint8_t *clk_setting_ptr) +{ + if (!argv_ptr || !clk_setting_ptr) { + lprintf(LOG_ERR, "is_clk_setting(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, clk_setting_ptr) == 0) { + return 0; + } + /* FIXME - validate bits 4-7 are 0 ? */ + lprintf(LOG_ERR, "Given Clock Setting '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_enable - wrapper to convert user input into integer. + * Valid input range for Enable is <0..1>. + * + * @argv_ptr: source string to convert from; usually argv + * @enable_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_enable(const char *argv_ptr, uint8_t *enable_ptr) +{ + if (!argv_ptr || !enable_ptr) { + lprintf(LOG_ERR, "is_enable(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, enable_ptr) == 0 + && (*enable_ptr == 0 || *enable_ptr == 1)) { + return 0; + } + lprintf(LOG_ERR, "Given Enable '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_enable - wrapper to convert user input into integer. + * LED colors: + * - valid <1..6>, <0xE..0xF> + * - reserved [0, 7] + * - undefined <8..D> + * + * @argv_ptr: source string to convert from; usually argv + * @enable_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_led_color(const char *argv_ptr, uint8_t *led_color_ptr) +{ + if (!argv_ptr || !led_color_ptr) { + lprintf(LOG_ERR, "is_led_color(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, led_color_ptr) != 0) { + lprintf(LOG_ERR, "Given LED Color '%s' is invalid.", + argv_ptr); + lprintf(LOG_ERR, + "LED Color must be from ranges: <1..6>, <0xE..0xF>"); + return (-1); + } + if ((*led_color_ptr >= 1 && *led_color_ptr <= 6) + || (*led_color_ptr >= 0xE && *led_color_ptr <= 0xF)) { + return 0; + } + lprintf(LOG_ERR, "Given LED Color '%s' is out of range.", argv_ptr); + lprintf(LOG_ERR, "LED Color must be from ranges: <1..6>, <0xE..0xF>"); + return (-1); +} +/* is_led_duration - wrapper to convert user input into integer. + * LED duration range is <1..127> + * + * @argv_ptr: source string to convert from; usually argv + * @enable_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_led_duration(const char *argv_ptr, uint8_t *led_duration_ptr) +{ + if (!argv_ptr || !led_duration_ptr) { + lprintf(LOG_ERR, "is_led_duration(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, led_duration_ptr) == 0 + && *led_duration_ptr > 0 && *led_duration_ptr <= 127) { + return 0; + } + lprintf(LOG_ERR, "Given LED Duration '%s' is invalid", argv_ptr); + return (-1); +} +/* is_led_function - wrapper to convert user input into integer. + * LED functions, however, might differ by OEM: + * - 0x00 - off override + * - <0x01..0xFA> - blinking override + * - 0xFB - lamp test state + * - 0xFC - state restored to local ctrl state + * - <0xFD..0xFE> - reserved + * - 0xFF - on override + * + * @argv_ptr: source string to convert from; usually argv + * @led_fn_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_led_function(const char *argv_ptr, uint8_t *led_fn_ptr) +{ + if (!argv_ptr || !led_fn_ptr) { + lprintf(LOG_ERR, "is_led_function(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, led_fn_ptr) == 0 + && (*led_fn_ptr < 0xFD || *led_fn_ptr > 0xFE)) { + return 0; + } + lprintf(LOG_ERR, "Given LED Function '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_led_id - wrapper to convert user input into integer. + * LED ID range seems to be <0..255> + * + * @argv_ptr: source string to convert from; usually argv + * @led_id_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_led_id(const char *argv_ptr, uint8_t *led_id_ptr) +{ + if (!argv_ptr || !led_id_ptr) { + lprintf(LOG_ERR, "is_led_id(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, led_id_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given LED ID '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_link_group - wrapper to convert user input into integer. + * Link Grouping ID limis are unknown, bits [31:24] by spec. + * + * @argv_ptr: source string to convert from; usually argv + * @link_grp_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_link_group(const char *argv_ptr, uint8_t *link_grp_ptr) +{ + if (!argv_ptr || !link_grp_ptr) { + lprintf(LOG_ERR, "is_link_group(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, link_grp_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Link Group '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_link_type - wrapper to convert user input into integer. + * Link Type limits are unknown, bits [19:12] + * + * @argv_ptr: source string to convert from; usually argv + * @link_type_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_link_type(const char *argv_ptr, uint8_t *link_type_ptr) +{ + if (!argv_ptr || !link_type_ptr) { + lprintf(LOG_ERR, "is_link_type(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, link_type_ptr) == 0) { + return 0; + } + lprintf(LOG_ERR, "Given Link Type '%s' is invalid.", argv_ptr); + return (-1); +} +/* is_link_type_ext - wrapper to convert user input into integer. + * Link Type Extension limits are unknown, bits [23:20] => <0..15> ? + * + * @argv_ptr: source string to convert from; usually argv + * @link_type_ext_ptr: pointer where to store result + * returns: zero on success, other values mean error + */ +int +is_link_type_ext(const char *argv_ptr, uint8_t *link_type_ext_ptr) +{ + if (!argv_ptr || !link_type_ext_ptr) { + lprintf(LOG_ERR, "is_link_type_ext(): invalid argument(s)."); + return (-1); + } + if (str2uchar(argv_ptr, link_type_ext_ptr) != 0 + || *link_type_ext_ptr > 15) { + lprintf(LOG_ERR, + "Given Link Type Extension '%s' is invalid.", + argv_ptr); + return (-1); + } + return 0; +} + +int +ipmi_picmg_getaddr(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data[5]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + msg_data[0] = 0; /* picmg identifier */ + msg_data[1] = 0; /* default fru id */ + + if(argc > 0) { + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + } + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + lprintf(LOG_ERR, "Error. No valid response received."); + return (-1); + } else if (rsp->ccode) { + lprintf(LOG_ERR, "Error getting address information CC: 0x%02x", + rsp->ccode); + return (-1); + } + + printf("Hardware Address : 0x%02x\n", rsp->data[1]); + printf("IPMB-0 Address : 0x%02x\n", rsp->data[2]); + printf("FRU ID : 0x%02x\n", rsp->data[4]); + printf("Site ID : 0x%02x\n", rsp->data[5]); + + printf("Site Type : "); + switch (rsp->data[6]) { + case PICMG_ATCA_BOARD: + printf("ATCA board\n"); + break; + case PICMG_POWER_ENTRY: + printf("Power Entry Module\n"); + break; + case PICMG_SHELF_FRU: + printf("Shelf FRU\n"); + break; + case PICMG_DEDICATED_SHMC: + printf("Dedicated Shelf Manager\n"); + break; + case PICMG_FAN_TRAY: + printf("Fan Tray\n"); + break; + case PICMG_FAN_FILTER_TRAY: + printf("Fan Filter Tray\n"); + break; + case PICMG_ALARM: + printf("Alarm module\n"); + break; + case PICMG_AMC: + printf("AMC"); + printf(" -> IPMB-L Address: 0x%02x\n", amcAddrMap[rsp->data[5]].ipmbLAddr); + break; + case PICMG_PMC: + printf("PMC\n"); + break; + case PICMG_RTM: + printf("RTM\n"); + break; + default: + if (rsp->data[6] >= 0xc0 && rsp->data[6] <= 0xcf) { + printf("OEM\n"); + } else { + printf("unknown\n"); + } + } + + return 0; +} + +int +ipmi_picmg_properties(struct ipmi_intf * intf, int show ) +{ + unsigned char PicmgExtMajorVersion; + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD; + req.msg.data = &msg_data; + req.msg.data_len = 1; + msg_data = 0; + + rsp = intf->sendrecv(intf, &req); + if (!rsp || rsp->ccode) { + lprintf(LOG_ERR, "Error getting address information."); + return -1; + } + + if( show ) + { + printf("PICMG identifier : 0x%02x\n", rsp->data[0]); + printf("PICMG Ext. Version : %i.%i\n", rsp->data[1]&0x0f, + (rsp->data[1]&0xf0) >> 4); + printf("Max FRU Device ID : 0x%02x\n", rsp->data[2]); + printf("FRU Device ID : 0x%02x\n", rsp->data[3]); + } + + /* We cache the major extension version ... + to know how to format some commands */ + PicmgExtMajorVersion = rsp->data[1]&0x0f; + + if( PicmgExtMajorVersion == PICMG_CPCI_MAJOR_VERSION ) { + PicmgCardType = PICMG_CARD_TYPE_CPCI; + } + else if( PicmgExtMajorVersion == PICMG_ATCA_MAJOR_VERSION) { + PicmgCardType = PICMG_CARD_TYPE_ATCA; + } + else if( PicmgExtMajorVersion == PICMG_AMC_MAJOR_VERSION) { + PicmgCardType = PICMG_CARD_TYPE_AMC; + } + + return 0; +} + + + +#define PICMG_FRU_DEACTIVATE (unsigned char) 0x00 +#define PICMG_FRU_ACTIVATE (unsigned char) 0x01 + +int +ipmi_picmg_fru_activation(struct ipmi_intf * intf, int argc, char ** argv, unsigned char state) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + struct picmg_set_fru_activation_cmd cmd; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_FRU_ACTIVATION_CMD; + req.msg.data = (unsigned char*) &cmd; + req.msg.data_len = 3; + + cmd.picmg_id = 0; /* PICMG identifier */ + if (is_fru_id(argv[0], &(cmd.fru_id)) != 0) { + return (-1); + } + cmd.fru_state = state; + + rsp = intf->sendrecv(intf, &req); + + if (!rsp || rsp->ccode) { + lprintf(LOG_ERR, "Error activation/deactivation of FRU."); + return -1; + } + if (rsp->data[0] != 0x00) { + lprintf(LOG_ERR, "Error activation/deactivation of FRU."); + } + + return 0; +} + + +int +ipmi_picmg_fru_activation_policy_get(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[4]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_FRU_POLICY_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + + msg_data[0] = 0; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + if (rsp->ccode) { + lprintf(LOG_ERR, "FRU activation policy get failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + printf(" %s\n", ((rsp->data[1] & 0x01) == 0x01) ? + "activation locked" : "activation not locked"); + printf(" %s\n", ((rsp->data[1] & 0x02) == 0x02) ? + "deactivation locked" : "deactivation not locked"); + + return 0; +} + +int +ipmi_picmg_fru_activation_policy_set(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[4]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_SET_FRU_POLICY_CMD; + req.msg.data = msg_data; + req.msg.data_len = 4; + + msg_data[0] = 0; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + if (str2uchar(argv[1], &msg_data[2]) != 0 || msg_data[2] > 1) { + /* FRU Lock Mask */ + lprintf(LOG_ERR, "Given FRU Lock Mask '%s' is invalid.", + argv[1]); + return (-1); + } + if (str2uchar(argv[2], &msg_data[3]) != 0 || msg_data[3] > 1) { + /* FRU Act Policy */ + lprintf(LOG_ERR, + "Given FRU Activation Policy '%s' is invalid.", + argv[2]); + return (-1); + } + msg_data[2]&= 0x03; + msg_data[3]&= 0x03; + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "FRU activation policy set failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + return 0; +} + +#define PICMG_MAX_LINK_PER_CHANNEL 4 + +int +ipmi_picmg_portstate_get(struct ipmi_intf * intf, int32_t interface, + uint8_t channel, int mode) +{ + struct ipmi_rs * rsp = NULL; + struct ipmi_rq req; + + unsigned char msg_data[4]; + + struct fru_picmgext_link_desc* d; /* descriptor pointer for rec. data */ + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_PORT_STATE_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + + msg_data[0] = 0x00; /* PICMG identifier */ + msg_data[1] = (interface & 0x3)<<6; /* interface */ + msg_data[1] |= (channel & 0x3F); /* channel number */ + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + if( mode == PICMG_EKEY_MODE_QUERY ){ + lprintf(LOG_ERR, "FRU portstate get failed with CC code 0x%02x", + rsp->ccode); + } + return -1; + } + + if (rsp->data_len >= 6) { + int index; + /* add support for more than one link per channel */ + for(index=0;index<PICMG_MAX_LINK_PER_CHANNEL;index++){ + if( rsp->data_len > (1+ (index*5))){ + d = (struct fru_picmgext_link_desc *) &(rsp->data[1 + (index*5)]); + + if + ( + mode == PICMG_EKEY_MODE_PRINT_ALL + || + mode == PICMG_EKEY_MODE_QUERY + || + ( + mode == PICMG_EKEY_MODE_PRINT_ENABLED + && + rsp->data[5 + (index*5) ] == 0x01 + ) + || + ( + mode == PICMG_EKEY_MODE_PRINT_DISABLED + && + rsp->data[5 + (index*5) ] == 0x00 + ) + ) + { + printf(" Link Grouping ID: 0x%02x\n", d->grouping); + printf(" Link Type Extension: 0x%02x\n", d->ext); + printf(" Link Type: 0x%02x ", d->type); + if (d->type == 0 || d->type == 0xff) + { + printf("Reserved %d\n",d->type); + } + else if (d->type >= 0x06 && d->type <= 0xef) + { + printf("Reserved\n"); + } + else if (d->type >= 0xf0 && d->type <= 0xfe) + { + printf("OEM GUID Definition\n"); + } + else + { + switch (d->type) + { + case FRU_PICMGEXT_LINK_TYPE_BASE: + printf("PICMG 3.0 Base Interface 10/100/1000\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET: + printf("PICMG 3.1 Ethernet Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND: + printf("PICMG 3.2 Infiniband Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR: + printf("PICMG 3.3 Star Fabric Interface\n"); + break; + case FRU_PICMGEXT_LINK_TYPE_PCIE: + printf("PCI Express Fabric Interface\n"); + break; + default: + printf("Invalid\n"); + break; + } + } + printf(" Link Designator: \n"); + printf(" Port Flag: 0x%02x\n", d->desig_port); + printf(" Interface: 0x%02x - ", d->desig_if); + switch (d->desig_if) + { + case FRU_PICMGEXT_DESIGN_IF_BASE: + printf("Base Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_FABRIC: + printf("Fabric Interface\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL: + printf("Update Channel\n"); + break; + case FRU_PICMGEXT_DESIGN_IF_RESERVED: + printf("Reserved\n"); + break; + default: + printf("Invalid"); + break; + } + printf(" Channel Number: 0x%02x\n", d->desig_channel); + printf(" STATE: %s\n", + ( rsp->data[5 +(index*5)] == 0x01) ?"enabled":"disabled"); + printf("\n"); + } + } + } + } + else + { + lprintf(LOG_ERR, "Unexpected answer, can't print result."); + } + + return 0; +} + + +int +ipmi_picmg_portstate_set(struct ipmi_intf * intf, int32_t interface, + uint8_t channel, int32_t port, uint8_t type, + uint8_t typeext, uint8_t group, uint8_t enable) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_SET_PORT_STATE_CMD; + req.msg.data = msg_data; + req.msg.data_len = 6; + + msg_data[0] = 0x00; /* PICMG identifier */ + msg_data[1] = (channel & 0x3f) | ((interface & 3) << 6); + msg_data[2] = (port & 0xf) | ((type & 0xf) << 4); + msg_data[3] = ((type >> 4) & 0xf) | ((typeext & 0xf) << 4); + msg_data[4] = group & 0xff; + msg_data[5] = (enable & 0x01); /* enable/disable */ + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Picmg portstate set failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + return 0; +} + + + +/* AMC.0 commands */ + +#define PICMG_AMC_MAX_LINK_PER_CHANNEL 4 + +int +ipmi_picmg_amc_portstate_get(struct ipmi_intf * intf, int32_t device, + uint8_t channel, int mode) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[4]; + + struct fru_picmgext_amc_link_info* d; /* descriptor pointer for rec. data */ + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_AMC_GET_PORT_STATE_CMD; + req.msg.data = msg_data; + + /* FIXME : add check for AMC or carrier device */ + if(device == -1 || PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + req.msg.data_len = 2; /* for amc only channel */ + }else{ + req.msg.data_len = 3; /* for carrier channel and device */ + } + + msg_data[0] = 0x00; /* PICMG identifier */ + msg_data[1] = channel ; + msg_data[2] = device ; + + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + if( mode == PICMG_EKEY_MODE_QUERY ){ + lprintf(LOG_ERR, "Amc portstate get failed with CC code 0x%02x", + rsp->ccode); + } + return -1; + } + + if (rsp->data_len >= 5) { + int index; + + /* add support for more than one link per channel */ + for(index=0;index<PICMG_AMC_MAX_LINK_PER_CHANNEL;index++){ + + if( rsp->data_len > (1+ (index*4))){ + unsigned char type; + unsigned char ext; + unsigned char grouping; + unsigned char port; + unsigned char enabled; + d = (struct fru_picmgext_amc_link_info *)&(rsp->data[1 + (index*4)]); + + + /* Removed endianness check here, probably not required + as we dont use bitfields */ + port = d->linkInfo[0] & 0x0F; + type = ((d->linkInfo[0] & 0xF0) >> 4 )|(d->linkInfo[1] & 0x0F ); + ext = ((d->linkInfo[1] & 0xF0) >> 4 ); + grouping = d->linkInfo[2]; + + + enabled = rsp->data[4 + (index*4) ]; + + if + ( + mode == PICMG_EKEY_MODE_PRINT_ALL + || + mode == PICMG_EKEY_MODE_QUERY + || + ( + mode == PICMG_EKEY_MODE_PRINT_ENABLED + && + enabled == 0x01 + ) + || + ( + mode == PICMG_EKEY_MODE_PRINT_DISABLED + && + enabled == 0x00 + ) + ) + { + if(device == -1 || PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + printf(" Link device : AMC\n"); + }else{ + printf(" Link device : 0x%02x\n", device ); + } + + printf(" Link Grouping ID: 0x%02x\n", grouping); + + if (type == 0 || type == 1 ||type == 0xff) + { + printf(" Link Type Extension: 0x%02x\n", ext); + printf(" Link Type: Reserved\n"); + } + else if (type >= 0xf0 && type <= 0xfe) + { + printf(" Link Type Extension: 0x%02x\n", ext); + printf(" Link Type: OEM GUID Definition\n"); + } + else + { + if (type <= FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE ) + { + printf(" Link Type Extension: %s\n", + amc_link_type_ext_str[type][ext]); + printf(" Link Type: %s\n", + amc_link_type_str[type]); + } + else{ + printf(" Link Type Extension: 0x%02x\n", ext); + printf(" Link Type: undefined\n"); + } + } + printf(" Link Designator: \n"); + printf(" Channel Number: 0x%02x\n", channel); + printf(" Port Flag: 0x%02x\n", port ); + printf(" STATE: %s\n", + ( enabled == 0x01 )?"enabled":"disabled"); + printf("\n"); + } + } + } + } + else + { + lprintf(LOG_NOTICE,"ipmi_picmg_amc_portstate_get"\ + "Unexpected answer, can't print result"); + } + + return 0; +} + + +int +ipmi_picmg_amc_portstate_set(struct ipmi_intf * intf, uint8_t channel, + int32_t port, uint8_t type, uint8_t typeext, + uint8_t group, uint8_t enable, int32_t device) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data[7]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_AMC_SET_PORT_STATE_CMD; + req.msg.data = msg_data; + + msg_data[0] = 0x00; /* PICMG identifier*/ + msg_data[1] = channel; /* channel id */ + msg_data[2] = port & 0xF; /* port flags */ + msg_data[2] |= (type & 0x0F)<<4; /* type */ + msg_data[3] = (type & 0xF0)>>4; /* type */ + msg_data[3] |= (typeext & 0x0F)<<4; /* extension */ + msg_data[4] = (group & 0xFF); /* group */ + msg_data[5] = (enable & 0x01); /* state */ + req.msg.data_len = 6; + + /* device id - only for carrier needed */ + if (device >= 0) { + msg_data[6] = device; + req.msg.data_len = 7; + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Amc portstate set failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + return 0; +} + + +int +ipmi_picmg_get_led_properties(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_FRU_LED_PROPERTIES_CMD; + req.msg.data = msg_data; + req.msg.data_len = 2; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "LED get properties failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + printf("General Status LED Properties: 0x%2x\n", rsp->data[1] ); + printf("App. Specific LED Count: 0x%2x\n", rsp->data[2] ); + + return 0; +} + +int +ipmi_picmg_get_led_capabilities(struct ipmi_intf * intf, int argc, char ** argv) +{ + int i; + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_LED_COLOR_CAPABILITIES_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0 + || is_led_id(argv[1], &msg_data[2]) != 0) { + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "LED get capabilities failed with CC code 0x%02x", + rsp->ccode); + return -1; + } + + printf("LED Color Capabilities: "); + for ( i=0 ; i<8 ; i++ ) { + if ( rsp->data[1] & (0x01 << i) ) { + printf("%s, ", led_color_str[ i ]); + } + } + printf("\n"); + + printf("Default LED Color in\n"); + printf(" LOCAL control: %s\n", led_color_str[ rsp->data[2] ] ); + printf(" OVERRIDE state: %s\n", led_color_str[ rsp->data[3] ] ); + + return 0; +} + +int +ipmi_picmg_get_led_state(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_FRU_LED_STATE_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0 + || is_led_id(argv[1], &msg_data[2]) != 0) { + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "LED get state failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + printf("LED states: %x ", rsp->data[1] ); + if (rsp->data[1] == 0x1) + printf("[LOCAL CONTROL]\n"); + else if (rsp->data[1] == 0x2) + printf("[OVERRIDE]\n"); + else if (rsp->data[1] == 0x4) + printf("[LAMPTEST]\n"); + else + printf("\n"); + + printf(" Local Control function: %x ", rsp->data[2] ); + if (rsp->data[2] == 0x0) + printf("[OFF]\n"); + else if (rsp->data[2] == 0xff) + printf("[ON]\n"); + else + printf("[BLINKING]\n"); + + printf(" Local Control On-Duration: %x\n", rsp->data[3] ); + printf(" Local Control Color: %x [%s]\n", rsp->data[4], led_color_str[ rsp->data[4] ]); + + /* override state or lamp test */ + if (rsp->data[1] == 0x02) { + printf(" Override function: %x ", rsp->data[5] ); + if (rsp->data[2] == 0x0) + printf("[OFF]\n"); + else if (rsp->data[2] == 0xff) + printf("[ON]\n"); + else + printf("[BLINKING]\n"); + + printf(" Override On-Duration: %x\n", rsp->data[6] ); + printf(" Override Color: %x [%s]\n", rsp->data[7], led_color_str[ rsp->data[7] ]); + + }else if (rsp->data[1] == 0x06) { + printf(" Override function: %x ", rsp->data[5] ); + if (rsp->data[2] == 0x0) + printf("[OFF]\n"); + else if (rsp->data[2] == 0xff) + printf("[ON]\n"); + else + printf("[BLINKING]\n"); + printf(" Override On-Duration: %x\n", rsp->data[6] ); + printf(" Override Color: %x [%s]\n", rsp->data[7], led_color_str[ rsp->data[7] ]); + printf(" Lamp test duration: %x\n", rsp->data[8] ); + } + + return 0; +} + +int +ipmi_picmg_set_led_state(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_SET_FRU_LED_STATE_CMD; + req.msg.data = msg_data; + req.msg.data_len = 6; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0 + || is_led_id(argv[1], &msg_data[2]) != 0 + || is_led_function(argv[2], &msg_data[3]) != 0 + || is_led_duration(argv[3], &msg_data[4]) != 0 + || is_led_color(argv[4], &msg_data[5]) != 0) { + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "LED set state failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + + return 0; +} + +int +ipmi_picmg_get_power_level(struct ipmi_intf * intf, int argc, char ** argv) +{ + int i; + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_POWER_LEVEL_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + /* PICMG Power Type - <0..3> */ + if (str2uchar(argv[1], &msg_data[2]) != 0 || msg_data[2] > 3) { + lprintf(LOG_ERR, "Given Power Type '%s' is invalid", + argv[1]); + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Power level get failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + printf("Dynamic Power Configuration: %s\n", (rsp->data[1]&0x80)==0x80?"enabled":"disabled" ); + printf("Actual Power Level: %i\n", (rsp->data[1] & 0xf)); + printf("Delay to stable Power: %i\n", rsp->data[2]); + printf("Power Multiplier: %i\n", rsp->data[3]); + + + for ( i = 1; i+3 < rsp->data_len ; i++ ) { + printf(" Power Draw %i: %i\n", i, (rsp->data[i+3]) * rsp->data[3] / 10); + } + return 0; +} + +int +ipmi_picmg_set_power_level(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_SET_POWER_LEVEL_CMD; + req.msg.data = msg_data; + req.msg.data_len = 4; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + /* PICMG Power Level - <0x00..0x14>, [0xFF] */ + if (str2uchar(argv[1], &msg_data[2]) != 0 + || (msg_data[2] > 0x14 && msg_data[2] != 0xFF)) { + lprintf(LOG_ERR, + "Given PICMG Power Level '%s' is invalid.", + argv[1]); + return (-1); + } + /* PICMG Present-to-desired - <0..1> */ + if (str2uchar(argv[2], &msg_data[3]) != 0 || msg_data[3] > 1) { + lprintf(LOG_ERR, + "Given PICMG Present-to-desired '%s' is invalid.", + argv[2]); + return (-1); + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Power level set failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + return 0; +} + +int +ipmi_picmg_bused_resource(struct ipmi_intf * intf, t_picmg_bused_resource_mode mode) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + memset(&req, 0, sizeof(req)); + + int status = 0; + switch ( mode ) { + case PICMG_BUSED_RESOURCE_SUMMARY: + { + t_picmg_busres_resource_id resource; + t_picmg_busres_board_cmd_types cmd =PICMG_BUSRES_BOARD_CMD_QUERY; + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_BUSED_RESOURCE_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + + /* IF BOARD + query for all resources + */ + for( resource=PICMG_BUSRES_METAL_TEST_BUS_1;resource<=PICMG_BUSRES_SYNC_CLOCK_GROUP_3;resource+=(t_picmg_busres_resource_id)1 ) { + msg_data[0] = 0x00; /* PICMG identifier */ + msg_data[1] = (unsigned char) cmd; + msg_data[2] = (unsigned char) resource; + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + printf("bused resource control: no response\n"); + return -1; + } + + if (rsp->ccode) { + printf("bused resource control: returned CC code 0x%02x\n", rsp->ccode); + return -1; + } else { + printf("Resource 0x%02x '%-26s' : 0x%02x [%s] \n" , + resource, val2str(resource,picmg_busres_id_vals), + rsp->data[1], oemval2str(cmd,rsp->data[1], + picmg_busres_board_status_vals)); + } + } + } + break; + default : + break; + } + + return status; +} + +int +ipmi_picmg_fru_control(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_FRU_CONTROL_CMD; + req.msg.data = msg_data; + req.msg.data_len = 3; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_fru_id(argv[0], &msg_data[1]) != 0) { + return (-1); + } + /* FRU Control Option, valid range: <0..4> */ + if (str2uchar(argv[1], &msg_data[2]) != 0 || msg_data[2] > 4) { + lprintf(LOG_ERR, + "Given FRU Control Option '%s' is invalid.", + argv[1]); + return (-1); + } + + printf("FRU Device Id: %d FRU Control Option: %s\n", msg_data[1], \ + val2str( msg_data[2], picmg_frucontrol_vals)); + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "frucontrol failed with CC code 0x%02x", rsp->ccode); + return -1; + } else { + printf("frucontrol: ok\n"); + } + + + + return 0; +} + + +int +ipmi_picmg_clk_get(struct ipmi_intf * intf, uint8_t clk_id, int8_t clk_res, + int mode) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char enabled; + unsigned char direction; + + unsigned char msg_data[6]; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_AMC_GET_CLK_STATE_CMD; + req.msg.data = msg_data; + + msg_data[0] = 0x00; /* PICMG identifier */ + msg_data[1] = clk_id; + + if(clk_res == -1 || PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + req.msg.data_len = 2; /* for amc only channel */ + }else{ + req.msg.data_len = 3; /* for carrier channel and device */ + msg_data[2] = clk_res; + } + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode && (mode == PICMG_EKEY_MODE_QUERY) ) { + lprintf(LOG_ERR, "Clk get failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + if (rsp->ccode == 0 ) { + enabled = (rsp->data[1]&0x8)!=0; + direction = (rsp->data[1]&0x4)!=0; + + if + ( + mode == PICMG_EKEY_MODE_QUERY + || + mode == PICMG_EKEY_MODE_PRINT_ALL + || + ( + mode == PICMG_EKEY_MODE_PRINT_DISABLED + && + enabled == 0 + ) + || + ( + mode == PICMG_EKEY_MODE_PRINT_ENABLED + && + enabled == 1 + ) + ) { + if( PicmgCardType != PICMG_CARD_TYPE_AMC ) { + printf("CLK resource id : %3d [ %s ]\n", clk_res , + oemval2str( ((clk_res>>6)&0x03), (clk_res&0x0F), + picmg_clk_resource_vals)); + } else { + printf("CLK resource id : N/A [ AMC Module ]\n"); + clk_res = 0x40; /* Set */ + } + printf("CLK id : %3d [ %s ]\n", clk_id, + oemval2str( ((clk_res>>6)&0x03), clk_id , + picmg_clk_id_vals)); + + + printf("CLK setting : 0x%02x\n", rsp->data[1]); + printf(" - state: %s\n", (enabled)?"enabled":"disabled"); + printf(" - direction: %s\n", (direction)?"Source":"Receiver"); + printf(" - PLL ctrl: 0x%x\n", rsp->data[1]&0x3); + + if(enabled){ + unsigned long freq = 0; + freq = ( rsp->data[5] << 0 + | rsp->data[6] << 8 + | rsp->data[7] << 16 + | rsp->data[8] << 24 ); + printf(" - Index: %3d\n", rsp->data[2]); + printf(" - Family: %3d [ %s ] \n", rsp->data[3], + val2str( rsp->data[3], picmg_clk_family_vals)); + printf(" - AccLVL: %3d [ %s ] \n", rsp->data[4], + oemval2str( rsp->data[3], rsp->data[4], + picmg_clk_accuracy_vals)); + + printf(" - Freq: %ld\n", freq); + } + } + } + return 0; +} + + +int +ipmi_picmg_clk_set(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + unsigned char msg_data[11] = {0}; + uint32_t freq = 0; + + memset(&req, 0, sizeof(req)); + + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_AMC_SET_CLK_STATE_CMD; + req.msg.data = msg_data; + + msg_data[0] = 0x00; /* PICMG identifier */ + if (is_clk_id(argv[0], &msg_data[1]) != 0 + || is_clk_index(argv[1], &msg_data[2]) != 0 + || is_clk_setting(argv[2], &msg_data[3]) != 0 + || is_clk_family(argv[3], &msg_data[4]) != 0 + || is_clk_acc(argv[4], &msg_data[5]) != 0 + || is_clk_freq(argv[5], &freq) != 0) { + return (-1); + } + + msg_data[6] = (freq >> 0)& 0xFF; /* freq */ + msg_data[7] = (freq >> 8)& 0xFF; /* freq */ + msg_data[8] = (freq >>16)& 0xFF; /* freq */ + msg_data[9] = (freq >>24)& 0xFF; /* freq */ + + req.msg.data_len = 10; + if( PicmgCardType == PICMG_CARD_TYPE_ATCA ) + { + if( argc > 7) + { + req.msg.data_len = 11; + if (is_clk_resid(argv[6], &msg_data[10]) != 0) { + return (-1); + } + } + else + { + lprintf(LOG_ERR, "Missing resource id for atca board."); + return -1; + } + } + +#if 1 +printf("## ID: %d\n", msg_data[1]); +printf("## index: %d\n", msg_data[2]); +printf("## setting: 0x%02x\n", msg_data[3]); +printf("## family: %d\n", msg_data[4]); +printf("## acc: %d\n", msg_data[5]); +printf("## freq: %ld\n", freq ); +printf("## res: %d\n", msg_data[10]); +#endif + + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_ERR, "No valid response received."); + return -1; + } + + if (rsp->ccode) { + lprintf(LOG_ERR, "Clk set failed with CC code 0x%02x", rsp->ccode); + return -1; + } + + return 0; +} + + + +int +ipmi_picmg_main (struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + int showProperties = 0; + + if (argc == 0 || (!strncmp(argv[0], "help", 4))) { + ipmi_picmg_help(); + return 0; + } + + /* Get PICMG properties is called to obtain version information */ + if (argc !=0 && !strncmp(argv[0], "properties", 10)) { + showProperties =1; + } + rc = ipmi_picmg_properties(intf,showProperties); + + /* address info command */ + if (!strncmp(argv[0], "addrinfo", 8)) { + rc = ipmi_picmg_getaddr(intf, argc-1, &argv[1]); + } + else if (!strncmp(argv[0], "busres", 6)) { + if (argc > 1) { + if (!strncmp(argv[1], "summary", 7)) { + ipmi_picmg_bused_resource(intf, PICMG_BUSED_RESOURCE_SUMMARY ); + } + } else { + lprintf(LOG_NOTICE, "usage: busres summary"); + } + } + /* fru control command */ + else if (!strncmp(argv[0], "frucontrol", 10)) { + if (argc > 2) { + rc = ipmi_picmg_fru_control(intf, argc-1, &(argv[1])); + } + else { + lprintf(LOG_NOTICE, "usage: frucontrol <FRU-ID> <OPTION>"); + lprintf(LOG_NOTICE, " OPTION:"); + lprintf(LOG_NOTICE, " 0 - Cold Reset"); + lprintf(LOG_NOTICE, " 1 - Warm Reset"); + lprintf(LOG_NOTICE, " 2 - Graceful Reboot"); + lprintf(LOG_NOTICE, " 3 - Issue Diagnostic Interrupt"); + lprintf(LOG_NOTICE, " 4 - Quiesce [AMC only]"); + lprintf(LOG_NOTICE, " 5-255 - Reserved"); + + return -1; + } + + } + + /* fru activation command */ + else if (!strncmp(argv[0], "activate", 8)) { + if (argc > 1) { + rc = ipmi_picmg_fru_activation(intf, argc-1, &(argv[1]), PICMG_FRU_ACTIVATE); + } + else { + lprintf(LOG_ERR, "Specify the FRU to activate."); + return -1; + } + } + + /* fru deactivation command */ + else if (!strncmp(argv[0], "deactivate", 10)) { + if (argc > 1) { + rc = ipmi_picmg_fru_activation(intf, argc-1, &(argv[1]), PICMG_FRU_DEACTIVATE); + }else { + lprintf(LOG_ERR, "Specify the FRU to deactivate."); + return -1; + } + } + + /* activation policy command */ + else if (!strncmp(argv[0], "policy", 6)) { + if (argc > 1) { + if (!strncmp(argv[1], "get", 3)) { + if (argc > 2) { + rc = ipmi_picmg_fru_activation_policy_get(intf, argc-1, &(argv[2])); + } else { + lprintf(LOG_NOTICE, "usage: get <fruid>"); + } + } else if (!strncmp(argv[1], "set", 3)) { + if (argc > 4) { + rc = ipmi_picmg_fru_activation_policy_set(intf, argc-1, &(argv[2])); + } else { + lprintf(LOG_NOTICE, "usage: set <fruid> <lockmask> <lock>"); + lprintf(LOG_NOTICE, + " lockmask: [1] affect the deactivation locked bit"); + lprintf(LOG_NOTICE, + " [0] affect the activation locked bit"); + lprintf(LOG_NOTICE, + " lock: [1] set/clear deactivation locked"); + lprintf(LOG_NOTICE, " [0] set/clear locked"); + } + } + else { + lprintf(LOG_ERR, "Specify FRU."); + return -1; + } + } else { + lprintf(LOG_ERR, "Wrong parameters."); + return -1; + } + } + + /* portstate command */ + else if (!strncmp(argv[0], "portstate", 9)) { + + lprintf(LOG_DEBUG,"PICMG: portstate API"); + + if (argc > 1) { + if (!strncmp(argv[1], "get", 3)) { + int32_t iface; + uint8_t channel = 0; + + lprintf(LOG_DEBUG,"PICMG: get"); + + if(!strncmp(argv[1], "getall", 6)) { + for(iface=0;iface<=PICMG_EKEY_MAX_INTERFACE;iface++) { + for(channel=1;channel<=PICMG_EKEY_MAX_CHANNEL;channel++) { + if(!(( iface == FRU_PICMGEXT_DESIGN_IF_FABRIC ) && + ( channel > PICMG_EKEY_MAX_FABRIC_CHANNEL ) )) + { + rc = ipmi_picmg_portstate_get(intf,iface,channel, + PICMG_EKEY_MODE_PRINT_ALL); + } + } + } + } + else if(!strncmp(argv[1], "getgranted", 10)) { + for(iface=0;iface<=PICMG_EKEY_MAX_INTERFACE;iface++) { + for(channel=1;channel<=PICMG_EKEY_MAX_CHANNEL;channel++) { + rc = ipmi_picmg_portstate_get(intf,iface,channel, + PICMG_EKEY_MODE_PRINT_ENABLED); + } + } + } + else if(!strncmp(argv[1], "getdenied", 9)){ + for(iface=0;iface<=PICMG_EKEY_MAX_INTERFACE;iface++) { + for(channel=1;channel<=PICMG_EKEY_MAX_CHANNEL;channel++) { + rc = ipmi_picmg_portstate_get(intf,iface,channel, + PICMG_EKEY_MODE_PRINT_DISABLED); + } + } + } + else if (argc > 3){ + if (is_amc_intf(argv[2], &iface) != 0 + || is_amc_channel(argv[3], &channel) != 0) { + return (-1); + } + lprintf(LOG_DEBUG,"PICMG: requesting interface %d",iface); + lprintf(LOG_DEBUG,"PICMG: requesting channel %d",channel); + + rc = ipmi_picmg_portstate_get(intf,iface,channel, + PICMG_EKEY_MODE_QUERY ); + } + else { + lprintf(LOG_NOTICE, "<intf> <chn>|getall|getgranted|getdenied"); + } + } + else if (!strncmp(argv[1], "set", 3)) { + if (argc == 9) { + int32_t interface = 0; + int32_t port = 0; + uint8_t channel = 0; + uint8_t enable = 0; + uint8_t group = 0; + uint8_t type = 0; + uint8_t typeext = 0; + if (is_amc_intf(argv[2], &interface) != 0 + || is_amc_channel(argv[3], &channel) != 0 + || is_amc_port(argv[4], &port) != 0 + || is_link_type(argv[5], &type) != 0 + || is_link_type_ext(argv[6], &typeext) != 0 + || is_link_group(argv[7], &group) != 0 + || is_enable(argv[8], &enable) != 0) { + return (-1); + } + + lprintf(LOG_DEBUG,"PICMG: interface %d",interface); + lprintf(LOG_DEBUG,"PICMG: channel %d",channel); + lprintf(LOG_DEBUG,"PICMG: port %d",port); + lprintf(LOG_DEBUG,"PICMG: type %d",type); + lprintf(LOG_DEBUG,"PICMG: typeext %d",typeext); + lprintf(LOG_DEBUG,"PICMG: group %d",group); + lprintf(LOG_DEBUG,"PICMG: enable %d",enable); + + rc = ipmi_picmg_portstate_set(intf, interface, + channel, port, type, typeext ,group ,enable); + } + else { + lprintf(LOG_NOTICE, + "<intf> <chn> <port> <type> <ext> <group> <1|0>"); + return -1; + } + } + } + else { + lprintf(LOG_NOTICE, "<set>|<getall>|<getgranted>|<getdenied>"); + return -1; + } + } + /* amc portstate command */ + else if (!strncmp(argv[0], "amcportstate", 12)) { + + lprintf(LOG_DEBUG,"PICMG: amcportstate API"); + + if (argc > 1) { + if (!strncmp(argv[1], "get", 3)){ + int32_t device; + uint8_t channel; + + lprintf(LOG_DEBUG,"PICMG: get"); + + if(!strncmp(argv[1], "getall", 6)){ + int maxDevice = PICMG_EKEY_AMC_MAX_DEVICE; + if( PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + maxDevice = 0; + } + for(device=0;device<=maxDevice;device++){ + for(channel=0;channel<=PICMG_EKEY_AMC_MAX_CHANNEL;channel++){ + rc = ipmi_picmg_amc_portstate_get(intf,device,channel, + PICMG_EKEY_MODE_PRINT_ALL); + } + } + } + else if(!strncmp(argv[1], "getgranted", 10)){ + int maxDevice = PICMG_EKEY_AMC_MAX_DEVICE; + if( PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + maxDevice = 0; + } + for(device=0;device<=maxDevice;device++){ + for(channel=0;channel<=PICMG_EKEY_AMC_MAX_CHANNEL;channel++){ + rc = ipmi_picmg_amc_portstate_get(intf,device,channel, + PICMG_EKEY_MODE_PRINT_ENABLED); + } + } + } + else if(!strncmp(argv[1], "getdenied", 9)){ + int maxDevice = PICMG_EKEY_AMC_MAX_DEVICE; + if( PicmgCardType != PICMG_CARD_TYPE_ATCA ){ + maxDevice = 0; + } + for(device=0;device<=maxDevice;device++){ + for(channel=0;channel<=PICMG_EKEY_AMC_MAX_CHANNEL;channel++){ + rc = ipmi_picmg_amc_portstate_get(intf,device,channel, + PICMG_EKEY_MODE_PRINT_DISABLED); + } + } + } + else if (argc > 2){ + if (is_amc_channel(argv[2], &channel) != 0) { + return (-1); + } + if (argc > 3){ + if (is_amc_dev(argv[3], &device) != 0) { + return (-1); + } + }else{ + device = -1; + } + lprintf(LOG_DEBUG,"PICMG: requesting device %d",device); + lprintf(LOG_DEBUG,"PICMG: requesting channel %d",channel); + + rc = ipmi_picmg_amc_portstate_get(intf,device,channel, + PICMG_EKEY_MODE_QUERY ); + } + else { + lprintf(LOG_NOTICE, "<chn> <device>|getall|getgranted|getdenied"); + } + } + else if (!strncmp(argv[1], "set", 3)) { + if (argc > 7) { + int32_t device = -1; + int32_t port = 0; + uint8_t channel = 0; + uint8_t enable = 0; + uint8_t group = 0; + uint8_t type = 0; + uint8_t typeext = 0; + if (is_amc_channel(argv[2], &channel) != 0 + || is_amc_port(argv[3], &port) != 0 + || is_link_type(argv[4], &type) !=0 + || is_link_type_ext(argv[5], &typeext) != 0 + || is_link_group(argv[6], &group) != 0 + || is_enable(argv[7], &enable) != 0) { + return (-1); + } + if(argc > 8){ + if (is_amc_dev(argv[8], &device) != 0) { + return (-1); + } + } + + lprintf(LOG_DEBUG,"PICMG: channel %d",channel); + lprintf(LOG_DEBUG,"PICMG: portflags %d",port); + lprintf(LOG_DEBUG,"PICMG: type %d",type); + lprintf(LOG_DEBUG,"PICMG: typeext %d",typeext); + lprintf(LOG_DEBUG,"PICMG: group %d",group); + lprintf(LOG_DEBUG,"PICMG: enable %d",enable); + lprintf(LOG_DEBUG,"PICMG: device %d",device); + + rc = ipmi_picmg_amc_portstate_set(intf, channel, port, type, + typeext, group, enable, device); + } + else { + lprintf(LOG_NOTICE, + "<chn> <portflags> <type> <ext> <group> <1|0> [<device>]"); + return -1; + } + } + } + else { + lprintf(LOG_NOTICE, "<set>|<get>|<getall>|<getgranted>|<getdenied>"); + return -1; + } + } + /* ATCA led commands */ + else if (!strncmp(argv[0], "led", 3)) { + if (argc > 1) { + if (!strncmp(argv[1], "prop", 4)) { + if (argc > 2) { + rc = ipmi_picmg_get_led_properties(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, "led prop <FRU-ID>"); + } + } + else if (!strncmp(argv[1], "cap", 3)) { + if (argc > 3) { + rc = ipmi_picmg_get_led_capabilities(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, "led cap <FRU-ID> <LED-ID>"); + } + } + else if (!strncmp(argv[1], "get", 3)) { + if (argc > 3) { + rc = ipmi_picmg_get_led_state(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, "led get <FRU-ID> <LED-ID>"); + } + } + else if (!strncmp(argv[1], "set", 3)) { + if (argc > 6) { + rc = ipmi_picmg_set_led_state(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, + "led set <FRU-ID> <LED-ID> <function> <duration> <color>"); + lprintf(LOG_NOTICE, " <FRU-ID>"); + lprintf(LOG_NOTICE, " <LED-ID> 0: Blue LED"); + lprintf(LOG_NOTICE, " 1: LED 1"); + lprintf(LOG_NOTICE, " 2: LED 2"); + lprintf(LOG_NOTICE, " 3: LED 3"); + lprintf(LOG_NOTICE, " 0x04-0xFE: OEM defined"); + lprintf(LOG_NOTICE, + " 0xFF: All LEDs under management control"); + lprintf(LOG_NOTICE, " <function> 0: LED OFF override"); + lprintf(LOG_NOTICE, + " 1 - 250: LED blinking override (off duration)"); + lprintf(LOG_NOTICE, " 251: LED Lamp Test"); + lprintf(LOG_NOTICE, + " 252: LED restore to local control"); + lprintf(LOG_NOTICE, " 255: LED ON override"); + lprintf(LOG_NOTICE, + " <duration> 1 - 127: LED Lamp Test / on duration"); + lprintf(LOG_NOTICE, " <color> 0: reserved"); + lprintf(LOG_NOTICE, " 1: BLUE"); + lprintf(LOG_NOTICE, " 2: RED"); + lprintf(LOG_NOTICE, " 3: GREEN"); + lprintf(LOG_NOTICE, " 4: AMBER"); + lprintf(LOG_NOTICE, " 5: ORANGE"); + lprintf(LOG_NOTICE, " 6: WHITE"); + lprintf(LOG_NOTICE, " 7: reserved"); + lprintf(LOG_NOTICE, " 0xE: do not change"); + lprintf(LOG_NOTICE, " 0xF: use default color"); + } + } + else { + lprintf(LOG_NOTICE, "prop | cap | get | set"); + } + } + } + /* power commands */ + else if (!strncmp(argv[0], "power", 5)) { + if (argc > 1) { + if (!strncmp(argv[1], "get", 3)) { + if (argc > 3) { + rc = ipmi_picmg_get_power_level(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, "power get <FRU-ID> <type>"); + lprintf(LOG_NOTICE, " <type> 0 : steady state power draw levels"); + lprintf(LOG_NOTICE, + " 1 : desired steady state draw levels"); + lprintf(LOG_NOTICE, " 2 : early power draw levels"); + lprintf(LOG_NOTICE, " 3 : desired early levels"); + + return -1; + } + } + else if (!strncmp(argv[1], "set", 3)) { + if (argc > 4) { + rc = ipmi_picmg_set_power_level(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, "power set <FRU-ID> <level> <present-desired>"); + lprintf(LOG_NOTICE, " <level> 0 : Power Off"); + lprintf(LOG_NOTICE, " 0x1-0x14 : Power level"); + lprintf(LOG_NOTICE, " 0xFF : do not change"); + lprintf(LOG_NOTICE, + "\n <present-desired> 0: do not change present levels"); + lprintf(LOG_NOTICE, + " 1: copy desired to present level"); + + return -1; + } + } + else { + lprintf(LOG_NOTICE, "<set>|<get>"); + return -1; + } + } + else { + lprintf(LOG_NOTICE, "<set>|<get>"); + return -1; + } + }/* clk commands*/ + else if (!strncmp(argv[0], "clk", 3)) { + if (argc > 1) { + if (!strncmp(argv[1], "get", 3)) { + int8_t clk_res = -1; + uint8_t clk_id; + uint8_t max_res = 15; + + if( PicmgCardType == PICMG_CARD_TYPE_AMC ) { + max_res = 0; + } + + if(!strncmp(argv[1], "getall", 6)) { + if( verbose ) { printf("Getting all clock state\n") ;} + for(clk_res=0;clk_res<=max_res;clk_res++) { + for(clk_id=0;clk_id<=15;clk_id++) { + rc = ipmi_picmg_clk_get(intf,clk_id,clk_res, + PICMG_EKEY_MODE_PRINT_ALL); + } + } + } + else if(!strncmp(argv[1], "getdenied", 6)) { + if( verbose ) { printf("Getting disabled clocks\n") ;} + for(clk_res=0;clk_res<=max_res;clk_res++) { + for(clk_id=0;clk_id<=15;clk_id++) { + rc = ipmi_picmg_clk_get(intf,clk_id,clk_res, + PICMG_EKEY_MODE_PRINT_DISABLED); + } + } + } + else if(!strncmp(argv[1], "getgranted", 6)) { + if( verbose ) { printf("Getting enabled clocks\n") ;} + for(clk_res=0;clk_res<=max_res;clk_res++) { + for(clk_id=0;clk_id<=15;clk_id++) { + rc = ipmi_picmg_clk_get(intf,clk_id,clk_res, + PICMG_EKEY_MODE_PRINT_ENABLED); + } + } + } + else if (argc > 2) { + if (is_clk_id(argv[2], &clk_id) != 0) { + return (-1); + } + if (argc > 3) { + if (is_clk_resid(argv[3], &clk_res) != 0) { + return (-1); + } + } + + rc = ipmi_picmg_clk_get(intf, clk_id, clk_res, + PICMG_EKEY_MODE_QUERY ); + } + else { + lprintf(LOG_NOTICE, "clk get"); + lprintf(LOG_NOTICE, + "<CLK-ID> [<DEV-ID>] |getall|getgranted|getdenied"); + return -1; + } + } + else if (!strncmp(argv[1], "set", 3)) { + if (argc > 7) { + rc = ipmi_picmg_clk_set(intf, argc-1, &(argv[2])); + } + else { + lprintf(LOG_NOTICE, + "clk set <CLK-ID> <index> <setting> <family> <acc-lvl> <freq> [<DEV-ID>]"); + + return -1; + } + } + else { + lprintf(LOG_NOTICE, "<set>|<get>|<getall>|<getgranted>|<getdenied>"); + return -1; + } + } + else { + lprintf(LOG_NOTICE, "<set>|<get>|<getall>|<getgranted>|<getdenied>"); + return -1; + } + } + + else if(showProperties == 0 ){ + + ipmi_picmg_help(); + return -1; + } + + return rc; +} + +uint8_t +ipmi_picmg_ipmb_address(struct ipmi_intf *intf) { + struct ipmi_rq req; + struct ipmi_rs *rsp; + char msg_data; + + if (!intf->picmg_avail) { + return 0; + } + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_ADDRESS_INFO_CMD; + msg_data = 0x00; + req.msg.data = &msg_data; + req.msg.data_len = 1; + msg_data = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp && !rsp->ccode) { + return rsp->data[2]; + } + if (rsp) { + lprintf(LOG_DEBUG, "Get Address Info failed: %#x %s", + rsp->ccode, val2str(rsp->ccode, completion_code_vals)); + } else { + lprintf(LOG_DEBUG, "Get Address Info failed: No Response"); + } + return 0; +} + +uint8_t +picmg_discover(struct ipmi_intf *intf) { + /* Check if PICMG extension is available to use the function + * GetDeviceLocator to retreive i2c address PICMG hack to set + * right IPMB address, If extension is not supported, should + * not give any problems + * PICMG Extension Version 2.0 (PICMG 3.0 Revision 1.0 ATCA) to + * PICMG Extension Version 2.3 (PICMG 3.0 Revision 3.0 ATCA) + * PICMG Extension Version 4.1 (PICMG 3.0 Revision 3.0 AMC) + */ + + /* First, check if PICMG extension is available and supported */ + struct ipmi_rq req; + struct ipmi_rs *rsp; + char msg_data; + + if (intf->picmg_avail == 0) { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = PICMG_GET_PICMG_PROPERTIES_CMD; + msg_data = 0x00; + req.msg.data = &msg_data; + req.msg.data_len = 1; + msg_data = 0; + + lprintf(LOG_INFO, "Running Get PICMG Properties my_addr %#x, transit %#x, target %#x", + intf->my_addr, intf->transit_addr, intf->target_addr); + rsp = intf->sendrecv(intf, &req); + if (rsp && !rsp->ccode) { + if ( (rsp->data[0] == 0) && + ((rsp->data[1] & 0x0F) == PICMG_ATCA_MAJOR_VERSION + || (rsp->data[1] & 0x0F) == PICMG_AMC_MAJOR_VERSION) ) { + intf->picmg_avail = 1; + lprintf(LOG_INFO, "Discovered PICMG Extension %d.%d", + (rsp->data[1] & 0x0f), (rsp->data[1] >> 4)); + } + } else { + if (rsp == NULL) { + lprintf(LOG_INFO,"No Response from Get PICMG Properties"); + } else { + lprintf(LOG_INFO,"Error Response %#x from Get PICMG Properities", rsp->ccode); + } + } + } + if (intf->picmg_avail == 0) { + lprintf(LOG_INFO, "No PICMG Extenstion discovered"); + } + return intf->picmg_avail; +} diff --git a/lib/ipmi_raw.c b/lib/ipmi_raw.c new file mode 100644 index 0000000..6959c1f --- /dev/null +++ b/lib/ipmi_raw.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_raw.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_strings.h> + +#define IPMI_I2C_MASTER_MAX_SIZE 0x40 /* 64 bytes */ + +static int is_valid_param(const char *input_param, uint8_t *uchr_ptr, + const char *label); + +/* ipmi_master_write_read - Perform I2C write/read transactions + * + * This function performs an I2C master write-read function through + * IPMI interface. It has a maximum transfer size of 32 bytes. + * + * @intf: ipmi interface + * @bus: channel number, i2c bus id and type + * @addr: i2c slave address + * @wdata: data to write + * @wsize: length of data to write (max 64 bytes) + * @rsize: length of data to read (max 64 bytes) + * + * Returns pointer to IPMI Response + */ +struct ipmi_rs * +ipmi_master_write_read(struct ipmi_intf * intf, uint8_t bus, uint8_t addr, + uint8_t * wdata, uint8_t wsize, uint8_t rsize) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + uint8_t rqdata[IPMI_I2C_MASTER_MAX_SIZE + 3]; + + if (rsize > IPMI_I2C_MASTER_MAX_SIZE) { + lprintf(LOG_ERR, "Master Write-Read: Too many bytes (%d) to read", rsize); + return NULL; + } + if (wsize > IPMI_I2C_MASTER_MAX_SIZE) { + lprintf(LOG_ERR, "Master Write-Read: Too many bytes (%d) to write", wsize); + return NULL; + } + + memset(&req, 0, sizeof(struct ipmi_rq)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x52; /* master write-read */ + req.msg.data = rqdata; + req.msg.data_len = 3; + + memset(rqdata, 0, IPMI_I2C_MASTER_MAX_SIZE + 3); + rqdata[0] = bus; /* channel number, bus id, bus type */ + rqdata[1] = addr; /* slave address */ + rqdata[2] = rsize; /* number of bytes to read */ + + if (wsize > 0) { + /* copy in data to write */ + memcpy(rqdata+3, wdata, wsize); + req.msg.data_len += wsize; + lprintf(LOG_DEBUG, "Writing %d bytes to i2cdev %02Xh", wsize, addr); + } + + if (rsize > 0) { + lprintf(LOG_DEBUG, "Reading %d bytes from i2cdev %02Xh", rsize, addr); + } + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "I2C Master Write-Read command failed"); + return NULL; + } + else if (rsp->ccode > 0) { + switch (rsp->ccode) { + case 0x81: + lprintf(LOG_ERR, "I2C Master Write-Read command failed: Lost Arbitration"); + break; + case 0x82: + lprintf(LOG_ERR, "I2C Master Write-Read command failed: Bus Error"); + break; + case 0x83: + lprintf(LOG_ERR, "I2C Master Write-Read command failed: NAK on Write"); + break; + case 0x84: + lprintf(LOG_ERR, "I2C Master Write-Read command failed: Truncated Read"); + break; + default: + lprintf(LOG_ERR, "I2C Master Write-Read command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + break; + } + return NULL; + } + + return rsp; +} + +#define RAW_SPD_SIZE 256 + +int +ipmi_rawspd_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs *rsp; + uint8_t msize = IPMI_I2C_MASTER_MAX_SIZE; /* allow to override default */ + uint8_t channel = 0; + uint8_t i2cbus = 0; + uint8_t i2caddr = 0; + uint8_t spd_data[RAW_SPD_SIZE]; + int i; + + memset(spd_data, 0, RAW_SPD_SIZE); + + if (argc < 2 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_NOTICE, "usage: spd <i2cbus> <i2caddr> [channel] [maxread]"); + return 0; + } + + if (is_valid_param(argv[0], &i2cbus, "i2cbus") != 0) + return (-1); + + if (is_valid_param(argv[1], &i2caddr, "i2caddr") != 0) + return (-1); + + if (argc >= 3) { + if (is_valid_param(argv[2], &channel, "channel") != 0) + return (-1); + } + + if (argc >= 4) { + if (is_valid_param(argv[3], &msize, "maxread") != 0) + return (-1); + } + + i2cbus = ((channel & 0xF) << 4) | ((i2cbus & 7) << 1) | 1; + + for (i = 0; i < RAW_SPD_SIZE; i+= msize) { + rsp = ipmi_master_write_read(intf, i2cbus, i2caddr, + (uint8_t *)&i, 1, msize ); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read"); + return -1; + } + + memcpy(spd_data+i, rsp->data, msize); + } + + ipmi_spd_print(spd_data, i); + return 0; +} + +static void rawi2c_usage(void) +{ + lprintf(LOG_NOTICE, "usage: i2c [bus=public|# [chan=#] <i2caddr> <read bytes> [write data]"); + lprintf(LOG_NOTICE, " bus=public is default"); + lprintf(LOG_NOTICE, " chan=0 is default, bus= must be specified to use chan="); +} + +int +ipmi_rawi2c_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + uint8_t wdata[IPMI_I2C_MASTER_MAX_SIZE]; + uint8_t i2caddr = 0; + uint8_t rsize = 0; + uint8_t wsize = 0; + unsigned int rbus = 0; + uint8_t bus = 0; + int i = 0; + + /* handle bus= argument */ + if (argc > 2 && strncmp(argv[0], "bus=", 4) == 0) { + i = 1; + if (strncmp(argv[0], "bus=public", 10) == 0) + bus = 0; + else if (sscanf(argv[0], "bus=%u", &rbus) == 1) + bus = ((rbus & 7) << 1) | 1; + else + bus = 0; + + /* handle channel= argument + * the bus= argument must be supplied first on command line */ + if (argc > 3 && strncmp(argv[1], "chan=", 5) == 0) { + i = 2; + if (sscanf(argv[1], "chan=%u", &rbus) == 1) + bus |= rbus << 4; + } + } + + if ((argc-i) < 2 || strncmp(argv[0], "help", 4) == 0) { + rawi2c_usage(); + return 0; + } + else if (argc-i-2 > IPMI_I2C_MASTER_MAX_SIZE) { + lprintf(LOG_ERR, "Raw command input limit (%d bytes) exceeded", + IPMI_I2C_MASTER_MAX_SIZE); + return -1; + } + + if (is_valid_param(argv[i++], &i2caddr, "i2caddr") != 0) + return (-1); + + if (is_valid_param(argv[i++], &rsize, "read size") != 0) + return (-1); + + if (i2caddr == 0) { + lprintf(LOG_ERR, "Invalid I2C address 0"); + rawi2c_usage(); + return -1; + } + + memset(wdata, 0, IPMI_I2C_MASTER_MAX_SIZE); + for (; i < argc; i++) { + uint8_t val = 0; + + if (is_valid_param(argv[i], &val, "parameter") != 0) + return (-1); + + wdata[wsize] = val; + wsize++; + } + + lprintf(LOG_INFO, "RAW I2C REQ (i2caddr=%x readbytes=%d writebytes=%d)", + i2caddr, rsize, wsize); + printbuf(wdata, wsize, "WRITE DATA"); + + rsp = ipmi_master_write_read(intf, bus, i2caddr, wdata, wsize, rsize); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read"); + return -1; + } + + if (wsize > 0) { + if (verbose || rsize == 0) + printf("Wrote %d bytes to I2C device %02Xh\n", wsize, i2caddr); + } + + if (rsize > 0) { + if (verbose || wsize == 0) + printf("Read %d bytes from I2C device %02Xh\n", rsp->data_len, i2caddr); + + if (rsp->data_len < rsize) + return -1; + + /* print the raw response buffer */ + for (i=0; i<rsp->data_len; i++) { + if (((i%16) == 0) && (i != 0)) + printf("\n"); + printf(" %2.2x", rsp->data[i]); + } + printf("\n"); + + if (rsp->data_len <= 4) { + uint32_t bit; + int j; + for (i = 0; i < rsp->data_len; i++) { + for (j = 1, bit = 0x80; bit > 0; bit /= 2, j++) { + printf("%s", (rsp->data[i] & bit) ? "1" : "0"); + } + printf(" "); + } + printf("\n"); + } + } + + return 0; +} + +/* ipmi_raw_help() - print 'raw' help text + * + * returns void + */ +void +ipmi_raw_help() +{ + lprintf(LOG_NOTICE, "RAW Commands: raw <netfn> <cmd> [data]"); + print_valstr(ipmi_netfn_vals, "Network Function Codes", LOG_NOTICE); + lprintf(LOG_NOTICE, "(can also use raw hex values)"); +} /* ipmi_raw_help() */ + +int +ipmi_raw_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t netfn, cmd, lun; + uint16_t netfn_tmp = 0; + int i; + uint8_t data[256]; + + if (argc == 1 && strncmp(argv[0], "help", 4) == 0) { + ipmi_raw_help(); + return 0; + } + else if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + ipmi_raw_help(); + return (-1); + } + else if (argc > sizeof(data)) + { + lprintf(LOG_NOTICE, "Raw command input limit (256 bytes) exceeded"); + return -1; + } + + ipmi_intf_session_set_timeout(intf, 15); + ipmi_intf_session_set_retry(intf, 1); + + lun = intf->target_lun; + netfn_tmp = str2val(argv[0], ipmi_netfn_vals); + if (netfn_tmp == 0xff) { + if (is_valid_param(argv[0], &netfn, "netfn") != 0) + return (-1); + } else { + if (netfn_tmp >= UINT8_MAX) { + lprintf(LOG_ERR, "Given netfn \"%s\" is out of range.", argv[0]); + return (-1); + } + netfn = netfn_tmp; + } + + if (is_valid_param(argv[1], &cmd, "command") != 0) + return (-1); + + memset(data, 0, sizeof(data)); + memset(&req, 0, sizeof(req)); + req.msg.netfn = netfn; + req.msg.lun = lun; + req.msg.cmd = cmd; + req.msg.data = data; + + for (i=2; i<argc; i++) { + uint8_t val = 0; + + if (is_valid_param(argv[i], &val, "data") != 0) + return (-1); + + req.msg.data[i-2] = val; + req.msg.data_len++; + } + + lprintf(LOG_INFO, + "RAW REQ (channel=0x%x netfn=0x%x lun=0x%x cmd=0x%x data_len=%d)", + intf->target_channel & 0x0f, req.msg.netfn,req.msg.lun , + req.msg.cmd, req.msg.data_len); + + printbuf(req.msg.data, req.msg.data_len, "RAW REQUEST"); + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to send RAW command " + "(channel=0x%x netfn=0x%x lun=0x%x cmd=0x%x)", + intf->target_channel & 0x0f, req.msg.netfn, req.msg.lun, req.msg.cmd); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to send RAW command " + "(channel=0x%x netfn=0x%x lun=0x%x cmd=0x%x rsp=0x%x): %s", + intf->target_channel & 0x0f, req.msg.netfn, req.msg.lun, req.msg.cmd, rsp->ccode, + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + lprintf(LOG_INFO, "RAW RSP (%d bytes)", rsp->data_len); + + /* print the raw response buffer */ + for (i=0; i<rsp->data_len; i++) { + if (((i%16) == 0) && (i != 0)) + printf("\n"); + printf(" %2.2x", rsp->data[i]); + } + printf("\n"); + + return 0; +} + +/* is_valid_param - + * + * @input_param: string to convert from + * @uchr_ptr: pointer where to store converted value + * @label: string used in error message + * + * returns 0 if parameter is valid + * returns (-1) if parameter is invalid/on error + */ +int +is_valid_param(const char *input_param, uint8_t *uchr_ptr, const char *label) { + if (input_param == NULL || label == NULL) { + lprintf(LOG_ERROR, "ERROR: NULL pointer passed."); + return (-1); + } + if (str2uchar(input_param, uchr_ptr) == 0) + return 0; + + lprintf(LOG_ERR, "Given %s \"%s\" is invalid.", label, input_param); + return (-1); +} diff --git a/lib/ipmi_sdr.c b/lib/ipmi_sdr.c new file mode 100644 index 0000000..fa7b082 --- /dev/null +++ b/lib/ipmi_sdr.c @@ -0,0 +1,4835 @@ +/* + * Copyright (c) 2012 Hewlett-Packard Development Company, L.P. + * + * Based on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> + +#include <math.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <time.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_sdradd.h> +#include <ipmitool/ipmi_sensor.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_entity.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_strings.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +extern int verbose; +static int use_built_in; /* Uses DeviceSDRs instead of SDRR */ +static int sdr_max_read_len = 0; +static int sdr_extended = 0; +static long sdriana = 0; + +static struct sdr_record_list *sdr_list_head = NULL; +static struct sdr_record_list *sdr_list_tail = NULL; +static struct ipmi_sdr_iterator *sdr_list_itr = NULL; + +void printf_sdr_usage(); + +/* ipmi_sdr_get_unit_string - return units for base/modifier + * + * @pct: units are a percentage + * @type: unit type + * @base: base + * @modifier: modifier + * + * returns pointer to static string + */ +const char * +ipmi_sdr_get_unit_string(uint8_t pct, uint8_t type, uint8_t base, uint8_t modifier) +{ + static char unitstr[16]; + /* + * By default, if units are supposed to be percent, we will pre-pend + * the percent string to the textual representation of the units. + */ + char *pctstr = pct ? "% " : ""; + memset(unitstr, 0, sizeof (unitstr)); + switch (type) { + case 2: + snprintf(unitstr, sizeof (unitstr), "%s%s * %s", + pctstr, unit_desc[base], unit_desc[modifier]); + break; + case 1: + snprintf(unitstr, sizeof (unitstr), "%s%s/%s", + pctstr, unit_desc[base], unit_desc[modifier]); + break; + case 0: + default: + /* + * Display the text "percent" only when the Base unit is + * "unspecified" and the caller specified to print percent. + */ + if (base == 0 && pct) { + snprintf(unitstr, sizeof(unitstr), "percent"); + } else { + snprintf(unitstr, sizeof (unitstr), "%s%s", + pctstr, unit_desc[base]); + } + break; + } + return unitstr; +} + +/* sdr_sensor_has_analog_reading - Determine if sensor has an analog reading + * + */ +static int +sdr_sensor_has_analog_reading(struct ipmi_intf *intf, + struct sensor_reading *sr) +{ + /* Compact sensors can't return analog values so we false */ + if (!sr->full) { + return 0; + } + /* + * Per the IPMI Specification: + * Only Full Threshold sensors are identified as providing + * analog readings. + * + * But... HP didn't interpret this as meaning that "Only Threshold + * Sensors" can provide analog readings. So, HP packed analog + * readings into some of their non-Threshold Sensor. There is + * nothing that explictly prohibits this in the spec, so if + * an Analog reading is available in a Non-Threshod sensor and + * there are units specified for identifying the reading then + * we do an analog conversion even though the sensor is + * non-Threshold. To be safe, we provide this extension for + * HP. + * + */ + if ( UNITS_ARE_DISCRETE(&sr->full->cmn) ) { + return 0;/* Sensor specified as not having Analog Units */ + } + if ( !IS_THRESHOLD_SENSOR(&sr->full->cmn) ) { + /* Non-Threshold Sensors are not defined as having analog */ + /* But.. We have one with defined with Analog Units */ + if ( (sr->full->cmn.unit.pct | sr->full->cmn.unit.modifier | + sr->full->cmn.unit.type.base | + sr->full->cmn.unit.type.modifier)) { + /* And it does have the necessary units specs */ + if ( !(intf->manufacturer_id == IPMI_OEM_HP) ) { + /* But to be safe we only do this for HP */ + return 0; + } + } else { + return 0; + } + } + /* + * If sensor has linearization, then we should be able to update the + * reading factors and if we cannot fail the conversion. + */ + if (sr->full->linearization >= SDR_SENSOR_L_NONLINEAR && + sr->full->linearization <= 0x7F) { + if (ipmi_sensor_get_sensor_reading_factors(intf, sr->full, sr->s_reading) < 0){ + sr->s_reading_valid = 0; + return 0; + } + } + + return 1; +} + +/* sdr_convert_sensor_reading - convert raw sensor reading + * + * @sensor: sensor record + * @val: raw sensor reading + * + * returns floating-point sensor reading + */ +double +sdr_convert_sensor_reading(struct sdr_record_full_sensor *sensor, uint8_t val) +{ + int m, b, k1, k2; + double result; + + m = __TO_M(sensor->mtol); + b = __TO_B(sensor->bacc); + k1 = __TO_B_EXP(sensor->bacc); + k2 = __TO_R_EXP(sensor->bacc); + + switch (sensor->cmn.unit.analog) { + case 0: + result = (double) (((m * val) + + (b * pow(10, k1))) * pow(10, k2)); + break; + case 1: + if (val & 0x80) + val++; + /* Deliberately fall through to case 2. */ + case 2: + result = (double) (((m * (int8_t) val) + + (b * pow(10, k1))) * pow(10, k2)); + break; + default: + /* Oops! This isn't an analog sensor. */ + return 0.0; + } + + switch (sensor->linearization & 0x7f) { + case SDR_SENSOR_L_LN: + result = log(result); + break; + case SDR_SENSOR_L_LOG10: + result = log10(result); + break; + case SDR_SENSOR_L_LOG2: + result = (double) (log(result) / log(2.0)); + break; + case SDR_SENSOR_L_E: + result = exp(result); + break; + case SDR_SENSOR_L_EXP10: + result = pow(10.0, result); + break; + case SDR_SENSOR_L_EXP2: + result = pow(2.0, result); + break; + case SDR_SENSOR_L_1_X: + result = pow(result, -1.0); /*1/x w/o exception */ + break; + case SDR_SENSOR_L_SQR: + result = pow(result, 2.0); + break; + case SDR_SENSOR_L_CUBE: + result = pow(result, 3.0); + break; + case SDR_SENSOR_L_SQRT: + result = sqrt(result); + break; + case SDR_SENSOR_L_CUBERT: + result = cbrt(result); + break; + case SDR_SENSOR_L_LINEAR: + default: + break; + } + return result; +} +/* sdr_convert_sensor_hysterisis - convert raw sensor hysterisis + * + * Even though spec says histerisis should be computed using Mx+B + * formula, B is irrelevant when doing raw comparison + * + * threshold rearm point is computed using threshold +/- hysterisis + * with the full formula however B can't be applied in raw comparisons + * + * @sensor: sensor record + * @val: raw sensor reading + * + * returns floating-point sensor reading + */ +double +sdr_convert_sensor_hysterisis(struct sdr_record_full_sensor *sensor, uint8_t val) +{ + int m, k2; + double result; + + m = __TO_M(sensor->mtol); + + k2 = __TO_R_EXP(sensor->bacc); + + switch (sensor->cmn.unit.analog) { + case 0: + result = (double) (((m * val)) * pow(10, k2)); + break; + case 1: + if (val & 0x80) + val++; + /* Deliberately fall through to case 2. */ + case 2: + result = (double) (((m * (int8_t) val) ) * pow(10, k2)); + break; + default: + /* Oops! This isn't an analog sensor. */ + return 0.0; + } + + switch (sensor->linearization & 0x7f) { + case SDR_SENSOR_L_LN: + result = log(result); + break; + case SDR_SENSOR_L_LOG10: + result = log10(result); + break; + case SDR_SENSOR_L_LOG2: + result = (double) (log(result) / log(2.0)); + break; + case SDR_SENSOR_L_E: + result = exp(result); + break; + case SDR_SENSOR_L_EXP10: + result = pow(10.0, result); + break; + case SDR_SENSOR_L_EXP2: + result = pow(2.0, result); + break; + case SDR_SENSOR_L_1_X: + result = pow(result, -1.0); /*1/x w/o exception */ + break; + case SDR_SENSOR_L_SQR: + result = pow(result, 2.0); + break; + case SDR_SENSOR_L_CUBE: + result = pow(result, 3.0); + break; + case SDR_SENSOR_L_SQRT: + result = sqrt(result); + break; + case SDR_SENSOR_L_CUBERT: + result = cbrt(result); + break; + case SDR_SENSOR_L_LINEAR: + default: + break; + } + return result; +} + + +/* sdr_convert_sensor_tolerance - convert raw sensor reading + * + * @sensor: sensor record + * @val: raw sensor reading + * + * returns floating-point sensor tolerance(interpreted) + */ +double +sdr_convert_sensor_tolerance(struct sdr_record_full_sensor *sensor, uint8_t val) +{ + int m, k2; + double result; + + m = __TO_M(sensor->mtol); + k2 = __TO_R_EXP(sensor->bacc); + + switch (sensor->cmn.unit.analog) { + case 0: + /* as suggested in section 30.4.1 of IPMI 1.5 spec */ + result = (double) ((((m * (double)val/2)) ) * pow(10, k2)); + break; + case 1: + if (val & 0x80) + val++; + /* Deliberately fall through to case 2. */ + case 2: + result = (double) (((m * ((double)((int8_t) val)/2))) * pow(10, k2)); + break; + default: + /* Oops! This isn't an analog sensor. */ + return 0.0; + } + + switch (sensor->linearization & 0x7f) { + case SDR_SENSOR_L_LN: + result = log(result); + break; + case SDR_SENSOR_L_LOG10: + result = log10(result); + break; + case SDR_SENSOR_L_LOG2: + result = (double) (log(result) / log(2.0)); + break; + case SDR_SENSOR_L_E: + result = exp(result); + break; + case SDR_SENSOR_L_EXP10: + result = pow(10.0, result); + break; + case SDR_SENSOR_L_EXP2: + result = pow(2.0, result); + break; + case SDR_SENSOR_L_1_X: + result = pow(result, -1.0); /*1/x w/o exception */ + break; + case SDR_SENSOR_L_SQR: + result = pow(result, 2.0); + break; + case SDR_SENSOR_L_CUBE: + result = pow(result, 3.0); + break; + case SDR_SENSOR_L_SQRT: + result = sqrt(result); + break; + case SDR_SENSOR_L_CUBERT: + result = cbrt(result); + break; + case SDR_SENSOR_L_LINEAR: + default: + break; + } + return result; +} + +/* sdr_convert_sensor_value_to_raw - convert sensor reading back to raw + * + * @sensor: sensor record + * @val: converted sensor reading + * + * returns raw sensor reading + */ +uint8_t +sdr_convert_sensor_value_to_raw(struct sdr_record_full_sensor * sensor, + double val) +{ + int m, b, k1, k2; + double result; + + /* only works for analog sensors */ + if (UNITS_ARE_DISCRETE((&sensor->cmn))) + return 0; + + m = __TO_M(sensor->mtol); + b = __TO_B(sensor->bacc); + k1 = __TO_B_EXP(sensor->bacc); + k2 = __TO_R_EXP(sensor->bacc); + + /* don't divide by zero */ + if (m == 0) + return 0; + + result = (((val / pow(10, k2)) - (b * pow(10, k1))) / m); + + if ((result - (int) result) >= .5) + return (uint8_t) ceil(result); + else + return (uint8_t) result; +} + +/* ipmi_sdr_get_sensor_thresholds - return thresholds for sensor + * + * @intf: ipmi interface + * @sensor: sensor number + * @target: sensor owner ID + * @lun: sensor lun + * @channel: channel number + * + * returns pointer to ipmi response + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_thresholds(struct ipmi_intf *intf, uint8_t sensor, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + if ( BRIDGE_TO_SENSOR(intf, target, channel) ) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = GET_SENSOR_THRESHOLDS; + req.msg.data = &sensor; + req.msg.data_len = sizeof (sensor); + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +/* ipmi_sdr_get_sensor_hysteresis - return hysteresis for sensor + * + * @intf: ipmi interface + * @sensor: sensor number + * @target: sensor owner ID + * @lun: sensor lun + * @channel: channel number + * + * returns pointer to ipmi response + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_hysteresis(struct ipmi_intf *intf, uint8_t sensor, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + uint8_t rqdata[2]; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + if ( BRIDGE_TO_SENSOR(intf, target, channel) ) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + + rqdata[0] = sensor; + rqdata[1] = 0xff; /* reserved */ + + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = GET_SENSOR_HYSTERESIS; + req.msg.data = rqdata; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +/* ipmi_sdr_get_sensor_reading - retrieve a raw sensor reading + * + * @intf: ipmi interface + * @sensor: sensor id + * + * returns ipmi response structure + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_reading(struct ipmi_intf *intf, uint8_t sensor) +{ + struct ipmi_rq req; + + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = GET_SENSOR_READING; + req.msg.data = &sensor; + req.msg.data_len = 1; + + return intf->sendrecv(intf, &req); +} + + +/* ipmi_sdr_get_sensor_reading_ipmb - retrieve a raw sensor reading from ipmb + * + * @intf: ipmi interface + * @sensor: sensor id + * @target: IPMB target address + * @lun: sensor lun + * @channel: channel number + * + * returns ipmi response structure + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_reading_ipmb(struct ipmi_intf *intf, uint8_t sensor, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + if ( BRIDGE_TO_SENSOR(intf, target, channel) ) { + lprintf(LOG_DEBUG, + "Bridge to Sensor " + "Intf my/%#x tgt/%#x:%#x Sdr tgt/%#x:%#x\n", + intf->my_addr, intf->target_addr, intf->target_channel, + target, channel); + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = GET_SENSOR_READING; + req.msg.data = &sensor; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +/* ipmi_sdr_get_sensor_event_status - retrieve sensor event status + * + * @intf: ipmi interface + * @sensor: sensor id + * @target: sensor owner ID + * @lun: sensor lun + * @channel: channel number + * + * returns ipmi response structure + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_event_status(struct ipmi_intf *intf, uint8_t sensor, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + if ( BRIDGE_TO_SENSOR(intf, target, channel) ) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = GET_SENSOR_EVENT_STATUS; + req.msg.data = &sensor; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +/* ipmi_sdr_get_sensor_event_enable - retrieve sensor event enables + * + * @intf: ipmi interface + * @sensor: sensor id + * @target: sensor owner ID + * @lun: sensor lun + * @channel: channel number + * + * returns ipmi response structure + */ +struct ipmi_rs * +ipmi_sdr_get_sensor_event_enable(struct ipmi_intf *intf, uint8_t sensor, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + if ( BRIDGE_TO_SENSOR(intf, target, channel) ) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = GET_SENSOR_EVENT_ENABLE; + req.msg.data = &sensor; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +/* ipmi_sdr_get_sensor_type_desc - Get sensor type descriptor + * + * @type: ipmi sensor type + * + * returns + * string from sensor_type_desc + * or "reserved" + * or "OEM reserved" + */ +const char * +ipmi_sdr_get_sensor_type_desc(const uint8_t type) +{ + static char desc[32]; + memset(desc, 0, 32); + if (type <= SENSOR_TYPE_MAX) + return sensor_type_desc[type]; + if (type < 0xc0) + snprintf(desc, 32, "reserved #%02x", type); + else + { + snprintf(desc, 32, oemval2str(sdriana,type,ipmi_oem_sdr_type_vals), + type); + } + return desc; +} + +/* ipmi_sdr_get_thresh_status - threshold status indicator + * + * @rsp: response from Get Sensor Reading comand + * @validread: validity of the status field argument + * @invalidstr: string to return if status field is not valid + * + * returns + * cr = critical + * nc = non-critical + * nr = non-recoverable + * ok = ok + * ns = not specified + */ +const char * +ipmi_sdr_get_thresh_status(struct sensor_reading *sr, const char *invalidstr) +{ + uint8_t stat; + if (!sr->s_reading_valid) { + return invalidstr; + } + stat = sr->s_data2; + if (stat & SDR_SENSOR_STAT_LO_NR) { + if (verbose) + return "Lower Non-Recoverable"; + else if (sdr_extended) + return "lnr"; + else + return "nr"; + } else if (stat & SDR_SENSOR_STAT_HI_NR) { + if (verbose) + return "Upper Non-Recoverable"; + else if (sdr_extended) + return "unr"; + else + return "nr"; + } else if (stat & SDR_SENSOR_STAT_LO_CR) { + if (verbose) + return "Lower Critical"; + else if (sdr_extended) + return "lcr"; + else + return "cr"; + } else if (stat & SDR_SENSOR_STAT_HI_CR) { + if (verbose) + return "Upper Critical"; + else if (sdr_extended) + return "ucr"; + else + return "cr"; + } else if (stat & SDR_SENSOR_STAT_LO_NC) { + if (verbose) + return "Lower Non-Critical"; + else if (sdr_extended) + return "lnc"; + else + return "nc"; + } else if (stat & SDR_SENSOR_STAT_HI_NC) { + if (verbose) + return "Upper Non-Critical"; + else if (sdr_extended) + return "unc"; + else + return "nc"; + } + return "ok"; +} + +/* ipmi_sdr_get_header - retreive SDR record header + * + * @intf: ipmi interface + * @itr: sdr iterator + * + * returns pointer to static sensor retrieval struct + * returns NULL on error + */ +static struct sdr_get_rs * +ipmi_sdr_get_header(struct ipmi_intf *intf, struct ipmi_sdr_iterator *itr) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + struct sdr_get_rq sdr_rq; + static struct sdr_get_rs sdr_rs; + int try = 0; + + memset(&sdr_rq, 0, sizeof (sdr_rq)); + sdr_rq.reserve_id = itr->reservation; + sdr_rq.id = itr->next; + sdr_rq.offset = 0; + sdr_rq.length = 5; /* only get the header */ + + memset(&req, 0, sizeof (req)); + if (itr->use_built_in == 0) { + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_SDR; + } else { + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = GET_DEVICE_SDR; + } + req.msg.data = (uint8_t *) & sdr_rq; + req.msg.data_len = sizeof (sdr_rq); + + for (try = 0; try < 5; try++) { + sdr_rq.reserve_id = itr->reservation; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SDR %04x command failed", + itr->next); + continue; + } else if (rsp->ccode == 0xc5) { + /* lost reservation */ + lprintf(LOG_DEBUG, "SDR reservation %04x cancelled. " + "Sleeping a bit and retrying...", + itr->reservation); + + sleep(rand() & 3); + + if (ipmi_sdr_get_reservation(intf, itr->use_built_in, + &(itr->reservation)) < 0) { + lprintf(LOG_ERR, + "Unable to renew SDR reservation"); + return NULL; + } + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SDR %04x command failed: %s", + itr->next, val2str(rsp->ccode, + completion_code_vals)); + continue; + } else { + break; + } + } + + if (try == 5) + return NULL; + + if (!rsp) + return NULL; + + lprintf(LOG_DEBUG, "SDR record ID : 0x%04x", itr->next); + + memcpy(&sdr_rs, rsp->data, sizeof (sdr_rs)); + + if (sdr_rs.length == 0) { + lprintf(LOG_ERR, "SDR record id 0x%04x: invalid length %d", + itr->next, sdr_rs.length); + return NULL; + } + + /* achu (chu11 at llnl dot gov): - Some boards are stupid and + * return a record id from the Get SDR Record command + * different than the record id passed in. If we find this + * situation, we cheat and put the original record id back in. + * Otherwise, a later Get SDR Record command will fail with + * completion code CBh = "Requested Sensor, data, or record + * not present" + */ + if (sdr_rs.id != itr->next) { + lprintf(LOG_DEBUG, "SDR record id mismatch: 0x%04x", sdr_rs.id); + sdr_rs.id = itr->next; + } + + lprintf(LOG_DEBUG, "SDR record type : 0x%02x", sdr_rs.type); + lprintf(LOG_DEBUG, "SDR record next : 0x%04x", sdr_rs.next); + lprintf(LOG_DEBUG, "SDR record bytes: %d", sdr_rs.length); + + return &sdr_rs; +} + +/* ipmi_sdr_get_next_header - retreive next SDR header + * + * @intf: ipmi interface + * @itr: sdr iterator + * + * returns pointer to sensor retrieval struct + * returns NULL on error + */ +struct sdr_get_rs * +ipmi_sdr_get_next_header(struct ipmi_intf *intf, struct ipmi_sdr_iterator *itr) +{ + struct sdr_get_rs *header; + + if (itr->next == 0xffff) + return NULL; + + header = ipmi_sdr_get_header(intf, itr); + if (header == NULL) + return NULL; + + itr->next = header->next; + + return header; +} + +/* + * This macro is used to print nominal, normal and threshold settings, + * but it is not compatible with PRINT_NORMAL/PRINT_THRESH since it does + * not have the sensor.init.thresholds setting qualifier as is done in + * PRINT_THRESH. This means CSV output can be different than non CSV + * output if sensor.init.thresholds is ever zero + */ +/* helper macro for printing CSV output for Full SDR Threshold reading */ +#define SENSOR_PRINT_CSV(FULLSENS, FLAG, READ) \ + if ((FLAG)) { \ + if (UNITS_ARE_DISCRETE((&FULLSENS->cmn))) \ + printf("0x%02X,", READ); \ + else \ + printf("%.3f,", sdr_convert_sensor_reading( \ + (FULLSENS), READ)); \ + } else { \ + printf(","); \ + } + +/* helper macro for printing analog values for Full SDR Threshold readings */ +#define SENSOR_PRINT_NORMAL(FULLSENS, NAME, READ) \ + if ((FULLSENS)->analog_flag.READ != 0) { \ + printf(" %-21s : ", NAME); \ + if (UNITS_ARE_DISCRETE((&FULLSENS->cmn))) \ + printf("0x%02X\n", \ + (FULLSENS)->READ); \ + else \ + printf("%.3f\n", sdr_convert_sensor_reading( \ + (FULLSENS), (FULLSENS)->READ));\ + } + +/* helper macro for printing Full SDR sensor Thresholds */ +#define SENSOR_PRINT_THRESH(FULLSENS, NAME, READ, FLAG) \ + if ((FULLSENS)->cmn.sensor.init.thresholds && \ + (FULLSENS)->cmn.mask.type.threshold.read.FLAG != 0) { \ + printf(" %-21s : ", NAME); \ + if (UNITS_ARE_DISCRETE((&FULLSENS->cmn))) \ + printf("0x%02X\n", \ + (FULLSENS)->threshold.READ); \ + else \ + printf("%.3f\n", sdr_convert_sensor_reading( \ + (FULLSENS), (FULLSENS)->threshold.READ)); \ + } + +int +ipmi_sdr_print_sensor_event_status(struct ipmi_intf *intf, + uint8_t sensor_num, + uint8_t sensor_type, + uint8_t event_type, int numeric_fmt, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rs *rsp; + int i; + const struct valstr assert_cond_1[] = { + {0x80, "unc+"}, + {0x40, "unc-"}, + {0x20, "lnr+"}, + {0x10, "lnr-"}, + {0x08, "lcr+"}, + {0x04, "lcr-"}, + {0x02, "lnc+"}, + {0x01, "lnc-"}, + {0x00, NULL}, + }; + const struct valstr assert_cond_2[] = { + {0x08, "unr+"}, + {0x04, "unr-"}, + {0x02, "ucr+"}, + {0x01, "ucr-"}, + {0x00, NULL}, + }; + + rsp = ipmi_sdr_get_sensor_event_status(intf, sensor_num, + target, lun, channel); + + if (rsp == NULL) { + lprintf(LOG_DEBUG, + "Error reading event status for sensor #%02x", + sensor_num); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_DEBUG, + "Error reading event status for sensor #%02x: %s", + sensor_num, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + /* There is an assumption here that data_len >= 1 */ + if (IS_READING_UNAVAILABLE(rsp->data[0])) { + printf(" Event Status : Unavailable\n"); + return 0; + } + if (IS_SCANNING_DISABLED(rsp->data[0])) { + //printf(" Event Status : Scanning Disabled\n"); + //return 0; + } + if (IS_EVENT_MSG_DISABLED(rsp->data[0])) { + printf(" Event Status : Event Messages Disabled\n"); + //return 0; + } + + switch (numeric_fmt) { + case DISCRETE_SENSOR: + if (rsp->data_len == 2) { + ipmi_sdr_print_discrete_state("Assertion Events", + sensor_type, event_type, + rsp->data[1], 0); + } else if (rsp->data_len > 2) { + ipmi_sdr_print_discrete_state("Assertion Events", + sensor_type, event_type, + rsp->data[1], + rsp->data[2]); + } + if (rsp->data_len == 4) { + ipmi_sdr_print_discrete_state("Deassertion Events", + sensor_type, event_type, + rsp->data[3], 0); + } else if (rsp->data_len > 4) { + ipmi_sdr_print_discrete_state("Deassertion Events", + sensor_type, event_type, + rsp->data[3], + rsp->data[4]); + } + break; + + case ANALOG_SENSOR: + printf(" Assertion Events : "); + for (i = 0; i < 8; i++) { + if (rsp->data[1] & (1 << i)) + printf("%s ", val2str(1 << i, assert_cond_1)); + } + if (rsp->data_len > 2) { + for (i = 0; i < 4; i++) { + if (rsp->data[2] & (1 << i)) + printf("%s ", + val2str(1 << i, assert_cond_2)); + } + printf("\n"); + if ((rsp->data_len == 4 && rsp->data[3] != 0) || + (rsp->data_len > 4 + && (rsp->data[3] != 0 && rsp->data[4] != 0))) { + printf(" Deassertion Events : "); + for (i = 0; i < 8; i++) { + if (rsp->data[3] & (1 << i)) + printf("%s ", + val2str(1 << i, + assert_cond_1)); + } + if (rsp->data_len > 4) { + for (i = 0; i < 4; i++) { + if (rsp->data[4] & (1 << i)) + printf("%s ", + val2str(1 << i, + assert_cond_2)); + } + } + printf("\n"); + } + } else { + printf("\n"); + } + break; + + default: + break; + } + + return 0; +} + +static int +ipmi_sdr_print_sensor_mask(struct sdr_record_mask *mask, + uint8_t sensor_type, + uint8_t event_type, int numeric_fmt) +{ + /* iceblink - don't print some event status fields - CVS rev1.53 */ + return 0; + + switch (numeric_fmt) { + case DISCRETE_SENSOR: + ipmi_sdr_print_discrete_state("Assert Event Mask", sensor_type, + event_type, + mask->type.discrete. + assert_event & 0xff, + (mask->type.discrete. + assert_event & 0xff00) >> 8); + ipmi_sdr_print_discrete_state("Deassert Event Mask", + sensor_type, event_type, + mask->type.discrete. + deassert_event & 0xff, + (mask->type.discrete. + deassert_event & 0xff00) >> 8); + break; + + case ANALOG_SENSOR: + printf(" Assert Event Mask : "); + if (mask->type.threshold.assert_lnr_high) + printf("lnr+ "); + if (mask->type.threshold.assert_lnr_low) + printf("lnr- "); + if (mask->type.threshold.assert_lcr_high) + printf("lcr+ "); + if (mask->type.threshold.assert_lcr_low) + printf("lcr- "); + if (mask->type.threshold.assert_lnc_high) + printf("lnc+ "); + if (mask->type.threshold.assert_lnc_low) + printf("lnc- "); + if (mask->type.threshold.assert_unc_high) + printf("unc+ "); + if (mask->type.threshold.assert_unc_low) + printf("unc- "); + if (mask->type.threshold.assert_ucr_high) + printf("ucr+ "); + if (mask->type.threshold.assert_ucr_low) + printf("ucr- "); + if (mask->type.threshold.assert_unr_high) + printf("unr+ "); + if (mask->type.threshold.assert_unr_low) + printf("unr- "); + printf("\n"); + + printf(" Deassert Event Mask : "); + if (mask->type.threshold.deassert_lnr_high) + printf("lnr+ "); + if (mask->type.threshold.deassert_lnr_low) + printf("lnr- "); + if (mask->type.threshold.deassert_lcr_high) + printf("lcr+ "); + if (mask->type.threshold.deassert_lcr_low) + printf("lcr- "); + if (mask->type.threshold.deassert_lnc_high) + printf("lnc+ "); + if (mask->type.threshold.deassert_lnc_low) + printf("lnc- "); + if (mask->type.threshold.deassert_unc_high) + printf("unc+ "); + if (mask->type.threshold.deassert_unc_low) + printf("unc- "); + if (mask->type.threshold.deassert_ucr_high) + printf("ucr+ "); + if (mask->type.threshold.deassert_ucr_low) + printf("ucr- "); + if (mask->type.threshold.deassert_unr_high) + printf("unr+ "); + if (mask->type.threshold.deassert_unr_low) + printf("unr- "); + printf("\n"); + break; + + default: + break; + } + + return 0; +} + +int +ipmi_sdr_print_sensor_event_enable(struct ipmi_intf *intf, + uint8_t sensor_num, + uint8_t sensor_type, + uint8_t event_type, int numeric_fmt, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rs *rsp; + int i; + const struct valstr assert_cond_1[] = { + {0x80, "unc+"}, + {0x40, "unc-"}, + {0x20, "lnr+"}, + {0x10, "lnr-"}, + {0x08, "lcr+"}, + {0x04, "lcr-"}, + {0x02, "lnc+"}, + {0x01, "lnc-"}, + {0x00, NULL}, + }; + const struct valstr assert_cond_2[] = { + {0x08, "unr+"}, + {0x04, "unr-"}, + {0x02, "ucr+"}, + {0x01, "ucr-"}, + {0x00, NULL}, + }; + + rsp = ipmi_sdr_get_sensor_event_enable(intf, sensor_num, + target, lun, channel); + + if (rsp == NULL) { + lprintf(LOG_DEBUG, + "Error reading event enable for sensor #%02x", + sensor_num); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_DEBUG, + "Error reading event enable for sensor #%02x: %s", + sensor_num, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (IS_SCANNING_DISABLED(rsp->data[0])) { + //printf(" Event Enable : Scanning Disabled\n"); + //return 0; + } + if (IS_EVENT_MSG_DISABLED(rsp->data[0])) { + printf(" Event Enable : Event Messages Disabled\n"); + //return 0; + } + + switch (numeric_fmt) { + case DISCRETE_SENSOR: + /* discrete */ + if (rsp->data_len == 2) { + ipmi_sdr_print_discrete_state("Assertions Enabled", + sensor_type, event_type, + rsp->data[1], 0); + } else if (rsp->data_len > 2) { + ipmi_sdr_print_discrete_state("Assertions Enabled", + sensor_type, event_type, + rsp->data[1], + rsp->data[2]); + } + if (rsp->data_len == 4) { + ipmi_sdr_print_discrete_state("Deassertions Enabled", + sensor_type, event_type, + rsp->data[3], 0); + } else if (rsp->data_len > 4) { + ipmi_sdr_print_discrete_state("Deassertions Enabled", + sensor_type, event_type, + rsp->data[3], + rsp->data[4]); + } + break; + + case ANALOG_SENSOR: + /* analog */ + printf(" Assertions Enabled : "); + for (i = 0; i < 8; i++) { + if (rsp->data[1] & (1 << i)) + printf("%s ", val2str(1 << i, assert_cond_1)); + } + if (rsp->data_len > 2) { + for (i = 0; i < 4; i++) { + if (rsp->data[2] & (1 << i)) + printf("%s ", + val2str(1 << i, assert_cond_2)); + } + printf("\n"); + if ((rsp->data_len == 4 && rsp->data[3] != 0) || + (rsp->data_len > 4 + && (rsp->data[3] != 0 || rsp->data[4] != 0))) { + printf(" Deassertions Enabled : "); + for (i = 0; i < 8; i++) { + if (rsp->data[3] & (1 << i)) + printf("%s ", + val2str(1 << i, + assert_cond_1)); + } + if (rsp->data_len > 4) { + for (i = 0; i < 4; i++) { + if (rsp->data[4] & (1 << i)) + printf("%s ", + val2str(1 << i, + assert_cond_2)); + } + } + printf("\n"); + } + } else { + printf("\n"); + } + break; + + default: + break; + } + + return 0; +} + +/* ipmi_sdr_print_sensor_hysteresis - print hysteresis for Discrete & Analog + * + * @sensor: Common Sensor Record SDR pointer + * @full: Full Sensor Record SDR pointer (if applicable) + * @hysteresis_value: Actual hysteresis value + * @hvstr: hysteresis value Identifier String + * + * returns void + */ +void +ipmi_sdr_print_sensor_hysteresis(struct sdr_record_common_sensor *sensor, + struct sdr_record_full_sensor *full, + uint8_t hysteresis_value, + const char *hvstr) +{ + /* + * compact can have pos/neg hysteresis, but they cannot be analog! + * We use not full in addition to our discrete units check just in + * case a compact sensor is incorrectly identified as analog. + */ + if (!full || UNITS_ARE_DISCRETE(sensor)) { + if ( hysteresis_value == 0x00 || hysteresis_value == 0xff ) { + printf(" %s : Unspecified\n", hvstr); + } else { + printf(" %s : 0x%02X\n", hvstr, hysteresis_value); + } + return; + } + /* A Full analog sensor */ + double creading = sdr_convert_sensor_hysterisis(full, hysteresis_value); + if ( hysteresis_value == 0x00 || hysteresis_value == 0xff || + creading == 0.0 ) { + printf(" %s : Unspecified\n", hvstr); + } else { + printf(" %s : %.3f\n", hvstr, creading); + } +} + +/* print_sensor_min_max - print Discrete & Analog Minimum/Maximum Sensor Range + * + * @full: Full Sensor Record SDR pointer + * + * returns void + */ +static void +print_sensor_min_max(struct sdr_record_full_sensor *full) +{ + if (!full) { /* No min/max for compact SDR record */ + return; + } + + double creading = 0.0; + uint8_t is_analog = !UNITS_ARE_DISCRETE(&full->cmn); + if (is_analog) + creading = sdr_convert_sensor_reading(full, full->sensor_min); + if ((full->cmn.unit.analog == 0 && full->sensor_min == 0x00) || + (full->cmn.unit.analog == 1 && full->sensor_min == 0xff) || + (full->cmn.unit.analog == 2 && full->sensor_min == 0x80) || + (is_analog && (creading == 0.0))) + printf(" Minimum sensor range : Unspecified\n"); + else { + if (is_analog) + printf(" Minimum sensor range : %.3f\n", creading); + else + printf(" Minimum sensor range : 0x%02X\n", full->sensor_min); + + } + if (is_analog) + creading = sdr_convert_sensor_reading(full, full->sensor_max); + if ((full->cmn.unit.analog == 0 && full->sensor_max == 0xff) || + (full->cmn.unit.analog == 1 && full->sensor_max == 0x00) || + (full->cmn.unit.analog == 2 && full->sensor_max == 0x7f) || + (is_analog && (creading == 0.0))) + printf(" Maximum sensor range : Unspecified\n"); + else { + if (is_analog) + printf(" Maximum sensor range : %.3f\n", creading); + else + printf(" Maximum sensor range : 0x%02X\n", full->sensor_max); + } +} + +/* print_csv_discrete - print csv formatted discrete sensor + * + * @sensor: common sensor structure + * @sr: sensor reading + * + * returns void + */ +static void +print_csv_discrete(struct sdr_record_common_sensor *sensor, + const struct sensor_reading *sr) +{ + if (!sr->s_reading_valid || sr->s_reading_unavailable) { + printf("%02Xh,ns,%d.%d,No Reading", + sensor->keys.sensor_num, + sensor->entity.id, + sensor->entity.instance); + return; + } + + if (sr->s_has_analog_value) { /* Sensor has an analog value */ + printf("%s,%s,", sr->s_a_str, sr->s_a_units); + } else { /* Sensor has a discrete value */ + printf("%02Xh,", sensor->keys.sensor_num); + } + printf("ok,%d.%d,", + sensor->entity.id, + sensor->entity.instance); + ipmi_sdr_print_discrete_state_mini(NULL, ", ", + sensor->sensor.type, + sensor->event_type, + sr->s_data2, + sr->s_data3); +} + +/* ipmi_sdr_read_sensor_value - read sensor value + * + * @intf Interface pointer + * @sensor Common sensor component pointer + * @sdr_record_type Type of sdr sensor record + * @precision decimal precision for analog format conversion + * + * returns a pointer to sensor value reading data structure + */ +struct sensor_reading * +ipmi_sdr_read_sensor_value(struct ipmi_intf *intf, + struct sdr_record_common_sensor *sensor, + uint8_t sdr_record_type, int precision) +{ + static struct sensor_reading sr; + + if (sensor == NULL) + return NULL; + + /* Initialize to reading valid value of zero */ + memset(&sr, 0, sizeof(sr)); + + switch (sdr_record_type) { + int idlen; + case (SDR_RECORD_TYPE_FULL_SENSOR): + sr.full = (struct sdr_record_full_sensor *)sensor; + idlen = sr.full->id_code & 0x1f; + idlen = idlen < sizeof(sr.s_id) ? + idlen : sizeof(sr.s_id) - 1; + memcpy(sr.s_id, sr.full->id_string, idlen); + break; + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sr.compact = (struct sdr_record_compact_sensor *)sensor; + idlen = sr.compact->id_code & 0x1f; + idlen = idlen < sizeof(sr.s_id) ? + idlen : sizeof(sr.s_id) - 1; + memcpy(sr.s_id, sr.compact->id_string, idlen); + break; + default: + return NULL; + } + + /* + * Get current reading via IPMI interface + */ + struct ipmi_rs *rsp; + rsp = ipmi_sdr_get_sensor_reading_ipmb(intf, + sensor->keys.sensor_num, + sensor->keys.owner_id, + sensor->keys.lun, + sensor->keys.channel); + sr.s_a_val = 0.0; /* init analog value to a floating point 0 */ + sr.s_a_str[0] = '\0'; /* no converted analog value string */ + sr.s_a_units = ""; /* no converted analog units units */ + + + if (rsp == NULL) { + lprintf(LOG_DEBUG, "Error reading sensor %s (#%02x)", + sr.s_id, sensor->keys.sensor_num); + return &sr; + } + + if (rsp->ccode) { + if ( !((sr.full && rsp->ccode == 0xcb) || + (sr.compact && rsp->ccode == 0xcd)) ) { + lprintf(LOG_DEBUG, + "Error reading sensor %s (#%02x): %s", sr.s_id, + sensor->keys.sensor_num, + val2str(rsp->ccode, completion_code_vals)); + } + return &sr; + } + + if (rsp->data_len < 2) { + /* + * We must be returned both a value (data[0]), and the validity + * of the value (data[1]), in order to correctly interpret + * the reading. If we don't have both of these we can't have + * a valid sensor reading. + */ + lprintf(LOG_DEBUG, "Error reading sensor %s invalid len %d", + sr.s_id, rsp->data_len); + return &sr; + } + + + if (IS_READING_UNAVAILABLE(rsp->data[1])) + sr.s_reading_unavailable = 1; + + if (IS_SCANNING_DISABLED(rsp->data[1])) { + sr.s_scanning_disabled = 1; + lprintf(LOG_DEBUG, "Sensor %s (#%02x) scanning disabled", + sr.s_id, sensor->keys.sensor_num); + return &sr; + } + if ( !sr.s_reading_unavailable ) { + sr.s_reading_valid = 1; + sr.s_reading = rsp->data[0]; + } + if (rsp->data_len > 2) + sr.s_data2 = rsp->data[2]; + if (rsp->data_len > 3) + sr.s_data3 = rsp->data[3]; + if (sdr_sensor_has_analog_reading(intf, &sr)) { + sr.s_has_analog_value = 1; + if (sr.s_reading_valid) { + sr.s_a_val = sdr_convert_sensor_reading(sr.full, sr.s_reading); + } + /* determine units string with possible modifiers */ + sr.s_a_units = ipmi_sdr_get_unit_string(sr.full->cmn.unit.pct, + sr.full->cmn.unit.modifier, + sr.full->cmn.unit.type.base, + sr.full->cmn.unit.type.modifier); + snprintf(sr.s_a_str, sizeof(sr.s_a_str), "%.*f", + (sr.s_a_val == (int) sr.s_a_val) ? 0 : + precision, sr.s_a_val); + } + return &sr; +} + +/* ipmi_sdr_print_sensor_fc - print full & compact SDR records + * + * @intf: ipmi interface + * @sensor: common sensor structure + * @sdr_record_type: type of sdr record, either full or compact + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_fc(struct ipmi_intf *intf, + struct sdr_record_common_sensor *sensor, + uint8_t sdr_record_type) +{ + char sval[16]; + int i = 0; + uint8_t target, lun, channel; + struct sensor_reading *sr; + + + sr = ipmi_sdr_read_sensor_value(intf, sensor, sdr_record_type, 2); + + if (sr == NULL) + return -1; + + target = sensor->keys.owner_id; + lun = sensor->keys.lun; + channel = sensor->keys.channel; + + /* + * CSV OUTPUT + */ + + if (csv_output) { + /* + * print sensor name, reading, unit, state + */ + printf("%s,", sr->s_id); + if (!IS_THRESHOLD_SENSOR(sensor)) { + /* Discrete/Non-Threshold */ + print_csv_discrete(sensor, sr); + printf("\n"); + } + else { + /* Threshold Analog & Discrete*/ + if (sr->s_reading_valid) { + if (sr->s_has_analog_value) { + /* Analog/Threshold */ + printf("%.*f,", (sr->s_a_val == + (int) sr->s_a_val) ? 0 : 3, + sr->s_a_val); + printf("%s,%s", sr->s_a_units, + ipmi_sdr_get_thresh_status(sr, "ns")); + } else { /* Discrete/Threshold */ + print_csv_discrete(sensor, sr); + } + } else { + printf(",,ns"); + } + + if (verbose) { + printf(",%d.%d,%s,%s,", + sensor->entity.id, sensor->entity.instance, + val2str(sensor->entity.id, entity_id_vals), + ipmi_sdr_get_sensor_type_desc(sensor->sensor. + type)); + + if (sr->full) { + SENSOR_PRINT_CSV(sr->full, sr->full->analog_flag.nominal_read, + sr->full->nominal_read); + SENSOR_PRINT_CSV(sr->full, sr->full->analog_flag.normal_min, + sr->full->normal_min); + SENSOR_PRINT_CSV(sr->full, sr->full->analog_flag.normal_max, + sr->full->normal_max); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.unr, + sr->full->threshold.upper.non_recover); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.ucr, + sr->full->threshold.upper.critical); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.unc, + sr->full->threshold.upper.non_critical); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.lnr, + sr->full->threshold.lower.non_recover); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.lcr, + sr->full->threshold.lower.critical); + SENSOR_PRINT_CSV(sr->full, sensor->mask.type.threshold.read.lnc, + sr->full->threshold.lower.non_critical); + + if (UNITS_ARE_DISCRETE(sensor)) { + printf("0x%02X,0x%02X", sr->full->sensor_min, sr->full->sensor_max); + } + else { + printf("%.3f,%.3f", + sdr_convert_sensor_reading(sr->full, + sr->full->sensor_min), + sdr_convert_sensor_reading(sr->full, + sr->full->sensor_max)); + } + } else { + printf(",,,,,,,,,,"); + } + } + printf("\n"); + } + + return 0; /* done */ + } + + /* + * NORMAL OUTPUT + */ + + if (verbose == 0 && sdr_extended == 0) { + /* + * print sensor name, reading, state + */ + printf("%-16s | ", sr->s_id); + + memset(sval, 0, sizeof (sval)); + + if (sr->s_reading_valid) { + if( sr->s_has_analog_value ) { + snprintf(sval, sizeof (sval), "%s %s", + sr->s_a_str, + sr->s_a_units); + } else /* Discrete */ + snprintf(sval, sizeof(sval), + "0x%02x", sr->s_reading); + } + else if (sr->s_scanning_disabled) + snprintf(sval, sizeof (sval), sr->full ? "disabled" : "Not Readable"); + else + snprintf(sval, sizeof (sval), sr->full ? "no reading" : "Not Readable"); + + printf("%s", sval); + + for (i = strlen(sval); i <= sizeof (sval); i++) + printf(" "); + printf(" | "); + + if (IS_THRESHOLD_SENSOR(sensor)) { + printf("%s", ipmi_sdr_get_thresh_status(sr, "ns")); + } + else { + printf("%s", sr->s_reading_valid ? "ok" : "ns"); + } + + printf("\n"); + + return 0; /* done */ + } else if (verbose == 0 && sdr_extended == 1) { + /* + * print sensor name, number, state, entity, reading + */ + printf("%-16s | %02Xh | ", + sr->s_id, sensor->keys.sensor_num); + + if (IS_THRESHOLD_SENSOR(sensor)) { + /* Threshold Analog & Discrete */ + printf("%-3s | %2d.%1d | ", + ipmi_sdr_get_thresh_status(sr, "ns"), + sensor->entity.id, sensor->entity.instance); + } + else { + /* Non Threshold Analog & Discrete */ + printf("%-3s | %2d.%1d | ", + (sr->s_reading_valid ? "ok" : "ns"), + sensor->entity.id, sensor->entity.instance); + } + + memset(sval, 0, sizeof (sval)); + + if (sr->s_reading_valid) { + if (IS_THRESHOLD_SENSOR(sensor) && + sr->s_has_analog_value ) { + /* Threshold Analog */ + snprintf(sval, sizeof (sval), "%s %s", + sr->s_a_str, + sr->s_a_units); + } else { + /* Analog & Discrete & Threshold/Discrete */ + char *header = NULL; + if (sr->s_has_analog_value) { /* Sensor has an analog value */ + printf("%s %s", sr->s_a_str, sr->s_a_units); + header = ", "; + } + ipmi_sdr_print_discrete_state_mini(header, ", ", + sensor->sensor.type, + sensor->event_type, + sr->s_data2, + sr->s_data3); + } + } + else if (sr->s_scanning_disabled) + snprintf(sval, sizeof (sval), "Disabled"); + else + snprintf(sval, sizeof (sval), "No Reading"); + + printf("%s\n", sval); + return 0; /* done */ + } + /* + * VERBOSE OUTPUT + */ + + printf("Sensor ID : %s (0x%x)\n", + sr->s_id, sensor->keys.sensor_num); + printf(" Entity ID : %d.%d (%s)\n", + sensor->entity.id, sensor->entity.instance, + val2str(sensor->entity.id, entity_id_vals)); + + if (!IS_THRESHOLD_SENSOR(sensor)) { + /* Discrete */ + printf(" Sensor Type (Discrete): %s (0x%02x)\n", + ipmi_sdr_get_sensor_type_desc(sensor->sensor.type), + sensor->sensor.type); + lprintf(LOG_DEBUG, " Event Type Code : 0x%02x", + sensor->event_type); + + printf(" Sensor Reading : "); + if (sr->s_reading_valid) { + if (sr->s_has_analog_value) { /* Sensor has an analog value */ + printf("%s %s\n", sr->s_a_str, sr->s_a_units); + } else { + printf("%xh\n", sr->s_reading); + } + } + else if (sr->s_scanning_disabled) + printf("Disabled\n"); + else { + /* Used to be 'Not Reading' */ + printf("No Reading\n"); + } + + printf(" Event Message Control : "); + switch (sensor->sensor.capabilities.event_msg) { + case 0: + printf("Per-threshold\n"); + break; + case 1: + printf("Entire Sensor Only\n"); + break; + case 2: + printf("Global Disable Only\n"); + break; + case 3: + printf("No Events From Sensor\n"); + break; + } + + ipmi_sdr_print_discrete_state("States Asserted", + sensor->sensor.type, + sensor->event_type, + sr->s_data2, + sr->s_data3); + ipmi_sdr_print_sensor_mask(&sensor->mask, sensor->sensor.type, + sensor->event_type, DISCRETE_SENSOR); + ipmi_sdr_print_sensor_event_status(intf, + sensor->keys.sensor_num, + sensor->sensor.type, + sensor->event_type, + DISCRETE_SENSOR, + target, + lun, channel); + ipmi_sdr_print_sensor_event_enable(intf, + sensor->keys.sensor_num, + sensor->sensor.type, + sensor->event_type, + DISCRETE_SENSOR, + target, + lun, channel); + printf(" OEM : %X\n", + sr->full ? sr->full->oem : sr->compact->oem); + printf("\n"); + + return 0; /* done */ + } + printf(" Sensor Type (Threshold) : %s (0x%02x)\n", + ipmi_sdr_get_sensor_type_desc(sensor->sensor.type), + sensor->sensor.type); + + printf(" Sensor Reading : "); + if (sr->s_reading_valid) { + if (sr->full) { + uint16_t raw_tol = __TO_TOL(sr->full->mtol); + if (UNITS_ARE_DISCRETE(sensor)) { + printf("0x%02X (+/- 0x%02X) %s\n", + sr->s_reading, raw_tol, sr->s_a_units); + } + else { + double tol = sdr_convert_sensor_tolerance(sr->full, raw_tol); + printf("%.*f (+/- %.*f) %s\n", + (sr->s_a_val == (int) sr->s_a_val) ? 0 : 3, + sr->s_a_val, (tol == (int) tol) ? 0 : + 3, tol, sr->s_a_units); + } + } else { + printf("0x%02X %s\n", sr->s_reading, sr->s_a_units); + } + } else if (sr->s_scanning_disabled) + printf("Disabled\n"); + else + printf("No Reading\n"); + + printf(" Status : %s\n", + ipmi_sdr_get_thresh_status(sr, "Not Available")); + + if(sr->full) { + SENSOR_PRINT_NORMAL(sr->full, "Nominal Reading", nominal_read); + SENSOR_PRINT_NORMAL(sr->full, "Normal Minimum", normal_min); + SENSOR_PRINT_NORMAL(sr->full, "Normal Maximum", normal_max); + + SENSOR_PRINT_THRESH(sr->full, "Upper non-recoverable", upper.non_recover, unr); + SENSOR_PRINT_THRESH(sr->full, "Upper critical", upper.critical, ucr); + SENSOR_PRINT_THRESH(sr->full, "Upper non-critical", upper.non_critical, unc); + SENSOR_PRINT_THRESH(sr->full, "Lower non-recoverable", lower.non_recover, lnr); + SENSOR_PRINT_THRESH(sr->full, "Lower critical", lower.critical, lcr); + SENSOR_PRINT_THRESH(sr->full, "Lower non-critical", lower.non_critical, lnc); + } + ipmi_sdr_print_sensor_hysteresis(sensor, sr->full, + sr->full ? sr->full->threshold.hysteresis.positive : + sr->compact->threshold.hysteresis.positive, "Positive Hysteresis"); + + ipmi_sdr_print_sensor_hysteresis(sensor, sr->full, + sr->full ? sr->full->threshold.hysteresis.negative : + sr->compact->threshold.hysteresis.negative, "Negative Hysteresis"); + + print_sensor_min_max(sr->full); + + printf(" Event Message Control : "); + switch (sensor->sensor.capabilities.event_msg) { + case 0: + printf("Per-threshold\n"); + break; + case 1: + printf("Entire Sensor Only\n"); + break; + case 2: + printf("Global Disable Only\n"); + break; + case 3: + printf("No Events From Sensor\n"); + break; + } + + printf(" Readable Thresholds : "); + switch (sensor->sensor.capabilities.threshold) { + case 0: + printf("No Thresholds\n"); + break; + case 1: /* readable according to mask */ + case 2: /* readable and settable according to mask */ + if (sensor->mask.type.threshold.read.lnr) + printf("lnr "); + if (sensor->mask.type.threshold.read.lcr) + printf("lcr "); + if (sensor->mask.type.threshold.read.lnc) + printf("lnc "); + if (sensor->mask.type.threshold.read.unc) + printf("unc "); + if (sensor->mask.type.threshold.read.ucr) + printf("ucr "); + if (sensor->mask.type.threshold.read.unr) + printf("unr "); + printf("\n"); + break; + case 3: + printf("Thresholds Fixed\n"); + break; + } + + printf(" Settable Thresholds : "); + switch (sensor->sensor.capabilities.threshold) { + case 0: + printf("No Thresholds\n"); + break; + case 1: /* readable according to mask */ + case 2: /* readable and settable according to mask */ + if (sensor->mask.type.threshold.set.lnr) + printf("lnr "); + if (sensor->mask.type.threshold.set.lcr) + printf("lcr "); + if (sensor->mask.type.threshold.set.lnc) + printf("lnc "); + if (sensor->mask.type.threshold.set.unc) + printf("unc "); + if (sensor->mask.type.threshold.set.ucr) + printf("ucr "); + if (sensor->mask.type.threshold.set.unr) + printf("unr "); + printf("\n"); + break; + case 3: + printf("Thresholds Fixed\n"); + break; + } + + if (sensor->mask.type.threshold.status_lnr || + sensor->mask.type.threshold.status_lcr || + sensor->mask.type.threshold.status_lnc || + sensor->mask.type.threshold.status_unc || + sensor->mask.type.threshold.status_ucr || + sensor->mask.type.threshold.status_unr) { + printf(" Threshold Read Mask : "); + if (sensor->mask.type.threshold.status_lnr) + printf("lnr "); + if (sensor->mask.type.threshold.status_lcr) + printf("lcr "); + if (sensor->mask.type.threshold.status_lnc) + printf("lnc "); + if (sensor->mask.type.threshold.status_unc) + printf("unc "); + if (sensor->mask.type.threshold.status_ucr) + printf("ucr "); + if (sensor->mask.type.threshold.status_unr) + printf("unr "); + printf("\n"); + } + + ipmi_sdr_print_sensor_mask(&sensor->mask, + sensor->sensor.type, + sensor->event_type, ANALOG_SENSOR); + ipmi_sdr_print_sensor_event_status(intf, + sensor->keys.sensor_num, + sensor->sensor.type, + sensor->event_type, ANALOG_SENSOR, + target, + lun, channel); + + ipmi_sdr_print_sensor_event_enable(intf, + sensor->keys.sensor_num, + sensor->sensor.type, + sensor->event_type, ANALOG_SENSOR, + target, + lun, channel); + + printf("\n"); + return 0; +} + +static inline int +get_offset(uint8_t x) +{ + int i; + for (i = 0; i < 8; i++) + if (x >> i == 1) + return i; + return 0; +} + +/* ipmi_sdr_print_discrete_state_mini - print list of asserted states + * for a discrete sensor + * + * @header : header string if necessary + * @separator : field separator string + * @sensor_type : sensor type code + * @event_type : event type code + * @state : mask of asserted states + * + * no meaningful return value + */ +void +ipmi_sdr_print_discrete_state_mini(const char *header, const char *separator, + uint8_t sensor_type, uint8_t event_type, + uint8_t state1, uint8_t state2) +{ + uint8_t typ; + struct ipmi_event_sensor_types *evt; + int pre = 0, c = 0; + + if (state1 == 0 && (state2 & 0x7f) == 0) + return; + + if (event_type == 0x6f) { + evt = sensor_specific_types; + typ = sensor_type; + } else { + evt = generic_event_types; + typ = event_type; + } + + if (header) + printf("%s", header); + + for (; evt->type != NULL; evt++) { + if ((evt->code != typ) || + (evt->data != 0xFF)) + continue; + + if (evt->offset > 7) { + if ((1 << (evt->offset - 8)) & (state2 & 0x7f)) { + if (pre++ != 0) + printf("%s", separator); + if (evt->desc) + printf("%s", evt->desc); + } + } else { + if ((1 << evt->offset) & state1) { + if (pre++ != 0) + printf("%s", separator); + if (evt->desc) + printf("%s", evt->desc); + } + } + c++; + } +} + +/* ipmi_sdr_print_discrete_state - print list of asserted states + * for a discrete sensor + * + * @desc : description for this line + * @sensor_type : sensor type code + * @event_type : event type code + * @state : mask of asserted states + * + * no meaningful return value + */ +void +ipmi_sdr_print_discrete_state(const char *desc, + uint8_t sensor_type, uint8_t event_type, + uint8_t state1, uint8_t state2) +{ + uint8_t typ; + struct ipmi_event_sensor_types *evt; + int pre = 0, c = 0; + + if (state1 == 0 && (state2 & 0x7f) == 0) + return; + + if (event_type == 0x6f) { + evt = sensor_specific_types; + typ = sensor_type; + } else { + evt = generic_event_types; + typ = event_type; + } + + for (; evt->type != NULL; evt++) { + if ((evt->code != typ) || + (evt->data != 0xFF)) + continue; + + if (pre == 0) { + printf(" %-21s : %s\n", desc, evt->type); + pre = 1; + } + + if (evt->offset > 7) { + if ((1 << (evt->offset - 8)) & (state2 & 0x7f)) { + if (evt->desc) { + printf(" " + "[%s]\n", + evt->desc); + } else { + printf(" " + "[no description]\n"); + } + } + } else { + if ((1 << evt->offset) & state1) { + if (evt->desc) { + printf(" " + "[%s]\n", + evt->desc); + } else { + printf(" " + "[no description]\n"); + } + } + } + c++; + } +} + + +/* ipmi_sdr_print_sensor_eventonly - print SDR event only record + * + * @intf: ipmi interface + * @sensor: event only sdr record + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_eventonly(struct ipmi_intf *intf, + struct sdr_record_eventonly_sensor *sensor) +{ + char desc[17]; + + if (sensor == NULL) + return -1; + + memset(desc, 0, sizeof (desc)); + snprintf(desc, (sensor->id_code & 0x1f) + 1, "%s", sensor->id_string); + + if (verbose) { + printf("Sensor ID : %s (0x%x)\n", + sensor->id_code ? desc : "", sensor->keys.sensor_num); + printf("Entity ID : %d.%d (%s)\n", + sensor->entity.id, sensor->entity.instance, + val2str(sensor->entity.id, entity_id_vals)); + printf("Sensor Type : %s (0x%02x)\n", + ipmi_sdr_get_sensor_type_desc(sensor->sensor_type), + sensor->sensor_type); + lprintf(LOG_DEBUG, "Event Type Code : 0x%02x", + sensor->event_type); + printf("\n"); + } else { + if (csv_output) + printf("%s,%02Xh,ns,%d.%d,Event-Only\n", + sensor->id_code ? desc : "", + sensor->keys.sensor_num, + sensor->entity.id, sensor->entity.instance); + else if (sdr_extended) + printf("%-16s | %02Xh | ns | %2d.%1d | Event-Only\n", + sensor->id_code ? desc : "", + sensor->keys.sensor_num, + sensor->entity.id, sensor->entity.instance); + else + printf("%-16s | Event-Only | ns\n", + sensor->id_code ? desc : ""); + } + + return 0; +} + +/* ipmi_sdr_print_sensor_mc_locator - print SDR MC locator record + * + * @intf: ipmi interface + * @mc: mc locator sdr record + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_mc_locator(struct ipmi_intf *intf, + struct sdr_record_mc_locator *mc) +{ + char desc[17]; + + if (mc == NULL) + return -1; + + memset(desc, 0, sizeof (desc)); + snprintf(desc, (mc->id_code & 0x1f) + 1, "%s", mc->id_string); + + if (verbose == 0) { + if (csv_output) + printf("%s,00h,ok,%d.%d\n", + mc->id_code ? desc : "", + mc->entity.id, mc->entity.instance); + else if (sdr_extended) { + printf("%-16s | 00h | ok | %2d.%1d | ", + mc->id_code ? desc : "", + mc->entity.id, mc->entity.instance); + + printf("%s MC @ %02Xh\n", + (mc-> + pwr_state_notif & 0x1) ? "Static" : "Dynamic", + mc->dev_slave_addr); + } else { + printf("%-16s | %s MC @ %02Xh %s | ok\n", + mc->id_code ? desc : "", + (mc-> + pwr_state_notif & 0x1) ? "Static" : "Dynamic", + mc->dev_slave_addr, + (mc->pwr_state_notif & 0x1) ? " " : ""); + } + + return 0; /* done */ + } + + printf("Device ID : %s\n", mc->id_string); + printf("Entity ID : %d.%d (%s)\n", + mc->entity.id, mc->entity.instance, + val2str(mc->entity.id, entity_id_vals)); + + printf("Device Slave Address : %02Xh\n", mc->dev_slave_addr); + printf("Channel Number : %01Xh\n", mc->channel_num); + + printf("ACPI System P/S Notif : %sRequired\n", + (mc->pwr_state_notif & 0x4) ? "" : "Not "); + printf("ACPI Device P/S Notif : %sRequired\n", + (mc->pwr_state_notif & 0x2) ? "" : "Not "); + printf("Controller Presence : %s\n", + (mc->pwr_state_notif & 0x1) ? "Static" : "Dynamic"); + printf("Logs Init Agent Errors : %s\n", + (mc->global_init & 0x8) ? "Yes" : "No"); + + printf("Event Message Gen : "); + if (!(mc->global_init & 0x3)) + printf("Enable\n"); + else if ((mc->global_init & 0x3) == 0x1) + printf("Disable\n"); + else if ((mc->global_init & 0x3) == 0x2) + printf("Do Not Init Controller\n"); + else + printf("Reserved\n"); + + printf("Device Capabilities\n"); + printf(" Chassis Device : %s\n", + (mc->dev_support & 0x80) ? "Yes" : "No"); + printf(" Bridge : %s\n", + (mc->dev_support & 0x40) ? "Yes" : "No"); + printf(" IPMB Event Generator : %s\n", + (mc->dev_support & 0x20) ? "Yes" : "No"); + printf(" IPMB Event Receiver : %s\n", + (mc->dev_support & 0x10) ? "Yes" : "No"); + printf(" FRU Inventory Device : %s\n", + (mc->dev_support & 0x08) ? "Yes" : "No"); + printf(" SEL Device : %s\n", + (mc->dev_support & 0x04) ? "Yes" : "No"); + printf(" SDR Repository : %s\n", + (mc->dev_support & 0x02) ? "Yes" : "No"); + printf(" Sensor Device : %s\n", + (mc->dev_support & 0x01) ? "Yes" : "No"); + + printf("\n"); + + return 0; +} + +/* ipmi_sdr_print_sensor_generic_locator - print generic device locator record + * + * @intf: ipmi interface + * @gen: generic device locator sdr record + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_generic_locator(struct ipmi_intf *intf, + struct sdr_record_generic_locator *dev) +{ + char desc[17]; + + memset(desc, 0, sizeof (desc)); + snprintf(desc, (dev->id_code & 0x1f) + 1, "%s", dev->id_string); + + if (!verbose) { + if (csv_output) + printf("%s,00h,ns,%d.%d\n", + dev->id_code ? desc : "", + dev->entity.id, dev->entity.instance); + else if (sdr_extended) + printf + ("%-16s | 00h | ns | %2d.%1d | Generic Device @%02Xh:%02Xh.%1d\n", + dev->id_code ? desc : "", dev->entity.id, + dev->entity.instance, dev->dev_access_addr, + dev->dev_slave_addr, dev->oem); + else + printf("%-16s | Generic @%02X:%02X.%-2d | ok\n", + dev->id_code ? desc : "", + dev->dev_access_addr, + dev->dev_slave_addr, dev->oem); + + return 0; + } + + printf("Device ID : %s\n", dev->id_string); + printf("Entity ID : %d.%d (%s)\n", + dev->entity.id, dev->entity.instance, + val2str(dev->entity.id, entity_id_vals)); + + printf("Device Access Address : %02Xh\n", dev->dev_access_addr); + printf("Device Slave Address : %02Xh\n", dev->dev_slave_addr); + printf("Address Span : %02Xh\n", dev->addr_span); + printf("Channel Number : %01Xh\n", dev->channel_num); + printf("LUN.Bus : %01Xh.%01Xh\n", dev->lun, dev->bus); + printf("Device Type.Modifier : %01Xh.%01Xh (%s)\n", + dev->dev_type, dev->dev_type_modifier, + val2str(dev->dev_type << 8 | dev->dev_type_modifier, + entity_device_type_vals)); + printf("OEM : %02Xh\n", dev->oem); + printf("\n"); + + return 0; +} + +/* ipmi_sdr_print_sensor_fru_locator - print FRU locator record + * + * @intf: ipmi interface + * @fru: fru locator sdr record + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_fru_locator(struct ipmi_intf *intf, + struct sdr_record_fru_locator *fru) +{ + char desc[17]; + + memset(desc, 0, sizeof (desc)); + snprintf(desc, (fru->id_code & 0x1f) + 1, "%s", fru->id_string); + + if (!verbose) { + if (csv_output) + printf("%s,00h,ns,%d.%d\n", + fru->id_code ? desc : "", + fru->entity.id, fru->entity.instance); + else if (sdr_extended) + printf("%-16s | 00h | ns | %2d.%1d | %s FRU @%02Xh\n", + fru->id_code ? desc : "", + fru->entity.id, fru->entity.instance, + (fru->logical) ? "Logical" : "Physical", + fru->device_id); + else + printf("%-16s | %s FRU @%02Xh %02x.%x | ok\n", + fru->id_code ? desc : "", + (fru->logical) ? "Log" : "Phy", + fru->device_id, + fru->entity.id, fru->entity.instance); + + return 0; + } + + printf("Device ID : %s\n", fru->id_string); + printf("Entity ID : %d.%d (%s)\n", + fru->entity.id, fru->entity.instance, + val2str(fru->entity.id, entity_id_vals)); + + printf("Device Access Address : %02Xh\n", fru->dev_slave_addr); + printf("%s: %02Xh\n", + fru->logical ? "Logical FRU Device " : + "Slave Address ", fru->device_id); + printf("Channel Number : %01Xh\n", fru->channel_num); + printf("LUN.Bus : %01Xh.%01Xh\n", fru->lun, fru->bus); + printf("Device Type.Modifier : %01Xh.%01Xh (%s)\n", + fru->dev_type, fru->dev_type_modifier, + val2str(fru->dev_type << 8 | fru->dev_type_modifier, + entity_device_type_vals)); + printf("OEM : %02Xh\n", fru->oem); + printf("\n"); + + return 0; +} + +/* ipmi_sdr_print_sensor_entity_assoc - print SDR entity association record + * + * @intf: ipmi interface + * @mc: entity association sdr record + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sensor_entity_assoc(struct ipmi_intf *intf, + struct sdr_record_entity_assoc *assoc) +{ + return 0; +} + +/* ipmi_sdr_print_sensor_oem_intel - print Intel OEM sensors + * + * @intf: ipmi interface + * @oem: oem sdr record + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_sdr_print_sensor_oem_intel(struct ipmi_intf *intf, + struct sdr_record_oem *oem) +{ + switch (oem->data[3]) { /* record sub-type */ + case 0x02: /* Power Unit Map */ + if (verbose) { + printf + ("Sensor ID : Power Unit Redundancy (0x%x)\n", + oem->data[4]); + printf + ("Sensor Type : Intel OEM - Power Unit Map\n"); + printf("Redundant Supplies : %d", oem->data[6]); + if (oem->data[5]) + printf(" (flags %xh)", oem->data[5]); + printf("\n"); + } + + switch (oem->data_len) { + case 7: /* SR1300, non-redundant */ + if (verbose) + printf("Power Redundancy : No\n"); + else if (csv_output) + printf("Power Redundancy,Not Available,nr\n"); + else + printf + ("Power Redundancy | Not Available | nr\n"); + break; + case 8: /* SR2300, redundant, PS1 & PS2 present */ + if (verbose) { + printf("Power Redundancy : No\n"); + printf("Power Supply 2 Sensor : %x\n", + oem->data[8]); + } else if (csv_output) { + printf("Power Redundancy,PS@%02xh,nr\n", + oem->data[8]); + } else { + printf + ("Power Redundancy | PS@%02xh | nr\n", + oem->data[8]); + } + break; + case 9: /* SR2300, non-redundant, PSx present */ + if (verbose) { + printf("Power Redundancy : Yes\n"); + printf("Power Supply Sensor : %x\n", + oem->data[7]); + printf("Power Supply Sensor : %x\n", + oem->data[8]); + } else if (csv_output) { + printf + ("Power Redundancy,PS@%02xh + PS@%02xh,ok\n", + oem->data[7], oem->data[8]); + } else { + printf + ("Power Redundancy | PS@%02xh + PS@%02xh | ok\n", + oem->data[7], oem->data[8]); + } + break; + } + if (verbose) + printf("\n"); + break; + case 0x03: /* Fan Speed Control */ + break; + case 0x06: /* System Information */ + break; + case 0x07: /* Ambient Temperature Fan Speed Control */ + break; + default: + lprintf(LOG_DEBUG, "Unknown Intel OEM SDR Record type %02x", + oem->data[3]); + } + + return 0; +} + +/* ipmi_sdr_print_sensor_oem - print OEM sensors + * + * This function is generally only filled out by decoding what + * a particular BMC might stuff into its OEM records. The + * records are keyed off manufacturer ID and record subtypes. + * + * @intf: ipmi interface + * @oem: oem sdr record + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_sdr_print_sensor_oem(struct ipmi_intf *intf, struct sdr_record_oem *oem) +{ + int rc = 0; + + if (oem == NULL) + return -1; + if (oem->data_len == 0 || oem->data == NULL) + return -1; + + if (verbose > 2) + printbuf(oem->data, oem->data_len, "OEM Record"); + + /* intel manufacturer id */ + if (oem->data[0] == 0x57 && + oem->data[1] == 0x01 && oem->data[2] == 0x00) { + rc = ipmi_sdr_print_sensor_oem_intel(intf, oem); + } + + return rc; +} + +/* ipmi_sdr_print_name_from_rawentry - Print SDR name from raw data + * + * @intf: ipmi interface + * @type: sensor type + * @raw: raw sensor data + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_name_from_rawentry(struct ipmi_intf *intf,uint16_t id, + uint8_t type,uint8_t * raw) +{ + union { + struct sdr_record_full_sensor *full; + struct sdr_record_compact_sensor *compact; + struct sdr_record_eventonly_sensor *eventonly; + struct sdr_record_generic_locator *genloc; + struct sdr_record_fru_locator *fruloc; + struct sdr_record_mc_locator *mcloc; + struct sdr_record_entity_assoc *entassoc; + struct sdr_record_oem *oem; + } record; + + int rc =0; + char desc[17]; + memset(desc, ' ', sizeof (desc)); + + switch ( type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + record.full = (struct sdr_record_full_sensor *) raw; + snprintf(desc, (record.full->id_code & 0x1f) +1, "%s", + (const char *)record.full->id_string); + break; + case SDR_RECORD_TYPE_COMPACT_SENSOR: + record.compact = (struct sdr_record_compact_sensor *) raw ; + snprintf(desc, (record.compact->id_code & 0x1f) +1, "%s", + (const char *)record.compact->id_string); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + record.eventonly = (struct sdr_record_eventonly_sensor *) raw ; + snprintf(desc, (record.eventonly->id_code & 0x1f) +1, "%s", + (const char *)record.eventonly->id_string); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + record.mcloc = (struct sdr_record_mc_locator *) raw ; + snprintf(desc, (record.mcloc->id_code & 0x1f) +1, "%s", + (const char *)record.mcloc->id_string); + break; + default: + rc = -1; + break; + } + + lprintf(LOG_INFO, "ID: 0x%04x , NAME: %-16s", id, desc); + return rc; +} + +/* ipmi_sdr_print_rawentry - Print SDR entry from raw data + * + * @intf: ipmi interface + * @type: sensor type + * @raw: raw sensor data + * @len: length of raw sensor data + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_rawentry(struct ipmi_intf *intf, uint8_t type, + uint8_t * raw, int len) +{ + int rc = 0; + + switch (type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + rc = ipmi_sdr_print_sensor_fc(intf, + (struct sdr_record_common_sensor *) raw, + type); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + rc = ipmi_sdr_print_sensor_eventonly(intf, + (struct + sdr_record_eventonly_sensor + *) raw); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_generic_locator(intf, + (struct + sdr_record_generic_locator + *) raw); + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_fru_locator(intf, + (struct + sdr_record_fru_locator + *) raw); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_mc_locator(intf, + (struct + sdr_record_mc_locator *) + raw); + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + rc = ipmi_sdr_print_sensor_entity_assoc(intf, + (struct + sdr_record_entity_assoc + *) raw); + break; + case SDR_RECORD_TYPE_OEM:{ + struct sdr_record_oem oem; + oem.data = raw; + oem.data_len = len; + rc = ipmi_sdr_print_sensor_oem(intf, + (struct sdr_record_oem *) + &oem); + break; + } + case SDR_RECORD_TYPE_DEVICE_ENTITY_ASSOC: + case SDR_RECORD_TYPE_MC_CONFIRMATION: + case SDR_RECORD_TYPE_BMC_MSG_CHANNEL_INFO: + /* not implemented */ + break; + } + + return rc; +} + +/* ipmi_sdr_print_listentry - Print SDR entry from list + * + * @intf: ipmi interface + * @entry: sdr record list entry + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_listentry(struct ipmi_intf *intf, struct sdr_record_list *entry) +{ + int rc = 0; + + switch (entry->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + rc = ipmi_sdr_print_sensor_fc(intf, entry->record.common, entry->type); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + rc = ipmi_sdr_print_sensor_eventonly(intf, + entry->record.eventonly); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_generic_locator(intf, + entry->record. + genloc); + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_fru_locator(intf, + entry->record.fruloc); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + rc = ipmi_sdr_print_sensor_mc_locator(intf, + entry->record.mcloc); + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + rc = ipmi_sdr_print_sensor_entity_assoc(intf, + entry->record.entassoc); + break; + case SDR_RECORD_TYPE_OEM: + rc = ipmi_sdr_print_sensor_oem(intf, entry->record.oem); + break; + case SDR_RECORD_TYPE_DEVICE_ENTITY_ASSOC: + case SDR_RECORD_TYPE_MC_CONFIRMATION: + case SDR_RECORD_TYPE_BMC_MSG_CHANNEL_INFO: + /* not implemented yet */ + break; + } + + return rc; +} + +/* ipmi_sdr_print_sdr - iterate through SDR printing records + * + * intf: ipmi interface + * type: record type to print + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_sdr(struct ipmi_intf *intf, uint8_t type) +{ + struct sdr_get_rs *header; + struct sdr_record_list *e; + int rc = 0; + + lprintf(LOG_DEBUG, "Querying SDR for sensor list"); + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return -1; + } + } + + for (e = sdr_list_head; e != NULL; e = e->next) { + if (type != e->type && type != 0xff && type != 0xfe) + continue; + if (type == 0xfe && + e->type != SDR_RECORD_TYPE_FULL_SENSOR && + e->type != SDR_RECORD_TYPE_COMPACT_SENSOR) + continue; + if (ipmi_sdr_print_listentry(intf, e) < 0) + rc = -1; + } + + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + lprintf(LOG_ERR, "ipmitool: ipmi_sdr_get_record() failed"); + rc = -1; + continue; + } + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + if (rec != NULL) { + free(rec); + rec = NULL; + } + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + lprintf(LOG_DEBUG, "SDR record ID : 0x%04x", sdrr->id); + + if (type == header->type || type == 0xff || + (type == 0xfe && + (header->type == SDR_RECORD_TYPE_FULL_SENSOR || + header->type == SDR_RECORD_TYPE_COMPACT_SENSOR))) { + if (ipmi_sdr_print_rawentry(intf, header->type, + rec, header->length) < 0) + rc = -1; + } + + /* add to global record liset */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + return rc; +} + +/* ipmi_sdr_get_reservation - Obtain SDR reservation ID + * + * @intf: ipmi interface + * @reserve_id: pointer to short int for storing the id + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_get_reservation(struct ipmi_intf *intf, int use_builtin, + uint16_t * reserve_id) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + + /* obtain reservation ID */ + memset(&req, 0, sizeof (req)); + + if (use_builtin == 0) { + req.msg.netfn = IPMI_NETFN_STORAGE; + } else { + req.msg.netfn = IPMI_NETFN_SE; + } + + req.msg.cmd = GET_SDR_RESERVE_REPO; + rsp = intf->sendrecv(intf, &req); + + /* be slient for errors, they are handled by calling function */ + if (rsp == NULL) + return -1; + if (rsp->ccode > 0) + return -1; + + *reserve_id = ((struct sdr_reserve_repo_rs *) &(rsp->data))->reserve_id; + lprintf(LOG_DEBUG, "SDR reservation ID %04x", *reserve_id); + + return 0; +} + +/* ipmi_sdr_start - setup sdr iterator + * + * @intf: ipmi interface + * + * returns sdr iterator structure pointer + * returns NULL on error + */ +struct ipmi_sdr_iterator * +ipmi_sdr_start(struct ipmi_intf *intf, int use_builtin) +{ + struct ipmi_sdr_iterator *itr; + struct ipmi_rs *rsp; + struct ipmi_rq req; + + struct ipm_devid_rsp *devid; + + itr = malloc(sizeof (struct ipmi_sdr_iterator)); + if (itr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + + /* check SDRR capability */ + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Device ID command failed"); + free(itr); + itr = NULL; + return NULL; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Device ID command failed: %#x %s", + rsp->ccode, val2str(rsp->ccode, completion_code_vals)); + free(itr); + itr = NULL; + return NULL; + } + devid = (struct ipm_devid_rsp *) rsp->data; + + sdriana = (long)IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id); + + if (!use_builtin && (devid->device_revision & IPM_DEV_DEVICE_ID_SDR_MASK)) { + if ((devid->adtl_device_support & 0x02) == 0) { + if ((devid->adtl_device_support & 0x01)) { + lprintf(LOG_DEBUG, "Using Device SDRs\n"); + use_built_in = 1; + } else { + lprintf(LOG_ERR, "Error obtaining SDR info"); + free(itr); + itr = NULL; + return NULL; + } + } else { + lprintf(LOG_DEBUG, "Using SDR from Repository \n"); + } + } + itr->use_built_in = use_builtin ? 1 : use_built_in; + /***********************/ + if (itr->use_built_in == 0) { + struct sdr_repo_info_rs sdr_info; + /* get sdr repository info */ + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_SDR_REPO_INFO; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error obtaining SDR info"); + free(itr); + itr = NULL; + return NULL; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error obtaining SDR info: %s", + val2str(rsp->ccode, completion_code_vals)); + free(itr); + itr = NULL; + return NULL; + } + + memcpy(&sdr_info, rsp->data, sizeof (sdr_info)); + /* IPMIv1.0 == 0x01 + * IPMIv1.5 == 0x51 + * IPMIv2.0 == 0x02 + */ + if ((sdr_info.version != 0x51) && + (sdr_info.version != 0x01) && + (sdr_info.version != 0x02)) { + lprintf(LOG_WARN, "WARNING: Unknown SDR repository " + "version 0x%02x", sdr_info.version); + } + + itr->total = sdr_info.count; + itr->next = 0; + + lprintf(LOG_DEBUG, "SDR free space: %d", sdr_info.free); + lprintf(LOG_DEBUG, "SDR records : %d", sdr_info.count); + + /* Build SDRR if there is no record in repository */ + if( sdr_info.count == 0 ) { + lprintf(LOG_DEBUG, "Rebuilding SDRR..."); + + if( ipmi_sdr_add_from_sensors( intf, 0 ) != 0 ) { + lprintf(LOG_ERR, "Could not build SDRR!"); + free(itr); + itr = NULL; + return NULL; + } + } + } else { + struct sdr_device_info_rs sdr_info; + /* get device sdr info */ + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = GET_DEVICE_SDR_INFO; + + rsp = intf->sendrecv(intf, &req); + if (!rsp || !rsp->data_len || rsp->ccode) { + printf("Err in cmd get sensor sdr info\n"); + free(itr); + itr = NULL; + return NULL; + } + memcpy(&sdr_info, rsp->data, sizeof (sdr_info)); + + itr->total = sdr_info.count; + itr->next = 0; + lprintf(LOG_DEBUG, "SDR records : %d", sdr_info.count); + } + + if (ipmi_sdr_get_reservation(intf, itr->use_built_in, + &(itr->reservation)) < 0) { + lprintf(LOG_ERR, "Unable to obtain SDR reservation"); + free(itr); + itr = NULL; + return NULL; + } + + return itr; +} + +/* ipmi_sdr_get_record - return RAW SDR record + * + * @intf: ipmi interface + * @header: SDR header + * @itr: SDR iterator + * + * returns raw SDR data + * returns NULL on error + */ +uint8_t * +ipmi_sdr_get_record(struct ipmi_intf * intf, struct sdr_get_rs * header, + struct ipmi_sdr_iterator * itr) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + struct sdr_get_rq sdr_rq; + uint8_t *data; + int i = 0, len = header->length; + + if (len < 1) + return NULL; + + data = malloc(len + 1); + if (data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(data, 0, len + 1); + + memset(&sdr_rq, 0, sizeof (sdr_rq)); + sdr_rq.reserve_id = itr->reservation; + sdr_rq.id = header->id; + sdr_rq.offset = 0; + + memset(&req, 0, sizeof (req)); + if (itr->use_built_in == 0) { + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = GET_SDR; + } else { + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = GET_DEVICE_SDR; + } + req.msg.data = (uint8_t *) & sdr_rq; + req.msg.data_len = sizeof (sdr_rq); + + /* check if max length is null */ + if ( sdr_max_read_len == 0 ) { + /* get maximum response size */ + sdr_max_read_len = ipmi_intf_get_max_response_data_size(intf) - 2; + + /* cap the number of bytes to read */ + if (sdr_max_read_len > 0xFE) { + sdr_max_read_len = 0xFE; + } + } + + /* read SDR record with partial reads + * because a full read usually exceeds the maximum + * transport buffer size. (completion code 0xca) + */ + while (i < len) { + sdr_rq.length = (len - i < sdr_max_read_len) ? + len - i : sdr_max_read_len; + sdr_rq.offset = i + 5; /* 5 header bytes */ + + lprintf(LOG_DEBUG, "Getting %d bytes from SDR at offset %d", + sdr_rq.length, sdr_rq.offset); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + sdr_max_read_len = sdr_rq.length - 1; + if (sdr_max_read_len > 0) { + /* no response may happen if requests are bridged + and too many bytes are requested */ + continue; + } else { + free(data); + data = NULL; + return NULL; + } + } + + switch (rsp->ccode) { + case 0xca: + /* read too many bytes at once */ + sdr_max_read_len = sdr_rq.length - 1; + continue; + case 0xc5: + /* lost reservation */ + lprintf(LOG_DEBUG, "SDR reservation cancelled. " + "Sleeping a bit and retrying..."); + + sleep(rand() & 3); + + if (ipmi_sdr_get_reservation(intf, itr->use_built_in, + &(itr->reservation)) < 0) { + free(data); + data = NULL; + return NULL; + } + sdr_rq.reserve_id = itr->reservation; + continue; + } + + /* special completion codes handled above */ + if (rsp->ccode > 0 || rsp->data_len == 0) { + free(data); + data = NULL; + return NULL; + } + + memcpy(data + i, rsp->data + 2, sdr_rq.length); + i += sdr_max_read_len; + } + + return data; +} + +/* ipmi_sdr_end - cleanup SDR iterator + * + * @intf: ipmi interface + * @itr: SDR iterator + * + * no meaningful return code + */ +void +ipmi_sdr_end(struct ipmi_intf *intf, struct ipmi_sdr_iterator *itr) +{ + if (itr) { + free(itr); + itr = NULL; + } +} + +/* __sdr_list_add - helper function to add SDR record to list + * + * @head: list head + * @entry: new entry to add to end of list + * + * returns 0 on success + * returns -1 on error + */ +static int +__sdr_list_add(struct sdr_record_list *head, struct sdr_record_list *entry) +{ + struct sdr_record_list *e; + struct sdr_record_list *new; + + if (head == NULL) + return -1; + + new = malloc(sizeof (struct sdr_record_list)); + if (new == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memcpy(new, entry, sizeof (struct sdr_record_list)); + + e = head; + while (e->next) + e = e->next; + e->next = new; + new->next = NULL; + + return 0; +} + +/* __sdr_list_empty - low-level handler to clean up record list + * + * @head: list head to clean + * + * no meaningful return code + */ +static void +__sdr_list_empty(struct sdr_record_list *head) +{ + struct sdr_record_list *e, *f; + for (e = head; e != NULL; e = f) { + f = e->next; + free(e); + e = NULL; + } + head = NULL; +} + +/* ipmi_sdr_list_empty - clean global SDR list + * + * @intf: ipmi interface + * + * no meaningful return code + */ +void +ipmi_sdr_list_empty(struct ipmi_intf *intf) +{ + struct sdr_record_list *list, *next; + + ipmi_sdr_end(intf, sdr_list_itr); + + for (list = sdr_list_head; list != NULL; list = next) { + switch (list->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + if (list->record.common) { + free(list->record.common); + list->record.common = NULL; + } + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + if (list->record.eventonly) { + free(list->record.eventonly); + list->record.eventonly = NULL; + } + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + if (list->record.genloc) { + free(list->record.genloc); + list->record.genloc = NULL; + } + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + if (list->record.fruloc) { + free(list->record.fruloc); + list->record.fruloc = NULL; + } + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + if (list->record.mcloc) { + free(list->record.mcloc); + list->record.mcloc = NULL; + } + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + if (list->record.entassoc) { + free(list->record.entassoc); + list->record.entassoc = NULL; + } + break; + } + next = list->next; + free(list); + list = NULL; + } + + sdr_list_head = NULL; + sdr_list_tail = NULL; + sdr_list_itr = NULL; +} + +/* ipmi_sdr_find_sdr_bynumtype - lookup SDR entry by number/type + * + * @intf: ipmi interface + * @gen_id: sensor owner ID/LUN - SEL generator ID + * @num: sensor number to search for + * @type: sensor type to search for + * + * returns pointer to SDR list + * returns NULL on error + */ +struct sdr_record_list * +ipmi_sdr_find_sdr_bynumtype(struct ipmi_intf *intf, uint16_t gen_id, uint8_t num, uint8_t type) +{ + struct sdr_get_rs *header; + struct sdr_record_list *e; + int found = 0; + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return NULL; + } + } + + /* check what we've already read */ + for (e = sdr_list_head; e != NULL; e = e->next) { + switch (e->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + if (e->record.common->keys.sensor_num == num && + e->record.common->keys.owner_id == (gen_id & 0x00ff) && + e->record.common->sensor.type == type) + return e; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + if (e->record.eventonly->keys.sensor_num == num && + e->record.eventonly->keys.owner_id == (gen_id & 0x00ff) && + e->record.eventonly->sensor_type == type) + return e; + break; + } + } + + /* now keep looking */ + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + if (sdrr->record.common->keys.sensor_num == num + && sdrr->record.common->keys.owner_id == (gen_id & 0x00ff) + && sdrr->record.common->sensor.type == type) + found = 1; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + if (sdrr->record.eventonly->keys.sensor_num == num + && sdrr->record.eventonly->keys.owner_id == (gen_id & 0x00ff) + && sdrr->record.eventonly->sensor_type == type) + found = 1; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* put in the global record list */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + + if (found) + return sdrr; + } + + return NULL; +} + +/* ipmi_sdr_find_sdr_bysensortype - lookup SDR entry by sensor type + * + * @intf: ipmi interface + * @type: sensor type to search for + * + * returns pointer to SDR list + * returns NULL on error + */ +struct sdr_record_list * +ipmi_sdr_find_sdr_bysensortype(struct ipmi_intf *intf, uint8_t type) +{ + struct sdr_record_list *head; + struct sdr_get_rs *header; + struct sdr_record_list *e; + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return NULL; + } + } + + /* check what we've already read */ + head = malloc(sizeof (struct sdr_record_list)); + if (head == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(head, 0, sizeof (struct sdr_record_list)); + + for (e = sdr_list_head; e != NULL; e = e->next) { + switch (e->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + if (e->record.common->sensor.type == type) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + if (e->record.eventonly->sensor_type == type) + __sdr_list_add(head, e); + break; + } + } + + /* now keep looking */ + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + if (sdrr->record.common->sensor.type == type) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + if (sdrr->record.eventonly->sensor_type == type) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* put in the global record list */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + return head; +} + +/* ipmi_sdr_find_sdr_byentity - lookup SDR entry by entity association + * + * @intf: ipmi interface + * @entity: entity id/instance to search for + * + * returns pointer to SDR list + * returns NULL on error + */ +struct sdr_record_list * +ipmi_sdr_find_sdr_byentity(struct ipmi_intf *intf, struct entity_id *entity) +{ + struct sdr_get_rs *header; + struct sdr_record_list *e; + struct sdr_record_list *head; + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return NULL; + } + } + + head = malloc(sizeof (struct sdr_record_list)); + if (head == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(head, 0, sizeof (struct sdr_record_list)); + + /* check what we've already read */ + for (e = sdr_list_head; e != NULL; e = e->next) { + switch (e->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + if (e->record.common->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.common->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + if (e->record.eventonly->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.eventonly->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + if (e->record.genloc->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.genloc->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + if (e->record.fruloc->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.fruloc->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + if (e->record.mcloc->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.mcloc->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + if (e->record.entassoc->entity.id == entity->id && + (entity->instance == 0x7f || + e->record.entassoc->entity.instance == + entity->instance)) + __sdr_list_add(head, e); + break; + } + } + + /* now keep looking */ + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + if (sdrr->record.common->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.common->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + if (sdrr->record.eventonly->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.eventonly->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + if (sdrr->record.genloc->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.genloc->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + if (sdrr->record.fruloc->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.fruloc->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + if (sdrr->record.mcloc->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.mcloc->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + if (sdrr->record.entassoc->entity.id == entity->id + && (entity->instance == 0x7f + || sdrr->record.entassoc->entity.instance == + entity->instance)) + __sdr_list_add(head, sdrr); + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* add to global record list */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + return head; +} + +/* ipmi_sdr_find_sdr_bytype - lookup SDR entries by type + * + * @intf: ipmi interface + * @type: type of sensor record to search for + * + * returns pointer to SDR list with all matching entities + * returns NULL on error + */ +struct sdr_record_list * +ipmi_sdr_find_sdr_bytype(struct ipmi_intf *intf, uint8_t type) +{ + struct sdr_get_rs *header; + struct sdr_record_list *e; + struct sdr_record_list *head; + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return NULL; + } + } + + head = malloc(sizeof (struct sdr_record_list)); + if (head == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(head, 0, sizeof (struct sdr_record_list)); + + /* check what we've already read */ + for (e = sdr_list_head; e != NULL; e = e->next) + if (e->type == type) + __sdr_list_add(head, e); + + /* now keep looking */ + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + if (header->type == type) + __sdr_list_add(head, sdrr); + + /* add to global record list */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + return head; +} + +/* ipmi_sdr_find_sdr_byid - lookup SDR entry by ID string + * + * @intf: ipmi interface + * @id: string to match for sensor name + * + * returns pointer to SDR list + * returns NULL on error + */ +struct sdr_record_list * +ipmi_sdr_find_sdr_byid(struct ipmi_intf *intf, char *id) +{ + struct sdr_get_rs *header; + struct sdr_record_list *e; + int found = 0; + int idlen; + + if (id == NULL) + return NULL; + + idlen = strlen(id); + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return NULL; + } + } + + /* check what we've already read */ + for (e = sdr_list_head; e != NULL; e = e->next) { + switch (e->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + if (!strncmp((const char *)e->record.full->id_string, + (const char *)id, + __max(e->record.full->id_code & 0x1f, idlen))) + return e; + break; + case SDR_RECORD_TYPE_COMPACT_SENSOR: + if (!strncmp((const char *)e->record.compact->id_string, + (const char *)id, + __max(e->record.compact->id_code & 0x1f, idlen))) + return e; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + if (!strncmp((const char *)e->record.eventonly->id_string, + (const char *)id, + __max(e->record.eventonly->id_code & 0x1f, idlen))) + return e; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + if (!strncmp((const char *)e->record.genloc->id_string, + (const char *)id, + __max(e->record.genloc->id_code & 0x1f, idlen))) + return e; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + if (!strncmp((const char *)e->record.fruloc->id_string, + (const char *)id, + __max(e->record.fruloc->id_code & 0x1f, idlen))) + return e; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + if (!strncmp((const char *)e->record.mcloc->id_string, + (const char *)id, + __max(e->record.mcloc->id_code & 0x1f, idlen))) + return e; + break; + } + } + + /* now keep looking */ + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + sdrr->record.full = + (struct sdr_record_full_sensor *) rec; + if (!strncmp( + (const char *)sdrr->record.full->id_string, + (const char *)id, + __max(sdrr->record.full->id_code & 0x1f, idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.compact = + (struct sdr_record_compact_sensor *) rec; + if (!strncmp( + (const char *)sdrr->record.compact->id_string, + (const char *)id, + __max(sdrr->record.compact->id_code & 0x1f, + idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + if (!strncmp( + (const char *)sdrr->record.eventonly->id_string, + (const char *)id, + __max(sdrr->record.eventonly->id_code & 0x1f, + idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + if (!strncmp( + (const char *)sdrr->record.genloc->id_string, + (const char *)id, + __max(sdrr->record.genloc->id_code & 0x1f, idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + if (!strncmp( + (const char *)sdrr->record.fruloc->id_string, + (const char *)id, + __max(sdrr->record.fruloc->id_code & 0x1f, idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + if (!strncmp( + (const char *)sdrr->record.mcloc->id_string, + (const char *)id, + __max(sdrr->record.mcloc->id_code & 0x1f, idlen))) + found = 1; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* add to global record liset */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + + if (found) + return sdrr; + } + + return NULL; +} + +/* ipmi_sdr_list_cache_fromfile - generate SDR cache for fast lookup from local file + * + * @intf: ipmi interface + * @ifile: input filename + * + * returns pointer to SDR list + * returns NULL on error + */ +int +ipmi_sdr_list_cache_fromfile(struct ipmi_intf *intf, const char *ifile) +{ + FILE *fp; + struct __sdr_header { + uint16_t id; + uint8_t version; + uint8_t type; + uint8_t length; + } header; + struct sdr_record_list *sdrr; + uint8_t *rec; + int ret = 0, count = 0, bc = 0; + + if (ifile == NULL) { + lprintf(LOG_ERR, "No SDR cache filename given"); + return -1; + } + + fp = ipmi_open_file_read(ifile); + if (fp == NULL) { + lprintf(LOG_ERR, "Unable to open SDR cache %s for reading", + ifile); + return -1; + } + + while (feof(fp) == 0) { + memset(&header, 0, 5); + bc = fread(&header, 1, 5, fp); + if (bc <= 0) + break; + + if (bc != 5) { + lprintf(LOG_ERR, "header read %d bytes, expected 5", + bc); + ret = -1; + break; + } + + if (header.length == 0) + continue; + + if (header.version != 0x51 && + header.version != 0x01 && + header.version != 0x02) { + lprintf(LOG_WARN, "invalid sdr header version %02x", + header.version); + ret = -1; + break; + } + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + ret = -1; + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + + sdrr->id = header.id; + sdrr->type = header.type; + + rec = malloc(header.length + 1); + if (rec == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + ret = -1; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + break; + } + memset(rec, 0, header.length + 1); + + bc = fread(rec, 1, header.length, fp); + if (bc != header.length) { + lprintf(LOG_ERR, + "record %04x read %d bytes, expected %d", + header.id, bc, header.length); + ret = -1; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + if (rec != NULL) { + free(rec); + rec = NULL; + } + break; + } + + switch (header.type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* add to global record liset */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + + count++; + + lprintf(LOG_DEBUG, "Read record %04x from file into cache", + sdrr->id); + } + + if (sdr_list_itr == NULL) { + sdr_list_itr = malloc(sizeof (struct ipmi_sdr_iterator)); + if (sdr_list_itr != NULL) { + sdr_list_itr->reservation = 0; + sdr_list_itr->total = count; + sdr_list_itr->next = 0xffff; + } + } + + fclose(fp); + return ret; +} + +/* ipmi_sdr_list_cache - generate SDR cache for fast lookup + * + * @intf: ipmi interface + * + * returns pointer to SDR list + * returns NULL on error + */ +int +ipmi_sdr_list_cache(struct ipmi_intf *intf) +{ + struct sdr_get_rs *header; + + if (sdr_list_itr == NULL) { + sdr_list_itr = ipmi_sdr_start(intf, 0); + if (sdr_list_itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return -1; + } + } + + while ((header = ipmi_sdr_get_next_header(intf, sdr_list_itr)) != NULL) { + uint8_t *rec; + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + break; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + sdrr->id = header->id; + sdrr->type = header->type; + + rec = ipmi_sdr_get_record(intf, header, sdr_list_itr); + if (rec == NULL) { + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + sdrr->record.common = + (struct sdr_record_common_sensor *) rec; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + sdrr->record.eventonly = + (struct sdr_record_eventonly_sensor *) rec; + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + sdrr->record.genloc = + (struct sdr_record_generic_locator *) rec; + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + sdrr->record.fruloc = + (struct sdr_record_fru_locator *) rec; + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + sdrr->record.mcloc = + (struct sdr_record_mc_locator *) rec; + break; + case SDR_RECORD_TYPE_ENTITY_ASSOC: + sdrr->record.entassoc = + (struct sdr_record_entity_assoc *) rec; + break; + default: + free(rec); + rec = NULL; + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + continue; + } + + /* add to global record liset */ + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + return 0; +} + +/* + * ipmi_sdr_get_info + * + * Execute the GET SDR REPOSITORY INFO command, and populate the sdr_info + * structure. + * See section 33.9 of the IPMI v2 specification for details + * + * returns 0 on success + * -1 on transport error + * > 0 for other errors + */ +int +ipmi_sdr_get_info(struct ipmi_intf *intf, + struct get_sdr_repository_info_rsp *sdr_repository_info) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof (req)); + + req.msg.netfn = IPMI_NETFN_STORAGE; // 0x0A + req.msg.cmd = IPMI_GET_SDR_REPOSITORY_INFO; // 0x20 + req.msg.data = 0; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SDR Repository Info command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SDR Repository Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(sdr_repository_info, + rsp->data, + __min(sizeof (struct get_sdr_repository_info_rsp), + rsp->data_len)); + + return 0; +} + +/* ipmi_sdr_timestamp - return string from timestamp value + * + * @stamp: 32bit timestamp + * + * returns pointer to static buffer + */ +static char * +ipmi_sdr_timestamp(uint32_t stamp) +{ + static char tbuf[40]; + time_t s = (time_t) stamp; + memset(tbuf, 0, 40); + if (stamp) + strftime(tbuf, sizeof (tbuf), "%m/%d/%Y %H:%M:%S", + gmtime(&s)); + return tbuf; +} + +/* + * ipmi_sdr_print_info + * + * Display the return data of the GET SDR REPOSITORY INFO command + * See section 33.9 of the IPMI v2 specification for details + * + * returns 0 on success + * -1 on error + */ +int +ipmi_sdr_print_info(struct ipmi_intf *intf) +{ + uint32_t timestamp; + uint16_t free_space; + + struct get_sdr_repository_info_rsp sdr_repository_info; + + if (ipmi_sdr_get_info(intf, &sdr_repository_info) != 0) + return -1; + + printf("SDR Version : 0x%x\n", + sdr_repository_info.sdr_version); + printf("Record Count : %d\n", + (sdr_repository_info.record_count_msb << 8) | + sdr_repository_info.record_count_lsb); + + free_space = + (sdr_repository_info.free_space[1] << 8) | + sdr_repository_info.free_space[0]; + + printf("Free Space : "); + switch (free_space) { + case 0x0000: + printf("none (full)\n"); + break; + case 0xFFFF: + printf("unspecified\n"); + break; + case 0xFFFE: + printf("> 64Kb - 2 bytes\n"); + break; + default: + printf("%d bytes\n", free_space); + break; + } + + timestamp = + (sdr_repository_info.most_recent_addition_timestamp[3] << 24) | + (sdr_repository_info.most_recent_addition_timestamp[2] << 16) | + (sdr_repository_info.most_recent_addition_timestamp[1] << 8) | + sdr_repository_info.most_recent_addition_timestamp[0]; + printf("Most recent Addition : %s\n", + ipmi_sdr_timestamp(timestamp)); + + timestamp = + (sdr_repository_info.most_recent_erase_timestamp[3] << 24) | + (sdr_repository_info.most_recent_erase_timestamp[2] << 16) | + (sdr_repository_info.most_recent_erase_timestamp[1] << 8) | + sdr_repository_info.most_recent_erase_timestamp[0]; + printf("Most recent Erase : %s\n", + ipmi_sdr_timestamp(timestamp)); + + printf("SDR overflow : %s\n", + (sdr_repository_info.overflow_flag ? "yes" : "no")); + + printf("SDR Repository Update Support : "); + switch (sdr_repository_info.modal_update_support) { + case 0: + printf("unspecified\n"); + break; + case 1: + printf("non-modal\n"); + break; + case 2: + printf("modal\n"); + break; + case 3: + printf("modal and non-modal\n"); + break; + default: + printf("error in response\n"); + break; + } + + printf("Delete SDR supported : %s\n", + sdr_repository_info.delete_sdr_supported ? "yes" : "no"); + printf("Partial Add SDR supported : %s\n", + sdr_repository_info.partial_add_sdr_supported ? "yes" : "no"); + printf("Reserve SDR repository supported : %s\n", + sdr_repository_info. + reserve_sdr_repository_supported ? "yes" : "no"); + printf("SDR Repository Alloc info supported : %s\n", + sdr_repository_info. + get_sdr_repository_allo_info_supported ? "yes" : "no"); + + return 0; +} + +/* ipmi_sdr_dump_bin - Write raw SDR to binary file + * + * used for post-processing by other utilities + * + * @intf: ipmi interface + * @ofile: output filename + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_sdr_dump_bin(struct ipmi_intf *intf, const char *ofile) +{ + struct sdr_get_rs *header; + struct ipmi_sdr_iterator *itr; + struct sdr_record_list *sdrr; + FILE *fp; + int rc = 0; + + /* open connection to SDR */ + itr = ipmi_sdr_start(intf, 0); + if (itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return -1; + } + + printf("Dumping Sensor Data Repository to '%s'\n", ofile); + + /* generate list of records */ + while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) { + sdrr = malloc(sizeof(struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memset(sdrr, 0, sizeof(struct sdr_record_list)); + + lprintf(LOG_INFO, "Record ID %04x (%d bytes)", + header->id, header->length); + + sdrr->id = header->id; + sdrr->version = header->version; + sdrr->type = header->type; + sdrr->length = header->length; + sdrr->raw = ipmi_sdr_get_record(intf, header, itr); + + if (sdrr->raw == NULL) { + lprintf(LOG_ERR, "ipmitool: cannot obtain SDR record %04x", header->id); + if (sdrr != NULL) { + free(sdrr); + sdrr = NULL; + } + return -1; + } + + if (sdr_list_head == NULL) + sdr_list_head = sdrr; + else + sdr_list_tail->next = sdrr; + + sdr_list_tail = sdrr; + } + + ipmi_sdr_end(intf, itr); + + /* now write to file */ + fp = ipmi_open_file_write(ofile); + if (fp == NULL) + return -1; + + for (sdrr = sdr_list_head; sdrr != NULL; sdrr = sdrr->next) { + int r; + uint8_t h[5]; + + /* build and write sdr header */ + h[0] = sdrr->id & 0xff; // LS Byte first + h[1] = (sdrr->id >> 8) & 0xff; + h[2] = sdrr->version; + h[3] = sdrr->type; + h[4] = sdrr->length; + + r = fwrite(h, 1, 5, fp); + if (r != 5) { + lprintf(LOG_ERR, "Error writing header " + "to output file %s", ofile); + rc = -1; + break; + } + + /* write sdr entry */ + if (!sdrr->raw) { + lprintf(LOG_ERR, "Error: raw data is null (length=%d)", + sdrr->length); + rc = -1; + break; + } + r = fwrite(sdrr->raw, 1, sdrr->length, fp); + if (r != sdrr->length) { + lprintf(LOG_ERR, "Error writing %d record bytes " + "to output file %s", sdrr->length, ofile); + rc = -1; + break; + } + } + fclose(fp); + + return rc; +} + +/* ipmi_sdr_print_type - print all sensors of specified type + * + * @intf: ipmi interface + * @type: sensor type + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_type(struct ipmi_intf *intf, char *type) +{ + struct sdr_record_list *list, *entry; + int rc = 0; + int x; + uint8_t sensor_type = 0; + + if (type == NULL || + strncasecmp(type, "help", 4) == 0 || + strncasecmp(type, "list", 4) == 0) { + printf("Sensor Types:\n"); + for (x = 1; x < SENSOR_TYPE_MAX; x += 2) { + printf("\t%-25s (0x%02x) %-25s (0x%02x)\n", + sensor_type_desc[x], x, + sensor_type_desc[x + 1], x + 1); + } + return 0; + } + + if (strncmp(type, "0x", 2) == 0) { + /* begins with 0x so let it be entered as raw hex value */ + if (str2uchar(type, &sensor_type) != 0) { + lprintf(LOG_ERR, + "Given type of sensor \"%s\" is either invalid or out of range.", + type); + return (-1); + } + } else { + for (x = 1; x < SENSOR_TYPE_MAX; x++) { + if (strncasecmp(sensor_type_desc[x], type, + __maxlen(type, + sensor_type_desc[x])) == 0) { + sensor_type = x; + break; + } + } + if (sensor_type != x) { + lprintf(LOG_ERR, "Sensor Type \"%s\" not found.", + type); + printf("Sensor Types:\n"); + for (x = 1; x < SENSOR_TYPE_MAX; x += 2) { + printf("\t%-25s (0x%02x) %-25s (0x%02x)\n", + sensor_type_desc[x], x, + sensor_type_desc[x + 1], x + 1); + } + return 0; + } + } + + list = ipmi_sdr_find_sdr_bysensortype(intf, sensor_type); + + for (entry = list; entry != NULL; entry = entry->next) { + rc = ipmi_sdr_print_listentry(intf, entry); + } + + __sdr_list_empty(list); + + return rc; +} + +/* ipmi_sdr_print_entity - print entity's for an id/instance + * + * @intf: ipmi interface + * @entitystr: entity id/instance string, i.e. "1.1" + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_print_entity(struct ipmi_intf *intf, char *entitystr) +{ + struct sdr_record_list *list, *entry; + struct entity_id entity; + unsigned id = 0; + unsigned instance = 0; + int rc = 0; + + if (entitystr == NULL || + strncasecmp(entitystr, "help", 4) == 0 || + strncasecmp(entitystr, "list", 4) == 0) { + print_valstr_2col(entity_id_vals, "Entity IDs", -1); + return 0; + } + + if (sscanf(entitystr, "%u.%u", &id, &instance) != 2) { + /* perhaps no instance was passed + * in which case we want all instances for this entity + * so set entity.instance = 0x7f to indicate this + */ + if (sscanf(entitystr, "%u", &id) != 1) { + int i, j=0; + + /* now try string input */ + for (i = 0; entity_id_vals[i].str != NULL; i++) { + if (strncasecmp(entitystr, entity_id_vals[i].str, + __maxlen(entitystr, entity_id_vals[i].str)) == 0) { + entity.id = entity_id_vals[i].val; + entity.instance = 0x7f; + j=1; + } + } + if (j == 0) { + lprintf(LOG_ERR, "Invalid entity: %s", entitystr); + return -1; + } + } else { + entity.id = id; + entity.instance = 0x7f; + } + } else { + entity.id = id; + entity.instance = instance; + } + + list = ipmi_sdr_find_sdr_byentity(intf, &entity); + + for (entry = list; entry != NULL; entry = entry->next) { + rc = ipmi_sdr_print_listentry(intf, entry); + } + + __sdr_list_empty(list); + + return rc; +} + +/* ipmi_sdr_print_entry_byid - print sdr entries identified by sensor id + * + * @intf: ipmi interface + * @argc: number of entries to print + * @argv: list of sensor ids + * + * returns 0 on success + * returns -1 on error + */ +static int +ipmi_sdr_print_entry_byid(struct ipmi_intf *intf, int argc, char **argv) +{ + struct sdr_record_list *sdr; + int rc = 0; + int v, i; + + if (argc < 1) { + lprintf(LOG_ERR, "No Sensor ID supplied"); + return -1; + } + + v = verbose; + verbose = 1; + + for (i = 0; i < argc; i++) { + sdr = ipmi_sdr_find_sdr_byid(intf, argv[i]); + if (sdr == NULL) { + lprintf(LOG_ERR, "Unable to find sensor id '%s'", + argv[i]); + } else { + if (ipmi_sdr_print_listentry(intf, sdr) < 0) + rc = -1; + } + } + + verbose = v; + + return rc; +} + +/* ipmi_sdr_main - top-level handler for SDR subsystem + * + * @intf: ipmi interface + * @argc: number of arguments + * @argv: argument list + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_sdr_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = 0; + + /* initialize random numbers used later */ + srand(time(NULL)); + + if (argc == 0) + return ipmi_sdr_print_sdr(intf, 0xfe); + else if (strncmp(argv[0], "help", 4) == 0) { + printf_sdr_usage(); + } else if (strncmp(argv[0], "list", 4) == 0 + || strncmp(argv[0], "elist", 5) == 0) { + + if (strncmp(argv[0], "elist", 5) == 0) + sdr_extended = 1; + else + sdr_extended = 0; + + if (argc <= 1) + rc = ipmi_sdr_print_sdr(intf, 0xfe); + else if (strncmp(argv[1], "all", 3) == 0) + rc = ipmi_sdr_print_sdr(intf, 0xff); + else if (strncmp(argv[1], "full", 4) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_FULL_SENSOR); + else if (strncmp(argv[1], "compact", 7) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_COMPACT_SENSOR); + else if (strncmp(argv[1], "event", 5) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_EVENTONLY_SENSOR); + else if (strncmp(argv[1], "mcloc", 5) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_MC_DEVICE_LOCATOR); + else if (strncmp(argv[1], "fru", 3) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR); + else if (strncmp(argv[1], "generic", 7) == 0) + rc = ipmi_sdr_print_sdr(intf, + SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + else if (strcmp(argv[1], "help") == 0) { + lprintf(LOG_NOTICE, + "usage: sdr %s [all|full|compact|event|mcloc|fru|generic]", + argv[0]); + return 0; + } + else { + lprintf(LOG_ERR, + "Invalid SDR %s command: %s", + argv[0], argv[1]); + lprintf(LOG_NOTICE, + "usage: sdr %s [all|full|compact|event|mcloc|fru|generic]", + argv[0]); + return (-1); + } + } else if (strncmp(argv[0], "type", 4) == 0) { + sdr_extended = 1; + rc = ipmi_sdr_print_type(intf, argv[1]); + } else if (strncmp(argv[0], "entity", 6) == 0) { + sdr_extended = 1; + rc = ipmi_sdr_print_entity(intf, argv[1]); + } else if (strncmp(argv[0], "info", 4) == 0) { + rc = ipmi_sdr_print_info(intf); + } else if (strncmp(argv[0], "get", 3) == 0) { + rc = ipmi_sdr_print_entry_byid(intf, argc - 1, &argv[1]); + } else if (strncmp(argv[0], "dump", 4) == 0) { + if (argc < 2) { + lprintf(LOG_ERR, "Not enough parameters given."); + lprintf(LOG_NOTICE, "usage: sdr dump <file>"); + return (-1); + } + rc = ipmi_sdr_dump_bin(intf, argv[1]); + } else if (strncmp(argv[0], "fill", 4) == 0) { + if (argc <= 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + lprintf(LOG_NOTICE, "usage: sdr fill sensors"); + lprintf(LOG_NOTICE, "usage: sdr fill file <file>"); + lprintf(LOG_NOTICE, "usage: sdr fill range <range>"); + return (-1); + } else if (strncmp(argv[1], "sensors", 7) == 0) { + rc = ipmi_sdr_add_from_sensors(intf, 21); + } else if (strncmp(argv[1], "nosat", 5) == 0) { + rc = ipmi_sdr_add_from_sensors(intf, 0); + } else if (strncmp(argv[1], "file", 4) == 0) { + if (argc < 3) { + lprintf(LOG_ERR, + "Not enough parameters given."); + lprintf(LOG_NOTICE, + "usage: sdr fill file <file>"); + return (-1); + } + rc = ipmi_sdr_add_from_file(intf, argv[2]); + } else if (strncmp(argv[1], "range", 4) == 0) { + if (argc < 3) { + lprintf(LOG_ERR, + "Not enough parameters given."); + lprintf(LOG_NOTICE, + "usage: sdr fill range <range>"); + return (-1); + } + rc = ipmi_sdr_add_from_list(intf, argv[2]); + } else { + lprintf(LOG_ERR, + "Invalid SDR %s command: %s", + argv[0], argv[1]); + lprintf(LOG_NOTICE, + "usage: sdr %s <sensors|nosat|file|range> [options]", + argv[0]); + return (-1); + } + } else { + lprintf(LOG_ERR, "Invalid SDR command: %s", argv[0]); + rc = -1; + } + + return rc; +} + +void +printf_sdr_usage() +{ + lprintf(LOG_NOTICE, +"usage: sdr <command> [options]"); + lprintf(LOG_NOTICE, +" list | elist [option]"); + lprintf(LOG_NOTICE, +" all All SDR Records"); + lprintf(LOG_NOTICE, +" full Full Sensor Record"); + lprintf(LOG_NOTICE, +" compact Compact Sensor Record"); + lprintf(LOG_NOTICE, +" event Event-Only Sensor Record"); + lprintf(LOG_NOTICE, +" mcloc Management Controller Locator Record"); + lprintf(LOG_NOTICE, +" fru FRU Locator Record"); + lprintf(LOG_NOTICE, +" generic Generic Device Locator Record\n"); + lprintf(LOG_NOTICE, +" type [option]"); + lprintf(LOG_NOTICE, +" <Sensor_Type> Retrieve the state of specified sensor."); + lprintf(LOG_NOTICE, +" Sensor_Type can be specified either as"); + lprintf(LOG_NOTICE, +" a string or a hex value."); + lprintf(LOG_NOTICE, +" list Get a list of available sensor types\n"); + lprintf(LOG_NOTICE, +" get <Sensor_ID>"); + lprintf(LOG_NOTICE, +" Retrieve state of the first sensor matched by Sensor_ID\n"); + lprintf(LOG_NOTICE, +" info"); + lprintf(LOG_NOTICE, +" Display information about the repository itself\n"); + lprintf(LOG_NOTICE, +" entity <Entity_ID>[.<Instance_ID>]"); + lprintf(LOG_NOTICE, +" Display all sensors associated with an entity\n"); + lprintf(LOG_NOTICE, +" dump <file>"); + lprintf(LOG_NOTICE, +" Dump raw SDR data to a file\n"); + lprintf(LOG_NOTICE, +" fill <option>"); + lprintf(LOG_NOTICE, +" sensors Creates the SDR repository for the current"); + lprintf(LOG_NOTICE, +" configuration"); + lprintf(LOG_NOTICE, +" nosat Creates the SDR repository for the current"); + lprintf(LOG_NOTICE, +" configuration, without satellite scan"); + lprintf(LOG_NOTICE, +" file <file> Load SDR repository from a file"); + lprintf(LOG_NOTICE, +" range <range> Load SDR repository from a provided list"); + lprintf(LOG_NOTICE, +" or range. Use ',' for list or '-' for"); + lprintf(LOG_NOTICE, +" range, eg. 0x28,0x32,0x40-0x44"); +} diff --git a/lib/ipmi_sdradd.c b/lib/ipmi_sdradd.c new file mode 100644 index 0000000..f3bc271 --- /dev/null +++ b/lib/ipmi_sdradd.c @@ -0,0 +1,668 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Functions to program the SDR repository, from built-in sensors or + * from sensors dumped in a binary file. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <fcntl.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/bswap.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_strings.h> + +#include <ipmitool/ipmi_sdr.h> + + +#define ADD_PARTIAL_SDR 0x25 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +struct sdr_add_rq { + uint16_t reserve_id; /* reservation ID */ + uint16_t id; /* record ID */ + uint8_t offset; /* offset into SDR */ + uint8_t in_progress; /* 0=partial, 1=last */ +#define PARTIAL_ADD (0) +#define LAST_RECORD (1) + uint8_t data[1]; /* SDR record data */ +} ATTRIBUTE_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +/* This was formerly initialized to 24, reduced this to 19 so the overall + message fits into the recommended 32-byte limit */ +static int sdr_max_write_len = 19; +int ipmi_parse_range_list(const char *rangeList, unsigned char *pHexList); +int ipmi_hex_to_dec( char * rangeList, unsigned char * pDecValue); + +static int +partial_send(struct ipmi_intf *intf, struct ipmi_rq *req, uint16_t *id) +{ + struct ipmi_rs *rsp; + rsp = intf->sendrecv(intf, req); + if (rsp == NULL) { + return -1; + } + + if (rsp->ccode || rsp->data_len < 2) { + return -1; + } + + *id = rsp->data[0] + (rsp->data[1] << 8); + return 0; +} + +int +ipmi_sdr_add_record(struct ipmi_intf *intf, struct sdr_record_list *sdrr) +{ + struct ipmi_rq req; + struct sdr_add_rq *sdr_rq; + uint16_t reserve_id; + uint16_t id; + int i; + int len = sdrr->length; + int rc = 0; + + /* actually no SDR to program */ + if (len < 1 || !sdrr->raw) { + lprintf(LOG_ERR, "ipmitool: bad record , skipped"); + return 0; + } + + if (ipmi_sdr_get_reservation(intf, 0, &reserve_id)) { + lprintf(LOG_ERR, "ipmitool: reservation failed"); + return -1; + } + + sdr_rq = (struct sdr_add_rq *)malloc(sizeof(*sdr_rq) + sdr_max_write_len); + if (sdr_rq == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + sdr_rq->reserve_id = reserve_id; + sdr_rq->in_progress = PARTIAL_ADD; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = ADD_PARTIAL_SDR; + req.msg.data = (uint8_t *) sdr_rq; + + /* header first */ + sdr_rq->id = 0; + sdr_rq->offset = 0; + sdr_rq->data[0] = sdrr->id & 0xFF; + sdr_rq->data[1] = (sdrr->id >> 8) & 0xFF; + sdr_rq->data[2] = sdrr->version; + sdr_rq->data[3] = sdrr->type; + sdr_rq->data[4] = sdrr->length; + req.msg.data_len = 5 + sizeof(*sdr_rq) - 1; + + if (partial_send(intf, &req, &id)) { + lprintf(LOG_ERR, "ipmitool: partial send error"); + free(sdr_rq); + sdr_rq = NULL; + return -1; + } + + i = 0; + + /* sdr entry */ + while (i < len) { + int data_len = 0; + if ( (len - i) <= sdr_max_write_len) { + /* last crunch */ + data_len = len - i; + sdr_rq->in_progress = LAST_RECORD; + } else { + data_len = sdr_max_write_len; + } + + sdr_rq->id = id; + sdr_rq->offset = i + 5; + memcpy(sdr_rq->data, sdrr->raw + i, data_len); + req.msg.data_len = data_len + sizeof(*sdr_rq) - 1; + + if ((rc = partial_send(intf, &req, &id)) != 0) { + lprintf(LOG_ERR, "ipmitool: partial add failed"); + break; + } + + i += data_len; + } + + free(sdr_rq); + sdr_rq = NULL; + return rc; +} + +static int +ipmi_sdr_repo_clear(struct ipmi_intf *intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[8]; + uint16_t reserve_id; + int try; + + if (ipmi_sdr_get_reservation(intf, 0, &reserve_id)) + return -1; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = 0x27; // FIXME + req.msg.data = msg_data; + req.msg.data_len = 6; + + msg_data[0] = reserve_id & 0xFF; + msg_data[1] = reserve_id >> 8; + msg_data[2] = 'C'; + msg_data[3] = 'L'; + msg_data[4] = 'R'; + msg_data[5] = 0xAA; + + for (try = 0; try < 5; try++) { + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to clear SDRR"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to clear SDRR: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if ((rsp->data[0] & 1) == 1) { + printf("SDRR successfully erased\n"); + return 0; + } + printf("Wait for SDRR erasure completed...\n"); + msg_data[5] = 0; + sleep(1); + } + + /* if we are here we fed up trying erase */ + return -1; +} + + +struct sdrr_queue { + struct sdr_record_list *head; + struct sdr_record_list *tail; +}; + + +/* + * Fill the SDR repository from built-in sensors + * + */ + +/* + * Get all the SDR records stored in <queue> + */ +static int +sdrr_get_records(struct ipmi_intf *intf, struct ipmi_sdr_iterator *itr, + struct sdrr_queue *queue) +{ + struct sdr_get_rs *header; + + queue->head = NULL; + queue->tail = NULL; + + while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) { + struct sdr_record_list *sdrr; + + sdrr = malloc(sizeof (struct sdr_record_list)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memset(sdrr, 0, sizeof (struct sdr_record_list)); + + sdrr->id = header->id; + sdrr->version = header->version; + sdrr->type = header->type; + sdrr->length = header->length; + sdrr->raw = ipmi_sdr_get_record(intf, header, itr); + (void)ipmi_sdr_print_name_from_rawentry(intf, sdrr->id, sdrr->type,sdrr->raw); + + /* put in the record queue */ + if (queue->head == NULL) + queue->head = sdrr; + else + queue->tail->next = sdrr; + queue->tail = sdrr; + } + return 0; +} + +static int +sdr_copy_to_sdrr(struct ipmi_intf *intf, int use_builtin, + int from_addr, int to_addr) +{ + int rc; + struct sdrr_queue sdrr_queue; + struct ipmi_sdr_iterator *itr; + struct sdr_record_list *sdrr; + struct sdr_record_list *sdrr_next; + + /* generate list of records for this target */ + intf->target_addr = from_addr; + + /* initialize iterator */ + itr = ipmi_sdr_start(intf, use_builtin); + if (itr == 0) + return 0; + + printf("Load SDRs from 0x%x\n", from_addr); + rc = sdrr_get_records(intf, itr, &sdrr_queue); + ipmi_sdr_end(intf, itr); + /* ... */ + + /* write the SDRs to the destination SDR Repository */ + intf->target_addr = to_addr; + for (sdrr = sdrr_queue.head; sdrr != NULL; sdrr = sdrr_next) { + sdrr_next = sdrr->next; + rc = ipmi_sdr_add_record(intf, sdrr); + if(rc < 0){ + lprintf(LOG_ERR, "Cannot add SDR ID 0x%04x to repository...", sdrr->id); + } + free(sdrr); + sdrr = NULL; + } + return rc; +} + +int +ipmi_sdr_add_from_sensors(struct ipmi_intf *intf, int maxslot) +{ + int i; + int rc = 0; + int slave_addr; + int myaddr = intf->target_addr; + + if (ipmi_sdr_repo_clear(intf)) { + lprintf(LOG_ERR, "Cannot erase SDRR. Give up."); + return -1; + } + + /* First fill the SDRR from local built-in sensors */ + rc = sdr_copy_to_sdrr(intf, 1, myaddr, myaddr); + + /* Now fill the SDRR with remote sensors */ + if( maxslot != 0 ) { + for (i = 0, slave_addr = 0xB0; i < maxslot; i++, slave_addr += 2) { + /* Hole in the PICMG 2.9 mapping */ + if (slave_addr == 0xC2) slave_addr += 2; + if(sdr_copy_to_sdrr(intf, 0, slave_addr, myaddr) < 0) + { + rc = -1; + } + } + } + return rc; +} + +int ipmi_hex_to_dec( char * strchar, unsigned char * pDecValue) +{ + int rc = -1; + unsigned char retValue = 0; + + if( + (strlen(strchar) == 4) + && + (strchar[0] == '0') + && + (strchar[1] == 'x') + ) + { + rc = 0; + + if((strchar[2] >= '0') && (strchar[2] <= '9')) + { + retValue += ((strchar[2]-'0') * 16); + } + else if((strchar[2] >= 'a') && (strchar[2] <= 'f')) + { + retValue += (((strchar[2]-'a') + 10) * 16); + } + else if((strchar[2] >= 'A') && (strchar[2] <= 'F')) + { + retValue += (((strchar[2]-'A') + 10) * 16); + } + else + { + rc = -1; + } + + if((strchar[3] >= '0') && (strchar[3] <= '9')) + { + retValue += ((strchar[3]-'0')); + } + else if((strchar[3] >= 'a') && (strchar[3] <= 'f')) + { + retValue += (((strchar[3]-'a') + 10)); + } + else if((strchar[3] >= 'A') && (strchar[3] <= 'F')) + { + retValue += (((strchar[3]-'A') + 10)); + } + else + { + rc = -1; + } + } + + if(rc == 0) + { + * pDecValue = retValue; + } + else + { + lprintf(LOG_ERR, "Must be Hex value of 4 characters (Ex.: 0x24)"); + } + + return rc; +} + + + +#define MAX_NUM_SLOT 128 +int ipmi_parse_range_list(const char *rangeList, unsigned char * pHexList) +{ + int rc = -1; + + unsigned char listOffset = 0; + char * nextString; + char * rangeString; + char * inProcessString = (char *) rangeList; + + /* Discard empty string */ + if(strlen(rangeList) == 0) + { + return rc; + } + + /* First, cut to comma separated string */ + nextString = strstr( rangeList, "," ); + + if(nextString != rangeList) + { + unsigned char isLast; + /* We get a valid string so far */ + rc = 0; + + do + { + if(nextString != NULL) + { + (*nextString)= 0; + nextString ++; + isLast = 0; + } + else + { + isLast = 1; + } + + /* At this point, it is a single entry or a range */ + rangeString = strstr( inProcessString, "-" ); + if(rangeString == NULL) + { + unsigned char decValue = 0; + + /* Single entry */ + rc = ipmi_hex_to_dec( inProcessString, &decValue); + + if(rc == 0) + { + if((decValue % 2) == 0) + { + pHexList[listOffset++] = decValue; + } + else + { + lprintf(LOG_ERR, "I2C address provided value must be even."); + } + } + } + else + { + unsigned char startValue = 0; + unsigned char endValue = 0; + + + (*rangeString)= 0; /* Cut string*/ + rangeString ++; + + /* Range */ + rc = ipmi_hex_to_dec( inProcessString, &startValue); + if(rc == 0) + rc = ipmi_hex_to_dec( rangeString, &endValue); + + if(rc == 0) + { + if(((startValue % 2) == 0) && ((endValue % 2) == 0)) + { + do + { + pHexList[listOffset++] = startValue; + startValue += 2; + } + while(startValue != endValue); + pHexList[listOffset++] = endValue; + } + else + { + lprintf(LOG_ERR, "I2C address provided value must be even."); + } + } + } + + if(isLast == 0) + { + /* Setup for next string */ + inProcessString = nextString; + nextString = strstr( rangeList, "," ); + } + }while ((isLast == 0) && (rc == 0)); + } + + return rc; +} + +int +ipmi_sdr_add_from_list(struct ipmi_intf *intf, const char *rangeList) +{ + int i; + int rc = 0; + int slave_addr; + int myaddr = intf->target_addr; + unsigned char listValue[MAX_NUM_SLOT]; + + memset( listValue, 0, MAX_NUM_SLOT ); + + /* Build list from string */ + if(ipmi_parse_range_list(rangeList, listValue) != 0) + { + lprintf(LOG_ERR, "Range - List invalid, cannot be parsed."); + return -1; + } + + { + unsigned char counter = 0; + printf("List to scan: (Built-in) "); + while(listValue[counter] != 0) + { + printf("%02x ", listValue[counter]); + counter++; + } + printf("\n"); + } + + printf("Clearing SDR Repository\n"); + if (ipmi_sdr_repo_clear(intf)) { + lprintf(LOG_ERR, "Cannot erase SDRR. Give up."); + return -1; + } + + /* First fill the SDRR from local built-in sensors */ + printf("Sanning built-in sensors..\n"); + rc = sdr_copy_to_sdrr(intf, 1, myaddr, myaddr); + + /* Now fill the SDRR with provided sensors list */ + { + unsigned char counter = 0; + while((rc == 0) && (listValue[counter] != 0)) + { + slave_addr = listValue[counter]; + printf("Scanning %02Xh..\n", slave_addr); + if(sdr_copy_to_sdrr(intf, 0, slave_addr, myaddr) < 0) + { + rc = -1; + } + counter++; + } + } + + return rc; +} + + +/* + * Fill the SDR repository from records stored in a binary file + * + */ + +static int +ipmi_sdr_read_records(const char *filename, struct sdrr_queue *queue) +{ + struct sdr_get_rs header; + int rc = 0; + int fd; + uint8_t binHdr[5]; + + queue->head = NULL; + queue->tail = NULL; + + if ((fd = open(filename, O_RDONLY)) < 0) { + return -1; + } + + while (read(fd, binHdr, 5) == 5) { + + struct sdr_record_list *sdrr; + + lprintf(LOG_DEBUG, "binHdr[0] (id[MSB]) = 0x%02x", binHdr[0]); + lprintf(LOG_DEBUG, "binHdr[1] (id[LSB]) = 0x%02x", binHdr[1]); + lprintf(LOG_DEBUG, "binHdr[2] (version) = 0x%02x", binHdr[2]); + lprintf(LOG_DEBUG, "binHdr[3] (type) = 0x%02x", binHdr[3]); + lprintf(LOG_DEBUG, "binHdr[4] (length) = 0x%02x", binHdr[4]); + + sdrr = malloc(sizeof(*sdrr)); + if (sdrr == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + rc = -1; + break; + } + sdrr->id = (binHdr[1] << 8) | binHdr[0]; // LS Byte first + sdrr->version = binHdr[2]; + sdrr->type = binHdr[3]; + sdrr->length = binHdr[4]; + + if ((sdrr->raw = malloc(sdrr->length)) == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + free(sdrr); + sdrr = NULL; + rc = -1; + break; + } + + if (read(fd, sdrr->raw, sdrr->length) != sdrr->length) { + lprintf(LOG_ERR, "SDR from '%s' truncated", filename); + free(sdrr->raw); + sdrr->raw = NULL; + free(sdrr); + sdrr = NULL; + rc = -1; + break; + } + + /* put in the record queue */ + if (queue->head == NULL) + queue->head = sdrr; + else + queue->tail->next = sdrr; + queue->tail = sdrr; + } + return rc; +} + +int +ipmi_sdr_add_from_file(struct ipmi_intf *intf, const char *ifile) +{ + int rc; + struct sdrr_queue sdrr_queue; + struct sdr_record_list *sdrr; + struct sdr_record_list *sdrr_next; + + /* read the SDR records from file */ + rc = ipmi_sdr_read_records(ifile, &sdrr_queue); + + if (ipmi_sdr_repo_clear(intf)) { + lprintf(LOG_ERR, "Cannot erase SDRR. Giving up."); + /* FIXME: free sdr list */ + return -1; + } + + /* write the SDRs to the SDR Repository */ + for (sdrr = sdrr_queue.head; sdrr != NULL; sdrr = sdrr_next) { + sdrr_next = sdrr->next; + rc = ipmi_sdr_add_record(intf, sdrr); + if(rc < 0){ + lprintf(LOG_ERR, "Cannot add SDR ID 0x%04x to repository...", sdrr->id); + } + free(sdrr); + sdrr = NULL; + } + return rc; +} + diff --git a/lib/ipmi_sel.c b/lib/ipmi_sel.c new file mode 100644 index 0000000..21ce0c4 --- /dev/null +++ b/lib/ipmi_sel.c @@ -0,0 +1,3094 @@ +/* -*-mode: C; indent-tabs-mode: t; -*- + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <math.h> +#define __USE_XOPEN /* glibc2 needs this for strptime */ +#include <time.h> +#include <ctype.h> +#include <errno.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_sensor.h> + +extern int verbose; +static int sel_extended = 0; +static int sel_oem_nrecs = 0; + +static IPMI_OEM sel_iana = IPMI_OEM_UNKNOWN; + +struct ipmi_sel_oem_msg_rec { + int value[14]; + char *string[14]; + char *text; +} *sel_oem_msg; + +#define SEL_BYTE(n) (n-3) /* So we can refer to byte positions in log entries (byte 3 is at index 0, etc) */ + +// Definiation for the Decoding the SEL OEM Bytes for DELL Platfoms +#define BIT(x) (1 << x) /* Select the Bit */ +#define SIZE_OF_DESC 128 /* Max Size of the description String to be displyed for the Each sel entry */ +#define MAX_CARDNO_STR 32 /* Max Size of Card number string */ +#define MAX_DIMM_STR 32 /* Max Size of DIMM string */ +#define MAX_CARD_STR 32 /* Max Size of Card string */ +/* + * Reads values found in message translation file. XX is a wildcard, R means reserved. + * Returns -1 for XX, -2 for R, -3 for non-hex (string), or positive integer from a hex value. + */ +static int ipmi_sel_oem_readval(char *str) +{ + int ret; + if (!strcmp(str, "XX")) { + return -1; + } + if (!strcmp(str, "R")) { + return -2; + } + if (sscanf(str, "0x%x", &ret) != 1) { + return -3; + } + return ret; +} + +/* + * This is where the magic happens. SEL_BYTE is a bit ugly, but it allows + * reference to byte positions instead of array indexes which (hopefully) + * helps make the code easier to read. + */ +static int ipmi_sel_oem_match(uint8_t *evt, struct ipmi_sel_oem_msg_rec rec) +{ + if (evt[2] == rec.value[SEL_BYTE(3)] && + ((rec.value[SEL_BYTE(4)] < 0) || (evt[3] == rec.value[SEL_BYTE(4)])) && + ((rec.value[SEL_BYTE(5)] < 0) || (evt[4] == rec.value[SEL_BYTE(5)])) && + ((rec.value[SEL_BYTE(6)] < 0) || (evt[5] == rec.value[SEL_BYTE(6)])) && + ((rec.value[SEL_BYTE(7)] < 0) || (evt[6] == rec.value[SEL_BYTE(7)])) && + ((rec.value[SEL_BYTE(11)] < 0) || (evt[10] == rec.value[SEL_BYTE(11)])) && + ((rec.value[SEL_BYTE(12)] < 0) || (evt[11] == rec.value[SEL_BYTE(12)]))) { + return 1; + } else { + return 0; + } +} + +int ipmi_sel_oem_init(const char * filename) +{ + FILE * fp; + int i, j, k, n, byte; + char buf[15][150]; + + if (filename == NULL) { + lprintf(LOG_ERR, "No SEL OEM filename provided"); + return -1; + } + + fp = ipmi_open_file_read(filename); + if (fp == NULL) { + lprintf(LOG_ERR, "Could not open %s file", filename); + return -1; + } + + /* count number of records (lines) in input file */ + sel_oem_nrecs = 0; + while (fscanf(fp, "%*[^\n]\n") == 0) { + sel_oem_nrecs++; + } + + printf("nrecs=%d\n", sel_oem_nrecs); + + rewind(fp); + sel_oem_msg = (struct ipmi_sel_oem_msg_rec *)calloc(sel_oem_nrecs, + sizeof(struct ipmi_sel_oem_msg_rec)); + + for (i=0; i < sel_oem_nrecs; i++) { + n=fscanf(fp, "\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" + "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" + "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" + "%[^\"]\",\"%[^\"]\",\"%[^\"]\"\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], + buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14]); + + if (n != 15) { + lprintf (LOG_ERR, "Encountered problems reading line %d of %s", + i+1, filename); + fclose(fp); + fp = NULL; + sel_oem_nrecs = 0; + /* free all the memory allocated so far */ + for (j=0; j<i ; j++) { + for (k=3; k<17; k++) { + if (sel_oem_msg[j].value[SEL_BYTE(k)] == -3) { + free(sel_oem_msg[j].string[SEL_BYTE(k)]); + sel_oem_msg[j].string[SEL_BYTE(k)] = NULL; + } + } + } + free(sel_oem_msg); + sel_oem_msg = NULL; + return -1; + } + + for (byte = 3; byte < 17; byte++) { + if ((sel_oem_msg[i].value[SEL_BYTE(byte)] = + ipmi_sel_oem_readval(buf[SEL_BYTE(byte)])) == -3) { + sel_oem_msg[i].string[SEL_BYTE(byte)] = + (char *)malloc(strlen(buf[SEL_BYTE(byte)]) + 1); + strcpy(sel_oem_msg[i].string[SEL_BYTE(byte)], + buf[SEL_BYTE(byte)]); + } + } + sel_oem_msg[i].text = (char *)malloc(strlen(buf[SEL_BYTE(17)]) + 1); + strcpy(sel_oem_msg[i].text, buf[SEL_BYTE(17)]); + } + + fclose(fp); + fp = NULL; + return 0; +} + +static void ipmi_sel_oem_message(struct sel_event_record * evt, int verbose) +{ + /* + * Note: although we have a verbose argument, currently the output + * isn't affected by it. + */ + int i, j; + + for (i=0; i < sel_oem_nrecs; i++) { + if (ipmi_sel_oem_match((uint8_t *)evt, sel_oem_msg[i])) { + printf (csv_output ? ",\"%s\"" : " | %s", sel_oem_msg[i].text); + for (j=4; j<17; j++) { + if (sel_oem_msg[i].value[SEL_BYTE(j)] == -3) { + printf (csv_output ? ",%s=0x%x" : " %s = 0x%x", + sel_oem_msg[i].string[SEL_BYTE(j)], + ((uint8_t *)evt)[SEL_BYTE(j)]); + } + } + } + } +} + +static const struct valstr event_dir_vals[] = { + { 0, "Assertion Event" }, + { 1, "Deassertion Event" }, + { 0, NULL }, +}; + +static const char * +ipmi_get_event_type(uint8_t code) +{ + if (code == 0) + return "Unspecified"; + if (code == 1) + return "Threshold"; + if (code >= 0x02 && code <= 0x0b) + return "Generic Discrete"; + if (code == 0x6f) + return "Sensor-specific Discrete"; + if (code >= 0x70 && code <= 0x7f) + return "OEM"; + return "Reserved"; +} + +static char * +ipmi_sel_timestamp(uint32_t stamp) +{ + static char tbuf[40]; + time_t s = (time_t)stamp; + memset(tbuf, 0, 40); + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&s)); + return tbuf; +} + +static char * +ipmi_sel_timestamp_date(uint32_t stamp) +{ + static char tbuf[11]; + time_t s = (time_t)stamp; + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y", gmtime(&s)); + return tbuf; +} + +static char * +ipmi_sel_timestamp_time(uint32_t stamp) +{ + static char tbuf[9]; + time_t s = (time_t)stamp; + strftime(tbuf, sizeof(tbuf), "%H:%M:%S", gmtime(&s)); + return tbuf; +} + +static char * +hex2ascii (uint8_t * hexChars, uint8_t numBytes) +{ + int count; + static char hexString[SEL_OEM_NOTS_DATA_LEN+1]; /*Max Size*/ + + if(numBytes > SEL_OEM_NOTS_DATA_LEN) + numBytes = SEL_OEM_NOTS_DATA_LEN; + + for(count=0;count < numBytes;count++) + { + if((hexChars[count]<0x40)||(hexChars[count]>0x7e)) + hexString[count]='.'; + else + hexString[count]=hexChars[count]; + } + hexString[numBytes]='\0'; + return hexString; +} + +IPMI_OEM +ipmi_get_oem(struct ipmi_intf * intf) +{ + /* Execute a Get Device ID command to determine the OEM */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct ipm_devid_rsp *devid; + + if (intf->fd == 0) { + if( sel_iana != IPMI_OEM_UNKNOWN ){ + return sel_iana; + } + return IPMI_OEM_UNKNOWN; + } + + /* + * Return the cached manufacturer id if the device is open and + * we got an identified OEM owner. Otherwise just attempt to read + * it. + */ + if (intf->opened && intf->manufacturer_id != IPMI_OEM_UNKNOWN) { + return intf->manufacturer_id; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get Device ID command failed"); + return IPMI_OEM_UNKNOWN; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get Device ID command failed: %#x %s", + rsp->ccode, val2str(rsp->ccode, completion_code_vals)); + return IPMI_OEM_UNKNOWN; + } + + devid = (struct ipm_devid_rsp *) rsp->data; + + lprintf(LOG_DEBUG,"Iana: %u", + IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id)); + + return IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id); +} + +static int +ipmi_sel_add_entry(struct ipmi_intf * intf, struct sel_event_record * rec) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_ADD_SEL_ENTRY; + req.msg.data = (unsigned char *)rec; + req.msg.data_len = 16; + + ipmi_sel_print_std_entry(intf, rec); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Add SEL Entry failed"); + return -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Add SEL Entry failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + + +static int +ipmi_sel_add_entries_fromfile(struct ipmi_intf * intf, const char * filename) +{ + FILE * fp; + char buf[1024]; + char * ptr, * tok; + int i, j; + int rc = 0; + uint8_t rqdata[8]; + struct sel_event_record sel_event; + + if (filename == NULL) + return -1; + + fp = ipmi_open_file_read(filename); + if (fp == NULL) + return -1; + + while (feof(fp) == 0) { + if (fgets(buf, 1024, fp) == NULL) + continue; + + /* clip off optional comment tail indicated by # */ + ptr = strchr(buf, '#'); + if (ptr) + *ptr = '\0'; + else + ptr = buf + strlen(buf); + + /* clip off trailing and leading whitespace */ + ptr--; + while (isspace((int)*ptr) && ptr >= buf) + *ptr-- = '\0'; + ptr = buf; + while (isspace((int)*ptr)) + ptr++; + if (strlen(ptr) == 0) + continue; + + /* parse the event, 7 bytes with optional comment */ + /* 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # event */ + i = 0; + tok = strtok(ptr, " "); + while (tok) { + if (i == 7) + break; + j = i++; + if (str2uchar(tok, &rqdata[j]) != 0) { + break; + } + tok = strtok(NULL, " "); + } + if (i < 7) { + lprintf(LOG_ERR, "Invalid Event: %s", + buf2str(rqdata, sizeof(rqdata))); + continue; + } + + memset(&sel_event, 0, sizeof(struct sel_event_record)); + sel_event.record_id = 0x0000; + sel_event.record_type = 0x02; + sel_event.sel_type.standard_type.gen_id = 0x00; + sel_event.sel_type.standard_type.evm_rev = rqdata[0]; + sel_event.sel_type.standard_type.sensor_type = rqdata[1]; + sel_event.sel_type.standard_type.sensor_num = rqdata[2]; + sel_event.sel_type.standard_type.event_type = rqdata[3] & 0x7f; + sel_event.sel_type.standard_type.event_dir = (rqdata[3] & 0x80) >> 7; + sel_event.sel_type.standard_type.event_data[0] = rqdata[4]; + sel_event.sel_type.standard_type.event_data[1] = rqdata[5]; + sel_event.sel_type.standard_type.event_data[2] = rqdata[6]; + + rc = ipmi_sel_add_entry(intf, &sel_event); + if (rc < 0) + break; + } + + fclose(fp); + return rc; +} + +static struct ipmi_event_sensor_types oem_kontron_event_reading_types[] __attribute__((unused)) = { + { 0x70 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 1", "Code Assert" }, + { 0x71 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 2", "Code Assert" }, +}; + +char * +get_kontron_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) +{ + char * description = NULL; + /* + * Kontron OEM events are described in the product's user manual, but are limited in favor of + * sensor specific + */ + + /* Only standard records are defined so far */ + if( rec->record_type < 0xC0 ){ + struct ipmi_event_sensor_types *st=NULL; + for ( st=oem_kontron_event_reading_types ; st->type != NULL; st++){ + if (st->code == rec->sel_type.standard_type.event_type ){ + size_t len =strlen(st->desc); + description = (char*)malloc( len + 1 ); + memcpy(description, st->desc , len); + description[len] = 0;; + return description; + } + } + } + + return NULL; +} + +char * +get_newisys_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) +{ + /* + * Newisys OEM event descriptions can be retrieved through an + * OEM IPMI command. + */ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[6]; + char * description = NULL; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = 0x2E; + req.msg.cmd = 0x01; + req.msg.data_len = sizeof(msg_data); + + msg_data[0] = 0x15; /* IANA LSB */ + msg_data[1] = 0x24; /* IANA */ + msg_data[2] = 0x00; /* IANA MSB */ + msg_data[3] = 0x01; /* Subcommand */ + msg_data[4] = rec->record_id & 0x00FF; /* SEL Record ID LSB */ + msg_data[5] = (rec->record_id & 0xFF00) >> 8; /* SEL Record ID MSB */ + + req.msg.data = msg_data; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + if (verbose) + lprintf(LOG_ERR, "Error issuing OEM command"); + return NULL; + } + if (rsp->ccode > 0) { + if (verbose) + lprintf(LOG_ERR, "OEM command returned error code: %s", + val2str(rsp->ccode, completion_code_vals)); + return NULL; + } + + /* Verify our response before we use it */ + if (rsp->data_len < 5) + { + lprintf(LOG_ERR, "Newisys OEM response too short"); + return NULL; + } + else if (rsp->data_len != (4 + rsp->data[3])) + { + lprintf(LOG_ERR, "Newisys OEM response has unexpected length"); + return NULL; + } + else if (IPM_DEV_MANUFACTURER_ID(rsp->data) != IPMI_OEM_NEWISYS) + { + lprintf(LOG_ERR, "Newisys OEM response has unexpected length"); + return NULL; + } + + description = (char*)malloc(rsp->data[3] + 1); + memcpy(description, rsp->data + 4, rsp->data[3]); + description[rsp->data[3]] = 0;; + + return description; +} + +char * +get_supermicro_evt_desc(struct ipmi_intf *intf, struct sel_event_record *rec) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + char *desc = NULL; + char *str; + int chipset_type = 1; + int data1; + int data2; + int data3; + int length; + int sensor_type; + uint8_t i = 0; + uint16_t oem_id = 0; + /* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to + * data1,data2,data3 + */ + data1 = rec->sel_type.standard_type.event_data[0]; + data2 = rec->sel_type.standard_type.event_data[1]; + data3 = rec->sel_type.standard_type.event_data[2]; + /* Check for the Standard Event type == 0x6F */ + if (rec->sel_type.standard_type.event_type != 0x6F) { + return NULL; + } + /* Allocate mem for te Description string */ + desc = (char *)malloc(SIZE_OF_DESC); + if (desc == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(desc,0,SIZE_OF_DESC); + sensor_type = rec->sel_type.standard_type.sensor_type; + switch (sensor_type) { + case SENSOR_TYPE_MEMORY: + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data = NULL; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, " Error getting system info"); + if (desc != NULL) { + free(desc); + desc = NULL; + } + return NULL; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, " Error getting system info: %s", + val2str(rsp->ccode, completion_code_vals)); + if (desc != NULL) { + free(desc); + desc = NULL; + } + return NULL; + } + /* check the chipset type */ + oem_id = ipmi_get_oem_id(intf); + if (oem_id == 0) { + return NULL; + } + length = sizeof(supermicro_X8); + for (i = 0; i < length; i++) { + if (oem_id == supermicro_X8[i]) { + chipset_type = 0; + break; + } + } + length = sizeof(supermicro_x9); + for (i = 0; i < length; i++) { + if (oem_id == supermicro_x9[i]) { + chipset_type = 2; + break; + } + } + if (chipset_type == 0) { + snprintf(desc, SIZE_OF_DESC, "@DIMM%2X(CPU%x)", + data2, + (data3 & 0x03) + 1); + } else if (chipset_type == 1) { + snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)", + (data2 >> 4) + 0x40 + (data3 & 0x3) * 4, + (data2 & 0xf) + 0x27, (data3 & 0x03) + 1); + } else if (chipset_type == 2) { + snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)", + (data2 >> 4) + 0x40 + (data3 & 0x3) * 3, + (data2 & 0xf) + 0x27, (data3 & 0x03) + 1); + } else { + snprintf(desc, SIZE_OF_DESC, ""); + } + break; + case SENSOR_TYPE_SUPERMICRO_OEM: + if (data1 == 0x80 && data3 == 0xFF) { + if (data2 == 0x0) { + snprintf(desc, SIZE_OF_DESC, "BMC unexpected reset"); + } else if (data2 == 0x1) { + snprintf(desc, SIZE_OF_DESC, "BMC cold reset"); + } else if (data2 == 0x2) { + snprintf(desc, SIZE_OF_DESC, "BMC warm reset"); + } + } + break; + } + return desc; +} + +/* + * Function : Decoding the SEL OEM Bytes for the DELL Platforms. + * Description : The below fucntion will decode the SEL Events OEM Bytes for the Dell specific Sensors only. + * The below function will append the additional information Strings/description to the normal sel desc. + * With this the SEL will display additional information sent via OEM Bytes of the SEL Record. + * NOTE : Specific to DELL Platforms only. + * Returns : Pointer to the char string. + */ +char * get_dell_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) +{ + int data1, data2, data3; + int sensor_type; + char *desc = NULL; + + unsigned char count; + unsigned char node; + unsigned char num; + unsigned char dimmNum; + unsigned char dimmsPerNode; + char dimmStr[MAX_DIMM_STR]; + char cardStr[MAX_CARD_STR]; + char numStr[MAX_CARDNO_STR]; + char tmpdesc[SIZE_OF_DESC]; + char* str; + unsigned char incr = 0; + unsigned char i=0,j = 0; + unsigned char postCode; + struct ipmi_rs *rsp; + struct ipmi_rq req; + char tmpData; + int version; + /* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to Data1,data2,data3 */ + data1 = rec->sel_type.standard_type.event_data[0]; + data2 = rec->sel_type.standard_type.event_data[1]; + data3 = rec->sel_type.standard_type.event_data[2]; + /* Check for the Standard Event type == 0x6F */ + if (0x6F == rec->sel_type.standard_type.event_type) + { + sensor_type = rec->sel_type.standard_type.sensor_type; + /* Allocate mem for te Description string */ + desc = (char*)malloc(SIZE_OF_DESC); + if(NULL == desc) + return NULL; + memset(desc,0,SIZE_OF_DESC); + memset(tmpdesc,0,SIZE_OF_DESC); + switch (sensor_type) { + case SENSOR_TYPE_PROCESSOR: /* Processor/CPU related OEM Sel Byte Decoding for DELL Platforms only */ + if((OEM_CODE_IN_BYTE2 == (data1 & DATA_BYTE2_SPECIFIED_MASK))) + { + if(0x00 == (data1 & MASK_LOWER_NIBBLE)) + snprintf(desc,SIZE_OF_DESC,"CPU Internal Err | "); + if(0x06 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc,SIZE_OF_DESC,"CPU Protocol Err | "); + + } + + /* change bit location to a number */ + for (count= 0; count < 8; count++) + { + if (BIT(count)& data2) + { + count++; + /* 0x0A - CPU sensor number */ + if((0x06 == (data1 & MASK_LOWER_NIBBLE)) && (0x0A == rec->sel_type.standard_type.sensor_num)) + snprintf(desc,SIZE_OF_DESC,"FSB %d ",count); // Which CPU Has generated the FSB + else + snprintf(desc,SIZE_OF_DESC,"CPU %d | APIC ID %d ",count,data3); /* Specific CPU related info */ + break; + } + } + } + break; + case SENSOR_TYPE_MEMORY: /* Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */ + case SENSOR_TYPE_EVT_LOG: /* Events Logging for Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */ + + /* Get the current version of the IPMI Spec Based on that Decoding of memory info is done.*/ + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.lun = 0; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data = NULL; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (NULL == rsp) + { + lprintf(LOG_ERR, " Error getting system info"); + if (desc != NULL) { + free(desc); + desc = NULL; + } + return NULL; + } + else if (rsp->ccode > 0) + { + lprintf(LOG_ERR, " Error getting system info: %s", + val2str(rsp->ccode, completion_code_vals)); + if (desc != NULL) { + free(desc); + desc = NULL; + } + return NULL; + } + version = rsp->data[4]; + /* Memory DIMMS */ + if( (data1 & OEM_CODE_IN_BYTE2) || (data1 & OEM_CODE_IN_BYTE3 ) ) + { + /* Memory Redundancy related oem bytes docoding .. */ + if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) ) + { + if(0x00 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc,SIZE_OF_DESC," Redundancy Regained | "); + } + else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc,SIZE_OF_DESC,"Redundancy Lost | "); + } + } /* Correctable and uncorrectable ECC Error Decoding */ + else if(SENSOR_TYPE_MEMORY == sensor_type) + { + if(0x00 == (data1 & MASK_LOWER_NIBBLE)) + { + /* 0x1C - Memory Sensor Number */ + if(0x1C == rec->sel_type.standard_type.sensor_num) + { + /*Add the complete information about the Memory Configs.*/ + if((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3 )) + { + count = 0; + snprintf(desc,SIZE_OF_DESC,"CRC Error on:"); + for(i=0;i<4;i++) + { + if((BIT(i))&(data2)) + { + if(count) + { + str = desc+strlen(desc); + *str++ = ','; + str = '\0'; + count = 0; + } + switch(i) /* Which type of memory config is present.. */ + { + case 0: snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Memory"); + strcat(desc,tmpdesc); + count++; + break; + case 1: snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Config"); + strcat(desc,tmpdesc); + count++; + break; + case 2: snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory"); + strcat(desc,tmpdesc); + count++; + break; + case 3: snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory-corr"); + strcat(desc,tmpdesc); + count++; + break; + default: + break; + } + } + } + if(data3>=0x00 && data3<0xFF) + { + snprintf(tmpdesc,SIZE_OF_DESC,"|Failing_Channel:%d",data3); + strcat(desc,tmpdesc); + } + } + break; + } + snprintf(desc,SIZE_OF_DESC,"Correctable ECC | "); + } + else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc,SIZE_OF_DESC,"UnCorrectable ECC | "); + } + } /* Corr Memory log disabled */ + else if(SENSOR_TYPE_EVT_LOG == sensor_type) + { + if(0x00 == (data1 & MASK_LOWER_NIBBLE)) + snprintf(desc,SIZE_OF_DESC,"Corr Memory Log Disabled | "); + } + } + else + { + if(SENSOR_TYPE_SYS_EVENT == sensor_type) + { + if(0x02 == (data1 & MASK_LOWER_NIBBLE)) + snprintf(desc,SIZE_OF_DESC,"Unknown System Hardware Failure "); + } + if(SENSOR_TYPE_EVT_LOG == sensor_type) + { + if(0x03 == (data1 & MASK_LOWER_NIBBLE)) + snprintf(desc,SIZE_OF_DESC,"All Even Logging Dissabled"); + } + } + /* + * Based on the above error, we need to find whcih memory slot or + * Card has got the Errors/Sel Generated. + */ + if(data1 & OEM_CODE_IN_BYTE2 ) + { + /* Find the Card Type */ + if((0x0F != (data2 >> 4)) && ((data2 >> 4) < 0x08)) + { + tmpData = ('A'+ (data2 >> 4)); + if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) ) + { + snprintf(tmpdesc, SIZE_OF_DESC, "Bad Card %c", tmpData); + } + else + { + snprintf(tmpdesc, SIZE_OF_DESC, "Card %c", tmpData); + } + strcat(desc, tmpdesc); + } /* Find the Bank Number of the DIMM */ + if (0x0F != (data2 & MASK_LOWER_NIBBLE)) + { + if(0x51 == version) + { + snprintf(tmpdesc, SIZE_OF_DESC, "Bank %d", ((data2 & 0x0F)+1)); + strcat(desc, tmpdesc); + } + else + { + incr = (data2 & 0x0f) << 3; + } + } + + } + /* Find the DIMM Number of the Memory which has Generated the Fault or Sel */ + if(data1 & OEM_CODE_IN_BYTE3 ) + { + // Based on the IPMI Spec Need Identify the DIMM Details. + // For the SPEC 1.5 Only the DIMM Number is Valid. + if(0x51 == version) + { + snprintf(tmpdesc, SIZE_OF_DESC, "DIMM %c", ('A'+ data3)); + strcat(desc, tmpdesc); + } + /* For the SPEC 2.0 Decode the DIMM Number as it supports more.*/ + else if( ((data2 >> 4) > 0x07) && (0x0F != (data2 >> 4) )) + { + strcpy(dimmStr, " DIMM"); + str = desc+strlen(desc); + dimmsPerNode = 4; + if(0x09 == (data2 >> 4)) dimmsPerNode = 6; + else if(0x0A == (data2 >> 4)) dimmsPerNode = 8; + else if(0x0B == (data2 >> 4)) dimmsPerNode = 9; + else if(0x0C == (data2 >> 4)) dimmsPerNode = 12; + else if(0x0D == (data2 >> 4)) dimmsPerNode = 24; + else if(0x0E == (data2 >> 4)) dimmsPerNode = 3; + count = 0; + for (i = 0; i < 8; i++) + { + if (BIT(i) & data3) + { + if(count) + { + strcat(str,","); + count = 0x00; + } + node = (incr + i)/dimmsPerNode; + dimmNum = ((incr + i)%dimmsPerNode)+1; + dimmStr[5] = node + 'A'; + sprintf(tmpdesc,"%d",dimmNum); + for(j = 0; j < strlen(tmpdesc);j++) + dimmStr[6+j] = tmpdesc[j]; + dimmStr[6+j] = '\0'; + strcat(str,dimmStr); // final DIMM Details. + count++; + } + } + } + else + { + strcpy(dimmStr, " DIMM"); + str = desc+strlen(desc); + count = 0; + for (i = 0; i < 8; i++) + { + if (BIT(i) & data3) + { + // check if more than one DIMM, if so add a comma to the string. + sprintf(tmpdesc,"%d",(i + incr + 1)); + if(count) + { + strcat(str,","); + count = 0x00; + } + for(j = 0; j < strlen(tmpdesc);j++) + dimmStr[5+j] = tmpdesc[j]; + dimmStr[5+j] = '\0'; + strcat(str, dimmStr); + count++; + } + } + } + } + break; + /* Sensor In system charectorization Error Decoding. + Sensor type 0x20*/ + case SENSOR_TYPE_TXT_CMD_ERROR: + if((0x00 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) + { + switch(data3) + { + case 0x01: + snprintf(desc,SIZE_OF_DESC,"BIOS TXT Error"); + break; + case 0x02: + snprintf(desc,SIZE_OF_DESC,"Processor/FIT TXT"); + break; + case 0x03: + snprintf(desc,SIZE_OF_DESC,"BIOS ACM TXT Error"); + break; + case 0x04: + snprintf(desc,SIZE_OF_DESC,"SINIT ACM TXT Error"); + break; + case 0xff: + snprintf(desc,SIZE_OF_DESC,"Unrecognized TT Error12"); + break; + default: + break; + } + } + break; + /* OS Watch Dog Timer Sel Events */ + case SENSOR_TYPE_WTDOG: + + if(SENSOR_TYPE_OEM_SEC_EVENT == data1) + { + if(0x04 == data2) + { + snprintf(desc,SIZE_OF_DESC,"Hard Reset|Interrupt type None,SMS/OS Timer used at expiration"); + } + } + + break; + /* This Event is for BMC to Othe Hardware or CPU . */ + case SENSOR_TYPE_VER_CHANGE: + if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) + { + if(0x02 == data2) + { + if(0x00 == data3) + { + snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and other hardware"); + } + else if(0x01 == data3) + { + snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and CPU"); + } + } + } + break; + /* Flex or Mac tuning OEM Decoding for the DELL. */ + case SENSOR_TYPE_OEM_SEC_EVENT: + /* 0x25 - Virtual MAC sensory number - Dell OEM */ + if(0x25 == rec->sel_type.standard_type.sensor_num) + { + if(0x01 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "Failed to program Virtual Mac Address"); + if((data1 & OEM_CODE_IN_BYTE2)&&(data1 & OEM_CODE_IN_BYTE3)) + { + snprintf(tmpdesc, SIZE_OF_DESC, " at bus:%.2x device:%.2x function:%x", + data3 &0x7F, (data2 >> 3) & 0x1F, + data2 & 0x07); + strcat(desc,tmpdesc); + } + } + else if(0x02 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "Device option ROM failed to support link tuning or flex address"); + } + else if(0x03 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "Failed to get link tuning or flex address data from BMC/iDRAC"); + } + } + break; + case SENSOR_TYPE_CRIT_INTR: + case SENSOR_TYPE_OEM_NFATAL_ERROR: /* Non - Fatal PCIe Express Error Decoding */ + case SENSOR_TYPE_OEM_FATAL_ERROR: /* Fatal IO Error Decoding */ + /* 0x29 - QPI Linx Error Sensor Dell OEM */ + if(0x29 == rec->sel_type.standard_type.sensor_num) + { + if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) + { + snprintf(tmpdesc, SIZE_OF_DESC, "Partner-(LinkId:%d,AgentId:%d)|",(data2 & 0xC0),(data2 & 0x30)); + strcat(desc,tmpdesc); + snprintf(tmpdesc, SIZE_OF_DESC, "ReportingAgent(LinkId:%d,AgentId:%d)|",(data2 & 0x0C),(data2 & 0x03)); + strcat(desc,tmpdesc); + if(0x00 == (data3 & 0xFC)) + { + snprintf(tmpdesc, SIZE_OF_DESC, "LinkWidthDegraded|"); + strcat(desc,tmpdesc); + } + if(BIT(1)& data3) + { + snprintf(tmpdesc,SIZE_OF_DESC,"PA_Type:IOH|"); + } + else + { + snprintf(tmpdesc,SIZE_OF_DESC,"PA-Type:CPU|"); + } + strcat(desc,tmpdesc); + if(BIT(0)& data3) + { + snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:IOH"); + } + else + { + snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:CPU"); + } + strcat(desc,tmpdesc); + } + } + else + { + + if(0x02 == (data1 & MASK_LOWER_NIBBLE)) + { + sprintf(desc,"%s","IO channel Check NMI"); + } + else + { + if(0x00 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "%s","PCIe Error |"); + } + else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "%s","I/O Error |"); + } + else if(0x04 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "%s","PCI PERR |"); + } + else if(0x05 == (data1 & MASK_LOWER_NIBBLE)) + { + snprintf(desc, SIZE_OF_DESC, "%s","PCI SERR |"); + } + else + { + snprintf(desc, SIZE_OF_DESC, "%s"," "); + } + if (data3 & 0x80) + snprintf(tmpdesc, SIZE_OF_DESC, "Slot %d", data3 & 0x7F); + else + snprintf(tmpdesc, SIZE_OF_DESC, "PCI bus:%.2x device:%.2x function:%x", + data3 &0x7F, (data2 >> 3) & 0x1F, + data2 & 0x07); + + strcat(desc,tmpdesc); + } + } + break; + /* POST Fatal Errors generated from the Server with much more info*/ + case SENSOR_TYPE_FRM_PROG: + if((0x0F == (data1 & MASK_LOWER_NIBBLE))&&(data1 & OEM_CODE_IN_BYTE2)) + { + switch(data2) + { + case 0x80: + snprintf(desc, SIZE_OF_DESC, "No memory is detected.");break; + case 0x81: + snprintf(desc,SIZE_OF_DESC, "Memory is detected but is not configurable.");break; + case 0x82: + snprintf(desc, SIZE_OF_DESC, "Memory is configured but not usable.");break; + case 0x83: + snprintf(desc, SIZE_OF_DESC, "System BIOS shadow failed.");break; + case 0x84: + snprintf(desc, SIZE_OF_DESC, "CMOS failed.");break; + case 0x85: + snprintf(desc, SIZE_OF_DESC, "DMA controller failed.");break; + case 0x86: + snprintf(desc, SIZE_OF_DESC, "Interrupt controller failed.");break; + case 0x87: + snprintf(desc, SIZE_OF_DESC, "Timer refresh failed.");break; + case 0x88: + snprintf(desc, SIZE_OF_DESC, "Programmable interval timer error.");break; + case 0x89: + snprintf(desc, SIZE_OF_DESC, "Parity error.");break; + case 0x8A: + snprintf(desc, SIZE_OF_DESC, "SIO failed.");break; + case 0x8B: + snprintf(desc, SIZE_OF_DESC, "Keyboard controller failed.");break; + case 0x8C: + snprintf(desc, SIZE_OF_DESC, "System management interrupt initialization failed.");break; + case 0x8D: + snprintf(desc, SIZE_OF_DESC, "TXT-SX Error.");break; + case 0xC0: + snprintf(desc, SIZE_OF_DESC, "Shutdown test failed.");break; + case 0xC1: + snprintf(desc, SIZE_OF_DESC, "BIOS POST memory test failed.");break; + case 0xC2: + snprintf(desc, SIZE_OF_DESC, "RAC configuration failed.");break; + case 0xC3: + snprintf(desc, SIZE_OF_DESC, "CPU configuration failed.");break; + case 0xC4: + snprintf(desc, SIZE_OF_DESC, "Incorrect memory configuration.");break; + case 0xFE: + snprintf(desc, SIZE_OF_DESC, "General failure after video."); + break; + } + } + break; + + default: + break; + } + } + else + { + sensor_type = rec->sel_type.standard_type.event_type; + } + return desc; +} + +char * +ipmi_get_oem_desc(struct ipmi_intf * intf, struct sel_event_record * rec) +{ + char * desc = NULL; + + switch (ipmi_get_oem(intf)) + { + case IPMI_OEM_NEWISYS: + desc = get_newisys_evt_desc(intf, rec); + break; + case IPMI_OEM_KONTRON: + desc = get_kontron_evt_desc(intf, rec); + break; + case IPMI_OEM_DELL: // Dell Decoding of the OEM Bytes from SEL Record. + desc = get_dell_evt_desc(intf, rec); + break; + case IPMI_OEM_SUPERMICRO: + case IPMI_OEM_SUPERMICRO_47488: + desc = get_supermicro_evt_desc(intf, rec); + break; + case IPMI_OEM_UNKNOWN: + default: + break; + } + + return desc; +} + + +void +ipmi_get_event_desc(struct ipmi_intf * intf, struct sel_event_record * rec, char ** desc) +{ + uint8_t code, offset; + struct ipmi_event_sensor_types *evt = NULL; + char *sfx = NULL; /* This will be assigned if the Platform is DELL, + additional info is appended to the current Description */ + + if (desc == NULL) + return; + *desc = NULL; + + if ((rec->sel_type.standard_type.event_type >= 0x70) && (rec->sel_type.standard_type.event_type < 0x7F)) { + *desc = ipmi_get_oem_desc(intf, rec); + return; + } else if (rec->sel_type.standard_type.event_type == 0x6f) { + if( rec->sel_type.standard_type.sensor_type >= 0xC0 && rec->sel_type.standard_type.sensor_type < 0xF0) { + IPMI_OEM iana = ipmi_get_oem(intf); + + switch(iana){ + case IPMI_OEM_KONTRON: + lprintf(LOG_DEBUG, "oem sensor type %x %d using oem type supplied description", + rec->sel_type.standard_type.sensor_type , iana); + + evt = oem_kontron_event_types; + code = rec->sel_type.standard_type.sensor_type; + break; + case IPMI_OEM_DELL: /* OEM Bytes Decoding for DELLi */ + evt = sensor_specific_types; + code = rec->sel_type.standard_type.sensor_type; + if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) || + (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) ) + { + if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) + evt->data = rec->sel_type.standard_type.event_data[1]; + + sfx = ipmi_get_oem_desc(intf, rec); + } + break; + case IPMI_OEM_SUPERMICRO: + case IPMI_OEM_SUPERMICRO_47488: + evt = sensor_specific_types; + code = rec->sel_type.standard_type.sensor_type; + sfx = ipmi_get_oem_desc(intf, rec); + break; + /* add your oem sensor assignation here */ + } + if( evt == NULL ){ + lprintf(LOG_DEBUG, "oem sensor type %x using standard type supplied description", + rec->sel_type.standard_type.sensor_type ); + } + } else { + switch (ipmi_get_oem(intf)) { + case IPMI_OEM_SUPERMICRO: + case IPMI_OEM_SUPERMICRO_47488: + evt = sensor_specific_types; + code = rec->sel_type.standard_type.sensor_type; + sfx = ipmi_get_oem_desc(intf, rec); + break; + } + } + if( evt == NULL ){ + evt = sensor_specific_types; + code = rec->sel_type.standard_type.sensor_type; + } + /* + * Check for the OEM DELL Interface based on the Dell Specific Vendor Code. + * If its Dell Platform, do the OEM Byte decode from the SEL Records. + * Additional information should be written by the ipmi_get_oem_desc() + */ + if(ipmi_get_oem(intf) == IPMI_OEM_DELL) { + code = rec->sel_type.standard_type.sensor_type; + if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) || + (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) ) + { + if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) + evt->data = rec->sel_type.standard_type.event_data[1]; + sfx = ipmi_get_oem_desc(intf, rec); + + } + else if(SENSOR_TYPE_OEM_SEC_EVENT == rec->sel_type.standard_type.event_data[0]) + { + /* 0x23 : Sensor Number.*/ + if(0x23 == rec->sel_type.standard_type.sensor_num) + { + evt->data = rec->sel_type.standard_type.event_data[1]; + sfx = ipmi_get_oem_desc(intf, rec); + } + } + } + } else { + evt = generic_event_types; + code = rec->sel_type.standard_type.event_type; + } + + offset = rec->sel_type.standard_type.event_data[0] & 0xf; + + while (evt->type) { + if ((evt->code == code && evt->offset == offset && evt->desc != NULL) && + ((evt->data == ALL_OFFSETS_SPECIFIED) || + ((rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) && + (evt->data == rec->sel_type.standard_type.event_data[1])))) + { + /* Increase the Malloc size to current_size + Dellspecific description size */ + *desc = (char *)malloc(strlen(evt->desc) + 48 + SIZE_OF_DESC); + if (NULL == *desc) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + memset(*desc, 0, strlen(evt->desc)+ 48 + SIZE_OF_DESC); + /* + * Additional info is present for the DELL Platforms. + * Append the same to the evt->desc string. + */ + if (sfx) { + sprintf(*desc, "%s (%s)", evt->desc, sfx); + free(sfx); + sfx = NULL; + } else { + sprintf(*desc, "%s", evt->desc); + } + return; + } + evt++; + } + /* The Above while Condition was not met beacouse the below sensor type were Newly defined OEM + Secondary Events. 0xC1, 0xC2, 0xC3. */ + if((sfx) && (0x6F == rec->sel_type.standard_type.event_type)) + { + uint8_t flag = 0x00; + switch(code) + { + case SENSOR_TYPE_FRM_PROG: + if(0x0F == offset) + flag = 0x01; + break; + case SENSOR_TYPE_OEM_SEC_EVENT: + if((0x01 == offset) || (0x02 == offset) || (0x03 == offset)) + flag = 0x01; + break; + case SENSOR_TYPE_OEM_NFATAL_ERROR: + if((0x00 == offset) || (0x02 == offset)) + flag = 0x01; + break; + case SENSOR_TYPE_OEM_FATAL_ERROR: + if(0x01 == offset) + flag = 0x01; + break; + case SENSOR_TYPE_SUPERMICRO_OEM: + flag = 0x02; + break; + default: + break; + } + if(flag) + { + *desc = (char *)malloc( 48 + SIZE_OF_DESC); + if (NULL == *desc) + { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + memset(*desc, 0, 48 + SIZE_OF_DESC); + if (flag == 0x02) { + sprintf(*desc, "%s", sfx); + return; + } + sprintf(*desc, "(%s)",sfx); + } + free(sfx); + sfx = NULL; + } +} + + +const char * +ipmi_sel_get_oem_sensor_type(IPMI_OEM iana, uint8_t code) +{ + struct ipmi_event_sensor_types *st = NULL; + + switch(iana){ + case IPMI_OEM_KONTRON: + st = oem_kontron_event_types; + break; + /* add you oem sensor type lookup assignement here */ + default: + lprintf(LOG_DEBUG, "ipmitool: missing OEM sensor type for %ul",iana); + break; + } + + if( st != NULL ) + for (; st->type != NULL; st++) + if (st->code == code) + return st->type; + + return ipmi_sel_get_sensor_type(code); +} + +const char * +ipmi_sel_get_oem_sensor_type_offset(IPMI_OEM iana, uint8_t code, uint8_t offset) +{ + struct ipmi_event_sensor_types *st = NULL; + + switch(iana){ + case IPMI_OEM_KONTRON: + st = oem_kontron_event_types; + break; + /* add you oem sensor type lookup assignement here */ + default: + lprintf(LOG_DEBUG, + "ipmitool: missing OEM sensor type offset for %ul",iana); + break; + } + + if( st != NULL ) + for (; st->type != NULL; st++) + { + if (st->code == code && st->offset == (offset&0xf)) + return st->type; + } + + return ipmi_sel_get_oem_sensor_type(iana,code); +} + +const char * +ipmi_sel_get_sensor_type(uint8_t code) +{ + struct ipmi_event_sensor_types *st; + for (st = sensor_specific_types; st->type != NULL; st++) + if (st->code == code) + return st->type; + return "Unknown"; +} + +const char * +ipmi_sel_get_sensor_type_offset(uint8_t code, uint8_t offset) +{ + struct ipmi_event_sensor_types *st; + for (st = sensor_specific_types; st->type != NULL; st++) + if (st->code == code && st->offset == (offset&0xf)) + return st->type; + + return ipmi_sel_get_sensor_type(code); +} + +static int +ipmi_sel_get_info(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint16_t e, version; + uint32_t f; + int pctfull = 0; + uint32_t fs = 0xffffffff; + uint32_t zeros = 0; + + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_GET_SEL_INFO; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SEL Info command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SEL Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "sel_info"); + + printf("SEL Information\n"); + version = rsp->data[0]; + printf("Version : %d.%d (%s)\n", + version & 0xf, (version>>4) & 0xf, + (version == 0x51 || version == 0x02) ? "v1.5, v2 compliant" : "Unknown"); + + /* save the entry count and free space to determine percent full */ + e = buf2short(rsp->data + 1); + f = buf2short(rsp->data + 3); + printf("Entries : %d\n", e); + printf("Free Space : %d bytes %s\n", f ,(f==65535 ? "or more" : "" )); + + if (e) { + e *= 16; /* each entry takes 16 bytes */ + f += e; /* this is supposed to give the total size ... */ + pctfull = (int)(100 * ( (double)e / (double)f )); + } + + if( f >= 65535 ) { + printf("Percent Used : %s\n", "unknown" ); + } + else { + printf("Percent Used : %d%%\n", pctfull); + } + + + if ((!memcmp(rsp->data + 5, &fs, 4)) || + (!memcmp(rsp->data + 5, &zeros, 4))) + printf("Last Add Time : Not Available\n"); + else + printf("Last Add Time : %s\n", + ipmi_sel_timestamp(buf2long(rsp->data + 5))); + + if ((!memcmp(rsp->data + 9, &fs, 4)) || + (!memcmp(rsp->data + 9, &zeros, 4))) + printf("Last Del Time : Not Available\n"); + else + printf("Last Del Time : %s\n", + ipmi_sel_timestamp(buf2long(rsp->data + 9))); + + + printf("Overflow : %s\n", + rsp->data[13] & 0x80 ? "true" : "false"); + printf("Supported Cmds : "); + if (rsp->data[13] & 0x0f) + { + if (rsp->data[13] & 0x08) + printf("'Delete' "); + if (rsp->data[13] & 0x04) + printf("'Partial Add' "); + if (rsp->data[13] & 0x02) + printf("'Reserve' "); + if (rsp->data[13] & 0x01) + printf("'Get Alloc Info' "); + } + else + printf("None"); + printf("\n"); + + /* get sel allocation info if supported */ + if (rsp->data[13] & 1) { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_GET_SEL_ALLOC_INFO; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Get SEL Allocation Info command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, + "Get SEL Allocation Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("# of Alloc Units : %d\n", buf2short(rsp->data)); + printf("Alloc Unit Size : %d\n", buf2short(rsp->data + 2)); + printf("# Free Units : %d\n", buf2short(rsp->data + 4)); + printf("Largest Free Blk : %d\n", buf2short(rsp->data + 6)); + printf("Max Record Size : %d\n", rsp->data[8]); + } + return 0; +} + +uint16_t +ipmi_sel_get_std_entry(struct ipmi_intf * intf, uint16_t id, + struct sel_event_record * evt) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + uint8_t msg_data[6]; + uint16_t next; + int data_count; + + memset(msg_data, 0, 6); + msg_data[0] = 0x00; /* no reserve id, not partial get */ + msg_data[1] = 0x00; + msg_data[2] = id & 0xff; + msg_data[3] = (id >> 8) & 0xff; + msg_data[4] = 0x00; /* offset */ + msg_data[5] = 0xff; /* length */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_GET_SEL_ENTRY; + req.msg.data = msg_data; + req.msg.data_len = 6; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SEL Entry %x command failed", id); + return 0; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SEL Entry %x command failed: %s", + id, val2str(rsp->ccode, completion_code_vals)); + return 0; + } + + /* save next entry id */ + next = (rsp->data[1] << 8) | rsp->data[0]; + + lprintf(LOG_DEBUG, "SEL Entry: %s", buf2str(rsp->data+2, rsp->data_len-2)); + memset(evt, 0, sizeof(*evt)); + + /*Clear SEL Structure*/ + evt->record_id = 0; + evt->record_type = 0; + if (evt->record_type < 0xc0) + { + evt->sel_type.standard_type.timestamp = 0; + evt->sel_type.standard_type.gen_id = 0; + evt->sel_type.standard_type.evm_rev = 0; + evt->sel_type.standard_type.sensor_type = 0; + evt->sel_type.standard_type.sensor_num = 0; + evt->sel_type.standard_type.event_type = 0; + evt->sel_type.standard_type.event_dir = 0; + evt->sel_type.standard_type.event_data[0] = 0; + evt->sel_type.standard_type.event_data[1] = 0; + evt->sel_type.standard_type.event_data[2] = 0; + } + else if (evt->record_type < 0xe0) + { + evt->sel_type.oem_ts_type.timestamp = 0; + evt->sel_type.oem_ts_type.manf_id[0] = 0; + evt->sel_type.oem_ts_type.manf_id[1] = 0; + evt->sel_type.oem_ts_type.manf_id[2] = 0; + for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++) + evt->sel_type.oem_ts_type.oem_defined[data_count] = 0; + } + else + { + for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++) + evt->sel_type.oem_nots_type.oem_defined[data_count] = 0; + } + + /* save response into SEL event structure */ + evt->record_id = (rsp->data[3] << 8) | rsp->data[2]; + evt->record_type = rsp->data[4]; + if (evt->record_type < 0xc0) + { + evt->sel_type.standard_type.timestamp = (rsp->data[8] << 24) | (rsp->data[7] << 16) | + (rsp->data[6] << 8) | rsp->data[5]; + evt->sel_type.standard_type.gen_id = (rsp->data[10] << 8) | rsp->data[9]; + evt->sel_type.standard_type.evm_rev = rsp->data[11]; + evt->sel_type.standard_type.sensor_type = rsp->data[12]; + evt->sel_type.standard_type.sensor_num = rsp->data[13]; + evt->sel_type.standard_type.event_type = rsp->data[14] & 0x7f; + evt->sel_type.standard_type.event_dir = (rsp->data[14] & 0x80) >> 7; + evt->sel_type.standard_type.event_data[0] = rsp->data[15]; + evt->sel_type.standard_type.event_data[1] = rsp->data[16]; + evt->sel_type.standard_type.event_data[2] = rsp->data[17]; + } + else if (evt->record_type < 0xe0) + { + evt->sel_type.oem_ts_type.timestamp= (rsp->data[8] << 24) | (rsp->data[7] << 16) | + (rsp->data[6] << 8) | rsp->data[5]; + evt->sel_type.oem_ts_type.manf_id[0]= rsp->data[11]; + evt->sel_type.oem_ts_type.manf_id[1]= rsp->data[10]; + evt->sel_type.oem_ts_type.manf_id[2]= rsp->data[9]; + for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++) + evt->sel_type.oem_ts_type.oem_defined[data_count] = rsp->data[(data_count+12)]; + } + else + { + for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++) + evt->sel_type.oem_nots_type.oem_defined[data_count] = rsp->data[(data_count+5)]; + } + return next; +} + +static void +ipmi_sel_print_event_file(struct ipmi_intf * intf, struct sel_event_record * evt, FILE * fp) +{ + char * description; + + if (fp == NULL) + return; + + ipmi_get_event_desc(intf, evt, &description); + + fprintf(fp, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x # %s #0x%02x %s\n", + evt->sel_type.standard_type.evm_rev, + evt->sel_type.standard_type.sensor_type, + evt->sel_type.standard_type.sensor_num, + evt->sel_type.standard_type.event_type | (evt->sel_type.standard_type.event_dir << 7), + evt->sel_type.standard_type.event_data[0], + evt->sel_type.standard_type.event_data[1], + evt->sel_type.standard_type.event_data[2], + ( + (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) + ? + ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + : + ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + ), + evt->sel_type.standard_type.sensor_num, + (description != NULL) ? description : "Unknown"); + + if (description != NULL) { + free(description); + description = NULL; + } +} + +void +ipmi_sel_print_extended_entry(struct ipmi_intf * intf, struct sel_event_record * evt) +{ + sel_extended++; + ipmi_sel_print_std_entry(intf, evt); + sel_extended--; +} + +void +ipmi_sel_print_std_entry(struct ipmi_intf * intf, struct sel_event_record * evt) +{ + char * description; + struct sdr_record_list * sdr = NULL; + int data_count; + + if (sel_extended && (evt->record_type < 0xc0)) + sdr = ipmi_sdr_find_sdr_bynumtype(intf, evt->sel_type.standard_type.gen_id, evt->sel_type.standard_type.sensor_num, evt->sel_type.standard_type.sensor_type); + + + if (!evt) + return; + + if (csv_output) + printf("%x,", evt->record_id); + else + printf("%4x | ", evt->record_id); + + if (evt->record_type == 0xf0) + { + if (csv_output) + printf(",,"); + + printf ("Linux kernel panic: %.11s\n", (char *) evt + 5); + return; + } + + if (evt->record_type < 0xe0) + { + if ((evt->sel_type.standard_type.timestamp < 0x20000000)||(evt->sel_type.oem_ts_type.timestamp < 0x20000000)){ + printf(" Pre-Init "); + + if (csv_output) + printf(","); + else + printf(" |"); + + printf("%010d", evt->sel_type.standard_type.timestamp ); + if (csv_output) + printf(","); + else + printf("| "); + } + else { + if (evt->record_type < 0xc0) + printf("%s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp)); + else + printf("%s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp)); + if (csv_output) + printf(","); + else + printf(" | "); + + if (evt->record_type < 0xc0) + printf("%s", ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); + else + printf("%s", ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp)); + + if (csv_output) + printf(","); + else + printf(" | "); + } + + } + else + { + if (csv_output) + printf(",,"); + } + + if (evt->record_type >= 0xc0) + { + printf ("OEM record %02x", evt->record_type); + if (csv_output) + printf(","); + else + printf(" | "); + + if(evt->record_type < 0xdf) + { + printf ("%02x%02x%02x", evt->sel_type.oem_ts_type.manf_id[0], evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]); + if (csv_output) + printf(","); + else + printf(" | "); + for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++) + printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]); + } + else + { + for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++) + printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]); + } + ipmi_sel_oem_message(evt, 0); + printf ("\n"); + return; + } + + /* lookup SDR entry based on sensor number and type */ + if (sdr != NULL) { + printf("%s ", + ( + (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) + ? + ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + : + ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + ) + ); + switch (sdr->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + printf("%s", sdr->record.full->id_string); + break; + case SDR_RECORD_TYPE_COMPACT_SENSOR: + printf("%s", sdr->record.compact->id_string); + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + printf("%s", sdr->record.eventonly->id_string); + break; + case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: + printf("%s", sdr->record.fruloc->id_string); + break; + case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: + printf("%s", sdr->record.mcloc->id_string); + break; + case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: + printf("%s", sdr->record.genloc->id_string); + break; + default: + printf("#%02x", evt->sel_type.standard_type.sensor_num); + break; + } + } else { + printf("%s",( + (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) + ? + ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + : + ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + )); + if (evt->sel_type.standard_type.sensor_num != 0) + printf(" #0x%02x", evt->sel_type.standard_type.sensor_num); + } + + if (csv_output) + printf(","); + else + printf(" | "); + + ipmi_get_event_desc(intf, evt, &description); + if (description) { + printf("%s", description); + free(description); + description = NULL; + } + + if (csv_output) { + printf(","); + } else { + printf(" | "); + } + + if (evt->sel_type.standard_type.event_dir) { + printf("Deasserted"); + } else { + printf("Asserted"); + } + + if (sdr != NULL && evt->sel_type.standard_type.event_type == 1) { + /* + * Threshold Event + */ + float trigger_reading = 0.0; + float threshold_reading = 0.0; + uint8_t threshold_reading_provided = 0; + + /* trigger reading in event data byte 2 */ + if (((evt->sel_type.standard_type.event_data[0] >> 6) & 3) == 1) { + trigger_reading = sdr_convert_sensor_reading( + sdr->record.full, evt->sel_type.standard_type.event_data[1]); + } + + /* trigger threshold in event data byte 3 */ + if (((evt->sel_type.standard_type.event_data[0] >> 4) & 3) == 1) { + threshold_reading = sdr_convert_sensor_reading( + sdr->record.full, evt->sel_type.standard_type.event_data[2]); + threshold_reading_provided = 1; + } + + if (csv_output) + printf(","); + else + printf(" | "); + + printf("Reading %.*f", + (trigger_reading==(int)trigger_reading) ? 0 : 2, + trigger_reading); + if (threshold_reading_provided) { + printf(" %s Threshold %.*f %s", + ((evt->sel_type.standard_type.event_data[0] & 0xf) % 2) ? ">" : "<", + (threshold_reading==(int)threshold_reading) ? 0 : 2, + threshold_reading, + ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, + sdr->record.common->unit.modifier, + sdr->record.common->unit.type.base, + sdr->record.common->unit.type.modifier)); + } + } + else if (evt->sel_type.standard_type.event_type == 0x6f) { + int print_sensor = 1; + switch (ipmi_get_oem(intf)) { + case IPMI_OEM_SUPERMICRO: + case IPMI_OEM_SUPERMICRO_47488: + print_sensor = 0; + break; + } + /* + * Sensor-Specific Discrete + */ + if (print_sensor && evt->sel_type.standard_type.sensor_type == 0xC && /*TODO*/ + evt->sel_type.standard_type.sensor_num == 0 && + (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20) { + /* break down memory ECC reporting if we can */ + if (csv_output) + printf(","); + else + printf(" | "); + + printf("CPU %d DIMM %d", + evt->sel_type.standard_type.event_data[2] & 0x0f, + (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4); + } + } + + printf("\n"); +} + +void +ipmi_sel_print_std_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt) +{ + char * description; + int data_count; + + if (!evt) + return; + + printf("SEL Record ID : %04x\n", evt->record_id); + + if (evt->record_type == 0xf0) + { + printf (" Record Type : Linux kernel panic (OEM record %02x)\n", evt->record_type); + printf (" Panic string : %.11s\n\n", (char *) evt + 5); + return; + } + + printf(" Record Type : %02x", evt->record_type); + if (evt->record_type >= 0xc0) + { + if (evt->record_type < 0xe0) + printf(" (OEM timestamped)"); + else + printf(" (OEM non-timestamped)"); + } + printf("\n"); + + if (evt->record_type < 0xe0) + { + printf(" Timestamp : "); + if (evt->record_type < 0xc0) + printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp), + ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); + else + printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp), + ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp)); + printf("\n"); + } + + if (evt->record_type >= 0xc0) + { + if(evt->record_type < 0xdf) + { + printf (" Manufactacturer ID : %02x%02x%02x\n", evt->sel_type.oem_ts_type.manf_id[0], + evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]); + printf (" OEM Defined : "); + for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++) + printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]); + printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_ts_type.oem_defined, SEL_OEM_TS_DATA_LEN)); + } + else + { + printf (" OEM Defined : "); + for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++) + printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]); + printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_nots_type.oem_defined, SEL_OEM_NOTS_DATA_LEN)); + ipmi_sel_oem_message(evt, 1); + } + return; + } + + printf(" Generator ID : %04x\n", + evt->sel_type.standard_type.gen_id); + printf(" EvM Revision : %02x\n", + evt->sel_type.standard_type.evm_rev); + printf(" Sensor Type : %s\n", + ( + (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) + ? + ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + : + ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) + ) + ); + printf(" Sensor Number : %02x\n", + evt->sel_type.standard_type.sensor_num); + printf(" Event Type : %s\n", + ipmi_get_event_type(evt->sel_type.standard_type.event_type)); + printf(" Event Direction : %s\n", + val2str(evt->sel_type.standard_type.event_dir, event_dir_vals)); + printf(" Event Data : %02x%02x%02x\n", + evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); + ipmi_get_event_desc(intf, evt, &description); + printf(" Description : %s\n", + description ? description : ""); + free(description); + description = NULL; + + printf("\n"); +} + + +void +ipmi_sel_print_extended_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt) +{ + struct sdr_record_list * sdr; + char * description; + + if (!evt) + return; + + sdr = ipmi_sdr_find_sdr_bynumtype(intf, + evt->sel_type.standard_type.gen_id, + evt->sel_type.standard_type.sensor_num, + evt->sel_type.standard_type.sensor_type); + if (sdr == NULL) + { + ipmi_sel_print_std_entry_verbose(intf, evt); + return; + } + + printf("SEL Record ID : %04x\n", evt->record_id); + + if (evt->record_type == 0xf0) + { + printf (" Record Type : " + "Linux kernel panic (OEM record %02x)\n", + evt->record_type); + printf (" Panic string : %.11s\n\n", + (char *) evt + 5); + return; + } + + printf(" Record Type : %02x\n", evt->record_type); + if (evt->record_type < 0xe0) + { + printf(" Timestamp : "); + printf("%s %s\n", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp), + ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); + } + + + printf(" Generator ID : %04x\n", + evt->sel_type.standard_type.gen_id); + printf(" EvM Revision : %02x\n", + evt->sel_type.standard_type.evm_rev); + printf(" Sensor Type : %s\n", + ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])); + printf(" Sensor Number : %02x\n", + evt->sel_type.standard_type.sensor_num); + printf(" Event Type : %s\n", + ipmi_get_event_type(evt->sel_type.standard_type.event_type)); + printf(" Event Direction : %s\n", + val2str(evt->sel_type.standard_type.event_dir, event_dir_vals)); + printf(" Event Data (RAW) : %02x%02x%02x\n", + evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); + + /* break down event data field + * as per IPMI Spec 2.0 Table 29-6 */ + if (evt->sel_type.standard_type.event_type == 1 && sdr->type == SDR_RECORD_TYPE_FULL_SENSOR) { + /* Threshold */ + switch ((evt->sel_type.standard_type.event_data[0] >> 6) & 3) { /* EV1[7:6] */ + case 0: + /* unspecified byte 2 */ + break; + case 1: + /* trigger reading in byte 2 */ + printf(" Trigger Reading : %.3f", + sdr_convert_sensor_reading(sdr->record.full, + evt->sel_type.standard_type.event_data[1])); + /* determine units with possible modifiers */ + printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, + sdr->record.common->unit.modifier, + sdr->record.common->unit.type.base, + sdr->record.common->unit.type.modifier)); + break; + case 2: + /* oem code in byte 2 */ + printf(" OEM Data : %02x\n", + evt->sel_type.standard_type.event_data[1]); + break; + case 3: + /* sensor-specific extension code in byte 2 */ + printf(" Sensor Extension Code : %02x\n", + evt->sel_type.standard_type.event_data[1]); + break; + } + switch ((evt->sel_type.standard_type.event_data[0] >> 4) & 3) { /* EV1[5:4] */ + case 0: + /* unspecified byte 3 */ + break; + case 1: + /* trigger threshold value in byte 3 */ + printf(" Trigger Threshold : %.3f", + sdr_convert_sensor_reading(sdr->record.full, + evt->sel_type.standard_type.event_data[2])); + /* determine units with possible modifiers */ + printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, + sdr->record.common->unit.modifier, + sdr->record.common->unit.type.base, + sdr->record.common->unit.type.modifier)); + break; + case 2: + /* OEM code in byte 3 */ + printf(" OEM Data : %02x\n", + evt->sel_type.standard_type.event_data[2]); + break; + case 3: + /* sensor-specific extension code in byte 3 */ + printf(" Sensor Extension Code : %02x\n", + evt->sel_type.standard_type.event_data[2]); + break; + } + } else if (evt->sel_type.standard_type.event_type >= 0x2 && evt->sel_type.standard_type.event_type <= 0xc) { + /* Generic Discrete */ + } else if (evt->sel_type.standard_type.event_type == 0x6f) { + + /* Sensor-Specific Discrete */ + if (evt->sel_type.standard_type.sensor_type == 0xC && + evt->sel_type.standard_type.sensor_num == 0 && /**** THIS LOOK TO BE OEM ****/ + (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20) + { + /* break down memory ECC reporting if we can */ + printf(" Event Data : CPU %d DIMM %d\n", + evt->sel_type.standard_type.event_data[2] & 0x0f, + (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4); + } + else if( + evt->sel_type.standard_type.sensor_type == 0x2b && /* Version change */ + evt->sel_type.standard_type.event_data[0] == 0xC1 /* Data in Data 2 */ + ) + + { + //evt->sel_type.standard_type.event_data[1] + } + else + { + /* FIXME : Add sensor specific discrete types */ + printf(" Event Interpretation : Missing\n"); + } + } else if (evt->sel_type.standard_type.event_type >= 0x70 && evt->sel_type.standard_type.event_type <= 0x7f) { + /* OEM */ + } else { + printf(" Event Data : %02x%02x%02x\n", + evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); + } + + ipmi_get_event_desc(intf, evt, &description); + printf(" Description : %s\n", + description ? description : ""); + free(description); + description = NULL; + + printf("\n"); +} + +static int +__ipmi_sel_savelist_entries(struct ipmi_intf * intf, int count, const char * savefile, + int binary) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint16_t next_id = 0, curr_id = 0; + struct sel_event_record evt; + int n=0; + FILE * fp = NULL; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_GET_SEL_INFO; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SEL Info command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SEL Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "sel_info"); + + if (rsp->data[1] == 0 && rsp->data[2] == 0) { + lprintf(LOG_ERR, "SEL has no entries"); + return 0; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_RESERVE_SEL; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Reserve SEL command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Reserve SEL command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (count < 0) { + /** Show only the most recent 'count' records. */ + int delta; + uint16_t entries; + + req.msg.cmd = IPMI_CMD_GET_SEL_INFO; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SEL Info command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SEL Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + entries = buf2short(rsp->data + 1); + if (-count > entries) + count = -entries; + + /* Get first record. */ + next_id = ipmi_sel_get_std_entry(intf, 0, &evt); + + delta = next_id - evt.record_id; + + /* Get last record. */ + next_id = ipmi_sel_get_std_entry(intf, 0xffff, &evt); + + next_id = evt.record_id + count * delta + delta; + } + + if (savefile != NULL) { + fp = ipmi_open_file_write(savefile); + } + + while (next_id != 0xffff) { + curr_id = next_id; + lprintf(LOG_DEBUG, "SEL Next ID: %04x", curr_id); + + next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt); + if (next_id == 0) { + /* + * usually next_id of zero means end but + * retry because some hardware has quirks + * and will return 0 randomly. + */ + next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt); + if (next_id == 0) + break; + } + + if (verbose) + ipmi_sel_print_std_entry_verbose(intf, &evt); + else + ipmi_sel_print_std_entry(intf, &evt); + + if (fp != NULL) { + if (binary) + fwrite(&evt, 1, 16, fp); + else + ipmi_sel_print_event_file(intf, &evt, fp); + } + + if (++n == count) { + break; + } + } + + if (fp != NULL) + fclose(fp); + + return 0; +} + +static int +ipmi_sel_list_entries(struct ipmi_intf * intf, int count) +{ + return __ipmi_sel_savelist_entries(intf, count, NULL, 0); +} + +static int +ipmi_sel_save_entries(struct ipmi_intf * intf, int count, const char * savefile) +{ + return __ipmi_sel_savelist_entries(intf, count, savefile, 0); +} + +/* + * ipmi_sel_interpret + * + * return 0 on success, + * -1 on error + */ +static int +ipmi_sel_interpret(struct ipmi_intf *intf, unsigned long iana, + const char *readfile, const char *format) +{ + FILE *fp = 0; + struct sel_event_record evt; + char *buffer = NULL; + char *cursor = NULL; + int status = 0; + /* since the interface is not used, iana is taken from + * the command line + */ + sel_iana = iana; + if (strncmp("pps", format, 3) == 0) { + /* Parser for the following format */ + /* 0x001F: Event: at Mar 27 06:41:10 2007;from:(0x9a,0,7); + * sensor:(0xc3,119); event:0x6f(asserted): 0xA3 0x00 0x88 + * commonly found in PPS shelf managers + * Supports a tweak for hotswap events that are already interpreted. + */ + fp = ipmi_open_file(readfile, 0); + if (fp == NULL) { + lprintf(LOG_ERR, "Failed to open file '%s' for reading.", + readfile); + return (-1); + } + buffer = (char *)malloc((size_t)256); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + fclose(fp); + return (-1); + } + do { + /* Only allow complete lines to be parsed, + * hardcoded maximum line length + */ + if (fgets(buffer, 256, fp) == NULL) { + status = (-1); + break; + } + if (strlen(buffer) > 255) { + lprintf(LOG_ERR, "ipmitool: invalid entry found in file."); + continue; + } + cursor = buffer; + /* assume normal "System" event */ + evt.record_type = 2; + errno = 0; + evt.record_id = strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid record ID."); + status = (-1); + break; + } + evt.sel_type.standard_type.evm_rev = 4; + + /* FIXME: convert*/ + evt.sel_type.standard_type.timestamp; + + /* skip timestamp */ + cursor = index((const char *)cursor, ';'); + cursor++; + + /* FIXME: parse originator */ + evt.sel_type.standard_type.gen_id = 0x0020; + + /* skip originator info */ + cursor = index((const char *)cursor, ';'); + cursor++; + + /* Get sensor type */ + cursor = index((const char *)cursor, '('); + cursor++; + + errno = 0; + evt.sel_type.standard_type.sensor_type = + strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Sensor Type."); + status = (-1); + break; + } + cursor = index((const char *)cursor, ','); + cursor++; + + errno = 0; + evt.sel_type.standard_type.sensor_num = + strtol((const char *)cursor, (char **)NULL, 10); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Sensor Number."); + status = (-1); + break; + } + + /* skip to event type info */ + cursor = index((const char *)cursor, ':'); + cursor++; + + errno = 0; + evt.sel_type.standard_type.event_type= + strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Type."); + status = (-1); + break; + } + + /* skip to event dir info */ + cursor = index((const char *)cursor, '('); + cursor++; + if (*cursor == 'a') { + evt.sel_type.standard_type.event_dir = 0; + } else { + evt.sel_type.standard_type.event_dir = 1; + } + /* skip to data info */ + cursor = index((const char *)cursor, ' '); + cursor++; + + if (evt.sel_type.standard_type.sensor_type == 0xF0) { + /* got to FRU id */ + while (!isdigit(*cursor)) { + cursor++; + } + /* store FRUid */ + errno = 0; + evt.sel_type.standard_type.event_data[2] = + strtol(cursor, (char **)NULL, 10); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#2."); + status = (-1); + break; + } + + /* Get to previous state */ + cursor = index((const char *)cursor, 'M'); + cursor++; + + /* Set previous state */ + errno = 0; + evt.sel_type.standard_type.event_data[1] = + strtol(cursor, (char **)NULL, 10); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#1."); + status = (-1); + break; + } + + /* Get to current state */ + cursor = index((const char *)cursor, 'M'); + cursor++; + + /* Set current state */ + errno = 0; + evt.sel_type.standard_type.event_data[0] = + 0xA0 | strtol(cursor, (char **)NULL, 10); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#0."); + status = (-1); + break; + } + + /* skip to cause */ + cursor = index((const char *)cursor, '='); + cursor++; + errno = 0; + evt.sel_type.standard_type.event_data[1] |= + (strtol(cursor, (char **)NULL, 16)) << 4; + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#1."); + status = (-1); + break; + } + } else if (*cursor == '0') { + errno = 0; + evt.sel_type.standard_type.event_data[0] = + strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#0."); + status = (-1); + break; + } + cursor = index((const char *)cursor, ' '); + cursor++; + + errno = 0; + evt.sel_type.standard_type.event_data[1] = + strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#1."); + status = (-1); + break; + } + + cursor = index((const char *)cursor, ' '); + cursor++; + + errno = 0; + evt.sel_type.standard_type.event_data[2] = + strtol((const char *)cursor, (char **)NULL, 16); + if (errno != 0) { + lprintf(LOG_ERR, "Invalid Event Data#2."); + status = (-1); + break; + } + } else { + lprintf(LOG_ERR, "ipmitool: can't guess format."); + } + /* parse the PPS line into a sel_event_record */ + if (verbose) { + ipmi_sel_print_std_entry_verbose(intf, &evt); + } else { + ipmi_sel_print_std_entry(intf, &evt); + } + cursor = NULL; + } while (status == 0); /* until file is completely read */ + cursor = NULL; + free(buffer); + buffer = NULL; + fclose(fp); + } else { + lprintf(LOG_ERR, "Given format '%s' is unknown.", format); + status = (-1); + } + return status; +} + + +static int +ipmi_sel_writeraw(struct ipmi_intf * intf, const char * savefile) +{ + return __ipmi_sel_savelist_entries(intf, 0, savefile, 1); +} + + +static int +ipmi_sel_readraw(struct ipmi_intf * intf, const char * inputfile) +{ + struct sel_event_record evt; + int ret = 0; + FILE* fp = 0; + + fp = ipmi_open_file(inputfile, 0); + if (fp) + { + size_t bytesRead; + + do { + if ((bytesRead = fread(&evt, 1, 16, fp)) == 16) + { + if (verbose) + ipmi_sel_print_std_entry_verbose(intf, &evt); + else + ipmi_sel_print_std_entry(intf, &evt); + } + else + { + if (bytesRead != 0) + { + lprintf(LOG_ERR, "ipmitool: incomplete record found in file."); + ret = -1; + } + + break; + } + + } while (1); + fclose(fp); + } + else + { + lprintf(LOG_ERR, "ipmitool: could not open input file."); + ret = -1; + } + return ret; +} + + + +static uint16_t +ipmi_sel_reserve(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_RESERVE_SEL; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_WARN, "Unable to reserve SEL"); + return 0; + } + if (rsp->ccode > 0) { + printf("Unable to reserve SEL: %s", + val2str(rsp->ccode, completion_code_vals)); + return 0; + } + + return (rsp->data[0] | (rsp->data[1] << 8)); +} + + + +/* + * ipmi_sel_get_time + * + * return 0 on success, + * -1 on error + */ +static int +ipmi_sel_get_time(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + static char tbuf[40]; + uint32_t timei; + time_t time; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_GET_SEL_TIME; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Get SEL Time command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get SEL Time command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + if (rsp->data_len != 4) { + lprintf(LOG_ERR, "Get SEL Time command failed: " + "Invalid data length %d", rsp->data_len); + return -1; + } + + memcpy(&timei, rsp->data, 4); +#if WORDS_BIGENDIAN + time = (time_t)(BSWAP_32(timei)); +#else + time = (time_t)timei; +#endif + + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&time)); + printf("%s\n", tbuf); + + return 0; +} + + + +/* + * ipmi_sel_set_time + * + * return 0 on success, + * -1 on error + */ +static int +ipmi_sel_set_time(struct ipmi_intf * intf, const char * time_string) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct tm tm = {0}; + time_t t; + uint32_t timei; + const char * time_format = "%m/%d/%Y %H:%M:%S"; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_SET_SEL_TIME; + + /* See if user requested set to current client system time */ + if (strncasecmp(time_string, "now", 3) == 0) { + t = time(NULL); + } + else { + /* Now how do we get our time_t from our ascii version? */ + if (strptime(time_string, time_format, &tm) == 0) { + lprintf(LOG_ERR, "Specified time could not be parsed"); + return -1; + } + tm.tm_isdst = (-1); /* look up DST information */ + t = mktime(&tm); + if (t < 0) { + lprintf(LOG_ERR, "Specified time could not be parsed"); + return -1; + } + } + + { + //modify UTC time to local time expressed in number of seconds from 1/1/70 0:0:0 1970 GMT + struct tm * tm_tmp = {0}; + int gt_year,gt_yday,gt_hour,lt_year,lt_yday,lt_hour; + int delta_hour; + tm_tmp=gmtime(&t); + gt_year=tm_tmp->tm_year; + gt_yday=tm_tmp->tm_yday; + gt_hour=tm_tmp->tm_hour; + memset(&*tm_tmp, 0, sizeof(struct tm)); + tm_tmp=localtime(&t); + lt_year=tm_tmp->tm_year; + lt_yday=tm_tmp->tm_yday; + lt_hour=tm_tmp->tm_hour; + delta_hour=lt_hour - gt_hour; + if ( (lt_year > gt_year) || ((lt_year == gt_year) && (lt_yday > gt_yday)) ) + delta_hour += 24; + if ( (lt_year < gt_year) || ((lt_year == gt_year) && (lt_yday < gt_yday)) ) + delta_hour -= 24; + + t += (delta_hour * 60 * 60); + } + + timei = (uint32_t)t; + req.msg.data = (uint8_t *)&timei; + req.msg.data_len = 4; + +#if WORDS_BIGENDIAN + timei = BSWAP_32(timei); +#endif + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set SEL Time command failed"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set SEL Time command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + ipmi_sel_get_time(intf); + + return 0; +} + + + +static int +ipmi_sel_clear(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint16_t reserve_id; + uint8_t msg_data[6]; + + reserve_id = ipmi_sel_reserve(intf); + if (reserve_id == 0) + return -1; + + memset(msg_data, 0, 6); + msg_data[0] = reserve_id & 0xff; + msg_data[1] = reserve_id >> 8; + msg_data[2] = 'C'; + msg_data[3] = 'L'; + msg_data[4] = 'R'; + msg_data[5] = 0xaa; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_CLEAR_SEL; + req.msg.data = msg_data; + req.msg.data_len = 6; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to clear SEL"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to clear SEL: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("Clearing SEL. Please allow a few seconds to erase.\n"); + return 0; +} + +static int +ipmi_sel_delete(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint16_t id; + uint8_t msg_data[4]; + int rc = 0; + + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_ERR, "usage: delete <id>...<id>\n"); + return -1; + } + + id = ipmi_sel_reserve(intf); + if (id == 0) + return -1; + + memset(msg_data, 0, 4); + msg_data[0] = id & 0xff; + msg_data[1] = id >> 8; + + for (; argc != 0; argc--) + { + id = (uint16_t) strtoul(argv[argc-1], NULL, 0); + if (str2ushort(argv[argc-1], &id) != 0) { + lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.", + argv[argc-1]); + rc = (-1); + continue; + } + msg_data[2] = id & 0xff; + msg_data[3] = id >> 8; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_STORAGE; + req.msg.cmd = IPMI_CMD_DELETE_SEL_ENTRY; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to delete entry %d", id); + rc = -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to delete entry %d: %s", id, + val2str(rsp->ccode, completion_code_vals)); + rc = -1; + } + else { + printf("Deleted entry %d\n", id); + } + } + + return rc; +} + +static int +ipmi_sel_show_entry(struct ipmi_intf * intf, int argc, char ** argv) +{ + uint16_t id; + int i, oldv; + struct sel_event_record evt; + struct sdr_record_list * sdr; + struct entity_id entity; + struct sdr_record_list * list, * entry; + int rc = 0; + + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_ERR, "usage: sel get <id>...<id>"); + return -1; + } + + if (ipmi_sel_reserve(intf) == 0) { + lprintf(LOG_ERR, "Unable to reserve SEL"); + return -1; + } + + for (i=0; i<argc; i++) { + if (str2ushort(argv[i], &id) != 0) { + lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.", + argv[i]); + rc = (-1); + continue; + } + + lprintf(LOG_DEBUG, "Looking up SEL entry 0x%x", id); + + /* lookup SEL entry based on ID */ + if (!ipmi_sel_get_std_entry(intf, id, &evt)) { + lprintf(LOG_DEBUG, "SEL Entry 0x%x not found.", id); + rc = (-1); + continue; + } + if (evt.sel_type.standard_type.sensor_num == 0 && evt.sel_type.standard_type.sensor_type == 0 && evt.record_type == 0) { + lprintf(LOG_WARN, "SEL Entry 0x%x not found", id); + rc = -1; + continue; + } + + /* lookup SDR entry based on sensor number and type */ + ipmi_sel_print_extended_entry_verbose(intf, &evt); + + sdr = ipmi_sdr_find_sdr_bynumtype(intf, evt.sel_type.standard_type.gen_id, evt.sel_type.standard_type.sensor_num, evt.sel_type.standard_type.sensor_type); + if (sdr == NULL) { + continue; + } + + /* print SDR entry */ + oldv = verbose; + verbose = verbose ? : 1; + switch (sdr->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + ipmi_sensor_print_fc(intf, sdr->record.common, + sdr->type); + entity.id = sdr->record.common->entity.id; + entity.instance = sdr->record.common->entity.instance; + break; + case SDR_RECORD_TYPE_EVENTONLY_SENSOR: + ipmi_sdr_print_sensor_eventonly(intf, sdr->record.eventonly); + entity.id = sdr->record.eventonly->entity.id; + entity.instance = sdr->record.eventonly->entity.instance; + break; + default: + verbose = oldv; + continue; + } + verbose = oldv; + + /* lookup SDR entry based on entity id */ + list = ipmi_sdr_find_sdr_byentity(intf, &entity); + for (entry=list; entry; entry=entry->next) { + /* print FRU devices we find for this entity */ + if (entry->type == SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR) + ipmi_fru_print(intf, entry->record.fruloc); + } + + if ((argc > 1) && (i<(argc-1))) + printf("----------------------\n\n"); + } + + return rc; +} + +int ipmi_sel_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc == 0) + rc = ipmi_sel_get_info(intf); + else if (strncmp(argv[0], "help", 4) == 0) + lprintf(LOG_ERR, "SEL Commands: " + "info clear delete list elist get add time save readraw writeraw interpret"); + else if (strncmp(argv[0], "interpret", 9) == 0) { + uint32_t iana = 0; + if (argc < 4) { + lprintf(LOG_NOTICE, "usage: sel interpret iana filename format(pps)"); + return 0; + } + if (str2uint(argv[1], &iana) != 0) { + lprintf(LOG_ERR, "Given IANA '%s' is invalid.", + argv[1]); + return (-1); + } + rc = ipmi_sel_interpret(intf, iana, argv[2], argv[3]); + } + else if (strncmp(argv[0], "info", 4) == 0) + rc = ipmi_sel_get_info(intf); + else if (strncmp(argv[0], "save", 4) == 0) { + if (argc < 2) { + lprintf(LOG_NOTICE, "usage: sel save <filename>"); + return 0; + } + rc = ipmi_sel_save_entries(intf, 0, argv[1]); + } + else if (strncmp(argv[0], "add", 3) == 0) { + if (argc < 2) { + lprintf(LOG_NOTICE, "usage: sel add <filename>"); + return 0; + } + rc = ipmi_sel_add_entries_fromfile(intf, argv[1]); + } + else if (strncmp(argv[0], "writeraw", 8) == 0) { + if (argc < 2) { + lprintf(LOG_NOTICE, "usage: sel writeraw <filename>"); + return 0; + } + rc = ipmi_sel_writeraw(intf, argv[1]); + } + else if (strncmp(argv[0], "readraw", 7) == 0) { + if (argc < 2) { + lprintf(LOG_NOTICE, "usage: sel readraw <filename>"); + return 0; + } + rc = ipmi_sel_readraw(intf, argv[1]); + } + else if (strncmp(argv[0], "ereadraw", 8) == 0) { + if (argc < 2) { + lprintf(LOG_NOTICE, "usage: sel ereadraw <filename>"); + return 0; + } + sel_extended = 1; + rc = ipmi_sel_readraw(intf, argv[1]); + } + else if (strncmp(argv[0], "list", 4) == 0 || + strncmp(argv[0], "elist", 5) == 0) { + /* + * Usage: + * list - show all SEL entries + * list first <n> - show the first (oldest) <n> SEL entries + * list last <n> - show the last (newsest) <n> SEL entries + */ + int count = 0; + int sign = 1; + char *countstr = NULL; + + if (strncmp(argv[0], "elist", 5) == 0) + sel_extended = 1; + else + sel_extended = 0; + + if (argc == 2) { + countstr = argv[1]; + } + else if (argc == 3) { + countstr = argv[2]; + + if (strncmp(argv[1], "last", 4) == 0) { + sign = -1; + } + else if (strncmp(argv[1], "first", 5) != 0) { + lprintf(LOG_ERR, "Unknown sel list option"); + return -1; + } + } + + if (countstr) { + if (str2int(countstr, &count) != 0) { + lprintf(LOG_ERR, "Numeric argument required; got '%s'", + countstr); + return -1; + } + } + count *= sign; + + rc = ipmi_sel_list_entries(intf,count); + } + else if (strncmp(argv[0], "clear", 5) == 0) + rc = ipmi_sel_clear(intf); + else if (strncmp(argv[0], "delete", 6) == 0) { + if (argc < 2) + lprintf(LOG_ERR, "usage: sel delete <id>...<id>"); + else + rc = ipmi_sel_delete(intf, argc-1, &argv[1]); + } + else if (strncmp(argv[0], "get", 3) == 0) { + if (argc < 2) + lprintf(LOG_ERR, "usage: sel get <entry>"); + else + rc = ipmi_sel_show_entry(intf, argc-1, &argv[1]); + } + else if (strncmp(argv[0], "time", 4) == 0) { + if (argc < 2) + lprintf(LOG_ERR, "sel time commands: get set"); + else if (strncmp(argv[1], "get", 3) == 0) + ipmi_sel_get_time(intf); + else if (strncmp(argv[1], "set", 3) == 0) { + if (argc < 3) + lprintf(LOG_ERR, "usage: sel time set \"mm/dd/yyyy hh:mm:ss\""); + else + rc = ipmi_sel_set_time(intf, argv[2]); + } else { + lprintf(LOG_ERR, "sel time commands: get set"); + } + } + else { + lprintf(LOG_ERR, "Invalid SEL command: %s", argv[0]); + rc = -1; + } + + return rc; +} diff --git a/lib/ipmi_sensor.c b/lib/ipmi_sensor.c new file mode 100644 index 0000000..4ef5138 --- /dev/null +++ b/lib/ipmi_sensor.c @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <math.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_sensor.h> + +extern int verbose; +void printf_sensor_get_usage(); + +// Macro's for Reading the current sensor Data. +#define SCANNING_DISABLED 0x40 +#define READING_UNAVAILABLE 0x20 +#define INVALID_THRESHOLD "Invalid Threshold data values. Cannot Set Threshold Data." +// static +int +ipmi_sensor_get_sensor_reading_factors( + struct ipmi_intf * intf, + struct sdr_record_full_sensor * sensor, + uint8_t reading) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + uint8_t req_data[2]; + + char id[17]; + + if (intf == NULL || sensor == NULL) + return -1; + + memset(id, 0, sizeof(id)); + memcpy(id, sensor->id_string, 16); + + req_data[0] = sensor->cmn.keys.sensor_num; + req_data[1] = reading; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = sensor->cmn.keys.lun; + req.msg.cmd = GET_SENSOR_FACTORS; + req.msg.data = req_data; + req.msg.data_len = sizeof(req_data); + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Error updating reading factor for sensor %s (#%02x)", + id, sensor->cmn.keys.sensor_num); + return -1; + } else if (rsp->ccode) { + return -1; + } else { + /* Update SDR copy with updated Reading Factors for this reading */ + /* Note: + * The Format of the returned data is exactly as in the SDR definition (Little Endian Format), + * therefore we can use raw copy operation here. + * Note: rsp->data[0] would point to the next valid entry in the sampling table + */ + // BUGBUG: uses 'hardcoded' length information from SDR Definition + memcpy(&sensor->mtol, &rsp->data[1], sizeof(sensor->mtol)); + memcpy(&sensor->bacc, &rsp->data[3], sizeof(sensor->bacc)); + return 0; + } + +} + +static +struct ipmi_rs * +ipmi_sensor_set_sensor_thresholds(struct ipmi_intf *intf, + uint8_t sensor, + uint8_t threshold, uint8_t setting, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rq req; + static struct sensor_set_thresh_rq set_thresh_rq; + struct ipmi_rs *rsp; + uint8_t bridged_request = 0; + uint32_t save_addr; + uint32_t save_channel; + + memset(&set_thresh_rq, 0, sizeof (set_thresh_rq)); + set_thresh_rq.sensor_num = sensor; + set_thresh_rq.set_mask = threshold; + if (threshold == UPPER_NON_RECOV_SPECIFIED) + set_thresh_rq.upper_non_recov = setting; + else if (threshold == UPPER_CRIT_SPECIFIED) + set_thresh_rq.upper_crit = setting; + else if (threshold == UPPER_NON_CRIT_SPECIFIED) + set_thresh_rq.upper_non_crit = setting; + else if (threshold == LOWER_NON_CRIT_SPECIFIED) + set_thresh_rq.lower_non_crit = setting; + else if (threshold == LOWER_CRIT_SPECIFIED) + set_thresh_rq.lower_crit = setting; + else if (threshold == LOWER_NON_RECOV_SPECIFIED) + set_thresh_rq.lower_non_recov = setting; + else + return NULL; + + if (BRIDGE_TO_SENSOR(intf, target, channel)) { + bridged_request = 1; + save_addr = intf->target_addr; + intf->target_addr = target; + save_channel = intf->target_channel; + intf->target_channel = channel; + } + memset(&req, 0, sizeof (req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.lun = lun; + req.msg.cmd = SET_SENSOR_THRESHOLDS; + req.msg.data = (uint8_t *) & set_thresh_rq; + req.msg.data_len = sizeof (set_thresh_rq); + + rsp = intf->sendrecv(intf, &req); + if (bridged_request) { + intf->target_addr = save_addr; + intf->target_channel = save_channel; + } + return rsp; +} + +static int +ipmi_sensor_print_fc_discrete(struct ipmi_intf *intf, + struct sdr_record_common_sensor *sensor, + uint8_t sdr_record_type) +{ + const char *id; + struct sensor_reading *sr; + + sr = ipmi_sdr_read_sensor_value(intf, sensor, sdr_record_type, 3); + + if (sr == NULL) { + return -1; + } + + if (csv_output) { + /* NOT IMPLEMENTED */ + } else { + if (verbose == 0) { + /* output format + * id value units status thresholds.... + */ + printf("%-16s ", sr->s_id); + if (sr->s_reading_valid) { + if (sr->s_has_analog_value) { + /* don't show discrete component */ + printf("| %-10s | %-10s | %-6s", + sr->s_a_str, sr->s_a_units, "ok"); + } else { + printf("| 0x%-8x | %-10s | 0x%02x%02x", + sr->s_reading, "discrete", + sr->s_data2, sr->s_data3); + } + } else { + printf("| %-10s | %-10s | %-6s", + "na", "discrete", "na"); + } + printf("| %-10s| %-10s| %-10s| %-10s| %-10s| %-10s", + "na", "na", "na", "na", "na", "na"); + + printf("\n"); + } else { + printf("Sensor ID : %s (0x%x)\n", + sr->s_id, sensor->keys.sensor_num); + printf(" Entity ID : %d.%d\n", + sensor->entity.id, sensor->entity.instance); + printf(" Sensor Type (Discrete): %s\n", + ipmi_sdr_get_sensor_type_desc(sensor->sensor. + type)); + if( sr->s_reading_valid ) + { + if (sr->s_has_analog_value) { + printf(" Sensor Reading : %s %s\n", sr->s_a_str, sr->s_a_units); + } + ipmi_sdr_print_discrete_state("States Asserted", + sensor->sensor.type, + sensor->event_type, + sr->s_data2, + sr->s_data3); + printf("\n"); + } else { + printf(" Unable to read sensor: Device Not Present\n\n"); + } + } + } + + return (sr->s_reading_valid ? 0 : -1 ); +} + +static void +print_thresh_setting(struct sdr_record_full_sensor *full, + uint8_t thresh_is_avail, uint8_t setting, + const char *field_sep, + const char *analog_fmt, + const char *discrete_fmt, + const char *na_fmt) +{ + printf("%s", field_sep); + if (!thresh_is_avail) { + printf(na_fmt, "na"); + return; + } + if (full && !UNITS_ARE_DISCRETE(&full->cmn)) { + printf(analog_fmt, sdr_convert_sensor_reading (full, setting)); + } else { + printf(discrete_fmt, setting); + } +} + +static int +ipmi_sensor_print_fc_threshold(struct ipmi_intf *intf, + struct sdr_record_common_sensor *sensor, + uint8_t sdr_record_type) +{ + int thresh_available = 1; + struct ipmi_rs *rsp; + struct sensor_reading *sr; + + sr = ipmi_sdr_read_sensor_value(intf, sensor, sdr_record_type, 3); + + if (sr == NULL) { + return -1; + } + + const char *thresh_status = ipmi_sdr_get_thresh_status(sr, "ns"); + + /* + * Get sensor thresholds + */ + rsp = ipmi_sdr_get_sensor_thresholds(intf, + sensor->keys.sensor_num, sensor->keys.owner_id, + sensor->keys.lun, sensor->keys.channel); + + if ((rsp == NULL) || (rsp->ccode > 0) || (rsp->data_len == 0)) + thresh_available = 0; + + if (csv_output) { + /* NOT IMPLEMENTED */ + } else { + if (verbose == 0) { + /* output format + * id value units status thresholds.... + */ + printf("%-16s ", sr->s_id); + if (sr->s_reading_valid) { + if (sr->s_has_analog_value) + printf("| %-10.3f | %-10s | %-6s", + sr->s_a_val, sr->s_a_units, thresh_status); + else + printf("| 0x%-8x | %-10s | %-6s", + sr->s_reading, sr->s_a_units, thresh_status); + } else { + printf("| %-10s | %-10s | %-6s", + "na", sr->s_a_units, "na"); + } + if (thresh_available && sr->full) { +#define PTS(bit, dataidx) { \ + print_thresh_setting(sr->full, rsp->data[0] & (bit), \ + rsp->data[(dataidx)], "| ", "%-10.3f", "0x-8x", "%-10s"); \ +} + PTS(LOWER_NON_RECOV_SPECIFIED, 3); + PTS(LOWER_CRIT_SPECIFIED, 2); + PTS(LOWER_NON_CRIT_SPECIFIED, 1); + PTS(UPPER_NON_CRIT_SPECIFIED, 4); + PTS(UPPER_CRIT_SPECIFIED, 5); + PTS(UPPER_NON_RECOV_SPECIFIED, 6); +#undef PTS + } else { + printf + ("| %-10s| %-10s| %-10s| %-10s| %-10s| %-10s", + "na", "na", "na", "na", "na", "na"); + } + + printf("\n"); + } else { + printf("Sensor ID : %s (0x%x)\n", + sr->s_id, sensor->keys.sensor_num); + + printf(" Entity ID : %d.%d\n", + sensor->entity.id, sensor->entity.instance); + + printf(" Sensor Type (Threshold) : %s\n", + ipmi_sdr_get_sensor_type_desc(sensor->sensor. + type)); + + printf(" Sensor Reading : "); + if (sr->s_reading_valid) { + if (sr->full) { + uint16_t raw_tol = __TO_TOL(sr->full->mtol); + if (sr->s_has_analog_value) { + double tol = + sdr_convert_sensor_tolerance(sr->full, + raw_tol); + printf("%.*f (+/- %.*f) %s\n", + (sr->s_a_val == (int) + sr->s_a_val) ? 0 : 3, + sr->s_a_val, + (tol == (int) tol) ? 0 : 3, tol, + sr->s_a_units); + } else { + printf("0x%x (+/- 0x%x) %s\n", + sr->s_reading, + raw_tol, + sr->s_a_units); + } + } else { + printf("0x%x %s\n", sr->s_reading, + sr->s_a_units); + } + printf(" Status : %s\n", thresh_status); + + if (thresh_available) { + if (sr->full) { +#define PTS(bit, dataidx, str) { \ +print_thresh_setting(sr->full, rsp->data[0] & (bit), \ + rsp->data[(dataidx)], \ + (str), "%.3f\n", "0x%x\n", "%s\n"); \ +} + + PTS(LOWER_NON_RECOV_SPECIFIED, 3, " Lower Non-Recoverable : "); + PTS(LOWER_CRIT_SPECIFIED, 2, " Lower Critical : "); + PTS(LOWER_NON_CRIT_SPECIFIED, 1, " Lower Non-Critical : "); + PTS(UPPER_NON_CRIT_SPECIFIED, 4, " Upper Non-Critical : "); + PTS(UPPER_CRIT_SPECIFIED, 5, " Upper Critical : "); + PTS(UPPER_NON_RECOV_SPECIFIED, 6, " Upper Non-Recoverable : "); +#undef PTS + + } + ipmi_sdr_print_sensor_hysteresis(sensor, sr->full, + sr->full ? sr->full->threshold.hysteresis.positive : + sr->compact->threshold.hysteresis.positive, + "Positive Hysteresis"); + + ipmi_sdr_print_sensor_hysteresis(sensor, sr->full, + sr->full ? sr->full->threshold.hysteresis.negative : + sr->compact->threshold.hysteresis.negative, + "Negative Hysteresis"); + } else { + printf(" Sensor Threshold Settings not available\n"); + } + } else { + printf(" Unable to read sensor: Device Not Present\n\n"); + } + + ipmi_sdr_print_sensor_event_status(intf, + sensor->keys. + sensor_num, + sensor->sensor.type, + sensor->event_type, + ANALOG_SENSOR, + sensor->keys.owner_id, + sensor->keys.lun, + sensor->keys.channel); + ipmi_sdr_print_sensor_event_enable(intf, + sensor->keys. + sensor_num, + sensor->sensor.type, + sensor->event_type, + ANALOG_SENSOR, + sensor->keys.owner_id, + sensor->keys.lun, + sensor->keys.channel); + + printf("\n"); + } + } + + return (sr->s_reading_valid ? 0 : -1 ); +} + +int +ipmi_sensor_print_fc(struct ipmi_intf *intf, + struct sdr_record_common_sensor *sensor, + uint8_t sdr_record_type) +{ + if (IS_THRESHOLD_SENSOR(sensor)) + return ipmi_sensor_print_fc_threshold(intf, sensor, sdr_record_type); + else + return ipmi_sensor_print_fc_discrete(intf, sensor, sdr_record_type); +} + +static int +ipmi_sensor_list(struct ipmi_intf *intf) +{ + struct sdr_get_rs *header; + struct ipmi_sdr_iterator *itr; + int rc = 0; + + lprintf(LOG_DEBUG, "Querying SDR for sensor list"); + + itr = ipmi_sdr_start(intf, 0); + if (itr == NULL) { + lprintf(LOG_ERR, "Unable to open SDR for reading"); + return -1; + } + + while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) { + uint8_t *rec; + + rec = ipmi_sdr_get_record(intf, header, itr); + if (rec == NULL) { + lprintf(LOG_DEBUG, "rec == NULL"); + continue; + } + + switch (header->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + ipmi_sensor_print_fc(intf, + (struct + sdr_record_common_sensor *) + rec, + header->type); + break; + } + free(rec); + rec = NULL; + + /* fix for CR6604909: */ + /* mask failure of individual reads in sensor list command */ + /* rc = (r == 0) ? rc : r; */ + } + + ipmi_sdr_end(intf, itr); + + return rc; +} + +static const struct valstr threshold_vals[] = { + {UPPER_NON_RECOV_SPECIFIED, "Upper Non-Recoverable"}, + {UPPER_CRIT_SPECIFIED, "Upper Critical"}, + {UPPER_NON_CRIT_SPECIFIED, "Upper Non-Critical"}, + {LOWER_NON_RECOV_SPECIFIED, "Lower Non-Recoverable"}, + {LOWER_CRIT_SPECIFIED, "Lower Critical"}, + {LOWER_NON_CRIT_SPECIFIED, "Lower Non-Critical"}, + {0x00, NULL}, +}; + +static int +__ipmi_sensor_set_threshold(struct ipmi_intf *intf, + uint8_t num, uint8_t mask, uint8_t setting, + uint8_t target, uint8_t lun, uint8_t channel) +{ + struct ipmi_rs *rsp; + + rsp = ipmi_sensor_set_sensor_thresholds(intf, num, mask, setting, + target, lun, channel); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting threshold"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error setting threshold: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static uint8_t +__ipmi_sensor_threshold_value_to_raw(struct sdr_record_full_sensor *full, double value) +{ + if (!UNITS_ARE_DISCRETE(&full->cmn)) { /* Has an analog reading */ + /* Has an analog reading and supports mx+b */ + return sdr_convert_sensor_value_to_raw(full, value); + } + else { + /* Does not have an analog reading and/or does not support mx+b */ + if (value > 255) { + return 255; + } + else if (value < 0) { + return 0; + } + else { + return (uint8_t )value; + } + } +} + + +static int +ipmi_sensor_set_threshold(struct ipmi_intf *intf, int argc, char **argv) +{ + char *id, *thresh; + uint8_t settingMask = 0; + double setting1 = 0.0, setting2 = 0.0, setting3 = 0.0; + int allUpper = 0, allLower = 0; + int ret = 0; + struct ipmi_rs *rsp; + int i =0; + double val[10] = {0}; + + struct sdr_record_list *sdr; + + if (argc < 3 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_NOTICE, "sensor thresh <id> <threshold> <setting>"); + lprintf(LOG_NOTICE, + " id : name of the sensor for which threshold is to be set"); + lprintf(LOG_NOTICE, " threshold : which threshold to set"); + lprintf(LOG_NOTICE, + " unr = upper non-recoverable"); + lprintf(LOG_NOTICE, " ucr = upper critical"); + lprintf(LOG_NOTICE, + " unc = upper non-critical"); + lprintf(LOG_NOTICE, + " lnc = lower non-critical"); + lprintf(LOG_NOTICE, " lcr = lower critical"); + lprintf(LOG_NOTICE, + " lnr = lower non-recoverable"); + lprintf(LOG_NOTICE, + " setting : the value to set the threshold to"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, + "sensor thresh <id> lower <lnr> <lcr> <lnc>"); + lprintf(LOG_NOTICE, + " Set all lower thresholds at the same time"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, + "sensor thresh <id> upper <unc> <ucr> <unr>"); + lprintf(LOG_NOTICE, + " Set all upper thresholds at the same time"); + lprintf(LOG_NOTICE, ""); + return 0; + } + + id = argv[0]; + thresh = argv[1]; + + if (strncmp(thresh, "upper", 5) == 0) { + if (argc < 5) { + lprintf(LOG_ERR, + "usage: sensor thresh <id> upper <unc> <ucr> <unr>"); + return -1; + } + allUpper = 1; + if (str2double(argv[2], &setting1) != 0) { + lprintf(LOG_ERR, "Given unc '%s' is invalid.", + argv[2]); + return (-1); + } + if (str2double(argv[3], &setting2) != 0) { + lprintf(LOG_ERR, "Given ucr '%s' is invalid.", + argv[3]); + return (-1); + } + if (str2double(argv[4], &setting3) != 0) { + lprintf(LOG_ERR, "Given unr '%s' is invalid.", + argv[4]); + return (-1); + } + } else if (strncmp(thresh, "lower", 5) == 0) { + if (argc < 5) { + lprintf(LOG_ERR, + "usage: sensor thresh <id> lower <unc> <ucr> <unr>"); + return -1; + } + allLower = 1; + if (str2double(argv[2], &setting1) != 0) { + lprintf(LOG_ERR, "Given lnc '%s' is invalid.", + argv[2]); + return (-1); + } + if (str2double(argv[3], &setting2) != 0) { + lprintf(LOG_ERR, "Given lcr '%s' is invalid.", + argv[3]); + return (-1); + } + if (str2double(argv[4], &setting3) != 0) { + lprintf(LOG_ERR, "Given lnr '%s' is invalid.", + argv[4]); + return (-1); + } + } else { + if (strncmp(thresh, "unr", 3) == 0) + settingMask = UPPER_NON_RECOV_SPECIFIED; + else if (strncmp(thresh, "ucr", 3) == 0) + settingMask = UPPER_CRIT_SPECIFIED; + else if (strncmp(thresh, "unc", 3) == 0) + settingMask = UPPER_NON_CRIT_SPECIFIED; + else if (strncmp(thresh, "lnc", 3) == 0) + settingMask = LOWER_NON_CRIT_SPECIFIED; + else if (strncmp(thresh, "lcr", 3) == 0) + settingMask = LOWER_CRIT_SPECIFIED; + else if (strncmp(thresh, "lnr", 3) == 0) + settingMask = LOWER_NON_RECOV_SPECIFIED; + else { + lprintf(LOG_ERR, + "Valid threshold '%s' for sensor '%s' not specified!", + thresh, id); + return -1; + } + if (str2double(argv[2], &setting1) != 0) { + lprintf(LOG_ERR, + "Given %s threshold value '%s' is invalid.", + thresh, argv[2]); + return (-1); + } + } + + printf("Locating sensor record '%s'...\n", id); + + /* lookup by sensor name */ + sdr = ipmi_sdr_find_sdr_byid(intf, id); + if (sdr == NULL) { + lprintf(LOG_ERR, "Sensor data record not found!"); + return -1; + } + + if (sdr->type != SDR_RECORD_TYPE_FULL_SENSOR) { + lprintf(LOG_ERR, "Invalid sensor type %02x", sdr->type); + return -1; + } + + if (!IS_THRESHOLD_SENSOR(sdr->record.common)) { + lprintf(LOG_ERR, "Invalid sensor event type %02x", sdr->record.common->event_type); + return -1; + } + + + if (allUpper) { + settingMask = UPPER_NON_CRIT_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting1); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting1), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + + settingMask = UPPER_CRIT_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting2); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting2), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + + settingMask = UPPER_NON_RECOV_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting3); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting3), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + } else if (allLower) { + settingMask = LOWER_NON_RECOV_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting1); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting1), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + + settingMask = LOWER_CRIT_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting2); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting2), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + + settingMask = LOWER_NON_CRIT_SPECIFIED; + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting3); + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting3), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + } else { + + /* + * Current implementation doesn't check for the valid setting of upper non critical and other thresholds. + * In the below logic: + * Get all the current reading of the sensor i.e. unc, uc, lc,lnc. + * Validate the values given by the user. + * If the values are not correct, then popup with the Error message and return. + */ + /* + * Get current reading + */ + rsp = ipmi_sdr_get_sensor_reading_ipmb(intf, + sdr->record.common->keys.sensor_num, + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun,sdr->record.common->keys.channel); + rsp = ipmi_sdr_get_sensor_thresholds(intf, + sdr->record.common->keys.sensor_num, + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + if ((rsp == NULL) || (rsp->ccode > 0)) { + lprintf(LOG_ERR, "Sensor data record not found!"); + return -1; + } + for(i=1;i<=6;i++) { + val[i] = sdr_convert_sensor_reading(sdr->record.full, rsp->data[i]); + if(val[i] < 0) + val[i] = 0; + } + /* Check for the valid Upper non recovarable Value.*/ + if( (settingMask & UPPER_NON_RECOV_SPECIFIED) ) { + + if( (rsp->data[0] & UPPER_NON_RECOV_SPECIFIED) && + (( (rsp->data[0] & UPPER_CRIT_SPECIFIED) && ( setting1 <= val[5])) || + ( (rsp->data[0] & UPPER_NON_CRIT_SPECIFIED) && ( setting1 <= val[4]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else if( (settingMask & UPPER_CRIT_SPECIFIED) ) { /* Check for the valid Upper critical Value.*/ + if( (rsp->data[0] & UPPER_CRIT_SPECIFIED) && + (((rsp->data[0] & UPPER_NON_RECOV_SPECIFIED)&& ( setting1 >= val[6])) || + ((rsp->data[0] & UPPER_NON_CRIT_SPECIFIED)&&( setting1 <= val[4]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else if( (settingMask & UPPER_NON_CRIT_SPECIFIED) ) { /* Check for the valid Upper non critical Value.*/ + if( (rsp->data[0] & UPPER_NON_CRIT_SPECIFIED) && + (((rsp->data[0] & UPPER_NON_RECOV_SPECIFIED)&&( setting1 >= val[6])) || + ((rsp->data[0] & UPPER_CRIT_SPECIFIED)&&( setting1 >= val[5])) || + ((rsp->data[0] & LOWER_NON_CRIT_SPECIFIED)&&( setting1 <= val[1]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else if( (settingMask & LOWER_NON_CRIT_SPECIFIED) ) { /* Check for the valid lower non critical Value.*/ + if( (rsp->data[0] & LOWER_NON_CRIT_SPECIFIED) && + (((rsp->data[0] & LOWER_CRIT_SPECIFIED)&&( setting1 <= val[2])) || + ((rsp->data[0] & LOWER_NON_RECOV_SPECIFIED)&&( setting1 <= val[3]))|| + ((rsp->data[0] & UPPER_NON_CRIT_SPECIFIED)&&( setting1 >= val[4]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else if( (settingMask & LOWER_CRIT_SPECIFIED) ) { /* Check for the valid lower critical Value.*/ + if( (rsp->data[0] & LOWER_CRIT_SPECIFIED) && + (((rsp->data[0] & LOWER_NON_CRIT_SPECIFIED)&&( setting1 >= val[1])) || + ((rsp->data[0] & LOWER_NON_RECOV_SPECIFIED)&&( setting1 <= val[3]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else if( (settingMask & LOWER_NON_RECOV_SPECIFIED) ) { /* Check for the valid lower non recovarable Value.*/ + if( (rsp->data[0] & LOWER_NON_RECOV_SPECIFIED) && + (((rsp->data[0] & LOWER_NON_CRIT_SPECIFIED)&&( setting1 >= val[1])) || + ((rsp->data[0] & LOWER_CRIT_SPECIFIED)&&( setting1 >= val[2]))) ) + { + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + } else { /* None of this Then Return with error messages.*/ + lprintf(LOG_ERR, INVALID_THRESHOLD); + return -1; + } + + + printf("Setting sensor \"%s\" %s threshold to %.3f\n", + sdr->record.full->id_string, + val2str(settingMask, threshold_vals), setting1); + + ret = __ipmi_sensor_set_threshold(intf, + sdr->record.common->keys. + sensor_num, settingMask, + __ipmi_sensor_threshold_value_to_raw(sdr->record.full, setting1), + sdr->record.common->keys.owner_id, + sdr->record.common->keys.lun, + sdr->record.common->keys.channel); + } + + return ret; +} + +static int +ipmi_sensor_get_reading(struct ipmi_intf *intf, int argc, char **argv) +{ + struct sdr_record_list *sdr; + int i, rc=0; + + if (argc < 1 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_NOTICE, "sensor reading <id> ... [id]"); + lprintf(LOG_NOTICE, " id : name of desired sensor"); + return -1; + } + + for (i = 0; i < argc; i++) { + sdr = ipmi_sdr_find_sdr_byid(intf, argv[i]); + if (sdr == NULL) { + lprintf(LOG_ERR, "Sensor \"%s\" not found!", + argv[i]); + rc = -1; + continue; + } + + switch (sdr->type) { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + { + struct sensor_reading *sr; + struct sdr_record_common_sensor *sensor = sdr->record.common; + sr = ipmi_sdr_read_sensor_value(intf, sensor, sdr->type, 3); + + if (sr == NULL) { + rc = -1; + continue; + } + + if (!sr->full) + continue; + + if (!sr->s_reading_valid) + continue; + + if (!sr->s_has_analog_value) { + lprintf(LOG_ERR, "Sensor \"%s\" is a discrete sensor!", argv[i]); + continue; + } + if (csv_output) + printf("%s,%s\n", argv[i], sr->s_a_str); + else + printf("%-16s | %s\n", argv[i], sr->s_a_str); + + break; + } + default: + continue; + } + } + + return rc; +} + +static int +ipmi_sensor_get(struct ipmi_intf *intf, int argc, char **argv) +{ + int i, v; + int rc = 0; + struct sdr_record_list *sdr; + + if (argc < 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_sensor_get_usage(); + return (-1); + } else if (strcmp(argv[0], "help") == 0) { + printf_sensor_get_usage(); + return 0; + } + printf("Locating sensor record...\n"); + /* lookup by sensor name */ + for (i = 0; i < argc; i++) { + sdr = ipmi_sdr_find_sdr_byid(intf, argv[i]); + if (sdr == NULL) { + lprintf(LOG_ERR, "Sensor data record \"%s\" not found!", + argv[i]); + rc = -1; + continue; + } + /* need to set verbose level to 1 */ + v = verbose; + verbose = 1; + if (ipmi_sdr_print_listentry(intf, sdr) < 0) { + rc = (-1); + } + verbose = v; + sdr = NULL; + } + return rc; +} + +int +ipmi_sensor_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = 0; + + if (argc == 0) { + rc = ipmi_sensor_list(intf); + } else if (strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_NOTICE, "Sensor Commands: list thresh get reading"); + } else if (strncmp(argv[0], "list", 4) == 0) { + rc = ipmi_sensor_list(intf); + } else if (strncmp(argv[0], "thresh", 5) == 0) { + rc = ipmi_sensor_set_threshold(intf, argc - 1, &argv[1]); + } else if (strncmp(argv[0], "get", 3) == 0) { + rc = ipmi_sensor_get(intf, argc - 1, &argv[1]); + } else if (strncmp(argv[0], "reading", 7) == 0) { + rc = ipmi_sensor_get_reading(intf, argc - 1, &argv[1]); + } else { + lprintf(LOG_ERR, "Invalid sensor command: %s", argv[0]); + rc = -1; + } + + return rc; +} + +/* printf_sensor_get_usage - print usage for # ipmitool sensor get NAC; + * + * @returns: void + */ +void +printf_sensor_get_usage() +{ + lprintf(LOG_NOTICE, "sensor get <id> ... [id]"); + lprintf(LOG_NOTICE, " id : name of desired sensor"); +} diff --git a/lib/ipmi_session.c b/lib/ipmi_session.c new file mode 100644 index 0000000..4855bc4 --- /dev/null +++ b/lib/ipmi_session.c @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_session.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + + +typedef enum { + IPMI_SESSION_REQUEST_CURRENT = 0, + IPMI_SESSION_REQUEST_ALL, + IPMI_SESSION_REQUEST_BY_ID, + IPMI_SESSION_REQUEST_BY_HANDLE +} Ipmi_Session_Request_Type; + + + + +/* + * print_session_info_csv + */ +static void +print_session_info_csv(const struct get_session_info_rsp * session_info, + int data_len) +{ + char buffer[18]; + uint16_t console_port_tmp; + + printf("%d", session_info->session_handle); + printf(",%d", session_info->session_slot_count); + printf(",%d", session_info->active_session_count); + + if (data_len == 3) + { + /* There is no session data here*/ + printf("\n"); + return; + } + + printf(",%d", session_info->user_id); + printf(",%s", val2str(session_info->privilege_level, ipmi_privlvl_vals)); + + printf(",%s", session_info->auxiliary_data? + "IPMIv2/RMCP+" : "IPMIv1.5"); + + printf(",0x%02x", session_info->channel_number); + + if (data_len == 18) + { + /* We have 802.3 LAN data */ + printf(",%s", + inet_ntop(AF_INET, + &(session_info->channel_data.lan_data.console_ip), + buffer, + 16)); + + printf(",%02x:%02x:%02x:%02x:%02x:%02x", + session_info->channel_data.lan_data.console_mac[0], + session_info->channel_data.lan_data.console_mac[1], + session_info->channel_data.lan_data.console_mac[2], + session_info->channel_data.lan_data.console_mac[3], + session_info->channel_data.lan_data.console_mac[4], + session_info->channel_data.lan_data.console_mac[5]); + + console_port_tmp = session_info->channel_data.lan_data.console_port; + #if WORDS_BIGENDIAN + console_port_tmp = BSWAP_16(console_port_tmp); + #endif + printf(",%d", console_port_tmp); + } + + + else if ((data_len == 12) || (data_len == 14)) + { + /* Channel async serial/modem */ + printf(",%s", + val2str(session_info->channel_data.modem_data.session_channel_activity_type, + ipmi_channel_activity_type_vals)); + + printf(",%d", + session_info->channel_data.modem_data.destination_selector); + + printf(",%s", + inet_ntop(AF_INET, + &(session_info->channel_data.modem_data.console_ip), + buffer, + 16)); + + if (data_len == 14) + { + /* Connection is PPP */ + console_port_tmp = session_info->channel_data.lan_data.console_port; + #if WORDS_BIGENDIAN + console_port_tmp = BSWAP_16(console_port_tmp); + #endif + printf(",%d", console_port_tmp); + } + } + + printf("\n"); +} + + + +/* + * print_session_info_verbose + */ +static void +print_session_info_verbose(const struct get_session_info_rsp * session_info, + int data_len) +{ + char buffer[18]; + uint16_t console_port_tmp; + + printf("session handle : %d\n", session_info->session_handle); + printf("slot count : %d\n", session_info->session_slot_count); + printf("active sessions : %d\n", session_info->active_session_count); + + if (data_len == 3) + { + /* There is no session data here */ + printf("\n"); + return; + } + + printf("user id : %d\n", session_info->user_id); + printf("privilege level : %s\n", + val2str(session_info->privilege_level, ipmi_privlvl_vals)); + + printf("session type : %s\n", session_info->auxiliary_data? + "IPMIv2/RMCP+" : "IPMIv1.5"); + + printf("channel number : 0x%02x\n", session_info->channel_number); + + + if (data_len == 18) + { + /* We have 802.3 LAN data */ + printf("console ip : %s\n", + inet_ntop(AF_INET, + &(session_info->channel_data.lan_data.console_ip), + buffer, + 16)); + + printf("console mac : %02x:%02x:%02x:%02x:%02x:%02x\n", + session_info->channel_data.lan_data.console_mac[0], + session_info->channel_data.lan_data.console_mac[1], + session_info->channel_data.lan_data.console_mac[2], + session_info->channel_data.lan_data.console_mac[3], + session_info->channel_data.lan_data.console_mac[4], + session_info->channel_data.lan_data.console_mac[5]); + + console_port_tmp = session_info->channel_data.lan_data.console_port; + #if WORDS_BIGENDIAN + console_port_tmp = BSWAP_16(console_port_tmp); + #endif + printf("console port : %d\n", console_port_tmp); + } + + + else if ((data_len == 12) || (data_len == 14)) + { + /* Channel async serial/modem */ + printf("Session/Channel Activity Type : %s\n", + val2str(session_info->channel_data.modem_data.session_channel_activity_type, + ipmi_channel_activity_type_vals)); + + printf("Destination selector : %d\n", + session_info->channel_data.modem_data.destination_selector); + + printf("console ip : %s\n", + inet_ntop(AF_INET, + &(session_info->channel_data.modem_data.console_ip), + buffer, + 16)); + + if (data_len == 14) + { + /* Connection is PPP */ + console_port_tmp = session_info->channel_data.lan_data.console_port; + #if WORDS_BIGENDIAN + console_port_tmp = BSWAP_16(console_port_tmp); + #endif + printf("console port : %d\n", console_port_tmp); + } + } + + printf("\n"); +} + + +static void print_session_info(const struct get_session_info_rsp * session_info, + int data_len) +{ + if (csv_output) + print_session_info_csv(session_info, data_len); + else + print_session_info_verbose(session_info, data_len); +} + + +/* + * ipmi_get_session_info + * + * returns 0 on success + * -1 on error + */ +int +ipmi_get_session_info(struct ipmi_intf * intf, + Ipmi_Session_Request_Type session_request_type, + uint32_t id_or_handle) +{ + int i, retval = 0; + + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[5]; // max length of the variable length request + struct get_session_info_rsp session_info; + + memset(&req, 0, sizeof(req)); + memset(&session_info, 0, sizeof(session_info)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_SESSION_INFO; // 0x3D + req.msg.data = rqdata; + + switch (session_request_type) + { + + case IPMI_SESSION_REQUEST_CURRENT: + case IPMI_SESSION_REQUEST_BY_ID: + case IPMI_SESSION_REQUEST_BY_HANDLE: + switch (session_request_type) + { + case IPMI_SESSION_REQUEST_CURRENT: + rqdata[0] = 0x00; + req.msg.data_len = 1; + break; + case IPMI_SESSION_REQUEST_BY_ID: + rqdata[0] = 0xFF; + rqdata[1] = id_or_handle & 0x000000FF; + rqdata[2] = (id_or_handle >> 8) & 0x000000FF; + rqdata[3] = (id_or_handle >> 16) & 0x000000FF; + rqdata[4] = (id_or_handle >> 24) & 0x000000FF; + req.msg.data_len = 5; + break; + case IPMI_SESSION_REQUEST_BY_HANDLE: + rqdata[0] = 0xFE; + rqdata[1] = (uint8_t)id_or_handle; + req.msg.data_len = 2; + break; + case IPMI_SESSION_REQUEST_ALL: + break; + } + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) + { + lprintf(LOG_ERR, "Get Session Info command failed"); + retval = -1; + } + else if (rsp->ccode > 0) + { + lprintf(LOG_ERR, "Get Session Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + retval = -1; + } + + if (retval < 0) + { + if ((session_request_type == IPMI_SESSION_REQUEST_CURRENT) && + (strncmp(intf->name, "lan", 3) != 0)) + lprintf(LOG_ERR, "It is likely that the channel in use " + "does not support sessions"); + } + else + { + memcpy(&session_info, rsp->data, rsp->data_len); + print_session_info(&session_info, rsp->data_len); + } + break; + + case IPMI_SESSION_REQUEST_ALL: + req.msg.data_len = 1; + i = 1; + do + { + rqdata[0] = i++; + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) + { + lprintf(LOG_ERR, "Get Session Info command failed"); + retval = -1; + break; + } + else if (rsp->ccode > 0 && rsp->ccode != 0xCC && rsp->ccode != 0xCB) + { + lprintf(LOG_ERR, "Get Session Info command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + retval = -1; + break; + } + else if (rsp->data_len < 3) + { + retval = -1; + break; + } + + memcpy(&session_info, rsp->data, rsp->data_len); + print_session_info(&session_info, rsp->data_len); + + } while (i <= session_info.session_slot_count); + break; + } + + return retval; +} + + + +static void +printf_session_usage(void) +{ + lprintf(LOG_NOTICE, "Session Commands: info <active | all | id 0xnnnnnnnn | handle 0xnn>"); +} + + +int +ipmi_session_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int retval = 0; + + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) + { + printf_session_usage(); + } + else if (strncmp(argv[0], "info", 4) == 0) + { + + if ((argc < 2) || strncmp(argv[1], "help", 4) == 0) + { + printf_session_usage(); + } + else + { + Ipmi_Session_Request_Type session_request_type = 0; + uint32_t id_or_handle = 0; + + if (strncmp(argv[1], "active", 6) == 0) + session_request_type = IPMI_SESSION_REQUEST_CURRENT; + else if (strncmp(argv[1], "all", 3) == 0) + session_request_type = IPMI_SESSION_REQUEST_ALL; + else if (strncmp(argv[1], "id", 2) == 0) + { + if (argc >= 3) + { + session_request_type = IPMI_SESSION_REQUEST_BY_ID; + if (str2uint(argv[2], &id_or_handle) != 0) { + lprintf(LOG_ERR, "HEX number expected, but '%s' given.", + argv[2]); + printf_session_usage(); + retval = -1; + } + } + else + { + lprintf(LOG_ERR, "Missing id argument"); + printf_session_usage(); + retval = -1; + } + } + else if (strncmp(argv[1], "handle", 6) == 0) + { + if (argc >= 3) + { + session_request_type = IPMI_SESSION_REQUEST_BY_HANDLE; + if (str2uint(argv[2], &id_or_handle) != 0) { + lprintf(LOG_ERR, "HEX number expected, but '%s' given.", + argv[2]); + printf_session_usage(); + retval = -1; + } + } + else + { + lprintf(LOG_ERR, "Missing handle argument"); + printf_session_usage(); + retval = -1; + } + } + else + { + lprintf(LOG_ERR, "Invalid SESSION info parameter: %s", argv[1]); + printf_session_usage(); + retval = -1; + } + + + if (retval == 0) + retval = ipmi_get_session_info(intf, + session_request_type, + id_or_handle); + } + } + else + { + lprintf(LOG_ERR, "Invalid SESSION command: %s", argv[0]); + printf_session_usage(); + retval = -1; + } + + return retval; +} + diff --git a/lib/ipmi_sol.c b/lib/ipmi_sol.c new file mode 100644 index 0000000..a5b962f --- /dev/null +++ b/lib/ipmi_sol.c @@ -0,0 +1,2098 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <unistd.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined(HAVE_TERMIOS_H) +# include <termios.h> +#elif defined (HAVE_SYS_TERMIOS_H) +# include <sys/termios.h> +#endif + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sol.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + + +#define SOL_PARAMETER_SET_IN_PROGRESS 0x00 +#define SOL_PARAMETER_SOL_ENABLE 0x01 +#define SOL_PARAMETER_SOL_AUTHENTICATION 0x02 +#define SOL_PARAMETER_CHARACTER_INTERVAL 0x03 +#define SOL_PARAMETER_SOL_RETRY 0x04 +#define SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE 0x05 +#define SOL_PARAMETER_SOL_VOLATILE_BIT_RATE 0x06 +#define SOL_PARAMETER_SOL_PAYLOAD_CHANNEL 0x07 +#define SOL_PARAMETER_SOL_PAYLOAD_PORT 0x08 + +#define MAX_SOL_RETRY 6 + +const struct valstr sol_parameter_vals[] = { + { SOL_PARAMETER_SET_IN_PROGRESS, "Set In Progress (0)" }, + { SOL_PARAMETER_SOL_ENABLE, "Enable (1)" }, + { SOL_PARAMETER_SOL_AUTHENTICATION, "Authentication (2)" }, + { SOL_PARAMETER_CHARACTER_INTERVAL, "Character Interval (3)" }, + { SOL_PARAMETER_SOL_RETRY, "Retry (4)" }, + { SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE, "Nonvolatile Bitrate (5)" }, + { SOL_PARAMETER_SOL_VOLATILE_BIT_RATE, "Volatile Bitrate (6)" }, + { SOL_PARAMETER_SOL_PAYLOAD_CHANNEL, "Payload Channel (7)" }, + { SOL_PARAMETER_SOL_PAYLOAD_PORT, "Payload Port (8)" }, + { 0x00, NULL }, +}; + + +static struct timeval _start_keepalive; +static struct termios _saved_tio; +static int _in_raw_mode = 0; +static int _disable_keepalive = 0; +static int _use_sol_for_keepalive = 0; +static int _keepalive_retries = 0; + +extern int verbose; + +/* + * ipmi_sol_payload_access + */ +int +ipmi_sol_payload_access(struct ipmi_intf * intf, uint8_t channel, + uint8_t userid, int enable) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + int rc = (-1); + uint8_t data[6]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_SET_USER_PAYLOAD_ACCESS; + req.msg.data = data; + req.msg.data_len = 6; + + memset(data, 0, 6); + /* channel */ + data[0] = channel & 0xf; + /* user id */ + data[1] = userid & 0x3f; + if (!enable) { + /* disable */ + data[1] |= 0x40; + } + /* payload 1 is SOL */ + data[2] = 0x02; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error %sabling SOL payload for user %d on channel %d", + enable ? "en" : "dis", userid, channel); + rc = (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Error %sabling SOL payload for user %d on channel %d: %s", + enable ? "en" : "dis", userid, channel, + val2str(rsp->ccode, completion_code_vals)); + rc = (-1); + } else { + rc = 0; + } + return rc; +} + +int +ipmi_sol_payload_access_status(struct ipmi_intf * intf, + uint8_t channel, + uint8_t userid) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + uint8_t data[2]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_GET_USER_PAYLOAD_ACCESS; + req.msg.data = data; + req.msg.data_len = sizeof(data); + + data[0] = channel & 0xf; /* channel */ + data[1] = userid & 0x3f; /* user id */ + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Error. No valid response received."); + return -1; + } + + switch(rsp->ccode) { + case 0x00: + if (rsp->data_len != 4) { + lprintf(LOG_ERR, "Error parsing SOL payload status for user %d on channel %d", + userid, channel); + return -1; + } + + printf("User %d on channel %d is %sabled\n", + userid, channel, (rsp->data[0] & 0x02) ? "en":"dis"); + return 0; + + default: + lprintf(LOG_ERR, "Error getting SOL payload status for user %d on channel %d: %s", + userid, channel, + val2str(rsp->ccode, completion_code_vals)); + return -1; + } +} + + +/* + * ipmi_get_sol_info + */ +int +ipmi_get_sol_info( + struct ipmi_intf * intf, + uint8_t channel, + struct sol_config_parameters * params) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[4]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_GET_SOL_CONFIG_PARAMETERS; + req.msg.data_len = 4; + req.msg.data = data; + + /* + * set in progress + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SET_IN_PROGRESS; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->set_in_progress = rsp->data[1]; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL enable + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_ENABLE; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->enabled = rsp->data[1]; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL authentication + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->force_encryption = ((rsp->data[1] & 0x80)? 1 : 0); + params->force_authentication = ((rsp->data[1] & 0x40)? 1 : 0); + params->privilege_level = rsp->data[1] & 0x0F; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * Character accumulate interval and character send interval + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 3) { + params->character_accumulate_level = rsp->data[1]; + params->character_send_threshold = rsp->data[2]; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL retry + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_RETRY; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 3) { + params->retry_count = rsp->data[1]; + params->retry_interval = rsp->data[2]; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL non-volatile bit rate + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->non_volatile_bit_rate = rsp->data[1] & 0x0F; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL volatile bit rate + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_VOLATILE_BIT_RATE; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->volatile_bit_rate = rsp->data[1] & 0x0F; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", + val2str(data[1], sol_parameter_vals)); + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL payload channel + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_PAYLOAD_CHANNEL; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 2) { + params->payload_channel = rsp->data[1]; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported - defaulting to 0x%02x", + val2str(data[1], sol_parameter_vals), channel); + params->payload_channel = channel; + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* + * SOL payload port + */ + memset(data, 0, sizeof(data)); + data[0] = channel; /* channel number */ + data[1] = SOL_PARAMETER_SOL_PAYLOAD_PORT; /* parameter selector */ + data[2] = 0x00; /* set selector */ + data[3] = 0x00; /* block selector */ + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 3) { + params->payload_port = (rsp->data[1]) | (rsp->data[2] << 8); + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "for SOL parameter '%s'", + rsp->data_len, + val2str(data[1], sol_parameter_vals)); + } + break; + case 0x80: + if( intf->session != NULL ) { + lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported - defaulting to %d", + val2str(data[1], sol_parameter_vals), intf->session->port); + params->payload_port = intf->session->port; + } else { + lprintf(LOG_ERR, + "Info: SOL parameter '%s' not supported - can't determine which " + "payload port to use on NULL session", + val2str(data[1], sol_parameter_vals)); + return (-1); + } + break; + default: + lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", + val2str(data[1], sol_parameter_vals), + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + return 0; +} + + + +/* + * ipmi_print_sol_info + */ +static int +ipmi_print_sol_info(struct ipmi_intf * intf, uint8_t channel) +{ + struct sol_config_parameters params = {0}; + if (ipmi_get_sol_info(intf, channel, ¶ms)) + return -1; + + if (csv_output) + { + printf("%s,", + val2str(params.set_in_progress & 0x03, + ipmi_set_in_progress_vals)); + printf("%s,", params.enabled?"true": "false"); + printf("%s,", params.force_encryption?"true":"false"); + printf("%s,", params.force_encryption?"true":"false"); + printf("%s,", + val2str(params.privilege_level, ipmi_privlvl_vals)); + printf("%d,", params.character_accumulate_level * 5); + printf("%d,", params.character_send_threshold); + printf("%d,", params.retry_count); + printf("%d,", params.retry_interval * 10); + + printf("%s,", + val2str(params.volatile_bit_rate, ipmi_bit_rate_vals)); + + printf("%s,", + val2str(params.non_volatile_bit_rate, ipmi_bit_rate_vals)); + + printf("%d,", params.payload_channel); + printf("%d\n", params.payload_port); + } + else + { + printf("Set in progress : %s\n", + val2str(params.set_in_progress & 0x03, + ipmi_set_in_progress_vals)); + printf("Enabled : %s\n", + params.enabled?"true": "false"); + printf("Force Encryption : %s\n", + params.force_encryption?"true":"false"); + printf("Force Authentication : %s\n", + params.force_authentication?"true":"false"); + printf("Privilege Level : %s\n", + val2str(params.privilege_level, ipmi_privlvl_vals)); + printf("Character Accumulate Level (ms) : %d\n", + params.character_accumulate_level * 5); + printf("Character Send Threshold : %d\n", + params.character_send_threshold); + printf("Retry Count : %d\n", + params.retry_count); + printf("Retry Interval (ms) : %d\n", + params.retry_interval * 10); + + printf("Volatile Bit Rate (kbps) : %s\n", + val2str(params.volatile_bit_rate, ipmi_bit_rate_vals)); + + printf("Non-Volatile Bit Rate (kbps) : %s\n", + val2str(params.non_volatile_bit_rate, ipmi_bit_rate_vals)); + + printf("Payload Channel : %d (0x%02x)\n", + params.payload_channel, params.payload_channel); + printf("Payload Port : %d\n", + params.payload_port); + } + + return 0; +} + + + +/* + * Small function to validate that user-supplied SOL + * configuration parameter values we store in uint8_t + * data type falls within valid range. With minval + * and maxval parameters we can use the same function + * to validate parameters that have different ranges + * of values. + * + * function will return -1 if value is not valid, or + * will return 0 if valid. + */ +int ipmi_sol_set_param_isvalid_uint8_t( const char *strval, + const char *name, + int base, + uint8_t minval, + uint8_t maxval, + uint8_t *out_value) +{ + if (str2uchar(strval, out_value) != 0 || (*out_value < minval) + || (*out_value > maxval)) { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + strval, name); + lprintf(LOG_ERR, "Valid values are %d-%d", minval, maxval); + return -1; + } + return 0; +} + + +/* + * ipmi_sol_set_param + * + * Set the specified Serial Over LAN value to the specified + * value + * + * return 0 on success, + * -1 on failure + */ +static int +ipmi_sol_set_param(struct ipmi_intf * intf, + uint8_t channel, + const char * param, + const char * value, + uint8_t guarded) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[4]; + int bGuarded = guarded; /* Use set-in-progress indicator? */ + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; /* 0x0c */ + req.msg.cmd = IPMI_SET_SOL_CONFIG_PARAMETERS; /* 0x21 */ + req.msg.data = data; + + data[0] = channel; + + /* + * set-in-progress + */ + if (! strcmp(param, "set-in-progress")) + { + bGuarded = 0; /* We _ARE_ the set-in-progress indicator */ + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SET_IN_PROGRESS; + + if (! strcmp(value, "set-complete")) + data[2] = 0x00; + else if (! strcmp(value, "set-in-progress")) + data[2] = 0x01; + else if (! strcmp(value, "commit-write")) + data[2] = 0x02; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are set-complete, set-in-progress " + "and commit-write"); + return -1; + } + } + + + /* + * enabled + */ + else if (! strcmp(param, "enabled")) + { + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_ENABLE; + + if (! strcmp(value, "true")) + data[2] = 0x01; + else if (! strcmp(value, "false")) + data[2] = 0x00; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are true and false"); + return -1; + } + } + + + /* + * force-payload-encryption + */ + else if (! strcmp(param, "force-encryption")) + { + struct sol_config_parameters params; + + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + + if (! strcmp(value, "true")) + data[2] = 0x80; + else if (! strcmp(value, "false")) + data[2] = 0x00; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are true and false"); + return -1; + } + + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[2] |= params.force_authentication? 0x40 : 0x00; + data[2] |= params.privilege_level; + } + + + /* + * force-payload-authentication + */ + else if (! strcmp(param, "force-authentication")) + { + struct sol_config_parameters params; + + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + + if (! strcmp(value, "true")) + data[2] = 0x40; + else if (! strcmp(value, "false")) + data[2] = 0x00; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are true and false"); + return -1; + } + + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[2] |= params.force_encryption? 0x80 : 0x00; + data[2] |= params.privilege_level; + } + + + /* + * privilege-level + */ + else if (! strcmp(param, "privilege-level")) + { + struct sol_config_parameters params; + + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + + if (! strcmp(value, "user")) + data[2] = 0x02; + else if (! strcmp(value, "operator")) + data[2] = 0x03; + else if (! strcmp(value, "admin")) + data[2] = 0x04; + else if (! strcmp(value, "oem")) + data[2] = 0x05; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem"); + return -1; + } + + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[2] |= params.force_encryption? 0x80 : 0x00; + data[2] |= params.force_authentication? 0x40 : 0x00; + } + + + /* + * character-accumulate-level + */ + else if (! strcmp(param, "character-accumulate-level")) + { + struct sol_config_parameters params; + + req.msg.data_len = 4; + data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; + + /* validate user-supplied input */ + if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 1, 255, &data[2])) + return -1; + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[3] = params.character_send_threshold; + } + + + /* + * character-send-threshold + */ + else if (! strcmp(param, "character-send-threshold")) + { + struct sol_config_parameters params; + + req.msg.data_len = 4; + data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; + + /* validate user-supplied input */ + if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 255, &data[3])) + return -1; + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[2] = params.character_accumulate_level; + } + + + /* + * retry-count + */ + else if (! strcmp(param, "retry-count")) + { + struct sol_config_parameters params; + + req.msg.data_len = 4; + data[1] = SOL_PARAMETER_SOL_RETRY; + + /* validate user input, 7 is max value */ + if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 7, &data[2])) + return -1; + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[3] = params.retry_interval; + } + + + /* + * retry-interval + */ + else if (! strcmp(param, "retry-interval")) + { + struct sol_config_parameters params; + + req.msg.data_len = 4; + data[1] = SOL_PARAMETER_SOL_RETRY; + + /* validate user-supplied input */ + if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 255, &data[3])) + return -1; + + /* We need other values to complete the request */ + if (ipmi_get_sol_info(intf, channel, ¶ms)) + { + lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", + param); + return -1; + } + + data[2] = params.retry_count; + } + + + /* + * non-volatile-bit-rate + */ + else if (! strcmp(param, "non-volatile-bit-rate")) + { + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE; + + if (!strcmp(value, "serial")) + { + data[2] = 0x00; + } + else if (!strcmp(value, "9.6")) + { + data[2] = 0x06; + } + else if (!strcmp(value, "19.2")) + { + data[2] = 0x07; + } + else if (!strcmp(value, "38.4")) + { + data[2] = 0x08; + } + else if (!strcmp(value, "57.6")) + { + data[2] = 0x09; + } + else if (!strcmp(value, "115.2")) + { + data[2] = 0x0A; + } + else + { + lprintf(LOG_ERR, "Invalid value \"%s\" for parameter \"%s\"", + value, + param); + lprintf(LOG_ERR, "Valid values are serial, 9.6 19.2, 38.4, 57.6 and 115.2"); + return -1; + } + } + + + /* + * volatile-bit-rate + */ + else if (! strcmp(param, "volatile-bit-rate")) + { + req.msg.data_len = 3; + data[1] = SOL_PARAMETER_SOL_VOLATILE_BIT_RATE; + + if (!strcmp(value, "serial")) + { + data[2] = 0x00; + } + else if (!strcmp(value, "9.6")) + { + data[2] = 0x06; + } + else if (!strcmp(value, "19.2")) + { + data[2] = 0x07; + } + else if (!strcmp(value, "38.4")) + { + data[2] = 0x08; + } + else if (!strcmp(value, "57.6")) + { + data[2] = 0x09; + } + else if (!strcmp(value, "115.2")) + { + data[2] = 0x0A; + } + else + { + lprintf(LOG_ERR, "Invalid value \"%s\" for parameter \"%s\"", + value, + param); + lprintf(LOG_ERR, "Valid values are serial, 9.6 19.2, 38.4, 57.6 and 115.2"); + return -1; + } + } + else + { + lprintf(LOG_ERR, "Error: invalid SOL parameter %s", param); + return -1; + } + + + /* + * Execute the request + */ + if (bGuarded && + (ipmi_sol_set_param(intf, + channel, + "set-in-progress", + "set-in-progress", + bGuarded))) + { + lprintf(LOG_ERR, "Error: set of parameter \"%s\" failed", param); + return -1; + } + + + /* The command proper */ + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Error setting SOL parameter '%s'", param); + return -1; + } + + if (!(!strncmp(param, "set-in-progress", 15) && !strncmp(value, "commit-write", 12)) && + rsp->ccode > 0) { + switch (rsp->ccode) { + case 0x80: + lprintf(LOG_ERR, "Error setting SOL parameter '%s': " + "Parameter not supported", param); + break; + case 0x81: + lprintf(LOG_ERR, "Error setting SOL parameter '%s': " + "Attempt to set set-in-progress when not in set-complete state", + param); + break; + case 0x82: + lprintf(LOG_ERR, "Error setting SOL parameter '%s': " + "Attempt to write read-only parameter", param); + break; + case 0x83: + lprintf(LOG_ERR, "Error setting SOL parameter '%s': " + "Attempt to read write-only parameter", param); + break; + default: + lprintf(LOG_ERR, "Error setting SOL parameter '%s' to '%s': %s", + param, value, val2str(rsp->ccode, completion_code_vals)); + break; + } + + if (bGuarded && + (ipmi_sol_set_param(intf, + channel, + "set-in-progress", + "set-complete", + bGuarded))) + { + lprintf(LOG_ERR, "Error could not set \"set-in-progress\" " + "to \"set-complete\""); + } + + return -1; + } + + + /* + * The commit write could very well fail, but that's ok. + * It may not be implemented. + */ + if (bGuarded) + ipmi_sol_set_param(intf, + channel, + "set-in-progress", + "commit-write", + bGuarded); + + + if (bGuarded && + ipmi_sol_set_param(intf, + channel, + "set-in-progress", + "set-complete", + bGuarded)) + { + lprintf(LOG_ERR, "Error could not set \"set-in-progress\" " + "to \"set-complete\""); + return -1; + } + + return 0; +} + + + +void +leave_raw_mode(void) +{ + if (!_in_raw_mode) + return; + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 0; +} + + + +void +enter_raw_mode(void) +{ + struct termios tio; + if (tcgetattr(fileno(stdin), &tio) == -1) { + perror("tcgetattr"); + return; + } + _saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); + // #ifdef IEXTEN + tio.c_lflag &= ~IEXTEN; + // #endif + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 1; +} + + +static void +sendBreak(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + v2_payload.payload.sol_packet.character_count = 0; + v2_payload.payload.sol_packet.generate_break = 1; + + intf->send_sol(intf, &v2_payload); +} + + + +/* + * suspendSelf + * + * Put ourself in the background + * + * param bRestoreTty specifies whether we will put our self back + * in raw mode when we resume + */ +static void +suspendSelf(int bRestoreTty) +{ + leave_raw_mode(); + kill(getpid(), SIGTSTP); + + if (bRestoreTty) + enter_raw_mode(); +} + + + +/* + * printSolEscapeSequences + * + * Send some useful documentation to the user + */ +static void +printSolEscapeSequences(struct ipmi_intf * intf) +{ + printf( + "%c?\n\ + Supported escape sequences:\n\ + %c. - terminate connection\n\ + %c^Z - suspend ipmitool\n\ + %c^X - suspend ipmitool, but don't restore tty on restart\n\ + %cB - send break\n\ + %c? - this message\n\ + %c%c - send the escape character by typing it twice\n\ + (Note that escapes are only recognized immediately after newline.)\n", + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char); +} + + + +/* + * output + * + * Send the specified data to stdout + */ +static void +output(struct ipmi_rs * rsp) +{ + /* Add checks to make sure it is actually SOL data, in general I see + * outside code mostly trying to guard against this happening, but + * some places fail to do so, so I do so here to make sure nothing gets + * through. If non-sol data comes through here, there is probably + * a packet that won't get processed somewhere else, but the alternative + * of outputting corrupt data is worse. Generally I see the get device + * id response make it here somehow. I assume it is a heartbeat and the + * other code will retry if it cares about the response and misses it. + */ + if (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) + { + int i; + + for (i = 0; i < rsp->data_len; ++i) + putc(rsp->data[i], stdout); + + fflush(stdout); + } +} + + + +/* + * ipmi_sol_deactivate + */ +static int +ipmi_sol_deactivate(struct ipmi_intf * intf, int instance) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[6]; + + if ((instance <= 0) || (instance > 15)) { + lprintf(LOG_ERR, "Error: Instance must range from 1 to 15"); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_DEACTIVATE_PAYLOAD; + req.msg.data_len = 6; + req.msg.data = data; + + bzero(data, sizeof(data)); + data[0] = IPMI_PAYLOAD_TYPE_SOL; /* payload type */ + data[1] = instance; /* payload instance. */ + + /* Lots of important data */ + data[2] = 0; + data[3] = 0; + data[4] = 0; + data[5] = 0; + + rsp = intf->sendrecv(intf, &req); + + if (NULL != rsp) { + switch (rsp->ccode) { + case 0x00: + return 0; + case 0x80: + lprintf(LOG_ERR, "Info: SOL payload already de-activated"); + break; + case 0x81: + lprintf(LOG_ERR, "Info: SOL payload type disabled"); + break; + default: + lprintf(LOG_ERR, "Error de-activating SOL payload: %s", + val2str(rsp->ccode, completion_code_vals)); + break; + } + } else { + lprintf(LOG_ERR, "Error: No response de-activating SOL payload"); + } + + return -1; +} + + + +/* + * processSolUserInput + * + * Act on user input into the SOL session. The only reason this + * is complicated is that we have to process escape sequences. + * + * return 0 on success + * 1 if we should exit + * < 0 on error (BMC probably closed the session) + */ +static int +processSolUserInput( + struct ipmi_intf * intf, + uint8_t * input, + uint16_t buffer_length) +{ + static int escape_pending = 0; + static int last_was_cr = 1; + struct ipmi_v2_payload v2_payload; + int length = 0; + int retval = 0; + char ch; + int i; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + /* + * Our first order of business is to check the input for escape + * sequences to act on. + */ + for (i = 0; i < buffer_length; ++i) + { + ch = input[i]; + + if (escape_pending){ + escape_pending = 0; + + /* + * Process a possible escape sequence. + */ + switch (ch) { + case '.': + printf("%c. [terminated ipmitool]\n", + intf->session->sol_escape_char); + retval = 1; + break; + + case 'Z' - 64: + printf("%c^Z [suspend ipmitool]\n", + intf->session->sol_escape_char); + suspendSelf(1); /* Restore tty back to raw */ + continue; + + case 'X' - 64: + printf("%c^Z [suspend ipmitool]\n", + intf->session->sol_escape_char); + suspendSelf(0); /* Don't restore to raw mode */ + continue; + + case 'B': + printf("%cB [send break]\n", + intf->session->sol_escape_char); + sendBreak(intf); + continue; + + case '?': + printSolEscapeSequences(intf); + continue; + + default: + if (ch != intf->session->sol_escape_char) + v2_payload.payload.sol_packet.data[length++] = + intf->session->sol_escape_char; + v2_payload.payload.sol_packet.data[length++] = ch; + } + } + + else + { + if (last_was_cr && (ch == intf->session->sol_escape_char)) { + escape_pending = 1; + continue; + } + + v2_payload.payload.sol_packet.data[length++] = ch; + } + + + /* + * Normal character. Record whether it was a newline. + */ + last_was_cr = (ch == '\r' || ch == '\n'); + } + + + /* + * If there is anything left to process we dispatch it to the BMC, + * send intf->session->sol_data.max_outbound_payload_size bytes + * at a time. + */ + if (length) + { + struct ipmi_rs * rsp = NULL; + int try = 0; + + while (try < intf->session->retry) { + + v2_payload.payload.sol_packet.character_count = length; + + rsp = intf->send_sol(intf, &v2_payload); + + if (rsp) + { + break; + } + + usleep(5000); + try++; + } + + if (! rsp) + { + lprintf(LOG_ERR, "Error sending SOL data: FAIL"); + retval = -1; + } + + /* If the sequence number is set we know we have new data */ + if (retval == 0) + if ((rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + output(rsp); + } + + return retval; +} + +static int +ipmi_sol_keepalive_using_sol(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_rs * rsp = NULL; + struct timeval end; + + int ret = 0; + + if (_disable_keepalive) + return 0; + + gettimeofday(&end, 0); + + if (end.tv_sec - _start_keepalive.tv_sec > SOL_KEEPALIVE_TIMEOUT) { + memset(&v2_payload, 0, sizeof(v2_payload)); + + v2_payload.payload.sol_packet.character_count = 0; + + rsp = intf->send_sol(intf, &v2_payload); + + gettimeofday(&_start_keepalive, 0); + } + return ret; +} + +static int +ipmi_sol_keepalive_using_getdeviceid(struct ipmi_intf * intf) +{ + struct timeval end; + static int ret = 0; + + if (_disable_keepalive) + return 0; + + gettimeofday(&end, 0); + + if (end.tv_sec - _start_keepalive.tv_sec > SOL_KEEPALIVE_TIMEOUT) { + ret = intf->keepalive(intf); + if ( (ret!=0) && (_keepalive_retries < SOL_KEEPALIVE_RETRIES) ) { + ret = 0; + _keepalive_retries++; + } + else if ((ret==0) && (_keepalive_retries > 0)) + _keepalive_retries = 0; + gettimeofday(&_start_keepalive, 0); + } + return ret; +} + + + +/* + * ipmi_sol_red_pill + */ +static int +ipmi_sol_red_pill(struct ipmi_intf * intf, int instance) +{ + char * buffer; + int numRead; + int bShouldExit = 0; + int bBmcClosedSession = 0; + fd_set read_fds; + struct timeval tv; + int retval; + int buffer_size = intf->session->sol_data.max_inbound_payload_size; + int keepAliveRet = 0; + int retrySol = 0; + + /* Subtract SOL header from max_inbound_payload_size */ + if (buffer_size > 4) + buffer_size -= 4; + + buffer = (char*)malloc(buffer_size); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + + /* Initialize keepalive start time */ + gettimeofday(&_start_keepalive, 0); + + enter_raw_mode(); + + while (! bShouldExit) + { + FD_ZERO(&read_fds); + FD_SET(0, &read_fds); + FD_SET(intf->fd, &read_fds); + + if (!ipmi_oem_active(intf,"i82571spt")) + { + /* Send periodic keepalive packet */ + if(_use_sol_for_keepalive == 0) + { + keepAliveRet = ipmi_sol_keepalive_using_getdeviceid(intf); + } + else + { + keepAliveRet = ipmi_sol_keepalive_using_sol(intf); + } + + if (keepAliveRet != 0) + { + /* + * Retrying the keep Alive before declaring a communication + * lost state with the IPMC. Helpful when the payload is + * reset and brings down the connection temporarily. Otherwise, + * if we send getDevice Id to check the status of IPMC during + * this down time when the connection is restarting, SOL will + * exit even though the IPMC is available and the session is open. + */ + if (retrySol == MAX_SOL_RETRY) + { + /* no response to Get Device ID keepalive message */ + bShouldExit = 1; + continue; + } + else + { + retrySol++; + } + } + else + { + /* if the keep Alive is successful reset retries to zero */ + retrySol = 0; + } + } /* !oem="i82571spt" */ + /* Wait up to half a second */ + tv.tv_sec = 0; + tv.tv_usec = 500000; + + retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv); + + if (retval) + { + if (retval == -1) + { + /* ERROR */ + perror("select"); + return -1; + } + + + /* + * Process input from the user + */ + if (FD_ISSET(0, &read_fds)) + { + memset(buffer, 0, buffer_size); + numRead = read(fileno(stdin), + buffer, + buffer_size); + + if (numRead > 0) + { + int rc = processSolUserInput(intf, (uint8_t *)buffer, numRead); + + if (rc) + { + if (rc < 0) + bShouldExit = bBmcClosedSession = 1; + else + bShouldExit = 1; + } + } + else + { + bShouldExit = 1; + } + } + + + /* + * Process input from the BMC + */ + else if (FD_ISSET(intf->fd, &read_fds)) + { + struct ipmi_rs * rs =intf->recv_sol(intf); + if (! rs) + { + bShouldExit = bBmcClosedSession = 1; + } + else + { + output(rs); + } + } + + + /* + * ERROR in select + */ + else + { + lprintf(LOG_ERR, "Error: Select returned with nothing to read"); + bShouldExit = 1; + } + } + } + + leave_raw_mode(); + + if (keepAliveRet != 0) + { + lprintf(LOG_ERR, "Error: No response to keepalive - Terminating session"); + /* attempt to clean up anyway */ + ipmi_sol_deactivate(intf, instance); + exit(1); + } + + if (bBmcClosedSession) + { + lprintf(LOG_ERR, "SOL session closed by BMC"); + exit(1); + } + else + ipmi_sol_deactivate(intf, instance); + + return 0; +} + + + + +/* + * ipmi_sol_activate + */ +static int +ipmi_sol_activate(struct ipmi_intf * intf, int looptest, int interval, + int instance) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct activate_payload_rsp ap_rsp; + uint8_t data[6]; + uint8_t bSolEncryption = 1; + uint8_t bSolAuthentication = 1; + + /* + * This command is only available over RMCP+ (the lanplus + * interface). + */ + if (strncmp(intf->name, "lanplus", 7) != 0) + { + lprintf(LOG_ERR, "Error: This command is only available over the " + "lanplus interface"); + return -1; + } + + if ((instance <= 0) || (instance > 15)) { + lprintf(LOG_ERR, "Error: Instance must range from 1 to 15"); + return -1; + } + + + /* + * Setup a callback so that the lanplus processing knows what + * to do with packets that come unexpectedly (while waiting for + * an ACK, perhaps. + */ + intf->session->sol_data.sol_input_handler = output; + + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_ACTIVATE_PAYLOAD; + req.msg.data_len = 6; + req.msg.data = data; + + data[0] = IPMI_PAYLOAD_TYPE_SOL; /* payload type */ + data[1] = instance; /* payload instance */ + + /* Lots of important data. Most is default */ + data[2] = bSolEncryption? 0x80 : 0; + data[2] |= bSolAuthentication? 0x40 : 0; + data[2] |= IPMI_SOL_SERIAL_ALERT_MASK_DEFERRED; + + if (ipmi_oem_active(intf, "intelplus")) { + data[2] |= IPMI_SOL_BMC_ASSERTS_CTS_MASK_TRUE; + } else if (ipmi_oem_active(intf, "i82571spt")) { + /* + * A quote from Intel: "Engineering believes the problem + * lies within the Auxiliary data being sent with the + * 'Activate Payload' command from IPMITool. IPMITool + * sends a C6h which sets some bits having to do with + * encryption and some behavior dealing with CTS DCD/DSR. + * I recommend that the customer modify this request + * to send 08h instead. This is what our internal utility + * sends and it works without issue. I will work with + * engineering to ensure the settings that IPMITool uses + * (C6h) are supported in the future. + */ + data[2] = 0x08; + } else { + data[2] |= IPMI_SOL_BMC_ASSERTS_CTS_MASK_FALSE; + } + + data[3] = 0x00; /* reserved */ + data[4] = 0x00; /* reserved */ + data[5] = 0x00; /* reserved */ + + rsp = intf->sendrecv(intf, &req); + + if (NULL != rsp) { + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 12) { + break; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "in payload activation response", + rsp->data_len); + return -1; + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: SOL payload already active on another session"); + return -1; + case 0x81: + lprintf(LOG_ERR, "Info: SOL payload disabled"); + return -1; + case 0x82: + lprintf(LOG_ERR, "Info: SOL payload activation limit reached"); + return -1; + case 0x83: + lprintf(LOG_ERR, "Info: cannot activate SOL payload with encryption"); + return -1; + case 0x84: + lprintf(LOG_ERR, "Info: cannot activate SOL payload without encryption"); + return -1; + default: + lprintf(LOG_ERR, "Error activating SOL payload: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } else { + lprintf(LOG_ERR, "Error: No response activating SOL payload"); + return -1; + } + + + memcpy(&ap_rsp, rsp->data, sizeof(struct activate_payload_rsp)); + + intf->session->sol_data.max_inbound_payload_size = + (ap_rsp.inbound_payload_size[1] << 8) | + ap_rsp.inbound_payload_size[0]; + + intf->session->sol_data.max_outbound_payload_size = + (ap_rsp.outbound_payload_size[1] << 8) | + ap_rsp.outbound_payload_size[0]; + + intf->session->sol_data.port = + (ap_rsp.payload_udp_port[1] << 8) | + ap_rsp.payload_udp_port[0]; + + intf->session->timeout = 1; + + + /* NOTE: the spec does allow for SOL traffic to be sent on + * a different port. we do not yet support that feature. */ + if (intf->session->sol_data.port != intf->session->port) + { + /* try byteswapping port in case BMC sent it incorrectly */ + uint16_t portswap = BSWAP_16(intf->session->sol_data.port); + + if (portswap == intf->session->port) { + intf->session->sol_data.port = portswap; + } + else { + lprintf(LOG_ERR, "Error: BMC requests SOL session on different port"); + return -1; + } + } + + printf("[SOL Session operational. Use %c? for help]\n", + intf->session->sol_escape_char); + + if(looptest == 1) + { + ipmi_sol_deactivate(intf, instance); + usleep(interval*1000); + return 0; + } + + /* + * At this point we are good to go with our SOL session. We + * need to listen to + * 1) STDIN for user input + * 2) The FD for incoming SOL packets + */ + if (ipmi_sol_red_pill(intf, instance)) + { + lprintf(LOG_ERR, "Error in SOL session"); + return -1; + } + + return 0; +} + + + +/* + * print_sol_usage + */ +static void +print_sol_usage(void) +{ + lprintf(LOG_NOTICE, "SOL Commands: info [<channel number>]"); + lprintf(LOG_NOTICE, " set <parameter> <value> [channel]"); + lprintf(LOG_NOTICE, " payload <enable|disable|status> [channel] [userid]"); + lprintf(LOG_NOTICE, " activate [<usesolkeepalive|nokeepalive>] [instance=<number>]"); + lprintf(LOG_NOTICE, " deactivate [instance=<number>]"); + lprintf(LOG_NOTICE, " looptest [<loop times> [<loop interval(in ms)> [<instance>]]]"); +} + + + +/* + * print_sol_set_usage + */ +static void +print_sol_set_usage(void) +{ + lprintf(LOG_NOTICE, "\nSOL set parameters and values: \n"); + lprintf(LOG_NOTICE, " set-in-progress set-complete | " + "set-in-progress | commit-write"); + lprintf(LOG_NOTICE, " enabled true | false"); + lprintf(LOG_NOTICE, " force-encryption true | false"); + lprintf(LOG_NOTICE, " force-authentication true | false"); + lprintf(LOG_NOTICE, " privilege-level user | operator | admin | oem"); + lprintf(LOG_NOTICE, " character-accumulate-level <in 5 ms increments>"); + lprintf(LOG_NOTICE, " character-send-threshold N"); + lprintf(LOG_NOTICE, " retry-count N"); + lprintf(LOG_NOTICE, " retry-interval <in 10 ms increments>"); + lprintf(LOG_NOTICE, " non-volatile-bit-rate " + "serial | 9.6 | 19.2 | 38.4 | 57.6 | 115.2"); + lprintf(LOG_NOTICE, " volatile-bit-rate " + "serial | 9.6 | 19.2 | 38.4 | 57.6 | 115.2"); + lprintf(LOG_NOTICE, ""); +} + + + +/* ipmi_sol_main */ +int +ipmi_sol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int retval = 0; + if (!argc || !strncmp(argv[0], "help", 4)) { + /* Help */ + print_sol_usage(); + } else if (!strncmp(argv[0], "info", 4)) { + /* Info */ + uint8_t channel; + if (argc == 1) { + /* Ask about the current channel */ + channel = 0x0E; + } else if (argc == 2) { + if (is_ipmi_channel_num(argv[1], &channel) != 0) { + return (-1); + } + } else { + print_sol_usage(); + return -1; + } + retval = ipmi_print_sol_info(intf, channel); + } else if (!strncmp(argv[0], "payload", 7)) { + /* Payload enable or disable */ + uint8_t channel = 0xe; + uint8_t userid = 1; + int enable = -1; + if (argc == 1 || argc > 4) { + print_sol_usage(); + return -1; + } + if (argc == 1 || argc > 4) { + print_sol_usage(); + return -1; + } + if (argc >= 3) { + if (is_ipmi_channel_num(argv[2], &channel) != 0) { + return (-1); + } + } + if (argc == 4) { + if (is_ipmi_user_id(argv[3], &userid) != 0) { + return (-1); + } + } + if (!strncmp(argv[1], "enable", 6)) { + enable = 1; + } else if (!strncmp(argv[1], "disable", 7)) { + enable = 0; + } else if (!strncmp(argv[1], "status", 6)) { + return ipmi_sol_payload_access_status(intf, channel, userid); + } else { + print_sol_usage(); + return -1; + } + retval = ipmi_sol_payload_access(intf, channel, userid, enable); + } else if (!strncmp(argv[0], "set", 3)) { + /* Set a parameter value */ + uint8_t channel = 0xe; + uint8_t guard = 1; + if (argc == 3) { + channel = 0xe; + } else if (argc == 4) { + if (!strncmp(argv[3], "noguard", 7)) { + guard = 0; + } else { + if (is_ipmi_channel_num(argv[3], &channel) != 0) { + return (-1); + } + } + } else if (argc == 5) { + if (is_ipmi_channel_num(argv[3], &channel) != 0) { + return (-1); + } + if (!strncmp(argv[4], "noguard", 7)) { + guard = 0; + } + } else { + print_sol_set_usage(); + return -1; + } + retval = ipmi_sol_set_param(intf, channel, argv[1], argv[2], guard); + } else if (!strncmp(argv[0], "activate", 8)) { + /* Activate */ + int i; + uint8_t instance = 1; + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "usesolkeepalive", 15)) { + _use_sol_for_keepalive = 1; + } else if (!strncmp(argv[i], "nokeepalive", 11)) { + _disable_keepalive = 1; + } else if (!strncmp(argv[i], "instance=", 9)) { + if (str2uchar(argv[i] + 9, &instance) != 0) { + lprintf(LOG_ERR, "Given instance '%s' is invalid.", argv[i] + 9); + print_sol_usage(); + return -1; + } + } else { + print_sol_usage(); + return -1; + } + } + retval = ipmi_sol_activate(intf, 0, 0, instance); + } else if (!strncmp(argv[0], "deactivate", 10)) { + /* Dectivate */ + int i; + uint8_t instance = 1; + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "instance=", 9)) { + if (str2uchar(argv[i] + 9, &instance) != 0) { + lprintf(LOG_ERR, + "Given instance '%s' is invalid.", + argv[i] + 9); + print_sol_usage(); + return -1; + } + } else { + print_sol_usage(); + return -1; + } + } + retval = ipmi_sol_deactivate(intf, instance); + } else if (!strncmp(argv[0], "looptest", 8)) { + /* SOL loop test: Activate and then Dectivate */ + int cnt = 200; + int interval = 100; /* Unit is: ms */ + uint8_t instance = 1; + if (argc > 4) { + print_sol_usage(); + return -1; + } + if (argc != 1) { + /* at least 2 */ + if (str2int(argv[1], &cnt) != 0) { + lprintf(LOG_ERR, "Given cnt '%s' is invalid.", + argv[1]); + return (-1); + } + if (cnt <= 0) { + cnt = 200; + } + } + if (argc >= 3) { + if (str2int(argv[2], &interval) != 0) { + lprintf(LOG_ERR, "Given interval '%s' is invalid.", + argv[2]); + return (-1); + } + if (interval < 0) { + interval = 0; + } + } + if (argc >= 4) { + if (str2uchar(argv[3], &instance) != 0) { + lprintf(LOG_ERR, "Given instance '%s' is invalid.", + argv[3]); + print_sol_usage(); + return -1; + } + } + + while (cnt > 0) { + printf("remain loop test counter: %d\n", cnt); + retval = ipmi_sol_activate(intf, 1, interval, instance); + if (retval) { + printf("SOL looptest failed: %d\n", + retval); + break; + } + cnt -= 1; + } + } else { + print_sol_usage(); + retval = -1; + } + return retval; +} diff --git a/lib/ipmi_strings.c b/lib/ipmi_strings.c new file mode 100644 index 0000000..277b82f --- /dev/null +++ b/lib/ipmi_strings.c @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stddef.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_sensor.h> +#include <ipmitool/ipmi_sel.h> /* for IPMI_OEM */ + +const struct valstr ipmi_oem_info[] = { + + { IPMI_OEM_UNKNOWN, "Unknown" }, + { IPMI_OEM_HP, "Hewlett-Packard" }, + { IPMI_OEM_SUN, "Sun Microsystems" }, + { IPMI_OEM_INTEL, "Intel Corporation" }, + { IPMI_OEM_LMC, "LMC" }, + { IPMI_OEM_RADISYS, "RadiSys Corporation" }, + { IPMI_OEM_TYAN, "Tyan Computer Corporation" }, + { IPMI_OEM_NEWISYS, "Newisys" }, + { IPMI_OEM_SUPERMICRO, "Supermicro" }, + { IPMI_OEM_GOOGLE, "Google" }, + { IPMI_OEM_KONTRON, "Kontron" }, + { IPMI_OEM_NOKIA, "Nokia" }, + { IPMI_OEM_PICMG, "PICMG" }, + { IPMI_OEM_PEPPERCON, "Peppercon AG" }, + { IPMI_OEM_DELL, "DELL Inc" }, + { IPMI_OEM_NEC, "NEC" }, + { IPMI_OEM_MAGNUM, "Magnum Technologies" }, + { IPMI_OEM_FUJITSU_SIEMENS, "Fujitsu Siemens" }, + { IPMI_OEM_TATUNG, "Tatung" }, + { IPMI_OEM_AMI, "AMI" }, + { IPMI_OEM_RARITAN, "Raritan" }, + { IPMI_OEM_AVOCENT, "Avocent" }, + { IPMI_OEM_OSA, "OSA" }, + { IPMI_OEM_TOSHIBA, "Toshiba" }, + { IPMI_OEM_HITACHI_116, "Hitachi" }, + { IPMI_OEM_HITACHI_399, "Hitachi" }, + { IPMI_OEM_NOKIA_SIEMENS_NETWORKS, "Nokia Siemens Networks" }, + { IPMI_OEM_BULL, "Bull Company" }, + { IPMI_OEM_PPS, "Pigeon Point Systems" }, + { IPMI_OEM_BROADCOM, "Broadcom Corporation" }, + { 0xffff , NULL }, +}; + +const struct oemvalstr ipmi_oem_product_info[] = { + /* Keep OEM grouped together */ + /* Intel stuff, thanks to Tim Bell */ + { IPMI_OEM_INTEL, 0x000C, "TSRLT2" }, + { IPMI_OEM_INTEL, 0x001B, "TIGPR2U" }, + { IPMI_OEM_INTEL, 0x0022, "TIGI2U" }, + { IPMI_OEM_INTEL, 0x0026, "Bridgeport" }, + { IPMI_OEM_INTEL, 0x0028, "S5000PAL" }, + { IPMI_OEM_INTEL, 0x0029, "S5000PSL" }, + { IPMI_OEM_INTEL, 0x0100, "Tiger4" }, + { IPMI_OEM_INTEL, 0x0103, "McCarran" }, + { IPMI_OEM_INTEL, 0x0800, "ZT5504" }, + { IPMI_OEM_INTEL, 0x0808, "MPCBL0001" }, + { IPMI_OEM_INTEL, 0x0811, "TIGW1U" }, + { IPMI_OEM_INTEL, 0x4311, "NSI2U" }, + /* Kontron */ + { IPMI_OEM_KONTRON,4000, "AM4000 AdvancedMC" }, + { IPMI_OEM_KONTRON,4001, "AM4001 AdvancedMC" }, + { IPMI_OEM_KONTRON,4002, "AM4002 AdvancedMC" }, + { IPMI_OEM_KONTRON,4010, "AM4010 AdvancedMC" }, + { IPMI_OEM_KONTRON,5503, "AM4500/4520 AdvancedMC" }, + { IPMI_OEM_KONTRON,5504, "AM4300 AdvancedMC" }, + { IPMI_OEM_KONTRON,5507, "AM4301 AdvancedMC" }, + { IPMI_OEM_KONTRON,5508, "AM4330 AdvancedMC" }, + { IPMI_OEM_KONTRON,5520, "KTC5520/EATX" }, + { IPMI_OEM_KONTRON,5703, "RTM8020" }, + { IPMI_OEM_KONTRON,5704, "RTM8030" }, + { IPMI_OEM_KONTRON,5705, "RTM8050" }, + { IPMI_OEM_KONTRON,6000, "CP6000" }, + { IPMI_OEM_KONTRON,6006, "DT-64" }, + { IPMI_OEM_KONTRON,6010, "CP6010" }, + { IPMI_OEM_KONTRON,6011, "CP6011" }, + { IPMI_OEM_KONTRON,6012, "CP6012" }, + { IPMI_OEM_KONTRON,6014, "CP6014" }, + { IPMI_OEM_KONTRON,5002, "AT8001" }, + { IPMI_OEM_KONTRON,5003, "AT8010" }, + { IPMI_OEM_KONTRON,5004, "AT8020" }, + { IPMI_OEM_KONTRON,5006, "AT8030 IPMC" }, + { IPMI_OEM_KONTRON,2025, "AT8030 MMC" }, + { IPMI_OEM_KONTRON,5007, "AT8050" }, + { IPMI_OEM_KONTRON,5301, "AT8400" }, + { IPMI_OEM_KONTRON,5303, "AT8901" }, + /* Broadcom */ + { IPMI_OEM_BROADCOM, 5725, "BCM5725" }, + + { 0xffffff , 0xffff , NULL }, + }; + +const struct oemvalstr ipmi_oem_sdr_type_vals[] = { + /* Keep OEM grouped together */ + { IPMI_OEM_KONTRON , 0xC0 , "OEM Firmware Info" }, + { IPMI_OEM_KONTRON , 0xC2 , "OEM Init Agent" }, + { IPMI_OEM_KONTRON , 0xC3 , "OEM IPMBL Link State" }, + { IPMI_OEM_KONTRON , 0xC4 , "OEM Board Reset" }, + { IPMI_OEM_KONTRON , 0xC5 , "OEM FRU Information Agent" }, + { IPMI_OEM_KONTRON , 0xC6 , "OEM POST Value Sensor" }, + { IPMI_OEM_KONTRON , 0xC7 , "OEM FWUM Status" }, + { IPMI_OEM_KONTRON , 0xC8 , "OEM Switch Mngt Software Status" }, + { IPMI_OEM_KONTRON , 0xC9 , "OEM OEM Diagnostic Status" }, + { IPMI_OEM_KONTRON , 0xCA , "OEM Component Firmware Upgrade" }, + { IPMI_OEM_KONTRON , 0xCB , "OEM FRU Over Current" }, + { IPMI_OEM_KONTRON , 0xCC , "OEM FRU Sensor Error" }, + { IPMI_OEM_KONTRON , 0xCD , "OEM FRU Power Denied" }, + { IPMI_OEM_KONTRON , 0xCE , "OEM Reserved" }, + { IPMI_OEM_KONTRON , 0xCF , "OEM Board Reset" }, + { IPMI_OEM_KONTRON , 0xD0 , "OEM Clock Resource Control" }, + { IPMI_OEM_KONTRON , 0xD1 , "OEM Power State" }, + { IPMI_OEM_KONTRON , 0xD2 , "OEM FRU Mngt Power Failure" }, + { IPMI_OEM_KONTRON , 0xD3 , "OEM Jumper Status" }, + { IPMI_OEM_KONTRON , 0xF2 , "OEM RTM Module Hotswap" }, + + { IPMI_OEM_PICMG , 0xF0 , "PICMG FRU Hotswap" }, + { IPMI_OEM_PICMG , 0xF1 , "PICMG IPMB0 Link State" }, + { IPMI_OEM_PICMG , 0xF2 , "PICMG Module Hotswap" }, + + { 0xffffff, 0x00, NULL } +}; + +const struct valstr ipmi_netfn_vals[] = { + { IPMI_NETFN_CHASSIS, "Chassis" }, + { IPMI_NETFN_BRIDGE, "Bridge" }, + { IPMI_NETFN_SE, "SensorEvent" }, + { IPMI_NETFN_APP, "Application" }, + { IPMI_NETFN_FIRMWARE, "Firmware" }, + { IPMI_NETFN_STORAGE, "Storage" }, + { IPMI_NETFN_TRANSPORT, "Transport" }, + { 0xff, NULL }, +}; + +/* + * From table 26-4 of the IPMI v2 specification + */ +const struct valstr ipmi_bit_rate_vals[] = { + { 0x00, "IPMI-Over-Serial-Setting"}, /* Using the value in the IPMI Over Serial Config */ + { 0x06, "9.6" }, + { 0x07, "19.2" }, + { 0x08, "38.4" }, + { 0x09, "57.6" }, + { 0x0A, "115.2" }, + { 0x00, NULL }, +}; + +const struct valstr ipmi_channel_activity_type_vals[] = { + { 0, "IPMI Messaging session active" }, + { 1, "Callback Messaging session active" }, + { 2, "Dial-out Alert active" }, + { 3, "TAP Page Active" }, + { 0x00, NULL }, +}; + + +const struct valstr ipmi_privlvl_vals[] = { + { IPMI_SESSION_PRIV_CALLBACK, "CALLBACK" }, + { IPMI_SESSION_PRIV_USER, "USER" }, + { IPMI_SESSION_PRIV_OPERATOR, "OPERATOR" }, + { IPMI_SESSION_PRIV_ADMIN, "ADMINISTRATOR" }, + { IPMI_SESSION_PRIV_OEM, "OEM" }, + { 0xF, "NO ACCESS" }, + { 0xFF, NULL }, +}; + + +const struct valstr ipmi_set_in_progress_vals[] = { + { IPMI_SET_IN_PROGRESS_SET_COMPLETE, "set-complete" }, + { IPMI_SET_IN_PROGRESS_IN_PROGRESS, "set-in-progress" }, + { IPMI_SET_IN_PROGRESS_COMMIT_WRITE, "commit-write" }, + { 0, NULL }, +}; + + +const struct valstr ipmi_authtype_session_vals[] = { + { IPMI_SESSION_AUTHTYPE_NONE, "NONE" }, + { IPMI_SESSION_AUTHTYPE_MD2, "MD2" }, + { IPMI_SESSION_AUTHTYPE_MD5, "MD5" }, + { IPMI_SESSION_AUTHTYPE_PASSWORD, "PASSWORD" }, + { IPMI_SESSION_AUTHTYPE_OEM, "OEM" }, + { IPMI_SESSION_AUTHTYPE_RMCP_PLUS,"RMCP+" }, + { 0xFF, NULL }, +}; + + +const struct valstr ipmi_authtype_vals[] = { + { IPMI_1_5_AUTH_TYPE_BIT_NONE, "NONE" }, + { IPMI_1_5_AUTH_TYPE_BIT_MD2, "MD2" }, + { IPMI_1_5_AUTH_TYPE_BIT_MD5, "MD5" }, + { IPMI_1_5_AUTH_TYPE_BIT_PASSWORD, "PASSWORD" }, + { IPMI_1_5_AUTH_TYPE_BIT_OEM, "OEM" }, + { 0, NULL }, +}; + +const struct valstr entity_id_vals[] = { + { 0x00, "Unspecified" }, + { 0x01, "Other" }, + { 0x02, "Unknown" }, + { 0x03, "Processor" }, + { 0x04, "Disk or Disk Bay" }, + { 0x05, "Peripheral Bay" }, + { 0x06, "System Management Module" }, + { 0x07, "System Board" }, + { 0x08, "Memory Module" }, + { 0x09, "Processor Module" }, + { 0x0a, "Power Supply" }, + { 0x0b, "Add-in Card" }, + { 0x0c, "Front Panel Board" }, + { 0x0d, "Back Panel Board" }, + { 0x0e, "Power System Board" }, + { 0x0f, "Drive Backplane" }, + { 0x10, "System Internal Expansion Board" }, + { 0x11, "Other System Board" }, + { 0x12, "Processor Board" }, + { 0x13, "Power Unit" }, + { 0x14, "Power Module" }, + { 0x15, "Power Management" }, + { 0x16, "Chassis Back Panel Board" }, + { 0x17, "System Chassis" }, + { 0x18, "Sub-Chassis" }, + { 0x19, "Other Chassis Board" }, + { 0x1a, "Disk Drive Bay" }, + { 0x1b, "Peripheral Bay" }, + { 0x1c, "Device Bay" }, + { 0x1d, "Fan Device" }, + { 0x1e, "Cooling Unit" }, + { 0x1f, "Cable/Interconnect" }, + { 0x20, "Memory Device" }, + { 0x21, "System Management Software" }, + { 0x22, "BIOS" }, + { 0x23, "Operating System" }, + { 0x24, "System Bus" }, + { 0x25, "Group" }, + { 0x26, "Remote Management Device" }, + { 0x27, "External Environment" }, + { 0x28, "Battery" }, + { 0x29, "Processing Blade" }, + { 0x2A, "Connectivity Switch" }, + { 0x2B, "Processor/Memory Module" }, + { 0x2C, "I/O Module" }, + { 0x2D, "Processor/IO Module" }, + { 0x2E, "Management Controller Firmware" }, + { 0x2F, "IPMI Channel" }, + { 0x30, "PCI Bus" }, + { 0x31, "PCI Express Bus" }, + { 0x32, "SCSI Bus (parallel)" }, + { 0x33, "SATA/SAS Bus" }, + { 0x34, "Processor/Front-Side Bus" }, + { 0x35, "Real Time Clock(RTC)" }, + { 0x36, "Reserved" }, + { 0x37, "Air Inlet" }, + { 0x38, "Reserved" }, + { 0x39, "Reserved" }, + { 0x3A, "Reserved" }, + { 0x3B, "Reserved" }, + { 0x3C, "Reserved" }, + { 0x3D, "Reserved" }, + { 0x3E, "Reserved" }, + { 0x3F, "Reserved" }, + { 0x40, "Air Inlet" }, + { 0x41, "Processor" }, + { 0x42, "Baseboard/Main System Board" }, + /* PICMG */ + { 0xA0, "PICMG Front Board" }, + { 0xC0, "PICMG Rear Transition Module" }, + { 0xC1, "PICMG AdvancedMC Module" }, + { 0xF0, "PICMG Shelf Management Controller" }, + { 0xF1, "PICMG Filtration Unit" }, + { 0xF2, "PICMG Shelf FRU Information" }, + { 0xF3, "PICMG Alarm Panel" }, + { 0x00, NULL }, +}; + +const struct valstr entity_device_type_vals[] = { + { 0x00, "Reserved" }, + { 0x01, "Reserved" }, + { 0x02, "DS1624 temperature sensor" }, + { 0x03, "DS1621 temperature sensor" }, + { 0x04, "LM75 Temperature Sensor" }, + { 0x05, "Heceta ASIC" }, + { 0x06, "Reserved" }, + { 0x07, "Reserved" }, + { 0x08, "EEPROM, 24C01" }, + { 0x09, "EEPROM, 24C02" }, + { 0x0a, "EEPROM, 24C04" }, + { 0x0b, "EEPROM, 24C08" }, + { 0x0c, "EEPROM, 24C16" }, + { 0x0d, "EEPROM, 24C17" }, + { 0x0e, "EEPROM, 24C32" }, + { 0x0f, "EEPROM, 24C64" }, + { 0x1000, "IPMI FRU Inventory" }, + { 0x1001, "DIMM Memory ID" }, + { 0x1002, "IPMI FRU Inventory" }, + { 0x1003, "System Processor Cartridge FRU" }, + { 0x11, "Reserved" }, + { 0x12, "Reserved" }, + { 0x13, "Reserved" }, + { 0x14, "PCF 8570 256 byte RAM" }, + { 0x15, "PCF 8573 clock/calendar" }, + { 0x16, "PCF 8574A I/O Port" }, + { 0x17, "PCF 8583 clock/calendar" }, + { 0x18, "PCF 8593 clock/calendar" }, + { 0x19, "Clock calendar" }, + { 0x1a, "PCF 8591 A/D, D/A Converter" }, + { 0x1b, "I/O Port" }, + { 0x1c, "A/D Converter" }, + { 0x1d, "D/A Converter" }, + { 0x1e, "A/D, D/A Converter" }, + { 0x1f, "LCD Controller/Driver" }, + { 0x20, "Core Logic (Chip set) Device" }, + { 0x21, "LMC6874 Intelligent Battery controller" }, + { 0x22, "Intelligent Batter controller" }, + { 0x23, "Combo Management ASIC" }, + { 0x24, "Maxim 1617 Temperature Sensor" }, + { 0xbf, "Other/Unspecified" }, + { 0x00, NULL }, +}; + +const struct valstr ipmi_channel_protocol_vals[] = { + { 0x00, "reserved" }, + { 0x01, "IPMB-1.0" }, + { 0x02, "ICMB-1.0" }, + { 0x03, "reserved" }, + { 0x04, "IPMI-SMBus" }, + { 0x05, "KCS" }, + { 0x06, "SMIC" }, + { 0x07, "BT-10" }, + { 0x08, "BT-15" }, + { 0x09, "TMode" }, + { 0x1c, "OEM 1" }, + { 0x1d, "OEM 2" }, + { 0x1e, "OEM 3" }, + { 0x1f, "OEM 4" }, + { 0x00, NULL }, +}; + + +const struct valstr ipmi_channel_medium_vals[] = { + { IPMI_CHANNEL_MEDIUM_RESERVED, "reserved" }, + { IPMI_CHANNEL_MEDIUM_IPMB_I2C, "IPMB (I2C)" }, + { IPMI_CHANNEL_MEDIUM_ICMB_1, "ICMB v1.0" }, + { IPMI_CHANNEL_MEDIUM_ICMB_09, "ICMB v0.9" }, + { IPMI_CHANNEL_MEDIUM_LAN, "802.3 LAN" }, + { IPMI_CHANNEL_MEDIUM_SERIAL, "Serial/Modem" }, + { IPMI_CHANNEL_MEDIUM_LAN_OTHER,"Other LAN" }, + { IPMI_CHANNEL_MEDIUM_SMBUS_PCI,"PCI SMBus" }, + { IPMI_CHANNEL_MEDIUM_SMBUS_1, "SMBus v1.0/v1.1" }, + { IPMI_CHANNEL_MEDIUM_SMBUS_2, "SMBus v2.0" }, + { IPMI_CHANNEL_MEDIUM_USB_1, "USB 1.x" }, + { IPMI_CHANNEL_MEDIUM_USB_2, "USB 2.x" }, + { IPMI_CHANNEL_MEDIUM_SYSTEM, "System Interface" }, + { 0x00, NULL }, +}; + +const struct valstr completion_code_vals[] = { + { 0x00, "Command completed normally" }, + { 0xc0, "Node busy" }, + { 0xc1, "Invalid command" }, + { 0xc2, "Invalid command on LUN" }, + { 0xc3, "Timeout" }, + { 0xc4, "Out of space" }, + { 0xc5, "Reservation cancelled or invalid" }, + { 0xc6, "Request data truncated" }, + { 0xc7, "Request data length invalid" }, + { 0xc8, "Request data field length limit exceeded" }, + { 0xc9, "Parameter out of range" }, + { 0xca, "Cannot return number of requested data bytes" }, + { 0xcb, "Requested sensor, data, or record not found" }, + { 0xcc, "Invalid data field in request" }, + { 0xcd, "Command illegal for specified sensor or record type" }, + { 0xce, "Command response could not be provided" }, + { 0xcf, "Cannot execute duplicated request" }, + { 0xd0, "SDR Repository in update mode" }, + { 0xd1, "Device firmeware in update mode" }, + { 0xd2, "BMC initialization in progress" }, + { 0xd3, "Destination unavailable" }, + { 0xd4, "Insufficient privilege level" }, + { 0xd5, "Command not supported in present state" }, + { 0xd6, "Cannot execute command, command disabled" }, + { 0xff, "Unspecified error" }, + { 0x00, NULL } +}; + +const struct valstr ipmi_chassis_power_control_vals[] = { + { IPMI_CHASSIS_CTL_POWER_DOWN, "Down/Off" }, + { IPMI_CHASSIS_CTL_POWER_UP, "Up/On" }, + { IPMI_CHASSIS_CTL_POWER_CYCLE, "Cycle" }, + { IPMI_CHASSIS_CTL_HARD_RESET, "Reset" }, + { IPMI_CHASSIS_CTL_PULSE_DIAG, "Diag" }, + { IPMI_CHASSIS_CTL_ACPI_SOFT, "Soft" }, + { 0x00, NULL }, +}; + +const struct valstr ipmi_auth_algorithms[] = { + { IPMI_AUTH_RAKP_NONE, "none" }, + { IPMI_AUTH_RAKP_HMAC_SHA1, "hmac_sha1" }, + { IPMI_AUTH_RAKP_HMAC_MD5, "hmac_md5" }, + { 0x00, NULL } +}; + +const struct valstr ipmi_integrity_algorithms[] = { + { IPMI_INTEGRITY_NONE, "none" }, + { IPMI_INTEGRITY_HMAC_SHA1_96, "hmac_sha1_96" }, + { IPMI_INTEGRITY_HMAC_MD5_128, "hmac_md5_128" }, + { IPMI_INTEGRITY_MD5_128 , "md5_128" }, + { 0x00, NULL } +}; + +const struct valstr ipmi_encryption_algorithms[] = { + { IPMI_CRYPT_NONE, "none" }, + { IPMI_CRYPT_AES_CBC_128, "aes_cbc_128" }, + { IPMI_CRYPT_XRC4_128, "xrc4_128" }, + { IPMI_CRYPT_XRC4_40, "xrc4_40" }, + { 0x00, NULL } +}; + +const struct valstr picmg_frucontrol_vals[] = { + { 0, "Cold Reset" }, + { 1, "Warm Reset" }, + { 2, "Graceful Reboot" }, + { 3, "Issue Diagnostic Interrupt" }, + { 4, "Quiesce" }, + { 5, NULL }, +}; + +const struct valstr picmg_clk_family_vals[] = { + { 0x00, "Unspecified" }, + { 0x01, "SONET/SDH/PDH" }, + { 0x02, "Reserved for PCI Express" }, + { 0x03, "Reserved" }, /* from 03h to C8h */ + { 0xC9, "Vendor defined clock family" }, /* from C9h to FFh */ + { 0x00, NULL }, +}; + +const struct oemvalstr picmg_clk_accuracy_vals[] = { + { 0x01, 10, "PRS" }, + { 0x01, 20, "STU" }, + { 0x01, 30, "ST2" }, + { 0x01, 40, "TNC" }, + { 0x01, 50, "ST3E" }, + { 0x01, 60, "ST3" }, + { 0x01, 70, "SMC" }, + { 0x01, 80, "ST4" }, + { 0x01, 90, "DUS" }, + { 0x02, 0xE0, "PCI Express Generation 2" }, + { 0x02, 0xF0, "PCI Express Generation 1" }, + { 0xffffff, 0x00, NULL } +}; + +const struct oemvalstr picmg_clk_resource_vals[] = { + { 0x0, 0, "On-Carrier Device 0" }, + { 0x0, 1, "On-Carrier Device 1" }, + { 0x1, 1, "AMC Site 1 - A1" }, + { 0x1, 2, "AMC Site 1 - A2" }, + { 0x1, 3, "AMC Site 1 - A3" }, + { 0x1, 4, "AMC Site 1 - A4" }, + { 0x1, 5, "AMC Site 1 - B1" }, + { 0x1, 6, "AMC Site 1 - B2" }, + { 0x1, 7, "AMC Site 1 - B3" }, + { 0x1, 8, "AMC Site 1 - B4" }, + { 0x2, 0, "ATCA Backplane" }, + { 0xffffff, 0x00, NULL } +}; + +const struct oemvalstr picmg_clk_id_vals[] = { + { 0x0, 0, "Clock 0" }, + { 0x0, 1, "Clock 1" }, + { 0x0, 2, "Clock 2" }, + { 0x0, 3, "Clock 3" }, + { 0x0, 4, "Clock 4" }, + { 0x0, 5, "Clock 5" }, + { 0x0, 6, "Clock 6" }, + { 0x0, 7, "Clock 7" }, + { 0x0, 8, "Clock 8" }, + { 0x0, 9, "Clock 9" }, + { 0x0, 10, "Clock 10" }, + { 0x0, 11, "Clock 11" }, + { 0x0, 12, "Clock 12" }, + { 0x0, 13, "Clock 13" }, + { 0x0, 14, "Clock 14" }, + { 0x0, 15, "Clock 15" }, + { 0x1, 1, "TCLKA" }, + { 0x1, 2, "TCLKB" }, + { 0x1, 3, "TCLKC" }, + { 0x1, 4, "TCLKD" }, + { 0x1, 5, "FLCKA" }, + { 0x2, 1, "CLK1A" }, + { 0x2, 2, "CLK1A" }, + { 0x2, 3, "CLK1A" }, + { 0x2, 4, "CLK1A" }, + { 0x2, 5, "CLK1A" }, + { 0x2, 6, "CLK1A" }, + { 0x2, 7, "CLK1A" }, + { 0x2, 8, "CLK1A" }, + { 0x2, 9, "CLK1A" }, + { 0xffffff, 0x00, NULL } +}; + +const struct valstr picmg_busres_id_vals[] = { + { 0x0, "Metallic Test Bus pair #1" }, + { 0x1, "Metallic Test Bus pair #2" }, + { 0x2, "Synch clock group 1 (CLK1)" }, + { 0x3, "Synch clock group 2 (CLK2)" }, + { 0x4, "Synch clock group 3 (CLK3)" }, + { 0x5, NULL } +}; +const struct valstr picmg_busres_board_cmd_vals[] = { + { 0x0, "Query" }, + { 0x1, "Release" }, + { 0x2, "Force" }, + { 0x3, "Bus Free" }, + { 0x4, NULL } +}; + +const struct valstr picmg_busres_shmc_cmd_vals[] = { + { 0x0, "Request" }, + { 0x1, "Relinquish" }, + { 0x2, "Notify" }, + { 0x3, NULL } +}; + +const struct oemvalstr picmg_busres_board_status_vals[] = { + { 0x0, 0x0, "In control" }, + { 0x0, 0x1, "No control" }, + { 0x1, 0x0, "Ack" }, + { 0x1, 0x1, "Refused" }, + { 0x1, 0x2, "No control" }, + { 0x2, 0x0, "Ack" }, + { 0x2, 0x1, "No control" }, + { 0x3, 0x0, "Accept" }, + { 0x3, 0x1, "Not Needed" }, + { 0xffffff, 0x00, NULL } +}; + +const struct oemvalstr picmg_busres_shmc_status_vals[] = { + { 0x0, 0x0, "Grant" }, + { 0x0, 0x1, "Busy" }, + { 0x0, 0x2, "Defer" }, + { 0x0, 0x3, "Deny" }, + + { 0x1, 0x0, "Ack" }, + { 0x1, 0x1, "Error" }, + + { 0x2, 0x0, "Ack" }, + { 0x2, 0x1, "Error" }, + { 0x2, 0x2, "Deny" }, + + { 0xffffff, 0x00, NULL } +}; diff --git a/lib/ipmi_sunoem.c b/lib/ipmi_sunoem.c new file mode 100644 index 0000000..16a6090 --- /dev/null +++ b/lib/ipmi_sunoem.c @@ -0,0 +1,2434 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> +#include <sys/time.h> +#include <limits.h> +#include <fcntl.h> + +#include <termios.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_sunoem.h> +#include <ipmitool/ipmi_raw.h> + +static const struct valstr sunoem_led_type_vals[] = { + { 0, "OK2RM" }, + { 1, "SERVICE" }, + { 2, "ACT" }, + { 3, "LOCATE" }, + { 0xFF, NULL }, +}; + +static const struct valstr sunoem_led_mode_vals[] = { + { 0, "OFF" }, + { 1, "ON" }, + { 2, "STANDBY" }, + { 3, "SLOW" }, + { 4, "FAST" }, + { 0xFF, NULL }, +}; + +static const struct valstr sunoem_led_mode_optvals[] = { + { 0, "STEADY_OFF" }, + { 1, "STEADY_ON" }, + { 2, "STANDBY_BLINK" }, + { 3, "SLOW_BLINK" }, + { 4, "FAST_BLINK" }, + { 0xFF, NULL }, +}; + +#define SUNOEM_SUCCESS 1 + +#define IPMI_SUNOEM_GETFILE_VERSION {3,2,0,0} +#define IPMI_SUNOEM_GETBEHAVIOR_VERSION {3,2,0,0} + +/* + * PRINT_NORMAL: print out the LED value as normal + * PRINT_ERROR: print out "na" for the LED value + */ +typedef enum +{ + PRINT_NORMAL = 0, PRINT_ERROR +} print_status_t; + +int ret_get = 0; +int ret_set = 0; + +static void +ipmi_sunoem_usage(void) +{ + lprintf(LOG_NOTICE, "Usage: sunoem <command> [option...]"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "Commands:"); + lprintf(LOG_NOTICE, " - cli [<command string> ...]"); + lprintf(LOG_NOTICE, " Execute SP CLI commands."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - led get [<sensor_id>] [ledtype]"); + lprintf(LOG_NOTICE, " - Read status of LED found in Generic Device Locator."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - led set <sensor_id> <led_mode> [led_type]"); + lprintf(LOG_NOTICE, " - Set mode of LED found in Generic Device Locator."); + lprintf(LOG_NOTICE, " - You can pass 'all' as the <senso_rid> to change the LED mode of all sensors."); + lprintf(LOG_NOTICE, " - Use 'sdr list generic' command to get list of Generic"); + lprintf(LOG_NOTICE, " - Devices that are controllable LEDs."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - Required SIS LED Mode:"); + lprintf(LOG_NOTICE, " OFF Off"); + lprintf(LOG_NOTICE, " ON Steady On"); + lprintf(LOG_NOTICE, " STANDBY 100ms on 2900ms off blink rate"); + lprintf(LOG_NOTICE, " SLOW 1HZ blink rate"); + lprintf(LOG_NOTICE, " FAST 4HZ blink rate"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - Optional SIS LED Type:"); + lprintf(LOG_NOTICE, " OK2RM OK to Remove"); + lprintf(LOG_NOTICE, " SERVICE Service Required"); + lprintf(LOG_NOTICE, " ACT Activity"); + lprintf(LOG_NOTICE, " LOCATE Locate"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>"); + lprintf(LOG_NOTICE, " - Returns the full nac name"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - ping NUMBER <q>"); + lprintf(LOG_NOTICE, " - Send and Receive NUMBER (64 Byte) packets."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - q - Quiet. Displays output at start and end"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - getval <target_name>"); + lprintf(LOG_NOTICE, " - Returns the ILOM property value"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - setval <property name> <property value> <timeout>"); + lprintf(LOG_NOTICE, " - Sets the ILOM property value"); + lprintf(LOG_NOTICE, " - If timeout is not specified, the default is 5 sec."); + lprintf(LOG_NOTICE, " - NOTE: must be executed locally on host, not remotely over LAN!"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - sshkey del <user_id>"); + lprintf(LOG_NOTICE, " - Delete ssh key for user id from authorized_keys,"); + lprintf(LOG_NOTICE, " - view users with 'user list' command."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - sshkey set <user_id> <id_rsa.pub>"); + lprintf(LOG_NOTICE, " - Set ssh key for a userid into authorized_keys,"); + lprintf(LOG_NOTICE, " - view users with 'user list' command."); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - version"); + lprintf(LOG_NOTICE, " - Display the software version"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>"); + lprintf(LOG_NOTICE, " - Returns the full nac name"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - getfile <file_string_id> <destination_file_name>"); + lprintf(LOG_NOTICE, " - Copy file <file_string_id> to <destination_file_name>"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - File string ids:"); + lprintf(LOG_NOTICE, " SSH_PUBKEYS"); + lprintf(LOG_NOTICE, " DIAG_PASSED"); + lprintf(LOG_NOTICE, " DIAG_FAILED"); + lprintf(LOG_NOTICE, " DIAG_END_TIME"); + lprintf(LOG_NOTICE, " DIAG_INVENTORY"); + lprintf(LOG_NOTICE, " DIAG_TEST_LOG"); + lprintf(LOG_NOTICE, " DIAG_START_TIME"); + lprintf(LOG_NOTICE, " DIAG_UEFI_LOG"); + lprintf(LOG_NOTICE, " DIAG_TEST_LOG"); + lprintf(LOG_NOTICE, " DIAG_LAST_LOG"); + lprintf(LOG_NOTICE, " DIAG_LAST_CMD"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - getbehavior <behavior_string_id>"); + lprintf(LOG_NOTICE, " - Test if ILOM behavior is enabled"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, " - Behavior string ids:"); + lprintf(LOG_NOTICE, " SUPPORTS_SIGNED_PACKAGES"); + lprintf(LOG_NOTICE, " REQUIRES_SIGNED_PACKAGES"); + lprintf(LOG_NOTICE, ""); +} + +#define SUNOEM_FAN_MODE_AUTO 0x00 +#define SUNOEM_FAN_MODE_MANUAL 0x01 + +static void +__sdr_list_empty(struct sdr_record_list * head) +{ + struct sdr_record_list * e, *f; + for (e = head; e != NULL; e = f) { + f = e->next; + free(e); + } + head = NULL; +} + +/* + * led_print + * Print out the led name and the state, if stat is PRINT_NORMAL. + * Otherwise, print out the led name and "na". + * The state parameter is not referenced if stat is not PRINT_NORMAL. + */ +static void +led_print(const char * name, print_status_t stat, uint8_t state) +{ + const char *theValue; + + if (stat == PRINT_NORMAL) { + theValue = val2str(state, sunoem_led_mode_vals); + } else { + theValue = "na"; + } + + if (csv_output) { + printf("%s,%s\n", name, theValue); + } else { + printf("%-16s | %s\n", name, theValue); + } +} + +#define CC_NORMAL 0x00 +#define CC_PARAM_OUT_OF_RANGE 0xc9 +#define CC_DEST_UNAVAILABLE 0xd3 +#define CC_UNSPECIFIED_ERR 0xff +#define CC_INSUFFICIENT_PRIVILEGE 0xd4 +#define CC_INV_CMD 0xc1 +#define CC_INV_DATA_FIELD 0xcc + +/* + * sunoem_led_get(....) + * + * OUTPUT: + * SUNOEM_EC_INVALID_ARG if dev is NULL, + * SUNOEM_EC_BMC_NOT_RESPONDING if no reply is obtained from BMC, + * SUNOEM_EC_BMC_CCODE_NONZERO if completion code is nonzero, + * SUNOEM_EC_SUCCESS otherwise. + */ +static sunoem_ec_t +sunoem_led_get(struct ipmi_intf * intf, struct sdr_record_generic_locator * dev, + int ledtype, struct ipmi_rs **loc_rsp) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[7]; + int rqdata_len; + + if (dev == NULL) { + *loc_rsp = NULL; + return (SUNOEM_EC_INVALID_ARG); + } + + rqdata[0] = dev->dev_slave_addr; + if (ledtype == 0xFF) + rqdata[1] = dev->oem; + else + rqdata[1] = ledtype; + + rqdata[2] = dev->dev_access_addr; + rqdata[3] = dev->oem; + rqdata[4] = dev->entity.id; + rqdata[5] = dev->entity.instance; + rqdata[6] = 0; + rqdata_len = 7; + + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_LED_GET; + req.msg.lun = dev->lun; + req.msg.data = rqdata; + req.msg.data_len = rqdata_len; + + rsp = intf->sendrecv(intf, &req); + /* + * Just return NULL if there was + * an error. + */ + if (rsp == NULL) { + *loc_rsp = NULL; + return (SUNOEM_EC_BMC_NOT_RESPONDING); + } else if (rsp->ccode > 0) { + *loc_rsp = rsp; + return (SUNOEM_EC_BMC_CCODE_NONZERO); + } else { + *loc_rsp = rsp; + return (SUNOEM_EC_SUCCESS); + } +} + +static struct ipmi_rs * +sunoem_led_set(struct ipmi_intf * intf, struct sdr_record_generic_locator * dev, + int ledtype, int ledmode) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[9]; + int rqdata_len; + + if (dev == NULL) + return NULL; + + rqdata[0] = dev->dev_slave_addr; + + if (ledtype == 0xFF) + rqdata[1] = dev->oem; + else + rqdata[1] = ledtype; + + rqdata[2] = dev->dev_access_addr; + rqdata[3] = dev->oem; + rqdata[4] = ledmode; + rqdata[5] = dev->entity.id; + rqdata[6] = dev->entity.instance; + rqdata[7] = 0; + rqdata[8] = 0; + rqdata_len = 9; + + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_LED_SET; + req.msg.lun = dev->lun; + req.msg.data = rqdata; + req.msg.data_len = rqdata_len; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM Set LED command failed."); + return NULL; + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Sun OEM Set LED command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return NULL; + } + + return (rsp); +} + +static void +sunoem_led_get_byentity(struct ipmi_intf * intf, uint8_t entity_id, + uint8_t entity_inst, int ledtype) +{ + struct ipmi_rs * rsp; + struct sdr_record_list *elist, *e; + struct entity_id entity; + sunoem_ec_t res; + + if (entity_id == 0) + return; + + /* lookup sdrs with this entity */ + memset(&entity, 0, sizeof(struct entity_id)); + entity.id = entity_id; + entity.instance = entity_inst; + + elist = ipmi_sdr_find_sdr_byentity(intf, &entity); + + if (elist == NULL) + ret_get = -1; + + /* for each generic sensor get its led state */ + for (e = elist; e != NULL; e = e->next) { + if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + continue; + + res = sunoem_led_get(intf, e->record.genloc, ledtype, &rsp); + + if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { + led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL, + rsp->data[0]); + } else { + led_print((const char *) e->record.genloc->id_string, PRINT_ERROR, + 0); + if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp + || rsp->ccode != CC_DEST_UNAVAILABLE) { + ret_get = -1; + } + } + } + __sdr_list_empty(elist); +} + +static void +sunoem_led_set_byentity(struct ipmi_intf * intf, uint8_t entity_id, + uint8_t entity_inst, int ledtype, int ledmode) +{ + struct ipmi_rs * rsp; + struct sdr_record_list *elist, *e; + struct entity_id entity; + + if (entity_id == 0) + return; + + /* lookup sdrs with this entity */ + memset(&entity, 0, sizeof(struct entity_id)); + entity.id = entity_id; + entity.instance = entity_inst; + + elist = ipmi_sdr_find_sdr_byentity(intf, &entity); + + if (elist == NULL) + ret_set = -1; + + /* for each generic sensor set its led state */ + for (e = elist; e != NULL; e = e->next) { + + if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + continue; + + rsp = sunoem_led_set(intf, e->record.genloc, ledtype, ledmode); + if (rsp && rsp->data_len == 0) { + led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL, + ledmode); + } else if (rsp == NULL) { + ret_set = -1; + } + } + + __sdr_list_empty(elist); +} + +/* + * IPMI Request Data: 5 bytes + * + * [byte 0] devAddr Value from the "Device Slave Address" field in + * LED's Generic Device Locator record in the SDR + * [byte 1] led LED Type: OK2RM, ACT, LOCATE, SERVICE + * [byte 2] ctrlrAddr Controller address; value from the "Device + * Access Address" field, 0x20 if the LED is local + * [byte 3] hwInfo The OEM field from the SDR record + * [byte 4] force 1 = directly access the device + * 0 = go thru its controller + * Ignored if LED is local + * + * The format below is for Sun Blade Modular systems only + * [byte 4] entityID The entityID field from the SDR record + * [byte 5] entityIns The entityIns field from the SDR record + * [byte 6] force 1 = directly access the device + * 0 = go thru its controller + * Ignored if LED is local + */ +static int +ipmi_sunoem_led_get(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct sdr_record_list *sdr; + struct sdr_record_list *alist, *a; + struct sdr_record_entity_assoc *assoc; + int ledtype = 0xFF; + int i; + sunoem_ec_t res; + + /* + * sunoem led/sbled get <id> [type] + */ + + if (argc < 1 || strncmp(argv[0], "help", 4) == 0) { + ipmi_sunoem_usage(); + return (0); + } + + if (argc > 1) { + ledtype = str2val(argv[1], sunoem_led_type_vals); + if (ledtype == 0xFF) + lprintf(LOG_ERR, + "Unknow ledtype, will use data from the SDR oem field"); + } + + if (strncasecmp(argv[0], "all", 3) == 0) { + /* do all generic sensors */ + alist = ipmi_sdr_find_sdr_bytype(intf, + SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + + if (alist == NULL) + return (-1); + + for (a = alist; a != NULL; a = a->next) { + if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + continue; + if (a->record.genloc->entity.logical) + continue; + + res = sunoem_led_get(intf, a->record.genloc, ledtype, &rsp); + + if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { + led_print((const char *) a->record.genloc->id_string, + PRINT_NORMAL, rsp->data[0]); + } else { + led_print((const char *) a->record.genloc->id_string, + PRINT_ERROR, 0); + if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp || + rsp->ccode != CC_DEST_UNAVAILABLE) { + ret_get = -1; + } + } + } + __sdr_list_empty(alist); + + if (ret_get == -1) + return (-1); + + return (0); + } + + /* look up generic device locator record in SDR */ + sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]); + + if (sdr == NULL) { + lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]); + return (-1); + } + + if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) { + lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type); + return (-1); + } + + if (!sdr->record.genloc->entity.logical) { + /* + * handle physical entity + */ + + res = sunoem_led_get(intf, sdr->record.genloc, ledtype, &rsp); + + if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { + led_print((const char *) sdr->record.genloc->id_string, + PRINT_NORMAL, rsp->data[0]); + + } else { + led_print((const char *) sdr->record.genloc->id_string, PRINT_ERROR, + 0); + if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp + || rsp->ccode != CC_DEST_UNAVAILABLE) { + ret_get = -1; + } + } + + if (ret_get == -1) + return (-1); + + return (0); + } + + /* + * handle logical entity for LED grouping + */ + + lprintf(LOG_INFO, "LED %s is logical device", argv[0]); + + /* get entity assoc records */ + alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC); + + if (alist == NULL) + return (-1); + + for (a = alist; a != NULL; a = a->next) { + if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC) + continue; + assoc = a->record.entassoc; + if (assoc == NULL) + continue; + + /* check that the entity id/instance matches our generic record */ + if (assoc->entity.id != sdr->record.genloc->entity.id + || assoc->entity.instance + != sdr->record.genloc->entity.instance) + continue; + + if (assoc->flags.isrange) { + /* + * handle ranged entity associations + * + * the test for non-zero entity id is handled in + * sunoem_led_get_byentity() + */ + + /* first range set - id 1 and 2 must be equal */ + if (assoc->entity_id_1 == assoc->entity_id_2) + for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++) + sunoem_led_get_byentity(intf, assoc->entity_id_1, i, + ledtype); + + /* second range set - id 3 and 4 must be equal */ + if (assoc->entity_id_3 == assoc->entity_id_4) + for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++) + sunoem_led_get_byentity(intf, assoc->entity_id_3, i, + ledtype); + } else { + /* + * handle entity list + */ + sunoem_led_get_byentity(intf, assoc->entity_id_1, + assoc->entity_inst_1, ledtype); + sunoem_led_get_byentity(intf, assoc->entity_id_2, + assoc->entity_inst_2, ledtype); + sunoem_led_get_byentity(intf, assoc->entity_id_3, + assoc->entity_inst_3, ledtype); + sunoem_led_get_byentity(intf, assoc->entity_id_4, + assoc->entity_inst_4, ledtype); + } + } + + __sdr_list_empty(alist); + + if (ret_get == -1) + return (-1); + + return (0); +} + +/* + * IPMI Request Data: 7 bytes + * + * [byte 0] devAddr Value from the "Device Slave Address" field in + * LED's Generic Device Locator record in the SDR + * [byte 1] led LED Type: OK2RM, ACT, LOCATE, SERVICE + * [byte 2] ctrlrAddr Controller address; value from the "Device + * Access Address" field, 0x20 if the LED is local + * [byte 3] hwInfo The OEM field from the SDR record + * [byte 4] mode LED Mode: OFF, ON, STANDBY, SLOW, FAST + * [byte 5] force TRUE - directly access the device + * FALSE - go thru its controller + * Ignored if LED is local + * [byte 6] role Used by BMC for authorization purposes + * + * The format below is for Sun Blade Modular systems only + * [byte 5] entityID The entityID field from the SDR record + * [byte 6] entityIns The entityIns field from the SDR record + * [byte 7] force TRUE - directly access the device + * FALSE - go thru its controller + * Ignored if LED is local + * [byte 8] role Used by BMC for authorization purposes + * + * + * IPMI Response Data: 1 byte + * + * [byte 0] mode LED Mode: OFF, ON, STANDBY, SLOW, FAST + */ + +static int +ipmi_sunoem_led_set(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct ipmi_rs * rsp; + struct sdr_record_list *sdr; + struct sdr_record_list *alist, *a; + struct sdr_record_entity_assoc *assoc; + int ledmode; + int ledtype = 0xFF; + int i; + + /* + * sunoem led/sbled set <id> <mode> [type] + */ + + if (argc < 2 || strncmp(argv[0], "help", 4) == 0) { + ipmi_sunoem_usage(); + return (0); + } + + ledmode = str2val(argv[1], sunoem_led_mode_vals); + if (ledmode == 0xFF) { + ledmode = str2val(argv[1], sunoem_led_mode_optvals); + if (ledmode == 0xFF) { + lprintf(LOG_NOTICE, "Invalid LED Mode: %s", argv[1]); + return (-1); + } + } + + if (argc > 3) { + ledtype = str2val(argv[2], sunoem_led_type_vals); + if (ledtype == 0xFF) + lprintf(LOG_ERR, + "Unknow ledtype, will use data from the SDR oem field"); + } + + if (strncasecmp(argv[0], "all", 3) == 0) { + /* do all generic sensors */ + alist = ipmi_sdr_find_sdr_bytype(intf, + SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + + if (alist == NULL) + return (-1); + + for (a = alist; a != NULL; a = a->next) { + if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) + continue; + if (a->record.genloc->entity.logical) + continue; + rsp = sunoem_led_set(intf, a->record.genloc, ledtype, ledmode); + if (rsp && rsp->ccode == 0) + led_print((const char *) a->record.genloc->id_string, + PRINT_NORMAL, ledmode); + else + ret_set = -1; + } + __sdr_list_empty(alist); + + if (ret_set == -1) + return (-1); + + return (0); + } + + /* look up generic device locator records in SDR */ + sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]); + + if (sdr == NULL) { + lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]); + return (-1); + } + + if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) { + lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type); + return (-1); + } + + if (!sdr->record.genloc->entity.logical) { + /* + * handle physical entity + */ + rsp = sunoem_led_set(intf, sdr->record.genloc, ledtype, ledmode); + if (rsp && rsp->ccode == 0) + led_print(argv[0], PRINT_NORMAL, ledmode); + else + return (-1); + + return (0); + } + + /* + * handle logical entity for LED grouping + */ + + lprintf(LOG_INFO, "LED %s is logical device", argv[0]); + + /* get entity assoc records */ + alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC); + + if (alist == NULL) + return (-1); + + for (a = alist; a != NULL; a = a->next) { + if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC) + continue; + assoc = a->record.entassoc; + if (assoc == NULL) + continue; + + /* check that the entity id/instance matches our generic record */ + if (assoc->entity.id != sdr->record.genloc->entity.id + || assoc->entity.instance + != sdr->record.genloc->entity.instance) + continue; + + if (assoc->flags.isrange) { + /* + * handle ranged entity associations + * + * the test for non-zero entity id is handled in + * sunoem_led_get_byentity() + */ + + /* first range set - id 1 and 2 must be equal */ + if (assoc->entity_id_1 == assoc->entity_id_2) + for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++) + sunoem_led_set_byentity(intf, assoc->entity_id_1, i, + ledtype, ledmode); + + /* second range set - id 3 and 4 must be equal */ + if (assoc->entity_id_3 == assoc->entity_id_4) + for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++) + sunoem_led_set_byentity(intf, assoc->entity_id_3, i, + ledtype, ledmode); + } else { + /* + * handle entity list + */ + sunoem_led_set_byentity(intf, assoc->entity_id_1, + assoc->entity_inst_1, ledtype, ledmode); + sunoem_led_set_byentity(intf, assoc->entity_id_2, + assoc->entity_inst_2, ledtype, ledmode); + sunoem_led_set_byentity(intf, assoc->entity_id_3, + assoc->entity_inst_3, ledtype, ledmode); + sunoem_led_set_byentity(intf, assoc->entity_id_4, + assoc->entity_inst_4, ledtype, ledmode); + } + } + + __sdr_list_empty(alist); + + if (ret_set == -1) + return (-1); + + return (0); +} + +static int +ipmi_sunoem_sshkey_del(struct ipmi_intf * intf, uint8_t uid) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(struct ipmi_rq)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_DEL_SSH_KEY; + req.msg.data = &uid; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to delete ssh key for UID %d", uid); + return (-1); + } else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to delete ssh key for UID %d: %s", uid, + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + printf("Deleted SSH key for user id %d\n", uid); + return (0); +} + +#define SSHKEY_BLOCK_SIZE 64 +static int +ipmi_sunoem_sshkey_set(struct ipmi_intf * intf, uint8_t uid, char * ifile) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + FILE * fp; + int count = 0; + uint8_t wbuf[SSHKEY_BLOCK_SIZE + 3]; + int32_t i_size = 0; + int32_t r = 0; + int32_t size = 0; + + if (ifile == NULL) { + lprintf(LOG_ERR, "Invalid or misisng input filename."); + return (-1); + } + + fp = ipmi_open_file_read(ifile); + if (fp == NULL) { + lprintf(LOG_ERR, "Unable to open file '%s' for reading.", ifile); + return (-1); + } + + memset(&req, 0, sizeof(struct ipmi_rq)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_SET_SSH_KEY; + req.msg.data = wbuf; + + if (fseek(fp, 0, SEEK_END) == (-1)) { + lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } + + size = (int32_t) ftell(fp); + if (size < 0) { + lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } else if (size == 0) { + lprintf(LOG_ERR, "File '%s' is empty.", ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } + + if (fseek(fp, 0, SEEK_SET) == (-1)) { + lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } + + printf("Setting SSH key for user id %d...", uid); + + for (r = 0; r < size; r += i_size) { + i_size = size - r; + if (i_size > SSHKEY_BLOCK_SIZE) + i_size = SSHKEY_BLOCK_SIZE; + + memset(wbuf, 0, SSHKEY_BLOCK_SIZE); + fseek(fp, r, SEEK_SET); + count = fread(wbuf + 3, 1, i_size, fp); + if (count != i_size) { + printf("failed\n"); + lprintf(LOG_ERR, "Unable to read %ld bytes from file '%s'.", i_size, + ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } + + printf("."); + fflush(stdout); + + wbuf[0] = uid; + if ((r + SSHKEY_BLOCK_SIZE) >= size) + wbuf[1] = 0xff; + else { + if ((r / SSHKEY_BLOCK_SIZE) > UINT8_MAX) { + printf("failed\n"); + lprintf(LOG_ERR, "Unable to pack byte %ld from file '%s'.", r, + ifile); + if (fp != NULL) + fclose(fp); + + return (-1); + } + wbuf[1] = (uint8_t) (r / SSHKEY_BLOCK_SIZE); + } + + wbuf[2] = (uint8_t) i_size; + + req.msg.data_len = i_size + 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + printf("failed\n"); + lprintf(LOG_ERR, "Unable to set ssh key for UID %d.", uid); + if (fp != NULL) + fclose(fp); + + return (-1); + } /* if (rsp == NULL) */ + if (rsp->ccode != 0) { + printf("failed\n"); + lprintf(LOG_ERR, "Unable to set ssh key for UID %d, %s.", uid, + val2str(rsp->ccode, completion_code_vals)); + if (fp != NULL) + fclose(fp); + + return (-1); + } /* if (rsp->ccode != 0) */ + } + + printf("done\n"); + + fclose(fp); + return (0); +} + +/* + * This structure is used in both the request to and response from the BMC. + */ +#define SUNOEM_CLI_LEGACY_VERSION 1 +#define SUNOEM_CLI_SEQNUM_VERSION 2 +#define SUNOEM_CLI_VERSION SUNOEM_CLI_SEQNUM_VERSION +#define SUNOEM_CLI_HEADER 8 /* command + spare + handle */ +#define SUNOEM_CLI_BUF_SIZE (80 - SUNOEM_CLI_HEADER) /* Total 80 bytes */ +#define SUNOEM_CLI_MSG_SIZE(msg) (SUNOEM_CLI_HEADER + strlen((msg).buf) + 1) + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + /* + * Set version to SUNOEM_CLI_VERSION. + */ + uint8_t version; + /* + * The command in a request, or in a response indicates an error if + * non-zero. + */ + uint8_t command_response; + uint8_t seqnum; + uint8_t spare; + /* + * Opaque 4-byte handle, supplied in the response to an OPEN request, + * and used in all subsequent POLL and CLOSE requests. + */ + uint8_t handle[4]; + /* + * The client data in a request, or the server data in a response. Must + * by null terminated, i.e., it must be at least one byte, but can be + * smaller if there's less data. + */ + char buf[SUNOEM_CLI_BUF_SIZE]; +}__attribute__((packed)) sunoem_cli_msg_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Command codes for the command_request field in each request. + */ +#define SUNOEM_CLI_CMD_OPEN 0 /* Open a new connection */ +#define SUNOEM_CLI_CMD_FORCE 1 /* Close any existing connection, then open */ +#define SUNOEM_CLI_CMD_CLOSE 2 /* Close the current connection */ +#define SUNOEM_CLI_CMD_POLL 3 /* Poll for new data to/from the server */ +#define SUNOEM_CLI_CMD_EOF 4 /* Poll, client is out of data */ + +#define SUNOEM_CLI_MAX_RETRY 3 /* Maximum number of retries */ + +#define SUNOEM_CLI_INVALID_VER_ERR "Invalid version" +#define SUNOEM_CLI_BUSY_ERR "Busy" + +typedef enum +{ + C_CTL_B = 0x02, /* same as left arrow */ + C_CTL_C = 0x03, + C_CTL_D = 0x04, + C_CTL_F = 0x06, /* same as right arrow */ + C_CTL_N = 0x0E, /* same as down arrow */ + C_CTL_P = 0x10, /* same as up arrow */ + C_DEL = 0x7f +} canon_char_t; + +static int +sunoem_cli_unbufmode_start(FILE *f, struct termios *orig_ts) +{ + struct termios ts; + int rc; + + if ((rc = tcgetattr(fileno(f), &ts))) { + return (rc); + } + *orig_ts = ts; + ts.c_lflag &= ~(ICANON | ECHO | ISIG); + ts.c_cc[VMIN] = 1; + if ((rc = tcsetattr(fileno(f), TCSAFLUSH, &ts))) { + return (rc); + } + + return (0); +} + +static int +sunoem_cli_unbufmode_stop(FILE *f, struct termios *ts) +{ + int rc; + + if ((rc = tcsetattr(fileno(f), TCSAFLUSH, ts))) { + return (rc); + } + + return (0); +} + +static int +ipmi_sunoem_cli(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_cli_msg_t cli_req; + sunoem_cli_msg_t *cli_rsp; + int arg_num = 0; + int arg_pos = 0; + time_t wait_time = 0; + int retries; + static uint8_t SunOemCliActingVersion = SUNOEM_CLI_VERSION; + + unsigned short first_char = 0; /*first char on the line*/ + struct termios orig_ts; + int error = 0; + + time_t now = 0; + int delay = 0; + + /* Prepare to open an SP shell session */ + memset(&cli_req, 0, sizeof(cli_req)); + cli_req.version = SunOemCliActingVersion; + cli_req.command_response = SUNOEM_CLI_CMD_OPEN; + if (argc > 0 && strcmp(argv[0], "force") == 0) { + cli_req.command_response = SUNOEM_CLI_CMD_FORCE; + argc--; + argv++; + } + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_CLI; + req.msg.data = (uint8_t *) &cli_req; + req.msg.data_len = SUNOEM_CLI_HEADER + 1; + retries = 0; + while (1) { + cli_req.version = SunOemCliActingVersion; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM cli command failed"); + return (-1); + } + cli_rsp = (sunoem_cli_msg_t *) rsp->data; + if ((cli_rsp->command_response != 0) || (rsp->ccode != 0)) { + if (strncmp(cli_rsp->buf, SUNOEM_CLI_INVALID_VER_ERR, + sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0 + || strncmp(&(cli_rsp->buf[1]), SUNOEM_CLI_INVALID_VER_ERR, + sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0) { + if (SunOemCliActingVersion == SUNOEM_CLI_VERSION) { + /* Server doesn't support version SUNOEM_CLI_VERSION + Fall back to legacy version, and try again*/ + SunOemCliActingVersion = SUNOEM_CLI_LEGACY_VERSION; + continue; + } + /* Server doesn't support legacy version either */ + lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); + return (-1); + } else if (strncmp(cli_rsp->buf, SUNOEM_CLI_BUSY_ERR, + sizeof(SUNOEM_CLI_BUSY_ERR) - 1) == 0) { + if (retries++ < SUNOEM_CLI_MAX_RETRY) { + lprintf(LOG_INFO, "Failed to connect: %s, retrying", + cli_rsp->buf); + sleep(2); + continue; + } + lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); + return (-1); + } else { + lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); + return (-1); + } + } + break; + } + if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) { + /* + * Bit 1 of seqnum is used as an alternating sequence number + * to allow a server that supports it to detect when a retry is being sent from the host IPMI driver. + * Typically when this occurs, the server's last response message would have been dropped. + * Once the server detects this condition, it will know that it should retry sending the response. + */ + cli_req.seqnum ^= 0x1; + } + printf("Connected. Use ^D to exit.\n"); + fflush(NULL); + + /* + * Remember the handle provided in the response, and issue a + * series of "poll" commands to send and get data + */ + memcpy(cli_req.handle, cli_rsp->handle, 4); + cli_req.command_response = SUNOEM_CLI_CMD_POLL; + /* + * If no arguments make input unbuffered and so interactive + */ + if (argc == 0) { + if (sunoem_cli_unbufmode_start(stdin, &orig_ts)) { + lprintf(LOG_ERR, "Failed to set interactive mode: %s", + strerror(errno)); + return (-1); + } + } + while (rsp->ccode == 0 && cli_rsp->command_response == 0) { + int rc = 0; + int count = 0; + cli_req.buf[0] = '\0'; + if (argc == 0) { + /* + * Accept input from stdin. Use select so we don't hang if + * there's no input to read. Select timeout is 500 msec. + */ + struct timeval tv = { 0, 500000 }; /* 500 msec */ + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(0, &rfds); + rc = select(1, &rfds, NULL, NULL, &tv); + if (rc < 0) { + /* Select returned an error so close and exit */ + printf("Broken pipe\n"); + cli_req.command_response = SUNOEM_CLI_CMD_CLOSE; + } else if (rc > 0) { + /* Read data from stdin */ + count = read(0, cli_req.buf, 1 /* sizeof (cli_req.buf) - 1 */); + /* + * If select said there was data but there was nothing to + * read. This implies user hit ^D. + * Also handle ^D input when pressed as first char at a new line. + */ + if (count <= 0 || (first_char && cli_req.buf[0] == C_CTL_D)) { + cli_req.command_response = SUNOEM_CLI_CMD_EOF; + count = 0; + } + first_char = cli_req.buf[0] == '\n' || cli_req.buf[0] == '\r'; + } + } else { + /* + * Get data from command line arguments + */ + now = time(NULL); + if (now < wait_time) { + /* Do nothing; we're waiting */ + } else if (arg_num >= argc) { + /* Last arg was sent. Set EOF */ + cli_req.command_response = SUNOEM_CLI_CMD_EOF; + } else if (strncmp(argv[arg_num], "@wait=", 6) == 0) { + /* This is a wait command */ + char *s = &argv[arg_num][6]; + delay = 0; + if (*s != '\0') { + if (str2int(s, &delay)) { + delay = 0; + } + if (delay < 0) { + delay = 0; + } + } + wait_time = now + delay; + arg_num++; + } else { + /* + * Take data from args. It may be that the argument is larger + * than the request buffer can hold. So pull off BUF_SIZE + * number of characters at a time. When we've consumed the + * entire arg, append a newline and advance to the next arg. + */ + int i; + char *s = argv[arg_num]; + for (i = arg_pos; + s[i] != '\0' && count < (SUNOEM_CLI_BUF_SIZE - 2); + i++, count++) { + cli_req.buf[count] = s[i]; + } + if (s[i] == '\0') { + /* Reached end of the arg string, so append a newline */ + cli_req.buf[count++] = '\n'; + /* Reset pos to 0 and advance to the next arg next time */ + arg_pos = 0; + arg_num++; + } else { + /* + * Otherwise, there's still more characters in the arg + * to send, so remember where we left off + */ + arg_pos = i; + } + } + } + /* + * Now send the clients's data (if any) and get data back from the + * server. Loop while the server is giving us data until we suck + * it dry. + */ + do { + cli_req.buf[count++] = '\0'; /* Terminate the string */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = 0x19; + req.msg.data = (uint8_t *) &cli_req; + req.msg.data_len = SUNOEM_CLI_HEADER + count; + for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) { + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Communication error."); + error = 1; + goto cleanup; + } + if (rsp->ccode == IPMI_CC_TIMEOUT) { /* Retry if timed out. */ + if (retries == SUNOEM_CLI_MAX_RETRY) { /* If it's the last retry. */ + lprintf(LOG_ERR, "Excessive timeout."); + error = 1; + goto cleanup; + } + continue; + } + break; + } /* for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) */ + + if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) { + cli_req.seqnum ^= 0x1; /* Toggle sequence number after request is sent */ + } + + cli_rsp = (sunoem_cli_msg_t *) rsp->data; + /* Make sure response string is null terminated */ + cli_rsp->buf[sizeof(cli_rsp->buf) - 1] = '\0'; + printf("%s", cli_rsp->buf); + fflush(NULL); /* Flush partial lines to stdout */ + count = 0; /* Don't re-send the client's data */ + if (cli_req.command_response == SUNOEM_CLI_CMD_EOF + && cli_rsp->command_response != 0 && rsp->ccode == 0) { + cli_rsp->command_response = 1; + } + } while (cli_rsp->command_response == 0 && cli_rsp->buf[0] != '\0'); + } + +cleanup: + /* Restore original input mode if cli was running interactively */ + if (argc == 0) { + if (sunoem_cli_unbufmode_stop(stdin, &orig_ts)) { + lprintf(LOG_ERR, "Failed to restore interactive mode: %s", + strerror(errno)); + return (-1); + } + } + + return ((error == 0 && cli_rsp->command_response == SUNOEM_SUCCESS) ? 0 : -1); +} +#define ECHO_DATA_SIZE 64 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + uint16_t seq_num; + unsigned char data[ECHO_DATA_SIZE]; +}__attribute__((packed)) sunoem_echo_msg_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Send and receive X packets to the BMC. Each packet has a + * payload size of (sunoem_echo_msg_t) bytes. Each packet is tagged with a + * sequence number + */ +static int +ipmi_sunoem_echo(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_echo_msg_t echo_req; + sunoem_echo_msg_t *echo_rsp; + struct timeval start_time; + struct timeval end_time; + + int rc = 0; + int received = 0; + int transmitted = 0; + int quiet_mode = 0; + + uint16_t num, i, j; + uint32_t total_time, resp_time, min_time, max_time; + + if (argc < 1) { + return (1); + } + + if (argc == 2) { + if (*(argv[1]) == 'q') { + quiet_mode = 1; + } else { + lprintf(LOG_ERR, "Unknown option '%s' given.", argv[1]); + return (-1); + } + } else if (argc > 2) { + lprintf(LOG_ERR, + "Too many parameters given. See help for more information."); + return (-1); + } + /* The number of packets to send/receive */ + if (str2ushort(argv[0], &num) != 0) { + lprintf(LOG_ERR, + "Given number of packets is either invalid or out of range."); + return (-1); + } + + /* Fill in data packet */ + for (i = 0; i < ECHO_DATA_SIZE; i++) { + if (i > UINT8_MAX) + break; + + echo_req.data[i] = (uint8_t) i; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_ECHO; + req.msg.data = (uint8_t *) &echo_req; + req.msg.data_len = sizeof(sunoem_echo_msg_t); + echo_req.seq_num = i; + min_time = INT_MAX; + max_time = 0; + total_time = 0; + for (i = 0; i < num; i++) { + echo_req.seq_num = i; + transmitted++; + gettimeofday(&start_time, NULL); + rsp = intf->sendrecv(intf, &req); + gettimeofday(&end_time, NULL); + resp_time = ((end_time.tv_sec - start_time.tv_sec) * 1000) + + ((end_time.tv_usec - start_time.tv_usec) / 1000); + if ((rsp == NULL) || (rsp->ccode != 0)) { + lprintf(LOG_ERR, "Sun OEM echo command failed. Seq # %d", + echo_req.seq_num); + rc = (-2); + break; + } + echo_rsp = (sunoem_echo_msg_t *) rsp->data; + + /* Test if sequence # is valid */ + if (echo_rsp->seq_num != echo_req.seq_num) { + printf("Invalid Seq # Expecting %d Received %d\n", echo_req.seq_num, + echo_rsp->seq_num); + rc = (-2); + break; + } + + /* Test if response length is valid */ + if (rsp->session.msglen == req.msg.data_len) { + printf("Invalid payload size for seq # %d. " + "Expecting %d Received %d\n", echo_rsp->seq_num, + req.msg.data_len, rsp->session.msglen); + rc = (-2); + break; + } + + /* Test if the data is valid */ + for (j = 0; j < ECHO_DATA_SIZE; j++) { + if (echo_rsp->data[j] != j) { + printf("Corrupt data packet. Seq # %d Offset %d\n", + echo_rsp->seq_num, j); + break; + } + } /* for (j = 0; j < ECHO_DATA_SIZE; j++) */ + + /* If the for loop terminated early - data is corrupt */ + if (j != ECHO_DATA_SIZE) { + rc = (-2); + break; + } + + /* cumalative time */ + total_time += resp_time; + + /* min time */ + if (resp_time < min_time) { + min_time = resp_time; + } + + /* max time */ + if (resp_time > max_time) { + max_time = resp_time; + } + + received++; + if (!quiet_mode) { + printf("Receive %u Bytes - Seq. # %d time=%d ms\n", + sizeof(sunoem_echo_msg_t), echo_rsp->seq_num, resp_time); + } + } /* for (i = 0; i < num; i++) */ + printf("%d packets transmitted, %d packets received\n", transmitted, + received); + if (received) { + printf("round-trip min/avg/max = %d/%d/%d ms\n", min_time, + total_time / received, max_time); + } + + return (rc); +} /* ipmi_sunoem_echo(...) */ + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char oem_record_ver_num; + unsigned char major; + unsigned char minor; + unsigned char update; + unsigned char micro; + char nano[10]; + char revision[10]; + char version[40]; + /* + * When adding new fields (using the spare bytes), + * add it immediately after the spare field to + * ensure backward compatability. + * + * e.g. char version[40]; + * unsigned char spare[11]; + * int new_item; + * } sunoem_version_response_t; + */ + unsigned char spare[15]; +}__attribute__((packed)) sunoem_version_response_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +typedef struct +{ + unsigned char major; + unsigned char minor; + unsigned char update; + unsigned char micro; +} supported_version_t; + +static int +ipmi_sunoem_getversion(struct ipmi_intf * intf, + sunoem_version_response_t **version_rsp) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_VERSION; + req.msg.data = NULL; + req.msg.data_len = 0; + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM Get SP Version Failed."); + return (-1); + } + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM Get SP Version Failed: %d", rsp->ccode); + return (-1); + } + + *version_rsp = (sunoem_version_response_t *) rsp->data; + + return (0); +} + +static void +ipmi_sunoem_print_required_version(const supported_version_t* supp_ver) +{ + lprintf(LOG_ERR, "Command is not supported by this version of ILOM," + " required at least: %d.%d.%d.%d", supp_ver->major, supp_ver->minor, + supp_ver->update, supp_ver->micro); +} + +/* + * Function checks current version result against required version. + * Returns: + * - negative value if current ILOM version is smaller than required or + * in case of error + * - positive value if current ILOM version is greater than required + * - 0 if there is an exact ILOM version match + */ +static int +ipmi_sunoem_checkversion(struct ipmi_intf * intf, supported_version_t* supp_ver) +{ + sunoem_version_response_t *version_rsp; + int i = 1; + + if (ipmi_sunoem_getversion(intf, &version_rsp)) { + lprintf(LOG_ERR, "Unable to get ILOM version"); + return (-1); + } + + if (version_rsp->major < supp_ver->major) return (-i); + if (version_rsp->major > supp_ver->major) return (i); + /*version_rsp->major == supp_ver->major*/ + ++i; + + if (version_rsp->minor < supp_ver->minor) return (-i); + if (version_rsp->minor > supp_ver->minor) return (i); + /*version_rsp->minor == supp_ver->minor*/ + ++i; + + if (version_rsp->update < supp_ver->update) return (-i); + if (version_rsp->update > supp_ver->update) return (i); + /*version_rsp->update == supp_ver->update*/ + ++i; + + if (version_rsp->micro < supp_ver->micro) return (-i); + if (version_rsp->micro > supp_ver->micro) return (i); + /*version_rsp->micro == supp_ver->micro*/ + + return (0); +} + +/* + * Extract the SP version data including + * - major # + * - minor # + * - update # + * - micro # + * - nano # + * - Revision/Build # + */ +static int +ipmi_sunoem_version(struct ipmi_intf * intf) +{ + sunoem_version_response_t *version_rsp; + int rc = ipmi_sunoem_getversion(intf, &version_rsp); + + if (!rc) { + printf("Version: %s\n", version_rsp->version); + } + + return (rc); +} + +/* + * IPMI Max string length is 16 bytes + * define in usr/src/common/include/ami/IPMI_SDRRecord.h + */ +#define MAX_ID_STR_LEN 16 +#define MAX_SUNOEM_NAC_SIZE 64 +#define LUAPI_MAX_OBJ_PATH_LEN 256 +#define LUAPI_MAX_OBJ_VAL_LEN 1024 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char seq_num; + char nac_name[MAX_SUNOEM_NAC_SIZE]; +}__attribute__((packed)) sunoem_nacname_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Retrieve the full NAC name of the IPMI target. + * + * The returned nac name may be larger than the payload size. + * In which case, it make take several request/payload to retrieve + * the entire full path name + * + * The initial seq_num is set to 0. If the return seq_num is incremented, + * only the 1st 72 bytes of the nac name is returned and the caller + * needs to get the next set of string data. + * If the returned seq_num is identical to the input seq_num, all data + * has been returned. + */ +static int +ipmi_sunoem_nacname(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_nacname_t nacname_req; + sunoem_nacname_t *nacname_rsp; + char full_nac_name[LUAPI_MAX_OBJ_PATH_LEN]; + + if (argc < 1) { + return (1); + } + + if (strlen(argv[0]) > MAX_ID_STR_LEN) { + lprintf(LOG_ERR, + "Sun OEM nacname command failed: Max size on IPMI name"); + return (-1); + } + + nacname_req.seq_num = 0; + strcpy(nacname_req.nac_name, argv[0]); + + full_nac_name[0] = '\0'; + while (1) { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_NACNAME; + req.msg.data = (uint8_t *) &nacname_req; + req.msg.data_len = sizeof(sunoem_nacname_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM nacname command failed."); + return (-1); + } + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM nacname command failed: %d", rsp->ccode); + return (-1); + } + + nacname_rsp = (sunoem_nacname_t *) rsp->data; + strncat(full_nac_name, nacname_rsp->nac_name, MAX_SUNOEM_NAC_SIZE); + + /* + * break out of the loop if there is no more data + * In most cases, if not all, the NAC name fits into a + * single payload + */ + if (nacname_req.seq_num == nacname_rsp->seq_num) { + break; + } + + /* Get the next seq of string bytes */ + nacname_req.seq_num = nacname_rsp->seq_num; + + /* Check if we exceeded the size of the full nac name */ + if ((nacname_req.seq_num * MAX_SUNOEM_NAC_SIZE) > LUAPI_MAX_OBJ_PATH_LEN) { + lprintf(LOG_ERR, + "Sun OEM nacname command failed: invalid path length"); + return (-1); + } + } + + printf("NAC Name: %s\n", full_nac_name); + return (0); +} + +/* Constants used by ipmi_sunoem_getval */ +#define MAX_SUNOEM_VAL_PAYLOAD 79 +#define MAX_SUNOEM_VAL_COMPACT_PAYLOAD 56 + +/* + * SUNOEM GET/SET LUAPI Commands + * + * SUNOEM_REQ_VAL - Request LUAPI Property Value + * SUNOEM_GET_VAL - Return the value from SUNOEM_REQ_VAL + * SUNOEM_SET_VAL - Set the LUAPI Property value + * SUNOEM_GET_STATUS - Return the Status from SUNOEM_SET_VAL + */ +#define SUNOEM_REQ_VAL 1 +#define SUNOEM_GET_VAL 2 +#define SUNOEM_SET_VAL 3 +#define SUNOEM_GET_STATUS 4 + +/* Status Code */ +#define SUNOEM_REQ_RECV 1 +#define SUNOEM_REQ_FAILED 2 +#define SUNOEM_DATA_READY 3 +#define SUNOEM_DATA_NOT_READY 4 +#define SUNOEM_DATA_NOT_FOUND 5 +#define GETVAL_MAX_RETRIES 5 + +/* Parameter type Codes */ +#define SUNOEM_LUAPI_TARGET 0 +#define SUNOEM_LUAPI_VALUE 1 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char cmd_code; + unsigned char luapi_value[MAX_SUNOEM_VAL_PAYLOAD]; +}__attribute__((packed)) sunoem_getval_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * REQUEST PAYLOAD + * + * cmd_code - SUNOEM GET/SET LUAPI Cmds - see above + * param_type: 0: luapi_data contains the luapi property name + * 1: luapi_data contains the luapi value + * luapi_data: Either luapi property name or value + * tid: Transaction ID. If 0. This is the initial request for the + * param_type. If tid > 0, this luapi_data string is a concatenation + * of the previous request. Handle cases where the LUAPI target name + * or value is > MAX_SUNOEM_VAL_COMPACT_PAYLOAD + * eof: If non zero, this is the last payload for the request + */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char cmd_code; + unsigned char param_type; + unsigned char tid; + unsigned char eof; + char luapi_data[MAX_SUNOEM_VAL_COMPACT_PAYLOAD]; +}__attribute__((packed)) sunoem_setval_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * RESPONSE PAYLOAD + * + * status_code - see above for code definitions + * tid - transaction ID - assigned ny the ILOM stack + */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char status_code; + unsigned char tid; +}__attribute__((packed)) sunoem_setval_resp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Return the ILOM target property value + */ +static int +ipmi_sunoem_getval(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_getval_t getval_req; + sunoem_getval_t *getval_rsp; + int i; + + const char* sp_path = "/SP"; + supported_version_t supp_ver = { 3, 2, 0, 0 }; + + if (argc < 1) { + return (1); + } + + if (strlen(argv[0]) > MAX_SUNOEM_VAL_PAYLOAD) { + lprintf(LOG_ERR, + "Sun OEM get value command failed: Max size on IPMI name"); + return (-1); + } + + if ((ipmi_sunoem_checkversion(intf, &supp_ver) < 0) + && (!strncmp(argv[0], sp_path, strlen(sp_path)))) { + argv[0][1] = 'X'; /*replace SP by X to gain access to hidden properties*/ + memmove(&argv[0][2], &argv[0][3], strlen(argv[0]) - 2); + } + + /* + * Setup the initial request to fetch the data. + * Upon function return, the next cmd (SUNOEM_GET_VAL) + * can be requested. + */ + memset(&getval_req, 0, sizeof(getval_req)); + strncpy((char*) getval_req.luapi_value, argv[0], MAX_SUNOEM_VAL_PAYLOAD); + getval_req.cmd_code = SUNOEM_REQ_VAL; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_GETVAL; + req.msg.data = (uint8_t *) &getval_req; + req.msg.data_len = sizeof(sunoem_getval_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM getval1 command failed."); + return (-1); + } + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM getval1 command failed: %d", rsp->ccode); + return (-1); + } + + /* + * Fetch the data value - if it is not ready, + * retry the request up to GETVAL_MAX_RETRIES + */ + for (i = 0; i < GETVAL_MAX_RETRIES; i++) { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_GETVAL; + getval_req.cmd_code = SUNOEM_GET_VAL; + req.msg.data = (uint8_t *) &getval_req; + req.msg.data_len = sizeof(sunoem_getval_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM getval2 command failed."); + return (-1); + } + + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM getval2 command failed: %d", rsp->ccode); + return (-1); + } + + getval_rsp = (sunoem_getval_t *) rsp->data; + + if (getval_rsp->cmd_code == SUNOEM_DATA_READY) { + printf("Target Value: %s\n", getval_rsp->luapi_value); + return (0); + } else if (getval_rsp->cmd_code == SUNOEM_DATA_NOT_FOUND) { + lprintf(LOG_ERR, "Target: %s not found", getval_req.luapi_value); + return (-1); + } + + sleep(1); + } + + lprintf(LOG_ERR, "Unable to retrieve target value."); + return (-1); +} + +static int +send_luapi_prop_name(struct ipmi_intf * intf, int len, char *prop_name, + unsigned char *tid_num) +{ + int i = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_setval_t setval_req; + sunoem_setval_resp_t *setval_rsp; + + *tid_num = 0; + while (i < len) { + /* + * Setup the request, + * Upon function return, the next cmd (SUNOEM_SET_VAL) + * can be requested. + */ + memset(&req, 0, sizeof(req)); + memset(&setval_req, 0, sizeof(sunoem_setval_t)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_SETVAL; + setval_req.cmd_code = SUNOEM_SET_VAL; + setval_req.param_type = SUNOEM_LUAPI_TARGET; + setval_req.tid = *tid_num; + setval_req.eof = 0; + /* + * If the property name is > payload, only copy + * the payload size and increment the string offset (i) + * for the next payload + */ + if (strlen(&(prop_name[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) { + strncpy(setval_req.luapi_data, &(prop_name[i]), + MAX_SUNOEM_VAL_COMPACT_PAYLOAD); + } else { + strncpy(setval_req.luapi_data, &(prop_name[i]), + strlen(&(prop_name[i]))); + } + req.msg.data = (uint8_t *) &setval_req; + req.msg.data_len = sizeof(sunoem_setval_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM setval prop name: response is NULL"); + return (-1); + } + + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM setval prop name: request failed: %d", + rsp->ccode); + return (-1); + } + + setval_rsp = (sunoem_setval_resp_t *) rsp->data; + + /* + * If the return code is other than data received, the + * request failed + */ + if (setval_rsp->status_code != SUNOEM_REQ_RECV) { + lprintf(LOG_ERR, + "Sun OEM setval prop name: invalid status code: %d", + setval_rsp->status_code); + return (-1); + } + /* Use the tid returned by ILOM */ + *tid_num = setval_rsp->tid; + /* Increment the string offset */ + i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD; + } + + return (0); +} + +static int +send_luapi_prop_value(struct ipmi_intf * intf, int len, char *prop_value, + unsigned char tid_num) +{ + int i = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_setval_t setval_req; + sunoem_setval_resp_t *setval_rsp; + + while (i < len) { + /* + * Setup the request, + * Upon function return, the next cmd (SUNOEM_GET_VAL) + * can be requested. + */ + memset(&req, 0, sizeof(req)); + memset(&setval_req, 0, sizeof(sunoem_setval_t)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_SETVAL; + setval_req.cmd_code = SUNOEM_SET_VAL; + setval_req.param_type = SUNOEM_LUAPI_VALUE; + setval_req.tid = tid_num; + /* + * If the property name is > payload, only copy the + * the payload size and increment the string offset + * for the next payload + */ + if (strlen(&(prop_value[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) { + strncpy(setval_req.luapi_data, &(prop_value[i]), + MAX_SUNOEM_VAL_COMPACT_PAYLOAD); + } else { + /* Captured the entire string, mark this as the last payload */ + strncpy(setval_req.luapi_data, &(prop_value[i]), + strlen(&(prop_value[i]))); + setval_req.eof = 1; + } + req.msg.data = (uint8_t *) &setval_req; + req.msg.data_len = sizeof(sunoem_setval_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM setval prop value: response is NULL"); + return (-1); + } + + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM setval prop value: request failed: %d", + rsp->ccode); + return (-1); + } + + setval_rsp = (sunoem_setval_resp_t *) rsp->data; + + /* + * If the return code is other than data received, the + * request failed + */ + if (setval_rsp->status_code != SUNOEM_REQ_RECV) { + lprintf(LOG_ERR, + "Sun OEM setval prop value: invalid status code: %d", + setval_rsp->status_code); + return (-1); + } + + /* Increment the string offset */ + i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD; + } + return (0); +} + +static int +ipmi_sunoem_setval(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + sunoem_setval_t setval_req; + sunoem_setval_resp_t *setval_rsp; + int prop_len; + int value_len; + int i; + unsigned char tid_num; + int retries; + + prop_len = strlen(argv[0]); + value_len = strlen(argv[1]); + if (prop_len > LUAPI_MAX_OBJ_PATH_LEN) { + lprintf(LOG_ERR, + "Sun OEM set value command failed: Max size on property name"); + return (-1); + } + if (value_len > LUAPI_MAX_OBJ_VAL_LEN) { + lprintf(LOG_ERR, + "Sun OEM set value command failed: Max size on property value"); + return (-1); + } + + /* Test if there is a timeout specified */ + if (argc == 3) { + if ((str2int(argv[2], &retries) != 0) || retries < 0) { + lprintf(LOG_ERR, + "Invalid input given or out of range for time-out parameter."); + return (-1); + } + } else { + retries = GETVAL_MAX_RETRIES; + } + + /* Send the property name 1st */ + if (send_luapi_prop_name(intf, prop_len, argv[0], &tid_num) != 0) { + /* return if there is an error */ + return (-1); + } + + if (send_luapi_prop_value(intf, value_len, argv[1], tid_num) != 0) { + /* return if there is an error */ + return (-1); + } + + /* + * Get The status of the command. + * if it is not ready, retry the request up to + * GETVAL_MAX_RETRIES + */ + for (i = 0; i < retries; i++) { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_SETVAL; + setval_req.cmd_code = SUNOEM_GET_STATUS; + setval_req.tid = tid_num; + req.msg.data = (uint8_t *) &setval_req; + req.msg.data_len = sizeof(sunoem_setval_t); + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM setval command failed."); + return (-1); + } + + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM setval command failed: %d", rsp->ccode); + return (-1); + } + + setval_rsp = (sunoem_setval_resp_t *) rsp->data; + + if (setval_rsp->status_code == SUNOEM_DATA_READY) { + printf("Sun OEM setval command successful.\n"); + return (0); + } else if (setval_rsp->status_code != SUNOEM_DATA_NOT_READY) { + lprintf(LOG_ERR, "Sun OEM setval command failed."); + return (-1); + } + + sleep(1); + } + /* If we reached here, retries exceeded */ + lprintf(LOG_ERR, "Sun OEM setval command failed: Command Timed Out"); + + return (-1); +} + +#define MAX_FILE_DATA_SIZE 1024 +#define MAX_FILEID_LEN 16 +#define CORE_TUNNEL_SUBCMD_GET_FILE 11 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char cmd_code; + unsigned char file_id[MAX_FILEID_LEN]; + unsigned int block_num; +}__attribute__((packed)) getfile_req_t; + +typedef struct +{ + unsigned int block_num; + unsigned int data_size; + unsigned char eof; + unsigned char data[MAX_FILE_DATA_SIZE]; +}__attribute__((packed)) getfile_rsp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +static int +ipmi_sunoem_getfile(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + getfile_req_t getfile_req; + getfile_rsp_t *getfile_rsp; + int block_num = 0; + int nbo_blk_num; /* Network Byte Order Block Num */ + FILE *fp; + unsigned data_size; + supported_version_t supp_ver = IPMI_SUNOEM_GETFILE_VERSION; + + if (argc < 1) { + return (-1); + } + + /*check if command is supported by this version of ilom*/ + if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) { + ipmi_sunoem_print_required_version(&supp_ver); + return (-1); + } + + /* + * File ID is < MAX_FILEID_LEN + * Save 1 byte for null Terminated string + */ + if (strlen(argv[0]) >= MAX_FILE_DATA_SIZE) { + lprintf(LOG_ERR, "File ID >= %d characters", MAX_FILEID_LEN); + return (-1); + } + + memset(&getfile_req, 0, sizeof(getfile_req)); + strncpy((char*) getfile_req.file_id, argv[0], MAX_FILEID_LEN - 1); + + /* Create the destination file */ + fp = ipmi_open_file_write(argv[1]); + if (fp == NULL) { + lprintf(LOG_ERR, "Unable to open file: %s", argv[1]); + return (-1); + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL; + req.msg.data = (uint8_t *) &getfile_req; + req.msg.data_len = sizeof(getfile_req_t); + getfile_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_FILE; + + do { + + nbo_blk_num = htonl(block_num); + /* Block Num must be in network byte order */ + memcpy(&(getfile_req.block_num), &nbo_blk_num, + sizeof(getfile_req.block_num)); + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM getfile command failed."); + fclose(fp); + return (-1); + } + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM getfile command failed: %d", rsp->ccode); + fclose(fp); + return (-1); + } + + getfile_rsp = (getfile_rsp_t *) rsp->data; + + memcpy(&data_size, &(getfile_rsp->data_size), + sizeof(getfile_rsp->data_size)); + data_size = ntohl(data_size); + + if (data_size > MAX_FILE_DATA_SIZE) { + lprintf(LOG_ERR, "Sun OEM getfile invalid data size: %d", + data_size); + fclose(fp); + return (-1); + } + + /* Check if Block Num matches */ + if (memcmp(&(getfile_req.block_num), &(getfile_rsp->block_num), + sizeof(getfile_req.block_num)) != 0) { + lprintf(LOG_ERR, "Sun OEM getfile Incorrect Block Num Returned"); + lprintf(LOG_ERR, "Expecting: %x Received: %x", + getfile_req.block_num, getfile_rsp->block_num); + fclose(fp); + return (-1); + } + + if (fwrite(getfile_rsp->data, 1, data_size, fp) != data_size) { + lprintf(LOG_ERR, "Sun OEM getfile write failed: %d", rsp->ccode); + fclose(fp); + return (-1); + } + + block_num++; + } while (getfile_rsp->eof == 0); + + fclose(fp); + + return (0); +} + +/* + * Query BMC for capability/behavior. + */ + +#define CORE_TUNNEL_SUBCMD_GET_BEHAVIOR 15 +#define SUNOEM_BEHAVIORID_SIZE 32 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ + unsigned char cmd_code; + unsigned char behavior_id[SUNOEM_BEHAVIORID_SIZE]; +}__attribute__((packed)) getbehavior_req_t; + +typedef struct +{ + unsigned char enabled; +}__attribute__((packed)) getbehavior_rsp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +static int +ipmi_sunoem_getbehavior(struct ipmi_intf * intf, int argc, char *argv[]) +{ + struct ipmi_rq req; + struct ipmi_rs *rsp; + getbehavior_req_t getbehavior_req; + getbehavior_rsp_t *getbehavior_rsp; + supported_version_t supp_ver = IPMI_SUNOEM_GETBEHAVIOR_VERSION; + + if (argc < 1) { + return (-1); + } + + /*check if command is supported by this version of ilom*/ + if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) { + ipmi_sunoem_print_required_version(&supp_ver); + return (-1); + } + + /* + * Behavior ID is < SUNOEM_BEHAVIORID_SIZE. + * Save 1 byte for null terminated string + */ + if (strlen(argv[0]) >= SUNOEM_BEHAVIORID_SIZE) { + lprintf(LOG_ERR, "Behavior ID >= %d characters", + SUNOEM_BEHAVIORID_SIZE); + return (-1); + } + + memset(&getbehavior_req, 0, sizeof(getbehavior_req)); + strncpy(getbehavior_req.behavior_id, argv[0], SUNOEM_BEHAVIORID_SIZE - 1); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SUNOEM; + req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL; + req.msg.data = (uint8_t *) &getbehavior_req; + req.msg.data_len = sizeof(getbehavior_req_t); + getbehavior_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_BEHAVIOR; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Sun OEM getbehavior command failed."); + return (-1); + } + + if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Sun OEM getbehavior command failed: %d", rsp->ccode); + return (-1); + } + + getbehavior_rsp = (getbehavior_rsp_t *) rsp->data; + printf("ILOM behavior %s %s enabled\n", getbehavior_req.behavior_id, + getbehavior_rsp->enabled ? "is" : "is not"); + + return (0); +} + +int +ipmi_sunoem_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc == 0 || strcmp(argv[0], "help") == 0) { + ipmi_sunoem_usage(); + return (0); + } /* if (argc == 0 || strcmp(argv[0], "help") == 0) */ + + if (strcmp(argv[0], "cli") == 0) { + rc = ipmi_sunoem_cli(intf, argc - 1, &argv[1]); + } else if ((strcmp(argv[0], "led") == 0) || (strcmp(argv[0], "sbled") == 0)) { + if (argc < 2) { + ipmi_sunoem_usage(); + return (-1); + } + + if (strcmp(argv[1], "get") == 0) { + if (argc < 3) { + char * arg[] = { "all" }; + rc = ipmi_sunoem_led_get(intf, 1, arg); + } else { + rc = ipmi_sunoem_led_get(intf, argc - 2, &(argv[2])); + } + } else if (strcmp(argv[1], "set") == 0) { + if (argc < 4) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_led_set(intf, argc - 2, &(argv[2])); + } else { + ipmi_sunoem_usage(); + return (-1); + } + } else if (strcmp(argv[0], "sshkey") == 0) { + uint8_t uid = 0; + if (argc < 3) { + ipmi_sunoem_usage(); + return (-1); + } + rc = str2uchar(argv[2], &uid); + if (rc == 0) { + /* conversion should be OK. */ + } else if (rc == 2) { + lprintf(LOG_NOTICE, "Invalid interval given."); + return (-1); + } else { + /* defaults to rc = 3 */ + lprintf(LOG_NOTICE, "Given interval is too big."); + return (-1); + } + + if (strcmp(argv[1], "del") == 0) { + /* number of arguments, three, is already checked at this point */ + rc = ipmi_sunoem_sshkey_del(intf, uid); + } else if (strcmp(argv[1], "set") == 0) { + if (argc < 4) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_sshkey_set(intf, uid, argv[3]); + } else { + ipmi_sunoem_usage(); + return (-1); + } + } else if (strcmp(argv[0], "ping") == 0) { + if (argc < 2) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_echo(intf, argc - 1, &(argv[1])); + } else if (strcmp(argv[0], "version") == 0) { + rc = ipmi_sunoem_version(intf); + } else if (strcmp(argv[0], "nacname") == 0) { + if (argc < 2) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_nacname(intf, argc - 1, &(argv[1])); + } else if (strcmp(argv[0], "getval") == 0) { + if (argc < 2) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_getval(intf, argc - 1, &(argv[1])); + } else if (strcmp(argv[0], "setval") == 0) { + if (argc < 3) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_setval(intf, argc - 1, &(argv[1])); + } else if (strcmp(argv[0], "getfile") == 0) { + if (argc < 3) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_getfile(intf, argc - 1, &(argv[1])); + } else if (strcmp(argv[0], "getbehavior") == 0) { + if (argc < 2) { + ipmi_sunoem_usage(); + return (-1); + } + rc = ipmi_sunoem_getbehavior(intf, argc - 1, &(argv[1])); + } else { + lprintf(LOG_ERR, "Invalid sunoem command: %s", argv[0]); + return (-1); + } /* if (strcmp(argv[0], "cli") == 0) */ + + return (rc); +} diff --git a/lib/ipmi_tsol.c b/lib/ipmi_tsol.c new file mode 100644 index 0000000..94ea284 --- /dev/null +++ b/lib/ipmi_tsol.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2005 Tyan Computer Corp. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <signal.h> + +#include <sys/select.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined(HAVE_TERMIOS_H) +# include <termios.h> +#elif defined (HAVE_SYS_TERMIOS_H) +# include <sys/termios.h> +#endif + +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_tsol.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + +static struct timeval _start_keepalive; +static struct termios _saved_tio; +static struct winsize _saved_winsize; +static int _in_raw_mode = 0; +static int _altterm = 0; + +extern int verbose; + +static int +ipmi_tsol_command(struct ipmi_intf * intf, char *recvip, int port, unsigned char cmd) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + unsigned char data[6]; + unsigned ip1, ip2, ip3, ip4; + + if (sscanf(recvip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) { + lprintf(LOG_ERR, "Invalid IP address: %s", recvip); + return -1; + } + + memset(&req, 0, sizeof(struct ipmi_rq)); + req.msg.netfn = IPMI_NETFN_TSOL; + req.msg.cmd = cmd; + req.msg.data_len = 6; + req.msg.data = data; + + memset(data, 0, sizeof(data)); + data[0] = ip1; + data[1] = ip2; + data[2] = ip3; + data[3] = ip4; + data[4] = (port & 0xff00) >> 8; + data[5] = (port & 0xff); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to perform TSOL command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to perform TSOL command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static int +ipmi_tsol_start(struct ipmi_intf * intf, char *recvip, int port) +{ + return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_START); +} + +static int +ipmi_tsol_stop(struct ipmi_intf * intf, char *recvip, int port) +{ + return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_STOP); +} + +static int +ipmi_tsol_send_keystroke(struct ipmi_intf * intf, char *buff, int length) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[16]; + static unsigned char keyseq = 0; + + memset(&req, 0, sizeof(struct ipmi_rq)); + req.msg.netfn = IPMI_NETFN_TSOL; + req.msg.cmd = IPMI_TSOL_CMD_SENDKEY; + req.msg.data_len = length + 2; + req.msg.data = data; + + memset(data, 0, sizeof(data)); + data[0] = length + 1; + memcpy(data + 1, buff, length); + data[length + 1] = keyseq++; + + rsp = intf->sendrecv(intf, &req); + if (verbose) { + if (rsp == NULL) { + lprintf(LOG_ERR, "Unable to send keystroke"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Unable to send keystroke: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } + + return length; +} + +static int +tsol_keepalive(struct ipmi_intf * intf) +{ + struct timeval end; + + gettimeofday(&end, 0); + + if (end.tv_sec - _start_keepalive.tv_sec <= 30) + return 0; + + intf->keepalive(intf); + + gettimeofday(&_start_keepalive, 0); + + return 0; +} + +static void +print_escape_seq(struct ipmi_intf *intf) +{ + lprintf(LOG_NOTICE, + " %c. - terminate connection\n" + " %c^Z - suspend ipmitool\n" + " %c^X - suspend ipmitool, but don't restore tty on restart\n" + " %c? - this message\n" + " %c%c - send the escape character by typing it twice\n" + " (Note that escapes are only recognized immediately after newline.)", + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char, + intf->session->sol_escape_char); +} + +static int +leave_raw_mode(void) +{ + if (!_in_raw_mode) + return -1; + else if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) + lperror(LOG_ERR, "tcsetattr(stdin)"); + else if (tcsetattr(fileno(stdout), TCSADRAIN, &_saved_tio) == -1) + lperror(LOG_ERR, "tcsetattr(stdout)"); + else + _in_raw_mode = 0; + + return 0; +} + +static int +enter_raw_mode(void) +{ + struct termios tio; + + if (tcgetattr(fileno(stdout), &_saved_tio) < 0) { + lperror(LOG_ERR, "tcgetattr failed"); + return -1; + } + + tio = _saved_tio; + + if (_altterm) { + tio.c_iflag &= (ISTRIP | IGNBRK ); + tio.c_cflag &= ~(CSIZE | PARENB | IXON | IXOFF | IXANY); + tio.c_cflag |= (CS8 |CREAD) | (IXON|IXOFF|IXANY); + tio.c_lflag &= 0; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + } else { + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN); + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + } + + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) + lperror(LOG_ERR, "tcsetattr(stdin)"); + else if (tcsetattr(fileno(stdout), TCSADRAIN, &tio) < 0) + lperror(LOG_ERR, "tcsetattr(stdout)"); + else + _in_raw_mode = 1; + + return 0; +} + +static void +suspend_self(int restore_tty) +{ + leave_raw_mode(); + + kill(getpid(), SIGTSTP); + + if (restore_tty) + enter_raw_mode(); +} + +static int +do_inbuf_actions(struct ipmi_intf *intf, char *in_buff, int len) +{ + static int in_esc = 0; + static int last_was_cr = 1; + int i; + + for(i = 0; i < len ;) { + if (!in_esc) { + if (last_was_cr && + (in_buff[i] == intf->session->sol_escape_char)) { + in_esc = 1; + memmove(in_buff, in_buff + 1, len - i - 1); + len--; + continue; + } + } + if (in_esc) { + if (in_buff[i] == intf->session->sol_escape_char) { + in_esc = 0; + i++; + continue; + } + + switch (in_buff[i]) { + case '.': + printf("%c. [terminated ipmitool]\n", + intf->session->sol_escape_char); + return -1; + + case 'Z' - 64: + printf("%c^Z [suspend ipmitool]\n", + intf->session->sol_escape_char); + suspend_self(1); /* Restore tty back to raw */ + break; + + case 'X' - 64: + printf("%c^X [suspend ipmitool]\n", + intf->session->sol_escape_char); + suspend_self(0); /* Don't restore to raw mode */ + break; + + case '?': + printf("%c? [ipmitool help]\n", + intf->session->sol_escape_char); + print_escape_seq(intf); + break; + } + + memmove(in_buff, in_buff + 1, len - i - 1); + len--; + in_esc = 0; + + continue; + } + + last_was_cr = (in_buff[i] == '\r' || in_buff[i] == '\n'); + + i++; + } + + return len; +} + + +static void +do_terminal_cleanup(void) +{ + if (_saved_winsize.ws_row > 0 && _saved_winsize.ws_col > 0) + ioctl(fileno(stdout), TIOCSWINSZ, &_saved_winsize); + + leave_raw_mode(); + + if (errno) + lprintf(LOG_ERR, "Exiting due to error %d -> %s", + errno, strerror(errno)); +} + +static void +set_terminal_size(int rows, int cols) +{ + struct winsize winsize; + + if (rows <= 0 || cols <= 0) + return; + + /* save initial winsize */ + ioctl(fileno(stdout), TIOCGWINSZ, &_saved_winsize); + + /* set new winsize */ + winsize.ws_row = rows; + winsize.ws_col = cols; + ioctl(fileno(stdout), TIOCSWINSZ, &winsize); +} + +static void +print_tsol_usage(void) +{ + struct winsize winsize; + + lprintf(LOG_NOTICE, "Usage: tsol [recvip] [port=NUM] [ro|rw] [rows=NUM] [cols=NUM] [altterm]"); + lprintf(LOG_NOTICE, " recvip Receiver IP Address [default=local]"); + lprintf(LOG_NOTICE, " port=NUM Receiver UDP Port [default=%d]", + IPMI_TSOL_DEF_PORT); + lprintf(LOG_NOTICE, " ro|rw Set Read-Only or Read-Write [default=rw]"); + + ioctl(fileno(stdout), TIOCGWINSZ, &winsize); + lprintf(LOG_NOTICE, " rows=NUM Set terminal rows [default=%d]", + winsize.ws_row); + lprintf(LOG_NOTICE, " cols=NUM Set terminal columns [default=%d]", + winsize.ws_col); + + lprintf(LOG_NOTICE, " altterm Alternate terminal setup [default=off]"); +} + +int +ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + struct pollfd fds_wait[3], fds_data_wait[3], *fds; + struct sockaddr_in sin, myaddr, *sa_in; + socklen_t mylen; + char *recvip = NULL; + char out_buff[IPMI_BUF_SIZE * 8], in_buff[IPMI_BUF_SIZE]; + char buff[IPMI_BUF_SIZE + 4]; + int fd_socket, result, i; + int out_buff_fill, in_buff_fill; + int ip1, ip2, ip3, ip4; + int read_only = 0, rows = 0, cols = 0; + int port = IPMI_TSOL_DEF_PORT; + + if (strlen(intf->name) < 3 || strncmp(intf->name, "lan", 3) != 0) { + lprintf(LOG_ERR, "Error: Tyan SOL is only available over lan interface"); + return -1; + } + + for (i = 0; i<argc; i++) { + if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) { + /* not free'd ...*/ + /* recvip = strdup(argv[i]); */ + recvip = argv[i]; + } + else if (sscanf(argv[i], "port=%d", &ip1) == 1) + port = ip1; + else if (sscanf(argv[i], "rows=%d", &ip1) == 1) + rows = ip1; + else if (sscanf(argv[i], "cols=%d", &ip1) == 1) + cols = ip1; + else if (strlen(argv[i]) == 2 && strncmp(argv[i], "ro", 2) == 0) + read_only = 1; + else if (strlen(argv[i]) == 2 && strncmp(argv[i], "rw", 2) == 0) + read_only = 0; + else if (strlen(argv[i]) == 7 && strncmp(argv[i], "altterm", 7) == 0) + _altterm = 1; + else if (strlen(argv[i]) == 4 && strncmp(argv[i], "help", 4) == 0) { + print_tsol_usage(); + return 0; + } + else { + print_tsol_usage(); + return 0; + } + } + + /* create udp socket to receive the packet */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + sa_in = (struct sockaddr_in *)&intf->session->addr; + result = inet_pton(AF_INET, (const char *)intf->session->hostname, + &sa_in->sin_addr); + + if (result <= 0) { + struct hostent *host = gethostbyname((const char *)intf->session->hostname); + if (host == NULL ) { + lprintf(LOG_ERR, "Address lookup for %s failed", + intf->session->hostname); + return -1; + } + if (host->h_addrtype != AF_INET) { + lprintf(LOG_ERR, + "Address lookup for %s failed. Got %s, expected IPv4 address.", + intf->session->hostname, + (host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown"); + return (-1); + } + sa_in->sin_family = host->h_addrtype; + memcpy(&sa_in->sin_addr, host->h_addr, host->h_length); + } + + fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd_socket < 0) { + lprintf(LOG_ERR, "Can't open port %d", port); + return -1; + } + if (-1 == bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin))) { + lprintf(LOG_ERR, "Failed to bind socket."); + close(fd_socket); + return -1; + } + + /* + * retrieve local IP address if not supplied on command line + */ + if (recvip == NULL) { + result = intf->open(intf); /* must connect first */ + if (result < 0) { + close(fd_socket); + return -1; + } + + mylen = sizeof(myaddr); + if (getsockname(intf->fd, (struct sockaddr *)&myaddr, &mylen) < 0) { + lperror(LOG_ERR, "getsockname failed"); + close(fd_socket); + return -1; + } + + recvip = inet_ntoa(myaddr.sin_addr); + if (recvip == NULL) { + lprintf(LOG_ERR, "Unable to find local IP address"); + close(fd_socket); + return -1; + } + } + + printf("[Starting %sSOL with receiving address %s:%d]\n", + read_only ? "Read-only " : "", recvip, port); + + set_terminal_size(rows, cols); + enter_raw_mode(); + + /* + * talk to smdc to start Console redirect - IP address and port as parameter + * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x06 0xC0 0xA8 0xA8 0x78 0x1A 0x0A + */ + result = ipmi_tsol_start(intf, recvip, port); + if (result < 0) { + lprintf(LOG_ERR, "Error starting SOL"); + close(fd_socket); + return -1; + } + + printf("[SOL Session operational. Use %c? for help]\n", + intf->session->sol_escape_char); + + gettimeofday(&_start_keepalive, 0); + + fds_wait[0].fd = fd_socket; + fds_wait[0].events = POLLIN; + fds_wait[0].revents = 0; + fds_wait[1].fd = fileno(stdin); + fds_wait[1].events = POLLIN; + fds_wait[1].revents = 0; + fds_wait[2].fd = -1; + fds_wait[2].events = 0; + fds_wait[2].revents = 0; + + fds_data_wait[0].fd = fd_socket; + fds_data_wait[0].events = POLLIN | POLLOUT; + fds_data_wait[0].revents = 0; + fds_data_wait[1].fd = fileno(stdin); + fds_data_wait[1].events = POLLIN; + fds_data_wait[1].revents = 0; + fds_data_wait[2].fd = fileno(stdout); + fds_data_wait[2].events = POLLOUT; + fds_data_wait[2].revents = 0; + + out_buff_fill = 0; + in_buff_fill = 0; + fds = fds_wait; + + for (;;) { + result = poll(fds, 3, 15*1000); + if (result < 0) + break; + + /* send keepalive packet */ + tsol_keepalive(intf); + + if ((fds[0].revents & POLLIN) && (sizeof(out_buff) > out_buff_fill)){ + socklen_t sin_len = sizeof(sin); + result = recvfrom(fd_socket, buff, sizeof(out_buff) - out_buff_fill + 4, 0, + (struct sockaddr *)&sin, &sin_len); + + /* read the data from udp socket, skip some bytes in the head */ + if((result - 4) > 0 ){ + int length = result - 4; +#if 1 + length = (unsigned char)buff[2] & 0xff; + length *= 256; + length += ((unsigned char)buff[3] & 0xff); + if ((length <= 0) || (length > (result - 4))) + length = result - 4; +#endif + memcpy(out_buff + out_buff_fill, buff + 4, length); + out_buff_fill += length; + } + } + if ((fds[1].revents & POLLIN) && (sizeof(in_buff) > in_buff_fill)) { + result = read(fileno(stdin), in_buff + in_buff_fill, + sizeof(in_buff) - in_buff_fill); // read from keyboard + if (result > 0) { + int bytes; + bytes = do_inbuf_actions(intf, in_buff + in_buff_fill, result); + if(bytes < 0) { + result = ipmi_tsol_stop(intf, recvip, port); + do_terminal_cleanup(); + return result; + } + if (read_only) + bytes = 0; + in_buff_fill += bytes; + } + } + if ((fds[2].revents & POLLOUT) && out_buff_fill) { + result = write(fileno(stdout), out_buff, out_buff_fill); // to screen + if (result > 0) { + out_buff_fill -= result; + if (out_buff_fill) { + memmove(out_buff, out_buff + result, out_buff_fill); + } + } + } + if ((fds[0].revents & POLLOUT) && in_buff_fill) { + /* + * translate key and send that to SMDC using IPMI + * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x03 0x04 0x1B 0x5B 0x43 + */ + result = ipmi_tsol_send_keystroke(intf, in_buff, __min(in_buff_fill,14)); + if (result > 0) { + gettimeofday(&_start_keepalive, 0); + in_buff_fill -= result; + if (in_buff_fill) { + memmove(in_buff, in_buff + result, in_buff_fill); + } + } + } + fds = (in_buff_fill || out_buff_fill )? + fds_data_wait : fds_wait; + } + + return 0; +} diff --git a/lib/ipmi_user.c b/lib/ipmi_user.c new file mode 100644 index 0000000..d7e5890 --- /dev/null +++ b/lib/ipmi_user.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/time.h> +#include <signal.h> +#include <unistd.h> + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_user.h> +#include <ipmitool/ipmi_constants.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + + +extern int verbose; +extern int csv_output; + + +#define IPMI_PASSWORD_DISABLE_USER 0x00 +#define IPMI_PASSWORD_ENABLE_USER 0x01 +#define IPMI_PASSWORD_SET_PASSWORD 0x02 +#define IPMI_PASSWORD_TEST_PASSWORD 0x03 + +/* + * ipmi_get_user_access + * + * param intf [in] + * param channel_number [in] + * param user_id [in] + * param user_access [out] + * + * return 0 on succes + * 1 on failure + */ +static int +ipmi_get_user_access( + struct ipmi_intf *intf, + uint8_t channel_number, + uint8_t user_id, + struct user_access_rsp *user_access) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; /* 0x06 */ + req.msg.cmd = IPMI_GET_USER_ACCESS; /* 0x44 */ + req.msg.data = msg_data; + req.msg.data_len = 2; + + + /* The channel number will remain constant throughout this function */ + msg_data[0] = channel_number; + msg_data[1] = user_id; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Get User Access command failed " + "(channel %d, user %d)", channel_number, user_id); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Get User Access command failed " + "(channel %d, user %d): %s", channel_number, user_id, + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(user_access, + rsp->data, + sizeof(struct user_access_rsp)); + + return 0; +} + + + +/* + * ipmi_get_user_name + * + * param intf [in] + * param channel_number [in] + * param user_id [in] + * param user_name [out] + * + * return 0 on succes + * 1 on failure + */ +static int +ipmi_get_user_name( + struct ipmi_intf *intf, + uint8_t user_id, + char *user_name) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[1]; + + memset(user_name, 0, 17); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; /* 0x06 */ + req.msg.cmd = IPMI_GET_USER_NAME; /* 0x45 */ + req.msg.data = msg_data; + req.msg.data_len = 1; + + msg_data[0] = user_id; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Get User Name command failed (user %d)", + user_id); + return -1; + } + if (rsp->ccode > 0) { + if (rsp->ccode == 0xcc) + return 0; + lprintf(LOG_ERR, "Get User Name command failed (user %d): %s", + user_id, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + memcpy(user_name, rsp->data, 16); + + return 0; +} + + + + +static void +dump_user_access( + uint8_t user_id, + const char * user_name, + struct user_access_rsp * user_access) +{ + static int printed_header = 0; + + if (! printed_header) + { + printf("ID Name Callin Link Auth IPMI Msg " + "Channel Priv Limit\n"); + printed_header = 1; + } + + printf("%-4d%-17s%-8s%-11s%-11s%-s\n", + user_id, + user_name, + user_access->no_callin_access? "false": "true ", + user_access->link_auth_access? "true ": "false", + user_access->ipmi_messaging_access? "true ": "false", + val2str(user_access->channel_privilege_limit, + ipmi_privlvl_vals)); +} + + + +static void +dump_user_access_csv( + uint8_t user_id, + const char *user_name, + struct user_access_rsp *user_access) +{ + printf("%d,%s,%s,%s,%s,%s\n", + user_id, + user_name, + user_access->no_callin_access? "false": "true", + user_access->link_auth_access? "true": "false", + user_access->ipmi_messaging_access? "true": "false", + val2str(user_access->channel_privilege_limit, + ipmi_privlvl_vals)); +} + +static int +ipmi_print_user_list( + struct ipmi_intf *intf, + uint8_t channel_number) +{ + /* This is where you were! */ + char user_name[17]; + struct user_access_rsp user_access; + uint8_t current_user_id = 1; + + + do + { + if (ipmi_get_user_access(intf, + channel_number, + current_user_id, + &user_access)) + return -1; + + + if (ipmi_get_user_name(intf, + current_user_id, + user_name)) + return -1; + + if ((current_user_id == 0) || + user_access.link_auth_access || + user_access.ipmi_messaging_access || + strcmp("", user_name)) + { + if (csv_output) + dump_user_access_csv(current_user_id, + user_name, &user_access); + else + dump_user_access(current_user_id, + user_name, + &user_access); + } + + + ++current_user_id; + } while((current_user_id <= user_access.maximum_ids) && + (current_user_id <= IPMI_UID_MAX)); /* Absolute maximum allowed by spec */ + + + return 0; +} + + + +static int +ipmi_print_user_summary( + struct ipmi_intf * intf, + uint8_t channel_number) +{ + struct user_access_rsp user_access; + + if (ipmi_get_user_access(intf, + channel_number, + 1, + &user_access)) + return -1; + + if (csv_output) + { + printf("%d,%d,%d\n", + user_access.maximum_ids, + user_access.enabled_user_count, + user_access.fixed_name_count); + } + else + { + printf("Maximum IDs : %d\n", + user_access.maximum_ids); + printf("Enabled User Count : %d\n", + user_access.enabled_user_count); + printf("Fixed Name Count : %d\n", + user_access.fixed_name_count); + } + + return 0; +} + + + +/* + * ipmi_user_set_username + */ +static int +ipmi_user_set_username( + struct ipmi_intf *intf, + uint8_t user_id, + const char *name) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[17]; + + /* + * Ensure there is space for the name in the request message buffer + */ + if (strlen(name) >= sizeof(msg_data)) { + return -1; + } + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; /* 0x06 */ + req.msg.cmd = IPMI_SET_USER_NAME; /* 0x45 */ + req.msg.data = msg_data; + req.msg.data_len = sizeof(msg_data); + memset(msg_data, 0, sizeof(msg_data)); + + /* The channel number will remain constant throughout this function */ + msg_data[0] = user_id; + strncpy((char *)(msg_data + 1), name, strlen(name)); + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Set User Name command failed (user %d, name %s)", + user_id, name); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set User Name command failed (user %d, name %s): %s", + user_id, name, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +static int +ipmi_user_set_userpriv( + struct ipmi_intf *intf, + uint8_t channel, + uint8_t user_id, + const unsigned char privLevel) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint8_t msg_data[4] = {0, 0, 0, 0}; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; /* 0x06 */ + req.msg.cmd = IPMI_SET_USER_ACCESS; /* 0x43 */ + req.msg.data = msg_data; + req.msg.data_len = 4; + + /* The channel number will remain constant throughout this function */ + msg_data[0] = (channel & 0x0f); + msg_data[1] = (user_id & 0x3f); + msg_data[2] = (privLevel & 0x0f); + msg_data[3] = 0; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) + { + lprintf(LOG_ERR, "Set Privilege Level command failed (user %d)", + user_id); + return -1; + } + if (rsp->ccode > 0) + { + lprintf(LOG_ERR, "Set Privilege Level command failed (user %d): %s", + user_id, val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +/* + * ipmi_user_set_password + * + * This function is responsible for 4 things + * Enabling/Disabling users + * Setting/Testing passwords + */ +static int +ipmi_user_set_password( + struct ipmi_intf * intf, + uint8_t user_id, + uint8_t operation, + const char *password, + int is_twenty_byte_password) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[22]; + + int password_length = (is_twenty_byte_password? 20 : 16); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; /* 0x06 */ + req.msg.cmd = IPMI_SET_USER_PASSWORD; /* 0x47 */ + req.msg.data = msg_data; + req.msg.data_len = password_length + 2; + + + /* The channel number will remain constant throughout this function */ + msg_data[0] = user_id; + + if (is_twenty_byte_password) + msg_data[0] |= 0x80; + + msg_data[1] = operation; + + memset(msg_data + 2, 0, password_length); + + if (password != NULL) + strncpy((char *)(msg_data + 2), password, password_length); + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_ERR, "Set User Password command failed (user %d)", + user_id); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set User Password command failed (user %d): %s", + user_id, val2str(rsp->ccode, completion_code_vals)); + return rsp->ccode; + } + + return 0; +} + + + +/* + * ipmi_user_test_password + * + * Call ipmi_user_set_password, and interpret the result + */ +static int +ipmi_user_test_password( + struct ipmi_intf * intf, + uint8_t user_id, + const char * password, + int is_twenty_byte_password) +{ + int ret; + + ret = ipmi_user_set_password(intf, + user_id, + IPMI_PASSWORD_TEST_PASSWORD, + password, + is_twenty_byte_password); + + switch (ret) { + case 0: + printf("Success\n"); + break; + case 0x80: + printf("Failure: password incorrect\n"); + break; + case 0x81: + printf("Failure: wrong password size\n"); + break; + default: + printf("Unknown error\n"); + } + + return ((ret == 0) ? 0 : -1); +} + + +/* + * print_user_usage + */ +static void +print_user_usage(void) +{ + lprintf(LOG_NOTICE, "User Commands:"); + lprintf(LOG_NOTICE, " summary [<channel number>]"); + lprintf(LOG_NOTICE, " list [<channel number>]"); + lprintf(LOG_NOTICE, " set name <user id> <username>"); + lprintf(LOG_NOTICE, " set password <user id> [<password>]"); + lprintf(LOG_NOTICE, " disable <user id>"); + lprintf(LOG_NOTICE, " enable <user id>"); + lprintf(LOG_NOTICE, + " priv <user id> <privilege level> [<channel number>]"); + lprintf(LOG_NOTICE, " test <user id> <16|20> [<password]>\n"); +} + + +const char * +ipmi_user_build_password_prompt(uint8_t user_id) +{ + static char prompt[128]; + memset(prompt, 0, 128); + snprintf(prompt, 128, "Password for user %d: ", user_id); + return prompt; +} + + +/* + * ipmi_user_main + * + * Upon entry to this function argv should contain our arguments + * specific to this subcommand + */ +int +ipmi_user_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int retval = 0; + + /* + * Help + */ + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) + { + print_user_usage(); + } + + /* + * Summary + */ + else if (strncmp(argv[0], "summary", 7) == 0) + { + uint8_t channel; + + if (argc == 1) + channel = 0x0E; /* Ask about the current channel */ + else if (argc == 2) + { + if (str2uchar(argv[1], &channel) != 0) + { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return (-1); + } + } + else + { + print_user_usage(); + return -1; + } + + retval = ipmi_print_user_summary(intf, channel); + } + + + /* + * List + */ + else if (strncmp(argv[0], "list", 4) == 0) + { + uint8_t channel; + + if (argc == 1) + channel = 0x0E; /* Ask about the current channel */ + else if (argc == 2) + { + if (str2uchar(argv[1], &channel) != 0) + { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return (-1); + } + } + else + { + print_user_usage(); + return -1; + } + + retval = ipmi_print_user_list(intf, channel); + } + + + + /* + * Test + */ + else if (strncmp(argv[0], "test", 4) == 0) + { + // a little irritating, isn't it + if (argc == 3 || argc == 4) + { + char * password = NULL; + int password_length = 0; + uint8_t user_id = 0; + if (is_ipmi_user_id(argv[1], &user_id)) { + return (-1); + } + if (str2int(argv[2], &password_length) != 0 + || (password_length != 16 && password_length != 20)) { + lprintf(LOG_ERR, + "Given password length '%s' is invalid.", + argv[2]); + lprintf(LOG_ERR, "Expected value is either 16 or 20."); + return (-1); + } + + if (argc == 3) + { + /* We need to prompt for a password */ + + char * tmp; + const char * password_prompt = + ipmi_user_build_password_prompt(user_id); +# ifdef HAVE_GETPASSPHRASE + tmp = getpassphrase (password_prompt); +# else + tmp = (char*)getpass (password_prompt); +# endif + if (tmp != NULL) { + password = strdup(tmp); + tmp = NULL; + } + if (password == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + } + else { + password = strdup(argv[3]); + } + + + retval = ipmi_user_test_password(intf, + user_id, + password, + password_length == 20); + if (password != NULL) { + free(password); + password = NULL; + } + } + else + { + print_user_usage(); + return -1; + } + } + + /* + * Set + */ + else if (strncmp(argv[0], "set", 3) == 0) + { + /* + * Set Password + */ + if ((argc >= 3) && + (strncmp("password", argv[1], 8) == 0)) + { + char * password = NULL; + uint8_t user_id = 0; + if (is_ipmi_user_id(argv[2], &user_id)) { + return (-1); + } + + if (argc == 3) + { + /* We need to prompt for a password */ + char * tmp; + const char * password_prompt = + ipmi_user_build_password_prompt(user_id); +# ifdef HAVE_GETPASSPHRASE + tmp = getpassphrase (password_prompt); +# else + tmp = (char*)getpass (password_prompt); +# endif + if (tmp != NULL) { + password = strdup(tmp); + tmp = NULL; + } + if (password == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } +# ifdef HAVE_GETPASSPHRASE + tmp = getpassphrase (password_prompt); +# else + tmp = (char*)getpass (password_prompt); +# endif + if (tmp == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return (-1); + } + if (strlen(password) != strlen(tmp) + || strncmp(password, tmp, strlen(tmp))) { + lprintf(LOG_ERR, "Passwords do not match."); + free(password); + password = NULL; + return -1; + } + tmp = NULL; + } else { + password = strdup(argv[3]); + } + + if (password == NULL) { + lprintf(LOG_ERR, "Unable to parse password argument."); + return -1; + } + else if (strlen(password) > 20) + { + lprintf(LOG_ERR, "Password is too long (> 20 bytes)"); + return -1; + } + + retval = ipmi_user_set_password(intf, + user_id, + IPMI_PASSWORD_SET_PASSWORD, + password, + strlen(password) > 16); + if (password != NULL) { + free(password); + password = NULL; + } + } + + /* + * Set Name + */ + else if ((argc >= 2) && + (strncmp("name", argv[1], 4) == 0)) + { + uint8_t user_id = 0; + if (argc != 4) + { + print_user_usage(); + return -1; + } + if (is_ipmi_user_id(argv[2], &user_id)) { + return (-1); + } + + if (strlen(argv[3]) > 16) + { + lprintf(LOG_ERR, "Username is too long (> 16 bytes)"); + return -1; + } + + retval = ipmi_user_set_username(intf, user_id, argv[3]); + } + else + { + print_user_usage(); + return -1; + } + } + + else if (strncmp(argv[0], "priv", 4) == 0) + { + uint8_t user_id; + uint8_t priv_level; + uint8_t channel = 0x0e; /* Use channel running on */ + + if (argc != 3 && argc != 4) + { + print_user_usage(); + return -1; + } + + if (argc == 4) + { + if (str2uchar(argv[3], &channel) != 0) + { + lprintf(LOG_ERR, "Invalid channel: %s", argv[3]); + return (-1); + } + channel = (channel & 0x0f); + } + + if (str2uchar(argv[2], &priv_level) != 0) + { + lprintf(LOG_ERR, "Invalid privilege level: %s", argv[2]); + return (-1); + } + priv_level = (priv_level & 0x0f); + + if (is_ipmi_user_id(argv[1], &user_id)) { + return (-1); + } + + retval = ipmi_user_set_userpriv(intf,channel,user_id,priv_level); + } + + /* + * Disable / Enable + */ + else if ((strncmp(argv[0], "disable", 7) == 0) || + (strncmp(argv[0], "enable", 6) == 0)) + { + uint8_t user_id; + uint8_t operation; + char null_password[16]; /* Not used, but required */ + + memset(null_password, 0, sizeof(null_password)); + + if (argc != 2) + { + print_user_usage(); + return -1; + } + + if (is_ipmi_user_id(argv[1], &user_id)) { + return (-1); + } + + operation = (strncmp(argv[0], "disable", 7) == 0) ? + IPMI_PASSWORD_DISABLE_USER : IPMI_PASSWORD_ENABLE_USER; + + retval = ipmi_user_set_password(intf, + user_id, + operation, + null_password, + 0); /* This field is ignored */ + } + else + { + retval = -1; + lprintf(LOG_ERR, "Invalid user command: '%s'\n", argv[0]); + print_user_usage(); + } + + return retval; +} diff --git a/lib/log.c b/lib/log.c new file mode 100644 index 0000000..bc80542 --- /dev/null +++ b/lib/log.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> + +#include <ipmitool/log.h> + +struct logpriv_s { + char * name; + int daemon; + int level; +}; +struct logpriv_s *logpriv; + +static void log_reinit(void) +{ + log_init(NULL, 0, 0); +} + +void lprintf(int level, const char * format, ...) +{ + static char logmsg[LOG_MSG_LENGTH]; + va_list vptr; + + if (!logpriv) + log_reinit(); + + if (logpriv->level < level) + return; + + va_start(vptr, format); + vsnprintf(logmsg, LOG_MSG_LENGTH, format, vptr); + va_end(vptr); + + if (logpriv->daemon) + syslog(level, "%s", logmsg); + else + fprintf(stderr, "%s\n", logmsg); + return; +} + +void lperror(int level, const char * format, ...) +{ + static char logmsg[LOG_MSG_LENGTH]; + va_list vptr; + + if (!logpriv) + log_reinit(); + + if (logpriv->level < level) + return; + + va_start(vptr, format); + vsnprintf(logmsg, LOG_MSG_LENGTH, format, vptr); + va_end(vptr); + + if (logpriv->daemon) + syslog(level, "%s: %s", logmsg, strerror(errno)); + else + fprintf(stderr, "%s: %s\n", logmsg, strerror(errno)); + return; +} + +/* + * open connection to syslog if daemon + */ +void log_init(const char * name, int isdaemon, int verbose) +{ + if (logpriv) + return; + + logpriv = malloc(sizeof(struct logpriv_s)); + if (!logpriv) + return; + + if (name != NULL) + logpriv->name = strdup(name); + else + logpriv->name = strdup(LOG_NAME_DEFAULT); + + if (logpriv->name == NULL) + fprintf(stderr, "ipmitool: malloc failure\n"); + + logpriv->daemon = isdaemon; + logpriv->level = verbose + LOG_NOTICE; + + if (logpriv->daemon) + openlog(logpriv->name, LOG_CONS, LOG_LOCAL4); +} + +/* + * stop syslog logging if daemon mode, + * free used memory that stored log service + */ +void log_halt(void) +{ + if (!logpriv) + return; + + if (logpriv->name) { + free(logpriv->name); + logpriv->name = NULL; + } + + if (logpriv->daemon) + closelog(); + + free(logpriv); + logpriv = NULL; +} + +int log_level_get(void) +{ + return logpriv->level; +} + +void log_level_set(int level) +{ + logpriv->level = level; +} + |