// Copyright(c) 2018-2020, Intel Corporation
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include "bmc.h"
#define _TIMESPEC_DEFINED
#include "../../types_int.h"
#include "bmcdata.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <glob.h>
#define NULL_CHECK(x) \
do { \
if (NULL == (x)) { \
return FPGA_INVALID_PARAM; \
} \
} while (0)
fpga_result read_sysfs_file(fpga_token token, const char *file,
void **buf, uint32_t *tot_bytes_ret)
{
char sysfspath[SYSFS_PATH_MAX] = { 0, };
struct stat stats;
int fd = 0;
fpga_result res = FPGA_OK;
NULL_CHECK(token);
NULL_CHECK(buf);
NULL_CHECK(file);
NULL_CHECK(tot_bytes_ret);
*buf = NULL;
*tot_bytes_ret = 0;
struct _fpga_token *tok = (struct _fpga_token *)token;
if (FPGA_TOKEN_MAGIC != tok->magic) {
return FPGA_INVALID_PARAM;
}
if (snprintf(sysfspath, sizeof(sysfspath),
"%s/%s", tok->sysfspath, file) < 0) {
OPAE_ERR("snprintf buffer overflow");
return FPGA_EXCEPTION;
}
glob_t pglob;
int gres = glob(sysfspath, GLOB_NOSORT, NULL, &pglob);
if ((gres) || (1 != pglob.gl_pathc)) {
globfree(&pglob);
return FPGA_NOT_FOUND;
}
fd = open(pglob.gl_pathv[0], O_RDONLY);
globfree(&pglob);
if (fd < 0) {
return FPGA_NOT_FOUND;
}
if (fstat(fd, &stats) != 0) {
close(fd);
return FPGA_NOT_FOUND;
}
// fstat for a sysfs file is not accurate for the BMC
// Read the entire file into a temp buffer to get actual size of file
*buf = (void *)calloc(stats.st_size, 1);
int32_t tot_bytes = 0;
int32_t bytes_read = 0;
do {
bytes_read = (int32_t)read(fd, *buf, stats.st_size);
if (bytes_read < 0) {
if (errno == EINTR) {
bytes_read = 1; // Fool the while loop
continue;
}
}
tot_bytes += bytes_read;
} while ((tot_bytes < stats.st_size) && (bytes_read > 0));
close(fd);
if ((tot_bytes > stats.st_size) || (bytes_read < 0)) {
res = FPGA_EXCEPTION;
free(*buf);
*buf = NULL;
goto out;
}
*tot_bytes_ret = tot_bytes;
out:
return res;
}
fpga_result bmcLoadSDRs(fpga_token token, bmc_sdr_handle *records,
uint32_t *num_sensors)
{
fpga_result res = FPGA_OK;
size_t len;
NULL_CHECK(token);
NULL_CHECK(num_sensors);
struct _sdr_rec *recs = NULL;
struct _sdr_content *tmp = NULL;
uint32_t tot_bytes;
res = read_sysfs_file(token, SYSFS_SDR_FILE, (void **)&tmp, &tot_bytes);
if (FPGA_OK != res) {
if (tmp) {
free(tmp);
}
goto out;
}
uint32_t sz = sizeof(sdr_header) + sizeof(sdr_key) + sizeof(sdr_body);
uint32_t num_of_sensors = (tot_bytes + sz - 1) / sz;
*num_sensors = num_of_sensors;
if (NULL == records) {
free(tmp);
return FPGA_OK;
}
*records = (bmc_sdr_handle)calloc(1, sizeof(struct _sdr_rec));
if (NULL == *records) {
free(tmp);
return FPGA_NO_MEMORY;
}
recs = (struct _sdr_rec *)*records;
recs->contents = tmp;
recs->magic = BMC_SDR_MAGIC;
recs->num_records = num_of_sensors;
struct _fpga_token *tok = (struct _fpga_token *)token;
len = strnlen(tok->sysfspath, SYSFS_PATH_MAX - 1);
strncpy(recs->sysfs_path, tok->sysfspath, len + 1);
recs->token = token;
out:
return res;
}
fpga_result bmcReadSensorValues(bmc_sdr_handle records, bmc_values_handle *values,
uint32_t *num_values)
{
fpga_result res = FPGA_OK;
struct _bmc_values *vals = NULL;
NULL_CHECK(records);
struct _sdr_rec *sdr = (struct _sdr_rec *)records;
struct _sensor_reading *tmp = NULL;
if (BMC_SDR_MAGIC != sdr->magic) {
return FPGA_INVALID_PARAM;
}
NULL_CHECK(num_values);
if (NULL == values) {
*num_values = sdr->num_records;
return FPGA_OK;
}
uint32_t tot_bytes;
res = read_sysfs_file(sdr->token, SYSFS_SENSOR_FILE, (void **)&tmp,
&tot_bytes);
if ((NULL == tmp) || (FPGA_OK != res)) {
fprintf(stderr, "Cannot read sensor file.\n");
if (tmp) {
free(tmp);
}
return FPGA_EXCEPTION;
}
if (tot_bytes != (sdr->num_records * sizeof(sensor_reading))) {
fprintf(stderr,
"Struct / file size mismatch: file size %d,"
" struct size %d.\n",
(int)tot_bytes,
(int)(sdr->num_records * sizeof(sensor_reading)));
free(tmp);
return FPGA_EXCEPTION;
}
*num_values = sdr->num_records;
*values = (bmc_values_handle)calloc(1, sizeof(struct _bmc_values));
if (NULL == *values) {
free(tmp);
return FPGA_NO_MEMORY;
}
vals = (struct _bmc_values *)*values;
vals->contents = tmp;
vals->magic = BMC_VALUES_MAGIC;
vals->num_records = sdr->num_records;
vals->values = (Values **)calloc(sdr->num_records, sizeof(Values *));
if (NULL == vals->values) {
return FPGA_NO_MEMORY;
}
uint32_t i;
for (i = 0; i < sdr->num_records; i++) {
vals->values[i] = bmc_build_values(
&vals->contents[i], &sdr->contents[i].header,
&sdr->contents[i].key, &sdr->contents[i].body);
vals->values[i]->sdr = &sdr->contents[i];
}
return res;
}
fpga_result bmcGetSensorReading(bmc_values_handle values,
uint32_t sensor_number, uint32_t *is_valid,
double *value)
{
NULL_CHECK(values);
NULL_CHECK(value);
struct _bmc_values *vals = (struct _bmc_values *)values;
if (BMC_VALUES_MAGIC != vals->magic) {
return FPGA_INVALID_PARAM;
}
if (sensor_number >= vals->num_records) {
return FPGA_INVALID_PARAM;
}
*is_valid = vals->values[sensor_number]->is_valid;
*value = vals->values[sensor_number]->value.f_val;
return FPGA_OK;
}
fpga_result bmcThresholdsTripped(bmc_values_handle values,
tripped_thresholds **tripped,
uint32_t *num_tripped)
{
fpga_result res = FPGA_OK;
int num_tripd = 0;
NULL_CHECK(values);
NULL_CHECK(num_tripped);
struct _bmc_values *vals = (struct _bmc_values *)values;
uint32_t i;
if (BMC_VALUES_MAGIC != vals->magic) {
res = FPGA_INVALID_PARAM;
goto out;
}
// Count the number tripped
for (i = 0; i < vals->num_records; i++) {
uint8_t indicators = vals->contents[i].threshold_events._value
& BMC_THRESHOLD_EVENT_MASK;
if (0 == indicators) {
continue;
}
num_tripd++;
}
*num_tripped = num_tripd;
if (0 == num_tripd) {
if (NULL != tripped) {
*tripped = NULL;
}
goto out;
}
*tripped = (tripped_thresholds *)calloc(num_tripd,
sizeof(tripped_thresholds));
if (NULL == *tripped) {
return FPGA_NO_MEMORY;
}
tripped_thresholds *rets = *tripped;
int index = 0;
// Fill in the tripped structures
for (i = 0; i < vals->num_records; i++) {
struct _sdr_content *sdr = vals->values[i]->sdr;
uint8_t indicators = vals->contents[i].threshold_events._value
& BMC_THRESHOLD_EVENT_MASK;
if (0 == indicators) {
continue;
}
rets[index].sensor_number = i;
rets[index].sensor_type = SDR_SENSOR_IS_POWER(&sdr->body)
? BMC_POWER : BMC_THERMAL;
rets[index].which_thresholds = indicators;
index++;
}
out:
return res;
}
fpga_result bmcDestroySDRs(bmc_sdr_handle *records)
{
fpga_result res = FPGA_OK;
NULL_CHECK(records);
struct _sdr_rec *sdr = (struct _sdr_rec *)*records;
if (BMC_SDR_MAGIC != sdr->magic) {
res = FPGA_INVALID_PARAM;
goto out;
}
free(sdr->contents);
sdr->magic = 0;
free(sdr);
*records = NULL;
out:
return res;
}
fpga_result bmcDestroySensorValues(bmc_values_handle *values)
{
fpga_result res = FPGA_OK;
NULL_CHECK(values);
NULL_CHECK(*values);
struct _bmc_values *vals = (struct _bmc_values *)*values;
uint32_t i;
if (BMC_VALUES_MAGIC != vals->magic) {
res = FPGA_INVALID_PARAM;
goto out;
}
for (i = 0; i < vals->num_records; i++) {
free(vals->values[i]->name);
free(vals->values[i]);
}
free(vals->contents);
free(vals->values);
vals->magic = 0;
free(vals);
*values = NULL;
out:
return res;
}
static void getThresholdValues(sdr_details *details, Values *this_val,
struct _sdr_content *sdr)
{
uint8_t settable =
(sdr->body.discrete_settable_readable_threshold_mask._value
& 0x3f00)
>> 8;
details->thresholds.upper_nr_thresh.is_valid = 0;
details->thresholds.upper_c_thresh.is_valid = 0;
details->thresholds.upper_nc_thresh.is_valid = 0;
details->thresholds.lower_nr_thresh.is_valid = 0;
details->thresholds.lower_c_thresh.is_valid = 0;
details->thresholds.lower_nc_thresh.is_valid = 0;
if (!settable) {
return;
}
if (settable & (1 << 5)) {
details->thresholds.upper_nr_thresh.is_valid = 1;
details->thresholds.upper_nr_thresh.value =
getvalue(this_val, sdr->body.upper_nr_threshold);
}
if (settable & (1 << 4)) {
details->thresholds.upper_c_thresh.is_valid = 1;
details->thresholds.upper_c_thresh.value =
getvalue(this_val, sdr->body.upper_c_threshold);
}
if (settable & (1 << 3)) {
details->thresholds.upper_nc_thresh.is_valid = 1;
details->thresholds.upper_nc_thresh.value =
getvalue(this_val, sdr->body.upper_nc_threshold);
}
if (settable & (1 << 2)) {
details->thresholds.lower_nr_thresh.is_valid = 1;
details->thresholds.lower_nr_thresh.value =
getvalue(this_val, sdr->body.lower_nr_threshold);
}
if (settable & (1 << 1)) {
details->thresholds.lower_c_thresh.is_valid = 1;
details->thresholds.lower_c_thresh.value =
getvalue(this_val, sdr->body.lower_c_threshold);
}
if (settable & (1 << 0)) {
details->thresholds.lower_nc_thresh.is_valid = 1;
details->thresholds.lower_nc_thresh.value =
getvalue(this_val, sdr->body.lower_nc_threshold);
}
}
fpga_result bmcGetSDRDetails(bmc_values_handle values, uint32_t sensor_number,
sdr_details *details)
{
fpga_result res = FPGA_OK;
NULL_CHECK(values);
NULL_CHECK(details);
struct _bmc_values *vals = (struct _bmc_values *)values;
Values *this_val = NULL;
if (BMC_VALUES_MAGIC != vals->magic) {
res = FPGA_INVALID_PARAM;
goto out;
}
if (sensor_number >= vals->num_records) {
res = FPGA_INVALID_PARAM;
goto out;
}
this_val = vals->values[sensor_number];
details->sensor_number = sensor_number;
details->sensor_type = this_val->sensor_type;
details->name = this_val->name;
details->units = this_val->units;
details->M = this_val->M;
details->B = this_val->B;
details->accuracy = this_val->accuracy;
details->tolerance = this_val->tolerance;
details->result_exp = this_val->result_exp;
getThresholdValues(details, this_val, vals->values[sensor_number]->sdr);
out:
return res;
}
fpga_result bmcDestroyTripped(tripped_thresholds *tripped)
{
fpga_result res = FPGA_OK;
NULL_CHECK(tripped);
free(tripped);
return res;
}
fpga_result bmcGetFirmwareVersion(fpga_token token, uint32_t *version)
{
fpga_result res = FPGA_OK;
NULL_CHECK(token);
NULL_CHECK(version);
*version = (uint32_t)-1;
device_id *tmp = NULL;
uint32_t tot_bytes;
res = read_sysfs_file(token, SYSFS_DEVID_FILE, (void **)&tmp,
&tot_bytes);
if (FPGA_OK != res) {
goto out;
}
if (!tmp) {
res = FPGA_EXCEPTION;
goto out;
}
if (tmp->completion_code != 0) {
res = FPGA_NOT_FOUND;
goto out;
}
*version = tmp->aux_fw_rev_0_7 | (tmp->aux_fw_rev_8_15 << 8)
| (tmp->aux_fw_rev_16_23 << 16)
| (tmp->aux_fw_rev_24_31 << 24);
out:
if (tmp) {
free(tmp);
}
return res;
}
fpga_result bmcGetLastPowerdownCause(fpga_token token, char **cause)
{
fpga_result res = FPGA_OK;
NULL_CHECK(token);
NULL_CHECK(cause);
*cause = NULL;
powerdown_cause *tmp = NULL;
uint32_t tot_bytes;
res = read_sysfs_file(token, SYSFS_PWRDN_FILE, (void **)&tmp,
&tot_bytes);
if (FPGA_OK != res) {
goto out;
}
if (!tmp) {
res = FPGA_EXCEPTION;
goto out;
}
if (tmp->completion_code != 0) {
res = FPGA_NOT_FOUND;
goto out;
}
*cause = strndup((const char *)tmp->message,
strnlen((const char *)tmp->message, SYSFS_PATH_MAX));
out:
if (tmp) {
free(tmp);
}
return res;
}
fpga_result bmcGetLastResetCause(fpga_token token, char **cause)
{
fpga_result res = FPGA_OK;
NULL_CHECK(token);
NULL_CHECK(cause);
*cause = NULL;
reset_cause *tmp = NULL;
uint32_t tot_bytes;
res = read_sysfs_file(token, SYSFS_RESET_FILE, (void **)&tmp,
&tot_bytes);
if (FPGA_OK != res) {
goto out;
}
if (!tmp) {
res = FPGA_EXCEPTION;
goto out;
}
if (tmp->completion_code != 0) {
res = FPGA_NOT_FOUND;
*cause = strndup((const char *)"Unavailable",
strnlen((const char *)"Unavailable", SYSFS_PATH_MAX));
goto out;
}
if (0 == tmp->reset_cause) {
*cause = strndup((const char *)"None",
strnlen((const char *)"None", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_EXTRST) {
*cause = strndup((const char *)"External reset",
strnlen((const char *)"External reset", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_BOD_IO) {
*cause = strndup((const char *)"Brown-out detected",
strnlen((const char *)"Brown-out detected", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_OCD) {
*cause = strndup((const char *)"On-chip debug system",
strnlen((const char *)"On-chip debug system", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_POR) {
*cause = strndup((const char *)"Power-on-reset",
strnlen((const char *)"Power-on-reset", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_SOFT) {
*cause = strndup((const char *)"Software reset",
strnlen((const char *)"Software reset", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_SPIKE) {
*cause = strndup((const char *)"Spike detected",
strnlen((const char *)"Spike detected", SYSFS_PATH_MAX));
goto out;
}
if (tmp->reset_cause & CHIP_RESET_CAUSE_WDT) {
*cause = strndup((const char *)"Watchdog timeout",
strnlen((const char *)"Watchdog timeout", SYSFS_PATH_MAX));
goto out;
}
*cause = strndup((const char *)"Unknown",
strnlen((const char *)"Unknown", SYSFS_PATH_MAX));
out:
if (tmp) {
free(tmp);
}
return res;
}