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
tlv_tag(__in tlv_cursor_t * cursor)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
tlv_length(__in tlv_cursor_t * cursor)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 *
tlv_value(__in tlv_cursor_t * cursor)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 *
tlv_item(__in tlv_cursor_t * cursor)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 *
tlv_next_item_ptr(__in tlv_cursor_t * cursor)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
tlv_advance(__in tlv_cursor_t * cursor)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
tlv_rewind(__in tlv_cursor_t * cursor)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
tlv_find(__in tlv_cursor_t * cursor,__in uint32_t tag)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
tlv_validate_state(__in tlv_cursor_t * cursor)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
tlv_init_cursor(__in tlv_cursor_t * cursor,__in uint32_t * block,__in uint32_t * limit)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
tlv_init_cursor_from_size(__in tlv_cursor_t * cursor,__in uint8_t * block,__in size_t size)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
tlv_require_end(__in tlv_cursor_t * cursor)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
tlv_block_length_used(__in tlv_cursor_t * cursor)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 *
tlv_write(__in tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)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
tlv_insert(__in tlv_cursor_t * cursor,__in uint32_t tag,__in uint8_t * data,__in size_t size)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
tlv_modify(__in tlv_cursor_t * cursor,__in uint32_t tag,__in uint8_t * data,__in size_t size)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
efx_nvram_tlv_validate(__in efx_nic_t * enp,__in uint32_t partn,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)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
hunt_nvram_read_tlv_segment(__in efx_nic_t * enp,__in uint32_t partn,__in size_t seg_offset,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size)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
hunt_nvram_buf_read_tlv(__in efx_nic_t * enp,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__deref_out_bcount_opt (* sizep)caddr_t * datap,__out size_t * sizep)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
hunt_nvram_partn_read_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__deref_out_bcount_opt (* seg_sizep)caddr_t * seg_datap,__out size_t * seg_sizep)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
hunt_nvram_buf_segment_size(__in caddr_t seg_data,__in size_t max_seg_size,__out size_t * seg_sizep)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
hunt_nvram_buf_write_tlv(__inout_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__in_bcount (tag_size)caddr_t tag_data,__in size_t tag_size,__out size_t * total_lengthp)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
hunt_nvram_partn_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size)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
hunt_nvram_segment_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__inout caddr_t * seg_datap,__inout size_t * partn_offsetp,__inout size_t * src_remain_lenp,__inout size_t * dest_remain_lenp,__in boolean_t write)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
hunt_nvram_partn_write_segment_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__in boolean_t all_segments)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, ¤t_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
hunt_nvram_partn_size(__in efx_nic_t * enp,__in unsigned int partn,__out size_t * sizep)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
hunt_nvram_partn_lock(__in efx_nic_t * enp,__in unsigned int partn)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
hunt_nvram_partn_read(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)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
hunt_nvram_partn_erase(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__in size_t size)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
hunt_nvram_partn_write(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)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
hunt_nvram_partn_unlock(__in efx_nic_t * enp,__in unsigned int partn)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 *
hunt_parttbl_entry(__in efx_nic_t * enp,__in efx_nvram_type_t type)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
hunt_nvram_test(__in efx_nic_t * enp)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
hunt_nvram_size(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out size_t * sizep)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
hunt_nvram_rw_start(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out size_t * chunk_sizep)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
hunt_nvram_read_chunk(__in efx_nic_t * enp,__in efx_nvram_type_t type,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)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
hunt_nvram_erase(__in efx_nic_t * enp,__in efx_nvram_type_t type)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
hunt_nvram_write_chunk(__in efx_nic_t * enp,__in efx_nvram_type_t type,__in unsigned int offset,__in_bcount (size)caddr_t data,__in size_t size)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
hunt_nvram_rw_finish(__in efx_nic_t * enp,__in efx_nvram_type_t type)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 */
1867