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