/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * The reference for the functions in this file is the
 *
 *	Mellanox HCA Flash Programming Application Note
 * (Mellanox document number 2205AN) rev 1.45, 2007.
 * Chapter 4 in particular.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/queue.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>

#include <sys/byteorder.h>

#include <libintl.h> /* for gettext(3c) */

#include <fwflash/fwflash.h>
#include "../../hdrs/hermon_ib.h"

char *devprefix = "/devices";
char drivername[] = "hermon\0";
char *devsuffix = ":devctl";

extern di_node_t rootnode;
extern int errno;
extern struct fw_plugin *self;
extern struct vrfyplugin *verifier;
extern int fwflash_debug;

/* required functions for this plugin */
int fw_readfw(struct devicelist *device, char *filename);
int fw_writefw(struct devicelist *device);
int fw_identify(int start);
int fw_devinfo();


/* helper functions */
static int cnx_identify(struct devicelist *thisdev);
static int cnx_get_guids(ib_cnx_encap_ident_t *handle);
static int cnx_close(struct devicelist *flashdev);
static int cnx_check_for_magic_pattern(ib_cnx_encap_ident_t *hdl, uint32_t adr);
static uint32_t cnx_get_log2_chunk_size_f_hdl(ib_cnx_encap_ident_t *handle,
    int type);
static uint32_t cnx_get_log2_chunk_size(uint32_t chunk_size_word);
static uint32_t cnx_cont2phys(uint32_t log2_chunk_sz, uint32_t cont_addr,
    int type);
static uint32_t cnx_get_image_size_f_hdl(ib_cnx_encap_ident_t *hdl, int type);
static void cnx_local_set_guid_crc_img(uint32_t offset, uint32_t guid_crc_size,
    uint32_t guid_crc_offset);
static int cnx_read_image(ib_cnx_encap_ident_t *handle);
static int cnx_write_file(ib_cnx_encap_ident_t *handle, const char *filename);
static int cnx_verify_image(ib_cnx_encap_ident_t *handle, int type);
static int cnx_read_guids(ib_cnx_encap_ident_t *handle, int type);
static int cnx_set_guids(ib_cnx_encap_ident_t *handle, void *arg);
static int cnx_write_image(ib_cnx_encap_ident_t *handle, int type);
static int cnx_read_ioctl(ib_cnx_encap_ident_t *hdl,
    hermon_flash_ioctl_t *info);
static int cnx_write_ioctl(ib_cnx_encap_ident_t *hdl,
    hermon_flash_ioctl_t *info);
static int cnx_erase_sector_ioctl(ib_cnx_encap_ident_t *hdl,
    hermon_flash_ioctl_t *info);
static int cnx_find_magic_n_chnk_sz(ib_cnx_encap_ident_t *handle, int type);
static int cnx_get_image_info(ib_cnx_encap_ident_t *handle);


int
fw_readfw(struct devicelist *flashdev, char *filename)
{
	ib_cnx_encap_ident_t	*manuf;
	int 			rv = FWFLASH_SUCCESS;

	logmsg(MSG_INFO, "hermon: fw_readfw: filename %s\n", filename);

	manuf = (ib_cnx_encap_ident_t *)flashdev->ident->encap_ident;
	if (CNX_I_CHECK_HANDLE(manuf)) {
		logmsg(MSG_ERROR, gettext("hermon: Invalid Handle for "
		    "device %s! \n"), flashdev->access_devname);
		return (FWFLASH_FAILURE);
	}

	logmsg(MSG_INFO, "hermon: fw_identify should have read the image. "
	    "state 0x%x\n", manuf->state);

	rv = cnx_read_image(manuf);
	if (rv != FWFLASH_SUCCESS) {
		logmsg(MSG_ERROR, gettext("hermon: Failed to read any valid "
		    "image on device (%s)\n"), flashdev->access_devname);
		logmsg(MSG_ERROR, gettext("Aborting read.\n"));
	} else {
		rv = cnx_write_file(manuf, filename);
	}

	cnx_close(flashdev);
	return (rv);
}


/*
 * If we're invoking fw_writefw, then flashdev is a valid,
 * flashable device as determined by fw_identify().
 *
 * If verifier is null, then we haven't been called following a firmware
 * image verification load operation.
 */
int
fw_writefw(struct devicelist *flashdev)
{
	ib_cnx_encap_ident_t	*manuf;
	int			i, j, k;

	logmsg(MSG_INFO, "hermon: fw_writefw\n");

	manuf = (ib_cnx_encap_ident_t *)flashdev->ident->encap_ident;

	if (CNX_I_CHECK_HANDLE(manuf)) {
		logmsg(MSG_ERROR, gettext("hermon: Invalid Handle for "
		    "device %s! \n"), flashdev->access_devname);
		return (FWFLASH_FAILURE);
	}

	/*
	 * Try the primary first, then the secondary.
	 * If we get here, then the verifier has _already_ checked that
	 * the part number in the firmware image matches that in the HCA,
	 * so we only need this check if there's no hardware info available
	 * already after running through fw_identify().
	 */
	if (manuf->pn_len == 0) {
		int resp;

		(void) fprintf(stderr, gettext("Unable to completely verify "
		    "that this firmware image (%s) is compatible with your "
		    "HCA %s"), verifier->imgfile, flashdev->access_devname);
		(void) fprintf(stderr, gettext("Do you really want to "
		    "continue? (Y/N): "));
		(void) fflush(stdin);
		resp = getchar();
		if (resp != 'Y' && resp != 'y') {
			(void) fprintf(stderr, gettext("Not proceeding with "
			    "flash operation of %s on %s"),
			    verifier->imgfile, flashdev->access_devname);
			return (FWFLASH_FAILURE);
		}
	}

	logmsg(MSG_INFO, "hermon: fw_writefw: Using Existing GUIDs.\n");
	manuf->state |=
	    FWFLASH_IB_STATE_GUIDN |
	    FWFLASH_IB_STATE_GUID1 |
	    FWFLASH_IB_STATE_GUID2 |
	    FWFLASH_IB_STATE_GUIDS;
	if (cnx_set_guids(manuf, manuf->ibguids) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: Failed to set GUIDs"));
	}

	/*
	 * Update both Primary and Secondary images
	 *
	 * For Failsafe firmware image update, if the current image (i.e.
	 * containing a magic pattern) on the Flash is stored on the Primary
	 * location, burn the new image to the Secondary location first,
	 * or vice versa.
	 */

	/* Note Current Image location. */
	j = manuf->state &
	    (FWFLASH_IB_STATE_IMAGE_PRI | FWFLASH_IB_STATE_IMAGE_SEC);

	/*
	 * If we find that current image location is not found, no worries
	 * we shall default to PRIMARY, and proceed with burning anyway.
	 */
	if (j == 0)
		j = FWFLASH_IB_STATE_IMAGE_PRI;

	for (i = FWFLASH_FLASH_IMAGES; i > 0; i--) {
		char *type;

		if (i == 2) {
			if (j == 2)
				k = 1;	/* Burn PRI First */
			else
				k = 2;	/* Burn SEC First */
		} else {
			if (k == 2)
				k = 1;	/* Burn PRI next */
			else
				k = 2;	/* Burn SEC next */
		}
		type = ((k == 1) ? "Primary" : "Secondary");

		logmsg(MSG_INFO, "hermon: fw_write: UPDATING %s image\n", type);

		if (cnx_write_image(manuf, k) != FWFLASH_SUCCESS) {
			(void) fprintf(stderr,
			    gettext("Failed to update %s image on device %s"),
			    type, flashdev->access_devname);
			goto out;
		}

		logmsg(MSG_INFO, "hermon: fw_write: Verify %s image..\n", type);
		if (cnx_verify_image(manuf, k) != FWFLASH_SUCCESS) {
			(void) fprintf(stderr,
			    gettext("Failed to verify %s image for device %s"),
			    type, flashdev->access_devname);
			goto out;
		}
	}
out:
	/* final update marker to the user */
	(void) printf(" +\n");
	return (cnx_close(flashdev));
}


/*
 * The fw_identify() function walks the device tree trying to find
 * devices which this plugin can work with.
 *
 * The parameter "start" gives us the starting index number
 * to give the device when we add it to the fw_devices list.
 *
 * firstdev is allocated by us and we add space as necessary
 */
