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