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