/* * 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); }