From: Andrew Rybchenko Date: Tue, 29 Nov 2016 16:18:59 +0000 (+0000) Subject: net/sfc/base: import VPD support X-Git-Tag: spdx-start~5072 X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=946ba3b6941a;p=dpdk.git net/sfc/base: import VPD support Provide API to read/write PCI Vital Product Data. EFSYS_OPT_VPD should be enabled to use it. From Solarflare Communications Inc. Signed-off-by: Andrew Rybchenko Reviewed-by: Ferruh Yigit --- diff --git a/drivers/net/sfc/base/ef10_impl.h b/drivers/net/sfc/base/ef10_impl.h index e3b262100d..192be5df32 100644 --- a/drivers/net/sfc/base/ef10_impl.h +++ b/drivers/net/sfc/base/ef10_impl.h @@ -817,6 +817,69 @@ ef10_nic_pio_unlink( /* VPD */ +#if EFSYS_OPT_VPD + +extern __checkReturn efx_rc_t +ef10_vpd_init( + __in efx_nic_t *enp); + +extern __checkReturn efx_rc_t +ef10_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep); + +extern __checkReturn efx_rc_t +ef10_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +ef10_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +ef10_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +ef10_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +ef10_vpd_set( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +ef10_vpd_next( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp); + +extern __checkReturn efx_rc_t +ef10_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern void +ef10_vpd_fini( + __in efx_nic_t *enp); + +#endif /* EFSYS_OPT_VPD */ + /* RX */ diff --git a/drivers/net/sfc/base/ef10_vpd.c b/drivers/net/sfc/base/ef10_vpd.c new file mode 100644 index 0000000000..71123a9098 --- /dev/null +++ b/drivers/net/sfc/base/ef10_vpd.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2009-2016 Solarflare Communications Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of the FreeBSD Project. + */ + +#include "efx.h" +#include "efx_impl.h" + + +#if EFSYS_OPT_VPD + +#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD + +#include "ef10_tlv_layout.h" + + __checkReturn efx_rc_t +ef10_vpd_init( + __in efx_nic_t *enp) +{ + caddr_t svpd; + size_t svpd_size; + uint32_t pci_pf; + uint32_t tag; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + if (enp->en_nic_cfg.enc_vpd_is_global) { + tag = TLV_TAG_GLOBAL_STATIC_VPD; + } else { + pci_pf = enp->en_nic_cfg.enc_pf; + tag = TLV_TAG_PF_STATIC_VPD(pci_pf); + } + + /* + * The VPD interface exposes VPD resources from the combined static and + * dynamic VPD storage. As the static VPD configuration should *never* + * change, we can cache it. + */ + svpd = NULL; + svpd_size = 0; + rc = ef10_nvram_partn_read_tlv(enp, + NVRAM_PARTITION_TYPE_STATIC_CONFIG, + tag, &svpd, &svpd_size); + if (rc != 0) { + if (rc == EACCES) { + /* Unprivileged functions cannot access VPD */ + goto out; + } + goto fail1; + } + + if (svpd != NULL && svpd_size > 0) { + if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0) + goto fail2; + } + + enp->en_arch.ef10.ena_svpd = svpd; + enp->en_arch.ef10.ena_svpd_length = svpd_size; + +out: + return (0); + +fail2: + EFSYS_PROBE(fail2); + + EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep) +{ + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + /* + * This function returns the total size the user should allocate + * for all VPD operations. We've already cached the static vpd, + * so we just need to return an upper bound on the dynamic vpd, + * which is the size of the DYNAMIC_CONFIG partition. + */ + if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, + sizep, NULL, NULL, NULL)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size) +{ + caddr_t dvpd; + size_t dvpd_size; + uint32_t pci_pf; + uint32_t tag; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + if (enp->en_nic_cfg.enc_vpd_is_global) { + tag = TLV_TAG_GLOBAL_DYNAMIC_VPD; + } else { + pci_pf = enp->en_nic_cfg.enc_pf; + tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf); + } + + if ((rc = ef10_nvram_partn_read_tlv(enp, + NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, + tag, &dvpd, &dvpd_size)) != 0) + goto fail1; + + if (dvpd_size > size) { + rc = ENOSPC; + goto fail2; + } + memcpy(data, dvpd, dvpd_size); + + /* Pad data with all-1s, consistent with update operations */ + memset(data + dvpd_size, 0xff, size - dvpd_size); + + EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd); + + return (0); + +fail2: + EFSYS_PROBE(fail2); + + EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + efx_vpd_tag_t stag; + efx_vpd_tag_t dtag; + efx_vpd_keyword_t skey; + efx_vpd_keyword_t dkey; + unsigned int scont; + unsigned int dcont; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + /* + * Strictly you could take the view that dynamic vpd is optional. + * Instead, to conform more closely to the read/verify/reinit() + * paradigm, we require dynamic vpd. ef10_vpd_reinit() will + * reinitialize it as required. + */ + if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0) + goto fail1; + + /* + * Verify that there is no duplication between the static and + * dynamic cfg sectors. + */ + if (enp->en_arch.ef10.ena_svpd_length == 0) + goto done; + + dcont = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_hunk_next(data, size, &dtag, + &dkey, NULL, NULL, &dcont)) != 0) + goto fail2; + if (dcont == 0) + break; + + /* + * Skip the RV keyword. It should be present in both the static + * and dynamic cfg sectors. + */ + if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V')) + continue; + + scont = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_hunk_next( + enp->en_arch.ef10.ena_svpd, + enp->en_arch.ef10.ena_svpd_length, &stag, &skey, + NULL, NULL, &scont)) != 0) + goto fail3; + if (scont == 0) + break; + + if (stag == dtag && skey == dkey) { + rc = EEXIST; + goto fail4; + } + } + } + +done: + return (0); + +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + boolean_t wantpid; + efx_rc_t rc; + + /* + * Only create an ID string if the dynamic cfg doesn't have one + */ + if (enp->en_arch.ef10.ena_svpd_length == 0) + wantpid = B_TRUE; + else { + unsigned int offset; + uint8_t length; + + rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, + enp->en_arch.ef10.ena_svpd_length, + EFX_VPD_ID, 0, &offset, &length); + if (rc == 0) + wantpid = B_FALSE; + else if (rc == ENOENT) + wantpid = B_TRUE; + else + goto fail1; + } + + if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp) +{ + unsigned int offset; + uint8_t length; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + /* Attempt to satisfy the request from svpd first */ + if (enp->en_arch.ef10.ena_svpd_length > 0) { + if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, + enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) == 0) { + evvp->evv_length = length; + memcpy(evvp->evv_value, + enp->en_arch.ef10.ena_svpd + offset, length); + return (0); + } else if (rc != ENOENT) + goto fail1; + } + + /* And then from the provided data buffer */ + if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) != 0) { + if (rc == ENOENT) + return (rc); + goto fail2; + } + + evvp->evv_length = length; + memcpy(evvp->evv_value, data + offset, length); + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_set( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp) +{ + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + /* If the provided (tag,keyword) exists in svpd, then it is readonly */ + if (enp->en_arch.ef10.ena_svpd_length > 0) { + unsigned int offset; + uint8_t length; + + if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd, + enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) == 0) { + rc = EACCES; + goto fail1; + } + } + + if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_vpd_next( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp) +{ + _NOTE(ARGUNUSED(enp, data, size, evvp, contp)) + + return (ENOTSUP); +} + + __checkReturn efx_rc_t +ef10_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + size_t vpd_length; + uint32_t pci_pf; + uint32_t tag; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + if (enp->en_nic_cfg.enc_vpd_is_global) { + tag = TLV_TAG_GLOBAL_DYNAMIC_VPD; + } else { + pci_pf = enp->en_nic_cfg.enc_pf; + tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf); + } + + /* Determine total length of new dynamic VPD */ + if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0) + goto fail1; + + /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */ + if ((rc = ef10_nvram_partn_write_segment_tlv(enp, + NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, + tag, data, vpd_length, B_TRUE)) != 0) { + goto fail2; + } + + return (0); + +fail2: + EFSYS_PROBE(fail2); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + void +ef10_vpd_fini( + __in efx_nic_t *enp) +{ + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || + enp->en_family == EFX_FAMILY_MEDFORD); + + if (enp->en_arch.ef10.ena_svpd_length > 0) { + EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length, + enp->en_arch.ef10.ena_svpd); + + enp->en_arch.ef10.ena_svpd = NULL; + enp->en_arch.ef10.ena_svpd_length = 0; + } +} + +#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ + +#endif /* EFSYS_OPT_VPD */ diff --git a/drivers/net/sfc/base/efx.h b/drivers/net/sfc/base/efx.h index 6c81d98d5e..bfa548eb68 100644 --- a/drivers/net/sfc/base/efx.h +++ b/drivers/net/sfc/base/efx.h @@ -1241,6 +1241,88 @@ efx_nic_get_vi_pool( __out uint32_t *txq_countp); +#if EFSYS_OPT_VPD + +typedef enum efx_vpd_tag_e { + EFX_VPD_ID = 0x02, + EFX_VPD_END = 0x0f, + EFX_VPD_RO = 0x10, + EFX_VPD_RW = 0x11, +} efx_vpd_tag_t; + +typedef uint16_t efx_vpd_keyword_t; + +typedef struct efx_vpd_value_s { + efx_vpd_tag_t evv_tag; + efx_vpd_keyword_t evv_keyword; + uint8_t evv_length; + uint8_t evv_value[0x100]; +} efx_vpd_value_t; + + +#define EFX_VPD_KEYWORD(x, y) ((x) | ((y) << 8)) + +extern __checkReturn efx_rc_t +efx_vpd_init( + __in efx_nic_t *enp); + +extern __checkReturn efx_rc_t +efx_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep); + +extern __checkReturn efx_rc_t +efx_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +efx_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +efx_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +efx_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +efx_vpd_set( + __in efx_nic_t *enp, + __inout_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +efx_vpd_next( + __in efx_nic_t *enp, + __inout_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp); + +extern __checkReturn efx_rc_t +efx_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern void +efx_vpd_fini( + __in efx_nic_t *enp); + +#endif /* EFSYS_OPT_VPD */ + /* NVRAM */ #if EFSYS_OPT_NVRAM diff --git a/drivers/net/sfc/base/efx_check.h b/drivers/net/sfc/base/efx_check.h index e6ef077eee..bd0a482044 100644 --- a/drivers/net/sfc/base/efx_check.h +++ b/drivers/net/sfc/base/efx_check.h @@ -290,6 +290,13 @@ # error "STAT_NAME is obsolete (replaced by NAMES)." #endif +#if EFSYS_OPT_VPD +/* Support PCI Vital Product Data (VPD) */ +# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD) +# error "VPD requires SIENA or HUNTINGTON or MEDFORD" +# endif +#endif /* EFSYS_OPT_VPD */ + #ifdef EFSYS_OPT_WOL # error "WOL is obsolete and is not supported" #endif /* EFSYS_OPT_WOL */ diff --git a/drivers/net/sfc/base/efx_impl.h b/drivers/net/sfc/base/efx_impl.h index 6b84f43514..980f964c07 100644 --- a/drivers/net/sfc/base/efx_impl.h +++ b/drivers/net/sfc/base/efx_impl.h @@ -477,6 +477,24 @@ typedef struct efx_nvram_ops_s { } efx_nvram_ops_t; #endif /* EFSYS_OPT_NVRAM */ +#if EFSYS_OPT_VPD +typedef struct efx_vpd_ops_s { + efx_rc_t (*evpdo_init)(efx_nic_t *); + efx_rc_t (*evpdo_size)(efx_nic_t *, size_t *); + efx_rc_t (*evpdo_read)(efx_nic_t *, caddr_t, size_t); + efx_rc_t (*evpdo_verify)(efx_nic_t *, caddr_t, size_t); + efx_rc_t (*evpdo_reinit)(efx_nic_t *, caddr_t, size_t); + efx_rc_t (*evpdo_get)(efx_nic_t *, caddr_t, size_t, + efx_vpd_value_t *); + efx_rc_t (*evpdo_set)(efx_nic_t *, caddr_t, size_t, + efx_vpd_value_t *); + efx_rc_t (*evpdo_next)(efx_nic_t *, caddr_t, size_t, + efx_vpd_value_t *, unsigned int *); + efx_rc_t (*evpdo_write)(efx_nic_t *, caddr_t, size_t); + void (*evpdo_fini)(efx_nic_t *); +} efx_vpd_ops_t; +#endif /* EFSYS_OPT_VPD */ + #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM __checkReturn efx_rc_t @@ -591,6 +609,9 @@ struct efx_nic_s { efx_nvram_type_t en_nvram_locked; const efx_nvram_ops_t *en_envop; #endif /* EFSYS_OPT_NVRAM */ +#if EFSYS_OPT_VPD + const efx_vpd_ops_t *en_evpdop; +#endif /* EFSYS_OPT_VPD */ #if EFSYS_OPT_RX_SCALE efx_rx_hash_support_t en_hash_support; efx_rx_scale_support_t en_rss_support; @@ -603,6 +624,10 @@ struct efx_nic_s { #if EFSYS_OPT_NVRAM || EFSYS_OPT_VPD unsigned int enu_partn_mask; #endif /* EFSYS_OPT_NVRAM || EFSYS_OPT_VPD */ +#if EFSYS_OPT_VPD + caddr_t enu_svpd; + size_t enu_svpd_length; +#endif /* EFSYS_OPT_VPD */ int enu_unused; } siena; #endif /* EFSYS_OPT_SIENA */ @@ -614,6 +639,10 @@ struct efx_nic_s { int ena_vi_base; int ena_vi_count; int ena_vi_shift; +#if EFSYS_OPT_VPD + caddr_t ena_svpd; + size_t ena_svpd_length; +#endif /* EFSYS_OPT_VPD */ efx_piobuf_handle_t ena_piobuf_handle[EF10_MAX_PIOBUF_NBUFS]; uint32_t ena_piobuf_count; uint32_t ena_pio_alloc_map[EF10_MAX_PIOBUF_NBUFS]; @@ -1020,6 +1049,55 @@ extern void efx_phy_unprobe( __in efx_nic_t *enp); +#if EFSYS_OPT_VPD + +/* VPD utility functions */ + +extern __checkReturn efx_rc_t +efx_vpd_hunk_length( + __in_bcount(size) caddr_t data, + __in size_t size, + __out size_t *lengthp); + +extern __checkReturn efx_rc_t +efx_vpd_hunk_verify( + __in_bcount(size) caddr_t data, + __in size_t size, + __out_opt boolean_t *cksummedp); + +extern __checkReturn efx_rc_t +efx_vpd_hunk_reinit( + __in_bcount(size) caddr_t data, + __in size_t size, + __in boolean_t wantpid); + +extern __checkReturn efx_rc_t +efx_vpd_hunk_get( + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_tag_t tag, + __in efx_vpd_keyword_t keyword, + __out unsigned int *payloadp, + __out uint8_t *paylenp); + +extern __checkReturn efx_rc_t +efx_vpd_hunk_next( + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_tag_t *tagp, + __out efx_vpd_keyword_t *keyword, + __out_opt unsigned int *payloadp, + __out_opt uint8_t *paylenp, + __inout unsigned int *contp); + +extern __checkReturn efx_rc_t +efx_vpd_hunk_set( + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp); + +#endif /* EFSYS_OPT_VPD */ + #if EFSYS_OPT_DIAG extern efx_sram_pattern_fn_t __efx_sram_pattern_fns[]; diff --git a/drivers/net/sfc/base/efx_vpd.c b/drivers/net/sfc/base/efx_vpd.c new file mode 100644 index 0000000000..1e47df2c1a --- /dev/null +++ b/drivers/net/sfc/base/efx_vpd.c @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2009-2016 Solarflare Communications Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of the FreeBSD Project. + */ + +#include "efx.h" +#include "efx_impl.h" + +#if EFSYS_OPT_VPD + +#define TAG_TYPE_LBN 7 +#define TAG_TYPE_WIDTH 1 +#define TAG_TYPE_LARGE_ITEM_DECODE 1 +#define TAG_TYPE_SMALL_ITEM_DECODE 0 + +#define TAG_SMALL_ITEM_NAME_LBN 3 +#define TAG_SMALL_ITEM_NAME_WIDTH 4 +#define TAG_SMALL_ITEM_SIZE_LBN 0 +#define TAG_SMALL_ITEM_SIZE_WIDTH 3 + +#define TAG_LARGE_ITEM_NAME_LBN 0 +#define TAG_LARGE_ITEM_NAME_WIDTH 7 + +#define TAG_NAME_END_DECODE 0x0f +#define TAG_NAME_ID_STRING_DECODE 0x02 +#define TAG_NAME_VPD_R_DECODE 0x10 +#define TAG_NAME_VPD_W_DECODE 0x11 + +#if EFSYS_OPT_SIENA + +static const efx_vpd_ops_t __efx_vpd_siena_ops = { + siena_vpd_init, /* evpdo_init */ + siena_vpd_size, /* evpdo_size */ + siena_vpd_read, /* evpdo_read */ + siena_vpd_verify, /* evpdo_verify */ + siena_vpd_reinit, /* evpdo_reinit */ + siena_vpd_get, /* evpdo_get */ + siena_vpd_set, /* evpdo_set */ + siena_vpd_next, /* evpdo_next */ + siena_vpd_write, /* evpdo_write */ + siena_vpd_fini, /* evpdo_fini */ +}; + +#endif /* EFSYS_OPT_SIENA */ + +#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD + +static const efx_vpd_ops_t __efx_vpd_ef10_ops = { + ef10_vpd_init, /* evpdo_init */ + ef10_vpd_size, /* evpdo_size */ + ef10_vpd_read, /* evpdo_read */ + ef10_vpd_verify, /* evpdo_verify */ + ef10_vpd_reinit, /* evpdo_reinit */ + ef10_vpd_get, /* evpdo_get */ + ef10_vpd_set, /* evpdo_set */ + ef10_vpd_next, /* evpdo_next */ + ef10_vpd_write, /* evpdo_write */ + ef10_vpd_fini, /* evpdo_fini */ +}; + +#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ + + __checkReturn efx_rc_t +efx_vpd_init( + __in efx_nic_t *enp) +{ + const efx_vpd_ops_t *evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); + EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD)); + + switch (enp->en_family) { +#if EFSYS_OPT_SIENA + case EFX_FAMILY_SIENA: + evpdop = &__efx_vpd_siena_ops; + break; +#endif /* EFSYS_OPT_SIENA */ + +#if EFSYS_OPT_HUNTINGTON + case EFX_FAMILY_HUNTINGTON: + evpdop = &__efx_vpd_ef10_ops; + break; +#endif /* EFSYS_OPT_HUNTINGTON */ + +#if EFSYS_OPT_MEDFORD + case EFX_FAMILY_MEDFORD: + evpdop = &__efx_vpd_ef10_ops; + break; +#endif /* EFSYS_OPT_MEDFORD */ + + default: + EFSYS_ASSERT(0); + rc = ENOTSUP; + goto fail1; + } + + if (evpdop->evpdo_init != NULL) { + if ((rc = evpdop->evpdo_init(enp)) != 0) + goto fail2; + } + + enp->en_evpdop = evpdop; + enp->en_mod_flags |= EFX_MOD_VPD; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_size(enp, sizep)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_read(enp, data, size)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if (evpdop->evpdo_reinit == NULL) { + rc = ENOTSUP; + goto fail1; + } + + if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) { + if (rc == ENOENT) + return (rc); + + goto fail1; + } + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_set( + __in efx_nic_t *enp, + __inout_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_next( + __in efx_nic_t *enp, + __inout_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if ((rc = evpdop->evpdo_write(enp, data, size)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + +static __checkReturn efx_rc_t +efx_vpd_next_tag( + __in caddr_t data, + __in size_t size, + __inout unsigned int *offsetp, + __out efx_vpd_tag_t *tagp, + __out uint16_t *lengthp) +{ + efx_byte_t byte; + efx_word_t word; + uint8_t name; + uint16_t length; + size_t headlen; + efx_rc_t rc; + + if (*offsetp >= size) { + rc = EFAULT; + goto fail1; + } + + EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]); + + switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) { + case TAG_TYPE_SMALL_ITEM_DECODE: + headlen = 1; + + name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME); + length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE); + + break; + + case TAG_TYPE_LARGE_ITEM_DECODE: + headlen = 3; + + if (*offsetp + headlen > size) { + rc = EFAULT; + goto fail2; + } + + name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME); + EFX_POPULATE_WORD_2(word, + EFX_BYTE_0, data[*offsetp + 1], + EFX_BYTE_1, data[*offsetp + 2]); + length = EFX_WORD_FIELD(word, EFX_WORD_0); + + break; + + default: + rc = EFAULT; + goto fail2; + } + + if (*offsetp + headlen + length > size) { + rc = EFAULT; + goto fail3; + } + + EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END); + EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID); + EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO); + EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW); + if (name != EFX_VPD_END && name != EFX_VPD_ID && + name != EFX_VPD_RO) { + rc = EFAULT; + goto fail4; + } + + *tagp = name; + *lengthp = length; + *offsetp += headlen; + + return (0); + +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + +static __checkReturn efx_rc_t +efx_vpd_next_keyword( + __in_bcount(size) caddr_t tag, + __in size_t size, + __in unsigned int pos, + __out efx_vpd_keyword_t *keywordp, + __out uint8_t *lengthp) +{ + efx_vpd_keyword_t keyword; + uint8_t length; + efx_rc_t rc; + + if (pos + 3U > size) { + rc = EFAULT; + goto fail1; + } + + keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]); + length = tag[pos + 2]; + + if (length == 0 || pos + 3U + length > size) { + rc = EFAULT; + goto fail2; + } + + *keywordp = keyword; + *lengthp = length; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_hunk_length( + __in_bcount(size) caddr_t data, + __in size_t size, + __out size_t *lengthp) +{ + efx_vpd_tag_t tag; + unsigned int offset; + uint16_t taglen; + efx_rc_t rc; + + offset = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_next_tag(data, size, &offset, + &tag, &taglen)) != 0) + goto fail1; + offset += taglen; + if (tag == EFX_VPD_END) + break; + } + + *lengthp = offset; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_hunk_verify( + __in_bcount(size) caddr_t data, + __in size_t size, + __out_opt boolean_t *cksummedp) +{ + efx_vpd_tag_t tag; + efx_vpd_keyword_t keyword; + unsigned int offset; + unsigned int pos; + unsigned int i; + uint16_t taglen; + uint8_t keylen; + uint8_t cksum; + boolean_t cksummed = B_FALSE; + efx_rc_t rc; + + /* + * Parse every tag,keyword in the existing VPD. If the csum is present, + * the assert it is correct, and is the final keyword in the RO block. + */ + offset = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_next_tag(data, size, &offset, + &tag, &taglen)) != 0) + goto fail1; + if (tag == EFX_VPD_END) + break; + else if (tag == EFX_VPD_ID) + goto done; + + for (pos = 0; pos != taglen; pos += 3 + keylen) { + /* RV keyword must be the last in the block */ + if (cksummed) { + rc = EFAULT; + goto fail2; + } + + if ((rc = efx_vpd_next_keyword(data + offset, + taglen, pos, &keyword, &keylen)) != 0) + goto fail3; + + if (keyword == EFX_VPD_KEYWORD('R', 'V')) { + cksum = 0; + for (i = 0; i < offset + pos + 4; i++) + cksum += data[i]; + + if (cksum != 0) { + rc = EFAULT; + goto fail4; + } + + cksummed = B_TRUE; + } + } + + done: + offset += taglen; + } + + if (!cksummed) { + rc = EFAULT; + goto fail5; + } + + if (cksummedp != NULL) + *cksummedp = cksummed; + + return (0); + +fail5: + EFSYS_PROBE(fail5); +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + +static uint8_t __efx_vpd_blank_pid[] = { + /* Large resource type ID length 1 */ + 0x82, 0x01, 0x00, + /* Product name ' ' */ + 0x32, +}; + +static uint8_t __efx_vpd_blank_r[] = { + /* Large resource type VPD-R length 4 */ + 0x90, 0x04, 0x00, + /* RV keyword length 1 */ + 'R', 'V', 0x01, + /* RV payload checksum */ + 0x00, +}; + + __checkReturn efx_rc_t +efx_vpd_hunk_reinit( + __in_bcount(size) caddr_t data, + __in size_t size, + __in boolean_t wantpid) +{ + unsigned int offset = 0; + unsigned int pos; + efx_byte_t byte; + uint8_t cksum; + efx_rc_t rc; + + if (size < 0x100) { + rc = ENOSPC; + goto fail1; + } + + if (wantpid) { + memcpy(data + offset, __efx_vpd_blank_pid, + sizeof (__efx_vpd_blank_pid)); + offset += sizeof (__efx_vpd_blank_pid); + } + + memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r)); + offset += sizeof (__efx_vpd_blank_r); + + /* Update checksum */ + cksum = 0; + for (pos = 0; pos < offset; pos++) + cksum += data[pos]; + data[offset - 1] -= cksum; + + /* Append trailing tag */ + EFX_POPULATE_BYTE_3(byte, + TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE, + TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE, + TAG_SMALL_ITEM_SIZE, 0); + data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0); + offset++; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_hunk_next( + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_tag_t *tagp, + __out efx_vpd_keyword_t *keywordp, + __out_opt unsigned int *payloadp, + __out_opt uint8_t *paylenp, + __inout unsigned int *contp) +{ + efx_vpd_tag_t tag; + efx_vpd_keyword_t keyword = 0; + unsigned int offset; + unsigned int pos; + unsigned int index; + uint16_t taglen; + uint8_t keylen; + uint8_t paylen; + efx_rc_t rc; + + offset = index = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_next_tag(data, size, &offset, + &tag, &taglen)) != 0) + goto fail1; + + if (tag == EFX_VPD_END) { + keyword = 0; + paylen = 0; + index = 0; + break; + } + + if (tag == EFX_VPD_ID) { + if (index++ == *contp) { + EFSYS_ASSERT3U(taglen, <, 0x100); + keyword = 0; + paylen = (uint8_t)MIN(taglen, 0xff); + + goto done; + } + } else { + for (pos = 0; pos != taglen; pos += 3 + keylen) { + if ((rc = efx_vpd_next_keyword(data + offset, + taglen, pos, &keyword, &keylen)) != 0) + goto fail2; + + if (index++ == *contp) { + offset += pos + 3; + paylen = keylen; + + goto done; + } + } + } + + offset += taglen; + } + +done: + *tagp = tag; + *keywordp = keyword; + if (payloadp != NULL) + *payloadp = offset; + if (paylenp != NULL) + *paylenp = paylen; + + *contp = index; + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_hunk_get( + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_tag_t tag, + __in efx_vpd_keyword_t keyword, + __out unsigned int *payloadp, + __out uint8_t *paylenp) +{ + efx_vpd_tag_t itag; + efx_vpd_keyword_t ikeyword; + unsigned int offset; + unsigned int pos; + uint16_t taglen; + uint8_t keylen; + efx_rc_t rc; + + offset = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_next_tag(data, size, &offset, + &itag, &taglen)) != 0) + goto fail1; + if (itag == EFX_VPD_END) + break; + + if (itag == tag) { + if (itag == EFX_VPD_ID) { + EFSYS_ASSERT3U(taglen, <, 0x100); + + *paylenp = (uint8_t)MIN(taglen, 0xff); + *payloadp = offset; + return (0); + } + + for (pos = 0; pos != taglen; pos += 3 + keylen) { + if ((rc = efx_vpd_next_keyword(data + offset, + taglen, pos, &ikeyword, &keylen)) != 0) + goto fail2; + + if (ikeyword == keyword) { + *paylenp = keylen; + *payloadp = offset + pos + 3; + return (0); + } + } + } + + offset += taglen; + } + + /* Not an error */ + return (ENOENT); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +efx_vpd_hunk_set( + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp) +{ + efx_word_t word; + efx_vpd_tag_t tag; + efx_vpd_keyword_t keyword; + unsigned int offset; + unsigned int pos; + unsigned int taghead; + unsigned int source; + unsigned int dest; + unsigned int i; + uint16_t taglen; + uint8_t keylen; + uint8_t cksum; + size_t used; + efx_rc_t rc; + + switch (evvp->evv_tag) { + case EFX_VPD_ID: + if (evvp->evv_keyword != 0) { + rc = EINVAL; + goto fail1; + } + + /* Can't delete the ID keyword */ + if (evvp->evv_length == 0) { + rc = EINVAL; + goto fail1; + } + break; + + case EFX_VPD_RO: + if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) { + rc = EINVAL; + goto fail1; + } + break; + + default: + rc = EINVAL; + goto fail1; + } + + /* Determine total size of all current tags */ + if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0) + goto fail2; + + offset = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + taghead = offset; + if ((rc = efx_vpd_next_tag(data, size, &offset, + &tag, &taglen)) != 0) + goto fail3; + if (tag == EFX_VPD_END) + break; + else if (tag != evvp->evv_tag) { + offset += taglen; + continue; + } + + /* We only support modifying large resource tags */ + if (offset - taghead != 3) { + rc = EINVAL; + goto fail4; + } + + /* + * Work out the offset of the byte immediately after the + * old (=source) and new (=dest) new keyword/tag + */ + pos = 0; + if (tag == EFX_VPD_ID) { + source = offset + taglen; + dest = offset + evvp->evv_length; + goto check_space; + } + + EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO); + source = dest = 0; + for (pos = 0; pos != taglen; pos += 3 + keylen) { + if ((rc = efx_vpd_next_keyword(data + offset, + taglen, pos, &keyword, &keylen)) != 0) + goto fail5; + + if (keyword == evvp->evv_keyword && + evvp->evv_length == 0) { + /* Deleting this keyword */ + source = offset + pos + 3 + keylen; + dest = offset + pos; + break; + + } else if (keyword == evvp->evv_keyword) { + /* Adjusting this keyword */ + source = offset + pos + 3 + keylen; + dest = offset + pos + 3 + evvp->evv_length; + break; + + } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) { + /* The RV keyword must be at the end */ + EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen); + + /* + * The keyword doesn't already exist. If the + * user deleting a non-existant keyword then + * this is a no-op. + */ + if (evvp->evv_length == 0) + return (0); + + /* Insert this keyword before the RV keyword */ + source = offset + pos; + dest = offset + pos + 3 + evvp->evv_length; + break; + } + } + + check_space: + if (used + dest > size + source) { + rc = ENOSPC; + goto fail6; + } + + /* Move trailing data */ + (void) memmove(data + dest, data + source, used - source); + + /* Copy contents */ + memcpy(data + dest - evvp->evv_length, evvp->evv_value, + evvp->evv_length); + + /* Insert new keyword header if required */ + if (tag != EFX_VPD_ID && evvp->evv_length > 0) { + EFX_POPULATE_WORD_1(word, EFX_WORD_0, + evvp->evv_keyword); + data[offset + pos + 0] = + EFX_WORD_FIELD(word, EFX_BYTE_0); + data[offset + pos + 1] = + EFX_WORD_FIELD(word, EFX_BYTE_1); + data[offset + pos + 2] = evvp->evv_length; + } + + /* Modify tag length (large resource type) */ + taglen += (dest - source); + EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen); + data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0); + data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1); + + goto checksum; + } + + /* Unable to find the matching tag */ + rc = ENOENT; + goto fail7; + +checksum: + /* Find the RV tag, and update the checksum */ + offset = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_next_tag(data, size, &offset, + &tag, &taglen)) != 0) + goto fail8; + if (tag == EFX_VPD_END) + break; + if (tag == EFX_VPD_RO) { + for (pos = 0; pos != taglen; pos += 3 + keylen) { + if ((rc = efx_vpd_next_keyword(data + offset, + taglen, pos, &keyword, &keylen)) != 0) + goto fail9; + + if (keyword == EFX_VPD_KEYWORD('R', 'V')) { + cksum = 0; + for (i = 0; i < offset + pos + 3; i++) + cksum += data[i]; + data[i] = -cksum; + break; + } + } + } + + offset += taglen; + } + + /* Zero out the unused portion */ + (void) memset(data + offset + taglen, 0xff, size - offset - taglen); + + return (0); + +fail9: + EFSYS_PROBE(fail9); +fail8: + EFSYS_PROBE(fail8); +fail7: + EFSYS_PROBE(fail7); +fail6: + EFSYS_PROBE(fail6); +fail5: + EFSYS_PROBE(fail5); +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + void +efx_vpd_fini( + __in efx_nic_t *enp) +{ + const efx_vpd_ops_t *evpdop = enp->en_evpdop; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); + + if (evpdop->evpdo_fini != NULL) + evpdop->evpdo_fini(enp); + + enp->en_evpdop = NULL; + enp->en_mod_flags &= ~EFX_MOD_VPD; +} + +#endif /* EFSYS_OPT_VPD */ diff --git a/drivers/net/sfc/base/siena_impl.h b/drivers/net/sfc/base/siena_impl.h index 6a427b9edb..ea6de98314 100644 --- a/drivers/net/sfc/base/siena_impl.h +++ b/drivers/net/sfc/base/siena_impl.h @@ -232,6 +232,69 @@ siena_nvram_partn_set_version( #endif /* EFSYS_OPT_NVRAM */ +#if EFSYS_OPT_VPD + +extern __checkReturn efx_rc_t +siena_vpd_init( + __in efx_nic_t *enp); + +extern __checkReturn efx_rc_t +siena_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep); + +extern __checkReturn efx_rc_t +siena_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +siena_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +siena_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern __checkReturn efx_rc_t +siena_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +siena_vpd_set( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp); + +extern __checkReturn efx_rc_t +siena_vpd_next( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp); + +extern __checkReturn efx_rc_t +siena_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size); + +extern void +siena_vpd_fini( + __in efx_nic_t *enp); + +#endif /* EFSYS_OPT_VPD */ + typedef struct siena_link_state_s { uint32_t sls_adv_cap_mask; uint32_t sls_lp_cap_mask; diff --git a/drivers/net/sfc/base/siena_vpd.c b/drivers/net/sfc/base/siena_vpd.c new file mode 100644 index 0000000000..4fb2e42689 --- /dev/null +++ b/drivers/net/sfc/base/siena_vpd.c @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2009-2016 Solarflare Communications Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of the FreeBSD Project. + */ + +#include "efx.h" +#include "efx_impl.h" + +#if EFSYS_OPT_VPD + +#if EFSYS_OPT_SIENA + +static __checkReturn efx_rc_t +siena_vpd_get_static( + __in efx_nic_t *enp, + __in uint32_t partn, + __deref_out_bcount_opt(*sizep) caddr_t *svpdp, + __out size_t *sizep) +{ + siena_mc_static_config_hdr_t *scfg; + caddr_t svpd; + size_t size; + uint8_t cksum; + unsigned int vpd_offset; + unsigned int vpd_length; + unsigned int hdr_length; + unsigned int pos; + unsigned int region; + efx_rc_t rc; + + EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 || + partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1); + + /* Allocate sufficient memory for the entire static cfg area */ + if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0) + goto fail1; + + EFSYS_KMEM_ALLOC(enp->en_esip, size, scfg); + if (scfg == NULL) { + rc = ENOMEM; + goto fail2; + } + + if ((rc = siena_nvram_partn_read(enp, partn, 0, + (caddr_t)scfg, SIENA_NVRAM_CHUNK)) != 0) + goto fail3; + + /* Verify the magic number */ + if (EFX_DWORD_FIELD(scfg->magic, EFX_DWORD_0) != + SIENA_MC_STATIC_CONFIG_MAGIC) { + rc = EINVAL; + goto fail4; + } + + /* All future versions of the structure must be backwards compatible */ + EFX_STATIC_ASSERT(SIENA_MC_STATIC_CONFIG_VERSION == 0); + + hdr_length = EFX_WORD_FIELD(scfg->length, EFX_WORD_0); + vpd_offset = EFX_DWORD_FIELD(scfg->static_vpd_offset, EFX_DWORD_0); + vpd_length = EFX_DWORD_FIELD(scfg->static_vpd_length, EFX_DWORD_0); + + /* Verify the hdr doesn't overflow the sector size */ + if (hdr_length > size || vpd_offset > size || vpd_length > size || + vpd_length + vpd_offset > size) { + rc = EINVAL; + goto fail5; + } + + /* Read the remainder of scfg + static vpd */ + region = vpd_offset + vpd_length; + if (region > SIENA_NVRAM_CHUNK) { + if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK, + (caddr_t)scfg + SIENA_NVRAM_CHUNK, + region - SIENA_NVRAM_CHUNK)) != 0) + goto fail6; + } + + /* Verify checksum */ + cksum = 0; + for (pos = 0; pos < hdr_length; pos++) + cksum += ((uint8_t *)scfg)[pos]; + if (cksum != 0) { + rc = EINVAL; + goto fail7; + } + + if (vpd_length == 0) + svpd = NULL; + else { + /* Copy the vpd data out */ + EFSYS_KMEM_ALLOC(enp->en_esip, vpd_length, svpd); + if (svpd == NULL) { + rc = ENOMEM; + goto fail8; + } + memcpy(svpd, (caddr_t)scfg + vpd_offset, vpd_length); + } + + EFSYS_KMEM_FREE(enp->en_esip, size, scfg); + + *svpdp = svpd; + *sizep = vpd_length; + + return (0); + +fail8: + EFSYS_PROBE(fail8); +fail7: + EFSYS_PROBE(fail7); +fail6: + EFSYS_PROBE(fail6); +fail5: + EFSYS_PROBE(fail5); +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); + + EFSYS_KMEM_FREE(enp->en_esip, size, scfg); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_init( + __in efx_nic_t *enp) +{ + efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); + caddr_t svpd = NULL; + unsigned int partn; + size_t size = 0; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + partn = (emip->emi_port == 1) + ? MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 + : MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1; + + /* + * We need the static VPD sector to present a unified static+dynamic + * VPD, that is, basically on every read, write, verify cycle. Since + * it should *never* change we can just cache it here. + */ + if ((rc = siena_vpd_get_static(enp, partn, &svpd, &size)) != 0) + goto fail1; + + if (svpd != NULL && size > 0) { + if ((rc = efx_vpd_hunk_verify(svpd, size, NULL)) != 0) + goto fail2; + } + + enp->en_u.siena.enu_svpd = svpd; + enp->en_u.siena.enu_svpd_length = size; + + return (0); + +fail2: + EFSYS_PROBE(fail2); + + EFSYS_KMEM_FREE(enp->en_esip, size, svpd); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_size( + __in efx_nic_t *enp, + __out size_t *sizep) +{ + efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); + uint32_t partn; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + /* + * This function returns the total size the user should allocate + * for all VPD operations. We've already cached the static vpd, + * so we just need to return an upper bound on the dynamic vpd. + * Since the dynamic_config structure can change under our feet, + * (as version numbers are inserted), just be safe and return the + * total size of the dynamic_config *sector* + */ + partn = (emip->emi_port == 1) + ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 + : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1; + + if ((rc = siena_nvram_partn_size(enp, partn, sizep)) != 0) + goto fail1; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_read( + __in efx_nic_t *enp, + __out_bcount(size) caddr_t data, + __in size_t size) +{ + efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); + siena_mc_dynamic_config_hdr_t *dcfg = NULL; + unsigned int vpd_length; + unsigned int vpd_offset; + unsigned int dcfg_partn; + size_t dcfg_size; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + dcfg_partn = (emip->emi_port == 1) + ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 + : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1; + + if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn, + B_TRUE, &dcfg, &dcfg_size)) != 0) + goto fail1; + + vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0); + vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0); + + if (vpd_length > size) { + rc = EFAULT; /* Invalid dcfg: header bigger than sector */ + goto fail2; + } + + EFSYS_ASSERT3U(vpd_length, <=, size); + memcpy(data, (caddr_t)dcfg + vpd_offset, vpd_length); + + /* Pad data with all-1s, consistent with update operations */ + memset(data + vpd_length, 0xff, size - vpd_length); + + EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg); + + return (0); + +fail2: + EFSYS_PROBE(fail2); + + EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_verify( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + efx_vpd_tag_t stag; + efx_vpd_tag_t dtag; + efx_vpd_keyword_t skey; + efx_vpd_keyword_t dkey; + unsigned int scont; + unsigned int dcont; + + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + /* + * Strictly you could take the view that dynamic vpd is optional. + * Instead, to conform more closely to the read/verify/reinit() + * paradigm, we require dynamic vpd. siena_vpd_reinit() will + * reinitialize it as required. + */ + if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0) + goto fail1; + + /* + * Verify that there is no duplication between the static and + * dynamic cfg sectors. + */ + if (enp->en_u.siena.enu_svpd_length == 0) + goto done; + + dcont = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_hunk_next(data, size, &dtag, + &dkey, NULL, NULL, &dcont)) != 0) + goto fail2; + if (dcont == 0) + break; + + /* + * Skip the RV keyword. It should be present in both the static + * and dynamic cfg sectors. + */ + if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V')) + continue; + + scont = 0; + _NOTE(CONSTANTCONDITION) + while (1) { + if ((rc = efx_vpd_hunk_next( + enp->en_u.siena.enu_svpd, + enp->en_u.siena.enu_svpd_length, &stag, &skey, + NULL, NULL, &scont)) != 0) + goto fail3; + if (scont == 0) + break; + + if (stag == dtag && skey == dkey) { + rc = EEXIST; + goto fail4; + } + } + } + +done: + return (0); + +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_reinit( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + boolean_t wantpid; + efx_rc_t rc; + + /* + * Only create a PID if the dynamic cfg doesn't have one + */ + if (enp->en_u.siena.enu_svpd_length == 0) + wantpid = B_TRUE; + else { + unsigned int offset; + uint8_t length; + + rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd, + enp->en_u.siena.enu_svpd_length, + EFX_VPD_ID, 0, &offset, &length); + if (rc == 0) + wantpid = B_FALSE; + else if (rc == ENOENT) + wantpid = B_TRUE; + else + goto fail1; + } + + if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_get( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __inout efx_vpd_value_t *evvp) +{ + unsigned int offset; + uint8_t length; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + /* Attempt to satisfy the request from svpd first */ + if (enp->en_u.siena.enu_svpd_length > 0) { + if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd, + enp->en_u.siena.enu_svpd_length, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) == 0) { + evvp->evv_length = length; + memcpy(evvp->evv_value, + enp->en_u.siena.enu_svpd + offset, length); + return (0); + } else if (rc != ENOENT) + goto fail1; + } + + /* And then from the provided data buffer */ + if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) != 0) { + if (rc == ENOENT) + return (rc); + + goto fail2; + } + + evvp->evv_length = length; + memcpy(evvp->evv_value, data + offset, length); + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_set( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __in efx_vpd_value_t *evvp) +{ + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + /* If the provided (tag,keyword) exists in svpd, then it is readonly */ + if (enp->en_u.siena.enu_svpd_length > 0) { + unsigned int offset; + uint8_t length; + + if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd, + enp->en_u.siena.enu_svpd_length, evvp->evv_tag, + evvp->evv_keyword, &offset, &length)) == 0) { + rc = EACCES; + goto fail1; + } + } + + if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +siena_vpd_next( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size, + __out efx_vpd_value_t *evvp, + __inout unsigned int *contp) +{ + _NOTE(ARGUNUSED(enp, data, size, evvp, contp)) + + return (ENOTSUP); +} + + __checkReturn efx_rc_t +siena_vpd_write( + __in efx_nic_t *enp, + __in_bcount(size) caddr_t data, + __in size_t size) +{ + efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); + siena_mc_dynamic_config_hdr_t *dcfg = NULL; + unsigned int vpd_offset; + unsigned int dcfg_partn; + unsigned int hdr_length; + unsigned int pos; + uint8_t cksum; + size_t partn_size, dcfg_size; + size_t vpd_length; + efx_rc_t rc; + + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + /* Determine total length of all tags */ + if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0) + goto fail1; + + /* Lock dynamic config sector for write, and read structure only */ + dcfg_partn = (emip->emi_port == 1) + ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 + : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1; + + if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0) + goto fail2; + + if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0) + goto fail3; + + if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn, + B_FALSE, &dcfg, &dcfg_size)) != 0) + goto fail4; + + hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0); + + /* Allocated memory should have room for the new VPD */ + if (hdr_length + vpd_length > dcfg_size) { + rc = ENOSPC; + goto fail5; + } + + /* Copy in new vpd and update header */ + vpd_offset = dcfg_size - vpd_length; + EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset, EFX_DWORD_0, vpd_offset); + memcpy((caddr_t)dcfg + vpd_offset, data, vpd_length); + EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, vpd_length); + + /* Update the checksum */ + cksum = 0; + for (pos = 0; pos < hdr_length; pos++) + cksum += ((uint8_t *)dcfg)[pos]; + dcfg->csum.eb_u8[0] -= cksum; + + /* Erase and write the new sector */ + if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0) + goto fail6; + + /* Write out the new structure to nvram */ + if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0, (caddr_t)dcfg, + vpd_offset + vpd_length)) != 0) + goto fail7; + + EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg); + + siena_nvram_partn_unlock(enp, dcfg_partn); + + return (0); + +fail7: + EFSYS_PROBE(fail7); +fail6: + EFSYS_PROBE(fail6); +fail5: + EFSYS_PROBE(fail5); + + EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg); +fail4: + EFSYS_PROBE(fail4); + + siena_nvram_partn_unlock(enp, dcfg_partn); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + void +siena_vpd_fini( + __in efx_nic_t *enp) +{ + EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA); + + if (enp->en_u.siena.enu_svpd_length > 0) { + EFSYS_KMEM_FREE(enp->en_esip, enp->en_u.siena.enu_svpd_length, + enp->en_u.siena.enu_svpd); + + enp->en_u.siena.enu_svpd = NULL; + enp->en_u.siena.enu_svpd_length = 0; + } +} + +#endif /* EFSYS_OPT_SIENA */ + +#endif /* EFSYS_OPT_VPD */