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