meta-facebook/meta-grandcanyon/recipes-grandcanyon/plat-libs/files/pal/pal.c (3,409 lines of code) (raw):
/*
*
* Copyright 2020-present Facebook. All Rights Reserved.
*
* This file contains code to support IPMI2.0 Specification 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <ctype.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <openbmc/obmc-sensors.h>
#include <openbmc/libgpio.h>
#include <openbmc/phymem.h>
#include <openbmc/obmc-i2c.h>
#include <facebook/fbgc_gpio.h>
#include <sys/un.h>
#include "pal.h"
#define NUM_SERVER_FRU 1
#define NUM_NIC_FRU 1
#define NUM_BMC_FRU 1
#define MAX_FAN_NAME_LEN 32 // include the string terminal
#define MAX_PWM_LABEL_LEN 32 // include the string terminal
#define MAX_TEMP_STR_SIZE 16
#define MAX_NUM_GPIO_LED_POSTCODE 8
#define MAX_NUM_GPIO_BMC_FPGA_UART_SEL 4
#define MAX_NET_DEV_NAME_SIZE 10 // include the string terminal
#define UNIX_PATH_MAX 108
#define MAX_SNR_NAME 32
#define MAX_EVENT_STR 256
#define KV_KEY_BIC_HEARTBEAT "bic_hb_status"
#define NIC_CARD_PERST_CTRL 0x09
#define PWM_ZONE 0
const char pal_fru_list[] = "all, server, bmc, uic, dpb, scc, nic, e1s_iocm";
// export to sensor-util
const char pal_fru_list_sensor_history[] = "all, server, uic, nic, e1s_iocm";
// export to power-util
const char pal_server_list[] = "server";
const char pal_dev_pwr_list[] = "e1s0, e1s1";
const char pal_dev_pwr_option_list[] = "status, off, on";
// export to fruid-util
const char pal_fru_list_print[] = "all, server, bmc, uic, dpb, scc, nic, e1s_iocm, fan0, fan1, fan2, fan3";
const char pal_fru_list_rw[] = "server, bmc, uic, nic, e1s_iocm";
// fru name list for pal_get_fru_id()
const char *fru_str_list[] = {"all", "server", "bmc", "uic", "dpb", "scc", "nic", "e1s_iocm", "fan0", "fan1", "fan2", "fan3"};
const char pal_pwm_list[] = "0";
const char pal_tach_list[] = "0...7";
size_t pal_pwm_cnt = 1;
size_t pal_tach_cnt = 8;
enum key_event {
KEY_BEFORE_SET,
KEY_AFTER_INI,
};
enum net_intf_act {
NET_INTF_DISABLE,
NET_INTF_ENABLE,
};
enum {
UNBIND = 0,
BIND = 1
};
struct pal_key_cfg {
char *name;
char *def_val;
int (*function)(int, void*);
} key_cfg[] = {
/* name, default value, function */
{"system_identify_server", "off", NULL},
{"server_boot_order", "0100090203ff", NULL},
{"server_por_cfg", "on", NULL},
{"sysfw_ver_server", "0", NULL},
{"system_identify_led_interval", "default", NULL},
{"system_info", "0", NULL},
{"scc_ioc_fw_recovery", "0", NULL},
{"iocm_ioc_fw_recovery", "0", NULL},
{"ntp_server", "", NULL},
{"server_sel_error", "1", NULL},
{"server_sensor_health", "1", NULL},
{"uic_sensor_health", "1", NULL},
{"dpb_sensor_health", "1", NULL},
{"scc_sensor_health", "1", NULL},
{"nic_sensor_health", "1", NULL},
{"e1s_iocm_sensor_health", "1", NULL},
{"bmc_health", "1", NULL},
{"timestamp_sled", "0", NULL},
{"heartbeat_health", "1", NULL},
{"fan_dead_rearm", "0", NULL},
/* Add more Keys here */
{NULL, NULL, NULL} /* This is the last key of the list */
};
char * cfg_support_key_list[] = {
"server_por_cfg",
"system_info",
NULL /* This is the last key of the list */
};
typedef struct {
uint8_t gpio;
gpio_value_t present_gpio_value;
} fru_present_gpio;
fru_present_gpio fru_present_gpio_table[] = {
[FRU_SERVER] = {GPIO_COMP_PRSNT_N, GPIO_VALUE_LOW},
[FRU_SCC] = {GPIO_SCC_LOC_INS_N, GPIO_VALUE_LOW},
[FRU_NIC] = {GPIO_NIC_PRSNTB3_N, GPIO_VALUE_LOW},
[FRU_FAN0] = {GPIO_FAN_0_INS_N, GPIO_VALUE_LOW},
[FRU_FAN1] = {GPIO_FAN_1_INS_N, GPIO_VALUE_LOW},
[FRU_FAN2] = {GPIO_FAN_2_INS_N, GPIO_VALUE_LOW},
[FRU_FAN3] = {GPIO_FAN_3_INS_N, GPIO_VALUE_LOW}
};
uint8_t GPIO_LED_POSTCODE_TABLE[MAX_NUM_GPIO_LED_POSTCODE] = {
GPIO_LED_POSTCODE_0,
GPIO_LED_POSTCODE_1,
GPIO_LED_POSTCODE_2,
GPIO_LED_POSTCODE_3,
GPIO_LED_POSTCODE_4,
GPIO_LED_POSTCODE_5,
GPIO_LED_POSTCODE_6,
GPIO_LED_POSTCODE_7,
};
uint8_t GPIO_BMC_FPGA_UART_SEL_TABLE[MAX_NUM_GPIO_BMC_FPGA_UART_SEL] = {
GPIO_BMC_FPGA_UART_SEL3_R,
GPIO_BMC_FPGA_UART_SEL2_R,
GPIO_BMC_FPGA_UART_SEL1_R,
GPIO_BMC_FPGA_UART_SEL0_R,
};
uint8_t GPIO_BOARD_REV_ID_TABLE[MAX_NUM_OF_BOARD_REV_ID_GPIO] = {
GPIO_BOARD_REV_ID0,
GPIO_BOARD_REV_ID1,
GPIO_BOARD_REV_ID2,
};
PCIE_ERR_DECODE pcie_err_table[] = {
{0x00, "Receiver Error"},
{0x01, "Bad TLP"},
{0x02, "Bad DLLP"},
{0x03, "Replay Time-out"},
{0x04, "Replay Rollover"},
{0x05, "Advisory Non-Fatal"},
{0x06, "Corrected Internal Error"},
{0x07, "Header Log Overflow"},
{0x20, "Data Link Protocol Error"},
{0x21, "Surprise Down Error"},
{0x22, "Poisoned TLP"},
{0x23, "Flow Control Protocol Error"},
{0x24, "Completion Timeout"},
{0x25, "Completer Abort"},
{0x26, "Unexpected Completion"},
{0x27, "Receiver Buffer Overflow"},
{0x28, "Malformed TLP"},
{0x29, "ECRC Error"},
{0x2A, "Unsupported Request"},
{0x2B, "ACS Violation"},
{0x2C, "Uncorrectable Internal Error"},
{0x2D, "MC Blocked TLP"},
{0x2E, "AtomicOp Egress Blocked"},
{0x2F, "TLP Prefix Blocked Error"},
{0x30, "Poisoned TLP Egress Blocked"},
{0x50, "Received ERR_COR Message"},
{0x51, "Received ERR_NONFATAL Message"},
{0x52, "Received ERR_FATAL Message"},
{0x59, "LER was triggered by ERR_NONFATAL"},
{0x5A, "LER was triggered by ERR_FATAL"},
{0xA0, "PERR (non-AER)"},
{0xA1, "SERR (non-AER)"},
{0xFF, "None"}
};
static int
pal_key_index(char *key) {
int i = 0;
while(key_cfg[i].name != NULL) {
// If Key is valid, return index
if (!strncmp(key, key_cfg[i].name, strlen(key_cfg[i].name))) {
return i;
}
i++;
}
#ifdef DEBUG
syslog(LOG_WARNING, "%s() invalid key - %s", __func__, key);
#endif
return -1;
}
// Check what keys can be set by cfg-util
int
pal_cfg_key_check(char *key) {
int i = 0;
while(cfg_support_key_list[i] != NULL) {
// If Key is valid and can be set, return success
if (!strncmp(key, cfg_support_key_list[i], strlen(cfg_support_key_list[i]))) {
return 0;
}
i++;
}
// If Key could not be set, print syslog and return -1.
syslog(LOG_WARNING, "%s(): invalid key - %s", __func__, key);
return -1;
}
int
pal_get_key_value(char *key, char *value) {
int index = 0;
int ret = 0;
// Check key is defined and valid
if ((index = pal_key_index(key)) < 0) {
return -1;
}
ret = kv_get(key, value, NULL, KV_FPERSIST);
if ((ret == 0) && (strcmp(key, "server_por_cfg") == 0)) {
if (strcmp(value, "lps") == 0) {
printf("Warning: LPS state is deprecated, set the power policy to be ON by default.\n");
snprintf(value, MAX_VALUE_LEN, "on");
kv_set(key, value, 0, KV_FPERSIST);
}
}
return ret;
}
int
pal_set_key_value(char *key, char *value) {
int index = 0, ret = 0;
// Check key is defined and valid
if ((index = pal_key_index(key)) < 0) {
return -1;
}
if (key_cfg[index].function) {
ret = key_cfg[index].function(KEY_BEFORE_SET, value);
if (ret < 0) {
return ret;
}
}
if (strcmp(key, "server_por_cfg") == 0) {
if (strcmp(value, "lps") == 0) {
printf("Warning: LPS state is deprecated, set the power policy to be ON by default.\n");
snprintf(value, MAX_VALUE_LEN, "on");
}
}
return kv_set(key, value, 0, KV_FPERSIST);
}
void
pal_dump_key_value(void) {
int i = 0;
char value[MAX_VALUE_LEN] = {0x0};
while(key_cfg[i].name != NULL) {
memset(value, 0, MAX_VALUE_LEN);
printf("%s:", key_cfg[i].name);
if (kv_get(key_cfg[i].name, value, NULL, KV_FPERSIST) < 0) {
printf("\n");
} else {
printf("%s\n", value);
}
i++;
}
}
int
pal_set_def_key_value() {
int i = 0;
int ret = 0, failed_count = 0;
for (i = 0; key_cfg[i].name != NULL; i++) {
if ((ret = kv_set(key_cfg[i].name, key_cfg[i].def_val, 0, KV_FCREATE | KV_FPERSIST)) < 0) {
// Ignore the error messages when the kv node already existed.
if (errno != EEXIST) {
syslog(LOG_WARNING, "%s(): kv_set failed. %d", __func__, ret);
failed_count ++;
}
}
if (key_cfg[i].function) {
key_cfg[i].function(KEY_AFTER_INI, key_cfg[i].name);
}
}
if (failed_count != 0) {
return -1;
}
return 0;
}
int
pal_get_fru_id(char *str, uint8_t *fru) {
int fru_id = -1;
bool found_id = false;
for (fru_id = FRU_ALL; fru_id <= MAX_NUM_FRUS; fru_id++) {
if (strncmp(str, fru_str_list[fru_id], MAX_FRU_CMD_STR) == 0) {
*fru = fru_id;
found_id = true;
break;
}
}
return found_id ? 0 : -1;
}
int
pal_is_fru_ready(uint8_t fru, uint8_t *status) {
switch (fru) {
case FRU_SERVER:
// TODO: Get server power status and BIC ready pin
case FRU_BMC:
case FRU_UIC:
case FRU_DPB:
case FRU_SCC:
case FRU_NIC:
case FRU_E1S_IOCM:
case FRU_FAN0:
case FRU_FAN1:
case FRU_FAN2:
case FRU_FAN3:
*status = 1;
break;
default:
*status = 0;
syslog(LOG_WARNING, "%s() wrong fru id 0x%02x", __func__, fru);
return PAL_ENOTSUP;
}
return 0;
}
int pal_check_gpio_prsnt(uint8_t gpio, int presnt_expect) {
int ret = GPIO_VALUE_INVALID;
ret = gpio_get_value_by_shadow(fbgc_get_gpio_name(gpio));
if (ret == GPIO_VALUE_INVALID) {
syslog(LOG_ERR, "%s: failed to get gpio value: gpio%d\n",__func__, gpio);
return -1;
}
if (ret == presnt_expect) {
return FRU_PRESENT;
} else {
return FRU_ABSENT;
}
}
int
pal_is_fru_prsnt(uint8_t fru, uint8_t *status) {
int ret = 0;
int e1s_0_ret = 0, e1s_1_ret = 0;
switch (fru) {
case FRU_SERVER:
case FRU_SCC:
case FRU_NIC:
case FRU_FAN0:
case FRU_FAN1:
case FRU_FAN2:
case FRU_FAN3:
ret = pal_check_gpio_prsnt(fru_present_gpio_table[fru].gpio, fru_present_gpio_table[fru].present_gpio_value);
if (ret == -1) {
*status = 0;
return PAL_ENOTSUP;
}
*status = ret;
break;
case FRU_E1S_IOCM:
e1s_0_ret = pal_check_gpio_prsnt(GPIO_E1S_1_PRSNT_N, GPIO_VALUE_LOW);
e1s_1_ret = pal_check_gpio_prsnt(GPIO_E1S_2_PRSNT_N, GPIO_VALUE_LOW);
if (e1s_0_ret == -1 || e1s_1_ret == -1) {
*status = 0;
return PAL_ENOTSUP;
}
*status = 0;
if ( e1s_0_ret == FRU_PRESENT) {
*status |= E1S0_IOCM_PRESENT_BIT;
}
if ( e1s_1_ret == FRU_PRESENT) {
*status |= E1S1_IOCM_PRESENT_BIT;
}
break;
case FRU_BMC:
case FRU_UIC:
case FRU_DPB:
*status = 1;
break;
default:
*status = 0;
syslog(LOG_WARNING, "%s() wrong fru id 0x%02x", __func__, fru);
return PAL_ENOTSUP;
}
return 0;
}
int
pal_get_fruid_name(uint8_t fru, char *name) {
return fbgc_get_fruid_name(fru, name);
}
int
pal_get_fruid_path(uint8_t fru, char *path) {
return fbgc_get_fruid_path(fru, path);
}
int
pal_get_fruid_eeprom_path(uint8_t fru, char *path) {
return fbgc_get_fruid_eeprom_path(fru, path);
}
int
pal_fruid_write(uint8_t fru, char *path) {
if (fru == FRU_SERVER) {
return fbgc_fruid_write(0, path);
} else {
return -1;
}
}
int
pal_get_fru_list(char *list) {
snprintf(list, sizeof(pal_fru_list), pal_fru_list);
return 0;
}
int
pal_get_fru_capability(uint8_t fru, unsigned int *caps)
{
int ret = 0;
uint8_t chassis_type = 0;
switch (fru) {
case FRU_ALL:
*caps = FRU_CAPABILITY_SENSOR_READ | FRU_CAPABILITY_SENSOR_HISTORY;
break;
case FRU_SERVER:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_POWER_ALL | FRU_CAPABILITY_POWER_12V_ALL | FRU_CAPABILITY_SERVER;
break;
case FRU_BMC:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_MANAGEMENT_CONTROLLER;
break;
case FRU_NIC:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_NETWORK_CARD;
break;
case FRU_UIC:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL;
break;
case FRU_DPB:
*caps = FRU_CAPABILITY_FRUID_READ | FRU_CAPABILITY_SENSOR_READ | FRU_CAPABILITY_SENSOR_HISTORY;
break;
case FRU_SCC:
*caps = FRU_CAPABILITY_FRUID_READ | FRU_CAPABILITY_SENSOR_READ | FRU_CAPABILITY_SENSOR_HISTORY;
break;
case FRU_E1S_IOCM:
*caps = FRU_CAPABILITY_SENSOR_ALL;
fbgc_common_get_chassis_type(&chassis_type);
if (chassis_type == CHASSIS_TYPE7) {
*caps |= FRU_CAPABILITY_FRUID_ALL;
}
break;
case FRU_FAN0:
case FRU_FAN1:
case FRU_FAN2:
case FRU_FAN3:
*caps = FRU_CAPABILITY_FRUID_READ;
break;
default:
ret = -1;
break;
}
return ret;
}
int
pal_get_tach_cnt() {
uint8_t type = 0;
fbgc_common_get_chassis_type(&type);
if (type == CHASSIS_TYPE5) {
return DUAL_FAN_CNT;
} else if (type == CHASSIS_TYPE7) {
return SINGLE_FAN_CNT;
} else {
return UNKNOWN_FAN_CNT;
}
}
int
pal_get_fan_name(uint8_t fan_id, char *name) {
int fan_cnt = pal_get_tach_cnt();
if (fan_id >= fan_cnt) {
syslog(LOG_WARNING, "%s: Invalid fan index: %d, fan count: %d", __func__, fan_id, fan_cnt);
return -1;
}
if (fan_cnt == SINGLE_FAN_CNT) {
snprintf(name, MAX_FAN_NAME_LEN, "Fan %d %s", fan_id, "Front");
} else if (fan_cnt == DUAL_FAN_CNT) {
snprintf(name, MAX_FAN_NAME_LEN, "Fan %d %s", (fan_id / 2), fan_id % 2 == 0 ? "Front" : "Rear");
} else {
syslog(LOG_WARNING, "%s: Unknown fan count", __func__);
return -1;
}
return 0;
}
int
pal_get_fru_name(uint8_t fru, char *name) {
switch(fru) {
case FRU_ALL:
snprintf(name, MAX_FRU_CMD_STR, "all");
break;
case FRU_SERVER:
snprintf(name, MAX_FRU_CMD_STR, "server");
break;
case FRU_BMC:
snprintf(name, MAX_FRU_CMD_STR, "bmc");
break;
case FRU_UIC:
snprintf(name, MAX_FRU_CMD_STR, "uic");
break;
case FRU_DPB:
snprintf(name, MAX_FRU_CMD_STR, "dpb");
break;
case FRU_SCC:
snprintf(name, MAX_FRU_CMD_STR, "scc");
break;
case FRU_NIC:
snprintf(name, MAX_FRU_CMD_STR, "nic");
break;
case FRU_E1S_IOCM:
snprintf(name, MAX_FRU_CMD_STR, "e1s_iocm");
break;
case FRU_FAN0:
snprintf(name, MAX_FRU_CMD_STR, "fan0");
break;
case FRU_FAN1:
snprintf(name, MAX_FRU_CMD_STR, "fan1");
break;
case FRU_FAN2:
snprintf(name, MAX_FRU_CMD_STR, "fan2");
break;
case FRU_FAN3:
snprintf(name, MAX_FRU_CMD_STR, "fan3");
break;
default:
if (fru > MAX_NUM_FRUS) {
return -1;
}
snprintf(name, MAX_FRU_CMD_STR, "fru%d", fru);
break;
}
return 0;
}
int
pal_set_fan_speed(uint8_t fan_id, uint8_t pwm) {
if (fan_id >= pal_pwm_cnt) {
syslog(LOG_WARNING, "%s: Invalid fan index: %d", __func__, fan_id);
return -1;
}
return sensors_write_pwmfan(PWM_ZONE, (float)pwm);
}
int
pal_get_pwm_value(uint8_t fan_id, uint8_t *pwm) {
float value = 0;
int ret = 0;
if (fan_id >= pal_get_tach_cnt()) {
syslog(LOG_WARNING, "%s: Invalid fan index: %d", __func__, fan_id);
return -1;
}
ret = sensors_read_pwmfan(PWM_ZONE, &value);
if (ret == 0) {
*pwm = (uint8_t)value;
}
return ret;
}
// GUID for System and Device
static int
pal_get_guid(uint16_t offset, char *guid) {
char path[MAX_FILE_PATH] = {0};
int fd = 0;
int ret = 0;
ssize_t bytes_rd = 0;
// Set path for UIC
snprintf(path, MAX_FILE_PATH, EEPROM_PATH, I2C_UIC_BUS, UIC_FRU_ADDR);
// check for file presence
if (access(path, F_OK)) {
syslog(LOG_ERR, "%s() unable to access %s: %s", __func__, path, strerror(errno));
return -1;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
syslog(LOG_ERR, "%s() unable to open %s: %s", __func__, path, strerror(errno));
return -1;
}
lseek(fd, offset, SEEK_SET);
bytes_rd = read(fd, guid, GUID_SIZE);
if (bytes_rd != GUID_SIZE) {
syslog(LOG_ERR, "%s() read from %s failed: %s", __func__, path, strerror(errno));
ret = -1;
}
close(fd);
return ret;
}
static int
pal_set_guid(uint16_t offset, char *guid) {
char path[MAX_FILE_PATH] = {0};
int fd = 0;
int ret = 0;
ssize_t bytes_wr = 0;
// Set path for UIC
snprintf(path, MAX_FILE_PATH, EEPROM_PATH, I2C_UIC_BUS, UIC_FRU_ADDR);
// check for file presence
if (access(path, F_OK)) {
syslog(LOG_ERR, "%s() unable to access %s: %s", __func__, path, strerror(errno));
return -1;
}
fd = open(path, O_WRONLY);
if (fd < 0) {
syslog(LOG_ERR, "%s() unable to open %s: %s", __func__, path, strerror(errno));
return -1;
}
lseek(fd, offset, SEEK_SET);
bytes_wr = write(fd, guid, GUID_SIZE);
if (bytes_wr != GUID_SIZE) {
syslog(LOG_ERR, "%s() write to %s failed: %s", __func__, path, strerror(errno));
ret = -1;
}
close(fd);
return ret;
}
int
pal_get_sys_guid(uint8_t fru, char *guid) {
int ret = 0;
if (guid == NULL) {
return -1;
}
if (fru == FRU_SERVER) {
ret = bic_get_sys_guid(fru, (uint8_t *)guid, GUID_SIZE);
if (ret < 0) {
syslog(LOG_ERR, "%s() Failed to get system GUID\n", __func__);
}
} else {
ret = -1;
}
return ret;
}
int
pal_set_sys_guid(uint8_t fru, char *str) {
int ret = 0;
char guid[GUID_SIZE] = {0};
if (str == NULL) {
return -1;
}
if (fru == FRU_SERVER) {
pal_populate_guid(guid, str);
ret = bic_set_sys_guid(fru, (uint8_t *)guid, GUID_SIZE);
if (ret < 0) {
syslog(LOG_ERR, "%s() Failed to set system GUID\n", __func__);
}
} else {
ret = -1;
}
return ret;
}
int
pal_get_dev_guid(uint8_t fru, char *guid) {
return pal_get_guid(OFFSET_DEV_GUID, guid);
}
int
pal_set_dev_guid(uint8_t fru, char *str) {
char guid[GUID_SIZE] = {0};
pal_populate_guid(guid, str);
return pal_set_guid(OFFSET_DEV_GUID, guid);
}
// Update the Identification LED for the given fru with the status
int
pal_set_id_led(uint8_t fru, enum LED_HIGH_ACTIVE status) {
int ret = 0;
gpio_value_t val = 0;
if (fru != FRU_UIC) {
return -1;
}
val = (status == LED_ON) ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW;
ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_BMC_LED_PWR_BTN_EN_R), val);
return ret;
}
int
pal_set_hb_led(uint8_t status) {
int ret = 0;
gpio_value_t val = 0;
if (status == LED_ON) {
val = GPIO_VALUE_HIGH;
} else {
val = GPIO_VALUE_LOW;
}
ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_BMC_LOC_HEARTBEAT_R), val);
return ret;
}
int
pal_set_status_led(uint8_t fru, status_led_color color) {
int ret = 0;
gpio_value_t val_yellow = 0, val_blue = 0;
if (fru != FRU_UIC) {
return -1;
}
switch (color) {
case STATUS_LED_OFF:
val_yellow = GPIO_VALUE_HIGH;
val_blue = GPIO_VALUE_LOW;
break;
case STATUS_LED_BLUE:
val_yellow = GPIO_VALUE_HIGH;
val_blue = GPIO_VALUE_HIGH;
break;
case STATUS_LED_YELLOW:
val_yellow = GPIO_VALUE_LOW;
val_blue = GPIO_VALUE_LOW;
break;
default:
syslog(LOG_ERR, "%s() Invalid LED color: %d\n", __func__, color);
return -1;
}
if (0 != (ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_BMC_LED_STATUS_YELLOW_EN_R), val_yellow))) {
syslog(LOG_ERR, "%s() Failed to set GPIO BMC_LED_STATUS_YELLOW_EN_R to %d\n", __func__, val_yellow);
}
if (0 != (ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_BMC_LED_STATUS_BLUE_EN_R), val_blue))) {
syslog(LOG_ERR, "%s() Failed to set GPIO BMC_LED_STATUS_BLUE_EN_R to %d\n", __func__, val_blue);
}
return ret;
}
int
pal_set_e1s_led(uint8_t fru, e1s_led_id id, enum LED_HIGH_ACTIVE status) {
int ret = 0;
gpio_value_t val = 0;
if (fru != FRU_E1S_IOCM) {
return -1;
}
if (status == LED_ON) {
val = GPIO_VALUE_HIGH;
} else {
val = GPIO_VALUE_LOW;
}
if (id == ID_E1S0_LED) {
ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_E1S_1_LED_ACT), val);
} else if (id == ID_E1S1_LED) {
ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_E1S_2_LED_ACT), val);
} else {
return -1;
}
return ret;
}
int
pal_get_boot_order(uint8_t slot, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) {
int i = 0;
int j = 0;
int ret = 0;
int msb = 0, lsb = 0;
int tmp_len = 0;
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
char tmp_str[4] = {0};
tmp_len = sizeof(tmp_str);
snprintf(key, MAX_KEY_LEN, "server_boot_order");
ret = pal_get_key_value(key, str);
if (ret != 0) {
*res_len = 0;
return ret;
}
for (i = 0; i < 2*SIZE_BOOT_ORDER; i += 2) {
snprintf(tmp_str, tmp_len, "%c\n", str[i]);
msb = strtol(tmp_str, NULL, 16);
snprintf(tmp_str, tmp_len, "%c\n", str[i+1]);
lsb = strtol(tmp_str, NULL, 16);
boot[j++] = (msb << 4) | lsb;
}
*res_len = SIZE_BOOT_ORDER;
return 0;
}
int
pal_set_boot_order(uint8_t slot, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) {
int i = 0;
int tmp_len = 0;
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
char tmp_str[4] = {0};
*res_len = 0;
tmp_len = sizeof(tmp_str);
snprintf(key, MAX_KEY_LEN, "server_boot_order");
for (i = 0; i < SIZE_BOOT_ORDER; i++) {
snprintf(tmp_str, tmp_len, "%02x", boot[i]);
strncat(str, tmp_str, tmp_len);
}
return pal_set_key_value(key, str);
}
bool
pal_is_fw_update_ongoing_system(void) {
uint8_t i = 0;
for (i = FRU_SERVER; i < FRU_CNT; i++) {
if (pal_is_fw_update_ongoing(i) == true) {
return true;
}
}
return false;
}
int
pal_is_slot_server(uint8_t fru) {
return (fru == FRU_SERVER) ? 1 : 0;
}
int
pal_get_sysfw_ver_from_bic(uint8_t slot, uint8_t *ver) {
int ret = 0;
uint8_t bios_post_cmplt = 0;
bic_gpio_t gpio = {0};
// Check BIOS is completed via BIC
if (bic_get_gpio(&gpio) < 0) {
syslog(LOG_WARNING, "%s() Failed to get value of BIOS complete pin via BIC", __func__);
return -1;
}
bios_post_cmplt = ((((uint8_t*)&gpio)[BIOS_POST_CMPLT/8]) >> (BIOS_POST_CMPLT % 8)) & 0x1;
if (bios_post_cmplt != GPIO_VALUE_LOW) {
syslog(LOG_WARNING, "%s() Failed to get BIOS firmware version because BIOS is not ready", __func__);
return -1;
}
// Get BIOS firmware version from BIC if key: sysfw_ver_server is not set
if (bic_get_sys_fw_ver(ver) < 0) {
syslog(LOG_WARNING, "%s() failed to get system firmware version from BIC", __func__);
return -1;
}
// Set BIOS firmware version to key: sysfw_ver_server
if (pal_set_sysfw_ver(slot, ver) < 0) {
syslog(LOG_WARNING, "%s() failed to set key value of system firmware version", __func__);
return -1;
}
return ret;
}
int
pal_set_sysfw_ver(uint8_t slot, uint8_t *ver) {
int i = 0, ret = 0;
int tmp_len = 0;
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
char tmp_str[MAX_TEMP_STR_SIZE] = {0};
if (ver == NULL) {
syslog(LOG_ERR, "%s() Pointer \"ver\" is NULL.\n", __func__);
return -1;
}
snprintf(key, sizeof(key), "sysfw_ver_server");
for (i = 0; i < SIZE_SYSFW_VER; i++) {
tmp_len = sizeof(tmp_str);
memset(tmp_str, 0, sizeof(tmp_str));
snprintf(tmp_str, sizeof(tmp_str), "%02x", ver[i]);
strncat(str, tmp_str, tmp_len);
}
ret = pal_set_key_value(key, str);
if (ret < 0) {
syslog(LOG_WARNING, "%s: failed to set key value %s.", __func__, key);
}
return ret;
}
int
pal_get_sysfw_ver(uint8_t slot, uint8_t *ver) {
int i = 0, j = 0;
int ret = 0;
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
char tmp_str[MAX_TEMP_STR_SIZE] = {0};
if (ver == NULL) {
syslog(LOG_ERR, "%s() Pointer \"ver\" is NULL.\n", __func__);
return -1;
}
snprintf(key, sizeof(key), "sysfw_ver_server");
ret = pal_get_key_value(key, str);
// Get BIOS f/w version from BIC if get key value failed or if key value was not set.
if ((ret != 0) || (strcmp(str, "0") == 0)) {
return pal_get_sysfw_ver_from_bic(slot, ver);
}
for (i = 0; i < 2*SIZE_SYSFW_VER; i += 2) {
snprintf(tmp_str, sizeof(tmp_str), "%c%c\n", str[i], str[i+1]);
ver[j++] = (uint8_t) strtol(tmp_str, NULL, 16);
}
return ret;
}
// Use part of the function for IPMI OEM Command "CMD_OEM_GET_POSS_PCIE_CONFIG" 0xF4
int pal_get_poss_pcie_config(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) {
int ret = 0;
uint8_t chassis_type = 0;
get_pcie_config_response response;
memset(&response, 0, sizeof(response));
response.completion_code = CC_UNSPECIFIED_ERROR;
if (req_data == NULL) {
syslog(LOG_WARNING, "%s(): fail to get PCIe configuration beacuse parameter: *req_data is NULL pointer", __func__);
return response.completion_code;
}
if (res_data == NULL) {
syslog(LOG_WARNING, "%s(): fail to get PCIe configuration beacuse parameter: *res_data is NULL pointer", __func__);
return response.completion_code;
}
if (res_len == NULL) {
syslog(LOG_WARNING, "%s(): fail to get PCIe configuration beacuse parameter: *res_len is NULL pointer", __func__);
return response.completion_code;
}
ret = fbgc_common_get_chassis_type(&chassis_type);
if ((ret == 0) && (chassis_type == CHASSIS_TYPE5)) {
response.pcie_cfg = PCIE_CONFIG_TYPE5;
} else if ((ret == 0) && (chassis_type == CHASSIS_TYPE7)) {
response.pcie_cfg = PCIE_CONFIG_TYPE7;
} else {
syslog(LOG_WARNING, "%s(): fail to get PCIe configuration because fbgc_common_get_chassis_type() error", __func__);
return response.completion_code;
}
memcpy(res_data, &response.pcie_cfg, MIN(MAX_IPMI_MSG_SIZE, sizeof(response.pcie_cfg)));
*res_len = sizeof(response.pcie_cfg);
response.completion_code = CC_SUCCESS;
return response.completion_code;
}
int
pal_add_i2c_device(uint8_t bus, uint8_t addr, char *device_name) {
int ret = 0;
char cmd[MAX_PATH_LEN] = {0};
char path[MAX_PATH_LEN] = {0};
FILE *fp = NULL;
if (device_name == NULL) {
syslog(LOG_ERR, "%s device name is null", __func__);
return -1;
}
snprintf(path, sizeof(path), "/sys/class/i2c-dev/i2c-%d/device/new_device", bus);
fp = fopen(path, "w");
if(fp == NULL) {
syslog(LOG_ERR, "%s Failed to open file: %s. %s", __func__, path, strerror(errno));
return -1;
}
snprintf(cmd, sizeof(cmd), "%s %d",device_name, addr);
if (fwrite(cmd, sizeof(char), strlen(cmd), fp) != strlen(cmd)) {
syslog(LOG_ERR, "%s Failed to write file: %s. %s", __func__, path, strerror(errno));
ret = -1;
}
fclose(fp);
return ret;
}
int
pal_del_i2c_device(uint8_t bus, uint8_t addr) {
int ret = 0;
char cmd[MAX_PATH_LEN] = {0};
char path[MAX_PATH_LEN] = {0};
FILE *fp = NULL;
snprintf(path, sizeof(path), "/sys/class/i2c-dev/i2c-%d/device/delete_device", bus);
fp = fopen(path, "w");
if(fp == NULL) {
syslog(LOG_ERR, "%s Failed to open file: %s. %s", __func__, path, strerror(errno));
return -1;
}
snprintf(cmd, sizeof(cmd), "%d", addr);
if (fwrite(cmd, sizeof(char), strlen(cmd), fp) != strlen(cmd)) {
syslog(LOG_ERR, "%s Failed to write file: %s. %s", __func__, path, strerror(errno));
ret = -1;
}
fclose(fp);
return ret;
}
static int
i2c_device_binding_operation(uint8_t bus, uint8_t addr, char *driver_name, uint8_t operation) {
int ret = 0;
char cmd[MAX_PATH_LEN] = {0};
char path[MAX_PATH_LEN] = {0};
FILE *fp = NULL;
if (driver_name == NULL) {
syslog(LOG_ERR, "%s driver name is null", __func__);
return -1;
}
if (operation == BIND) {
snprintf(path, sizeof(path), "/sys/bus/i2c/drivers/%s/bind", driver_name);
} else {
snprintf(path, sizeof(path), "/sys/bus/i2c/drivers/%s/unbind", driver_name);
}
fp = fopen(path, "w");
if(fp == NULL) {
syslog(LOG_ERR, "%s Failed to open file: %s. %s", __func__, path, strerror(errno));
return -1;
}
snprintf(cmd, sizeof(cmd), "%d-00%02x", bus, addr);
if (fwrite(cmd, sizeof(char), strlen(cmd), fp) != strlen(cmd)) {
syslog(LOG_ERR, "%s Failed to write file: %s. %s", __func__, path, strerror(errno));
ret = -1;
}
fclose(fp);
return ret;
}
int
pal_bind_i2c_device(uint8_t bus, uint8_t addr, char *driver_name, char *bind_dir) {
if (bind_dir != NULL && access(bind_dir, F_OK) != 0) {
return i2c_device_binding_operation(bus, addr, driver_name, BIND);
}
return 0;
}
int
pal_unbind_i2c_device(uint8_t bus, uint8_t addr, char *driver_name, char *bind_dir) {
if (bind_dir != NULL && access(bind_dir, F_OK) == 0) {
return i2c_device_binding_operation(bus, addr, driver_name, UNBIND);
}
return 0;
}
// To get the platform sku
int pal_get_sku(platformInformation *pal_sku) {
int i = 0;
int ret = 0;
int pal_sku_size = 0, pal_sku_value = 0;
uint8_t tmp_pal_sku[SKU_SIZE] = {0};
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
if (pal_sku == NULL) {
syslog(LOG_ERR, "%s(): Failed to get platform SKU because parameter is NULL\n", __func__);
return -1;
}
pal_sku_size = sizeof(platformInformation);
// PAL_SKU[0:1] = {UIC_ID0, UIC_ID1}
// PAL_SKU[2:5] = {UIC_TYPE0, UIC_TYPE1, UIC_TYPE2, UIC_TYPE3}
snprintf(key, MAX_KEY_LEN, "system_info");
ret = pal_get_key_value(key, str);
if (ret < 0) {
syslog(LOG_ERR, "%s(): Failed to get platform SKU because failed to get key value of %s\n", __func__, key);
return -1;
}
pal_sku_value = atoi(str);
if (pal_sku_value >= MAX_SKU_VALUE) {
syslog(LOG_WARNING, "%s(): Failed to get platform SKU because SKU value is wrong\n", __func__);
return -1;
} else {
for (i = pal_sku_size - 1; i >= 0; i--) {
tmp_pal_sku[i] = (pal_sku_value & 1) + '0';
pal_sku_value = pal_sku_value >> 1;
}
memcpy(pal_sku, tmp_pal_sku, pal_sku_size);
}
return ret;
}
// To get the UIC location
int pal_get_uic_location(uint8_t *uic_id){
int ret = 0;
// Add one byte of NULL for converting string to integer.
char tmp_uic_id[SKU_UIC_ID_SIZE + 1] = {0};
platformInformation pal_sku;
memset(&pal_sku, 0, sizeof(pal_sku));
if (uic_id == NULL) {
syslog(LOG_ERR, "%s(): Failed to get UIC location because parameter is NULL\n", __func__);
return -1;
}
// UIC_ID[0:1]: 01=UIC_A; 10=UIC_B
ret = pal_get_sku(&pal_sku);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): Failed to get UIC location because failed to get sku value\n", __func__);
return -1;
}
memcpy(tmp_uic_id, pal_sku.uicId, MIN(sizeof(tmp_uic_id), sizeof(pal_sku.uicId)));
*uic_id = (uint8_t) strtol(tmp_uic_id, NULL, 2);
return ret;
}
//For IPMI OEM command "CMD_OEM_GET_PLAT_INFO" 0x7E
int pal_get_plat_sku_id(void){
uint8_t uic_type = 0;
uint8_t uic_location = 0;
int platform_info = 0;
if ((fbgc_common_get_chassis_type(&uic_type) < 0) || (pal_get_uic_location(&uic_location) < 0)) {
return -1;
}
if(uic_type == CHASSIS_TYPE5) {
if(uic_location == UIC_SIDEA) {
platform_info = PLAT_INFO_SKUID_TYPE5A;
} else if(uic_location == UIC_SIDEB) {
platform_info = PLAT_INFO_SKUID_TYPE5B;
} else {
return -1;
}
} else if (uic_type == CHASSIS_TYPE7) {
platform_info = PLAT_INFO_SKUID_TYPE7_HEADNODE;
} else {
return -1;
}
return platform_info;
}
int
pal_copy_eeprom_to_bin(const char *eeprom_file, const char *bin_file) {
int eeprom = 0;
int bin = 0;
int ret = 0;
uint8_t tmp[FRUID_SIZE] = {0};
ssize_t bytes_rd = 0, bytes_wr = 0;
errno = 0;
if (eeprom_file == NULL || bin_file == NULL) {
syslog(LOG_ERR, "%s: invalid parameter", __func__);
return -1;
}
eeprom = open(eeprom_file, O_RDONLY);
if (eeprom < 0) {
syslog(LOG_ERR, "%s: unable to open the %s file: %s",
__func__, eeprom_file, strerror(errno));
return -1;
}
bin = open(bin_file, O_WRONLY | O_CREAT, 0644);
if (bin < 0) {
syslog(LOG_ERR, "%s: unable to create %s file: %s",
__func__, bin_file, strerror(errno));
ret = -1;
goto err;
}
bytes_rd = read(eeprom, tmp, FRUID_SIZE);
if (bytes_rd < 0) {
syslog(LOG_ERR, "%s: read %s file failed: %s",
__func__, eeprom_file, strerror(errno));
ret = -1;
goto exit;
} else if (bytes_rd < FRUID_SIZE) {
syslog(LOG_ERR, "%s: less than %d bytes", __func__, FRUID_SIZE);
ret = -1;
goto exit;
}
bytes_wr = write(bin, tmp, bytes_rd);
if (bytes_wr != bytes_rd) {
syslog(LOG_ERR, "%s: write to %s file failed: %s",
__func__, bin_file, strerror(errno));
ret = -1;
}
exit:
close(bin);
err:
close(eeprom);
return ret;
}
int
pal_post_display(uint8_t status) {
int ret = 0, i = 0;
gpio_value_t value = GPIO_VALUE_INVALID;
for (i = 0; i < MAX_NUM_GPIO_LED_POSTCODE; i++) {
if (BIT(status, i) != 0) {
value = GPIO_VALUE_HIGH;
} else {
value = GPIO_VALUE_LOW;
}
ret = gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_LED_POSTCODE_TABLE[i]), value);
if (ret < 0) {
syslog(LOG_WARNING, "%s fail to display post code to debug card, failed gpio: LED_POSTCODE_%d, ret: %d\n", __func__, i, ret);
break;
}
}
return ret;
}
int
pal_get_current_led_post_code(uint8_t *post_code) {
int i = 0;
gpio_value_t value = GPIO_VALUE_INVALID;
if (post_code == NULL) {
syslog(LOG_ERR, "%s Invalid parameter: post_code is NULL\n", __func__);
return -1;
}
*post_code = 0;
for (i = (MAX_NUM_GPIO_LED_POSTCODE - 1); i >= 0; i--) {
value = gpio_get_value_by_shadow(fbgc_get_gpio_name(GPIO_LED_POSTCODE_TABLE[i]));
if (value == GPIO_VALUE_INVALID) {
syslog(LOG_WARNING, "%s fail to get post code, failed gpio: LED_POSTCODE_%d\n", __func__, i);
return -1;
}
// convert GPIOs to number
(*post_code) <<= 1;
(*post_code) |= (uint8_t)value;
}
return 0;
}
int
pal_get_debug_card_uart_sel(uint8_t *uart_sel) {
int i = 0;
gpio_value_t val = GPIO_VALUE_INVALID;
uint8_t uart_sel_tmp = 0;
uint8_t uart_sel_list[] = { DEBUG_UART_SEL_BMC, DEBUG_UART_SEL_HOST, DEBUG_UART_SEL_BIC,
DEBUG_UART_SEL_EXP_SMART, DEBUG_UART_SEL_EXP_SDB,
DEBUG_UART_SEL_IOC_T5_SMART, DEBUG_UART_SEL_IOC_T7_SMART };
if (uart_sel == NULL) {
syslog(LOG_ERR, "%s Invalid parameter: UART selection\n", __func__);
return -1;
}
*uart_sel = 0;
for (i = 0; i < MAX_NUM_GPIO_BMC_FPGA_UART_SEL; i++) {
val = gpio_get_value_by_shadow(fbgc_get_gpio_name(GPIO_BMC_FPGA_UART_SEL_TABLE[i]));
if (val == GPIO_VALUE_INVALID) {
syslog(LOG_WARNING, "%s() Can not get GPIO_BMC_FPGA_UART_SEL%d", __func__, i);
return -1;
}
// convert GPIOs to number
uart_sel_tmp <<= 1;
uart_sel_tmp |= (uint8_t)val;
}
(*uart_sel) = uart_sel_list[uart_sel_tmp];
return 0;
}
int
pal_is_debug_card_present(uint8_t *status) {
int present_status = 0;
int ret = 0;
if (status == NULL) {
syslog(LOG_ERR, "%s Invalid parameter: present status\n", __func__);
return -1;
}
present_status = pal_check_gpio_prsnt(GPIO_DEBUG_CARD_PRSNT_N, GPIO_VALUE_LOW);
switch (present_status) {
case FRU_PRESENT:
case FRU_ABSENT:
*status = present_status;
break;
default:
syslog(LOG_ERR, "%s failed to get debug card present gpio, status: %d\n", __func__, present_status);
ret = -1;
break;
}
return ret;
}
int
pal_post_handle(uint8_t slot, uint8_t postcode) {
uint8_t present_status = 0, uart_sel = 0;
int ret = 0;
ret = pal_is_debug_card_present(&present_status);
if (ret < 0) {
syslog(LOG_ERR, "%s: failed to get debug card present status. ret code: %d\n", __func__, ret);
return ret;
}
if (present_status == FRU_ABSENT) {
return 0;
}
ret = pal_get_debug_card_uart_sel(&uart_sel);
if (ret < 0) {
return ret;
}
if (uart_sel == DEBUG_UART_SEL_BMC) { // Do not overwrite BMC error code
return 0;
}
ret = pal_post_display(postcode);
return ret;
}
int
pal_get_fan_latch(uint8_t *chassis_status) {
gpio_value_t fan_latch_status = GPIO_VALUE_INVALID;
if (chassis_status == NULL) {
syslog(LOG_WARNING, "%s() failed to get the status of fan latch due to the NULL parameter.", __func__);
return -1;
}
fan_latch_status = gpio_get_value_by_shadow(fbgc_get_gpio_name(GPIO_DRAWER_CLOSED_N));
if (fan_latch_status == GPIO_VALUE_INVALID) {
syslog(LOG_WARNING, "%s() failed to get the status of fan latch due to the invalid gpio value.", __func__);
return -1;
}
if (fan_latch_status == GPIO_VALUE_HIGH) {
*chassis_status = CHASSIS_OUT;
} else {
*chassis_status = CHASSIS_IN;
}
return 0;
}
void
pal_specific_plat_fan_check(bool status)
{
uint8_t chassis_status = 0;
if (pal_get_fan_latch(&chassis_status) < 0) {
syslog(LOG_WARNING, "%s: Get chassis status in/out failed.", __func__);
return;
}
if(chassis_status == CHASSIS_OUT) {
printf("Sled Fan Latch Open: True\n");
} else {
printf("Sled Fan Latch Open: False\n");
}
return;
}
// IPMI OEM Command
// netfn: NETFN_OEM_1S_REQ (0x30)
// command code: CMD_OEM_BYPASS_CMD (0x34)
int
pal_bypass_cmd(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) {
int ret = 0;
int completion_code = CC_SUCCESS;
uint8_t netfn = 0, cmd = 0;
uint8_t tlen = 0, rlen = 0;
uint8_t prsnt_status = 0, pwr_status = 0;
uint8_t netdev = 0;
uint8_t action = 0;
uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0};
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
char sendcmd[MAX_SYS_CMD_REQ_LEN] = {0};
ipmi_req_t* ipmi_req = (ipmi_req_t*)tbuf;
ipmi_res_t* ipmi_resp = (ipmi_res_t*)rbuf;
NCSI_NL_MSG_T *msg = NULL;
NCSI_NL_RSP_T *rsp = NULL;
bypass_ncsi_req ncsi_req = {0};
network_cmd net_req = {0};
if (req_data == NULL) {
syslog(LOG_WARNING, "%s(): NULL request data, can not bypass the command", __func__);
return CC_INVALID_PARAM;
}
if (res_data == NULL) {
syslog(LOG_WARNING, "%s(): NULL response data, can not bypass the command", __func__);
return CC_INVALID_PARAM;
}
if (res_len == NULL) {
syslog(LOG_WARNING, "%s(): NULL response length, can not bypass the command", __func__);
return CC_INVALID_PARAM;
}
*res_len = 0;
ret = pal_is_fru_prsnt(FRU_SERVER, &prsnt_status);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): Can not bypass the command due to get server present status failed", __func__);
return CC_UNSPECIFIED_ERROR;
}
if (prsnt_status == FRU_ABSENT) {
syslog(LOG_WARNING, "%s(): Can not bypass the command due to server absent", __func__);
return CC_NOT_SUPP_IN_CURR_STATE;
}
ret = pal_get_server_12v_power(slot, &pwr_status);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): Can not bypass the command due to get server 12V power status failed", __func__);
return CC_UNSPECIFIED_ERROR;
}
if (pwr_status == SERVER_12V_OFF) {
syslog(LOG_WARNING, "%s(): Can not bypass the command due to server 12V power off", __func__);
return CC_NOT_SUPP_IN_CURR_STATE;
}
memset(tbuf, 0, sizeof(tbuf));
memset(rbuf, 0, sizeof(rbuf));
switch (((bypass_cmd*)req_data)->target) {
case BYPASS_BIC:
if ((req_len < sizeof(bypass_ipmi_header)) || (req_len > sizeof(tbuf))) {
completion_code = CC_INVALID_LENGTH;
break;
}
tlen = req_len - sizeof(bypass_ipmi_header);
memcpy(ipmi_req, &((bypass_cmd*)req_data)->data[0], sizeof(tbuf));
netfn = ipmi_req->netfn_lun;
cmd = ipmi_req->cmd;
// Bypass command to Bridge IC
if (tlen != 0) {
ret = bic_ipmb_wrapper(netfn, cmd, (uint8_t*)ipmi_req->data, tlen, res_data, res_len);
} else {
ret = bic_ipmb_wrapper(netfn, cmd, NULL, 0, res_data, res_len);
}
if (ret < 0) {
syslog(LOG_WARNING, "%s(): Failed to bypass IPMI command to BIC", __func__);
completion_code = CC_UNSPECIFIED_ERROR;
}
break;
case BYPASS_ME:
if ((req_len < sizeof(bypass_ipmi_header)) || (req_len > sizeof(tbuf))) {
completion_code = CC_INVALID_LENGTH;
break;
}
tlen = req_len - sizeof(bypass_me_header);
memcpy(ipmi_req, &((bypass_cmd*)req_data)->data[0], sizeof(tbuf));
ipmi_req->netfn_lun = IPMI_NETFN_SHIFT(ipmi_req->netfn_lun);
// Bypass command to ME
ret = bic_me_xmit((uint8_t*)ipmi_req, tlen, (uint8_t*)(&ipmi_resp->cc), &rlen);
if (ret == 0) {
completion_code = ipmi_resp->cc;
memcpy(&res_data[0], ipmi_resp->data, (rlen - sizeof(bypass_me_resp_header)));
*res_len = rlen - sizeof(bypass_me_resp_header);
} else {
syslog(LOG_WARNING, "%s(): Failed to send IPMI command to ME", __func__);
completion_code = CC_UNSPECIFIED_ERROR;
}
break;
case BYPASS_NCSI:
if ((req_len < sizeof(bypass_ncsi_header)) || (req_len > sizeof(NCSI_NL_MSG_T))) {
completion_code = CC_INVALID_LENGTH;
break;
}
tlen = req_len - sizeof(bypass_ncsi_header);
msg = calloc(1, sizeof(NCSI_NL_MSG_T));
if (msg == NULL) {
syslog(LOG_ERR, "%s(): failed msg buffer allocation", __func__);
completion_code = CC_UNSPECIFIED_ERROR;
break;
}
memset(&ncsi_req, 0, sizeof(ncsi_req));
memcpy(&ncsi_req, &((bypass_cmd*)req_data)->data[0], sizeof(ncsi_req));
memset(msg, 0, sizeof(*msg));
snprintf(msg->dev_name, MAX_NET_DEV_NAME_SIZE, "eth%d", ncsi_req.netdev);
msg->channel_id = ncsi_req.channel;
msg->cmd = ncsi_req.cmd;
msg->payload_length = tlen;
memcpy(&msg->msg_payload[0], &ncsi_req.data[0], NCSI_MAX_PAYLOAD);
rsp = send_nl_msg_libnl(msg);
if (rsp != NULL) {
memcpy(&res_data[0], &rsp->msg_payload[0], rsp->hdr.payload_length);
*res_len = rsp->hdr.payload_length;
} else {
completion_code = CC_UNSPECIFIED_ERROR;
}
break;
case BYPASS_NETWORK:
if (req_len != sizeof(bypass_network_header)) {
completion_code = CC_INVALID_LENGTH;
break;
}
memset(&net_req, 0, sizeof(net_req));
memcpy(&net_req, &((bypass_cmd*)req_data)->data[0], sizeof(net_req));
netdev = net_req.netdev;
action = net_req.action;
if (action == NET_INTF_ENABLE) {
snprintf(sendcmd, sizeof(sendcmd), "ifup eth%d", netdev);
} else if (action == NET_INTF_DISABLE) {
snprintf(sendcmd, sizeof(sendcmd), "ifdown eth%d", netdev);
} else {
completion_code = CC_INVALID_PARAM;
break;
}
ret = run_command(sendcmd);
if (ret != 0) {
syslog(LOG_WARNING, "%s(): sytem command: %s failed, error: %s", __func__, sendcmd, strerror(errno));
completion_code = CC_UNSPECIFIED_ERROR;
}
break;
default:
completion_code = CC_NOT_SUPP_IN_CURR_STATE;
break;
}
if (msg != NULL) {
free(msg);
}
if (rsp != NULL) {
free(rsp);
}
return completion_code;
}
int
pal_get_uic_board_id(uint8_t *board_id) {
gpio_value_t val = GPIO_VALUE_INVALID;
int i = 0;
if (board_id == NULL) {
syslog(LOG_ERR, "%s Invalid parameter: board id\n", __func__);
return -1;
}
*board_id = 0;
for (i = 0; i < MAX_NUM_OF_BOARD_REV_ID_GPIO; i++) {
val = gpio_get_value_by_shadow(fbgc_get_gpio_name(GPIO_BOARD_REV_ID_TABLE[i]));
if (val == GPIO_VALUE_INVALID) {
syslog(LOG_WARNING, "%s() Can not get GPIO_BOARD_REV_ID%d", __func__, i);
return -1;
}
// convert GPIOs to number
(*board_id) <<= 1;
(*board_id) |= (uint8_t)val;
}
return 0;
}
int
pal_get_80port_record(uint8_t slot_id, uint8_t *res_data, size_t max_len, size_t *res_len) {
int ret = 0;
uint8_t present_status = FRU_PRESENT;
uint8_t power_status = SERVER_12V_ON;
uint8_t len = 0;
if ((res_data == NULL) || (res_len == NULL)) {
syslog(LOG_WARNING, "%s: Failed to get 80 port record because of the NULL parameters.", __func__);
ret = PAL_ENOTREADY;
goto error_exit;
}
ret = pal_is_slot_server(slot_id);
if (ret == 0) {
ret = PAL_ENOTSUP;
syslog(LOG_WARNING, "%s: FRU: %d is not server.", __func__, slot_id);
goto error_exit;
}
ret = pal_is_fru_prsnt(slot_id, &present_status);
if ((ret < 0) || (present_status == FRU_ABSENT)) {
ret = PAL_ENOTREADY;
syslog(LOG_WARNING, "%s: Failed to get 80 port record because the server is not present.", __func__);
goto error_exit;
}
ret = pal_get_server_12v_power(slot_id, &power_status);
if((ret < 0) || (power_status == SERVER_12V_OFF)) {
ret = PAL_ENOTREADY;
syslog(LOG_WARNING, "%s: Failed to get 80 port record because the server is not 12V On.", __func__);
goto error_exit;
}
// Send command to get 80 port record from Bridge IC
ret = bic_get_80port_record((uint16_t)max_len, res_data, &len);
if (ret < 0) {
syslog(LOG_WARNING, "%s: Failed to get 80 port record from Bridge IC.", __func__);
ret = PAL_ENOTREADY;
goto error_exit;
} else {
*res_len = (size_t)len;
}
error_exit:
return ret;
}
int
pal_get_num_slots(uint8_t *num) {
*num = NUM_SERVER_FRU;
return 0;
}
int
pal_set_ioc_fw_recovery(uint8_t *ioc_recovery_setting, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) {
ioc_fw_recovery_req recovery_req = {0};
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
memset(key, 0, sizeof(key));
memset(str, 0, sizeof(str));
if ((ioc_recovery_setting == NULL) || (res_data == NULL) || (res_len == NULL)) {
syslog(LOG_ERR, "%s: Failed to set IOC firmware recovery status because the parameters are NULL.", __func__);
return CC_INVALID_PARAM;
}
if (req_len != sizeof(recovery_req)) {
syslog(LOG_ERR, "%s: Failed to set IOC firmware recovery status because the request length: %d is wrong, expected length: %d.", __func__, req_len, sizeof(recovery_req));
return CC_INVALID_PARAM;
}
memset(&recovery_req, 0, sizeof(recovery_req));
memcpy(&recovery_req, ioc_recovery_setting, sizeof(recovery_req));
if (recovery_req.component == IOC_RECOVERY_SCC) {
snprintf(key, sizeof(key), "scc_ioc_fw_recovery");
} else if (recovery_req.component == IOC_RECOVERY_IOCM) {
snprintf(key, sizeof(key), "iocm_ioc_fw_recovery");
} else {
syslog(LOG_ERR, "%s: Failed to set IOC firmware recovery status because wrong component.", __func__);
return CC_INVALID_PARAM;
}
if ((recovery_req.status == DISABLE_IOC_RECOVERY) || (recovery_req.status == ENABLE_IOC_RECOVERY)) {
snprintf(str, sizeof(str), "%x", recovery_req.status);
} else {
syslog(LOG_ERR, "%s: Failed to set IOC firmware recovery status because wrong recovery status.", __func__);
return CC_INVALID_PARAM;
}
if (pal_set_key_value(key, str) < 0) {
syslog(LOG_ERR, "%s: Failed to set IOC firmware recovery status because failed to set key value of %s.", __func__, key);
return CC_UNSPECIFIED_ERROR;
}
return CC_SUCCESS;
}
int
pal_get_ioc_fw_recovery(uint8_t ioc_recovery_component, uint8_t *res_data, uint8_t *res_len) {
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
memset(key, 0, sizeof(key));
memset(str, 0, sizeof(str));
if ((res_data == NULL) || (res_len == NULL)) {
syslog(LOG_ERR, "%s: Failed to get IOC firmware recovery status because the parameters are NULL.", __func__);
return CC_INVALID_PARAM;
}
if (ioc_recovery_component == IOC_RECOVERY_SCC) {
snprintf(key, sizeof(key), "scc_ioc_fw_recovery");
} else if (ioc_recovery_component == IOC_RECOVERY_IOCM) {
snprintf(key, sizeof(key), "iocm_ioc_fw_recovery");
} else {
return CC_INVALID_PARAM;
}
if (pal_get_key_value(key, str) != 0) {
syslog(LOG_ERR, "%s: Failed to get IOC firmware recovery status because failed to get key value of %s.", __func__, key);
return CC_UNSPECIFIED_ERROR;
}
*res_len = 1;
res_data[0] = strtol(str, NULL, 16);
return CC_SUCCESS;
}
int
pal_read_error_code_file(uint8_t *error_code_array, uint8_t error_code_array_len) {
FILE *err_file = NULL;
int i = 0, ret = 0;
unsigned int err_tmp = 0;
if (error_code_array == NULL) {
syslog(LOG_WARNING, "%s(): fail to read error code because NULL parameter: *error_code_byte", __func__);
return -1;
}
// if no file, create file
if (access(ERR_CODE_BIN, F_OK) == -1) {
err_file = fopen(ERR_CODE_BIN, "w");
if (err_file == NULL) {
syslog(LOG_WARNING, "%s: fail to open %s file because %s ", __func__, ERR_CODE_BIN, strerror(errno));
return -1;
}
ret = pal_flock_retry(fileno(err_file));
if (ret < 0) {
syslog(LOG_WARNING, "%s: fail to flock %s file because %s ", __func__, ERR_CODE_BIN, strerror(errno));
fclose(err_file);
return -1;
}
memset(error_code_array, 0, error_code_array_len);
for (i = 0; i < error_code_array_len; i++) {
fprintf(err_file, "%X ", error_code_array[i]);
}
fprintf(err_file, "\n");
pal_unflock_retry(fileno(err_file));
fclose(err_file);
return 0;
}
err_file = fopen(ERR_CODE_BIN, "r");
if (err_file == NULL) {
syslog(LOG_WARNING, "%s: fail to open %s file because %s ", __func__, ERR_CODE_BIN, strerror(errno));
return -1;
}
for (i = 0; (fscanf(err_file, "%X", &err_tmp) != EOF) && (i < error_code_array_len); i++) {
error_code_array[i] = (uint8_t) err_tmp;
}
fclose(err_file);
return 0;
}
int
pal_write_error_code_file(unsigned char error_code_update, uint8_t error_code_status) {
FILE *err_file = NULL;
int i = 0, ret = 0;
int byte_site = 0 , bit_site = 0;
uint8_t error_code_array[MAX_NUM_ERR_CODES_ARRAY] = {0};
memset(error_code_array, 0, sizeof(error_code_array));
ret = pal_read_error_code_file(error_code_array, sizeof(error_code_array));
if (ret < 0) {
syslog(LOG_WARNING, "%s(): fail to write error code 0x%X because read %s error", __func__, error_code_update, ERR_CODE_BIN);
return ret;
}
err_file = fopen(ERR_CODE_BIN, "r+");
ret = pal_flock_retry(fileno(err_file));
if (ret < 0) {
syslog(LOG_WARNING, "%s: fail to flock %s file because %s ", __func__, ERR_CODE_BIN, strerror(errno));
fclose(err_file);
return ret;
}
byte_site = error_code_update / 8;
bit_site = error_code_update % 8;
if (error_code_status == ERR_CODE_ENABLE) {
error_code_array[byte_site] = SETBIT(error_code_array[byte_site], bit_site);
} else {
error_code_array[byte_site] = CLEARBIT(error_code_array[byte_site], bit_site);
}
for (i = 0; i < sizeof(error_code_array); i++) {
fprintf(err_file, "%X ", error_code_array[i]);
}
fprintf(err_file, "\n");
pal_unflock_retry(fileno(err_file));
fclose(err_file);
return 0;
}
int
pal_get_error_code(uint8_t *data, uint8_t* error_count) {
uint8_t tbuf[MAX_IPMB_BUFFER] = {0x00};
uint8_t rbuf[MAX_IPMB_BUFFER] = {0x00};
uint8_t rlen = 0 , tlen = 0;
uint8_t total_error_array[MAX_NUM_ERR_CODES_ARRAY] = {0};
uint8_t exp_error_array[MAX_NUM_EXP_ERR_CODES_ARRAY] = {0};
int ret = 0, i = 0, j = 0;
int tmp_err_count = 0;
if (error_count == NULL) {
printf("%s: fail to get error code because NULL parameter: *error_count", __func__);
return -1;
}
if (data == NULL) {
printf("%s: fail to get error code because NULL parameter: *data", __func__);
return -1;
}
memset(tbuf, 0x00, sizeof(tbuf));
memset(rbuf, 0x00, sizeof(rbuf));
memset(exp_error_array, 0, sizeof(exp_error_array));
memset(total_error_array, 0, sizeof(total_error_array));
// get expander error code
ret = expander_ipmb_wrapper(NETFN_OEM_REQ, CMD_OEM_EXP_ERROR_CODE, tbuf, tlen, rbuf, &rlen);
if (ret < 0) {
printf("enclosure-util: failed to get expander error code\n");
printf("NetFn: 0x%2X Code: 0x%02X was error\n", NETFN_OEM_REQ, CMD_OEM_EXP_ERROR_CODE);
// when Epander fail, fill all data to 0
memset(exp_error_array, 0, sizeof(exp_error_array));
} else {
memcpy(exp_error_array, rbuf, MIN(rlen, sizeof(exp_error_array)));
}
// error code 0 is "no Error", ignore
exp_error_array[0] = CLEARBIT(exp_error_array[0], 0);
// get bmc error code
ret = pal_read_error_code_file(total_error_array, sizeof(total_error_array));
if (ret < 0) {
printf("enclosure-util: failed to get bmc error code\n");
memset(total_error_array, 0, sizeof(total_error_array));
}
// Expander Error Code 0~99; BMC Error Code 0x64(100)~0xFF(255)
// copy expander 0~96 (byte 0~12)
memcpy(total_error_array, exp_error_array, sizeof(exp_error_array) - 1);
// copy expander 97~100 (byte 12 bit 0~3)
total_error_array[sizeof(exp_error_array) - 1]
= ((total_error_array[sizeof(exp_error_array) - 1] & 0xF0)
+ (exp_error_array[sizeof(exp_error_array) - 1] & 0x0F));
// count error and change storage format from byte array to number
memset(data, 0, MAX_NUM_ERR_CODES);
for (i = 0; i < MAX_NUM_ERR_CODES_ARRAY; i++) {
for (j = 0; j < 8; j++) {
if (GETBIT(total_error_array[i], j) == 1) {
data[tmp_err_count] = (i * 8) + j;
tmp_err_count++;
}
}
}
*error_count = tmp_err_count;
return 0;
}
void pal_set_error_code(unsigned char error_num, uint8_t error_code_status) {
int ret = 0;
// BMC error code number 0x64(100)~0xFF(255)
if (error_num < MAX_NUM_EXP_ERR_CODES) {
return;
}
if (error_num < MAX_NUM_ERR_CODES) {
ret = pal_write_error_code_file(error_num, error_code_status);
if (ret < 0) {
syslog(LOG_ERR, "%s(): fail to write error code: 0x%02X", __func__, error_num);
}
} else {
syslog(LOG_WARNING, "%s(): invalid error code number", __func__);
}
}
int
pal_bmc_err_enable(const char *error_item) {
if (error_item == NULL) {
printf("%s: fail to enable error code because NULL parameter: *error_item", __func__);
return -1;
}
if (strcasestr(error_item, "CPU") != 0ULL) {
pal_set_error_code(ERR_CODE_CPU_UTILIZA, ERR_CODE_ENABLE);
} else if (strcasestr(error_item, "Memory") != 0ULL) {
pal_set_error_code(ERR_CODE_MEM_UTILIZA, ERR_CODE_ENABLE);
} else if (strcasestr(error_item, "ECC Recoverable") != 0ULL) {
pal_set_error_code(ERR_CODE_ECC_RECOVERABLE, ERR_CODE_ENABLE);
} else if (strcasestr(error_item, "ECC Unrecoverable") != 0ULL) {
pal_set_error_code(ERR_CODE_ECC_UNRECOVERABLE, ERR_CODE_ENABLE);
} else {
syslog(LOG_WARNING, "%s: invalid bmc health item: %s", __func__, error_item);
return -1;
}
return 0;
}
int
pal_bmc_err_disable(const char *error_item) {
if (error_item == NULL) {
printf("%s: fail to disable error code because NULL parameter: *error_item", __func__);
return -1;
}
if (strcasestr(error_item, "CPU") != 0ULL) {
pal_set_error_code(ERR_CODE_CPU_UTILIZA, ERR_CODE_DISABLE);
} else if (strcasestr(error_item, "Memory") != 0ULL) {
pal_set_error_code(ERR_CODE_MEM_UTILIZA, ERR_CODE_DISABLE);
} else if (strcasestr(error_item, "ECC Unrecoverable") != 0ULL) {
pal_set_error_code(ERR_CODE_ECC_RECOVERABLE, ERR_CODE_DISABLE);
} else if (strcasestr(error_item, "ECC Recoverable") != 0ULL) {
pal_set_error_code(ERR_CODE_ECC_UNRECOVERABLE, ERR_CODE_DISABLE);
} else {
syslog(LOG_WARNING, "%s: invalid bmc health item: %s", __func__, error_item);
return -1;
}
return 0;
}
void
pal_i2c_crash_assert_handle(int i2c_bus_num) {
// I2C bus number: 0~15
if (i2c_bus_num < MAX_NUM_I2C_BUS) {
pal_set_error_code(ERR_CODE_I2C_CRASH_BASE + i2c_bus_num, ERR_CODE_ENABLE);
} else {
syslog(LOG_WARNING, "%s(): invalid I2C bus number: %d", __func__, i2c_bus_num);
}
}
void
pal_i2c_crash_deassert_handle(int i2c_bus_num) {
// I2C bus number: 0~15
if (i2c_bus_num < MAX_NUM_I2C_BUS) {
pal_set_error_code(ERR_CODE_I2C_CRASH_BASE + i2c_bus_num, ERR_CODE_DISABLE);
} else {
syslog(LOG_WARNING, "%s(): invalid I2C bus number: %d", __func__, i2c_bus_num);
}
}
static int
set_exp_uart_bridging(uint8_t bridging_status) {
uint8_t bmc_rev_id = 0;
uint32_t reg_value = 0, hicr9_value = 0, hicra_value = 0;
exp_uart_bridging_cmd cmd = {0};
int fd = 0, retry = 0, ret = CC_SUCCESS;
bool is_ctrl_via_fpga = false;
char uic_fpga_ver[MAX_VALUE_LEN] = {0};
char uic_fpga_stage[MAX_VALUE_LEN] = {0};
char uic_fpga_ver_num[MAX_VALUE_LEN] = {0};
switch (bridging_status) {
case ENABLE_BRIDGING:
hicr9_value = ROUTE_IO2_TO_IO6;
hicra_value = ROUTE_IO6_TO_IO2;
break;
case DISABLE_BRIDGING:
hicr9_value = ROUTE_UART6_TO_IO6;
hicra_value = ROUTE_UART2_TO_IO2;
break;
default:
syslog(LOG_WARNING, "%s: failed to route UART due to wrong status", __func__);
return CC_INVALID_PARAM;
}
// Get BMC Revision ID
phymem_get_dword(SCU_BASE, REG_SCU014, ®_value);
bmc_rev_id = (reg_value >> OFFSET_BMC_REV_ID) & 0xf;
// Get UIC FPGA firmware version
if (pal_get_fpga_ver_cache(I2C_UIC_FPGA_BUS, GET_FPGA_VER_ADDR, uic_fpga_ver) == 0) {
snprintf(uic_fpga_stage, sizeof(uic_fpga_stage), "%c%c", uic_fpga_ver[4], uic_fpga_ver[5]);
snprintf(uic_fpga_ver_num, sizeof(uic_fpga_ver_num), "%c%c", uic_fpga_ver[6], uic_fpga_ver[7]);
// Support to route in UIC FPGA with firmware version D04
if (((strcmp(uic_fpga_stage, "0D") == 0) && (atoi(uic_fpga_ver_num) >= 0x04))
|| (strcmp(uic_fpga_stage, "0A") == 0)) {
is_ctrl_via_fpga = true;
}
} else {
syslog(LOG_WARNING, "%s: failed to route UART because failed to get UIC FPGA firmware version", __func__);
return CC_UNSPECIFIED_ERROR;
}
if (bmc_rev_id < ASPEED_A2) {
// Routing by BMC. Not support routing with UART6/IO6 after A2 silicon
// set HICR9 register
if (phymem_set_dword(LPC_CTR_BASE, HICR9_ADDR, hicr9_value) < 0) {
syslog(LOG_WARNING, "%s: failed to route UART by setting HICR9 with 0x%X", __func__, hicr9_value);
return CC_UNSPECIFIED_ERROR;
}
// set HICRA register
if (phymem_set_dword(LPC_CTR_BASE, HICRA_ADDR, hicra_value) < 0) {
syslog(LOG_WARNING, "%s: failed to route UART by setting HICRA with 0x%X", __func__, hicra_value);
return CC_UNSPECIFIED_ERROR;
}
} else {
// Routing by FGPA
if (is_ctrl_via_fpga == false) {
syslog(LOG_ERR, "%s: failed to route UART by UIC FPGA because firmware version is too old, please update.", __func__);
return CC_NOT_SUPP_IN_CURR_STATE;
}
cmd.exp_uart_bridging_cmd_code = UIC_FPGA_UART_BRIDGING_OFFSET;
cmd.exp_uart_bridging_mode = bridging_status;
fd = i2c_cdev_slave_open(I2C_UIC_FPGA_BUS, UIC_FPGA_SLAVE_ADDR >> 1, I2C_SLAVE_FORCE_CLAIM);
if (fd < 0) {
syslog(LOG_WARNING, "%s() Failed to open i2c bus %d", __func__, I2C_UIC_FPGA_BUS);
return CC_UNSPECIFIED_ERROR;
}
while (retry < MAX_RETRY) {
ret = i2c_rdwr_msg_transfer(fd, UIC_FPGA_SLAVE_ADDR, (uint8_t *)&cmd, sizeof(cmd), NULL, 0);
if (ret < 0) {
retry++;
msleep(100);
} else {
break;
}
}
if (retry == MAX_RETRY) {
syslog(LOG_WARNING, "%s() Failed to send \"set exander UART briding\" command to UIC FPGA", __func__);
ret = CC_UNSPECIFIED_ERROR;
}
}
if (fd >= 0) {
close(fd);
}
return ret;
}
int
pal_setup_exp_uart_bridging(void) {
return set_exp_uart_bridging(ENABLE_BRIDGING);
}
int
pal_teardown_exp_uart_bridging(void) {
return set_exp_uart_bridging(DISABLE_BRIDGING);
}
int
pal_get_drive_health(const char* i2c_bus_dev) {
uint8_t status_flags = 0;
uint8_t warning_value = 0;
if (i2c_bus_dev == NULL) {
syslog(LOG_WARNING, "fail to get drive health because NULL parameter: *i2c_bus_dev\n");
return -1;
}
if (nvme_smart_warning_read(i2c_bus_dev, &warning_value) < 0) {
syslog(LOG_ERR, "fail to get drive health because read %s error\n", i2c_bus_dev);
return -1;
} else {
if ((warning_value & NVME_SMART_WARNING_MASK) != NVME_SMART_WARNING_MASK) {
return -1;
}
}
if (nvme_sflgs_read(i2c_bus_dev, &status_flags) < 0) {
syslog(LOG_WARNING, "fail to get drive health because read %s error\n", i2c_bus_dev);
return -1;
} else {
if ((status_flags & NVME_STATUS_MASK) != NVME_STATUS_NORMAL) {
return -1;
}
}
return 0;
}
int
pal_get_drive_status(const char* i2c_bus_dev) {
ssd_data ssd;
t_key_value_pair vendor_decode_result;
t_key_value_pair sn_decode_result;
t_key_value_pair temp_decode_result;
t_key_value_pair pdlu_decode_result;
t_status_flags status_flag_decode_result;
t_smart_warning smart_warning_decode_result;
if (i2c_bus_dev == NULL) {
syslog(LOG_WARNING, "fail to get drive status because NULL parameter: *i2c_bus_dev\n");
return -1;
}
memset(&ssd, 0, sizeof(ssd));
memset(&vendor_decode_result, 0, sizeof(vendor_decode_result));
memset(&sn_decode_result, 0, sizeof(sn_decode_result));
memset(&temp_decode_result, 0, sizeof(temp_decode_result));
memset(&pdlu_decode_result, 0, sizeof(pdlu_decode_result));
memset(&status_flag_decode_result, 0, sizeof(status_flag_decode_result));
memset(&smart_warning_decode_result, 0, sizeof(smart_warning_decode_result));
if (nvme_vendor_read_decode(i2c_bus_dev, &ssd.vendor, &vendor_decode_result) < 0) {
printf("%s: Fail to read Vendor\n", vendor_decode_result.key);
} else {
printf("%s: %s\n", vendor_decode_result.key, vendor_decode_result.value);
}
if (nvme_serial_num_read_decode(i2c_bus_dev, ssd.serial_num, MAX_SERIAL_NUM_SIZE, &sn_decode_result) < 0) {
printf("%s: Fail to read Serial Number\n", sn_decode_result.key);
} else {
printf("%s: %s\n", sn_decode_result.key, sn_decode_result.value);
}
if (nvme_temp_read_decode(i2c_bus_dev, &ssd.temp, &temp_decode_result) < 0) {
printf("%s: Fail to read Composite Temperature\n", temp_decode_result.key);
} else {
printf("%s: %s\n", temp_decode_result.key, temp_decode_result.value);
}
if (nvme_pdlu_read_decode(i2c_bus_dev, &ssd.pdlu, &pdlu_decode_result) < 0) {
printf("%s: Fail to read Percentage Drive Life Useds\n", temp_decode_result.key);
} else {
printf("%s: %s\n", temp_decode_result.key, pdlu_decode_result.value);
}
if (nvme_sflgs_read_decode(i2c_bus_dev, &ssd.sflgs, &status_flag_decode_result) < 0) {
printf("%s: Fail to read Status Flags\n", status_flag_decode_result.self.key);
} else {
printf("%s: %s\n", status_flag_decode_result.self.key, status_flag_decode_result.self.value);
printf(" %s: %s\n", status_flag_decode_result.read_complete.key, status_flag_decode_result.read_complete.value);
printf(" %s: %s\n", status_flag_decode_result.ready.key, status_flag_decode_result.ready.value);
printf(" %s: %s\n", status_flag_decode_result.functional.key, status_flag_decode_result.functional.value);
printf(" %s: %s\n", status_flag_decode_result.reset_required.key, status_flag_decode_result.reset_required.value);
printf(" %s: %s\n", status_flag_decode_result.port0_link.key, status_flag_decode_result.port0_link.value);
printf(" %s: %s\n", status_flag_decode_result.port1_link.key, status_flag_decode_result.port1_link.value);
}
if (nvme_smart_warning_read_decode(i2c_bus_dev, &ssd.warning, &smart_warning_decode_result) < 0) {
printf("%s: Fail to read SMART Critical Warning\n", smart_warning_decode_result.self.key);
} else {
printf("%s: %s\n", smart_warning_decode_result.self.key, smart_warning_decode_result.self.value);
printf(" %s: %s\n", smart_warning_decode_result.spare_space.key, smart_warning_decode_result.spare_space.value);
printf(" %s: %s\n", smart_warning_decode_result.temp_warning.key, smart_warning_decode_result.temp_warning.value);
printf(" %s: %s\n", smart_warning_decode_result.reliability.key, smart_warning_decode_result.reliability.value);
printf(" %s: %s\n", smart_warning_decode_result.media_status.key, smart_warning_decode_result.media_status.value);
printf(" %s: %s\n", smart_warning_decode_result.backup_device.key, smart_warning_decode_result.backup_device.value);
}
printf("\n");
return 0;
}
int
pal_is_crashdump_ongoing(uint8_t fru)
{
char fname[MAX_PATH_LEN] = {0};
char value[MAX_VALUE_LEN] = {0};
struct timespec ts;
int ret = 0;
//if pid file not exist, return false
snprintf(fname, sizeof(fname), SERVER_CRASHDUMP_PID_PATH);
if (access(fname, F_OK) != 0) {
return 0;
}
snprintf(fname, sizeof(fname), SERVER_CRASHDUMP_KV_KEY);
ret = kv_get(fname, value, NULL, 0);
if (ret < 0) {
return 0;
}
clock_gettime(CLOCK_MONOTONIC, &ts);
if (strtoul(value, NULL, 10) > ts.tv_sec) {
return 1;
}
//over the threshold time, return false
return 0; /* false */
}
// Determine if BMC is AC on
int
pal_is_bmc_por(void) {
int ret = 0;
char ast_por_flag[MAX_VALUE_LEN] = {0x0};
char ast_por_true[] = "1";
uint32_t reg_value = 0, sig = 0;
if (kv_get("ast_por_flag", ast_por_flag, NULL, 0) < 0) {
// Read Boot Magic
phymem_get_dword(SRAM_BMC_REBOOT_BASE, BOOT_MAGIC_OFFSET, &sig);
// Check Power on reset SRST# event log (SCU074[0]) to determine if this boot is AC on or not
phymem_get_dword(SCU_BASE, REG_SCU074, ®_value);
if ((sig != BOOT_MAGIC) && ((reg_value & OFFSET_SRST_EVENT_LOG) == 1)) {
// Power ON Reset
kv_set("ast_por_flag", STR_VALUE_1, 0, 0);
ret = 1;
} else {
// External Reset
kv_set("ast_por_flag", STR_VALUE_0, 0, 0);
ret = 0;
}
// Clear SCU074
phymem_set_dword(SCU_BASE, REG_SCU074, 0xffffffff);
return ret;
}
if (strncmp(ast_por_flag, ast_por_true, strlen(ast_por_true)) == 0) {
return 1;
} else {
return 0;
}
}
static int
pal_store_crashdump() {
uint8_t status = 0;
char cmd[MAX_PATH_LEN] = {0};
if (pal_get_server_power(FRU_SERVER, &status) < 0) {
syslog(LOG_WARNING, "%s(): Fail to get server power status", __func__);
return PAL_ENOTSUP;
}
if (status != SERVER_POWER_ON) {
syslog(LOG_WARNING, "%s(): Fail to generate crashdump: server is OFF", __func__);
return PAL_ENOTSUP;
}
if (access(CRASHDUMP_BIN, F_OK) == -1) {
syslog(LOG_CRIT, "%s() Try to run autodump but %s is not existed", __func__, CRASHDUMP_BIN);
return PAL_ENOTSUP;
}
snprintf(cmd, sizeof(cmd), "%s server &", CRASHDUMP_BIN);
if (run_command(cmd) == 0) {
syslog(LOG_INFO, "%s() Crashdump for SERVER is being generated.", __func__);
} else {
syslog(LOG_INFO, "%s() Failed to generate crashdump.", __func__);
}
return PAL_EOK;
}
static int
pal_bic_sel_handler(uint8_t snr_num, uint8_t *event_data) {
int ret = PAL_EOK;
bool is_err_server_sel = false;
char key[MAX_KEY_LEN] = {0};
char val[MAX_VALUE_LEN] = {0};
uint8_t event_dir = EVENT_DEASSERT;
int sel_error_record = 0, sel_event_error_record = 0;
if (event_data == NULL) {
syslog(LOG_ERR, "%s(): Failed to handle BIC sel because event data is NULL", __func__);
return -1;
}
// Event Dir is used to check the assertion of event. Refer to IPMI v2.0 Section 32.1.
event_dir = event_data[2] & 0x80;
switch (snr_num) {
case CATERR_B:
ret = pal_store_crashdump();
is_err_server_sel = true;
break;
case CPU_DIMM_HOT:
case PWR_ERR:
is_err_server_sel = true;
break;
default:
break;
}
if (is_err_server_sel == true) {
// Get and update SEL error record
snprintf(key, sizeof(key), "sel_error_record");
if (kv_get(key, val, NULL, 0) == 0) {
sel_error_record = atoi(val);
}
if (event_dir == EVENT_ASSERT) {
sel_error_record++;
} else {
sel_error_record--;
}
snprintf(val, sizeof(val), "%d", sel_error_record);
kv_set(key, val, 0, 0);
// Get SEL event error record
snprintf(key, sizeof(key), "sel_event_error_record");
if (kv_get(key, val, NULL, 0) == 0) {
sel_event_error_record = atoi(val);
}
// Update server_sel_error
snprintf(key, sizeof(key), "server_sel_error");
if ((sel_error_record > 0) || (sel_event_error_record > 0)) {
snprintf(val, sizeof(val), "%d", FRU_STATUS_BAD);
} else {
snprintf(val, sizeof(val), "%d", FRU_STATUS_GOOD);
}
ret = pal_set_key_value(key, val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): Failed to set FRU SEL value because failed to set key value of %s.", __func__, key);
return ret;
}
}
return ret;
}
int
pal_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) {
int ret = PAL_EOK;
if (event_data == NULL) {
syslog(LOG_ERR, "%s(): Invalid parameter: event data is NULL", __func__);
return -1;
}
switch(fru) {
case FRU_SERVER:
ret = pal_bic_sel_handler(snr_num, event_data);
break;
default:
ret = PAL_ENOTSUP;
break;
}
return ret;
}
int
pal_oem_unified_sel_handler(uint8_t fru, uint8_t general_info, uint8_t *sel) {
char key[MAX_KEY_LEN] = {0};
char val[MAX_VALUE_LEN] = {0};
int sel_event_error_record = 0;
if (sel == NULL) {
syslog(LOG_ERR, "%s(): Failed to handle OEM unified sel due to NULL parameter.", __func__);
return PAL_ENOTREADY;
}
// Update SEL event error record
snprintf(key, sizeof(key), "sel_event_error_record");
if (kv_get(key, val, NULL, 0) == 0) {
sel_event_error_record = atoi(val);
}
sel_event_error_record++;
snprintf(val, sizeof(val), "%d", sel_event_error_record);
if (kv_set(key, val, 0, 0) < 0) {
syslog(LOG_ERR, "%s(): Failed to handle OEM unified sel due to pal_set_key_value failed. key: %s", __func__, key);
return PAL_ENOTREADY;
}
// Update server_sel_error
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
snprintf(key, sizeof(key), "server_sel_error");
snprintf(val, sizeof(val), "%d", FRU_STATUS_BAD);
if (pal_set_key_value(key, val) < 0) {
syslog(LOG_ERR, "%s(): Failed to handle OEM unified sel because failed set key value of %s.", __func__, key);
return PAL_ENOTREADY;
}
return PAL_EOK;
}
int
pal_get_fru_health(uint8_t fru, uint8_t *value) {
char val[MAX_VALUE_LEN] = {0};
char key[MAX_KEY_LEN] = {0};
int ret = 0;
if (value == NULL) {
syslog(LOG_WARNING, "%s(): failed to get fru health because the parameter: *value is NULL", __func__);
}
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
switch (fru) {
case FRU_SERVER:
snprintf(key, sizeof(key), "server_sensor_health");
break;
case FRU_UIC:
snprintf(key, sizeof(key), "uic_sensor_health");
break;
case FRU_DPB:
snprintf(key, sizeof(key), "dpb_sensor_health");
break;
case FRU_SCC:
snprintf(key, sizeof(key), "scc_sensor_health");
break;
case FRU_NIC:
snprintf(key, sizeof(key), "nic_sensor_health");
break;
case FRU_E1S_IOCM:
snprintf(key, sizeof(key), "e1s_iocm_sensor_health");
break;
case FRU_FAN0:
case FRU_FAN1:
case FRU_FAN2:
case FRU_FAN3:
case FRU_BMC:
return ERR_SENSOR_NA;
default:
return -1;
}
ret = pal_get_key_value(key, val);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): failed to get the fru health because get the value of key: %s failed", __func__, key);
return ret;
}
*value = atoi(val);
// Check server error SEL
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
if (fru == FRU_SERVER) {
snprintf(key, sizeof(key), "server_sel_error");
} else {
return 0;
}
ret = pal_get_key_value(key, val);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): failed to get the fru health because get the value of key: %s failed", __func__, key);
return ret;
}
*value = *value & atoi(val);
return 0;
}
int
pal_set_sensor_health(uint8_t fru, uint8_t value) {
char val[MAX_VALUE_LEN] = {0};
char key[MAX_KEY_LEN] = {0};
int ret = 0;
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
switch (fru) {
case FRU_SERVER:
snprintf(key, sizeof(key), "server_sensor_health");
break;
case FRU_UIC:
snprintf(key, sizeof(key), "uic_sensor_health");
break;
case FRU_DPB:
// DPB sensor event SEL are sent by Expander
// health value will change when get SEL
return 0;
case FRU_SCC:
// SCC IOC temperature is monitoring by BMC and the rest of sensors of SCC are monitoring by Expander.
snprintf(key, sizeof(key), "scc_sensor_health");
break;
case FRU_NIC:
snprintf(key, sizeof(key), "nic_sensor_health");
break;
case FRU_E1S_IOCM:
snprintf(key, sizeof(key), "e1s_iocm_sensor_health");
break;
default:
return -1;
}
if (value == FRU_STATUS_BAD) {
snprintf(val, sizeof(val), "%d", FRU_STATUS_BAD);
} else {
snprintf(val, sizeof(val), "%d", FRU_STATUS_GOOD);
}
ret = pal_set_key_value(key, val);
if (ret < 0) {
syslog(LOG_WARNING, "%s(): failed to set sensor health because set key: %s value: %s failed", __func__, key, val);
}
return ret;
}
void
pal_log_clear(char *fru) {
char val[MAX_VALUE_LEN] = {0};
int ret = 0;
if (fru == NULL) {
syslog(LOG_WARNING, "%s(): failed to clear the health value because the parameter: *fru is NULL", __func__);
}
memset(val, 0, sizeof(val));
snprintf(val, sizeof(val), "%d", FRU_STATUS_GOOD);
if (strcmp(fru, "server") == 0) {
ret = pal_set_key_value("server_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear server seneor health value", __func__);
}
ret = pal_set_key_value("server_sel_error", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear server sel error value", __func__);
}
kv_set("sel_error_record", STR_VALUE_0, 0, 0);
kv_set("sel_event_error_record", STR_VALUE_0, 0, 0);
} else if (strcmp(fru, "uic") == 0) {
ret = pal_set_key_value("uic_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear uic seneor health value", __func__);
}
} else if (strcmp(fru, "dpb") == 0) {
ret = pal_set_key_value("dpb_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear dpb seneor health value", __func__);
}
} else if (strcmp(fru, "scc") == 0) {
ret = pal_set_key_value("scc_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear the scc seneor health value", __func__);
}
} else if (strcmp(fru, "nic") == 0) {
ret = pal_set_key_value("nic_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear the nic seneor health value", __func__);
}
} else if (strcmp(fru, "e1s_iocm") == 0) {
ret = pal_set_key_value("e1s_iocm_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear the e1s/ iocm seneor health value", __func__);
}
} else if (strcmp(fru, "all") == 0) {
ret = pal_set_key_value("server_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear server seneor health value", __func__);
}
ret = pal_set_key_value("server_sel_error", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear server sel error value", __func__);
}
kv_set("sel_error_record", STR_VALUE_0, 0, 0);
kv_set("sel_event_error_record", STR_VALUE_0, 0, 0);
ret = pal_set_key_value("uic_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear uic seneor health value", __func__);
}
ret = pal_set_key_value("dpb_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear dpb seneor health value", __func__);
}
ret = pal_set_key_value("scc_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear scc seneor health value", __func__);
}
ret = pal_set_key_value("nic_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear nic seneor health value", __func__);
}
ret = pal_set_key_value("e1s_iocm_sensor_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear e1s/iocm seneor health value", __func__);
}
ret = pal_set_key_value("bmc_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear bmc health value", __func__);
}
ret = pal_set_key_value("heartbeat_health", val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to clear heartbeat health value", __func__);
}
ret = kv_set("fan_dead_rearm", "1", 0, 1);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to set fan dead re-arm value, err: %s", __func__, strerror(errno));
}
ret = kv_set("healthd_rearm", "1", 0, 0);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to set healthd re-arm value, err: %s", __func__, strerror(errno));
}
}
}
int
pal_get_platform_name(char *name) {
if (name == NULL) {
syslog(LOG_ERR, "%s(): Failed to get platform name due to NULL parameter", __func__);
return PAL_ENOTSUP;
}
strncpy(name, PLATFORM_NAME, MAX_PLATFORM_NAME_SIZE);
return PAL_EOK;
}
void pal_update_ts_sled() {
char key[MAX_KEY_LEN] = {0};
char timestamp_str[MAX_VALUE_LEN] = {0};
struct timespec timestamp;
int ret = 0;
memset(key, 0, sizeof(key));
memset(timestamp_str, 0, sizeof(timestamp_str));
memset(×tamp, 0, sizeof(timestamp));
clock_gettime(CLOCK_REALTIME, ×tamp);
snprintf(key, sizeof(key), "timestamp_sled");
snprintf(timestamp_str, sizeof(timestamp_str), "%ld", timestamp.tv_sec);
ret = pal_set_key_value(key, timestamp_str);
if (ret < 0) {
syslog(LOG_ERR, "%s(): failed to set key: %s value: %s", __func__, key, timestamp_str);
}
}
bool
pal_is_heartbeat_ok(uint8_t component) {
char label[MAX_PWM_LABEL_LEN] = {0};
char kv_value[MAX_VALUE_LEN] = {0};
float hb_val = 0;
bool is_read = false;
snprintf(label, sizeof(label), "fan%d", component);
// get heartbeat from tacho driver
if (sensors_read_fan(label, &hb_val) < 0) {
syslog(LOG_WARNING, "%s(): fail to get heartbeat, component = %d", __func__, component);
} else if (hb_val != 0) {
is_read = true;
}
if (component == HEARTBEAT_BIC) { // cache BIC heartbeat reading for healthd
snprintf(kv_value, sizeof(kv_value), "%d", is_read);
kv_set(KV_KEY_BIC_HEARTBEAT, kv_value, 0, 0);
}
return is_read;
}
int
pal_handle_oem_1s_intr(uint8_t fru, uint8_t *data)
{
int sock = 0;
int err = 0;
struct sockaddr_un server;
if (access(SOCK_PATH_ASD_BIC, F_OK) == -1) {
// SOCK_PATH_ASD_BIC doesn't exist, means ASD daemon for this
// fru is not running, exit
return 0;
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
err = errno;
syslog(LOG_ERR, "%s failed open socket (errno=%d)", __FUNCTION__, err);
return -1;
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, SOCK_PATH_ASD_BIC);
if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
err = errno;
close(sock);
syslog(LOG_ERR, "%s failed connecting stream socket (errno=%d), %s",
__FUNCTION__, err, server.sun_path);
return -1;
}
if (write(sock, data, 2) < 0) {
err = errno;
syslog(LOG_ERR, "%s error writing on stream sockets (errno=%d)",
__FUNCTION__, err);
}
close(sock);
return 0;
}
int
pal_handle_oem_1s_asd_msg_in(uint8_t fru, uint8_t *data, uint8_t data_len)
{
int sock = 0;
int err = 0;
struct sockaddr_un server;
if (access(SOCK_PATH_JTAG_MSG, F_OK) == -1) {
// SOCK_PATH_JTAG_MSG doesn't exist, means ASD daemon for this
// fru is not running, exit
return 0;
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
err = errno;
syslog(LOG_ERR, "%s failed open socket (errno=%d)", __FUNCTION__, err);
return -1;
}
server.sun_family = AF_UNIX;
strncpy(server.sun_path, SOCK_PATH_JTAG_MSG, UNIX_PATH_MAX);
if (connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
err = errno;
close(sock);
syslog(LOG_ERR, "%s failed connecting stream socket (errno=%d), %s",
__FUNCTION__, err, server.sun_path);
return -1;
}
if (write(sock, data, data_len) < 0) {
err = errno;
syslog(LOG_ERR, "%s error writing on stream sockets (errno=%d)", __FUNCTION__, err);
}
close(sock);
return 0;
}
static int
pal_get_custom_event_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) {
int ret = PAL_EOK;
if (name == NULL) {
syslog(LOG_ERR, "%s() sensor name is missing", __func__);
return -1;
}
switch(fru) {
case FRU_SERVER:
switch(sensor_num) {
case BIC_SENSOR_VRHOT:
snprintf(name, MAX_SNR_NAME, "VR_HOT");
break;
case BIC_SENSOR_SYSTEM_STATUS:
snprintf(name, MAX_SNR_NAME, "SYSTEM_STATUS");
break;
case BIC_SENSOR_PROC_FAIL:
snprintf(name, MAX_SNR_NAME, "PROC_FAIL");
break;
default:
snprintf(name, MAX_SNR_NAME, "Unknown");
ret = PAL_ENOTSUP;
break;
}
break;
case FRU_SCC:
switch(sensor_num) {
case SCC_DRAWER:
snprintf(name, MAX_SNR_NAME, "Drawer");
break;
default:
snprintf(name, MAX_SNR_NAME, "Unknown");
ret = PAL_ENOTSUP;
break;
}
break;
default:
snprintf(name, MAX_SNR_NAME, "Unknown");
ret = PAL_ENOTSUP;
break;
}
return ret;
}
static int
pal_parse_proc_fail(uint8_t *event_data, char *error_log) {
enum {
FRB3 = 0x04,
};
if (event_data == NULL || error_log == NULL) {
syslog(LOG_WARNING, "%s(): NULL parameter", __func__);
return -1;
}
switch(event_data[0]) {
case FRB3:
strcat(error_log, "FRB3, ");
break;
default:
strcat(error_log, "Undefined data, ");
break;
}
return PAL_EOK;
}
int
pal_get_event_sensor_name(uint8_t fru, uint8_t *sel, char *name) {
if (sel == NULL) {
syslog(LOG_ERR, "%s() SEL content is missing", __func__);
return -1;
}
if (name == NULL) {
syslog(LOG_ERR, "%s() sensor name is missing", __func__);
return -1;
}
uint8_t snr_type = sel[SEL_SNR_TYPE];
uint8_t snr_num = sel[SEL_SNR_NUM];
switch (snr_type) {
// If SNR_TYPE is OS_BOOT, sensor name is OS
case OS_BOOT:
// OS_BOOT used by OS
snprintf(name, MAX_SNR_NAME, "OS");
return PAL_EOK;
default:
if (pal_get_custom_event_sensor_name(fru, snr_num, name) == PAL_EOK ) {
return PAL_EOK;
}
break;
}
// Otherwise, translate it based on snr_num
return pal_get_x86_event_sensor_name(fru, snr_num, name);
}
static int
pal_parse_vr_event(uint8_t *event_data, char *error_log) {
if (event_data == NULL) {
syslog(LOG_ERR, "%s() event data is missing", __func__);
return -1;
}
if (error_log == NULL) {
syslog(LOG_ERR, "%s() event log is missing", __func__);
return -1;
}
enum {
VCCIN_VRHOT = 0x00,
VCCIO_VRHOT = 0x01,
DIMM_AB_VRHOT = 0x02,
DIMM_DE_VRHOT = 0x03,
};
uint8_t event = event_data[0];
switch (event) {
case VCCIN_VRHOT:
strcat(error_log, "CPU VCCIN VR HOT Warning");
break;
case VCCIO_VRHOT:
strcat(error_log, "CPU VCCIO VR HOT Warning");
break;
case DIMM_AB_VRHOT:
strcat(error_log, "DIMM AB Memory VR HOT Warning");
break;
case DIMM_DE_VRHOT:
strcat(error_log, "DIMM DE Memory VR HOT Warning");
break;
default:
strcat(error_log, "Undefined VR event");
break;
}
return PAL_EOK;
}
static int
pal_parse_sys_sts_event(uint8_t *event_data, char *error_log) {
if (event_data == NULL) {
syslog(LOG_ERR, "%s() event data is missing", __func__);
return -1;
}
if (error_log == NULL) {
syslog(LOG_ERR, "%s() event log is missing", __func__);
return -1;
}
uint8_t event = event_data[0];
char event_str[MAX_EVENT_STR] = {0};
switch (event) {
case SYS_THERM_TRIP:
strcat(error_log, "System thermal trip");
break;
case SYS_FIVR_FAULT:
strcat(error_log, "System FIVR fault");
break;
case SYS_SURGE_CURR:
strcat(error_log, "Surge Current Warning");
break;
case SYS_PCH_PROCHOT:
strcat(error_log, "PCH prochot");
break;
case SYS_UV_DETECT:
strcat(error_log, "Under Voltage Warning");
break;
case SYS_OC_DETECT:
strcat(error_log, "OC Warning");
break;
case SYS_OCP_FAULT_WARN:
strcat(error_log, "OCP Fault Warning");
break;
case SYS_FW_TRIGGER:
strcat(error_log, "Firmware");
break;
case SYS_HSC_FAULT:
strcat(error_log, "HSC fault");
break;
case SYS_VR_WDT_TIMEOUT:
strcat(error_log, "VR WDT");
break;
case SYS_M2_VPP:
snprintf(event_str, sizeof(event_str), "E1.S device %d VPP Power Control", event_data[2]);
strcat(error_log, event_str);
break;
case SYS_VCCIO_FAULT:
strcat(error_log, "VCCIO fault");
break;
case SYS_SMI_STUCK_LOW:
strcat(error_log, "SMI stuck low over 90s");
break;
case SYS_OV_DETECT:
strcat(error_log, "VCCIO Over Voltage Fault");
break;
default:
strcat(error_log, "Undefined system event");
break;
}
return PAL_EOK;
}
int
pal_parse_sel(uint8_t fru, uint8_t *sel, char *error_log) {
bool is_parsed = false;
if (sel == NULL) {
syslog(LOG_ERR, "%s() SEL is missing", __func__);
return -1;
}
if (error_log == NULL) {
syslog(LOG_ERR, "%s() event log is missing", __func__);
return -1;
}
enum {
EVENT_TYPE_NOTIF = 0x77, /*IPMI-Table 42-1, Event/Reading Type Code Ranges - OEM specific*/
};
uint8_t snr_type = sel[SEL_SNR_TYPE];
uint8_t snr_num = sel[SEL_SNR_NUM];
uint8_t event_dir = sel[SEL_EVENT_TYPE] & 0x80;
uint8_t event_type = sel[SEL_EVENT_TYPE] & 0x7f;
uint8_t *event_data = &sel[SEL_EVENT_DATA];
error_log[0] = '\0';
switch (fru) {
case FRU_SERVER:
switch (snr_num) {
case BIC_SENSOR_VRHOT:
pal_parse_vr_event(event_data, error_log);
is_parsed = true;
break;
case BIC_SENSOR_SYSTEM_STATUS:
pal_parse_sys_sts_event(event_data, error_log);
is_parsed = true;
break;
case BIC_SENSOR_PROC_FAIL:
pal_parse_proc_fail(event_data, error_log);
is_parsed = true;
break;
default:
break;
}
if (is_parsed == true) {
if (event_type == EVENT_TYPE_NOTIF) {
strcat(error_log, " Triggered");
} else {
strcat(error_log, ((event_dir & 0x80) == 0)?" Assertion":" Deassertion");
}
}
break;
case FRU_SCC:
switch (event_type) {
case SENSOR_SPECIFIC:
switch (snr_type) {
case PHYSICAL_SECURITY:
switch (event_data[0] & 0x0F) {
//Sensor Type Code: Physical Security 0x5h, SENSOR_SPECIFIC Offset 0x0h, General Chassis Intrusion: 0x0h
case GENERAL_CHASSIS_INTRUSION:
is_parsed = true;
if (event_dir == 0) {
strcat(error_log, "Drawer be Pulled Out");
} else {
strcat(error_log, "Drawer be Pushed Back");
}
break;
default:
break;
}
break;
default:
break;
}
break;
case GENERIC:
is_parsed = true;
if ((event_data[0] & 0x0F) == 0x00) {
strcat(error_log, "ASSERT, Limit Exceeded");
} else {
strcat(error_log, "DEASSERT, Limit Not Exceeded");
}
break;
default:
break;
}
break;
default:
break;
}
if (is_parsed == false) {
pal_parse_sel_helper(fru, sel, error_log);
}
return PAL_EOK;
}
int
pal_bic_self_test(void) {
uint8_t result[SIZE_SELF_TEST_RESULT] = {0};
return bic_get_self_test_result(result);
}
int
pal_is_bic_ready(uint8_t fru, uint8_t *status) {
*status = is_bic_ready();
return PAL_EOK;
}
bool
pal_is_bic_heartbeat_ok(uint8_t fru) {
uint8_t power_status = 0, server_present = 0;
int ret = 0;
char val[MAX_VALUE_LEN] = {0};
if (fru != FRU_SERVER) {
syslog(LOG_WARNING, "%s(): FRU %x does not have BIC component", __func__, fru);
return PAL_ENOTSUP;
}
ret = pal_is_fru_prsnt(fru, &server_present);
if ((ret == 0) && (server_present == FRU_ABSENT)) {
return true;
}
ret = pal_get_server_12v_power(fru, &power_status);
if ((ret == 0) && (power_status == SERVER_12V_OFF)) {
return true;
}
if (kv_get(KV_KEY_BIC_HEARTBEAT, val, NULL, 0)) {
return false;
}
return atoi(val);
}
int
pal_bic_hw_reset(void) {
if (gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_UIC_COMP_BIC_RST_N), GPIO_VALUE_LOW) < 0) {
syslog(LOG_WARNING, "%s(): failed to reset BIC by hardware", __func__);
return -1;
}
sleep(1);
if (gpio_set_value_by_shadow(fbgc_get_gpio_name(GPIO_UIC_COMP_BIC_RST_N), GPIO_VALUE_HIGH) < 0) {
syslog(LOG_WARNING, "%s(): failed to reset BIC by hardware", __func__);
return -1;
}
return 0;
}
int
pal_set_nic_perst(uint8_t val) {
int i2cfd = 0;
int ret = 0;
uint8_t tbuf[MAX_IPMB_REQ_LEN] = {NIC_CARD_PERST_CTRL, val};
uint8_t tlen = 2;
i2cfd = i2c_cdev_slave_open(I2C_UIC_FPGA_BUS, UIC_FPGA_SLAVE_ADDR >> 1, I2C_SLAVE_FORCE_CLAIM);
if (i2cfd < 0) {
syslog(LOG_WARNING, "%s() Failed to open I2C bus %d, addr 0x%x", __func__, I2C_UIC_FPGA_BUS, UIC_FPGA_SLAVE_ADDR);
return -1;
}
ret = i2c_rdwr_msg_transfer(i2cfd, UIC_FPGA_SLAVE_ADDR, tbuf, tlen, NULL, 0);
if (ret < 0) {
syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen);
}
if (i2cfd >= 0) {
close(i2cfd);
}
return ret;
}
static int
pal_get_iocm_wwid(uint16_t offset, char *wwid) {
char path[MAX_FILE_PATH] = {0};
int fd = 0;
int ret = 0;
ssize_t bytes_rd = 0;
uint8_t type = 0;
if (wwid == NULL) {
syslog(LOG_WARNING, "%s() Failed to get IOCM IOC WWID due to NULL parameter", __func__);
return -1;
}
if (fbgc_common_get_chassis_type(&type) < 0) {
syslog(LOG_WARNING, "%s() Failed to get chassis type\n", __func__);
return -1;
}
if (type == CHASSIS_TYPE5) {
syslog(LOG_WARNING, "%s: IOCM not supported on type 5 system", __func__);
return -1;
}
// Set path for IOCM EEPROM
snprintf(path, sizeof(path), EEPROM_PATH, I2C_T5E1S1_T7IOC_BUS, IOCM_FRU_ADDR);
// check for file presence
if (access(path, F_OK)) {
syslog(LOG_ERR, "%s() Failed to get IOCM IOC WWID because unable to access %s: %s", __func__, path, strerror(errno));
return -1;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
syslog(LOG_ERR, "%s() Failed to get IOCM IOC WWID because unable to open %s: %s", __func__, path, strerror(errno));
return -1;
}
lseek(fd, offset, SEEK_SET);
bytes_rd = read(fd, wwid, WWID_SIZE);
if (bytes_rd != WWID_SIZE) {
syslog(LOG_ERR, "%s() read IOC WWID from %s failed: %s", __func__, path, strerror(errno));
ret = -1;
}
close(fd);
return ret;
}
static int
pal_set_iocm_wwid(uint16_t offset, char *wwid) {
char path[MAX_FILE_PATH] = {0};
int fd = 0;
int ret = 0;
ssize_t bytes_wr = 0;
uint8_t type = 0;
if (wwid == NULL) {
syslog(LOG_WARNING, "%s() Failed to set IOCM IOC WWID due to NULL parameter", __func__);
return -1;
}
if (fbgc_common_get_chassis_type(&type) < 0) {
syslog(LOG_WARNING, "%s() Failed to get chassis type\n", __func__);
return -1;
}
if (type == CHASSIS_TYPE5) {
syslog(LOG_WARNING, "%s: IOCM not supported on type 5 system", __func__);
return -1;
}
// Set path for IOCM EEPROM
snprintf(path, sizeof(path), EEPROM_PATH, I2C_T5E1S1_T7IOC_BUS, IOCM_FRU_ADDR);
// check for file presence
if (access(path, F_OK)) {
syslog(LOG_ERR, "%s() Failed to set IOCM IOC WWID because unable to access %s: %s", __func__, path, strerror(errno));
return -1;
}
fd = open(path, O_WRONLY);
if (fd < 0) {
syslog(LOG_ERR, "%s() Failed to set IOCM IOC WWID because unable to open %s: %s", __func__, path, strerror(errno));
return -1;
}
lseek(fd, offset, SEEK_SET);
bytes_wr = write(fd, wwid, WWID_SIZE);
if (bytes_wr != WWID_SIZE) {
syslog(LOG_ERR, "%s() write IOC WWID to %s failed: %s", __func__, path, strerror(errno));
ret = -1;
}
close(fd);
return ret;
}
static int
pal_get_scc_wwid(char *wwid) {
int ret = 0;
uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0};
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t rlen = 0, tlen = 0;
if (wwid == NULL) {
syslog(LOG_WARNING, "%s() Failed to get SCC IOC WWID due to NULL parameter", __func__);
return -1;
}
memset(tbuf, 0, sizeof(tbuf));
memset(rbuf, 0, sizeof(rbuf));
ret = expander_ipmb_wrapper(NETFN_OEM_REQ, CMD_OEM_EXP_GET_IOC_WWID, tbuf, tlen, rbuf, &rlen);
if ((ret != 0) || (rlen != WWID_SIZE)) {
syslog(LOG_WARNING, "%s() Failed to get SCC IOC WWID", __func__);
return -1;
}
memcpy(wwid, rbuf, rlen);
return ret;
}
int
pal_set_ioc_wwid(uint8_t *ioc_wwid, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) {
int ret = 0;
ioc_wwid_req wwid_req = {0};
if ((ioc_wwid == NULL) || (res_data == NULL) || (res_len == NULL)) {
syslog(LOG_ERR, "%s: Failed to set IOC WWID because the parameters are NULL.", __func__);
return CC_INVALID_PARAM;
}
*res_len = 0;
memset(&wwid_req, 0, sizeof(wwid_req));
memcpy(&wwid_req, ioc_wwid, sizeof(wwid_req));
if (wwid_req.component == IOCM_IOC_WWID) {
ret = pal_set_iocm_wwid(WWID_OFFSET, (char *)wwid_req.wwid);
if (ret < 0 ) {
syslog(LOG_ERR, "%s: Failed to set IOCM IOC WWID.", __func__);
return CC_UNSPECIFIED_ERROR;
}
} else {
syslog(LOG_ERR, "%s: Failed to set IOC WWID because wrong component.", __func__);
return CC_INVALID_PARAM;
}
return CC_SUCCESS;
}
int
pal_get_ioc_wwid(uint8_t ioc_component, uint8_t *res_data, uint8_t *res_len)
{
int ret = 0;
uint8_t ioc_wwid_res[WWID_SIZE] = {0};
if ((res_data == NULL) || (res_len) == NULL) {
syslog(LOG_ERR, "%s: Failed to get IOC WWID because the parameters are NULL.", __func__);
return CC_INVALID_PARAM;
}
*res_len = 0;
memset(&ioc_wwid_res, 0, sizeof(ioc_wwid_res));
if (ioc_component == SCC_IOC_WWID) {
ret = pal_get_scc_wwid((char *)ioc_wwid_res);
if (ret < 0 ) {
syslog(LOG_ERR, "%s: Failed to get SCC IOC WWID.", __func__);
return CC_UNSPECIFIED_ERROR;
}
} else if (ioc_component == IOCM_IOC_WWID) {
ret = pal_get_iocm_wwid(WWID_OFFSET, (char *)ioc_wwid_res);
if (ret < 0 ) {
syslog(LOG_ERR, "%s: Failed to get IOCM IOC WWID.", __func__);
return CC_UNSPECIFIED_ERROR;
}
} else {
syslog(LOG_ERR, "%s: Failed to get IOC WWID because wrong component.", __func__);
return CC_INVALID_PARAM;
}
*res_len = sizeof(ioc_wwid_res);
memcpy(res_data, ioc_wwid_res, sizeof(ioc_wwid_res));
return CC_SUCCESS;
}
bool
pal_is_ioc_ready(uint8_t i2c_bus) {
uint8_t bios_post_cmplt = 0;
uint8_t server_status = 0, fru_present_flag = 0;
gpio_value_t scc_pwr_status = 0;
bic_gpio_t gpio = {0};
// Check server power status
if (pal_get_server_power(FRU_SERVER, &server_status) < 0){
syslog(LOG_WARNING, "%s(): Failed to check IOC is ready because failed to get server power status.", __func__);
return false;
}
if (server_status != SERVER_POWER_ON) {
return false;
}
if (i2c_bus == I2C_T5IOC_BUS) {
// Check SCC present
if (pal_is_fru_prsnt(FRU_SCC, &fru_present_flag) < 0) {
syslog(LOG_WARNING, "%s() Failed to check SCC IOC is ready because failed to get SCC present status.", __func__);
return false;
}
if (fru_present_flag != FRU_PRESENT) {
return false;
}
// Check SCC power status
scc_pwr_status = gpio_get_value_by_shadow(fbgc_get_gpio_name(GPIO_SCC_STBY_PWR_EN));
if (scc_pwr_status == GPIO_VALUE_INVALID) {
syslog(LOG_WARNING, "%s(): Failed to check SCC IOC is ready because failed to get SCC power status.", __func__);
return false;
} else if (scc_pwr_status == GPIO_VALUE_LOW) {
return false;
}
} else if (i2c_bus == I2C_T5E1S0_T7IOC_BUS) {
// Check IOCM IOC present
if (is_e1s_iocm_present(T5_E1S0_T7_IOC_AVENGER) == false) {
return false;
}
if (is_e1s_iocm_i2c_enabled(T5_E1S0_T7_IOC_AVENGER) == false) {
return false;
}
} else {
syslog(LOG_WARNING, "%s() Failed to check IOC is ready due to unknown i2c bus: %d", __func__, i2c_bus);
return false;
}
// Check if BIC is updating
if (pal_is_fw_update_ongoing(FRU_SERVER) == true) {
return false;
}
// Check BIOS is completed via BIC
if (bic_get_gpio(&gpio) < 0) {
syslog(LOG_WARNING, "%s() Failed to get value of BIOS complete pin via BIC", __func__);
return false;
}
bios_post_cmplt = ((((uint8_t*)&gpio)[BIOS_POST_CMPLT/8]) >> (BIOS_POST_CMPLT % 8)) & 0x1;
if (bios_post_cmplt != GPIO_VALUE_LOW) {
return false;
}
return true;
}
int
pal_check_fru_is_valid(const char* fruid_path) {
if (fruid_path == NULL) {
syslog(LOG_ERR, "%s: Failed to check FRU header is valid or not because NULL parameter.", __func__);
return -1;
}
return fbgc_check_fru_is_valid(fruid_path);
}
int
pal_devnum_to_fruid(int devnum) {
return FRU_SERVER;
}
// Get non-persistent key value
int
pal_get_cached_value(char *key, char *value) {
int i = 0;
int ret = 0;
if ((key == NULL) || (value == NULL)) {
syslog(LOG_ERR, "%s, Failed to read cached key value due to NULL parameter", __func__);
return -1;
}
for (i = 0; i < MAX_RETRY; i++) {
ret = 0;
ret = kv_get(key, value, NULL, 0);
if (ret != 0) {
syslog(LOG_ERR, "%s, failed to read cached key value (%s), ret: %d, retry: %d", __func__, key, ret, i);
}
else {
break;
}
msleep(100);
}
return ret;
}
// Set non-persistent key value
int
pal_set_cached_value(char *key, char *value) {
int i = 0;
int ret = 0;
if ((key == NULL) || (value == NULL)) {
syslog(LOG_ERR, "%s, Failed to write cached key value due to NULL parameter", __func__);
return -1;
}
for (i = 0; i < MAX_RETRY; i++) {
ret = 0;
ret = kv_set(key, value, 0, 0);
if (ret != 0) {
syslog(LOG_ERR, "%s, failed to write cached key value (%s), ret: %d, retry: %d", __func__, key, ret, i);
}
else {
break;
}
msleep(100);
}
return ret;
}
int
pal_handle_dcmi(uint8_t fru, uint8_t *request, uint8_t req_len, uint8_t *response, uint8_t *rlen) {
int ret = 0;
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t len = 0;
if ((request == NULL) || (response == NULL) || (rlen == NULL)) {
syslog(LOG_ERR, "%s: Failed to handle DCMI command because the parameters are NULL.", __func__);
return -1;
}
memset(&rbuf, 0, sizeof(rbuf));
ret = bic_me_xmit(request, req_len, rbuf, &len);
if ((ret != 0) || (len < 1)) {
return -1;
}
*rlen = len;
memcpy(response, &rbuf[0], *rlen);
return 0;
}
int
pal_handle_string_sel(char *log, uint8_t log_len)
{
char val[MAX_VALUE_LEN] = {0};
char key[MAX_KEY_LEN] = {0};
int ret = 0, i = 0;
uint8_t fru = 0;
static uint8_t sensor_event_record_list[2] = {0};
uint8_t fru_list[2] = {FRU_SCC, FRU_DPB};
if (log == NULL) {
syslog(LOG_ERR, "%s: Failed to check SCC/DPB sensor SEL", __func__);
return -1;
}
if ((strstr(log, "DPB_") != NULL) || (strstr(log, "HDD_") != NULL)
|| (strstr(log, "FAN_") != NULL) || (strstr(log, "AIRFLOW") != NULL)) {
fru = FRU_DPB;
snprintf(key, sizeof(key), "dpb_sensor_health");
} else if (strstr(log, "SCC_") != NULL) {
fru = FRU_SCC;
snprintf(key, sizeof(key), "scc_sensor_health");
} else {
return ret;
}
for (i = 0; i < sizeof(fru_list); i++) {
if (fru == fru_list[i]) {
// Check assert/deassert
if (strstr(log, "DEASSERT") != NULL) {
sensor_event_record_list[i]--;
} else if (strstr(log, "ASSERT") != NULL) {
sensor_event_record_list[i]++;
} else {
return ret;
}
// Modify health value
if (sensor_event_record_list[i] == 0) {
snprintf(val, sizeof(val), "%d", FRU_STATUS_GOOD);
} else {
snprintf(val, sizeof(val), "%d", FRU_STATUS_BAD);
}
}
}
ret = pal_set_key_value(key, val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): Failed to set key value of %s.", __func__, key);
}
return ret;
}
int
pal_get_nm_selftest_result(uint8_t fruid, uint8_t *data) {
int ret = 0;
ipmi_req_t_common_header req = {0};
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t rlen = 0;
me_xmit_res *res = (me_xmit_res *)rbuf;
req.netfn_lun = IPMI_NETFN_SHIFT(NETFN_APP_REQ);
req.cmd = CMD_APP_GET_SELFTEST_RESULTS;
ret = bic_me_xmit((uint8_t *)(&req), sizeof(ipmi_req_t_common_header), (uint8_t *)res, &rlen);
if (ret < 0 ) {
syslog(LOG_ERR, "%s: Failed to do ME self test because ME transmission failed", __func__);
return ret;
}
if ((rlen - 1) != SIZE_SELF_TEST_RESULT) {
syslog(LOG_ERR, "%s: Failed to do ME self test because the response size is wrong: %d, expected: %d", __func__, (rlen - 1), SIZE_SELF_TEST_RESULT);
return -1;
}
if (res->cc != CC_SUCCESS) {
syslog(LOG_ERR, "%s: Failed to do ME self test, Completion Code: %02X", __func__, res->cc);
return -1;
}
memcpy(data, res->data, (rlen - 1));
return 0;
}
int
pal_get_fpga_ver_cache(uint8_t bus, uint8_t addr, char *ver_str) {
char key[MAX_KEY_LEN] = {0};
if (ver_str == NULL) {
syslog(LOG_WARNING, "Fail to get FPGA version cache because parameter *ver_str is NULL.");
return -1;
}
snprintf(key, sizeof(key), "fpga_bus%d_addr%02Xh_version", bus, addr);
if (kv_get(key, ver_str, NULL, 0) != 0) {
if (pal_set_fpga_ver_cache(bus, addr) != 0) {
return -1;
}
}
kv_get(key, ver_str, NULL, 0);
return 0;
}
int
pal_set_fpga_ver_cache(uint8_t bus, uint8_t addr) {
uint32_t ver_reg = GET_FPGA_VER_OFFSET;
int i2cfd = 0, ret = 0, retry = 0;
uint8_t tbuf[MAX_FPGA_VER_LEN] = {0x00};
uint8_t rbuf[MAX_FPGA_VER_LEN] = {0x00};
uint8_t rlen = sizeof(rbuf), tlen = sizeof(tbuf);
char key[MAX_KEY_LEN] = {0};
char value[MAX_VALUE_LEN] = {0};
i2cfd = i2c_cdev_slave_open(bus, addr >> 1, I2C_SLAVE_FORCE_CLAIM);
if (i2cfd < 0) {
syslog(LOG_ERR, "Failed to set FPGA version cache value due to I2C BUS: %d open failed.", bus);
return i2cfd;
}
if (ioctl(i2cfd, I2C_SLAVE, addr) < 0) {
syslog(LOG_ERR, "Failed to set FPGA version cache value due to talk to slave%02Xh failed.", addr);
ret = -1;
} else {
memcpy(tbuf, &ver_reg, tlen);
while (retry < MAX_RETRY) {
ret = i2c_rdwr_msg_transfer(i2cfd, addr << 1, tbuf, tlen, rbuf, rlen);
if (ret == 0) {
snprintf(key, sizeof(key), "fpga_bus%d_addr%02Xh_version", bus, addr);
snprintf(value, sizeof(value), "%02X%02X%02X%02X", rbuf[3], rbuf[2], rbuf[1], rbuf[0]);
kv_set(key, value, 0, 0);
break;
} else {
retry++;
msleep(100);
}
}
if (retry == MAX_RETRY) {
syslog(LOG_ERR, "Fail to set FPGA version cache value due to i2c_rdwr_msg_transfer failed. bus: %d addr: %02Xh ret: %d", bus, addr, ret);
}
}
close(i2cfd);
return ret;
}
int pal_get_num_devs(uint8_t slot, uint8_t *num) {
if (num == NULL) {
syslog(LOG_ERR, "%s: Failed to get device num due to parameter *num is NULL", __func__);
return -1;
}
*num = MAX_NUM_DEVS - 1;
return 0;
}
int
pal_get_dev_id(char *str, uint8_t *dev) {
if ((str == NULL) || (dev == NULL)) {
syslog(LOG_ERR, "%s: Failed to get device id due to parameter is NULL", __func__);
return -1;
}
return fbgc_common_dev_id(str, dev);
}
int
pal_handle_oem_1s_dev_power(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) {
uint8_t dev_type = 0, dev_id = 0;
uint8_t chassis_type = 0;
int ret = 0;
char key[MAX_KEY_LEN] = {0};
char val[MAX_VALUE_LEN] = {0};
if ((req_data == NULL) || (res_data == NULL) || (res_len == NULL)) {
syslog(LOG_WARNING, "%s: Failed to handle device power due to parameters are NULL.", __func__);
return CC_INVALID_PARAM;
}
if ((req_len < 2) || (req_len > 3)) {
return CC_INVALID_LENGTH;
}
ret = fbgc_common_get_chassis_type(&chassis_type);
if ((ret < 0) || (chassis_type != CHASSIS_TYPE5)) {
syslog(LOG_WARNING, "%s: Failed to handle device power due to only support Type5.", __func__);
return CC_UNSPECIFIED_ERROR;
}
dev_id = req_data[0];
if ((dev_id != T5_E1S0_T7_IOC_AVENGER) && (dev_id != T5_E1S1_T7_IOCM_VOLT)) {
syslog(LOG_ERR, "%s: Failed to handle device power due to wrong device id: %d.", __func__, dev_id);
return CC_PARAM_OUT_OF_RANGE;
}
// Action: get device power
if ((req_data[1] == GET_DEV_POWER) && (req_len == 2)) {
ret = pal_get_device_power(slot, dev_id + 1, &res_data[0], &dev_type);
if (ret < 0) {
syslog(LOG_WARNING, "%s: Failed to get device %d power", __func__, dev_id);
return CC_UNSPECIFIED_ERROR;
}
*res_len = 1;
// Action: set device power
} else if ((req_data[1] == SET_DEV_POWER) && (req_len == 3)) {
if ((req_data[2] != DEVICE_POWER_ON) && (req_data[2] != DEVICE_POWER_OFF)) {
syslog(LOG_ERR, "%s: Failed to set device power due to wrong power status: 0x%02X.", __func__, req_data[2]);
return CC_UNSPECIFIED_ERROR;
}
ret = pal_set_dev_power_status(dev_id + 1, req_data[2]);
if (ret < 0) {
syslog(LOG_ERR, "%s: Failed to set device %d power.", __func__, dev_id);
return CC_UNSPECIFIED_ERROR;
}
// Action: get device led
} else if ((req_data[1] == GET_DEV_LED) && (req_len == 2)) {
snprintf(key, sizeof(key), "e1s%d_led_status", dev_id);
ret = kv_get(key, val, NULL, 0);
if (ret < 0) {
syslog(LOG_ERR, "%s: Failed to get device %d led status.", __func__, dev_id);
return CC_UNSPECIFIED_ERROR;
}
if (strcmp(val, "on") == 0) {
res_data[0] = DEV_LED_ON;
} else if (strcmp(val, "off") == 0) {
res_data[0] = DEV_LED_OFF;
} else if (strcmp(val, "blinking") == 0) {
res_data[0] = DEV_LED_BLINKING;
}
*res_len = 1;
// Action: set device led
} else if ((req_data[1] == SET_DEV_LED) && (req_len == 3)) {
snprintf(key, sizeof(key), "e1s%d_led_status", dev_id);
if (req_data[2] == DEV_LED_ON) {
snprintf(val, sizeof(val), "on");
} else if (req_data[2] == DEV_LED_OFF) {
snprintf(val, sizeof(val), "off");
} else if (req_data[2] == DEV_LED_BLINKING) {
snprintf(val, sizeof(val), "blinking");
} else {
syslog(LOG_ERR, "%s: Failed to set device led status due to wrong led status: 0x%02X.", __func__, req_data[2]);
return CC_UNSPECIFIED_ERROR;
}
ret = kv_set(key, val, 0, 0);
if (ret < 0) {
syslog(LOG_ERR, "%s: Failed to set device %d led status.", __func__, dev_id);
return CC_UNSPECIFIED_ERROR;
}
// Action: get device present
} else if ((req_data[1] == GET_DEV_PRESENT) && (req_len == 2)) {
if (is_e1s_iocm_present(dev_id) == true) {
res_data[0] = FRU_PRESENT;
} else {
res_data[0] = FRU_ABSENT;
}
*res_len = 1;
} else {
syslog(LOG_ERR, "%s: Failed to handle device: 0x%02X action: 0x%02X req_len: %d", __func__, dev_id, req_data[1], req_len);
return CC_UNSPECIFIED_ERROR;
}
return CC_SUCCESS;
}
int
pal_clear_event_only_error_ack () {
int ret = 0;
char key[MAX_KEY_LEN] = {0};
char val[MAX_VALUE_LEN] = {0};
int sel_error_record = 0, sel_event_error_record = 0;
// Clear SEL event error record
snprintf(key, sizeof(key), "sel_event_error_record");
snprintf(val, sizeof(val), "%d", sel_event_error_record);
ret = kv_set(key, val, 0, 0);
if (ret < 0) {
syslog(LOG_ERR, "%s(): Failed to clear event only error record due to pal_set_key_value failed. key: %s", __func__, key);
return -1;
}
// Get SEL error record
snprintf(key, sizeof(key), "sel_error_record");
if (kv_get(key, val, NULL, 0) == 0) {
sel_error_record = atoi(val);
}
// Update server_sel_error
snprintf(key, sizeof(key), "server_sel_error");
if (sel_error_record > 0) {
snprintf(val, sizeof(val), "%d", FRU_STATUS_BAD);
} else {
snprintf(val, sizeof(val), "%d", FRU_STATUS_GOOD);
}
ret = pal_set_key_value(key, val);
if (ret < 0) {
syslog(LOG_ERR, "%s(): Failed to update error record due to pal_set_key_value failed. key: %s", __func__, key);
}
return ret;
}
static void
pal_search_pcie_err(uint8_t err1_id, uint8_t err2_id, char **err1_desc, char **err2_desc, int *err1_size, int *err2_size) {
int i = 0;
int err_desc_size = 0;
int err_table_size = (sizeof(pcie_err_table) / sizeof(PCIE_ERR_DECODE));
for ( i = 0; i < err_table_size; i++ ) {
if ( err2_id == pcie_err_table[i].err_id ) {
err_desc_size = strlen(pcie_err_table[i].err_desc) + ERROR_ID_LOG_LEN + 2;
*err2_desc = calloc(err_desc_size, sizeof(char));
*err2_size = err_desc_size;
snprintf(*err2_desc, err_desc_size, ", ErrID2: 0x%02X(%s)",err2_id, pcie_err_table[i].err_desc);
continue;
} else if ( err1_id == pcie_err_table[i].err_id ) {
err_desc_size = strlen(pcie_err_table[i].err_desc) + ERROR_ID_LOG_LEN + 2;
*err1_desc = calloc(err_desc_size, sizeof(char));
*err1_size = err_desc_size;
snprintf(*err1_desc, err_desc_size, ", ErrID1: 0x%02X(%s)",err1_id, pcie_err_table[i].err_desc);
continue;
}
if ( *err1_desc != NULL && *err2_desc != NULL ) {
break;
}
}
if (*err2_desc == NULL) {
*err2_desc = calloc(ERROR_ID_LOG_LEN, sizeof(char));
*err2_size = ERROR_ID_LOG_LEN;
snprintf(*err2_desc, ERROR_ID_LOG_LEN, ", ErrID2: 0x%02X", err2_id);
}
if (*err1_desc == NULL) {
*err1_desc = calloc(ERROR_ID_LOG_LEN, sizeof(char));
*err1_size = ERROR_ID_LOG_LEN;
snprintf(*err1_desc, ERROR_ID_LOG_LEN, ", ErrID1: 0x%02X", err1_id);
}
return;
}
int
pal_parse_oem_unified_sel(uint8_t fru, uint8_t *sel, char *error_log) {
#define ERROR_LOG_LEN 256
uint8_t general_info = 0;
uint8_t error_type = 0;
uint8_t err_id1 = 0, err_id2 = 0;
uint8_t bus_id = 0, dev_id = 0, fun_id = 0;
uint16_t totalerrid1cnt = 0, err_info = 0;
uint8_t plat = 0;
char temp_log[ERROR_LOG_LEN/2] = {0};
char *err1_descript = NULL, *err2_descript = NULL;
int err1_size = 0, err2_size = 0;
if ((sel == NULL) || (error_log == NULL)) {
syslog(LOG_WARNING, "%s(): Failed to parse OEM unfied SEL due to NULL parameters.", __func__);
return PAL_ENOTSUP;
}
error_log[0] = '\0';
general_info = (uint8_t) sel[3];
error_type = general_info & 0x0f;
err_info = ((sel[9] << 8) | sel[8]);
dev_id = sel[10] >> 3;
fun_id = sel[10] & 0x7;
bus_id = sel[11];
totalerrid1cnt = ((sel[13] << 8) | sel[12]);
err_id2 = sel[14];
err_id1 = sel[15];
switch (error_type) {
case UNIFIED_PCIE_ERR:
plat = (general_info & 0x10) >> 4;
if (plat == 0) { //x86
pal_search_pcie_err(err_id1, err_id2, &err1_descript, &err2_descript, &err1_size, &err2_size);
snprintf(error_log, ERROR_LOG_LEN, "GeneralInfo: x86/PCIeErr(0x%02X), Bus %02X/Dev %02X/Fun %02X, TotalErrID1Cnt: 0x%04X",
general_info, bus_id, dev_id, fun_id, totalerrid1cnt);
if (err2_descript != NULL) {
strncat(error_log, err2_descript, err2_size);
free(err2_descript);
}
if (err1_descript != NULL) {
strncat(error_log, err1_descript, err1_size);
free(err1_descript);
}
} else {
snprintf(error_log, ERROR_LOG_LEN, "GeneralInfo: ARM/PCIeErr(0x%02X), Aux. Info: 0x%04X, Bus %02X/Dev %02X/Fun %02X, TotalErrID1Cnt: 0x%04X, ErrID2: 0x%02X, ErrID1: 0x%02X",
general_info, err_info, bus_id, dev_id, fun_id, totalerrid1cnt, err_id2, err_id1);
}
snprintf(temp_log, sizeof(temp_log), "B %02X D %02X F %02X PCIe err,FRU:%u", bus_id, dev_id, fun_id, fru);
pal_add_cri_sel(temp_log);
return PAL_EOK;
}
pal_parse_oem_unified_sel_common(fru, sel, error_log);
return PAL_EOK;
}
int
pal_ignore_thresh(uint8_t fru, uint8_t snr_num, uint8_t thresh) {
// Only SCC IOC temperature is monitoring by BMC
if ((fru == FRU_SCC) && (snr_num != SCC_IOC_TEMP)) {
return 1;
}
return 0;
}
int
pal_get_fanfru_serial_num(int fan_id, uint8_t *serial_num, uint8_t serial_len) {
char path[MAX_PATH_LEN] = {0};
int fruid_len = 0, bytes_rd = 0;
uint8_t serial_addr = 0, produce_start_addr = 0;
FILE *fruid_fd;
uint8_t *eeprom;
int ret = 0;
if (serial_num == NULL) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to parameter is NULL.", __func__, fan_id);
return ret;
}
ret = pal_get_fruid_path(FRU_FAN0 + fan_id, path);
if (ret < 0) {
return -1;
}
ret = -1;
fruid_fd = fopen(path, "rb");
if (fruid_fd == NULL) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to unable to open the file %s", __func__, fan_id, path);
return ret;
}
fseek(fruid_fd, 0, SEEK_END);
fruid_len = (uint32_t) ftell(fruid_fd);
if (fruid_len == 0) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to file %s is empty", __func__, fan_id, path);
goto exit;
}
fseek(fruid_fd, 0, SEEK_SET);
eeprom = (uint8_t *) malloc(fruid_len);
if (eeprom == NULL) {
syslog(LOG_ERR, "%s: FAN%d FRU malloc: memory allocation failed", __func__, fan_id);
goto exit;
}
bytes_rd = fread(eeprom, sizeof(uint8_t), fruid_len, fruid_fd);
if (bytes_rd != fruid_len) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to the file size is wrong: %d, expected: %d",
__func__, fan_id, bytes_rd, fruid_len);
goto exit;
}
// Read Produce Serial Number
if (FRUID_HEADER_OFFSET_PRODUCT_INFO >= fruid_len) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to the file is incomplete: %d, expected: %d",
__func__, fan_id, fruid_len, FRUID_HEADER_OFFSET_PRODUCT_INFO);
goto exit;
}
produce_start_addr = eeprom[FRUID_HEADER_OFFSET_PRODUCT_INFO]*FRUID_OFFSET_MULTIPLIER;
if (produce_start_addr >= fruid_len) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to the file is incomplete: %d, expected: %d",
__func__, fan_id, fruid_len, produce_start_addr);
goto exit;
}
serial_addr = produce_start_addr + FRUID_PRODUCT_OFFSET_SERIAL;
if (serial_addr >= fruid_len) {
syslog(LOG_ERR, "%s: Failed to read FAN%d FRU due to the file is incomplete: %d, expected: %d",
__func__, fan_id, fruid_len, serial_addr);
goto exit;
}
memcpy(serial_num, &eeprom[serial_addr], serial_len);
ret = 0;
exit:
if (fruid_fd != NULL) {
fclose(fruid_fd);
}
if (eeprom != NULL) {
free(eeprom);
}
return ret;
}
int
pal_handle_fan_fru_checksum_sel(char *log, uint8_t log_len) {
uint8_t *fanfru_check_sel;
uint8_t *fanfru_check_bin;
char cmd[MAX_SYS_CMD_REQ_LEN] = {0};
char key[MAX_KEY_LEN] = {0};
char path[MAX_PATH_LEN] = {0};
char *temp_str;
int i = 0, j = 0, ret = 0;
uint8_t check_len = 0, check_index = 1;
if (log == NULL) {
syslog(LOG_WARNING, "%s: Failed to handle fan fru certified SEL due to parameters is NULL.", __func__);
return -1;
}
// if SEL doesn't contain FANFRU certified data
// don't handle
if (strstr(log, "FANFRU:") == NULL) {
return -1;
}
temp_str = strtok(log, ":");
temp_str = strtok(NULL, ":");
for (i = 0; i < SINGLE_FAN_CNT; i++) { // 4 fans
memset(key, 0, sizeof(key));
memset(path, 0, sizeof(path));
snprintf(key, sizeof(key), "fan%d_dumped", i);
ret = pal_get_fruid_path(FRU_FAN0 + i, path);
if (ret < 0) {
syslog(LOG_WARNING, "%s: Failed to get fan%d fru path.", __func__, i);
continue;
}
// Get certified data length from SEL
check_len = temp_str[check_index];
check_index = check_index + 1;
// if fanN check len equal to 0
// indicate Expander could not get the correct SN of fanN
// SEL will not bring value of fanN
// Skip the fanN update
if (check_len == 0) {
continue;
}
// Get certified data from SEL
fanfru_check_sel = (uint8_t *) malloc(check_len);
for (j = 0; j < check_len; j++) {
fanfru_check_sel[j] = temp_str[check_index + j];
}
check_index = check_index + check_len;
// Get certified data from bin file
fanfru_check_bin = (uint8_t *) malloc(check_len);
ret = pal_get_fanfru_serial_num(i, fanfru_check_bin, check_len);
// Update fan fru if get serial number failed, serial number is different, or local fan fru checksum is wrong.
// run exp-cache to udpate FAN FRU binary data
if ((ret < 0) || (strncmp(fanfru_check_sel, fanfru_check_bin, check_len) != 0) || (pal_check_fru_is_valid(path) < 0)) {
snprintf(cmd, sizeof(cmd), "/usr/bin/exp-cached --update_fan fan%d> /dev/null 2>&1 &", i);
run_command(cmd);
}
free(fanfru_check_sel);
free(fanfru_check_bin);
kv_set(key, STR_VALUE_1, 0, 0);
}
return 0;
}
void
pal_inform_bic_mode(uint8_t fru, uint8_t mode) {
switch(mode) {
case BIC_MODE_NORMAL:
// Bridge IC entered normal mode
syslog(LOG_CRIT, "%s(): BIC has been reset.", __func__);
break;
case BIC_MODE_UPDATE:
// Bridge IC entered update mode
break;
default:
break;
}
return;
}