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