summaryrefslogtreecommitdiff
path: root/lib/ipmi_fwum.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ipmi_fwum.c')
-rw-r--r--lib/ipmi_fwum.c1132
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, &not_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, &not_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);
+}