# # 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/transport # include $(SRC)/Makefile.master # Hammerhead: amd64-only COMMON_SUBDIRS= $(MACH64) SUBDIRS= $(COMMON_SUBDIRS) all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg .KEEP_STATE: all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC cd $@; pwd; $(MAKE) $(TARGET) FRC: # # 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. # # Copyright 2020 Joyent, Inc. # Copyright 2020 Oxide Computer Company # include $(SRC)/lib/Makefile.lib SES_LIB= ses.so # Hammerhead: tavor/hermon IB transport plugins removed — IB drivers not in base OS # TAVOR_LIB= tavor.so # HERMON_LIB= hermon.so SD_LIB= sd.so UFM_LIB= ufm.so PLUGINS= $(SES_LIB) $(SD_LIB) $(UFM_LIB) OBJECTS= $(PLUGINS:%.so=%.o) DYNLIB= $(PLUGINS:%=%) POFILES= $(PLUGINS:%.so=%.po) SLINKS= sgen.so POFILE= fwflash_transport_identify_ses.po SRCDIR= ../common include $(SRC)/cmd/fwflash/Makefile.com CLEANFILES= $(PLUGINS) $(POFILES) $(POFILE) $(SLINKS) LIBS= $(DYNLIB) CFLAGS += $(C_PICFLAGS) ROOTLIBDIR= $(ROOTUSRLIBFWFLASHIDF) # Hammerhead: ROOTLIBS is empty when BUILD32 is disabled; redefine for 64-bit only ROOTLIBS= $(LIBS:%=$(ROOTLIBDIR)/%) LDLIBS += -ldevinfo MAPFILES= ../common/mapfile-vers FILEMODE= 0755 $(SES_LIB): PICS= pics/$(SES_LIB:%.so=%.o) $(SD_LIB): PICS= pics/$(SD_LIB:%.so=%.o) $(UFM_LIB): PICS= pics/$(UFM_LIB:%.so=%.o) $(SES_LIB): SONAME = $(SES_LIB) $(SD_LIB): SONAME = $(SD_LIB) $(UFM_LIB): SONAME = $(UFM_LIB) $(SD_LIB): MAPFILES += ../common/mapfile-vers-plus $(UFM_LIB): MAPFILES += ../common/mapfile-vers-plus $(SES_LIB): LDLIBS += -L$(ROOT)/usr/lib/scsi -lscsi -lses -lnvpair -lc $(SD_LIB): LDLIBS += -L$(ROOT)/usr/lib/scsi -lscsi -lumem -lc $(UFM_LIB): LDLIBS += -lpcidb -lnvpair -lc $(SES_LIB): DYNFLAGS += -R/usr/lib/scsi $(SD_LIB): DYNFLAGS += -R/usr/lib/scsi # Hammerhead: fwflash identify plugins get verifier/logmsg symbols from the # fwflash daemon at load time via dlopen. Suppress -zdefs for GNU ld. ZDEFS = # Size assertions are non-constant and not useful here ZGUIDANCE=-Wl,-zguidance=noasserts .KEEP_STATE: $(ROOTUSRLIBFWFLASHIDF)/$(SLINKS) : $(ROOTUSRLIBFWFLASHIDF)/$(SES_LIB) @$(RM) $@ $(SYMLINK) $(SES_LIB) $@ all: $(LIBS) install: all $(ROOTLIBS) \ $(ROOTUSRLIBFWFLASHIDF)/$(SLINKS) _msg: $(POFILE) include $(SRC)/lib/Makefile.targ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # Copyright 2025 Hammerhead Project # include ../Makefile.targ .KEEP_STATE: all: clean: clobber: clean install: all /* * 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. */ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include /* for gettext(3c) */ #include #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[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) { 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 %X (%s)\n"), encap->info.mlx_pn, encap->hwrev, encap->info.mlx_id); else fprintf(stdout, gettext("\tProduct\t\t : %s %X\n"), encap->info.mlx_pn, encap->hwrev); if (strlen(encap->info.mlx_psid)) fprintf(stdout, gettext("\tPSID\t\t : %s\n"), encap->info.mlx_psid); else if (strlen(thisdev->ident->pid)) fprintf(stdout, gettext("\t%s\n"), thisdev->ident->pid); } else { fprintf(stdout, gettext("\t%s\n"), thisdev->ident->pid); } fprintf(stdout, "\n\n"); return (cnx_close(thisdev)); } /* * Helper functions lurk beneath this point */ /* * Notes: * 1. flash read is done in 32 bit quantities, and the driver returns * data in host byteorder form. * 2. flash write is done in 8 bit quantities by the driver. * 3. data in the flash should be in network byteorder. * 4. data in image files is in network byteorder form. * 5. data in image structures in memory is kept in network byteorder. * 6. the functions in this file deal with data in host byteorder form. */ static int cnx_read_image(ib_cnx_encap_ident_t *handle) { hermon_flash_ioctl_t ioctl_info; uint32_t phys_addr; int ret, i; int image_size; int type; type = handle->state & (FWFLASH_IB_STATE_IMAGE_PRI | FWFLASH_IB_STATE_IMAGE_SEC); logmsg(MSG_INFO, "cnx_read_image: type %lx\n", type); if (type == 0) { logmsg(MSG_ERROR, gettext("cnx_read_image: Must read in " "image first\n")); return (FWFLASH_FAILURE); } image_size = handle->fw_sz; if (image_size <= 0) { logmsg(MSG_ERROR, gettext("cnx_read_image: Invalid image size " "0x%x for %s image\n"), image_size, (type == 0x1 ? "Primary" : "Secondary")); return (FWFLASH_FAILURE); } logmsg(MSG_INFO, "hermon: fw_size: 0x%x\n", image_size); handle->fw = (uint32_t *)calloc(1, image_size); if (handle->fw == NULL) { logmsg(MSG_ERROR, gettext("cnx_read_image: Unable to allocate " "memory for fw_img : (%s)\n"), strerror(errno)); return (FWFLASH_FAILURE); } ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; for (i = 0; i < image_size; i += 4) { phys_addr = cnx_cont2phys(handle->log2_chunk_sz, i, type); ioctl_info.af_addr = phys_addr; ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_ERROR, gettext("cnx_read_image: Failed to " "read sector %d\n"), i); free(handle->fw); return (FWFLASH_FAILURE); } handle->fw[i / 4] = htonl(ioctl_info.af_quadlet); } for (i = 0; i < image_size; i += 4) { logmsg(MSG_INFO, "cnx_read_image: addr[0x%x] = 0x%08x\n", i, ntohl(handle->fw[i / 4])); } return (FWFLASH_SUCCESS); } static int cnx_write_file(ib_cnx_encap_ident_t *handle, const char *filename) { FILE *fp; int fd; mode_t mode = S_IRUSR | S_IWUSR; int len; logmsg(MSG_INFO, "cnx_write_file\n"); errno = 0; if ((fd = open(filename, O_RDWR|O_CREAT|O_DSYNC, mode)) < 0) { logmsg(MSG_ERROR, gettext("hermon: Unable to open specified " "file (%s) for writing: %s\n"), filename, strerror(errno)); return (FWFLASH_FAILURE); } errno = 0; fp = fdopen(fd, "w"); if (fp == NULL) { (void) fprintf(stderr, gettext("hermon: Unknown filename %s : " "%s\n"), filename, strerror(errno)); return (FWFLASH_FAILURE); } len = ntohl(handle->fw[CNX_IMG_SIZE_OFFSET / 4]); logmsg(MSG_INFO, "cnx_write_file: Writing to file. Length 0x%x\n", len); if (fwrite(&handle->fw[0], len, 1, fp) == 0) { (void) fprintf(stderr, gettext("hermon: fwrite failed")); perror("fwrite"); (void) fclose(fp); return (FWFLASH_FAILURE); } (void) fclose(fp); return (FWFLASH_SUCCESS); } static int cnx_verify_image(ib_cnx_encap_ident_t *handle, int type) { uint32_t new_start_addr; logmsg(MSG_INFO, "hermon: cnx_verify_image\n"); new_start_addr = cnx_cont2phys(handle->log2_chunk_sz, 0, type); return (cnx_check_for_magic_pattern(handle, new_start_addr)); } static int cnx_set_guids(ib_cnx_encap_ident_t *handle, void *arg) { uint32_t addr; uint32_t *guids; logmsg(MSG_INFO, "hermon: cnx_set_guids\n"); guids = (uint32_t *)arg; addr = ntohl(verifier->fwimage[CNX_NGUIDPTR_OFFSET / 4]) / 4; logmsg(MSG_INFO, "cnx_set_guids: guid_start_addr: 0x%x\n", addr * 4); /* * guids are supplied by callers as 64 bit values in host byteorder. * Storage is in network byteorder. */ #ifdef _BIG_ENDIAN if (handle->state & FWFLASH_IB_STATE_GUIDN) { verifier->fwimage[addr] = guids[0]; verifier->fwimage[addr + 1] = guids[1]; } if (handle->state & FWFLASH_IB_STATE_GUID1) { verifier->fwimage[addr + 2] = guids[2]; verifier->fwimage[addr + 3] = guids[3]; } if (handle->state & FWFLASH_IB_STATE_GUID2) { verifier->fwimage[addr + 4] = guids[4]; verifier->fwimage[addr + 5] = guids[5]; } if (handle->state & FWFLASH_IB_STATE_GUIDS) { verifier->fwimage[addr + 6] = guids[6]; verifier->fwimage[addr + 7] = guids[7]; } #else if (handle->state & FWFLASH_IB_STATE_GUIDN) { verifier->fwimage[addr] = htonl(guids[1]); verifier->fwimage[addr + 1] = htonl(guids[0]); } if (handle->state & FWFLASH_IB_STATE_GUID1) { verifier->fwimage[addr + 2] = htonl(guids[3]); verifier->fwimage[addr + 3] = htonl(guids[2]); } if (handle->state & FWFLASH_IB_STATE_GUID2) { verifier->fwimage[addr + 4] = htonl(guids[5]); verifier->fwimage[addr + 5] = htonl(guids[4]); } if (handle->state & FWFLASH_IB_STATE_GUIDS) { verifier->fwimage[addr + 6] = htonl(guids[7]); verifier->fwimage[addr + 7] = htonl(guids[6]); } #endif cnx_local_set_guid_crc_img((addr * 4) - 0x10, CNX_GUID_CRC16_SIZE, CNX_GUID_CRC16_OFFSET); return (FWFLASH_SUCCESS); } /* * Notes: Burn the image * * 1. Erase the entire sector where the new image is to be burned. * 2. Burn the image WITHOUT the magic pattern. This marks the new image * as invalid during the burn process. If the current image (i.e * containing a magic pattern) on the Flash is stored on the even * chunks (PRIMARY), burn the new image to the odd chunks (SECONDARY), * or vice versa. * 3. Burn the magic pattern at the beginning of the new image on the Flash. * This will validate the new image. * 4. Set the BootAddress register to its new location. */ static int cnx_write_image(ib_cnx_encap_ident_t *handle, int type) { hermon_flash_ioctl_t ioctl_info; int sector_size; int size; int i; uint32_t new_start_addr; uint32_t log2_chunk_sz; uint8_t *fw; logmsg(MSG_INFO, "hermon: cnx_write_image\n"); if (type == 0) { logmsg(MSG_ERROR, gettext("cnx_write_image: Must inform us " " where to write.\n")); return (FWFLASH_FAILURE); } log2_chunk_sz = cnx_get_log2_chunk_size( ntohl(verifier->fwimage[CNX_CHUNK_SIZE_OFFSET / 4])); sector_size = handle->sector_sz; new_start_addr = ((type - 1) << handle->log2_chunk_sz); /* Read Image Size */ size = ntohl(verifier->fwimage[CNX_IMG_SIZE_OFFSET / 4]); logmsg(MSG_INFO, "cnx_write_image: fw image size: 0x%x\n", size); /* Sectors must be erased before they can be written to. */ ioctl_info.af_type = HERMON_FLASH_ERASE_SECTOR; for (i = 0; i < size; i += sector_size) { ioctl_info.af_sector_num = cnx_cont2phys(log2_chunk_sz, i, type) / sector_size; if (cnx_erase_sector_ioctl(handle, &ioctl_info) != 0) { logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to " "erase sector 0x%x\n"), ioctl_info.af_sector_num); return (FWFLASH_FAILURE); } } fw = (uint8_t *)verifier->fwimage; ioctl_info.af_type = HERMON_FLASH_WRITE_BYTE; /* Write the new image without the magic pattern */ for (i = 16; i < size; i++) { ioctl_info.af_byte = fw[i]; ioctl_info.af_addr = cnx_cont2phys(log2_chunk_sz, i, type); if (cnx_write_ioctl(handle, &ioctl_info) != 0) { logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to " "write byte 0x%x\n"), ioctl_info.af_byte); return (FWFLASH_FAILURE); } if (i && !(i % handle->sector_sz)) { (void) printf(" ."); (void) fflush((void *)NULL); } } /* Validate the new image -- Write the magic pattern. */ for (i = 0; i < 16; i++) { ioctl_info.af_byte = fw[i]; ioctl_info.af_addr = cnx_cont2phys(log2_chunk_sz, i, type); if (cnx_write_ioctl(handle, &ioctl_info) != 0) { logmsg(MSG_ERROR, gettext("cnx_write_image: Failed to " "write magic pattern byte 0x%x\n"), ioctl_info.af_byte); return (FWFLASH_FAILURE); } } /* Write new image start address to CR space */ errno = 0; ioctl_info.af_addr = new_start_addr; if (ioctl(handle->fd, HERMON_IOCTL_WRITE_BOOT_ADDR, &ioctl_info) != 0) { logmsg(MSG_WARN, gettext("cnx_write_image: Failed to " "update boot address register: %s\n"), strerror(errno)); } return (FWFLASH_SUCCESS); } /* * cnx_identify performs the following actions: * * allocates and assigns thisdev->vpr * * allocates space for the 4 GUIDs which each IB device must have * queries the hermon driver for this device's GUIDs * * determines the hardware vendor, so that thisdev->vpr->vid * can be set correctly */ static int cnx_identify(struct devicelist *thisdev) { int fd, ret, i; hermon_flash_init_ioctl_t init_ioctl; ib_cnx_encap_ident_t *manuf; cfi_t cfi; int hw_psid_found = 0; logmsg(MSG_INFO, "hermon: cnx_identify\n"); /* open the device */ /* hook thisdev->ident->encap_ident to ib_cnx_encap_ident_t */ /* check that all the bits are sane */ /* return success, if warranted */ errno = 0; if ((fd = open(thisdev->access_devname, O_RDONLY)) < 0) { logmsg(MSG_ERROR, gettext("hermon: Unable to open a %s-" "attached device node: %s: %s\n"), drivername, thisdev->access_devname, strerror(errno)); return (FWFLASH_FAILURE); } if ((manuf = calloc(1, sizeof (ib_cnx_encap_ident_t))) == NULL) { logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space " "for a %s-attached handle structure\n"), drivername); close(fd); return (FWFLASH_FAILURE); } manuf->magic = FWFLASH_IB_MAGIC_NUMBER; manuf->state = FWFLASH_IB_STATE_NONE; manuf->fd = fd; manuf->log2_chunk_sz = 0; thisdev->ident->encap_ident = manuf; /* * Inform driver that this command supports the Intel Extended * CFI command set. */ cfi.cfi_char[0x10] = 'M'; cfi.cfi_char[0x11] = 'X'; cfi.cfi_char[0x12] = '2'; init_ioctl.af_cfi_info[0x4] = ntohl(cfi.cfi_int[0x4]); errno = 0; ret = ioctl(fd, HERMON_IOCTL_FLASH_INIT, &init_ioctl); if (ret < 0) { logmsg(MSG_ERROR, gettext("hermon: HERMON_IOCTL_FLASH_INIT " "failed: %s\n"), strerror(errno)); close(fd); free(manuf); return (FWFLASH_FAILURE); } manuf->hwrev = init_ioctl.af_hwrev; logmsg(MSG_INFO, "hermon: init_ioctl: hwrev: %x, fwver: %d.%d.%04d, " "PN# Len %d\n", init_ioctl.af_hwrev, init_ioctl.af_fwrev.afi_maj, init_ioctl.af_fwrev.afi_min, init_ioctl.af_fwrev.afi_sub, init_ioctl.af_pn_len); /* * Determine whether the attached driver supports the Intel or * AMD Extended CFI command sets. If it doesn't support either, * then we're hosed, so error out. */ for (i = 0; i < HERMON_FLASH_CFI_SIZE_QUADLET; i++) { cfi.cfi_int[i] = ntohl(init_ioctl.af_cfi_info[i]); } manuf->cmd_set = cfi.cfi_char[0x13]; if (cfi.cfi_char[0x10] == 'Q' && cfi.cfi_char[0x11] == 'R' && cfi.cfi_char[0x12] == 'Y') { /* make sure the cmd set is SPI */ if (manuf->cmd_set != HERMON_FLASH_SPI_CMDSET) { logmsg(MSG_ERROR, gettext("hermon: Unsupported flash " "device command set\n")); goto identify_end; } /* set some defaults */ manuf->sector_sz = HERMON_FLASH_SECTOR_SZ_DEFAULT; manuf->device_sz = HERMON_FLASH_DEVICE_SZ_DEFAULT; } else if (manuf->cmd_set == HERMON_FLASH_SPI_CMDSET) { manuf->sector_sz = HERMON_FLASH_SPI_SECTOR_SIZE; manuf->device_sz = HERMON_FLASH_SPI_DEVICE_SIZE; } else { if (manuf->cmd_set != HERMON_FLASH_AMD_CMDSET && manuf->cmd_set != HERMON_FLASH_INTEL_CMDSET) { logmsg(MSG_ERROR, gettext("hermon: Unknown flash " "device command set %lx\n"), manuf->cmd_set); goto identify_end; } /* read from the CFI data */ manuf->sector_sz = ((cfi.cfi_char[0x30] << 8) | cfi.cfi_char[0x2F]) << 8; manuf->device_sz = 0x1 << cfi.cfi_char[0x27]; } logmsg(MSG_INFO, "hermon: sector_sz: 0x%08x device_sz: 0x%08x\n", manuf->sector_sz, manuf->device_sz); /* set firmware revision */ manuf->hwfw_img_info.fw_rev.major = init_ioctl.af_fwrev.afi_maj; manuf->hwfw_img_info.fw_rev.minor = init_ioctl.af_fwrev.afi_min; manuf->hwfw_img_info.fw_rev.subminor = init_ioctl.af_fwrev.afi_sub; if (((thisdev->ident->vid = calloc(1, MLX_VPR_VIDLEN + 1)) == NULL) || ((thisdev->ident->revid = calloc(1, MLX_VPR_REVLEN + 1)) == NULL)) { logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space " "for a VPR record.\n")); goto identify_end; } (void) strlcpy(thisdev->ident->vid, "MELLANOX", MLX_VPR_VIDLEN); /* * We actually want the hwrev field from the ioctl above. * Until we find out otherwise, add it onto the end of the * firmware version details. */ snprintf(thisdev->ident->revid, MLX_VPR_REVLEN, "%d.%d.%03d", manuf->hwfw_img_info.fw_rev.major, manuf->hwfw_img_info.fw_rev.minor, manuf->hwfw_img_info.fw_rev.subminor); if ((ret = cnx_get_guids(manuf)) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: No GUIDs found for " "device %s!\n"), thisdev->access_devname); } /* set hw part number, psid, and name in handle */ /* now walk the magic decoder ring table */ manuf->info.mlx_pn = NULL; manuf->info.mlx_psid = NULL; manuf->info.mlx_id = NULL; if (cnx_get_image_info(manuf) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: Failed to read Image Info " "for PSID\n")); hw_psid_found = 0; } else { hw_psid_found = 1; } if (init_ioctl.af_pn_len != 0) { /* part number length */ for (i = 0; i < init_ioctl.af_pn_len; i++) { if (init_ioctl.af_hwpn[i] == ' ') { manuf->pn_len = i; break; } } if (i == init_ioctl.af_pn_len) { manuf->pn_len = init_ioctl.af_pn_len; } } else { logmsg(MSG_INFO, "hermon: Failed to get Part# from hermon " "driver \n"); manuf->pn_len = 0; } if (manuf->pn_len != 0) { errno = 0; manuf->info.mlx_pn = calloc(1, manuf->pn_len); if (manuf->info.mlx_pn == NULL) { logmsg(MSG_ERROR, gettext("hermon: no space available " "for the HCA PN record (%s)\n"), strerror(errno)); goto identify_end; } (void) memcpy(manuf->info.mlx_pn, init_ioctl.af_hwpn, manuf->pn_len); manuf->info.mlx_pn[manuf->pn_len] = 0; logmsg(MSG_INFO, "hermon: HCA PN (%s) PN-Len %d\n", manuf->info.mlx_pn, manuf->pn_len); errno = 0; manuf->info.mlx_psid = calloc(1, MLX_PSID_SZ); if (manuf->info.mlx_psid == NULL) { logmsg(MSG_ERROR, gettext("hermon: PSID calloc " "failed :%s\n"), strerror(errno)); goto identify_end; } errno = 0; if ((manuf->info.mlx_id = calloc(1, MLX_STR_ID_SZ)) == NULL) { logmsg(MSG_ERROR, gettext("hermon: " "ID calloc failed (%s)\n"), strerror(errno)); goto identify_end; } /* Find part number, set the rest */ for (i = 0; i < MLX_MAX_ID; i++) { if (strncmp((const char *)init_ioctl.af_hwpn, mlx_mdr[i].mlx_pn, manuf->pn_len) == 0) { if (hw_psid_found) { logmsg(MSG_INFO, "HW-PSID: %s " "MLX_MDR[%d]: %s\n", manuf->hwfw_img_info.psid, i, mlx_mdr[i].mlx_psid); if (strncmp((const char *) manuf->hwfw_img_info.psid, mlx_mdr[i].mlx_psid, MLX_PSID_SZ) != 0) continue; } /* Set PSID */ (void) memcpy(manuf->info.mlx_psid, mlx_mdr[i].mlx_psid, MLX_PSID_SZ); manuf->info.mlx_psid[MLX_PSID_SZ - 1] = 0; logmsg(MSG_INFO, "hermon: HCA PSID (%s)\n", manuf->info.mlx_psid); (void) strlcpy(manuf->info.mlx_id, mlx_mdr[i].mlx_id, strlen(mlx_mdr[i].mlx_id) + 1); logmsg(MSG_INFO, "hermon: HCA Name (%s)\n", manuf->info.mlx_id); break; } } } if ((manuf->pn_len == 0) || (i == MLX_MAX_ID)) { logmsg(MSG_INFO, "hermon: No hardware part number " "information available for this HCA\n"); i = strlen("No hardware information available for this device"); thisdev->ident->pid = calloc(1, i + 2); sprintf(thisdev->ident->pid, "No additional hardware info " "available for this device"); } else { errno = 0; if ((thisdev->ident->pid = calloc(1, strlen(manuf->info.mlx_psid) + 1)) != NULL) { (void) strlcpy(thisdev->ident->pid, manuf->info.mlx_psid, strlen(manuf->info.mlx_psid) + 1); } else { logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space for a " "hardware identifier: %s\n"), strerror(errno)); goto identify_end; } } for (i = 0; i < 4; i++) { errno = 0; if ((thisdev->addresses[i] = calloc(1, (2 * sizeof (uint64_t)) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("hermon: Unable to allocate space for a " "human-readable HCA guid: %s\n"), strerror(errno)); goto identify_end; } (void) sprintf(thisdev->addresses[i], "%016llx", manuf->ibguids[i]); } /* * We do NOT close the fd here, since we can close it * at the end of the fw_readfw() or fw_writefw() functions * instead and not get the poor dear confused about whether * it's been inited already. */ return (FWFLASH_SUCCESS); /* cleanup */ identify_end: cnx_close(thisdev); return (FWFLASH_FAILURE); } static int cnx_get_guids(ib_cnx_encap_ident_t *handle) { int i, rv; logmsg(MSG_INFO, "cnx_get_guids\n"); /* make sure we've got our fallback position organised */ for (i = 0; i < 4; i++) { handle->ibguids[i] = 0x00000000; } rv = cnx_find_magic_n_chnk_sz(handle, FWFLASH_IB_STATE_IMAGE_PRI); if (rv != FWFLASH_SUCCESS) { logmsg(MSG_INFO, "hermon: Failed to get Primary magic number. " "Trying Secondary... \n"); rv = cnx_find_magic_n_chnk_sz(handle, FWFLASH_IB_STATE_IMAGE_SEC); if (rv != FWFLASH_SUCCESS) { logmsg(MSG_ERROR, gettext("hermon: Failed to get " "Secondary magic number.\n")); logmsg(MSG_ERROR, gettext("Warning: HCA Firmware corrupt.\n")); return (FWFLASH_FAILURE); } rv = cnx_read_guids(handle, FWFLASH_IB_STATE_IMAGE_SEC); if (rv != FWFLASH_SUCCESS) { logmsg(MSG_ERROR, gettext("hermon: Failed to read " "secondary guids.\n")); return (FWFLASH_FAILURE); } } else { rv = cnx_read_guids(handle, FWFLASH_IB_STATE_IMAGE_PRI); if (rv != FWFLASH_SUCCESS) { logmsg(MSG_ERROR, gettext("hermon: Failed to read " "primary guids.\n")); return (FWFLASH_FAILURE); } } for (i = 0; i < 4; i++) { logmsg(MSG_INFO, "hermon: ibguids[%d] 0x%016llx\n", i, handle->ibguids[i]); } for (i = 0; i < 2; i++) { logmsg(MSG_INFO, "hermon: ib_portmac[%d] 0x%016llx\n", i, handle->ib_mac[i]); } return (FWFLASH_SUCCESS); } static int cnx_close(struct devicelist *flashdev) { ib_cnx_encap_ident_t *handle; logmsg(MSG_INFO, "cnx_close\n"); handle = (ib_cnx_encap_ident_t *)flashdev->ident->encap_ident; if (CNX_I_CHECK_HANDLE(handle)) { logmsg(MSG_ERROR, gettext("hermon: Invalid Handle to close " "device %s! \n"), flashdev->access_devname); return (FWFLASH_FAILURE); } if (handle->fd > 0) { errno = 0; (void) ioctl(handle->fd, HERMON_IOCTL_FLASH_FINI); if (close(handle->fd) != 0) { logmsg(MSG_ERROR, gettext("hermon: Unable to properly " "close device %s! (%s)\n"), flashdev->access_devname, strerror(errno)); return (FWFLASH_FAILURE); } } if (handle != NULL) { if (handle->info.mlx_id != NULL) free(handle->info.mlx_id); if (handle->info.mlx_psid != NULL) free(handle->info.mlx_psid); if (handle->fw != NULL) free(handle->fw); free(handle); } if (flashdev->ident->vid != NULL) free(flashdev->ident->vid); if (flashdev->ident->revid != NULL) free(flashdev->ident->revid); return (FWFLASH_SUCCESS); } /* * Driver read/write ioctl calls. */ static int cnx_read_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info) { int ret; #ifdef CNX_DEBUG logmsg(MSG_INFO, "cnx_read_ioctl: fd %d af_type 0x%x af_addr 0x%x " "af_sector_num(0x%x)\n", hdl->fd, info->af_type, info->af_addr, info->af_sector_num); #endif errno = 0; ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_READ, info); if (ret != 0) { logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_READ failed " "(%s)\n"), strerror(errno)); } return (ret); } static int cnx_write_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info) { int ret; #ifdef CNX_DEBUG logmsg(MSG_INFO, "cnx_write_ioctl: fd(%d) af_type(0x%x) " "af_addr(0x%x) af_sector_num(0x%x) af_byte(0x%x)\n", hdl->fd, info->af_type, info->af_addr, info->af_sector_num, info->af_byte); #endif errno = 0; ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_WRITE, info); if (ret != 0) { logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_WRITE " "failed (%s)\n"), strerror(errno)); } return (ret); } static int cnx_erase_sector_ioctl(ib_cnx_encap_ident_t *hdl, hermon_flash_ioctl_t *info) { int ret; #ifdef CNX_DEBUG logmsg(MSG_INFO, "cnx_erase_sector_ioctl: fd(%d) af_type(0x%x) " "af_sector_num(0x%x)\n", hdl->fd, info->af_type, info->af_sector_num); #endif errno = 0; ret = ioctl(hdl->fd, HERMON_IOCTL_FLASH_ERASE, info); if (ret != 0) { logmsg(MSG_ERROR, gettext("HERMON_IOCTL_FLASH_ERASE " "failed (%s)\n"), strerror(errno)); } return (ret); } /* * cnx_crc16 - computes 16 bit crc of supplied buffer. * image should be in network byteorder * result is returned in host byteorder form */ uint16_t cnx_crc16(uint8_t *image, uint32_t size, int is_image) { const uint16_t poly = 0x100b; uint32_t crc = 0xFFFF; uint32_t word; uint32_t i, j; logmsg(MSG_INFO, "hermon: cnx_crc16\n"); for (i = 0; i < size / 4; i++) { word = (image[4 * i] << 24) | (image[4 * i + 1] << 16) | (image[4 * i + 2] << 8) | (image[4 * i + 3]); if (is_image == CNX_HW_IMG) word = MLXSWAPBITS32(word); for (j = 0; j < 32; j++) { if (crc & 0x8000) { crc = (((crc << 1) | (word >> 31)) ^ poly) & 0xFFFF; } else { crc = ((crc << 1) | (word >> 31)) & 0xFFFF; } word = (word << 1) & 0xFFFFFFFF; } } for (i = 0; i < 16; i++) { if (crc & 0x8000) { crc = ((crc << 1) ^ poly) & 0xFFFF; } else { crc = (crc << 1) & 0xFFFF; } } crc = crc ^ 0xFFFF; return (crc & 0xFFFF); } static void cnx_local_set_guid_crc_img(uint32_t offset, uint32_t guid_crc_size, uint32_t guid_crc_offset) { uint16_t crc; uint8_t *fw_p = (uint8_t *)&verifier->fwimage[0]; crc = htons(cnx_crc16((uint8_t *)&verifier->fwimage[offset / 4], guid_crc_size, CNX_FILE_IMG)); logmsg(MSG_INFO, "cnx_local_set_guid_crc_img: new guid_sect crc: %x\n", ntohs(crc)); (void) memcpy(&fw_p[offset + guid_crc_offset], &crc, 2); } /* * Address translation functions for ConnectX * Variable definitions: * - log2_chunk_size: log2 of a Flash chunk size * - cont_addr: a contiguous image address to be translated * - is_image_in_odd_chunk: When this bit is 1, it indicates the new image is * stored in odd chunks of the Flash. */ static uint32_t cnx_cont2phys(uint32_t log2_chunk_size, uint32_t cont_addr, int type) { uint32_t result; int is_image_in_odd_chunks; is_image_in_odd_chunks = type - 1; if (log2_chunk_size) { result = cont_addr & (0xffffffff >> (32 - log2_chunk_size)) | (is_image_in_odd_chunks << log2_chunk_size) | (cont_addr << 1) & (0xffffffff << (log2_chunk_size + 1)); } else { result = cont_addr; } return (result); } static int cnx_read_guids(ib_cnx_encap_ident_t *handle, int type) { #ifdef _LITTLE_ENDIAN uint32_t *ptr, tmp; #endif hermon_flash_ioctl_t ioctl_info; uint32_t *guids; uint32_t *ibmac; int ret, i; uint32_t nguidptr_addr; union { uint8_t bytes[4]; uint32_t dword; } crc16_u; uint32_t *guid_structure; uint16_t crc; logmsg(MSG_INFO, "cnx_read_guids\n"); errno = 0; guid_structure = (uint32_t *)calloc(1, CNX_GUID_CRC16_SIZE / 4 * sizeof (uint32_t)); if (guid_structure == NULL) { logmsg(MSG_WARN, gettext("hermon: Can't calloc guid_structure " ": (%s)\n"), strerror(errno)); return (FWFLASH_FAILURE); } ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz, CNX_NGUIDPTR_OFFSET, type); ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_WARN, gettext("hermon: Failed to read GUID Pointer " "Address\n")); goto out; } guids = (uint32_t *)&handle->ibguids[0]; ibmac = (uint32_t *)&handle->ib_mac[0]; nguidptr_addr = cnx_cont2phys(handle->log2_chunk_sz, ioctl_info.af_quadlet, type); logmsg(MSG_INFO, "NGUIDPTR: 0x%08x \n", nguidptr_addr); /* Read in the entire guid section in order to calculate the CRC */ ioctl_info.af_addr = nguidptr_addr - 0x10; ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) { ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_INFO, "Failed to read guid_structure " "(0x%x)\n", i); goto out; } if (i >= 4 && i < 12) { guids[i - 4] = ioctl_info.af_quadlet; } if (i >= 12 && i < 16) { ibmac[i - 12] = ioctl_info.af_quadlet; } guid_structure[i] = ioctl_info.af_quadlet; ioctl_info.af_addr += 4; } for (i = 0; i < CNX_GUID_CRC16_SIZE / 4; i++) { logmsg(MSG_INFO, "guid_structure[%x] = 0x%08x\n", i, guid_structure[i]); } /* * Check the CRC--make sure it computes. */ /* 0x12 subtracted: 0x2 for alignment, 0x10 to reach structure start */ ioctl_info.af_addr = nguidptr_addr + CNX_GUID_CRC16_OFFSET - 0x12; ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_WARN, gettext("hermon: Failed to read guid crc " "at 0x%x\n"), ioctl_info.af_addr); goto out; } crc16_u.dword = ioctl_info.af_quadlet; crc = cnx_crc16((uint8_t *)guid_structure, CNX_GUID_CRC16_SIZE, CNX_HW_IMG); if (crc != crc16_u.dword) { logmsg(MSG_WARN, gettext("hermon: calculated crc16: 0x%x " "differs from GUID section 0x%x\n"), crc, crc16_u.dword); } else { logmsg(MSG_INFO, "hermon: calculated crc16: 0x%x MATCHES with " "GUID section 0x%x\n", crc, crc16_u.dword); } #ifdef _LITTLE_ENDIAN /* * guids are read as pairs of 32 bit host byteorder values and treated * by callers as 64 bit values. So swap each pair of 32 bit values * to make them correct */ ptr = (uint32_t *)guids; for (ret = 0; ret < 8; ret += 2) { tmp = ptr[ret]; ptr[ret] = ptr[ret+1]; ptr[ret+1] = tmp; } ptr = (uint32_t *)&handle->ib_mac[0]; for (ret = 0; ret < 4; ret += 2) { tmp = ptr[ret]; ptr[ret] = ptr[ret+1]; ptr[ret+1] = tmp; } #endif ret = FWFLASH_SUCCESS; out: free(guid_structure); return (ret); } static int cnx_find_magic_n_chnk_sz(ib_cnx_encap_ident_t *handle, int type) { int i, found = 0; uint32_t addr; uint32_t boot_addresses[] = {0, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000}; logmsg(MSG_INFO, "cnx_find_magic_n_chnk_sz\n"); switch (type) { case FWFLASH_IB_STATE_IMAGE_PRI: addr = 0; if (cnx_check_for_magic_pattern(handle, addr) != FWFLASH_SUCCESS) { goto err; } break; case FWFLASH_IB_STATE_IMAGE_SEC: for (i = 1; i < 6; i++) { addr = boot_addresses[i]; if (cnx_check_for_magic_pattern(handle, addr) == FWFLASH_SUCCESS) { found = 1; break; } } if (!found) { goto err; } break; default: logmsg(MSG_INFO, "cnx_find_magic_pattern: unknown type\n"); goto err; } logmsg(MSG_INFO, "magic_pattern found at addr %x\n", addr); handle->img2_start_addr = addr; handle->log2_chunk_sz = cnx_get_log2_chunk_size_f_hdl(handle, type); if (handle->log2_chunk_sz == 0) { logmsg(MSG_INFO, "no chunk size found for type %x. " "Assuming non-failsafe burn\n", type); } handle->fw_sz = cnx_get_image_size_f_hdl(handle, type); if (handle->fw_sz == 0) { logmsg(MSG_INFO, "no fw size found for type %x. \n", type); } handle->state |= type; return (FWFLASH_SUCCESS); err: logmsg(MSG_INFO, "no magic_pattern found for type %x\n", type); return (FWFLASH_FAILURE); } static int cnx_check_for_magic_pattern(ib_cnx_encap_ident_t *handle, uint32_t addr) { int i; hermon_flash_ioctl_t ioctl_info; int magic_pattern_buf[4]; logmsg(MSG_INFO, "cnx_check_for_magic_pattern\n"); ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; for (i = 0; i < 4; i++) { ioctl_info.af_addr = addr + (i * sizeof (uint32_t)); if (cnx_read_ioctl(handle, &ioctl_info) != 0) { logmsg(MSG_INFO, "\nFailed to read magic pattern\n"); return (FWFLASH_FAILURE); } magic_pattern_buf[i] = ioctl_info.af_quadlet; } return (cnx_is_magic_pattern_present(magic_pattern_buf, CNX_HW_IMG)); } int cnx_is_magic_pattern_present(int *data, int is_image) { int i; int dword; logmsg(MSG_INFO, "cnx_is_magic_pattern_present\n"); for (i = 0; i < 4; i++) { if (is_image == CNX_FILE_IMG) dword = MLXSWAPBITS32(data[i]); else dword = data[i]; logmsg(MSG_INFO, "local_quadlet: %08x, magic pattern: %08x\n", dword, cnx_magic_pattern[i]); if (dword != cnx_magic_pattern[i]) { return (FWFLASH_FAILURE); } } return (FWFLASH_SUCCESS); } static uint32_t cnx_get_log2_chunk_size_f_hdl(ib_cnx_encap_ident_t *handle, int type) { hermon_flash_ioctl_t ioctl_info; int ret; logmsg(MSG_INFO, "cnx_get_log2_chunk_size_f_hdl\n"); /* If chunk size is already set, just return it. */ if (handle->log2_chunk_sz) { return (handle->log2_chunk_sz); } switch (type) { case FWFLASH_IB_STATE_IMAGE_PRI: ioctl_info.af_addr = CNX_CHUNK_SIZE_OFFSET; break; case FWFLASH_IB_STATE_IMAGE_SEC: ioctl_info.af_addr = handle->img2_start_addr + CNX_CHUNK_SIZE_OFFSET; break; default: logmsg(MSG_INFO, "cnx_get_log2_chunk_size_f_hdl: unknown type\n"); return (0); } ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_INFO, "\nFailed to read chunk size\n"); return (0); } return (cnx_get_log2_chunk_size(ioctl_info.af_quadlet)); } static uint32_t cnx_get_log2_chunk_size(uint32_t chunk_size_word) { uint8_t checksum; uint32_t log2_chunk_size; logmsg(MSG_INFO, "cnx_get_log2_chunk_size: chunk_size_word:" " 0x%x\n", chunk_size_word); checksum = (chunk_size_word & 0xff) + ((chunk_size_word >> 8) & 0xff) + ((chunk_size_word >> 16) & 0xff) + ((chunk_size_word >> 24) & 0xff); if (checksum != 0) { logmsg(MSG_INFO, "Corrupted chunk size checksum\n"); return (0); } if (chunk_size_word & 0x8) { log2_chunk_size = (chunk_size_word & 0x7) + 16; logmsg(MSG_INFO, "log2 chunk size: 0x%x\n", log2_chunk_size); return (log2_chunk_size); } else { return (0); } } static uint32_t cnx_get_image_size_f_hdl(ib_cnx_encap_ident_t *handle, int type) { hermon_flash_ioctl_t ioctl_info; int ret; logmsg(MSG_INFO, "cnx_get_image_size_f_hdl\n"); ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz, CNX_IMG_SIZE_OFFSET, type); ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; ret = cnx_read_ioctl(handle, &ioctl_info); if (ret != 0) { logmsg(MSG_INFO, "Failed to read image size\n"); return (0); } logmsg(MSG_INFO, "Image Size: 0x%x\n", ioctl_info.af_quadlet); return (ioctl_info.af_quadlet); } static int cnx_get_image_info(ib_cnx_encap_ident_t *handle) { uint32_t ii_ptr_addr; uint32_t ii_size; int *buf; int i, type; hermon_flash_ioctl_t ioctl_info; logmsg(MSG_INFO, "cnx_get_image_info: state %x\n", handle->state); type = handle->state & (FWFLASH_IB_STATE_IMAGE_PRI | FWFLASH_IB_STATE_IMAGE_SEC); /* Get the image info pointer */ ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz, CNX_IMG_INF_PTR_OFFSET, type); ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: Failed to read image info " "Address\n")); return (FWFLASH_FAILURE); } ii_ptr_addr = ioctl_info.af_quadlet & 0xffffff; /* Get the image info size, a negative offset from the image info ptr */ ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz, ii_ptr_addr + CNX_IMG_INF_SZ_OFFSET, type); ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: Failed to read image info " "size\n")); return (FWFLASH_FAILURE); } logmsg(MSG_INFO, "hermon: ImageInfo Sz: 0x%x\n", ioctl_info.af_quadlet); ii_size = ioctl_info.af_quadlet; /* size is in dwords--convert it to bytes */ ii_size *= 4; logmsg(MSG_INFO, "hermon: ii_ptr_addr: 0x%x ii_size: 0x%x\n", ii_ptr_addr, ii_size); buf = (int *)calloc(1, ii_size); ioctl_info.af_addr = cnx_cont2phys(handle->log2_chunk_sz, ii_ptr_addr, type); ioctl_info.af_type = HERMON_FLASH_READ_QUADLET; for (i = 0; i < ii_size/4; i++) { if (cnx_read_ioctl(handle, &ioctl_info) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: Failed to read " "image info (0x%x)\n"), i); free(buf); return (FWFLASH_FAILURE); } buf[i] = ioctl_info.af_quadlet; ioctl_info.af_addr += 4; } /* Parse the image info section */ if (cnx_parse_img_info(buf, ii_size, &handle->hwfw_img_info, CNX_HW_IMG) != FWFLASH_SUCCESS) { logmsg(MSG_WARN, gettext("hermon: Failed to parse Image Info " "section\n")); free(buf); return (FWFLASH_FAILURE); } free(buf); return (FWFLASH_SUCCESS); } int cnx_parse_img_info(int *buf, uint32_t byte_size, cnx_img_info_t *img_info, int is_image) { uint32_t *p; uint32_t offs = 0; uint32_t tag_num = 0; int end_found = 0; uint32_t tag_size, tag_id; uint32_t tmp; const char *str; int i; p = (uint32_t *)buf; logmsg(MSG_INFO, "hermon: cnx_parse_img_info\n"); while (!end_found && (offs < byte_size)) { if (is_image == CNX_FILE_IMG) { tag_size = ntohl(*p) & 0xffffff; tag_id = ntohl(*p) >> 24; tmp = ntohl(*(p + 1)); } else { tag_size = ((*p) & 0xffffff); tag_id = ((*p) >> 24); tmp = (*(p + 1)); } logmsg(MSG_INFO, "tag_id: %d tag_size: %d\n", tag_id, tag_size); if ((offs + tag_size) > byte_size) { logmsg(MSG_WARN, gettext("hermon: Image Info section " "corrupted: Tag# %d - tag_id %d, size %d exceeds " "info section size (%d bytes)"), tag_num, tag_id, tag_size, byte_size); return (FWFLASH_FAILURE); } switch (tag_id) { case CNX_FW_VER: if (tag_size != CNX_FW_VER_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_FW_VER_SZ); } tmp = (tmp & CNX_MASK_FW_VER_MAJ) >> 16; img_info->fw_rev.major = tmp; if (is_image == CNX_FILE_IMG) tmp = ntohl(*(p + 2)); else tmp = (*(p + 2)); img_info->fw_rev.minor = (tmp & CNX_MASK_FW_VER_MIN)>> 16; img_info->fw_rev.subminor = tmp & CNX_MASK_FW_VER_SUBMIN; logmsg(MSG_INFO, "FW_VER: %d.%d.%03d\n", img_info->fw_rev.major, img_info->fw_rev.minor, img_info->fw_rev.subminor); break; case CNX_FW_BUILD_TIME: if (tag_size != CNX_FW_BUILD_TIME_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_FW_BUILD_TIME_SZ); } img_info->fw_buildtime.hour = (tmp & CNX_MASK_FW_BUILD_HOUR) >> 16; img_info->fw_buildtime.minute = (tmp & CNX_MASK_FW_BUILD_MIN) >> 8; img_info->fw_buildtime.second = (tmp & CNX_MASK_FW_BUILD_SEC); if (is_image == CNX_FILE_IMG) tmp = ntohl(*(p + 2)); else tmp = (*(p + 2)); img_info->fw_buildtime.year = (tmp & CNX_MASK_FW_BUILD_YEAR) >> 16; img_info->fw_buildtime.month = (tmp & CNX_MASK_FW_BUILD_MON) >> 8; img_info->fw_buildtime.day = (tmp & CNX_MASK_FW_BUILD_DAY); logmsg(MSG_INFO, "Build TIME: %d:%d:%d %d:%d:%d\n", img_info->fw_buildtime.year, img_info->fw_buildtime.month, img_info->fw_buildtime.day, img_info->fw_buildtime.hour, img_info->fw_buildtime.minute, img_info->fw_buildtime.second); break; case CNX_DEV_TYPE: if (tag_size != CNX_DEV_TYPE_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_DEV_TYPE_SZ); } img_info->dev_id = tmp & CNX_MASK_DEV_TYPE_ID; logmsg(MSG_INFO, "DEV_TYPE: %d\n", img_info->dev_id); break; case CNX_VSD_VENDOR_ID: if (tag_size != CNX_VSD_VENDOR_ID_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_VSD_VENDOR_ID_SZ); } img_info->vsd_vendor_id = tmp & CNX_MASK_VSD_VENDORID; logmsg(MSG_INFO, "VSD Vendor ID: 0x%lX\n", img_info->vsd_vendor_id); break; case CNX_PSID: if (tag_size != CNX_PSID_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_PSID_SZ); } str = (const char *)p; str += 4; for (i = 0; i < CNX_PSID_SZ; i++) img_info->psid[i] = str[i]; #ifdef _LITTLE_ENDIAN if (is_image == CNX_HW_IMG) { for (i = 0; i < CNX_PSID_SZ; i += 4) { img_info->psid[i+3] = str[i]; img_info->psid[i+2] = str[i+1]; img_info->psid[i+1] = str[i+2]; img_info->psid[i] = str[i+3]; } } #endif logmsg(MSG_INFO, "PSID: %s\n", img_info->psid); break; case CNX_VSD: if (tag_size != CNX_VSD_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_VSD_SZ); } str = (const char *)p; str += 4; for (i = 0; i < CNX_VSD_SZ; i++) img_info->vsd[i] = str[i]; #ifdef _LITTLE_ENDIAN if (is_image == CNX_HW_IMG) { for (i = 0; i < CNX_VSD_SZ; i += 4) { img_info->vsd[i+3] = str[i]; img_info->vsd[i+2] = str[i+1]; img_info->vsd[i+1] = str[i+2]; img_info->vsd[i] = str[i+3]; } } #endif logmsg(MSG_INFO, "VSD: %s\n", img_info->vsd); break; case CNX_END_TAG: if (tag_size != CNX_END_TAG_SZ) { logmsg(MSG_INFO, "ERROR: tag_id: %d tag_size: " "%d expected sz %d\n", tag_id, tag_size, CNX_END_TAG_SZ); } end_found = 1; break; default: if (tag_id > CNX_END_TAG) { logmsg(MSG_WARN, gettext("Invalid img_info " "tag ID %d of size %d\n"), tag_id, tag_size); } break; } p += (tag_size / 4) + 1; offs += tag_size + 4; tag_num++; } if (offs != byte_size) { logmsg(MSG_WARN, gettext("hermon: Corrupt Image Info section " "in firmware image\n")); if (end_found) { logmsg(MSG_WARN, gettext("Info section corrupted: " "Section data size is %x bytes, but end tag found " "after %x bytes.\n"), byte_size, offs); } else { logmsg(MSG_WARN, gettext("Info section corrupted: " "Section data size is %x bytes, but end tag not " "found at section end.\n"), byte_size); } return (FWFLASH_FAILURE); } return (FWFLASH_SUCCESS); } # # 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 }; drivername; fw_devinfo; fw_identify; fw_readfw; fw_writefw; local: *; }; # # 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: cnx_crc16; cnx_is_magic_pattern_present; cnx_parse_img_info; }; # # 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_cleanup; plugin_version; }; /* * 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. * * Copyright 2016 Joyent, Inc. */ /* * sd / ssd (SCSI Direct-attached Device) specific functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for gettext(3c) */ #include #include #include typedef struct sam4_statdesc { int sam_status; char *sam_message; } sam4_statdesc_t; static sam4_statdesc_t sam4_status[] = { { SAM4_STATUS_GOOD, "Status: GOOD (success)" }, { SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" }, { SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" }, { SAM4_STATUS_BUSY, "Status: Device is BUSY" }, { SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" }, { SAM4_STATUS_TASK_SET_FULL, "Status: TASK SET FULL (insufficient resources in command queue" }, { SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" } }; #define NSAM4_STATUS (sizeof (sam4_status) / sizeof (sam4_status[0])) #define FW_SD_FREE_DEVPATH(devpath) { \ di_devfs_path_free((devpath)); \ } #define FW_SD_FREE_DEVICELIST(thisdev, devpath) { \ free((thisdev)); \ FW_SD_FREE_DEVPATH((devpath)) \ } #define FW_SD_FREE_DRV_NAME(thisdev, devpath) { \ free((thisdev)->drvname); \ FW_SD_FREE_DEVICELIST((thisdev), (devpath)) \ } #define FW_SD_FREE_CLS_NAME(thisdev, devpath) { \ free((thisdev)->classname); \ FW_SD_FREE_DRV_NAME((thisdev), (devpath)) \ } #define FW_SD_FREE_ACC_NAME(thisdev, devpath) { \ free((thisdev)->access_devname); \ FW_SD_FREE_CLS_NAME(thisdev, devpath) \ } #define FW_SD_FREE_ADDR(thisdev, devpath) { \ free((thisdev)->addresses[0]); \ FW_SD_FREE_ACC_NAME(thisdev, devpath) \ } #define FW_SD_FREE_IDENT(thisdev, devpath) { \ free((thisdev)->ident); \ FW_SD_FREE_ADDR((thisdev), (devpath)) \ } #define FW_SD_FREE_IDENT_VID(thisdev, devpath) { \ free((thisdev)->ident->vid); \ FW_SD_FREE_IDENT((thisdev), (devpath)) \ } #define FW_SD_FREE_IDENT_PID(thisdev, devpath) { \ free((thisdev)->ident->pid); \ FW_SD_FREE_IDENT_VID((thisdev), (devpath)) \ } #define FW_SD_FREE_IDENT_ALL(thisdev, devpath) { \ free((thisdev)->ident->revid); \ FW_SD_FREE_IDENT_PID((thisdev), (devpath)) \ } /* * This is our default partial write size when we encounter a situation where we * need to upgrade disks whose firmware image cannot be done in a single write. * While in theory we should just use the maximum transfer size and make sure * it's aligned, that's proven to be problematic for some Seagate disks. Hence * we just make sure that if partial writes are required that this value fits in * the required alignment and in the actual maximum transfer size. */ #define FW_SD_PARTIAL_WRITE_SIZE (64 * 1024) /* * Declarations required for fwflash */ char drivername[] = "sd\0"; int plugin_version = FWPLUGIN_VERSION_2; /* * Data provided by fwflash */ extern di_node_t rootnode; extern struct fw_plugin *self; extern struct vrfyplugin *verifier; extern int fwflash_debug; static char *sdfw_devprefix = "/devices"; static char *sdfw_find_link(di_node_t bnode, char *acc_devname); static int sdfw_link_cb(di_devlink_t devlink, void *arg); static int sdfw_idtfy_custmz(struct devicelist *device, char *sp); /* * We don't currently support reading firmware from a disk. If we do eventually * support it, we would use the scsi READ BUFFER command to do so. */ int fw_readfw(struct devicelist *flashdev, char *filename) { logmsg(MSG_INFO, "%s: not writing firmware for device %s to file %s\n", flashdev->drvname, flashdev->access_devname, filename); logmsg(MSG_ERROR, gettext("\n\nReading of firmware images from %s-attached " "devices is not supported\n\n"), flashdev->drvname); return (FWFLASH_SUCCESS); } static int sdfw_read_descriptor(struct devicelist *flashdev, libscsi_hdl_t *hdl, libscsi_target_t *targ, uint8_t *align) { spc3_read_buffer_cdb_t *rb_cdb; size_t nwritten; libscsi_action_t *action = NULL; uint8_t descbuf[4]; sam4_status_t samstatus; VERIFY3P(hdl, !=, NULL); VERIFY3P(targ, !=, NULL); VERIFY3P(align, !=, NULL); if ((action = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER, LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: " "%s\n"), flashdev->drvname, libscsi_errmsg(hdl)); return (FWFLASH_FAILURE); } rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(action); rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR; /* * Microcode upgrade usually only uses the first buffer ID which is * 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(action, targ) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer " "data read: %s\n"), flashdev->drvname, libscsi_errmsg(hdl)); libscsi_action_free(action); return (FWFLASH_FAILURE); } if ((samstatus = libscsi_action_get_status(action)) != SAM4_STATUS_GOOD) { int i; for (i = 0; i < NSAM4_STATUS; i++) { if (samstatus == sam4_status[i].sam_status) { logmsg(MSG_ERROR, gettext("%s: SCSI buffer " "data read failed: %s\n"), flashdev->drvname, sam4_status[i].sam_message); libscsi_action_free(action); return (FWFLASH_FAILURE); } } logmsg(MSG_ERROR, gettext("%s: SCSI buffer data read failed: " "unknown error: %d\n"), flashdev->drvname, samstatus); libscsi_action_free(action); return (FWFLASH_FAILURE); } if (libscsi_action_get_buffer(action, NULL, NULL, &nwritten) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to get actual data " "size: %s\n"), flashdev->drvname, libscsi_errmsg(hdl)); libscsi_action_free(action); return (FWFLASH_FAILURE); } libscsi_action_free(action); if (nwritten != sizeof (descbuf)) { logmsg(MSG_ERROR, gettext("%s: received a short read from the " "SCSI READ BUFFER command, expected %u bytes, read %zu\n"), flashdev->drvname, sizeof (descbuf), nwritten); return (FWFLASH_FAILURE); } 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, flashdev->access_devname); return (FWFLASH_FAILURE); } *align = descbuf[0]; return (FWFLASH_SUCCESS); } static int sdfw_write(struct devicelist *flashdev, libscsi_hdl_t *handle, libscsi_target_t *target, size_t len, size_t off, void *buf) { sam4_status_t samstatus; libscsi_action_t *action = NULL; spc3_write_buffer_cdb_t *wb_cdb; logmsg(MSG_INFO, "%s: writing %u bytes of image %s at offset %zu from " "address %p\n", flashdev->drvname, len, verifier->imgfile, off, buf); logmsg(MSG_INFO, "%s: writing to buffer id %u\n", flashdev->drvname, verifier->flashbuf); VERIFY3P(flashdev, !=, NULL); VERIFY3P(handle, !=, NULL); VERIFY3P(target, !=, NULL); VERIFY3P(buf, !=, NULL); VERIFY3U(len, >, 0); VERIFY3U(off + len, <=, verifier->imgsize); action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER, LIBSCSI_AF_WRITE | LIBSCSI_AF_RQSENSE | LIBSCSI_AF_ISOLATE, buf, len); if (action == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: " "%s\n"), flashdev->drvname, libscsi_errmsg(handle)); goto err; } wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action); wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_OFFS_SAVE; wb_cdb->wbc_buffer_offset[0] = (off >> 16) & 0xff; wb_cdb->wbc_buffer_offset[1] = (off >> 8) & 0xff; wb_cdb->wbc_buffer_offset[2] = off & 0xff; wb_cdb->wbc_bufferid = verifier->flashbuf; wb_cdb->wbc_parameter_list_len[0] = (len >> 16) & 0xff; wb_cdb->wbc_parameter_list_len[1] = (len >> 8) & 0xff; wb_cdb->wbc_parameter_list_len[2] = len & 0xff; logmsg(MSG_INFO, "%s: spc3_write_buffer_cdb_t opcode: %u\n", flashdev->drvname, wb_cdb->wbc_opcode); if (libscsi_exec(action, target) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI WRITE " "BUFFER: %s\n"), flashdev->drvname, libscsi_errmsg(handle)); goto err; } if ((samstatus = libscsi_action_get_status(action)) == SAM4_STATUS_CHECK_CONDITION) { uint64_t asc = 0, ascq = 0, key = 0; const char *code, *keystr; if (libscsi_action_parse_sense(action, &key, &asc, &ascq, NULL) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to write " "firmware. Received CHECK_CONDITION that cannot be " "parsed.\n"), flashdev->drvname); goto err; } code = libscsi_sense_code_name(asc, ascq); keystr = libscsi_sense_key_name(key); logmsg(MSG_ERROR, gettext("%s: failed to write firmware: " "received sense key %" PRIu64 " (%s) additional sense code " "0x%" PRIx64 "/0x%" PRIx64 " (%s)\n"), flashdev->drvname, key, keystr != NULL ? keystr : "", asc, ascq, code != NULL ? code : ""); goto err; } else if (samstatus != SAM4_STATUS_GOOD) { int i; logmsg(MSG_ERROR, gettext("%s: SCSI buffer data write failed:"), flashdev->drvname); for (i = 0; i < NSAM4_STATUS; i++) { if (samstatus == sam4_status[i].sam_status) { logmsg(MSG_ERROR, gettext("%s\n"), sam4_status[i].sam_message); goto err; } } logmsg(MSG_ERROR, gettext("unknown error: %d\n"), samstatus); goto err; } else { logmsg(MSG_INFO, "%s: received STATUS GOOD\n", flashdev->drvname); } libscsi_action_free(action); return (FWFLASH_SUCCESS); err: if (action != NULL) libscsi_action_free(action); return (FWFLASH_FAILURE); } int fw_writefw(struct devicelist *flashdev) { libscsi_hdl_t *handle; libscsi_target_t *target; libscsi_errno_t serr; size_t maxxfer, nwrite; uint8_t align; int ret = FWFLASH_FAILURE; if ((verifier == NULL) || (verifier->imgsize == 0) || (verifier->fwimage == NULL)) { /* should _NOT_ happen */ logmsg(MSG_ERROR, gettext("%s: Firmware image has not been verified\n"), flashdev->drvname); return (FWFLASH_FAILURE); } if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to initialize libscsi\n"), flashdev->drvname); return (FWFLASH_FAILURE); } if ((target = libscsi_open(handle, NULL, flashdev->access_devname)) == NULL) { logmsg(MSG_ERROR, gettext("%s: unable to open device %s\n"), flashdev->drvname, flashdev->access_devname); libscsi_fini(handle); return (FWFLASH_FAILURE); } if (libscsi_max_transfer(target, &maxxfer) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to determine device " "maximum transfer size: %s\n"), flashdev->drvname, libscsi_errmsg(handle)); goto err; } if (sdfw_read_descriptor(flashdev, handle, target, &align) != FWFLASH_SUCCESS) { goto err; } /* * If the maximum transfer size is less than the maximum image size then * we have to do some additional work. We need to read the descriptor * via a READ BUFFER command and make sure that we support the required * offset alignment. Note that an alignment of 0xff indicates that the * device does not support partial writes and must receive the firmware * in a single WRITE BUFFER. Otherwise a value in align represents a * required offset alignment of 2^off. From there, we make sure that * this works for our partial write size and that our partial write size * fits in the maximum transfer size. */ if (maxxfer < verifier->imgsize) { logmsg(MSG_INFO, "%s: Maximum transfer is %zu, required " "alignment is 2^%u\n", flashdev->drvname, maxxfer, align); if (FW_SD_PARTIAL_WRITE_SIZE > maxxfer) { logmsg(MSG_ERROR, gettext("%s: cannot write firmware " "image: HBA enforces a maximum transfer size of " "%zu bytes, but the default partial transfer size " "is %u bytes\n"), flashdev->drvname, maxxfer, FW_SD_PARTIAL_WRITE_SIZE); goto err; } maxxfer = FW_SD_PARTIAL_WRITE_SIZE; if (ffsll(maxxfer) < align || align == 0xff) { logmsg(MSG_ERROR, gettext("%s: cannot write firmware " "image: device requires partial writes aligned " "to an unsupported value\n"), flashdev->drvname); goto err; } logmsg(MSG_INFO, "%s: final transfer block size is %zu\n", flashdev->drvname, maxxfer); } logmsg(MSG_INFO, "%s: Writing out %u bytes to %s\n", flashdev->drvname, verifier->imgsize, flashdev->access_devname); nwrite = 0; for (;;) { uintptr_t buf; size_t towrite = MIN(maxxfer, verifier->imgsize - nwrite); if (towrite == 0) break; buf = (uintptr_t)verifier->fwimage; buf += nwrite; if (sdfw_write(flashdev, handle, target, towrite, nwrite, (void *)buf) != FWFLASH_SUCCESS) { logmsg(MSG_ERROR, gettext("%s: failed to write to %s " "successfully: %s\n"), flashdev->drvname, flashdev->access_devname, libscsi_errmsg(handle)); goto err; } nwrite += towrite; } logmsg(MSG_ERROR, gettext("Note: For flash based disks " "(SSD, etc). You may need power off the system to wait a " "few minutes for supercap to fully discharge, then power " "on the system again to activate the new firmware\n")); ret = FWFLASH_SUCCESS; err: if (target != NULL) libscsi_close(handle, target); if (handle != NULL) libscsi_fini(handle); return (ret); } /* * 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 needed * * When we store the desired information, inquiry-serial-no * goes in thisdev->addresses[1], and client-guid goes in * thisdev->addresses[2]. */ int fw_identify(int start) { int idx = start; int fw_sata_disk = 0; int *exists; di_node_t thisnode; struct devicelist *newdev = NULL; char *devpath = NULL; char *driver = NULL; char *sp_temp; char *sp_temp_cut; /* We need to inquiry information manually by sending probe command */ libscsi_hdl_t *handle; libscsi_target_t *target; libscsi_errno_t serr; /* Just in case we've got an FC-attached device on sparc */ if (strcmp(self->drvname, "ssd") == 0) { driver = self->drvname; } else driver = drivername; thisnode = di_drv_first_node(driver, rootnode); if (thisnode == DI_NODE_NIL) { logmsg(MSG_INFO, "No %s nodes in this system\n", driver); return (FWFLASH_FAILURE); } if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to initialize " "libscsi\n"), newdev->drvname); return (FWFLASH_FAILURE); } /* we've found one, at least */ for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) { /* Need to free by di_devfs_path_free */ if ((devpath = di_devfs_path(thisnode)) == NULL) { logmsg(MSG_INFO, "unable to get device path for " "current node with errno %d\n", errno); continue; } /* * We check if this is removable device, in which case * we really aren't interested, so exit stage left */ if (di_prop_lookup_ints(DDI_DEV_T_ANY, thisnode, "removable-media", &exists) > -1) { logmsg(MSG_INFO, "%s: not interested in removable media device\n" "%s\n", driver, devpath); FW_SD_FREE_DEVPATH(devpath) continue; } if ((newdev = calloc(1, sizeof (struct devicelist))) == NULL) { logmsg(MSG_ERROR, gettext("%s: identification function unable " "to allocate space for device entry\n"), driver); libscsi_fini(handle); FW_SD_FREE_DEVPATH(devpath) return (FWFLASH_FAILURE); } if ((newdev->drvname = calloc(1, strlen(driver) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate space to store a " "driver name\n"), driver); libscsi_fini(handle); FW_SD_FREE_DEVICELIST(newdev, devpath) return (FWFLASH_FAILURE); } (void) strlcpy(newdev->drvname, driver, strlen(driver) + 1); if ((newdev->classname = calloc(1, strlen(driver) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate space for a class " "name\n"), drivername); libscsi_fini(handle); FW_SD_FREE_DRV_NAME(newdev, devpath) return (FWFLASH_FAILURE); } (void) strlcpy(newdev->classname, driver, strlen(driver) + 1); /* Get the access name for current node */ if ((newdev->access_devname = calloc(1, MAXPATHLEN)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate space for a devfs " "name\n"), driver); libscsi_fini(handle); FW_SD_FREE_CLS_NAME(newdev, devpath) return (FWFLASH_FAILURE); } /* The slice number may be 2 or 0, we will try 2 first */ (void) snprintf(newdev->access_devname, MAXPATHLEN, "%s%s:c,raw", sdfw_devprefix, devpath); if ((target = libscsi_open(handle, NULL, newdev->access_devname)) == NULL) { /* try 0 for EFI label */ (void) snprintf(newdev->access_devname, MAXPATHLEN, "%s%s:a,raw", sdfw_devprefix, devpath); if ((target = libscsi_open(handle, NULL, newdev->access_devname)) == NULL) { logmsg(MSG_INFO, "%s: unable to open device %s\n", newdev->drvname, newdev->access_devname); FW_SD_FREE_ACC_NAME(newdev, devpath) continue; } } /* and the /dev/rdsk/ name */ if ((newdev->addresses[0] = sdfw_find_link(thisnode, newdev->access_devname)) == NULL) { libscsi_fini(handle); FW_SD_FREE_ACC_NAME(newdev, devpath) return (FWFLASH_FAILURE); } /* * Only alloc as much as we truly need, and DON'T forget * that libdevinfo manages the memory! */ if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate space for SCSI " "INQUIRY data\n"), driver); libscsi_fini(handle); FW_SD_FREE_ADDR(newdev, devpath) return (FWFLASH_FAILURE); } /* We don't use new->ident->encap_ident currently */ /* Retrive information by using libscsi */ /* Vendor ID */ sp_temp = (char *)libscsi_vendor(target); if (strncmp(sp_temp, "ATA", 3) == 0) { /* We need to do customize the output for SATA disks */ fw_sata_disk = 1; } else { fw_sata_disk = 0; if ((newdev->ident->vid = calloc(1, strlen(sp_temp) + 1)) == NULL || sp_temp == NULL) { if (!sp_temp) { logmsg(MSG_ERROR, gettext("%s: unable " "to get vendor id of %s\n"), newdev->drvname, newdev->access_devname); } else { logmsg(MSG_ERROR, gettext("Memory " "allocation failure\n")); } libscsi_close(handle, target); libscsi_fini(handle); FW_SD_FREE_IDENT(newdev, devpath) return (FWFLASH_FAILURE); } strlcpy(newdev->ident->vid, sp_temp, strlen(sp_temp) + 1); } /* Product ID */ sp_temp = (char *)libscsi_product(target); if (fw_sata_disk) { sp_temp_cut = strchr(sp_temp, ' '); if (!sp_temp_cut) { /* * There is no SPACE character in the PID field * Customize strings for special SATA disks */ if (sdfw_idtfy_custmz(newdev, sp_temp) != FWFLASH_SUCCESS) { libscsi_close(handle, target); libscsi_fini(handle); FW_SD_FREE_IDENT(newdev, devpath) return (FWFLASH_FAILURE); } } else { /* The first string is vendor id */ if ((newdev->ident->vid = calloc(1, (sp_temp_cut - sp_temp + 1))) == NULL) { logmsg(MSG_ERROR, gettext("%s: unable " "to get sata vendor id of %s\n"), newdev->drvname, newdev->access_devname); libscsi_close(handle, target); libscsi_fini(handle); FW_SD_FREE_IDENT(newdev, devpath) return (FWFLASH_FAILURE); } strlcpy(newdev->ident->vid, sp_temp, sp_temp_cut - sp_temp + 1); /* The second string is product id */ if ((newdev->ident->pid = calloc(1, strlen(sp_temp) - strlen(newdev->ident->vid))) == NULL) { logmsg(MSG_ERROR, gettext("%s: unable " "to get sata product id of %s\n"), newdev->drvname, newdev->access_devname); libscsi_close(handle, target); libscsi_fini(handle); FW_SD_FREE_IDENT_VID(newdev, devpath) return (FWFLASH_FAILURE); } strlcpy(newdev->ident->pid, sp_temp_cut + 1, strlen(sp_temp) - strlen(newdev->ident->vid)); } } else { if ((newdev->ident->pid = calloc(1, strlen(sp_temp) + 1)) == NULL || sp_temp == NULL) { logmsg(MSG_ERROR, gettext("%s: unable to get " "product id of %s\n"), newdev->drvname, newdev->access_devname); FW_SD_FREE_IDENT_VID(newdev, devpath) libscsi_close(handle, target); libscsi_fini(handle); return (FWFLASH_FAILURE); } strlcpy(newdev->ident->pid, sp_temp, strlen(sp_temp) + 1); } /* Revision ID */ sp_temp = (char *)libscsi_revision(target); if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1)) == NULL || sp_temp == NULL) { logmsg(MSG_ERROR, gettext("%s: unable to get revision " "id of %s\n"), newdev->drvname, newdev->access_devname); libscsi_close(handle, target); libscsi_fini(handle); FW_SD_FREE_IDENT_PID(newdev, devpath) return (FWFLASH_FAILURE); } strlcpy(newdev->ident->revid, sp_temp, strlen(sp_temp) + 1); /* Finish using libscsi */ libscsi_close(handle, target); if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, "inquiry-serial-no", &newdev->addresses[1]) < 0) { logmsg(MSG_INFO, "%s: no inquiry-serial-no property for %s\n", driver, newdev->access_devname); logmsg(MSG_INFO, "The errno is %d\n", errno); } if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, "client-guid", &newdev->addresses[2])) < 0) { logmsg(MSG_INFO, "%s: no client-guid property " "for device %s\n", driver, newdev->access_devname); /* try fallback */ if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, "guid", &newdev->addresses[2])) < 0) { logmsg(MSG_INFO, "%s: no guid property for device %s\n", driver, newdev->access_devname); } } else { logmsg(MSG_INFO, "client-guid property: %s\n", newdev->addresses[2]); } newdev->index = idx; ++idx; newdev->plugin = self; TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev); FW_SD_FREE_DEVPATH(devpath) } libscsi_fini(handle); /* Check if sd targets presented are all unflashable. */ if (idx == start) return (FWFLASH_FAILURE); if (fwflash_debug != 0) { struct devicelist *tempdev; TAILQ_FOREACH(tempdev, fw_devices, nextdev) { logmsg(MSG_INFO, "%s:fw_identify:\n", driver); 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\taddress[0]: %s\n" "\t\taddress[1]: %s\n" "\t\taddress[2]: %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[1] : "(not supported)"), (tempdev->addresses[2] ? tempdev->addresses[2] : "(not supported)"), &tempdev->plugin); } } return (FWFLASH_SUCCESS); } int fw_devinfo(struct devicelist *thisdev) { fprintf(stdout, gettext("Device[%d]\t\t\t%s\n" " Class [%s]\t\t\t%s\n"), thisdev->index, thisdev->access_devname, thisdev->classname, thisdev->addresses[0]); fprintf(stdout, gettext( "\tVendor\t\t\t: %s\n" "\tProduct\t\t\t: %s\n" "\tFirmware revision\t: %-s\n" "\tInquiry Serial Number : %-s\n" "\tGUID\t\t\t: %s\n"), thisdev->ident->vid, thisdev->ident->pid, thisdev->ident->revid, (thisdev->addresses[1] ? thisdev->addresses[1] : "(not supported)"), (thisdev->addresses[2] ? thisdev->addresses[2] : "(not supported)")); fprintf(stdout, "\n\n"); return (FWFLASH_SUCCESS); } void fw_cleanup(struct devicelist *thisdev) { /* * Function to clean up all the memory allocated * by this plugin, for thisdev. */ free(thisdev->access_devname); free(thisdev->drvname); free(thisdev->classname); /* * Note that we DO NOT free addresses[1,2] because _IF_ * these elements are valid, they are managed by libdevinfo * and we didn't allocate any space for them. */ free(thisdev->addresses[0]); /* what this points to is freed in common code */ thisdev->plugin = NULL; free(thisdev->ident->vid); free(thisdev->ident->pid); free(thisdev->ident->revid); thisdev->ident = NULL; } /* * Helper functions */ static int sdfw_link_cb(di_devlink_t devlink, void *arg) { const char *result; result = di_devlink_path(devlink); if (result == NULL) { arg = (void *)"(null)"; } else { (void) strlcpy(arg, result, strlen(result) + 1); } logmsg(MSG_INFO, "\nsdfw_link_cb::linkdata->resultstr = %s\n", ((result != NULL) ? result : "(null)")); return (DI_WALK_CONTINUE); } static char * sdfw_find_link(di_node_t bnode, char *acc_devname) { di_minor_t devminor = DI_MINOR_NIL; di_devlink_handle_t hdl; char *cbresult = NULL; char linkname[] = "^rdsk/\0"; if (bnode == DI_NODE_NIL) { logmsg(MSG_ERROR, gettext("sdfw_find_link must be called with non-null " "di_node_t\n")); return (NULL); } if ((cbresult = calloc(1, MAXPATHLEN)) == NULL) { logmsg(MSG_ERROR, gettext("unable to allocate space for dev " "link\n")); return (NULL); } devminor = di_minor_next(bnode, devminor); errno = 0; hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK); if (hdl == NULL) { if (errno == EPERM || errno == EACCES) { logmsg(MSG_ERROR, gettext("%s: You must be super-user to use this " "plugin.\n"), drivername); } else { logmsg(MSG_ERROR, gettext("unable to take devlink snapshot: %s\n"), strerror(errno)); } free(cbresult); return (NULL); } errno = 0; if (di_devlink_walk(hdl, linkname, acc_devname + strlen(sdfw_devprefix), DI_PRIMARY_LINK, (void *)cbresult, sdfw_link_cb) < 0) { logmsg(MSG_ERROR, gettext("Unable to walk devlink snapshot for %s: %s\n"), acc_devname, strerror(errno)); free(cbresult); return (NULL); } if (di_devlink_fini(&hdl) < 0) { logmsg(MSG_ERROR, gettext("Unable to close devlink snapshot: %s\n"), strerror(errno)); } logmsg(MSG_INFO, "cbresult: %s\n", cbresult); return (cbresult); } static int sdfw_idtfy_custmz(struct devicelist *device, char *sp) { /* vid customization */ if (strncmp(sp, "ST", 2) == 0) { /* Customize retail Seagate disks */ if ((device->ident->vid = strdup("SEAGATE")) == NULL) { return (FWFLASH_FAILURE); } } else if (strncmp(sp, "SSD", 3) == 0) { /* Customize retail INTEL disks */ if ((device->ident->vid = strdup("INTEL")) == NULL) { return (FWFLASH_FAILURE); } } else { /* disks to do in the future, fill 'ATA' first */ if ((device->ident->vid = strdup("ATA")) == NULL) { return (FWFLASH_FAILURE); } } /* pid customization */ if ((device->ident->pid = calloc(1, strlen(sp) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("Unable to allocate space for " "product id\n")); free(device->ident->vid); return (FWFLASH_FAILURE); } strlcpy(device->ident->pid, sp, strlen(sp) + 1); return (FWFLASH_SUCCESS); } /* * 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. */ /* * ses (SCSI Generic Device) specific functions. */ #include #include #include #include #include #include #include #include #include #include #include #include /* for gettext(3c) */ #include #define VIDLEN 0x08 #define PIDLEN 0x10 #define REVLEN 0x04 #define SASADDRLEN 0x10 #define PCBUFLEN 0x40 #define RQBUFLEN 0xfe #define STATBUFLEN 0xfe #define INQBUFLEN 0x80 /* useful defines */ #define UCODE_CHECK_STATUS 0 #define UCODE_CHECK_SUPPORTED 1 typedef struct ucode_statdesc { uint64_t us_value; const char *us_desc; boolean_t us_pending; boolean_t us_iserr; } ucode_statdesc_t; static ucode_statdesc_t ucode_statdesc_table[] = { { SES2_DLUCODE_S_NOP, "none", B_FALSE, B_FALSE }, { SES2_DLUCODE_S_INPROGRESS, "in progress", B_TRUE, B_FALSE }, { SES2_DLUCODE_S_SAVING, "saved", B_TRUE, B_FALSE }, { SES2_DLUCODE_S_COMPLETE_NOW, "completed (available)", B_FALSE, B_FALSE }, { SES2_DLUCODE_S_COMPLETE_AT_RESET, "completed (need reset or power on)", B_FALSE, B_FALSE }, { SES2_DLUCODE_S_COMPLETE_AT_POWERON, "completed (need power on)", B_FALSE, B_FALSE }, { SES2_DLUCODE_S_PAGE_ERR, "page error (offset %d)", B_FALSE, B_TRUE }, { SES2_DLUCODE_S_IMAGE_ERR, "invalid image", B_FALSE, B_TRUE }, { SES2_DLUCODE_S_TIMEOUT, "download timeout", B_FALSE, B_TRUE }, { SES2_DLUCODE_S_INTERNAL_NEEDIMAGE, "internal error (NEED NEW IMAGE BEFORE RESET)", B_FALSE, B_TRUE }, { SES2_DLUCODE_S_INTERNAL_SAFE, "internal error (reset to revert to backup)", B_FALSE, B_TRUE }, }; #define NUCODE_STATUS \ (sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0])) typedef struct ucode_status { uint64_t us_status; boolean_t us_iserr; boolean_t us_pending; char us_desc[128]; } ucode_status_t; typedef struct ucode_wait { uint64_t uw_prevstatus; boolean_t uw_pending; ses_node_t *uw_oldnp; } ucode_wait_t; typedef struct sam4_statdesc { int status; char *message; } sam4_statdesc_t; static sam4_statdesc_t sam4_status[] = { { SAM4_STATUS_GOOD, "Status: GOOD (success)" }, { SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" }, { SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" }, { SAM4_STATUS_BUSY, "Status: Device is BUSY" }, { SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" }, { SAM4_STATUS_TASK_SET_FULL, "Status: TASK SET FULL (insufficient resources in command queue" }, { SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" }, { 0, NULL } }; #define NSAM4_STATUS \ (sizeof (sam4_status) / sizeof (sam4_status[0])) char drivername[] = "ses\0"; static char *devprefix = "/devices"; static char *sessuffix = ":0"; static char *sgensuffix = ":ses"; static ses_target_t *ses_target; 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(struct devicelist *thisdev); /* helper functions */ static int print_updated_status(ses_node_t *np, void *arg); static int get_status(nvlist_t *props, ucode_status_t *sp); static int sendimg(ses_node_t *np, void *data); static int scsi_writebuf(); /* * We don't currently support reading firmware from a SAS * expander. If we do eventually support it, we would use * the scsi READ BUFFER command to do so. */ int fw_readfw(struct devicelist *flashdev, char *filename) { logmsg(MSG_INFO, "%s: not writing firmware for device %s to file %s\n", flashdev->drvname, flashdev->access_devname, filename); logmsg(MSG_ERROR, gettext("\n\nReading of firmware images from %s-attached " "devices is not supported\n\n"), flashdev->drvname); return (FWFLASH_SUCCESS); } /* * If we're invoking fw_writefw, then flashdev is a valid, * flashable device supporting the SES2 Download Microcode Diagnostic * Control page (0x0e). * * If verifier is null, then we haven't been called following a firmware * image verification load operation. * * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to * achieve the task... if you chase down to the bottom of libses you * can see that too. */ int fw_writefw(struct devicelist *flashdev) { int rv = FWFLASH_FAILURE; nvlist_t *nvl; ses_snap_t *snapshot; ses_node_t *targetnode; if ((verifier == NULL) || (verifier->imgsize == 0) || (verifier->fwimage == NULL)) { /* should _not_ happen */ logmsg(MSG_ERROR, gettext("%s: Firmware image has not " "been verified.\n"), flashdev->drvname); return (FWFLASH_FAILURE); } if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE, SES_DLUCODE_M_WITH_OFFS) != 0) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate " "space for device prop list\n"), flashdev->drvname); return (FWFLASH_FAILURE); } fprintf(stdout, "\n"); /* get a fresh line for progress updates */ if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, verifier->flashbuf) != 0) { logmsg(MSG_ERROR, gettext("%s: Unable to add buffer id " "property, hence unable to flash device\n"), flashdev->drvname); goto cancel; } if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA, (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) { logmsg(MSG_ERROR, "%s: Out of memory for property addition\n", flashdev->drvname); goto cancel; } if ((ses_target = ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to open flashable device %s\n"), flashdev->drvname, flashdev->access_devname); goto cancel; } snapshot = ses_snap_hold(ses_target); if ((targetnode = ses_snap_primary_enclosure(snapshot)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to locate primary enclosure for " "device %s\n"), flashdev->access_devname); } else { rv = sendimg(targetnode, nvl); if (rv == FWFLASH_SUCCESS) { logmsg(MSG_ERROR, gettext("%s: Done. New image will be active " "after the system is rebooted.\n\n"), flashdev->drvname); } else { logmsg(MSG_INFO, "%s: unable to flash image %s to device %s\n\n", flashdev->drvname, verifier->imgfile, flashdev->access_devname); } } ses_snap_rele(snapshot); ses_close(ses_target); cancel: nvlist_free(nvl); return (rv); } /* * 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 needed */ int fw_identify(int start) { int rv = FWFLASH_FAILURE; di_node_t thisnode; struct devicelist *newdev; char *devpath; char *devsuffix; char *driver; int idx = start; size_t devlength = 0; nvlist_t *props; ses_snap_t *snapshot; ses_node_t *rootnodep, *nodep; if (strcmp(self->drvname, "sgen") == 0) { devsuffix = sgensuffix; driver = self->drvname; } else { devsuffix = sessuffix; driver = drivername; } thisnode = di_drv_first_node(driver, rootnode); if (thisnode == DI_NODE_NIL) { logmsg(MSG_INFO, gettext("No %s nodes in this system\n"), driver); return (FWFLASH_FAILURE); } if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate space " "for a device node\n"), driver); return (FWFLASH_FAILURE); } /* 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"), driver); free(devpath); return (FWFLASH_FAILURE); } /* calloc enough for /devices + devpath + devsuffix + '\0' */ devlength = strlen(devpath) + strlen(devprefix) + strlen(devsuffix) + 2; if ((newdev->access_devname = calloc(1, devlength)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate " "space for a devfs name\n"), driver); free(devpath); free(newdev); return (FWFLASH_FAILURE); } snprintf(newdev->access_devname, devlength, "%s%s%s", devprefix, devpath, devsuffix); if ((newdev->drvname = calloc(1, strlen(driver) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to allocate " "space to store a driver name\n"), driver); free(newdev->access_devname); free(newdev); free(devpath); return (FWFLASH_FAILURE); } (void) strlcpy(newdev->drvname, driver, strlen(driver) + 1); if ((newdev->classname = calloc(1, strlen(driver) + 1)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to malloc " "space for a class name\n"), drivername); free(newdev->access_devname); free(newdev->drvname); free(newdev); free(devpath); return (FWFLASH_FAILURE); } (void) strlcpy(newdev->classname, driver, strlen(driver) + 1); /* * Only alloc as much as we truly need, and DON'T forget * that libnvpair manages the memory for property lookups! * The same goes for libdevinfo properties. * * Also note that we're allocating here before we try to * ses_open() the target, because if we can't allocate * sufficient space then we might as well go home. */ newdev->ident = calloc(1, VIDLEN + PIDLEN + REVLEN + 3); if (newdev->ident == NULL) { logmsg(MSG_ERROR, gettext("%s: Unable to malloc space for" "SCSI INQUIRY data\n"), driver); free(newdev->classname); free(newdev->drvname); free(newdev->access_devname); free(newdev); free(devpath); return (FWFLASH_FAILURE); } if ((ses_target = ses_open(LIBSES_VERSION, newdev->access_devname)) == NULL) { logmsg(MSG_INFO, gettext("%s: Unable to open device %s\n"), driver, newdev->access_devname); free(newdev->ident); free(newdev->classname); free(newdev->access_devname); free(newdev->drvname); free(newdev); free(devpath); continue; } snapshot = ses_snap_hold(ses_target); rootnodep = ses_root_node(snapshot); /* * If the node has no properties, or the INQUIRY properties * don't exist, this device does not comply with SES2 so we * won't touch it. */ if ((props = ses_node_props(rootnodep)) == NULL) { free(newdev->ident); ses_snap_rele(snapshot); ses_close(ses_target); free(newdev->classname); free(newdev->access_devname); free(newdev->drvname); free(newdev); free(devpath); continue; } if ((nvlist_lookup_string(props, SCSI_PROP_VENDOR, &newdev->ident->vid) != 0) || (nvlist_lookup_string(props, SCSI_PROP_PRODUCT, &newdev->ident->pid) != 0) || (nvlist_lookup_string(props, SCSI_PROP_REVISION, &newdev->ident->revid) != 0)) { free(newdev->ident); ses_snap_rele(snapshot); ses_close(ses_target); free(newdev->classname); free(newdev->access_devname); free(newdev->drvname); free(newdev); free(devpath); continue; } nodep = ses_snap_primary_enclosure(snapshot); if ((props = ses_node_props(nodep)) == NULL) { free(newdev->ident); ses_snap_rele(snapshot); ses_close(ses_target); free(newdev->classname); free(newdev->access_devname); free(newdev->drvname); free(newdev); free(devpath); continue; } logmsg(MSG_INFO, "\nvid: %s\npid: %s\nrevid: %s\n", newdev->ident->vid, newdev->ident->pid, newdev->ident->revid); if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &newdev->addresses[0]) == 0) { logmsg(MSG_INFO, "Chassis Serial Number: %s\n", newdev->addresses[0]); } else logmsg(MSG_INFO, "%s: no chassis-serial-number property " "for device %s\n", driver, newdev->access_devname); rv = di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, "target-port", &newdev->addresses[1]); if (rv < 0) { logmsg(MSG_INFO, "%s: no target-port property " "for device %s\n", driver, newdev->access_devname); } else logmsg(MSG_INFO, "target-port property: %s\n", newdev->addresses[1]); newdev->index = idx; ++idx; newdev->plugin = self; ses_snap_rele(snapshot); TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev); } if (fwflash_debug != 0) { struct devicelist *tempdev; TAILQ_FOREACH(tempdev, fw_devices, nextdev) { logmsg(MSG_INFO, "%s:fw_identify:\n", driver); 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\taddress[0]: %s\n" "\t\taddress[1]: %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->plugin); } } return (FWFLASH_SUCCESS); } int fw_devinfo(struct devicelist *thisdev) { fprintf(stdout, gettext("Device[%d] %s\n Class [%s]\n"), thisdev->index, thisdev->access_devname, thisdev->classname); fprintf(stdout, gettext("\tVendor : %s\n" "\tProduct : %s\n" "\tFirmware revision : %s\n" "\tChassis Serial Number : %s\n" "\tTarget-port identifier : %s\n"), thisdev->ident->vid, thisdev->ident->pid, thisdev->ident->revid, (thisdev->addresses[0] ? thisdev->addresses[0] : "(not supported)"), (thisdev->addresses[1] ? thisdev->addresses[1] : "(not supported)")); fprintf(stdout, "\n\n"); return (FWFLASH_SUCCESS); } /*ARGSUSED*/ static int get_status(nvlist_t *props, ucode_status_t *sp) { int i; uint64_t status, astatus; if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) { sp->us_status = -1ULL; (void) snprintf(sp->us_desc, sizeof (sp->us_desc), "not supported"); return (FWFLASH_FAILURE); } if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A, &astatus) != 0) { logmsg(MSG_ERROR, gettext("\nError: Unable to retrieve current status\n")); return (FWFLASH_FAILURE); } for (i = 0; i < NUCODE_STATUS; i++) { if (ucode_statdesc_table[i].us_value == status) break; } sp->us_status = status; if (i == NUCODE_STATUS) { (void) snprintf(sp->us_desc, sizeof (sp->us_desc), "unknown (0x%02x)", (int)status); sp->us_iserr = sp->us_pending = B_TRUE; return (FWFLASH_FAILURE); } else { /* LINTED */ (void) snprintf(sp->us_desc, sizeof (sp->us_desc), ucode_statdesc_table[i].us_desc, (int)astatus); sp->us_iserr = ucode_statdesc_table[i].us_iserr; sp->us_pending = ucode_statdesc_table[i].us_pending; } return (FWFLASH_SUCCESS); } static int print_updated_status(ses_node_t *np, void *arg) { ucode_wait_t *uwp = arg; nvlist_t *props; ucode_status_t status; if ((props = ses_node_props(np)) == NULL) { return (FWFLASH_FAILURE); } if (get_status(props, &status) != FWFLASH_SUCCESS) return (FWFLASH_FAILURE); if (status.us_status != uwp->uw_prevstatus) (void) printf("%30s: %s\n", "status", status.us_desc); uwp->uw_prevstatus = status.us_status; uwp->uw_pending = status.us_pending; if (status.us_iserr) { logmsg(MSG_INFO, "libses: status.us_iserr: 0x%0x\n", status.us_iserr); return (FWFLASH_FAILURE); } return (FWFLASH_SUCCESS); } /*ARGSUSED*/ static int sendimg(ses_node_t *np, void *data) { nvlist_t *props; nvlist_t *arg = data; char *vendor, *product, *revision, *csn; char buf[128]; ses_snap_t *newsnap; int ret; ucode_status_t statdesc; ucode_wait_t wait; uint8_t *imagedata; uint_t len; /* If we've been called without data, eject */ if (nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA, &imagedata, &len) != 0) { return (FWFLASH_FAILURE); } props = ses_node_props(np); if ((props == NULL) || (nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) != 0) || (nvlist_lookup_string(props, SES_EN_PROP_PID, &product) != 0) || (nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) != 0) || (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) != 0)) { return (FWFLASH_FAILURE); } (void) printf("%30s: %s\n", "vendor", vendor); (void) printf("%30s: %s\n", "product", product); (void) printf("%30s: %s\n", "revision", revision); (void) printf("%30s: %s\n", "serial", csn); ret = get_status(props, &statdesc); (void) printf("%30s: %s\n", "current status", statdesc.us_desc); if (ret != FWFLASH_SUCCESS) { return (FWFLASH_FAILURE); } (void) snprintf(buf, sizeof (buf), "downloading %u bytes", len); (void) printf("\n%30s: ", buf); /* * If the bufferid isn't 2, then the verifier has already * OK'd the image that the user has provided. * * At present the non-"standard" images need to be flashed * using the scsi WRITE BUFFER command */ if (verifier->flashbuf != 2) return (scsi_writebuf()); if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != FWFLASH_SUCCESS) { (void) printf("failed!\n"); (void) printf("%s\n", ses_errmsg()); return (FWFLASH_FAILURE); } else { (void) printf("ok\n"); } wait.uw_prevstatus = -1ULL; wait.uw_oldnp = np; if ((newsnap = ses_snap_new(ses_target)) == NULL) { logmsg(MSG_ERROR, "failed to update SES snapshot: %s", ses_errmsg()); return (FWFLASH_FAILURE); } print_updated_status(ses_snap_primary_enclosure(newsnap), &wait); ses_snap_rele(newsnap); return (FWFLASH_SUCCESS); } static int scsi_writebuf() { int ret; int i = 0; libscsi_action_t *action; spc3_write_buffer_cdb_t *wb_cdb; libscsi_hdl_t *handle; libscsi_target_t *target; sam4_status_t samstatus; target = ses_scsi_target(ses_target); handle = libscsi_get_handle(target); action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER, LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE, (void *)verifier->fwimage, (size_t)verifier->imgsize); wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action); wb_cdb->wbc_mode = SPC3_WB_MODE_DATA; wb_cdb->wbc_bufferid = verifier->flashbuf; wb_cdb->wbc_buffer_offset[0] = 0; wb_cdb->wbc_buffer_offset[1] = 0; wb_cdb->wbc_buffer_offset[2] = 0; wb_cdb->wbc_parameter_list_len[0] = (verifier->imgsize & 0xff0000) >> 16; wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8; wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff); ret = libscsi_exec(action, target); samstatus = libscsi_action_get_status(action); logmsg(MSG_INFO, "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n", ret, samstatus); if ((ret != FWFLASH_SUCCESS) || samstatus != SAM4_STATUS_GOOD) { libscsi_action_free(action); return (FWFLASH_FAILURE); } else { (void) printf("ok\n"); } for (i = 0; i < NSAM4_STATUS; i++) { if (sam4_status[i].status == samstatus) { (void) printf("%s\n", (sam4_status[i].message)); break; } } if (i == NSAM4_STATUS) (void) printf("Status: UNKNOWN\n"); if (samstatus == SAM4_STATUS_GOOD) { return (FWFLASH_SUCCESS); } 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. */ /* * 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); } /* * 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 2022 Oxide Computer Company */ /* * fwflash(8) backend for UFMs. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * We pick a fixed size unit to work on. */ #define UFM_READ_BUFLEN (16 * 1024 * 1024) /* * These are indexes into the addresses array that we use. */ #define UFM_ADDR_PATH 0 #define UFM_ADDR_SUB 1 #define UFM_ADDR_CAP 2 typedef struct ufmfw_ident_arg { uint_t uia_nfound; int uia_index; int uia_err; } ufmfw_ident_arg_t; /* * fwflash requires we declare our driver name as data with this name. */ const char drivername[] = "ufm"; const int plugin_version = FWPLUGIN_VERSION_2; /* * External data from fwflash. */ extern di_node_t rootnode; extern struct fw_plugin *self; /* * Global, shared data. */ static int ufmfw_ufm_fd = -1; static pcidb_hdl_t *ufmfw_pcidb; static boolean_t ufmfw_ready = B_FALSE; /* * Read image zero and slot zero that we find. */ int fw_readfw(struct devicelist *flashdev, const char *filename) { nvlist_t **images, **slots; uint_t nimages, nslots, caps; uint64_t imgsize, offset; void *buf; int fd; nvlist_t *nvl = flashdev->ident->encap_ident; caps = (uintptr_t)flashdev->addresses[UFM_ADDR_CAP]; if ((caps & DDI_UFM_CAP_READIMG) == 0) { logmsg(MSG_ERROR, "%s: device %s does not support reading " "images\n", flashdev->drvname, flashdev->access_devname); return (FWFLASH_FAILURE); } if (nvlist_lookup_nvlist_array(nvl, DDI_UFM_NV_IMAGES, &images, &nimages) != 0) { logmsg(MSG_ERROR, gettext("%s: %s missing UFM image data\n"), flashdev->drvname, flashdev->access_devname); return (FWFLASH_FAILURE); } if (nimages == 0) { logmsg(MSG_ERROR, gettext("%s: %s has no UFM images\n"), flashdev->drvname, flashdev->access_devname); return (FWFLASH_FAILURE); } if (nvlist_lookup_nvlist_array(images[0], DDI_UFM_NV_IMAGE_SLOTS, &slots, &nslots) != 0) { logmsg(MSG_ERROR, gettext("%s: image zero of %s has no " "slots\n"), flashdev->drvname, flashdev->access_devname); return (FWFLASH_FAILURE); } if (nvlist_lookup_uint64(slots[0], DDI_UFM_NV_SLOT_IMGSIZE, &imgsize) != 0) { logmsg(MSG_ERROR, gettext("%s: device %s doesn't have an image " "size\n"), flashdev->drvname, flashdev->access_devname); return (FWFLASH_FAILURE); } logmsg(MSG_INFO, gettext("%s: Need to read %" PRIu64 " bytes\n"), flashdev->drvname, imgsize); if ((buf = malloc(UFM_READ_BUFLEN)) == NULL) { logmsg(MSG_ERROR, gettext("%s: Failed to allocate data " "buffer\n"), flashdev->drvname); return (FWFLASH_FAILURE); } if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0) { logmsg(MSG_ERROR, gettext("%s: failed to open file %s: %s\n"), flashdev->drvname, filename, strerror(errno)); free(buf); return (FWFLASH_FAILURE); } offset = 0; while (imgsize > 0) { ufm_ioc_readimg_t rimg; uint64_t toread = MIN(imgsize, UFM_READ_BUFLEN); size_t woff; bzero(&rimg, sizeof (rimg)); rimg.ufri_version = DDI_UFM_CURRENT_VERSION; rimg.ufri_imageno = 0; rimg.ufri_slotno = 0; rimg.ufri_offset = offset; rimg.ufri_len = toread; rimg.ufri_buf = buf; (void) strlcpy(rimg.ufri_devpath, flashdev->addresses[UFM_ADDR_PATH], sizeof (rimg.ufri_devpath)); logmsg(MSG_INFO, gettext("%s: want to read %" PRIu64 " bytes " "at offset %" PRIu64 "\n"), flashdev->drvname, rimg.ufri_len, rimg.ufri_offset); if (ioctl(ufmfw_ufm_fd, UFM_IOC_READIMG, &rimg) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to read image: " "%s\n"), flashdev->drvname, strerror(errno)); free(buf); (void) close(fd); return (FWFLASH_FAILURE); } logmsg(MSG_INFO, gettext("%s: read %" PRIu64 " bytes at offset " "%" PRIu64 "\n"), flashdev->drvname, rimg.ufri_nread, offset); offset += rimg.ufri_nread; imgsize -= rimg.ufri_nread; woff = 0; while (rimg.ufri_nread > 0) { size_t towrite = MIN(rimg.ufri_nread, UFM_READ_BUFLEN); ssize_t ret = write(fd, buf + woff, towrite); if (ret == -1) { logmsg(MSG_ERROR, gettext("%s: failed to write " "to %s: %s\n"), flashdev->drvname, filename, strerror(errno)); free(buf); (void) close(fd); return (FWFLASH_FAILURE); } rimg.ufri_nread -= ret; woff += ret; } } free(buf); if (close(fd) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to finish writing to %s: " "%s\n"), flashdev->drvname, filename, strerror(errno)); return (FWFLASH_FAILURE); } logmsg(MSG_INFO, gettext("%s: successfully wrote image to %s\n"), flashdev->drvname, filename); return (FWFLASH_SUCCESS); } int fw_writefw(struct devicelist *flashdev) { return (FWFLASH_SUCCESS); } static void ufmfw_flashdev_free(struct devicelist *flashdev) { if (flashdev == NULL) return; if (flashdev->ident != NULL) { free(flashdev->ident->vid); free(flashdev->ident->pid); nvlist_free(flashdev->ident->encap_ident); } free(flashdev->ident); free(flashdev->drvname); free(flashdev->classname); free(flashdev->access_devname); di_devfs_path_free(flashdev->addresses[UFM_ADDR_PATH]); free(flashdev->addresses[UFM_ADDR_SUB]); free(flashdev); } /* * Check if a node is a PCI device. This is so we can deal with VPD information. * Hopefully we'll have a generalized devinfo or fmtopo VPD section which we can * then use for this instead. */ static boolean_t ufmfw_node_pci(di_node_t node) { while (node != DI_NODE_NIL) { char *strs; int ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "device_type", &strs); if (ret > 0) { if (strcmp(strs, "pci") == 0 || strcmp(strs, "pciex") == 0) { return (B_TRUE); } } node = di_parent_node(node); } return (B_FALSE); } /* * Cons up VPD information based on the PCI ID. Hopefully in time we'll use the * actual PCI VPD information and more generally allow a device to specify its * vpd automatically. */ static boolean_t ufmfw_fill_vpd(struct devicelist *flashdev, di_node_t node) { int *vid, *did, *svid, *sdid; pcidb_vendor_t *vend = NULL; pcidb_device_t *dev = NULL; pcidb_subvd_t *subdv = NULL; char *vstr, *dstr; if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid) != 1) { logmsg(MSG_ERROR, gettext("%s: %s missing 'vendor-id' " "property\n"), flashdev->drvname, flashdev->access_devname); return (B_FALSE); } if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did) != 1) { logmsg(MSG_ERROR, gettext("%s: %s missing 'device-id' " "property\n"), flashdev->drvname, flashdev->access_devname); return (B_FALSE); } if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id", &svid) != 1 || di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-device-id", &sdid) != 1) { svid = NULL; sdid = NULL; } vend = pcidb_lookup_vendor(ufmfw_pcidb, vid[0]); if (vend != NULL) { dev = pcidb_lookup_device_by_vendor(vend, did[0]); } if (dev != NULL && svid != NULL && sdid != NULL) { subdv = pcidb_lookup_subvd_by_device(dev, svid[0], sdid[0]); } if (vend != NULL) { vstr = strdup(pcidb_vendor_name(vend)); } else { (void) asprintf(&vstr, "pci:%x", vid[0]); } if (vstr == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to allocate vid " "string\n"), flashdev->drvname); return (B_FALSE); } flashdev->ident->vid = vstr; if (dev != NULL) { dstr = strdup(pcidb_device_name(dev)); } else { (void) asprintf(&dstr, "pci:%x", did[0]); } if (dstr == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to allocate pid " "string\n"), flashdev->drvname); return (B_FALSE); } flashdev->ident->pid = dstr; if (subdv != NULL) { /* * Because this is optional, don't fail if we fail to duplicate * this. */ flashdev->addresses[UFM_ADDR_SUB] = strdup(pcidb_subvd_name(subdv)); if (flashdev->addresses[UFM_ADDR_SUB] == NULL) { logmsg(MSG_WARN, gettext("%s: failed to allocate vpd " "subsystem name\n"), flashdev->drvname); } } return (B_TRUE); } static int ufmfw_di_walk_cb(di_node_t node, void *arg) { int ret; boolean_t found = B_FALSE; di_prop_t prop = DI_PROP_NIL; ufmfw_ident_arg_t *uia = arg; struct devicelist *flashdev = NULL; ufm_ioc_getcaps_t caps; ufm_ioc_bufsz_t bufsz; ufm_ioc_report_t rep; char *devfs, *packnvl; nvlist_t *nvl = NULL; while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { const char *pname = di_prop_name(prop); if (strcmp(pname, "ddi-ufm-capable") == 0) { found = B_TRUE; break; } } if (!found) { return (DI_WALK_CONTINUE); } if (!ufmfw_node_pci(node)) { return (DI_WALK_CONTINUE); } if ((devfs = di_devfs_path(node)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to get device node " "path\n"), drivername); goto err; } bzero(&caps, sizeof (caps)); caps.ufmg_version = DDI_UFM_CURRENT_VERSION; (void) strlcpy(caps.ufmg_devpath, devfs, sizeof (caps.ufmg_devpath)); if (ioctl(ufmfw_ufm_fd, UFM_IOC_GETCAPS, &caps) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to get UFM caps for " "UFM compatible device %s: %s\n"), drivername, devfs, strerror(errno)); di_devfs_path_free(devfs); return (DI_WALK_CONTINUE); } /* * If nothing is supported just leave it be. */ if (caps.ufmg_caps == 0) { di_devfs_path_free(devfs); return (DI_WALK_CONTINUE); } bzero(&bufsz, sizeof (bufsz)); bufsz.ufbz_version = DDI_UFM_CURRENT_VERSION; (void) strlcpy(bufsz.ufbz_devpath, devfs, sizeof (bufsz.ufbz_devpath)); if (ioctl(ufmfw_ufm_fd, UFM_IOC_REPORTSZ, &bufsz) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to get UFM report size " "for device %s: %s\n"), drivername, devfs, strerror(errno)); di_devfs_path_free(devfs); return (DI_WALK_CONTINUE); } if ((packnvl = malloc(bufsz.ufbz_size)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to allocate %zu bytes " "for report buffer\n"), drivername, bufsz.ufbz_size); di_devfs_path_free(devfs); goto err; } bzero(&rep, sizeof (rep)); rep.ufmr_version = DDI_UFM_CURRENT_VERSION; rep.ufmr_bufsz = bufsz.ufbz_size; rep.ufmr_buf = packnvl; (void) strlcpy(rep.ufmr_devpath, devfs, sizeof (rep.ufmr_devpath)); if (ioctl(ufmfw_ufm_fd, UFM_IOC_REPORT, &rep) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to get UFM report " "for device %s: %s\n"), drivername, devfs, strerror(errno)); free(packnvl); di_devfs_path_free(devfs); return (DI_WALK_CONTINUE); } if ((ret = nvlist_unpack(packnvl, rep.ufmr_bufsz, &nvl, 0)) != 0) { logmsg(MSG_ERROR, gettext("%s: failed to unpack UFM report " "for device %s: %s\n"), drivername, devfs, strerror(ret)); free(packnvl); di_devfs_path_free(devfs); return (DI_WALK_CONTINUE); } free(packnvl); if ((flashdev = calloc(1, sizeof (*flashdev))) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to allocate new " "device entry for node %s\n"), drivername, devfs); di_devfs_path_free(devfs); goto err; } flashdev->addresses[UFM_ADDR_PATH] = devfs; if (asprintf(&flashdev->access_devname, "/devices%s", flashdev->addresses[UFM_ADDR_PATH]) == -1) { logmsg(MSG_ERROR, gettext("%s: failed to construct device " "path\n"), drivername); goto err; } if ((flashdev->drvname = strdup(drivername)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to construct driver " "name\n"), drivername); goto err; } if ((flashdev->classname = strdup(drivername)) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to allocate vpd " "data\n"), drivername); goto err; } if ((flashdev->ident = calloc(1, sizeof (struct vpr))) == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to construct class " "name\n"), drivername); goto err; } if (!ufmfw_fill_vpd(flashdev, node)) { goto err; } flashdev->ident->encap_ident = nvl; flashdev->index = uia->uia_index; uia->uia_index++; flashdev->addresses[UFM_ADDR_CAP] = (void *)(uintptr_t)caps.ufmg_caps; flashdev->plugin = self; uia->uia_nfound++; TAILQ_INSERT_TAIL(fw_devices, flashdev, nextdev); return (DI_WALK_CONTINUE); err: nvlist_free(nvl); uia->uia_err = FWFLASH_FAILURE; ufmfw_flashdev_free(flashdev); return (DI_WALK_TERMINATE); } int fw_identify(int start) { ufmfw_ident_arg_t uia; if (!ufmfw_ready) { return (FWFLASH_FAILURE); } uia.uia_nfound = 0; uia.uia_index = start; uia.uia_err = FWFLASH_SUCCESS; (void) di_walk_node(rootnode, DI_WALK_CLDFIRST, &uia, ufmfw_di_walk_cb); if (uia.uia_nfound == 0) { return (FWFLASH_FAILURE); } return (uia.uia_err); } int fw_devinfo(struct devicelist *flashdev) { nvlist_t *nvl, **images; uint_t nimages, img, caps; (void) printf(gettext("Device[%d] %s\n"), flashdev->index, flashdev->access_devname); (void) printf(gettext("Class [%s]\n"), flashdev->classname); (void) printf(gettext("\tVendor: %s\n\tDevice: %s\n"), flashdev->ident->vid, flashdev->ident->pid); if (flashdev->addresses[UFM_ADDR_SUB] != NULL) { (void) printf(gettext("\tSubsystem: %s\n"), flashdev->addresses[UFM_ADDR_SUB]); } caps = (uintptr_t)flashdev->addresses[UFM_ADDR_CAP]; if (caps != 0) { boolean_t first = B_TRUE; (void) printf(gettext("\tCapabilities: ")); if (caps & DDI_UFM_CAP_REPORT) { (void) printf(gettext("Report")); first = B_FALSE; } if (caps & DDI_UFM_CAP_READIMG) { (void) printf(gettext("%sRead Image"), first ? "" : ", "); } (void) printf("\n"); } nvl = flashdev->ident->encap_ident; if (nvlist_lookup_nvlist_array(nvl, DDI_UFM_NV_IMAGES, &images, &nimages) != 0) { goto done; } for (img = 0; img < nimages; img++) { nvlist_t **slots; uint_t nslots, s; char *desc; if (nvlist_lookup_nvlist_array(images[img], DDI_UFM_NV_IMAGE_SLOTS, &slots, &nslots) != 0) { goto done; } if (nvlist_lookup_string(images[img], DDI_UFM_NV_IMAGE_DESC, &desc) != 0) { desc = NULL; } if (desc != NULL) { (void) printf(gettext("\tImage %d: %s\n"), img, desc); } else { (void) printf(gettext("\tImage %d:\n"), img); } for (s = 0; s < nslots; s++) { uint32_t attr; char *version; if (nvlist_lookup_uint32(slots[s], DDI_UFM_NV_SLOT_ATTR, &attr) != 0) { attr = 0; } if (nvlist_lookup_string(slots[s], DDI_UFM_NV_SLOT_VERSION, &version) != 0) { version = ""; } printf(gettext("\t Slot %d (%c|%c|%c): %s\n"), s, attr & DDI_UFM_ATTR_READABLE ? 'r' : '-', attr & DDI_UFM_ATTR_WRITEABLE ? 'w' : '-', attr & DDI_UFM_ATTR_ACTIVE ? 'a' : '-', attr & DDI_UFM_ATTR_EMPTY ? "" : version); } } done: (void) printf("\n\n"); return (FWFLASH_SUCCESS); } void fw_cleanup(struct devicelist *flashdev) { ufmfw_flashdev_free(flashdev); } #pragma init(ufmfw_init) static void ufmfw_init(void) { ufmfw_ufm_fd = open("/dev/ufm", O_RDONLY); if (ufmfw_ufm_fd < 0) { logmsg(MSG_ERROR, gettext("%s: failed to open /dev/ufm: %s\n"), drivername, strerror(errno)); return; } ufmfw_pcidb = pcidb_open(PCIDB_VERSION); if (ufmfw_pcidb == NULL) { logmsg(MSG_ERROR, gettext("%s: failed to open libpcidb: %s\n"), drivername, strerror(errno)); return; } ufmfw_ready = B_TRUE; } #pragma fini(ufmfw_fini) static void ufmfw_fini(void) { pcidb_close(ufmfw_pcidb); if (ufmfw_ufm_fd >= 0) { (void) close(ufmfw_ufm_fd); } ufmfw_ready = B_FALSE; }