1227569Sphilip/*-
2227569Sphilip * Copyright 2009 Solarflare Communications Inc.  All rights reserved.
3227569Sphilip *
4227569Sphilip * Redistribution and use in source and binary forms, with or without
5227569Sphilip * modification, are permitted provided that the following conditions
6227569Sphilip * are met:
7227569Sphilip * 1. Redistributions of source code must retain the above copyright
8227569Sphilip *    notice, this list of conditions and the following disclaimer.
9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
10227569Sphilip *    notice, this list of conditions and the following disclaimer in the
11227569Sphilip *    documentation and/or other materials provided with the distribution.
12227569Sphilip *
13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23227569Sphilip * SUCH DAMAGE.
24227569Sphilip */
25227569Sphilip
26228100Sphilip#include <sys/cdefs.h>
27228100Sphilip__FBSDID("$FreeBSD$");
28228100Sphilip
29227569Sphilip#include "efsys.h"
30227569Sphilip#include "efx.h"
31227569Sphilip#include "efx_types.h"
32227569Sphilip#include "efx_regs.h"
33227569Sphilip#include "efx_impl.h"
34227569Sphilip
35227569Sphilip#if EFSYS_OPT_VPD
36227569Sphilip
37227569Sphilip#define	TAG_TYPE_LBN 7
38227569Sphilip#define	TAG_TYPE_WIDTH 1
39227569Sphilip#define	TAG_TYPE_LARGE_ITEM_DECODE 1
40227569Sphilip#define	TAG_TYPE_SMALL_ITEM_DECODE 0
41227569Sphilip
42227569Sphilip#define	TAG_SMALL_ITEM_NAME_LBN 3
43227569Sphilip#define	TAG_SMALL_ITEM_NAME_WIDTH 4
44227569Sphilip#define	TAG_SMALL_ITEM_SIZE_LBN 0
45227569Sphilip#define	TAG_SMALL_ITEM_SIZE_WIDTH 3
46227569Sphilip
47227569Sphilip#define	TAG_LARGE_ITEM_NAME_LBN 0
48227569Sphilip#define	TAG_LARGE_ITEM_NAME_WIDTH 7
49227569Sphilip
50227569Sphilip#define	TAG_NAME_END_DECODE 0x0f
51227569Sphilip#define	TAG_NAME_ID_STRING_DECODE 0x02
52227569Sphilip#define	TAG_NAME_VPD_R_DECODE 0x10
53227569Sphilip#define	TAG_NAME_VPD_W_DECODE 0x11
54227569Sphilip
55227569Sphilip#if EFSYS_OPT_FALCON
56227569Sphilip
57227569Sphilipstatic efx_vpd_ops_t	__cs	__efx_vpd_falcon_ops = {
58227569Sphilip	NULL,			/* evpdo_init */
59227569Sphilip	falcon_vpd_size,	/* evpdo_size */
60227569Sphilip	falcon_vpd_read,	/* evpdo_read */
61227569Sphilip	falcon_vpd_verify,	/* evpdo_verify */
62227569Sphilip	NULL,			/* evpdo_reinit */
63227569Sphilip	falcon_vpd_get,		/* evpdo_get */
64227569Sphilip	falcon_vpd_set,		/* evpdo_set */
65227569Sphilip	falcon_vpd_next,	/* evpdo_next */
66227569Sphilip	falcon_vpd_write,	/* evpdo_write */
67227569Sphilip	NULL,			/* evpdo_fini */
68227569Sphilip};
69227569Sphilip
70227569Sphilip#endif	/* EFSYS_OPT_FALCON */
71227569Sphilip
72227569Sphilip#if EFSYS_OPT_SIENA
73227569Sphilip
74227569Sphilipstatic efx_vpd_ops_t	__cs	__efx_vpd_siena_ops = {
75227569Sphilip	siena_vpd_init,		/* evpdo_init */
76227569Sphilip	siena_vpd_size,		/* evpdo_size */
77227569Sphilip	siena_vpd_read,		/* evpdo_read */
78227569Sphilip	siena_vpd_verify,	/* evpdo_verify */
79227569Sphilip	siena_vpd_reinit,	/* evpdo_reinit */
80227569Sphilip	siena_vpd_get,		/* evpdo_get */
81227569Sphilip	siena_vpd_set,		/* evpdo_set */
82227569Sphilip	siena_vpd_next,		/* evpdo_next */
83227569Sphilip	siena_vpd_write,	/* evpdo_write */
84227569Sphilip	siena_vpd_fini,		/* evpdo_fini */
85227569Sphilip};
86227569Sphilip
87227569Sphilip#endif	/* EFSYS_OPT_SIENA */
88227569Sphilip
89227569Sphilip	__checkReturn		int
90227569Sphilipefx_vpd_init(
91227569Sphilip	__in			efx_nic_t *enp)
92227569Sphilip{
93227569Sphilip	efx_vpd_ops_t *evpdop;
94227569Sphilip	int rc;
95227569Sphilip
96227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
97227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
98227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD));
99227569Sphilip
100227569Sphilip	switch (enp->en_family) {
101227569Sphilip#if EFSYS_OPT_FALCON
102227569Sphilip	case EFX_FAMILY_FALCON:
103227569Sphilip		evpdop = (efx_vpd_ops_t *)&__efx_vpd_falcon_ops;
104227569Sphilip		break;
105227569Sphilip#endif	/* EFSYS_OPT_FALCON */
106227569Sphilip
107227569Sphilip#if EFSYS_OPT_SIENA
108227569Sphilip	case EFX_FAMILY_SIENA:
109227569Sphilip		evpdop = (efx_vpd_ops_t *)&__efx_vpd_siena_ops;
110227569Sphilip		break;
111227569Sphilip#endif	/* EFSYS_OPT_SIENA */
112227569Sphilip
113227569Sphilip	default:
114227569Sphilip		EFSYS_ASSERT(0);
115227569Sphilip		rc = ENOTSUP;
116227569Sphilip		goto fail1;
117227569Sphilip	}
118227569Sphilip
119227569Sphilip	if (evpdop->evpdo_init != NULL) {
120227569Sphilip		if ((rc = evpdop->evpdo_init(enp)) != 0)
121227569Sphilip			goto fail2;
122227569Sphilip	}
123227569Sphilip
124227569Sphilip	enp->en_evpdop = evpdop;
125227569Sphilip	enp->en_mod_flags |= EFX_MOD_VPD;
126227569Sphilip
127227569Sphilip	return (0);
128227569Sphilip
129227569Sphilipfail2:
130227569Sphilip	EFSYS_PROBE(fail2);
131227569Sphilipfail1:
132227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
133227569Sphilip
134227569Sphilip	return (rc);
135227569Sphilip}
136227569Sphilip
137227569Sphilip	__checkReturn		int
138227569Sphilipefx_vpd_size(
139227569Sphilip	__in			efx_nic_t *enp,
140227569Sphilip	__out			size_t *sizep)
141227569Sphilip{
142227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
143227569Sphilip	int rc;
144227569Sphilip
145227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
146227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
147227569Sphilip
148227569Sphilip	if ((rc = evpdop->evpdo_size(enp, sizep)) != 0)
149227569Sphilip		goto fail1;
150227569Sphilip
151227569Sphilip	return (0);
152227569Sphilip
153227569Sphilipfail1:
154227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
155227569Sphilip
156227569Sphilip	return (rc);
157227569Sphilip}
158227569Sphilip
159227569Sphilip	__checkReturn		int
160227569Sphilipefx_vpd_read(
161227569Sphilip	__in			efx_nic_t *enp,
162227569Sphilip	__out_bcount(size)	caddr_t data,
163227569Sphilip	__in			size_t size)
164227569Sphilip{
165227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
166227569Sphilip	int rc;
167227569Sphilip
168227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
169227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
170227569Sphilip
171227569Sphilip	if ((rc = evpdop->evpdo_read(enp, data, size)) != 0)
172227569Sphilip		goto fail1;
173227569Sphilip
174227569Sphilip	return (0);
175227569Sphilip
176227569Sphilipfail1:
177227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
178227569Sphilip
179227569Sphilip	return (rc);
180227569Sphilip}
181227569Sphilip
182227569Sphilip	__checkReturn		int
183227569Sphilipefx_vpd_verify(
184227569Sphilip	__in			efx_nic_t *enp,
185227569Sphilip	__in_bcount(size)	caddr_t data,
186227569Sphilip	__in			size_t size)
187227569Sphilip{
188227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
189227569Sphilip	int rc;
190227569Sphilip
191227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
192227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
193227569Sphilip
194227569Sphilip	if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0)
195227569Sphilip		goto fail1;
196227569Sphilip
197227569Sphilip	return (0);
198227569Sphilip
199227569Sphilipfail1:
200227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
201227569Sphilip
202227569Sphilip	return (rc);
203227569Sphilip}
204227569Sphilip
205227569Sphilip	__checkReturn		int
206227569Sphilipefx_vpd_reinit(
207227569Sphilip	__in			efx_nic_t *enp,
208227569Sphilip	__in_bcount(size)	caddr_t data,
209227569Sphilip	__in			size_t size)
210227569Sphilip{
211227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
212227569Sphilip	int rc;
213227569Sphilip
214227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
215227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
216227569Sphilip
217227569Sphilip	if (evpdop->evpdo_reinit == NULL) {
218227569Sphilip		rc = ENOTSUP;
219227569Sphilip		goto fail1;
220227569Sphilip	}
221227569Sphilip
222227569Sphilip	if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0)
223227569Sphilip		goto fail2;
224227569Sphilip
225227569Sphilip	return (0);
226227569Sphilip
227227569Sphilipfail2:
228227569Sphilip	EFSYS_PROBE(fail2);
229227569Sphilipfail1:
230227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
231227569Sphilip
232227569Sphilip	return (rc);
233227569Sphilip}
234227569Sphilip
235227569Sphilip	__checkReturn		int
236227569Sphilipefx_vpd_get(
237227569Sphilip	__in			efx_nic_t *enp,
238227569Sphilip	__in_bcount(size)	caddr_t data,
239227569Sphilip	__in			size_t size,
240227569Sphilip	__inout			efx_vpd_value_t *evvp)
241227569Sphilip{
242227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
243227569Sphilip	int rc;
244227569Sphilip
245227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
246227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
247227569Sphilip
248227569Sphilip	if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0)
249227569Sphilip		goto fail1;
250227569Sphilip
251227569Sphilip	return (0);
252227569Sphilip
253227569Sphilipfail1:
254227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
255227569Sphilip
256227569Sphilip	return (rc);
257227569Sphilip}
258227569Sphilip
259227569Sphilip	__checkReturn		int
260227569Sphilipefx_vpd_set(
261227569Sphilip	__in			efx_nic_t *enp,
262227569Sphilip	__inout_bcount(size)	caddr_t data,
263227569Sphilip	__in			size_t size,
264227569Sphilip	__in			efx_vpd_value_t *evvp)
265227569Sphilip{
266227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
267227569Sphilip	int rc;
268227569Sphilip
269227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
270227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
271227569Sphilip
272227569Sphilip	if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0)
273227569Sphilip		goto fail1;
274227569Sphilip
275227569Sphilip	return (0);
276227569Sphilip
277227569Sphilipfail1:
278227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
279227569Sphilip
280227569Sphilip	return (rc);
281227569Sphilip}
282227569Sphilip
283227569Sphilip	__checkReturn		int
284227569Sphilipefx_vpd_next(
285227569Sphilip	__in			efx_nic_t *enp,
286227569Sphilip	__inout_bcount(size)	caddr_t data,
287227569Sphilip	__in			size_t size,
288227569Sphilip	__out			efx_vpd_value_t *evvp,
289227569Sphilip	__inout			unsigned int *contp)
290227569Sphilip{
291227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
292227569Sphilip	int rc;
293227569Sphilip
294227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
295227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
296227569Sphilip
297227569Sphilip	if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0)
298227569Sphilip		goto fail1;
299227569Sphilip
300227569Sphilip	return (0);
301227569Sphilip
302227569Sphilipfail1:
303227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
304227569Sphilip
305227569Sphilip	return (rc);
306227569Sphilip}
307227569Sphilip
308227569Sphilip	__checkReturn		int
309227569Sphilipefx_vpd_write(
310227569Sphilip	__in			efx_nic_t *enp,
311227569Sphilip	__in_bcount(size)	caddr_t data,
312227569Sphilip	__in			size_t size)
313227569Sphilip{
314227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
315227569Sphilip	int rc;
316227569Sphilip
317227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
318227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
319227569Sphilip
320227569Sphilip	if ((rc = evpdop->evpdo_write(enp, data, size)) != 0)
321227569Sphilip		goto fail1;
322227569Sphilip
323227569Sphilip	return (0);
324227569Sphilip
325227569Sphilipfail1:
326227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
327227569Sphilip
328227569Sphilip	return (rc);
329227569Sphilip}
330227569Sphilip
331227569Sphilipstatic	__checkReturn		int
332227569Sphilipefx_vpd_next_tag(
333227569Sphilip	__in			caddr_t data,
334227569Sphilip	__in			size_t size,
335227569Sphilip	__inout			unsigned int *offsetp,
336227569Sphilip	__out			efx_vpd_tag_t *tagp,
337227569Sphilip	__out			uint16_t *lengthp)
338227569Sphilip{
339227569Sphilip	efx_byte_t byte;
340227569Sphilip	efx_word_t word;
341227569Sphilip	uint8_t name;
342227569Sphilip	uint16_t length;
343227569Sphilip	size_t headlen;
344227569Sphilip	int rc;
345227569Sphilip
346227569Sphilip	if (*offsetp >= size) {
347227569Sphilip		rc = EFAULT;
348227569Sphilip		goto fail1;
349227569Sphilip	}
350227569Sphilip
351227569Sphilip	EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]);
352227569Sphilip
353227569Sphilip	switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) {
354227569Sphilip	case TAG_TYPE_SMALL_ITEM_DECODE:
355227569Sphilip		headlen = 1;
356227569Sphilip
357227569Sphilip		name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME);
358227569Sphilip		length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE);
359227569Sphilip
360227569Sphilip		break;
361227569Sphilip
362227569Sphilip	case TAG_TYPE_LARGE_ITEM_DECODE:
363227569Sphilip		headlen = 3;
364227569Sphilip
365227569Sphilip		if (*offsetp + headlen > size) {
366227569Sphilip			rc = EFAULT;
367227569Sphilip			goto fail2;
368227569Sphilip		}
369227569Sphilip
370227569Sphilip		name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME);
371227569Sphilip		EFX_POPULATE_WORD_2(word,
372227569Sphilip				    EFX_BYTE_0, data[*offsetp + 1],
373227569Sphilip				    EFX_BYTE_1, data[*offsetp + 2]);
374227569Sphilip		length = EFX_WORD_FIELD(word, EFX_WORD_0);
375227569Sphilip
376227569Sphilip		break;
377227569Sphilip
378227569Sphilip	default:
379227569Sphilip		rc = EFAULT;
380227569Sphilip		goto fail2;
381227569Sphilip	}
382227569Sphilip
383227569Sphilip	if (*offsetp + headlen + length > size) {
384227569Sphilip		rc = EFAULT;
385227569Sphilip		goto fail3;
386227569Sphilip	}
387227569Sphilip
388227569Sphilip	EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END);
389227569Sphilip	EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID);
390227569Sphilip	EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO);
391227569Sphilip	EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW);
392227569Sphilip	if (name != EFX_VPD_END && name != EFX_VPD_ID &&
393227569Sphilip	    name != EFX_VPD_RO) {
394227569Sphilip		rc = EFAULT;
395227569Sphilip		goto fail4;
396227569Sphilip	}
397227569Sphilip
398227569Sphilip	*tagp = name;
399227569Sphilip	*lengthp = length;
400227569Sphilip	*offsetp += headlen;
401227569Sphilip
402227569Sphilip	return (0);
403227569Sphilip
404227569Sphilipfail4:
405227569Sphilip	EFSYS_PROBE(fail4);
406227569Sphilipfail3:
407227569Sphilip	EFSYS_PROBE(fail3);
408227569Sphilipfail2:
409227569Sphilip	EFSYS_PROBE(fail2);
410227569Sphilipfail1:
411227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
412227569Sphilip
413227569Sphilip	return (rc);
414227569Sphilip}
415227569Sphilip
416227569Sphilipstatic	__checkReturn		int
417227569Sphilipefx_vpd_next_keyword(
418227569Sphilip	__in_bcount(size)	caddr_t tag,
419227569Sphilip	__in			size_t size,
420227569Sphilip	__in			unsigned int pos,
421227569Sphilip	__out			efx_vpd_keyword_t *keywordp,
422227569Sphilip	__out			uint8_t *lengthp)
423227569Sphilip{
424227569Sphilip	efx_vpd_keyword_t keyword;
425227569Sphilip	uint8_t length;
426227569Sphilip	int rc;
427227569Sphilip
428227569Sphilip	if (pos + 3U > size) {
429227569Sphilip		rc = EFAULT;
430227569Sphilip		goto fail1;
431227569Sphilip	}
432227569Sphilip
433227569Sphilip	keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]);
434227569Sphilip	length = tag[pos + 2];
435227569Sphilip
436227569Sphilip	if (length == 0 || pos + 3U + length > size) {
437227569Sphilip		rc = EFAULT;
438227569Sphilip		goto fail2;
439227569Sphilip	}
440227569Sphilip
441227569Sphilip	*keywordp = keyword;
442227569Sphilip	*lengthp = length;
443227569Sphilip
444227569Sphilip	return (0);
445227569Sphilip
446227569Sphilipfail2:
447227569Sphilip	EFSYS_PROBE(fail2);
448227569Sphilipfail1:
449227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
450227569Sphilip
451227569Sphilip	return (rc);
452227569Sphilip}
453227569Sphilip
454227569Sphilip	__checkReturn		int
455227569Sphilipefx_vpd_hunk_length(
456227569Sphilip	__in_bcount(size)	caddr_t data,
457227569Sphilip	__in			size_t size,
458227569Sphilip	__out			size_t *lengthp)
459227569Sphilip{
460227569Sphilip	efx_vpd_tag_t tag;
461227569Sphilip	unsigned int offset;
462227569Sphilip	uint16_t taglen;
463227569Sphilip	int rc;
464227569Sphilip
465227569Sphilip	offset = 0;
466227569Sphilip	_NOTE(CONSTANTCONDITION)
467227569Sphilip	while (1) {
468227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
469227569Sphilip		    &tag, &taglen)) != 0)
470227569Sphilip			goto fail1;
471227569Sphilip		offset += taglen;
472227569Sphilip		if (tag == EFX_VPD_END)
473227569Sphilip			break;
474227569Sphilip	}
475227569Sphilip
476227569Sphilip	*lengthp = offset;
477227569Sphilip
478227569Sphilip	return (0);
479227569Sphilip
480227569Sphilipfail1:
481227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
482227569Sphilip
483227569Sphilip	return (rc);
484227569Sphilip}
485227569Sphilip
486227569Sphilip	__checkReturn		int
487227569Sphilipefx_vpd_hunk_verify(
488227569Sphilip	__in_bcount(size)	caddr_t data,
489227569Sphilip	__in			size_t size,
490227569Sphilip	__out_opt		boolean_t *cksummedp)
491227569Sphilip{
492227569Sphilip	efx_vpd_tag_t tag;
493227569Sphilip	efx_vpd_keyword_t keyword;
494227569Sphilip	unsigned int offset;
495227569Sphilip	unsigned int pos;
496227569Sphilip	unsigned int i;
497227569Sphilip	uint16_t taglen;
498227569Sphilip	uint8_t keylen;
499227569Sphilip	uint8_t cksum;
500227569Sphilip	boolean_t cksummed = B_FALSE;
501227569Sphilip	int rc;
502227569Sphilip
503227569Sphilip	/*
504227569Sphilip	 * Parse every tag,keyword in the existing VPD. If the csum is present,
505227569Sphilip	 * the assert it is correct, and is the final keyword in the RO block.
506227569Sphilip	 */
507227569Sphilip	offset = 0;
508227569Sphilip	_NOTE(CONSTANTCONDITION)
509227569Sphilip	while (1) {
510227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
511227569Sphilip		    &tag, &taglen)) != 0)
512227569Sphilip			goto fail1;
513227569Sphilip		if (tag == EFX_VPD_END)
514227569Sphilip			break;
515227569Sphilip		else if (tag == EFX_VPD_ID)
516227569Sphilip			goto done;
517227569Sphilip
518227569Sphilip		for (pos = 0; pos != taglen; pos += 3 + keylen) {
519227569Sphilip			/* RV keyword must be the last in the block */
520227569Sphilip			if (cksummed)
521227569Sphilip				goto fail2;
522227569Sphilip
523227569Sphilip			if ((rc = efx_vpd_next_keyword(data + offset,
524227569Sphilip			    taglen, pos, &keyword, &keylen)) != 0)
525227569Sphilip				goto fail3;
526227569Sphilip
527227569Sphilip			if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
528227569Sphilip				cksum = 0;
529227569Sphilip				for (i = 0; i < offset + pos + 4; i++)
530227569Sphilip					cksum += data[i];
531227569Sphilip
532227569Sphilip				if (cksum != 0) {
533227569Sphilip					rc = EFAULT;
534227569Sphilip					goto fail4;
535227569Sphilip				}
536227569Sphilip
537227569Sphilip				cksummed = B_TRUE;
538227569Sphilip			}
539227569Sphilip		}
540227569Sphilip
541227569Sphilip	done:
542227569Sphilip		offset += taglen;
543227569Sphilip	}
544227569Sphilip
545227569Sphilip	if (!cksummed) {
546227569Sphilip		rc = EFAULT;
547227569Sphilip		goto fail5;
548227569Sphilip	}
549227569Sphilip
550227569Sphilip	if (cksummedp != NULL)
551227569Sphilip		*cksummedp = cksummed;
552227569Sphilip
553227569Sphilip	return (0);
554227569Sphilip
555227569Sphilipfail5:
556227569Sphilip	EFSYS_PROBE(fail5);
557227569Sphilipfail4:
558227569Sphilip	EFSYS_PROBE(fail4);
559227569Sphilipfail3:
560227569Sphilip	EFSYS_PROBE(fail3);
561227569Sphilipfail2:
562227569Sphilip	EFSYS_PROBE(fail2);
563227569Sphilipfail1:
564227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
565227569Sphilip
566227569Sphilip	return (rc);
567227569Sphilip}
568227569Sphilip
569227569Sphilipstatic	uint8_t	__cs	__efx_vpd_blank_pid[] = {
570227569Sphilip	/* Large resource type ID length 1 */
571227569Sphilip	0x82, 0x01, 0x00,
572227569Sphilip	/* Product name ' ' */
573227569Sphilip	0x32,
574227569Sphilip};
575227569Sphilip
576227569Sphilipstatic uint8_t __cs	__efx_vpd_blank_r[] = {
577227569Sphilip	/* Large resource type VPD-R length 4 */
578227569Sphilip	0x90, 0x04, 0x00,
579227569Sphilip	/* RV keyword length 1 */
580227569Sphilip	'R', 'V', 0x01,
581227569Sphilip	/* RV payload checksum */
582227569Sphilip	0x00,
583227569Sphilip};
584227569Sphilip
585227569Sphilip	__checkReturn		int
586227569Sphilipefx_vpd_hunk_reinit(
587227569Sphilip	__in			caddr_t data,
588227569Sphilip	__in			size_t size,
589227569Sphilip	__in			boolean_t wantpid)
590227569Sphilip{
591227569Sphilip	unsigned int offset = 0;
592227569Sphilip	unsigned int pos;
593227569Sphilip	efx_byte_t byte;
594227569Sphilip	uint8_t cksum;
595227569Sphilip	int rc;
596227569Sphilip
597227569Sphilip	if (size < 0x100) {
598227569Sphilip		rc = ENOSPC;
599227569Sphilip		goto fail1;
600227569Sphilip	}
601227569Sphilip
602227569Sphilip	if (wantpid) {
603227569Sphilip		memcpy(data + offset, __efx_vpd_blank_pid,
604227569Sphilip		    sizeof (__efx_vpd_blank_pid));
605227569Sphilip		offset += sizeof (__efx_vpd_blank_pid);
606227569Sphilip	}
607227569Sphilip
608227569Sphilip	memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
609227569Sphilip	offset += sizeof (__efx_vpd_blank_r);
610227569Sphilip
611227569Sphilip	/* Update checksum */
612227569Sphilip	cksum = 0;
613227569Sphilip	for (pos = 0; pos < offset; pos++)
614227569Sphilip		cksum += data[pos];
615227569Sphilip	data[offset - 1] -= cksum;
616227569Sphilip
617227569Sphilip	/* Append trailing tag */
618227569Sphilip	EFX_POPULATE_BYTE_3(byte,
619227569Sphilip			    TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
620227569Sphilip			    TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
621227569Sphilip			    TAG_SMALL_ITEM_SIZE, 0);
622227569Sphilip	data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
623227569Sphilip	offset++;
624227569Sphilip
625227569Sphilip	return (0);
626227569Sphilip
627227569Sphilipfail1:
628227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
629227569Sphilip
630227569Sphilip	return (rc);
631227569Sphilip}
632227569Sphilip
633227569Sphilip	__checkReturn			int
634227569Sphilipefx_vpd_hunk_next(
635227569Sphilip	__in_bcount(size)		caddr_t data,
636227569Sphilip	__in				size_t size,
637227569Sphilip	__out				efx_vpd_tag_t *tagp,
638227569Sphilip	__out				efx_vpd_keyword_t *keywordp,
639227569Sphilip	__out_bcount_opt(*paylenp)	unsigned int *payloadp,
640227569Sphilip	__out_opt			uint8_t *paylenp,
641227569Sphilip	__inout				unsigned int *contp)
642227569Sphilip{
643227569Sphilip	efx_vpd_tag_t tag;
644227569Sphilip	efx_vpd_keyword_t keyword = 0;
645227569Sphilip	unsigned int offset;
646227569Sphilip	unsigned int pos;
647227569Sphilip	unsigned int index;
648227569Sphilip	uint16_t taglen;
649227569Sphilip	uint8_t keylen;
650227569Sphilip	uint8_t paylen;
651227569Sphilip	int rc;
652227569Sphilip
653227569Sphilip	offset = index = 0;
654227569Sphilip	_NOTE(CONSTANTCONDITION)
655227569Sphilip	while (1) {
656227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
657227569Sphilip		    &tag, &taglen)) != 0)
658227569Sphilip			goto fail1;
659227569Sphilip		if (tag == EFX_VPD_END)
660227569Sphilip			break;
661227569Sphilip
662227569Sphilip		if (tag == EFX_VPD_ID) {
663227569Sphilip			if (index == *contp) {
664227569Sphilip				EFSYS_ASSERT3U(taglen, <, 0x100);
665227569Sphilip				paylen = (uint8_t)MIN(taglen, 0xff);
666227569Sphilip
667227569Sphilip				goto done;
668227569Sphilip			}
669227569Sphilip		} else {
670227569Sphilip			for (pos = 0; pos != taglen; pos += 3 + keylen) {
671227569Sphilip				if ((rc = efx_vpd_next_keyword(data + offset,
672227569Sphilip				    taglen, pos, &keyword, &keylen)) != 0)
673227569Sphilip					goto fail2;
674227569Sphilip
675227569Sphilip				if (index == *contp) {
676227569Sphilip					offset += pos + 3;
677227569Sphilip					paylen = keylen;
678227569Sphilip
679227569Sphilip					goto done;
680227569Sphilip				}
681227569Sphilip			}
682227569Sphilip		}
683227569Sphilip
684227569Sphilip		offset += taglen;
685227569Sphilip	}
686227569Sphilip
687227569Sphilip	*contp = 0;
688227569Sphilip	return (0);
689227569Sphilip
690227569Sphilipdone:
691227569Sphilip	*tagp = tag;
692227569Sphilip	*keywordp = keyword;
693227569Sphilip	if (payloadp != NULL)
694227569Sphilip		*payloadp = offset;
695227569Sphilip	if (paylenp != NULL)
696227569Sphilip		*paylenp = paylen;
697227569Sphilip
698227569Sphilip	++(*contp);
699227569Sphilip	return (0);
700227569Sphilip
701227569Sphilipfail2:
702227569Sphilip	EFSYS_PROBE(fail2);
703227569Sphilipfail1:
704227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
705227569Sphilip
706227569Sphilip	return (rc);
707227569Sphilip}
708227569Sphilip
709227569Sphilip	__checkReturn		int
710227569Sphilipefx_vpd_hunk_get(
711227569Sphilip	__in_bcount(size)	caddr_t data,
712227569Sphilip	__in			size_t size,
713227569Sphilip	__in			efx_vpd_tag_t tag,
714227569Sphilip	__in			efx_vpd_keyword_t keyword,
715227569Sphilip	__out			unsigned int *payloadp,
716227569Sphilip	__out			uint8_t *paylenp)
717227569Sphilip{
718227569Sphilip	efx_vpd_tag_t itag;
719227569Sphilip	efx_vpd_keyword_t ikeyword;
720227569Sphilip	unsigned int offset;
721227569Sphilip	unsigned int pos;
722227569Sphilip	uint16_t taglen;
723227569Sphilip	uint8_t keylen;
724227569Sphilip	int rc;
725227569Sphilip
726227569Sphilip	offset = 0;
727227569Sphilip	_NOTE(CONSTANTCONDITION)
728227569Sphilip	while (1) {
729227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
730227569Sphilip		    &itag, &taglen)) != 0)
731227569Sphilip			goto fail1;
732227569Sphilip		if (itag == EFX_VPD_END)
733227569Sphilip			break;
734227569Sphilip
735227569Sphilip		if (itag == tag) {
736227569Sphilip			if (itag == EFX_VPD_ID) {
737227569Sphilip				EFSYS_ASSERT3U(taglen, <, 0x100);
738227569Sphilip
739227569Sphilip				*paylenp = (uint8_t)MIN(taglen, 0xff);
740227569Sphilip				*payloadp = offset;
741227569Sphilip				return (0);
742227569Sphilip			}
743227569Sphilip
744227569Sphilip			for (pos = 0; pos != taglen; pos += 3 + keylen) {
745227569Sphilip				if ((rc = efx_vpd_next_keyword(data + offset,
746227569Sphilip				    taglen, pos, &ikeyword, &keylen)) != 0)
747227569Sphilip					goto fail2;
748227569Sphilip
749227569Sphilip				if (ikeyword == keyword) {
750227569Sphilip					*paylenp = keylen;
751227569Sphilip					*payloadp = offset + pos + 3;
752227569Sphilip					return (0);
753227569Sphilip				}
754227569Sphilip			}
755227569Sphilip		}
756227569Sphilip
757227569Sphilip		offset += taglen;
758227569Sphilip	}
759227569Sphilip
760227569Sphilip	/* Not an error */
761227569Sphilip	return (ENOENT);
762227569Sphilip
763227569Sphilipfail2:
764227569Sphilip	EFSYS_PROBE(fail2);
765227569Sphilipfail1:
766227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
767227569Sphilip
768227569Sphilip	return (rc);
769227569Sphilip}
770227569Sphilip
771227569Sphilip	__checkReturn		int
772227569Sphilipefx_vpd_hunk_set(
773227569Sphilip	__in_bcount(size)	caddr_t data,
774227569Sphilip	__in			size_t size,
775227569Sphilip	__in			efx_vpd_value_t *evvp)
776227569Sphilip{
777227569Sphilip	efx_word_t word;
778227569Sphilip	efx_vpd_tag_t tag;
779227569Sphilip	efx_vpd_keyword_t keyword;
780227569Sphilip	unsigned int offset;
781227569Sphilip	unsigned int pos;
782227569Sphilip	unsigned int taghead;
783227569Sphilip	unsigned int source;
784227569Sphilip	unsigned int dest;
785227569Sphilip	unsigned int i;
786227569Sphilip	uint16_t taglen;
787227569Sphilip	uint8_t keylen;
788227569Sphilip	uint8_t cksum;
789227569Sphilip	size_t used;
790227569Sphilip	int rc;
791227569Sphilip
792227569Sphilip	switch (evvp->evv_tag) {
793227569Sphilip	case EFX_VPD_ID:
794227569Sphilip		if (evvp->evv_keyword != 0) {
795227569Sphilip			rc = EINVAL;
796227569Sphilip			goto fail1;
797227569Sphilip		}
798227569Sphilip
799227569Sphilip		/* Can't delete the ID keyword */
800227569Sphilip		if (evvp->evv_length == 0) {
801227569Sphilip			rc = EINVAL;
802227569Sphilip			goto fail1;
803227569Sphilip		}
804227569Sphilip		break;
805227569Sphilip
806227569Sphilip	case EFX_VPD_RO:
807227569Sphilip		if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
808227569Sphilip			rc = EINVAL;
809227569Sphilip			goto fail1;
810227569Sphilip		}
811227569Sphilip		break;
812227569Sphilip
813227569Sphilip	default:
814227569Sphilip		rc = EINVAL;
815227569Sphilip		goto fail1;
816227569Sphilip	}
817227569Sphilip
818227569Sphilip	/* Determine total size of all current tags */
819227569Sphilip	if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
820227569Sphilip		goto fail2;
821227569Sphilip
822227569Sphilip	offset = 0;
823227569Sphilip	_NOTE(CONSTANTCONDITION)
824227569Sphilip	while (1) {
825227569Sphilip		taghead = offset;
826227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
827227569Sphilip		    &tag, &taglen)) != 0)
828227569Sphilip			goto fail3;
829227569Sphilip		if (tag == EFX_VPD_END)
830227569Sphilip			break;
831227569Sphilip		else if (tag != evvp->evv_tag) {
832227569Sphilip			offset += taglen;
833227569Sphilip			continue;
834227569Sphilip		}
835227569Sphilip
836227569Sphilip		/* We only support modifying large resource tags */
837227569Sphilip		if (offset - taghead != 3) {
838227569Sphilip			rc = EINVAL;
839227569Sphilip			goto fail4;
840227569Sphilip		}
841227569Sphilip
842227569Sphilip		/*
843227569Sphilip		 * Work out the offset of the byte immediately after the
844227569Sphilip		 * old (=source) and new (=dest) new keyword/tag
845227569Sphilip		 */
846227569Sphilip		pos = 0;
847227569Sphilip		if (tag == EFX_VPD_ID) {
848227569Sphilip			source = offset + taglen;
849227569Sphilip			dest = offset + evvp->evv_length;
850227569Sphilip			goto check_space;
851227569Sphilip		}
852227569Sphilip
853227569Sphilip		EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
854227569Sphilip		source = dest = 0;
855227569Sphilip		for (pos = 0; pos != taglen; pos += 3 + keylen) {
856227569Sphilip			if ((rc = efx_vpd_next_keyword(data + offset,
857227569Sphilip			    taglen, pos, &keyword, &keylen)) != 0)
858227569Sphilip				goto fail5;
859227569Sphilip
860227569Sphilip			if (keyword == evvp->evv_keyword &&
861227569Sphilip			    evvp->evv_length == 0) {
862227569Sphilip				/* Deleting this keyword */
863227569Sphilip				source = offset + pos + 3 + keylen;
864227569Sphilip				dest = offset + pos;
865227569Sphilip				break;
866227569Sphilip
867227569Sphilip			} else if (keyword == evvp->evv_keyword) {
868227569Sphilip				/* Adjusting this keyword */
869227569Sphilip				source = offset + pos + 3 + keylen;
870227569Sphilip				dest = offset + pos + 3 + evvp->evv_length;
871227569Sphilip				break;
872227569Sphilip
873227569Sphilip			} else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
874227569Sphilip				/* The RV keyword must be at the end */
875227569Sphilip				EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
876227569Sphilip
877227569Sphilip				/*
878227569Sphilip				 * The keyword doesn't already exist. If the
879227569Sphilip				 * user deleting a non-existant keyword then
880227569Sphilip				 * this is a no-op.
881227569Sphilip				 */
882227569Sphilip				if (evvp->evv_length == 0)
883227569Sphilip					return (0);
884227569Sphilip
885227569Sphilip				/* Insert this keyword before the RV keyword */
886227569Sphilip				source = offset + pos;
887227569Sphilip				dest = offset + pos + 3 + evvp->evv_length;
888227569Sphilip				break;
889227569Sphilip			}
890227569Sphilip		}
891227569Sphilip
892227569Sphilip	check_space:
893227569Sphilip		if (used + dest > size + source) {
894227569Sphilip			rc = ENOSPC;
895227569Sphilip			goto fail6;
896227569Sphilip		}
897227569Sphilip
898227569Sphilip		/* Move trailing data */
899227569Sphilip		(void) memmove(data + dest, data + source, used - source);
900227569Sphilip
901227569Sphilip		/* Copy contents */
902227569Sphilip		memcpy(data + dest - evvp->evv_length, evvp->evv_value,
903227569Sphilip		    evvp->evv_length);
904227569Sphilip
905227569Sphilip		/* Insert new keyword header if required */
906227569Sphilip		if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
907227569Sphilip			EFX_POPULATE_WORD_1(word, EFX_WORD_0,
908227569Sphilip					    evvp->evv_keyword);
909227569Sphilip			data[offset + pos + 0] =
910227569Sphilip			    EFX_WORD_FIELD(word, EFX_BYTE_0);
911227569Sphilip			data[offset + pos + 1] =
912227569Sphilip			    EFX_WORD_FIELD(word, EFX_BYTE_1);
913227569Sphilip			data[offset + pos + 2] = evvp->evv_length;
914227569Sphilip		}
915227569Sphilip
916227569Sphilip		/* Modify tag length (large resource type) */
917227569Sphilip		taglen += (dest - source);
918227569Sphilip		EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
919227569Sphilip		data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
920227569Sphilip		data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
921227569Sphilip
922227569Sphilip		goto checksum;
923227569Sphilip	}
924227569Sphilip
925227569Sphilip	/* Unable to find the matching tag */
926227569Sphilip	rc = ENOENT;
927227569Sphilip	goto fail7;
928227569Sphilip
929227569Sphilipchecksum:
930227569Sphilip	/* Find the RV tag, and update the checksum */
931227569Sphilip	offset = 0;
932227569Sphilip	_NOTE(CONSTANTCONDITION)
933227569Sphilip	while (1) {
934227569Sphilip		if ((rc = efx_vpd_next_tag(data, size, &offset,
935227569Sphilip		    &tag, &taglen)) != 0)
936227569Sphilip			goto fail8;
937227569Sphilip		if (tag == EFX_VPD_END)
938227569Sphilip			break;
939227569Sphilip		if (tag == EFX_VPD_RO) {
940227569Sphilip			for (pos = 0; pos != taglen; pos += 3 + keylen) {
941227569Sphilip				if ((rc = efx_vpd_next_keyword(data + offset,
942227569Sphilip				    taglen, pos, &keyword, &keylen)) != 0)
943227569Sphilip					goto fail9;
944227569Sphilip
945227569Sphilip				if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
946227569Sphilip					cksum = 0;
947227569Sphilip					for (i = 0; i < offset + pos + 3; i++)
948227569Sphilip						cksum += data[i];
949227569Sphilip					data[i] = -cksum;
950227569Sphilip					break;
951227569Sphilip				}
952227569Sphilip			}
953227569Sphilip		}
954227569Sphilip
955227569Sphilip		offset += taglen;
956227569Sphilip	}
957227569Sphilip
958227569Sphilip	/* Zero out the unused portion */
959227569Sphilip	(void) memset(data + offset + taglen, 0xff, size - offset - taglen);
960227569Sphilip
961227569Sphilip	return (0);
962227569Sphilip
963227569Sphilipfail9:
964227569Sphilip	EFSYS_PROBE(fail9);
965227569Sphilipfail8:
966227569Sphilip	EFSYS_PROBE(fail8);
967227569Sphilipfail7:
968227569Sphilip	EFSYS_PROBE(fail7);
969227569Sphilipfail6:
970227569Sphilip	EFSYS_PROBE(fail6);
971227569Sphilipfail5:
972227569Sphilip	EFSYS_PROBE(fail5);
973227569Sphilipfail4:
974227569Sphilip	EFSYS_PROBE(fail4);
975227569Sphilipfail3:
976227569Sphilip	EFSYS_PROBE(fail3);
977227569Sphilipfail2:
978227569Sphilip	EFSYS_PROBE(fail2);
979227569Sphilipfail1:
980227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
981227569Sphilip
982227569Sphilip	return (rc);
983227569Sphilip}
984227569Sphilip
985227569Sphilip				void
986227569Sphilipefx_vpd_fini(
987227569Sphilip	__in			efx_nic_t *enp)
988227569Sphilip{
989227569Sphilip	efx_vpd_ops_t *evpdop = enp->en_evpdop;
990227569Sphilip
991227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
992227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
993227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
994227569Sphilip
995227569Sphilip	if (evpdop->evpdo_fini != NULL)
996227569Sphilip		evpdop->evpdo_fini(enp);
997227569Sphilip
998227569Sphilip	enp->en_evpdop = NULL;
999227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_VPD;
1000227569Sphilip}
1001227569Sphilip
1002227569Sphilip#endif	/* EFSYS_OPT_VPD */
1003