int
fw_identify(int start)
{
	int		rv = FWFLASH_FAILURE;
	di_node_t	thisnode;
	struct devicelist *newdev;
	char		*devpath;
	int		idx = start;
	int		devlength = 0;

	logmsg(MSG_INFO, "hermon: fw_identify\n");
	thisnode = di_drv_first_node(drivername, rootnode);

	if (thisnode == DI_NODE_NIL) {
		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
		    drivername);
		return (rv);
	}

	/* we've found one, at least */
	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {

		devpath = di_devfs_path(thisnode);

		if ((newdev = calloc(1, sizeof (struct devicelist))) == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to allocate "
			    "space for device entry\n"));
			di_devfs_path_free(devpath);
			return (FWFLASH_FAILURE);
		}

		/* calloc enough for /devices + devpath + ":devctl" + '\0' */
		devlength = strlen(devpath) + strlen(devprefix) +
		    strlen(devsuffix) + 2;

		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to allocate "
			    "space for a devfs name\n"));
			(void) free(newdev);
			di_devfs_path_free(devpath);
			return (FWFLASH_FAILURE);
		}
		snprintf(newdev->access_devname, devlength,
		    "%s%s%s", devprefix, devpath, devsuffix);

		if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to allocate "
			    "space for a device identification record\n"));
			(void) free(newdev->access_devname);
			(void) free(newdev);
			di_devfs_path_free(devpath);
			return (FWFLASH_FAILURE);
		}

		/* CHECK VARIOUS IB THINGS HERE */
		rv = cnx_identify(newdev);
		if (rv == FWFLASH_FAILURE) {
			(void) free(newdev->ident);
			(void) free(newdev->access_devname);
			(void) free(newdev);
			di_devfs_path_free(devpath);
			continue;
		}

		if ((newdev->drvname = calloc(1, strlen(drivername) + 1))
		    == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to allocate"
			    " space for a driver name\n"));
			(void) free(newdev->ident);
			(void) free(newdev->access_devname);
			(void) free(newdev);
			di_devfs_path_free(devpath);
			return (FWFLASH_FAILURE);
		}

		(void) strlcpy(newdev->drvname, drivername,
		    strlen(drivername) + 1);

		/* this next bit is backwards compatibility - "IB\0" */
		if ((newdev->classname = calloc(1, 3)) == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to allocate "
			    "space for a class name\n"));
			(void) free(newdev->drvname);
			(void) free(newdev->ident);
			(void) free(newdev->access_devname);
			(void) free(newdev);
			di_devfs_path_free(devpath);
			return (FWFLASH_FAILURE);
		}
		(void) strlcpy(newdev->classname, "IB", 3);

		newdev->index = idx;
		++idx;
		newdev->plugin = self;

		di_devfs_path_free(devpath);

		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
	}

	if (fwflash_debug != 0) {
		struct devicelist *tempdev;

		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
			logmsg(MSG_INFO, "fw_identify: hermon:\n");
			logmsg(MSG_INFO, "\ttempdev @ 0x%lx\n"
			    "\t\taccess_devname: %s\n"
			    "\t\tdrvname: %s\tclassname: %s\n"
			    "\t\tident->vid:   %s\n"
			    "\t\tident->pid:   %s\n"
			    "\t\tident->revid: %s\n"
			    "\t\tindex: %d\n"
			    "\t\tguid0: %s\n"
			    "\t\tguid1: %s\n"
			    "\t\tguid2: %s\n"
			    "\t\tguid3: %s\n"
			    "\t\tplugin @ 0x%lx\n\n",
			    &tempdev,
			    tempdev->access_devname,
			    tempdev->drvname, newdev->classname,
			    tempdev->ident->vid,
			    tempdev->ident->pid,
			    tempdev->ident->revid,
			    tempdev->index,
			    tempdev->addresses[0],
			    tempdev->addresses[1],
			    tempdev->addresses[2],
			    tempdev->addresses[3],
			    tempdev->plugin);
		}
	}

	return (FWFLASH_SUCCESS);
}


int
fw_devinfo(struct devicelist *thisdev)
{
	ib_cnx_encap_ident_t	*encap;

	logmsg(MSG_INFO, "hermon: fw_devinfo\n");

	encap = (ib_cnx_encap_ident_t *)thisdev->ident->encap_ident;
	if (CNX_I_CHECK_HANDLE(encap)) {
		logmsg(MSG_ERROR, gettext("hermon: fw_devinfo: Invalid handle "
		    "for device %s! \n"), thisdev->access_devname);
		return (FWFLASH_FAILURE);
	}

	/* Try the primary first, then the secondary */
	fprintf(stdout, gettext("Device[%d] %s\n"),
	    thisdev->index, thisdev->access_devname);
	fprintf(stdout, gettext("Class [%s]\n"), thisdev->classname);

	fprintf(stdout, "\t");

	/* Mellanox HCA Flash app note, p40, #4.2.3 table 9 */
	fprintf(stdout, gettext("GUID: System Image - %s\n"),
	    thisdev->addresses[3]);
	fprintf(stdout, gettext("\t\tNode Image - %s\n"),
	    thisdev->addresses[0]);
	fprintf(stdout, gettext("\t\tPort 1\t   - %s\n"),
	    thisdev->addresses[1]);
	fprintf(stdout, gettext("\t\tPort 2\t   - %s\n"),
	    thisdev->addresses[2]);

	fprintf(stdout, gettext("\tFirmware revision  : %s\n"),
	    thisdev->ident->revid);

	if (encap->pn_len != 0) {
		if (strlen(encap->info.mlx_id))
			fprintf(stdout, gettext("\tProduct\t\t   : %s (%s)\n"),
			    encap->info.mlx_pn, encap->info.mlx_id);
		else
			fprintf(stdout, gettext("\tProduct\t\t   : %s \n"),
			    encap->info.mlx_pn);

		if (strlen(encap->info.mlx_psid))
			fprintf(stdout, gettext("\tPSID\t\t   : %s\n"),
			    encap->info.mlx_psid);
		else if (strlen(thisdev->ident->pid))
			fprintf(stdout, gettext("\t%s\n"), thisdev->ident->pid);
	} else {
		fprintf(stdout, gettext("\t%s\n"), thisdev->ident->pid);
	}
	fprintf(stdout, "\n\n");

	return (cnx_close(thisdev));
}


/*
 * Helper functions lurk beneath this point
 */


/*
 * Notes:
 * 1. flash read is done in 32 bit quantities, and the driver returns
 *    data in host byteorder form.
 * 2. flash write is done in 8 bit quantities by the driver.
 * 3. data in the flash should be in network byteorder.
 * 4. data in image files is in network byteorder form.
 * 5. data in image structures in memory is kept in network byteorder.
 * 6. the functions in this file deal with data in host byteorder form.
 */

static int
cnx_read_image(ib_cnx_encap_ident_t *handle)
{
	hermon_flash_ioctl_t	ioctl_info;
	uint32_t		phys_addr;
	int			ret, i;
	int			image_size;
	int			type;

	type = handle->state &
	    (FWFLASH_IB_STATE_IMAGE_PRI | FWFLASH_IB_STATE_IMAGE_SEC);
	logmsg(MSG_INFO, "cnx_read_image: type %lx\n", type);

	if (type == 0) {
		logmsg(MSG_ERROR, gettext("cnx_read_image: Must read in "
		    "image first\n"));
		return (FWFLASH_FAILURE);
	}

	image_size = handle->fw_sz;
	if (image_size <= 0) {
		logmsg(MSG_ERROR, gettext("cnx_read_image: Invalid image size "
		    "0x%x for %s image\n"),
		    image_size, (type == 0x1 ? "Primary" : "Secondary"));
		return (FWFLASH_FAILURE);
	}

	logmsg(MSG_INFO, "hermon: fw_size: 0x%x\n", image_size);

	handle->fw = (uint32_t *)calloc(1, image_size);
	if (handle->fw == NULL) {
		logmsg(MSG_ERROR, gettext("cnx_read_image: Unable to allocate "
		    "memory for fw_img : (%s)\n"), strerror(errno));
		return (FWFLASH_FAILURE);
	}

	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;
	for (i = 0; i < image_size; i += 4) {
		phys_addr = cnx_cont2phys(handle->log2_chunk_sz, i, type);
		ioctl_info.af_addr = phys_addr;

		ret = cnx_read_ioctl(handle, &ioctl_info);
		if (ret != 0) {
			logmsg(MSG_ERROR, gettext("cnx_read_image: Failed to "
			    "read sector %d\n"), i);
			free(handle->fw);
			return (FWFLASH_FAILURE);
		}
		handle->fw[i / 4] = htonl(ioctl_info.af_quadlet);
	}

	for (i = 0; i < image_size; i += 4) {
		logmsg(MSG_INFO, "cnx_read_image: addr[0x%x] = 0x%08x\n", i,
		    ntohl(handle->fw[i / 4]));
	}

	return (FWFLASH_SUCCESS);
}

