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