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