meta-facebook/meta-fby3/recipes-fby3/plat-libs/files/bic/bic_xfer.c (303 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 <time.h> #include <fcntl.h> #include <syslog.h> #include <errno.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> #include <openbmc/kv.h> #include <openbmc/obmc-i2c.h> #include <openbmc/libgpio.h> #include "bic_xfer.h" #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif const uint32_t IANA_ID = 0x009C9C; 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; } } int i2c_open(uint8_t bus_id, uint8_t addr_7bit) { int fd = -1; fd = i2c_cdev_slave_open(bus_id, addr_7bit, I2C_SLAVE_FORCE_CLAIM); if ( fd < 0 ) { syslog(LOG_ERR, "Failed to open /dev/i2c-%d\n", bus_id); return BIC_STATUS_FAILURE; } return fd; } 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 BIC_STATUS_FAILURE; } return BIC_STATUS_SUCCESS; } int is_bic_ready(uint8_t slot_id, uint8_t intf) { //TODO: 1. check the ready pin of server bic by reading GPIO //send a command to BIC2 to see if it can be reached return BIC_STATUS_SUCCESS; } // Repack data according to the interface int bic_ipmb_send(uint8_t slot_id, uint8_t netfn, uint8_t cmd, uint8_t *tbuf, uint8_t tlen, uint8_t *rbuf, uint8_t *rlen, uint8_t intf) { int ret = 0; uint8_t tmp_buf[MAX_IPMB_RES_LEN] = {0x0}; uint8_t rsp_buf[MAX_IPMB_RES_LEN] = {0x0}; uint8_t tmp_len = 0; uint8_t rsp_len = 0; switch(intf) { case NONE_INTF: ret = bic_ipmb_wrapper(slot_id, netfn, cmd, tbuf, tlen, rbuf, rlen); break; case FEXP_BIC_INTF: case BB_BIC_INTF: case REXP_BIC_INTF: if ( tlen + MIN_IPMB_REQ_LEN + MIN_IPMB_BYPASS_LEN > MAX_IPMB_RES_LEN ) { syslog(LOG_WARNING, "%s() xfer length is too long. len=%d, max=%d", __func__, (tlen + MIN_IPMB_REQ_LEN + MIN_IPMB_BYPASS_LEN), MAX_IPMB_RES_LEN); return BIC_STATUS_FAILURE; } tmp_len = 3; memcpy(tmp_buf, (uint8_t *)&IANA_ID, tmp_len); tmp_buf[tmp_len++] = intf; tmp_buf[tmp_len++] = netfn << 2; tmp_buf[tmp_len++] = cmd; memcpy(&tmp_buf[tmp_len], tbuf, tlen); tmp_len += tlen; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, tmp_buf, tmp_len, rsp_buf, &rsp_len); //rsp_buf[6] is the completion code if ( (ret < 0) || (ret == BIC_STATUS_SUCCESS && rsp_buf[6] != CC_SUCCESS) ) { syslog(LOG_WARNING, "%s() The 2nd BIC cannot be reached. CC: 0x%02X, intf: 0x%x, ret = %d\n", __func__, rsp_buf[6], intf, ret); syslog(LOG_WARNING, "%s() Netfn:%02X, Cmd: %02X\n", __func__, netfn << 2, cmd); switch(rsp_buf[6]) { case CC_NOT_SUPP_IN_CURR_STATE: ret = BIC_STATUS_NOT_SUPP_IN_CURR_STATE; break; default: ret = BIC_STATUS_FAILURE; break; } } else { //catch the data and ignore the packet of the bypass command. *rlen = rsp_len - 7; memmove(rbuf, &rsp_buf[7], *rlen); } break; case RREXP_BIC_INTF1: case RREXP_BIC_INTF2: if ( tlen + MIN_IPMB_REQ_LEN + MIN_IPMB_BYPASS_LEN * 2 > MAX_IPMB_RES_LEN ) { syslog(LOG_WARNING, "%s() xfer length is too long. len=%d, max=%d", __func__, (tlen + MIN_IPMB_REQ_LEN + MIN_IPMB_BYPASS_LEN * 2), MAX_IPMB_RES_LEN); return BIC_STATUS_FAILURE; } tmp_len = 3; memcpy(tmp_buf, (uint8_t *)&IANA_ID, tmp_len); tmp_buf[tmp_len++] = REXP_BIC_INTF; tmp_buf[tmp_len++] = NETFN_OEM_1S_REQ << 2; tmp_buf[tmp_len++] = CMD_OEM_1S_MSG_OUT; memcpy(&tmp_buf[tmp_len], (uint8_t *)&IANA_ID, 3); tmp_len += 3; tmp_buf[tmp_len++] = intf; tmp_buf[tmp_len++] = netfn << 2; tmp_buf[tmp_len++] = cmd; memcpy(&tmp_buf[tmp_len], tbuf, tlen); tmp_len += tlen; ret = bic_ipmb_wrapper(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_MSG_OUT, tmp_buf, tmp_len, rsp_buf, &rsp_len); //rsp_buf[6] is the completion code if ( (ret < 0) || (ret == BIC_STATUS_SUCCESS && (rsp_buf[6] != CC_SUCCESS || rsp_buf[13] != CC_SUCCESS)) ) { if ( rsp_buf[6] != CC_SUCCESS || rsp_buf[13] != CC_SUCCESS ) { syslog(LOG_WARNING, "%s() The Exp BIC cannot be reached. CC0:0x%02X, CC1:0x%02X, intf:0x%x\n", __func__, rsp_buf[6], rsp_buf[13], intf); } syslog(LOG_WARNING, "%s() Netfn:%02X, Cmd:%02X Ret:%d\n", __func__, netfn << 2, cmd, ret); switch(rsp_buf[13] == CC_SUCCESS ? rsp_buf[6] : rsp_buf[13]) { case CC_NOT_SUPP_IN_CURR_STATE: ret = BIC_STATUS_NOT_SUPP_IN_CURR_STATE; break; default: ret = BIC_STATUS_FAILURE; break; } } else { //catch the data and ignore the packet of the bypass command. *rlen = rsp_len - 14; memmove(rbuf, &rsp_buf[14], *rlen); } break; } return ret; } 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) { ipmb_req_t *req; ipmb_res_t *res; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t tbuf[MAX_IPMB_RES_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; uint8_t status_12v = 0; #if 0 //TODO: implement the is_bic_ready funcitons if (!is_bic_ready(slot_id)) { return -1; } #endif ret = fby3_common_get_bus_id(slot_id); if (ret < 0) { syslog(LOG_ERR, "%s: Wrong Slot ID %d\n", __func__, slot_id); 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 < RETRY_TIME) { // avoid meaningless retry ret = fby3_common_server_stby_pwr_sts(slot_id, &status_12v); if ( ret < 0 || status_12v == 0) { return BIC_STATUS_FAILURE; } // Invoke IPMB library handler lib_ipmb_handle(bus_id, tbuf, tlen, rbuf, &rlen); if (rlen == 0) { #if 0 //TODO: implement the is_bic_ready funcitons if (!is_bic_ready(slot_id)) { break; } #endif retry++; msleep(IPMB_RETRY_DELAY_TIME); } else { res = (ipmb_res_t*) rbuf; if ( res->cc == CC_NODE_BUSY ) { retry++; msleep(IPMB_RETRY_DELAY_TIME); } else break; } } if (rlen == 0) { syslog(LOG_ERR, "bic_ipmb_wrapper: slot%d netfn: 0x%02X cmd: 0x%02X, Zero bytes received, retry:%d ", slot_id, netfn, cmd, retry ); return BIC_STATUS_FAILURE; } // Handle IPMB response if (res->cc) { syslog(LOG_ERR, "bic_ipmb_wrapper: slot%d netfn: 0x%02X cmd: 0x%02X, Completion Code: 0x%02X ", slot_id, netfn, cmd, res->cc); return BIC_STATUS_FAILURE; } // copy the received data back to caller *rxlen = rlen - IPMB_HDR_SIZE - IPMI_RESP_HDR_SIZE; 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, "bic_ipmb_wrapper: slot%d netfn: 0x%02X cmd: 0x%02X, Receive Data cksum does not match (expectative 0x%x, actual 0x%x)", slot_id, netfn, cmd, dataCksum, rbuf[rlen-1]); return BIC_STATUS_FAILURE; } return BIC_STATUS_SUCCESS; } int bic_me_xmit(uint8_t slot_id, uint8_t *txbuf, uint8_t txlen, uint8_t *rxbuf, uint8_t *rxlen) { uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t rlen = 0; uint8_t tlen = 0; int ret; // Fill the IANA ID memcpy(tbuf, (uint8_t *)&IANA_ID, 3); // 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 BIC_STATUS_FAILURE; } // Make sure the received interface number is same if (rbuf[3] != tbuf[3]) { return BIC_STATUS_FAILURE; } // Copy the received data to caller skipping header memcpy(rxbuf, &rbuf[6], rlen-6); *rxlen = rlen-6; return BIC_STATUS_SUCCESS; } int send_image_data_via_bic(uint8_t slot_id, uint8_t comp, uint8_t intf, uint32_t offset, uint16_t len, uint32_t image_len, uint8_t *buf) { uint8_t tbuf[256] = {0}; uint8_t rbuf[16] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; int ret = -1; int retries = 3; memcpy(tbuf, (uint8_t *)&IANA_ID, 3); tbuf[3] = comp; 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; if ( image_len > 0 ) { 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; } else { memcpy(&tbuf[10], buf, len); tlen = len + 10; } do { ret = bic_ipmb_send(slot_id, NETFN_OEM_1S_REQ, CMD_OEM_1S_UPDATE_FW, tbuf, tlen, rbuf, &rlen, intf); if (ret != BIC_STATUS_SUCCESS) { if (ret == BIC_STATUS_NOT_SUPP_IN_CURR_STATE) return ret; printf("%s() slot: %d, target: %d, offset: %d, len: %d retrying..\n", __func__, slot_id, comp, offset, len); } } while( (ret < 0) && (retries--)); return ret; } int open_and_get_size(char *path, int *file_size) { struct stat finfo; int fd; fd = open(path, O_RDONLY, 0666); if ( fd < 0 ) { return fd; } fstat(fd, &finfo); *file_size = finfo.st_size; return fd; }