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