xref: /freebsd/sys/dev/sfxge/common/efx_vpd.c (revision 77226f89411a4b45bebf4e66760976aae85cac2a)
1e948693eSPhilip Paeps /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4929c7febSAndrew Rybchenko  * Copyright (c) 2009-2016 Solarflare Communications Inc.
53c838a9fSAndrew Rybchenko  * All rights reserved.
6e948693eSPhilip Paeps  *
7e948693eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
83c838a9fSAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
9e948693eSPhilip Paeps  *
103c838a9fSAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
113c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer.
123c838a9fSAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
133c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
143c838a9fSAndrew Rybchenko  *    and/or other materials provided with the distribution.
153c838a9fSAndrew Rybchenko  *
163c838a9fSAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
173c838a9fSAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
183c838a9fSAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
193c838a9fSAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
203c838a9fSAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
213c838a9fSAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
223c838a9fSAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
233c838a9fSAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
243c838a9fSAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
253c838a9fSAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
263c838a9fSAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
273c838a9fSAndrew Rybchenko  *
283c838a9fSAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
293c838a9fSAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
303c838a9fSAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
31e948693eSPhilip Paeps  */
32e948693eSPhilip Paeps 
335dee87d7SPhilip Paeps #include <sys/cdefs.h>
345dee87d7SPhilip Paeps __FBSDID("$FreeBSD$");
355dee87d7SPhilip Paeps 
36e948693eSPhilip Paeps #include "efx.h"
37e948693eSPhilip Paeps #include "efx_impl.h"
38e948693eSPhilip Paeps 
39e948693eSPhilip Paeps #if EFSYS_OPT_VPD
40e948693eSPhilip Paeps 
41e948693eSPhilip Paeps #define	TAG_TYPE_LBN 7
42e948693eSPhilip Paeps #define	TAG_TYPE_WIDTH 1
43e948693eSPhilip Paeps #define	TAG_TYPE_LARGE_ITEM_DECODE 1
44e948693eSPhilip Paeps #define	TAG_TYPE_SMALL_ITEM_DECODE 0
45e948693eSPhilip Paeps 
46e948693eSPhilip Paeps #define	TAG_SMALL_ITEM_NAME_LBN 3
47e948693eSPhilip Paeps #define	TAG_SMALL_ITEM_NAME_WIDTH 4
48e948693eSPhilip Paeps #define	TAG_SMALL_ITEM_SIZE_LBN 0
49e948693eSPhilip Paeps #define	TAG_SMALL_ITEM_SIZE_WIDTH 3
50e948693eSPhilip Paeps 
51e948693eSPhilip Paeps #define	TAG_LARGE_ITEM_NAME_LBN 0
52e948693eSPhilip Paeps #define	TAG_LARGE_ITEM_NAME_WIDTH 7
53e948693eSPhilip Paeps 
54e948693eSPhilip Paeps #define	TAG_NAME_END_DECODE 0x0f
55e948693eSPhilip Paeps #define	TAG_NAME_ID_STRING_DECODE 0x02
56e948693eSPhilip Paeps #define	TAG_NAME_VPD_R_DECODE 0x10
57e948693eSPhilip Paeps #define	TAG_NAME_VPD_W_DECODE 0x11
58e948693eSPhilip Paeps 
59e948693eSPhilip Paeps #if EFSYS_OPT_SIENA
60e948693eSPhilip Paeps 
61ec831f7fSAndrew Rybchenko static const efx_vpd_ops_t	__efx_vpd_siena_ops = {
62e948693eSPhilip Paeps 	siena_vpd_init,		/* evpdo_init */
63e948693eSPhilip Paeps 	siena_vpd_size,		/* evpdo_size */
64e948693eSPhilip Paeps 	siena_vpd_read,		/* evpdo_read */
65e948693eSPhilip Paeps 	siena_vpd_verify,	/* evpdo_verify */
66e948693eSPhilip Paeps 	siena_vpd_reinit,	/* evpdo_reinit */
67e948693eSPhilip Paeps 	siena_vpd_get,		/* evpdo_get */
68e948693eSPhilip Paeps 	siena_vpd_set,		/* evpdo_set */
69e948693eSPhilip Paeps 	siena_vpd_next,		/* evpdo_next */
70e948693eSPhilip Paeps 	siena_vpd_write,	/* evpdo_write */
71e948693eSPhilip Paeps 	siena_vpd_fini,		/* evpdo_fini */
72e948693eSPhilip Paeps };
73e948693eSPhilip Paeps 
74e948693eSPhilip Paeps #endif	/* EFSYS_OPT_SIENA */
75e948693eSPhilip Paeps 
76*77226f89SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
773c838a9fSAndrew Rybchenko 
78ec831f7fSAndrew Rybchenko static const efx_vpd_ops_t	__efx_vpd_ef10_ops = {
795311abfaSAndrew Rybchenko 	ef10_vpd_init,		/* evpdo_init */
805311abfaSAndrew Rybchenko 	ef10_vpd_size,		/* evpdo_size */
815311abfaSAndrew Rybchenko 	ef10_vpd_read,		/* evpdo_read */
825311abfaSAndrew Rybchenko 	ef10_vpd_verify,	/* evpdo_verify */
835311abfaSAndrew Rybchenko 	ef10_vpd_reinit,	/* evpdo_reinit */
845311abfaSAndrew Rybchenko 	ef10_vpd_get,		/* evpdo_get */
855311abfaSAndrew Rybchenko 	ef10_vpd_set,		/* evpdo_set */
865311abfaSAndrew Rybchenko 	ef10_vpd_next,		/* evpdo_next */
875311abfaSAndrew Rybchenko 	ef10_vpd_write,		/* evpdo_write */
885311abfaSAndrew Rybchenko 	ef10_vpd_fini,		/* evpdo_fini */
893c838a9fSAndrew Rybchenko };
903c838a9fSAndrew Rybchenko 
91*77226f89SAndrew Rybchenko #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
923c838a9fSAndrew Rybchenko 
93460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
94e948693eSPhilip Paeps efx_vpd_init(
95e948693eSPhilip Paeps 	__in			efx_nic_t *enp)
96e948693eSPhilip Paeps {
97ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop;
98460cb568SAndrew Rybchenko 	efx_rc_t rc;
99e948693eSPhilip Paeps 
100e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
101e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
102e948693eSPhilip Paeps 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD));
103e948693eSPhilip Paeps 
104e948693eSPhilip Paeps 	switch (enp->en_family) {
105e948693eSPhilip Paeps #if EFSYS_OPT_SIENA
106e948693eSPhilip Paeps 	case EFX_FAMILY_SIENA:
107ec831f7fSAndrew Rybchenko 		evpdop = &__efx_vpd_siena_ops;
108e948693eSPhilip Paeps 		break;
109e948693eSPhilip Paeps #endif	/* EFSYS_OPT_SIENA */
110e948693eSPhilip Paeps 
1113c838a9fSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON
1123c838a9fSAndrew Rybchenko 	case EFX_FAMILY_HUNTINGTON:
113ec831f7fSAndrew Rybchenko 		evpdop = &__efx_vpd_ef10_ops;
1143c838a9fSAndrew Rybchenko 		break;
1153c838a9fSAndrew Rybchenko #endif	/* EFSYS_OPT_HUNTINGTON */
1163c838a9fSAndrew Rybchenko 
1175311abfaSAndrew Rybchenko #if EFSYS_OPT_MEDFORD
1185311abfaSAndrew Rybchenko 	case EFX_FAMILY_MEDFORD:
119ec831f7fSAndrew Rybchenko 		evpdop = &__efx_vpd_ef10_ops;
1205311abfaSAndrew Rybchenko 		break;
1215311abfaSAndrew Rybchenko #endif	/* EFSYS_OPT_MEDFORD */
1225311abfaSAndrew Rybchenko 
123*77226f89SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2
124*77226f89SAndrew Rybchenko 	case EFX_FAMILY_MEDFORD2:
125*77226f89SAndrew Rybchenko 		evpdop = &__efx_vpd_ef10_ops;
126*77226f89SAndrew Rybchenko 		break;
127*77226f89SAndrew Rybchenko #endif	/* EFSYS_OPT_MEDFORD2 */
128*77226f89SAndrew Rybchenko 
129e948693eSPhilip Paeps 	default:
130e948693eSPhilip Paeps 		EFSYS_ASSERT(0);
131e948693eSPhilip Paeps 		rc = ENOTSUP;
132e948693eSPhilip Paeps 		goto fail1;
133e948693eSPhilip Paeps 	}
134e948693eSPhilip Paeps 
135e948693eSPhilip Paeps 	if (evpdop->evpdo_init != NULL) {
136e948693eSPhilip Paeps 		if ((rc = evpdop->evpdo_init(enp)) != 0)
137e948693eSPhilip Paeps 			goto fail2;
138e948693eSPhilip Paeps 	}
139e948693eSPhilip Paeps 
140e948693eSPhilip Paeps 	enp->en_evpdop = evpdop;
141e948693eSPhilip Paeps 	enp->en_mod_flags |= EFX_MOD_VPD;
142e948693eSPhilip Paeps 
143e948693eSPhilip Paeps 	return (0);
144e948693eSPhilip Paeps 
145e948693eSPhilip Paeps fail2:
146e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
147e948693eSPhilip Paeps fail1:
148460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
149e948693eSPhilip Paeps 
150e948693eSPhilip Paeps 	return (rc);
151e948693eSPhilip Paeps }
152e948693eSPhilip Paeps 
153460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
154e948693eSPhilip Paeps efx_vpd_size(
155e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
156e948693eSPhilip Paeps 	__out			size_t *sizep)
157e948693eSPhilip Paeps {
158ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
159460cb568SAndrew Rybchenko 	efx_rc_t rc;
160e948693eSPhilip Paeps 
161e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
162e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
163e948693eSPhilip Paeps 
164e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_size(enp, sizep)) != 0)
165e948693eSPhilip Paeps 		goto fail1;
166e948693eSPhilip Paeps 
167e948693eSPhilip Paeps 	return (0);
168e948693eSPhilip Paeps 
169e948693eSPhilip Paeps fail1:
170460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
171e948693eSPhilip Paeps 
172e948693eSPhilip Paeps 	return (rc);
173e948693eSPhilip Paeps }
174e948693eSPhilip Paeps 
175460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
176e948693eSPhilip Paeps efx_vpd_read(
177e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
178e948693eSPhilip Paeps 	__out_bcount(size)	caddr_t data,
179e948693eSPhilip Paeps 	__in			size_t size)
180e948693eSPhilip Paeps {
181ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
182460cb568SAndrew Rybchenko 	efx_rc_t rc;
183e948693eSPhilip Paeps 
184e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
185e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
186e948693eSPhilip Paeps 
187e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_read(enp, data, size)) != 0)
188e948693eSPhilip Paeps 		goto fail1;
189e948693eSPhilip Paeps 
190e948693eSPhilip Paeps 	return (0);
191e948693eSPhilip Paeps 
192e948693eSPhilip Paeps fail1:
193460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
194e948693eSPhilip Paeps 
195e948693eSPhilip Paeps 	return (rc);
196e948693eSPhilip Paeps }
197e948693eSPhilip Paeps 
198460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
199e948693eSPhilip Paeps efx_vpd_verify(
200e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
201e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
202e948693eSPhilip Paeps 	__in			size_t size)
203e948693eSPhilip Paeps {
204ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
205460cb568SAndrew Rybchenko 	efx_rc_t rc;
206e948693eSPhilip Paeps 
207e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
208e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
209e948693eSPhilip Paeps 
210e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0)
211e948693eSPhilip Paeps 		goto fail1;
212e948693eSPhilip Paeps 
213e948693eSPhilip Paeps 	return (0);
214e948693eSPhilip Paeps 
215e948693eSPhilip Paeps fail1:
216460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
217e948693eSPhilip Paeps 
218e948693eSPhilip Paeps 	return (rc);
219e948693eSPhilip Paeps }
220e948693eSPhilip Paeps 
221460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
222e948693eSPhilip Paeps efx_vpd_reinit(
223e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
224e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
225e948693eSPhilip Paeps 	__in			size_t size)
226e948693eSPhilip Paeps {
227ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
228460cb568SAndrew Rybchenko 	efx_rc_t rc;
229e948693eSPhilip Paeps 
230e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
231e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
232e948693eSPhilip Paeps 
233e948693eSPhilip Paeps 	if (evpdop->evpdo_reinit == NULL) {
234e948693eSPhilip Paeps 		rc = ENOTSUP;
235e948693eSPhilip Paeps 		goto fail1;
236e948693eSPhilip Paeps 	}
237e948693eSPhilip Paeps 
238e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0)
239e948693eSPhilip Paeps 		goto fail2;
240e948693eSPhilip Paeps 
241e948693eSPhilip Paeps 	return (0);
242e948693eSPhilip Paeps 
243e948693eSPhilip Paeps fail2:
244e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
245e948693eSPhilip Paeps fail1:
246460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
247e948693eSPhilip Paeps 
248e948693eSPhilip Paeps 	return (rc);
249e948693eSPhilip Paeps }
250e948693eSPhilip Paeps 
251460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
252e948693eSPhilip Paeps efx_vpd_get(
253e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
254e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
255e948693eSPhilip Paeps 	__in			size_t size,
256e948693eSPhilip Paeps 	__inout			efx_vpd_value_t *evvp)
257e948693eSPhilip Paeps {
258ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
259460cb568SAndrew Rybchenko 	efx_rc_t rc;
260e948693eSPhilip Paeps 
261e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
262e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
263e948693eSPhilip Paeps 
26401215be2SAndrew Rybchenko 	if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) {
26501215be2SAndrew Rybchenko 		if (rc == ENOENT)
26601215be2SAndrew Rybchenko 			return (rc);
26701215be2SAndrew Rybchenko 
268e948693eSPhilip Paeps 		goto fail1;
26901215be2SAndrew Rybchenko 	}
270e948693eSPhilip Paeps 
271e948693eSPhilip Paeps 	return (0);
272e948693eSPhilip Paeps 
273e948693eSPhilip Paeps fail1:
274460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
275e948693eSPhilip Paeps 
276e948693eSPhilip Paeps 	return (rc);
277e948693eSPhilip Paeps }
278e948693eSPhilip Paeps 
279460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
280e948693eSPhilip Paeps efx_vpd_set(
281e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
282e948693eSPhilip Paeps 	__inout_bcount(size)	caddr_t data,
283e948693eSPhilip Paeps 	__in			size_t size,
284e948693eSPhilip Paeps 	__in			efx_vpd_value_t *evvp)
285e948693eSPhilip Paeps {
286ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
287460cb568SAndrew Rybchenko 	efx_rc_t rc;
288e948693eSPhilip Paeps 
289e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
290e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
291e948693eSPhilip Paeps 
292e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0)
293e948693eSPhilip Paeps 		goto fail1;
294e948693eSPhilip Paeps 
295e948693eSPhilip Paeps 	return (0);
296e948693eSPhilip Paeps 
297e948693eSPhilip Paeps fail1:
298460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
299e948693eSPhilip Paeps 
300e948693eSPhilip Paeps 	return (rc);
301e948693eSPhilip Paeps }
302e948693eSPhilip Paeps 
303460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
304e948693eSPhilip Paeps efx_vpd_next(
305e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
306e948693eSPhilip Paeps 	__inout_bcount(size)	caddr_t data,
307e948693eSPhilip Paeps 	__in			size_t size,
308e948693eSPhilip Paeps 	__out			efx_vpd_value_t *evvp,
309e948693eSPhilip Paeps 	__inout			unsigned int *contp)
310e948693eSPhilip Paeps {
311ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
312460cb568SAndrew Rybchenko 	efx_rc_t rc;
313e948693eSPhilip Paeps 
314e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
315e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
316e948693eSPhilip Paeps 
317e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0)
318e948693eSPhilip Paeps 		goto fail1;
319e948693eSPhilip Paeps 
320e948693eSPhilip Paeps 	return (0);
321e948693eSPhilip Paeps 
322e948693eSPhilip Paeps fail1:
323460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
324e948693eSPhilip Paeps 
325e948693eSPhilip Paeps 	return (rc);
326e948693eSPhilip Paeps }
327e948693eSPhilip Paeps 
328460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
329e948693eSPhilip Paeps efx_vpd_write(
330e948693eSPhilip Paeps 	__in			efx_nic_t *enp,
331e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
332e948693eSPhilip Paeps 	__in			size_t size)
333e948693eSPhilip Paeps {
334ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
335460cb568SAndrew Rybchenko 	efx_rc_t rc;
336e948693eSPhilip Paeps 
337e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
338e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
339e948693eSPhilip Paeps 
340e948693eSPhilip Paeps 	if ((rc = evpdop->evpdo_write(enp, data, size)) != 0)
341e948693eSPhilip Paeps 		goto fail1;
342e948693eSPhilip Paeps 
343e948693eSPhilip Paeps 	return (0);
344e948693eSPhilip Paeps 
345e948693eSPhilip Paeps fail1:
346460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
347e948693eSPhilip Paeps 
348e948693eSPhilip Paeps 	return (rc);
349e948693eSPhilip Paeps }
350e948693eSPhilip Paeps 
351460cb568SAndrew Rybchenko static	__checkReturn		efx_rc_t
352e948693eSPhilip Paeps efx_vpd_next_tag(
353e948693eSPhilip Paeps 	__in			caddr_t data,
354e948693eSPhilip Paeps 	__in			size_t size,
355e948693eSPhilip Paeps 	__inout			unsigned int *offsetp,
356e948693eSPhilip Paeps 	__out			efx_vpd_tag_t *tagp,
357e948693eSPhilip Paeps 	__out			uint16_t *lengthp)
358e948693eSPhilip Paeps {
359e948693eSPhilip Paeps 	efx_byte_t byte;
360e948693eSPhilip Paeps 	efx_word_t word;
361e948693eSPhilip Paeps 	uint8_t name;
362e948693eSPhilip Paeps 	uint16_t length;
363e948693eSPhilip Paeps 	size_t headlen;
364460cb568SAndrew Rybchenko 	efx_rc_t rc;
365e948693eSPhilip Paeps 
366e948693eSPhilip Paeps 	if (*offsetp >= size) {
367e948693eSPhilip Paeps 		rc = EFAULT;
368e948693eSPhilip Paeps 		goto fail1;
369e948693eSPhilip Paeps 	}
370e948693eSPhilip Paeps 
371e948693eSPhilip Paeps 	EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]);
372e948693eSPhilip Paeps 
373e948693eSPhilip Paeps 	switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) {
374e948693eSPhilip Paeps 	case TAG_TYPE_SMALL_ITEM_DECODE:
375e948693eSPhilip Paeps 		headlen = 1;
376e948693eSPhilip Paeps 
377e948693eSPhilip Paeps 		name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME);
378e948693eSPhilip Paeps 		length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE);
379e948693eSPhilip Paeps 
380e948693eSPhilip Paeps 		break;
381e948693eSPhilip Paeps 
382e948693eSPhilip Paeps 	case TAG_TYPE_LARGE_ITEM_DECODE:
383e948693eSPhilip Paeps 		headlen = 3;
384e948693eSPhilip Paeps 
385e948693eSPhilip Paeps 		if (*offsetp + headlen > size) {
386e948693eSPhilip Paeps 			rc = EFAULT;
387e948693eSPhilip Paeps 			goto fail2;
388e948693eSPhilip Paeps 		}
389e948693eSPhilip Paeps 
390e948693eSPhilip Paeps 		name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME);
391e948693eSPhilip Paeps 		EFX_POPULATE_WORD_2(word,
392e948693eSPhilip Paeps 				    EFX_BYTE_0, data[*offsetp + 1],
393e948693eSPhilip Paeps 				    EFX_BYTE_1, data[*offsetp + 2]);
394e948693eSPhilip Paeps 		length = EFX_WORD_FIELD(word, EFX_WORD_0);
395e948693eSPhilip Paeps 
396e948693eSPhilip Paeps 		break;
397e948693eSPhilip Paeps 
398e948693eSPhilip Paeps 	default:
399e948693eSPhilip Paeps 		rc = EFAULT;
400e948693eSPhilip Paeps 		goto fail2;
401e948693eSPhilip Paeps 	}
402e948693eSPhilip Paeps 
403e948693eSPhilip Paeps 	if (*offsetp + headlen + length > size) {
404e948693eSPhilip Paeps 		rc = EFAULT;
405e948693eSPhilip Paeps 		goto fail3;
406e948693eSPhilip Paeps 	}
407e948693eSPhilip Paeps 
408e948693eSPhilip Paeps 	EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END);
409e948693eSPhilip Paeps 	EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID);
410e948693eSPhilip Paeps 	EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO);
411e948693eSPhilip Paeps 	EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW);
412e948693eSPhilip Paeps 	if (name != EFX_VPD_END && name != EFX_VPD_ID &&
413e948693eSPhilip Paeps 	    name != EFX_VPD_RO) {
414e948693eSPhilip Paeps 		rc = EFAULT;
415e948693eSPhilip Paeps 		goto fail4;
416e948693eSPhilip Paeps 	}
417e948693eSPhilip Paeps 
418e948693eSPhilip Paeps 	*tagp = name;
419e948693eSPhilip Paeps 	*lengthp = length;
420e948693eSPhilip Paeps 	*offsetp += headlen;
421e948693eSPhilip Paeps 
422e948693eSPhilip Paeps 	return (0);
423e948693eSPhilip Paeps 
424e948693eSPhilip Paeps fail4:
425e948693eSPhilip Paeps 	EFSYS_PROBE(fail4);
426e948693eSPhilip Paeps fail3:
427e948693eSPhilip Paeps 	EFSYS_PROBE(fail3);
428e948693eSPhilip Paeps fail2:
429e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
430e948693eSPhilip Paeps fail1:
431460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
432e948693eSPhilip Paeps 
433e948693eSPhilip Paeps 	return (rc);
434e948693eSPhilip Paeps }
435e948693eSPhilip Paeps 
436460cb568SAndrew Rybchenko static	__checkReturn		efx_rc_t
437e948693eSPhilip Paeps efx_vpd_next_keyword(
438e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t tag,
439e948693eSPhilip Paeps 	__in			size_t size,
440e948693eSPhilip Paeps 	__in			unsigned int pos,
441e948693eSPhilip Paeps 	__out			efx_vpd_keyword_t *keywordp,
442e948693eSPhilip Paeps 	__out			uint8_t *lengthp)
443e948693eSPhilip Paeps {
444e948693eSPhilip Paeps 	efx_vpd_keyword_t keyword;
445e948693eSPhilip Paeps 	uint8_t length;
446460cb568SAndrew Rybchenko 	efx_rc_t rc;
447e948693eSPhilip Paeps 
448e948693eSPhilip Paeps 	if (pos + 3U > size) {
449e948693eSPhilip Paeps 		rc = EFAULT;
450e948693eSPhilip Paeps 		goto fail1;
451e948693eSPhilip Paeps 	}
452e948693eSPhilip Paeps 
453e948693eSPhilip Paeps 	keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]);
454e948693eSPhilip Paeps 	length = tag[pos + 2];
455e948693eSPhilip Paeps 
456e948693eSPhilip Paeps 	if (length == 0 || pos + 3U + length > size) {
457e948693eSPhilip Paeps 		rc = EFAULT;
458e948693eSPhilip Paeps 		goto fail2;
459e948693eSPhilip Paeps 	}
460e948693eSPhilip Paeps 
461e948693eSPhilip Paeps 	*keywordp = keyword;
462e948693eSPhilip Paeps 	*lengthp = length;
463e948693eSPhilip Paeps 
464e948693eSPhilip Paeps 	return (0);
465e948693eSPhilip Paeps 
466e948693eSPhilip Paeps fail2:
467e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
468e948693eSPhilip Paeps fail1:
469460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
470e948693eSPhilip Paeps 
471e948693eSPhilip Paeps 	return (rc);
472e948693eSPhilip Paeps }
473e948693eSPhilip Paeps 
474460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
475e948693eSPhilip Paeps efx_vpd_hunk_length(
476e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
477e948693eSPhilip Paeps 	__in			size_t size,
478e948693eSPhilip Paeps 	__out			size_t *lengthp)
479e948693eSPhilip Paeps {
480e948693eSPhilip Paeps 	efx_vpd_tag_t tag;
481e948693eSPhilip Paeps 	unsigned int offset;
482e948693eSPhilip Paeps 	uint16_t taglen;
483460cb568SAndrew Rybchenko 	efx_rc_t rc;
484e948693eSPhilip Paeps 
485e948693eSPhilip Paeps 	offset = 0;
486e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
487e948693eSPhilip Paeps 	while (1) {
488e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
489e948693eSPhilip Paeps 		    &tag, &taglen)) != 0)
490e948693eSPhilip Paeps 			goto fail1;
491e948693eSPhilip Paeps 		offset += taglen;
492e948693eSPhilip Paeps 		if (tag == EFX_VPD_END)
493e948693eSPhilip Paeps 			break;
494e948693eSPhilip Paeps 	}
495e948693eSPhilip Paeps 
496e948693eSPhilip Paeps 	*lengthp = offset;
497e948693eSPhilip Paeps 
498e948693eSPhilip Paeps 	return (0);
499e948693eSPhilip Paeps 
500e948693eSPhilip Paeps fail1:
501460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
502e948693eSPhilip Paeps 
503e948693eSPhilip Paeps 	return (rc);
504e948693eSPhilip Paeps }
505e948693eSPhilip Paeps 
506460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
507e948693eSPhilip Paeps efx_vpd_hunk_verify(
508e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
509e948693eSPhilip Paeps 	__in			size_t size,
510e948693eSPhilip Paeps 	__out_opt		boolean_t *cksummedp)
511e948693eSPhilip Paeps {
512e948693eSPhilip Paeps 	efx_vpd_tag_t tag;
513e948693eSPhilip Paeps 	efx_vpd_keyword_t keyword;
514e948693eSPhilip Paeps 	unsigned int offset;
515e948693eSPhilip Paeps 	unsigned int pos;
516e948693eSPhilip Paeps 	unsigned int i;
517e948693eSPhilip Paeps 	uint16_t taglen;
518e948693eSPhilip Paeps 	uint8_t keylen;
519e948693eSPhilip Paeps 	uint8_t cksum;
520e948693eSPhilip Paeps 	boolean_t cksummed = B_FALSE;
521460cb568SAndrew Rybchenko 	efx_rc_t rc;
522e948693eSPhilip Paeps 
523e948693eSPhilip Paeps 	/*
524e948693eSPhilip Paeps 	 * Parse every tag,keyword in the existing VPD. If the csum is present,
525e948693eSPhilip Paeps 	 * the assert it is correct, and is the final keyword in the RO block.
526e948693eSPhilip Paeps 	 */
527e948693eSPhilip Paeps 	offset = 0;
528e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
529e948693eSPhilip Paeps 	while (1) {
530e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
531e948693eSPhilip Paeps 		    &tag, &taglen)) != 0)
532e948693eSPhilip Paeps 			goto fail1;
533e948693eSPhilip Paeps 		if (tag == EFX_VPD_END)
534e948693eSPhilip Paeps 			break;
535e948693eSPhilip Paeps 		else if (tag == EFX_VPD_ID)
536e948693eSPhilip Paeps 			goto done;
537e948693eSPhilip Paeps 
538e948693eSPhilip Paeps 		for (pos = 0; pos != taglen; pos += 3 + keylen) {
539e948693eSPhilip Paeps 			/* RV keyword must be the last in the block */
5409dbc22c5SAndrew Rybchenko 			if (cksummed) {
5419dbc22c5SAndrew Rybchenko 				rc = EFAULT;
542e948693eSPhilip Paeps 				goto fail2;
5439dbc22c5SAndrew Rybchenko 			}
544e948693eSPhilip Paeps 
545e948693eSPhilip Paeps 			if ((rc = efx_vpd_next_keyword(data + offset,
546e948693eSPhilip Paeps 			    taglen, pos, &keyword, &keylen)) != 0)
547e948693eSPhilip Paeps 				goto fail3;
548e948693eSPhilip Paeps 
549e948693eSPhilip Paeps 			if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
550e948693eSPhilip Paeps 				cksum = 0;
551e948693eSPhilip Paeps 				for (i = 0; i < offset + pos + 4; i++)
552e948693eSPhilip Paeps 					cksum += data[i];
553e948693eSPhilip Paeps 
554e948693eSPhilip Paeps 				if (cksum != 0) {
555e948693eSPhilip Paeps 					rc = EFAULT;
556e948693eSPhilip Paeps 					goto fail4;
557e948693eSPhilip Paeps 				}
558e948693eSPhilip Paeps 
559e948693eSPhilip Paeps 				cksummed = B_TRUE;
560e948693eSPhilip Paeps 			}
561e948693eSPhilip Paeps 		}
562e948693eSPhilip Paeps 
563e948693eSPhilip Paeps 	done:
564e948693eSPhilip Paeps 		offset += taglen;
565e948693eSPhilip Paeps 	}
566e948693eSPhilip Paeps 
567e948693eSPhilip Paeps 	if (!cksummed) {
568e948693eSPhilip Paeps 		rc = EFAULT;
569e948693eSPhilip Paeps 		goto fail5;
570e948693eSPhilip Paeps 	}
571e948693eSPhilip Paeps 
572e948693eSPhilip Paeps 	if (cksummedp != NULL)
573e948693eSPhilip Paeps 		*cksummedp = cksummed;
574e948693eSPhilip Paeps 
575e948693eSPhilip Paeps 	return (0);
576e948693eSPhilip Paeps 
577e948693eSPhilip Paeps fail5:
578e948693eSPhilip Paeps 	EFSYS_PROBE(fail5);
579e948693eSPhilip Paeps fail4:
580e948693eSPhilip Paeps 	EFSYS_PROBE(fail4);
581e948693eSPhilip Paeps fail3:
582e948693eSPhilip Paeps 	EFSYS_PROBE(fail3);
583e948693eSPhilip Paeps fail2:
584e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
585e948693eSPhilip Paeps fail1:
586460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
587e948693eSPhilip Paeps 
588e948693eSPhilip Paeps 	return (rc);
589e948693eSPhilip Paeps }
590e948693eSPhilip Paeps 
5913c838a9fSAndrew Rybchenko static	uint8_t	__efx_vpd_blank_pid[] = {
592e948693eSPhilip Paeps 	/* Large resource type ID length 1 */
593e948693eSPhilip Paeps 	0x82, 0x01, 0x00,
594e948693eSPhilip Paeps 	/* Product name ' ' */
595e948693eSPhilip Paeps 	0x32,
596e948693eSPhilip Paeps };
597e948693eSPhilip Paeps 
5983c838a9fSAndrew Rybchenko static uint8_t __efx_vpd_blank_r[] = {
599e948693eSPhilip Paeps 	/* Large resource type VPD-R length 4 */
600e948693eSPhilip Paeps 	0x90, 0x04, 0x00,
601e948693eSPhilip Paeps 	/* RV keyword length 1 */
602e948693eSPhilip Paeps 	'R', 'V', 0x01,
603e948693eSPhilip Paeps 	/* RV payload checksum */
604e948693eSPhilip Paeps 	0x00,
605e948693eSPhilip Paeps };
606e948693eSPhilip Paeps 
607460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
608e948693eSPhilip Paeps efx_vpd_hunk_reinit(
6093c838a9fSAndrew Rybchenko 	__in_bcount(size)	caddr_t data,
610e948693eSPhilip Paeps 	__in			size_t size,
611e948693eSPhilip Paeps 	__in			boolean_t wantpid)
612e948693eSPhilip Paeps {
613e948693eSPhilip Paeps 	unsigned int offset = 0;
614e948693eSPhilip Paeps 	unsigned int pos;
615e948693eSPhilip Paeps 	efx_byte_t byte;
616e948693eSPhilip Paeps 	uint8_t cksum;
617460cb568SAndrew Rybchenko 	efx_rc_t rc;
618e948693eSPhilip Paeps 
619e948693eSPhilip Paeps 	if (size < 0x100) {
620e948693eSPhilip Paeps 		rc = ENOSPC;
621e948693eSPhilip Paeps 		goto fail1;
622e948693eSPhilip Paeps 	}
623e948693eSPhilip Paeps 
624e948693eSPhilip Paeps 	if (wantpid) {
625e948693eSPhilip Paeps 		memcpy(data + offset, __efx_vpd_blank_pid,
626e948693eSPhilip Paeps 		    sizeof (__efx_vpd_blank_pid));
627e948693eSPhilip Paeps 		offset += sizeof (__efx_vpd_blank_pid);
628e948693eSPhilip Paeps 	}
629e948693eSPhilip Paeps 
630e948693eSPhilip Paeps 	memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
631e948693eSPhilip Paeps 	offset += sizeof (__efx_vpd_blank_r);
632e948693eSPhilip Paeps 
633e948693eSPhilip Paeps 	/* Update checksum */
634e948693eSPhilip Paeps 	cksum = 0;
635e948693eSPhilip Paeps 	for (pos = 0; pos < offset; pos++)
636e948693eSPhilip Paeps 		cksum += data[pos];
637e948693eSPhilip Paeps 	data[offset - 1] -= cksum;
638e948693eSPhilip Paeps 
639e948693eSPhilip Paeps 	/* Append trailing tag */
640e948693eSPhilip Paeps 	EFX_POPULATE_BYTE_3(byte,
641e948693eSPhilip Paeps 			    TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
642e948693eSPhilip Paeps 			    TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
643e948693eSPhilip Paeps 			    TAG_SMALL_ITEM_SIZE, 0);
644e948693eSPhilip Paeps 	data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
645e948693eSPhilip Paeps 	offset++;
646e948693eSPhilip Paeps 
647e948693eSPhilip Paeps 	return (0);
648e948693eSPhilip Paeps 
649e948693eSPhilip Paeps fail1:
650460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
651e948693eSPhilip Paeps 
652e948693eSPhilip Paeps 	return (rc);
653e948693eSPhilip Paeps }
654e948693eSPhilip Paeps 
655460cb568SAndrew Rybchenko 	__checkReturn			efx_rc_t
656e948693eSPhilip Paeps efx_vpd_hunk_next(
657e948693eSPhilip Paeps 	__in_bcount(size)		caddr_t data,
658e948693eSPhilip Paeps 	__in				size_t size,
659e948693eSPhilip Paeps 	__out				efx_vpd_tag_t *tagp,
660e948693eSPhilip Paeps 	__out				efx_vpd_keyword_t *keywordp,
66172cda832SAndrew Rybchenko 	__out_opt			unsigned int *payloadp,
662e948693eSPhilip Paeps 	__out_opt			uint8_t *paylenp,
663e948693eSPhilip Paeps 	__inout				unsigned int *contp)
664e948693eSPhilip Paeps {
665e948693eSPhilip Paeps 	efx_vpd_tag_t tag;
666e948693eSPhilip Paeps 	efx_vpd_keyword_t keyword = 0;
667e948693eSPhilip Paeps 	unsigned int offset;
668e948693eSPhilip Paeps 	unsigned int pos;
669e948693eSPhilip Paeps 	unsigned int index;
670e948693eSPhilip Paeps 	uint16_t taglen;
671e948693eSPhilip Paeps 	uint8_t keylen;
672e948693eSPhilip Paeps 	uint8_t paylen;
673460cb568SAndrew Rybchenko 	efx_rc_t rc;
674e948693eSPhilip Paeps 
675e948693eSPhilip Paeps 	offset = index = 0;
676e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
677e948693eSPhilip Paeps 	while (1) {
678e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
679e948693eSPhilip Paeps 		    &tag, &taglen)) != 0)
680e948693eSPhilip Paeps 			goto fail1;
68172cda832SAndrew Rybchenko 
68272cda832SAndrew Rybchenko 		if (tag == EFX_VPD_END) {
68372cda832SAndrew Rybchenko 			keyword = 0;
68472cda832SAndrew Rybchenko 			paylen = 0;
68572cda832SAndrew Rybchenko 			index = 0;
686e948693eSPhilip Paeps 			break;
68772cda832SAndrew Rybchenko 		}
688e948693eSPhilip Paeps 
689e948693eSPhilip Paeps 		if (tag == EFX_VPD_ID) {
69072cda832SAndrew Rybchenko 			if (index++ == *contp) {
691e948693eSPhilip Paeps 				EFSYS_ASSERT3U(taglen, <, 0x100);
69272cda832SAndrew Rybchenko 				keyword = 0;
693e948693eSPhilip Paeps 				paylen = (uint8_t)MIN(taglen, 0xff);
694e948693eSPhilip Paeps 
695e948693eSPhilip Paeps 				goto done;
696e948693eSPhilip Paeps 			}
697e948693eSPhilip Paeps 		} else {
698e948693eSPhilip Paeps 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
699e948693eSPhilip Paeps 				if ((rc = efx_vpd_next_keyword(data + offset,
700e948693eSPhilip Paeps 				    taglen, pos, &keyword, &keylen)) != 0)
701e948693eSPhilip Paeps 					goto fail2;
702e948693eSPhilip Paeps 
70372cda832SAndrew Rybchenko 				if (index++ == *contp) {
704e948693eSPhilip Paeps 					offset += pos + 3;
705e948693eSPhilip Paeps 					paylen = keylen;
706e948693eSPhilip Paeps 
707e948693eSPhilip Paeps 					goto done;
708e948693eSPhilip Paeps 				}
709e948693eSPhilip Paeps 			}
710e948693eSPhilip Paeps 		}
711e948693eSPhilip Paeps 
712e948693eSPhilip Paeps 		offset += taglen;
713e948693eSPhilip Paeps 	}
714e948693eSPhilip Paeps 
715e948693eSPhilip Paeps done:
716e948693eSPhilip Paeps 	*tagp = tag;
717e948693eSPhilip Paeps 	*keywordp = keyword;
718e948693eSPhilip Paeps 	if (payloadp != NULL)
719e948693eSPhilip Paeps 		*payloadp = offset;
720e948693eSPhilip Paeps 	if (paylenp != NULL)
721e948693eSPhilip Paeps 		*paylenp = paylen;
722e948693eSPhilip Paeps 
72372cda832SAndrew Rybchenko 	*contp = index;
724e948693eSPhilip Paeps 	return (0);
725e948693eSPhilip Paeps 
726e948693eSPhilip Paeps fail2:
727e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
728e948693eSPhilip Paeps fail1:
729460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
730e948693eSPhilip Paeps 
731e948693eSPhilip Paeps 	return (rc);
732e948693eSPhilip Paeps }
733e948693eSPhilip Paeps 
734460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
735e948693eSPhilip Paeps efx_vpd_hunk_get(
736e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
737e948693eSPhilip Paeps 	__in			size_t size,
738e948693eSPhilip Paeps 	__in			efx_vpd_tag_t tag,
739e948693eSPhilip Paeps 	__in			efx_vpd_keyword_t keyword,
740e948693eSPhilip Paeps 	__out			unsigned int *payloadp,
741e948693eSPhilip Paeps 	__out			uint8_t *paylenp)
742e948693eSPhilip Paeps {
743e948693eSPhilip Paeps 	efx_vpd_tag_t itag;
744e948693eSPhilip Paeps 	efx_vpd_keyword_t ikeyword;
745e948693eSPhilip Paeps 	unsigned int offset;
746e948693eSPhilip Paeps 	unsigned int pos;
747e948693eSPhilip Paeps 	uint16_t taglen;
748e948693eSPhilip Paeps 	uint8_t keylen;
749460cb568SAndrew Rybchenko 	efx_rc_t rc;
750e948693eSPhilip Paeps 
751e948693eSPhilip Paeps 	offset = 0;
752e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
753e948693eSPhilip Paeps 	while (1) {
754e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
755e948693eSPhilip Paeps 		    &itag, &taglen)) != 0)
756e948693eSPhilip Paeps 			goto fail1;
757e948693eSPhilip Paeps 		if (itag == EFX_VPD_END)
758e948693eSPhilip Paeps 			break;
759e948693eSPhilip Paeps 
760e948693eSPhilip Paeps 		if (itag == tag) {
761e948693eSPhilip Paeps 			if (itag == EFX_VPD_ID) {
762e948693eSPhilip Paeps 				EFSYS_ASSERT3U(taglen, <, 0x100);
763e948693eSPhilip Paeps 
764e948693eSPhilip Paeps 				*paylenp = (uint8_t)MIN(taglen, 0xff);
765e948693eSPhilip Paeps 				*payloadp = offset;
766e948693eSPhilip Paeps 				return (0);
767e948693eSPhilip Paeps 			}
768e948693eSPhilip Paeps 
769e948693eSPhilip Paeps 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
770e948693eSPhilip Paeps 				if ((rc = efx_vpd_next_keyword(data + offset,
771e948693eSPhilip Paeps 				    taglen, pos, &ikeyword, &keylen)) != 0)
772e948693eSPhilip Paeps 					goto fail2;
773e948693eSPhilip Paeps 
774e948693eSPhilip Paeps 				if (ikeyword == keyword) {
775e948693eSPhilip Paeps 					*paylenp = keylen;
776e948693eSPhilip Paeps 					*payloadp = offset + pos + 3;
777e948693eSPhilip Paeps 					return (0);
778e948693eSPhilip Paeps 				}
779e948693eSPhilip Paeps 			}
780e948693eSPhilip Paeps 		}
781e948693eSPhilip Paeps 
782e948693eSPhilip Paeps 		offset += taglen;
783e948693eSPhilip Paeps 	}
784e948693eSPhilip Paeps 
785e948693eSPhilip Paeps 	/* Not an error */
786e948693eSPhilip Paeps 	return (ENOENT);
787e948693eSPhilip Paeps 
788e948693eSPhilip Paeps fail2:
789e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
790e948693eSPhilip Paeps fail1:
791460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
792e948693eSPhilip Paeps 
793e948693eSPhilip Paeps 	return (rc);
794e948693eSPhilip Paeps }
795e948693eSPhilip Paeps 
796460cb568SAndrew Rybchenko 	__checkReturn		efx_rc_t
797e948693eSPhilip Paeps efx_vpd_hunk_set(
798e948693eSPhilip Paeps 	__in_bcount(size)	caddr_t data,
799e948693eSPhilip Paeps 	__in			size_t size,
800e948693eSPhilip Paeps 	__in			efx_vpd_value_t *evvp)
801e948693eSPhilip Paeps {
802e948693eSPhilip Paeps 	efx_word_t word;
803e948693eSPhilip Paeps 	efx_vpd_tag_t tag;
804e948693eSPhilip Paeps 	efx_vpd_keyword_t keyword;
805e948693eSPhilip Paeps 	unsigned int offset;
806e948693eSPhilip Paeps 	unsigned int pos;
807e948693eSPhilip Paeps 	unsigned int taghead;
808e948693eSPhilip Paeps 	unsigned int source;
809e948693eSPhilip Paeps 	unsigned int dest;
810e948693eSPhilip Paeps 	unsigned int i;
811e948693eSPhilip Paeps 	uint16_t taglen;
812e948693eSPhilip Paeps 	uint8_t keylen;
813e948693eSPhilip Paeps 	uint8_t cksum;
814e948693eSPhilip Paeps 	size_t used;
815460cb568SAndrew Rybchenko 	efx_rc_t rc;
816e948693eSPhilip Paeps 
817e948693eSPhilip Paeps 	switch (evvp->evv_tag) {
818e948693eSPhilip Paeps 	case EFX_VPD_ID:
819e948693eSPhilip Paeps 		if (evvp->evv_keyword != 0) {
820e948693eSPhilip Paeps 			rc = EINVAL;
821e948693eSPhilip Paeps 			goto fail1;
822e948693eSPhilip Paeps 		}
823e948693eSPhilip Paeps 
824e948693eSPhilip Paeps 		/* Can't delete the ID keyword */
825e948693eSPhilip Paeps 		if (evvp->evv_length == 0) {
826e948693eSPhilip Paeps 			rc = EINVAL;
827e948693eSPhilip Paeps 			goto fail1;
828e948693eSPhilip Paeps 		}
829e948693eSPhilip Paeps 		break;
830e948693eSPhilip Paeps 
831e948693eSPhilip Paeps 	case EFX_VPD_RO:
832e948693eSPhilip Paeps 		if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
833e948693eSPhilip Paeps 			rc = EINVAL;
834e948693eSPhilip Paeps 			goto fail1;
835e948693eSPhilip Paeps 		}
836e948693eSPhilip Paeps 		break;
837e948693eSPhilip Paeps 
838e948693eSPhilip Paeps 	default:
839e948693eSPhilip Paeps 		rc = EINVAL;
840e948693eSPhilip Paeps 		goto fail1;
841e948693eSPhilip Paeps 	}
842e948693eSPhilip Paeps 
843e948693eSPhilip Paeps 	/* Determine total size of all current tags */
844e948693eSPhilip Paeps 	if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
845e948693eSPhilip Paeps 		goto fail2;
846e948693eSPhilip Paeps 
847e948693eSPhilip Paeps 	offset = 0;
848e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
849e948693eSPhilip Paeps 	while (1) {
850e948693eSPhilip Paeps 		taghead = offset;
851e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
852e948693eSPhilip Paeps 		    &tag, &taglen)) != 0)
853e948693eSPhilip Paeps 			goto fail3;
854e948693eSPhilip Paeps 		if (tag == EFX_VPD_END)
855e948693eSPhilip Paeps 			break;
856e948693eSPhilip Paeps 		else if (tag != evvp->evv_tag) {
857e948693eSPhilip Paeps 			offset += taglen;
858e948693eSPhilip Paeps 			continue;
859e948693eSPhilip Paeps 		}
860e948693eSPhilip Paeps 
861e948693eSPhilip Paeps 		/* We only support modifying large resource tags */
862e948693eSPhilip Paeps 		if (offset - taghead != 3) {
863e948693eSPhilip Paeps 			rc = EINVAL;
864e948693eSPhilip Paeps 			goto fail4;
865e948693eSPhilip Paeps 		}
866e948693eSPhilip Paeps 
867e948693eSPhilip Paeps 		/*
868e948693eSPhilip Paeps 		 * Work out the offset of the byte immediately after the
869e948693eSPhilip Paeps 		 * old (=source) and new (=dest) new keyword/tag
870e948693eSPhilip Paeps 		 */
871e948693eSPhilip Paeps 		pos = 0;
872e948693eSPhilip Paeps 		if (tag == EFX_VPD_ID) {
873e948693eSPhilip Paeps 			source = offset + taglen;
874e948693eSPhilip Paeps 			dest = offset + evvp->evv_length;
875e948693eSPhilip Paeps 			goto check_space;
876e948693eSPhilip Paeps 		}
877e948693eSPhilip Paeps 
878e948693eSPhilip Paeps 		EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
879e948693eSPhilip Paeps 		source = dest = 0;
880e948693eSPhilip Paeps 		for (pos = 0; pos != taglen; pos += 3 + keylen) {
881e948693eSPhilip Paeps 			if ((rc = efx_vpd_next_keyword(data + offset,
882e948693eSPhilip Paeps 			    taglen, pos, &keyword, &keylen)) != 0)
883e948693eSPhilip Paeps 				goto fail5;
884e948693eSPhilip Paeps 
885e948693eSPhilip Paeps 			if (keyword == evvp->evv_keyword &&
886e948693eSPhilip Paeps 			    evvp->evv_length == 0) {
887e948693eSPhilip Paeps 				/* Deleting this keyword */
888e948693eSPhilip Paeps 				source = offset + pos + 3 + keylen;
889e948693eSPhilip Paeps 				dest = offset + pos;
890e948693eSPhilip Paeps 				break;
891e948693eSPhilip Paeps 
892e948693eSPhilip Paeps 			} else if (keyword == evvp->evv_keyword) {
893e948693eSPhilip Paeps 				/* Adjusting this keyword */
894e948693eSPhilip Paeps 				source = offset + pos + 3 + keylen;
895e948693eSPhilip Paeps 				dest = offset + pos + 3 + evvp->evv_length;
896e948693eSPhilip Paeps 				break;
897e948693eSPhilip Paeps 
898e948693eSPhilip Paeps 			} else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
899e948693eSPhilip Paeps 				/* The RV keyword must be at the end */
900e948693eSPhilip Paeps 				EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
901e948693eSPhilip Paeps 
902e948693eSPhilip Paeps 				/*
903e948693eSPhilip Paeps 				 * The keyword doesn't already exist. If the
904e948693eSPhilip Paeps 				 * user deleting a non-existant keyword then
905e948693eSPhilip Paeps 				 * this is a no-op.
906e948693eSPhilip Paeps 				 */
907e948693eSPhilip Paeps 				if (evvp->evv_length == 0)
908e948693eSPhilip Paeps 					return (0);
909e948693eSPhilip Paeps 
910e948693eSPhilip Paeps 				/* Insert this keyword before the RV keyword */
911e948693eSPhilip Paeps 				source = offset + pos;
912e948693eSPhilip Paeps 				dest = offset + pos + 3 + evvp->evv_length;
913e948693eSPhilip Paeps 				break;
914e948693eSPhilip Paeps 			}
915e948693eSPhilip Paeps 		}
916e948693eSPhilip Paeps 
917e948693eSPhilip Paeps 	check_space:
918e948693eSPhilip Paeps 		if (used + dest > size + source) {
919e948693eSPhilip Paeps 			rc = ENOSPC;
920e948693eSPhilip Paeps 			goto fail6;
921e948693eSPhilip Paeps 		}
922e948693eSPhilip Paeps 
923e948693eSPhilip Paeps 		/* Move trailing data */
924e948693eSPhilip Paeps 		(void) memmove(data + dest, data + source, used - source);
925e948693eSPhilip Paeps 
926e948693eSPhilip Paeps 		/* Copy contents */
927e948693eSPhilip Paeps 		memcpy(data + dest - evvp->evv_length, evvp->evv_value,
928e948693eSPhilip Paeps 		    evvp->evv_length);
929e948693eSPhilip Paeps 
930e948693eSPhilip Paeps 		/* Insert new keyword header if required */
931e948693eSPhilip Paeps 		if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
932e948693eSPhilip Paeps 			EFX_POPULATE_WORD_1(word, EFX_WORD_0,
933e948693eSPhilip Paeps 					    evvp->evv_keyword);
934e948693eSPhilip Paeps 			data[offset + pos + 0] =
935e948693eSPhilip Paeps 			    EFX_WORD_FIELD(word, EFX_BYTE_0);
936e948693eSPhilip Paeps 			data[offset + pos + 1] =
937e948693eSPhilip Paeps 			    EFX_WORD_FIELD(word, EFX_BYTE_1);
938e948693eSPhilip Paeps 			data[offset + pos + 2] = evvp->evv_length;
939e948693eSPhilip Paeps 		}
940e948693eSPhilip Paeps 
941e948693eSPhilip Paeps 		/* Modify tag length (large resource type) */
9425ef1830cSAndrew Rybchenko 		taglen += (uint16_t)(dest - source);
943e948693eSPhilip Paeps 		EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
944e948693eSPhilip Paeps 		data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
945e948693eSPhilip Paeps 		data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
946e948693eSPhilip Paeps 
947e948693eSPhilip Paeps 		goto checksum;
948e948693eSPhilip Paeps 	}
949e948693eSPhilip Paeps 
950e948693eSPhilip Paeps 	/* Unable to find the matching tag */
951e948693eSPhilip Paeps 	rc = ENOENT;
952e948693eSPhilip Paeps 	goto fail7;
953e948693eSPhilip Paeps 
954e948693eSPhilip Paeps checksum:
955e948693eSPhilip Paeps 	/* Find the RV tag, and update the checksum */
956e948693eSPhilip Paeps 	offset = 0;
957e948693eSPhilip Paeps 	_NOTE(CONSTANTCONDITION)
958e948693eSPhilip Paeps 	while (1) {
959e948693eSPhilip Paeps 		if ((rc = efx_vpd_next_tag(data, size, &offset,
960e948693eSPhilip Paeps 		    &tag, &taglen)) != 0)
961e948693eSPhilip Paeps 			goto fail8;
962e948693eSPhilip Paeps 		if (tag == EFX_VPD_END)
963e948693eSPhilip Paeps 			break;
964e948693eSPhilip Paeps 		if (tag == EFX_VPD_RO) {
965e948693eSPhilip Paeps 			for (pos = 0; pos != taglen; pos += 3 + keylen) {
966e948693eSPhilip Paeps 				if ((rc = efx_vpd_next_keyword(data + offset,
967e948693eSPhilip Paeps 				    taglen, pos, &keyword, &keylen)) != 0)
968e948693eSPhilip Paeps 					goto fail9;
969e948693eSPhilip Paeps 
970e948693eSPhilip Paeps 				if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
971e948693eSPhilip Paeps 					cksum = 0;
972e948693eSPhilip Paeps 					for (i = 0; i < offset + pos + 3; i++)
973e948693eSPhilip Paeps 						cksum += data[i];
974e948693eSPhilip Paeps 					data[i] = -cksum;
975e948693eSPhilip Paeps 					break;
976e948693eSPhilip Paeps 				}
977e948693eSPhilip Paeps 			}
978e948693eSPhilip Paeps 		}
979e948693eSPhilip Paeps 
980e948693eSPhilip Paeps 		offset += taglen;
981e948693eSPhilip Paeps 	}
982e948693eSPhilip Paeps 
983e948693eSPhilip Paeps 	/* Zero out the unused portion */
984e948693eSPhilip Paeps 	(void) memset(data + offset + taglen, 0xff, size - offset - taglen);
985e948693eSPhilip Paeps 
986e948693eSPhilip Paeps 	return (0);
987e948693eSPhilip Paeps 
988e948693eSPhilip Paeps fail9:
989e948693eSPhilip Paeps 	EFSYS_PROBE(fail9);
990e948693eSPhilip Paeps fail8:
991e948693eSPhilip Paeps 	EFSYS_PROBE(fail8);
992e948693eSPhilip Paeps fail7:
993e948693eSPhilip Paeps 	EFSYS_PROBE(fail7);
994e948693eSPhilip Paeps fail6:
995e948693eSPhilip Paeps 	EFSYS_PROBE(fail6);
996e948693eSPhilip Paeps fail5:
997e948693eSPhilip Paeps 	EFSYS_PROBE(fail5);
998e948693eSPhilip Paeps fail4:
999e948693eSPhilip Paeps 	EFSYS_PROBE(fail4);
1000e948693eSPhilip Paeps fail3:
1001e948693eSPhilip Paeps 	EFSYS_PROBE(fail3);
1002e948693eSPhilip Paeps fail2:
1003e948693eSPhilip Paeps 	EFSYS_PROBE(fail2);
1004e948693eSPhilip Paeps fail1:
1005460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1006e948693eSPhilip Paeps 
1007e948693eSPhilip Paeps 	return (rc);
1008e948693eSPhilip Paeps }
1009e948693eSPhilip Paeps 
1010e948693eSPhilip Paeps 				void
1011e948693eSPhilip Paeps efx_vpd_fini(
1012e948693eSPhilip Paeps 	__in			efx_nic_t *enp)
1013e948693eSPhilip Paeps {
1014ec831f7fSAndrew Rybchenko 	const efx_vpd_ops_t *evpdop = enp->en_evpdop;
1015e948693eSPhilip Paeps 
1016e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1017e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1018e948693eSPhilip Paeps 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
1019e948693eSPhilip Paeps 
1020e948693eSPhilip Paeps 	if (evpdop->evpdo_fini != NULL)
1021e948693eSPhilip Paeps 		evpdop->evpdo_fini(enp);
1022e948693eSPhilip Paeps 
1023e948693eSPhilip Paeps 	enp->en_evpdop = NULL;
1024e948693eSPhilip Paeps 	enp->en_mod_flags &= ~EFX_MOD_VPD;
1025e948693eSPhilip Paeps }
1026e948693eSPhilip Paeps 
1027e948693eSPhilip Paeps #endif	/* EFSYS_OPT_VPD */
1028