meta-facebook/meta-cloudripper/recipes-cloudripper/platform-lib/files/pal/pal.c (881 lines of code) (raw):

/* * * Copyright 2020-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. */ /* * This file contains functions and logics that depends on Cloudripper specific * hardware and kernel drivers. Here, some of the empty "default" functions * are overridden with simple functions that returns non-zero error code. * This is for preventing any potential escape of failures through the * default functions that will return 0 no matter what. */ // #define DEBUG #include <ctype.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/time.h> #include <time.h> #include <unistd.h> #include <pthread.h> #include <math.h> #include <openbmc/log.h> #include <openbmc/libgpio.h> #include <openbmc/kv.h> #include <openbmc/obmc-i2c.h> #include <openbmc/sensor-correction.h> #include <openbmc/misc-utils.h> #include <facebook/bic.h> #include <facebook/wedge_eeprom.h> #include "pal.h" uint8_t g_dev_guid[GUID_SIZE] = {0}; struct threadinfo { uint8_t is_running; uint8_t fru; pthread_t pt; }; static struct threadinfo t_dump[MAX_NUM_FRUS] = {0}; const char pal_fru_list[] = "all, scm, smb, "\ "psu1, psu2, fan1, fan2, fan3, fan4 "; char *key_list[] = { "pwr_server_last_state", "sysfw_ver_server", "timestamp_sled", "server_por_cfg", "server_sel_error", "scm_sensor_health", "smb_sensor_health", "psu1_sensor_health", "psu2_sensor_health", "fan1_sensor_health", "fan2_sensor_health", "fan3_sensor_health", "fan4_sensor_health", "server_boot_order", /* Add more Keys here */ LAST_KEY /* This is the last key of the list */ }; char *def_val_list[] = { "on", /* pwr_server_last_state */ "0", /* sysfw_ver_server */ "0", /* timestamp_sled */ "lps", /* server_por_cfg */ "1", /* server_sel_error */ "1", /* scm_sensor_health */ "1", /* smb_sensor_health */ "1", /* psu1_sensor_health */ "1", /* psu2_sensor_health */ "1", /* fan1_sensor_health */ "1", /* fan2_sensor_health */ "1", /* fan3_sensor_health */ "1", /* fan4_sensor_health */ "0000000",/* server_boot_order */ /* Add more def values for the correspoding keys*/ LAST_KEY /* Same as last entry of the key_list */ }; void pal_inform_bic_mode(uint8_t fru, uint8_t mode) { switch(mode) { case BIC_MODE_NORMAL: // Bridge IC entered normal mode // Inform BIOS that BMC is ready bic_set_gpio(fru, BMC_READY_N, 0); break; case BIC_MODE_UPDATE: // Bridge IC entered update mode // TODO: Might need to handle in future break; default: break; } } static int pal_key_check(char *key) { int i = 0; while(strcmp(key_list[i], LAST_KEY)) { // If Key is valid, return success if (!strcmp(key, key_list[i])) return 0; i++; } PAL_DEBUG("pal_key_check: invalid key - %s", key); return -1; } int pal_get_key_value(char *key, char *value) { int ret; // Check is key is defined and valid if (pal_key_check(key)) return -1; ret = kv_get(key, value, NULL, KV_FPERSIST); return ret; } int pal_set_key_value(char *key, char *value) { // Check is key is defined and valid if (pal_key_check(key)) return -1; return kv_set(key, value, 0, KV_FPERSIST); } int pal_get_fru_list(char *list) { strcpy(list, pal_fru_list); return 0; } int pal_get_fru_capability(uint8_t fru, unsigned int *caps) { int ret = 0; switch(fru) { case FRU_SMB: case FRU_PSU1: case FRU_PSU2: case FRU_FAN1: case FRU_FAN2: case FRU_FAN3: case FRU_FAN4: case FRU_CPLD: case FRU_FPGA: *caps = FRU_CAPABILITY_SENSOR_ALL; break; case FRU_SCM: *caps = FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_POWER_ALL; break; case FRU_BMC: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_MANAGEMENT_CONTROLLER; break; default: ret = -1; break; } return ret; } int pal_get_fru_id(char *str, uint8_t *fru) { if (!strcmp(str, "all")) { *fru = FRU_ALL; } else if (!strcmp(str, "smb")) { *fru = FRU_SMB; } else if (!strcmp(str, "scm")) { *fru = FRU_SCM; } else if (!strcmp(str, "psu1")) { *fru = FRU_PSU1; } else if (!strcmp(str, "psu2")) { *fru = FRU_PSU2; } else if (!strcmp(str, "fan1")) { *fru = FRU_FAN1; } else if (!strcmp(str, "fan2")) { *fru = FRU_FAN2; } else if (!strcmp(str, "fan3")) { *fru = FRU_FAN3; } else if (!strcmp(str, "fan4")) { *fru = FRU_FAN4; } else if (!strcmp(str, "bmc")) { *fru = FRU_BMC; } else if (!strcmp(str, "cpld")) { *fru = FRU_CPLD; } else if (!strcmp(str, "fpga")) { *fru = FRU_FPGA; } else { OBMC_WARN("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_SMB: strcpy(name, "smb"); break; case FRU_SCM: strcpy(name, "scm"); break; case FRU_PSU1: strcpy(name, "psu1"); break; case FRU_PSU2: strcpy(name, "psu2"); break; case FRU_FAN1: strcpy(name, "fan1"); break; case FRU_FAN2: strcpy(name, "fan2"); break; case FRU_FAN3: strcpy(name, "fan3"); break; case FRU_FAN4: strcpy(name, "fan4"); break; default: if (fru > MAX_NUM_FRUS) return -1; sprintf(name, "fru%d", fru); break; } return 0; } // Platform Abstraction Layer (PAL) Functions int pal_get_platform_name(char *name) { strcpy(name, PLATFORM_NAME); return 0; } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { int val; char tmp[LARGEST_DEVICE_NAME]; char path[LARGEST_DEVICE_NAME + 1]; *status = 0; switch (fru) { case FRU_SMB: *status = 1; return 0; case FRU_SCM: snprintf(path, LARGEST_DEVICE_NAME, SMB_SYSFS, SCM_PRSNT_STATUS); break; case FRU_PSU1: case FRU_PSU2: snprintf(tmp, LARGEST_DEVICE_NAME, SMB_SYSFS, PSU_PRSNT_STATUS); snprintf(path, LARGEST_DEVICE_NAME, tmp, fru - FRU_PSU1 + 1); break; case FRU_FAN1: case FRU_FAN2: case FRU_FAN3: case FRU_FAN4: snprintf(tmp, LARGEST_DEVICE_NAME, FCM_SYSFS, FAN_PRSNT_STATUS); snprintf(path, LARGEST_DEVICE_NAME, tmp, fru - FRU_FAN1 + 1); break; default: OBMC_INFO("unsupported fru id %d", fru); return -1; } if (device_read(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; return 0; } return 0; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { int ret = 0; switch(fru) { default: *status = 1; break; } return ret; } void pal_update_ts_sled() { char key[MAX_KEY_LEN]; 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_is_debug_card_prsnt(uint8_t *status) { int val; char path[LARGEST_DEVICE_NAME + 1]; snprintf(path, LARGEST_DEVICE_NAME, GPIO_DEBUG_PRSNT_N, "value"); if (device_read(path, &val)) { return -1; } if (val) { *status = 1; } else { *status = 0; } return 0; } // Return the Front panel Power Button int pal_get_board_rev(int *rev) { char path[LARGEST_DEVICE_NAME + 1]; snprintf(path, sizeof(path), SMB_SYSFS, "board_ver"); if (device_read(path, rev)) { return -1; } return 0; } int pal_get_board_type(uint8_t *brd_type) { *brd_type = BOARD_TYPE_CLOUDRIPPER; return CC_SUCCESS; } int pal_get_board_type_rev(uint8_t *brd_type_rev) { *brd_type_rev = BOARD_CLOUDRIPPER; return 0; } int pal_get_cpld_board_rev(int *rev, const char *device) { char full_name[LARGEST_DEVICE_NAME + 1]; snprintf(full_name, LARGEST_DEVICE_NAME, device, "board_ver"); if (device_read(full_name, rev)) { return -1; } return 0; } int pal_get_cpld_fpga_fw_ver(uint8_t fru, const char *device, uint8_t* ver) { int val = -1; char ver_path[PATH_MAX]; char sub_ver_path[PATH_MAX]; switch(fru) { case FRU_CPLD: if (!(strncmp(device, SCM_CPLD, strlen(SCM_CPLD)))) { snprintf(ver_path, sizeof(ver_path), SCM_SYSFS, "cpld_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), SCM_SYSFS, "cpld_sub_ver"); } else if (!(strncmp(device, SMB_CPLD, strlen(SMB_CPLD)))) { snprintf(ver_path, sizeof(ver_path), SMB_SYSFS, "cpld_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), SMB_SYSFS, "cpld_sub_ver"); } else if (!(strncmp(device, PWR_CPLD, strlen(PWR_CPLD)))) { snprintf(ver_path, sizeof(ver_path), PWR_SYSFS, "cpld_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), PWR_SYSFS, "cpld_sub_ver"); } else if (!(strncmp(device, FCM_CPLD, strlen(FCM_CPLD)))) { snprintf(ver_path, sizeof(ver_path), FCM_SYSFS, "cpld_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), FCM_SYSFS, "cpld_sub_ver"); } else { return -1; } break; case FRU_FPGA: if (!(strncmp(device, DOM_FPGA1, strlen(DOM_FPGA1)))) { snprintf(ver_path, sizeof(ver_path), DOMFPGA1_SYSFS, "fpga_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), DOMFPGA1_SYSFS, "fpga_sub_ver"); } else if (!(strncmp(device, DOM_FPGA2, strlen(DOM_FPGA2)))) { snprintf(ver_path, sizeof(ver_path), DOMFPGA2_SYSFS, "fpga_ver"); snprintf(sub_ver_path, sizeof(sub_ver_path), DOMFPGA2_SYSFS, "fpga_sub_ver"); } else { return -1; } break; default: return -1; } if (!device_read(ver_path, &val)) { ver[0] = (uint8_t)val; } else { return -1; } if (!device_read(sub_ver_path, &val)) { ver[1] = (uint8_t)val; } else { printf("[debug][ver:%s]\n", ver_path); printf("[debug][sub_ver:%s]\n", sub_ver_path); OBMC_INFO("[debug][ver:%s]\n", ver_path); OBMC_INFO("[debug][sub_ver:%s]\n", sub_ver_path); return -1; } return 0; } int pal_get_num_slots(uint8_t *num) { *num = MAX_NUM_SCM; return PAL_EOK; } void *generate_dump(void *arg) { uint8_t fru = *(uint8_t *) arg; char cmd[256]; char fname[128]; char fruname[16]; // Usually the pthread cancel state are enable by default but // here we explicitly would like to enable them pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); pal_get_fru_name(fru, fruname);//scm memset(fname, 0, sizeof(fname)); snprintf(fname, 128, "/var/run/autodump%d.pid", fru); if (access(fname, F_OK) == 0) { memset(cmd, 0, sizeof(cmd)); snprintf(cmd,sizeof(cmd),"rm %s",fname); if (system(cmd)) { OBMC_CRIT("Removing old crashdump: %s failed!\n", fname); } } // Execute automatic crashdump memset(cmd, 0, 128); sprintf(cmd, "%s %s", CRASHDUMP_BIN, fruname); if (system(cmd)) { OBMC_CRIT("Crashdump for FRU: %d failed.", fru); } else { OBMC_CRIT("Crashdump for FRU: %d is generated.", fru); } t_dump[fru-1].is_running = 0; return 0; } static int pal_store_crashdump(uint8_t fru) { int ret; char cmd[100]; // Check if the crashdump script exist if (access(CRASHDUMP_BIN, F_OK) == -1) { OBMC_CRIT("Crashdump for FRU: %d failed : " "auto crashdump binary is not preset", fru); return 0; } // Check if a crashdump for that fru is already running. // If yes, kill that thread and start a new one. if (t_dump[fru-1].is_running) { ret = pthread_cancel(t_dump[fru-1].pt); if (ret == ESRCH) { OBMC_INFO("pal_store_crashdump: No Crashdump pthread exists"); } else { pthread_join(t_dump[fru-1].pt, NULL); sprintf(cmd, "ps | grep '{dump.sh}' | grep 'scm' " "| awk '{print $1}'| xargs kill"); if (system(cmd)) { OBMC_INFO("Detection of existing crashdump failed!\n"); } sprintf(cmd, "ps | grep 'bic-util' | grep 'scm' " "| awk '{print $1}'| xargs kill"); if (system(cmd)) { OBMC_INFO("Detection of existing bic-util scm failed!\n"); } PAL_DEBUG("pal_store_crashdump: Previous crashdump thread is cancelled"); } } // Start a thread to generate the crashdump t_dump[fru-1].fru = fru; if (pthread_create(&(t_dump[fru-1].pt), NULL, generate_dump, (void*) &t_dump[fru-1].fru) < 0) { OBMC_WARN("pal_store_crashdump: pthread_create for" " FRU %d failed\n", fru); return -1; } t_dump[fru-1].is_running = 1; OBMC_INFO("Crashdump for FRU: %d is being generated.", fru); return 0; } int pal_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) { char key[MAX_KEY_LEN] = {0}; char cvalue[MAX_VALUE_LEN] = {0}; static int assert_cnt[MAX_NUM_SLOTS] = {0}; switch(fru) { case FRU_SCM: switch(snr_num) { case CATERR_B: pal_store_crashdump(fru); break; case 0x00: // don't care sensor number 00h return 0; } sprintf(key, "server_sel_error"); if ((event_data[2] & 0x80) == 0) { // 0: Assertion, 1: Deassertion assert_cnt[fru-1]++; } else { if (--assert_cnt[fru-1] < 0) assert_cnt[fru-1] = 0; } sprintf(cvalue, "%s", (assert_cnt[fru-1] > 0) ? "0" : "1"); break; default: return -1; } /* Write the value "0" which means FRU_STATUS_BAD */ return pal_set_key_value(key, cvalue); } int pal_mon_fw_upgrade(uint8_t *status) { char cmd[5]; FILE *fp; int ret=-1; char *buf_ptr; int buf_size = 1000; int str_size = 200; int tmp_size; char str[200]; snprintf(cmd, sizeof(cmd), "ps w"); fp = popen(cmd, "r"); if (NULL == fp) return -1; buf_ptr = (char *)malloc(buf_size * sizeof(char) + sizeof(char)); memset(buf_ptr, 0, sizeof(char)); tmp_size = str_size; while(fgets(str, str_size, fp) != NULL) { tmp_size = tmp_size + str_size; if (tmp_size + str_size >= buf_size) { buf_ptr = realloc(buf_ptr, sizeof(char) * buf_size * 2 + sizeof(char)); buf_size *= 2; } if (!buf_ptr) { OBMC_ERROR(-1, "%s realloc() fail, please check memory remaining", __func__); goto free_buf; } strncat(buf_ptr, str, str_size); } *status = strstr(buf_ptr, "spi_util.sh") != NULL ? 1 : 0; if (*status) goto close_fp; *status = (strstr(buf_ptr, "fw-util") != NULL) ? ((strstr(buf_ptr, "--update") != NULL) ? 1 : 0) : 0; if (*status) goto close_fp; *status = (strstr(buf_ptr, "psu-util") != NULL) ? ((strstr(buf_ptr, "--update") != NULL) ? 1 : 0) : 0; if (*status) goto close_fp; *status = (strstr(buf_ptr, "cpld_update.sh") != NULL) ? 1 : 0; if (*status) goto close_fp; *status = (strstr(buf_ptr, "flashcp") != NULL) ? 1 : 0; if (*status) goto close_fp; close_fp: ret = pclose(fp); if (-1 == ret) OBMC_ERROR(-1, "%s pclose() fail ", __func__); free_buf: free(buf_ptr); return 0; } int pal_set_def_key_value(void) { int i, ret; char path[LARGEST_DEVICE_NAME + 1]; for (i = 0; strcmp(key_list[i], LAST_KEY) != 0; i++) { snprintf(path, LARGEST_DEVICE_NAME, KV_PATH, key_list[i]); if ((ret = kv_set(key_list[i], def_val_list[i], 0, KV_FPERSIST | KV_FCREATE)) < 0) { PAL_DEBUG("pal_set_def_key_value: kv_set failed. %d", ret); } } return 0; } int pal_init_sensor_check(uint8_t fru, uint8_t snr_num, void *snr) { pal_set_def_key_value(); return 0; } int pal_get_fru_health(uint8_t fru, uint8_t *value) { char cvalue[MAX_VALUE_LEN] = {0}; char key[MAX_KEY_LEN] = {0}; int ret; switch(fru) { case FRU_SCM: sprintf(key, "scm_sensor_health"); break; case FRU_SMB: sprintf(key, "smb_sensor_health"); break; case FRU_PSU1: sprintf(key, "psu1_sensor_health"); break; case FRU_PSU2: sprintf(key, "psu2_sensor_health"); break; case FRU_FAN1: sprintf(key, "fan1_sensor_health"); break; case FRU_FAN2: sprintf(key, "fan2_sensor_health"); break; case FRU_FAN3: sprintf(key, "fan3_sensor_health"); break; case FRU_FAN4: sprintf(key, "fan4_sensor_health"); break; default: return -1; } ret = pal_get_key_value(key, cvalue); if (ret) { return ret; } *value = atoi(cvalue); *value = *value & atoi(cvalue); return 0; } int pal_set_sensor_health(uint8_t fru, uint8_t value) { char key[MAX_KEY_LEN] = {0}; char cvalue[MAX_VALUE_LEN] = {0}; switch(fru) { case FRU_SCM: sprintf(key, "scm_sensor_health"); break; case FRU_SMB: sprintf(key, "smb_sensor_health"); break; case FRU_PSU1: sprintf(key, "psu1_sensor_health"); break; case FRU_PSU2: sprintf(key, "psu2_sensor_health"); break; case FRU_FAN1: sprintf(key, "fan1_sensor_health"); break; case FRU_FAN2: sprintf(key, "fan2_sensor_health"); break; case FRU_FAN3: sprintf(key, "fan3_sensor_health"); break; case FRU_FAN4: sprintf(key, "fan4_sensor_health"); break; default: return -1; } sprintf(cvalue, (value > 0) ? "1": "0"); return pal_set_key_value(key, cvalue); } int pal_parse_sel(uint8_t fru, uint8_t *sel, char *error_log) { uint8_t snr_num = sel[11]; uint8_t *event_data = &sel[10]; uint8_t *ed = &event_data[3]; char temp_log[512] = {0}; uint8_t sen_type = event_data[0]; uint8_t chn_num, dimm_num; bool parsed = false; switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: strcpy(error_log, ""); switch (ed[0] & 0x0F) { case 0x00: strcat(error_log, "SOC_Thermal_Trip"); break; case 0x01: strcat(error_log, "SOC_FIVR_Fault"); break; case 0x02: strcat(error_log, "SOC_Throttle"); break; case 0x03: strcat(error_log, "PCH_HOT"); break; } parsed = true; break; case BIC_SENSOR_CPU_DIMM_HOT: strcpy(error_log, ""); switch (ed[0] & 0x0F) { case 0x01: strcat(error_log, "SOC_MEMHOT"); break; } parsed = true; break; case MEMORY_ECC_ERR: case MEMORY_ERR_LOG_DIS: strcpy(error_log, ""); if (snr_num == MEMORY_ECC_ERR) { // SEL from MEMORY_ECC_ERR Sensor if ((ed[0] & 0x0F) == 0x0) { if (sen_type == 0x0C) { strcat(error_log, "Correctable"); sprintf(temp_log, "DIMM%02X ECC err", ed[2]); pal_add_cri_sel(temp_log); } else if (sen_type == 0x10) strcat(error_log, "Correctable ECC error Logging Disabled"); } else if ((ed[0] & 0x0F) == 0x1) { strcat(error_log, "Uncorrectable"); sprintf(temp_log, "DIMM%02X UECC err", ed[2]); pal_add_cri_sel(temp_log); } else if ((ed[0] & 0x0F) == 0x5) strcat(error_log, "Correctable ECC error Logging Limit Reached"); else strcat(error_log, "Unknown"); } else { // SEL from MEMORY_ERR_LOG_DIS Sensor if ((ed[0] & 0x0F) == 0x0) strcat(error_log, "Correctable Memory Error Logging Disabled"); else strcat(error_log, "Unknown"); } // DIMM number (ed[2]): // Bit[7:5]: Socket number (Range: 0-7) // Bit[4:3]: Channel number (Range: 0-3) // Bit[2:0]: DIMM number (Range: 0-7) if (((ed[1] & 0xC) >> 2) == 0x0) { /* All Info Valid */ chn_num = (ed[2] & 0x18) >> 3; dimm_num = ed[2] & 0x7; /* If critical SEL logging is available, do it */ if (sen_type == 0x0C) { if ((ed[0] & 0x0F) == 0x0) { sprintf(temp_log, "DIMM%c%d ECC err,FRU:%u", 'A'+chn_num, dimm_num, fru); pal_add_cri_sel(temp_log); } else if ((ed[0] & 0x0F) == 0x1) { sprintf(temp_log, "DIMM%c%d UECC err,FRU:%u", 'A'+chn_num, dimm_num, fru); pal_add_cri_sel(temp_log); } } /* Then continue parse the error into a string. */ /* All Info Valid */ sprintf(temp_log, " DIMM %c%d Logical Rank %d (CPU# %d, CHN# %d, DIMM# %d)", 'A'+chn_num, dimm_num, ed[1] & 0x03, (ed[2] & 0xE0) >> 5, chn_num, dimm_num); } else if (((ed[1] & 0xC) >> 2) == 0x1) { /* DIMM info not valid */ sprintf(temp_log, " (CPU# %d, CHN# %d)", (ed[2] & 0xE0) >> 5, (ed[2] & 0x18) >> 3); } else if (((ed[1] & 0xC) >> 2) == 0x2) { /* CHN info not valid */ sprintf(temp_log, " (CPU# %d, DIMM# %d)", (ed[2] & 0xE0) >> 5, ed[2] & 0x7); } else if (((ed[1] & 0xC) >> 2) == 0x3) { /* CPU info not valid */ sprintf(temp_log, " (CHN# %d, DIMM# %d)", (ed[2] & 0x18) >> 3, ed[2] & 0x7); } strcat(error_log, temp_log); parsed = true; break; } if (parsed == true) { if ((event_data[2] & 0x80) == 0) { strcat(error_log, " Assertion"); } else { strcat(error_log, " Deassertion"); } return 0; } pal_parse_sel_helper(fru, sel, error_log); return 0; } static int platform_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) { switch(fru) { case FRU_SCM: switch(sensor_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SYSTEM_STATUS"); break; case BIC_SENSOR_SYS_BOOT_STAT: sprintf(name, "SYS_BOOT_STAT"); break; case BIC_SENSOR_CPU_DIMM_HOT: sprintf(name, "CPU_DIMM_HOT"); break; case BIC_SENSOR_PROC_FAIL: sprintf(name, "PROC_FAIL"); break; case BIC_SENSOR_VR_HOT: sprintf(name, "VR_HOT"); break; default: return -1; } break; } return 0; } 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]; // If SNR_TYPE is OS_BOOT, sensor name is OS switch (snr_type) { case OS_BOOT: // OS_BOOT used by OS sprintf(name, "OS"); return 0; default: if (platform_sensor_name(fru, snr_num, name) != 0) { break; } return 0; } // Otherwise, translate it based on snr_num return pal_get_x86_event_sensor_name(fru, snr_num, name); } // Write GUID into EEPROM static int pal_set_guid(uint16_t offset, char *guid) { int fd = 0; ssize_t bytes_wr; char eeprom_path[FBW_EEPROM_PATH_SIZE]; errno = 0; wedge_eeprom_path(eeprom_path); // Check for file presence if (access(eeprom_path, F_OK) == -1) { OBMC_ERROR(-1, "pal_set_guid: unable to access the %s file: %s", eeprom_path, strerror(errno)); return errno; } // Open file fd = open(eeprom_path, O_WRONLY); if (fd == -1) { OBMC_ERROR(-1, "pal_set_guid: unable to open the %s file: %s", eeprom_path, strerror(errno)); return errno; } // Seek the offset lseek(fd, offset, SEEK_SET); // Write GUID data bytes_wr = write(fd, guid, GUID_SIZE); if (bytes_wr != GUID_SIZE) { OBMC_ERROR(-1, "pal_set_guid: write to %s file failed: %s", eeprom_path, strerror(errno)); goto err_exit; } err_exit: close(fd); return errno; } // Read GUID from EEPROM static int pal_get_guid(uint16_t offset, char *guid) { int fd = 0; ssize_t bytes_rd; char eeprom_path[FBW_EEPROM_PATH_SIZE]; errno = 0; wedge_eeprom_path(eeprom_path); // Check if file is present if (access(eeprom_path, F_OK) == -1) { OBMC_ERROR(-1, "pal_get_guid: unable to access the %s file: %s", eeprom_path, strerror(errno)); return errno; } // Open the file fd = open(eeprom_path, O_RDONLY); if (fd == -1) { OBMC_ERROR(-1, "pal_get_guid: unable to open the %s file: %s", eeprom_path, strerror(errno)); return errno; } // seek to the offset lseek(fd, offset, SEEK_SET); // Read bytes from location bytes_rd = read(fd, guid, GUID_SIZE); if (bytes_rd != GUID_SIZE) { OBMC_ERROR(-1, "pal_get_guid: read to %s file failed: %s", eeprom_path, strerror(errno)); goto err_exit; } err_exit: close(fd); return errno; } int pal_get_dev_guid(uint8_t fru, char *guid) { pal_get_guid(OFFSET_DEV_GUID, (char *)g_dev_guid); memcpy(guid, g_dev_guid, GUID_SIZE); return 0; } int pal_set_dev_guid(uint8_t slot, char *guid) { pal_populate_guid(g_dev_guid, guid); return pal_set_guid(OFFSET_DEV_GUID, (char *)g_dev_guid); } int pal_get_boot_order(uint8_t slot, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) { int ret, msb, lsb, i, j = 0; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; ret = pal_get_key_value("server_boot_order", str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2*SIZE_BOOT_ORDER; i += 2) { snprintf(tstr, sizeof(tstr), "%c\n", str[i]); msb = strtol(tstr, NULL, 16); snprintf(tstr, sizeof(tstr), "%c\n", str[i+1]); lsb = strtol(tstr, NULL, 16); boot[j++] = (msb << 4) | lsb; } *res_len = SIZE_BOOT_ORDER; return 0; } int pal_set_boot_order(uint8_t slot, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) { int i, j, offset, network_dev = 0; char str[MAX_VALUE_LEN] = {0}; enum { BOOT_DEVICE_IPV4 = 0x1, BOOT_DEVICE_IPV6 = 0x9, }; *res_len = 0; for (i = offset = 0; i < SIZE_BOOT_ORDER && offset < sizeof(str); i++) { if (i > 0) { // byte[0] is boot mode, byte[1:5] are boot order 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), bit[3] is IPv4/IPv6 order // bit[3]=0b: IPv4 first // bit[3]=1b: IPv6 first if ((boot[i] == BOOT_DEVICE_IPV4) || (boot[i] == BOOT_DEVICE_IPV6)) network_dev++; } offset += snprintf(str + offset, sizeof(str) - offset, "%02x", boot[i]); } // 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("server_boot_order", str); } int pal_get_bmc_ipmb_slave_addr(uint16_t *slave_addr, uint8_t bus_id) { *slave_addr = 0x10; 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; if ((bus == 4) && (((uint8_t *)buf)[0] == 0x20)) { // OCP LCD debug card clock_gettime(CLOCK_MONOTONIC, &ts); if (ts.tv_sec >= (last_time + 5)) { last_time = ts.tv_sec; ts.tv_sec += 30; snprintf(key, sizeof(key), "ocpdbg_lcd"); snprintf(value, sizeof(value), "%ld", ts.tv_sec); if (kv_set(key, value, 0, 0) < 0) { return -1; } } } return 0; } bool pal_is_mcu_working(void) { char key[MAX_KEY_LEN] = {0}; char value[MAX_VALUE_LEN] = {0}; struct timespec ts; snprintf(key, sizeof(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; } return false; }