Index: head/sys/dev/sfxge/common/hunt_impl.h =================================================================== --- head/sys/dev/sfxge/common/hunt_impl.h (revision 299401) +++ head/sys/dev/sfxge/common/hunt_impl.h (revision 299402) @@ -1,1059 +1,1135 @@ /*- * Copyright (c) 2012-2015 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. * * $FreeBSD$ */ #ifndef _SYS_HUNT_IMPL_H #define _SYS_HUNT_IMPL_H #include "efx.h" #include "efx_regs.h" #include "efx_regs_ef10.h" #include "efx_mcdi.h" #ifdef __cplusplus extern "C" { #endif /* * FIXME: This is just a power of 2 which fits in an MCDI v1 message, and could * possibly be increased, or the write size reported by newer firmware used * instead. */ #define EF10_NVRAM_CHUNK 0x80 /* Alignment requirement for value written to RX WPTR: * the WPTR must be aligned to an 8 descriptor boundary */ #define EF10_RX_WPTR_ALIGN 8 /* * Max byte offset into the packet the TCP header must start for the hardware * to be able to parse the packet correctly. * FIXME: Move to ef10_impl.h when it is included in all driver builds. */ #define EF10_TCP_HEADER_OFFSET_LIMIT 208 /* Invalid RSS context handle */ #define EF10_RSS_CONTEXT_INVALID (0xffffffff) /* EV */ __checkReturn efx_rc_t ef10_ev_init( __in efx_nic_t *enp); void ef10_ev_fini( __in efx_nic_t *enp); __checkReturn efx_rc_t ef10_ev_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in efsys_mem_t *esmp, __in size_t n, __in uint32_t id, __in efx_evq_t *eep); void ef10_ev_qdestroy( __in efx_evq_t *eep); __checkReturn efx_rc_t ef10_ev_qprime( __in efx_evq_t *eep, __in unsigned int count); void ef10_ev_qpost( __in efx_evq_t *eep, __in uint16_t data); __checkReturn efx_rc_t ef10_ev_qmoderate( __in efx_evq_t *eep, __in unsigned int us); #if EFSYS_OPT_QSTATS void ef10_ev_qstats_update( __in efx_evq_t *eep, __inout_ecount(EV_NQSTATS) efsys_stat_t *stat); #endif /* EFSYS_OPT_QSTATS */ void ef10_ev_rxlabel_init( __in efx_evq_t *eep, __in efx_rxq_t *erp, __in unsigned int label); void ef10_ev_rxlabel_fini( __in efx_evq_t *eep, __in unsigned int label); /* INTR */ __checkReturn efx_rc_t ef10_intr_init( __in efx_nic_t *enp, __in efx_intr_type_t type, __in efsys_mem_t *esmp); void ef10_intr_enable( __in efx_nic_t *enp); void ef10_intr_disable( __in efx_nic_t *enp); void ef10_intr_disable_unlocked( __in efx_nic_t *enp); __checkReturn efx_rc_t ef10_intr_trigger( __in efx_nic_t *enp, __in unsigned int level); void ef10_intr_status_line( __in efx_nic_t *enp, __out boolean_t *fatalp, __out uint32_t *qmaskp); void ef10_intr_status_message( __in efx_nic_t *enp, __in unsigned int message, __out boolean_t *fatalp); void ef10_intr_fatal( __in efx_nic_t *enp); void ef10_intr_fini( __in efx_nic_t *enp); /* NIC */ extern __checkReturn efx_rc_t ef10_nic_probe( __in efx_nic_t *enp); extern __checkReturn efx_rc_t hunt_board_cfg( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_nic_set_drv_limits( __inout efx_nic_t *enp, __in efx_drv_limits_t *edlp); extern __checkReturn efx_rc_t ef10_nic_get_vi_pool( __in efx_nic_t *enp, __out uint32_t *vi_countp); extern __checkReturn efx_rc_t ef10_nic_get_bar_region( __in efx_nic_t *enp, __in efx_nic_region_t region, __out uint32_t *offsetp, __out size_t *sizep); extern __checkReturn efx_rc_t ef10_nic_reset( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_nic_init( __in efx_nic_t *enp); #if EFSYS_OPT_DIAG extern __checkReturn efx_rc_t ef10_nic_register_test( __in efx_nic_t *enp); #endif /* EFSYS_OPT_DIAG */ extern void ef10_nic_fini( __in efx_nic_t *enp); extern void ef10_nic_unprobe( __in efx_nic_t *enp); /* MAC */ extern __checkReturn efx_rc_t ef10_mac_poll( __in efx_nic_t *enp, __out efx_link_mode_t *link_modep); extern __checkReturn efx_rc_t ef10_mac_up( __in efx_nic_t *enp, __out boolean_t *mac_upp); extern __checkReturn efx_rc_t ef10_mac_addr_set( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_mac_pdu_set( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_mac_reconfigure( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_mac_multicast_list_set( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_mac_filter_default_rxq_set( __in efx_nic_t *enp, __in efx_rxq_t *erp, __in boolean_t using_rss); extern void ef10_mac_filter_default_rxq_clear( __in efx_nic_t *enp); #if EFSYS_OPT_LOOPBACK extern __checkReturn efx_rc_t ef10_mac_loopback_set( __in efx_nic_t *enp, __in efx_link_mode_t link_mode, __in efx_loopback_type_t loopback_type); #endif /* EFSYS_OPT_LOOPBACK */ #if EFSYS_OPT_MAC_STATS extern __checkReturn efx_rc_t ef10_mac_stats_update( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __inout_ecount(EFX_MAC_NSTATS) efsys_stat_t *stat, __inout_opt uint32_t *generationp); #endif /* EFSYS_OPT_MAC_STATS */ /* MCDI */ #if EFSYS_OPT_MCDI extern __checkReturn efx_rc_t ef10_mcdi_init( __in efx_nic_t *enp, __in const efx_mcdi_transport_t *mtp); extern void ef10_mcdi_fini( __in efx_nic_t *enp); extern void ef10_mcdi_send_request( __in efx_nic_t *enp, __in void *hdrp, __in size_t hdr_len, __in void *sdup, __in size_t sdu_len); extern __checkReturn boolean_t ef10_mcdi_poll_response( __in efx_nic_t *enp); extern void ef10_mcdi_read_response( __in efx_nic_t *enp, __out_bcount(length) void *bufferp, __in size_t offset, __in size_t length); extern efx_rc_t ef10_mcdi_poll_reboot( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_mcdi_feature_supported( __in efx_nic_t *enp, __in efx_mcdi_feature_id_t id, __out boolean_t *supportedp); #endif /* EFSYS_OPT_MCDI */ /* NVRAM */ #if EFSYS_OPT_NVRAM || EFSYS_OPT_VPD extern __checkReturn efx_rc_t ef10_nvram_buf_read_tlv( __in efx_nic_t *enp, __in_bcount(max_seg_size) caddr_t seg_data, __in size_t max_seg_size, __in uint32_t tag, __deref_out_bcount_opt(*sizep) caddr_t *datap, __out size_t *sizep); extern __checkReturn efx_rc_t ef10_nvram_buf_write_tlv( __inout_bcount(partn_size) caddr_t partn_data, __in size_t partn_size, __in uint32_t tag, __in_bcount(tag_size) caddr_t tag_data, __in size_t tag_size, __out size_t *total_lengthp); extern __checkReturn efx_rc_t ef10_nvram_partn_read_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __deref_out_bcount_opt(*sizep) caddr_t *datap, __out size_t *sizep); extern __checkReturn efx_rc_t ef10_nvram_partn_write_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __in_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t ef10_nvram_partn_write_segment_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __in_bcount(size) caddr_t data, __in size_t size, __in boolean_t all_segments); extern __checkReturn efx_rc_t ef10_nvram_partn_lock( __in efx_nic_t *enp, __in uint32_t partn); extern void ef10_nvram_partn_unlock( __in efx_nic_t *enp, __in uint32_t partn); #endif /* EFSYS_OPT_NVRAM || EFSYS_OPT_VPD */ #if EFSYS_OPT_NVRAM #if EFSYS_OPT_DIAG extern __checkReturn efx_rc_t ef10_nvram_test( __in efx_nic_t *enp); #endif /* EFSYS_OPT_DIAG */ extern __checkReturn efx_rc_t ef10_nvram_type_to_partn( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out uint32_t *partnp); extern __checkReturn efx_rc_t ef10_nvram_partn_size( __in efx_nic_t *enp, __in uint32_t partn, __out size_t *sizep); extern __checkReturn efx_rc_t ef10_nvram_partn_rw_start( __in efx_nic_t *enp, __in uint32_t partn, __out size_t *chunk_sizep); extern __checkReturn efx_rc_t ef10_nvram_partn_read_mode( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size, __in uint32_t mode); extern __checkReturn efx_rc_t ef10_nvram_partn_read( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size); extern __checkReturn efx_rc_t ef10_nvram_partn_erase( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __in size_t size); extern __checkReturn efx_rc_t ef10_nvram_partn_write( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size); extern void ef10_nvram_partn_rw_finish( __in efx_nic_t *enp, __in uint32_t partn); extern __checkReturn efx_rc_t ef10_nvram_partn_get_version( __in efx_nic_t *enp, __in uint32_t partn, __out uint32_t *subtypep, __out_ecount(4) uint16_t version[4]); extern __checkReturn efx_rc_t ef10_nvram_partn_set_version( __in efx_nic_t *enp, __in uint32_t partn, __in_ecount(4) uint16_t version[4]); extern __checkReturn efx_rc_t ef10_nvram_buffer_validate( __in efx_nic_t *enp, __in uint32_t partn, __in_bcount(buffer_size) caddr_t bufferp, __in size_t buffer_size); +extern __checkReturn efx_rc_t +ef10_nvram_buffer_create( + __in efx_nic_t *enp, + __in uint16_t partn_type, + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_find_item_start( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __out uint32_t *startp + ); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_find_end( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __out uint32_t *endp + ); + +extern __checkReturn __success(return != B_FALSE) boolean_t +ef10_nvram_buffer_find_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __out uint32_t *startp, + __out uint32_t *lengthp + ); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_get_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in uint32_t length, + __out_bcount_part(item_max_size, *lengthp) + caddr_t itemp, + __in size_t item_max_size, + __out uint32_t *lengthp + ); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_insert_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in_bcount(length) caddr_t keyp, + __in uint32_t length, + __out uint32_t *lengthp + ); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_delete_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in uint32_t length, + __in uint32_t end + ); + +extern __checkReturn efx_rc_t +ef10_nvram_buffer_finish( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size + ); + #endif /* EFSYS_OPT_NVRAM */ /* PHY */ typedef struct ef10_link_state_s { uint32_t els_adv_cap_mask; uint32_t els_lp_cap_mask; unsigned int els_fcntl; efx_link_mode_t els_link_mode; #if EFSYS_OPT_LOOPBACK efx_loopback_type_t els_loopback; #endif boolean_t els_mac_up; } ef10_link_state_t; extern void ef10_phy_link_ev( __in efx_nic_t *enp, __in efx_qword_t *eqp, __out efx_link_mode_t *link_modep); extern __checkReturn efx_rc_t ef10_phy_get_link( __in efx_nic_t *enp, __out ef10_link_state_t *elsp); extern __checkReturn efx_rc_t ef10_phy_power( __in efx_nic_t *enp, __in boolean_t on); extern __checkReturn efx_rc_t ef10_phy_reconfigure( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_phy_verify( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_phy_oui_get( __in efx_nic_t *enp, __out uint32_t *ouip); #if EFSYS_OPT_PHY_STATS extern __checkReturn efx_rc_t ef10_phy_stats_update( __in efx_nic_t *enp, __in efsys_mem_t *esmp, __inout_ecount(EFX_PHY_NSTATS) uint32_t *stat); #endif /* EFSYS_OPT_PHY_STATS */ #if EFSYS_OPT_PHY_PROPS #if EFSYS_OPT_NAMES extern const char * ef10_phy_prop_name( __in efx_nic_t *enp, __in unsigned int id); #endif /* EFSYS_OPT_NAMES */ extern __checkReturn efx_rc_t ef10_phy_prop_get( __in efx_nic_t *enp, __in unsigned int id, __in uint32_t flags, __out uint32_t *valp); extern __checkReturn efx_rc_t ef10_phy_prop_set( __in efx_nic_t *enp, __in unsigned int id, __in uint32_t val); #endif /* EFSYS_OPT_PHY_PROPS */ #if EFSYS_OPT_BIST extern __checkReturn efx_rc_t hunt_bist_enable_offline( __in efx_nic_t *enp); extern __checkReturn efx_rc_t hunt_bist_start( __in efx_nic_t *enp, __in efx_bist_type_t type); extern __checkReturn efx_rc_t hunt_bist_poll( __in efx_nic_t *enp, __in efx_bist_type_t type, __out efx_bist_result_t *resultp, __out_opt __drv_when(count > 0, __notnull) uint32_t *value_maskp, __out_ecount_opt(count) __drv_when(count > 0, __notnull) unsigned long *valuesp, __in size_t count); extern void hunt_bist_stop( __in efx_nic_t *enp, __in efx_bist_type_t type); #endif /* EFSYS_OPT_BIST */ /* SRAM */ #if EFSYS_OPT_DIAG extern __checkReturn efx_rc_t ef10_sram_test( __in efx_nic_t *enp, __in efx_sram_pattern_fn_t func); #endif /* EFSYS_OPT_DIAG */ /* TX */ extern __checkReturn efx_rc_t ef10_tx_init( __in efx_nic_t *enp); extern void ef10_tx_fini( __in efx_nic_t *enp); extern __checkReturn efx_rc_t ef10_tx_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in efsys_mem_t *esmp, __in size_t n, __in uint32_t id, __in uint16_t flags, __in efx_evq_t *eep, __in efx_txq_t *etp, __out unsigned int *addedp); extern void ef10_tx_qdestroy( __in efx_txq_t *etp); extern __checkReturn efx_rc_t ef10_tx_qpost( __in efx_txq_t *etp, __in_ecount(n) efx_buffer_t *eb, __in unsigned int n, __in unsigned int completed, __inout unsigned int *addedp); extern void ef10_tx_qpush( __in efx_txq_t *etp, __in unsigned int added, __in unsigned int pushed); extern __checkReturn efx_rc_t ef10_tx_qpace( __in efx_txq_t *etp, __in unsigned int ns); extern __checkReturn efx_rc_t ef10_tx_qflush( __in efx_txq_t *etp); extern void ef10_tx_qenable( __in efx_txq_t *etp); extern __checkReturn efx_rc_t ef10_tx_qpio_enable( __in efx_txq_t *etp); extern void ef10_tx_qpio_disable( __in efx_txq_t *etp); extern __checkReturn efx_rc_t ef10_tx_qpio_write( __in efx_txq_t *etp, __in_ecount(buf_length) uint8_t *buffer, __in size_t buf_length, __in size_t pio_buf_offset); extern __checkReturn efx_rc_t ef10_tx_qpio_post( __in efx_txq_t *etp, __in size_t pkt_length, __in unsigned int completed, __inout unsigned int *addedp); extern __checkReturn efx_rc_t ef10_tx_qdesc_post( __in efx_txq_t *etp, __in_ecount(n) efx_desc_t *ed, __in unsigned int n, __in unsigned int completed, __inout unsigned int *addedp); extern void ef10_tx_qdesc_dma_create( __in efx_txq_t *etp, __in efsys_dma_addr_t addr, __in size_t size, __in boolean_t eop, __out efx_desc_t *edp); extern void hunt_tx_qdesc_tso_create( __in efx_txq_t *etp, __in uint16_t ipv4_id, __in uint32_t tcp_seq, __in uint8_t tcp_flags, __out efx_desc_t *edp); extern void ef10_tx_qdesc_tso2_create( __in efx_txq_t *etp, __in uint16_t ipv4_id, __in uint32_t tcp_seq, __in uint16_t tcp_mss, __out_ecount(count) efx_desc_t *edp, __in int count); extern void ef10_tx_qdesc_vlantci_create( __in efx_txq_t *etp, __in uint16_t vlan_tci, __out efx_desc_t *edp); #if EFSYS_OPT_QSTATS extern void ef10_tx_qstats_update( __in efx_txq_t *etp, __inout_ecount(TX_NQSTATS) efsys_stat_t *stat); #endif /* EFSYS_OPT_QSTATS */ /* PIO */ /* Missing register definitions */ #ifndef ER_DZ_TX_PIOBUF_OFST #define ER_DZ_TX_PIOBUF_OFST 0x00001000 #endif #ifndef ER_DZ_TX_PIOBUF_STEP #define ER_DZ_TX_PIOBUF_STEP 8192 #endif #ifndef ER_DZ_TX_PIOBUF_ROWS #define ER_DZ_TX_PIOBUF_ROWS 2048 #endif #ifndef ER_DZ_TX_PIOBUF_SIZE #define ER_DZ_TX_PIOBUF_SIZE 2048 #endif #define HUNT_PIOBUF_NBUFS (16) #define HUNT_PIOBUF_SIZE (ER_DZ_TX_PIOBUF_SIZE) #define HUNT_MIN_PIO_ALLOC_SIZE (HUNT_PIOBUF_SIZE / 32) #define EF10_LEGACY_PF_PRIVILEGE_MASK \ (MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_LINK | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_ONLOAD | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_PTP | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_INSECURE_FILTERS | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_UNICAST | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_MULTICAST | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_BROADCAST | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_ALL_MULTICAST | \ MC_CMD_PRIVILEGE_MASK_IN_GRP_PROMISCUOUS) #define EF10_LEGACY_VF_PRIVILEGE_MASK 0 typedef uint32_t efx_piobuf_handle_t; #define EFX_PIOBUF_HANDLE_INVALID ((efx_piobuf_handle_t) -1) extern __checkReturn efx_rc_t ef10_nic_pio_alloc( __inout efx_nic_t *enp, __out uint32_t *bufnump, __out efx_piobuf_handle_t *handlep, __out uint32_t *blknump, __out uint32_t *offsetp, __out size_t *sizep); extern __checkReturn efx_rc_t ef10_nic_pio_free( __inout efx_nic_t *enp, __in uint32_t bufnum, __in uint32_t blknum); extern __checkReturn efx_rc_t ef10_nic_pio_link( __inout efx_nic_t *enp, __in uint32_t vi_index, __in efx_piobuf_handle_t handle); extern __checkReturn efx_rc_t ef10_nic_pio_unlink( __inout efx_nic_t *enp, __in uint32_t vi_index); /* 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 */ extern __checkReturn efx_rc_t ef10_rx_init( __in efx_nic_t *enp); #if EFSYS_OPT_RX_SCATTER extern __checkReturn efx_rc_t ef10_rx_scatter_enable( __in efx_nic_t *enp, __in unsigned int buf_size); #endif /* EFSYS_OPT_RX_SCATTER */ #if EFSYS_OPT_RX_SCALE extern __checkReturn efx_rc_t ef10_rx_scale_mode_set( __in efx_nic_t *enp, __in efx_rx_hash_alg_t alg, __in efx_rx_hash_type_t type, __in boolean_t insert); extern __checkReturn efx_rc_t ef10_rx_scale_key_set( __in efx_nic_t *enp, __in_ecount(n) uint8_t *key, __in size_t n); extern __checkReturn efx_rc_t ef10_rx_scale_tbl_set( __in efx_nic_t *enp, __in_ecount(n) unsigned int *table, __in size_t n); extern __checkReturn uint32_t ef10_rx_prefix_hash( __in efx_nic_t *enp, __in efx_rx_hash_alg_t func, __in uint8_t *buffer); #endif /* EFSYS_OPT_RX_SCALE */ extern __checkReturn efx_rc_t ef10_rx_prefix_pktlen( __in efx_nic_t *enp, __in uint8_t *buffer, __out uint16_t *lengthp); extern void ef10_rx_qpost( __in efx_rxq_t *erp, __in_ecount(n) efsys_dma_addr_t *addrp, __in size_t size, __in unsigned int n, __in unsigned int completed, __in unsigned int added); extern void ef10_rx_qpush( __in efx_rxq_t *erp, __in unsigned int added, __inout unsigned int *pushedp); extern __checkReturn efx_rc_t ef10_rx_qflush( __in efx_rxq_t *erp); extern void ef10_rx_qenable( __in efx_rxq_t *erp); extern __checkReturn efx_rc_t ef10_rx_qcreate( __in efx_nic_t *enp, __in unsigned int index, __in unsigned int label, __in efx_rxq_type_t type, __in efsys_mem_t *esmp, __in size_t n, __in uint32_t id, __in efx_evq_t *eep, __in efx_rxq_t *erp); extern void ef10_rx_qdestroy( __in efx_rxq_t *erp); extern void ef10_rx_fini( __in efx_nic_t *enp); #if EFSYS_OPT_FILTER typedef struct ef10_filter_handle_s { uint32_t efh_lo; uint32_t efh_hi; } ef10_filter_handle_t; typedef struct ef10_filter_entry_s { uintptr_t efe_spec; /* pointer to filter spec plus busy bit */ ef10_filter_handle_t efe_handle; } ef10_filter_entry_t; /* * BUSY flag indicates that an update is in progress. * AUTO_OLD flag is used to mark and sweep MAC packet filters. */ #define EFX_EF10_FILTER_FLAG_BUSY 1U #define EFX_EF10_FILTER_FLAG_AUTO_OLD 2U #define EFX_EF10_FILTER_FLAGS 3U /* * Size of the hash table used by the driver. Doesn't need to be the * same size as the hardware's table. */ #define EFX_EF10_FILTER_TBL_ROWS 8192 /* Allow for the broadcast address to be added to the multicast list */ #define EFX_EF10_FILTER_MULTICAST_FILTERS_MAX (EFX_MAC_MULTICAST_LIST_MAX + 1) typedef struct ef10_filter_table_s { ef10_filter_entry_t eft_entry[EFX_EF10_FILTER_TBL_ROWS]; efx_rxq_t * eft_default_rxq; boolean_t eft_using_rss; uint32_t eft_unicst_filter_index; boolean_t eft_unicst_filter_set; uint32_t eft_mulcst_filter_indexes[ EFX_EF10_FILTER_MULTICAST_FILTERS_MAX]; uint32_t eft_mulcst_filter_count; } ef10_filter_table_t; __checkReturn efx_rc_t ef10_filter_init( __in efx_nic_t *enp); void ef10_filter_fini( __in efx_nic_t *enp); __checkReturn efx_rc_t ef10_filter_restore( __in efx_nic_t *enp); __checkReturn efx_rc_t ef10_filter_add( __in efx_nic_t *enp, __inout efx_filter_spec_t *spec, __in boolean_t may_replace); __checkReturn efx_rc_t ef10_filter_delete( __in efx_nic_t *enp, __inout efx_filter_spec_t *spec); extern __checkReturn efx_rc_t ef10_filter_supported_filters( __in efx_nic_t *enp, __out uint32_t *list, __out size_t *length); extern __checkReturn efx_rc_t ef10_filter_reconfigure( __in efx_nic_t *enp, __in_ecount(6) uint8_t const *mac_addr, __in boolean_t all_unicst, __in boolean_t mulcst, __in boolean_t all_mulcst, __in boolean_t brdcst, __in_ecount(6*count) uint8_t const *addrs, __in int count); extern void ef10_filter_get_default_rxq( __in efx_nic_t *enp, __out efx_rxq_t **erpp, __out boolean_t *using_rss); extern void ef10_filter_default_rxq_set( __in efx_nic_t *enp, __in efx_rxq_t *erp, __in boolean_t using_rss); extern void ef10_filter_default_rxq_clear( __in efx_nic_t *enp); #endif /* EFSYS_OPT_FILTER */ extern __checkReturn efx_rc_t efx_mcdi_get_function_info( __in efx_nic_t *enp, __out uint32_t *pfp, __out_opt uint32_t *vfp); extern __checkReturn efx_rc_t efx_mcdi_privilege_mask( __in efx_nic_t *enp, __in uint32_t pf, __in uint32_t vf, __out uint32_t *maskp); #ifdef __cplusplus } #endif #endif /* _SYS_HUNT_IMPL_H */ Index: head/sys/dev/sfxge/common/hunt_nvram.c =================================================================== --- head/sys/dev/sfxge/common/hunt_nvram.c (revision 299401) +++ head/sys/dev/sfxge/common/hunt_nvram.c (revision 299402) @@ -1,1981 +1,2333 @@ /*- * Copyright (c) 2012-2015 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 __FBSDID("$FreeBSD$"); #include "efx.h" #include "efx_impl.h" #if EFSYS_OPT_HUNTINGTON #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM #include "ef10_tlv_layout.h" /* Cursor for TLV partition format */ typedef struct tlv_cursor_s { uint32_t *block; /* Base of data block */ uint32_t *current; /* Cursor position */ uint32_t *end; /* End tag position */ uint32_t *limit; /* Last dword of data block */ } tlv_cursor_t; typedef struct nvram_partition_s { uint16_t type; uint8_t chip_select; uint8_t flags; /* * The full length of the NVRAM partition. * This is different from tlv_partition_header.total_length, * which can be smaller. */ uint32_t length; uint32_t erase_size; uint32_t *data; tlv_cursor_t tlv_cursor; } nvram_partition_t; static __checkReturn efx_rc_t tlv_validate_state( __inout tlv_cursor_t *cursor); static void tlv_init_block( __out uint32_t *block) { *block = __CPU_TO_LE_32(TLV_TAG_END); } static uint32_t tlv_tag( __in tlv_cursor_t *cursor) { uint32_t dword, tag; dword = cursor->current[0]; tag = __LE_TO_CPU_32(dword); return (tag); } static size_t tlv_length( __in tlv_cursor_t *cursor) { uint32_t dword, length; if (tlv_tag(cursor) == TLV_TAG_END) return (0); dword = cursor->current[1]; length = __LE_TO_CPU_32(dword); return ((size_t)length); } static uint8_t * tlv_value( __in tlv_cursor_t *cursor) { if (tlv_tag(cursor) == TLV_TAG_END) return (NULL); return ((uint8_t *)(&cursor->current[2])); } static uint8_t * tlv_item( __in tlv_cursor_t *cursor) { if (tlv_tag(cursor) == TLV_TAG_END) return (NULL); return ((uint8_t *)cursor->current); } /* * TLV item DWORD length is tag + length + value (rounded up to DWORD) * equivalent to tlv_n_words_for_len in mc-comms tlv.c */ #define TLV_DWORD_COUNT(length) \ (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t))) static uint32_t * tlv_next_item_ptr( __in tlv_cursor_t *cursor) { uint32_t length; length = tlv_length(cursor); return (cursor->current + TLV_DWORD_COUNT(length)); } static __checkReturn efx_rc_t tlv_advance( __inout tlv_cursor_t *cursor) { efx_rc_t rc; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; if (cursor->current == cursor->end) { /* No more tags after END tag */ cursor->current = NULL; rc = ENOENT; goto fail2; } /* Advance to next item and validate */ cursor->current = tlv_next_item_ptr(cursor); if ((rc = tlv_validate_state(cursor)) != 0) goto fail3; return (0); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } static efx_rc_t tlv_rewind( __in tlv_cursor_t *cursor) { efx_rc_t rc; cursor->current = cursor->block; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } static efx_rc_t tlv_find( __inout tlv_cursor_t *cursor, __in uint32_t tag) { efx_rc_t rc; rc = tlv_rewind(cursor); while (rc == 0) { if (tlv_tag(cursor) == tag) break; rc = tlv_advance(cursor); } return (rc); } static __checkReturn efx_rc_t tlv_validate_state( __inout tlv_cursor_t *cursor) { efx_rc_t rc; /* Check cursor position */ if (cursor->current < cursor->block) { rc = EINVAL; goto fail1; } if (cursor->current > cursor->limit) { rc = EINVAL; goto fail2; } if (tlv_tag(cursor) != TLV_TAG_END) { /* Check current item has space for tag and length */ if (cursor->current > (cursor->limit - 2)) { cursor->current = NULL; rc = EFAULT; goto fail3; } /* Check we have value data for current item and another tag */ if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) { cursor->current = NULL; rc = EFAULT; goto fail4; } } 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 efx_rc_t tlv_init_cursor( __out tlv_cursor_t *cursor, __in uint32_t *block, __in uint32_t *limit, __in uint32_t *current) { cursor->block = block; cursor->limit = limit; cursor->current = current; cursor->end = NULL; return (tlv_validate_state(cursor)); } static __checkReturn efx_rc_t tlv_init_cursor_from_size( __out tlv_cursor_t *cursor, __in_bcount(size) uint8_t *block, __in size_t size) { uint32_t *limit; limit = (uint32_t *)(block + size - sizeof (uint32_t)); return (tlv_init_cursor(cursor, (uint32_t *)block, limit, (uint32_t *)block)); } static __checkReturn efx_rc_t tlv_init_cursor_at_offset( __out tlv_cursor_t *cursor, __in_bcount(size) uint8_t *block, __in size_t size, __in size_t offset) { uint32_t *limit; uint32_t *current; limit = (uint32_t *)(block + size - sizeof (uint32_t)); current = (uint32_t *)(block + offset); return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current)); } static __checkReturn efx_rc_t tlv_require_end( __inout tlv_cursor_t *cursor) { uint32_t *pos; efx_rc_t rc; if (cursor->end == NULL) { pos = cursor->current; if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0) goto fail1; cursor->end = cursor->current; cursor->current = pos; } return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } static size_t tlv_block_length_used( __inout tlv_cursor_t *cursor) { efx_rc_t rc; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; if ((rc = tlv_require_end(cursor)) != 0) goto fail2; /* Return space used (including the END tag) */ return (cursor->end + 1 - cursor->block) * sizeof (uint32_t); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (0); } static uint32_t * tlv_last_segment_end( __in tlv_cursor_t *cursor) { tlv_cursor_t segment_cursor; uint32_t *last_segment_end = cursor->block; uint32_t *segment_start = cursor->block; /* * Go through each segment and check that it has an end tag. If there * is no end tag then the previous segment was the last valid one, * so return the pointer to its end tag. */ while (1) { if (tlv_init_cursor(&segment_cursor, segment_start, cursor->limit, segment_start) != 0) break; if (tlv_require_end(&segment_cursor) != 0) break; last_segment_end = segment_cursor.end; segment_start = segment_cursor.end + 1; } return (last_segment_end); } static uint32_t * tlv_write( __in tlv_cursor_t *cursor, __in uint32_t tag, __in_bcount(size) uint8_t *data, __in size_t size) { uint32_t len = size; uint32_t *ptr; ptr = cursor->current; *ptr++ = __CPU_TO_LE_32(tag); *ptr++ = __CPU_TO_LE_32(len); if (len > 0) { ptr[(len - 1) / sizeof (uint32_t)] = 0; memcpy(ptr, data, len); ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr); } return (ptr); } static __checkReturn efx_rc_t tlv_insert( __inout tlv_cursor_t *cursor, __in uint32_t tag, __in_bcount(size) uint8_t *data, __in size_t size) { unsigned int delta; uint32_t *last_segment_end; efx_rc_t rc; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; if ((rc = tlv_require_end(cursor)) != 0) goto fail2; if (tag == TLV_TAG_END) { rc = EINVAL; goto fail3; } last_segment_end = tlv_last_segment_end(cursor); delta = TLV_DWORD_COUNT(size); if (last_segment_end + 1 + delta > cursor->limit) { rc = ENOSPC; goto fail4; } /* Move data up: new space at cursor->current */ memmove(cursor->current + delta, cursor->current, (last_segment_end + 1 - cursor->current) * sizeof (uint32_t)); /* Adjust the end pointer */ cursor->end += delta; /* Write new TLV item */ tlv_write(cursor, tag, data, size); 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 tlv_delete( __inout tlv_cursor_t *cursor) { unsigned int delta; uint32_t *last_segment_end; efx_rc_t rc; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; if (tlv_tag(cursor) == TLV_TAG_END) { rc = EINVAL; goto fail2; } delta = TLV_DWORD_COUNT(tlv_length(cursor)); if ((rc = tlv_require_end(cursor)) != 0) goto fail3; last_segment_end = tlv_last_segment_end(cursor); /* Shuffle things down, destroying the item at cursor->current */ memmove(cursor->current, cursor->current + delta, (last_segment_end + 1 - cursor->current) * sizeof (uint32_t)); /* Zero the new space at the end of the TLV chain */ memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t)); /* Adjust the end pointer */ cursor->end -= delta; return (0); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } static __checkReturn efx_rc_t tlv_modify( __inout tlv_cursor_t *cursor, __in uint32_t tag, __in_bcount(size) uint8_t *data, __in size_t size) { uint32_t *pos; unsigned int old_ndwords; unsigned int new_ndwords; unsigned int delta; uint32_t *last_segment_end; efx_rc_t rc; if ((rc = tlv_validate_state(cursor)) != 0) goto fail1; if (tlv_tag(cursor) == TLV_TAG_END) { rc = EINVAL; goto fail2; } if (tlv_tag(cursor) != tag) { rc = EINVAL; goto fail3; } old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor)); new_ndwords = TLV_DWORD_COUNT(size); if ((rc = tlv_require_end(cursor)) != 0) goto fail4; last_segment_end = tlv_last_segment_end(cursor); if (new_ndwords > old_ndwords) { /* Expand space used for TLV item */ delta = new_ndwords - old_ndwords; pos = cursor->current + old_ndwords; if (last_segment_end + 1 + delta > cursor->limit) { rc = ENOSPC; goto fail5; } /* Move up: new space at (cursor->current + old_ndwords) */ memmove(pos + delta, pos, (last_segment_end + 1 - pos) * sizeof (uint32_t)); /* Adjust the end pointer */ cursor->end += delta; } else if (new_ndwords < old_ndwords) { /* Shrink space used for TLV item */ delta = old_ndwords - new_ndwords; pos = cursor->current + new_ndwords; /* Move down: remove words at (cursor->current + new_ndwords) */ memmove(pos, pos + delta, (last_segment_end + 1 - pos) * sizeof (uint32_t)); /* Zero the new space at the end of the TLV chain */ memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t)); /* Adjust the end pointer */ cursor->end -= delta; } /* Write new data */ tlv_write(cursor, tag, data, size); 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 uint32_t checksum_tlv_partition( __in nvram_partition_t *partition) { tlv_cursor_t *cursor; uint32_t *ptr; uint32_t *end; uint32_t csum; size_t len; cursor = &partition->tlv_cursor; len = tlv_block_length_used(cursor); EFSYS_ASSERT3U((len & 3), ==, 0); csum = 0; ptr = partition->data; end = &ptr[len >> 2]; while (ptr < end) csum += __LE_TO_CPU_32(*ptr++); return (csum); } static __checkReturn efx_rc_t tlv_update_partition_len_and_cks( __in tlv_cursor_t *cursor) { efx_rc_t rc; nvram_partition_t partition; struct tlv_partition_header *header; struct tlv_partition_trailer *trailer; size_t new_len; /* * We just modified the partition, so the total length may not be * valid. Don't use tlv_find(), which performs some sanity checks * that may fail here. */ partition.data = cursor->block; memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor)); header = (struct tlv_partition_header *)partition.data; /* Sanity check. */ if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) { rc = EFAULT; goto fail1; } new_len = tlv_block_length_used(&partition.tlv_cursor); if (new_len == 0) { rc = EFAULT; goto fail2; } header->total_length = __CPU_TO_LE_32(new_len); /* Ensure the modified partition always has a new generation count. */ header->generation = __CPU_TO_LE_32( __LE_TO_CPU_32(header->generation) + 1); trailer = (struct tlv_partition_trailer *)((uint8_t *)header + new_len - sizeof (*trailer) - sizeof (uint32_t)); trailer->generation = header->generation; trailer->checksum = __CPU_TO_LE_32( __LE_TO_CPU_32(trailer->checksum) - checksum_tlv_partition(&partition)); return (0); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } /* Validate buffer contents (before writing to flash) */ __checkReturn efx_rc_t ef10_nvram_buffer_validate( __in efx_nic_t *enp, __in uint32_t partn, __in_bcount(partn_size) caddr_t partn_data, __in size_t partn_size) { tlv_cursor_t cursor; struct tlv_partition_header *header; struct tlv_partition_trailer *trailer; size_t total_length; uint32_t cksum; int pos; efx_rc_t rc; EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK); if ((partn_data == NULL) || (partn_size == 0)) { rc = EINVAL; goto fail1; } /* The partition header must be the first item (at offset zero) */ if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data, partn_size)) != 0) { rc = EFAULT; goto fail2; } if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) { rc = EINVAL; goto fail3; } header = (struct tlv_partition_header *)tlv_item(&cursor); /* Check TLV partition length (includes the END tag) */ total_length = __LE_TO_CPU_32(header->total_length); if (total_length > partn_size) { rc = EFBIG; goto fail4; } /* Check partition ends with PARTITION_TRAILER and END tags */ if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) { rc = EINVAL; goto fail5; } trailer = (struct tlv_partition_trailer *)tlv_item(&cursor); if ((rc = tlv_advance(&cursor)) != 0) { rc = EINVAL; goto fail6; } if (tlv_tag(&cursor) != TLV_TAG_END) { rc = EINVAL; goto fail7; } /* Check generation counts are consistent */ if (trailer->generation != header->generation) { rc = EINVAL; goto fail8; } /* Verify partition checksum */ cksum = 0; for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) { cksum += *((uint32_t *)(partn_data + pos)); } if (cksum != 0) { rc = EINVAL; goto fail9; } 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); } + + + __checkReturn efx_rc_t +ef10_nvram_buffer_create( + __in efx_nic_t *enp, + __in uint16_t partn_type, + __in_bcount(partn_size) caddr_t partn_data, + __in size_t partn_size) +{ + uint32_t *buf = (uint32_t *)partn_data; + efx_rc_t rc; + tlv_cursor_t cursor; + struct tlv_partition_header header; + struct tlv_partition_trailer trailer; + + unsigned min_buf_size = sizeof (struct tlv_partition_header) + + sizeof (struct tlv_partition_trailer); + if (partn_size < min_buf_size) { + rc = EINVAL; + goto fail1; + } + + memset(buf, 0xff, partn_size); + + tlv_init_block(buf); + if ((rc = tlv_init_cursor(&cursor, buf, + (uint32_t *)((uint8_t *)buf + partn_size), + buf)) != 0) { + goto fail2; + } + + header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER); + header.length = __CPU_TO_LE_32(sizeof (header) - 8); + header.type_id = __CPU_TO_LE_16(partn_type); + header.preset = 0; + header.generation = __CPU_TO_LE_32(1); + header.total_length = 0; /* This will be fixed below. */ + if ((rc = tlv_insert( + &cursor, TLV_TAG_PARTITION_HEADER, + (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0) + goto fail3; + if ((rc = tlv_advance(&cursor)) != 0) + goto fail4; + + trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER); + trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8); + trailer.generation = header.generation; + trailer.checksum = 0; /* This will be fixed below. */ + if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER, + (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0) + goto fail5; + + if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0) + goto fail6; + + /* Check that the partition is valid. */ + if ((rc = ef10_nvram_buffer_validate(enp, partn_type, + partn_data, partn_size)) != 0) + goto fail7; + + return (0); + +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); +} + +static uint32_t +byte_offset( + __in uint32_t *position, + __in uint32_t *base) +{ + return (uint32_t)((uint8_t *)position - (uint8_t *)base); +} + + __checkReturn efx_rc_t +ef10_nvram_buffer_find_item_start( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __out uint32_t *startp) +{ + // Read past partition header to find start address of the first key + tlv_cursor_t cursor; + efx_rc_t rc; + + /* A PARTITION_HEADER tag must be the first item (at offset zero) */ + if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp, + buffer_size)) != 0) { + rc = EFAULT; + goto fail1; + } + if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) { + rc = EINVAL; + goto fail2; + } + + if ((rc = tlv_advance(&cursor)) != 0) { + rc = EINVAL; + goto fail3; + } + *startp = byte_offset(cursor.current, cursor.block); + + if ((rc = tlv_require_end(&cursor)) != 0) + goto fail4; + + 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_nvram_buffer_find_end( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __out uint32_t *endp) +{ + // Read to end of partition + tlv_cursor_t cursor; + efx_rc_t rc; + + if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp, + buffer_size)) != 0) { + rc = EFAULT; + goto fail1; + } + + if ((rc = tlv_require_end(&cursor)) != 0) + goto fail2; + + *endp = byte_offset(tlv_last_segment_end(&cursor)+1, cursor.block); + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn __success(return != B_FALSE) boolean_t +ef10_nvram_buffer_find_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __out uint32_t *startp, + __out uint32_t *lengthp) +{ + // Find TLV at offset and return key start and length + tlv_cursor_t cursor; + uint8_t *key; + uint32_t tag; + + if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp, + buffer_size, offset) != 0) { + return (B_FALSE); + } + + while ((key = tlv_item(&cursor)) != NULL) { + tag = tlv_tag(&cursor); + if (tag == TLV_TAG_PARTITION_HEADER || + tag == TLV_TAG_PARTITION_TRAILER) { + if (tlv_advance(&cursor) != 0) { + break; + } + continue; + } + *startp = byte_offset(cursor.current, cursor.block); + *lengthp = byte_offset(tlv_next_item_ptr(&cursor), + cursor.current); + return (B_TRUE); + } + + return (B_FALSE); +} + + __checkReturn efx_rc_t +ef10_nvram_buffer_get_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in uint32_t length, + __out_bcount_part(item_max_size, *lengthp) + caddr_t itemp, + __in size_t item_max_size, + __out uint32_t *lengthp) +{ + efx_rc_t rc; + tlv_cursor_t cursor; + uint32_t item_length; + + if (item_max_size < length) { + rc = ENOSPC; + goto fail1; + } + + if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp, + buffer_size, offset)) != 0) { + goto fail2; + } + + item_length = tlv_length(&cursor); + if (length < item_length) { + rc = ENOSPC; + goto fail3; + } + memcpy(itemp, tlv_value(&cursor), item_length); + + *lengthp = item_length; + + return (0); + +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_nvram_buffer_insert_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in_bcount(length) caddr_t keyp, + __in uint32_t length, + __out uint32_t *lengthp) +{ + efx_rc_t rc; + tlv_cursor_t cursor; + + if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp, + buffer_size, offset)) != 0) { + goto fail1; + } + + rc = tlv_insert(&cursor, TLV_TAG_LICENSE, keyp, length); + + if (rc != 0) { + goto fail2; + } + + *lengthp = byte_offset(tlv_next_item_ptr(&cursor), + cursor.current); + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_nvram_buffer_delete_item( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size, + __in uint32_t offset, + __in uint32_t length, + __in uint32_t end) +{ + efx_rc_t rc; + tlv_cursor_t cursor; + + if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp, + buffer_size, offset)) != 0) { + goto fail1; + } + + if ((rc = tlv_delete(&cursor)) != 0) + goto fail2; + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + __checkReturn efx_rc_t +ef10_nvram_buffer_finish( + __in_bcount(buffer_size) + caddr_t bufferp, + __in size_t buffer_size) +{ + efx_rc_t rc; + tlv_cursor_t cursor; + + if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp, + buffer_size)) != 0) { + rc = EFAULT; + goto fail1; + } + + if ((rc = tlv_require_end(&cursor)) != 0) + goto fail2; + + if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0) + goto fail3; + + return (0); + +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + + + /* * Read and validate a segment from a partition. A segment is a complete * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may * be multiple segments in a partition, so seg_offset allows segments * beyond the first to be read. */ static __checkReturn efx_rc_t ef10_nvram_read_tlv_segment( __in efx_nic_t *enp, __in uint32_t partn, __in size_t seg_offset, __in_bcount(max_seg_size) caddr_t seg_data, __in size_t max_seg_size) { tlv_cursor_t cursor; struct tlv_partition_header *header; struct tlv_partition_trailer *trailer; size_t total_length; uint32_t cksum; int pos; efx_rc_t rc; EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK); if ((seg_data == NULL) || (max_seg_size == 0)) { rc = EINVAL; goto fail1; } /* Read initial chunk of the segment, starting at offset */ if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data, EF10_NVRAM_CHUNK, MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) { goto fail2; } /* A PARTITION_HEADER tag must be the first item at the given offset */ if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data, max_seg_size)) != 0) { rc = EFAULT; goto fail3; } if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) { rc = EINVAL; goto fail4; } header = (struct tlv_partition_header *)tlv_item(&cursor); /* Check TLV segment length (includes the END tag) */ total_length = __LE_TO_CPU_32(header->total_length); if (total_length > max_seg_size) { rc = EFBIG; goto fail5; } /* Read the remaining segment content */ if (total_length > EF10_NVRAM_CHUNK) { if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset + EF10_NVRAM_CHUNK, seg_data + EF10_NVRAM_CHUNK, total_length - EF10_NVRAM_CHUNK, MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) goto fail6; } /* Check segment ends with PARTITION_TRAILER and END tags */ if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) { rc = EINVAL; goto fail7; } trailer = (struct tlv_partition_trailer *)tlv_item(&cursor); if ((rc = tlv_advance(&cursor)) != 0) { rc = EINVAL; goto fail8; } if (tlv_tag(&cursor) != TLV_TAG_END) { rc = EINVAL; goto fail9; } /* Check data read from segment is consistent */ if (trailer->generation != header->generation) { /* * The partition data may have been modified between successive * MCDI NVRAM_READ requests by the MC or another PCI function. * * The caller must retry to obtain consistent partition data. */ rc = EAGAIN; goto fail10; } /* Verify segment checksum */ cksum = 0; for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) { cksum += *((uint32_t *)(seg_data + pos)); } if (cksum != 0) { rc = EINVAL; goto fail11; } return (0); fail11: EFSYS_PROBE(fail11); fail10: EFSYS_PROBE(fail10); 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); } /* * Read a single TLV item from a host memory * buffer containing a TLV formatted segment. */ __checkReturn efx_rc_t ef10_nvram_buf_read_tlv( __in efx_nic_t *enp, __in_bcount(max_seg_size) caddr_t seg_data, __in size_t max_seg_size, __in uint32_t tag, __deref_out_bcount_opt(*sizep) caddr_t *datap, __out size_t *sizep) { tlv_cursor_t cursor; caddr_t data; size_t length; caddr_t value; efx_rc_t rc; if ((seg_data == NULL) || (max_seg_size == 0)) { rc = EINVAL; goto fail1; } /* Find requested TLV tag in segment data */ if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data, max_seg_size)) != 0) { rc = EFAULT; goto fail2; } if ((rc = tlv_find(&cursor, tag)) != 0) { rc = ENOENT; goto fail3; } value = (caddr_t)tlv_value(&cursor); length = tlv_length(&cursor); if (length == 0) data = NULL; else { /* Copy out data from TLV item */ EFSYS_KMEM_ALLOC(enp->en_esip, length, data); if (data == NULL) { rc = ENOMEM; goto fail4; } memcpy(data, value, length); } *datap = data; *sizep = length; return (0); fail4: EFSYS_PROBE(fail4); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } /* Read a single TLV item from the first segment in a TLV formatted partition */ __checkReturn efx_rc_t ef10_nvram_partn_read_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap, __out size_t *seg_sizep) { caddr_t seg_data = NULL; size_t partn_size = 0; size_t length; caddr_t data; int retry; efx_rc_t rc; /* Allocate sufficient memory for the entire partition */ if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0) goto fail1; if (partn_size == 0) { rc = ENOENT; goto fail2; } EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data); if (seg_data == NULL) { rc = ENOMEM; goto fail3; } /* * Read the first segment in a TLV partition. Retry until consistent * segment contents are returned. Inconsistent data may be read if: * a) the segment contents are invalid * b) the MC has rebooted while we were reading the partition * c) the partition has been modified while we were reading it * Limit retry attempts to ensure forward progress. */ retry = 10; do { rc = ef10_nvram_read_tlv_segment(enp, partn, 0, seg_data, partn_size); } while ((rc == EAGAIN) && (--retry > 0)); if (rc != 0) { /* Failed to obtain consistent segment data */ goto fail4; } if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size, tag, &data, &length)) != 0) goto fail5; EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data); *seg_datap = data; *seg_sizep = length; return (0); fail5: EFSYS_PROBE(fail5); fail4: EFSYS_PROBE(fail4); EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } /* Compute the size of a segment. */ static __checkReturn efx_rc_t ef10_nvram_buf_segment_size( __in caddr_t seg_data, __in size_t max_seg_size, __out size_t *seg_sizep) { efx_rc_t rc; tlv_cursor_t cursor; struct tlv_partition_header *header; uint32_t cksum; int pos; uint32_t *end_tag_position; uint32_t segment_length; /* A PARTITION_HEADER tag must be the first item at the given offset */ if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data, max_seg_size)) != 0) { rc = EFAULT; goto fail1; } if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) { rc = EINVAL; goto fail2; } header = (struct tlv_partition_header *)tlv_item(&cursor); /* Check TLV segment length (includes the END tag) */ *seg_sizep = __LE_TO_CPU_32(header->total_length); if (*seg_sizep > max_seg_size) { rc = EFBIG; goto fail3; } /* Check segment ends with PARTITION_TRAILER and END tags */ if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) { rc = EINVAL; goto fail4; } if ((rc = tlv_advance(&cursor)) != 0) { rc = EINVAL; goto fail5; } if (tlv_tag(&cursor) != TLV_TAG_END) { rc = EINVAL; goto fail6; } end_tag_position = cursor.current; /* Verify segment checksum */ cksum = 0; for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) { cksum += *((uint32_t *)(seg_data + pos)); } if (cksum != 0) { rc = EINVAL; goto fail7; } /* * Calculate total length from HEADER to END tags and compare to * max_seg_size and the total_length field in the HEADER tag. */ segment_length = tlv_block_length_used(&cursor); if (segment_length > max_seg_size) { rc = EINVAL; goto fail8; } if (segment_length != *seg_sizep) { rc = EINVAL; goto fail9; } /* Skip over the first HEADER tag. */ rc = tlv_rewind(&cursor); rc = tlv_advance(&cursor); while (rc == 0) { if (tlv_tag(&cursor) == TLV_TAG_END) { /* Check that the END tag is the one found earlier. */ if (cursor.current != end_tag_position) goto fail10; break; } /* Check for duplicate HEADER tags before the END tag. */ if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) { rc = EINVAL; goto fail11; } rc = tlv_advance(&cursor); } if (rc != 0) goto fail12; return (0); fail12: EFSYS_PROBE(fail12); fail11: EFSYS_PROBE(fail11); fail10: EFSYS_PROBE(fail10); 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); } /* * Add or update a single TLV item in a host memory buffer containing a TLV * formatted segment. Historically partitions consisted of only one segment. */ __checkReturn efx_rc_t ef10_nvram_buf_write_tlv( __inout_bcount(max_seg_size) caddr_t seg_data, __in size_t max_seg_size, __in uint32_t tag, __in_bcount(tag_size) caddr_t tag_data, __in size_t tag_size, __out size_t *total_lengthp) { tlv_cursor_t cursor; struct tlv_partition_header *header; struct tlv_partition_trailer *trailer; uint32_t generation; uint32_t cksum; int pos; efx_rc_t rc; /* A PARTITION_HEADER tag must be the first item (at offset zero) */ if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data, max_seg_size)) != 0) { rc = EFAULT; goto fail1; } if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) { rc = EINVAL; goto fail2; } header = (struct tlv_partition_header *)tlv_item(&cursor); /* Update the TLV chain to contain the new data */ if ((rc = tlv_find(&cursor, tag)) == 0) { /* Modify existing TLV item */ if ((rc = tlv_modify(&cursor, tag, (uint8_t *)tag_data, tag_size)) != 0) goto fail3; } else { /* Insert a new TLV item before the PARTITION_TRAILER */ rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER); if (rc != 0) { rc = EINVAL; goto fail4; } if ((rc = tlv_insert(&cursor, tag, (uint8_t *)tag_data, tag_size)) != 0) { rc = EINVAL; goto fail5; } } /* Find the trailer tag */ if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) { rc = EINVAL; goto fail6; } trailer = (struct tlv_partition_trailer *)tlv_item(&cursor); /* Update PARTITION_HEADER and PARTITION_TRAILER fields */ *total_lengthp = tlv_block_length_used(&cursor); if (*total_lengthp > max_seg_size) { rc = ENOSPC; goto fail7; } generation = __LE_TO_CPU_32(header->generation) + 1; header->total_length = __CPU_TO_LE_32(*total_lengthp); header->generation = __CPU_TO_LE_32(generation); trailer->generation = __CPU_TO_LE_32(generation); /* Recompute PARTITION_TRAILER checksum */ trailer->checksum = 0; cksum = 0; for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) { cksum += *((uint32_t *)(seg_data + pos)); } trailer->checksum = ~cksum + 1; return (0); 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); } /* * Add or update a single TLV item in the first segment of a TLV formatted * dynamic config partition. The first segment is the current active * configuration. */ __checkReturn efx_rc_t ef10_nvram_partn_write_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __in_bcount(size) caddr_t data, __in size_t size) { return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data, size, B_FALSE); } /* * Read a segment from nvram at the given offset into a buffer (segment_data) * and optionally write a new tag to it. */ static __checkReturn efx_rc_t ef10_nvram_segment_write_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __in_bcount(size) caddr_t data, __in size_t size, __inout caddr_t *seg_datap, __inout size_t *partn_offsetp, __inout size_t *src_remain_lenp, __inout size_t *dest_remain_lenp, __in boolean_t write) { efx_rc_t rc; efx_rc_t status; size_t original_segment_size; size_t modified_segment_size; /* * Read the segment from NVRAM into the segment_data buffer and validate * it, returning if it does not validate. This is not a failure unless * this is the first segment in a partition. In this case the caller * must propagate the error. */ status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp, *seg_datap, *src_remain_lenp); if (status != 0) return (EINVAL); status = ef10_nvram_buf_segment_size(*seg_datap, *src_remain_lenp, &original_segment_size); if (status != 0) return (EINVAL); if (write) { /* Update the contents of the segment in the buffer */ if ((rc = ef10_nvram_buf_write_tlv(*seg_datap, *dest_remain_lenp, tag, data, size, &modified_segment_size)) != 0) goto fail1; *dest_remain_lenp -= modified_segment_size; *seg_datap += modified_segment_size; } else { /* * We won't modify this segment, but still need to update the * remaining lengths and pointers. */ *dest_remain_lenp -= original_segment_size; *seg_datap += original_segment_size; } *partn_offsetp += original_segment_size; *src_remain_lenp -= original_segment_size; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } /* * Add or update a single TLV item in either the first segment or in all * segments in a TLV formatted dynamic config partition. Dynamic config * partitions on boards that support RFID are divided into a number of segments, * each formatted like a partition, with header, trailer and end tags. The first * segment is the current active configuration. * * The segments are initialised by manftest and each contain a different * configuration e.g. firmware variant. The firmware can be instructed * via RFID to copy a segment to replace the first segment, hence changing the * active configuration. This allows ops to change the configuration of a board * prior to shipment using RFID. * * Changes to the dynamic config may need to be written to all segments (e.g. * firmware versions) or just the first segment (changes to the active * configuration). See SF-111324-SW "The use of RFID in Solarflare Products". * If only the first segment is written the code still needs to be aware of the * possible presence of subsequent segments as writing to a segment may cause * its size to increase, which would overwrite the subsequent segments and * invalidate them. */ __checkReturn efx_rc_t ef10_nvram_partn_write_segment_tlv( __in efx_nic_t *enp, __in uint32_t partn, __in uint32_t tag, __in_bcount(size) caddr_t data, __in size_t size, __in boolean_t all_segments) { size_t partn_size = 0; caddr_t partn_data; size_t total_length = 0; efx_rc_t rc; size_t current_offset = 0; size_t remaining_original_length; size_t remaining_modified_length; caddr_t segment_data; EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG); /* Allocate sufficient memory for the entire partition */ if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0) goto fail1; EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data); if (partn_data == NULL) { rc = ENOMEM; goto fail2; } remaining_original_length = partn_size; remaining_modified_length = partn_size; segment_data = partn_data; /* Lock the partition */ if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0) goto fail3; /* Iterate over each (potential) segment to update it. */ do { boolean_t write = all_segments || current_offset == 0; rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size, &segment_data, ¤t_offset, &remaining_original_length, &remaining_modified_length, write); if (rc != 0) { if (current_offset == 0) { /* * If no data has been read then the first * segment is invalid, which is an error. */ goto fail4; } break; } } while (current_offset < partn_size); total_length = segment_data - partn_data; /* * We've run out of space. This should actually be dealt with by * ef10_nvram_buf_write_tlv returning ENOSPC. */ if (total_length > partn_size) { rc = ENOSPC; goto fail5; } /* Erase the whole partition in NVRAM */ if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0) goto fail6; /* Write new partition contents from the buffer to NVRAM */ if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data, total_length)) != 0) goto fail7; /* Unlock the partition */ ef10_nvram_partn_unlock(enp, partn); EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data); return (0); fail7: EFSYS_PROBE(fail7); fail6: EFSYS_PROBE(fail6); fail5: EFSYS_PROBE(fail5); fail4: EFSYS_PROBE(fail4); ef10_nvram_partn_unlock(enp, partn); fail3: EFSYS_PROBE(fail3); EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } /* * Get the size of a NVRAM partition. This is the total size allocated in nvram, * not the data used by the segments in the partition. */ __checkReturn efx_rc_t ef10_nvram_partn_size( __in efx_nic_t *enp, __in uint32_t partn, __out size_t *sizep) { efx_rc_t rc; if ((rc = efx_mcdi_nvram_info(enp, partn, sizep, NULL, NULL, NULL)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t ef10_nvram_partn_lock( __in efx_nic_t *enp, __in uint32_t partn) { efx_rc_t rc; if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t ef10_nvram_partn_read_mode( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size, __in uint32_t mode) { size_t chunk; efx_rc_t rc; while (size > 0) { chunk = MIN(size, EF10_NVRAM_CHUNK); if ((rc = efx_mcdi_nvram_read(enp, partn, offset, data, chunk, mode)) != 0) { goto fail1; } size -= chunk; data += chunk; offset += chunk; } return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t ef10_nvram_partn_read( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size) { /* * Read requests which come in through the EFX API expect to * read the current, active partition. */ return ef10_nvram_partn_read_mode(enp, partn, offset, data, size, MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT); } __checkReturn efx_rc_t ef10_nvram_partn_erase( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __in size_t size) { efx_rc_t rc; uint32_t erase_size; if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL, &erase_size, NULL)) != 0) goto fail1; if (erase_size == 0) { if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0) goto fail2; } else { if (size % erase_size != 0) { rc = EINVAL; goto fail3; } while (size > 0) { if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, erase_size)) != 0) goto fail4; offset += erase_size; size -= erase_size; } } 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_nvram_partn_write( __in efx_nic_t *enp, __in uint32_t partn, __in unsigned int offset, __out_bcount(size) caddr_t data, __in size_t size) { size_t chunk; uint32_t write_size; efx_rc_t rc; if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL, NULL, &write_size)) != 0) goto fail1; if (write_size != 0) { /* * Check that the size is a multiple of the write chunk size if * the write chunk size is available. */ if (size % write_size != 0) { rc = EINVAL; goto fail2; } } else { write_size = EF10_NVRAM_CHUNK; } while (size > 0) { chunk = MIN(size, write_size); if ((rc = efx_mcdi_nvram_write(enp, partn, offset, data, chunk)) != 0) { goto fail3; } size -= chunk; data += chunk; offset += chunk; } return (0); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } void ef10_nvram_partn_unlock( __in efx_nic_t *enp, __in uint32_t partn) { boolean_t reboot; efx_rc_t rc; reboot = B_FALSE; if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0) goto fail1; return; fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); } __checkReturn efx_rc_t ef10_nvram_partn_set_version( __in efx_nic_t *enp, __in uint32_t partn, __in_ecount(4) uint16_t version[4]) { struct tlv_partition_version partn_version; size_t size; efx_rc_t rc; /* Add or modify partition version TLV item */ partn_version.version_w = __CPU_TO_LE_16(version[0]); partn_version.version_x = __CPU_TO_LE_16(version[1]); partn_version.version_y = __CPU_TO_LE_16(version[2]); partn_version.version_z = __CPU_TO_LE_16(version[3]); size = sizeof (partn_version) - (2 * sizeof (uint32_t)); /* Write the version number to all segments in the partition */ if ((rc = ef10_nvram_partn_write_segment_tlv(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, TLV_TAG_PARTITION_VERSION(partn), (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */ #if EFSYS_OPT_NVRAM typedef struct ef10_parttbl_entry_s { unsigned int partn; unsigned int port; efx_nvram_type_t nvtype; } ef10_parttbl_entry_t; /* Translate EFX NVRAM types to firmware partition types */ static ef10_parttbl_entry_t hunt_parttbl[] = { {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE} }; static ef10_parttbl_entry_t medford_parttbl[] = { {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG}, {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP}, {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE}, {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE} }; static __checkReturn efx_rc_t ef10_parttbl_get( __in efx_nic_t *enp, __out ef10_parttbl_entry_t **parttblp, __out size_t *parttbl_rowsp) { switch (enp->en_family) { case EFX_FAMILY_HUNTINGTON: *parttblp = hunt_parttbl; *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl); break; case EFX_FAMILY_MEDFORD: *parttblp = medford_parttbl; *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl); break; default: EFSYS_ASSERT(B_FALSE); return (EINVAL); } return (0); } __checkReturn efx_rc_t ef10_nvram_type_to_partn( __in efx_nic_t *enp, __in efx_nvram_type_t type, __out uint32_t *partnp) { efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); ef10_parttbl_entry_t *parttbl = NULL; size_t parttbl_rows = 0; unsigned int i; EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES); EFSYS_ASSERT(partnp != NULL); if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) { for (i = 0; i < parttbl_rows; i++) { ef10_parttbl_entry_t *entry = &parttbl[i]; if (entry->nvtype == type && entry->port == emip->emi_port) { *partnp = entry->partn; return (0); } } } return (ENOTSUP); } #if EFSYS_OPT_DIAG static __checkReturn efx_rc_t ef10_nvram_partn_to_type( __in efx_nic_t *enp, __in uint32_t partn, __out efx_nvram_type_t *typep) { efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); ef10_parttbl_entry_t *parttbl = NULL; size_t parttbl_rows = 0; unsigned int i; EFSYS_ASSERT(typep != NULL); if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) { for (i = 0; i < parttbl_rows; i++) { ef10_parttbl_entry_t *entry = &parttbl[i]; if (entry->partn == partn && entry->port == emip->emi_port) { *typep = entry->nvtype; return (0); } } } return (ENOTSUP); } __checkReturn efx_rc_t ef10_nvram_test( __in efx_nic_t *enp) { efx_nvram_type_t type; unsigned int npartns = 0; uint32_t *partns = NULL; size_t size; unsigned int i; efx_rc_t rc; /* Read available partitions from NVRAM partition map */ size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t); EFSYS_KMEM_ALLOC(enp->en_esip, size, partns); if (partns == NULL) { rc = ENOMEM; goto fail1; } if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size, &npartns)) != 0) { goto fail2; } for (i = 0; i < npartns; i++) { /* Check if the partition is supported for this port */ if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0) continue; if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0) goto fail3; } EFSYS_KMEM_FREE(enp->en_esip, size, partns); return (0); fail3: EFSYS_PROBE(fail3); fail2: EFSYS_PROBE(fail2); EFSYS_KMEM_FREE(enp->en_esip, size, partns); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } #endif /* EFSYS_OPT_DIAG */ __checkReturn efx_rc_t ef10_nvram_partn_get_version( __in efx_nic_t *enp, __in uint32_t partn, __out uint32_t *subtypep, __out_ecount(4) uint16_t version[4]) { efx_rc_t rc; /* FIXME: get highest partn version from all ports */ /* FIXME: return partn description if available */ if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep, version, NULL, 0)) != 0) goto fail1; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } __checkReturn efx_rc_t ef10_nvram_partn_rw_start( __in efx_nic_t *enp, __in uint32_t partn, __out size_t *chunk_sizep) { efx_rc_t rc; if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0) goto fail1; if (chunk_sizep != NULL) *chunk_sizep = EF10_NVRAM_CHUNK; return (0); fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); } void ef10_nvram_partn_rw_finish( __in efx_nic_t *enp, __in uint32_t partn) { ef10_nvram_partn_unlock(enp, partn); } #endif /* EFSYS_OPT_NVRAM */ #endif /* EFSYS_OPT_HUNTINGTON */