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