xref: /freebsd/sys/dev/sfxge/common/ef10_vpd.c (revision 0f7f3352c8bc463607912e2463d13e52d44a4cae)
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 
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
45 ef10_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 
91 out:
92 	return (0);
93 
94 fail2:
95 	EFSYS_PROBE(fail2);
96 
97 	EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
98 fail1:
99 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
100 
101 	return (rc);
102 }
103 
104 	__checkReturn		efx_rc_t
105 ef10_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 
126 fail1:
127 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
128 
129 	return (rc);
130 }
131 
132 	__checkReturn		efx_rc_t
133 ef10_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 
172 fail2:
173 	EFSYS_PROBE(fail2);
174 
175 	EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
176 fail1:
177 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
178 
179 	return (rc);
180 }
181 
182 	__checkReturn		efx_rc_t
183 ef10_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 
249 done:
250 	return (0);
251 
252 fail4:
253 	EFSYS_PROBE(fail4);
254 fail3:
255 	EFSYS_PROBE(fail3);
256 fail2:
257 	EFSYS_PROBE(fail2);
258 fail1:
259 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
260 
261 	return (rc);
262 }
263 
264 	__checkReturn		efx_rc_t
265 ef10_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 
298 fail2:
299 	EFSYS_PROBE(fail2);
300 fail1:
301 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
302 
303 	return (rc);
304 }
305 
306 	__checkReturn		efx_rc_t
307 ef10_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 		if (rc == ENOENT)
337 			return (rc);
338 		goto fail2;
339 	}
340 
341 	evvp->evv_length = length;
342 	memcpy(evvp->evv_value, data + offset, length);
343 
344 	return (0);
345 
346 fail2:
347 	EFSYS_PROBE(fail2);
348 fail1:
349 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
350 
351 	return (rc);
352 }
353 
354 	__checkReturn		efx_rc_t
355 ef10_vpd_set(
356 	__in			efx_nic_t *enp,
357 	__in_bcount(size)	caddr_t data,
358 	__in			size_t size,
359 	__in			efx_vpd_value_t *evvp)
360 {
361 	efx_rc_t rc;
362 
363 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
364 		    enp->en_family == EFX_FAMILY_MEDFORD);
365 
366 	/* If the provided (tag,keyword) exists in svpd, then it is readonly */
367 	if (enp->en_arch.ef10.ena_svpd_length > 0) {
368 		unsigned int offset;
369 		uint8_t length;
370 
371 		if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
372 		    enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
373 		    evvp->evv_keyword, &offset, &length)) == 0) {
374 			rc = EACCES;
375 			goto fail1;
376 		}
377 	}
378 
379 	if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
380 		goto fail2;
381 
382 	return (0);
383 
384 fail2:
385 	EFSYS_PROBE(fail2);
386 fail1:
387 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
388 
389 	return (rc);
390 }
391 
392 	__checkReturn		efx_rc_t
393 ef10_vpd_next(
394 	__in			efx_nic_t *enp,
395 	__in_bcount(size)	caddr_t data,
396 	__in			size_t size,
397 	__out			efx_vpd_value_t *evvp,
398 	__inout			unsigned int *contp)
399 {
400 	_NOTE(ARGUNUSED(enp, data, size, evvp, contp))
401 
402 	return (ENOTSUP);
403 }
404 
405 	__checkReturn		efx_rc_t
406 ef10_vpd_write(
407 	__in			efx_nic_t *enp,
408 	__in_bcount(size)	caddr_t data,
409 	__in			size_t size)
410 {
411 	size_t vpd_length;
412 	uint32_t pci_pf;
413 	uint32_t tag;
414 	efx_rc_t rc;
415 
416 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
417 		    enp->en_family == EFX_FAMILY_MEDFORD);
418 
419 	if (enp->en_nic_cfg.enc_vpd_is_global) {
420 		tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
421 	} else {
422 		pci_pf = enp->en_nic_cfg.enc_pf;
423 		tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
424 	}
425 
426 	/* Determine total length of new dynamic VPD */
427 	if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
428 		goto fail1;
429 
430 	/* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
431 	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
432 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
433 		    tag, data, vpd_length, B_TRUE)) != 0) {
434 		goto fail2;
435 	}
436 
437 	return (0);
438 
439 fail2:
440 	EFSYS_PROBE(fail2);
441 
442 fail1:
443 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
444 
445 	return (rc);
446 }
447 
448 				void
449 ef10_vpd_fini(
450 	__in			efx_nic_t *enp)
451 {
452 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
453 		    enp->en_family == EFX_FAMILY_MEDFORD);
454 
455 	if (enp->en_arch.ef10.ena_svpd_length > 0) {
456 		EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length,
457 				enp->en_arch.ef10.ena_svpd);
458 
459 		enp->en_arch.ef10.ena_svpd = NULL;
460 		enp->en_arch.ef10.ena_svpd_length = 0;
461 	}
462 }
463 
464 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
465 
466 #endif	/* EFSYS_OPT_VPD */
467