diff options
Diffstat (limited to 'lib/ipmi_fwum.c')
-rw-r--r-- | lib/ipmi_fwum.c | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/lib/ipmi_fwum.c b/lib/ipmi_fwum.c new file mode 100644 index 0000000..b666a2b --- /dev/null +++ b/lib/ipmi_fwum.c @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2004 Kontron Canada, Inc. All Rights Reserved. + * + * Base on code from + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <string.h> +#include <math.h> +#include <time.h> +#include <unistd.h> + +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_fwum.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_mc.h> + +extern int verbose; +unsigned char firmBuf[1024*512]; +tKFWUM_SaveFirmwareInfo save_fw_nfo; + +int KfwumGetFileSize(const char *pFileName, + unsigned long *pFileSize); +int KfwumSetupBuffersFromFile(const char *pFileName, + unsigned long fileSize); +void KfwumShowProgress(const char *task, unsigned long current, + unsigned long total); +unsigned short KfwumCalculateChecksumPadding(unsigned char *pBuffer, + unsigned long totalSize); +int KfwumGetInfo(struct ipmi_intf *intf, unsigned char output, + unsigned char *pNumBank); +int KfwumGetDeviceInfo(struct ipmi_intf *intf, + unsigned char output, tKFWUM_BoardInfo *pBoardInfo); +int KfwumGetStatus(struct ipmi_intf *intf); +int KfwumManualRollback(struct ipmi_intf *intf); +int KfwumStartFirmwareImage(struct ipmi_intf *intf, + unsigned long length, unsigned short padding); +int KfwumSaveFirmwareImage(struct ipmi_intf *intf, + unsigned char sequenceNumber, unsigned long address, + unsigned char *pFirmBuf, unsigned char *pInBufLength); +int KfwumFinishFirmwareImage(struct ipmi_intf *intf, + tKFWUM_InFirmwareInfo firmInfo); +int KfwumUploadFirmware(struct ipmi_intf *intf, + unsigned char *pBuffer, unsigned long totalSize); +int KfwumStartFirmwareUpgrade(struct ipmi_intf *intf); +int KfwumGetInfoFromFirmware(unsigned char *pBuf, + unsigned long bufSize, tKFWUM_InFirmwareInfo *pInfo); +void KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo *pInfo); +int KfwumGetTraceLog(struct ipmi_intf *intf); +int ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo); + +int ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action); +int ipmi_fwum_info(struct ipmi_intf *intf); +int ipmi_fwum_status(struct ipmi_intf *intf); +void printf_kfwum_help(void); +void printf_kfwum_info(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo); + +/* String table */ +/* Must match eFWUM_CmdId */ +const char *CMD_ID_STRING[] = { + "GetFwInfo", + "KickWatchdog", + "GetLastAnswer", + "BootHandshake", + "ReportStatus", + "CtrlIPMBLine", + "SetFwState", + "GetFwStatus", + "GetSpiMemStatus", + "StartFwUpdate", + "StartFwImage", + "SaveFwImage", + "FinishFwImage", + "ReadFwImage", + "ManualRollback", + "GetTraceLog" +}; + +const char *EXT_CMD_ID_STRING[] = { + "FwUpgradeLock", + "ProcessFwUpg", + "ProcessFwRb", + "WaitHSAfterUpg", + "WaitFirstHSUpg", + "FwInfoStateChange" +}; + +const char *CMD_STATE_STRING[] = { + "Invalid", + "Begin", + "Progress", + "Completed" +}; + +const struct valstr bankStateValS[] = { + { 0x00, "Not programmed" }, + { 0x01, "New firmware" }, + { 0x02, "Wait for validation" }, + { 0x03, "Last Known Good" }, + { 0x04, "Previous Good" } +}; + +/* ipmi_fwum_main - entry point for this ipmitool mode + * + * @intf: ipmi interface + * @arc: number of arguments + * @argv: point to argument array + * + * returns 0 on success + * returns -1 on error + */ +int +ipmi_fwum_main(struct ipmi_intf *intf, int argc, char **argv) +{ + int rc = 0; + printf("FWUM extension Version %d.%d\n", VER_MAJOR, VER_MINOR); + if (argc < 1) { + lprintf(LOG_ERR, "Not enough parameters given."); + printf_kfwum_help(); + return (-1); + } + if (strncmp(argv[0], "help", 4) == 0) { + printf_kfwum_help(); + rc = 0; + } else if (strncmp(argv[0], "info", 4) == 0) { + rc = ipmi_fwum_info(intf); + } else if (strncmp(argv[0], "status", 6) == 0) { + rc = ipmi_fwum_status(intf); + } else if (strncmp(argv[0], "rollback", 8) == 0) { + rc = KfwumManualRollback(intf); + } else if (strncmp(argv[0], "download", 8) == 0) { + if ((argc < 2) || (strlen(argv[1]) < 1)) { + lprintf(LOG_ERR, + "Path and file name must be specified."); + return (-1); + } + printf("Firmware File Name : %s\n", argv[1]); + rc = ipmi_fwum_fwupgrade(intf, argv[1], 0); + } else if (strncmp(argv[0], "upgrade", 7) == 0) { + if ((argc >= 2) && (strlen(argv[1]) > 0)) { + printf("Upgrading using file name %s\n", argv[1]); + rc = ipmi_fwum_fwupgrade(intf, argv[1], 1); + } else { + rc = KfwumStartFirmwareUpgrade(intf); + } + } else if (strncmp(argv[0], "tracelog", 8) == 0) { + rc = KfwumGetTraceLog(intf); + } else { + lprintf(LOG_ERR, "Invalid KFWUM command: %s", argv[0]); + printf_kfwum_help(); + rc = (-1); + } + return rc; +} + +void +printf_kfwum_help(void) +{ + lprintf(LOG_NOTICE, +"KFWUM Commands: info status download upgrade rollback tracelog"); +} + +/* private definitions and macros */ +typedef enum eFWUM_CmdId +{ + KFWUM_CMD_ID_GET_FIRMWARE_INFO = 0, + KFWUM_CMD_ID_KICK_IPMC_WATCHDOG = 1, + KFWUM_CMD_ID_GET_LAST_ANSWER = 2, + KFWUM_CMD_ID_BOOT_HANDSHAKE = 3, + KFWUM_CMD_ID_REPORT_STATUS = 4, + KFWUM_CMD_ID_GET_FIRMWARE_STATUS = 7, + KFWUM_CMD_ID_START_FIRMWARE_UPDATE = 9, + KFWUM_CMD_ID_START_FIRMWARE_IMAGE = 0x0a, + KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE = 0x0b, + KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE = 0x0c, + KFWUM_CMD_ID_READ_FIRMWARE_IMAGE = 0x0d, + KFWUM_CMD_ID_MANUAL_ROLLBACK = 0x0e, + KFWUM_CMD_ID_GET_TRACE_LOG = 0x0f, + KFWUM_CMD_ID_STD_MAX_CMD, + KFWUM_CMD_ID_EXTENDED_CMD = 0xC0 +} tKFWUM_CmdId; + +int +ipmi_fwum_info(struct ipmi_intf *intf) +{ + tKFWUM_BoardInfo b_info; + int rc = 0; + unsigned char not_used; + if (verbose) { + printf("Getting Kontron FWUM Info\n"); + } + if (KfwumGetDeviceInfo(intf, 1, &b_info) != 0) { + rc = (-1); + } + if (KfwumGetInfo(intf, 1, ¬_used) != 0) { + rc = (-1); + } + return rc; +} + +int +ipmi_fwum_status(struct ipmi_intf *intf) +{ + if (verbose) { + printf("Getting Kontron FWUM Status\n"); + } + if (KfwumGetStatus(intf) != 0) { + return (-1); + } + return 0; +} + +/* ipmi_fwum_fwupgrade - function implements download/upload of the firmware + * data received as parameters + * + * @file: fw file + * @action: 0 = download, 1 = upload/start upload + * + * returns 0 on success, otherwise (-1) + */ +int +ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action) +{ + tKFWUM_BoardInfo b_info; + tKFWUM_InFirmwareInfo fw_info = { 0 }; + unsigned short padding; + unsigned long fsize = 0; + unsigned char not_used; + if (file == NULL) { + lprintf(LOG_ERR, "No file given."); + return (-1); + } + if (KfwumGetFileSize(file, &fsize) != 0) { + return (-1); + } + if (KfwumSetupBuffersFromFile(file, fsize) != 0) { + return (-1); + } + padding = KfwumCalculateChecksumPadding(firmBuf, fsize); + if (KfwumGetInfoFromFirmware(firmBuf, fsize, &fw_info) != 0) { + return (-1); + } + if (KfwumGetDeviceInfo(intf, 0, &b_info) != 0) { + return (-1); + } + if (ipmi_kfwum_checkfwcompat(b_info, fw_info) != 0) { + return (-1); + } + KfwumGetInfo(intf, 0, ¬_used); + printf_kfwum_info(b_info, fw_info); + if (KfwumStartFirmwareImage(intf, fsize, padding) != 0) { + return (-1); + } + if (KfwumUploadFirmware(intf, firmBuf, fsize) != 0) { + return (-1); + } + if (KfwumFinishFirmwareImage(intf, fw_info) != 0) { + return (-1); + } + if (KfwumGetStatus(intf) != 0) { + return (-1); + } + if (action != 0) { + if (KfwumStartFirmwareUpgrade(intf) != 0) { + return (-1); + } + } + return 0; +} + +/* KfwumGetFileSize - gets the file size + * + * @pFileName : filename ptr + * @pFileSize : output ptr for filesize + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetFileSize(const char *pFileName, unsigned long *pFileSize) +{ + FILE *pFileHandle = NULL; + pFileHandle = fopen(pFileName, "rb"); + if (pFileHandle == NULL) { + return (-1); + } + if (fseek(pFileHandle, 0L , SEEK_END) == 0) { + *pFileSize = ftell(pFileHandle); + } + fclose(pFileHandle); + if (*pFileSize != 0) { + return 0; + } + return (-1); +} + +/* KfwumSetupBuffersFromFile - small buffers are used to store the file data + * + * @pFileName : filename ptr + * unsigned long : filesize + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumSetupBuffersFromFile(const char *pFileName, unsigned long fileSize) +{ + int rc = (-1); + FILE *pFileHandle = NULL; + int count; + int modulus; + int qty = 0; + + pFileHandle = fopen(pFileName, "rb"); + if (pFileHandle == NULL) { + lprintf(LOG_ERR, "Failed to open '%s' for reading.", + pFileName); + return (-1); + } + count = fileSize / MAX_BUFFER_SIZE; + modulus = fileSize % MAX_BUFFER_SIZE; + + rewind(pFileHandle); + for (qty = 0; qty < count; qty++) { + KfwumShowProgress("Reading Firmware from File", + qty, count); + if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1, + MAX_BUFFER_SIZE, + pFileHandle) == MAX_BUFFER_SIZE) { + rc = 0; + } + } + if (modulus) { + if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1, + modulus, pFileHandle) == modulus) { + rc = 0; + } + } + if (rc == 0) { + KfwumShowProgress("Reading Firmware from File", 100, 100); + } + fclose(pFileHandle); + return rc; +} + +/* KfwumShowProgress - helper routine to display progress bar + * + * Converts current/total in percent + * + * *task : string identifying current operation + * current: progress + * total : limit + */ +void +KfwumShowProgress(const char *task, unsigned long current, unsigned long total) +{ +# define PROG_LENGTH 42 + static unsigned long staticProgress=0xffffffff; + unsigned char spaces[PROG_LENGTH + 1]; + unsigned short hash; + float percent = ((float)current / total); + unsigned long progress = 100 * (percent); + + if (staticProgress == progress) { + /* We displayed the same last time.. so don't do it */ + return; + } + staticProgress = progress; + printf("%-25s : ", task); /* total 20 bytes */ + hash = (percent * PROG_LENGTH); + memset(spaces, '#', hash); + spaces[hash] = '\0'; + + printf("%s", spaces); + memset(spaces, ' ', (PROG_LENGTH - hash)); + spaces[(PROG_LENGTH - hash)] = '\0'; + printf("%s", spaces ); + + printf(" %3ld %%\r", progress); /* total 7 bytes */ + if (progress == 100) { + printf("\n"); + } + fflush(stdout); +} + +/* KfwumCalculateChecksumPadding - TBD + */ +unsigned short +KfwumCalculateChecksumPadding(unsigned char *pBuffer, unsigned long totalSize) +{ + unsigned short sumOfBytes = 0; + unsigned short padding; + unsigned long counter; + for (counter = 0; counter < totalSize; counter ++) { + sumOfBytes += pBuffer[counter]; + } + padding = 0 - sumOfBytes; + return padding; +} + +/* KfwumGetInfo - Get Firmware Update Manager (FWUM) information + * + * *intf : IPMI interface + * output : when set to non zero, queried information is displayed + * pNumBank: output ptr for number of banks + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetInfo(struct ipmi_intf *intf, unsigned char output, + unsigned char *pNumBank) +{ + int rc = 0; + static struct KfwumGetInfoResp *pGetInfo; + struct ipmi_rs *rsp; + struct ipmi_rq req; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_INFO; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + lprintf(LOG_ERR, "Error in FWUM Firmware Get Info Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "FWUM Firmware Get Info returned %x", + rsp->ccode); + return (-1); + } + + pGetInfo = (struct KfwumGetInfoResp *)rsp->data; + if (output) { + printf("\nFWUM info\n"); + printf("=========\n"); + printf("Protocol Revision : %02Xh\n", + pGetInfo->protocolRevision); + printf("Controller Device Id : %02Xh\n", + pGetInfo->controllerDeviceId); + printf("Firmware Revision : %u.%u%u", + pGetInfo->firmRev1, pGetInfo->firmRev2 >> 4, + pGetInfo->firmRev2 & 0x0f); + if (pGetInfo->byte.mode != 0) { + printf(" - DEBUG BUILD\n"); + } else { + printf("\n"); + } + printf("Number Of Memory Bank : %u\n", pGetInfo->numBank); + } + *pNumBank = pGetInfo->numBank; + /* Determine wich type of download to use: */ + /* Old FWUM or Old IPMC fw (data_len < 7) + * --> Address with small buffer size + */ + if ((pGetInfo->protocolRevision) <= 0x05 || (rsp->data_len < 7 )) { + save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_ADDRESS; + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + save_fw_nfo.overheadSize = KFWUM_OLD_CMD_OVERHEAD; + if (verbose) { + printf("Protocol Revision :"); + printf(" <= 5 detected, adjusting buffers\n"); + } + } else { + /* Both fw are using the new protocol */ + save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_SEQUENCE; + save_fw_nfo.overheadSize = KFWUM_NEW_CMD_OVERHEAD; + /* Buffer size depending on access type (Local or remote) */ + /* Look if we run remote or locally */ + if (verbose) { + printf("Protocol Revision :"); + printf(" > 5 optimizing buffers\n"); + } + if (strstr(intf->name,"lan") != NULL) { + /* also covers lanplus */ + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + if (verbose) { + printf("IOL payload size : %d\n", + save_fw_nfo.bufferSize); + } + } else if ((strstr(intf->name,"open")!= NULL) + && intf->target_addr != IPMI_BMC_SLAVE_ADDR + && (intf->target_addr != intf->my_addr)) { + save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER; + if (verbose) { + printf("IPMB payload size : %d\n", + save_fw_nfo.bufferSize); + } + } else { + save_fw_nfo.bufferSize = KFWUM_BIG_BUFFER; + if (verbose) { + printf("SMI payload size : %d\n", + save_fw_nfo.bufferSize); + } + } + } + return rc; +} + +/* KfwumGetDeviceInfo - Get IPMC/Board information + * + * *intf: IPMI interface + * output: when set to non zero, queried information is displayed + * tKFWUM_BoardInfo: output ptr for IPMC/Board information + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetDeviceInfo(struct ipmi_intf *intf, unsigned char output, + tKFWUM_BoardInfo *pBoardInfo) +{ + struct ipm_devid_rsp *pGetDevId; + struct ipmi_rs *rsp; + struct ipmi_rq req; + /* Send Get Device Id */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_GET_DEVICE_ID; + req.msg.data_len = 0; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get Device Id Command"); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Get Device Id returned %x", + rsp->ccode); + return (-1); + } + pGetDevId = (struct ipm_devid_rsp *)rsp->data; + pBoardInfo->iana = IPM_DEV_MANUFACTURER_ID(pGetDevId->manufacturer_id); + pBoardInfo->boardId = buf2short(pGetDevId->product_id); + if (output) { + printf("\nIPMC Info\n"); + printf("=========\n"); + printf("Manufacturer Id : %u\n", + pBoardInfo->iana); + printf("Board Id : %u\n", + pBoardInfo->boardId); + printf("Firmware Revision : %u.%u%u", + pGetDevId->fw_rev1, pGetDevId->fw_rev2 >> 4, + pGetDevId->fw_rev2 & 0x0f); + if (((pBoardInfo->iana == IPMI_OEM_KONTRON) + && (pBoardInfo->boardId = KFWUM_BOARD_KONTRON_5002))) { + printf(" SDR %u", pGetDevId->aux_fw_rev[0]); + } + printf("\n"); + } + return 0; +} + +/* KfwumGetStatus - Get (and prints) FWUM banks information + * + * *intf : IPMI interface + * + * returns 0 on success, otherwise (-1) + */ +int +KfwumGetStatus(struct ipmi_intf * intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumGetStatusResp *pGetStatus; + unsigned char numBank; + unsigned char counter; + unsigned long firmLength; + if (verbose) { + printf(" Getting Status!\n"); + } + /* Retreive the number of bank */ + rc = KfwumGetInfo(intf, 0, &numBank); + for(counter = 0; + (counter < numBank) && (rc == 0); + counter ++) { + /* Retreive the status of each bank */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_STATUS; + req.msg.data = &counter; + req.msg.data_len = 1; + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Get Status Command."); + rc = (-1); + break; + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Get Status returned %x", + rsp->ccode); + rc = (-1); + break; + } + pGetStatus = (struct KfwumGetStatusResp *) rsp->data; + printf("\nBank State %d : %s\n", + counter, + val2str(pGetStatus->bankState, bankStateValS)); + if (!pGetStatus->bankState) { + continue; + } + firmLength = pGetStatus->firmLengthMSB; + firmLength = firmLength << 8; + firmLength |= pGetStatus->firmLengthMid; + firmLength = firmLength << 8; + firmLength |= pGetStatus->firmLengthLSB; + printf("Firmware Length : %ld bytes\n", + firmLength); + printf("Firmware Revision : %u.%u%u SDR %u\n", + pGetStatus->firmRev1, + pGetStatus->firmRev2 >> 4, + pGetStatus->firmRev2 & 0x0f, + pGetStatus->firmRev3); + } + printf("\n"); + return rc; +} + +/* KfwumManualRollback - Ask IPMC to rollback to previous version + * + * *intf : IPMI interface + * + * returns 0 on success + * returns (-1) on error + */ +int +KfwumManualRollback(struct ipmi_intf *intf) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumManualRollbackReq thisReq; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_MANUAL_ROLLBACK; + thisReq.type = 0; /* Wait BMC shutdown */ + req.msg.data = (unsigned char *)&thisReq; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in FWUM Manual Rollback Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "Error in FWUM Manual Rollback Command returned %x", + rsp->ccode); + return (-1); + } + printf("FWUM Starting Manual Rollback \n"); + return 0; +} + +int +KfwumStartFirmwareImage(struct ipmi_intf *intf, unsigned long length, + unsigned short padding) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumStartFirmwareDownloadResp *pResp; + struct KfwumStartFirmwareDownloadReq thisReq; + + thisReq.lengthLSB = length & 0x000000ff; + thisReq.lengthMid = (length >> 8) & 0x000000ff; + thisReq.lengthMSB = (length >> 16) & 0x000000ff; + thisReq.paddingLSB = padding & 0x00ff; + thisReq.paddingMSB = (padding>> 8) & 0x00ff; + thisReq.useSequence = 0x01; + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_IMAGE; + req.msg.data = (unsigned char *) &thisReq; + /* Look for download type */ + if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) { + req.msg.data_len = 5; + } else { + req.msg.data_len = 6; + } + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Start Firmware Image Download Command."); + return (-1); + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Start Firmware Image Download returned %x", + rsp->ccode); + return (-1); + } + pResp = (struct KfwumStartFirmwareDownloadResp *)rsp->data; + printf("Bank holding new firmware : %d\n", pResp->bank); + sleep(5); + return 0; +} + +int +KfwumSaveFirmwareImage(struct ipmi_intf *intf, unsigned char sequenceNumber, + unsigned long address, unsigned char *pFirmBuf, + unsigned char *pInBufLength) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumSaveFirmwareAddressReq addr_req; + struct KfwumSaveFirmwareSequenceReq seq_req; + int retry = 0; + int no_rsp = 0; + do { + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE; + if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) { + addr_req.addressLSB = address & 0x000000ff; + addr_req.addressMid = (address >> 8) & 0x000000ff; + addr_req.addressMSB = (address >> 16) & 0x000000ff; + addr_req.numBytes = *pInBufLength; + memcpy(addr_req.txBuf, pFirmBuf, *pInBufLength); + req.msg.data = (unsigned char *)&addr_req; + req.msg.data_len = *pInBufLength + 4; + } else { + seq_req.sequenceNumber = sequenceNumber; + memcpy(seq_req.txBuf, pFirmBuf, *pInBufLength); + req.msg.data = (unsigned char *)&seq_req; + req.msg.data_len = *pInBufLength + sizeof(unsigned char); + /* + 1 => sequenceNumber*/ + } + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Save Firmware Image Download Command."); + /* We don't receive "C7" on errors with IOL, + * instead we receive nothing + */ + if (strstr(intf->name, "lan") != NULL) { + no_rsp++; + if (no_rsp < FWUM_SAVE_FIRMWARE_NO_RESPONSE_LIMIT) { + *pInBufLength -= 1; + continue; + } + lprintf(LOG_ERR, + "Error, too many commands without response."); + *pInBufLength = 0; + break; + } /* For other interface keep trying */ + } else if (rsp->ccode != 0) { + if (rsp->ccode == 0xc0) { + sleep(1); + } else if ((rsp->ccode == 0xc7) + || ((rsp->ccode == 0xc3) + && (sequenceNumber == 0))) { + *pInBufLength -= 1; + retry = 1; + } else if (rsp->ccode == 0x82) { + /* Double sent, continue */ + rc = 0; + break; + } else if (rsp->ccode == 0x83) { + if (retry == 0) { + retry = 1; + continue; + } + rc = (-1); + break; + } else if (rsp->ccode == 0xcf) { + /* Ok if receive duplicated request */ + retry = 1; + } else if (rsp->ccode == 0xc3) { + if (retry == 0) { + retry = 1; + continue; + } + rc = (-1); + break; + } else { + lprintf(LOG_ERR, + "FWUM Firmware Save Firmware Image Download returned %x", + rsp->ccode); + rc = (-1); + break; + } + } else { + break; + } + } while (1); + return rc; +} + +int +KfwumFinishFirmwareImage(struct ipmi_intf *intf, tKFWUM_InFirmwareInfo firmInfo) +{ + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct KfwumFinishFirmwareDownloadReq thisReq; + + thisReq.versionMaj = firmInfo.versMajor; + thisReq.versionMinSub = ((firmInfo.versMinor <<4) + | firmInfo.versSubMinor); + thisReq.versionSdr = firmInfo.sdrRev; + thisReq.reserved = 0; + /* Byte 4 reserved, write 0 */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE; + req.msg.data = (unsigned char *)&thisReq; + req.msg.data_len = 4; + /* Infinite loop if BMC doesn't reply or replies 0xc0 every time. */ + do { + rsp = intf->sendrecv(intf, &req); + } while (rsp == NULL || rsp->ccode == 0xc0); + if (!rsp) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Finish Firmware Image Download Command."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, + "FWUM Firmware Finish Firmware Image Download returned %x", + rsp->ccode); + return (-1); + } + return 0; +} + +int +KfwumUploadFirmware(struct ipmi_intf *intf, unsigned char *pBuffer, + unsigned long totalSize) +{ + int rc = (-1); + unsigned long address = 0x0; + unsigned char writeSize; + unsigned char oldWriteSize; + unsigned long lastAddress = 0; + unsigned char sequenceNumber = 0; + unsigned char retry = FWUM_MAX_UPLOAD_RETRY; + unsigned char isLengthValid = 1; + do { + writeSize = save_fw_nfo.bufferSize - save_fw_nfo.overheadSize; + /* Reach the end */ + if (address + writeSize > totalSize) { + writeSize = (totalSize - address); + } else if (((address % KFWUM_PAGE_SIZE) + + writeSize) > KFWUM_PAGE_SIZE) { + /* Reach boundary end */ + writeSize = (KFWUM_PAGE_SIZE - (address % KFWUM_PAGE_SIZE)); + } + oldWriteSize = writeSize; + rc = KfwumSaveFirmwareImage(intf, sequenceNumber, + address, &pBuffer[address], &writeSize); + if ((rc != 0) && (retry-- != 0)) { + address = lastAddress; + rc = 0; + } else if ( writeSize == 0) { + rc = (-1); + } else { + if (writeSize != oldWriteSize) { + printf("Adjusting length to %d bytes \n", + writeSize); + save_fw_nfo.bufferSize -= (oldWriteSize - writeSize); + } + retry = FWUM_MAX_UPLOAD_RETRY; + lastAddress = address; + address+= writeSize; + } + if (rc == 0) { + if ((address % 1024) == 0) { + KfwumShowProgress("Writting Firmware in Flash", + address, totalSize); + } + sequenceNumber++; + } + } while ((rc == 0) && (address < totalSize)); + if (rc == 0) { + KfwumShowProgress("Writting Firmware in Flash", + 100, 100); + } + return rc; +} + +int +KfwumStartFirmwareUpgrade(struct ipmi_intf *intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + /* Upgrade type, wait BMC shutdown */ + unsigned char upgType = 0 ; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_UPDATE; + req.msg.data = (unsigned char *) &upgType; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Start Firmware Upgrade Command"); + rc = (-1); + } else if (rsp->ccode) { + if (rsp->ccode == 0xd5) { + lprintf(LOG_ERR, + "No firmware available for upgrade. Download Firmware first."); + } else { + lprintf(LOG_ERR, + "FWUM Firmware Start Firmware Upgrade returned %x", + rsp->ccode); + } + rc = (-1); + } + return rc; +} + +int +KfwumGetTraceLog(struct ipmi_intf *intf) +{ + int rc = 0; + struct ipmi_rs *rsp; + struct ipmi_rq req; + unsigned char chunkIdx; + unsigned char cmdIdx; + if (verbose) { + printf(" Getting Trace Log!\n"); + } + for (chunkIdx = 0; + (chunkIdx < TRACE_LOG_CHUNK_COUNT) + && (rc == 0); + chunkIdx++) { + /* Retreive each log chunk and print it */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_FIRMWARE; + req.msg.cmd = KFWUM_CMD_ID_GET_TRACE_LOG; + req.msg.data = &chunkIdx; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Error in FWUM Firmware Get Trace Log Command"); + rc = (-1); + break; + } else if (rsp->ccode) { + lprintf(LOG_ERR, + "FWUM Firmware Get Trace Log returned %x", + rsp->ccode); + rc = (-1); + break; + } + for (cmdIdx=0; cmdIdx < TRACE_LOG_CHUNK_SIZE; cmdIdx++) { + /* Don't diplay commands with an invalid state */ + if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0) + && (rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] < KFWUM_CMD_ID_STD_MAX_CMD)) { + printf(" Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n", + CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx]], + CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]], + rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]); + } else if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0) + && (rsp->data[TRACE_LOG_ATT_COUNT*cmdIdx] >= KFWUM_CMD_ID_EXTENDED_CMD)) { + printf(" Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n", + EXT_CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] - KFWUM_CMD_ID_EXTENDED_CMD], + CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]], + rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]); + } + } + } + printf("\n"); + return rc; +} + +int +KfwumGetInfoFromFirmware(unsigned char *pBuf, unsigned long bufSize, + tKFWUM_InFirmwareInfo *pInfo) +{ + unsigned long offset = 0; + if (bufSize < (IN_FIRMWARE_INFO_OFFSET_LOCATION + IN_FIRMWARE_INFO_SIZE)) { + return (-1); + } + offset = IN_FIRMWARE_INFO_OFFSET_LOCATION; + + /* Now, fill the structure with read informations */ + pInfo->checksum = (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + 0 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM ) << 8; + + pInfo->checksum|= (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + 1 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM); + + pInfo->sumToRemoveFromChecksum = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM); + + pInfo->sumToRemoveFromChecksum+= KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM + 1); + + pInfo->fileSize = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 0) << 24; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 1) << 16; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 2) << 8; + + pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 3); + + pInfo->boardId = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 0) << 8; + + pInfo->boardId|= KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 1); + + pInfo->deviceId = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_DEVICE_ID); + + pInfo->tableVers = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_TABLE_VERSION); + + pInfo->implRev = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IMPLEMENT_REV); + + pInfo->versMajor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + + IN_FIRMWARE_INFO_OFFSET_VER_MAJOROR)) & 0x0f; + + pInfo->versMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + + IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB) >> 4) & 0x0f; + + pInfo->versSubMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB)) & 0x0f; + + pInfo->sdrRev = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_SDR_REV); + + pInfo->iana = KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA2) << 16; + + pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA1) << 8; + + pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf, + offset + IN_FIRMWARE_INFO_OFFSET_IANA0); + + KfwumFixTableVersionForOldFirmware(pInfo); + return 0; +} + +void +KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo * pInfo) +{ + switch(pInfo->boardId) { + case KFWUM_BOARD_KONTRON_UNKNOWN: + pInfo->tableVers = 0xff; + break; + default: + /* pInfo->tableVers is already set for + * the right version + */ + break; + } +} + +/* ipmi_kfwum_checkfwcompat - check whether firmware we're about to upload is + * compatible with board. + * + * @boardInfo: + * @firmInfo: + * + * returns 0 if compatible, otherwise (-1) + */ +int +ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo, + tKFWUM_InFirmwareInfo firmInfo) +{ + int compatible = 0; + if (boardInfo.iana != firmInfo.iana) { + lprintf(LOG_ERR, + "Board IANA does not match firmware IANA."); + compatible = (-1); + } + if (boardInfo.boardId != firmInfo.boardId) { + lprintf(LOG_ERR, + "Board IANA does not match firmware IANA."); + compatible = (-1); + } + if (compatible != 0) { + lprintf(LOG_ERR, + "Firmware invalid for target board. Download of upgrade aborted."); + } + return compatible; +} + +void +printf_kfwum_info(tKFWUM_BoardInfo boardInfo, tKFWUM_InFirmwareInfo firmInfo) +{ + printf( +"Target Board Id : %u\n", boardInfo.boardId); + printf( +"Target IANA number : %u\n", boardInfo.iana); + printf( +"File Size : %lu bytes\n", firmInfo.fileSize); + printf( +"Firmware Version : %d.%d%d SDR %d\n", firmInfo.versMajor, +firmInfo.versMinor, firmInfo.versSubMinor, firmInfo.sdrRev); +} |