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
efx_vpd_init(__in efx_nic_t * enp)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
efx_vpd_size(__in efx_nic_t * enp,__out size_t * sizep)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
efx_vpd_read(__in efx_nic_t * enp,__out_bcount (size)caddr_t data,__in size_t size)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
efx_vpd_verify(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)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
efx_vpd_reinit(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)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
efx_vpd_get(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size,__inout efx_vpd_value_t * evvp)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
efx_vpd_set(__in efx_nic_t * enp,__inout_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_value_t * evvp)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
efx_vpd_next(__in efx_nic_t * enp,__inout_bcount (size)caddr_t data,__in size_t size,__out efx_vpd_value_t * evvp,__inout unsigned int * contp)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
efx_vpd_write(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)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
efx_vpd_next_tag(__in caddr_t data,__in size_t size,__inout unsigned int * offsetp,__out efx_vpd_tag_t * tagp,__out uint16_t * lengthp)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
efx_vpd_next_keyword(__in_bcount (size)caddr_t tag,__in size_t size,__in unsigned int pos,__out efx_vpd_keyword_t * keywordp,__out uint8_t * lengthp)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
efx_vpd_hunk_length(__in_bcount (size)caddr_t data,__in size_t size,__out size_t * lengthp)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
efx_vpd_hunk_verify(__in_bcount (size)caddr_t data,__in size_t size,__out_opt boolean_t * cksummedp)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
efx_vpd_hunk_reinit(__in caddr_t data,__in size_t size,__in boolean_t wantpid)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
efx_vpd_hunk_next(__in_bcount (size)caddr_t data,__in size_t size,__out efx_vpd_tag_t * tagp,__out efx_vpd_keyword_t * keywordp,__out_bcount_opt (* paylenp)unsigned int * payloadp,__out_opt uint8_t * paylenp,__inout unsigned int * contp)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
efx_vpd_hunk_get(__in_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_tag_t tag,__in efx_vpd_keyword_t keyword,__out unsigned int * payloadp,__out uint8_t * paylenp)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
efx_vpd_hunk_set(__in_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_value_t * evvp)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
efx_vpd_fini(__in efx_nic_t * enp)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 */
1003