xref: /illumos-gate/usr/src/cmd/fwflash/plugins/transport/common/tavor.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * IB (InfiniBand) specific functions.
29  */
30 
31 /*
32  * The reference for the functions in this file is the
33  *
34  *	Mellanox HCA Flash Programming Application Note
35  * (Mellanox document number 2205AN)
36  * rev 1.44, 2007. Chapter 4 in particular.
37  *
38  * NOTE: this Mellanox document is labelled Confidential
39  * so DO NOT move this file out of usr/closed without
40  * explicit approval from Sun Legal.
41  */
42 
43 /*
44  * IMPORTANT NOTE:
45  * 1. flash read is done in 32 bit quantities, and the driver returns
46  *    data in host byteorder form.
47  * 2. flash write is done in 8 bit quantities by the driver.
48  * 3. data in the flash should be in network byteorder (bigendian).
49  * 4. data in image files is in network byteorder form.
50  * 5. data in image structures in memory is kept in network byteorder.
51  * 6. the functions in this file deal with data in host byteorder form.
52  */
53 
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/sysmacros.h>
61 #include <sys/queue.h>
62 #include <fcntl.h>
63 #include <ctype.h>
64 #include <string.h>
65 #include <strings.h>
66 
67 #include <sys/byteorder.h>
68 
69 #include <libintl.h> /* for gettext(3c) */
70 
71 #include <fwflash/fwflash.h>
72 #include "../../hdrs/MELLANOX.h"
73 #include "../../hdrs/tavor_ib.h"
74 
75 
76 
77 char *devprefix = "/devices";
78 char drivername[] = "tavor\0";
79 char *devsuffix = ":devctl";
80 
81 
82 extern di_node_t rootnode;
83 extern int errno;
84 extern struct fw_plugin *self;
85 extern struct vrfyplugin *verifier;
86 extern int fwflash_debug;
87 
88 
89 /* required functions for this plugin */
90 int fw_readfw(struct devicelist *device, char *filename);
91 int fw_writefw(struct devicelist *device);
92 int fw_identify(int start);
93 int fw_devinfo();
94 
95 
96 /* helper functions */
97 
98 static int tavor_identify(struct devicelist *thisdev);
99 static int tavor_get_guids(struct ib_encap_ident *handle);
100 static int tavor_close(struct devicelist *flashdev);
101 static void tavor_cisco_extensions(mlx_xps_t *hcaxps, mlx_xps_t *diskxps);
102 static uint16_t crc16(uint8_t *image, uint32_t size);
103 static int tavor_write_sector(int fd, int sectnum, int32_t *data);
104 static int tavor_zero_sig_crc(int fd, uint32_t start);
105 static int tavor_write_xps_fia(int fd, uint32_t offset, uint32_t start);
106 static int tavor_write_xps_crc_sig(int fd, uint32_t offset, uint16_t newcrc);
107 static int tavor_blast_image(int fd, int prisec, uint32_t hcafia,
108     uint32_t sectsz, struct mlx_xps *newxps);
109 static int tavor_readback(int infd, int whichsect, int sectsz);
110 
111 
112 
113 int
114 fw_readfw(struct devicelist *flashdev, char *filename)
115 {
116 
117 	int 				rv = FWFLASH_SUCCESS;
118 	int 				fd;
119 	mode_t				mode = S_IRUSR | S_IWUSR;
120 	uint8_t				pchunks;
121 	uint8_t				*raw_pfi;
122 	uint8_t				*raw_sfi;
123 	uint32_t			j, offset;
124 	uint32_t			pfia, sfia, psz, ssz;
125 	tavor_flash_ioctl_t		tfi_data;
126 	struct ib_encap_ident		*manuf;
127 	struct mlx_xps			*lpps;
128 	struct mlx_xps			*lsps;
129 #if defined(_LITTLE_ENDIAN)
130 	uint32_t			*ptr;
131 #endif
132 
133 	errno = 0;
134 	if ((fd = open(filename, O_RDWR|O_CREAT|O_DSYNC, mode)) < 0) {
135 		logmsg(MSG_ERROR,
136 		    gettext("tavor: Unable to open specified file "
137 		    "(%s) for writing: %s\n"), filename, strerror(errno));
138 		return (FWFLASH_FAILURE);
139 	}
140 
141 	manuf =
142 	    (struct ib_encap_ident *)(uintptr_t)flashdev->ident->encap_ident;
143 	lpps = (struct mlx_xps *)(uintptr_t)manuf->pps;
144 	lsps = (struct mlx_xps *)(uintptr_t)manuf->sps;
145 
146 	/*
147 	 * Now that we've got an open, init'd fd, we can read the
148 	 * xFI from the device itself. We've already got the IS
149 	 * and xPS stored in manuf.
150 	 */
151 
152 	/* stash some values for later */
153 	pfia = MLXSWAPBITS32(lpps->fia);
154 	sfia = MLXSWAPBITS32(lsps->fia);
155 	psz = MLXSWAPBITS32(lpps->fis);
156 	ssz = MLXSWAPBITS32(lsps->fis);
157 
158 	/* Invariant Sector comes first */
159 	if ((j = write(fd, manuf->inv, manuf->sector_sz)) !=
160 	    manuf->sector_sz) {
161 		logmsg(MSG_ERROR,
162 		    gettext("tavor: Unable to write HCA Invariant Sector "
163 		    "(%d of %d bytes)\n"),
164 		    j, manuf->sector_sz);
165 		(void) tavor_close(flashdev);
166 		return (FWFLASH_FAILURE);
167 	} else {
168 		fprintf(stdout, gettext("Writing ."));
169 	}
170 
171 	/* followed by Primary Pointer Sector */
172 	if ((j = write(fd, manuf->pps, manuf->sector_sz)) !=
173 	    manuf->sector_sz) {
174 		logmsg(MSG_ERROR,
175 		    gettext("tavor: Unable to write HCA Primary Pointer "
176 		    "Sector (%d of %d bytes)\n)"),
177 		    j, manuf->sector_sz);
178 		(void) tavor_close(flashdev);
179 		return (FWFLASH_FAILURE);
180 	} else {
181 		fprintf(stdout, " .");
182 	}
183 
184 	/* followed by Secondary Pointer Sector */
185 	if ((j = write(fd, manuf->sps, manuf->sector_sz)) !=
186 	    manuf->sector_sz) {
187 		logmsg(MSG_ERROR,
188 		    gettext("tavor: Unable to write HCA Secondary Pointer "
189 		    "Sector (%d of %d bytes)\n"),
190 		    j, manuf->sector_sz);
191 		(void) tavor_close(flashdev);
192 		return (FWFLASH_FAILURE);
193 	} else {
194 		fprintf(stdout, " .");
195 	}
196 
197 	/* Now for the xFI sectors */
198 	pchunks = psz / manuf->sector_sz;
199 
200 	if ((psz % manuf->sector_sz) != 0)
201 		pchunks++;
202 
203 	/* Get the PFI, then the SFI */
204 	if ((raw_pfi = calloc(1, pchunks * manuf->sector_sz)) == NULL) {
205 		logmsg(MSG_ERROR,
206 		    gettext("tavor: Unable to allocate space for "
207 		    "device's Primary Firmware Image\n"));
208 		return (FWFLASH_FAILURE);
209 	}
210 	bzero(&tfi_data, sizeof (tavor_flash_ioctl_t));
211 	tfi_data.tf_type = TAVOR_FLASH_READ_SECTOR;
212 	j = pfia / manuf->sector_sz;
213 
214 	for (offset = 0; offset < psz; offset += manuf->sector_sz) {
215 		tfi_data.tf_sector_num = j;
216 		tfi_data.tf_sector = (caddr_t)&raw_pfi[offset];
217 		rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &tfi_data);
218 		if (rv < 0) {
219 			logmsg(MSG_ERROR,
220 			    gettext("tavor: Unable to read sector %d of "
221 			    "HCA Primary Firmware Image\n"), j);
222 			free(raw_pfi);
223 			(void) tavor_close(flashdev);
224 			return (FWFLASH_FAILURE);
225 		}
226 		++j;
227 	}
228 
229 	/*
230 	 * It appears that the tavor driver is returning a signed
231 	 * -1 (0xffff) in unassigned quadlets if we read a sector
232 	 * that isn't full, so for backwards compatibility with
233 	 * earlier fwflash versions, we need to zero out what
234 	 * remains in the sector.
235 	 */
236 	bzero(&raw_pfi[psz], (pchunks * manuf->sector_sz) - psz);
237 
238 #if defined(_LITTLE_ENDIAN)
239 	ptr = (uint32_t *)(uintptr_t)raw_pfi;
240 	for (j = 0; j < (pchunks * manuf->sector_sz / 4); j++) {
241 		ptr[j] = htonl(ptr[j]);
242 		if (j > psz)
243 			break;
244 	}
245 #endif
246 
247 	if ((j = write(fd, raw_pfi, pchunks * manuf->sector_sz))
248 	    != pchunks * manuf->sector_sz) {
249 		logmsg(MSG_ERROR,
250 		    gettext("tavor: Unable to write HCA Primary Firmware "
251 		    "Image data (%d of %d bytes)\n"),
252 		    j, pchunks * manuf->sector_sz);
253 		free(raw_pfi);
254 		(void) tavor_close(flashdev);
255 		return (FWFLASH_FAILURE);
256 	} else {
257 		fprintf(stdout, " .");
258 	}
259 
260 	pchunks = ssz / manuf->sector_sz;
261 
262 	if ((ssz % manuf->sector_sz) != 0)
263 		pchunks++;
264 
265 	/*
266 	 * We allocate wholenum sectors, but only write out what we
267 	 * really need (ssz bytes)
268 	 */
269 	if ((raw_sfi = calloc(1, pchunks * manuf->sector_sz)) == NULL) {
270 		logmsg(MSG_ERROR,
271 		    gettext("tavor: Unable to allocate space for "
272 		    "device's Secondary Firmware Image\n"));
273 		free(raw_pfi);
274 		return (FWFLASH_FAILURE);
275 	}
276 	bzero(&tfi_data, sizeof (tavor_flash_ioctl_t));
277 	tfi_data.tf_type = TAVOR_FLASH_READ_SECTOR;
278 
279 	/* get our starting sector number */
280 	j = sfia / manuf->sector_sz;
281 
282 	for (offset = 0; offset < ssz; offset += manuf->sector_sz) {
283 		tfi_data.tf_sector_num = j;
284 		tfi_data.tf_sector = (caddr_t)&raw_sfi[offset];
285 		if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ,
286 		    &tfi_data)) < 0) {
287 			logmsg(MSG_ERROR,
288 			    gettext("tavor: Unable to read sector %d of "
289 			    "HCA Secondary Firmware Image\n"), j);
290 			(void) tavor_close(flashdev);
291 			free(raw_pfi);
292 			free(raw_sfi);
293 			return (FWFLASH_FAILURE);
294 		}
295 		++j;
296 	}
297 
298 	/*
299 	 * It appears that the tavor driver is returning a signed
300 	 * -1 (0xffff) in unassigned quadlets if we read a sector
301 	 * that isn't full, so for backwards compatibility with
302 	 * earlier fwflash versions, we need to zero out what
303 	 * remains in the sector.
304 	 */
305 	bzero(&raw_sfi[ssz], (pchunks * manuf->sector_sz) - ssz);
306 
307 #if defined(_LITTLE_ENDIAN)
308 	ptr = (uint32_t *)(uintptr_t)raw_sfi;
309 	for (j = 0; j < ssz / 4; j++) {
310 		ptr[j] = htonl(ptr[j]);
311 	}
312 #endif
313 
314 	/* only write out ssz bytes */
315 	if ((j = write(fd, raw_sfi, ssz)) != ssz) {
316 		logmsg(MSG_ERROR,
317 		    gettext("tavor: Unable to write HCA Secondary Firmware "
318 		    "Image data (%d of %d bytes)\n"),
319 		    j, ssz);
320 		(void) tavor_close(flashdev);
321 		free(raw_pfi);
322 		free(raw_sfi);
323 		return (FWFLASH_FAILURE);
324 	} else {
325 		fprintf(stdout, " .\n");
326 	}
327 
328 	fprintf(stdout,
329 	    gettext("Done.\n"));
330 
331 	free(raw_pfi);
332 	free(raw_sfi);
333 	/*
334 	 * this should succeed, but we don't just blindly ignore
335 	 * the return code cos that would be obnoxious.
336 	 */
337 	return (tavor_close(flashdev));
338 }
339 
340 
341 /*
342  * If we're invoking fw_writefw, then flashdev is a valid,
343  * flashable device as determined by fw_identify().
344  *
345  * If verifier is null, then we haven't been called following a firmware
346  * image verification load operation.
347  */
348 int
349 fw_writefw(struct devicelist *flashdev)
350 {
351 
352 	int			rv;
353 	uint32_t 		j, sectsz, hpfia, hsfia;
354 	uint32_t		ipfia, isfia, ipfis, isfis;
355 	struct ib_encap_ident	*manuf;
356 	struct mlx_is		*iinv;
357 	struct mlx_xps		*ipps, *lpps;
358 	struct mlx_xps		*isps, *lsps;
359 	struct mlx_xfi		*ipfi, *isfi;
360 
361 	/*
362 	 * linv, lpps/lsps are from the HCA whereas
363 	 * iinv/ipps/isps are in the on-disk firmware image that
364 	 * we've read in to the verifier->fwimage field, and are
365 	 * about to do some hand-waving with.
366 	 */
367 
368 	/*
369 	 * From the Mellanox HCA Flash programming app note,
370 	 * start of ch4, page36:
371 	 * ===========================================================
372 	 * Failsafe firmware programming ensures that an HCA device
373 	 * can boot up in a functional mode even if the burn process
374 	 * was interrupted (because of a power failure, reboot, user
375 	 * interrupt, etc.). This can be implemented by burning the
376 	 * new image to a vacant region on the Flash, and erasing the
377 	 * old image only after the new image is successfully burnt.
378 	 * This method ensures that there is at least one valid firmware
379 	 * image on the Flash at all times. Thus, in case a firmware
380 	 * image programming process is aborted for any reason, the HCA
381 	 * will still be able to boot up properly using the valid image
382 	 * on the Flash.
383 	 * ...
384 	 *
385 	 * 4.1 Notes on Image Programming of HCA Flashes
386 	 * Following are some general notes regarding the Flash memory
387 	 * in the context of Mellanox HCA devices:
388 	 * > The Flash memory is divided into sectors, and each sector
389 	 *   must be erased prior to its programming.
390 	 * > The image to be burnt is byte packed and should be programmed
391 	 *   into the Flash byte by byte, preserving the byte order, starting
392 	 *   at offset zero. No amendments are needed for endianess.
393 	 * > It is recommended to program the Flash while the device is idle.
394 	 * ===========================================================
395 	 *
396 	 * The comment about endianness is particularly important for us
397 	 * since we operate on both big- and litte-endian hosts - it means
398 	 * we have to do some byte-swapping gymnastics
399 	 */
400 
401 	/*
402 	 * From the Mellanox HCA Flash programming app note,
403 	 * section 4.2.5 on page 41/42:
404 	 * ===========================================================
405 	 * 4.2.5 Failsafe Programming Example
406 	 * This section provides an example of a programming utility
407 	 * that performs a Failsafe firmware image update. The flow
408 	 * ensures that there is at least one valid firmware image on
409 	 * the Flash at all times. Thus, in case a firmware image pro-
410 	 * gramming process is aborted for any reason, the HCA will
411 	 * still be able to boot up properly using the valid image on
412 	 * the Flash. Any other flow that ensures the above is also
413 	 * considered a Failsafe firmware update.
414 	 *
415 	 * Update Flow:
416 	 * * Check the validity of the PPS and SPS:
417 	 * > If both PSs are valid, arbitrarily invalidate one of them
418 	 * > If both PSs are invalid, the image on flash is corrupted
419 	 *   and cannot be updated in a Failsafe way. The user must
420 	 *   burn a full image in a non-failsafe way.
421 	 *
422 	 * > If only the PPS is valid:
423 	 *   i.Burn the secondary image (erase each sector first)
424 	 *  ii.Burn the SPS with the correct image address (FIA field)
425 	 * iii.Invalidate the PPS
426 	 *
427 	 * > If only the SPS is valid:
428 	 *   i.Burn the primary image (erase each sector first)
429 	 *  ii.Burn the PPS with the correct image address (FIA field)
430 	 * iii.Invalidate the SPS
431 	 * ===========================================================
432 	 */
433 
434 	/*
435 	 * Other required tasks called from this function:
436 	 *
437 	 * * check for CISCO boot extensions in the current xPS, and
438 	 *   if found, set them in the new xPS
439 	 *
440 	 * * update the xPS CRC field
441 	 *
442 	 * _then_ you can setup the outbound transfer to the HCA flash.
443 	 */
444 
445 	/*
446 	 * VERY IMPORTANT NOTE:
447 	 * The above text from the app note programming guide v1.44 does
448 	 * NOT match reality. If you try to do exactly what the above
449 	 * text specifies then you'll wind up with a warm, brick-like
450 	 * HCA that if you're really lucky has booted up in maintenance
451 	 * mode for you to re-flash.
452 	 *
453 	 * What you need to do is follow the example of the previous
454 	 * (v1.2 etc) version from the ON gate - which is what happens
455 	 * in this file. Basically - don't erase prior to writing a new
456 	 * sector, and _read back_ each sector after writing it. Especially
457 	 * the pointer sectors. Otherwise you'll get a warm brick.
458 	 */
459 
460 	manuf =
461 	    (struct ib_encap_ident *)(uintptr_t)flashdev->ident->encap_ident;
462 	lpps = (struct mlx_xps *)(uintptr_t)manuf->pps;
463 	lsps = (struct mlx_xps *)(uintptr_t)manuf->sps;
464 	iinv = (struct mlx_is *)&verifier->fwimage[0];
465 	sectsz = 1 << MLXSWAPBITS16(iinv->log2sectsz + iinv->log2sectszp);
466 	ipps = (struct mlx_xps *)&verifier->fwimage[sectsz/4];
467 	isps = (struct mlx_xps *)&verifier->fwimage[sectsz/2];
468 
469 	/*
470 	 * If we get here, then the verifier has _already_ checked that
471 	 * the part number in the firmware image matches that in the HCA,
472 	 * so we only need this check if there's no hardware info available
473 	 * already after running through fw_identify().
474 	 */
475 	if (manuf->pn_len == 0) {
476 		int resp;
477 
478 		(void) printf("\nUnable to completely verify that this "
479 		    "firmware image\n\t(%s)\nis compatible with your "
480 		    "HCA\n\t%s\n",
481 		    verifier->imgfile, flashdev->access_devname);
482 		(void) printf("\n\tDo you really want to continue? (Y/N): ");
483 
484 		(void) fflush(stdin);
485 		resp = getchar();
486 		if (resp != 'Y' && resp != 'y') {
487 			(void) printf("\nNot proceeding with flash "
488 			    "operation of %s on %s\n",
489 			    verifier->imgfile, flashdev->access_devname);
490 			return (FWFLASH_FAILURE);
491 		}
492 	}
493 
494 	/* stash these for later */
495 	hpfia = MLXSWAPBITS32(lpps->fia);
496 	hsfia = MLXSWAPBITS32(lsps->fia);
497 
498 	/* where does the on-disk image think everything is at? */
499 	ipfia = MLXSWAPBITS32(ipps->fia);
500 	isfia = MLXSWAPBITS32(isps->fia);
501 	ipfis = MLXSWAPBITS32(ipps->fis);
502 	isfis = MLXSWAPBITS32(isps->fis);
503 
504 	logmsg(MSG_INFO, "tavor: hpfia 0x%0x hsfia 0x%0x "
505 	    "ipfia 0x%0x isfia 0x%0x ipfis 0x%0x isfis 0x%0x\n",
506 	    hpfia, hsfia, ipfia, isfia, ipfis, isfis);
507 
508 	if ((ipfis + isfis) > manuf->device_sz) {
509 		/*
510 		 * This is bad - don't flash an image which is larger
511 		 * than the size of the HCA's flash
512 		 */
513 		logmsg(MSG_ERROR,
514 		    gettext("tavor: on-disk firmware image size (0x%lx bytes) "
515 		    "exceeds HCA's flash memory size (0x%lx bytes)!\n"),
516 		    ipfis + isfis, manuf->device_sz);
517 		logmsg(MSG_ERROR,
518 		    gettext("tavor: not flashing this image (%s)\n"),
519 		    verifier->imgfile);
520 		return (FWFLASH_FAILURE);
521 	}
522 
523 	/*
524 	 * The Mellanox HCA Flash app programming note does _not_
525 	 * specify that you have to insert the HCA's guid section
526 	 * into the flash image before burning it.
527 	 *
528 	 * HOWEVER it was determined during testing that this is
529 	 * actually required (otherwise your HCA's GUIDs revert to
530 	 * the manufacturer's defaults, ugh!), so we'll do it too.
531 	 */
532 
533 	ipfi = (struct mlx_xfi *)&verifier->fwimage[ipfia/4];
534 	isfi = (struct mlx_xfi *)&verifier->fwimage[isfia/4];
535 
536 	/*
537 	 * Here we check against our stored, properly-bitwise-munged copy
538 	 * of the HCA's GUIDS. If they're not set to default AND the OUI
539 	 * is MLX_OUI, then they're ok so we copy the HCA's version into
540 	 * our in-memory copy and blat it. If the GUIDs don't match this
541 	 * condition, then we use the default GUIDs which are in the on-disk
542 	 * firmware image instead.
543 	 */
544 	if (((manuf->ibguids[0] != MLX_DEFAULT_NODE_GUID) &&
545 	    (manuf->ibguids[1] != MLX_DEFAULT_P1_GUID) &&
546 	    (manuf->ibguids[2] != MLX_DEFAULT_P2_GUID) &&
547 	    (manuf->ibguids[3] != MLX_DEFAULT_SYSIMG_GUID)) &&
548 	    ((((manuf->ibguids[0] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) ||
549 	    (((manuf->ibguids[1] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) ||
550 	    (((manuf->ibguids[2] & HIGHBITS64) >> OUISHIFT) == MLX_OUI) ||
551 	    (((manuf->ibguids[3] & HIGHBITS64) >> OUISHIFT) == MLX_OUI))) {
552 		/* The GUIDs are ok, blat them into the in-memory image */
553 		j = ((ipfia + MLXSWAPBITS32(ipfi->nguidptr)) / 4) - 4;
554 		bcopy(manuf->pri_guid_section, &verifier->fwimage[j],
555 		    sizeof (struct mlx_guid_sect));
556 		j = ((isfia + MLXSWAPBITS32(isfi->nguidptr)) / 4) - 4;
557 		bcopy(manuf->sec_guid_section, &verifier->fwimage[j],
558 		    sizeof (struct mlx_guid_sect));
559 	} else {
560 		/*
561 		 * The GUIDs are hosed, we'll have to use
562 		 * the vendor defaults in the image instead
563 		 */
564 		logmsg(MSG_ERROR,
565 		    gettext("tavor: HCA's GUID section is set to defaults or "
566 		    " is invalid, using firmware image manufacturer's "
567 		    "default GUID section instead\n"));
568 	}
569 
570 	/* Just in case somebody is booting from this card... */
571 	tavor_cisco_extensions(lpps, ipps);
572 	tavor_cisco_extensions(lsps, isps);
573 
574 	/* first we write the secondary image and SPS, then the primary */
575 	rv = tavor_blast_image(manuf->fd, 2, hsfia, manuf->sector_sz, isps);
576 	if (rv != FWFLASH_SUCCESS) {
577 		logmsg(MSG_INFO,
578 		    "tavor: failed to update #2 firmware image\n");
579 		(void) tavor_close(flashdev);
580 		return (FWFLASH_FAILURE);
581 	}
582 
583 	rv = tavor_blast_image(manuf->fd, 1, hpfia, manuf->sector_sz, ipps);
584 	if (rv != FWFLASH_SUCCESS) {
585 		logmsg(MSG_INFO,
586 		    "tavor: failed to update #1 firmware image\n");
587 		(void) tavor_close(flashdev);
588 		return (FWFLASH_FAILURE);
589 	}
590 
591 	/* final update marker to the user */
592 	(void) printf(" +\n");
593 	return (tavor_close(flashdev));
594 }
595 
596 
597 /*
598  * The fw_identify() function walks the device
599  * tree trying to find devices which this plugin
600  * can work with.
601  *
602  * The parameter "start" gives us the starting index number
603  * to give the device when we add it to the fw_devices list.
604  *
605  * firstdev is allocated by us and we add space as necessary
606  *
607  */
608 int
609 fw_identify(int start)
610 {
611 	int rv = FWFLASH_FAILURE;
612 	di_node_t thisnode;
613 	struct devicelist *newdev;
614 	char *devpath;
615 	int idx = start;
616 	int devlength = 0;
617 
618 	thisnode = di_drv_first_node(drivername, rootnode);
619 
620 	if (thisnode == DI_NODE_NIL) {
621 		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
622 		    drivername);
623 		return (rv);
624 	}
625 
626 	/* we've found one, at least */
627 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
628 
629 		devpath = di_devfs_path(thisnode);
630 
631 		if ((newdev = calloc(1, sizeof (struct devicelist)))
632 		    == NULL) {
633 			logmsg(MSG_ERROR,
634 			    gettext("%s identification function unable "
635 			    "to allocate space for device entry\n"));
636 			di_devfs_path_free(devpath);
637 			return (rv);
638 		}
639 
640 		/* calloc enough for /devices + devpath + ":devctl" + '\0' */
641 		devlength = strlen(devpath) + strlen(devprefix) +
642 		    strlen(devsuffix) + 2;
643 
644 		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
645 			logmsg(MSG_ERROR, gettext("Unable to calloc space "
646 			    "for a devfs name\n"));
647 			di_devfs_path_free(devpath);
648 			(void) free(newdev);
649 			return (FWFLASH_FAILURE);
650 		}
651 		snprintf(newdev->access_devname, devlength,
652 		    "%s%s%s", devprefix, devpath, devsuffix);
653 
654 		/* CHECK VARIOUS IB THINGS HERE */
655 
656 		if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
657 			logmsg(MSG_ERROR,
658 			    gettext("tavor: Unable to allocate space for a "
659 			    "device identification record\n"));
660 			(void) free(newdev->access_devname);
661 			(void) free(newdev);
662 			di_devfs_path_free(devpath);
663 			return (FWFLASH_FAILURE);
664 		}
665 
666 		rv = tavor_identify(newdev);
667 		if (rv == FWFLASH_FAILURE) {
668 			(void) free(newdev->ident);
669 			(void) free(newdev->access_devname);
670 			(void) free(newdev);
671 			di_devfs_path_free(devpath);
672 			continue;
673 		}
674 
675 		if ((newdev->drvname = calloc(1, strlen(drivername) + 1))
676 		    == NULL) {
677 			logmsg(MSG_ERROR, gettext("Unable to allocate space "
678 			    "for a driver name\n"));
679 			(void) free(newdev->ident);
680 			(void) free(newdev->access_devname);
681 			(void) free(newdev);
682 			di_devfs_path_free(devpath);
683 			return (FWFLASH_FAILURE);
684 		}
685 
686 		(void) strlcpy(newdev->drvname, drivername,
687 		    strlen(drivername) + 1);
688 
689 		/* this next bit is backwards compatibility - "IB\0" */
690 		if ((newdev->classname = calloc(1, 3)) == NULL) {
691 			logmsg(MSG_ERROR, gettext("Unable to allocate space "
692 			    "for a class name\n"));
693 			(void) free(newdev->drvname);
694 			(void) free(newdev->ident);
695 			(void) free(newdev->access_devname);
696 			(void) free(newdev);
697 			di_devfs_path_free(devpath);
698 			return (FWFLASH_FAILURE);
699 		}
700 		(void) strlcpy(newdev->classname, "IB", 3);
701 
702 		newdev->index = idx;
703 		++idx;
704 		newdev->plugin = self;
705 
706 		di_devfs_path_free(devpath);
707 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
708 	}
709 
710 	if (fwflash_debug != 0) {
711 		struct devicelist *tempdev;
712 
713 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
714 			logmsg(MSG_INFO, "ib:fw_identify:\n");
715 			logmsg(MSG_INFO, "\ttempdev @ 0x%lx\n"
716 			    "\t\taccess_devname: %s\n"
717 			    "\t\tdrvname: %s\tclassname: %s\n"
718 			    "\t\tident->vid:   %s\n"
719 			    "\t\tident->pid:   %s\n"
720 			    "\t\tident->revid: %s\n"
721 			    "\t\tindex: %d\n"
722 			    "\t\tguid0: %s\n"
723 			    "\t\tguid1: %s\n"
724 			    "\t\tguid2: %s\n"
725 			    "\t\tguid3: %s\n"
726 			    "\t\tplugin @ 0x%lx\n\n",
727 			    &tempdev,
728 			    tempdev->access_devname,
729 			    tempdev->drvname, newdev->classname,
730 			    tempdev->ident->vid,
731 			    tempdev->ident->pid,
732 			    tempdev->ident->revid,
733 			    tempdev->index,
734 			    tempdev->addresses[0],
735 			    tempdev->addresses[1],
736 			    tempdev->addresses[2],
737 			    tempdev->addresses[3],
738 			    tempdev->plugin);
739 		}
740 	}
741 
742 	return (FWFLASH_SUCCESS);
743 }
744 
745 
746 
747 int
748 fw_devinfo(struct devicelist *thisdev)
749 {
750 
751 	struct ib_encap_ident	*encap;
752 
753 
754 	encap = (struct ib_encap_ident *)thisdev->ident->encap_ident;
755 
756 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
757 	    thisdev->index, thisdev->access_devname, thisdev->classname);
758 
759 	fprintf(stdout, "\t");
760 
761 	/* Mellanox HCA Flash app note, p40, #4.2.3 table 9 */
762 	fprintf(stdout,
763 	    gettext("GUID: System Image - %s\n"),
764 	    thisdev->addresses[3]);
765 	fprintf(stdout,
766 	    gettext("\t\tNode Image - %s\n"),
767 	    thisdev->addresses[0]);
768 	fprintf(stdout,
769 	    gettext("\t\tPort 1\t   - %s\n"),
770 	    thisdev->addresses[1]);
771 	fprintf(stdout,
772 	    gettext("\t\tPort 2\t   - %s\n"),
773 	    thisdev->addresses[2]);
774 
775 	if (encap->pn_len != 0) {
776 		fprintf(stdout,
777 		    gettext("\tFirmware revision : %s\n"
778 		    "\tProduct\t\t: %s\n"
779 		    "\tPSID\t\t: %s\n"),
780 		    thisdev->ident->revid,
781 		    encap->info.mlx_pn,
782 		    encap->info.mlx_psid);
783 		} else {
784 		fprintf(stdout,
785 		    gettext("\tFirmware revision : %s\n"
786 		    "\tNo hardware information available for this "
787 		    "device\n"), thisdev->ident->revid);
788 	}
789 	fprintf(stdout, "\n\n");
790 
791 	return (tavor_close(thisdev));
792 }
793 
794 
795 /*
796  * Helper functions lurk beneath this point
797  */
798 
799 
800 /*
801  * tavor_identify performs the following actions:
802  *
803  *	allocates and assigns thisdev->vpr
804  *
805  *	allocates space for the 4 GUIDs which each IB device must have
806  *	queries the tavor driver for this device's GUIDs
807  *
808  *	determines the hardware vendor, so that thisdev->vpr->vid
809  *	can be set correctly
810  */
811 static int
812 tavor_identify(struct devicelist *thisdev)
813 {
814 	int rv = FWFLASH_SUCCESS;
815 	int fd, ret, i;
816 
817 	tavor_flash_init_ioctl_t	init_ioctl;
818 	tavor_flash_ioctl_t		info;
819 	struct ib_encap_ident		*manuf;
820 	cfi_t				cfi;
821 	char temppsid[17];
822 	char rawpsid[16];
823 
824 #if defined(_LITTLE_ENDIAN)
825 	uint32_t			*ptr;
826 #endif
827 
828 	/* open the device */
829 	/* hook thisdev->ident->encap_ident to ib_encap_ident */
830 	/* check that all the bits are sane */
831 	/* return success, if warranted */
832 
833 	errno = 0;
834 	if ((fd = open(thisdev->access_devname, O_RDONLY)) < 0) {
835 		logmsg(MSG_INFO,
836 		    gettext("tavor: Unable to open a %s-attached "
837 		    "device node: %s: %s\n"), drivername,
838 		    thisdev->access_devname, strerror(errno));
839 		return (FWFLASH_FAILURE);
840 	}
841 
842 	if ((manuf = calloc(1, sizeof (ib_encap_ident_t))) == NULL) {
843 		logmsg(MSG_ERROR,
844 		    gettext("tavor: Unable to calloc space for a "
845 		    "%s-attached handle structure\n"),
846 		    drivername);
847 		return (FWFLASH_FAILURE);
848 	}
849 	manuf->magic = FWFLASH_IB_MAGIC_NUMBER;
850 	manuf->state = FWFLASH_IB_STATE_NONE;
851 	manuf->fd = fd;
852 
853 	thisdev->ident->encap_ident = manuf;
854 
855 	/*
856 	 * Inform driver that this command supports the Intel Extended
857 	 * CFI command set.
858 	 */
859 	cfi.cfi_char[0x10] = 'M';
860 	cfi.cfi_char[0x11] = 'X';
861 	cfi.cfi_char[0x12] = '2';
862 	init_ioctl.tf_cfi_info[0x4] = MLXSWAPBITS32(cfi.cfi_int[0x4]);
863 
864 	errno = 0;
865 	ret = ioctl(fd, TAVOR_IOCTL_FLASH_INIT, &init_ioctl);
866 	if (ret < 0) {
867 		logmsg(MSG_ERROR,
868 		    gettext("ib: TAVOR_IOCTL_FLASH_INIT failed: %s\n"),
869 		    strerror(errno));
870 		free(manuf);
871 		close(fd);
872 		return (FWFLASH_FAILURE);
873 	}
874 
875 	manuf->hwrev = init_ioctl.tf_hwrev;
876 
877 	/*
878 	 * Determine whether the attached driver supports the Intel or
879 	 * AMD Extended CFI command sets. If it doesn't support either,
880 	 * then we're hosed, so error out.
881 	 */
882 	for (i = 0; i < TAVOR_FLASH_CFI_SIZE_QUADLET; i++) {
883 		cfi.cfi_int[i] = MLXSWAPBITS32(init_ioctl.tf_cfi_info[i]);
884 	}
885 	manuf->cmd_set = cfi.cfi_char[0x13];
886 
887 	if (cfi.cfi_char[0x10] == 'Q' &&
888 	    cfi.cfi_char[0x11] == 'R' &&
889 	    cfi.cfi_char[0x12] == 'Y') {
890 		/* make sure the cmd set is AMD */
891 		if (manuf->cmd_set != TAVOR_FLASH_AMD_CMDSET) {
892 			logmsg(MSG_ERROR,
893 			    gettext("tavor: Unsupported flash device "
894 			    "command set\n"));
895 			free(manuf);
896 			close(fd);
897 			return (FWFLASH_FAILURE);
898 		}
899 		/* set some defaults */
900 		manuf->device_sz = TAVOR_FLASH_DEVICE_SZ_DEFAULT;
901 	} else {
902 		if (manuf->cmd_set != TAVOR_FLASH_AMD_CMDSET &&
903 		    manuf->cmd_set != TAVOR_FLASH_INTEL_CMDSET) {
904 			logmsg(MSG_ERROR,
905 			    gettext("ib: Unknown flash device command set\n"));
906 			free(manuf);
907 			close(fd);
908 			return (FWFLASH_FAILURE);
909 		}
910 		/* read from the CFI data */
911 		manuf->sector_sz = ((cfi.cfi_char[0x30] << 8) |
912 		    cfi.cfi_char[0x2F]) << 8;
913 		manuf->device_sz = 0x1 << cfi.cfi_char[0x27];
914 	}
915 
916 	logmsg(MSG_INFO, "sector_sz: 0x%08x\ndevice_sz: 0x%08x\n",
917 	    manuf->sector_sz, manuf->device_sz);
918 
919 	manuf->state |= FWFLASH_IB_STATE_MMAP;
920 
921 	/* set firmware revision */
922 	manuf->fw_rev.major = init_ioctl.tf_fwrev.tfi_maj;
923 	manuf->fw_rev.minor = init_ioctl.tf_fwrev.tfi_min;
924 	manuf->fw_rev.subminor = init_ioctl.tf_fwrev.tfi_sub;
925 
926 	if (((thisdev->ident->vid = calloc(1, MLX_VPR_VIDLEN + 1)) == NULL) ||
927 	    ((thisdev->ident->revid = calloc(1, MLX_VPR_REVLEN + 1)) == NULL)) {
928 
929 		logmsg(MSG_ERROR,
930 		    gettext("ib: Unable to allocate space for a VPR "
931 		    "record.\n"));
932 		free(thisdev->ident);
933 		free(manuf->info.mlx_pn);
934 		free(manuf->info.mlx_psid);
935 		free(manuf->info.mlx_id);
936 		free(manuf);
937 		close(fd);
938 		return (FWFLASH_FAILURE);
939 	}
940 	(void) strlcpy(thisdev->ident->vid, "MELLANOX", MLX_VPR_VIDLEN);
941 	/*
942 	 * We actually want the hwrev field from the ioctl above.
943 	 * Until we find out otherwise, add it onto the end of the
944 	 * firmware version details.
945 	 */
946 
947 	snprintf(thisdev->ident->revid, MLX_VPR_REVLEN, "%d.%d.%04d",
948 	    manuf->fw_rev.major, manuf->fw_rev.minor,
949 	    manuf->fw_rev.subminor);
950 
951 	bzero(manuf->ibguids, sizeof (manuf->ibguids));
952 
953 	/*
954 	 * For convenience we read in the Invariant Sector as
955 	 * well as both the Primary and Secondary Pointer Sectors
956 	 */
957 
958 	if ((manuf->inv = calloc(1, manuf->sector_sz)) == NULL) {
959 		logmsg(MSG_ERROR,
960 		    gettext("tavor: Unable to allocate space for storing "
961 		    "the HCA's Invariant Sector\n"));
962 		return (FWFLASH_FAILURE);
963 	}
964 	bzero(&info, sizeof (tavor_flash_ioctl_t));
965 
966 	info.tf_type = TAVOR_FLASH_READ_SECTOR;
967 	info.tf_sector = (caddr_t)manuf->inv;
968 	info.tf_sector_num = 0;
969 
970 	errno = 0;
971 
972 	if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info))
973 	    < 0) {
974 		logmsg(MSG_ERROR,
975 		    gettext("tavor: Unable to read HCA Invariant Sector\n"));
976 		return (FWFLASH_FAILURE);
977 	}
978 
979 #if defined(_LITTLE_ENDIAN)
980 	ptr = (uint32_t *)(uintptr_t)manuf->inv;
981 	for (i = 0; i < (manuf->sector_sz / 4); i++) {
982 		ptr[i] = htonl(ptr[i]);
983 	}
984 #endif
985 
986 	if ((manuf->pps = calloc(1, manuf->sector_sz)) == NULL) {
987 		logmsg(MSG_ERROR,
988 		    gettext("tavor: Unable to allocate space for storing "
989 		    "the HCA's Primary Pointer Sector\n"));
990 		return (FWFLASH_FAILURE);
991 	}
992 	bzero(&info, sizeof (tavor_flash_ioctl_t));
993 
994 	info.tf_type = TAVOR_FLASH_READ_SECTOR;
995 	info.tf_sector = (caddr_t)manuf->pps;
996 	info.tf_sector_num = 1;
997 
998 	errno = 0;
999 
1000 	if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info))
1001 	    < 0) {
1002 		logmsg(MSG_ERROR,
1003 		    gettext("tavor: Unable to read HCA Primary "
1004 		    "Pointer Sector\n"));
1005 		return (FWFLASH_FAILURE);
1006 	}
1007 
1008 #if defined(_LITTLE_ENDIAN)
1009 	ptr = (uint32_t *)(uintptr_t)manuf->pps;
1010 	for (i = 0; i < (manuf->sector_sz / 4); i++) {
1011 		ptr[i] = htonl(ptr[i]);
1012 	}
1013 #endif
1014 
1015 	if ((manuf->sps = calloc(1, manuf->sector_sz)) == NULL) {
1016 		logmsg(MSG_ERROR,
1017 		    gettext("tavor: Unable to allocate space for storing "
1018 		    "the HCA's Secondary Pointer Sector\n"));
1019 		return (FWFLASH_FAILURE);
1020 	}
1021 	bzero(&info, sizeof (tavor_flash_ioctl_t));
1022 
1023 	info.tf_type = TAVOR_FLASH_READ_SECTOR;
1024 	info.tf_sector = (caddr_t)manuf->sps;
1025 	info.tf_sector_num = 2;
1026 
1027 	errno = 0;
1028 
1029 	if ((rv = ioctl(manuf->fd, TAVOR_IOCTL_FLASH_READ, &info))
1030 	    < 0) {
1031 		logmsg(MSG_ERROR,
1032 		    gettext("tavor: Unable to read HCA Secondary "
1033 		    "Pointer Sector\n"));
1034 		return (FWFLASH_FAILURE);
1035 	}
1036 
1037 #if defined(_LITTLE_ENDIAN)
1038 	ptr = (uint32_t *)(uintptr_t)manuf->sps;
1039 	for (i = 0; i < (manuf->sector_sz / 4); i++) {
1040 		ptr[i] = htonl(ptr[i]);
1041 	}
1042 #endif
1043 
1044 	if ((ret = tavor_get_guids(manuf)) != FWFLASH_SUCCESS) {
1045 		logmsg(MSG_INFO,
1046 		    gettext("ib: No guids found for device %s!\n"),
1047 		    thisdev->access_devname);
1048 	}
1049 
1050 	/* set hw part number, psid, and name in handle */
1051 	bzero(temppsid, 17);
1052 	bcopy(manuf->pps+FLASH_PS_PSID_OFFSET, &rawpsid, 16);
1053 
1054 	for (i = 0; i < 16; i += 4) {
1055 		temppsid[i]   = rawpsid[i+3];
1056 		temppsid[i+1] = rawpsid[i+2];
1057 		temppsid[i+2] = rawpsid[i+1];
1058 		temppsid[i+3] = rawpsid[i];
1059 	}
1060 	logmsg(MSG_INFO,
1061 	    "tavor: have raw '%s', want munged '%s'\n",
1062 	    rawpsid, temppsid);
1063 
1064 	/* now walk the magic decoder ring table */
1065 	manuf->info.mlx_pn = NULL;
1066 	manuf->info.mlx_psid = NULL;
1067 	manuf->info.mlx_id = NULL;
1068 	manuf->pn_len = 0;
1069 
1070 	for (i = 0; i < MLX_MAX_ID; i++) {
1071 		if ((strncmp(temppsid, mlx_mdr[i].mlx_psid,
1072 		    MLX_PSID_SZ)) == 0) {
1073 			/* matched */
1074 			if ((manuf->info.mlx_pn = calloc(1,
1075 			    strlen(mlx_mdr[i].mlx_pn) + 1)) == NULL) {
1076 				logmsg(MSG_INFO,
1077 				    "tavor: no space available for the "
1078 				    "HCA PSID record (1)\n");
1079 			} else {
1080 				(void) strlcpy(manuf->info.mlx_pn,
1081 				    mlx_mdr[i].mlx_pn,
1082 				    strlen(mlx_mdr[i].mlx_pn) + 1);
1083 				manuf->pn_len = strlen(mlx_mdr[i].mlx_pn);
1084 			}
1085 
1086 			if ((manuf->info.mlx_psid = calloc(1,
1087 			    strlen(mlx_mdr[i].mlx_psid) + 1)) == NULL) {
1088 				logmsg(MSG_INFO,
1089 				    "tavor: no space available for the "
1090 				    "HCA PSID record (2)\n");
1091 			} else {
1092 				(void) strlcpy(manuf->info.mlx_psid,
1093 				    mlx_mdr[i].mlx_psid,
1094 				    strlen(mlx_mdr[i].mlx_psid) + 1);
1095 			}
1096 			if ((manuf->info.mlx_id = calloc(1,
1097 			    strlen(mlx_mdr[i].mlx_id) + 1)) == NULL) {
1098 				logmsg(MSG_INFO,
1099 				    "tavor: no space available for the "
1100 				    "HCA PSID record (3)\n");
1101 			} else {
1102 				(void) strlcpy(manuf->info.mlx_id,
1103 				    mlx_mdr[i].mlx_id,
1104 				    strlen(mlx_mdr[i].mlx_id) + 1);
1105 			}
1106 		}
1107 	}
1108 	if ((manuf->pn_len == 0) || (i == MLX_MAX_ID)) {
1109 		logmsg(MSG_INFO,
1110 		    "tavor: No hardware part number information available "
1111 		    "for this HCA\n");
1112 		/* Until we deliver the arbel driver, it's all Mellanox */
1113 		i = strlen("No hardware information available for this device");
1114 
1115 		thisdev->ident->pid = calloc(1, i + 2);
1116 		sprintf(thisdev->ident->pid, "No hardware information "
1117 		    "available for this device");
1118 	} else {
1119 		if ((thisdev->ident->pid = calloc(1,
1120 		    strlen(manuf->info.mlx_psid) + 1)) != NULL) {
1121 			(void) strlcpy(thisdev->ident->pid,
1122 			    manuf->info.mlx_psid,
1123 			    strlen(manuf->info.mlx_psid) + 1);
1124 		} else {
1125 			logmsg(MSG_ERROR,
1126 			    gettext("ib: Unable to allocate space for a "
1127 			    "hardware identifier\n"));
1128 			free(thisdev->ident);
1129 			free(manuf->info.mlx_pn);
1130 			free(manuf->info.mlx_psid);
1131 			free(manuf->info.mlx_id);
1132 			free(manuf);
1133 			close(fd);
1134 			return (FWFLASH_FAILURE);
1135 		}
1136 	}
1137 
1138 	for (i = 0; i < 4; i++) {
1139 		if ((thisdev->addresses[i] = calloc(1,
1140 		    (2 * sizeof (uint64_t)) + 1)) == NULL) {
1141 			logmsg(MSG_ERROR,
1142 			    gettext("tavor: Unable to allocate space for a "
1143 			    "human-readable HCA guid\n"));
1144 			return (FWFLASH_FAILURE);
1145 		}
1146 		(void) sprintf(thisdev->addresses[i], "%016llx",
1147 		    manuf->ibguids[i]);
1148 	}
1149 
1150 	/*
1151 	 * We do NOT close the fd here, since we can close it
1152 	 * at the end of the fw_readfw() or fw_writefw() functions
1153 	 * instead and not get the poor dear confused about whether
1154 	 * it's been inited already.
1155 	 */
1156 
1157 	return (rv);
1158 }
1159 
1160 /*ARGSUSED*/
1161 static int
1162 tavor_get_guids(struct ib_encap_ident *handle)
1163 {
1164 	int 			rv, j;
1165 	uint32_t		i = 0x00;
1166 	tavor_flash_ioctl_t	info;
1167 	struct mlx_guid_sect	*p, *s;
1168 
1169 #if defined(_LITTLE_ENDIAN)
1170 	uint32_t		*ptr, tmp;
1171 #endif
1172 
1173 	/*
1174 	 * The reference for this function is the
1175 	 *	Mellanox HCA Flash Programming Application Note
1176 	 * rev 1.44, 2007. Chapter 4 in particular.
1177 	 *
1178 	 * NOTE: this Mellanox document is labelled Confidential
1179 	 * so DO NOT move this file out of usr/closed without
1180 	 * explicit approval from Sun Legal.
1181 	 */
1182 
1183 	/*
1184 	 * We need to check for both the Primary and Secondary
1185 	 * Image GUIDs. handle->pps and handle->sps should be
1186 	 * non-NULL by the time we're called, since we depend
1187 	 * on them being stashed in handle. Saves on an ioctl().
1188 	 */
1189 
1190 	/* make sure we've got our fallback position organised */
1191 	for (i = 0; i < 4; i++) {
1192 		handle->ibguids[i] = 0x00000000;
1193 	}
1194 
1195 	/* convenience .... */
1196 
1197 	if ((p = calloc(1, sizeof (mlx_guid_sect_t))) == NULL) {
1198 		logmsg(MSG_ERROR,
1199 		    gettext("tavor: Unable to allocate space for "
1200 		    "HCA guid record (1)\n"));
1201 		return (FWFLASH_FAILURE);
1202 	}
1203 	if ((s = calloc(1, sizeof (mlx_guid_sect_t))) == NULL) {
1204 		logmsg(MSG_ERROR,
1205 		    gettext("tavor: Unable to allocate space for "
1206 		    "HCA guid record (2)\n"));
1207 		free(p);
1208 		return (FWFLASH_FAILURE);
1209 	}
1210 
1211 	bcopy(&handle->pps[0], &i, 4);
1212 	handle->pfi_guid_addr = MLXSWAPBITS32(i) + FLASH_GUID_PTR;
1213 	bcopy(&handle->sps[0], &i, 4);
1214 	handle->sfi_guid_addr = MLXSWAPBITS32(i) + FLASH_GUID_PTR;
1215 
1216 	bzero(&info, sizeof (tavor_flash_ioctl_t));
1217 	info.tf_type = TAVOR_FLASH_READ_QUADLET;
1218 	info.tf_addr = handle->pfi_guid_addr;
1219 
1220 	errno = 0;
1221 
1222 	rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ, &info);
1223 	if (rv < 0) {
1224 		logmsg(MSG_ERROR,
1225 		    gettext("tavor: Unable to read Primary Image "
1226 		    "guid offset\n"));
1227 		free(p);
1228 		free(s);
1229 		return (FWFLASH_FAILURE);
1230 	}
1231 
1232 	/*
1233 	 * This is because we want the whole of the section
1234 	 * including the 16 reserved bytes at the front so
1235 	 * that if we recalculate the CRC we've got the correct
1236 	 * data to do it with
1237 	 */
1238 	info.tf_addr = handle->pfi_guid_addr + info.tf_quadlet
1239 	    - FLASH_GUID_PTR - 16;
1240 
1241 	bzero(handle->pri_guid_section, sizeof (mlx_guid_sect_t));
1242 
1243 	for (j = 0; j < 13; j++) {
1244 		errno = 0;
1245 		if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ,
1246 		    &info)) < 0) {
1247 			logmsg(MSG_ERROR,
1248 			    gettext("tavor: Unable to read Primary Image "
1249 			    "guid chunk %d\n"), j);
1250 		}
1251 		handle->pri_guid_section[j] = info.tf_quadlet;
1252 		info.tf_addr += 4;
1253 	}
1254 	bcopy(&handle->pri_guid_section, p, sizeof (struct mlx_guid_sect));
1255 
1256 	/* now grab the secondary guid set */
1257 	bzero(&info, sizeof (tavor_flash_ioctl_t));
1258 	info.tf_type = TAVOR_FLASH_READ_QUADLET;
1259 	info.tf_addr = handle->sfi_guid_addr;
1260 
1261 	errno = 0;
1262 
1263 	if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ,
1264 	    &info)) < 0) {
1265 		logmsg(MSG_ERROR,
1266 		    gettext("tavor: Unable to read Secondary Image "
1267 		    "guid offset (%s)\n"), strerror(errno));
1268 		free(p);
1269 		free(s);
1270 		return (FWFLASH_FAILURE);
1271 	}
1272 
1273 	info.tf_addr = handle->sfi_guid_addr + info.tf_quadlet
1274 	    - FLASH_GUID_PTR - 16;
1275 
1276 	bzero(handle->sec_guid_section, sizeof (mlx_guid_sect_t));
1277 
1278 	for (j = 0; j < 13; j++) {
1279 		errno = 0;
1280 		if ((rv = ioctl(handle->fd, TAVOR_IOCTL_FLASH_READ,
1281 		    &info)) < 0) {
1282 			logmsg(MSG_ERROR,
1283 			    gettext("tavor: Unable to read Secondary Image "
1284 			    "guid chunk %d (%s)\n"), j, strerror(errno));
1285 			return (FWFLASH_FAILURE);
1286 		}
1287 		handle->sec_guid_section[j] = info.tf_quadlet;
1288 		info.tf_addr += 4;
1289 	}
1290 
1291 	bcopy(&handle->sec_guid_section, s, sizeof (struct mlx_guid_sect));
1292 
1293 #if defined(_LITTLE_ENDIAN)
1294 
1295 	/*
1296 	 * We don't actually care about p or s later on if we
1297 	 * write to the HCA - we've already stored the binary
1298 	 * form in handle->pri_guid_section and handle->sec_guid_section.
1299 	 * What we're doing here is creating human-readable forms.
1300 	 */
1301 
1302 	ptr = (uint32_t *)(uintptr_t)p;
1303 	for (j = 0; j < 14; j += 2) {
1304 		tmp = ptr[j];
1305 		ptr[j] = ptr[j+1];
1306 		ptr[j+1] = tmp;
1307 	}
1308 
1309 	ptr = (uint32_t *)(uintptr_t)s;
1310 	for (j = 0; j < 14; j += 2) {
1311 		tmp = ptr[j];
1312 		ptr[j] = ptr[j+1];
1313 		ptr[j+1] = tmp;
1314 	}
1315 #endif
1316 
1317 	/*
1318 	 * We don't check and munge the GUIDs to the manufacturer's
1319 	 * defaults, because if the GUIDs are actually set incorrectly
1320 	 * at identify time, we really need to know that.
1321 	 *
1322 	 * If the GUIDs are bogus, then we'll fix that in fw_writefw()
1323 	 * by blatting the manufacturer's defaults from the firmware
1324 	 * image file instead.
1325 	 */
1326 	if ((p->nodeguid == s->nodeguid) &&
1327 	    (p->port1guid == s->port1guid) &&
1328 	    (p->port2guid == s->port2guid) &&
1329 	    (p->sysimguid == s->sysimguid)) {
1330 		logmsg(MSG_INFO,
1331 		    "tavor: primary and secondary guids are the same\n");
1332 		handle->ibguids[0] = p->nodeguid;
1333 		handle->ibguids[1] = p->port1guid;
1334 		handle->ibguids[2] = p->port2guid;
1335 		handle->ibguids[3] = p->sysimguid;
1336 	} else {
1337 		/*
1338 		 * We're going to assume that the guids which are numerically
1339 		 * larger than the others are correct and copy them to
1340 		 * handle->ibguids.
1341 		 *
1342 		 * For those in the know wrt InfiniBand, if this assumption
1343 		 * is incorrect, _please_ bug this and fix it, adding a
1344 		 * comment or two to indicate why
1345 		 */
1346 		logmsg(MSG_INFO,
1347 		    "tavor: primary and secondary guids don't all match\n");
1348 
1349 		if (s->nodeguid > p->nodeguid) {
1350 			handle->ibguids[0] = s->nodeguid;
1351 			handle->ibguids[1] = s->port1guid;
1352 			handle->ibguids[2] = s->port2guid;
1353 			handle->ibguids[3] = s->sysimguid;
1354 			bzero(p, sizeof (struct mlx_guid_sect));
1355 		} else {
1356 			handle->ibguids[0] = p->nodeguid;
1357 			handle->ibguids[1] = p->port1guid;
1358 			handle->ibguids[2] = p->port2guid;
1359 			handle->ibguids[3] = p->sysimguid;
1360 			bzero(s, sizeof (struct mlx_guid_sect));
1361 		}
1362 	}
1363 
1364 	free(p);
1365 	free(s);
1366 
1367 	if (fwflash_debug) {
1368 		for (i = 0; i < 4; i++) {
1369 			logmsg(MSG_INFO, "ibguids[%d] %0llx\n", i,
1370 			    handle->ibguids[i]);
1371 		}
1372 	}
1373 
1374 	return (FWFLASH_SUCCESS);
1375 }
1376 
1377 
1378 int
1379 tavor_close(struct devicelist *flashdev)
1380 {
1381 
1382 	struct ib_encap_ident *handle;
1383 
1384 	handle = (struct ib_encap_ident *)flashdev->ident->encap_ident;
1385 	if (handle->fd > 0) {
1386 		(void) ioctl(handle->fd, TAVOR_IOCTL_FLASH_FINI);
1387 		errno = 0;
1388 		if (close(handle->fd) != 0) {
1389 			logmsg(MSG_ERROR,
1390 			    gettext("tavor: Unable to properly close "
1391 			    "device %s! (%s)\n"),
1392 			    flashdev->access_devname,
1393 			    strerror(errno));
1394 			return (FWFLASH_FAILURE);
1395 		}
1396 		return (FWFLASH_SUCCESS);
1397 	} else
1398 		return (FWFLASH_FAILURE);
1399 }
1400 
1401 
1402 /*
1403  * We would not need this if it were not for Cisco's image using the
1404  * VSD to store boot options and flags for their PXE boot extension,
1405  * but not setting the proper default values for the extension in
1406  * their image.  As it turns out, some of the data for the extension
1407  * is stored in the VSD in the firmware file, and the rest is set by
1408  * their firmware utility.  That's not very nice for us, since it could
1409  * change at any time without our knowledge.  Well, for the time being,
1410  * we can use this to examine and fix up anything in the VSD that we might
1411  * need to handle, for any vendor specific settings.
1412  */
1413 static void
1414 tavor_cisco_extensions(mlx_xps_t *hcaxps, mlx_xps_t *diskxps)
1415 {
1416 	uint16_t sig1, sig2;
1417 	uint32_t i;
1418 
1419 
1420 	bcopy(hcaxps->vsdpsid, &i, 4);
1421 	sig1 = htonl(i);
1422 	bcopy(&hcaxps->vsdpsid[223], &i, 4);
1423 	sig2 = htonl(i);
1424 
1425 
1426 	if (sig1 == FLASH_VSD_CISCO_SIGNATURE &&
1427 	    sig2 == FLASH_VSD_CISCO_SIGNATURE) {
1428 		logmsg(MSG_INFO,
1429 		    "tavor: CISCO signature found in HCA's VSD, copying to "
1430 		    "new image's VSD\n");
1431 
1432 		i = htonl(FLASH_VSD_CISCO_SIGNATURE);
1433 		bcopy(&i, diskxps->vsdpsid, 2);
1434 
1435 		/*
1436 		 * Set the boot_version field to '2'. This value is
1437 		 * located in the 2nd byte of the last uint32_t.
1438 		 * Per the previous version of fwflash, we just or
1439 		 * the bit in and get on with it.
1440 		 */
1441 
1442 		i = (diskxps->vsdpsid[222] | FLASH_VSD_CISCO_BOOT_VERSION);
1443 		bcopy(&i, &diskxps->vsdpsid[222], 2);
1444 		/*
1445 		 * Now set some defaults for the SRP boot extension,
1446 		 * currently the only extension we support. These flags
1447 		 * are located in the second uint32_t of the VSD.
1448 		 */
1449 
1450 		logmsg(MSG_INFO, "tavor: CISCO boot flags currently set "
1451 		    "to 0x%08x\n",
1452 		    diskxps->vsdpsid[1]);
1453 
1454 		diskxps->vsdpsid[1] =
1455 		    htonl(diskxps->vsdpsid[1] |
1456 		    FLASH_VSD_CISCO_FLAG_AUTOUPGRADE |
1457 		    FLASH_VSD_CISCO_BOOT_OPTIONS |
1458 		    FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_1 |
1459 		    FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_PORT_2 |
1460 		    FLASH_VSD_CISCO_FLAG_BOOT_ENABLE_SCAN |
1461 		    FLASH_VSD_CISCO_FLAG_BOOT_TYPE_WELL_KNOWN |
1462 		    FLASH_VSD_CISCO_FLAG_BOOT_TRY_FOREVER);
1463 
1464 		logmsg(MSG_INFO, "tavor: CISCO boot flags now set "
1465 		    "to 0x%08x\n",
1466 		    diskxps->vsdpsid[1]);
1467 	} else
1468 		logmsg(MSG_INFO,
1469 		    "tavor: CISCO signature not found in HCA's VSD\n");
1470 }
1471 
1472 
1473 static int
1474 tavor_write_sector(int fd, int sectnum, int32_t *data)
1475 {
1476 	int rv, i;
1477 	tavor_flash_ioctl_t	cmd;
1478 
1479 
1480 	bzero(&cmd, sizeof (tavor_flash_ioctl_t));
1481 
1482 	cmd.tf_type = TAVOR_FLASH_WRITE_SECTOR;
1483 	cmd.tf_sector_num = sectnum;
1484 	cmd.tf_sector = (caddr_t)data;
1485 
1486 	errno = 0;
1487 
1488 	logmsg(MSG_INFO,
1489 	    "tavor: tavor_write_sector(fd %d, sectnum 0x%x, data 0x%lx)\n",
1490 	    fd, sectnum, data);
1491 	logmsg(MSG_INFO,
1492 	    "tavor:\n"
1493 	    "\tcmd.tf_type       %d\n"
1494 	    "\tcmd.tf_sector     0x%lx\n"
1495 	    "\tcmd.tf_sector_num %d\n",
1496 	    cmd.tf_type, data, cmd.tf_sector_num);
1497 
1498 	/*
1499 	 * If we're debugging, dump the first 64 uint32_t that we've
1500 	 * been passed
1501 	 */
1502 	if (fwflash_debug > 0) {
1503 		i = 0;
1504 		while (i < 64) {
1505 			logmsg(MSG_INFO,
1506 			    "%02x: %08x %08x %08x %08x\n",
1507 			    i, data[i], data[i+1],
1508 			    data[i+2], data[i+3]);
1509 			i += 4;
1510 		}
1511 	}
1512 
1513 	rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1514 	if (rv < 0) {
1515 		logmsg(MSG_ERROR,
1516 		    gettext("tavor: WRITE SECTOR failed for sector "
1517 		    "%d: %s\n"),
1518 		    sectnum, strerror(errno));
1519 		return (FWFLASH_FAILURE);
1520 	} else
1521 		return (FWFLASH_SUCCESS);
1522 }
1523 
1524 /*
1525  * Write zeros to the on-HCA signature and CRC16 fields of sector.
1526  *
1527  * NOTE we do _not_ divide start by 4 because we're talking to the
1528  * HCA, and not finding an offset into verifier->fwimage.
1529  */
1530 
1531 static int
1532 tavor_zero_sig_crc(int fd, uint32_t start)
1533 {
1534 	int 			i, rv;
1535 	tavor_flash_ioctl_t 	cmd;
1536 
1537 	/* signature first, then CRC16 */
1538 	bzero(&cmd, sizeof (tavor_flash_ioctl_t));
1539 	cmd.tf_type = TAVOR_FLASH_WRITE_BYTE;
1540 	cmd.tf_byte = 0x00;
1541 
1542 	logmsg(MSG_INFO,
1543 	    "tavor: tavor_zero_sig_crc(fd %d, start 0x%04x)\n",
1544 	    fd, start);
1545 
1546 	for (i = 0; i < 4; i++) {
1547 		cmd.tf_addr = start + FLASH_PS_SIGNATURE_OFFSET + i;
1548 
1549 		logmsg(MSG_INFO,
1550 		    "tavor: invalidating xPS sig (offset from IS 0x%04x) "
1551 		    "byte %d\n",
1552 		    cmd.tf_addr, i);
1553 		errno = 0;
1554 
1555 		rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1556 		if (rv < 0) {
1557 			logmsg(MSG_INFO,
1558 			    gettext("tavor: Unable to write 0x00 to "
1559 			    "offset 0x%04x from IS (sig byte %d): %s\n"),
1560 			    cmd.tf_addr, i, strerror(errno));
1561 			return (FWFLASH_FAILURE);
1562 		}
1563 	}
1564 
1565 	cmd.tf_byte = 0x00;
1566 	for (i = 0; i < 2; i++) {
1567 		cmd.tf_addr = start + FLASH_PS_CRC16_OFFSET + i;
1568 
1569 		logmsg(MSG_INFO,
1570 		    "tavor: invalidating xPS CRC16 (offset from IS 0x%04x) "
1571 		    "byte %d\n",
1572 		    cmd.tf_addr, i);
1573 		errno = 0;
1574 
1575 		rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1576 		if (rv < 0) {
1577 			logmsg(MSG_INFO,
1578 			    gettext("tavor: Unable to write 0x00 to "
1579 			    "offset 0x%04x from IS (CRC16 byte %d): %s\n"),
1580 			    cmd.tf_addr, i, strerror(errno));
1581 			return (FWFLASH_FAILURE);
1582 		}
1583 	}
1584 	return (FWFLASH_SUCCESS);
1585 }
1586 
1587 
1588 /*
1589  * Write a new FIA for the given xPS. The _caller_ handles
1590  * any required byte-swapping for us.
1591  *
1592  * NOTE we do _not_ divide start by 4 because we're talking to the
1593  * HCA, and not finding an offset into verifier->fwimage.
1594  */
1595 static int
1596 tavor_write_xps_fia(int fd, uint32_t offset, uint32_t start)
1597 {
1598 	int 			i, rv;
1599 	uint8_t			*addrbytep;
1600 	tavor_flash_ioctl_t 	cmd;
1601 
1602 	logmsg(MSG_INFO,
1603 	    "tavor: tavor_write_xps_fia(fd %d, offset 0x%04x, "
1604 	    "start 0x%04x)\n",
1605 	    fd, offset, start);
1606 
1607 	addrbytep = (uint8_t *)&start;
1608 
1609 	bzero(&cmd, sizeof (tavor_flash_ioctl_t));
1610 	cmd.tf_type = TAVOR_FLASH_WRITE_BYTE;
1611 	for (i = 0; i < 4; i++) {
1612 		cmd.tf_byte = addrbytep[i];
1613 		cmd.tf_addr = offset + FLASH_PS_FI_ADDR_OFFSET + i;
1614 		logmsg(MSG_INFO,
1615 		    "tavor: writing xPS' new FIA, byte %d (0x%0x) at "
1616 		    "offset from IS 0x%04x\n",
1617 		    i, cmd.tf_byte, cmd.tf_addr);
1618 		errno = 0;
1619 
1620 		rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1621 		if (rv < 0) {
1622 			logmsg(MSG_INFO,
1623 			    gettext("tavor: Unable to write byte %d "
1624 			    "of xPS new FIA (0x%0x, offset from IS "
1625 			    "0x%04x): %s\n"),
1626 			    i, cmd.tf_byte, cmd.tf_addr, strerror(errno));
1627 			return (FWFLASH_FAILURE);
1628 		}
1629 	}
1630 	return (FWFLASH_SUCCESS);
1631 }
1632 
1633 
1634 /*
1635  * Write the new CRC16 and Signature to the given xPS. The caller
1636  * has already byte-swapped newcrc if that's necessary.
1637  *
1638  * NOTE we do _not_ divide start by 4 because we're talking to the
1639  * HCA, and not finding an offset into verifier->fwimage.
1640  */
1641 static int
1642 tavor_write_xps_crc_sig(int fd, uint32_t offset, uint16_t newcrc)
1643 {
1644 	int 			i, rv;
1645 	uint8_t			*bytep;
1646 	uint32_t		tempsig;
1647 	tavor_flash_ioctl_t 	cmd;
1648 
1649 	logmsg(MSG_INFO,
1650 	    "tavor: tavor_write_xps_crc_sig(fd %d, offset 0x%04x, "
1651 	    "newcrc 0x%04x)\n",
1652 	    fd, offset, newcrc);
1653 
1654 	bytep = (uint8_t *)&newcrc;
1655 
1656 	bzero(&cmd, sizeof (tavor_flash_ioctl_t));
1657 	cmd.tf_type = TAVOR_FLASH_WRITE_BYTE;
1658 	for (i = 0; i < 2; i++) {
1659 		cmd.tf_byte = bytep[i];
1660 		cmd.tf_addr = offset + FLASH_PS_CRC16_OFFSET + i;
1661 		logmsg(MSG_INFO,
1662 		    "tavor: writing new XPS CRC16, byte %d (0x%0x) at "
1663 		    "offset from IS 0x%04x\n",
1664 		    i, bytep[i], cmd.tf_addr);
1665 		errno = 0;
1666 
1667 		rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1668 		if (rv < 0) {
1669 			logmsg(MSG_INFO,
1670 			    gettext("tavor: Unable to write byte %d "
1671 			    "(0x%0x) of xPS' new CRC16 to offset "
1672 			    "from IS 0x%04x: %s\n"),
1673 			    i, bytep[i], cmd.tf_addr, strerror(errno));
1674 			return (FWFLASH_FAILURE);
1675 		}
1676 	}
1677 
1678 	tempsig = htonl(FLASH_PS_SIGNATURE);
1679 	bytep = (uint8_t *)&tempsig;
1680 
1681 	for (i = 0; i < 4; i++) {
1682 		cmd.tf_byte = bytep[i];
1683 		cmd.tf_addr = offset + FLASH_PS_SIGNATURE_OFFSET + i;
1684 		logmsg(MSG_INFO,
1685 		    "tavor: writing new xPS Signature, byte %d (0x%0x) at "
1686 		    "offset from IS 0x%04x\n",
1687 		    i, bytep[i], cmd.tf_addr);
1688 		errno = 0;
1689 
1690 		rv = ioctl(fd, TAVOR_IOCTL_FLASH_WRITE, &cmd);
1691 		if (rv < 0) {
1692 			logmsg(MSG_INFO,
1693 			    gettext("tavor: Unable to write byte %d (0x%0x) "
1694 			    "of xPS' signature at offset from IS 0x%04x: %s\n"),
1695 			    i, bytep[i], cmd.tf_addr, strerror(errno));
1696 			return (FWFLASH_FAILURE);
1697 		}
1698 	}
1699 	return (FWFLASH_SUCCESS);
1700 }
1701 
1702 
1703 
1704 /*
1705  * This function contains "Begin/End documentation departure point"
1706  * because the reality of what actually _works_ is quite, quite
1707  * different to what is written in the Mellanox HCA Flash Application
1708  * Programming Guide.
1709  */
1710 static int
1711 tavor_blast_image(int fd, int prisec, uint32_t hcafia, uint32_t sectsz,
1712     struct mlx_xps *newxps)
1713 {
1714 	uint32_t i, j, rv;
1715 	uint32_t startsectimg, startsecthca, numsect;
1716 
1717 	if ((prisec != 1) && (prisec != 2)) {
1718 		logmsg(MSG_ERROR,
1719 		    "tavor: invalid image number requested (%d)\n");
1720 		return (FWFLASH_FAILURE);
1721 	}
1722 
1723 	/* Begin documentation departure point  */
1724 
1725 	/* zero the HCA's PPS signature and CRC */
1726 	if (tavor_zero_sig_crc(fd, (prisec * sectsz))
1727 	    != FWFLASH_SUCCESS) {
1728 		logmsg(MSG_INFO,
1729 		    "tavor: Unable zero HCA's %s signature "
1730 		    "and CRC16 fields\n",
1731 		    ((prisec == 1) ? "PPS" : "SPS"));
1732 		return (FWFLASH_FAILURE);
1733 	}
1734 
1735 	logmsg(MSG_INFO, "tavor: zeroing HCA's %s sig and crc\n",
1736 	    (prisec == 1) ? "pps" : "sps");
1737 
1738 	/* End documentation departure point  */
1739 
1740 	/* make sure we don't inadvertently overwrite bits */
1741 
1742 	startsectimg = MLXSWAPBITS32(newxps->fia) / sectsz;
1743 	startsecthca = hcafia / sectsz;
1744 
1745 	numsect = (MLXSWAPBITS32(newxps->fis) / sectsz) +
1746 	    ((MLXSWAPBITS32(newxps->fis) % sectsz) ? 1 : 0);
1747 
1748 	logmsg(MSG_INFO, "tavor: %s imgsize 0x%0x  startsecthca %d, "
1749 	    "startsectimg %d, num sectors %d\n",
1750 	    (prisec == 1) ? "PFI" : "SFI", MLXSWAPBITS32(newxps->fis),
1751 	    startsecthca, startsectimg, numsect);
1752 
1753 	for (i = 0; i < numsect; i++) {
1754 
1755 		j = (MLXSWAPBITS32(newxps->fia) + (i * sectsz)) / 4;
1756 
1757 		logmsg(MSG_INFO, "tavor: image offset 0x%0x\n", j);
1758 		logmsg(MSG_INFO, "tavor: writing HCA sector %d\n",
1759 		    i + startsecthca);
1760 
1761 		if (tavor_write_sector(fd, i + startsecthca,
1762 		    &verifier->fwimage[j])
1763 		    != FWFLASH_SUCCESS) {
1764 			logmsg(MSG_ERROR,
1765 			    gettext("tavor: Unable to write "
1766 			    "sector %d to HCA\n"),
1767 			    i + startsecthca);
1768 			return (FWFLASH_FAILURE);
1769 		}
1770 		(void) printf(" .");
1771 
1772 		rv = tavor_readback(fd, i + startsecthca, sectsz);
1773 		if (rv != FWFLASH_SUCCESS) {
1774 			logmsg(MSG_ERROR,
1775 			    gettext("tavor: Unable to read sector %d "
1776 			    "back from HCA\n"), i + startsecthca);
1777 			return (FWFLASH_FAILURE);
1778 		}
1779 		(void) printf(" | ");
1780 	}
1781 
1782 	/* Begin documentation departure point  */
1783 
1784 	/* invalidate the xps signature and fia fields */
1785 	newxps->signature = 0xffffffff;
1786 	newxps->crc16 = 0xffff;
1787 	/* we put the fia back to imgfia later */
1788 	newxps->fia = 0xffffffff;
1789 	/* End documentation departure point  */
1790 
1791 	/* success so far, now burn the new xPS */
1792 	if (tavor_write_sector(fd, prisec, (int *)newxps)
1793 	    != FWFLASH_SUCCESS) {
1794 		logmsg(MSG_ERROR,
1795 		    gettext("tavor: Unable to write new %s "
1796 		    "pointer sector to HCA\n"),
1797 		    (prisec == 1) ? "primary" : "secondary");
1798 		return (FWFLASH_FAILURE);
1799 	}
1800 	(void) printf(" .");
1801 
1802 	/* Begin documentation departure point  */
1803 
1804 	/* write new fia to the HCA's pps */
1805 	logmsg(MSG_INFO, "tavor: writing new fia (0x%0x) to HCA\n",
1806 	    MLXSWAPBITS32(newxps->fia));
1807 
1808 	if (tavor_write_xps_fia(fd, (prisec * sectsz),
1809 	    MLXSWAPBITS32(hcafia)) != FWFLASH_SUCCESS) {
1810 		logmsg(MSG_ERROR,
1811 		    gettext("tavor: Unable to update HCA's %s "
1812 		    "pointer sector FIA record\n"),
1813 		    (prisec == 1) ? "primary" : "secondary");
1814 		return (FWFLASH_FAILURE);
1815 	}
1816 
1817 	/* don't forget the byte-swapping */
1818 	newxps->fia = MLXSWAPBITS32(hcafia);
1819 	newxps->signature =
1820 	    (uint32_t)MLXSWAPBITS32(FLASH_PS_SIGNATURE);
1821 	newxps->crc16 =
1822 	    MLXSWAPBITS16(crc16((uint8_t *)newxps, FLASH_PS_CRC16_SIZE));
1823 
1824 	logmsg(MSG_INFO, "tavor: writing new fia 0x%0x, "
1825 	    "sig 0x%0x and new crc16 0x%0x\n",
1826 	    newxps->fia, MLXSWAPBITS32(newxps->signature),
1827 	    newxps->crc16);
1828 
1829 	if (tavor_write_xps_crc_sig(fd, (prisec * sectsz),
1830 	    newxps->crc16) != FWFLASH_SUCCESS) {
1831 		/*
1832 		 * Now we're REALLY hosed. If the card comes up at all,
1833 		 * expect it to be in "Maintenance Mode".
1834 		 */
1835 		logmsg(MSG_ERROR,
1836 		    gettext("tavor: Unable to update HCA's %s CRC "
1837 		    "and Firmware Image signature fields\n"),
1838 		    (prisec == 1) ? "PPS" : "SPS");
1839 		return (FWFLASH_FAILURE);
1840 	}
1841 
1842 	rv = tavor_readback(fd, prisec, sectsz);
1843 	if (rv != FWFLASH_SUCCESS) {
1844 		logmsg(MSG_ERROR,
1845 		    gettext("tavor: Unable to read %s pointer sector "
1846 		    "from HCA\n"),
1847 		    (prisec == 1) ? "Primary" : "Secondary");
1848 		return (FWFLASH_FAILURE);
1849 	}
1850 	(void) printf(" |");
1851 	/* End documentation departure point  */
1852 	return (FWFLASH_SUCCESS);
1853 }
1854 
1855 
1856 static int
1857 tavor_readback(int infd, int whichsect, int sectsz)
1858 {
1859 	uint32_t *data;
1860 	tavor_flash_ioctl_t	cmd;
1861 	int rv;
1862 
1863 	bzero(&cmd, sizeof (tavor_flash_ioctl_t));
1864 	data = calloc(1, sectsz); /* assumption! */
1865 
1866 	cmd.tf_type = TAVOR_FLASH_READ_SECTOR;
1867 	cmd.tf_sector_num = whichsect;
1868 	cmd.tf_sector = (caddr_t)data;
1869 	rv = ioctl(infd, TAVOR_IOCTL_FLASH_READ, &cmd);
1870 	if (rv < 0) {
1871 		logmsg(MSG_INFO,
1872 		    "tavor: UNABLE TO READ BACK SECTOR %d from HCA\n",
1873 		    whichsect);
1874 		return (FWFLASH_FAILURE);
1875 	}
1876 	free(data);
1877 	return (FWFLASH_SUCCESS);
1878 }
1879 
1880 
1881 /*
1882  * crc16 - computes 16 bit crc of supplied buffer.
1883  *   image should be in network byteorder
1884  *   result is returned in host byteorder form
1885  */
1886 static uint16_t
1887 crc16(uint8_t *image, uint32_t size)
1888 {
1889 	const uint16_t	poly = 0x100b;
1890 	uint32_t	crc = 0xFFFF;
1891 	uint32_t	word;
1892 	uint32_t	i, j;
1893 
1894 	for (i = 0; i < size / 4; i++) {
1895 		word = (image[4 * i] << 24) |
1896 		    (image[4 * i + 1] << 16) |
1897 		    (image[4 * i + 2] << 8) |
1898 		    (image[4 * i + 3]);
1899 
1900 		for (j = 0; j < 32; j++) {
1901 			if (crc & 0x8000) {
1902 				crc = (((crc << 1) |
1903 				    (word >> 31)) ^ poly) & 0xFFFF;
1904 			} else {
1905 				crc = ((crc << 1) | (word >> 31)) & 0xFFFF;
1906 			}
1907 			word = (word << 1) & 0xFFFFFFFF;
1908 		}
1909 	}
1910 
1911 	for (i = 0; i < 16; i++) {
1912 		if (crc & 0x8000) {
1913 			crc = ((crc << 1) ^ poly) & 0xFFFF;
1914 		} else {
1915 			crc = (crc << 1) & 0xFFFF;
1916 		}
1917 	}
1918 
1919 	crc = crc ^ 0xFFFF;
1920 	return (crc & 0xFFFF);
1921 }
1922