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_VPD
38
39#if EFSYS_OPT_SIENA
40
41static	__checkReturn			efx_rc_t
42siena_vpd_get_static(
43	__in				efx_nic_t *enp,
44	__in				uint32_t partn,
45	__deref_out_bcount_opt(*sizep)	caddr_t *svpdp,
46	__out				size_t *sizep)
47{
48	siena_mc_static_config_hdr_t *scfg;
49	caddr_t svpd;
50	size_t size;
51	uint8_t cksum;
52	unsigned int vpd_offset;
53	unsigned int vpd_length;
54	unsigned int hdr_length;
55	unsigned int pos;
56	unsigned int region;
57	efx_rc_t rc;
58
59	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 ||
60		    partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1);
61
62	/* Allocate sufficient memory for the entire static cfg area */
63	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
64		goto fail1;
65
66	if (size < SIENA_NVRAM_CHUNK) {
67		rc = EINVAL;
68		goto fail2;
69	}
70
71	EFSYS_KMEM_ALLOC(enp->en_esip, size, scfg);
72	if (scfg == NULL) {
73		rc = ENOMEM;
74		goto fail3;
75	}
76
77	if ((rc = siena_nvram_partn_read(enp, partn, 0,
78	    (caddr_t)scfg, SIENA_NVRAM_CHUNK)) != 0)
79		goto fail4;
80
81	/* Verify the magic number */
82	if (EFX_DWORD_FIELD(scfg->magic, EFX_DWORD_0) !=
83	    SIENA_MC_STATIC_CONFIG_MAGIC) {
84		rc = EINVAL;
85		goto fail5;
86	}
87
88	/* All future versions of the structure must be backwards compatible */
89	EFX_STATIC_ASSERT(SIENA_MC_STATIC_CONFIG_VERSION == 0);
90
91	hdr_length = EFX_WORD_FIELD(scfg->length, EFX_WORD_0);
92	vpd_offset = EFX_DWORD_FIELD(scfg->static_vpd_offset, EFX_DWORD_0);
93	vpd_length = EFX_DWORD_FIELD(scfg->static_vpd_length, EFX_DWORD_0);
94
95	/* Verify the hdr doesn't overflow the sector size */
96	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
97	    vpd_length + vpd_offset > size) {
98		rc = EINVAL;
99		goto fail6;
100	}
101
102	/* Read the remainder of scfg + static vpd */
103	region = vpd_offset + vpd_length;
104	if (region > SIENA_NVRAM_CHUNK) {
105		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
106		    (caddr_t)scfg + SIENA_NVRAM_CHUNK,
107		    region - SIENA_NVRAM_CHUNK)) != 0)
108			goto fail7;
109	}
110
111	/* Verify checksum */
112	cksum = 0;
113	for (pos = 0; pos < hdr_length; pos++)
114		cksum += ((uint8_t *)scfg)[pos];
115	if (cksum != 0) {
116		rc = EINVAL;
117		goto fail8;
118	}
119
120	if (vpd_length == 0)
121		svpd = NULL;
122	else {
123		/* Copy the vpd data out */
124		EFSYS_KMEM_ALLOC(enp->en_esip, vpd_length, svpd);
125		if (svpd == NULL) {
126			rc = ENOMEM;
127			goto fail9;
128		}
129		memcpy(svpd, (caddr_t)scfg + vpd_offset, vpd_length);
130	}
131
132	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
133
134	*svpdp = svpd;
135	*sizep = vpd_length;
136
137	return (0);
138
139fail9:
140	EFSYS_PROBE(fail9);
141fail8:
142	EFSYS_PROBE(fail8);
143fail7:
144	EFSYS_PROBE(fail7);
145fail6:
146	EFSYS_PROBE(fail6);
147fail5:
148	EFSYS_PROBE(fail5);
149fail4:
150	EFSYS_PROBE(fail4);
151
152	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
153
154fail3:
155	EFSYS_PROBE(fail3);
156fail2:
157	EFSYS_PROBE(fail2);
158fail1:
159	EFSYS_PROBE1(fail1, efx_rc_t, rc);
160
161	return (rc);
162}
163
164	__checkReturn		efx_rc_t
165siena_vpd_init(
166	__in			efx_nic_t *enp)
167{
168	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
169	caddr_t svpd = NULL;
170	unsigned int partn;
171	size_t size = 0;
172	efx_rc_t rc;
173
174	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
175
176	partn = (emip->emi_port == 1)
177		? MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0
178		: MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1;
179
180	/*
181	 * We need the static VPD sector to present a unified static+dynamic
182	 * VPD, that is, basically on every read, write, verify cycle. Since
183	 * it should *never* change we can just cache it here.
184	 */
185	if ((rc = siena_vpd_get_static(enp, partn, &svpd, &size)) != 0)
186		goto fail1;
187
188	if (svpd != NULL && size > 0) {
189		if ((rc = efx_vpd_hunk_verify(svpd, size, NULL)) != 0)
190			goto fail2;
191	}
192
193	enp->en_u.siena.enu_svpd = svpd;
194	enp->en_u.siena.enu_svpd_length = size;
195
196	return (0);
197
198fail2:
199	EFSYS_PROBE(fail2);
200
201	EFSYS_KMEM_FREE(enp->en_esip, size, svpd);
202fail1:
203	EFSYS_PROBE1(fail1, efx_rc_t, rc);
204
205	return (rc);
206}
207
208	__checkReturn		efx_rc_t
209siena_vpd_size(
210	__in			efx_nic_t *enp,
211	__out			size_t *sizep)
212{
213	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
214	uint32_t partn;
215	efx_rc_t rc;
216
217	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
218
219	/*
220	 * This function returns the total size the user should allocate
221	 * for all VPD operations. We've already cached the static vpd,
222	 * so we just need to return an upper bound on the dynamic vpd.
223	 * Since the dynamic_config structure can change under our feet,
224	 * (as version numbers are inserted), just be safe and return the
225	 * total size of the dynamic_config *sector*
226	 */
227	partn = (emip->emi_port == 1)
228		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
229		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
230
231	if ((rc = siena_nvram_partn_size(enp, partn, sizep)) != 0)
232		goto fail1;
233
234	return (0);
235
236fail1:
237	EFSYS_PROBE1(fail1, efx_rc_t, rc);
238
239	return (rc);
240}
241
242	__checkReturn		efx_rc_t
243siena_vpd_read(
244	__in			efx_nic_t *enp,
245	__out_bcount(size)	caddr_t data,
246	__in			size_t size)
247{
248	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
249	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
250	unsigned int vpd_length;
251	unsigned int vpd_offset;
252	unsigned int dcfg_partn;
253	size_t dcfg_size;
254	efx_rc_t rc;
255
256	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
257
258	dcfg_partn = (emip->emi_port == 1)
259		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
260		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
261
262	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
263	    B_TRUE, &dcfg, &dcfg_size)) != 0)
264		goto fail1;
265
266	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
267	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
268
269	if (vpd_length > size) {
270		rc = EFAULT;	/* Invalid dcfg: header bigger than sector */
271		goto fail2;
272	}
273
274	EFSYS_ASSERT3U(vpd_length, <=, size);
275	memcpy(data, (caddr_t)dcfg + vpd_offset, vpd_length);
276
277	/* Pad data with all-1s, consistent with update operations */
278	memset(data + vpd_length, 0xff, size - vpd_length);
279
280	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
281
282	return (0);
283
284fail2:
285	EFSYS_PROBE(fail2);
286
287	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
288fail1:
289	EFSYS_PROBE1(fail1, efx_rc_t, rc);
290
291	return (rc);
292}
293
294	__checkReturn		efx_rc_t
295siena_vpd_verify(
296	__in			efx_nic_t *enp,
297	__in_bcount(size)	caddr_t data,
298	__in			size_t size)
299{
300	efx_vpd_tag_t stag;
301	efx_vpd_tag_t dtag;
302	efx_vpd_keyword_t skey;
303	efx_vpd_keyword_t dkey;
304	unsigned int scont;
305	unsigned int dcont;
306
307	efx_rc_t rc;
308
309	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
310
311	/*
312	 * Strictly you could take the view that dynamic vpd is optional.
313	 * Instead, to conform more closely to the read/verify/reinit()
314	 * paradigm, we require dynamic vpd. siena_vpd_reinit() will
315	 * reinitialize it as required.
316	 */
317	if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
318		goto fail1;
319
320	/*
321	 * Verify that there is no duplication between the static and
322	 * dynamic cfg sectors.
323	 */
324	if (enp->en_u.siena.enu_svpd_length == 0)
325		goto done;
326
327	dcont = 0;
328	_NOTE(CONSTANTCONDITION)
329	while (1) {
330		if ((rc = efx_vpd_hunk_next(data, size, &dtag,
331		    &dkey, NULL, NULL, &dcont)) != 0)
332			goto fail2;
333		if (dcont == 0)
334			break;
335
336		/*
337		 * Skip the RV keyword. It should be present in both the static
338		 * and dynamic cfg sectors.
339		 */
340		if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
341			continue;
342
343		scont = 0;
344		_NOTE(CONSTANTCONDITION)
345		while (1) {
346			if ((rc = efx_vpd_hunk_next(
347			    enp->en_u.siena.enu_svpd,
348			    enp->en_u.siena.enu_svpd_length, &stag, &skey,
349			    NULL, NULL, &scont)) != 0)
350				goto fail3;
351			if (scont == 0)
352				break;
353
354			if (stag == dtag && skey == dkey) {
355				rc = EEXIST;
356				goto fail4;
357			}
358		}
359	}
360
361done:
362	return (0);
363
364fail4:
365	EFSYS_PROBE(fail4);
366fail3:
367	EFSYS_PROBE(fail3);
368fail2:
369	EFSYS_PROBE(fail2);
370fail1:
371	EFSYS_PROBE1(fail1, efx_rc_t, rc);
372
373	return (rc);
374}
375
376	__checkReturn		efx_rc_t
377siena_vpd_reinit(
378	__in			efx_nic_t *enp,
379	__in_bcount(size)	caddr_t data,
380	__in			size_t size)
381{
382	boolean_t wantpid;
383	efx_rc_t rc;
384
385	/*
386	 * Only create a PID if the dynamic cfg doesn't have one
387	 */
388	if (enp->en_u.siena.enu_svpd_length == 0)
389		wantpid = B_TRUE;
390	else {
391		unsigned int offset;
392		uint8_t length;
393
394		rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
395				    enp->en_u.siena.enu_svpd_length,
396				    EFX_VPD_ID, 0, &offset, &length);
397		if (rc == 0)
398			wantpid = B_FALSE;
399		else if (rc == ENOENT)
400			wantpid = B_TRUE;
401		else
402			goto fail1;
403	}
404
405	if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
406		goto fail2;
407
408	return (0);
409
410fail2:
411	EFSYS_PROBE(fail2);
412fail1:
413	EFSYS_PROBE1(fail1, efx_rc_t, rc);
414
415	return (rc);
416}
417
418	__checkReturn		efx_rc_t
419siena_vpd_get(
420	__in			efx_nic_t *enp,
421	__in_bcount(size)	caddr_t data,
422	__in			size_t size,
423	__inout			efx_vpd_value_t *evvp)
424{
425	unsigned int offset;
426	uint8_t length;
427	efx_rc_t rc;
428
429	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
430
431	/* Attempt to satisfy the request from svpd first */
432	if (enp->en_u.siena.enu_svpd_length > 0) {
433		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
434		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
435		    evvp->evv_keyword, &offset, &length)) == 0) {
436			evvp->evv_length = length;
437			memcpy(evvp->evv_value,
438			    enp->en_u.siena.enu_svpd + offset, length);
439			return (0);
440		} else if (rc != ENOENT)
441			goto fail1;
442	}
443
444	/* And then from the provided data buffer */
445	if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
446	    evvp->evv_keyword, &offset, &length)) != 0) {
447		if (rc == ENOENT)
448			return (rc);
449
450		goto fail2;
451	}
452
453	evvp->evv_length = length;
454	memcpy(evvp->evv_value, data + offset, length);
455
456	return (0);
457
458fail2:
459	EFSYS_PROBE(fail2);
460fail1:
461	EFSYS_PROBE1(fail1, efx_rc_t, rc);
462
463	return (rc);
464}
465
466	__checkReturn		efx_rc_t
467siena_vpd_set(
468	__in			efx_nic_t *enp,
469	__in_bcount(size)	caddr_t data,
470	__in			size_t size,
471	__in			efx_vpd_value_t *evvp)
472{
473	efx_rc_t rc;
474
475	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
476
477	/* If the provided (tag,keyword) exists in svpd, then it is readonly */
478	if (enp->en_u.siena.enu_svpd_length > 0) {
479		unsigned int offset;
480		uint8_t length;
481
482		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
483		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
484		    evvp->evv_keyword, &offset, &length)) == 0) {
485			rc = EACCES;
486			goto fail1;
487		}
488	}
489
490	if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
491		goto fail2;
492
493	return (0);
494
495fail2:
496	EFSYS_PROBE(fail2);
497fail1:
498	EFSYS_PROBE1(fail1, efx_rc_t, rc);
499
500	return (rc);
501}
502
503	__checkReturn		efx_rc_t
504siena_vpd_next(
505	__in			efx_nic_t *enp,
506	__in_bcount(size)	caddr_t data,
507	__in			size_t size,
508	__out			efx_vpd_value_t *evvp,
509	__inout			unsigned int *contp)
510{
511	_NOTE(ARGUNUSED(enp, data, size, evvp, contp))
512
513	return (ENOTSUP);
514}
515
516	__checkReturn		efx_rc_t
517siena_vpd_write(
518	__in			efx_nic_t *enp,
519	__in_bcount(size)	caddr_t data,
520	__in			size_t size)
521{
522	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
523	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
524	unsigned int vpd_offset;
525	unsigned int dcfg_partn;
526	unsigned int hdr_length;
527	unsigned int pos;
528	uint8_t cksum;
529	size_t partn_size, dcfg_size;
530	size_t vpd_length;
531	efx_rc_t rc;
532
533	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
534
535	/* Determine total length of all tags */
536	if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
537		goto fail1;
538
539	/* Lock dynamic config sector for write, and read structure only */
540	dcfg_partn = (emip->emi_port == 1)
541		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
542		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
543
544	if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
545		goto fail2;
546
547	if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
548		goto fail3;
549
550	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
551	    B_FALSE, &dcfg, &dcfg_size)) != 0)
552		goto fail4;
553
554	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
555
556	/* Allocated memory should have room for the new VPD */
557	if (hdr_length + vpd_length > dcfg_size) {
558		rc = ENOSPC;
559		goto fail5;
560	}
561
562	/* Copy in new vpd and update header */
563	vpd_offset = dcfg_size - vpd_length;
564	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset, EFX_DWORD_0, vpd_offset);
565	memcpy((caddr_t)dcfg + vpd_offset, data, vpd_length);
566	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, vpd_length);
567
568	/* Update the checksum */
569	cksum = 0;
570	for (pos = 0; pos < hdr_length; pos++)
571		cksum += ((uint8_t *)dcfg)[pos];
572	dcfg->csum.eb_u8[0] -= cksum;
573
574	/* Erase and write the new sector */
575	if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
576		goto fail6;
577
578	/* Write out the new structure to nvram */
579	if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0, (caddr_t)dcfg,
580	    vpd_offset + vpd_length)) != 0)
581		goto fail7;
582
583	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
584
585	siena_nvram_partn_unlock(enp, dcfg_partn, NULL);
586
587	return (0);
588
589fail7:
590	EFSYS_PROBE(fail7);
591fail6:
592	EFSYS_PROBE(fail6);
593fail5:
594	EFSYS_PROBE(fail5);
595
596	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
597fail4:
598	EFSYS_PROBE(fail4);
599
600	siena_nvram_partn_unlock(enp, dcfg_partn, NULL);
601fail3:
602	EFSYS_PROBE(fail3);
603fail2:
604	EFSYS_PROBE(fail2);
605fail1:
606	EFSYS_PROBE1(fail1, efx_rc_t, rc);
607
608	return (rc);
609}
610
611				void
612siena_vpd_fini(
613	__in			efx_nic_t *enp)
614{
615	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
616
617	if (enp->en_u.siena.enu_svpd_length > 0) {
618		EFSYS_KMEM_FREE(enp->en_esip, enp->en_u.siena.enu_svpd_length,
619				enp->en_u.siena.enu_svpd);
620
621		enp->en_u.siena.enu_svpd = NULL;
622		enp->en_u.siena.enu_svpd_length = 0;
623	}
624}
625
626#endif	/* EFSYS_OPT_SIENA */
627
628#endif	/* EFSYS_OPT_VPD */
629