meta-facebook/meta-fby35/recipes-fby35/plat-libs/files/pal/pal.c (2,993 lines of code) (raw):

/* * * Copyright 2015-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. */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <ctype.h> #include <errno.h> #include <syslog.h> #include <string.h> #include <unistd.h> #include <stddef.h> #include <time.h> #include <stdlib.h> #include <sys/time.h> #include <sys/types.h> #include <openbmc/kv.h> #include <openbmc/libgpio.h> #include <openbmc/obmc-i2c.h> #include <openbmc/ncsi.h> #include <openbmc/nl-wrapper.h> #include <sys/socket.h> #include <linux/netlink.h> #include <sys/un.h> #include "pal.h" #define PLATFORM_NAME "yosemitev35" #define LAST_KEY "last_key" #define OFFSET_SYS_GUID 0x17F0 #define OFFSET_DEV_GUID 0x1800 #define NUM_SERVER_FRU 4 #define NUM_NIC_FRU 1 #define NUM_BMC_FRU 1 #define FAN_FAIL_RECORD_PATH "/tmp/cache_store/fan_fail_boost" const char pal_fru_list_print[] = "all, slot1, slot2, slot3, slot4, bmc, nic, bb, nicexp"; const char pal_fru_list_rw[] = "slot1, slot2, slot3, slot4, bmc, bb, nicexp"; const char pal_fru_list_sensor_history[] = "all, slot1, slot2, slot3, slot4, bmc nic"; const char pal_fru_list[] = "all, slot1, slot2, slot3, slot4, bmc, nic"; const char pal_guid_fru_list[] = "slot1, slot2, slot3, slot4, bmc"; const char pal_server_list[] = "slot1, slot2, slot3, slot4"; const char pal_dev_fru_list[] = "all, 1U, 2U, 1U-dev0, 1U-dev1, 1U-dev2, 1U-dev3, 2U-dev0, 2U-dev1, 2U-dev2, 2U-dev3, 2U-dev4, 2U-dev5, " \ "2U-dev6, 2U-dev7, 2U-dev8, 2U-dev9, 2U-dev10, 2U-dev11, 2U-dev12, 2U-dev13, 2U-X8, 2U-X16"; const char pal_dev_pwr_list[] = "all, 1U-dev0, 1U-dev1, 1U-dev2, 1U-dev3, 2U-dev0, 2U-dev1, 2U-dev2, 2U-dev3, 2U-dev4, 2U-dev5, " \ "2U-dev6, 2U-dev7, 2U-dev8, 2U-dev9, 2U-dev10, 2U-dev11, 2U-dev12, 2U-dev13"; const char pal_dev_pwr_option_list[] = "status, off, on, cycle"; const char *pal_server_fru_list[NUM_SERVER_FRU] = {"slot1", "slot2", "slot3", "slot4"}; const char *pal_nic_fru_list[NUM_NIC_FRU] = {"nic"}; const char *pal_bmc_fru_list[NUM_BMC_FRU] = {"bmc"}; static char sel_error_record[NUM_SERVER_FRU] = {0}; size_t server_fru_cnt = NUM_SERVER_FRU; size_t nic_fru_cnt = NUM_NIC_FRU; size_t bmc_fru_cnt = NUM_BMC_FRU; #define SYSFW_VER "sysfw_ver_slot" #define SYSFW_VER_STR SYSFW_VER "%d" #define BOOR_ORDER_STR "slot%d_boot_order" #define SEL_ERROR_STR "slot%d_sel_error" #define SNR_HEALTH_STR "slot%d_sensor_health" #define GPIO_OCP_DEBUG_BMC_PRSNT_N "OCP_DEBUG_BMC_PRSNT_N" #define SLOT1_POSTCODE_OFFSET 0x02 #define SLOT2_POSTCODE_OFFSET 0x03 #define SLOT3_POSTCODE_OFFSET 0x04 #define SLOT4_POSTCODE_OFFSET 0x05 #define DEBUG_CARD_UART_MUX 0x06 #define BB_CPLD_IO_BASE_OFFSET 0x16 #define ENABLE_STR "enable" #define DISABLE_STR "disable" #define STATUS_STR "status" #define FAN_MODE_FILE "/tmp/cache_store/fan_mode" #define FAN_MODE_STR_LEN 8 // include the string terminal #define IPMI_GET_VER_FRU_NUM 5 #define IPMI_GET_VER_MAX_COMP 9 #define MAX_FW_VER_LEN 32 //include the string terminal #define MAX_COMPONENT_LEN 32 //include the string terminal #define BMC_CPLD_BUS (12) #define NIC_EXP_CPLD_BUS (9) #define CPLD_FW_VER_ADDR (0x80) #define BMC_CPLD_VER_REG (0x28002000) #define SB_CPLD_VER_REG (0x000000c0) #define KEY_BMC_CPLD_VER "bmc_cpld_ver" #define ERROR_LOG_LEN 256 #define ERR_DESC_LEN 64 static int key_func_pwr_last_state(int event, void *arg); static int key_func_por_cfg(int event, void *arg); enum key_event { KEY_BEFORE_SET, KEY_AFTER_INI, }; enum sel_event_data_index { DATA_INDEX_0 = 3, DATA_INDEX_1 = 4, DATA_INDEX_2 = 5, }; struct pal_key_cfg { char *name; char *def_val; int (*function)(int, void*); } key_cfg[] = { /* name, default value, function */ {SYSFW_VER "1", "0", NULL}, {SYSFW_VER "2", "0", NULL}, {SYSFW_VER "3", "0", NULL}, {SYSFW_VER "4", "0", NULL}, {"pwr_server1_last_state", "on", key_func_pwr_last_state}, {"pwr_server2_last_state", "on", key_func_pwr_last_state}, {"pwr_server3_last_state", "on", key_func_pwr_last_state}, {"pwr_server4_last_state", "on", key_func_pwr_last_state}, {"timestamp_sled", "0", NULL}, {"slot1_por_cfg", "lps", key_func_por_cfg}, {"slot2_por_cfg", "lps", key_func_por_cfg}, {"slot3_por_cfg", "lps", key_func_por_cfg}, {"slot4_por_cfg", "lps", key_func_por_cfg}, {"slot1_boot_order", "0100090203ff", NULL}, {"slot2_boot_order", "0100090203ff", NULL}, {"slot3_boot_order", "0100090203ff", NULL}, {"slot4_boot_order", "0100090203ff", NULL}, {"slot1_cpu_ppin", "0", NULL}, {"slot2_cpu_ppin", "0", NULL}, {"slot3_cpu_ppin", "0", NULL}, {"slot4_cpu_ppin", "0", NULL}, {"fru1_restart_cause", "3", NULL}, {"fru2_restart_cause", "3", NULL}, {"fru3_restart_cause", "3", NULL}, {"fru4_restart_cause", "3", NULL}, {"slot1_sensor_health", "1", NULL}, {"slot2_sensor_health", "1", NULL}, {"slot3_sensor_health", "1", NULL}, {"slot4_sensor_health", "1", NULL}, {"slot1_sel_error", "1", NULL}, {"slot2_sel_error", "1", NULL}, {"slot3_sel_error", "1", NULL}, {"slot4_sel_error", "1", NULL}, {"ntp_server", "", NULL}, /* Add more Keys here */ {LAST_KEY, LAST_KEY, NULL} /* This is the last key of the list */ }; MAPTOSTRING root_port_common_mapping[] = { // XCC { 0xB3, 1, 0x5A, "Num 0", "SB" }, // root_port=0x5A, Boot Drive { 0xB3, 5, 0x5E, "Class 1", "NIC"}, // root_port=0x5E, Class 1 NIC // MCC { 0xBB, 7, 0x57, "Num 0", "SB" }, // root_port=0x5G, Boot Drive { 0xBB, 5, 0x5E, "Num 0", "SB" }, // root_port=0x5E, Boot Drive { 0xBB, 1, 0x5A, "Class 1", "NIC"}, // root_port=0x5A, Class 1 NIC }; MAPTOSTRING root_port_mapping[] = { { 0xB2, 3, 0x3D, "Num 0", "1OU"}, //Port 0x4D { 0xB2, 2, 0x3C, "Num 1", "1OU"}, //Port 0x4C { 0xB2, 1, 0x3B, "Num 2", "1OU"}, //Port 0x4B { 0xB2, 0, 0x3A, "Num 3", "1OU"}, //Port 0x4A { 0x15, 0, 0x1A, "Num 0", "2OU"}, //Port 0x1A { 0x15, 1, 0x1B, "Num 1", "2OU"}, //Port 0x1B { 0x63, 1, 0x2B, "Num 2", "2OU"}, //Port 0x2B { 0x63, 0, 0x2A, "Num 3", "2OU"}, //Port 0x2A { 0x15, 2, 0x1C, "Num 4", "2OU"}, //Port 0x1C { 0x15, 3, 0x1D, "Num 5", "2OU"}, //Port 0x1D }; MAPTOSTRING root_port_mapping_gpv3[] = { // bus, device, port, silk screen, location { 0x17, 0, 0x01, "Num 0", "2OU"}, { 0x17, 1, 0x02, "Num 1", "2OU"}, { 0x17, 2, 0x03, "Num 2", "2OU"}, { 0x17, 3, 0x04, "Num 3", "2OU"}, { 0x17, 4, 0x05, "Num 4", "2OU"}, { 0x17, 5, 0x06, "Num 5", "2OU"}, { 0x17, 6, 0x07, "Num 6", "2OU"}, { 0x17, 7, 0x08, "Num 7", "2OU"}, { 0x65, 0, 0x09, "Num 8", "2OU"}, { 0x65, 1, 0x0A, "Num 9", "2OU"}, { 0x65, 2, 0x0B, "Num 10", "2OU"}, { 0x65, 3, 0x0C, "Num 11", "2OU"}, { 0x17, 8, 0x0D, "E1S 0", "2OU"}, { 0x65, 4, 0x0E, "E1S 1", "2OU"}, }; MAPTOSTRING root_port_mapping_e1s[] = { // bus, device, port, silk screen, location { 0xB2, 0, 0x3A, "Num 0", "1OU"}, { 0xB2, 1, 0x3B, "Num 1", "1OU"}, { 0xB2, 2, 0x3C, "Num 2", "1OU"}, { 0xB2, 3, 0x3D, "Num 3", "1OU"}, { 0x63, 0, 0x2A, "Num 0", "2OU"}, { 0x63, 1, 0x2B, "Num 1", "2OU"}, { 0x15, 3, 0x1D, "Num 2", "2OU"}, { 0x15, 2, 0x1C, "Num 3", "2OU"}, { 0x15, 1, 0x1B, "Num 4", "2OU"}, { 0x15, 0, 0x1A, "Num 5", "2OU"}, }; PCIE_ERR_DECODE pcie_err_tab[] = { {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"}, {0x53, "DPC triggered by uncorrectable error"}, {0x54, "DPC triggered by ERR_NONFATAL"}, {0x55, "DPC triggered by ERR_FATAL"}, {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; i = 0; while(strcmp(key_cfg[i].name, LAST_KEY)) { // If Key is valid, return success if (!strcmp(key, key_cfg[i].name)) return i; i++; } #ifdef DEBUG syslog(LOG_WARNING, "pal_key_index: invalid key - %s", key); #endif return -1; } static int key_func_pwr_last_state(int event, void *arg) { if (event == KEY_BEFORE_SET) { if (strcmp((char *)arg, "on") && strcmp((char *)arg, "off")) return -1; } return 0; } static int key_func_por_cfg(int event, void *arg) { if (event == KEY_BEFORE_SET) { if (strcmp((char *)arg, "lps") && strcmp((char *)arg, "on") && strcmp((char *)arg, "off")) return -1; } return 0; } int pal_get_key_value(char *key, char *value) { int index; // Check is key is defined and valid if ((index = pal_key_index(key)) < 0) return -1; return kv_get(key, value, NULL, KV_FPERSIST); } int pal_set_key_value(char *key, char *value) { int index, ret; // Check is 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; } return kv_set(key, value, 0, KV_FPERSIST); } void pal_dump_key_value(void) { int ret; int i = 0; char value[MAX_VALUE_LEN] = {0x0}; while (strcmp(key_cfg[i].name, LAST_KEY)) { printf("%s:", key_cfg[i].name); if ((ret = kv_get(key_cfg[i].name, value, NULL, KV_FPERSIST)) < 0) { printf("\n"); } else { printf("%s\n", value); } i++; memset(value, 0, MAX_VALUE_LEN); } } int pal_set_def_key_value() { int i; //char key[MAX_KEY_LEN] = {0}; for(i = 0; strcmp(key_cfg[i].name, LAST_KEY) != 0; i++) { if (kv_set(key_cfg[i].name, key_cfg[i].def_val, 0, KV_FCREATE | KV_FPERSIST)) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_def_key_value: kv_set failed."); #endif } if (key_cfg[i].function) { key_cfg[i].function(KEY_AFTER_INI, key_cfg[i].name); } } return 0; } int pal_get_boot_order(uint8_t slot_id, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) { int i = 0, j = 0; int ret = PAL_EOK; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; *res_len = 0; sprintf(key, BOOR_ORDER_STR, slot_id); ret = pal_get_key_value(key, str); if ( ret < 0 ) { *res_len = 0; goto error_exit; } for (i = 0; i < 2*SIZE_BOOT_ORDER; i += 2) { sprintf(tstr, "%c%c\n", str[i], str[i+1]); boot[j++] = strtol(tstr, NULL, 16); } *res_len = SIZE_BOOT_ORDER; error_exit: return ret; } int pal_set_boot_order(uint8_t slot_id, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) { int i = 0; int j = 0; int network_dev = 0; int ret = PAL_EOK; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; enum { BOOT_DEVICE_IPV4 = 0x1, BOOT_DEVICE_IPV6 = 0x9, BOOT_DEVICE_RSVD = 0xff, }; *res_len = 0; for (i = 0; i < SIZE_BOOT_ORDER; i++) { if ( (i > 0) && (boot[i] != BOOT_DEVICE_RSVD) ) { // byte[0] is boot mode, byte[1:5] are boot order for (j = i+1; j < SIZE_BOOT_ORDER; j++) { //not allow having the same boot devcie in the boot order if ( boot[i] == boot[j] ) { syslog(LOG_WARNING, "Not allow having the same boot devcie in the boot order"); ret = CC_INVALID_PARAM; goto error_exit; } } if ((boot[i] == BOOT_DEVICE_IPV4) || (boot[i] == BOOT_DEVICE_IPV6)) { network_dev++; } } snprintf(tstr, 3, "%02x", boot[i]); #pragma GCC diagnostic push // avoid the following compililatin error // error: '__builtin___strncat_chk' output may be truncated copying 3 bytes from a string of length 9 [-Werror=stringop-truncation] // // per https://stackoverflow.com/questions/50198319/gcc-8-wstringop-truncation-what-is-the-good-practice // this warning was was added in gcc8 // // here we do want to truncate the string #pragma GCC diagnostic ignored "-Wstringop-truncation" strncat(str, tstr, 3); #pragma GCC diagnostic pop } //not allow having more than 1 network boot device in the boot order if ( network_dev > 1 ) { syslog(LOG_WARNING, "Not allow having more than 1 network boot device in the boot order"); ret = CC_INVALID_PARAM; goto error_exit; } sprintf(key, BOOR_ORDER_STR, slot_id); ret = pal_set_key_value(key, str); error_exit: return ret; } int pal_set_ppin_info(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { char key[MAX_KEY_LEN]; char str[MAX_VALUE_LEN] = {0}; char tstr[8]; int i, comp_code = CC_UNSPECIFIED_ERROR; *res_len = 0; for (i = 0; i < SIZE_CPU_PPIN; i++) { sprintf(tstr, "%02x", req_data[i]); strcat(str, tstr); } sprintf(key, "slot%u_cpu_ppin", slot); if (!pal_set_key_value(key, str)) { comp_code = CC_SUCCESS; } return comp_code; } int pal_get_80port_record(uint8_t slot_id, uint8_t *res_data, size_t max_len, size_t *res_len) { int ret; uint8_t status; uint8_t len; ret = fby35_common_check_slot_id(slot_id); if (ret < 0 ) { ret = PAL_ENOTSUP; goto error_exit; } ret = pal_is_fru_prsnt(slot_id, &status); if ( ret < 0 || status == 0 ) { ret = PAL_ENOTREADY; goto error_exit; } ret = pal_get_server_12v_power(slot_id, &status); if(ret < 0 || SERVER_12V_OFF == status) { ret = PAL_ENOTREADY; goto error_exit; } // Send command to get 80 port record from Bridge IC ret = bic_get_80port_record(slot_id, res_data, &len, NONE_INTF); if (ret == 0) *res_len = (size_t)len; error_exit: return ret; } bool pal_is_fw_update_ongoing(uint8_t fruid) { char key[MAX_KEY_LEN]; char value[MAX_VALUE_LEN] = {0}; int ret; struct timespec ts; sprintf(key, "fru%d_fwupd", fruid); ret = kv_get(key, value, NULL, 0); if (ret < 0) { return false; } clock_gettime(CLOCK_MONOTONIC, &ts); if (strtoul(value, NULL, 10) > ts.tv_sec) return true; return false; } int pal_set_sysfw_ver(uint8_t slot, uint8_t *ver) { int i; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; sprintf(key, SYSFW_VER_STR, (int) slot); for (i = 0; i < SIZE_SYSFW_VER; i++) { sprintf(tstr, "%02x", ver[i]); strcat(str, tstr); } return pal_set_key_value(key, str); } void pal_update_ts_sled() { char key[MAX_KEY_LEN] = {0}; char tstr[MAX_VALUE_LEN] = {0}; struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); sprintf(tstr, "%ld", ts.tv_sec); sprintf(key, "timestamp_sled"); pal_set_key_value(key, tstr); } int pal_get_platform_name(char *name) { strcpy(name, PLATFORM_NAME); return PAL_EOK; } int pal_get_num_slots(uint8_t *num) { *num = MAX_NODES; return 0; } int pal_get_fru_list(char *list) { strcpy(list, pal_fru_list); return PAL_EOK; } int pal_get_dev_list(uint8_t fru, char *list) { strcpy(list, pal_dev_fru_list); return 0; } int pal_get_fru_capability(uint8_t fru, unsigned int *caps) { int ret = 0; switch (fru) { case FRU_ALL: *caps = FRU_CAPABILITY_SENSOR_HISTORY; break; case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_SERVER | FRU_CAPABILITY_POWER_ALL | FRU_CAPABILITY_POWER_12V_ALL | FRU_CAPABILITY_HAS_DEVICE; 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_READ | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_NETWORK_CARD; break; case FRU_NICEXP: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_READ; break; case FRU_BB: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_READ; break; default: ret = -1; break; } return ret; } int pal_get_dev_capability(uint8_t fru, uint8_t dev, unsigned int *caps) { if (fru < FRU_SLOT1 || fru > FRU_SLOT4) return -1; if (dev >= DEV_ID0_1OU && dev <= DEV_ID13_2OU) { *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | (FRU_CAPABILITY_POWER_ALL & (~FRU_CAPABILITY_POWER_RESET)); } else if (dev >= BOARD_1OU && dev <= BOARD_2OU_X16) { *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL; } else { *caps = 0; } return 0; } int pal_get_board_id(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { int ret = CC_UNSPECIFIED_ERROR; uint8_t *data = res_data; uint8_t bmc_location = 0; //the value of bmc_location is board id. *res_len = 0; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); goto error_exit; } *data++ = bmc_location; if ( bmc_location == BB_BMC ) { int dev, retry = 3; uint8_t tbuf[4] = {0}; uint8_t rbuf[4] = {0}; dev = open("/dev/i2c-12", O_RDWR); if ( dev < 0 ) { return -1; } while ((--retry) > 0) { tbuf[0] = 8; ret = i2c_rdwr_msg_transfer(dev, 0x1E, tbuf, 1, rbuf, 1); if (!ret) break; if (retry) msleep(10); } close(dev); if (ret) { *data++ = 0x00; //board rev id } else { *data++ = rbuf[0]; //board rev id } } else { // Config C can not get rev id form NIC EXP CPLD so far *data++ = 0x00; //board rev id } *data++ = slot; //slot id *data++ = 0x00; //slot type. server = 0x00 *res_len = data - res_data; ret = CC_SUCCESS; error_exit: return ret; } int pal_get_slot_index(unsigned char payload_id) { uint8_t bmc_location = 0; uint8_t slot_index = 0; uint8_t tbuf[16] = {0}; uint8_t rbuf[16] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = -1; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return payload_id; } if ( bmc_location == NIC_BMC ) { ret = bic_ipmb_send(payload_id, NETFN_OEM_REQ, 0xF0, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret) { return payload_id; } else { slot_index = rbuf[0]; return slot_index; } } else { return payload_id; } } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { int ret = PAL_EOK; uint8_t bmc_location = 0; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: if ( bmc_location == BB_BMC ) { ret = fby35_common_is_fru_prsnt(fru, status); } else { if ( fru == FRU_SLOT1 ) { *status = 1; } else { *status = 0; } } break; case FRU_BB: *status = 1; break; case FRU_NICEXP: *status = (bmc_location == NIC_BMC)?1:0; break; case FRU_NIC: *status = 1; break; case FRU_BMC: *status = 1; break; default: *status = 0; syslog(LOG_WARNING, "%s() wrong fru id 0x%02x", __func__, fru); ret = PAL_ENOTSUP; } return ret; } int pal_get_fru_id(char *str, uint8_t *fru) { int ret = 0; ret = fby35_common_get_fru_id(str, fru); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() wrong fru %s", __func__, str); return -1; } return ret; } int pal_get_fruid_name(uint8_t fru, char *name) { return fby35_get_fruid_name(fru, name); } int pal_get_fru_name(uint8_t fru, char *name) { int ret = 0; uint8_t bmc_location = 0; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } switch(fru) { case FRU_ALL: sprintf(name, "all"); break; case FRU_SLOT1: sprintf(name, "slot1"); break; case FRU_SLOT2: sprintf(name, "slot2"); break; case FRU_SLOT3: sprintf(name, "slot3"); break; case FRU_SLOT4: sprintf(name, "slot4"); break; case FRU_BMC: sprintf(name, "bmc"); break; case FRU_NIC: sprintf(name, "nic"); break; case FRU_BB: sprintf(name, "bb"); break; case FRU_NICEXP: sprintf(name, "nicexp"); break; case FRU_AGGREGATE: ret = PAL_EOK; //it's the virtual FRU. break; default: syslog(LOG_WARNING, "%s() unknown fruid %d", __func__, fru); ret = PAL_ENOTSUP; } return ret; } int pal_get_fruid_eeprom_path(uint8_t fru, char *path) { int ret = 0; uint8_t bmc_location = 0; uint8_t fru_bus = 0; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } switch(fru) { case FRU_BMC: if ( bmc_location == BB_BMC ) { fru_bus = CLASS1_FRU_BUS; } else { fru_bus = CLASS2_FRU_BUS; } sprintf(path, EEPROM_PATH, fru_bus, BMC_FRU_ADDR); break; case FRU_BB: if ( bmc_location == NIC_BMC ) { //The FRU of baseboard is owned by BIC on class 2. //And so, there is no eeprom path. ret = PAL_ENOTSUP; } else { sprintf(path, EEPROM_PATH, CLASS1_FRU_BUS, BB_FRU_ADDR); } break; case FRU_NICEXP: sprintf(path, EEPROM_PATH, CLASS2_FRU_BUS, NICEXP_FRU_ADDR); break; case FRU_NIC: sprintf(path, EEPROM_PATH, NIC_FRU_BUS, NIC_FRU_ADDR); break; default: ret = PAL_ENOTSUP; } return ret; } int pal_get_dev_fruid_eeprom_path(uint8_t fru, uint8_t dev_id, char *path, uint8_t path_len) { switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: if (dev_id == BOARD_2OU_X8) { snprintf(path, path_len, EEPROM_PATH, FRU_DPV2_X8_BUS(fru), DPV2_FRU_ADDR); } else { return PAL_ENOTSUP; } break; default: return PAL_ENOTSUP; } return PAL_EOK; } int pal_get_fruid_path(uint8_t fru, char *path) { char fname[16] = {0}; int ret = 0; uint8_t bmc_location = 0; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } switch(fru) { case FRU_SLOT1: sprintf(fname, "slot1"); break; case FRU_SLOT2: sprintf(fname, "slot2"); break; case FRU_SLOT3: sprintf(fname, "slot3"); break; case FRU_SLOT4: sprintf(fname, "slot4"); break; case FRU_BMC: sprintf(fname, "bmc"); break; case FRU_NIC: sprintf(fname, "nic"); break; case FRU_BB: sprintf(fname, "bb"); break; case FRU_NICEXP: sprintf(fname, "nicexp"); break; default: syslog(LOG_WARNING, "%s() unknown fruid %d", __func__, fru); ret = PAL_ENOTSUP; } if ( ret != PAL_ENOTSUP ) { sprintf(path, "/tmp/fruid_%s.bin", fname); } return ret; } int pal_fruid_write(uint8_t fru, char *path) { if (fru == FRU_NIC) { syslog(LOG_WARNING, "%s() nic is not supported", __func__); return PAL_ENOTSUP; } else if (fru == FRU_BB) { return bic_write_fruid(FRU_SLOT1, 0, path, BB_BIC_INTF); } return bic_write_fruid(fru, 0, path, NONE_INTF); } int pal_dev_fruid_write(uint8_t fru, uint8_t dev_id, char *path) { int ret = PAL_ENOTSUP; uint8_t config_status = 0; uint8_t bmc_location = 0; uint8_t type_2ou = UNKNOWN_BOARD; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { printf("%s() Couldn't get the location of BMC\n", __func__); return ret; } ret = bic_is_m2_exp_prsnt(fru); if ( ret < 0 ) { printf("%s() Couldn't get the status of 1OU/2OU\n", __func__); return ret; } config_status = (uint8_t) ret; if ( (dev_id == BOARD_1OU) && ((config_status & PRESENT_1OU) == PRESENT_1OU) && (bmc_location != NIC_BMC) ) { // 1U return bic_write_fruid(fru, 0, path, FEXP_BIC_INTF); } else if ( (config_status & PRESENT_2OU) == PRESENT_2OU ) { if ( fby35_common_get_2ou_board_type(fru, &type_2ou) < 0 ) { syslog(LOG_WARNING, "%s() Failed to get 2OU board type\n", __func__); } else if ( dev_id == BOARD_2OU_X16 && ((type_2ou & DPV2_X16_BOARD) == DPV2_X16_BOARD)) { return bic_write_fruid(fru, 1, path, NONE_INTF); } else if ( dev_id == BOARD_2OU ) { return bic_write_fruid(fru, 0, path, REXP_BIC_INTF); } else if ( dev_id >= DEV_ID0_2OU && dev_id <= DEV_ID11_2OU ) { return bic_write_fruid(fru, dev_id - DEV_ID0_2OU + 1, path, REXP_BIC_INTF); } else { printf("Dev%d is not supported on 2OU!\n", dev_id); } } else { printf("%s is not present!\n", (dev_id == BOARD_1OU)?"1OU":"2OU"); return PAL_ENOTSUP; } return ret; } int pal_is_bmc_por(void) { FILE *fp; int por = 0; fp = fopen("/tmp/ast_por", "r"); if (fp != NULL) { if (fscanf(fp, "%d", &por) != 1) { por = 0; } fclose(fp); } return (por)?1:0; } int pal_get_sysfw_ver(uint8_t slot, uint8_t *ver) { int i; int j = 0; int ret = 0; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; sprintf(key, SYSFW_VER_STR, (int) slot); ret = pal_get_key_value(key, str); if (ret) { syslog(LOG_WARNING, "%s() Failed to run pal_get_key_value. key:%s", __func__, key); ret = PAL_ENOTSUP; goto error_exit; } for (i = 0; i < 2*SIZE_SYSFW_VER; i += 2) { sprintf(tstr, "%c%c\n", str[i], str[i+1]); ver[j++] = strtol(tstr, NULL, 16); } error_exit: return ret; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { int ret = PAL_EOK; uint8_t bmc_location = 0; uint8_t status_12v = 1; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: ret = pal_get_server_12v_power(fru, &status_12v); if(ret < 0 || status_12v == SERVER_12V_OFF) { *status = 0; } else { *status = 1; } break; case FRU_BB: *status = 1; break; case FRU_NICEXP: *status = (bmc_location == NIC_BMC)?1:0; break; case FRU_NIC: *status = 1; break; case FRU_BMC: *status = 1; break; default: ret = PAL_ENOTSUP; *status = 0; syslog(LOG_WARNING, "%s() wrong fru id 0x%02x", __func__, fru); break; } return ret; } // GUID for System and Device static int pal_get_guid(uint16_t offset, char *guid) { char path[128] = {0}; int fd; uint8_t bmc_location = 0; uint8_t fru_bus = 0; ssize_t bytes_rd; errno = 0; if ( fby35_common_get_bmc_location(&bmc_location) < 0 ) { syslog(LOG_ERR, "%s() Cannot get the location of BMC", __func__); return -1; } if ( bmc_location == BB_BMC ) { fru_bus = CLASS1_FRU_BUS; } else { fru_bus = CLASS2_FRU_BUS; } snprintf(path, sizeof(path), EEPROM_PATH, fru_bus, BB_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 errno; } fd = open(path, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "%s() unable to open %s: %s", __func__, path, strerror(errno)); return errno; } 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)); } if (fd > 0 ) { close(fd); } return errno; } static int pal_set_guid(uint16_t offset, char *guid) { char path[128] = {0}; int fd; uint8_t bmc_location = 0; uint8_t fru_bus = 0; ssize_t bytes_wr; errno = 0; if ( fby35_common_get_bmc_location(&bmc_location) < 0 ) { syslog(LOG_ERR, "%s() Cannot get the location of BMC", __func__); return -1; } if ( bmc_location == BB_BMC ) { fru_bus = CLASS1_FRU_BUS; } else { fru_bus = CLASS2_FRU_BUS; } snprintf(path, sizeof(path), EEPROM_PATH, fru_bus, BB_FRU_ADDR); // check for file presence if (access(path, F_OK)) { syslog(LOG_ERR, "%s() unable to open %s: %s", __func__, path, strerror(errno)); return errno; } fd = open(path, O_WRONLY); if (fd < 0) { syslog(LOG_ERR, "%s() read from %s failed: %s", __func__, path, strerror(errno)); return errno; } 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)); } close(fd); return errno; } int pal_get_sys_guid(uint8_t fru, char *guid) { if (fru == FRU_SLOT1 || fru == FRU_SLOT2 || fru == FRU_SLOT3 || fru == FRU_SLOT4) { return bic_get_sys_guid(fru, (uint8_t *)guid); } else { return -1; } } int pal_set_sys_guid(uint8_t fru, char *str) { char guid[GUID_SIZE] = {0}; if (fru == FRU_SLOT1 || fru == FRU_SLOT2 || fru == FRU_SLOT3 || fru == FRU_SLOT4) { pal_populate_guid(guid, str); return bic_set_sys_guid(fru, (uint8_t *)guid); } else { return -1; } } 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); if (fru == FRU_BMC) { return pal_set_guid(OFFSET_DEV_GUID, guid); } else { return -1; } } bool pal_is_fw_update_ongoing_system(void) { uint8_t i; for (i = 1; i <= MAX_NUM_FRUS; i++) { if (pal_is_fw_update_ongoing(i) == true) return true; } return false; } /* DPV2 Riser bifurcation table ------------------------------------------------- | P3 P2 P1 P0 | Speed ------------------------------------------------- Retimer 1x16 | 1 0 0 0 | x16 (0x08) Retimer 2x8 | 0 0 1 0 | x8 (0x09) Retimer 4x4 | 0 0 0 0 | x4 (0x0A) Others Cards | 0 1 1 1 | x16 (0x08) */ static int pal_get_dpv2_pcie_config(uint8_t slot_id, uint8_t *pcie_config) { const uint8_t dp_pcie_card_mask = 0x01; uint8_t dp_pcie_conf; if (bic_get_dp_pcie_config(slot_id, &dp_pcie_conf)) { syslog(LOG_ERR, "%s() Cannot get DPV2 PCIE configuration\n", __func__); return -1; } syslog(LOG_INFO, "%s() DPV2 PCIE config: %u\n", __func__, dp_pcie_conf); if (dp_pcie_conf & dp_pcie_card_mask) { // PCIE Card (*pcie_config) = CONFIG_B_DPV2_X16; } else { // Retimer Card switch(dp_pcie_conf) { case DPV2_RETIMER_X16: (*pcie_config) = CONFIG_B_DPV2_X16; break; case DPV2_RETIMER_X8: (*pcie_config) = CONFIG_B_DPV2_X8; break; case DPV2_RETIMER_X4: (*pcie_config) = CONFIG_B_DPV2_X4; break; default: syslog(LOG_ERR, "%s() Unable to get correct DP PCIE configuration\n", __func__); return -1; } } return 0; } 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) { uint8_t pcie_conf = 0xff; uint8_t *data = res_data; int ret = 0, config_status = 0; uint8_t bmc_location = 0; uint8_t type_1ou = 0; uint8_t type_2ou = UNKNOWN_BOARD; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_ERR, "%s() Cannot get the location of BMC", __func__); return -1; } config_status = bic_is_m2_exp_prsnt(slot); if ( config_status < 0 ) { syslog(LOG_ERR, "%s() Cannot get the status of 1OU/2OU", __func__); config_status = 0; } if ( (config_status & PRESENT_2OU) == PRESENT_2OU ) { if ( fby35_common_get_2ou_board_type(slot, &type_2ou) < 0 ) { syslog(LOG_WARNING, "%s() Failed to get 2OU board type", __func__); } } if ( bmc_location == BB_BMC ) { switch (config_status & (PRESENT_2OU|PRESENT_1OU)) { case 0: pcie_conf = CONFIG_A; break; case PRESENT_1OU: if ( bic_get_1ou_type(slot, &type_1ou) != 0 ) { pcie_conf = CONFIG_C; break; } switch (type_1ou) { case EDSFF_1U: case M2_1U: pcie_conf = CONFIG_MFG; break; default: pcie_conf = CONFIG_C; break; } break; case PRESENT_2OU: pcie_conf = CONFIG_B; if ( type_2ou == DPV2_BOARD ) { // To be defined on fby35 pal_get_dpv2_pcie_config(slot, &pcie_conf); } break; case (PRESENT_2OU|PRESENT_1OU): // MFG Test: 1OU=M2, 2OU=DPV2 pcie_conf = CONFIG_MFG; break; } } else { pcie_conf = CONFIG_D; } *data++ = pcie_conf; *res_len = data - res_data; return ret; } int pal_is_slot_server(uint8_t fru) { if ( SERVER_TYPE_DL == fby35_common_get_slot_type(fru) ) { return 1; } return 0; } int pal_is_cmd_valid(uint8_t *data) { uint8_t bus_num = ((data[0] & 0x7E) >> 1); //extend bit[7:1] for bus ID; uint8_t address = data[1]; // protect slot1,2,3,4 BIC if (address == 0x40 && bus_num <= 3) { return -1; } return PAL_EOK; } static int pal_get_custom_event_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) { int ret = PAL_EOK; uint8_t board_type = 0; switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: switch(sensor_num) { case BIC_SENSOR_VRHOT: sprintf(name, "VR_HOT"); break; case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SYSTEM_STATUS"); break; case ME_SENSOR_SMART_CLST: sprintf(name, "SmaRT&CLST"); break; case BIC_SENSOR_PROC_FAIL: sprintf(name, "PROC_FAIL"); break; case BIC_SENSOR_SSD_HOT_PLUG: if (fby35_common_get_2ou_board_type(fru, &board_type) < 0) { syslog(LOG_ERR, "%s() Cannot get board_type", __func__); board_type = M2_BOARD; } if (board_type == E1S_BOARD) { snprintf(name, MAX_SNR_NAME, "E1S_NOT_PRESENT"); } else { snprintf(name, MAX_SNR_NAME, "SSD_HOT_PLUG"); } break; case BB_BIC_SENSOR_POWER_DETECT: sprintf(name, "POWER_DETECT"); break; case BB_BIC_SENSOR_BUTTON_DETECT: sprintf(name, "BUTTON_DETECT"); break; default: sprintf(name, "Unknown"); ret = PAL_ENOTSUP; break; } break; default: sprintf(name, "Unknown"); ret = PAL_ENOTSUP; break; } return ret; } int pal_get_event_sensor_name(uint8_t fru, uint8_t *sel, char *name) { uint8_t snr_type = sel[10]; uint8_t snr_num = sel[11]; switch (snr_type) { // If SNR_TYPE is OS_BOOT, sensor name is OS case OS_BOOT: // OS_BOOT used by OS sprintf(name, "OS"); return PAL_EOK; default: if ( pal_get_custom_event_sensor_name(fru, snr_num, name) == PAL_EOK ) { return PAL_EOK; } } // Otherwise, translate it based on snr_num return pal_get_x86_event_sensor_name(fru, snr_num, name); } static int pal_parse_proc_fail(uint8_t fru, uint8_t *event_data, char *error_log) { enum { FRB3 = 0x04, }; switch(event_data[0]) { case FRB3: strcat(error_log, "FRB3/Processor Startup/Initialization Failure, "); break; default: strcat(error_log, "Undefined data, "); break; } return PAL_EOK; } static int pal_parse_smart_clst_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { /* 00h - transition to OK 01h - transition to noncritical from OK 02h - transition to critical from less severe 03h - transition to unrecoverable from less severe 04h - transition to noncritical from more severe 05h - transition to critical from unrecoverable 06h - transition to unrecoverable 07h - monitor 08h - informational*/ TRANS_TO_OK = 0x00, TRANS_TO_NON_CRIT_FROM_OK = 0x01, TRANS_TO_CRIT_FROM_LESS_SEVERE = 0x02, TRANS_TO_UNR_FROM_LESS_SEVERE = 0x03, TRANS_TO_NCR_FROM_MORE_SEVERE = 0x04, TRANS_TO_CRIT_FROM_UNR = 0x05, TRANS_TO_UNR = 0x06, MONITOR = 0x07, INFORMATIONAL = 0x08, /* 0h – State Deasserted (throttling released) 1h – State Asserted (throttling enforced) */ THROTTLING_RELEASED = 0x00, THROTTLING_ENFORCED = 0x01, }; uint8_t code = (event_data[0] & 0x1); uint8_t severity = (event_data[1] >> 4) & 0x0f; const uint8_t ME_FW_ASSERTION[3] = {0x61, 0x2F, 0x00}; //612F00h is a special case. it just displays the ME FW assertion. //handle the special case if ( memcmp(event_data, (uint8_t *)&ME_FW_ASSERTION, 3) == 0 ) { strcat(error_log, "Management Engine FW"); return PAL_EOK; } switch(code) { case THROTTLING_RELEASED: strcat(error_log, "Throttling released, "); break; case THROTTLING_ENFORCED: strcat(error_log, "Throttling enforced, "); break; default: strcat(error_log, "Undefined data, "); break; } switch (severity) { case TRANS_TO_OK: strcat(error_log, "Transition to OK"); break; case TRANS_TO_NON_CRIT_FROM_OK: strcat(error_log, "Transition to noncritical from OK"); break; case TRANS_TO_CRIT_FROM_LESS_SEVERE: strcat(error_log, "Transition to critical from less severe"); break; case TRANS_TO_UNR_FROM_LESS_SEVERE: strcat(error_log, "Transition to unrecoverable from less severe"); break; case TRANS_TO_NCR_FROM_MORE_SEVERE: strcat(error_log, "Transition to noncritical from more severe"); break; case TRANS_TO_CRIT_FROM_UNR: strcat(error_log, "Transition to critical from unrecoverable"); break; case TRANS_TO_UNR: strcat(error_log, "Transition to unrecoverable"); break; case MONITOR: strcat(error_log, "Monitor"); break; case INFORMATIONAL: strcat(error_log, "Informational"); break; default: strcat(error_log, "Undefined severity"); break; } return PAL_EOK; } static int pal_parse_vr_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { SOC_VRHOT = 0x00, }; uint8_t event = event_data[0]; switch (event) { case SOC_VRHOT: strcat(error_log, "SOC VR HOT warning"); break; default: strcat(error_log, "Undefined VR event"); break; } return PAL_EOK; } static void pal_sel_root_port_mapping_tbl(uint8_t fru, uint8_t *bmc_location, MAPTOSTRING **tbl, uint8_t *cnt) { uint8_t board_1u = M2_BOARD; uint8_t board_2u = M2_BOARD; uint8_t config_status = CONFIG_UNKNOWN; int ret = 0; do { ret = fby35_common_get_bmc_location(bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC\n", __func__); break; } ret = bic_is_m2_exp_prsnt(fru); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the status of 1OU/2OU", __func__); break; } else config_status = (uint8_t)ret; // For Config C and D, there are EDSFF_1U, E1S_BOARD and GPv3 architecture // BMC should select the corresponding table. // For Config B and A, root_port_mapping should be selected. // only check it when 1OU is present if ( *bmc_location != NIC_BMC && ((config_status & PRESENT_1OU) == PRESENT_1OU) ) { ret = bic_get_1ou_type(fru, &board_1u); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 1ou_board_type\n", __func__); break; } } // only check it when 2OU is present if ( ((config_status & PRESENT_2OU) == PRESENT_2OU) ) { ret = fby35_common_get_2ou_board_type(fru, &board_2u); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 2ou_board_type\n", __func__); break; } } } while(0); if ( ret < 0 ) { syslog(LOG_ERR, "%s() Use the default root_port_mapping\n", __func__); board_1u = M2_BOARD; //make sure the default is used board_2u = M2_BOARD; } if ( board_1u == EDSFF_1U || board_2u == E1S_BOARD ) { // case 1/2OU E1S *tbl = root_port_mapping_e1s; *cnt = sizeof(root_port_mapping_e1s)/sizeof(MAPTOSTRING); } else if ( (board_2u == GPV3_MCHP_BOARD || board_2u == GPV3_BRCM_BOARD) && \ (*bmc_location == NIC_BMC) ) { *tbl = root_port_mapping_gpv3; *cnt = sizeof(root_port_mapping_gpv3)/sizeof(MAPTOSTRING); } else { *tbl = root_port_common_mapping; *cnt = sizeof(root_port_common_mapping)/sizeof(MAPTOSTRING); } return; } static void pal_search_pcie_err(uint8_t err1_id, uint8_t err2_id, char *err1_desc, char *err2_desc) { int i; int size = (sizeof(pcie_err_tab)/sizeof(PCIE_ERR_DECODE)); for ( i = 0; i < size; i++ ) { if ( err2_id == pcie_err_tab[i].err_id ) { snprintf(err2_desc, ERR_DESC_LEN, "(%s)", pcie_err_tab[i].err_descr); continue; } else if ( err1_id == pcie_err_tab[i].err_id ) { snprintf(err1_desc, ERR_DESC_LEN, "(%s)", pcie_err_tab[i].err_descr); continue; } if ( err1_desc[0] && err2_desc[0] ) { break; } } return; } static bool pal_search_pcie_dev(MAPTOSTRING *tbl, int size, uint8_t bmc_location, uint8_t dev, uint8_t bus, char **sil, char **location) { int i = 0; for ( i = 0; i < size; i++ ) { // check bus and dev are match if ( (bus == tbl[i].bus_value) && \ (dev == tbl[i].dev_value) ) { *location = tbl[i].location; // 1OU is not expected on class 2, skip if ( !strcmp(*location, "1OU") && bmc_location == NIC_BMC ) { continue; } *sil = tbl[i].silk_screen; return true; } } return false; } static void pal_get_pcie_err_string(uint8_t fru, uint8_t *pdata, char **sil, char **location, char *err1_str, char *err2_str) { uint8_t bmc_location = 0; uint8_t dev = pdata[0] >> 3; uint8_t bus = pdata[1]; uint8_t err1_id = pdata[5]; uint8_t err2_id = pdata[4]; uint8_t size = 0; MAPTOSTRING *mapping_table = NULL; // get the table first pal_sel_root_port_mapping_tbl(fru, &bmc_location, &mapping_table, &size); // search for the device table first if ( pal_search_pcie_dev(mapping_table, size, bmc_location, dev, bus, sil, location) == false ) { // if dev is not found in the device table, search for the common table size = sizeof(root_port_common_mapping)/sizeof(MAPTOSTRING); pal_search_pcie_dev(root_port_common_mapping, size, bmc_location, dev, bus, sil, location); } // parse err pal_search_pcie_err(err1_id, err2_id, err1_str, err2_str); return; } static void pal_get_m2vpp_str_name(uint8_t fru, uint8_t comp, uint8_t root_port, char *error_log) { int i = 0; uint8_t size = 0; uint8_t bmc_location = 0; MAPTOSTRING *mapping_table = NULL; // select root port mapping tbl first pal_sel_root_port_mapping_tbl(fru, &bmc_location, &mapping_table, &size); for ( i = 0 ; i < size; i++ ) { if ( mapping_table[i].root_port == root_port ) { char *silk_screen = mapping_table[i].silk_screen; char *location = mapping_table[i].location; snprintf(error_log, 256, "%s/%s ", location, silk_screen); return; } } if ( i == size ) { snprintf(error_log, 256, "Undefined M2 RootPort %X ", root_port); } return; } static const char* pal_get_board_name(uint8_t comp) { const char *comp_str[5] = {"ServerBoard", "1OU", "2OU", "SPE", "GPv3"}; const uint8_t comp_size = ARRAY_SIZE(comp_str); if ( comp < comp_size ) { return comp_str[comp]; } return "Undefined board"; } static void pal_get_m2_str_name(uint8_t comp, uint8_t device_num, char *error_log) { snprintf(error_log, 256, "%s/Num %d ", pal_get_board_name(comp), device_num); return; } static void pal_get_2ou_vr_str_name(uint8_t comp, uint8_t vr_num, char *error_log) { const char *vr_list_str[5] = {"P3V3_STBY1", "P3V3_STBY2", "P3V3_STBY3", "P1V8", "PESW VR"}; const uint8_t vr_list_size = ARRAY_SIZE(vr_list_str); snprintf(error_log, 256, "%s/%s ", pal_get_board_name(comp), (vr_num < vr_list_size)?vr_list_str[vr_num]:"Undefined VR"); return; } static int pal_parse_sys_sts_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { SYS_SOC_THERM_TRIP = 0x00, SYS_THROTTLE = 0x02, SYS_PCH_THERM_TRIP = 0x03, SYS_HSC_THROTTLE = 0x05, SYS_OC_DETECT = 0x06, SYS_MB_THROTTLE = 0x07, SYS_HSC_FAULT = 0x08, SYS_RSVD = 0x09, SYS_WDT_TIMEOUT = 0x0A, SYS_M2_VPP = 0x0B, SYS_M2_PGOOD = 0x0C, SYS_VCCIO_FAULT = 0x0D, SYS_SMI_STUCK_LOW = 0x0E, SYS_OV_DETECT = 0x0F, SYS_FM_THROTTLE = 0x10, SYS_CPU_MEM_THERM_TRIP = 0x11, SYS_PESW_ERR = 0x12, SYS_2OU_VR_FAULT = 0x13, SYS_FAN_SERVICE = 0x14, SYS_BB_FW_EVENT = 0x15, E1S_1OU_HSC_PWR_ALERT = 0x82, }; uint8_t event = event_data[0]; char log_msg[MAX_ERR_LOG_SIZE] = {0}; char fan_mode_str[FAN_MODE_STR_LEN] = {0}; char component_str[MAX_COMPONENT_LEN] = {0}; switch (event) { case SYS_SOC_THERM_TRIP: strcat(error_log, "SOC thermal trip"); break; case SYS_THROTTLE: strcat(error_log, "SYS_Throttle throttle"); break; case SYS_PCH_THERM_TRIP: strcat(error_log, "PCH thermal trip"); break; case SYS_FM_THROTTLE: strcat(error_log, "FM_Throttle throttle"); break; case SYS_HSC_THROTTLE: strcat(error_log, "HSC_Throttle throttle"); break; case SYS_OC_DETECT: strcat(error_log, "HSC_OC warning"); break; case SYS_MB_THROTTLE: strcat(error_log, "MB_Throttle throttle"); break; case SYS_HSC_FAULT: strcat(error_log, "HSC fault"); break; case SYS_WDT_TIMEOUT: strcat(error_log, "VR Watchdog timeout"); break; case SYS_M2_VPP: pal_get_m2vpp_str_name(fru, event_data[1], event_data[2], error_log); strcat(error_log, "VPP power control"); break; case SYS_M2_PGOOD: pal_get_m2_str_name(event_data[1], event_data[2], error_log); strcat(error_log, "Power Good fault"); 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; case SYS_CPU_MEM_THERM_TRIP: strcat(error_log, "CPU/Memory thermal trip"); break; case SYS_PESW_ERR: strcat(error_log, "2OU PESW error"); break; case SYS_2OU_VR_FAULT: pal_get_2ou_vr_str_name(event_data[1], event_data[2], error_log); strcat(error_log, "2OU VR fault"); break; case SYS_FAN_SERVICE: if (event_data[2] == FAN_MANUAL_MODE) { snprintf(fan_mode_str, sizeof(fan_mode_str), "manual"); } else { snprintf(fan_mode_str, sizeof(fan_mode_str), "auto"); } if ((event_data[1] == FRU_SLOT1) || (event_data[1] == FRU_SLOT3)) { snprintf(log_msg, sizeof(log_msg), "Fan mode changed to %s mode by slot%d", fan_mode_str, event_data[1]); } else { snprintf(log_msg, sizeof(log_msg), "Fan mode changed to %s mode by unknown slot", fan_mode_str); } strcat(error_log, log_msg); break; case SYS_BB_FW_EVENT: if (event_data[1] == FW_BB_BIC) { strncpy(component_str, "BIC", sizeof(component_str)); } else if (event_data[1] == FW_BB_CPLD) { strncpy(component_str, "CPLD", sizeof(component_str)); } else { strncpy(component_str, "unknown component", sizeof(component_str)); } snprintf(log_msg, sizeof(log_msg), "Baseboard firmware %s update is ongoing", component_str); strcat(error_log, log_msg); break; case E1S_1OU_HSC_PWR_ALERT: strcat(error_log, "E1S 1OU HSC power alert"); break; default: strcat(error_log, "Undefined system event"); break; } return PAL_EOK; } static int pal_parse_ssd_hot_plug_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { SSD0 = 0x00, SSD1 = 0x01, SSD2 = 0x02, SSD3 = 0x03, SSD4 = 0x04, SSD5 = 0x05, }; uint8_t event = event_data[0]; switch (event) { case SSD0: strcat(error_log, "SSD0"); break; case SSD1: strcat(error_log, "SSD1"); break; case SSD2: strcat(error_log, "SSD2"); break; case SSD3: strcat(error_log, "SSD3"); break; case SSD4: strcat(error_log, "SSD4"); break; case SSD5: strcat(error_log, "SSD5"); break; default: strcat(error_log, "Undefined hot plug event"); break; } return PAL_EOK; } static int pal_parse_pwr_detect_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { SLED_CYCLE = 0x00, SLOT = 0x01, }; enum { CYCLE_12V = 0x00, ON_12V = 0x01, OFF_12V = 0x02, }; switch (event_data[0]) { case SLED_CYCLE: pal_set_nic_perst(fru, NIC_PE_RST_LOW); strcat(error_log, "SLED_CYCLE by BB BIC"); break; case SLOT: strcat(error_log, "SERVER "); switch (event_data[1]) { case CYCLE_12V: strcat(error_log, "12V CYCLE by BB BIC"); break; case ON_12V: strcat(error_log, "12V ON by BB BIC"); break; case OFF_12V: strcat(error_log, "12V OFF by BB BIC"); break; default: strcat(error_log, "Undefined Baseboard BIC event"); break; } break; default: strcat(error_log, "Undefined Baseboard BIC event"); break; } return PAL_EOK; } static int pal_parse_button_detect_event(uint8_t fru, uint8_t *event_data, char *error_log) { enum { ADAPTER_BUTTON_BMC_CO_N_R = 0x01, AC_ON_OFF_BTN_SLOT1_N = 0x02, AC_ON_OFF_BTN_SLOT3_N = 0x03, }; switch (event_data[0]) { case ADAPTER_BUTTON_BMC_CO_N_R: strcat(error_log, "ADAPTER_BUTTON_BMC_CO_N_R"); break; case AC_ON_OFF_BTN_SLOT1_N: strcat(error_log, "AC_ON_OFF_BTN_SLOT1_N"); break; case AC_ON_OFF_BTN_SLOT3_N: strcat(error_log, "AC_ON_OFF_BTN_SLOT3_N"); break; default: strcat(error_log, "Undefined Baseboard BIC event"); break; } return PAL_EOK; } int pal_parse_sel(uint8_t fru, uint8_t *sel, char *error_log) { enum { EVENT_TYPE_NOTIF = 0x77, /*IPMI-Table 42-1, Event/Reading Type Code Ranges - OEM specific*/ }; uint8_t snr_num = sel[11]; uint8_t event_dir = sel[12] & 0x80; uint8_t event_type = sel[12] & 0x7f; uint8_t *event_data = &sel[13]; bool unknown_snr = false; error_log[0] = '\0'; switch (snr_num) { case BIC_SENSOR_VRHOT: pal_parse_vr_event(fru, event_data, error_log); break; case BIC_SENSOR_SYSTEM_STATUS: pal_parse_sys_sts_event(fru, event_data, error_log); break; case ME_SENSOR_SMART_CLST: pal_parse_smart_clst_event(fru, event_data, error_log); break; case BIC_SENSOR_PROC_FAIL: pal_parse_proc_fail(fru, event_data, error_log); break; case BIC_SENSOR_SSD_HOT_PLUG: pal_parse_ssd_hot_plug_event(fru, event_data, error_log); break; case BB_BIC_SENSOR_POWER_DETECT: pal_parse_pwr_detect_event(fru, event_data, error_log); break; case BB_BIC_SENSOR_BUTTON_DETECT: pal_parse_button_detect_event(fru, event_data, error_log); break; default: unknown_snr = true; break; } if ( unknown_snr == false ) { if ( event_type == EVENT_TYPE_NOTIF ) { strcat(error_log, " Triggered"); } else { strcat(error_log, ((event_dir & 0x80) == 0)?" Assertion":" Deassertion"); } } else { pal_parse_sel_helper(fru, sel, error_log); } return PAL_EOK; } int pal_parse_oem_unified_sel(uint8_t fru, uint8_t *sel, char *error_log) { uint8_t general_info = (uint8_t) sel[3]; uint8_t error_type = general_info & 0x0f; uint8_t plat = 0; char temp_log[128] = {0}; error_log[0] = '\0'; char *sil = "NA"; char *location = "NA"; char err1_desc[ERR_DESC_LEN] = {0}, err2_desc[ERR_DESC_LEN] = {0}; switch (error_type) { case UNIFIED_PCIE_ERR: plat = (general_info & 0x10) >> 4; if (plat == 0) { //x86 pal_get_pcie_err_string(fru, &sel[10], &sil, &location, err1_desc, err2_desc); snprintf(error_log, ERROR_LOG_LEN, "GeneralInfo: x86/PCIeErr(0x%02X), Bus %02X/Dev %02X/Fun %02X, %s/%s," "TotalErrID1Cnt: 0x%04X, ErrID2: 0x%02X%s, ErrID1: 0x%02X%s", general_info, sel[11], sel[10] >> 3, sel[10] & 0x7, location, sil, ((sel[13]<<8)|sel[12]), sel[14], err2_desc, sel[15], err1_desc); } 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, ((sel[9]<<8)|sel[8]),sel[11], sel[10] >> 3, sel[10] & 0x7, ((sel[13]<<8)|sel[12]), sel[14], sel[15]); } sprintf(temp_log, "PCIe Error ,FRU:%u", 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_oem_unified_sel_handler(uint8_t fru, uint8_t general_info, uint8_t *sel) { char key[MAX_KEY_LEN] = {0}; snprintf(key, MAX_KEY_LEN, SEL_ERROR_STR, fru); sel_error_record[fru-1]++; return pal_set_key_value(key, "0"); } void pal_log_clear(char *fru) { char key[MAX_KEY_LEN] = {0}; uint8_t fru_cnt = 0; int i = 0; if ( strncmp(fru, "slot", 4) == 0 ) { fru_cnt = fru[4] - 0x30; i = fru_cnt; } else if ( strcmp(fru, "all") == 0 ) { fru_cnt = 4; i = 1; } for ( ; ((i <= fru_cnt) && (i != 0)); i++ ) { snprintf(key, MAX_KEY_LEN, SEL_ERROR_STR, i); pal_set_key_value(key, "1"); snprintf(key, MAX_KEY_LEN, SNR_HEALTH_STR, i); pal_set_key_value(key, "1"); sel_error_record[i-1] = 0; } } int pal_is_debug_card_prsnt(uint8_t *status) { int ret = -1; gpio_value_t value; gpio_desc_t *gpio = gpio_open_by_shadow(GPIO_OCP_DEBUG_BMC_PRSNT_N); if (!gpio) { return -1; } ret = gpio_get_value(gpio, &value); gpio_close(gpio); if (ret != 0) { return -1; } if (value == 0x0) { *status = 1; } else { *status = 0; } return 0; } int pal_set_uart_IO_sts(uint8_t slot_id, uint8_t io_sts) { const uint8_t UART_POS_BMC = 0x00; const uint8_t MAX_RETRY = 3; int i2cfd = -1; int ret = PAL_EOK; int retry = MAX_RETRY; int st_idx = slot_id, end_idx = slot_id; uint8_t tbuf[2] = {0x00}; uint8_t tlen = 2; i2cfd = i2c_cdev_slave_open(BB_CPLD_BUS, CPLD_ADDRESS >> 1, I2C_SLAVE_FORCE_CLAIM); if ( i2cfd < 0 ) { syslog(LOG_WARNING, "Failed to open bus 12\n"); return i2cfd; } // adjust the st_idx and end_idx, we need to reset all reg values // when uart_pos is at BMC position if ( slot_id == UART_POS_BMC ) { st_idx = FRU_SLOT1; end_idx = FRU_SLOT4; } do { tbuf[0] = BB_CPLD_IO_BASE_OFFSET + st_idx; //get the correspoding reg tbuf[1] = io_sts; // data to be written ret = i2c_rdwr_msg_transfer(i2cfd, CPLD_ADDRESS, tbuf, tlen, NULL, 0); if ( ret < 0 ) { retry--; } else { st_idx++; //move on retry = MAX_RETRY; //reset it } } while ( retry > 0 && st_idx <= end_idx ); if ( retry == 0 ) { syslog(LOG_WARNING, "Failed to update IO sts after performed 3 time attempts. reg:%02X, data: %02X\n", tbuf[0], tbuf[1]); } if ( i2cfd > 0 ) close(i2cfd); return ret; } int pal_get_uart_select_from_kv(uint8_t *uart_select) { char value[MAX_VALUE_LEN] = {0}; uint8_t loc; int ret = -1; ret = kv_get("debug_card_uart_select", value, NULL, 0); if (!ret) { loc = atoi(value); *uart_select = loc; } return ret; } int pal_get_uart_select_from_cpld(uint8_t *uart_select) { int fd = 0; int retry = 3; int ret = -1; uint8_t tbuf[1] = {0x00}; uint8_t rbuf[1] = {0x00}; uint8_t tlen = 0; uint8_t rlen = 0; fd = open("/dev/i2c-12", O_RDWR); if (fd < 0) { syslog(LOG_WARNING, "Failed to open bus 12"); return -1; } tbuf[0] = DEBUG_CARD_UART_MUX; tlen = 1; rlen = 1; while (ret < 0 && retry-- > 0) { ret = i2c_rdwr_msg_transfer(fd, CPLD_ADDRESS, tbuf, tlen, rbuf, rlen); } if (fd > 0) { close(fd); } if (ret < 0) { return -1; } *uart_select = rbuf[0]; return 0; } int pal_post_display(uint8_t uart_select, uint8_t postcode) { int fd = 0; int retry = 3; int ret = -1; uint8_t tlen = 0; uint8_t rlen = 0; uint8_t tbuf[2] = {0x00}; uint8_t rbuf[1] = {0x00}; uint8_t offset = 0; fd = open("/dev/i2c-12", O_RDWR); if (fd < 0) { syslog(LOG_WARNING, "Failed to open bus 12"); return -1; } switch (uart_select) { case 1: offset = SLOT1_POSTCODE_OFFSET; break; case 2: offset = SLOT2_POSTCODE_OFFSET; break; case 3: offset = SLOT3_POSTCODE_OFFSET; break; case 4: offset = SLOT4_POSTCODE_OFFSET; break; } tbuf[0] = offset; tbuf[1] = postcode; tlen = 2; rlen = 0; while (ret < 0 && retry-- > 0) { ret = i2c_rdwr_msg_transfer(fd, CPLD_ADDRESS, tbuf, tlen, rbuf, rlen); } if (fd > 0) { close(fd); } return ret; } // Handle the received post code, display it on debug card int pal_post_handle(uint8_t slot, uint8_t postcode) { uint8_t prsnt = 0; uint8_t uart_select = 0; int ret = -1; // Check for debug card presence ret = pal_is_debug_card_prsnt(&prsnt); if (ret) { return ret; } // No debug card present, return if (prsnt == 0) { return 0; } // Get the UART SELECT from kv, avoid large access CPLD in a short time ret = pal_get_uart_select_from_kv(&uart_select); if (ret) { return ret; } // If the give server is not selected, return if (uart_select != slot) { return 0; } // Display the post code in the debug card ret = pal_post_display(uart_select, postcode); return ret; } static int pal_store_crashdump(uint8_t fru, bool ierr) { uint8_t status; if (!pal_get_server_power(fru, &status) && !status) { syslog(LOG_WARNING, "%s() fru %u is OFF", __func__, fru); return PAL_ENOTSUP; } return fby35_common_crashdump(fru, ierr, false); } static int pal_bic_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) { int ret = PAL_EOK; int i = 0; bool is_cri_sel = false; switch (snr_num) { case CATERR_B: is_cri_sel = true; pal_store_crashdump(fru, (event_data[3] == 0x00)); // 00h:IERR, 0Bh:MCERR if (event_data[3] == 0x00) { // IERR fby35_common_fscd_ctrl((event_data[2] == SEL_ASSERT) ? FAN_MANUAL_MODE : FAN_AUTO_MODE); if (event_data[2] == SEL_ASSERT) { for (i = 0; i < pal_pwm_cnt; i++) { pal_set_fan_speed(i, 100); } } } break; case CPU_DIMM_HOT: case PWR_ERR: is_cri_sel = true; break; case BIC_SENSOR_SYSTEM_STATUS: switch(event_data[3]) { case 0x14: //SYS_FAN_SERVICE case 0x11: //SYS_SLOT_PRSNT case 0x0B: //SYS_M2_VPP case 0x07: //SYS_FW_TRIGGER break; default: is_cri_sel = true; break; } if (event_data[3] == 0x11) { // when another blade insert/remove, start/stop fscd ret = system("/etc/init.d/setup-fan.sh reload &"); if (ret != 0) { syslog(LOG_WARNING, "%s() can not reload setup-fan.sh", __func__); return -1; } } else if (event_data[3] == SYS_FAN_EVENT) { if (pal_is_fw_update_ongoing(fru) == true) { return PAL_EOK; } // start/stop fscd according fan mode change fby35_common_fscd_ctrl(event_data[DATA_INDEX_2]); } else if (event_data[3] == SYS_BB_FW_UPDATE) { if (event_data[2] == SEL_ASSERT) { if (event_data[4] == FW_BB_BIC) { kv_set("bb_fw_update", "bic", 0, 0); } else if (event_data[4] == FW_BB_CPLD) { kv_set("bb_fw_update", "cpld", 0, 0); } else { kv_set("bb_fw_update", "unknown", 0, 0); } } else if (event_data[2] == SEL_DEASSERT) { // if BB fw update complete, delete the key kv_del("bb_fw_update", 0); } return PAL_EOK; } break; } if ( is_cri_sel == true ) { char key[MAX_KEY_LEN] = {0}; if ( (event_data[2] & 0x80) == 0 ) sel_error_record[fru-1]++; else sel_error_record[fru-1]--; snprintf(key, MAX_KEY_LEN, SEL_ERROR_STR, fru); pal_set_key_value(key, (sel_error_record[fru-1] > 0)?"0":"1"); // 0: Assertion, 1: Deassertion } return PAL_EOK; } int pal_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) { int ret = PAL_EOK; switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: ret = pal_bic_sel_handler(fru, snr_num, event_data); break; default: ret = PAL_ENOTSUP; break; } return ret; } int pal_get_dev_id(char *str, uint8_t *dev) { return fby35_common_dev_id(str, dev); } int pal_get_num_devs(uint8_t slot, uint8_t *num) { if (fby35_common_check_slot_id(slot) == 0) { *num = MAX_NUM_DEVS - 1; } return 0; } int pal_get_dev_fruid_name(uint8_t fru, uint8_t dev, char *name) { char temp[64] = {0}; char dev_name[32] = {0}; int ret = pal_get_fruid_name(fru, name); if (ret < 0) { return ret; } switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: fby35_common_dev_name(dev, dev_name); break; default: return -1; } snprintf(temp, sizeof(temp), "%s %s", name, dev_name); strcpy(name, temp); return 0; } int pal_get_dev_name(uint8_t fru, uint8_t dev, char *name) { switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: fby35_common_dev_name(dev, name); break; default: return -1; } return 0; } int pal_get_dev_fruid_path(uint8_t fru, uint8_t dev_id, char *path) { return fby35_get_fruid_path(fru, dev_id, path); } int pal_handle_dcmi(uint8_t fru, uint8_t *request, uint8_t req_len, uint8_t *response, uint8_t *rlen) { int ret; uint8_t rbuf[256] = {0x00}, len = 0; ret = bic_me_xmit(fru, request, req_len, rbuf, &len); if (ret || (len < 1)) { return -1; } if (rbuf[0] != 0x00) { return -1; } *rlen = len - 1; memcpy(response, &rbuf[1], *rlen); return 0; } int pal_set_fan_ctrl (char *ctrl_opt) { FILE* fp = NULL; uint8_t bmc_location = 0; uint8_t ctrl_mode, status; int ret = 0; char cmd[64] = {0}; char buf[32]; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); return ret; } if (!strcmp(ctrl_opt, ENABLE_STR)) { ctrl_mode = AUTO_MODE; snprintf(cmd, sizeof(cmd), "sv start fscd > /dev/null 2>&1"); } else if (!strcmp(ctrl_opt, DISABLE_STR)) { ctrl_mode = MANUAL_MODE; snprintf(cmd, sizeof(cmd), "sv force-stop fscd > /dev/null 2>&1"); } else if (!strcmp(ctrl_opt, STATUS_STR)) { ctrl_mode = GET_FAN_MODE; } else { return -1; } // notify baseboard bic and another slot BMC (Class 2) if (bmc_location == NIC_BMC) { // get/set fan status if ( bic_set_fan_auto_mode(ctrl_mode, &status) < 0 ) { syslog(LOG_WARNING, "%s() Failed to call bic_set_fan_auto_mode. ctrl_mode=%02X", __func__, ctrl_mode); return -1; } // notify the other BMC except for getting fan mode if ( ctrl_mode != GET_FAN_MODE && (bic_notify_fan_mode(ctrl_mode) < 0) ) { syslog(LOG_WARNING, "%s() Failed to call bic_notify_fan_mode. ctrl_mode=%02X", __func__, ctrl_mode); return -1; } } if (ctrl_mode == GET_FAN_MODE) { if (bmc_location == NIC_BMC) { if (status == AUTO_MODE) { printf("Auto Mode: Normal\n"); } else if (status == MANUAL_MODE) { printf("Auto Mode: Manual\n"); } else { printf("Auto Mode: Unknown\n"); } } else { snprintf(cmd, sizeof(cmd), "fan-util --get | grep \"Fan Mode:\" | cut -d: -f2-"); if((fp = popen(cmd, "r")) == NULL) { printf("Auto Mode: Unknown\n"); return -1; } if(fgets(buf, sizeof(buf), fp) != NULL) { printf("Auto Mode:%s",buf); } pclose(fp); } } else { // AUTO_MODE or MANUAL_MODE if(ctrl_mode == AUTO_MODE) { if (system(cmd) != 0) return -1; } else if (ctrl_mode == MANUAL_MODE){ if (system(cmd) != 0) { // Although sv force-stop sends kill (-9) signal after timeout, // it still returns an error code. // we will check status here to ensure that fscd has stopped completely. syslog(LOG_WARNING, "%s() force-stop timeout", __func__); snprintf(cmd, sizeof(cmd), "sv status fscd 2>/dev/null | cut -d: -f1"); if((fp = popen(cmd, "r")) == NULL) { syslog(LOG_WARNING, "%s() popen failed, cmd: %s", __func__, cmd); ret = -1; } else if(fgets(buf, sizeof(buf), fp) == NULL) { syslog(LOG_WARNING, "%s() read popen failed, cmd: %s", __func__, cmd); ret = -1; } else if(strncmp(buf, "down", 4) != 0) { syslog(LOG_WARNING, "%s() failed to terminate fscd", __func__); ret = -1; } if(fp != NULL) pclose(fp); if(ret != 0) return ret; } } } return ret; } // OEM Command "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; int completion_code = CC_UNSPECIFIED_ERROR; uint8_t netfn, cmd, select; uint8_t tlen, rlen; uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t status; NCSI_NL_MSG_T *msg = NULL; NCSI_NL_RSP_T *rsp = NULL; uint8_t channel = 0; uint8_t netdev = 0; uint8_t netenable = 0; char sendcmd[128] = {0}; int i; *res_len = 0; if (slot < FRU_SLOT1 || slot > FRU_SLOT4) { return CC_PARAM_OUT_OF_RANGE; } ret = pal_is_fru_prsnt(slot, &status); if (ret < 0) { return -1; } if (status == 0) { return CC_UNSPECIFIED_ERROR; } ret = pal_get_server_12v_power(slot, &status); if(ret < 0 || status == SERVER_12V_OFF) { return CC_NOT_SUPP_IN_CURR_STATE; } if(!pal_is_slot_server(slot)) { return CC_UNSPECIFIED_ERROR; } select = req_data[0]; switch (select) { case BYPASS_BIC: if (req_len < 6) { completion_code = CC_INVALID_LENGTH; break; } tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) netfn = req_data[1]; cmd = req_data[2]; // Bypass command to Bridge IC if (tlen != 0) { ret = bic_ipmb_wrapper(slot, netfn, cmd, &req_data[3], tlen, res_data, res_len); } else { ret = bic_ipmb_wrapper(slot, netfn, cmd, NULL, 0, res_data, res_len); } if (0 == ret) { completion_code = CC_SUCCESS; } break; case BYPASS_ME: if (req_len < 6) { completion_code = CC_INVALID_LENGTH; break; } tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) netfn = req_data[1]; cmd = req_data[2]; tlen += 2; memcpy(tbuf, &req_data[1], tlen); tbuf[0] = tbuf[0] << 2; // Bypass command to ME ret = bic_me_xmit(slot, tbuf, tlen, rbuf, &rlen); if (0 == ret) { completion_code = rbuf[0]; memcpy(&res_data[0], &rbuf[1], (rlen - 1)); *res_len = rlen - 1; } break; case BYPASS_NCSI: if (req_len < 7) { completion_code = CC_INVALID_LENGTH; break; } tlen = req_len - 7; // payload_id, netfn, cmd, data[0] (select), netdev, channel, cmd netdev = req_data[1]; channel = req_data[2]; cmd = req_data[3]; msg = calloc(1, sizeof(NCSI_NL_MSG_T)); if (!msg) { syslog(LOG_ERR, "%s Error: failed msg buffer allocation", __func__); break; } memset(msg, 0, sizeof(NCSI_NL_MSG_T)); sprintf(msg->dev_name, "eth%d", netdev); msg->channel_id = channel; msg->cmd = cmd; msg->payload_length = tlen; for (i=0; i<msg->payload_length; i++) { msg->msg_payload[i] = req_data[4+i]; } //send_nl_msg_libnl rsp = send_nl_msg_libnl(msg); if (rsp) { memcpy(&res_data[0], &rsp->msg_payload[0], rsp->hdr.payload_length); *res_len = rsp->hdr.payload_length; completion_code = CC_SUCCESS; } else { completion_code = CC_UNSPECIFIED_ERROR; } free(msg); if (rsp) free(rsp); break; case BYPASS_NETWORK: if (req_len != 6) { completion_code = CC_INVALID_LENGTH; break; } tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), netdev, netenable netdev = req_data[1]; netenable = req_data[2]; if (netenable) { if (netenable > 1) { completion_code = CC_INVALID_PARAM; break; } sprintf(sendcmd, "ifup eth%d", netdev); } else { sprintf(sendcmd, "ifdown eth%d", netdev); } ret = system(sendcmd); completion_code = CC_SUCCESS; break; default: return completion_code; } return completion_code; } int pal_parse_oem_sel(uint8_t fru, uint8_t *sel, char *error_log) { uint8_t mfg_id[] = {0x9c, 0x9c, 0x00}; char temp_log[MAX_ERR_LOG_SIZE]; error_log[0] = '\0'; // Record Type: 0xC0 (OEM) if ((sel[2] == 0xC0) && !memcmp(&sel[7], mfg_id, sizeof(mfg_id))) { snprintf(temp_log, MAX_ERR_LOG_SIZE, "%s: Can not control SSD%d alert LED", sel[10] ? "Assert" : "Deassert", sel[11]); strcat(error_log, temp_log); } return 0; } int pal_check_sled_mgmt_cbl_id(uint8_t slot_id, uint8_t *cbl_val, bool log_evnt, uint8_t bmc_location) { enum { SLOT1_CBL = 0x03, SLOT2_CBL = 0x02, SLOT3_CBL = 0x01, SLOT4_CBL = 0x00, }; enum { SLOT1_ID0_DETECT_BMC_N = 33, SLOT1_ID1_DETECT_BMC_N = 34, SLOT3_ID0_DETECT_BMC_N = 37, SLOT3_ID1_DETECT_BMC_N = 38, }; const uint8_t mapping_tbl[4] = {SLOT1_CBL, SLOT2_CBL, SLOT3_CBL, SLOT4_CBL}; const char *gpio_mgmt_cbl_tbl[] = {"SLOT%d_ID0_DETECT_BMC_N", "SLOT%d_ID1_DETECT_BMC_N"}; const int num_of_mgmt_pins = ARRAY_SIZE(gpio_mgmt_cbl_tbl); int i = 0; int ret = 0; char dev[32] = {0}; uint8_t val = 0; gpio_value_t gval; uint8_t gpio_vals = 0; bic_gpio_t gpio = {0}; int i2cfd = 0; uint8_t bus = 0; uint8_t tbuf[1] = {0x06}; uint8_t rbuf[1] = {0}; uint8_t tlen = 1; uint8_t rlen = 1; uint8_t cpld_slot_cbl_val = 0; uint8_t slot_id_tmp = slot_id; if ( bmc_location == BB_BMC ) { //read GPIO vals for ( i = 0; i < num_of_mgmt_pins; i++ ) { snprintf(dev, sizeof(dev), gpio_mgmt_cbl_tbl[i], slot_id); if ( (gval = gpio_get_value_by_shadow(dev)) == GPIO_VALUE_INVALID ) { syslog(LOG_WARNING, "%s() Failed to read %s", __func__, dev); } val = (uint8_t)gval; gpio_vals |= (val << i); } } else { //NIC EXP //a bus starts from 4 ret = fby35_common_get_bus_id(slot_id) + 4; if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the bus with fru%d", __func__, slot_id); return -1; } bus = (uint8_t)ret; i2cfd = i2c_cdev_slave_open(bus, SB_CPLD_ADDR, I2C_SLAVE_FORCE_CLAIM); if ( i2cfd < 0 ) { syslog(LOG_WARNING, "%s() Failed to open bus %d. Err: %s", __func__, bus, strerror(errno)); return -1; } //read 06h from SB CPLD ret = i2c_rdwr_msg_transfer(i2cfd, (SB_CPLD_ADDR << 1), tbuf, tlen, rbuf, rlen); if ( i2cfd > 0 ) close(i2cfd); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen); return -1; } cpld_slot_cbl_val = rbuf[0]; //read GPIO from BB BIC ret = bic_get_gpio(slot_id, &gpio, BB_BIC_INTF); if ( ret < 0 ) { printf("%s() bic_get_gpio returns %d\n", __func__, ret); return ret; } if (cpld_slot_cbl_val == SLOT1_CBL) { val = BIT_VALUE(gpio, SLOT1_ID1_DETECT_BMC_N); gpio_vals |= (val << 0); val = BIT_VALUE(gpio, SLOT1_ID0_DETECT_BMC_N); gpio_vals |= (val << 1); } else { val = BIT_VALUE(gpio, SLOT3_ID1_DETECT_BMC_N); gpio_vals |= (val << 0); val = BIT_VALUE(gpio, SLOT3_ID0_DETECT_BMC_N); gpio_vals |= (val << 1); slot_id_tmp = 3; } } bool vals_match = (bmc_location == BB_BMC) ? (gpio_vals == mapping_tbl[slot_id-1]):(gpio_vals == cpld_slot_cbl_val); if (vals_match == false) { for ( i = 0; i < (sizeof(mapping_tbl)/sizeof(uint8_t)); i++ ) { if(mapping_tbl[i] == gpio_vals) { break; } } if (log_evnt == true) { syslog(LOG_CRIT, "Abnormal - slot%d instead of slot%d", slot_id_tmp, (i+1)); } } if ( cbl_val != NULL ) { cbl_val[0] = (vals_match == false)?STATUS_ABNORMAL:STATUS_PRSNT; if (cbl_val[0] == STATUS_ABNORMAL) { cbl_val[1] = slot_id_tmp << 4 | (i+1); } else { cbl_val[1] = 0x00; } } return ret; } int pal_get_cpld_ver(uint8_t fru, uint8_t *ver) { int ret, i2cfd; uint8_t rbuf[4] = {0}; uint8_t i2c_bus = BMC_CPLD_BUS; uint8_t cpld_addr = CPLD_FW_VER_ADDR; uint32_t ver_reg = BMC_CPLD_VER_REG; char value[MAX_VALUE_LEN] = {0}; uint8_t bmc_location = 0; if (fby35_common_get_bmc_location(&bmc_location) < 0) { printf("Failed to get BMC location\n"); return -1; } switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: i2c_bus = fby35_common_get_bus_id(fru) + 4; ver_reg = SB_CPLD_VER_REG; break; case FRU_BMC: if(bmc_location == NIC_BMC) { i2c_bus = NIC_EXP_CPLD_BUS; } if (!kv_get(KEY_BMC_CPLD_VER, value, NULL, 0)) { *(uint32_t *)rbuf = strtol(value, NULL, 16); memcpy(ver, rbuf, sizeof(rbuf)); return 0; } break; default: return -1; } i2cfd = i2c_cdev_slave_open(i2c_bus, cpld_addr >> 1, I2C_SLAVE_FORCE_CLAIM); if (i2cfd < 0) { syslog(LOG_WARNING, "Failed to open bus %u", i2c_bus); return -1; } ret = i2c_rdwr_msg_transfer(i2cfd, cpld_addr, (uint8_t *)&ver_reg, sizeof(ver_reg), rbuf, sizeof(rbuf)); close(i2cfd); if (ret < 0) { syslog(LOG_WARNING, "%s() i2c_rdwr_msg_transfer to slave@0x%02X on bus %u failed", __func__, cpld_addr, i2c_bus); return -1; } if (fru == FRU_BMC) { snprintf(value, sizeof(value), "%02X%02X%02X%02X", rbuf[3], rbuf[2], rbuf[1], rbuf[0]); kv_set(KEY_BMC_CPLD_VER, value, 0, 0); } memcpy(ver, rbuf, sizeof(rbuf)); return 0; } int pal_get_fw_info(uint8_t fru, unsigned char target, unsigned char* res, unsigned char* res_len) { uint8_t bmc_location = 0; uint8_t config_status = CONFIG_UNKNOWN; int ret = PAL_ENOTSUP; uint8_t tmp_cpld_swap[4] = {0}; uint8_t type_2ou = UNKNOWN_BOARD; ret = fby35_common_get_bmc_location(&bmc_location); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get the location of BMC", __func__); goto error_exit; } if (target == FW_CPLD) { if (pal_get_cpld_ver(fru, res)) { goto error_exit; } } else if(target == FW_BIOS) { ret = pal_get_sysfw_ver(fru, res); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to get sysfw ver", __func__); goto error_exit; } } else { switch(target) { case FW_1OU_BIC: case FW_1OU_CPLD: ret = bic_is_m2_exp_prsnt(fru); if (ret < 0) { syslog(LOG_ERR, "%s() Couldn't get the status of 1OU/2OU", __func__); goto error_exit; } config_status = ret; if (!((bmc_location == BB_BMC) && ((config_status & PRESENT_1OU) == PRESENT_1OU))) { goto not_support; } break; case FW_2OU_BIC: case FW_2OU_CPLD: ret = bic_is_m2_exp_prsnt(fru); if (ret < 0) { syslog(LOG_ERR, "%s() Couldn't get the status of 1OU/2OU", __func__); goto error_exit; } config_status = ret; if ( fby35_common_get_2ou_board_type(fru, &type_2ou) < 0 ) { syslog(LOG_WARNING, "%s() Failed to get 2OU board type\n", __func__); } if ((config_status & PRESENT_2OU) != PRESENT_2OU || type_2ou == DPV2_BOARD) { goto not_support; } break; case FW_BB_BIC: case FW_BB_CPLD: if (bmc_location != NIC_BMC) { goto not_support; } break; default: if (target >= FW_COMPONENT_LAST_ID) goto not_support; break; } ret = bic_get_fw_ver(fru, target, res); if (ret != BIC_STATUS_SUCCESS) { syslog(LOG_WARNING, "%s() bic_get_fw_ver returns %d\n", __func__, ret); goto error_exit; } } switch(target) { case FW_CPLD: *res_len = 4; break; case FW_1OU_CPLD: case FW_2OU_CPLD: case FW_BB_CPLD: tmp_cpld_swap[0] = res[3]; tmp_cpld_swap[1] = res[2]; tmp_cpld_swap[2] = res[1]; tmp_cpld_swap[3] = res[0]; memcpy(res, tmp_cpld_swap, 4); *res_len = 4; break; case FW_ME: *res_len = 5; break; case FW_BIC: *res_len = strlen((char*)res); if (*res_len == 2) { // old version format } else if (*res_len >= 4){ // new version format *res_len = 7; //check BIC code, keep 7 bytes. } else { syslog(LOG_WARNING, "%s() Format not supported, length invalid %d", __func__, *res_len); ret = -1; goto error_exit; } break; case FW_1OU_BIC: case FW_2OU_BIC: case FW_BB_BIC: *res_len = 2; break; case FW_BIOS: *res_len = 16; break; default: goto not_support; } return PAL_EOK; not_support: return PAL_ENOTSUP; error_exit: return ret; } int pal_set_nic_perst(uint8_t fru, uint8_t val) { int i2cfd = 0; int ret = 0; char path[32] = {0}; uint8_t bmc_location = 0; uint8_t bus = 0; uint8_t tbuf[2] = {NIC_CARD_PERST_CTRL, val}; uint8_t tlen = 2; ret = fby35_common_get_bmc_location(&bmc_location); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Cannot get the location of BMC", __func__); goto error_exit; } if ( bmc_location == BB_BMC ) { return 0; } bus= (uint8_t)NIC_CPLD_BUS; snprintf(path, sizeof(path), "/dev/i2c-%d", bus); i2cfd = open(path, O_RDWR); if ( i2cfd < 0 ) { syslog(LOG_WARNING, "%s() Failed to open %s", __func__, path); goto error_exit; } ret = i2c_rdwr_msg_transfer(i2cfd, CPLD_ADDRESS, tbuf, tlen, NULL, 0); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen); goto error_exit; } error_exit: if ( i2cfd > 0 ) { close(i2cfd); } return ret; } static const char *sock_path_asd_bic[MAX_NODES+1] = { "", SOCK_PATH_ASD_BIC "_1", SOCK_PATH_ASD_BIC "_2", SOCK_PATH_ASD_BIC "_3", SOCK_PATH_ASD_BIC "_4" }; static const char *sock_path_jtag_msg[MAX_NODES+1] = { "", SOCK_PATH_JTAG_MSG "_1", SOCK_PATH_JTAG_MSG "_2", SOCK_PATH_JTAG_MSG "_3", SOCK_PATH_JTAG_MSG "_4" }; int pal_handle_oem_1s_intr(uint8_t slot, uint8_t *data) { int sock; int err; struct sockaddr_un server; if (access(sock_path_asd_bic[slot], F_OK) == -1) { // SOCK_PATH_ASD_BIC doesn't exist, means ASD daemon for this // slot 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[slot]); 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 slot, uint8_t *data, uint8_t data_len) { int sock; int err; struct sockaddr_un server; if (access(sock_path_jtag_msg[slot], F_OK) == -1) { // SOCK_PATH_JTAG_MSG doesn't exist, means ASD daemon for this // slot 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_jtag_msg[slot]); 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; } // It's called by fpc-util and front-paneld int pal_sb_set_amber_led(uint8_t fru, bool led_on, uint8_t led_mode) { int ret = 0; int i2cfd = -1; uint8_t bus = 0; ret = fby35_common_get_bus_id(fru); if ( ret < 0 ) { printf("%s() Couldn't get the bus id of fru%d\n", __func__, fru); goto err_exit; } bus = (uint8_t)ret + 4; i2cfd = i2c_cdev_slave_open(bus, SB_CPLD_ADDR, I2C_SLAVE_FORCE_CLAIM); if ( i2cfd < 0 ) { printf("%s() Couldn't open i2c bus%d, err: %s\n", __func__, bus, strerror(errno)); goto err_exit; } uint8_t tbuf[2] = {0x0, (led_on == true)?0x01:0x00}; if ( led_mode == LED_LOCATE_MODE ) { /* LOCATE_MODE */ // 0x0f 01h: off // 00h: on tbuf[0] = 0x0f; tbuf[1] = (led_on == true)?0x01:0x00; } else if ( led_mode == LED_CRIT_PWR_OFF_MODE || led_mode == LED_CRIT_PWR_ON_MODE ) { /* CRIT_MODE */ // 0x12 02h: 900ms_on/100ms_off // 01h: 900ms_off/100ms_on // 00h: off tbuf[0] = 0x12; tbuf[1] = (led_on == false)?0x00:(led_mode == LED_CRIT_PWR_OFF_MODE)?0x01:0x02; } else { syslog(LOG_WARNING, "%s() fru:%d, led_on:%d, led_mode:%d\n", __func__, fru, led_on, led_mode); } ret = i2c_rdwr_msg_transfer(i2cfd, (SB_CPLD_ADDR << 1), tbuf, 2, NULL, 0); if ( ret < 0 ) { printf("%s() Couldn't write data to addr %02X, err: %s\n", __func__, SB_CPLD_ADDR, strerror(errno)); } err_exit: if ( i2cfd > 0 ) close(i2cfd); return ret; } // IPMI chassis identification LED command // ipmitool chassis identify [force|0] // force: turn on LED indefinitely // 0: turn off LED int pal_set_slot_led(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { int rsp_cc = CC_UNSPECIFIED_ERROR; uint8_t *data = req_data; *res_len = 0; /* There are 2 option bytes for Chassis Identify Command * Byte 1 : Identify Interval in seconds. (Not support, OpenBMC only support turn off action) * 00h = Turn off Identify * Byte 2 : Force Identify On * BIT0 : 1b = Turn on Identify indefinitely. This overrides the values in byte 1. * 0b = Identify state driven according to byte 1. */ if ( 5 == req_len ) { if ( GETBIT(*(data+1), 0) ) { //turn on rsp_cc = pal_sb_set_amber_led(slot, true, LED_LOCATE_MODE); } else if ( 0 == *data ) { //turn off rsp_cc = pal_sb_set_amber_led(slot, false, LED_LOCATE_MODE); } else { rsp_cc = CC_INVALID_PARAM; } } else if ( 4 == req_len ) { if (0 == *data) { //turn off rsp_cc = pal_sb_set_amber_led(slot, false, LED_LOCATE_MODE); } else { rsp_cc = CC_INVALID_PARAM; } } if ( rsp_cc < 0 ) rsp_cc = CC_UNSPECIFIED_ERROR; return rsp_cc; } int pal_get_dev_info(uint8_t slot_id, uint8_t dev_id, uint8_t *nvme_ready, uint8_t *status, uint8_t *type) { return bic_get_dev_info(slot_id, dev_id, nvme_ready, status, type); } int pal_check_slot_cpu_present(uint8_t slot_id) { int ret = 0; bic_gpio_t gpio = {0}; ret = bic_get_gpio(slot_id, &gpio, NONE_INTF); if ( ret < 0 ) { printf("%s() bic_get_gpio returns %d\n", __func__, ret); return ret; } if (BIT_VALUE(gpio, FM_CPU_SKTOCC_LVT3_PLD_N)) { syslog(LOG_CRIT, "FRU: %d, CPU absence", slot_id); } else { syslog(LOG_CRIT, "FRU: %d, CPU presence", slot_id); } return ret; } int pal_get_sensor_util_timeout(uint8_t fru) { switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: case FRU_BMC: return 10; case FRU_NIC: default: return 4; } } // IPMI OEM Command // netfn: NETFN_OEM_1S_REQ (0x38) // command code: CMD_OEM_1S_GET_SYS_FW_VER (0x40) int pal_get_fw_ver(uint8_t slot, uint8_t *req_data, uint8_t *res_data, uint8_t *res_len) { int ret = 0; uint8_t type_2ou = 0; uint8_t fru = 0; uint8_t comp = 0; FILE* fp = NULL; char buf[MAX_FW_VER_LEN] = {0}; // To keep the format consistent with fw-util, get version from fw-util directly. static const char* cmd_table[IPMI_GET_VER_FRU_NUM][IPMI_GET_VER_MAX_COMP] = { // BMC { "/usr/bin/fw-util bmc --version bmc | awk '{print $NF}'", "/usr/bin/fw-util bmc --version rom | awk '{print $NF}'", "/usr/bin/fw-util bmc --version cpld | awk '{print $NF}'", "/usr/bin/fw-util bmc --version fscd | awk '{print $NF}'", "/usr/bin/fw-util bmc --version tpm | awk '{print $NF}'", NULL, NULL, NULL, NULL }, // NIC { "/usr/bin/fw-util nic --version | awk '{print $NF}'", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, // Base board { "/usr/bin/fw-util slot1 --version bb_bic | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version bb_bicbl | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version bb_cpld | awk '{print $NF}'", NULL, NULL, NULL, NULL, NULL, NULL }, // Server board { "/usr/bin/fw-util slot1 --version bic | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version bicbl | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version bios | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version cpld | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version me | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version vr | grep 'VCCIN' | awk '{print $5}' | cut -c -8", "/usr/bin/fw-util slot1 --version vr | grep 'VCCD' | awk '{print $5}' | cut -c -8", "/usr/bin/fw-util slot1 --version vr | grep 'VCCINFAON' | awk '{print $5}' | cut -c -8" }, // 2OU { "/usr/bin/fw-util slot1 --version 2ou_bic | awk '{print $NF}'", "/usr/bin/fw-util slot1 --version 2ou_bicbl | awk '{print $NF}'", NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; if (res_len == NULL) { syslog(LOG_ERR, "%s(): IPMI request failed due to NULL parameter: *res_len", __func__); return CC_INVALID_PARAM; } *res_len = 0; if (req_data == NULL) { syslog(LOG_ERR, "%s(): IPMI request failed due to NULL parameter: *req_data", __func__); return CC_INVALID_PARAM; } if (res_data == NULL) { syslog(LOG_ERR, "%s(): IPMI request failed due to NULL parameter: *res_data", __func__); return CC_INVALID_PARAM; } ret = fby35_common_get_2ou_board_type(slot, &type_2ou); if (ret < 0) { syslog(LOG_ERR, "%s(): Not support CMD_OEM_1S_GET_SYS_FW_VER IPMI command due to get 2ou board type failed", __func__); return CC_UNSPECIFIED_ERROR; } if (type_2ou != E1S_BOARD) { syslog(LOG_ERR, "%s(): CMD_OEM_1S_GET_SYS_FW_VER IPMI command only support by Sierra Point system", __func__); return CC_NOT_SUPP_IN_CURR_STATE; } fru = ((GET_FW_VER_REQ*)req_data)->fru; comp = ((GET_FW_VER_REQ*)req_data)->component; if ((fru >= IPMI_GET_VER_FRU_NUM) || (comp >= IPMI_GET_VER_MAX_COMP) || (cmd_table[fru][comp] == NULL)) { syslog(LOG_ERR, "%s(): wrong FRU or component, fru = %x, comp = %x", __func__, fru, comp); return CC_PARAM_OUT_OF_RANGE; } if((fp = popen(cmd_table[fru][comp], "r")) == NULL) { syslog(LOG_ERR, "%s(): fail to send command: %s, errno: %s", __func__, cmd_table[fru][comp], strerror(errno)); return CC_UNSPECIFIED_ERROR; } memset(buf, 0, sizeof(buf)); if(fgets(buf, sizeof(buf), fp) != NULL) { *res_len = strlen(buf); strncpy((char*)res_data, buf, MAX_FW_VER_LEN); } pclose(fp); return CC_SUCCESS; } int pal_gpv3_mux_select(uint8_t slot_id, uint8_t dev_id) { if ( bic_mux_select(slot_id, get_gpv3_bus_number(dev_id), dev_id, REXP_BIC_INTF) < 0 ) { printf("* Failed to select MUX\n"); return BIC_STATUS_FAILURE; } return BIC_STATUS_SUCCESS; } bool pal_is_aggregate_snr_valid(uint8_t snr_num) { char sys_conf[MAX_VALUE_LEN] = {0}; switch(snr_num) { // In type 8 system, if one fan fail, show NA in airflow reading. case AGGREGATE_SENSOR_SYSTEM_AIRFLOW: memset(sys_conf, 0, sizeof(sys_conf)); if (kv_get("sled_system_conf", sys_conf, NULL, KV_FPERSIST) < 0) { syslog(LOG_WARNING, "%s() Failed to read sled_system_conf", __func__); return true; } if (strcmp(sys_conf, "Type_8") != 0) { return true; } if (access(FAN_FAIL_RECORD_PATH, F_OK) == 0) { return false; } break; default: return true; } return true; } int pal_check_slot_fru(uint8_t slot_id) { int ret = 0, i2cfd = 0 ,retry = 0; uint8_t bus = slot_id + SLOT_BUS_BASE; uint8_t tbuf[1] = {0x11}; uint8_t rbuf[1] = {0xff}; uint8_t tlen = 1; uint8_t rlen = 1; i2cfd = i2c_cdev_slave_open(bus, CPLD_ADDRESS >> 1, I2C_SLAVE_FORCE_CLAIM); if ( i2cfd < 0) { printf("%s(): Failed to open bus %d. Err: %s\n", __func__, bus, strerror(errno)); goto error_exit; } while ( retry < MAX_READ_RETRY ) { ret = i2c_rdwr_msg_transfer(i2cfd, CPLD_ADDRESS, tbuf, tlen, rbuf, rlen); if ( ret < 0 ) { retry++; sleep(1); } else { break; } } if ( retry == MAX_READ_RETRY ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen); goto error_exit; } if ( rbuf[0] != 0x0 ) { syslog(LOG_CRIT, "Slot%d plugged in a wrong FRU", slot_id); } error_exit: if ( i2cfd > 0 ) close(i2cfd); return ret; } int pal_clear_cmos(uint8_t slot_id) { int ret = 0, i2cfd = 0, retry = 0; uint8_t rtc_rst_reg = 0x2C + (slot_id - 1); uint8_t tbuf[2] = {rtc_rst_reg, 0x00}; uint8_t tlen = 2; uint8_t bmc_location = 0, status = 0; ret = fby35_common_get_bmc_location(&bmc_location); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get the location of BMC", __func__); return ret; } if ( bmc_location != BB_BMC ) { // TODO: Class 2 printf("Not supported"); return -1; } ret = pal_set_server_power(slot_id, SERVER_12V_OFF); if (ret < 0) { printf("Failed to set server power 12V-off\n"); return ret; } sleep(DELAY_12V_CYCLE); printf("Performing CMOS clear\n"); i2cfd = i2c_cdev_slave_open(BB_CPLD_BUS, CPLD_ADDRESS >> 1, I2C_SLAVE_FORCE_CLAIM); if ( i2cfd < 0) { printf("%s(): Failed to open bus %d. Err: %s\n", __func__, BB_CPLD_BUS, strerror(errno)); return -1; } while ( retry < MAX_READ_RETRY ) { // to generate 200ms high pulse to clear CMOS ret = i2c_rdwr_msg_transfer(i2cfd, CPLD_ADDRESS, tbuf, tlen, NULL, 0); if ( ret < 0 ) { retry++; sleep(1); } else { break; } } close(i2cfd); if ( retry == MAX_READ_RETRY ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen); return -1; } sleep(1); ret = pal_set_server_power(slot_id, SERVER_12V_ON); if (ret < 0) { printf("Failed to set server power 12V-on\n"); return ret; } if ( (pal_get_server_power(slot_id, &status) == 0) && (status == SERVER_POWER_OFF) ) { ret = pal_set_server_power(slot_id, SERVER_POWER_ON); if (ret < 0) { printf("Failed to set server power on\n"); return ret; } } return ret; }