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