meta-facebook/meta-fby35/recipes-fby35/plat-libs/files/bic/bic_ipmi.c (1,621 lines of code) (raw):

/* * * Copyright 2015-present Facebook. All Rights Reserved. * * This file contains code to support IPMI2.0 Specificaton available @ * http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <syslog.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/file.h> #include <openbmc/obmc-i2c.h> #include <openbmc/kv.h> #include "bic_ipmi.h" #include "bic_xfer.h" //FRU #define FRUID_READ_COUNT_MAX 0x20 #define FRUID_WRITE_COUNT_MAX 0x20 #define FRUID_SIZE 256 //SDR #define SDR_READ_COUNT_MAX 0x1A #pragma pack(push, 1) typedef struct _sdr_rec_hdr_t { uint16_t rec_id; uint8_t ver; uint8_t type; uint8_t len; } sdr_rec_hdr_t; #pragma pack(pop) #define SIZE_SYS_GUID 16 #define KV_SLOT_IS_M2_EXP_PRESENT "slot%x_is_m2_exp_prsnt" #define KV_SLOT_GET_1OU_TYPE "slot%x_get_1ou_type" #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define BIC_SENSOR_SYSTEM_STATUS 0x10 #define BB_FW_UPDATE_STAT_FILE "/tmp/cache_store/bb_fw_update" enum { M2_PWR_OFF = 0x00, M2_PWR_ON = 0x01, M2_REG_2OU = 0x04, M2_REG_1OU = 0x05, }; enum { M2_ROOT_PORT0 = 0x0, M2_ROOT_PORT1 = 0x1, M2_ROOT_PORT2 = 0x2, M2_ROOT_PORT3 = 0x3, M2_ROOT_PORT4 = 0x4, M2_ROOT_PORT5 = 0x5, }; enum { SNR_READ_CACHE = 0, SNR_READ_FORCE = 1, }; enum { BB_BIC_SLOT3_PRSNT_PIN = 3, BB_BIC_SLOT1_PRSNT_PIN = 15, }; uint8_t mapping_m2_prsnt[2][6] = { {M2_ROOT_PORT0, M2_ROOT_PORT1, M2_ROOT_PORT5, M2_ROOT_PORT4, M2_ROOT_PORT2, M2_ROOT_PORT3}, {M2_ROOT_PORT4, M2_ROOT_PORT3, M2_ROOT_PORT2, M2_ROOT_PORT1}}; uint8_t mapping_e1s_prsnt[2][6] = { {M2_ROOT_PORT4, M2_ROOT_PORT5, M2_ROOT_PORT3, M2_ROOT_PORT2, M2_ROOT_PORT1, M2_ROOT_PORT0}, {M2_ROOT_PORT1, M2_ROOT_PORT2, M2_ROOT_PORT3, M2_ROOT_PORT4}}; uint8_t mapping_e1s_pwr[2][6] = { {M2_ROOT_PORT3, M2_ROOT_PORT2, M2_ROOT_PORT5, M2_ROOT_PORT4, M2_ROOT_PORT1, M2_ROOT_PORT0}, {M2_ROOT_PORT4, M2_ROOT_PORT3, M2_ROOT_PORT2, M2_ROOT_PORT1} }; static int snr_read_support[4] = {UNKNOWN_CMD, UNKNOWN_CMD, UNKNOWN_CMD, UNKNOWN_CMD}; int bic_get_std_sensor(uint8_t slot_id, uint8_t sensor_num, ipmi_extend_sensor_reading_t *sensor, uint8_t intf) { uint8_t rlen = 0; int ret = 0; ipmi_sensor_reading_t std_sensor = {0}; ret = bic_ipmb_send(slot_id, NETFN_SENSOR_REQ, CMD_SENSOR_GET_SENSOR_READING, &sensor_num, 1, (uint8_t *)&std_sensor, &rlen, intf); if (ret != 0) { return ret; } sensor->value = std_sensor.value; sensor->flags = std_sensor.flags; sensor->status = std_sensor.status; sensor->ext_status = std_sensor.ext_status; sensor->read_type = STANDARD_CMD; snr_read_support[slot_id-1] = STANDARD_CMD; return ret; } int bic_get_accur_sensor(uint8_t slot_id, uint8_t sensor_num, ipmi_extend_sensor_reading_t *sensor, uint8_t intf) { uint8_t rlen = 0; int ret = 0; uint8_t tbuf[5] = {0x9c, 0x9c, 0x0, sensor_num, SNR_READ_CACHE}; uint8_t rbuf[16] = {0}; ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_ACCURACY_SENSOR_READING, tbuf, 5, rbuf, &rlen, intf); if (ret != 0) { return ret; } memcpy(sensor->iana_id, rbuf, 3); if (rlen == 6) { // Byte 1 - 3 : IANA // Byte 4 - 5 : sensor raw data // Byte 6 : flags memcpy((uint8_t *)&sensor->value, rbuf+3, 2); sensor->flags = rbuf[5]; sensor->read_type = ACCURATE_CMD; snr_read_support[slot_id-1] = ACCURATE_CMD; } else if (rlen == 8) { // Byte 1 - 3 : IANA // Byte 4 - 7 : sensor raw data // Byte 8 : flags memcpy((uint8_t *)&sensor->value, rbuf+3, 4); sensor->flags = rbuf[7]; sensor->read_type = ACCURATE_CMD_4BYTE; snr_read_support[slot_id-1] = ACCURATE_CMD_4BYTE; } else { return BIC_STATUS_NOT_SUPP_IN_CURR_STATE; } return ret; } // S/E - Get Sensor reading // Netfn: 0x04, Cmd: 0x2d // S/E - Get Accuracy Sensor reading // Netfn: 0x38, Cmd: 0x23 int bic_get_sensor_reading(uint8_t slot_id, uint8_t sensor_num, ipmi_extend_sensor_reading_t *sensor, uint8_t intf) { int ret = 0; if (snr_read_support[slot_id-1] == STANDARD_CMD) { ret = bic_get_std_sensor(slot_id, sensor_num, sensor, intf); } else if (snr_read_support[slot_id-1] == ACCURATE_CMD || snr_read_support[slot_id-1] == ACCURATE_CMD_4BYTE) { ret = bic_get_accur_sensor(slot_id, sensor_num, sensor, intf); } else { // first read, try accurate command if (bic_get_accur_sensor(slot_id, sensor_num, sensor, intf) == BIC_STATUS_NOT_SUPP_IN_CURR_STATE) { // not support accurate reading, switch to standard command ret = bic_get_std_sensor(slot_id, sensor_num, sensor, intf); } } return ret; } // APP - Get Device ID // Netfn: 0x06, Cmd: 0x01 int bic_get_dev_id(uint8_t slot_id, ipmi_dev_id_t *dev_id, uint8_t intf) { uint8_t rlen = 0; return bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_GET_DEVICE_ID, NULL, 0, (uint8_t *) dev_id, &rlen, intf); } // APP - Get Device ID // Netfn: 0x06, Cmd: 0x04 int bic_get_self_test_result(uint8_t slot_id, uint8_t *self_test_result, uint8_t intf) { uint8_t rlen = 0; return bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_GET_SELFTEST_RESULTS, NULL, 0, (uint8_t *)self_test_result, &rlen, intf); } // Storage - Get FRUID info // Netfn: 0x0A, Cmd: 0x10 int bic_get_fruid_info(uint8_t slot_id, uint8_t fru_id, ipmi_fruid_info_t *info, uint8_t intf) { uint8_t rlen = 0; uint8_t fruid = 0; return bic_ipmb_send(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_FRUID_INFO, &fruid, 1, (uint8_t *) info, &rlen, intf); } // Storage - Reserve SDR // Netfn: 0x0A, Cmd: 0x22 static int _get_sdr_rsv(uint8_t slot_id, uint8_t *rsv, uint8_t intf) { uint8_t rlen = 0; return bic_ipmb_send(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_RSV_SDR, NULL, 0, (uint8_t *) rsv, &rlen, intf); } // Storage - Get SDR // Netfn: 0x0A, Cmd: 0x23 static int _get_sdr(uint8_t slot_id, ipmi_sel_sdr_req_t *req, ipmi_sel_sdr_res_t *res, uint8_t *rlen, uint8_t intf) { return bic_ipmb_send(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SDR, (uint8_t *)req, sizeof(ipmi_sel_sdr_req_t), (uint8_t*)res, rlen, intf); } int bic_get_sdr(uint8_t slot_id, ipmi_sel_sdr_req_t *req, ipmi_sel_sdr_res_t *res, uint8_t *rlen, uint8_t intf) { int ret; uint8_t tbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t tlen; uint8_t len; ipmi_sel_sdr_res_t *tres; sdr_rec_hdr_t *hdr; tres = (ipmi_sel_sdr_res_t *) tbuf; // Get SDR reservation ID for the given record ret = _get_sdr_rsv(slot_id, rbuf, intf); if (ret) { syslog(LOG_ERR, "%s() _get_sdr_rsv returns %d\n", __func__, ret); return ret; } req->rsv_id = (rbuf[1] << 8) | rbuf[0]; // Initialize the response length to zero *rlen = 0; // Read SDR Record Header req->offset = 0; req->nbytes = sizeof(sdr_rec_hdr_t); ret = _get_sdr(slot_id, req, (ipmi_sel_sdr_res_t *)tbuf, &tlen, intf); if (ret) { syslog(LOG_ERR, "%s() _get_sdr returns %d\n", __func__, ret); return ret; } // Copy the next record id to response res->next_rec_id = tres->next_rec_id; // Copy the header excluding first two bytes(next_rec_id) memcpy(res->data, tres->data, tlen-2); // Update response length and offset for next requesdr_rec_hdr_t *rlen += tlen-2; req->offset = tlen-2; // Find length of data from header info hdr = (sdr_rec_hdr_t *) tres->data; len = hdr->len; // Keep reading chunks of SDR record in a loop while (len > 0) { if (len > SDR_READ_COUNT_MAX) { req->nbytes = SDR_READ_COUNT_MAX; } else { req->nbytes = len; } ret = _get_sdr(slot_id, req, (ipmi_sel_sdr_res_t *)tbuf, &tlen, intf); if (ret) { syslog(LOG_ERR, "%s() _get_sdr returns %d\n", __func__, ret); return ret; } // Copy the data excluding the first two bytes(next_rec_id) memcpy(&res->data[req->offset], tres->data, tlen-2); // Update response length, offset for next request, and remaining length *rlen += tlen-2; req->offset += tlen-2; len -= tlen-2; } return BIC_STATUS_SUCCESS; } // Storage - Get SDR // Netfn: 0x0A, Cmd: 0x11 static int _read_fruid(uint8_t slot_id, uint8_t fru_id, uint32_t offset, uint8_t count, uint8_t *rbuf, uint8_t *rlen, uint8_t intf) { uint8_t tbuf[4] = {0}; uint8_t tlen; tbuf[0] = fru_id; tbuf[1] = offset & 0xFF; tbuf[2] = (offset >> 8) & 0xFF; tbuf[3] = count; tlen = 4; return bic_ipmb_send(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_READ_FRUID_DATA, tbuf, tlen, rbuf, rlen, intf); } int bic_read_fruid(uint8_t slot_id, uint8_t fru_id, const char *path, int *fru_size, uint8_t intf) { int ret = 0; uint32_t nread; uint32_t offset; uint8_t count; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t rlen = 0; int fd; ssize_t bytes_wr; ipmi_fruid_info_t info; // Remove the file if exists already unlink(path); // Open the file exclusively for write fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666); if (fd < 0) { syslog(LOG_ERR, "bic_read_fruid: open fails for path: %s\n", path); goto error_exit; } // Read the FRUID information ret = bic_get_fruid_info(slot_id, fru_id, &info, intf); if (ret) { syslog(LOG_ERR, "bic_read_fruid: bic_read_fruid_info returns %d\n", ret); goto error_exit; } // Indicates the size of the FRUID nread = (info.size_msb << 8) | info.size_lsb; if (nread > FRUID_SIZE) { nread = FRUID_SIZE; } *fru_size = nread; if (*fru_size == 0) goto error_exit; // Read chunks of FRUID binary data in a loop offset = 0; while (nread > 0) { if (nread > FRUID_READ_COUNT_MAX) { count = FRUID_READ_COUNT_MAX; } else { count = nread; } ret = _read_fruid(slot_id, fru_id, offset, count, rbuf, &rlen, intf); if (ret) { syslog(LOG_ERR, "bic_read_fruid: ipmb_wrapper fails\n"); goto error_exit; } // Ignore the first byte as it indicates length of response bytes_wr = write(fd, &rbuf[1], rlen-1); if (bytes_wr != rlen-1) { syslog(LOG_ERR, "bic_read_fruid: write to FRU failed\n"); return -1; } // Update offset offset += (rlen-1); nread -= (rlen-1); } error_exit: if (fd > 0 ) { close(fd); } return ret; } // Storage - Get SDR // Netfn: 0x0A, Cmd: 0x12 static int _write_fruid(uint8_t slot_id, uint8_t fru_id, uint32_t offset, uint8_t count, uint8_t *buf, uint8_t intf) { int ret; uint8_t tbuf[64] = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; tbuf[0] = fru_id; tbuf[1] = offset & 0xFF; tbuf[2] = (offset >> 8) & 0xFF; memcpy(&tbuf[3], buf, count); tlen = count + 3; ret = bic_ipmb_send(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_WRITE_FRUID_DATA, tbuf, tlen, rbuf, &rlen, intf); if ( rbuf[0] != count ) { syslog(LOG_WARNING, "%s() Failed to write fruid data. %d != %d \n", __func__, rbuf[0], count); return -1; } return ret; } int bic_write_fruid(uint8_t slot_id, uint8_t fru_id, const char *path, uint8_t intf) { int ret = -1; int fd, count; uint32_t offset; uint8_t buf[64] = {0}; // Open the file exclusively for read fd = open(path, O_RDONLY, 0666); if (fd < 0) { syslog(LOG_ERR, "%s() Failed to open %s\n", __func__, path); goto error_exit; } // Write chunks of FRUID binary data in a loop offset = 0; while (1) { // Read from file count = read(fd, buf, FRUID_WRITE_COUNT_MAX); if (count <= 0) { break; } // Write to the FRUID ret = _write_fruid(slot_id, fru_id, offset, count, buf, intf); if (ret) { break; } // Update counter offset += count; } error_exit: if (fd > 0 ) { close(fd); } return ret; } // OEM - Get Firmware Version // Netfn: 0x38, Cmd: 0x0B static int _bic_get_fw_ver(uint8_t slot_id, uint8_t fw_comp, uint8_t *ver, uint8_t intf) { uint8_t tbuf[4] = {0x9c, 0x9c, 0x00, fw_comp}; //IANA ID + FW_COMP uint8_t rbuf[16] = {0x00}; uint8_t rlen = 0; int ret = BIC_STATUS_FAILURE; ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_FW_VER, tbuf, 4, rbuf, &rlen, intf); // rlen should be greater than or equal to 4 (IANA + Data1 +...+ DataN) if ( ret < 0 || rlen < 4 ) { syslog(LOG_ERR, "%s: ret: %d, rlen: %d, slot_id:%x, intf:%x\n", __func__, ret, rlen, slot_id, intf); } else { //Ignore IANA ID memcpy(ver, &rbuf[3], rlen-3); ret = BIC_STATUS_SUCCESS; } switch (fw_comp) { case FW_BIC: case FW_1OU_BIC: case FW_2OU_BIC: case FW_BB_BIC: ver[rlen - 2] = '\0'; break; default: // do nothing break; } return ret; } // It's an API and provided to fw-util int bic_get_fw_ver(uint8_t slot_id, uint8_t comp, uint8_t *ver) { uint8_t fw_comp = 0x0; uint8_t intf = 0x0; int ret = BIC_STATUS_FAILURE; //get the component switch (comp) { case FW_1OU_BIC: case FW_2OU_BIC: case FW_BB_BIC: fw_comp = FW_BIC; break; default: fw_comp = comp; break; } //get the intf switch (comp) { case FW_CPLD: case FW_ME: case FW_BIC: intf = NONE_INTF; break; case FW_1OU_BIC: case FW_1OU_CPLD: intf = FEXP_BIC_INTF; break; case FW_2OU_BIC: case FW_2OU_CPLD: case FW_2OU_3V3_VR1: case FW_2OU_3V3_VR2: case FW_2OU_3V3_VR3: case FW_2OU_1V8_VR: case FW_2OU_PESW_VR: case FW_2OU_PESW_CFG_VER: case FW_2OU_PESW_FW_VER: case FW_2OU_PESW_BL0_VER: case FW_2OU_PESW_BL1_VER: case FW_2OU_PESW_PART_MAP0_VER: case FW_2OU_PESW_PART_MAP1_VER: intf = REXP_BIC_INTF; break; case FW_BB_BIC: case FW_BB_CPLD: intf = BB_BIC_INTF; break; } //run cmd switch (comp) { case FW_CPLD: case FW_ME: case FW_BIC: case FW_1OU_BIC: case FW_2OU_BIC: case FW_2OU_3V3_VR1: case FW_2OU_3V3_VR2: case FW_2OU_3V3_VR3: case FW_2OU_1V8_VR: case FW_2OU_PESW_VR: case FW_2OU_PESW_CFG_VER: case FW_2OU_PESW_FW_VER: case FW_2OU_PESW_BL0_VER: case FW_2OU_PESW_BL1_VER: case FW_2OU_PESW_PART_MAP0_VER: case FW_2OU_PESW_PART_MAP1_VER: case FW_BB_BIC: ret = _bic_get_fw_ver(slot_id, fw_comp, ver, intf); break; case FW_1OU_CPLD: ret = bic_get_exp_cpld_ver(slot_id, fw_comp, ver, 0/*bus 0*/, 0x80/*8-bit addr*/, intf); break; case FW_2OU_CPLD: { uint8_t board_type = 0; if ( fby35_common_get_2ou_board_type(slot_id, &board_type) < 0 ) { syslog(LOG_WARNING, "Failed to get 2ou board type\n"); } if ( board_type == GPV3_MCHP_BOARD || board_type == GPV3_BRCM_BOARD ) { ret = _bic_get_fw_ver(slot_id, fw_comp, ver, intf); } else { ret = bic_get_exp_cpld_ver(slot_id, fw_comp, ver, 0/*bus 0*/, 0x80/*8-bit addr*/, intf); } } break; case FW_BB_CPLD: ret = bic_get_cpld_ver(slot_id, fw_comp, ver, 0/*bus 0*/, 0x80/*8-bit addr*/, intf); break; } return ret; } uint8_t get_gpv3_bus_number(uint8_t dev_id) { switch(dev_id) { case FW_2OU_M2_DEV0: case FW_2OU_M2_DEV1: case DEV_ID0_2OU: case DEV_ID1_2OU: return 0x2; case FW_2OU_M2_DEV2: case FW_2OU_M2_DEV3: case DEV_ID2_2OU: case DEV_ID3_2OU: return 0x4; case FW_2OU_M2_DEV4: case FW_2OU_M2_DEV5: case DEV_ID4_2OU: case DEV_ID5_2OU: return 0x6; case FW_2OU_M2_DEV6: case FW_2OU_M2_DEV7: case DEV_ID6_2OU: case DEV_ID7_2OU: return 0x5; case FW_2OU_M2_DEV8: case FW_2OU_M2_DEV9: case DEV_ID8_2OU: case DEV_ID9_2OU: return 0x7; case FW_2OU_M2_DEV10: case FW_2OU_M2_DEV11: case DEV_ID10_2OU: case DEV_ID11_2OU: return 0x3; case DEV_ID12_2OU: case DEV_ID13_2OU: return 0x9; } return 0xff; } uint8_t get_gpv3_channel_number(uint8_t dev_id) { switch(dev_id) { case FW_2OU_M2_DEV0: case FW_2OU_M2_DEV2: case FW_2OU_M2_DEV4: case FW_2OU_M2_DEV6: case FW_2OU_M2_DEV8: case FW_2OU_M2_DEV10: case DEV_ID0_2OU: case DEV_ID2_2OU: case DEV_ID4_2OU: case DEV_ID6_2OU: case DEV_ID8_2OU: case DEV_ID10_2OU: return 0x1; case FW_2OU_M2_DEV1: case FW_2OU_M2_DEV3: case FW_2OU_M2_DEV5: case FW_2OU_M2_DEV7: case FW_2OU_M2_DEV9: case FW_2OU_M2_DEV11: case DEV_ID1_2OU: case DEV_ID3_2OU: case DEV_ID5_2OU: case DEV_ID7_2OU: case DEV_ID9_2OU: case DEV_ID11_2OU: return 0x0; case DEV_ID12_2OU: // E1.S A return 0x4; case DEV_ID13_2OU: // E1.S B return 0x8; } return 0xff; } int bic_enable_ssd_sensor_monitor(uint8_t slot_id, bool enable, uint8_t intf) { uint8_t tbuf[4] = {0x9c, 0x9c, 0x00, ( enable == true )?0x1:0x0}; uint8_t tlen = 4; uint8_t rbuf[16] = {0}; uint8_t rlen = 0; return bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_BIC_SNR_MONITOR, tbuf, tlen, rbuf, &rlen, intf); } int bic_get_1ou_type(uint8_t slot_id, uint8_t *type) { uint8_t tbuf[3] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[16] = {0}; uint8_t rlen = 0; int ret = 0; int retry = 0; char key[MAX_KEY_LEN] = {0}; char tmp_str[MAX_VALUE_LEN] = {0}; int val = 0; snprintf(key, sizeof(key), KV_SLOT_GET_1OU_TYPE, slot_id); while (retry < 3) { ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_BOARD_ID, tbuf, 3, rbuf, &rlen, FEXP_BIC_INTF); if (ret == 0) break; retry++; } if (ret == 0) { *type = rbuf[3]; val = *type; } else { syslog(LOG_WARNING, "[%s] fail at slot%d", __func__, slot_id); val = ret; } snprintf(tmp_str, sizeof(tmp_str), "%d", val); kv_set(key, tmp_str, 0, 0); return ret; } int bic_get_1ou_type_cache(uint8_t slot_id, uint8_t *type) { char key[MAX_KEY_LEN] = {0}; char tmp_str[MAX_VALUE_LEN] = {0}; int val = 0; snprintf(key, sizeof(key), KV_SLOT_GET_1OU_TYPE, slot_id); if (kv_get(key, tmp_str, NULL, 0)) return -1; val = atoi(tmp_str); if (val < 0 || val > 255) return -1; *type = val; return 0; } int bic_set_amber_led(uint8_t slot_id, uint8_t dev_id, uint8_t status) { uint8_t tbuf[5] = {0x9c, 0x9c, 0x00, 0x00, 0x00}; uint8_t rbuf[2] = {0}; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[3] = dev_id; // 0'base tbuf[4] = status; // 0->off, 1->on while (retry < 3) { ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_SET_AMBER_LED, tbuf, 5, rbuf, &rlen, FEXP_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { syslog(LOG_WARNING, "[%s] fail at slot%u dev%u", __func__, slot_id, dev_id); } return ret; } // OEM - Get Post Code buffer // Netfn: 0x38, Cmd: 0x12 int bic_get_80port_record(uint8_t slot_id, uint8_t *rbuf, uint8_t *rlen, uint8_t intf) { int ret = 0; uint8_t tbuf[3] = {0}; uint8_t tlen = sizeof(tbuf); // File the IANA ID memcpy(tbuf, (uint8_t *)&IANA_ID, 3); ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_BUF, tbuf, tlen, rbuf, rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "[%s] Cannot get the postcode buffer from slot%d", __func__, slot_id); } else { *rlen -= 3; memmove(rbuf, &rbuf[3], *rlen); } return ret; } // Custom Command for getting cpld version int bic_get_cpld_ver(uint8_t slot_id, uint8_t comp, uint8_t *ver, uint8_t bus, uint8_t addr, uint8_t intf) { uint8_t tbuf[32] = {0}; uint8_t rbuf[4] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; const uint32_t reg = 0x00200028; //for altera cpld tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x04; //read back 4 bytes tbuf[3] = (reg >> 24) & 0xff; tbuf[4] = (reg >> 16) & 0xff; tbuf[5] = (reg >> 8) & 0xff; tbuf[6] = (reg >> 0) & 0xff; tlen = 7; int ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); for (int i = 0; i < rlen; i++) ver[i] = rbuf[3-i]; return ret; } // Custom Command for getting vr version/device id int bic_get_vr_device_id(uint8_t slot_id, uint8_t *devid, uint8_t *id_len, uint8_t bus, uint8_t addr, uint8_t intf) { uint8_t tbuf[16] = {0}; uint8_t rbuf[16] = {0}; uint8_t tlen = 0; uint8_t rlen = sizeof(rbuf); int ret = 0; tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x07; // read back 7 bytes tbuf[3] = 0xAD; // get device id command tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to get vr device id, ret=%d", __func__, ret); } else { rlen = (*id_len > rbuf[0]) ? rbuf[0] : *id_len; *id_len = rbuf[0]; memmove(devid, &rbuf[1], rlen); } return ret; } int bic_ifx_vr_mfr_fw(uint8_t slot_id, uint8_t bus, uint8_t addr, uint8_t code, uint8_t *data, uint8_t *resp, uint8_t intf) { uint8_t tbuf[16] = {0}; uint8_t rbuf[16] = {0}; uint8_t tlen = 0; uint8_t rlen = sizeof(rbuf); int ret = 0; tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x00; // read count if ( data != NULL ) { tbuf[3] = IFX_MFR_FW_CMD_DATA; tbuf[4] = 4; // block write 4 bytes memcpy(&tbuf[5], data, 4); tlen = 9; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Block write 0x%02X failed", __func__, tbuf[3]); return ret; } } tbuf[3] = IFX_MFR_FW_CMD; tbuf[4] = code; tlen = 5; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Write code 0x%02X failed", __func__, tbuf[4]); return ret; } if ( resp != NULL ) { tbuf[2] = 0x06; // read count tbuf[3] = IFX_MFR_FW_CMD_DATA; tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Block read 0x%02X failed", __func__, tbuf[3]); return ret; } if ( (rlen > 4) && (rbuf[0] == 4) ) { memcpy(resp, rbuf, 5); } else { syslog(LOG_WARNING, "%s() Unexpected data, rlen = %u", __func__, rlen); return -1; } } return ret; } int bic_get_ifx_vr_remaining_writes(uint8_t slot_id, uint8_t bus, uint8_t addr, uint8_t *writes, uint8_t intf) { #define IFX_CONF_SIZE 1344 // Config(604) + PMBus(504) + SVID(156) + PMBusPartial(80) #define REMAINING_TIMES(x) (((x[2] << 8) | x[1]) / IFX_CONF_SIZE) uint8_t tbuf[16] = {0}; uint8_t rbuf[16] = {0}; int ret = 0; ret = bic_ifx_vr_mfr_fw(slot_id, bus, addr, OTP_PTN_RMNG, tbuf, rbuf, intf); if ( ret < 0 ) { return ret; } *writes = REMAINING_TIMES(rbuf); return ret; } int bic_get_isl_vr_remaining_writes(uint8_t slot_id, uint8_t bus, uint8_t addr, uint8_t *writes, uint8_t intf) { uint8_t tbuf[16] = {0}; uint8_t rbuf[16] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = 0; tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x00; //read cnt tbuf[3] = 0xC7; //command code tbuf[4] = 0x35; //data1 tbuf[5] = 0x00; //data2 tlen = 6; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to send command and data...", __func__); goto error_exit; } tbuf[2] = 0x04; //read cnt tbuf[3] = 0xC5; //command code tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to read NVM slots...", __func__); goto error_exit; } *writes = rbuf[0]; error_exit: return ret; } int bic_get_vr_ver(uint8_t slot_id, uint8_t intf, uint8_t bus, uint8_t addr, char *key, char *ver_str) { uint8_t tbuf[32] = {0}; uint8_t rbuf[32] = {0}; uint8_t tlen = 0; uint8_t rlen = sizeof(rbuf); uint8_t remaining_writes = 0; int ret = 0; ret = bic_get_vr_device_id(slot_id, rbuf, &rlen, bus, addr, intf); if ( ret < 0 ) { return ret; } //The length of GET_DEVICE_ID is different. if ( rlen == 2 ) { //Infineon //get the remaining size ret = bic_get_ifx_vr_remaining_writes(slot_id, bus, addr, &remaining_writes, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr remaining writes. ret=%d", __func__, __LINE__, ret); return ret; } memset(tbuf, 0, 4); ret = bic_ifx_vr_mfr_fw(slot_id, bus, addr, GET_CRC, tbuf, rbuf, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr ver. ret=%d", __func__, __LINE__, ret); return ret; } if ( rbuf[0] != 4 ) { syslog(LOG_WARNING, "%s():%d Unexpected data 0x%02X", __func__, __LINE__, rbuf[0]); return -1; } snprintf(ver_str, MAX_VALUE_LEN, "Infineon %02X%02X%02X%02X, Remaining Writes: %d", rbuf[4], rbuf[3], rbuf[2], rbuf[1], remaining_writes); kv_set(key, ver_str, 0, 0); } else if ( rlen > 4 ) { //TI tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x02; //read cnt tbuf[3] = 0xF4; //command code, NVM_CHECKSUM tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr ver. ret=%d", __func__,__LINE__, ret); return ret; } snprintf(ver_str, MAX_VALUE_LEN, "Texas Instruments %02X%02X", rbuf[1], rbuf[0]); kv_set(key, ver_str, 0, 0); } else { //ISL //get the reamaining write ret = bic_get_isl_vr_remaining_writes(slot_id, bus, addr, &remaining_writes, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr remaining writes. ret=%d", __func__,__LINE__, ret); return ret; } //get the CRC32 of the VR tbuf[0] = (bus << 1) + 1; tbuf[1] = addr; tbuf[2] = 0x00; //read cnt tbuf[3] = 0xC7; //command code tbuf[4] = 0x94; //reg tbuf[5] = 0x00; //dummy data tlen = 6; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr ver. ret=%d", __func__,__LINE__, ret); return ret; } tbuf[2] = 0x04; //read cnt tbuf[3] = 0xC5; //command code tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s():%d Failed to send command code to get vr ver. ret=%d", __func__,__LINE__, ret); return ret; } snprintf(ver_str, MAX_VALUE_LEN, "Renesas %02X%02X%02X%02X, Remaining Writes: %d", rbuf[3], rbuf[2], rbuf[1], rbuf[0], remaining_writes); kv_set(key, ver_str, 0, 0); } return ret; } int bic_get_vr_ver_cache(uint8_t slot_id, uint8_t intf, uint8_t bus, uint8_t addr, char *ver_str) { char key[MAX_KEY_LEN], tmp_str[MAX_VALUE_LEN] = {0}; snprintf(key, sizeof(key), "slot%x_vr_%02xh_crc", slot_id, addr); if (kv_get(key, tmp_str, NULL, 0)) { if (bic_get_vr_ver(slot_id, intf, bus, addr, key, tmp_str)) return -1; } if (snprintf(ver_str, MAX_VER_STR_LEN, "%s", tmp_str) > (MAX_VER_STR_LEN-1)) return -1; return 0; } int bic_get_exp_cpld_ver(uint8_t slot_id, uint8_t comp, uint8_t *ver, uint8_t bus, uint8_t addr, uint8_t intf) { uint8_t tbuf[32] = {0}; uint8_t rbuf[4] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = 0; //mux tbuf[0] = (bus << 1) + 1; //bus tbuf[1] = 0xE2; //mux addr tbuf[2] = 0x00; tbuf[3] = 0x02; tlen = 4; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to send the command to switch the mux. ret=%d", __func__, ret); goto error_exit; } //read cpld tbuf[1] = addr; tbuf[2] = 0x04; //read cnt tbuf[3] = 0xC0; //data 1 tbuf[4] = 0x00; //data 2 tbuf[5] = 0x00; //data 3 tbuf[6] = 0x00; //data 4 tlen = 7; ret = bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, ver, &rlen, intf); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to send the command code to get cpld ver. ret=%d", __func__, ret); } error_exit: return ret; } int bic_is_m2_exp_prsnt(uint8_t slot_id) { uint8_t tbuf[4] = {0}; uint8_t rbuf[8] = {0}; uint8_t tlen = 4; uint8_t rlen = 0; int ret, val = 0; int retry = 0; char key[MAX_KEY_LEN] = {0}; char tmp_str[MAX_VALUE_LEN] = {0}; snprintf(key, sizeof(key), KV_SLOT_IS_M2_EXP_PRESENT, slot_id); if (!kv_get(key, tmp_str, NULL, 0)) { val = atoi(tmp_str); return val; } tbuf[0] = 0x03; //bus id tbuf[1] = 0x42; //slave addr tbuf[2] = 0x01; //read 1 byte tbuf[3] = 0x05; //register offset while (retry++ < 3) { ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen); if (!ret) break; } if (ret < 0) { val = -1; } else { val = ((rbuf[0] & 0xC) >> 2) ^ 0x3; // (PRESENT_2OU | PRESENT_1OU) snprintf(tmp_str, sizeof(tmp_str), "%d", val); kv_set(key, tmp_str, 0, 0); } return val; } /* 0x2E 0xDF: Force Intel ME Recovery Request Byte 1:3 = Intel Manufacturer ID - 000157h, LS byte first. Byte 4 - Command = 01h Restart using Recovery Firmware (Intel ME FW configuration is not restored to factory defaults) = 02h Restore Factory Default Variable values and restart the Intel ME FW = 03h PTT Initial State Restore Response Byte 1 - Completion Code = 00h - Success (Remaining standard Completion Codes are shown in Section 2.12) = 81h - Unsupported Command parameter value in the Byte 4 of the request. Byte 2:4 = Intel Manufacturer ID - 000157h, LS byte first. */ int me_recovery(uint8_t slot_id, uint8_t command) { uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = 0; int retry = 0; while (retry <= RETRY_3_TIME) { tbuf[0] = 0xB8; tbuf[1] = 0xDF; tbuf[2] = 0x57; tbuf[3] = 0x01; tbuf[4] = 0x00; tbuf[5] = command; tlen = 6; ret = bic_me_xmit(slot_id, tbuf, tlen, rbuf, &rlen); if (ret) { retry++; sleep(1); continue; } else break; } if (retry > RETRY_3_TIME) { //if the third retry still failed, return -1 syslog(LOG_CRIT, "%s: Restart using Recovery Firmware failed..., retried: %d", __func__, retry); return -1; } sleep(10); retry = 0; memset(&tbuf, 0, 256); memset(&rbuf, 0, 256); /* 0x6 0x4: Get Self-Test Results Byte 1 - Completion Code Byte 2 = 55h - No error. All Self-Tests Passed. = 81h - Firmware entered Recovery bootloader mode Byte 3 For byte 2 = 55h, 56h, FFh: =00h =02h - recovery mode entered by IPMI command "Force ME Recovery" */ //Using ME self-test result to check if the ME Recovery Command Success or not while (retry <= RETRY_3_TIME) { tbuf[0] = 0x18; tbuf[1] = 0x04; tlen = 2; ret = bic_me_xmit(slot_id, tbuf, tlen, rbuf, &rlen); if (ret) { retry++; sleep(1); continue; } //if Get Self-Test Results is 0x55 0x00, means No error. All Self-Tests Passed. //if Get Self-Test Results is 0x81 0x02, means Firmware entered Recovery bootloader mode if ( (command == RECOVERY_MODE) && (rbuf[1] == 0x81) && (rbuf[2] == 0x02) ) { return 0; } else if ( (command == RESTORE_FACTORY_DEFAULT) && (rbuf[1] == 0x55) && (rbuf[2] == 0x00) ) { return 0; } else { return -1; } } if (retry > RETRY_3_TIME) { //if the third retry still failed, return -1 syslog(LOG_CRIT, "%s: Restore Factory Default failed..., retried: %d", __func__, retry); return -1; } return 0; } int me_reset(uint8_t slot_id) { uint8_t tbuf[2] = {0x00}; uint8_t rbuf[2] = {0x00}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = 0; int retry = 0; while (retry <= RETRY_3_TIME) { tbuf[0] = 0x18; tbuf[1] = 0x02; tlen = 2; ret = bic_me_xmit(slot_id, tbuf, tlen, rbuf, &rlen); if (ret) { retry++; sleep(1); continue; } break; } if (retry > RETRY_3_TIME) { //if the third retry still failed, return -1 syslog(LOG_CRIT, "%s: ME Reset failed..., retried: %d", __func__, retry); return -1; } return 0; } int bic_switch_mux_for_bios_spi(uint8_t slot_id, uint8_t mux) { uint8_t tbuf[5] = {0}; uint8_t rbuf[1] = {0}; uint8_t tlen = 5; uint8_t rlen = 0; int ret = 0; tbuf[0] = 0x03; //bus id tbuf[1] = 0x42; //slave addr tbuf[2] = 0x01; //read 1 byte tbuf[3] = 0x00; //register offset tbuf[4] = mux; //mux setting ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen); if ( ret < 0 ) { return -1; } return 0; } int bic_asd_init(uint8_t slot_id, uint8_t cmd) { uint8_t tbuf[4] = {0x00}; uint8_t rbuf[8] = {0x00}; uint8_t tlen = 4; uint8_t rlen = 0; memcpy(tbuf, (uint8_t *)&IANA_ID, 3); tbuf[3] = cmd; return bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_ASD_INIT, tbuf, tlen, rbuf, &rlen); } int bic_get_gpio_config(uint8_t slot_id, uint8_t gpio, uint8_t *data) { uint8_t tbuf[13] = {0x00}; uint8_t rbuf[6] = {0x00}; uint8_t rlen = 0; uint8_t tlen = sizeof(tbuf); uint8_t index = 0; uint8_t pin = 0; int ret = 0; // File the IANA ID memcpy(tbuf, (uint8_t *)&IANA_ID, 3); //get the buffer index index = (gpio / 8) + 3; //3 is the size of IANA ID pin = 1 << (gpio % 8); tbuf[index] = pin; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO_CONFIG, tbuf, tlen, rbuf, &rlen); *data = rbuf[3]; return ret; } int bic_set_gpio_config(uint8_t slot_id, uint8_t gpio, uint8_t data) { uint8_t tbuf[14] = {0x00}; uint8_t rbuf[6] = {0x00}; uint8_t rlen = 0; uint8_t tlen = sizeof(tbuf); uint8_t index = 0; uint8_t pin = 0; int ret = 0; // File the IANA ID memcpy(tbuf, (uint8_t *)&IANA_ID, 3); //get the buffer index index = (gpio / 8) + 3; //3 is the size of IANA ID pin = 1 << (gpio % 8); tbuf[index] = pin; tbuf[13] = data & 0x1f; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO_CONFIG, tbuf, tlen, rbuf, &rlen); return ret; } int bic_set_gpio(uint8_t slot_id, uint8_t gpio_num, uint8_t value) { uint8_t tbuf[6] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[1] = {0}; uint8_t tlen = 6; uint8_t rlen = 0; int ret = 0; tbuf[3] = 0x01; tbuf[4] = gpio_num; tbuf[5] = value; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_SET_GPIO, tbuf, tlen, rbuf, &rlen); if ( ret < 0 ) { return -1; } return 0; } int remote_bic_set_gpio(uint8_t slot_id, uint8_t gpio_num, uint8_t value, uint8_t intf) { uint8_t tbuf[6] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[1] = {0}; uint8_t tlen = 6; uint8_t rlen = 0; int ret = 0; tbuf[3] = 0x01; tbuf[4] = gpio_num; tbuf[5] = value; ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_SET_GPIO, tbuf, tlen, rbuf, &rlen, intf); if ( ret < 0 ) { return -1; } return 0; } // Get all GPIO pin status int bic_get_gpio(uint8_t slot_id, bic_gpio_t *gpio, uint8_t intf) { uint8_t tbuf[4] = {0x9c, 0x9c, 0x00}; // IANA ID uint8_t rbuf[13] = {0x00}; uint8_t rlen = 0; int ret; memset(gpio, 0, sizeof(*gpio)); ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO, tbuf, 3, rbuf, &rlen, intf); if (ret != 0 || rlen < 3) return -1; rlen -= 3; if (rlen > sizeof(bic_gpio_t)) rlen = sizeof(bic_gpio_t); // Ignore first 3 bytes of IANA ID memcpy((uint8_t*) gpio, &rbuf[3], rlen); return ret; } // Get an GPIO pin status int bic_get_one_gpio_status(uint8_t slot_id, uint8_t gpio_num, uint8_t *value){ uint8_t tbuf[5] = {0x00}; uint8_t rbuf[5] = {0x00}; uint8_t tlen = 5; uint8_t rlen = 0; int ret = 0; // File the IANA ID memcpy(tbuf, (uint8_t *)&IANA_ID, 3); tbuf[3] = 0x00; tbuf[4] = gpio_num; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_SET_GPIO, tbuf, tlen, rbuf, &rlen); *value = rbuf[4] & 0x01; return ret; } int bic_get_sys_guid(uint8_t slot_id, uint8_t *guid) { int ret; uint8_t rlen = 0; ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_GET_SYSTEM_GUID, NULL, 0, guid, &rlen); if (rlen != SIZE_SYS_GUID) { #ifdef DEBUG syslog(LOG_ERR, "bic_get_sys_guid: returned rlen of %d\n"); #endif return -1; } return ret; } int bic_set_sys_guid(uint8_t slot_id, uint8_t *guid) { int ret; uint8_t rlen = 0; uint8_t rbuf[MAX_IPMB_RES_LEN]={0x00}; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_REQ, CMD_OEM_SET_SYSTEM_GUID, guid, SIZE_SYS_GUID, rbuf, &rlen); return ret; } int bic_do_sled_cycle(uint8_t slot_id) { uint8_t tbuf[5] = {0}; uint8_t tlen = 5; tbuf[0] = 0x03; //bus id tbuf[1] = 0x1E; //slave addr tbuf[2] = 0x00; //read 0 byte tbuf[3] = 0x2B; //register offset tbuf[4] = 0x01; //excecute sled cycle tlen = 5; return bic_ipmb_send(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, NULL, 0, BB_BIC_INTF); } // Only For Class 2 int bic_set_fan_auto_mode(uint8_t crtl, uint8_t *status) { uint8_t tbuf[1] = {0}; uint8_t rbuf[1] = {0}; uint8_t tlen = 1; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[0] = crtl; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_REQ, BIC_CMD_OEM_FAN_CTRL_STAT, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } *status = rbuf[0]; return 0; } // Only For Class 2 int bic_set_fan_speed(uint8_t fan_id, uint8_t pwm) { uint8_t tbuf[5] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[1] = {0}; uint8_t tlen = 5; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[3] = fan_id; tbuf[4] = pwm; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, BIC_CMD_OEM_BMC_FAN_CTRL, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if ( ret == 0 ) break; retry++; } if (ret != 0) { return -1; } return 0; } // Only For Class 2 int bic_manual_set_fan_speed(uint8_t fan_id, uint8_t pwm) { uint8_t tbuf[2] = {0}; uint8_t rbuf[1] = {0}; uint8_t tlen = 2; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[0] = fan_id; tbuf[1] = pwm; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_REQ, BIC_CMD_OEM_SET_FAN_DUTY, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } return 0; } // Only For Class 2 int bic_get_fan_speed(uint8_t fan_id, float *value) { uint8_t tbuf[4] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[5] = {0}; uint8_t tlen = 4; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[3] = fan_id; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_FAN_RPM, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } *value = (float)((rbuf[3] << 8)+ rbuf[4]); return 0; } // Only For Class 2 int bic_get_fan_pwm(uint8_t fan_id, float *value) { uint8_t tbuf[4] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[4] = {0}; uint8_t tlen = 4; uint8_t rlen = 0; int ret = 0; int retry = 0; tbuf[3] = fan_id; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_FAN_DUTY, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } *value = (float)rbuf[3]; return 0; } // Only For Class 2 int bic_get_mb_prsnt(uint8_t slot, uint8_t *prsnt) { uint8_t rlen = 0; uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; memcpy(tbuf, (uint8_t *)&IANA_ID, IANA_ID_SIZE); tbuf[3] = 0x0; // get gpio tbuf[4] = (slot == FRU_SLOT1) ? BB_BIC_SLOT1_PRSNT_PIN : BB_BIC_SLOT3_PRSNT_PIN; if (bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, BIC_CMD_OEM_GET_SET_GPIO, tbuf, 5, rbuf, &rlen, BB_BIC_INTF) < 0) { syslog(LOG_WARNING, "%s(): fail to get MB present status", __func__); return -1; } *prsnt = rbuf[4]; return 0; } // Only For Class 2 int bic_notify_fan_mode(int mode) { uint8_t tlen = 0; uint8_t rlen = 0; uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t slot1_prsnt = 0, slot3_prsnt = 0; char iana_id[IANA_LEN] = {0x9c, 0x9c, 0x0}; BYPASS_MSG req; IPMI_SEL_MSG sel; GET_MB_INDEX_RESP resp; FAN_SERVICE_EVENT fan_event; bic_get_mb_prsnt(FRU_SLOT1, &slot1_prsnt); bic_get_mb_prsnt(FRU_SLOT3, &slot3_prsnt); //when one of slot not present, no need to notify another BMC if ((slot1_prsnt | slot3_prsnt) == NOT_PRESENT) { return 0; } memset(&req, 0, sizeof(req)); memset(&sel, 0, sizeof(sel)); memset(&fan_event, 0, sizeof(fan_event)); memset(tbuf, 0, sizeof(tbuf)); memset(rbuf, 0, sizeof(rbuf)); sel.netfn = NETFN_STORAGE_REQ; sel.cmd = CMD_STORAGE_ADD_SEL; sel.record_type = SEL_SYS_EVENT_RECORD; sel.slave_addr = BRIDGE_SLAVE_ADDR << 1; // 8 bit sel.rev = SEL_IPMI_V2_REV; sel.snr_type = SEL_SNR_TYPE_FAN; sel.snr_num = BIC_SENSOR_SYSTEM_STATUS; sel.event_dir_type = SEL_ASSERT; fan_event.type = SYS_FAN_EVENT; if (bic_ipmb_send(FRU_SLOT1, NETFN_OEM_REQ, BIC_CMD_OEM_GET_MB_INDEX, tbuf, 0, (uint8_t*) &resp, &rlen, BB_BIC_INTF) < 0) { syslog(LOG_WARNING, "%s(): fail to get MB index", __func__); return -1; } if (rlen == sizeof(GET_MB_INDEX_RESP)) { fan_event.slot = resp.index; } else { fan_event.slot = UNKNOWN_SLOT; syslog(LOG_WARNING, "%s(): wrong response while getting MB index", __func__); } memcpy(req.iana_id, iana_id, MIN(sizeof(req.iana_id), sizeof(iana_id))); req.bypass_intf = BMC_INTF; fan_event.mode = mode; memcpy(sel.event_data, (uint8_t*) &fan_event, MIN(sizeof(sel.event_data), sizeof(fan_event))); memcpy(req.bypass_data, (uint8_t*) &sel, MIN(sizeof(req.bypass_data), sizeof(sel))); tlen = sizeof(iana_id) + 1 + sizeof(sel); // IANA ID + interface + add SEL command if (bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, (uint8_t*) &req, tlen, rbuf, &rlen, BB_BIC_INTF) < 0) { syslog(LOG_WARNING, "%s(): fail to notify another BMC fan mode changed", __func__); return -1; } return 0; } int bic_get_dev_info(uint8_t slot_id, uint8_t dev_id, uint8_t *nvme_ready, uint8_t *status, uint8_t *type) { int ret = 0; uint8_t retry = MAX_READ_RETRY; uint16_t vendor_id = 0, reversed_vender_sph = 0; uint8_t ffi = 0 ,meff = 0 ,major_ver = 0, minor_ver = 0; uint8_t type_2ou = UNKNOWN_BOARD; ret = bic_is_m2_exp_prsnt(slot_id); if ((ret < 0) || ((ret & PRESENT_2OU) != PRESENT_2OU)) { syslog(LOG_WARNING,"%s() Cannot get 2ou board", __func__); *type = DEV_TYPE_UNKNOWN; return -1; } ret = fby35_common_get_2ou_board_type(slot_id, &type_2ou); if ( ret < 0 ) { syslog(LOG_WARNING,"%s() Cannot get 2ou board type", __func__); *type = DEV_TYPE_UNKNOWN; return -1; } if ( type_2ou == GPV3_MCHP_BOARD || type_2ou == GPV3_BRCM_BOARD ) { while (retry) { ret = bic_get_dev_power_status(slot_id, dev_id, nvme_ready, status, &ffi, &meff, &vendor_id, &major_ver,&minor_ver, REXP_BIC_INTF); if (!ret) break; msleep(50); retry--; } //syslog(LOG_WARNING, "bic_get_dev_power_status: dev_id %d nvme_ready %d status %d",dev_id, *nvme_ready, *status); reversed_vender_sph = (((VENDOR_SPH << 8) & 0xFF00) | ((VENDOR_SPH >> 8) & 0x00FF)); if (*nvme_ready) { if ( meff == MEFF_DUAL_M2 ) { *type = DEV_TYPE_DUAL_M2; } else{ if (ffi == FFI_ACCELERATOR) { if (vendor_id == VENDOR_BRCM) { *type = DEV_TYPE_BRCM_ACC; } else if (vendor_id == VENDOR_SPH || vendor_id == reversed_vender_sph) { *type = DEV_TYPE_SPH_ACC; } else { *type = DEV_TYPE_M2; } } else { *type = DEV_TYPE_SSD; } } } else { *type = DEV_TYPE_UNKNOWN; } return 0; } *type = DEV_TYPE_UNKNOWN; return -1; } int bic_get_dev_power_status(uint8_t slot_id, uint8_t dev_id, uint8_t *nvme_ready, uint8_t *status, \ uint8_t *ffi, uint8_t *meff, uint16_t *vendor_id, uint8_t *major_ver, uint8_t *minor_ver, uint8_t intf) { uint8_t tbuf[5] = {0x9c, 0x9c, 0x00}; // IANA ID uint8_t rbuf[11] = {0x00}; uint8_t tlen = 5; uint8_t rlen = 0; int ret = 0; uint8_t table = 0, board_type = 0; if (intf == FEXP_BIC_INTF) { table = 1; ret = bic_get_1ou_type(slot_id, &board_type); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 1ou board_type", __func__); board_type = M2_BOARD; } } else if (intf == REXP_BIC_INTF) { table = 0; ret = fby35_common_get_2ou_board_type(slot_id, &board_type); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 2ou board_type", __func__); board_type = M2_BOARD; } } else { return -1; } //Send the command memcpy(tbuf, (uint8_t *)&IANA_ID, 3); if (board_type == EDSFF_1U) { // case 1OU E1S tbuf[3] = mapping_e1s_pwr[table][dev_id - 1]; } else if (board_type == E1S_BOARD) { // case 2OU E1S tbuf[3] = mapping_e1s_pwr[table][dev_id - 1] + 1; // device ID 1 based in power control } else { // case 1/2OU M.2 tbuf[3] = dev_id; } tbuf[4] = 0x3; //get power status ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DEV_POWER, tbuf, tlen, rbuf, &rlen, intf); // Ignore first 3 bytes of IANA ID *status = rbuf[3]; *nvme_ready = (intf == REXP_BIC_INTF)?rbuf[4]:0x1; //1 is assigned directly. for REXP_EXP_INTF, we assige it from rbuf[4] if ( ffi != NULL ) *ffi = rbuf[5]; // FFI_0 0:Storage 1:Accelerator if ( meff != NULL ) *meff = rbuf[6]; // MEFF 0x35: M.2 22110 0xF0: Dual M.2 if ( vendor_id != NULL ) *vendor_id = (rbuf[7] << 8 ) | rbuf[8]; // PCIe Vendor ID if ( major_ver != NULL ) *major_ver = rbuf[9]; //FW version Major Revision if ( minor_ver != NULL ) *minor_ver = rbuf[10]; //FW version Minor Revision return ret; } int bic_set_dev_power_status(uint8_t slot_id, uint8_t dev_id, uint8_t status, uint8_t intf) { uint8_t tbuf[5] = {0x00}; uint8_t rbuf[10] = {0x00}; uint8_t tlen = 0; uint8_t rlen = 0; uint8_t bus_num = 0; uint8_t table = 0, board_type = 0; uint8_t prsnt_bit = 0; int fd = 0; int ret = 0; do { if (intf == FEXP_BIC_INTF) { table = 1; ret = bic_get_1ou_type(slot_id, &board_type); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 1ou board_type", __func__); board_type = M2_BOARD; } } else if (intf == REXP_BIC_INTF) { table = 0; ret = fby35_common_get_2ou_board_type(slot_id, &board_type); if (ret < 0) { syslog(LOG_ERR, "%s() Cannot get 2ou board_type", __func__); board_type = M2_BOARD; } // No VPP and hotplug on GPv3, skip it if ( board_type == GPV3_MCHP_BOARD || board_type == GPV3_BRCM_BOARD ) break; } else { return -1; } bus_num = fby35_common_get_bus_id(slot_id) + 4; //set the present status of M.2 fd = i2c_open(bus_num, SB_CPLD_ADDR); if ( fd < 0 ) { printf("Cannot open /dev/i2c-%d\n", bus_num); ret = BIC_STATUS_FAILURE; goto error_exit; } tbuf[0] = (intf == REXP_BIC_INTF )?M2_REG_2OU:M2_REG_1OU; tlen = 1; rlen = 1; ret = i2c_rdwr_msg_transfer(fd, SB_CPLD_ADDR << 1, tbuf, tlen, rbuf, rlen); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d, ret", __func__, tlen); goto error_exit; } if (board_type == EDSFF_1U || board_type == E1S_BOARD) { // case 1/2OU E1S prsnt_bit = mapping_e1s_prsnt[table][dev_id - 1]; } else { // case 1/2OU M.2 prsnt_bit = mapping_m2_prsnt[table][dev_id - 1]; } if ( status == M2_PWR_OFF ) { tbuf[1] = (rbuf[0] | (0x1 << (prsnt_bit))); } else { tbuf[1] = (rbuf[0] & ~(0x1 << (prsnt_bit))); } tlen = 2; rlen = 0; ret = i2c_rdwr_msg_transfer(fd, SB_CPLD_ADDR << 1, tbuf, tlen, rbuf, rlen); if ( ret < 0 ) { syslog(LOG_WARNING, "%s() Failed to do i2c_rdwr_msg_transfer, tlen=%d", __func__, tlen); goto error_exit; } } while(0); //Send the command memcpy(tbuf, (uint8_t *)&IANA_ID, 3); if (board_type == EDSFF_1U) { // case 1OU E1S tbuf[3] = mapping_e1s_pwr[table][dev_id - 1]; } else if (board_type == E1S_BOARD) { // case 2OU E1S tbuf[3] = mapping_e1s_pwr[table][dev_id - 1] + 1; // device ID 1 based in power control } else { // case 1/2OU M.2 tbuf[3] = dev_id; } tbuf[4] = status; //set power status tlen = 5; ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DEV_POWER, tbuf, tlen, rbuf, &rlen, intf); error_exit: if ( fd > 0 ) close(fd); return ret; } int bic_disable_sensor_monitor(uint8_t slot_id, uint8_t dis, uint8_t intf) { uint8_t tbuf[8] = {0x9c, 0x9c, 0x00}; // IANA ID uint8_t rbuf[8] = {0x00}; uint8_t rlen = sizeof(rbuf); tbuf[3] = dis; // 1: disable sensor monitor; 0: enable sensor monitor return bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DISABLE_SEN_MON, tbuf, 4, rbuf, &rlen, intf); } int bic_master_write_read(uint8_t slot_id, uint8_t bus, uint8_t addr, uint8_t *wbuf, uint8_t wcnt, uint8_t *rbuf, uint8_t rcnt) { uint8_t tbuf[256]; uint8_t tlen = 3, rlen = 0; int ret; tbuf[0] = bus; tbuf[1] = addr; tbuf[2] = rcnt; if (wcnt) { memcpy(&tbuf[3], wbuf, wcnt); tlen += wcnt; } ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_MASTER_WRITE_READ, tbuf, tlen, rbuf, &rlen); return ret; } int bic_reset(uint8_t slot_id) { uint8_t tbuf[3] = {0x9c, 0x9c, 0x00}; // IANA ID uint8_t rbuf[8] = {0x00}; uint8_t rlen = 0; int ret; ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_COLD_RESET, tbuf, 0, rbuf, &rlen); return ret; } // Only For Class 2 int bic_inform_sled_cycle(void) { uint8_t tbuf[3] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[1] = {0x00}; uint8_t tlen = 3; uint8_t rlen = 0; int ret = 0; int retry = 0; while (retry < 3) { ret = bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, BIC_CMD_OEM_INFORM_SLED_CYCLE, tbuf, tlen, rbuf, &rlen, BB_BIC_INTF); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } return 0; } // For Discovery Point, get pcie config int bic_get_dp_pcie_config(uint8_t slot_id, uint8_t *pcie_config) { uint8_t tbuf[3] = {0x9c, 0x9c, 0x00}; uint8_t rbuf[4] = {0x00}; uint8_t tlen = 3; uint8_t rlen = 0; int ret = 0; int retry = 0; while (retry < 3) { ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_PCIE_CONFIG, tbuf, tlen, rbuf, &rlen); if (ret == 0) break; retry++; } if (ret != 0) { return -1; } (*pcie_config) = (rbuf[3] & 0x0f); return 0; } // For class 2 system, BMC need to get MB index from BB BIC int bic_get_mb_index(uint8_t *index) { GET_MB_INDEX_RESP resp = {0}; uint8_t rlen = 0; uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0}; if (index == NULL) { syslog(LOG_WARNING, "%s(): invalid index parameter", __func__); return -1; } memset(tbuf, 0, sizeof(tbuf)); memset(&resp, 0, sizeof(resp)); if (bic_ipmb_send(FRU_SLOT1, NETFN_OEM_REQ, BIC_CMD_OEM_GET_MB_INDEX, tbuf, 0, (uint8_t*) &resp, &rlen, BB_BIC_INTF) < 0) { syslog(LOG_WARNING, "%s(): fail to get MB index", __func__); return -1; } if (rlen == sizeof(GET_MB_INDEX_RESP)) { *index = resp.index; } else { syslog(LOG_WARNING, "%s(): wrong response length (%d), while getting MB index, expected = %d", __func__, rlen, sizeof(GET_MB_INDEX_RESP)); return -1; } return 0; } // For class 2 system, bypass command to another slot BMC int bic_bypass_to_another_bmc(uint8_t* data, uint8_t len) { uint8_t tlen = 0; uint8_t rlen = 0; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t slot1_prsnt = 0, slot3_prsnt = 0; char iana_id[IANA_LEN] = {0x9c, 0x9c, 0x0}; BYPASS_MSG req = {0}; if (data == NULL) { syslog(LOG_WARNING, "%s(): NULL bypass data", __func__); return -1; } bic_get_mb_prsnt(FRU_SLOT1, &slot1_prsnt); bic_get_mb_prsnt(FRU_SLOT3, &slot3_prsnt); //when one of slot not present, no need to notify another BMC if ((slot1_prsnt | slot3_prsnt) == NOT_PRESENT) { return 0; } memset(&req, 0, sizeof(req)); memset(rbuf, 0, sizeof(rbuf)); memcpy(req.iana_id, iana_id, MIN(sizeof(req.iana_id), sizeof(iana_id))); req.bypass_intf = BMC_INTF; memcpy(req.bypass_data, data, MIN(sizeof(req.bypass_data), len)); tlen = sizeof(BYPASS_MSG_HEADER) + len; if (bic_ipmb_send(FRU_SLOT1, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, (uint8_t*) &req, tlen, rbuf, &rlen, BB_BIC_INTF) < 0) { syslog(LOG_WARNING, "%s(): fail to bypass command to another BMC", __func__); return -1; } return 0; } // For class 2 system, notify another BMC the BB fw is updating int bic_set_bb_fw_update_ongoing(uint8_t component, uint8_t option) { IPMI_SEL_MSG sel = {0}; BB_FW_UPDATE_EVENT update_event = {0}; int ret = 0; memset(&sel, 0, sizeof(sel)); memset(&update_event, 0, sizeof(update_event)); sel.netfn = NETFN_STORAGE_REQ; sel.cmd = CMD_STORAGE_ADD_SEL; sel.record_type = SEL_SYS_EVENT_RECORD; sel.slave_addr = BRIDGE_SLAVE_ADDR << 1; // 8 bit sel.rev = SEL_IPMI_V2_REV; sel.snr_type = SEL_SNR_TYPE_FW_STAT; sel.snr_num = BIC_SENSOR_SYSTEM_STATUS; sel.event_dir_type = option; update_event.type = SYS_BB_FW_UPDATE; update_event.component = component; memcpy(sel.event_data, (uint8_t*) &update_event, MIN(sizeof(sel.event_data), sizeof(update_event))); ret = bic_bypass_to_another_bmc((uint8_t*)&sel, sizeof(sel)); if (ret < 0) { syslog(LOG_WARNING, "%s(): failed to set update flag to another bmc", __func__); } return ret; } // For class 2 system, check BB fw update status to avoid updating at the same time int bic_check_bb_fw_update_ongoing() { uint8_t mb_index = 0; int ret = 0; char update_stat[MAX_VALUE_LEN] = {0}; // if key exist, BB fw is updating by another slot if (access(BB_FW_UPDATE_STAT_FILE, F_OK) == 0) { if (kv_get("bb_fw_update", update_stat, NULL, 0) != 0) { printf("Fail to get BB firmware update status\n"); strncpy(update_stat, "unknown", sizeof(update_stat)); } printf("BB firmware: %s update is ongoing\n", update_stat); return -1; } //to avoid the case that two BMCs run the update command at the same time. //delay the checking time according to MB index ret = bic_get_mb_index(&mb_index); if (ret < 0) { printf("Fail to get MB index\n"); return -1; } sleep(mb_index); if (access(BB_FW_UPDATE_STAT_FILE, F_OK) == 0) { if (kv_get("bb_fw_update", update_stat, NULL, 0) != 0) { printf("Fail to get BB firmware update status\n"); strncpy(update_stat, "unknown", sizeof(update_stat)); } printf("BB firmware: %s update is ongoing\n", update_stat); return -1; } return 0; }