]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/sfxge/common/efx_vpd.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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           int
112 efx_vpd_init(
113         __in                    efx_nic_t *enp)
114 {
115         efx_vpd_ops_t *evpdop;
116         int 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, int, rc);
161
162         return (rc);
163 }
164
165         __checkReturn           int
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         int 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, int, rc);
183
184         return (rc);
185 }
186
187         __checkReturn           int
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         int 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, int, rc);
206
207         return (rc);
208 }
209
210         __checkReturn           int
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         int 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, int, rc);
229
230         return (rc);
231 }
232
233         __checkReturn           int
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         int 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, int, rc);
259
260         return (rc);
261 }
262
263         __checkReturn           int
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         int 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, int, rc);
283
284         return (rc);
285 }
286
287         __checkReturn           int
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         int 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, int, rc);
307
308         return (rc);
309 }
310
311         __checkReturn           int
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         int 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, int, rc);
332
333         return (rc);
334 }
335
336         __checkReturn           int
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         int 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, int, rc);
355
356         return (rc);
357 }
358
359 static  __checkReturn           int
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         int 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, int, rc);
440
441         return (rc);
442 }
443
444 static  __checkReturn           int
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         int 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, int, rc);
478
479         return (rc);
480 }
481
482         __checkReturn           int
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         int 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, int, rc);
510
511         return (rc);
512 }
513
514         __checkReturn           int
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         int 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                                 goto fail2;
550
551                         if ((rc = efx_vpd_next_keyword(data + offset,
552                             taglen, pos, &keyword, &keylen)) != 0)
553                                 goto fail3;
554
555                         if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
556                                 cksum = 0;
557                                 for (i = 0; i < offset + pos + 4; i++)
558                                         cksum += data[i];
559
560                                 if (cksum != 0) {
561                                         rc = EFAULT;
562                                         goto fail4;
563                                 }
564
565                                 cksummed = B_TRUE;
566                         }
567                 }
568
569         done:
570                 offset += taglen;
571         }
572
573         if (!cksummed) {
574                 rc = EFAULT;
575                 goto fail5;
576         }
577
578         if (cksummedp != NULL)
579                 *cksummedp = cksummed;
580
581         return (0);
582
583 fail5:
584         EFSYS_PROBE(fail5);
585 fail4:
586         EFSYS_PROBE(fail4);
587 fail3:
588         EFSYS_PROBE(fail3);
589 fail2:
590         EFSYS_PROBE(fail2);
591 fail1:
592         EFSYS_PROBE1(fail1, int, rc);
593
594         return (rc);
595 }
596
597 static  uint8_t __efx_vpd_blank_pid[] = {
598         /* Large resource type ID length 1 */
599         0x82, 0x01, 0x00,
600         /* Product name ' ' */
601         0x32,
602 };
603
604 static uint8_t __efx_vpd_blank_r[] = {
605         /* Large resource type VPD-R length 4 */
606         0x90, 0x04, 0x00,
607         /* RV keyword length 1 */
608         'R', 'V', 0x01,
609         /* RV payload checksum */
610         0x00,
611 };
612
613         __checkReturn           int
614 efx_vpd_hunk_reinit(
615         __in_bcount(size)       caddr_t data,
616         __in                    size_t size,
617         __in                    boolean_t wantpid)
618 {
619         unsigned int offset = 0;
620         unsigned int pos;
621         efx_byte_t byte;
622         uint8_t cksum;
623         int rc;
624
625         if (size < 0x100) {
626                 rc = ENOSPC;
627                 goto fail1;
628         }
629
630         if (wantpid) {
631                 memcpy(data + offset, __efx_vpd_blank_pid,
632                     sizeof (__efx_vpd_blank_pid));
633                 offset += sizeof (__efx_vpd_blank_pid);
634         }
635
636         memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r));
637         offset += sizeof (__efx_vpd_blank_r);
638
639         /* Update checksum */
640         cksum = 0;
641         for (pos = 0; pos < offset; pos++)
642                 cksum += data[pos];
643         data[offset - 1] -= cksum;
644
645         /* Append trailing tag */
646         EFX_POPULATE_BYTE_3(byte,
647                             TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE,
648                             TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE,
649                             TAG_SMALL_ITEM_SIZE, 0);
650         data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0);
651         offset++;
652
653         return (0);
654
655 fail1:
656         EFSYS_PROBE1(fail1, int, rc);
657
658         return (rc);
659 }
660
661         __checkReturn                   int
662 efx_vpd_hunk_next(
663         __in_bcount(size)               caddr_t data,
664         __in                            size_t size,
665         __out                           efx_vpd_tag_t *tagp,
666         __out                           efx_vpd_keyword_t *keywordp,
667         __out_bcount_opt(*paylenp)      unsigned int *payloadp,
668         __out_opt                       uint8_t *paylenp,
669         __inout                         unsigned int *contp)
670 {
671         efx_vpd_tag_t tag;
672         efx_vpd_keyword_t keyword = 0;
673         unsigned int offset;
674         unsigned int pos;
675         unsigned int index;
676         uint16_t taglen;
677         uint8_t keylen;
678         uint8_t paylen;
679         int rc;
680
681         offset = index = 0;
682         _NOTE(CONSTANTCONDITION)
683         while (1) {
684                 if ((rc = efx_vpd_next_tag(data, size, &offset,
685                     &tag, &taglen)) != 0)
686                         goto fail1;
687                 if (tag == EFX_VPD_END)
688                         break;
689
690                 if (tag == EFX_VPD_ID) {
691                         if (index == *contp) {
692                                 EFSYS_ASSERT3U(taglen, <, 0x100);
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         *contp = 0;
716         return (0);
717
718 done:
719         *tagp = tag;
720         *keywordp = keyword;
721         if (payloadp != NULL)
722                 *payloadp = offset;
723         if (paylenp != NULL)
724                 *paylenp = paylen;
725
726         ++(*contp);
727         return (0);
728
729 fail2:
730         EFSYS_PROBE(fail2);
731 fail1:
732         EFSYS_PROBE1(fail1, int, rc);
733
734         return (rc);
735 }
736
737         __checkReturn           int
738 efx_vpd_hunk_get(
739         __in_bcount(size)       caddr_t data,
740         __in                    size_t size,
741         __in                    efx_vpd_tag_t tag,
742         __in                    efx_vpd_keyword_t keyword,
743         __out                   unsigned int *payloadp,
744         __out                   uint8_t *paylenp)
745 {
746         efx_vpd_tag_t itag;
747         efx_vpd_keyword_t ikeyword;
748         unsigned int offset;
749         unsigned int pos;
750         uint16_t taglen;
751         uint8_t keylen;
752         int rc;
753
754         offset = 0;
755         _NOTE(CONSTANTCONDITION)
756         while (1) {
757                 if ((rc = efx_vpd_next_tag(data, size, &offset,
758                     &itag, &taglen)) != 0)
759                         goto fail1;
760                 if (itag == EFX_VPD_END)
761                         break;
762
763                 if (itag == tag) {
764                         if (itag == EFX_VPD_ID) {
765                                 EFSYS_ASSERT3U(taglen, <, 0x100);
766
767                                 *paylenp = (uint8_t)MIN(taglen, 0xff);
768                                 *payloadp = offset;
769                                 return (0);
770                         }
771
772                         for (pos = 0; pos != taglen; pos += 3 + keylen) {
773                                 if ((rc = efx_vpd_next_keyword(data + offset,
774                                     taglen, pos, &ikeyword, &keylen)) != 0)
775                                         goto fail2;
776
777                                 if (ikeyword == keyword) {
778                                         *paylenp = keylen;
779                                         *payloadp = offset + pos + 3;
780                                         return (0);
781                                 }
782                         }
783                 }
784
785                 offset += taglen;
786         }
787
788         /* Not an error */
789         return (ENOENT);
790
791 fail2:
792         EFSYS_PROBE(fail2);
793 fail1:
794         EFSYS_PROBE1(fail1, int, rc);
795
796         return (rc);
797 }
798
799         __checkReturn           int
800 efx_vpd_hunk_set(
801         __in_bcount(size)       caddr_t data,
802         __in                    size_t size,
803         __in                    efx_vpd_value_t *evvp)
804 {
805         efx_word_t word;
806         efx_vpd_tag_t tag;
807         efx_vpd_keyword_t keyword;
808         unsigned int offset;
809         unsigned int pos;
810         unsigned int taghead;
811         unsigned int source;
812         unsigned int dest;
813         unsigned int i;
814         uint16_t taglen;
815         uint8_t keylen;
816         uint8_t cksum;
817         size_t used;
818         int rc;
819
820         switch (evvp->evv_tag) {
821         case EFX_VPD_ID:
822                 if (evvp->evv_keyword != 0) {
823                         rc = EINVAL;
824                         goto fail1;
825                 }
826
827                 /* Can't delete the ID keyword */
828                 if (evvp->evv_length == 0) {
829                         rc = EINVAL;
830                         goto fail1;
831                 }
832                 break;
833
834         case EFX_VPD_RO:
835                 if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) {
836                         rc = EINVAL;
837                         goto fail1;
838                 }
839                 break;
840
841         default:
842                 rc = EINVAL;
843                 goto fail1;
844         }
845
846         /* Determine total size of all current tags */
847         if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0)
848                 goto fail2;
849
850         offset = 0;
851         _NOTE(CONSTANTCONDITION)
852         while (1) {
853                 taghead = offset;
854                 if ((rc = efx_vpd_next_tag(data, size, &offset,
855                     &tag, &taglen)) != 0)
856                         goto fail3;
857                 if (tag == EFX_VPD_END)
858                         break;
859                 else if (tag != evvp->evv_tag) {
860                         offset += taglen;
861                         continue;
862                 }
863
864                 /* We only support modifying large resource tags */
865                 if (offset - taghead != 3) {
866                         rc = EINVAL;
867                         goto fail4;
868                 }
869
870                 /*
871                  * Work out the offset of the byte immediately after the
872                  * old (=source) and new (=dest) new keyword/tag
873                  */
874                 pos = 0;
875                 if (tag == EFX_VPD_ID) {
876                         source = offset + taglen;
877                         dest = offset + evvp->evv_length;
878                         goto check_space;
879                 }
880
881                 EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO);
882                 source = dest = 0;
883                 for (pos = 0; pos != taglen; pos += 3 + keylen) {
884                         if ((rc = efx_vpd_next_keyword(data + offset,
885                             taglen, pos, &keyword, &keylen)) != 0)
886                                 goto fail5;
887
888                         if (keyword == evvp->evv_keyword &&
889                             evvp->evv_length == 0) {
890                                 /* Deleting this keyword */
891                                 source = offset + pos + 3 + keylen;
892                                 dest = offset + pos;
893                                 break;
894
895                         } else if (keyword == evvp->evv_keyword) {
896                                 /* Adjusting this keyword */
897                                 source = offset + pos + 3 + keylen;
898                                 dest = offset + pos + 3 + evvp->evv_length;
899                                 break;
900
901                         } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
902                                 /* The RV keyword must be at the end */
903                                 EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen);
904
905                                 /*
906                                  * The keyword doesn't already exist. If the
907                                  * user deleting a non-existant keyword then
908                                  * this is a no-op.
909                                  */
910                                 if (evvp->evv_length == 0)
911                                         return (0);
912
913                                 /* Insert this keyword before the RV keyword */
914                                 source = offset + pos;
915                                 dest = offset + pos + 3 + evvp->evv_length;
916                                 break;
917                         }
918                 }
919
920         check_space:
921                 if (used + dest > size + source) {
922                         rc = ENOSPC;
923                         goto fail6;
924                 }
925
926                 /* Move trailing data */
927                 (void) memmove(data + dest, data + source, used - source);
928
929                 /* Copy contents */
930                 memcpy(data + dest - evvp->evv_length, evvp->evv_value,
931                     evvp->evv_length);
932
933                 /* Insert new keyword header if required */
934                 if (tag != EFX_VPD_ID && evvp->evv_length > 0) {
935                         EFX_POPULATE_WORD_1(word, EFX_WORD_0,
936                                             evvp->evv_keyword);
937                         data[offset + pos + 0] =
938                             EFX_WORD_FIELD(word, EFX_BYTE_0);
939                         data[offset + pos + 1] =
940                             EFX_WORD_FIELD(word, EFX_BYTE_1);
941                         data[offset + pos + 2] = evvp->evv_length;
942                 }
943
944                 /* Modify tag length (large resource type) */
945                 taglen += (dest - source);
946                 EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen);
947                 data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0);
948                 data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1);
949
950                 goto checksum;
951         }
952
953         /* Unable to find the matching tag */
954         rc = ENOENT;
955         goto fail7;
956
957 checksum:
958         /* Find the RV tag, and update the checksum */
959         offset = 0;
960         _NOTE(CONSTANTCONDITION)
961         while (1) {
962                 if ((rc = efx_vpd_next_tag(data, size, &offset,
963                     &tag, &taglen)) != 0)
964                         goto fail8;
965                 if (tag == EFX_VPD_END)
966                         break;
967                 if (tag == EFX_VPD_RO) {
968                         for (pos = 0; pos != taglen; pos += 3 + keylen) {
969                                 if ((rc = efx_vpd_next_keyword(data + offset,
970                                     taglen, pos, &keyword, &keylen)) != 0)
971                                         goto fail9;
972
973                                 if (keyword == EFX_VPD_KEYWORD('R', 'V')) {
974                                         cksum = 0;
975                                         for (i = 0; i < offset + pos + 3; i++)
976                                                 cksum += data[i];
977                                         data[i] = -cksum;
978                                         break;
979                                 }
980                         }
981                 }
982
983                 offset += taglen;
984         }
985
986         /* Zero out the unused portion */
987         (void) memset(data + offset + taglen, 0xff, size - offset - taglen);
988
989         return (0);
990
991 fail9:
992         EFSYS_PROBE(fail9);
993 fail8:
994         EFSYS_PROBE(fail8);
995 fail7:
996         EFSYS_PROBE(fail7);
997 fail6:
998         EFSYS_PROBE(fail6);
999 fail5:
1000         EFSYS_PROBE(fail5);
1001 fail4:
1002         EFSYS_PROBE(fail4);
1003 fail3:
1004         EFSYS_PROBE(fail3);
1005 fail2:
1006         EFSYS_PROBE(fail2);
1007 fail1:
1008         EFSYS_PROBE1(fail1, int, rc);
1009
1010         return (rc);
1011 }
1012
1013                                 void
1014 efx_vpd_fini(
1015         __in                    efx_nic_t *enp)
1016 {
1017         efx_vpd_ops_t *evpdop = enp->en_evpdop;
1018
1019         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1020         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1021         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD);
1022
1023         if (evpdop->evpdo_fini != NULL)
1024                 evpdop->evpdo_fini(enp);
1025
1026         enp->en_evpdop = NULL;
1027         enp->en_mod_flags &= ~EFX_MOD_VPD;
1028 }
1029
1030 #endif  /* EFSYS_OPT_VPD */