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