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