xref: /freebsd/sys/dev/sfxge/common/siena_nvram.c (revision 26a222dc0c048fc071b548eadad7b80405a1b126)
1 /*-
2  * Copyright 2009 Solarflare Communications Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include "efsys.h"
30 #include "efx.h"
31 #include "efx_types.h"
32 #include "efx_regs.h"
33 #include "efx_impl.h"
34 
35 #if EFSYS_OPT_SIENA
36 
37 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
38 
39 	__checkReturn		int
40 siena_nvram_partn_size(
41 	__in			efx_nic_t *enp,
42 	__in			unsigned int partn,
43 	__out			size_t *sizep)
44 {
45 	efx_mcdi_req_t req;
46 	uint8_t payload[MAX(MC_CMD_NVRAM_INFO_IN_LEN,
47 			    MC_CMD_NVRAM_INFO_OUT_LEN)];
48 	int rc;
49 
50 	if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
51 		rc = ENOTSUP;
52 		goto fail1;
53 	}
54 
55 	req.emr_cmd = MC_CMD_NVRAM_INFO;
56 	req.emr_in_buf = payload;
57 	req.emr_in_length = MC_CMD_NVRAM_INFO_IN_LEN;
58 	req.emr_out_buf = payload;
59 	req.emr_out_length = MC_CMD_NVRAM_INFO_OUT_LEN;
60 
61 	MCDI_IN_SET_DWORD(req, NVRAM_INFO_IN_TYPE, partn);
62 
63 	efx_mcdi_execute(enp, &req);
64 
65 	if (req.emr_rc != 0) {
66 		rc = req.emr_rc;
67 		goto fail2;
68 	}
69 
70 	if (req.emr_out_length_used < MC_CMD_NVRAM_INFO_OUT_LEN) {
71 		rc = EMSGSIZE;
72 		goto fail3;
73 	}
74 
75 	*sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_SIZE);
76 
77 	return (0);
78 
79 fail3:
80 	EFSYS_PROBE(fail3);
81 fail2:
82 	EFSYS_PROBE(fail2);
83 fail1:
84 	EFSYS_PROBE1(fail1, int, rc);
85 
86 	return (rc);
87 }
88 
89 	__checkReturn		int
90 siena_nvram_partn_lock(
91 	__in			efx_nic_t *enp,
92 	__in			unsigned int partn)
93 {
94 	efx_mcdi_req_t req;
95 	uint8_t payload[MC_CMD_NVRAM_UPDATE_START_IN_LEN];
96 	int rc;
97 
98 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_START;
99 	req.emr_in_buf = payload;
100 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_START_IN_LEN;
101 	EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_START_OUT_LEN == 0);
102 	req.emr_out_buf = NULL;
103 	req.emr_out_length = 0;
104 
105 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_START_IN_TYPE, partn);
106 
107 	efx_mcdi_execute(enp, &req);
108 
109 	if (req.emr_rc != 0) {
110 		rc = req.emr_rc;
111 		goto fail1;
112 	}
113 
114 	return (0);
115 
116 fail1:
117 	EFSYS_PROBE1(fail1, int, rc);
118 
119 	return (rc);
120 }
121 
122 	__checkReturn		int
123 siena_nvram_partn_read(
124 	__in			efx_nic_t *enp,
125 	__in			unsigned int partn,
126 	__in			unsigned int offset,
127 	__out_bcount(size)	caddr_t data,
128 	__in			size_t size)
129 {
130 	efx_mcdi_req_t req;
131 	uint8_t payload[MAX(MC_CMD_NVRAM_READ_IN_LEN,
132 			    MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK))];
133 	size_t chunk;
134 	int rc;
135 
136 	while (size > 0) {
137 		chunk = MIN(size, SIENA_NVRAM_CHUNK);
138 
139 		req.emr_cmd = MC_CMD_NVRAM_READ;
140 		req.emr_in_buf = payload;
141 		req.emr_in_length = MC_CMD_NVRAM_READ_IN_LEN;
142 		req.emr_out_buf = payload;
143 		req.emr_out_length =
144 			MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK);
145 
146 		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_TYPE, partn);
147 		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_OFFSET, offset);
148 		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_LENGTH, chunk);
149 
150 		efx_mcdi_execute(enp, &req);
151 
152 		if (req.emr_rc != 0) {
153 			rc = req.emr_rc;
154 			goto fail1;
155 		}
156 
157 		if (req.emr_out_length_used <
158 		    MC_CMD_NVRAM_READ_OUT_LEN(chunk)) {
159 			rc = EMSGSIZE;
160 			goto fail2;
161 		}
162 
163 		memcpy(data,
164 		    MCDI_OUT2(req, uint8_t, NVRAM_READ_OUT_READ_BUFFER),
165 		    chunk);
166 
167 		size -= chunk;
168 		data += chunk;
169 		offset += chunk;
170 	}
171 
172 	return (0);
173 
174 fail2:
175 	EFSYS_PROBE(fail2);
176 fail1:
177 	EFSYS_PROBE1(fail1, int, rc);
178 
179 	return (rc);
180 }
181 
182 	__checkReturn		int
183 siena_nvram_partn_erase(
184 	__in			efx_nic_t *enp,
185 	__in			unsigned int partn,
186 	__in			unsigned int offset,
187 	__in			size_t size)
188 {
189 	efx_mcdi_req_t req;
190 	uint8_t payload[MC_CMD_NVRAM_ERASE_IN_LEN];
191 	int rc;
192 
193 	req.emr_cmd = MC_CMD_NVRAM_ERASE;
194 	req.emr_in_buf = payload;
195 	req.emr_in_length = MC_CMD_NVRAM_ERASE_IN_LEN;
196 	EFX_STATIC_ASSERT(MC_CMD_NVRAM_ERASE_OUT_LEN == 0);
197 	req.emr_out_buf = NULL;
198 	req.emr_out_length = 0;
199 
200 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_TYPE, partn);
201 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_OFFSET, offset);
202 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_LENGTH, size);
203 
204 	efx_mcdi_execute(enp, &req);
205 
206 	if (req.emr_rc != 0) {
207 		rc = req.emr_rc;
208 		goto fail1;
209 	}
210 
211 	return (0);
212 
213 fail1:
214 	EFSYS_PROBE1(fail1, int, rc);
215 
216 	return (rc);
217 }
218 
219 	__checkReturn		int
220 siena_nvram_partn_write(
221 	__in			efx_nic_t *enp,
222 	__in			unsigned int partn,
223 	__in			unsigned int offset,
224 	__out_bcount(size)	caddr_t data,
225 	__in			size_t size)
226 {
227 	efx_mcdi_req_t req;
228 	uint8_t payload[MC_CMD_NVRAM_WRITE_IN_LEN(SIENA_NVRAM_CHUNK)];
229 	size_t chunk;
230 	int rc;
231 
232 	while (size > 0) {
233 		chunk = MIN(size, SIENA_NVRAM_CHUNK);
234 
235 		req.emr_cmd = MC_CMD_NVRAM_WRITE;
236 		req.emr_in_buf = payload;
237 		req.emr_in_length = MC_CMD_NVRAM_WRITE_IN_LEN(chunk);
238 		EFX_STATIC_ASSERT(MC_CMD_NVRAM_WRITE_OUT_LEN == 0);
239 		req.emr_out_buf = NULL;
240 		req.emr_out_length = 0;
241 
242 		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_TYPE, partn);
243 		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_OFFSET, offset);
244 		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_LENGTH, chunk);
245 
246 		memcpy(MCDI_IN2(req, uint8_t, NVRAM_WRITE_IN_WRITE_BUFFER),
247 		    data, chunk);
248 
249 		efx_mcdi_execute(enp, &req);
250 
251 		if (req.emr_rc != 0) {
252 			rc = req.emr_rc;
253 			goto fail1;
254 		}
255 
256 		size -= chunk;
257 		data += chunk;
258 		offset += chunk;
259 	}
260 
261 	return (0);
262 
263 fail1:
264 	EFSYS_PROBE1(fail1, int, rc);
265 
266 	return (rc);
267 }
268 
269 				void
270 siena_nvram_partn_unlock(
271 	__in			efx_nic_t *enp,
272 	__in			unsigned int partn)
273 {
274 	efx_mcdi_req_t req;
275 	uint8_t payload[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN];
276 	uint32_t reboot;
277 	int rc;
278 
279 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_FINISH;
280 	req.emr_in_buf = payload;
281 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN;
282 	EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN == 0);
283 	req.emr_out_buf = NULL;
284 	req.emr_out_length = 0;
285 
286 	/*
287 	 * Reboot into the new image only for PHYs. The driver has to
288 	 * explicitly cope with an MC reboot after a firmware update.
289 	 */
290 	reboot = (partn == MC_CMD_NVRAM_TYPE_PHY_PORT0 ||
291 		    partn == MC_CMD_NVRAM_TYPE_PHY_PORT1 ||
292 		    partn == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO);
293 
294 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_TYPE, partn);
295 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_REBOOT, reboot);
296 
297 	efx_mcdi_execute(enp, &req);
298 
299 	if (req.emr_rc != 0) {
300 		rc = req.emr_rc;
301 		goto fail1;
302 	}
303 
304 	return;
305 
306 fail1:
307 	EFSYS_PROBE1(fail1, int, rc);
308 }
309 
310 #endif	/* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
311 
312 #if EFSYS_OPT_NVRAM
313 
314 typedef struct siena_parttbl_entry_s {
315 	unsigned int		partn;
316 	unsigned int		port;
317 	efx_nvram_type_t	nvtype;
318 } siena_parttbl_entry_t;
319 
320 static siena_parttbl_entry_t siena_parttbl[] = {
321 	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	1, EFX_NVRAM_NULLPHY},
322 	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	2, EFX_NVRAM_NULLPHY},
323 	{MC_CMD_NVRAM_TYPE_MC_FW,		1, EFX_NVRAM_MC_FIRMWARE},
324 	{MC_CMD_NVRAM_TYPE_MC_FW,		2, EFX_NVRAM_MC_FIRMWARE},
325 	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	1, EFX_NVRAM_MC_GOLDEN},
326 	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	2, EFX_NVRAM_MC_GOLDEN},
327 	{MC_CMD_NVRAM_TYPE_EXP_ROM,		1, EFX_NVRAM_BOOTROM},
328 	{MC_CMD_NVRAM_TYPE_EXP_ROM,		2, EFX_NVRAM_BOOTROM},
329 	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0,	1, EFX_NVRAM_BOOTROM_CFG},
330 	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1,	2, EFX_NVRAM_BOOTROM_CFG},
331 	{MC_CMD_NVRAM_TYPE_PHY_PORT0,		1, EFX_NVRAM_PHY},
332 	{MC_CMD_NVRAM_TYPE_PHY_PORT1,		2, EFX_NVRAM_PHY},
333 	{MC_CMD_NVRAM_TYPE_FPGA,		1, EFX_NVRAM_FPGA},
334 	{MC_CMD_NVRAM_TYPE_FPGA,		2, EFX_NVRAM_FPGA},
335 	{MC_CMD_NVRAM_TYPE_FPGA_BACKUP,		1, EFX_NVRAM_FPGA_BACKUP},
336 	{MC_CMD_NVRAM_TYPE_FPGA_BACKUP,		2, EFX_NVRAM_FPGA_BACKUP},
337 	{MC_CMD_NVRAM_TYPE_FC_FW,		1, EFX_NVRAM_FCFW},
338 	{MC_CMD_NVRAM_TYPE_FC_FW,		2, EFX_NVRAM_FCFW},
339 	{MC_CMD_NVRAM_TYPE_CPLD,		1, EFX_NVRAM_CPLD},
340 	{MC_CMD_NVRAM_TYPE_CPLD,		2, EFX_NVRAM_CPLD},
341 	{0, 0, 0},
342 };
343 
344 static	__checkReturn		siena_parttbl_entry_t *
345 siena_parttbl_entry(
346 	__in			efx_nic_t *enp,
347 	__in			efx_nvram_type_t type)
348 {
349 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
350 	siena_parttbl_entry_t *entry;
351 
352 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
353 
354 	for (entry = siena_parttbl; entry->port > 0; ++entry) {
355 		if (entry->port == emip->emi_port && entry->nvtype == type)
356 			return (entry);
357 	}
358 
359 	return (NULL);
360 }
361 
362 #if EFSYS_OPT_DIAG
363 
364 	__checkReturn		int
365 siena_nvram_test(
366 	__in			efx_nic_t *enp)
367 {
368 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
369 	siena_parttbl_entry_t *entry;
370 	efx_mcdi_req_t req;
371 	uint8_t payload[MAX(MC_CMD_NVRAM_TEST_IN_LEN,
372 			    MC_CMD_NVRAM_TEST_OUT_LEN)];
373 	int result;
374 	int rc;
375 
376 	req.emr_cmd = MC_CMD_NVRAM_TEST;
377 	req.emr_in_buf = payload;
378 	req.emr_in_length = MC_CMD_NVRAM_TEST_IN_LEN;
379 	req.emr_out_buf = payload;
380 	req.emr_out_length = MC_CMD_NVRAM_TEST_OUT_LEN;
381 
382 	/*
383 	 * Iterate over the list of supported partition types
384 	 * applicable to *this* port
385 	 */
386 	for (entry = siena_parttbl; entry->port > 0; ++entry) {
387 		if (entry->port != emip->emi_port ||
388 		    !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn)))
389 			continue;
390 
391 		MCDI_IN_SET_DWORD(req, NVRAM_TEST_IN_TYPE, entry->partn);
392 
393 		efx_mcdi_execute(enp, &req);
394 
395 		if (req.emr_rc != 0) {
396 			rc = req.emr_rc;
397 			goto fail1;
398 		}
399 
400 		if (req.emr_out_length_used < MC_CMD_NVRAM_TEST_OUT_LEN) {
401 			rc = EMSGSIZE;
402 			goto fail2;
403 		}
404 
405 		result = MCDI_OUT_DWORD(req, NVRAM_TEST_OUT_RESULT);
406 		if (result == MC_CMD_NVRAM_TEST_FAIL) {
407 
408 			EFSYS_PROBE1(nvram_test_failure, int, entry->partn);
409 
410 			rc = (EINVAL);
411 			goto fail3;
412 		}
413 	}
414 
415 	return (0);
416 
417 fail3:
418 	EFSYS_PROBE(fail3);
419 fail2:
420 	EFSYS_PROBE(fail2);
421 fail1:
422 	EFSYS_PROBE1(fail1, int, rc);
423 
424 	return (rc);
425 }
426 
427 #endif	/* EFSYS_OPT_DIAG */
428 
429 	__checkReturn		int
430 siena_nvram_size(
431 	__in			efx_nic_t *enp,
432 	__in			efx_nvram_type_t type,
433 	__out			size_t *sizep)
434 {
435 	siena_parttbl_entry_t *entry;
436 	int rc;
437 
438 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
439 		rc = ENOTSUP;
440 		goto fail1;
441 	}
442 
443 	if ((rc = siena_nvram_partn_size(enp, entry->partn, sizep)) != 0)
444 		goto fail2;
445 
446 	return (0);
447 
448 fail2:
449 	EFSYS_PROBE(fail2);
450 fail1:
451 	EFSYS_PROBE1(fail1, int, rc);
452 
453 	*sizep = 0;
454 
455 	return (rc);
456 }
457 
458 #define	SIENA_DYNAMIC_CFG_SIZE(_nitems)					\
459 	(sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) *		\
460 	sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0])))
461 
462 	__checkReturn		int
463 siena_nvram_get_dynamic_cfg(
464 	__in			efx_nic_t *enp,
465 	__in			unsigned int partn,
466 	__in			boolean_t vpd,
467 	__out			siena_mc_dynamic_config_hdr_t **dcfgp,
468 	__out			size_t *sizep)
469 {
470 	siena_mc_dynamic_config_hdr_t *dcfg;
471 	size_t size;
472 	uint8_t cksum;
473 	unsigned int vpd_offset;
474 	unsigned int vpd_length;
475 	unsigned int hdr_length;
476 	unsigned int nversions;
477 	unsigned int pos;
478 	unsigned int region;
479 	int rc;
480 
481 	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 ||
482 		    partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1);
483 
484 	/*
485 	 * Allocate sufficient memory for the entire dynamiccfg area, even
486 	 * if we're not actually going to read in the VPD.
487 	 */
488 	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
489 		goto fail1;
490 
491 	EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg);
492 	if (dcfg == NULL) {
493 		rc = ENOMEM;
494 		goto fail2;
495 	}
496 
497 	if ((rc = siena_nvram_partn_read(enp, partn, 0,
498 	    (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0)
499 		goto fail3;
500 
501 	/* Verify the magic */
502 	if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0)
503 	    != SIENA_MC_DYNAMIC_CONFIG_MAGIC)
504 		goto invalid1;
505 
506 	/* All future versions of the structure must be backwards compatable */
507 	EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0);
508 
509 	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
510 	nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
511 	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
512 	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
513 
514 	/* Verify the hdr doesn't overflow the partn size */
515 	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
516 	    vpd_length + vpd_offset > size)
517 		goto invalid2;
518 
519 	/* Verify the header has room for all it's versions */
520 	if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) ||
521 	    hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions))
522 		goto invalid3;
523 
524 	/*
525 	 * Read the remaining portion of the dcfg, either including
526 	 * the whole of VPD (there is no vpd length in this structure,
527 	 * so we have to parse each tag), or just the dcfg header itself
528 	 */
529 	region = vpd ? vpd_offset + vpd_length : hdr_length;
530 	if (region > SIENA_NVRAM_CHUNK) {
531 		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
532 		    (caddr_t)dcfg + SIENA_NVRAM_CHUNK,
533 		    region - SIENA_NVRAM_CHUNK)) != 0)
534 			goto fail4;
535 	}
536 
537 	/* Verify checksum */
538 	cksum = 0;
539 	for (pos = 0; pos < hdr_length; pos++)
540 		cksum += ((uint8_t *)dcfg)[pos];
541 	if (cksum != 0)
542 		goto invalid4;
543 
544 	goto done;
545 
546 invalid4:
547 	EFSYS_PROBE(invalid4);
548 invalid3:
549 	EFSYS_PROBE(invalid3);
550 invalid2:
551 	EFSYS_PROBE(invalid2);
552 invalid1:
553 	EFSYS_PROBE(invalid1);
554 
555 	/*
556 	 * Construct a new "null" dcfg, with an empty version vector,
557 	 * and an empty VPD chunk trailing. This has the neat side effect
558 	 * of testing the exception paths in the write path.
559 	 */
560 	EFX_POPULATE_DWORD_1(dcfg->magic,
561 			    EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC);
562 	EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg));
563 	EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0,
564 			    SIENA_MC_DYNAMIC_CONFIG_VERSION);
565 	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
566 			    EFX_DWORD_0, sizeof (*dcfg));
567 	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0);
568 	EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0);
569 
570 done:
571 	*dcfgp = dcfg;
572 	*sizep = size;
573 
574 	return (0);
575 
576 fail4:
577 	EFSYS_PROBE(fail4);
578 fail3:
579 	EFSYS_PROBE(fail3);
580 fail2:
581 	EFSYS_PROBE(fail2);
582 
583 	EFSYS_KMEM_FREE(enp->en_esip, size, dcfg);
584 
585 fail1:
586 	EFSYS_PROBE1(fail1, int, rc);
587 
588 	return (rc);
589 }
590 
591 static	__checkReturn		int
592 siena_nvram_get_subtype(
593 	__in			efx_nic_t *enp,
594 	__in			unsigned int partn,
595 	__out			uint32_t *subtypep)
596 {
597 	efx_mcdi_req_t req;
598 	uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LENMAX];
599 	efx_word_t *fw_list;
600 	int rc;
601 
602 	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
603 	EFX_STATIC_ASSERT(MC_CMD_GET_BOARD_CFG_IN_LEN == 0);
604 	req.emr_in_buf = NULL;
605 	req.emr_in_length = 0;
606 	req.emr_out_buf = outbuf;
607 	req.emr_out_length = sizeof (outbuf);
608 
609 	efx_mcdi_execute(enp, &req);
610 
611 	if (req.emr_rc != 0) {
612 		rc = req.emr_rc;
613 		goto fail1;
614 	}
615 
616 	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
617 		rc = EMSGSIZE;
618 		goto fail2;
619 	}
620 
621 	if (req.emr_out_length_used <
622 	    MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST +
623 	    (partn + 1) * sizeof (efx_word_t)) {
624 		rc = ENOENT;
625 		goto fail3;
626 	}
627 
628 	fw_list = MCDI_OUT2(req, efx_word_t,
629 			    GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
630 	*subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0);
631 
632 	return (0);
633 
634 fail3:
635 	EFSYS_PROBE(fail3);
636 fail2:
637 	EFSYS_PROBE(fail2);
638 fail1:
639 	EFSYS_PROBE1(fail1, int, rc);
640 
641 	return (rc);
642 }
643 
644 	__checkReturn		int
645 siena_nvram_get_version(
646 	__in			efx_nic_t *enp,
647 	__in			efx_nvram_type_t type,
648 	__out			uint32_t *subtypep,
649 	__out_ecount(4)		uint16_t version[4])
650 {
651 	siena_mc_dynamic_config_hdr_t *dcfg;
652 	siena_parttbl_entry_t *entry;
653 	unsigned int dcfg_partn;
654 	unsigned int partn;
655 	int rc;
656 
657 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
658 		rc = ENOTSUP;
659 		goto fail1;
660 	}
661 	partn = entry->partn;
662 
663 	if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
664 		rc = ENOTSUP;
665 		goto fail2;
666 	}
667 
668 	if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0)
669 		goto fail3;
670 
671 	/*
672 	 * Some partitions are accessible from both ports (for instance BOOTROM)
673 	 * Find the highest version reported by all dcfg structures on ports
674 	 * that have access to this partition.
675 	 */
676 	version[0] = version[1] = version[2] = version[3] = 0;
677 	for (entry = siena_parttbl; entry->port > 0; ++entry) {
678 		unsigned int nitems;
679 		uint16_t temp[4];
680 		size_t length;
681 
682 		if (entry->partn != partn)
683 			continue;
684 
685 		dcfg_partn = (entry->port == 1)
686 			? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
687 			: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
688 		/*
689 		 * Ingore missing partitions on port 2, assuming they're due
690 		 * to to running on a single port part.
691 		 */
692 		if ((1 << dcfg_partn) &  ~enp->en_u.siena.enu_partn_mask) {
693 			if (entry->port == 2)
694 				continue;
695 		}
696 
697 		if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
698 		    B_FALSE, &dcfg, &length)) != 0)
699 			goto fail4;
700 
701 		nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items,
702 			    EFX_DWORD_0);
703 		if (nitems < entry->partn)
704 			goto done;
705 
706 		temp[0] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_w,
707 			    EFX_WORD_0);
708 		temp[1] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_x,
709 			    EFX_WORD_0);
710 		temp[2] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_y,
711 			    EFX_WORD_0);
712 		temp[3] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_z,
713 			    EFX_WORD_0);
714 		if (memcmp(version, temp, sizeof (temp)) < 0)
715 			memcpy(version, temp, sizeof (temp));
716 
717 	done:
718 		EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
719 	}
720 
721 	return (0);
722 
723 fail4:
724 	EFSYS_PROBE(fail4);
725 fail3:
726 	EFSYS_PROBE(fail3);
727 fail2:
728 	EFSYS_PROBE(fail2);
729 fail1:
730 	EFSYS_PROBE1(fail1, int, rc);
731 
732 	return (rc);
733 }
734 
735 	__checkReturn		int
736 siena_nvram_rw_start(
737 	__in			efx_nic_t *enp,
738 	__in			efx_nvram_type_t type,
739 	__out			size_t *chunk_sizep)
740 {
741 	siena_parttbl_entry_t *entry;
742 	int rc;
743 
744 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
745 		rc = ENOTSUP;
746 		goto fail1;
747 	}
748 
749 	if ((rc = siena_nvram_partn_lock(enp, entry->partn)) != 0)
750 		goto fail2;
751 
752 	if (chunk_sizep != NULL)
753 		*chunk_sizep = SIENA_NVRAM_CHUNK;
754 
755 	return (0);
756 
757 fail2:
758 	EFSYS_PROBE(fail2);
759 fail1:
760 	EFSYS_PROBE1(fail1, int, rc);
761 
762 	return (rc);
763 }
764 
765 	__checkReturn		int
766 siena_nvram_read_chunk(
767 	__in			efx_nic_t *enp,
768 	__in			efx_nvram_type_t type,
769 	__in			unsigned int offset,
770 	__out_bcount(size)	caddr_t data,
771 	__in			size_t size)
772 {
773 	siena_parttbl_entry_t *entry;
774 	int rc;
775 
776 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
777 		rc = ENOTSUP;
778 		goto fail1;
779 	}
780 
781 	if ((rc = siena_nvram_partn_read(enp, entry->partn,
782 	    offset, data, size)) != 0)
783 		goto fail2;
784 
785 	return (0);
786 
787 fail2:
788 	EFSYS_PROBE(fail2);
789 fail1:
790 	EFSYS_PROBE1(fail1, int, rc);
791 
792 	return (rc);
793 }
794 
795 	__checkReturn		int
796 siena_nvram_erase(
797 	__in			efx_nic_t *enp,
798 	__in			efx_nvram_type_t type)
799 {
800 	siena_parttbl_entry_t *entry;
801 	size_t size;
802 	int rc;
803 
804 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
805 		rc = ENOTSUP;
806 		goto fail1;
807 	}
808 
809 	if ((rc = siena_nvram_partn_size(enp, entry->partn, &size)) != 0)
810 		goto fail2;
811 
812 	if ((rc = siena_nvram_partn_erase(enp, entry->partn, 0, size)) != 0)
813 		goto fail3;
814 
815 	return (0);
816 
817 fail3:
818 	EFSYS_PROBE(fail3);
819 fail2:
820 	EFSYS_PROBE(fail2);
821 fail1:
822 	EFSYS_PROBE1(fail1, int, rc);
823 
824 	return (rc);
825 }
826 
827 	__checkReturn		int
828 siena_nvram_write_chunk(
829 	__in			efx_nic_t *enp,
830 	__in			efx_nvram_type_t type,
831 	__in			unsigned int offset,
832 	__in_bcount(size)	caddr_t data,
833 	__in			size_t size)
834 {
835 	siena_parttbl_entry_t *entry;
836 	int rc;
837 
838 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
839 		rc = ENOTSUP;
840 		goto fail1;
841 	}
842 
843 	if ((rc = siena_nvram_partn_write(enp, entry->partn,
844 	    offset, data, size)) != 0)
845 		goto fail2;
846 
847 	return (0);
848 
849 fail2:
850 	EFSYS_PROBE(fail2);
851 fail1:
852 	EFSYS_PROBE1(fail1, int, rc);
853 
854 	return (rc);
855 }
856 
857 				void
858 siena_nvram_rw_finish(
859 	__in			efx_nic_t *enp,
860 	__in			efx_nvram_type_t type)
861 {
862 	siena_parttbl_entry_t *entry;
863 
864 	if ((entry = siena_parttbl_entry(enp, type)) != NULL)
865 		siena_nvram_partn_unlock(enp, entry->partn);
866 }
867 
868 	__checkReturn		int
869 siena_nvram_set_version(
870 	__in			efx_nic_t *enp,
871 	__in			efx_nvram_type_t type,
872 	__out			uint16_t version[4])
873 {
874 	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
875 	siena_parttbl_entry_t *entry;
876 	unsigned int dcfg_partn;
877 	size_t partn_size;
878 	unsigned int hdr_length;
879 	unsigned int vpd_length;
880 	unsigned int vpd_offset;
881 	unsigned int nitems;
882 	unsigned int required_hdr_length;
883 	unsigned int pos;
884 	uint8_t cksum;
885 	uint32_t subtype;
886 	size_t length;
887 	int rc;
888 
889 	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
890 		rc = ENOTSUP;
891 		goto fail1;
892 	}
893 
894 	dcfg_partn = (entry->port == 1)
895 		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
896 		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
897 
898 	if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
899 		goto fail2;
900 
901 	if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
902 		goto fail2;
903 
904 	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
905 	    B_TRUE, &dcfg, &length)) != 0)
906 		goto fail3;
907 
908 	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
909 	nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
910 	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
911 	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
912 
913 	/*
914 	 * NOTE: This function will blatt any fields trailing the version
915 	 * vector, or the VPD chunk.
916 	 */
917 	required_hdr_length = SIENA_DYNAMIC_CFG_SIZE(entry->partn + 1);
918 	if (required_hdr_length + vpd_length > length) {
919 		rc = ENOSPC;
920 		goto fail4;
921 	}
922 
923 	if (vpd_offset < required_hdr_length) {
924 		(void) memmove((caddr_t)dcfg + required_hdr_length,
925 			(caddr_t)dcfg + vpd_offset, vpd_length);
926 		vpd_offset = required_hdr_length;
927 		EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
928 				    EFX_DWORD_0, vpd_offset);
929 	}
930 
931 	if (hdr_length < required_hdr_length) {
932 		(void) memset((caddr_t)dcfg + hdr_length, 0,
933 			required_hdr_length - hdr_length);
934 		hdr_length = required_hdr_length;
935 		EFX_POPULATE_WORD_1(dcfg->length,
936 				    EFX_WORD_0, hdr_length);
937 	}
938 
939 	/* Get the subtype to insert into the fw_subtype array */
940 	if ((rc = siena_nvram_get_subtype(enp, entry->partn, &subtype)) != 0)
941 		goto fail5;
942 
943 	/* Fill out the new version */
944 	EFX_POPULATE_DWORD_1(dcfg->fw_version[entry->partn].fw_subtype,
945 			    EFX_DWORD_0, subtype);
946 	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_w,
947 			    EFX_WORD_0, version[0]);
948 	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_x,
949 			    EFX_WORD_0, version[1]);
950 	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_y,
951 			    EFX_WORD_0, version[2]);
952 	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_z,
953 			    EFX_WORD_0, version[3]);
954 
955 	/* Update the version count */
956 	if (nitems < entry->partn + 1) {
957 		nitems = entry->partn + 1;
958 		EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items,
959 				    EFX_DWORD_0, nitems);
960 	}
961 
962 	/* Update the checksum */
963 	cksum = 0;
964 	for (pos = 0; pos < hdr_length; pos++)
965 		cksum += ((uint8_t *)dcfg)[pos];
966 	dcfg->csum.eb_u8[0] -= cksum;
967 
968 	/* Erase and write the new partition */
969 	if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
970 		goto fail6;
971 
972 	/* Write out the new structure to nvram */
973 	if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0,
974 	    (caddr_t)dcfg, vpd_offset + vpd_length)) != 0)
975 		goto fail7;
976 
977 	EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
978 
979 	siena_nvram_partn_unlock(enp, dcfg_partn);
980 
981 	return (0);
982 
983 fail7:
984 	EFSYS_PROBE(fail7);
985 fail6:
986 	EFSYS_PROBE(fail6);
987 fail5:
988 	EFSYS_PROBE(fail5);
989 fail4:
990 	EFSYS_PROBE(fail4);
991 
992 	EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
993 fail3:
994 	EFSYS_PROBE(fail3);
995 fail2:
996 	EFSYS_PROBE(fail2);
997 fail1:
998 	EFSYS_PROBE1(fail1, int, rc);
999 
1000 	return (rc);
1001 }
1002 
1003 #endif	/* EFSYS_OPT_NVRAM */
1004 
1005 #endif	/* EFSYS_OPT_SIENA */
1006