xref: /freebsd/sys/dev/sfxge/common/efx_nvram.c (revision 315ee00f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2016 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * The views and conclusions contained in the software and documentation are
29  * those of the authors and should not be interpreted as representing official
30  * policies, either expressed or implied, of the FreeBSD Project.
31  */
32 
33 #include <sys/cdefs.h>
34 #include "efx.h"
35 #include "efx_impl.h"
36 
37 #if EFSYS_OPT_NVRAM
38 
39 #if EFSYS_OPT_SIENA
40 
41 static const efx_nvram_ops_t	__efx_nvram_siena_ops = {
42 #if EFSYS_OPT_DIAG
43 	siena_nvram_test,		/* envo_test */
44 #endif	/* EFSYS_OPT_DIAG */
45 	siena_nvram_type_to_partn,	/* envo_type_to_partn */
46 	siena_nvram_partn_size,		/* envo_partn_size */
47 	siena_nvram_partn_rw_start,	/* envo_partn_rw_start */
48 	siena_nvram_partn_read,		/* envo_partn_read */
49 	siena_nvram_partn_read,		/* envo_partn_read_backup */
50 	siena_nvram_partn_erase,	/* envo_partn_erase */
51 	siena_nvram_partn_write,	/* envo_partn_write */
52 	siena_nvram_partn_rw_finish,	/* envo_partn_rw_finish */
53 	siena_nvram_partn_get_version,	/* envo_partn_get_version */
54 	siena_nvram_partn_set_version,	/* envo_partn_set_version */
55 	NULL,				/* envo_partn_validate */
56 };
57 
58 #endif	/* EFSYS_OPT_SIENA */
59 
60 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
61 
62 static const efx_nvram_ops_t	__efx_nvram_ef10_ops = {
63 #if EFSYS_OPT_DIAG
64 	ef10_nvram_test,		/* envo_test */
65 #endif	/* EFSYS_OPT_DIAG */
66 	ef10_nvram_type_to_partn,	/* envo_type_to_partn */
67 	ef10_nvram_partn_size,		/* envo_partn_size */
68 	ef10_nvram_partn_rw_start,	/* envo_partn_rw_start */
69 	ef10_nvram_partn_read,		/* envo_partn_read */
70 	ef10_nvram_partn_read_backup,	/* envo_partn_read_backup */
71 	ef10_nvram_partn_erase,		/* envo_partn_erase */
72 	ef10_nvram_partn_write,		/* envo_partn_write */
73 	ef10_nvram_partn_rw_finish,	/* envo_partn_rw_finish */
74 	ef10_nvram_partn_get_version,	/* envo_partn_get_version */
75 	ef10_nvram_partn_set_version,	/* envo_partn_set_version */
76 	ef10_nvram_buffer_validate,	/* envo_buffer_validate */
77 };
78 
79 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
80 
81 	__checkReturn	efx_rc_t
82 efx_nvram_init(
83 	__in		efx_nic_t *enp)
84 {
85 	const efx_nvram_ops_t *envop;
86 	efx_rc_t rc;
87 
88 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
89 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
90 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NVRAM));
91 
92 	switch (enp->en_family) {
93 #if EFSYS_OPT_SIENA
94 	case EFX_FAMILY_SIENA:
95 		envop = &__efx_nvram_siena_ops;
96 		break;
97 #endif	/* EFSYS_OPT_SIENA */
98 
99 #if EFSYS_OPT_HUNTINGTON
100 	case EFX_FAMILY_HUNTINGTON:
101 		envop = &__efx_nvram_ef10_ops;
102 		break;
103 #endif	/* EFSYS_OPT_HUNTINGTON */
104 
105 #if EFSYS_OPT_MEDFORD
106 	case EFX_FAMILY_MEDFORD:
107 		envop = &__efx_nvram_ef10_ops;
108 		break;
109 #endif	/* EFSYS_OPT_MEDFORD */
110 
111 #if EFSYS_OPT_MEDFORD2
112 	case EFX_FAMILY_MEDFORD2:
113 		envop = &__efx_nvram_ef10_ops;
114 		break;
115 #endif	/* EFSYS_OPT_MEDFORD2 */
116 
117 	default:
118 		EFSYS_ASSERT(0);
119 		rc = ENOTSUP;
120 		goto fail1;
121 	}
122 
123 	enp->en_envop = envop;
124 	enp->en_mod_flags |= EFX_MOD_NVRAM;
125 
126 	enp->en_nvram_partn_locked = EFX_NVRAM_PARTN_INVALID;
127 
128 	return (0);
129 
130 fail1:
131 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
132 
133 	return (rc);
134 }
135 
136 #if EFSYS_OPT_DIAG
137 
138 	__checkReturn		efx_rc_t
139 efx_nvram_test(
140 	__in			efx_nic_t *enp)
141 {
142 	const efx_nvram_ops_t *envop = enp->en_envop;
143 	efx_rc_t rc;
144 
145 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
146 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
147 
148 	if ((rc = envop->envo_test(enp)) != 0)
149 		goto fail1;
150 
151 	return (0);
152 
153 fail1:
154 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
155 
156 	return (rc);
157 }
158 
159 #endif	/* EFSYS_OPT_DIAG */
160 
161 	__checkReturn		efx_rc_t
162 efx_nvram_size(
163 	__in			efx_nic_t *enp,
164 	__in			efx_nvram_type_t type,
165 	__out			size_t *sizep)
166 {
167 	const efx_nvram_ops_t *envop = enp->en_envop;
168 	uint32_t partn;
169 	efx_rc_t rc;
170 
171 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
172 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
173 
174 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
175 		goto fail1;
176 
177 	if ((rc = envop->envo_partn_size(enp, partn, sizep)) != 0)
178 		goto fail2;
179 
180 	return (0);
181 
182 fail2:
183 	EFSYS_PROBE(fail2);
184 fail1:
185 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
186 	*sizep = 0;
187 
188 	return (rc);
189 }
190 
191 	__checkReturn		efx_rc_t
192 efx_nvram_get_version(
193 	__in			efx_nic_t *enp,
194 	__in			efx_nvram_type_t type,
195 	__out			uint32_t *subtypep,
196 	__out_ecount(4)		uint16_t version[4])
197 {
198 	const efx_nvram_ops_t *envop = enp->en_envop;
199 	uint32_t partn;
200 	efx_rc_t rc;
201 
202 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
203 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
204 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
205 
206 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
207 		goto fail1;
208 
209 	if ((rc = envop->envo_partn_get_version(enp, partn,
210 		    subtypep, version)) != 0)
211 		goto fail2;
212 
213 	return (0);
214 
215 fail2:
216 	EFSYS_PROBE(fail2);
217 fail1:
218 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
219 
220 	return (rc);
221 }
222 
223 	__checkReturn		efx_rc_t
224 efx_nvram_rw_start(
225 	__in			efx_nic_t *enp,
226 	__in			efx_nvram_type_t type,
227 	__out_opt		size_t *chunk_sizep)
228 {
229 	const efx_nvram_ops_t *envop = enp->en_envop;
230 	uint32_t partn;
231 	efx_rc_t rc;
232 
233 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
234 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
235 
236 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
237 		goto fail1;
238 
239 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, EFX_NVRAM_PARTN_INVALID);
240 
241 	if ((rc = envop->envo_partn_rw_start(enp, partn, chunk_sizep)) != 0)
242 		goto fail2;
243 
244 	enp->en_nvram_partn_locked = partn;
245 
246 	return (0);
247 
248 fail2:
249 	EFSYS_PROBE(fail2);
250 fail1:
251 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
252 
253 	return (rc);
254 }
255 
256 	__checkReturn		efx_rc_t
257 efx_nvram_read_chunk(
258 	__in			efx_nic_t *enp,
259 	__in			efx_nvram_type_t type,
260 	__in			unsigned int offset,
261 	__out_bcount(size)	caddr_t data,
262 	__in			size_t size)
263 {
264 	const efx_nvram_ops_t *envop = enp->en_envop;
265 	uint32_t partn;
266 	efx_rc_t rc;
267 
268 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
269 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
270 
271 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
272 		goto fail1;
273 
274 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn);
275 
276 	if ((rc = envop->envo_partn_read(enp, partn, offset, data, size)) != 0)
277 		goto fail2;
278 
279 	return (0);
280 
281 fail2:
282 	EFSYS_PROBE(fail2);
283 fail1:
284 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
285 
286 	return (rc);
287 }
288 
289 /*
290  * Read from the backup (writeable) store of an A/B partition.
291  * For non A/B partitions, there is only a single store, and so this
292  * function has the same behaviour as efx_nvram_read_chunk().
293  */
294 	__checkReturn		efx_rc_t
295 efx_nvram_read_backup(
296 	__in			efx_nic_t *enp,
297 	__in			efx_nvram_type_t type,
298 	__in			unsigned int offset,
299 	__out_bcount(size)	caddr_t data,
300 	__in			size_t size)
301 {
302 	const efx_nvram_ops_t *envop = enp->en_envop;
303 	uint32_t partn;
304 	efx_rc_t rc;
305 
306 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
307 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
308 
309 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
310 		goto fail1;
311 
312 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn);
313 
314 	if ((rc = envop->envo_partn_read_backup(enp, partn, offset,
315 		    data, size)) != 0)
316 		goto fail2;
317 
318 	return (0);
319 
320 fail2:
321 	EFSYS_PROBE(fail2);
322 fail1:
323 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
324 
325 	return (rc);
326 }
327 
328 	__checkReturn		efx_rc_t
329 efx_nvram_erase(
330 	__in			efx_nic_t *enp,
331 	__in			efx_nvram_type_t type)
332 {
333 	const efx_nvram_ops_t *envop = enp->en_envop;
334 	unsigned int offset = 0;
335 	size_t size = 0;
336 	uint32_t partn;
337 	efx_rc_t rc;
338 
339 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
340 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
341 
342 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
343 		goto fail1;
344 
345 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn);
346 
347 	if ((rc = envop->envo_partn_size(enp, partn, &size)) != 0)
348 		goto fail2;
349 
350 	if ((rc = envop->envo_partn_erase(enp, partn, offset, size)) != 0)
351 		goto fail3;
352 
353 	return (0);
354 
355 fail3:
356 	EFSYS_PROBE(fail3);
357 fail2:
358 	EFSYS_PROBE(fail2);
359 fail1:
360 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
361 
362 	return (rc);
363 }
364 
365 	__checkReturn		efx_rc_t
366 efx_nvram_write_chunk(
367 	__in			efx_nic_t *enp,
368 	__in			efx_nvram_type_t type,
369 	__in			unsigned int offset,
370 	__in_bcount(size)	caddr_t data,
371 	__in			size_t size)
372 {
373 	const efx_nvram_ops_t *envop = enp->en_envop;
374 	uint32_t partn;
375 	efx_rc_t rc;
376 
377 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
378 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
379 
380 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
381 		goto fail1;
382 
383 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn);
384 
385 	if ((rc = envop->envo_partn_write(enp, partn, offset, data, size)) != 0)
386 		goto fail2;
387 
388 	return (0);
389 
390 fail2:
391 	EFSYS_PROBE(fail2);
392 fail1:
393 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
394 
395 	return (rc);
396 }
397 
398 	__checkReturn		efx_rc_t
399 efx_nvram_rw_finish(
400 	__in			efx_nic_t *enp,
401 	__in			efx_nvram_type_t type,
402 	__out_opt		uint32_t *verify_resultp)
403 {
404 	const efx_nvram_ops_t *envop = enp->en_envop;
405 	uint32_t partn;
406 	uint32_t verify_result = 0;
407 	efx_rc_t rc;
408 
409 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
410 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
411 
412 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
413 		goto fail1;
414 
415 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn);
416 
417 	if ((rc = envop->envo_partn_rw_finish(enp, partn, &verify_result)) != 0)
418 		goto fail2;
419 
420 	enp->en_nvram_partn_locked = EFX_NVRAM_PARTN_INVALID;
421 
422 	if (verify_resultp != NULL)
423 		*verify_resultp = verify_result;
424 
425 	return (0);
426 
427 fail2:
428 	EFSYS_PROBE(fail2);
429 	enp->en_nvram_partn_locked = EFX_NVRAM_PARTN_INVALID;
430 
431 fail1:
432 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
433 
434 	/* Always report verification result */
435 	if (verify_resultp != NULL)
436 		*verify_resultp = verify_result;
437 
438 	return (rc);
439 }
440 
441 	__checkReturn		efx_rc_t
442 efx_nvram_set_version(
443 	__in			efx_nic_t *enp,
444 	__in			efx_nvram_type_t type,
445 	__in_ecount(4)		uint16_t version[4])
446 {
447 	const efx_nvram_ops_t *envop = enp->en_envop;
448 	uint32_t partn;
449 	efx_rc_t rc;
450 
451 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
452 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
453 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
454 
455 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
456 		goto fail1;
457 
458 	/*
459 	 * The Siena implementation of envo_set_version() will attempt to
460 	 * acquire the NVRAM_UPDATE lock for the DYNAMIC_CONFIG partition.
461 	 * Therefore, you can't have already acquired the NVRAM_UPDATE lock.
462 	 */
463 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, EFX_NVRAM_PARTN_INVALID);
464 
465 	if ((rc = envop->envo_partn_set_version(enp, partn, version)) != 0)
466 		goto fail2;
467 
468 	return (0);
469 
470 fail2:
471 	EFSYS_PROBE(fail2);
472 fail1:
473 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
474 
475 	return (rc);
476 }
477 
478 /* Validate buffer contents (before writing to flash) */
479 	__checkReturn		efx_rc_t
480 efx_nvram_validate(
481 	__in			efx_nic_t *enp,
482 	__in			efx_nvram_type_t type,
483 	__in_bcount(partn_size)	caddr_t partn_data,
484 	__in			size_t partn_size)
485 {
486 	const efx_nvram_ops_t *envop = enp->en_envop;
487 	uint32_t partn;
488 	efx_rc_t rc;
489 
490 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
491 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
492 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
493 
494 	if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0)
495 		goto fail1;
496 
497 	if (envop->envo_buffer_validate != NULL) {
498 		if ((rc = envop->envo_buffer_validate(partn,
499 			    partn_data, partn_size)) != 0)
500 			goto fail2;
501 	}
502 
503 	return (0);
504 
505 fail2:
506 	EFSYS_PROBE(fail2);
507 fail1:
508 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
509 
510 	return (rc);
511 }
512 
513 void
514 efx_nvram_fini(
515 	__in		efx_nic_t *enp)
516 {
517 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
518 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
519 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
520 
521 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, EFX_NVRAM_PARTN_INVALID);
522 
523 	enp->en_envop = NULL;
524 	enp->en_mod_flags &= ~EFX_MOD_NVRAM;
525 }
526 
527 #endif	/* EFSYS_OPT_NVRAM */
528 
529 #if EFSYS_OPT_NVRAM || EFSYS_OPT_VPD
530 
531 /*
532  * Internal MCDI request handling
533  */
534 
535 	__checkReturn		efx_rc_t
536 efx_mcdi_nvram_partitions(
537 	__in			efx_nic_t *enp,
538 	__out_bcount(size)	caddr_t data,
539 	__in			size_t size,
540 	__out			unsigned int *npartnp)
541 {
542 	efx_mcdi_req_t req;
543 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_PARTITIONS_IN_LEN,
544 		MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX);
545 	unsigned int npartn;
546 	efx_rc_t rc;
547 
548 	req.emr_cmd = MC_CMD_NVRAM_PARTITIONS;
549 	req.emr_in_buf = payload;
550 	req.emr_in_length = MC_CMD_NVRAM_PARTITIONS_IN_LEN;
551 	req.emr_out_buf = payload;
552 	req.emr_out_length = MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX;
553 
554 	efx_mcdi_execute(enp, &req);
555 
556 	if (req.emr_rc != 0) {
557 		rc = req.emr_rc;
558 		goto fail1;
559 	}
560 
561 	if (req.emr_out_length_used < MC_CMD_NVRAM_PARTITIONS_OUT_LENMIN) {
562 		rc = EMSGSIZE;
563 		goto fail2;
564 	}
565 	npartn = MCDI_OUT_DWORD(req, NVRAM_PARTITIONS_OUT_NUM_PARTITIONS);
566 
567 	if (req.emr_out_length_used < MC_CMD_NVRAM_PARTITIONS_OUT_LEN(npartn)) {
568 		rc = ENOENT;
569 		goto fail3;
570 	}
571 
572 	if (size < npartn * sizeof (uint32_t)) {
573 		rc = ENOSPC;
574 		goto fail3;
575 	}
576 
577 	*npartnp = npartn;
578 
579 	memcpy(data,
580 	    MCDI_OUT2(req, uint32_t, NVRAM_PARTITIONS_OUT_TYPE_ID),
581 	    (npartn * sizeof (uint32_t)));
582 
583 	return (0);
584 
585 fail3:
586 	EFSYS_PROBE(fail3);
587 fail2:
588 	EFSYS_PROBE(fail2);
589 fail1:
590 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
591 
592 	return (rc);
593 }
594 
595 	__checkReturn		efx_rc_t
596 efx_mcdi_nvram_metadata(
597 	__in			efx_nic_t *enp,
598 	__in			uint32_t partn,
599 	__out			uint32_t *subtypep,
600 	__out_ecount(4)		uint16_t version[4],
601 	__out_bcount_opt(size)	char *descp,
602 	__in			size_t size)
603 {
604 	efx_mcdi_req_t req;
605 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_METADATA_IN_LEN,
606 		MC_CMD_NVRAM_METADATA_OUT_LENMAX);
607 	efx_rc_t rc;
608 
609 	req.emr_cmd = MC_CMD_NVRAM_METADATA;
610 	req.emr_in_buf = payload;
611 	req.emr_in_length = MC_CMD_NVRAM_METADATA_IN_LEN;
612 	req.emr_out_buf = payload;
613 	req.emr_out_length = MC_CMD_NVRAM_METADATA_OUT_LENMAX;
614 
615 	MCDI_IN_SET_DWORD(req, NVRAM_METADATA_IN_TYPE, partn);
616 
617 	efx_mcdi_execute_quiet(enp, &req);
618 
619 	if (req.emr_rc != 0) {
620 		rc = req.emr_rc;
621 		goto fail1;
622 	}
623 
624 	if (req.emr_out_length_used < MC_CMD_NVRAM_METADATA_OUT_LENMIN) {
625 		rc = EMSGSIZE;
626 		goto fail2;
627 	}
628 
629 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
630 		NVRAM_METADATA_OUT_SUBTYPE_VALID)) {
631 		*subtypep = MCDI_OUT_DWORD(req, NVRAM_METADATA_OUT_SUBTYPE);
632 	} else {
633 		*subtypep = 0;
634 	}
635 
636 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
637 		NVRAM_METADATA_OUT_VERSION_VALID)) {
638 		version[0] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_W);
639 		version[1] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_X);
640 		version[2] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_Y);
641 		version[3] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_Z);
642 	} else {
643 		version[0] = version[1] = version[2] = version[3] = 0;
644 	}
645 
646 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
647 		NVRAM_METADATA_OUT_DESCRIPTION_VALID)) {
648 		/* Return optional descrition string */
649 		if ((descp != NULL) && (size > 0)) {
650 			size_t desclen;
651 
652 			descp[0] = '\0';
653 			desclen = (req.emr_out_length_used
654 			    - MC_CMD_NVRAM_METADATA_OUT_LEN(0));
655 
656 			EFSYS_ASSERT3U(desclen, <=,
657 			    MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM);
658 
659 			if (size < desclen) {
660 				rc = ENOSPC;
661 				goto fail3;
662 			}
663 
664 			memcpy(descp, MCDI_OUT2(req, char,
665 				NVRAM_METADATA_OUT_DESCRIPTION),
666 			    desclen);
667 
668 			/* Ensure string is NUL terminated */
669 			descp[desclen] = '\0';
670 		}
671 	}
672 
673 	return (0);
674 
675 fail3:
676 	EFSYS_PROBE(fail3);
677 fail2:
678 	EFSYS_PROBE(fail2);
679 fail1:
680 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
681 
682 	return (rc);
683 }
684 
685 	__checkReturn		efx_rc_t
686 efx_mcdi_nvram_info(
687 	__in			efx_nic_t *enp,
688 	__in			uint32_t partn,
689 	__out_opt		size_t *sizep,
690 	__out_opt		uint32_t *addressp,
691 	__out_opt		uint32_t *erase_sizep,
692 	__out_opt		uint32_t *write_sizep)
693 {
694 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_INFO_IN_LEN,
695 		MC_CMD_NVRAM_INFO_V2_OUT_LEN);
696 	efx_mcdi_req_t req;
697 	efx_rc_t rc;
698 
699 	req.emr_cmd = MC_CMD_NVRAM_INFO;
700 	req.emr_in_buf = payload;
701 	req.emr_in_length = MC_CMD_NVRAM_INFO_IN_LEN;
702 	req.emr_out_buf = payload;
703 	req.emr_out_length = MC_CMD_NVRAM_INFO_V2_OUT_LEN;
704 
705 	MCDI_IN_SET_DWORD(req, NVRAM_INFO_IN_TYPE, partn);
706 
707 	efx_mcdi_execute_quiet(enp, &req);
708 
709 	if (req.emr_rc != 0) {
710 		rc = req.emr_rc;
711 		goto fail1;
712 	}
713 
714 	if (req.emr_out_length_used < MC_CMD_NVRAM_INFO_OUT_LEN) {
715 		rc = EMSGSIZE;
716 		goto fail2;
717 	}
718 
719 	if (sizep)
720 		*sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_SIZE);
721 
722 	if (addressp)
723 		*addressp = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_PHYSADDR);
724 
725 	if (erase_sizep)
726 		*erase_sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_ERASESIZE);
727 
728 	if (write_sizep) {
729 		*write_sizep =
730 			(req.emr_out_length_used <
731 			    MC_CMD_NVRAM_INFO_V2_OUT_LEN) ?
732 			0 : MCDI_OUT_DWORD(req, NVRAM_INFO_V2_OUT_WRITESIZE);
733 	}
734 
735 	return (0);
736 
737 fail2:
738 	EFSYS_PROBE(fail2);
739 fail1:
740 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
741 
742 	return (rc);
743 }
744 
745 /*
746  * MC_CMD_NVRAM_UPDATE_START_V2 must be used to support firmware-verified
747  * NVRAM updates. Older firmware will ignore the flags field in the request.
748  */
749 	__checkReturn		efx_rc_t
750 efx_mcdi_nvram_update_start(
751 	__in			efx_nic_t *enp,
752 	__in			uint32_t partn)
753 {
754 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN,
755 		MC_CMD_NVRAM_UPDATE_START_OUT_LEN);
756 	efx_mcdi_req_t req;
757 	efx_rc_t rc;
758 
759 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_START;
760 	req.emr_in_buf = payload;
761 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN;
762 	req.emr_out_buf = payload;
763 	req.emr_out_length = MC_CMD_NVRAM_UPDATE_START_OUT_LEN;
764 
765 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_START_V2_IN_TYPE, partn);
766 
767 	MCDI_IN_POPULATE_DWORD_1(req, NVRAM_UPDATE_START_V2_IN_FLAGS,
768 	    NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT, 1);
769 
770 	efx_mcdi_execute(enp, &req);
771 
772 	if (req.emr_rc != 0) {
773 		rc = req.emr_rc;
774 		goto fail1;
775 	}
776 
777 	return (0);
778 
779 fail1:
780 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
781 
782 	return (rc);
783 }
784 
785 	__checkReturn		efx_rc_t
786 efx_mcdi_nvram_read(
787 	__in			efx_nic_t *enp,
788 	__in			uint32_t partn,
789 	__in			uint32_t offset,
790 	__out_bcount(size)	caddr_t data,
791 	__in			size_t size,
792 	__in			uint32_t mode)
793 {
794 	efx_mcdi_req_t req;
795 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_READ_IN_V2_LEN,
796 		MC_CMD_NVRAM_READ_OUT_LENMAX);
797 	efx_rc_t rc;
798 
799 	if (size > MC_CMD_NVRAM_READ_OUT_LENMAX) {
800 		rc = EINVAL;
801 		goto fail1;
802 	}
803 
804 	req.emr_cmd = MC_CMD_NVRAM_READ;
805 	req.emr_in_buf = payload;
806 	req.emr_in_length = MC_CMD_NVRAM_READ_IN_V2_LEN;
807 	req.emr_out_buf = payload;
808 	req.emr_out_length = MC_CMD_NVRAM_READ_OUT_LENMAX;
809 
810 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_TYPE, partn);
811 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_OFFSET, offset);
812 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_LENGTH, size);
813 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_MODE, mode);
814 
815 	efx_mcdi_execute(enp, &req);
816 
817 	if (req.emr_rc != 0) {
818 		rc = req.emr_rc;
819 		goto fail1;
820 	}
821 
822 	if (req.emr_out_length_used < MC_CMD_NVRAM_READ_OUT_LEN(size)) {
823 		rc = EMSGSIZE;
824 		goto fail2;
825 	}
826 
827 	memcpy(data,
828 	    MCDI_OUT2(req, uint8_t, NVRAM_READ_OUT_READ_BUFFER),
829 	    size);
830 
831 	return (0);
832 
833 fail2:
834 	EFSYS_PROBE(fail2);
835 fail1:
836 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
837 
838 	return (rc);
839 }
840 
841 	__checkReturn		efx_rc_t
842 efx_mcdi_nvram_erase(
843 	__in			efx_nic_t *enp,
844 	__in			uint32_t partn,
845 	__in			uint32_t offset,
846 	__in			size_t size)
847 {
848 	efx_mcdi_req_t req;
849 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_ERASE_IN_LEN,
850 		MC_CMD_NVRAM_ERASE_OUT_LEN);
851 	efx_rc_t rc;
852 
853 	req.emr_cmd = MC_CMD_NVRAM_ERASE;
854 	req.emr_in_buf = payload;
855 	req.emr_in_length = MC_CMD_NVRAM_ERASE_IN_LEN;
856 	req.emr_out_buf = payload;
857 	req.emr_out_length = MC_CMD_NVRAM_ERASE_OUT_LEN;
858 
859 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_TYPE, partn);
860 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_OFFSET, offset);
861 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_LENGTH, size);
862 
863 	efx_mcdi_execute(enp, &req);
864 
865 	if (req.emr_rc != 0) {
866 		rc = req.emr_rc;
867 		goto fail1;
868 	}
869 
870 	return (0);
871 
872 fail1:
873 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
874 
875 	return (rc);
876 }
877 
878 /*
879  * The NVRAM_WRITE MCDI command is a V1 command and so is supported by both
880  * Sienna and EF10 based boards.  However EF10 based boards support the use
881  * of this command with payloads up to the maximum MCDI V2 payload length.
882  */
883 	__checkReturn		efx_rc_t
884 efx_mcdi_nvram_write(
885 	__in			efx_nic_t *enp,
886 	__in			uint32_t partn,
887 	__in			uint32_t offset,
888 	__in_bcount(size)	caddr_t data,
889 	__in			size_t size)
890 {
891 	efx_mcdi_req_t req;
892 	uint8_t *payload;
893 	efx_rc_t rc;
894 	size_t max_data_size;
895 	size_t payload_len = enp->en_nic_cfg.enc_mcdi_max_payload_length;
896 
897 	max_data_size = payload_len - MC_CMD_NVRAM_WRITE_IN_LEN(0);
898 	EFSYS_ASSERT3U(payload_len, >, 0);
899 	EFSYS_ASSERT3U(max_data_size, <, payload_len);
900 
901 	if (size > max_data_size) {
902 		rc = EINVAL;
903 		goto fail1;
904 	}
905 
906 	EFSYS_KMEM_ALLOC(enp->en_esip, payload_len, payload);
907 	if (payload == NULL) {
908 		rc = ENOMEM;
909 		goto fail2;
910 	}
911 
912 	(void) memset(payload, 0, payload_len);
913 	req.emr_cmd = MC_CMD_NVRAM_WRITE;
914 	req.emr_in_buf = payload;
915 	req.emr_in_length = MC_CMD_NVRAM_WRITE_IN_LEN(size);
916 	req.emr_out_buf = payload;
917 	req.emr_out_length = MC_CMD_NVRAM_WRITE_OUT_LEN;
918 
919 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_TYPE, partn);
920 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_OFFSET, offset);
921 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_LENGTH, size);
922 
923 	memcpy(MCDI_IN2(req, uint8_t, NVRAM_WRITE_IN_WRITE_BUFFER),
924 	    data, size);
925 
926 	efx_mcdi_execute(enp, &req);
927 
928 	if (req.emr_rc != 0) {
929 		rc = req.emr_rc;
930 		goto fail3;
931 	}
932 
933 	EFSYS_KMEM_FREE(enp->en_esip, payload_len, payload);
934 
935 	return (0);
936 
937 fail3:
938 	EFSYS_PROBE(fail3);
939 	EFSYS_KMEM_FREE(enp->en_esip, payload_len, payload);
940 fail2:
941 	EFSYS_PROBE(fail2);
942 fail1:
943 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
944 
945 	return (rc);
946 }
947 
948 /*
949  * MC_CMD_NVRAM_UPDATE_FINISH_V2 must be used to support firmware-verified
950  * NVRAM updates. Older firmware will ignore the flags field in the request.
951  */
952 	__checkReturn		efx_rc_t
953 efx_mcdi_nvram_update_finish(
954 	__in			efx_nic_t *enp,
955 	__in			uint32_t partn,
956 	__in			boolean_t reboot,
957 	__out_opt		uint32_t *verify_resultp)
958 {
959 	const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
960 	efx_mcdi_req_t req;
961 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN,
962 		MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
963 	uint32_t verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
964 	efx_rc_t rc;
965 
966 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_FINISH;
967 	req.emr_in_buf = payload;
968 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN;
969 	req.emr_out_buf = payload;
970 	req.emr_out_length = MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN;
971 
972 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_V2_IN_TYPE, partn);
973 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_V2_IN_REBOOT, reboot);
974 
975 	MCDI_IN_POPULATE_DWORD_1(req, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
976 	    NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT, 1);
977 
978 	efx_mcdi_execute(enp, &req);
979 
980 	if (req.emr_rc != 0) {
981 		rc = req.emr_rc;
982 		goto fail1;
983 	}
984 
985 	if (req.emr_out_length_used < MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
986 		verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
987 		if (encp->enc_nvram_update_verify_result_supported) {
988 			/* Result of update verification is missing */
989 			rc = EMSGSIZE;
990 			goto fail2;
991 		}
992 	} else {
993 		verify_result =
994 		    MCDI_OUT_DWORD(req, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
995 	}
996 
997 	if ((encp->enc_nvram_update_verify_result_supported) &&
998 	    (verify_result != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)) {
999 		/* Update verification failed */
1000 		rc = EINVAL;
1001 		goto fail3;
1002 	}
1003 
1004 	if (verify_resultp != NULL)
1005 		*verify_resultp = verify_result;
1006 
1007 	return (0);
1008 
1009 fail3:
1010 	EFSYS_PROBE(fail3);
1011 fail2:
1012 	EFSYS_PROBE(fail2);
1013 fail1:
1014 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1015 
1016 	/* Always report verification result */
1017 	if (verify_resultp != NULL)
1018 		*verify_resultp = verify_result;
1019 
1020 	return (rc);
1021 }
1022 
1023 #if EFSYS_OPT_DIAG
1024 
1025 	__checkReturn		efx_rc_t
1026 efx_mcdi_nvram_test(
1027 	__in			efx_nic_t *enp,
1028 	__in			uint32_t partn)
1029 {
1030 	efx_mcdi_req_t req;
1031 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_TEST_IN_LEN,
1032 		MC_CMD_NVRAM_TEST_OUT_LEN);
1033 	int result;
1034 	efx_rc_t rc;
1035 
1036 	req.emr_cmd = MC_CMD_NVRAM_TEST;
1037 	req.emr_in_buf = payload;
1038 	req.emr_in_length = MC_CMD_NVRAM_TEST_IN_LEN;
1039 	req.emr_out_buf = payload;
1040 	req.emr_out_length = MC_CMD_NVRAM_TEST_OUT_LEN;
1041 
1042 	MCDI_IN_SET_DWORD(req, NVRAM_TEST_IN_TYPE, partn);
1043 
1044 	efx_mcdi_execute(enp, &req);
1045 
1046 	if (req.emr_rc != 0) {
1047 		rc = req.emr_rc;
1048 		goto fail1;
1049 	}
1050 
1051 	if (req.emr_out_length_used < MC_CMD_NVRAM_TEST_OUT_LEN) {
1052 		rc = EMSGSIZE;
1053 		goto fail2;
1054 	}
1055 
1056 	result = MCDI_OUT_DWORD(req, NVRAM_TEST_OUT_RESULT);
1057 	if (result == MC_CMD_NVRAM_TEST_FAIL) {
1058 		EFSYS_PROBE1(nvram_test_failure, int, partn);
1059 
1060 		rc = (EINVAL);
1061 		goto fail3;
1062 	}
1063 
1064 	return (0);
1065 
1066 fail3:
1067 	EFSYS_PROBE(fail3);
1068 fail2:
1069 	EFSYS_PROBE(fail2);
1070 fail1:
1071 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1072 
1073 	return (rc);
1074 }
1075 
1076 #endif	/* EFSYS_OPT_DIAG */
1077 
1078 #endif /* EFSYS_OPT_NVRAM || EFSYS_OPT_VPD */
1079