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
57static 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
74static 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
90efx_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
129fail2:
130	EFSYS_PROBE(fail2);
131fail1:
132	EFSYS_PROBE1(fail1, int, rc);
133
134	return (rc);
135}
136
137	__checkReturn		int
138efx_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
153fail1:
154	EFSYS_PROBE1(fail1, int, rc);
155
156	return (rc);
157}
158
159	__checkReturn		int
160efx_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
176fail1:
177	EFSYS_PROBE1(fail1, int, rc);
178
179	return (rc);
180}
181
182	__checkReturn		int
183efx_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
199fail1:
200	EFSYS_PROBE1(fail1, int, rc);
201
202	return (rc);
203}
204
205	__checkReturn		int
206efx_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
227fail2:
228	EFSYS_PROBE(fail2);
229fail1:
230	EFSYS_PROBE1(fail1, int, rc);
231
232	return (rc);
233}
234
235	__checkReturn		int
236efx_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
253fail1:
254	EFSYS_PROBE1(fail1, int, rc);
255
256	return (rc);
257}
258
259	__checkReturn		int
260efx_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
277fail1:
278	EFSYS_PROBE1(fail1, int, rc);
279
280	return (rc);
281}
282
283	__checkReturn		int
284efx_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
302fail1:
303	EFSYS_PROBE1(fail1, int, rc);
304
305	return (rc);
306}
307
308	__checkReturn		int
309efx_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
325fail1:
326	EFSYS_PROBE1(fail1, int, rc);
327
328	return (rc);
329}
330
331static	__checkReturn		int
332efx_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
404fail4:
405	EFSYS_PROBE(fail4);
406fail3:
407	EFSYS_PROBE(fail3);
408fail2:
409	EFSYS_PROBE(fail2);
410fail1:
411	EFSYS_PROBE1(fail1, int, rc);
412
413	return (rc);
414}
415
416static	__checkReturn		int
417efx_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
446fail2:
447	EFSYS_PROBE(fail2);
448fail1:
449	EFSYS_PROBE1(fail1, int, rc);
450
451	return (rc);
452}
453
454	__checkReturn		int
455efx_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
480fail1:
481	EFSYS_PROBE1(fail1, int, rc);
482
483	return (rc);
484}
485
486	__checkReturn		int
487efx_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
555fail5:
556	EFSYS_PROBE(fail5);
557fail4:
558	EFSYS_PROBE(fail4);
559fail3:
560	EFSYS_PROBE(fail3);
561fail2:
562	EFSYS_PROBE(fail2);
563fail1:
564	EFSYS_PROBE1(fail1, int, rc);
565
566	return (rc);
567}
568
569static	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
576static 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
586efx_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
627fail1:
628	EFSYS_PROBE1(fail1, int, rc);
629
630	return (rc);
631}
632
633	__checkReturn			int
634efx_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
690done:
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
701fail2:
702	EFSYS_PROBE(fail2);
703fail1:
704	EFSYS_PROBE1(fail1, int, rc);
705
706	return (rc);
707}
708
709	__checkReturn		int
710efx_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
763fail2:
764	EFSYS_PROBE(fail2);
765fail1:
766	EFSYS_PROBE1(fail1, int, rc);
767
768	return (rc);
769}
770
771	__checkReturn		int
772efx_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
929checksum:
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
963fail9:
964	EFSYS_PROBE(fail9);
965fail8:
966	EFSYS_PROBE(fail8);
967fail7:
968	EFSYS_PROBE(fail7);
969fail6:
970	EFSYS_PROBE(fail6);
971fail5:
972	EFSYS_PROBE(fail5);
973fail4:
974	EFSYS_PROBE(fail4);
975fail3:
976	EFSYS_PROBE(fail3);
977fail2:
978	EFSYS_PROBE(fail2);
979fail1:
980	EFSYS_PROBE1(fail1, int, rc);
981
982	return (rc);
983}
984
985				void
986efx_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