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