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