/* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * ConnectX (hermon) firmware image verification plugin */ #include #include #include #include #include #include #include #include #include #include #include #include /* for gettext(3c) */ #include #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) { int resp; 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")); } logmsg(MSG_ERROR, gettext("Do you want to continue? (Y/N): ")); (void) fflush(stdin); resp = getchar(); if (resp != 'Y' && resp != 'y') { logmsg(MSG_ERROR, gettext("Not proceeding with " "flash operation of %s on %s \n"), verifier->imgfile, devicenode->drvname); return (FWFLASH_FAILURE); } } else { 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 if (hwver == 0xB0 && local_hwver == 0xB0) { logmsg(MSG_INFO, ("B0 board found.\r\n")); } else if (hwver != local_hwver) { 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.%03d\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.%03d\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); } } # # 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate { global: fw_devices { FLAGS = PARENT }; fw_pluginlist { FLAGS = PARENT }; fwflash_debug { FLAGS = PARENT }; rootnode { FLAGS = PARENT }; self { FLAGS = PARENT }; verifier { FLAGS = PARENT }; logmsg { FLAGS = PARENT }; vendorvrfy; vendor; local: *; }; /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Joyent, Inc. */ /* * This is a general firmware flash plugin that does basic verification for * devices backed by sd(4D). * * The sd(4D) target for firmware flashing uses the general SCSI WRITE BUFFER * options with various modes to instruct the drive to download and install * microcode (what SPC-3 calls firmware). To verify that something fits, we can * use the READ BUFFER command with mode 03h to indicate that we want to * buffer's descriptor. This gives us both the buffer's total size and the * required alignment for writes. * * Unfortunately, it's impossible to know for certain if that size is supposed * to be equivalent to the microcode's. While a READ BUFFER is supposed to * return the same data as with a WRITE BUFFER command, experimental evidence * has shown that this isn't always the case. Especially as the firmware buffer * usually leverages buffer zero, but has custom modes to access it. */ #include #include #include /* * The fwflash plugin interface is a bit odd for a modern committed interface * and requires us to refer to data objects in the parent explicitly to get * access to and set various information. It also doesn't allow us a means of * setting data for our transport layer. */ extern struct vrfyplugin *verifier; /* * Declare the name of our vendor. This is required by the fwflash * plugin interface. Note it must be a character array. Using a pointer may * confuse the framework and its use of dlsym. */ char vendor[] = "GENERIC"; int vendorvrfy(struct devicelist *dvp) { libscsi_hdl_t *hdl = NULL; libscsi_target_t *targ = NULL; libscsi_action_t *act = NULL; libscsi_errno_t serr; spc3_read_buffer_cdb_t *rb_cdb; uint8_t descbuf[4]; uint32_t size; int ret = FWFLASH_FAILURE; if ((hdl = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to initialize " "libscsi: %s\n"), verifier->vendor, libscsi_strerror(serr)); return (FWFLASH_FAILURE); } if ((targ = libscsi_open(hdl, NULL, dvp->access_devname)) == NULL) { logmsg(MSG_ERROR, gettext("%s: unable to open device %s\n"), verifier->vendor, dvp->access_devname); goto cleanup; } if ((act = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER, LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) { logmsg(MSG_ERROR, "%s: failed to alloc scsi action: %s\n", verifier->vendor, libscsi_errmsg(hdl)); goto cleanup; } rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(act); rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR; /* * Microcode upgrade usually only uses the first buffer ID which are * sequentially indexed from zero. Strictly speaking these are all * vendor defined, but so far most vendors we've seen use index zero * for this. */ rb_cdb->rbc_bufferid = 0; rb_cdb->rbc_allocation_len[0] = 0; rb_cdb->rbc_allocation_len[1] = 0; rb_cdb->rbc_allocation_len[2] = sizeof (descbuf); if (libscsi_exec(act, targ) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer " "descriptor read: %s\n"), verifier->vendor, libscsi_errmsg(hdl)); goto cleanup; } if (libscsi_action_get_status(act) != SAM4_STATUS_GOOD) { logmsg(MSG_ERROR, gettext("%s: SCSI READ BUFFER command to " "determine maximum image size failed\n"), verifier->vendor); goto cleanup; } if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 && descbuf[3] == 0) { logmsg(MSG_ERROR, gettext("%s: devices %s does not support " "firmware upgrade\n"), verifier->vendor, dvp->access_devname); goto cleanup; } size = (descbuf[1] << 16) | (descbuf[2] << 8) | descbuf[3]; logmsg(MSG_INFO, gettext("%s: checking maximum image size %u against " "actual image size: %u\n"), verifier->vendor, size, verifier->imgsize); if (size < verifier->imgsize) { logmsg(MSG_ERROR, gettext("%s: supplied firmware image %s " "exceeds maximum image size of %u\n"), verifier->vendor, verifier->imgfile, size); goto cleanup; } logmsg(MSG_INFO, gettext("%s: successfully validated images %s\n"), verifier->vendor, verifier->imgfile); verifier->flashbuf = 0; ret = FWFLASH_SUCCESS; cleanup: if (act != NULL) libscsi_action_free(act); if (targ != NULL) libscsi_close(hdl, targ); if (hdl != NULL) libscsi_fini(hdl); return (ret); } /* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Mellanox firmware image verification plugin */ #include #include #include #include #include #include #include #include #include #include #include #include /* for gettext(3c) */ #include #include "../hdrs/MELLANOX.h" #include "../hdrs/tavor_ib.h" char vendor[] = "MELLANOX\0"; extern int errno; extern struct vrfyplugin *verifier; /* 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]; int offset; 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; /* * The actual location of log2_sector_sz can be calculated * by adding 0x32 to the value that is written in the * log2_sector_sz_ptr field. The log2_sector_sz_ptr is located * at 0x16 byte offset in Invariant Sector. */ offset = FLASH_IS_SECTOR_SIZE_OFFSET + MLXSWAPBITS32(firmware[FLASH_IS_SECT_SIZE_PTR/4]); sector_sz = 1 << MLXSWAPBITS32(firmware[offset/4]); if (sector_sz != encap->sector_sz) { logmsg(MSG_ERROR, gettext("%s firmware image verifier: " "Invariant Sector is invalid\n"), verifier->vendor); logmsg(MSG_ERROR, gettext("Mis-match in sector size: " "device's 0x%X file 0x%X\n"), encap->sector_sz, sector_sz); logmsg(MSG_ERROR, gettext("Firmware image file is not " "appropriate for this device.\n")); /* 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"), verifier->vendor); 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. */ bzero(temppsid, 17); bcopy(vps->vsdpsid+0xd0, &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); logmsg(MSG_INFO, "tavor_vrfy: PSID file '%s' HCA's PSID '%s'\n", (temppsid != NULL) ? temppsid : "(null)", (encap->info.mlx_psid != NULL) ? encap->info.mlx_psid : "(null)"); if (encap->info.mlx_psid != NULL) { int resp; 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 " "%s (PSID file %s vs PSID device %s)\n"), verifier->vendor, verifier->imgfile, devicenode->drvname, ((temppsid != NULL) ? temppsid : "(null)"), encap->info.mlx_psid); logmsg(MSG_ERROR, gettext("Do you want to continue? (Y/N): ")); (void) fflush(stdin); resp = getchar(); if (resp != 'Y' && resp != 'y') { free(vps); logmsg(MSG_ERROR, gettext("Not proceeding with " "flash operation of %s on %s\n"), verifier->imgfile, devicenode->drvname); 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_INFO, 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_INFO, 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_INFO, 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_INFO, gettext("%s firmware image verifier: " "Secondary Firmware Image GUID section " "is invalid\n"), verifier->vendor); i++; } } free(vfi); if (i == 2) logmsg(MSG_WARN, gettext("%s firmware image verifier: " "FAILED\n"), verifier->vendor); 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); } }