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