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