common/recipes-core/ipmid/files/ipmid.c (4,051 lines of code) (raw):
/*
 *
 * 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>
#include <openbmc/kv.h>
#include <openbmc/pal.h>
#include <openbmc/pal_sensors.h>
#include <sys/reboot.h>
#include <openbmc/obmc-i2c.h>
#include <openbmc/ipc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "sensor.h"
#define MAX_REQUESTS 64
#define SIZE_IANA_ID 3
#define SIZE_GUID 16
//declare for clearing BIOS flag
#define BIOS_Timeout 600
// Boot valid flag
#define BIOS_BOOT_VALID_FLAG (1U << 7)
#define CMOS_VALID_FLAG      (1U << 1)
#define FORCE_BOOT_BIOS_SETUP_VALID_FLAG (1U << 2)
//#define CHASSIS_GET_BOOT_OPTION_SUPPORT
//#define CHASSIS_SET_BOOT_OPTION_SUPPORT
#define CONFIG_FBTP 1
#define OBMC_DUMP_STAT_KEY "obmc_dump_stat"
// PPR definition
#define PPR_MAX_ROW_COUNT 100
#define PPR_MAX_HISTORY_COUNT 100
#define PPR_ROW_ADDR_DATA_LEN 8
#define PPR_HISTORY_DATA_LEN 17
static unsigned char IsTimerStart[MAX_NODES] = {0};
static unsigned char bmc_global_enable_setting[] = {0x0c,0x0c,0x0c,0x0c};
extern void plat_lan_init(lan_config_t *lan);
// TODO: Once data storage is finalized, the following structure needs
// to be retrieved/updated from persistent backend storage
static lan_config_t g_lan_config = { 0 };
// TODO: Need to store this info after identifying proper storage
static sys_info_param_t g_sys_info_params;
// enable IPMI cmd logging
static uint8_t gLogEnable = 0;
// IPMI Watchdog Timer Structure
struct watchdog_data {
  pthread_mutex_t mutex;
  pthread_t tid;
  uint8_t slot;
  uint8_t valid;
  uint8_t run;
  uint8_t no_log;
  uint8_t use;
  uint8_t pre_action;
  uint8_t action;
  uint8_t pre_interval;
  uint8_t expiration;
  uint16_t init_count_down;
  uint16_t present_count_down;
};
static struct watchdog_data *g_wdt[MAX_NUM_FRUS];
static char* wdt_use_name[8] = {
  "reserved",
  "BIOS FRB2",
  "BIOS/POST",
  "OS Load",
  "SMS/OS",
  "OEM",
  "reserved",
  "reserved",
};
static char* wdt_action_name[8] = {
  "Timer expired",
  "Hard Reset",
  "Power Down",
  "Power Cycle",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
};
static char *cpu_info_key[] =
{
  "",
  "product_name",
  "basic_info",
  "type",
  "micro_code",
  "turbo_mode"
};
static char *dimm_info_key[] =
{
  "",
  "location",
  "type",
  "speed",
  "part_name",
  "serial_num",
  "manufacturer_id",
  "status",
  "present_bit"
};
static char *drive_info_key[] =
{
  "location",
  "serial_num",
  "model_name",
  "fw_version",
  "capacity",
  "quantity",
  "type",
  "wwn"
};
// obmc-dump status
enum {
  DUMP_DONE = 0x0,
  DUMP_FAIL = 0x1,
  DUMP_NEVER = 0x2,
  DUMP_ONGOING = 0x3,
};
// 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_sensor;
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_storage;
static pthread_mutex_t m_oem_1s;
static pthread_mutex_t m_oem_usb_dbg;
static pthread_mutex_t m_oem_q;
static pthread_mutex_t m_oem_zion;
extern int plat_udbg_get_frame_info();
extern int plat_udbg_get_updated_frames(uint8_t *count, uint8_t *buffer);
extern int plat_udbg_get_post_desc(uint8_t index, uint8_t *next, uint8_t phase,  uint8_t *end, uint8_t *length, uint8_t *buffer);
extern int plat_udbg_get_gpio_desc(uint8_t index, uint8_t *next, uint8_t *level, uint8_t *def,
                            uint8_t *count, uint8_t *buffer);
extern int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t *next, uint8_t *count, uint8_t *buffer);
extern int plat_udbg_control_panel(uint8_t panel, uint8_t operation, uint8_t item, uint8_t *count, uint8_t *buffer);
static void ipmi_handle(unsigned char *request, unsigned char req_len,
       unsigned char *response, unsigned char *res_len);
extern int bbv_power_cycle(int delay_time);
static struct watchdog_data *get_watchdog(int slot_id)
{
  if (slot_id > MAX_NUM_FRUS)
    return NULL;
  return g_wdt[slot_id - 1];
}
static int length_check(unsigned char cmd_len, unsigned char req_len, unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  // req_len = cmd_len + 3 (payload_id, cmd and netfn)
  if( req_len != (cmd_len + IPMI_MN_REQ_HDR_SIZE) ){
    res->cc = CC_INVALID_LENGTH;
    *res_len = 0;
    return 1;
  }
  return 0;
}
/*
 **Function to handle with clearing BIOS flag
 */
void
*clear_bios_data_timer(void *ptr)
{
  int timer = 0;
  int slot_id = (int)ptr;
  int oldstate;
  unsigned char boot[SIZE_BOOT_ORDER]={0};
  unsigned char res_len;
  pthread_detach(pthread_self());
  while( timer <= BIOS_Timeout )
  {
#ifdef DEBUG
    syslog(LOG_WARNING, "[%s][%lu] Timer: %d\n", __func__, pthread_self(), timer);
#endif
    sleep(1);
    timer++;
  }
  //get boot order setting
  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
  pal_get_boot_order(slot_id, NULL, boot, &res_len);
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
#ifdef DEBUG
  syslog(LOG_WARNING, "[%s][%lu] Get: %x %x %x %x %x %x\n", __func__, pthread_self() ,boot[0], boot[1], boot[2], boot[3], boot[4], boot[5]);
#endif
  //clear boot-valid and cmos bits due to timeout:
  boot[0] &= ~(BIOS_BOOT_VALID_FLAG | CMOS_VALID_FLAG);
#ifdef DEBUG
  syslog(LOG_WARNING, "[%s][%lu] Set: %x %x %x %x %x %x\n", __func__, pthread_self() , boot[0], boot[1], boot[2], boot[3], boot[4], boot[5]);
#endif
  //set data
  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
  pal_set_boot_order(slot_id, boot, NULL, &res_len);
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
  IsTimerStart[slot_id - 1] = false;
  pthread_exit(0);
}
/*
 * Function(s) to handle IPMI messages with NetFn: Chassis
 */
