summaryrefslogtreecommitdiffstats
path: root/common/recipes-core
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-core')
-rw-r--r--common/recipes-core/base-files/base-files_%.bbappend4
-rw-r--r--common/recipes-core/cfg-util/cfg-util_0.1.bb36
-rw-r--r--common/recipes-core/cfg-util/files/Makefile11
-rw-r--r--common/recipes-core/cfg-util/files/cfg-util.c78
-rw-r--r--common/recipes-core/consoled/consoled_0.1.bb36
-rw-r--r--common/recipes-core/consoled/files/Makefile11
-rw-r--r--common/recipes-core/consoled/files/consoled.c284
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl/Makefile26
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl/README5
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl/fand.cpp1030
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl/watchdog.cpp201
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl/watchdog.h60
-rw-r--r--common/recipes-core/fan-ctrl/fan-ctrl_0.1.bb61
-rw-r--r--common/recipes-core/fruid/files/Makefile26
-rw-r--r--common/recipes-core/fruid/files/fruid-util.c145
-rw-r--r--common/recipes-core/fruid/fruid_0.1.bb55
-rw-r--r--common/recipes-core/ipmbd/files/Makefile10
-rw-r--r--common/recipes-core/ipmbd/files/ipmbd.c843
-rw-r--r--common/recipes-core/ipmbd/ipmbd_0.1.bb19
-rw-r--r--common/recipes-core/ipmid/files/Makefile29
-rw-r--r--common/recipes-core/ipmid/files/fruid.h28
-rw-r--r--common/recipes-core/ipmid/files/ipmid.c1412
-rw-r--r--common/recipes-core/ipmid/files/sdr.c414
-rw-r--r--common/recipes-core/ipmid/files/sdr.h72
-rw-r--r--common/recipes-core/ipmid/files/sel.c440
-rw-r--r--common/recipes-core/ipmid/files/sel.h51
-rw-r--r--common/recipes-core/ipmid/files/sensor.h117
-rw-r--r--common/recipes-core/ipmid/files/timestamp.c46
-rw-r--r--common/recipes-core/ipmid/files/timestamp.h30
-rw-r--r--common/recipes-core/ipmid/ipmid_0.2.bb42
-rw-r--r--common/recipes-core/power-util/files/Makefile11
-rw-r--r--common/recipes-core/power-util/files/power-util.c295
-rw-r--r--common/recipes-core/power-util/power-util_0.1.bb36
-rw-r--r--common/recipes-core/sensor-mon/files/Makefile26
-rw-r--r--common/recipes-core/sensor-mon/files/sensord.c1031
-rw-r--r--common/recipes-core/sensor-mon/sensor-mon_0.1.bb58
-rw-r--r--common/recipes-core/sensor-util/files/Makefile11
-rw-r--r--common/recipes-core/sensor-util/files/sensor-util.c173
-rw-r--r--common/recipes-core/sensor-util/sensor-util_0.1.bb34
-rw-r--r--common/recipes-core/watchdog-ctrl/watchdog-ctrl_0.1.bb15
40 files changed, 7310 insertions, 2 deletions
diff --git a/common/recipes-core/base-files/base-files_%.bbappend b/common/recipes-core/base-files/base-files_%.bbappend
index 340ad2c..6a73f2b 100644
--- a/common/recipes-core/base-files/base-files_%.bbappend
+++ b/common/recipes-core/base-files/base-files_%.bbappend
@@ -15,8 +15,8 @@ do_install_bmc_issue () {
dir=$(dirname $dir)
done
- if [ -d "$dir/meta-aspeed/.git" ]; then
- srcdir="$dir/meta-aspeed"
+ if [ -d "$dir/meta-openbmc/.git" ]; then
+ srcdir="$dir/meta-openbmc"
srcdir_git="${srcdir}/.git"
version=$(git --git-dir=${srcdir_git} --work-tree=${srcdir} describe --dirty 2> /dev/null)
else
diff --git a/common/recipes-core/cfg-util/cfg-util_0.1.bb b/common/recipes-core/cfg-util/cfg-util_0.1.bb
new file mode 100644
index 0000000..13b6568
--- /dev/null
+++ b/common/recipes-core/cfg-util/cfg-util_0.1.bb
@@ -0,0 +1,36 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+SUMMARY = "Configuration Utility"
+DESCRIPTION = "Utility for reading or writing configuration"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://cfg-util.c;beginline=4;endline=16;md5=b395943ba8a0717a83e62ca123a8d238"
+
+SRC_URI = "file://Makefile \
+ file://cfg-util.c \
+ "
+S = "${WORKDIR}"
+
+LDFLAGS =+ " -lpal "
+
+DEPENDS =+ " libpal "
+
+binfiles = "cfg-util"
+
+pkgdir = "cfg-util"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 cfg-util ${dst}/cfg-util
+ ln -snf ../fbpackages/${pkgdir}/cfg-util ${bin}/cfg-util
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/cfg-util ${prefix}/local/bin"
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/cfg-util/files/Makefile b/common/recipes-core/cfg-util/files/Makefile
new file mode 100644
index 0000000..05b9b16
--- /dev/null
+++ b/common/recipes-core/cfg-util/files/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+all: cfg-util
+
+
+cfg-util: cfg-util.o
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o cfg-util
diff --git a/common/recipes-core/cfg-util/files/cfg-util.c b/common/recipes-core/cfg-util/files/cfg-util.c
new file mode 100644
index 0000000..99618f7
--- /dev/null
+++ b/common/recipes-core/cfg-util/files/cfg-util.c
@@ -0,0 +1,78 @@
+/*
+ * cfg-util
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <openbmc/pal.h>
+
+static void
+print_usage(void) {
+ printf("Usage: cfg-util <dump-all|key> <value>\n");
+}
+
+int
+main(int argc, char **argv) {
+
+ int ret;
+ uint8_t key[MAX_KEY_LEN] = {0};
+ uint8_t val[MAX_VALUE_LEN] = {0};
+
+ // Handle boundary checks
+ if (argc < 2 || argc > 3) {
+ goto err_exit;
+ }
+
+ // Handle dump of key value data base
+ if ((argc == 2) && (!strcmp(argv[1], "dump-all"))){
+ pal_dump_key_value();
+ return 0;
+ }
+
+ // Handle Get the Configuration
+ if (argc == 2) {
+ snprintf(key, MAX_KEY_LEN, "%s", argv[1]);
+
+ ret = pal_get_key_value(key, val);
+ if (ret) {
+ goto err_exit;
+ }
+
+ printf("%s\n", val);
+ return 0;
+ }
+
+ // Handle Set the configuration
+ snprintf(key, MAX_KEY_LEN, "%s", argv[1]);
+ snprintf(val, MAX_VALUE_LEN, "%s", argv[2]);
+
+ ret = pal_set_key_value(key, val);
+ if (ret) {
+ goto err_exit;
+ }
+
+ return 0;
+
+err_exit:
+ print_usage();
+ exit(-1);
+}
diff --git a/common/recipes-core/consoled/consoled_0.1.bb b/common/recipes-core/consoled/consoled_0.1.bb
new file mode 100644
index 0000000..5b6edd1
--- /dev/null
+++ b/common/recipes-core/consoled/consoled_0.1.bb
@@ -0,0 +1,36 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+SUMMARY = "Serial Buffer"
+DESCRIPTION = "Daemon for serial buffering"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://consoled.c;beginline=4;endline=16;md5=b395943ba8a0717a83e62ca123a8d238"
+
+SRC_URI = "file://Makefile \
+ file://consoled.c \
+ "
+S = "${WORKDIR}"
+
+LDFLAGS =+ " -lpal "
+
+DEPENDS =+ " libpal "
+
+binfiles = "consoled"
+
+pkgdir = "consoled"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 consoled ${dst}/consoled
+ ln -snf ../fbpackages/${pkgdir}/consoled ${bin}/consoled
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/consoled ${prefix}/local/bin"
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/consoled/files/Makefile b/common/recipes-core/consoled/files/Makefile
new file mode 100644
index 0000000..2b30ef2
--- /dev/null
+++ b/common/recipes-core/consoled/files/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+all: consoled
+
+
+consoled: consoled.o
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o consoled
diff --git a/common/recipes-core/consoled/files/consoled.c b/common/recipes-core/consoled/files/consoled.c
new file mode 100644
index 0000000..9f17e31
--- /dev/null
+++ b/common/recipes-core/consoled/files/consoled.c
@@ -0,0 +1,284 @@
+/*
+ * consoled
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <termios.h>
+#include <signal.h>
+#include <openbmc/pal.h>
+
+#define BAUDRATE B57600
+#define CTRL_X 0x18
+#define ASCII_ENTER 0x0D
+
+static sig_atomic_t sigexit = 0;
+
+static void
+write_data(int file, char *buf, int len, char *fname) {
+ int wlen = 0;
+ char *tbuf = buf;
+ while(len > 0) {
+ errno = 0;
+ wlen = write(file, tbuf, len);
+ // In case wlen < 0, retry write()
+ if (wlen >= 0) {
+ len -= wlen;
+ tbuf = tbuf + wlen;
+ } else {
+ syslog(LOG_ALERT, "write_data: write() failed to file %s | errno: %d",
+ fname, errno);
+ return;
+ }
+ }
+}
+
+static void
+exit_session(int sig)
+{
+ sigexit = sig;
+}
+
+static void
+print_usage() {
+ printf("Usage: consoled [ %s ]\n", pal_server_list);
+}
+
+static void
+run_console(char* fru_name, int term) {
+
+ int i;
+ int tty; // serial port
+ int buf_fd; // Buffer File
+ int blen; // len for
+ int nfd = 0; // For number of fd
+ int nevents; // For number of events in fd
+ int nline = 0;
+ //int pid_fd;
+ int flags;
+ pid_t pid; // For pid of the daemon
+ uint8_t fru;
+ char in; // For stdin character
+ char data[32];
+ char pid_file[64];
+ char devtty[32]; // For tty dev path
+ char bfname[32]; // For buffer file path
+ char old_bfname[32]; // For old buffer file path
+ char buf[256]; // For buffer data
+ struct termios ottytio, nttytio; // For the tty dev
+
+ int stdi; // STDIN_FILENO
+ int stdo; // STDOUT_FILENO
+ struct termios ostditio, nstditio; // For STDIN_FILENO
+ struct termios ostdotio, nstdotio; // For STDOUT_FILENO
+
+ struct pollfd pfd[2];
+
+ /* Start Daemon for the console buffering */
+ if (!term) {
+ daemon(0,1);
+ openlog("consoled", LOG_CONS, LOG_DAEMON);
+ syslog(LOG_INFO, "consoled: daemon started");
+ }
+
+ if (pal_get_fru_id(fru_name, &fru)) {
+ exit(-1);
+ }
+
+ if (pal_get_fru_devtty(fru, devtty)) {
+ exit(-1);
+ }
+
+ /* Handling the few Signals differently */
+ signal(SIGHUP, exit_session);
+ signal(SIGINT, exit_session);
+ signal(SIGTERM, exit_session);
+ signal(SIGPIPE, exit_session);
+ signal(SIGQUIT, exit_session);
+
+ /* Different flag value for tty dev */
+ flags = term == 1 ? (O_RDWR | O_NOCTTY | O_NONBLOCK) :
+ (O_RDONLY | O_NOCTTY | O_NONBLOCK);
+
+ if ((tty = open(devtty, flags)) < 0) {
+ syslog(LOG_ALERT, "Cannot open the file %s", devtty);
+ exit(-1);
+ }
+ fcntl(tty, F_SETFL, O_RDWR);
+
+ /* Changing the attributes of the tty dev */
+ tcgetattr(tty, &ottytio);
+ memcpy(&nttytio, &ottytio, sizeof(struct termios));
+ cfmakeraw(&nttytio);
+ cfsetspeed(&nttytio, BAUDRATE);
+ tcflush(tty, TCIFLUSH);
+ tcsetattr(tty, TCSANOW, &nttytio);
+ pfd[0].fd = tty;
+ pfd[0].events = POLLIN;
+ nfd++;
+
+ /* Buffering the console data into a file */
+ sprintf(old_bfname, "/tmp/consoled_%s_log-old", fru_name);
+ sprintf(bfname, "/tmp/consoled_%s_log", fru_name);
+ if ((buf_fd = open(bfname, O_RDWR | O_APPEND | O_CREAT, 0666)) < 0) {
+ syslog(LOG_ALERT, "Cannot open the file %s", bfname);
+ exit(-1);
+ }
+
+ if (term) {
+ /* Changing the attributes of STDIN_FILENO */
+ stdi = STDIN_FILENO;
+ tcgetattr(stdi, &ostditio);
+ memcpy(&nstditio, &ostditio, sizeof(struct termios));
+ cfmakeraw(&nstditio);
+ tcflush(stdi, TCIFLUSH);
+ tcsetattr(stdi, TCSANOW, &nstditio);
+
+ /* Changing the attributes of STDOUT_FILENO */
+ stdo = STDOUT_FILENO;
+ tcgetattr(stdo, &ostdotio);
+ memcpy(&nstdotio, &ostdotio, sizeof(struct termios));
+ cfmakeraw(&nstdotio);
+ tcflush(stdo, TCIFLUSH);
+ tcsetattr(stdo, TCSANOW, &nstdotio);
+
+ /* Adding STDIN_FILENO to the poll fd set */
+ pfd[1].fd = stdi;
+ pfd[1].events = POLLIN;
+ nfd++;
+ }
+
+ /* Handling the input event from the terminal and tty dev */
+ while (!sigexit && (nevents = poll(pfd, nfd, -1 /* Timeout */))) {
+
+ /* Input to the terminal from the user */
+ if (term && nevents && nfd > 1 && pfd[1].revents > 0) {
+ blen = read(stdi, &in, sizeof(in));
+ if (blen < 1) {
+ nfd--;
+ }
+
+ if (in == CTRL_X) {
+ break;
+ }
+
+ write_data(tty, &in, sizeof(in), "tty");
+
+ nevents--;
+ }
+
+ /* Input from the tty dev */
+ if (nevents && pfd[0].revents > 0) {
+ blen = read(tty, buf, sizeof(buf));
+ if (blen > 0) {
+ for (i = 0; i < blen; i++) {
+ if (buf[i] == 0xD)
+ nline++;
+ }
+ write_data(buf_fd, buf, blen, bfname);
+ fsync(buf_fd);
+ if (term) {
+ write_data(stdo, buf, blen, "STDOUT_FILENO");
+ }
+ } else if (blen < 0) {
+ raise(SIGHUP);
+ }
+
+ /* Log Rotation */
+ if (nline >= 300) {
+ close(buf_fd);
+ remove(old_bfname);
+ rename(bfname, old_bfname);
+ if ((buf_fd = open(bfname, O_RDWR | O_APPEND | O_CREAT, 0666)) < 0) {
+ syslog(LOG_ALERT, "Cannot open the file %s", bfname);
+ exit(-1);
+ }
+ nline = 0;
+ }
+ nevents--;
+ }
+ }
+
+ /* Close the console buffer file */
+ close(buf_fd);
+
+ /* Revert the tty dev to old attributes */
+ tcflush(tty, TCIFLUSH);
+ tcsetattr(tty, TCSANOW, &ottytio);
+
+ /* Revert STDIN to old attributes */
+ if (term) {
+ tcflush(stdo, TCIFLUSH);
+ tcsetattr(stdo, TCSANOW, &ostdotio);
+ tcflush(stdi, TCIFLUSH);
+ tcsetattr(stdi, TCSANOW, &ostditio);
+ }
+
+ /* Delete the pid file */
+ if (!term)
+ remove(pid_file);
+}
+
+int
+main(int argc, void **argv) {
+ int dev, rc, lock_file;
+ char file[64];
+ int term;
+ char *fru_name;
+
+ if (argc != 3) {
+ print_usage();
+ exit(1);
+ }
+
+ // A lock file for one instance of consoled for each fru
+ sprintf(file, "/var/lock/consoled_%s", argv[1]);
+ lock_file = open(file, O_CREAT | O_RDWR, 0666);
+ rc = flock(lock_file, LOCK_EX | LOCK_NB);
+ if(rc) {
+ if(errno == EWOULDBLOCK) {
+ printf("Another consoled %s instance is running...\n", argv[1]);
+ exit(-1);
+ }
+ } else {
+
+ fru_name = argv[1];
+
+ if (!strcmp(argv[2], "--buffer")) {
+ term = 0;
+ } else if (!strcmp(argv[2], "--term")) {
+ term = 1;
+ } else {
+ print_usage();
+ exit(-1);
+ }
+
+ run_console(fru_name, term);
+ }
+
+ return sigexit;
+}
+
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl/Makefile b/common/recipes-core/fan-ctrl/fan-ctrl/Makefile
new file mode 100644
index 0000000..da74a34
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl/Makefile
@@ -0,0 +1,26 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+all: fand
+
+fand: fand.cpp watchdog.cpp
+ $(CXX) $(CXXFLAGS) -pthread -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o fand
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl/README b/common/recipes-core/fan-ctrl/fan-ctrl/README
new file mode 100644
index 0000000..2a92b9d
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl/README
@@ -0,0 +1,5 @@
+The AST PWM/Tach driver is in the kernel sources at drivers/hwmon/ast_pwm_fan.c
+
+There are 7 PWM output pins. Each PWM can be configured in one of 3 types (M,
+N, or O). The clock settings for each type are configurable. See init_pwm.sh
+for more comments about how we configure the settings.
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl/fand.cpp b/common/recipes-core/fan-ctrl/fan-ctrl/fand.cpp
new file mode 100644
index 0000000..49c6f6b
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl/fand.cpp
@@ -0,0 +1,1030 @@
+/*
+ * fand
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Daemon to manage the fan speed to ensure that we stay within a reasonable
+ * temperature range. We're using a simplistic algorithm to get started:
+ *
+ * If the fan is already on high, we'll move it to medium if we fall below
+ * a top temperature. If we're on medium, we'll move it to high
+ * if the temperature goes over the top value, and to low if the
+ * temperature falls to a bottom level. If the fan is on low,
+ * we'll increase the speed if the temperature rises to the top level.
+ *
+ * To ensure that we're not just turning the fans up, then back down again,
+ * we'll require an extra few degrees of temperature drop before we lower
+ * the fan speed.
+ *
+ * We check the RPM of the fans against the requested RPMs to determine
+ * whether the fans are failing, in which case we'll turn up all of
+ * the other fans and report the problem..
+ *
+ * TODO: Implement a PID algorithm to closely track the ideal temperature.
+ * TODO: Determine if the daemon is already started.
+ */
+
+/* Yeah, the file ends in .cpp, but it's a C program. Deal. */
+
+#if !defined(CONFIG_YOSEMITE) && !defined(CONFIG_WEDGE)
+#error "No hardware platform defined!"
+#endif
+#if defined(CONFIG_YOSEMITE) && defined(CONFIG_WEDGE)
+#error "Both hardware platform defined!"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <syslog.h>
+#ifdef CONFIG_YOSEMITE
+#include <openbmc/ipmi.h>
+#include <facebook/bic.h>
+#include <facebook/yosemite_sensor.h>
+#endif
+#ifdef CONFIG_WEDGE
+#include <facebook/wedge_eeprom.h>
+#endif
+
+#include "watchdog.h"
+
+/* Sensor definitions */
+
+#ifdef CONFIG_WEDGE
+#define INTERNAL_TEMPS(x) ((x) * 1000) // stored a C * 1000
+#define EXTERNAL_TEMPS(x) ((x) / 1000)
+#elif CONFIG_YOSEMITE
+#define INTERNAL_TEMPS(x) (x)
+#define EXTERNAL_TEMPS(x) (x)
+#define TOTAL_1S_SERVERS 4
+#endif
+
+#define I2C_BUS_3_DIR "/sys/class/i2c-adapter/i2c-3/"
+#define I2C_BUS_4_DIR "/sys/class/i2c-adapter/i2c-4/"
+
+#define INTAKE_TEMP_DEVICE I2C_BUS_3_DIR "3-0048"
+#define T2_TEMP_DEVICE I2C_BUS_3_DIR "3-0049"
+#define EXHAUST_TEMP_DEVICE I2C_BUS_3_DIR "3-004a"
+#define USERVER_TEMP_DEVICE I2C_BUS_4_DIR "4-0040"
+
+/*
+ * The sensor for the uServer CPU is not on the CPU itself, so it reads
+ * a little low. We are special casing this, but we should obviously
+ * be thinking about a way to generalize these tweaks, and perhaps
+ * the entire configuration. JSON file?
+ */
+
+#ifdef CONFIG_WEDGE
+#define USERVER_TEMP_FUDGE INTERNAL_TEMPS(10)
+#else
+#define USERVER_TEMP_FUDGE INTERNAL_TEMPS(1)
+#endif
+
+#define BAD_TEMP INTERNAL_TEMPS(-60)
+
+#define BAD_READ_THRESHOLD 4 /* How many times can reads fail */
+#define FAN_FAILURE_THRESHOLD 4 /* How many times can a fan fail */
+#define FAN_SHUTDOWN_THRESHOLD 20 /* How long fans can be failed before */
+ /* we just shut down the whole thing. */
+
+
+#define PWM_DIR "/sys/devices/platform/ast_pwm_tacho.0"
+
+#define PWM_UNIT_MAX 96
+
+#define LARGEST_DEVICE_NAME 120
+
+#define GPIO_USERVER_POWER_DIRECTION "/sys/class/gpio/gpio25/direction"
+#define GPIO_USERVER_POWER "/sys/class/gpio/gpio25/value"
+#define GPIO_T2_POWER_DIRECTION "/tmp/gpionames/T2_POWER_UP/direction"
+#define GPIO_T2_POWER "/tmp/gpionames/T2_POWER_UP/value"
+
+#define GPIO_FAN0_LED "/sys/class/gpio/gpio53/value"
+#define GPIO_FAN1_LED "/sys/class/gpio/gpio54/value"
+#define GPIO_FAN2_LED "/sys/class/gpio/gpio55/value"
+#define GPIO_FAN3_LED "/sys/class/gpio/gpio72/value"
+
+const char *fan_led[] = {GPIO_FAN0_LED, GPIO_FAN1_LED,
+ GPIO_FAN2_LED, GPIO_FAN3_LED};
+
+#define FAN_LED_RED "0"
+#define FAN_LED_BLUE "1"
+
+#define GPIO_PIN_ID "/sys/class/gpio/gpio%d/value"
+#define REV_IDS 3
+#define GPIO_REV_ID_START 192
+
+#define BOARD_IDS 4
+#define GPIO_BOARD_ID_START 160
+
+/*
+ * With hardware revisions after 3, we use a different set of pins for
+ * the BOARD_ID.
+ */
+
+#define REV_ID_NEW_BOARD_ID 3
+#define GPIO_BOARD_ID_START_NEW 166
+
+#define REPORT_TEMP 720 /* Report temp every so many cycles */
+
+/* Sensor limits and tuning parameters */
+
+#define INTAKE_LIMIT INTERNAL_TEMPS(60)
+#define T2_LIMIT INTERNAL_TEMPS(80)
+#define USERVER_LIMIT INTERNAL_TEMPS(90)
+
+#define TEMP_TOP INTERNAL_TEMPS(70)
+#define TEMP_BOTTOM INTERNAL_TEMPS(40)
+
+/*
+ * Toggling the fan constantly will wear it out (and annoy anyone who
+ * can hear it), so we'll only turn down the fan after the temperature
+ * has dipped a bit below the point at which we'd otherwise switch
+ * things up.
+ */
+
+#define COOLDOWN_SLOP INTERNAL_TEMPS(6)
+
+#define WEDGE_FAN_LOW 35
+#define WEDGE_FAN_MEDIUM 50
+#define WEDGE_FAN_HIGH 70
+#define WEDGE_FAN_MAX 99
+
+#define SIXPACK_FAN_LOW 35
+#define SIXPACK_FAN_MEDIUM 55
+#define SIXPACK_FAN_HIGH 75
+#define SIXPACK_FAN_MAX 99
+
+/*
+ * Mapping physical to hardware addresses for fans; it's different for
+ * RPM measuring and PWM setting, naturally. Doh.
+ */
+
+#ifdef CONFIG_WEDGE
+int fan_to_rpm_map[] = {3, 2, 0, 1};
+int fan_to_pwm_map[] = {7, 6, 0, 1};
+#define FANS 4
+// Tacho offset between front and rear fans:
+#define REAR_FAN_OFFSET 4
+#define BACK_TO_BACK_FANS
+#else
+int fan_to_rpm_map[] = {0, 1};
+int fan_to_pwm_map[] = {0, 1};
+#define FANS 2
+// Tacho offset between front and rear fans:
+#define REAR_FAN_OFFSET 1
+#endif
+
+
+/*
+ * The measured RPM of the fans doesn't match linearly to the requested
+ * rate. In addition, there are coaxially mounted fans, so the rear fans
+ * feed into the front fans. The rear fans will run slower since they're
+ * grabbing still air, and the front fants are getting an extra boost.
+ *
+ * We'd like to measure the fan RPM and compare it to the expected RPM
+ * so that we can detect failed fans, so we have a table (derived from
+ * hardware testing):
+ */
+
+struct rpm_to_pct_map {
+ ushort pct;
+ ushort rpm;
+};
+
+#ifdef CONFIG_WEDGE
+struct rpm_to_pct_map rpm_front_map[] = {{30, 6150},
+ {35, 7208},
+ {40, 8195},
+ {45, 9133},
+ {50, 10017},
+ {55, 10847},
+ {60, 11612},
+ {65, 12342},
+ {70, 13057},
+ {75, 13717},
+ {80, 14305},
+ {85, 14869},
+ {90, 15384},
+ {95, 15871},
+ {100, 16095}};
+#define FRONT_MAP_SIZE (sizeof(rpm_front_map) / sizeof(struct rpm_to_pct_map))
+
+struct rpm_to_pct_map rpm_rear_map[] = {{30, 3911},
+ {35, 4760},
+ {40, 5587},
+ {45, 6434},
+ {50, 7295},
+ {55, 8187},
+ {60, 9093},
+ {65, 10008},
+ {70, 10949},
+ {75, 11883},
+ {80, 12822},
+ {85, 13726},
+ {90, 14690},
+ {95, 15516},
+ {100, 15897}};
+#define REAR_MAP_SIZE (sizeof(rpm_rear_map) / sizeof(struct rpm_to_pct_map))
+#else
+struct rpm_to_pct_map rpm_map[] = {{30, 3413},
+ {35, 3859},
+ {40, 4305},
+ {45, 4686},
+ {50, 5032},
+ {55, 5432},
+ {60, 5991},
+ {65, 6460},
+ {70, 6927},
+ {75, 7379},
+ {80, 7733},
+ {85, 8156},
+ {90, 8599},
+ {95, 9049},
+ {100, 9265}};
+struct rpm_to_pct_map *rpm_front_map = rpm_map;
+struct rpm_to_pct_map *rpm_rear_map = rpm_map;
+#define MAP_SIZE (sizeof(rpm_map) / sizeof(struct rpm_to_pct_map))
+#define FRONT_MAP_SIZE MAP_SIZE
+#define REAR_MAP_SIZE MAP_SIZE
+
+#endif
+
+#define FAN_FAILURE_OFFSET 30
+
+int fan_low = WEDGE_FAN_LOW;
+int fan_medium = WEDGE_FAN_MEDIUM;
+int fan_high = WEDGE_FAN_HIGH;
+int fan_max = WEDGE_FAN_MAX;
+int total_fans = FANS;
+int fan_offset = 0;
+
+int temp_bottom = TEMP_BOTTOM;
+int temp_top = TEMP_TOP;
+
+int report_temp = REPORT_TEMP;
+bool verbose = false;
+
+void usage() {
+ fprintf(stderr,
+ "fand [-v] [-l <low-pct>] [-m <medium-pct>] "
+ "[-h <high-pct>]\n"
+ "\t[-b <temp-bottom>] [-t <temp-top>] [-r <report-temp>]\n\n"
+ "\tlow-pct defaults to %d%% fan\n"
+ "\tmedium-pct defaults to %d%% fan\n"
+ "\thigh-pct defaults to %d%% fan\n"
+ "\ttemp-bottom defaults to %dC\n"
+ "\ttemp-top defaults to %dC\n"
+ "\treport-temp defaults to every %d measurements\n\n"
+ "fand compensates for uServer temperature reading %d degrees low\n"
+ "kill with SIGUSR1 to stop watchdog\n",
+ fan_low,
+ fan_medium,
+ fan_high,
+ EXTERNAL_TEMPS(temp_bottom),
+ EXTERNAL_TEMPS(temp_top),
+ report_temp,
+ EXTERNAL_TEMPS(USERVER_TEMP_FUDGE));
+ exit(1);
+}
+
+/* We need to open the device each time to read a value */
+
+int read_device(const char *device, int *value) {
+ FILE *fp;
+ int rc;
+
+ fp = fopen(device, "r");
+ if (!fp) {
+ int err = errno;
+
+ syslog(LOG_INFO, "failed to open device %s", device);
+ return err;
+ }
+
+ rc = fscanf(fp, "%d", value);
+ fclose(fp);
+
+ if (rc != 1) {
+ syslog(LOG_INFO, "failed to read device %s", device);
+ return ENOENT;
+ } else {
+ return 0;
+ }
+}
+
+/* We need to open the device again each time to write a value */
+
+int write_device(const char *device, const char *value) {
+ FILE *fp;
+ int rc;
+
+ fp = fopen(device, "w");
+ if (!fp) {
+ int err = errno;
+
+ syslog(LOG_INFO, "failed to open device for write %s", device);
+ return err;
+ }
+
+ rc = fputs(value, fp);
+ fclose(fp);
+
+ if (rc < 0) {
+ syslog(LOG_INFO, "failed to write device %s", device);
+ return ENOENT;
+ } else {
+ return 0;
+ }
+}
+
+#ifdef CONFIG_WEDGE
+int read_temp(const char *device, int *value) {
+ char full_name[LARGEST_DEVICE_NAME + 1];
+
+ /* We set an impossible value to check for errors */
+ *value = BAD_TEMP;
+ snprintf(
+ full_name, LARGEST_DEVICE_NAME, "%s/temp1_input", device);
+ return read_device(full_name, value);
+}
+
+int read_gpio_value(const int id, const char *device, int *value) {
+ char full_name[LARGEST_DEVICE_NAME];
+
+ snprintf(full_name, LARGEST_DEVICE_NAME, device, id);
+ return read_device(full_name, value);
+}
+
+int read_gpio_values(const int start, const int count,
+ const char *device, int *result) {
+ int status = 0;
+ int value;
+
+ *result = 0;
+ for (int i = 0; i < count; i++) {
+ status |= read_gpio_value(start + i, GPIO_PIN_ID, &value);
+ *result |= value << i;
+ }
+ return status;
+}
+
+int read_ids(int *rev_id, int *board_id) {
+ int status = 0;
+ int value;
+
+ status = read_gpio_values(GPIO_REV_ID_START, REV_IDS, GPIO_PIN_ID, rev_id);
+ if (status != 0) {
+ syslog(LOG_INFO, "failed to read rev_id");
+ return status;
+ }
+
+ int board_id_start;
+ if (*rev_id >= REV_ID_NEW_BOARD_ID) {
+ board_id_start = GPIO_BOARD_ID_START_NEW;
+ } else {
+ board_id_start = GPIO_BOARD_ID_START;
+ }
+
+ status = read_gpio_values(board_id_start, BOARD_IDS, GPIO_PIN_ID, board_id);
+ if (status != 0) {
+ syslog(LOG_INFO, "failed to read board_id");
+ }
+ return status;
+}
+
+bool is_two_fan_board(bool verbose) {
+ struct wedge_eeprom_st eeprom;
+ /* Retrieve the board type from EEPROM */
+ if (wedge_eeprom_parse(NULL, &eeprom) == 0) {
+ /* able to parse EEPROM */
+ if (verbose) {
+ syslog(LOG_INFO, "board type is %s", eeprom.fbw_location);
+ }
+ /* only WEDGE is NOT two-fan board */
+ return strncasecmp(eeprom.fbw_location, "wedge",
+ sizeof(eeprom.fbw_location));
+ } else {
+ int status;
+ int board_id = 0;
+ int rev_id = 0;
+ /*
+ * Could not parse EEPROM. Most likely, it is an old HW without EEPROM.
+ * In this case, use board ID to distinguish if it is wedge or 6-pack.
+ */
+ status = read_ids(&rev_id, &board_id);
+ if (verbose) {
+ syslog(LOG_INFO, "rev ID %d, board id %d", rev_id, board_id);
+ }
+ if (status == 0 && board_id != 0xf) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+#endif
+
+int read_fan_value(const int fan, const char *device, int *value) {
+ char device_name[LARGEST_DEVICE_NAME];
+ char output_value[LARGEST_DEVICE_NAME];
+ char full_name[LARGEST_DEVICE_NAME];
+
+ snprintf(device_name, LARGEST_DEVICE_NAME, device, fan);
+ snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name);
+ return read_device(full_name, value);
+}
+
+int write_fan_value(const int fan, const char *device, const int value) {
+ char full_name[LARGEST_DEVICE_NAME];
+ char device_name[LARGEST_DEVICE_NAME];
+ char output_value[LARGEST_DEVICE_NAME];
+
+ snprintf(device_name, LARGEST_DEVICE_NAME, device, fan);
+ snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name);
+ snprintf(output_value, LARGEST_DEVICE_NAME, "%d", value);
+ return write_device(full_name, output_value);
+}
+
+/* Return fan speed as a percentage of maximum -- not necessarily linear. */
+
+int fan_rpm_to_pct(const struct rpm_to_pct_map *table,
+ const int table_len,
+ int rpm) {
+ int i;
+
+ for (i = 0; i < table_len; i++) {
+ if (table[i].rpm > rpm) {
+ break;
+ }
+ }
+
+ /*
+ * If the fan RPM is lower than the lowest value in the table,
+ * we may have a problem -- fans can only go so slow, and it might
+ * have stopped. In this case, we'll return an interpolated
+ * percentage, as just returning zero is even more problematic.
+ */
+
+ if (i == 0) {
+ return (rpm * table[i].pct) / table[i].rpm;
+ } else if (i == table_len) { // Fell off the top?
+ return table[i - 1].pct;
+ }
+
+ // Interpolate the right percentage value:
+
+ int percent_diff = table[i].pct - table[i - 1].pct;
+ int rpm_diff = table[i].rpm - table[i - 1].rpm;
+ int fan_diff = table[i].rpm - rpm;
+
+ return table[i].pct - (fan_diff * percent_diff / rpm_diff);
+}
+
+int fan_speed_okay(const int fan, const int speed, const int slop) {
+ int front_fan, rear_fan;
+ int front_pct, rear_pct;
+ int real_fan;
+ int okay;
+
+ /*
+ * The hardware fan numbers are different from the physical order
+ * in the box, so we have to map them:
+ */
+
+ real_fan = fan_to_rpm_map[fan];
+
+ front_fan = 0;
+ read_fan_value(real_fan, "tacho%d_rpm", &front_fan);
+ front_pct = fan_rpm_to_pct(rpm_front_map, FRONT_MAP_SIZE, front_fan);
+#ifdef BACK_TO_BACK_FANS
+ rear_fan = 0;
+ read_fan_value(real_fan + REAR_FAN_OFFSET, "tacho%d_rpm", &rear_fan);
+ rear_pct = fan_rpm_to_pct(rpm_rear_map, REAR_MAP_SIZE, rear_fan);
+#endif
+
+
+ /*
+ * If the fans are broken, the measured rate will be rather
+ * different from the requested rate, and we can turn up the
+ * rest of the fans to compensate. The slop is the percentage
+ * of error that we'll tolerate.
+ *
+ * XXX: I suppose that we should only measure negative values;
+ * running too fast isn't really a problem.
+ */
+
+#ifdef BACK_TO_BACK_FANS
+ okay = (abs(front_pct - speed) * 100 / speed < slop &&
+ abs(rear_pct - speed) * 100 / speed < slop);
+#else
+ okay = (abs(front_pct - speed) * 100 / speed < slop);
+#endif
+
+ if (!okay || verbose) {
+ syslog(!okay ? LOG_ALERT : LOG_INFO,
+#ifdef BACK_TO_BACK_FANS
+ "fan %d rear %d (%d%%), front %d (%d%%), expected %d",
+#else
+ "fan %d %d RPM (%d%%), expected %d",
+#endif
+ fan,
+#ifdef BACK_TO_BACK_FANS
+ rear_fan,
+ rear_pct,
+#endif
+ front_fan,
+ front_pct,
+ speed);
+ }
+
+ return okay;
+}
+
+/* Set fan speed as a percentage */
+
+int write_fan_speed(const int fan, const int value) {
+ /*
+ * The hardware fan numbers for pwm control are different from
+ * both the physical order in the box, and the mapping for reading
+ * the RPMs per fan, above.
+ */
+
+ int real_fan = fan_to_pwm_map[fan];
+
+ if (value == 0) {
+ return write_fan_value(real_fan, "pwm%d_en", 0);
+ } else {
+ int unit = value * PWM_UNIT_MAX / 100;
+ int status;
+
+ if (unit == PWM_UNIT_MAX)
+ unit = 0;
+
+ if ((status = write_fan_value(real_fan, "pwm%d_type", 0)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_rising", 0)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_falling", unit)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_en", 1)) != 0) {
+ return status;
+ }
+ }
+}
+
+/* Set up fan LEDs */
+
+int write_fan_led(const int fan, const char *color)
+{
+#ifdef CONFIG_WEDGE
+ return write_device(fan_led[fan], color);
+#else
+ return 0;
+#endif
+}
+
+int server_shutdown(const char *why) {
+ int fan;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_max);
+ }
+
+ syslog(LOG_EMERG, "Shutting down: %s", why);
+ write_device(GPIO_USERVER_POWER_DIRECTION, "out");
+ write_device(GPIO_USERVER_POWER, "0");
+#ifdef CONFIG_WEDGE
+ /*
+ * Putting T2 in reset generates a non-maskable interrupt to uS,
+ * the kernel running on uS might panic depending on its version.
+ * sleep 5s here to make sure uS is completely down.
+ */
+ sleep(5);
+
+ if (write_device(GPIO_T2_POWER_DIRECTION, "out") ||
+ write_device(GPIO_T2_POWER, "1")) {
+ /*
+ * We're here because something has gone badly wrong. If we
+ * didn't manage to shut down the T2, cut power to the whole box,
+ * using the PMBus OPERATION register. This will require a power
+ * cycle (removal of both power inputs) to recover.
+ */
+ syslog(LOG_EMERG, "T2 power off failed; turning off via ADM1278");
+ system("rmmod adm1275");
+ system("i2cset -y 12 0x10 0x01 00");
+ }
+
+#else
+ // TODO(7088822): try throttling, then shutting down server.
+ syslog(LOG_EMERG, "Need to implement actual shutdown!\n");
+#endif
+
+ /*
+ * We have to stop the watchdog, or the system will be automatically
+ * rebooted some seconds after fand exits (and stops kicking the
+ * watchdog).
+ */
+
+ stop_watchdog();
+
+ sleep(2);
+ exit(2);
+}
+
+/* Gracefully shut down on receipt of a signal */
+
+void fand_interrupt(int sig)
+{
+ int fan;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_max);
+ }
+
+ syslog(LOG_ALERT, "Shutting down fand on signal %s", strsignal(sig));
+ if (sig == SIGUSR1) {
+ stop_watchdog();
+ }
+ exit(3);
+}
+
+int main(int argc, char **argv) {
+ /* Sensor values */
+
+#ifdef CONFIG_WEDGE
+ int intake_temp;
+ int exhaust_temp;
+ int t2_temp;
+ int userver_temp;
+#else
+ float intake_temp;
+ float exhaust_temp;
+ float userver_temp;
+#endif
+
+ int fan_speed = fan_high;
+ int bad_reads = 0;
+ int fan_failure = 0;
+ int fan_speed_changes = 0;
+ int old_speed;
+
+ int fan_bad[FANS];
+ int fan;
+
+ unsigned log_count = 0; // How many times have we logged our temps?
+ int opt;
+ int prev_fans_bad = 0;
+
+ struct sigaction sa;
+
+ sa.sa_handler = fand_interrupt;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+
+ // Start writing to syslog as early as possible for diag purposes.
+
+ openlog("fand", LOG_CONS, LOG_DAEMON);
+
+#ifdef CONFIG_WEDGE
+ if (is_two_fan_board(false)) {
+ /* Alternate, two fan configuration */
+ total_fans = 2;
+ fan_offset = 2; /* fan 3 is the first */
+
+ fan_low = SIXPACK_FAN_LOW;
+ fan_medium = SIXPACK_FAN_MEDIUM;
+ fan_high = SIXPACK_FAN_HIGH;
+ fan_max = SIXPACK_FAN_MAX;
+ fan_speed = fan_high;
+ }
+#endif
+
+ while ((opt = getopt(argc, argv, "l:m:h:b:t:r:v")) != -1) {
+ switch (opt) {
+ case 'l':
+ fan_low = atoi(optarg);
+ break;
+ case 'm':
+ fan_medium = atoi(optarg);
+ break;
+ case 'h':
+ fan_high = atoi(optarg);
+ break;
+ case 'b':
+ temp_bottom = INTERNAL_TEMPS(atoi(optarg));
+ break;
+ case 't':
+ temp_top = INTERNAL_TEMPS(atoi(optarg));
+ break;
+ case 'r':
+ report_temp = atoi(optarg);
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind > argc) {
+ usage();
+ }
+
+ if (temp_bottom > temp_top) {
+ fprintf(stderr,
+ "Should temp-bottom (%d) be higher than "
+ "temp-top (%d)? Starting anyway.\n",
+ EXTERNAL_TEMPS(temp_bottom),
+ EXTERNAL_TEMPS(temp_top));
+ }
+
+ if (fan_low > fan_medium || fan_low > fan_high || fan_medium > fan_high) {
+ fprintf(stderr,
+ "fan RPMs not strictly increasing "
+ "-- %d, %d, %d, starting anyway\n",
+ fan_low,
+ fan_medium,
+ fan_high);
+ }
+
+ daemon(1, 0);
+
+ if (verbose) {
+ syslog(LOG_DEBUG, "Starting up; system should have %d fans.",
+ total_fans);
+ }
+
+ for (fan = 0; fan < total_fans; fan++) {
+ fan_bad[fan] = 0;
+ write_fan_speed(fan + fan_offset, fan_speed);
+ write_fan_led(fan + fan_offset, FAN_LED_BLUE);
+ }
+
+#ifdef CONFIG_YOSEMITE
+ /* Ensure that we can read from sensors before proceeding. */
+
+ while (yosemite_sensor_read(1, BIC_SENSOR_SOC_TEMP, &userver_temp))
+ syslog(LOG_DEBUG, "Failed reading of SOC_TEMP.");
+#endif
+
+ /* Start watchdog in manual mode */
+ start_watchdog(0);
+
+ /* Set watchdog to persistent mode so timer expiry will happen independent
+ * of this process's liveliness. */
+ set_persistent_watchdog(WATCHDOG_SET_PERSISTENT);
+
+ sleep(5); /* Give the fans time to come up to speed */
+
+ while (1) {
+ int max_temp;
+ old_speed = fan_speed;
+
+ /* Read sensors */
+
+#ifdef CONFIG_WEDGE
+ read_temp(INTAKE_TEMP_DEVICE, &intake_temp);
+ read_temp(EXHAUST_TEMP_DEVICE, &exhaust_temp);
+ read_temp(T2_TEMP_DEVICE, &t2_temp);
+ read_temp(USERVER_TEMP_DEVICE, &userver_temp);
+
+ /*
+ * uServer can be powered down, but all of the rest of the sensors
+ * should be readable at any time.
+ */
+
+ if ((intake_temp == BAD_TEMP || exhaust_temp == BAD_TEMP ||
+ t2_temp == BAD_TEMP)) {
+ bad_reads++;
+ }
+#else
+ userver_temp = BAD_TEMP;
+ if (yosemite_sensor_read(1, SP_SENSOR_INLET_TEMP, &intake_temp) ||
+ yosemite_sensor_read(1, SP_SENSOR_OUTLET_TEMP, &exhaust_temp))
+ bad_reads++;
+
+ /*
+ * There are a number of 1S servers; any or all of them
+ * could be powered off and returning no values. Ignore these
+ * invalid values.
+ */
+ for (int node = 1; node <= TOTAL_1S_SERVERS; node++) {
+ float new_temp;
+ if (!yosemite_sensor_read(node, BIC_SENSOR_SOC_TEMP, &new_temp)) {
+ if (userver_temp < new_temp) {
+ userver_temp = new_temp;
+ }
+ }
+ }
+#endif
+
+ if (bad_reads > BAD_READ_THRESHOLD) {
+ server_shutdown("Some sensors couldn't be read");
+ }
+
+ if (log_count++ % report_temp == 0) {
+ syslog(LOG_DEBUG,
+#ifdef CONFIG_WEDGE
+ "Temp intake %d, t2 %d, "
+ " userver %d, exhaust %d, "
+ "fan speed %d, speed changes %d",
+#else
+ "Temp intake %f, max server %f, exhaust %f, "
+ "fan speed %d, speed changes %d",
+#endif
+ intake_temp,
+#ifdef CONFIG_WEDGE
+ t2_temp,
+#endif
+ userver_temp,
+ exhaust_temp,
+ fan_speed,
+ fan_speed_changes);
+ }
+
+ /* Protection heuristics */
+
+ if (intake_temp > INTAKE_LIMIT) {
+ server_shutdown("Intake temp limit reached");
+ }
+
+#ifdef CONFIG_WEDGE
+ if (t2_temp > T2_LIMIT) {
+ server_shutdown("T2 temp limit reached");
+ }
+#endif
+
+ if (userver_temp + USERVER_TEMP_FUDGE > USERVER_LIMIT) {
+ server_shutdown("uServer temp limit reached");
+ }
+
+ /*
+ * Calculate change needed -- we should eventually
+ * do something more sophisticated, like PID.
+ *
+ * We should use the intake temperature to adjust this
+ * as well.
+ */
+
+#ifdef CONFIG_WEDGE
+ if (t2_temp > userver_temp + USERVER_TEMP_FUDGE) {
+ max_temp = t2_temp;
+ } else {
+ max_temp = userver_temp + USERVER_TEMP_FUDGE;
+ }
+#else
+ /* Yosemite could have no servers turned on, so ignore that case. */
+ if (userver_temp + USERVER_TEMP_FUDGE > exhaust_temp) {
+ max_temp = userver_temp + USERVER_TEMP_FUDGE;
+ } else {
+ max_temp = exhaust_temp;
+ }
+#endif
+
+ /*
+ * If recovering from a fan problem, spin down fans gradually in case
+ * temperatures are still high. Gradual spin down also reduces wear on
+ * the fans.
+ */
+ if (fan_speed == fan_max) {
+ if (fan_failure == 0) {
+ fan_speed = fan_high;
+ }
+ } else if (fan_speed == fan_high) {
+ if (max_temp + COOLDOWN_SLOP < temp_top) {
+ fan_speed = fan_medium;
+ }
+ } else if (fan_speed == fan_medium) {
+ if (max_temp > temp_top) {
+ fan_speed = fan_high;
+ } else if (max_temp + COOLDOWN_SLOP < temp_bottom) {
+ fan_speed = fan_low;
+ }
+ } else {/* low */
+ if (max_temp > temp_bottom) {
+ fan_speed = fan_medium;
+ }
+ }
+
+ /*
+ * Update fans only if there are no failed ones. If any fans failed
+ * earlier, all remaining fans should continue to run at max speed.
+ */
+
+ if (fan_failure == 0 && fan_speed != old_speed) {
+ syslog(LOG_NOTICE,
+ "Fan speed changing from %d to %d",
+ old_speed,
+ fan_speed);
+ fan_speed_changes++;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_speed);
+ }
+ }
+
+ /*
+ * Wait for some change. Typical I2C temperature sensors
+ * only provide a new value every second and a half, so
+ * checking again more quickly than that is a waste.
+ *
+ * We also have to wait for the fan changes to take effect
+ * before measuring them.
+ */
+
+ sleep(5);
+
+ /* Check fan RPMs */
+
+ for (fan = 0; fan < total_fans; fan++) {
+ /*
+ * Make sure that we're within some percentage
+ * of the requested speed.
+ */
+ if (fan_speed_okay(fan + fan_offset, fan_speed, FAN_FAILURE_OFFSET)) {
+ if (fan_bad[fan] > FAN_FAILURE_THRESHOLD) {
+ write_fan_led(fan + fan_offset, FAN_LED_BLUE);
+ syslog(LOG_NOTICE,
+ "Fan %d has recovered",
+ fan);
+ }
+ fan_bad[fan] = 0;
+ } else {
+ fan_bad[fan]++;
+ }
+ }
+
+ fan_failure = 0;
+ for (fan = 0; fan < total_fans; fan++) {
+ if (fan_bad[fan] > FAN_FAILURE_THRESHOLD) {
+ fan_failure++;
+ write_fan_led(fan + fan_offset, FAN_LED_RED);
+ }
+ }
+
+ if (fan_failure > 0) {
+ if (prev_fans_bad != fan_failure) {
+ syslog(LOG_ALERT, "%d fans failed", fan_failure);
+ }
+
+ /*
+ * If fans are bad, we need to blast all of the
+ * fans at 100%; we don't bother to turn off
+ * the bad fans, in case they are all that is left.
+ *
+ * Note that we have a temporary bug with setting fans to
+ * 100% so we only do fan_max = 99%.
+ */
+
+ fan_speed = fan_max;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_speed);
+ }
+
+ if (fan_failure == total_fans) {
+ int count = 0;
+ for (fan = 0; fan < total_fans; fan++) {
+ if (fan_bad[fan] > FAN_SHUTDOWN_THRESHOLD)
+ count++;
+ }
+ if (count == total_fans) {
+ server_shutdown("all fans are bad for more than 12 cycles");
+ }
+ }
+
+
+ /*
+ * Fans can be hot swapped and replaced; in which case the fan daemon
+ * will automatically detect the new fan and (assuming the new fan isn't
+ * itself faulty), automatically readjust the speeds for all fans down
+ * to a more suitable rpm. The fan daemon does not need to be restarted.
+ */
+ }
+
+ /* Suppress multiple warnings for similar number of fan failures. */
+ prev_fans_bad = fan_failure;
+
+ /* if everything is fine, restart the watchdog countdown. If this process
+ * is terminated, the persistent watchdog setting will cause the system
+ * to reboot after the watchdog timeout. */
+ kick_watchdog();
+ }
+}
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.cpp b/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.cpp
new file mode 100644
index 0000000..ebb390a
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.cpp
@@ -0,0 +1,201 @@
+/*
+ * watchdog
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "watchdog.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define WATCHDOG_START_KEY "x"
+#define WATCHDOG_STOP_KEY "X"
+/* The "magic close" character (as defined in Linux watchdog specs). */
+#define WATCHDOG_PERSISTENT_KEY "V"
+#define WATCHDOG_NON_PERSISTENT_KEY "a"
+
+static int watchdog_dev = -1;
+
+/* This is needed to prevent rapid consecutive stop/start watchdog calls from
+ * generating multiple threads. */
+static int watchdog_started = 0;
+
+static const char* watchdog_kick_key = WATCHDOG_PERSISTENT_KEY;
+static pthread_t watchdog_tid;
+static pthread_mutex_t watchdog_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Forward declarations. */
+static void* watchdog_thread(void* args);
+static int kick_watchdog_unsafe();
+
+/*
+ * When started, this background thread will constantly reset the watchdog
+ * at every 5 second interval.
+ */
+
+static void* watchdog_thread(void* args) {
+
+ pthread_detach(pthread_self());
+
+ /* Make sure another instance of the thread hasn't already been started. */
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_started) {
+ goto done;
+ } else {
+ watchdog_started = 1;
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+
+ /* Actual loop for refreshing the watchdog timer. */
+ while (1) {
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_dev != -1) {
+ kick_watchdog_unsafe();
+ } else {
+ break;
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+ sleep(5);
+ }
+
+ /* Broke out of loop because watchdog was stopped. */
+ watchdog_started = 0;
+done:
+ pthread_mutex_unlock(&watchdog_lock);
+ return NULL;
+}
+
+/*
+ * Starts the watchdog timer. timer counts down and restarts the ARM chip
+ * upon timeout. use kick_watchdog() to restart the timer.
+ *
+ * Returns: 1 on success; 0 otherwise.
+ */
+
+int start_watchdog(const int auto_mode) {
+ int status;
+
+ pthread_mutex_lock(&watchdog_lock);
+
+ /* Don't start the watchdog again if it has already been started. */
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, WATCHDOG_START_KEY, 1)) == 0
+ && errno == EINTR);
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_ALERT, "system watchdog already started.\n");
+ return 0;
+ }
+
+ while (((watchdog_dev = open("/dev/watchdog", O_WRONLY)) == -1) &&
+ errno == EINTR);
+
+ /* Fail if watchdog device is invalid or if the user asked for auto
+ * mode and the thread failed to spawn. */
+ if ((watchdog_dev == -1) ||
+ (auto_mode == 1 && watchdog_started == 0 &&
+ pthread_create(&watchdog_tid, NULL, watchdog_thread, NULL) != 0)) {
+ goto fail;
+ }
+
+ while ((status = write(watchdog_dev, WATCHDOG_START_KEY, 1)) == 0
+ && errno == EINTR);
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_INFO, "system watchdog started.\n");
+ return 1;
+
+fail:
+ if (watchdog_dev != -1) {
+ close(watchdog_dev);
+ watchdog_dev = -1;
+ }
+
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_ALERT, "system watchdog failed to start!\n");
+ return 0;
+}
+
+/*
+ * Toggles between watchdog persistent modes. In persistent mode, the watchdog
+ * timer will continue to tick even after process shutdown. Under non-
+ * persistent mode, the watchdog timer will automatically be disabled when the
+ * process shuts down.
+ */
+void set_persistent_watchdog(enum watchdog_persistent_en persistent) {
+ switch (persistent) {
+ case WATCHDOG_SET_PERSISTENT:
+ watchdog_kick_key = WATCHDOG_PERSISTENT_KEY;
+ break;
+ default:
+ watchdog_kick_key = WATCHDOG_NON_PERSISTENT_KEY;
+ break;
+ }
+ kick_watchdog();
+}
+
+/*
+ * Restarts the countdown timer on the watchdog, delaying restart by another
+ * timeout period (default: 11 seconds as configured in the device driver).
+ *
+ * This function assumes the watchdog lock has already been acquired and is
+ * only used internally within the watchdog code.
+ *
+ * Returns 1 on success; 0 or -1 indicates failure (check errno).
+ */
+
+static int kick_watchdog_unsafe() {
+ int status = 0;
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, watchdog_kick_key, 1)) == 0
+ && errno == EINTR);
+ }
+ return status;
+}
+
+/*
+ * Acquires the watchdog lock and resets the watchdog atomically. For use by
+ * library users.
+ */
+
+int kick_watchdog() {
+ int result;
+ pthread_mutex_lock(&watchdog_lock);
+ result = kick_watchdog_unsafe();
+ pthread_mutex_unlock(&watchdog_lock);
+
+ return result;
+}
+
+/* Shuts down the watchdog gracefully and disables the watchdog timer so that
+ * restarts no longer happen.
+ */
+
+void stop_watchdog() {
+ int status;
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, WATCHDOG_STOP_KEY, 1)) == 0
+ && errno == EINTR);
+ close(watchdog_dev);
+ watchdog_dev = -1;
+ syslog(LOG_INFO, "system watchdog stopped.\n");
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+}
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.h b/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.h
new file mode 100644
index 0000000..19b9944
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl/watchdog.h
@@ -0,0 +1,60 @@
+/*
+ * watchdog
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Utility library to handle the aspeed watchdog. Only one watchdog
+ * should be in use at any time throughout the entire system (multiple users
+ * will not cause adverse effects but the behavior of the watchdog becomes
+ * undefined).
+ *
+ * The watchdog can be started in either manual or automatic mode. In manual
+ * mode, the watchdog has to be constantly reset by the user via the
+ * kick_watchdog() function. Under automatic mode, the watchdog will
+ * run in a separate thread and reset the timer on its own so no intervention
+ * is required from the user.
+ *
+ * In both modes, the watchdog timer will not stop when the process is
+ * terminated, unless a call to stop_watchdog() has been made beforehand, or
+ * if the user runs in manual mode and uses a non persistent watchdog kick.
+ *
+ * The default timeout for the watchdog is 11 seconds. When this time period
+ * elapses, the ARM chip is restarted and the kernel is rebooted. Other
+ * hardware state is not reset, so this may introduce strange behavior on
+ * reboot (example: an I2C bus may be left in the open state, triggering
+ * constant interrupts). In rare cases, this could result in the kernel
+ * failing to fully restart itself and thus preclude the possibility of
+ * reinitializing the watchdog timer. Someone will then have to go over and
+ * physically restart the machine.
+ *
+ * The alternative to the soft reset is to request the watchdog device driver
+ * for a hard reset on timeout. However this will stop the fans. If the
+ * kernel fails to fully boot and restart the fan daemon, the system could
+ * overheat. For this reason, we've chosen to take the risk of a stuck soft
+ * reset instead.
+ *
+ */
+
+/* Forward declarations. */
+int start_watchdog(const int auto_mode);
+enum watchdog_persistent_en {
+ WATCHDOG_SET_PERSISTENT,
+ WATCHDOG_SET_NONPERSISTENT,
+};
+void set_persistent_watchdog(enum watchdog_persistent_en persistent);
+int kick_watchdog();
+void stop_watchdog();
diff --git a/common/recipes-core/fan-ctrl/fan-ctrl_0.1.bb b/common/recipes-core/fan-ctrl/fan-ctrl_0.1.bb
new file mode 100644
index 0000000..1abfaaa
--- /dev/null
+++ b/common/recipes-core/fan-ctrl/fan-ctrl_0.1.bb
@@ -0,0 +1,61 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+SUMMARY = "Fan controller"
+DESCRIPTION = "The utilities to control fan."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://fand.cpp;beginline=6;endline=18;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://README \
+ file://Makefile \
+ file://fand.cpp \
+ file://watchdog.h \
+ file://watchdog.cpp \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "fand \
+ "
+
+otherfiles = "README"
+
+pkgdir = "fan_ctrl"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+ for f in ${otherfiles}; do
+ install -m 644 $f ${dst}/$f
+ done
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/fan_ctrl ${prefix}/local/bin"
+
+# Inhibit complaints about .debug directories for the fand binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/fruid/files/Makefile b/common/recipes-core/fruid/files/Makefile
new file mode 100644
index 0000000..5c6aff4
--- /dev/null
+++ b/common/recipes-core/fruid/files/Makefile
@@ -0,0 +1,26 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+all: fruid-util
+
+fruid: fruid-util.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o fruid-util
diff --git a/common/recipes-core/fruid/files/fruid-util.c b/common/recipes-core/fruid/files/fruid-util.c
new file mode 100644
index 0000000..3c8d2e1
--- /dev/null
+++ b/common/recipes-core/fruid/files/fruid-util.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <openbmc/fruid.h>
+#include <openbmc/pal.h>
+
+/* Print the FRUID in detail */
+void print_fruid_info(fruid_info_t *fruid, const char *name)
+{
+ /* Print format */
+ printf("%-27s: %s", "\nFRU Information",
+ name /* Name of the FRU device */ );
+ printf("%-27s: %s", "\n---------------", "------------------");
+
+ if (fruid->chassis.flag) {
+ printf("%-27s: %s", "\nChassis Type",fruid->chassis.type_str);
+ printf("%-27s: %s", "\nChassis Part",fruid->chassis.part);
+ printf("%-27s: %s", "\nChassis Serial Number",fruid->chassis.serial);
+ printf("%-27s: %s", "\nChassis Custom Data",fruid->chassis.custom);
+ }
+
+ if (fruid->board.flag) {
+ printf("%-27s: %s", "\nBoard Mfg Time",fruid->board.mfg_time_str);
+ printf("%-27s: %s", "\nBoard Manufacturer",fruid->board.mfg);
+ printf("%-27s: %s", "\nBoard Name",fruid->board.name);
+ printf("%-27s: %s", "\nBoard Serial Number",fruid->board.serial);
+ printf("%-27s: %s", "\nBoard Part",fruid->board.part);
+ printf("%-27s: %s", "\nBoard FRU ID",fruid->board.fruid);
+ printf("%-27s: %s", "\nBoard Custom Data",fruid->board.custom);
+ }
+
+ if (fruid->product.flag) {
+ printf("%-27s: %s", "\nProduct Manufacturer",fruid->product.mfg);
+ printf("%-27s: %s", "\nProduct Name",fruid->product.name);
+ printf("%-27s: %s", "\nProduct Part",fruid->product.part);
+ printf("%-27s: %s", "\nProduct Version",fruid->product.version);
+ printf("%-27s: %s", "\nProduct Serial Number",fruid->product.serial);
+ printf("%-27s: %s", "\nProduct Asset Tag",fruid->product.asset_tag);
+ printf("%-27s: %s", "\nProduct FRU ID",fruid->product.fruid);
+ printf("%-27s: %s", "\nProduct Custom Data",fruid->product.custom);
+ }
+
+ printf("\n");
+}
+
+/* Populate and print fruid_info by parsing the fru's binary dump */
+void get_fruid_info(uint8_t fru, char *path, char* name) {
+ int ret;
+ fruid_info_t fruid;
+
+ ret = fruid_parse(path, &fruid);
+ if (ret) {
+ fprintf(stderr, "Failed print FRUID for %s\nCheck syslog for errors!\n",
+ name);
+ } else {
+ print_fruid_info(&fruid, name);
+ free_fruid_info(&fruid);
+ }
+
+}
+
+static int
+print_usage() {
+ printf("Usage: fruid-util [ %s ]\n", pal_fru_list);
+}
+
+/* Utility to just print the FRUID */
+int main(int argc, char * argv[]) {
+
+ int ret;
+ uint8_t fru;
+ char path[64] = {0};
+ char name[64] = {0};
+
+ if (argc != 2) {
+ print_usage();
+ exit(-1);
+ }
+
+ ret = pal_get_fru_id(argv[1], &fru);
+ if (ret < 0) {
+ print_usage();
+ return ret;
+ }
+
+ if (fru == 0) {
+ fru = 1;
+ while (fru <= MAX_NUM_FRUS) {
+ ret = pal_get_fruid_path(fru, path);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_get_fruid_name(fru, name);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (fru == FRU_NIC) {
+ printf("fruid-util does not support nic\n");
+ exit(-1);
+ }
+
+ get_fruid_info(fru, path, name);
+
+ fru++;
+ }
+ } else {
+ ret = pal_get_fruid_path(fru, path);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_get_fruid_name(fru, name);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (fru == FRU_NIC) {
+ printf("fruid-util does not support nic\n");
+ exit(-1);
+ }
+
+ get_fruid_info(fru, path, name);
+ }
+
+ return 0;
+}
diff --git a/common/recipes-core/fruid/fruid_0.1.bb b/common/recipes-core/fruid/fruid_0.1.bb
new file mode 100644
index 0000000..604845f
--- /dev/null
+++ b/common/recipes-core/fruid/fruid_0.1.bb
@@ -0,0 +1,55 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+SUMMARY = "IPMI FRUID Utilities"
+DESCRIPTION = "Util for ipmi fruid"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://fruid-util.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+LDFLAGS = " -lfruid -lpal "
+DEPENDS = "libfruid libpal "
+
+SRC_URI = "file://Makefile \
+ file://fruid-util.c \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "fruid-util \
+ "
+
+pkgdir = "fruid"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/fruid ${prefix}/local/bin"
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/ipmbd/files/Makefile b/common/recipes-core/ipmbd/files/Makefile
new file mode 100644
index 0000000..719ccc3
--- /dev/null
+++ b/common/recipes-core/ipmbd/files/Makefile
@@ -0,0 +1,10 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+all: ipmbd
+
+ipmbd: ipmbd.o
+ $(CC) $(CFLAGS) -pthread -lrt -lipmi -std=gnu99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o ipmbd
diff --git a/common/recipes-core/ipmbd/files/ipmbd.c b/common/recipes-core/ipmbd/files/ipmbd.c
new file mode 100644
index 0000000..5ea5af3
--- /dev/null
+++ b/common/recipes-core/ipmbd/files/ipmbd.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This module handles all the IPMB communication protocol
+ * Refer http://www.intel.com/content/www/us/en/servers/ipmi/ipmp-spec-v1-0.html
+ * for more information.
+ *
+ * IPMB packet format is described here for quick reference
+ * Request:
+ * <Responder Slave Address(rsSA)>
+ * <NetFn/ResponderLUN(Netfn/rsLUN)>
+ * <Header Checksum(hdrCksum)>
+ * <Requester Slave Address(rqSA)>
+ * <Requester Sequence Number/RequesterLUN(rqSeq/rqLUN>
+ * <Command>
+ * <Data[0..n]>
+ * <Data Checksum(dataCksum)>
+ * Response:
+ * <Requester Slave Address(rqSA)>
+ * <NetFn/RequesterLUN(Netfn/rqLUN)>
+ * <Header Checksum(hdrCksum)>
+ * <Responder Slave Address(rsSA)>
+ * <Requester Sequence Number/ResponderLUN(rqSeq/rsLUN>
+ * <Command>
+ * <Completion Code(CC)>
+ * <Data[0..n]>
+ * <Data Checksum(dataCksum)>
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <mqueue.h>
+#include <semaphore.h>
+
+#include "facebook/i2c-dev.h"
+#include "openbmc/ipmi.h"
+#include "openbmc/ipmb.h"
+
+//#define DEBUG 0
+
+#define MAX_BYTES 255
+
+#define MQ_IPMB_REQ "/mq_ipmb_req"
+#define MQ_IPMB_RES "/mq_ipmb_res"
+#define MQ_MAX_MSG_SIZE MAX_BYTES
+#define MQ_MAX_NUM_MSGS 10
+
+#define SEQ_NUM_MAX 64
+
+#define I2C_RETRIES_MAX 3
+
+// Structure for i2c file descriptor and socket
+typedef struct _ipmb_sfd_t {
+ int fd;
+ int sock;
+} ipmb_sfd_t;
+
+// Structure for sequence number and buffer
+typedef struct _seq_buf_t {
+ bool in_use; // seq# is being used
+ uint8_t len; // buffer size
+ uint8_t *p_buf; // pointer to buffer
+ sem_t s_seq; // semaphore for thread sync.
+} seq_buf_t;
+
+// Structure for holding currently used sequence number and
+// array of all possible sequence number
+typedef struct _ipmb_sbuf_t {
+ uint8_t curr_seq; // currently used seq#
+ seq_buf_t seq[SEQ_NUM_MAX]; //array of all possible seq# struct.
+} ipmb_sbuf_t;
+
+// Global storage for holding IPMB sequence number and buffer
+ipmb_sbuf_t g_seq;
+
+// mutex to protect global data access
+pthread_mutex_t m_seq;
+
+pthread_mutex_t m_i2c;
+
+#ifdef CONFIG_YOSEMITE
+// Returns the payload ID from IPMB bus routing
+// Slot#1: bus#3, Slot#2: bus#1, Slot#3: bus#7, Slot#4: bus#5
+static uint8_t
+get_payload_id(uint8_t bus_id) {
+ uint8_t payload_id = 0xFF; // Invalid payload ID
+
+ switch(bus_id) {
+ case 1:
+ payload_id = 2;
+ break;
+ case 3:
+ payload_id = 1;
+ break;
+ case 5:
+ payload_id = 4;
+ break;
+ case 7:
+ payload_id = 3;
+ break;
+ default:
+ syslog(LOG_ALERT, "get_payload_id: Wrong bus ID\n");
+ break;
+ }
+
+ return payload_id;
+}
+#endif
+
+// Returns an unused seq# from all possible seq#
+static uint8_t
+seq_get_new(void) {
+ uint8_t ret = -1;
+ uint8_t index;
+
+ pthread_mutex_lock(&m_seq);
+
+ // Search for unused sequence number
+ index = g_seq.curr_seq;
+ do {
+ if (g_seq.seq[index].in_use == false) {
+ // Found it!
+ ret = index;
+ g_seq.seq[index].in_use = true;
+ g_seq.seq[index].len = 0;
+ break;
+ }
+
+ if (++index == SEQ_NUM_MAX) {
+ index = 0;
+ }
+ } while (index != g_seq.curr_seq);
+
+ // Update the current seq num
+ if (ret >= 0) {
+ if (++index == SEQ_NUM_MAX) {
+ index = 0;
+ }
+ g_seq.curr_seq = index;
+ }
+
+ pthread_mutex_unlock(&m_seq);
+
+ return ret;
+}
+
+static int
+i2c_open(uint8_t bus_num) {
+ int fd;
+ char fn[32];
+ int rc;
+
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus_num);
+ fd = open(fn, O_RDWR);
+ if (fd == -1) {
+ syslog(LOG_ALERT, "Failed to open i2c device %s", fn);
+ return -1;
+ }
+
+ rc = ioctl(fd, I2C_SLAVE, BRIDGE_SLAVE_ADDR);
+ if (rc < 0) {
+ syslog(LOG_ALERT, "Failed to open slave @ address 0x%x", BRIDGE_SLAVE_ADDR);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+i2c_write(int fd, uint8_t *buf, uint8_t len) {
+ struct i2c_rdwr_ioctl_data data;
+ struct i2c_msg msg;
+ int rc;
+ int i;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.addr = BRIDGE_SLAVE_ADDR;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = buf;
+
+ data.msgs = &msg;
+ data.nmsgs = 1;
+
+ pthread_mutex_lock(&m_i2c);
+
+ for (i = 0; i < I2C_RETRIES_MAX; i++) {
+ rc = ioctl(fd, I2C_RDWR, &data);
+ if (rc < 0) {
+ sleep(1);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ syslog(LOG_ALERT, "Failed to do raw io");
+ pthread_mutex_unlock(&m_i2c);
+ return -1;
+ }
+
+ pthread_mutex_unlock(&m_i2c);
+
+ return 0;
+}
+
+static int
+i2c_slave_open(uint8_t bus_num) {
+ int fd;
+ char fn[32];
+ int rc;
+ struct i2c_rdwr_ioctl_data data;
+ struct i2c_msg msg;
+ uint8_t read_bytes[MAX_BYTES] = { 0 };
+
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus_num);
+ fd = open(fn, O_RDWR);
+ if (fd == -1) {
+ syslog(LOG_ALERT, "Failed to open i2c device %s", fn);
+ return -1;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.addr = BMC_SLAVE_ADDR;
+ msg.flags = I2C_S_EN;
+ msg.len = 1;
+ msg.buf = read_bytes;
+ msg.buf[0] = 1;
+
+ data.msgs = &msg;
+ data.nmsgs = 1;
+
+ rc = ioctl(fd, I2C_SLAVE_RDWR, &data);
+ if (rc < 0) {
+ syslog(LOG_ALERT, "Failed to open slave @ address 0x%x", BMC_SLAVE_ADDR);
+ close(fd);
+ }
+
+ return fd;
+}
+
+static int
+i2c_slave_read(int fd, uint8_t *buf, uint8_t *len) {
+ struct i2c_rdwr_ioctl_data data;
+ struct i2c_msg msg;
+ int rc;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.addr = BMC_SLAVE_ADDR;
+ msg.flags = 0;
+ msg.len = MAX_BYTES;
+ msg.buf = buf;
+
+ data.msgs = &msg;
+ data.nmsgs = 1;
+
+ rc = ioctl(fd, I2C_SLAVE_RDWR, &data);
+ if (rc < 0) {
+ return -1;
+ }
+
+ *len = msg.len;
+
+ return 0;
+}
+
+// Thread to handle new requests
+static void*
+ipmb_req_handler(void *bus_num) {
+ uint8_t *bnum = (uint8_t*) bus_num;
+ mqd_t mq;
+ int fd;
+ int i;
+
+ //Buffers for IPMB transport
+ uint8_t rxbuf[MQ_MAX_MSG_SIZE] = {0};
+ uint8_t txbuf[MQ_MAX_MSG_SIZE] = {0};
+ ipmb_req_t *p_ipmb_req;
+ ipmb_res_t *p_ipmb_res;
+
+ p_ipmb_req = (ipmb_req_t*) rxbuf;
+ p_ipmb_res = (ipmb_res_t*) txbuf;
+
+ //Buffers for IPMI Stack
+ uint8_t rbuf[MQ_MAX_MSG_SIZE] = {0};
+ uint8_t tbuf[MQ_MAX_MSG_SIZE] = {0};
+ ipmi_mn_req_t *p_ipmi_mn_req;
+ ipmi_res_t *p_ipmi_res;
+
+ p_ipmi_mn_req = (ipmi_mn_req_t*) rbuf;
+ p_ipmi_res = (ipmi_res_t*) tbuf;
+
+ uint8_t rlen = 0;
+ uint8_t tlen = 0;
+
+ char mq_ipmb_req[64] = {0};
+
+ sprintf(mq_ipmb_req, "%s_%d", MQ_IPMB_REQ, *bnum);
+
+ // Open Queue to receive requests
+ mq = mq_open(mq_ipmb_req, O_RDONLY);
+ if (mq == (mqd_t) -1) {
+ return NULL;
+ }
+
+ // Open the i2c bus for sending response
+ fd = i2c_open(*bnum);
+ if (fd < 0) {
+ syslog(LOG_ALERT, "i2c_open failure\n");
+ close(mq);
+ return NULL;
+ }
+
+ // Loop to process incoming requests
+ while (1) {
+ if ((rlen = mq_receive(mq, rxbuf, MQ_MAX_MSG_SIZE, NULL)) < 0) {
+ sleep(1);
+ continue;
+ }
+
+#ifdef DEBUG
+ syslog(LOG_ALERT, "Received Request of %d bytes\n", rlen);
+ for (i = 0; i < rlen; i++) {
+ syslog(LOG_ALERT, "0x%X", rxbuf[i]);
+ }
+#endif
+
+ // Create IPMI request from IPMB data
+#ifdef CONFIG_YOSEMITE
+ p_ipmi_mn_req->payload_id = get_payload_id(*bnum);
+#else
+ // For single node systems use payload ID as 1
+ p_ipmi_mn_req->payload_id = 0x1;
+#endif
+ p_ipmi_mn_req->netfn_lun = p_ipmb_req->netfn_lun;
+ p_ipmi_mn_req->cmd = p_ipmb_req->cmd;
+
+ memcpy(p_ipmi_mn_req->data, p_ipmb_req->data, rlen - IPMB_HDR_SIZE - IPMI_REQ_HDR_SIZE);
+
+ // Send to IPMI stack and get response
+ // Additional byte as we are adding and passing payload ID for MN support
+ lib_ipmi_handle(rbuf, rlen - IPMB_HDR_SIZE + 1, tbuf, &tlen);
+
+ // Populate IPMB response data from IPMB request
+ p_ipmb_res->req_slave_addr = p_ipmb_req->req_slave_addr;
+ p_ipmb_res->res_slave_addr = p_ipmb_req->res_slave_addr;
+ p_ipmb_res->cmd = p_ipmb_req->cmd;
+ p_ipmb_res->seq_lun = p_ipmb_req->seq_lun;
+
+ // Add IPMI response data
+ p_ipmb_res->netfn_lun = p_ipmi_res->netfn_lun;
+ p_ipmb_res->cc = p_ipmi_res->cc;
+
+ memcpy(p_ipmb_res->data, p_ipmi_res->data, tlen - IPMI_RESP_HDR_SIZE);
+
+ // Calculate Header Checksum
+ p_ipmb_res->hdr_cksum = p_ipmb_res->req_slave_addr +
+ p_ipmb_res->netfn_lun;
+ p_ipmb_res->hdr_cksum = ZERO_CKSUM_CONST - p_ipmb_res->hdr_cksum;
+
+ // Calculate Data Checksum
+ p_ipmb_res->data[tlen-IPMI_RESP_HDR_SIZE] = p_ipmb_res->res_slave_addr +
+ p_ipmb_res->seq_lun +
+ p_ipmb_res->cmd +
+ p_ipmb_res->cc;
+
+ for (i = 0; i < tlen-IPMI_RESP_HDR_SIZE; i++) {
+ p_ipmb_res->data[tlen-IPMI_RESP_HDR_SIZE] += p_ipmb_res->data[i];
+ }
+
+ p_ipmb_res->data[tlen-IPMI_RESP_HDR_SIZE] = ZERO_CKSUM_CONST -
+ p_ipmb_res->data[tlen-IPMI_RESP_HDR_SIZE];
+
+#ifdef DEBUG
+ syslog(LOG_ALERT, "Sending Response of %d bytes\n", tlen+IPMB_HDR_SIZE-1);
+ for (i = 1; i < tlen+IPMB_HDR_SIZE; i++) {
+ syslog(LOG_ALERT, "0x%X:", txbuf[i]);
+ }
+#endif
+
+ // Send response back
+ i2c_write(fd, &txbuf[1], tlen+IPMB_HDR_SIZE-1);
+ }
+}
+
+// Thread to handle the incoming responses
+static void*
+ipmb_res_handler(void *bus_num) {
+ uint8_t *bnum = (uint8_t*) bus_num;
+ uint8_t buf[MQ_MAX_MSG_SIZE] = { 0 };
+ uint8_t len = 0;
+ mqd_t mq;
+ ipmb_res_t *p_res;
+ uint8_t index;
+ char mq_ipmb_res[64] = {0};
+
+ sprintf(mq_ipmb_res, "%s_%d", MQ_IPMB_RES, *bnum);
+
+ // Open the message queue
+ mq = mq_open(mq_ipmb_res, O_RDONLY);
+ if (mq == (mqd_t) -1) {
+ syslog(LOG_ALERT, "mq_open fails\n");
+ return NULL;
+ }
+
+ // Loop to wait for incomng response messages
+ while (1) {
+ if ((len = mq_receive(mq, buf, MQ_MAX_MSG_SIZE, NULL)) < 0) {
+ sleep(1);
+ continue;
+ }
+
+ p_res = (ipmb_res_t *) buf;
+
+ // Check the seq# of response
+ index = p_res->seq_lun >> LUN_OFFSET;
+
+ // Check if the response is being waited for
+ pthread_mutex_lock(&m_seq);
+ if (g_seq.seq[index].in_use) {
+ // Copy the response to the requester's buffer
+ memcpy(g_seq.seq[index].p_buf, buf, len);
+ g_seq.seq[index].len = len;
+
+ // Wake up the worker thread to receive the response
+ sem_post(&g_seq.seq[index].s_seq);
+ }
+ pthread_mutex_unlock(&m_seq);
+
+#ifdef DEBUG
+ syslog(LOG_ALERT, "Received Response of %d bytes\n", len);
+ int i;
+ for (i = 0; i < len; i++) {
+ syslog(LOG_ALERT, "0x%X:", buf[i]);
+ }
+#endif
+ }
+}
+
+// Thread to receive the IPMB messages over i2c bus as a slave
+static void*
+ipmb_rx_handler(void *bus_num) {
+ uint8_t *bnum = (uint8_t*) bus_num;
+ int fd;
+ uint8_t len;
+ uint8_t tlun;
+ uint8_t buf[MAX_BYTES] = { 0 };
+ mqd_t mq_req, mq_res, tmq;
+ ipmb_req_t *p_req;
+ struct timespec req;
+ struct timespec rem;
+ char mq_ipmb_req[64] = {0};
+ char mq_ipmb_res[64] = {0};
+
+ // Setup wait time
+ req.tv_sec = 0;
+ req.tv_nsec = 10000000;//10mSec
+
+ // Open the i2c bus as a slave
+ fd = i2c_slave_open(*bnum);
+ if (fd < 0) {
+ syslog(LOG_ALERT, "i2c_slave_open fails\n");
+ goto cleanup;
+ }
+
+ sprintf(mq_ipmb_req, "%s_%d", MQ_IPMB_REQ, *bnum);
+ sprintf(mq_ipmb_res, "%s_%d", MQ_IPMB_RES, *bnum);
+
+ // Open the message queues for post processing
+ mq_req = mq_open(mq_ipmb_req, O_WRONLY);
+ if (mq_req == (mqd_t) -1) {
+ syslog(LOG_ALERT, "mq_open req fails\n");
+ goto cleanup;
+ }
+
+ mq_res = mq_open(mq_ipmb_res, O_WRONLY);
+ if (mq_res == (mqd_t) -1) {
+ syslog(LOG_ALERT, "mq_open res fails\n");
+ goto cleanup;
+ }
+
+ // Loop that retrieves messages
+ while (1) {
+ // Read messages from i2c driver
+ if (i2c_slave_read(fd, buf, &len) < 0) {
+ nanosleep(&req, &rem);
+ continue;
+ }
+
+ // Check if the messages is request or response
+ // Even NetFn: Request, Odd NetFn: Response
+ p_req = (ipmb_req_t*) buf;
+ tlun = p_req->netfn_lun >> LUN_OFFSET;
+ if (tlun%2) {
+ tmq = mq_res;
+ } else {
+ tmq = mq_req;
+ }
+ // Post message to approriate Queue for further processing
+ if (mq_send(tmq, buf, len, 0)) {
+ syslog(LOG_ALERT, "mq_send failed\n");
+ sleep(1);
+ continue;
+ }
+ }
+
+cleanup:
+ if (fd > 0) {
+ close (fd);
+ }
+
+ if (mq_req > 0) {
+ close(mq_req);
+ }
+
+ if (mq_res > 0) {
+ close(mq_req);
+ }
+}
+
+/*
+ * Function to handle all IPMB requests
+ */
+static void
+ipmb_handle (int fd, unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmb_req_t *req = (ipmb_req_t *) request;
+ ipmb_res_t *res = (ipmb_res_t *) response;
+
+ uint8_t index;
+ struct timespec ts;
+
+ // Allocate right sequence Number
+ index = seq_get_new();
+ if (index < 0) {
+ *res_len = 0;
+ return ;
+ }
+
+ req->seq_lun = index << LUN_OFFSET;
+
+ // Calculate/update dataCksum
+ // Note: dataCkSum byte is last byte
+ int i;
+ for (i = IPMB_DATA_OFFSET; i < req_len-1; i++) {
+ request[req_len-1] += request[i];
+ }
+
+ request[req_len-1] = ZERO_CKSUM_CONST - request[req_len-1];
+
+ // Setup response buffer
+ pthread_mutex_lock(&m_seq);
+ g_seq.seq[index].p_buf = response;
+ pthread_mutex_unlock(&m_seq);
+
+ // Send request over i2c bus
+ // Note: Need not send first byte SlaveAddress automatically added by driver
+ if (i2c_write(fd, &request[1], req_len-1)) {
+ goto ipmb_handle_out;
+ }
+
+ // Wait on semaphore for that sequence Number
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ ts.tv_sec += TIMEOUT_IPMI;
+
+ int ret;
+ ret = sem_timedwait(&g_seq.seq[index].s_seq, &ts);
+ if (ret == -1) {
+ syslog(LOG_ALERT, "No response for sequence number: %d\n", index);
+ *res_len = 0;
+ }
+
+ipmb_handle_out:
+ // Reply to user with data
+ pthread_mutex_lock(&m_seq);
+ *res_len = g_seq.seq[index].len;
+
+ g_seq.seq[index].in_use = false;
+ pthread_mutex_unlock(&m_seq);
+
+ return;
+}
+
+void
+*conn_handler(void *sfd) {
+ ipmb_sfd_t *p_sfd = (ipmb_sfd_t *) sfd;
+
+ int sock = p_sfd->sock;
+ int fd = p_sfd->fd;
+ int n;
+ unsigned char req_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_len = 0;
+
+ n = recv(sock, req_buf, sizeof(req_buf), 0);
+ if (n <= 0) {
+ syslog(LOG_ALERT, "ipmbd: recv() failed with %d\n", n);
+ goto conn_cleanup;
+ }
+
+ ipmb_handle(fd, req_buf, n, res_buf, &res_len);
+
+ if (send(sock, res_buf, res_len, MSG_NOSIGNAL) < 0) {
+ syslog(LOG_ALERT, "ipmbd: send() failed\n");
+ }
+
+conn_cleanup:
+ close(sock);
+ free(p_sfd);
+
+ pthread_exit(NULL);
+ return 0;
+}
+
+// Thread to receive the IPMB lib messages from various apps
+static void*
+ipmb_lib_handler(void *bus_num) {
+ int s, s2, t, len;
+ struct sockaddr_un local, remote;
+ pthread_t tid;
+ ipmb_sfd_t *sfd;
+ int fd;
+ uint8_t *bnum = (uint8_t*) bus_num;
+ char sock_path[20] = {0};
+
+ // Open the i2c bus for sending request
+ fd = i2c_open(*bnum);
+ if (fd < 0) {
+ syslog(LOG_ALERT, "i2c_open failure\n");
+ return NULL;
+ }
+
+ // Initialize g_seq structure
+ int i;
+ for (i = 0; i < SEQ_NUM_MAX; i++) {
+ g_seq.seq[i].in_use = false;
+ sem_init(&g_seq.seq[i].s_seq, 0, 0);
+ g_seq.seq[i].len = 0;
+ }
+
+ // Initialize mutex to access global structure
+ pthread_mutex_init(&m_seq, NULL);
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ syslog(LOG_ALERT, "ipmbd: socket() failed\n");
+ exit (1);
+ }
+
+ sprintf(sock_path, "%s_%d", SOCK_PATH_IPMB, *bnum);
+
+ local.sun_family = AF_UNIX;
+ strcpy (local.sun_path, sock_path);
+ unlink (local.sun_path);
+ len = strlen (local.sun_path) + sizeof (local.sun_family);
+ if (bind (s, (struct sockaddr *) &local, len) == -1)
+ {
+ syslog(LOG_ALERT, "ipmbd: bind() failed\n");
+ exit (1);
+ }
+
+ if (listen (s, 5) == -1)
+ {
+ syslog(LOG_ALERT, "ipmbd: listen() failed\n");
+ exit (1);
+ }
+
+ while(1) {
+ int n;
+ t = sizeof (remote);
+ if ((s2 = accept (s, (struct sockaddr *) &remote, &t)) < 0) {
+ syslog(LOG_ALERT, "ipmbd: accept() failed\n");
+ break;
+ }
+
+ // Creating a worker thread to handle the request
+ // TODO: Need to monitor the server performance with higher load and
+ // see if we need to create pre-defined number of workers and schedule
+ // the requests among them.
+ sfd = (ipmb_sfd_t *) malloc(sizeof(ipmb_sfd_t));
+ sfd->fd = fd;
+ sfd->sock = s2;
+ if (pthread_create(&tid, NULL, conn_handler, (void*) sfd) < 0) {
+ syslog(LOG_ALERT, "ipmbd: pthread_create failed\n");
+ close(s2);
+ continue;
+ }
+
+ pthread_detach(tid);
+ }
+
+ close(s);
+ pthread_mutex_destroy(&m_seq);
+
+ return 0;
+}
+
+int
+main(int argc, char * const argv[]) {
+ pthread_t tid_ipmb_rx;
+ pthread_t tid_req_handler;
+ pthread_t tid_res_handler;
+ pthread_t tid_lib_handler;
+ uint8_t ipmb_bus_num;
+ mqd_t mqd_req, mqd_res;
+ struct mq_attr attr;
+ char mq_ipmb_req[64] = {0};
+ char mq_ipmb_res[64] = {0};
+
+ daemon(1, 0);
+ openlog("ipmbd", LOG_CONS, LOG_DAEMON);
+
+ if (argc != 2) {
+ syslog(LOG_ALERT, "ipmbd: Usage: ipmbd <bus#>");
+ exit(1);
+ }
+
+ ipmb_bus_num = atoi(argv[1]);
+syslog(LOG_ALERT, "ipmbd: bus#:%d\n", ipmb_bus_num);
+
+ pthread_mutex_init(&m_i2c, NULL);
+
+ // Create Message Queues for Request Messages and Response Messages
+ attr.mq_flags = 0;
+ attr.mq_maxmsg = MQ_MAX_NUM_MSGS;
+ attr.mq_msgsize = MQ_MAX_MSG_SIZE;
+ attr.mq_curmsgs = 0;
+
+ sprintf(mq_ipmb_req, "%s_%d", MQ_IPMB_REQ, ipmb_bus_num);
+ sprintf(mq_ipmb_res, "%s_%d", MQ_IPMB_RES, ipmb_bus_num);
+
+ // Remove the MQ if exists
+ mq_unlink(mq_ipmb_req);
+
+ errno = 0;
+ mqd_req = mq_open(mq_ipmb_req, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &attr);
+ if (mqd_req == (mqd_t) -1) {
+ syslog(LOG_ALERT, "ipmbd: mq_open request failed errno:%d\n", errno);
+ goto cleanup;
+ }
+
+ // Remove the MQ if exists
+ mq_unlink(mq_ipmb_res);
+
+ errno = 0;
+ mqd_res = mq_open(mq_ipmb_res, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &attr);
+ if (mqd_res == (mqd_t) -1) {
+ syslog(LOG_ALERT, "ipmbd: mq_open response failed errno: %d\n", errno);
+ goto cleanup;
+ }
+
+ // Create thread to handle IPMB Requests
+ if (pthread_create(&tid_req_handler, NULL, ipmb_req_handler, (void*) &ipmb_bus_num) < 0) {
+ syslog(LOG_ALERT, "ipmbd: pthread_create failed\n");
+ goto cleanup;
+ }
+
+ // Create thread to handle IPMB Responses
+ if (pthread_create(&tid_res_handler, NULL, ipmb_res_handler, (void*) &ipmb_bus_num) < 0) {
+ syslog(LOG_ALERT, "ipmbd: pthread_create failed\n");
+ goto cleanup;
+ }
+
+ // Create thread to retrieve ipmb traffic from i2c bus as slave
+ if (pthread_create(&tid_ipmb_rx, NULL, ipmb_rx_handler, (void*) &ipmb_bus_num) < 0) {
+ syslog(LOG_ALERT, "ipmbd: pthread_create failed\n");
+ goto cleanup;
+ }
+
+ // Create thread to receive ipmb library requests from apps
+ if (pthread_create(&tid_lib_handler, NULL, ipmb_lib_handler, (void*) &ipmb_bus_num) < 0) {
+ syslog(LOG_ALERT, "ipmbd: pthread_create failed\n");
+ goto cleanup;
+ }
+
+cleanup:
+ if (tid_ipmb_rx > 0) {
+ pthread_join(tid_ipmb_rx, NULL);
+ }
+
+ if (tid_req_handler > 0) {
+ pthread_join(tid_req_handler, NULL);
+ }
+
+ if (tid_res_handler > 0) {
+ pthread_join(tid_res_handler, NULL);
+ }
+
+ if (tid_lib_handler > 0) {
+ pthread_join(tid_lib_handler, NULL);
+ }
+
+ if (mqd_res > 0) {
+ mq_close(mqd_res);
+ mq_unlink(mq_ipmb_res);
+ }
+
+ if (mqd_req > 0) {
+ mq_close(mqd_req);
+ mq_unlink(mq_ipmb_req);
+ }
+
+ pthread_mutex_destroy(&m_i2c);
+
+ return 0;
+}
diff --git a/common/recipes-core/ipmbd/ipmbd_0.1.bb b/common/recipes-core/ipmbd/ipmbd_0.1.bb
new file mode 100644
index 0000000..f5724b0
--- /dev/null
+++ b/common/recipes-core/ipmbd/ipmbd_0.1.bb
@@ -0,0 +1,19 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+
+SUMMARY = "ipmbd tx/rx daemon"
+DESCRIPTION = "The ipmb daemon to receive/transmit messages"
+SECTION = "base"
+PR = "r2"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://ipmbd.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://Makefile \
+ file://ipmbd.c \
+ "
+
+S = "${WORKDIR}"
+DEPENDS += "libipmi libipmb"
+
+binfiles = "ipmbd"
+
+pkgdir = "ipmbd"
diff --git a/common/recipes-core/ipmid/files/Makefile b/common/recipes-core/ipmid/files/Makefile
new file mode 100644
index 0000000..4f0db3d
--- /dev/null
+++ b/common/recipes-core/ipmid/files/Makefile
@@ -0,0 +1,29 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+C_SRCS := $(wildcard *.c)
+C_OBJS := ${C_SRCS:.c=.o}
+
+all: ipmid
+
+ipmid: $(C_OBJS)
+ $(CC) -pthread -lpal -std=c99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o ipmid
diff --git a/common/recipes-core/ipmid/files/fruid.h b/common/recipes-core/ipmid/files/fruid.h
new file mode 100644
index 0000000..3580b08
--- /dev/null
+++ b/common/recipes-core/ipmid/files/fruid.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FRUID_H__
+#define __FRUID_H__
+
+int plat_fruid_size(void);
+int plat_fruid_data(int offset, int count, unsigned char *data);
+int plat_fruid_init(void);
+
+#endif /* __FRUID_H__ */
diff --git a/common/recipes-core/ipmid/files/ipmid.c b/common/recipes-core/ipmid/files/ipmid.c
new file mode 100644
index 0000000..c79d3e2
--- /dev/null
+++ b/common/recipes-core/ipmid/files/ipmid.c
@@ -0,0 +1,1412 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file contains code to support IPMI2.0 Specificaton available @
+ * http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sdr.h"
+#include "sel.h"
+#include "fruid.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <openbmc/ipmi.h>
+
+// TODO: Once data storage is finalized, the following structure needs
+// to be retrieved/updated from persistant backend storage
+static lan_config_t g_lan_config = { 0 };
+static proc_info_t g_proc_info = { 0 };
+static dimm_info_t g_dimm_info[MAX_NUM_DIMMS] = { 0 };
+
+// TODO: Need to store this info after identifying proper storage
+static sys_info_param_t g_sys_info_params;
+
+// TODO: Based on performance testing results, might need fine grained locks
+// Since the global data is specific to a NetFunction, adding locs at NetFn level
+static pthread_mutex_t m_chassis;
+static pthread_mutex_t m_app;
+static pthread_mutex_t m_storage;
+static pthread_mutex_t m_transport;
+static pthread_mutex_t m_oem;
+static pthread_mutex_t m_oem_1s;
+
+static void ipmi_handle(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len);
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Chassis
+ */
+// Get Chassis Status (IPMI/Section 28.2)
+static void
+chassis_get_status (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ // TODO: Need to obtain current power state and last power event
+ // from platform and return
+ *data++ = 0x01; // Current Power State
+ *data++ = 0x00; // Last Power Event
+ *data++ = 0x40; // Misc. Chassis Status
+ *data++ = 0x00; // Front Panel Button Disable
+
+ res_len = data - &res->data[0];
+}
+
+// Get System Boot Options (IPMI/Section 28.12)
+static void
+chassis_get_boot_options (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res= (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[0];
+
+ // Fill response with default values
+ res->cc = CC_SUCCESS;
+ *data++ = 0x01; // Parameter Version
+ *data++ = req->data[0]; // Parameter
+
+ // TODO: Need to store user settings and return
+ switch (param)
+ {
+ case PARAM_SET_IN_PROG:
+ *data++ = 0x00; // Set In Progress
+ break;
+ case PARAM_SVC_PART_SELECT:
+ *data++ = 0x00; // Service Partition Selector
+ break;
+ case PARAM_SVC_PART_SCAN:
+ *data++ = 0x00; // Service Partition Scan
+ break;
+ case PARAM_BOOT_FLAG_CLR:
+ *data++ = 0x00; // BMC Boot Flag Valid Bit Clear
+ break;
+ case PARAM_BOOT_INFO_ACK:
+ *data++ = 0x00; // Write Mask
+ *data++ = 0x00; // Boot Initiator Ack Data
+ break;
+ case PARAM_BOOT_FLAGS:
+ *data++ = 0x00; // Boot Flags
+ *data++ = 0x00; // Boot Device Selector
+ *data++ = 0x00; // Firmwaer Verbosity
+ *data++ = 0x00; // BIOS Override
+ *data++ = 0x00; // Device Instance Selector
+ break;
+ case PARAM_BOOT_INIT_INFO:
+ *data++ = 0x00; // Chanel Number
+ *data++ = 0x00; // Session ID (4 bytes)
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00; // Boot Info Timestamp (4 bytes)
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00;
+ break;
+ deault:
+ res->cc = CC_PARAM_OUT_OF_RANGE;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+}
+
+// Handle Chassis Commands (IPMI/Section 28)
+static void
+ipmi_handle_chassis (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_chassis);
+ switch (cmd)
+ {
+ case CMD_CHASSIS_GET_STATUS:
+ chassis_get_status (response, res_len);
+ break;
+ case CMD_CHASSIS_GET_BOOT_OPTIONS:
+ chassis_get_boot_options (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_chassis);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Application
+ */
+// Get Device ID (IPMI/Section 20.1)
+static void
+app_get_device_id (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ //TODO: Following data needs to be updated based on platform
+ *data++ = 0x20; // Device ID
+ *data++ = 0x81; // Device Revision
+ *data++ = 0x00; // Firmware Revision Major
+ *data++ = 0x09; // Firmware Revision Minor
+ *data++ = 0x02; // IPMI Version
+ *data++ = 0xBF; // Additional Device Support
+ *data++ = 0x15; // Manufacturer ID1
+ *data++ = 0xA0; // Manufacturer ID2
+ *data++ = 0x00; // Manufacturer ID3
+ *data++ = 0x46; // Product ID1
+ *data++ = 0x31; // Product ID2
+ *data++ = 0x00; // Aux. Firmware Version1
+ *data++ = 0x00; // Aux. Firmware Version2
+ *data++ = 0x00; // Aux. Firmware Version3
+ *data++ = 0x00; // Aux. Firmware Version4
+
+ *res_len = data - &res->data[0];
+}
+
+// Get Self Test Results (IPMI/Section 20.4)
+static void
+app_get_selftest_results (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ //TODO: Following data needs to be updated based on self-test results
+ *data++ = 0x55; // Self-Test result
+ *data++ = 0x00; // Extra error info in case of failure
+
+ *res_len = data - &res->data[0];
+}
+
+// Get Device GUID (IPMI/Section 20.8)
+static void
+app_get_device_guid (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = 0x00;
+
+ // TODO: Following data is Globaly Unique ID i.e. MAC Address..
+ *data++ = 0x0;
+ *data++ = 0x1;
+ *data++ = 0x2;
+ *data++ = 0x3;
+ *data++ = 0x4;
+ *data++ = 0x5;
+ *data++ = 0x6;
+ *data++ = 0x7;
+ *data++ = 0x8;
+ *data++ = 0x9;
+ *data++ = 0xa;
+ *data++ = 0xb;
+ *data++ = 0xc;
+ *data++ = 0xd;
+ *data++ = 0xe;
+ *data++ = 0xf;
+
+ *res_len = data - &res->data[0];
+}
+
+// Get BMC Global Enables (IPMI/Section 22.2)
+static void
+app_get_global_enables (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = 0x09; // Global Enable
+
+ *res_len = data - &res->data[0];
+}
+
+// Set System Info Params (IPMI/Section 22.14a)
+static void
+app_set_sys_info_params (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char param = req->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ switch (param)
+ {
+ case SYS_INFO_PARAM_SET_IN_PROG:
+ g_sys_info_params.set_in_prog = req->data[1];
+ break;
+ case SYS_INFO_PARAM_SYSFW_VER:
+ memcpy(g_sys_info_params.sysfw_ver, &req->data[1], SIZE_SYSFW_VER);
+ break;
+ case SYS_INFO_PARAM_SYS_NAME:
+ memcpy(g_sys_info_params.sys_name, &req->data[1], SIZE_SYS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRI_OS_NAME:
+ memcpy(g_sys_info_params.pri_os_name, &req->data[1], SIZE_OS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_NAME:
+ memcpy(g_sys_info_params.present_os_name, &req->data[1], SIZE_OS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_VER:
+ memcpy(g_sys_info_params.present_os_ver, &req->data[1], SIZE_OS_VER);
+ break;
+ case SYS_INFO_PARAM_BMC_URL:
+ memcpy(g_sys_info_params.bmc_url, &req->data[1], SIZE_BMC_URL);
+ break;
+ case SYS_INFO_PARAM_OS_HV_URL:
+ memcpy(g_sys_info_params.os_hv_url, &req->data[1], SIZE_OS_HV_URL);
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ return;
+}
+
+// Get System Info Params (IPMI/Section 22.14b)
+static void
+app_get_sys_info_params (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[1];
+
+ // Fill default return values
+ res->cc = CC_SUCCESS;
+ *data++ = 1; // Parameter revision
+
+ switch (param)
+ {
+ case SYS_INFO_PARAM_SET_IN_PROG:
+ *data++ = g_sys_info_params.set_in_prog;
+ break;
+ case SYS_INFO_PARAM_SYSFW_VER:
+ memcpy(data, g_sys_info_params.sysfw_ver, SIZE_SYSFW_VER);
+ data += SIZE_SYSFW_VER;
+ break;
+ case SYS_INFO_PARAM_SYS_NAME:
+ memcpy(data, g_sys_info_params.sys_name, SIZE_SYS_NAME);
+ data += SIZE_SYS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRI_OS_NAME:
+ memcpy(data, g_sys_info_params.pri_os_name, SIZE_OS_NAME);
+ data += SIZE_OS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_NAME:
+ memcpy(data, g_sys_info_params.present_os_name, SIZE_OS_NAME);
+ data += SIZE_OS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_VER:
+ memcpy(data, g_sys_info_params.present_os_ver, SIZE_OS_VER);
+ data += SIZE_OS_VER;
+ break;
+ case SYS_INFO_PARAM_BMC_URL:
+ memcpy(data, g_sys_info_params.bmc_url, SIZE_BMC_URL);
+ data += SIZE_BMC_URL;
+ break;
+ case SYS_INFO_PARAM_OS_HV_URL:
+ memcpy(data, g_sys_info_params.os_hv_url, SIZE_OS_HV_URL);
+ data += SIZE_OS_HV_URL;
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+
+ return;
+}
+
+// Handle Appliction Commands (IPMI/Section 20)
+static void
+ipmi_handle_app (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_app);
+ switch (cmd)
+ {
+ case CMD_APP_GET_DEVICE_ID:
+ app_get_device_id (response, res_len);
+ break;
+ case CMD_APP_GET_SELFTEST_RESULTS:
+ app_get_selftest_results (response, res_len);
+ break;
+ case CMD_APP_GET_DEVICE_GUID:
+ case CMD_APP_GET_SYSTEM_GUID:
+ // Get Device GUID and Get System GUID returns same data
+ // from IPMI stack. FYI, Get System GUID will have to be
+ // sent with in an IPMI session that includes session info
+ app_get_device_guid (response, res_len);
+ break;
+ case CMD_APP_GET_GLOBAL_ENABLES:
+ app_get_global_enables (response, res_len);
+ break;
+ case CMD_APP_SET_SYS_INFO_PARAMS:
+ app_set_sys_info_params (request, response, res_len);
+ break;
+ case CMD_APP_GET_SYS_INFO_PARAMS:
+ app_get_sys_info_params (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_app);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Storage
+ */
+
+static void
+storage_get_fruid_info(unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int size = plat_fruid_size();
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = size & 0xFF; // FRUID size LSB
+ *data++ = (size >> 8) & 0xFF; // FRUID size MSB
+ *data++ = 0x00; // Device accessed by bytes
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_fruid_data(unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int offset = req->data[1] + (req->data[2] << 8);
+ int count = req->data[3];
+
+ int ret = plat_fruid_data(offset, count, &(res->data[1]));
+ if (ret) {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ } else {
+ res->cc = CC_SUCCESS;
+ *data++ = count;
+ data += count;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+ return;
+}
+
+static void
+storage_get_sdr_info (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int num_entries; // number of sdr records
+ int free_space; // free space in SDR device in bytes
+ time_stamp_t ts_recent_add; // Recent Addition Timestamp
+ time_stamp_t ts_recent_erase; // Recent Erasure Timestamp
+
+ // Use platform APIs to get SDR information
+ num_entries = sdr_num_entries ();
+ free_space = sdr_free_space ();
+ sdr_ts_recent_add (&ts_recent_add);
+ sdr_ts_recent_erase (&ts_recent_erase);
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = IPMI_SDR_VERSION; // SDR version
+ *data++ = num_entries & 0xFF; // number of sdr entries
+ *data++ = (num_entries >> 8) & 0xFF;
+ *data++ = free_space & 0xFF; // Free SDR Space
+ *data++ = (free_space >> 8) & 0xFF;
+
+ memcpy(data, ts_recent_add.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ memcpy(data, ts_recent_erase.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ *data++ = 0x02; // Operations supported
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_rsv_sdr (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int rsv_id; // SDR reservation ID
+
+ // Use platform APIs to get a SDR reservation ID
+ rsv_id = sdr_rsv_id ();
+ if (rsv_id < 0)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = rsv_id & 0xFF; // Reservation ID
+ *data++ = (rsv_id >> 8) & 0XFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sdr (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int read_rec_id; //record ID to be read
+ int next_rec_id; //record ID for the next entry
+ int rsv_id; // Reservation ID for the request
+ int rec_offset; // Read offset into the record
+ int rec_bytes; // Number of bytes to be read
+ sdr_rec_t entry; // SDR record entry
+ int ret;
+
+ rsv_id = (req->data[1] >> 8) | req->data[0];
+ read_rec_id = (req->data[3] >> 8) | req->data[2];
+ rec_offset = req->data[4];
+ rec_bytes = req->data[5];
+
+ // Use platform API to read the record Id and get next ID
+ ret = sdr_get_entry (rsv_id, read_rec_id, &entry, &next_rec_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = next_rec_id & 0xFF; // next record ID
+ *data++ = (next_rec_id >> 8) & 0xFF;
+
+ memcpy (data, &entry.rec[rec_offset], rec_bytes);
+ data += rec_bytes;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sel_info (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int num_entries; // number of log entries
+ int free_space; // free space in SEL device in bytes
+ time_stamp_t ts_recent_add; // Recent Addition Timestamp
+ time_stamp_t ts_recent_erase; // Recent Erasure Timestamp
+
+ // Use platform APIs to get SEL information
+ num_entries = sel_num_entries ();
+ free_space = sel_free_space ();
+ sel_ts_recent_add (&ts_recent_add);
+ sel_ts_recent_erase (&ts_recent_erase);
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = IPMI_SEL_VERSION; // SEL version
+ *data++ = num_entries & 0xFF; // number of log entries
+ *data++ = (num_entries >> 8) & 0xFF;
+ *data++ = free_space & 0xFF; // Free SEL Space
+ *data++ = (free_space >> 8) & 0xFF;
+
+ memcpy(data, ts_recent_add.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ memcpy(data, ts_recent_erase.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ *data++ = 0x02; // Operations supported
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_rsv_sel (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int rsv_id; // SEL reservation ID
+
+ // Use platform APIs to get a SEL reservation ID
+ rsv_id = sel_rsv_id ();
+ if (rsv_id < 0)
+ {
+ res->cc = CC_SEL_ERASE_PROG;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = rsv_id & 0xFF; // Reservation ID
+ *data++ = (rsv_id >> 8) & 0XFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int read_rec_id; //record ID to be read
+ int next_rec_id; //record ID for the next msg
+ sel_msg_t entry; // SEL log entry
+ int ret;
+
+ read_rec_id = (req->data[3] >> 8) | req->data[2];
+
+ // Use platform API to read the record Id and get next ID
+ ret = sel_get_entry (read_rec_id, &entry, &next_rec_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = next_rec_id & 0xFF; // next record ID
+ *data++ = (next_rec_id >> 8) & 0xFF;
+
+ memcpy(data, entry.msg, SIZE_SEL_REC);
+ data += SIZE_SEL_REC;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_add_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+
+ int record_id; // Record ID for added entry
+ int ret;
+
+ sel_msg_t entry;
+
+ memcpy(entry.msg, req->data, SIZE_SEL_REC);
+
+ // Use platform APIs to add the new SEL entry
+ ret = sel_add_entry (&entry, &record_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = record_id & 0xFF;
+ *data++ = (record_id >> 8) & 0xFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_clr_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ sel_erase_stat_t status;
+ int ret;
+ int rsv_id;
+
+ // Verify the request to contain 'CLR' characters
+ if ((req->data[2] != 'C') || (req->data[3] != 'L') || (req->data[4] != 'R'))
+ {
+ res->cc = CC_INVALID_PARAM;
+ return;
+ }
+
+ // Populate reservation ID given in request
+ rsv_id = (req->data[1] << 8) | req->data[0];
+
+ // Use platform APIs to clear or get status
+ if (req->data[5] == IPMI_SEL_INIT_ERASE)
+ {
+ ret = sel_erase (rsv_id);
+ }
+ else if (req->data[5] == IPMI_SEL_ERASE_STAT)
+ {
+ ret = sel_erase_status (rsv_id, &status);
+ }
+ else
+ {
+ res->cc = CC_INVALID_PARAM;
+ return;
+ }
+
+ // Handle platform error and return
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = status;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+ipmi_handle_storage (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+
+ pthread_mutex_lock(&m_storage);
+ switch (cmd)
+ {
+ case CMD_STORAGE_GET_FRUID_INFO:
+ storage_get_fruid_info (response, res_len);
+ break;
+ case CMD_STORAGE_READ_FRUID_DATA:
+ storage_get_fruid_data (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SEL_INFO:
+ storage_get_sel_info (response, res_len);
+ break;
+ case CMD_STORAGE_RSV_SEL:
+ storage_rsv_sel (response, res_len);
+ break;
+ case CMD_STORAGE_ADD_SEL:
+ storage_add_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SEL:
+ storage_get_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_CLR_SEL:
+ storage_clr_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SDR_INFO:
+ storage_get_sdr_info (response, res_len);
+ break;
+ case CMD_STORAGE_RSV_SDR:
+ storage_rsv_sdr (response, res_len);
+ break;
+ case CMD_STORAGE_GET_SDR:
+ storage_get_sdr (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+
+ pthread_mutex_unlock(&m_storage);
+ return;
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Transport
+ */
+
+// Set LAN Configuration (IPMI/Section 23.1)
+static void
+transport_set_lan_config (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char param = req->data[1];
+
+ // Fill the response with default values
+ res->cc = CC_SUCCESS;
+
+ switch (param)
+ {
+ case LAN_PARAM_SET_IN_PROG:
+ g_lan_config.set_in_prog = req->data[2];
+ break;
+ case LAN_PARAM_AUTH_SUPPORT:
+ g_lan_config.auth_support = req->data[2];
+ break;
+ case LAN_PARAM_AUTH_ENABLES:
+ memcpy(g_lan_config.auth_enables, &req->data[2], SIZE_AUTH_ENABLES);
+ break;
+ case LAN_PARAM_IP_ADDR:
+ memcpy(g_lan_config.ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_IP_SRC:
+ g_lan_config.ip_src = req->data[2];
+ break;
+ case LAN_PARAM_MAC_ADDR:
+ memcpy(g_lan_config.mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_NET_MASK:
+ memcpy(g_lan_config.net_mask, &req->data[2], SIZE_NET_MASK);
+ break;
+ case LAN_PARAM_IP_HDR:
+ memcpy(g_lan_config.ip_hdr, &req->data[2], SIZE_IP_HDR);
+ break;
+ case LAN_PARAM_PRI_RMCP_PORT:
+ g_lan_config.pri_rmcp_port[0] = req->data[2];
+ g_lan_config.pri_rmcp_port[1] = req->data[3];
+ break;
+ case LAN_PARAM_SEC_RMCP_PORT:
+ g_lan_config.sec_rmcp_port[0] = req->data[2];
+ g_lan_config.sec_rmcp_port[1] = req->data[3];
+ break;
+ case LAN_PARAM_ARP_CTRL:
+ g_lan_config.arp_ctrl = req->data[2];
+ break;
+ case LAN_PARAM_GARP_INTERVAL:
+ g_lan_config.garp_interval = req->data[2];
+ break;
+ case LAN_PARAM_DF_GW_IP_ADDR:
+ memcpy(g_lan_config.df_gw_ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_DF_GW_MAC_ADDR:
+ memcpy(g_lan_config.df_gw_mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_BACK_GW_IP_ADDR:
+ memcpy(g_lan_config.back_gw_ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_BACK_GW_MAC_ADDR:
+ memcpy(g_lan_config.back_gw_mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_COMMUNITY_STR:
+ memcpy(g_lan_config.community_str, &req->data[2], SIZE_COMMUNITY_STR);
+ break;
+ case LAN_PARAM_NO_OF_DEST:
+ g_lan_config.no_of_dest = req->data[2];
+ break;
+ case LAN_PARAM_DEST_TYPE:
+ memcpy(g_lan_config.dest_type, &req->data[2], SIZE_DEST_TYPE);
+ break;
+ case LAN_PARAM_DEST_ADDR:
+ memcpy(g_lan_config.dest_addr, &req->data[2], SIZE_DEST_ADDR);
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+}
+
+// Get LAN Configuration (IPMI/Section 23.2)
+static void
+transport_get_lan_config (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[1];
+
+ // Fill the response with default values
+ res->cc = CC_SUCCESS;
+ *data++ = 0x01; // Parameter revision
+
+ switch (param)
+ {
+ case LAN_PARAM_SET_IN_PROG:
+ *data++ = g_lan_config.set_in_prog;
+ break;
+ case LAN_PARAM_AUTH_SUPPORT:
+ *data++ = g_lan_config.auth_support;
+ break;
+ case LAN_PARAM_AUTH_ENABLES:
+ memcpy(data, g_lan_config.auth_enables, SIZE_AUTH_ENABLES);
+ data += SIZE_AUTH_ENABLES;
+ break;
+ case LAN_PARAM_IP_ADDR:
+ memcpy(data, g_lan_config.ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_IP_SRC:
+ *data++ = g_lan_config.ip_src;
+ break;
+ case LAN_PARAM_MAC_ADDR:
+ memcpy(data, g_lan_config.mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_NET_MASK:
+ memcpy(data, g_lan_config.net_mask, SIZE_NET_MASK);
+ data += SIZE_NET_MASK;
+ break;
+ case LAN_PARAM_IP_HDR:
+ memcpy(data, g_lan_config.ip_hdr, SIZE_IP_HDR);
+ data += SIZE_IP_HDR;
+ break;
+ case LAN_PARAM_PRI_RMCP_PORT:
+ *data++ = g_lan_config.pri_rmcp_port[0];
+ *data++ = g_lan_config.pri_rmcp_port[1];
+ break;
+ case LAN_PARAM_SEC_RMCP_PORT:
+ *data++ = g_lan_config.sec_rmcp_port[0];
+ *data++ = g_lan_config.sec_rmcp_port[1];
+ break;
+ case LAN_PARAM_ARP_CTRL:
+ *data++ = g_lan_config.arp_ctrl;
+ break;
+ case LAN_PARAM_GARP_INTERVAL:
+ *data++ = g_lan_config.garp_interval;
+ break;
+ case LAN_PARAM_DF_GW_IP_ADDR:
+ memcpy(data, g_lan_config.df_gw_ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_DF_GW_MAC_ADDR:
+ memcpy(data, g_lan_config.df_gw_mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_BACK_GW_IP_ADDR:
+ memcpy(data, g_lan_config.back_gw_ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_BACK_GW_MAC_ADDR:
+ memcpy(data, g_lan_config.back_gw_mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_COMMUNITY_STR:
+ memcpy(data, g_lan_config.community_str, SIZE_COMMUNITY_STR);
+ data += SIZE_COMMUNITY_STR;
+ break;
+ case LAN_PARAM_NO_OF_DEST:
+ *data++ = g_lan_config.no_of_dest;
+ break;
+ case LAN_PARAM_DEST_TYPE:
+ memcpy(data, g_lan_config.dest_type, SIZE_DEST_TYPE);
+ data += SIZE_DEST_TYPE;
+ break;
+ case LAN_PARAM_DEST_ADDR:
+ memcpy(data, g_lan_config.dest_addr, SIZE_DEST_ADDR);
+ data += SIZE_DEST_ADDR;
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+}
+
+// Handle Transport Commands (IPMI/Section 23)
+static void
+ipmi_handle_transport (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_transport);
+ switch (cmd)
+ {
+ case CMD_TRANSPORT_SET_LAN_CONFIG:
+ transport_set_lan_config (request, response, res_len);
+ break;
+ case CMD_TRANSPORT_GET_LAN_CONFIG:
+ transport_get_lan_config (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_transport);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: OEM
+ */
+
+static void
+oem_set_proc_info (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ g_proc_info.type = req->data[1];
+ g_proc_info.freq[0] = req->data[2];
+ g_proc_info.freq[1] = req->data[3];
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_dimm_info (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ unsigned char index = req->data[0];
+
+ g_dimm_info[index].type = req->data[1];
+ g_dimm_info[index].speed[0] = req->data[2];
+ g_dimm_info[index].speed[1] = req->data[3];
+ g_dimm_info[index].size[0] = req->data[4];
+ g_dimm_info[index].size[1] = req->data[5];
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_post_start (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // TODO: For now logging the event, need to find usage for this info
+ syslog (LOG_INFO, "POST Start Event for Payload#%d\n", req->payload_id);
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_post_end (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // TODO: For now logging the event, need to find usage for this info
+ syslog (LOG_INFO, "POST End Event for Payload#%d\n", req->payload_id);
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+ipmi_handle_oem (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_oem);
+ switch (cmd)
+ {
+ case CMD_OEM_SET_PROC_INFO:
+ oem_set_proc_info (request, response, res_len);
+ break;
+ case CMD_OEM_SET_DIMM_INFO:
+ oem_set_dimm_info (request, response, res_len);
+ break;
+ case CMD_OEM_SET_POST_START:
+ oem_set_post_start (request, response, res_len);
+ break;
+ case CMD_OEM_SET_POST_END:
+ oem_set_post_end (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_oem);
+}
+
+static void
+oem_1s_handle_ipmb_kcs(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // Need to extract bridged IPMI command and handle
+ unsigned char req_buf[MAX_IPMI_MSG_SIZE] = {0};
+ unsigned char res_buf[MAX_IPMI_MSG_SIZE] = {0};
+
+ // Add the payload id from the bridged command
+ req_buf[0] = req->payload_id;
+
+ // Remove OEM IPMI Header + 1 byte for BIC interface
+ // The offset moves by one due to the payload ID
+ memcpy(&req_buf[1], &request[BIC_INTF_HDR_SIZE + 1], req_len - BIC_INTF_HDR_SIZE);
+
+ // Send the bridged KCS command along with the payload ID
+ // The offset moves by one due to the payload ID
+ ipmi_handle(req_buf, req_len - BIC_INTF_HDR_SIZE + 1, res_buf, res_len);
+
+ // Copy the response back
+ memcpy(&res->data[1], res_buf, *res_len);
+
+ // Add the OEM command's response
+ res->cc = CC_SUCCESS;
+ res->data[0] = req->data[0]; // Bridge-IC interface
+ *res_len += 1;
+}
+
+static void
+oem_1s_handle_ipmb_req(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // handle based on Bridge-IC interface
+ switch(req->data[0]) {
+ case BIC_INTF_ME:
+ // TODO: Need to call ME command handler
+ syslog(LOG_INFO, "oem_1s_handle_ipmb_req: Command received from ME for "
+ "payload#%d\n", req->payload_id);
+ res->data[0] = BIC_INTF_ME;
+ res->cc = CC_SUCCESS;
+ *res_len = 1;
+ break;
+ case BIC_INTF_SOL:
+ // TODO: Need to call Optional SoL message handler
+ syslog(LOG_INFO, "oem_1s_handle_ipmb_req: Command received from SOL for "
+ "payload#%d\n", req->payload_id);
+ res->data[0] = BIC_INTF_SOL;
+ res->cc = CC_SUCCESS;
+ *res_len = 1;
+ break;
+ case BIC_INTF_KCS:
+ oem_1s_handle_ipmb_kcs(request, req_len, response, res_len);
+ break;
+ default:
+ // TODO: Need to add additonal interface handler, if supported
+ syslog(LOG_ALERT, "oem_1s_handle_ipmb_req: Command received on intf#%d "
+ "for payload#%d", req->data[0], req->payload_id);
+ res->cc = CC_INVALID_PARAM;
+ *res_len = 0;
+ break;
+ }
+}
+
+static void
+ipmi_handle_oem_1s(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ int i;
+
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_oem_1s);
+ switch (cmd)
+ {
+ case CMD_OEM_1S_MSG_IN:
+ oem_1s_handle_ipmb_req(request, req_len, response, res_len);
+ break;
+ case CMD_OEM_1S_INTR:
+ syslog(LOG_INFO, "ipmi_handle_oem_1s: 1S server interrupt#%d received "
+ "for payload#%d\n", req->data[0], req->payload_id);
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+ break;
+ case CMD_OEM_1S_POST_BUF:
+ for (i = 1; i <= req->data[0]; i++) {
+ pal_post_handle(req->payload_id, req->data[i]);
+ }
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+ break;
+ case CMD_OEM_1S_PLAT_DISC:
+ syslog(LOG_INFO, "ipmi_handle_oem_1s: Platform Discovery received for "
+ "payload#%d\n", req->payload_id);
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+ break;
+ case CMD_OEM_1S_BIC_RESET:
+ syslog(LOG_INFO, "ipmi_handle_oem_1s: BIC Reset received "
+ "for payload#%d\n", req->payload_id);
+
+ if (req->data[0] == 0x0) {
+ syslog(LOG_ALERT, "Cold Reset by Firmware Update\n");
+ res->cc = CC_SUCCESS;
+ } else if (req->data[1] == 0x01) {
+ syslog(LOG_ALERT, "WDT Reset\n");
+ res->cc = CC_SUCCESS;
+ } else {
+ syslog(LOG_ALERT, "Error\n");
+ res->cc = CC_INVALID_PARAM;
+ }
+
+ *res_len = 0;
+ break;
+ case CMD_OEM_1S_BIC_UPDATE_MODE:
+ syslog(LOG_INFO, "ipmi_handle_oem_1s: BIC Update Mode received "
+ "for payload#%d\n", req->payload_id);
+
+ if (req->data[0] == 0x0) {
+ syslog(LOG_INFO, "Normal Mode\n");
+ res->cc = CC_SUCCESS;
+ } else if (req->data[1] == 0x0F) {
+ syslog(LOG_INFO, "Update Mode\n");
+ res->cc = CC_SUCCESS;
+ } else {
+ syslog(LOG_ALERT, "Error\n");
+ res->cc = CC_INVALID_PARAM;
+ }
+
+ *res_len = 0;
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ *res_len = 0;
+ break;
+ }
+ pthread_mutex_unlock(&m_oem_1s);
+}
+
+/*
+ * Function to handle all IPMI messages
+ */
+static void
+ipmi_handle (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char netfn;
+
+ netfn = req->netfn_lun >> 2;
+
+ // Provide default values in the response message
+ res->cmd = req->cmd;
+ res->cc = 0xFF; // Unspecified completion code
+ *res_len = 0;
+
+ switch (netfn)
+ {
+ case NETFN_CHASSIS_REQ:
+ res->netfn_lun = NETFN_CHASSIS_RES << 2;
+ ipmi_handle_chassis (request, req_len, response, res_len);
+ break;
+ case NETFN_APP_REQ:
+ res->netfn_lun = NETFN_APP_RES << 2;
+ ipmi_handle_app (request, req_len, response, res_len);
+ break;
+ case NETFN_STORAGE_REQ:
+ res->netfn_lun = NETFN_STORAGE_RES << 2;
+ ipmi_handle_storage (request, req_len, response, res_len);
+ break;
+ case NETFN_TRANSPORT_REQ:
+ res->netfn_lun = NETFN_TRANSPORT_RES << 2;
+ ipmi_handle_transport (request, req_len, response, res_len);
+ break;
+ case NETFN_OEM_REQ:
+ res->netfn_lun = NETFN_OEM_RES << 2;
+ ipmi_handle_oem (request, req_len, response, res_len);
+ break;
+ case NETFN_OEM_1S_REQ:
+ res->netfn_lun = NETFN_OEM_1S_RES << 2;
+ ipmi_handle_oem_1s(request, req_len, response, res_len);
+ break;
+ default:
+ res->netfn_lun = (netfn + 1) << 2;
+ break;
+ }
+
+ // This header includes NetFunction, Command, and Completion Code
+ *res_len += IPMI_RESP_HDR_SIZE;
+
+ return;
+}
+
+void
+*conn_handler(void *socket_desc) {
+ int sock = *(int*)socket_desc;
+ int n;
+ unsigned char req_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_len = 0;
+
+ n = recv (sock, req_buf, sizeof(req_buf), 0);
+ if (n <= 0) {
+ syslog(LOG_ALERT, "ipmid: recv() failed with %d\n", n);
+ goto conn_cleanup;
+ }
+
+ ipmi_handle(req_buf, n, res_buf, &res_len);
+
+ if (send (sock, res_buf, res_len, 0) < 0) {
+ syslog(LOG_ALERT, "ipmid: send() failed\n");
+ }
+
+conn_cleanup:
+ close(sock);
+
+ pthread_exit(NULL);
+ return 0;
+}
+
+
+int
+main (void)
+{
+ int s, s2, t, len;
+ struct sockaddr_un local, remote;
+ pthread_t tid;
+
+ daemon(1, 0);
+ openlog("ipmid", LOG_CONS, LOG_DAEMON);
+
+
+ plat_fruid_init();
+ plat_sensor_init();
+
+ sdr_init();
+ sel_init();
+
+ pthread_mutex_init(&m_chassis, NULL);
+ pthread_mutex_init(&m_app, NULL);
+ pthread_mutex_init(&m_storage, NULL);
+ pthread_mutex_init(&m_transport, NULL);
+ pthread_mutex_init(&m_oem, NULL);
+ pthread_mutex_init(&m_oem_1s, NULL);
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: socket() failed\n");
+ exit (1);
+ }
+
+ local.sun_family = AF_UNIX;
+ strcpy (local.sun_path, SOCK_PATH_IPMI);
+ unlink (local.sun_path);
+ len = strlen (local.sun_path) + sizeof (local.sun_family);
+ if (bind (s, (struct sockaddr *) &local, len) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: bind() failed\n");
+ exit (1);
+ }
+
+ if (listen (s, 5) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: listen() failed\n");
+ exit (1);
+ }
+
+ while(1) {
+ int n;
+ t = sizeof (remote);
+ if ((s2 = accept (s, (struct sockaddr *) &remote, &t)) < 0) {
+ syslog(LOG_ALERT, "ipmid: accept() failed\n");
+ break;
+ }
+
+ // Creating a worker thread to handle the request
+ // TODO: Need to monitor the server performance with higher load and
+ // see if we need to create pre-defined number of workers and schedule
+ // the requests among them.
+ if (pthread_create(&tid, NULL, conn_handler, (void*) &s2) < 0) {
+ syslog(LOG_ALERT, "ipmid: pthread_create failed\n");
+ close(s2);
+ continue;
+ }
+
+ pthread_detach(tid);
+ }
+
+ close(s);
+
+ pthread_mutex_destroy(&m_chassis);
+ pthread_mutex_destroy(&m_app);
+ pthread_mutex_destroy(&m_storage);
+ pthread_mutex_destroy(&m_transport);
+ pthread_mutex_destroy(&m_oem);
+ pthread_mutex_destroy(&m_oem_1s);
+
+ return 0;
+}
diff --git a/common/recipes-core/ipmid/files/sdr.c b/common/recipes-core/ipmid/files/sdr.c
new file mode 100644
index 0000000..91a4df5
--- /dev/null
+++ b/common/recipes-core/ipmid/files/sdr.c
@@ -0,0 +1,414 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file represents platform specific implementation for storing
+ * SDR record entries and acts as back-end for IPMI stack
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "sdr.h"
+#include "sensor.h"
+#include "timestamp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <openbmc/ipmi.h>
+
+// SDR Header magic number
+#define SDR_HDR_MAGIC 0xFBFBFBFB
+
+// SDR Header version number
+#define SDR_HDR_VERSION 0x01
+
+// SDR reservation IDs can not be 0x00 or 0xFFFF
+#define SDR_RSVID_MIN 0x01
+#define SDR_RSVID_MAX 0xFFFE
+
+#define SDR_RECORDS_MAX 64 // to support around 64 sensors
+
+// SDR index to keep track
+#define SDR_INDEX_MIN 0
+#define SDR_INDEX_MAX (SDR_RECORDS_MAX - 1)
+
+// Record ID can not be 0x0 (IPMI/Section 31)
+#define SDR_RECID_MIN 1
+#define SDR_RECID_MAX SDR_RECORDS_MAX
+
+// Special RecID value for first and last (IPMI/Section 31)
+#define SDR_RECID_FIRST 0x0000
+#define SDR_RECID_LAST 0xFFFF
+
+#define SDR_VERSION 0x51
+#define SDR_LEN_MAX 64
+
+#define SDR_FULL_TYPE 0x01
+#define SDR_MGMT_TYPE 0x12
+#define SDR_OEM_TYPE 0xC0
+
+#define SDR_FULL_LEN 64
+#define SDR_MGMT_LEN 32
+#define SDR_OEM_LEN 64
+
+// SDR header struct to keep track of SEL Log entries
+typedef struct {
+ int magic; // Magic number to check validity
+ int version; // version number of this header
+ int begin; // index to the first SDR entry
+ int end; // index to the last SDR entry
+ time_stamp_t ts_add; // last addition time stamp
+ time_stamp_t ts_erase; // last erase time stamp
+} sdr_hdr_t;
+
+// Keep track of last Reservation ID
+static int g_rsv_id = 0x01;
+
+// SDR Header and data global structures
+static sdr_hdr_t g_sdr_hdr;
+static sdr_rec_t g_sdr_data[SDR_RECORDS_MAX];
+
+// Add a new SDR entry
+static int
+sdr_add_entry(sdr_rec_t *rec, int *rec_id) {
+ // If SDR is full, return error
+ if (sdr_num_entries() == SDR_RECORDS_MAX) {
+ syslog(LOG_ALERT, "sdr_add_entry: SDR full\n");
+ return -1;
+ }
+
+ // Add Record ID which is array index + 1
+ rec->rec[0] = g_sdr_hdr.end+1;
+
+ // Add the enry at end
+ memcpy(g_sdr_data[g_sdr_hdr.end].rec, rec->rec, sizeof(sdr_rec_t));
+
+ // Return the newly added record ID
+ *rec_id = g_sdr_hdr.end+1;
+
+ // Increment the end pointer
+ ++g_sdr_hdr.end;
+
+ // Update timestamp for add in header
+ time_stamp_fill(g_sdr_hdr.ts_add.ts);
+
+ return 0;
+}
+
+static int
+sdr_add_mgmt_rec(sensor_mgmt_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_mgmt_t rec = { 0 };
+
+ // Populate SDR MGMT record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_MGMT_TYPE;
+ rec.len = SDR_MGMT_LEN;
+
+ rec.slave_addr = p_rec->slave_addr;
+ rec.chan_no = p_rec->chan_no;
+
+ rec.pwr_state_init = p_rec->pwr_state_init;
+ rec.dev_caps = p_rec->dev_caps;
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_mgmt_rec: sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_disc_rec(sensor_disc_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_full_t rec = { 0 };
+
+ // Populate SDR FULL record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_FULL_TYPE;
+ rec.len = SDR_FULL_LEN;
+
+ rec.owner = p_rec->owner;
+ rec.lun = p_rec->lun;
+
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.sensor_init = p_rec->sensor_init;
+ rec.sensor_caps = p_rec->sensor_caps;
+ rec.sensor_type = p_rec->sensor_type;
+ rec.evt_read_type = p_rec->evt_read_type;
+ memcpy(rec.assert_evt_mask, p_rec->assert_evt_mask, 2);
+ memcpy(rec.deassert_evt_mask, p_rec->deassert_evt_mask, 2);
+ memcpy(rec.read_evt_mask, p_rec->read_evt_mask, 2);
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_disc_rec: sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_thresh_rec(sensor_thresh_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_full_t rec = { 0 };
+
+ // Populate SDR FULL record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_FULL_TYPE;
+ rec.len = SDR_FULL_LEN;
+
+ rec.owner = p_rec->owner;
+ rec.lun = p_rec->lun;
+
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.sensor_init = p_rec->sensor_init;
+ rec.sensor_caps = p_rec->sensor_caps;
+ rec.sensor_type = p_rec->sensor_type;
+ rec.evt_read_type = p_rec->evt_read_type;
+ memcpy(rec.lt_read_mask, p_rec->lt_read_mask, 2);
+ memcpy(rec.ut_read_mask, p_rec->ut_read_mask, 2);
+ memcpy(rec.set_thresh_mask, p_rec->set_thresh_mask, 2);
+ rec.sensor_units1 = p_rec->sensor_units1;
+ rec.sensor_units2 = p_rec->sensor_units2;
+ rec.sensor_units3 = p_rec->sensor_units3;
+ rec.linear = p_rec->linear;
+ rec.m_val = p_rec->m_val;
+ rec.m_tolerance = p_rec->m_tolerance;
+ rec.b_val = p_rec->b_val;
+ rec.b_accuracy = p_rec->b_accuracy;
+ rec.analog_flags = p_rec->analog_flags;
+ rec.nominal = p_rec->nominal;
+ rec.normal_max = p_rec->normal_max;
+ rec.normal_min = p_rec->normal_min;
+ rec.max_reading = p_rec->max_reading;
+ rec.min_reading = p_rec->min_reading;
+ rec.unr_thresh = p_rec->unr_thresh;
+ rec.uc_thresh = p_rec->uc_thresh;
+ rec.unc_thresh = p_rec->unc_thresh;
+ rec.lnr_thresh = p_rec->lnr_thresh;
+ rec.lc_thresh = p_rec->lc_thresh;
+ rec.lnc_thresh = p_rec->lnc_thresh;
+ rec.pos_hyst = p_rec->pos_hyst;
+ rec.neg_hyst = p_rec->neg_hyst;
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_thresh_rec: sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_oem_rec(sensor_oem_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_oem_t rec = { 0 };
+
+ // Populate SDR OEM record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_OEM_TYPE;
+ rec.len = SDR_OEM_LEN;
+
+ memcpy(rec.mfr_id, p_rec->mfr_id, 3);
+ memcpy(rec.oem_data, p_rec->oem_data, SENSOR_OEM_DATA_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_oem_rec: sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// Platform specific SEL API entry points
+// Retrieve time stamp for recent add operation
+void
+sdr_ts_recent_add(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sdr_hdr.ts_add.ts, 0x04);
+}
+
+// Retrieve time stamp for recent erase operation
+void
+sdr_ts_recent_erase(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sdr_hdr.ts_erase.ts, 0x04);
+}
+
+// Retrieve total number of entries in SDR repo
+int
+sdr_num_entries(void) {
+ return (g_sdr_hdr.end - g_sdr_hdr.begin);
+}
+
+// Retrieve total free space available in SDR repo
+int
+sdr_free_space(void) {
+ int total_space;
+ int used_space;
+
+ total_space = SDR_RECORDS_MAX * sizeof(sdr_rec_t);
+ used_space = sdr_num_entries() * sizeof(sdr_rec_t);
+
+ return (total_space - used_space);
+}
+
+// Reserve an ID that will be used in later operations
+// IPMI/Section 33.11
+int
+sdr_rsv_id() {
+ // Increment the current reservation ID and return
+ if (g_rsv_id++ == SDR_RSVID_MAX) {
+ g_rsv_id = SDR_RSVID_MIN;
+ }
+
+ return g_rsv_id;
+}
+
+// Get the SDR entry for a given record ID
+// IPMI/Section 33.12
+int
+sdr_get_entry(int rsv_id, int read_rec_id, sdr_rec_t *rec,
+ int *next_rec_id) {
+
+ int index;
+
+ // Make sure the rsv_id matches
+ if (rsv_id != g_rsv_id) {
+ syslog(LOG_ALERT, "sdr_get_entry: Reservation ID mismatch\n");
+ return -1;
+ }
+
+ // Find the index in to array based on given index
+ if (read_rec_id == SDR_RECID_FIRST) {
+ index = g_sdr_hdr.begin;
+ } else if (read_rec_id == SDR_RECID_LAST) {
+ index = g_sdr_hdr.end - 1;
+ } else {
+ index = read_rec_id - 1;
+ }
+
+ // If the SDR repo is empty return error
+ if (sdr_num_entries() == 0) {
+ syslog(LOG_ALERT, "sdr_get_entry: No entries\n");
+ return -1;
+ }
+
+ // Check for boundary conditions
+ if ((index < SDR_INDEX_MIN) || (index > SDR_INDEX_MAX)) {
+ syslog(LOG_ALERT, "sdr_get_entry: Invalid Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ // Check to make sure the given id is valid
+ if (index < g_sdr_hdr.begin || index >= g_sdr_hdr.end) {
+ syslog(LOG_ALERT, "sdr_get_entry: Wrong Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ memcpy(rec->rec, g_sdr_data[index].rec, sizeof(sdr_rec_t));
+
+ // Return the next record ID in the log
+ *next_rec_id = ++read_rec_id;
+
+ // If this is the last entry in the log, return 0xFFFF
+ if (*next_rec_id == g_sdr_hdr.end) {
+ *next_rec_id = SDR_RECID_LAST;
+ }
+
+ return 0;
+}
+
+
+// Initialize SDR Repo structure
+int
+sdr_init(void) {
+ int num;
+ int i;
+ sensor_mgmt_t *p_mgmt;
+ sensor_thresh_t *p_thresh;
+ sensor_disc_t *p_disc;
+ sensor_oem_t *p_oem;
+
+ // Populate SDR Header
+ g_sdr_hdr.magic = SDR_HDR_MAGIC;
+ g_sdr_hdr.version = SDR_HDR_VERSION;
+ g_sdr_hdr.begin = SDR_INDEX_MIN;
+ g_sdr_hdr.end = SDR_INDEX_MIN;
+ memset(g_sdr_hdr.ts_add.ts, 0x0, 4);
+ memset(g_sdr_hdr.ts_erase.ts, 0x0, 4);
+
+ // Populate all mgmt control sensors
+ plat_sensor_mgmt_info(&num, &p_mgmt);
+ for (i = 0; i < num; i++) {
+ sdr_add_mgmt_rec(&p_mgmt[i]);
+ }
+
+ // Populate all discrete sensors
+ plat_sensor_disc_info(&num, &p_disc);
+ for (i = 0; i < num; i++) {
+ sdr_add_disc_rec(&p_disc[i]);
+ }
+
+ // Populate all threshold sensors
+ plat_sensor_thresh_info(&num, &p_thresh);
+ for (i = 0; i < num; i++) {
+ sdr_add_thresh_rec(&p_thresh[i]);
+ }
+
+ // Populate all OEM sensors
+ plat_sensor_oem_info(&num, &p_oem);
+ for (i = 0; i < num; i++) {
+ sdr_add_oem_rec(&p_oem[i]);
+ }
+
+ return 0;
+}
diff --git a/common/recipes-core/ipmid/files/sdr.h b/common/recipes-core/ipmid/files/sdr.h
new file mode 100644
index 0000000..5c11c31
--- /dev/null
+++ b/common/recipes-core/ipmid/files/sdr.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SDR_H__
+#define __SDR_H__
+
+#include "timestamp.h"
+
+typedef struct {
+ unsigned char rec[64];
+} sdr_rec_t;
+
+// Mgmt. Controller SDR record; IPMI/ Section 43.9
+typedef struct {
+ // Sensor Record Header
+ unsigned char rec_id[2];
+ unsigned char ver;
+ unsigned char type;
+ unsigned char len;
+ // Record Key Bytes
+ unsigned char slave_addr;
+ unsigned char chan_no;
+ // Record Body Bytes
+ unsigned char pwr_state_init;
+ unsigned char dev_caps;
+ unsigned char rsvd[3];
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[16];
+} sdr_mgmt_t;
+
+// OEM type SDR record; IPMI/Section 43.12
+typedef struct {
+ // Sensor Record Header
+ unsigned char rec_id[2];
+ unsigned char ver;
+ unsigned char type;
+ unsigned char len;
+ // Record Body Bytes
+ unsigned char mfr_id[3];
+ unsigned char oem_data[56];
+} sdr_oem_t;
+
+void sdr_ts_recent_add(time_stamp_t *ts);
+void sdr_ts_recent_erase(time_stamp_t *ts);
+int sdr_num_entries(void);
+int sdr_free_space(void);
+int sdr_rsv_id();
+int sdr_get_entry(int rsv_id, int read_rec_id, sdr_rec_t *rec,
+ int *next_rec_id);
+int sdr_init(void);
+
+#endif /* __SDR_H__ */
diff --git a/common/recipes-core/ipmid/files/sel.c b/common/recipes-core/ipmid/files/sel.c
new file mode 100644
index 0000000..d598bb1
--- /dev/null
+++ b/common/recipes-core/ipmid/files/sel.c
@@ -0,0 +1,440 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file represents platform specific implementation for storing
+ * SEL logs and acts as back-end for IPMI stack
+ *
+ * TODO: Optimize the file handling to keep file open always instead of
+ * current open/seek/close
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "sel.h"
+#include "timestamp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+
+// SEL File.
+#define SEL_LOG_FILE "/mnt/data/sel.bin"
+
+// SEL Header magic number
+#define SEL_HDR_MAGIC 0xFBFBFBFB
+
+// SEL Header version number
+#define SEL_HDR_VERSION 0x01
+
+// SEL Data offset from file beginning
+#define SEL_DATA_OFFSET 0x100
+
+// SEL reservation IDs can not be 0x00 or 0xFFFF
+#define SEL_RSVID_MIN 0x01
+#define SEL_RSVID_MAX 0xFFFE
+
+// Number of SEL records before wrap
+#define SEL_RECORDS_MAX 128 // TODO: Based on need we can make it bigger
+#define SEL_ELEMS_MAX (SEL_RECORDS_MAX+1)
+
+// Index for circular array
+#define SEL_INDEX_MIN 0x00
+#define SEL_INDEX_MAX SEL_RECORDS_MAX
+
+// Record ID can not be 0x0 (IPMI/Section 31)
+#define SEL_RECID_MIN (SEL_INDEX_MIN+1)
+#define SEL_RECID_MAX (SEL_INDEX_MAX+1)
+
+// Special RecID value for first and last (IPMI/Section 31)
+#define SEL_RECID_FIRST 0x0000
+#define SEL_RECID_LAST 0xFFFF
+
+// SEL header struct to keep track of SEL Log entries
+typedef struct {
+ int magic; // Magic number to check validity
+ int version; // version number of this header
+ int begin; // index to the begining of the log
+ int end; // index to end of the log
+ time_stamp_t ts_add; // last addition time stamp
+ time_stamp_t ts_erase; // last erase time stamp
+} sel_hdr_t;
+
+// Keep track of last Reservation ID
+static int g_rsv_id = 0x01;
+
+// Cached version of SEL Header and data
+static sel_hdr_t g_sel_hdr;
+static sel_msg_t g_sel_data[SEL_ELEMS_MAX];
+
+// Local helper functions to interact with file system
+static int
+file_get_sel_hdr(void) {
+ FILE *fp;
+
+ fp = fopen(SEL_LOG_FILE, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ if (fread(&g_sel_hdr, sizeof(sel_hdr_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_get_sel_hdr: fread\n");
+ fclose (fp);
+ return -1;
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int
+file_get_sel_data(void) {
+ FILE *fp;
+ int i, j;
+
+ fp = fopen(SEL_LOG_FILE, "r");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_get_sel_data: fopen\n");
+ return -1;
+ }
+
+ if (fseek(fp, SEL_DATA_OFFSET, SEEK_SET)) {
+ syslog(LOG_ALERT, "file_get_sel_data: fseek\n");
+ fclose(fp);
+ return -1;
+ }
+
+ unsigned char buf[SEL_ELEMS_MAX * 16];
+ if (fread(buf, 1, SEL_ELEMS_MAX * sizeof(sel_msg_t), fp) <= 0) {
+ syslog(LOG_ALERT, "file_get_sel_data: fread\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ for (i = 0; i < SEL_ELEMS_MAX; i++) {
+ for (j = 0; j < sizeof(sel_msg_t);j++) {
+ g_sel_data[i].msg[j] = buf[i*16 + j];
+ }
+ }
+
+ return 0;
+}
+
+static int
+file_store_sel_hdr(void) {
+ FILE *fp;
+
+ fp = fopen(SEL_LOG_FILE, "r+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_store_sel_hdr: fopen\n");
+ return -1;
+ }
+
+ if (fwrite(&g_sel_hdr, sizeof(sel_hdr_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_store_sel_hdr: fwrite\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int
+file_store_sel_data(int recId, sel_msg_t *data) {
+ FILE *fp;
+ int index;
+
+ fp = fopen(SEL_LOG_FILE, "r+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_store_sel_data: fopen\n");
+ return -1;
+ }
+
+ // Records are stored using zero-based index
+ index = (recId-1) * sizeof(sel_msg_t);
+
+ if (fseek(fp, SEL_DATA_OFFSET+index, SEEK_SET)) {
+ syslog(LOG_ALERT, "file_store_sel_data: fseek\n");
+ fclose(fp);
+ return -1;
+ }
+
+ if (fwrite(data->msg, sizeof(sel_msg_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_store_sel_data: fwrite\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+// Platform specific SEL API entry points
+// Retrieve time stamp for recent add operation
+void
+sel_ts_recent_add(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sel_hdr.ts_add.ts, 0x04);
+}
+
+// Retrieve time stamp for recent erase operation
+void
+sel_ts_recent_erase(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sel_hdr.ts_erase.ts, 0x04);
+}
+
+// Retrieve total number of entries in SEL log
+int
+sel_num_entries(void) {
+ if (g_sel_hdr.begin <= g_sel_hdr.end) {
+ return (g_sel_hdr.end - g_sel_hdr.begin);
+ } else {
+ return (g_sel_hdr.end + (SEL_INDEX_MAX - g_sel_hdr.begin + 1));
+ }
+}
+
+// Retrieve total free space available in SEL log
+int
+sel_free_space(void) {
+ int total_space;
+ int used_space;
+
+ total_space = SEL_RECORDS_MAX * sizeof(sel_msg_t);
+ used_space = sel_num_entries() * sizeof(sel_msg_t);
+
+ return (total_space - used_space);
+}
+
+// Reserve an ID that will be used in later operations
+// IPMI/Section 31.4
+int
+sel_rsv_id() {
+ // Increment the current reservation ID and return
+ if (g_rsv_id++ == SEL_RSVID_MAX) {
+ g_rsv_id = SEL_RSVID_MIN;
+ }
+
+ return g_rsv_id;
+}
+
+// Get the SEL entry for a given record ID
+// IPMI/Section 31.5
+int
+sel_get_entry(int read_rec_id, sel_msg_t *msg, int *next_rec_id) {
+
+ int index;
+
+ // Find the index in to array based on given index
+ if (read_rec_id == SEL_RECID_FIRST) {
+ index = g_sel_hdr.begin;
+ } else if (read_rec_id == SEL_RECID_LAST) {
+ if (g_sel_hdr.end) {
+ index = g_sel_hdr.end - 1;
+ } else {
+ index = SEL_INDEX_MAX;
+ }
+ } else {
+ index = read_rec_id - 1;
+ }
+
+ // If the log is empty return error
+ if (sel_num_entries() == 0) {
+ syslog(LOG_ALERT, "sel_get_entry: No entries\n");
+ return -1;
+ }
+
+ // Check for boundary conditions
+ if ((index < SEL_INDEX_MIN) || (index > SEL_INDEX_MAX)) {
+ syslog(LOG_ALERT, "sel_get_entry: Invalid Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ // If begin < end, check to make sure the given id falls between
+ if (g_sel_hdr.begin < g_sel_hdr.end) {
+ if (index < g_sel_hdr.begin || index >= g_sel_hdr.end) {
+ syslog(LOG_ALERT, "sel_get_entry: Wrong Record ID %d\n", read_rec_id);
+ return -1;
+ }
+ }
+
+ // If end < begin, check to make sure the given id is valid
+ if (g_sel_hdr.begin > g_sel_hdr.end) {
+ if (index >= g_sel_hdr.end && index < g_sel_hdr.begin) {
+ syslog(LOG_ALERT, "sel_get_entry: Wrong Record ID2 %d\n", read_rec_id);
+ return -1;
+ }
+ }
+
+ memcpy(msg->msg, g_sel_data[index].msg, sizeof(sel_msg_t));
+
+ // Return the next record ID in the log
+ *next_rec_id = read_rec_id++;
+ if (*next_rec_id > SEL_INDEX_MAX) {
+ *next_rec_id = SEL_INDEX_MIN;
+ }
+
+ // If this is the last entry in the log, return 0xFFFF
+ if (*next_rec_id == g_sel_hdr.end) {
+ *next_rec_id = SEL_RECID_LAST;
+ }
+
+ return 0;
+}
+
+// Add a new entry in to SEL log
+// IPMI/Section 31.6
+int
+sel_add_entry(sel_msg_t *msg, int *rec_id) {
+ // If the SEL if full, roll over. To keep track of empty condition, use
+ // one empty location less than the max records.
+ if (sel_num_entries() == SEL_RECORDS_MAX) {
+ syslog(LOG_ALERT, "sel_add_entry: SEL rollover\n");
+ if (++g_sel_hdr.begin > SEL_INDEX_MAX) {
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ }
+ }
+
+ // Update message's time stamp starting at byte 4
+ time_stamp_fill(&msg->msg[3]);
+
+ // Add the enry at end
+ memcpy(g_sel_data[g_sel_hdr.end].msg, msg->msg, sizeof(sel_msg_t));
+
+ // Return the newly added record ID
+ *rec_id = g_sel_hdr.end+1;
+
+ if (file_store_sel_data(*rec_id, msg)) {
+ syslog(LOG_ALERT, "sel_add_entry: file_store_sel_data\n");
+ return -1;
+ }
+
+ // Increment the end pointer
+ if (++g_sel_hdr.end > SEL_INDEX_MAX) {
+ g_sel_hdr.end = SEL_INDEX_MIN;
+ }
+
+ // Update timestamp for add in header
+ time_stamp_fill(g_sel_hdr.ts_add.ts);
+
+ // Store the structure persistently
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "sel_add_entry: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// Erase the SEL completely
+// IPMI/Section 31.9
+// Note: To reduce wear/tear, instead of erasing, manipulating the metadata
+int
+sel_erase(int rsv_id) {
+ if (rsv_id != g_rsv_id) {
+ return -1;
+ }
+
+ // Erase SEL Logs
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ g_sel_hdr.end = SEL_INDEX_MIN;
+
+ // Update timestamp for erase in header
+ time_stamp_fill(g_sel_hdr.ts_erase.ts);
+
+ // Store the structure persistently
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "sel_erase: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// To get the erase status while erase happens
+// IPMI/Section 31.2
+// Note: Since we are not doing offline erasing, need not return in-progress state
+int
+sel_erase_status(int rsv_id, sel_erase_stat_t *status) {
+ if (rsv_id != g_rsv_id) {
+ return -1;
+ }
+
+ // Since we do not do any offline erasing, always return erase done
+ *status = SEL_ERASE_DONE;
+
+ return 0;
+}
+
+// Initialize SEL log file
+int
+sel_init(void) {
+ FILE *fp;
+ int i;
+
+ // Check if the file exists or not
+ if (access(SEL_LOG_FILE, F_OK) == 0) {
+ // Since file is present, fetch all the contents to cache
+ if (file_get_sel_hdr()) {
+ syslog(LOG_ALERT, "init_sel: file_get_sel_hdr\n");
+ return -1;
+ }
+
+ if (file_get_sel_data()) {
+ syslog(LOG_ALERT, "init_sel: file_get_sel_data\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ // File not present, so create the file
+ fp = fopen(SEL_LOG_FILE, "w+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "init_sel: fopen\n");
+ return -1;
+ }
+
+ fclose (fp);
+
+ // Populate SEL Header in to the file
+ g_sel_hdr.magic = SEL_HDR_MAGIC;
+ g_sel_hdr.version = SEL_HDR_VERSION;
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ g_sel_hdr.end = SEL_INDEX_MIN;
+ memset(g_sel_hdr.ts_add.ts, 0x0, 4);
+ memset(g_sel_hdr.ts_erase.ts, 0x0, 4);
+
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "init_sel: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ // Populate SEL Data in to the file
+ for (i = 1; i <= SEL_RECORDS_MAX; i++) {
+ sel_msg_t msg = {0};
+ if (file_store_sel_data(i, &msg)) {
+ syslog(LOG_ALERT, "init_sel: file_store_sel_data\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/common/recipes-core/ipmid/files/sel.h b/common/recipes-core/ipmid/files/sel.h
new file mode 100644
index 0000000..3bb9a2f
--- /dev/null
+++ b/common/recipes-core/ipmid/files/sel.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SEL_H__
+#define __SEL_H__
+
+#include "timestamp.h"
+
+enum {
+ IPMI_SEL_INIT_ERASE = 0xAA,
+ IPMI_SEL_ERASE_STAT = 0x00,
+};
+
+typedef enum {
+ SEL_ERASE_IN_PROG = 0x00,
+ SEL_ERASE_DONE = 0x01,
+} sel_erase_stat_t;
+
+typedef struct {
+ unsigned char msg[16];
+} sel_msg_t;
+
+void sel_ts_recent_add(time_stamp_t *ts);
+void sel_ts_recent_erase(time_stamp_t *ts);
+int sel_num_entries(void);
+int sel_free_space(void);
+int sel_rsv_id();
+int sel_get_entry(int read_rec_id, sel_msg_t *msg, int *next_rec_id);
+int sel_add_entry(sel_msg_t *msg, int *rec_id);
+int sel_erase(int rsv_id);
+int sel_erase_status(int rsv_id, sel_erase_stat_t *status);
+int sel_init(void);
+
+#endif /* __SEL_H__ */
diff --git a/common/recipes-core/ipmid/files/sensor.h b/common/recipes-core/ipmid/files/sensor.h
new file mode 100644
index 0000000..5d8c11a
--- /dev/null
+++ b/common/recipes-core/ipmid/files/sensor.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SENSOR_H__
+#define __SENSOR_H__
+
+#include "timestamp.h"
+
+#define IANA_ID_SIZE 3
+#define SENSOR_STR_SIZE 16
+#define SENSOR_OEM_DATA_SIZE 56
+
+// Threshold Sensor Descriptor
+typedef struct {
+ unsigned char owner;
+ unsigned char lun;
+ unsigned char sensor_num;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char sensor_init;
+ unsigned char sensor_caps;
+ unsigned char sensor_type;
+ unsigned char evt_read_type;
+ unsigned char lt_read_mask[2];
+ unsigned char ut_read_mask[2];
+ unsigned char set_thresh_mask[2];
+ unsigned char sensor_units1;
+ unsigned char sensor_units2;
+ unsigned char sensor_units3;
+ unsigned char linear;
+ unsigned char m_val;
+ unsigned char m_tolerance;
+ unsigned char b_val;
+ unsigned char b_accuracy;
+ unsigned char accuracy_dir;
+ unsigned char rb_exp;
+ unsigned char analog_flags;
+ unsigned char nominal;
+ unsigned char normal_max;
+ unsigned char normal_min;
+ unsigned char max_reading;
+ unsigned char min_reading;
+ unsigned char unr_thresh;
+ unsigned char uc_thresh;
+ unsigned char unc_thresh;
+ unsigned char lnr_thresh;
+ unsigned char lc_thresh;
+ unsigned char lnc_thresh;
+ unsigned char pos_hyst;
+ unsigned char neg_hyst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_thresh_t;
+
+// Discrete Sensor Descriptor
+typedef struct {
+ unsigned char owner;
+ unsigned char lun;
+ unsigned char sensor_num;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char sensor_init;
+ unsigned char sensor_caps;
+ unsigned char sensor_type;
+ unsigned char evt_read_type;
+ unsigned char assert_evt_mask[2];
+ unsigned char deassert_evt_mask[2];
+ unsigned char read_evt_mask[2];
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_disc_t;
+
+// Mgmt. Controller Sensor Descriptor
+typedef struct {
+ unsigned char slave_addr;
+ unsigned char chan_no;
+ unsigned char pwr_state_init;
+ unsigned char dev_caps;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_mgmt_t;
+
+// OEM type Sensor Descriptor
+typedef struct {
+ unsigned char mfr_id[IANA_ID_SIZE];
+ unsigned char oem_data[SENSOR_OEM_DATA_SIZE];
+} sensor_oem_t;
+
+void plat_sensor_mgmt_info(int *num, sensor_mgmt_t **p_sensor);
+void plat_sensor_disc_info(int *num, sensor_disc_t **p_sensor);
+void plat_sensor_thresh_info(int *num, sensor_thresh_t **p_sensor);
+void plat_sensor_oem_info(int *num, sensor_oem_t **p_sensor);
+int plat_sensor_init(void);
+
+#endif /* __SENSOR_H__ */
diff --git a/common/recipes-core/ipmid/files/timestamp.c b/common/recipes-core/ipmid/files/timestamp.c
new file mode 100644
index 0000000..11ac03e
--- /dev/null
+++ b/common/recipes-core/ipmid/files/timestamp.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file is a helper file to fill timestamps from platform
+ * used by SEL Logs, SDR records etc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+// Local helper function to fill time stamp
+void
+time_stamp_fill(unsigned char *ts) {
+ unsigned int time;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ time = tv.tv_sec;
+ ts[0] = time & 0xFF;
+ ts[1] = (time >> 8) & 0xFF;
+ ts[2] = (time >> 16) & 0xFF;
+ ts[3] = (time >> 24) & 0xFF;
+
+ return;
+}
diff --git a/common/recipes-core/ipmid/files/timestamp.h b/common/recipes-core/ipmid/files/timestamp.h
new file mode 100644
index 0000000..430dd23
--- /dev/null
+++ b/common/recipes-core/ipmid/files/timestamp.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __TIMESTAMP_H__
+#define __TIMESTAMP_H__
+
+typedef struct {
+ unsigned char ts[4];
+} time_stamp_t;
+
+void time_stamp_fill(unsigned char *ts);
+
+#endif /* __TIMESTAMP_H__ */
diff --git a/common/recipes-core/ipmid/ipmid_0.2.bb b/common/recipes-core/ipmid/ipmid_0.2.bb
new file mode 100644
index 0000000..14c8c7f
--- /dev/null
+++ b/common/recipes-core/ipmid/ipmid_0.2.bb
@@ -0,0 +1,42 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+SUMMARY = "IPMI Daemon"
+DESCRIPTION = "Daemon to handle IPMI Messages."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://ipmid.c;beginline=8;endline=20;md5=da35978751a9d71b73679307c4d296ec"
+
+
+SRC_URI = "file://Makefile \
+ file://ipmid.c \
+ file://timestamp.c \
+ file://timestamp.h \
+ file://sel.c \
+ file://sel.h \
+ file://sdr.c \
+ file://sdr.h \
+ file://sensor.h \
+ file://fruid.h \
+ "
+
+DEPENDS += " libpal "
+
+binfiles = "ipmid"
+
+pkgdir = "ipmid"
diff --git a/common/recipes-core/power-util/files/Makefile b/common/recipes-core/power-util/files/Makefile
new file mode 100644
index 0000000..97ee9d5
--- /dev/null
+++ b/common/recipes-core/power-util/files/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+all: power-util
+
+
+power-util: power-util.o
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o power-util
diff --git a/common/recipes-core/power-util/files/power-util.c b/common/recipes-core/power-util/files/power-util.c
new file mode 100644
index 0000000..0fda9d6
--- /dev/null
+++ b/common/recipes-core/power-util/files/power-util.c
@@ -0,0 +1,295 @@
+/*
+ * power-util
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <openbmc/pal.h>
+
+#define POWER_ON_STR "on"
+#define POWER_OFF_STR "off"
+
+const char *pwr_option_list = "status, graceful-shutdown, off, on, cycle, 12V-off,"
+ "12V-on, 12V-cycle";
+
+enum {
+ PWR_STATUS = 1,
+ PWR_GRACEFUL_SHUTDOWN,
+ PWR_OFF,
+ PWR_ON,
+ PWR_CYCLE,
+ PWR_12V_OFF,
+ PWR_12V_ON,
+ PWR_12V_CYCLE,
+ PWR_SLED_CYCLE
+};
+
+static int
+set_last_pwr_state(uint8_t fru, char * state) {
+
+ int ret;
+ char key[MAX_KEY_LEN] = {0};
+
+ sprintf(key, "pwr_server%d_last_state", (int) fru);
+
+ ret = pal_set_key_value(key, state);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "set_last_pwr_state: pal_set_key_value failed for "
+ "fru %u", fru);
+ }
+ return ret;
+}
+
+static void
+print_usage() {
+ printf("Usage: power-util [ %s ] [ %s ]\nUsage: power-util sled-cycle\n",
+ pal_server_list, pwr_option_list);
+}
+
+static int
+get_power_opt(char *option, uint8_t *opt) {
+
+ if (!strcmp(option, "status")) {
+ *opt = PWR_STATUS;
+ } else if (!strcmp(option, "graceful-shutdown")) {
+ *opt = PWR_GRACEFUL_SHUTDOWN;
+ } else if (!strcmp(option, "off")) {
+ *opt = PWR_OFF;
+ } else if (!strcmp(option, "on")) {
+ *opt = PWR_ON;
+ } else if (!strcmp(option, "cycle")) {
+ *opt = PWR_CYCLE;
+ } else if (!strcmp(option, "12V-off")) {
+ *opt = PWR_12V_OFF;
+ } else if (!strcmp(option, "12V-on")) {
+ *opt = PWR_12V_ON;
+ } else if (!strcmp(option, "12V-cycle")) {
+ *opt = PWR_12V_CYCLE;
+ } else if (!strcmp(option, "sled-cycle")) {
+ *opt = PWR_SLED_CYCLE;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+power_util(uint8_t fru, uint8_t opt) {
+
+ int ret;
+ uint8_t status;
+
+ switch(opt) {
+ case PWR_STATUS:
+ ret = pal_get_server_power(fru, &status);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_get_server_power failed for fru %u\n", fru);
+ return ret;
+ }
+ printf("Power status for fru %u : %s\n", fru, status?"ON":"OFF");
+ break;
+
+ case PWR_GRACEFUL_SHUTDOWN:
+
+ printf("Shutting down fru %u gracefully...\n", fru);
+
+ ret = pal_set_server_power(fru, SERVER_GRACEFUL_SHUTDOWN);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_server_power failed for"
+ " fru %u", fru);
+ return ret;
+ } else if (ret == 1) {
+ printf("fru %u is already powered OFF...\n", fru);
+ return 0;
+ }
+
+ ret = set_last_pwr_state(fru, POWER_OFF_STR);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_set_led(fru, LED_STATE_OFF);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_led failed for fru %u", fru);
+ return ret;
+ }
+ break;
+
+ case PWR_OFF:
+
+ printf("Powering fru %u to OFF state...\n", fru);
+
+ ret = pal_set_server_power(fru, SERVER_POWER_OFF);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_server_power failed for"
+ " fru %u", fru);
+ return ret;
+ } else if (ret == 1) {
+ printf("fru %u is already powered OFF...\n", fru);
+ return 0;
+ }
+
+ ret = set_last_pwr_state(fru, POWER_OFF_STR);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_set_led(fru, LED_STATE_OFF);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_led failed for fru %u", fru);
+ return ret;
+ }
+ break;
+
+ case PWR_ON:
+
+ printf("Powering fru %u to ON state...\n", fru);
+
+ ret = pal_set_server_power(fru, SERVER_POWER_ON);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_server_power failed for"
+ " fru %u", fru);
+ return ret;
+ } else if (ret == 1) {
+ printf("fru %u is already powered ON...\n", fru);
+ return 0;
+ }
+
+ ret = set_last_pwr_state(fru, POWER_ON_STR);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_set_led(fru, LED_STATE_ON);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_led failed for fru %u", fru);
+ return ret;
+ }
+ break;
+
+ case PWR_CYCLE:
+
+ printf("Power cycling fru %u...\n", fru);
+
+ ret = pal_set_server_power(fru, SERVER_POWER_CYCLE);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_server_power failed for"
+ " fru %u", fru);
+ return ret;
+ }
+
+ ret = set_last_pwr_state(fru, POWER_ON_STR);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = pal_set_led(fru, LED_STATE_ON);
+ if (ret < 0) {
+ syslog(LOG_ALERT, "power_util: pal_set_led failed for fru %u", fru);
+ return ret;
+ }
+ break;
+
+ case PWR_12V_OFF:
+ ret = 0; // TODO: Need to add the API to support this power state setting
+ break;
+
+ case PWR_12V_ON:
+ ret = 0; // TODO: Need to add the API to support this power state setting
+ break;
+
+ case PWR_12V_CYCLE:
+ ret = 0; // TODO: Need to add the API to support this power state setting
+ break;
+
+ case PWR_SLED_CYCLE:
+ pal_sled_cycle();
+ break;
+
+ default:
+ syslog(LOG_ALERT, "power_util: wrong option");
+
+ }
+
+ return ret;
+}
+
+int
+main(int argc, char **argv) {
+
+ int ret;
+
+ uint8_t fru, status, opt;
+ char *option;
+
+ /* Check for sled-cycle */
+ if (argc < 2 || argc > 3) {
+ print_usage();
+ exit (-1);
+ }
+
+ option = argc == 2 ? argv[1] : argv [2];
+
+ ret = get_power_opt(option, &opt);
+ /* If argc is 2, the option is sled-cycle */
+ if ((ret < 0) || (argc == 2 && opt != PWR_SLED_CYCLE)) {
+ printf("Wrong option: %s\n", option);
+ print_usage();
+ exit(-1);
+ }
+
+ if (argc > 2) {
+ ret = pal_get_fru_id(argv[1], &fru);
+ if (ret < 0) {
+ printf("Wrong fru: %s\n", argv[1]);
+ print_usage();
+ exit(-1);
+ }
+ } else {
+ fru = -1;
+ }
+
+ if (argc > 2) {
+ ret = pal_is_server_prsnt(fru, &status);
+ if (ret < 0) {
+ printf("pal_is_server_prsnt failed for fru: %d\n", fru);
+ print_usage();
+ exit(-1);
+ }
+ if (status == 0) {
+ printf("%s is empty!\n", argv[1]);
+ print_usage();
+ exit(-1);
+ }
+ }
+
+ ret = power_util(fru, opt);
+ if (ret < 0) {
+ print_usage();
+ return ret;
+ }
+}
diff --git a/common/recipes-core/power-util/power-util_0.1.bb b/common/recipes-core/power-util/power-util_0.1.bb
new file mode 100644
index 0000000..69ef2ad
--- /dev/null
+++ b/common/recipes-core/power-util/power-util_0.1.bb
@@ -0,0 +1,36 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+SUMMARY = "Power Utility"
+DESCRIPTION = "Utility for Power Policy and Management"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://power-util.c;beginline=4;endline=16;md5=b395943ba8a0717a83e62ca123a8d238"
+
+SRC_URI = "file://Makefile \
+ file://power-util.c \
+ "
+S = "${WORKDIR}"
+
+LDFLAGS =+ " -lpal "
+
+DEPENDS =+ " libpal "
+
+binfiles = "power-util"
+
+pkgdir = "power-util"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 power-util ${dst}/power-util
+ ln -snf ../fbpackages/${pkgdir}/power-util ${bin}/power-util
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/power-util ${prefix}/local/bin"
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/sensor-mon/files/Makefile b/common/recipes-core/sensor-mon/files/Makefile
new file mode 100644
index 0000000..68a9fe7
--- /dev/null
+++ b/common/recipes-core/sensor-mon/files/Makefile
@@ -0,0 +1,26 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+all: sensord
+
+sensord: sensord.c
+ $(CC) $(CFLAGS) -D _XOPEN_SOURCE -pthread -lm -std=c99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o sensord
diff --git a/common/recipes-core/sensor-mon/files/sensord.c b/common/recipes-core/sensor-mon/files/sensord.c
new file mode 100644
index 0000000..1852af3
--- /dev/null
+++ b/common/recipes-core/sensor-mon/files/sensord.c
@@ -0,0 +1,1031 @@
+/*
+ * sensord
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <math.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <facebook/bic.h>
+#include <openbmc/ipmi.h>
+#include <openbmc/sdr.h>
+#ifdef CONFIG_YOSEMITE
+#include <facebook/yosemite_sensor.h>
+#endif /* CONFIG_YOSEMITE */
+
+#define MAX_SENSOR_NUM 0xFF
+#define NORMAL_STATE 0x00
+
+#define SETBIT(x, y) (x | (1 << y))
+#define GETBIT(x, y) ((x & (1 << y)) > y)
+#define CLEARBIT(x, y) (x & (~(1 << y)))
+#define GETMASK(y) (1 << y)
+
+
+
+/* Enum for type of Upper and Lower threshold values */
+enum {
+ UCR_THRESH = 0x01,
+ UNC_THRESH,
+ UNR_THRESH,
+ LCR_THRESH,
+ LNC_THRESH,
+ LNR_THRESH,
+ POS_HYST,
+ NEG_HYST,
+};
+
+/* To hold the sensor info and calculated threshold values from the SDR */
+typedef struct {
+ uint8_t flag;
+ float ucr_thresh;
+ float unc_thresh;
+ float unr_thresh;
+ float lcr_thresh;
+ float lnc_thresh;
+ float lnr_thresh;
+ float pos_hyst;
+ float neg_hyst;
+ int curr_state;
+ char name[23];
+ char units[64];
+
+} thresh_sensor_t;
+
+/* Function pointer to read sensor current value */
+static int (*read_snr_val)(uint8_t, uint8_t, void *);
+
+#ifdef CONFIG_YOSEMITE
+
+// TODO: Change to 6 after adding SPB and NIC
+#define MAX_NUM_FRUS 4
+#define YOSEMITE_SDR_PATH "/tmp/sdr_%s.bin"
+
+static thresh_sensor_t snr_slot1[MAX_SENSOR_NUM] = {0};
+static thresh_sensor_t snr_slot2[MAX_SENSOR_NUM] = {0};
+static thresh_sensor_t snr_slot3[MAX_SENSOR_NUM] = {0};
+static thresh_sensor_t snr_slot4[MAX_SENSOR_NUM] = {0};
+static thresh_sensor_t snr_spb[MAX_SENSOR_NUM] = {0};
+static thresh_sensor_t snr_nic[MAX_SENSOR_NUM] = {0};
+
+static sensor_info_t sinfo_slot1[MAX_SENSOR_NUM] = {0};
+static sensor_info_t sinfo_slot2[MAX_SENSOR_NUM] = {0};
+static sensor_info_t sinfo_slot3[MAX_SENSOR_NUM] = {0};
+static sensor_info_t sinfo_slot4[MAX_SENSOR_NUM] = {0};
+static sensor_info_t sinfo_spb[MAX_SENSOR_NUM] = {0};
+static sensor_info_t sinfo_nic[MAX_SENSOR_NUM] = {0};
+#endif /* CONFIG_YOSEMITE */
+
+
+/*
+ * Returns the pointer to the struct holding all sensor info and
+ * calculated threshold values for the fru#
+ */
+static thresh_sensor_t *
+get_struct_thresh_sensor(uint8_t fru) {
+
+ thresh_sensor_t *snr;
+
+#ifdef CONFIG_YOSEMITE
+ switch (fru) {
+ case FRU_SLOT1:
+ snr = snr_slot1;
+ break;
+ case FRU_SLOT2:
+ snr = snr_slot2;
+ break;
+ case FRU_SLOT3:
+ snr = snr_slot3;
+ break;
+ case FRU_SLOT4:
+ snr = snr_slot4;
+ break;
+ case FRU_SPB:
+ snr = snr_spb;
+ break;
+ case FRU_NIC:
+ snr = snr_nic;
+ break;
+ default:
+ syslog(LOG_ALERT, "get_struct_thresh_sensor: Wrong FRU ID %d\n", fru);
+ return NULL;
+ }
+#endif /* CONFIG_YOSEMITE */
+
+ return snr;
+}
+
+
+/* Returns the all the SDRs for the particular fru# */
+static sensor_info_t *
+get_struct_sensor_info(uint8_t fru) {
+
+ sensor_info_t *sinfo;
+
+#ifdef CONFIG_YOSEMITE
+ switch (fru) {
+ case FRU_SLOT1:
+ sinfo = sinfo_slot1;
+ break;
+ case FRU_SLOT2:
+ sinfo = sinfo_slot2;
+ break;
+ case FRU_SLOT3:
+ sinfo = sinfo_slot3;
+ break;
+ case FRU_SLOT4:
+ sinfo = sinfo_slot4;
+ break;
+ case FRU_SPB:
+ sinfo = sinfo_spb;
+ break;
+ case FRU_NIC:
+ sinfo = sinfo_nic;
+ break;
+ default:
+ syslog(LOG_ALERT, "get_struct_sensor_info: Wrong FRU ID %d\n", fru);
+ return NULL;
+ }
+#endif /* CONFIG_YOSEMITE */
+
+ return sinfo;
+}
+
+
+/* Returns the SDR for a particular sensor of particular fru# */
+static sdr_full_t *
+get_struct_sdr(uint8_t fru, uint8_t snr_num) {
+
+ sdr_full_t *sdr;
+ sensor_info_t *sinfo;
+ sinfo = get_struct_sensor_info(fru);
+ if (sinfo == NULL) {
+ syslog(LOG_ALERT, "get_struct_sdr: get_struct_sensor_info failed\n");
+ return NULL;
+ }
+ sdr = &sinfo[snr_num].sdr;
+ return sdr;
+}
+
+/* Get the threshold values from the SDRs */
+static int
+get_sdr_thresh_val(uint8_t fru, uint8_t snr_num, uint8_t thresh, void *value) {
+
+ int ret;
+ uint8_t x;
+ uint8_t m_lsb, m_msb, m;
+ uint8_t b_lsb, b_msb, b;
+ int8_t b_exp, r_exp;
+ uint8_t thresh_val;
+ sdr_full_t *sdr;
+
+ sdr = get_struct_sdr(fru, snr_num);
+ if (sdr == NULL) {
+ syslog(LOG_ALERT, "get_sdr_thresh_val: get_struct_sdr failed\n");
+ return -1;
+ }
+
+ switch (thresh) {
+ case UCR_THRESH:
+ thresh_val = sdr->uc_thresh;
+ break;
+ case UNC_THRESH:
+ thresh_val = sdr->unc_thresh;
+ break;
+ case UNR_THRESH:
+ thresh_val = sdr->unr_thresh;
+ break;
+ case LCR_THRESH:
+ thresh_val = sdr->lc_thresh;
+ break;
+ case LNC_THRESH:
+ thresh_val = sdr->lnc_thresh;
+ break;
+ case LNR_THRESH:
+ thresh_val = sdr->lnr_thresh;
+ break;
+ case POS_HYST:
+ thresh_val = sdr->pos_hyst;
+ break;
+ case NEG_HYST:
+ thresh_val = sdr->neg_hyst;
+ break;
+ default:
+ syslog(LOG_ERR, "get_sdr_thresh_val: reading unknown threshold val");
+ return -1;
+ }
+
+ // y = (mx + b * 10^b_exp) * 10^r_exp
+ x = thresh_val;
+
+ m_lsb = sdr->m_val;
+ m_msb = sdr->m_tolerance >> 6;
+ m = (m_msb << 8) | m_lsb;
+
+ b_lsb = sdr->b_val;
+ b_msb = sdr->b_accuracy >> 6;
+ b = (b_msb << 8) | b_lsb;
+
+ // exponents are 2's complement 4-bit number
+ b_exp = sdr->rb_exp & 0xF;
+ if (b_exp > 7) {
+ b_exp = (~b_exp + 1) & 0xF;
+ b_exp = -b_exp;
+ }
+ r_exp = (sdr->rb_exp >> 4) & 0xF;
+ if (r_exp > 7) {
+ r_exp = (~r_exp + 1) & 0xF;
+ r_exp = -r_exp;
+ }
+
+ * (float *) value = ((m * x) + (b * pow(10, b_exp))) * (pow(10, r_exp));
+
+ return 0;
+}
+
+/*
+ * Populate all fields of thresh_sensor_t struct for a particular sensor.
+ * Incase the threshold value is 0 mask the check for that threshvold
+ * value in flag field.
+ */
+int
+init_snr_thresh(uint8_t fru, uint8_t snr_num, uint8_t flag) {
+
+ int value;
+ float fvalue;
+ uint8_t op, modifier;
+ thresh_sensor_t *snr;
+
+ sdr_full_t *sdr;
+
+ sdr = get_struct_sdr(fru, snr_num);
+ if (sdr == NULL) {
+ syslog(LOG_ALERT, "init_snr_name: get_struct_sdr failed\n");
+ return -1;
+ }
+
+ snr = get_struct_thresh_sensor(fru);
+ if (snr == NULL) {
+ syslog(LOG_ALERT, "init_snr_thresh: get_struct_thresh_sensor failed");
+ return -1;
+ }
+
+ snr[snr_num].flag = flag;
+ snr[snr_num].curr_state = NORMAL_STATE;
+
+ if (sdr_get_sensor_name(sdr, snr[snr_num].name)) {
+ syslog(LOG_ALERT, "sdr_get_sensor_name: FRU %d: num: 0x%X: reading name"
+ " from SDR failed.", fru, snr_num);
+ return -1;
+ }
+
+ // TODO: Add support for modifier (Mostly modifier is zero)
+ if (sdr_get_sensor_units(sdr, &op, &modifier, snr[snr_num].units)) {
+ syslog(LOG_ALERT, "sdr_get_sensor_units: FRU %d: num 0x%X: reading units"
+ " from SDR failed.", fru, snr_num);
+ return -1;
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, UCR_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, UCR_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].ucr_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, UCR_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, UCR_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, UNC_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, UNC_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].unc_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, UNC_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, UNC_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, UNR_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, UNR_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].unr_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, UNR_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, UNR_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, LCR_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, LCR_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].lcr_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, LCR_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, LCR_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, LNC_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, LNC_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].lnc_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, LNC_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, LNC_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, LNR_THRESH, &fvalue)) {
+ syslog(LOG_ERR,
+ "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, LNR_THRESH",
+ fru, snr_num, snr[snr_num].name);
+ } else {
+ snr[snr_num].lnr_thresh = fvalue;
+ if (!(fvalue)) {
+ snr[snr_num].flag = CLEARBIT(snr[snr_num].flag, LNR_THRESH);
+ syslog(LOG_ALERT,
+ "FRU: %d, num: 0x%X, %-16s, LNR_THRESH check disabled val->%.2f",
+ fru, snr_num, snr[snr_num].name, fvalue);
+ }
+ }
+
+ if (get_sdr_thresh_val(fru, snr_num, POS_HYST, &fvalue)) {
+ syslog(LOG_ERR, "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, POS_HYST",
+ fru, snr_num, snr[snr_num].name);
+ } else
+ snr[snr_num].pos_hyst = fvalue;
+
+ if (get_sdr_thresh_val(fru, snr_num, NEG_HYST, &fvalue)) {
+ syslog(LOG_ERR, "get_sdr_thresh_val: failed for FRU: %d, num: 0x%X, %-16s, NEG_HYST",
+ fru, snr_num, snr[snr_num].name);
+ } else
+ snr[snr_num].neg_hyst = fvalue;
+
+ return 0;
+}
+
+#ifdef CONFIG_YOSEMITE
+/* Initialize all thresh_sensor_t structs for all the Yosemite sensors */
+static void
+init_yosemite_snr_thresh(uint8_t fru) {
+
+ int i;
+
+ switch (fru) {
+ case FRU_SLOT1:
+ case FRU_SLOT2:
+ case FRU_SLOT3:
+ case FRU_SLOT4:
+
+ for (i < 0; i < bic_sensor_cnt; i++) {
+ init_snr_thresh(fru, bic_sensor_list[i], GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+ }
+ /*
+ init_snr_thresh(fru, BIC_SENSOR_MB_OUTLET_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCCIN_VR_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_GBE_VR_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_1V05PCH_VR_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_MB_INLET_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_PCH_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_THERM_MARGIN, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VDDR_VR_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_GBE_VR_CURR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_1V05_PCH_VR_CURR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCCIN_VR_POUT, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCCIN_VR_CURR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCCIN_VR_VOL,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_INA230_POWER, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_PACKAGE_PWR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_TJMAX, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VDDR_VR_POUT, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VDDR_VR_CURR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VDDR_VR_VOL,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_SCSUS_VR_CURR, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_SCSUS_VR_VOL,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_SCSUS_VR_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_SCSUS_VR_POUT, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_GBE_VR_POUT, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_VCC_GBE_VR_VOL,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_1V05_PCH_VR_VOL,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_DIMMA0_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_DIMMA1_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_DIMMB0_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_SOC_DIMMB1_TEMP, GETMASK(UCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_P3V3_MB,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_P12V_MB,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_P1V05_PCH,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_P3V3_STBY_MB,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_P5V_STBY_MB,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_PV_BAT,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_PVDDR,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+
+ init_snr_thresh(fru, BIC_SENSOR_PVCC_GBE,
+ GETMASK(UCR_THRESH) | GETMASK(LCR_THRESH));
+ // TODO: Add Support for Discrete sensors
+ // init_snr_thresh(fru, BIC_SENSOR_POST_ERR, //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_SYSTEM_STATUS, //Discrete
+ // init_snr_thresh(fru, BIC_SENSOR_SPS_FW_HLTH); //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_POWER_THRESH_EVENT, //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_MACHINE_CHK_ERR, //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_PCIE_ERR); //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_OTHER_IIO_ERR); //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_PROC_HOT_EXT); //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_POWER_ERR); //Event-only
+ // init_snr_thresh(fru, , ); //Event-only
+ // init_snr_thresh(fru, BIC_SENSOR_PROC_FAIL); //Discrete
+ // init_snr_thresh(fru, BIC_SENSOR_SYS_BOOT_STAT ); //Discrete
+ // init_snr_thresh(fru, BIC_SENSOR_VR_HOT); //Discrete
+ // init_snr_thresh(fru, BIC_SENSOR_CPU_DIMM_HOT ); //Discrete
+ // init_snr_thresh(fru, BIC_SENSOR_CAT_ERR, //Event-only
+
+ */
+
+ break;
+
+ case FRU_SPB:
+ // TODO: Add support for threshold calculation for SP sensors
+ /*
+ init_snr_thresh(fru, SP_SENSOR_INLET_TEMP, "SP_SENSOR_INLET_TEMP");
+ init_snr_thresh(fru, SP_SENSOR_OUTLET_TEMP, "SP_SENSOR_OUTLET_TEMP");
+ init_snr_thresh(fru, SP_SENSOR_MEZZ_TEMP, "SP_SENSOR_MEZZ_TEMP");
+ init_snr_thresh(fru, SP_SENSOR_FAN0_TACH, "SP_SENSOR_FAN0_TACH");
+ init_snr_thresh(fru, SP_SENSOR_FAN1_TACH, "SP_SENSOR_FAN1_TACH");
+ init_snr_thresh(fru, SP_SENSOR_AIR_FLOW, "SP_SENSOR_AIR_FLOW");
+ init_snr_thresh(fru, SP_SENSOR_P5V, "SP_SENSOR_P5V");
+ init_snr_thresh(fru, SP_SENSOR_P12V, "SP_SENSOR_P12V");
+ init_snr_thresh(fru, SP_SENSOR_P3V3_STBY, "SP_SENSOR_P3V3_STBY");
+ init_snr_thresh(fru, SP_SENSOR_P12V_SLOT0, "SP_SENSOR_P12V_SLOT0");
+ init_snr_thresh(fru, SP_SENSOR_P12V_SLOT1, "SP_SENSOR_P12V_SLOT1");
+ init_snr_thresh(fru, SP_SENSOR_P12V_SLOT2, "SP_SENSOR_P12V_SLOT2");
+ init_snr_thresh(fru, SP_SENSOR_P12V_SLOT3, "SP_SENSOR_P12V_SLOT3");
+ init_snr_thresh(fru, SP_SENSOR_P3V3, "SP_SENSOR_P3V3");
+ init_snr_thresh(fru, SP_SENSOR_HSC_IN_VOLT, "SP_SENSOR_HSC_IN_VOLT");
+ init_snr_thresh(fru, SP_SENSOR_HSC_OUT_CURR, "SP_SENSOR_HSC_OUT_CURR");
+ init_snr_thresh(fru, SP_SENSOR_HSC_TEMP, "SP_SENSOR_HSC_TEMP");
+ init_snr_thresh(fru, SP_SENSOR_HSC_IN_POWER, "SP_SENSOR_HSC_IN_POWER");
+ */
+ break;
+
+ case FRU_NIC:
+ // TODO: Add support for NIC sensor threshold, if any.
+ break;
+
+ default:
+ syslog(LOG_ALERT, "init_yosemite_snr_thresh: wrong FRU ID");
+ exit(-1);
+ }
+}
+#endif /* CONFIG_YOSEMITE */
+
+/* Wrapper function to initialize all the platform sensors */
+static void
+init_all_snr_thresh() {
+ int fru;
+
+ char path[64] = {0};
+ sensor_info_t *sinfo;
+
+
+#ifdef CONFIG_YOSEMITE
+ for (fru = FRU_SLOT1; fru < (FRU_SLOT1 + MAX_NUM_FRUS); fru++) {
+
+ if (get_fru_sdr_path(fru, path) < 0) {
+ syslog(LOG_ALERT, "yosemite_sdr_init: get_fru_sdr_path failed\n");
+ continue;
+ }
+#endif /* CONFIG_YOSEMITE */
+
+ sinfo = get_struct_sensor_info(fru);
+
+ if (sdr_init(path, sinfo) < 0)
+ syslog(LOG_ERR, "init_all_snr_thresh: sdr_init failed for FRU %d", fru);
+ }
+
+#ifdef CONFIG_YOSEMITE
+ for (fru = FRU_SLOT1; fru < (FRU_SLOT1 + MAX_NUM_FRUS); fru++) {
+ init_yosemite_snr_thresh(fru);
+ }
+#endif /* CONFIG_YOSEMITE */
+}
+
+
+
+
+static float
+get_snr_thresh_val(uint8_t fru, uint8_t snr_num, uint8_t thresh) {
+
+ float val;
+ thresh_sensor_t *snr;
+
+ snr = get_struct_thresh_sensor(fru);
+
+ switch (thresh) {
+ case UCR_THRESH:
+ val = snr[snr_num].ucr_thresh;
+ break;
+ case UNC_THRESH:
+ val = snr[snr_num].unc_thresh;
+ break;
+ case UNR_THRESH:
+ val = snr[snr_num].unr_thresh;
+ break;
+ case LCR_THRESH:
+ val = snr[snr_num].lcr_thresh;
+ break;
+ case LNC_THRESH:
+ val = snr[snr_num].lnc_thresh;
+ break;
+ case LNR_THRESH:
+ val = snr[snr_num].lnr_thresh;
+ break;
+ default:
+ syslog(LOG_ALERT, "get_snr_thresh_val: wrong thresh enum value");
+ exit(-1);
+ }
+
+ return val;
+}
+
+/*
+ * Check the curr sensor values against the threshold and
+ * if the curr val has deasserted, log it.
+ */
+static void
+check_thresh_deassert(uint8_t fru, uint8_t snr_num, uint8_t thresh,
+ float curr_val) {
+ uint8_t curr_state = 0;
+ float thresh_val;
+ char thresh_name[100];
+ thresh_sensor_t *snr;
+
+ snr = get_struct_thresh_sensor(fru);
+
+ if (!GETBIT(snr[snr_num].flag, thresh) ||
+ !GETBIT(snr[snr_num].curr_state, thresh))
+ return;
+
+ thresh_val = get_snr_thresh_val(fru, snr_num, thresh);
+
+ switch (thresh) {
+ case UNC_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, UNR_THRESH) |
+ SETBIT(curr_state, UCR_THRESH) |
+ SETBIT(curr_state, UNC_THRESH));
+ sprintf(thresh_name, "Upper Non Critical");
+ }
+ break;
+
+ case UCR_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, UCR_THRESH) |
+ SETBIT(curr_state, UNR_THRESH));
+ sprintf(thresh_name, "Upper Critical");
+ }
+ break;
+
+ case UNR_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, UNR_THRESH));
+ sprintf(thresh_name, "Upper Non Recoverable");
+ }
+ break;
+
+ case LNC_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, LNR_THRESH) |
+ SETBIT(curr_state, LCR_THRESH) |
+ SETBIT(curr_state, LNC_THRESH));
+ sprintf(thresh_name, "Lower Non Critical");
+ }
+ break;
+
+ case LCR_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, LCR_THRESH) |
+ SETBIT(curr_state, LNR_THRESH));
+ sprintf(thresh_name, "Lower Critical");
+ }
+ break;
+
+ case LNR_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = ~(SETBIT(curr_state, LNR_THRESH));
+ sprintf(thresh_name, "Lower Non Recoverable");
+ }
+ break;
+
+ default:
+ syslog(LOG_ALERT, "get_snr_thresh_val: wrong thresh enum value");
+ exit(-1);
+ }
+
+ if (curr_state) {
+ snr[snr_num].curr_state &= curr_state;
+ syslog(LOG_CRIT, "DEASSERT: %s threshold raised - FRU: %d, num: 0x%X,"
+ " snr: %-16s,",thresh_name, fru, snr_num, snr[snr_num].name);
+ syslog(LOG_CRIT, "curr_val: %.2f %s, thresh_val: %.2f %s cf: %u",
+ curr_val, snr[snr_num].units, thresh_val, snr[snr_num].units, snr[snr_num].curr_state);
+ }
+}
+
+
+/*
+ * Check the curr sensor values against the threshold and
+ * if the curr val has asserted, log it.
+ */
+static void
+check_thresh_assert(uint8_t fru, uint8_t snr_num, uint8_t thresh,
+ float curr_val) {
+ uint8_t curr_state = 0;
+ float thresh_val;
+ char thresh_name[100];
+ thresh_sensor_t *snr;
+
+ snr = get_struct_thresh_sensor(fru);
+
+ if (!GETBIT(snr[snr_num].flag, thresh) ||
+ GETBIT(snr[snr_num].curr_state, thresh))
+ return;
+
+ thresh_val = get_snr_thresh_val(fru, snr_num, thresh);
+
+ switch (thresh) {
+ case UNR_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = (SETBIT(curr_state, UNR_THRESH) |
+ SETBIT(curr_state, UCR_THRESH) |
+ SETBIT(curr_state, UNC_THRESH));
+ sprintf(thresh_name, "Upper Non Recoverable");
+ }
+ break;
+
+ case UCR_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = (SETBIT(curr_state, UCR_THRESH) |
+ SETBIT(curr_state, UNC_THRESH));
+ sprintf(thresh_name, "Upper Critical");
+ }
+ break;
+
+ case UNC_THRESH:
+ if (curr_val >= thresh_val) {
+ curr_state = (SETBIT(curr_state, UNC_THRESH));
+ sprintf(thresh_name, "Upper Non Critical");
+ }
+ break;
+
+ case LNR_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = (SETBIT(curr_state, LNR_THRESH) |
+ SETBIT(curr_state, LCR_THRESH) |
+ SETBIT(curr_state, LNC_THRESH));
+ sprintf(thresh_name, "Lower Non Recoverable");
+ }
+ break;
+
+ case LCR_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = (SETBIT(curr_state, LCR_THRESH) |
+ SETBIT(curr_state, LNC_THRESH));
+ sprintf(thresh_name, "Lower Critical");
+ }
+ break;
+
+ case LNC_THRESH:
+ if (curr_val <= thresh_val) {
+ curr_state = (SETBIT(curr_state, LNC_THRESH));
+ sprintf(thresh_name, "Lower Non Critical");
+ }
+ break;
+
+ default:
+ syslog(LOG_ALERT, "get_snr_thresh_val: wrong thresh enum value");
+ exit(-1);
+ }
+
+ if (curr_state) {
+ curr_state &= snr[snr_num].flag;
+ snr[snr_num].curr_state |= curr_state;
+ syslog(LOG_CRIT, "ASSERT: %s threshold raised - FRU: %d, num: 0x%X,"
+ " snr: %-16s,",thresh_name, fru, snr_num, snr[snr_num].name);
+ syslog(LOG_CRIT, "curr_val: %.2f %s, thresh_val: %.2f %s cf: %u",
+ curr_val, snr[snr_num].units, thresh_val, snr[snr_num].units, snr[snr_num].curr_state);
+ }
+}
+
+/*
+ * Starts monitoring all the sensors on a fru for all the threshold values.
+ * Each pthread runs this monitoring for a different fru.
+ */
+static void *
+snr_monitor(void *arg) {
+
+ uint8_t fru = *(uint8_t *) arg;
+ int f, ret, snr_num;
+ float normal_val, curr_val;
+ thresh_sensor_t *snr;
+
+#ifdef TESTING
+ float temp_thresh;
+ int cnt = 0;
+#endif /* TESTING */
+
+ snr = get_struct_thresh_sensor(fru);
+ if (snr == NULL) {
+ syslog(LOG_ALERT, "snr_monitor: get_struct_thresh_sensor failed");
+ exit(-1);
+ }
+
+ while(1) {
+
+#ifdef TESTING
+ cnt++;
+#endif /* TESTING */
+
+ for (snr_num = 0; snr_num < MAX_SENSOR_NUM; snr_num++) {
+ curr_val = 0;
+ if (snr[snr_num].flag) {
+ if (!(ret = read_snr_val(fru, snr_num, &curr_val))) {
+
+
+#ifdef TESTING
+ /*
+ * The curr_val crosses UCR and then return to a state
+ * where UNC < curr_val < UCR and then eventually back to normal.
+ */
+ if (cnt == 5 && snr_num == BIC_SENSOR_MB_INLET_TEMP) {
+ snr[snr_num].flag |= SETBIT(snr[snr_num].flag, UNC_THRESH);
+ temp_thresh = snr[snr_num].ucr_thresh;
+ snr[snr_num].ucr_thresh = 20.0;
+ snr[snr_num].unc_thresh = 10.0;
+ } else if (cnt == 8 && snr_num == BIC_SENSOR_MB_INLET_TEMP) {
+ snr[snr_num].ucr_thresh = temp_thresh;
+ } else if (cnt == 10 && snr_num == BIC_SENSOR_MB_INLET_TEMP) {
+ snr[snr_num].unc_thresh = 50.0;
+ } else if (cnt == 11 && snr_num == BIC_SENSOR_MB_INLET_TEMP) {
+ snr[snr_num].unc_thresh = 0.0;
+ snr[snr_num].flag &= CLEARBIT(snr[snr_num].flag, UNC_THRESH);
+
+ }
+#endif /* TESTING */
+
+#ifdef DEBUG
+ if (cnt == 2) {
+ syslog(LOG_INFO, "pthread %d, cnt: %d, num: 0x%X name: %-16s"
+ " units:%s", fru, cnt, snr_num, snr[snr_num].name,
+ snr[snr_num].units);
+ }
+#endif /* DEBUG */
+
+ check_thresh_assert(fru, snr_num, UNR_THRESH, curr_val);
+ check_thresh_assert(fru, snr_num, UCR_THRESH, curr_val);
+ check_thresh_assert(fru, snr_num, UNC_THRESH, curr_val);
+ check_thresh_assert(fru, snr_num, LNR_THRESH, curr_val);
+ check_thresh_assert(fru, snr_num, LCR_THRESH, curr_val);
+ check_thresh_assert(fru, snr_num, LNC_THRESH, curr_val);
+
+ check_thresh_deassert(fru, snr_num, UNC_THRESH, curr_val);
+ check_thresh_deassert(fru, snr_num, UCR_THRESH, curr_val);
+ check_thresh_deassert(fru, snr_num, UNR_THRESH, curr_val);
+ check_thresh_deassert(fru, snr_num, LNC_THRESH, curr_val);
+ check_thresh_deassert(fru, snr_num, LCR_THRESH, curr_val);
+ check_thresh_deassert(fru, snr_num, LNR_THRESH, curr_val);
+
+ } else {
+ /*
+ * Incase the read_snr_val failed for a sensor,
+ * disable all the threshold checks for that sensor
+ * after logging an approciate syslog message.
+ */
+ if (ret) {
+ syslog(LOG_ERR, "FRU: %d, num: 0x%X, snr:%-16s, read failed",
+ fru, snr_num, snr[snr_num].name);
+ syslog(LOG_ERR, "FRU: %d, num: 0x%X, snr:%-16s, check disabled",
+ fru, snr_num, snr[snr_num].name);
+ snr[snr_num].flag = 0;
+ //}
+ }
+ } /* read_snr_val return check */
+ } /* flag check */
+ } /* loop for all sensors */
+ sleep(5);
+ } /* while loop*/
+} /* function definition */
+
+
+/* Spawns a pthread for each fru to monitor all the sensors on it */
+static void
+run_sensord(int argc, char **argv) {
+ int i, arg;
+
+ pthread_t thread_snr[MAX_NUM_FRUS];
+ int fru[MAX_NUM_FRUS] = {0};
+
+ for (arg = 1; arg < argc; arg ++) {
+#ifdef CONFIG_YOSEMITE
+ if (!(strcmp(argv[arg], "slot1")))
+ fru[FRU_SLOT1 - 1] = FRU_SLOT1;
+ else if (!(strcmp(argv[arg], "slot2")))
+ fru[FRU_SLOT2 - 1] = FRU_SLOT2;
+ else if (!(strcmp(argv[arg], "slot3")))
+ fru[FRU_SLOT3 - 1] = FRU_SLOT3;
+ else if (!(strcmp(argv[arg], "slot4")))
+ fru[FRU_SLOT4 - 1] = FRU_SLOT4;
+ else if (!(strcmp(argv[arg], "spb")))
+ fru[FRU_SPB - 1] = FRU_SPB;
+ else if (!(strcmp(argv[arg], "nic")))
+ fru[FRU_NIC - 1] = FRU_NIC;
+ else {
+ syslog(LOG_ALERT, "Wrong argument: %s", argv[arg]);
+ exit(1);
+ }
+#endif /* CONFIG_YOSEMITE */
+
+ }
+
+ for (i = 0; i < MAX_NUM_FRUS; i++) {
+ if (fru[i]) {
+ if (pthread_create(&thread_snr[i], NULL, snr_monitor,
+ (void*) &fru[i]) < 0) {
+ syslog(LOG_ALERT, "pthread_create for FRU %d failed\n", fru[i]);
+ }
+#ifdef DEBUG
+ else {
+ syslog(LOG_ALERT, "pthread_create for FRU %d succeed\n", fru[i]);
+ }
+#endif /* DEBUG */
+ }
+ sleep(1);
+ }
+
+ for (i = 0; i < MAX_NUM_FRUS; i++) {
+ if (fru[i])
+ pthread_join(thread_snr[i], NULL);
+ }
+}
+
+
+#ifdef DEBUG
+void print_snr_thread(uint8_t fru, thresh_sensor_t *snr)
+{
+ int i;
+ float curr_val;
+
+ for (i = 1; i <= MAX_SENSOR_NUM; i++) {
+ if (snr[i].flag) {
+ curr_val = 0;
+ if(!(read_snr_val(fru, i, &curr_val))) {
+ printf("%-30s:\t%.2f %s\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
+ snr[i].name, curr_val,snr[i].units,
+ snr[i].ucr_thresh,
+ snr[i].unc_thresh,
+ snr[i].lcr_thresh,
+ snr[i].lnr_thresh,
+ snr[i].pos_hyst,
+ snr[i].neg_hyst);
+ } else
+ printf("Reading failed: %-16s\n", snr[i].name);
+ }
+ }
+}
+#endif /* DEBUG */
+
+int
+main(int argc, void **argv) {
+ int dev, rc, pid_file;
+
+ if (argc < 2) {
+ syslog(LOG_ALERT, "Usage: sensord <options>");
+ printf("Usage: sensord <options>\n");
+#ifdef CONFIG_YOSEMITE
+ syslog(LOG_ALERT, "Options: [slot1 | slot2 | slot3 | slot4 | spb | nic]");
+ printf("Options: [slot1 | slot2 | slot3 | slot4 | spb | nic]\n");
+#endif /* CONFIG_YOSEMITE */
+ exit(1);
+ }
+
+ pid_file = open("/var/run/sensord.pid", O_CREAT | O_RDWR, 0666);
+ rc = flock(pid_file, LOCK_EX | LOCK_NB);
+ if(rc) {
+ if(EWOULDBLOCK == errno) {
+ printf("Another sensord instance is running...\n");
+ exit(-1);
+ }
+ } else {
+
+#ifdef CONFIG_YOSEMITE
+ read_snr_val = &yosemite_sensor_read;
+#endif /* CONFIG_YOSEMITE */
+
+ init_all_snr_thresh();
+
+#ifdef DEBUG
+ print_snr_thread(1, snr_slot1);
+ print_snr_thread(2, snr_slot2);
+ print_snr_thread(3, snr_slot3);
+ print_snr_thread(4, snr_slot4);
+#endif /* DEBUG */
+
+ daemon(0,1);
+ openlog("sensord", LOG_CONS, LOG_DAEMON);
+ syslog(LOG_INFO, "sensord: daemon started");
+ run_sensord(argc, (char **) argv);
+ }
+
+ return 0;
+}
diff --git a/common/recipes-core/sensor-mon/sensor-mon_0.1.bb b/common/recipes-core/sensor-mon/sensor-mon_0.1.bb
new file mode 100644
index 0000000..adbd4cb
--- /dev/null
+++ b/common/recipes-core/sensor-mon/sensor-mon_0.1.bb
@@ -0,0 +1,58 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+SUMMARY = "Sensor Monitoring Daemon"
+DESCRIPTION = "Daemon for monitoring the sensors"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://sensord.c;beginline=4;endline=16;md5=b395943ba8a0717a83e62ca123a8d238"
+
+SRC_URI = "file://Makefile \
+ file://sensord.c \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "sensord \
+ "
+
+CFLAGS += " -lsdr "
+
+DEPENDS += " libsdr "
+
+pkgdir = "sensor-mon"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/sensor-mon ${prefix}/local/bin"
+
+# Inhibit complaints about .debug directories for the sensord binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/sensor-util/files/Makefile b/common/recipes-core/sensor-util/files/Makefile
new file mode 100644
index 0000000..2d39a04
--- /dev/null
+++ b/common/recipes-core/sensor-util/files/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+all: sensor-util
+
+
+sensor-util: sensor-util.o
+ $(CC) $(CFLAGS) -lpal -lrt -lm -std=gnu99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o sensor-util
diff --git a/common/recipes-core/sensor-util/files/sensor-util.c b/common/recipes-core/sensor-util/files/sensor-util.c
new file mode 100644
index 0000000..eb38d65
--- /dev/null
+++ b/common/recipes-core/sensor-util/files/sensor-util.c
@@ -0,0 +1,173 @@
+/*
+ * yosemite-sensors
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <openbmc/pal.h>
+
+static int
+print_usage() {
+ printf("Usage: sensor-util [ %s ] <sensor num>\n"
+ "sensor num is optional.", pal_fru_list);
+}
+
+static void
+print_single_sensor_reading(uint8_t fru, uint8_t *sensor_list, int sensor_cnt, uint8_t num) {
+
+ int i;
+ float fvalue;
+ char name[24];
+ char units[64];
+ int ret = 0;
+
+ for (i = 0; i < sensor_cnt; i++) {
+
+ if (sensor_list[i] == num) {
+ ret = 1;
+ pal_get_sensor_name(fru, sensor_list[i], name);
+ pal_get_sensor_units(fru, sensor_list[i], units);
+
+ if (pal_sensor_read(fru, sensor_list[i], &fvalue) < 0) {
+
+ printf("pal_sensor_read failed: fru: %d num: 0x%X name: %-23s\n",
+ fru, sensor_list[i], name);
+ } else {
+ printf("%-23s: %.2f %s\n", name, fvalue, units);
+ }
+
+ break;
+ }
+ }
+
+ if (!ret) {
+ printf("Wrong sensor number!\n");
+ print_usage();
+ exit(-1);
+ }
+}
+
+static void
+print_sensor_reading(uint8_t fru, uint8_t *sensor_list, int sensor_cnt) {
+
+ int i;
+ float fvalue;
+ char name[24];
+ char units[64];
+
+ for (i = 0; i < sensor_cnt; i++) {
+
+ /* Clear the variable */
+ sprintf(name, "");
+ sprintf(units, "");
+
+ pal_get_sensor_name(fru, sensor_list[i], name);
+ pal_get_sensor_units(fru, sensor_list[i], units);
+
+ if (pal_sensor_read(fru, sensor_list[i], &fvalue) < 0) {
+
+ printf("pal_sensor_read failed: fru: %d num: 0x%X name: %-23s\n",
+ fru, sensor_list[i], name);
+ } else {
+ printf("%-23s: %.2f %s\n", name, fvalue, units);
+ }
+ }
+}
+
+int
+main(int argc, char **argv) {
+
+ int ret;
+ int sensor_cnt;
+ uint8_t *sensor_list;
+ uint8_t fru;
+ uint8_t num;
+
+ if (argc < 2 || argc > 3) {
+ print_usage();
+ exit(-1);
+ }
+
+ if (argc == 3) {
+ errno = 0;
+ num = (uint8_t) strtol(argv[2], NULL, 0);
+ if (errno) {
+ printf("Sensor number format incorrect.\n");
+ print_usage();
+ exit(-1);
+ }
+ }
+
+
+ ret = pal_get_fru_id(argv[1], &fru);
+ if (ret < 0) {
+ print_usage();
+ return ret;
+ }
+
+ if (fru == 0) {
+ fru = 1;
+ while (fru <= MAX_NUM_FRUS) {
+
+ if (fru == FRU_NIC) {
+ printf("\nsensor-util does not support nic\n");
+ exit(-1);
+ }
+
+ ret = pal_get_fru_sensor_list(fru, &sensor_list, &sensor_cnt);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (num) {
+ print_single_sensor_reading(fru, sensor_list, sensor_cnt, num);
+ } else {
+ print_sensor_reading(fru, sensor_list, sensor_cnt);
+ }
+
+ fru++;
+ printf("\n");
+ }
+ } else {
+
+ if (fru == FRU_NIC) {
+ printf("\nsensor-util does not support nic\n");
+ //exit(-1);
+ }
+
+ ret = pal_get_fru_sensor_list(fru, &sensor_list, &sensor_cnt);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (num) {
+ print_single_sensor_reading(fru, sensor_list, sensor_cnt, num);
+ } else {
+ print_sensor_reading(fru, sensor_list, sensor_cnt);
+ }
+ }
+
+ return 0;
+}
diff --git a/common/recipes-core/sensor-util/sensor-util_0.1.bb b/common/recipes-core/sensor-util/sensor-util_0.1.bb
new file mode 100644
index 0000000..227ec53
--- /dev/null
+++ b/common/recipes-core/sensor-util/sensor-util_0.1.bb
@@ -0,0 +1,34 @@
+# Copyright 2015-present Facebook. All Rights Reserved.
+SUMMARY = "Sensor Utility"
+DESCRIPTION = "Util for reading various sensors"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://sensor-util.c;beginline=4;endline=16;md5=b395943ba8a0717a83e62ca123a8d238"
+
+SRC_URI = "file://Makefile \
+ file://sensor-util.c \
+ "
+S = "${WORKDIR}"
+
+binfiles = "sensor-util"
+
+DEPENDS =+ " libpal "
+
+pkgdir = "sensor-util"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 sensor-util ${dst}/sensor-util
+ ln -snf ../fbpackages/${pkgdir}/sensor-util ${bin}/sensor-util
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/sensor-util ${prefix}/local/bin"
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/common/recipes-core/watchdog-ctrl/watchdog-ctrl_0.1.bb b/common/recipes-core/watchdog-ctrl/watchdog-ctrl_0.1.bb
index 00cf0f9..14c0fee 100644
--- a/common/recipes-core/watchdog-ctrl/watchdog-ctrl_0.1.bb
+++ b/common/recipes-core/watchdog-ctrl/watchdog-ctrl_0.1.bb
@@ -1,4 +1,19 @@
# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
SUMMARY = "Watchdog control utilities."
DESCRIPTION = "The utilities to control system watchdog."
SECTION = "base"
OpenPOWER on IntegriCloud