1 /*-
2 * Copyright (c) 2012-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include "efx.h"
35 #include "efx_impl.h"
36
37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
38
39 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
40
41 #include "ef10_tlv_layout.h"
42
43 /* Cursor for TLV partition format */
44 typedef struct tlv_cursor_s {
45 uint32_t *block; /* Base of data block */
46 uint32_t *current; /* Cursor position */
47 uint32_t *end; /* End tag position */
48 uint32_t *limit; /* Last dword of data block */
49 } tlv_cursor_t;
50
51 typedef struct nvram_partition_s {
52 uint16_t type;
53 uint8_t chip_select;
54 uint8_t flags;
55 /*
56 * The full length of the NVRAM partition.
57 * This is different from tlv_partition_header.total_length,
58 * which can be smaller.
59 */
60 uint32_t length;
61 uint32_t erase_size;
62 uint32_t *data;
63 tlv_cursor_t tlv_cursor;
64 } nvram_partition_t;
65
66 static __checkReturn efx_rc_t
67 tlv_validate_state(
68 __inout tlv_cursor_t *cursor);
69
70 static void
71 tlv_init_block(
72 __out uint32_t *block)
73 {
74 *block = __CPU_TO_LE_32(TLV_TAG_END);
75 }
76
77 static uint32_t
78 tlv_tag(
79 __in tlv_cursor_t *cursor)
80 {
81 uint32_t dword, tag;
82
83 dword = cursor->current[0];
84 tag = __LE_TO_CPU_32(dword);
85
86 return (tag);
87 }
88
89 static size_t
90 tlv_length(
91 __in tlv_cursor_t *cursor)
92 {
93 uint32_t dword, length;
94
95 if (tlv_tag(cursor) == TLV_TAG_END)
96 return (0);
97
98 dword = cursor->current[1];
99 length = __LE_TO_CPU_32(dword);
100
101 return ((size_t)length);
102 }
103
104 static uint8_t *
105 tlv_value(
106 __in tlv_cursor_t *cursor)
107 {
108 if (tlv_tag(cursor) == TLV_TAG_END)
109 return (NULL);
110
111 return ((uint8_t *)(&cursor->current[2]));
112 }
113
114 static uint8_t *
115 tlv_item(
116 __in tlv_cursor_t *cursor)
117 {
118 if (tlv_tag(cursor) == TLV_TAG_END)
119 return (NULL);
120
121 return ((uint8_t *)cursor->current);
122 }
123
124 /*
125 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
126 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
127 */
128 #define TLV_DWORD_COUNT(length) \
129 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
130
131 static uint32_t *
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
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
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
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
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 - 1)) {
231 cursor->current = NULL;
232 rc = EFAULT;
233 goto fail3;
234 }
235
236 /* Check we have value data for current item and an END tag */
237 if (tlv_next_item_ptr(cursor) > cursor->limit) {
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
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
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 = (uint32_t *)(block + size - sizeof (uint32_t));
283 return (tlv_init_cursor(cursor, (uint32_t *)block,
284 limit, (uint32_t *)block));
285 }
286
287 static __checkReturn efx_rc_t
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 = (uint32_t *)(block + size - sizeof (uint32_t));
298 current = (uint32_t *)(block + offset);
299 return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
300 }
301
302 static __checkReturn efx_rc_t
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
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 *
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 static uint32_t *
376 tlv_write(
377 __in tlv_cursor_t *cursor,
378 __in uint32_t tag,
379 __in_bcount(size) uint8_t *data,
380 __in size_t size)
381 {
382 uint32_t len = size;
383 uint32_t *ptr;
384
385 ptr = cursor->current;
386
387 *ptr++ = __CPU_TO_LE_32(tag);
388 *ptr++ = __CPU_TO_LE_32(len);
389
390 if (len > 0) {
391 ptr[(len - 1) / sizeof (uint32_t)] = 0;
392 memcpy(ptr, data, len);
393 ptr += EFX_P2ROUNDUP(uint32_t, len,
394 sizeof (uint32_t)) / sizeof (*ptr);
395 }
396
397 return (ptr);
398 }
399
400 static __checkReturn efx_rc_t
401 tlv_insert(
402 __inout tlv_cursor_t *cursor,
403 __in uint32_t tag,
404 __in_bcount(size)
405 uint8_t *data,
406 __in size_t size)
407 {
408 unsigned int delta;
409 uint32_t *last_segment_end;
410 efx_rc_t rc;
411
412 if ((rc = tlv_validate_state(cursor)) != 0)
413 goto fail1;
414
415 if ((rc = tlv_require_end(cursor)) != 0)
416 goto fail2;
417
418 if (tag == TLV_TAG_END) {
419 rc = EINVAL;
420 goto fail3;
421 }
422
423 last_segment_end = tlv_last_segment_end(cursor);
424
425 delta = TLV_DWORD_COUNT(size);
426 if (last_segment_end + 1 + delta > cursor->limit) {
427 rc = ENOSPC;
428 goto fail4;
429 }
430
431 /* Move data up: new space at cursor->current */
432 memmove(cursor->current + delta, cursor->current,
433 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
434
435 /* Adjust the end pointer */
436 cursor->end += delta;
437
438 /* Write new TLV item */
439 tlv_write(cursor, tag, data, size);
440
441 return (0);
442
443 fail4:
444 EFSYS_PROBE(fail4);
445 fail3:
446 EFSYS_PROBE(fail3);
447 fail2:
448 EFSYS_PROBE(fail2);
449 fail1:
450 EFSYS_PROBE1(fail1, efx_rc_t, rc);
451
452 return (rc);
453 }
454
455 static __checkReturn efx_rc_t
456 tlv_delete(
457 __inout tlv_cursor_t *cursor)
458 {
459 unsigned int delta;
460 uint32_t *last_segment_end;
461 efx_rc_t rc;
462
463 if ((rc = tlv_validate_state(cursor)) != 0)
464 goto fail1;
465
466 if (tlv_tag(cursor) == TLV_TAG_END) {
467 rc = EINVAL;
468 goto fail2;
469 }
470
471 delta = TLV_DWORD_COUNT(tlv_length(cursor));
472
473 if ((rc = tlv_require_end(cursor)) != 0)
474 goto fail3;
475
476 last_segment_end = tlv_last_segment_end(cursor);
477
478 /* Shuffle things down, destroying the item at cursor->current */
479 memmove(cursor->current, cursor->current + delta,
480 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
481 /* Zero the new space at the end of the TLV chain */
482 memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
483 /* Adjust the end pointer */
484 cursor->end -= delta;
485
486 return (0);
487
488 fail3:
489 EFSYS_PROBE(fail3);
490 fail2:
491 EFSYS_PROBE(fail2);
492 fail1:
493 EFSYS_PROBE1(fail1, efx_rc_t, rc);
494
495 return (rc);
496 }
497
498 static __checkReturn efx_rc_t
499 tlv_modify(
500 __inout tlv_cursor_t *cursor,
501 __in uint32_t tag,
502 __in_bcount(size)
503 uint8_t *data,
504 __in size_t size)
505 {
506 uint32_t *pos;
507 unsigned int old_ndwords;
508 unsigned int new_ndwords;
509 unsigned int delta;
510 uint32_t *last_segment_end;
511 efx_rc_t rc;
512
513 if ((rc = tlv_validate_state(cursor)) != 0)
514 goto fail1;
515
516 if (tlv_tag(cursor) == TLV_TAG_END) {
517 rc = EINVAL;
518 goto fail2;
519 }
520 if (tlv_tag(cursor) != tag) {
521 rc = EINVAL;
522 goto fail3;
523 }
524
525 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
526 new_ndwords = TLV_DWORD_COUNT(size);
527
528 if ((rc = tlv_require_end(cursor)) != 0)
529 goto fail4;
530
531 last_segment_end = tlv_last_segment_end(cursor);
532
533 if (new_ndwords > old_ndwords) {
534 /* Expand space used for TLV item */
535 delta = new_ndwords - old_ndwords;
536 pos = cursor->current + old_ndwords;
537
538 if (last_segment_end + 1 + delta > cursor->limit) {
539 rc = ENOSPC;
540 goto fail5;
541 }
542
543 /* Move up: new space at (cursor->current + old_ndwords) */
544 memmove(pos + delta, pos,
545 (last_segment_end + 1 - pos) * sizeof (uint32_t));
546
547 /* Adjust the end pointer */
548 cursor->end += delta;
549
550 } else if (new_ndwords < old_ndwords) {
551 /* Shrink space used for TLV item */
552 delta = old_ndwords - new_ndwords;
553 pos = cursor->current + new_ndwords;
554
555 /* Move down: remove words at (cursor->current + new_ndwords) */
556 memmove(pos, pos + delta,
557 (last_segment_end + 1 - pos) * sizeof (uint32_t));
558
559 /* Zero the new space at the end of the TLV chain */
560 memset(last_segment_end + 1 - delta, 0,
561 delta * sizeof (uint32_t));
562
563 /* Adjust the end pointer */
564 cursor->end -= delta;
565 }
566
567 /* Write new data */
568 tlv_write(cursor, tag, data, size);
569
570 return (0);
571
572 fail5:
573 EFSYS_PROBE(fail5);
574 fail4:
575 EFSYS_PROBE(fail4);
576 fail3:
577 EFSYS_PROBE(fail3);
578 fail2:
579 EFSYS_PROBE(fail2);
580 fail1:
581 EFSYS_PROBE1(fail1, efx_rc_t, rc);
582
583 return (rc);
584 }
585
586 static uint32_t checksum_tlv_partition(
587 __in nvram_partition_t *partition)
588 {
589 tlv_cursor_t *cursor;
590 uint32_t *ptr;
591 uint32_t *end;
592 uint32_t csum;
593 size_t len;
594
595 cursor = &partition->tlv_cursor;
596 len = tlv_block_length_used(cursor);
597 EFSYS_ASSERT3U((len & 3), ==, 0);
598
599 csum = 0;
600 ptr = partition->data;
601 end = &ptr[len >> 2];
602
603 while (ptr < end)
604 csum += __LE_TO_CPU_32(*ptr++);
605
606 return (csum);
607 }
608
609 static __checkReturn efx_rc_t
610 tlv_update_partition_len_and_cks(
611 __in tlv_cursor_t *cursor)
612 {
613 efx_rc_t rc;
614 nvram_partition_t partition;
615 struct tlv_partition_header *header;
616 struct tlv_partition_trailer *trailer;
617 size_t new_len;
618
619 /*
620 * We just modified the partition, so the total length may not be
621 * valid. Don't use tlv_find(), which performs some sanity checks
622 * that may fail here.
623 */
624 partition.data = cursor->block;
625 memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
626 header = (struct tlv_partition_header *)partition.data;
627 /* Sanity check. */
628 if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
629 rc = EFAULT;
630 goto fail1;
631 }
632 new_len = tlv_block_length_used(&partition.tlv_cursor);
633 if (new_len == 0) {
634 rc = EFAULT;
635 goto fail2;
636 }
637 header->total_length = __CPU_TO_LE_32(new_len);
638 /* Ensure the modified partition always has a new generation count. */
639 header->generation = __CPU_TO_LE_32(
640 __LE_TO_CPU_32(header->generation) + 1);
641
642 trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
643 new_len - sizeof (*trailer) - sizeof (uint32_t));
644 trailer->generation = header->generation;
645 trailer->checksum = __CPU_TO_LE_32(
646 __LE_TO_CPU_32(trailer->checksum) -
647 checksum_tlv_partition(&partition));
648
649 return (0);
650
651 fail2:
652 EFSYS_PROBE(fail2);
653 fail1:
654 EFSYS_PROBE1(fail1, efx_rc_t, rc);
655
656 return (rc);
657 }
658
659 /* Validate buffer contents (before writing to flash) */
660 __checkReturn efx_rc_t
661 ef10_nvram_buffer_validate(
662 __in uint32_t partn,
663 __in_bcount(partn_size) caddr_t partn_data,
664 __in size_t partn_size)
665 {
666 tlv_cursor_t cursor;
667 struct tlv_partition_header *header;
668 struct tlv_partition_trailer *trailer;
669 size_t total_length;
670 uint32_t cksum;
671 int pos;
672 efx_rc_t rc;
673
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 = (struct tlv_partition_header *)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 header matches partn */
701 if (__LE_TO_CPU_16(header->type_id) != partn) {
702 rc = EINVAL;
703 goto fail5;
704 }
705
706 /* Check partition ends with PARTITION_TRAILER and END tags */
707 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
708 rc = EINVAL;
709 goto fail6;
710 }
711 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
712
713 if ((rc = tlv_advance(&cursor)) != 0) {
714 rc = EINVAL;
715 goto fail7;
716 }
717 if (tlv_tag(&cursor) != TLV_TAG_END) {
718 rc = EINVAL;
719 goto fail8;
720 }
721
722 /* Check generation counts are consistent */
723 if (trailer->generation != header->generation) {
724 rc = EINVAL;
725 goto fail9;
726 }
727
728 /* Verify partition checksum */
729 cksum = 0;
730 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
731 cksum += *((uint32_t *)(partn_data + pos));
732 }
733 if (cksum != 0) {
734 rc = EINVAL;
735 goto fail10;
736 }
737
738 return (0);
739
740 fail10:
741 EFSYS_PROBE(fail10);
742 fail9:
743 EFSYS_PROBE(fail9);
744 fail8:
745 EFSYS_PROBE(fail8);
746 fail7:
747 EFSYS_PROBE(fail7);
748 fail6:
749 EFSYS_PROBE(fail6);
750 fail5:
751 EFSYS_PROBE(fail5);
752 fail4:
753 EFSYS_PROBE(fail4);
754 fail3:
755 EFSYS_PROBE(fail3);
756 fail2:
757 EFSYS_PROBE(fail2);
758 fail1:
759 EFSYS_PROBE1(fail1, efx_rc_t, rc);
760
761 return (rc);
762 }
763
764 void
765 ef10_nvram_buffer_init(
766 __out_bcount(buffer_size)
767 caddr_t bufferp,
768 __in size_t buffer_size)
769 {
770 uint32_t *buf = (uint32_t *)bufferp;
771
772 memset(buf, 0xff, buffer_size);
773
774 tlv_init_block(buf);
775 }
776
777 __checkReturn efx_rc_t
778 ef10_nvram_buffer_create(
779 __in uint32_t partn_type,
780 __out_bcount(partn_size)
781 caddr_t partn_data,
782 __in size_t partn_size)
783 {
784 uint32_t *buf = (uint32_t *)partn_data;
785 efx_rc_t rc;
786 tlv_cursor_t cursor;
787 struct tlv_partition_header header;
788 struct tlv_partition_trailer trailer;
789
790 unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
791 sizeof (struct tlv_partition_trailer);
792 if (partn_size < min_buf_size) {
793 rc = EINVAL;
794 goto fail1;
795 }
796
797 ef10_nvram_buffer_init(partn_data, partn_size);
798
799 if ((rc = tlv_init_cursor(&cursor, buf,
800 (uint32_t *)((uint8_t *)buf + partn_size),
801 buf)) != 0) {
802 goto fail2;
803 }
804
805 header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
806 header.length = __CPU_TO_LE_32(sizeof (header) - 8);
807 header.type_id = __CPU_TO_LE_16(partn_type);
808 header.preset = 0;
809 header.generation = __CPU_TO_LE_32(1);
810 header.total_length = 0; /* This will be fixed below. */
811 if ((rc = tlv_insert(
812 &cursor, TLV_TAG_PARTITION_HEADER,
813 (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
814 goto fail3;
815 if ((rc = tlv_advance(&cursor)) != 0)
816 goto fail4;
817
818 trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
819 trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
820 trailer.generation = header.generation;
821 trailer.checksum = 0; /* This will be fixed below. */
822 if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
823 (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
824 goto fail5;
825
826 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
827 goto fail6;
828
829 /* Check that the partition is valid. */
830 if ((rc = ef10_nvram_buffer_validate(partn_type,
831 partn_data, partn_size)) != 0)
832 goto fail7;
833
834 return (0);
835
836 fail7:
837 EFSYS_PROBE(fail7);
838 fail6:
839 EFSYS_PROBE(fail6);
840 fail5:
841 EFSYS_PROBE(fail5);
842 fail4:
843 EFSYS_PROBE(fail4);
844 fail3:
845 EFSYS_PROBE(fail3);
846 fail2:
847 EFSYS_PROBE(fail2);
848 fail1:
849 EFSYS_PROBE1(fail1, efx_rc_t, rc);
850
851 return (rc);
852 }
853
854 static uint32_t
855 byte_offset(
856 __in uint32_t *position,
857 __in uint32_t *base)
858 {
859 return (uint32_t)((uint8_t *)position - (uint8_t *)base);
860 }
861
862 __checkReturn efx_rc_t
863 ef10_nvram_buffer_find_item_start(
864 __in_bcount(buffer_size)
865 caddr_t bufferp,
866 __in size_t buffer_size,
867 __out uint32_t *startp)
868 {
869 /* Read past partition header to find start address of the first key */
870 tlv_cursor_t cursor;
871 efx_rc_t rc;
872
873 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
874 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
875 buffer_size)) != 0) {
876 rc = EFAULT;
877 goto fail1;
878 }
879 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
880 rc = EINVAL;
881 goto fail2;
882 }
883
884 if ((rc = tlv_advance(&cursor)) != 0) {
885 rc = EINVAL;
886 goto fail3;
887 }
888 *startp = byte_offset(cursor.current, cursor.block);
889
890 if ((rc = tlv_require_end(&cursor)) != 0)
891 goto fail4;
892
893 return (0);
894
895 fail4:
896 EFSYS_PROBE(fail4);
897 fail3:
898 EFSYS_PROBE(fail3);
899 fail2:
900 EFSYS_PROBE(fail2);
901 fail1:
902 EFSYS_PROBE1(fail1, efx_rc_t, rc);
903
904 return (rc);
905 }
906
907 __checkReturn efx_rc_t
908 ef10_nvram_buffer_find_end(
909 __in_bcount(buffer_size)
910 caddr_t bufferp,
911 __in size_t buffer_size,
912 __in uint32_t offset,
913 __out uint32_t *endp)
914 {
915 /* Read to end of partition */
916 tlv_cursor_t cursor;
917 efx_rc_t rc;
918 uint32_t *segment_used;
919
920 _NOTE(ARGUNUSED(offset))
921
922 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
923 buffer_size)) != 0) {
924 rc = EFAULT;
925 goto fail1;
926 }
927
928 segment_used = cursor.block;
929
930 /*
931 * Go through each segment and check that it has an end tag. If there
932 * is no end tag then the previous segment was the last valid one,
933 * so return the used space including that end tag.
934 */
935 while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
936 if (tlv_require_end(&cursor) != 0) {
937 if (segment_used == cursor.block) {
938 /*
939 * First segment is corrupt, so there is
940 * no valid data in partition.
941 */
942 rc = EINVAL;
943 goto fail2;
944 }
945 break;
946 }
947 segment_used = cursor.end + 1;
948
949 cursor.current = segment_used;
950 }
951 /* Return space used (including the END tag) */
952 *endp = (segment_used - cursor.block) * sizeof (uint32_t);
953
954 return (0);
955
956 fail2:
957 EFSYS_PROBE(fail2);
958 fail1:
959 EFSYS_PROBE1(fail1, efx_rc_t, rc);
960
961 return (rc);
962 }
963
964 __checkReturn __success(return != B_FALSE) boolean_t
965 ef10_nvram_buffer_find_item(
966 __in_bcount(buffer_size)
967 caddr_t bufferp,
968 __in size_t buffer_size,
969 __in uint32_t offset,
970 __out uint32_t *startp,
971 __out uint32_t *lengthp)
972 {
973 /* Find TLV at offset and return key start and length */
974 tlv_cursor_t cursor;
975 uint8_t *key;
976 uint32_t tag;
977
978 if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
979 buffer_size, offset) != 0) {
980 return (B_FALSE);
981 }
982
983 while ((key = tlv_item(&cursor)) != NULL) {
984 tag = tlv_tag(&cursor);
985 if (tag == TLV_TAG_PARTITION_HEADER ||
986 tag == TLV_TAG_PARTITION_TRAILER) {
987 if (tlv_advance(&cursor) != 0) {
988 break;
989 }
990 continue;
991 }
992 *startp = byte_offset(cursor.current, cursor.block);
993 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
994 cursor.current);
995 return (B_TRUE);
996 }
997
998 return (B_FALSE);
999 }
1000
1001 __checkReturn efx_rc_t
1002 ef10_nvram_buffer_peek_item(
1003 __in_bcount(buffer_size)
1004 caddr_t bufferp,
1005 __in size_t buffer_size,
1006 __in uint32_t offset,
1007 __out uint32_t *tagp,
1008 __out uint32_t *lengthp,
1009 __out uint32_t *value_offsetp)
1010 {
1011 efx_rc_t rc;
1012 tlv_cursor_t cursor;
1013 uint32_t tag;
1014
1015 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1016 buffer_size, offset)) != 0) {
1017 goto fail1;
1018 }
1019
1020 tag = tlv_tag(&cursor);
1021 *tagp = tag;
1022 if (tag == TLV_TAG_END) {
1023 /*
1024 * To allow stepping over the END tag, report the full tag
1025 * length and a zero length value.
1026 */
1027 *lengthp = sizeof (tag);
1028 *value_offsetp = sizeof (tag);
1029 } else {
1030 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1031 cursor.current);
1032 *value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1033 cursor.current);
1034 }
1035 return (0);
1036
1037 fail1:
1038 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1039
1040 return (rc);
1041 }
1042
1043 __checkReturn efx_rc_t
1044 ef10_nvram_buffer_get_item(
1045 __in_bcount(buffer_size)
1046 caddr_t bufferp,
1047 __in size_t buffer_size,
1048 __in uint32_t offset,
1049 __in uint32_t length,
1050 __out uint32_t *tagp,
1051 __out_bcount_part(value_max_size, *lengthp)
1052 caddr_t valuep,
1053 __in size_t value_max_size,
1054 __out uint32_t *lengthp)
1055 {
1056 efx_rc_t rc;
1057 tlv_cursor_t cursor;
1058 uint32_t value_length;
1059
1060 if (buffer_size < (offset + length)) {
1061 rc = ENOSPC;
1062 goto fail1;
1063 }
1064
1065 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1066 buffer_size, offset)) != 0) {
1067 goto fail2;
1068 }
1069
1070 value_length = tlv_length(&cursor);
1071 if (value_max_size < value_length) {
1072 rc = ENOSPC;
1073 goto fail3;
1074 }
1075 memcpy(valuep, tlv_value(&cursor), value_length);
1076
1077 *tagp = tlv_tag(&cursor);
1078 *lengthp = value_length;
1079
1080 return (0);
1081
1082 fail3:
1083 EFSYS_PROBE(fail3);
1084 fail2:
1085 EFSYS_PROBE(fail2);
1086 fail1:
1087 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1088
1089 return (rc);
1090 }
1091
1092 __checkReturn efx_rc_t
1093 ef10_nvram_buffer_insert_item(
1094 __in_bcount(buffer_size)
1095 caddr_t bufferp,
1096 __in size_t buffer_size,
1097 __in uint32_t offset,
1098 __in uint32_t tag,
1099 __in_bcount(length) caddr_t valuep,
1100 __in uint32_t length,
1101 __out uint32_t *lengthp)
1102 {
1103 efx_rc_t rc;
1104 tlv_cursor_t cursor;
1105
1106 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1107 buffer_size, offset)) != 0) {
1108 goto fail1;
1109 }
1110
1111 rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1112
1113 if (rc != 0)
1114 goto fail2;
1115
1116 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1117 cursor.current);
1118
1119 return (0);
1120
1121 fail2:
1122 EFSYS_PROBE(fail2);
1123 fail1:
1124 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1125
1126 return (rc);
1127 }
1128
1129 __checkReturn efx_rc_t
1130 ef10_nvram_buffer_modify_item(
1131 __in_bcount(buffer_size)
1132 caddr_t bufferp,
1133 __in size_t buffer_size,
1134 __in uint32_t offset,
1135 __in uint32_t tag,
1136 __in_bcount(length) caddr_t valuep,
1137 __in uint32_t length,
1138 __out uint32_t *lengthp)
1139 {
1140 efx_rc_t rc;
1141 tlv_cursor_t cursor;
1142
1143 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1144 buffer_size, offset)) != 0) {
1145 goto fail1;
1146 }
1147
1148 rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
1149
1150 if (rc != 0) {
1151 goto fail2;
1152 }
1153
1154 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1155 cursor.current);
1156
1157 return (0);
1158
1159 fail2:
1160 EFSYS_PROBE(fail2);
1161 fail1:
1162 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1163
1164 return (rc);
1165 }
1166
1167 __checkReturn efx_rc_t
1168 ef10_nvram_buffer_delete_item(
1169 __in_bcount(buffer_size)
1170 caddr_t bufferp,
1171 __in size_t buffer_size,
1172 __in uint32_t offset,
1173 __in uint32_t length,
1174 __in uint32_t end)
1175 {
1176 efx_rc_t rc;
1177 tlv_cursor_t cursor;
1178
1179 _NOTE(ARGUNUSED(length, end))
1180
1181 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1182 buffer_size, offset)) != 0) {
1183 goto fail1;
1184 }
1185
1186 if ((rc = tlv_delete(&cursor)) != 0)
1187 goto fail2;
1188
1189 return (0);
1190
1191 fail2:
1192 EFSYS_PROBE(fail2);
1193 fail1:
1194 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1195
1196 return (rc);
1197 }
1198
1199 __checkReturn efx_rc_t
1200 ef10_nvram_buffer_finish(
1201 __in_bcount(buffer_size)
1202 caddr_t bufferp,
1203 __in size_t buffer_size)
1204 {
1205 efx_rc_t rc;
1206 tlv_cursor_t cursor;
1207
1208 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1209 buffer_size)) != 0) {
1210 rc = EFAULT;
1211 goto fail1;
1212 }
1213
1214 if ((rc = tlv_require_end(&cursor)) != 0)
1215 goto fail2;
1216
1217 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1218 goto fail3;
1219
1220 return (0);
1221
1222 fail3:
1223 EFSYS_PROBE(fail3);
1224 fail2:
1225 EFSYS_PROBE(fail2);
1226 fail1:
1227 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1228
1229 return (rc);
1230 }
1231
1232 /*
1233 * Read and validate a segment from a partition. A segment is a complete
1234 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1235 * be multiple segments in a partition, so seg_offset allows segments
1236 * beyond the first to be read.
1237 */
1238 static __checkReturn efx_rc_t
1239 ef10_nvram_read_tlv_segment(
1240 __in efx_nic_t *enp,
1241 __in uint32_t partn,
1242 __in size_t seg_offset,
1243 __in_bcount(max_seg_size) caddr_t seg_data,
1244 __in size_t max_seg_size)
1245 {
1246 tlv_cursor_t cursor;
1247 struct tlv_partition_header *header;
1248 struct tlv_partition_trailer *trailer;
1249 size_t total_length;
1250 uint32_t cksum;
1251 int pos;
1252 efx_rc_t rc;
1253
1254 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1255
1256 if ((seg_data == NULL) || (max_seg_size == 0)) {
1257 rc = EINVAL;
1258 goto fail1;
1259 }
1260
1261 /* Read initial chunk of the segment, starting at offset */
1262 if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1263 EF10_NVRAM_CHUNK,
1264 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1265 goto fail2;
1266 }
1267
1268 /* A PARTITION_HEADER tag must be the first item at the given offset */
1269 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1270 max_seg_size)) != 0) {
1271 rc = EFAULT;
1272 goto fail3;
1273 }
1274 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1275 rc = EINVAL;
1276 goto fail4;
1277 }
1278 header = (struct tlv_partition_header *)tlv_item(&cursor);
1279
1280 /* Check TLV segment length (includes the END tag) */
1281 total_length = __LE_TO_CPU_32(header->total_length);
1282 if (total_length > max_seg_size) {
1283 rc = EFBIG;
1284 goto fail5;
1285 }
1286
1287 /* Read the remaining segment content */
1288 if (total_length > EF10_NVRAM_CHUNK) {
1289 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1290 seg_offset + EF10_NVRAM_CHUNK,
1291 seg_data + EF10_NVRAM_CHUNK,
1292 total_length - EF10_NVRAM_CHUNK,
1293 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1294 goto fail6;
1295 }
1296
1297 /* Check segment ends with PARTITION_TRAILER and END tags */
1298 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1299 rc = EINVAL;
1300 goto fail7;
1301 }
1302 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1303
1304 if ((rc = tlv_advance(&cursor)) != 0) {
1305 rc = EINVAL;
1306 goto fail8;
1307 }
1308 if (tlv_tag(&cursor) != TLV_TAG_END) {
1309 rc = EINVAL;
1310 goto fail9;
1311 }
1312
1313 /* Check data read from segment is consistent */
1314 if (trailer->generation != header->generation) {
1315 /*
1316 * The partition data may have been modified between successive
1317 * MCDI NVRAM_READ requests by the MC or another PCI function.
1318 *
1319 * The caller must retry to obtain consistent partition data.
1320 */
1321 rc = EAGAIN;
1322 goto fail10;
1323 }
1324
1325 /* Verify segment checksum */
1326 cksum = 0;
1327 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1328 cksum += *((uint32_t *)(seg_data + pos));
1329 }
1330 if (cksum != 0) {
1331 rc = EINVAL;
1332 goto fail11;
1333 }
1334
1335 return (0);
1336
1337 fail11:
1338 EFSYS_PROBE(fail11);
1339 fail10:
1340 EFSYS_PROBE(fail10);
1341 fail9:
1342 EFSYS_PROBE(fail9);
1343 fail8:
1344 EFSYS_PROBE(fail8);
1345 fail7:
1346 EFSYS_PROBE(fail7);
1347 fail6:
1348 EFSYS_PROBE(fail6);
1349 fail5:
1350 EFSYS_PROBE(fail5);
1351 fail4:
1352 EFSYS_PROBE(fail4);
1353 fail3:
1354 EFSYS_PROBE(fail3);
1355 fail2:
1356 EFSYS_PROBE(fail2);
1357 fail1:
1358 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1359
1360 return (rc);
1361 }
1362
1363 /*
1364 * Read a single TLV item from a host memory
1365 * buffer containing a TLV formatted segment.
1366 */
1367 __checkReturn efx_rc_t
1368 ef10_nvram_buf_read_tlv(
1369 __in efx_nic_t *enp,
1370 __in_bcount(max_seg_size) caddr_t seg_data,
1371 __in size_t max_seg_size,
1372 __in uint32_t tag,
1373 __deref_out_bcount_opt(*sizep) caddr_t *datap,
1374 __out size_t *sizep)
1375 {
1376 tlv_cursor_t cursor;
1377 caddr_t data;
1378 size_t length;
1379 caddr_t value;
1380 efx_rc_t rc;
1381
1382 _NOTE(ARGUNUSED(enp))
1383
1384 if ((seg_data == NULL) || (max_seg_size == 0)) {
1385 rc = EINVAL;
1386 goto fail1;
1387 }
1388
1389 /* Find requested TLV tag in segment data */
1390 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1391 max_seg_size)) != 0) {
1392 rc = EFAULT;
1393 goto fail2;
1394 }
1395 if ((rc = tlv_find(&cursor, tag)) != 0) {
1396 rc = ENOENT;
1397 goto fail3;
1398 }
1399 value = (caddr_t)tlv_value(&cursor);
1400 length = tlv_length(&cursor);
1401
1402 if (length == 0)
1403 data = NULL;
1404 else {
1405 /* Copy out data from TLV item */
1406 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1407 if (data == NULL) {
1408 rc = ENOMEM;
1409 goto fail4;
1410 }
1411 memcpy(data, value, length);
1412 }
1413
1414 *datap = data;
1415 *sizep = length;
1416
1417 return (0);
1418
1419 fail4:
1420 EFSYS_PROBE(fail4);
1421 fail3:
1422 EFSYS_PROBE(fail3);
1423 fail2:
1424 EFSYS_PROBE(fail2);
1425 fail1:
1426 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1427
1428 return (rc);
1429 }
1430
1431 /* Read a single TLV item from the first segment in a TLV formatted partition */
1432 __checkReturn efx_rc_t
1433 ef10_nvram_partn_read_tlv(
1434 __in efx_nic_t *enp,
1435 __in uint32_t partn,
1436 __in uint32_t tag,
1437 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
1438 __out size_t *seg_sizep)
1439 {
1440 caddr_t seg_data = NULL;
1441 size_t partn_size = 0;
1442 size_t length;
1443 caddr_t data;
1444 int retry;
1445 efx_rc_t rc;
1446
1447 /* Allocate sufficient memory for the entire partition */
1448 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1449 goto fail1;
1450
1451 if (partn_size == 0) {
1452 rc = ENOENT;
1453 goto fail2;
1454 }
1455
1456 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1457 if (seg_data == NULL) {
1458 rc = ENOMEM;
1459 goto fail3;
1460 }
1461
1462 /*
1463 * Read the first segment in a TLV partition. Retry until consistent
1464 * segment contents are returned. Inconsistent data may be read if:
1465 * a) the segment contents are invalid
1466 * b) the MC has rebooted while we were reading the partition
1467 * c) the partition has been modified while we were reading it
1468 * Limit retry attempts to ensure forward progress.
1469 */
1470 retry = 10;
1471 do {
1472 if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1473 seg_data, partn_size)) != 0)
1474 --retry;
1475 } while ((rc == EAGAIN) && (retry > 0));
1476
1477 if (rc != 0) {
1478 /* Failed to obtain consistent segment data */
1479 if (rc == EAGAIN)
1480 rc = EIO;
1481
1482 goto fail4;
1483 }
1484
1485 if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1486 tag, &data, &length)) != 0)
1487 goto fail5;
1488
1489 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1490
1491 *seg_datap = data;
1492 *seg_sizep = length;
1493
1494 return (0);
1495
1496 fail5:
1497 EFSYS_PROBE(fail5);
1498 fail4:
1499 EFSYS_PROBE(fail4);
1500
1501 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
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 /* Compute the size of a segment. */
1513 static __checkReturn efx_rc_t
1514 ef10_nvram_buf_segment_size(
1515 __in caddr_t seg_data,
1516 __in size_t max_seg_size,
1517 __out size_t *seg_sizep)
1518 {
1519 efx_rc_t rc;
1520 tlv_cursor_t cursor;
1521 struct tlv_partition_header *header;
1522 uint32_t cksum;
1523 int pos;
1524 uint32_t *end_tag_position;
1525 uint32_t segment_length;
1526
1527 /* A PARTITION_HEADER tag must be the first item at the given offset */
1528 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1529 max_seg_size)) != 0) {
1530 rc = EFAULT;
1531 goto fail1;
1532 }
1533 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1534 rc = EINVAL;
1535 goto fail2;
1536 }
1537 header = (struct tlv_partition_header *)tlv_item(&cursor);
1538
1539 /* Check TLV segment length (includes the END tag) */
1540 *seg_sizep = __LE_TO_CPU_32(header->total_length);
1541 if (*seg_sizep > max_seg_size) {
1542 rc = EFBIG;
1543 goto fail3;
1544 }
1545
1546 /* Check segment ends with PARTITION_TRAILER and END tags */
1547 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1548 rc = EINVAL;
1549 goto fail4;
1550 }
1551
1552 if ((rc = tlv_advance(&cursor)) != 0) {
1553 rc = EINVAL;
1554 goto fail5;
1555 }
1556 if (tlv_tag(&cursor) != TLV_TAG_END) {
1557 rc = EINVAL;
1558 goto fail6;
1559 }
1560 end_tag_position = cursor.current;
1561
1562 /* Verify segment checksum */
1563 cksum = 0;
1564 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1565 cksum += *((uint32_t *)(seg_data + pos));
1566 }
1567 if (cksum != 0) {
1568 rc = EINVAL;
1569 goto fail7;
1570 }
1571
1572 /*
1573 * Calculate total length from HEADER to END tags and compare to
1574 * max_seg_size and the total_length field in the HEADER tag.
1575 */
1576 segment_length = tlv_block_length_used(&cursor);
1577
1578 if (segment_length > max_seg_size) {
1579 rc = EINVAL;
1580 goto fail8;
1581 }
1582
1583 if (segment_length != *seg_sizep) {
1584 rc = EINVAL;
1585 goto fail9;
1586 }
1587
1588 /* Skip over the first HEADER tag. */
1589 rc = tlv_rewind(&cursor);
1590 rc = tlv_advance(&cursor);
1591
1592 while (rc == 0) {
1593 if (tlv_tag(&cursor) == TLV_TAG_END) {
1594 /* Check that the END tag is the one found earlier. */
1595 if (cursor.current != end_tag_position)
1596 goto fail10;
1597 break;
1598 }
1599 /* Check for duplicate HEADER tags before the END tag. */
1600 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1601 rc = EINVAL;
1602 goto fail11;
1603 }
1604
1605 rc = tlv_advance(&cursor);
1606 }
1607 if (rc != 0)
1608 goto fail12;
1609
1610 return (0);
1611
1612 fail12:
1613 EFSYS_PROBE(fail12);
1614 fail11:
1615 EFSYS_PROBE(fail11);
1616 fail10:
1617 EFSYS_PROBE(fail10);
1618 fail9:
1619 EFSYS_PROBE(fail9);
1620 fail8:
1621 EFSYS_PROBE(fail8);
1622 fail7:
1623 EFSYS_PROBE(fail7);
1624 fail6:
1625 EFSYS_PROBE(fail6);
1626 fail5:
1627 EFSYS_PROBE(fail5);
1628 fail4:
1629 EFSYS_PROBE(fail4);
1630 fail3:
1631 EFSYS_PROBE(fail3);
1632 fail2:
1633 EFSYS_PROBE(fail2);
1634 fail1:
1635 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1636
1637 return (rc);
1638 }
1639
1640 /*
1641 * Add or update a single TLV item in a host memory buffer containing a TLV
1642 * formatted segment. Historically partitions consisted of only one segment.
1643 */
1644 __checkReturn efx_rc_t
1645 ef10_nvram_buf_write_tlv(
1646 __inout_bcount(max_seg_size) caddr_t seg_data,
1647 __in size_t max_seg_size,
1648 __in uint32_t tag,
1649 __in_bcount(tag_size) caddr_t tag_data,
1650 __in size_t tag_size,
1651 __out size_t *total_lengthp)
1652 {
1653 tlv_cursor_t cursor;
1654 struct tlv_partition_header *header;
1655 struct tlv_partition_trailer *trailer;
1656 uint32_t generation;
1657 uint32_t cksum;
1658 int pos;
1659 efx_rc_t rc;
1660
1661 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1662 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1663 max_seg_size)) != 0) {
1664 rc = EFAULT;
1665 goto fail1;
1666 }
1667 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1668 rc = EINVAL;
1669 goto fail2;
1670 }
1671 header = (struct tlv_partition_header *)tlv_item(&cursor);
1672
1673 /* Update the TLV chain to contain the new data */
1674 if ((rc = tlv_find(&cursor, tag)) == 0) {
1675 /* Modify existing TLV item */
1676 if ((rc = tlv_modify(&cursor, tag,
1677 (uint8_t *)tag_data, tag_size)) != 0)
1678 goto fail3;
1679 } else {
1680 /* Insert a new TLV item before the PARTITION_TRAILER */
1681 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1682 if (rc != 0) {
1683 rc = EINVAL;
1684 goto fail4;
1685 }
1686 if ((rc = tlv_insert(&cursor, tag,
1687 (uint8_t *)tag_data, tag_size)) != 0) {
1688 rc = EINVAL;
1689 goto fail5;
1690 }
1691 }
1692
1693 /* Find the trailer tag */
1694 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1695 rc = EINVAL;
1696 goto fail6;
1697 }
1698 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1699
1700 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1701 *total_lengthp = tlv_block_length_used(&cursor);
1702 if (*total_lengthp > max_seg_size) {
1703 rc = ENOSPC;
1704 goto fail7;
1705 }
1706 generation = __LE_TO_CPU_32(header->generation) + 1;
1707
1708 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1709 header->generation = __CPU_TO_LE_32(generation);
1710 trailer->generation = __CPU_TO_LE_32(generation);
1711
1712 /* Recompute PARTITION_TRAILER checksum */
1713 trailer->checksum = 0;
1714 cksum = 0;
1715 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1716 cksum += *((uint32_t *)(seg_data + pos));
1717 }
1718 trailer->checksum = ~cksum + 1;
1719
1720 return (0);
1721
1722 fail7:
1723 EFSYS_PROBE(fail7);
1724 fail6:
1725 EFSYS_PROBE(fail6);
1726 fail5:
1727 EFSYS_PROBE(fail5);
1728 fail4:
1729 EFSYS_PROBE(fail4);
1730 fail3:
1731 EFSYS_PROBE(fail3);
1732 fail2:
1733 EFSYS_PROBE(fail2);
1734 fail1:
1735 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1736
1737 return (rc);
1738 }
1739
1740 /*
1741 * Add or update a single TLV item in the first segment of a TLV formatted
1742 * dynamic config partition. The first segment is the current active
1743 * configuration.
1744 */
1745 __checkReturn efx_rc_t
1746 ef10_nvram_partn_write_tlv(
1747 __in efx_nic_t *enp,
1748 __in uint32_t partn,
1749 __in uint32_t tag,
1750 __in_bcount(size) caddr_t data,
1751 __in size_t size)
1752 {
1753 return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1754 size, B_FALSE);
1755 }
1756
1757 /*
1758 * Read a segment from nvram at the given offset into a buffer (segment_data)
1759 * and optionally write a new tag to it.
1760 */
1761 static __checkReturn efx_rc_t
1762 ef10_nvram_segment_write_tlv(
1763 __in efx_nic_t *enp,
1764 __in uint32_t partn,
1765 __in uint32_t tag,
1766 __in_bcount(size) caddr_t data,
1767 __in size_t size,
1768 __inout caddr_t *seg_datap,
1769 __inout size_t *partn_offsetp,
1770 __inout size_t *src_remain_lenp,
1771 __inout size_t *dest_remain_lenp,
1772 __in boolean_t write)
1773 {
1774 efx_rc_t rc;
1775 efx_rc_t status;
1776 size_t original_segment_size;
1777 size_t modified_segment_size;
1778
1779 /*
1780 * Read the segment from NVRAM into the segment_data buffer and validate
1781 * it, returning if it does not validate. This is not a failure unless
1782 * this is the first segment in a partition. In this case the caller
1783 * must propagate the error.
1784 */
1785 status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1786 *seg_datap, *src_remain_lenp);
1787 if (status != 0) {
1788 rc = EINVAL;
1789 goto fail1;
1790 }
1791
1792 status = ef10_nvram_buf_segment_size(*seg_datap,
1793 *src_remain_lenp, &original_segment_size);
1794 if (status != 0) {
1795 rc = EINVAL;
1796 goto fail2;
1797 }
1798
1799 if (write) {
1800 /* Update the contents of the segment in the buffer */
1801 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1802 *dest_remain_lenp, tag, data, size,
1803 &modified_segment_size)) != 0) {
1804 goto fail3;
1805 }
1806 *dest_remain_lenp -= modified_segment_size;
1807 *seg_datap += modified_segment_size;
1808 } else {
1809 /*
1810 * We won't modify this segment, but still need to update the
1811 * remaining lengths and pointers.
1812 */
1813 *dest_remain_lenp -= original_segment_size;
1814 *seg_datap += original_segment_size;
1815 }
1816
1817 *partn_offsetp += original_segment_size;
1818 *src_remain_lenp -= original_segment_size;
1819
1820 return (0);
1821
1822 fail3:
1823 EFSYS_PROBE(fail3);
1824 fail2:
1825 EFSYS_PROBE(fail2);
1826 fail1:
1827 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1828
1829 return (rc);
1830 }
1831
1832 /*
1833 * Add or update a single TLV item in either the first segment or in all
1834 * segments in a TLV formatted dynamic config partition. Dynamic config
1835 * partitions on boards that support RFID are divided into a number of segments,
1836 * each formatted like a partition, with header, trailer and end tags. The first
1837 * segment is the current active configuration.
1838 *
1839 * The segments are initialised by manftest and each contain a different
1840 * configuration e.g. firmware variant. The firmware can be instructed
1841 * via RFID to copy a segment to replace the first segment, hence changing the
1842 * active configuration. This allows ops to change the configuration of a board
1843 * prior to shipment using RFID.
1844 *
1845 * Changes to the dynamic config may need to be written to all segments (e.g.
1846 * firmware versions) or just the first segment (changes to the active
1847 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1848 * If only the first segment is written the code still needs to be aware of the
1849 * possible presence of subsequent segments as writing to a segment may cause
1850 * its size to increase, which would overwrite the subsequent segments and
1851 * invalidate them.
1852 */
1853 __checkReturn efx_rc_t
1854 ef10_nvram_partn_write_segment_tlv(
1855 __in efx_nic_t *enp,
1856 __in uint32_t partn,
1857 __in uint32_t tag,
1858 __in_bcount(size) caddr_t data,
1859 __in size_t size,
1860 __in boolean_t all_segments)
1861 {
1862 size_t partn_size = 0;
1863 caddr_t partn_data;
1864 size_t total_length = 0;
1865 efx_rc_t rc;
1866 size_t current_offset = 0;
1867 size_t remaining_original_length;
1868 size_t remaining_modified_length;
1869 caddr_t segment_data;
1870
1871 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1872
1873 /* Allocate sufficient memory for the entire partition */
1874 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1875 goto fail1;
1876
1877 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1878 if (partn_data == NULL) {
1879 rc = ENOMEM;
1880 goto fail2;
1881 }
1882
1883 remaining_original_length = partn_size;
1884 remaining_modified_length = partn_size;
1885 segment_data = partn_data;
1886
1887 /* Lock the partition */
1888 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1889 goto fail3;
1890
1891 /* Iterate over each (potential) segment to update it. */
1892 do {
1893 boolean_t write = all_segments || current_offset == 0;
1894
1895 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1896 &segment_data, ¤t_offset, &remaining_original_length,
1897 &remaining_modified_length, write);
1898 if (rc != 0) {
1899 if (current_offset == 0) {
1900 /*
1901 * If no data has been read then the first
1902 * segment is invalid, which is an error.
1903 */
1904 goto fail4;
1905 }
1906 break;
1907 }
1908 } while (current_offset < partn_size);
1909
1910 total_length = segment_data - partn_data;
1911
1912 /*
1913 * We've run out of space. This should actually be dealt with by
1914 * ef10_nvram_buf_write_tlv returning ENOSPC.
1915 */
1916 if (total_length > partn_size) {
1917 rc = ENOSPC;
1918 goto fail5;
1919 }
1920
1921 /* Erase the whole partition in NVRAM */
1922 if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1923 goto fail6;
1924
1925 /* Write new partition contents from the buffer to NVRAM */
1926 if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1927 total_length)) != 0)
1928 goto fail7;
1929
1930 /* Unlock the partition */
1931 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1932
1933 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1934
1935 return (0);
1936
1937 fail7:
1938 EFSYS_PROBE(fail7);
1939 fail6:
1940 EFSYS_PROBE(fail6);
1941 fail5:
1942 EFSYS_PROBE(fail5);
1943 fail4:
1944 EFSYS_PROBE(fail4);
1945
1946 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1947 fail3:
1948 EFSYS_PROBE(fail3);
1949
1950 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1951 fail2:
1952 EFSYS_PROBE(fail2);
1953 fail1:
1954 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1955
1956 return (rc);
1957 }
1958
1959 /*
1960 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1961 * not the data used by the segments in the partition.
1962 */
1963 __checkReturn efx_rc_t
1964 ef10_nvram_partn_size(
1965 __in efx_nic_t *enp,
1966 __in uint32_t partn,
1967 __out size_t *sizep)
1968 {
1969 efx_rc_t rc;
1970
1971 if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1972 NULL, NULL, NULL)) != 0)
1973 goto fail1;
1974
1975 return (0);
1976
1977 fail1:
1978 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1979
1980 return (rc);
1981 }
1982
1983 __checkReturn efx_rc_t
1984 ef10_nvram_partn_lock(
1985 __in efx_nic_t *enp,
1986 __in uint32_t partn)
1987 {
1988 efx_rc_t rc;
1989
1990 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1991 goto fail1;
1992
1993 return (0);
1994
1995 fail1:
1996 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1997
1998 return (rc);
1999 }
2000
2001 __checkReturn efx_rc_t
2002 ef10_nvram_partn_read_mode(
2003 __in efx_nic_t *enp,
2004 __in uint32_t partn,
2005 __in unsigned int offset,
2006 __out_bcount(size) caddr_t data,
2007 __in size_t size,
2008 __in uint32_t mode)
2009 {
2010 size_t chunk;
2011 efx_rc_t rc;
2012
2013 while (size > 0) {
2014 chunk = MIN(size, EF10_NVRAM_CHUNK);
2015
2016 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2017 data, chunk, mode)) != 0) {
2018 goto fail1;
2019 }
2020
2021 size -= chunk;
2022 data += chunk;
2023 offset += chunk;
2024 }
2025
2026 return (0);
2027
2028 fail1:
2029 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2030
2031 return (rc);
2032 }
2033
2034 __checkReturn efx_rc_t
2035 ef10_nvram_partn_read(
2036 __in efx_nic_t *enp,
2037 __in uint32_t partn,
2038 __in unsigned int offset,
2039 __out_bcount(size) caddr_t data,
2040 __in size_t size)
2041 {
2042 /*
2043 * An A/B partition has two data stores (current and backup).
2044 * Read requests which come in through the EFX API expect to read the
2045 * current, active store of an A/B partition. For non A/B partitions,
2046 * there is only a single store and so the mode param is ignored.
2047 */
2048 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2049 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2050 }
2051
2052 __checkReturn efx_rc_t
2053 ef10_nvram_partn_read_backup(
2054 __in efx_nic_t *enp,
2055 __in uint32_t partn,
2056 __in unsigned int offset,
2057 __out_bcount(size) caddr_t data,
2058 __in size_t size)
2059 {
2060 /*
2061 * An A/B partition has two data stores (current and backup).
2062 * Read the backup store of an A/B partition (i.e. the store currently
2063 * being written to if the partition is locked).
2064 *
2065 * This is needed when comparing the existing partition content to avoid
2066 * unnecessary writes, or to read back what has been written to check
2067 * that the writes have succeeded.
2068 */
2069 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2070 MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
2071 }
2072
2073 __checkReturn efx_rc_t
2074 ef10_nvram_partn_erase(
2075 __in efx_nic_t *enp,
2076 __in uint32_t partn,
2077 __in unsigned int offset,
2078 __in size_t size)
2079 {
2080 efx_rc_t rc;
2081 uint32_t erase_size;
2082
2083 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2084 &erase_size, NULL)) != 0)
2085 goto fail1;
2086
2087 if (erase_size == 0) {
2088 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
2089 goto fail2;
2090 } else {
2091 if (size % erase_size != 0) {
2092 rc = EINVAL;
2093 goto fail3;
2094 }
2095 while (size > 0) {
2096 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
2097 erase_size)) != 0)
2098 goto fail4;
2099 offset += erase_size;
2100 size -= erase_size;
2101 }
2102 }
2103
2104 return (0);
2105
2106 fail4:
2107 EFSYS_PROBE(fail4);
2108 fail3:
2109 EFSYS_PROBE(fail3);
2110 fail2:
2111 EFSYS_PROBE(fail2);
2112 fail1:
2113 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2114
2115 return (rc);
2116 }
2117
2118 __checkReturn efx_rc_t
2119 ef10_nvram_partn_write(
2120 __in efx_nic_t *enp,
2121 __in uint32_t partn,
2122 __in unsigned int offset,
2123 __in_bcount(size) caddr_t data,
2124 __in size_t size)
2125 {
2126 size_t chunk;
2127 uint32_t write_size;
2128 efx_rc_t rc;
2129
2130 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2131 NULL, &write_size)) != 0)
2132 goto fail1;
2133
2134 if (write_size != 0) {
2135 /*
2136 * Check that the size is a multiple of the write chunk size if
2137 * the write chunk size is available.
2138 */
2139 if (size % write_size != 0) {
2140 rc = EINVAL;
2141 goto fail2;
2142 }
2143 } else {
2144 write_size = EF10_NVRAM_CHUNK;
2145 }
2146
2147 while (size > 0) {
2148 chunk = MIN(size, write_size);
2149
2150 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2151 data, chunk)) != 0) {
2152 goto fail3;
2153 }
2154
2155 size -= chunk;
2156 data += chunk;
2157 offset += chunk;
2158 }
2159
2160 return (0);
2161
2162 fail3:
2163 EFSYS_PROBE(fail3);
2164 fail2:
2165 EFSYS_PROBE(fail2);
2166 fail1:
2167 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2168
2169 return (rc);
2170 }
2171
2172 __checkReturn efx_rc_t
2173 ef10_nvram_partn_unlock(
2174 __in efx_nic_t *enp,
2175 __in uint32_t partn,
2176 __out_opt uint32_t *verify_resultp)
2177 {
2178 boolean_t reboot = B_FALSE;
2179 efx_rc_t rc;
2180
2181 if (verify_resultp != NULL)
2182 *verify_resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2183
2184 rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, verify_resultp);
2185 if (rc != 0)
2186 goto fail1;
2187
2188 return (0);
2189
2190 fail1:
2191 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2192
2193 return (rc);
2194 }
2195
2196 __checkReturn efx_rc_t
2197 ef10_nvram_partn_set_version(
2198 __in efx_nic_t *enp,
2199 __in uint32_t partn,
2200 __in_ecount(4) uint16_t version[4])
2201 {
2202 struct tlv_partition_version partn_version;
2203 size_t size;
2204 efx_rc_t rc;
2205
2206 /* Add or modify partition version TLV item */
2207 partn_version.version_w = __CPU_TO_LE_16(version[0]);
2208 partn_version.version_x = __CPU_TO_LE_16(version[1]);
2209 partn_version.version_y = __CPU_TO_LE_16(version[2]);
2210 partn_version.version_z = __CPU_TO_LE_16(version[3]);
2211
2212 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2213
2214 /* Write the version number to all segments in the partition */
2215 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2216 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2217 TLV_TAG_PARTITION_VERSION(partn),
2218 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2219 goto fail1;
2220
2221 return (0);
2222
2223 fail1:
2224 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2225
2226 return (rc);
2227 }
2228
2229 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2230
2231 #if EFSYS_OPT_NVRAM
2232
2233 typedef struct ef10_parttbl_entry_s {
2234 unsigned int partn;
2235 unsigned int port_mask;
2236 efx_nvram_type_t nvtype;
2237 } ef10_parttbl_entry_t;
2238
2239 /* Port mask values */
2240 #define PORT_1 (1u << 1)
2241 #define PORT_2 (1u << 2)
2242 #define PORT_3 (1u << 3)
2243 #define PORT_4 (1u << 4)
2244 #define PORT_ALL (0xffffffffu)
2245
2246 #define PARTN_MAP_ENTRY(partn, port_mask, nvtype) \
2247 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
2248
2249 /* Translate EFX NVRAM types to firmware partition types */
2250 static ef10_parttbl_entry_t hunt_parttbl[] = {
2251 /* partn ports nvtype */
2252 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2253 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2254 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2255 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0, 1, BOOTROM_CFG),
2256 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1, 2, BOOTROM_CFG),
2257 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2, 3, BOOTROM_CFG),
2258 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3, 4, BOOTROM_CFG),
2259 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2260 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2261 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2262 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2263 };
2264
2265 static ef10_parttbl_entry_t medford_parttbl[] = {
2266 /* partn ports nvtype */
2267 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2268 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2269 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2270 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2271 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2272 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2273 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2274 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2275 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2276 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2277 };
2278
2279 static ef10_parttbl_entry_t medford2_parttbl[] = {
2280 /* partn ports nvtype */
2281 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2282 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2283 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2284 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2285 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2286 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2287 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2288 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2289 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2290 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2291 PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS, ALL, DYNCONFIG_DEFAULTS),
2292 PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS, ALL, ROMCONFIG_DEFAULTS),
2293 };
2294
2295 static __checkReturn efx_rc_t
2296 ef10_parttbl_get(
2297 __in efx_nic_t *enp,
2298 __out ef10_parttbl_entry_t **parttblp,
2299 __out size_t *parttbl_rowsp)
2300 {
2301 switch (enp->en_family) {
2302 case EFX_FAMILY_HUNTINGTON:
2303 *parttblp = hunt_parttbl;
2304 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2305 break;
2306
2307 case EFX_FAMILY_MEDFORD:
2308 *parttblp = medford_parttbl;
2309 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2310 break;
2311
2312 case EFX_FAMILY_MEDFORD2:
2313 *parttblp = medford2_parttbl;
2314 *parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
2315 break;
2316
2317 default:
2318 EFSYS_ASSERT(B_FALSE);
2319 return (EINVAL);
2320 }
2321 return (0);
2322 }
2323
2324 __checkReturn efx_rc_t
2325 ef10_nvram_type_to_partn(
2326 __in efx_nic_t *enp,
2327 __in efx_nvram_type_t type,
2328 __out uint32_t *partnp)
2329 {
2330 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2331 ef10_parttbl_entry_t *parttbl = NULL;
2332 size_t parttbl_rows = 0;
2333 unsigned int i;
2334
2335 EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
2336 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2337 EFSYS_ASSERT(partnp != NULL);
2338
2339 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2340 for (i = 0; i < parttbl_rows; i++) {
2341 ef10_parttbl_entry_t *entry = &parttbl[i];
2342
2343 if ((entry->nvtype == type) &&
2344 (entry->port_mask & (1u << emip->emi_port))) {
2345 *partnp = entry->partn;
2346 return (0);
2347 }
2348 }
2349 }
2350
2351 return (ENOTSUP);
2352 }
2353
2354 #if EFSYS_OPT_DIAG
2355
2356 static __checkReturn efx_rc_t
2357 ef10_nvram_partn_to_type(
2358 __in efx_nic_t *enp,
2359 __in uint32_t partn,
2360 __out efx_nvram_type_t *typep)
2361 {
2362 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2363 ef10_parttbl_entry_t *parttbl = NULL;
2364 size_t parttbl_rows = 0;
2365 unsigned int i;
2366
2367 EFSYS_ASSERT(typep != NULL);
2368
2369 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2370 for (i = 0; i < parttbl_rows; i++) {
2371 ef10_parttbl_entry_t *entry = &parttbl[i];
2372
2373 if ((entry->partn == partn) &&
2374 (entry->port_mask & (1u << emip->emi_port))) {
2375 *typep = entry->nvtype;
2376 return (0);
2377 }
2378 }
2379 }
2380
2381 return (ENOTSUP);
2382 }
2383
2384 __checkReturn efx_rc_t
2385 ef10_nvram_test(
2386 __in efx_nic_t *enp)
2387 {
2388 efx_nvram_type_t type;
2389 unsigned int npartns = 0;
2390 uint32_t *partns = NULL;
2391 size_t size;
2392 unsigned int i;
2393 efx_rc_t rc;
2394
2395 /* Read available partitions from NVRAM partition map */
2396 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2397 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2398 if (partns == NULL) {
2399 rc = ENOMEM;
2400 goto fail1;
2401 }
2402
2403 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2404 &npartns)) != 0) {
2405 goto fail2;
2406 }
2407
2408 for (i = 0; i < npartns; i++) {
2409 /* Check if the partition is supported for this port */
2410 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2411 continue;
2412
2413 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2414 goto fail3;
2415 }
2416
2417 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2418 return (0);
2419
2420 fail3:
2421 EFSYS_PROBE(fail3);
2422 fail2:
2423 EFSYS_PROBE(fail2);
2424 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2425 fail1:
2426 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2427 return (rc);
2428 }
2429
2430 #endif /* EFSYS_OPT_DIAG */
2431
2432 __checkReturn efx_rc_t
2433 ef10_nvram_partn_get_version(
2434 __in efx_nic_t *enp,
2435 __in uint32_t partn,
2436 __out uint32_t *subtypep,
2437 __out_ecount(4) uint16_t version[4])
2438 {
2439 efx_rc_t rc;
2440
2441 /* FIXME: get highest partn version from all ports */
2442 /* FIXME: return partn description if available */
2443
2444 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2445 version, NULL, 0)) != 0)
2446 goto fail1;
2447
2448 return (0);
2449
2450 fail1:
2451 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2452
2453 return (rc);
2454 }
2455
2456 __checkReturn efx_rc_t
2457 ef10_nvram_partn_rw_start(
2458 __in efx_nic_t *enp,
2459 __in uint32_t partn,
2460 __out size_t *chunk_sizep)
2461 {
2462 uint32_t write_size = 0;
2463 efx_rc_t rc;
2464
2465 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2466 NULL, &write_size)) != 0)
2467 goto fail1;
2468
2469 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2470 goto fail2;
2471
2472 if (chunk_sizep != NULL) {
2473 if (write_size == 0)
2474 *chunk_sizep = EF10_NVRAM_CHUNK;
2475 else
2476 *chunk_sizep = write_size;
2477 }
2478
2479 return (0);
2480
2481 fail2:
2482 EFSYS_PROBE(fail2);
2483 fail1:
2484 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2485
2486 return (rc);
2487 }
2488
2489 __checkReturn efx_rc_t
2490 ef10_nvram_partn_rw_finish(
2491 __in efx_nic_t *enp,
2492 __in uint32_t partn,
2493 __out_opt uint32_t *verify_resultp)
2494 {
2495 efx_rc_t rc;
2496
2497 if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
2498 goto fail1;
2499
2500 return (0);
2501
2502 fail1:
2503 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2504
2505 return (rc);
2506 }
2507
2508 #endif /* EFSYS_OPT_NVRAM */
2509
2510 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
Cache object: 52a96b755823207d9ca34cf33350ef3c
|