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
43static 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
64static 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
84efx_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
132fail1:
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
141efx_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
155fail1:
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
164efx_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
184fail2:
185	EFSYS_PROBE(fail2);
186fail1:
187	EFSYS_PROBE1(fail1, efx_rc_t, rc);
188	*sizep = 0;
189
190	return (rc);
191}
192
193	__checkReturn		efx_rc_t
194efx_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
217fail2:
218	EFSYS_PROBE(fail2);
219fail1:
220	EFSYS_PROBE1(fail1, efx_rc_t, rc);
221
222	return (rc);
223}
224
225	__checkReturn		efx_rc_t
226efx_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
250fail2:
251	EFSYS_PROBE(fail2);
252fail1:
253	EFSYS_PROBE1(fail1, efx_rc_t, rc);
254
255	return (rc);
256}
257
258	__checkReturn		efx_rc_t
259efx_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
283fail2:
284	EFSYS_PROBE(fail2);
285fail1:
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
297efx_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
322fail2:
323	EFSYS_PROBE(fail2);
324fail1:
325	EFSYS_PROBE1(fail1, efx_rc_t, rc);
326
327	return (rc);
328}
329
330	__checkReturn		efx_rc_t
331efx_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
357fail3:
358	EFSYS_PROBE(fail3);
359fail2:
360	EFSYS_PROBE(fail2);
361fail1:
362	EFSYS_PROBE1(fail1, efx_rc_t, rc);
363
364	return (rc);
365}
366
367	__checkReturn		efx_rc_t
368efx_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
392fail2:
393	EFSYS_PROBE(fail2);
394fail1:
395	EFSYS_PROBE1(fail1, efx_rc_t, rc);
396
397	return (rc);
398}
399
400	__checkReturn		efx_rc_t
401efx_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
429fail2:
430	EFSYS_PROBE(fail2);
431	enp->en_nvram_partn_locked = EFX_NVRAM_PARTN_INVALID;
432
433fail1:
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
444efx_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
472fail2:
473	EFSYS_PROBE(fail2);
474fail1:
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
482efx_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
507fail2:
508	EFSYS_PROBE(fail2);
509fail1:
510	EFSYS_PROBE1(fail1, efx_rc_t, rc);
511
512	return (rc);
513}
514
515void
516efx_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
538efx_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
587fail3:
588	EFSYS_PROBE(fail3);
589fail2:
590	EFSYS_PROBE(fail2);
591fail1:
592	EFSYS_PROBE1(fail1, efx_rc_t, rc);
593
594	return (rc);
595}
596
597	__checkReturn		efx_rc_t
598efx_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
677fail3:
678	EFSYS_PROBE(fail3);
679fail2:
680	EFSYS_PROBE(fail2);
681fail1:
682	EFSYS_PROBE1(fail1, efx_rc_t, rc);
683
684	return (rc);
685}
686
687	__checkReturn		efx_rc_t
688efx_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
739fail2:
740	EFSYS_PROBE(fail2);
741fail1:
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
752efx_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
781fail1:
782	EFSYS_PROBE1(fail1, efx_rc_t, rc);
783
784	return (rc);
785}
786
787	__checkReturn		efx_rc_t
788efx_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
835fail2:
836	EFSYS_PROBE(fail2);
837fail1:
838	EFSYS_PROBE1(fail1, efx_rc_t, rc);
839
840	return (rc);
841}
842
843	__checkReturn		efx_rc_t
844efx_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
874fail1:
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
886efx_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
939fail3:
940	EFSYS_PROBE(fail3);
941	EFSYS_KMEM_FREE(enp->en_esip, payload_len, payload);
942fail2:
943	EFSYS_PROBE(fail2);
944fail1:
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
955efx_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
1011fail3:
1012	EFSYS_PROBE(fail3);
1013fail2:
1014	EFSYS_PROBE(fail2);
1015fail1:
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
1028efx_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
1068fail3:
1069	EFSYS_PROBE(fail3);
1070fail2:
1071	EFSYS_PROBE(fail2);
1072fail1:
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