/* SPDX-License-Identifier: BSD-3-Clause
*
- * Copyright(c) 2019 Xilinx, Inc. All rights reserved.
- * All rights reserved.
+ * Copyright(c) 2019-2021 Xilinx, Inc.
*/
#include "efx.h"
* The information in it is meant to be used internally by
* APIs for addressing a given field in a mask-value pairs
* structure and for validation purposes.
+ *
+ * A field may have an alternative one. This structure
+ * has additional members to reference the alternative
+ * field's mask. See efx_mae_match_spec_is_valid().
*/
typedef struct efx_mae_mv_desc_s {
efx_mae_field_cap_id_t emmd_field_cap_id;
size_t emmd_mask_size;
size_t emmd_mask_offset;
+ /*
+ * Having the alternative field's mask size set to 0
+ * means that there's no alternative field specified.
+ */
+ size_t emmd_alt_mask_size;
+ size_t emmd_alt_mask_offset;
+
+ /* Primary field and the alternative one are of the same endianness. */
efx_mae_field_endianness_t emmd_endianness;
} efx_mae_mv_desc_t;
MAE_FIELD_MASK_VALUE_PAIRS_##_name##_OFST, \
MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_LEN, \
MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_OFST, \
+ 0, 0 /* no alternative field */, \
_endianness \
}
MAE_ENC_FIELD_PAIRS_##_name##_OFST, \
MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN, \
MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST, \
+ 0, 0 /* no alternative field */, \
+ _endianness \
+ }
+
+/* Same as EFX_MAE_MV_DESC(), but also indicates an alternative field. */
+#define EFX_MAE_MV_DESC_ALT(_name, _alt_name, _endianness) \
+ [EFX_MAE_FIELD_##_name] = \
+ { \
+ EFX_MAE_FIELD_ID_##_name, \
+ MAE_ENC_FIELD_PAIRS_##_name##_LEN, \
+ MAE_ENC_FIELD_PAIRS_##_name##_OFST, \
+ MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN, \
+ MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST, \
+ MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_LEN, \
+ MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_OFST, \
_endianness \
}
EFX_MAE_MV_DESC(ENC_VLAN0_PROTO_BE, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_VLAN1_TCI_BE, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_VLAN1_PROTO_BE, EFX_MAE_FIELD_BE),
- EFX_MAE_MV_DESC(ENC_SRC_IP4_BE, EFX_MAE_FIELD_BE),
- EFX_MAE_MV_DESC(ENC_DST_IP4_BE, EFX_MAE_FIELD_BE),
+ EFX_MAE_MV_DESC_ALT(ENC_SRC_IP4_BE, ENC_SRC_IP6_BE, EFX_MAE_FIELD_BE),
+ EFX_MAE_MV_DESC_ALT(ENC_DST_IP4_BE, ENC_DST_IP6_BE, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_IP_PROTO, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_IP_TOS, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_IP_TTL, EFX_MAE_FIELD_BE),
- EFX_MAE_MV_DESC(ENC_SRC_IP6_BE, EFX_MAE_FIELD_BE),
- EFX_MAE_MV_DESC(ENC_DST_IP6_BE, EFX_MAE_FIELD_BE),
+ EFX_MAE_MV_DESC_ALT(ENC_SRC_IP6_BE, ENC_SRC_IP4_BE, EFX_MAE_FIELD_BE),
+ EFX_MAE_MV_DESC_ALT(ENC_DST_IP6_BE, ENC_DST_IP4_BE, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_L4_SPORT_BE, EFX_MAE_FIELD_BE),
EFX_MAE_MV_DESC(ENC_L4_DPORT_BE, EFX_MAE_FIELD_BE),
+#undef EFX_MAE_MV_DESC_ALT
#undef EFX_MAE_MV_DESC
};
MAE_MPORT_SELECTOR_PPORT_ID, phy_port);
memset(mportp, 0, sizeof (*mportp));
- mportp->sel = dword.ed_u32[0];
+ /*
+ * The constructed DWORD is little-endian,
+ * but the resulting value is meant to be
+ * passed to MCDIs, where it will undergo
+ * host-order to little endian conversion.
+ */
+ mportp->sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
return (0);
MAE_MPORT_SELECTOR_FUNC_VF_ID, vf);
memset(mportp, 0, sizeof (*mportp));
- mportp->sel = dword.ed_u32[0];
+ /*
+ * The constructed DWORD is little-endian,
+ * but the resulting value is meant to be
+ * passed to MCDIs, where it will undergo
+ * host-order to little endian conversion.
+ */
+ mportp->sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
return (0);
goto fail2;
}
- if (value_size != descp->emmd_value_size) {
+ if (descp->emmd_mask_size == 0) {
+ /* The ID points to a gap in the array of field descriptors. */
rc = EINVAL;
goto fail3;
}
- if (mask_size != descp->emmd_mask_size) {
+ if (value_size != descp->emmd_value_size) {
rc = EINVAL;
goto fail4;
}
+ if (mask_size != descp->emmd_mask_size) {
+ rc = EINVAL;
+ goto fail5;
+ }
+
if (descp->emmd_endianness == EFX_MAE_FIELD_BE) {
+ unsigned int i;
+
/*
* The mask/value are in network (big endian) order.
* The MCDI request field is also big endian.
*/
- memcpy(mvp + descp->emmd_value_offset, value, value_size);
- memcpy(mvp + descp->emmd_mask_offset, mask, mask_size);
+
+ EFSYS_ASSERT3U(value_size, ==, mask_size);
+
+ for (i = 0; i < value_size; ++i) {
+ uint8_t *v_bytep = mvp + descp->emmd_value_offset + i;
+ uint8_t *m_bytep = mvp + descp->emmd_mask_offset + i;
+
+ /*
+ * Apply the mask (which may be all-zeros) to the value.
+ *
+ * If this API is provided with some value to set for a
+ * given field in one specification and with some other
+ * value to set for this field in another specification,
+ * then, if the two masks are all-zeros, the field will
+ * avoid being counted as a mismatch when comparing the
+ * specifications using efx_mae_match_specs_equal() API.
+ */
+ *v_bytep = value[i] & mask[i];
+ *m_bytep = mask[i];
+ }
} else {
efx_dword_t dword;
return (0);
+fail5:
+ EFSYS_PROBE(fail5);
fail4:
EFSYS_PROBE(fail4);
fail3:
++field_id) {
const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
+ const uint8_t *alt_m_buf = mvp + descp->emmd_alt_mask_offset;
const uint8_t *m_buf = mvp + descp->emmd_mask_offset;
+ size_t alt_m_size = descp->emmd_alt_mask_size;
size_t m_size = descp->emmd_mask_size;
if (m_size == 0)
continue; /* Skip array gap */
- if ((unsigned int)field_cap_id >= field_ncaps)
- break;
+ if ((unsigned int)field_cap_id >= field_ncaps) {
+ /*
+ * The FW has not reported capability status for
+ * this field. Make sure that its mask is zeroed.
+ */
+ is_valid = efx_mask_is_all_zeros(m_size, m_buf);
+ if (is_valid != B_FALSE)
+ continue;
+ else
+ break;
+ }
switch (field_caps[field_cap_id].emfc_support) {
case MAE_FIELD_SUPPORTED_MATCH_MASK:
break;
case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
is_valid = efx_mask_is_all_ones(m_size, m_buf);
+
+ if ((is_valid == B_FALSE) && (alt_m_size != 0)) {
+ /*
+ * This field has an alternative one. The FW
+ * reports ALWAYS for both implying that one
+ * of them is required to have all-ones mask.
+ *
+ * The primary field's mask is incorrect; go
+ * on to check that of the alternative field.
+ */
+ is_valid = efx_mask_is_all_ones(alt_m_size,
+ alt_m_buf);
+ }
break;
case MAE_FIELD_SUPPORTED_MATCH_NEVER:
case MAE_FIELD_UNSUPPORTED:
EFX_POPULATE_DWORD_1(dword,
MAE_MPORT_SELECTOR_FLAT, MAE_MPORT_SELECTOR_NULL);
- mport.sel = dword.ed_u32[0];
+ /*
+ * The constructed DWORD is little-endian,
+ * but the resulting value is meant to be
+ * passed to MCDIs, where it will undergo
+ * host-order to little endian conversion.
+ */
+ mport.sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
arg = (const uint8_t *)&mport.sel;
++field_id) {
const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
-
- if (descp->emmd_mask_size == 0)
+ const uint8_t *lmaskp = mvpl + descp->emmd_mask_offset;
+ const uint8_t *rmaskp = mvpr + descp->emmd_mask_offset;
+ size_t mask_size = descp->emmd_mask_size;
+ const uint8_t *lvalp = mvpl + descp->emmd_value_offset;
+ const uint8_t *rvalp = mvpr + descp->emmd_value_offset;
+ size_t value_size = descp->emmd_value_size;
+
+ if (mask_size == 0)
continue; /* Skip array gap */
- if ((unsigned int)field_cap_id >= field_ncaps)
- break;
+ if ((unsigned int)field_cap_id >= field_ncaps) {
+ /*
+ * The FW has not reported capability status for this
+ * field. It's unknown whether any difference between
+ * the two masks / values affects the class. The only
+ * case when the class must be the same is when these
+ * mask-value pairs match. Otherwise, report mismatch.
+ */
+ if ((memcmp(lmaskp, rmaskp, mask_size) == 0) &&
+ (memcmp(lvalp, rvalp, value_size) == 0))
+ continue;
+ else
+ break;
+ }
if (field_caps[field_cap_id].emfc_mask_affects_class) {
- const uint8_t *lmaskp = mvpl + descp->emmd_mask_offset;
- const uint8_t *rmaskp = mvpr + descp->emmd_mask_offset;
- size_t mask_size = descp->emmd_mask_size;
-
if (memcmp(lmaskp, rmaskp, mask_size) != 0) {
have_same_class = B_FALSE;
break;
}
if (field_caps[field_cap_id].emfc_match_affects_class) {
- const uint8_t *lvalp = mvpl + descp->emmd_value_offset;
- const uint8_t *rvalp = mvpr + descp->emmd_value_offset;
- size_t value_size = descp->emmd_value_size;
-
if (memcmp(lvalp, rvalp, value_size) != 0) {
have_same_class = B_FALSE;
break;