xref: /freebsd/sys/dev/sfxge/common/efx_nvram.c (revision 32100375a661c1e16588ddfa7b90ca8d26cb9786)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
516 void
517 efx_nvram_fini(
518 	__in		efx_nic_t *enp)
519 {
520 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
521 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
522 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM);
523 
524 	EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, EFX_NVRAM_PARTN_INVALID);
525 
526 	enp->en_envop = NULL;
527 	enp->en_mod_flags &= ~EFX_MOD_NVRAM;
528 }
529 
530 #endif	/* EFSYS_OPT_NVRAM */
531 
532 #if EFSYS_OPT_NVRAM || EFSYS_OPT_VPD
533 
534 /*
535  * Internal MCDI request handling
536  */
537 
538 	__checkReturn		efx_rc_t
539 efx_mcdi_nvram_partitions(
540 	__in			efx_nic_t *enp,
541 	__out_bcount(size)	caddr_t data,
542 	__in			size_t size,
543 	__out			unsigned int *npartnp)
544 {
545 	efx_mcdi_req_t req;
546 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_PARTITIONS_IN_LEN,
547 		MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX);
548 	unsigned int npartn;
549 	efx_rc_t rc;
550 
551 	req.emr_cmd = MC_CMD_NVRAM_PARTITIONS;
552 	req.emr_in_buf = payload;
553 	req.emr_in_length = MC_CMD_NVRAM_PARTITIONS_IN_LEN;
554 	req.emr_out_buf = payload;
555 	req.emr_out_length = MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX;
556 
557 	efx_mcdi_execute(enp, &req);
558 
559 	if (req.emr_rc != 0) {
560 		rc = req.emr_rc;
561 		goto fail1;
562 	}
563 
564 	if (req.emr_out_length_used < MC_CMD_NVRAM_PARTITIONS_OUT_LENMIN) {
565 		rc = EMSGSIZE;
566 		goto fail2;
567 	}
568 	npartn = MCDI_OUT_DWORD(req, NVRAM_PARTITIONS_OUT_NUM_PARTITIONS);
569 
570 	if (req.emr_out_length_used < MC_CMD_NVRAM_PARTITIONS_OUT_LEN(npartn)) {
571 		rc = ENOENT;
572 		goto fail3;
573 	}
574 
575 	if (size < npartn * sizeof (uint32_t)) {
576 		rc = ENOSPC;
577 		goto fail3;
578 	}
579 
580 	*npartnp = npartn;
581 
582 	memcpy(data,
583 	    MCDI_OUT2(req, uint32_t, NVRAM_PARTITIONS_OUT_TYPE_ID),
584 	    (npartn * sizeof (uint32_t)));
585 
586 	return (0);
587 
588 fail3:
589 	EFSYS_PROBE(fail3);
590 fail2:
591 	EFSYS_PROBE(fail2);
592 fail1:
593 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
594 
595 	return (rc);
596 }
597 
598 	__checkReturn		efx_rc_t
599 efx_mcdi_nvram_metadata(
600 	__in			efx_nic_t *enp,
601 	__in			uint32_t partn,
602 	__out			uint32_t *subtypep,
603 	__out_ecount(4)		uint16_t version[4],
604 	__out_bcount_opt(size)	char *descp,
605 	__in			size_t size)
606 {
607 	efx_mcdi_req_t req;
608 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_METADATA_IN_LEN,
609 		MC_CMD_NVRAM_METADATA_OUT_LENMAX);
610 	efx_rc_t rc;
611 
612 	req.emr_cmd = MC_CMD_NVRAM_METADATA;
613 	req.emr_in_buf = payload;
614 	req.emr_in_length = MC_CMD_NVRAM_METADATA_IN_LEN;
615 	req.emr_out_buf = payload;
616 	req.emr_out_length = MC_CMD_NVRAM_METADATA_OUT_LENMAX;
617 
618 	MCDI_IN_SET_DWORD(req, NVRAM_METADATA_IN_TYPE, partn);
619 
620 	efx_mcdi_execute_quiet(enp, &req);
621 
622 	if (req.emr_rc != 0) {
623 		rc = req.emr_rc;
624 		goto fail1;
625 	}
626 
627 	if (req.emr_out_length_used < MC_CMD_NVRAM_METADATA_OUT_LENMIN) {
628 		rc = EMSGSIZE;
629 		goto fail2;
630 	}
631 
632 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
633 		NVRAM_METADATA_OUT_SUBTYPE_VALID)) {
634 		*subtypep = MCDI_OUT_DWORD(req, NVRAM_METADATA_OUT_SUBTYPE);
635 	} else {
636 		*subtypep = 0;
637 	}
638 
639 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
640 		NVRAM_METADATA_OUT_VERSION_VALID)) {
641 		version[0] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_W);
642 		version[1] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_X);
643 		version[2] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_Y);
644 		version[3] = MCDI_OUT_WORD(req, NVRAM_METADATA_OUT_VERSION_Z);
645 	} else {
646 		version[0] = version[1] = version[2] = version[3] = 0;
647 	}
648 
649 	if (MCDI_OUT_DWORD_FIELD(req, NVRAM_METADATA_OUT_FLAGS,
650 		NVRAM_METADATA_OUT_DESCRIPTION_VALID)) {
651 		/* Return optional descrition string */
652 		if ((descp != NULL) && (size > 0)) {
653 			size_t desclen;
654 
655 			descp[0] = '\0';
656 			desclen = (req.emr_out_length_used
657 			    - MC_CMD_NVRAM_METADATA_OUT_LEN(0));
658 
659 			EFSYS_ASSERT3U(desclen, <=,
660 			    MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM);
661 
662 			if (size < desclen) {
663 				rc = ENOSPC;
664 				goto fail3;
665 			}
666 
667 			memcpy(descp, MCDI_OUT2(req, char,
668 				NVRAM_METADATA_OUT_DESCRIPTION),
669 			    desclen);
670 
671 			/* Ensure string is NUL terminated */
672 			descp[desclen] = '\0';
673 		}
674 	}
675 
676 	return (0);
677 
678 fail3:
679 	EFSYS_PROBE(fail3);
680 fail2:
681 	EFSYS_PROBE(fail2);
682 fail1:
683 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
684 
685 	return (rc);
686 }
687 
688 	__checkReturn		efx_rc_t
689 efx_mcdi_nvram_info(
690 	__in			efx_nic_t *enp,
691 	__in			uint32_t partn,
692 	__out_opt		size_t *sizep,
693 	__out_opt		uint32_t *addressp,
694 	__out_opt		uint32_t *erase_sizep,
695 	__out_opt		uint32_t *write_sizep)
696 {
697 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_INFO_IN_LEN,
698 		MC_CMD_NVRAM_INFO_V2_OUT_LEN);
699 	efx_mcdi_req_t req;
700 	efx_rc_t rc;
701 
702 	req.emr_cmd = MC_CMD_NVRAM_INFO;
703 	req.emr_in_buf = payload;
704 	req.emr_in_length = MC_CMD_NVRAM_INFO_IN_LEN;
705 	req.emr_out_buf = payload;
706 	req.emr_out_length = MC_CMD_NVRAM_INFO_V2_OUT_LEN;
707 
708 	MCDI_IN_SET_DWORD(req, NVRAM_INFO_IN_TYPE, partn);
709 
710 	efx_mcdi_execute_quiet(enp, &req);
711 
712 	if (req.emr_rc != 0) {
713 		rc = req.emr_rc;
714 		goto fail1;
715 	}
716 
717 	if (req.emr_out_length_used < MC_CMD_NVRAM_INFO_OUT_LEN) {
718 		rc = EMSGSIZE;
719 		goto fail2;
720 	}
721 
722 	if (sizep)
723 		*sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_SIZE);
724 
725 	if (addressp)
726 		*addressp = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_PHYSADDR);
727 
728 	if (erase_sizep)
729 		*erase_sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_ERASESIZE);
730 
731 	if (write_sizep) {
732 		*write_sizep =
733 			(req.emr_out_length_used <
734 			    MC_CMD_NVRAM_INFO_V2_OUT_LEN) ?
735 			0 : MCDI_OUT_DWORD(req, NVRAM_INFO_V2_OUT_WRITESIZE);
736 	}
737 
738 	return (0);
739 
740 fail2:
741 	EFSYS_PROBE(fail2);
742 fail1:
743 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
744 
745 	return (rc);
746 }
747 
748 /*
749  * MC_CMD_NVRAM_UPDATE_START_V2 must be used to support firmware-verified
750  * NVRAM updates. Older firmware will ignore the flags field in the request.
751  */
752 	__checkReturn		efx_rc_t
753 efx_mcdi_nvram_update_start(
754 	__in			efx_nic_t *enp,
755 	__in			uint32_t partn)
756 {
757 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN,
758 		MC_CMD_NVRAM_UPDATE_START_OUT_LEN);
759 	efx_mcdi_req_t req;
760 	efx_rc_t rc;
761 
762 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_START;
763 	req.emr_in_buf = payload;
764 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN;
765 	req.emr_out_buf = payload;
766 	req.emr_out_length = MC_CMD_NVRAM_UPDATE_START_OUT_LEN;
767 
768 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_START_V2_IN_TYPE, partn);
769 
770 	MCDI_IN_POPULATE_DWORD_1(req, NVRAM_UPDATE_START_V2_IN_FLAGS,
771 	    NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT, 1);
772 
773 	efx_mcdi_execute(enp, &req);
774 
775 	if (req.emr_rc != 0) {
776 		rc = req.emr_rc;
777 		goto fail1;
778 	}
779 
780 	return (0);
781 
782 fail1:
783 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
784 
785 	return (rc);
786 }
787 
788 	__checkReturn		efx_rc_t
789 efx_mcdi_nvram_read(
790 	__in			efx_nic_t *enp,
791 	__in			uint32_t partn,
792 	__in			uint32_t offset,
793 	__out_bcount(size)	caddr_t data,
794 	__in			size_t size,
795 	__in			uint32_t mode)
796 {
797 	efx_mcdi_req_t req;
798 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_READ_IN_V2_LEN,
799 		MC_CMD_NVRAM_READ_OUT_LENMAX);
800 	efx_rc_t rc;
801 
802 	if (size > MC_CMD_NVRAM_READ_OUT_LENMAX) {
803 		rc = EINVAL;
804 		goto fail1;
805 	}
806 
807 	req.emr_cmd = MC_CMD_NVRAM_READ;
808 	req.emr_in_buf = payload;
809 	req.emr_in_length = MC_CMD_NVRAM_READ_IN_V2_LEN;
810 	req.emr_out_buf = payload;
811 	req.emr_out_length = MC_CMD_NVRAM_READ_OUT_LENMAX;
812 
813 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_TYPE, partn);
814 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_OFFSET, offset);
815 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_LENGTH, size);
816 	MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_V2_MODE, mode);
817 
818 	efx_mcdi_execute(enp, &req);
819 
820 	if (req.emr_rc != 0) {
821 		rc = req.emr_rc;
822 		goto fail1;
823 	}
824 
825 	if (req.emr_out_length_used < MC_CMD_NVRAM_READ_OUT_LEN(size)) {
826 		rc = EMSGSIZE;
827 		goto fail2;
828 	}
829 
830 	memcpy(data,
831 	    MCDI_OUT2(req, uint8_t, NVRAM_READ_OUT_READ_BUFFER),
832 	    size);
833 
834 	return (0);
835 
836 fail2:
837 	EFSYS_PROBE(fail2);
838 fail1:
839 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
840 
841 	return (rc);
842 }
843 
844 	__checkReturn		efx_rc_t
845 efx_mcdi_nvram_erase(
846 	__in			efx_nic_t *enp,
847 	__in			uint32_t partn,
848 	__in			uint32_t offset,
849 	__in			size_t size)
850 {
851 	efx_mcdi_req_t req;
852 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_ERASE_IN_LEN,
853 		MC_CMD_NVRAM_ERASE_OUT_LEN);
854 	efx_rc_t rc;
855 
856 	req.emr_cmd = MC_CMD_NVRAM_ERASE;
857 	req.emr_in_buf = payload;
858 	req.emr_in_length = MC_CMD_NVRAM_ERASE_IN_LEN;
859 	req.emr_out_buf = payload;
860 	req.emr_out_length = MC_CMD_NVRAM_ERASE_OUT_LEN;
861 
862 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_TYPE, partn);
863 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_OFFSET, offset);
864 	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_LENGTH, size);
865 
866 	efx_mcdi_execute(enp, &req);
867 
868 	if (req.emr_rc != 0) {
869 		rc = req.emr_rc;
870 		goto fail1;
871 	}
872 
873 	return (0);
874 
875 fail1:
876 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
877 
878 	return (rc);
879 }
880 
881 /*
882  * The NVRAM_WRITE MCDI command is a V1 command and so is supported by both
883  * Sienna and EF10 based boards.  However EF10 based boards support the use
884  * of this command with payloads up to the maximum MCDI V2 payload length.
885  */
886 	__checkReturn		efx_rc_t
887 efx_mcdi_nvram_write(
888 	__in			efx_nic_t *enp,
889 	__in			uint32_t partn,
890 	__in			uint32_t offset,
891 	__in_bcount(size)	caddr_t data,
892 	__in			size_t size)
893 {
894 	efx_mcdi_req_t req;
895 	uint8_t *payload;
896 	efx_rc_t rc;
897 	size_t max_data_size;
898 	size_t payload_len = enp->en_nic_cfg.enc_mcdi_max_payload_length;
899 
900 	max_data_size = payload_len - MC_CMD_NVRAM_WRITE_IN_LEN(0);
901 	EFSYS_ASSERT3U(payload_len, >, 0);
902 	EFSYS_ASSERT3U(max_data_size, <, payload_len);
903 
904 	if (size > max_data_size) {
905 		rc = EINVAL;
906 		goto fail1;
907 	}
908 
909 	EFSYS_KMEM_ALLOC(enp->en_esip, payload_len, payload);
910 	if (payload == NULL) {
911 		rc = ENOMEM;
912 		goto fail2;
913 	}
914 
915 	(void) memset(payload, 0, payload_len);
916 	req.emr_cmd = MC_CMD_NVRAM_WRITE;
917 	req.emr_in_buf = payload;
918 	req.emr_in_length = MC_CMD_NVRAM_WRITE_IN_LEN(size);
919 	req.emr_out_buf = payload;
920 	req.emr_out_length = MC_CMD_NVRAM_WRITE_OUT_LEN;
921 
922 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_TYPE, partn);
923 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_OFFSET, offset);
924 	MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_LENGTH, size);
925 
926 	memcpy(MCDI_IN2(req, uint8_t, NVRAM_WRITE_IN_WRITE_BUFFER),
927 	    data, size);
928 
929 	efx_mcdi_execute(enp, &req);
930 
931 	if (req.emr_rc != 0) {
932 		rc = req.emr_rc;
933 		goto fail3;
934 	}
935 
936 	EFSYS_KMEM_FREE(enp->en_esip, payload_len, payload);
937 
938 	return (0);
939 
940 fail3:
941 	EFSYS_PROBE(fail3);
942 	EFSYS_KMEM_FREE(enp->en_esip, payload_len, payload);
943 fail2:
944 	EFSYS_PROBE(fail2);
945 fail1:
946 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
947 
948 	return (rc);
949 }
950 
951 
952 /*
953  * MC_CMD_NVRAM_UPDATE_FINISH_V2 must be used to support firmware-verified
954  * NVRAM updates. Older firmware will ignore the flags field in the request.
955  */
956 	__checkReturn		efx_rc_t
957 efx_mcdi_nvram_update_finish(
958 	__in			efx_nic_t *enp,
959 	__in			uint32_t partn,
960 	__in			boolean_t reboot,
961 	__out_opt		uint32_t *verify_resultp)
962 {
963 	const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
964 	efx_mcdi_req_t req;
965 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN,
966 		MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
967 	uint32_t verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
968 	efx_rc_t rc;
969 
970 	req.emr_cmd = MC_CMD_NVRAM_UPDATE_FINISH;
971 	req.emr_in_buf = payload;
972 	req.emr_in_length = MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN;
973 	req.emr_out_buf = payload;
974 	req.emr_out_length = MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN;
975 
976 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_V2_IN_TYPE, partn);
977 	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_V2_IN_REBOOT, reboot);
978 
979 	MCDI_IN_POPULATE_DWORD_1(req, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
980 	    NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT, 1);
981 
982 	efx_mcdi_execute(enp, &req);
983 
984 	if (req.emr_rc != 0) {
985 		rc = req.emr_rc;
986 		goto fail1;
987 	}
988 
989 	if (req.emr_out_length_used < MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
990 		verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
991 		if (encp->enc_nvram_update_verify_result_supported) {
992 			/* Result of update verification is missing */
993 			rc = EMSGSIZE;
994 			goto fail2;
995 		}
996 	} else {
997 		verify_result =
998 		    MCDI_OUT_DWORD(req, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
999 	}
1000 
1001 	if ((encp->enc_nvram_update_verify_result_supported) &&
1002 	    (verify_result != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)) {
1003 		/* Update verification failed */
1004 		rc = EINVAL;
1005 		goto fail3;
1006 	}
1007 
1008 	if (verify_resultp != NULL)
1009 		*verify_resultp = verify_result;
1010 
1011 	return (0);
1012 
1013 fail3:
1014 	EFSYS_PROBE(fail3);
1015 fail2:
1016 	EFSYS_PROBE(fail2);
1017 fail1:
1018 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1019 
1020 	/* Always report verification result */
1021 	if (verify_resultp != NULL)
1022 		*verify_resultp = verify_result;
1023 
1024 	return (rc);
1025 }
1026 
1027 #if EFSYS_OPT_DIAG
1028 
1029 	__checkReturn		efx_rc_t
1030 efx_mcdi_nvram_test(
1031 	__in			efx_nic_t *enp,
1032 	__in			uint32_t partn)
1033 {
1034 	efx_mcdi_req_t req;
1035 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_NVRAM_TEST_IN_LEN,
1036 		MC_CMD_NVRAM_TEST_OUT_LEN);
1037 	int result;
1038 	efx_rc_t rc;
1039 
1040 	req.emr_cmd = MC_CMD_NVRAM_TEST;
1041 	req.emr_in_buf = payload;
1042 	req.emr_in_length = MC_CMD_NVRAM_TEST_IN_LEN;
1043 	req.emr_out_buf = payload;
1044 	req.emr_out_length = MC_CMD_NVRAM_TEST_OUT_LEN;
1045 
1046 	MCDI_IN_SET_DWORD(req, NVRAM_TEST_IN_TYPE, partn);
1047 
1048 	efx_mcdi_execute(enp, &req);
1049 
1050 	if (req.emr_rc != 0) {
1051 		rc = req.emr_rc;
1052 		goto fail1;
1053 	}
1054 
1055 	if (req.emr_out_length_used < MC_CMD_NVRAM_TEST_OUT_LEN) {
1056 		rc = EMSGSIZE;
1057 		goto fail2;
1058 	}
1059 
1060 	result = MCDI_OUT_DWORD(req, NVRAM_TEST_OUT_RESULT);
1061 	if (result == MC_CMD_NVRAM_TEST_FAIL) {
1062 
1063 		EFSYS_PROBE1(nvram_test_failure, int, partn);
1064 
1065 		rc = (EINVAL);
1066 		goto fail3;
1067 	}
1068 
1069 	return (0);
1070 
1071 fail3:
1072 	EFSYS_PROBE(fail3);
1073 fail2:
1074 	EFSYS_PROBE(fail2);
1075 fail1:
1076 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1077 
1078 	return (rc);
1079 }
1080 
1081 #endif	/* EFSYS_OPT_DIAG */
1082 
1083 
1084 #endif /* EFSYS_OPT_NVRAM || EFSYS_OPT_VPD */
1085