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