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