meta-facebook/meta-fbttn/recipes-fbttn/fblibs/files/pal/pal.c (3,505 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. */ #define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <stdint.h> #include <stdbool.h> #include <fcntl.h> #include <errno.h> #include <syslog.h> #include <sys/mman.h> #include <string.h> #include <pthread.h> #include <openbmc/kv.h> #include <facebook/bic.h> #include "pal.h" #include "pal_sensors.h" #define BIT(value, index) ((value >> index) & 1) #define FBTTN_PLATFORM_NAME "FBTTN" #define LAST_KEY "last_key" #define MAX_NUM_SLOTS 1 #define GPIO_VAL "/sys/class/gpio/gpio%d/value" #define GPIO_DIR "/sys/class/gpio/gpio%d/direction" /*For Triton with GPIO Table 0.5 * BMC_TO_EXP_RESET GPIOA2 2 //Reset EXP * PWRBTN_OUT_N GPIOD2 26 //power button signal input * COMP_PWR_BTN_N GPIOD3 27 //power button signal output * RSTBTN_OUT_N GPIOD4 28 //input * SYS_RESET_N_OUT GPIOD5 29 //output * COMP_RST_BTN_N GPIOAA0 208 //Dedicate reset Mono Lake * SCC_STBY_PWR_EN GPIOF4 44 //Enable SCC STBY pwr * SCC_LOC_FULL_PWR_EN GPIOF0 40 //For control SCC Power sequence * SCC_RMT_FULL_PWR_EN GPIOF1 41 // * COMP_POWER_FAIL_N GPIOO6 118 * COMP_PWR_EN GPIOO7 119 * P12V_A_PGOOD GPIOF6 46 //Whole system stby pwr good * IOM_FULL_PWR_EN GPIOAA7 215 * IOM_FULL_PGOOD GPIOAB1 217 // EVT: GPIOAB2(218); DVT: GPIOAB1(217) * BMC_LOC_HEARTBEAT GPIOO1 113 * UART_SEL_IN GPIOS1 145 // input; 0: UART_SEL_SERVER; 1: UART_SEL_BMC * DB_PRSNT_BMC_N GPIOQ6 134 * SYS_PWR_LED GPIOA3 3 * ENCL_FAULT_LED GPIOO3 115 * BOARD_REV_0 GPIOJ0 72 // GPIOJ[0:2] = 000 is EVT * BOARD_REV_1 GPIOJ1 73 // GPIOJ[0:2] = 001 is DVT * BOARD_REV_2 GPIOJ2 74 // GPIOJ[0:2] = 010 is MP * IOM_TYPE0 GPIOJ4 76 * IOM_TYPE1 GPIOJ5 77 * IOM_TYPE2 GPIOJ6 78 * IOM_TYPE3 GPIOJ7 79 * DEBUG_PWR_BTN_N GPIOP3 123 * DEBUG_RST_BTN_N GPIOZ0 200 * */ //Update at 9/12/2016 for Triton #define GPIO_PWR_BTN 26 #define GPIO_PWR_BTN_N 27 #define GPIO_RST_BTN 28 #define GPIO_SYS_RST_BTN 29 // active by low pulse #define GPIO_COMP_PWR_EN 119 // computer power enable. GPO. 1: enable #define GPIO_IOM_FULL_PWR_EN 215 #define GPIO_SCC_RMT_TYPE_0 47 #define GPIO_SLOTID_0 48 #define GPIO_SLOTID_1 49 #define GPIO_IOM_TYPE0 76 #define GPIO_IOM_TYPE1 77 #define GPIO_IOM_TYPE2 78 #define GPIO_IOM_TYPE3 79 #define GPIO_HB_LED 113 #define GPIO_PWR_LED 3 #define GPIO_ENCL_FAULT_LED 115 #define GPIO_BMC_ML_PWR_YELLOW_LED_N 201 #define GPIO_BMC_ML_PWR_BLUE_LED 202 #define BMC_EXT1_LED_Y_N 37 #define BMC_EXT2_LED_Y_N 39 #define UART_SEL_IN 145 #define GPIO_POSTCODE_0 56 #define GPIO_POSTCODE_1 57 #define GPIO_POSTCODE_2 58 #define GPIO_POSTCODE_3 59 #define GPIO_POSTCODE_4 60 #define GPIO_POSTCODE_5 61 #define GPIO_POSTCODE_6 62 #define GPIO_POSTCODE_7 63 #define GPIO_DBG_CARD_PRSNT 134 #define GPIO_SCC_A_INS 478 // 0: Present; 1: Absent #define GPIO_SCC_B_INS 479 // 0: Present; 1: Absent #define GPIO_BMC_READY_N 28 #define GPIO_BIC_READY_N 55 // GPIOG7: I2C_COMP_ALERT_N #define GPIO_CHASSIS_INTRUSION 487 #define GPIO_PCIE_RESET 203 #define GPIO_BOARD_REV_0 72 #define GPIO_BOARD_REV_1 73 #define GPIO_BOARD_REV_2 74 #define GPIO_DEBUG_PWR_BTN_N 123 #define GPIO_DEBUG_RST_BTN_N 200 #define PAGE_SIZE 0x1000 #define AST_SCU_BASE 0x1e6e2000 #define PIN_CTRL1_OFFSET 0x80 #define PIN_CTRL2_OFFSET 0x84 #define SCU_RST_STS_OFFSET 0x3C // SCU3C: System reset control/status //#define UART1_TXD (1 << 22) #define DELAY_GRACEFUL_SHUTDOWN 1 #define DELAY_POWER_OFF 6 #define DELAY_POWER_CYCLE 10 #define DELAY_12V_CYCLE 5 #define RETRY_COUNT 5 #define CRASHDUMP_BIN "/usr/local/bin/dump.sh" #define CRASHDUMP_FILE "/mnt/data/crashdump_" #define LARGEST_DEVICE_NAME 120 #define PWM_DIR "/sys/devices/platform/ast_pwm_tacho.0" #define PWM_UNIT_MAX 100 #define TACH_RPM "/sys/devices/platform/ast_pwm_tacho.0/tacho%d_rpm" #define TACH_BMC_RMT_HB 0 #define TACH_SCC_LOC_HB 4 #define TACH_SCC_RMT_HB 5 #define PLATFORM_FILE "/tmp/system.bin" #define ERR_CODE_FILE "/tmp/error_code.bin" #define BOOT_LIST_FILE "/tmp/boot_list.bin" #define FIXED_BOOT_DEVICE_FILE "/tmp/fixed_boot_device.bin" #define BIOS_DEFAULT_SETTING_FILE "/tmp/bios_default_setting.bin" #define LAST_BOOT_TIME "/tmp/last_boot_time.bin" #define AST_POR_FLAG "/tmp/ast_por" // SHIFT to 16 #define UART1_TXD 0 #define NUM_SERVER_FRU 1 #define NUM_NIC_FRU 1 #define NUM_BMC_FRU 1 unsigned char g_err_code[ERROR_CODE_NUM]; /* For fbttn Power Sequence (controlled by HW) * 1. Input : SCC_STBY_PWR_EN * 2. Check Input : SCC_STBY_PWR_GOOD // OVER GPIO_EXP * 3. Input : SCC_LOC_FULL_PWR_EN * 4. Check Input : SCC_LOC_FULL_PWR_GOOD // OVER GPIO_EXP * 5. Input : IOM_FULL_PWR_EN * 6. Check Input : IOM_FULL_PGOOD * After BMC ready (controlled by BMC) * 7. Output : SCC_RMT_FULL_PWR_EN // type 7 only * 8. Output : COMP_PWR_EN * 9. Output : COMP_PWR_BTN_N // is_server_prsnt = true */ const static uint8_t gpio_rst_btn[] = { 0, GPIO_SYS_RST_BTN }; const static uint8_t gpio_led[] = { 0, GPIO_PWR_LED }; // System power LED (Blue color, on front panel) const static uint8_t gpio_id_led[] = { 0, GPIO_PWR_LED }; // Identify LED (System power LED) //const static uint8_t gpio_prsnt[] = { 0, 61 }; //const static uint8_t gpio_bic_ready[] = { 0, 107 }; const static uint8_t gpio_power_btn[] = { 0, GPIO_PWR_BTN_N }; const static uint8_t gpio_12v[] = { 0, GPIO_COMP_PWR_EN }; const char pal_fru_list[] = "all, server, iom, dpb, scc, nic"; const char pal_fru_list_print[] = "all, server, iom, dpb, scc"; // Cannot read fruid from "nic" const char pal_fru_list_rw[] = "server, iom"; // Cannot write fruid to "scc" and "dpb" const char pal_server_list[] = "server"; const char pal_fru_list_sensor_history[] = "all, server, iom, nic"; // Cannot show sensor history to "scc" and "dpb" size_t pal_pwm_cnt = 2; size_t pal_tach_cnt = 8; const char pal_pwm_list[] = "0, 1"; const char pal_tach_list[] = "0...7"; uint8_t fanid2pwmid_mapping[] = {1, 1, 0, 0, 0, 0, 1, 1}; static uint8_t bios_default_setting_timer_flag = 0; static uint8_t otp_server_12v_off_flag = 0; char * key_list[] = { "pwr_server1_last_state", "sysfw_ver_slot1", "identify_slot1", "timestamp_sled", "slot1_por_cfg", "slot1_sensor_health", "iom_sensor_health", "dpb_sensor_health", "scc_sensor_health", "nic_sensor_health", "heartbeat_health", "fru_prsnt_health", "bmc_health", "slot1_sel_error", "slot1_boot_order", "server_pcie_port_config", /* Add more Keys here */ LAST_KEY /* This is the last key of the list */ }; char * def_val_list[] = { "on", /* pwr_server1_last_state */ "0", /* sysfw_ver_slot */ "off", /* identify_slot */ "0", /* timestamp_sled */ "lps", /* slot_por_cfg */ "1", /* slot_sensor_health */ "1", /* iom_sensor_health */ "1", /* dpb_sensor_health */ "1", /* scc_sensor_health */ "1", /* nic_sensor_health */ "1", /* heartbeat_health */ "1", /* fru_prsnt_health */ "1", /* bmc_health */ "1", /* slot_sel_error */ "0000000", /* slot1_boot_order */ "0000", /* server_pcie_port_config */ /* Add more def values for the correspoding keys*/ LAST_KEY /* Same as last entry of the key_list */ }; char * cfg_support_key_list[] = { "slot1_por_cfg", LAST_KEY /* This is the last key of the list */ }; struct power_coeff { float ein; float coeff; }; /* IPMI SEL: System Firmware Error string table */ struct system_fw_progress { uint8_t EventData1; char DecodeString[128]; }; struct system_fw_progress system_fw_error[] = { {0x00, "Unspecified"}, {0x01, "No system memory is physically installed in the system"}, {0x02, "No usable system memory, all installed memory has experienced an unrecoverable failure"}, {0x03, "Unrecoverable hard-disk/ATAPI/IDE device failure"}, {0x04, "Unrecoverable system-board failure"}, {0x05, "Unrecoverable diskette subsystem failure"}, {0x06, "Unrecoverable hard-disk controller failure"}, {0x07, "Unrecoverable PS/2 or USB keyboard failure"}, {0x08, "Removable boot media not found"}, {0x09, "Unrecoverable video controller failure"}, {0x0A, "No video device detected"}, {0x0B, "Firmware (BIOS) ROM corruption detected"}, {0x0C, "CPU voltage mismatch"}, {0x0D, "CPU speed matching failure"}, }; struct system_fw_progress system_fw_hang_or_progress[] = { {0x00, "Unspecified"}, {0x01, "Memory initialization"}, {0x02, "Hard-disk initialization"}, {0x03, "Secondary processor(s) initialization"}, {0x04, "User authentication"}, {0x05, "User-initiated system setup"}, {0x06, "USB resource configuration"}, {0x07, "PCI resource configuration"}, {0x08, "Option ROM initialization"}, {0x09, "Video initialization"}, {0x0A, "Cache initialization"}, {0x0B, "SM Bus initialization"}, {0x0C, "Keyboard controller initialization"}, {0x0D, "Embedded controller/management controller initialization"}, {0x0E, "Docking station attachment"}, {0x0F, "Enabling docking station"}, {0x10, "Docking station ejection"}, {0x11, "Disabling docking station"}, {0x12, "Calling operating system wake-up vector"}, {0x13, "Starting operating system boot process, e.g. calling Int 19h"}, {0x14, "Baseboard or motherboard initialization"}, {0x15, "reserved"}, {0x16, "Floppy initialization"}, {0x17, "Keyboard test"}, {0x18, "Pointing device test"}, {0x19, "Primary processor initialization"}, }; /* NVMe-MI SSD Status Flag bit mask */ #define NVME_SFLGS_MASK_BIT 0x28 //Just check bit 3,5 #define NVME_SFLGS_CHECK_VALUE 0x28 // normal - bit 3,5 = 1 /* NVMe-MI SSD SMART Critical Warning */ #define NVME_SMART_WARNING_MASK_BIT 0x1F // Check bit 0~4 #define MAX_SERIAL_NUM 20 // Helper Functions int read_device(const char *device, int *value) { FILE *fp; int rc; fp = fopen(device, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", device); #endif return err; } rc = fscanf(fp, "%d", value); fclose(fp); if (rc != 1) { #ifdef DEBUG syslog(LOG_INFO, "failed to read device %s", device); #endif return ENOENT; } else { return 0; } } static int read_device_hex(const char *device, int *value) { FILE *fp; int rc; fp = fopen(device, "r"); if (!fp) { #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", device); #endif return errno; } rc = fscanf(fp, "%x", value); fclose(fp); if (rc != 1) { #ifdef DEBUG syslog(LOG_INFO, "failed to read device %s", device); #endif return ENOENT; } else { return 0; } } int write_device(const char *device, const char *value) { FILE *fp; int rc; fp = fopen(device, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device for write %s", device); #endif return err; } rc = fputs(value, fp); fclose(fp); if (rc < 0) { #ifdef DEBUG syslog(LOG_INFO, "failed to write device %s", device); #endif return ENOENT; } else { return 0; } } static int pal_key_check(char *key) { int i; i = 0; while(strcmp(key_list[i], LAST_KEY)) { // If Key is valid, return success if (!strcmp(key, key_list[i])) { return 0; } i++; } #ifdef DEBUG syslog(LOG_WARNING, "pal_key_check: invalid key - %s", key); #endif return -1; } // Check what keys can be set by cfg-util int pal_cfg_key_check(char *key) { int i; i = 0; while(strcmp(cfg_support_key_list[i], LAST_KEY)) { // If Key is valid and can be set, return success if (!strcmp(key, cfg_support_key_list[i])) { return 0; } i++; } // Check is key is defined and valid but cannot be set if (pal_key_check(key) == 0) { return 1; } else { #ifdef DEBUG syslog(LOG_WARNING, "%s: invalid key - %s", __func__, key); #endif return -1; } } int pal_get_key_value(char *key, char *value) { int i = 0; int ret = 0; // Check is key is defined and valid if (pal_key_check(key)) return -1; //Retry for max RETRY_COUNT Times for (i = 0; i < RETRY_COUNT; i++) { ret = 0; ret = kv_get(key, value, NULL, KV_FPERSIST); if (ret != 0) { syslog(LOG_ERR, "%s, failed to read device (%s), ret: %d, retry: %d", __func__, key, ret, i); } else { break; } msleep(100); } return ret; } int pal_set_key_value(char *key, char *value) { int i = 0; int ret = 0; // Check is key is defined and valid if (pal_key_check(key)) return -1; //Retry for max RETRY_COUNT Times for (i = 0; i < RETRY_COUNT; i++) { ret = 0; ret = kv_set(key, value, 0, KV_FPERSIST); if (ret != 0) { syslog(LOG_ERR, "%s, failed to write device (%s), ret: %d, retry: %d", __func__, key, ret, i); } else { break; } msleep(100); } return ret; } static int power_on_server_physically(uint8_t slot_id){ char vpath[64] = {0}; sprintf(vpath, GPIO_VAL, gpio_power_btn[slot_id]); if (write_device(vpath, "1")) { return -1; } if (write_device(vpath, "0")) { return -1; } msleep(200); if (write_device(vpath, "1")) { return -1; } return 0; } // Power On the server in a given slot static int server_power_on(uint8_t slot_id) { char vpath[64] = {0}; int val = 0; int loop = 0; int max_retry = 5; // Check if another instance is running if (pal_powering_on_flag(slot_id) < 0) { syslog(LOG_WARNING, "%s(): Another instance is running for FRU: %d.\n", __func__, slot_id); //Make server_power_on exit code to "-2" when another instance is running return -2; } //Enable IOM full power via GPIO_IOM_FULL_PWR_EN sprintf(vpath, GPIO_VAL, GPIO_IOM_FULL_PWR_EN); for (loop = 0; loop < max_retry; loop++){ write_device(vpath, "1"); read_device(vpath, &val); if (val == 1) break; syslog(LOG_WARNING, "%s(): GPIO_IOM_FULL_PWR_EN status is %d. Try %d time(s).\n", __func__, val, loop); msleep(10); // Max retry case if (loop == (max_retry-1)) { syslog(LOG_CRIT, "%s(): Fail to enable GPIO_IOM_FULL_PWR_EN after %d tries.\n", __func__, max_retry); pal_rm_powering_on_flag(slot_id); return -1; } } // Power on server for (loop = 0; loop < max_retry; loop++){ val = power_on_server_physically(slot_id); if (val == 0) break; syslog(LOG_WARNING, "%s(): Power on server failed for %d time(s).\n", __func__, loop); sleep(2); // Max retry case if (loop == (max_retry-1)) { pal_rm_powering_on_flag(slot_id); return -1; } } pal_rm_powering_on_flag(slot_id); return 0; } // Power Off the server in given slot static int server_power_off(uint8_t slot_id, bool gs_flag, bool cycle_flag) { char vpath[64] = {0}; if (slot_id != FRU_SLOT1) { return -1; } sprintf(vpath, GPIO_VAL, gpio_power_btn[slot_id]); if (write_device(vpath, "1")) { return -1; } sleep(1); if (write_device(vpath, "0")) { return -1; } if (gs_flag) { sleep(DELAY_GRACEFUL_SHUTDOWN); } else { sleep(DELAY_POWER_OFF); } if (write_device(vpath, "1")) { return -1; } return 0; } int server_power_reset(uint8_t slot_id) { int ret = 0; ret = pal_set_rst_btn(slot_id, GPIO_LOW); if (ret < 0) return ret; msleep(100); ret = pal_set_rst_btn(slot_id, GPIO_HIGH); if (ret < 0) return ret; return 0; } // Control 12V to the server in a given slot int server_12v_on(uint8_t slot_id) { if (slot_id != FRU_SLOT1) { return -1; } return set_gpio_value(gpio_12v[slot_id], CTRL_ENABLE); } // Turn off 12V for the server in given slot int server_12v_off(uint8_t slot_id) { char vpath[64] = {0}; if (slot_id != FRU_SLOT1) { return -1; } sprintf(vpath, GPIO_VAL, gpio_12v[slot_id]); if (write_device(vpath, "0")) { return -1; } return 0; } // Display the given POST code using GPIO port static int pal_post_display(uint8_t status) { char path[64] = {0}; int ret; char *val; #ifdef DEBUG syslog(LOG_WARNING, "pal_post_display: status is %d\n", status); #endif sprintf(path, GPIO_VAL, GPIO_POSTCODE_0); if (BIT(status, 0)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_1); if (BIT(status, 1)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_2); if (BIT(status, 2)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_3); if (BIT(status, 3)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_4); if (BIT(status, 4)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_5); if (BIT(status, 5)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_6); if (BIT(status, 6)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_7); if (BIT(status, 7)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } post_exit: if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "write_device failed for %s\n", path); #endif return -1; } else { return 0; } } // Platform Abstraction Layer (PAL) Functions int pal_get_platform_name(char *name) { strcpy(name, FBTTN_PLATFORM_NAME); return 0; } int pal_get_num_slots(uint8_t *num) { *num = MAX_NUM_SLOTS; return 0; } int pal_is_scc_prsnt() { uint8_t val = 0; int ret = 0; int sku = 0; int gpio_num; sku = pal_get_iom_type(); // IOM type: M.2 solution if (sku == IOM_M2) { if (pal_get_iom_location() == IOM_SIDEA) { // IOMA gpio_num = GPIO_SCC_A_INS; } else if (pal_get_iom_location() == IOM_SIDEB) { // IOMB gpio_num = GPIO_SCC_B_INS; } else { gpio_num = GPIO_SCC_A_INS; } // IOM type: IOC solution } else { gpio_num = GPIO_SCC_A_INS; } ret = get_gpio_value(gpio_num, &val); if (ret != 0) { return -1; } if (val == 0) { return 1; // Present } else { return 0; // Absent } } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { switch (fru) { case FRU_SLOT1: *status = is_server_prsnt(fru); break; // Need to check if local SCC is inserted. case FRU_SCC: *status = pal_is_scc_prsnt(); break; case FRU_DPB: case FRU_IOM: case FRU_NIC: *status = 1; break; default: return -1; } return 0; } int pal_is_slot_server(uint8_t fru) { if (fru == FRU_SLOT1) return 1; return 0; } int pal_is_server_12v_on(uint8_t slot_id, uint8_t *status) { int ret = -1; uint8_t val = -1; if (slot_id != FRU_SLOT1) { return -1; } ret = get_gpio_value(gpio_12v[slot_id], &val); if (ret != 0) return -1; if (val == 0x1) { *status = CTRL_ENABLE; } else { *status = CTRL_DISABLE; } return 0; } int pal_is_bic_ready(uint8_t slot_id, uint8_t *status) { uint8_t val = 0; int ret = 0; ret = get_gpio_value(GPIO_BIC_READY_N, &val); if (ret != 0) { return -1; } if (val == 0) { *status = BIC_READY; } else { *status = BIC_NOT_READY; } return 0; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { uint8_t ctrl_status, val; switch (fru) { case FRU_SLOT1: // Check if the server board is powered up if (!pal_is_server_12v_on(fru, &ctrl_status)) { // If the server is powered up, check if the BIC is ready. if (ctrl_status == CTRL_ENABLE) { if (!pal_is_bic_ready(fru, &val)) *status = (val == BIC_READY)? 1 : 0; // BIC_READY: 0; BIC_NOT_READY: 1 // ctrl_status == CTRL_DISABLE; the server is 12V-OFF } else { *status = 0; } break; // if pal_is_server_12v_on failed } else return -1; case FRU_DPB: // Only if local SCC is present, BMC can read the DPB sensors and fruid *status = pal_is_scc_prsnt(); //TODO: is_SCC_ready needed to confirm is expander is ready break; case FRU_SCC: //TODO: is_SCC_ready needed to confirm is expander is ready case FRU_IOM: case FRU_NIC: *status = 1; break; default: return -1; } return 0; } int pal_is_debug_card_prsnt(uint8_t *status) { int val; char path[64] = {0}; sprintf(path, GPIO_VAL, GPIO_DBG_CARD_PRSNT); if (read_device(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; } return 0; } int pal_get_server_power(uint8_t slot_id, uint8_t *status) { int ret; int iom_board_id = BOARD_MP; uint8_t gpio_perst_val = 0; char value[MAX_VALUE_LEN] = { 0 }; bic_gpio_t gpio; /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(slot_id, status); if (ret < 0) { syslog(LOG_ERR, "pal_get_server_power: pal_is_server_12v_on failed"); return -1; } /* If 12V-off, return */ if (*status == CTRL_DISABLE) { *status = SERVER_12V_OFF; return 0; } // In DVT systems, we added a new GPIO pin to detect power status iom_board_id = pal_get_iom_board_id(); if (iom_board_id == BOARD_EVT) { // Get server power status via BIC /* If 12V-on, check if the CPU is turned on or not */ ret = bic_get_gpio(slot_id, &gpio); if (ret) { // Check for if the BIC is irresponsive due to 12V_OFF or 12V_CYCLE syslog(LOG_INFO, "pal_get_server_power: bic_get_gpio returned error hence" "reading the kv_store for last power state for fru %d", slot_id); pal_get_last_pwr_state(slot_id, value); if (!(strcmp(value, "off"))) { *status = SERVER_POWER_OFF; } else if (!(strcmp(value, "on"))) { *status = SERVER_POWER_ON; } else { return ret; } return 0; } if (gpio.pwrgood_cpu) { *status = SERVER_POWER_ON; } else { *status = SERVER_POWER_OFF; } } else { // Get server power status via GPIO PERST ret = get_gpio_value(GPIO_PCIE_RESET, &gpio_perst_val); if (ret != 0) { syslog(LOG_ERR, "%s: get GPIO_PCIE_RESET fail", __func__); return -1; } // Each time Server Power On and Reset, BIOS sends a 13~14ms PERST# low pulse to PCIe devices. // To filter-out the low pulse to prevent the false power status query, // we add recheck in 50ms (add some tolerance) if the power status is off. if (gpio_perst_val == 0) { msleep(50); ret = get_gpio_value(GPIO_PCIE_RESET, &gpio_perst_val); if (ret != 0) { syslog(LOG_ERR, "%s: get GPIO_PCIE_RESET fail", __func__); return -1; } } if (gpio_perst_val == 0) { *status = SERVER_POWER_OFF; } else { *status = SERVER_POWER_ON; } } return 0; } // Power Off, Power On, or Power Reset the server in given slot int pal_set_server_power(uint8_t slot_id, uint8_t cmd) { uint8_t status; bool gs_flag = false; bool cycle_flag = false; if (slot_id != FRU_SLOT1) { return -1; } if (pal_get_server_power(slot_id, &status) < 0) { return -1; } switch(cmd) { case SERVER_POWER_ON: if (status == SERVER_POWER_ON) return 1; else return server_power_on(slot_id); break; case SERVER_POWER_OFF: if (status == SERVER_POWER_OFF) return 1; else return server_power_off(slot_id, gs_flag, cycle_flag); break; case SERVER_POWER_CYCLE: cycle_flag = true; if (status == SERVER_POWER_ON) { if (server_power_off(slot_id, gs_flag, cycle_flag)) return -1; sleep(DELAY_POWER_CYCLE); return server_power_on(slot_id); } else if (status == SERVER_POWER_OFF) { return server_power_on(slot_id); } break; case SERVER_POWER_RESET: if (status == SERVER_POWER_OFF) { syslog(LOG_WARNING, "%s: power reset has no effect while power is off", __func__); return -1; } else { return server_power_reset(slot_id); } break; case SERVER_GRACEFUL_SHUTDOWN: if (status == SERVER_POWER_OFF) { return 1; } else { gs_flag = true; return server_power_off(slot_id, gs_flag, cycle_flag); } break; case SERVER_12V_ON: if (status != SERVER_12V_OFF) // pal_get_server_power() doesn't provide status SERVER_12V_ON return 1; else return server_12v_on(slot_id); break; case SERVER_12V_OFF: if (status == SERVER_12V_OFF) return 1; else return server_12v_off(slot_id); break; case SERVER_12V_CYCLE: if (server_12v_off(slot_id)) { return -1; } sleep(DELAY_12V_CYCLE); return server_12v_on(slot_id); default: return -1; } return 0; } int pal_sled_cycle(void) { pal_update_ts_sled(); // Remove the adm1275 module as the HSC device is busy system("rmmod adm1275"); // Send command to HSC power cycle system("i2cset -y 0 0x10 0xd9 c"); return 0; } // Read Debug Card UART Select and return the position int pal_get_uart_sel_pos(uint8_t *pos) { uint8_t uart_sel = HAND_SW_BMC; int ret = 0; // UART_SEL_IN: GPIOS1 (145) // 0: UART_SEL_SERVER; 1: UART_SEL_BMC ret = get_gpio_value(UART_SEL_IN, &uart_sel); if (ret != 0) { return -1; } *pos = uart_sel; return 0; } // Return the Front panel's debug card Power Button status int pal_get_dbg_pwr_btn(uint8_t *status) { uint8_t val = 0; int ret = 0; ret = get_gpio_value(GPIO_DEBUG_PWR_BTN_N, &val); if (ret != 0) { return -1; } if (val == 1) { *status = 0x0; } else { *status = 0x1; } return 0; } // Return the front panel's debug card Reset Button status int pal_get_dbg_rst_btn(uint8_t *status) { uint8_t val = 0; int ret = 0; ret = get_gpio_value(GPIO_DEBUG_RST_BTN_N, &val); if (ret != 0) { return -1; } if (val == 1) { *status = 0x0; } else { *status = 0x1; } return 0; } // Return the Front panel Power Button int pal_get_pwr_btn(uint8_t *status) { uint8_t val = 0; int ret = 0; ret = get_gpio_value(GPIO_PWR_BTN, &val); if (ret != 0) { return -1; } if (val) { *status = 0x0; } else { *status = 0x1; } return 0; } // Return the front panel's Reset Button status int pal_get_rst_btn(uint8_t *status) { uint8_t val = 0; int ret = 0; ret = get_gpio_value(GPIO_RST_BTN, &val); if (ret != 0) { return -1; } if (val) { *status = 0x0; } else { *status = 0x1; } return 0; } // Update the Reset button input to the server at given slot int pal_set_rst_btn(uint8_t slot, uint8_t status) { if (slot < 1 || slot > MAX_NUM_SLOTS) { return -1; } return set_gpio_value(gpio_rst_btn[slot], status); } // Update the LED for the given slot with the status int pal_set_led(uint8_t slot, uint8_t status) { char path[64] = {0}; char *val; if (slot < 1 || slot > 4) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, gpio_led[slot]); if (write_device(path, val)) { return -1; } return 0; } // Update Heartbeet LED int pal_set_hb_led(uint8_t status) { char path[64] = {0}; char *val; if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, GPIO_HB_LED); if (write_device(path, val)) { return -1; } return 0; } // Update the Identification LED for the given slot with the status int pal_set_id_led(uint8_t slot, uint8_t status) { char path[64] = {0}; char *val; if (slot < 1 || slot > 4) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, gpio_id_led[slot]); if (write_device(path, val)) { return -1; } return 0; } // Update the USB Mux to the server at given slot // In Triton, we don't need it int pal_switch_usb_mux(uint8_t slot) { return 0; } // Enable POST buffer for the server in given slot int pal_post_enable(uint8_t slot) { int ret; bic_config_t config = {0}; bic_config_u *t = (bic_config_u *) &config; ret = bic_get_config(slot, &config); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "post_enable: bic_get_config failed for fru: %d\n", slot); #endif return ret; } if (t->bits.post == 0) { t->bits.post = 1; ret = bic_set_config(slot, &config); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "post_enable: bic_set_config failed\n"); #endif return ret; } } return 0; } // Disable POST buffer for the server in given slot int pal_post_disable(uint8_t slot) { int ret; bic_config_t config = {0}; bic_config_u *t = (bic_config_u *) &config; ret = bic_get_config(slot, &config); if (ret) { return ret; } t->bits.post = 0; ret = bic_set_config(slot, &config); if (ret) { return ret; } return 0; } // Get the last post code of the given slot int pal_post_get_last(uint8_t slot, uint8_t *status) { int ret; uint8_t buf[MAX_IPMB_RES_LEN] = {0x0}; uint8_t len; ret = bic_get_post_buf(slot, buf, &len); if (ret) { return ret; } // The post buffer is LIFO and the first byte gives the latest post code *status = buf[0]; return 0; } // Handle the received post code, for now display it on debug card int pal_post_handle(uint8_t slot, uint8_t status) { int ret; // Only allow front-paneld to control if ((slot == HAND_SW_SERVER) || (slot == HAND_SW_BMC)) { // Display the post code or error code in the 7-seg LED of debug card ret = pal_post_display(status); if (ret) { return ret; } } return 0; } 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_SLOT1: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_SERVER | FRU_CAPABILITY_POWER_ALL; break; case FRU_IOM: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_MANAGEMENT_CONTROLLER; break; case FRU_DPB: case FRU_SCC: *caps = FRU_CAPABILITY_FRUID_READ | FRU_CAPABILITY_SENSOR_READ; break; case FRU_NIC: *caps = FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_NETWORK_CARD; break; default: ret = -1; break; } return ret; } int pal_get_fru_id(char *str, uint8_t *fru) { return fbttn_common_fru_id(str, fru); } int pal_get_fru_name(uint8_t fru, char *name) { return fbttn_common_fru_name(fru, name); } int pal_get_fru_sdr_path(uint8_t fru, char *path) { return fbttn_sensor_sdr_path(fru, path); } int pal_get_fru_sensor_list(uint8_t fru, uint8_t **sensor_list, int *cnt) { int sku = 0; int iom_board_id = BOARD_MP; switch(fru) { case FRU_SLOT1: *sensor_list = (uint8_t *) bic_sensor_list; *cnt = bic_sensor_cnt; break; case FRU_IOM: sku = pal_get_iom_type(); if (sku == IOM_M2) { // IOM type: M.2 solution // It's a transition period from EVT to DVT iom_board_id = pal_get_iom_board_id(); if (iom_board_id == BOARD_EVT) { // EVT *sensor_list = (uint8_t *) iom_sensor_list_type5; *cnt = iom_sensor_cnt_type5; } else { // DVT later *sensor_list = (uint8_t *) iom_sensor_list_type5_dvt; *cnt = iom_sensor_cnt_type5_dvt; } } else { // IOM type: IOC solution *sensor_list = (uint8_t *) iom_sensor_list_type7; *cnt = iom_sensor_cnt_type7; } break; case FRU_DPB: *sensor_list = (uint8_t *) dpb_sensor_list; *cnt = dpb_sensor_cnt; break; case FRU_SCC: *sensor_list = (uint8_t *) scc_sensor_list; *cnt = scc_sensor_cnt; break; case FRU_NIC: *sensor_list = (uint8_t *) nic_sensor_list; *cnt = nic_sensor_cnt; break; default: #ifdef DEBUG syslog(LOG_WARNING, "pal_get_fru_sensor_list: Wrong fru id %u", fru); #endif return -1; } return 0; } int pal_fruid_write(uint8_t fru, char *path) { return bic_write_fruid(fru, 0, path); } int pal_sensor_sdr_init(uint8_t fru, sensor_info_t *sinfo) { uint8_t status; // SDR only supported for MonoLake switch(fru) { case FRU_SLOT1: pal_is_fru_prsnt(fru, &status); if (status) return fbttn_sensor_sdr_init(fru, sinfo); default: return -1; } } bool pal_sensor_is_cached(uint8_t fru, uint8_t sensor_num) { if (fru == FRU_DPB || fru == FRU_SCC) { return false; } return true; } int pal_sensor_read_raw(uint8_t fru, uint8_t sensor_num, void *value) { uint8_t status; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; int ret; int sku = 0; int i; bool check_server_power_status = false; if(pal_is_fru_prsnt(fru, &status) < 0) return -1; if (!status) return -1; switch(fru) { case FRU_SLOT1: sprintf(key, "server_sensor%d", sensor_num); break; case FRU_IOM: sprintf(key, "iom_sensor%d", sensor_num); break; case FRU_DPB: pal_expander_sensor_check(fru, sensor_num); sprintf(key, "dpb_sensor%d", sensor_num); break; case FRU_SCC: pal_expander_sensor_check(fru, sensor_num); sprintf(key, "scc_sensor%d", sensor_num); break; case FRU_NIC: sprintf(key, "nic_sensor%d", sensor_num); break; } // Check for the power status ret = pal_get_server_power(FRU_SLOT1, &status); if (ret == 0) { ret = fbttn_sensor_read(fru, sensor_num, value, status, key); if (ret != 0) { if (ret < 0) { if (fru == FRU_IOM || fru == FRU_DPB || fru == FRU_SCC || fru == FRU_NIC) { ret = -1; } else if (pal_get_server_power(fru, &status) < 0) { ret = -1; } else if (status == SERVER_POWER_ON) { // This check helps interpret the IPMI packet loss scenario ret = -1; } strcpy(str, "NA"); } else { // If ret = READING_SKIP, doesn't update sensor reading and keep the previous value return ret; } } else { // On successful sensor read sku = pal_get_iom_type(); if (sku == IOM_M2) { // IOM type: M.2 solution for (i = 0; i < iom_t5_non_stby_sensor_cnt; i++) { if (sensor_num == iom_t5_non_stby_sensor_list[i]) { check_server_power_status = true; break; } } } else { // IOM type: IOC solution for (i = 0; i < iom_t7_non_stby_sensor_cnt; i++) { if (sensor_num == iom_t7_non_stby_sensor_list[i]) { check_server_power_status = true; break; } } } if (check_server_power_status == true) { // If server is powered off, ignore the read and write NA if (status != SERVER_POWER_ON) { strcpy(str, "NA"); ret = -1; } else { // double check power status, after reading the sensor pal_get_server_power(FRU_SLOT1, &status); // If server is powered off, ignore the read and write NA if (status != SERVER_POWER_ON) { strcpy(str, "NA"); ret = -1; } else { sprintf(str, "%.2f",*((float*)value)); } } } else { // check_server_power_status == false sprintf(str, "%.2f",*((float*)value)); } } if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_sensor_read_raw: cache_set key = %s, str = %s failed.", key, str); #endif return -1; } else { return ret; } } else { return -1; } } int pal_sensor_threshold_flag(uint8_t fru, uint8_t snr_num, uint16_t *flag) { switch(fru) { case FRU_SLOT1: if (snr_num == BIC_SENSOR_SOC_THERM_MARGIN) *flag = GETMASK(SENSOR_VALID) | GETMASK(UCR_THRESH); else if (snr_num == BIC_SENSOR_SOC_PACKAGE_PWR) *flag = GETMASK(SENSOR_VALID); else if (snr_num == BIC_SENSOR_SOC_TJMAX) *flag = GETMASK(SENSOR_VALID); break; case FRU_IOM: case FRU_DPB: case FRU_SCC: case FRU_NIC: break; } return 0; } int pal_get_sensor_threshold(uint8_t fru, uint8_t sensor_num, uint8_t thresh, void *value) { int iom_type = IOM_M2; iom_type = pal_get_iom_type(); return fbttn_sensor_threshold(fru, sensor_num, thresh, value, iom_type); } int pal_get_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) { return fbttn_sensor_name(fru, sensor_num, name); } int pal_get_sensor_units(uint8_t fru, uint8_t sensor_num, char *units) { return fbttn_sensor_units(fru, sensor_num, units); } int pal_get_fruid_path(uint8_t fru, char *path) { //Return -1 when ask for NIC fruid path if ( fru == FRU_NIC ) return -1; else return fbttn_get_fruid_path(fru, path); } int pal_get_fruid_eeprom_path(uint8_t fru, char *path) { return fbttn_get_fruid_eeprom_path(fru, path); } int pal_get_fruid_name(uint8_t fru, char *name) { return fbttn_get_fruid_name(fru, name); } int pal_set_def_key_value() { int ret; int i; int fru; char key[MAX_KEY_LEN] = {0}; for(i = 0; strcmp(key_list[i], LAST_KEY) != 0; i++) { if ((ret = kv_set(key_list[i], def_val_list[i], 0, KV_FPERSIST | KV_FCREATE)) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_def_key_value: kv_set failed. %d", ret); #endif } } /* Actions to be taken on Power On Reset */ if (pal_is_bmc_por()) { for (fru = 1; fru <= MAX_NUM_FRUS; fru++) { /* Clear all the SEL errors */ memset(key, 0, MAX_KEY_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sel_error", fru); break; case FRU_IOM: continue; case FRU_DPB: continue; case FRU_SCC: continue; case FRU_NIC: continue; default: return -1; } /* Write the value "1" which means FRU_STATUS_GOOD */ ret = pal_set_key_value(key, "1"); /* Clear all the sensor health files*/ memset(key, 0, MAX_KEY_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_IOM: continue; case FRU_DPB: continue; case FRU_SCC: continue; case FRU_NIC: continue; default: return -1; } /* Write the value "1" which means FRU_STATUS_GOOD */ ret = pal_set_key_value(key, "1"); } } return 0; } int pal_get_fru_devtty(uint8_t fru, char *devtty) { switch(fru) { case FRU_SLOT1: sprintf(devtty, "/dev/ttyS1"); break; default: #ifdef DEBUG syslog(LOG_CRIT, "pal_get_fru_devtty: Wrong fru id %u", fru); #endif return -1; } return 0; } void pal_dump_key_value(void) { int i = 0; char value[MAX_VALUE_LEN] = {0x0}; while (strcmp(key_list[i], LAST_KEY)) { printf("%s:", key_list[i]); if (kv_get(key_list[i], value, NULL, KV_FPERSIST) < 0) { printf("\n"); } else { printf("%s\n", value); } i++; memset(value, 0, MAX_VALUE_LEN); } } int pal_set_last_pwr_state(uint8_t fru, char *state) { int ret; char key[MAX_KEY_LEN] = {0}; uint8_t last_boot_time[4] = {0}; sprintf(key, "pwr_server%d_last_state", (int) fru); //If the OS state is "off", clear the last boot time to 0 0 0 0 if(!strcmp(state, "off")) pal_set_last_boot_time(1, last_boot_time); ret = pal_set_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_last_pwr_state: pal_set_key_value failed for " "fru %u", fru); #endif } return ret; } int pal_get_last_pwr_state(uint8_t fru, char *state) { int ret; char key[MAX_KEY_LEN] = {0}; switch(fru) { case FRU_SLOT1: sprintf(key, "pwr_server%d_last_state", (int) fru); ret = pal_get_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_get_last_pwr_state: pal_get_key_value failed for " "fru %u", fru); #endif } return ret; case FRU_IOM: case FRU_DPB: case FRU_SCC: case FRU_NIC: sprintf(state, "on"); return 0; } return -1; } int pal_get_sys_guid(uint8_t slot, char *guid) { return bic_get_sys_guid(slot, (uint8_t *)guid); } 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_slot%d", (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); } int pal_get_sysfw_ver(uint8_t slot, uint8_t *ver) { 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, "sysfw_ver_slot%d", (int) slot); ret = pal_get_key_value(key, str); if (ret) { return ret; } for (i = 0; i < 2*SIZE_SYSFW_VER; 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); ver[j++] = (msb << 4) | lsb; } return 0; } // Determine if BMC is AC on int pal_is_bmc_por(void) { FILE *fp; int por = 0; fp = fopen(AST_POR_FLAG, "r"); if (fp != NULL) { fscanf(fp, "%d", &por); fclose(fp); } return (por == 1) ? 1 : 0; } int pal_get_fru_discrete_list(uint8_t fru, uint8_t **sensor_list, int *cnt) { switch(fru) { case FRU_SLOT1: *sensor_list = (uint8_t *) bic_discrete_list; *cnt = bic_discrete_cnt; break; case FRU_DPB: *sensor_list = (uint8_t *) dpb_discrete_list; *cnt = dpb_discrete_cnt; break; case FRU_IOM: case FRU_SCC: case FRU_NIC: *sensor_list = NULL; *cnt = 0; break; default: #ifdef DEBUG syslog(LOG_WARNING, "pal_get_fru_discrete_list: Wrong fru id %u", fru); #endif break; } return 0; } static void _print_sensor_discrete_log(uint8_t fru, uint8_t snr_num, char *snr_name, uint8_t val, char *event) { if (val) { syslog(LOG_CRIT, "ASSERT: %s discrete - raised - FRU: %d, num: 0x%X," " snr: %-16s val: %d", event, fru, snr_num, snr_name, val); } else { syslog(LOG_CRIT, "DEASSERT: %s discrete - settled - FRU: %d, num: 0x%X," " snr: %-16s val: %d", event, fru, snr_num, snr_name, val); } pal_update_ts_sled(); } int pal_sensor_discrete_check(uint8_t fru, uint8_t snr_num, char *snr_name, uint8_t o_val, uint8_t n_val) { char name[32]; bool valid = false; uint8_t diff = o_val ^ n_val; if (GETBIT(diff, 0)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SOC_Thermal_Trip"); valid = true; break; case BIC_SENSOR_VR_HOT: sprintf(name, "SOC_VR_Hot"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 0), name); valid = false; } } if (GETBIT(diff, 1)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SOC_FIVR_Fault"); valid = true; break; case BIC_SENSOR_VR_HOT: sprintf(name, "SOC_DIMM_VR_Hot"); valid = true; break; case BIC_SENSOR_CPU_DIMM_HOT: sprintf(name, "SOC_MEMHOT"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 1), name); valid = false; } } if (GETBIT(diff, 2)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SOC_Throttle"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 2), name); valid = false; } } return 0; } static int pal_store_crashdump(uint8_t fru) { return fbttn_common_crashdump(fru); } int pal_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) { uint8_t snr_type = event_data[0]; uint8_t *ed = &event_data[3]; char key[MAX_KEY_LEN] = {0}; bool is_err_server_sel = true; /* For every SEL event received from the BIC except the below, set the critical LED on 1. OS_BOOT: (1) Base OS/Hypervisor Installation started (2) Base OS/Hypervisor Installation completed 2. CATERR_B: Cause of Time change - <"NTP" | "Host RTL" | "Set SEL time cmd" | "Set SEL time UTC offset cmd" | "Unknown"> - <"First" | "Second"> Time 3. ME_POWER_STATE: RUNNING 4. SPS_FW_HEALTH: (1) Flash state information (2) Direct Flash update (3) Auto-configuration finished (4) CPU Debug Capability Disabled 5. PWR_THRESH_EVT: Limit Not Exceeded 6. HPR_WARNING: (1) Infinite Time (2) <Time> minutes */ switch(fru) { case FRU_SLOT1: switch (snr_type) { case OS_BOOT: switch (ed[0] & 0xF) { case 0x07: // Base OS/Hypervisor Installation started case 0x08: // Base OS/Hypervisor Installation completed is_err_server_sel = false; break; } break; } switch(snr_num) { case CATERR_B: pal_store_crashdump(fru); break; case SYSTEM_EVENT: if (ed[0] == 0xE5) { /* Cause of Time change - <"NTP" | "Host RTL" | "Set SEL time cmd" | "Set SEL time UTC offset cmd" | "Unknown"> - <"First" | "Second"> Time */ is_err_server_sel = false; } break; case ME_POWER_STATE: switch (ed[0]) { case 0: // RUNNING is_err_server_sel = false; break; } break; case SPS_FW_HEALTH: if ((ed[0] & 0x0F) == 0x00) { switch (ed[1]) { case 0x03: // Flash state information case 0x06: // Direct Flash update case 0x0F: // Auto-configuration finished case 0x12: // CPU Debug Capability Disabled is_err_server_sel = false; break; } } break; case PWR_THRESH_EVT: if (ed[0] == 0x00) { // Limit Not Exceeded is_err_server_sel = false; } break; case HPR_WARNING: if (ed[2] == 0x01) { // "Infinite Time" or "<Time> minutes" is_err_server_sel = false; } break; } sprintf(key, "slot%d_sel_error", fru); break; case FRU_IOM: return 0; case FRU_DPB: return 0; case FRU_SCC: return 0; case FRU_NIC: return 0; default: return -1; } /* Write the value "0" which means FRU_STATUS_BAD. If this server SEL is non-error SEL, skip setting the error code for the server */ if (is_err_server_sel == true) { return pal_set_key_value(key, "0"); } else { 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]; switch (snr_type) { case OS_BOOT: // OS_BOOT used by OS sprintf(name, "OS"); return 0; } switch (fru) { case FRU_SLOT1: pal_get_x86_event_sensor_name(fru, snr_num, name); break; case FRU_SCC: fbttn_sensor_name(fru, snr_num, name); fbttn_sensor_name(fru-1, snr_num, name); // the fru is always 4, so we have to minus 1 for DPB sensors, since scc and dpb sensor all come from Expander break; } return 0; } int pal_parse_mem_mapping_str(uint8_t map_of_dimm_num, char *mem_mapping_string) { mem_mapping_string[0] = '\0'; switch (map_of_dimm_num) { case 0x00: strcpy(mem_mapping_string, "A0"); break; case 0x01: strcpy(mem_mapping_string, "A1"); break; case 0x08: strcpy(mem_mapping_string, "B0"); break; case 0x09: strcpy(mem_mapping_string, "B1"); break; default: strcpy(mem_mapping_string, "Unknown"); break; } return 0; } int pal_parse_sel(uint8_t fru, uint8_t *sel, char *error_log) { bool parsed; uint8_t snr_type = sel[10]; uint8_t snr_num = sel[11]; uint8_t *event_data = &sel[10]; uint8_t *ed = &event_data[3]; char temp_log[512] = {0}; char err_str[512] = {0}; char mem_mapping_string[512] = {0}; uint8_t sen_type = event_data[0]; uint8_t event_type = sel[12] & 0x7F; uint8_t event_dir = sel[12] & 0x80; uint8_t map_of_dimm_num; parsed = false; strcpy(error_log, ""); // First, try checking if the SEL message is a platform-specific one (FBTTN) switch (fru) { case (FRU_SLOT1): switch (snr_num) { case POST_ERROR: parsed = true; if (((ed[0] >> 6) & 0x03) == 0x3) { // Reference from IPMI spec v2.0 Table 42-3, Sensor Type Codes switch (ed[0] & 0xF) { case 0x00: strcat(error_log, "System Firmware Error (POST Error), IPMI Post Code"); if (ed[1] < (sizeof(system_fw_error) / sizeof(system_fw_error[0]))) { sprintf(temp_log, ", %s", system_fw_error[ed[1]].DecodeString); } else { sprintf(temp_log, ", reserved"); } break; case 0x01: strcat(error_log, "System Firmware Hang, IPMI Post Code"); case 0x02: if (strcmp(error_log, "") == 0) { strcat(error_log, "System Firmware Progress, IPMI Post Code"); } if (ed[1] < (sizeof(system_fw_hang_or_progress) / sizeof(system_fw_hang_or_progress[0]))) { sprintf(temp_log, ", %s", system_fw_hang_or_progress[ed[1]].DecodeString); } else { sprintf(temp_log, ", reserved"); } break; default: sprintf(temp_log, "Unknown"); break; } strcat(error_log, temp_log); } else if (((ed[0] >> 6) & 0x03) == 0x2) { if ((ed[0] & 0x0F) == 0x0) strcat(error_log, "System Firmware Error (POST Error)"); else strcat(error_log, "Unknown"); sprintf(temp_log, ", OEM Post Code, 0x%02X%02X", ed[2], ed[1]); strcat(error_log, temp_log); switch ((ed[2] << 8) | ed[1]) { case 0xA104: sprintf(temp_log, ", CMOS/NVRAM configuration cleared"); strcat(error_log, temp_log); break; case 0xA105: sprintf(temp_log, ", BMC Failed (No Response)"); strcat(error_log, temp_log); break; default: break; } } else { strcat(error_log, "Unknown"); } break; case MEMORY_ECC_ERR: parsed = true; map_of_dimm_num = ed[2]; memset(mem_mapping_string, '\0', sizeof(mem_mapping_string)); pal_parse_mem_mapping_str(map_of_dimm_num, mem_mapping_string); if (sen_type == 0x0C) { // SEL from MEMORY_ECC_ERR if ((ed[0] & 0x0F) == 0x0) { strcat(error_log, "Correctable"); snprintf(temp_log, sizeof(temp_log), "DIMM %s(%02X) ECC err", mem_mapping_string, map_of_dimm_num); } else if ((ed[0] & 0x0F) == 0x1) { strcat(error_log, "Uncorrectable"); snprintf(temp_log, sizeof(temp_log), "DIMM %s(%02X) UECC err", mem_mapping_string, map_of_dimm_num); } else if ((ed[0] & 0x0F) == 0x5) { strcat(error_log, "Correctable ECC error Logging Limit Reached"); } else { strcat(error_log, "Sensor Type: 0x0C, Unknown"); } } else if (sen_type == 0x10) { // SEL from MEMORY_ERR_LOG_DIS if ((ed[0] & 0x0F) == 0x5) { strcat(error_log, "Correctable Memory Error Logging Disabled"); } else { strcat(error_log, "Sensor Type: 0x10, Unknown"); } } // Common routine for both MEM_ECC_ERR and MEMORY_ERR_LOG_DIS snprintf(temp_log, sizeof(temp_log), " (DIMM %s)", mem_mapping_string); strcat(error_log, temp_log); snprintf(temp_log, sizeof(temp_log), " Logical Rank %d", ed[1] & 0x03); strcat(error_log, temp_log); // Bit[7:5]: CPU 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 */ // uint8_t chn_num = (ed[2] & 0x18) >> 3; // uint8_t dimm_num = ed[2] & 0x7; /* If critical SEL logging is available, do it */ if (sen_type == 0x0C) { if ((ed[0] & 0x0F) == 0x0) { snprintf(err_str, sizeof(err_str), "ECC err"); } else if ((ed[0] & 0x0F) == 0x1) { snprintf(err_str, sizeof(err_str), "UECC err"); } else if ((ed[0] & 0x0F) == 0x5) { snprintf(err_str, sizeof(err_str), "ECC err logging limit reached"); } else { snprintf(err_str, sizeof(err_str), "Unknown err"); } snprintf(temp_log, sizeof(temp_log), "DIMM %s(%02X) %s, FRU %u", mem_mapping_string, map_of_dimm_num, err_str, fru); pal_add_cri_sel(temp_log); } /* Then continue parse the error into a string. */ /* All Info Valid */ strcpy(temp_log, ""); snprintf(temp_log, sizeof(temp_log)," (CPU# %d, CHN# %d, DIMM# %d)", (ed[2] & 0xE0) >> 5, (ed[2] & 0x18) >> 3, ed[2] & 0x7); } else if (((ed[1] & 0xC) >> 2) == 0x1) { /* DIMM info not valid */ snprintf(temp_log, sizeof(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 */ snprintf(temp_log, sizeof(temp_log), " (CPU# %d, DIMM# %d)", (ed[2] & 0xE0) >> 5, ed[2] & 0x7); } else if (((ed[1] & 0xC) >> 2) == 0x3) { /* CPU info not valid */ snprintf(temp_log, sizeof(temp_log), " (CHN# %d, DIMM# %d)", (ed[2] & 0x18) >> 3, ed[2] & 0x7); } strcat(error_log, temp_log); break; } break; case (FRU_SCC): parsed = true; switch (event_type) { case SENSOR_SPECIFIC: switch (snr_type) { case DIGITAL_DISCRETE: switch (ed[0] & 0x0F) { //Sensor Type Code, Physical Security 0x5h, SENSOR_SPECIFIC Offset 0x0h General Chassis Intrusion case 0x0: if (!event_dir) sprintf(error_log, "Drawer be Pulled Out"); else sprintf(error_log, "Drawer be Pushed Back"); break; } break; } break; case GENERIC: if (ed[0] & 0x0F) sprintf(error_log, "ASSERT, Limit Exceeded"); else sprintf(error_log, "DEASSERT, Limit Not Exceeded"); break; default: sprintf(error_log, "Unknown"); break; } break; default: break; } // If this message was not a platform specific message, just run a // common SEL parsing routine if (!parsed) pal_parse_sel_helper(fru, sel, error_log); 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_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_IOM: sprintf(key, "iom_sensor_health"); break; case FRU_DPB: sprintf(key, "dpb_sensor_health"); break; case FRU_SCC: sprintf(key, "scc_sensor_health"); break; case FRU_NIC: sprintf(key, "nic_sensor_health"); break; default: return -1; } sprintf(cvalue, (value > 0) ? "1": "0"); return pal_set_key_value(key, cvalue); } 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_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_IOM: sprintf(key, "iom_sensor_health"); break; case FRU_DPB: sprintf(key, "dpb_sensor_health"); break; case FRU_SCC: sprintf(key, "scc_sensor_health"); break; case FRU_NIC: sprintf(key, "nic_sensor_health"); break; default: return -1; } ret = pal_get_key_value(key, cvalue); if (ret) { return ret; } *value = atoi(cvalue); memset(key, 0, MAX_KEY_LEN); memset(cvalue, 0, MAX_VALUE_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sel_error", fru); break; case FRU_IOM: return 0; case FRU_DPB: return 0; case FRU_SCC: return 0; case FRU_NIC: return 0; default: return -1; } ret = pal_get_key_value(key, cvalue); if (ret) { return ret; } *value = *value & atoi(cvalue); return 0; } // TBD 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, GPIO_BMC_READY_N, 0); break; case BIC_MODE_UPDATE: // Bridge IC entered update mode // TODO: Might need to handle in future break; default: break; } } int pal_get_fan_name(uint8_t num, char *name) { switch(DPB_SENSOR_FAN1_FRONT+num) { case DPB_SENSOR_FAN1_FRONT: sprintf(name, "Fan 1 Front"); break; case DPB_SENSOR_FAN1_REAR: sprintf(name, "Fan 1 Rear"); break; case DPB_SENSOR_FAN2_FRONT: sprintf(name, "Fan 2 Front"); break; case DPB_SENSOR_FAN2_REAR: sprintf(name, "Fan 2 Rear"); break; case DPB_SENSOR_FAN3_FRONT: sprintf(name, "Fan 3 Front"); break; case DPB_SENSOR_FAN3_REAR: sprintf(name, "Fan 3 Rear"); break; case DPB_SENSOR_FAN4_FRONT: sprintf(name, "Fan 4 Front"); break; case DPB_SENSOR_FAN4_REAR: sprintf(name, "Fan 4 Rear"); break; default: return -1; } return 0; } static int write_fan_value(const int fan, const char *device, const int value) { char full_name[LARGEST_DEVICE_NAME]; char device_name[LARGEST_DEVICE_NAME]; char output_value[LARGEST_DEVICE_NAME]; snprintf(device_name, LARGEST_DEVICE_NAME, device, fan); snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); snprintf(output_value, LARGEST_DEVICE_NAME, "%d", value); return write_device(full_name, output_value); } int pal_set_fan_speed(uint8_t fan, uint8_t pwm) { int unit; int ret; if (fan >= pal_pwm_cnt) { syslog(LOG_INFO, "pal_set_fan_speed: fan number is invalid - %d", fan); return -1; } // Convert the percentage to our 1/100th unit. unit = pwm * PWM_UNIT_MAX / 100; // For 0%, turn off the PWM entirely if (unit == 0) { ret = write_fan_value(fan, "pwm%d_en", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } return 0; // For 100%, set falling and rising to the same value } else if (unit == PWM_UNIT_MAX) { unit = 0; } ret = write_fan_value(fan, "pwm%d_type", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_rising", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_falling", unit); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_en", 1); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } return 0; } int pal_get_fan_speed(uint8_t fan, int *rpm) { int ret; float value; // Fan value have to be fetch real value from DPB, so have to use pal_sensor_read_raw to force updating cache value ret = pal_sensor_read_raw(FRU_DPB, DPB_SENSOR_FAN1_FRONT + fan , &value); if (ret == 0) *rpm = (int) value; return ret; } 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_handle_dcmi(uint8_t fru, uint8_t *request, uint8_t req_len, uint8_t *response, uint8_t *rlen) { return bic_me_xmit(fru, request, req_len, response, rlen); } //For Merge Yosemite and TP int pal_get_platform_id(uint8_t *id) { return 0; } int pal_get_board_rev_id(uint8_t *id) { return 0; } int pal_get_mb_slot_id(uint8_t *id) { return 0; } int pal_get_slot_cfg_id(uint8_t *id) { return 0; } 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, "slot1_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; } int pal_set_boot_order(uint8_t slot, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) { int i; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; *res_len = 0; sprintf(key, "slot1_boot_order"); for (i = 0; i < SIZE_BOOT_ORDER; i++) { snprintf(tstr, 3, "%02x", boot[i]); strncat(str, tstr, 3); } return pal_set_key_value(key, str); } int pal_get_dev_guid(uint8_t fru, char *guid) { return 0; } 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; uint8_t status, ret; unsigned char *data = res_data; // Platform Power Policy memset(str_server_por_cfg, 0 , sizeof(char) * 64); sprintf(str_server_por_cfg, "%s", "slot1_por_cfg"); if (pal_get_key_value(str_server_por_cfg, buff) == 0) { if (!memcmp(buff, "off", strlen("off"))) policy = POWER_CFG_OFF; else if (!memcmp(buff, "lps", strlen("lps"))) policy = POWER_CFG_LPS; else if (!memcmp(buff, "on", strlen("on"))) policy = POWER_CFG_ON; else policy = POWER_CFG_UKNOWN; } // Current Power State ret = pal_get_server_power(FRU_SLOT1, &status); if (ret >= 0) { switch (status) { case SERVER_POWER_ON: status = SERVER_POWER_ON; break; case SERVER_POWER_OFF: case SERVER_12V_OFF: status = SERVER_POWER_OFF; break; default: status = SERVER_POWER_OFF; break; } *data++ = status | (policy << 5); } else { // load default syslog(LOG_WARNING, "ipmid: pal_get_server_power failed for slot1\n"); *data++ = 0x00 | (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_log_clear(char *fru) { if (!strcmp(fru, "server")) { pal_set_key_value("slot1_sensor_health", "1"); pal_set_key_value("slot1_sel_error", "1"); } else if (!strcmp(fru, "iom")) { pal_set_key_value("iom_sensor_health", "1"); } else if (!strcmp(fru, "dpb")) { pal_set_key_value("dpb_sensor_health", "1"); } else if (!strcmp(fru, "scc")) { pal_set_key_value("scc_sensor_health", "1"); } else if (!strcmp(fru, "nic")) { pal_set_key_value("nic_sensor_health", "1"); } else if (!strcmp(fru, "all")) { pal_set_key_value("slot1_sensor_health", "1"); pal_set_key_value("slot1_sel_error", "1"); pal_set_key_value("iom_sensor_health", "1"); pal_set_key_value("dpb_sensor_health", "1"); pal_set_key_value("scc_sensor_health", "1"); pal_set_key_value("nic_sensor_health", "1"); pal_set_key_value("heartbeat_health", "1"); pal_set_key_value("fru_prsnt_health", "1"); pal_set_key_value("bmc_health", "1"); } } // To get the platform sku int pal_get_sku(void){ int pal_sku = 0; // PAL_SKU[6:4] = {SCC_RMT_TYPE_0, SLOTID_0, SLOTID_1} // PAL_SKU[3:0] = {IOM_TYPE0, IOM_TYPE1, IOM_TYPE2, IOM_TYPE3} if (read_device(PLATFORM_FILE, &pal_sku)) { printf("Get platform SKU failed\n"); return -1; } return pal_sku; } // To get the BMC location int pal_get_iom_location(void){ int pal_sku = 0, slotid = 0; // SLOTID_[0:1]: 01=IOM_A; 10=IOM_B pal_sku = pal_get_sku(); slotid = ((pal_sku >> 4) & 0x03); return slotid; } // To get the IOM type int pal_get_iom_type(void){ int pal_sku = 0, iom_type = 0; // Type [0:3]: 0001=M.2 solution; 0010=IOC solution pal_sku = pal_get_sku(); iom_type = (pal_sku & 0x0F); return iom_type; } int pal_is_scc_stb_pwrgood(void){ //To do: to get SCC STB Power good from IO Exp via I2C return 0; } int pal_is_scc_full_pwrgood(void){ //To do: to get SCC STB Power good from IO Exp via I2C return 0; } int pal_is_iom_full_pwrgood(void){ //To do: to get IOM PWR GOOD IOM_FULL_PGOOD GPIOAB2 return 0; } int pal_en_scc_stb_pwr(void){ //To do: enable SCC STB PWR; SCC_STBY_PWR_EN GPIOF4 44 return 0; } int pal_en_scc_full_pwr(void){ //To do: ENABLE SCC STB PWR; SCC_LOC_FULL_PWR_ENGPIOF0 40 return 0; } int pal_en_iom_full_pwr(void){ //To do: enable iom PWR ;IOM_FULL_PWR_EN GPIOAA7 return 0; } //For OEM command "CMD_OEM_GET_PLAT_INFO" 0x7e int pal_get_plat_sku_id(void){ int sku = 0; int location = 0; uint8_t platform_info; sku = pal_get_iom_type(); location = pal_get_iom_location(); if(sku == IOM_M2) { if(location == IOM_SIDEA) { platform_info = PLAT_INFO_SKUID_TYPE5A; } else if(location == IOM_SIDEB) { platform_info = PLAT_INFO_SKUID_TYPE5B; } else return -1; } else if (sku == IOM_IOC) { platform_info = PLAT_INFO_SKUID_TYPE7SS; } else return -1; return platform_info; } //Use part of the function for OEM Command "CMD_OEM_GET_POSS_PCIE_CONFIG" 0xF4 int pal_get_poss_pcie_config(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len){ int sku = 0; uint8_t pcie_conf = 0x00; uint8_t completion_code = CC_UNSPECIFIED_ERROR; unsigned char *data = res_data; sku = pal_get_iom_type(); if(sku == IOM_M2) pcie_conf = PICE_CONFIG_TYPE5; else if (sku == IOM_IOC) pcie_conf = PICE_CONFIG_TYPE7; else return completion_code; *data++ = pcie_conf; *res_len = data - res_data; completion_code = CC_SUCCESS; return completion_code; } int pal_minisas_led(uint8_t port, uint8_t operation) { //operation - 1: on, 0: off if (port == SAS_EXT_PORT_1) { if (operation == LED_ON) return set_gpio_value(BMC_EXT1_LED_Y_N, LED_N_ON); else return set_gpio_value(BMC_EXT1_LED_Y_N, LED_N_OFF); } else if (port == SAS_EXT_PORT_2) { if (operation == LED_ON) return set_gpio_value(BMC_EXT2_LED_Y_N, LED_N_ON); else return set_gpio_value(BMC_EXT2_LED_Y_N, LED_N_OFF); } else { syslog(LOG_WARNING, "%s(): Unexpected mini sas port %d", __func__, port+1); } return 0; } int pal_get_pwm_value(uint8_t fan_num, uint8_t *value) { char path[LARGEST_DEVICE_NAME] = {0}; char device_name[LARGEST_DEVICE_NAME] = {0}; int val = 0; int pwm_enable = 0; snprintf(device_name, LARGEST_DEVICE_NAME, "pwm%d_en", fanid2pwmid_mapping[fan_num]); snprintf(path, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); if (read_device(path, &pwm_enable)) { syslog(LOG_INFO, "pal_get_pwm_value: read %s failed", path); return -1; } // Check the PWM is enable or not if(pwm_enable) { // fan number should in this range if(fan_num >= 0 && fan_num <= 11) snprintf(device_name, LARGEST_DEVICE_NAME, "pwm%d_falling", fanid2pwmid_mapping[fan_num]); else { syslog(LOG_INFO, "pal_get_pwm_value: fan number is invalid - %d", fan_num); return -1; } snprintf(path, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); if (read_device_hex(path, &val)) { syslog(LOG_INFO, "pal_get_pwm_value: read %s failed", path); return -1; } if(val) *value = (100 * val) / PWM_UNIT_MAX; else // 0 means duty cycle is 100% *value = 100; } else //PWM is disable *value = 0; return 0; } int pal_fan_dead_handle(int fan_num) { // TODO: Add action in case of fan dead return 0; } int pal_fan_recovered_handle(int fan_num) { // TODO: Add action in case of fan recovered return 0; } int pal_expander_sensor_check(uint8_t fru, uint8_t sensor_num) { int ret; char key[MAX_KEY_LEN] = {0}; char cvalue[MAX_VALUE_LEN] = {0}; int timestamp, timestamp_flag = 0, current_time, tolerance = 0; //clock_gettime parameters char tstr[MAX_VALUE_LEN] = {0}; struct timespec ts; int sensor_cnt = 1; //default is single call, so just 1 sensor uint8_t *sensor_list; switch(fru) { case FRU_DPB: sprintf(key, "dpb_sensor_timestamp"); break; case FRU_SCC: sprintf(key, "scc_sensor_timestamp"); break; default: return -1; } ret = pal_get_edb_value(key, cvalue); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_expander_sensor_check: pal_get_key_value failed for " "fru %u", fru); #endif return ret; } timestamp = atoi(cvalue); clock_gettime(CLOCK_REALTIME, &ts); sprintf(tstr, "%ld", ts.tv_sec); current_time = atoi(tstr); //set 1 sec tolerance for Firsr Sensor Number, to avoid updating all FRU sensor when interval around 4.9999 second if (sensor_num == DPB_FIRST_SENSOR_NUM || sensor_num == SCC_FIRST_SENSOR_NUM) { tolerance = 1; timestamp_flag = 1; //Update only after First sensor update //Get FRU sensor list for update all sensor ret = pal_get_fru_sensor_list(fru, &sensor_list, &sensor_cnt); if (ret < 0) { return ret; } } //timeout: 5 second, 1 second tolerance only for First sensor if ( abs(current_time - timestamp) > (5 - tolerance) ) { //SCC switch(fru) { case FRU_SCC: ret = pal_exp_scc_read_sensor_wrapper(fru, sensor_list, sensor_cnt, sensor_num); if (ret < 0) { return ret; } break; case FRU_DPB: // DPB sensors are too much, needs twice ipmb commands ret = pal_exp_dpb_read_sensor_wrapper(fru, sensor_list, MAX_EXP_IPMB_SENSOR_COUNT, sensor_num, 0); if (ret < 0) { return ret; } //DO the Second transaction only when sensor number is the first in DPB sensor list if (sensor_num == DPB_FIRST_SENSOR_NUM) { ret = pal_exp_dpb_read_sensor_wrapper(fru, sensor_list, (sensor_cnt - MAX_EXP_IPMB_SENSOR_COUNT), sensor_num, 1); if (ret < 0) { return ret; } } break; } if (timestamp_flag) { //update timestamp after Updated Expander sensor clock_gettime(CLOCK_REALTIME, &ts); sprintf(tstr, "%ld", ts.tv_sec); pal_set_edb_value(key, tstr); } } return 0; } int pal_exp_dpb_read_sensor_wrapper(uint8_t fru, uint8_t *sensor_list, int sensor_cnt, uint8_t sensor_num, int second_transaction) { uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t rlen = 0; uint8_t tlen = 0; int ret, i = 0; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; float value; char units[64]; int offset = 0; //sensor overload offset if (second_transaction) offset = MAX_EXP_IPMB_SENSOR_COUNT; //Fill up sensor number if (sensor_num == DPB_FIRST_SENSOR_NUM) { tbuf[0] = sensor_cnt; for( i = 0 ; i < sensor_cnt; i++) { tbuf[i+1] = sensor_list[i + offset]; //feed sensor number to tbuf } tlen = sensor_cnt + 1; } else { sensor_cnt = 1; tbuf[0] = sensor_cnt; tbuf[1] = sensor_num; tlen = 2; } //send tbuf with sensor count and numbers to get spcific sensor data from exp ret = expander_ipmb_wrapper(NETFN_OEM_REQ, CMD_EXP_GET_SENSOR_READING, tbuf, tlen, rbuf, &rlen); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "%s: expander_ipmb_wrapper failed.", __func__); #endif //if expander doesn't respond, set all sensors value to NA and save to cache for(i = 0; i < sensor_cnt; i++) { sprintf(key, "dpb_sensor%d", tbuf[i+1]); sprintf(str, "NA"); if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "%s: cache_set key = %s, str = %s failed.", __func__ , key, str); #endif } } return ret; } for(i = 0; i < sensor_cnt; i++) { if (rbuf[5*i+4] != 0) { //if sensor status byte is not 0, means sensor reading is unavailable sprintf(str, "NA"); } else { // search the corresponding sensor table to fill up the raw data and status // rbuf[5*i+1] sensor number // rbuf[5*i+2] sensor raw data1 // rbuf[5*i+3] sensor raw data2 // rbuf[5*i+4] sensor status // rbuf[5*i+5] reserved fbttn_sensor_units(fru, rbuf[5*i+1], units); if( strcmp(units,"C") == 0 ) { value = rbuf[5*i+2]; } else if( rbuf[5*i+1] >= DPB_SENSOR_FAN1_FRONT && rbuf[5*i+1] <= DPB_SENSOR_FAN4_REAR ) { value = (((rbuf[5*i+2] << 8) + rbuf[5*i+3])); value = value * 10; } else if( rbuf[5*i+1] == DPB_SENSOR_HSC_POWER || rbuf[5*i+1] == DPB_SENSOR_12V_POWER_CLIP || (rbuf[5*i+1] == AIRFLOW) ) { value = (((rbuf[5*i+2] << 8) + rbuf[5*i+3])); } else { value = (((rbuf[5*i+2] << 8) + rbuf[5*i+3])); value = value/100; } sprintf(str, "%.2f",(float)value); } //cache sensor reading sprintf(key, "dpb_sensor%d", rbuf[5*i+1]); if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "%s: cache_set key = %s, str = %s failed.", __func__ , key, str); #endif } } return 0; } int pal_exp_scc_read_sensor_wrapper(uint8_t fru, uint8_t *sensor_list, int sensor_cnt, uint8_t sensor_num) { uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t rlen = 0; uint8_t tlen = 0; int ret, i; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; float value; char units[64]; uint8_t status; tbuf[0] = sensor_cnt; //sensor_count //Fill up sensor number if (sensor_num == SCC_FIRST_SENSOR_NUM) { for( i = 0 ; i < sensor_cnt; i++) { tbuf[i+1] = sensor_list[i]; //feed sensor number to tbuf } } else { tbuf[1] = sensor_num; } tlen = sensor_cnt + 1; //send tbuf with sensor count and numbers to get spcific sensor data from exp ret = expander_ipmb_wrapper(NETFN_OEM_REQ, CMD_EXP_GET_SENSOR_READING, tbuf, tlen, rbuf, &rlen); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "%s: expander_ipmb_wrapper failed.", __func__); #endif //if expander doesn't respond, set all sensors value to NA and save to cache for(i = 0; i < sensor_cnt; i++) { sprintf(key, "scc_sensor%d", tbuf[i+1]); sprintf(str, "NA"); if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "%s: cache_set key = %s, str = %s failed.", __func__ , key, str); #endif } } return ret; } for(i = 0; i < sensor_cnt; i++) { if (rbuf[5*i+4] != 0) { //if sensor status byte is not 0, means sensor reading is unavailable sprintf(str, "NA"); } else { // search the corresponding sensor table to fill up the raw data and status // rbuf[5*i+1] sensor number // rbuf[5*i+2] sensor raw data1 // rbuf[5*i+3] sensor raw data2 // rbuf[5*i+4] sensor status // rbuf[5*i+5] reserved fbttn_sensor_units(fru, rbuf[5*i+1], units); if( strcmp(units,"C") == 0 ) { value = rbuf[5*i+2]; } else if( strcmp(units,"Watts") == 0 ) { value = (((rbuf[5*i+2] << 8) + rbuf[5*i+3])); } else { value = (((rbuf[5*i+2] << 8) + rbuf[5*i+3])); value = value/100; } sprintf(str, "%.2f",(float)value); // SCC_IOC have to check if the server is on, if not shows "NA" if (rbuf[5*i+1] == SCC_SENSOR_IOC_TEMP) { pal_get_server_power(FRU_SLOT1, &status); if (status != SERVER_POWER_ON) { strcpy(str, "NA"); } } } //cache sensor reading sprintf(key, "scc_sensor%d", rbuf[5*i+1]); if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "%s: cache_set key = %s, str = %s failed.", __func__, key, str); #endif } } return 0; } int pal_get_bmc_rmt_hb(void) { int bmc_rmt_hb = 0; char path[64] = {0}; sprintf(path, TACH_RPM, TACH_BMC_RMT_HB); if (read_device(path, &bmc_rmt_hb)) { return -1; } return bmc_rmt_hb; } int pal_get_scc_loc_hb(void) { int scc_loc_hb = 0; char path[64] = {0}; sprintf(path, TACH_RPM, TACH_SCC_LOC_HB); if (read_device(path, &scc_loc_hb)) { return -1; } return scc_loc_hb; } int pal_get_scc_rmt_hb(void) { int scc_rmt_hb = 0; char path[64] = {0}; sprintf(path, TACH_RPM, TACH_SCC_RMT_HB); if (read_device(path, &scc_rmt_hb)) { return -1; } return scc_rmt_hb; } void pal_err_code_enable(unsigned char num) { error_code errorCode = {0, 0}; if(num < 100) { // It's used for expander (0~99). return; } errorCode.code = num; if (errorCode.code < (ERROR_CODE_NUM * 8)) { errorCode.status = 1; pal_write_error_code_file(&errorCode); } else { syslog(LOG_WARNING, "%s(): wrong error code number", __func__); } } void pal_err_code_disable(unsigned char num) { error_code errorCode = {0, 0}; if(num < 100) { return; } errorCode.code = num; if (errorCode.code < (ERROR_CODE_NUM * 8)) { errorCode.status = 0; pal_write_error_code_file(&errorCode); } else { syslog(LOG_WARNING, "%s(): wrong error code number", __func__); } } uint8_t pal_read_error_code_file(uint8_t *error_code_array) { FILE *fp = NULL; uint8_t ret = 0; int i = 0; int tmp = 0; int retry_count = 0; if (access(ERR_CODE_FILE, F_OK) == -1) { memset(error_code_array, 0, ERROR_CODE_NUM); return 0; } else { fp = fopen(ERR_CODE_FILE, "r"); } if (!fp) { return -1; } ret = flock(fileno(fp), LOCK_EX | LOCK_NB); while (ret && (retry_count < 3)) { retry_count++; msleep(100); ret = flock(fileno(fp), LOCK_EX | LOCK_NB); } if (ret) { int err = errno; syslog(LOG_WARNING, "%s(): failed to flock on %s, err %d", __func__, ERR_CODE_FILE, err); fclose(fp); return -1; } for (i = 0; fscanf(fp, "%X", &tmp) != EOF && i < ERROR_CODE_NUM; i++) { error_code_array[i] = (uint8_t) tmp; } flock(fileno(fp), LOCK_UN); fclose(fp); return 0; } uint8_t pal_write_error_code_file(error_code *update) { FILE *fp = NULL; uint8_t ret = 0; int retry_count = 0; int i = 0; int stat = 0; int bit_stat = 0; uint8_t error_code_array[ERROR_CODE_NUM] = {0}; if (pal_read_error_code_file(error_code_array) != 0) { syslog(LOG_ERR, "%s(): pal_read_error_code_file() failed", __func__); return -1; } if (access(ERR_CODE_FILE, F_OK) == -1) { fp = fopen(ERR_CODE_FILE, "w"); } else { fp = fopen(ERR_CODE_FILE, "r+"); } if (!fp) { syslog(LOG_ERR, "%s(): open %s failed. %s", __func__, ERR_CODE_FILE, strerror(errno)); return -1; } ret = flock(fileno(fp), LOCK_EX | LOCK_NB); while (ret && (retry_count < 3)) { retry_count++; msleep(100); ret = flock(fileno(fp), LOCK_EX | LOCK_NB); } if (ret) { syslog(LOG_WARNING, "%s(): failed to flock on %s. %s", __func__, ERR_CODE_FILE, strerror(errno)); fclose(fp); return -1; } stat = update->code / 8; bit_stat = update->code % 8; if (update->status) { error_code_array[stat] |= 1 << bit_stat; } else { error_code_array[stat] &= ~(1 << bit_stat); } for (i = 0; i < ERROR_CODE_NUM; i++) { fprintf(fp, "%X ", error_code_array[i]); if(error_code_array[i] != 0) { ret = 1; } } fprintf(fp, "\n"); flock(fileno(fp), LOCK_UN); fclose(fp); return ret; } /* * Calculate the sum of error code * If Err happen, the sum does not equal to 0 * */ unsigned char pal_sum_error_code(void) { uint8_t error_code_array[ERROR_CODE_NUM] = {0}; int i; if (pal_read_error_code_file(error_code_array) != 0) { syslog(LOG_ERR, "%s(): pal_read_error_code_file() failed", __func__); return -1; } for (i = 0; i < ERROR_CODE_NUM; i++) { if (error_code_array[i] != 0) { return 1; } } return 0; } void pal_sensor_assert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { uint8_t status; if ((snr_num == MEZZ_SENSOR_TEMP) && (thresh == UNR_THRESH)) { pal_get_server_power(FRU_SLOT1, &status); if (status != SERVER_12V_OFF) { pal_nic_otp(FRU_NIC, snr_num, nic_sensor_threshold[snr_num][UNR_THRESH]); } } return; } void pal_sensor_deassert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { uint8_t status; if ((snr_num == MEZZ_SENSOR_TEMP) && (thresh == UNC_THRESH)) { pal_get_server_power(FRU_SLOT1, &status); if ((status != SERVER_12V_ON) && (otp_server_12v_off_flag == 1)) { // power on Mono Lake 12V HSC syslog(LOG_CRIT, "Due to NIC temp UNC deassert. Power On Server 12V. (val = %.2f)", val); server_12v_on(FRU_SLOT1); otp_server_12v_off_flag = 0; } } return; } int pal_get_fw_info(uint8_t fru, unsigned char target, unsigned char* res, unsigned char* res_len) { if(target > TARGET_VR_PVCCSCUS_VER) return -1; if( target!= TARGET_BIOS_VER ) { bic_get_fw_ver(FRU_SLOT1, target, res); if( target == TARGET_BIC_VER) *res_len = 2; else *res_len = 4; return 0; } if( target == TARGET_BIOS_VER ) { pal_get_sysfw_ver(FRU_SLOT1, res); *res_len = 2 + res[2]; return 0; } return -1; } int pal_self_tray_location(uint8_t *value) { char path[64] = {0}; int val; sprintf(path, GPIO_VAL, GPIO_CHASSIS_INTRUSION); if (read_device(path, &val)) return -1; *value = (uint8_t) val; return 0; } int pal_get_iom_ioc_ver(uint8_t *ver) { return mctp_get_iom_ioc_ver(ver); } int pal_oem_bitmap(uint8_t* in_error,uint8_t* data) { int ret = 0; int ii=0; int kk=0; int NUM = 0; if(data == NULL) { return 0; } for(ii = 0; ii < 32; ii++) { for(kk = 0; kk < 8; kk++) { if(((in_error[ii] >> kk)&0x01) == 1) { NUM = ii*8 + kk; *(data + ret) = NUM; ret++; } } } return ret; } int pal_get_error_code(uint8_t data[MAX_ERROR_CODES], uint8_t* error_count) { uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t rlen = 0; uint8_t tlen = 0; FILE *fp; uint8_t exp_error[13]; uint8_t error[32]; int ret, count = 0; ret = expander_ipmb_wrapper(NETFN_OEM_REQ, EXPANDER_ERROR_CODE, tbuf, tlen, rbuf, &rlen); if (ret) { printf("Error: Expander Error Code Query Fail...\n"); #ifdef DEBUG syslog(LOG_WARNING, "enclosure-util: get_error_code, expander_ipmb_wrapper failed."); #endif memset(exp_error, 0, 13); //When Epander Fail, fill all data to 0 } else memcpy(exp_error, rbuf, rlen); exp_error[0] &= ~(0x1); //Expander Error Code 1 is no Error, Ignore it. fp = fopen("/tmp/error_code.bin", "r"); if (!fp) { printf("fopen Fail: %s, Error Code: %d\n", strerror(errno), errno); #ifdef DEBUG syslog(LOG_WARNING, "enclosure-util get_error_code, BMC error code File open failed...\n"); #endif memset(error, 0, 32); //When BMC Error Code file Open Fail, fill all data to 0 } else { lockf(fileno(fp),F_LOCK,0L); while (fscanf(fp, "%hhX", error+count) != EOF && count!=32) { count++; } lockf(fileno(fp),F_ULOCK,0L); fclose(fp); } //Expander Error Code 0~99; BMC Error Code 100~255 memcpy(error, exp_error, rlen - 1); //Not the last one (12th) error[12] = ((error[12] & 0xF0) + (exp_error[12] & 0xF)); memset(data, 0, MAX_ERROR_CODES); *error_count = pal_oem_bitmap(error, data); return 0; } // Get post code buffer of the given slot from BIC int pal_post_get_buffer(uint8_t *buffer, uint8_t *buf_len) { int ret; uint8_t buf[MAX_IPMB_RES_LEN] = {0x0}; uint8_t len; ret = bic_get_post_buf(FRU_SLOT1, buf, &len); if (ret) return ret; // The post buffer is LIFO and the first byte gives the latest post code memcpy(buffer, buf, len); *buf_len = len; return 0; } void pal_add_cri_sel(char *str) { char cmd[128]; snprintf(cmd, 128, "logger -p local0.err \"%s\"",str); system(cmd); } void pal_i2c_crash_assert_handle(int i2c_bus_num) { // I2C bus number: 0~13 if (i2c_bus_num < I2C_BUS_MAX_NUMBER) { pal_err_code_enable(ERR_CODE_I2C_CRASH_BASE + i2c_bus_num); } else { syslog(LOG_WARNING, "%s(): wrong I2C bus number", __func__); } } void pal_i2c_crash_deassert_handle(int i2c_bus_num) { // I2C bus number: 0~13 if (i2c_bus_num < I2C_BUS_MAX_NUMBER) { pal_err_code_disable(ERR_CODE_I2C_CRASH_BASE + i2c_bus_num); } else { syslog(LOG_WARNING, "%s(): wrong I2C bus number", __func__); } } int pal_set_bios_current_boot_list(uint8_t slot, uint8_t *boot_list, uint8_t list_length, uint8_t *cc) { FILE *fp; int i; if(*boot_list != 1) { *cc = CC_INVALID_PARAM; return -1; } fp = fopen(BOOT_LIST_FILE, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", BOOT_LIST_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); for(i = 0; i < list_length; i++) { fprintf(fp, "%02X ", *(boot_list+i)); } fprintf(fp, "\n"); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } int pal_get_bios_current_boot_list(uint8_t slot, uint8_t *boot_list, uint8_t *list_length) { FILE *fp; uint8_t count=0; fp = fopen(BOOT_LIST_FILE, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", BOOT_LIST_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); while (fscanf(fp, "%hhX", boot_list+count) != EOF) { count++; } *list_length = count; lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } int pal_set_bios_fixed_boot_device(uint8_t slot, uint8_t *fixed_boot_device) { FILE *fp; fp = fopen(FIXED_BOOT_DEVICE_FILE, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", FIXED_BOOT_DEVICE_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); fprintf(fp, "%02X ", *fixed_boot_device); fprintf(fp, "\n"); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } void *pal_clear_bios_default_setting_timer_handler(void *unused){ uint8_t default_setting; bios_default_setting_timer_flag = 0; sleep(200); pal_get_bios_restores_default_setting(1, &default_setting); if(default_setting != 0) { default_setting = 0; pal_set_bios_restores_default_setting(1, &default_setting); } return NULL; } int pal_set_bios_restores_default_setting(uint8_t slot, uint8_t *default_setting) { FILE *fp; pthread_t tid_clear_bios_default_setting_timer; fp = fopen(BIOS_DEFAULT_SETTING_FILE, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", BIOS_DEFAULT_SETTING_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); fprintf(fp, "%02X ", *default_setting); fprintf(fp, "\n"); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); //Hack a thread wait a certain time then clear the setting, when userA didn't reboot the System, and userB doesn't know about the setting. if(!bios_default_setting_timer_flag) { if (pthread_create(&tid_clear_bios_default_setting_timer, NULL, pal_clear_bios_default_setting_timer_handler, NULL) < 0) { syslog(LOG_WARNING, "pthread_create for clear default setting timer error\n"); exit(1); } else bios_default_setting_timer_flag = 1; } return 0; } int pal_get_bios_restores_default_setting(uint8_t slot, uint8_t *default_setting) { FILE *fp; fp = fopen(BIOS_DEFAULT_SETTING_FILE, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", BIOS_DEFAULT_SETTING_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); fscanf(fp, "%hhX", default_setting); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } int pal_set_last_boot_time(uint8_t slot, uint8_t *last_boot_time) { FILE *fp; int i; fp = fopen(LAST_BOOT_TIME, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", LAST_BOOT_TIME); #endif return err; } lockf(fileno(fp),F_LOCK,0L); for(i = 0; i < 4; i++) { fprintf(fp, "%02X ", *(last_boot_time+i)); } fprintf(fp, "\n"); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } int pal_get_last_boot_time(uint8_t slot, uint8_t *last_boot_time) { FILE *fp; int i; fp = fopen(LAST_BOOT_TIME, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", LAST_BOOT_TIME); #endif return err; } lockf(fileno(fp),F_LOCK,0L); for(i = 0; i < 4; i++) { fscanf(fp, "%hhX", last_boot_time+i); } lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } int pal_get_bios_fixed_boot_device(uint8_t slot, uint8_t *fixed_boot_device) { FILE *fp; fp = fopen(FIXED_BOOT_DEVICE_FILE, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", FIXED_BOOT_DEVICE_FILE); #endif return err; } lockf(fileno(fp),F_LOCK,0L); fscanf(fp, "%hhX", fixed_boot_device); lockf(fileno(fp),F_ULOCK,0L); fclose(fp); return 0; } unsigned char option_offset[] = {0,1,2,3,4,6,11,20,37,164}; unsigned char option_size[] = {1,1,1,1,2,5,9,17,127}; void pal_save_boot_option(unsigned char* buff) { int fp = 0; fp = open("/tmp/boot.in", O_WRONLY|O_CREAT); if(fp > 0 ) { write(fp,buff,256); close(fp); } } int pal_load_boot_option(unsigned char* buff) { int fp = 0; fp = open("/tmp/boot.in", O_RDONLY); if(fp > 0 ) { read(fp,buff,256); close(fp); return 0; } else return -1; } void pal_set_boot_option(unsigned char para,unsigned char* pbuff) { unsigned char buff[256] = { 0 }; unsigned char offset = option_offset[para]; unsigned char size = option_size[para]; pal_load_boot_option(buff); memcpy(buff + offset, pbuff, size); pal_save_boot_option(buff); } int pal_get_boot_option(unsigned char para,unsigned char* pbuff) { unsigned char buff[256] = { 0 }; int ret = 0; unsigned char offset = option_offset[para]; unsigned char size = option_size[para]; ret = pal_load_boot_option(buff); if (!ret){ memcpy(pbuff,(buff + offset), size); } else memcpy(pbuff,buff,size); return size; } int pal_nic_otp(int fru, int snr_num, float thresh_val) { int retry = 0; int ret = 0; float curr_val = 0; while (retry < NIC_TEMP_RETRY) { ret = pal_sensor_read_raw(fru, snr_num, &curr_val); if (ret < 0) { return -1; } if (curr_val >= thresh_val) { retry++; } else { return 0; } msleep(200); } // power off Mono Lake 12V HSC syslog(LOG_CRIT, "Powering Off Server 12V due to NIC temp UNR reached. (val = %.2f)", curr_val); server_12v_off(FRU_SLOT1); otp_server_12v_off_flag = 1; return 0; } int pal_bmc_err_enable(const char *error_item) { if (strcasestr(error_item, "CPU") != 0ULL) { pal_err_code_enable(ERR_CODE_CPU); } else if (strcasestr(error_item, "Memory") != 0ULL) { pal_err_code_enable(ERR_CODE_MEM); } else if (strcasestr(error_item, "ECC Unrecoverable") != 0ULL) { pal_err_code_enable(ERR_CODE_ECC_UNRECOVERABLE); } else if (strcasestr(error_item, "ECC Recoverable") != 0ULL) { pal_err_code_enable(ERR_CODE_ECC_RECOVERABLE); } else { syslog(LOG_WARNING, "%s: invalid bmc health item: %s", __func__, error_item); return -1; } return 0; } int pal_bmc_err_disable(const char *error_item) { if (strcasestr(error_item, "CPU") != 0ULL) { pal_err_code_disable(ERR_CODE_CPU); } else if (strcasestr(error_item, "Memory") != 0ULL) { pal_err_code_disable(ERR_CODE_MEM); } else if (strcasestr(error_item, "ECC Unrecoverable") != 0ULL) { pal_err_code_disable(ERR_CODE_ECC_UNRECOVERABLE); } else if (strcasestr(error_item, "ECC Recoverable") != 0ULL) { pal_err_code_disable(ERR_CODE_ECC_RECOVERABLE); } else { syslog(LOG_WARNING, "%s: invalid bmc health item: %s", __func__, error_item); return -1; } return 0; } uint8_t pal_set_power_restore_policy(uint8_t slot, uint8_t *pwr_policy, uint8_t *res_data) { uint8_t completion_code; completion_code = CC_SUCCESS; // Fill response with default values unsigned char policy = *pwr_policy & 0x07; // Power restore policy if (slot != FRU_SLOT1) { return -1; } switch (policy) { case 0: if (pal_set_key_value("slot1_por_cfg", "off") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 1: if (pal_set_key_value("slot1_por_cfg", "lps") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 2: if (pal_set_key_value("slot1_por_cfg", "on") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 3: // no change (just get present policy support) break; default: completion_code = CC_PARAM_OUT_OF_RANGE; break; } return completion_code; } int pal_drive_status(const char* i2c_bus) { ssd_data ssd; t_status_flags status_flag_decoding; t_smart_warning smart_warning_decoding; t_key_value_pair temp_decoding; t_key_value_pair pdlu_decoding; t_key_value_pair vendor_decoding; t_key_value_pair sn_decoding; if (nvme_vendor_read_decode(i2c_bus, &ssd.vendor, &vendor_decoding)) printf("%s: Fail on reading Vendor ID\n", vendor_decoding.key); else printf("%s: %s\n", vendor_decoding.key, vendor_decoding.value); if (nvme_serial_num_read_decode(i2c_bus, ssd.serial_num, MAX_SERIAL_NUM, &sn_decoding)) printf("%s: Fail on reading Serial Number\n", sn_decoding.key); else printf("%s: %s\n", sn_decoding.key, sn_decoding.value); if (nvme_temp_read_decode(i2c_bus, &ssd.temp, &temp_decoding)) printf("%s: Fail on reading Composite Temperature\n", temp_decoding.key); else printf("%s: %s\n", temp_decoding.key, temp_decoding.value); if (nvme_pdlu_read_decode(i2c_bus, &ssd.pdlu, &pdlu_decoding)) printf("%s: Fail on reading Percentage Drive Life Used\n", pdlu_decoding.key); else printf("%s: %s\n", pdlu_decoding.key, pdlu_decoding.value); if (nvme_sflgs_read_decode(i2c_bus, &ssd.sflgs, &status_flag_decoding)) printf("%s: Fail on reading Status Flags\n", status_flag_decoding.self.key); else { printf("%s: %s\n", status_flag_decoding.self.key, status_flag_decoding.self.value); printf(" %s: %s\n", status_flag_decoding.read_complete.key, status_flag_decoding.read_complete.value); printf(" %s: %s\n", status_flag_decoding.ready.key, status_flag_decoding.ready.value); printf(" %s: %s\n", status_flag_decoding.functional.key, status_flag_decoding.functional.value); printf(" %s: %s\n", status_flag_decoding.reset_required.key, status_flag_decoding.reset_required.value); printf(" %s: %s\n", status_flag_decoding.port0_link.key, status_flag_decoding.port0_link.value); printf(" %s: %s\n", status_flag_decoding.port1_link.key, status_flag_decoding.port1_link.value); } if (nvme_smart_warning_read_decode(i2c_bus, &ssd.warning, &smart_warning_decoding)) printf("%s: Fail on reading SMART Critical Warning\n", smart_warning_decoding.self.key); else { printf("%s: %s\n", smart_warning_decoding.self.key, smart_warning_decoding.self.value); printf(" %s: %s\n", smart_warning_decoding.spare_space.key, smart_warning_decoding.spare_space.value); printf(" %s: %s\n", smart_warning_decoding.temp_warning.key, smart_warning_decoding.temp_warning.value); printf(" %s: %s\n", smart_warning_decoding.reliability.key, smart_warning_decoding.reliability.value); printf(" %s: %s\n", smart_warning_decoding.media_status.key, smart_warning_decoding.media_status.value); printf(" %s: %s\n", smart_warning_decoding.backup_device.key, smart_warning_decoding.backup_device.value); } printf("\n"); return 0; } int pal_drive_health(const char* dev) { uint8_t sflgs; uint8_t warning; if (nvme_smart_warning_read(dev, &warning)) return -1; else if ((warning & NVME_SMART_WARNING_MASK_BIT) != NVME_SMART_WARNING_MASK_BIT) return -1; if (nvme_sflgs_read(dev, &sflgs)) return -1; else if ((sflgs & NVME_SFLGS_MASK_BIT) != NVME_SFLGS_CHECK_VALUE) return -1; return 0; } int pal_open_fw_update_flag(void) { int fd; //if fd = 0 means open successfully. fd = open(FW_UPDATE_FLAG, O_CREAT); if (!fd) { close(fd); } return fd; } int pal_remove_fw_update_flag(void) { int ret; //if ret = 0 means remove successfully. ret = remove(FW_UPDATE_FLAG); return ret; } int pal_get_fw_update_flag(void) { int ret; //if ret = 0 means BMC is Updating a Device FW, -1 means BMC is not Updating a Device FW ret = access(FW_UPDATE_FLAG, F_OK); return !ret; } //To control iom led to yellow or blue color uint8_t pal_iom_led_control(uint8_t color) { switch(color) { case IOM_LED_OFF: if(set_gpio_value(GPIO_BMC_ML_PWR_YELLOW_LED_N, LED_N_OFF)) break; if(set_gpio_value(GPIO_BMC_ML_PWR_BLUE_LED, LED_OFF)) break; return 0; case IOM_LED_YELLOW: if(set_gpio_value(GPIO_BMC_ML_PWR_YELLOW_LED_N, LED_N_ON)) break; if(set_gpio_value(GPIO_BMC_ML_PWR_BLUE_LED, LED_OFF)) break; return 0; case IOM_LED_BLUE: if(set_gpio_value(GPIO_BMC_ML_PWR_YELLOW_LED_N, LED_N_OFF)) break; if(set_gpio_value(GPIO_BMC_ML_PWR_BLUE_LED, LED_ON)) break; return 0; default: break; } syslog(LOG_WARNING, "%s, Failed to Control IOM LED...", __func__); return -1; } // ex: set_gpio_value(GPIO_IOM_FULL_PWR_EN, 1); int set_gpio_value(int gpio_num, uint8_t value) { char vpath[64] = {0}; sprintf(vpath, GPIO_VAL, gpio_num); if (value == 0) return write_device(vpath, "0"); else if (value == 1) return write_device(vpath, "1"); else return -1; } int get_gpio_value(int gpio_num, uint8_t *value){ char vpath[64] = {0}; FILE *fp = NULL; int rc = 0; int ret = 0; int retry_cnt = 5; int i = 0; int tmp_value = -1; sprintf(vpath, GPIO_VAL, gpio_num); for (i = 0; i < retry_cnt; i++) { fp = fopen(vpath, "r"); if (fp == NULL) { syslog(LOG_ERR, "%s(): failed to open device %s (%s)", __func__, vpath, strerror(errno)); if (i == (retry_cnt - 1)) { return errno; } } else { break; } msleep(100); } for (i = 0; i < retry_cnt; i++) { ret = 0; rc = fscanf(fp, "%d", &tmp_value); if (rc != 1) { syslog(LOG_ERR, "failed to read device %s (%s)", vpath, strerror(errno)); if (i == (retry_cnt - 1)) { ret = errno; } } else { break; } msleep(100); } fclose(fp); *value = (uint8_t) tmp_value; return ret; } int pal_get_iom_board_id (void) { uint8_t gpio_board_rev_val[3] = {0}; int iom_board_id = BOARD_MP; int ret = 0; ret = get_gpio_value(GPIO_BOARD_REV_0, &gpio_board_rev_val[0]); if (ret != 0) { goto default_iom_board_id; } ret = get_gpio_value(GPIO_BOARD_REV_1, &gpio_board_rev_val[1]); if (ret != 0) { goto default_iom_board_id; } ret = get_gpio_value(GPIO_BOARD_REV_2, &gpio_board_rev_val[2]); if (ret != 0) { goto default_iom_board_id; } iom_board_id = (gpio_board_rev_val[0] << 2) | (gpio_board_rev_val[1] << 1) | gpio_board_rev_val[2]; return iom_board_id; default_iom_board_id: syslog(LOG_WARNING, "%s: cannot get IOM board ID, used default setting: MP", __func__); iom_board_id = BOARD_MP; return iom_board_id; } int pal_get_pcie_port_config (uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t pcie_port_config[SIZE_PCIE_PORT_CONFIG] = {0}; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; int msb, lsb; int ret; int i; int j = 0; sprintf(key, "server_pcie_port_config"); ret = pal_get_key_value(key, str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2 * SIZE_PCIE_PORT_CONFIG; 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); pcie_port_config[j] = (msb << 4) | lsb; j++; } memcpy(res_data, pcie_port_config, SIZE_PCIE_PORT_CONFIG); *res_len = SIZE_PCIE_PORT_CONFIG; return 0; } int pal_set_pcie_port_config (uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t pcie_port_config[SIZE_PCIE_PORT_CONFIG] = {0}; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; int i; *res_len = 0; sprintf(key, "server_pcie_port_config"); memcpy(pcie_port_config, req_data, SIZE_PCIE_PORT_CONFIG); for (i = 0; i < SIZE_PCIE_PORT_CONFIG; i++) { snprintf(tstr, 3, "%02x", pcie_port_config[i]); strncat(str, tstr, 3); } return pal_set_key_value(key, str); } int pal_get_edb_value(char *key, char *value) { int i = 0; int ret = 0; for (i = 0; i < RETRY_COUNT; i++) { ret = 0; ret = kv_get(key, value, NULL, 0); if (ret != 0) { syslog(LOG_ERR, "%s, failed to read edb key (%s), ret: %d, retry: %d", __func__, key, ret, i); } else { break; } msleep(100); } return ret; } int pal_set_edb_value(char *key, char *value) { int i = 0; int ret = 0; for (i = 0; i < RETRY_COUNT; i++) { ret = 0; ret = kv_set(key, value, 0, 0); if (ret != 0) { syslog(LOG_ERR, "%s, failed to write edb key (%s), ret: %d, retry: %d", __func__, key, ret, i); } else { break; } msleep(100); } return ret; } int pal_powering_on_flag(uint8_t slot_id) { int pid_file; char path[128]; sprintf(path, SERVER_PWR_ON_LOCK, slot_id); pid_file = open(path, O_CREAT | O_RDWR, 0666); if (pid_file == -1) { return -1; } close(pid_file); return 0; } void pal_rm_powering_on_flag(uint8_t slot_id) { char path[128]; sprintf(path, SERVER_PWR_ON_LOCK, slot_id); remove(path); } //Overwrite the one in obmc-pal.c without systme call of flashcp check 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; } //check power policy and power state to power on/off server after AC power restore void pal_power_policy_control(uint8_t fru, char *last_ps) { uint8_t chassis_status[5] = {0}; uint8_t chassis_status_length; uint8_t power_policy = POWER_CFG_UKNOWN; char pwr_state[MAX_VALUE_LEN] = {0}; //get power restore policy //defined by IPMI Spec/Section 28.2. pal_get_chassis_status(fru, NULL, chassis_status, &chassis_status_length); //byte[1], bit[6:5]: power restore policy power_policy = (*chassis_status >> 5); //Check power policy and last power state if(power_policy == POWER_CFG_LPS) { if (!last_ps) { pal_get_last_pwr_state(fru, pwr_state); last_ps = pwr_state; } if (!(strcmp(last_ps, "on"))) { sleep(3); pal_set_server_power(fru, SERVER_POWER_ON); } } else if(power_policy == POWER_CFG_ON) { sleep(3); pal_set_server_power(fru, SERVER_POWER_ON); } } // 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; uint8_t tbuf[256] = {0x00}; uint8_t status; if (slot != FRU_SLOT1) { 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_is_server_12v_on(slot, &status); if((ret < 0) || (status == CTRL_DISABLE)) { return CC_NOT_SUPP_IN_CURR_STATE; } select = req_data[0]; netfn = req_data[1]; cmd = req_data[2]; tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) if (tlen < 0) { return completion_code; } if (BYPASS_BIC == select) { //BIC // 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; } } else if (BYPASS_ME == select) { //ME tlen += 2; memcpy(tbuf, &req_data[1], tlen); tbuf[0] = tbuf[0] << 2; // Bypass command to ME ret = bic_me_xmit(slot, tbuf, tlen, res_data, res_len); if (0 == ret) { completion_code = CC_SUCCESS; } } return completion_code; }