1 /*
2 * Copyright (c) 2012-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_HUNTINGTON || EFSYS_OPT_MEDFORD
35
36 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
37
38 #include "ef10_tlv_layout.h"
39
40 /* Cursor for TLV partition format */
41 typedef struct tlv_cursor_s {
42 uint32_t *block; /* Base of data block */
43 uint32_t *current; /* Cursor position */
44 uint32_t *end; /* End tag position */
45 uint32_t *limit; /* Last dword of data block */
46 } tlv_cursor_t;
47
48 typedef struct nvram_partition_s {
49 uint16_t type;
50 uint8_t chip_select;
51 uint8_t flags;
52 /*
53 * The full length of the NVRAM partition.
54 * This is different from tlv_partition_header.total_length,
55 * which can be smaller.
56 */
57 uint32_t length;
58 uint32_t erase_size;
59 uint32_t *data;
60 tlv_cursor_t tlv_cursor;
61 } nvram_partition_t;
62
63
64 static __checkReturn efx_rc_t
65 tlv_validate_state(
66 __inout tlv_cursor_t *cursor);
67
68
69 static void
tlv_init_block(__out uint32_t * block)70 tlv_init_block(
71 __out uint32_t *block)
72 {
73 *block = __CPU_TO_LE_32(TLV_TAG_END);
74 }
75
76 static uint32_t
tlv_tag(__in tlv_cursor_t * cursor)77 tlv_tag(
78 __in tlv_cursor_t *cursor)
79 {
80 uint32_t dword, tag;
81
82 dword = cursor->current[0];
83 tag = __LE_TO_CPU_32(dword);
84
85 return (tag);
86 }
87
88 static size_t
tlv_length(__in tlv_cursor_t * cursor)89 tlv_length(
90 __in tlv_cursor_t *cursor)
91 {
92 uint32_t dword, length;
93
94 if (tlv_tag(cursor) == TLV_TAG_END)
95 return (0);
96
97 dword = cursor->current[1];
98 length = __LE_TO_CPU_32(dword);
99
100 return ((size_t)length);
101 }
102
103 static uint8_t *
tlv_value(__in tlv_cursor_t * cursor)104 tlv_value(
105 __in tlv_cursor_t *cursor)
106 {
107 if (tlv_tag(cursor) == TLV_TAG_END)
108 return (NULL);
109
110 return ((uint8_t *)(&cursor->current[2]));
111 }
112
113 static uint8_t *
tlv_item(__in tlv_cursor_t * cursor)114 tlv_item(
115 __in tlv_cursor_t *cursor)
116 {
117 if (tlv_tag(cursor) == TLV_TAG_END)
118 return (NULL);
119
120 return ((uint8_t *)cursor->current);
121 }
122
123 /*
124 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
125 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
126 */
127 #define TLV_DWORD_COUNT(length) \
128 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
129
130
131 static uint32_t *
tlv_next_item_ptr(__in tlv_cursor_t * cursor)132 tlv_next_item_ptr(
133 __in tlv_cursor_t *cursor)
134 {
135 uint32_t length;
136
137 length = tlv_length(cursor);
138
139 return (cursor->current + TLV_DWORD_COUNT(length));
140 }
141
142 static __checkReturn efx_rc_t
tlv_advance(__inout tlv_cursor_t * cursor)143 tlv_advance(
144 __inout tlv_cursor_t *cursor)
145 {
146 efx_rc_t rc;
147
148 if ((rc = tlv_validate_state(cursor)) != 0)
149 goto fail1;
150
151 if (cursor->current == cursor->end) {
152 /* No more tags after END tag */
153 cursor->current = NULL;
154 rc = ENOENT;
155 goto fail2;
156 }
157
158 /* Advance to next item and validate */
159 cursor->current = tlv_next_item_ptr(cursor);
160
161 if ((rc = tlv_validate_state(cursor)) != 0)
162 goto fail3;
163
164 return (0);
165
166 fail3:
167 EFSYS_PROBE(fail3);
168 fail2:
169 EFSYS_PROBE(fail2);
170 fail1:
171 EFSYS_PROBE1(fail1, efx_rc_t, rc);
172
173 return (rc);
174 }
175
176 static efx_rc_t
tlv_rewind(__in tlv_cursor_t * cursor)177 tlv_rewind(
178 __in tlv_cursor_t *cursor)
179 {
180 efx_rc_t rc;
181
182 cursor->current = cursor->block;
183
184 if ((rc = tlv_validate_state(cursor)) != 0)
185 goto fail1;
186
187 return (0);
188
189 fail1:
190 EFSYS_PROBE1(fail1, efx_rc_t, rc);
191
192 return (rc);
193 }
194
195 static efx_rc_t
tlv_find(__inout tlv_cursor_t * cursor,__in uint32_t tag)196 tlv_find(
197 __inout tlv_cursor_t *cursor,
198 __in uint32_t tag)
199 {
200 efx_rc_t rc;
201
202 rc = tlv_rewind(cursor);
203 while (rc == 0) {
204 if (tlv_tag(cursor) == tag)
205 break;
206
207 rc = tlv_advance(cursor);
208 }
209 return (rc);
210 }
211
212 static __checkReturn efx_rc_t
tlv_validate_state(__inout tlv_cursor_t * cursor)213 tlv_validate_state(
214 __inout tlv_cursor_t *cursor)
215 {
216 efx_rc_t rc;
217
218 /* Check cursor position */
219 if (cursor->current < cursor->block) {
220 rc = EINVAL;
221 goto fail1;
222 }
223 if (cursor->current > cursor->limit) {
224 rc = EINVAL;
225 goto fail2;
226 }
227
228 if (tlv_tag(cursor) != TLV_TAG_END) {
229 /* Check current item has space for tag and length */
230 if (cursor->current > (cursor->limit - 2)) {
231 cursor->current = NULL;
232 rc = EFAULT;
233 goto fail3;
234 }
235
236 /* Check we have value data for current item and another tag */
237 if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
238 cursor->current = NULL;
239 rc = EFAULT;
240 goto fail4;
241 }
242 }
243
244 return (0);
245
246 fail4:
247 EFSYS_PROBE(fail4);
248 fail3:
249 EFSYS_PROBE(fail3);
250 fail2:
251 EFSYS_PROBE(fail2);
252 fail1:
253 EFSYS_PROBE1(fail1, efx_rc_t, rc);
254
255 return (rc);
256 }
257
258 static efx_rc_t
tlv_init_cursor(__out tlv_cursor_t * cursor,__in uint32_t * block,__in uint32_t * limit,__in uint32_t * current)259 tlv_init_cursor(
260 __out tlv_cursor_t *cursor,
261 __in uint32_t *block,
262 __in uint32_t *limit,
263 __in uint32_t *current)
264 {
265 cursor->block = block;
266 cursor->limit = limit;
267
268 cursor->current = current;
269 cursor->end = NULL;
270
271 return (tlv_validate_state(cursor));
272 }
273
274 static __checkReturn efx_rc_t
tlv_init_cursor_from_size(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size)275 tlv_init_cursor_from_size(
276 __out tlv_cursor_t *cursor,
277 __in_bcount(size)
278 uint8_t *block,
279 __in size_t size)
280 {
281 uint32_t *limit;
282 limit = (void *)(block + size - sizeof (uint32_t));
283 return (tlv_init_cursor(cursor, (void *)block,
284 limit, (void *)block));
285 }
286
287 static __checkReturn efx_rc_t
tlv_init_cursor_at_offset(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size,__in size_t offset)288 tlv_init_cursor_at_offset(
289 __out tlv_cursor_t *cursor,
290 __in_bcount(size)
291 uint8_t *block,
292 __in size_t size,
293 __in size_t offset)
294 {
295 uint32_t *limit;
296 uint32_t *current;
297 limit = (void *)(block + size - sizeof (uint32_t));
298 current = (void *)(block + offset);
299 return (tlv_init_cursor(cursor, (void *)block, limit, current));
300 }
301
302 static __checkReturn efx_rc_t
tlv_require_end(__inout tlv_cursor_t * cursor)303 tlv_require_end(
304 __inout tlv_cursor_t *cursor)
305 {
306 uint32_t *pos;
307 efx_rc_t rc;
308
309 if (cursor->end == NULL) {
310 pos = cursor->current;
311 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
312 goto fail1;
313
314 cursor->end = cursor->current;
315 cursor->current = pos;
316 }
317
318 return (0);
319
320 fail1:
321 EFSYS_PROBE1(fail1, efx_rc_t, rc);
322
323 return (rc);
324 }
325
326 static size_t
tlv_block_length_used(__inout tlv_cursor_t * cursor)327 tlv_block_length_used(
328 __inout tlv_cursor_t *cursor)
329 {
330 efx_rc_t rc;
331
332 if ((rc = tlv_validate_state(cursor)) != 0)
333 goto fail1;
334
335 if ((rc = tlv_require_end(cursor)) != 0)
336 goto fail2;
337
338 /* Return space used (including the END tag) */
339 return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
340
341 fail2:
342 EFSYS_PROBE(fail2);
343 fail1:
344 EFSYS_PROBE1(fail1, efx_rc_t, rc);
345
346 return (0);
347 }
348
349 static uint32_t *
tlv_last_segment_end(__in tlv_cursor_t * cursor)350 tlv_last_segment_end(
351 __in tlv_cursor_t *cursor)
352 {
353 tlv_cursor_t segment_cursor;
354 uint32_t *last_segment_end = cursor->block;
355 uint32_t *segment_start = cursor->block;
356
357 /*
358 * Go through each segment and check that it has an end tag. If there
359 * is no end tag then the previous segment was the last valid one,
360 * so return the pointer to its end tag.
361 */
362 for (;;) {
363 if (tlv_init_cursor(&segment_cursor, segment_start,
364 cursor->limit, segment_start) != 0)
365 break;
366 if (tlv_require_end(&segment_cursor) != 0)
367 break;
368 last_segment_end = segment_cursor.end;
369 segment_start = segment_cursor.end + 1;
370 }
371
372 return (last_segment_end);
373 }
374
375
376 static void
tlv_write(__in tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)377 tlv_write(
378 __in tlv_cursor_t *cursor,
379 __in uint32_t tag,
380 __in_bcount(size) uint8_t *data,
381 __in size_t size)
382 {
383 uint32_t len = (uint32_t)size;
384 uint32_t *ptr;
385
386 ptr = cursor->current;
387
388 *ptr++ = __CPU_TO_LE_32(tag);
389 *ptr++ = __CPU_TO_LE_32(len);
390
391 if (len > 0) {
392 ptr[(len - 1) / sizeof (uint32_t)] = 0;
393 (void) memcpy(ptr, data, len);
394 }
395 }
396
397 static __checkReturn efx_rc_t
tlv_insert(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)398 tlv_insert(
399 __inout tlv_cursor_t *cursor,
400 __in uint32_t tag,
401 __in_bcount(size)
402 uint8_t *data,
403 __in size_t size)
404 {
405 unsigned int delta;
406 uint32_t *last_segment_end;
407 efx_rc_t rc;
408
409 if ((rc = tlv_validate_state(cursor)) != 0)
410 goto fail1;
411
412 if ((rc = tlv_require_end(cursor)) != 0)
413 goto fail2;
414
415 if (tag == TLV_TAG_END) {
416 rc = EINVAL;
417 goto fail3;
418 }
419
420 last_segment_end = tlv_last_segment_end(cursor);
421
422 delta = TLV_DWORD_COUNT(size);
423 if (last_segment_end + 1 + delta > cursor->limit) {
424 rc = ENOSPC;
425 goto fail4;
426 }
427
428 /* Move data up: new space at cursor->current */
429 (void) memmove(cursor->current + delta, cursor->current,
430 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
431
432 /* Adjust the end pointer */
433 cursor->end += delta;
434
435 /* Write new TLV item */
436 tlv_write(cursor, tag, data, size);
437
438 return (0);
439
440 fail4:
441 EFSYS_PROBE(fail4);
442 fail3:
443 EFSYS_PROBE(fail3);
444 fail2:
445 EFSYS_PROBE(fail2);
446 fail1:
447 EFSYS_PROBE1(fail1, efx_rc_t, rc);
448
449 return (rc);
450 }
451
452 static __checkReturn efx_rc_t
tlv_delete(__inout tlv_cursor_t * cursor)453 tlv_delete(
454 __inout tlv_cursor_t *cursor)
455 {
456 unsigned int delta;
457 uint32_t *last_segment_end;
458 efx_rc_t rc;
459
460 if ((rc = tlv_validate_state(cursor)) != 0)
461 goto fail1;
462
463 if (tlv_tag(cursor) == TLV_TAG_END) {
464 rc = EINVAL;
465 goto fail2;
466 }
467
468 delta = TLV_DWORD_COUNT(tlv_length(cursor));
469
470 if ((rc = tlv_require_end(cursor)) != 0)
471 goto fail3;
472
473 last_segment_end = tlv_last_segment_end(cursor);
474
475 /* Shuffle things down, destroying the item at cursor->current */
476 (void) memmove(cursor->current, cursor->current + delta,
477 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
478 /* Zero the new space at the end of the TLV chain */
479 (void) memset(last_segment_end + 1 - delta, 0,
480 delta * sizeof (uint32_t));
481 /* Adjust the end pointer */
482 cursor->end -= delta;
483
484 return (0);
485
486 fail3:
487 EFSYS_PROBE(fail3);
488 fail2:
489 EFSYS_PROBE(fail2);
490 fail1:
491 EFSYS_PROBE1(fail1, efx_rc_t, rc);
492
493 return (rc);
494 }
495
496 static __checkReturn efx_rc_t
tlv_modify(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)497 tlv_modify(
498 __inout tlv_cursor_t *cursor,
499 __in uint32_t tag,
500 __in_bcount(size)
501 uint8_t *data,
502 __in size_t size)
503 {
504 uint32_t *pos;
505 unsigned int old_ndwords;
506 unsigned int new_ndwords;
507 unsigned int delta;
508 uint32_t *last_segment_end;
509 efx_rc_t rc;
510
511 if ((rc = tlv_validate_state(cursor)) != 0)
512 goto fail1;
513
514 if (tlv_tag(cursor) == TLV_TAG_END) {
515 rc = EINVAL;
516 goto fail2;
517 }
518 if (tlv_tag(cursor) != tag) {
519 rc = EINVAL;
520 goto fail3;
521 }
522
523 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
524 new_ndwords = TLV_DWORD_COUNT(size);
525
526 if ((rc = tlv_require_end(cursor)) != 0)
527 goto fail4;
528
529 last_segment_end = tlv_last_segment_end(cursor);
530
531 if (new_ndwords > old_ndwords) {
532 /* Expand space used for TLV item */
533 delta = new_ndwords - old_ndwords;
534 pos = cursor->current + old_ndwords;
535
536 if (last_segment_end + 1 + delta > cursor->limit) {
537 rc = ENOSPC;
538 goto fail5;
539 }
540
541 /* Move up: new space at (cursor->current + old_ndwords) */
542 (void) memmove(pos + delta, pos,
543 (last_segment_end + 1 - pos) * sizeof (uint32_t));
544
545 /* Adjust the end pointer */
546 cursor->end += delta;
547
548 } else if (new_ndwords < old_ndwords) {
549 /* Shrink space used for TLV item */
550 delta = old_ndwords - new_ndwords;
551 pos = cursor->current + new_ndwords;
552
553 /* Move down: remove words at (cursor->current + new_ndwords) */
554 (void) memmove(pos, pos + delta,
555 (last_segment_end + 1 - pos) * sizeof (uint32_t));
556
557 /* Zero the new space at the end of the TLV chain */
558 (void) memset(last_segment_end + 1 - delta, 0,
559 delta * sizeof (uint32_t));
560
561 /* Adjust the end pointer */
562 cursor->end -= delta;
563 }
564
565 /* Write new data */
566 tlv_write(cursor, tag, data, size);
567
568 return (0);
569
570 fail5:
571 EFSYS_PROBE(fail5);
572 fail4:
573 EFSYS_PROBE(fail4);
574 fail3:
575 EFSYS_PROBE(fail3);
576 fail2:
577 EFSYS_PROBE(fail2);
578 fail1:
579 EFSYS_PROBE1(fail1, efx_rc_t, rc);
580
581 return (rc);
582 }
583
checksum_tlv_partition(__in nvram_partition_t * partition)584 static uint32_t checksum_tlv_partition(
585 __in nvram_partition_t *partition)
586 {
587 tlv_cursor_t *cursor;
588 uint32_t *ptr;
589 uint32_t *end;
590 uint32_t csum;
591 size_t len;
592
593 cursor = &partition->tlv_cursor;
594 len = tlv_block_length_used(cursor);
595 EFSYS_ASSERT3U((len & 3), ==, 0);
596
597 csum = 0;
598 ptr = partition->data;
599 end = &ptr[len >> 2];
600
601 while (ptr < end)
602 csum += __LE_TO_CPU_32(*ptr++);
603
604 return (csum);
605 }
606
607 static __checkReturn efx_rc_t
tlv_update_partition_len_and_cks(__in tlv_cursor_t * cursor)608 tlv_update_partition_len_and_cks(
609 __in tlv_cursor_t *cursor)
610 {
611 efx_rc_t rc;
612 nvram_partition_t partition;
613 struct tlv_partition_header *header;
614 struct tlv_partition_trailer *trailer;
615 size_t new_len;
616
617 /*
618 * We just modified the partition, so the total length may not be
619 * valid. Don't use tlv_find(), which performs some sanity checks
620 * that may fail here.
621 */
622 partition.data = cursor->block;
623 (void) memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
624 header = (struct tlv_partition_header *)partition.data;
625 /* Sanity check. */
626 if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
627 rc = EFAULT;
628 goto fail1;
629 }
630 new_len = tlv_block_length_used(&partition.tlv_cursor);
631 if (new_len == 0) {
632 rc = EFAULT;
633 goto fail2;
634 }
635 header->total_length = __CPU_TO_LE_32(new_len);
636 /* Ensure the modified partition always has a new generation count. */
637 header->generation = __CPU_TO_LE_32(
638 __LE_TO_CPU_32(header->generation) + 1);
639
640 trailer = (void *)((uint8_t *)header +
641 new_len - sizeof (*trailer) - sizeof (uint32_t));
642 trailer->generation = header->generation;
643 trailer->checksum = __CPU_TO_LE_32(
644 __LE_TO_CPU_32(trailer->checksum) -
645 checksum_tlv_partition(&partition));
646
647 return (0);
648
649 fail2:
650 EFSYS_PROBE(fail2);
651 fail1:
652 EFSYS_PROBE1(fail1, efx_rc_t, rc);
653
654 return (rc);
655 }
656
657 /* Validate buffer contents (before writing to flash) */
658 __checkReturn efx_rc_t
ef10_nvram_buffer_validate(__in efx_nic_t * enp,__in uint32_t partn,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)659 ef10_nvram_buffer_validate(
660 __in efx_nic_t *enp,
661 __in uint32_t partn,
662 __in_bcount(partn_size) caddr_t partn_data,
663 __in size_t partn_size)
664 {
665 tlv_cursor_t cursor;
666 struct tlv_partition_header *header;
667 struct tlv_partition_trailer *trailer;
668 size_t total_length;
669 uint32_t cksum;
670 int pos;
671 efx_rc_t rc;
672
673 _NOTE(ARGUNUSED(enp, partn));
674 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
675
676 if ((partn_data == NULL) || (partn_size == 0)) {
677 rc = EINVAL;
678 goto fail1;
679 }
680
681 /* The partition header must be the first item (at offset zero) */
682 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
683 partn_size)) != 0) {
684 rc = EFAULT;
685 goto fail2;
686 }
687 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
688 rc = EINVAL;
689 goto fail3;
690 }
691 header = (void *)tlv_item(&cursor);
692
693 /* Check TLV partition length (includes the END tag) */
694 total_length = __LE_TO_CPU_32(header->total_length);
695 if (total_length > partn_size) {
696 rc = EFBIG;
697 goto fail4;
698 }
699
700 /* Check partition ends with PARTITION_TRAILER and END tags */
701 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
702 rc = EINVAL;
703 goto fail5;
704 }
705 trailer = (void *)tlv_item(&cursor);
706
707 if ((rc = tlv_advance(&cursor)) != 0) {
708 rc = EINVAL;
709 goto fail6;
710 }
711 if (tlv_tag(&cursor) != TLV_TAG_END) {
712 rc = EINVAL;
713 goto fail7;
714 }
715
716 /* Check generation counts are consistent */
717 if (trailer->generation != header->generation) {
718 rc = EINVAL;
719 goto fail8;
720 }
721
722 /* Verify partition checksum */
723 cksum = 0;
724 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
725 cksum += *((uint32_t *)(void *)(partn_data + pos));
726 }
727 if (cksum != 0) {
728 rc = EINVAL;
729 goto fail9;
730 }
731
732 return (0);
733
734 fail9:
735 EFSYS_PROBE(fail9);
736 fail8:
737 EFSYS_PROBE(fail8);
738 fail7:
739 EFSYS_PROBE(fail7);
740 fail6:
741 EFSYS_PROBE(fail6);
742 fail5:
743 EFSYS_PROBE(fail5);
744 fail4:
745 EFSYS_PROBE(fail4);
746 fail3:
747 EFSYS_PROBE(fail3);
748 fail2:
749 EFSYS_PROBE(fail2);
750 fail1:
751 EFSYS_PROBE1(fail1, efx_rc_t, rc);
752
753 return (rc);
754 }
755
756
757
758 __checkReturn efx_rc_t
ef10_nvram_buffer_create(__in efx_nic_t * enp,__in uint16_t partn_type,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)759 ef10_nvram_buffer_create(
760 __in efx_nic_t *enp,
761 __in uint16_t partn_type,
762 __in_bcount(partn_size) caddr_t partn_data,
763 __in size_t partn_size)
764 {
765 uint32_t *buf = (void *)partn_data;
766 efx_rc_t rc;
767 tlv_cursor_t cursor;
768 struct tlv_partition_header header;
769 struct tlv_partition_trailer trailer;
770
771 unsigned min_buf_size = sizeof (struct tlv_partition_header) +
772 sizeof (struct tlv_partition_trailer);
773 if (partn_size < min_buf_size) {
774 rc = EINVAL;
775 goto fail1;
776 }
777
778 (void) memset(buf, 0xff, partn_size);
779
780 tlv_init_block(buf);
781 if ((rc = tlv_init_cursor(&cursor, buf,
782 (void *)((uint8_t *)buf + partn_size),
783 buf)) != 0) {
784 goto fail2;
785 }
786
787 header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
788 header.length = __CPU_TO_LE_32(sizeof (header) - 8);
789 header.type_id = __CPU_TO_LE_16(partn_type);
790 header.preset = 0;
791 header.generation = __CPU_TO_LE_32(1);
792 header.total_length = 0; /* This will be fixed below. */
793 if ((rc = tlv_insert(
794 &cursor, TLV_TAG_PARTITION_HEADER,
795 (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
796 goto fail3;
797 if ((rc = tlv_advance(&cursor)) != 0)
798 goto fail4;
799
800 trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
801 trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
802 trailer.generation = header.generation;
803 trailer.checksum = 0; /* This will be fixed below. */
804 if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
805 (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
806 goto fail5;
807
808 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
809 goto fail6;
810
811 /* Check that the partition is valid. */
812 if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
813 partn_data, partn_size)) != 0)
814 goto fail7;
815
816 return (0);
817
818 fail7:
819 EFSYS_PROBE(fail7);
820 fail6:
821 EFSYS_PROBE(fail6);
822 fail5:
823 EFSYS_PROBE(fail5);
824 fail4:
825 EFSYS_PROBE(fail4);
826 fail3:
827 EFSYS_PROBE(fail3);
828 fail2:
829 EFSYS_PROBE(fail2);
830 fail1:
831 EFSYS_PROBE1(fail1, efx_rc_t, rc);
832
833 return (rc);
834 }
835
836 static uint32_t
byte_offset(__in uint32_t * position,__in uint32_t * base)837 byte_offset(
838 __in uint32_t *position,
839 __in uint32_t *base)
840 {
841 return (uint32_t)((uintptr_t)position - (uintptr_t)base);
842 }
843
844 __checkReturn efx_rc_t
ef10_nvram_buffer_find_item_start(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__out uint32_t * startp)845 ef10_nvram_buffer_find_item_start(
846 __in_bcount(buffer_size)
847 caddr_t bufferp,
848 __in size_t buffer_size,
849 __out uint32_t *startp)
850 {
851 // Read past partition header to find start address of the first key
852 tlv_cursor_t cursor;
853 efx_rc_t rc;
854
855 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
856 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
857 buffer_size)) != 0) {
858 rc = EFAULT;
859 goto fail1;
860 }
861 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
862 rc = EINVAL;
863 goto fail2;
864 }
865
866 if ((rc = tlv_advance(&cursor)) != 0) {
867 rc = EINVAL;
868 goto fail3;
869 }
870 *startp = byte_offset(cursor.current, cursor.block);
871
872 if ((rc = tlv_require_end(&cursor)) != 0)
873 goto fail4;
874
875 return (0);
876
877 fail4:
878 EFSYS_PROBE(fail4);
879 fail3:
880 EFSYS_PROBE(fail3);
881 fail2:
882 EFSYS_PROBE(fail2);
883 fail1:
884 EFSYS_PROBE1(fail1, efx_rc_t, rc);
885
886 return (rc);
887 }
888
889 __checkReturn efx_rc_t
ef10_nvram_buffer_find_end(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * endp)890 ef10_nvram_buffer_find_end(
891 __in_bcount(buffer_size)
892 caddr_t bufferp,
893 __in size_t buffer_size,
894 __in uint32_t offset,
895 __out uint32_t *endp)
896 {
897 // Read to end of partition
898 tlv_cursor_t cursor;
899 efx_rc_t rc;
900
901 _NOTE(ARGUNUSED(offset));
902
903 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
904 buffer_size)) != 0) {
905 rc = EFAULT;
906 goto fail1;
907 }
908
909 if ((rc = tlv_require_end(&cursor)) != 0)
910 goto fail2;
911
912 *endp = byte_offset(tlv_last_segment_end(&cursor)+1, cursor.block);
913
914 return (0);
915
916 fail2:
917 EFSYS_PROBE(fail2);
918 fail1:
919 EFSYS_PROBE1(fail1, efx_rc_t, rc);
920
921 return (rc);
922 }
923
924 __checkReturn __success(return != B_FALSE) boolean_t
ef10_nvram_buffer_find_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * startp,__out uint32_t * lengthp)925 ef10_nvram_buffer_find_item(
926 __in_bcount(buffer_size)
927 caddr_t bufferp,
928 __in size_t buffer_size,
929 __in uint32_t offset,
930 __out uint32_t *startp,
931 __out uint32_t *lengthp)
932 {
933 // Find TLV at offset and return key start and length
934 tlv_cursor_t cursor;
935 uint32_t tag;
936
937 if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
938 buffer_size, offset) != 0) {
939 return (B_FALSE);
940 }
941
942 while (tlv_item(&cursor) != NULL) {
943 tag = tlv_tag(&cursor);
944 if (tag == TLV_TAG_PARTITION_HEADER ||
945 tag == TLV_TAG_PARTITION_TRAILER) {
946 if (tlv_advance(&cursor) != 0) {
947 break;
948 }
949 continue;
950 }
951 *startp = byte_offset(cursor.current, cursor.block);
952 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
953 cursor.current);
954 return (B_TRUE);
955 }
956
957 return (B_FALSE);
958 }
959
960 __checkReturn efx_rc_t
ef10_nvram_buffer_get_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__out_bcount_part (item_max_size,* lengthp)caddr_t itemp,__in size_t item_max_size,__out uint32_t * lengthp)961 ef10_nvram_buffer_get_item(
962 __in_bcount(buffer_size)
963 caddr_t bufferp,
964 __in size_t buffer_size,
965 __in uint32_t offset,
966 __in uint32_t length,
967 __out_bcount_part(item_max_size, *lengthp)
968 caddr_t itemp,
969 __in size_t item_max_size,
970 __out uint32_t *lengthp)
971 {
972 efx_rc_t rc;
973 tlv_cursor_t cursor;
974 uint32_t item_length;
975
976 if (item_max_size < length) {
977 rc = ENOSPC;
978 goto fail1;
979 }
980
981 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
982 buffer_size, offset)) != 0) {
983 goto fail2;
984 }
985
986 item_length = tlv_length(&cursor);
987 if (length < item_length) {
988 rc = ENOSPC;
989 goto fail3;
990 }
991 (void) memcpy(itemp, tlv_value(&cursor), item_length);
992
993 *lengthp = item_length;
994
995 return (0);
996
997 fail3:
998 EFSYS_PROBE(fail3);
999 fail2:
1000 EFSYS_PROBE(fail2);
1001 fail1:
1002 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1003
1004 return (rc);
1005 }
1006
1007 __checkReturn efx_rc_t
ef10_nvram_buffer_insert_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in_bcount (length)caddr_t keyp,__in uint32_t length,__out uint32_t * lengthp)1008 ef10_nvram_buffer_insert_item(
1009 __in_bcount(buffer_size)
1010 caddr_t bufferp,
1011 __in size_t buffer_size,
1012 __in uint32_t offset,
1013 __in_bcount(length) caddr_t keyp,
1014 __in uint32_t length,
1015 __out uint32_t *lengthp)
1016 {
1017 efx_rc_t rc;
1018 tlv_cursor_t cursor;
1019
1020 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1021 buffer_size, offset)) != 0) {
1022 goto fail1;
1023 }
1024
1025 rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1026
1027 if (rc != 0) {
1028 goto fail2;
1029 }
1030
1031 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1032 cursor.current);
1033
1034 return (0);
1035
1036 fail2:
1037 EFSYS_PROBE(fail2);
1038 fail1:
1039 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1040
1041 return (rc);
1042 }
1043
1044 __checkReturn efx_rc_t
ef10_nvram_buffer_delete_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__in uint32_t end)1045 ef10_nvram_buffer_delete_item(
1046 __in_bcount(buffer_size)
1047 caddr_t bufferp,
1048 __in size_t buffer_size,
1049 __in uint32_t offset,
1050 __in uint32_t length,
1051 __in uint32_t end)
1052 {
1053 efx_rc_t rc;
1054 tlv_cursor_t cursor;
1055 _NOTE(ARGUNUSED(length, end))
1056
1057 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1058 buffer_size, offset)) != 0) {
1059 goto fail1;
1060 }
1061
1062 if ((rc = tlv_delete(&cursor)) != 0)
1063 goto fail2;
1064
1065 return (0);
1066
1067 fail2:
1068 EFSYS_PROBE(fail2);
1069 fail1:
1070 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1071
1072 return (rc);
1073 }
1074
1075 __checkReturn efx_rc_t
ef10_nvram_buffer_finish(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size)1076 ef10_nvram_buffer_finish(
1077 __in_bcount(buffer_size)
1078 caddr_t bufferp,
1079 __in size_t buffer_size)
1080 {
1081 efx_rc_t rc;
1082 tlv_cursor_t cursor;
1083
1084 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1085 buffer_size)) != 0) {
1086 rc = EFAULT;
1087 goto fail1;
1088 }
1089
1090 if ((rc = tlv_require_end(&cursor)) != 0)
1091 goto fail2;
1092
1093 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1094 goto fail3;
1095
1096 return (0);
1097
1098 fail3:
1099 EFSYS_PROBE(fail3);
1100 fail2:
1101 EFSYS_PROBE(fail2);
1102 fail1:
1103 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1104
1105 return (rc);
1106 }
1107
1108
1109
1110 /*
1111 * Read and validate a segment from a partition. A segment is a complete
1112 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1113 * be multiple segments in a partition, so seg_offset allows segments
1114 * beyond the first to be read.
1115 */
1116 static __checkReturn efx_rc_t
ef10_nvram_read_tlv_segment(__in efx_nic_t * enp,__in uint32_t partn,__in size_t seg_offset,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size)1117 ef10_nvram_read_tlv_segment(
1118 __in efx_nic_t *enp,
1119 __in uint32_t partn,
1120 __in size_t seg_offset,
1121 __in_bcount(max_seg_size) caddr_t seg_data,
1122 __in size_t max_seg_size)
1123 {
1124 tlv_cursor_t cursor;
1125 struct tlv_partition_header *header;
1126 struct tlv_partition_trailer *trailer;
1127 size_t total_length;
1128 uint32_t cksum;
1129 int pos;
1130 efx_rc_t rc;
1131
1132 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1133
1134 if ((seg_data == NULL) || (max_seg_size == 0)) {
1135 rc = EINVAL;
1136 goto fail1;
1137 }
1138
1139 /* Read initial chunk of the segment, starting at offset */
1140 if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1141 EF10_NVRAM_CHUNK,
1142 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1143 goto fail2;
1144 }
1145
1146 /* A PARTITION_HEADER tag must be the first item at the given offset */
1147 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1148 max_seg_size)) != 0) {
1149 rc = EFAULT;
1150 goto fail3;
1151 }
1152 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1153 rc = EINVAL;
1154 goto fail4;
1155 }
1156 header = (void *)tlv_item(&cursor);
1157
1158 /* Check TLV segment length (includes the END tag) */
1159 total_length = __LE_TO_CPU_32(header->total_length);
1160 if (total_length > max_seg_size) {
1161 rc = EFBIG;
1162 goto fail5;
1163 }
1164
1165 /* Read the remaining segment content */
1166 if (total_length > EF10_NVRAM_CHUNK) {
1167 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1168 seg_offset + EF10_NVRAM_CHUNK,
1169 seg_data + EF10_NVRAM_CHUNK,
1170 total_length - EF10_NVRAM_CHUNK,
1171 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1172 goto fail6;
1173 }
1174
1175 /* Check segment ends with PARTITION_TRAILER and END tags */
1176 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1177 rc = EINVAL;
1178 goto fail7;
1179 }
1180 trailer = (void *)tlv_item(&cursor);
1181
1182 if ((rc = tlv_advance(&cursor)) != 0) {
1183 rc = EINVAL;
1184 goto fail8;
1185 }
1186 if (tlv_tag(&cursor) != TLV_TAG_END) {
1187 rc = EINVAL;
1188 goto fail9;
1189 }
1190
1191 /* Check data read from segment is consistent */
1192 if (trailer->generation != header->generation) {
1193 /*
1194 * The partition data may have been modified between successive
1195 * MCDI NVRAM_READ requests by the MC or another PCI function.
1196 *
1197 * The caller must retry to obtain consistent partition data.
1198 */
1199 rc = EAGAIN;
1200 goto fail10;
1201 }
1202
1203 /* Verify segment checksum */
1204 cksum = 0;
1205 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1206 cksum += *((uint32_t *)(void *)(seg_data + pos));
1207 }
1208 if (cksum != 0) {
1209 rc = EINVAL;
1210 goto fail11;
1211 }
1212
1213 return (0);
1214
1215 fail11:
1216 EFSYS_PROBE(fail11);
1217 fail10:
1218 EFSYS_PROBE(fail10);
1219 fail9:
1220 EFSYS_PROBE(fail9);
1221 fail8:
1222 EFSYS_PROBE(fail8);
1223 fail7:
1224 EFSYS_PROBE(fail7);
1225 fail6:
1226 EFSYS_PROBE(fail6);
1227 fail5:
1228 EFSYS_PROBE(fail5);
1229 fail4:
1230 EFSYS_PROBE(fail4);
1231 fail3:
1232 EFSYS_PROBE(fail3);
1233 fail2:
1234 EFSYS_PROBE(fail2);
1235 fail1:
1236 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1237
1238 return (rc);
1239 }
1240
1241 /*
1242 * Read a single TLV item from a host memory
1243 * buffer containing a TLV formatted segment.
1244 */
1245 __checkReturn efx_rc_t
ef10_nvram_buf_read_tlv(__in efx_nic_t * enp,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__deref_out_bcount_opt (* sizep)caddr_t * datap,__out size_t * sizep)1246 ef10_nvram_buf_read_tlv(
1247 __in efx_nic_t *enp,
1248 __in_bcount(max_seg_size) caddr_t seg_data,
1249 __in size_t max_seg_size,
1250 __in uint32_t tag,
1251 __deref_out_bcount_opt(*sizep) caddr_t *datap,
1252 __out size_t *sizep)
1253 {
1254 tlv_cursor_t cursor;
1255 caddr_t data;
1256 size_t length;
1257 caddr_t value;
1258 efx_rc_t rc;
1259
1260 if ((seg_data == NULL) || (max_seg_size == 0)) {
1261 rc = EINVAL;
1262 goto fail1;
1263 }
1264
1265 /* Find requested TLV tag in segment data */
1266 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1267 max_seg_size)) != 0) {
1268 rc = EFAULT;
1269 goto fail2;
1270 }
1271 if ((rc = tlv_find(&cursor, tag)) != 0) {
1272 rc = ENOENT;
1273 goto fail3;
1274 }
1275 value = (caddr_t)tlv_value(&cursor);
1276 length = tlv_length(&cursor);
1277
1278 if (length == 0)
1279 data = NULL;
1280 else {
1281 /* Copy out data from TLV item */
1282 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1283 if (data == NULL) {
1284 rc = ENOMEM;
1285 goto fail4;
1286 }
1287 (void) memcpy(data, value, length);
1288 }
1289
1290 *datap = data;
1291 *sizep = length;
1292
1293 return (0);
1294
1295 fail4:
1296 EFSYS_PROBE(fail4);
1297 fail3:
1298 EFSYS_PROBE(fail3);
1299 fail2:
1300 EFSYS_PROBE(fail2);
1301 fail1:
1302 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1303
1304 return (rc);
1305 }
1306
1307 /* Read a single TLV item from the first segment in a TLV formatted partition */
1308 __checkReturn efx_rc_t
ef10_nvram_partn_read_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__deref_out_bcount_opt (* seg_sizep)caddr_t * seg_datap,__out size_t * seg_sizep)1309 ef10_nvram_partn_read_tlv(
1310 __in efx_nic_t *enp,
1311 __in uint32_t partn,
1312 __in uint32_t tag,
1313 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
1314 __out size_t *seg_sizep)
1315 {
1316 caddr_t seg_data = NULL;
1317 size_t partn_size = 0;
1318 size_t length;
1319 caddr_t data;
1320 int retry;
1321 efx_rc_t rc;
1322
1323 /* Allocate sufficient memory for the entire partition */
1324 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1325 goto fail1;
1326
1327 if (partn_size == 0) {
1328 rc = ENOENT;
1329 goto fail2;
1330 }
1331
1332 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1333 if (seg_data == NULL) {
1334 rc = ENOMEM;
1335 goto fail3;
1336 }
1337
1338 /*
1339 * Read the first segment in a TLV partition. Retry until consistent
1340 * segment contents are returned. Inconsistent data may be read if:
1341 * a) the segment contents are invalid
1342 * b) the MC has rebooted while we were reading the partition
1343 * c) the partition has been modified while we were reading it
1344 * Limit retry attempts to ensure forward progress.
1345 */
1346 retry = 10;
1347 do {
1348 rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1349 seg_data, partn_size);
1350 } while ((rc == EAGAIN) && (--retry > 0));
1351
1352 if (rc != 0) {
1353 /* Failed to obtain consistent segment data */
1354 goto fail4;
1355 }
1356
1357 if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1358 tag, &data, &length)) != 0)
1359 goto fail5;
1360
1361 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1362
1363 *seg_datap = data;
1364 *seg_sizep = length;
1365
1366 return (0);
1367
1368 fail5:
1369 EFSYS_PROBE(fail5);
1370 fail4:
1371 EFSYS_PROBE(fail4);
1372
1373 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1374 fail3:
1375 EFSYS_PROBE(fail3);
1376 fail2:
1377 EFSYS_PROBE(fail2);
1378 fail1:
1379 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1380
1381 return (rc);
1382 }
1383
1384 /* Compute the size of a segment. */
1385 static __checkReturn efx_rc_t
ef10_nvram_buf_segment_size(__in caddr_t seg_data,__in size_t max_seg_size,__out size_t * seg_sizep)1386 ef10_nvram_buf_segment_size(
1387 __in caddr_t seg_data,
1388 __in size_t max_seg_size,
1389 __out size_t *seg_sizep)
1390 {
1391 efx_rc_t rc;
1392 tlv_cursor_t cursor;
1393 struct tlv_partition_header *header;
1394 uint32_t cksum;
1395 int pos;
1396 uint32_t *end_tag_position;
1397 uint32_t segment_length;
1398
1399 /* A PARTITION_HEADER tag must be the first item at the given offset */
1400 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1401 max_seg_size)) != 0) {
1402 rc = EFAULT;
1403 goto fail1;
1404 }
1405 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1406 rc = EINVAL;
1407 goto fail2;
1408 }
1409 header = (void *)tlv_item(&cursor);
1410
1411 /* Check TLV segment length (includes the END tag) */
1412 *seg_sizep = __LE_TO_CPU_32(header->total_length);
1413 if (*seg_sizep > max_seg_size) {
1414 rc = EFBIG;
1415 goto fail3;
1416 }
1417
1418 /* Check segment ends with PARTITION_TRAILER and END tags */
1419 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1420 rc = EINVAL;
1421 goto fail4;
1422 }
1423
1424 if ((rc = tlv_advance(&cursor)) != 0) {
1425 rc = EINVAL;
1426 goto fail5;
1427 }
1428 if (tlv_tag(&cursor) != TLV_TAG_END) {
1429 rc = EINVAL;
1430 goto fail6;
1431 }
1432 end_tag_position = cursor.current;
1433
1434 /* Verify segment checksum */
1435 cksum = 0;
1436 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1437 cksum += *((uint32_t *)(void *)(seg_data + pos));
1438 }
1439 if (cksum != 0) {
1440 rc = EINVAL;
1441 goto fail7;
1442 }
1443
1444 /*
1445 * Calculate total length from HEADER to END tags and compare to
1446 * max_seg_size and the total_length field in the HEADER tag.
1447 */
1448 segment_length = tlv_block_length_used(&cursor);
1449
1450 if (segment_length > max_seg_size) {
1451 rc = EINVAL;
1452 goto fail8;
1453 }
1454
1455 if (segment_length != *seg_sizep) {
1456 rc = EINVAL;
1457 goto fail9;
1458 }
1459
1460 /* Skip over the first HEADER tag. */
1461 rc = tlv_rewind(&cursor);
1462 rc = tlv_advance(&cursor);
1463
1464 while (rc == 0) {
1465 if (tlv_tag(&cursor) == TLV_TAG_END) {
1466 /* Check that the END tag is the one found earlier. */
1467 if (cursor.current != end_tag_position)
1468 goto fail10;
1469 break;
1470 }
1471 /* Check for duplicate HEADER tags before the END tag. */
1472 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1473 rc = EINVAL;
1474 goto fail11;
1475 }
1476
1477 rc = tlv_advance(&cursor);
1478 }
1479 if (rc != 0)
1480 goto fail12;
1481
1482 return (0);
1483
1484 fail12:
1485 EFSYS_PROBE(fail12);
1486 fail11:
1487 EFSYS_PROBE(fail11);
1488 fail10:
1489 EFSYS_PROBE(fail10);
1490 fail9:
1491 EFSYS_PROBE(fail9);
1492 fail8:
1493 EFSYS_PROBE(fail8);
1494 fail7:
1495 EFSYS_PROBE(fail7);
1496 fail6:
1497 EFSYS_PROBE(fail6);
1498 fail5:
1499 EFSYS_PROBE(fail5);
1500 fail4:
1501 EFSYS_PROBE(fail4);
1502 fail3:
1503 EFSYS_PROBE(fail3);
1504 fail2:
1505 EFSYS_PROBE(fail2);
1506 fail1:
1507 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1508
1509 return (rc);
1510 }
1511
1512 /*
1513 * Add or update a single TLV item in a host memory buffer containing a TLV
1514 * formatted segment. Historically partitions consisted of only one segment.
1515 */
1516 __checkReturn efx_rc_t
ef10_nvram_buf_write_tlv(__inout_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__in_bcount (tag_size)caddr_t tag_data,__in size_t tag_size,__out size_t * total_lengthp)1517 ef10_nvram_buf_write_tlv(
1518 __inout_bcount(max_seg_size) caddr_t seg_data,
1519 __in size_t max_seg_size,
1520 __in uint32_t tag,
1521 __in_bcount(tag_size) caddr_t tag_data,
1522 __in size_t tag_size,
1523 __out size_t *total_lengthp)
1524 {
1525 tlv_cursor_t cursor;
1526 struct tlv_partition_header *header;
1527 struct tlv_partition_trailer *trailer;
1528 uint32_t generation;
1529 uint32_t cksum;
1530 int pos;
1531 efx_rc_t rc;
1532
1533 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1534 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1535 max_seg_size)) != 0) {
1536 rc = EFAULT;
1537 goto fail1;
1538 }
1539 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1540 rc = EINVAL;
1541 goto fail2;
1542 }
1543 header = (void *)tlv_item(&cursor);
1544
1545 /* Update the TLV chain to contain the new data */
1546 if ((rc = tlv_find(&cursor, tag)) == 0) {
1547 /* Modify existing TLV item */
1548 if ((rc = tlv_modify(&cursor, tag,
1549 (uint8_t *)tag_data, tag_size)) != 0)
1550 goto fail3;
1551 } else {
1552 /* Insert a new TLV item before the PARTITION_TRAILER */
1553 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1554 if (rc != 0) {
1555 rc = EINVAL;
1556 goto fail4;
1557 }
1558 if ((rc = tlv_insert(&cursor, tag,
1559 (uint8_t *)tag_data, tag_size)) != 0) {
1560 rc = EINVAL;
1561 goto fail5;
1562 }
1563 }
1564
1565 /* Find the trailer tag */
1566 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1567 rc = EINVAL;
1568 goto fail6;
1569 }
1570 trailer = (void *)tlv_item(&cursor);
1571
1572 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1573 *total_lengthp = tlv_block_length_used(&cursor);
1574 if (*total_lengthp > max_seg_size) {
1575 rc = ENOSPC;
1576 goto fail7;
1577 }
1578 generation = __LE_TO_CPU_32(header->generation) + 1;
1579
1580 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1581 header->generation = __CPU_TO_LE_32(generation);
1582 trailer->generation = __CPU_TO_LE_32(generation);
1583
1584 /* Recompute PARTITION_TRAILER checksum */
1585 trailer->checksum = 0;
1586 cksum = 0;
1587 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1588 cksum += *((uint32_t *)(void *)(seg_data + pos));
1589 }
1590 trailer->checksum = ~cksum + 1;
1591
1592 return (0);
1593
1594 fail7:
1595 EFSYS_PROBE(fail7);
1596 fail6:
1597 EFSYS_PROBE(fail6);
1598 fail5:
1599 EFSYS_PROBE(fail5);
1600 fail4:
1601 EFSYS_PROBE(fail4);
1602 fail3:
1603 EFSYS_PROBE(fail3);
1604 fail2:
1605 EFSYS_PROBE(fail2);
1606 fail1:
1607 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1608
1609 return (rc);
1610 }
1611
1612 /*
1613 * Add or update a single TLV item in the first segment of a TLV formatted
1614 * dynamic config partition. The first segment is the current active
1615 * configuration.
1616 */
1617 __checkReturn efx_rc_t
ef10_nvram_partn_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size)1618 ef10_nvram_partn_write_tlv(
1619 __in efx_nic_t *enp,
1620 __in uint32_t partn,
1621 __in uint32_t tag,
1622 __in_bcount(size) caddr_t data,
1623 __in size_t size)
1624 {
1625 return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1626 size, B_FALSE);
1627 }
1628
1629 /*
1630 * Read a segment from nvram at the given offset into a buffer (segment_data)
1631 * and optionally write a new tag to it.
1632 */
1633 static __checkReturn efx_rc_t
ef10_nvram_segment_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__inout caddr_t * seg_datap,__inout size_t * partn_offsetp,__inout size_t * src_remain_lenp,__inout size_t * dest_remain_lenp,__in boolean_t write)1634 ef10_nvram_segment_write_tlv(
1635 __in efx_nic_t *enp,
1636 __in uint32_t partn,
1637 __in uint32_t tag,
1638 __in_bcount(size) caddr_t data,
1639 __in size_t size,
1640 __inout caddr_t *seg_datap,
1641 __inout size_t *partn_offsetp,
1642 __inout size_t *src_remain_lenp,
1643 __inout size_t *dest_remain_lenp,
1644 __in boolean_t write)
1645 {
1646 efx_rc_t rc;
1647 efx_rc_t status;
1648 size_t original_segment_size;
1649 size_t modified_segment_size;
1650
1651 /*
1652 * Read the segment from NVRAM into the segment_data buffer and validate
1653 * it, returning if it does not validate. This is not a failure unless
1654 * this is the first segment in a partition. In this case the caller
1655 * must propogate the error.
1656 */
1657 status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1658 *seg_datap, *src_remain_lenp);
1659 if (status != 0)
1660 return (EINVAL);
1661
1662 status = ef10_nvram_buf_segment_size(*seg_datap,
1663 *src_remain_lenp, &original_segment_size);
1664 if (status != 0)
1665 return (EINVAL);
1666
1667 if (write) {
1668 /* Update the contents of the segment in the buffer */
1669 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1670 *dest_remain_lenp, tag, data, size,
1671 &modified_segment_size)) != 0)
1672 goto fail1;
1673 *dest_remain_lenp -= modified_segment_size;
1674 *seg_datap += modified_segment_size;
1675 } else {
1676 /*
1677 * We won't modify this segment, but still need to update the
1678 * remaining lengths and pointers.
1679 */
1680 *dest_remain_lenp -= original_segment_size;
1681 *seg_datap += original_segment_size;
1682 }
1683
1684 *partn_offsetp += original_segment_size;
1685 *src_remain_lenp -= original_segment_size;
1686
1687 return (0);
1688
1689 fail1:
1690 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1691
1692 return (rc);
1693 }
1694
1695 /*
1696 * Add or update a single TLV item in either the first segment or in all
1697 * segments in a TLV formatted dynamic config partition. Dynamic config
1698 * partitions on boards that support RFID are divided into a number of segments,
1699 * each formatted like a partition, with header, trailer and end tags. The first
1700 * segment is the current active configuration.
1701 *
1702 * The segments are initialised by manftest and each contain a different
1703 * configuration e.g. firmware variant. The firmware can be instructed
1704 * via RFID to copy a segment to replace the first segment, hence changing the
1705 * active configuration. This allows ops to change the configuration of a board
1706 * prior to shipment using RFID.
1707 *
1708 * Changes to the dynamic config may need to be written to all segments (e.g.
1709 * firmware versions) or just the first segment (changes to the active
1710 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1711 * If only the first segment is written the code still needs to be aware of the
1712 * possible presence of subsequent segments as writing to a segment may cause
1713 * its size to increase, which would overwrite the subsequent segments and
1714 * invalidate them.
1715 */
1716 __checkReturn efx_rc_t
ef10_nvram_partn_write_segment_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__in boolean_t all_segments)1717 ef10_nvram_partn_write_segment_tlv(
1718 __in efx_nic_t *enp,
1719 __in uint32_t partn,
1720 __in uint32_t tag,
1721 __in_bcount(size) caddr_t data,
1722 __in size_t size,
1723 __in boolean_t all_segments)
1724 {
1725 size_t partn_size = 0;
1726 caddr_t partn_data;
1727 size_t total_length = 0;
1728 efx_rc_t rc;
1729 size_t current_offset = 0;
1730 size_t remaining_original_length;
1731 size_t remaining_modified_length;
1732 caddr_t segment_data;
1733
1734 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1735
1736 /* Allocate sufficient memory for the entire partition */
1737 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1738 goto fail1;
1739
1740 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1741 if (partn_data == NULL) {
1742 rc = ENOMEM;
1743 goto fail2;
1744 }
1745
1746 remaining_original_length = partn_size;
1747 remaining_modified_length = partn_size;
1748 segment_data = partn_data;
1749
1750 /* Lock the partition */
1751 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1752 goto fail3;
1753
1754 /* Iterate over each (potential) segment to update it. */
1755 do {
1756 boolean_t write = all_segments || current_offset == 0;
1757
1758 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1759 &segment_data, ¤t_offset, &remaining_original_length,
1760 &remaining_modified_length, write);
1761 if (rc != 0) {
1762 if (current_offset == 0) {
1763 /*
1764 * If no data has been read then the first
1765 * segment is invalid, which is an error.
1766 */
1767 goto fail4;
1768 }
1769 break;
1770 }
1771 } while (current_offset < partn_size);
1772
1773 total_length = (uintptr_t)segment_data - (uintptr_t)partn_data;
1774
1775 /*
1776 * We've run out of space. This should actually be dealt with by
1777 * ef10_nvram_buf_write_tlv returning ENOSPC.
1778 */
1779 if (total_length > partn_size) {
1780 rc = ENOSPC;
1781 goto fail5;
1782 }
1783
1784 /* Erase the whole partition in NVRAM */
1785 if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1786 goto fail6;
1787
1788 /* Write new partition contents from the buffer to NVRAM */
1789 if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1790 total_length)) != 0)
1791 goto fail7;
1792
1793 /* Unlock the partition */
1794 ef10_nvram_partn_unlock(enp, partn);
1795
1796 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1797
1798 return (0);
1799
1800 fail7:
1801 EFSYS_PROBE(fail7);
1802 fail6:
1803 EFSYS_PROBE(fail6);
1804 fail5:
1805 EFSYS_PROBE(fail5);
1806 fail4:
1807 EFSYS_PROBE(fail4);
1808
1809 ef10_nvram_partn_unlock(enp, partn);
1810 fail3:
1811 EFSYS_PROBE(fail3);
1812
1813 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1814 fail2:
1815 EFSYS_PROBE(fail2);
1816 fail1:
1817 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1818
1819 return (rc);
1820 }
1821
1822 /*
1823 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1824 * not the data used by the segments in the partition.
1825 */
1826 __checkReturn efx_rc_t
ef10_nvram_partn_size(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * sizep)1827 ef10_nvram_partn_size(
1828 __in efx_nic_t *enp,
1829 __in uint32_t partn,
1830 __out size_t *sizep)
1831 {
1832 efx_rc_t rc;
1833
1834 if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1835 NULL, NULL, NULL)) != 0)
1836 goto fail1;
1837
1838 return (0);
1839
1840 fail1:
1841 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1842
1843 return (rc);
1844 }
1845
1846 __checkReturn efx_rc_t
ef10_nvram_partn_lock(__in efx_nic_t * enp,__in uint32_t partn)1847 ef10_nvram_partn_lock(
1848 __in efx_nic_t *enp,
1849 __in uint32_t partn)
1850 {
1851 efx_rc_t rc;
1852
1853 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1854 goto fail1;
1855
1856 return (0);
1857
1858 fail1:
1859 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1860
1861 return (rc);
1862 }
1863
1864 __checkReturn efx_rc_t
ef10_nvram_partn_read_mode(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size,__in uint32_t mode)1865 ef10_nvram_partn_read_mode(
1866 __in efx_nic_t *enp,
1867 __in uint32_t partn,
1868 __in unsigned int offset,
1869 __out_bcount(size) caddr_t data,
1870 __in size_t size,
1871 __in uint32_t mode)
1872 {
1873 size_t chunk;
1874 efx_rc_t rc;
1875
1876 while (size > 0) {
1877 chunk = MIN(size, EF10_NVRAM_CHUNK);
1878
1879 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1880 data, chunk, mode)) != 0) {
1881 goto fail1;
1882 }
1883
1884 size -= chunk;
1885 data += chunk;
1886 offset += chunk;
1887 }
1888
1889 return (0);
1890
1891 fail1:
1892 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1893
1894 return (rc);
1895 }
1896
1897 __checkReturn efx_rc_t
ef10_nvram_partn_read(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1898 ef10_nvram_partn_read(
1899 __in efx_nic_t *enp,
1900 __in uint32_t partn,
1901 __in unsigned int offset,
1902 __out_bcount(size) caddr_t data,
1903 __in size_t size)
1904 {
1905 /*
1906 * Read requests which come in through the EFX API expect to
1907 * read the current, active partition.
1908 */
1909 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1910 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1911 }
1912
1913 __checkReturn efx_rc_t
ef10_nvram_partn_erase(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__in size_t size)1914 ef10_nvram_partn_erase(
1915 __in efx_nic_t *enp,
1916 __in uint32_t partn,
1917 __in unsigned int offset,
1918 __in size_t size)
1919 {
1920 efx_rc_t rc;
1921 uint32_t erase_size;
1922
1923 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1924 &erase_size, NULL)) != 0)
1925 goto fail1;
1926
1927 if (erase_size == 0) {
1928 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1929 goto fail2;
1930 } else {
1931 if (size % erase_size != 0) {
1932 rc = EINVAL;
1933 goto fail3;
1934 }
1935 while (size > 0) {
1936 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1937 erase_size)) != 0)
1938 goto fail4;
1939 offset += erase_size;
1940 size -= erase_size;
1941 }
1942 }
1943
1944 return (0);
1945
1946 fail4:
1947 EFSYS_PROBE(fail4);
1948 fail3:
1949 EFSYS_PROBE(fail3);
1950 fail2:
1951 EFSYS_PROBE(fail2);
1952 fail1:
1953 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1954
1955 return (rc);
1956 }
1957
1958 __checkReturn efx_rc_t
ef10_nvram_partn_write(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1959 ef10_nvram_partn_write(
1960 __in efx_nic_t *enp,
1961 __in uint32_t partn,
1962 __in unsigned int offset,
1963 __out_bcount(size) caddr_t data,
1964 __in size_t size)
1965 {
1966 size_t chunk;
1967 uint32_t write_size;
1968 efx_rc_t rc;
1969
1970 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1971 NULL, &write_size)) != 0)
1972 goto fail1;
1973
1974 if (write_size != 0) {
1975 /*
1976 * Check that the size is a multiple of the write chunk size if
1977 * the write chunk size is available.
1978 */
1979 if (size % write_size != 0) {
1980 rc = EINVAL;
1981 goto fail2;
1982 }
1983 } else {
1984 write_size = EF10_NVRAM_CHUNK;
1985 }
1986
1987 while (size > 0) {
1988 chunk = MIN(size, write_size);
1989
1990 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1991 data, chunk)) != 0) {
1992 goto fail3;
1993 }
1994
1995 size -= chunk;
1996 data += chunk;
1997 offset += chunk;
1998 }
1999
2000 return (0);
2001
2002 fail3:
2003 EFSYS_PROBE(fail3);
2004 fail2:
2005 EFSYS_PROBE(fail2);
2006 fail1:
2007 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2008
2009 return (rc);
2010 }
2011
2012 void
ef10_nvram_partn_unlock(__in efx_nic_t * enp,__in uint32_t partn)2013 ef10_nvram_partn_unlock(
2014 __in efx_nic_t *enp,
2015 __in uint32_t partn)
2016 {
2017 boolean_t reboot;
2018 efx_rc_t rc;
2019
2020 reboot = B_FALSE;
2021 if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
2022 goto fail1;
2023
2024 return;
2025
2026 fail1:
2027 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2028 }
2029
2030 __checkReturn efx_rc_t
2031 ef10_nvram_partn_set_version(
2032 __in efx_nic_t *enp,
2033 __in uint32_t partn,
2034 __in_ecount(4) uint16_t version[4])
2035 {
2036 struct tlv_partition_version partn_version;
2037 size_t size;
2038 efx_rc_t rc;
2039
2040 /* Add or modify partition version TLV item */
2041 partn_version.version_w = __CPU_TO_LE_16(version[0]);
2042 partn_version.version_x = __CPU_TO_LE_16(version[1]);
2043 partn_version.version_y = __CPU_TO_LE_16(version[2]);
2044 partn_version.version_z = __CPU_TO_LE_16(version[3]);
2045
2046 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2047
2048 /* Write the version number to all segments in the partition */
2049 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2050 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2051 TLV_TAG_PARTITION_VERSION(partn),
2052 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2053 goto fail1;
2054
2055 return (0);
2056
2057 fail1:
2058 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2059
2060 return (rc);
2061 }
2062
2063 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2064
2065 #if EFSYS_OPT_NVRAM
2066
2067 typedef struct ef10_parttbl_entry_s {
2068 unsigned int partn;
2069 unsigned int port;
2070 efx_nvram_type_t nvtype;
2071 } ef10_parttbl_entry_t;
2072
2073 /* Translate EFX NVRAM types to firmware partition types */
2074 static ef10_parttbl_entry_t hunt_parttbl[] = {
2075 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE},
2076 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE},
2077 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE},
2078 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE},
2079 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN},
2080 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN},
2081 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN},
2082 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN},
2083 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM},
2084 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM},
2085 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM},
2086 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM},
2087 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2088 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2089 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2090 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2091 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG},
2092 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG},
2093 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG},
2094 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG},
2095 {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA},
2096 {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA},
2097 {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA},
2098 {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA},
2099 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP},
2100 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP},
2101 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP},
2102 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP},
2103 {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE},
2104 {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE},
2105 {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE},
2106 {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE}
2107 };
2108
2109 static ef10_parttbl_entry_t medford_parttbl[] = {
2110 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE},
2111 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE},
2112 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE},
2113 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE},
2114 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN},
2115 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN},
2116 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN},
2117 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN},
2118 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM},
2119 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM},
2120 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM},
2121 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM},
2122 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2123 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2124 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2125 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2126 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG},
2127 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG},
2128 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG},
2129 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG},
2130 {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA},
2131 {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA},
2132 {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA},
2133 {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA},
2134 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP},
2135 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP},
2136 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP},
2137 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP},
2138 {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE},
2139 {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE},
2140 {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE},
2141 {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE}
2142 };
2143
2144 static __checkReturn efx_rc_t
ef10_parttbl_get(__in efx_nic_t * enp,__out ef10_parttbl_entry_t ** parttblp,__out size_t * parttbl_rowsp)2145 ef10_parttbl_get(
2146 __in efx_nic_t *enp,
2147 __out ef10_parttbl_entry_t **parttblp,
2148 __out size_t *parttbl_rowsp)
2149 {
2150 switch (enp->en_family) {
2151 case EFX_FAMILY_HUNTINGTON:
2152 *parttblp = hunt_parttbl;
2153 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2154 break;
2155
2156 case EFX_FAMILY_MEDFORD:
2157 *parttblp = medford_parttbl;
2158 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2159 break;
2160
2161 default:
2162 EFSYS_ASSERT(B_FALSE);
2163 return (EINVAL);
2164 }
2165 return (0);
2166 }
2167
2168 __checkReturn efx_rc_t
ef10_nvram_type_to_partn(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out uint32_t * partnp)2169 ef10_nvram_type_to_partn(
2170 __in efx_nic_t *enp,
2171 __in efx_nvram_type_t type,
2172 __out uint32_t *partnp)
2173 {
2174 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2175 ef10_parttbl_entry_t *parttbl = NULL;
2176 size_t parttbl_rows = 0;
2177 unsigned int i;
2178
2179 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2180 EFSYS_ASSERT(partnp != NULL);
2181
2182 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2183 for (i = 0; i < parttbl_rows; i++) {
2184 ef10_parttbl_entry_t *entry = &parttbl[i];
2185
2186 if (entry->nvtype == type &&
2187 entry->port == emip->emi_port) {
2188 *partnp = entry->partn;
2189 return (0);
2190 }
2191 }
2192 }
2193
2194 return (ENOTSUP);
2195 }
2196
2197 #if EFSYS_OPT_DIAG
2198
2199 static __checkReturn efx_rc_t
ef10_nvram_partn_to_type(__in efx_nic_t * enp,__in uint32_t partn,__out efx_nvram_type_t * typep)2200 ef10_nvram_partn_to_type(
2201 __in efx_nic_t *enp,
2202 __in uint32_t partn,
2203 __out efx_nvram_type_t *typep)
2204 {
2205 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2206 ef10_parttbl_entry_t *parttbl = NULL;
2207 size_t parttbl_rows = 0;
2208 unsigned int i;
2209
2210 EFSYS_ASSERT(typep != NULL);
2211
2212 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2213 for (i = 0; i < parttbl_rows; i++) {
2214 ef10_parttbl_entry_t *entry = &parttbl[i];
2215
2216 if (entry->partn == partn &&
2217 entry->port == emip->emi_port) {
2218 *typep = entry->nvtype;
2219 return (0);
2220 }
2221 }
2222 }
2223
2224 return (ENOTSUP);
2225 }
2226
2227 __checkReturn efx_rc_t
ef10_nvram_test(__in efx_nic_t * enp)2228 ef10_nvram_test(
2229 __in efx_nic_t *enp)
2230 {
2231 efx_nvram_type_t type;
2232 unsigned int npartns = 0;
2233 uint32_t *partns = NULL;
2234 size_t size;
2235 unsigned int i;
2236 efx_rc_t rc;
2237
2238 /* Read available partitions from NVRAM partition map */
2239 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2240 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2241 if (partns == NULL) {
2242 rc = ENOMEM;
2243 goto fail1;
2244 }
2245
2246 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2247 &npartns)) != 0) {
2248 goto fail2;
2249 }
2250
2251 for (i = 0; i < npartns; i++) {
2252 /* Check if the partition is supported for this port */
2253 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2254 continue;
2255
2256 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2257 goto fail3;
2258 }
2259
2260 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2261 return (0);
2262
2263 fail3:
2264 EFSYS_PROBE(fail3);
2265 fail2:
2266 EFSYS_PROBE(fail2);
2267 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2268 fail1:
2269 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2270 return (rc);
2271 }
2272
2273 #endif /* EFSYS_OPT_DIAG */
2274
2275 __checkReturn efx_rc_t
2276 ef10_nvram_partn_get_version(
2277 __in efx_nic_t *enp,
2278 __in uint32_t partn,
2279 __out uint32_t *subtypep,
2280 __out_ecount(4) uint16_t version[4])
2281 {
2282 efx_rc_t rc;
2283
2284 /* FIXME: get highest partn version from all ports */
2285 /* FIXME: return partn description if available */
2286
2287 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2288 version, NULL, 0)) != 0)
2289 goto fail1;
2290
2291 return (0);
2292
2293 fail1:
2294 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2295
2296 return (rc);
2297 }
2298
2299 __checkReturn efx_rc_t
ef10_nvram_partn_rw_start(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * chunk_sizep)2300 ef10_nvram_partn_rw_start(
2301 __in efx_nic_t *enp,
2302 __in uint32_t partn,
2303 __out size_t *chunk_sizep)
2304 {
2305 efx_rc_t rc;
2306
2307 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2308 goto fail1;
2309
2310 if (chunk_sizep != NULL)
2311 *chunk_sizep = EF10_NVRAM_CHUNK;
2312
2313 return (0);
2314
2315 fail1:
2316 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2317
2318 return (rc);
2319 }
2320
2321 void
ef10_nvram_partn_rw_finish(__in efx_nic_t * enp,__in uint32_t partn)2322 ef10_nvram_partn_rw_finish(
2323 __in efx_nic_t *enp,
2324 __in uint32_t partn)
2325 {
2326 ef10_nvram_partn_unlock(enp, partn);
2327 }
2328
2329 #endif /* EFSYS_OPT_NVRAM */
2330
2331 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
2332