diff options
author | Bill Taylor <Bill.Taylor@Sun.COM> | 2009-04-30 13:42:05 -0700 |
---|---|---|
committer | Bill Taylor <Bill.Taylor@Sun.COM> | 2009-04-30 13:42:05 -0700 |
commit | 9e39c5ba00a55fa05777cc94b148296af305e135 (patch) | |
tree | c461af0758c1502672201c88b4032ea0df9ea42c /usr/src/cmd | |
parent | b31d69afd0b141cad8082cc344f651651108c210 (diff) | |
download | illumos-gate-9e39c5ba00a55fa05777cc94b148296af305e135.tar.gz |
6808773 code for Mellanox devices (hermon, tavor, udapl, fwflash) can become open source
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/fwflash/plugins/Makefile | 6 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/hdrs/MELLANOX.h | 199 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/hdrs/hermon_ib.h | 282 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/hdrs/tavor_ib.h | 226 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/i386/Makefile | 94 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/sparc/Makefile | 94 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/common/hermon.c | 1885 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/common/tavor.c | 1921 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/i386/Makefile | 31 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/sparc/Makefile | 28 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/vendor/hermon-MELLANOX.c | 383 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/vendor/tavor-MELLANOX.c | 337 |
12 files changed, 5452 insertions, 34 deletions
diff --git a/usr/src/cmd/fwflash/plugins/Makefile b/usr/src/cmd/fwflash/plugins/Makefile index 53b9d8ccf3..3a28e5a923 100644 --- a/usr/src/cmd/fwflash/plugins/Makefile +++ b/usr/src/cmd/fwflash/plugins/Makefile @@ -19,9 +19,7 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/fwflash/plugins @@ -29,7 +27,7 @@ include $(SRC)/cmd/Makefile.cmd -COMMON_SUBDIRS= transport +COMMON_SUBDIRS= $(MACH) transport CLOSED_SUBDIRS= $(CLOSED)/cmd/fwflash/plugins SUBDIRS= $(COMMON_SUBDIRS) $(CLOSED_SUBDIRS) diff --git a/usr/src/cmd/fwflash/plugins/hdrs/MELLANOX.h b/usr/src/cmd/fwflash/plugins/hdrs/MELLANOX.h new file mode 100644 index 0000000000..ec214d62af --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/hdrs/MELLANOX.h @@ -0,0 +1,199 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _HDRS_MELLANOX_H +#define _HDRS_MELLANOX_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * MELLANOX.h + * + * This file contain common information related to Mellanox technologies + * HCA cards. + */ +#define SUNW_OUI 0x0003baULL +#define MLX_OUI 0x0002c9ULL +#define MLX_DEFAULT_NODE_GUID 0x2c9000100d050ULL +#define MLX_DEFAULT_P1_GUID 0x2c9000100d051ULL +#define MLX_DEFAULT_P2_GUID 0x2c9000100d052ULL +#define MLX_DEFAULT_SYSIMG_GUID 0x2c9000100d053ULL + +/* How many bits to shift and leave just the OUI */ +#define OUISHIFT 40 + +#define MLX_VPR_VIDLEN 9 /* "MELLANOX" + '\0' */ +#define MLX_VPR_REVLEN 21 /* "%04x.%04x.%04x: %04x" + '\0' */ + +#define FWFLASH_IB_MAGIC_NUMBER 0xF00B0021 + +/* Numerically largest OUI that's presently assigned */ +#define TAVOR_MAX_OUI 0xacde48 + +#define FWFLASH_IB_STATE_NONE 0x00 +#define FWFLASH_IB_STATE_IMAGE_PRI 0x01 +#define FWFLASH_IB_STATE_IMAGE_SEC 0x02 +#define FWFLASH_IB_STATE_MMAP 0x04 +#define FWFLASH_IB_STATE_GUIDN 0x10 +#define FWFLASH_IB_STATE_GUID1 0x20 +#define FWFLASH_IB_STATE_GUID2 0x40 +#define FWFLASH_IB_STATE_GUIDS 0x80 + +#define FWFLASH_IB_STATE_IMAGE FWFLASH_IB_STATE_IMAGE_PRI + +#define FWFLASH_IB_STATE_PFI_IMAGE FWFLASH_IB_STATE_IMAGE_PRI +#define FWFLASH_IB_STATE_SFI_IMAGE FWFLASH_IB_STATE_IMAGE_SEC + +/* + * Structure to hold the part number, PSID, and string ID + * for an HCA card. + */ +typedef struct mlx_mdr_s { + char *mlx_pn; + char *mlx_psid; + char *mlx_id; +} mlx_mdr_t; + +/* + * Magic decoder ring for matching HCA hardware/firmware. + * Part Number / PSID / String ID + */ +mlx_mdr_t mlx_mdr[] = { + /* Part No PSID Card ID */ + { "MHEA28-XS", "MT_0250000001", "Lion mini" }, + { "MHEA28-XSC", "MT_0390110001", "Lion mini" }, + { "MHEA28-XT", "MT_0150000001", "Lion mini" }, + { "MHEA28-XTC", "MT_0370110001", "Lion mini" }, + { "MHGA28-XT", "MT_0150000002", "Lion mini" }, + { "MHGA28-XTC", "MT_0370110002", "Lion mini" }, + { "MHGA28-XTC", "MT_0370130002", "Lion mini" }, + { "MHGA28-XS", "MT_0250000002", "Lion mini" }, + { "MHGA28-XSC", "MT_0390110002", "Lion mini" }, + { "MHGA28-XSC", "MT_0390130002", "Lion mini" }, + { "MHEL-CF128", "MT_0190000001", "Lion cub" }, + { "MHEL-CF128-T", "MT_00A0000001", "Lion cub" }, + { "MTLP25208-CF128T", "MT_00A0000001", "Lion cub" }, + { "MHEL-CF128-TC", "MT_00A0010001", "Lion cub" }, + { "MHEL-CF128-TC", "MT_0140010001", "Lion cub" }, + { "MHEL-CF128-SC", "MT_0190010001", "Lion cub" }, + { "MHEA28-1TC", "MT_02F0110001", "Lion cub" }, + { "MHEA28-1SC", "MT_0330110001", "Lion cub" }, + { "MHGA28-1T", "MT_0200000001", "Lion cub" }, + { "MHGA28-1TC", "MT_02F0110002", "Lion cub" }, + { "MHGA28-1SC", "MT_0330110002", "Lion cub" }, + { "MHGA28-1S", "MT_0430000001", "Lion cub" }, + { "MHEL-CF256-T", "MT_00B0000001", "Lion cub" }, + { "MTLP25208-CF256T", "MT_00B0000001", "Lion cub" }, + { "MHEL-CF256-TC", "MT_00B0010001", "Lion cub" }, + { "MHEA28-2TC", "MT_0300110001", "Lion cub" }, + { "MHEA28-2SC", "MT_0340110001", "Lion cub" }, + { "MHGA28-2T", "MT_0210000001", "Lion cub" }, + { "MHGA28-2TC", "MT_0300110002", "Lion cub" }, + { "MHGA28-2SC", "MT_0340110002", "Lion cub" }, + { "MHEL-CF512-T", "MT_00C0000001", "Lion cub" }, + { "MTLP25208-CF512T", "MT_00C0000001", "Lion cub" }, + { "MHGA28-5T", "MT_0220000001", "Lion cub" }, + { "375-3382-01", "SUN0030000001", "Sun Lion cub DDR" }, + { "MHES14-XSC", "MT_0410110001", "Tiger" }, + { "MHES14-XT", "MT_01F0000001", "Tiger" }, + { "MHES14-XTC", "MT_03F0110001", "Tiger" }, + { "MHES18-XS", "MT_0260000001", "Cheetah" }, + { "MHES18-XS", "MT_0260010001", "Cheetah" }, + { "MHES18-XSC", "MT_03D0110001", "Cheetah" }, + { "MHES18-XSC", "MT_03D0120001", "Cheetah" }, + { "MHES18-XSC", "MT_03D0130001", "Cheetah" }, + { "MHES18-XT", "MT_0230000002", "Cheetah" }, + { "MHES18-XT", "MT_0230010002", "Cheetah" }, + { "MHES18-XTC", "MT_03B0110001", "Cheetah" }, + { "MHES18-XTC", "MT_03B0120001", "Cheetah" }, + { "MHES18-XTC", "MT_03B0140001", "Cheetah" }, + { "MHGS18-XS", "MT_0260000002", "Cheetah" }, + { "MHGS18-XSC", "MT_03D0110002", "Cheetah" }, + { "MHGS18-XSC", "MT_03D0120002", "Cheetah" }, + { "MHGS18-XSC", "MT_03D0130002", "Cheetah" }, + { "MHGS18-XT", "MT_0230000001", "Cheetah" }, + { "MHGS18-XTC", "MT_03B0110002", "Cheetah" }, + { "MHGS18-XTC", "MT_03B0120002", "Cheetah" }, + { "MHGS18-XTC", "MT_03B0140002", "Cheetah" }, + { "MHXL-CF128", "MT_0180000001", "Cougar Cub 128" }, + { "MHXL-CF128-T", "MT_0030000001", "Cougar Cub 128" }, + { "MTLP23108-CF128T", "MT_0030000001", "Cougar Cub 128" }, + { "MHET2X-1SC", "MT_0280110001", "Cougar Cub 128" }, + { "MHET2X-1SC", "MT_0280120001", "Cougar Cub 128" }, + { "MHET2X-1TC", "MT_0270110001", "Cougar Cub 128" }, + { "MHET2X-1TC", "MT_0270120001", "Cougar Cub 128" }, + { "MHXL-CF256-T", "MT_0040000001", "Cougar Cub 256" }, + { "MHET2X-2SC", "MT_02D0110001", "Cougar Cub 256" }, + { "MHET2X-2SC", "MT_02D0120001", "Cougar Cub 256" }, + { "MHET2X-2TC", "MT_02B0110001", "Cougar Cub 256" }, + { "MHET2X-2TC", "MT_02B0120001", "Cougar Cub 256" }, + { "375-3481-01", "SUN0040000001", "Sun Cougar Cub SDR" }, + { "MHX-CE128-T", "MT_0000000001", "Cougar 128" }, + { "MTPB23108-CE128", "MT_0000000001", "Cougar 128" }, + { "MHX-CE256-T", "MT_0010000001", "Cougar 256" }, + { "MTPB23108-CE256", "MT_0010000001", "Cougar 256" }, + { "MHX-CE512-T", "MT_0050000001", "Cougar 512" }, + { "MTPB23108-CE512", "MT_0050000001", "Cougar 512" }, + { "MHEH28-XSC", "MT_04C0110001", "Eagle SDR" }, + { "MHEH28-XSC", "MT_04C0130005", "Eagle SDR" }, + { "MHEH28-XTC", "MT_04A0110001", "Eagle SDR" }, + { "MHEH28-XTC", "MT_04A0130005", "Eagle SDR" }, + { "MHGH28-XSC", "MT_04C0110002", "Eagle DDR" }, + { "MHGH28-XSC", "MT_04C0120002", "Eagle DDR" }, + { "MHGH28-XSC", "MT_04C0140005", "Eagle DDR" }, + { "MHGH28-XTC", "MT_04A0110002", "Eagle DDR" }, + { "MHGH28-XTC", "MT_04A0120002", "Eagle DDR" }, + { "MHGH28-XTC", "MT_04A0140005", "Eagle DDR" }, + { "X1289A-Z", "SUN0010010001", "Sun IB NEM DDR" }, + { "375-3548-01", "SUN0060000001", "Sun IB EM DDR X4216A-Z" }, + { "375-3549-01", "SUN0070000001", "Sun PCIe DDR X4217A" }, + { "375-3481-01", "SUN0050000001", "Sun PCIe EM SDR" }, + { "MHGH29-XSC", "MT_0A60110002", "Eagle DDR PCIe Gen 2.0" }, + { "MHGH29-XSC", "MT_0A60120005", "Eagle DDR PCIe Gen 2.0" }, + { "MHGH29-XTC", "MT_0A50110002", "Eagle DDR PCIe Gen 2.0" }, + { "MHGH29-XTC", "MT_0A50120005", "Eagle DDR PCIe Gen 2.0" }, + { "375-3605-01", "SUN0160000001", "Sun Mirage QDR" }, + { "375-3606-01", "SUN0150000001", "Sun Falcon QDR" }, + { "MHJH29-XTC", "MT_04E0110003", "Eagle QDR" }, + { "375-3551-05", "SUN0080000001", "Sun C48-IB-NEM" } +}; + +/* Get mlx_mdr[] array size */ +#define MLX_SZ_MLX_MDR sizeof (mlx_mdr) +#define MLX_SZ_MLX_MDR_STRUCT sizeof (mlx_mdr[0]) + +#define MLX_MAX_ID MLX_SZ_MLX_MDR/MLX_SZ_MLX_MDR_STRUCT +#define MLX_PSID_SZ 16 +#define MLX_STR_ID_SZ 64 + +#ifdef __cplusplus +} +#endif + +#endif /* _HDRS_MELLANOX_H */ diff --git a/usr/src/cmd/fwflash/plugins/hdrs/hermon_ib.h b/usr/src/cmd/fwflash/plugins/hdrs/hermon_ib.h new file mode 100644 index 0000000000..1de41aa8fd --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/hdrs/hermon_ib.h @@ -0,0 +1,282 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _HDRS_HERMON_IB_H +#define _HDRS_HERMON_IB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ConnectX (hermon) specific definitions. + */ + +/* + * The reference for the definitions in this file is the + * + * Mellanox HCA Flash Programming Application Note + * (Mellanox document number 2205AN) + * rev 1.45, 2007. Chapter 4 in particular. + */ + +#include <sys/types.h> +#include <sys/ib/adapters/hermon/hermon_ioctl.h> +#include "MELLANOX.h" + +#define FWFLASH_IB_HERMON_DRIVER "hermon" + +/* + * Image Info section: Refer Mellanox App note 1.45, Section 4.4 + * + * The Image Info section contains management information about the + * firmware image. It consists of a series of consecutive data tags. + * Each tag contains a 32-bit header, providing a TagID which indicates + * the data type, and the size of the data in the tag. + */ +#define MLX_MASK_TAGID 0xff000000 +#define MLX_MASK_TAGSIZE 0x00ffffff + +enum tag_ids { + CNX_IMAGE_INFO_REV = 0, /* IMAGE_INFO format revision */ + CNX_FW_VER = 1, /* Firmware Version */ + CNX_FW_BUILD_TIME = 2, /* Firmware Build Time */ + CNX_DEV_TYPE = 3, /* Device Type */ + CNX_PSID = 4, /* Parameter Set IDentification */ + CNX_VSD = 5, /* Vendor Specific Data */ + CNX_RES1 = 6, /* reserved */ + CNX_RES2 = 7, /* reserved */ + CNX_VSD_VENDOR_ID = 8, /* PCISIG vendor ID */ + /* 0x9 - 0xFE are reserved */ + CNX_END_TAG = 0xff /* END tag */ +}; + +enum tag_sizes { + CNX_IMAGE_INFO_REV_SZ = 4, + CNX_FW_VER_SZ = 8, + CNX_FW_BUILD_TIME_SZ = 8, + CNX_DEV_TYPE_SZ = 4, + CNX_PSID_SZ = 16, + CNX_VSD_SZ = 208, + CNX_VSD_VENDOR_ID_SZ = 4, + CNX_END_TAG_SZ = 0 +}; + +/* + * Image Info Format revision (TagID - CNX_IMAGE_INFO_REV). + * Provides the format revision of the Image Info section. Currently it is 0x1 + */ +#define CNX_IMAGE_INFO_VER 1 + +/* + * Firmware Version (TagID - CNX_FW_VER) + * Provides the major, minor and sub-minor versions of the firmware image. + */ +#define CNX_MASK_FW_VER_MAJ 0xffff0000 +#define CNX_MASK_FW_VER_MIN CNX_MASK_FW_VER_MAJ +#define CNX_MASK_FW_VER_SUBMIN 0x0000ffff + +typedef struct cnx_fw_rev_s { + uint16_t major; + uint16_t reserved; + uint16_t minor; + uint16_t subminor; +} cnx_fw_rev_t; + + +/* + * Firmware Build Time (TagID - CNX_FW_BUILD_TIME) + * Provides the data and time of the firmware image build. + */ +#define CNX_MASK_FW_BUILD_HOUR 0x00ff0000 +#define CNX_MASK_FW_BUILD_MIN 0x0000ff00 +#define CNX_MASK_FW_BUILD_SEC 0x000000ff +#define CNX_MASK_FW_BUILD_YEAR 0xffff0000 +#define CNX_MASK_FW_BUILD_MON 0x0000ff00 +#define CNX_MASK_FW_BUILD_DAY 0x000000ff + +typedef struct cnx_fw_build_time_tag { + uint8_t reserved; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint16_t year; + uint8_t month; + uint8_t day; +} cnx_fw_build_time_t; + +/* + * Device Type (TagID - CNX_DEV_TYPE) + * The device type tag is only 4 bytes long, so we don't bother to + * declare a separate struct for it. + * + * The CNX_MASK_DEV_TYPE_REV provides the mask to extract the hardware + * device's PCI Revision ID. + * The CNX_MASK_DEV_TYPE_ID provides the mask to extract the hardware + * device's PCI Device ID. + */ +#define CNX_MASK_DEV_TYPE_REV 0x00ff0000 +#define CNX_MASK_DEV_TYPE_ID 0x0000ffff + +/* + * The PSID (TagID - CNX_PSID) and VSD (TagID - CNX_VSD) tag contents are + * just bytes without any specific structure, so we'll declare their sizes + * but nothing else. + */ +#define CNX_TAG_PSID_SIZE 0x10 +#define CNX_TAG_VSD_SIZE 0xD0 + +/* + * VSD Vendor ID (TagID - CNX_VSD_VENDOR_ID) + * The VSD Vendor ID tag holds the PCISIG vendor ID of the vendor that + * fills the VSD tag. + */ +#define CNX_MASK_VSD_VENDORID 0x00ff + +typedef struct cnx_img_info_s { + cnx_fw_rev_t fw_rev; + cnx_fw_build_time_t fw_buildtime; + uint16_t dev_id; + uint16_t vsd_vendor_id; + uint8_t psid[CNX_PSID_SZ]; + uint8_t vsd[CNX_VSD_SZ]; +} cnx_img_info_t; + +/* + * ConnectX Devices Firmware Image Format + */ +typedef struct mlx_cnx_xfi { /* Byte Offset */ + uint32_t magic_pattern[4]; /* 0x00 - 0x0F */ + uint8_t xfiresv1[24]; /* 0x10 - 0x27 */ + uint32_t failsafechunkinfo; /* 0x28 - 0x2B */ + uint32_t imageinfoptr; /* 0x2C - 0x2F */ + uint32_t fwimagesz; /* 0x30 - 0x33 */ + uint32_t nguidptr; /* 0x34 - 0x37 */ + uint8_t *xfiremainder; +} mlx_cnx_xfi_t; + +uint32_t cnx_magic_pattern[4] = { + 0x4D544657, + 0x8CDFD000, + 0xDEAD9270, + 0x4154BEEF }; + +#define CNX_XFI_IMGINFO_CKSUM_MASK 0xFF000000 +#define CNX_XFI_IMGINFO_PTR_MASK 0x00FFFFFF + +#define CNX_HWVER_OFFSET 0x20 +#define CNX_HWVER_MASK 0xFF000000 + +#define CNX_CHUNK_SIZE_OFFSET 0x28 +#define CNX_IMG_INF_PTR_OFFSET 0x2C +#define CNX_IMG_INF_SZ_OFFSET -0x0C +#define CNX_IMG_SIZE_OFFSET 0x30 +#define CNX_NGUIDPTR_OFFSET 0x34 + +/* + * ConnectX Devices GUID Section Structure. + * + * Of all the structures we poke around with, we're packing + * these because we frequently have to operate on them as + * plain old byte arrays. If we don't pack it then the compiler + * will "properly" align it for us - which results in a + * structure that's a l l s p r e a d o u t. + */ +#pragma pack(1) +typedef struct mlx_cnx_guid_sect { /* Byte Offset */ + uint8_t guidresv[16]; /* 0x00 - 0x0F */ + uint64_t nodeguid; /* 0x10 - 0x17 */ + uint64_t port1guid; /* 0x18 - 0x1F */ + uint64_t port2guid; /* 0x20 - 0x27 */ + uint64_t sysimguid; /* 0x28 - 0x2F */ + uint64_t port1_mac; /* 0x30 - 0x31 - rsvd - must be zero */ + /* 0x32 - 0x37 - Port1 MAC [47:0] */ + uint64_t port2_mac; /* 0x38 - 0x39 - rsvd - must be zero */ + /* 0x3A - 0x3F - Port2 MAC [47:0] */ + uint16_t guidresv2; /* 0x40 - 0x41 */ + uint16_t guidcrc; /* 0x42 - 0x43 */ +} mlx_cnx_guid_sect_t; +#pragma pack() + +#define CNX_NGUID_OFFSET 0x10 +#define CNX_P1GUID_OFFSET 0x18 +#define CNX_P2GUID_OFFSET 0x20 +#define CNX_SYSIMGUID_OFFSET 0x28 +#define CNX_P1MAC_OFFSET 0x32 +#define CNX_P2MAC_OFFSET 0x3A +#define CNX_GUID_CRC16_SIZE 0x40 /* 00-3F */ +#define CNX_GUID_CRC16_OFFSET 0x42 + + +/* we hook this struct into vpr->encap_ident */ +typedef struct ib_cnx_encap_ident_s { + uint_t magic; /* FWFLASH_IB_MAGIC_NUMBER */ + int fd; /* fd of hermon device */ + int cmd_set; /* COMMAND SET */ + int pn_len; /* Part# Length */ + int hwfw_match; /* 1 = match, 0 - nomatch */ + /* Used during write for validation */ + cnx_img_info_t hwfw_img_info; /* HW Image Info Section */ + cnx_img_info_t file_img_info; /* Image File's Image Info Section */ + mlx_mdr_t info; /* Details of HW part#, name, */ + uint32_t *fw; /* this where image is read to */ + uint32_t hwrev; /* H/W revision. ex: A0, A1 */ + uint32_t fw_sz; /* FW image size */ + uint32_t sector_sz; /* FW sector size */ + uint32_t device_sz; /* FW device size */ + uint32_t state; + uint64_t ibguids[4]; /* HW's GUIDs backup info */ + uint64_t ib_mac[2]; /* HW's MAC backup info */ + uint32_t log2_chunk_sz; /* FW chunk size */ + uint32_t img2_start_addr; /* Boot Address, 0 - Pri */ +} ib_cnx_encap_ident_t; + +/* + * Common Flash Interface data. + */ +typedef union cfi_u { + uchar_t cfi_char[HERMON_CFI_INFO_SIZE]; + uint32_t cfi_int[HERMON_CFI_INFO_QSIZE]; +} cfi_t; + +/* used by both identify and verifier plugin */ +uint16_t cnx_crc16(uint8_t *image, uint32_t size, int is_image); +int cnx_is_magic_pattern_present(int *data, int hwim_or_fwim); +int cnx_parse_img_info(int *buf, uint32_t byte_size, cnx_img_info_t *img_info, + int is_image); + +#define CNX_FILE_IMG 1 /* Processing File Image */ +#define CNX_HW_IMG 2 /* Processing Hardware Image */ + +/* Validate the handle */ +#define CNX_I_CHECK_HANDLE(s) \ + ((s == NULL) || ((s)->magic != FWFLASH_IB_MAGIC_NUMBER)) + +#ifdef __cplusplus +} +#endif + +#endif /* _HDRS_HERMON_IB_H */ diff --git a/usr/src/cmd/fwflash/plugins/hdrs/tavor_ib.h b/usr/src/cmd/fwflash/plugins/hdrs/tavor_ib.h new file mode 100644 index 0000000000..e2445163c9 --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/hdrs/tavor_ib.h @@ -0,0 +1,226 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _TAVOR_IB_H +#define _TAVOR_IB_H + +/* + * tavor_ib.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <sys/ib/adapters/tavor/tavor_ioctl.h> + +#define FWFLASH_IB_DRIVER_NAME "tavor" + +#define NODE_GUID_OFFSET 0x0 +#define PORT1_GUID_OFFSET 0x08 +#define PORT2_GUID_OFFSET 0x10 +#define FLASH_SIZE_OFFSET 0x20 +#define FLASH_GUID_PTR 0x24 + +typedef struct fw_rev_s { + uint32_t major; + uint32_t minor; + uint32_t subminor; + uint32_t holder; +} fw_rev_t; + + +typedef struct mlx_is { + uint8_t isresv1[16]; + uint8_t hwrev; /* hardware version */ + uint8_t isver; /* Invariant Sector version */ + uint32_t isresv2; + /* offset from 0x32 to get log2sectsz */ + uint16_t log2sectszp; + /* + * 3rd lot of reserved bytes CAN BE variable length, + * but defaults to 0x18 bytes + */ + uint8_t isresv3[0x18]; + uint16_t log2sectsz; /* log_2 of flash sector size */ + uint8_t *isresv4; /* remainder of IS */ +} mlx_is_t; + +typedef struct mlx_xps { + uint32_t fia; /* fw image addr */ + uint32_t fis; /* fw image size */ + uint32_t signature; /* firmware signature */ + uint8_t xpsresv1[20]; + uint8_t vsdpsid[224]; /* VSD and PSID */ + uint32_t xpsresv2; + uint16_t xpsresv3; /* MUST be zero */ + uint16_t crc16; + uint8_t *xpsresv4; /* from 0x108 to END OF SECTOR */ +} mlx_xps_t; + + +#define XFI_IMGINFO_OFFSET 28 +#define XFI_IMGINFO_CKSUM_MASK 0xFF000000 +#define XFI_IMGINFO_PTR_MASK 0x00FFFFFF + +typedef struct mlx_xfi { + uint8_t xfiresv1[28]; + uint32_t imageinfoptr; + uint32_t xfiresv2; + uint32_t nguidptr; + uint8_t *xfiremainder; +} mlx_xfi_t; + +/* + * Of all the structures we poke around with, we're packing + * these because we frequently have to operate on them as + * plain old byte arrays. If we don't pack it then the compiler + * will "properly" align it for us - which results in a + * structure that's a l l s p r e a d o u t. + */ +#pragma pack(1) +typedef struct mlx_guid_sect +{ + uint8_t guidresv[16]; + uint64_t nodeguid; + uint64_t port1guid; + uint64_t port2guid; + uint64_t sysimguid; + uint16_t guidresv2; + uint16_t guidcrc; +} mlx_guid_sect_t; +#pragma pack() + +/* this is 13x 32bit words */ +#define GUIDSECTION_SZ sizeof (struct mlx_guid_sect) + +/* we hook this struct into vpr->encap_ident */ +typedef struct ib_encap_ident { + uint_t magic; + int fd; + fw_rev_t fw_rev; + uint32_t hwrev; + uint32_t sector_sz; + uint32_t device_sz; + uint32_t state; + int cmd_set; + mlx_mdr_t info; + int pn_len; + int hwfw_match; + uint32_t pfi_guid_addr; /* addr of the offset */ + uint32_t sfi_guid_addr; + uint32_t pri_guid_section[GUIDSECTION_SZ]; + uint32_t sec_guid_section[GUIDSECTION_SZ]; + uint64_t ibguids[4]; + uint8_t *inv; /* Invariant Sector */ + uint8_t *pps; /* Primary Pointer Sector */ + uint8_t *sps; /* Secondary Pointer Sector */ + uint8_t *pfi; /* Primary Firmware Image */ + uint8_t *sfi; /* Secondary Firmware Image */ + uint8_t mlx_psid[16]; + uint8_t mlx_vsd[208]; +} ib_encap_ident_t; + +#define FLASH_PS_SIGNATURE 0x5a445a44 + +#define FLASH_IS_SECTOR_SIZE_OFFSET 0x32 +#define FLASH_IS_SECTOR_SIZE_MASK 0x0000FFFF +#define FLASH_IS_HWVER_OFFSET 0x10 +#define FLASH_IS_HWVER_MASK 0xFF000000 +#define FLASH_IS_ISVER_MASK 0x00FF0000 + +#define FLASH_IS_SECT_SIZE_PTR 0x16 +#define FLASH_IS_SECT_SIZE_PTR_MASK 0x0000FFFF + +#define FLASH_PS_FI_ADDR_OFFSET 0x00 +#define FLASH_PS_FW_SIZE_OFFSET 0x04 +#define FLASH_PS_SIGNATURE_OFFSET 0x08 +/* Vendor Specific Data (VSD) */ +#define FLASH_PS_VSD_OFFSET 0x20 +/* VSD length in bytes */ +#define FLASH_PS_VSD_LENGTH 0xE0 +#define FLASH_PS_VSD_LENGTH_4 0x38 +/* PSID is the last 16B of VSD */ +#define FLASH_PS_PSID_OFFSET 0xF0 + +/* For use with Cisco's VSD */ +#define FLASH_VSD_CISCO_SIGNATURE 0x05ad +#define FLASH_VSD_CISCO_BOOT_OPTIONS 0x00000004 +#define FLASH_VSD_CISCO_FLAG_AUTOUPGRADE 0x01000000 +#define FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_1 0x00010000 +#define FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_2 0x00020000 +#define FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_SCAN 0x00040000 +#define FLASH_VSD_CISCO_FLAG_BOOT_TYPE_WELL_KNOWN 0x00000000 +#define FLASH_VSD_CISCO_FLAG_BOOT_TRY_FOREVER 0x00001000 +#define FLASH_VSD_CISCO_BOOT_VERSION 2 +/* For use with Cisco's VSD */ + +#define MLX_CISCO_CHECK 1 +#define MLX_CISCO_SET 2 + +#define FLASH_PS_CRC16_SIZE 0x104 +#define FLASH_PS_CRC16_OFFSET 0x106 + +#define FLASH_FI_NGUID_OFFSET 0x0 +#define FLASH_FI_P1GUID_OFFSET 0x08 +#define FLASH_FI_P2GUID_OFFSET 0x10 +#define FLASH_FI_SYSIMGUID_OFFSET 0x18 +#define FLASH_GUID_CRC16_SIZE 0x30 +#define FLASH_GUID_CRC16_OFFSET 0x32 +#define FLASH_GUID_SIZE 0x34 + +#define FLASH_GUID_CRC_LEN 0x2F +/* + * Used during read/write ioctl calls to setup the offset into the firmware + * image memory for that particular sector. + */ +#define FLASH_SECTOR_OFFSET(fw, sect, sz) \ + (caddr_t)((uintptr_t)fw + (sect << sz)) + +/* + * Vital System Data from PCI config space. + */ +uint32_t vsd_int[FLASH_PS_VSD_LENGTH_4]; + + +/* + * Common Flash Interface data. + */ +typedef union cfi_u { + uchar_t cfi_char[TAVOR_CFI_INFO_SIZE]; + uint32_t cfi_int[TAVOR_CFI_INFO_QSIZE]; +} cfi_t; + + +#ifdef __cplusplus +} +#endif + + + + +#endif /* _TAVOR_IB_H */ diff --git a/usr/src/cmd/fwflash/plugins/i386/Makefile b/usr/src/cmd/fwflash/plugins/i386/Makefile new file mode 100644 index 0000000000..068a041c0d --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/i386/Makefile @@ -0,0 +1,94 @@ +# +# 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. +# +# cmd/fwflash/plugins/i386 +# + +SRCS= tavor-MELLANOX.c hermon-MELLANOX.c +OBJECTS= $(SRCS:%.c=%.o) +PLUGINS= $(SRCS:%.c=%.so) +POFILES= $(SRCS:%.c=%.po) +LINTFILE= $(SRCS:%.c=%.ln) + +VERIFYPOFILE= fwflash_verify_msg.po + +CLOBBERFILES= $(PLUGINS) $(OBJECTS) $(LINTFILE) \ + $(POFILES) $(VERIFYPOFILE) +TEXT_DOMAIN= SUNW_OST_OSCMD + +all: $(PLUGINS) + + +include $(SRC)/Makefile.master +include $(SRC)/cmd/fwflash/Makefile.com + + +CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS -I$(ROOT)/usr/include +MANUFACTURING_MODE=0 +CFLAGS += -DMANUFACTURING_MODE=$(MANUFACTURING_MODE) +LDLIBS += -ldevinfo -lumem -lc +DYNFLAGS += -Bdynamic +LIBS= $(DYNLIB) + +BUILD.SO= $(LD) -o $@ -G $(DYNFLAGS) + +%.o: ../vendor/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.so: %.o + $(BUILD.SO) $< + $(POST_PROCESS) + +%.ln: ../vendor/%.c + $(LINT.c) $(LINTFLAGS) -c $< + +%.po: ../vendor/%.c + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) \ + `($(GREP) -l gettext $< || echo /dev/null)` + $(SED) "/^domain/d" messages.po > $@ + $(RM) messages.po + +# +# Message catalog +# + +$(VERIFYPOFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +install: $(ROOTLIBFWFLASHVERIFY) \ + $(ROOTLIBFWFLASHVERIFY)/tavor-MELLANOX.so \ + $(ROOTLIBFWFLASHVERIFY)/hermon-MELLANOX.so + +clean: + $(RM) $(OBJECTS) + +clobber: clean + $(RM) $(CLOBBERFILES) + +lint: lint_SRCS +lint_SRCS: $(LINTFILE) + +_msg msg: $(VERIFYPOFILE) diff --git a/usr/src/cmd/fwflash/plugins/sparc/Makefile b/usr/src/cmd/fwflash/plugins/sparc/Makefile new file mode 100644 index 0000000000..d5ca244954 --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/sparc/Makefile @@ -0,0 +1,94 @@ +# +# 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. +# +# cmd/fwflash/plugins/sparc +# + +SRCS= tavor-MELLANOX.c hermon-MELLANOX.c +OBJECTS= $(SRCS:%.c=%.o) +PLUGINS= $(SRCS:%.c=%.so) +POFILES= $(SRCS:%.c=%.po) +LINTFILE= $(SRCS:%.c=%.ln) + +VERIFYPOFILE= fwflash_verify_msg.po + +CLOBBERFILES= $(PLUGINS) $(OBJECTS) $(LINTFILE) \ + $(POFILES) $(VERIFYPOFILE) +TEXT_DOMAIN= SUNW_OST_OSCMD + +all: $(PLUGINS) + + +include $(SRC)/Makefile.master +include $(SRC)/cmd/fwflash/Makefile.com + + +CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS -I$(ROOT)/usr/include +MANUFACTURING_MODE=0 +CFLAGS += -DMANUFACTURING_MODE=$(MANUFACTURING_MODE) +LDLIBS += -ldevinfo -lumem -lc +DYNFLAGS += -Bdynamic +LIBS= $(DYNLIB) + +BUILD.SO= $(LD) -o $@ -G $(DYNFLAGS) + +%.o: ../vendor/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.so: %.o + $(BUILD.SO) $< + $(POST_PROCESS) + +%.ln: ../vendor/%.c + $(LINT.c) $(LINTFLAGS) -c $< + +%.po: ../vendor/%.c + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) \ + `($(GREP) -l gettext $< || echo /dev/null)` + $(SED) "/^domain/d" messages.po > $@ + $(RM) messages.po + +# +# Message catalog +# + +$(VERIFYPOFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +install: $(ROOTLIBFWFLASHVERIFY) \ + $(ROOTLIBFWFLASHVERIFY)/tavor-MELLANOX.so \ + $(ROOTLIBFWFLASHVERIFY)/hermon-MELLANOX.so + +clean: + $(RM) $(OBJECTS) + +clobber: clean + $(RM) $(CLOBBERFILES) + +lint: lint_SRCS +lint_SRCS: $(LINTFILE) + +_msg msg: $(VERIFYPOFILE) diff --git a/usr/src/cmd/fwflash/plugins/transport/common/hermon.c b/usr/src/cmd/fwflash/plugins/transport/common/hermon.c new file mode 100644 index 0000000000..d9394640be --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/transport/common/hermon.c @@ -0,0 +1,1885 @@ +/* + * 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.%04d", + 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.%d\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); +} diff --git a/usr/src/cmd/fwflash/plugins/transport/common/tavor.c b/usr/src/cmd/fwflash/plugins/transport/common/tavor.c new file mode 100644 index 0000000000..2df9209760 --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/transport/common/tavor.c @@ -0,0 +1,1921 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * IB (InfiniBand) specific functions. + */ + +/* + * The reference for the functions in this file is the + * + * Mellanox HCA Flash Programming Application Note + * (Mellanox document number 2205AN) + * rev 1.44, 2007. Chapter 4 in particular. + * + * NOTE: this Mellanox document is labelled Confidential + * so DO NOT move this file out of usr/closed without + * explicit approval from Sun Legal. + */ + +/* + * IMPORTANT NOTE: + * 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 (bigendian). + * 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. + */ + + +#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/MELLANOX.h" +#include "../../hdrs/tavor_ib.h" + + + +char *devprefix = "/devices"; +char drivername[] = "tavor\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 tavor_identify(struct devicelist *thisdev); +static int tavor_get_guids(struct ib_encap_ident *handle); +static int tavor_close(struct devicelist *flashdev); +static void tavor_cisco_extensions(mlx_xps_t *hcaxps, mlx_xps_t *diskxps); +static uint16_t crc16(uint8_t *image, uint32_t size); +static int tavor_write_sector(int fd, int sectnum, int32_t *data); +static int tavor_zero_sig_crc(int fd, uint32_t start); +static int tavor_write_xps_fia(int fd, uint32_t offset, uint32_t start); +static int tavor_write_xps_crc_sig(int fd, uint32_t offset, uint16_t newcrc); +static int tavor_blast_image(int fd, int prisec, uint32_t hcafia, + uint32_t sectsz, struct mlx_xps *newxps); +static int tavor_readback(int infd, int whichsect, int sectsz); + + + +int +fw_readfw(struct devicelist *flashdev, char *filename) +{ + + int rv = FWFLASH_SUCCESS; + int fd; + mode_t mode = S_IRUSR | S_IWUSR; + uint8_t pchunks; + uint8_t *raw_pfi; + uint8_t *raw_sfi; + uint32_t j, offset; + uint32_t pfia, sfia, psz, ssz; + tavor_flash_ioctl_t tfi_data; + struct ib_encap_ident *manuf; + struct mlx_xps *lpps; + struct mlx_xps *lsps; +#if defined(_LITTLE_ENDIAN) + uint32_t *ptr; +#endif + + errno = 0; + if ((fd = open(filename, O_RDWR|O_CREAT|O_DSYNC, mode)) < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to open specified file " + "(%s) for writing: %s\n"), filename, strerror(errno)); + return (FWFLASH_FAILURE); + } + + manuf = + (struct ib_encap_ident *)(uintptr_t)flashdev->ident->encap_ident; + lpps = (struct mlx_xps *)(uintptr_t)manuf->pps; + lsps = (struct mlx_xps *)(uintptr_t)manuf->sps; + + /* + * Now that we've got an open, init'd fd, we can read the + * xFI from the device itself. We've already got the IS + * and xPS stored in manuf. + */ + + /* stash some values for later */ + pfia = MLXSWAPBITS32(lpps->fia); + sfia = MLXSWAPBITS32(lsps->fia); + psz = MLXSWAPBITS32(lpps->fis); + ssz = MLXSWAPBITS32(lsps->fis); + + /* Invariant Sector comes first */ + if ((j = write(fd, manuf->inv, manuf->sector_sz)) != + manuf->sector_sz) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write HCA Invariant Sector " + "(%d of %d bytes)\n"), + j, manuf->sector_sz); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } else { + fprintf(stdout, gettext("Writing .")); + } + + /* followed by Primary Pointer Sector */ + if ((j = write(fd, manuf->pps, manuf->sector_sz)) != + manuf->sector_sz) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write HCA Primary Pointer " + "Sector (%d of %d bytes)\n)"), + j, manuf->sector_sz); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } else { + fprintf(stdout, " ."); + } + + /* followed by Secondary Pointer Sector */ + if ((j = write(fd, manuf->sps, manuf->sector_sz)) != + manuf->sector_sz) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write HCA Secondary Pointer " + "Sector (%d of %d bytes)\n"), + j, manuf->sector_sz); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } else { + fprintf(stdout, " ."); + } + + /* Now for the xFI sectors */ + pchunks = psz / manuf->sector_sz; + + if ((psz % manuf->sector_sz) != 0) + pchunks++; + + /* Get the PFI, then the SFI */ + if ((raw_pfi = calloc(1, pchunks * manuf->sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for " + "device's Primary Firmware Image\n")); + return (FWFLASH_FAILURE); + } + bzero(&tfi_data, sizeof (tavor_flash_ioctl_t)); + tfi_data.tf_type = TAVOR_FLASH_READ_SECTOR; + j = pfia / manuf->sector_sz; + + for (offset = 0; offset < psz; offset += manuf->sector_sz) { + tfi_data.tf_sector_num = j; + tfi_data.tf_sector = (caddr_t)&raw_pfi[offset]; + rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &tfi_data); + if (rv < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read sector %d of " + "HCA Primary Firmware Image\n"), j); + free(raw_pfi); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } + ++j; + } + + /* + * It appears that the tavor driver is returning a signed + * -1 (0xffff) in unassigned quadlets if we read a sector + * that isn't full, so for backwards compatibility with + * earlier fwflash versions, we need to zero out what + * remains in the sector. + */ + bzero(&raw_pfi[psz], (pchunks * manuf->sector_sz) - psz); + +#if defined(_LITTLE_ENDIAN) + ptr = (uint32_t *)(uintptr_t)raw_pfi; + for (j = 0; j < (pchunks * manuf->sector_sz / 4); j++) { + ptr[j] = htonl(ptr[j]); + if (j > psz) + break; + } +#endif + + if ((j = write(fd, raw_pfi, pchunks * manuf->sector_sz)) + != pchunks * manuf->sector_sz) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write HCA Primary Firmware " + "Image data (%d of %d bytes)\n"), + j, pchunks * manuf->sector_sz); + free(raw_pfi); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } else { + fprintf(stdout, " ."); + } + + pchunks = ssz / manuf->sector_sz; + + if ((ssz % manuf->sector_sz) != 0) + pchunks++; + + /* + * We allocate wholenum sectors, but only write out what we + * really need (ssz bytes) + */ + if ((raw_sfi = calloc(1, pchunks * manuf->sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for " + "device's Secondary Firmware Image\n")); + free(raw_pfi); + return (FWFLASH_FAILURE); + } + bzero(&tfi_data, sizeof (tavor_flash_ioctl_t)); + tfi_data.tf_type = TAVOR_FLASH_READ_SECTOR; + + /* get our starting sector number */ + j = sfia / manuf->sector_sz; + + for (offset = 0; offset < ssz; offset += manuf->sector_sz) { + tfi_data.tf_sector_num = j; + tfi_data.tf_sector = (caddr_t)&raw_sfi[offset]; + if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, + &tfi_data)) < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read sector %d of " + "HCA Secondary Firmware Image\n"), j); + (void) tavor_close(flashdev); + free(raw_pfi); + free(raw_sfi); + return (FWFLASH_FAILURE); + } + ++j; + } + + /* + * It appears that the tavor driver is returning a signed + * -1 (0xffff) in unassigned quadlets if we read a sector + * that isn't full, so for backwards compatibility with + * earlier fwflash versions, we need to zero out what + * remains in the sector. + */ + bzero(&raw_sfi[ssz], (pchunks * manuf->sector_sz) - ssz); + +#if defined(_LITTLE_ENDIAN) + ptr = (uint32_t *)(uintptr_t)raw_sfi; + for (j = 0; j < ssz / 4; j++) { + ptr[j] = htonl(ptr[j]); + } +#endif + + /* only write out ssz bytes */ + if ((j = write(fd, raw_sfi, ssz)) != ssz) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write HCA Secondary Firmware " + "Image data (%d of %d bytes)\n"), + j, ssz); + (void) tavor_close(flashdev); + free(raw_pfi); + free(raw_sfi); + return (FWFLASH_FAILURE); + } else { + fprintf(stdout, " .\n"); + } + + fprintf(stdout, + gettext("Done.\n")); + + free(raw_pfi); + free(raw_sfi); + /* + * this should succeed, but we don't just blindly ignore + * the return code cos that would be obnoxious. + */ + return (tavor_close(flashdev)); +} + + +/* + * 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) +{ + + int rv; + uint32_t j, sectsz, hpfia, hsfia; + uint32_t ipfia, isfia, ipfis, isfis; + struct ib_encap_ident *manuf; + struct mlx_is *iinv; + struct mlx_xps *ipps, *lpps; + struct mlx_xps *isps, *lsps; + struct mlx_xfi *ipfi, *isfi; + + /* + * linv, lpps/lsps are from the HCA whereas + * iinv/ipps/isps are in the on-disk firmware image that + * we've read in to the verifier->fwimage field, and are + * about to do some hand-waving with. + */ + + /* + * From the Mellanox HCA Flash programming app note, + * start of ch4, page36: + * =========================================================== + * Failsafe firmware programming ensures that an HCA device + * can boot up in a functional mode even if the burn process + * was interrupted (because of a power failure, reboot, user + * interrupt, etc.). This can be implemented by burning the + * new image to a vacant region on the Flash, and erasing the + * old image only after the new image is successfully burnt. + * This method ensures that there is at least one valid firmware + * image on the Flash at all times. Thus, in case a firmware + * image programming process is aborted for any reason, the HCA + * will still be able to boot up properly using the valid image + * on the Flash. + * ... + * + * 4.1 Notes on Image Programming of HCA Flashes + * Following are some general notes regarding the Flash memory + * in the context of Mellanox HCA devices: + * > The Flash memory is divided into sectors, and each sector + * must be erased prior to its programming. + * > The image to be burnt is byte packed and should be programmed + * into the Flash byte by byte, preserving the byte order, starting + * at offset zero. No amendments are needed for endianess. + * > It is recommended to program the Flash while the device is idle. + * =========================================================== + * + * The comment about endianness is particularly important for us + * since we operate on both big- and litte-endian hosts - it means + * we have to do some byte-swapping gymnastics + */ + + /* + * From the Mellanox HCA Flash programming app note, + * section 4.2.5 on page 41/42: + * =========================================================== + * 4.2.5 Failsafe Programming Example + * This section provides an example of a programming utility + * that performs a Failsafe firmware image update. The flow + * ensures that there is at least one valid firmware image on + * the Flash at all times. Thus, in case a firmware image pro- + * gramming process is aborted for any reason, the HCA will + * still be able to boot up properly using the valid image on + * the Flash. Any other flow that ensures the above is also + * considered a Failsafe firmware update. + * + * Update Flow: + * * Check the validity of the PPS and SPS: + * > If both PSs are valid, arbitrarily invalidate one of them + * > If both PSs are invalid, the image on flash is corrupted + * and cannot be updated in a Failsafe way. The user must + * burn a full image in a non-failsafe way. + * + * > If only the PPS is valid: + * i.Burn the secondary image (erase each sector first) + * ii.Burn the SPS with the correct image address (FIA field) + * iii.Invalidate the PPS + * + * > If only the SPS is valid: + * i.Burn the primary image (erase each sector first) + * ii.Burn the PPS with the correct image address (FIA field) + * iii.Invalidate the SPS + * =========================================================== + */ + + /* + * Other required tasks called from this function: + * + * * check for CISCO boot extensions in the current xPS, and + * if found, set them in the new xPS + * + * * update the xPS CRC field + * + * _then_ you can setup the outbound transfer to the HCA flash. + */ + + /* + * VERY IMPORTANT NOTE: + * The above text from the app note programming guide v1.44 does + * NOT match reality. If you try to do exactly what the above + * text specifies then you'll wind up with a warm, brick-like + * HCA that if you're really lucky has booted up in maintenance + * mode for you to re-flash. + * + * What you need to do is follow the example of the previous + * (v1.2 etc) version from the ON gate - which is what happens + * in this file. Basically - don't erase prior to writing a new + * sector, and _read back_ each sector after writing it. Especially + * the pointer sectors. Otherwise you'll get a warm brick. + */ + + manuf = + (struct ib_encap_ident *)(uintptr_t)flashdev->ident->encap_ident; + lpps = (struct mlx_xps *)(uintptr_t)manuf->pps; + lsps = (struct mlx_xps *)(uintptr_t)manuf->sps; + iinv = (struct mlx_is *)&verifier->fwimage[0]; + sectsz = 1 << MLXSWAPBITS16(iinv->log2sectsz + iinv->log2sectszp); + ipps = (struct mlx_xps *)&verifier->fwimage[sectsz/4]; + isps = (struct mlx_xps *)&verifier->fwimage[sectsz/2]; + + /* + * 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) printf("\nUnable to completely verify that this " + "firmware image\n\t(%s)\nis compatible with your " + "HCA\n\t%s\n", + verifier->imgfile, flashdev->access_devname); + (void) printf("\n\tDo you really want to continue? (Y/N): "); + + (void) fflush(stdin); + resp = getchar(); + if (resp != 'Y' && resp != 'y') { + (void) printf("\nNot proceeding with flash " + "operation of %s on %s\n", + verifier->imgfile, flashdev->access_devname); + return (FWFLASH_FAILURE); + } + } + + /* stash these for later */ + hpfia = MLXSWAPBITS32(lpps->fia); + hsfia = MLXSWAPBITS32(lsps->fia); + + /* where does the on-disk image think everything is at? */ + ipfia = MLXSWAPBITS32(ipps->fia); + isfia = MLXSWAPBITS32(isps->fia); + ipfis = MLXSWAPBITS32(ipps->fis); + isfis = MLXSWAPBITS32(isps->fis); + + logmsg(MSG_INFO, "tavor: hpfia 0x%0x hsfia 0x%0x " + "ipfia 0x%0x isfia 0x%0x ipfis 0x%0x isfis 0x%0x\n", + hpfia, hsfia, ipfia, isfia, ipfis, isfis); + + if ((ipfis + isfis) > manuf->device_sz) { + /* + * This is bad - don't flash an image which is larger + * than the size of the HCA's flash + */ + logmsg(MSG_ERROR, + gettext("tavor: on-disk firmware image size (0x%lx bytes) " + "exceeds HCA's flash memory size (0x%lx bytes)!\n"), + ipfis + isfis, manuf->device_sz); + logmsg(MSG_ERROR, + gettext("tavor: not flashing this image (%s)\n"), + verifier->imgfile); + return (FWFLASH_FAILURE); + } + + /* + * The Mellanox HCA Flash app programming note does _not_ + * specify that you have to insert the HCA's guid section + * into the flash image before burning it. + * + * HOWEVER it was determined during testing that this is + * actually required (otherwise your HCA's GUIDs revert to + * the manufacturer's defaults, ugh!), so we'll do it too. + */ + + ipfi = (struct mlx_xfi *)&verifier->fwimage[ipfia/4]; + isfi = (struct mlx_xfi *)&verifier->fwimage[isfia/4]; + + /* + * Here we check against our stored, properly-bitwise-munged copy + * of the HCA's GUIDS. If they're not set to default AND the OUI + * is MLX_OUI, then they're ok so we copy the HCA's version into + * our in-memory copy and blat it. If the GUIDs don't match this + * condition, then we use the default GUIDs which are in the on-disk + * firmware image instead. + */ + if (((manuf->ibguids[0] != MLX_DEFAULT_NODE_GUID) && + (manuf->ibguids[1] != MLX_DEFAULT_P1_GUID) && + (manuf->ibguids[2] != MLX_DEFAULT_P2_GUID) && + (manuf->ibguids[3] != MLX_DEFAULT_SYSIMG_GUID)) && + ((((manuf->ibguids[0] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) || + (((manuf->ibguids[1] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) || + (((manuf->ibguids[2] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) || + (((manuf->ibguids[3] & HIGHBITS64) >> OUISHIFT) == MLX_OUI))) { + /* The GUIDs are ok, blat them into the in-memory image */ + j = ((ipfia + MLXSWAPBITS32(ipfi->nguidptr)) / 4) - 4; + bcopy(manuf->pri_guid_section, &verifier->fwimage[j], + sizeof (struct mlx_guid_sect)); + j = ((isfia + MLXSWAPBITS32(isfi->nguidptr)) / 4) - 4; + bcopy(manuf->sec_guid_section, &verifier->fwimage[j], + sizeof (struct mlx_guid_sect)); + } else { + /* + * The GUIDs are hosed, we'll have to use + * the vendor defaults in the image instead + */ + logmsg(MSG_ERROR, + gettext("tavor: HCA's GUID section is set to defaults or " + " is invalid, using firmware image manufacturer's " + "default GUID section instead\n")); + } + + /* Just in case somebody is booting from this card... */ + tavor_cisco_extensions(lpps, ipps); + tavor_cisco_extensions(lsps, isps); + + /* first we write the secondary image and SPS, then the primary */ + rv = tavor_blast_image(manuf->fd, 2, hsfia, manuf->sector_sz, isps); + if (rv != FWFLASH_SUCCESS) { + logmsg(MSG_INFO, + "tavor: failed to update #2 firmware image\n"); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } + + rv = tavor_blast_image(manuf->fd, 1, hpfia, manuf->sector_sz, ipps); + if (rv != FWFLASH_SUCCESS) { + logmsg(MSG_INFO, + "tavor: failed to update #1 firmware image\n"); + (void) tavor_close(flashdev); + return (FWFLASH_FAILURE); + } + + /* final update marker to the user */ + (void) printf(" +\n"); + return (tavor_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; + + 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("%s identification function unable " + "to allocate space for device entry\n")); + di_devfs_path_free(devpath); + return (rv); + } + + /* 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("Unable to calloc space " + "for a devfs name\n")); + di_devfs_path_free(devpath); + (void) free(newdev); + return (FWFLASH_FAILURE); + } + snprintf(newdev->access_devname, devlength, + "%s%s%s", devprefix, devpath, devsuffix); + + /* CHECK VARIOUS IB THINGS HERE */ + + if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: 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); + } + + rv = tavor_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("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("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, "ib:fw_identify:\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) +{ + + struct ib_encap_ident *encap; + + + encap = (struct ib_encap_ident *)thisdev->ident->encap_ident; + + fprintf(stdout, gettext("Device[%d] %s\n Class [%s]\n"), + thisdev->index, thisdev->access_devname, 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]); + + if (encap->pn_len != 0) { + fprintf(stdout, + gettext("\tFirmware revision : %s\n" + "\tProduct\t\t: %s\n" + "\tPSID\t\t: %s\n"), + thisdev->ident->revid, + encap->info.mlx_pn, + encap->info.mlx_psid); + } else { + fprintf(stdout, + gettext("\tFirmware revision : %s\n" + "\tNo hardware information available for this " + "device\n"), thisdev->ident->revid); + } + fprintf(stdout, "\n\n"); + + return (tavor_close(thisdev)); +} + + +/* + * Helper functions lurk beneath this point + */ + + +/* + * tavor_identify performs the following actions: + * + * allocates and assigns thisdev->vpr + * + * allocates space for the 4 GUIDs which each IB device must have + * queries the tavor driver for this device's GUIDs + * + * determines the hardware vendor, so that thisdev->vpr->vid + * can be set correctly + */ +static int +tavor_identify(struct devicelist *thisdev) +{ + int rv = FWFLASH_SUCCESS; + int fd, ret, i; + + tavor_flash_init_ioctl_t init_ioctl; + tavor_flash_ioctl_t info; + struct ib_encap_ident *manuf; + cfi_t cfi; + char temppsid[17]; + char rawpsid[16]; + +#if defined(_LITTLE_ENDIAN) + uint32_t *ptr; +#endif + + /* open the device */ + /* hook thisdev->ident->encap_ident to ib_encap_ident */ + /* check that all the bits are sane */ + /* return success, if warranted */ + + errno = 0; + if ((fd = open(thisdev->access_devname, O_RDONLY)) < 0) { + logmsg(MSG_INFO, + gettext("tavor: 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_encap_ident_t))) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to calloc space for a " + "%s-attached handle structure\n"), + drivername); + return (FWFLASH_FAILURE); + } + manuf->magic = FWFLASH_IB_MAGIC_NUMBER; + manuf->state = FWFLASH_IB_STATE_NONE; + manuf->fd = fd; + + 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.tf_cfi_info[0x4] = MLXSWAPBITS32(cfi.cfi_int[0x4]); + + errno = 0; + ret = ioctl(fd, TAVOR_IOCTL_FLASH_INIT, &init_ioctl); + if (ret < 0) { + logmsg(MSG_ERROR, + gettext("ib: TAVOR_IOCTL_FLASH_INIT failed: %s\n"), + strerror(errno)); + free(manuf); + close(fd); + return (FWFLASH_FAILURE); + } + + manuf->hwrev = init_ioctl.tf_hwrev; + + /* + * 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 < TAVOR_FLASH_CFI_SIZE_QUADLET; i++) { + cfi.cfi_int[i] = MLXSWAPBITS32(init_ioctl.tf_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 AMD */ + if (manuf->cmd_set != TAVOR_FLASH_AMD_CMDSET) { + logmsg(MSG_ERROR, + gettext("tavor: Unsupported flash device " + "command set\n")); + free(manuf); + close(fd); + return (FWFLASH_FAILURE); + } + /* set some defaults */ + manuf->device_sz = TAVOR_FLASH_DEVICE_SZ_DEFAULT; + } else { + if (manuf->cmd_set != TAVOR_FLASH_AMD_CMDSET && + manuf->cmd_set != TAVOR_FLASH_INTEL_CMDSET) { + logmsg(MSG_ERROR, + gettext("ib: Unknown flash device command set\n")); + free(manuf); + close(fd); + return (FWFLASH_FAILURE); + } + /* 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, "sector_sz: 0x%08x\ndevice_sz: 0x%08x\n", + manuf->sector_sz, manuf->device_sz); + + manuf->state |= FWFLASH_IB_STATE_MMAP; + + /* set firmware revision */ + manuf->fw_rev.major = init_ioctl.tf_fwrev.tfi_maj; + manuf->fw_rev.minor = init_ioctl.tf_fwrev.tfi_min; + manuf->fw_rev.subminor = init_ioctl.tf_fwrev.tfi_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("ib: Unable to allocate space for a VPR " + "record.\n")); + free(thisdev->ident); + free(manuf->info.mlx_pn); + free(manuf->info.mlx_psid); + free(manuf->info.mlx_id); + free(manuf); + close(fd); + return (FWFLASH_FAILURE); + } + (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.%04d", + manuf->fw_rev.major, manuf->fw_rev.minor, + manuf->fw_rev.subminor); + + bzero(manuf->ibguids, sizeof (manuf->ibguids)); + + /* + * For convenience we read in the Invariant Sector as + * well as both the Primary and Secondary Pointer Sectors + */ + + if ((manuf->inv = calloc(1, manuf->sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for storing " + "the HCA's Invariant Sector\n")); + return (FWFLASH_FAILURE); + } + bzero(&info, sizeof (tavor_flash_ioctl_t)); + + info.tf_type = TAVOR_FLASH_READ_SECTOR; + info.tf_sector = (caddr_t)manuf->inv; + info.tf_sector_num = 0; + + errno = 0; + + if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info)) + < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read HCA Invariant Sector\n")); + return (FWFLASH_FAILURE); + } + +#if defined(_LITTLE_ENDIAN) + ptr = (uint32_t *)(uintptr_t)manuf->inv; + for (i = 0; i < (manuf->sector_sz / 4); i++) { + ptr[i] = htonl(ptr[i]); + } +#endif + + if ((manuf->pps = calloc(1, manuf->sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for storing " + "the HCA's Primary Pointer Sector\n")); + return (FWFLASH_FAILURE); + } + bzero(&info, sizeof (tavor_flash_ioctl_t)); + + info.tf_type = TAVOR_FLASH_READ_SECTOR; + info.tf_sector = (caddr_t)manuf->pps; + info.tf_sector_num = 1; + + errno = 0; + + if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info)) + < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read HCA Primary " + "Pointer Sector\n")); + return (FWFLASH_FAILURE); + } + +#if defined(_LITTLE_ENDIAN) + ptr = (uint32_t *)(uintptr_t)manuf->pps; + for (i = 0; i < (manuf->sector_sz / 4); i++) { + ptr[i] = htonl(ptr[i]); + } +#endif + + if ((manuf->sps = calloc(1, manuf->sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for storing " + "the HCA's Secondary Pointer Sector\n")); + return (FWFLASH_FAILURE); + } + bzero(&info, sizeof (tavor_flash_ioctl_t)); + + info.tf_type = TAVOR_FLASH_READ_SECTOR; + info.tf_sector = (caddr_t)manuf->sps; + info.tf_sector_num = 2; + + errno = 0; + + if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info)) + < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read HCA Secondary " + "Pointer Sector\n")); + return (FWFLASH_FAILURE); + } + +#if defined(_LITTLE_ENDIAN) + ptr = (uint32_t *)(uintptr_t)manuf->sps; + for (i = 0; i < (manuf->sector_sz / 4); i++) { + ptr[i] = htonl(ptr[i]); + } +#endif + + if ((ret = tavor_get_guids(manuf)) != FWFLASH_SUCCESS) { + logmsg(MSG_INFO, + gettext("ib: No guids found for device %s!\n"), + thisdev->access_devname); + } + + /* set hw part number, psid, and name in handle */ + bzero(temppsid, 17); + bcopy(manuf->pps+FLASH_PS_PSID_OFFSET, &rawpsid, 16); + + for (i = 0; i < 16; i += 4) { + temppsid[i] = rawpsid[i+3]; + temppsid[i+1] = rawpsid[i+2]; + temppsid[i+2] = rawpsid[i+1]; + temppsid[i+3] = rawpsid[i]; + } + logmsg(MSG_INFO, + "tavor: have raw '%s', want munged '%s'\n", + rawpsid, temppsid); + + /* now walk the magic decoder ring table */ + manuf->info.mlx_pn = NULL; + manuf->info.mlx_psid = NULL; + manuf->info.mlx_id = NULL; + manuf->pn_len = 0; + + for (i = 0; i < MLX_MAX_ID; i++) { + if ((strncmp(temppsid, mlx_mdr[i].mlx_psid, + MLX_PSID_SZ)) == 0) { + /* matched */ + if ((manuf->info.mlx_pn = calloc(1, + strlen(mlx_mdr[i].mlx_pn) + 1)) == NULL) { + logmsg(MSG_INFO, + "tavor: no space available for the " + "HCA PSID record (1)\n"); + } else { + (void) strlcpy(manuf->info.mlx_pn, + mlx_mdr[i].mlx_pn, + strlen(mlx_mdr[i].mlx_pn) + 1); + manuf->pn_len = strlen(mlx_mdr[i].mlx_pn); + } + + if ((manuf->info.mlx_psid = calloc(1, + strlen(mlx_mdr[i].mlx_psid) + 1)) == NULL) { + logmsg(MSG_INFO, + "tavor: no space available for the " + "HCA PSID record (2)\n"); + } else { + (void) strlcpy(manuf->info.mlx_psid, + mlx_mdr[i].mlx_psid, + strlen(mlx_mdr[i].mlx_psid) + 1); + } + if ((manuf->info.mlx_id = calloc(1, + strlen(mlx_mdr[i].mlx_id) + 1)) == NULL) { + logmsg(MSG_INFO, + "tavor: no space available for the " + "HCA PSID record (3)\n"); + } else { + (void) strlcpy(manuf->info.mlx_id, + mlx_mdr[i].mlx_id, + strlen(mlx_mdr[i].mlx_id) + 1); + } + } + } + if ((manuf->pn_len == 0) || (i == MLX_MAX_ID)) { + logmsg(MSG_INFO, + "tavor: No hardware part number information available " + "for this HCA\n"); + /* Until we deliver the arbel driver, it's all Mellanox */ + i = strlen("No hardware information available for this device"); + + thisdev->ident->pid = calloc(1, i + 2); + sprintf(thisdev->ident->pid, "No hardware information " + "available for this device"); + } else { + 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("ib: Unable to allocate space for a " + "hardware identifier\n")); + free(thisdev->ident); + free(manuf->info.mlx_pn); + free(manuf->info.mlx_psid); + free(manuf->info.mlx_id); + free(manuf); + close(fd); + return (FWFLASH_FAILURE); + } + } + + for (i = 0; i < 4; i++) { + if ((thisdev->addresses[i] = calloc(1, + (2 * sizeof (uint64_t)) + 1)) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for a " + "human-readable HCA guid\n")); + return (FWFLASH_FAILURE); + } + (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 (rv); +} + +/*ARGSUSED*/ +static int +tavor_get_guids(struct ib_encap_ident *handle) +{ + int rv, j; + uint32_t i = 0x00; + tavor_flash_ioctl_t info; + struct mlx_guid_sect *p, *s; + +#if defined(_LITTLE_ENDIAN) + uint32_t *ptr, tmp; +#endif + + /* + * The reference for this function is the + * Mellanox HCA Flash Programming Application Note + * rev 1.44, 2007. Chapter 4 in particular. + * + * NOTE: this Mellanox document is labelled Confidential + * so DO NOT move this file out of usr/closed without + * explicit approval from Sun Legal. + */ + + /* + * We need to check for both the Primary and Secondary + * Image GUIDs. handle->pps and handle->sps should be + * non-NULL by the time we're called, since we depend + * on them being stashed in handle. Saves on an ioctl(). + */ + + /* make sure we've got our fallback position organised */ + for (i = 0; i < 4; i++) { + handle->ibguids[i] = 0x00000000; + } + + /* convenience .... */ + + if ((p = calloc(1, sizeof (mlx_guid_sect_t))) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for " + "HCA guid record (1)\n")); + return (FWFLASH_FAILURE); + } + if ((s = calloc(1, sizeof (mlx_guid_sect_t))) == NULL) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to allocate space for " + "HCA guid record (2)\n")); + free(p); + return (FWFLASH_FAILURE); + } + + bcopy(&handle->pps[0], &i, 4); + handle->pfi_guid_addr = MLXSWAPBITS32(i) + FLASH_GUID_PTR; + bcopy(&handle->sps[0], &i, 4); + handle->sfi_guid_addr = MLXSWAPBITS32(i) + FLASH_GUID_PTR; + + bzero(&info, sizeof (tavor_flash_ioctl_t)); + info.tf_type = TAVOR_FLASH_READ_QUADLET; + info.tf_addr = handle->pfi_guid_addr; + + errno = 0; + + rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ, &info); + if (rv < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read Primary Image " + "guid offset\n")); + free(p); + free(s); + return (FWFLASH_FAILURE); + } + + /* + * This is because we want the whole of the section + * including the 16 reserved bytes at the front so + * that if we recalculate the CRC we've got the correct + * data to do it with + */ + info.tf_addr = handle->pfi_guid_addr + info.tf_quadlet + - FLASH_GUID_PTR - 16; + + bzero(handle->pri_guid_section, sizeof (mlx_guid_sect_t)); + + for (j = 0; j < 13; j++) { + errno = 0; + if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ, + &info)) < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read Primary Image " + "guid chunk %d\n"), j); + } + handle->pri_guid_section[j] = info.tf_quadlet; + info.tf_addr += 4; + } + bcopy(&handle->pri_guid_section, p, sizeof (struct mlx_guid_sect)); + + /* now grab the secondary guid set */ + bzero(&info, sizeof (tavor_flash_ioctl_t)); + info.tf_type = TAVOR_FLASH_READ_QUADLET; + info.tf_addr = handle->sfi_guid_addr; + + errno = 0; + + if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ, + &info)) < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read Secondary Image " + "guid offset (%s)\n"), strerror(errno)); + free(p); + free(s); + return (FWFLASH_FAILURE); + } + + info.tf_addr = handle->sfi_guid_addr + info.tf_quadlet + - FLASH_GUID_PTR - 16; + + bzero(handle->sec_guid_section, sizeof (mlx_guid_sect_t)); + + for (j = 0; j < 13; j++) { + errno = 0; + if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ, + &info)) < 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read Secondary Image " + "guid chunk %d (%s)\n"), j, strerror(errno)); + return (FWFLASH_FAILURE); + } + handle->sec_guid_section[j] = info.tf_quadlet; + info.tf_addr += 4; + } + + bcopy(&handle->sec_guid_section, s, sizeof (struct mlx_guid_sect)); + +#if defined(_LITTLE_ENDIAN) + + /* + * We don't actually care about p or s later on if we + * write to the HCA - we've already stored the binary + * form in handle->pri_guid_section and handle->sec_guid_section. + * What we're doing here is creating human-readable forms. + */ + + ptr = (uint32_t *)(uintptr_t)p; + for (j = 0; j < 14; j += 2) { + tmp = ptr[j]; + ptr[j] = ptr[j+1]; + ptr[j+1] = tmp; + } + + ptr = (uint32_t *)(uintptr_t)s; + for (j = 0; j < 14; j += 2) { + tmp = ptr[j]; + ptr[j] = ptr[j+1]; + ptr[j+1] = tmp; + } +#endif + + /* + * We don't check and munge the GUIDs to the manufacturer's + * defaults, because if the GUIDs are actually set incorrectly + * at identify time, we really need to know that. + * + * If the GUIDs are bogus, then we'll fix that in fw_writefw() + * by blatting the manufacturer's defaults from the firmware + * image file instead. + */ + if ((p->nodeguid == s->nodeguid) && + (p->port1guid == s->port1guid) && + (p->port2guid == s->port2guid) && + (p->sysimguid == s->sysimguid)) { + logmsg(MSG_INFO, + "tavor: primary and secondary guids are the same\n"); + handle->ibguids[0] = p->nodeguid; + handle->ibguids[1] = p->port1guid; + handle->ibguids[2] = p->port2guid; + handle->ibguids[3] = p->sysimguid; + } else { + /* + * We're going to assume that the guids which are numerically + * larger than the others are correct and copy them to + * handle->ibguids. + * + * For those in the know wrt InfiniBand, if this assumption + * is incorrect, _please_ bug this and fix it, adding a + * comment or two to indicate why + */ + logmsg(MSG_INFO, + "tavor: primary and secondary guids don't all match\n"); + + if (s->nodeguid > p->nodeguid) { + handle->ibguids[0] = s->nodeguid; + handle->ibguids[1] = s->port1guid; + handle->ibguids[2] = s->port2guid; + handle->ibguids[3] = s->sysimguid; + bzero(p, sizeof (struct mlx_guid_sect)); + } else { + handle->ibguids[0] = p->nodeguid; + handle->ibguids[1] = p->port1guid; + handle->ibguids[2] = p->port2guid; + handle->ibguids[3] = p->sysimguid; + bzero(s, sizeof (struct mlx_guid_sect)); + } + } + + free(p); + free(s); + + if (fwflash_debug) { + for (i = 0; i < 4; i++) { + logmsg(MSG_INFO, "ibguids[%d] %0llx\n", i, + handle->ibguids[i]); + } + } + + return (FWFLASH_SUCCESS); +} + + +int +tavor_close(struct devicelist *flashdev) +{ + + struct ib_encap_ident *handle; + + handle = (struct ib_encap_ident *)flashdev->ident->encap_ident; + if (handle->fd > 0) { + (void) ioctl(handle->fd, TAVOR_IOCTL_FLASH_FINI); + errno = 0; + if (close(handle->fd) != 0) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to properly close " + "device %s! (%s)\n"), + flashdev->access_devname, + strerror(errno)); + return (FWFLASH_FAILURE); + } + return (FWFLASH_SUCCESS); + } else + return (FWFLASH_FAILURE); +} + + +/* + * We would not need this if it were not for Cisco's image using the + * VSD to store boot options and flags for their PXE boot extension, + * but not setting the proper default values for the extension in + * their image. As it turns out, some of the data for the extension + * is stored in the VSD in the firmware file, and the rest is set by + * their firmware utility. That's not very nice for us, since it could + * change at any time without our knowledge. Well, for the time being, + * we can use this to examine and fix up anything in the VSD that we might + * need to handle, for any vendor specific settings. + */ +static void +tavor_cisco_extensions(mlx_xps_t *hcaxps, mlx_xps_t *diskxps) +{ + uint16_t sig1, sig2; + uint32_t i; + + + bcopy(hcaxps->vsdpsid, &i, 4); + sig1 = htonl(i); + bcopy(&hcaxps->vsdpsid[223], &i, 4); + sig2 = htonl(i); + + + if (sig1 == FLASH_VSD_CISCO_SIGNATURE && + sig2 == FLASH_VSD_CISCO_SIGNATURE) { + logmsg(MSG_INFO, + "tavor: CISCO signature found in HCA's VSD, copying to " + "new image's VSD\n"); + + i = htonl(FLASH_VSD_CISCO_SIGNATURE); + bcopy(&i, diskxps->vsdpsid, 2); + + /* + * Set the boot_version field to '2'. This value is + * located in the 2nd byte of the last uint32_t. + * Per the previous version of fwflash, we just or + * the bit in and get on with it. + */ + + i = (diskxps->vsdpsid[222] | FLASH_VSD_CISCO_BOOT_VERSION); + bcopy(&i, &diskxps->vsdpsid[222], 2); + /* + * Now set some defaults for the SRP boot extension, + * currently the only extension we support. These flags + * are located in the second uint32_t of the VSD. + */ + + logmsg(MSG_INFO, "tavor: CISCO boot flags currently set " + "to 0x%08x\n", + diskxps->vsdpsid[1]); + + diskxps->vsdpsid[1] = + htonl(diskxps->vsdpsid[1] | + FLASH_VSD_CISCO_FLAG_AUTOUPGRADE | + FLASH_VSD_CISCO_BOOT_OPTIONS | + FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_1 | + FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_2 | + FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_SCAN | + FLASH_VSD_CISCO_FLAG_BOOT_TYPE_WELL_KNOWN | + FLASH_VSD_CISCO_FLAG_BOOT_TRY_FOREVER); + + logmsg(MSG_INFO, "tavor: CISCO boot flags now set " + "to 0x%08x\n", + diskxps->vsdpsid[1]); + } else + logmsg(MSG_INFO, + "tavor: CISCO signature not found in HCA's VSD\n"); +} + + +static int +tavor_write_sector(int fd, int sectnum, int32_t *data) +{ + int rv, i; + tavor_flash_ioctl_t cmd; + + + bzero(&cmd, sizeof (tavor_flash_ioctl_t)); + + cmd.tf_type = TAVOR_FLASH_WRITE_SECTOR; + cmd.tf_sector_num = sectnum; + cmd.tf_sector = (caddr_t)data; + + errno = 0; + + logmsg(MSG_INFO, + "tavor: tavor_write_sector(fd %d, sectnum 0x%x, data 0x%lx)\n", + fd, sectnum, data); + logmsg(MSG_INFO, + "tavor:\n" + "\tcmd.tf_type %d\n" + "\tcmd.tf_sector 0x%lx\n" + "\tcmd.tf_sector_num %d\n", + cmd.tf_type, data, cmd.tf_sector_num); + + /* + * If we're debugging, dump the first 64 uint32_t that we've + * been passed + */ + if (fwflash_debug > 0) { + i = 0; + while (i < 64) { + logmsg(MSG_INFO, + "%02x: %08x %08x %08x %08x\n", + i, data[i], data[i+1], + data[i+2], data[i+3]); + i += 4; + } + } + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_ERROR, + gettext("tavor: WRITE SECTOR failed for sector " + "%d: %s\n"), + sectnum, strerror(errno)); + return (FWFLASH_FAILURE); + } else + return (FWFLASH_SUCCESS); +} + +/* + * Write zeros to the on-HCA signature and CRC16 fields of sector. + * + * NOTE we do _not_ divide start by 4 because we're talking to the + * HCA, and not finding an offset into verifier->fwimage. + */ + +static int +tavor_zero_sig_crc(int fd, uint32_t start) +{ + int i, rv; + tavor_flash_ioctl_t cmd; + + /* signature first, then CRC16 */ + bzero(&cmd, sizeof (tavor_flash_ioctl_t)); + cmd.tf_type = TAVOR_FLASH_WRITE_BYTE; + cmd.tf_byte = 0x00; + + logmsg(MSG_INFO, + "tavor: tavor_zero_sig_crc(fd %d, start 0x%04x)\n", + fd, start); + + for (i = 0; i < 4; i++) { + cmd.tf_addr = start + FLASH_PS_SIGNATURE_OFFSET + i; + + logmsg(MSG_INFO, + "tavor: invalidating xPS sig (offset from IS 0x%04x) " + "byte %d\n", + cmd.tf_addr, i); + errno = 0; + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + gettext("tavor: Unable to write 0x00 to " + "offset 0x%04x from IS (sig byte %d): %s\n"), + cmd.tf_addr, i, strerror(errno)); + return (FWFLASH_FAILURE); + } + } + + cmd.tf_byte = 0x00; + for (i = 0; i < 2; i++) { + cmd.tf_addr = start + FLASH_PS_CRC16_OFFSET + i; + + logmsg(MSG_INFO, + "tavor: invalidating xPS CRC16 (offset from IS 0x%04x) " + "byte %d\n", + cmd.tf_addr, i); + errno = 0; + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + gettext("tavor: Unable to write 0x00 to " + "offset 0x%04x from IS (CRC16 byte %d): %s\n"), + cmd.tf_addr, i, strerror(errno)); + return (FWFLASH_FAILURE); + } + } + return (FWFLASH_SUCCESS); +} + + +/* + * Write a new FIA for the given xPS. The _caller_ handles + * any required byte-swapping for us. + * + * NOTE we do _not_ divide start by 4 because we're talking to the + * HCA, and not finding an offset into verifier->fwimage. + */ +static int +tavor_write_xps_fia(int fd, uint32_t offset, uint32_t start) +{ + int i, rv; + uint8_t *addrbytep; + tavor_flash_ioctl_t cmd; + + logmsg(MSG_INFO, + "tavor: tavor_write_xps_fia(fd %d, offset 0x%04x, " + "start 0x%04x)\n", + fd, offset, start); + + addrbytep = (uint8_t *)&start; + + bzero(&cmd, sizeof (tavor_flash_ioctl_t)); + cmd.tf_type = TAVOR_FLASH_WRITE_BYTE; + for (i = 0; i < 4; i++) { + cmd.tf_byte = addrbytep[i]; + cmd.tf_addr = offset + FLASH_PS_FI_ADDR_OFFSET + i; + logmsg(MSG_INFO, + "tavor: writing xPS' new FIA, byte %d (0x%0x) at " + "offset from IS 0x%04x\n", + i, cmd.tf_byte, cmd.tf_addr); + errno = 0; + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + gettext("tavor: Unable to write byte %d " + "of xPS new FIA (0x%0x, offset from IS " + "0x%04x): %s\n"), + i, cmd.tf_byte, cmd.tf_addr, strerror(errno)); + return (FWFLASH_FAILURE); + } + } + return (FWFLASH_SUCCESS); +} + + +/* + * Write the new CRC16 and Signature to the given xPS. The caller + * has already byte-swapped newcrc if that's necessary. + * + * NOTE we do _not_ divide start by 4 because we're talking to the + * HCA, and not finding an offset into verifier->fwimage. + */ +static int +tavor_write_xps_crc_sig(int fd, uint32_t offset, uint16_t newcrc) +{ + int i, rv; + uint8_t *bytep; + uint32_t tempsig; + tavor_flash_ioctl_t cmd; + + logmsg(MSG_INFO, + "tavor: tavor_write_xps_crc_sig(fd %d, offset 0x%04x, " + "newcrc 0x%04x)\n", + fd, offset, newcrc); + + bytep = (uint8_t *)&newcrc; + + bzero(&cmd, sizeof (tavor_flash_ioctl_t)); + cmd.tf_type = TAVOR_FLASH_WRITE_BYTE; + for (i = 0; i < 2; i++) { + cmd.tf_byte = bytep[i]; + cmd.tf_addr = offset + FLASH_PS_CRC16_OFFSET + i; + logmsg(MSG_INFO, + "tavor: writing new XPS CRC16, byte %d (0x%0x) at " + "offset from IS 0x%04x\n", + i, bytep[i], cmd.tf_addr); + errno = 0; + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + gettext("tavor: Unable to write byte %d " + "(0x%0x) of xPS' new CRC16 to offset " + "from IS 0x%04x: %s\n"), + i, bytep[i], cmd.tf_addr, strerror(errno)); + return (FWFLASH_FAILURE); + } + } + + tempsig = htonl(FLASH_PS_SIGNATURE); + bytep = (uint8_t *)&tempsig; + + for (i = 0; i < 4; i++) { + cmd.tf_byte = bytep[i]; + cmd.tf_addr = offset + FLASH_PS_SIGNATURE_OFFSET + i; + logmsg(MSG_INFO, + "tavor: writing new xPS Signature, byte %d (0x%0x) at " + "offset from IS 0x%04x\n", + i, bytep[i], cmd.tf_addr); + errno = 0; + + rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + gettext("tavor: Unable to write byte %d (0x%0x) " + "of xPS' signature at offset from IS 0x%04x: %s\n"), + i, bytep[i], cmd.tf_addr, strerror(errno)); + return (FWFLASH_FAILURE); + } + } + return (FWFLASH_SUCCESS); +} + + + +/* + * This function contains "Begin/End documentation departure point" + * because the reality of what actually _works_ is quite, quite + * different to what is written in the Mellanox HCA Flash Application + * Programming Guide. + */ +static int +tavor_blast_image(int fd, int prisec, uint32_t hcafia, uint32_t sectsz, + struct mlx_xps *newxps) +{ + uint32_t i, j, rv; + uint32_t startsectimg, startsecthca, numsect; + + if ((prisec != 1) && (prisec != 2)) { + logmsg(MSG_ERROR, + "tavor: invalid image number requested (%d)\n"); + return (FWFLASH_FAILURE); + } + + /* Begin documentation departure point */ + + /* zero the HCA's PPS signature and CRC */ + if (tavor_zero_sig_crc(fd, (prisec * sectsz)) + != FWFLASH_SUCCESS) { + logmsg(MSG_INFO, + "tavor: Unable zero HCA's %s signature " + "and CRC16 fields\n", + ((prisec == 1) ? "PPS" : "SPS")); + return (FWFLASH_FAILURE); + } + + logmsg(MSG_INFO, "tavor: zeroing HCA's %s sig and crc\n", + (prisec == 1) ? "pps" : "sps"); + + /* End documentation departure point */ + + /* make sure we don't inadvertently overwrite bits */ + + startsectimg = MLXSWAPBITS32(newxps->fia) / sectsz; + startsecthca = hcafia / sectsz; + + numsect = (MLXSWAPBITS32(newxps->fis) / sectsz) + + ((MLXSWAPBITS32(newxps->fis) % sectsz) ? 1 : 0); + + logmsg(MSG_INFO, "tavor: %s imgsize 0x%0x startsecthca %d, " + "startsectimg %d, num sectors %d\n", + (prisec == 1) ? "PFI" : "SFI", MLXSWAPBITS32(newxps->fis), + startsecthca, startsectimg, numsect); + + for (i = 0; i < numsect; i++) { + + j = (MLXSWAPBITS32(newxps->fia) + (i * sectsz)) / 4; + + logmsg(MSG_INFO, "tavor: image offset 0x%0x\n", j); + logmsg(MSG_INFO, "tavor: writing HCA sector %d\n", + i + startsecthca); + + if (tavor_write_sector(fd, i + startsecthca, + &verifier->fwimage[j]) + != FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write " + "sector %d to HCA\n"), + i + startsecthca); + return (FWFLASH_FAILURE); + } + (void) printf(" ."); + + rv = tavor_readback(fd, i + startsecthca, sectsz); + if (rv != FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read sector %d " + "back from HCA\n"), i + startsecthca); + return (FWFLASH_FAILURE); + } + (void) printf(" | "); + } + + /* Begin documentation departure point */ + + /* invalidate the xps signature and fia fields */ + newxps->signature = 0xffffffff; + newxps->crc16 = 0xffff; + /* we put the fia back to imgfia later */ + newxps->fia = 0xffffffff; + /* End documentation departure point */ + + /* success so far, now burn the new xPS */ + if (tavor_write_sector(fd, prisec, (int *)newxps) + != FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to write new %s " + "pointer sector to HCA\n"), + (prisec == 1) ? "primary" : "secondary"); + return (FWFLASH_FAILURE); + } + (void) printf(" ."); + + /* Begin documentation departure point */ + + /* write new fia to the HCA's pps */ + logmsg(MSG_INFO, "tavor: writing new fia (0x%0x) to HCA\n", + MLXSWAPBITS32(newxps->fia)); + + if (tavor_write_xps_fia(fd, (prisec * sectsz), + MLXSWAPBITS32(hcafia)) != FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to update HCA's %s " + "pointer sector FIA record\n"), + (prisec == 1) ? "primary" : "secondary"); + return (FWFLASH_FAILURE); + } + + /* don't forget the byte-swapping */ + newxps->fia = MLXSWAPBITS32(hcafia); + newxps->signature = + (uint32_t)MLXSWAPBITS32(FLASH_PS_SIGNATURE); + newxps->crc16 = + MLXSWAPBITS16(crc16((uint8_t *)newxps, FLASH_PS_CRC16_SIZE)); + + logmsg(MSG_INFO, "tavor: writing new fia 0x%0x, " + "sig 0x%0x and new crc16 0x%0x\n", + newxps->fia, MLXSWAPBITS32(newxps->signature), + newxps->crc16); + + if (tavor_write_xps_crc_sig(fd, (prisec * sectsz), + newxps->crc16) != FWFLASH_SUCCESS) { + /* + * Now we're REALLY hosed. If the card comes up at all, + * expect it to be in "Maintenance Mode". + */ + logmsg(MSG_ERROR, + gettext("tavor: Unable to update HCA's %s CRC " + "and Firmware Image signature fields\n"), + (prisec == 1) ? "PPS" : "SPS"); + return (FWFLASH_FAILURE); + } + + rv = tavor_readback(fd, prisec, sectsz); + if (rv != FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, + gettext("tavor: Unable to read %s pointer sector " + "from HCA\n"), + (prisec == 1) ? "Primary" : "Secondary"); + return (FWFLASH_FAILURE); + } + (void) printf(" |"); + /* End documentation departure point */ + return (FWFLASH_SUCCESS); +} + + +static int +tavor_readback(int infd, int whichsect, int sectsz) +{ + uint32_t *data; + tavor_flash_ioctl_t cmd; + int rv; + + bzero(&cmd, sizeof (tavor_flash_ioctl_t)); + data = calloc(1, sectsz); /* assumption! */ + + cmd.tf_type = TAVOR_FLASH_READ_SECTOR; + cmd.tf_sector_num = whichsect; + cmd.tf_sector = (caddr_t)data; + rv = ioctl(infd, TAVOR_IOCTL_FLASH_READ, &cmd); + if (rv < 0) { + logmsg(MSG_INFO, + "tavor: UNABLE TO READ BACK SECTOR %d from HCA\n", + whichsect); + return (FWFLASH_FAILURE); + } + free(data); + return (FWFLASH_SUCCESS); +} + + +/* + * crc16 - computes 16 bit crc of supplied buffer. + * image should be in network byteorder + * result is returned in host byteorder form + */ +static uint16_t +crc16(uint8_t *image, uint32_t size) +{ + const uint16_t poly = 0x100b; + uint32_t crc = 0xFFFF; + uint32_t word; + uint32_t i, j; + + 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]); + + 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); +} diff --git a/usr/src/cmd/fwflash/plugins/transport/i386/Makefile b/usr/src/cmd/fwflash/plugins/transport/i386/Makefile index 499f8ee4ad..260a39533d 100644 --- a/usr/src/cmd/fwflash/plugins/transport/i386/Makefile +++ b/usr/src/cmd/fwflash/plugins/transport/i386/Makefile @@ -18,28 +18,26 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# -# # cmd/fwflash/plugins/transport/i386 # -SRCS= ses.c +SRCS= ses.c tavor.c hermon.c OBJECTS= $(SRCS:%.c=%.o) PLUGINS= $(SRCS:%.c=%.so) -POFILES= ses.po +POFILES= $(SRCS:%.c=%.po) IDENTPOFILE= fwflash_transport_identify_ses.po -LINTFILE= ses.ln +LINTFILE= $(SRCS:%.c=%.ln) SLINKS= sgen.so CLEANFILES= $(OBJECTS) CLOBBERFILES= $(PLUGINS) $(POFILES) $(IDENTPOFILE) $(LINTFILE) $(SLINKS) + all: $(PLUGINS) _msg msg: $(IDENTPOFILE) @@ -59,17 +57,18 @@ $(PLUGINS) := FILEMODE = 0555 $(ROOTLIBFWFLASHPLUGINS)/$(SLINKS) : $(ROOTLIBFWFLASHPLUGINS)/ses.so @$(RM) $@ - $(SYMLINK) $(PLUGINS) $@ - + $(SYMLINK) ses.so $@ - -install: $(ROOTLIBFWFLASHPLUGINS) \ +install: all $(ROOTLIBFWFLASHPLUGINS) \ $(ROOTLIBFWFLASHPLUGINS)/ses.so \ + $(ROOTLIBFWFLASHPLUGINS)/tavor.so \ + $(ROOTLIBFWFLASHPLUGINS)/hermon.so \ $(ROOTLIBFWFLASHPLUGINS)/$(SLINKS) clobber clean: $(RM) $(CLEANFILES) $(CLOBBERFILES) +lint_SRCS: $(LINTFILE) lint: lint_SRCS %.o: ../common/%.c @@ -79,14 +78,17 @@ lint: lint_SRCS %.so: %.o $(BUILD.SO) $< +%.ln: ../common/%.c + $(LINT.c) $(LINTFLAGS) -c $< + # # Message catalog # -$(POFILES): ../common/$(SRCS) +%.po: ../common/%.c $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) \ - `($(GREP) -l gettext ../common/ses.c || echo /dev/null)` + `($(GREP) -l gettext $< || echo /dev/null)` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po @@ -94,6 +96,3 @@ $(IDENTPOFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ -lint_SRCS: ../common/$(SRCS:%.c=%.ln) - -msg: $(IDENTPOFILE) diff --git a/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile b/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile index 108044f881..1d428ed093 100644 --- a/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile +++ b/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile @@ -18,22 +18,19 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# -# # cmd/fwflash/plugins/transport/sparc # -SRCS= ses.c +SRCS= ses.c tavor.c hermon.c OBJECTS= $(SRCS:%.c=%.o) PLUGINS= $(SRCS:%.c=%.so) -POFILES= ses.po +POFILES= $(SRCS:%.c=%.po) IDENTPOFILE= fwflash_transport_identify_ses.po -LINTFILE= ses.ln +LINTFILE= $(SRCS:%.c=%.ln) SLINKS= sgen.so CLEANFILES= $(OBJECTS) @@ -60,15 +57,18 @@ $(PLUGINS) := FILEMODE = 0555 $(ROOTLIBFWFLASHPLUGINS)/$(SLINKS) : $(ROOTLIBFWFLASHPLUGINS)/ses.so @$(RM) $@ - $(SYMLINK) $(PLUGINS) $@ + $(SYMLINK) ses.so $@ -install: $(ROOTLIBFWFLASHPLUGINS) \ +install: all $(ROOTLIBFWFLASHPLUGINS) \ $(ROOTLIBFWFLASHPLUGINS)/ses.so \ + $(ROOTLIBFWFLASHPLUGINS)/tavor.so \ + $(ROOTLIBFWFLASHPLUGINS)/hermon.so \ $(ROOTLIBFWFLASHPLUGINS)/$(SLINKS) clobber clean: $(RM) $(CLEANFILES) $(CLOBBERFILES) +lint_SRCS: $(LINTFILE) lint: lint_SRCS %.o: ../common/%.c @@ -78,14 +78,17 @@ lint: lint_SRCS %.so: %.o $(BUILD.SO) $< +%.ln: ../common/%.c + $(LINT.c) $(LINTFLAGS) -c $< + # # Message catalog # -$(POFILES): ../common/$(SRCS) +%.po: ../common/%.c $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) \ - `($(GREP) -l gettext ../common/ses.c || echo /dev/null)` + `($(GREP) -l gettext $< || echo /dev/null)` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po @@ -93,6 +96,3 @@ $(IDENTPOFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ -lint_SRCS: ../common/$(SRCS:%.c=%.ln) - -msg: $(IDENTPOFILE) diff --git a/usr/src/cmd/fwflash/plugins/vendor/hermon-MELLANOX.c b/usr/src/cmd/fwflash/plugins/vendor/hermon-MELLANOX.c new file mode 100644 index 0000000000..a42a4c7954 --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/vendor/hermon-MELLANOX.c @@ -0,0 +1,383 @@ +/* + * 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. + */ + +/* + * ConnectX (hermon) firmware image verification plugin + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <fcntl.h> +#include <sys/condvar.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 vendor[] = "MELLANOX\0"; + +extern struct vrfyplugin *verifier; + + +/* required functions for this plugin */ +int vendorvrfy(struct devicelist *devicenode); + +/* helper functions */ +static uint16_t cnx_check_hwver_img(ib_cnx_encap_ident_t *handle); +static void cnx_flash_verify_flash_match_img(ib_cnx_encap_ident_t *handle); +static void cnx_flash_verify_flash_pn_img(ib_cnx_encap_ident_t *handle, + uchar_t *psid, int psid_size); +static uchar_t *cnx_flash_get_psid_img(ib_cnx_encap_ident_t *handle); +static void cnx_display_fwver(ib_cnx_encap_ident_t *handle); +static int cnx_check_guid_section(); + + +int +vendorvrfy(struct devicelist *devicenode) +{ + struct ib_cnx_encap_ident_s *handle; + uint16_t ver; + + logmsg(MSG_INFO, "hermon: vendorvrfy \n"); + + handle = (struct ib_cnx_encap_ident_s *)devicenode->ident->encap_ident; + + if (CNX_I_CHECK_HANDLE(handle)) { + logmsg(MSG_ERROR, gettext("hermon: Invalid Handle for " + "device %s! \n"), devicenode->access_devname); + return (FWFLASH_FAILURE); + } + + /* + * NOTE verifier->fwimage is where file is read to. + */ + if (cnx_is_magic_pattern_present(&verifier->fwimage[0], 1) != + FWFLASH_SUCCESS) { + logmsg(MSG_ERROR, gettext("%s firmware image verifier: " + "No magic pattern found in firmware file %s \n"), + verifier->vendor, verifier->imgfile); + return (FWFLASH_FAILURE); + } + + if (cnx_check_guid_section() == FWFLASH_FAILURE) { + logmsg(MSG_INFO, "%s firmware image verifier: " + "Firmware Image GUID section is invalid\n", + verifier->vendor); + } + + cnx_flash_verify_flash_match_img(handle); + + /* Check Hardware Rev */ + ver = cnx_check_hwver_img(handle); + if (ver != 0) { + logmsg(MSG_ERROR, gettext("hermon: Firmware mismatch: " + "ver(0x%X) hw_ver(0x%X)\n"), (ver >> 8), ver & 0xFF); + return (FWFLASH_FAILURE); + } + + if (handle->hwfw_match == 0) { + if (handle->pn_len != 0) { + /* HW VPD exist and a mismatch was found */ + logmsg(MSG_ERROR, gettext("hermon: Please verify that " + "the firmware image is intended for use with this " + "hardware\n")); + } else { + logmsg(MSG_ERROR, gettext("hermon: Unable to verify " + "firmware is appropriate for the hardware\n")); + } + return (FWFLASH_FAILURE); + } + logmsg(MSG_INFO, "%s firmware image verifier: HCA PSID (%s) " + "matches firmware image %s's PSID\n", verifier->vendor, + handle->info.mlx_psid, verifier->imgfile); + + cnx_display_fwver(handle); + + return (FWFLASH_SUCCESS); +} + +static uint16_t +cnx_check_hwver_img(ib_cnx_encap_ident_t *handle) +{ + uint8_t hwver; + uint8_t local_hwver; + + logmsg(MSG_INFO, "hermon: verify: cnx_check_hwver_img\n"); + if ((handle->state & FWFLASH_IB_STATE_IMAGE_PRI) == 0 && + (handle->state & FWFLASH_IB_STATE_IMAGE_SEC) == 0) { + logmsg(MSG_ERROR, gettext("hermon: Must read in image " + "first\n")); + return (1); + } + + /* Read Flash HW Version */ + hwver = (uint8_t)handle->hwrev; + local_hwver = (ntohl(verifier->fwimage[CNX_HWVER_OFFSET / 4]) & + CNX_HWVER_MASK) >> 24; + + logmsg(MSG_INFO, "local_hwver: %x, hwver: %x\n", local_hwver, hwver); + + if ((hwver == 0xA0 || hwver == 0x00 || hwver == 0x20) && + (local_hwver == 0x00 || local_hwver == 0xA0 || + local_hwver == 0x20)) { + logmsg(MSG_INFO, ("A0 board found.\r\n")); + } else if (hwver == 0xA1 && local_hwver == 0xA1) { + logmsg(MSG_INFO, ("A1 board found.\r\n")); + } else if (hwver == 0xA2 && local_hwver == 0xA2) { + logmsg(MSG_INFO, ("A2 board found.\r\n")); + } else if (hwver == 0xA3 && local_hwver == 0xA3) { + logmsg(MSG_INFO, ("A3 board found.\r\n")); + } else { + return ((uint16_t)(local_hwver << 8) | hwver); + } + return (0); +} + +static void +cnx_display_fwver(ib_cnx_encap_ident_t *handle) +{ + logmsg(MSG_INFO, "hermon: verify: cnx_display_fwver\n"); + + (void) fprintf(stdout, gettext(" The current HCA firmware version " + "is : %d.%d.%04d\n"), + handle->hwfw_img_info.fw_rev.major, + handle->hwfw_img_info.fw_rev.minor, + handle->hwfw_img_info.fw_rev.subminor); + (void) fprintf(stdout, gettext(" Will be updated to HCA firmware " + "ver of : %d.%d.%04d\n"), + handle->file_img_info.fw_rev.major, + handle->file_img_info.fw_rev.minor, + handle->file_img_info.fw_rev.subminor); +} + +static uchar_t * +cnx_flash_get_psid_img(ib_cnx_encap_ident_t *handle) +{ + uint32_t ii_ptr_addr; + uint32_t ii_size; + + logmsg(MSG_INFO, "hermon: verify: cnx_flash_get_psid_img\n"); + + /* Get the image info pointer */ + ii_ptr_addr = ntohl(verifier->fwimage[CNX_IMG_INF_PTR_OFFSET / 4]); + ii_ptr_addr &= 0xffffff; /* Bits 23:0 - Image Info Data Pointer */ + + /* Get the image info size, a negative offset from the image info ptr */ + ii_size = + ntohl(verifier->fwimage[(ii_ptr_addr + CNX_IMG_INF_SZ_OFFSET) / 4]); + /* size is in dwords--convert it to bytes */ + ii_size *= 4; + + logmsg(MSG_INFO, "ImgInfo_ptr_addr: 0x%lx, ImgInfo_size: 0x%x\n", + ii_ptr_addr, ii_size); + + /* Parse the image info section */ + if (cnx_parse_img_info(&verifier->fwimage[ii_ptr_addr / 4], ii_size, + &handle->file_img_info, CNX_FILE_IMG) != FWFLASH_SUCCESS) { + logmsg(MSG_WARN, gettext("hermon: Failed to parse ImageInfo " + "section\n")); + return (NULL); + } + + return (handle->file_img_info.psid); +} + +static void +cnx_flash_verify_flash_pn_img(ib_cnx_encap_ident_t *handle, uchar_t *psid, + int psid_size) +{ + int i; + int no_match = 0; + + logmsg(MSG_INFO, "hermon: verify: cnx_flash_verify_flash_pn_img\n"); + /* verify fw matches the hardware */ + if (handle->hwfw_match == 1) { + /* already been verified */ + return; + } + + /* find the PSID from FW in the mlx table */ + for (i = 0; i < MLX_MAX_ID; i++) { + if (handle->hwfw_match == 1) { + /* + * Need this check here and the 'continue's below + * because there are some cards that have a + * 'new' part number but the same PSID value. + */ + break; + } + + /* match PSID */ + if (strncmp((const char *)psid, mlx_mdr[i].mlx_psid, + psid_size) == 0) { + logmsg(MSG_INFO, "Found Matching firmware image's " + "PSID (%s) entry in MDR Table\n", psid); + + logmsg(MSG_INFO, "Search for firmware image's part# " + "(%s), MDR/HW PN (%s) \n", + handle->info.mlx_pn, mlx_mdr[i].mlx_pn); + + /* match part numbers */ + if (strncmp(handle->info.mlx_pn, mlx_mdr[i].mlx_pn, + handle->pn_len) == 0) { + handle->hwfw_match = 1; + logmsg(MSG_INFO, "Match Found \n"); + continue; + } else { + handle->hwfw_match = 0; + no_match = i; + logmsg(MSG_INFO, "Match NOT Found \n"); + continue; + } + } + } + if (i == MLX_MAX_ID && no_match == 0) { + /* no match found */ + handle->hwfw_match = 0; + handle->pn_len = 0; + logmsg(MSG_WARN, gettext("hermon: No PSID match found\n")); + } else { + if (handle->hwfw_match == 0) { + logmsg(MSG_WARN, gettext("WARNING: Firmware " + "image is meant for %s but the hardware " + "is %s\n"), mlx_mdr[no_match].mlx_pn, + handle->info.mlx_pn); + } + } +} + +static void +cnx_flash_verify_flash_match_img(ib_cnx_encap_ident_t *handle) +{ + uchar_t *psid; + + logmsg(MSG_INFO, "hermon: verify: cnx_flash_verify_flash_match_img\n"); + /* get PSID of firmware file */ + psid = cnx_flash_get_psid_img(handle); + if (psid == NULL) { + handle->hwfw_match = 0; + handle->pn_len = 0; + return; + } + logmsg(MSG_INFO, "FW PSID (%s)\n", psid); + + /* + * Check the part number of the hardware against the part number + * of the firmware file. If the hardware information is not + * available, check the currently loaded firmware against the + * firmware file to be uploaded. + */ + if (handle->pn_len != 0) { + cnx_flash_verify_flash_pn_img(handle, psid, CNX_PSID_SZ); + } +} + + +static int +cnx_check_guid_section() +{ + struct mlx_cnx_xfi xfisect; + struct mlx_cnx_guid_sect guidsect; + uint32_t nguidptr_addr; + uint16_t calculated_crc; + + logmsg(MSG_INFO, "cnx_check_guid_section: \n"); + + bcopy(&verifier->fwimage[0], &xfisect, sizeof (struct mlx_cnx_xfi)); + logmsg(MSG_INFO, "FailSafeChunkSz: 0x%08x, ImageInfoPtr: 0x%08x\n", + MLXSWAPBITS32(xfisect.failsafechunkinfo), + MLXSWAPBITS32(xfisect.imageinfoptr) & CNX_XFI_IMGINFO_PTR_MASK); + logmsg(MSG_INFO, "FW Size: 0x%08x NGUIDPTR: 0x%08x\n", + MLXSWAPBITS32(xfisect.fwimagesz), MLXSWAPBITS32(xfisect.nguidptr)); + + nguidptr_addr = (MLXSWAPBITS32(xfisect.nguidptr) - 0x10) / 4; + bcopy(&verifier->fwimage[nguidptr_addr], &guidsect, + sizeof (struct mlx_cnx_guid_sect)); + + logmsg(MSG_INFO, "Node GUID : 0x%016llx \n", + MLXSWAPBITS64(guidsect.nodeguid)); + logmsg(MSG_INFO, "Port1 GUID: 0x%016llx \n", + MLXSWAPBITS64(guidsect.port1guid)); + logmsg(MSG_INFO, "Port2 GUID: 0x%016llx \n", + MLXSWAPBITS64(guidsect.port2guid)); + logmsg(MSG_INFO, "SysIm GUID: 0x%016llx \n", + MLXSWAPBITS64(guidsect.sysimguid)); + logmsg(MSG_INFO, "Port 1 MAC: 0x%016llx \n", + MLXSWAPBITS64(guidsect.port1_mac)); + logmsg(MSG_INFO, "Port 2 MAC: 0x%016llx \n", + MLXSWAPBITS64(guidsect.port2_mac)); + + calculated_crc = cnx_crc16((uint8_t *)&verifier->fwimage[nguidptr_addr], + CNX_GUID_CRC16_SIZE, CNX_FILE_IMG); + if (calculated_crc != ntohs(guidsect.guidcrc)) { + logmsg(MSG_WARN, gettext("hermon: calculated crc value 0x%x " + "differs from GUID section 0x%x\n"), calculated_crc, + ntohs(guidsect.guidcrc)); + } else { + logmsg(MSG_INFO, "hermon: calculated crc value 0x%x MATCHES " + "with GUID section 0x%x\n", calculated_crc, + ntohs(guidsect.guidcrc)); + } + + if ((MLXSWAPBITS64(guidsect.nodeguid) == MLX_DEFAULT_NODE_GUID) && + (MLXSWAPBITS64(guidsect.port1guid) == MLX_DEFAULT_P1_GUID) && + (MLXSWAPBITS64(guidsect.port2guid) == MLX_DEFAULT_P2_GUID) && + ((MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_SYSIMG_GUID) || + (MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_NODE_GUID)) || + ((((MLXSWAPBITS64(guidsect.nodeguid) & HIGHBITS64) >> 40) == + MLX_OUI) || + (((MLXSWAPBITS64(guidsect.port1guid) & HIGHBITS64) >> 40) == + MLX_OUI) || + (((MLXSWAPBITS64(guidsect.port2guid) & HIGHBITS64) >> 40) == + MLX_OUI) || + (((MLXSWAPBITS64(guidsect.sysimguid) & HIGHBITS64) >> 40) == + MLX_OUI)) || + ((((MLXSWAPBITS64(guidsect.nodeguid) & HIGHBITS64) >> 40) == + SUNW_OUI) || + (((MLXSWAPBITS64(guidsect.port1guid) & HIGHBITS64) >> 40) == + SUNW_OUI) || + (((MLXSWAPBITS64(guidsect.port2guid) & HIGHBITS64) >> 40) == + SUNW_OUI) || + (((MLXSWAPBITS64(guidsect.sysimguid) & HIGHBITS64) >> 40) == + SUNW_OUI))) { + logmsg(MSG_INFO, "%s firmware image verifier: GUID Prefix " + "is as expected\n", verifier->vendor); + return (FWFLASH_SUCCESS); + } else { + logmsg(MSG_INFO, "%s firmware image verifier: GUID prefix " + "is not as expected\n", verifier->vendor); + return (FWFLASH_FAILURE); + } +} diff --git a/usr/src/cmd/fwflash/plugins/vendor/tavor-MELLANOX.c b/usr/src/cmd/fwflash/plugins/vendor/tavor-MELLANOX.c new file mode 100644 index 0000000000..5b7f7c6fd7 --- /dev/null +++ b/usr/src/cmd/fwflash/plugins/vendor/tavor-MELLANOX.c @@ -0,0 +1,337 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Mellanox firmware image verification plugin + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <fcntl.h> +#include <sys/condvar.h> +#include <string.h> +#include <strings.h> + +#include <sys/byteorder.h> + +#include <libintl.h> /* for gettext(3c) */ + +#include <fwflash/fwflash.h> +#include "../hdrs/MELLANOX.h" +#include "../hdrs/tavor_ib.h" + + + +char vendor[] = "MELLANOX\0"; + +extern int errno; +extern struct vrfyplugin *verifier; +extern uint16_t crc16(uint8_t *image, uint32_t size); + + +/* required functions for this plugin */ +int vendorvrfy(struct devicelist *devicenode); + + +/* helper functions */ +static int check_guid_ptr(uint8_t *data); + + +int +vendorvrfy(struct devicelist *devicenode) +{ + struct ib_encap_ident *encap; + uint32_t sector_sz; + int *firmware; + uint32_t vp_fia, vs_fia; + uint32_t vp_imginfo, vs_imginfo; + struct mlx_xps *vps; + uint8_t *vfi; + int i = 0, a, b, c, d; + char temppsid[17]; + char rawpsid[16]; + + encap = (struct ib_encap_ident *)devicenode->ident->encap_ident; + + /* + * NOTE that since verifier->fwimage is an array of ints, + * we have to divide our actual desired number by 4 to get + * the right data. + */ + firmware = verifier->fwimage; + + /* sector_sz is stored as Log_2 of the real value */ + sector_sz = 1 << MLXSWAPBITS32(firmware[FLASH_IS_SECTOR_SIZE_OFFSET/4]); + + if (sector_sz != encap->sector_sz) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Invariant Sector is invalid\n"), + verifier->vendor); + /* this is fatal */ + return (FWFLASH_FAILURE); + } + + /* now verify primary pointer sector */ + if ((vps = calloc(1, sizeof (struct mlx_xps))) == NULL) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Unable to allocate memory for Primary Pointer " + "Sector verification\n")); + return (FWFLASH_FAILURE); + } + bcopy(&firmware[sector_sz / 4], vps, sizeof (struct mlx_xps)); + if ((MLXSWAPBITS32(vps->signature) != FLASH_PS_SIGNATURE) || + (vps->xpsresv3 != 0)) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Primary Pointer Sector is invalid\n"), + verifier->vendor); + } + vp_fia = MLXSWAPBITS32(vps->fia); + + /* + * A slight diversion - check the PSID in the last + * 16 bytes of the first 256bytes in the xPS sectors. + * This will give us our part number to match. If the + * part number in the image doesn't match the part number + * in the encap_ident info (and pn_len > 0) then we reject + * this image as being incompatible with the HCA. + * + * In this bit we're only checking the info.mlx_psid field + * of the primary image in the on-disk image. If that's + * invalid we reject the image. + */ + + if (encap->info.mlx_psid != NULL) { + bzero(temppsid, 17); + bcopy(vps->vsdpsid+0xd0, &rawpsid, 16); + +#if !defined(_LITTLE_ENDIAN) + for (i = 0; i < 16; i += 4) { + temppsid[i] = rawpsid[i+3]; + temppsid[i+1] = rawpsid[i+2]; + temppsid[i+2] = rawpsid[i+1]; + temppsid[i+3] = rawpsid[i]; + } + logmsg(MSG_INFO, + "tavor: have raw '%s', want munged '%s'\n", + rawpsid, temppsid); +#else + bcopy(vps->vsdpsid+0xd0, &temppsid, 16); +#endif + if (strncmp(encap->info.mlx_psid, temppsid, 16) != 0) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "firmware image file %s is not appropriate " + "for device\n" + "%s (PSID file %s vs PSID device %s)\n"), + verifier->vendor, verifier->imgfile, + encap->info.mlx_psid, + ((temppsid != NULL) ? temppsid : "(null)")); + + free(vps); + return (FWFLASH_FAILURE); + } else { + logmsg(MSG_INFO, + "%s firmware image verifier: HCA PSID (%s) " + "matches firmware image %s's PSID\n", + verifier->vendor, + encap->info.mlx_psid, + verifier->imgfile); + } + } + + + /* now verify secondary pointer sector */ + bzero(vps, sizeof (struct mlx_xps)); + + bcopy(&firmware[sector_sz / 2], vps, sizeof (struct mlx_xps)); + if ((MLXSWAPBITS32(vps->signature) != FLASH_PS_SIGNATURE) || + (vps->xpsresv3 != 0)) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Secondary Pointer Sector is invalid\n"), + verifier->vendor); + } + vs_fia = MLXSWAPBITS32(vps->fia); + + (void) free(vps); + + if ((vfi = calloc(1, sector_sz)) == NULL) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Unable to allocate space for Primary " + "Firmware Image verification\n"), + verifier->vendor); + return (FWFLASH_FAILURE); + } + bcopy(&firmware[vp_fia / 4], vfi, sector_sz); + bcopy(&vfi[XFI_IMGINFO_OFFSET], &i, 4); + vp_imginfo = MLXSWAPBITS32(i); + + /* for readability only */ + a = (vp_imginfo & 0xff000000) >> 24; + b = (vp_imginfo & 0x00ff0000) >> 16; + c = (vp_imginfo & 0x0000ff00) >> 8; + d = (vp_imginfo & 0x000000ff); + + /* + * It appears to be the case (empirically) that this particular + * check condition for ImageInfoPtr doesn't hold for A1 firmware + * images. So if the ((a+b+c+d)%0x100) fails, don't worry unless + * the contents of the GUID section do not match the Mellanox + * default GUIDs 2c9000100d05[0123]. The A2++ images also have + * these default GUIDS. + * + * Unfortunately we can't depend on the hwrev field of the image's + * Invariant Sector for another level of confirmation, since A2++ + * images seem to have that field set to 0xa1 as well as the A1 + * images. Annoying! + */ + + if ((((a+b+c+d) % 0x100) == 0) && + (vp_imginfo != 0x00000000)) { + logmsg(MSG_INFO, + "%s firmware image verifier: " + "Primary Firmware Image Info pointer is valid\n", + verifier->vendor); + } else { + + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Primary Firmware Image Info pointer is invalid " + "(0x%04x)\nChecking GUID section.....\n"), + verifier->vendor, vp_imginfo); + + if (check_guid_ptr(vfi) == FWFLASH_FAILURE) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Primary Firmware Image GUID section " + "is invalid\n"), + verifier->vendor); + i = 1; + } else { + logmsg(MSG_INFO, + "%s firmware image verifier: " + "Primary GUID section is ok\n", + verifier->vendor); + } + + } + + bzero(vfi, sector_sz); + bcopy(&firmware[vs_fia / 4], vfi, sector_sz); + + bcopy(&vfi[XFI_IMGINFO_OFFSET], &i, 4); + vs_imginfo = MLXSWAPBITS32(i); + + /* for readability only */ + a = (vs_imginfo & 0xff000000) >> 24; + b = (vs_imginfo & 0x00ff0000) >> 16; + c = (vs_imginfo & 0x0000ff00) >> 8; + d = (vs_imginfo & 0x000000ff); + + if ((((a+b+c+d) % 0x100) == 0) && + (vs_imginfo != 0x00000000)) { + logmsg(MSG_INFO, + "%s firmware image verifier: " + "Secondary Firmware Image Info pointer is valid\n", + verifier->vendor); + } else { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Secondary Firmware Image Info pointer is invalid " + "(0x%04x)\nChecking GUID section.....\n"), + verifier->vendor, vp_imginfo); + + if (check_guid_ptr(vfi) == FWFLASH_FAILURE) { + logmsg(MSG_ERROR, + gettext("%s firmware image verifier: " + "Secondary Firmware Image GUID section " + "is invalid\n"), + verifier->vendor); + i++; + } + } + + free(vfi); + + + return ((i == 2) ? (FWFLASH_FAILURE) : (FWFLASH_SUCCESS)); +} + + +/* + * Very simple function - we're given an array of bytes, + * we know that we need to read the value at offset FLASH_GUID_PTR + * and jump to that location to read 4x uint64_t of (hopefully) + * GUID data. If we can read that data, and it matches the default + * Mellanox GUIDs, then we return success. We need all 4 default + * GUIDs to match otherwise we return failure. + */ +static int +check_guid_ptr(uint8_t *data) +{ + struct mlx_xfi xfisect; + struct mlx_guid_sect guidsect; + + bcopy(data, &xfisect, sizeof (xfisect)); + bcopy(&data[MLXSWAPBITS32(xfisect.nguidptr) - 16], &guidsect, + GUIDSECTION_SZ); + + logmsg(MSG_INFO, "nodeguid: %0llx\n", + MLXSWAPBITS64(guidsect.nodeguid)); + logmsg(MSG_INFO, "port1guid: %0llx\n", + MLXSWAPBITS64(guidsect.port1guid)); + logmsg(MSG_INFO, "port2guid: %0llx\n", + MLXSWAPBITS64(guidsect.port2guid)); + logmsg(MSG_INFO, "sysimguid: %0llx\n", + MLXSWAPBITS64(guidsect.sysimguid)); + + if ((MLXSWAPBITS64(guidsect.nodeguid) == MLX_DEFAULT_NODE_GUID) && + (MLXSWAPBITS64(guidsect.port1guid) == MLX_DEFAULT_P1_GUID) && + (MLXSWAPBITS64(guidsect.port2guid) == MLX_DEFAULT_P2_GUID) && + ((MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_SYSIMG_GUID) || + (MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_NODE_GUID)) || + ((((MLXSWAPBITS64(guidsect.nodeguid) & HIGHBITS64) >> 40) + == MLX_OUI) || + (((MLXSWAPBITS64(guidsect.port1guid) & HIGHBITS64) >> 40) + == MLX_OUI) || + (((MLXSWAPBITS64(guidsect.port2guid) & HIGHBITS64) >> 40) + == MLX_OUI) || + (((MLXSWAPBITS64(guidsect.sysimguid) & HIGHBITS64) >> 40) + == MLX_OUI))) { + return (FWFLASH_SUCCESS); + } else { + return (FWFLASH_FAILURE); + } +} |