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