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