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$");
33
34#include "efx.h"
35#include "efx_impl.h"
36
37#if EFSYS_OPT_VPD
38
39#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
40
41#include "ef10_tlv_layout.h"
42
43	__checkReturn		efx_rc_t
44ef10_vpd_init(
45	__in			efx_nic_t *enp)
46{
47	caddr_t svpd;
48	size_t svpd_size;
49	uint32_t pci_pf;
50	uint32_t tag;
51	efx_rc_t rc;
52
53	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
54	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
55	    enp->en_family == EFX_FAMILY_MEDFORD ||
56	    enp->en_family == EFX_FAMILY_MEDFORD2);
57
58	if (enp->en_nic_cfg.enc_vpd_is_global) {
59		tag = TLV_TAG_GLOBAL_STATIC_VPD;
60	} else {
61		pci_pf = enp->en_nic_cfg.enc_pf;
62		tag = TLV_TAG_PF_STATIC_VPD(pci_pf);
63	}
64
65	/*
66	 * The VPD interface exposes VPD resources from the combined static and
67	 * dynamic VPD storage. As the static VPD configuration should *never*
68	 * change, we can cache it.
69	 */
70	svpd = NULL;
71	svpd_size = 0;
72	rc = ef10_nvram_partn_read_tlv(enp,
73	    NVRAM_PARTITION_TYPE_STATIC_CONFIG,
74	    tag, &svpd, &svpd_size);
75	if (rc != 0) {
76		if (rc == EACCES) {
77			/* Unprivileged functions cannot access VPD */
78			goto out;
79		}
80		goto fail1;
81	}
82
83	if (svpd != NULL && svpd_size > 0) {
84		if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0)
85			goto fail2;
86	}
87
88	enp->en_arch.ef10.ena_svpd = svpd;
89	enp->en_arch.ef10.ena_svpd_length = svpd_size;
90
91out:
92	return (0);
93
94fail2:
95	EFSYS_PROBE(fail2);
96
97	EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
98fail1:
99	EFSYS_PROBE1(fail1, efx_rc_t, rc);
100
101	return (rc);
102}
103
104	__checkReturn		efx_rc_t
105ef10_vpd_size(
106	__in			efx_nic_t *enp,
107	__out			size_t *sizep)
108{
109	efx_rc_t rc;
110
111	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
112	    enp->en_family == EFX_FAMILY_MEDFORD ||
113	    enp->en_family == EFX_FAMILY_MEDFORD2);
114
115	/*
116	 * This function returns the total size the user should allocate
117	 * for all VPD operations. We've already cached the static vpd,
118	 * so we just need to return an upper bound on the dynamic vpd,
119	 * which is the size of the DYNAMIC_CONFIG partition.
120	 */
121	if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
122		    sizep, NULL, NULL, NULL)) != 0)
123		goto fail1;
124
125	return (0);
126
127fail1:
128	EFSYS_PROBE1(fail1, efx_rc_t, rc);
129
130	return (rc);
131}
132
133	__checkReturn		efx_rc_t
134ef10_vpd_read(
135	__in			efx_nic_t *enp,
136	__out_bcount(size)	caddr_t data,
137	__in			size_t size)
138{
139	caddr_t dvpd;
140	size_t dvpd_size;
141	uint32_t pci_pf;
142	uint32_t tag;
143	efx_rc_t rc;
144
145	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
146	    enp->en_family == EFX_FAMILY_MEDFORD ||
147	    enp->en_family == EFX_FAMILY_MEDFORD2);
148
149	if (enp->en_nic_cfg.enc_vpd_is_global) {
150		tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
151	} else {
152		pci_pf = enp->en_nic_cfg.enc_pf;
153		tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
154	}
155
156	if ((rc = ef10_nvram_partn_read_tlv(enp,
157		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
158		    tag, &dvpd, &dvpd_size)) != 0)
159		goto fail1;
160
161	if (dvpd_size > size) {
162		rc = ENOSPC;
163		goto fail2;
164	}
165	if (dvpd != NULL)
166		memcpy(data, dvpd, dvpd_size);
167
168	/* Pad data with all-1s, consistent with update operations */
169	memset(data + dvpd_size, 0xff, size - dvpd_size);
170
171	if (dvpd != NULL)
172		EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
173
174	return (0);
175
176fail2:
177	EFSYS_PROBE(fail2);
178
179	if (dvpd != NULL)
180		EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
181fail1:
182	EFSYS_PROBE1(fail1, efx_rc_t, rc);
183
184	return (rc);
185}
186
187	__checkReturn		efx_rc_t
188ef10_vpd_verify(
189	__in			efx_nic_t *enp,
190	__in_bcount(size)	caddr_t data,
191	__in			size_t size)
192{
193	efx_vpd_tag_t stag;
194	efx_vpd_tag_t dtag;
195	efx_vpd_keyword_t skey;
196	efx_vpd_keyword_t dkey;
197	unsigned int scont;
198	unsigned int dcont;
199	efx_rc_t rc;
200
201	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
202	    enp->en_family == EFX_FAMILY_MEDFORD ||
203	    enp->en_family == EFX_FAMILY_MEDFORD2);
204
205	/*
206	 * Strictly you could take the view that dynamic vpd is optional.
207	 * Instead, to conform more closely to the read/verify/reinit()
208	 * paradigm, we require dynamic vpd. ef10_vpd_reinit() will
209	 * reinitialize it as required.
210	 */
211	if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
212		goto fail1;
213
214	/*
215	 * Verify that there is no duplication between the static and
216	 * dynamic cfg sectors.
217	 */
218	if (enp->en_arch.ef10.ena_svpd_length == 0)
219		goto done;
220
221	dcont = 0;
222	_NOTE(CONSTANTCONDITION)
223	while (1) {
224		if ((rc = efx_vpd_hunk_next(data, size, &dtag,
225		    &dkey, NULL, NULL, &dcont)) != 0)
226			goto fail2;
227		if (dcont == 0)
228			break;
229
230		/*
231		 * Skip the RV keyword. It should be present in both the static
232		 * and dynamic cfg sectors.
233		 */
234		if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
235			continue;
236
237		scont = 0;
238		_NOTE(CONSTANTCONDITION)
239		while (1) {
240			if ((rc = efx_vpd_hunk_next(
241			    enp->en_arch.ef10.ena_svpd,
242			    enp->en_arch.ef10.ena_svpd_length, &stag, &skey,
243			    NULL, NULL, &scont)) != 0)
244				goto fail3;
245			if (scont == 0)
246				break;
247
248			if (stag == dtag && skey == dkey) {
249				rc = EEXIST;
250				goto fail4;
251			}
252		}
253	}
254
255done:
256	return (0);
257
258fail4:
259	EFSYS_PROBE(fail4);
260fail3:
261	EFSYS_PROBE(fail3);
262fail2:
263	EFSYS_PROBE(fail2);
264fail1:
265	EFSYS_PROBE1(fail1, efx_rc_t, rc);
266
267	return (rc);
268}
269
270	__checkReturn		efx_rc_t
271ef10_vpd_reinit(
272	__in			efx_nic_t *enp,
273	__in_bcount(size)	caddr_t data,
274	__in			size_t size)
275{
276	boolean_t wantpid;
277	efx_rc_t rc;
278
279	/*
280	 * Only create an ID string if the dynamic cfg doesn't have one
281	 */
282	if (enp->en_arch.ef10.ena_svpd_length == 0)
283		wantpid = B_TRUE;
284	else {
285		unsigned int offset;
286		uint8_t length;
287
288		rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
289				    enp->en_arch.ef10.ena_svpd_length,
290				    EFX_VPD_ID, 0, &offset, &length);
291		if (rc == 0)
292			wantpid = B_FALSE;
293		else if (rc == ENOENT)
294			wantpid = B_TRUE;
295		else
296			goto fail1;
297	}
298
299	if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
300		goto fail2;
301
302	return (0);
303
304fail2:
305	EFSYS_PROBE(fail2);
306fail1:
307	EFSYS_PROBE1(fail1, efx_rc_t, rc);
308
309	return (rc);
310}
311
312	__checkReturn		efx_rc_t
313ef10_vpd_get(
314	__in			efx_nic_t *enp,
315	__in_bcount(size)	caddr_t data,
316	__in			size_t size,
317	__inout			efx_vpd_value_t *evvp)
318{
319	unsigned int offset;
320	uint8_t length;
321	efx_rc_t rc;
322
323	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
324	    enp->en_family == EFX_FAMILY_MEDFORD ||
325	    enp->en_family == EFX_FAMILY_MEDFORD2);
326
327	/* Attempt to satisfy the request from svpd first */
328	if (enp->en_arch.ef10.ena_svpd_length > 0) {
329		if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
330		    enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
331		    evvp->evv_keyword, &offset, &length)) == 0) {
332			evvp->evv_length = length;
333			memcpy(evvp->evv_value,
334			    enp->en_arch.ef10.ena_svpd + offset, length);
335			return (0);
336		} else if (rc != ENOENT)
337			goto fail1;
338	}
339
340	/* And then from the provided data buffer */
341	if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
342	    evvp->evv_keyword, &offset, &length)) != 0) {
343		if (rc == ENOENT)
344			return (rc);
345		goto fail2;
346	}
347
348	evvp->evv_length = length;
349	memcpy(evvp->evv_value, data + offset, length);
350
351	return (0);
352
353fail2:
354	EFSYS_PROBE(fail2);
355fail1:
356	EFSYS_PROBE1(fail1, efx_rc_t, rc);
357
358	return (rc);
359}
360
361	__checkReturn		efx_rc_t
362ef10_vpd_set(
363	__in			efx_nic_t *enp,
364	__in_bcount(size)	caddr_t data,
365	__in			size_t size,
366	__in			efx_vpd_value_t *evvp)
367{
368	efx_rc_t rc;
369
370	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
371	    enp->en_family == EFX_FAMILY_MEDFORD ||
372	    enp->en_family == EFX_FAMILY_MEDFORD2);
373
374	/* If the provided (tag,keyword) exists in svpd, then it is readonly */
375	if (enp->en_arch.ef10.ena_svpd_length > 0) {
376		unsigned int offset;
377		uint8_t length;
378
379		if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
380		    enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
381		    evvp->evv_keyword, &offset, &length)) == 0) {
382			rc = EACCES;
383			goto fail1;
384		}
385	}
386
387	if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 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
401ef10_vpd_next(
402	__in			efx_nic_t *enp,
403	__in_bcount(size)	caddr_t data,
404	__in			size_t size,
405	__out			efx_vpd_value_t *evvp,
406	__inout			unsigned int *contp)
407{
408	_NOTE(ARGUNUSED(enp, data, size, evvp, contp))
409
410	return (ENOTSUP);
411}
412
413	__checkReturn		efx_rc_t
414ef10_vpd_write(
415	__in			efx_nic_t *enp,
416	__in_bcount(size)	caddr_t data,
417	__in			size_t size)
418{
419	size_t vpd_length;
420	uint32_t pci_pf;
421	uint32_t tag;
422	efx_rc_t rc;
423
424	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
425	    enp->en_family == EFX_FAMILY_MEDFORD ||
426	    enp->en_family == EFX_FAMILY_MEDFORD2);
427
428	if (enp->en_nic_cfg.enc_vpd_is_global) {
429		tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
430	} else {
431		pci_pf = enp->en_nic_cfg.enc_pf;
432		tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
433	}
434
435	/* Determine total length of new dynamic VPD */
436	if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
437		goto fail1;
438
439	/* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
440	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
441		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
442		    tag, data, vpd_length, B_TRUE)) != 0) {
443		goto fail2;
444	}
445
446	return (0);
447
448fail2:
449	EFSYS_PROBE(fail2);
450
451fail1:
452	EFSYS_PROBE1(fail1, efx_rc_t, rc);
453
454	return (rc);
455}
456
457				void
458ef10_vpd_fini(
459	__in			efx_nic_t *enp)
460{
461	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
462	    enp->en_family == EFX_FAMILY_MEDFORD ||
463	    enp->en_family == EFX_FAMILY_MEDFORD2);
464
465	if (enp->en_arch.ef10.ena_svpd_length > 0) {
466		EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length,
467				enp->en_arch.ef10.ena_svpd);
468
469		enp->en_arch.ef10.ena_svpd = NULL;
470		enp->en_arch.ef10.ena_svpd_length = 0;
471	}
472}
473
474#endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
475
476#endif	/* EFSYS_OPT_VPD */
477