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