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