common/recipes-core/ipmid/files/sdr.c (276 lines of code) (raw):
/*
 *
 * Copyright 2014-present Facebook. All Rights Reserved.
 *
 * This file represents platform specific implementation for storing
 * SDR record entries and acts as back-end for IPMI stack
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include "sdr.h"
#include "sensor.h"
#include "timestamp.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <openbmc/ipmi.h>
#include <openbmc/pal.h>
// SDR Header magic number
#define SDR_HDR_MAGIC 0xFBFBFBFB
// SDR Header version number
#define SDR_HDR_VERSION 0x01
// SDR reservation IDs can not be 0x00 or 0xFFFF
#define SDR_RSVID_MIN  0x01
#define SDR_RSVID_MAX  0xFFFE
#define SDR_RECORDS_MAX 64 // to support around 64 sensors
// SDR index to keep track
#define SDR_INDEX_MIN 0
#define SDR_INDEX_MAX (SDR_RECORDS_MAX - 1)
// Record ID can not be 0x0 (IPMI/Section 31)
#define SDR_RECID_MIN 1
#define SDR_RECID_MAX SDR_RECORDS_MAX
// Special RecID value for first and last (IPMI/Section 31)
#define SDR_RECID_FIRST 0x0000
#define SDR_RECID_LAST 0xFFFF
#define SDR_VERSION 0x51
#define SDR_LEN_MAX 64
#define SDR_FULL_TYPE 0x01
#define SDR_MGMT_TYPE 0x12
#define SDR_OEM_TYPE 0xC0
#define SDR_FULL_LEN 64
#define SDR_MGMT_LEN 32
#define SDR_OEM_LEN 64
// SDR header struct to keep track of SEL Log entries
typedef struct {
  int magic; // Magic number to check validity
  int version; // version number of this header
  int begin; // index to the first SDR entry
  int end; // index to the last SDR entry
  time_stamp_t ts_add; // last addition time stamp
  time_stamp_t ts_erase; // last erase time stamp
} sdr_hdr_t;
// Keep track of last Reservation ID
static int g_rsv_id[MAX_NODES+1];
// SDR Header and data global structures
static sdr_hdr_t g_sdr_hdr;
static sdr_rec_t g_sdr_data[SDR_RECORDS_MAX];
// Add a new SDR entry
static int
sdr_add_entry(sdr_rec_t *rec, int *rec_id) {
  // If SDR is full, return error
  if (sdr_num_entries() == SDR_RECORDS_MAX) {
      syslog(LOG_WARNING, "sdr_add_entry: SDR full\n");
      return -1;
  }
  // Add Record ID which is array index + 1
  rec->rec[0] = g_sdr_hdr.end+1;
  // Add the enry at end
  memcpy(g_sdr_data[g_sdr_hdr.end].rec, rec->rec, sizeof(sdr_rec_t));
  // Return the newly added record ID
  *rec_id = g_sdr_hdr.end+1;
  // Increment the end pointer
  ++g_sdr_hdr.end;
  // Update timestamp for add in header
  time_stamp_fill(g_sdr_hdr.ts_add.ts);
  return 0;
}
static int
sdr_add_mgmt_rec(sensor_mgmt_t *p_rec) {
  int rec_id = 0;
  sdr_rec_t sdr = { 0 };
  sdr_mgmt_t rec = { 0 };
  // Populate SDR MGMT record
  rec.ver = SDR_VERSION;
  rec.type = SDR_MGMT_TYPE;
  rec.len = SDR_MGMT_LEN;
  rec.slave_addr = p_rec->slave_addr;
  rec.chan_no = p_rec->chan_no;
  rec.pwr_state_init = p_rec->pwr_state_init;
  rec.dev_caps = p_rec->dev_caps;
  rec.ent_id = p_rec->ent_id;
  rec.ent_inst = p_rec->ent_inst;
  rec.oem = p_rec->oem;
  rec.str_type_len = p_rec->str_type_len;
  memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
  // Copy this record to generic SDR record
  // sdr.rec (SDR_LEN_MAX bytes) is expected
  // to be larger than the current record.
  memcpy(sdr.rec, &rec, sizeof(rec) < sizeof(sdr.rec) ?
      sizeof(rec) : sizeof(sdr.rec));
  // Add this record to SDR repo
  if (sdr_add_entry(&sdr, &rec_id)) {
    syslog(LOG_WARNING, "sdr_add_mgmt_rec: sdr_add_entry failed\n");
    return -1;
  }
  return 0;
}
static int
sdr_add_disc_rec(sensor_disc_t *p_rec) {
  int rec_id = 0;
  sdr_rec_t sdr = { 0 };
  sdr_full_t rec = { 0 };
  // Populate SDR FULL record
  rec.ver = SDR_VERSION;
  rec.type = SDR_FULL_TYPE;
  rec.len = SDR_FULL_LEN;
  rec.owner = p_rec->owner;
  rec.lun = p_rec->lun;
  rec.ent_id = p_rec->ent_id;
  rec.ent_inst = p_rec->ent_inst;
  rec.sensor_init = p_rec->sensor_init;
  rec.sensor_caps = p_rec->sensor_caps;
  rec.sensor_type = p_rec->sensor_type;
  rec.evt_read_type = p_rec->evt_read_type;
  memcpy(rec.assert_evt_mask, p_rec->assert_evt_mask, 2);
  memcpy(rec.deassert_evt_mask, p_rec->deassert_evt_mask, 2);
  memcpy(rec.read_evt_mask, p_rec->read_evt_mask, 2);
  rec.oem = p_rec->oem;
  rec.str_type_len = p_rec->str_type_len;
  memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
  // Copy this record to generic SDR record
  memcpy(sdr.rec, &rec, SDR_LEN_MAX);
  // Add this record to SDR repo
  if (sdr_add_entry(&sdr, &rec_id)) {
    syslog(LOG_WARNING, "sdr_add_disc_rec: sdr_add_entry failed\n");
    return -1;
  }
  return 0;
}
static int
sdr_add_thresh_rec(sensor_thresh_t *p_rec) {
  int rec_id = 0;
  sdr_rec_t sdr = { 0 };
  sdr_full_t rec = { 0 };
  // Populate SDR FULL record
  rec.ver = SDR_VERSION;
  rec.type = SDR_FULL_TYPE;
  rec.len = SDR_FULL_LEN;
  rec.owner = p_rec->owner;
  rec.lun = p_rec->lun;
  rec.ent_id = p_rec->ent_id;
  rec.ent_inst = p_rec->ent_inst;
  rec.sensor_init = p_rec->sensor_init;
  rec.sensor_caps = p_rec->sensor_caps;
  rec.sensor_type = p_rec->sensor_type;
  rec.evt_read_type = p_rec->evt_read_type;
  memcpy(rec.lt_read_mask, p_rec->lt_read_mask, 2);
  memcpy(rec.ut_read_mask, p_rec->ut_read_mask, 2);
  memcpy(rec.set_thresh_mask, p_rec->set_thresh_mask, 2);
  rec.sensor_units1 = p_rec->sensor_units1;
  rec.sensor_units2 = p_rec->sensor_units2;
  rec.sensor_units3 = p_rec->sensor_units3;
  rec.linear = p_rec->linear;
  rec.m_val = p_rec->m_val;
  rec.m_tolerance = p_rec->m_tolerance;
  rec.b_val = p_rec->b_val;
  rec.b_accuracy = p_rec->b_accuracy;
  rec.analog_flags = p_rec->analog_flags;
  rec.nominal = p_rec->nominal;
  rec.normal_max = p_rec->normal_max;
  rec.normal_min = p_rec->normal_min;
  rec.max_reading = p_rec->max_reading;
  rec.min_reading = p_rec->min_reading;
  rec.unr_thresh = p_rec->unr_thresh;
  rec.uc_thresh = p_rec->uc_thresh;
  rec.unc_thresh = p_rec->unc_thresh;
  rec.lnr_thresh = p_rec->lnr_thresh;
  rec.lc_thresh = p_rec->lc_thresh;
  rec.lnc_thresh = p_rec->lnc_thresh;
  rec.pos_hyst = p_rec->pos_hyst;
  rec.neg_hyst = p_rec->neg_hyst;
  rec.oem = p_rec->oem;
  rec.str_type_len = p_rec->str_type_len;
  memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
  // Copy this record to generic SDR record
  memcpy(sdr.rec, &rec, SDR_LEN_MAX);
  // Add this record to SDR repo
  if (sdr_add_entry(&sdr, &rec_id)) {
    syslog(LOG_WARNING, "sdr_add_thresh_rec: sdr_add_entry failed\n");
    return -1;
  }
  return 0;
}
static int
sdr_add_oem_rec(sensor_oem_t *p_rec) {
  int rec_id = 0;
  sdr_rec_t sdr = { 0 };
  sdr_oem_t rec = { 0 };
  // Populate SDR OEM record
  rec.ver = SDR_VERSION;
  rec.type = SDR_OEM_TYPE;
  rec.len = SDR_OEM_LEN;
  memcpy(rec.mfr_id, p_rec->mfr_id, 3);
  memcpy(rec.oem_data, p_rec->oem_data, SENSOR_OEM_DATA_SIZE);
  // Copy this record to generic SDR record
  memcpy(sdr.rec, &rec, SDR_LEN_MAX);
  // Add this record to SDR repo
  if (sdr_add_entry(&sdr, &rec_id)) {
    syslog(LOG_WARNING, "sdr_add_oem_rec: sdr_add_entry failed\n");
    return -1;
  }
  return 0;
}
// Platform specific SEL API entry points
// Retrieve time stamp for recent add operation
void
sdr_ts_recent_add(time_stamp_t *ts) {
  memcpy(ts->ts, g_sdr_hdr.ts_add.ts, 0x04);
}
// Retrieve time stamp for recent erase operation
void
sdr_ts_recent_erase(time_stamp_t *ts) {
  memcpy(ts->ts, g_sdr_hdr.ts_erase.ts, 0x04);
}
// Retrieve total number of entries in SDR repo
int
sdr_num_entries(void) {
    return (g_sdr_hdr.end - g_sdr_hdr.begin);
}
// Retrieve total free space available in SDR repo
int
sdr_free_space(void) {
  int total_space;
  int used_space;
  total_space = SDR_RECORDS_MAX * sizeof(sdr_rec_t);
  used_space = sdr_num_entries() * sizeof(sdr_rec_t);
  return (total_space - used_space);
}
// Reserve an ID that will be used in later operations
// IPMI/Section 33.11
int
sdr_rsv_id(int node) {
  // Increment the current reservation ID and return
  if (g_rsv_id[node]++ == SDR_RSVID_MAX) {
    g_rsv_id[node] = SDR_RSVID_MIN;
  }
  return g_rsv_id[node];
}
// Get the SDR entry for a given record ID
// IPMI/Section 33.12
int
sdr_get_entry(int node, int rsv_id, int read_rec_id, sdr_rec_t *rec,
                   int *next_rec_id) {
  int index;
  // Make sure the rsv_id matches
  if (rsv_id != g_rsv_id[node]) {
    syslog(LOG_WARNING, "sdr_get_entry: Reservation ID mismatch\n");
    return -1;
  }
  // Find the index in to array based on given index
  if (read_rec_id == SDR_RECID_FIRST) {
    index = g_sdr_hdr.begin;
  } else if (read_rec_id == SDR_RECID_LAST) {
    if (g_sdr_hdr.end) {
      index = g_sdr_hdr.end - 1;
    } else {
      index = SDR_INDEX_MAX;
    }
  } else {
    index = read_rec_id;
  }
  // If the SDR repo is empty return error
  if (sdr_num_entries() == 0) {
    syslog(LOG_WARNING, "sdr_get_entry: No entries\n");
    return -1;
  }
  // Check for boundary conditions
  if ((index < SDR_INDEX_MIN) || (index > SDR_INDEX_MAX)) {
    syslog(LOG_WARNING, "sdr_get_entry: Invalid Record ID %d\n", read_rec_id);
    return -1;
  }
  // Check to make sure the given id is valid
  if (index < g_sdr_hdr.begin || index >= g_sdr_hdr.end) {
    syslog(LOG_WARNING, "sdr_get_entry: Wrong Record ID %d\n", read_rec_id);
    return -1;
  }
  memcpy(rec->rec, g_sdr_data[index].rec, sizeof(sdr_rec_t));
  // Return the next record ID in the log
  *next_rec_id = ++read_rec_id;
  // If this is the last entry in the log, return 0xFFFF
  if (*next_rec_id == g_sdr_hdr.end) {
    *next_rec_id = SDR_RECID_LAST;
  }
  return 0;
}
// Initialize SDR Repo structure
int
sdr_init(void) {
  int num;
  int i;
  sensor_mgmt_t *p_mgmt;
  sensor_thresh_t *p_thresh;
  sensor_disc_t *p_disc;
  sensor_oem_t *p_oem;
  // Populate SDR Header
  g_sdr_hdr.magic = SDR_HDR_MAGIC;
  g_sdr_hdr.version = SDR_HDR_VERSION;
  g_sdr_hdr.begin = SDR_INDEX_MIN;
  g_sdr_hdr.end = SDR_INDEX_MIN;
  memset(g_sdr_hdr.ts_add.ts, 0x0, 4);
  memset(g_sdr_hdr.ts_erase.ts, 0x0, 4);
  // Populate all mgmt control sensors
  plat_sensor_mgmt_info(&num, &p_mgmt);
  for (i = 0; i < num; i++) {
    sdr_add_mgmt_rec(&p_mgmt[i]);
  }
  // Populate all discrete sensors
  plat_sensor_disc_info(&num, &p_disc);
  for (i = 0; i < num; i++) {
    sdr_add_disc_rec(&p_disc[i]);
  }
  // Populate all threshold sensors
  plat_sensor_thresh_info(&num, &p_thresh);
  for (i = 0; i < num; i++) {
    sdr_add_thresh_rec(&p_thresh[i]);
  }
  // Populate all OEM sensors
  plat_sensor_oem_info(&num, &p_oem);
  for (i = 0; i < num; i++) {
    sdr_add_oem_rec(&p_oem[i]);
  }
  // Initialize the reservation IDs
  for (i = 0; i <= MAX_NODES; i++) {
    g_rsv_id[i] = 0x01;
  }
  return 0;
}