static int
cnx_write_file(ib_cnx_encap_ident_t *handle, const char *filename)
{
	FILE		*fp;
	int 		fd;
	mode_t		mode = S_IRUSR | S_IWUSR;
	int		len;

	logmsg(MSG_INFO, "cnx_write_file\n");

	errno = 0;
	if ((fd = open(filename, O_RDWR|O_CREAT|O_DSYNC, mode)) < 0) {
		logmsg(MSG_ERROR, gettext("hermon: Unable to open specified "
		    "file (%s) for writing: %s\n"), filename, strerror(errno));
		return (FWFLASH_FAILURE);
	}

	errno = 0;
	fp = fdopen(fd, "w");
	if (fp == NULL) {
		(void) fprintf(stderr, gettext("hermon: Unknown filename %s : "
		    "%s\n"), filename, strerror(errno));
		return (FWFLASH_FAILURE);
	}

	len = ntohl(handle->fw[CNX_IMG_SIZE_OFFSET / 4]);
	logmsg(MSG_INFO, "cnx_write_file: Writing to file. Length 0x%x\n", len);

	if (fwrite(&handle->fw[0], len, 1, fp) == 0) {
		(void) fprintf(stderr, gettext("hermon: fwrite failed"));
		perror("fwrite");
		(void) fclose(fp);
		return (FWFLASH_FAILURE);
	}
	(void) fclose(fp);
	return (FWFLASH_SUCCESS);
}

static int
cnx_verify_image(ib_cnx_encap_ident_t *handle, int type)
{
	uint32_t	new_start_addr;

	logmsg(MSG_INFO, "hermon: cnx_verify_image\n");

	new_start_addr = cnx_cont2phys(handle->log2_chunk_sz, 0, type);

	return (cnx_check_for_magic_pattern(handle, new_start_addr));
}

static int
cnx_set_guids(ib_cnx_encap_ident_t *handle, void *arg)
{
	uint32_t	addr;
	uint32_t	*guids;

	logmsg(MSG_INFO, "hermon: cnx_set_guids\n");

	guids = (uint32_t *)arg;
	addr = ntohl(verifier->fwimage[CNX_NGUIDPTR_OFFSET / 4]) / 4;
	logmsg(MSG_INFO, "cnx_set_guids: guid_start_addr: 0x%x\n", addr * 4);

	/*
	 * guids are supplied by callers as 64 bit values in host byteorder.
	 * Storage is in network byteorder.
	 */
#ifdef _BIG_ENDIAN
	if (handle->state & FWFLASH_IB_STATE_GUIDN) {
		verifier->fwimage[addr] = guids[0];
		verifier->fwimage[addr + 1] = guids[1];
	}

	if (handle->state & FWFLASH_IB_STATE_GUID1) {
		verifier->fwimage[addr + 2] = guids[2];
		verifier->fwimage[addr + 3] = guids[3];
	}

	if (handle->state & FWFLASH_IB_STATE_GUID2) {
		verifier->fwimage[addr + 4] = guids[4];
		verifier->fwimage[addr + 5] = guids[5];
	}

	if (handle->state & FWFLASH_IB_STATE_GUIDS) {
		verifier->fwimage[addr + 6] = guids[6];
		verifier->fwimage[addr + 7] = guids[7];
	}
#else
	if (handle->state & FWFLASH_IB_STATE_GUIDN) {
		verifier->fwimage[addr] = htonl(guids[1]);
		verifier->fwimage[addr + 1] = htonl(guids[0]);
	}

	if (handle->state & FWFLASH_IB_STATE_GUID1) {
		verifier->fwimage[addr + 2] = htonl(guids[3]);
		verifier->fwimage[addr + 3] = htonl(guids[2]);
	}

	if (handle->state & FWFLASH_IB_STATE_GUID2) {
		verifier->fwimage[addr + 4] = htonl(guids[5]);
		verifier->fwimage[addr + 5] = htonl(guids[4]);
	}

	if (handle->state & FWFLASH_IB_STATE_GUIDS) {
		verifier->fwimage[addr + 6] = htonl(guids[7]);
		verifier->fwimage[addr + 7] = htonl(guids[6]);
	}
#endif

	cnx_local_set_guid_crc_img((addr * 4) - 0x10, CNX_GUID_CRC16_SIZE,
	    CNX_GUID_CRC16_OFFSET);

	return (FWFLASH_SUCCESS);
}

/*
 * Notes: Burn the image
 *
 * 1. Erase the entire sector where the new image is to be burned.
 * 2. Burn the image WITHOUT the magic pattern. This marks the new image
 *    as invalid during the burn process. If the current image (i.e
 *    containing a magic pattern) on the Flash is stored on the even
 *    chunks (PRIMARY), burn the new image to the odd chunks (SECONDARY),
 *    or vice versa.
 * 3. Burn the magic pattern at the beginning of the new image on the Flash.
 *    This will validate the new image.
 * 4. Set the BootAddress register to its new location.
 */
static int
cnx_write_image(ib_cnx_encap_ident_t *handle, int type)
{
	hermon_flash_ioctl_t	ioctl_info;
	int			sector_size;
	int			size;
	int			i;
	uint32_t		new_start_addr;
	uint32_t		log2_chunk_sz;
	uint8_t			*fw;

	logmsg(MSG_INFO, "hermon: cnx_write_image\n");

	if (type == 0) {
		logmsg(MSG_ERROR, gettext("cnx_write_image: Must inform us "
		    " where to write.\n"));
		return (FWFLASH_FAILURE);
	}

	log2_chunk_sz = cnx_get_log2_chunk_size(
	    ntohl(verifier->fwimage[CNX_CHUNK_SIZE_OFFSET / 4]));

	sector_size = handle->sector_sz;
	new_start_addr = ((type - 1) << handle->log2_chunk_sz);

	/* Read Image Size */
	size = ntohl(verifier->fwimage[CNX_IMG_SIZE_OFFSET / 4]);
	logmsg(MSG_INFO, "cnx_write_image: fw image size: 0x%x\n", size);

	/* Sectors must be erased before they can be written to. */
	ioctl_info.af_type = HERMON_FLASH_ERASE_SECTOR;
	for (i = 0; i < size; i += sector_size) {
		ioctl_info.af_sector_num =
		    cnx_cont2phys(log2_chunk_sz, i, type) / sector_size;
		if (cnx_erase_sector_ioctl(handle, &ioctl_info) != 0) {
			logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to "
			    "erase sector 0x%x\n"), ioctl_info.af_sector_num);
			return (FWFLASH_FAILURE);
		}
	}

	fw = (uint8_t *)verifier->fwimage;
	ioctl_info.af_type = HERMON_FLASH_WRITE_BYTE;

	/* Write the new image without the magic pattern */
	for (i = 16; i < size; i++) {
		ioctl_info.af_byte = fw[i];
		ioctl_info.af_addr = cnx_cont2phys(log2_chunk_sz, i, type);
		if (cnx_write_ioctl(handle, &ioctl_info) != 0) {
			logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to "
			    "write byte 0x%x\n"), ioctl_info.af_byte);
			return (FWFLASH_FAILURE);
		}

		if (i && !(i % handle->sector_sz)) {
			(void) printf(" .");
			(void) fflush((void *)NULL);
		}
	}

	/* Validate the new image -- Write the magic pattern. */
	for (i = 0; i < 16; i++) {
		ioctl_info.af_byte = fw[i];
		ioctl_info.af_addr = cnx_cont2phys(log2_chunk_sz, i, type);
		if (cnx_write_ioctl(handle, &ioctl_info) != 0) {
			logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to "
			    "write magic pattern byte 0x%x\n"),
			    ioctl_info.af_byte);
			return (FWFLASH_FAILURE);
		}
	}

	/* Write new image start address to CR space */
	errno = 0;
	ioctl_info.af_addr = new_start_addr;
	if (ioctl(handle->fd, HERMON_IOCTL_WRITE_BOOT_ADDR, &ioctl_info) != 0) {
		logmsg(MSG_WARN, gettext("cnx_write_image: Failed to "
		    "update boot address register: %s\n"), strerror(errno));
	}

	return (FWFLASH_SUCCESS);
}


