text: /*
   * 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->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);
  }
  