meta-facebook/meta-fby2/recipes-fby2/plat-libs/files/bic/bic.c (3,280 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 <stdbool.h>
#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 <time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bic.h"
#include <openbmc/kv.h>
#include <openbmc/obmc-i2c.h>
#include <openbmc/misc-utils.h>
#include <openbmc/libgpio.h>
#include <openssl/sha.h>
#define FRUID_READ_COUNT_MAX 0x20
#define FRUID_WRITE_COUNT_MAX 0x20
#define FRUID_SIZE 256
#define IPMB_READ_COUNT_MAX 224
#define IPMB_WRITE_COUNT_MAX 224
#define IPMB_BIOS_WRITE_COUNT_MAX 2048 // 2065 - 17
#define NUM_ATTEMPTS 5
#define BIOS_ERASE_PKT_SIZE (64*1024)
#define BIOS_VERIFY_PKT_SIZE (32*1024)
#define BIOS_UPDATE_BLK_SIZE (64*1024)
#define SIMPLE_DIGEST_LENGTH 4
#define STRONG_DIGEST_LENGTH SHA256_DIGEST_LENGTH
#define SDR_READ_COUNT_MAX 0x1A
#define SIZE_SYS_GUID 16
#define SIZE_IANA_ID 3
#define BIOS_VER_REGION_SIZE (4*1024*1024)
#define BIOS_VER_STR "F09_"
#define Y2_BIOS_VER_STR "YMV2"
#define GPV2_BIOS_VER_STR "F09B"
#define YV2_MERGE_BIOS_VER_STR "F09M"
#define ND_BIOS_VER_STR "F09C"
#define BIC_SIGN_SIZE 32
#define BIC_UPDATE_RETRIES 12
#define PING_IPMB_RETRIES 5
#define BIC_UPDATE_TIMEOUT 500
#define GPV2_BIC_PLAT_STR "F09B"
#define ND_BIC_PLAT_STR "ND"
#define BIC_FLASH_START 0x8000
#define BIC_PKT_MAX 252
#define BIC_CMD_DOWNLOAD 0x21
#define BIC_CMD_RUN 0x22
#define BIC_CMD_STATUS 0x23
#define BIC_CMD_DATA 0x24
#define CMD_DOWNLOAD_SIZE 11
#define CMD_RUN_SIZE 7
#define CMD_STATUS_SIZE 3
#define CMD_DATA_SIZE 0xFF
#define IMC_VER_SIZE 8
#define SLOT_FILE "/tmp/slot%d.bin"
#define SLOT_RECORD_FILE "/tmp/slot%d.rc"
#define SERVER_TYPE_FILE "/tmp/server_type%d.bin"
#define MAX_FW_PCIE_SWITCH_BLOCK_SIZE 1008
#define LAST_FW_PCIE_SWITCH_TRUNK_SIZE (1008%224)
#define MAX_POST_CODE_PAGE 17
#define BRCM_UPDATE_STATUS 128
#define BRCM_WRITE_CMD 130
#define BRCM_READ_CMD 131
#define BRCM_UPDATE_RTRIES 3
#define VSI_UPDATE_STATUS 133
#define VSI_FW_TYPE 135
#define VSI_WRITE_CMD 136
#define PCIE_PHY_FW 0x00
#define BOOT_ROM_PATCH_FW 0x01
#define UNKOWN_FW -1
#define PCIE_SW_MAX_RETRY 50
#define PCIE_LINK_CHECK_MAX_RETRY 3
#define DEVICE_MUX_ADDR 0xE2
#define MAX_GPV2_DRIVE_NUM 12
#define GPIO_FALSE_DIR "/tmp/gpio/bic%d_%llu"
#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)
typedef struct {
uint8_t ver[2];
uint8_t iaan[3];
uint8_t prj_name[11];
} __attribute__ ((__packed__)) vr_header;
static char* gpio_bic_ready[] = { 0, "I2C_SLOT1_ALERT_N", "I2C_SLOT2_ALERT_N", "I2C_SLOT3_ALERT_N", "I2C_SLOT4_ALERT_N" };
static char* gpio_12v[] = { 0, "P12V_STBY_SLOT1_EN", "P12V_STBY_SLOT2_EN", "P12V_STBY_SLOT3_EN", "P12V_STBY_SLOT4_EN" };
static char* gpio_power_en[] = { 0, "SLOT1_POWER_EN", "SLOT2_POWER_EN", "SLOT3_POWER_EN", "SLOT4_POWER_EN" };
// Helper Functions
static void
msleep(int msec) {
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = msec * 1000 * 1000;
while(nanosleep(&req, &req) == -1 && errno == EINTR) {
continue;
}
}
static int
i2c_open(uint8_t bus_id) {
int fd;
char fn[32];
int rc;
snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus_id);
fd = open(fn, O_RDWR);
if (fd == -1) {
syslog(LOG_ERR, "Failed to open i2c device %s", fn);
return -1;
}
rc = ioctl(fd, I2C_SLAVE, BRIDGE_SLAVE_ADDR);
if (rc < 0) {
syslog(LOG_ERR, "Failed to open slave @ address 0x%x", BRIDGE_SLAVE_ADDR);
close(fd);
}
return fd;
}
static int
i2c_io(int fd, uint8_t *tbuf, uint8_t tcount, uint8_t *rbuf, uint8_t rcount) {
struct i2c_rdwr_ioctl_data data;
struct i2c_msg msg[2];
int n_msg = 0;
int rc;
memset(&msg, 0, sizeof(msg));
if (tcount) {
msg[n_msg].addr = BRIDGE_SLAVE_ADDR;
msg[n_msg].flags = 0;
msg[n_msg].len = tcount;
msg[n_msg].buf = tbuf;
n_msg++;
}
if (rcount) {
msg[n_msg].addr = BRIDGE_SLAVE_ADDR;
msg[n_msg].flags = I2C_M_RD;
msg[n_msg].len = rcount;
msg[n_msg].buf = rbuf;
n_msg++;
}
data.msgs = msg;
data.nmsgs = n_msg;
rc = ioctl(fd, I2C_RDWR, &data);
if (rc < 0) {
syslog(LOG_ERR, "Failed to do raw io");
return -1;
}
return 0;
}
static 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
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;
}
}
uint8_t
is_bic_ready(uint8_t slot_id) {
gpio_value_t val;
if (slot_id < 1 || slot_id > 4) {
return 0;
}
if (fby2_common_get_gpio_val(gpio_bic_ready[slot_id], &val) != 0) {
return 0;
}
if (val == GPIO_VALUE_LOW) {
return 1;
} else {
return 0;
}
}
int
bic_is_slot_12v_on(uint8_t slot_id) {
gpio_value_t val;
if (slot_id < 1 || slot_id > 4) {
return 0;
}
if (fby2_common_get_gpio_val(gpio_12v[slot_id], &val) != 0) {
return 0;
}
if (val == GPIO_VALUE_LOW) {
return 0;
} else {
return 1;
}
}
int bic_set_slot_12v(uint8_t slot_id, uint8_t status)
{
gpio_value_t val = status ? GPIO_VALUE_HIGH: GPIO_VALUE_LOW;
if (slot_id < 1 || slot_id > 4) {
return -1;
}
if (fby2_common_set_gpio_val(gpio_12v[slot_id], val) != 0) {
return -1;
}
return 0;
}
int
bic_is_slot_power_en(uint8_t slot_id) {
gpio_value_t val;
if (slot_id < 1 || slot_id > 4) {
return 0;
}
if (fby2_common_get_gpio_val(gpio_power_en[slot_id], &val) != 0) {
return 0;
}
if (val == GPIO_VALUE_LOW) {
return 0;
} else {
return 1;
}
}
// Common IPMB Wrapper function
static int
get_ipmb_bus_id(uint8_t slot_id) {
int bus_id;
switch(slot_id) {
case FRU_SLOT1:
bus_id = IPMB_BUS_SLOT1;
break;
case FRU_SLOT2:
bus_id = IPMB_BUS_SLOT2;
break;
case FRU_SLOT3:
bus_id = IPMB_BUS_SLOT3;
break;
case FRU_SLOT4:
bus_id = IPMB_BUS_SLOT4;
break;
default:
bus_id = -1;
break;
}
return bus_id;
}
int
bic_ipmb_wrapper(uint8_t slot_id, uint8_t netfn, uint8_t cmd,
uint8_t *txbuf, uint16_t txlen,
uint8_t *rxbuf, uint8_t *rxlen) {
return bic_ipmb_limit_rlen_wrapper(slot_id,netfn,cmd,txbuf,txlen,rxbuf,rxlen, 255 - IPMB_HDR_SIZE - IPMI_RESP_HDR_SIZE);
}
int
bic_ipmb_limit_rlen_wrapper(uint8_t slot_id, uint8_t netfn, uint8_t cmd,
uint8_t *txbuf, uint16_t txlen,
uint8_t *rxbuf, uint8_t *rxlen, uint8_t max_rlen) {
ipmb_req_t *req;
ipmb_res_t *res;
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0};
uint16_t tlen = 0;
uint8_t rlen = 0;
int i = 0;
int ret;
uint8_t bus_id;
uint8_t dataCksum;
int retry = 0;
if (!is_bic_ready(slot_id)) {
return -1;
}
ret = get_ipmb_bus_id(slot_id);
if (ret < 0) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_ipmb_wrapper: Wrong Slot ID %d\n", slot_id);
#endif
return ret;
}
bus_id = (uint8_t) ret;
req = (ipmb_req_t*)tbuf;
req->res_slave_addr = BRIDGE_SLAVE_ADDR << 1;
req->netfn_lun = netfn << LUN_OFFSET;
req->hdr_cksum = req->res_slave_addr +
req->netfn_lun;
req->hdr_cksum = ZERO_CKSUM_CONST - req->hdr_cksum;
req->req_slave_addr = BMC_SLAVE_ADDR << 1;
req->seq_lun = 0x00;
req->cmd = cmd;
//copy the data to be send
if (txlen) {
memcpy(req->data, txbuf, txlen);
}
tlen = IPMB_HDR_SIZE + IPMI_REQ_HDR_SIZE + txlen;
while(retry < 3) {
// Invoke IPMB library handler
int ret = lib_ipmb_handle(bus_id, tbuf, tlen, rbuf, &rlen);
if (ret != 0) {
syslog(LOG_ERR, "%s: Failed to send.\n", __func__);
}
if (rlen == 0) {
if (!is_bic_ready(slot_id)) {
syslog(LOG_ERR, "%s: BIC is busy.\n", __func__);
break;
}
retry++;
msleep(20);
}
else {
break;
}
}
if (rlen == 0) {
#ifdef DEBUG
syslog(LOG_DEBUG, "bic_ipmb_wrapper: Zero bytes received, retry:%d\n", retry);
#endif
return -1;
}
// Handle IPMB response
res = (ipmb_res_t*) rbuf;
if (res->cc) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_ipmb_wrapper: Completion Code: 0x%X\n", res->cc);
#endif
if (res->cc == CC_BIC_RETRY) { // Completion Code for BIC retry
syslog(LOG_ERR, "bic_ipmb_wrapper: Completion Code: 0x%X for BIC retry\n", res->cc);
return BIC_RETRY_ACTION;
} else if (res->cc == CC_BIC_INVALID_CMD) { // Completion Code for BIC do not support the command yet
syslog(LOG_ERR, "bic_ipmb_wrapper: Completion Code: 0x%X for BIC invalid command\n", res->cc);
return BIC_INVALID_CMD;
}
return -1;
}
// copy the received data back to caller
*rxlen = rlen - IPMB_HDR_SIZE - IPMI_RESP_HDR_SIZE;
if (*rxlen > max_rlen) {
syslog(LOG_ERR, "bic_ipmb_wrapper: return length %d larger then its maximum: %d\n",*rxlen, max_rlen);
return -1;
}
memcpy(rxbuf, res->data, *rxlen);
// Calculate dataCksum
// Note: dataCkSum byte is last byte
dataCksum = 0;
for (i = IPMB_DATA_OFFSET; i < (rlen - 1); i++) {
dataCksum += rbuf[i];
}
dataCksum = ZERO_CKSUM_CONST - dataCksum;
if (dataCksum != rbuf[rlen - 1]) {
syslog(LOG_ERR, "%s: Receive Data cksum does not match (expectative 0x%x, actual 0x%x)", __func__, dataCksum, rbuf[rlen - 1]);
return -1;
}
return 0;
}
int
bic_ipmb_wrapper_with_dev_mux_selection(uint8_t slot_id, int dev_id, uint8_t netfn, uint8_t cmd,
uint8_t *txbuf, uint16_t txlen,
uint8_t *rxbuf, uint8_t *rxlen) {
int ret = 0, s_ret = 0, bus = 0;
uint8_t wbuf[256] = {0x00};
uint8_t rbuf[256] = {0x00};
if (dev_id < 0 || dev_id >= MAX_GPV2_DRIVE_NUM) {
syslog(LOG_ERR, "%s: Invalid dev_id: %d\n", __FUNCTION__, dev_id);
return -1;
}
s_ret = bic_disable_sensor_monitor(slot_id, BIC_SENSOR_MONITOR_DISABLE);
if (s_ret < 0) {
syslog(LOG_ERR, "%s: Fail to stop sensor monitor, error: %d\n", __FUNCTION__, s_ret);
return s_ret;
}
msleep(100);
// select MUX
wbuf[0] = 1 << (dev_id % 2);
bus = *txbuf;
ret = bic_master_write_read(slot_id, bus, DEVICE_MUX_ADDR, wbuf, 1, rbuf, 0);
if (ret < 0) {
syslog(LOG_ERR, "%s(): bic_master_write_read failed, error: %d\n", __FUNCTION__, ret);
goto Err;
}
ret = bic_ipmb_wrapper(slot_id, netfn, cmd, txbuf, txlen, rxbuf, rxlen);
if (ret < 0) {
syslog(LOG_WARNING, "%s: Fail to send IPMB command to slot%d, error: %d", __FUNCTION__, slot_id, ret);
}
Err:
s_ret = bic_disable_sensor_monitor(slot_id, BIC_SENSOR_MONITOR_ENABLE);
if (s_ret < 0) {
syslog(LOG_ERR, "%s: Fail to start sensor monitor, error: %d\n", __FUNCTION__, s_ret);
}
msleep(100);
return ret;
}
// Get Self-Test result
int
bic_get_self_test_result(uint8_t slot_id, uint8_t *self_test_result) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_GET_SELFTEST_RESULTS, NULL, 0, (uint8_t *) self_test_result, &rlen);
return ret;
}
// Get Device ID
int
bic_get_dev_id(uint8_t slot_id, ipmi_dev_id_t *dev_id) {
uint8_t rlen = 0;
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_GET_DEVICE_ID, NULL, 0, (uint8_t *) dev_id, &rlen);
return ret;
}
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 tbuf[5] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[11] = {0x00};
uint8_t rlen = 0;
int ret;
tbuf[3] = 0x3; //get power status
tbuf[4] = dev_id;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DEV_POWER, tbuf, 5, rbuf, &rlen);
// Ignore first 3 bytes of IANA ID
*status = rbuf[3];
*nvme_ready = rbuf[4];
*ffi = rbuf[5]; // FFI_0 0:Storage 1:Accelerator
*meff = rbuf[6]; // MEFF 0x35: M.2 22110 0xF0: Dual M.2
*vendor_id = (rbuf[7] << 8 ) | rbuf[8]; // PCIe Vendor ID
*major_ver = rbuf[9]; //FW version Major Revision
*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 tbuf[5]; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
int ret;
#if defined(CONFIG_FBY2_GPV2)
int spb_type = 0;
spb_type = fby2_common_get_spb_type();
if (spb_type == TYPE_SPB_YV250) {
tbuf[0] = 0x9C; // WiWynn IANA
tbuf[1] = 0x9C;
tbuf[2] = 0x00;
}
else
#endif
{
tbuf[0] = 0x15; // FB IANA
tbuf[1] = 0xA0;
tbuf[2] = 0x00;
}
tbuf[3] = status; //set power status
tbuf[4] = dev_id;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DEV_POWER, tbuf, 5, rbuf, &rlen);
#if defined(CONFIG_FBY2_GPV2)
if ((spb_type == TYPE_SPB_YV250) && (status == 1)) {
// Check link state when power on
bic_check_pcie_link(slot_id);
}
#endif
return ret;
}
// Get GPIO value and configuration
int
bic_get_gpio(uint8_t slot_id, bic_gpio_t *gpio) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[12] = {0x00};
uint8_t rlen = 0;
uint8_t server_type = SERVER_TYPE_NONE;
int ret;
// Ensure the reserved bits are set to 0.
memset(gpio, 0, sizeof(*gpio));
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO, tbuf, 3, rbuf, &rlen);
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);
if (bic_get_server_type(slot_id, &server_type) < 0) {
return -1;
} else if (server_type == SERVER_TYPE_ND) {
// invert gpio pin 21 (ND_FM_BIOS_POST_COMPT_N) for northdome platform
gpio->gpio = gpio->gpio ^ (1 << ND_FM_BIOS_POST_COMPT_N);
}
#ifdef ENABLE_INJECTION
// putting this after the data checksum
uint64_t i, bmask;
int fd, flen;
char fpath[30], fdata;
for (i = 0; i < MAX_GPIO_PINS; i++) {
sprintf(fpath, GPIO_FALSE_DIR, slot_id, i);
if (!access(fpath, F_OK)) {
fd = open(fpath, O_RDONLY);
if (fd == -1) {
syslog(LOG_WARNING, "Could not open bic injection file for gpio %llu", i);
return -1;
}
flen = read(fd, &fdata, 1);
if (flen == 0) {
close(fd);
return -1;
}
int fret = close(fd);
if (fret) {
syslog(LOG_WARNING, "Failed to close bic injection file for gpio %llu", i);
return -1;
}
// First set the bit to 0
bmask = 1 << (i % 64);
gpio->gpio &= ~bmask;
// Now we can change it to whatever we want
gpio->gpio |= (((fdata - 0x30) & 0x1) << (i % 64));
syslog(LOG_INFO, "Injected %d into gpio %llu at slot %d", (fdata - 0x30), i, slot_id);
}
}
#endif // ENABLE_INJECTION
return ret;
}
int
bic_get_gpio_status(uint8_t slot_id, uint8_t pin, uint8_t *status)
{
bic_gpio_t gpio;
if (bic_get_gpio(slot_id, &gpio)) {
return -1;
}
*status = (uint8_t)((gpio.gpio >> pin) & 1);
return 0;
}
int
bic_set_gpio(uint8_t slot_id, uint8_t gpio, uint8_t value) {
uint8_t tbuf[16] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
// Fill the value
if (value) {
memset(&tbuf[9], 0xFF, 6);
} else {
memset(&tbuf[9] , 0x00, 6);
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO, tbuf, 15, rbuf, &rlen);
return ret;
}
int
bic_set_gpio64(uint8_t slot_id, uint8_t gpio, uint8_t value) {
uint8_t tbuf[20] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
tbuf[9] = (pin >> 48) & 0xFF;
tbuf[10] = (pin >> 56) & 0xFF;
// Fill the value
if (value) {
memset(&tbuf[11], 0xFF, 8);
} else {
memset(&tbuf[11], 0x00, 8);
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO, tbuf, 19, rbuf, &rlen);
return ret;
}
int
bic_get_gpio_config(uint8_t slot_id, uint8_t gpio, bic_gpio_config_t *gpio_config) {
uint8_t tbuf[12] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO_CONFIG, tbuf, 9, rbuf, &rlen);
// Ignore IANA ID
*(uint8_t *) gpio_config = rbuf[3];
return ret;
}
int
bic_get_gpio64_config(uint8_t slot_id, uint8_t gpio, bic_gpio_config_t *gpio_config) {
uint8_t tbuf[12] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
tbuf[9] = (pin >> 48) & 0xFF;
tbuf[10] = (pin >> 56) & 0xFF;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO_CONFIG, tbuf, 11, rbuf, &rlen);
// Ignore IANA ID
*(uint8_t *) gpio_config = rbuf[3];
return ret;
}
int
bic_set_gpio_config(uint8_t slot_id, uint8_t gpio, bic_gpio_config_t *gpio_config) {
uint8_t tbuf[12] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
tbuf[9] = (*(uint8_t *) gpio_config) & 0x1F;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO_CONFIG, tbuf, 10, rbuf, &rlen);
return ret;
}
int
bic_set_gpio64_config(uint8_t slot_id, uint8_t gpio, bic_gpio_config_t *gpio_config) {
uint8_t tbuf[12] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint64_t pin;
int ret;
pin = 1LL << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (pin >> 32) & 0xFF;
tbuf[8] = (pin >> 40) & 0xFF;
tbuf[9] = (pin >> 48) & 0xFF;
tbuf[10] = (pin >> 56) & 0xFF;
tbuf[11] = (*(uint8_t *) gpio_config) & 0x1F;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO_CONFIG, tbuf, 12, rbuf, &rlen);
return ret;
}
// Get BIC Configuration
int
bic_get_config(uint8_t slot_id, bic_config_t *cfg) {
uint8_t tbuf[3] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_CONFIG,
tbuf, 0x03, rbuf, &rlen);
// Ignore IANA ID
*(uint8_t *) cfg = rbuf[3];
return ret;
}
// Set BIC Configuration
int
bic_set_config(uint8_t slot_id, bic_config_t *cfg) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rlen = 0;
uint8_t rbuf[4] = {0};
int ret;
tbuf[3] = *(uint8_t *) cfg;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_CONFIG,
tbuf, 0x04, rbuf, &rlen);
return ret;
}
// Read POST Buffer
int
bic_get_post_buf(uint8_t slot_id, uint8_t *buf, uint8_t *len) {
uint8_t tbuf[3] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[255] = {0x00};
uint8_t rlen = 0;
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_BUF, tbuf, 0x03, rbuf, &rlen);
//Ignore IANA ID
memcpy(buf, &rbuf[3], rlen-3);
*len = rlen - 3;
return ret;
}
// Read Firwmare Versions of various components
int
bic_get_fw_ver(uint8_t slot_id, uint8_t comp, uint8_t *ver) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
// Fill the component for which firmware is requested
tbuf[3] = comp;
ret = bic_ipmb_limit_rlen_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_FW_VER, tbuf, 0x04, rbuf, &rlen,16);
// fw version has to be between 1 and 5 bytes based on component
if (ret || (rlen < 1+SIZE_IANA_ID) || (rlen > 5+SIZE_IANA_ID)) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_get_fw_ver: ret: %d, rlen: %d\n", ret, rlen);
#endif
return -1;
}
//Ignore IANA ID
memcpy(ver, &rbuf[3], rlen-3);
return ret;
}
// Read checksum of various components
int
bic_get_fw_cksum(uint8_t slot_id, uint8_t target, uint32_t offset, uint32_t len, uint8_t *ver) {
uint8_t tbuf[12] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
// Fill the component for which firmware is requested
tbuf[3] = target;
// Fill the offset
tbuf[4] = (offset) & 0xFF;
tbuf[5] = (offset >> 8) & 0xFF;
tbuf[6] = (offset >> 16) & 0xFF;
tbuf[7] = (offset >> 24) & 0xFF;
// Fill the length
tbuf[8] = (len) & 0xFF;
tbuf[9] = (len >> 8) & 0xFF;
tbuf[10] = (len >> 16) & 0xFF;
tbuf[11] = (len >> 24) & 0xFF;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_FW_CKSUM, tbuf, 12, rbuf, &rlen);
if ((ret || (rlen != 4+SIZE_IANA_ID)) && (retries--)) { // checksum has to be 4 bytes
sleep(1);
syslog(LOG_ERR, "bic_get_fw_cksum: slot: %d, target %d, offset: %d, ret: %d, rlen: %d\n", slot_id, target, offset, ret, rlen);
goto bic_send;
}
if (ret || (rlen != 4+SIZE_IANA_ID)) {
return -1;
}
#ifdef DEBUG
printf("cksum returns: %x:%x:%x::%x:%x:%x:%x\n", rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4], rbuf[5], rbuf[6]);
#endif
//Ignore IANA ID
memcpy(ver, &rbuf[SIZE_IANA_ID], rlen-SIZE_IANA_ID);
return ret;
}
// Check IPMB start or not
static int
_ping_ipmb(uint8_t slot_id) {
uint8_t rbuf[3] = {0};
uint8_t tbuf[3] = {0x15, 0xA0, 0x00}; // IANA
uint16_t tlen = 2;
uint8_t rlen = 2;
uint8_t bus_id;
int ret = 0;
int retry = 0;
if (!is_bic_ready(slot_id)) {
return -1;
}
ret = get_ipmb_bus_id(slot_id);
if (ret < 0) {
return ret;
}
bus_id = (uint8_t) ret;
while(retry < 3) {
// Invoke IPMB library handler
ret = lib_ipmb_handle(bus_id, tbuf, tlen, rbuf, &rlen);
if (ret != 0) {
syslog(LOG_ERR, "%s: Failed to ping.\n", __func__);
}
if (rlen == IPMB_PING_LEN && !memcpy(tbuf,rbuf,IPMB_PING_LEN) ) {
break;
}
retry++;
msleep(20);
}
return ret;
}
// Read Firwmare Versions of various components
static int
_enable_bic_update(uint8_t slot_id) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00};
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
// 0x1: Update on I2C Channel, the only available option from BMC
tbuf[3] = 0x1;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_ENABLE_BIC_UPDATE, tbuf, 4, rbuf, &rlen);
return ret;
}
// Update firmware for various components
static int
_update_fw(uint8_t slot_id, uint8_t target, uint32_t offset, uint16_t len, uint8_t *buf) {
uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint16_t tlen = 0;
uint8_t rlen = 0;
int ret;
int retries = 3;
// Fill the component for which firmware is requested
tbuf[3] = target;
tbuf[4] = (offset) & 0xFF;
tbuf[5] = (offset >> 8) & 0xFF;
tbuf[6] = (offset >> 16) & 0xFF;
tbuf[7] = (offset >> 24) & 0xFF;
tbuf[8] = len & 0xFF;
tbuf[9] = (len >> 8) & 0xFF;
memcpy(&tbuf[10], buf, len);
tlen = len + 10;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_UPDATE_FW, tbuf, tlen, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
printf("_update_fw: slot: %d, target %d, offset: %d, len: %d retrying..\
\n", slot_id, target, offset, len);
goto bic_send;
}
return ret;
}
int
_check_brcm_fw_status(uint8_t slot_id, uint8_t drv_num) {
uint8_t bus, wbuf[256], rbuf[256];
int ret = 0;
int rlen = 4; // read
bus = (2 + drv_num/2) * 2 + 1;
wbuf[0] = BRCM_READ_CMD; // offset 131
wbuf[1] = 9;
wbuf[2] = 0x20;
wbuf[3] = 0x04;
wbuf[4] = 0x07;
wbuf[5] = 0x40;
wbuf[6] = 4;//count;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 7, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d failed", __func__,wbuf[0]);
} else {
syslog(LOG_DEBUG,"%s(): rbuf[0]=0x%X rbuf[1]=0x%X rbuf[2]=0x%X rbuf[3]=0x%X", __func__,rbuf[0],rbuf[1],rbuf[2],rbuf[3]);
uint32_t result;
memcpy(&result,&rbuf[0], sizeof(uint32_t));
syslog(LOG_DEBUG,"%s(): result=0x%X", __func__,result);
}
return ret;
}
int
bic_disable_brcm_parity_init(uint8_t slot_id, uint8_t drv_num) {
uint8_t bus, wbuf[256], rbuf[256];
int ret = 0;
int rlen = 0; // write
bic_disable_sensor_monitor(slot_id, 1); // disable sensor monitor
msleep(100);
// MUX select
bus = (2 + drv_num/2) * 2 + 1;
wbuf[0] = 1 << (drv_num%2);
ret = bic_master_write_read(slot_id, bus, 0xe2, wbuf, 1, rbuf, 0);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d failed", __func__,wbuf[0]);
bic_disable_sensor_monitor(slot_id, 0); // enable sensor monitor
return ret;
}
wbuf[0] = BRCM_WRITE_CMD; // offset 130
wbuf[1] = 0x08;
wbuf[2] = 0x78;
wbuf[3] = 0x0c;
wbuf[4] = 0x07;
wbuf[5] = 0x40;
wbuf[6] = 0x04;
wbuf[7] = 0x00;
wbuf[8] = 0x40;
wbuf[9] = 0x00;
wbuf[10] = 0x00;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 11, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d failed", __func__,wbuf[0]);
}
bic_disable_sensor_monitor(slot_id, 0); // enable sensor monitor
return ret;
}
int
_update_brcm_fw(uint8_t slot_id, uint8_t drv_num, uint8_t target, uint32_t offset, uint16_t count, uint8_t * buf) {
uint8_t bus, wbuf[256], rbuf[256];
int ret = 0;
int rlen = 0; // write
bus = (2 + drv_num/2) * 2 + 1;
wbuf[0] = BRCM_WRITE_CMD; // offset 130
wbuf[1] = count+5;
offset += 0x00400000;
memcpy(&wbuf[2],&offset, sizeof(uint32_t));
wbuf[6] = 5;
memcpy(&wbuf[7],buf, sizeof(uint8_t)*count);
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, count+7, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d failed", __func__,wbuf[0]);
}
return ret;
}
int
_update_vsi_fw(uint8_t slot_id, uint8_t drv_num, uint8_t target, uint32_t offset, uint16_t count, uint8_t * buf) {
uint8_t bus, wbuf[256], rbuf[256];
int ret = 0;
int rlen = 0; // write
bus = (2 + drv_num/2) * 2 + 1;
wbuf[0] = VSI_WRITE_CMD; // offset 136
memcpy(&wbuf[1],buf, sizeof(uint8_t)*count);
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, count+1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d failed\n", __func__,wbuf[0]);
}
return ret;
}
// Update firmware for various components
static int
_update_pcie_sw_fw(uint8_t slot_id, uint8_t target, uint32_t offset, uint16_t len, u_int32_t image_len, uint8_t *buf) {
uint8_t tbuf[256] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t tlen = 0;
uint8_t rlen = 0;
int ret;
int retries = 3;
// Fill the component for which firmware is requested
tbuf[3] = target;
tbuf[4] = (offset) & 0xFF;
tbuf[5] = (offset >> 8) & 0xFF;
tbuf[6] = (offset >> 16) & 0xFF;
tbuf[7] = (offset >> 24) & 0xFF;
tbuf[8] = len & 0xFF;
tbuf[9] = (len >> 8) & 0xFF;
tbuf[10] = (image_len) & 0xFF;
tbuf[11] = (image_len >> 8) & 0xFF;
tbuf[12] = (image_len >> 16) & 0xFF;
tbuf[13] = (image_len >> 24) & 0xFF;
memcpy(&tbuf[14], buf, len);
tlen = len + 14;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_UPDATE_FW, tbuf, tlen, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
printf("_update_fw: slot: %d, target %d, offset: %d, len: %d retrying..\
\n", slot_id, target, offset, len);
goto bic_send;
}
return ret;
}
// Get PCIE switch update status
static int
_get_pcie_sw_update_status(uint8_t slot_id, uint8_t *status) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
tbuf[3] = 0x01;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_PCIE_SWITCH_STATUS, tbuf, 4, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
syslog(LOG_ERR,"_get_pcie_sw_update_status: slot: %d, retrying..\n", slot_id);
goto bic_send;
}
// Ignore IANA ID
memcpy(status, &rbuf[3], 2);
return ret;
}
// Reset PCIE switch update status
static int
_reset_pcie_sw_update_status(uint8_t slot_id) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
tbuf[3] = 0x00;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_PCIE_SWITCH_STATUS, tbuf, 4, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
printf("_reset_pcie_sw_update_status: slot: %d, retrying..\n", slot_id);
goto bic_send;
}
return ret;
}
// Terminate PCIE switch update
static int
_terminate_pcie_sw_update(uint8_t slot_id) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
tbuf[3] = 0x02;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_PCIE_SWITCH_STATUS, tbuf, 4, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
printf("_terminate_pcie_sw_update: slot: %d, retrying..\n", slot_id);
goto bic_send;
}
return ret;
}
// Read firmware for various components
static int
_dump_fw(uint8_t slot_id, uint8_t target, uint32_t offset, uint8_t len, uint8_t *rbuf, uint8_t *rlen) {
uint8_t tbuf[16] = {0x15, 0xA0, 0x00}; // IANA ID
int ret;
int retries = 3;
// Fill the component for which firmware is requested
tbuf[3] = target;
memcpy(&tbuf[4], &offset, sizeof(uint32_t));
tbuf[8] = len;
do {
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_READ_FW_IMAGE, tbuf, 9, rbuf, rlen);
if (!ret && (len == (*rlen -= 3)))
return 0;
sleep(1);
printf("_dump_fw: slot: %d, target %d, offset: %d, len: %d retrying..\n", slot_id, target, offset, len);
} while ((--retries));
return -1;
}
// Get CPLD update progress
static int
_get_cpld_update_progress(uint8_t slot_id, uint8_t *progress) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[16] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_CPLD_UPDATE_PROGRESS, tbuf, 3, rbuf, &rlen);
if ((ret) && (retries--)) {
sleep(1);
printf("_get_cpld_update_progress: slot: %d, retrying..\n", slot_id);
goto bic_send;
}
// Ignore IANA ID
memcpy(progress, &rbuf[3], 1);
return ret;
}
static int
check_bic_image(uint8_t slot_id, int fd, long size) {
int offs, rcnt;
uint8_t slot_type;
uint8_t server_type;
uint8_t buf[64];
const uint8_t hdr_common[] = {0x01,0x00,0x4c,0x1c,0x00,0x53,0x4e,0x4f,0x57,0x46,0x4c,0x41,0x4b,0x45};
const char *plat_str = NULL;
if (size < BIC_SIGN_SIZE) {
return -1;
}
slot_type = bic_get_slot_type(slot_id);
switch (slot_type) {
case SLOT_TYPE_GPV2:
plat_str = GPV2_BIC_PLAT_STR;
break;
case SLOT_TYPE_SERVER:
bic_get_server_type(slot_id, &server_type);
if(server_type == SERVER_TYPE_ND)
plat_str = ND_BIC_PLAT_STR;
else
return 0;
break;
default:
return 0;
}
offs = size - BIC_SIGN_SIZE;
if (lseek(fd, offs, SEEK_SET) != (off_t)offs) {
syslog(LOG_ERR, "%s: lseek to %d failed", __func__, offs);
return -1;
}
offs = 0;
while (offs < BIC_SIGN_SIZE) {
rcnt = file_read_bytes(fd, (buf + offs), BIC_SIGN_SIZE);
if (rcnt <= 0) {
syslog(LOG_ERR, "%s: unexpected rcnt: %d", __func__, rcnt);
return -1;
}
offs += rcnt;
}
if (memcmp(buf, hdr_common, sizeof(hdr_common))) {
return -1;
}
if (memcmp(buf+sizeof(hdr_common), plat_str, strlen(plat_str)+1)) {
return -1;
}
if ((offs = lseek(fd, 0, SEEK_SET))) {
syslog(LOG_ERR, "%s: fail to init file offset %d, errno=%d", __func__, offs, errno);
return -1;
}
return 0;
}
static int
check_gpv2_rp_image(int *rp_type, int fd, long size) {
int offs;
uint8_t buf[4];
const uint8_t hdr_pcie_phy[] = {0x45,0x49,0x43,0x50}; //EICP
const uint8_t hdr_rom_patch[] = {0x48,0x54,0x41,0x50}; //HTAP
if (size < 4)
return -1;
if (read(fd, buf, 4) != 4)
return -1;
if (memcmp(buf, hdr_pcie_phy, 4) == 0) {
printf("Update PCIE PHY FW\n");
syslog(LOG_CRIT, "Update VSI: PCIE PHY FW\n");
*rp_type = PCIE_PHY_FW;
} else if (memcmp(buf, hdr_rom_patch, 4) == 0) {
printf("Update ROM PATCH FW\n");
syslog(LOG_CRIT, "Update VSI: ROM PATCH FW\n");
*rp_type = BOOT_ROM_PATCH_FW;
} else {
printf("invalid file for RP!\n");
*rp_type = UNKOWN_FW;
return -1;
}
if ((offs = lseek(fd, 0, SEEK_SET))) {
syslog(LOG_ERR, "%s: fail to init file offset %d, errno=%d", __func__, offs, errno);
return -1;
}
return 0;
}
static int
_update_bic_main(uint8_t slot_id, char *path, uint8_t force) {
#define MAX_CMD_LEN 100
int fd;
int ifd = -1;
char cmd[MAX_CMD_LEN] = {0};
struct stat buf;
int size;
uint8_t tbuf[256] = {0};
uint8_t rbuf[16] = {0};
uint8_t tcount;
uint8_t rcount;
volatile int xcount;
int i = 0;
int ret = -1, rc;
uint8_t xbuf[256] = {0};
uint32_t offset = 0, last_offset = 0, dsize;
struct rlimit mqlim;
uint8_t done = 0;
// Open the file exclusively for read
fd = open(path, O_RDONLY, 0666);
if (fd < 0) {
syslog(LOG_ERR, "bic_update_fw: open fails for path: %s\n", path);
goto error_exit2;
}
fstat(fd, &buf);
size = buf.st_size;
printf("size of file is %d bytes\n", size);
dsize = size/20;
if (!force && check_bic_image(slot_id, fd, size)) {
printf("invalid BIC file!\n");
goto error_exit2;
}
syslog(LOG_CRIT, "bic_update_fw: update bic firmware on slot %d\n", slot_id);
// Open the i2c driver
ifd = i2c_open(get_ipmb_bus_id(slot_id));
if (ifd < 0) {
printf("ifd error\n");
goto error_exit2;
}
// Kill ipmb daemon for this slot
sprintf(cmd, "sv stop ipmbd_%d", get_ipmb_bus_id(slot_id));
log_system(cmd);
printf("stop ipmbd for slot %x..\n", slot_id);
// The I2C high speed clock (1M) could cause to read BIC data abnormally.
// So reduce I2C bus clock speed which is a workaround for BIC update.
switch(slot_id)
{
case FRU_SLOT1:
log_system("devmem 0x1e78a084 w 0xFFF77304");
break;
case FRU_SLOT2:
log_system("devmem 0x1e78a104 w 0xFFF77304");
break;
case FRU_SLOT3:
log_system("devmem 0x1e78a184 w 0xFFF77304");
break;
case FRU_SLOT4:
log_system("devmem 0x1e78a304 w 0xFFF77304");
break;
default:
syslog(LOG_CRIT, "bic_update_fw: incorrect slot_id %d\n", slot_id);
goto error_exit;
break;
}
sleep(1);
printf("Stopped ipmbd for this slot %x..\n",slot_id);
if (is_bic_ready(slot_id)) {
mqlim.rlim_cur = RLIM_INFINITY;
mqlim.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_MSGQUEUE, &mqlim) < 0) {
goto error_exit;
}
// Restart ipmb daemon with "-u|--enable-bic-update" for bic update
memset(cmd, 0, sizeof(cmd));
sprintf(cmd, "/usr/local/bin/ipmbd -u %d %d > /dev/null 2>&1 &", get_ipmb_bus_id(slot_id), slot_id);
log_system(cmd);
printf("start ipmbd -u for this slot %x..\n",slot_id);
syslog(LOG_WARNING, "start ipmbd -u for this slot %x..\n",slot_id);
for (i = 0; i < PING_IPMB_RETRIES; i++) {
if (!_ping_ipmb(slot_id)) {
printf("ipmbd ready for update after %d tries\n", i);
break;
}
sleep(1);
}
if (i != PING_IPMB_RETRIES) { // ipmb ready
// Enable Bridge-IC update
syslog(LOG_WARNING, "_enable_bic_update slot %x..\n",slot_id);
_enable_bic_update(slot_id);
}
// Kill ipmb daemon "--enable-bic-update" for this slot
memset(cmd, 0, sizeof(cmd));
sprintf(cmd, "ps -w | grep -v 'grep' | grep 'ipmbd -u %d' |awk '{print $1}'| xargs kill", get_ipmb_bus_id(slot_id));
log_system(cmd);
printf("stop ipmbd for slot %x..\n", slot_id);
if (i == BIC_UPDATE_RETRIES) { // ipmb not ready
printf("ipmbd is NOT ready for update\n");
syslog(LOG_CRIT, "bic_update_fw: ipmbd is NOT ready for update\n");
goto error_exit;
}
}
// Wait for SMB_BMC_3v3SB_ALRT_N
for (i = 0; i < BIC_UPDATE_RETRIES; i++) {
if (!is_bic_ready(slot_id)) {
printf("bic ready for update after %d tries\n", i);
break;
}
msleep(BIC_UPDATE_TIMEOUT);
}
if (i == BIC_UPDATE_RETRIES) {
printf("bic is NOT ready for update\n");
syslog(LOG_CRIT, "bic_update_fw: bic is NOT ready for update\n");
goto error_exit;
}
sleep(1);
// Start Bridge IC update(0x21)
tbuf[0] = CMD_DOWNLOAD_SIZE;
tbuf[1] = 0x00; //Checksum, will fill later
tbuf[2] = BIC_CMD_DOWNLOAD;
// update flash address: 0x8000
tbuf[3] = (BIC_FLASH_START >> 24) & 0xff;
tbuf[4] = (BIC_FLASH_START >> 16) & 0xff;
tbuf[5] = (BIC_FLASH_START >> 8) & 0xff;
tbuf[6] = (BIC_FLASH_START) & 0xff;
// image size
tbuf[7] = (size >> 24) & 0xff;
tbuf[8] = (size >> 16) & 0xff;
tbuf[9] = (size >> 8) & 0xff;
tbuf[10] = (size) & 0xff;
// calcualte checksum for data portion
for (i = 2; i < CMD_DOWNLOAD_SIZE; i++) {
tbuf[1] += tbuf[i];
}
tcount = CMD_DOWNLOAD_SIZE;
rcount = 0;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed download\n");
goto error_exit;
}
//delay for download command process ---
msleep(500);
tcount = 0;
rcount = 2;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed download ack\n");
goto error_exit;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
printf("download response: %x:%x\n", rbuf[0], rbuf[1]);
goto error_exit;
}
// Loop to send all the image data
while (1) {
// Get Status
tbuf[0] = CMD_STATUS_SIZE;
tbuf[1] = BIC_CMD_STATUS;// checksum same as data
tbuf[2] = BIC_CMD_STATUS;
tcount = CMD_STATUS_SIZE;
rcount = 0;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed get status\n");
goto error_exit;
}
tcount = 0;
rcount = 5;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed get status ack\n");
goto error_exit;
}
if (rbuf[0] != 0x00 ||
rbuf[1] != 0xcc ||
rbuf[2] != 0x03 ||
rbuf[3] != 0x40 ||
rbuf[4] != 0x40) {
printf("status resp: %x:%x:%x:%x:%x", rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4]);
goto error_exit;
}
// Send ACK ---
tbuf[0] = 0xcc;
tcount = 1;
rcount = 0;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed send ack\n");
goto error_exit;
}
// send next packet
xcount = read(fd, xbuf, BIC_PKT_MAX);
if (xcount <= 0) {
#ifdef DEBUG
printf("read returns %d\n", xcount);
#endif
break;
}
#ifdef DEBUG
printf("read returns %d bytes\n", xcount);
#endif
offset += xcount;
if((last_offset + dsize) <= offset) {
printf("updated bic: %d %%\n", offset/dsize*5);
last_offset += dsize;
}
tbuf[0] = xcount + 3;
tbuf[1] = BIC_CMD_DATA;
tbuf[2] = BIC_CMD_DATA;
memcpy(&tbuf[3], xbuf, xcount);
for (i = 0; i < xcount; i++) {
tbuf[1] += xbuf[i];
}
tcount = tbuf[0];
rcount = 0;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io error send data\n");
goto error_exit;
}
msleep(10);
tcount = 0;
rcount = 2;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io error send data ack\n");
goto error_exit;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
printf("data error: %x:%x\n", rbuf[0], rbuf[1]);
goto error_exit;
}
}
msleep(500);
// Run the new image
tbuf[0] = CMD_RUN_SIZE;
tbuf[1] = 0x00; //checksum
tbuf[2] = BIC_CMD_RUN;
tbuf[3] = (BIC_FLASH_START >> 24) & 0xff;
tbuf[4] = (BIC_FLASH_START >> 16) & 0xff;
tbuf[5] = (BIC_FLASH_START >> 8) & 0xff;
tbuf[6] = (BIC_FLASH_START) & 0xff;
for (i = 2; i < CMD_RUN_SIZE; i++) {
tbuf[1] += tbuf[i];
}
tcount = CMD_RUN_SIZE;
rcount = 0;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed run\n");
goto error_exit;
}
tcount = 0;
rcount = 2;
rc = i2c_io(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
printf("i2c_io failed run ack\n");
goto error_exit;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
printf("run response: %x:%x\n", rbuf[0], rbuf[1]);
goto error_exit;
}
msleep(500);
// Wait for SMB_BMC_3v3SB_ALRT_N
for (i = 0; i < BIC_UPDATE_RETRIES; i++) {
if (is_bic_ready(slot_id))
break;
msleep(BIC_UPDATE_TIMEOUT);
}
if (i == BIC_UPDATE_RETRIES) {
printf("bic is NOT ready\n");
goto error_exit;
}
ret = 0;
done = 1;
error_exit:
// Restore the I2C bus clock to 1M.
switch(slot_id)
{
case FRU_SLOT1:
log_system("devmem 0x1e78a084 w 0xFFF5E700");
break;
case FRU_SLOT2:
log_system("devmem 0x1e78a104 w 0xFFF5E700");
break;
case FRU_SLOT3:
log_system("devmem 0x1e78a184 w 0xFFF5E700");
break;
case FRU_SLOT4:
log_system("devmem 0x1e78a304 w 0xFFF5E700");
break;
default:
syslog(LOG_ERR, "bic_update_fw: incorrect slot_id %d\n", slot_id);
break;
}
// Restart ipmbd daemon
sleep(1);
memset(cmd, 0, sizeof(cmd));
sprintf(cmd, "sv start ipmbd_%d", get_ipmb_bus_id(slot_id));
log_system(cmd);
error_exit2:
syslog(LOG_CRIT, "bic_update_fw: updating bic firmware is exiting on slot %d\n", slot_id);
if (fd > 0) {
close(fd);
}
if (ifd > 0) {
close(ifd);
}
// do not get sdr during sdr update
bic_set_sdr_threshold_update_flag(slot_id, 0);
if (done == 1) { //update successfully
memset(cmd, 0, sizeof(cmd));
snprintf(cmd, MAX_CMD_LEN, "(/usr/local/bin/bic-cached %d; /usr/bin/kv set slot%d_sdr_thresh_update 1) &", slot_id, slot_id); //retrieve SDR data after BIC FW update
log_system(cmd);
}
return ret;
}
static int
check_vr_image(uint8_t slot_id, int fd, long size) {
uint8_t buf[32];
uint8_t *hdr;
uint8_t hdr_size;
int offs;
int ret;
uint8_t server_type = 0xFF;
vr_header vr_hdr = {
.ver = {0x00,0x01},
.iaan = {0x4c,0x1c,0x00},
.prj_name = {0}
};
ret = bic_get_server_type(slot_id, &server_type);
if (ret) {
syslog(LOG_ERR, "%s, Get server type failed\n", __func__);
return -1;
}
switch (server_type) {
case SERVER_TYPE_ND:
sprintf((char*) vr_hdr.prj_name, "F09C");
hdr = (uint8_t*)&vr_hdr;
hdr_size = 9; // ver (2) + iaan (3) + prj_name (4)
break;
default:
sprintf((char*) vr_hdr.prj_name, "F09");
hdr = (uint8_t*)&vr_hdr;
hdr_size = 8; // ver (2) + iaan (3) + prj_name (3)
break;
}
if (size < 16)
return -1;
offs = 1;
if (lseek(fd, offs, SEEK_SET) != (off_t)offs) {
syslog(LOG_ERR, "%s: lseek to %d failed", __func__, offs);
return -1;
}
if (read(fd, buf, hdr_size) != hdr_size)
return -1;
if (memcmp(buf, hdr, hdr_size))
return -1;
if ((offs = lseek(fd, 0, SEEK_SET))) {
syslog(LOG_ERR, "%s: fail to init file offset %d, errno=%d", __func__, offs, errno);
return -1;
}
return 0;
}
static int
check_cpld_image(uint8_t slot_id, int fd, long size) {
uint8_t buf[32];
uint8_t hdr_tl[] = {0x01,0x00,0x4c,0x1c,0x00,0x01,0x2b,0xb0,0x43,0x46,0x30,0x39};
uint8_t *hdr = hdr_tl, hdr_size = sizeof(hdr_tl);
int offs;
#if defined(CONFIG_FBY2_ND)
int ret;
uint8_t server_type = 0xFF;
uint8_t hdr_nd[] = {0x01,0x00,0x4c,0x1c,0x00,0x01,0x2b,0xb0,0x43,0x46,0x30,0x39,0x43};
ret = bic_get_server_type(slot_id, &server_type);
if (ret) {
syslog(LOG_ERR, "%s, Get server type failed\n", __func__);
return -1;
}
switch (server_type) {
case SERVER_TYPE_ND:
hdr = hdr_nd;
hdr_size = sizeof(hdr_nd);
break;
}
#endif
if (size < 32)
return -1;
if (read(fd, buf, hdr_size) != hdr_size)
return -1;
if (memcmp(buf, hdr, hdr_size))
return -1;
if ((offs = lseek(fd, 0, SEEK_SET))) {
syslog(LOG_ERR, "%s: fail to init file offset %d, errno=%d", __func__, offs, errno);
return -1;
}
return 0;
}
static int
check_bios_image(uint8_t slot_id, int fd, long size) {
int offs, rcnt, end;
uint8_t *buf;
uint8_t ver_sig[] = { 0x46, 0x49, 0x44, 0x04, 0x78, 0x00 };
if (size < BIOS_VER_REGION_SIZE)
return -1;
buf = (uint8_t *)malloc(BIOS_VER_REGION_SIZE);
if (!buf) {
return -1;
}
offs = size - BIOS_VER_REGION_SIZE;
if (lseek(fd, offs, SEEK_SET) != (off_t)offs) {
syslog(LOG_ERR, "%s: lseek to %d failed", __func__, offs);
free(buf);
return -1;
}
offs = 0;
while (offs < BIOS_VER_REGION_SIZE) {
rcnt = read(fd, (buf + offs), BIOS_ERASE_PKT_SIZE);
if (rcnt <= 0) {
if (errno == EINTR) {
continue;
}
syslog(LOG_ERR, "check_bios_image: unexpected rcnt: %d", rcnt);
free(buf);
return -1;
}
offs += rcnt;
}
end = BIOS_VER_REGION_SIZE - (sizeof(ver_sig) + strlen(BIOS_VER_STR));
for (offs = 0; offs < end; offs++) {
if (!memcmp(buf+offs, ver_sig, sizeof(ver_sig))) {
#if defined(CONFIG_FBY2_ND)
if (memcmp(
buf + offs + sizeof(ver_sig),
ND_BIOS_VER_STR,
strlen(ND_BIOS_VER_STR))) {
offs = end;
}
break;
#endif
if (memcmp(
buf + offs + sizeof(ver_sig),
BIOS_VER_STR,
strlen(BIOS_VER_STR)) &&
memcmp(
buf + offs + sizeof(ver_sig),
Y2_BIOS_VER_STR,
strlen(Y2_BIOS_VER_STR)) &&
memcmp(
buf + offs + sizeof(ver_sig),
GPV2_BIOS_VER_STR,
strlen(GPV2_BIOS_VER_STR))&&
memcmp(
buf + offs + sizeof(ver_sig),
YV2_MERGE_BIOS_VER_STR,
strlen(YV2_MERGE_BIOS_VER_STR))) {
offs = end;
}
break;
}
}
free(buf);
if (offs >= end)
return -1;
if ((offs = lseek(fd, 0, SEEK_SET))) {
syslog(LOG_ERR, "%s: fail to init file offset %d, errno=%d", __func__, offs, errno);
return -1;
}
return 0;
}
static int
_set_fw_update_ongoing(uint8_t slot_id, uint16_t tmout) {
char key[64];
char value[64] = {0};
struct timespec ts;
sprintf(key, "fru%u_fwupd", slot_id);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += tmout;
sprintf(value, "%ld", ts.tv_sec);
if (kv_set(key, value, 0, 0) < 0) {
return -1;
}
return 0;
}
int
bic_dump_fw(uint8_t slot_id, uint8_t comp, char *path) {
int ret = -1, rc, fd;
uint32_t offset, next_doffset;
#if defined(CONFIG_FBY2_ND)
uint32_t img_size = 0x1000000;
#else
uint32_t img_size = 0x2000000;
#endif
uint32_t dsize;
uint8_t count, read_count;
uint8_t buf[256], rlen;
if (comp != DUMP_BIOS) {
printf("ERROR: only support dump BIOS image!\n");
return ret;
}
printf("dumping fw on slot %d:\n", slot_id);
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
printf("ERROR: invalid file path!\n");
#ifdef DEBUG
syslog(LOG_ERR, "bic_dump_fw: open fails for path: %s\n", path);
#endif
goto error_exit;
}
// Write chunks of binary data in a loop
dsize = img_size / 100;
offset = 0;
next_doffset = offset + dsize;
while (1) {
read_count = ((offset + IPMB_READ_COUNT_MAX) <= img_size) ? IPMB_READ_COUNT_MAX : (img_size - offset);
// read image from Bridge-IC
rc = _dump_fw(slot_id, comp, offset, read_count, buf, &rlen);
if (rc) {
goto error_exit;
}
// Write to file
count = write(fd, &buf[3], rlen);
if (count <= 0) {
goto error_exit;
}
// Update counter
offset += count;
if (offset >= next_doffset) {
switch (comp) {
case DUMP_BIOS:
_set_fw_update_ongoing(slot_id, 60);
printf("\rdumped bios: %d %%", offset/dsize);
break;
}
fflush(stdout);
next_doffset += dsize;
}
if (offset >= img_size)
break;
}
if (comp == DUMP_BIOS) {
_set_fw_update_ongoing(slot_id, 60 * 2);
}
ret = 0;
error_exit:
printf("\n");
if (fd > 0 ) {
close(fd);
}
return ret;
}
int
bic_update_dev_firmware(uint8_t slot_id, uint8_t dev_id, uint8_t comp, char *path, uint8_t force) {
int ret = -1;
uint32_t offset;
volatile uint16_t count, read_count;
uint8_t buf[256] = {0};
int fd;
int retries = 0;
int rp_fw_type = UNKOWN_FW;
printf("updating fw on slot %d device %d:\n", slot_id,dev_id);
uint32_t dsize, last_offset;
struct stat st;
// Open the file exclusively for read
fd = open(path, O_RDONLY, 0666);
if (fd < 0) {
printf("ERROR: invalid file path!\n");
#ifdef DEBUG
syslog(LOG_ERR, "bic_update_fw: open fails for path: %s\n", path);
#endif
goto error_exit;
}
stat(path, &st);
if (comp == UPDATE_BRCM) {
syslog(LOG_CRIT, "Update BRCM: update brcm vk firmware on slot %d device %d\n", slot_id, dev_id);
} else if (comp == UPDATE_VSI) {
if (check_gpv2_rp_image(&rp_fw_type, fd, st.st_size) < 0) {
goto error_exit;
}
syslog(LOG_CRIT, "Update VSI: update vsi rp firmware on slot %d device %d\n", slot_id, dev_id);
}
if (st.st_size/100 < 100)
dsize = st.st_size;
else
dsize = st.st_size/100;
bic_disable_sensor_monitor(slot_id, 1);
msleep(100);
offset = 0;
last_offset = 0;
uint8_t bus, wbuf[256], rbuf[256];
int rlen = 0;
bus = (2 + dev_id/2) * 2 + 1;
wbuf[0] = 1 << (dev_id%2);
// MUX
ret = bic_master_write_read(slot_id, bus, 0xe2, wbuf, 1, rbuf, 0);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read failed\n", __func__);
goto error_exit;
}
if (comp == UPDATE_BRCM) {
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 process bit == 0?
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): process bit == 0? offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
if (rbuf[0] & 2) {
syslog(LOG_DEBUG,"%s(): process bit == 1 offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
ret = -1;
goto error_exit;
} else {
syslog(LOG_DEBUG,"%s(): process bit == 0 offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
}
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 set download init
wbuf[1] = rbuf[0] | 0x80;
rlen = 0; // write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 2, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): set download init offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
} else {
syslog(LOG_DEBUG,"%s(): set download init offset=%d", __func__,wbuf[0]);
}
for (retries = 0; retries < BRCM_UPDATE_RTRIES; retries++) {
sleep(2);
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 reday bit == 1?
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): reday bit == 1? offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
if (!(rbuf[0] & 0x40)) {
syslog(LOG_DEBUG,"%s(): reday bit == 0 reties = %d", __func__, retries);
ret = -1;
} else {
syslog(LOG_DEBUG,"%s(): reday bit == 1 offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
break;
}
}
if (retries == BRCM_UPDATE_RTRIES) {
syslog(LOG_DEBUG,"%s(): reday bit == 0 reties failed", __func__);
goto error_exit;
}
wbuf[0] = BRCM_WRITE_CMD; // offset 130
wbuf[1] = 9;
uint32_t addr = 0x00400001;
memcpy(&wbuf[2],&addr, sizeof(uint32_t));
wbuf[6] = 4;
wbuf[7] = 0x00;
wbuf[8] = 0x00;
wbuf[9] = 0x00;
wbuf[10] = 0x00;
rlen = 0; // write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 11, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): bic_master_write_read offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
} else if (comp == UPDATE_VSI) { // update VSI
//# Step 1: host informs ASIC: host ready
wbuf[0] = VSI_UPDATE_STATUS; // offset 133
wbuf[1] = 0x80;
rlen = 0; //write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 2, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): host informs ASIC: host ready offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
sleep(1);
//# Step 2: host informs ASIC: host will trans the image of bootrom patch
wbuf[0] = VSI_FW_TYPE; // offset 135
wbuf[1] = rp_fw_type;
rlen = 0; //write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 2, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): host will trans the image offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
sleep(1);
//# Step 3: ASIC informs host: ASIC ready
wbuf[0] = VSI_UPDATE_STATUS; // offset 133
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s():ASIC ready offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
} else {
syslog(LOG_DEBUG,"%s(): ASIC ready offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
}
sleep(2);
}
// Write chunks of binary data in a loop
syslog(LOG_DEBUG,"%s(): Start a loop", __func__);
while (1) {
if (comp == UPDATE_BRCM) {
read_count = IPMB_WRITE_COUNT_MAX;
} else {
read_count = 1;
}
// Read from file
count = read(fd, buf, read_count);
if (count <= 0 || count > read_count) {
break;
}
if (comp == UPDATE_BRCM) {
ret = _update_brcm_fw(slot_id, dev_id, comp, offset, count, buf);
} else if (comp == UPDATE_VSI) {
ret = _update_vsi_fw(slot_id, dev_id, comp, offset, count, buf);
}
if (ret) {
goto error_exit;
}
if (comp == UPDATE_BRCM) {
msleep(1); // wait
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 process bit == 1?
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): process bit == 1? offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
if (!(rbuf[0] & 2)) {
syslog(LOG_DEBUG,"%s(): process bit == 0", __func__);
ret = -1;
goto error_exit;
}
} else {
msleep(1); // wait
}
// Update counter
offset += count;
if ((last_offset + dsize) <= offset) {
switch(comp) {
case UPDATE_BRCM:
_set_fw_update_ongoing(slot_id, 60);
if (st.st_size/100 < 100)
printf("\rupdated brcm vk: %d %%", offset/dsize*100);
else
printf("\rupdated brcm vk: %d %%", offset/dsize);
break;
case UPDATE_VSI:
_set_fw_update_ongoing(slot_id, 60);
if (st.st_size/100 < 100)
printf("\rupdated vsi rp: %d %%", offset/dsize*100);
else
printf("\rupdated vsi rp: %d %%", offset/dsize);
break;
}
fflush(stdout);
last_offset += dsize;
}
}
syslog(LOG_DEBUG,"%s(): End a loop", __func__);
if (comp == UPDATE_BRCM) {
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 set activation
wbuf[1] = rbuf[0] | 0x8;
rlen = 0; // write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 2, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): set activation offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
wbuf[0] = BRCM_UPDATE_STATUS; // offset 128 readiness bit == 1?
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): readiness bit == 1? offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
if (!(rbuf[0] & 4)) {
syslog(LOG_DEBUG,"%s(): readiness bit == 0", __func__);
goto error_exit;
} else {
syslog(LOG_DEBUG,"%s(): readiness bit == 1 offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
}
for (int i=0;i<5;i++) {
_check_brcm_fw_status(slot_id,dev_id);
sleep(1);
}
} else {
sleep(2);
//# Step 5: host informs ASIC: host Tx done
wbuf[0] = VSI_UPDATE_STATUS; // offset 133
wbuf[1] = 0x08;
rlen = 0; //write
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 2, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): host informs ASIC: host Tx done offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
}
sleep(2);
//# Step 6: ASIC inform host: ASIC Rx done
wbuf[0] = VSI_UPDATE_STATUS; // offset 133
rlen = 1;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG,"%s(): ASIC inform host: ASIC Rx done offset=%d read length=%d failed", __func__,wbuf[0],rlen);
goto error_exit;
} else {
syslog(LOG_DEBUG,"%s(): ASIC inform host: ASIC Rx done offset=%d rbuf[0]=%d", __func__,wbuf[0],rbuf[0]);
if (rbuf[0] != 6) {
ret = -1;
goto error_exit;
}
}
sleep(1);
}
ret = 0;
error_exit:
bic_disable_sensor_monitor(slot_id, 0);
printf("\n");
switch(comp) {
case UPDATE_BRCM:
syslog(LOG_CRIT, "Update BRCM: updating brcm vk device firmware is exiting on slot %d device %d\n", slot_id, dev_id);
break;
case UPDATE_VSI:
syslog(LOG_CRIT, "Update VSI: updating vsi rp device firmware is exiting on slot %d device %d\n", slot_id, dev_id);
break;
}
if (fd > 0 ) {
close(fd);
}
return ret;
}
int
bic_get_fw_cksum_sha256(uint8_t slot_id, uint8_t target, uint32_t offset, uint32_t len, uint8_t *ver) {
uint8_t tbuf[12] = {0x15, 0xa0, 0x00}; // IANA ID
uint8_t rbuf[256] = {0x00};
uint8_t rlen = 0;
int ret;
int retries = 3;
// Fill the component for which firmware is requested
tbuf[3] = target;
// Fill the offset
tbuf[4] = (offset) & 0xFF;
tbuf[5] = (offset >> 8) & 0xFF;
tbuf[6] = (offset >> 16) & 0xFF;
tbuf[7] = (offset >> 24) & 0xFF;
// Fill the length
tbuf[8] = (len) & 0xFF;
tbuf[9] = (len >> 8) & 0xFF;
tbuf[10] = (len >> 16) & 0xFF;
tbuf[11] = (len >> 24) & 0xFF;
bic_send:
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_SHA256, tbuf, 12, rbuf, &rlen);
if (ret == BIC_INVALID_CMD) { // Once the command is not supported from BIC, do not retry the command
syslog(LOG_ERR, "bic_get_fw_cksum_sha256: slot: %d, BIC not support checking sha256 sum\n", slot_id);
} else if ((ret || (rlen != 32+SIZE_IANA_ID)) && (retries--)) { // sha256 is 32 bytes
sleep(1);
syslog(LOG_ERR, "bic_get_fw_cksum_sha256: slot: %d, target %d, offset: %d, ret: %d, rlen: %d\n", slot_id, target, offset, ret, rlen);
goto bic_send;
}
if (ret != 0) {
// just return if invalid cmd or no response
goto out;
}
//Ignore IANA ID
memcpy(ver, &rbuf[SIZE_IANA_ID], rlen-SIZE_IANA_ID);
out:
return ret;
}
static int
calc_checksum_simple(const uint8_t *buf, size_t len, uint8_t *out) {
uint32_t cs = 0;
while (len-- > 0) {
cs += *buf++;
}
*((uint32_t *) out) = cs;
return 0;
}
static int
calc_checksum_sha256(const void *buf, size_t len, uint8_t *out) {
SHA256_CTX ctx = {0};
memset(out, 0, STRONG_DIGEST_LENGTH);
if (SHA256_Init(&ctx) != 1) return -1;
if (SHA256_Update(&ctx, buf, len) != 1) return -2;
if (SHA256_Final(out, &ctx) != 1) return -3;
return 0;
}
static bool
bic_have_checksum_sha256(uint8_t slot_id) {
uint8_t cs[STRONG_DIGEST_LENGTH];
return (bic_get_fw_cksum_sha256(slot_id, UPDATE_BIOS, 0, BIOS_UPDATE_BLK_SIZE, cs) == 0);
}
static int
get_block_checksum(uint8_t slot_id, size_t offset, int cs_len, uint8_t *out) {
int rc;
if (cs_len == STRONG_DIGEST_LENGTH) {
rc = bic_get_fw_cksum_sha256(slot_id, UPDATE_BIOS, offset, BIOS_UPDATE_BLK_SIZE, out);
} else {
rc = bic_get_fw_cksum(slot_id, UPDATE_BIOS, offset, BIOS_VERIFY_PKT_SIZE, out);
if (rc == 0) {
rc = bic_get_fw_cksum(slot_id, UPDATE_BIOS, offset + BIOS_VERIFY_PKT_SIZE,
BIOS_VERIFY_PKT_SIZE, out + SIMPLE_DIGEST_LENGTH);
}
}
return rc;
}
struct bic_update_fw_req {
uint8_t iana_id[3];
uint8_t target;
uint32_t offset;
uint16_t length;
} __attribute__((packed));
#define UPD_PKT_HDR_SIZE sizeof(struct bic_update_fw_req)
static int
bic_update_bios_firmware(uint8_t slot_id, int fd)
{
int ret = -1, rc = 0;
uint8_t *buf = NULL;
size_t write_offset = 0;
const char *dedup_env = getenv("FW_UTIL_DEDUP");
const char *verify_env = getenv("FW_UTIL_VERIFY");
bool dedup = (dedup_env != NULL ? (*dedup_env == '1') : true);
bool verify = (verify_env != NULL ? (*verify_env == '1') : true);
buf = malloc(UPD_PKT_HDR_SIZE + BIOS_UPDATE_BLK_SIZE);
if (buf == NULL) {
fprintf(stderr, "failed to allocate memory\n");
goto out;
}
int num_blocks_written = 0, num_blocks_skipped = 0;
uint8_t fcs[STRONG_DIGEST_LENGTH], cs[STRONG_DIGEST_LENGTH];
int cs_len = STRONG_DIGEST_LENGTH;
if (!bic_have_checksum_sha256(slot_id)) {
if (dedup && !(dedup_env != NULL && *dedup_env == '1')) {
fprintf(stderr, "Strong checksum function is not available, disabling "
"deduplication. Update BIC firmware to at least 1.24\n");
dedup = false;
}
cs_len = SIMPLE_DIGEST_LENGTH * 2;
}
fprintf(stderr, "Updating BIOS, dedup is %s, verification is %s.\n",
(dedup ? "on" : "off"), (verify ? "on" : "off"));
_set_fw_update_ongoing(slot_id, 60);
int attempts = NUM_ATTEMPTS;
size_t file_buf_num_bytes = 0;
while (attempts > 0) {
uint8_t *file_buf = buf + UPD_PKT_HDR_SIZE;
size_t file_buf_pos = 0;
fprintf(stderr, "\r%d blocks (%d written, %d skipped)...",
num_blocks_written + num_blocks_skipped,
num_blocks_written, num_blocks_skipped);
fflush(stderr);
_set_fw_update_ongoing(slot_id, 60);
// Read a block of data from file.
while (file_buf_num_bytes < BIOS_UPDATE_BLK_SIZE) {
size_t num_to_read = BIOS_UPDATE_BLK_SIZE - file_buf_num_bytes;
ssize_t num_read = read(fd, file_buf, num_to_read);
if (num_read < 0) {
if (errno == EINTR) {
continue;
}
fprintf(stderr, "read error: %d\n", errno);
goto out;
}
if (num_read == 0) {
break;
}
file_buf_num_bytes += num_read;
}
// Finished.
if (file_buf_num_bytes == 0) {
break;
}
// Pad to 64K with 0xff, if needed.
for (size_t i = file_buf_num_bytes; i < BIOS_UPDATE_BLK_SIZE; i++) {
file_buf[i] = '\xff';
}
// Check if we need to write this block at all.
if (dedup || verify) {
if (cs_len == STRONG_DIGEST_LENGTH) {
rc = calc_checksum_sha256(file_buf, BIOS_UPDATE_BLK_SIZE, fcs);
} else {
rc = calc_checksum_simple(file_buf, BIOS_VERIFY_PKT_SIZE, fcs);
if (rc == 0) {
rc = calc_checksum_simple(file_buf + BIOS_VERIFY_PKT_SIZE,
BIOS_VERIFY_PKT_SIZE, fcs + SIMPLE_DIGEST_LENGTH);
}
}
if (rc != 0) {
fprintf(stderr, "calc_checksum error: %d (cs_len %d)\n", rc, cs_len);
goto out;
}
}
if (dedup) {
rc = get_block_checksum(slot_id, write_offset, cs_len, cs);
if (rc == 0 && memcmp(cs, fcs, cs_len) == 0) {
write_offset += BIOS_UPDATE_BLK_SIZE;
file_buf_num_bytes = 0;
num_blocks_skipped++;
attempts = NUM_ATTEMPTS;
continue;
}
}
int rc = 0;
while (file_buf_pos < file_buf_num_bytes) {
size_t count = file_buf_num_bytes - file_buf_pos;
// Large packets and SHA256 checksums were added together
// so if we have SHA256 checksum, we can use large packets as well.
size_t limit = (cs_len == STRONG_DIGEST_LENGTH ?
IPMB_BIOS_WRITE_COUNT_MAX : IPMB_WRITE_COUNT_MAX);
if (count > limit) count = limit;
struct bic_update_fw_req *pkt = (struct bic_update_fw_req *) (
file_buf + file_buf_pos - UPD_PKT_HDR_SIZE);
pkt->iana_id[0] = 0x15;
pkt->iana_id[1] = 0xa0;
pkt->iana_id[2] = 0x00;
pkt->target = UPDATE_BIOS;
pkt->offset = write_offset + file_buf_pos;
pkt->length = count;
uint8_t rbuf[16];
uint8_t rlen = 0;
rc = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_UPDATE_FW,
(uint8_t *) pkt, UPD_PKT_HDR_SIZE + count, rbuf, &rlen);
if (rc < 0) {
fprintf(stderr, "failed to write %d bytes @ %d: %d\n", count, write_offset, rc);
attempts--;
break;
}
file_buf_pos += count;
}
if (rc < 0) {
continue;
}
// Verify written data.
if (verify) {
rc = get_block_checksum(slot_id, write_offset, cs_len, cs);
if (rc != 0) {
fprintf(stderr, "get_block_checksum @ %d failed (cs_len %d)\n", write_offset, cs_len);
attempts--;
continue;
}
if (memcmp(cs, fcs, cs_len) != 0) {
fprintf(stderr, "Data checksum mismatch @ %d (cs_len %d, 0x%016llx vs 0x%016llx)\n",
write_offset, cs_len, *((uint64_t *) cs), *((uint64_t *) fcs));
attempts--;
continue;
}
}
write_offset += BIOS_UPDATE_BLK_SIZE;
file_buf_num_bytes = 0;
num_blocks_written++;
attempts = NUM_ATTEMPTS;
}
if (attempts == 0) {
fprintf(stderr, "failed.\n");
goto out;
}
fprintf(stderr, "finished.\n");
ret = 0;
out:
free(buf);
return ret;
}
static int
bic_update_firmware_path_or_fd(uint8_t slot_id, uint8_t comp, char *path, int fd, uint8_t force) {
int ret = -1, rc;
uint32_t offset;
volatile uint32_t count, read_count;
uint8_t buf[BIOS_UPDATE_BLK_SIZE] ={0};
uint8_t target;
uint8_t slot_num = 0, dev_id = 0;
char fdstr[32] = {0};
bool fd_opened = false;
int i;
struct stat st;
if (path == NULL) {
if (fd < 0) {
syslog(LOG_ERR, "%s(): Update aborted due to NULL pointer: *path", __func__);
return -1;
}
snprintf(fdstr, sizeof(fdstr) - 1, "<%d>", fd);
path = fdstr;
} else {
fd = open(path, O_RDONLY, 0);
if (fd < 0) {
syslog(LOG_ERR, "%s(): Unable to open %s: %d", __func__, path, errno);
return -1;
}
fd_opened = true;
}
fstat(fd, &st);
fprintf(stderr, "updating fw on slot %d:\n", slot_id);
// Handle Bridge IC firmware separately as the process differs significantly from others
if (comp == UPDATE_BIC) {
ret = _update_bic_main(slot_id, path, force);
goto out;
} else if (comp == UPDATE_SPH) {
slot_num = (slot_id >> 4);
dev_id = (slot_id & 0xF);
slot_id = slot_num;
syslog(LOG_CRIT, "Update Intel: update intel sph firmware on slot %d device %d\n", slot_id, dev_id);
ret = update_dev_firmware(slot_num, dev_id, path);
goto out;
} else if (comp == UPDATE_BIOS) {
if (path != NULL && !force && check_bios_image(slot_id, fd, st.st_size) < 0) {
fprintf(stderr, "invalid BIOS file!\n");
goto out;
}
syslog(LOG_CRIT, "Update BIOS: update bios firmware on slot %d\n", slot_id);
ret = bic_update_bios_firmware(slot_id, fd);
goto out;
}
uint32_t dsize, last_offset, block_offset;
if (comp == UPDATE_VR) {
if (check_vr_image(slot_id, fd, st.st_size) < 0) {
printf("invalid VR file!\n");
goto out;
}
syslog(LOG_CRIT, "Update VR: update vr firmware on slot %d\n", slot_id);
dsize = st.st_size/5;
} else if (comp == UPDATE_PCIE_SWITCH) {
if (_reset_pcie_sw_update_status(slot_id) != 0) {
goto out;
}
syslog(LOG_CRIT, "Update PCIE SWITCH: update pcie_sw firmware on slot %d\n", slot_id);
if (st.st_size/100 < 100)
dsize = st.st_size;
else
dsize = st.st_size/100;
} else {
if ((comp == UPDATE_CPLD) && (check_cpld_image(slot_id, fd, st.st_size) < 0)) {
printf("invalid CPLD file!\n");
goto out;
}
switch(comp){
case UPDATE_CPLD:
syslog(LOG_CRIT, "Update CPLD: update cpld firmware on slot %d\n", slot_id);
break;
case UPDATE_BIC_BOOTLOADER:
syslog(LOG_CRIT, "Update BIC BL: update bic bootloader firmware on slot %d\n", slot_id);
break;
}
dsize = st.st_size/20;
}
// Write chunks of binary data in a loop
offset = 0;
last_offset = 0;
block_offset = 0;// for pcie switch update
i = 1;
int last_tunk_pcie_sw_flag = 0;
while (1) {
if (comp == UPDATE_PCIE_SWITCH) {
if (i%5 == 0) {
//last trunk at a block
read_count = ((offset + LAST_FW_PCIE_SWITCH_TRUNK_SIZE) <= st.st_size) ? LAST_FW_PCIE_SWITCH_TRUNK_SIZE : (st.st_size - offset);
last_tunk_pcie_sw_flag = 1;
} else {
last_tunk_pcie_sw_flag = ((offset + IPMB_WRITE_COUNT_MAX) < st.st_size) ? 0 : 1;
if (last_tunk_pcie_sw_flag) {
read_count = st.st_size - offset;
} else {
read_count = IPMB_WRITE_COUNT_MAX;
}
}
i++;
} else {
read_count = IPMB_WRITE_COUNT_MAX;
}
// Read from file
count = read(fd, buf, read_count);
if (count <= 0 || count > read_count) {
break;
}
if (comp == UPDATE_PCIE_SWITCH){
// For PCIE switch update, the last trunk of a block is indicated by extra flag
if (last_tunk_pcie_sw_flag) {
target = comp | 0x80;
} else {
target = comp;
}
} else if ((offset + count) >= st.st_size) {
// For non-BIOS update, the last packet is indicated by extra flag
target = comp | 0x80;
} else {
target = comp;
}
if (comp == UPDATE_PCIE_SWITCH) {
rc = _update_pcie_sw_fw(slot_id, target, block_offset, count, st.st_size, buf);
} else {
// Send data to Bridge-IC
rc = _update_fw(slot_id, target, offset, count, buf);
}
if (rc) {
goto out;
}
// Update counter
offset += count;
if ((last_offset + dsize) <= offset) {
switch(comp) {
case UPDATE_CPLD:
printf("\ruploaded cpld: %d %%", offset/dsize*5);
break;
case UPDATE_VR:
printf("\rupdated vr: %d %%", offset/dsize*20);
break;
case UPDATE_PCIE_SWITCH:
_set_fw_update_ongoing(slot_id, 60);
if (st.st_size/100 < 100)
printf("\rupdated pcie switch: %d %%", offset/dsize*100);
else
printf("\rupdated pcie switch: %d %%", offset/dsize);
break;
default:
printf("\rupdated bic boot loader: %d %%", offset/dsize*5);
break;
}
fflush(stdout);
last_offset += dsize;
}
//wait for writing to pcie switch
if (comp == UPDATE_PCIE_SWITCH && (target & 0x80) ) {
uint8_t status[2] = {0};
int j = 0;
block_offset = offset; //update block offset
for (j=0;j<PCIE_SW_MAX_RETRY;j++) {
rc = _get_pcie_sw_update_status(slot_id,status);
if (rc) {
syslog(LOG_WARNING,"_get_pcie_sw_update_status status: %d %d",status[0],status[1]);
goto out;
}
if (status[0] == FW_PCIE_SWITCH_DLSTAT_INPROGRESS || status[0] == FW_PCIE_SWITCH_DLSTAT_READY) {
// check background status
if ( status[1] == FW_PCIE_SWITCH_STAT_INPROGRESS || status[1] == FW_PCIE_SWITCH_STAT_IDLE ) {
// do nothing
} else if (status[1] == FW_PCIE_SWITCH_STAT_DONE) {
// In the middle of FW upfdate, wait for download status done
break;
} else {
_terminate_pcie_sw_update(slot_id);
syslog(LOG_WARNING,"_get_pcie_sw_update_status status: %d %d",status[0],status[1]);
goto out;
}
} else if (status[0] == FW_PCIE_SWITCH_DLSTAT_COMPLETES
|| status[0] == FW_PCIE_SWITCH_DLSTAT_SUCESS_FIRM_ACT
|| status[0] == FW_PCIE_SWITCH_DLSTAT_SUCESS_DATA_ACT) {
// check background status
if ( status[1] == FW_PCIE_SWITCH_STAT_INPROGRESS) {
// do nothing
} else if (status[1] == FW_PCIE_SWITCH_STAT_IDLE || status[1] == FW_PCIE_SWITCH_STAT_DONE) {
// At the end of FW update, after done then chenage to idle
break;
} else {
_terminate_pcie_sw_update(slot_id);
syslog(LOG_WARNING,"_get_pcie_sw_update_status status: %d %d",status[0],status[1]);
goto out;
}
} else {
_terminate_pcie_sw_update(slot_id);
syslog(LOG_WARNING,"_get_pcie_sw_update_status status: %d %d",status[0],status[1]);
goto out;
}
msleep(100);
}
if (j == PCIE_SW_MAX_RETRY) {
_terminate_pcie_sw_update(slot_id);
syslog(LOG_WARNING,"_get_pcie_sw_update_status retry == %d status: %d %d",PCIE_SW_MAX_RETRY,status[0],status[1]);
goto out;
}
if (offset == st.st_size) {
_terminate_pcie_sw_update(slot_id);
}
}
}
if (comp == UPDATE_CPLD) {
printf("\n");
for (i = 0; i < 60; i++) { // wait 60s at most
rc = _get_cpld_update_progress(slot_id, buf);
if (rc) {
goto out;
}
if (buf[0] == 0xFD) { // error code
goto out;
}
printf("\rupdated cpld: %d %%", buf[0]);
fflush(stdout);
buf[0] %= 101;
if (buf[0] == 100)
break;
sleep(2);
}
}
ret = 0;
out:
printf("\n");
switch(comp) {
case UPDATE_BIOS:
syslog(LOG_CRIT, "Update BIOS: updating bios firmware is exiting on slot %d\n", slot_id);
break;
case UPDATE_CPLD:
syslog(LOG_CRIT, "Update CPLD: updating cpld firmware is exiting on slot %d\n", slot_id);
break;
case UPDATE_VR:
syslog(LOG_CRIT, "Update VR: updating vr firmware is exiting on slot %d\n", slot_id);
break;
case UPDATE_BIC_BOOTLOADER:
syslog(LOG_CRIT, "Update BIC BL: updating bic bootloader firmware is exiting on slot %d\n", slot_id);
break;
case UPDATE_PCIE_SWITCH:
syslog(LOG_CRIT, "Update PCIE SWITCH: updating pcie switch firmware is exiting on slot %d\n", slot_id);
break;
case UPDATE_SPH:
syslog(LOG_CRIT, "Update Intel: updating intel sph device firmware is exiting on slot %d device %d\n", slot_id, dev_id);
break;
}
if (fd_opened) {
close(fd);
}
return ret;
}
int
bic_update_firmware(uint8_t slot_id, uint8_t comp, char *path, uint8_t force) {
return bic_update_firmware_path_or_fd(slot_id, comp, path, -1, force);
}
int
bic_update_firmware_fd(uint8_t slot_id, uint8_t comp, int fd, uint8_t force) {
return bic_update_firmware_path_or_fd(slot_id, comp, NULL, fd, force);
}
int
bic_update_fw(uint8_t slot_id, uint8_t comp, char *path) {
return bic_update_firmware_path_or_fd(slot_id, comp, path, -1, 0 /* force */);
}
int
bic_imc_xmit(uint8_t slot_id, uint8_t *txbuf, uint8_t txlen, uint8_t *rxbuf, uint8_t *rxlen) {
uint8_t tbuf[256] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[256] = {0x00};
uint8_t rlen = 0;
uint8_t tlen = 0;
int ret;
// Fill the interface number as IMC
tbuf[3] = BIC_INTF_IMC;
// Fill the data to be sent
memcpy(&tbuf[4], txbuf, txlen);
// Send data length includes IANA ID and interface number
tlen = txlen + 4;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, tbuf, tlen, rbuf, &rlen);
if (ret ) {
return -1;
}
// Make sure the received interface number is same
if (rbuf[3] != tbuf[3]) {
return -1;
}
// Copy the received data to caller skipping header
memcpy(rxbuf, &rbuf[4], rlen-4);
*rxlen = rlen-4;
return 0;
}
int
bic_me_xmit(uint8_t slot_id, uint8_t *txbuf, uint8_t txlen, uint8_t *rxbuf, uint8_t *rxlen) {
uint8_t tbuf[256] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[256] = {0x00};
uint8_t rlen = 0;
uint8_t tlen = 0;
int ret;
// Fill the interface number as ME
tbuf[3] = BIC_INTF_ME;
// Fill the data to be sent
memcpy(&tbuf[4], txbuf, txlen);
// Send data length includes IANA ID and interface number
tlen = txlen + 4;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, tbuf, tlen, rbuf, &rlen);
if (ret ) {
return -1;
}
// Make sure the received interface number is same
if (rbuf[3] != tbuf[3]) {
return -1;
}
// Copy the received data to caller skipping header
memcpy(rxbuf, &rbuf[6], rlen-6);
*rxlen = rlen-6;
return 0;
}
// Read 1S server's FRUID
int
bic_get_fruid_info(uint8_t slot_id, uint8_t fru_id, ipmi_fruid_info_t *info) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_FRUID_INFO, &fru_id, 1, (uint8_t *) info, &rlen);
return ret;
}
static int
_read_fruid(uint8_t slot_id, uint8_t fru_id, uint32_t offset, uint8_t count, uint8_t *rbuf, uint8_t *rlen) {
int ret;
uint8_t tbuf[4] = {0};
tbuf[0] = fru_id;
tbuf[1] = offset & 0xFF;
tbuf[2] = (offset >> 8) & 0xFF;
tbuf[3] = count;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_READ_FRUID_DATA, tbuf, 4, rbuf, rlen);
return ret;
}
int
bic_read_fruid(uint8_t slot_id, uint8_t fru_id, const char *path, int *fru_size) {
int ret = 0;
uint32_t nread;
uint32_t offset;
uint8_t count;
uint8_t rbuf[256] = {0};
uint8_t rlen = 0;
int fd;
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) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_fruid: open fails for path: %s\n", path);
#endif
goto error_exit;
}
// Read the FRUID information
ret = bic_get_fruid_info(slot_id, fru_id, &info);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_fruid: bic_read_fruid_info returns %d\n", ret);
#endif
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);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_fruid: ipmb_wrapper fails\n");
#endif
goto error_exit;
}
msleep(10);
// Ignore the first byte as it indicates length of response
if (write(fd, &rbuf[1], rlen-1) < (rlen-1)) {
syslog(LOG_WARNING, "%s: write file failed, file: %s\n", __func__, path);
goto error_exit;
}
// Update offset
offset += (rlen-1);
nread -= (rlen-1);
}
close(fd);
return ret;
error_exit:
if (fd > 0 ) {
close(fd);
}
return -1;
}
static int
_write_fruid(uint8_t slot_id, uint8_t fru_id, uint32_t offset, uint8_t count, uint8_t *buf) {
int ret;
uint8_t tbuf[64] = {0};
uint8_t rbuf[4] = {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_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_WRITE_FRUID_DATA, tbuf, tlen, rbuf, &rlen);
if (ret) {
return ret;
}
if (rbuf[0] != count) {
return -1;
}
return ret;
}
int
bic_write_fruid(uint8_t slot_id, uint8_t fru_id, const char *path) {
int ret = -1;
uint32_t offset;
uint8_t count;
uint8_t buf[64] = {0};
int fd;
// Open the file exclusively for read
fd = open(path, O_RDONLY, 0666);
if (fd < 0) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_write_fruid: open fails for path: %s\n", path);
#endif
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);
if (ret) {
break;
}
msleep(10);
// Update counter
offset += count;
}
error_exit:
if (fd > 0 ) {
close(fd);
}
return ret;
}
// Read System Event Log (SEL)
int
bic_get_sel_info(uint8_t slot_id, ipmi_sel_sdr_info_t *info) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SEL_INFO, NULL, 0, (uint8_t *)info, &rlen);
return ret;
}
int
bic_get_sel(uint8_t slot_id, ipmi_sel_sdr_req_t *req, ipmi_sel_sdr_res_t *res, uint8_t *rlen) {
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SEL, (uint8_t *)req, sizeof(ipmi_sel_sdr_req_t), (uint8_t*)res, rlen);
return ret;
}
// Read Sensor Data Records (SDR)
int
bic_get_sdr_info(uint8_t slot_id, ipmi_sel_sdr_info_t *info) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SDR_INFO, NULL, 0, (uint8_t *) info, &rlen);
return ret;
}
static int
_get_sdr_rsv(uint8_t slot_id, uint8_t *rsv) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_RSV_SDR, NULL, 0, (uint8_t *) rsv, &rlen);
return ret;
}
static int
_get_sdr(uint8_t slot_id, ipmi_sel_sdr_req_t *req, ipmi_sel_sdr_res_t *res, uint8_t *rlen) {
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SDR, (uint8_t *)req, sizeof(ipmi_sel_sdr_req_t), (uint8_t*)res, rlen);
return ret;
}
int
bic_get_sdr(uint8_t slot_id, ipmi_sel_sdr_req_t *req, ipmi_sel_sdr_res_t *res, uint8_t *rlen) {
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);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_sdr: _get_sdr_rsv returns %d\n", ret);
#endif
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);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_sdr: _get_sdr returns %d\n", ret);
#endif
return ret;
}
#ifdef DEBUG
syslog(LOG_DEBUG, "rsv_id: 0x%x, rec_id: 0x%x, offset: 0x%x, nbytes: %d\n", req->rsv_id, req->rec_id, req->offset, req->nbytes);
#endif
if ((tlen - 2) != req->nbytes) { // subtract the first two bytes(rsv_id) length from tlen and we will have the expected data length
syslog(LOG_WARNING, "%s: SLOT%u SDR rsv_id: 0x%x, record_id: 0x%x, offset: 0x%x. Received Data Length does not match (expectative: 0x%x, actual: 0x%x)",
__func__, slot_id, req->rsv_id, req->rec_id, req->offset, req->nbytes, tlen - 2);
return -1;
}
// 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 request
*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);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_sdr: _get_sdr returns %d\n", ret);
#endif
return ret;
}
#ifdef DEBUG
syslog(LOG_DEBUG, "rsv_id: 0x%x, rec_id: 0x%x, offset: 0x%x, nbytes: %d\n", req->rsv_id, req->rec_id, req->offset, req->nbytes);
#endif
if ((tlen - 2) != req->nbytes) {
syslog(LOG_WARNING, "%s: SLOT%u SDR rsv_id: 0x%x, record_id: 0x%x, offset: 0x%x. Received Data Length does not match (expectative: 0x%x, actual: 0x%x)",
__func__, slot_id, req->rsv_id, req->rec_id, req->offset, req->nbytes, tlen - 2);
continue;
}
// 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;
}
if (*rlen == 0) {
syslog(LOG_ERR, "%s: SLOT%u - SDR size is zero\n", __func__, slot_id);
}
return 0;
}
int
bic_read_sensor(uint8_t slot_id, uint8_t sensor_num, ipmi_sensor_reading_t *sensor) {
int ret;
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_SENSOR_REQ, CMD_SENSOR_GET_SENSOR_READING, (uint8_t *)&sensor_num, 1, (uint8_t *)sensor, &rlen);
return ret;
}
int
bic_read_device_sensors(uint8_t slot_id, uint8_t dev_id, ipmi_device_sensor_reading_t *sensor, uint8_t *len) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00, 0x00}; // IANA ID + Sensor Num
uint8_t rbuf[255] = {0x00};
uint8_t rlen = 0;
int ret;
tbuf[3] = dev_id;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_DEVICE_SENSOR_READING, tbuf, 0x04, rbuf, &rlen);
if (rlen >= DEV_SENSOR_INFO_LEN + 3) { // at least one sensor
memcpy(sensor, &rbuf[3], rlen-3); // Ignore IANA ID
*len = rlen - 3;
} else {
*len = 0;
return -1; // unavailable
}
return ret;
}
int
bic_read_accuracy_sensor(uint8_t slot_id, uint8_t sensor_num, ipmi_accuracy_sensor_reading_t *sensor) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00, 0x00}; // IANA ID + Sensor Num
uint8_t rbuf[255] = {0x00};
uint8_t rlen = 0;
int ret;
tbuf[3] = sensor_num;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_ACCURACY_SENSOR_READING, tbuf, 0x04, rbuf, &rlen);
if (rlen == 6) {
memcpy(sensor, &rbuf[3], rlen-3); // Ignore IANA ID
} else {
sensor->flags = 0x20; // unavailable
}
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_request_post_buffer_data(uint8_t slot_id, uint8_t *port_buff, uint8_t *len) {
int ret;
uint8_t tbuf[3] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[MAX_IPMB_RES_LEN]={0x00};
uint8_t rlen = 0;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_BUF, tbuf, 0x03, rbuf, &rlen);
if(0 != ret)
goto exit_done;
// Ignore first 3 bytes of IANA ID
memcpy(port_buff, &rbuf[3], rlen - 3);
*len = rlen - 3;
exit_done:
return ret;
}
int
bic_request_post_buffer_page_data(uint8_t slot_id, uint8_t page_num, uint8_t *port_buff, uint8_t *len) {
int ret;
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[MAX_IPMB_RES_LEN]={0x00};
uint8_t rlen = 0;
tbuf[3] = page_num & 0xFF;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_CODE_BUF, tbuf, 0x04, rbuf, &rlen);
if(0 != ret)
goto exit_done;
// Ignore first 3 bytes of IANA ID
memcpy(port_buff, &rbuf[3], rlen - 3);
*len = rlen - 3;
exit_done:
return ret;
}
int
bic_request_post_buffer_dword_data(uint8_t slot_id, uint32_t *port_buff, uint32_t input_len, uint32_t *output_len) {
int ret = 0;
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[MAX_IPMB_RES_LEN]={0x00};
uint8_t rlen = 0;
int totol_length = 0;
for(int i = 1; i < MAX_POST_CODE_PAGE; i++)
{
tbuf[3] = i & 0xFF;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_CODE_BUF, tbuf, 0x04, rbuf, &rlen);
if(0 != ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_get_post_code_buf error, ret:%d", ret);
#endif
ret = -1;
return ret;
}
// Ignore first 3 bytes of IANA ID
for(int k = 3; (k < rlen-3) && (totol_length < input_len); k+=(sizeof(uint32_t)/sizeof(uint8_t)))
{
port_buff[totol_length] = rbuf[k] | (rbuf[k+1] << 8) | (rbuf[k+2] << 16) | (rbuf[k+3] << 24);
totol_length++;
}
}
*output_len = totol_length;
return ret;
}
/*
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) {
//ND system does not have me
#ifndef CONFIG_FBY2_ND
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 <= 3) {
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 == 4) { //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 <= 3) {
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 == 4) { //if the third retry still failed, return -1
syslog(LOG_CRIT, "%s: Restore Factory Default failed..., retried: %d", __func__, retry);
return -1;
}
#endif
return 0;
}
int
bic_get_slot_type(uint8_t fru) {
int type = 3; //set default to 3(Empty Slot)
int retry = 3;
char key[MAX_KEY_LEN] = {0};
if ((fru < FRU_SLOT1) || (fru > FRU_SLOT4))
return type;
snprintf(key, sizeof(key), SLOT_FILE, fru);
do {
if (read_device(key, &type) == 0)
break;
syslog(LOG_WARNING,"bic_get_slot_type failed");
msleep(10);
} while (--retry);
return type;
}
int
bic_get_record_slot_type(uint8_t fru) {
int type = 3; //set default to 3(Empty Slot)
int retry = 3;
char key[MAX_KEY_LEN] = {0};
if ((fru < FRU_SLOT1) || (fru > FRU_SLOT4))
return type;
snprintf(key, sizeof(key), SLOT_RECORD_FILE, fru);
do {
if (read_device(key, &type) == 0)
break;
syslog(LOG_WARNING,"bic_get_record_slot_type failed");
msleep(10);
} while (--retry);
return type;
}
int
bic_set_slot_type(uint8_t fru,uint8_t type) {
int retry = 3;
char key[MAX_KEY_LEN] = {0};
char value[MAX_VALUE_LEN] = {0};
if ((fru < FRU_SLOT1) || (fru > FRU_SLOT4))
return type;
snprintf(key, sizeof(key), SLOT_FILE, fru);
snprintf(value, sizeof(value),"%d", type);
do {
if (write_device(key, value) == 0)
break;
syslog(LOG_WARNING,"bic_set_slot_type failed");
msleep(10);
} while (--retry);
return 0;
}
int
bic_get_server_type(uint8_t fru, uint8_t *type) {
int ret;
int retries = 3;
int server_type = SERVER_TYPE_NONE;
ipmi_dev_id_t id = {0};
char key[MAX_KEY_LEN] = {0};
if ((fru < FRU_SLOT1) || (fru > FRU_SLOT4)) {
*type = SERVER_TYPE_NONE;
return 0;
}
// SERVER_TYPE = 0(TwinLake), 1(RC), 2(EP), 3(unknown)
sprintf(key, SERVER_TYPE_FILE, fru);
do {
if (read_device(key, &server_type) == 0)
break;
syslog(LOG_WARNING,"bic_get_server_type failed");
msleep(10);
} while (--retries);
if (retries == 0) {
retries = 3;
do {
ret = bic_get_dev_id(fru, &id);
if (!ret) {
// Use product ID to identify the server type
if (id.prod_id[0] == 0x39 && id.prod_id[1] == 0x30) {
*type = SERVER_TYPE_TL;
} else if (id.prod_id[0] == 0x44 && id.prod_id[1] == 0x4E) {
*type = SERVER_TYPE_ND;
} else {
*type = SERVER_TYPE_NONE;
}
break;
}
} while ((--retries));
if (retries == 0) {
*type = SERVER_TYPE_NONE;
syslog(LOG_ERR, "%s : Get server type failed for slot%u", __func__, fru);
return -1;
}
} else {
*type = server_type;
}
return 0;
}
int
bic_asd_init(uint8_t slot_id, uint8_t cmd) {
uint8_t tbuf[8] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0x00};
uint8_t rlen = 0;
int ret;
tbuf[3] = cmd;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_ASD_INIT, tbuf, 4, rbuf, &rlen);
if (ret != CC_SUCCESS) {
ret = -1;
}
return ret;
}
int
bic_clear_cmos(uint8_t slot_id) {
uint8_t tbuf[3] = {0x15, 0xa0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0x00};
uint8_t rlen = 0;
return bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_CLEAR_CMOS, tbuf, 3, rbuf, &rlen);
}
int
bic_reset(uint8_t slot_id) {
uint8_t tbuf[3] = {0x00, 0x00, 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;
}
int
bic_set_pcie_config(uint8_t slot_id, uint8_t config) {
uint8_t tbuf[4] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rlen = 0;
uint8_t rbuf[16] = {0};
int ret;
if (bic_is_slot_power_en(slot_id)) {
return 0;
}
tbuf[3] = config;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_PCIE_CONFIG, tbuf, 0x04, rbuf, &rlen);
return ret;
}
int get_imc_version(uint8_t slot, uint8_t *ver) {
int i;
int ret;
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
sprintf(key, "slot%d_imc_ver", (int)slot);
ret = kv_get(key, str, NULL, KV_FPERSIST);
if (ret) {
return ret;
}
for (i = 0; i < IMC_VER_SIZE; i++) {
ver[i] = str[i] - '0';
}
return 0;
}
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_disable_sensor_monitor(uint8_t slot_id, uint8_t dis) {
uint8_t tbuf[8] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0x00};
uint8_t rlen = 0;
int ret;
tbuf[3] = dis; // 1: disable sensor monitor; 0: enable sensor monitor
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_DISABLE_SEN_MON, tbuf, 4, rbuf, &rlen);
return ret;
}
int
bic_send_jtag_instruction(uint8_t slot_id, uint8_t dev_id, uint8_t *rbuf, uint8_t ir) {
uint8_t tbuf[8] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rlen = 0;
int ret;
tbuf[3] = dev_id; // 0-based
tbuf[4] = ir;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, 0x39, tbuf, 5, rbuf, &rlen);
return ret;
}
int
bic_get_debug_mode(uint8_t slot_id, uint8_t *debug_mode) {
uint8_t tbuf[8] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[8] = {0}; // IANA ID
uint8_t rlen = 0;
int ret;
tbuf[3] = 1; // 0: write 1: read
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, 0x3C, tbuf, 4, rbuf, &rlen);
if ((ret == 0) && (rlen == 4)){
*debug_mode = rbuf[3];
} else {
ret = -1;
}
return ret;
}
int
bic_set_sdr_threshold_update_flag(uint8_t slot, uint8_t update) {
char key[MAX_KEY_LEN] = {0};
char str[MAX_VALUE_LEN] = {0};
snprintf(key,MAX_KEY_LEN, "slot%u_sdr_thresh_update", slot);
snprintf(str,MAX_VALUE_LEN, "%u",update);
return kv_set(key, str, 0, 0);
}
int
bic_get_sdr_threshold_update_flag(uint8_t slot) {
int ret;
char key[MAX_KEY_LEN] = {0};
char cvalue[MAX_VALUE_LEN] = {0};
sprintf(key, "slot%u_sdr_thresh_update", slot);
ret = kv_get(key, cvalue, NULL,0);
if (ret) {
return 0;
}
return atoi(cvalue);
}
int
bic_get_device_type(uint8_t slot_id, uint8_t dev_num) {
int ret = 0;
int rlen = 0;
uint8_t bus, wbuf[8], rbuf[64], fb_defined, ffi_0, offset_base = 0;
bus = (2 + (dev_num / 2)) * 2 + 1;
wbuf[0] = 1 << (dev_num % 2);
ret = bic_master_write_read(slot_id, bus, 0xe2, wbuf, 1, rbuf, 0);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read failed", __func__);
return ret;
}
wbuf[0] = 0x20; // offset 32
rlen = 55;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read offset=%d read length=%d failed", __func__,wbuf[0],rlen);
return -1;
}
if (rbuf[0] == wbuf[0]) {
offset_base = SPRINGHILL_M2_OFFSET_BASE;
}
fb_defined = rbuf[offset_base + 1];
ffi_0 = rbuf[offset_base + 43];
if (fb_defined == 1) {
if (ffi_0 == 0x01) {
return DEV_TYPE_ASIC;
} else {
return DEV_TYPE_M_2;
}
} else {
return DEV_TYPE_M_2;
}
}
int reverse_bits(int raw_val)
{
int c, reverse_val = 0;
for (c = 7; c > 0; c--) {
if (raw_val & 1) {
reverse_val++;
}
reverse_val = reverse_val << 1;
raw_val = raw_val >> 1;
}
if(raw_val & 1) {
reverse_val++;
}
return reverse_val;
}
int program_dev_fw(uint8_t slot_id, uint8_t dev_id, int bus, char *image, int start, int end) {
uint8_t wbuf[DEV_UPDATE_BATCH_SIZE + DEV_UPDATE_IPMI_HEAD_SIZE + 2]; //2 means buffer for ipmb
uint8_t rlen = 0;
uint8_t rbuf[DEV_UPDATE_BATCH_SIZE];
FILE *fp1 = NULL;
unsigned char buffer[DEV_UPDATE_BATCH_SIZE];
int addr = 0, tmp, i, ret = 0;
float cur_progress = 0.05;
float run = 1;
int total_run;
int fileSize;
printf("* Reading image...\n");
addr = start;
wbuf[0] = bus;
wbuf[1] = PROGRAM_DEV_FW;
fp1 = fopen(image, "rb");
if (!fp1){
printf("Image %s not found", image);
return -1;
}
fseek(fp1, 0 , SEEK_END);
fileSize = ftell(fp1);
fseek(fp1, 0 , SEEK_SET);
total_run = fileSize / DEV_UPDATE_BATCH_SIZE;
if ((fileSize / 4) > (end - start + 1)) {
printf("File size too large\n");
fclose(fp1);
return -1;
}
while(fread(buffer, DEV_UPDATE_BATCH_SIZE, 1, fp1)) {
tmp = addr;
for(i = DEV_UPDATE_IPMI_HEAD_SIZE - 1; i > 1; i--) { //offset is 0-based
wbuf[i] = (tmp & 0xFF);
tmp /= 0x100;
}
for(i = 0; i < DEV_UPDATE_BATCH_SIZE; i++) {
wbuf[i + DEV_UPDATE_IPMI_HEAD_SIZE] = reverse_bits(buffer[i]);
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_STORAGE_REQ, DEV_UPDATE, wbuf,
(DEV_UPDATE_BATCH_SIZE + DEV_UPDATE_IPMI_HEAD_SIZE), rbuf, &rlen);
if (ret < 0) {
printf("Failed to send IPMB command!\n");
goto err_exit;
}
if (rbuf[0] == CC_CAN_NOT_RESPOND) {
printf("Command response could not be provided\n");
fclose(fp1);
return -1;
}
addr += DEV_UPDATE_BATCH_SIZE / 4;
if (((float)(run/total_run) >= cur_progress) && ((float)((run-1)/total_run) <= cur_progress)) {
printf("updated device: %d %%\n", (int)(cur_progress*100));
cur_progress += 0.05;
}
run++;
}
printf("updated device: 100 %%\n");
err_exit:
fclose(fp1);
return ret;
}
/* Below IPMI command support to device fw update
Request:
Byte 1 – I2C Bus Number (0-based)
Byte 2 – Device Upgrade Option
00h: Erase device sector
01h: Programming the device image
02h: Reload device image
03h: Get the image location of device booting
Byte 3:6 – Image Offset
only for Byte 1 = 01h
Byte 7:N – Image Raw Data
only for Byte 1 = 01h
Response:
Byte 1 – Completion Code
00h: Success
CEh: Command response could not be provided
Byte 2 – device Command Status
only for Byte 1 = 00h or 03h
*/
int update_dev_firmware (uint8_t slot_id, uint8_t dev_id, char* image) {
uint8_t wbuf[2];
uint8_t rlen = 0;
uint8_t rbuf[DEV_UPDATE_BATCH_SIZE];
uint8_t bus = (2 + dev_id/2) * 2 + 1;
int ret = 0;
printf("* Turning off sensor monitor...\n");
ret = bic_disable_sensor_monitor(slot_id, 1);
if (ret < 0) {
printf("Turn off slot%u sensor monitor failed\n", slot_id);
return -1;
}
sleep(2);
printf("* Mux selecting...\n");
wbuf[0] = 1 << (dev_id % 2);
ret = bic_master_write_read(slot_id, bus, 0xe2, wbuf, 1, rbuf, 0);
if (ret < 0) {
printf("Select mux failed\n");
return -1;
}
sleep(2);
bus = (dev_id / 2) + 2;
printf("* Erasing Device CFM0 sector...\n");
wbuf[0] = bus;
wbuf[1] = ERASE_DEV_FW;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_STORAGE_REQ, DEV_UPDATE, wbuf, 2, rbuf, &rlen);
if (ret < 0 ) {
printf("Failed to send IPMB command!\n");
return ret;
}
if (rbuf[0] == CC_CAN_NOT_RESPOND) {
printf("Command response could not be provided\n");
return -1;
}
ret = program_dev_fw(slot_id, dev_id, bus, image, CFM0_START, CFM0_END);
if (ret < 0) {
return ret;
}
printf("* Turning on sensor monitor...\n");
ret = bic_disable_sensor_monitor(slot_id, 0);
if (ret < 0) {
printf("Turn on slot%u sensor monitor failed\n", slot_id);
return ret;
}
return ret;
}
int
bic_fget_device_info(uint8_t slot_id, uint8_t dev_num, uint8_t *ffi, uint8_t *meff, uint16_t *vendor_id, uint8_t *major_ver, uint8_t *minor_ver) {
int ret = 0;
int rlen = 0;
uint8_t bus, wbuf[8], rbuf[64];
dev_num = dev_num - 1;
bus = (2 + (dev_num / 2)) * 2 + 1;
ret = bic_disable_sensor_monitor(slot_id, 1);
if (ret < 0) {
printf("Turn off slot%u sensor monitor failed\n", slot_id);
return -1;
}
wbuf[0] = 1 << (dev_num % 2);
ret = bic_master_write_read(slot_id, bus, 0xe2, wbuf, 1, rbuf, 0);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read failed", __func__);
return ret;
}
msleep(50);
wbuf[0] = 0x08; // offset 08
rlen = 24;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read offset=%d read length=%d failed", __func__,wbuf[0],rlen);
return ret;
}
*vendor_id = (rbuf[1] << 8) | rbuf[2];
wbuf[0] = 0x20; // offset 32
rlen = 55;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read offset=%d read length=%d failed", __func__,wbuf[0],rlen);
return ret;
}
*meff = rbuf[42];
*ffi = rbuf[43];
wbuf[0] = 0x68; // offset 104
rlen = 8;
ret = bic_master_write_read(slot_id, bus, 0xd4, wbuf, 1, rbuf, rlen);
if (ret != 0) {
syslog(LOG_DEBUG, "%s(): bic_master_write_read offset=%d read length=%d failed", __func__,wbuf[0],rlen);
return ret;
}
*major_ver = rbuf[2];
*minor_ver = rbuf[3];
ret = bic_disable_sensor_monitor(slot_id, 0);
if (ret < 0) {
printf("Turn on slot%u sensor monitor failed\n", slot_id);
return ret;
}
return ret;
}
void
bic_check_pcie_link(uint8_t fru) {
int ret = 0, slot_id = 0, retry = 0;
uint8_t rlen = 0;
uint8_t rbuf[255] = {0x0};
switch (fru) {
case FRU_SLOT1:
case FRU_SLOT2:
slot_id = FRU_SLOT1;
break;
case FRU_SLOT3:
case FRU_SLOT4:
slot_id = FRU_SLOT3;
break;
default:
return;
}
while (retry < PCIE_LINK_CHECK_MAX_RETRY) {
if (!is_bic_ready(slot_id)) {
sleep(1);
retry++;
} else {
break;
}
}
if (retry == PCIE_LINK_CHECK_MAX_RETRY) {
syslog(LOG_WARNING, "FRU: %d, BIC is not ready to check PCIe link.", slot_id);
return;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_STORAGE_REQ, CHECK_PCIE_LINK, NULL, 0, rbuf, &rlen);
if (ret < 0) {
syslog(LOG_WARNING, "%s: Fail to send IPMB command to slot%d", __func__, slot_id);
return;
}
if (rbuf[0] == CC_SUCCESS) {
syslog(LOG_INFO, "FRU: %d, Checking PCIe link successfully", slot_id);
} else {
syslog(LOG_WARNING, "FRU: %d, Fail to create PCIe link checking thread.", slot_id);
}
}