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