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
54static 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
71static 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
87efx_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
126fail2:
127	EFSYS_PROBE(fail2);
128fail1:
129	EFSYS_PROBE1(fail1, int, rc);
130
131	return (rc);
132}
133
134	__checkReturn		int
135efx_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
150fail1:
151	EFSYS_PROBE1(fail1, int, rc);
152
153	return (rc);
154}
155
156	__checkReturn		int
157efx_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
173fail1:
174	EFSYS_PROBE1(fail1, int, rc);
175
176	return (rc);
177}
178
179	__checkReturn		int
180efx_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
196fail1:
197	EFSYS_PROBE1(fail1, int, rc);
198
199	return (rc);
200}
201
202	__checkReturn		int
203efx_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
224fail2:
225	EFSYS_PROBE(fail2);
226fail1:
227	EFSYS_PROBE1(fail1, int, rc);
228
229	return (rc);
230}
231
232	__checkReturn		int
233efx_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
250fail1:
251	EFSYS_PROBE1(fail1, int, rc);
252
253	return (rc);
254}
255
256	__checkReturn		int
257efx_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
274fail1:
275	EFSYS_PROBE1(fail1, int, rc);
276
277	return (rc);
278}
279
280	__checkReturn		int
281efx_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
299fail1:
300	EFSYS_PROBE1(fail1, int, rc);
301
302	return (rc);
303}
304
305	__checkReturn		int
306efx_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
322fail1:
323	EFSYS_PROBE1(fail1, int, rc);
324
325	return (rc);
326}
327
328static	__checkReturn		int
329efx_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
401fail4:
402	EFSYS_PROBE(fail4);
403fail3:
404	EFSYS_PROBE(fail3);
405fail2:
406	EFSYS_PROBE(fail2);
407fail1:
408	EFSYS_PROBE1(fail1, int, rc);
409
410	return (rc);
411}
412
413static	__checkReturn		int
414efx_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
443fail2:
444	EFSYS_PROBE(fail2);
445fail1:
446	EFSYS_PROBE1(fail1, int, rc);
447
448	return (rc);
449}
450
451	__checkReturn		int
452efx_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
477fail1:
478	EFSYS_PROBE1(fail1, int, rc);
479
480	return (rc);
481}
482
483	__checkReturn		int
484efx_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
552fail5:
553	EFSYS_PROBE(fail5);
554fail4:
555	EFSYS_PROBE(fail4);
556fail3:
557	EFSYS_PROBE(fail3);
558fail2:
559	EFSYS_PROBE(fail2);
560fail1:
561	EFSYS_PROBE1(fail1, int, rc);
562
563	return (rc);
564}
565
566static	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
573static 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
583efx_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
624fail1:
625	EFSYS_PROBE1(fail1, int, rc);
626
627	return (rc);
628}
629
630	__checkReturn			int
631efx_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 idx;
645	uint16_t taglen;
646	uint8_t keylen;
647	uint8_t paylen;
648	int rc;
649
650	offset = idx = 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 (idx == *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 (idx == *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
687done:
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
698fail2:
699	EFSYS_PROBE(fail2);
700fail1:
701	EFSYS_PROBE1(fail1, int, rc);
702
703	return (rc);
704}
705
706	__checkReturn		int
707efx_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
760fail2:
761	EFSYS_PROBE(fail2);
762fail1:
763	EFSYS_PROBE1(fail1, int, rc);
764
765	return (rc);
766}
767
768	__checkReturn		int
769efx_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
926checksum:
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
960fail9:
961	EFSYS_PROBE(fail9);
962fail8:
963	EFSYS_PROBE(fail8);
964fail7:
965	EFSYS_PROBE(fail7);
966fail6:
967	EFSYS_PROBE(fail6);
968fail5:
969	EFSYS_PROBE(fail5);
970fail4:
971	EFSYS_PROBE(fail4);
972fail3:
973	EFSYS_PROBE(fail3);
974fail2:
975	EFSYS_PROBE(fail2);
976fail1:
977	EFSYS_PROBE1(fail1, int, rc);
978
979	return (rc);
980}
981
982				void
983efx_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