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