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