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