// 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 #include #include #ifndef _WIN32 #include #else #include #endif #include #include #include #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; }