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