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