xref: /freebsd/sys/dev/sfxge/common/ef10_vpd.c (revision 734e82fe33aa764367791a7d603b383996c6b40b)
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
42 ef10_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 
89 out:
90 	return (0);
91 
92 fail2:
93 	EFSYS_PROBE(fail2);
94 
95 	EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
96 fail1:
97 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
98 
99 	return (rc);
100 }
101 
102 	__checkReturn		efx_rc_t
103 ef10_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 
125 fail1:
126 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
127 
128 	return (rc);
129 }
130 
131 	__checkReturn		efx_rc_t
132 ef10_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 
174 fail2:
175 	EFSYS_PROBE(fail2);
176 
177 	if (dvpd != NULL)
178 		EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
179 fail1:
180 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
181 
182 	return (rc);
183 }
184 
185 	__checkReturn		efx_rc_t
186 ef10_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 
253 done:
254 	return (0);
255 
256 fail4:
257 	EFSYS_PROBE(fail4);
258 fail3:
259 	EFSYS_PROBE(fail3);
260 fail2:
261 	EFSYS_PROBE(fail2);
262 fail1:
263 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
264 
265 	return (rc);
266 }
267 
268 	__checkReturn		efx_rc_t
269 ef10_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 
302 fail2:
303 	EFSYS_PROBE(fail2);
304 fail1:
305 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
306 
307 	return (rc);
308 }
309 
310 	__checkReturn		efx_rc_t
311 ef10_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 
351 fail2:
352 	EFSYS_PROBE(fail2);
353 fail1:
354 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
355 
356 	return (rc);
357 }
358 
359 	__checkReturn		efx_rc_t
360 ef10_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 
390 fail2:
391 	EFSYS_PROBE(fail2);
392 fail1:
393 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
394 
395 	return (rc);
396 }
397 
398 	__checkReturn		efx_rc_t
399 ef10_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
412 ef10_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 
446 fail2:
447 	EFSYS_PROBE(fail2);
448 
449 fail1:
450 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
451 
452 	return (rc);
453 }
454 
455 				void
456 ef10_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