meta-facebook/meta-yosemite/recipes-yosemite/fblibs/files/bic/bic.c (1,212 lines of code) (raw):
/*
*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This file contains code to support IPMI2.0 Specificaton available @
* http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include "bic.h"
#include <openbmc/kv.h>
#include <openbmc/obmc-i2c.h>
#define FRUID_READ_COUNT_MAX 0x20
#define FRUID_WRITE_COUNT_MAX 0x20
#define IPMB_WRITE_COUNT_MAX 224
#define BIOS_ERASE_PKT_SIZE (64*1024)
#define BIOS_VERIFY_PKT_SIZE (32*1024)
#define SDR_READ_COUNT_MAX 0x1A
#define SIZE_SYS_GUID 16
#define SIZE_IANA_ID 3
#define GPIO_MAX 31
#define BIOS_VER_REGION_SIZE (4*1024*1024)
#define BIOS_VER_STR "F20_"
#define BIC_UPDATE_RETRIES 12
#define BIC_UPDATE_TIMEOUT 500
#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 GPIO_VAL "/sys/class/gpio/gpio%d/value"
enum {
IPMB_BUS_SLOT1 = 3,
IPMB_BUS_SLOT2 = 1,
IPMB_BUS_SLOT3 = 7,
IPMB_BUS_SLOT4 = 5,
};
#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)
const static uint8_t gpio_bic_update_ready[] = { 0, 107, 106, 109, 108 };
// 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
read_device(const char *device, int *value) {
FILE *fp;
int rc;
fp = fopen(device, "r");
if (!fp) {
int err = errno;
#ifdef DEBUG
syslog(LOG_INFO, "failed to open device %s", device);
#endif
return err;
}
rc = fscanf(fp, "%d", value);
fclose(fp);
if (rc != 1) {
#ifdef DEBUG
syslog(LOG_INFO, "failed to read device %s", device);
#endif
return ENOENT;
} else {
return 0;
}
}
static uint8_t
_is_bic_update_ready(uint8_t slot_id) {
int val;
char path[64] = {0};
if (slot_id < 1 || slot_id > 4) {
return 0;
}
sprintf(path, GPIO_VAL, gpio_bic_update_ready[slot_id]);
if (read_device(path, &val)) {
return 0;
}
if (val == 0x0) {
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 1:
bus_id = IPMB_BUS_SLOT1;
break;
case 2:
bus_id = IPMB_BUS_SLOT2;
break;
case 3:
bus_id = IPMB_BUS_SLOT3;
break;
case 4:
bus_id = IPMB_BUS_SLOT4;
break;
default:
bus_id = -1;
break;
}
return bus_id;
}
static int
set_fw_update_ongoing(uint8_t slot_id, uint16_t tmout) {
char key[64];
char value[64];
struct timespec ts;
sprintf(key, "fru%u_fwupd", slot_id);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += tmout;
sprintf(value, "%d", (int) ts.tv_sec);
if (kv_set(key, value, 0, 0) < 0) {
return -1;
}
return 0;
}
int
bic_ipmb_wrapper(uint8_t slot_id, uint8_t netfn, uint8_t cmd,
uint8_t *txbuf, uint8_t txlen,
uint8_t *rxbuf, uint8_t *rxlen) {
ipmb_req_t *req;
ipmb_res_t *res;
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t tbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t tlen = 0;
uint8_t rlen = 0;
int ret;
uint8_t bus_id;
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;
// Invoke IPMB library handler
lib_ipmb_handle(bus_id, tbuf, tlen, rbuf, &rlen);
if (rlen == 0) {
#ifdef DEBUG
syslog(LOG_DEBUG, "bic_ipmb_wrapper: Zero bytes received\n");
#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
return -1;
}
// copy the received data back to caller
*rxlen = rlen - IPMB_HDR_SIZE - IPMI_RESP_HDR_SIZE;
memcpy(rxbuf, res->data, *rxlen);
return 0;
}
// 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;
}
// Get GPIO value and configuration
int
bic_get_gpio(uint8_t slot_id, bic_gpio_t *gpio) {
uint8_t tbuf[3] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[7] = {0x00};
uint8_t rlen = 0;
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO, tbuf, 0x03, rbuf, &rlen);
// Ignore first 3 bytes of IANA ID
memcpy((uint8_t*) gpio, &rbuf[3], 4);
return ret;
}
int
bic_set_gpio(uint8_t slot_id, uint8_t gpio, uint8_t value) {
uint8_t tbuf[11] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[3] = {0x00};
uint8_t rlen = 0;
int ret;
// Check for boundary conditions
if (gpio > GPIO_MAX) {
return -1;
}
// Create the mask bytes for the given GPIO#
if (gpio < 7) {
tbuf[3] = 1 << gpio;
tbuf[4] = 0x00;
tbuf[5] = 0x00;
tbuf[6] = 0x00;
} else if (gpio < 15) {
gpio -= 8;
tbuf[3] = 0x00;
tbuf[4] = 1 << gpio;
tbuf[5] = 0x00;
tbuf[6] = 0x00;
} else if (gpio < 23) {
gpio -= 16;
tbuf[3] = 0x00;
tbuf[4] = 0x00;
tbuf[5] = 1 << gpio;
tbuf[6] = 0x00;
} else {
gpio -= 24;
tbuf[3] = 0x00;
tbuf[4] = 0x00;
tbuf[5] = 0x00;
tbuf[6] = 1 << gpio;
}
// Fill the value
if (value) {
memset(&tbuf[7], 0xFF, 4);
} else {
memset(&tbuf[7] , 0x00, 4);
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO, tbuf, 11, 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[7] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint8_t tlen = 0;
uint32_t pin;
int ret;
pin = 1 << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tlen = 7;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO_CONFIG, tbuf, tlen, 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[8] = {0x15, 0xA0, 0x00}; // IANA ID
uint8_t rbuf[4] = {0x00};
uint8_t rlen = 0;
uint8_t tlen = 0;
uint32_t pin;
int ret;
pin = 1 << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
tbuf[7] = (*(uint8_t *) gpio_config) & 0x1F;
tlen = 8;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_GPIO_CONFIG,
tbuf, tlen, 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_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_FW_VER, tbuf, 0x04, rbuf, &rlen);
// fw version has to be between 2 and 5 bytes based on component
if (ret || (rlen < 2+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 comp, 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;
// Fill the component for which firmware is requested
tbuf[3] = comp;
// 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;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_FW_CKSUM, tbuf, 12, rbuf, &rlen);
// checksum has to be 4 bytes
if (ret || (rlen != 4+SIZE_IANA_ID)) {
syslog(LOG_ERR, "bic_get_fw_cksum: ret: %d, rlen: %d\n", ret, rlen);
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;
}
// 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[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;
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;
}
static int
_update_bic_main(uint8_t slot_id, char *path) {
int fd;
int ifd = -1;
char cmd[100] = {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;
syslog(LOG_CRIT, "bic_update_fw: update bic firmware on slot %d\n", slot_id);
// 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_exit;
}
fstat(fd, &buf);
size = buf.st_size;
printf("size of file is %d bytes\n", size);
dsize = size/20;
// Open the i2c driver
ifd = i2c_open(get_ipmb_bus_id(slot_id));
if (ifd < 0) {
printf("ifd error\n");
goto error_exit;
}
// Kill ipmb daemon for this slot
sprintf(cmd, "sv stop ipmbd_%d", get_ipmb_bus_id(slot_id));
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 1:
system("devmem 0x1e78a104 w 0x77776305");
break;
case 2:
system("devmem 0x1e78a084 w 0x77776305");
break;
case 3:
system("devmem 0x1e78a304 w 0x77776305");
break;
case 4:
system("devmem 0x1e78a184 w 0x77776305");
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_update_ready(slot_id)) {
// 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);
system(cmd);
printf("start ipmbd -u for this slot %x..\n",slot_id);
sleep(2);
// Enable Bridge-IC update
_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));
system(cmd);
printf("stop ipmbd for slot %x..\n", slot_id);
}
// Wait for SMB_BMC_3v3SB_ALRT_N
for (i = 0; i < BIC_UPDATE_RETRIES; i++) {
if (_is_bic_update_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 update_done;
}
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_update_ready(slot_id))
break;
msleep(BIC_UPDATE_TIMEOUT);
}
if (i == BIC_UPDATE_RETRIES) {
printf("bic is NOT ready\n");
goto error_exit;
}
update_done:
ret = 0;
// Restore the I2C bus clock to 1M.
switch(slot_id)
{
case 1:
system("devmem 0x1e78a104 w 0x77744302");
break;
case 2:
system("devmem 0x1e78a084 w 0x77744302");
break;
case 3:
system("devmem 0x1e78a304 w 0x77744302");
break;
case 4:
system("devmem 0x1e78a184 w 0x77744302");
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));
system(cmd);
error_exit:
syslog(LOG_CRIT, "bic_update_fw: updating bic firmware is exiting\n");
if (fd > 0) {
close(fd);
}
if (ifd > 0) {
close(ifd);
}
set_fw_update_ongoing(slot_id, 0);
return ret;
}
static int
check_cpld_image(int fd, long size) {
int i, j, rcnt;
uint8_t *buf, data;
uint16_t crc_exp, crc_val = 0xffff;
uint32_t dword, crc_offs;
if (size < 52)
return -1;
buf = (uint8_t *)malloc(size);
if (!buf) {
return -1;
}
i = 0;
while (i < size) {
rcnt = read(fd, (buf + i), size);
if ((rcnt < 0) && (errno != EINTR)) {
free(buf);
return -1;
}
i += rcnt;
}
dword = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if ((dword != 0x4A414D00) && (dword != 0x4A414D01)) {
free(buf);
return -1;
}
i = 32 + (dword & 0x1) * 8;
crc_offs = (buf[i] << 24) | (buf[i+1] << 16) | (buf[i+2] << 8) | buf[i+3];
if ((crc_offs + sizeof(crc_exp)) > size) {
free(buf);
return -1;
}
crc_exp = (buf[crc_offs] << 8) | buf[crc_offs+1];
for (i = 0; i < crc_offs; i++) {
data = buf[i];
for (j = 0; j < 8; j++, data >>= 1) {
crc_val = ((data ^ crc_val) & 0x1) ? ((crc_val >> 1) ^ 0x8408) : (crc_val >> 1);
}
}
crc_val = ~crc_val;
free(buf);
if (crc_exp != crc_val)
return -1;
lseek(fd, 0, SEEK_SET);
return 0;
}
static int
check_bios_image(int fd, long size) {
int i, 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;
}
lseek(fd, (size - BIOS_VER_REGION_SIZE), SEEK_SET);
i = 0;
while (i < BIOS_VER_REGION_SIZE) {
rcnt = read(fd, (buf + i), BIOS_ERASE_PKT_SIZE);
if ((rcnt < 0) && (errno != EINTR)) {
free(buf);
return -1;
}
i += rcnt;
}
end = BIOS_VER_REGION_SIZE - (sizeof(ver_sig) + strlen(BIOS_VER_STR));
for (i = 0; i < end; i++) {
if (!memcmp(buf+i, ver_sig, sizeof(ver_sig))) {
if (memcmp(buf+i+sizeof(ver_sig), BIOS_VER_STR, strlen(BIOS_VER_STR))) {
i = end;
}
break;
}
}
free(buf);
if (i >= end)
return -1;
lseek(fd, 0, SEEK_SET);
return 0;
}
int
bic_update_firmware(uint8_t slot_id, uint8_t comp, char *path, uint8_t force) {
int ret = -1, rc;
uint32_t offset;
volatile uint16_t count, read_count;
uint8_t buf[256] = {0};
uint8_t target;
int fd;
int i;
uint32_t tcksum;
uint32_t gcksum;
uint8_t *tbuf = NULL;
printf("updating fw on slot %d:\n", slot_id);
// Handle Bridge IC firmware separately as the process differs significantly from others
if (comp == UPDATE_BIC) {
set_fw_update_ongoing(slot_id, 60);
return _update_bic_main(slot_id, path);
}
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_BIOS) {
if (!force && check_bios_image(fd, st.st_size) < 0) {
printf("invalid BIOS file!\n");
goto error_exit;
}
syslog(LOG_CRIT, "bic_update_fw: update bios firmware on slot %d\n", slot_id);
set_fw_update_ongoing(slot_id, 30);
dsize = st.st_size/100;
} else {
if ((comp == UPDATE_CPLD) && (check_cpld_image(fd, st.st_size) < 0)) {
printf("invalid CPLD file!\n");
goto error_exit;
}
switch(comp){
case UPDATE_CPLD:
syslog(LOG_CRIT, "bic_update_fw: update cpld firmware on slot %d\n", slot_id);
break;
case UPDATE_BIC_BOOTLOADER:
syslog(LOG_CRIT, "bic_update_fw: update bic bootloader firmware on slot %d\n", slot_id);
break;
}
set_fw_update_ongoing(slot_id, 20);
dsize = st.st_size/20;
}
// Write chunks of binary data in a loop
offset = 0;
last_offset = 0;
i = 1;
while (1) {
// For BIOS, send packets in blocks of 64K
if (comp == UPDATE_BIOS && ((offset+IPMB_WRITE_COUNT_MAX) > (i * BIOS_ERASE_PKT_SIZE))) {
read_count = (i * BIOS_ERASE_PKT_SIZE) - offset;
i++;
} else {
read_count = IPMB_WRITE_COUNT_MAX;
}
// Read from file
count = read(fd, buf, read_count);
if (count <= 0) {
break;
}
// For non-BIOS update, the last packet is indicated by extra flag
if ((comp != UPDATE_BIOS) && (count < read_count)) {
target = comp | 0x80;
} else {
target = comp;
}
// Send data to Bridge-IC
rc = _update_fw(slot_id, target, offset, count, buf);
if (rc) {
goto error_exit;
}
// Update counter
offset += count;
if((last_offset + dsize) <= offset) {
switch(comp) {
case UPDATE_BIOS:
set_fw_update_ongoing(slot_id, 25);
printf("updated bios: %d %%\n", offset/dsize);
break;
case UPDATE_CPLD:
printf("updated cpld: %d %%\n", offset/dsize*5);
break;
default:
printf("updated bic boot loader: %d %%\n", offset/dsize*5);
break;
}
fflush(stdout);
last_offset += dsize;
}
}
if (comp != UPDATE_BIOS) {
goto update_done;
}
set_fw_update_ongoing(slot_id, 55);
// Checksum calculation for BIOS image
tbuf = malloc(BIOS_VERIFY_PKT_SIZE * sizeof(uint8_t));
if (!tbuf) {
goto error_exit;
}
lseek(fd, 0, SEEK_SET);
offset = 0;
while (1) {
count = read(fd, tbuf, BIOS_VERIFY_PKT_SIZE);
if (count <= 0) {
break;
}
tcksum = 0;
for (i = 0; i < BIOS_VERIFY_PKT_SIZE; i++) {
tcksum += tbuf[i];
}
// Get the checksum of binary image
rc = bic_get_fw_cksum(slot_id, comp, offset, BIOS_VERIFY_PKT_SIZE, (uint8_t*)&gcksum);
if (rc) {
goto error_exit;
}
// Compare both and see if they match or not
if (gcksum != tcksum) {
printf("checksum does not match offset:0x%x, 0x%x:0x%x\n", offset, tcksum, gcksum);
goto error_exit;
}
offset += BIOS_VERIFY_PKT_SIZE;
}
update_done:
ret = 0;
error_exit:
switch(comp) {
case UPDATE_BIOS:
syslog(LOG_CRIT, "bic_update_fw: updating bios firmware is exiting\n");
break;
case UPDATE_CPLD:
syslog(LOG_CRIT, "bic_update_fw: updating cpld firmware is exiting\n");
break;
case UPDATE_BIC_BOOTLOADER:
syslog(LOG_CRIT, "bic_update_fw: updating bic bootloader firmware is exiting\n");
break;
}
if (fd > 0 ) {
close(fd);
}
if (tbuf) {
free(tbuf);
}
set_fw_update_ongoing(slot_id, 0);
return ret;
}
int
bic_update_fw(uint8_t slot_id, uint8_t comp, char *path) {
return bic_update_firmware(slot_id, comp, path, 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[7], rlen-7);
*rxlen = rlen-7;
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);
*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;
}
// Ignore the first byte as it indicates length of response
write(fd, &rbuf[1], rlen-1);
// 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 = 0;
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;
}
// 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, uint16_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 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, &req->rsv_id);
if (ret) {
#ifdef DEBUG
syslog(LOG_ERR, "bic_read_sdr: _get_sdr_rsv returns %d\n", ret);
#endif
return ret;
}
// 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;
}
// 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;
}
// Copy the data excluding the first two bytes(next_rec_id)
memcpy(&res->data[req->offset], tres->data, tlen-2);
// Update response length, offset for next request, and remaining length
*rlen += tlen-2;
req->offset += tlen-2;
len -= tlen-2;
}
return 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_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_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;
}