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