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