3f9d3750396b941d401c5561d235b8d5973f761b
[deb_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         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
676
677         if ((partn_data == NULL) || (partn_size == 0)) {
678                 rc = EINVAL;
679                 goto fail1;
680         }
681
682         /* The partition header must be the first item (at offset zero) */
683         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
684                     partn_size)) != 0) {
685                 rc = EFAULT;
686                 goto fail2;
687         }
688         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
689                 rc = EINVAL;
690                 goto fail3;
691         }
692         header = (struct tlv_partition_header *)tlv_item(&cursor);
693
694         /* Check TLV partition length (includes the END tag) */
695         total_length = __LE_TO_CPU_32(header->total_length);
696         if (total_length > partn_size) {
697                 rc = EFBIG;
698                 goto fail4;
699         }
700
701         /* Check partition ends with PARTITION_TRAILER and END tags */
702         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
703                 rc = EINVAL;
704                 goto fail5;
705         }
706         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
707
708         if ((rc = tlv_advance(&cursor)) != 0) {
709                 rc = EINVAL;
710                 goto fail6;
711         }
712         if (tlv_tag(&cursor) != TLV_TAG_END) {
713                 rc = EINVAL;
714                 goto fail7;
715         }
716
717         /* Check generation counts are consistent */
718         if (trailer->generation != header->generation) {
719                 rc = EINVAL;
720                 goto fail8;
721         }
722
723         /* Verify partition checksum */
724         cksum = 0;
725         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
726                 cksum += *((uint32_t *)(partn_data + pos));
727         }
728         if (cksum != 0) {
729                 rc = EINVAL;
730                 goto fail9;
731         }
732
733         return (0);
734
735 fail9:
736         EFSYS_PROBE(fail9);
737 fail8:
738         EFSYS_PROBE(fail8);
739 fail7:
740         EFSYS_PROBE(fail7);
741 fail6:
742         EFSYS_PROBE(fail6);
743 fail5:
744         EFSYS_PROBE(fail5);
745 fail4:
746         EFSYS_PROBE(fail4);
747 fail3:
748         EFSYS_PROBE(fail3);
749 fail2:
750         EFSYS_PROBE(fail2);
751 fail1:
752         EFSYS_PROBE1(fail1, efx_rc_t, rc);
753
754         return (rc);
755 }
756
757
758
759         __checkReturn           efx_rc_t
760 ef10_nvram_buffer_create(
761         __in                    efx_nic_t *enp,
762         __in                    uint16_t partn_type,
763         __in_bcount(partn_size) caddr_t partn_data,
764         __in                    size_t partn_size)
765 {
766         uint32_t *buf = (uint32_t *)partn_data;
767         efx_rc_t rc;
768         tlv_cursor_t cursor;
769         struct tlv_partition_header header;
770         struct tlv_partition_trailer trailer;
771
772         unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
773             sizeof (struct tlv_partition_trailer);
774         if (partn_size < min_buf_size) {
775                 rc = EINVAL;
776                 goto fail1;
777         }
778
779         memset(buf, 0xff, partn_size);
780
781         tlv_init_block(buf);
782         if ((rc = tlv_init_cursor(&cursor, buf,
783             (uint32_t *)((uint8_t *)buf + partn_size),
784             buf)) != 0) {
785                 goto fail2;
786         }
787
788         header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
789         header.length = __CPU_TO_LE_32(sizeof (header) - 8);
790         header.type_id = __CPU_TO_LE_16(partn_type);
791         header.preset = 0;
792         header.generation = __CPU_TO_LE_32(1);
793         header.total_length = 0;  /* This will be fixed below. */
794         if ((rc = tlv_insert(
795             &cursor, TLV_TAG_PARTITION_HEADER,
796             (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
797                 goto fail3;
798         if ((rc = tlv_advance(&cursor)) != 0)
799                 goto fail4;
800
801         trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
802         trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
803         trailer.generation = header.generation;
804         trailer.checksum = 0;  /* This will be fixed below. */
805         if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
806             (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
807                 goto fail5;
808
809         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
810                 goto fail6;
811
812         /* Check that the partition is valid. */
813         if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
814             partn_data, partn_size)) != 0)
815                 goto fail7;
816
817         return (0);
818
819 fail7:
820         EFSYS_PROBE(fail7);
821 fail6:
822         EFSYS_PROBE(fail6);
823 fail5:
824         EFSYS_PROBE(fail5);
825 fail4:
826         EFSYS_PROBE(fail4);
827 fail3:
828         EFSYS_PROBE(fail3);
829 fail2:
830         EFSYS_PROBE(fail2);
831 fail1:
832         EFSYS_PROBE1(fail1, efx_rc_t, rc);
833
834         return (rc);
835 }
836
837 static                  uint32_t
838 byte_offset(
839         __in            uint32_t *position,
840         __in            uint32_t *base)
841 {
842         return (uint32_t)((uint8_t *)position - (uint8_t *)base);
843 }
844
845         __checkReturn           efx_rc_t
846 ef10_nvram_buffer_find_item_start(
847         __in_bcount(buffer_size)
848                                 caddr_t bufferp,
849         __in                    size_t buffer_size,
850         __out                   uint32_t *startp)
851 {
852         /* Read past partition header to find start address of the first key */
853         tlv_cursor_t cursor;
854         efx_rc_t rc;
855
856         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
857         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
858                         buffer_size)) != 0) {
859                 rc = EFAULT;
860                 goto fail1;
861         }
862         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
863                 rc = EINVAL;
864                 goto fail2;
865         }
866
867         if ((rc = tlv_advance(&cursor)) != 0) {
868                 rc = EINVAL;
869                 goto fail3;
870         }
871         *startp = byte_offset(cursor.current, cursor.block);
872
873         if ((rc = tlv_require_end(&cursor)) != 0)
874                 goto fail4;
875
876         return (0);
877
878 fail4:
879         EFSYS_PROBE(fail4);
880 fail3:
881         EFSYS_PROBE(fail3);
882 fail2:
883         EFSYS_PROBE(fail2);
884 fail1:
885         EFSYS_PROBE1(fail1, efx_rc_t, rc);
886
887         return (rc);
888 }
889
890         __checkReturn           efx_rc_t
891 ef10_nvram_buffer_find_end(
892         __in_bcount(buffer_size)
893                                 caddr_t bufferp,
894         __in                    size_t buffer_size,
895         __in                    uint32_t offset,
896         __out                   uint32_t *endp)
897 {
898         /* Read to end of partition */
899         tlv_cursor_t cursor;
900         efx_rc_t rc;
901         uint32_t *segment_used;
902
903         _NOTE(ARGUNUSED(offset))
904
905         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
906                         buffer_size)) != 0) {
907                 rc = EFAULT;
908                 goto fail1;
909         }
910
911         segment_used = cursor.block;
912
913         /*
914          * Go through each segment and check that it has an end tag. If there
915          * is no end tag then the previous segment was the last valid one,
916          * so return the used space including that end tag.
917          */
918         while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
919                 if (tlv_require_end(&cursor) != 0) {
920                         if (segment_used == cursor.block) {
921                                 /*
922                                  * First segment is corrupt, so there is
923                                  * no valid data in partition.
924                                  */
925                                 rc = EINVAL;
926                                 goto fail2;
927                         }
928                         break;
929                 }
930                 segment_used = cursor.end + 1;
931
932                 cursor.current = segment_used;
933         }
934         /* Return space used (including the END tag) */
935         *endp = (segment_used - cursor.block) * sizeof (uint32_t);
936
937         return (0);
938
939 fail2:
940         EFSYS_PROBE(fail2);
941 fail1:
942         EFSYS_PROBE1(fail1, efx_rc_t, rc);
943
944         return (rc);
945 }
946
947         __checkReturn   __success(return != B_FALSE)    boolean_t
948 ef10_nvram_buffer_find_item(
949         __in_bcount(buffer_size)
950                                 caddr_t bufferp,
951         __in                    size_t buffer_size,
952         __in                    uint32_t offset,
953         __out                   uint32_t *startp,
954         __out                   uint32_t *lengthp)
955 {
956         /* Find TLV at offset and return key start and length */
957         tlv_cursor_t cursor;
958         uint8_t *key;
959         uint32_t tag;
960
961         if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
962                         buffer_size, offset) != 0) {
963                 return (B_FALSE);
964         }
965
966         while ((key = tlv_item(&cursor)) != NULL) {
967                 tag = tlv_tag(&cursor);
968                 if (tag == TLV_TAG_PARTITION_HEADER ||
969                     tag == TLV_TAG_PARTITION_TRAILER) {
970                         if (tlv_advance(&cursor) != 0) {
971                                 break;
972                         }
973                         continue;
974                 }
975                 *startp = byte_offset(cursor.current, cursor.block);
976                 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
977                     cursor.current);
978                 return (B_TRUE);
979         }
980
981         return (B_FALSE);
982 }
983
984         __checkReturn           efx_rc_t
985 ef10_nvram_buffer_get_item(
986         __in_bcount(buffer_size)
987                                 caddr_t bufferp,
988         __in                    size_t buffer_size,
989         __in                    uint32_t offset,
990         __in                    uint32_t length,
991         __out_bcount_part(item_max_size, *lengthp)
992                                 caddr_t itemp,
993         __in                    size_t item_max_size,
994         __out                   uint32_t *lengthp)
995 {
996         efx_rc_t rc;
997         tlv_cursor_t cursor;
998         uint32_t item_length;
999
1000         if (item_max_size < length) {
1001                 rc = ENOSPC;
1002                 goto fail1;
1003         }
1004
1005         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1006                         buffer_size, offset)) != 0) {
1007                 goto fail2;
1008         }
1009
1010         item_length = tlv_length(&cursor);
1011         if (length < item_length) {
1012                 rc = ENOSPC;
1013                 goto fail3;
1014         }
1015         memcpy(itemp, tlv_value(&cursor), item_length);
1016
1017         *lengthp = item_length;
1018
1019         return (0);
1020
1021 fail3:
1022         EFSYS_PROBE(fail3);
1023 fail2:
1024         EFSYS_PROBE(fail2);
1025 fail1:
1026         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1027
1028         return (rc);
1029 }
1030
1031         __checkReturn           efx_rc_t
1032 ef10_nvram_buffer_insert_item(
1033         __in_bcount(buffer_size)
1034                                 caddr_t bufferp,
1035         __in                    size_t buffer_size,
1036         __in                    uint32_t offset,
1037         __in_bcount(length)     caddr_t keyp,
1038         __in                    uint32_t length,
1039         __out                   uint32_t *lengthp)
1040 {
1041         efx_rc_t rc;
1042         tlv_cursor_t cursor;
1043
1044         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1045                         buffer_size, offset)) != 0) {
1046                 goto fail1;
1047         }
1048
1049         rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1050
1051         if (rc != 0) {
1052                 goto fail2;
1053         }
1054
1055         *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1056                     cursor.current);
1057
1058         return (0);
1059
1060 fail2:
1061         EFSYS_PROBE(fail2);
1062 fail1:
1063         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1064
1065         return (rc);
1066 }
1067
1068         __checkReturn           efx_rc_t
1069 ef10_nvram_buffer_delete_item(
1070         __in_bcount(buffer_size)
1071                                 caddr_t bufferp,
1072         __in                    size_t buffer_size,
1073         __in                    uint32_t offset,
1074         __in                    uint32_t length,
1075         __in                    uint32_t end)
1076 {
1077         efx_rc_t rc;
1078         tlv_cursor_t cursor;
1079
1080         _NOTE(ARGUNUSED(length, end))
1081
1082         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1083                         buffer_size, offset)) != 0) {
1084                 goto fail1;
1085         }
1086
1087         if ((rc = tlv_delete(&cursor)) != 0)
1088                 goto fail2;
1089
1090         return (0);
1091
1092 fail2:
1093         EFSYS_PROBE(fail2);
1094 fail1:
1095         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1096
1097         return (rc);
1098 }
1099
1100         __checkReturn           efx_rc_t
1101 ef10_nvram_buffer_finish(
1102         __in_bcount(buffer_size)
1103                                 caddr_t bufferp,
1104         __in                    size_t buffer_size)
1105 {
1106         efx_rc_t rc;
1107         tlv_cursor_t cursor;
1108
1109         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1110                         buffer_size)) != 0) {
1111                 rc = EFAULT;
1112                 goto fail1;
1113         }
1114
1115         if ((rc = tlv_require_end(&cursor)) != 0)
1116                 goto fail2;
1117
1118         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1119                 goto fail3;
1120
1121         return (0);
1122
1123 fail3:
1124         EFSYS_PROBE(fail3);
1125 fail2:
1126         EFSYS_PROBE(fail2);
1127 fail1:
1128         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1129
1130         return (rc);
1131 }
1132
1133
1134
1135 /*
1136  * Read and validate a segment from a partition. A segment is a complete
1137  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1138  * be multiple segments in a partition, so seg_offset allows segments
1139  * beyond the first to be read.
1140  */
1141 static  __checkReturn                   efx_rc_t
1142 ef10_nvram_read_tlv_segment(
1143         __in                            efx_nic_t *enp,
1144         __in                            uint32_t partn,
1145         __in                            size_t seg_offset,
1146         __in_bcount(max_seg_size)       caddr_t seg_data,
1147         __in                            size_t max_seg_size)
1148 {
1149         tlv_cursor_t cursor;
1150         struct tlv_partition_header *header;
1151         struct tlv_partition_trailer *trailer;
1152         size_t total_length;
1153         uint32_t cksum;
1154         int pos;
1155         efx_rc_t rc;
1156
1157         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1158
1159         if ((seg_data == NULL) || (max_seg_size == 0)) {
1160                 rc = EINVAL;
1161                 goto fail1;
1162         }
1163
1164         /* Read initial chunk of the segment, starting at offset */
1165         if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1166                     EF10_NVRAM_CHUNK,
1167                     MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1168                 goto fail2;
1169         }
1170
1171         /* A PARTITION_HEADER tag must be the first item at the given offset */
1172         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1173                     max_seg_size)) != 0) {
1174                 rc = EFAULT;
1175                 goto fail3;
1176         }
1177         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1178                 rc = EINVAL;
1179                 goto fail4;
1180         }
1181         header = (struct tlv_partition_header *)tlv_item(&cursor);
1182
1183         /* Check TLV segment length (includes the END tag) */
1184         total_length = __LE_TO_CPU_32(header->total_length);
1185         if (total_length > max_seg_size) {
1186                 rc = EFBIG;
1187                 goto fail5;
1188         }
1189
1190         /* Read the remaining segment content */
1191         if (total_length > EF10_NVRAM_CHUNK) {
1192                 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1193                             seg_offset + EF10_NVRAM_CHUNK,
1194                             seg_data + EF10_NVRAM_CHUNK,
1195                             total_length - EF10_NVRAM_CHUNK,
1196                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1197                         goto fail6;
1198         }
1199
1200         /* Check segment ends with PARTITION_TRAILER and END tags */
1201         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1202                 rc = EINVAL;
1203                 goto fail7;
1204         }
1205         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1206
1207         if ((rc = tlv_advance(&cursor)) != 0) {
1208                 rc = EINVAL;
1209                 goto fail8;
1210         }
1211         if (tlv_tag(&cursor) != TLV_TAG_END) {
1212                 rc = EINVAL;
1213                 goto fail9;
1214         }
1215
1216         /* Check data read from segment is consistent */
1217         if (trailer->generation != header->generation) {
1218                 /*
1219                  * The partition data may have been modified between successive
1220                  * MCDI NVRAM_READ requests by the MC or another PCI function.
1221                  *
1222                  * The caller must retry to obtain consistent partition data.
1223                  */
1224                 rc = EAGAIN;
1225                 goto fail10;
1226         }
1227
1228         /* Verify segment checksum */
1229         cksum = 0;
1230         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1231                 cksum += *((uint32_t *)(seg_data + pos));
1232         }
1233         if (cksum != 0) {
1234                 rc = EINVAL;
1235                 goto fail11;
1236         }
1237
1238         return (0);
1239
1240 fail11:
1241         EFSYS_PROBE(fail11);
1242 fail10:
1243         EFSYS_PROBE(fail10);
1244 fail9:
1245         EFSYS_PROBE(fail9);
1246 fail8:
1247         EFSYS_PROBE(fail8);
1248 fail7:
1249         EFSYS_PROBE(fail7);
1250 fail6:
1251         EFSYS_PROBE(fail6);
1252 fail5:
1253         EFSYS_PROBE(fail5);
1254 fail4:
1255         EFSYS_PROBE(fail4);
1256 fail3:
1257         EFSYS_PROBE(fail3);
1258 fail2:
1259         EFSYS_PROBE(fail2);
1260 fail1:
1261         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1262
1263         return (rc);
1264 }
1265
1266 /*
1267  * Read a single TLV item from a host memory
1268  * buffer containing a TLV formatted segment.
1269  */
1270         __checkReturn           efx_rc_t
1271 ef10_nvram_buf_read_tlv(
1272         __in                            efx_nic_t *enp,
1273         __in_bcount(max_seg_size)       caddr_t seg_data,
1274         __in                            size_t max_seg_size,
1275         __in                            uint32_t tag,
1276         __deref_out_bcount_opt(*sizep)  caddr_t *datap,
1277         __out                           size_t *sizep)
1278 {
1279         tlv_cursor_t cursor;
1280         caddr_t data;
1281         size_t length;
1282         caddr_t value;
1283         efx_rc_t rc;
1284
1285         if ((seg_data == NULL) || (max_seg_size == 0)) {
1286                 rc = EINVAL;
1287                 goto fail1;
1288         }
1289
1290         /* Find requested TLV tag in segment data */
1291         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1292                     max_seg_size)) != 0) {
1293                 rc = EFAULT;
1294                 goto fail2;
1295         }
1296         if ((rc = tlv_find(&cursor, tag)) != 0) {
1297                 rc = ENOENT;
1298                 goto fail3;
1299         }
1300         value = (caddr_t)tlv_value(&cursor);
1301         length = tlv_length(&cursor);
1302
1303         if (length == 0)
1304                 data = NULL;
1305         else {
1306                 /* Copy out data from TLV item */
1307                 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1308                 if (data == NULL) {
1309                         rc = ENOMEM;
1310                         goto fail4;
1311                 }
1312                 memcpy(data, value, length);
1313         }
1314
1315         *datap = data;
1316         *sizep = length;
1317
1318         return (0);
1319
1320 fail4:
1321         EFSYS_PROBE(fail4);
1322 fail3:
1323         EFSYS_PROBE(fail3);
1324 fail2:
1325         EFSYS_PROBE(fail2);
1326 fail1:
1327         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1328
1329         return (rc);
1330 }
1331
1332 /* Read a single TLV item from the first segment in a TLV formatted partition */
1333         __checkReturn           efx_rc_t
1334 ef10_nvram_partn_read_tlv(
1335         __in                                    efx_nic_t *enp,
1336         __in                                    uint32_t partn,
1337         __in                                    uint32_t tag,
1338         __deref_out_bcount_opt(*seg_sizep)      caddr_t *seg_datap,
1339         __out                                   size_t *seg_sizep)
1340 {
1341         caddr_t seg_data = NULL;
1342         size_t partn_size = 0;
1343         size_t length;
1344         caddr_t data;
1345         int retry;
1346         efx_rc_t rc;
1347
1348         /* Allocate sufficient memory for the entire partition */
1349         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1350                 goto fail1;
1351
1352         if (partn_size == 0) {
1353                 rc = ENOENT;
1354                 goto fail2;
1355         }
1356
1357         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1358         if (seg_data == NULL) {
1359                 rc = ENOMEM;
1360                 goto fail3;
1361         }
1362
1363         /*
1364          * Read the first segment in a TLV partition. Retry until consistent
1365          * segment contents are returned. Inconsistent data may be read if:
1366          *  a) the segment contents are invalid
1367          *  b) the MC has rebooted while we were reading the partition
1368          *  c) the partition has been modified while we were reading it
1369          * Limit retry attempts to ensure forward progress.
1370          */
1371         retry = 10;
1372         do {
1373                 rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1374                     seg_data, partn_size);
1375         } while ((rc == EAGAIN) && (--retry > 0));
1376
1377         if (rc != 0) {
1378                 /* Failed to obtain consistent segment data */
1379                 goto fail4;
1380         }
1381
1382         if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1383                     tag, &data, &length)) != 0)
1384                 goto fail5;
1385
1386         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1387
1388         *seg_datap = data;
1389         *seg_sizep = length;
1390
1391         return (0);
1392
1393 fail5:
1394         EFSYS_PROBE(fail5);
1395 fail4:
1396         EFSYS_PROBE(fail4);
1397
1398         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1399 fail3:
1400         EFSYS_PROBE(fail3);
1401 fail2:
1402         EFSYS_PROBE(fail2);
1403 fail1:
1404         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1405
1406         return (rc);
1407 }
1408
1409 /* Compute the size of a segment. */
1410         static  __checkReturn   efx_rc_t
1411 ef10_nvram_buf_segment_size(
1412         __in                    caddr_t seg_data,
1413         __in                    size_t max_seg_size,
1414         __out                   size_t *seg_sizep)
1415 {
1416         efx_rc_t rc;
1417         tlv_cursor_t cursor;
1418         struct tlv_partition_header *header;
1419         uint32_t cksum;
1420         int pos;
1421         uint32_t *end_tag_position;
1422         uint32_t segment_length;
1423
1424         /* A PARTITION_HEADER tag must be the first item at the given offset */
1425         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1426                     max_seg_size)) != 0) {
1427                 rc = EFAULT;
1428                 goto fail1;
1429         }
1430         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1431                 rc = EINVAL;
1432                 goto fail2;
1433         }
1434         header = (struct tlv_partition_header *)tlv_item(&cursor);
1435
1436         /* Check TLV segment length (includes the END tag) */
1437         *seg_sizep = __LE_TO_CPU_32(header->total_length);
1438         if (*seg_sizep > max_seg_size) {
1439                 rc = EFBIG;
1440                 goto fail3;
1441         }
1442
1443         /* Check segment ends with PARTITION_TRAILER and END tags */
1444         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1445                 rc = EINVAL;
1446                 goto fail4;
1447         }
1448
1449         if ((rc = tlv_advance(&cursor)) != 0) {
1450                 rc = EINVAL;
1451                 goto fail5;
1452         }
1453         if (tlv_tag(&cursor) != TLV_TAG_END) {
1454                 rc = EINVAL;
1455                 goto fail6;
1456         }
1457         end_tag_position = cursor.current;
1458
1459         /* Verify segment checksum */
1460         cksum = 0;
1461         for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1462                 cksum += *((uint32_t *)(seg_data + pos));
1463         }
1464         if (cksum != 0) {
1465                 rc = EINVAL;
1466                 goto fail7;
1467         }
1468
1469         /*
1470          * Calculate total length from HEADER to END tags and compare to
1471          * max_seg_size and the total_length field in the HEADER tag.
1472          */
1473         segment_length = tlv_block_length_used(&cursor);
1474
1475         if (segment_length > max_seg_size) {
1476                 rc = EINVAL;
1477                 goto fail8;
1478         }
1479
1480         if (segment_length != *seg_sizep) {
1481                 rc = EINVAL;
1482                 goto fail9;
1483         }
1484
1485         /* Skip over the first HEADER tag. */
1486         rc = tlv_rewind(&cursor);
1487         rc = tlv_advance(&cursor);
1488
1489         while (rc == 0) {
1490                 if (tlv_tag(&cursor) == TLV_TAG_END) {
1491                         /* Check that the END tag is the one found earlier. */
1492                         if (cursor.current != end_tag_position)
1493                                 goto fail10;
1494                         break;
1495                 }
1496                 /* Check for duplicate HEADER tags before the END tag. */
1497                 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1498                         rc = EINVAL;
1499                         goto fail11;
1500                 }
1501
1502                 rc = tlv_advance(&cursor);
1503         }
1504         if (rc != 0)
1505                 goto fail12;
1506
1507         return (0);
1508
1509 fail12:
1510         EFSYS_PROBE(fail12);
1511 fail11:
1512         EFSYS_PROBE(fail11);
1513 fail10:
1514         EFSYS_PROBE(fail10);
1515 fail9:
1516         EFSYS_PROBE(fail9);
1517 fail8:
1518         EFSYS_PROBE(fail8);
1519 fail7:
1520         EFSYS_PROBE(fail7);
1521 fail6:
1522         EFSYS_PROBE(fail6);
1523 fail5:
1524         EFSYS_PROBE(fail5);
1525 fail4:
1526         EFSYS_PROBE(fail4);
1527 fail3:
1528         EFSYS_PROBE(fail3);
1529 fail2:
1530         EFSYS_PROBE(fail2);
1531 fail1:
1532         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1533
1534         return (rc);
1535 }
1536
1537 /*
1538  * Add or update a single TLV item in a host memory buffer containing a TLV
1539  * formatted segment. Historically partitions consisted of only one segment.
1540  */
1541         __checkReturn                   efx_rc_t
1542 ef10_nvram_buf_write_tlv(
1543         __inout_bcount(max_seg_size)    caddr_t seg_data,
1544         __in                            size_t max_seg_size,
1545         __in                            uint32_t tag,
1546         __in_bcount(tag_size)           caddr_t tag_data,
1547         __in                            size_t tag_size,
1548         __out                           size_t *total_lengthp)
1549 {
1550         tlv_cursor_t cursor;
1551         struct tlv_partition_header *header;
1552         struct tlv_partition_trailer *trailer;
1553         uint32_t generation;
1554         uint32_t cksum;
1555         int pos;
1556         efx_rc_t rc;
1557
1558         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1559         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1560                         max_seg_size)) != 0) {
1561                 rc = EFAULT;
1562                 goto fail1;
1563         }
1564         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1565                 rc = EINVAL;
1566                 goto fail2;
1567         }
1568         header = (struct tlv_partition_header *)tlv_item(&cursor);
1569
1570         /* Update the TLV chain to contain the new data */
1571         if ((rc = tlv_find(&cursor, tag)) == 0) {
1572                 /* Modify existing TLV item */
1573                 if ((rc = tlv_modify(&cursor, tag,
1574                             (uint8_t *)tag_data, tag_size)) != 0)
1575                         goto fail3;
1576         } else {
1577                 /* Insert a new TLV item before the PARTITION_TRAILER */
1578                 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1579                 if (rc != 0) {
1580                         rc = EINVAL;
1581                         goto fail4;
1582                 }
1583                 if ((rc = tlv_insert(&cursor, tag,
1584                             (uint8_t *)tag_data, tag_size)) != 0) {
1585                         rc = EINVAL;
1586                         goto fail5;
1587                 }
1588         }
1589
1590         /* Find the trailer tag */
1591         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1592                 rc = EINVAL;
1593                 goto fail6;
1594         }
1595         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1596
1597         /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1598         *total_lengthp = tlv_block_length_used(&cursor);
1599         if (*total_lengthp > max_seg_size) {
1600                 rc = ENOSPC;
1601                 goto fail7;
1602         }
1603         generation = __LE_TO_CPU_32(header->generation) + 1;
1604
1605         header->total_length    = __CPU_TO_LE_32(*total_lengthp);
1606         header->generation      = __CPU_TO_LE_32(generation);
1607         trailer->generation     = __CPU_TO_LE_32(generation);
1608
1609         /* Recompute PARTITION_TRAILER checksum */
1610         trailer->checksum = 0;
1611         cksum = 0;
1612         for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1613                 cksum += *((uint32_t *)(seg_data + pos));
1614         }
1615         trailer->checksum = ~cksum + 1;
1616
1617         return (0);
1618
1619 fail7:
1620         EFSYS_PROBE(fail7);
1621 fail6:
1622         EFSYS_PROBE(fail6);
1623 fail5:
1624         EFSYS_PROBE(fail5);
1625 fail4:
1626         EFSYS_PROBE(fail4);
1627 fail3:
1628         EFSYS_PROBE(fail3);
1629 fail2:
1630         EFSYS_PROBE(fail2);
1631 fail1:
1632         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1633
1634         return (rc);
1635 }
1636
1637 /*
1638  * Add or update a single TLV item in the first segment of a TLV formatted
1639  * dynamic config partition. The first segment is the current active
1640  * configuration.
1641  */
1642         __checkReturn           efx_rc_t
1643 ef10_nvram_partn_write_tlv(
1644         __in                    efx_nic_t *enp,
1645         __in                    uint32_t partn,
1646         __in                    uint32_t tag,
1647         __in_bcount(size)       caddr_t data,
1648         __in                    size_t size)
1649 {
1650         return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1651             size, B_FALSE);
1652 }
1653
1654 /*
1655  * Read a segment from nvram at the given offset into a buffer (segment_data)
1656  * and optionally write a new tag to it.
1657  */
1658 static  __checkReturn           efx_rc_t
1659 ef10_nvram_segment_write_tlv(
1660         __in                    efx_nic_t *enp,
1661         __in                    uint32_t partn,
1662         __in                    uint32_t tag,
1663         __in_bcount(size)       caddr_t data,
1664         __in                    size_t size,
1665         __inout                 caddr_t *seg_datap,
1666         __inout                 size_t *partn_offsetp,
1667         __inout                 size_t *src_remain_lenp,
1668         __inout                 size_t *dest_remain_lenp,
1669         __in                    boolean_t write)
1670 {
1671         efx_rc_t rc;
1672         efx_rc_t status;
1673         size_t original_segment_size;
1674         size_t modified_segment_size;
1675
1676         /*
1677          * Read the segment from NVRAM into the segment_data buffer and validate
1678          * it, returning if it does not validate. This is not a failure unless
1679          * this is the first segment in a partition. In this case the caller
1680          * must propagate the error.
1681          */
1682         status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1683             *seg_datap, *src_remain_lenp);
1684         if (status != 0) {
1685                 rc = EINVAL;
1686                 goto fail1;
1687         }
1688
1689         status = ef10_nvram_buf_segment_size(*seg_datap,
1690             *src_remain_lenp, &original_segment_size);
1691         if (status != 0) {
1692                 rc = EINVAL;
1693                 goto fail2;
1694         }
1695
1696         if (write) {
1697                 /* Update the contents of the segment in the buffer */
1698                 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1699                         *dest_remain_lenp, tag, data, size,
1700                         &modified_segment_size)) != 0) {
1701                         goto fail3;
1702                 }
1703                 *dest_remain_lenp -= modified_segment_size;
1704                 *seg_datap += modified_segment_size;
1705         } else {
1706                 /*
1707                  * We won't modify this segment, but still need to update the
1708                  * remaining lengths and pointers.
1709                  */
1710                 *dest_remain_lenp -= original_segment_size;
1711                 *seg_datap += original_segment_size;
1712         }
1713
1714         *partn_offsetp += original_segment_size;
1715         *src_remain_lenp -= original_segment_size;
1716
1717         return (0);
1718
1719 fail3:
1720         EFSYS_PROBE(fail3);
1721 fail2:
1722         EFSYS_PROBE(fail2);
1723 fail1:
1724         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1725
1726         return (rc);
1727 }
1728
1729 /*
1730  * Add or update a single TLV item in either the first segment or in all
1731  * segments in a TLV formatted dynamic config partition. Dynamic config
1732  * partitions on boards that support RFID are divided into a number of segments,
1733  * each formatted like a partition, with header, trailer and end tags. The first
1734  * segment is the current active configuration.
1735  *
1736  * The segments are initialised by manftest and each contain a different
1737  * configuration e.g. firmware variant. The firmware can be instructed
1738  * via RFID to copy a segment to replace the first segment, hence changing the
1739  * active configuration.  This allows ops to change the configuration of a board
1740  * prior to shipment using RFID.
1741  *
1742  * Changes to the dynamic config may need to be written to all segments (e.g.
1743  * firmware versions) or just the first segment (changes to the active
1744  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1745  * If only the first segment is written the code still needs to be aware of the
1746  * possible presence of subsequent segments as writing to a segment may cause
1747  * its size to increase, which would overwrite the subsequent segments and
1748  * invalidate them.
1749  */
1750         __checkReturn           efx_rc_t
1751 ef10_nvram_partn_write_segment_tlv(
1752         __in                    efx_nic_t *enp,
1753         __in                    uint32_t partn,
1754         __in                    uint32_t tag,
1755         __in_bcount(size)       caddr_t data,
1756         __in                    size_t size,
1757         __in                    boolean_t all_segments)
1758 {
1759         size_t partn_size = 0;
1760         caddr_t partn_data;
1761         size_t total_length = 0;
1762         efx_rc_t rc;
1763         size_t current_offset = 0;
1764         size_t remaining_original_length;
1765         size_t remaining_modified_length;
1766         caddr_t segment_data;
1767
1768         EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1769
1770         /* Allocate sufficient memory for the entire partition */
1771         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1772                 goto fail1;
1773
1774         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1775         if (partn_data == NULL) {
1776                 rc = ENOMEM;
1777                 goto fail2;
1778         }
1779
1780         remaining_original_length = partn_size;
1781         remaining_modified_length = partn_size;
1782         segment_data = partn_data;
1783
1784         /* Lock the partition */
1785         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1786                 goto fail3;
1787
1788         /* Iterate over each (potential) segment to update it. */
1789         do {
1790                 boolean_t write = all_segments || current_offset == 0;
1791
1792                 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1793                     &segment_data, &current_offset, &remaining_original_length,
1794                     &remaining_modified_length, write);
1795                 if (rc != 0) {
1796                         if (current_offset == 0) {
1797                                 /*
1798                                  * If no data has been read then the first
1799                                  * segment is invalid, which is an error.
1800                                  */
1801                                 goto fail4;
1802                         }
1803                         break;
1804                 }
1805         } while (current_offset < partn_size);
1806
1807         total_length = segment_data - partn_data;
1808
1809         /*
1810          * We've run out of space.  This should actually be dealt with by
1811          * ef10_nvram_buf_write_tlv returning ENOSPC.
1812          */
1813         if (total_length > partn_size) {
1814                 rc = ENOSPC;
1815                 goto fail5;
1816         }
1817
1818         /* Erase the whole partition in NVRAM */
1819         if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1820                 goto fail6;
1821
1822         /* Write new partition contents from the buffer to NVRAM */
1823         if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1824                     total_length)) != 0)
1825                 goto fail7;
1826
1827         /* Unlock the partition */
1828         ef10_nvram_partn_unlock(enp, partn, NULL);
1829
1830         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1831
1832         return (0);
1833
1834 fail7:
1835         EFSYS_PROBE(fail7);
1836 fail6:
1837         EFSYS_PROBE(fail6);
1838 fail5:
1839         EFSYS_PROBE(fail5);
1840 fail4:
1841         EFSYS_PROBE(fail4);
1842
1843         ef10_nvram_partn_unlock(enp, partn, NULL);
1844 fail3:
1845         EFSYS_PROBE(fail3);
1846
1847         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1848 fail2:
1849         EFSYS_PROBE(fail2);
1850 fail1:
1851         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1852
1853         return (rc);
1854 }
1855
1856 /*
1857  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1858  * not the data used by the segments in the partition.
1859  */
1860         __checkReturn           efx_rc_t
1861 ef10_nvram_partn_size(
1862         __in                    efx_nic_t *enp,
1863         __in                    uint32_t partn,
1864         __out                   size_t *sizep)
1865 {
1866         efx_rc_t rc;
1867
1868         if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1869             NULL, NULL, NULL)) != 0)
1870                 goto fail1;
1871
1872         return (0);
1873
1874 fail1:
1875         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1876
1877         return (rc);
1878 }
1879
1880         __checkReturn           efx_rc_t
1881 ef10_nvram_partn_lock(
1882         __in                    efx_nic_t *enp,
1883         __in                    uint32_t partn)
1884 {
1885         efx_rc_t rc;
1886
1887         if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1888                 goto fail1;
1889
1890         return (0);
1891
1892 fail1:
1893         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1894
1895         return (rc);
1896 }
1897
1898         __checkReturn           efx_rc_t
1899 ef10_nvram_partn_read_mode(
1900         __in                    efx_nic_t *enp,
1901         __in                    uint32_t partn,
1902         __in                    unsigned int offset,
1903         __out_bcount(size)      caddr_t data,
1904         __in                    size_t size,
1905         __in                    uint32_t mode)
1906 {
1907         size_t chunk;
1908         efx_rc_t rc;
1909
1910         while (size > 0) {
1911                 chunk = MIN(size, EF10_NVRAM_CHUNK);
1912
1913                 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1914                             data, chunk, mode)) != 0) {
1915                         goto fail1;
1916                 }
1917
1918                 size -= chunk;
1919                 data += chunk;
1920                 offset += chunk;
1921         }
1922
1923         return (0);
1924
1925 fail1:
1926         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1927
1928         return (rc);
1929 }
1930
1931         __checkReturn           efx_rc_t
1932 ef10_nvram_partn_read(
1933         __in                    efx_nic_t *enp,
1934         __in                    uint32_t partn,
1935         __in                    unsigned int offset,
1936         __out_bcount(size)      caddr_t data,
1937         __in                    size_t size)
1938 {
1939         /*
1940          * Read requests which come in through the EFX API expect to
1941          * read the current, active partition.
1942          */
1943         return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1944                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1945 }
1946
1947         __checkReturn           efx_rc_t
1948 ef10_nvram_partn_erase(
1949         __in                    efx_nic_t *enp,
1950         __in                    uint32_t partn,
1951         __in                    unsigned int offset,
1952         __in                    size_t size)
1953 {
1954         efx_rc_t rc;
1955         uint32_t erase_size;
1956
1957         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1958             &erase_size, NULL)) != 0)
1959                 goto fail1;
1960
1961         if (erase_size == 0) {
1962                 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1963                         goto fail2;
1964         } else {
1965                 if (size % erase_size != 0) {
1966                         rc = EINVAL;
1967                         goto fail3;
1968                 }
1969                 while (size > 0) {
1970                         if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1971                             erase_size)) != 0)
1972                                 goto fail4;
1973                         offset += erase_size;
1974                         size -= erase_size;
1975                 }
1976         }
1977
1978         return (0);
1979
1980 fail4:
1981         EFSYS_PROBE(fail4);
1982 fail3:
1983         EFSYS_PROBE(fail3);
1984 fail2:
1985         EFSYS_PROBE(fail2);
1986 fail1:
1987         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1988
1989         return (rc);
1990 }
1991
1992         __checkReturn           efx_rc_t
1993 ef10_nvram_partn_write(
1994         __in                    efx_nic_t *enp,
1995         __in                    uint32_t partn,
1996         __in                    unsigned int offset,
1997         __out_bcount(size)      caddr_t data,
1998         __in                    size_t size)
1999 {
2000         size_t chunk;
2001         uint32_t write_size;
2002         efx_rc_t rc;
2003
2004         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2005             NULL, &write_size)) != 0)
2006                 goto fail1;
2007
2008         if (write_size != 0) {
2009                 /*
2010                  * Check that the size is a multiple of the write chunk size if
2011                  * the write chunk size is available.
2012                  */
2013                 if (size % write_size != 0) {
2014                         rc = EINVAL;
2015                         goto fail2;
2016                 }
2017         } else {
2018                 write_size = EF10_NVRAM_CHUNK;
2019         }
2020
2021         while (size > 0) {
2022                 chunk = MIN(size, write_size);
2023
2024                 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2025                             data, chunk)) != 0) {
2026                         goto fail3;
2027                 }
2028
2029                 size -= chunk;
2030                 data += chunk;
2031                 offset += chunk;
2032         }
2033
2034         return (0);
2035
2036 fail3:
2037         EFSYS_PROBE(fail3);
2038 fail2:
2039         EFSYS_PROBE(fail2);
2040 fail1:
2041         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2042
2043         return (rc);
2044 }
2045
2046         __checkReturn           efx_rc_t
2047 ef10_nvram_partn_unlock(
2048         __in                    efx_nic_t *enp,
2049         __in                    uint32_t partn,
2050         __out_opt               uint32_t *resultp)
2051 {
2052         boolean_t reboot = B_FALSE;
2053         efx_rc_t rc;
2054
2055         if (resultp != NULL)
2056                 *resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2057
2058         rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, resultp);
2059         if (rc != 0)
2060                 goto fail1;
2061
2062         return (0);
2063
2064 fail1:
2065         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2066
2067         return (rc);
2068 }
2069
2070         __checkReturn           efx_rc_t
2071 ef10_nvram_partn_set_version(
2072         __in                    efx_nic_t *enp,
2073         __in                    uint32_t partn,
2074         __in_ecount(4)          uint16_t version[4])
2075 {
2076         struct tlv_partition_version partn_version;
2077         size_t size;
2078         efx_rc_t rc;
2079
2080         /* Add or modify partition version TLV item */
2081         partn_version.version_w = __CPU_TO_LE_16(version[0]);
2082         partn_version.version_x = __CPU_TO_LE_16(version[1]);
2083         partn_version.version_y = __CPU_TO_LE_16(version[2]);
2084         partn_version.version_z = __CPU_TO_LE_16(version[3]);
2085
2086         size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2087
2088         /* Write the version number to all segments in the partition */
2089         if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2090                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2091                     TLV_TAG_PARTITION_VERSION(partn),
2092                     (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2093                 goto fail1;
2094
2095         return (0);
2096
2097 fail1:
2098         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2099
2100         return (rc);
2101 }
2102
2103 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2104
2105 #if EFSYS_OPT_NVRAM
2106
2107 typedef struct ef10_parttbl_entry_s {
2108         unsigned int            partn;
2109         unsigned int            port;
2110         efx_nvram_type_t        nvtype;
2111 } ef10_parttbl_entry_t;
2112
2113 /* Translate EFX NVRAM types to firmware partition types */
2114 static ef10_parttbl_entry_t hunt_parttbl[] = {
2115         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
2116         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
2117         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
2118         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
2119         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2120         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2121         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2122         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2123         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
2124         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
2125         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
2126         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
2127         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2128         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2129         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2130         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2131         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
2132         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
2133         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
2134         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
2135         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
2136         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
2137         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
2138         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
2139         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
2140         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
2141         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
2142         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
2143         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
2144         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
2145         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
2146         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE}
2147 };
2148
2149 static ef10_parttbl_entry_t medford_parttbl[] = {
2150         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
2151         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
2152         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
2153         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
2154         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2155         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2156         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2157         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2158         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
2159         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
2160         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
2161         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
2162         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2163         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2164         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2165         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2166         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
2167         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
2168         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
2169         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
2170         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
2171         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
2172         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
2173         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
2174         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
2175         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
2176         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
2177         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
2178         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
2179         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
2180         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
2181         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE},
2182         {NVRAM_PARTITION_TYPE_EXPANSION_UEFI,      1, EFX_NVRAM_UEFIROM},
2183         {NVRAM_PARTITION_TYPE_EXPANSION_UEFI,      2, EFX_NVRAM_UEFIROM},
2184         {NVRAM_PARTITION_TYPE_EXPANSION_UEFI,      3, EFX_NVRAM_UEFIROM},
2185         {NVRAM_PARTITION_TYPE_EXPANSION_UEFI,      4, EFX_NVRAM_UEFIROM}
2186 };
2187
2188 static  __checkReturn           efx_rc_t
2189 ef10_parttbl_get(
2190         __in                    efx_nic_t *enp,
2191         __out                   ef10_parttbl_entry_t **parttblp,
2192         __out                   size_t *parttbl_rowsp)
2193 {
2194         switch (enp->en_family) {
2195         case EFX_FAMILY_HUNTINGTON:
2196                 *parttblp = hunt_parttbl;
2197                 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2198                 break;
2199
2200         case EFX_FAMILY_MEDFORD:
2201                 *parttblp = medford_parttbl;
2202                 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2203                 break;
2204
2205         default:
2206                 EFSYS_ASSERT(B_FALSE);
2207                 return (EINVAL);
2208         }
2209         return (0);
2210 }
2211
2212         __checkReturn           efx_rc_t
2213 ef10_nvram_type_to_partn(
2214         __in                    efx_nic_t *enp,
2215         __in                    efx_nvram_type_t type,
2216         __out                   uint32_t *partnp)
2217 {
2218         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2219         ef10_parttbl_entry_t *parttbl = NULL;
2220         size_t parttbl_rows = 0;
2221         unsigned int i;
2222
2223         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2224         EFSYS_ASSERT(partnp != NULL);
2225
2226         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2227                 for (i = 0; i < parttbl_rows; i++) {
2228                         ef10_parttbl_entry_t *entry = &parttbl[i];
2229
2230                         if (entry->nvtype == type &&
2231                             entry->port == emip->emi_port) {
2232                                 *partnp = entry->partn;
2233                                 return (0);
2234                         }
2235                 }
2236         }
2237
2238         return (ENOTSUP);
2239 }
2240
2241 #if EFSYS_OPT_DIAG
2242
2243 static  __checkReturn           efx_rc_t
2244 ef10_nvram_partn_to_type(
2245         __in                    efx_nic_t *enp,
2246         __in                    uint32_t partn,
2247         __out                   efx_nvram_type_t *typep)
2248 {
2249         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2250         ef10_parttbl_entry_t *parttbl = NULL;
2251         size_t parttbl_rows = 0;
2252         unsigned int i;
2253
2254         EFSYS_ASSERT(typep != NULL);
2255
2256         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2257                 for (i = 0; i < parttbl_rows; i++) {
2258                         ef10_parttbl_entry_t *entry = &parttbl[i];
2259
2260                         if (entry->partn == partn &&
2261                             entry->port == emip->emi_port) {
2262                                 *typep = entry->nvtype;
2263                                 return (0);
2264                         }
2265                 }
2266         }
2267
2268         return (ENOTSUP);
2269 }
2270
2271         __checkReturn           efx_rc_t
2272 ef10_nvram_test(
2273         __in                    efx_nic_t *enp)
2274 {
2275         efx_nvram_type_t type;
2276         unsigned int npartns = 0;
2277         uint32_t *partns = NULL;
2278         size_t size;
2279         unsigned int i;
2280         efx_rc_t rc;
2281
2282         /* Read available partitions from NVRAM partition map */
2283         size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2284         EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2285         if (partns == NULL) {
2286                 rc = ENOMEM;
2287                 goto fail1;
2288         }
2289
2290         if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2291                     &npartns)) != 0) {
2292                 goto fail2;
2293         }
2294
2295         for (i = 0; i < npartns; i++) {
2296                 /* Check if the partition is supported for this port */
2297                 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2298                         continue;
2299
2300                 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2301                         goto fail3;
2302         }
2303
2304         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2305         return (0);
2306
2307 fail3:
2308         EFSYS_PROBE(fail3);
2309 fail2:
2310         EFSYS_PROBE(fail2);
2311         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2312 fail1:
2313         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2314         return (rc);
2315 }
2316
2317 #endif  /* EFSYS_OPT_DIAG */
2318
2319         __checkReturn           efx_rc_t
2320 ef10_nvram_partn_get_version(
2321         __in                    efx_nic_t *enp,
2322         __in                    uint32_t partn,
2323         __out                   uint32_t *subtypep,
2324         __out_ecount(4)         uint16_t version[4])
2325 {
2326         efx_rc_t rc;
2327
2328         /* FIXME: get highest partn version from all ports */
2329         /* FIXME: return partn description if available */
2330
2331         if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2332                     version, NULL, 0)) != 0)
2333                 goto fail1;
2334
2335         return (0);
2336
2337 fail1:
2338         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2339
2340         return (rc);
2341 }
2342
2343         __checkReturn           efx_rc_t
2344 ef10_nvram_partn_rw_start(
2345         __in                    efx_nic_t *enp,
2346         __in                    uint32_t partn,
2347         __out                   size_t *chunk_sizep)
2348 {
2349         efx_rc_t rc;
2350
2351         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2352                 goto fail1;
2353
2354         if (chunk_sizep != NULL)
2355                 *chunk_sizep = EF10_NVRAM_CHUNK;
2356
2357         return (0);
2358
2359 fail1:
2360         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2361
2362         return (rc);
2363 }
2364
2365         __checkReturn           efx_rc_t
2366 ef10_nvram_partn_rw_finish(
2367         __in                    efx_nic_t *enp,
2368         __in                    uint32_t partn)
2369 {
2370         efx_rc_t rc;
2371
2372         if ((rc = ef10_nvram_partn_unlock(enp, partn, NULL)) != 0)
2373                 goto fail1;
2374
2375         return (0);
2376
2377 fail1:
2378         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2379
2380         return (rc);
2381 }
2382
2383 #endif  /* EFSYS_OPT_NVRAM */
2384
2385 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */