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