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