From: Stefan Sperling Subject: ice(4) firmware support To: tech@openbsd.org Date: Fri, 28 Mar 2025 12:00:33 +0100 ice(4) devices ship with a limited capability firmware in flash, which is what our driver has been using so far. However, most offloading features require a firmware to be loaded. (Intel calls it "Dynamic Device Personalization", "DDP", rather than just "firmware".) This patch adds support for loading firmware. Even if we do not make use of the advanced features yet, it seems reasonable to run the latest available firmware on these devices, seeing they are just about as complex and sensitive as network switches. If firmware cannot be found on disk the driver prints a warning but remains operational with its basic feature set: ice0: could not read firmware ice-ddp (error 2); entering safe mode ice0: fw 6.2.9 api 1.7 nvm 3.20 etid 8000d853 netlist 3.20.5000-1.e.0.495c77bc oem 1.3146.0, address ... When firmware can be loaded the driver shows a ddp version in dmesg: ice0: fw 6.2.9 api 1.7 nvm 3.20 etid 8000d853 netlist 3.20.5000-1.e.0.495c77bc oem 1.3146.0 ddp 1.3.41.0, address ... I will send a corresponding firmware port to ports@ next. ok? teach ice(4) to load firmware packages; based on the freebsd driver M sys/dev/pci/if_ice.c | 2632+ 528- M sys/dev/pci/if_icevar.h | 329+ 4- 2 files changed, 2961 insertions(+), 532 deletions(-) commit - e3f22ffac827541d427f44b1e428849ef707c76a commit + d17331a27416ba587241de0cd7b8aedb707b9ad8 blob - d564eb220f3f35608ba89006fefa6362874ef754 blob + 8e970791b09998501432c6c485c6a15a03c47ffd --- sys/dev/pci/if_ice.c +++ sys/dev/pci/if_ice.c @@ -14237,17 +14237,222 @@ ice_send_version(struct ice_softc *sc) return (0); } -void +int ice_reinit_hw(struct ice_softc *sc) { struct ice_hw *hw = &sc->hw; + enum ice_fw_modes fw_mode; + enum ice_status status; + int err; - ice_deinit_hw(hw); + fw_mode = ice_get_fw_mode(hw); + if (fw_mode == ICE_FW_MODE_REC) { + printf("%s: firmware is in recovery mode\n", + sc->sc_dev.dv_xname); +#if 0 + err = ice_attach_pre_recovery_mode(sc); + if (err) + goto free_pci_mapping; +#endif + return ENODEV; + } - printf("%s: not implemented\n", __func__); + /* Initialize the hw data structure */ + status = ice_init_hw(hw); + if (status) { + if (status == ICE_ERR_FW_API_VER) { + printf("%s: incompatible firmware API version\n", + sc->sc_dev.dv_xname); +#if 0 + /* Enter recovery mode, so that the driver remains + * loaded. This way, if the system administrator + * cannot update the driver, they may still attempt to + * downgrade the NVM. + */ + err = ice_attach_pre_recovery_mode(sc); + if (err) + goto free_pci_mapping; +#endif + err = ENOTSUP; + goto deinit_hw; + } else { + printf("%s: could not initialize hardware, " + "status %s aq_err %s\n", + sc->sc_dev.dv_xname, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + err = EIO; + goto deinit_hw; + } + } + + ice_init_device_features(sc); + + /* Keep flag set by default */ + ice_set_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN); + + /* Notify firmware of the device driver version */ + err = ice_send_version(sc); +deinit_hw: + if (err) + ice_deinit_hw(hw); + return err; } /** + * ice_get_set_tx_topo - get or set tx topology + * @hw: pointer to the HW struct + * @buf: pointer to tx topology buffer + * @buf_size: buffer size + * @cd: pointer to command details structure or NULL + * @flags: pointer to descriptor flags + * @set: 0-get, 1-set topology + * + * The function will get or set tx topology + */ +enum ice_status +ice_get_set_tx_topo(struct ice_hw *hw, uint8_t *buf, uint16_t buf_size, + struct ice_sq_cd *cd, uint8_t *flags, bool set) +{ + struct ice_aqc_get_set_tx_topo *cmd; + struct ice_aq_desc desc; + enum ice_status status; + + cmd = &desc.params.get_set_tx_topo; + if (set) { + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_tx_topo); + cmd->set_flags = ICE_AQC_TX_TOPO_FLAGS_ISSUED; + /* requested to update a new topology, not a default topolgy */ + if (buf) + cmd->set_flags |= ICE_AQC_TX_TOPO_FLAGS_SRC_RAM | + ICE_AQC_TX_TOPO_FLAGS_LOAD_NEW; + } else { + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_tx_topo); + cmd->get_flags = ICE_AQC_TX_TOPO_GET_RAM; + } + desc.flags |= htole16(ICE_AQ_FLAG_RD); + status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd); + if (status) + return status; + /* read the return flag values (first byte) for get operation */ + if (!set && flags) + *flags = desc.params.get_set_tx_topo.set_flags; + + return ICE_SUCCESS; +} + +/** + * ice_verify_pkg - verify package + * @pkg: pointer to the package buffer + * @len: size of the package buffer + * + * Verifies various attributes of the package file, including length, format + * version, and the requirement of at least one segment. + */ +enum ice_ddp_state +ice_verify_pkg(struct ice_pkg_hdr *pkg, uint32_t len) +{ + uint32_t seg_count; + uint32_t i; + + if (len < ice_struct_size(pkg, seg_offset, 1)) + return ICE_DDP_PKG_INVALID_FILE; + + if (pkg->pkg_format_ver.major != ICE_PKG_FMT_VER_MAJ || + pkg->pkg_format_ver.minor != ICE_PKG_FMT_VER_MNR || + pkg->pkg_format_ver.update != ICE_PKG_FMT_VER_UPD || + pkg->pkg_format_ver.draft != ICE_PKG_FMT_VER_DFT) + return ICE_DDP_PKG_INVALID_FILE; + + /* pkg must have at least one segment */ + seg_count = le32toh(pkg->seg_count); + if (seg_count < 1) + return ICE_DDP_PKG_INVALID_FILE; + + /* make sure segment array fits in package length */ + if (len < ice_struct_size(pkg, seg_offset, seg_count)) + return ICE_DDP_PKG_INVALID_FILE; + + /* all segments must fit within length */ + for (i = 0; i < seg_count; i++) { + uint32_t off = le32toh(pkg->seg_offset[i]); + struct ice_generic_seg_hdr *seg; + + /* segment header must fit */ + if (len < off + sizeof(*seg)) + return ICE_DDP_PKG_INVALID_FILE; + + seg = (struct ice_generic_seg_hdr *)((uint8_t *)pkg + off); + + /* segment body must fit */ + if (len < off + le32toh(seg->seg_size)) + return ICE_DDP_PKG_INVALID_FILE; + } + + return ICE_DDP_PKG_SUCCESS; +} + +/** + * ice_find_seg_in_pkg + * @hw: pointer to the hardware structure + * @seg_type: the segment type to search for (i.e., SEGMENT_TYPE_CPK) + * @pkg_hdr: pointer to the package header to be searched + * + * This function searches a package file for a particular segment type. On + * success it returns a pointer to the segment header, otherwise it will + * return NULL. + */ +struct ice_generic_seg_hdr * +ice_find_seg_in_pkg(struct ice_hw *hw, uint32_t seg_type, + struct ice_pkg_hdr *pkg_hdr) +{ + uint32_t i; + + DNPRINTF(ICE_DBG_PKG, "Package format version: %d.%d.%d.%d\n", + pkg_hdr->pkg_format_ver.major, pkg_hdr->pkg_format_ver.minor, + pkg_hdr->pkg_format_ver.update, + pkg_hdr->pkg_format_ver.draft); + + /* Search all package segments for the requested segment type */ + for (i = 0; i < le32toh(pkg_hdr->seg_count); i++) { + struct ice_generic_seg_hdr *seg; + + seg = (struct ice_generic_seg_hdr *) + ((uint8_t *)pkg_hdr + le32toh(pkg_hdr->seg_offset[i])); + + if (le32toh(seg->seg_type) == seg_type) + return seg; + } + + return NULL; +} + +/** + * ice_pkg_val_buf + * @buf: pointer to the ice buffer + * + * This helper function validates a buffer's header. + */ +struct ice_buf_hdr * +ice_pkg_val_buf(struct ice_buf *buf) +{ + struct ice_buf_hdr *hdr; + uint16_t section_count; + uint16_t data_end; + + hdr = (struct ice_buf_hdr *)buf->buf; + /* verify data */ + section_count = le16toh(hdr->section_count); + if (section_count < ICE_MIN_S_COUNT || section_count > ICE_MAX_S_COUNT) + return NULL; + + data_end = le16toh(hdr->data_end); + if (data_end < ICE_MIN_S_DATA_END || data_end > ICE_MAX_S_DATA_END) + return NULL; + + return hdr; +} + +/** * ice_cfg_tx_topo - Initialize new tx topology if available * @hw: pointer to the HW struct * @buf: pointer to Tx topology buffer @@ -14257,29 +14462,30 @@ ice_reinit_hw(struct ice_softc *sc) * if available. */ enum ice_status -ice_cfg_tx_topo(struct ice_hw *hw, uint8_t *buf, uint32_t len) +ice_cfg_tx_topo(struct ice_softc *sc, uint8_t *buf, uint32_t len) { -#if 0 - u8 *current_topo, *new_topo = NULL; + struct ice_hw *hw = &sc->hw; + uint8_t *current_topo, *new_topo = NULL; struct ice_run_time_cfg_seg *seg; struct ice_buf_hdr *section; struct ice_pkg_hdr *pkg_hdr; enum ice_ddp_state state; - u16 i, size = 0, offset; + uint16_t i, size = 0, offset; enum ice_status status; - u32 reg = 0; - u8 flags; + uint32_t reg = 0; + uint8_t flags; if (!buf || !len) return ICE_ERR_PARAM; /* Does FW support new Tx topology mode ? */ if (!hw->func_caps.common_cap.tx_sched_topo_comp_mode_en) { - ice_debug(hw, ICE_DBG_INIT, "FW doesn't support compatibility mode\n"); + DNPRINTF(ICE_DBG_INIT, + "FW doesn't support compatibility mode\n"); return ICE_ERR_NOT_SUPPORTED; } - current_topo = (u8 *)ice_malloc(hw, ICE_AQ_MAX_BUF_LEN); + current_topo = (uint8_t *)ice_malloc(hw, ICE_AQ_MAX_BUF_LEN); if (!current_topo) return ICE_ERR_NO_MEMORY; @@ -14289,14 +14495,14 @@ ice_cfg_tx_topo(struct ice_hw *hw, uint8_t *buf, uint3 ice_free(hw, current_topo); if (status) { - ice_debug(hw, ICE_DBG_INIT, "Get current topology is failed\n"); + DNPRINTF(ICE_DBG_INIT, "Get current topology is failed\n"); return status; } /* Is default topology already applied ? */ if (!(flags & ICE_AQC_TX_TOPO_FLAGS_LOAD_NEW) && hw->num_tx_sched_layers == 9) { - ice_debug(hw, ICE_DBG_INIT, "Loaded default topology\n"); + DNPRINTF(ICE_DBG_INIT, "Loaded default topology\n"); /* Already default topology is loaded */ return ICE_ERR_ALREADY_EXISTS; } @@ -14304,14 +14510,15 @@ ice_cfg_tx_topo(struct ice_hw *hw, uint8_t *buf, uint3 /* Is new topology already applied ? */ if ((flags & ICE_AQC_TX_TOPO_FLAGS_LOAD_NEW) && hw->num_tx_sched_layers == 5) { - ice_debug(hw, ICE_DBG_INIT, "Loaded new topology\n"); + DNPRINTF(ICE_DBG_INIT, "Loaded new topology\n"); /* Already new topology is loaded */ return ICE_ERR_ALREADY_EXISTS; } /* Is set topology issued already ? */ if (flags & ICE_AQC_TX_TOPO_FLAGS_ISSUED) { - ice_debug(hw, ICE_DBG_INIT, "Update tx topology was done by another PF\n"); + DNPRINTF(ICE_DBG_INIT, + "Update tx topology was done by another PF\n"); /* add a small delay before exiting */ for (i = 0; i < 20; i++) ice_msec_delay(100, true); @@ -14321,15 +14528,15 @@ ice_cfg_tx_topo(struct ice_hw *hw, uint8_t *buf, uint3 /* Change the topology from new to default (5 to 9) */ if (!(flags & ICE_AQC_TX_TOPO_FLAGS_LOAD_NEW) && hw->num_tx_sched_layers == 5) { - ice_debug(hw, ICE_DBG_INIT, "Change topology from 5 to 9 layers\n"); + DNPRINTF(ICE_DBG_INIT, "Change topology from 5 to 9 layers\n"); goto update_topo; } pkg_hdr = (struct ice_pkg_hdr *)buf; state = ice_verify_pkg(pkg_hdr, len); if (state) { - ice_debug(hw, ICE_DBG_INIT, "failed to verify pkg (err: %d)\n", - state); + printf("%s: failed to verify firmware pkg (err: %d)\n", + sc->sc_dev.dv_xname, state); return ICE_ERR_CFG; } @@ -14337,39 +14544,42 @@ ice_cfg_tx_topo(struct ice_hw *hw, uint8_t *buf, uint3 seg = (struct ice_run_time_cfg_seg *) ice_find_seg_in_pkg(hw, SEGMENT_TYPE_ICE_RUN_TIME_CFG, pkg_hdr); if (!seg) { - ice_debug(hw, ICE_DBG_INIT, "5 layer topology segment is missing\n"); + DNPRINTF(ICE_DBG_INIT, "5 layer topology segment is missing\n"); return ICE_ERR_CFG; } - if (LE32_TO_CPU(seg->buf_table.buf_count) < ICE_MIN_S_COUNT) { - ice_debug(hw, ICE_DBG_INIT, "5 layer topology segment count(%d) is wrong\n", - seg->buf_table.buf_count); + if (le32toh(seg->buf_table.buf_count) < ICE_MIN_S_COUNT) { + DNPRINTF(ICE_DBG_INIT, + "5 layer topology segment count(%d) is wrong\n", + seg->buf_table.buf_count); return ICE_ERR_CFG; } section = ice_pkg_val_buf(seg->buf_table.buf_array); - if (!section || LE32_TO_CPU(section->section_entry[0].type) != + if (!section || le32toh(section->section_entry[0].type) != ICE_SID_TX_5_LAYER_TOPO) { - ice_debug(hw, ICE_DBG_INIT, "5 layer topology section type is wrong\n"); + DNPRINTF(ICE_DBG_INIT, + "5 layer topology section type is wrong\n"); return ICE_ERR_CFG; } - size = LE16_TO_CPU(section->section_entry[0].size); - offset = LE16_TO_CPU(section->section_entry[0].offset); + size = le16toh(section->section_entry[0].size); + offset = le16toh(section->section_entry[0].offset); if (size < ICE_MIN_S_SZ || size > ICE_MAX_S_SZ) { - ice_debug(hw, ICE_DBG_INIT, "5 layer topology section size is wrong\n"); + DNPRINTF(ICE_DBG_INIT, + "5 layer topology section size is wrong\n"); return ICE_ERR_CFG; } /* make sure the section fits in the buffer */ if (offset + size > ICE_PKG_BUF_SIZE) { - ice_debug(hw, ICE_DBG_INIT, "5 layer topology buffer > 4K\n"); + DNPRINTF(ICE_DBG_INIT, "5 layer topology buffer > 4K\n"); return ICE_ERR_CFG; } /* Get the new topology buffer */ - new_topo = ((u8 *)section) + offset; + new_topo = ((uint8_t *)section) + offset; update_topo: /* acquire global lock to make sure that set topology issued @@ -14378,15 +14588,16 @@ update_topo: status = ice_acquire_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID, ICE_RES_WRITE, ICE_GLOBAL_CFG_LOCK_TIMEOUT); if (status) { - ice_debug(hw, ICE_DBG_INIT, "Failed to acquire global lock\n"); + DNPRINTF(ICE_DBG_INIT, "Failed to acquire global lock\n"); return status; } /* check reset was triggered already or not */ - reg = rd32(hw, GLGEN_RSTAT); + reg = ICE_READ(hw, GLGEN_RSTAT); if (reg & GLGEN_RSTAT_DEVSTATE_M) { /* Reset is in progress, re-init the hw again */ - ice_debug(hw, ICE_DBG_INIT, "Reset is in progress. layer topology might be applied already\n"); + DNPRINTF(ICE_DBG_INIT, "Reset is in progress. layer " + "topology might be applied already\n"); ice_check_reset(hw); return ICE_SUCCESS; } @@ -14394,7 +14605,7 @@ update_topo: /* set new topology */ status = ice_get_set_tx_topo(hw, new_topo, size, NULL, NULL, true); if (status) { - ice_debug(hw, ICE_DBG_INIT, "Set tx topology is failed\n"); + DNPRINTF(ICE_DBG_INIT, "Set tx topology is failed\n"); return status; } @@ -14406,9 +14617,6 @@ update_topo: * required for release */ return ICE_SUCCESS; -#else - return ICE_ERR_NOT_IMPL; -#endif } /** @@ -14486,11 +14694,9 @@ ice_active_pkg_version_str(struct ice_hw *hw, char *bu void ice_os_pkg_version_str(struct ice_hw *hw, char *buf, size_t bufsize) { - char name_buf[ICE_PKG_NAME_SIZE]; - - /* If the OS DDP package info is empty, use "None" */ + /* If the OS DDP package info is empty, use "" */ if (pkg_ver_empty(&hw->pkg_ver, hw->pkg_name)) { - snprintf(buf, bufsize, "None"); + buf[0] = '\0'; return; } @@ -14499,10 +14705,7 @@ ice_os_pkg_version_str(struct ice_hw *hw, char *buf, s * value from an external source, strlcpy() into a new buffer to * make sure. */ - strlcpy(name_buf, (char *)hw->pkg_name, bufsize); - - snprintf(buf, bufsize, "%s version %u.%u.%u.%u", - name_buf, + snprintf(buf, bufsize, "%u.%u.%u.%u", hw->pkg_ver.major, hw->pkg_ver.minor, hw->pkg_ver.update, @@ -14705,6 +14908,2344 @@ ice_log_pkg_init(struct ice_softc *sc, enum ice_ddp_st } /** + * ice_has_signing_seg - determine if package has a signing segment + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to the driver's package hdr + */ +bool +ice_has_signing_seg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) +{ + struct ice_generic_seg_hdr *seg_hdr; + + seg_hdr = (struct ice_generic_seg_hdr *) + ice_find_seg_in_pkg(hw, SEGMENT_TYPE_SIGNING, pkg_hdr); + + return seg_hdr ? true : false; +} + +/** + * ice_get_pkg_segment_id - get correct package segment id, based on device + * @mac_type: MAC type of the device + */ +uint32_t +ice_get_pkg_segment_id(enum ice_mac_type mac_type) +{ + uint32_t seg_id; + + switch (mac_type) { + case ICE_MAC_GENERIC: + case ICE_MAC_GENERIC_3K: + case ICE_MAC_GENERIC_3K_E825: + default: + seg_id = SEGMENT_TYPE_ICE_E810; + break; + } + + return seg_id; +} + +/** + * ice_get_pkg_sign_type - get package segment sign type, based on device + * @mac_type: MAC type of the device + */ +uint32_t +ice_get_pkg_sign_type(enum ice_mac_type mac_type) +{ + uint32_t sign_type; + + switch (mac_type) { + case ICE_MAC_GENERIC_3K: + sign_type = SEGMENT_SIGN_TYPE_RSA3K; + break; + case ICE_MAC_GENERIC_3K_E825: + sign_type = SEGMENT_SIGN_TYPE_RSA3K_E825; + break; + case ICE_MAC_GENERIC: + default: + sign_type = SEGMENT_SIGN_TYPE_RSA2K; + break; + } + + return sign_type; +} + +/** + * ice_get_signing_req - get correct package requirements, based on device + * @hw: pointer to the hardware structure + */ +void +ice_get_signing_req(struct ice_hw *hw) +{ + hw->pkg_seg_id = ice_get_pkg_segment_id(hw->mac_type); + hw->pkg_sign_type = ice_get_pkg_sign_type(hw->mac_type); +} + +/** + * ice_get_pkg_seg_by_idx + * @pkg_hdr: pointer to the package header to be searched + * @idx: index of segment + */ +struct ice_generic_seg_hdr * +ice_get_pkg_seg_by_idx(struct ice_pkg_hdr *pkg_hdr, uint32_t idx) +{ + struct ice_generic_seg_hdr *seg = NULL; + + if (idx < le32toh(pkg_hdr->seg_count)) + seg = (struct ice_generic_seg_hdr *) + ((uint8_t *)pkg_hdr + + le32toh(pkg_hdr->seg_offset[idx])); + + return seg; +} +/** + * ice_is_signing_seg_at_idx - determine if segment is a signing segment + * @pkg_hdr: pointer to package header + * @idx: segment index + */ +bool +ice_is_signing_seg_at_idx(struct ice_pkg_hdr *pkg_hdr, uint32_t idx) +{ + struct ice_generic_seg_hdr *seg; + bool retval = false; + + seg = ice_get_pkg_seg_by_idx(pkg_hdr, idx); + if (seg) + retval = le32toh(seg->seg_type) == SEGMENT_TYPE_SIGNING; + + return retval; +} + +/** + * ice_is_signing_seg_type_at_idx + * @pkg_hdr: pointer to package header + * @idx: segment index + * @seg_id: segment id that is expected + * @sign_type: signing type + * + * Determine if a segment is a signing segment of the correct type + */ +bool +ice_is_signing_seg_type_at_idx(struct ice_pkg_hdr *pkg_hdr, uint32_t idx, + uint32_t seg_id, uint32_t sign_type) +{ + bool result = false; + + if (ice_is_signing_seg_at_idx(pkg_hdr, idx)) { + struct ice_sign_seg *seg; + + seg = (struct ice_sign_seg *)ice_get_pkg_seg_by_idx(pkg_hdr, + idx); + if (seg && le32toh(seg->seg_id) == seg_id && + le32toh(seg->sign_type) == sign_type) + result = true; + } + + return result; +} + +/** + * ice_match_signing_seg - determine if a matching signing segment exists + * @pkg_hdr: pointer to package header + * @seg_id: segment id that is expected + * @sign_type: signing type + */ +bool +ice_match_signing_seg(struct ice_pkg_hdr *pkg_hdr, uint32_t seg_id, + uint32_t sign_type) +{ + bool match = false; + uint32_t i; + + for (i = 0; i < le32toh(pkg_hdr->seg_count); i++) { + if (ice_is_signing_seg_type_at_idx(pkg_hdr, i, seg_id, + sign_type)) { + match = true; + break; + } + } + + return match; +} + +/** + * ice_find_buf_table + * @ice_seg: pointer to the ice segment + * + * Returns the address of the buffer table within the ice segment. + */ +struct ice_buf_table * +ice_find_buf_table(struct ice_seg *ice_seg) +{ + struct ice_nvm_table *nvms; + + nvms = (struct ice_nvm_table *) + (ice_seg->device_table + + le32toh(ice_seg->device_table_count)); + + return (struct ice_buf_table *) + (nvms->vers + le32toh(nvms->table_count)); +} + +/** + * ice_pkg_enum_buf + * @ice_seg: pointer to the ice segment (or NULL on subsequent calls) + * @state: pointer to the enum state + * + * This function will enumerate all the buffers in the ice segment. The first + * call is made with the ice_seg parameter non-NULL; on subsequent calls, + * ice_seg is set to NULL which continues the enumeration. When the function + * returns a NULL pointer, then the end of the buffers has been reached, or an + * unexpected value has been detected (for example an invalid section count or + * an invalid buffer end value). + */ +struct ice_buf_hdr * +ice_pkg_enum_buf(struct ice_seg *ice_seg, struct ice_pkg_enum *state) +{ + if (ice_seg) { + state->buf_table = ice_find_buf_table(ice_seg); + if (!state->buf_table) + return NULL; + + state->buf_idx = 0; + return ice_pkg_val_buf(state->buf_table->buf_array); + } + + if (++state->buf_idx < le32toh(state->buf_table->buf_count)) + return ice_pkg_val_buf(state->buf_table->buf_array + + state->buf_idx); + else + return NULL; +} + +/** + * ice_pkg_advance_sect + * @ice_seg: pointer to the ice segment (or NULL on subsequent calls) + * @state: pointer to the enum state + * + * This helper function will advance the section within the ice segment, + * also advancing the buffer if needed. + */ +bool +ice_pkg_advance_sect(struct ice_seg *ice_seg, struct ice_pkg_enum *state) +{ + if (!ice_seg && !state->buf) + return false; + + if (!ice_seg && state->buf) + if (++state->sect_idx < le16toh(state->buf->section_count)) + return true; + + state->buf = ice_pkg_enum_buf(ice_seg, state); + if (!state->buf) + return false; + + /* start of new buffer, reset section index */ + state->sect_idx = 0; + return true; +} + +/** + * ice_pkg_enum_section + * @ice_seg: pointer to the ice segment (or NULL on subsequent calls) + * @state: pointer to the enum state + * @sect_type: section type to enumerate + * + * This function will enumerate all the sections of a particular type in the + * ice segment. The first call is made with the ice_seg parameter non-NULL; + * on subsequent calls, ice_seg is set to NULL which continues the enumeration. + * When the function returns a NULL pointer, then the end of the matching + * sections has been reached. + */ +void * +ice_pkg_enum_section(struct ice_seg *ice_seg, struct ice_pkg_enum *state, + uint32_t sect_type) +{ + uint16_t offset, size; + + if (ice_seg) + state->type = sect_type; + + if (!ice_pkg_advance_sect(ice_seg, state)) + return NULL; + + /* scan for next matching section */ + while (state->buf->section_entry[state->sect_idx].type != + htole32(state->type)) + if (!ice_pkg_advance_sect(NULL, state)) + return NULL; + + /* validate section */ + offset = le16toh(state->buf->section_entry[state->sect_idx].offset); + if (offset < ICE_MIN_S_OFF || offset > ICE_MAX_S_OFF) + return NULL; + + size = le16toh(state->buf->section_entry[state->sect_idx].size); + if (size < ICE_MIN_S_SZ || size > ICE_MAX_S_SZ) + return NULL; + + /* make sure the section fits in the buffer */ + if (offset + size > ICE_PKG_BUF_SIZE) + return NULL; + + state->sect_type = + le32toh(state->buf->section_entry[state->sect_idx].type); + + /* calc pointer to this section */ + state->sect = ((uint8_t *)state->buf) + + le16toh(state->buf->section_entry[state->sect_idx].offset); + + return state->sect; +} + +/** + * ice_init_pkg_info + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to the driver's package hdr + * + * Saves off the package details into the HW structure. + */ +enum ice_ddp_state +ice_init_pkg_info(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) +{ + struct ice_generic_seg_hdr *seg_hdr; + + if (!pkg_hdr) + return ICE_DDP_PKG_ERR; + + hw->pkg_has_signing_seg = ice_has_signing_seg(hw, pkg_hdr); + ice_get_signing_req(hw); + + DNPRINTF(ICE_DBG_INIT, "Pkg using segment id: 0x%08X\n", + hw->pkg_seg_id); + + seg_hdr = (struct ice_generic_seg_hdr *) + ice_find_seg_in_pkg(hw, hw->pkg_seg_id, pkg_hdr); + if (seg_hdr) { + struct ice_meta_sect *meta; + struct ice_pkg_enum state; + + memset(&state, 0, sizeof(state)); + + /* Get package information from the Metadata Section */ + meta = (struct ice_meta_sect *) + ice_pkg_enum_section((struct ice_seg *)seg_hdr, &state, + ICE_SID_METADATA); + if (!meta) { + DNPRINTF(ICE_DBG_INIT, + "Did not find ice metadata section in package\n"); + return ICE_DDP_PKG_INVALID_FILE; + } + + hw->pkg_ver = meta->ver; + memcpy(hw->pkg_name, meta->name, sizeof(meta->name)); + + DNPRINTF(ICE_DBG_PKG, "Pkg: %d.%d.%d.%d, %s\n", + meta->ver.major, meta->ver.minor, meta->ver.update, + meta->ver.draft, meta->name); + + hw->ice_seg_fmt_ver = seg_hdr->seg_format_ver; + memcpy(hw->ice_seg_id, seg_hdr->seg_id, sizeof(hw->ice_seg_id)); + + DNPRINTF(ICE_DBG_PKG, "Ice Seg: %d.%d.%d.%d, %s\n", + seg_hdr->seg_format_ver.major, + seg_hdr->seg_format_ver.minor, + seg_hdr->seg_format_ver.update, + seg_hdr->seg_format_ver.draft, + seg_hdr->seg_id); + } else { + DNPRINTF(ICE_DBG_INIT, + "Did not find ice segment in driver package\n"); + return ICE_DDP_PKG_INVALID_FILE; + } + + return ICE_DDP_PKG_SUCCESS; +} + +/** + * ice_chk_pkg_version - check package version for compatibility with driver + * @pkg_ver: pointer to a version structure to check + * + * Check to make sure that the package about to be downloaded is compatible with + * the driver. To be compatible, the major and minor components of the package + * version must match our ICE_PKG_SUPP_VER_MAJ and ICE_PKG_SUPP_VER_MNR + * definitions. + */ +enum ice_ddp_state +ice_chk_pkg_version(struct ice_pkg_ver *pkg_ver) +{ + if (pkg_ver->major > ICE_PKG_SUPP_VER_MAJ || + (pkg_ver->major == ICE_PKG_SUPP_VER_MAJ && + pkg_ver->minor > ICE_PKG_SUPP_VER_MNR)) + return ICE_DDP_PKG_FILE_VERSION_TOO_HIGH; + else if (pkg_ver->major < ICE_PKG_SUPP_VER_MAJ || + (pkg_ver->major == ICE_PKG_SUPP_VER_MAJ && + pkg_ver->minor < ICE_PKG_SUPP_VER_MNR)) + return ICE_DDP_PKG_FILE_VERSION_TOO_LOW; + + return ICE_DDP_PKG_SUCCESS; +} + +/** + * ice_aq_get_pkg_info_list + * @hw: pointer to the hardware structure + * @pkg_info: the buffer which will receive the information list + * @buf_size: the size of the pkg_info information buffer + * @cd: pointer to command details structure or NULL + * + * Get Package Info List (0x0C43) + */ +enum ice_status +ice_aq_get_pkg_info_list(struct ice_hw *hw, + struct ice_aqc_get_pkg_info_resp *pkg_info, + uint16_t buf_size, struct ice_sq_cd *cd) +{ + struct ice_aq_desc desc; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_pkg_info_list); + + return ice_aq_send_cmd(hw, &desc, pkg_info, buf_size, cd); +} + +/** + * ice_chk_pkg_compat + * @hw: pointer to the hardware structure + * @ospkg: pointer to the package hdr + * @seg: pointer to the package segment hdr + * + * This function checks the package version compatibility with driver and NVM + */ +enum ice_ddp_state +ice_chk_pkg_compat(struct ice_hw *hw, struct ice_pkg_hdr *ospkg, + struct ice_seg **seg) +{ + struct ice_aqc_get_pkg_info_resp *pkg; + enum ice_ddp_state state; + uint16_t size; + uint32_t i; + + /* Check package version compatibility */ + state = ice_chk_pkg_version(&hw->pkg_ver); + if (state) { + DNPRINTF(ICE_DBG_INIT, "Package version check failed.\n"); + return state; + } + + /* find ICE segment in given package */ + *seg = (struct ice_seg *)ice_find_seg_in_pkg(hw, hw->pkg_seg_id, + ospkg); + if (!*seg) { + DNPRINTF(ICE_DBG_INIT, "no ice segment in package.\n"); + return ICE_DDP_PKG_INVALID_FILE; + } + + /* Check if FW is compatible with the OS package */ + size = ice_struct_size(pkg, pkg_info, ICE_PKG_CNT); + pkg = (struct ice_aqc_get_pkg_info_resp *)ice_malloc(hw, size); + if (!pkg) + return ICE_DDP_PKG_ERR; + + if (ice_aq_get_pkg_info_list(hw, pkg, size, NULL)) { + state = ICE_DDP_PKG_ERR; + goto fw_ddp_compat_free_alloc; + } + + for (i = 0; i < le32toh(pkg->count); i++) { + /* loop till we find the NVM package */ + if (!pkg->pkg_info[i].is_in_nvm) + continue; + if ((*seg)->hdr.seg_format_ver.major != + pkg->pkg_info[i].ver.major || + (*seg)->hdr.seg_format_ver.minor > + pkg->pkg_info[i].ver.minor) { + state = ICE_DDP_PKG_FW_MISMATCH; + DNPRINTF(ICE_DBG_INIT, + "OS package is not compatible with NVM.\n"); + } + /* done processing NVM package so break */ + break; + } +fw_ddp_compat_free_alloc: + ice_free(hw, pkg); + return state; +} + +/** + * ice_pkg_enum_entry + * @ice_seg: pointer to the ice segment (or NULL on subsequent calls) + * @state: pointer to the enum state + * @sect_type: section type to enumerate + * @offset: pointer to variable that receives the offset in the table (optional) + * @handler: function that handles access to the entries into the section type + * + * This function will enumerate all the entries in particular section type in + * the ice segment. The first call is made with the ice_seg parameter non-NULL; + * on subsequent calls, ice_seg is set to NULL which continues the enumeration. + * When the function returns a NULL pointer, then the end of the entries has + * been reached. + * + * Since each section may have a different header and entry size, the handler + * function is needed to determine the number and location entries in each + * section. + * + * The offset parameter is optional, but should be used for sections that + * contain an offset for each section table. For such cases, the section handler + * function must return the appropriate offset + index to give the absolution + * offset for each entry. For example, if the base for a section's header + * indicates a base offset of 10, and the index for the entry is 2, then + * section handler function should set the offset to 10 + 2 = 12. + */ +void * +ice_pkg_enum_entry(struct ice_seg *ice_seg, struct ice_pkg_enum *state, + uint32_t sect_type, uint32_t *offset, void *(*handler)(uint32_t sect_type, + void *section, uint32_t index, uint32_t *offset)) +{ + void *entry; + + if (ice_seg) { + if (!handler) + return NULL; + + if (!ice_pkg_enum_section(ice_seg, state, sect_type)) + return NULL; + + state->entry_idx = 0; + state->handler = handler; + } else { + state->entry_idx++; + } + + if (!state->handler) + return NULL; + + /* get entry */ + entry = state->handler(state->sect_type, state->sect, state->entry_idx, + offset); + if (!entry) { + /* end of a section, look for another section of this type */ + if (!ice_pkg_enum_section(NULL, state, 0)) + return NULL; + + state->entry_idx = 0; + entry = state->handler(state->sect_type, state->sect, + state->entry_idx, offset); + } + + return entry; +} + +/** + * ice_label_enum_handler + * @sect_type: section type + * @section: pointer to section + * @index: index of the label entry to be returned + * @offset: pointer to receive absolute offset, always zero for label sections + * + * This is a callback function that can be passed to ice_pkg_enum_entry. + * Handles enumeration of individual label entries. + */ +void * +ice_label_enum_handler(uint32_t sect_type, void *section, uint32_t index, + uint32_t *offset) +{ + struct ice_label_section *labels; + + if (!section) + return NULL; + + if (index > ICE_MAX_LABELS_IN_BUF) + return NULL; + + if (offset) + *offset = 0; + + labels = (struct ice_label_section *)section; + if (index >= le16toh(labels->count)) + return NULL; + + return labels->label + index; +} + +/** + * ice_enum_labels + * @ice_seg: pointer to the ice segment (NULL on subsequent calls) + * @type: the section type that will contain the label (0 on subsequent calls) + * @state: ice_pkg_enum structure that will hold the state of the enumeration + * @value: pointer to a value that will return the label's value if found + * + * Enumerates a list of labels in the package. The caller will call + * ice_enum_labels(ice_seg, type, ...) to start the enumeration, then call + * ice_enum_labels(NULL, 0, ...) to continue. When the function returns a NULL + * the end of the list has been reached. + */ +char * +ice_enum_labels(struct ice_seg *ice_seg, uint32_t type, + struct ice_pkg_enum *state, uint16_t *value) +{ + struct ice_label *label; + + /* Check for valid label section on first call */ + if (type && !(type >= ICE_SID_LBL_FIRST && type <= ICE_SID_LBL_LAST)) + return NULL; + + label = (struct ice_label *)ice_pkg_enum_entry(ice_seg, state, type, + NULL, ice_label_enum_handler); + if (!label) + return NULL; + + *value = le16toh(label->value); + return label->name; +} + +/** + * ice_boost_tcam_handler + * @sect_type: section type + * @section: pointer to section + * @index: index of the boost TCAM entry to be returned + * @offset: pointer to receive absolute offset, always 0 for boost TCAM sections + * + * This is a callback function that can be passed to ice_pkg_enum_entry. + * Handles enumeration of individual boost TCAM entries. + */ +void * +ice_boost_tcam_handler(uint32_t sect_type, void *section, uint32_t index, + uint32_t *offset) +{ + struct ice_boost_tcam_section *boost; + + if (!section) + return NULL; + + if (sect_type != ICE_SID_RXPARSER_BOOST_TCAM) + return NULL; + + if (index > ICE_MAX_BST_TCAMS_IN_BUF) + return NULL; + + if (offset) + *offset = 0; + + boost = (struct ice_boost_tcam_section *)section; + if (index >= le16toh(boost->count)) + return NULL; + + return boost->tcam + index; +} + +/** + * ice_find_boost_entry + * @ice_seg: pointer to the ice segment (non-NULL) + * @addr: Boost TCAM address of entry to search for + * @entry: returns pointer to the entry + * + * Finds a particular Boost TCAM entry and returns a pointer to that entry + * if it is found. The ice_seg parameter must not be NULL since the first call + * to ice_pkg_enum_entry requires a pointer to an actual ice_segment structure. + */ +enum ice_status +ice_find_boost_entry(struct ice_seg *ice_seg, uint16_t addr, + struct ice_boost_tcam_entry **entry) +{ + struct ice_boost_tcam_entry *tcam; + struct ice_pkg_enum state; + + memset(&state, 0, sizeof(state)); + + if (!ice_seg) + return ICE_ERR_PARAM; + + do { + tcam = (struct ice_boost_tcam_entry *) + ice_pkg_enum_entry(ice_seg, &state, + ICE_SID_RXPARSER_BOOST_TCAM, NULL, + ice_boost_tcam_handler); + if (tcam && le16toh(tcam->addr) == addr) { + *entry = tcam; + return ICE_SUCCESS; + } + + ice_seg = NULL; + } while (tcam); + + *entry = NULL; + return ICE_ERR_CFG; +} + +static const struct ice_tunnel_type_scan tnls[] = { + { TNL_VXLAN, "TNL_VXLAN_PF" }, + { TNL_GENEVE, "TNL_GENEVE_PF" }, + { TNL_LAST, "" } +}; + +/** + * ice_add_tunnel_hint + * @hw: pointer to the HW structure + * @label_name: label text + * @val: value of the tunnel port boost entry + */ +void +ice_add_tunnel_hint(struct ice_hw *hw, char *label_name, uint16_t val) +{ + if (hw->tnl.count < ICE_TUNNEL_MAX_ENTRIES) { + uint16_t i; + + for (i = 0; tnls[i].type != TNL_LAST; i++) { + size_t len = strlen(tnls[i].label_prefix); + + /* Look for matching label start, before continuing */ + if (strncmp(label_name, tnls[i].label_prefix, len)) + continue; + + /* Make sure this label matches our PF. Note that the + * PF character ('0' - '7') will be located where our + * prefix string's null terminator is located. + */ + if ((label_name[len] - '0') == hw->pf_id) { + hw->tnl.tbl[hw->tnl.count].type = tnls[i].type; + hw->tnl.tbl[hw->tnl.count].valid = false; + hw->tnl.tbl[hw->tnl.count].in_use = false; + hw->tnl.tbl[hw->tnl.count].marked = false; + hw->tnl.tbl[hw->tnl.count].boost_addr = val; + hw->tnl.tbl[hw->tnl.count].port = 0; + hw->tnl.count++; + break; + } + } + } +} + +/** + * ice_init_pkg_hints + * @hw: pointer to the HW structure + * @ice_seg: pointer to the segment of the package scan (non-NULL) + * + * This function will scan the package and save off relevant information + * (hints or metadata) for driver use. The ice_seg parameter must not be NULL + * since the first call to ice_enum_labels requires a pointer to an actual + * ice_seg structure. + */ +void +ice_init_pkg_hints(struct ice_hw *hw, struct ice_seg *ice_seg) +{ + struct ice_pkg_enum state; + char *label_name; + uint16_t val; + int i; + + memset(&hw->tnl, 0, sizeof(hw->tnl)); + memset(&state, 0, sizeof(state)); + + if (!ice_seg) + return; + + label_name = ice_enum_labels(ice_seg, ICE_SID_LBL_RXPARSER_TMEM, &state, + &val); + + while (label_name) { + if (!strncmp(label_name, ICE_TNL_PRE, strlen(ICE_TNL_PRE))) + /* check for a tunnel entry */ + ice_add_tunnel_hint(hw, label_name, val); + + label_name = ice_enum_labels(NULL, 0, &state, &val); + } + + /* Cache the appropriate boost TCAM entry pointers for tunnels */ + for (i = 0; i < hw->tnl.count; i++) { + ice_find_boost_entry(ice_seg, hw->tnl.tbl[i].boost_addr, + &hw->tnl.tbl[i].boost_entry); + if (hw->tnl.tbl[i].boost_entry) + hw->tnl.tbl[i].valid = true; + } +} + +/** + * ice_acquire_global_cfg_lock + * @hw: pointer to the HW structure + * @access: access type (read or write) + * + * This function will request ownership of the global config lock for reading + * or writing of the package. When attempting to obtain write access, the + * caller must check for the following two return values: + * + * ICE_SUCCESS - Means the caller has acquired the global config lock + * and can perform writing of the package. + * ICE_ERR_AQ_NO_WORK - Indicates another driver has already written the + * package or has found that no update was necessary; in + * this case, the caller can just skip performing any + * update of the package. + */ +enum ice_status +ice_acquire_global_cfg_lock(struct ice_hw *hw, + enum ice_aq_res_access_type access) +{ + enum ice_status status; + + status = ice_acquire_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID, access, + ICE_GLOBAL_CFG_LOCK_TIMEOUT); + + if (status == ICE_ERR_AQ_NO_WORK) + DNPRINTF(ICE_DBG_PKG, "Global config lock: No work to do\n"); + + return status; +} + +/** + * ice_release_global_cfg_lock + * @hw: pointer to the HW structure + * + * This function will release the global config lock. + */ +void +ice_release_global_cfg_lock(struct ice_hw *hw) +{ + ice_release_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID); +} + +enum ice_ddp_state +ice_map_aq_err_to_ddp_state(enum ice_aq_err aq_err) +{ + switch (aq_err) { + case ICE_AQ_RC_ENOSEC: + return ICE_DDP_PKG_NO_SEC_MANIFEST; + case ICE_AQ_RC_EBADSIG: + return ICE_DDP_PKG_FILE_SIGNATURE_INVALID; + case ICE_AQ_RC_ESVN: + return ICE_DDP_PKG_SECURE_VERSION_NBR_TOO_LOW; + case ICE_AQ_RC_EBADMAN: + return ICE_DDP_PKG_MANIFEST_INVALID; + case ICE_AQ_RC_EBADBUF: + return ICE_DDP_PKG_BUFFER_INVALID; + default: + return ICE_DDP_PKG_ERR; + } +} + +/** + * ice_is_buffer_metadata - determine if package buffer is a metadata buffer + * @buf: pointer to buffer header + */ +bool +ice_is_buffer_metadata(struct ice_buf_hdr *buf) +{ + return !!(le32toh(buf->section_entry[0].type) & ICE_METADATA_BUF); +} + +/** + * ice_is_last_download_buffer + * @buf: pointer to current buffer header + * @idx: index of the buffer in the current sequence + * @count: the buffer count in the current sequence + * + * Note: this routine should only be called if the buffer is not the last buffer + */ +bool +ice_is_last_download_buffer(struct ice_buf_hdr *buf, uint32_t idx, + uint32_t count) +{ + bool last = ((idx + 1) == count); + + /* A set metadata flag in the next buffer will signal that the current + * buffer will be the last buffer downloaded + */ + if (!last) { + struct ice_buf *next_buf = ((struct ice_buf *)buf) + 1; + + last = ice_is_buffer_metadata((struct ice_buf_hdr *)next_buf); + } + + return last; +} + +/** + * ice_aq_download_pkg + * @hw: pointer to the hardware structure + * @pkg_buf: the package buffer to transfer + * @buf_size: the size of the package buffer + * @last_buf: last buffer indicator + * @error_offset: returns error offset + * @error_info: returns error information + * @cd: pointer to command details structure or NULL + * + * Download Package (0x0C40) + */ +enum ice_status +ice_aq_download_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf, + uint16_t buf_size, bool last_buf, uint32_t *error_offset, + uint32_t *error_info, struct ice_sq_cd *cd) +{ + struct ice_aqc_download_pkg *cmd; + struct ice_aq_desc desc; + enum ice_status status; + + if (error_offset) + *error_offset = 0; + if (error_info) + *error_info = 0; + + cmd = &desc.params.download_pkg; + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_download_pkg); + desc.flags |= htole16(ICE_AQ_FLAG_RD); + + if (last_buf) + cmd->flags |= ICE_AQC_DOWNLOAD_PKG_LAST_BUF; + + status = ice_aq_send_cmd(hw, &desc, pkg_buf, buf_size, cd); + if (status == ICE_ERR_AQ_ERROR) { + /* Read error from buffer only when the FW returned an error */ + struct ice_aqc_download_pkg_resp *resp; + + resp = (struct ice_aqc_download_pkg_resp *)pkg_buf; + if (error_offset) + *error_offset = le32toh(resp->error_offset); + if (error_info) + *error_info = le32toh(resp->error_info); + } + + return status; +} + +/** + * ice_dwnld_cfg_bufs_no_lock + * @hw: pointer to the hardware structure + * @bufs: pointer to an array of buffers + * @start: buffer index of first buffer to download + * @count: the number of buffers to download + * @indicate_last: if true, then set last buffer flag on last buffer download + * + * Downloads package configuration buffers to the firmware. Metadata buffers + * are skipped, and the first metadata buffer found indicates that the rest + * of the buffers are all metadata buffers. + */ +enum ice_ddp_state +ice_dwnld_cfg_bufs_no_lock(struct ice_hw *hw, struct ice_buf *bufs, + uint32_t start, uint32_t count, bool indicate_last) +{ + enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS; + struct ice_buf_hdr *bh; + enum ice_aq_err err; + uint32_t offset, info, i; + + if (!bufs || !count) + return ICE_DDP_PKG_ERR; + + /* If the first buffer's first section has its metadata bit set + * then there are no buffers to be downloaded, and the operation is + * considered a success. + */ + bh = (struct ice_buf_hdr *)(bufs + start); + if (le32toh(bh->section_entry[0].type) & ICE_METADATA_BUF) + return ICE_DDP_PKG_SUCCESS; + + for (i = 0; i < count; i++) { + enum ice_status status; + bool last = false; + + bh = (struct ice_buf_hdr *)(bufs + start + i); + + if (indicate_last) + last = ice_is_last_download_buffer(bh, i, count); + + status = ice_aq_download_pkg(hw, bh, ICE_PKG_BUF_SIZE, last, + &offset, &info, NULL); + + /* Save AQ status from download package */ + if (status) { + DNPRINTF(ICE_DBG_PKG, + "Pkg download failed: err %d off %d inf %d\n", + status, offset, info); + err = hw->adminq.sq_last_status; + state = ice_map_aq_err_to_ddp_state(err); + break; + } + + if (last) + break; + } + + return state; +} + +/** + * ice_download_pkg_sig_seg - download a signature segment + * @hw: pointer to the hardware structure + * @seg: pointer to signature segment + */ +enum ice_ddp_state +ice_download_pkg_sig_seg(struct ice_hw *hw, struct ice_sign_seg *seg) +{ + enum ice_ddp_state state; + + state = ice_dwnld_cfg_bufs_no_lock(hw, seg->buf_tbl.buf_array, 0, + le32toh(seg->buf_tbl.buf_count), false); + + return state; +} + +/** + * ice_download_pkg_config_seg - download a config segment + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to package header + * @idx: segment index + * @start: starting buffer + * @count: buffer count + * + * Note: idx must reference a ICE segment + */ +enum ice_ddp_state +ice_download_pkg_config_seg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr, + uint32_t idx, uint32_t start, uint32_t count) +{ + struct ice_buf_table *bufs; + struct ice_seg *seg; + uint32_t buf_count; + + seg = (struct ice_seg *)ice_get_pkg_seg_by_idx(pkg_hdr, idx); + if (!seg) + return ICE_DDP_PKG_ERR; + + bufs = ice_find_buf_table(seg); + buf_count = le32toh(bufs->buf_count); + + if (start >= buf_count || start + count > buf_count) + return ICE_DDP_PKG_ERR; + + return ice_dwnld_cfg_bufs_no_lock(hw, bufs->buf_array, start, count, + true); +} + +/** + * ice_dwnld_sign_and_cfg_segs - download a signing segment and config segment + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to package header + * @idx: segment index (must be a signature segment) + * + * Note: idx must reference a signature segment + */ +enum ice_ddp_state +ice_dwnld_sign_and_cfg_segs(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr, + uint32_t idx) +{ + enum ice_ddp_state state; + struct ice_sign_seg *seg; + uint32_t conf_idx; + uint32_t start; + uint32_t count; + + seg = (struct ice_sign_seg *)ice_get_pkg_seg_by_idx(pkg_hdr, idx); + if (!seg) + return ICE_DDP_PKG_ERR; + + conf_idx = le32toh(seg->signed_seg_idx); + start = le32toh(seg->signed_buf_start); + count = le32toh(seg->signed_buf_count); + + state = ice_download_pkg_sig_seg(hw, seg); + if (state) + return state; + + return ice_download_pkg_config_seg(hw, pkg_hdr, conf_idx, start, count); +} + +/** + * ice_aq_set_port_params - set physical port parameters + * @pi: pointer to the port info struct + * @bad_frame_vsi: defines the VSI to which bad frames are forwarded + * @save_bad_pac: if set packets with errors are forwarded to the bad frames VSI + * @pad_short_pac: if set transmit packets smaller than 60 bytes are padded + * @double_vlan: if set double VLAN is enabled + * @cd: pointer to command details structure or NULL + * + * Set Physical port parameters (0x0203) + */ +enum ice_status +ice_aq_set_port_params(struct ice_port_info *pi, uint16_t bad_frame_vsi, + bool save_bad_pac, bool pad_short_pac, bool double_vlan, + struct ice_sq_cd *cd) +{ + struct ice_aqc_set_port_params *cmd; + struct ice_hw *hw = pi->hw; + struct ice_aq_desc desc; + uint16_t cmd_flags = 0; + + cmd = &desc.params.set_port_params; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_port_params); + cmd->bad_frame_vsi = htole16(bad_frame_vsi); + if (save_bad_pac) + cmd_flags |= ICE_AQC_SET_P_PARAMS_SAVE_BAD_PACKETS; + if (pad_short_pac) + cmd_flags |= ICE_AQC_SET_P_PARAMS_PAD_SHORT_PACKETS; + if (double_vlan) + cmd_flags |= ICE_AQC_SET_P_PARAMS_DOUBLE_VLAN_ENA; + cmd->cmd_flags = htole16(cmd_flags); + + return ice_aq_send_cmd(hw, &desc, NULL, 0, cd); +} + +/** + * ice_aq_set_vlan_mode - set the VLAN mode of the device + * @hw: pointer to the HW structure + * @set_params: requested VLAN mode configuration + * + * Set VLAN Mode Parameters (0x020C) + */ +enum ice_status +ice_aq_set_vlan_mode(struct ice_hw *hw, + struct ice_aqc_set_vlan_mode *set_params) +{ + uint8_t rdma_packet, mng_vlan_prot_id; + struct ice_aq_desc desc; + + if (!set_params) + return ICE_ERR_PARAM; + + if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX) + return ICE_ERR_PARAM; + + rdma_packet = set_params->rdma_packet; + if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING && + rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING) + return ICE_ERR_PARAM; + + mng_vlan_prot_id = set_params->mng_vlan_prot_id; + if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER && + mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER) + return ICE_ERR_PARAM; + + ice_fill_dflt_direct_cmd_desc(&desc, + ice_aqc_opc_set_vlan_mode_parameters); + desc.flags |= htole16(ICE_AQ_FLAG_RD); + + return ice_aq_send_cmd(hw, &desc, set_params, sizeof(*set_params), + NULL); +} + +/** + * ice_set_svm - set single VLAN mode + * @hw: pointer to the HW structure + */ +enum ice_status +ice_set_svm(struct ice_hw *hw) +{ + struct ice_aqc_set_vlan_mode *set_params; + enum ice_status status; + + status = ice_aq_set_port_params(hw->port_info, 0, + false, false, false, NULL); + if (status) { + DNPRINTF(ICE_DBG_INIT, + "Failed to set port parameters for single VLAN mode\n"); + return status; + } + + set_params = (struct ice_aqc_set_vlan_mode *) + ice_malloc(hw, sizeof(*set_params)); + if (!set_params) + return ICE_ERR_NO_MEMORY; + + /* default configuration for SVM configurations */ + set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG; + set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING; + set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER; + + status = ice_aq_set_vlan_mode(hw, set_params); + if (status) + DNPRINTF(ICE_DBG_INIT, + "Failed to configure port in single VLAN mode\n"); + + ice_free(hw, set_params); + return status; +} + +/** + * ice_pkg_get_supported_vlan_mode - chk if DDP supports Double VLAN mode (DVM) + * @hw: pointer to the HW struct + * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false) + */ +enum ice_status +ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm) +{ +#if 0 + u16 meta_init_size = sizeof(struct ice_meta_init_section); + struct ice_meta_init_section *sect; + struct ice_buf_build *bld; + enum ice_status status; + + /* if anything fails, we assume there is no DVM support */ + *dvm = false; + + bld = ice_pkg_buf_alloc_single_section(hw, + ICE_SID_RXPARSER_METADATA_INIT, + meta_init_size, (void **)§); + if (!bld) + return ICE_ERR_NO_MEMORY; + + /* only need to read a single section */ + sect->count = CPU_TO_LE16(1); + sect->offset = CPU_TO_LE16(ICE_META_VLAN_MODE_ENTRY); + + status = ice_aq_upload_section(hw, + (struct ice_buf_hdr *)ice_pkg_buf(bld), + ICE_PKG_BUF_SIZE, NULL); + if (!status) { + ice_declare_bitmap(entry, ICE_META_INIT_BITS); + u32 arr[ICE_META_INIT_DW_CNT]; + u16 i; + + /* convert to host bitmap format */ + for (i = 0; i < ICE_META_INIT_DW_CNT; i++) + arr[i] = LE32_TO_CPU(sect->entry[0].bm[i]); + + ice_bitmap_from_array32(entry, arr, (u16)ICE_META_INIT_BITS); + + /* check if DVM is supported */ + *dvm = ice_is_bit_set(entry, ICE_META_VLAN_MODE_BIT); + } + + ice_pkg_buf_free(hw, bld); + + return status; +#else + return ICE_ERR_NOT_IMPL; +#endif +} + +/** + * ice_pkg_supports_dvm - find out if DDP supports DVM + * @hw: pointer to the HW structure + */ +bool +ice_pkg_supports_dvm(struct ice_hw *hw) +{ + enum ice_status status; + bool pkg_supports_dvm; + + status = ice_pkg_get_supported_vlan_mode(hw, &pkg_supports_dvm); + if (status) { + DNPRINTF(ICE_DBG_PKG, + "Failed to get supported VLAN mode, status %d\n", status); + return false; + } + + return pkg_supports_dvm; +} + +/** + * ice_aq_get_vlan_mode - get the VLAN mode of the device + * @hw: pointer to the HW structure + * @get_params: structure FW fills in based on the current VLAN mode config + * + * Get VLAN Mode Parameters (0x020D) + */ +enum ice_status +ice_aq_get_vlan_mode(struct ice_hw *hw, + struct ice_aqc_get_vlan_mode *get_params) +{ + struct ice_aq_desc desc; + + if (!get_params) + return ICE_ERR_PARAM; + + ice_fill_dflt_direct_cmd_desc(&desc, + ice_aqc_opc_get_vlan_mode_parameters); + + return ice_aq_send_cmd(hw, &desc, get_params, sizeof(*get_params), + NULL); +} + +/** + * ice_fw_supports_dvm - find out if FW supports DVM + * @hw: pointer to the HW structure + */ +bool +ice_fw_supports_dvm(struct ice_hw *hw) +{ + struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; + enum ice_status status; + + /* If firmware returns success, then it supports DVM, else it only + * supports SVM + */ + status = ice_aq_get_vlan_mode(hw, &get_vlan_mode); + if (status) { + DNPRINTF(ICE_DBG_NVM, + "%s: Failed to get VLAN mode, status %d\n", + __func__, status); + return false; + } + + return true; +} + +/** + * ice_is_dvm_supported - check if Double VLAN Mode is supported + * @hw: pointer to the hardware structure + * + * Returns true if Double VLAN Mode (DVM) is supported and false if only Single + * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and + * firmware must support it, otherwise only SVM is supported. This function + * should only be called while the global config lock is held and after the + * package has been successfully downloaded. + */ +bool +ice_is_dvm_supported(struct ice_hw *hw) +{ + if (!ice_pkg_supports_dvm(hw)) { + DNPRINTF(ICE_DBG_PKG, "DDP doesn't support DVM\n"); + return false; + } + + if (!ice_fw_supports_dvm(hw)) { + DNPRINTF(ICE_DBG_PKG, "FW doesn't support DVM\n"); + return false; + } + + return true; +} + + +/** + * ice_set_vlan_mode + * @hw: pointer to the HW structure + */ +enum ice_status +ice_set_vlan_mode(struct ice_hw *hw) +{ + if (!ice_is_dvm_supported(hw)) + return ICE_SUCCESS; + + return ice_set_svm(hw); +} + +/** + * ice_post_dwnld_pkg_actions - perform post download package actions + * @hw: pointer to the hardware structure + */ +enum ice_ddp_state +ice_post_dwnld_pkg_actions(struct ice_hw *hw) +{ + enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS; + enum ice_status status; + + status = ice_set_vlan_mode(hw); + if (status) { + DNPRINTF(ICE_DBG_PKG, "Failed to set VLAN mode: err %d\n", + status); + state = ICE_DDP_PKG_ERR; + } + + return state; +} + +/** + * ice_download_pkg_with_sig_seg - download package using signature segments + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to package header + */ +enum ice_ddp_state +ice_download_pkg_with_sig_seg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) +{ + enum ice_aq_err aq_err = hw->adminq.sq_last_status; + enum ice_ddp_state state = ICE_DDP_PKG_ERR; + enum ice_status status; + uint32_t i; + + DNPRINTF(ICE_DBG_INIT, "Segment ID %d\n", hw->pkg_seg_id); + DNPRINTF(ICE_DBG_INIT, "Signature type %d\n", hw->pkg_sign_type); + + status = ice_acquire_global_cfg_lock(hw, ICE_RES_WRITE); + if (status) { + if (status == ICE_ERR_AQ_NO_WORK) + state = ICE_DDP_PKG_ALREADY_LOADED; + else + state = ice_map_aq_err_to_ddp_state(aq_err); + return state; + } + + for (i = 0; i < le32toh(pkg_hdr->seg_count); i++) { + if (!ice_is_signing_seg_type_at_idx(pkg_hdr, i, hw->pkg_seg_id, + hw->pkg_sign_type)) + continue; + + state = ice_dwnld_sign_and_cfg_segs(hw, pkg_hdr, i); + if (state) + break; + } + + if (!state) + state = ice_post_dwnld_pkg_actions(hw); + + ice_release_global_cfg_lock(hw); + + return state; +} + +/** + * ice_dwnld_cfg_bufs + * @hw: pointer to the hardware structure + * @bufs: pointer to an array of buffers + * @count: the number of buffers in the array + * + * Obtains global config lock and downloads the package configuration buffers + * to the firmware. + */ +enum ice_ddp_state +ice_dwnld_cfg_bufs(struct ice_hw *hw, struct ice_buf *bufs, uint32_t count) +{ + enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS; + enum ice_status status; + struct ice_buf_hdr *bh; + + if (!bufs || !count) + return ICE_DDP_PKG_ERR; + + /* If the first buffer's first section has its metadata bit set + * then there are no buffers to be downloaded, and the operation is + * considered a success. + */ + bh = (struct ice_buf_hdr *)bufs; + if (le32toh(bh->section_entry[0].type) & ICE_METADATA_BUF) + return ICE_DDP_PKG_SUCCESS; + + status = ice_acquire_global_cfg_lock(hw, ICE_RES_WRITE); + if (status) { + if (status == ICE_ERR_AQ_NO_WORK) + return ICE_DDP_PKG_ALREADY_LOADED; + return ice_map_aq_err_to_ddp_state(hw->adminq.sq_last_status); + } + + state = ice_dwnld_cfg_bufs_no_lock(hw, bufs, 0, count, true); + if (!state) + state = ice_post_dwnld_pkg_actions(hw); + + ice_release_global_cfg_lock(hw); + + return state; +} +/** + * ice_download_pkg_without_sig_seg + * @hw: pointer to the hardware structure + * @ice_seg: pointer to the segment of the package to be downloaded + * + * Handles the download of a complete package without signature segment. + */ +enum ice_ddp_state +ice_download_pkg_without_sig_seg(struct ice_hw *hw, struct ice_seg *ice_seg) +{ + struct ice_buf_table *ice_buf_tbl; + + DNPRINTF(ICE_DBG_PKG, "Segment format version: %d.%d.%d.%d\n", + ice_seg->hdr.seg_format_ver.major, + ice_seg->hdr.seg_format_ver.minor, + ice_seg->hdr.seg_format_ver.update, + ice_seg->hdr.seg_format_ver.draft); + + DNPRINTF(ICE_DBG_PKG, "Seg: type 0x%X, size %d, name %s\n", + le32toh(ice_seg->hdr.seg_type), + le32toh(ice_seg->hdr.seg_size), ice_seg->hdr.seg_id); + + ice_buf_tbl = ice_find_buf_table(ice_seg); + + DNPRINTF(ICE_DBG_PKG, "Seg buf count: %d\n", + le32toh(ice_buf_tbl->buf_count)); + + return ice_dwnld_cfg_bufs(hw, ice_buf_tbl->buf_array, + le32toh(ice_buf_tbl->buf_count)); +} + +/** + * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled + * @hw: pointer to the HW structure + * + * Returns true if the hardware/firmware is configured in double VLAN mode, + * else return false signaling that the hardware/firmware is configured in + * single VLAN mode. + * + * Also, return false if this call fails for any reason (i.e. firmware doesn't + * support this AQ call). + */ +bool +ice_aq_is_dvm_ena(struct ice_hw *hw) +{ + struct ice_aqc_get_vlan_mode get_params = { 0 }; + enum ice_status status; + + status = ice_aq_get_vlan_mode(hw, &get_params); + if (status) { + DNPRINTF(ICE_DBG_AQ, "Failed to get VLAN mode, status %d\n", + status); + return false; + } + + return (get_params.vlan_mode & ICE_AQ_VLAN_MODE_DVM_ENA); +} + +/** + * ice_cache_vlan_mode - cache VLAN mode after DDP is downloaded + * @hw: pointer to the HW structure + * + * This is only called after downloading the DDP and after the global + * configuration lock has been released because all ports on a device need to + * cache the VLAN mode. + */ +void +ice_cache_vlan_mode(struct ice_hw *hw) +{ + hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false; +} + +/** + * ice_post_pkg_dwnld_vlan_mode_cfg - configure VLAN mode after DDP download + * @hw: pointer to the HW structure + * + * This function is meant to configure any VLAN mode specific functionality + * after the global configuration lock has been released and the DDP has been + * downloaded. + * + * Since only one PF downloads the DDP and configures the VLAN mode there needs + * to be a way to configure the other PFs after the DDP has been downloaded and + * the global configuration lock has been released. All such code should go in + * this function. + */ +void +ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw) +{ + ice_cache_vlan_mode(hw); +} + +/** + * ice_download_pkg + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to package header + * @ice_seg: pointer to the segment of the package to be downloaded + * + * Handles the download of a complete package. + */ +enum ice_ddp_state +ice_download_pkg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr, + struct ice_seg *ice_seg) +{ + enum ice_ddp_state state; + + if (hw->pkg_has_signing_seg) + state = ice_download_pkg_with_sig_seg(hw, pkg_hdr); + else + state = ice_download_pkg_without_sig_seg(hw, ice_seg); + + ice_post_pkg_dwnld_vlan_mode_cfg(hw); + + return state; +} + +/** + * ice_get_pkg_info + * @hw: pointer to the hardware structure + * + * Store details of the package currently loaded in HW into the HW structure. + */ +enum ice_ddp_state +ice_get_pkg_info(struct ice_hw *hw) +{ + enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS; + struct ice_aqc_get_pkg_info_resp *pkg_info; + uint16_t size; + uint32_t i; + + size = ice_struct_size(pkg_info, pkg_info, ICE_PKG_CNT); + pkg_info = (struct ice_aqc_get_pkg_info_resp *)ice_malloc(hw, size); + if (!pkg_info) + return ICE_DDP_PKG_ERR; + + if (ice_aq_get_pkg_info_list(hw, pkg_info, size, NULL)) { + state = ICE_DDP_PKG_ERR; + goto init_pkg_free_alloc; + } + + for (i = 0; i < le32toh(pkg_info->count); i++) { +#define ICE_PKG_FLAG_COUNT 4 + char flags[ICE_PKG_FLAG_COUNT + 1] = { 0 }; + uint8_t place = 0; + + if (pkg_info->pkg_info[i].is_active) { + flags[place++] = 'A'; + hw->active_pkg_ver = pkg_info->pkg_info[i].ver; + hw->active_track_id = + le32toh(pkg_info->pkg_info[i].track_id); + memcpy(hw->active_pkg_name, pkg_info->pkg_info[i].name, + sizeof(pkg_info->pkg_info[i].name)); + hw->active_pkg_in_nvm = pkg_info->pkg_info[i].is_in_nvm; + } + if (pkg_info->pkg_info[i].is_active_at_boot) + flags[place++] = 'B'; + if (pkg_info->pkg_info[i].is_modified) + flags[place++] = 'M'; + if (pkg_info->pkg_info[i].is_in_nvm) + flags[place++] = 'N'; + + DNPRINTF(ICE_DBG_PKG, "Pkg[%d]: %d.%d.%d.%d,%s,%s\n", + i, pkg_info->pkg_info[i].ver.major, + pkg_info->pkg_info[i].ver.minor, + pkg_info->pkg_info[i].ver.update, + pkg_info->pkg_info[i].ver.draft, + pkg_info->pkg_info[i].name, flags); + } + +init_pkg_free_alloc: + ice_free(hw, pkg_info); + + return state; +} + +/** + * ice_get_ddp_pkg_state - get DDP pkg state after download + * @hw: pointer to the HW struct + * @already_loaded: indicates if pkg was already loaded onto the device + * + */ +enum ice_ddp_state +ice_get_ddp_pkg_state(struct ice_hw *hw, bool already_loaded) +{ + if (hw->pkg_ver.major == hw->active_pkg_ver.major && + hw->pkg_ver.minor == hw->active_pkg_ver.minor && + hw->pkg_ver.update == hw->active_pkg_ver.update && + hw->pkg_ver.draft == hw->active_pkg_ver.draft && + !memcmp(hw->pkg_name, hw->active_pkg_name, sizeof(hw->pkg_name))) { + if (already_loaded) + return ICE_DDP_PKG_SAME_VERSION_ALREADY_LOADED; + else + return ICE_DDP_PKG_SUCCESS; + } else if (hw->active_pkg_ver.major != ICE_PKG_SUPP_VER_MAJ || + hw->active_pkg_ver.minor != ICE_PKG_SUPP_VER_MNR) { + return ICE_DDP_PKG_ALREADY_LOADED_NOT_SUPPORTED; + } else if (hw->active_pkg_ver.major == ICE_PKG_SUPP_VER_MAJ && + hw->active_pkg_ver.minor == ICE_PKG_SUPP_VER_MNR) { + return ICE_DDP_PKG_COMPATIBLE_ALREADY_LOADED; + } else { + return ICE_DDP_PKG_ERR; + } +} + +/** + * ice_init_pkg_regs - initialize additional package registers + * @hw: pointer to the hardware structure + */ +void +ice_init_pkg_regs(struct ice_hw *hw) +{ +#define ICE_SW_BLK_INP_MASK_L 0xFFFFFFFF +#define ICE_SW_BLK_INP_MASK_H 0x0000FFFF +#define ICE_SW_BLK_IDX 0 + + /* setup Switch block input mask, which is 48-bits in two parts */ + ICE_WRITE(hw, GL_PREEXT_L2_PMASK0(ICE_SW_BLK_IDX), + ICE_SW_BLK_INP_MASK_L); + ICE_WRITE(hw, GL_PREEXT_L2_PMASK1(ICE_SW_BLK_IDX), + ICE_SW_BLK_INP_MASK_H); +} + +/** + * ice_ptg_alloc_val - Allocates a new packet type group ID by value + * @hw: pointer to the hardware structure + * @blk: HW block + * @ptg: the PTG to allocate + * + * This function allocates a given packet type group ID specified by the PTG + * parameter. + */ +void +ice_ptg_alloc_val(struct ice_hw *hw, enum ice_block blk, uint8_t ptg) +{ + hw->blk[blk].xlt1.ptg_tbl[ptg].in_use = true; +} + +/** + * ice_ptg_find_ptype - Search for packet type group using packet type (ptype) + * @hw: pointer to the hardware structure + * @blk: HW block + * @ptype: the ptype to search for + * @ptg: pointer to variable that receives the PTG + * + * This function will search the PTGs for a particular ptype, returning the + * PTG ID that contains it through the PTG parameter, with the value of + * ICE_DEFAULT_PTG (0) meaning it is part the default PTG. + */ +enum ice_status +ice_ptg_find_ptype(struct ice_hw *hw, enum ice_block blk, uint16_t ptype, + uint8_t *ptg) +{ + if (ptype >= ICE_XLT1_CNT || !ptg) + return ICE_ERR_PARAM; + + *ptg = hw->blk[blk].xlt1.ptypes[ptype].ptg; + return ICE_SUCCESS; +} + +/** + * ice_ptg_remove_ptype - Removes ptype from a particular packet type group + * @hw: pointer to the hardware structure + * @blk: HW block + * @ptype: the ptype to remove + * @ptg: the PTG to remove the ptype from + * + * This function will remove the ptype from the specific PTG, and move it to + * the default PTG (ICE_DEFAULT_PTG). + */ +enum ice_status +ice_ptg_remove_ptype(struct ice_hw *hw, enum ice_block blk, uint16_t ptype, + uint8_t ptg) +{ + struct ice_ptg_ptype **ch; + struct ice_ptg_ptype *p; + + if (ptype > ICE_XLT1_CNT - 1) + return ICE_ERR_PARAM; + + if (!hw->blk[blk].xlt1.ptg_tbl[ptg].in_use) + return ICE_ERR_DOES_NOT_EXIST; + + /* Should not happen if .in_use is set, bad config */ + if (!hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype) + return ICE_ERR_CFG; + + /* find the ptype within this PTG, and bypass the link over it */ + p = hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; + ch = &hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; + while (p) { + if (ptype == (p - hw->blk[blk].xlt1.ptypes)) { + *ch = p->next_ptype; + break; + } + + ch = &p->next_ptype; + p = p->next_ptype; + } + + hw->blk[blk].xlt1.ptypes[ptype].ptg = ICE_DEFAULT_PTG; + hw->blk[blk].xlt1.ptypes[ptype].next_ptype = NULL; + + return ICE_SUCCESS; +} + +/** + * ice_ptg_add_mv_ptype - Adds/moves ptype to a particular packet type group + * @hw: pointer to the hardware structure + * @blk: HW block + * @ptype: the ptype to add or move + * @ptg: the PTG to add or move the ptype to + * + * This function will either add or move a ptype to a particular PTG depending + * on if the ptype is already part of another group. Note that using a + * a destination PTG ID of ICE_DEFAULT_PTG (0) will move the ptype to the + * default PTG. + */ +enum ice_status +ice_ptg_add_mv_ptype(struct ice_hw *hw, enum ice_block blk, uint16_t ptype, + uint8_t ptg) +{ + enum ice_status status; + uint8_t original_ptg; + + if (ptype > ICE_XLT1_CNT - 1) + return ICE_ERR_PARAM; + + if (!hw->blk[blk].xlt1.ptg_tbl[ptg].in_use && ptg != ICE_DEFAULT_PTG) + return ICE_ERR_DOES_NOT_EXIST; + + status = ice_ptg_find_ptype(hw, blk, ptype, &original_ptg); + if (status) + return status; + + /* Is ptype already in the correct PTG? */ + if (original_ptg == ptg) + return ICE_SUCCESS; + + /* Remove from original PTG and move back to the default PTG */ + if (original_ptg != ICE_DEFAULT_PTG) + ice_ptg_remove_ptype(hw, blk, ptype, original_ptg); + + /* Moving to default PTG? Then we're done with this request */ + if (ptg == ICE_DEFAULT_PTG) + return ICE_SUCCESS; + + /* Add ptype to PTG at beginning of list */ + hw->blk[blk].xlt1.ptypes[ptype].next_ptype = + hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; + hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype = + &hw->blk[blk].xlt1.ptypes[ptype]; + + hw->blk[blk].xlt1.ptypes[ptype].ptg = ptg; + hw->blk[blk].xlt1.t[ptype] = ptg; + + return ICE_SUCCESS; +} + +/** + * ice_init_sw_xlt1_db - init software XLT1 database from HW tables + * @hw: pointer to the hardware structure + * @blk: the HW block to initialize + */ +void +ice_init_sw_xlt1_db(struct ice_hw *hw, enum ice_block blk) +{ + uint16_t pt; + + for (pt = 0; pt < hw->blk[blk].xlt1.count; pt++) { + uint8_t ptg; + + ptg = hw->blk[blk].xlt1.t[pt]; + if (ptg != ICE_DEFAULT_PTG) { + ice_ptg_alloc_val(hw, blk, ptg); + ice_ptg_add_mv_ptype(hw, blk, pt, ptg); + } + } +} + +/** + * ice_vsig_alloc_val - allocate a new VSIG by value + * @hw: pointer to the hardware structure + * @blk: HW block + * @vsig: the VSIG to allocate + * + * This function will allocate a given VSIG specified by the VSIG parameter. + */ +uint16_t +ice_vsig_alloc_val(struct ice_hw *hw, enum ice_block blk, uint16_t vsig) +{ + uint16_t idx = vsig & ICE_VSIG_IDX_M; + + if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) { + TAILQ_INIT(&hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst); + hw->blk[blk].xlt2.vsig_tbl[idx].in_use = true; + } + + return ICE_VSIG_VALUE(idx, hw->pf_id); +} + +/** + * ice_vsig_find_vsi - find a VSIG that contains a specified VSI + * @hw: pointer to the hardware structure + * @blk: HW block + * @vsi: VSI of interest + * @vsig: pointer to receive the VSI group + * + * This function will lookup the VSI entry in the XLT2 list and return + * the VSI group its associated with. + */ +enum ice_status +ice_vsig_find_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, + uint16_t *vsig) +{ + if (!vsig || vsi >= ICE_MAX_VSI) + return ICE_ERR_PARAM; + + /* As long as there's a default or valid VSIG associated with the input + * VSI, the functions returns a success. Any handling of VSIG will be + * done by the following add, update or remove functions. + */ + *vsig = hw->blk[blk].xlt2.vsis[vsi].vsig; + + return ICE_SUCCESS; +} + +/** + * ice_vsig_remove_vsi - remove VSI from VSIG + * @hw: pointer to the hardware structure + * @blk: HW block + * @vsi: VSI to remove + * @vsig: VSI group to remove from + * + * The function will remove the input VSI from its VSI group and move it + * to the DEFAULT_VSIG. + */ +enum ice_status +ice_vsig_remove_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, + uint16_t vsig) +{ + struct ice_vsig_vsi **vsi_head, *vsi_cur, *vsi_tgt; + uint16_t idx; + + idx = vsig & ICE_VSIG_IDX_M; + + if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) + return ICE_ERR_PARAM; + + if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) + return ICE_ERR_DOES_NOT_EXIST; + + /* entry already in default VSIG, don't have to remove */ + if (idx == ICE_DEFAULT_VSIG) + return ICE_SUCCESS; + + vsi_head = &hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; + if (!(*vsi_head)) + return ICE_ERR_CFG; + + vsi_tgt = &hw->blk[blk].xlt2.vsis[vsi]; + vsi_cur = (*vsi_head); + + /* iterate the VSI list, skip over the entry to be removed */ + while (vsi_cur) { + if (vsi_tgt == vsi_cur) { + (*vsi_head) = vsi_cur->next_vsi; + break; + } + vsi_head = &vsi_cur->next_vsi; + vsi_cur = vsi_cur->next_vsi; + } + + /* verify if VSI was removed from group list */ + if (!vsi_cur) + return ICE_ERR_DOES_NOT_EXIST; + + vsi_cur->vsig = ICE_DEFAULT_VSIG; + vsi_cur->changed = 1; + vsi_cur->next_vsi = NULL; + + return ICE_SUCCESS; +} + +/** + * ice_vsig_add_mv_vsi - add or move a VSI to a VSI group + * @hw: pointer to the hardware structure + * @blk: HW block + * @vsi: VSI to move + * @vsig: destination VSI group + * + * This function will move or add the input VSI to the target VSIG. + * The function will find the original VSIG the VSI belongs to and + * move the entry to the DEFAULT_VSIG, update the original VSIG and + * then move entry to the new VSIG. + */ +enum ice_status +ice_vsig_add_mv_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, + uint16_t vsig) +{ + struct ice_vsig_vsi *tmp; + enum ice_status status; + uint16_t orig_vsig, idx; + + idx = vsig & ICE_VSIG_IDX_M; + + if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) + return ICE_ERR_PARAM; + + /* if VSIG not in use and VSIG is not default type this VSIG + * doesn't exist. + */ + if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use && + vsig != ICE_DEFAULT_VSIG) + return ICE_ERR_DOES_NOT_EXIST; + + status = ice_vsig_find_vsi(hw, blk, vsi, &orig_vsig); + if (status) + return status; + + /* no update required if vsigs match */ + if (orig_vsig == vsig) + return ICE_SUCCESS; + + if (orig_vsig != ICE_DEFAULT_VSIG) { + /* remove entry from orig_vsig and add to default VSIG */ + status = ice_vsig_remove_vsi(hw, blk, vsi, orig_vsig); + if (status) + return status; + } + + if (idx == ICE_DEFAULT_VSIG) + return ICE_SUCCESS; + + /* Create VSI entry and add VSIG and prop_mask values */ + hw->blk[blk].xlt2.vsis[vsi].vsig = vsig; + hw->blk[blk].xlt2.vsis[vsi].changed = 1; + + /* Add new entry to the head of the VSIG list */ + tmp = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; + hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi = + &hw->blk[blk].xlt2.vsis[vsi]; + hw->blk[blk].xlt2.vsis[vsi].next_vsi = tmp; + hw->blk[blk].xlt2.t[vsi] = vsig; + + return ICE_SUCCESS; +} + +/** + * ice_init_sw_xlt2_db - init software XLT2 database from HW tables + * @hw: pointer to the hardware structure + * @blk: the HW block to initialize + */ +void +ice_init_sw_xlt2_db(struct ice_hw *hw, enum ice_block blk) +{ + uint16_t vsi; + + for (vsi = 0; vsi < hw->blk[blk].xlt2.count; vsi++) { + uint16_t vsig; + + vsig = hw->blk[blk].xlt2.t[vsi]; + if (vsig) { + ice_vsig_alloc_val(hw, blk, vsig); + ice_vsig_add_mv_vsi(hw, blk, vsi, vsig); + /* no changes at this time, since this has been + * initialized from the original package + */ + hw->blk[blk].xlt2.vsis[vsi].changed = 0; + } + } +} + +/** + * ice_init_sw_db - init software database from HW tables + * @hw: pointer to the hardware structure + */ +void +ice_init_sw_db(struct ice_hw *hw) +{ + uint16_t i; + + for (i = 0; i < ICE_BLK_COUNT; i++) { + ice_init_sw_xlt1_db(hw, (enum ice_block)i); + ice_init_sw_xlt2_db(hw, (enum ice_block)i); + } +} + +/** + * ice_fill_tbl - Reads content of a single table type into database + * @hw: pointer to the hardware structure + * @block_id: Block ID of the table to copy + * @sid: Section ID of the table to copy + * + * Will attempt to read the entire content of a given table of a single block + * into the driver database. We assume that the buffer will always + * be as large or larger than the data contained in the package. If + * this condition is not met, there is most likely an error in the package + * contents. + */ +void +ice_fill_tbl(struct ice_hw *hw, enum ice_block block_id, uint32_t sid) +{ + uint32_t dst_len, sect_len, offset = 0; + struct ice_prof_redir_section *pr; + struct ice_prof_id_section *pid; + struct ice_xlt1_section *xlt1; + struct ice_xlt2_section *xlt2; + struct ice_sw_fv_section *es; + struct ice_pkg_enum state; + uint8_t *src, *dst; + void *sect; + + /* if the HW segment pointer is null then the first iteration of + * ice_pkg_enum_section() will fail. In this case the HW tables will + * not be filled and return success. + */ + if (!hw->seg) { + DNPRINTF(ICE_DBG_PKG, + "hw->seg is NULL, tables are not filled\n"); + return; + } + + memset(&state, 0, sizeof(state)); + + sect = ice_pkg_enum_section(hw->seg, &state, sid); + while (sect) { + switch (sid) { + case ICE_SID_XLT1_SW: + case ICE_SID_XLT1_FD: + case ICE_SID_XLT1_RSS: + case ICE_SID_XLT1_ACL: + case ICE_SID_XLT1_PE: + xlt1 = (struct ice_xlt1_section *)sect; + src = xlt1->value; + sect_len = le16toh(xlt1->count) * + sizeof(*hw->blk[block_id].xlt1.t); + dst = hw->blk[block_id].xlt1.t; + dst_len = hw->blk[block_id].xlt1.count * + sizeof(*hw->blk[block_id].xlt1.t); + break; + case ICE_SID_XLT2_SW: + case ICE_SID_XLT2_FD: + case ICE_SID_XLT2_RSS: + case ICE_SID_XLT2_ACL: + case ICE_SID_XLT2_PE: + xlt2 = (struct ice_xlt2_section *)sect; + src = (uint8_t *)xlt2->value; + sect_len = le16toh(xlt2->count) * + sizeof(*hw->blk[block_id].xlt2.t); + dst = (uint8_t *)hw->blk[block_id].xlt2.t; + dst_len = hw->blk[block_id].xlt2.count * + sizeof(*hw->blk[block_id].xlt2.t); + break; + case ICE_SID_PROFID_TCAM_SW: + case ICE_SID_PROFID_TCAM_FD: + case ICE_SID_PROFID_TCAM_RSS: + case ICE_SID_PROFID_TCAM_ACL: + case ICE_SID_PROFID_TCAM_PE: + pid = (struct ice_prof_id_section *)sect; + src = (uint8_t *)pid->entry; + sect_len = le16toh(pid->count) * + sizeof(*hw->blk[block_id].prof.t); + dst = (uint8_t *)hw->blk[block_id].prof.t; + dst_len = hw->blk[block_id].prof.count * + sizeof(*hw->blk[block_id].prof.t); + break; + case ICE_SID_PROFID_REDIR_SW: + case ICE_SID_PROFID_REDIR_FD: + case ICE_SID_PROFID_REDIR_RSS: + case ICE_SID_PROFID_REDIR_ACL: + case ICE_SID_PROFID_REDIR_PE: + pr = (struct ice_prof_redir_section *)sect; + src = pr->redir_value; + sect_len = le16toh(pr->count) * + sizeof(*hw->blk[block_id].prof_redir.t); + dst = hw->blk[block_id].prof_redir.t; + dst_len = hw->blk[block_id].prof_redir.count * + sizeof(*hw->blk[block_id].prof_redir.t); + break; + case ICE_SID_FLD_VEC_SW: + case ICE_SID_FLD_VEC_FD: + case ICE_SID_FLD_VEC_RSS: + case ICE_SID_FLD_VEC_ACL: + case ICE_SID_FLD_VEC_PE: + es = (struct ice_sw_fv_section *)sect; + src = (uint8_t *)es->fv; + sect_len = (uint32_t)(le16toh(es->count) * + hw->blk[block_id].es.fvw) * + sizeof(*hw->blk[block_id].es.t); + dst = (uint8_t *)hw->blk[block_id].es.t; + dst_len = (uint32_t)(hw->blk[block_id].es.count * + hw->blk[block_id].es.fvw) * + sizeof(*hw->blk[block_id].es.t); + break; + default: + return; + } + + /* if the section offset exceeds destination length, terminate + * table fill. + */ + if (offset > dst_len) + return; + + /* if the sum of section size and offset exceed destination size + * then we are out of bounds of the HW table size for that PF. + * Changing section length to fill the remaining table space + * of that PF. + */ + if ((offset + sect_len) > dst_len) + sect_len = dst_len - offset; + + memcpy(dst + offset, src, sect_len); + offset += sect_len; + sect = ice_pkg_enum_section(NULL, &state, sid); + } +} + +/** + * ice_fill_blk_tbls - Read package context for tables + * @hw: pointer to the hardware structure + * + * Reads the current package contents and populates the driver + * database with the data iteratively for all advanced feature + * blocks. Assume that the HW tables have been allocated. + */ +void +ice_fill_blk_tbls(struct ice_hw *hw) +{ + uint8_t i; + + for (i = 0; i < ICE_BLK_COUNT; i++) { + enum ice_block blk_id = (enum ice_block)i; + + ice_fill_tbl(hw, blk_id, hw->blk[blk_id].xlt1.sid); + ice_fill_tbl(hw, blk_id, hw->blk[blk_id].xlt2.sid); + ice_fill_tbl(hw, blk_id, hw->blk[blk_id].prof.sid); + ice_fill_tbl(hw, blk_id, hw->blk[blk_id].prof_redir.sid); + ice_fill_tbl(hw, blk_id, hw->blk[blk_id].es.sid); + } + + ice_init_sw_db(hw); +} + +/** + * ice_sw_fv_handler + * @sect_type: section type + * @section: pointer to section + * @index: index of the field vector entry to be returned + * @offset: ptr to variable that receives the offset in the field vector table + * + * This is a callback function that can be passed to ice_pkg_enum_entry. + * This function treats the given section as of type ice_sw_fv_section and + * enumerates offset field. "offset" is an index into the field vector table. + */ +void * +ice_sw_fv_handler(uint32_t sect_type, void *section, uint32_t index, + uint32_t *offset) +{ + struct ice_sw_fv_section *fv_section = + (struct ice_sw_fv_section *)section; + + if (!section || sect_type != ICE_SID_FLD_VEC_SW) + return NULL; + if (index >= le16toh(fv_section->count)) + return NULL; + if (offset) + /* "index" passed in to this function is relative to a given + * 4k block. To get to the true index into the field vector + * table need to add the relative index to the base_offset + * field of this section + */ + *offset = le16toh(fv_section->base_offset) + index; + return fv_section->fv + index; +} + +/** + * ice_get_prof_index_max - get the max profile index for used profile + * @hw: pointer to the HW struct + * + * Calling this function will get the max profile index for used profile + * and store the index number in struct ice_switch_info *switch_info + * in hw for following use. + */ +int +ice_get_prof_index_max(struct ice_hw *hw) +{ + uint16_t prof_index = 0, j, max_prof_index = 0; + struct ice_pkg_enum state; + struct ice_seg *ice_seg; + bool flag = false; + struct ice_fv *fv; + uint32_t offset; + + memset(&state, 0, sizeof(state)); + + if (!hw->seg) + return ICE_ERR_PARAM; + + ice_seg = hw->seg; + + do { + fv = (struct ice_fv *) + ice_pkg_enum_entry(ice_seg, &state, ICE_SID_FLD_VEC_SW, + &offset, ice_sw_fv_handler); + if (!fv) + break; + ice_seg = NULL; + + /* in the profile that not be used, the prot_id is set to 0xff + * and the off is set to 0x1ff for all the field vectors. + */ + for (j = 0; j < hw->blk[ICE_BLK_SW].es.fvw; j++) + if (fv->ew[j].prot_id != ICE_PROT_INVALID || + fv->ew[j].off != ICE_FV_OFFSET_INVAL) + flag = true; + if (flag && prof_index > max_prof_index) + max_prof_index = prof_index; + + prof_index++; + flag = false; + } while (fv); + + hw->switch_info->max_used_prof_index = max_prof_index; + + return ICE_SUCCESS; +} + +/** + * ice_init_pkg - initialize/download package + * @hw: pointer to the hardware structure + * @buf: pointer to the package buffer + * @len: size of the package buffer + * + * This function initializes a package. The package contains HW tables + * required to do packet processing. First, the function extracts package + * information such as version. Then it finds the ice configuration segment + * within the package; this function then saves a copy of the segment pointer + * within the supplied package buffer. Next, the function will cache any hints + * from the package, followed by downloading the package itself. Note, that if + * a previous PF driver has already downloaded the package successfully, then + * the current driver will not have to download the package again. + * + * The local package contents will be used to query default behavior and to + * update specific sections of the HW's version of the package (e.g. to update + * the parse graph to understand new protocols). + * + * This function stores a pointer to the package buffer memory, and it is + * expected that the supplied buffer will not be freed immediately. If the + * package buffer needs to be freed, such as when read from a file, use + * ice_copy_and_init_pkg() instead of directly calling ice_init_pkg() in this + * case. + */ +enum ice_ddp_state +ice_init_pkg(struct ice_hw *hw, uint8_t *buf, uint32_t len) +{ + bool already_loaded = false; + enum ice_ddp_state state; + struct ice_pkg_hdr *pkg; + struct ice_seg *seg; + + if (!buf || !len) + return ICE_DDP_PKG_ERR; + + pkg = (struct ice_pkg_hdr *)buf; + state = ice_verify_pkg(pkg, len); + if (state) { + DNPRINTF(ICE_DBG_INIT, "failed to verify pkg (err: %d)\n", + state); + return state; + } + + /* initialize package info */ + state = ice_init_pkg_info(hw, pkg); + if (state) + return state; + + /* For packages with signing segments, must be a matching segment */ + if (hw->pkg_has_signing_seg) + if (!ice_match_signing_seg(pkg, hw->pkg_seg_id, + hw->pkg_sign_type)) + return ICE_DDP_PKG_ERR; + + /* before downloading the package, check package version for + * compatibility with driver + */ + state = ice_chk_pkg_compat(hw, pkg, &seg); + if (state) + return state; + + /* initialize package hints and then download package */ + ice_init_pkg_hints(hw, seg); + state = ice_download_pkg(hw, pkg, seg); + + if (state == ICE_DDP_PKG_ALREADY_LOADED) { + DNPRINTF(ICE_DBG_INIT, + "package previously loaded - no work.\n"); + already_loaded = true; + } + + /* Get information on the package currently loaded in HW, then make sure + * the driver is compatible with this version. + */ + if (!state || state == ICE_DDP_PKG_ALREADY_LOADED) { + state = ice_get_pkg_info(hw); + if (!state) + state = ice_get_ddp_pkg_state(hw, already_loaded); + } + + if (ice_is_init_pkg_successful(state)) { + hw->seg = seg; + /* on successful package download update other required + * registers to support the package and fill HW tables + * with package content. + */ + ice_init_pkg_regs(hw); + ice_fill_blk_tbls(hw); + ice_get_prof_index_max(hw); + } else { + DNPRINTF(ICE_DBG_INIT, "package load failed, %d\n", state); + } + + return state; +} + +/** * ice_copy_and_init_pkg - initialize/download a copy of the package * @hw: pointer to the hardware structure * @buf: pointer to the package buffer @@ -14730,14 +17271,13 @@ ice_log_pkg_init(struct ice_softc *sc, enum ice_ddp_st enum ice_ddp_state ice_copy_and_init_pkg(struct ice_hw *hw, const uint8_t *buf, uint32_t len) { -#if 0 enum ice_ddp_state state; - u8 *buf_copy; + uint8_t *buf_copy; if (!buf || !len) return ICE_DDP_PKG_ERR; - buf_copy = (u8 *)ice_memdup(hw, buf, len, ICE_NONDMA_TO_NONDMA); + buf_copy = (uint8_t *)ice_memdup(hw, buf, len); state = ice_init_pkg(hw, buf_copy, len); if (!ice_is_init_pkg_successful(state)) { @@ -14749,9 +17289,6 @@ ice_copy_and_init_pkg(struct ice_hw *hw, const uint8_t hw->pkg_size = len; } return state; -#else - return ICE_DDP_PKG_ERR; -#endif } /** @@ -14779,13 +17316,6 @@ ice_load_pkg_file(struct ice_softc *sc) const char *fwname = "ice-ddp"; int err; - /* - * XXX At present this driver always runs in safe mode. - * Do not attempt to load firmware yet. - */ - status = ICE_ERR_NOT_IMPL; - goto err_load_pkg; - err = loadfirmware(fwname, &pkg, &pkg_size); if (err) { printf("%s: could not read firmware %s (error %d); " @@ -14797,7 +17327,7 @@ ice_load_pkg_file(struct ice_softc *sc) /* Check for topology change */ if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_TX_BALANCE)) { cached_layer_count = hw->num_tx_sched_layers; - status = ice_cfg_tx_topo(&sc->hw, pkg, pkg_size); + status = ice_cfg_tx_topo(sc, pkg, pkg_size); /* Success indicates a change was made */ if (status == ICE_SUCCESS) { /* 9 -> 5 */ @@ -14812,9 +17342,9 @@ ice_load_pkg_file(struct ice_softc *sc) return (status); } else if (status == ICE_ERR_CFG) { /* DDP does not support transmit balancing */ - DPRINTF("%s: DDP package does not support transmit balancing " - "feature - please update to the latest DDP package and " - "try again\n", __func__); + DPRINTF("%s: DDP package does not support transmit " + "balancing feature - please update to the " + "latest DDP package and try again\n", __func__); } } @@ -14827,11 +17357,11 @@ ice_load_pkg_file(struct ice_softc *sc) /* Check the active DDP package version and log a message */ ice_log_pkg_init(sc, state); - /* Place the driver into safe mode */ if (ice_is_init_pkg_successful(state)) return (ICE_ERR_ALREADY_EXISTS); err_load_pkg: + /* Place the driver into safe mode */ ice_zero_bitmap(sc->feat_cap, ICE_FEATURE_COUNT); ice_zero_bitmap(sc->feat_en, ICE_FEATURE_COUNT); ice_set_bit(ICE_FEATURE_SAFE_MODE, sc->feat_cap); @@ -14910,276 +17440,6 @@ ice_init_link_events(struct ice_softc *sc) } /** - * ice_pkg_get_supported_vlan_mode - chk if DDP supports Double VLAN mode (DVM) - * @hw: pointer to the HW struct - * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false) - */ -enum ice_status -ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm) -{ -#if 0 - u16 meta_init_size = sizeof(struct ice_meta_init_section); - struct ice_meta_init_section *sect; - struct ice_buf_build *bld; - enum ice_status status; - - /* if anything fails, we assume there is no DVM support */ - *dvm = false; - - bld = ice_pkg_buf_alloc_single_section(hw, - ICE_SID_RXPARSER_METADATA_INIT, - meta_init_size, (void **)§); - if (!bld) - return ICE_ERR_NO_MEMORY; - - /* only need to read a single section */ - sect->count = CPU_TO_LE16(1); - sect->offset = CPU_TO_LE16(ICE_META_VLAN_MODE_ENTRY); - - status = ice_aq_upload_section(hw, - (struct ice_buf_hdr *)ice_pkg_buf(bld), - ICE_PKG_BUF_SIZE, NULL); - if (!status) { - ice_declare_bitmap(entry, ICE_META_INIT_BITS); - u32 arr[ICE_META_INIT_DW_CNT]; - u16 i; - - /* convert to host bitmap format */ - for (i = 0; i < ICE_META_INIT_DW_CNT; i++) - arr[i] = LE32_TO_CPU(sect->entry[0].bm[i]); - - ice_bitmap_from_array32(entry, arr, (u16)ICE_META_INIT_BITS); - - /* check if DVM is supported */ - *dvm = ice_is_bit_set(entry, ICE_META_VLAN_MODE_BIT); - } - - ice_pkg_buf_free(hw, bld); - - return status; -#else - return ICE_ERR_NOT_IMPL; -#endif -} - -/** - * ice_pkg_supports_dvm - find out if DDP supports DVM - * @hw: pointer to the HW structure - */ -bool -ice_pkg_supports_dvm(struct ice_hw *hw) -{ - enum ice_status status; - bool pkg_supports_dvm; - - status = ice_pkg_get_supported_vlan_mode(hw, &pkg_supports_dvm); - if (status) { - DNPRINTF(ICE_DBG_PKG, - "Failed to get supported VLAN mode, status %d\n", status); - return false; - } - - return pkg_supports_dvm; -} - -/** - * ice_aq_get_vlan_mode - get the VLAN mode of the device - * @hw: pointer to the HW structure - * @get_params: structure FW fills in based on the current VLAN mode config - * - * Get VLAN Mode Parameters (0x020D) - */ -enum ice_status -ice_aq_get_vlan_mode(struct ice_hw *hw, - struct ice_aqc_get_vlan_mode *get_params) -{ - struct ice_aq_desc desc; - - if (!get_params) - return ICE_ERR_PARAM; - - ice_fill_dflt_direct_cmd_desc(&desc, - ice_aqc_opc_get_vlan_mode_parameters); - - return ice_aq_send_cmd(hw, &desc, get_params, sizeof(*get_params), - NULL); -} - -/** - * ice_fw_supports_dvm - find out if FW supports DVM - * @hw: pointer to the HW structure - */ -bool -ice_fw_supports_dvm(struct ice_hw *hw) -{ - struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; - enum ice_status status; - - /* If firmware returns success, then it supports DVM, else it only - * supports SVM - */ - status = ice_aq_get_vlan_mode(hw, &get_vlan_mode); - if (status) { - DNPRINTF(ICE_DBG_NVM, - "%s: Failed to get VLAN mode, status %d\n", - __func__, status); - return false; - } - - return true; -} - -/** - * ice_is_dvm_supported - check if Double VLAN Mode is supported - * @hw: pointer to the hardware structure - * - * Returns true if Double VLAN Mode (DVM) is supported and false if only Single - * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and - * firmware must support it, otherwise only SVM is supported. This function - * should only be called while the global config lock is held and after the - * package has been successfully downloaded. - */ -bool -ice_is_dvm_supported(struct ice_hw *hw) -{ - if (!ice_pkg_supports_dvm(hw)) { - DNPRINTF(ICE_DBG_PKG, "DDP doesn't support DVM\n"); - return false; - } - - if (!ice_fw_supports_dvm(hw)) { - DNPRINTF(ICE_DBG_PKG, "FW doesn't support DVM\n"); - return false; - } - - return true; -} - -/** - * ice_aq_set_port_params - set physical port parameters - * @pi: pointer to the port info struct - * @bad_frame_vsi: defines the VSI to which bad frames are forwarded - * @save_bad_pac: if set packets with errors are forwarded to the bad frames VSI - * @pad_short_pac: if set transmit packets smaller than 60 bytes are padded - * @double_vlan: if set double VLAN is enabled - * @cd: pointer to command details structure or NULL - * - * Set Physical port parameters (0x0203) - */ -enum ice_status -ice_aq_set_port_params(struct ice_port_info *pi, uint16_t bad_frame_vsi, - bool save_bad_pac, bool pad_short_pac, bool double_vlan, - struct ice_sq_cd *cd) -{ - struct ice_aqc_set_port_params *cmd; - struct ice_hw *hw = pi->hw; - struct ice_aq_desc desc; - uint16_t cmd_flags = 0; - - cmd = &desc.params.set_port_params; - - ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_port_params); - cmd->bad_frame_vsi = htole16(bad_frame_vsi); - if (save_bad_pac) - cmd_flags |= ICE_AQC_SET_P_PARAMS_SAVE_BAD_PACKETS; - if (pad_short_pac) - cmd_flags |= ICE_AQC_SET_P_PARAMS_PAD_SHORT_PACKETS; - if (double_vlan) - cmd_flags |= ICE_AQC_SET_P_PARAMS_DOUBLE_VLAN_ENA; - cmd->cmd_flags = htole16(cmd_flags); - - return ice_aq_send_cmd(hw, &desc, NULL, 0, cd); -} - -/** - * ice_aq_set_vlan_mode - set the VLAN mode of the device - * @hw: pointer to the HW structure - * @set_params: requested VLAN mode configuration - * - * Set VLAN Mode Parameters (0x020C) - */ -enum ice_status -ice_aq_set_vlan_mode(struct ice_hw *hw, - struct ice_aqc_set_vlan_mode *set_params) -{ - uint8_t rdma_packet, mng_vlan_prot_id; - struct ice_aq_desc desc; - - if (!set_params) - return ICE_ERR_PARAM; - - if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX) - return ICE_ERR_PARAM; - - rdma_packet = set_params->rdma_packet; - if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING && - rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING) - return ICE_ERR_PARAM; - - mng_vlan_prot_id = set_params->mng_vlan_prot_id; - if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER && - mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER) - return ICE_ERR_PARAM; - - ice_fill_dflt_direct_cmd_desc(&desc, - ice_aqc_opc_set_vlan_mode_parameters); - desc.flags |= htole16(ICE_AQ_FLAG_RD); - - return ice_aq_send_cmd(hw, &desc, set_params, sizeof(*set_params), - NULL); -} - -/** - * ice_set_svm - set single VLAN mode - * @hw: pointer to the HW structure - */ -enum ice_status -ice_set_svm(struct ice_hw *hw) -{ - struct ice_aqc_set_vlan_mode *set_params; - enum ice_status status; - - status = ice_aq_set_port_params(hw->port_info, 0, - false, false, false, NULL); - if (status) { - DNPRINTF(ICE_DBG_INIT, - "Failed to set port parameters for single VLAN mode\n"); - return status; - } - - set_params = (struct ice_aqc_set_vlan_mode *) - ice_malloc(hw, sizeof(*set_params)); - if (!set_params) - return ICE_ERR_NO_MEMORY; - - /* default configuration for SVM configurations */ - set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG; - set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING; - set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER; - - status = ice_aq_set_vlan_mode(hw, set_params); - if (status) - DNPRINTF(ICE_DBG_INIT, - "Failed to configure port in single VLAN mode\n"); - - ice_free(hw, set_params); - return status; -} - -/** - * ice_set_vlan_mode - * @hw: pointer to the HW structure - */ -enum ice_status -ice_set_vlan_mode(struct ice_hw *hw) -{ - if (!ice_is_dvm_supported(hw)) - return ICE_SUCCESS; - - return ice_set_svm(hw); -} - -/** * ice_nvm_version_str - Format the NVM version information into a sbuf * @hw: device hw structure * @buf: string buffer to store version string @@ -15224,10 +17484,14 @@ void ice_print_nvm_version(struct ice_softc *sc) { struct ice_hw *hw = &sc->hw; - static char buf[512]; + char os_pkg[ICE_PKG_NAME_SIZE]; + char buf[512]; - printf("%s: %s, address %s\n", sc->sc_dev.dv_xname, + ice_os_pkg_version_str(hw, os_pkg, sizeof(os_pkg)); + + printf("%s: %s%s%s, address %s\n", sc->sc_dev.dv_xname, ice_nvm_version_str(hw, buf, sizeof(buf)), + os_pkg[0] ? " ddp " : "", os_pkg[0] ? os_pkg : "", ether_sprintf(hw->port_info->mac.perm_addr)); } @@ -15637,32 +17901,6 @@ free_tx_qmap: } /** - * ice_vsig_find_vsi - find a VSIG that contains a specified VSI - * @hw: pointer to the hardware structure - * @blk: HW block - * @vsi: VSI of interest - * @vsig: pointer to receive the VSI group - * - * This function will lookup the VSI entry in the XLT2 list and return - * the VSI group its associated with. - */ -enum ice_status -ice_vsig_find_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, - uint16_t *vsig) -{ - if (!vsig || vsi >= ICE_MAX_VSI) - return ICE_ERR_PARAM; - - /* As long as there's a default or valid VSIG associated with the input - * VSI, the functions returns a success. Any handling of VSIG will be - * done by the following add, update or remove functions. - */ - *vsig = hw->blk[blk].xlt2.vsis[vsi].vsig; - - return ICE_SUCCESS; -} - -/** * ice_vsig_prof_id_count - count profiles in a VSIG * @hw: pointer to the HW struct * @blk: hardware block @@ -16478,127 +18716,6 @@ ice_rem_prof_from_list(struct ice_hw *hw, struct ice_v } /** - * ice_vsig_remove_vsi - remove VSI from VSIG - * @hw: pointer to the hardware structure - * @blk: HW block - * @vsi: VSI to remove - * @vsig: VSI group to remove from - * - * The function will remove the input VSI from its VSI group and move it - * to the DEFAULT_VSIG. - */ -enum ice_status -ice_vsig_remove_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, - uint16_t vsig) -{ - struct ice_vsig_vsi **vsi_head, *vsi_cur, *vsi_tgt; - uint16_t idx; - - idx = vsig & ICE_VSIG_IDX_M; - - if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) - return ICE_ERR_PARAM; - - if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) - return ICE_ERR_DOES_NOT_EXIST; - - /* entry already in default VSIG, don't have to remove */ - if (idx == ICE_DEFAULT_VSIG) - return ICE_SUCCESS; - - vsi_head = &hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; - if (!(*vsi_head)) - return ICE_ERR_CFG; - - vsi_tgt = &hw->blk[blk].xlt2.vsis[vsi]; - vsi_cur = (*vsi_head); - - /* iterate the VSI list, skip over the entry to be removed */ - while (vsi_cur) { - if (vsi_tgt == vsi_cur) { - (*vsi_head) = vsi_cur->next_vsi; - break; - } - vsi_head = &vsi_cur->next_vsi; - vsi_cur = vsi_cur->next_vsi; - } - - /* verify if VSI was removed from group list */ - if (!vsi_cur) - return ICE_ERR_DOES_NOT_EXIST; - - vsi_cur->vsig = ICE_DEFAULT_VSIG; - vsi_cur->changed = 1; - vsi_cur->next_vsi = NULL; - - return ICE_SUCCESS; -} - -/** - * ice_vsig_add_mv_vsi - add or move a VSI to a VSI group - * @hw: pointer to the hardware structure - * @blk: HW block - * @vsi: VSI to move - * @vsig: destination VSI group - * - * This function will move or add the input VSI to the target VSIG. - * The function will find the original VSIG the VSI belongs to and - * move the entry to the DEFAULT_VSIG, update the original VSIG and - * then move entry to the new VSIG. - */ -enum ice_status -ice_vsig_add_mv_vsi(struct ice_hw *hw, enum ice_block blk, uint16_t vsi, - uint16_t vsig) -{ - struct ice_vsig_vsi *tmp; - enum ice_status status; - uint16_t orig_vsig, idx; - - idx = vsig & ICE_VSIG_IDX_M; - - if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) - return ICE_ERR_PARAM; - - /* if VSIG not in use and VSIG is not default type this VSIG - * doesn't exist. - */ - if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use && - vsig != ICE_DEFAULT_VSIG) - return ICE_ERR_DOES_NOT_EXIST; - - status = ice_vsig_find_vsi(hw, blk, vsi, &orig_vsig); - if (status) - return status; - - /* no update required if vsigs match */ - if (orig_vsig == vsig) - return ICE_SUCCESS; - - if (orig_vsig != ICE_DEFAULT_VSIG) { - /* remove entry from orig_vsig and add to default VSIG */ - status = ice_vsig_remove_vsi(hw, blk, vsi, orig_vsig); - if (status) - return status; - } - - if (idx == ICE_DEFAULT_VSIG) - return ICE_SUCCESS; - - /* Create VSI entry and add VSIG and prop_mask values */ - hw->blk[blk].xlt2.vsis[vsi].vsig = vsig; - hw->blk[blk].xlt2.vsis[vsi].changed = 1; - - /* Add new entry to the head of the VSIG list */ - tmp = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; - hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi = - &hw->blk[blk].xlt2.vsis[vsi]; - hw->blk[blk].xlt2.vsis[vsi].next_vsi = tmp; - hw->blk[blk].xlt2.t[vsi] = vsig; - - return ICE_SUCCESS; -} - -/** * ice_move_vsi - move VSI to another VSIG * @hw: pointer to the HW struct * @blk: hardware block @@ -16638,27 +18755,6 @@ ice_move_vsi(struct ice_hw *hw, enum ice_block blk, ui } /** - * ice_vsig_alloc_val - allocate a new VSIG by value - * @hw: pointer to the hardware structure - * @blk: HW block - * @vsig: the VSIG to allocate - * - * This function will allocate a given VSIG specified by the VSIG parameter. - */ -uint16_t -ice_vsig_alloc_val(struct ice_hw *hw, enum ice_block blk, uint16_t vsig) -{ - uint16_t idx = vsig & ICE_VSIG_IDX_M; - - if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) { - TAILQ_INIT(&hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst); - hw->blk[blk].xlt2.vsig_tbl[idx].in_use = true; - } - - return ICE_VSIG_VALUE(idx, hw->pf_id); -} - -/** * ice_vsig_alloc - Finds a free entry and allocates a new VSIG * @hw: pointer to the hardware structure * @blk: HW block @@ -22097,28 +24193,6 @@ ice_alloc_prof_id(struct ice_hw *hw, enum ice_block bl } /** - * ice_ptg_find_ptype - Search for packet type group using packet type (ptype) - * @hw: pointer to the hardware structure - * @blk: HW block - * @ptype: the ptype to search for - * @ptg: pointer to variable that receives the PTG - * - * This function will search the PTGs for a particular ptype, returning the - * PTG ID that contains it through the PTG parameter, with the value of - * ICE_DEFAULT_PTG (0) meaning it is part the default PTG. - */ -enum ice_status -ice_ptg_find_ptype(struct ice_hw *hw, enum ice_block blk, uint16_t ptype, - uint8_t *ptg) -{ - if (ptype >= ICE_XLT1_CNT || !ptg) - return ICE_ERR_PARAM; - - *ptg = hw->blk[blk].xlt1.ptypes[ptype].ptg; - return ICE_SUCCESS; -} - -/** * ice_add_prof - add profile * @hw: pointer to the HW struct * @blk: hardware block @@ -23355,6 +25429,36 @@ ice_config_rss(struct ice_vsi *vsi) } /** + * ice_transition_safe_mode - Transition to safe mode + * @sc: the device private softc + * + * Called when the driver attempts to reload the DDP package during a device + * reset, and the new download fails. If so, we must transition to safe mode + * at run time. + * + * @remark although safe mode normally allocates only a single queue, we can't + * change the number of queues dynamically when using iflib. Due to this, we + * do not attempt to reduce the number of queues. + */ +void +ice_transition_safe_mode(struct ice_softc *sc) +{ + /* Indicate that we are in Safe mode */ + ice_set_bit(ICE_FEATURE_SAFE_MODE, sc->feat_cap); + ice_set_bit(ICE_FEATURE_SAFE_MODE, sc->feat_en); +#if 0 + ice_rdma_pf_detach(sc); +#endif + ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap); + + ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en); + ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_cap); + + ice_clear_bit(ICE_FEATURE_RSS, sc->feat_cap); + ice_clear_bit(ICE_FEATURE_RSS, sc->feat_en); +} + +/** * ice_rebuild - Rebuild driver state post reset * @sc: The device private softc * @@ -23464,7 +25568,6 @@ ice_rebuild(struct ice_softc *sc) /* If we previously loaded the package, it needs to be reloaded now */ if (!ice_is_bit_set(sc->feat_en, ICE_FEATURE_SAFE_MODE)) { -#if 0 enum ice_ddp_state pkg_state; pkg_state = ice_init_pkg(hw, hw->pkg_copy, hw->pkg_size); @@ -23472,7 +25575,6 @@ ice_rebuild(struct ice_softc *sc) ice_log_pkg_init(sc, pkg_state); ice_transition_safe_mode(sc); } -#endif } ice_reset_pf_stats(sc); @@ -27312,8 +29414,10 @@ ice_attach_hook(struct device *self) */ status = ice_load_pkg_file(sc); if (status == ICE_SUCCESS) { - ice_reinit_hw(sc); - return; + ice_deinit_hw(hw); + err = ice_reinit_hw(sc); + if (err) + return; } err = ice_init_link_events(sc); blob - ca5fd00f521795c6fd31fb824725b73e787cc152 blob + 251ca398d7861a20e1e1dd63dd0d26df38c2b89c --- sys/dev/pci/if_icevar.h +++ sys/dev/pci/if_icevar.h @@ -2306,6 +2306,8 @@ struct ice_prot_ext_tbl_entry { uint8_t offs[sizeof(union ice_prot_hdr)]; }; +#define ICE_FV_OFFSET_INVAL 0x1FF + /* Extraction Sequence (Field Vector) Table */ struct ice_fv_word { uint8_t prot_id; @@ -2319,6 +2321,40 @@ struct ice_fv { struct ice_fv_word ew[ICE_MAX_FV_WORDS]; }; +/* Packet Type (PTYPE) values */ +#define ICE_PTYPE_MAC_PAY 1 +#define ICE_PTYPE_IPV4FRAG_PAY 22 +#define ICE_PTYPE_IPV4_PAY 23 +#define ICE_PTYPE_IPV4_UDP_PAY 24 +#define ICE_PTYPE_IPV4_TCP_PAY 26 +#define ICE_PTYPE_IPV4_SCTP_PAY 27 +#define ICE_PTYPE_IPV4_ICMP_PAY 28 +#define ICE_PTYPE_IPV6FRAG_PAY 88 +#define ICE_PTYPE_IPV6_PAY 89 +#define ICE_PTYPE_IPV6_UDP_PAY 90 +#define ICE_PTYPE_IPV6_TCP_PAY 92 +#define ICE_PTYPE_IPV6_SCTP_PAY 93 +#define ICE_PTYPE_IPV6_ICMP_PAY 94 + +struct ice_meta_sect { + struct ice_pkg_ver ver; +#define ICE_META_SECT_NAME_SIZE 28 + char name[ICE_META_SECT_NAME_SIZE]; + uint32_t track_id; +}; + +/* Packet Type Groups (PTG) - Inner Most fields (IM) */ +#define ICE_PTG_IM_IPV4_TCP 16 +#define ICE_PTG_IM_IPV4_UDP 17 +#define ICE_PTG_IM_IPV4_SCTP 18 +#define ICE_PTG_IM_IPV4_PAY 20 +#define ICE_PTG_IM_IPV4_OTHER 21 +#define ICE_PTG_IM_IPV6_TCP 32 +#define ICE_PTG_IM_IPV6_UDP 33 +#define ICE_PTG_IM_IPV6_SCTP 34 +#define ICE_PTG_IM_IPV6_OTHER 37 +#define ICE_PTG_IM_L2_OTHER 67 + /* Extractions to be looked up for a given recipe */ struct ice_prot_lkup_ext { uint16_t prot_type; @@ -3069,7 +3105,124 @@ enum ice_ddp_state { ICE_DDP_PKG_ERR = -14, }; +/* Package and segment headers and tables */ +struct ice_pkg_hdr { + struct ice_pkg_ver pkg_format_ver; + uint32_t seg_count; + uint32_t seg_offset[STRUCT_HACK_VAR_LEN]; +}; +/* Package signing algorithm types */ +#define SEGMENT_SIGN_TYPE_INVALID 0x00000000 +#define SEGMENT_SIGN_TYPE_RSA2K 0x00000001 +#define SEGMENT_SIGN_TYPE_RSA3K 0x00000002 +#define SEGMENT_SIGN_TYPE_RSA3K_SBB 0x00000003 /* Secure Boot Block */ +#define SEGMENT_SIGN_TYPE_RSA3K_E825 0x00000005 + +/* generic segment */ +struct ice_generic_seg_hdr { +#define SEGMENT_TYPE_INVALID 0x00000000 +#define SEGMENT_TYPE_METADATA 0x00000001 +#define SEGMENT_TYPE_ICE_E810 0x00000010 +#define SEGMENT_TYPE_SIGNING 0x00001001 +#define SEGMENT_TYPE_ICE_RUN_TIME_CFG 0x00000020 + uint32_t seg_type; + struct ice_pkg_ver seg_format_ver; + uint32_t seg_size; + char seg_id[ICE_PKG_NAME_SIZE]; +}; + +/* ice specific segment */ + +union ice_device_id { + struct { + uint16_t device_id; + uint16_t vendor_id; + } dev_vend_id; + uint32_t id; +}; + +struct ice_device_id_entry { + union ice_device_id device; + union ice_device_id sub_device; +}; + +struct ice_seg { + struct ice_generic_seg_hdr hdr; + uint32_t device_table_count; + struct ice_device_id_entry device_table[STRUCT_HACK_VAR_LEN]; +}; + +struct ice_nvm_table { + uint32_t table_count; + uint32_t vers[STRUCT_HACK_VAR_LEN]; +}; + +struct ice_buf { +#define ICE_PKG_BUF_SIZE 4096 + uint8_t buf[ICE_PKG_BUF_SIZE]; +}; + +struct ice_buf_table { + uint32_t buf_count; + struct ice_buf buf_array[STRUCT_HACK_VAR_LEN]; +}; + +struct ice_run_time_cfg_seg { + struct ice_generic_seg_hdr hdr; + uint8_t rsvd[8]; + struct ice_buf_table buf_table; +}; + +/* global metadata specific segment */ +struct ice_global_metadata_seg { + struct ice_generic_seg_hdr hdr; + struct ice_pkg_ver pkg_ver; + uint32_t rsvd; + char pkg_name[ICE_PKG_NAME_SIZE]; +}; + +#define ICE_MIN_S_OFF 12 +#define ICE_MAX_S_OFF 4095 +#define ICE_MIN_S_SZ 1 +#define ICE_MAX_S_SZ 4084 + +struct ice_sign_seg { + struct ice_generic_seg_hdr hdr; + uint32_t seg_id; + uint32_t sign_type; + uint32_t signed_seg_idx; + uint32_t signed_buf_start; + uint32_t signed_buf_count; +#define ICE_SIGN_SEG_RESERVED_COUNT 44 + uint8_t reserved[ICE_SIGN_SEG_RESERVED_COUNT]; + struct ice_buf_table buf_tbl; +}; + +/* section information */ +struct ice_section_entry { + uint32_t type; + uint16_t offset; + uint16_t size; +}; + +#define ICE_MIN_S_COUNT 1 +#define ICE_MAX_S_COUNT 511 +#define ICE_MIN_S_DATA_END 12 +#define ICE_MAX_S_DATA_END 4096 + +#define ICE_METADATA_BUF 0x80000000 + +struct ice_buf_hdr { + uint16_t section_count; + uint16_t data_end; + struct ice_section_entry section_entry[STRUCT_HACK_VAR_LEN]; +}; + +#define ICE_MAX_ENTRIES_IN_BUF(hd_sz, ent_sz) ((ICE_PKG_BUF_SIZE - \ + ice_struct_size((struct ice_buf_hdr *)0, section_entry, 1) - (hd_sz)) /\ + (ent_sz)) + /* ice package section IDs */ #define ICE_SID_METADATA 1 #define ICE_SID_XLT0_SW 10 @@ -3215,6 +3368,171 @@ enum ice_block { ICE_BLK_COUNT }; +enum ice_sect { + ICE_XLT0 = 0, + ICE_XLT_KB, + ICE_XLT1, + ICE_XLT2, + ICE_PROF_TCAM, + ICE_PROF_REDIR, + ICE_VEC_TBL, + ICE_CDID_KB, + ICE_CDID_REDIR, + ICE_SECT_COUNT +}; + +/* package buffer building */ + +struct ice_buf_build { + struct ice_buf buf; + uint16_t reserved_section_table_entries; +}; + +struct ice_pkg_enum { + struct ice_buf_table *buf_table; + uint32_t buf_idx; + + uint32_t type; + struct ice_buf_hdr *buf; + uint32_t sect_idx; + void *sect; + uint32_t sect_type; + + uint32_t entry_idx; + void *(*handler)(uint32_t sect_type, void *section, uint32_t index, + uint32_t *offset); +}; + +struct ice_flex_fields { + union { + struct { + uint8_t src_ip; + uint8_t dst_ip; + uint8_t flow_label; /* valid for IPv6 only */ + } ip_fields; + + struct { + uint8_t src_prt; + uint8_t dst_prt; + } tcp_udp_fields; + + struct { + uint8_t src_ip; + uint8_t dst_ip; + uint8_t src_prt; + uint8_t dst_prt; + } ip_tcp_udp_fields; + + struct { + uint8_t src_prt; + uint8_t dst_prt; + uint8_t flow_label; /* valid for IPv6 only */ + uint8_t spi; + } ip_esp_fields; + + struct { + uint32_t offset; + uint32_t length; + } off_len; + } fields; +}; + +#define ICE_XLT1_DFLT_GRP 0 +#define ICE_XLT1_TABLE_SIZE 1024 + +/* package labels */ +struct ice_label { + uint16_t value; +#define ICE_PKG_LABEL_SIZE 64 + char name[ICE_PKG_LABEL_SIZE]; +}; + +struct ice_label_section { + uint16_t count; + struct ice_label label[STRUCT_HACK_VAR_LEN]; +}; + +#define ICE_MAX_LABELS_IN_BUF ICE_MAX_ENTRIES_IN_BUF( \ + ice_struct_size((struct ice_label_section *)0, label, 1) - \ + sizeof(struct ice_label), sizeof(struct ice_label)) + +struct ice_sw_fv_section { + uint16_t count; + uint16_t base_offset; + struct ice_fv fv[STRUCT_HACK_VAR_LEN]; +}; + + +#pragma pack(1) +/* The BOOST TCAM stores the match packet header in reverse order, meaning + * the fields are reversed; in addition, this means that the normally big endian + * fields of the packet are now little endian. + */ +struct ice_boost_key_value { +#define ICE_BOOST_REMAINING_HV_KEY 15 + uint8_t remaining_hv_key[ICE_BOOST_REMAINING_HV_KEY]; + union { + struct { + uint16_t hv_dst_port_key; + uint16_t hv_src_port_key; + } /* udp_tunnel */; + struct { + uint16_t hv_vlan_id_key; + uint16_t hv_etype_key; + } vlan; + }; + uint8_t tcam_search_key; +}; +#pragma pack() + +struct ice_boost_key { + struct ice_boost_key_value key; + struct ice_boost_key_value key2; +}; + +/* package Boost TCAM entry */ +struct ice_boost_tcam_entry { + uint16_t addr; + uint16_t reserved; + /* break up the 40 bytes of key into different fields */ + struct ice_boost_key key; + uint8_t boost_hit_index_group; + /* The following contains bitfields which are not on byte boundaries. + * These fields are currently unused by driver software. + */ +#define ICE_BOOST_BIT_FIELDS 43 + uint8_t bit_fields[ICE_BOOST_BIT_FIELDS]; +}; + +struct ice_boost_tcam_section { + uint16_t count; + uint16_t reserved; + struct ice_boost_tcam_entry tcam[STRUCT_HACK_VAR_LEN]; +}; + +#define ICE_MAX_BST_TCAMS_IN_BUF ICE_MAX_ENTRIES_IN_BUF( \ + ice_struct_size((struct ice_boost_tcam_section *)0, tcam, 1) - \ + sizeof(struct ice_boost_tcam_entry), \ + sizeof(struct ice_boost_tcam_entry)) + +struct ice_xlt1_section { + uint16_t count; + uint16_t offset; + uint8_t value[STRUCT_HACK_VAR_LEN]; +}; + +struct ice_xlt2_section { + uint16_t count; + uint16_t offset; + uint16_t value[STRUCT_HACK_VAR_LEN]; +}; + +struct ice_prof_redir_section { + uint16_t count; + uint16_t offset; + uint8_t redir_value[STRUCT_HACK_VAR_LEN]; +}; + /* Tunnel enabling */ enum ice_tunnel_type { @@ -3251,6 +3569,12 @@ struct ice_tunnel_table { uint16_t count; }; + +/* To support tunneling entries by PF, the package will append the PF number to + * the label; for example TNL_VXLAN_PF0, TNL_VXLAN_PF1, TNL_VXLAN_PF2, etc. + */ +#define ICE_TNL_PRE "TNL_" + struct ice_pkg_es { uint16_t count; uint16_t offset; @@ -3656,7 +3980,7 @@ struct ice_hw { /* Driver's package ver - (from the Ice Metadata section) */ struct ice_pkg_ver pkg_ver; uint8_t pkg_name[ICE_PKG_NAME_SIZE]; -#if 0 + /* Driver's Ice segment format version and id (from the Ice seg) */ struct ice_pkg_ver ice_seg_fmt_ver; uint8_t ice_seg_id[ICE_SEG_ID_SIZE]; @@ -3666,12 +3990,12 @@ struct ice_hw { /* Pointer to allocated copy of pkg memory */ uint8_t *pkg_copy; - u32 pkg_size; + uint32_t pkg_size; /* tunneling info */ struct ice_lock tnl_lock; struct ice_tunnel_table tnl; -#endif + /* HW block tables */ struct ice_blk_info blk[ICE_BLK_COUNT]; #if 0 @@ -3685,8 +4009,9 @@ struct ice_hw { #if 0 uint16_t vsi_owning_pf_lut; /* SW IDX of VSI that acquired PF RSS LUT */ struct ice_mbx_snapshot mbx_snapshot; +#endif uint8_t dvm_ena; - +#if 0 bool subscribable_recipes_supported; #endif };