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