xref: /freebsd/sys/dev/sfxge/common/efx_vpd.c (revision 895f86f15fbf6540071feb9328c3c50ed1f027b8)
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		efx_rc_t
112 efx_vpd_init(
113 	__in			efx_nic_t *enp)
114 {
115 	efx_vpd_ops_t *evpdop;
116 	efx_rc_t 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, efx_rc_t, rc);
161 
162 	return (rc);
163 }
164 
165 	__checkReturn		efx_rc_t
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 	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_size(enp, sizep)) != 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_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 	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_read(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_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 	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 ((rc = evpdop->evpdo_verify(enp, data, size)) != 0)
223 		goto fail1;
224 
225 	return (0);
226 
227 fail1:
228 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
229 
230 	return (rc);
231 }
232 
233 	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
259 
260 	return (rc);
261 }
262 
263 	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
283 
284 	return (rc);
285 }
286 
287 	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
307 
308 	return (rc);
309 }
310 
311 	__checkReturn		efx_rc_t
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 	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_next(enp, data, size, evvp, contp)) != 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 	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
355 
356 	return (rc);
357 }
358 
359 static	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
440 
441 	return (rc);
442 }
443 
444 static	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
478 
479 	return (rc);
480 }
481 
482 	__checkReturn		efx_rc_t
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 	efx_rc_t 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, efx_rc_t, rc);
510 
511 	return (rc);
512 }
513 
514 	__checkReturn		efx_rc_t
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 	efx_rc_t 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 				rc = EFAULT;
550 				goto fail2;
551 			}
552 
553 			if ((rc = efx_vpd_next_keyword(data + offset,
554 			    taglen, pos, &keyword, &keylen)) != 0)
555 				goto fail3;
556 
557 			if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
558 				cksum = 0;
559 				for (i = 0; i < offset + pos + 4; i++)
560 					cksum += data[i];
561 
562 				if (cksum != 0) {
563 					rc = EFAULT;
564 					goto fail4;
565 				}
566 
567 				cksummed = B_TRUE;
568 			}
569 		}
570 
571 	done:
572 		offset += taglen;
573 	}
574 
575 	if (!cksummed) {
576 		rc = EFAULT;
577 		goto fail5;
578 	}
579 
580 	if (cksummedp != NULL)
581 		*cksummedp = cksummed;
582 
583 	return (0);
584 
585 fail5:
586 	EFSYS_PROBE(fail5);
587 fail4:
588 	EFSYS_PROBE(fail4);
589 fail3:
590 	EFSYS_PROBE(fail3);
591 fail2:
592 	EFSYS_PROBE(fail2);
593 fail1:
594 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
595 
596 	return (rc);
597 }
598 
599 static	uint8_t	__efx_vpd_blank_pid[] = {
600 	/* Large resource type ID length 1 */
601 	0x82, 0x01, 0x00,
602 	/* Product name ' ' */
603 	0x32,
604 };
605 
606 static uint8_t __efx_vpd_blank_r[] = {
607 	/* Large resource type VPD-R length 4 */
608 	0x90, 0x04, 0x00,
609 	/* RV keyword length 1 */
610 	'R', 'V', 0x01,
611 	/* RV payload checksum */
612 	0x00,
613 };
614 
615 	__checkReturn		efx_rc_t
616 efx_vpd_hunk_reinit(
617 	__in_bcount(size)	caddr_t data,
618 	__in			size_t size,
619 	__in			boolean_t wantpid)
620 {
621 	unsigned int offset = 0;
622 	unsigned int pos;
623 	efx_byte_t byte;
624 	uint8_t cksum;
625 	efx_rc_t rc;
626 
627 	if (size < 0x100) {
628 		rc = ENOSPC;
629 		goto fail1;
630 	}
631 
632 	if (wantpid) {
633 		memcpy(data + offset, __efx_vpd_blank_pid,
634 		    sizeof (__efx_vpd_blank_pid));
635 		offset += sizeof (__efx_vpd_blank_pid);
636 	}
637 
638 	memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
639 	offset += sizeof (__efx_vpd_blank_r);
640 
641 	/* Update checksum */
642 	cksum = 0;
643 	for (pos = 0; pos < offset; pos++)
644 		cksum += data[pos];
645 	data[offset - 1] -= cksum;
646 
647 	/* Append trailing tag */
648 	EFX_POPULATE_BYTE_3(byte,
649 			    TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
650 			    TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
651 			    TAG_SMALL_ITEM_SIZE, 0);
652 	data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
653 	offset++;
654 
655 	return (0);
656 
657 fail1:
658 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
659 
660 	return (rc);
661 }
662 
663 	__checkReturn			efx_rc_t
664 efx_vpd_hunk_next(
665 	__in_bcount(size)		caddr_t data,
666 	__in				size_t size,
667 	__out				efx_vpd_tag_t *tagp,
668 	__out				efx_vpd_keyword_t *keywordp,
669 	__out_bcount_opt(*paylenp)	unsigned int *payloadp,
670 	__out_opt			uint8_t *paylenp,
671 	__inout				unsigned int *contp)
672 {
673 	efx_vpd_tag_t tag;
674 	efx_vpd_keyword_t keyword = 0;
675 	unsigned int offset;
676 	unsigned int pos;
677 	unsigned int index;
678 	uint16_t taglen;
679 	uint8_t keylen;
680 	uint8_t paylen;
681 	efx_rc_t rc;
682 
683 	offset = index = 0;
684 	_NOTE(CONSTANTCONDITION)
685 	while (1) {
686 		if ((rc = efx_vpd_next_tag(data, size, &offset,
687 		    &tag, &taglen)) != 0)
688 			goto fail1;
689 		if (tag == EFX_VPD_END)
690 			break;
691 
692 		if (tag == EFX_VPD_ID) {
693 			if (index == *contp) {
694 				EFSYS_ASSERT3U(taglen, <, 0x100);
695 				paylen = (uint8_t)MIN(taglen, 0xff);
696 
697 				goto done;
698 			}
699 		} else {
700 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
701 				if ((rc = efx_vpd_next_keyword(data + offset,
702 				    taglen, pos, &keyword, &keylen)) != 0)
703 					goto fail2;
704 
705 				if (index == *contp) {
706 					offset += pos + 3;
707 					paylen = keylen;
708 
709 					goto done;
710 				}
711 			}
712 		}
713 
714 		offset += taglen;
715 	}
716 
717 	*contp = 0;
718 	return (0);
719 
720 done:
721 	*tagp = tag;
722 	*keywordp = keyword;
723 	if (payloadp != NULL)
724 		*payloadp = offset;
725 	if (paylenp != NULL)
726 		*paylenp = paylen;
727 
728 	++(*contp);
729 	return (0);
730 
731 fail2:
732 	EFSYS_PROBE(fail2);
733 fail1:
734 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
735 
736 	return (rc);
737 }
738 
739 	__checkReturn		efx_rc_t
740 efx_vpd_hunk_get(
741 	__in_bcount(size)	caddr_t data,
742 	__in			size_t size,
743 	__in			efx_vpd_tag_t tag,
744 	__in			efx_vpd_keyword_t keyword,
745 	__out			unsigned int *payloadp,
746 	__out			uint8_t *paylenp)
747 {
748 	efx_vpd_tag_t itag;
749 	efx_vpd_keyword_t ikeyword;
750 	unsigned int offset;
751 	unsigned int pos;
752 	uint16_t taglen;
753 	uint8_t keylen;
754 	efx_rc_t rc;
755 
756 	offset = 0;
757 	_NOTE(CONSTANTCONDITION)
758 	while (1) {
759 		if ((rc = efx_vpd_next_tag(data, size, &offset,
760 		    &itag, &taglen)) != 0)
761 			goto fail1;
762 		if (itag == EFX_VPD_END)
763 			break;
764 
765 		if (itag == tag) {
766 			if (itag == EFX_VPD_ID) {
767 				EFSYS_ASSERT3U(taglen, <, 0x100);
768 
769 				*paylenp = (uint8_t)MIN(taglen, 0xff);
770 				*payloadp = offset;
771 				return (0);
772 			}
773 
774 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
775 				if ((rc = efx_vpd_next_keyword(data + offset,
776 				    taglen, pos, &ikeyword, &keylen)) != 0)
777 					goto fail2;
778 
779 				if (ikeyword == keyword) {
780 					*paylenp = keylen;
781 					*payloadp = offset + pos + 3;
782 					return (0);
783 				}
784 			}
785 		}
786 
787 		offset += taglen;
788 	}
789 
790 	/* Not an error */
791 	return (ENOENT);
792 
793 fail2:
794 	EFSYS_PROBE(fail2);
795 fail1:
796 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
797 
798 	return (rc);
799 }
800 
801 	__checkReturn		efx_rc_t
802 efx_vpd_hunk_set(
803 	__in_bcount(size)	caddr_t data,
804 	__in			size_t size,
805 	__in			efx_vpd_value_t *evvp)
806 {
807 	efx_word_t word;
808 	efx_vpd_tag_t tag;
809 	efx_vpd_keyword_t keyword;
810 	unsigned int offset;
811 	unsigned int pos;
812 	unsigned int taghead;
813 	unsigned int source;
814 	unsigned int dest;
815 	unsigned int i;
816 	uint16_t taglen;
817 	uint8_t keylen;
818 	uint8_t cksum;
819 	size_t used;
820 	efx_rc_t rc;
821 
822 	switch (evvp->evv_tag) {
823 	case EFX_VPD_ID:
824 		if (evvp->evv_keyword != 0) {
825 			rc = EINVAL;
826 			goto fail1;
827 		}
828 
829 		/* Can't delete the ID keyword */
830 		if (evvp->evv_length == 0) {
831 			rc = EINVAL;
832 			goto fail1;
833 		}
834 		break;
835 
836 	case EFX_VPD_RO:
837 		if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
838 			rc = EINVAL;
839 			goto fail1;
840 		}
841 		break;
842 
843 	default:
844 		rc = EINVAL;
845 		goto fail1;
846 	}
847 
848 	/* Determine total size of all current tags */
849 	if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
850 		goto fail2;
851 
852 	offset = 0;
853 	_NOTE(CONSTANTCONDITION)
854 	while (1) {
855 		taghead = offset;
856 		if ((rc = efx_vpd_next_tag(data, size, &offset,
857 		    &tag, &taglen)) != 0)
858 			goto fail3;
859 		if (tag == EFX_VPD_END)
860 			break;
861 		else if (tag != evvp->evv_tag) {
862 			offset += taglen;
863 			continue;
864 		}
865 
866 		/* We only support modifying large resource tags */
867 		if (offset - taghead != 3) {
868 			rc = EINVAL;
869 			goto fail4;
870 		}
871 
872 		/*
873 		 * Work out the offset of the byte immediately after the
874 		 * old (=source) and new (=dest) new keyword/tag
875 		 */
876 		pos = 0;
877 		if (tag == EFX_VPD_ID) {
878 			source = offset + taglen;
879 			dest = offset + evvp->evv_length;
880 			goto check_space;
881 		}
882 
883 		EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
884 		source = dest = 0;
885 		for (pos = 0; pos != taglen; pos += 3 + keylen) {
886 			if ((rc = efx_vpd_next_keyword(data + offset,
887 			    taglen, pos, &keyword, &keylen)) != 0)
888 				goto fail5;
889 
890 			if (keyword == evvp->evv_keyword &&
891 			    evvp->evv_length == 0) {
892 				/* Deleting this keyword */
893 				source = offset + pos + 3 + keylen;
894 				dest = offset + pos;
895 				break;
896 
897 			} else if (keyword == evvp->evv_keyword) {
898 				/* Adjusting this keyword */
899 				source = offset + pos + 3 + keylen;
900 				dest = offset + pos + 3 + evvp->evv_length;
901 				break;
902 
903 			} else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
904 				/* The RV keyword must be at the end */
905 				EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
906 
907 				/*
908 				 * The keyword doesn't already exist. If the
909 				 * user deleting a non-existant keyword then
910 				 * this is a no-op.
911 				 */
912 				if (evvp->evv_length == 0)
913 					return (0);
914 
915 				/* Insert this keyword before the RV keyword */
916 				source = offset + pos;
917 				dest = offset + pos + 3 + evvp->evv_length;
918 				break;
919 			}
920 		}
921 
922 	check_space:
923 		if (used + dest > size + source) {
924 			rc = ENOSPC;
925 			goto fail6;
926 		}
927 
928 		/* Move trailing data */
929 		(void) memmove(data + dest, data + source, used - source);
930 
931 		/* Copy contents */
932 		memcpy(data + dest - evvp->evv_length, evvp->evv_value,
933 		    evvp->evv_length);
934 
935 		/* Insert new keyword header if required */
936 		if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
937 			EFX_POPULATE_WORD_1(word, EFX_WORD_0,
938 					    evvp->evv_keyword);
939 			data[offset + pos + 0] =
940 			    EFX_WORD_FIELD(word, EFX_BYTE_0);
941 			data[offset + pos + 1] =
942 			    EFX_WORD_FIELD(word, EFX_BYTE_1);
943 			data[offset + pos + 2] = evvp->evv_length;
944 		}
945 
946 		/* Modify tag length (large resource type) */
947 		taglen += (dest - source);
948 		EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
949 		data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
950 		data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
951 
952 		goto checksum;
953 	}
954 
955 	/* Unable to find the matching tag */
956 	rc = ENOENT;
957 	goto fail7;
958 
959 checksum:
960 	/* Find the RV tag, and update the checksum */
961 	offset = 0;
962 	_NOTE(CONSTANTCONDITION)
963 	while (1) {
964 		if ((rc = efx_vpd_next_tag(data, size, &offset,
965 		    &tag, &taglen)) != 0)
966 			goto fail8;
967 		if (tag == EFX_VPD_END)
968 			break;
969 		if (tag == EFX_VPD_RO) {
970 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
971 				if ((rc = efx_vpd_next_keyword(data + offset,
972 				    taglen, pos, &keyword, &keylen)) != 0)
973 					goto fail9;
974 
975 				if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
976 					cksum = 0;
977 					for (i = 0; i < offset + pos + 3; i++)
978 						cksum += data[i];
979 					data[i] = -cksum;
980 					break;
981 				}
982 			}
983 		}
984 
985 		offset += taglen;
986 	}
987 
988 	/* Zero out the unused portion */
989 	(void) memset(data + offset + taglen, 0xff, size - offset - taglen);
990 
991 	return (0);
992 
993 fail9:
994 	EFSYS_PROBE(fail9);
995 fail8:
996 	EFSYS_PROBE(fail8);
997 fail7:
998 	EFSYS_PROBE(fail7);
999 fail6:
1000 	EFSYS_PROBE(fail6);
1001 fail5:
1002 	EFSYS_PROBE(fail5);
1003 fail4:
1004 	EFSYS_PROBE(fail4);
1005 fail3:
1006 	EFSYS_PROBE(fail3);
1007 fail2:
1008 	EFSYS_PROBE(fail2);
1009 fail1:
1010 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1011 
1012 	return (rc);
1013 }
1014 
1015 				void
1016 efx_vpd_fini(
1017 	__in			efx_nic_t *enp)
1018 {
1019 	efx_vpd_ops_t *evpdop = enp->en_evpdop;
1020 
1021 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1022 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1023 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
1024 
1025 	if (evpdop->evpdo_fini != NULL)
1026 		evpdop->evpdo_fini(enp);
1027 
1028 	enp->en_evpdop = NULL;
1029 	enp->en_mod_flags &= ~EFX_MOD_VPD;
1030 }
1031 
1032 #endif	/* EFSYS_OPT_VPD */
1033