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