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