/*
 * cnx_identify performs the following actions:
 *
 *	allocates and assigns thisdev->vpr
 *
 *	allocates space for the 4 GUIDs which each IB device must have
 *	queries the hermon driver for this device's GUIDs
 *
 *	determines the hardware vendor, so that thisdev->vpr->vid
 *	can be set correctly
 */
static int
cnx_identify(struct devicelist *thisdev)
{
	int				fd, ret, i;
	hermon_flash_init_ioctl_t	init_ioctl;
	ib_cnx_encap_ident_t		*manuf;
	cfi_t				cfi;
	int				hw_psid_found = 0;

	logmsg(MSG_INFO, "hermon: cnx_identify\n");
	/* open the device */
	/* hook thisdev->ident->encap_ident to ib_cnx_encap_ident_t */
	/* check that all the bits are sane */
	/* return success, if warranted */

	errno = 0;
	if ((fd = open(thisdev->access_devname, O_RDONLY)) < 0) {
		logmsg(MSG_ERROR, gettext("hermon: Unable to open a %s-"
		    "attached device node: %s: %s\n"), drivername,
		    thisdev->access_devname, strerror(errno));
		return (FWFLASH_FAILURE);
	}

	if ((manuf = calloc(1, sizeof (ib_cnx_encap_ident_t))) == NULL) {
		logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space "
		    "for a %s-attached handle structure\n"), drivername);
		close(fd);
		return (FWFLASH_FAILURE);
	}
	manuf->magic = FWFLASH_IB_MAGIC_NUMBER;
	manuf->state = FWFLASH_IB_STATE_NONE;
	manuf->fd = fd;
	manuf->log2_chunk_sz = 0;

	thisdev->ident->encap_ident = manuf;

	/*
	 * Inform driver that this command supports the Intel Extended
	 * CFI command set.
	 */
	cfi.cfi_char[0x10] = 'M';
	cfi.cfi_char[0x11] = 'X';
	cfi.cfi_char[0x12] = '2';
	init_ioctl.af_cfi_info[0x4] = ntohl(cfi.cfi_int[0x4]);

	errno = 0;
	ret = ioctl(fd, HERMON_IOCTL_FLASH_INIT, &init_ioctl);
	if (ret < 0) {
		logmsg(MSG_ERROR, gettext("hermon: HERMON_IOCTL_FLASH_INIT "
		    "failed: %s\n"), strerror(errno));
		close(fd);
		free(manuf);
		return (FWFLASH_FAILURE);
	}

	manuf->hwrev = init_ioctl.af_hwrev;
	logmsg(MSG_INFO, "hermon: init_ioctl: hwrev: %x, fwver: %d.%d.%04d, "
	    "PN# Len %d\n", init_ioctl.af_hwrev, init_ioctl.af_fwrev.afi_maj,
	    init_ioctl.af_fwrev.afi_min, init_ioctl.af_fwrev.afi_sub,
	    init_ioctl.af_pn_len);

	/*
	 * Determine whether the attached driver supports the Intel or
	 * AMD Extended CFI command sets. If it doesn't support either,
	 * then we're hosed, so error out.
	 */
	for (i = 0; i < HERMON_FLASH_CFI_SIZE_QUADLET; i++) {
		cfi.cfi_int[i] = ntohl(init_ioctl.af_cfi_info[i]);
	}
	manuf->cmd_set = cfi.cfi_char[0x13];

	if (cfi.cfi_char[0x10] == 'Q' &&
	    cfi.cfi_char[0x11] == 'R' &&
	    cfi.cfi_char[0x12] == 'Y') {
		/* make sure the cmd set is SPI */
		if (manuf->cmd_set != HERMON_FLASH_SPI_CMDSET) {
			logmsg(MSG_ERROR, gettext("hermon: Unsupported flash "
			    "device command set\n"));
			goto identify_end;
		}
		/* set some defaults */
		manuf->sector_sz = HERMON_FLASH_SECTOR_SZ_DEFAULT;
		manuf->device_sz = HERMON_FLASH_DEVICE_SZ_DEFAULT;
	} else if (manuf->cmd_set == HERMON_FLASH_SPI_CMDSET) {
		manuf->sector_sz = HERMON_FLASH_SPI_SECTOR_SIZE;
		manuf->device_sz = HERMON_FLASH_SPI_DEVICE_SIZE;
	} else {
		if (manuf->cmd_set != HERMON_FLASH_AMD_CMDSET &&
		    manuf->cmd_set != HERMON_FLASH_INTEL_CMDSET) {
			logmsg(MSG_ERROR, gettext("hermon: Unknown flash "
			    "device command set %lx\n"), manuf->cmd_set);
			goto identify_end;
		}
		/* read from the CFI data */
		manuf->sector_sz = ((cfi.cfi_char[0x30] << 8) |
		    cfi.cfi_char[0x2F]) << 8;
		manuf->device_sz = 0x1 << cfi.cfi_char[0x27];
	}

	logmsg(MSG_INFO, "hermon: sector_sz: 0x%08x device_sz: 0x%08x\n",
	    manuf->sector_sz, manuf->device_sz);

	/* set firmware revision */
	manuf->hwfw_img_info.fw_rev.major = init_ioctl.af_fwrev.afi_maj;
	manuf->hwfw_img_info.fw_rev.minor = init_ioctl.af_fwrev.afi_min;
	manuf->hwfw_img_info.fw_rev.subminor = init_ioctl.af_fwrev.afi_sub;

	if (((thisdev->ident->vid = calloc(1, MLX_VPR_VIDLEN + 1)) == NULL) ||
	    ((thisdev->ident->revid = calloc(1, MLX_VPR_REVLEN + 1)) == NULL)) {
		logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space "
		    "for a VPR record.\n"));
		goto identify_end;
	}
	(void) strlcpy(thisdev->ident->vid, "MELLANOX", MLX_VPR_VIDLEN);

	/*
	 * We actually want the hwrev field from the ioctl above.
	 * Until we find out otherwise, add it onto the end of the
	 * firmware version details.
	 */
	snprintf(thisdev->ident->revid, MLX_VPR_REVLEN, "%d.%d.%03d",
	    manuf->hwfw_img_info.fw_rev.major,
	    manuf->hwfw_img_info.fw_rev.minor,
	    manuf->hwfw_img_info.fw_rev.subminor);

	if ((ret = cnx_get_guids(manuf)) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: No GUIDs found for "
		    "device %s!\n"), thisdev->access_devname);
	}

	/* set hw part number, psid, and name in handle */
	/* now walk the magic decoder ring table */
	manuf->info.mlx_pn = NULL;
	manuf->info.mlx_psid = NULL;
	manuf->info.mlx_id = NULL;

	if (cnx_get_image_info(manuf) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: Failed to read Image Info "
		    "for PSID\n"));
		hw_psid_found = 0;
	} else {
		hw_psid_found = 1;
	}

	if (init_ioctl.af_pn_len != 0) {
		/* part number length */
		for (i = 0; i < init_ioctl.af_pn_len; i++) {
			if (init_ioctl.af_hwpn[i] == ' ') {
				manuf->pn_len = i;
				break;
			}
		}
		if (i == init_ioctl.af_pn_len) {
			manuf->pn_len = init_ioctl.af_pn_len;
		}
	} else {
		logmsg(MSG_INFO, "hermon: Failed to get Part# from hermon "
		    "driver \n");
		manuf->pn_len = 0;
	}

	if (manuf->pn_len != 0) {
		errno = 0;
		manuf->info.mlx_pn = calloc(1, manuf->pn_len);
		if (manuf->info.mlx_pn == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: no space available "
			    "for the HCA PN record (%s)\n"), strerror(errno));
			goto identify_end;
		}
		(void) memcpy(manuf->info.mlx_pn, init_ioctl.af_hwpn,
		    manuf->pn_len);
		manuf->info.mlx_pn[manuf->pn_len] = 0;

		logmsg(MSG_INFO, "hermon: HCA PN (%s) PN-Len %d\n",
		    manuf->info.mlx_pn, manuf->pn_len);

		errno = 0;
		manuf->info.mlx_psid = calloc(1, MLX_PSID_SZ);
		if (manuf->info.mlx_psid == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: PSID calloc "
			    "failed :%s\n"), strerror(errno));
			goto identify_end;
		}

		errno = 0;
		if ((manuf->info.mlx_id = calloc(1, MLX_STR_ID_SZ)) == NULL) {
			logmsg(MSG_ERROR, gettext("hermon: "
			    "ID calloc failed (%s)\n"),
			    strerror(errno));
			goto identify_end;
		}

		/* Find part number, set the rest */
		for (i = 0; i < MLX_MAX_ID; i++) {
			if (strncmp((const char *)init_ioctl.af_hwpn,
			    mlx_mdr[i].mlx_pn, manuf->pn_len) == 0) {

				if (hw_psid_found) {
					logmsg(MSG_INFO, "HW-PSID: %s "
					    "MLX_MDR[%d]: %s\n",
					    manuf->hwfw_img_info.psid, i,
					    mlx_mdr[i].mlx_psid);
					if (strncmp((const char *)
					    manuf->hwfw_img_info.psid,
					    mlx_mdr[i].mlx_psid,
					    MLX_PSID_SZ) != 0)
						continue;
				}
				/* Set PSID */
				(void) memcpy(manuf->info.mlx_psid,
				    mlx_mdr[i].mlx_psid, MLX_PSID_SZ);
				manuf->info.mlx_psid[MLX_PSID_SZ - 1] = 0;

				logmsg(MSG_INFO, "hermon: HCA PSID (%s)\n",
				    manuf->info.mlx_psid);

				(void) strlcpy(manuf->info.mlx_id,
				    mlx_mdr[i].mlx_id,
				    strlen(mlx_mdr[i].mlx_id) + 1);

				logmsg(MSG_INFO, "hermon: HCA Name (%s)\n",
				    manuf->info.mlx_id);

				break;
			}
		}
	}

	if ((manuf->pn_len == 0) || (i == MLX_MAX_ID)) {
		logmsg(MSG_INFO, "hermon: No hardware part number "
		    "information available for this HCA\n");

		i = strlen("No hardware information available for this device");

		thisdev->ident->pid = calloc(1, i + 2);
		sprintf(thisdev->ident->pid, "No additional hardware info "
		    "available for this device");
	} else {
		errno = 0;
		if ((thisdev->ident->pid = calloc(1,
		    strlen(manuf->info.mlx_psid) + 1)) != NULL) {
			(void) strlcpy(thisdev->ident->pid,
			    manuf->info.mlx_psid,
			    strlen(manuf->info.mlx_psid) + 1);
		} else {
			logmsg(MSG_ERROR,
			    gettext("hermon: Unable to allocate space for a "
			    "hardware identifier: %s\n"), strerror(errno));
			goto identify_end;
		}
	}

	for (i = 0; i < 4; i++) {
		errno = 0;
		if ((thisdev->addresses[i] = calloc(1,
		    (2 * sizeof (uint64_t)) + 1)) == NULL) {
			logmsg(MSG_ERROR,
			    gettext("hermon: Unable to allocate space for a "
			    "human-readable HCA guid: %s\n"), strerror(errno));
			goto identify_end;
		}
		(void) sprintf(thisdev->addresses[i], "%016llx",
		    manuf->ibguids[i]);
	}

	/*
	 * We do NOT close the fd here, since we can close it
	 * at the end of the fw_readfw() or fw_writefw() functions
	 * instead and not get the poor dear confused about whether
	 * it's been inited already.
	 */

	return (FWFLASH_SUCCESS);

	/* cleanup */