// Get Chassis Status (IPMI/Section 28.2)
static void
chassis_get_status (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;
  res->cc = CC_SUCCESS;
  pal_get_chassis_status(req->payload_id, req->data, res->data, res_len);
}
static void
chassis_control(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 ret;
  ret = pal_chassis_control(req->payload_id, req->data, (req_len - 3));
  if (ret == PAL_ENOTSUP) {
    res->cc = CC_INVALID_CMD;
  } else {
    res->cc = ret;
  }
  *res_len = 0;
  return;
}
// Set Power Restore Policy (IPMI/Section 28.8)
static void
chassis_set_power_restore_policy(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 *data = &res->data[0];
  *data++ = 0x07;  // Power restore policy support(bitfield)
  res->cc = pal_set_power_restore_policy(req->payload_id, req->data, res->data);
  if (res->cc == CC_SUCCESS) {
    *res_len = data - &res->data[0];
  }
}
// Set Power Restore Policy (IPMI/Section 28.11)
static void
chassis_get_system_restart_cause(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 *data = &res->data[0];
  *res_len = 0;
  if (pal_get_restart_cause(req->payload_id, &data[0])) {
    res->cc = CC_UNSPECIFIED_ERROR;
  }
  data[1] = 0; // Channel number
  res->cc = CC_SUCCESS;
  *res_len = 2;
}
static void
chassis_identify(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;
  res->cc = pal_set_slot_led(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
#ifdef CHASSIS_GET_BOOT_OPTION_SUPPORT
// Get System Boot Options (IPMI/Section 28.12)
static void
chassis_get_boot_options (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 *data = &res->data[0];
  unsigned char param = req->data[0];
  if(param >= 8) {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    return;
  }
  // Fill response with default values
  res->cc = CC_SUCCESS;
  *data++ = 0x01;   // Parameter Version
  *data++ = req->data[0]; // Parameter
  *res_len = pal_get_boot_option(param, data) + 2;
}
#endif
#ifdef CHASSIS_SET_BOOT_OPTION_SUPPORT
// Set System Boot Options (IPMI/Section 28)
static void
chassis_set_boot_options (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 *data = &res->data[0];
  unsigned char param = req->data[0];
  // Fill response with default values
  if(param >= 8) {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    return;
  }
  res->cc = CC_SUCCESS;
  pal_set_boot_option(param,req->data+1);
  *res_len = data - &res->data[0];
}
#endif
// 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 (request, req_len, response, res_len);
      break;
    case CMD_CHASSIS_CONTROL:
      chassis_control(request, req_len, response, res_len);
      break;
    case CMD_CHASSIS_IDENTIFY:
      chassis_identify (request, req_len, response, res_len);
      break;
    case CMD_CHASSIS_SET_POWER_RESTORE_POLICY:
      chassis_set_power_restore_policy(request, req_len, response, res_len);
      break;
    case CMD_CHASSIS_GET_SYSTEM_RESTART_CAUSE:
      chassis_get_system_restart_cause(request, req_len, response, res_len);
      break;
#ifdef CHASSIS_GET_BOOT_OPTION_SUPPORT
    case CMD_CHASSIS_GET_BOOT_OPTIONS:
      chassis_get_boot_options(request, req_len, response, res_len);
      break;
#endif
#ifdef CHASSIS_SET_BOOT_OPTION_SUPPORT
    case CMD_CHASSIS_SET_BOOT_OPTIONS:
      chassis_set_boot_options(request, req_len, response, res_len);
      break;
#endif
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  pthread_mutex_unlock(&m_chassis);
}
/*
 * Function(s) to handle IPMI messages with NetFn: Sensor
 */
// Platform Event Message (IPMI/Section 29.3)
static void
sensor_plat_event_msg(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 record_id;    // Record ID for added entry
  int ret;
  sel_msg_t entry;
  entry.msg[2] = 0x02;  /* Set Record Type to be system event record.*/
  if (req_len == 11) { // For messaging from system interface
    entry.msg[7] = req->data[0];  //Store Generator ID
    memcpy(&entry.msg[9], req->data + 1, 7);
  } else {
    memcpy(&entry.msg[9], req->data, 7);  // Platform event provides only last 7 bytes of SEL's 16-byte entry
  }
  // Use platform APIs to add the new SEL entry
  ret = sel_add_entry (req->payload_id, &entry, &record_id);
  if (ret)
  {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  res->cc = CC_SUCCESS;
}
// Alert Immediate Command (IPMI/Section 30.7)
static void
sensor_alert_immediate_msg(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 record_id;    // Record ID for added entry
  int ret;
  sel_msg_t entry;
  entry.msg[2] = 0x02;  /* Set Record Type to be system event record.*/
  memcpy(&entry.msg[10], &req->data[5], 6);
  // Use platform APIs to add the new SEL entry
  ret = sel_add_entry (req->payload_id, &entry, &record_id);
  if (ret)
  {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  res->cc = CC_SUCCESS;
}
// Set sensor reading (IPMI/Section 35.17)
static void
sensor_set_reading(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;
  uint8_t sensor_num;
  uint8_t flags;
  float value;
  bool available = true;
  // We do not support the command in full.
  if (length_check(3, req_len, response, res_len))
    return;
  sensor_num = req->data[0];
  flags = req->data[1];
  value = req->data[2];
  // We support the case only when flags == 1 (write given value
  // to sensor reading byte ([1:0] - sensor reading operation))
  if (flags != 0x1) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    return;
  }
  if (!pal_sensor_is_source_host(req->payload_id, sensor_num)) {
    res->cc = CC_INVALID_PARAM;
    return;
  }
  // may use NVMe-MI CTemp spec or other spec to decode cache send from IPMI command
  if (pal_correct_sensor_reading_from_cache(req->payload_id, sensor_num, &value) != 0) {
    available = false;
  }
  if (sensor_cache_write(req->payload_id, sensor_num, available, value)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  res->cc = CC_SUCCESS;
}
// Handle Sensor/Event Commands (IPMI/Section 29)
static void
ipmi_handle_sensor(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_sensor);
  switch (cmd)
  {
    case CMD_SENSOR_PLAT_EVENT_MSG:
      sensor_plat_event_msg(request, req_len, response, res_len);
      break;
    case CMD_SENSOR_ALERT_IMMEDIATE_MSG:
      sensor_alert_immediate_msg(request, req_len, response, res_len);
      break;
    case CMD_SENSOR_SET_SENSOR_READING:
      sensor_set_reading(request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  pthread_mutex_unlock(&m_sensor);
}
/*
 * Function(s) to handle IPMI messages with NetFn: Application
 */
// Get Device ID (IPMI/Section 20.1)
static void
app_get_device_id (unsigned char *request, unsigned char req_len,
                   unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  unsigned char *data = &res->data[0];
  FILE *fp=NULL;
  int fv_major = 0x01, fv_minor = 0x03, fv_patch = 0x00;
  char buffer[64];
  if(length_check(0, req_len, response, res_len))
  {
    return;
  }
  fp = fopen("/etc/issue","r");
  if (fp != NULL)
  {
    if (fgets(buffer, sizeof(buffer), fp)) {
      char *version = strstr(buffer, "-v");
      if (version != NULL) {
        sscanf(version, "-v%d.%d.%d", &fv_major, &fv_minor, &fv_patch);
      }
    }
    fclose(fp);
  }
  // If we are using date based versioning, return
  // the two digit year instead of the 4 digit year.
  // Look out for year 2127 when this overflows the
  // 7 bit allowance for major version :-)
  if (fv_major > 2000) {
    fv_major -= 2000;
  }
  res->cc = CC_SUCCESS;
  //TODO: Following data needs to be updated based on platform
  *data++ = 0x20;   // Device ID
  *data++ = 0x81;   // Device Revision
  *data++ = fv_major & 0x7f;      // Firmware Revision Major
  *data++ = ((fv_minor / 10) << 4) | (fv_minor % 10);      // 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++ = (uint8_t) fv_patch; // Aux. Firmware Version1 (patch ver)
  *data++ = 0x00;               // Aux. Firmware Version2
  *data++ = 0x00;               // Aux. Firmware Version3
  *data++ = 0x00;               // Aux. Firmware Version4
  *res_len = data - &res->data[0];
}
static void *
wait_and_reboot() {
  sleep(1);
  pal_bmc_reboot(RB_AUTOBOOT);
  return NULL;
}
// Cold Reset (IPMI/Section 20.2)
static void
app_cold_reset(unsigned char *request, unsigned char req_len,
              unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  int i;
  pthread_t do_reboot;
  *res_len = 0;
  res->cc = CC_SUCCESS;
  if (pal_is_fw_update_ongoing_system()) {
    res->cc = CC_NODE_BUSY;
    return;
  }
  for (i = 1; i < MAX_NUM_FRUS; i++) {
    if (pal_is_crashdump_ongoing(i)) {
      res->cc = CC_NODE_BUSY;
      return;
    }
  }
  for (i = 1; i < MAX_NUM_FRUS; i++) {
    if (pal_is_cplddump_ongoing(i)) {
      res->cc = CC_NODE_BUSY;
      return;
    }
  }
  syslog(LOG_CRIT, "BMC Cold Reset.");
  if (pthread_create(&do_reboot, NULL, wait_and_reboot, NULL) < 0) {
    syslog(LOG_WARNING, "pthread_create for doing reset failed\n");
  }
}
// Get Self Test Results (IPMI/Section 20.4)
static void
app_get_selftest_results (unsigned char *request, unsigned char req_len,
              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];
}
// Manufacturing Test On (IPMI/Section 20.5)
static void
app_manufacturing_test_on (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 *data = &res->data[0];
  char stat_buf[10] = {0};
  size_t len = 0;
  bool is_first_exc = false;
  res->cc = CC_SUCCESS;
  if ((!memcmp(req->data, "sled-cycle", strlen("sled-cycle"))) &&
      (req_len - ((void*)req->data - (void*)req)) == strlen("sled-cycle")) {
    if (system("/usr/local/bin/power-util sled-cycle") != 0) {
      res->cc = CC_UNSPECIFIED_ERROR;
    }
  } else if ((!memcmp(req->data, "obmc-dump", strlen("obmc-dump"))) &&
      (req_len - ((void*)req->data - (void*)req)) == strlen("obmc-dump")) {
    if (kv_get(OBMC_DUMP_STAT_KEY, stat_buf, &len, KV_FPERSIST) < 0) {
      is_first_exc = true;
    }
    // If obmc-dump is ongiong, skip the request
    if ((is_first_exc == true) || strncmp(stat_buf, "Ongoing", strlen("Ongoing")) != 0) {
      if (system("/usr/local/bin/obmc-dump -y &") != 0) {
        res->cc = CC_NOT_SUPP_IN_CURR_STATE;
      }
    }
  } else if ((!memcmp(req->data, "obmc-dump -s", strlen("obmc-dump -s"))) &&
      (req_len - ((void*)req->data - (void*)req)) == strlen("obmc-dump -s")) {
    if (kv_get(OBMC_DUMP_STAT_KEY, stat_buf, &len, KV_FPERSIST) < 0) {
      *data++ = DUMP_NEVER;
      goto exit;
    }
    if (strncmp(stat_buf, "Ongoing", strlen("Ongoing")) == 0) {
      *data++ = DUMP_ONGOING;
    } else if (strncmp(stat_buf, "Done", strlen("Done")) == 0) {
      *data++ = DUMP_DONE;
    } else if (strncmp(stat_buf, "Fail", strlen("Fail")) == 0) {
      *data++ = DUMP_FAIL;
    } else {
      res->cc = CC_UNSPECIFIED_ERROR;
    }
  } else {
    res->cc = CC_INVALID_PARAM;
  }
exit:
  *res_len = data - &res->data[0];
}
// Get Device GUID (IPMI/Section 20.8)
static void
app_get_device_guid (unsigned char *request, unsigned char req_len,
                    unsigned char *response, unsigned char *res_len)
{
  int ret;
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  ipmi_res_t *res = (ipmi_res_t *) response;
  // Get the 16 bytes of Device GUID from PAL library
  ret = pal_get_dev_guid(req->payload_id, (char *)res->data);
  if (ret) {
      res->cc = CC_UNSPECIFIED_ERROR;
      *res_len = 0x00;
  } else {
      res->cc = CC_SUCCESS;
      *res_len = SIZE_GUID;
  }
}
static void
app_get_device_sys_guid (unsigned char *request, unsigned char req_len,
                        unsigned char *response, unsigned char *res_len)
{
  int ret;
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  ipmi_res_t *res = (ipmi_res_t *) response;
  // Get the 16 bytes of System GUID from PAL library
  ret = pal_get_sys_guid(req->payload_id, (char *)res->data);
  if (ret) {
      res->cc = CC_UNSPECIFIED_ERROR;
      *res_len = 0x00;
  } else {
      res->cc = CC_SUCCESS;
      *res_len = SIZE_GUID;
  }
}
static void
oem_set_device_sys_guid (unsigned char *request, unsigned char req_len,
                        unsigned char *response, unsigned char *res_len)
{
  int ret;
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  ipmi_res_t *res = (ipmi_res_t *) response;
  // Get the 16 bytes of System GUID from PAL library
  ret = pal_set_sys_guid(req->payload_id, (char *)req->data);
  if (ret) {
      res->cc = CC_UNSPECIFIED_ERROR;
      *res_len = 0x00;
  } else {
      res->cc = CC_SUCCESS;
      *res_len = 0x00;
  }
}
// Reset Watchdog Timer (IPMI/Section 27.5)
static void
app_reset_watchdog_timer (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 *data = &res->data[0];
  struct watchdog_data *wdt = get_watchdog(req->payload_id);
  if (!wdt) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  pthread_mutex_lock(&wdt->mutex);
  if (wdt->valid) {
    res->cc = CC_SUCCESS;
    wdt->present_count_down = wdt->init_count_down;
    wdt->run = 1;
  }
  else
    res->cc = CC_INVALID_PARAM; // un-initialized watchdog
  pthread_mutex_unlock(&wdt->mutex);
  *res_len = data - &res->data[0];
}
// Set Watchdog Timer (IPMI/Section 27.6)
static void
app_set_watchdog_timer (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 *data = &res->data[0];
  struct watchdog_data *wdt = get_watchdog(req->payload_id);
  if (!wdt) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  // no support pre-itmeout interrupt
  if (req->data[1] & 0xF0) {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  pthread_mutex_lock(&wdt->mutex);
  wdt->no_log = req->data[0] >> 7;
  wdt->use = req->data[0] & 0x7;
  wdt->pre_action = 0; // no support
  wdt->action = req->data[1] & 0x7;
  wdt->pre_interval = req->data[2];
  wdt->expiration &= ~(req->data[3]);
  wdt->init_count_down = (req->data[5]<<8 | req->data[4]);
  wdt->present_count_down = wdt->init_count_down;
  if (!(req->data[0] & 0x40)) // 'do not stop timer' bit
    wdt->run = 0;
  wdt->valid = 1;
  pthread_mutex_unlock(&wdt->mutex);
  res->cc = CC_SUCCESS;
  *res_len = data - &res->data[0];
}
// Get Watchdog Timer (IPMI/Section 27.7)
static void
app_get_watchdog_timer (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 *data = &res->data[0];
  unsigned char byte;
  struct watchdog_data *wdt = get_watchdog(req->payload_id);
  if (!wdt) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  pthread_mutex_lock(&wdt->mutex);
  byte = wdt->use;
  if (wdt->no_log)
    byte |= 0x80;
  if (wdt->run)
    byte |= 0x40;
  *data++ = byte;
  *data++ = (wdt->pre_action << 4 | wdt->action);
  *data++ = wdt->pre_interval;
  *data++ = wdt->expiration;
  *data++ = wdt->init_count_down & 0xFF;
  *data++ = (wdt->init_count_down >> 8) & 0xFF;
  *data++ = wdt->present_count_down & 0xFF;
  *data++ = (wdt->present_count_down >> 8) & 0xFF;
  pthread_mutex_unlock(&wdt->mutex);
  res->cc = CC_SUCCESS;
  *res_len = data - &res->data[0];
}
// Set BMC Global Enables (IPMI/Section 22.1)
static void
app_set_global_enables (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;
  bmc_global_enable_setting[req->payload_id - 1] = req->data[0];
  res->cc = CC_SUCCESS;
  // Do nothing
  *res_len = 0;
}
// Get BMC Global Enables (IPMI/Section 22.2)
static void
app_get_global_enables (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 *data = &res->data[0];
  res->cc = CC_SUCCESS;
  *data++ = bmc_global_enable_setting[req->payload_id - 1];    // Global Enable
  *res_len = data - &res->data[0];
}
// Clear Message flags Command (IPMI/Section 22.3)
static void
app_clear_message_flags (unsigned char *request, unsigned char req_len,
                         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;
  // Do nothing
  *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 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 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);
      pal_set_sysfw_ver(req->payload_id, g_sys_info_params.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;
    case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
      memcpy(g_sys_info_params.bios_current_boot_list, &req->data[1], req_len-4); // boot list length = req_len-4 (payload_id, cmd, netfn, param)
      pal_set_bios_current_boot_list(req->payload_id, g_sys_info_params.bios_current_boot_list, req_len-4, &res->cc);
      break;
    case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
      if(length_check(SIZE_BIOS_FIXED_BOOT_DEVICE+1, req_len, response, res_len))
        break;
      memcpy(g_sys_info_params.bios_fixed_boot_device, &req->data[1], SIZE_BIOS_FIXED_BOOT_DEVICE);
      pal_set_bios_fixed_boot_device(req->payload_id, g_sys_info_params.bios_fixed_boot_device);
      break;
    case SYS_INFO_PARAM_BIOS_RESTORES_DEFAULT_SETTING:
      if(length_check(SIZE_BIOS_RESTORES_DEFAULT_SETTING+1, req_len, response, res_len))
        break;
      memcpy(g_sys_info_params.bios_restores_default_setting, &req->data[1], SIZE_BIOS_RESTORES_DEFAULT_SETTING);
      pal_set_bios_restores_default_setting(req->payload_id, g_sys_info_params.bios_restores_default_setting);
      break;
    case SYS_INFO_PARAM_LAST_BOOT_TIME:
      if(length_check(SIZE_LAST_BOOT_TIME+1, req_len, response, res_len))
        break;
      memcpy(g_sys_info_params.last_boot_time, &req->data[1], SIZE_LAST_BOOT_TIME);
      pal_set_last_boot_time(req->payload_id, g_sys_info_params.last_boot_time);
      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 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 *data = &res->data[0];
  unsigned char param = req->data[1];
  // Fill default return values
  res->cc = CC_SUCCESS;
  *data++ = 1;		// Parameter revision
  if(!length_check(4, req_len, response, res_len))
  {
    switch (param)
    {
      case SYS_INFO_PARAM_SET_IN_PROG:
        *data++ = g_sys_info_params.set_in_prog;
        break;
      case SYS_INFO_PARAM_SYSFW_VER:
        pal_get_sysfw_ver(req->payload_id, g_sys_info_params.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;
      case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
        if(pal_get_bios_current_boot_list(req->payload_id, g_sys_info_params.bios_current_boot_list, res_len))
        {
          res->cc = CC_UNSPECIFIED_ERROR;
          break;
        }
        memcpy(data, g_sys_info_params.bios_current_boot_list, *res_len);
        data += *res_len;
        break;
      case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
        if(pal_get_bios_fixed_boot_device(req->payload_id, g_sys_info_params.bios_fixed_boot_device))
        {
          res->cc = CC_UNSPECIFIED_ERROR;
          break;
        }
        memcpy(data, g_sys_info_params.bios_fixed_boot_device, SIZE_BIOS_FIXED_BOOT_DEVICE);
        data += SIZE_BIOS_FIXED_BOOT_DEVICE;
        break;
      case SYS_INFO_PARAM_BIOS_RESTORES_DEFAULT_SETTING:
        if(pal_get_bios_restores_default_setting(req->payload_id, g_sys_info_params.bios_restores_default_setting))
        {
          res->cc = CC_UNSPECIFIED_ERROR;
          break;
        }
        memcpy(data, g_sys_info_params.bios_restores_default_setting, SIZE_BIOS_RESTORES_DEFAULT_SETTING);
        data += SIZE_BIOS_RESTORES_DEFAULT_SETTING;
        break;
      case SYS_INFO_PARAM_LAST_BOOT_TIME:
        if(pal_get_last_boot_time(req->payload_id, g_sys_info_params.last_boot_time))
        {
          res->cc = CC_UNSPECIFIED_ERROR;
          break;
        }
        memcpy(data, g_sys_info_params.last_boot_time, SIZE_LAST_BOOT_TIME);
        data += SIZE_LAST_BOOT_TIME;
        break;
      default:
        res->cc = CC_INVALID_PARAM;
        break;
    }
  }
  if (res->cc == CC_SUCCESS) {
    *res_len = data - &res->data[0];
  }
  return;
}
static void
app_master_write_read (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;
  uint8_t *ptr = req->data;
  uint8_t writeCnt, readCnt;
  uint8_t buf[42];
  uint8_t bus_num, ret;
  int fd;
  char fn[32];
  if( req_len < 6 ){
    res->cc = CC_INVALID_LENGTH;
    return;
  }
  readCnt = req->data[2];
  writeCnt = req_len - 6;
  if ( ptr[0] & 0x01 ) {
    if ( (readCnt > 35) || (writeCnt > 36) ) {
      res->cc = CC_INVALID_DATA_FIELD;
      return;
    } else {
      if ( pal_is_cmd_valid(req->data) ) {
        res->cc = CC_INVALID_DATA_FIELD;
        return;
      }
      memcpy(buf, &req->data[3], writeCnt);
      bus_num = ((req->data[0] & 0x7E) >> 1); //extend bit[7:1] for bus ID
      //ret = pal_i2c_write_read(bus_num, req->data[1], buf, writeCnt, res->data, readCnt);
      snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus_num);
      fd = open(fn, O_RDWR);
      if (fd < 0) {
        res->cc = CC_UNSPECIFIED_ERROR;
        return;
      }
      ret = i2c_rdwr_msg_transfer(fd, req->data[1], buf, writeCnt, res->data, readCnt);
      if(!ret) {
         *res_len = readCnt;
         res->cc = CC_SUCCESS;
      } else {
        syslog(LOG_WARNING, "app_master_write_read: master read fail, bus %d, addr %x", bus_num, req->data[1]);
          res->cc = CC_UNSPECIFIED_ERROR;
      }
      close(fd);
    }
  }
  else
  {
    res->cc = CC_INVALID_DATA_FIELD;
  }
}
static void
app_get_sys_intf_caps (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 *data = &res->data[0];
  res->cc = CC_SUCCESS;
  switch (req->data[0]) {
    case 0:  // SSIF
      *data++ = 0x00;
      *data++ = 0x80;
      *data++ = 255;
      *data++ = 125;
      break;
    case 1:  // KCS
      *data++ = 0x00;
      *data++ = 0x00;
      *data++ = 255;
      break;
    default:
      res->cc = CC_INVALID_DATA_FIELD;
      break;
  }
  *res_len = data - &res->data[0];
  pal_get_sys_intf_caps(req->payload_id, req->data, res->data, res_len);
}
// 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 (request, req_len, response, res_len);
      break;
    case CMD_APP_COLD_RESET:
      if(pal_is_fw_update_ongoing_system())
        res->cc = CC_NODE_BUSY;
      else
        app_cold_reset (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_SELFTEST_RESULTS:
      app_get_selftest_results (request, req_len, response, res_len);
      break;
    case CMD_APP_MANUFACTURING_TEST_ON:
      if(pal_is_fw_update_ongoing_system())
        res->cc = CC_NODE_BUSY;
      else
        app_manufacturing_test_on (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_DEVICE_GUID:
      app_get_device_guid (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_SYSTEM_GUID:
      app_get_device_sys_guid (request, req_len, response, res_len);
      break;
    case CMD_APP_RESET_WDT:
      app_reset_watchdog_timer (request, req_len, response, res_len);
      break;
    case CMD_APP_SET_WDT:
      app_set_watchdog_timer (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_WDT:
      app_get_watchdog_timer (request, req_len, response, res_len);
      break;
    case CMD_APP_SET_GLOBAL_ENABLES:
      app_set_global_enables (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_GLOBAL_ENABLES:
      app_get_global_enables (request, req_len, response, res_len);
      break;
    case CMD_APP_SET_SYS_INFO_PARAMS:
      app_set_sys_info_params (request, req_len, response, res_len);
      break;
    case CMD_APP_CLEAR_MESSAGE_FLAGS:
      app_clear_message_flags (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_SYS_INFO_PARAMS:
      app_get_sys_info_params (request, req_len, response, res_len);
      break;
    case CMD_APP_MASTER_WRITE_READ:
      app_master_write_read (request, req_len, response, res_len);
      break;
    case CMD_APP_GET_SYS_INTF_CAPS:
      app_get_sys_intf_caps (request, req_len, 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 *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 size = plat_fruid_size(req->payload_id);
  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 fru_id = req->data[0];
  int offset = req->data[1] + (req->data[2] << 8);
  int count = req->data[3];
  int ret = plat_fruid_data(req->payload_id, fru_id, 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) {
    *(unsigned short*)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 *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 rsv_id;     // SDR reservation ID
  // Use platform APIs to get a SDR reservation ID
  rsv_id = sdr_rsv_id(req->payload_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 (req->payload_id, 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 *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 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 (req->payload_id);
  free_space = sel_free_space (req->payload_id);
  sel_ts_recent_add (req->payload_id, &ts_recent_add);
  sel_ts_recent_erase (req->payload_id, &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 * 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 rsv_id;     // SEL reservation ID
  // Use platform APIs to get a SEL reservation ID
  rsv_id = sel_rsv_id (req->payload_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;
  unsigned char offset = req->data[4];
  unsigned char len = req->data[5];
  if (len == 0xFF) {  // FFh means read entire record
    offset = 0;
    len = SIZE_SEL_REC;
  } else if ((offset >= SIZE_SEL_REC) || (len > SIZE_SEL_REC) || ((offset+len) > SIZE_SEL_REC)) {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    return;
  }
  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 (req->payload_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.msg[offset], len);
  data += len;
  *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 (req->payload_id, &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 = SEL_ERASE_DONE;
  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 (req->payload_id, rsv_id);
  }
  else if (req->data[5] == IPMI_SEL_ERASE_STAT)
  {
    ret = sel_erase_status (req->payload_id, 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;
}
#if defined(CONFIG_FBTTN) || defined(CONFIG_FBTP)
static void
storage_get_sel_time (unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  res->cc = CC_SUCCESS;
  time_stamp_fill(res->data);
  *res_len = SIZE_TIME_STAMP;
  return;
}
#endif
#if defined(CONFIG_FBY2_ND)
static void
storage_set_sel_time (unsigned char *request, unsigned char *response, unsigned char req_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  static uint8_t time_sync = 0;
  if (req_len != 7) { // payload_id netfn_lun cmd data[4];
    res->cc = CC_INVALID_LENGTH;
  }
  if (!time_sync) {
    if (pal_set_time_sync(req->data,req_len-3)) {
      res->cc = CC_UNSPECIFIED_ERROR;
      return;
    }
    res->cc = CC_SUCCESS;
    time_sync = 1;
  } else {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
  }
  return;
}
#endif
static void
storage_get_sel_utc (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: For now, the SEL time stamp is based on UTC time,
  // so return 0x0000 as offset. Might need to change once
  // supporting zones in SEL time stamps
  *data++ = 0x00;
  *data++ = 0x00;
  *res_len = data - &res->data[0];
}
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 (request, 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 (request, response, res_len);
      break;
    case CMD_STORAGE_RSV_SEL:
      storage_rsv_sel (request, 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;
#if defined(CONFIG_FBTTN) || defined(CONFIG_FBTP)
     // To avoid BIOS using this command to update RTC
     // TBD: Respond only if BMC's time has synced with NTP
    case CMD_STORAGE_GET_SEL_TIME:
      storage_get_sel_time (response, res_len);
      break;
#endif
#if defined(CONFIG_FBY2_ND)
    case CMD_STORAGE_SET_SEL_TIME:
      storage_set_sel_time (request, response, req_len);
      break;
#endif
    case CMD_STORAGE_GET_SEL_UTC:
      storage_get_sel_utc (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 (request, 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;
    case LAN_PARAM_IP6_ADDR:
      memcpy(g_lan_config.ip6_addr, &req->data[2], SIZE_IP6_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++ = 0x11;   // 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:
      plat_lan_init(&g_lan_config);
      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:
      plat_lan_init(&g_lan_config);
      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;
    case LAN_PARAM_IP6_ADDR:
      plat_lan_init(&g_lan_config);
      memcpy(data, g_lan_config.ip6_addr, SIZE_IP6_ADDR);
      data += SIZE_IP6_ADDR;
      break;
   case LAN_PARAM_IP6_DYNAMIC_ADDR:
      plat_lan_init(&g_lan_config);
      data[0] = 0; // Selector
      data[1] = 0x02; // DHCPv6
      memcpy(&data[2], g_lan_config.ip6_addr, SIZE_IP6_ADDR);
      data[18] = g_lan_config.ip6_prefix;
      data[19] = 0x00; // Active
      data += SIZE_IP6_ADDR + 4;
      break;
    case LAN_PARAM_ADDR_ENABLES:
      data[0] = 0x02; // Enable both IPv4 and IPv6
      data ++;
      break;
    default:
      res->cc = CC_INVALID_PARAM;
      break;
  }
  if (res->cc == CC_SUCCESS) {
    *res_len = data - &res->data[0];
  }
}
// Get SoL Configuration (IPMI/Section 26.3)
static void
transport_get_sol_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 SOL_PARAM_SET_IN_PROG:
      *data++ = 0x00;
      break;
    case SOL_PARAM_SOL_ENABLE:
      *data++ = 0x01;
      break;
    case SOL_PARAM_SOL_AUTH:
      *data++ = 0x02;
      break;
    case SOL_PARAM_SOL_THRESHOLD:
      *data++ = 0x00;
      *data++ = 0x01;
      break;
    case SOL_PARAM_SOL_RETRY:
      *data++ = 0x00;
      *data++ = 0x00;
      break;
    case SOL_PARAM_SOL_BITRATE:
    case SOL_PARAM_SOL_NV_BITRATE:
      *data++ = 0x09;
      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;
    case CMD_TRANSPORT_GET_SOL_CONFIG:
      transport_get_sol_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: DCMI
 */
static void
ipmi_handle_dcmi(unsigned char *request, unsigned char req_len,
     unsigned char *response, unsigned char *res_len)
{
  int ret;
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  ipmi_res_t *res = (ipmi_res_t *) response;
  // If there is no command to process return
  if (req->cmd == 0x0) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  // Since DCMI handling is specific to platform, call PAL to process
  ret = pal_handle_dcmi(req->payload_id, &request[1], req_len-1, res->data, res_len);
  if (ret < 0) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
}
/*
 * Function(s) to handle IPMI messages with NetFn: OEM
 */
static void
oem_add_ras_sel (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;
  *res_len = 0;
  ras_sel_msg_t entry;
  memcpy(entry.msg, req->data, SIZE_RAS_SEL);
  res->cc = ras_sel_add_entry (req->payload_id, &entry);
  return;
}
static void
oem_add_imc_log (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;
  *res_len = 0;
  res->cc = pal_add_imc_log (req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_q_sled_cycle_prepare_request (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;
  char key[32] = "blk_fwupd";
  char value[8] = {0};
  if (pal_is_fw_update_ongoing_system()) {
    res->cc = CC_NODE_BUSY;
    return;
  }
  if ( req->data[0] > 1 ) {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    return;
  }
  if ( req_len != 4 ) {
    res->cc = CC_INVALID_LENGTH;
    return;
  }
  sprintf(value, "%02x", req->data[0]);
  if( kv_set(key, value, 0, 0) ) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_q_sled_cycle_prepare_status (unsigned char *request, unsigned char req_len, unsigned char *response,
      unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  char key[32] = "blk_fwupd";
  char value[8] = {0};
  if ( req_len != 3 ) {
    res->cc = CC_INVALID_LENGTH;
    return;
  }
  if ( kv_get(key, value, 0, 0) != 0 ) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  res->data[0] = (uint8_t)atoi(value);
  *res_len = 1;
  res->cc = CC_SUCCESS;
}
static void
oem_set_proc_info (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;
  char key[MAX_KEY_LEN];
  char value[MAX_VALUE_LEN];
  unsigned int index = req->data[0];
  *res_len = 0;
  res->cc = CC_UNSPECIFIED_ERROR;
  sprintf(key, "sys_config/fru%u_cpu%u_basic_info", req->payload_id, index);
  value[0] = 1; // Num cores (Unknown?)
  value[1] = 1; // Thread count (Unknown?)
  value[2] = 0;
  value[3] = req->data[2]; // Frequency
  value[4] = req->data[3];
  value[5] = req->data[1]; // Type stored as revision.
  value[6] = 0;
  if(kv_set(key, value, 7, KV_FPERSIST) != 0) {
    return;
  }
  res->cc = CC_SUCCESS;
}
static void
oem_get_proc_info (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;
  char key[MAX_KEY_LEN];
  char value[MAX_VALUE_LEN];
  unsigned int index = req->data[0];
  size_t ret;
  *res_len = 0;
  res->cc = CC_UNSPECIFIED_ERROR;
  sprintf(key, "sys_config/fru%u_cpu%u_basic_info", req->payload_id, index);
  if(kv_get(key, value, &ret, KV_FPERSIST) != 0 || ret < 7) {
    return;
  }
  res->data[0] = value[5]; // Return Processor revision as Type
  res->data[1] = value[3]; // Processor Frequency
  res->data[2] = value[4];
  res->data[3] = 0x1;      // Processor Present (0x1, 0xff Not present)
  *res_len = 4;
  res->cc = CC_SUCCESS;
}
static void
oem_set_dimm_info (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;
  char key[MAX_KEY_LEN] = {0};
  char value[MAX_VALUE_LEN] = {0};
  unsigned char index = req->data[0];
  *res_len = 0;
  res->cc = CC_UNSPECIFIED_ERROR;
  sprintf(key, "sys_config/fru%d_dimm%d_type", req->payload_id, index);
  if(kv_set(key, (char *)&req->data[1], 1, KV_FPERSIST)) {
    return;
  }
  sprintf(key, "sys_config/fru%d_dimm%d_speed", req->payload_id, index);
  memcpy(value, &req->data[2], 2);
  memcpy(value + 2, &req->data[4], 4);
  if(kv_set(key, (char *)value, 6, KV_FPERSIST)) {
    return;
  }
  res->cc = CC_SUCCESS;
}
static void
oem_get_dimm_info (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;
  char key[MAX_KEY_LEN] = {0};
  char value[MAX_VALUE_LEN] = {0};
  unsigned char index = req->data[0];
  size_t ret;
  *res_len = 0;
  res->cc = CC_UNSPECIFIED_ERROR;
  sprintf(key, "sys_config/fru%d_dimm%d_type", req->payload_id, index);
  if(kv_get(key, value, &ret, KV_FPERSIST) != 0 || ret < 1) {
    return;
  }
  memcpy(&res->data[0], value, 1); // Type
  sprintf(key, "sys_config/fru%d_dimm%d_speed", req->payload_id, index);
  if(kv_get(key, value, &ret, KV_FPERSIST) != 0 || ret < 6) {
    return;
  }
  memcpy(&res->data[1], value, 2); // speed
  memcpy(&res->data[3], value + 2, 4); // size
  res->data[7] = 0x2; // 0x2 DIMM Present, 0x3 DIMM not Present
  *res_len = 8;
  res->cc = CC_SUCCESS;
}
/*
 * Function(s) to handle IPMI messages with NetFn: OEM 0x36
 */
static void
oem_q_set_proc_info (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;
  char key[100] = {0};
  char payload[100] = {0};
  int numOfParam = sizeof(cpu_info_key)/sizeof(char *);
  if (req_len < 8 || req_len >= sizeof(payload) ||
      (req->data[4] >= numOfParam) ||
      (strlen(cpu_info_key[ (int) req->data[4]]) == 0))
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_cpu%d_%s", req->payload_id, req->data[3], cpu_info_key[req->data[4]]);
  memcpy(payload, &req->data[5], req_len -8);
  if(kv_set(key, payload, req_len - 8, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_q_get_proc_info (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;
  char key[100] = {0};
  size_t ret = 0;
  int numOfParam = sizeof(cpu_info_key)/sizeof(char *);
  if ((req->data[4] >= numOfParam) ||
      (strlen(cpu_info_key[ (int) req->data[4]]) <= 0) )
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_cpu%d_%s", req->payload_id, req->data[3], cpu_info_key[req->data[4]]);
  if (kv_get(key, (char *)res->data, &ret, KV_FPERSIST) < 0) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = (unsigned char)ret;
}
static void
oem_q_set_dimm_info (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;
  char key[100] = {0};
  char payload[100] = {0};
  int numOfParam = sizeof(dimm_info_key)/sizeof(char *);
  if (req_len < 8 || req_len >= sizeof(payload) ||
      (req->data[4] >= numOfParam) ||
      (strlen(dimm_info_key[ (int) req->data[4]]) == 0))
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_dimm%d_%s", req->payload_id, req->data[3], dimm_info_key[req->data[4]]);
  memcpy(payload, &req->data[5], req_len -8);
  if(kv_set(key, payload, req_len - 8, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_q_get_dimm_info (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;
  char key[100] = {0};
  size_t ret = 0;
  int numOfParam = sizeof(dimm_info_key)/sizeof(char *);
  if ((req->data[4] >= numOfParam) ||
      (strlen(dimm_info_key[ (int) req->data[4]]) <= 0) )
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_dimm%d_%s", req->payload_id, req->data[3], dimm_info_key[req->data[4]]);
  if(kv_get(key, (char *)res->data, &ret, KV_FPERSIST) < 0) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = (unsigned char)ret;
}
static void
oem_q_set_drive_info(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;
  char key[100] = {0}, cltrType;
  char payload[100] = {0};
  int numOfParam = sizeof(drive_info_key)/sizeof(char *);
  if (req_len < 9 || req_len >= sizeof(payload) ||
      (req->data[5] >= numOfParam) ||
      (strlen(drive_info_key[ (int) req->data[5]]) == 0))
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  if( (req->data[3] & 0x0f) ==0x00)
    cltrType = 'B'; // BIOS
  else if ((req->data[3] & 0x0f) ==0x01)
    cltrType = 'E'; // Expander
  else if  ((req->data[3] & 0x0f) ==0x02)
    cltrType = 'L'; // LSI
  else {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_%c_drive%d_%s", req->payload_id, cltrType, req->data[4], drive_info_key[req->data[5]]);
  memcpy(payload, &req->data[6], req_len - 9);
  if(kv_set(key, payload, req_len - 9, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_q_get_drive_info(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;
  char key[100] = {0}, cltrType;
  size_t ret = 0;
  int numOfParam = sizeof(drive_info_key)/sizeof(char *);
  if ((req->data[5] >= numOfParam) ||
      (strlen(drive_info_key[ (int) req->data[5]]) <= 0) )
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  if( (req->data[3] & 0x0f) ==0x00)
    cltrType = 'B'; // BIOS
  else if ((req->data[3] & 0x0f) ==0x01)
    cltrType = 'E'; // Expander
  else if  ((req->data[3] & 0x0f) ==0x02)
    cltrType = 'L'; // LSI
  else {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  sprintf(key, "sys_config/fru%d_%c_drive%d_%s", req->payload_id, cltrType, req->data[4], drive_info_key[req->data[5]]);
  if(kv_get(key, (char *)res->data, &ret, KV_FPERSIST) < 0) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = (unsigned char)ret;
}
static void
oem_q_set_smu_psp_ver(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;
  char key[100] = {0};
  char payload[100] = {0};
  if (req_len < 8)
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  if( (req->data[4] & 0x0f) == 0x01)
    sprintf(key, "sys_config/fru%d_psp_ver", req->payload_id);
  else if ((req->data[4] & 0x0f) == 0x02)
    sprintf(key, "sys_config/fru%d_smu_ver", req->payload_id);
  memcpy(payload, &req->data[5], req_len - 8);
  if(kv_set(key, payload, req_len - 8, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_q_get_smu_psp_ver(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;
  char key[100] = {0};
  size_t ret = 0;
  if (req_len < 5)
  {
    res->cc = CC_PARAM_OUT_OF_RANGE;
    *res_len = 0;
    return;
  }
  if( (req->data[4] & 0x0f) == 0x01)
    sprintf(key, "sys_config/fru%d_psp_ver", req->payload_id);
  else if ((req->data[4] & 0x0f) == 0x02)
    sprintf(key, "sys_config/fru%d_smu_ver", req->payload_id);
  if(kv_get(key, (char *)res->data, &ret, KV_FPERSIST) < 0) {
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = (unsigned char)ret;
}
static void
oem_set_boot_order(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 ret;
  int slot_id = req->payload_id;
  static pthread_t bios_timer_tid[MAX_NODES];
  if ( IsTimerStart[req->payload_id - 1] )
  {
#ifdef DEBUG
    syslog(LOG_WARNING, "[%s] Close the previous thread\n", __func__);
#endif
    pthread_cancel(bios_timer_tid[req->payload_id - 1]);
    IsTimerStart[req->payload_id - 1] = false;
  }
  if (req->data[0] & (BIOS_BOOT_VALID_FLAG | CMOS_VALID_FLAG | FORCE_BOOT_BIOS_SETUP_VALID_FLAG)) {
    /*Create timer thread*/
    ret = pthread_create(&bios_timer_tid[req->payload_id - 1], NULL, clear_bios_data_timer, (void *)slot_id);
    if (ret < 0) {
      syslog(LOG_WARNING, "[%s] Create BIOS timer thread failed!\n", __func__);
      res->cc = CC_NODE_BUSY;
      *res_len = 0;
      return;
    }
    IsTimerStart[req->payload_id - 1] = true;
  }
  ret = pal_set_boot_order(req->payload_id, req->data, res->data, res_len);
  if(ret == 0)
  {
    res->cc = CC_SUCCESS;
  }
  else
  {
    res->cc = CC_INVALID_PARAM;
  }
}
static void
oem_get_boot_order(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 ret;
  ret = pal_get_boot_order(req->payload_id, req->data, res->data, res_len);
#ifdef DEBUG
  syslog(LOG_WARNING, "[%s] Get: %x %x %x %x %x %x\n", __func__, res->data[0], res->data[1], res->data[2], res->data[3], res->data[4], res->data[5]);
#endif
  if(ret == 0)
  {
	res->cc = CC_SUCCESS;
  }
  else
  {
	res->cc = CC_INVALID_PARAM;
  }
}
static void
oem_set_tpm_presence(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 ret;
  int slot_id = req->payload_id;
  int presence = req->data[0];
  if (presence == 0) {
    ret = pal_set_tpm_physical_presence(slot_id,presence);
    if (ret == 0) {
      res->cc = CC_SUCCESS;
    } else {
      res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    }
  } else {
    // do not set tpm physical presence flag to 1
    res->cc = CC_UNSPECIFIED_ERROR;
  }
}
static void
oem_get_tpm_presence(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 value;
  value = pal_get_tpm_physical_presence(req->payload_id);
  syslog(LOG_WARNING, "[%s] Get: %x\n", __func__, value);
  if ((value != 0) && (value != 1)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  // BMC auto-clear after BIOS retrieve it
  // if (value == 1) {
  //   int ret = pal_set_tpm_physical_presence(req->payload_id,0);
  //   if (ret != 0) {
  //     res->cc = CC_UNSPECIFIED_ERROR;
  //     *res_len = 0;
  //     return;
  //   }
  // }
  res->cc = CC_SUCCESS;
  res->data[0] = value;
  *res_len = 1;
}
static void
oem_set_ppr (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;
  uint8_t selector = req->data[0];
  uint8_t ppr_enable, ppr_type, ppr_action = 0;
  int size = 0, offset;
  char temp[20]= {0};
  char filepath[128]= {0};
  bool append_mode = true;
  FILE *fp = NULL;
  if (access("/mnt/data/ppr/", F_OK) == -1)
     mkdir("/mnt/data/ppr/", 0777);
  *res_len = 0;
  res->cc = CC_SUCCESS;
  switch(selector) {
    case PPR_ACTION:
      ppr_enable = req->data[1] & 0x80;
      ppr_type = req->data[1] & 0x7f;
      if (ppr_enable) {
        if (ppr_type != PPR_SOFT && ppr_type != PPR_HARD  && ppr_type != PPR_TEST_MODE) {
          res->cc = CC_INVALID_DATA_FIELD;
          return;
        }
        ppr_action = req->data[1];
      }
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_count", req->payload_id);
      fp = fopen(filepath, "r");
      if (!fp) {
        res->cc = CC_NOT_SUPP_IN_CURR_STATE;
        return;
      }
      if (fread(res->data, 1, 1, fp) != 1) {
        fclose(fp);
        res->cc = CC_NOT_SUPP_IN_CURR_STATE;
        return;
      }
      if(res->data[0] == 0) {
        fclose(fp);
        res->cc = CC_NOT_SUPP_IN_CURR_STATE;
        return;
      }
      fclose(fp);
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_action", req->payload_id);
      fp = fopen(filepath, "w");
      if (fp != NULL) {
        fwrite(&ppr_action, sizeof(uint8_t), 1, fp);
      }
      break;
    case PPR_ROW_COUNT:
      if (req->data[1] > PPR_MAX_ROW_COUNT) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      else
      {
        sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_count", req->payload_id);
        fp = fopen(filepath, "w");
        if(fp != NULL)
          fwrite(&req->data[1], sizeof(uint8_t), 1, fp);
      }
      break;
    case PPR_ROW_ADDR:
      if (req->data[1] > PPR_MAX_ROW_COUNT) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      if (req_len != (PPR_ROW_ADDR_DATA_LEN + 4)) {
        res->cc = CC_INVALID_LENGTH;
        return;
      }
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_addr", req->payload_id);
      fp = fopen(filepath, "a+");
      fseek(fp, 0, SEEK_END);
      size = ftell(fp);
      fseek(fp, 0, SEEK_SET);
      append_mode = true;
      offset = 0;
      if(size != 0) {
        while (fread(temp, sizeof(uint8_t), PPR_ROW_ADDR_DATA_LEN, fp)) {
          offset = ftell(fp);
          if(temp[0] == req->data[1]) {
            append_mode = false;
            fclose(fp);
            fp = fopen(filepath, "w+");
            offset = ftell(fp) - PPR_ROW_ADDR_DATA_LEN;
            if (offset < 0)
              offset = 0;
            break;
          }
        }
      }
      if (append_mode && size > PPR_MAX_ROW_COUNT * PPR_ROW_ADDR_DATA_LEN) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        fclose(fp);
        return;
      }
      fseek(fp, offset , SEEK_SET);
      fwrite(&req->data[1], sizeof(uint8_t), PPR_ROW_ADDR_DATA_LEN, fp);
      break;
    case PPR_HISTORY_DATA:
      if (req->data[1] > PPR_MAX_HISTORY_COUNT) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      if (req_len != (PPR_HISTORY_DATA_LEN + 4)) {
        res->cc = CC_INVALID_LENGTH;
        return;
      }
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_history_data", req->payload_id);
      fp = fopen(filepath, "a+");
      fseek(fp, 0, SEEK_END);
      size = ftell(fp);
      fseek(fp, 0, SEEK_SET);
      append_mode = true;
      offset = 0;
      if(size != 0) {
        while (fread(temp, sizeof(uint8_t), PPR_HISTORY_DATA_LEN, fp)) {
          offset = ftell(fp);
          if(temp[0] == req->data[1]) {
            append_mode = false;
            fclose(fp);
            fp = fopen(filepath, "w+");
            offset = ftell(fp) - PPR_HISTORY_DATA_LEN;
            if (offset < 0)
              offset = 0;
            break;
          }
        }
      }
      if (append_mode && size > PPR_MAX_HISTORY_COUNT * PPR_HISTORY_DATA_LEN) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        fclose(fp);
        return;
      }
      fseek(fp, offset , SEEK_SET);
      fwrite(&req->data[1], sizeof(uint8_t), PPR_HISTORY_DATA_LEN, fp);
      break;
    default:
      res->cc = CC_INVALID_PARAM;
      break;
  }
  if (fp) {
    fclose(fp);
  }
}
static void
oem_get_ppr (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;
  uint8_t selector = req->data[0];
  FILE *fp = NULL;
  char filepath[128]= {0};
  char temp[20], kpath[20];
  sprintf(kpath, "%s", "/mnt/data/ppr");
  if (access(kpath, F_OK) == -1)
    mkdir(kpath, 0777);
  *res_len = 0;
  res->cc = CC_SUCCESS;
  switch(selector) {
    case PPR_ACTION:
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_count", req->payload_id);
      fp = fopen(filepath, "r");
      if (!fp) {
        fp = fopen(filepath, "w");
        sprintf(temp, "%c",0);
        fwrite(temp, sizeof(char), 1, fp);
      }
      else {
        if (fread(res->data, 1, 1, fp) != 1) {
          fclose(fp);
          res->cc = CC_UNSPECIFIED_ERROR;
          return;
        }
      }
      fclose(fp);
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_action", req->payload_id);
      fp = fopen(filepath, "r");
      if (!fp) {
        fp = fopen(filepath, "w");
        sprintf(temp, "%c",0);
        fwrite(temp, sizeof(char), 1, fp);
        fclose(fp);
        res->data[0] = 0;
        *res_len = 1;
        return;
      }
      if (res->data[0] != 0 ) {
        if (fread(res->data, 1, 1, fp) != 1) {
          fclose(fp);
          res->cc = CC_UNSPECIFIED_ERROR;
          return;
        }
        if((res->data[0] & 0x80) == 0 )
          res->data[0] = 0;
      }
      *res_len = 1;
      break;
    case PPR_ROW_COUNT:
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_count", req->payload_id);
      fp = fopen(filepath, "r");
      if (!fp) {
        fp = fopen(filepath, "w");
        sprintf(temp, "%c",0);
        fwrite(temp, sizeof(char), 1, fp);
        res->data[0] = 0;
      }
      else {
        if (fread(res->data, 1, 1, fp) != 1) {
          fclose(fp);
          res->cc = CC_UNSPECIFIED_ERROR;
          return;
        }
      }
      *res_len = 1;
      break;
    case PPR_ROW_ADDR:
      if (req->data[1] > PPR_MAX_ROW_COUNT) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_row_addr", req->payload_id);
      fp = fopen(filepath, "r+");
      if (!fp) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      while(fread(res->data, sizeof(uint8_t), PPR_ROW_ADDR_DATA_LEN, fp)) {
        if(res->data[0] == req->data[1]) {
          *res_len = PPR_ROW_ADDR_DATA_LEN;
          break;
        }
      }
      break;
    case PPR_HISTORY_DATA:
      if (req->data[1] > PPR_MAX_HISTORY_COUNT) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      sprintf(filepath, "/mnt/data/ppr/fru%d_ppr_history_data", req->payload_id);
      fp = fopen(filepath, "r+");
      if (!fp) {
        res->cc = CC_PARAM_OUT_OF_RANGE;
        return;
      }
      while(fread(res->data, sizeof(uint8_t), PPR_HISTORY_DATA_LEN, fp)) {
        if(res->data[0] == req->data[1]) {
          *res_len = PPR_HISTORY_DATA_LEN;
          break;
        }
      }
      break;
    default:
      res->cc = CC_INVALID_PARAM;
      break;
  }
  if (fp) {
    fclose(fp);
  }
}
static void
oem_set_post_start (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;
  pal_set_post_start(req->payload_id, req->data, res->data, res_len);
  res->cc = CC_SUCCESS;
}
static void
oem_set_post_end (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;
  pal_update_ts_sled();
  pal_set_post_end(req->payload_id, req->data, res->data, res_len);
  res->cc = CC_SUCCESS;
}
static void
oem_set_ppin_info(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;
  res->cc = pal_set_ppin_info(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_set_adr_trigger(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;
  bool trigger = (bool)req->data[3]; // high/low = true/false
  int ret;
  ret = pal_set_adr_trigger(req->payload_id, trigger);
  if (!ret) {
    res->cc = CC_SUCCESS;
  } else if (ret == PAL_ENOTSUP) {
    // If not supported on this platform act like
    // the command is not implemented.
    res->cc = CC_INVALID_CMD;
  } else {
    res->cc = CC_UNSPECIFIED_ERROR;
  }
  *res_len = 0;
}
static void
oem_get_plat_info(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 ret;
  uint8_t pres  = 0x00;
  uint8_t pinfo = 0x00;
  // Platform info:
  // Bit[7]: Not Present:0/Present:1  (from pal)
  // Bit[6]: Test Board:1, Non Test Board:0 (TODO from pal)
  // Bit[5-3] : SKU ID:000:Yosemite, 010:Triton
  // Bit[2:0] : Slot Index, 1 based
  ret = pal_is_fru_prsnt(req->payload_id, &pres);
  if (ret) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0x00;
    return;
  }
  // Populate the presence bit[7]
  if (pres) {
    pinfo = 0x80;
  }
  // Bit[6]: Test Board:1, Non Test Board:0
  if (pal_is_test_board()) {
    pinfo |= 0x40;
  }
  // Get the SKU ID bit[5-3]
  ret = pal_get_plat_sku_id();
  if (ret == -1){
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0x00;
    return;
  }else{
    pinfo |= (ret << 3);
  }
  // Populate the slot Index bit[2:0]
  ret = pal_get_slot_index(req->payload_id);
  pinfo |= (ret & 0x7);
  // Prepare response buffer
  res->cc = CC_SUCCESS;
  res->data[0] = pinfo;
  *res_len = 0x01;
}
static void
oem_sled_ac_cycle(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 ret;
  ret = pal_sled_ac_cycle(req->payload_id, req->data, (req_len - 3), res->data, res_len);
  if (ret == PAL_ENOTSUP) {
    res->cc = CC_INVALID_CMD;
  } else {
    res->cc = ret;
  }
  *res_len = 0;
  return;
}
static void
oem_get_poss_pcie_config(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;
  res->cc = pal_get_poss_pcie_config(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_set_imc_version(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;
  res->cc = pal_set_imc_version(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_set_fw_update_state(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 ret = -1;
  *res_len = 0;
  if (req_len == 5) {
    // Implicit FRU_ID
    ret = pal_set_fw_update_ongoing(req->payload_id, (req->data[1]<<8 | req->data[0]));
  } else if (req_len == 6) {
    // Explicit FRU_ID
    ret = pal_set_fw_update_ongoing(req->data[0], (req->data[1]<<8 | req->data[2]));
  } else {
    res->cc = CC_INVALID_LENGTH;
    return;
  }
  res->cc = ret == 0 ? CC_SUCCESS : CC_UNSPECIFIED_ERROR;
}
static void
oem_bypass_cmd(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;
  res->cc = pal_bypass_cmd(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_bypass_dev_card(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;
  res->cc = pal_bypass_dev_card(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_get_board_id(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;
  res->cc = pal_get_board_id(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_get_80port_record ( 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 ret;
  // XXX ipmi_handle() defines res_len as a size_t.
  // The correct change (but a much bigger one) is to replace
  // all `unsigned char *res_len` with `size_t *res_len`.
  ret = pal_get_80port_record (req->payload_id, res->data, 256, (size_t *)res_len);
  switch(ret) {
    case PAL_EOK:
      res->cc = CC_SUCCESS;
      break;
    case PAL_ENOTSUP:
      res->cc = CC_INVALID_PARAM;
      break;
    case PAL_ENOTREADY:
      res->cc = CC_NOT_SUPP_IN_CURR_STATE;
      break;
    default:
      res->cc = CC_UNSPECIFIED_ERROR;
      break;
  }
}
static void
oem_get_fw_info ( 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 ret;
  if (req_len != 4) {
    res->cc = CC_INVALID_LENGTH;
    *res_len = 0;
    return;
  }
  ret = pal_get_fw_info(req->payload_id, req->data[0], res->data, res_len);
  if (ret != 0) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0x00;
    return;
  }
  // Prepare response buffer
  res->cc = CC_SUCCESS;
}
static void
oem_set_machine_config_info ( 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;
  pal_set_machine_configuration(req->payload_id, req->data, req_len-3, response, res_len);
  res->cc = CC_SUCCESS;
  *res_len = 0;
}
static void
oem_set_flash_info ( 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;
  char key[MAX_KEY_LEN];
  char value[MAX_VALUE_LEN];
  char fruname[32];
  uint8_t mfg_id = 0;
  uint16_t dev_id = 0;
  uint16_t sts = 0;
  *res_len = 0x0;
  memcpy(&mfg_id, &req->data[3], 1);
  memcpy(&dev_id, &req->data[4], 2);
  memcpy(&sts, &req->data[6], 2);
  if (pal_get_fru_name(req->payload_id, fruname)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  sprintf(key, "%s_bios_flashinfo", fruname);
  sprintf(value, "%u %u %u", mfg_id, dev_id, sts);
  if (kv_set(key, value, 0, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  res->cc = CC_SUCCESS;
}
static void
oem_get_flash_info ( 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;
  char key[MAX_KEY_LEN];
  char value[MAX_VALUE_LEN];
  char fruname[32];
  unsigned int mfg_id = 0;
  unsigned int dev_id = 0;
  unsigned int sts = 0;
  *res_len = 0;
  if (pal_get_fru_name(req->payload_id, fruname)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  sprintf(key, "%s_bios_flashinfo", fruname);
  if (kv_get(key, value, NULL, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  if (sscanf(value, "%u %u %u", &mfg_id, &dev_id, &sts) != 3) {
    res->cc = CC_UNSPECIFIED_ERROR;
    return;
  }
  // Prepare response buffer
  memcpy(&res->data[0], &mfg_id, 1);
  (*res_len)++;
  memcpy(&res->data[1], &dev_id, 2);
  (*res_len) += 2;
  memcpy(&res->data[3], &sts, 2);
  (*res_len) += 2;
  res->cc = CC_SUCCESS;
}
static void
oem_get_pcie_port_config(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 ret;
  ret = pal_get_pcie_port_config(req->payload_id, req->data, req_len, res->data, res_len);
#ifdef DEBUG
  syslog(LOG_WARNING, "[%s] Get: %x %x\n", __func__, res->data[0], res->data[1]);
#endif
  if(ret == 0) {
    res->cc = CC_SUCCESS;
  } else {
    res->cc = CC_UNSPECIFIED_ERROR;
  }
}
static void
oem_set_pcie_port_config(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 ret;
  ret = pal_set_pcie_port_config(req->payload_id, req->data, req_len, res->data, res_len);
  if(ret == 0) {
    res->cc = CC_SUCCESS;
  } else {
    res->cc = CC_UNSPECIFIED_ERROR;
  }
}
static void
oem_add_cper_log(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;
  *res_len = 0;
  res->cc = pal_add_cper_log(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_set_psb_info(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;
  *res_len = 0;
  res->cc = pal_set_psb_info(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
oem_set_m2_info (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;
  char key[100] = {0};
  char payload[100] = {0};
  unsigned char cmd_len = 8;
  if (length_check(cmd_len, req_len, response, res_len))
    return;
  sprintf(key, "sys_config/fru%d_m2_%d_info", req->payload_id, req->data[0]);
  memcpy(payload, &req->data[1], req_len - 4);
  if(kv_set(key, payload, req_len - 4, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
  return;
}
static void
oem_get_80port_dword_record (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 ret;
  *res_len = 0;
  ret = pal_get_80port_page_record (req->payload_id, req->data[0], res->data, 256, (size_t *)res_len);
  switch(ret) {
    case PAL_EOK:
      res->cc = CC_SUCCESS;
      break;
    case PAL_ENOTSUP:
      res->cc = CC_INVALID_CMD;
      break;
    case PAL_ENOTREADY:
      res->cc = CC_NOT_SUPP_IN_CURR_STATE;
      break;
    default:
      res->cc = CC_UNSPECIFIED_ERROR;
      break;
  }
}
static void
oem_get_dev_card_sensor(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;
  res->cc = pal_get_dev_card_sensor(req->payload_id, req->data, req_len, res->data, res_len);
}
static void
oem_get_sensor_real_reading(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;
  uint8_t fru, snr_num;
  uint8_t ret = 0;
  float val = 0;
  if (req_len != 5) {
    res->cc = CC_INVALID_LENGTH;
    *res_len = 0;
    return;
  }
  fru = req->data[0];
  snr_num = req->data[1];
  ret = pal_sensor_read_raw(fru, snr_num, &val);
  if (ret == 0) {
    res->cc = CC_SUCCESS;
    *res_len = 3;
    res->data[0] = ((int)val) >> 8;
    res->data[1] = (int)val & 0xFF;
    res->data[2] = (((int)(val*100)) % 100);
  } else {
    *res_len = 0;
    res->cc = CC_UNSPECIFIED_ERROR;
  }
}
static void
oem_bbv_power_cycle ( 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 delay_time;
  *res_len = 0;
  delay_time = req->data[0];
  res->cc = bbv_power_cycle(delay_time);
}
static void
oem_stor_add_string_sel(unsigned char *request, unsigned char req_len,
                        unsigned char *response, unsigned char *res_len)
{
  // Byte0        Reserved
  // Byte1        String length
  // Byte2~ByteN  Event log string (ASCII)
  ipmi_mn_req_t *req = (ipmi_mn_req_t *) request;
  ipmi_res_t *res = (ipmi_res_t *) response;
  char string_log[248] = {0};
  //Reserve byte[0:3] bytes for future implementation
  uint8_t string_log_len = req->data[4];
  *res_len = 0;
  if (string_log_len >= sizeof(string_log)){
    res->cc = CC_INVALID_LENGTH;
    syslog(LOG_ERR, "%s(): max supported string length is %d, but got %d",
                       __func__, sizeof(string_log)-1, string_log_len);
    return;
  }
  memcpy(string_log, &req->data[5], string_log_len+1);
  // To avoid repeat display when Expander executes reset test
  // will filter fan fru checksum SEL
  if (!pal_handle_fan_fru_checksum_sel(string_log, string_log_len)) {
    res->cc = CC_SUCCESS;
    return;
  }
  syslog(LOG_CRIT, "%s", string_log);
  if (!pal_handle_string_sel(string_log, string_log_len))
    res->cc = CC_SUCCESS;
  else
    res->cc = CC_UNSPECIFIED_ERROR;
  return;
}
static void
oem_set_bios_cap_fw_ver(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 ret;
  ret = pal_set_bios_cap_fw_ver(req->payload_id, req->data, req_len, res->data, res_len);
  if(ret == 0) {
    res->cc = CC_SUCCESS;
  } else {
    res->cc = CC_UNSPECIFIED_ERROR;
  }
}
static void
oem_set_fscd(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 data = req->data[0];
  char cmd[100] = {0};
  switch (data)
  {
    case 0x00:
      sprintf(cmd, "sv force-stop fscd");
      break;
    case 0x01:
      sprintf(cmd, "sv start fscd");
      break;
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  if (system(cmd)) {
    syslog(LOG_WARNING, "set fscd cmd failed (%s)\n", cmd);
    res->cc = CC_UNSPECIFIED_ERROR;
  } else {
    res->cc = CC_SUCCESS;
  }
}
static void
oem_set_slot_power_policy(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 *data = &res->data[0];
  *data++ = 0x07;  // Power restore policy support(bitfield)
  // Set specific slave addr device's power restore policy
  res->cc = pal_set_slot_power_policy(req->data, res->data);
  if (res->cc == CC_SUCCESS) {
    *res_len = data - &res->data[0];
  }
}
static void
oem_get_usb_cdc_status (unsigned char *request, unsigned char req_len,
                   unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res= (ipmi_res_t *) response;
  int ret = 0;
  if (length_check(0, req_len, response, res_len)) {
    return;
  }
  ret = system("lsmod | grep -q g_cdc");
  if (ret != 0) {
    res->data[0] = DISABLE_USB_CDC;
  } else {
    res->data[0] = ENABLE_USB_CDC;
  }
  res->cc = CC_SUCCESS;
  *res_len = 1;
}
static void
oem_control_usb_cdc (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;
  uint8_t status_ctrl = req->data[0]; // 0 - disable, 1 - enable
  int ret = 0;
  if (length_check(1, req_len, response, res_len)) {
    return;
  }
  res->cc = CC_SUCCESS;
  if (status_ctrl == DISABLE_USB_CDC) {
    // Disable USB serial console by usbcons.sh, then unload USB CDC driver
    ret = run_command("/etc/init.d/usbcons.sh stop");
    if (ret == 0) {
      ret = system("modprobe -qr g_cdc");
      if (WIFEXITED(ret) == 0) {
        syslog(LOG_ERR, "%s: failed to disable ether-over-usb (usb0), ret: %d\n", __func__, ret);
        res->cc = CC_UNSPECIFIED_ERROR;
      }
    } else {
      syslog(LOG_ERR, "%s: failed to disable USB serial console, ret: %d\n", __func__, ret);
      res->cc = CC_UNSPECIFIED_ERROR;
    }
  } else if (status_ctrl == ENABLE_USB_CDC){
    // Enable USB serial console and reload USB CDC driver by usbcons.sh, then link up usb0
    // The 'restart' action will kill all usbmod.sh processes then start usbmon.sh
    ret = run_command("/etc/init.d/usbcons.sh restart");
    if (ret == 0) {
      ret = system("sleep 1; ifconfig usb0 up");
      if (WIFEXITED(ret) == 0) {
        syslog(LOG_ERR, "%s: failed to link up ether-over-usb (usb0), ret: %d\n", __func__, ret);
        res->cc = CC_UNSPECIFIED_ERROR;
      }
    } else {
      syslog(LOG_ERR, "%s: failed to enable ether-over-usb (usb0), ret: %d\n", __func__, ret);
      res->cc = CC_UNSPECIFIED_ERROR;
    }
  } else {
    res->cc = CC_INVALID_PARAM;
  }
  *res_len = 0;
}
static void
oem_set_ioc_fw_recovery (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;
  *res_len = 0;
  if (length_check(2, req_len, response, res_len) != 0) {
    return;
  }
  // cmd_len = req_len - 3 (payload_id, cmd and netfn)
  res->cc = pal_set_ioc_fw_recovery(req->data, (req_len - IPMI_MN_REQ_HDR_SIZE), res->data, res_len);
}
static void
oem_get_ioc_fw_recovery (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;
  uint8_t component = 0;
  *res_len = 0;
  if (length_check(1, req_len, response, res_len) != 0) {
    return;
  }
  component = req->data[0];
  res->cc = pal_get_ioc_fw_recovery(component, res->data, res_len);
}
static void
oem_setup_exp_uart_bridging (unsigned char *request, unsigned char req_len,
                   unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  *res_len = 0;
  if (length_check(0, req_len, response, res_len) != 0) {
    return;
  }
  res->cc = pal_setup_exp_uart_bridging();
}
static void
oem_teardown_exp_uart_bridging (unsigned char *request, unsigned char req_len,
                   unsigned char *response, unsigned char *res_len)
{
  ipmi_res_t *res = (ipmi_res_t *) response;
  *res_len = 0;
  if (length_check(0, req_len, response, res_len) != 0) {
    return;
  }
  res->cc = pal_teardown_exp_uart_bridging();
}
static void
oem_set_ioc_wwid (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;
  *res_len = 0;
  if (length_check(9, req_len, response, res_len) != 0) {
    return;
  }
  res->cc = pal_set_ioc_wwid(req->data, (req_len - IPMI_MN_REQ_HDR_SIZE), res->data, res_len);
}
static void
oem_get_ioc_wwid (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;
  uint8_t component = 0;
  *res_len = 0;
  if (length_check(1, req_len, response, res_len) != 0) {
    return;
  }
  component = req->data[0];
  res->cc = pal_get_ioc_wwid(component, res->data, res_len);
}
static void
oem_set_pcie_info (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;
  char key[100] = {0};
  char payload[100] = {0};
  size_t data_len = req_len - IPMI_MN_REQ_HDR_SIZE;
  sprintf(key, "sys_config/fru%d_pcie_i%02X_s%02X_info",
      req->payload_id, req->data[0], req->data[1]);
  memcpy(payload, &req->data[0], data_len);
  if(kv_set(key, payload, data_len, KV_FPERSIST)) {
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = 0;
    return;
  }
  res->cc = CC_SUCCESS;
  *res_len = 0;
  return;
}
static void
oem_bios_extra_setup(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;
  res->cc = pal_oem_bios_extra_setup(req->payload_id, req->data, req_len, res->data, res_len);
}
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_ADD_RAS_SEL:
      oem_add_ras_sel (request, req_len, response, res_len);
      break;
    case CMD_OEM_ADD_IMC_LOG:
      oem_add_imc_log (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PROC_INFO:
      oem_set_proc_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_PROC_INFO:
      oem_get_proc_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_DIMM_INFO:
      oem_set_dimm_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_DIMM_INFO:
      oem_get_dimm_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_BOOT_ORDER:
      if(length_check(SIZE_BOOT_ORDER, req_len, response, res_len)) {
        break;
      }
      oem_set_boot_order(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_BOOT_ORDER:
      oem_get_boot_order(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_TPM_PRESENCE:
      oem_set_tpm_presence(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_TPM_PRESENCE:
      oem_get_tpm_presence(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PPR:
    case CMD_OEM_LEGACY_SET_PPR:
      oem_set_ppr (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_PPR:
    case CMD_OEM_LEGACY_GET_PPR:
      oem_get_ppr (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_POST_START:
      oem_set_post_start (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_POST_END:
      oem_set_post_end (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PPIN_INFO:
      oem_set_ppin_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_ADR_TRIGGER:
      oem_set_adr_trigger(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_PLAT_INFO:
      oem_get_plat_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_SYSTEM_GUID:
      oem_set_device_sys_guid (request, req_len, response, res_len);
      break;
    case CMD_OEM_SLED_AC_CYCLE:
      oem_sled_ac_cycle (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_PCIE_CONFIG:
      oem_get_poss_pcie_config (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_IMC_VERSION:
      oem_set_imc_version (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_FW_UPDATE_STATE:
      oem_set_fw_update_state (request, req_len, response, res_len);
      break;
    case CMD_OEM_BYPASS_CMD:
      oem_bypass_cmd (request, req_len, response, res_len);
      break;
    case CMD_OEM_BYPASS_DEV_CARD:
      oem_bypass_dev_card (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_BOARD_ID:
      oem_get_board_id (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_80PORT_RECORD:
      oem_get_80port_record (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_FW_INFO:
      oem_get_fw_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_MACHINE_CONFIG_INFO:
      oem_set_machine_config_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_BIOS_FLASH_INFO:
      oem_set_flash_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_BIOS_FLASH_INFO:
      oem_get_flash_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_PCIE_PORT_CONFIG:
      if(length_check(0, req_len, response, res_len))
        break;
      oem_get_pcie_port_config(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PCIE_PORT_CONFIG:
      if(length_check(SIZE_PCIE_PORT_CONFIG, req_len, response, res_len))
        break;
      oem_set_pcie_port_config(request, req_len, response, res_len);
      break;
    case CMD_OEM_BBV_POWER_CYCLE:
      oem_bbv_power_cycle(request, req_len, response, res_len);
      break;
    case CMD_OEM_ADD_CPER_LOG:
      oem_add_cper_log(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PSB_INFO:
      oem_set_psb_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_M2_INFO:
      oem_set_m2_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_80_PORT_DWORD_BUFFER:
      oem_get_80port_dword_record (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_DEV_CARD_SENSOR:
      oem_get_dev_card_sensor(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_SENSOR_REAL_READING:
      oem_get_sensor_real_reading(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_BIOS_CAP_FW_VER:
      oem_set_bios_cap_fw_ver(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_FSCD:
      oem_set_fscd(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_POWER_POLICY:
      oem_set_slot_power_policy(request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_USB_CDC_STATUS:
      oem_get_usb_cdc_status(request, req_len, response, res_len);
      break;
    case CMD_OEM_CTRL_USB_CDC:
      oem_control_usb_cdc(request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_PCIE_INFO:
      oem_set_pcie_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_BIOS_EXTRA_SETUP:
      oem_bios_extra_setup(request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  pthread_mutex_unlock(&m_oem);
}
static void
ipmi_handle_oem_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;
  pthread_mutex_lock(&m_oem_storage);
  switch (cmd)
  {
    case CMD_OEM_STOR_ADD_STRING_SEL:
      oem_stor_add_string_sel (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_IOC_FW_RECOVERY:
      oem_set_ioc_fw_recovery (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_IOC_FW_RECOVERY:
      oem_get_ioc_fw_recovery (request, req_len, response, res_len);
      break;
    case CMD_OEM_SETUP_EXP_UART_BRIDGING:
      oem_setup_exp_uart_bridging (request, req_len, response, res_len);
      break;
    case CMD_OEM_TEARDOWN_EXP_UART_BRIDGING:
      oem_teardown_exp_uart_bridging (request, req_len, response, res_len);
      break;
    case CMD_OEM_SET_IOC_WWID:
      oem_set_ioc_wwid (request, req_len, response, res_len);
      break;
    case CMD_OEM_GET_IOC_WWID:
      oem_get_ioc_wwid (request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  pthread_mutex_unlock(&m_oem_storage);
}
static void
ipmi_handle_oem_q (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_q);
  switch (cmd)
  {
    case CMD_OEM_Q_SET_PROC_INFO:
      oem_q_set_proc_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_GET_PROC_INFO:
      oem_q_get_proc_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_SET_DIMM_INFO:
      oem_q_set_dimm_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_GET_DIMM_INFO:
      oem_q_get_dimm_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_SET_DRIVE_INFO:
      oem_q_set_drive_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_GET_DRIVE_INFO:
      oem_q_get_drive_info (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_SET_SMU_PSP_VER:
      oem_q_set_smu_psp_ver (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_GET_SMU_PSP_VER:
      oem_q_get_smu_psp_ver (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_SLED_CYCLE_PREPARE_REQUEST:
      oem_q_sled_cycle_prepare_request (request, req_len, response, res_len);
      break;
    case CMD_OEM_Q_SLED_CYCLE_PREPARE_STATUS:
      oem_q_sled_cycle_prepare_status (request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      break;
  }
  pthread_mutex_unlock(&m_oem_q);
}
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 (including 1 byte for interface type, 3 bytes for IANA ID)
  // The offset moves by one due to the payload ID
  memcpy(&req_buf[1], &request[BIC_INTF_HDR_SIZE], 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 (1 byte interface type, 3 bytes for IANA ID)
  memcpy(&res->data[4], res_buf, *res_len);
  // Add the OEM command's response
  res->cc = CC_SUCCESS;
  memcpy(res->data, &req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = req->data[3]; // Bridge-IC interface
  *res_len += 4; // Interface type + IANA ID
}
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[3]) {
    case BIC_INTF_ME:
#ifdef DEBUG
      syslog(LOG_INFO, "oem_1s_handle_ipmb_req: Command received from ME for "
                  "payload#%d\n", req->payload_id);
#endif
      oem_1s_handle_ipmb_kcs(request, req_len, response, res_len);
      break;
    case BIC_INTF_SOL:
      // TODO: Need to call Optional SoL message handler
#ifdef DEBUG
      syslog(LOG_INFO, "oem_1s_handle_ipmb_req: Command received from SOL for "
                  "payload#%d\n", req->payload_id);
#endif
      memcpy(res->data, req->data, 4); //IANA ID + Interface type
      res->cc = CC_SUCCESS;
      *res_len = 4;
      break;
    case BIC_INTF_KCS:
    case BIC_INTF_KCS_SMM:
    case BIC_INTF_SSIF:
    case BIC_INTF_IMC:
      oem_1s_handle_ipmb_kcs(request, req_len, response, res_len);
      break;
    default:
      // TODO: Need to add additonal interface handler, if supported
      syslog(LOG_WARNING, "oem_1s_handle_ipmb_req: Command received on intf#%d "
                 "for payload#%d", req->data[3], req->payload_id);
      memcpy(res->data, req->data, 4); //IANA ID + Interface type
      res->cc = CC_INVALID_PARAM;
      *res_len = 4;
      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:
      // As all bridge in messages are IPMI request
      // all IPMI request will be process by ipmi_handle
      // which will "properly" serialize the processing according to netfn
      // Thus it is not necessary to serialize processing of MSG-IN.
      pthread_mutex_unlock(&m_oem_1s);
      oem_1s_handle_ipmb_req(request, req_len, response, res_len);
      pthread_mutex_lock(&m_oem_1s);
      break;
    case CMD_OEM_1S_INTR:
#ifdef DEBUG
      syslog(LOG_INFO, "ipmi_handle_oem_1s (cmd 0x%02x): 1S server interrupt#%d received "
                "for payload#%d\n", cmd, req->data[3], req->payload_id);
#endif
      pal_handle_oem_1s_intr(req->payload_id, &(req->data[3]));
      res->cc = CC_SUCCESS;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
    case CMD_OEM_1S_POST_BUF:
      // Skip the first 3 bytes of IANA ID and one byte of length field
      for (i = SIZE_IANA_ID+1; i <= req->data[SIZE_IANA_ID]+SIZE_IANA_ID; i++) {
        pal_post_handle(req->payload_id, req->data[i]);
      }
      res->cc = CC_SUCCESS;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      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;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      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[3] == 0x0) {
         syslog(LOG_WARNING, "Cold Reset by Firmware Update\n");
         res->cc = CC_SUCCESS;
      } else if (req->data[3] == 0x01) {
         syslog(LOG_WARNING, "WDT Reset\n");
         res->cc = CC_SUCCESS;
      } else {
         syslog(LOG_WARNING, "Error\n");
         res->cc = CC_INVALID_PARAM;
      }
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
    case CMD_OEM_1S_BIC_UPDATE_MODE:
#ifdef DEBUG
      syslog(LOG_INFO, "ipmi_handle_oem_1s: BIC Update Mode received "
                "for payload#%d\n", req->payload_id);
#endif
      if (req->data[3] == 0x1) {
         syslog(LOG_INFO, "BIC Mode: Normal\n");
         res->cc = CC_SUCCESS;
      } else if (req->data[3] == 0x0F) {
         syslog(LOG_INFO, "BIC Mode: Update\n");
         res->cc = CC_SUCCESS;
      } else {
         syslog(LOG_WARNING, "Error\n");
         res->cc = CC_INVALID_PARAM;
      }
      pal_inform_bic_mode(req->payload_id, req->data[3]);
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
    case CMD_OEM_1S_ASD_MSG_IN:
      if (req_len > 7) {  // payload_id, netfn, cmd, IANA[3], data type
        pal_handle_oem_1s_asd_msg_in(req->payload_id, &req->data[3], req_len-6);
        res->cc = CC_SUCCESS;
      } else {
        res->cc = CC_INVALID_LENGTH;
      }
      memcpy(res->data, req->data, SIZE_IANA_ID);
      *res_len = 3;
      break;
    case CMD_OEM_1S_RAS_DUMP_IN:
      if (req_len > 7) {  // payload_id, netfn, cmd, IANA[3], data type
        pal_handle_oem_1s_ras_dump_in(req->payload_id, &req->data[3], req_len-7);
        res->cc = CC_SUCCESS;
      } else {
        res->cc = CC_INVALID_LENGTH;
      }
      memcpy(res->data, req->data, SIZE_IANA_ID);
      *res_len = 3;
      break;
    case CMD_OEM_1S_4BYTE_POST_BUF:
      // Skip the first 3 bytes of IANA ID and one byte of length field
      for(int k = SIZE_IANA_ID + 1; k < req->data[SIZE_IANA_ID]+SIZE_IANA_ID; k+=(sizeof(uint32_t)/sizeof(uint8_t)))
      {
        uint32_t port_buff = req->data[k] | (req->data[k+1] << 8) | (req->data[k+2] << 16) | (req->data[k+3] << 24);
        pal_display_4byte_post_code(req->payload_id, port_buff);
      }
      res->cc = CC_SUCCESS;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
    case CMD_OEM_1S_GET_SYS_FW_VER:
      if (req_len == 8) { // payload_id, netfn, cmd, IANA[3], data[0] (fru), data[1] (component)
        memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
        res->cc = pal_get_fw_ver(req->payload_id, &req->data[3], &res->data[3], res_len);
        *res_len += SIZE_IANA_ID;
      } else {
        res->cc = CC_INVALID_LENGTH;
        *res_len = SIZE_IANA_ID;
      }
      break;
    case CMD_OEM_1S_DEV_POWER:
      // payload_id, netfn, cmd, data[0] (device id), data[1] (action), data[2] (data)
      res->cc = pal_handle_oem_1s_dev_power(req->payload_id, &req->data[0], req_len-3, &res->data[0], res_len);
      break;
    case CMD_OEM_1S_UPDATE_SDR:
      res->cc = pal_handle_oem_1s_update_sdr(req->payload_id);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
  }
  pthread_mutex_unlock(&m_oem_1s);
}
static void
oem_usb_dbg_get_frame_info(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;
  uint8_t num_frames;
  int ret;
  ret = plat_udbg_get_frame_info();
  if (ret < 0) {
    memcpy(res->data, req->data, 3); // IANA ID
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = SIZE_IANA_ID;
    return;
  }
  num_frames = (uint8_t) ret;
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = num_frames;
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + 1;
}
static void
oem_usb_dbg_get_updated_frames(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;
  uint8_t num_updates;
  int ret;
  ret = plat_udbg_get_updated_frames(&num_updates, &res->data[4]);
  if (ret) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = SIZE_IANA_ID;
    return;
  }
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = num_updates;
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + 1 + num_updates;
}
static void
oem_usb_dbg_get_post_desc(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;
  uint8_t index;
  uint8_t next;
  uint8_t end;
  uint8_t phase;
  uint8_t count;
  int ret;
  index = req->data[3];
  phase = req->data[4];
  ret = plat_udbg_get_post_desc(index, &next, phase, &end, &count, &res->data[8]);
  if (ret) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = SIZE_IANA_ID;
    return;
  }
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = index;
  res->data[4] = next;
  res->data[5] = phase;
  res->data[6] = end;
  res->data[7] = count;
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + 5 + count;
}
static void
oem_usb_dbg_get_gpio_desc(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;
  uint8_t index;
  uint8_t next;
  uint8_t level;
  uint8_t in_out;
  uint8_t count;
  int ret;
  index = req->data[3];
  ret = plat_udbg_get_gpio_desc(index, &next, &level, &in_out, &count, &res->data[8]);
  if (ret) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = SIZE_IANA_ID;
    return;
  }
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = index;
  res->data[4] = next;
  res->data[5] = level;
  res->data[6] = in_out;
  res->data[7] = count;
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + 5 + count;
}
static void
oem_usb_dbg_get_frame_data(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;
  uint8_t frame;
  uint8_t page;
  uint8_t next;
  uint8_t count;
  int ret;
  frame = req->data[3];
  page = req->data[4];
  ret = plat_udbg_get_frame_data(frame, page, &next, &count, &res->data[7]);
  if (ret) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    res->cc = CC_UNSPECIFIED_ERROR;
    *res_len = SIZE_IANA_ID;
    return;
  }
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->data[3] = frame;
  res->data[4] = page;
  res->data[5] = next;
  res->data[6] = count;
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + 4 + count;
}
static void
oem_usb_dbg_control_panel(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;
  uint8_t panel;
  uint8_t operation;
  uint8_t item;
  uint8_t count;
  int ret;
  panel = req->data[3];
  operation = req->data[4];
  item = req->data[5];
  ret = plat_udbg_control_panel(panel, operation, item, &count, &res->data[3]);
  if (ret) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    res->cc = ret;
    *res_len = SIZE_IANA_ID;
    return;
  }
  memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
  res->cc = CC_SUCCESS;
  *res_len = SIZE_IANA_ID + count;
}
static void
ipmi_handle_oem_usb_dbg(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;
  char key[32] = "blk_ocp_cmd";
  char value[8] = {0};
  if ( kv_get(key, value, 0, 0) == 0 && atoi(value) != 0) {
    syslog(LOG_INFO, "lock cmd");
    res->cc = CC_NOT_SUPP_IN_CURR_STATE;
    *res_len = 0;
    return;
  }
  pthread_mutex_lock(&m_oem_usb_dbg);
  switch (cmd)
  {
    case CMD_OEM_USB_DBG_GET_FRAME_INFO:
      oem_usb_dbg_get_frame_info(request, req_len, response, res_len);
      break;
    case CMD_OEM_USB_DBG_GET_UPDATED_FRAMES:
      oem_usb_dbg_get_updated_frames(request, req_len, response, res_len);
      break;
    case CMD_OEM_USB_DBG_GET_POST_DESC:
      oem_usb_dbg_get_post_desc(request, req_len, response, res_len);
      break;
    case CMD_OEM_USB_DBG_GET_GPIO_DESC:
      oem_usb_dbg_get_gpio_desc(request, req_len, response, res_len);
      break;
    case CMD_OEM_USB_DBG_GET_FRAME_DATA:
      oem_usb_dbg_get_frame_data(request, req_len, response, res_len);
      break;
    case CMD_OEM_USB_DBG_CTRL_PANEL:
      oem_usb_dbg_control_panel(request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
  }
  pthread_mutex_unlock(&m_oem_usb_dbg);
}
static void
oem_zion_get_system_mode(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;
  uint8_t mode = 0xff;
  int ret;
  ret = pal_get_host_system_mode(&mode);
  if (ret == PAL_ENOTSUP) {
    memcpy(res->data, req->data, SIZE_IANA_ID); // IANA ID
    *res_len = SIZE_IANA_ID;
    res->cc = CC_INVALID_CMD;
  } else {
    res->data[0] = mode;
    *res_len = 1;
    res->cc = ret;
  }
}
static void
oem_zion_set_system_mode(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 ret;
  ret = pal_set_host_system_mode(req->data[0]);
  if (ret == PAL_ENOTSUP)
    res->cc = CC_INVALID_CMD;
  else
    res->cc = ret;
}
static void
oem_zion_set_usb_path(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;
  uint8_t slot = req->data[0];
  uint8_t endpoint = req->data[1];
  int ret;
  ret = pal_set_usb_path(slot, endpoint);
  if (ret == PAL_ENOTSUP)
    res->cc = CC_INVALID_CMD;
  else
    res->cc = ret;
}
static void
oem_zion_get_sensor_value(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;
  res->cc = pal_ipmb_get_sensor_val(req->payload_id, req->data, req_len, res->data, res_len);
  return;
}
static void
ipmi_handle_oem_zion(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_zion);
  switch (cmd)
  {
    case CMD_OEM_ZION_GET_SYSTEM_MODE:
      oem_zion_get_system_mode(request, req_len, response, res_len);
      break;
    case CMD_OEM_ZION_GET_SENSOR_VALUE:
      oem_zion_get_sensor_value(request, req_len, response, res_len);
      break;
    case CMD_OEM_ZION_SET_SYSTEM_MODE:
      oem_zion_set_system_mode(request, req_len, response, res_len);
      break;
    case CMD_OEM_ZION_SET_USB_PATH:
      oem_zion_set_usb_path(request, req_len, response, res_len);
      break;
    default:
      res->cc = CC_INVALID_CMD;
      memcpy(res->data, req->data, SIZE_IANA_ID); //IANA ID
      *res_len = 3;
      break;
  }
  pthread_mutex_unlock(&m_oem_zion);
}
/*
 * 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
  printf("ipmi_handle netfn %x cmd %x len %d\n", netfn, req->cmd, req_len);
  if (gLogEnable) {
    char logbuf[256];
    uint16_t nleft = sizeof(logbuf);
    uint16_t nwrite = 0;
    uint16_t i = 0;
    syslog(LOG_INFO, "%lu ipmi_handle  netfn %x cmd %x len %d\n",
          (unsigned long)time(NULL), netfn, req->cmd, req_len);
    nwrite = snprintf(logbuf, nleft, " ipmi_handle recv data: ");
    i += nwrite;
    nleft -= nwrite;
    for (int j = 0; j < req_len; ++j) {
      nwrite = snprintf(logbuf + i, nleft, "%x ", (unsigned int)(request[j]));
      i += nwrite;
      nleft -= nwrite;
    }
    syslog(LOG_INFO, "%s", logbuf);
  }
  *(unsigned short*)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_SENSOR_REQ:
      res->netfn_lun = NETFN_SENSOR_RES << 2;
      ipmi_handle_sensor (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_DCMI_REQ:
      res->netfn_lun = NETFN_DCMI_RES << 2;
      ipmi_handle_dcmi(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_STORAGE_REQ:
      res->netfn_lun = NETFN_OEM_STORAGE_RES << 2;
      ipmi_handle_oem_storage (request, req_len, response, res_len);
      break;
    case NETFN_OEM_Q_REQ:
      res->netfn_lun = NETFN_OEM_Q_RES << 2;
      ipmi_handle_oem_q (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;
    case NETFN_OEM_USB_DBG_REQ:
      res->netfn_lun = NETFN_OEM_USB_DBG_RES << 2;
      ipmi_handle_oem_usb_dbg(request, req_len, response, res_len);
      break;
    case NETFN_OEM_ZION_REQ:
      res->netfn_lun = NETFN_OEM_ZION_RES << 2;
      ipmi_handle_oem_zion(request, req_len, response, res_len);
      break;
    default:
      res->netfn_lun = (netfn + 1) << 2;
      break;
  }
  // This header includes NetFunction, Command, and Completion Code
  *(unsigned short*)res_len += IPMI_RESP_HDR_SIZE;
  return;
}
int
conn_handler(client_t *cli) {
  unsigned char req_buf[MAX_IPMI_MSG_SIZE];
  unsigned char res_buf[MAX_IPMI_MSG_SIZE];
  size_t req_len = MAX_IPMI_MSG_SIZE, res_len = 0;
  memset(req_buf, 0, sizeof(req_buf));
  memset(res_buf, 0, sizeof(res_buf));
  if (ipc_recv_req(cli, req_buf, &req_len, TIMEOUT_IPMI)) {
    syslog(LOG_WARNING, "ipmid: recv() failed\n");
    return -1;
  }
  ipmi_handle(req_buf, (unsigned char)req_len, res_buf, (unsigned char*)&res_len);
  if (res_len == 0) {
    return -1;
  }
  if(ipc_send_resp(cli, res_buf, res_len)) {
    syslog(LOG_WARNING, "ipmid: send() failed\n");
    return -1;
  }
  return 0;
}
void *
wdt_timer (void *arg) {
  int ret;
  struct watchdog_data *wdt = (struct watchdog_data *)arg;
  uint8_t status;
  int action = 0;
  while (1) {
    usleep(100*1000);
    pthread_mutex_lock(&wdt->mutex);
    if (wdt->valid && wdt->run) {
      // Check if power off
      ret = pal_get_server_power(wdt->slot, &status);
      if ((ret >= 0) && (status != SERVER_POWER_ON)) {
        wdt->run = 0;
        pthread_mutex_unlock(&wdt->mutex);
        continue;
      }
      // count down; counter 0 and run associated timer events occur immediately
      if (wdt->present_count_down)
        wdt->present_count_down--;
      // Pre-timeout no support
      /*
      if (wdt->present_count_down == wdt->pre_interval*10) {
      }
      */
      // Timeout
      if (wdt->present_count_down == 0) {
        wdt->expiration |= (1 << wdt->use);
        // Execute actin out of mutex
        action = wdt->action;
        pal_set_fru_post(wdt->slot, 0);
        if (wdt->no_log) {
          wdt->no_log = 0;
        }
        else {
          syslog(LOG_CRIT, "FRU: %u, %s Watchdog %s",
            wdt->slot,
            wdt_use_name[wdt->use & 0x7],
            wdt_action_name[action & 0x7]);
        }
        wdt->run = 0;
      } /* End of Timeout Action*/
    } /* End of Valid and Run */
    pthread_mutex_unlock(&wdt->mutex);
    // Execute actin out of mutex
    if (action) {
      if (pal_is_crashdump_ongoing(wdt->slot)) {
        syslog(LOG_WARNING, "ipmid: fru%u crashdump is ongoing, ignore wdt action %s",
          wdt->slot,
          wdt_action_name[action & 0x7]);
        action = 0;
        continue;
      }
      pal_set_restart_cause(wdt->slot, RESTART_CAUSE_WATCHDOG_EXPIRATION);
      switch (action) {
      case 1: // Hard Reset
        pal_set_server_power(wdt->slot, SERVER_POWER_RESET);
        break;
      case 2: // Power Down
        pal_set_server_power(wdt->slot, SERVER_POWER_OFF);
        break;
      case 3: // Power Cycle
        pal_set_server_power(wdt->slot, SERVER_POWER_CYCLE);
        break;
      case 0: // no action
      default:
        break;
      }
      action = 0;
    }
  } /* Forever while */
  pthread_exit(NULL);
}
// optional  "-d" cmd line option to enable logging
int
main (int argc, char **argv)
{
  int fru;
  pthread_t tid;
  uint8_t max_slot_num = 0;
  //daemon(1, 1);
  //openlog("ipmid", LOG_CONS, LOG_DAEMON);
  if (argc > 2) {
    syslog(LOG_ERR, "ipmid: argc > 2!");
    exit(1);
  } else {
    if ((argc == 2) && (strncmp(argv[1], "-d", 2) == 0)) {
      syslog(LOG_INFO, "ipmid: enable logging");
      gLogEnable = 1;
    }
  }
  plat_fruid_init();
  plat_sensor_init();
  plat_lan_init(&g_lan_config);
  sdr_init();
  sel_init();
  pthread_mutex_init(&m_chassis, NULL);
  pthread_mutex_init(&m_sensor, 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);
  pthread_mutex_init(&m_oem_usb_dbg, NULL);
  pthread_mutex_init(&m_oem_q, NULL);
  pthread_mutex_init(&m_oem_zion, NULL);
  pal_get_num_slots(&max_slot_num);
  fru = 1;
  while(fru <= max_slot_num){
    //tpm
    pal_create_TPMTimer(fru);
    struct watchdog_data *wdt_data = calloc(1, sizeof(struct watchdog_data));
    if (!wdt_data) {
      syslog(LOG_WARNING, "ipmid: allocation wdt info failed!\n");
      continue;
    }
    wdt_data->slot = fru;
    wdt_data->valid = 0;
    wdt_data->pre_interval = 1;
    pthread_mutex_init(&wdt_data->mutex, NULL);
    g_wdt[fru - 1] = wdt_data;
    pthread_create(&wdt_data->tid, NULL, wdt_timer, wdt_data);
    pthread_detach(wdt_data->tid);
    pal_set_def_restart_cause( fru );
    fru++;
  }
  // set flag to notice BMC ipmid is ready
  kv_set("flag_ipmid", "1", 0, 0);
  if (ipc_start_svc(SOCK_PATH_IPMI, conn_handler, MAX_REQUESTS, NULL, &tid) == 0) {
    pthread_join(tid, NULL);
  }
  pthread_mutex_destroy(&m_chassis);
  pthread_mutex_destroy(&m_sensor);
  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);
  pthread_mutex_destroy(&m_oem_usb_dbg);
  pthread_mutex_destroy(&m_oem_q);
  return 0;
}