]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/dev/sfxge/common/hunt_nvram.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / dev / sfxge / common / hunt_nvram.c
1 /*-
2  * Copyright (c) 2012-2015 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
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 static  __checkReturn           efx_rc_t
52 tlv_validate_state(
53         __in                    tlv_cursor_t *cursor);
54
55
56 /*
57  * Operations on TLV formatted partition data.
58  */
59 static                          uint32_t
60 tlv_tag(
61         __in    tlv_cursor_t    *cursor)
62 {
63         uint32_t dword, tag;
64
65         dword = cursor->current[0];
66         tag = __LE_TO_CPU_32(dword);
67
68         return (tag);
69 }
70
71 static                          size_t
72 tlv_length(
73         __in    tlv_cursor_t    *cursor)
74 {
75         uint32_t dword, length;
76
77         if (tlv_tag(cursor) == TLV_TAG_END)
78                 return (0);
79
80         dword = cursor->current[1];
81         length = __LE_TO_CPU_32(dword);
82
83         return ((size_t)length);
84 }
85
86 static                          uint8_t *
87 tlv_value(
88         __in    tlv_cursor_t    *cursor)
89 {
90         if (tlv_tag(cursor) == TLV_TAG_END)
91                 return (NULL);
92
93         return ((uint8_t *)(&cursor->current[2]));
94 }
95
96 static                          uint8_t *
97 tlv_item(
98         __in    tlv_cursor_t    *cursor)
99 {
100         if (tlv_tag(cursor) == TLV_TAG_END)
101                 return (NULL);
102
103         return ((uint8_t *)cursor->current);
104 }
105
106 /*
107  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
108  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
109  */
110 #define TLV_DWORD_COUNT(length) \
111         (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
112
113
114 static                          uint32_t *
115 tlv_next_item_ptr(
116         __in    tlv_cursor_t    *cursor)
117 {
118         uint32_t length;
119
120         length = tlv_length(cursor);
121
122         return (cursor->current + TLV_DWORD_COUNT(length));
123 }
124
125 static                          efx_rc_t
126 tlv_advance(
127         __in    tlv_cursor_t    *cursor)
128 {
129         efx_rc_t rc;
130
131         if ((rc = tlv_validate_state(cursor)) != 0)
132                 goto fail1;
133
134         if (cursor->current == cursor->end) {
135                 /* No more tags after END tag */
136                 cursor->current = NULL;
137                 rc = ENOENT;
138                 goto fail2;
139         }
140
141         /* Advance to next item and validate */
142         cursor->current = tlv_next_item_ptr(cursor);
143
144         if ((rc = tlv_validate_state(cursor)) != 0)
145                 goto fail3;
146
147         return (0);
148
149 fail3:
150         EFSYS_PROBE(fail3);
151 fail2:
152         EFSYS_PROBE(fail2);
153 fail1:
154         EFSYS_PROBE1(fail1, efx_rc_t, rc);
155
156         return (rc);
157 }
158
159 static                          efx_rc_t
160 tlv_rewind(
161         __in    tlv_cursor_t    *cursor)
162 {
163         efx_rc_t rc;
164
165         cursor->current = cursor->block;
166
167         if ((rc = tlv_validate_state(cursor)) != 0)
168                 goto fail1;
169
170         return (0);
171
172 fail1:
173         EFSYS_PROBE1(fail1, efx_rc_t, rc);
174
175         return (rc);
176 }
177
178 static                          efx_rc_t
179 tlv_find(
180         __in    tlv_cursor_t    *cursor,
181         __in    uint32_t        tag)
182 {
183         efx_rc_t rc;
184
185         rc = tlv_rewind(cursor);
186         while (rc == 0) {
187                 if (tlv_tag(cursor) == tag)
188                         break;
189
190                 rc = tlv_advance(cursor);
191         }
192         return (rc);
193 }
194
195 static  __checkReturn           efx_rc_t
196 tlv_validate_state(
197         __in    tlv_cursor_t    *cursor)
198 {
199         efx_rc_t rc;
200
201         /* Check cursor position */
202         if (cursor->current < cursor->block) {
203                 rc = EINVAL;
204                 goto fail1;
205         }
206         if (cursor->current > cursor->limit) {
207                 rc = EINVAL;
208                 goto fail2;
209         }
210
211         if (tlv_tag(cursor) != TLV_TAG_END) {
212                 /* Check current item has space for tag and length */
213                 if (cursor->current > (cursor->limit - 2)) {
214                         cursor->current = NULL;
215                         rc = EFAULT;
216                         goto fail3;
217                 }
218
219                 /* Check we have value data for current item and another tag */
220                 if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
221                         cursor->current = NULL;
222                         rc = EFAULT;
223                         goto fail4;
224                 }
225         }
226
227         return (0);
228
229 fail4:
230         EFSYS_PROBE(fail4);
231 fail3:
232         EFSYS_PROBE(fail3);
233 fail2:
234         EFSYS_PROBE(fail2);
235 fail1:
236         EFSYS_PROBE1(fail1, efx_rc_t, rc);
237
238         return (rc);
239 }
240
241 static                          efx_rc_t
242 tlv_init_cursor(
243         __out   tlv_cursor_t    *cursor,
244         __in    uint32_t        *block,
245         __in    uint32_t        *limit)
246 {
247         cursor->block   = block;
248         cursor->limit   = limit;
249
250         cursor->current = cursor->block;
251         cursor->end     = NULL;
252
253         return (tlv_validate_state(cursor));
254 }
255
256 static                          efx_rc_t
257 tlv_init_cursor_from_size(
258         __out   tlv_cursor_t    *cursor,
259         __in    uint8_t *block,
260         __in    size_t          size)
261 {
262         uint32_t *limit;
263         limit = (uint32_t *)(block + size - sizeof (uint32_t));
264         return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
265 }
266
267 static                          efx_rc_t
268 tlv_require_end(
269         __in    tlv_cursor_t    *cursor)
270 {
271         uint32_t *pos;
272         efx_rc_t rc;
273
274         if (cursor->end == NULL) {
275                 pos = cursor->current;
276                 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
277                         goto fail1;
278
279                 cursor->end = cursor->current;
280                 cursor->current = pos;
281         }
282
283         return (0);
284
285 fail1:
286         EFSYS_PROBE1(fail1, efx_rc_t, rc);
287
288         return (rc);
289 }
290
291 static                          size_t
292 tlv_block_length_used(
293         __in    tlv_cursor_t    *cursor)
294 {
295         efx_rc_t rc;
296
297         if ((rc = tlv_validate_state(cursor)) != 0)
298                 goto fail1;
299
300         if ((rc = tlv_require_end(cursor)) != 0)
301                 goto fail2;
302
303         /* Return space used (including the END tag) */
304         return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
305
306 fail2:
307         EFSYS_PROBE(fail2);
308 fail1:
309         EFSYS_PROBE1(fail1, efx_rc_t, rc);
310
311         return (0);
312 }
313
314
315 static  __checkReturn           uint32_t *
316 tlv_write(
317         __in                    tlv_cursor_t *cursor,
318         __in                    uint32_t tag,
319         __in_bcount(size)       uint8_t *data,
320         __in                    size_t size)
321 {
322         uint32_t len = size;
323         uint32_t *ptr;
324
325         ptr = cursor->current;
326
327         *ptr++ = __CPU_TO_LE_32(tag);
328         *ptr++ = __CPU_TO_LE_32(len);
329
330         if (len > 0) {
331                 ptr[(len - 1) / sizeof (uint32_t)] = 0;
332                 memcpy(ptr, data, len);
333                 ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
334         }
335
336         return (ptr);
337 }
338
339 static  __checkReturn           efx_rc_t
340 tlv_insert(
341         __in    tlv_cursor_t    *cursor,
342         __in    uint32_t        tag,
343         __in    uint8_t         *data,
344         __in    size_t          size)
345 {
346         unsigned int delta;
347         efx_rc_t rc;
348
349         if ((rc = tlv_validate_state(cursor)) != 0)
350                 goto fail1;
351
352         if ((rc = tlv_require_end(cursor)) != 0)
353                 goto fail2;
354
355         if (tag == TLV_TAG_END) {
356                 rc = EINVAL;
357                 goto fail3;
358         }
359
360         delta = TLV_DWORD_COUNT(size);
361         if (cursor->end + 1 + delta > cursor->limit) {
362                 rc = ENOSPC;
363                 goto fail4;
364         }
365
366         /* Move data up: new space at cursor->current */
367         memmove(cursor->current + delta, cursor->current,
368             (cursor->end + 1 - cursor->current) * sizeof (uint32_t));
369
370         /* Adjust the end pointer */
371         cursor->end += delta;
372
373         /* Write new TLV item */
374         tlv_write(cursor, tag, data, size);
375
376         return (0);
377
378 fail4:
379         EFSYS_PROBE(fail4);
380 fail3:
381         EFSYS_PROBE(fail3);
382 fail2:
383         EFSYS_PROBE(fail2);
384 fail1:
385         EFSYS_PROBE1(fail1, efx_rc_t, rc);
386
387         return (rc);
388 }
389
390 static  __checkReturn           efx_rc_t
391 tlv_modify(
392         __in    tlv_cursor_t    *cursor,
393         __in    uint32_t        tag,
394         __in    uint8_t         *data,
395         __in    size_t          size)
396 {
397         uint32_t *pos;
398         unsigned int old_ndwords;
399         unsigned int new_ndwords;
400         unsigned int delta;
401         efx_rc_t rc;
402
403         if ((rc = tlv_validate_state(cursor)) != 0)
404                 goto fail1;
405
406         if (tlv_tag(cursor) == TLV_TAG_END) {
407                 rc = EINVAL;
408                 goto fail2;
409         }
410         if (tlv_tag(cursor) != tag) {
411                 rc = EINVAL;
412                 goto fail3;
413         }
414
415         old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
416         new_ndwords = TLV_DWORD_COUNT(size);
417
418         if ((rc = tlv_require_end(cursor)) != 0)
419                 goto fail4;
420
421         if (new_ndwords > old_ndwords) {
422                 /* Expand space used for TLV item */
423                 delta = new_ndwords - old_ndwords;
424                 pos = cursor->current + old_ndwords;
425
426                 if (cursor->end + 1 + delta > cursor->limit) {
427                         rc = ENOSPC;
428                         goto fail5;
429                 }
430
431                 /* Move up: new space at (cursor->current + old_ndwords) */
432                 memmove(pos + delta, pos,
433                     (cursor->end + 1 - pos) * sizeof (uint32_t));
434
435                 /* Adjust the end pointer */
436                 cursor->end += delta;
437
438         } else if (new_ndwords < old_ndwords) {
439                 /* Shrink space used for TLV item */
440                 delta = old_ndwords - new_ndwords;
441                 pos = cursor->current + new_ndwords;
442
443                 /* Move down: remove words at (cursor->current + new_ndwords) */
444                 memmove(pos, pos + delta,
445                     (cursor->end + 1 - pos) * sizeof (uint32_t));
446
447                 /* Zero the new space at the end of the TLV chain */
448                 memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
449
450                 /* Adjust the end pointer */
451                 cursor->end -= delta;
452         }
453
454         /* Write new data */
455         tlv_write(cursor, tag, data, size);
456
457         return (0);
458
459 fail5:
460         EFSYS_PROBE(fail5);
461 fail4:
462         EFSYS_PROBE(fail4);
463 fail3:
464         EFSYS_PROBE(fail3);
465 fail2:
466         EFSYS_PROBE(fail2);
467 fail1:
468         EFSYS_PROBE1(fail1, efx_rc_t, rc);
469
470         return (rc);
471 }
472
473 /* Validate TLV formatted partition contents (before writing to flash) */
474         __checkReturn           efx_rc_t
475 efx_nvram_tlv_validate(
476         __in                    efx_nic_t *enp,
477         __in                    uint32_t partn,
478         __in_bcount(partn_size) caddr_t partn_data,
479         __in                    size_t partn_size)
480 {
481         tlv_cursor_t cursor;
482         struct tlv_partition_header *header;
483         struct tlv_partition_trailer *trailer;
484         size_t total_length;
485         uint32_t cksum;
486         int pos;
487         efx_rc_t rc;
488
489         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
490
491         if ((partn_data == NULL) || (partn_size == 0)) {
492                 rc = EINVAL;
493                 goto fail1;
494         }
495
496         /* The partition header must be the first item (at offset zero) */
497         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
498                     partn_size)) != 0) {
499                 rc = EFAULT;
500                 goto fail2;
501         }
502         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
503                 rc = EINVAL;
504                 goto fail3;
505         }
506         header = (struct tlv_partition_header *)tlv_item(&cursor);
507
508         /* Check TLV partition length (includes the END tag) */
509         total_length = __LE_TO_CPU_32(header->total_length);
510         if (total_length > partn_size) {
511                 rc = EFBIG;
512                 goto fail4;
513         }
514
515         /* Check partition ends with PARTITION_TRAILER and END tags */
516         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
517                 rc = EINVAL;
518                 goto fail5;
519         }
520         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
521
522         if ((rc = tlv_advance(&cursor)) != 0) {
523                 rc = EINVAL;
524                 goto fail6;
525         }
526         if (tlv_tag(&cursor) != TLV_TAG_END) {
527                 rc = EINVAL;
528                 goto fail7;
529         }
530
531         /* Check generation counts are consistent */
532         if (trailer->generation != header->generation) {
533                 rc = EINVAL;
534                 goto fail8;
535         }
536
537         /* Verify partition checksum */
538         cksum = 0;
539         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
540                 cksum += *((uint32_t *)(partn_data + pos));
541         }
542         if (cksum != 0) {
543                 rc = EINVAL;
544                 goto fail9;
545         }
546
547         return (0);
548
549 fail9:
550         EFSYS_PROBE(fail9);
551 fail8:
552         EFSYS_PROBE(fail8);
553 fail7:
554         EFSYS_PROBE(fail7);
555 fail6:
556         EFSYS_PROBE(fail6);
557 fail5:
558         EFSYS_PROBE(fail5);
559 fail4:
560         EFSYS_PROBE(fail4);
561 fail3:
562         EFSYS_PROBE(fail3);
563 fail2:
564         EFSYS_PROBE(fail2);
565 fail1:
566         EFSYS_PROBE1(fail1, efx_rc_t, rc);
567
568         return (rc);
569 }
570
571 /*
572  * Read and validate a segment from a partition. A segment is a complete
573  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
574  * be multiple segments in a partition, so seg_offset allows segments
575  * beyond the first to be read.
576  */
577 static  __checkReturn                   efx_rc_t
578 ef10_nvram_read_tlv_segment(
579         __in                            efx_nic_t *enp,
580         __in                            uint32_t partn,
581         __in                            size_t seg_offset,
582         __in_bcount(max_seg_size)       caddr_t seg_data,
583         __in                            size_t max_seg_size)
584 {
585         tlv_cursor_t cursor;
586         struct tlv_partition_header *header;
587         struct tlv_partition_trailer *trailer;
588         size_t total_length;
589         uint32_t cksum;
590         int pos;
591         efx_rc_t rc;
592
593         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
594
595         if ((seg_data == NULL) || (max_seg_size == 0)) {
596                 rc = EINVAL;
597                 goto fail1;
598         }
599
600         /* Read initial chunk of the segment, starting at offset */
601         if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
602                     EF10_NVRAM_CHUNK,
603                     MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
604                 goto fail2;
605         }
606
607         /* A PARTITION_HEADER tag must be the first item at the given offset */
608         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
609                     max_seg_size)) != 0) {
610                 rc = EFAULT;
611                 goto fail3;
612         }
613         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
614                 rc = EINVAL;
615                 goto fail4;
616         }
617         header = (struct tlv_partition_header *)tlv_item(&cursor);
618
619         /* Check TLV segment length (includes the END tag) */
620         total_length = __LE_TO_CPU_32(header->total_length);
621         if (total_length > max_seg_size) {
622                 rc = EFBIG;
623                 goto fail5;
624         }
625
626         /* Read the remaining segment content */
627         if (total_length > EF10_NVRAM_CHUNK) {
628                 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
629                             seg_offset + EF10_NVRAM_CHUNK,
630                             seg_data + EF10_NVRAM_CHUNK,
631                             total_length - EF10_NVRAM_CHUNK,
632                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
633                         goto fail6;
634         }
635
636         /* Check segment ends with PARTITION_TRAILER and END tags */
637         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
638                 rc = EINVAL;
639                 goto fail7;
640         }
641         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
642
643         if ((rc = tlv_advance(&cursor)) != 0) {
644                 rc = EINVAL;
645                 goto fail8;
646         }
647         if (tlv_tag(&cursor) != TLV_TAG_END) {
648                 rc = EINVAL;
649                 goto fail9;
650         }
651
652         /* Check data read from segment is consistent */
653         if (trailer->generation != header->generation) {
654                 /*
655                  * The partition data may have been modified between successive
656                  * MCDI NVRAM_READ requests by the MC or another PCI function.
657                  *
658                  * The caller must retry to obtain consistent partition data.
659                  */
660                 rc = EAGAIN;
661                 goto fail10;
662         }
663
664         /* Verify segment checksum */
665         cksum = 0;
666         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
667                 cksum += *((uint32_t *)(seg_data + pos));
668         }
669         if (cksum != 0) {
670                 rc = EINVAL;
671                 goto fail11;
672         }
673
674         return (0);
675
676 fail11:
677         EFSYS_PROBE(fail11);
678 fail10:
679         EFSYS_PROBE(fail10);
680 fail9:
681         EFSYS_PROBE(fail9);
682 fail8:
683         EFSYS_PROBE(fail8);
684 fail7:
685         EFSYS_PROBE(fail7);
686 fail6:
687         EFSYS_PROBE(fail6);
688 fail5:
689         EFSYS_PROBE(fail5);
690 fail4:
691         EFSYS_PROBE(fail4);
692 fail3:
693         EFSYS_PROBE(fail3);
694 fail2:
695         EFSYS_PROBE(fail2);
696 fail1:
697         EFSYS_PROBE1(fail1, efx_rc_t, rc);
698
699         return (rc);
700 }
701
702 /*
703  * Read a single TLV item from a host memory
704  * buffer containing a TLV formatted segment.
705  */
706         __checkReturn           efx_rc_t
707 ef10_nvram_buf_read_tlv(
708         __in                            efx_nic_t *enp,
709         __in_bcount(max_seg_size)       caddr_t seg_data,
710         __in                            size_t max_seg_size,
711         __in                            uint32_t tag,
712         __deref_out_bcount_opt(*sizep)  caddr_t *datap,
713         __out                           size_t *sizep)
714 {
715         tlv_cursor_t cursor;
716         caddr_t data;
717         size_t length;
718         caddr_t value;
719         efx_rc_t rc;
720
721         if ((seg_data == NULL) || (max_seg_size == 0)) {
722                 rc = EINVAL;
723                 goto fail1;
724         }
725
726         /* Find requested TLV tag in segment data */
727         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
728                     max_seg_size)) != 0) {
729                 rc = EFAULT;
730                 goto fail2;
731         }
732         if ((rc = tlv_find(&cursor, tag)) != 0) {
733                 rc = ENOENT;
734                 goto fail3;
735         }
736         value = (caddr_t)tlv_value(&cursor);
737         length = tlv_length(&cursor);
738
739         if (length == 0)
740                 data = NULL;
741         else {
742                 /* Copy out data from TLV item */
743                 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
744                 if (data == NULL) {
745                         rc = ENOMEM;
746                         goto fail4;
747                 }
748                 memcpy(data, value, length);
749         }
750
751         *datap = data;
752         *sizep = length;
753
754         return (0);
755
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 /* Read a single TLV item from the first segment in a TLV formatted partition */
769         __checkReturn           efx_rc_t
770 ef10_nvram_partn_read_tlv(
771         __in                                    efx_nic_t *enp,
772         __in                                    uint32_t partn,
773         __in                                    uint32_t tag,
774         __deref_out_bcount_opt(*seg_sizep)      caddr_t *seg_datap,
775         __out                                   size_t *seg_sizep)
776 {
777         caddr_t seg_data = NULL;
778         size_t partn_size = 0;
779         size_t length;
780         caddr_t data;
781         int retry;
782         efx_rc_t rc;
783
784         /* Allocate sufficient memory for the entire partition */
785         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
786                 goto fail1;
787
788         if (partn_size == 0) {
789                 rc = ENOENT;
790                 goto fail2;
791         }
792
793         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
794         if (seg_data == NULL) {
795                 rc = ENOMEM;
796                 goto fail3;
797         }
798
799         /*
800          * Read the first segment in a TLV partition. Retry until consistent
801          * segment contents are returned. Inconsistent data may be read if:
802          *  a) the segment contents are invalid
803          *  b) the MC has rebooted while we were reading the partition
804          *  c) the partition has been modified while we were reading it
805          * Limit retry attempts to ensure forward progress.
806          */
807         retry = 10;
808         do {
809                 rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
810                     seg_data, partn_size);
811         } while ((rc == EAGAIN) && (--retry > 0));
812
813         if (rc != 0) {
814                 /* Failed to obtain consistent segment data */
815                 goto fail4;
816         }
817
818         if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
819                     tag, &data, &length)) != 0)
820                 goto fail5;
821
822         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
823
824         *seg_datap = data;
825         *seg_sizep = length;
826
827         return (0);
828
829 fail5:
830         EFSYS_PROBE(fail5);
831 fail4:
832         EFSYS_PROBE(fail4);
833
834         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
835 fail3:
836         EFSYS_PROBE(fail3);
837 fail2:
838         EFSYS_PROBE(fail2);
839 fail1:
840         EFSYS_PROBE1(fail1, efx_rc_t, rc);
841
842         return (rc);
843 }
844
845 /* Compute the size of a segment. */
846         static  __checkReturn   efx_rc_t
847 ef10_nvram_buf_segment_size(
848         __in                    caddr_t seg_data,
849         __in                    size_t max_seg_size,
850         __out                   size_t *seg_sizep)
851 {
852         efx_rc_t rc;
853         tlv_cursor_t cursor;
854         struct tlv_partition_header *header;
855         uint32_t cksum;
856         int pos;
857         uint32_t *end_tag_position;
858         uint32_t segment_length;
859
860         /* A PARTITION_HEADER tag must be the first item at the given offset */
861         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
862                     max_seg_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         header = (struct tlv_partition_header *)tlv_item(&cursor);
871
872         /* Check TLV segment length (includes the END tag) */
873         *seg_sizep = __LE_TO_CPU_32(header->total_length);
874         if (*seg_sizep > max_seg_size) {
875                 rc = EFBIG;
876                 goto fail3;
877         }
878
879         /* Check segment ends with PARTITION_TRAILER and END tags */
880         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
881                 rc = EINVAL;
882                 goto fail4;
883         }
884
885         if ((rc = tlv_advance(&cursor)) != 0) {
886                 rc = EINVAL;
887                 goto fail5;
888         }
889         if (tlv_tag(&cursor) != TLV_TAG_END) {
890                 rc = EINVAL;
891                 goto fail6;
892         }
893         end_tag_position = cursor.current;
894
895         /* Verify segment checksum */
896         cksum = 0;
897         for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
898                 cksum += *((uint32_t *)(seg_data + pos));
899         }
900         if (cksum != 0) {
901                 rc = EINVAL;
902                 goto fail7;
903         }
904
905         /*
906          * Calculate total length from HEADER to END tags and compare to
907          * max_seg_size and the total_length field in the HEADER tag.
908          */
909         segment_length = tlv_block_length_used(&cursor);
910
911         if (segment_length > max_seg_size) {
912                 rc = EINVAL;
913                 goto fail8;
914         }
915
916         if (segment_length != *seg_sizep) {
917                 rc = EINVAL;
918                 goto fail9;
919         }
920
921         /* Skip over the first HEADER tag. */
922         rc = tlv_rewind(&cursor);
923         rc = tlv_advance(&cursor);
924
925         while (rc == 0) {
926                 if (tlv_tag(&cursor) == TLV_TAG_END) {
927                         /* Check that the END tag is the one found earlier. */
928                         if (cursor.current != end_tag_position)
929                                 goto fail10;
930                         break;
931                 }
932                 /* Check for duplicate HEADER tags before the END tag. */
933                 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
934                         rc = EINVAL;
935                         goto fail11;
936                 }
937
938                 rc = tlv_advance(&cursor);
939         }
940         if (rc != 0)
941                 goto fail12;
942
943         return (0);
944
945 fail12:
946         EFSYS_PROBE(fail12);
947 fail11:
948         EFSYS_PROBE(fail11);
949 fail10:
950         EFSYS_PROBE(fail10);
951 fail9:
952         EFSYS_PROBE(fail9);
953 fail8:
954         EFSYS_PROBE(fail8);
955 fail7:
956         EFSYS_PROBE(fail7);
957 fail6:
958         EFSYS_PROBE(fail6);
959 fail5:
960         EFSYS_PROBE(fail5);
961 fail4:
962         EFSYS_PROBE(fail4);
963 fail3:
964         EFSYS_PROBE(fail3);
965 fail2:
966         EFSYS_PROBE(fail2);
967 fail1:
968         EFSYS_PROBE1(fail1, efx_rc_t, rc);
969
970         return (rc);
971 }
972
973 /*
974  * Add or update a single TLV item in a host memory buffer containing a TLV
975  * formatted segment. Historically partitions consisted of only one segment.
976  */
977         __checkReturn                   efx_rc_t
978 ef10_nvram_buf_write_tlv(
979         __inout_bcount(max_seg_size)    caddr_t seg_data,
980         __in                            size_t max_seg_size,
981         __in                            uint32_t tag,
982         __in_bcount(tag_size)           caddr_t tag_data,
983         __in                            size_t tag_size,
984         __out                           size_t *total_lengthp)
985 {
986         tlv_cursor_t cursor;
987         struct tlv_partition_header *header;
988         struct tlv_partition_trailer *trailer;
989         uint32_t generation;
990         uint32_t cksum;
991         int pos;
992         efx_rc_t rc;
993
994         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
995         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
996                         max_seg_size)) != 0) {
997                 rc = EFAULT;
998                 goto fail1;
999         }
1000         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1001                 rc = EINVAL;
1002                 goto fail2;
1003         }
1004         header = (struct tlv_partition_header *)tlv_item(&cursor);
1005
1006         /* Update the TLV chain to contain the new data */
1007         if ((rc = tlv_find(&cursor, tag)) == 0) {
1008                 /* Modify existing TLV item */
1009                 if ((rc = tlv_modify(&cursor, tag,
1010                             (uint8_t *)tag_data, tag_size)) != 0)
1011                         goto fail3;
1012         } else {
1013                 /* Insert a new TLV item before the PARTITION_TRAILER */
1014                 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1015                 if (rc != 0) {
1016                         rc = EINVAL;
1017                         goto fail4;
1018                 }
1019                 if ((rc = tlv_insert(&cursor, tag,
1020                             (uint8_t *)tag_data, tag_size)) != 0) {
1021                         rc = EINVAL;
1022                         goto fail5;
1023                 }
1024         }
1025
1026         /* Find the trailer tag */
1027         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1028                 rc = EINVAL;
1029                 goto fail6;
1030         }
1031         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1032
1033         /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1034         *total_lengthp = tlv_block_length_used(&cursor);
1035         if (*total_lengthp > max_seg_size) {
1036                 rc = ENOSPC;
1037                 goto fail7;
1038         }
1039         generation = __LE_TO_CPU_32(header->generation) + 1;
1040
1041         header->total_length    = __CPU_TO_LE_32(*total_lengthp);
1042         header->generation      = __CPU_TO_LE_32(generation);
1043         trailer->generation     = __CPU_TO_LE_32(generation);
1044
1045         /* Recompute PARTITION_TRAILER checksum */
1046         trailer->checksum = 0;
1047         cksum = 0;
1048         for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1049                 cksum += *((uint32_t *)(seg_data + pos));
1050         }
1051         trailer->checksum = ~cksum + 1;
1052
1053         return (0);
1054
1055 fail7:
1056         EFSYS_PROBE(fail7);
1057 fail6:
1058         EFSYS_PROBE(fail6);
1059 fail5:
1060         EFSYS_PROBE(fail5);
1061 fail4:
1062         EFSYS_PROBE(fail4);
1063 fail3:
1064         EFSYS_PROBE(fail3);
1065 fail2:
1066         EFSYS_PROBE(fail2);
1067 fail1:
1068         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1069
1070         return (rc);
1071 }
1072
1073 /*
1074  * Add or update a single TLV item in the first segment of a TLV formatted
1075  * dynamic config partition. The first segment is the current active
1076  * configuration.
1077  */
1078         __checkReturn           efx_rc_t
1079 ef10_nvram_partn_write_tlv(
1080         __in                    efx_nic_t *enp,
1081         __in                    uint32_t partn,
1082         __in                    uint32_t tag,
1083         __in_bcount(size)       caddr_t data,
1084         __in                    size_t size)
1085 {
1086         return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1087             size, B_FALSE);
1088 }
1089
1090 /*
1091  * Read a segment from nvram at the given offset into a buffer (segment_data)
1092  * and optionally write a new tag to it.
1093  */
1094         static  __checkReturn   efx_rc_t
1095 ef10_nvram_segment_write_tlv(
1096         __in                    efx_nic_t *enp,
1097         __in                    uint32_t partn,
1098         __in                    uint32_t tag,
1099         __in_bcount(size)       caddr_t data,
1100         __in                    size_t size,
1101         __inout                 caddr_t *seg_datap,
1102         __inout                 size_t *partn_offsetp,
1103         __inout                 size_t *src_remain_lenp,
1104         __inout                 size_t *dest_remain_lenp,
1105         __in                    boolean_t write)
1106 {
1107         efx_rc_t rc;
1108         efx_rc_t status;
1109         size_t original_segment_size;
1110         size_t modified_segment_size;
1111
1112         /*
1113          * Read the segment from NVRAM into the segment_data buffer and validate
1114          * it, returning if it does not validate. This is not a failure unless
1115          * this is the first segment in a partition. In this case the caller
1116          * must propogate the error.
1117          */
1118         status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1119             *seg_datap, *src_remain_lenp);
1120         if (status != 0)
1121                 return (EINVAL);
1122
1123         status = ef10_nvram_buf_segment_size(*seg_datap,
1124             *src_remain_lenp, &original_segment_size);
1125         if (status != 0)
1126                 return (EINVAL);
1127
1128         if (write) {
1129                 /* Update the contents of the segment in the buffer */
1130                 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1131                         *dest_remain_lenp, tag, data, size,
1132                         &modified_segment_size)) != 0)
1133                         goto fail1;
1134                 *dest_remain_lenp -= modified_segment_size;
1135                 *seg_datap += modified_segment_size;
1136         } else {
1137                 /*
1138                  * We won't modify this segment, but still need to update the
1139                  * remaining lengths and pointers.
1140                  */
1141                 *dest_remain_lenp -= original_segment_size;
1142                 *seg_datap += original_segment_size;
1143         }
1144
1145         *partn_offsetp += original_segment_size;
1146         *src_remain_lenp -= original_segment_size;
1147
1148         return (0);
1149
1150 fail1:
1151         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1152
1153         return (rc);
1154 }
1155
1156 /*
1157  * Add or update a single TLV item in either the first segment or in all
1158  * segments in a TLV formatted dynamic config partition. Dynamic config
1159  * partitions on boards that support RFID are divided into a number of segments,
1160  * each formatted like a partition, with header, trailer and end tags. The first
1161  * segment is the current active configuration.
1162  *
1163  * The segments are initialised by manftest and each contain a different
1164  * configuration e.g. firmware variant. The firmware can be instructed
1165  * via RFID to copy a segment to replace the first segment, hence changing the
1166  * active configuration.  This allows ops to change the configuration of a board
1167  * prior to shipment using RFID.
1168  *
1169  * Changes to the dynamic config may need to be written to all segments (e.g.
1170  * firmware versions) or just the first segment (changes to the active
1171  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1172  * If only the first segment is written the code still needs to be aware of the
1173  * possible presence of subsequent segments as writing to a segment may cause
1174  * its size to increase, which would overwrite the subsequent segments and
1175  * invalidate them.
1176  */
1177         __checkReturn           efx_rc_t
1178 ef10_nvram_partn_write_segment_tlv(
1179         __in                    efx_nic_t *enp,
1180         __in                    uint32_t partn,
1181         __in                    uint32_t tag,
1182         __in_bcount(size)       caddr_t data,
1183         __in                    size_t size,
1184         __in                    boolean_t all_segments)
1185 {
1186         size_t partn_size = 0;
1187         caddr_t partn_data;
1188         size_t total_length = 0;
1189         efx_rc_t rc;
1190         size_t current_offset = 0;
1191         size_t remaining_original_length;
1192         size_t remaining_modified_length;
1193         caddr_t segment_data;
1194
1195         EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1196
1197         /* Allocate sufficient memory for the entire partition */
1198         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1199                 goto fail1;
1200
1201         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1202         if (partn_data == NULL) {
1203                 rc = ENOMEM;
1204                 goto fail2;
1205         }
1206
1207         remaining_original_length = partn_size;
1208         remaining_modified_length = partn_size;
1209         segment_data = partn_data;
1210
1211         /* Lock the partition */
1212         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1213                 goto fail3;
1214
1215         /* Iterate over each (potential) segment to update it. */
1216         do {
1217                 boolean_t write = all_segments || current_offset == 0;
1218
1219                 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1220                     &segment_data, &current_offset, &remaining_original_length,
1221                     &remaining_modified_length, write);
1222                 if (rc != 0) {
1223                         if (current_offset == 0) {
1224                                 /*
1225                                  * If no data has been read then the first
1226                                  * segment is invalid, which is an error.
1227                                  */
1228                                 goto fail4;
1229                         }
1230                         break;
1231                 }
1232         } while (current_offset < partn_size);
1233
1234         total_length = segment_data - partn_data;
1235
1236         /*
1237          * We've run out of space.  This should actually be dealt with by
1238          * ef10_nvram_buf_write_tlv returning ENOSPC.
1239          */
1240         if (total_length > partn_size) {
1241                 rc = ENOSPC;
1242                 goto fail5;
1243         }
1244
1245         /* Erase the whole partition in NVRAM */
1246         if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1247                 goto fail6;
1248
1249         /* Write new partition contents from the buffer to NVRAM */
1250         if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1251                     total_length)) != 0)
1252                 goto fail7;
1253
1254         /* Unlock the partition */
1255         ef10_nvram_partn_unlock(enp, partn);
1256
1257         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1258
1259         return (0);
1260
1261 fail7:
1262         EFSYS_PROBE(fail7);
1263 fail6:
1264         EFSYS_PROBE(fail6);
1265 fail5:
1266         EFSYS_PROBE(fail5);
1267 fail4:
1268         EFSYS_PROBE(fail4);
1269
1270         ef10_nvram_partn_unlock(enp, partn);
1271 fail3:
1272         EFSYS_PROBE(fail3);
1273
1274         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1275 fail2:
1276         EFSYS_PROBE(fail2);
1277 fail1:
1278         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1279
1280         return (rc);
1281 }
1282
1283 /*
1284  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1285  * not the data used by the segments in the partition.
1286  */
1287         __checkReturn           efx_rc_t
1288 ef10_nvram_partn_size(
1289         __in                    efx_nic_t *enp,
1290         __in                    uint32_t partn,
1291         __out                   size_t *sizep)
1292 {
1293         efx_rc_t rc;
1294
1295         if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1296             NULL, NULL, NULL)) != 0)
1297                 goto fail1;
1298
1299         return (0);
1300
1301 fail1:
1302         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1303
1304         return (rc);
1305 }
1306
1307         __checkReturn           efx_rc_t
1308 ef10_nvram_partn_lock(
1309         __in                    efx_nic_t *enp,
1310         __in                    uint32_t partn)
1311 {
1312         efx_rc_t rc;
1313
1314         if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1315                 goto fail1;
1316
1317         return (0);
1318
1319 fail1:
1320         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1321
1322         return (rc);
1323 }
1324
1325         __checkReturn           efx_rc_t
1326 ef10_nvram_partn_read_mode(
1327         __in                    efx_nic_t *enp,
1328         __in                    uint32_t partn,
1329         __in                    unsigned int offset,
1330         __out_bcount(size)      caddr_t data,
1331         __in                    size_t size,
1332         __in                    uint32_t mode)
1333 {
1334         size_t chunk;
1335         efx_rc_t rc;
1336
1337         while (size > 0) {
1338                 chunk = MIN(size, EF10_NVRAM_CHUNK);
1339
1340                 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1341                             data, chunk, mode)) != 0) {
1342                         goto fail1;
1343                 }
1344
1345                 size -= chunk;
1346                 data += chunk;
1347                 offset += chunk;
1348         }
1349
1350         return (0);
1351
1352 fail1:
1353         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1354
1355         return (rc);
1356 }
1357
1358         __checkReturn           efx_rc_t
1359 ef10_nvram_partn_read(
1360         __in                    efx_nic_t *enp,
1361         __in                    uint32_t partn,
1362         __in                    unsigned int offset,
1363         __out_bcount(size)      caddr_t data,
1364         __in                    size_t size)
1365 {
1366         /*
1367          * Read requests which come in through the EFX API expect to
1368          * read the current, active partition.
1369          */
1370         return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1371                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1372 }
1373
1374         __checkReturn           efx_rc_t
1375 ef10_nvram_partn_erase(
1376         __in                    efx_nic_t *enp,
1377         __in                    uint32_t partn,
1378         __in                    unsigned int offset,
1379         __in                    size_t size)
1380 {
1381         efx_rc_t rc;
1382         uint32_t erase_size;
1383
1384         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1385             &erase_size, NULL)) != 0)
1386                 goto fail1;
1387
1388         if (erase_size == 0) {
1389                 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1390                         goto fail2;
1391         } else {
1392                 if (size % erase_size != 0) {
1393                         rc = EINVAL;
1394                         goto fail3;
1395                 }
1396                 while (size > 0) {
1397                         if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1398                             erase_size)) != 0)
1399                                 goto fail4;
1400                         offset += erase_size;
1401                         size -= erase_size;
1402                 }
1403         }
1404
1405         return (0);
1406
1407 fail4:
1408         EFSYS_PROBE(fail4);
1409 fail3:
1410         EFSYS_PROBE(fail3);
1411 fail2:
1412         EFSYS_PROBE(fail2);
1413 fail1:
1414         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1415
1416         return (rc);
1417 }
1418
1419         __checkReturn           efx_rc_t
1420 ef10_nvram_partn_write(
1421         __in                    efx_nic_t *enp,
1422         __in                    uint32_t partn,
1423         __in                    unsigned int offset,
1424         __out_bcount(size)      caddr_t data,
1425         __in                    size_t size)
1426 {
1427         size_t chunk;
1428         uint32_t write_size;
1429         efx_rc_t rc;
1430
1431         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1432             NULL, &write_size)) != 0)
1433                 goto fail1;
1434
1435         if (write_size != 0) {
1436                 /*
1437                  * Check that the size is a multiple of the write chunk size if
1438                  * the write chunk size is available.
1439                  */
1440                 if (size % write_size != 0) {
1441                         rc = EINVAL;
1442                         goto fail2;
1443                 }
1444         } else {
1445                 write_size = EF10_NVRAM_CHUNK;
1446         }
1447
1448         while (size > 0) {
1449                 chunk = MIN(size, write_size);
1450
1451                 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1452                             data, chunk)) != 0) {
1453                         goto fail3;
1454                 }
1455
1456                 size -= chunk;
1457                 data += chunk;
1458                 offset += chunk;
1459         }
1460
1461         return (0);
1462
1463 fail3:
1464         EFSYS_PROBE(fail3);
1465 fail2:
1466         EFSYS_PROBE(fail2);
1467 fail1:
1468         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1469
1470         return (rc);
1471 }
1472
1473                                 void
1474 ef10_nvram_partn_unlock(
1475         __in                    efx_nic_t *enp,
1476         __in                    uint32_t partn)
1477 {
1478         boolean_t reboot;
1479         efx_rc_t rc;
1480
1481         reboot = B_FALSE;
1482         if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
1483                 goto fail1;
1484
1485         return;
1486
1487 fail1:
1488         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1489 }
1490
1491         __checkReturn           efx_rc_t
1492 ef10_nvram_partn_set_version(
1493         __in                    efx_nic_t *enp,
1494         __in                    uint32_t partn,
1495         __in_ecount(4)          uint16_t version[4])
1496 {
1497         struct tlv_partition_version partn_version;
1498         size_t size;
1499         efx_rc_t rc;
1500
1501         /* Add or modify partition version TLV item */
1502         partn_version.version_w = __CPU_TO_LE_16(version[0]);
1503         partn_version.version_x = __CPU_TO_LE_16(version[1]);
1504         partn_version.version_y = __CPU_TO_LE_16(version[2]);
1505         partn_version.version_z = __CPU_TO_LE_16(version[3]);
1506
1507         size = sizeof (partn_version) - (2 * sizeof (uint32_t));
1508
1509         /* Write the version number to all segments in the partition */
1510         if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
1511                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
1512                     TLV_TAG_PARTITION_VERSION(partn),
1513                     (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
1514                 goto fail1;
1515
1516         return (0);
1517
1518 fail1:
1519         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1520
1521         return (rc);
1522 }
1523
1524 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
1525
1526 #if EFSYS_OPT_NVRAM
1527
1528 typedef struct ef10_parttbl_entry_s {
1529         unsigned int            partn;
1530         unsigned int            port;
1531         efx_nvram_type_t        nvtype;
1532 } ef10_parttbl_entry_t;
1533
1534 /* Translate EFX NVRAM types to firmware partition types */
1535 static ef10_parttbl_entry_t hunt_parttbl[] = {
1536         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
1537         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
1538         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
1539         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
1540         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
1541         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
1542         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
1543         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
1544         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
1545         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
1546         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
1547         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
1548         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1549         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
1550         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
1551         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
1552         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
1553         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
1554         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
1555         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
1556         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
1557         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
1558         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
1559         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
1560         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
1561         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
1562         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
1563         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
1564         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
1565         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
1566         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
1567         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE}
1568 };
1569
1570 static ef10_parttbl_entry_t medford_parttbl[] = {
1571         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
1572         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
1573         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
1574         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
1575         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
1576         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
1577         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
1578         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
1579         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
1580         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
1581         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
1582         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
1583         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1584         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
1585         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
1586         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
1587         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
1588         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
1589         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
1590         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
1591         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
1592         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
1593         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
1594         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
1595         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
1596         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
1597         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
1598         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
1599         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
1600         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
1601         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
1602         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE}
1603 };
1604
1605 static  __checkReturn           efx_rc_t
1606 ef10_parttbl_get(
1607         __in                    efx_nic_t *enp,
1608         __out                   ef10_parttbl_entry_t **parttblp,
1609         __out                   size_t *parttbl_rowsp)
1610 {
1611         switch (enp->en_family) {
1612         case EFX_FAMILY_HUNTINGTON:
1613                 *parttblp = hunt_parttbl;
1614                 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
1615                 break;
1616
1617         case EFX_FAMILY_MEDFORD:
1618                 *parttblp = medford_parttbl;
1619                 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
1620                 break;
1621
1622         default:
1623                 EFSYS_ASSERT(B_FALSE);
1624                 return (EINVAL);
1625         }
1626         return (0);
1627 }
1628
1629         __checkReturn           efx_rc_t
1630 ef10_nvram_type_to_partn(
1631         __in                    efx_nic_t *enp,
1632         __in                    efx_nvram_type_t type,
1633         __out                   uint32_t *partnp)
1634 {
1635         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1636         ef10_parttbl_entry_t *parttbl = NULL;
1637         size_t parttbl_rows = 0;
1638         unsigned int i;
1639
1640         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
1641         EFSYS_ASSERT(partnp != NULL);
1642
1643         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1644                 for (i = 0; i < parttbl_rows; i++) {
1645                         ef10_parttbl_entry_t *entry = &parttbl[i];
1646
1647                         if (entry->nvtype == type &&
1648                             entry->port == emip->emi_port) {
1649                                 *partnp = entry->partn;
1650                                 return (0);
1651                         }
1652                 }
1653         }
1654
1655         return (ENOTSUP);
1656 }
1657
1658 #if EFSYS_OPT_DIAG
1659
1660 static  __checkReturn           efx_rc_t
1661 ef10_nvram_partn_to_type(
1662         __in                    efx_nic_t *enp,
1663         __in                    uint32_t partn,
1664         __out                   efx_nvram_type_t *typep)
1665 {
1666         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1667         ef10_parttbl_entry_t *parttbl = NULL;
1668         size_t parttbl_rows = 0;
1669         unsigned int i;
1670
1671         EFSYS_ASSERT(typep != NULL);
1672
1673         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1674                 for (i = 0; i < parttbl_rows; i++) {
1675                         ef10_parttbl_entry_t *entry = &parttbl[i];
1676
1677                         if (entry->partn == partn &&
1678                             entry->port == emip->emi_port) {
1679                                 *typep = entry->nvtype;
1680                                 return (0);
1681                         }
1682                 }
1683         }
1684
1685         return (ENOTSUP);
1686 }
1687
1688         __checkReturn           efx_rc_t
1689 ef10_nvram_test(
1690         __in                    efx_nic_t *enp)
1691 {
1692         efx_nvram_type_t type;
1693         unsigned int npartns = 0;
1694         uint32_t *partns = NULL;
1695         size_t size;
1696         unsigned int i;
1697         efx_rc_t rc;
1698
1699         /* Read available partitions from NVRAM partition map */
1700         size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
1701         EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
1702         if (partns == NULL) {
1703                 rc = ENOMEM;
1704                 goto fail1;
1705         }
1706
1707         if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
1708                     &npartns)) != 0) {
1709                 goto fail2;
1710         }
1711
1712         for (i = 0; i < npartns; i++) {
1713                 /* Check if the partition is supported for this port */
1714                 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
1715                         continue;
1716
1717                 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
1718                         goto fail3;
1719         }
1720
1721         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1722         return (0);
1723
1724 fail3:
1725         EFSYS_PROBE(fail3);
1726 fail2:
1727         EFSYS_PROBE(fail2);
1728         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1729 fail1:
1730         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1731         return (rc);
1732 }
1733
1734 #endif  /* EFSYS_OPT_DIAG */
1735
1736         __checkReturn           efx_rc_t
1737 ef10_nvram_partn_get_version(
1738         __in                    efx_nic_t *enp,
1739         __in                    uint32_t partn,
1740         __out                   uint32_t *subtypep,
1741         __out_ecount(4)         uint16_t version[4])
1742 {
1743         efx_rc_t rc;
1744
1745         /* FIXME: get highest partn version from all ports */
1746         /* FIXME: return partn description if available */
1747
1748         if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
1749                     version, NULL, 0)) != 0)
1750                 goto fail1;
1751
1752         return (0);
1753
1754 fail1:
1755         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1756
1757         return (rc);
1758 }
1759
1760         __checkReturn           efx_rc_t
1761 ef10_nvram_partn_rw_start(
1762         __in                    efx_nic_t *enp,
1763         __in                    uint32_t partn,
1764         __out                   size_t *chunk_sizep)
1765 {
1766         efx_rc_t rc;
1767
1768         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1769                 goto fail1;
1770
1771         if (chunk_sizep != NULL)
1772                 *chunk_sizep = EF10_NVRAM_CHUNK;
1773
1774         return (0);
1775
1776 fail1:
1777         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1778
1779         return (rc);
1780 }
1781
1782                                 void
1783 ef10_nvram_partn_rw_finish(
1784         __in                    efx_nic_t *enp,
1785         __in                    uint32_t partn)
1786 {
1787         ef10_nvram_partn_unlock(enp, partn);
1788 }
1789
1790 #endif  /* EFSYS_OPT_NVRAM */
1791
1792 #endif  /* EFSYS_OPT_HUNTINGTON */