meta-facebook/meta-fbal/recipes-fbal/plat-libs/files/pal/pal.c (1,663 lines of code) (raw):

/* * * Copyright 2015-present Facebook. All Rights Reserved. * * This file contains code to support IPMI2.0 Specificaton 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 <time.h> #include <sys/mman.h> #include <sys/time.h> #include <dirent.h> #include <openbmc/kv.h> #include <openbmc/libgpio.h> #include <openbmc/nm.h> #include <openbmc/obmc-i2c.h> #include <facebook/fbal_fruid.h> #include <openbmc/ipmb.h> #include <openbmc/ncsi.h> #include <openbmc/nl-wrapper.h> #include "pal.h" #define FBAL_PLATFORM_NAME "angelslanding" #define LAST_KEY "last_key" #define GPIO_LOCATE_LED_ACT "FP_LOCATE_LED_ACT" #define GPIO_LOCATE_LED "FP_LOCATE_LED" #define GPIO_FAULT_LED "FP_FAULT_LED_N" #define GPIO_NIC0_PRSNT "HP_LVC3_OCP_V3_1_PRSNT2_N" #define GPIO_NIC1_PRSNT "HP_LVC3_OCP_V3_2_PRSNT2_N" #define OFFSET_SYS_GUID 0x17F0 #define OFFSET_DEV_GUID 0x1800 #define MAX_CPU_NUM 8 #define ETHERNET_BR0_INTERFACE_PATH "/sys/class/net/br0" #define NUM_SERVER_FRU 1 #define NUM_NIC_FRU 2 #define NUM_BMC_FRU 1 const char pal_fru_list[] = "all, mb, nic0, nic1, pdb, bmc, tray0_mb, tray1_mb, tray0_nic0, tray0_nic1, tray1_nic0, tray1_nic1, tray0_bmc, tray1_bmc"; const char pal_server_list[] = "mb"; static int key_func_por_policy (int event, void *arg); static int key_func_lps (int event, void *arg); uint8_t FRU_MB = FRU_TRAY0_MB; uint8_t FRU_NIC0 = FRU_TRAY0_NIC0; uint8_t FRU_NIC1 = FRU_TRAY0_NIC1; uint8_t FRU_BMC = FRU_TRAY0_BMC; enum key_event { KEY_BEFORE_SET, KEY_AFTER_INI, }; typedef enum { SV_LAST_PWR_ST = 0, SV_SYSFW_VER, SLED_IDENTIFY, SLED_TIMESTAMP, SV_POR_CFG, SV_SNR_HEALTH, NIC0_SNR_HEALTH, NIC1_SNR_HEALTH, PDB_SNR_HEALTH, SV_SEL_ERR, SV_BOOT_ORDER, CPU0_PPIN, CPU1_PPIN, CPU2_PPIN, CPU3_PPIN, CPU4_PPIN, CPU5_PPIN, CPU6_PPIN, CPU7_PPIN, NTP_SERVER, LAST_ID = 255 } key_cfg_id; struct pal_key_cfg { key_cfg_id id; char *name; char *def_val; int (*function)(int, void*); } key_cfg[] = { /* name, default value, function */ {SV_LAST_PWR_ST, "pwr_server_last_state", "on", key_func_lps}, {SV_SYSFW_VER, "sysfw_ver_server", "0", NULL}, {SLED_IDENTIFY, "identify_sled", "off", NULL}, {SLED_TIMESTAMP, "timestamp_sled", "0", NULL}, {SV_POR_CFG, "server_por_cfg", "lps", key_func_por_policy}, {SV_SNR_HEALTH, "server_sensor_health", "1", NULL}, {NIC0_SNR_HEALTH, "nic0_sensor_health", "1", NULL}, {NIC1_SNR_HEALTH, "nic1_sensor_health", "1", NULL}, {PDB_SNR_HEALTH, "pdb_sensor_health", "1", NULL}, {SV_SEL_ERR, "server_sel_error", "1", NULL}, {SV_BOOT_ORDER, "server_boot_order", "0100090203ff", NULL}, {CPU0_PPIN, "cpu0_ppin", "", NULL}, {CPU1_PPIN, "cpu1_ppin", "", NULL}, {CPU2_PPIN, "cpu2_ppin", "", NULL}, {CPU3_PPIN, "cpu3_ppin", "", NULL}, {CPU4_PPIN, "cpu4_ppin", "", NULL}, {CPU5_PPIN, "cpu5_ppin", "", NULL}, {CPU6_PPIN, "cpu6_ppin", "", NULL}, {CPU7_PPIN, "cpu7_ppin", "", NULL}, {NTP_SERVER, "ntp_server", "", NULL}, /* Add more Keys here */ {LAST_ID, LAST_KEY, LAST_KEY, NULL} /* This is the last key of the list */ }; static int pal_key_index(char *key) { int i; for (i = 0; key_cfg[i].id != LAST_ID; i++) { // If Key is valid, return success if (!strcmp(key, key_cfg[i].name)) return i; } #ifdef DEBUG syslog(LOG_WARNING, "pal_key_index: invalid key - %s", key); #endif return -1; } 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); } static int fw_getenv(char *key, char *value) { char cmd[MAX_KEY_LEN + 32] = {0}; char *p; FILE *fp; sprintf(cmd, "/sbin/fw_printenv -n %s", key); fp = popen(cmd, "r"); if (!fp) { return -1; } if (fgets(value, MAX_VALUE_LEN, fp) == NULL) { pclose(fp); return -1; } for (p = value; *p != '\0'; p++) { if (*p == '\n' || *p == '\r') { *p = '\0'; break; } } pclose(fp); return 0; } static int fw_setenv(char *key, char *value) { char old_value[MAX_VALUE_LEN] = {0}; if (fw_getenv(key, old_value) != 0 || strcmp(old_value, value) != 0) { /* Set the env key:value if either the key * does not exist or the value is different from * what we want set */ char cmd[MAX_VALUE_LEN] = {0}; snprintf(cmd, MAX_VALUE_LEN, "/sbin/fw_setenv %s %s", key, value); return system(cmd); } return 0; } static int key_func_por_policy(int event, void *arg) { char value[MAX_VALUE_LEN] = {0}; int ret = 0; switch (event) { case KEY_BEFORE_SET: if (strcmp((char *)arg, "lps") && strcmp((char *)arg, "on") && strcmp((char *)arg, "off")) return -1; if (pal_is_fw_update_ongoing(FRU_BMC)) { syslog(LOG_WARNING, "key_func_por_policy: cannot setenv por_policy=%s", (char *)arg); break; } ret = fw_setenv("por_policy", (char *)arg); break; case KEY_AFTER_INI: kv_get("server_por_cfg", value, NULL, KV_FPERSIST); ret = fw_setenv("por_policy", value); break; } return ret; } static int key_func_lps(int event, void *arg) { char value[MAX_VALUE_LEN] = {0}; switch (event) { case KEY_BEFORE_SET: if (pal_is_fw_update_ongoing(FRU_BMC)) { syslog(LOG_WARNING, "key_func_lps: cannot setenv por_ls=%s", (char *)arg); break; } fw_setenv("por_ls", (char *)arg); break; case KEY_AFTER_INI: kv_get("pwr_server_last_state", value, NULL, KV_FPERSIST); fw_setenv("por_ls", value); break; } return 0; } 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_platform_name(char *name) { strcpy(name, FBAL_PLATFORM_NAME); return 0; } int pal_get_num_slots(uint8_t *num) { *num = 1; return 0; } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { gpio_desc_t *gdesc = NULL; gpio_value_t val; uint8_t mode; bool master; if (pal_get_host_system_mode(&mode)) { return -1; } master = pal_get_config_is_master(); *status = 0; // This MB, PDB, BMC && DBG if (fru == FRU_MB || fru == FRU_PDB || fru == FRU_BMC || fru == FRU_DBG) { *status = 1; } else if ( master && (mode == MB_4S_EX_MODE || mode == MB_4S_EP_MODE) && (fru == FRU_TRAY1_MB) ) { // Support tray1 MB in master BMC. *status = 1; } else if (fru == FRU_NIC0) { if ((gdesc = gpio_open_by_shadow(GPIO_NIC0_PRSNT))) { if (!gpio_get_value(gdesc, &val)) { *status = !val; } gpio_close(gdesc); } } else if (fru == FRU_NIC1) { if ((gdesc = gpio_open_by_shadow(GPIO_NIC1_PRSNT))) { if (!gpio_get_value(gdesc, &val)) { *status = !val; } gpio_close(gdesc); } } else if (fru > FRU_ALL && fru < FRU_CNT) { *status = 0; } else { return -1; } return 0; } int pal_is_slot_server(uint8_t fru) { if (fru == FRU_MB) return 1; return 0; } // Update the Identification LED for the given fru with the status int pal_set_id_led(uint8_t fru, uint8_t status) { int ret = -1; gpio_desc_t *gdesc_id = NULL, *gdesc_loc = NULL; do { if (!(gdesc_id = gpio_open_by_shadow(GPIO_LOCATE_LED_ACT))) break; if (!(gdesc_loc = gpio_open_by_shadow(GPIO_LOCATE_LED))) break; if (status == 0xFF) { // restore FP_LOCATE_LED_ACT ret = gpio_set_value(gdesc_id, GPIO_VALUE_LOW); ret |= gpio_set_value(gdesc_loc, GPIO_VALUE_LOW); break; } ret = gpio_set_value(gdesc_id, GPIO_VALUE_HIGH); ret |= gpio_set_value(gdesc_loc, status ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW); } while (0); if (gdesc_id) { gpio_close(gdesc_id); } if (gdesc_loc) { gpio_close(gdesc_loc); } return ret; } int pal_set_fault_led(uint8_t fru, uint8_t status) { int ret; gpio_desc_t *gdesc = NULL; if (!(gdesc = gpio_open_by_shadow(GPIO_FAULT_LED))) { return -1; } ret = gpio_set_value(gdesc, status ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW); gpio_close(gdesc); return ret; } int pal_get_fru_id(char *str, uint8_t *fru) { if (!strcmp(str, "all")) { *fru = FRU_ALL; } else if (!strcmp(str, "mb") || !strcmp(str, "cpld") || !strcmp(str, "vr")) { *fru = FRU_MB; } else if (!strcmp(str, "tray0_mb")) { *fru = FRU_TRAY0_MB; } else if (!strcmp(str, "tray1_mb")) { *fru = FRU_TRAY1_MB; } else if (!strcmp(str, "pdb")) { *fru = FRU_PDB; } else if (!strcmp(str, "nic0") || !strcmp(str, "nic")) { *fru = FRU_NIC0; } else if (!strcmp(str, "nic1")) { *fru = FRU_NIC1; } else if (!strcmp(str, "tray0_nic0")) { *fru = FRU_TRAY0_NIC0; } else if (!strcmp(str, "tray0_nic1")) { *fru = FRU_TRAY0_NIC1; } else if (!strcmp(str, "tray1_nic0")) { *fru = FRU_TRAY1_NIC0; } else if (!strcmp(str, "tray1_nic1")) { *fru = FRU_TRAY1_NIC1; } else if (!strcmp(str, "ocpdbg")) { *fru = FRU_DBG; } else if (!strcmp(str, "bmc")) { *fru = FRU_BMC; } else if (!strcmp(str, "tray0_bmc")) { *fru = FRU_TRAY0_BMC; } else if (!strcmp(str, "tray1_bmc")) { *fru = FRU_TRAY1_BMC; } else { syslog(LOG_WARNING, "pal_get_fru_id: Wrong fru#%s", str); return -1; } return 0; } int pal_get_fru_name(uint8_t fru, char *name) { switch (fru) { case FRU_TRAY0_MB: strcpy(name, "tray0_mb"); break; case FRU_TRAY1_MB: strcpy(name, "tray1_mb"); break; case FRU_PDB: strcpy(name, "pdb"); break; case FRU_TRAY0_NIC0: strcpy(name, "tray0_nic0"); break; case FRU_TRAY0_NIC1: strcpy(name, "tray0_nic1"); break; case FRU_TRAY1_NIC0: strcpy(name, "tray1_nic0"); break; case FRU_TRAY1_NIC1: strcpy(name, "tray1_nic1"); break; case FRU_DBG: strcpy(name, "ocpdbg"); break; case FRU_TRAY0_BMC: strcpy(name, "tray0_bmc"); break; case FRU_TRAY1_BMC: strcpy(name, "tray1_bmc"); break; default: if (fru > MAX_NUM_FRUS) return -1; sprintf(name, "fru%d", fru); break; } return 0; } void pal_dump_key_value(void) { int i; uint8_t mode; char value[MAX_VALUE_LEN]; for (i = 0; key_cfg[i].id != LAST_ID; i++) { if ((key_cfg[i].id >= CPU2_PPIN) && (key_cfg[i].id <= CPU7_PPIN)) { if (!pal_get_host_system_mode(&mode) && (mode == MB_2S_MODE)) { continue; } } printf("%s:", key_cfg[i].name); memset(value, 0, sizeof(value)); if (kv_get(key_cfg[i].name, value, NULL, KV_FPERSIST) < 0) { printf("\n"); } else { printf("%s\n", value); } } } void pal_get_chassis_status(uint8_t slot, uint8_t *req_data, uint8_t *res_data, uint8_t *res_len) { char str_server_por_cfg[64]; char buff[MAX_VALUE_LEN]; int policy = 3; unsigned char *data = res_data; // Platform Power Policy memset(str_server_por_cfg, 0 , sizeof(char) * 64); sprintf(str_server_por_cfg, "%s", "server_por_cfg"); if (pal_get_key_value(str_server_por_cfg, buff) == 0) { if (!memcmp(buff, "off", strlen("off"))) policy = 0; else if (!memcmp(buff, "lps", strlen("lps"))) policy = 1; else if (!memcmp(buff, "on", strlen("on"))) policy = 2; else policy = 3; } *data++ = ((is_server_off())?0x00:0x01) | (policy << 5); *data++ = 0x00; // Last Power Event *data++ = 0x40; // Misc. Chassis Status *data++ = 0x00; // Front Panel Button Disable *res_len = data - res_data; } 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_fruid_path(uint8_t fru, char *path) { char fname[16] = {0}; if (fru == FRU_MB) { sprintf(fname, "mb"); } else if (fru == FRU_NIC0) { sprintf(fname, "nic0"); } else if (fru == FRU_NIC1) { sprintf(fname, "nic1"); } else if (fru == FRU_PDB) { sprintf(fname, "pdb"); } else if (fru == FRU_BMC) { sprintf(fname, "bmc"); } else if (fru == FRU_TRAY1_MB) { // In 4s mode, tray1_mb still present on tray0 even though path not exist. return -2; } else { return -1; } sprintf(path, "/tmp/fruid_%s.bin", fname); return 0; } void fru_eeprom_mb_check(char *mb_path) { uint8_t id = REV_DVT; pal_get_board_rev_id(&id); if (id >= REV_DVT) { sprintf(mb_path, FRU_EEPROM_MB_T, 57); } else { sprintf(mb_path, FRU_EEPROM_MB_T, 54); } } int pal_get_fruid_eeprom_path(uint8_t fru, char *path) { char FRU_EEPROM_MB[64]; if (fru == FRU_MB) { fru_eeprom_mb_check(FRU_EEPROM_MB); sprintf(path, "%s", FRU_EEPROM_MB); } else if (fru == FRU_NIC0) { sprintf(path, FRU_EEPROM_NIC0); } else if (fru == FRU_NIC1) { sprintf(path, FRU_EEPROM_NIC1); } else if (fru == FRU_BMC) { sprintf(path, FRU_EEPROM_BMC); } else { return -1; } return 0; } int pal_get_fruid_name(uint8_t fru, char *name) { switch(fru) { case FRU_TRAY0_MB: sprintf(name, "Tray0 Mother Board"); break; case FRU_TRAY0_NIC0: sprintf(name, "Tray0 Mezz Card 0"); break; case FRU_TRAY0_NIC1: sprintf(name, "Tray0 Mezz Card 1"); break; case FRU_TRAY1_MB: sprintf(name, "Tray1 Mother Board"); break; case FRU_TRAY1_NIC0: sprintf(name, "Tray1 Mezz Card 0"); break; case FRU_TRAY1_NIC1: sprintf(name, "Tray1 Mezz Card 1"); break; case FRU_PDB: sprintf(name, "PDB"); break; case FRU_TRAY0_BMC: sprintf(name, "Tray0 BMC"); break; case FRU_TRAY1_BMC: sprintf(name, "Tray1 BMC"); break; default: return -1; } return 0; } int pal_fruid_write(uint8_t fru, char *path) { if (fru == FRU_PDB) { return fbal_write_pdb_fruid(0, path); } return -1; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { *status = 1; return 0; } int pal_devnum_to_fruid(int devnum) { return FRU_MB; } int pal_channel_to_bus(int channel) { switch (channel) { case IPMI_CHANNEL_0: return I2C_BUS_0; // USB (LCD Debug Board) case IPMI_CHANNEL_2: return I2C_BUS_2; // Slave BMC case IPMI_CHANNEL_6: return I2C_BUS_5; // ME case IPMI_CHANNEL_8: return I2C_BUS_8; // CM case IPMI_CHANNEL_9: return I2C_BUS_6; // EP } // Debug purpose, map to real bus number if (channel & 0x80) { return (channel & 0x7f); } return channel; } 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; } int pal_set_fw_update_ongoing(uint8_t fruid, uint16_t tmout) { char key[64] = {0}; char value[64] = {0}; struct timespec ts; int index; sprintf(key, "fru%d_fwupd", fruid); clock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_sec += tmout; sprintf(value, "%ld", ts.tv_sec); if (kv_set(key, value, 0, 0) < 0) { return -1; } if (fruid == FRU_PDB) return 0; index = lib_cmc_get_block_index(fruid); if(index < 0) { return -1; } if (tmout == 0) { lib_cmc_set_block_command_flag(index, CM_COMMAND_UNBLOCK); } else { lib_cmc_set_block_command_flag(index, CM_COMMAND_BLOCK); } return 0; } int pal_set_def_key_value() { int i; char key[MAX_KEY_LEN] = {0}; for (i = 0; key_cfg[i].id != LAST_ID; 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); } } /* Actions to be taken on Power On Reset */ if (pal_is_bmc_por()) { /* Clear all the SEL errors */ memset(key, 0, MAX_KEY_LEN); strcpy(key, "server_sel_error"); /* Write the value "1" which means FRU_STATUS_GOOD */ pal_set_key_value(key, "1"); /* Clear all the sensor health files*/ memset(key, 0, MAX_KEY_LEN); strcpy(key, "server_sensor_health"); /* Write the value "1" which means FRU_STATUS_GOOD */ pal_set_key_value(key, "1"); } return 0; } int pal_get_bmc_ipmb_slave_addr(uint16_t* slave_addr, uint8_t bus_id) { uint8_t val; int ret; static uint8_t addr=0; if (bus_id == I2C_BUS_2) { if (addr == 0) { ret = pal_get_mb_position(&val); if (ret != 0) { return -1; } addr = 0x10 | val; *slave_addr = addr; } else { *slave_addr = addr; } } else { *slave_addr = 0x10; } #ifdef DEBUG syslog(LOG_DEBUG,"%s BMC Slave Addr=%d bus=%d", __func__, *slave_addr, bus_id); #endif return 0; } int pal_peer_tray_get_lan_config(uint8_t sel, uint8_t *buf, uint8_t *rlen) { uint8_t netfn = NETFN_TRANSPORT_REQ; uint8_t ipmi_cmd = CMD_TRANSPORT_GET_LAN_CONFIG; uint8_t req[2] = {0x0, sel}; uint8_t resp[MAX_IPMI_MSG_SIZE]; uint16_t bmc_addr; uint16_t dest_bmc_addr; uint8_t val; int ret; ret = pal_get_mb_position(&val); if (ret) { return ret; } dest_bmc_addr = val ? 0x20 : 0x22; ret = pal_get_bmc_ipmb_slave_addr(&bmc_addr, I2C_BUS_2); if (ret) { return ret; } ret = lib_ipmb_send_request(ipmi_cmd, netfn, req, 2, resp, rlen, I2C_BUS_2, dest_bmc_addr, bmc_addr); if (ret) { return ret; } if (*rlen <= 1) { return -1; } *rlen = *rlen - 1; memcpy(buf, resp + 1, *rlen); return 0; } int pal_ipmb_processing(int bus, void *buf, uint16_t size) { char key[MAX_KEY_LEN]; char value[MAX_VALUE_LEN]; struct timespec ts; static time_t last_time = 0; switch (bus) { case I2C_BUS_0: if (((uint8_t *)buf)[0] == 0x20) { clock_gettime(CLOCK_MONOTONIC, &ts); if (ts.tv_sec >= (last_time + 5)) { last_time = ts.tv_sec; ts.tv_sec += 20; sprintf(key, "ocpdbg_lcd"); sprintf(value, "%ld", ts.tv_sec); if (kv_set(key, value, 0, 0) < 0) { return -1; } } } break; } return 0; } int pal_is_mcu_ready(uint8_t bus) { char key[MAX_KEY_LEN]; char value[MAX_VALUE_LEN] = {0}; struct timespec ts; switch (bus) { case I2C_BUS_0: sprintf(key, "ocpdbg_lcd"); if (kv_get(key, value, NULL, 0)) { return false; } clock_gettime(CLOCK_MONOTONIC, &ts); if (strtoul(value, NULL, 10) > ts.tv_sec) { return true; } break; case I2C_BUS_8: return true; } return false; } static int pal_get_blade_id(uint8_t *id) { char value[MAX_VALUE_LEN] = {0}; if (kv_get("mb_pos", value, NULL, 0)) { return -1; } *id = (uint8_t)atoi(value); return 0; } int pal_get_mb_position(uint8_t *pos) { static bool cached = false; static uint8_t cached_pos = 0; if (!cached) { if (pal_get_blade_id(&cached_pos)) return -1; switch (cached_pos) { case 0: cached_pos = MB_ID0; break; case 1: cached_pos = MB_ID1; break; default: return -1; } cached = true; } #ifdef DEBUG syslog(LOG_DEBUG, "%s: BMC Position ID = %u", __func__, cached_pos); #endif *pos = cached_pos; return 0; } int pal_get_config_is_master(void) { char value[MAX_VALUE_LEN] = {0}; static bool cached = false; static int status = 1; if (!cached) { if (kv_get("mb_skt", value, NULL, 0)) { return status; } status = ((atoi(value) & 0x1) == GPIO_VALUE_LOW); cached = true; } #ifdef DEBUG syslog(LOG_DEBUG, "%s: status = %d", __func__, status); #endif return status; } int pal_get_host_system_mode(uint8_t *mode) { char value[MAX_VALUE_LEN] = {0}; static bool cached = false; static unsigned int cached_id = 0; if (!cached) { if (kv_get("mb_skt", value, NULL, 0)) { return -1; } cached_id = atoi(value) >> 1; cached = true; } #ifdef DEBUG syslog(LOG_DEBUG, "%s: System Mode = %u", __func__, cached_id); #endif *mode = (uint8_t)cached_id; return 0; } int pal_get_platform_id(uint8_t *id) { char value[MAX_VALUE_LEN] = {0}; if (kv_get("mb_sku", value, NULL, 0)) { return -1; } *id = (uint8_t)atoi(value); return 0; } int pal_set_sysfw_ver(uint8_t slot, uint8_t *ver) { int i; char str[MAX_VALUE_LEN] = {0}; char tstr[8] = {0}; for (i = 0; i < SIZE_SYSFW_VER; i++) { sprintf(tstr, "%02x", ver[i]); strcat(str, tstr); } return pal_set_key_value("sysfw_ver_server", str); } int pal_get_sysfw_ver(uint8_t slot, uint8_t *ver) { int ret = -1; int i, j; char str[MAX_VALUE_LEN] = {0}; char tstr[8] = {0}; uint8_t mode; ret = pal_get_host_system_mode(&mode); if(ret != 0) { return ret; } ret = pal_get_config_is_master(); if(ret < 0) { return ret; } if( ret || (mode == MB_2S_MODE) ) { #ifdef DEBUG syslog(LOG_DEBUG, "%s true, mode=%d\n", __func__, mode); #endif ret = pal_get_key_value("sysfw_ver_server", str); if (ret) { return ret; } for (i = 0, j = 0; i < 2*SIZE_SYSFW_VER; i += 2) { sprintf(tstr, "%c%c", str[i], str[i+1]); ver[j++] = strtol(tstr, NULL, 16); } } else { return -1; } return 0; } int pal_postcode_select(int option) { int mmap_fd; uint32_t ctrl; void *reg_base; void *reg_offset; mmap_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mmap_fd < 0) { return -1; } reg_base = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mmap_fd, AST_GPIO_BASE); reg_offset = (uint8_t *)reg_base + UARTSW_OFFSET; ctrl = *(volatile uint32_t *)reg_offset; switch(option) { case POSTCODE_BY_BMC: // POST code LED controlled by BMC ctrl &= 0x00ffffff; break; case POSTCODE_BY_HOST: // POST code LED controlled by LPC ctrl |= 0x01000000; break; default: syslog(LOG_WARNING, "pal_mmap: unknown option"); break; } *(volatile uint32_t *)reg_offset = ctrl; munmap(reg_base, PAGE_SIZE); close(mmap_fd); return 0; } int pal_uart_select_led_set(void) { static uint32_t pre_channel = 0xFF; uint32_t channel; uint32_t vals; const char *uartsw_pins[] = { "FM_UARTSW_LSB_N", "FM_UARTSW_MSB_N" }; const char *postcode_pins[] = { "LED_POSTCODE_0", "LED_POSTCODE_1", "LED_POSTCODE_2", "LED_POSTCODE_3", "LED_POSTCODE_4", "LED_POSTCODE_5", "LED_POSTCODE_6", "LED_POSTCODE_7" }; pal_postcode_select(POSTCODE_BY_BMC); if (gpio_get_value_by_shadow_list(uartsw_pins, ARRAY_SIZE(uartsw_pins), &vals)) { return -1; } channel = ~vals & 0x3; // the GPIOs are active-low, so invert it if (channel != pre_channel) { // show channel on 7-segment display if (gpio_set_value_by_shadow_list(postcode_pins, ARRAY_SIZE(postcode_pins), channel)) { return -1; } pre_channel = channel; } return 0; } bool pal_check_boot_device_is_vaild(uint8_t device) { bool vaild = false; switch (device) { case BOOT_DEVICE_USB: case BOOT_DEVICE_IPV4: case BOOT_DEVICE_HDD: case BOOT_DEVICE_CDROM: case BOOT_DEVICE_OTHERS: case BOOT_DEVICE_IPV6: case BOOT_DEVICE_RESERVED: vaild = true; break; default: break; } return vaild; } int pal_set_boot_order(uint8_t slot, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) { int i, j, network_dev = 0; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; *res_len = 0; sprintf(key, "server_boot_order"); for (i = 0; i < SIZE_BOOT_ORDER; i++) { //Byte 0 is boot mode, Byte 1~5 is boot order if ((i > 0) && (boot[i] != 0xFF)) { if(!pal_check_boot_device_is_vaild(boot[i])) return CC_INVALID_PARAM; for (j = i+1; j < SIZE_BOOT_ORDER; j++) { if ( boot[i] == boot[j]) return CC_INVALID_PARAM; } //If Bit 2:0 is 001b (Network), Bit3 is IPv4/IPv6 order //Bit3=0b: IPv4 first //Bit3=1b: IPv6 first if ( boot[i] == BOOT_DEVICE_IPV4 || boot[i] == BOOT_DEVICE_IPV6) network_dev++; } snprintf(tstr, sizeof(tstr), "%02x", boot[i]); strncat(str, tstr, sizeof(str) - 1); } //Not allow having more than 1 network boot device in the boot order. if (network_dev > 1) return CC_INVALID_PARAM; return pal_set_key_value(key, str); } int pal_get_boot_order(uint8_t slot, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) { int i; int j = 0; int ret; int msb, lsb; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; sprintf(key, "server_boot_order"); ret = pal_get_key_value(key, str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2*SIZE_BOOT_ORDER; i += 2) { sprintf(tstr, "%c\n", str[i]); msb = strtol(tstr, NULL, 16); sprintf(tstr, "%c\n", str[i+1]); lsb = strtol(tstr, NULL, 16); boot[j++] = (msb << 4) | lsb; } *res_len = SIZE_BOOT_ORDER; return 0; } // Get ME Firmware Version int pal_get_me_fw_ver(uint8_t bus, uint8_t addr, uint8_t *ver) { ipmi_dev_id_t dev_id; NM_RW_INFO info; int ret; info.bus = bus; info.nm_addr = addr; ret = pal_get_bmc_ipmb_slave_addr(&info.bmc_addr, info.bus); if (ret != 0) { return ret; } ret = cmd_NM_get_dev_id(&info, &dev_id); if (ret != 0) { return ret; } /* Major version number: byte 4[6:0] Minor version number: high 4 bits of byte 5 Milestone version number: low 4 bits of byte 5 Build version number: byte 14 and byte 15 dev_id.fw_rev1 = byte 4 dev_id.fw_rev2 = byte 5 dev_id.aux_fw_rev[1] = byte 14 dev_id.aux_fw_rev[2] = byte 15 */ ver[0] = dev_id.fw_rev1 & 0x7F; ver[1] = dev_id.fw_rev2 >> 4; ver[2] = dev_id.fw_rev2 & 0x0f; ver[3] = dev_id.aux_fw_rev[1]; ver[4] = dev_id.aux_fw_rev[2] >> 4; return ret; } // GUID for System and Device static int pal_get_guid(uint16_t offset, char *guid) { int fd; ssize_t bytes_rd; char FRU_EEPROM_MB[64]; fru_eeprom_mb_check(FRU_EEPROM_MB); errno = 0; // check for file presence if (access(FRU_EEPROM_MB, F_OK)) { syslog(LOG_ERR, "pal_get_guid: unable to access %s: %s", FRU_EEPROM_MB, strerror(errno)); return errno; } fd = open(FRU_EEPROM_MB, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "pal_get_guid: unable to open %s: %s", FRU_EEPROM_MB, strerror(errno)); return errno; } lseek(fd, offset, SEEK_SET); bytes_rd = read(fd, guid, GUID_SIZE); if (bytes_rd != GUID_SIZE) { syslog(LOG_ERR, "pal_get_guid: read from %s failed: %s", FRU_EEPROM_MB, strerror(errno)); } close(fd); return errno; } static int pal_set_guid(uint16_t offset, char *guid) { int fd; ssize_t bytes_wr; char FRU_EEPROM_MB[64]; fru_eeprom_mb_check(FRU_EEPROM_MB); errno = 0; // check for file presence if (access(FRU_EEPROM_MB, F_OK)) { syslog(LOG_ERR, "pal_set_guid: unable to access %s: %s", FRU_EEPROM_MB, strerror(errno)); return errno; } fd = open(FRU_EEPROM_MB, O_WRONLY); if (fd < 0) { syslog(LOG_ERR, "pal_set_guid: unable to open %s: %s", FRU_EEPROM_MB, strerror(errno)); return errno; } lseek(fd, offset, SEEK_SET); bytes_wr = write(fd, guid, GUID_SIZE); if (bytes_wr != GUID_SIZE) { syslog(LOG_ERR, "pal_set_guid: write to %s failed: %s", FRU_EEPROM_MB, strerror(errno)); } close(fd); return errno; } int pal_get_sys_guid(uint8_t fru, char *guid) { pal_get_guid(OFFSET_SYS_GUID, guid); return 0; } int pal_set_sys_guid(uint8_t fru, char *str) { char guid[GUID_SIZE] = {0}; pal_populate_guid(guid, str); return pal_set_guid(OFFSET_SYS_GUID, guid); } int pal_get_dev_guid(uint8_t fru, char *guid) { pal_get_guid(OFFSET_DEV_GUID, guid); return 0; } 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); } int pal_get_fru_list(char *list) { strcpy(list, pal_fru_list); return 0; } int pal_get_board_rev_id(uint8_t *id) { char value[MAX_VALUE_LEN] = {0}; if (kv_get("mb_rev", value, NULL, 0)) { return -1; } *id = (uint8_t)atoi(value); 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; uint8_t platform_id = 0x00; uint8_t board_rev_id = 0x00; int completion_code=CC_UNSPECIFIED_ERROR; ret = pal_get_platform_id(&platform_id); if (ret) { *res_len = 0x00; return completion_code; } ret = pal_get_board_rev_id(&board_rev_id); if (ret) { *res_len = 0x00; return completion_code; } // Prepare response buffer completion_code = CC_SUCCESS; res_data[0] = platform_id; res_data[1] = board_rev_id; *res_len = 0x02; return completion_code; } 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]; int i, comp_code = CC_SUCCESS; *res_len = 0; if (req_len > SIZE_CPU_PPIN*MAX_CPU_NUM) req_len = SIZE_CPU_PPIN*MAX_CPU_NUM; for (i = 0; i < req_len; i++) { sprintf(&str[(i%MAX_CPU_NUM)*2], "%02x", req_data[i]); if (!((i+1)%MAX_CPU_NUM)) { sprintf(key, "cpu%d_ppin", i/MAX_CPU_NUM); if (pal_set_key_value(key, str)) { comp_code = CC_UNSPECIFIED_ERROR; break; } } } return comp_code; } bool pal_skip_access_me(void) { if (!access("/tmp/fin_bios_upd", F_OK)) { return true; } return false; } int pal_get_nm_selftest_result(uint8_t fruid, uint8_t *data) { NM_RW_INFO info; uint8_t rbuf[8]; uint8_t rlen; int ret; // If device is slave, fake the data of selftest, // which is {0x55, 0x00} refer common/healthd.c if (!pal_get_config_is_master() || pal_skip_access_me()){ data[0] = 0x55; data[1] = 0x00; return PAL_EOK; } info.bus = NM_IPMB_BUS_ID; info.nm_addr = NM_SLAVE_ADDR; ret = pal_get_bmc_ipmb_slave_addr(&info.bmc_addr, info.bus); if (ret != 0) { return PAL_ENOTSUP; } ret = cmd_NM_get_self_test_result(&info, rbuf, &rlen); if (ret != 0) { return PAL_ENOTSUP; } memcpy(data, rbuf, rlen); #ifdef DEBUG syslog(LOG_WARNING, "rbuf[0] =%x rbuf[1] =%x\n", rbuf[0], rbuf[1]); #endif return ret; } static int get_dev_bridge_info(uint8_t slot, uint8_t* dev_addr, uint8_t* bus_num, uint16_t* bmc_addr) { int ret=0; switch(slot) { case BYPASS_ME: *dev_addr = NM_SLAVE_ADDR; *bus_num = NM_IPMB_BUS_ID; break; case BRIDGE_2_CM: *dev_addr = CM_SLAVE_ADDR; *bus_num = CM_IPMB_BUS_ID; break; case BRIDGE_2_MB_BMC0: //BMC IPMB SLVAE ADDR MB0 0x20 *dev_addr = BMC0_SLAVE_DEF_ADDR; *bus_num = BMC_IPMB_BUS_ID; break; case BRIDGE_2_MB_BMC1: //BMC IPMB SLVAE ADDR MB1:0x22 *dev_addr = BMC1_SLAVE_DEF_ADDR; *bus_num = BMC_IPMB_BUS_ID; break; case BRIDGE_2_MB_BMC2: //BMC IPMB SLVAE ADDR MB2:0x24 *dev_addr = BMC2_SLAVE_DEF_ADDR; *bus_num = BMC_IPMB_BUS_ID; break; case BRIDGE_2_MB_BMC3: //BMC IPMB SLVAE ADDR MB3:0x26 *dev_addr = BMC3_SLAVE_DEF_ADDR; *bus_num = BMC_IPMB_BUS_ID; break; case BRIDGE_2_ASIC_BMC: *dev_addr = ASIC_BMC_SLAVE_ADDR; *bus_num = ASIC_IPMB_BUS_ID; break; case BRIDGE_2_IOX_BMC: *dev_addr = IOX_BMC_SLAVE_ADDR; *bus_num = IOX_IPMB_BUS_ID; break; default: return -1; } ret = pal_get_bmc_ipmb_slave_addr(bmc_addr, *bus_num); if(ret != 0) { return -1; } return 0; } static int pal_ipmb_bypass (uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { int ret; uint8_t slot, netfn, cmd; uint8_t dev_addr, bus_num; uint8_t txlen, rxlen; uint8_t txbuf[256] = {0x00}; uint8_t rxbuf[256] = {0x00}; uint16_t bmc_addr; //payload, netfn, cmd, data[0]:select, data[1]:bypass netfn, data[2]:bypass cmd txlen = req_len - 6; slot = req_data[0]; netfn = req_data[1]; cmd = req_data[2]; ret = get_dev_bridge_info(slot, &dev_addr, &bus_num, &bmc_addr); if(ret != 0) { return CC_OEM_DEVICE_INFO_ERR; } //Don't allow bridge to self if(bmc_addr << 1 == dev_addr) { return CC_OEM_DEVICE_DESTINATION_ERR; } memcpy(txbuf, &req_data[3], txlen); ret = lib_ipmb_send_request(cmd, netfn, txbuf, txlen, rxbuf, &rxlen, bus_num, dev_addr, bmc_addr); if(ret != CC_SUCCESS) { return ret; } memcpy(&res_data[0], &rxbuf[0], rxlen); *res_len = rxlen; return CC_SUCCESS; } static int pal_ncsi_bypass (uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t cmd; uint8_t channel = 0; uint8_t netdev = 0; int cc=CC_UNSPECIFIED_ERROR; NCSI_NL_MSG_T *msg = NULL; NCSI_NL_RSP_T *rsp = NULL; if (req_len < 7) { // payload_id, netfn, cmd, data[0] (select), netdev, channel, cmd return CC_INVALID_LENGTH; } 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__); return cc; } 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 = req_len - 7; for (int i=0; i<msg->payload_length; i++) { msg->msg_payload[i] = req_data[4+i]; } 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; free(rsp); cc = CC_SUCCESS; } else { cc = CC_UNSPECIFIED_ERROR; } free(msg); return cc; } // OEM Command "CMD_OEM_BYPASS_CMD" 0x34 return: CC Code 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; uint8_t select; uint8_t status; *res_len = 0; ret = pal_is_fru_prsnt(FRU_MB, &status); if (ret < 0) { return CC_OEM_DEVICE_NOT_PRESENT; } if (status == 0) { return CC_UNSPECIFIED_ERROR; } select = req_data[0]; switch (select) { case BYPASS_ME: //ME case BRIDGE_2_CM: //Chassis Manager case BRIDGE_2_MB_BMC0: //MB BMC0 case BRIDGE_2_MB_BMC1: //MB BMC1 case BRIDGE_2_MB_BMC2: //MB BMC2 case BRIDGE_2_MB_BMC3: //MB BMC3 case BRIDGE_2_ASIC_BMC: //FBEP case BRIDGE_2_IOX_BMC: //FBCC ret = pal_ipmb_bypass(req_data, req_len, res_data, res_len); break; case BYPASS_NCSI: ret = pal_ncsi_bypass(req_data, req_len, res_data, res_len); break; default: return CC_UNSPECIFIED_ERROR; } if(ret != CC_SUCCESS) { return ret; } return CC_SUCCESS; } int pal_convert_to_dimm_str(uint8_t cpu, uint8_t channel, uint8_t slot, char *str) { uint8_t idx; uint8_t cpu_num; char label[] = {'A','C','B','D'}; cpu_num = cpu%2; if ((idx = cpu_num*2+slot) < sizeof(label)) { sprintf(str, "%c%d", label[idx], channel); } else { sprintf(str, "NA"); } return 0; } int pal_handle_dcmi(uint8_t fru, uint8_t *request, uint8_t req_len, uint8_t *response, uint8_t *rlen) { NM_RW_INFO info; int ret; info.bus = NM_IPMB_BUS_ID; info.nm_addr = NM_SLAVE_ADDR; ret = pal_get_bmc_ipmb_slave_addr(&info.bmc_addr, info.bus); if (ret != 0) { return PAL_ENOTSUP; } return lib_dcmi_wrapper(&info, request, req_len, response, rlen); } int pal_get_cpu_amount(uint8_t* amount) { static uint8_t cache_amount; static bool cached = false; uint8_t mode; int ret; if(!cached) { //Get Config ret = pal_get_host_system_mode(&mode); if (ret != 0) { syslog(LOG_WARNING,"%s Wrong get system mode\n", __func__); return ret; } if( mode == MB_2S_MODE ) { cache_amount = 2; } else if( (mode == MB_4S_EX_MODE || mode == MB_4S_EP_MODE) ) { cache_amount = 4; } else { cache_amount = 0; } } *amount = cache_amount; return 0; } int pal_get_dimm_amount(uint8_t* amount) { static uint8_t cache_amount; static bool cached = false; uint8_t mode; int ret; if(!cached) { //Get Config ret = pal_get_host_system_mode(&mode); if (ret != 0) { syslog(LOG_WARNING,"%s Wrong get system mode\n", __func__); return ret; } if( mode == MB_2S_MODE ) { cache_amount = 24; } else if( (mode == MB_4S_EX_MODE || mode == MB_4S_EP_MODE) ) { cache_amount = 48; } else { cache_amount = 0; } } *amount = cache_amount; // syslog(LOG_DEBUG,"DIMM Number=%d\n", cache_amount); return 0; } bool is_cpu_socket_occupy(uint8_t cpu_idx) { static bool cached = false; static unsigned int cached_id = 0; if (!cached) { const char *shadows[] = { "FM_CPU0_SKTOCC_LVT3_PLD_N", "FM_CPU1_SKTOCC_LVT3_PLD_N" }; if (gpio_get_value_by_shadow_list(shadows, ARRAY_SIZE(shadows), &cached_id)) { return false; } cached = true; } // bit == 1 implies CPU is absent. if (cached_id & (1 << cpu_idx)) { return false; } return true; } int pal_get_syscfg_text(char *text) { int cnt=0; char key[MAX_KEY_LEN], value[MAX_VALUE_LEN], entry[MAX_VALUE_LEN]; char *key_prefix = "sys_config/"; char *buf = NULL; char str[16]; uint8_t cpu_num=0; uint8_t cpu_index=0; uint8_t cpu_core_num=0; float cpu_speed=0; uint8_t dimm_num=0; uint8_t dimm_index=0; uint16_t dimm_speed=0; uint32_t dimm_capacity=0; size_t ret; static char *dimm_label[24] = { "A0", "C0", "A1", "C1", "A2", "C2", "A3", "C3", "A4", "C4", "A5", "C5", "B0", "D0", "B1", "D1", "B2", "D2", "B3", "D3", "B4", "D4", "B5", "D5" }; if (text == NULL) return -1; // Clear string buffer text[0] = '\0'; if(pal_get_cpu_amount(&cpu_num)) { return -1; } if(pal_get_dimm_amount(&dimm_num)) { return -1; } // CPU information for (cpu_index = 0; cpu_index < cpu_num; cpu_index++) { if(cpu_num == 2) { if (!is_cpu_socket_occupy(cpu_index)) continue; } sprintf(entry, "CPU%d:", cpu_index); // Processor# snprintf(key, MAX_KEY_LEN, "%sfru1_cpu%d_product_name", key_prefix, cpu_index); if (kv_get(key, value, &ret, KV_FPERSIST) == 0 ) { cnt = 0; buf = strtok(value, " "); while( buf != NULL ) { if(cnt == 3) { //Full Name // strncpy(str, buf, sizeof(str)); snprintf(&entry[strlen(entry)], sizeof(str), "%s", buf); break; } cnt++; buf = strtok(NULL, " "); } } // Frequency & Core Number snprintf(key, MAX_KEY_LEN, "%sfru1_cpu%d_basic_info", key_prefix, cpu_index); if(kv_get(key, value, &ret, KV_FPERSIST) == 0 && ret >= 5) { cpu_speed = (float)(value[4] << 8 | value[3])/1000; cpu_core_num = value[0]; sprintf(&entry[strlen(entry)], "/%.1fG/%dc", cpu_speed, cpu_core_num); } sprintf(&entry[strlen(entry)], "\n"); strcat(text, entry); } for (dimm_index=0; dimm_index<dimm_num; dimm_index++) { // 2S:DIMM=24, 4S:DIMM=48; sprintf(entry, "CPU%d_MEM%s:", dimm_index/12, dimm_label[dimm_index%24]); // Check Present snprintf(key, MAX_KEY_LEN, "%sfru1_dimm%d_location", key_prefix, dimm_index); if(kv_get(key, value, &ret, KV_FPERSIST) == 0 && ret >= 1) { //syslog(LOG_DEBUG, "CPU%d_MEM%s: Present=%d", dimm_index/12, dimm_label[dimm_index%24], value[0]); // Skip if not present if (value[0] != 0x01) continue; } // Module Manufacturer ID snprintf(key, MAX_KEY_LEN, "%sfru1_dimm%d_manufacturer_id", key_prefix, dimm_index); if(kv_get(key, value, &ret, KV_FPERSIST) == 0 && ret >= 2) { switch (value[1]) { case 0xce: sprintf(&entry[strlen(entry)], "Samsung"); break; case 0xad: sprintf(&entry[strlen(entry)], "Hynix"); break; case 0x2c: sprintf(&entry[strlen(entry)], "Micron"); break; default: sprintf(&entry[strlen(entry)], "unknown"); break; } } // Speed snprintf(key, MAX_KEY_LEN, "%sfru1_dimm%d_speed",key_prefix, dimm_index); if(kv_get(key, value, &ret, KV_FPERSIST) == 0 && ret >= 6) { dimm_speed = value[1]<<8 | value[0]; dimm_capacity = (value[5]<<24 | value[4]<<16 | value[3]<<8 | value[2])/1024; sprintf(&entry[strlen(entry)], "%dMhz/%uGB", dimm_speed, dimm_capacity); } sprintf(&entry[strlen(entry)], "\n"); strcat(text, entry); } return 0; } void pal_get_eth_intf_name(char* intf_name) { char path[64] = {0}; DIR *dir; snprintf(path, sizeof(path), ETHERNET_BR0_INTERFACE_PATH); dir = opendir(path); if (dir != NULL) { snprintf(intf_name, 8, "br0"); closedir(dir); } else { snprintf(intf_name, 8, "eth0"); } } void pal_set_post_end(uint8_t slot, uint8_t *req_data, uint8_t *res_data, uint8_t *res_len) { // log the post end event syslog (LOG_INFO, "POST End Event for Payload#%d\n", slot); // Sync time with system if (system("/etc/init.d/sync_date.sh &") != 0) { syslog(LOG_ERR, "Sync date failed!\n"); } else { syslog(LOG_INFO, "Sync date success!\n"); } } int pal_get_target_bmc_addr(uint8_t *tar_bmc_addr) { uint16_t m_bmc_addr; if ( pal_get_bmc_ipmb_slave_addr(&m_bmc_addr, BMC_IPMB_BUS_ID) ) return -1; if (m_bmc_addr == 0x10) *tar_bmc_addr = BMC1_SLAVE_DEF_ADDR; else *tar_bmc_addr = BMC0_SLAVE_DEF_ADDR; return 0; } int pal_get_sensor_util_timeout(uint8_t fru) { if ( fru == FRU_MB ) { return 10; } else { return 4; } } static int get_cpld_version(char* str, uint8_t addr, uint8_t bus, uint32_t offset, uint8_t* rbuf) { int fd = 0, ret = -1; uint8_t tlen, rlen; uint8_t tbuf[16] = {0}; uint8_t ver[16] = {0}; long rev; char value[MAX_VALUE_LEN] = {0}; if( kv_get(str, value, 0, 0) ) { fd = i2c_cdev_slave_open(bus, addr >> 1, I2C_SLAVE_FORCE_CLAIM); if (fd < 0) { syslog(LOG_WARNING, "%s() Failed to open %d", __func__, bus); return ret; } tbuf[0] = (offset >> 24 ) & 0xFF; tbuf[1] = (offset >> 16 ) & 0xFF; tbuf[2] = (offset >> 8 ) & 0xFF; tbuf[3] = (offset >> 0 ) & 0xFF; tlen = 4; rlen = 4; ret = i2c_rdwr_msg_transfer(fd, addr, tbuf, tlen, ver, rlen); i2c_cdev_slave_close(fd); if (ret == -1) { syslog(LOG_WARNING, "%s bus=%x slavaddr=%x offset=%x\n", __func__, bus, addr >> 1, offset); return ret; } else { sprintf(value, "%02x%02x%02x%02x", ver[3], ver[2], ver[1], ver[0]); kv_set(str, value, 0, 0); rbuf[0] = ver[3]; rbuf[1] = ver[2]; rbuf[2] = ver[1]; rbuf[3] = ver[0]; } } else { rev = strtol(value, NULL, 16); rbuf[0] = (rev >> 24) & 0xff; rbuf[1] = (rev >> 16) & 0xff; rbuf[2] = (rev >> 8) & 0xff; rbuf[3] = (rev >> 0) & 0xff; } return 0; } int pal_get_fw_info(uint8_t fru, unsigned char target, unsigned char* res, unsigned char* res_len) { int ret = -1; uint8_t rbuf[16] = {0}; char str[16] = {0}; if( fru != FRU_MB ) return -1; switch (target) { case CMD_GET_MAIN_CPLD_VER: strncpy(str, "pfr_cpld", sizeof(str)); ret = get_cpld_version(str, MAIN_CPLD_SLV_ADDR, MAIN_CPLD_BUS_NUM, CPLD_VER_REG, rbuf); break; case CMD_GET_MOD_CPLD_VER: strncpy(str, "mod_cpld", sizeof(str)); ret = get_cpld_version(str, MOD_CPLD_SLV_ADDR, MOD_CPLD_BUS_NUM, CPLD_VER_REG, rbuf); break; case CMD_GET_GLB_CPLD_VER: strncpy(str, "glb_cpld", sizeof(str)); ret = get_cpld_version(str, GLB_CPLD_SLV_ADDR, GLB_CPLD_BUS_NUM, CPLD_VER_REG, rbuf); break; default: return -1; } if( ret == 0 ) { memcpy(res, rbuf, 4); *res_len = 4; } return ret; } void __attribute__((constructor)) update_local_fruid(void) { if (!pal_get_config_is_master()) { FRU_MB = FRU_TRAY1_MB; FRU_NIC0 = FRU_TRAY1_NIC0; FRU_NIC1 = FRU_TRAY1_NIC1; FRU_BMC = FRU_TRAY1_BMC; } } int pal_get_fru_capability(uint8_t fru, unsigned int *caps) { int ret = 0; switch (fru) { case FRU_TRAY0_MB: case FRU_TRAY1_MB: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SERVER | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_POWER_ALL; break; case FRU_TRAY0_NIC0: case FRU_TRAY0_NIC1: case FRU_TRAY1_NIC0: case FRU_TRAY1_NIC1: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_NETWORK_CARD; break; case FRU_PDB: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL; break; case FRU_DBG: *caps = FRU_CAPABILITY_SENSOR_ALL; break; case FRU_TRAY0_BMC: case FRU_TRAY1_BMC: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_MANAGEMENT_CONTROLLER; break; default: ret = -1; break; } return ret; } int pal_get_dev_capability(uint8_t fru, uint8_t dev, unsigned int *caps) { return -1; } // Variable addr would be 8-bit form int pal_i2c_write_read (uint8_t bus, uint8_t addr, uint8_t *txbuf, uint8_t txlen, uint8_t *rxbuf, uint8_t rxlen) { int fd = 0, retCode = -1; fd = i2c_cdev_slave_open (bus, addr >> 1, I2C_SLAVE_FORCE_CLAIM); if (fd < 0) { syslog(LOG_WARNING, "Failed to open i2c-%d: %s", bus, strerror(errno)); return retCode; } retCode = i2c_rdwr_msg_transfer (fd, addr, txbuf, txlen, rxbuf, rxlen); if (retCode == -1) { syslog (LOG_WARNING, "i2c transaction error %s bus=%x slavaddr=%x offset=%x\n", __func__, bus, addr >> 1, txbuf[0]); } else { retCode = (int)rxbuf[0]; } i2c_cdev_slave_close(fd); return retCode; }