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