hermon.c revision ee5d8455d3cff95bf8149073831be4560d60250d
/*
* 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
* 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/sysmacros.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <sys/byteorder.h>
#include <libintl.h> /* for gettext(3c) */
#include "../../hdrs/hermon_ib.h"
char *devprefix = "/devices";
char drivername[] = "hermon\0";
char *devsuffix = ":devctl";
extern int errno;
extern struct vrfyplugin *verifier;
extern int fwflash_debug;
/* required functions for this plugin */
int fw_identify(int start);
int fw_devinfo();
/* helper functions */
int type);
int type);
int
{
int rv = FWFLASH_SUCCESS;
if (CNX_I_CHECK_HANDLE(manuf)) {
return (FWFLASH_FAILURE);
}
if (rv != FWFLASH_SUCCESS) {
} else {
}
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
{
int i, j, k;
if (CNX_I_CHECK_HANDLE(manuf)) {
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().
*/
int resp;
"that this firmware image (%s) is compatible with your "
"continue? (Y/N): "));
"flash operation of %s on %s"),
return (FWFLASH_FAILURE);
}
}
}
/*
* 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. */
/*
* 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)
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 */
}
gettext("Failed to update %s image on device %s"),
goto out;
}
gettext("Failed to verify %s image for device %s"),
goto out;
}
}
out:
/* final update marker to the user */
(void) printf(" +\n");
}
/*
* 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;
struct devicelist *newdev;
char *devpath;
int devlength = 0;
if (thisnode == DI_NODE_NIL) {
return (rv);
}
/* we've found one, at least */
"space for device entry\n"));
return (FWFLASH_FAILURE);
}
/* calloc enough for /devices + devpath + ":devctl" + '\0' */
"space for a devfs name\n"));
return (FWFLASH_FAILURE);
}
"space for a device identification record\n"));
return (FWFLASH_FAILURE);
}
/* CHECK VARIOUS IB THINGS HERE */
if (rv == FWFLASH_FAILURE) {
continue;
}
== NULL) {
" space for a driver name\n"));
return (FWFLASH_FAILURE);
}
/* this next bit is backwards compatibility - "IB\0" */
"space for a class name\n"));
return (FWFLASH_FAILURE);
}
++idx;
}
if (fwflash_debug != 0) {
struct devicelist *tempdev;
"\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,
}
}
return (FWFLASH_SUCCESS);
}
int
{
if (CNX_I_CHECK_HANDLE(encap)) {
return (FWFLASH_FAILURE);
}
/* Try the primary first, then the secondary */
/* Mellanox HCA Flash app note, p40, #4.2.3 table 9 */
else
} else {
}
}
/*
* 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
{
int ret, i;
int image_size;
int type;
if (type == 0) {
"image first\n"));
return (FWFLASH_FAILURE);
}
if (image_size <= 0) {
"0x%x for %s image\n"),
return (FWFLASH_FAILURE);
}
return (FWFLASH_FAILURE);
}
for (i = 0; i < image_size; i += 4) {
if (ret != 0) {
"read sector %d\n"), i);
return (FWFLASH_FAILURE);
}
}
for (i = 0; i < image_size; i += 4) {
}
return (FWFLASH_SUCCESS);
}
static int
{
int fd;
int len;
errno = 0;
return (FWFLASH_FAILURE);
}
errno = 0;
return (FWFLASH_FAILURE);
}
perror("fwrite");
return (FWFLASH_FAILURE);
}
return (FWFLASH_SUCCESS);
}
static int
{
}
static int
{
/*
* guids are supplied by callers as 64 bit values in host byteorder.
* Storage is in network byteorder.
*/
#ifdef _BIG_ENDIAN
}
}
}
}
#else
}
}
}
}
#endif
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
{
int sector_size;
int size;
int i;
if (type == 0) {
" where to write.\n"));
return (FWFLASH_FAILURE);
}
/* Read Image Size */
/* Sectors must be erased before they can be written to. */
for (i = 0; i < size; i += sector_size) {
return (FWFLASH_FAILURE);
}
}
/* Write the new image without the magic pattern */
for (i = 16; i < size; i++) {
return (FWFLASH_FAILURE);
}
(void) printf(" .");
}
}
/* Validate the new image -- Write the magic pattern. */
for (i = 0; i < 16; i++) {
"write magic pattern byte 0x%x\n"),
return (FWFLASH_FAILURE);
}
}
/* Write new image start address to CR space */
errno = 0;
}
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
{
int hw_psid_found = 0;
/* 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;
"attached device node: %s: %s\n"), drivername,
return (FWFLASH_FAILURE);
}
"for a %s-attached handle structure\n"), drivername);
return (FWFLASH_FAILURE);
}
manuf->log2_chunk_sz = 0;
/*
* Inform driver that this command supports the Intel Extended
* CFI command set.
*/
errno = 0;
if (ret < 0) {
return (FWFLASH_FAILURE);
}
/*
* 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++) {
}
/* make sure the cmd set is SPI */
"device command set\n"));
goto identify_end;
}
/* set some defaults */
} else {
goto identify_end;
}
/* read from the CFI data */
}
/* set firmware revision */
"for a VPR record.\n"));
goto identify_end;
}
/*
* 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.
*/
}
/* set hw part number, psid, and name in handle */
/* now walk the magic decoder ring table */
"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++) {
break;
}
}
if (i == init_ioctl.af_pn_len) {
}
} else {
"driver \n");
}
errno = 0;
goto identify_end;
}
errno = 0;
goto identify_end;
}
errno = 0;
"ID calloc failed (%s)\n"),
goto identify_end;
}
/* Find part number, set the rest */
for (i = 0; i < MLX_MAX_ID; i++) {
if (hw_psid_found) {
"MLX_MDR[%d]: %s\n",
if (strncmp((const char *)
MLX_PSID_SZ) != 0)
continue;
}
/* Set PSID */
break;
}
}
}
"information available for this HCA\n");
i = strlen("No hardware information available for this device");
"available for this device");
} else {
errno = 0;
} else {
gettext("hermon: Unable to allocate space for a "
goto identify_end;
}
}
for (i = 0; i < 4; i++) {
errno = 0;
gettext("hermon: Unable to allocate space for a "
goto identify_end;
}
}
/*
* 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 */
return (FWFLASH_FAILURE);
}
static int
{
int i, rv;
/* make sure we've got our fallback position organised */
for (i = 0; i < 4; i++) {
}
if (rv != FWFLASH_SUCCESS) {
"Trying Secondary... \n");
if (rv != FWFLASH_SUCCESS) {
"Secondary magic number.\n"));
gettext("Warning: HCA Firmware corrupt.\n"));
return (FWFLASH_FAILURE);
}
if (rv != FWFLASH_SUCCESS) {
"secondary guids.\n"));
return (FWFLASH_FAILURE);
}
} else {
if (rv != FWFLASH_SUCCESS) {
"primary guids.\n"));
return (FWFLASH_FAILURE);
}
}
for (i = 0; i < 4; i++) {
}
for (i = 0; i < 2; i++) {
}
return (FWFLASH_SUCCESS);
}
static int
{
if (CNX_I_CHECK_HANDLE(handle)) {
return (FWFLASH_FAILURE);
}
errno = 0;
"close device %s! (%s)\n"),
return (FWFLASH_FAILURE);
}
}
}
return (FWFLASH_SUCCESS);
}
/*
*/
static int
{
int ret;
#ifdef CNX_DEBUG
#endif
errno = 0;
if (ret != 0) {
}
return (ret);
}
static int
{
int ret;
#ifdef CNX_DEBUG
"af_addr(0x%x) af_sector_num(0x%x) af_byte(0x%x)\n",
#endif
errno = 0;
if (ret != 0) {
}
return (ret);
}
static int
{
int ret;
#ifdef CNX_DEBUG
#endif
errno = 0;
if (ret != 0) {
}
return (ret);
}
/*
* cnx_crc16 - computes 16 bit crc of supplied buffer.
* image should be in network byteorder
* result is returned in host byteorder form
*/
{
uint32_t i, j;
for (i = 0; i < size / 4; i++) {
if (is_image == CNX_HW_IMG)
for (j = 0; j < 32; j++) {
if (crc & 0x8000) {
} else {
}
}
}
for (i = 0; i < 16; i++) {
if (crc & 0x8000) {
} else {
}
}
return (crc & 0xFFFF);
}
static void
{
}
/*
* 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
{
if (log2_chunk_size) {
} else {
}
return (result);
}
static int
{
#ifdef _LITTLE_ENDIAN
#endif
int ret, i;
union {
} crc16_u;
errno = 0;
if (guid_structure == NULL) {
return (FWFLASH_FAILURE);
}
if (ret != 0) {
"Address\n"));
goto out;
}
/* Read in the entire guid section in order to calculate the CRC */
for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) {
if (ret != 0) {
"(0x%x)\n", i);
goto out;
}
if (i >= 4 && i < 12) {
}
if (i >= 12 && i < 16) {
}
}
for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) {
guid_structure[i]);
}
/*
* Check the CRC--make sure it computes.
*/
/* 0x12 subtracted: 0x2 for alignment, 0x10 to reach structure start */
if (ret != 0) {
goto out;
}
} else {
}
#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
*/
}
}
#endif
out:
return (ret);
}
static int
{
int i, found = 0;
{0, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000};
switch (type) {
addr = 0;
goto err;
}
break;
for (i = 1; i < 6; i++) {
addr = boot_addresses[i];
found = 1;
break;
}
}
if (!found) {
goto err;
}
break;
default:
goto err;
}
if (handle->log2_chunk_sz == 0) {
"Assuming non-failsafe burn\n", type);
}
}
return (FWFLASH_SUCCESS);
err:
return (FWFLASH_FAILURE);
}
static int
{
int i;
int magic_pattern_buf[4];
for (i = 0; i < 4; i++) {
return (FWFLASH_FAILURE);
}
}
}
int
{
int i;
int dword;
for (i = 0; i < 4; i++) {
if (is_image == CNX_FILE_IMG)
else
dword, cnx_magic_pattern[i]);
if (dword != cnx_magic_pattern[i]) {
return (FWFLASH_FAILURE);
}
}
return (FWFLASH_SUCCESS);
}
static uint32_t
{
int ret;
/* If chunk size is already set, just return it. */
if (handle->log2_chunk_sz) {
return (handle->log2_chunk_sz);
}
switch (type) {
break;
break;
default:
"cnx_get_log2_chunk_size_f_hdl: unknown type\n");
return (0);
}
if (ret != 0) {
return (0);
}
}
static uint32_t
{
" 0x%x\n", chunk_size_word);
checksum =
(chunk_size_word & 0xff) +
if (checksum != 0) {
return (0);
}
if (chunk_size_word & 0x8) {
return (log2_chunk_size);
} else {
return (0);
}
}
static uint32_t
{
int ret;
if (ret != 0) {
return (0);
}
return (ioctl_info.af_quadlet);
}
static int
{
int *buf;
int i, type;
/* Get the image info pointer */
"Address\n"));
return (FWFLASH_FAILURE);
}
/* Get the image info size, a negative offset from the image info ptr */
"size\n"));
return (FWFLASH_FAILURE);
}
/* size is in dwords--convert it to bytes */
ii_size *= 4;
ii_ptr_addr, type);
for (i = 0; i < ii_size/4; i++) {
"image info (0x%x)\n"), i);
return (FWFLASH_FAILURE);
}
}
/* Parse the image info section */
CNX_HW_IMG) != FWFLASH_SUCCESS) {
"section\n"));
return (FWFLASH_FAILURE);
}
return (FWFLASH_SUCCESS);
}
int
int is_image)
{
uint32_t *p;
int end_found = 0;
const char *str;
int i;
if (is_image == CNX_FILE_IMG) {
} else {
tag_size = ((*p) & 0xffffff);
tag_id = ((*p) >> 24);
tmp = (*(p + 1));
}
"corrupted: Tag# %d - tag_id %d, size %d exceeds "
return (FWFLASH_FAILURE);
}
switch (tag_id) {
case CNX_FW_VER:
if (tag_size != CNX_FW_VER_SZ) {
}
if (is_image == CNX_FILE_IMG)
else
tmp = (*(p + 2));
break;
case CNX_FW_BUILD_TIME:
if (tag_size != CNX_FW_BUILD_TIME_SZ) {
}
(tmp & CNX_MASK_FW_BUILD_SEC);
if (is_image == CNX_FILE_IMG)
else
tmp = (*(p + 2));
(tmp & CNX_MASK_FW_BUILD_DAY);
break;
case CNX_DEV_TYPE:
if (tag_size != CNX_DEV_TYPE_SZ) {
}
break;
case CNX_VSD_VENDOR_ID:
if (tag_size != CNX_VSD_VENDOR_ID_SZ) {
}
break;
case CNX_PSID:
if (tag_size != CNX_PSID_SZ) {
}
str = (const char *)p;
str += 4;
for (i = 0; i < CNX_PSID_SZ; i++)
#ifdef _LITTLE_ENDIAN
if (is_image == CNX_HW_IMG) {
for (i = 0; i < CNX_PSID_SZ; i += 4) {
}
}
#endif
break;
case CNX_VSD:
if (tag_size != CNX_VSD_SZ) {
}
str = (const char *)p;
str += 4;
for (i = 0; i < CNX_VSD_SZ; i++)
#ifdef _LITTLE_ENDIAN
if (is_image == CNX_HW_IMG) {
for (i = 0; i < CNX_VSD_SZ; i += 4) {
}
}
#endif
break;
case CNX_END_TAG:
if (tag_size != CNX_END_TAG_SZ) {
}
end_found = 1;
break;
default:
if (tag_id > CNX_END_TAG) {
"tag ID %d of size %d\n"), tag_id,
tag_size);
}
break;
}
tag_num++;
}
"in firmware image\n"));
if (end_found) {
"Section data size is %x bytes, but end tag found "
} else {
"Section data size is %x bytes, but end tag not "
"found at section end.\n"), byte_size);
}
return (FWFLASH_FAILURE);
}
return (FWFLASH_SUCCESS);
}