summaryrefslogtreecommitdiffstats
path: root/common/recipes-core/ipmid/files
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-core/ipmid/files')
-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
10 files changed, 2639 insertions, 0 deletions
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__ */
OpenPOWER on IntegriCloud