]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/sfxge/common/efx_vpd.c
MFC r340765
[FreeBSD/stable/10.git] / sys / dev / sfxge / common / efx_vpd.c
1 /*-
2  * Copyright (c) 2009-2016 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                 if (rc == ENOENT)
258                         return (rc);
259
260                 goto fail1;
261         }
262
263         return (0);
264
265 fail1:
266         EFSYS_PROBE1(fail1, efx_rc_t, rc);
267
268         return (rc);
269 }
270
271         __checkReturn           efx_rc_t
272 efx_vpd_set(
273         __in                    efx_nic_t *enp,
274         __inout_bcount(size)    caddr_t data,
275         __in                    size_t size,
276         __in                    efx_vpd_value_t *evvp)
277 {
278         const efx_vpd_ops_t *evpdop = enp->en_evpdop;
279         efx_rc_t rc;
280
281         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
282         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
283
284         if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0)
285                 goto fail1;
286
287         return (0);
288
289 fail1:
290         EFSYS_PROBE1(fail1, efx_rc_t, rc);
291
292         return (rc);
293 }
294
295         __checkReturn           efx_rc_t
296 efx_vpd_next(
297         __in                    efx_nic_t *enp,
298         __inout_bcount(size)    caddr_t data,
299         __in                    size_t size,
300         __out                   efx_vpd_value_t *evvp,
301         __inout                 unsigned int *contp)
302 {
303         const efx_vpd_ops_t *evpdop = enp->en_evpdop;
304         efx_rc_t rc;
305
306         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
307         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
308
309         if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0)
310                 goto fail1;
311
312         return (0);
313
314 fail1:
315         EFSYS_PROBE1(fail1, efx_rc_t, rc);
316
317         return (rc);
318 }
319
320         __checkReturn           efx_rc_t
321 efx_vpd_write(
322         __in                    efx_nic_t *enp,
323         __in_bcount(size)       caddr_t data,
324         __in                    size_t size)
325 {
326         const efx_vpd_ops_t *evpdop = enp->en_evpdop;
327         efx_rc_t rc;
328
329         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
330         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
331
332         if ((rc = evpdop->evpdo_write(enp, data, size)) != 0)
333                 goto fail1;
334
335         return (0);
336
337 fail1:
338         EFSYS_PROBE1(fail1, efx_rc_t, rc);
339
340         return (rc);
341 }
342
343 static  __checkReturn           efx_rc_t
344 efx_vpd_next_tag(
345         __in                    caddr_t data,
346         __in                    size_t size,
347         __inout                 unsigned int *offsetp,
348         __out                   efx_vpd_tag_t *tagp,
349         __out                   uint16_t *lengthp)
350 {
351         efx_byte_t byte;
352         efx_word_t word;
353         uint8_t name;
354         uint16_t length;
355         size_t headlen;
356         efx_rc_t rc;
357
358         if (*offsetp >= size) {
359                 rc = EFAULT;
360                 goto fail1;
361         }
362
363         EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]);
364
365         switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) {
366         case TAG_TYPE_SMALL_ITEM_DECODE:
367                 headlen = 1;
368
369                 name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME);
370                 length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE);
371
372                 break;
373
374         case TAG_TYPE_LARGE_ITEM_DECODE:
375                 headlen = 3;
376
377                 if (*offsetp + headlen > size) {
378                         rc = EFAULT;
379                         goto fail2;
380                 }
381
382                 name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME);
383                 EFX_POPULATE_WORD_2(word,
384                                     EFX_BYTE_0, data[*offsetp + 1],
385                                     EFX_BYTE_1, data[*offsetp + 2]);
386                 length = EFX_WORD_FIELD(word, EFX_WORD_0);
387
388                 break;
389
390         default:
391                 rc = EFAULT;
392                 goto fail2;
393         }
394
395         if (*offsetp + headlen + length > size) {
396                 rc = EFAULT;
397                 goto fail3;
398         }
399
400         EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END);
401         EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID);
402         EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO);
403         EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW);
404         if (name != EFX_VPD_END && name != EFX_VPD_ID &&
405             name != EFX_VPD_RO) {
406                 rc = EFAULT;
407                 goto fail4;
408         }
409
410         *tagp = name;
411         *lengthp = length;
412         *offsetp += headlen;
413
414         return (0);
415
416 fail4:
417         EFSYS_PROBE(fail4);
418 fail3:
419         EFSYS_PROBE(fail3);
420 fail2:
421         EFSYS_PROBE(fail2);
422 fail1:
423         EFSYS_PROBE1(fail1, efx_rc_t, rc);
424
425         return (rc);
426 }
427
428 static  __checkReturn           efx_rc_t
429 efx_vpd_next_keyword(
430         __in_bcount(size)       caddr_t tag,
431         __in                    size_t size,
432         __in                    unsigned int pos,
433         __out                   efx_vpd_keyword_t *keywordp,
434         __out                   uint8_t *lengthp)
435 {
436         efx_vpd_keyword_t keyword;
437         uint8_t length;
438         efx_rc_t rc;
439
440         if (pos + 3U > size) {
441                 rc = EFAULT;
442                 goto fail1;
443         }
444
445         keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]);
446         length = tag[pos + 2];
447
448         if (length == 0 || pos + 3U + length > size) {
449                 rc = EFAULT;
450                 goto fail2;
451         }
452
453         *keywordp = keyword;
454         *lengthp = length;
455
456         return (0);
457
458 fail2:
459         EFSYS_PROBE(fail2);
460 fail1:
461         EFSYS_PROBE1(fail1, efx_rc_t, rc);
462
463         return (rc);
464 }
465
466         __checkReturn           efx_rc_t
467 efx_vpd_hunk_length(
468         __in_bcount(size)       caddr_t data,
469         __in                    size_t size,
470         __out                   size_t *lengthp)
471 {
472         efx_vpd_tag_t tag;
473         unsigned int offset;
474         uint16_t taglen;
475         efx_rc_t rc;
476
477         offset = 0;
478         _NOTE(CONSTANTCONDITION)
479         while (1) {
480                 if ((rc = efx_vpd_next_tag(data, size, &offset,
481                     &tag, &taglen)) != 0)
482                         goto fail1;
483                 offset += taglen;
484                 if (tag == EFX_VPD_END)
485                         break;
486         }
487
488         *lengthp = offset;
489
490         return (0);
491
492 fail1:
493         EFSYS_PROBE1(fail1, efx_rc_t, rc);
494
495         return (rc);
496 }
497
498         __checkReturn           efx_rc_t
499 efx_vpd_hunk_verify(
500         __in_bcount(size)       caddr_t data,
501         __in                    size_t size,
502         __out_opt               boolean_t *cksummedp)
503 {
504         efx_vpd_tag_t tag;
505         efx_vpd_keyword_t keyword;
506         unsigned int offset;
507         unsigned int pos;
508         unsigned int i;
509         uint16_t taglen;
510         uint8_t keylen;
511         uint8_t cksum;
512         boolean_t cksummed = B_FALSE;
513         efx_rc_t rc;
514
515         /*
516          * Parse every tag,keyword in the existing VPD. If the csum is present,
517          * the assert it is correct, and is the final keyword in the RO block.
518          */
519         offset = 0;
520         _NOTE(CONSTANTCONDITION)
521         while (1) {
522                 if ((rc = efx_vpd_next_tag(data, size, &offset,
523                     &tag, &taglen)) != 0)
524                         goto fail1;
525                 if (tag == EFX_VPD_END)
526                         break;
527                 else if (tag == EFX_VPD_ID)
528                         goto done;
529
530                 for (pos = 0; pos != taglen; pos += 3 + keylen) {
531                         /* RV keyword must be the last in the block */
532                         if (cksummed) {
533                                 rc = EFAULT;
534                                 goto fail2;
535                         }
536
537                         if ((rc = efx_vpd_next_keyword(data + offset,
538                             taglen, pos, &keyword, &keylen)) != 0)
539                                 goto fail3;
540
541                         if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
542                                 cksum = 0;
543                                 for (i = 0; i < offset + pos + 4; i++)
544                                         cksum += data[i];
545
546                                 if (cksum != 0) {
547                                         rc = EFAULT;
548                                         goto fail4;
549                                 }
550
551                                 cksummed = B_TRUE;
552                         }
553                 }
554
555         done:
556                 offset += taglen;
557         }
558
559         if (!cksummed) {
560                 rc = EFAULT;
561                 goto fail5;
562         }
563
564         if (cksummedp != NULL)
565                 *cksummedp = cksummed;
566
567         return (0);
568
569 fail5:
570         EFSYS_PROBE(fail5);
571 fail4:
572         EFSYS_PROBE(fail4);
573 fail3:
574         EFSYS_PROBE(fail3);
575 fail2:
576         EFSYS_PROBE(fail2);
577 fail1:
578         EFSYS_PROBE1(fail1, efx_rc_t, rc);
579
580         return (rc);
581 }
582
583 static  uint8_t __efx_vpd_blank_pid[] = {
584         /* Large resource type ID length 1 */
585         0x82, 0x01, 0x00,
586         /* Product name ' ' */
587         0x32,
588 };
589
590 static uint8_t __efx_vpd_blank_r[] = {
591         /* Large resource type VPD-R length 4 */
592         0x90, 0x04, 0x00,
593         /* RV keyword length 1 */
594         'R', 'V', 0x01,
595         /* RV payload checksum */
596         0x00,
597 };
598
599         __checkReturn           efx_rc_t
600 efx_vpd_hunk_reinit(
601         __in_bcount(size)       caddr_t data,
602         __in                    size_t size,
603         __in                    boolean_t wantpid)
604 {
605         unsigned int offset = 0;
606         unsigned int pos;
607         efx_byte_t byte;
608         uint8_t cksum;
609         efx_rc_t rc;
610
611         if (size < 0x100) {
612                 rc = ENOSPC;
613                 goto fail1;
614         }
615
616         if (wantpid) {
617                 memcpy(data + offset, __efx_vpd_blank_pid,
618                     sizeof (__efx_vpd_blank_pid));
619                 offset += sizeof (__efx_vpd_blank_pid);
620         }
621
622         memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
623         offset += sizeof (__efx_vpd_blank_r);
624
625         /* Update checksum */
626         cksum = 0;
627         for (pos = 0; pos < offset; pos++)
628                 cksum += data[pos];
629         data[offset - 1] -= cksum;
630
631         /* Append trailing tag */
632         EFX_POPULATE_BYTE_3(byte,
633                             TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
634                             TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
635                             TAG_SMALL_ITEM_SIZE, 0);
636         data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
637         offset++;
638
639         return (0);
640
641 fail1:
642         EFSYS_PROBE1(fail1, efx_rc_t, rc);
643
644         return (rc);
645 }
646
647         __checkReturn                   efx_rc_t
648 efx_vpd_hunk_next(
649         __in_bcount(size)               caddr_t data,
650         __in                            size_t size,
651         __out                           efx_vpd_tag_t *tagp,
652         __out                           efx_vpd_keyword_t *keywordp,
653         __out_opt                       unsigned int *payloadp,
654         __out_opt                       uint8_t *paylenp,
655         __inout                         unsigned int *contp)
656 {
657         efx_vpd_tag_t tag;
658         efx_vpd_keyword_t keyword = 0;
659         unsigned int offset;
660         unsigned int pos;
661         unsigned int index;
662         uint16_t taglen;
663         uint8_t keylen;
664         uint8_t paylen;
665         efx_rc_t rc;
666
667         offset = index = 0;
668         _NOTE(CONSTANTCONDITION)
669         while (1) {
670                 if ((rc = efx_vpd_next_tag(data, size, &offset,
671                     &tag, &taglen)) != 0)
672                         goto fail1;
673
674                 if (tag == EFX_VPD_END) {
675                         keyword = 0;
676                         paylen = 0;
677                         index = 0;
678                         break;
679                 }
680
681                 if (tag == EFX_VPD_ID) {
682                         if (index++ == *contp) {
683                                 EFSYS_ASSERT3U(taglen, <, 0x100);
684                                 keyword = 0;
685                                 paylen = (uint8_t)MIN(taglen, 0xff);
686
687                                 goto done;
688                         }
689                 } else {
690                         for (pos = 0; pos != taglen; pos += 3 + keylen) {
691                                 if ((rc = efx_vpd_next_keyword(data + offset,
692                                     taglen, pos, &keyword, &keylen)) != 0)
693                                         goto fail2;
694
695                                 if (index++ == *contp) {
696                                         offset += pos + 3;
697                                         paylen = keylen;
698
699                                         goto done;
700                                 }
701                         }
702                 }
703
704                 offset += taglen;
705         }
706
707 done:
708         *tagp = tag;
709         *keywordp = keyword;
710         if (payloadp != NULL)
711                 *payloadp = offset;
712         if (paylenp != NULL)
713                 *paylenp = paylen;
714
715         *contp = index;
716         return (0);
717
718 fail2:
719         EFSYS_PROBE(fail2);
720 fail1:
721         EFSYS_PROBE1(fail1, efx_rc_t, rc);
722
723         return (rc);
724 }
725
726         __checkReturn           efx_rc_t
727 efx_vpd_hunk_get(
728         __in_bcount(size)       caddr_t data,
729         __in                    size_t size,
730         __in                    efx_vpd_tag_t tag,
731         __in                    efx_vpd_keyword_t keyword,
732         __out                   unsigned int *payloadp,
733         __out                   uint8_t *paylenp)
734 {
735         efx_vpd_tag_t itag;
736         efx_vpd_keyword_t ikeyword;
737         unsigned int offset;
738         unsigned int pos;
739         uint16_t taglen;
740         uint8_t keylen;
741         efx_rc_t rc;
742
743         offset = 0;
744         _NOTE(CONSTANTCONDITION)
745         while (1) {
746                 if ((rc = efx_vpd_next_tag(data, size, &offset,
747                     &itag, &taglen)) != 0)
748                         goto fail1;
749                 if (itag == EFX_VPD_END)
750                         break;
751
752                 if (itag == tag) {
753                         if (itag == EFX_VPD_ID) {
754                                 EFSYS_ASSERT3U(taglen, <, 0x100);
755
756                                 *paylenp = (uint8_t)MIN(taglen, 0xff);
757                                 *payloadp = offset;
758                                 return (0);
759                         }
760
761                         for (pos = 0; pos != taglen; pos += 3 + keylen) {
762                                 if ((rc = efx_vpd_next_keyword(data + offset,
763                                     taglen, pos, &ikeyword, &keylen)) != 0)
764                                         goto fail2;
765
766                                 if (ikeyword == keyword) {
767                                         *paylenp = keylen;
768                                         *payloadp = offset + pos + 3;
769                                         return (0);
770                                 }
771                         }
772                 }
773
774                 offset += taglen;
775         }
776
777         /* Not an error */
778         return (ENOENT);
779
780 fail2:
781         EFSYS_PROBE(fail2);
782 fail1:
783         EFSYS_PROBE1(fail1, efx_rc_t, rc);
784
785         return (rc);
786 }
787
788         __checkReturn           efx_rc_t
789 efx_vpd_hunk_set(
790         __in_bcount(size)       caddr_t data,
791         __in                    size_t size,
792         __in                    efx_vpd_value_t *evvp)
793 {
794         efx_word_t word;
795         efx_vpd_tag_t tag;
796         efx_vpd_keyword_t keyword;
797         unsigned int offset;
798         unsigned int pos;
799         unsigned int taghead;
800         unsigned int source;
801         unsigned int dest;
802         unsigned int i;
803         uint16_t taglen;
804         uint8_t keylen;
805         uint8_t cksum;
806         size_t used;
807         efx_rc_t rc;
808
809         switch (evvp->evv_tag) {
810         case EFX_VPD_ID:
811                 if (evvp->evv_keyword != 0) {
812                         rc = EINVAL;
813                         goto fail1;
814                 }
815
816                 /* Can't delete the ID keyword */
817                 if (evvp->evv_length == 0) {
818                         rc = EINVAL;
819                         goto fail1;
820                 }
821                 break;
822
823         case EFX_VPD_RO:
824                 if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
825                         rc = EINVAL;
826                         goto fail1;
827                 }
828                 break;
829
830         default:
831                 rc = EINVAL;
832                 goto fail1;
833         }
834
835         /* Determine total size of all current tags */
836         if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
837                 goto fail2;
838
839         offset = 0;
840         _NOTE(CONSTANTCONDITION)
841         while (1) {
842                 taghead = offset;
843                 if ((rc = efx_vpd_next_tag(data, size, &offset,
844                     &tag, &taglen)) != 0)
845                         goto fail3;
846                 if (tag == EFX_VPD_END)
847                         break;
848                 else if (tag != evvp->evv_tag) {
849                         offset += taglen;
850                         continue;
851                 }
852
853                 /* We only support modifying large resource tags */
854                 if (offset - taghead != 3) {
855                         rc = EINVAL;
856                         goto fail4;
857                 }
858
859                 /*
860                  * Work out the offset of the byte immediately after the
861                  * old (=source) and new (=dest) new keyword/tag
862                  */
863                 pos = 0;
864                 if (tag == EFX_VPD_ID) {
865                         source = offset + taglen;
866                         dest = offset + evvp->evv_length;
867                         goto check_space;
868                 }
869
870                 EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
871                 source = dest = 0;
872                 for (pos = 0; pos != taglen; pos += 3 + keylen) {
873                         if ((rc = efx_vpd_next_keyword(data + offset,
874                             taglen, pos, &keyword, &keylen)) != 0)
875                                 goto fail5;
876
877                         if (keyword == evvp->evv_keyword &&
878                             evvp->evv_length == 0) {
879                                 /* Deleting this keyword */
880                                 source = offset + pos + 3 + keylen;
881                                 dest = offset + pos;
882                                 break;
883
884                         } else if (keyword == evvp->evv_keyword) {
885                                 /* Adjusting this keyword */
886                                 source = offset + pos + 3 + keylen;
887                                 dest = offset + pos + 3 + evvp->evv_length;
888                                 break;
889
890                         } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
891                                 /* The RV keyword must be at the end */
892                                 EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
893
894                                 /*
895                                  * The keyword doesn't already exist. If the
896                                  * user deleting a non-existant keyword then
897                                  * this is a no-op.
898                                  */
899                                 if (evvp->evv_length == 0)
900                                         return (0);
901
902                                 /* Insert this keyword before the RV keyword */
903                                 source = offset + pos;
904                                 dest = offset + pos + 3 + evvp->evv_length;
905                                 break;
906                         }
907                 }
908
909         check_space:
910                 if (used + dest > size + source) {
911                         rc = ENOSPC;
912                         goto fail6;
913                 }
914
915                 /* Move trailing data */
916                 (void) memmove(data + dest, data + source, used - source);
917
918                 /* Copy contents */
919                 memcpy(data + dest - evvp->evv_length, evvp->evv_value,
920                     evvp->evv_length);
921
922                 /* Insert new keyword header if required */
923                 if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
924                         EFX_POPULATE_WORD_1(word, EFX_WORD_0,
925                                             evvp->evv_keyword);
926                         data[offset + pos + 0] =
927                             EFX_WORD_FIELD(word, EFX_BYTE_0);
928                         data[offset + pos + 1] =
929                             EFX_WORD_FIELD(word, EFX_BYTE_1);
930                         data[offset + pos + 2] = evvp->evv_length;
931                 }
932
933                 /* Modify tag length (large resource type) */
934                 taglen += (dest - source);
935                 EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
936                 data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
937                 data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
938
939                 goto checksum;
940         }
941
942         /* Unable to find the matching tag */
943         rc = ENOENT;
944         goto fail7;
945
946 checksum:
947         /* Find the RV tag, and update the checksum */
948         offset = 0;
949         _NOTE(CONSTANTCONDITION)
950         while (1) {
951                 if ((rc = efx_vpd_next_tag(data, size, &offset,
952                     &tag, &taglen)) != 0)
953                         goto fail8;
954                 if (tag == EFX_VPD_END)
955                         break;
956                 if (tag == EFX_VPD_RO) {
957                         for (pos = 0; pos != taglen; pos += 3 + keylen) {
958                                 if ((rc = efx_vpd_next_keyword(data + offset,
959                                     taglen, pos, &keyword, &keylen)) != 0)
960                                         goto fail9;
961
962                                 if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
963                                         cksum = 0;
964                                         for (i = 0; i < offset + pos + 3; i++)
965                                                 cksum += data[i];
966                                         data[i] = -cksum;
967                                         break;
968                                 }
969                         }
970                 }
971
972                 offset += taglen;
973         }
974
975         /* Zero out the unused portion */
976         (void) memset(data + offset + taglen, 0xff, size - offset - taglen);
977
978         return (0);
979
980 fail9:
981         EFSYS_PROBE(fail9);
982 fail8:
983         EFSYS_PROBE(fail8);
984 fail7:
985         EFSYS_PROBE(fail7);
986 fail6:
987         EFSYS_PROBE(fail6);
988 fail5:
989         EFSYS_PROBE(fail5);
990 fail4:
991         EFSYS_PROBE(fail4);
992 fail3:
993         EFSYS_PROBE(fail3);
994 fail2:
995         EFSYS_PROBE(fail2);
996 fail1:
997         EFSYS_PROBE1(fail1, efx_rc_t, rc);
998
999         return (rc);
1000 }
1001
1002                                 void
1003 efx_vpd_fini(
1004         __in                    efx_nic_t *enp)
1005 {
1006         const efx_vpd_ops_t *evpdop = enp->en_evpdop;
1007
1008         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1009         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1010         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
1011
1012         if (evpdop->evpdo_fini != NULL)
1013                 evpdop->evpdo_fini(enp);
1014
1015         enp->en_evpdop = NULL;
1016         enp->en_mod_flags &= ~EFX_MOD_VPD;
1017 }
1018
1019 #endif  /* EFSYS_OPT_VPD */