xref: /freebsd/sys/dev/sfxge/common/ef10_vpd.c (revision 7cc42f6d)
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
44 ef10_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 
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 	    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 
127 fail1:
128 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
129 
130 	return (rc);
131 }
132 
133 	__checkReturn		efx_rc_t
134 ef10_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 
176 fail2:
177 	EFSYS_PROBE(fail2);
178 
179 	if (dvpd != NULL)
180 		EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
181 fail1:
182 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
183 
184 	return (rc);
185 }
186 
187 	__checkReturn		efx_rc_t
188 ef10_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 
255 done:
256 	return (0);
257 
258 fail4:
259 	EFSYS_PROBE(fail4);
260 fail3:
261 	EFSYS_PROBE(fail3);
262 fail2:
263 	EFSYS_PROBE(fail2);
264 fail1:
265 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
266 
267 	return (rc);
268 }
269 
270 	__checkReturn		efx_rc_t
271 ef10_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 
304 fail2:
305 	EFSYS_PROBE(fail2);
306 fail1:
307 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
308 
309 	return (rc);
310 }
311 
312 	__checkReturn		efx_rc_t
313 ef10_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 
353 fail2:
354 	EFSYS_PROBE(fail2);
355 fail1:
356 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
357 
358 	return (rc);
359 }
360 
361 	__checkReturn		efx_rc_t
362 ef10_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 
392 fail2:
393 	EFSYS_PROBE(fail2);
394 fail1:
395 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
396 
397 	return (rc);
398 }
399 
400 	__checkReturn		efx_rc_t
401 ef10_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
414 ef10_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 
448 fail2:
449 	EFSYS_PROBE(fail2);
450 
451 fail1:
452 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
453 
454 	return (rc);
455 }
456 
457 				void
458 ef10_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