/* * 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 #include #include #include #include #include #include #include #include #include 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->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; 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("Writing Firmware in Flash", address, totalSize); } sequenceNumber++; } } while ((rc == 0) && (address < totalSize)); if (rc == 0) { KfwumShowProgress("Writing 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); }