1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2009-2016 Solarflare Communications Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * The views and conclusions contained in the software and documentation are 29 * those of the authors and should not be interpreted as representing official 30 * policies, either expressed or implied, of the FreeBSD Project. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "efx.h" 37 #include "efx_impl.h" 38 39 #if EFSYS_OPT_BOOTCFG 40 41 /* 42 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. 43 * NOTE: This is larger than the Medford per-PF bootcfg sector. 44 */ 45 #define BOOTCFG_MAX_SIZE 0x1000 46 47 /* Medford per-PF bootcfg sector */ 48 #define BOOTCFG_PER_PF 0x800 49 #define BOOTCFG_PF_COUNT 16 50 51 #define DHCP_END ((uint8_t)0xff) 52 #define DHCP_PAD ((uint8_t)0) 53 54 55 /* Report the layout of bootcfg sectors in NVRAM partition. */ 56 __checkReturn efx_rc_t 57 efx_bootcfg_sector_info( 58 __in efx_nic_t *enp, 59 __in uint32_t pf, 60 __out_opt uint32_t *sector_countp, 61 __out size_t *offsetp, 62 __out size_t *max_sizep) 63 { 64 uint32_t count; 65 size_t max_size; 66 size_t offset; 67 int rc; 68 69 switch (enp->en_family) { 70 #if EFSYS_OPT_SIENA 71 case EFX_FAMILY_SIENA: 72 max_size = BOOTCFG_MAX_SIZE; 73 offset = 0; 74 count = 1; 75 break; 76 #endif /* EFSYS_OPT_SIENA */ 77 78 #if EFSYS_OPT_HUNTINGTON 79 case EFX_FAMILY_HUNTINGTON: 80 max_size = BOOTCFG_MAX_SIZE; 81 offset = 0; 82 count = 1; 83 break; 84 #endif /* EFSYS_OPT_HUNTINGTON */ 85 86 #if EFSYS_OPT_MEDFORD 87 case EFX_FAMILY_MEDFORD: { 88 /* Shared partition (array indexed by PF) */ 89 max_size = BOOTCFG_PER_PF; 90 count = BOOTCFG_PF_COUNT; 91 if (pf >= count) { 92 rc = EINVAL; 93 goto fail2; 94 } 95 offset = max_size * pf; 96 break; 97 } 98 #endif /* EFSYS_OPT_MEDFORD */ 99 100 default: 101 EFSYS_ASSERT(0); 102 rc = ENOTSUP; 103 goto fail1; 104 } 105 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 106 107 if (sector_countp != NULL) 108 *sector_countp = count; 109 *offsetp = offset; 110 *max_sizep = max_size; 111 112 return (0); 113 114 #if EFSYS_OPT_MEDFORD 115 fail2: 116 EFSYS_PROBE(fail2); 117 #endif 118 fail1: 119 EFSYS_PROBE1(fail1, efx_rc_t, rc); 120 return (rc); 121 } 122 123 124 static __checkReturn uint8_t 125 efx_bootcfg_csum( 126 __in efx_nic_t *enp, 127 __in_bcount(size) uint8_t const *data, 128 __in size_t size) 129 { 130 _NOTE(ARGUNUSED(enp)) 131 132 unsigned int pos; 133 uint8_t checksum = 0; 134 135 for (pos = 0; pos < size; pos++) 136 checksum += data[pos]; 137 return (checksum); 138 } 139 140 static __checkReturn efx_rc_t 141 efx_bootcfg_verify( 142 __in efx_nic_t *enp, 143 __in_bcount(size) uint8_t const *data, 144 __in size_t size, 145 __out_opt size_t *usedp) 146 { 147 size_t offset = 0; 148 size_t used = 0; 149 efx_rc_t rc; 150 151 /* Start parsing tags immediately after the checksum */ 152 for (offset = 1; offset < size; ) { 153 uint8_t tag; 154 uint8_t length; 155 156 /* Consume tag */ 157 tag = data[offset]; 158 if (tag == DHCP_END) { 159 offset++; 160 used = offset; 161 break; 162 } 163 if (tag == DHCP_PAD) { 164 offset++; 165 continue; 166 } 167 168 /* Consume length */ 169 if (offset + 1 >= size) { 170 rc = ENOSPC; 171 goto fail1; 172 } 173 length = data[offset + 1]; 174 175 /* Consume *length */ 176 if (offset + 1 + length >= size) { 177 rc = ENOSPC; 178 goto fail2; 179 } 180 181 offset += 2 + length; 182 used = offset; 183 } 184 185 /* Checksum the entire sector, including bytes after any DHCP_END */ 186 if (efx_bootcfg_csum(enp, data, size) != 0) { 187 rc = EINVAL; 188 goto fail3; 189 } 190 191 if (usedp != NULL) 192 *usedp = used; 193 194 return (0); 195 196 fail3: 197 EFSYS_PROBE(fail3); 198 fail2: 199 EFSYS_PROBE(fail2); 200 fail1: 201 EFSYS_PROBE1(fail1, efx_rc_t, rc); 202 203 return (rc); 204 } 205 206 /* 207 * Copy bootcfg sector data to a target buffer which may differ in size. 208 * Optionally corrects format errors in source buffer. 209 */ 210 efx_rc_t 211 efx_bootcfg_copy_sector( 212 __in efx_nic_t *enp, 213 __inout_bcount(sector_length) 214 uint8_t *sector, 215 __in size_t sector_length, 216 __out_bcount(data_size) uint8_t *data, 217 __in size_t data_size, 218 __in boolean_t handle_format_errors) 219 { 220 size_t used_bytes; 221 efx_rc_t rc; 222 223 /* Verify that the area is correctly formatted and checksummed */ 224 rc = efx_bootcfg_verify(enp, sector, sector_length, 225 &used_bytes); 226 227 if (!handle_format_errors) { 228 if (rc != 0) 229 goto fail1; 230 231 if ((used_bytes < 2) || 232 (sector[used_bytes - 1] != DHCP_END)) { 233 /* Block too short, or DHCP_END missing */ 234 rc = ENOENT; 235 goto fail2; 236 } 237 } 238 239 /* Synthesize empty format on verification failure */ 240 if (rc != 0 || used_bytes == 0) { 241 sector[0] = 0; 242 sector[1] = DHCP_END; 243 used_bytes = 2; 244 } 245 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 246 EFSYS_ASSERT(used_bytes <= sector_length); 247 EFSYS_ASSERT(sector_length >= 2); 248 249 /* 250 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 251 * Modify the returned payload so it does. 252 * Reinitialise the sector if there isn't room for the character. 253 */ 254 if (sector[used_bytes - 1] != DHCP_END) { 255 if (used_bytes >= sector_length) { 256 sector[0] = 0; 257 used_bytes = 1; 258 } 259 sector[used_bytes] = DHCP_END; 260 ++used_bytes; 261 } 262 263 /* 264 * Verify that the target buffer is large enough for the 265 * entire used bootcfg area, then copy into the target buffer. 266 */ 267 if (used_bytes > data_size) { 268 rc = ENOSPC; 269 goto fail3; 270 } 271 memcpy(data, sector, used_bytes); 272 273 /* Zero out the unused portion of the target buffer */ 274 if (used_bytes < data_size) 275 (void) memset(data + used_bytes, 0, data_size - used_bytes); 276 277 /* 278 * The checksum includes trailing data after any DHCP_END character, 279 * which we've just modified (by truncation or appending DHCP_END). 280 */ 281 data[0] -= efx_bootcfg_csum(enp, data, data_size); 282 283 return (0); 284 285 fail3: 286 EFSYS_PROBE(fail3); 287 fail2: 288 EFSYS_PROBE(fail2); 289 fail1: 290 EFSYS_PROBE1(fail1, efx_rc_t, rc); 291 292 return (rc); 293 } 294 295 efx_rc_t 296 efx_bootcfg_read( 297 __in efx_nic_t *enp, 298 __out_bcount(size) caddr_t data, 299 __in size_t size) 300 { 301 uint8_t *payload = NULL; 302 size_t used_bytes; 303 size_t partn_length; 304 size_t sector_length; 305 size_t sector_offset; 306 efx_rc_t rc; 307 uint32_t sector_number; 308 309 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 310 sector_number = enp->en_nic_cfg.enc_pf; 311 #else 312 sector_number = 0; 313 #endif 314 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 315 if (rc != 0) 316 goto fail1; 317 318 /* The bootcfg sector may be stored in a (larger) shared partition */ 319 rc = efx_bootcfg_sector_info(enp, sector_number, 320 NULL, §or_offset, §or_length); 321 if (rc != 0) 322 goto fail2; 323 324 if (sector_length > BOOTCFG_MAX_SIZE) 325 sector_length = BOOTCFG_MAX_SIZE; 326 327 if (sector_offset + sector_length > partn_length) { 328 /* Partition is too small */ 329 rc = EFBIG; 330 goto fail3; 331 } 332 333 /* 334 * We need to read the entire BOOTCFG sector to ensure we read all the 335 * tags, because legacy bootcfg sectors are not guaranteed to end with 336 * a DHCP_END character. If the user hasn't supplied a sufficiently 337 * large buffer then use our own buffer. 338 */ 339 if (sector_length > size) { 340 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 341 if (payload == NULL) { 342 rc = ENOMEM; 343 goto fail4; 344 } 345 } else 346 payload = (uint8_t *)data; 347 348 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 349 goto fail5; 350 351 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 352 sector_offset, (caddr_t)payload, sector_length)) != 0) { 353 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 354 goto fail6; 355 } 356 357 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 358 goto fail7; 359 360 /* Verify that the area is correctly formatted and checksummed */ 361 rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, 362 &used_bytes); 363 if (rc != 0 || used_bytes == 0) { 364 payload[0] = (uint8_t)~DHCP_END; 365 payload[1] = DHCP_END; 366 used_bytes = 2; 367 } 368 369 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 370 EFSYS_ASSERT(used_bytes <= sector_length); 371 372 /* 373 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 374 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 375 * definition large enough for any valid (per-port) bootcfg sector, 376 * so reinitialise the sector if there isn't room for the character. 377 */ 378 if (payload[used_bytes - 1] != DHCP_END) { 379 if (used_bytes + 1 > sector_length) { 380 payload[0] = 0; 381 used_bytes = 1; 382 } 383 384 payload[used_bytes] = DHCP_END; 385 ++used_bytes; 386 } 387 388 /* 389 * Verify that the user supplied buffer is large enough for the 390 * entire used bootcfg area, then copy into the user supplied buffer. 391 */ 392 if (used_bytes > size) { 393 rc = ENOSPC; 394 goto fail8; 395 } 396 if (sector_length > size) { 397 memcpy(data, payload, used_bytes); 398 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 399 } 400 401 /* Zero out the unused portion of the user buffer */ 402 if (used_bytes < size) 403 (void) memset(data + used_bytes, 0, size - used_bytes); 404 405 /* 406 * The checksum includes trailing data after any DHCP_END character, 407 * which we've just modified (by truncation or appending DHCP_END). 408 */ 409 data[0] -= efx_bootcfg_csum(enp, data, size); 410 411 return (0); 412 413 fail8: 414 EFSYS_PROBE(fail8); 415 fail7: 416 EFSYS_PROBE(fail7); 417 fail6: 418 EFSYS_PROBE(fail6); 419 fail5: 420 EFSYS_PROBE(fail5); 421 if (sector_length > size) 422 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 423 fail4: 424 EFSYS_PROBE(fail4); 425 fail3: 426 EFSYS_PROBE(fail3); 427 fail2: 428 EFSYS_PROBE(fail2); 429 fail1: 430 EFSYS_PROBE1(fail1, efx_rc_t, rc); 431 432 return (rc); 433 } 434 435 efx_rc_t 436 efx_bootcfg_write( 437 __in efx_nic_t *enp, 438 __in_bcount(size) caddr_t data, 439 __in size_t size) 440 { 441 uint8_t *partn_data; 442 uint8_t checksum; 443 size_t partn_length; 444 size_t sector_length; 445 size_t sector_offset; 446 size_t used_bytes; 447 efx_rc_t rc; 448 uint32_t sector_number; 449 450 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 451 sector_number = enp->en_nic_cfg.enc_pf; 452 #else 453 sector_number = 0; 454 #endif 455 456 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 457 if (rc != 0) 458 goto fail1; 459 460 /* The bootcfg sector may be stored in a (larger) shared partition */ 461 rc = efx_bootcfg_sector_info(enp, sector_number, 462 NULL, §or_offset, §or_length); 463 if (rc != 0) 464 goto fail2; 465 466 if (sector_length > BOOTCFG_MAX_SIZE) 467 sector_length = BOOTCFG_MAX_SIZE; 468 469 if (sector_offset + sector_length > partn_length) { 470 /* Partition is too small */ 471 rc = EFBIG; 472 goto fail3; 473 } 474 475 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 476 goto fail4; 477 478 /* The caller *must* terminate their block with a DHCP_END character */ 479 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 480 /* Block too short or DHCP_END missing */ 481 rc = ENOENT; 482 goto fail5; 483 } 484 485 /* Check that the hardware has support for this much data */ 486 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 487 rc = ENOSPC; 488 goto fail6; 489 } 490 491 /* 492 * If the BOOTCFG sector is stored in a shared partition, then we must 493 * read the whole partition and insert the updated bootcfg sector at the 494 * correct offset. 495 */ 496 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 497 if (partn_data == NULL) { 498 rc = ENOMEM; 499 goto fail7; 500 } 501 502 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 503 if (rc != 0) 504 goto fail8; 505 506 /* Read the entire partition */ 507 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 508 (caddr_t)partn_data, partn_length); 509 if (rc != 0) 510 goto fail9; 511 512 /* 513 * Insert the BOOTCFG sector into the partition, Zero out all data after 514 * the DHCP_END tag, and adjust the checksum. 515 */ 516 (void) memset(partn_data + sector_offset, 0x0, sector_length); 517 (void) memcpy(partn_data + sector_offset, data, used_bytes); 518 519 checksum = efx_bootcfg_csum(enp, data, used_bytes); 520 partn_data[sector_offset] -= checksum; 521 522 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 523 goto fail10; 524 525 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 526 0, (caddr_t)partn_data, partn_length)) != 0) 527 goto fail11; 528 529 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 530 goto fail12; 531 532 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 533 534 return (0); 535 536 fail12: 537 EFSYS_PROBE(fail12); 538 fail11: 539 EFSYS_PROBE(fail11); 540 fail10: 541 EFSYS_PROBE(fail10); 542 fail9: 543 EFSYS_PROBE(fail9); 544 545 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 546 fail8: 547 EFSYS_PROBE(fail8); 548 549 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 550 fail7: 551 EFSYS_PROBE(fail7); 552 fail6: 553 EFSYS_PROBE(fail6); 554 fail5: 555 EFSYS_PROBE(fail5); 556 fail4: 557 EFSYS_PROBE(fail4); 558 fail3: 559 EFSYS_PROBE(fail3); 560 fail2: 561 EFSYS_PROBE(fail2); 562 fail1: 563 EFSYS_PROBE1(fail1, efx_rc_t, rc); 564 565 return (rc); 566 } 567 568 #endif /* EFSYS_OPT_BOOTCFG */ 569