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