/* * 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. */ /* * 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 #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 *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("tavor 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, "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[0] : "(not supported)"), (tempdev->addresses[1] ? tempdev->addresses[1] : "(not supported)"), (tempdev->addresses[2] ? tempdev->addresses[2] : "(not supported)"), (tempdev->addresses[3] ? tempdev->addresses[3] : "(not supported)"), 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 %X\n" "\tPSID\t\t: %s\n"), thisdev->ident->revid, encap->info.mlx_pn, encap->hwrev, 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; bzero(&init_ioctl, sizeof (tavor_flash_init_ioctl_t)); bzero(&cfi, sizeof (cfi_t)); /* * 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; logmsg(MSG_INFO, "tavor_identify: init_ioctl: hwrev: %X, " "fwver: %d.%d.%04d\n", init_ioctl.tf_hwrev, init_ioctl.tf_fwrev.tfi_maj, init_ioctl.tf_fwrev.tfi_min, init_ioctl.tf_fwrev.tfi_sub); /* * 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->sector_sz = TAVOR_FLASH_SECTOR_SZ_DEFAULT; manuf->device_sz = TAVOR_FLASH_DEVICE_SZ_DEFAULT; logmsg(MSG_INFO, "tavor_identify: CMDSET is AMD, SectorSz " "are default \n"); } 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, "tavor_identify: SectorSz is from CFI Data\n"); } logmsg(MSG_INFO, "tavor_identify: sector_sz: 0x%08x dev_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; logmsg(MSG_INFO, "tavor_identify: pn_len %d hwpn %s \n", init_ioctl.tf_pn_len, (init_ioctl.tf_pn_len != 0) ? init_ioctl.tf_hwpn : "(null)"); 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.%03d", 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, gettext("tavor: invalid image number requested (%d)\n"), prisec); 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); }