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