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