common/recipes-lib/bic/files/bic.c (1,220 lines of code) (raw):
/*
*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This file contains code to support IPMI2.0 Specification 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 <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <linux/limits.h>
#include <openbmc/kv.h>
#include <openbmc/log.h>
#include <openbmc/obmc-i2c.h>
#include "bic.h"
#define SIZE_IANA_ID 3
#define SDR_READ_COUNT_MAX 0x1A
#define FRUID_WRITE_COUNT_MAX 0x30
#define GPIO_MAX 31
#define IPMB_WRITE_COUNT_MAX 224
#define BIOS_ERASE_PKT_SIZE (64 * 1024)
#define BIOS_VERIFY_PKT_SIZE (32 * 1024)
#define BIOS_VER_REGION_SIZE (4 * 1024 * 1024)
#define BIOS_VER_STR "XG1_"
#define BIC_CMD_RUN 0x22
#define BIC_CMD_DOWNLOAD 0x21
#define BIC_FLASH_START 0x8000
#define BIC_CMD_STATUS 0x23
#define BIC_PKT_MAX 252
#define BIC_CMD_DATA 0x24
#define BIC_IANA_ID {0x15, 0xA0, 0x00}
#define BIC_IANA_ID_SIZE 3
#define CMD_RUN_SIZE 7
#define CMD_DOWNLOAD_SIZE 11
#define CMD_STATUS_SIZE 3
#define MIN_CMD_SIZE 3
#define FRU_SCM 1
#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)
/*
* BIC GPIO pin names.
*/
#define BIC_GPIO_DEF(type, name) [type] = name
static const char *gpio_pin_names[BIC_GPIO_MAX] = {
BIC_GPIO_LIST,
};
#undef BIC_GPIO_DEF
#define BIC_ASSERT(expr, fmt, args...) \
do { \
if (!(expr)) { \
OBMC_CRIT("%s %s: " fmt, __FILE__, __func__, ##args); \
assert(0); \
} \
} while (0)
#define IPMB_XFER_VERIFY(ret, res_size, exp_res_size) \
do { \
if ((ret) != 0) { \
return -1; \
} else if ((res_size) != (exp_res_size)) { \
errno = EBADMSG; \
return -1; \
} \
} while (0)
/*
* NOTE:
* - callers need to set "rxlen" to "rxbuf" size to avoid buffer
* overflow.
* - if the function returns successfully, "rxlen" would be set to the
* actual response length.
*/
int bic_ipmb_wrapper(uint8_t slot_id, uint8_t netfn, uint8_t cmd,
uint8_t* txbuf, size_t txlen, uint8_t* rxbuf,
size_t* rxlen) {
unsigned short tlen;
unsigned char rlen = 0;
uint8_t rbuf[MAX_IPMB_RES_LEN] = {0};
uint8_t tbuf[MAX_IPMB_RES_LEN] = {0};
ipmb_req_t* req = (ipmb_req_t*)tbuf;
ipmb_res_t* res = (ipmb_res_t*)rbuf;
if (txlen) {
if ((txbuf == NULL) ||
(IPMB_HDR_SIZE + IPMI_REQ_HDR_SIZE + txlen > sizeof(tbuf))) {
errno = EINVAL;
return -1;
}
memcpy(req->data, txbuf, txlen);
}
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;
// Invoke IPMB library handler
tlen = IPMB_HDR_SIZE + IPMI_REQ_HDR_SIZE + txlen;
if (lib_ipmb_handle(slot_id, tbuf, tlen, rbuf, &rlen) != 0) {
return -1;
}
// Handle IPMB response
if (res->cc || rlen < (IPMB_HDR_SIZE + IPMI_RESP_HDR_SIZE)) {
errno = EBADMSG;
return -1;
}
// copy the received data back to caller
if (rxbuf != NULL && rxlen != NULL) {
rlen -= (IPMB_HDR_SIZE + IPMI_RESP_HDR_SIZE);
if (rlen > *rxlen) {
OBMC_WARN("%s: rxbuf truncated: expect %u, actual %u", __func__,
rlen, *rxlen);
rlen = *rxlen;
}
*rxlen = rlen;
memcpy(rxbuf, res->data, *rxlen);
}
return 0;
}
const char *bic_gpio_name(unsigned int pin)
{
if ((pin < BIC_GPIO_MAX) && (gpio_pin_names[pin] != NULL)) {
return gpio_pin_names[pin];
}
return "";
}
int bic_get_gpio_config(uint8_t slot_id, uint8_t gpio,
bic_gpio_config_t* gpio_config) {
uint8_t tbuf[7] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[4] = {0x00};
size_t rlen = sizeof(rbuf);
uint32_t pin;
int ret;
if (gpio > GPIO_MAX || gpio_config == NULL) {
errno = EINVAL;
return -1;
}
pin = 1 << gpio;
tbuf[3] = pin & 0xFF;
tbuf[4] = (pin >> 8) & 0xFF;
tbuf[5] = (pin >> 16) & 0xFF;
tbuf[6] = (pin >> 24) & 0xFF;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO_CONFIG,
tbuf, sizeof(tbuf), rbuf, &rlen);
IPMB_XFER_VERIFY(ret, rlen, sizeof(rbuf));
*((uint8_t*)gpio_config) = rbuf[BIC_IANA_ID_SIZE];
return 0;
}
// Get GPIO value and configuration
int bic_get_gpio(uint8_t slot_id, bic_gpio_t* gpio) {
uint8_t tbuf[3] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[7] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
if (gpio == NULL) {
errno = EINVAL;
return -1;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_GPIO, tbuf,
0x03, rbuf, &rlen);
IPMB_XFER_VERIFY(ret, rlen, sizeof(rbuf));
memcpy((uint8_t*)gpio, &rbuf[BIC_IANA_ID_SIZE], 4);
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;
size_t rlen = sizeof(*info);
if (info == NULL) {
errno = EINVAL;
return -1;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_FRUID_INFO,
&fru_id, 1, (uint8_t*)info, &rlen);
IPMB_XFER_VERIFY(ret, rlen, 3);
return 0;
}
int bic_set_gpio(uint8_t slot_id, uint8_t gpio, uint8_t value) {
uint8_t tbuf[11] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[3] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
// Check for boundary conditions
if (gpio > GPIO_MAX) {
errno = EINVAL;
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);
IPMB_XFER_VERIFY(ret, rlen, sizeof(rbuf));
return 0;
}
// Get BIC Configuration
int bic_get_config(uint8_t slot_id, bic_config_t* cfg) {
uint8_t tbuf[3] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[4] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_CONFIG, tbuf,
0x03, rbuf, &rlen);
IPMB_XFER_VERIFY(ret, rlen, sizeof(rbuf));
*(uint8_t*)cfg = rbuf[BIC_IANA_ID_SIZE];
return 0;
}
// Set BIC Configuration
int bic_set_config(uint8_t slot_id, bic_config_t* cfg) {
uint8_t tbuf[4] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[4] = {0};
size_t rlen = sizeof(rbuf);
int ret;
if (cfg == NULL) {
errno = EINVAL;
return -1;
}
tbuf[3] = *(uint8_t*)cfg;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_SET_CONFIG, tbuf,
sizeof(tbuf), 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] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[255] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
if (buf == NULL || len == NULL) {
errno = EINVAL;
return -1;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_GET_POST_BUF,
tbuf, sizeof(tbuf), rbuf, &rlen);
if (ret != 0) {
return -1;
} else if (rlen < BIC_IANA_ID_SIZE) {
errno = EBADMSG;
return -1;
}
/*
* XXX potential buffer overflow: callers need to set <len> to <buf>
* size before calling the function.
*/
rlen -= BIC_IANA_ID_SIZE;
memcpy(buf, &rbuf[BIC_IANA_ID_SIZE], rlen);
*len = rlen;
return ret;
}
// Read System Event Log (SEL)
int bic_get_sel_info(uint8_t slot_id, ipmi_sel_sdr_info_t* info) {
int ret;
size_t rlen = sizeof(*info);
if (info == NULL) {
errno = EINVAL;
return -1;
}
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, size_t* rlen) {
int ret;
if (req == NULL || res == NULL || rlen == NULL) {
errno = EINVAL;
return -1;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SEL,
(uint8_t*)req, sizeof(*req), (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;
size_t rlen = sizeof(*info);
if (info == NULL) {
errno = EINVAL;
return -1;
}
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(uint8_t slot_id, ipmi_sel_sdr_req_t* req,
ipmi_sel_sdr_res_t* res, size_t* rlen) {
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_GET_SDR,
(uint8_t*)req, sizeof(*req), (uint8_t*)res, rlen);
return ret;
}
static int _get_sdr_rsv(uint8_t slot_id, uint16_t* rsv) {
int ret;
size_t rlen = sizeof(*rsv);
ret = bic_ipmb_wrapper(slot_id, NETFN_STORAGE_REQ, CMD_STORAGE_RSV_SDR, NULL,
0, (uint8_t*)rsv, &rlen);
return ret;
}
int bic_get_sdr(uint8_t slot_id, ipmi_sel_sdr_req_t* req,
ipmi_sel_sdr_res_t* res, size_t* rlen) {
int ret;
uint8_t tbuf[MAX_IPMB_RES_LEN] = {0};
size_t tlen = sizeof(tbuf);
uint8_t len;
ipmi_sel_sdr_res_t* tres = (ipmi_sel_sdr_res_t*)tbuf;
sdr_rec_hdr_t* hdr;
if (req == NULL || res == NULL || rlen == NULL) {
errno = EINVAL;
return -1;
}
// Get SDR reservation ID for the given record
ret = _get_sdr_rsv(slot_id, &req->rsv_id);
if (ret) {
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) {
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;
}
tlen = sizeof(tbuf);
ret = _get_sdr(slot_id, req, (ipmi_sel_sdr_res_t*)tbuf, &tlen);
if (ret) {
return ret;
}
// Copy the data excluding the first two bytes(next_rec_id)
memcpy(&res->data[req->offset], tres->data, tlen - 2);
// Update response length, offset for next request, and remaining length
*rlen += tlen - 2;
req->offset += tlen - 2;
len -= tlen - 2;
}
return 0;
}
// Get Device ID
int bic_get_dev_id(uint8_t slot_id, ipmi_dev_id_t* dev_id) {
size_t rlen = sizeof(*dev_id);
int ret;
if (dev_id == NULL) {
errno = EINVAL;
return -1;
}
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_read_sensor(uint8_t slot_id, uint8_t sensor_num,
ipmi_sensor_reading_t* sensor) {
int ret;
size_t rlen = sizeof(*sensor);
if (sensor == NULL) {
errno = EINVAL;
return -1;
}
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;
}
static int _read_fruid(uint8_t slot_id, uint8_t fru_id, uint32_t offset,
uint8_t count, uint8_t* rbuf, size_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, sizeof(tbuf), 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[MAX_IPMI_MSG_SIZE] = {0};
size_t rlen = 0;
int fd = -1;
ipmi_fruid_info_t info;
if (path == NULL || fru_size == NULL) {
errno = EINVAL;
return -1;
}
// 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) {
return -1;
}
// Read the FRUID information
ret = bic_get_fruid_info(slot_id, fru_id, &info);
if (ret) {
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;
}
rlen = sizeof(rbuf);
ret = _read_fruid(slot_id, fru_id, offset, count, rbuf, &rlen);
if (ret) {
goto error_exit;
}
// Ignore the first byte as it indicates length of response
ret = write(fd, &rbuf[1], rlen - 1);
if (ret < 0) {
OBMC_ERROR(errno, "failed to write %s", path);
goto error_exit;
} else if (ret != rlen - 1) {
OBMC_WARN("data truncated (write %s): expect %u, actual %d\n",
path, rlen - 1, ret);
/*
* XXX shall we exit or continue?
*/
}
// Update offset
offset += (rlen - 1);
nread -= (rlen - 1);
}
close(fd);
return 0;
error_exit:
if (fd >= 0) {
close(fd);
}
return -1;
}
int bic_read_mac(uint8_t slot_id, char* rbuf, size_t rlen) {
uint8_t tbuf[2] = {0x00, 0x01};
int ret;
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_REQ, CMD_OEM_GET_MAC_ADDR, tbuf,
sizeof(tbuf), (uint8_t*)rbuf, &rlen);
if (ret)
return -1;
return 0;
}
static int set_fw_update_ongoing(uint8_t fru_id, uint16_t timeout) {
char key[64];
char value[64];
struct timespec ts;
snprintf(key, sizeof(key), "fru%d_fwupd", fru_id);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += timeout;
snprintf(value, sizeof(value), "%d", (int)ts.tv_sec);
if (kv_set(key, value, 0, 0) < 0) {
return -1;
}
return 0;
}
// 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_IPMI_MSG_SIZE] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[16] = {0x00};
size_t tlen, rlen;
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;
tlen = len + 10;
BIC_ASSERT(tlen < sizeof(tbuf), "txbuf overflow: txlen=%u, buflen=%u",
tlen, sizeof(tbuf));
memcpy(&tbuf[10], buf, len);
while (retries-- > 0) {
rlen = sizeof(rbuf);
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_UPDATE_FW,
tbuf, tlen, rbuf, &rlen);
if (ret == 0)
break;
sleep(1);
OBMC_INFO("%s: slot: %d, target %d, offset: %d, len: %d retrying..\n",
__func__, slot_id, target, offset, len);
}
return ret;
}
// Read Firwmare Versions of various components
static int _enable_bic_update(uint8_t slot_id) {
uint8_t tbuf[4] = BIC_IANA_ID;
uint8_t rbuf[16] = {0x00};
size_t rlen = sizeof(rbuf);
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, sizeof(tbuf), rbuf, &rlen);
return ret;
}
// 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 bic_i2c_xfer(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;
OBMC_DEBUG("start i2c xfer with bic: tx=%u, rx=%u bytes", tcount, rcount);
rc = ioctl(fd, I2C_RDWR, &data);
if (rc < 0) {
return -1;
}
return 0;
}
static int run_shell_cmd(const char* cmd) {
OBMC_DEBUG("running <%s> command\n", cmd);
return system(cmd);
}
static int send_bic_image_data(int ifd, int xcount, uint8_t* xbuf) {
uint8_t tbuf[MAX_IPMI_MSG_SIZE] = {0};
uint8_t rbuf[MAX_IPMI_MSG_SIZE] = {0};
uint8_t tcount = 0;
uint8_t rcount = 0;
int i = 0, rc = 0, retry = 5;
while( retry-- )
{
//Setup Command to Set Update Data
tbuf[0] = xcount + MIN_CMD_SIZE; //Command Length
tbuf[1] = BIC_CMD_DATA; //Checksum
tbuf[2] = BIC_CMD_DATA; //Command Number
memcpy(&tbuf[MIN_CMD_SIZE], xbuf, xcount); //Image Data
//Checksum calculation
for (i = 0; i < xcount; i++) {
tbuf[1] += xbuf[i];
}
tcount = tbuf[0];
rcount = 0;
//Send Command to Set Update Data
rc = bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
OBMC_ERROR(errno, "fail to send bic update command. retry...");
continue;
}
//Waittig for bic to do command's action
msleep(10);
//Setup Command to Get Response
tcount = 0;
rcount = 2;
//Send Command to Get Response
rc = bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
OBMC_ERROR(errno, "fail to get command response. retry...");
continue;
}
//Check Set Update Data's Response
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
OBMC_WARN("check bic res. data error, expect 0x00,0xcc but receive : %#x:%#x\n", rbuf[0], rbuf[1]);
continue;
}
break;
}
if(retry)
return 0;
else
return 1;
}
static int prepare_update_bic(uint8_t slot_id, int ifd, int size) {
int i = 0;
uint8_t tbuf[MAX_IPMI_MSG_SIZE] = {0};
uint8_t rbuf[16] = {0};
uint8_t tcount;
uint8_t rcount;
/* Kill ipmb daemon for this slot */
run_shell_cmd("sv stop ipmbd_0");
sleep(1);
OBMC_INFO("stopped ipmbd for slot %u\n", slot_id);
/* Restart ipmb daemon with "-u|--enable-bic-update" for bic update */
run_shell_cmd("/usr/local/bin/ipmbd -u 0 1 > /dev/null 2>&1 &");
OBMC_INFO("started 'ipmbd -u' for minilake\n");
sleep(2);
/* Enable Bridge-IC update */
_enable_bic_update(slot_id);
/* Kill ipmb daemon "--enable-bic-update" for this slot */
run_shell_cmd(
"ps -w | grep -v 'grep' | grep 'ipmbd -u 0' | awk '{print $1}' "
"| xargs kill");
OBMC_INFO("stopped 'ipmbd -u' for minilake\n");
// The I2C fast speed clock (400KHz) may cause to read BIC data abnormally.
// So reduce I2C bus clock speed which is a workaround for BIC update.
run_shell_cmd("devmem 0x1e78a044 w 0xfff77304");
OBMC_INFO("updated i2c-0 timing settings\n");
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;
if (bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount)) {
OBMC_ERROR(errno, "bic_i2c_xfer failed download");
return -1;
}
msleep(500); // delay for download command process
tcount = 0;
rcount = 2;
if (bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount)) {
OBMC_ERROR(errno, "bic_i2c_xfer failed download ack");
return -1;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
OBMC_WARN("unexpected download response: %x:%x\n", rbuf[0], rbuf[1]);
return -1;
}
return 0;
}
static int update_bic_status(uint8_t slot_id, int ifd) {
uint8_t tbuf[MAX_IPMI_MSG_SIZE] = {0};
uint8_t rbuf[16] = {0};
uint8_t tcount;
uint8_t rcount;
/* 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;
if (bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount)) {
OBMC_ERROR(errno, "failed to get BIC_CMD_STATUS");
return -1;
}
tcount = 0;
rcount = 5;
if (bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount)) {
OBMC_ERROR(errno, "failed to get status ack");
return -1;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc || rbuf[2] != 0x03 ||
rbuf[3] != 0x40 || rbuf[4] != 0x40) {
OBMC_WARN("unexpected status resp: %x:%x:%x:%x:%x\n", rbuf[0], rbuf[1],
rbuf[2], rbuf[3], rbuf[4]);
return -1;
}
return 0;
}
static int _update_bic_main(uint8_t slot_id, const char* path) {
int fd;
int ifd = -1;
struct stat buf;
int size;
uint8_t tbuf[MAX_IPMI_MSG_SIZE] = {0};
uint8_t rbuf[16] = {0};
uint8_t tcount;
uint8_t rcount;
volatile int xcount;
int i = 0, retry = 5;
int ret = -1, rc;
uint8_t xbuf[MAX_IPMI_MSG_SIZE] = {0};
uint32_t offset = 0, last_offset = 0, dsize;
syslog(LOG_CRIT, "update bic firmware on minilake\n");
// Open the file exclusively for read
fd = open(path, O_RDONLY, 0666);
if (fd < 0) {
syslog(LOG_ERR, "failed to open %s: %s\n", path, strerror(errno));
goto error_exit;
}
fstat(fd, &buf);
size = buf.st_size;
OBMC_INFO("size of %s is %d bytes\n", path, size);
dsize = size / 20;
// Open the i2c driver
ifd = i2c_cdev_slave_open(0, BRIDGE_SLAVE_ADDR, 0); // bus 0
if (ifd < 0) {
printf("ifd error\n");
goto error_exit;
}
if (prepare_update_bic(slot_id, ifd, size)) {
goto error_exit;
}
// Loop to send all the image data
while (1) {
/* Sometimes BIC update status get fail, so add retry for workaround */
while ((rc = update_bic_status(slot_id, ifd)) != 0 && retry--) {
OBMC_WARN("update_bic, BIC status get fail retrying ...\n");
offset = 0;
last_offset = 0;
lseek(fd, 0, SEEK_SET);
prepare_update_bic(slot_id, ifd, size);
}
if (rc) {
goto error_exit;
}
// Send ACK ---
tbuf[0] = 0xcc;
tcount = 1;
rcount = 0;
rc = bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
OBMC_ERROR(errno, "failed send ack");
goto error_exit;
}
// read data from update image
xcount = read(fd, xbuf, BIC_PKT_MAX);
if (xcount <= 0) {
break;
}
// calc percentage
offset += xcount;
if ((last_offset + dsize) <= offset) {
OBMC_INFO("updated bic: %d %%\n", offset / dsize * 5);
last_offset += dsize;
}
// send update image data to bic
rc = send_bic_image_data(ifd, xcount, xbuf);
if (rc) {
OBMC_ERROR(errno, "failed send update bic command, please check bic's status");
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 = bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
OBMC_ERROR(errno, "bic_i2c_xfer failed run");
goto error_exit;
}
tcount = 0;
rcount = 2;
rc = bic_i2c_xfer(ifd, tbuf, tcount, rbuf, rcount);
if (rc) {
OBMC_ERROR(errno, "failed run ack");
goto error_exit;
}
if (rbuf[0] != 0x00 || rbuf[1] != 0xcc) {
OBMC_WARN("unexpected run response: %#x:%#x\n", rbuf[0], rbuf[1]);
goto error_exit;
}
msleep(500);
ret = 0;
// Restore the I2C bus clock to 400KHz.
OBMC_INFO("restoring i2c-0 timing settings");
run_shell_cmd("devmem 0x1e78a044 w 0xfff77302");
// Restart ipmbd daemon
sleep(1);
run_shell_cmd("sv start ipmbd_0");
error_exit:
syslog(LOG_CRIT, "updating bic firmware is exiting, ret=%d\n", ret);
if (fd >= 0) {
close(fd);
}
if (ifd >= 0) {
close(ifd);
}
set_fw_update_ongoing(FRU_SCM, 0);
return ret;
}
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;
if (lseek(fd, (size - BIOS_VER_REGION_SIZE), SEEK_SET) == (off_t) -1) {
OBMC_ERROR(errno, "failed to update file (fd=%d) offset to %ld: %s",
fd, size - BIOS_VER_REGION_SIZE, strerror(errno));
return -1;
}
buf = (uint8_t*)malloc(BIOS_VER_REGION_SIZE);
if (buf == NULL) {
return -1;
}
i = 0;
while (i < BIOS_VER_REGION_SIZE) {
rcnt = read(fd, (buf + i), BIOS_ERASE_PKT_SIZE);
if (rcnt < 0) {
if (errno == EINTR)
continue;
free(buf);
return -1;
} else if (rcnt == 0) {
OBMC_WARN("bios image file reached EOF unexpectedly!");
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;
if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
OBMC_ERROR(errno, "failed to restore file (fd=%d) offset to 0: %s",
fd, strerror(errno));
return -1;
}
return 0;
}
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;
}
// 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] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[16] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
if (ver == NULL) {
errno = EINVAL;
return -1;
}
// 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;
}
OBMC_DEBUG("cksum returns: %x:%x:%x::%x:%x:%x:%x\n", rbuf[0], rbuf[1],
rbuf[2], rbuf[3], rbuf[4], rbuf[5], rbuf[6]);
// Ignore IANA ID
memcpy(ver, &rbuf[SIZE_IANA_ID], rlen - SIZE_IANA_ID);
return ret;
}
static int check_vr_image(int fd, long size) {
uint8_t buf[32];
uint8_t hdr[] = {0x00, 0x01, 0x4c, 0x1c, 0x00, 0x58, 0x47, 0x31,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (size < 32)
return -1;
lseek(fd, 1, SEEK_SET);
if (read(fd, buf, sizeof(hdr)) != sizeof(hdr))
return -1;
if (memcmp(buf, hdr, sizeof(hdr)))
return -1;
lseek(fd, 0, SEEK_SET);
return 0;
}
static struct {
const char* name;
int (*check_image)(int fd, long size);
} fw_update_info[UPDATE_VR + 1] = {
[UPDATE_BIOS] =
{
.name = "bios",
check_bios_image,
},
[UPDATE_CPLD] =
{
.name = "cpld",
.check_image = check_cpld_image,
},
[UPDATE_BIC_BOOTLOADER] =
{
.name = "bic bootloader",
},
[UPDATE_BIC] =
{
.name = "bic",
},
[UPDATE_VR] =
{
.name = "vr",
.check_image = check_vr_image,
},
};
int bic_update_fw(uint8_t slot_id, uint8_t comp, const char* image_file) {
uint16_t count, read_count;
uint8_t buf[MAX_IPMI_MSG_SIZE] = {0};
int i, fd, rc, ret = -1;
uint8_t* tbuf = NULL;
uint32_t dsize, offset, last_offset;
struct stat st;
OBMC_INFO("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(FRU_SCM, 60);
return _update_bic_main(slot_id, image_file);
}
// Open the file exclusively for read
fd = open(image_file, O_RDONLY);
if (fd < 0) {
OBMC_ERROR(errno, "failed to open %s for read", image_file);
goto error_exit;
}
if (stat(image_file, &st) != 0) {
OBMC_ERROR(errno, "failed to get %s status", image_file);
goto error_exit;
}
switch (comp) {
case UPDATE_BIOS:
set_fw_update_ongoing(FRU_SCM, 30);
dsize = st.st_size / 100;
break;
case UPDATE_VR:
dsize = st.st_size / 5;
break;
case UPDATE_CPLD:
case UPDATE_BIC_BOOTLOADER:
set_fw_update_ongoing(FRU_SCM, 20);
dsize = st.st_size / 20;
break;
default:
OBMC_WARN("unsupported firmware component %u\n", comp);
goto error_exit;
}
if ((fw_update_info[comp].check_image != NULL) &&
(fw_update_info[comp].check_image(fd, st.st_size) < 0)) {
OBMC_WARN("invalid %s firmware image!", fw_update_info[comp].name);
goto error_exit;
}
syslog(LOG_CRIT, "%s: update %s firmware on slot %d\n",
__func__, fw_update_info[comp].name, slot_id);
// Write chunks of binary data in a loop
offset = 0;
last_offset = 0;
i = 1;
while (1) {
uint8_t target;
// 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
assert(read_count < sizeof(buf));
count = read(fd, buf, read_count);
if (count < 0) {
OBMC_ERROR(errno, "failed to read %s", image_file);
break;
} else if (count == 0) { /* EOF */
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
if (_update_fw(slot_id, target, offset, count, buf) != 0) {
goto error_exit;
}
// Update counter
offset += count;
if ((last_offset + dsize) <= offset) {
switch (comp) {
case UPDATE_BIOS:
set_fw_update_ongoing(FRU_SCM, 25);
printf("updated bios: %d %%\n", offset / dsize);
break;
case UPDATE_CPLD:
printf("updated cpld: %d %%\n", offset / dsize * 5);
break;
case UPDATE_VR:
printf("updated vr: %d %%\n", offset / dsize * 20);
break;
default:
printf("updated bic boot loader: %d %%\n", offset / dsize * 5);
break;
}
last_offset += dsize;
}
} /* while */
if (comp != UPDATE_BIOS) {
goto update_done;
}
set_fw_update_ongoing(FRU_SCM, 55);
// Checksum calculation for BIOS image
tbuf = malloc(BIOS_VERIFY_PKT_SIZE * sizeof(uint8_t));
if (!tbuf) {
goto error_exit;
}
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
OBMC_ERROR(errno, "failed to set %s file offset", image_file);
goto error_exit;
}
offset = 0;
while (1) {
uint32_t tcksum, gcksum;
count = read(fd, tbuf, BIOS_VERIFY_PKT_SIZE);
if (count < 0) {
OBMC_ERROR(errno, "failed to read %s", image_file);
break;
} else if (count == 0) {
break; /* EOF */
}
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) {
OBMC_WARN("checksum does not match offset: %#x, %#x:%#x\n", offset,
tcksum, gcksum);
goto error_exit;
}
offset += BIOS_VERIFY_PKT_SIZE;
} /* while */
update_done:
ret = 0;
error_exit:
syslog(LOG_CRIT, "%s: updating %s firmware is exiting, ret=%d\n",
__func__, fw_update_info[comp].name, ret);
if (fd >= 0) {
close(fd);
}
if (tbuf) {
free(tbuf);
}
set_fw_update_ongoing(FRU_SCM, 0);
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] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[16] = {0x00};
size_t rlen = sizeof(rbuf);
int ret;
if (ver == NULL) {
errno = EINVAL;
return -1;
}
// 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) {
return -1;
} else if ((rlen < 2 + SIZE_IANA_ID) || (rlen > 5 + SIZE_IANA_ID)) {
errno = EBADMSG;
return -1;
}
// Ignore IANA ID
// XXX potential buffer overflow (when ver is not big enough to hold
// <rlen - 3> bytes).
memcpy(ver, &rbuf[3], rlen - 3);
return ret;
}
int bic_get_self_test_result(uint8_t slot_id, uint8_t* self_test_result) {
int ret;
size_t rlen = SIZE_SELF_TEST_RESULT;
if (self_test_result == NULL) {
errno = EINVAL;
return -1;
}
ret = bic_ipmb_wrapper(slot_id, NETFN_APP_REQ, CMD_APP_GET_SELFTEST_RESULTS,
NULL, 0, (uint8_t*)self_test_result, &rlen);
return ret;
}
int bic_me_xmit(uint8_t slot_id, uint8_t* txbuf, size_t txlen, uint8_t* rxbuf,
size_t* rxlen) {
uint8_t tbuf[MAX_IPMI_MSG_SIZE] = BIC_IANA_ID; // IANA ID
uint8_t rbuf[MAX_IPMI_MSG_SIZE] = {0x00};
size_t rlen = sizeof(rbuf);
size_t tlen = 0;
int ret;
if (txbuf == NULL || rxbuf == NULL || rxlen == NULL) {
errno = EINVAL;
return -1;
}
// Fill the interface number as ME
tbuf[3] = BIC_INTF_ME;
// Fill the data to be sent
tlen = txlen + 4;
BIC_ASSERT(tlen < sizeof(tbuf), "txbuf overflow");
memcpy(&tbuf[4], txbuf, txlen);
// Send data length includes IANA ID and interface number
ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, tbuf,
tlen, rbuf, &rlen);
if (ret) {
return -1;
} else if (rlen < 6 || rbuf[3] != tbuf[3]) {
// Make sure the received interface number is same
errno = EBADMSG;
return -1;
}
// Copy the received data to caller skipping header
// XXX potential buffer overflow (when rxbuf is not big enough to hold
// *rxlen bytes).
*rxlen = rlen - 6;
memcpy(rxbuf, &rbuf[6], *rxlen);
return 0;
}
/*
* 0x2E 0xDF: Force Intel ME Recovery
* Request
* Byte 1:3 = Intel Manufacturer ID - 000157h, LS byte first.
* Byte 4 - Command
* = 01h Restart using Recovery Firmware
* (Intel ME FW configuration is not restored to factory defaults)
* = 02h Restore Factory Default Variable values and restart the Intel ME FW
* = 03h PTT Initial State Restore
*
* Response
* Byte 1 - Completion Code
* = 00h - Success
* (Remaining standard Completion Codes are shown in Section 2.12)
* = 81h - Unsupported Command parameter value in the Byte 4 of the request.
*
* Byte 2:4 = Intel Manufacturer ID - 000157h, LS byte first.
*/
int me_recovery(uint8_t slot_id, uint8_t command) {
uint8_t tbuf[256] = {0x00};
uint8_t rbuf[256] = {0x00};
size_t tlen = 0;
size_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 */
OBMC_CRIT("%s: Restart using Recovery Firmware failed..., retried: %d",
__func__, retry);
return -1;
}
sleep(10);
retry = 0;
memset(&tbuf, 0, sizeof(tbuf));
memset(&rbuf, 0, sizeof(rbuf));
/*
* 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 */
OBMC_CRIT("%s: Restore Factory Default failed..., retried: %d",
__func__, retry);
return -1;
}
return 0;
}