identify_end:
	cnx_close(thisdev);
	return (FWFLASH_FAILURE);
}

static int
cnx_get_guids(ib_cnx_encap_ident_t *handle)
{
	int	i, rv;

	logmsg(MSG_INFO, "cnx_get_guids\n");

	/* make sure we've got our fallback position organised */
	for (i = 0; i < 4; i++) {
		handle->ibguids[i] = 0x00000000;
	}

	rv = cnx_find_magic_n_chnk_sz(handle, FWFLASH_IB_STATE_IMAGE_PRI);
	if (rv != FWFLASH_SUCCESS) {
		logmsg(MSG_INFO, "hermon: Failed to get Primary magic number. "
		    "Trying Secondary... \n");
		rv = cnx_find_magic_n_chnk_sz(handle,
		    FWFLASH_IB_STATE_IMAGE_SEC);
		if (rv != FWFLASH_SUCCESS) {
			logmsg(MSG_ERROR, gettext("hermon: Failed to get "
			    "Secondary magic number.\n"));
			logmsg(MSG_ERROR,
			    gettext("Warning: HCA Firmware corrupt.\n"));
			return (FWFLASH_FAILURE);
		}
		rv = cnx_read_guids(handle, FWFLASH_IB_STATE_IMAGE_SEC);
		if (rv != FWFLASH_SUCCESS) {
			logmsg(MSG_ERROR, gettext("hermon: Failed to read "
			    "secondary guids.\n"));
			return (FWFLASH_FAILURE);
		}
	} else {
		rv = cnx_read_guids(handle, FWFLASH_IB_STATE_IMAGE_PRI);
		if (rv != FWFLASH_SUCCESS) {
			logmsg(MSG_ERROR, gettext("hermon: Failed to read "
			    "primary guids.\n"));
			return (FWFLASH_FAILURE);
		}
	}
	for (i = 0; i < 4; i++) {
		logmsg(MSG_INFO, "hermon: ibguids[%d] 0x%016llx\n", i,
		    handle->ibguids[i]);
	}
	for (i = 0; i < 2; i++) {
		logmsg(MSG_INFO, "hermon: ib_portmac[%d] 0x%016llx\n", i,
		    handle->ib_mac[i]);
	}

	return (FWFLASH_SUCCESS);
}

static int
cnx_close(struct devicelist *flashdev)
{
	ib_cnx_encap_ident_t	*handle;

	logmsg(MSG_INFO, "cnx_close\n");

	handle = (ib_cnx_encap_ident_t *)flashdev->ident->encap_ident;

	if (CNX_I_CHECK_HANDLE(handle)) {
		logmsg(MSG_ERROR, gettext("hermon: Invalid Handle to close "
		    "device %s! \n"), flashdev->access_devname);
		return (FWFLASH_FAILURE);
	}

	if (handle->fd > 0) {
		errno = 0;
		(void) ioctl(handle->fd, HERMON_IOCTL_FLASH_FINI);
		if (close(handle->fd) != 0) {
			logmsg(MSG_ERROR, gettext("hermon: Unable to properly "
			    "close device %s! (%s)\n"),
			    flashdev->access_devname, strerror(errno));
			return (FWFLASH_FAILURE);
		}
	}

	if (handle != NULL) {
		if (handle->info.mlx_id != NULL)
			free(handle->info.mlx_id);

		if (handle->info.mlx_psid != NULL)
			free(handle->info.mlx_psid);

		if (handle->fw != NULL)
			free(handle->fw);
		free(handle);
	}

	if (flashdev->ident->vid != NULL)
		free(flashdev->ident->vid);

	if (flashdev->ident->revid != NULL)
		free(flashdev->ident->revid);

	return (FWFLASH_SUCCESS);
}


/*
 * Driver read/write ioctl calls.
 */
