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;
}