xref: /dpdk/drivers/common/sfc_efx/base/efx_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 #if EFSYS_OPT_VPD
11 
12 #define	TAG_TYPE_LBN 7
13 #define	TAG_TYPE_WIDTH 1
14 #define	TAG_TYPE_LARGE_ITEM_DECODE 1
15 #define	TAG_TYPE_SMALL_ITEM_DECODE 0
16 
17 #define	TAG_SMALL_ITEM_NAME_LBN 3
18 #define	TAG_SMALL_ITEM_NAME_WIDTH 4
19 #define	TAG_SMALL_ITEM_SIZE_LBN 0
20 #define	TAG_SMALL_ITEM_SIZE_WIDTH 3
21 
22 #define	TAG_LARGE_ITEM_NAME_LBN 0
23 #define	TAG_LARGE_ITEM_NAME_WIDTH 7
24 
25 #define	TAG_NAME_END_DECODE 0x0f
26 #define	TAG_NAME_ID_STRING_DECODE 0x02
27 #define	TAG_NAME_VPD_R_DECODE 0x10
28 #define	TAG_NAME_VPD_W_DECODE 0x11
29 
30 #if EFSYS_OPT_SIENA
31 
32 static const efx_vpd_ops_t	__efx_vpd_siena_ops = {
33 	siena_vpd_init,		/* evpdo_init */
34 	siena_vpd_size,		/* evpdo_size */
35 	siena_vpd_read,		/* evpdo_read */
36 	siena_vpd_verify,	/* evpdo_verify */
37 	siena_vpd_reinit,	/* evpdo_reinit */
38 	siena_vpd_get,		/* evpdo_get */
39 	siena_vpd_set,		/* evpdo_set */
40 	siena_vpd_next,		/* evpdo_next */
41 	siena_vpd_write,	/* evpdo_write */
42 	siena_vpd_fini,		/* evpdo_fini */
43 };
44 
45 #endif	/* EFSYS_OPT_SIENA */
46 
47 #if EFX_OPTS_EF10()
48 
49 static const efx_vpd_ops_t	__efx_vpd_ef10_ops = {
50 	ef10_vpd_init,		/* evpdo_init */
51 	ef10_vpd_size,		/* evpdo_size */
52 	ef10_vpd_read,		/* evpdo_read */
53 	ef10_vpd_verify,	/* evpdo_verify */
54 	ef10_vpd_reinit,	/* evpdo_reinit */
55 	ef10_vpd_get,		/* evpdo_get */
56 	ef10_vpd_set,		/* evpdo_set */
57 	ef10_vpd_next,		/* evpdo_next */
58 	ef10_vpd_write,		/* evpdo_write */
59 	ef10_vpd_fini,		/* evpdo_fini */
60 };
61 
62 #endif	/* EFX_OPTS_EF10() */
63 
64 	__checkReturn		efx_rc_t
efx_vpd_init(__in efx_nic_t * enp)65 efx_vpd_init(
66 	__in			efx_nic_t *enp)
67 {
68 	const efx_vpd_ops_t *evpdop;
69 	efx_rc_t rc;
70 
71 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
72 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
73 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD));
74 
75 	switch (enp->en_family) {
76 #if EFSYS_OPT_SIENA
77 	case EFX_FAMILY_SIENA:
78 		evpdop = &__efx_vpd_siena_ops;
79 		break;
80 #endif	/* EFSYS_OPT_SIENA */
81 
82 #if EFSYS_OPT_HUNTINGTON
83 	case EFX_FAMILY_HUNTINGTON:
84 		evpdop = &__efx_vpd_ef10_ops;
85 		break;
86 #endif	/* EFSYS_OPT_HUNTINGTON */
87 
88 #if EFSYS_OPT_MEDFORD
89 	case EFX_FAMILY_MEDFORD:
90 		evpdop = &__efx_vpd_ef10_ops;
91 		break;
92 #endif	/* EFSYS_OPT_MEDFORD */
93 
94 #if EFSYS_OPT_MEDFORD2
95 	case EFX_FAMILY_MEDFORD2:
96 		evpdop = &__efx_vpd_ef10_ops;
97 		break;
98 #endif	/* EFSYS_OPT_MEDFORD2 */
99 
100 	default:
101 		EFSYS_ASSERT(0);
102 		rc = ENOTSUP;
103 		goto fail1;
104 	}
105 
106 	if (evpdop->evpdo_init != NULL) {
107 		if ((rc = evpdop->evpdo_init(enp)) != 0)
108 			goto fail2;
109 	}
110 
111 	enp->en_evpdop = evpdop;
112 	enp->en_mod_flags |= EFX_MOD_VPD;
113 
114 	return (0);
115 
116 fail2:
117 	EFSYS_PROBE(fail2);
118 fail1:
119 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
120 
121 	return (rc);
122 }
123 
124 	__checkReturn		efx_rc_t
efx_vpd_size(__in efx_nic_t * enp,__out size_t * sizep)125 efx_vpd_size(
126 	__in			efx_nic_t *enp,
127 	__out			size_t *sizep)
128 {
129 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
130 	efx_rc_t rc;
131 
132 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
133 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
134 
135 	if ((rc = evpdop->evpdo_size(enp, sizep)) != 0)
136 		goto fail1;
137 
138 	return (0);
139 
140 fail1:
141 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
142 
143 	return (rc);
144 }
145 
146 	__checkReturn		efx_rc_t
efx_vpd_read(__in efx_nic_t * enp,__out_bcount (size)caddr_t data,__in size_t size)147 efx_vpd_read(
148 	__in			efx_nic_t *enp,
149 	__out_bcount(size)	caddr_t data,
150 	__in			size_t size)
151 {
152 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
153 	efx_rc_t rc;
154 
155 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
156 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
157 
158 	if ((rc = evpdop->evpdo_read(enp, data, size)) != 0)
159 		goto fail1;
160 
161 	return (0);
162 
163 fail1:
164 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
165 
166 	return (rc);
167 }
168 
169 	__checkReturn		efx_rc_t
efx_vpd_verify(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)170 efx_vpd_verify(
171 	__in			efx_nic_t *enp,
172 	__in_bcount(size)	caddr_t data,
173 	__in			size_t size)
174 {
175 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
176 	efx_rc_t rc;
177 
178 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
179 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
180 
181 	if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0)
182 		goto fail1;
183 
184 	return (0);
185 
186 fail1:
187 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
188 
189 	return (rc);
190 }
191 
192 	__checkReturn		efx_rc_t
efx_vpd_reinit(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)193 efx_vpd_reinit(
194 	__in			efx_nic_t *enp,
195 	__in_bcount(size)	caddr_t data,
196 	__in			size_t size)
197 {
198 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
199 	efx_rc_t rc;
200 
201 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
202 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
203 
204 	if (evpdop->evpdo_reinit == NULL) {
205 		rc = ENOTSUP;
206 		goto fail1;
207 	}
208 
209 	if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0)
210 		goto fail2;
211 
212 	return (0);
213 
214 fail2:
215 	EFSYS_PROBE(fail2);
216 fail1:
217 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
218 
219 	return (rc);
220 }
221 
222 	__checkReturn		efx_rc_t
efx_vpd_get(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size,__inout efx_vpd_value_t * evvp)223 efx_vpd_get(
224 	__in			efx_nic_t *enp,
225 	__in_bcount(size)	caddr_t data,
226 	__in			size_t size,
227 	__inout			efx_vpd_value_t *evvp)
228 {
229 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
230 	efx_rc_t rc;
231 
232 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
233 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
234 
235 	if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) {
236 		if (rc == ENOENT)
237 			return (rc);
238 
239 		goto fail1;
240 	}
241 
242 	return (0);
243 
244 fail1:
245 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
246 
247 	return (rc);
248 }
249 
250 	__checkReturn		efx_rc_t
efx_vpd_set(__in efx_nic_t * enp,__inout_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_value_t * evvp)251 efx_vpd_set(
252 	__in			efx_nic_t *enp,
253 	__inout_bcount(size)	caddr_t data,
254 	__in			size_t size,
255 	__in			efx_vpd_value_t *evvp)
256 {
257 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
258 	efx_rc_t rc;
259 
260 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
261 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
262 
263 	if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0)
264 		goto fail1;
265 
266 	return (0);
267 
268 fail1:
269 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
270 
271 	return (rc);
272 }
273 
274 	__checkReturn		efx_rc_t
efx_vpd_next(__in efx_nic_t * enp,__inout_bcount (size)caddr_t data,__in size_t size,__out efx_vpd_value_t * evvp,__inout unsigned int * contp)275 efx_vpd_next(
276 	__in			efx_nic_t *enp,
277 	__inout_bcount(size)	caddr_t data,
278 	__in			size_t size,
279 	__out			efx_vpd_value_t *evvp,
280 	__inout			unsigned int *contp)
281 {
282 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
283 	efx_rc_t rc;
284 
285 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
286 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
287 
288 	if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0)
289 		goto fail1;
290 
291 	return (0);
292 
293 fail1:
294 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
295 
296 	return (rc);
297 }
298 
299 	__checkReturn		efx_rc_t
efx_vpd_write(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)300 efx_vpd_write(
301 	__in			efx_nic_t *enp,
302 	__in_bcount(size)	caddr_t data,
303 	__in			size_t size)
304 {
305 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
306 	efx_rc_t rc;
307 
308 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
309 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
310 
311 	if ((rc = evpdop->evpdo_write(enp, data, size)) != 0)
312 		goto fail1;
313 
314 	return (0);
315 
316 fail1:
317 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
318 
319 	return (rc);
320 }
321 
322 static	__checkReturn		efx_rc_t
efx_vpd_next_tag(__in caddr_t data,__in size_t size,__inout unsigned int * offsetp,__out efx_vpd_tag_t * tagp,__out uint16_t * lengthp)323 efx_vpd_next_tag(
324 	__in			caddr_t data,
325 	__in			size_t size,
326 	__inout			unsigned int *offsetp,
327 	__out			efx_vpd_tag_t *tagp,
328 	__out			uint16_t *lengthp)
329 {
330 	efx_byte_t byte;
331 	efx_word_t word;
332 	uint8_t name;
333 	uint16_t length;
334 	size_t headlen;
335 	efx_rc_t rc;
336 
337 	if (*offsetp >= size) {
338 		rc = EFAULT;
339 		goto fail1;
340 	}
341 
342 	EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]);
343 
344 	switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) {
345 	case TAG_TYPE_SMALL_ITEM_DECODE:
346 		headlen = 1;
347 
348 		name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME);
349 		length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE);
350 
351 		break;
352 
353 	case TAG_TYPE_LARGE_ITEM_DECODE:
354 		headlen = 3;
355 
356 		if (*offsetp + headlen > size) {
357 			rc = EFAULT;
358 			goto fail2;
359 		}
360 
361 		name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME);
362 		EFX_POPULATE_WORD_2(word,
363 				    EFX_BYTE_0, data[*offsetp + 1],
364 				    EFX_BYTE_1, data[*offsetp + 2]);
365 		length = EFX_WORD_FIELD(word, EFX_WORD_0);
366 
367 		break;
368 
369 	default:
370 		rc = EFAULT;
371 		goto fail2;
372 	}
373 
374 	if (*offsetp + headlen + length > size) {
375 		rc = EFAULT;
376 		goto fail3;
377 	}
378 
379 	EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END);
380 	EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID);
381 	EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO);
382 	EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW);
383 	if (name != EFX_VPD_END && name != EFX_VPD_ID &&
384 	    name != EFX_VPD_RO) {
385 		rc = EFAULT;
386 		goto fail4;
387 	}
388 
389 	*tagp = name;
390 	*lengthp = length;
391 	*offsetp += headlen;
392 
393 	return (0);
394 
395 fail4:
396 	EFSYS_PROBE(fail4);
397 fail3:
398 	EFSYS_PROBE(fail3);
399 fail2:
400 	EFSYS_PROBE(fail2);
401 fail1:
402 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
403 
404 	return (rc);
405 }
406 
407 static	__checkReturn		efx_rc_t
efx_vpd_next_keyword(__in_bcount (size)caddr_t tag,__in size_t size,__in unsigned int pos,__out efx_vpd_keyword_t * keywordp,__out uint8_t * lengthp)408 efx_vpd_next_keyword(
409 	__in_bcount(size)	caddr_t tag,
410 	__in			size_t size,
411 	__in			unsigned int pos,
412 	__out			efx_vpd_keyword_t *keywordp,
413 	__out			uint8_t *lengthp)
414 {
415 	efx_vpd_keyword_t keyword;
416 	uint8_t length;
417 	efx_rc_t rc;
418 
419 	if (pos + 3U > size) {
420 		rc = EFAULT;
421 		goto fail1;
422 	}
423 
424 	keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]);
425 	length = tag[pos + 2];
426 
427 	if (length == 0 || pos + 3U + length > size) {
428 		rc = EFAULT;
429 		goto fail2;
430 	}
431 
432 	*keywordp = keyword;
433 	*lengthp = length;
434 
435 	return (0);
436 
437 fail2:
438 	EFSYS_PROBE(fail2);
439 fail1:
440 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
441 
442 	return (rc);
443 }
444 
445 	__checkReturn		efx_rc_t
efx_vpd_hunk_length(__in_bcount (size)caddr_t data,__in size_t size,__out size_t * lengthp)446 efx_vpd_hunk_length(
447 	__in_bcount(size)	caddr_t data,
448 	__in			size_t size,
449 	__out			size_t *lengthp)
450 {
451 	efx_vpd_tag_t tag;
452 	unsigned int offset;
453 	uint16_t taglen;
454 	efx_rc_t rc;
455 
456 	offset = 0;
457 	_NOTE(CONSTANTCONDITION)
458 	while (1) {
459 		if ((rc = efx_vpd_next_tag(data, size, &offset,
460 		    &tag, &taglen)) != 0)
461 			goto fail1;
462 		offset += taglen;
463 		if (tag == EFX_VPD_END)
464 			break;
465 	}
466 
467 	*lengthp = offset;
468 
469 	return (0);
470 
471 fail1:
472 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
473 
474 	return (rc);
475 }
476 
477 	__checkReturn		efx_rc_t
efx_vpd_hunk_verify(__in_bcount (size)caddr_t data,__in size_t size,__out_opt boolean_t * cksummedp)478 efx_vpd_hunk_verify(
479 	__in_bcount(size)	caddr_t data,
480 	__in			size_t size,
481 	__out_opt		boolean_t *cksummedp)
482 {
483 	efx_vpd_tag_t tag;
484 	efx_vpd_keyword_t keyword;
485 	unsigned int offset;
486 	unsigned int pos;
487 	unsigned int i;
488 	uint16_t taglen;
489 	uint8_t keylen;
490 	uint8_t cksum;
491 	boolean_t cksummed = B_FALSE;
492 	efx_rc_t rc;
493 
494 	/*
495 	 * Parse every tag,keyword in the existing VPD. If the csum is present,
496 	 * the assert it is correct, and is the final keyword in the RO block.
497 	 */
498 	offset = 0;
499 	_NOTE(CONSTANTCONDITION)
500 	while (1) {
501 		if ((rc = efx_vpd_next_tag(data, size, &offset,
502 		    &tag, &taglen)) != 0)
503 			goto fail1;
504 		if (tag == EFX_VPD_END)
505 			break;
506 		else if (tag == EFX_VPD_ID)
507 			goto done;
508 
509 		for (pos = 0; pos != taglen; pos += 3 + keylen) {
510 			/* RV keyword must be the last in the block */
511 			if (cksummed) {
512 				rc = EFAULT;
513 				goto fail2;
514 			}
515 
516 			if ((rc = efx_vpd_next_keyword(data + offset,
517 			    taglen, pos, &keyword, &keylen)) != 0)
518 				goto fail3;
519 
520 			if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
521 				cksum = 0;
522 				for (i = 0; i < offset + pos + 4; i++)
523 					cksum += data[i];
524 
525 				if (cksum != 0) {
526 					rc = EFAULT;
527 					goto fail4;
528 				}
529 
530 				cksummed = B_TRUE;
531 			}
532 		}
533 
534 	done:
535 		offset += taglen;
536 	}
537 
538 	if (!cksummed) {
539 		rc = EFAULT;
540 		goto fail5;
541 	}
542 
543 	if (cksummedp != NULL)
544 		*cksummedp = cksummed;
545 
546 	return (0);
547 
548 fail5:
549 	EFSYS_PROBE(fail5);
550 fail4:
551 	EFSYS_PROBE(fail4);
552 fail3:
553 	EFSYS_PROBE(fail3);
554 fail2:
555 	EFSYS_PROBE(fail2);
556 fail1:
557 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
558 
559 	return (rc);
560 }
561 
562 static	uint8_t	__efx_vpd_blank_pid[] = {
563 	/* Large resource type ID length 1 */
564 	0x82, 0x01, 0x00,
565 	/* Product name ' ' */
566 	0x32,
567 };
568 
569 static uint8_t __efx_vpd_blank_r[] = {
570 	/* Large resource type VPD-R length 4 */
571 	0x90, 0x04, 0x00,
572 	/* RV keyword length 1 */
573 	'R', 'V', 0x01,
574 	/* RV payload checksum */
575 	0x00,
576 };
577 
578 	__checkReturn		efx_rc_t
efx_vpd_hunk_reinit(__in_bcount (size)caddr_t data,__in size_t size,__in boolean_t wantpid)579 efx_vpd_hunk_reinit(
580 	__in_bcount(size)	caddr_t data,
581 	__in			size_t size,
582 	__in			boolean_t wantpid)
583 {
584 	unsigned int offset = 0;
585 	unsigned int pos;
586 	efx_byte_t byte;
587 	uint8_t cksum;
588 	efx_rc_t rc;
589 
590 	if (size < 0x100) {
591 		rc = ENOSPC;
592 		goto fail1;
593 	}
594 
595 	if (wantpid) {
596 		memcpy(data + offset, __efx_vpd_blank_pid,
597 		    sizeof (__efx_vpd_blank_pid));
598 		offset += sizeof (__efx_vpd_blank_pid);
599 	}
600 
601 	memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
602 	offset += sizeof (__efx_vpd_blank_r);
603 
604 	/* Update checksum */
605 	cksum = 0;
606 	for (pos = 0; pos < offset; pos++)
607 		cksum += data[pos];
608 	data[offset - 1] -= cksum;
609 
610 	/* Append trailing tag */
611 	EFX_POPULATE_BYTE_3(byte,
612 			    TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
613 			    TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
614 			    TAG_SMALL_ITEM_SIZE, 0);
615 	data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
616 	offset++;
617 
618 	return (0);
619 
620 fail1:
621 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
622 
623 	return (rc);
624 }
625 
626 	__checkReturn			efx_rc_t
efx_vpd_hunk_next(__in_bcount (size)caddr_t data,__in size_t size,__out efx_vpd_tag_t * tagp,__out efx_vpd_keyword_t * keywordp,__out_opt unsigned int * payloadp,__out_opt uint8_t * paylenp,__inout unsigned int * contp)627 efx_vpd_hunk_next(
628 	__in_bcount(size)		caddr_t data,
629 	__in				size_t size,
630 	__out				efx_vpd_tag_t *tagp,
631 	__out				efx_vpd_keyword_t *keywordp,
632 	__out_opt			unsigned int *payloadp,
633 	__out_opt			uint8_t *paylenp,
634 	__inout				unsigned int *contp)
635 {
636 	efx_vpd_tag_t tag;
637 	efx_vpd_keyword_t keyword = 0;
638 	unsigned int offset;
639 	unsigned int pos;
640 	unsigned int index;
641 	uint16_t taglen;
642 	uint8_t keylen;
643 	uint8_t paylen;
644 	efx_rc_t rc;
645 
646 	offset = index = 0;
647 	_NOTE(CONSTANTCONDITION)
648 	while (1) {
649 		if ((rc = efx_vpd_next_tag(data, size, &offset,
650 		    &tag, &taglen)) != 0)
651 			goto fail1;
652 
653 		if (tag == EFX_VPD_END) {
654 			keyword = 0;
655 			paylen = 0;
656 			index = 0;
657 			break;
658 		}
659 
660 		if (tag == EFX_VPD_ID) {
661 			if (index++ == *contp) {
662 				EFSYS_ASSERT3U(taglen, <, 0x100);
663 				keyword = 0;
664 				paylen = (uint8_t)MIN(taglen, 0xff);
665 
666 				goto done;
667 			}
668 		} else {
669 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
670 				if ((rc = efx_vpd_next_keyword(data + offset,
671 				    taglen, pos, &keyword, &keylen)) != 0)
672 					goto fail2;
673 
674 				if (index++ == *contp) {
675 					offset += pos + 3;
676 					paylen = keylen;
677 
678 					goto done;
679 				}
680 			}
681 		}
682 
683 		offset += taglen;
684 	}
685 
686 done:
687 	*tagp = tag;
688 	*keywordp = keyword;
689 	if (payloadp != NULL)
690 		*payloadp = offset;
691 	if (paylenp != NULL)
692 		*paylenp = paylen;
693 
694 	*contp = index;
695 	return (0);
696 
697 fail2:
698 	EFSYS_PROBE(fail2);
699 fail1:
700 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
701 
702 	return (rc);
703 }
704 
705 	__checkReturn		efx_rc_t
efx_vpd_hunk_get(__in_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_tag_t tag,__in efx_vpd_keyword_t keyword,__out unsigned int * payloadp,__out uint8_t * paylenp)706 efx_vpd_hunk_get(
707 	__in_bcount(size)	caddr_t data,
708 	__in			size_t size,
709 	__in			efx_vpd_tag_t tag,
710 	__in			efx_vpd_keyword_t keyword,
711 	__out			unsigned int *payloadp,
712 	__out			uint8_t *paylenp)
713 {
714 	efx_vpd_tag_t itag;
715 	efx_vpd_keyword_t ikeyword;
716 	unsigned int offset;
717 	unsigned int pos;
718 	uint16_t taglen;
719 	uint8_t keylen;
720 	efx_rc_t rc;
721 
722 	offset = 0;
723 	_NOTE(CONSTANTCONDITION)
724 	while (1) {
725 		if ((rc = efx_vpd_next_tag(data, size, &offset,
726 		    &itag, &taglen)) != 0)
727 			goto fail1;
728 		if (itag == EFX_VPD_END)
729 			break;
730 
731 		if (itag == tag) {
732 			if (itag == EFX_VPD_ID) {
733 				EFSYS_ASSERT3U(taglen, <, 0x100);
734 
735 				*paylenp = (uint8_t)MIN(taglen, 0xff);
736 				*payloadp = offset;
737 				return (0);
738 			}
739 
740 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
741 				if ((rc = efx_vpd_next_keyword(data + offset,
742 				    taglen, pos, &ikeyword, &keylen)) != 0)
743 					goto fail2;
744 
745 				if (ikeyword == keyword) {
746 					*paylenp = keylen;
747 					*payloadp = offset + pos + 3;
748 					return (0);
749 				}
750 			}
751 		}
752 
753 		offset += taglen;
754 	}
755 
756 	/* Not an error */
757 	return (ENOENT);
758 
759 fail2:
760 	EFSYS_PROBE(fail2);
761 fail1:
762 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
763 
764 	return (rc);
765 }
766 
767 	__checkReturn		efx_rc_t
efx_vpd_hunk_set(__in_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_value_t * evvp)768 efx_vpd_hunk_set(
769 	__in_bcount(size)	caddr_t data,
770 	__in			size_t size,
771 	__in			efx_vpd_value_t *evvp)
772 {
773 	efx_word_t word;
774 	efx_vpd_tag_t tag;
775 	efx_vpd_keyword_t keyword;
776 	unsigned int offset;
777 	unsigned int pos;
778 	unsigned int taghead;
779 	unsigned int source;
780 	unsigned int dest;
781 	unsigned int i;
782 	uint16_t taglen;
783 	uint8_t keylen;
784 	uint8_t cksum;
785 	size_t used;
786 	efx_rc_t rc;
787 
788 	switch (evvp->evv_tag) {
789 	case EFX_VPD_ID:
790 		if (evvp->evv_keyword != 0) {
791 			rc = EINVAL;
792 			goto fail1;
793 		}
794 
795 		/* Can't delete the ID keyword */
796 		if (evvp->evv_length == 0) {
797 			rc = EINVAL;
798 			goto fail1;
799 		}
800 		break;
801 
802 	case EFX_VPD_RO:
803 		if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
804 			rc = EINVAL;
805 			goto fail1;
806 		}
807 		break;
808 
809 	default:
810 		rc = EINVAL;
811 		goto fail1;
812 	}
813 
814 	/* Determine total size of all current tags */
815 	if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
816 		goto fail2;
817 
818 	offset = 0;
819 	_NOTE(CONSTANTCONDITION)
820 	while (1) {
821 		taghead = offset;
822 		if ((rc = efx_vpd_next_tag(data, size, &offset,
823 		    &tag, &taglen)) != 0)
824 			goto fail3;
825 		if (tag == EFX_VPD_END)
826 			break;
827 		else if (tag != evvp->evv_tag) {
828 			offset += taglen;
829 			continue;
830 		}
831 
832 		/* We only support modifying large resource tags */
833 		if (offset - taghead != 3) {
834 			rc = EINVAL;
835 			goto fail4;
836 		}
837 
838 		/*
839 		 * Work out the offset of the byte immediately after the
840 		 * old (=source) and new (=dest) new keyword/tag
841 		 */
842 		pos = 0;
843 		if (tag == EFX_VPD_ID) {
844 			source = offset + taglen;
845 			dest = offset + evvp->evv_length;
846 			goto check_space;
847 		}
848 
849 		EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
850 		source = dest = 0;
851 		for (pos = 0; pos != taglen; pos += 3 + keylen) {
852 			if ((rc = efx_vpd_next_keyword(data + offset,
853 			    taglen, pos, &keyword, &keylen)) != 0)
854 				goto fail5;
855 
856 			if (keyword == evvp->evv_keyword &&
857 			    evvp->evv_length == 0) {
858 				/* Deleting this keyword */
859 				source = offset + pos + 3 + keylen;
860 				dest = offset + pos;
861 				break;
862 
863 			} else if (keyword == evvp->evv_keyword) {
864 				/* Adjusting this keyword */
865 				source = offset + pos + 3 + keylen;
866 				dest = offset + pos + 3 + evvp->evv_length;
867 				break;
868 
869 			} else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
870 				/* The RV keyword must be at the end */
871 				EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
872 
873 				/*
874 				 * The keyword doesn't already exist. If the
875 				 * user deleting a non-existant keyword then
876 				 * this is a no-op.
877 				 */
878 				if (evvp->evv_length == 0)
879 					return (0);
880 
881 				/* Insert this keyword before the RV keyword */
882 				source = offset + pos;
883 				dest = offset + pos + 3 + evvp->evv_length;
884 				break;
885 			}
886 		}
887 
888 	check_space:
889 		if (used + dest > size + source) {
890 			rc = ENOSPC;
891 			goto fail6;
892 		}
893 
894 		/* Move trailing data */
895 		(void) memmove(data + dest, data + source, used - source);
896 
897 		/* Copy contents */
898 		memcpy(data + dest - evvp->evv_length, evvp->evv_value,
899 		    evvp->evv_length);
900 
901 		/* Insert new keyword header if required */
902 		if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
903 			EFX_POPULATE_WORD_1(word, EFX_WORD_0,
904 					    evvp->evv_keyword);
905 			data[offset + pos + 0] =
906 			    EFX_WORD_FIELD(word, EFX_BYTE_0);
907 			data[offset + pos + 1] =
908 			    EFX_WORD_FIELD(word, EFX_BYTE_1);
909 			data[offset + pos + 2] = evvp->evv_length;
910 		}
911 
912 		/* Modify tag length (large resource type) */
913 		taglen += (uint16_t)(dest - source);
914 		EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
915 		data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
916 		data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
917 
918 		goto checksum;
919 	}
920 
921 	/* Unable to find the matching tag */
922 	rc = ENOENT;
923 	goto fail7;
924 
925 checksum:
926 	/* Find the RV tag, and update the checksum */
927 	offset = 0;
928 	_NOTE(CONSTANTCONDITION)
929 	while (1) {
930 		if ((rc = efx_vpd_next_tag(data, size, &offset,
931 		    &tag, &taglen)) != 0)
932 			goto fail8;
933 		if (tag == EFX_VPD_END)
934 			break;
935 		if (tag == EFX_VPD_RO) {
936 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
937 				if ((rc = efx_vpd_next_keyword(data + offset,
938 				    taglen, pos, &keyword, &keylen)) != 0)
939 					goto fail9;
940 
941 				if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
942 					cksum = 0;
943 					for (i = 0; i < offset + pos + 3; i++)
944 						cksum += data[i];
945 					data[i] = -cksum;
946 					break;
947 				}
948 			}
949 		}
950 
951 		offset += taglen;
952 	}
953 
954 	/* Zero out the unused portion */
955 	(void) memset(data + offset + taglen, 0xff, size - offset - taglen);
956 
957 	return (0);
958 
959 fail9:
960 	EFSYS_PROBE(fail9);
961 fail8:
962 	EFSYS_PROBE(fail8);
963 fail7:
964 	EFSYS_PROBE(fail7);
965 fail6:
966 	EFSYS_PROBE(fail6);
967 fail5:
968 	EFSYS_PROBE(fail5);
969 fail4:
970 	EFSYS_PROBE(fail4);
971 fail3:
972 	EFSYS_PROBE(fail3);
973 fail2:
974 	EFSYS_PROBE(fail2);
975 fail1:
976 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
977 
978 	return (rc);
979 }
980 
981 				void
efx_vpd_fini(__in efx_nic_t * enp)982 efx_vpd_fini(
983 	__in			efx_nic_t *enp)
984 {
985 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
986 
987 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
988 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
989 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
990 
991 	if (evpdop->evpdo_fini != NULL)
992 		evpdop->evpdo_fini(enp);
993 
994 	enp->en_evpdop = NULL;
995 	enp->en_mod_flags &= ~EFX_MOD_VPD;
996 }
997 
998 #endif	/* EFSYS_OPT_VPD */
999