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