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
50char vendor[] = "MELLANOX\0";
51
52extern int errno;
53extern struct vrfyplugin *verifier;
54
55
56/* required functions for this plugin */
57int vendorvrfy(struct devicelist *devicenode);
58
59
60/* helper functions */
61static int check_guid_ptr(uint8_t *data);
62
63
64int
65vendorvrfy(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 */
323static int
324check_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