static int
cnx_read_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info)
{
	int	ret;

#ifdef CNX_DEBUG
	logmsg(MSG_INFO, "cnx_read_ioctl: fd %d af_type 0x%x af_addr 0x%x "
	    "af_sector_num(0x%x)\n", hdl->fd, info->af_type,
	    info->af_addr, info->af_sector_num);
#endif

	errno = 0;
	ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_READ, info);
	if (ret != 0) {
		logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_READ failed "
		    "(%s)\n"), strerror(errno));
	}
	return (ret);
}

static int
cnx_write_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info)
{
	int	ret;

#ifdef CNX_DEBUG
	logmsg(MSG_INFO, "cnx_write_ioctl: fd(%d) af_type(0x%x) "
	    "af_addr(0x%x) af_sector_num(0x%x) af_byte(0x%x)\n",
	    hdl->fd, info->af_type, info->af_addr, info->af_sector_num,
	    info->af_byte);
#endif
	errno = 0;
	ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_WRITE, info);
	if (ret != 0) {
		logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_WRITE "
		    "failed (%s)\n"), strerror(errno));
	}
	return (ret);
}

static int
cnx_erase_sector_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info)
{
	int	ret;

#ifdef CNX_DEBUG
	logmsg(MSG_INFO, "cnx_erase_sector_ioctl: fd(%d) af_type(0x%x) "
	    "af_sector_num(0x%x)\n", hdl->fd, info->af_type,
	    info->af_sector_num);
#endif
	errno = 0;
	ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_ERASE, info);
	if (ret != 0) {
		logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_ERASE "
		    "failed (%s)\n"), strerror(errno));
	}
	return (ret);
}

/*
 * cnx_crc16 - computes 16 bit crc of supplied buffer.
 *   image should be in network byteorder
 *   result is returned in host byteorder form
 */
uint16_t
cnx_crc16(uint8_t *image, uint32_t size, int is_image)
{
	const uint16_t	poly = 0x100b;
	uint32_t	crc = 0xFFFF;
	uint32_t	word;
	uint32_t	i, j;

	logmsg(MSG_INFO, "hermon: cnx_crc16\n");

	for (i = 0; i < size / 4; i++) {
		word = (image[4 * i] << 24) |
		    (image[4 * i + 1] << 16) |
		    (image[4 * i + 2] << 8) |
		    (image[4 * i + 3]);

		if (is_image == CNX_HW_IMG)
			word = MLXSWAPBITS32(word);

		for (j = 0; j < 32; j++) {
			if (crc & 0x8000) {
				crc = (((crc << 1) |
				    (word >> 31)) ^ poly) & 0xFFFF;
			} else {
				crc = ((crc << 1) | (word >> 31)) & 0xFFFF;
			}
			word = (word << 1) & 0xFFFFFFFF;
		}
	}

	for (i = 0; i < 16; i++) {
		if (crc & 0x8000) {
			crc = ((crc << 1) ^ poly) & 0xFFFF;
		} else {
			crc = (crc << 1) & 0xFFFF;
		}
	}

	crc = crc ^ 0xFFFF;
	return (crc & 0xFFFF);
}

static void
cnx_local_set_guid_crc_img(uint32_t offset, uint32_t guid_crc_size,
    uint32_t guid_crc_offset)
{
	uint16_t	crc;
	uint8_t		*fw_p = (uint8_t *)&verifier->fwimage[0];

	crc = htons(cnx_crc16((uint8_t *)&verifier->fwimage[offset / 4],
	    guid_crc_size, CNX_FILE_IMG));

	logmsg(MSG_INFO, "cnx_local_set_guid_crc_img: new guid_sect crc: %x\n",
	    ntohs(crc));
	(void) memcpy(&fw_p[offset + guid_crc_offset], &crc, 2);
}

/*
 * Address translation functions for ConnectX
 * Variable definitions:
 * - log2_chunk_size: log2 of a Flash chunk size
 * - cont_addr: a contiguous image address to be translated
 * - is_image_in_odd_chunk: When this bit is 1, it indicates the new image is
 * stored in odd chunks of the Flash.
 */
static uint32_t
cnx_cont2phys(uint32_t log2_chunk_size, uint32_t cont_addr, int type)
{
	uint32_t	result;
	int		is_image_in_odd_chunks;

	is_image_in_odd_chunks = type - 1;

	if (log2_chunk_size) {
		result = cont_addr & (0xffffffff >> (32 - log2_chunk_size)) |
		    (is_image_in_odd_chunks << log2_chunk_size) |
		    (cont_addr << 1) & (0xffffffff << (log2_chunk_size + 1));
	} else {
		result = cont_addr;
	}

	return (result);
}

static int
cnx_read_guids(ib_cnx_encap_ident_t *handle, int type)
{
#ifdef _LITTLE_ENDIAN
	uint32_t		*ptr, tmp;
#endif
	hermon_flash_ioctl_t	ioctl_info;
	uint32_t		*guids;
	uint32_t		*ibmac;
	int			ret, i;
	uint32_t		nguidptr_addr;
	union {
		uint8_t		bytes[4];
		uint32_t	dword;
	} crc16_u;
	uint32_t		*guid_structure;
	uint16_t		crc;

	logmsg(MSG_INFO, "cnx_read_guids\n");

	errno = 0;
	guid_structure = (uint32_t *)calloc(1,
	    CNX_GUID_CRC16_SIZE / 4 * sizeof (uint32_t));
	if (guid_structure == NULL) {
		logmsg(MSG_WARN, gettext("hermon: Can't calloc guid_structure "
		    ": (%s)\n"), strerror(errno));
		return (FWFLASH_FAILURE);
	}

	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;
	ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    CNX_NGUIDPTR_OFFSET, type);

	ret = cnx_read_ioctl(handle, &ioctl_info);
	if (ret != 0) {
		logmsg(MSG_WARN, gettext("hermon: Failed to read GUID Pointer "
		    "Address\n"));
		goto out;
	}

	guids = (uint32_t *)&handle->ibguids[0];
	ibmac = (uint32_t *)&handle->ib_mac[0];
	nguidptr_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    ioctl_info.af_quadlet, type);

	logmsg(MSG_INFO, "NGUIDPTR: 0x%08x \n", nguidptr_addr);
	/* Read in the entire guid section in order to calculate the CRC */
	ioctl_info.af_addr = nguidptr_addr - 0x10;
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) {
		ret = cnx_read_ioctl(handle, &ioctl_info);
		if (ret != 0) {
			logmsg(MSG_INFO, "Failed to read guid_structure "
			    "(0x%x)\n", i);
			goto out;
		}

		if (i >= 4 && i < 12) {
			guids[i - 4] = ioctl_info.af_quadlet;
		}
		if (i >= 12 && i < 16) {
			ibmac[i - 12] = ioctl_info.af_quadlet;
		}

		guid_structure[i] = ioctl_info.af_quadlet;
		ioctl_info.af_addr += 4;
	}

	for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) {
		logmsg(MSG_INFO, "guid_structure[%x] = 0x%08x\n", i,
		    guid_structure[i]);
	}

	/*
	 * Check the CRC--make sure it computes.
	 */

	/* 0x12 subtracted: 0x2 for alignment, 0x10 to reach structure start */
	ioctl_info.af_addr = nguidptr_addr + CNX_GUID_CRC16_OFFSET - 0x12;
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	ret = cnx_read_ioctl(handle, &ioctl_info);
	if (ret != 0) {
		logmsg(MSG_WARN, gettext("hermon: Failed to read guid crc "
		    "at 0x%x\n"), ioctl_info.af_addr);
		goto out;
	}

	crc16_u.dword = ioctl_info.af_quadlet;
	crc = cnx_crc16((uint8_t *)guid_structure, CNX_GUID_CRC16_SIZE,
	    CNX_HW_IMG);

	if (crc != crc16_u.dword) {
		logmsg(MSG_WARN, gettext("hermon: calculated crc16: 0x%x "
		    "differs from GUID section 0x%x\n"), crc, crc16_u.dword);
	} else {
		logmsg(MSG_INFO, "hermon: calculated crc16: 0x%x MATCHES with "
		    "GUID section 0x%x\n", crc, crc16_u.dword);
	}

