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