xref: /illumos-gate/usr/src/cmd/fwflash/plugins/vendor/tavor-MELLANOX.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Mellanox firmware image verification plugin
28  */
29 
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/sysmacros.h>
37 #include <fcntl.h>
38 #include <sys/condvar.h>
39 #include <string.h>
40 #include <strings.h>
41 
42 #include <sys/byteorder.h>
43 
44 #include <libintl.h> /* for gettext(3c) */
45 
46 #include <fwflash/fwflash.h>
47 #include "../hdrs/MELLANOX.h"
48 #include "../hdrs/tavor_ib.h"
49 
50 char vendor[] = "MELLANOX\0";
51 
52 extern int errno;
53 extern struct vrfyplugin *verifier;
54 
55 
56 /* required functions for this plugin */
57 int vendorvrfy(struct devicelist *devicenode);
58 
59 
60 /* helper functions */
61 static int check_guid_ptr(uint8_t *data);
62 
63 
64 int
65 vendorvrfy(struct devicelist *devicenode)
66 {
67 	struct ib_encap_ident	*encap;
68 	uint32_t	sector_sz;
69 	int		*firmware;
70 	uint32_t	vp_fia, vs_fia;
71 	uint32_t	vp_imginfo, vs_imginfo;
72 	struct mlx_xps	*vps;
73 	uint8_t		*vfi;
74 	int		i = 0, a, b, c, d;
75 	char		temppsid[17];
76 	char		rawpsid[16];
77 	int		offset;
78 
79 	encap = (struct ib_encap_ident *)devicenode->ident->encap_ident;
80 
81 	/*
82 	 * NOTE that since verifier->fwimage is an array of ints,
83 	 * we have to divide our actual desired number by 4 to get
84 	 * the right data.
85 	 */
86 	firmware = verifier->fwimage;
87 
88 	/*
89 	 * The actual location of log2_sector_sz can be calculated
90 	 * by adding 0x32 to the value that is written in the
91 	 * log2_sector_sz_ptr field.  The log2_sector_sz_ptr is located
92 	 * at 0x16 byte offset in Invariant Sector.
93 	 */
94 	offset = FLASH_IS_SECTOR_SIZE_OFFSET +
95 	    MLXSWAPBITS32(firmware[FLASH_IS_SECT_SIZE_PTR/4]);
96 
97 	sector_sz = 1 << MLXSWAPBITS32(firmware[offset/4]);
98 
99 	if (sector_sz != encap->sector_sz) {
100 		logmsg(MSG_ERROR,
101 		    gettext("%s firmware image verifier: "
102 		    "Invariant Sector is invalid\n"), verifier->vendor);
103 		logmsg(MSG_ERROR, gettext("Mis-match in sector size: "
104 		    "device's 0x%X file 0x%X\n"), encap->sector_sz, sector_sz);
105 		logmsg(MSG_ERROR, gettext("Firmware image file is not "
106 		    "appropriate for this device.\n"));
107 		/* this is fatal */
108 		return (FWFLASH_FAILURE);
109 	}
110 
111 	/* now verify primary pointer sector */
112 	if ((vps = calloc(1, sizeof (struct mlx_xps))) == NULL) {
113 		logmsg(MSG_ERROR,
114 		    gettext("%s firmware image verifier: "
115 		    "Unable to allocate memory for Primary Pointer "
116 		    "Sector verification\n"), verifier->vendor);
117 		return (FWFLASH_FAILURE);
118 	}
119 	bcopy(&firmware[sector_sz / 4], vps, sizeof (struct mlx_xps));
120 	if ((MLXSWAPBITS32(vps->signature) != FLASH_PS_SIGNATURE) ||
121 	    (vps->xpsresv3 != 0)) {
122 		logmsg(MSG_ERROR,
123 		    gettext("%s firmware image verifier: "
124 		    "Primary Pointer Sector is invalid\n"),
125 		    verifier->vendor);
126 	}
127 	vp_fia = MLXSWAPBITS32(vps->fia);
128 
129 	/*
130 	 * A slight diversion - check the PSID in the last
131 	 * 16 bytes of the first 256bytes in the xPS sectors.
132 	 * This will give us our part number to match. If the
133 	 * part number in the image doesn't match the part number
134 	 * in the encap_ident info (and pn_len > 0) then we reject
135 	 * this image as being incompatible with the HCA.
136 	 *
137 	 * In this bit we're only checking the info.mlx_psid field
138 	 * of the primary image in the on-disk image. If that's
139 	 * invalid we reject the image.
140 	 */
141 
142 	bzero(temppsid, 17);
143 	bcopy(vps->vsdpsid+0xd0, &rawpsid, 16);
144 
145 	for (i = 0; i < 16; i += 4) {
146 		temppsid[i]   = rawpsid[i+3];
147 		temppsid[i+1] = rawpsid[i+2];
148 		temppsid[i+2] = rawpsid[i+1];
149 		temppsid[i+3] = rawpsid[i];
150 	}
151 	logmsg(MSG_INFO,
152 	    "tavor: have raw '%s', want munged '%s'\n",
153 	    rawpsid, temppsid);
154 	logmsg(MSG_INFO, "tavor_vrfy: PSID file '%s' HCA's PSID '%s'\n",
155 	    (temppsid != NULL) ? temppsid : "(null)",
156 	    (encap->info.mlx_psid != NULL) ? encap->info.mlx_psid : "(null)");
157 
158 	if (encap->info.mlx_psid != NULL) {
159 		int resp;
160 		if (strncmp(encap->info.mlx_psid, temppsid, 16) != 0) {
161 			logmsg(MSG_ERROR,
162 			    gettext("%s firmware image verifier: "
163 			    "firmware image file %s is not appropriate "
164 			    "for device "
165 			    "%s (PSID file %s vs PSID device %s)\n"),
166 			    verifier->vendor, verifier->imgfile,
167 			    devicenode->drvname,
168 			    ((temppsid != NULL) ? temppsid : "(null)"),
169 			    encap->info.mlx_psid);
170 
171 			logmsg(MSG_ERROR,
172 			    gettext("Do you want to continue? (Y/N): "));
173 			(void) fflush(stdin);
174 			resp = getchar();
175 			if (resp != 'Y' && resp != 'y') {
176 				free(vps);
177 				logmsg(MSG_ERROR, gettext("Not proceeding with "
178 				    "flash operation of %s on %s\n"),
179 				    verifier->imgfile, devicenode->drvname);
180 				return (FWFLASH_FAILURE);
181 			}
182 		} else {
183 			logmsg(MSG_INFO,
184 			    "%s firmware image verifier: HCA PSID (%s) "
185 			    "matches firmware image %s's PSID\n",
186 			    verifier->vendor,
187 			    encap->info.mlx_psid,
188 			    verifier->imgfile);
189 		}
190 	}
191 
192 
193 	/* now verify secondary pointer sector */
194 	bzero(vps, sizeof (struct mlx_xps));
195 
196 	bcopy(&firmware[sector_sz / 2], vps, sizeof (struct mlx_xps));
197 	if ((MLXSWAPBITS32(vps->signature) != FLASH_PS_SIGNATURE) ||
198 	    (vps->xpsresv3 != 0)) {
199 		logmsg(MSG_ERROR,
200 		    gettext("%s firmware image verifier: "
201 		    "Secondary Pointer Sector is invalid\n"),
202 		    verifier->vendor);
203 	}
204 	vs_fia = MLXSWAPBITS32(vps->fia);
205 
206 	(void) free(vps);
207 
208 	if ((vfi = calloc(1, sector_sz)) == NULL) {
209 		logmsg(MSG_ERROR,
210 		    gettext("%s firmware image verifier: "
211 		    "Unable to allocate space for Primary "
212 		    "Firmware Image verification\n"),
213 		    verifier->vendor);
214 		return (FWFLASH_FAILURE);
215 	}
216 	bcopy(&firmware[vp_fia / 4], vfi, sector_sz);
217 	bcopy(&vfi[XFI_IMGINFO_OFFSET], &i, 4);
218 	vp_imginfo = MLXSWAPBITS32(i);
219 
220 	/* for readability only */
221 	a = (vp_imginfo & 0xff000000) >> 24;
222 	b = (vp_imginfo & 0x00ff0000) >> 16;
223 	c = (vp_imginfo & 0x0000ff00) >> 8;
224 	d = (vp_imginfo & 0x000000ff);
225 
226 	/*
227 	 * It appears to be the case (empirically) that this particular
228 	 * check condition for ImageInfoPtr doesn't hold for A1 firmware
229 	 * images. So if the ((a+b+c+d)%0x100) fails, don't worry unless
230 	 * the contents of the GUID section do not match the Mellanox
231 	 * default GUIDs 2c9000100d05[0123]. The A2++ images also have
232 	 * these default GUIDS.
233 	 *
234 	 * Unfortunately we can't depend on the hwrev field of the image's
235 	 * Invariant Sector for another level of confirmation, since A2++
236 	 * images seem to have that field set to 0xa1 as well as the A1
237 	 * images. Annoying!
238 	 */
239 
240 	if ((((a+b+c+d) % 0x100) == 0) &&
241 	    (vp_imginfo != 0x00000000)) {
242 		logmsg(MSG_INFO,
243 		    "%s firmware image verifier: "
244 		    "Primary Firmware Image Info pointer is valid\n",
245 		    verifier->vendor);
246 	} else {
247 
248 		logmsg(MSG_INFO,
249 		    gettext("%s firmware image verifier: "
250 		    "Primary Firmware Image Info pointer is invalid "
251 		    "(0x%04x)\nChecking GUID section.....\n"),
252 		    verifier->vendor, vp_imginfo);
253 
254 		if (check_guid_ptr(vfi) == FWFLASH_FAILURE) {
255 			logmsg(MSG_INFO,
256 			    gettext("%s firmware image verifier: "
257 			    "Primary Firmware Image GUID section "
258 			    "is invalid\n"),
259 			    verifier->vendor);
260 			i = 1;
261 		} else {
262 			logmsg(MSG_INFO,
263 			    "%s firmware image verifier: "
264 			    "Primary GUID section is ok\n",
265 			    verifier->vendor);
266 		}
267 
268 	}
269 
270 	bzero(vfi, sector_sz);
271 	bcopy(&firmware[vs_fia / 4], vfi, sector_sz);
272 
273 	bcopy(&vfi[XFI_IMGINFO_OFFSET], &i, 4);
274 	vs_imginfo = MLXSWAPBITS32(i);
275 
276 	/* for readability only */
277 	a = (vs_imginfo & 0xff000000) >> 24;
278 	b = (vs_imginfo & 0x00ff0000) >> 16;
279 	c = (vs_imginfo & 0x0000ff00) >> 8;
280 	d = (vs_imginfo & 0x000000ff);
281 
282 	if ((((a+b+c+d) % 0x100) == 0) &&
283 	    (vs_imginfo != 0x00000000)) {
284 		logmsg(MSG_INFO,
285 		    "%s firmware image verifier: "
286 		    "Secondary Firmware Image Info pointer is valid\n",
287 		    verifier->vendor);
288 	} else {
289 		logmsg(MSG_INFO,
290 		    gettext("%s firmware image verifier: "
291 		    "Secondary Firmware Image Info pointer is invalid "
292 		    "(0x%04x)\nChecking GUID section.....\n"),
293 		    verifier->vendor, vp_imginfo);
294 
295 		if (check_guid_ptr(vfi) == FWFLASH_FAILURE) {
296 			logmsg(MSG_INFO,
297 			    gettext("%s firmware image verifier: "
298 			    "Secondary Firmware Image GUID section "
299 			    "is invalid\n"),
300 			    verifier->vendor);
301 			i++;
302 		}
303 	}
304 
305 	free(vfi);
306 
307 	if (i == 2)
308 		logmsg(MSG_WARN, gettext("%s firmware image verifier: "
309 		    "FAILED\n"), verifier->vendor);
310 
311 	return ((i == 2) ? (FWFLASH_FAILURE) : (FWFLASH_SUCCESS));
312 }
313 
314 
315 /*
316  * Very simple function - we're given an array of bytes,
317  * we know that we need to read the value at offset FLASH_GUID_PTR
318  * and jump to that location to read 4x uint64_t of (hopefully)
319  * GUID data. If we can read that data, and it matches the default
320  * Mellanox GUIDs, then we return success. We need all 4 default
321  * GUIDs to match otherwise we return failure.
322  */
323 static int
324 check_guid_ptr(uint8_t *data)
325 {
326 	struct mlx_xfi	xfisect;
327 	struct mlx_guid_sect	guidsect;
328 
329 	bcopy(data, &xfisect, sizeof (xfisect));
330 	bcopy(&data[MLXSWAPBITS32(xfisect.nguidptr) - 16], &guidsect,
331 	    GUIDSECTION_SZ);
332 
333 	logmsg(MSG_INFO, "nodeguid:  %0llx\n",
334 	    MLXSWAPBITS64(guidsect.nodeguid));
335 	logmsg(MSG_INFO, "port1guid: %0llx\n",
336 	    MLXSWAPBITS64(guidsect.port1guid));
337 	logmsg(MSG_INFO, "port2guid: %0llx\n",
338 	    MLXSWAPBITS64(guidsect.port2guid));
339 	logmsg(MSG_INFO, "sysimguid: %0llx\n",
340 	    MLXSWAPBITS64(guidsect.sysimguid));
341 
342 	if ((MLXSWAPBITS64(guidsect.nodeguid) == MLX_DEFAULT_NODE_GUID) &&
343 	    (MLXSWAPBITS64(guidsect.port1guid) == MLX_DEFAULT_P1_GUID) &&
344 	    (MLXSWAPBITS64(guidsect.port2guid) == MLX_DEFAULT_P2_GUID) &&
345 	    ((MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_SYSIMG_GUID) ||
346 	    (MLXSWAPBITS64(guidsect.sysimguid) == MLX_DEFAULT_NODE_GUID)) ||
347 	    ((((MLXSWAPBITS64(guidsect.nodeguid) & HIGHBITS64) >> 40)
348 	    == MLX_OUI) ||
349 	    (((MLXSWAPBITS64(guidsect.port1guid) & HIGHBITS64) >> 40)
350 	    == MLX_OUI) ||
351 	    (((MLXSWAPBITS64(guidsect.port2guid) & HIGHBITS64) >> 40)
352 	    == MLX_OUI) ||
353 	    (((MLXSWAPBITS64(guidsect.sysimguid) & HIGHBITS64) >> 40)
354 	    == MLX_OUI))) {
355 		return (FWFLASH_SUCCESS);
356 	} else {
357 		return (FWFLASH_FAILURE);
358 	}
359 }
360