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