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