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