1227569Sphilip/*- 2227569Sphilip * Copyright 2009 Solarflare Communications Inc. All rights reserved. 3227569Sphilip * 4227569Sphilip * Redistribution and use in source and binary forms, with or without 5227569Sphilip * modification, are permitted provided that the following conditions 6227569Sphilip * are met: 7227569Sphilip * 1. Redistributions of source code must retain the above copyright 8227569Sphilip * notice, this list of conditions and the following disclaimer. 9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright 10227569Sphilip * notice, this list of conditions and the following disclaimer in the 11227569Sphilip * documentation and/or other materials provided with the distribution. 12227569Sphilip * 13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16227569Sphilip * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23227569Sphilip * SUCH DAMAGE. 24227569Sphilip */ 25227569Sphilip 26228100Sphilip#include <sys/cdefs.h> 27228100Sphilip__FBSDID("$FreeBSD$"); 28228100Sphilip 29227569Sphilip#include "efsys.h" 30227569Sphilip#include "efx.h" 31227569Sphilip#include "efx_types.h" 32227569Sphilip#include "efx_regs.h" 33227569Sphilip#include "efx_impl.h" 34227569Sphilip 35227569Sphilip#if EFSYS_OPT_VPD 36227569Sphilip 37227569Sphilip#define TAG_TYPE_LBN 7 38227569Sphilip#define TAG_TYPE_WIDTH 1 39227569Sphilip#define TAG_TYPE_LARGE_ITEM_DECODE 1 40227569Sphilip#define TAG_TYPE_SMALL_ITEM_DECODE 0 41227569Sphilip 42227569Sphilip#define TAG_SMALL_ITEM_NAME_LBN 3 43227569Sphilip#define TAG_SMALL_ITEM_NAME_WIDTH 4 44227569Sphilip#define TAG_SMALL_ITEM_SIZE_LBN 0 45227569Sphilip#define TAG_SMALL_ITEM_SIZE_WIDTH 3 46227569Sphilip 47227569Sphilip#define TAG_LARGE_ITEM_NAME_LBN 0 48227569Sphilip#define TAG_LARGE_ITEM_NAME_WIDTH 7 49227569Sphilip 50227569Sphilip#define TAG_NAME_END_DECODE 0x0f 51227569Sphilip#define TAG_NAME_ID_STRING_DECODE 0x02 52227569Sphilip#define TAG_NAME_VPD_R_DECODE 0x10 53227569Sphilip#define TAG_NAME_VPD_W_DECODE 0x11 54227569Sphilip 55227569Sphilip#if EFSYS_OPT_FALCON 56227569Sphilip 57227569Sphilipstatic efx_vpd_ops_t __cs __efx_vpd_falcon_ops = { 58227569Sphilip NULL, /* evpdo_init */ 59227569Sphilip falcon_vpd_size, /* evpdo_size */ 60227569Sphilip falcon_vpd_read, /* evpdo_read */ 61227569Sphilip falcon_vpd_verify, /* evpdo_verify */ 62227569Sphilip NULL, /* evpdo_reinit */ 63227569Sphilip falcon_vpd_get, /* evpdo_get */ 64227569Sphilip falcon_vpd_set, /* evpdo_set */ 65227569Sphilip falcon_vpd_next, /* evpdo_next */ 66227569Sphilip falcon_vpd_write, /* evpdo_write */ 67227569Sphilip NULL, /* evpdo_fini */ 68227569Sphilip}; 69227569Sphilip 70227569Sphilip#endif /* EFSYS_OPT_FALCON */ 71227569Sphilip 72227569Sphilip#if EFSYS_OPT_SIENA 73227569Sphilip 74227569Sphilipstatic efx_vpd_ops_t __cs __efx_vpd_siena_ops = { 75227569Sphilip siena_vpd_init, /* evpdo_init */ 76227569Sphilip siena_vpd_size, /* evpdo_size */ 77227569Sphilip siena_vpd_read, /* evpdo_read */ 78227569Sphilip siena_vpd_verify, /* evpdo_verify */ 79227569Sphilip siena_vpd_reinit, /* evpdo_reinit */ 80227569Sphilip siena_vpd_get, /* evpdo_get */ 81227569Sphilip siena_vpd_set, /* evpdo_set */ 82227569Sphilip siena_vpd_next, /* evpdo_next */ 83227569Sphilip siena_vpd_write, /* evpdo_write */ 84227569Sphilip siena_vpd_fini, /* evpdo_fini */ 85227569Sphilip}; 86227569Sphilip 87227569Sphilip#endif /* EFSYS_OPT_SIENA */ 88227569Sphilip 89227569Sphilip __checkReturn int 90227569Sphilipefx_vpd_init( 91227569Sphilip __in efx_nic_t *enp) 92227569Sphilip{ 93227569Sphilip efx_vpd_ops_t *evpdop; 94227569Sphilip int rc; 95227569Sphilip 96227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 97227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 98227569Sphilip EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD)); 99227569Sphilip 100227569Sphilip switch (enp->en_family) { 101227569Sphilip#if EFSYS_OPT_FALCON 102227569Sphilip case EFX_FAMILY_FALCON: 103227569Sphilip evpdop = (efx_vpd_ops_t *)&__efx_vpd_falcon_ops; 104227569Sphilip break; 105227569Sphilip#endif /* EFSYS_OPT_FALCON */ 106227569Sphilip 107227569Sphilip#if EFSYS_OPT_SIENA 108227569Sphilip case EFX_FAMILY_SIENA: 109227569Sphilip evpdop = (efx_vpd_ops_t *)&__efx_vpd_siena_ops; 110227569Sphilip break; 111227569Sphilip#endif /* EFSYS_OPT_SIENA */ 112227569Sphilip 113227569Sphilip default: 114227569Sphilip EFSYS_ASSERT(0); 115227569Sphilip rc = ENOTSUP; 116227569Sphilip goto fail1; 117227569Sphilip } 118227569Sphilip 119227569Sphilip if (evpdop->evpdo_init != NULL) { 120227569Sphilip if ((rc = evpdop->evpdo_init(enp)) != 0) 121227569Sphilip goto fail2; 122227569Sphilip } 123227569Sphilip 124227569Sphilip enp->en_evpdop = evpdop; 125227569Sphilip enp->en_mod_flags |= EFX_MOD_VPD; 126227569Sphilip 127227569Sphilip return (0); 128227569Sphilip 129227569Sphilipfail2: 130227569Sphilip EFSYS_PROBE(fail2); 131227569Sphilipfail1: 132227569Sphilip EFSYS_PROBE1(fail1, int, rc); 133227569Sphilip 134227569Sphilip return (rc); 135227569Sphilip} 136227569Sphilip 137227569Sphilip __checkReturn int 138227569Sphilipefx_vpd_size( 139227569Sphilip __in efx_nic_t *enp, 140227569Sphilip __out size_t *sizep) 141227569Sphilip{ 142227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 143227569Sphilip int rc; 144227569Sphilip 145227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 146227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 147227569Sphilip 148227569Sphilip if ((rc = evpdop->evpdo_size(enp, sizep)) != 0) 149227569Sphilip goto fail1; 150227569Sphilip 151227569Sphilip return (0); 152227569Sphilip 153227569Sphilipfail1: 154227569Sphilip EFSYS_PROBE1(fail1, int, rc); 155227569Sphilip 156227569Sphilip return (rc); 157227569Sphilip} 158227569Sphilip 159227569Sphilip __checkReturn int 160227569Sphilipefx_vpd_read( 161227569Sphilip __in efx_nic_t *enp, 162227569Sphilip __out_bcount(size) caddr_t data, 163227569Sphilip __in size_t size) 164227569Sphilip{ 165227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 166227569Sphilip int rc; 167227569Sphilip 168227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 169227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 170227569Sphilip 171227569Sphilip if ((rc = evpdop->evpdo_read(enp, data, size)) != 0) 172227569Sphilip goto fail1; 173227569Sphilip 174227569Sphilip return (0); 175227569Sphilip 176227569Sphilipfail1: 177227569Sphilip EFSYS_PROBE1(fail1, int, rc); 178227569Sphilip 179227569Sphilip return (rc); 180227569Sphilip} 181227569Sphilip 182227569Sphilip __checkReturn int 183227569Sphilipefx_vpd_verify( 184227569Sphilip __in efx_nic_t *enp, 185227569Sphilip __in_bcount(size) caddr_t data, 186227569Sphilip __in size_t size) 187227569Sphilip{ 188227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 189227569Sphilip int rc; 190227569Sphilip 191227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 192227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 193227569Sphilip 194227569Sphilip if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0) 195227569Sphilip goto fail1; 196227569Sphilip 197227569Sphilip return (0); 198227569Sphilip 199227569Sphilipfail1: 200227569Sphilip EFSYS_PROBE1(fail1, int, rc); 201227569Sphilip 202227569Sphilip return (rc); 203227569Sphilip} 204227569Sphilip 205227569Sphilip __checkReturn int 206227569Sphilipefx_vpd_reinit( 207227569Sphilip __in efx_nic_t *enp, 208227569Sphilip __in_bcount(size) caddr_t data, 209227569Sphilip __in size_t size) 210227569Sphilip{ 211227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 212227569Sphilip int rc; 213227569Sphilip 214227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 215227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 216227569Sphilip 217227569Sphilip if (evpdop->evpdo_reinit == NULL) { 218227569Sphilip rc = ENOTSUP; 219227569Sphilip goto fail1; 220227569Sphilip } 221227569Sphilip 222227569Sphilip if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0) 223227569Sphilip goto fail2; 224227569Sphilip 225227569Sphilip return (0); 226227569Sphilip 227227569Sphilipfail2: 228227569Sphilip EFSYS_PROBE(fail2); 229227569Sphilipfail1: 230227569Sphilip EFSYS_PROBE1(fail1, int, rc); 231227569Sphilip 232227569Sphilip return (rc); 233227569Sphilip} 234227569Sphilip 235227569Sphilip __checkReturn int 236227569Sphilipefx_vpd_get( 237227569Sphilip __in efx_nic_t *enp, 238227569Sphilip __in_bcount(size) caddr_t data, 239227569Sphilip __in size_t size, 240227569Sphilip __inout efx_vpd_value_t *evvp) 241227569Sphilip{ 242227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 243227569Sphilip int rc; 244227569Sphilip 245227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 246227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 247227569Sphilip 248227569Sphilip if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) 249227569Sphilip goto fail1; 250227569Sphilip 251227569Sphilip return (0); 252227569Sphilip 253227569Sphilipfail1: 254227569Sphilip EFSYS_PROBE1(fail1, int, rc); 255227569Sphilip 256227569Sphilip return (rc); 257227569Sphilip} 258227569Sphilip 259227569Sphilip __checkReturn int 260227569Sphilipefx_vpd_set( 261227569Sphilip __in efx_nic_t *enp, 262227569Sphilip __inout_bcount(size) caddr_t data, 263227569Sphilip __in size_t size, 264227569Sphilip __in efx_vpd_value_t *evvp) 265227569Sphilip{ 266227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 267227569Sphilip int rc; 268227569Sphilip 269227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 270227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 271227569Sphilip 272227569Sphilip if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0) 273227569Sphilip goto fail1; 274227569Sphilip 275227569Sphilip return (0); 276227569Sphilip 277227569Sphilipfail1: 278227569Sphilip EFSYS_PROBE1(fail1, int, rc); 279227569Sphilip 280227569Sphilip return (rc); 281227569Sphilip} 282227569Sphilip 283227569Sphilip __checkReturn int 284227569Sphilipefx_vpd_next( 285227569Sphilip __in efx_nic_t *enp, 286227569Sphilip __inout_bcount(size) caddr_t data, 287227569Sphilip __in size_t size, 288227569Sphilip __out efx_vpd_value_t *evvp, 289227569Sphilip __inout unsigned int *contp) 290227569Sphilip{ 291227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 292227569Sphilip int rc; 293227569Sphilip 294227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 295227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 296227569Sphilip 297227569Sphilip if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0) 298227569Sphilip goto fail1; 299227569Sphilip 300227569Sphilip return (0); 301227569Sphilip 302227569Sphilipfail1: 303227569Sphilip EFSYS_PROBE1(fail1, int, rc); 304227569Sphilip 305227569Sphilip return (rc); 306227569Sphilip} 307227569Sphilip 308227569Sphilip __checkReturn int 309227569Sphilipefx_vpd_write( 310227569Sphilip __in efx_nic_t *enp, 311227569Sphilip __in_bcount(size) caddr_t data, 312227569Sphilip __in size_t size) 313227569Sphilip{ 314227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 315227569Sphilip int rc; 316227569Sphilip 317227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 318227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 319227569Sphilip 320227569Sphilip if ((rc = evpdop->evpdo_write(enp, data, size)) != 0) 321227569Sphilip goto fail1; 322227569Sphilip 323227569Sphilip return (0); 324227569Sphilip 325227569Sphilipfail1: 326227569Sphilip EFSYS_PROBE1(fail1, int, rc); 327227569Sphilip 328227569Sphilip return (rc); 329227569Sphilip} 330227569Sphilip 331227569Sphilipstatic __checkReturn int 332227569Sphilipefx_vpd_next_tag( 333227569Sphilip __in caddr_t data, 334227569Sphilip __in size_t size, 335227569Sphilip __inout unsigned int *offsetp, 336227569Sphilip __out efx_vpd_tag_t *tagp, 337227569Sphilip __out uint16_t *lengthp) 338227569Sphilip{ 339227569Sphilip efx_byte_t byte; 340227569Sphilip efx_word_t word; 341227569Sphilip uint8_t name; 342227569Sphilip uint16_t length; 343227569Sphilip size_t headlen; 344227569Sphilip int rc; 345227569Sphilip 346227569Sphilip if (*offsetp >= size) { 347227569Sphilip rc = EFAULT; 348227569Sphilip goto fail1; 349227569Sphilip } 350227569Sphilip 351227569Sphilip EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]); 352227569Sphilip 353227569Sphilip switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) { 354227569Sphilip case TAG_TYPE_SMALL_ITEM_DECODE: 355227569Sphilip headlen = 1; 356227569Sphilip 357227569Sphilip name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME); 358227569Sphilip length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE); 359227569Sphilip 360227569Sphilip break; 361227569Sphilip 362227569Sphilip case TAG_TYPE_LARGE_ITEM_DECODE: 363227569Sphilip headlen = 3; 364227569Sphilip 365227569Sphilip if (*offsetp + headlen > size) { 366227569Sphilip rc = EFAULT; 367227569Sphilip goto fail2; 368227569Sphilip } 369227569Sphilip 370227569Sphilip name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME); 371227569Sphilip EFX_POPULATE_WORD_2(word, 372227569Sphilip EFX_BYTE_0, data[*offsetp + 1], 373227569Sphilip EFX_BYTE_1, data[*offsetp + 2]); 374227569Sphilip length = EFX_WORD_FIELD(word, EFX_WORD_0); 375227569Sphilip 376227569Sphilip break; 377227569Sphilip 378227569Sphilip default: 379227569Sphilip rc = EFAULT; 380227569Sphilip goto fail2; 381227569Sphilip } 382227569Sphilip 383227569Sphilip if (*offsetp + headlen + length > size) { 384227569Sphilip rc = EFAULT; 385227569Sphilip goto fail3; 386227569Sphilip } 387227569Sphilip 388227569Sphilip EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END); 389227569Sphilip EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID); 390227569Sphilip EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO); 391227569Sphilip EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW); 392227569Sphilip if (name != EFX_VPD_END && name != EFX_VPD_ID && 393227569Sphilip name != EFX_VPD_RO) { 394227569Sphilip rc = EFAULT; 395227569Sphilip goto fail4; 396227569Sphilip } 397227569Sphilip 398227569Sphilip *tagp = name; 399227569Sphilip *lengthp = length; 400227569Sphilip *offsetp += headlen; 401227569Sphilip 402227569Sphilip return (0); 403227569Sphilip 404227569Sphilipfail4: 405227569Sphilip EFSYS_PROBE(fail4); 406227569Sphilipfail3: 407227569Sphilip EFSYS_PROBE(fail3); 408227569Sphilipfail2: 409227569Sphilip EFSYS_PROBE(fail2); 410227569Sphilipfail1: 411227569Sphilip EFSYS_PROBE1(fail1, int, rc); 412227569Sphilip 413227569Sphilip return (rc); 414227569Sphilip} 415227569Sphilip 416227569Sphilipstatic __checkReturn int 417227569Sphilipefx_vpd_next_keyword( 418227569Sphilip __in_bcount(size) caddr_t tag, 419227569Sphilip __in size_t size, 420227569Sphilip __in unsigned int pos, 421227569Sphilip __out efx_vpd_keyword_t *keywordp, 422227569Sphilip __out uint8_t *lengthp) 423227569Sphilip{ 424227569Sphilip efx_vpd_keyword_t keyword; 425227569Sphilip uint8_t length; 426227569Sphilip int rc; 427227569Sphilip 428227569Sphilip if (pos + 3U > size) { 429227569Sphilip rc = EFAULT; 430227569Sphilip goto fail1; 431227569Sphilip } 432227569Sphilip 433227569Sphilip keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]); 434227569Sphilip length = tag[pos + 2]; 435227569Sphilip 436227569Sphilip if (length == 0 || pos + 3U + length > size) { 437227569Sphilip rc = EFAULT; 438227569Sphilip goto fail2; 439227569Sphilip } 440227569Sphilip 441227569Sphilip *keywordp = keyword; 442227569Sphilip *lengthp = length; 443227569Sphilip 444227569Sphilip return (0); 445227569Sphilip 446227569Sphilipfail2: 447227569Sphilip EFSYS_PROBE(fail2); 448227569Sphilipfail1: 449227569Sphilip EFSYS_PROBE1(fail1, int, rc); 450227569Sphilip 451227569Sphilip return (rc); 452227569Sphilip} 453227569Sphilip 454227569Sphilip __checkReturn int 455227569Sphilipefx_vpd_hunk_length( 456227569Sphilip __in_bcount(size) caddr_t data, 457227569Sphilip __in size_t size, 458227569Sphilip __out size_t *lengthp) 459227569Sphilip{ 460227569Sphilip efx_vpd_tag_t tag; 461227569Sphilip unsigned int offset; 462227569Sphilip uint16_t taglen; 463227569Sphilip int rc; 464227569Sphilip 465227569Sphilip offset = 0; 466227569Sphilip _NOTE(CONSTANTCONDITION) 467227569Sphilip while (1) { 468227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 469227569Sphilip &tag, &taglen)) != 0) 470227569Sphilip goto fail1; 471227569Sphilip offset += taglen; 472227569Sphilip if (tag == EFX_VPD_END) 473227569Sphilip break; 474227569Sphilip } 475227569Sphilip 476227569Sphilip *lengthp = offset; 477227569Sphilip 478227569Sphilip return (0); 479227569Sphilip 480227569Sphilipfail1: 481227569Sphilip EFSYS_PROBE1(fail1, int, rc); 482227569Sphilip 483227569Sphilip return (rc); 484227569Sphilip} 485227569Sphilip 486227569Sphilip __checkReturn int 487227569Sphilipefx_vpd_hunk_verify( 488227569Sphilip __in_bcount(size) caddr_t data, 489227569Sphilip __in size_t size, 490227569Sphilip __out_opt boolean_t *cksummedp) 491227569Sphilip{ 492227569Sphilip efx_vpd_tag_t tag; 493227569Sphilip efx_vpd_keyword_t keyword; 494227569Sphilip unsigned int offset; 495227569Sphilip unsigned int pos; 496227569Sphilip unsigned int i; 497227569Sphilip uint16_t taglen; 498227569Sphilip uint8_t keylen; 499227569Sphilip uint8_t cksum; 500227569Sphilip boolean_t cksummed = B_FALSE; 501227569Sphilip int rc; 502227569Sphilip 503227569Sphilip /* 504227569Sphilip * Parse every tag,keyword in the existing VPD. If the csum is present, 505227569Sphilip * the assert it is correct, and is the final keyword in the RO block. 506227569Sphilip */ 507227569Sphilip offset = 0; 508227569Sphilip _NOTE(CONSTANTCONDITION) 509227569Sphilip while (1) { 510227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 511227569Sphilip &tag, &taglen)) != 0) 512227569Sphilip goto fail1; 513227569Sphilip if (tag == EFX_VPD_END) 514227569Sphilip break; 515227569Sphilip else if (tag == EFX_VPD_ID) 516227569Sphilip goto done; 517227569Sphilip 518227569Sphilip for (pos = 0; pos != taglen; pos += 3 + keylen) { 519227569Sphilip /* RV keyword must be the last in the block */ 520227569Sphilip if (cksummed) 521227569Sphilip goto fail2; 522227569Sphilip 523227569Sphilip if ((rc = efx_vpd_next_keyword(data + offset, 524227569Sphilip taglen, pos, &keyword, &keylen)) != 0) 525227569Sphilip goto fail3; 526227569Sphilip 527227569Sphilip if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 528227569Sphilip cksum = 0; 529227569Sphilip for (i = 0; i < offset + pos + 4; i++) 530227569Sphilip cksum += data[i]; 531227569Sphilip 532227569Sphilip if (cksum != 0) { 533227569Sphilip rc = EFAULT; 534227569Sphilip goto fail4; 535227569Sphilip } 536227569Sphilip 537227569Sphilip cksummed = B_TRUE; 538227569Sphilip } 539227569Sphilip } 540227569Sphilip 541227569Sphilip done: 542227569Sphilip offset += taglen; 543227569Sphilip } 544227569Sphilip 545227569Sphilip if (!cksummed) { 546227569Sphilip rc = EFAULT; 547227569Sphilip goto fail5; 548227569Sphilip } 549227569Sphilip 550227569Sphilip if (cksummedp != NULL) 551227569Sphilip *cksummedp = cksummed; 552227569Sphilip 553227569Sphilip return (0); 554227569Sphilip 555227569Sphilipfail5: 556227569Sphilip EFSYS_PROBE(fail5); 557227569Sphilipfail4: 558227569Sphilip EFSYS_PROBE(fail4); 559227569Sphilipfail3: 560227569Sphilip EFSYS_PROBE(fail3); 561227569Sphilipfail2: 562227569Sphilip EFSYS_PROBE(fail2); 563227569Sphilipfail1: 564227569Sphilip EFSYS_PROBE1(fail1, int, rc); 565227569Sphilip 566227569Sphilip return (rc); 567227569Sphilip} 568227569Sphilip 569227569Sphilipstatic uint8_t __cs __efx_vpd_blank_pid[] = { 570227569Sphilip /* Large resource type ID length 1 */ 571227569Sphilip 0x82, 0x01, 0x00, 572227569Sphilip /* Product name ' ' */ 573227569Sphilip 0x32, 574227569Sphilip}; 575227569Sphilip 576227569Sphilipstatic uint8_t __cs __efx_vpd_blank_r[] = { 577227569Sphilip /* Large resource type VPD-R length 4 */ 578227569Sphilip 0x90, 0x04, 0x00, 579227569Sphilip /* RV keyword length 1 */ 580227569Sphilip 'R', 'V', 0x01, 581227569Sphilip /* RV payload checksum */ 582227569Sphilip 0x00, 583227569Sphilip}; 584227569Sphilip 585227569Sphilip __checkReturn int 586227569Sphilipefx_vpd_hunk_reinit( 587227569Sphilip __in caddr_t data, 588227569Sphilip __in size_t size, 589227569Sphilip __in boolean_t wantpid) 590227569Sphilip{ 591227569Sphilip unsigned int offset = 0; 592227569Sphilip unsigned int pos; 593227569Sphilip efx_byte_t byte; 594227569Sphilip uint8_t cksum; 595227569Sphilip int rc; 596227569Sphilip 597227569Sphilip if (size < 0x100) { 598227569Sphilip rc = ENOSPC; 599227569Sphilip goto fail1; 600227569Sphilip } 601227569Sphilip 602227569Sphilip if (wantpid) { 603227569Sphilip memcpy(data + offset, __efx_vpd_blank_pid, 604227569Sphilip sizeof (__efx_vpd_blank_pid)); 605227569Sphilip offset += sizeof (__efx_vpd_blank_pid); 606227569Sphilip } 607227569Sphilip 608227569Sphilip memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r)); 609227569Sphilip offset += sizeof (__efx_vpd_blank_r); 610227569Sphilip 611227569Sphilip /* Update checksum */ 612227569Sphilip cksum = 0; 613227569Sphilip for (pos = 0; pos < offset; pos++) 614227569Sphilip cksum += data[pos]; 615227569Sphilip data[offset - 1] -= cksum; 616227569Sphilip 617227569Sphilip /* Append trailing tag */ 618227569Sphilip EFX_POPULATE_BYTE_3(byte, 619227569Sphilip TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE, 620227569Sphilip TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE, 621227569Sphilip TAG_SMALL_ITEM_SIZE, 0); 622227569Sphilip data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0); 623227569Sphilip offset++; 624227569Sphilip 625227569Sphilip return (0); 626227569Sphilip 627227569Sphilipfail1: 628227569Sphilip EFSYS_PROBE1(fail1, int, rc); 629227569Sphilip 630227569Sphilip return (rc); 631227569Sphilip} 632227569Sphilip 633227569Sphilip __checkReturn int 634227569Sphilipefx_vpd_hunk_next( 635227569Sphilip __in_bcount(size) caddr_t data, 636227569Sphilip __in size_t size, 637227569Sphilip __out efx_vpd_tag_t *tagp, 638227569Sphilip __out efx_vpd_keyword_t *keywordp, 639227569Sphilip __out_bcount_opt(*paylenp) unsigned int *payloadp, 640227569Sphilip __out_opt uint8_t *paylenp, 641227569Sphilip __inout unsigned int *contp) 642227569Sphilip{ 643227569Sphilip efx_vpd_tag_t tag; 644227569Sphilip efx_vpd_keyword_t keyword = 0; 645227569Sphilip unsigned int offset; 646227569Sphilip unsigned int pos; 647227569Sphilip unsigned int index; 648227569Sphilip uint16_t taglen; 649227569Sphilip uint8_t keylen; 650227569Sphilip uint8_t paylen; 651227569Sphilip int rc; 652227569Sphilip 653227569Sphilip offset = index = 0; 654227569Sphilip _NOTE(CONSTANTCONDITION) 655227569Sphilip while (1) { 656227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 657227569Sphilip &tag, &taglen)) != 0) 658227569Sphilip goto fail1; 659227569Sphilip if (tag == EFX_VPD_END) 660227569Sphilip break; 661227569Sphilip 662227569Sphilip if (tag == EFX_VPD_ID) { 663227569Sphilip if (index == *contp) { 664227569Sphilip EFSYS_ASSERT3U(taglen, <, 0x100); 665227569Sphilip paylen = (uint8_t)MIN(taglen, 0xff); 666227569Sphilip 667227569Sphilip goto done; 668227569Sphilip } 669227569Sphilip } else { 670227569Sphilip for (pos = 0; pos != taglen; pos += 3 + keylen) { 671227569Sphilip if ((rc = efx_vpd_next_keyword(data + offset, 672227569Sphilip taglen, pos, &keyword, &keylen)) != 0) 673227569Sphilip goto fail2; 674227569Sphilip 675227569Sphilip if (index == *contp) { 676227569Sphilip offset += pos + 3; 677227569Sphilip paylen = keylen; 678227569Sphilip 679227569Sphilip goto done; 680227569Sphilip } 681227569Sphilip } 682227569Sphilip } 683227569Sphilip 684227569Sphilip offset += taglen; 685227569Sphilip } 686227569Sphilip 687227569Sphilip *contp = 0; 688227569Sphilip return (0); 689227569Sphilip 690227569Sphilipdone: 691227569Sphilip *tagp = tag; 692227569Sphilip *keywordp = keyword; 693227569Sphilip if (payloadp != NULL) 694227569Sphilip *payloadp = offset; 695227569Sphilip if (paylenp != NULL) 696227569Sphilip *paylenp = paylen; 697227569Sphilip 698227569Sphilip ++(*contp); 699227569Sphilip return (0); 700227569Sphilip 701227569Sphilipfail2: 702227569Sphilip EFSYS_PROBE(fail2); 703227569Sphilipfail1: 704227569Sphilip EFSYS_PROBE1(fail1, int, rc); 705227569Sphilip 706227569Sphilip return (rc); 707227569Sphilip} 708227569Sphilip 709227569Sphilip __checkReturn int 710227569Sphilipefx_vpd_hunk_get( 711227569Sphilip __in_bcount(size) caddr_t data, 712227569Sphilip __in size_t size, 713227569Sphilip __in efx_vpd_tag_t tag, 714227569Sphilip __in efx_vpd_keyword_t keyword, 715227569Sphilip __out unsigned int *payloadp, 716227569Sphilip __out uint8_t *paylenp) 717227569Sphilip{ 718227569Sphilip efx_vpd_tag_t itag; 719227569Sphilip efx_vpd_keyword_t ikeyword; 720227569Sphilip unsigned int offset; 721227569Sphilip unsigned int pos; 722227569Sphilip uint16_t taglen; 723227569Sphilip uint8_t keylen; 724227569Sphilip int rc; 725227569Sphilip 726227569Sphilip offset = 0; 727227569Sphilip _NOTE(CONSTANTCONDITION) 728227569Sphilip while (1) { 729227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 730227569Sphilip &itag, &taglen)) != 0) 731227569Sphilip goto fail1; 732227569Sphilip if (itag == EFX_VPD_END) 733227569Sphilip break; 734227569Sphilip 735227569Sphilip if (itag == tag) { 736227569Sphilip if (itag == EFX_VPD_ID) { 737227569Sphilip EFSYS_ASSERT3U(taglen, <, 0x100); 738227569Sphilip 739227569Sphilip *paylenp = (uint8_t)MIN(taglen, 0xff); 740227569Sphilip *payloadp = offset; 741227569Sphilip return (0); 742227569Sphilip } 743227569Sphilip 744227569Sphilip for (pos = 0; pos != taglen; pos += 3 + keylen) { 745227569Sphilip if ((rc = efx_vpd_next_keyword(data + offset, 746227569Sphilip taglen, pos, &ikeyword, &keylen)) != 0) 747227569Sphilip goto fail2; 748227569Sphilip 749227569Sphilip if (ikeyword == keyword) { 750227569Sphilip *paylenp = keylen; 751227569Sphilip *payloadp = offset + pos + 3; 752227569Sphilip return (0); 753227569Sphilip } 754227569Sphilip } 755227569Sphilip } 756227569Sphilip 757227569Sphilip offset += taglen; 758227569Sphilip } 759227569Sphilip 760227569Sphilip /* Not an error */ 761227569Sphilip return (ENOENT); 762227569Sphilip 763227569Sphilipfail2: 764227569Sphilip EFSYS_PROBE(fail2); 765227569Sphilipfail1: 766227569Sphilip EFSYS_PROBE1(fail1, int, rc); 767227569Sphilip 768227569Sphilip return (rc); 769227569Sphilip} 770227569Sphilip 771227569Sphilip __checkReturn int 772227569Sphilipefx_vpd_hunk_set( 773227569Sphilip __in_bcount(size) caddr_t data, 774227569Sphilip __in size_t size, 775227569Sphilip __in efx_vpd_value_t *evvp) 776227569Sphilip{ 777227569Sphilip efx_word_t word; 778227569Sphilip efx_vpd_tag_t tag; 779227569Sphilip efx_vpd_keyword_t keyword; 780227569Sphilip unsigned int offset; 781227569Sphilip unsigned int pos; 782227569Sphilip unsigned int taghead; 783227569Sphilip unsigned int source; 784227569Sphilip unsigned int dest; 785227569Sphilip unsigned int i; 786227569Sphilip uint16_t taglen; 787227569Sphilip uint8_t keylen; 788227569Sphilip uint8_t cksum; 789227569Sphilip size_t used; 790227569Sphilip int rc; 791227569Sphilip 792227569Sphilip switch (evvp->evv_tag) { 793227569Sphilip case EFX_VPD_ID: 794227569Sphilip if (evvp->evv_keyword != 0) { 795227569Sphilip rc = EINVAL; 796227569Sphilip goto fail1; 797227569Sphilip } 798227569Sphilip 799227569Sphilip /* Can't delete the ID keyword */ 800227569Sphilip if (evvp->evv_length == 0) { 801227569Sphilip rc = EINVAL; 802227569Sphilip goto fail1; 803227569Sphilip } 804227569Sphilip break; 805227569Sphilip 806227569Sphilip case EFX_VPD_RO: 807227569Sphilip if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) { 808227569Sphilip rc = EINVAL; 809227569Sphilip goto fail1; 810227569Sphilip } 811227569Sphilip break; 812227569Sphilip 813227569Sphilip default: 814227569Sphilip rc = EINVAL; 815227569Sphilip goto fail1; 816227569Sphilip } 817227569Sphilip 818227569Sphilip /* Determine total size of all current tags */ 819227569Sphilip if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0) 820227569Sphilip goto fail2; 821227569Sphilip 822227569Sphilip offset = 0; 823227569Sphilip _NOTE(CONSTANTCONDITION) 824227569Sphilip while (1) { 825227569Sphilip taghead = offset; 826227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 827227569Sphilip &tag, &taglen)) != 0) 828227569Sphilip goto fail3; 829227569Sphilip if (tag == EFX_VPD_END) 830227569Sphilip break; 831227569Sphilip else if (tag != evvp->evv_tag) { 832227569Sphilip offset += taglen; 833227569Sphilip continue; 834227569Sphilip } 835227569Sphilip 836227569Sphilip /* We only support modifying large resource tags */ 837227569Sphilip if (offset - taghead != 3) { 838227569Sphilip rc = EINVAL; 839227569Sphilip goto fail4; 840227569Sphilip } 841227569Sphilip 842227569Sphilip /* 843227569Sphilip * Work out the offset of the byte immediately after the 844227569Sphilip * old (=source) and new (=dest) new keyword/tag 845227569Sphilip */ 846227569Sphilip pos = 0; 847227569Sphilip if (tag == EFX_VPD_ID) { 848227569Sphilip source = offset + taglen; 849227569Sphilip dest = offset + evvp->evv_length; 850227569Sphilip goto check_space; 851227569Sphilip } 852227569Sphilip 853227569Sphilip EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO); 854227569Sphilip source = dest = 0; 855227569Sphilip for (pos = 0; pos != taglen; pos += 3 + keylen) { 856227569Sphilip if ((rc = efx_vpd_next_keyword(data + offset, 857227569Sphilip taglen, pos, &keyword, &keylen)) != 0) 858227569Sphilip goto fail5; 859227569Sphilip 860227569Sphilip if (keyword == evvp->evv_keyword && 861227569Sphilip evvp->evv_length == 0) { 862227569Sphilip /* Deleting this keyword */ 863227569Sphilip source = offset + pos + 3 + keylen; 864227569Sphilip dest = offset + pos; 865227569Sphilip break; 866227569Sphilip 867227569Sphilip } else if (keyword == evvp->evv_keyword) { 868227569Sphilip /* Adjusting this keyword */ 869227569Sphilip source = offset + pos + 3 + keylen; 870227569Sphilip dest = offset + pos + 3 + evvp->evv_length; 871227569Sphilip break; 872227569Sphilip 873227569Sphilip } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 874227569Sphilip /* The RV keyword must be at the end */ 875227569Sphilip EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen); 876227569Sphilip 877227569Sphilip /* 878227569Sphilip * The keyword doesn't already exist. If the 879227569Sphilip * user deleting a non-existant keyword then 880227569Sphilip * this is a no-op. 881227569Sphilip */ 882227569Sphilip if (evvp->evv_length == 0) 883227569Sphilip return (0); 884227569Sphilip 885227569Sphilip /* Insert this keyword before the RV keyword */ 886227569Sphilip source = offset + pos; 887227569Sphilip dest = offset + pos + 3 + evvp->evv_length; 888227569Sphilip break; 889227569Sphilip } 890227569Sphilip } 891227569Sphilip 892227569Sphilip check_space: 893227569Sphilip if (used + dest > size + source) { 894227569Sphilip rc = ENOSPC; 895227569Sphilip goto fail6; 896227569Sphilip } 897227569Sphilip 898227569Sphilip /* Move trailing data */ 899227569Sphilip (void) memmove(data + dest, data + source, used - source); 900227569Sphilip 901227569Sphilip /* Copy contents */ 902227569Sphilip memcpy(data + dest - evvp->evv_length, evvp->evv_value, 903227569Sphilip evvp->evv_length); 904227569Sphilip 905227569Sphilip /* Insert new keyword header if required */ 906227569Sphilip if (tag != EFX_VPD_ID && evvp->evv_length > 0) { 907227569Sphilip EFX_POPULATE_WORD_1(word, EFX_WORD_0, 908227569Sphilip evvp->evv_keyword); 909227569Sphilip data[offset + pos + 0] = 910227569Sphilip EFX_WORD_FIELD(word, EFX_BYTE_0); 911227569Sphilip data[offset + pos + 1] = 912227569Sphilip EFX_WORD_FIELD(word, EFX_BYTE_1); 913227569Sphilip data[offset + pos + 2] = evvp->evv_length; 914227569Sphilip } 915227569Sphilip 916227569Sphilip /* Modify tag length (large resource type) */ 917227569Sphilip taglen += (dest - source); 918227569Sphilip EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen); 919227569Sphilip data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0); 920227569Sphilip data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1); 921227569Sphilip 922227569Sphilip goto checksum; 923227569Sphilip } 924227569Sphilip 925227569Sphilip /* Unable to find the matching tag */ 926227569Sphilip rc = ENOENT; 927227569Sphilip goto fail7; 928227569Sphilip 929227569Sphilipchecksum: 930227569Sphilip /* Find the RV tag, and update the checksum */ 931227569Sphilip offset = 0; 932227569Sphilip _NOTE(CONSTANTCONDITION) 933227569Sphilip while (1) { 934227569Sphilip if ((rc = efx_vpd_next_tag(data, size, &offset, 935227569Sphilip &tag, &taglen)) != 0) 936227569Sphilip goto fail8; 937227569Sphilip if (tag == EFX_VPD_END) 938227569Sphilip break; 939227569Sphilip if (tag == EFX_VPD_RO) { 940227569Sphilip for (pos = 0; pos != taglen; pos += 3 + keylen) { 941227569Sphilip if ((rc = efx_vpd_next_keyword(data + offset, 942227569Sphilip taglen, pos, &keyword, &keylen)) != 0) 943227569Sphilip goto fail9; 944227569Sphilip 945227569Sphilip if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 946227569Sphilip cksum = 0; 947227569Sphilip for (i = 0; i < offset + pos + 3; i++) 948227569Sphilip cksum += data[i]; 949227569Sphilip data[i] = -cksum; 950227569Sphilip break; 951227569Sphilip } 952227569Sphilip } 953227569Sphilip } 954227569Sphilip 955227569Sphilip offset += taglen; 956227569Sphilip } 957227569Sphilip 958227569Sphilip /* Zero out the unused portion */ 959227569Sphilip (void) memset(data + offset + taglen, 0xff, size - offset - taglen); 960227569Sphilip 961227569Sphilip return (0); 962227569Sphilip 963227569Sphilipfail9: 964227569Sphilip EFSYS_PROBE(fail9); 965227569Sphilipfail8: 966227569Sphilip EFSYS_PROBE(fail8); 967227569Sphilipfail7: 968227569Sphilip EFSYS_PROBE(fail7); 969227569Sphilipfail6: 970227569Sphilip EFSYS_PROBE(fail6); 971227569Sphilipfail5: 972227569Sphilip EFSYS_PROBE(fail5); 973227569Sphilipfail4: 974227569Sphilip EFSYS_PROBE(fail4); 975227569Sphilipfail3: 976227569Sphilip EFSYS_PROBE(fail3); 977227569Sphilipfail2: 978227569Sphilip EFSYS_PROBE(fail2); 979227569Sphilipfail1: 980227569Sphilip EFSYS_PROBE1(fail1, int, rc); 981227569Sphilip 982227569Sphilip return (rc); 983227569Sphilip} 984227569Sphilip 985227569Sphilip void 986227569Sphilipefx_vpd_fini( 987227569Sphilip __in efx_nic_t *enp) 988227569Sphilip{ 989227569Sphilip efx_vpd_ops_t *evpdop = enp->en_evpdop; 990227569Sphilip 991227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 992227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 993227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 994227569Sphilip 995227569Sphilip if (evpdop->evpdo_fini != NULL) 996227569Sphilip evpdop->evpdo_fini(enp); 997227569Sphilip 998227569Sphilip enp->en_evpdop = NULL; 999227569Sphilip enp->en_mod_flags &= ~EFX_MOD_VPD; 1000227569Sphilip} 1001227569Sphilip 1002227569Sphilip#endif /* EFSYS_OPT_VPD */ 1003