#ifdef _LITTLE_ENDIAN
	/*
	 * guids are read as pairs of 32 bit host byteorder values and treated
	 * by callers as 64 bit values. So swap each pair of 32 bit values
	 * to make them correct
	 */
	ptr = (uint32_t *)guids;
	for (ret = 0; ret < 8; ret += 2) {
		tmp = ptr[ret];
		ptr[ret] = ptr[ret+1];
		ptr[ret+1] = tmp;
	}
	ptr = (uint32_t *)&handle->ib_mac[0];
	for (ret = 0; ret < 4; ret += 2) {
		tmp = ptr[ret];
		ptr[ret] = ptr[ret+1];
		ptr[ret+1] = tmp;
	}
#endif
	ret = FWFLASH_SUCCESS;

out:
	free(guid_structure);
	return (ret);
}

static int
cnx_find_magic_n_chnk_sz(ib_cnx_encap_ident_t *handle, int type)
{
	int	i, found = 0;
	uint32_t addr;
	uint32_t boot_addresses[] =
	    {0, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000};

	logmsg(MSG_INFO, "cnx_find_magic_n_chnk_sz\n");

	switch (type) {
	case FWFLASH_IB_STATE_IMAGE_PRI:
		addr = 0;
		if (cnx_check_for_magic_pattern(handle, addr) !=
		    FWFLASH_SUCCESS) {
			goto err;
		}
		break;

	case FWFLASH_IB_STATE_IMAGE_SEC:
		for (i = 1; i < 6; i++) {
			addr = boot_addresses[i];
			if (cnx_check_for_magic_pattern(handle, addr) ==
			    FWFLASH_SUCCESS) {
				found = 1;
				break;
			}
		}
		if (!found) {
			goto err;
		}
		break;

	default:
		logmsg(MSG_INFO, "cnx_find_magic_pattern: unknown type\n");
		goto err;
	}

	logmsg(MSG_INFO, "magic_pattern found at addr %x\n", addr);
	handle->img2_start_addr = addr;

	handle->log2_chunk_sz = cnx_get_log2_chunk_size_f_hdl(handle, type);
	if (handle->log2_chunk_sz == 0) {
		logmsg(MSG_INFO, "no chunk size found for type %x. "
		    "Assuming non-failsafe burn\n", type);
	}

	handle->fw_sz = cnx_get_image_size_f_hdl(handle, type);
	if (handle->fw_sz == 0) {
		logmsg(MSG_INFO, "no fw size found for type %x. \n", type);
	}
	handle->state |= type;

	return (FWFLASH_SUCCESS);
err:
	logmsg(MSG_INFO, "no magic_pattern found for type %x\n", type);
	return (FWFLASH_FAILURE);
}

static int
cnx_check_for_magic_pattern(ib_cnx_encap_ident_t *handle, uint32_t addr)
{
	int 			i;
	hermon_flash_ioctl_t	ioctl_info;
	int 			magic_pattern_buf[4];

	logmsg(MSG_INFO, "cnx_check_for_magic_pattern\n");

	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	for (i = 0; i < 4; i++) {
		ioctl_info.af_addr = addr + (i * sizeof (uint32_t));
		if (cnx_read_ioctl(handle, &ioctl_info) != 0) {
			logmsg(MSG_INFO, "\nFailed to read magic pattern\n");
			return (FWFLASH_FAILURE);
		}

		magic_pattern_buf[i] = ioctl_info.af_quadlet;
	}

	return (cnx_is_magic_pattern_present(magic_pattern_buf, CNX_HW_IMG));

}

int
cnx_is_magic_pattern_present(int *data, int is_image)
{
	int	i;
	int	dword;

	logmsg(MSG_INFO, "cnx_is_magic_pattern_present\n");

	for (i = 0; i < 4; i++) {
		if (is_image == CNX_FILE_IMG)
			dword = MLXSWAPBITS32(data[i]);
		else
			dword = data[i];
		logmsg(MSG_INFO, "local_quadlet: %08x, magic pattern: %08x\n",
		    dword, cnx_magic_pattern[i]);
		if (dword != cnx_magic_pattern[i]) {
			return (FWFLASH_FAILURE);
		}
	}

	return (FWFLASH_SUCCESS);
}

static uint32_t
cnx_get_log2_chunk_size_f_hdl(ib_cnx_encap_ident_t *handle, int type)
{
	hermon_flash_ioctl_t	ioctl_info;
	int			ret;

	logmsg(MSG_INFO, "cnx_get_log2_chunk_size_f_hdl\n");

	/* If chunk size is already set, just return it. */
	if (handle->log2_chunk_sz) {
		return (handle->log2_chunk_sz);
	}

	switch (type) {
	case FWFLASH_IB_STATE_IMAGE_PRI:
		ioctl_info.af_addr = CNX_CHUNK_SIZE_OFFSET;
		break;
	case FWFLASH_IB_STATE_IMAGE_SEC:
		ioctl_info.af_addr =
		    handle->img2_start_addr + CNX_CHUNK_SIZE_OFFSET;
		break;
	default:
		logmsg(MSG_INFO,
		    "cnx_get_log2_chunk_size_f_hdl: unknown type\n");
		return (0);
	}

	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	ret = cnx_read_ioctl(handle, &ioctl_info);
	if (ret != 0) {
		logmsg(MSG_INFO, "\nFailed to read chunk size\n");
		return (0);
	}

	return (cnx_get_log2_chunk_size(ioctl_info.af_quadlet));
}


static uint32_t
cnx_get_log2_chunk_size(uint32_t chunk_size_word)
{
	uint8_t		checksum;
	uint32_t	log2_chunk_size;

	logmsg(MSG_INFO, "cnx_get_log2_chunk_size: chunk_size_word:"
	    " 0x%x\n", chunk_size_word);

	checksum =
	    (chunk_size_word & 0xff) +
	    ((chunk_size_word >> 8) & 0xff) +
	    ((chunk_size_word >> 16) & 0xff) +
	    ((chunk_size_word >> 24) & 0xff);

	if (checksum != 0) {
		logmsg(MSG_INFO, "Corrupted chunk size checksum\n");
		return (0);
	}

	if (chunk_size_word & 0x8) {
		log2_chunk_size = (chunk_size_word & 0x7) + 16;
		logmsg(MSG_INFO, "log2 chunk size: 0x%x\n", log2_chunk_size);
		return (log2_chunk_size);
	} else {
		return (0);
	}
}

static uint32_t
cnx_get_image_size_f_hdl(ib_cnx_encap_ident_t *handle, int type)
{
	hermon_flash_ioctl_t	ioctl_info;
	int			ret;

	logmsg(MSG_INFO, "cnx_get_image_size_f_hdl\n");

	ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    CNX_IMG_SIZE_OFFSET, type);
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	ret = cnx_read_ioctl(handle, &ioctl_info);
	if (ret != 0) {
		logmsg(MSG_INFO, "Failed to read image size\n");
		return (0);
	}

	logmsg(MSG_INFO, "Image Size: 0x%x\n", ioctl_info.af_quadlet);

	return (ioctl_info.af_quadlet);
}

static int
cnx_get_image_info(ib_cnx_encap_ident_t *handle)
{
	uint32_t	ii_ptr_addr;
	uint32_t	ii_size;
	int		*buf;
	int		i, type;
	hermon_flash_ioctl_t	ioctl_info;

	logmsg(MSG_INFO, "cnx_get_image_info: state %x\n", handle->state);

	type = handle->state &
	    (FWFLASH_IB_STATE_IMAGE_PRI | FWFLASH_IB_STATE_IMAGE_SEC);

	/* Get the image info pointer */
	ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    CNX_IMG_INF_PTR_OFFSET, type);
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: Failed to read image info "
		    "Address\n"));
		return (FWFLASH_FAILURE);
	}
	ii_ptr_addr = ioctl_info.af_quadlet & 0xffffff;

	/* Get the image info size, a negative offset from the image info ptr */
	ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    ii_ptr_addr + CNX_IMG_INF_SZ_OFFSET, type);
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: Failed to read image info "
		    "size\n"));
		return (FWFLASH_FAILURE);
	}
	logmsg(MSG_INFO, "hermon: ImageInfo Sz: 0x%x\n", ioctl_info.af_quadlet);

	ii_size = ioctl_info.af_quadlet;
	/* size is in dwords--convert it to bytes */
	ii_size *= 4;

	logmsg(MSG_INFO, "hermon: ii_ptr_addr: 0x%x ii_size: 0x%x\n",
	    ii_ptr_addr, ii_size);

	buf = (int *)calloc(1, ii_size);

	ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz,
	    ii_ptr_addr, type);
	ioctl_info.af_type = HERMON_FLASH_READ_QUADLET;

	for (i = 0; i < ii_size/4; i++) {
		if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) {
			logmsg(MSG_WARN, gettext("hermon: Failed to read "
			    "image info (0x%x)\n"), i);
			free(buf);
			return (FWFLASH_FAILURE);
		}

		buf[i] = ioctl_info.af_quadlet;
		ioctl_info.af_addr += 4;
	}

	/* Parse the image info section */
	if (cnx_parse_img_info(buf, ii_size, &handle->hwfw_img_info,
	    CNX_HW_IMG) != FWFLASH_SUCCESS) {
		logmsg(MSG_WARN, gettext("hermon: Failed to parse Image Info "
		    "section\n"));
		free(buf);
		return (FWFLASH_FAILURE);
	}

	free(buf);
	return (FWFLASH_SUCCESS);
}

