1 /*- 2 * Copyright (c) 2009-2016 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * The views and conclusions contained in the software and documentation are 27 * those of the authors and should not be interpreted as representing official 28 * policies, either expressed or implied, of the FreeBSD Project. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include "efx.h" 35 #include "efx_impl.h" 36 37 #if EFSYS_OPT_VPD 38 39 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 40 41 #include "ef10_tlv_layout.h" 42 43 __checkReturn efx_rc_t 44 ef10_vpd_init( 45 __in efx_nic_t *enp) 46 { 47 caddr_t svpd; 48 size_t svpd_size; 49 uint32_t pci_pf; 50 uint32_t tag; 51 efx_rc_t rc; 52 53 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 54 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 55 enp->en_family == EFX_FAMILY_MEDFORD || 56 enp->en_family == EFX_FAMILY_MEDFORD2); 57 58 if (enp->en_nic_cfg.enc_vpd_is_global) { 59 tag = TLV_TAG_GLOBAL_STATIC_VPD; 60 } else { 61 pci_pf = enp->en_nic_cfg.enc_pf; 62 tag = TLV_TAG_PF_STATIC_VPD(pci_pf); 63 } 64 65 /* 66 * The VPD interface exposes VPD resources from the combined static and 67 * dynamic VPD storage. As the static VPD configuration should *never* 68 * change, we can cache it. 69 */ 70 svpd = NULL; 71 svpd_size = 0; 72 rc = ef10_nvram_partn_read_tlv(enp, 73 NVRAM_PARTITION_TYPE_STATIC_CONFIG, 74 tag, &svpd, &svpd_size); 75 if (rc != 0) { 76 if (rc == EACCES) { 77 /* Unprivileged functions cannot access VPD */ 78 goto out; 79 } 80 goto fail1; 81 } 82 83 if (svpd != NULL && svpd_size > 0) { 84 if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0) 85 goto fail2; 86 } 87 88 enp->en_arch.ef10.ena_svpd = svpd; 89 enp->en_arch.ef10.ena_svpd_length = svpd_size; 90 91 out: 92 return (0); 93 94 fail2: 95 EFSYS_PROBE(fail2); 96 97 EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd); 98 fail1: 99 EFSYS_PROBE1(fail1, efx_rc_t, rc); 100 101 return (rc); 102 } 103 104 __checkReturn efx_rc_t 105 ef10_vpd_size( 106 __in efx_nic_t *enp, 107 __out size_t *sizep) 108 { 109 efx_rc_t rc; 110 111 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 112 enp->en_family == EFX_FAMILY_MEDFORD || 113 enp->en_family == EFX_FAMILY_MEDFORD2); 114 115 /* 116 * This function returns the total size the user should allocate 117 * for all VPD operations. We've already cached the static vpd, 118 * so we just need to return an upper bound on the dynamic vpd, 119 * which is the size of the DYNAMIC_CONFIG partition. 120 */ 121 if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 122 sizep, NULL, NULL, NULL)) != 0) 123 goto fail1; 124 125 return (0); 126 127 fail1: 128 EFSYS_PROBE1(fail1, efx_rc_t, rc); 129 130 return (rc); 131 } 132 133 __checkReturn efx_rc_t 134 ef10_vpd_read( 135 __in efx_nic_t *enp, 136 __out_bcount(size) caddr_t data, 137 __in size_t size) 138 { 139 caddr_t dvpd; 140 size_t dvpd_size; 141 uint32_t pci_pf; 142 uint32_t tag; 143 efx_rc_t rc; 144 145 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 146 enp->en_family == EFX_FAMILY_MEDFORD || 147 enp->en_family == EFX_FAMILY_MEDFORD2); 148 149 if (enp->en_nic_cfg.enc_vpd_is_global) { 150 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD; 151 } else { 152 pci_pf = enp->en_nic_cfg.enc_pf; 153 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf); 154 } 155 156 if ((rc = ef10_nvram_partn_read_tlv(enp, 157 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 158 tag, &dvpd, &dvpd_size)) != 0) 159 goto fail1; 160 161 if (dvpd_size > size) { 162 rc = ENOSPC; 163 goto fail2; 164 } 165 if (dvpd != NULL) 166 memcpy(data, dvpd, dvpd_size); 167 168 /* Pad data with all-1s, consistent with update operations */ 169 memset(data + dvpd_size, 0xff, size - dvpd_size); 170 171 if (dvpd != NULL) 172 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd); 173 174 return (0); 175 176 fail2: 177 EFSYS_PROBE(fail2); 178 179 if (dvpd != NULL) 180 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd); 181 fail1: 182 EFSYS_PROBE1(fail1, efx_rc_t, rc); 183 184 return (rc); 185 } 186 187 __checkReturn efx_rc_t 188 ef10_vpd_verify( 189 __in efx_nic_t *enp, 190 __in_bcount(size) caddr_t data, 191 __in size_t size) 192 { 193 efx_vpd_tag_t stag; 194 efx_vpd_tag_t dtag; 195 efx_vpd_keyword_t skey; 196 efx_vpd_keyword_t dkey; 197 unsigned int scont; 198 unsigned int dcont; 199 efx_rc_t rc; 200 201 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 202 enp->en_family == EFX_FAMILY_MEDFORD || 203 enp->en_family == EFX_FAMILY_MEDFORD2); 204 205 /* 206 * Strictly you could take the view that dynamic vpd is optional. 207 * Instead, to conform more closely to the read/verify/reinit() 208 * paradigm, we require dynamic vpd. ef10_vpd_reinit() will 209 * reinitialize it as required. 210 */ 211 if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0) 212 goto fail1; 213 214 /* 215 * Verify that there is no duplication between the static and 216 * dynamic cfg sectors. 217 */ 218 if (enp->en_arch.ef10.ena_svpd_length == 0) 219 goto done; 220 221 dcont = 0; 222 _NOTE(CONSTANTCONDITION) 223 while (1) { 224 if ((rc = efx_vpd_hunk_next(data, size, &dtag, 225 &dkey, NULL, NULL, &dcont)) != 0) 226 goto fail2; 227 if (dcont == 0) 228 break; 229 230 /* 231 * Skip the RV keyword. It should be present in both the static 232 * and dynamic cfg sectors. 233 */ 234 if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V')) 235 continue; 236 237 scont = 0; 238 _NOTE(CONSTANTCONDITION) 239 while (1) { 240 if ((rc = efx_vpd_hunk_next( 241 enp->en_arch.ef10.ena_svpd, 242 enp->en_arch.ef10.ena_svpd_length, &stag, &skey, 243 NULL, NULL, &scont)) != 0) 244 goto fail3; 245 if (scont == 0) 246 break; 247 248 if (stag == dtag && skey == dkey) { 249 rc = EEXIST; 250 goto fail4; 251 } 252 } 253 } 254 255 done: 256 return (0); 257 258 fail4: 259 EFSYS_PROBE(fail4); 260 fail3: 261 EFSYS_PROBE(fail3); 262 fail2: 263 EFSYS_PROBE(fail2); 264 fail1: 265 EFSYS_PROBE1(fail1, efx_rc_t, rc); 266 267 return (rc); 268 } 269 270 __checkReturn efx_rc_t 271 ef10_vpd_reinit( 272 __in efx_nic_t *enp, 273 __in_bcount(size) caddr_t data, 274 __in size_t size) 275 { 276 boolean_t wantpid; 277 efx_rc_t rc; 278 279 /* 280 * Only create an ID string if the dynamic cfg doesn't have one 281 */ 282 if (enp->en_arch.ef10.ena_svpd_length == 0) 283 wantpid = B_TRUE; 284 else { 285 unsigned int offset; 286 uint8_t length; 287 288 rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, 289 enp->en_arch.ef10.ena_svpd_length, 290 EFX_VPD_ID, 0, &offset, &length); 291 if (rc == 0) 292 wantpid = B_FALSE; 293 else if (rc == ENOENT) 294 wantpid = B_TRUE; 295 else 296 goto fail1; 297 } 298 299 if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0) 300 goto fail2; 301 302 return (0); 303 304 fail2: 305 EFSYS_PROBE(fail2); 306 fail1: 307 EFSYS_PROBE1(fail1, efx_rc_t, rc); 308 309 return (rc); 310 } 311 312 __checkReturn efx_rc_t 313 ef10_vpd_get( 314 __in efx_nic_t *enp, 315 __in_bcount(size) caddr_t data, 316 __in size_t size, 317 __inout efx_vpd_value_t *evvp) 318 { 319 unsigned int offset; 320 uint8_t length; 321 efx_rc_t rc; 322 323 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 324 enp->en_family == EFX_FAMILY_MEDFORD || 325 enp->en_family == EFX_FAMILY_MEDFORD2); 326 327 /* Attempt to satisfy the request from svpd first */ 328 if (enp->en_arch.ef10.ena_svpd_length > 0) { 329 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, 330 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag, 331 evvp->evv_keyword, &offset, &length)) == 0) { 332 evvp->evv_length = length; 333 memcpy(evvp->evv_value, 334 enp->en_arch.ef10.ena_svpd + offset, length); 335 return (0); 336 } else if (rc != ENOENT) 337 goto fail1; 338 } 339 340 /* And then from the provided data buffer */ 341 if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag, 342 evvp->evv_keyword, &offset, &length)) != 0) { 343 if (rc == ENOENT) 344 return (rc); 345 goto fail2; 346 } 347 348 evvp->evv_length = length; 349 memcpy(evvp->evv_value, data + offset, length); 350 351 return (0); 352 353 fail2: 354 EFSYS_PROBE(fail2); 355 fail1: 356 EFSYS_PROBE1(fail1, efx_rc_t, rc); 357 358 return (rc); 359 } 360 361 __checkReturn efx_rc_t 362 ef10_vpd_set( 363 __in efx_nic_t *enp, 364 __in_bcount(size) caddr_t data, 365 __in size_t size, 366 __in efx_vpd_value_t *evvp) 367 { 368 efx_rc_t rc; 369 370 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 371 enp->en_family == EFX_FAMILY_MEDFORD || 372 enp->en_family == EFX_FAMILY_MEDFORD2); 373 374 /* If the provided (tag,keyword) exists in svpd, then it is readonly */ 375 if (enp->en_arch.ef10.ena_svpd_length > 0) { 376 unsigned int offset; 377 uint8_t length; 378 379 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, 380 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag, 381 evvp->evv_keyword, &offset, &length)) == 0) { 382 rc = EACCES; 383 goto fail1; 384 } 385 } 386 387 if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0) 388 goto fail2; 389 390 return (0); 391 392 fail2: 393 EFSYS_PROBE(fail2); 394 fail1: 395 EFSYS_PROBE1(fail1, efx_rc_t, rc); 396 397 return (rc); 398 } 399 400 __checkReturn efx_rc_t 401 ef10_vpd_next( 402 __in efx_nic_t *enp, 403 __in_bcount(size) caddr_t data, 404 __in size_t size, 405 __out efx_vpd_value_t *evvp, 406 __inout unsigned int *contp) 407 { 408 _NOTE(ARGUNUSED(enp, data, size, evvp, contp)) 409 410 return (ENOTSUP); 411 } 412 413 __checkReturn efx_rc_t 414 ef10_vpd_write( 415 __in efx_nic_t *enp, 416 __in_bcount(size) caddr_t data, 417 __in size_t size) 418 { 419 size_t vpd_length; 420 uint32_t pci_pf; 421 uint32_t tag; 422 efx_rc_t rc; 423 424 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 425 enp->en_family == EFX_FAMILY_MEDFORD || 426 enp->en_family == EFX_FAMILY_MEDFORD2); 427 428 if (enp->en_nic_cfg.enc_vpd_is_global) { 429 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD; 430 } else { 431 pci_pf = enp->en_nic_cfg.enc_pf; 432 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf); 433 } 434 435 /* Determine total length of new dynamic VPD */ 436 if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0) 437 goto fail1; 438 439 /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */ 440 if ((rc = ef10_nvram_partn_write_segment_tlv(enp, 441 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 442 tag, data, vpd_length, B_TRUE)) != 0) { 443 goto fail2; 444 } 445 446 return (0); 447 448 fail2: 449 EFSYS_PROBE(fail2); 450 451 fail1: 452 EFSYS_PROBE1(fail1, efx_rc_t, rc); 453 454 return (rc); 455 } 456 457 void 458 ef10_vpd_fini( 459 __in efx_nic_t *enp) 460 { 461 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 462 enp->en_family == EFX_FAMILY_MEDFORD || 463 enp->en_family == EFX_FAMILY_MEDFORD2); 464 465 if (enp->en_arch.ef10.ena_svpd_length > 0) { 466 EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length, 467 enp->en_arch.ef10.ena_svpd); 468 469 enp->en_arch.ef10.ena_svpd = NULL; 470 enp->en_arch.ef10.ena_svpd_length = 0; 471 } 472 } 473 474 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */ 475 476 #endif /* EFSYS_OPT_VPD */ 477