int
cnx_parse_img_info(int *buf, uint32_t byte_size, cnx_img_info_t *img_info,
    int is_image)
{
	uint32_t 	*p;
	uint32_t 	offs = 0;
	uint32_t 	tag_num = 0;
	int 		end_found = 0;
	uint32_t 	tag_size, tag_id;
	uint32_t 	tmp;
	const char 	*str;
	int		i;

	p = (uint32_t *)buf;

	logmsg(MSG_INFO, "hermon: cnx_parse_img_info\n");

	while (!end_found && (offs < byte_size)) {
		if (is_image == CNX_FILE_IMG) {
			tag_size = ntohl(*p) & 0xffffff;
			tag_id = ntohl(*p) >> 24;
			tmp = ntohl(*(p + 1));
		} else {
			tag_size = ((*p) & 0xffffff);
			tag_id = ((*p) >> 24);
			tmp = (*(p + 1));
		}

		logmsg(MSG_INFO, "tag_id: %d tag_size: %d\n", tag_id, tag_size);

		if ((offs + tag_size) > byte_size) {
			logmsg(MSG_WARN, gettext("hermon: Image Info section "
			    "corrupted: Tag# %d - tag_id %d, size %d exceeds "
			    "info section size (%d bytes)"), tag_num, tag_id,
			    tag_size, byte_size);
			return (FWFLASH_FAILURE);
		}

		switch (tag_id) {
		case CNX_FW_VER:
			if (tag_size != CNX_FW_VER_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_FW_VER_SZ);
			}
			tmp = (tmp & CNX_MASK_FW_VER_MAJ) >> 16;
			img_info->fw_rev.major = tmp;
			if (is_image == CNX_FILE_IMG)
				tmp = ntohl(*(p + 2));
			else
				tmp = (*(p + 2));
			img_info->fw_rev.minor =
			    (tmp & CNX_MASK_FW_VER_MIN)>> 16;
			img_info->fw_rev.subminor =
			    tmp & CNX_MASK_FW_VER_SUBMIN;

			logmsg(MSG_INFO, "FW_VER: %d.%d.%03d\n",
			    img_info->fw_rev.major, img_info->fw_rev.minor,
			    img_info->fw_rev.subminor);
			break;

		case CNX_FW_BUILD_TIME:
			if (tag_size != CNX_FW_BUILD_TIME_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_FW_BUILD_TIME_SZ);
			}
			img_info->fw_buildtime.hour =
			    (tmp & CNX_MASK_FW_BUILD_HOUR) >> 16;
			img_info->fw_buildtime.minute =
			    (tmp & CNX_MASK_FW_BUILD_MIN) >> 8;
			img_info->fw_buildtime.second =
			    (tmp & CNX_MASK_FW_BUILD_SEC);

			if (is_image == CNX_FILE_IMG)
				tmp = ntohl(*(p + 2));
			else
				tmp = (*(p + 2));

			img_info->fw_buildtime.year =
			    (tmp & CNX_MASK_FW_BUILD_YEAR) >> 16;
			img_info->fw_buildtime.month =
			    (tmp & CNX_MASK_FW_BUILD_MON) >> 8;
			img_info->fw_buildtime.day =
			    (tmp & CNX_MASK_FW_BUILD_DAY);

			logmsg(MSG_INFO, "Build TIME: %d:%d:%d %d:%d:%d\n",
			    img_info->fw_buildtime.year,
			    img_info->fw_buildtime.month,
			    img_info->fw_buildtime.day,
			    img_info->fw_buildtime.hour,
			    img_info->fw_buildtime.minute,
			    img_info->fw_buildtime.second);
			break;

		case CNX_DEV_TYPE:
			if (tag_size != CNX_DEV_TYPE_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_DEV_TYPE_SZ);
			}
			img_info->dev_id = tmp & CNX_MASK_DEV_TYPE_ID;
			logmsg(MSG_INFO, "DEV_TYPE: %d\n", img_info->dev_id);
			break;

		case CNX_VSD_VENDOR_ID:
			if (tag_size != CNX_VSD_VENDOR_ID_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_VSD_VENDOR_ID_SZ);
			}
			img_info->vsd_vendor_id = tmp & CNX_MASK_VSD_VENDORID;
			logmsg(MSG_INFO, "VSD Vendor ID: 0x%lX\n",
			    img_info->vsd_vendor_id);
			break;

		case CNX_PSID:
			if (tag_size != CNX_PSID_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_PSID_SZ);
			}
			str = (const char *)p;
			str += 4;

			for (i = 0; i < CNX_PSID_SZ; i++)
				img_info->psid[i] = str[i];

#ifdef _LITTLE_ENDIAN
			if (is_image == CNX_HW_IMG) {
				for (i = 0; i < CNX_PSID_SZ; i += 4) {
					img_info->psid[i+3] = str[i];
					img_info->psid[i+2] = str[i+1];
					img_info->psid[i+1] = str[i+2];
					img_info->psid[i] = str[i+3];
				}
			}
#endif

			logmsg(MSG_INFO, "PSID: %s\n", img_info->psid);
			break;

		case CNX_VSD:
			if (tag_size != CNX_VSD_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_VSD_SZ);
			}
			str = (const char *)p;
			str += 4;

			for (i = 0; i < CNX_VSD_SZ; i++)
				img_info->vsd[i] = str[i];

#ifdef _LITTLE_ENDIAN
			if (is_image == CNX_HW_IMG) {
				for (i = 0; i < CNX_VSD_SZ; i += 4) {
					img_info->vsd[i+3] = str[i];
					img_info->vsd[i+2] = str[i+1];
					img_info->vsd[i+1] = str[i+2];
					img_info->vsd[i] = str[i+3];
				}
			}
#endif
			logmsg(MSG_INFO, "VSD: %s\n", img_info->vsd);
			break;

		case CNX_END_TAG:
			if (tag_size != CNX_END_TAG_SZ) {
				logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: "
				    "%d expected sz %d\n", tag_id, tag_size,
				    CNX_END_TAG_SZ);
			}
			end_found = 1;
			break;

		default:
			if (tag_id > CNX_END_TAG) {
				logmsg(MSG_WARN, gettext("Invalid img_info "
				    "tag ID %d of size %d\n"), tag_id,
				    tag_size);
			}
			break;
		}

		p += (tag_size / 4) + 1;
		offs += tag_size + 4;
		tag_num++;
	}

	if (offs != byte_size) {
		logmsg(MSG_WARN, gettext("hermon: Corrupt Image Info section "
		    "in firmware image\n"));
		if (end_found) {
			logmsg(MSG_WARN, gettext("Info section corrupted: "
			    "Section data size is %x bytes, but end tag found "
			    "after %x bytes.\n"), byte_size, offs);
		} else {
			logmsg(MSG_WARN, gettext("Info section corrupted: "
			    "Section data size is %x bytes, but end tag not "
			    "found at section end.\n"), byte_size);
		}
		return (FWFLASH_FAILURE);
	}

	return (FWFLASH_SUCCESS);
}