xref: /dpdk/drivers/common/sfc_efx/base/efx_mae.c (revision b19f366c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2021 Xilinx, Inc.
4  */
5 
6 #include "efx.h"
7 #include "efx_impl.h"
8 
9 
10 #if EFSYS_OPT_MAE
11 
12 static	__checkReturn			efx_rc_t
13 efx_mae_get_capabilities(
14 	__in				efx_nic_t *enp)
15 {
16 	efx_mcdi_req_t req;
17 	EFX_MCDI_DECLARE_BUF(payload,
18 	    MC_CMD_MAE_GET_CAPS_IN_LEN,
19 	    MC_CMD_MAE_GET_CAPS_OUT_LEN);
20 	struct efx_mae_s *maep = enp->en_maep;
21 	efx_rc_t rc;
22 
23 	req.emr_cmd = MC_CMD_MAE_GET_CAPS;
24 	req.emr_in_buf = payload;
25 	req.emr_in_length = MC_CMD_MAE_GET_CAPS_IN_LEN;
26 	req.emr_out_buf = payload;
27 	req.emr_out_length = MC_CMD_MAE_GET_CAPS_OUT_LEN;
28 
29 	efx_mcdi_execute(enp, &req);
30 
31 	if (req.emr_rc != 0) {
32 		rc = req.emr_rc;
33 		goto fail1;
34 	}
35 
36 	if (req.emr_out_length_used < MC_CMD_MAE_GET_CAPS_OUT_LEN) {
37 		rc = EMSGSIZE;
38 		goto fail2;
39 	}
40 
41 	maep->em_max_n_outer_prios =
42 	    MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_OUTER_PRIOS);
43 
44 	maep->em_max_n_action_prios =
45 	    MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_ACTION_PRIOS);
46 
47 	maep->em_encap_types_supported = 0;
48 
49 	if (MCDI_OUT_DWORD_FIELD(req, MAE_GET_CAPS_OUT_ENCAP_TYPES_SUPPORTED,
50 	    MAE_GET_CAPS_OUT_ENCAP_TYPE_VXLAN) != 0) {
51 		maep->em_encap_types_supported |=
52 		    (1U << EFX_TUNNEL_PROTOCOL_VXLAN);
53 	}
54 
55 	if (MCDI_OUT_DWORD_FIELD(req, MAE_GET_CAPS_OUT_ENCAP_TYPES_SUPPORTED,
56 	    MAE_GET_CAPS_OUT_ENCAP_TYPE_GENEVE) != 0) {
57 		maep->em_encap_types_supported |=
58 		    (1U << EFX_TUNNEL_PROTOCOL_GENEVE);
59 	}
60 
61 	if (MCDI_OUT_DWORD_FIELD(req, MAE_GET_CAPS_OUT_ENCAP_TYPES_SUPPORTED,
62 	    MAE_GET_CAPS_OUT_ENCAP_TYPE_NVGRE) != 0) {
63 		maep->em_encap_types_supported |=
64 		    (1U << EFX_TUNNEL_PROTOCOL_NVGRE);
65 	}
66 
67 	maep->em_max_nfields =
68 	    MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
69 
70 	return (0);
71 
72 fail2:
73 	EFSYS_PROBE(fail2);
74 fail1:
75 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
76 	return (rc);
77 }
78 
79 static	__checkReturn			efx_rc_t
80 efx_mae_get_outer_rule_caps(
81 	__in				efx_nic_t *enp,
82 	__in				unsigned int field_ncaps,
83 	__out_ecount(field_ncaps)	efx_mae_field_cap_t *field_caps)
84 {
85 	efx_mcdi_req_t req;
86 	EFX_MCDI_DECLARE_BUF(payload,
87 	    MC_CMD_MAE_GET_OR_CAPS_IN_LEN,
88 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LENMAX_MCDI2);
89 	unsigned int mcdi_field_ncaps;
90 	unsigned int i;
91 	efx_rc_t rc;
92 
93 	if (MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(field_ncaps) >
94 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LENMAX_MCDI2) {
95 		rc = EINVAL;
96 		goto fail1;
97 	}
98 
99 	req.emr_cmd = MC_CMD_MAE_GET_OR_CAPS;
100 	req.emr_in_buf = payload;
101 	req.emr_in_length = MC_CMD_MAE_GET_OR_CAPS_IN_LEN;
102 	req.emr_out_buf = payload;
103 	req.emr_out_length = MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(field_ncaps);
104 
105 	efx_mcdi_execute(enp, &req);
106 
107 	if (req.emr_rc != 0) {
108 		rc = req.emr_rc;
109 		goto fail2;
110 	}
111 
112 	mcdi_field_ncaps = MCDI_OUT_DWORD(req, MAE_GET_OR_CAPS_OUT_COUNT);
113 
114 	if (req.emr_out_length_used <
115 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(mcdi_field_ncaps)) {
116 		rc = EMSGSIZE;
117 		goto fail3;
118 	}
119 
120 	if (mcdi_field_ncaps > field_ncaps) {
121 		rc = EMSGSIZE;
122 		goto fail4;
123 	}
124 
125 	for (i = 0; i < mcdi_field_ncaps; ++i) {
126 		uint32_t match_flag;
127 		uint32_t mask_flag;
128 
129 		field_caps[i].emfc_support = MCDI_OUT_INDEXED_DWORD_FIELD(req,
130 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
131 		    MAE_FIELD_FLAGS_SUPPORT_STATUS);
132 
133 		match_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
134 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
135 		    MAE_FIELD_FLAGS_MATCH_AFFECTS_CLASS);
136 
137 		field_caps[i].emfc_match_affects_class =
138 		    (match_flag != 0) ? B_TRUE : B_FALSE;
139 
140 		mask_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
141 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
142 		    MAE_FIELD_FLAGS_MASK_AFFECTS_CLASS);
143 
144 		field_caps[i].emfc_mask_affects_class =
145 		    (mask_flag != 0) ? B_TRUE : B_FALSE;
146 	}
147 
148 	return (0);
149 
150 fail4:
151 	EFSYS_PROBE(fail4);
152 fail3:
153 	EFSYS_PROBE(fail3);
154 fail2:
155 	EFSYS_PROBE(fail2);
156 fail1:
157 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
158 	return (rc);
159 }
160 
161 static	__checkReturn			efx_rc_t
162 efx_mae_get_action_rule_caps(
163 	__in				efx_nic_t *enp,
164 	__in				unsigned int field_ncaps,
165 	__out_ecount(field_ncaps)	efx_mae_field_cap_t *field_caps)
166 {
167 	efx_mcdi_req_t req;
168 	EFX_MCDI_DECLARE_BUF(payload,
169 	    MC_CMD_MAE_GET_AR_CAPS_IN_LEN,
170 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2);
171 	unsigned int mcdi_field_ncaps;
172 	unsigned int i;
173 	efx_rc_t rc;
174 
175 	if (MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps) >
176 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2) {
177 		rc = EINVAL;
178 		goto fail1;
179 	}
180 
181 	req.emr_cmd = MC_CMD_MAE_GET_AR_CAPS;
182 	req.emr_in_buf = payload;
183 	req.emr_in_length = MC_CMD_MAE_GET_AR_CAPS_IN_LEN;
184 	req.emr_out_buf = payload;
185 	req.emr_out_length = MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps);
186 
187 	efx_mcdi_execute(enp, &req);
188 
189 	if (req.emr_rc != 0) {
190 		rc = req.emr_rc;
191 		goto fail2;
192 	}
193 
194 	mcdi_field_ncaps = MCDI_OUT_DWORD(req, MAE_GET_OR_CAPS_OUT_COUNT);
195 
196 	if (req.emr_out_length_used <
197 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(mcdi_field_ncaps)) {
198 		rc = EMSGSIZE;
199 		goto fail3;
200 	}
201 
202 	if (mcdi_field_ncaps > field_ncaps) {
203 		rc = EMSGSIZE;
204 		goto fail4;
205 	}
206 
207 	for (i = 0; i < mcdi_field_ncaps; ++i) {
208 		uint32_t match_flag;
209 		uint32_t mask_flag;
210 
211 		field_caps[i].emfc_support = MCDI_OUT_INDEXED_DWORD_FIELD(req,
212 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
213 		    MAE_FIELD_FLAGS_SUPPORT_STATUS);
214 
215 		match_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
216 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
217 		    MAE_FIELD_FLAGS_MATCH_AFFECTS_CLASS);
218 
219 		field_caps[i].emfc_match_affects_class =
220 		    (match_flag != 0) ? B_TRUE : B_FALSE;
221 
222 		mask_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
223 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
224 		    MAE_FIELD_FLAGS_MASK_AFFECTS_CLASS);
225 
226 		field_caps[i].emfc_mask_affects_class =
227 		    (mask_flag != 0) ? B_TRUE : B_FALSE;
228 	}
229 
230 	return (0);
231 
232 fail4:
233 	EFSYS_PROBE(fail4);
234 fail3:
235 	EFSYS_PROBE(fail3);
236 fail2:
237 	EFSYS_PROBE(fail2);
238 fail1:
239 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
240 	return (rc);
241 }
242 
243 	__checkReturn			efx_rc_t
244 efx_mae_init(
245 	__in				efx_nic_t *enp)
246 {
247 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
248 	efx_mae_field_cap_t *or_fcaps;
249 	size_t or_fcaps_size;
250 	efx_mae_field_cap_t *ar_fcaps;
251 	size_t ar_fcaps_size;
252 	efx_mae_t *maep;
253 	efx_rc_t rc;
254 
255 	if (encp->enc_mae_supported == B_FALSE) {
256 		rc = ENOTSUP;
257 		goto fail1;
258 	}
259 
260 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*maep), maep);
261 	if (maep == NULL) {
262 		rc = ENOMEM;
263 		goto fail2;
264 	}
265 
266 	enp->en_maep = maep;
267 
268 	rc = efx_mae_get_capabilities(enp);
269 	if (rc != 0)
270 		goto fail3;
271 
272 	or_fcaps_size = maep->em_max_nfields * sizeof (*or_fcaps);
273 	EFSYS_KMEM_ALLOC(enp->en_esip, or_fcaps_size, or_fcaps);
274 	if (or_fcaps == NULL) {
275 		rc = ENOMEM;
276 		goto fail4;
277 	}
278 
279 	maep->em_outer_rule_field_caps_size = or_fcaps_size;
280 	maep->em_outer_rule_field_caps = or_fcaps;
281 
282 	rc = efx_mae_get_outer_rule_caps(enp, maep->em_max_nfields, or_fcaps);
283 	if (rc != 0)
284 		goto fail5;
285 
286 	ar_fcaps_size = maep->em_max_nfields * sizeof (*ar_fcaps);
287 	EFSYS_KMEM_ALLOC(enp->en_esip, ar_fcaps_size, ar_fcaps);
288 	if (ar_fcaps == NULL) {
289 		rc = ENOMEM;
290 		goto fail6;
291 	}
292 
293 	maep->em_action_rule_field_caps_size = ar_fcaps_size;
294 	maep->em_action_rule_field_caps = ar_fcaps;
295 
296 	rc = efx_mae_get_action_rule_caps(enp, maep->em_max_nfields, ar_fcaps);
297 	if (rc != 0)
298 		goto fail7;
299 
300 	return (0);
301 
302 fail7:
303 	EFSYS_PROBE(fail5);
304 	EFSYS_KMEM_FREE(enp->en_esip, ar_fcaps_size, ar_fcaps);
305 fail6:
306 	EFSYS_PROBE(fail4);
307 fail5:
308 	EFSYS_PROBE(fail5);
309 	EFSYS_KMEM_FREE(enp->en_esip, or_fcaps_size, or_fcaps);
310 fail4:
311 	EFSYS_PROBE(fail4);
312 fail3:
313 	EFSYS_PROBE(fail3);
314 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (struct efx_mae_s), enp->en_maep);
315 	enp->en_maep = NULL;
316 fail2:
317 	EFSYS_PROBE(fail2);
318 fail1:
319 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
320 	return (rc);
321 }
322 
323 					void
324 efx_mae_fini(
325 	__in				efx_nic_t *enp)
326 {
327 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
328 	efx_mae_t *maep = enp->en_maep;
329 
330 	if (encp->enc_mae_supported == B_FALSE)
331 		return;
332 
333 	EFSYS_KMEM_FREE(enp->en_esip, maep->em_action_rule_field_caps_size,
334 	    maep->em_action_rule_field_caps);
335 	EFSYS_KMEM_FREE(enp->en_esip, maep->em_outer_rule_field_caps_size,
336 	    maep->em_outer_rule_field_caps);
337 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*maep), maep);
338 	enp->en_maep = NULL;
339 }
340 
341 	__checkReturn			efx_rc_t
342 efx_mae_get_limits(
343 	__in				efx_nic_t *enp,
344 	__out				efx_mae_limits_t *emlp)
345 {
346 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
347 	struct efx_mae_s *maep = enp->en_maep;
348 	efx_rc_t rc;
349 
350 	if (encp->enc_mae_supported == B_FALSE) {
351 		rc = ENOTSUP;
352 		goto fail1;
353 	}
354 
355 	emlp->eml_max_n_outer_prios = maep->em_max_n_outer_prios;
356 	emlp->eml_max_n_action_prios = maep->em_max_n_action_prios;
357 	emlp->eml_encap_types_supported = maep->em_encap_types_supported;
358 	emlp->eml_encap_header_size_limit =
359 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_MAXNUM_MCDI2;
360 
361 	return (0);
362 
363 fail1:
364 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
365 	return (rc);
366 }
367 
368 	__checkReturn			efx_rc_t
369 efx_mae_match_spec_init(
370 	__in				efx_nic_t *enp,
371 	__in				efx_mae_rule_type_t type,
372 	__in				uint32_t prio,
373 	__out				efx_mae_match_spec_t **specp)
374 {
375 	efx_mae_match_spec_t *spec;
376 	efx_rc_t rc;
377 
378 	switch (type) {
379 	case EFX_MAE_RULE_OUTER:
380 		break;
381 	case EFX_MAE_RULE_ACTION:
382 		break;
383 	default:
384 		rc = ENOTSUP;
385 		goto fail1;
386 	}
387 
388 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
389 	if (spec == NULL) {
390 		rc = ENOMEM;
391 		goto fail2;
392 	}
393 
394 	spec->emms_type = type;
395 	spec->emms_prio = prio;
396 
397 	*specp = spec;
398 
399 	return (0);
400 
401 fail2:
402 	EFSYS_PROBE(fail2);
403 fail1:
404 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
405 	return (rc);
406 }
407 
408 					void
409 efx_mae_match_spec_fini(
410 	__in				efx_nic_t *enp,
411 	__in				efx_mae_match_spec_t *spec)
412 {
413 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
414 }
415 
416 /* Named identifiers which are valid indices to efx_mae_field_cap_t */
417 typedef enum efx_mae_field_cap_id_e {
418 	EFX_MAE_FIELD_ID_INGRESS_MPORT_SELECTOR = MAE_FIELD_INGRESS_PORT,
419 	EFX_MAE_FIELD_ID_ETHER_TYPE_BE = MAE_FIELD_ETHER_TYPE,
420 	EFX_MAE_FIELD_ID_ETH_SADDR_BE = MAE_FIELD_ETH_SADDR,
421 	EFX_MAE_FIELD_ID_ETH_DADDR_BE = MAE_FIELD_ETH_DADDR,
422 	EFX_MAE_FIELD_ID_VLAN0_TCI_BE = MAE_FIELD_VLAN0_TCI,
423 	EFX_MAE_FIELD_ID_VLAN0_PROTO_BE = MAE_FIELD_VLAN0_PROTO,
424 	EFX_MAE_FIELD_ID_VLAN1_TCI_BE = MAE_FIELD_VLAN1_TCI,
425 	EFX_MAE_FIELD_ID_VLAN1_PROTO_BE = MAE_FIELD_VLAN1_PROTO,
426 	EFX_MAE_FIELD_ID_SRC_IP4_BE = MAE_FIELD_SRC_IP4,
427 	EFX_MAE_FIELD_ID_DST_IP4_BE = MAE_FIELD_DST_IP4,
428 	EFX_MAE_FIELD_ID_IP_PROTO = MAE_FIELD_IP_PROTO,
429 	EFX_MAE_FIELD_ID_IP_TOS = MAE_FIELD_IP_TOS,
430 	EFX_MAE_FIELD_ID_IP_TTL = MAE_FIELD_IP_TTL,
431 	EFX_MAE_FIELD_ID_SRC_IP6_BE = MAE_FIELD_SRC_IP6,
432 	EFX_MAE_FIELD_ID_DST_IP6_BE = MAE_FIELD_DST_IP6,
433 	EFX_MAE_FIELD_ID_L4_SPORT_BE = MAE_FIELD_L4_SPORT,
434 	EFX_MAE_FIELD_ID_L4_DPORT_BE = MAE_FIELD_L4_DPORT,
435 	EFX_MAE_FIELD_ID_TCP_FLAGS_BE = MAE_FIELD_TCP_FLAGS,
436 	EFX_MAE_FIELD_ID_ENC_ETHER_TYPE_BE = MAE_FIELD_ENC_ETHER_TYPE,
437 	EFX_MAE_FIELD_ID_ENC_ETH_SADDR_BE = MAE_FIELD_ENC_ETH_SADDR,
438 	EFX_MAE_FIELD_ID_ENC_ETH_DADDR_BE = MAE_FIELD_ENC_ETH_DADDR,
439 	EFX_MAE_FIELD_ID_ENC_VLAN0_TCI_BE = MAE_FIELD_ENC_VLAN0_TCI,
440 	EFX_MAE_FIELD_ID_ENC_VLAN0_PROTO_BE = MAE_FIELD_ENC_VLAN0_PROTO,
441 	EFX_MAE_FIELD_ID_ENC_VLAN1_TCI_BE = MAE_FIELD_ENC_VLAN1_TCI,
442 	EFX_MAE_FIELD_ID_ENC_VLAN1_PROTO_BE = MAE_FIELD_ENC_VLAN1_PROTO,
443 	EFX_MAE_FIELD_ID_ENC_SRC_IP4_BE = MAE_FIELD_ENC_SRC_IP4,
444 	EFX_MAE_FIELD_ID_ENC_DST_IP4_BE = MAE_FIELD_ENC_DST_IP4,
445 	EFX_MAE_FIELD_ID_ENC_IP_PROTO = MAE_FIELD_ENC_IP_PROTO,
446 	EFX_MAE_FIELD_ID_ENC_IP_TOS = MAE_FIELD_ENC_IP_TOS,
447 	EFX_MAE_FIELD_ID_ENC_IP_TTL = MAE_FIELD_ENC_IP_TTL,
448 	EFX_MAE_FIELD_ID_ENC_SRC_IP6_BE = MAE_FIELD_ENC_SRC_IP6,
449 	EFX_MAE_FIELD_ID_ENC_DST_IP6_BE = MAE_FIELD_ENC_DST_IP6,
450 	EFX_MAE_FIELD_ID_ENC_L4_SPORT_BE = MAE_FIELD_ENC_L4_SPORT,
451 	EFX_MAE_FIELD_ID_ENC_L4_DPORT_BE = MAE_FIELD_ENC_L4_DPORT,
452 	EFX_MAE_FIELD_ID_ENC_VNET_ID_BE = MAE_FIELD_ENC_VNET_ID,
453 	EFX_MAE_FIELD_ID_OUTER_RULE_ID = MAE_FIELD_OUTER_RULE_ID,
454 
455 	EFX_MAE_FIELD_CAP_NIDS
456 } efx_mae_field_cap_id_t;
457 
458 typedef enum efx_mae_field_endianness_e {
459 	EFX_MAE_FIELD_LE = 0,
460 	EFX_MAE_FIELD_BE,
461 
462 	EFX_MAE_FIELD_ENDIANNESS_NTYPES
463 } efx_mae_field_endianness_t;
464 
465 /*
466  * The following structure is a means to describe an MAE field.
467  * The information in it is meant to be used internally by
468  * APIs for addressing a given field in a mask-value pairs
469  * structure and for validation purposes.
470  *
471  * A field may have an alternative one. This structure
472  * has additional members to reference the alternative
473  * field's mask. See efx_mae_match_spec_is_valid().
474  */
475 typedef struct efx_mae_mv_desc_s {
476 	efx_mae_field_cap_id_t		emmd_field_cap_id;
477 
478 	size_t				emmd_value_size;
479 	size_t				emmd_value_offset;
480 	size_t				emmd_mask_size;
481 	size_t				emmd_mask_offset;
482 
483 	/*
484 	 * Having the alternative field's mask size set to 0
485 	 * means that there's no alternative field specified.
486 	 */
487 	size_t				emmd_alt_mask_size;
488 	size_t				emmd_alt_mask_offset;
489 
490 	/* Primary field and the alternative one are of the same endianness. */
491 	efx_mae_field_endianness_t	emmd_endianness;
492 } efx_mae_mv_desc_t;
493 
494 /* Indices to this array are provided by efx_mae_field_id_t */
495 static const efx_mae_mv_desc_t __efx_mae_action_rule_mv_desc_set[] = {
496 #define	EFX_MAE_MV_DESC(_name, _endianness)				\
497 	[EFX_MAE_FIELD_##_name] =					\
498 	{								\
499 		EFX_MAE_FIELD_ID_##_name,				\
500 		MAE_FIELD_MASK_VALUE_PAIRS_##_name##_LEN,		\
501 		MAE_FIELD_MASK_VALUE_PAIRS_##_name##_OFST,		\
502 		MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_LEN,		\
503 		MAE_FIELD_MASK_VALUE_PAIRS_##_name##_MASK_OFST,		\
504 		0, 0 /* no alternative field */,			\
505 		_endianness						\
506 	}
507 
508 	EFX_MAE_MV_DESC(INGRESS_MPORT_SELECTOR, EFX_MAE_FIELD_LE),
509 	EFX_MAE_MV_DESC(ETHER_TYPE_BE, EFX_MAE_FIELD_BE),
510 	EFX_MAE_MV_DESC(ETH_SADDR_BE, EFX_MAE_FIELD_BE),
511 	EFX_MAE_MV_DESC(ETH_DADDR_BE, EFX_MAE_FIELD_BE),
512 	EFX_MAE_MV_DESC(VLAN0_TCI_BE, EFX_MAE_FIELD_BE),
513 	EFX_MAE_MV_DESC(VLAN0_PROTO_BE, EFX_MAE_FIELD_BE),
514 	EFX_MAE_MV_DESC(VLAN1_TCI_BE, EFX_MAE_FIELD_BE),
515 	EFX_MAE_MV_DESC(VLAN1_PROTO_BE, EFX_MAE_FIELD_BE),
516 	EFX_MAE_MV_DESC(SRC_IP4_BE, EFX_MAE_FIELD_BE),
517 	EFX_MAE_MV_DESC(DST_IP4_BE, EFX_MAE_FIELD_BE),
518 	EFX_MAE_MV_DESC(IP_PROTO, EFX_MAE_FIELD_BE),
519 	EFX_MAE_MV_DESC(IP_TOS, EFX_MAE_FIELD_BE),
520 	EFX_MAE_MV_DESC(IP_TTL, EFX_MAE_FIELD_BE),
521 	EFX_MAE_MV_DESC(SRC_IP6_BE, EFX_MAE_FIELD_BE),
522 	EFX_MAE_MV_DESC(DST_IP6_BE, EFX_MAE_FIELD_BE),
523 	EFX_MAE_MV_DESC(L4_SPORT_BE, EFX_MAE_FIELD_BE),
524 	EFX_MAE_MV_DESC(L4_DPORT_BE, EFX_MAE_FIELD_BE),
525 	EFX_MAE_MV_DESC(TCP_FLAGS_BE, EFX_MAE_FIELD_BE),
526 	EFX_MAE_MV_DESC(ENC_VNET_ID_BE, EFX_MAE_FIELD_BE),
527 	EFX_MAE_MV_DESC(OUTER_RULE_ID, EFX_MAE_FIELD_LE),
528 
529 #undef EFX_MAE_MV_DESC
530 };
531 
532 /* Indices to this array are provided by efx_mae_field_id_t */
533 static const efx_mae_mv_desc_t __efx_mae_outer_rule_mv_desc_set[] = {
534 #define	EFX_MAE_MV_DESC(_name, _endianness)				\
535 	[EFX_MAE_FIELD_##_name] =					\
536 	{								\
537 		EFX_MAE_FIELD_ID_##_name,				\
538 		MAE_ENC_FIELD_PAIRS_##_name##_LEN,			\
539 		MAE_ENC_FIELD_PAIRS_##_name##_OFST,			\
540 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN,			\
541 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST,		\
542 		0, 0 /* no alternative field */,			\
543 		_endianness						\
544 	}
545 
546 /* Same as EFX_MAE_MV_DESC(), but also indicates an alternative field. */
547 #define	EFX_MAE_MV_DESC_ALT(_name, _alt_name, _endianness)		\
548 	[EFX_MAE_FIELD_##_name] =					\
549 	{								\
550 		EFX_MAE_FIELD_ID_##_name,				\
551 		MAE_ENC_FIELD_PAIRS_##_name##_LEN,			\
552 		MAE_ENC_FIELD_PAIRS_##_name##_OFST,			\
553 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN,			\
554 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST,		\
555 		MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_LEN,		\
556 		MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_OFST,		\
557 		_endianness						\
558 	}
559 
560 	EFX_MAE_MV_DESC(INGRESS_MPORT_SELECTOR, EFX_MAE_FIELD_LE),
561 	EFX_MAE_MV_DESC(ENC_ETHER_TYPE_BE, EFX_MAE_FIELD_BE),
562 	EFX_MAE_MV_DESC(ENC_ETH_SADDR_BE, EFX_MAE_FIELD_BE),
563 	EFX_MAE_MV_DESC(ENC_ETH_DADDR_BE, EFX_MAE_FIELD_BE),
564 	EFX_MAE_MV_DESC(ENC_VLAN0_TCI_BE, EFX_MAE_FIELD_BE),
565 	EFX_MAE_MV_DESC(ENC_VLAN0_PROTO_BE, EFX_MAE_FIELD_BE),
566 	EFX_MAE_MV_DESC(ENC_VLAN1_TCI_BE, EFX_MAE_FIELD_BE),
567 	EFX_MAE_MV_DESC(ENC_VLAN1_PROTO_BE, EFX_MAE_FIELD_BE),
568 	EFX_MAE_MV_DESC_ALT(ENC_SRC_IP4_BE, ENC_SRC_IP6_BE, EFX_MAE_FIELD_BE),
569 	EFX_MAE_MV_DESC_ALT(ENC_DST_IP4_BE, ENC_DST_IP6_BE, EFX_MAE_FIELD_BE),
570 	EFX_MAE_MV_DESC(ENC_IP_PROTO, EFX_MAE_FIELD_BE),
571 	EFX_MAE_MV_DESC(ENC_IP_TOS, EFX_MAE_FIELD_BE),
572 	EFX_MAE_MV_DESC(ENC_IP_TTL, EFX_MAE_FIELD_BE),
573 	EFX_MAE_MV_DESC_ALT(ENC_SRC_IP6_BE, ENC_SRC_IP4_BE, EFX_MAE_FIELD_BE),
574 	EFX_MAE_MV_DESC_ALT(ENC_DST_IP6_BE, ENC_DST_IP4_BE, EFX_MAE_FIELD_BE),
575 	EFX_MAE_MV_DESC(ENC_L4_SPORT_BE, EFX_MAE_FIELD_BE),
576 	EFX_MAE_MV_DESC(ENC_L4_DPORT_BE, EFX_MAE_FIELD_BE),
577 
578 #undef EFX_MAE_MV_DESC_ALT
579 #undef EFX_MAE_MV_DESC
580 };
581 
582 	__checkReturn			efx_rc_t
583 efx_mae_mport_by_phy_port(
584 	__in				uint32_t phy_port,
585 	__out				efx_mport_sel_t *mportp)
586 {
587 	efx_dword_t dword;
588 	efx_rc_t rc;
589 
590 	if (phy_port > EFX_MASK32(MAE_MPORT_SELECTOR_PPORT_ID)) {
591 		rc = EINVAL;
592 		goto fail1;
593 	}
594 
595 	EFX_POPULATE_DWORD_2(dword,
596 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
597 	    MAE_MPORT_SELECTOR_PPORT_ID, phy_port);
598 
599 	memset(mportp, 0, sizeof (*mportp));
600 	/*
601 	 * The constructed DWORD is little-endian,
602 	 * but the resulting value is meant to be
603 	 * passed to MCDIs, where it will undergo
604 	 * host-order to little endian conversion.
605 	 */
606 	mportp->sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
607 
608 	return (0);
609 
610 fail1:
611 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
612 	return (rc);
613 }
614 
615 	__checkReturn			efx_rc_t
616 efx_mae_mport_by_pcie_function(
617 	__in				uint32_t pf,
618 	__in				uint32_t vf,
619 	__out				efx_mport_sel_t *mportp)
620 {
621 	efx_dword_t dword;
622 	efx_rc_t rc;
623 
624 	EFX_STATIC_ASSERT(EFX_PCI_VF_INVALID ==
625 	    MAE_MPORT_SELECTOR_FUNC_VF_ID_NULL);
626 
627 	if (pf > EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_PF_ID)) {
628 		rc = EINVAL;
629 		goto fail1;
630 	}
631 
632 	if (vf > EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_VF_ID)) {
633 		rc = EINVAL;
634 		goto fail2;
635 	}
636 
637 	EFX_POPULATE_DWORD_3(dword,
638 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
639 	    MAE_MPORT_SELECTOR_FUNC_PF_ID, pf,
640 	    MAE_MPORT_SELECTOR_FUNC_VF_ID, vf);
641 
642 	memset(mportp, 0, sizeof (*mportp));
643 	/*
644 	 * The constructed DWORD is little-endian,
645 	 * but the resulting value is meant to be
646 	 * passed to MCDIs, where it will undergo
647 	 * host-order to little endian conversion.
648 	 */
649 	mportp->sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
650 
651 	return (0);
652 
653 fail2:
654 	EFSYS_PROBE(fail2);
655 fail1:
656 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
657 	return (rc);
658 }
659 
660 	__checkReturn			efx_rc_t
661 efx_mae_match_spec_field_set(
662 	__in				efx_mae_match_spec_t *spec,
663 	__in				efx_mae_field_id_t field_id,
664 	__in				size_t value_size,
665 	__in_bcount(value_size)		const uint8_t *value,
666 	__in				size_t mask_size,
667 	__in_bcount(mask_size)		const uint8_t *mask)
668 {
669 	const efx_mae_mv_desc_t *descp;
670 	unsigned int desc_set_nentries;
671 	uint8_t *mvp;
672 	efx_rc_t rc;
673 
674 	switch (spec->emms_type) {
675 	case EFX_MAE_RULE_OUTER:
676 		desc_set_nentries =
677 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
678 		descp = &__efx_mae_outer_rule_mv_desc_set[field_id];
679 		mvp = spec->emms_mask_value_pairs.outer;
680 		break;
681 	case EFX_MAE_RULE_ACTION:
682 		desc_set_nentries =
683 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
684 		descp = &__efx_mae_action_rule_mv_desc_set[field_id];
685 		mvp = spec->emms_mask_value_pairs.action;
686 		break;
687 	default:
688 		rc = ENOTSUP;
689 		goto fail1;
690 	}
691 
692 	if ((unsigned int)field_id >= desc_set_nentries) {
693 		rc = EINVAL;
694 		goto fail2;
695 	}
696 
697 	if (descp->emmd_mask_size == 0) {
698 		/* The ID points to a gap in the array of field descriptors. */
699 		rc = EINVAL;
700 		goto fail3;
701 	}
702 
703 	if (value_size != descp->emmd_value_size) {
704 		rc = EINVAL;
705 		goto fail4;
706 	}
707 
708 	if (mask_size != descp->emmd_mask_size) {
709 		rc = EINVAL;
710 		goto fail5;
711 	}
712 
713 	if (descp->emmd_endianness == EFX_MAE_FIELD_BE) {
714 		unsigned int i;
715 
716 		/*
717 		 * The mask/value are in network (big endian) order.
718 		 * The MCDI request field is also big endian.
719 		 */
720 
721 		EFSYS_ASSERT3U(value_size, ==, mask_size);
722 
723 		for (i = 0; i < value_size; ++i) {
724 			uint8_t *v_bytep = mvp + descp->emmd_value_offset + i;
725 			uint8_t *m_bytep = mvp + descp->emmd_mask_offset + i;
726 
727 			/*
728 			 * Apply the mask (which may be all-zeros) to the value.
729 			 *
730 			 * If this API is provided with some value to set for a
731 			 * given field in one specification and with some other
732 			 * value to set for this field in another specification,
733 			 * then, if the two masks are all-zeros, the field will
734 			 * avoid being counted as a mismatch when comparing the
735 			 * specifications using efx_mae_match_specs_equal() API.
736 			 */
737 			*v_bytep = value[i] & mask[i];
738 			*m_bytep = mask[i];
739 		}
740 	} else {
741 		efx_dword_t dword;
742 
743 		/*
744 		 * The mask/value are in host byte order.
745 		 * The MCDI request field is little endian.
746 		 */
747 		switch (value_size) {
748 		case 4:
749 			EFX_POPULATE_DWORD_1(dword,
750 			    EFX_DWORD_0, *(const uint32_t *)value);
751 
752 			memcpy(mvp + descp->emmd_value_offset,
753 			    &dword, sizeof (dword));
754 			break;
755 		default:
756 			EFSYS_ASSERT(B_FALSE);
757 		}
758 
759 		switch (mask_size) {
760 		case 4:
761 			EFX_POPULATE_DWORD_1(dword,
762 			    EFX_DWORD_0, *(const uint32_t *)mask);
763 
764 			memcpy(mvp + descp->emmd_mask_offset,
765 			    &dword, sizeof (dword));
766 			break;
767 		default:
768 			EFSYS_ASSERT(B_FALSE);
769 		}
770 	}
771 
772 	return (0);
773 
774 fail5:
775 	EFSYS_PROBE(fail5);
776 fail4:
777 	EFSYS_PROBE(fail4);
778 fail3:
779 	EFSYS_PROBE(fail3);
780 fail2:
781 	EFSYS_PROBE(fail2);
782 fail1:
783 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
784 	return (rc);
785 }
786 
787 	__checkReturn			efx_rc_t
788 efx_mae_match_spec_mport_set(
789 	__in				efx_mae_match_spec_t *spec,
790 	__in				const efx_mport_sel_t *valuep,
791 	__in_opt			const efx_mport_sel_t *maskp)
792 {
793 	uint32_t full_mask = UINT32_MAX;
794 	const uint8_t *vp;
795 	const uint8_t *mp;
796 	efx_rc_t rc;
797 
798 	if (valuep == NULL) {
799 		rc = EINVAL;
800 		goto fail1;
801 	}
802 
803 	vp = (const uint8_t *)&valuep->sel;
804 	if (maskp != NULL)
805 		mp = (const uint8_t *)&maskp->sel;
806 	else
807 		mp = (const uint8_t *)&full_mask;
808 
809 	rc = efx_mae_match_spec_field_set(spec,
810 	    EFX_MAE_FIELD_INGRESS_MPORT_SELECTOR,
811 	    sizeof (valuep->sel), vp, sizeof (maskp->sel), mp);
812 	if (rc != 0)
813 		goto fail2;
814 
815 	return (0);
816 
817 fail2:
818 	EFSYS_PROBE(fail2);
819 fail1:
820 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
821 	return (rc);
822 }
823 
824 	__checkReturn			boolean_t
825 efx_mae_match_specs_equal(
826 	__in				const efx_mae_match_spec_t *left,
827 	__in				const efx_mae_match_spec_t *right)
828 {
829 	return ((memcmp(left, right, sizeof (*left)) == 0) ? B_TRUE : B_FALSE);
830 }
831 
832 #define	EFX_MASK_BIT_IS_SET(_mask, _mask_page_nbits, _bit)		\
833 	    ((_mask)[(_bit) / (_mask_page_nbits)] &			\
834 		    (1ULL << ((_bit) & ((_mask_page_nbits) - 1))))
835 
836 static					boolean_t
837 efx_mask_is_prefix(
838 	__in				size_t mask_nbytes,
839 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
840 {
841 	boolean_t prev_bit_is_set = B_TRUE;
842 	unsigned int i;
843 
844 	for (i = 0; i < 8 * mask_nbytes; ++i) {
845 		boolean_t bit_is_set = EFX_MASK_BIT_IS_SET(maskp, 8, i);
846 
847 		if (!prev_bit_is_set && bit_is_set)
848 			return B_FALSE;
849 
850 		prev_bit_is_set = bit_is_set;
851 	}
852 
853 	return B_TRUE;
854 }
855 
856 static					boolean_t
857 efx_mask_is_all_ones(
858 	__in				size_t mask_nbytes,
859 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
860 {
861 	unsigned int i;
862 	uint8_t t = ~0;
863 
864 	for (i = 0; i < mask_nbytes; ++i)
865 		t &= maskp[i];
866 
867 	return (t == (uint8_t)(~0));
868 }
869 
870 static					boolean_t
871 efx_mask_is_all_zeros(
872 	__in				size_t mask_nbytes,
873 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
874 {
875 	unsigned int i;
876 	uint8_t t = 0;
877 
878 	for (i = 0; i < mask_nbytes; ++i)
879 		t |= maskp[i];
880 
881 	return (t == 0);
882 }
883 
884 	__checkReturn			boolean_t
885 efx_mae_match_spec_is_valid(
886 	__in				efx_nic_t *enp,
887 	__in				const efx_mae_match_spec_t *spec)
888 {
889 	efx_mae_t *maep = enp->en_maep;
890 	unsigned int field_ncaps = maep->em_max_nfields;
891 	const efx_mae_field_cap_t *field_caps;
892 	const efx_mae_mv_desc_t *desc_setp;
893 	unsigned int desc_set_nentries;
894 	boolean_t is_valid = B_TRUE;
895 	efx_mae_field_id_t field_id;
896 	const uint8_t *mvp;
897 
898 	switch (spec->emms_type) {
899 	case EFX_MAE_RULE_OUTER:
900 		field_caps = maep->em_outer_rule_field_caps;
901 		desc_setp = __efx_mae_outer_rule_mv_desc_set;
902 		desc_set_nentries =
903 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
904 		mvp = spec->emms_mask_value_pairs.outer;
905 		break;
906 	case EFX_MAE_RULE_ACTION:
907 		field_caps = maep->em_action_rule_field_caps;
908 		desc_setp = __efx_mae_action_rule_mv_desc_set;
909 		desc_set_nentries =
910 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
911 		mvp = spec->emms_mask_value_pairs.action;
912 		break;
913 	default:
914 		return (B_FALSE);
915 	}
916 
917 	if (field_caps == NULL)
918 		return (B_FALSE);
919 
920 	for (field_id = 0; (unsigned int)field_id < desc_set_nentries;
921 	     ++field_id) {
922 		const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
923 		efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
924 		const uint8_t *alt_m_buf = mvp + descp->emmd_alt_mask_offset;
925 		const uint8_t *m_buf = mvp + descp->emmd_mask_offset;
926 		size_t alt_m_size = descp->emmd_alt_mask_size;
927 		size_t m_size = descp->emmd_mask_size;
928 
929 		if (m_size == 0)
930 			continue; /* Skip array gap */
931 
932 		if ((unsigned int)field_cap_id >= field_ncaps) {
933 			/*
934 			 * The FW has not reported capability status for
935 			 * this field. Make sure that its mask is zeroed.
936 			 */
937 			is_valid = efx_mask_is_all_zeros(m_size, m_buf);
938 			if (is_valid != B_FALSE)
939 				continue;
940 			else
941 				break;
942 		}
943 
944 		switch (field_caps[field_cap_id].emfc_support) {
945 		case MAE_FIELD_SUPPORTED_MATCH_MASK:
946 			is_valid = B_TRUE;
947 			break;
948 		case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
949 			is_valid = efx_mask_is_prefix(m_size, m_buf);
950 			break;
951 		case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
952 			is_valid = (efx_mask_is_all_ones(m_size, m_buf) ||
953 			    efx_mask_is_all_zeros(m_size, m_buf));
954 			break;
955 		case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
956 			is_valid = efx_mask_is_all_ones(m_size, m_buf);
957 
958 			if ((is_valid == B_FALSE) && (alt_m_size != 0)) {
959 				/*
960 				 * This field has an alternative one. The FW
961 				 * reports ALWAYS for both implying that one
962 				 * of them is required to have all-ones mask.
963 				 *
964 				 * The primary field's mask is incorrect; go
965 				 * on to check that of the alternative field.
966 				 */
967 				is_valid = efx_mask_is_all_ones(alt_m_size,
968 								alt_m_buf);
969 			}
970 			break;
971 		case MAE_FIELD_SUPPORTED_MATCH_NEVER:
972 		case MAE_FIELD_UNSUPPORTED:
973 		default:
974 			is_valid = efx_mask_is_all_zeros(m_size, m_buf);
975 			break;
976 		}
977 
978 		if (is_valid == B_FALSE)
979 			break;
980 	}
981 
982 	return (is_valid);
983 }
984 
985 	__checkReturn			efx_rc_t
986 efx_mae_action_set_spec_init(
987 	__in				efx_nic_t *enp,
988 	__out				efx_mae_actions_t **specp)
989 {
990 	efx_mae_actions_t *spec;
991 	efx_rc_t rc;
992 
993 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
994 	if (spec == NULL) {
995 		rc = ENOMEM;
996 		goto fail1;
997 	}
998 
999 	spec->ema_rsrc.emar_eh_id.id = EFX_MAE_RSRC_ID_INVALID;
1000 
1001 	*specp = spec;
1002 
1003 	return (0);
1004 
1005 fail1:
1006 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1007 	return (rc);
1008 }
1009 
1010 					void
1011 efx_mae_action_set_spec_fini(
1012 	__in				efx_nic_t *enp,
1013 	__in				efx_mae_actions_t *spec)
1014 {
1015 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
1016 }
1017 
1018 static	__checkReturn			efx_rc_t
1019 efx_mae_action_set_add_decap(
1020 	__in				efx_mae_actions_t *spec,
1021 	__in				size_t arg_size,
1022 	__in_bcount(arg_size)		const uint8_t *arg)
1023 {
1024 	efx_rc_t rc;
1025 
1026 	_NOTE(ARGUNUSED(spec))
1027 
1028 	if (arg_size != 0) {
1029 		rc = EINVAL;
1030 		goto fail1;
1031 	}
1032 
1033 	if (arg != NULL) {
1034 		rc = EINVAL;
1035 		goto fail2;
1036 	}
1037 
1038 	/* This action does not have any arguments, so do nothing here. */
1039 
1040 	return (0);
1041 
1042 fail2:
1043 	EFSYS_PROBE(fail2);
1044 fail1:
1045 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1046 	return (rc);
1047 }
1048 
1049 static	__checkReturn			efx_rc_t
1050 efx_mae_action_set_add_vlan_pop(
1051 	__in				efx_mae_actions_t *spec,
1052 	__in				size_t arg_size,
1053 	__in_bcount(arg_size)		const uint8_t *arg)
1054 {
1055 	efx_rc_t rc;
1056 
1057 	if (arg_size != 0) {
1058 		rc = EINVAL;
1059 		goto fail1;
1060 	}
1061 
1062 	if (arg != NULL) {
1063 		rc = EINVAL;
1064 		goto fail2;
1065 	}
1066 
1067 	if (spec->ema_n_vlan_tags_to_pop == EFX_MAE_VLAN_POP_MAX_NTAGS) {
1068 		rc = ENOTSUP;
1069 		goto fail3;
1070 	}
1071 
1072 	++spec->ema_n_vlan_tags_to_pop;
1073 
1074 	return (0);
1075 
1076 fail3:
1077 	EFSYS_PROBE(fail3);
1078 fail2:
1079 	EFSYS_PROBE(fail2);
1080 fail1:
1081 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1082 	return (rc);
1083 }
1084 
1085 static	__checkReturn			efx_rc_t
1086 efx_mae_action_set_add_vlan_push(
1087 	__in				efx_mae_actions_t *spec,
1088 	__in				size_t arg_size,
1089 	__in_bcount(arg_size)		const uint8_t *arg)
1090 {
1091 	unsigned int n_tags = spec->ema_n_vlan_tags_to_push;
1092 	efx_rc_t rc;
1093 
1094 	if (arg_size != sizeof (*spec->ema_vlan_push_descs)) {
1095 		rc = EINVAL;
1096 		goto fail1;
1097 	}
1098 
1099 	if (arg == NULL) {
1100 		rc = EINVAL;
1101 		goto fail2;
1102 	}
1103 
1104 	if (n_tags == EFX_MAE_VLAN_PUSH_MAX_NTAGS) {
1105 		rc = ENOTSUP;
1106 		goto fail3;
1107 	}
1108 
1109 	memcpy(&spec->ema_vlan_push_descs[n_tags], arg, arg_size);
1110 	++(spec->ema_n_vlan_tags_to_push);
1111 
1112 	return (0);
1113 
1114 fail3:
1115 	EFSYS_PROBE(fail3);
1116 fail2:
1117 	EFSYS_PROBE(fail2);
1118 fail1:
1119 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1120 	return (rc);
1121 }
1122 
1123 static	__checkReturn			efx_rc_t
1124 efx_mae_action_set_add_encap(
1125 	__in				efx_mae_actions_t *spec,
1126 	__in				size_t arg_size,
1127 	__in_bcount(arg_size)		const uint8_t *arg)
1128 {
1129 	efx_rc_t rc;
1130 
1131 	/*
1132 	 * Adding this specific action to an action set spec and setting encap.
1133 	 * header ID in the spec are two individual steps. This design allows
1134 	 * the client driver to avoid encap. header allocation when it simply
1135 	 * needs to check the order of actions submitted by user ("validate"),
1136 	 * without actually allocating an action set and inserting a rule.
1137 	 *
1138 	 * For now, mark encap. header ID as invalid; the caller will invoke
1139 	 * efx_mae_action_set_fill_in_eh_id() to override the field prior
1140 	 * to action set allocation; otherwise, the allocation will fail.
1141 	 */
1142 	spec->ema_rsrc.emar_eh_id.id = EFX_MAE_RSRC_ID_INVALID;
1143 
1144 	/*
1145 	 * As explained above, there are no arguments to handle here.
1146 	 * efx_mae_action_set_fill_in_eh_id() will take care of them.
1147 	 */
1148 	if (arg_size != 0) {
1149 		rc = EINVAL;
1150 		goto fail1;
1151 	}
1152 
1153 	if (arg != NULL) {
1154 		rc = EINVAL;
1155 		goto fail2;
1156 	}
1157 
1158 	return (0);
1159 
1160 fail2:
1161 	EFSYS_PROBE(fail2);
1162 fail1:
1163 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1164 	return (rc);
1165 }
1166 
1167 static	__checkReturn			efx_rc_t
1168 efx_mae_action_set_add_flag(
1169 	__in				efx_mae_actions_t *spec,
1170 	__in				size_t arg_size,
1171 	__in_bcount(arg_size)		const uint8_t *arg)
1172 {
1173 	efx_rc_t rc;
1174 
1175 	_NOTE(ARGUNUSED(spec))
1176 
1177 	if (arg_size != 0) {
1178 		rc = EINVAL;
1179 		goto fail1;
1180 	}
1181 
1182 	if (arg != NULL) {
1183 		rc = EINVAL;
1184 		goto fail2;
1185 	}
1186 
1187 	/* This action does not have any arguments, so do nothing here. */
1188 
1189 	return (0);
1190 
1191 fail2:
1192 	EFSYS_PROBE(fail2);
1193 fail1:
1194 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1195 	return (rc);
1196 }
1197 
1198 static	__checkReturn			efx_rc_t
1199 efx_mae_action_set_add_mark(
1200 	__in				efx_mae_actions_t *spec,
1201 	__in				size_t arg_size,
1202 	__in_bcount(arg_size)		const uint8_t *arg)
1203 {
1204 	efx_rc_t rc;
1205 
1206 	if (arg_size != sizeof (spec->ema_mark_value)) {
1207 		rc = EINVAL;
1208 		goto fail1;
1209 	}
1210 
1211 	if (arg == NULL) {
1212 		rc = EINVAL;
1213 		goto fail2;
1214 	}
1215 
1216 	memcpy(&spec->ema_mark_value, arg, arg_size);
1217 
1218 	return (0);
1219 
1220 fail2:
1221 	EFSYS_PROBE(fail2);
1222 fail1:
1223 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1224 	return (rc);
1225 }
1226 
1227 static	__checkReturn			efx_rc_t
1228 efx_mae_action_set_add_deliver(
1229 	__in				efx_mae_actions_t *spec,
1230 	__in				size_t arg_size,
1231 	__in_bcount(arg_size)		const uint8_t *arg)
1232 {
1233 	efx_rc_t rc;
1234 
1235 	if (arg_size != sizeof (spec->ema_deliver_mport)) {
1236 		rc = EINVAL;
1237 		goto fail1;
1238 	}
1239 
1240 	if (arg == NULL) {
1241 		rc = EINVAL;
1242 		goto fail2;
1243 	}
1244 
1245 	memcpy(&spec->ema_deliver_mport, arg, arg_size);
1246 
1247 	return (0);
1248 
1249 fail2:
1250 	EFSYS_PROBE(fail2);
1251 fail1:
1252 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1253 	return (rc);
1254 }
1255 
1256 typedef struct efx_mae_action_desc_s {
1257 	/* Action specific handler */
1258 	efx_rc_t	(*emad_add)(efx_mae_actions_t *,
1259 				    size_t, const uint8_t *);
1260 } efx_mae_action_desc_t;
1261 
1262 static const efx_mae_action_desc_t efx_mae_actions[EFX_MAE_NACTIONS] = {
1263 	[EFX_MAE_ACTION_DECAP] = {
1264 		.emad_add = efx_mae_action_set_add_decap
1265 	},
1266 	[EFX_MAE_ACTION_VLAN_POP] = {
1267 		.emad_add = efx_mae_action_set_add_vlan_pop
1268 	},
1269 	[EFX_MAE_ACTION_VLAN_PUSH] = {
1270 		.emad_add = efx_mae_action_set_add_vlan_push
1271 	},
1272 	[EFX_MAE_ACTION_ENCAP] = {
1273 		.emad_add = efx_mae_action_set_add_encap
1274 	},
1275 	[EFX_MAE_ACTION_FLAG] = {
1276 		.emad_add = efx_mae_action_set_add_flag
1277 	},
1278 	[EFX_MAE_ACTION_MARK] = {
1279 		.emad_add = efx_mae_action_set_add_mark
1280 	},
1281 	[EFX_MAE_ACTION_DELIVER] = {
1282 		.emad_add = efx_mae_action_set_add_deliver
1283 	}
1284 };
1285 
1286 static const uint32_t efx_mae_action_ordered_map =
1287 	(1U << EFX_MAE_ACTION_DECAP) |
1288 	(1U << EFX_MAE_ACTION_VLAN_POP) |
1289 	(1U << EFX_MAE_ACTION_VLAN_PUSH) |
1290 	(1U << EFX_MAE_ACTION_ENCAP) |
1291 	(1U << EFX_MAE_ACTION_FLAG) |
1292 	(1U << EFX_MAE_ACTION_MARK) |
1293 	(1U << EFX_MAE_ACTION_DELIVER);
1294 
1295 /*
1296  * These actions must not be added after DELIVER, but
1297  * they can have any place among the rest of
1298  * strictly ordered actions.
1299  */
1300 static const uint32_t efx_mae_action_nonstrict_map =
1301 	(1U << EFX_MAE_ACTION_FLAG) |
1302 	(1U << EFX_MAE_ACTION_MARK);
1303 
1304 static const uint32_t efx_mae_action_repeat_map =
1305 	(1U << EFX_MAE_ACTION_VLAN_POP) |
1306 	(1U << EFX_MAE_ACTION_VLAN_PUSH);
1307 
1308 /*
1309  * Add an action to an action set.
1310  *
1311  * This has to be invoked in the desired action order.
1312  * An out-of-order action request will be turned down.
1313  */
1314 static	__checkReturn			efx_rc_t
1315 efx_mae_action_set_spec_populate(
1316 	__in				efx_mae_actions_t *spec,
1317 	__in				efx_mae_action_t type,
1318 	__in				size_t arg_size,
1319 	__in_bcount(arg_size)		const uint8_t *arg)
1320 {
1321 	uint32_t action_mask;
1322 	efx_rc_t rc;
1323 
1324 	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
1325 	    (sizeof (efx_mae_action_ordered_map) * 8));
1326 	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
1327 	    (sizeof (efx_mae_action_repeat_map) * 8));
1328 
1329 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_DELIVER + 1 == EFX_MAE_NACTIONS);
1330 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_FLAG + 1 == EFX_MAE_ACTION_MARK);
1331 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_MARK + 1 == EFX_MAE_ACTION_DELIVER);
1332 
1333 	if (type >= EFX_ARRAY_SIZE(efx_mae_actions)) {
1334 		rc = EINVAL;
1335 		goto fail1;
1336 	}
1337 
1338 	action_mask = (1U << type);
1339 
1340 	if ((spec->ema_actions & action_mask) != 0) {
1341 		/* The action set already contains this action. */
1342 		if ((efx_mae_action_repeat_map & action_mask) == 0) {
1343 			/* Cannot add another non-repeatable action. */
1344 			rc = ENOTSUP;
1345 			goto fail2;
1346 		}
1347 	}
1348 
1349 	if ((efx_mae_action_ordered_map & action_mask) != 0) {
1350 		uint32_t strict_ordered_map =
1351 		    efx_mae_action_ordered_map & ~efx_mae_action_nonstrict_map;
1352 		uint32_t later_actions_mask =
1353 		    strict_ordered_map & ~(action_mask | (action_mask - 1));
1354 
1355 		if ((spec->ema_actions & later_actions_mask) != 0) {
1356 			/* Cannot add an action after later ordered actions. */
1357 			rc = ENOTSUP;
1358 			goto fail3;
1359 		}
1360 	}
1361 
1362 	if (efx_mae_actions[type].emad_add != NULL) {
1363 		rc = efx_mae_actions[type].emad_add(spec, arg_size, arg);
1364 		if (rc != 0)
1365 			goto fail4;
1366 	}
1367 
1368 	spec->ema_actions |= action_mask;
1369 
1370 	return (0);
1371 
1372 fail4:
1373 	EFSYS_PROBE(fail4);
1374 fail3:
1375 	EFSYS_PROBE(fail3);
1376 fail2:
1377 	EFSYS_PROBE(fail2);
1378 fail1:
1379 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1380 	return (rc);
1381 }
1382 
1383 	__checkReturn			efx_rc_t
1384 efx_mae_action_set_populate_decap(
1385 	__in				efx_mae_actions_t *spec)
1386 {
1387 	return (efx_mae_action_set_spec_populate(spec,
1388 	    EFX_MAE_ACTION_DECAP, 0, NULL));
1389 }
1390 
1391 	__checkReturn			efx_rc_t
1392 efx_mae_action_set_populate_vlan_pop(
1393 	__in				efx_mae_actions_t *spec)
1394 {
1395 	return (efx_mae_action_set_spec_populate(spec,
1396 	    EFX_MAE_ACTION_VLAN_POP, 0, NULL));
1397 }
1398 
1399 	__checkReturn			efx_rc_t
1400 efx_mae_action_set_populate_vlan_push(
1401 	__in				efx_mae_actions_t *spec,
1402 	__in				uint16_t tpid_be,
1403 	__in				uint16_t tci_be)
1404 {
1405 	efx_mae_action_vlan_push_t action;
1406 	const uint8_t *arg = (const uint8_t *)&action;
1407 
1408 	action.emavp_tpid_be = tpid_be;
1409 	action.emavp_tci_be = tci_be;
1410 
1411 	return (efx_mae_action_set_spec_populate(spec,
1412 	    EFX_MAE_ACTION_VLAN_PUSH, sizeof (action), arg));
1413 }
1414 
1415 	__checkReturn			efx_rc_t
1416 efx_mae_action_set_populate_encap(
1417 	__in				efx_mae_actions_t *spec)
1418 {
1419 	/*
1420 	 * There is no argument to pass encap. header ID, thus, one does not
1421 	 * need to allocate an encap. header while parsing application input.
1422 	 * This is useful since building an action set may be done simply to
1423 	 * validate a rule, whilst resource allocation usually consumes time.
1424 	 */
1425 	return (efx_mae_action_set_spec_populate(spec,
1426 	    EFX_MAE_ACTION_ENCAP, 0, NULL));
1427 }
1428 
1429 	__checkReturn			efx_rc_t
1430 efx_mae_action_set_populate_flag(
1431 	__in				efx_mae_actions_t *spec)
1432 {
1433 	return (efx_mae_action_set_spec_populate(spec,
1434 	    EFX_MAE_ACTION_FLAG, 0, NULL));
1435 }
1436 
1437 	__checkReturn			efx_rc_t
1438 efx_mae_action_set_populate_mark(
1439 	__in				efx_mae_actions_t *spec,
1440 	__in				uint32_t mark_value)
1441 {
1442 	const uint8_t *arg = (const uint8_t *)&mark_value;
1443 
1444 	return (efx_mae_action_set_spec_populate(spec,
1445 	    EFX_MAE_ACTION_MARK, sizeof (mark_value), arg));
1446 }
1447 
1448 	__checkReturn			efx_rc_t
1449 efx_mae_action_set_populate_deliver(
1450 	__in				efx_mae_actions_t *spec,
1451 	__in				const efx_mport_sel_t *mportp)
1452 {
1453 	const uint8_t *arg;
1454 	efx_rc_t rc;
1455 
1456 	if (mportp == NULL) {
1457 		rc = EINVAL;
1458 		goto fail1;
1459 	}
1460 
1461 	arg = (const uint8_t *)&mportp->sel;
1462 
1463 	return (efx_mae_action_set_spec_populate(spec,
1464 	    EFX_MAE_ACTION_DELIVER, sizeof (mportp->sel), arg));
1465 
1466 fail1:
1467 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1468 	return (rc);
1469 }
1470 
1471 	__checkReturn			efx_rc_t
1472 efx_mae_action_set_populate_drop(
1473 	__in				efx_mae_actions_t *spec)
1474 {
1475 	efx_mport_sel_t mport;
1476 	const uint8_t *arg;
1477 	efx_dword_t dword;
1478 
1479 	EFX_POPULATE_DWORD_1(dword,
1480 	    MAE_MPORT_SELECTOR_FLAT, MAE_MPORT_SELECTOR_NULL);
1481 
1482 	/*
1483 	 * The constructed DWORD is little-endian,
1484 	 * but the resulting value is meant to be
1485 	 * passed to MCDIs, where it will undergo
1486 	 * host-order to little endian conversion.
1487 	 */
1488 	mport.sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
1489 
1490 	arg = (const uint8_t *)&mport.sel;
1491 
1492 	return (efx_mae_action_set_spec_populate(spec,
1493 	    EFX_MAE_ACTION_DELIVER, sizeof (mport.sel), arg));
1494 }
1495 
1496 	__checkReturn			boolean_t
1497 efx_mae_action_set_specs_equal(
1498 	__in				const efx_mae_actions_t *left,
1499 	__in				const efx_mae_actions_t *right)
1500 {
1501 	size_t cmp_size = EFX_FIELD_OFFSET(efx_mae_actions_t, ema_rsrc);
1502 
1503 	/*
1504 	 * An action set specification consists of two parts. The first part
1505 	 * indicates what actions are included in the action set, as well as
1506 	 * extra quantitative values (in example, the number of VLAN tags to
1507 	 * push). The second part comprises resource IDs used by the actions.
1508 	 *
1509 	 * A resource, in example, a counter, is allocated from the hardware
1510 	 * by the client, and it's the client who is responsible for keeping
1511 	 * track of allocated resources and comparing resource IDs if needed.
1512 	 *
1513 	 * In this API, don't compare resource IDs in the two specifications.
1514 	 */
1515 
1516 	return ((memcmp(left, right, cmp_size) == 0) ? B_TRUE : B_FALSE);
1517 }
1518 
1519 	__checkReturn			efx_rc_t
1520 efx_mae_match_specs_class_cmp(
1521 	__in				efx_nic_t *enp,
1522 	__in				const efx_mae_match_spec_t *left,
1523 	__in				const efx_mae_match_spec_t *right,
1524 	__out				boolean_t *have_same_classp)
1525 {
1526 	efx_mae_t *maep = enp->en_maep;
1527 	unsigned int field_ncaps = maep->em_max_nfields;
1528 	const efx_mae_field_cap_t *field_caps;
1529 	const efx_mae_mv_desc_t *desc_setp;
1530 	unsigned int desc_set_nentries;
1531 	boolean_t have_same_class = B_TRUE;
1532 	efx_mae_field_id_t field_id;
1533 	const uint8_t *mvpl;
1534 	const uint8_t *mvpr;
1535 	efx_rc_t rc;
1536 
1537 	switch (left->emms_type) {
1538 	case EFX_MAE_RULE_OUTER:
1539 		field_caps = maep->em_outer_rule_field_caps;
1540 		desc_setp = __efx_mae_outer_rule_mv_desc_set;
1541 		desc_set_nentries =
1542 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
1543 		mvpl = left->emms_mask_value_pairs.outer;
1544 		mvpr = right->emms_mask_value_pairs.outer;
1545 		break;
1546 	case EFX_MAE_RULE_ACTION:
1547 		field_caps = maep->em_action_rule_field_caps;
1548 		desc_setp = __efx_mae_action_rule_mv_desc_set;
1549 		desc_set_nentries =
1550 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
1551 		mvpl = left->emms_mask_value_pairs.action;
1552 		mvpr = right->emms_mask_value_pairs.action;
1553 		break;
1554 	default:
1555 		rc = ENOTSUP;
1556 		goto fail1;
1557 	}
1558 
1559 	if (field_caps == NULL) {
1560 		rc = EAGAIN;
1561 		goto fail2;
1562 	}
1563 
1564 	if (left->emms_type != right->emms_type ||
1565 	    left->emms_prio != right->emms_prio) {
1566 		/*
1567 		 * Rules of different types can never map to the same class.
1568 		 *
1569 		 * The FW can support some set of match criteria for one
1570 		 * priority and not support the very same set for
1571 		 * another priority. Thus, two rules which have
1572 		 * different priorities can never map to
1573 		 * the same class.
1574 		 */
1575 		*have_same_classp = B_FALSE;
1576 		return (0);
1577 	}
1578 
1579 	for (field_id = 0; (unsigned int)field_id < desc_set_nentries;
1580 	     ++field_id) {
1581 		const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
1582 		efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
1583 		const uint8_t *lmaskp = mvpl + descp->emmd_mask_offset;
1584 		const uint8_t *rmaskp = mvpr + descp->emmd_mask_offset;
1585 		size_t mask_size = descp->emmd_mask_size;
1586 		const uint8_t *lvalp = mvpl + descp->emmd_value_offset;
1587 		const uint8_t *rvalp = mvpr + descp->emmd_value_offset;
1588 		size_t value_size = descp->emmd_value_size;
1589 
1590 		if (mask_size == 0)
1591 			continue; /* Skip array gap */
1592 
1593 		if ((unsigned int)field_cap_id >= field_ncaps) {
1594 			/*
1595 			 * The FW has not reported capability status for this
1596 			 * field. It's unknown whether any difference between
1597 			 * the two masks / values affects the class. The only
1598 			 * case when the class must be the same is when these
1599 			 * mask-value pairs match. Otherwise, report mismatch.
1600 			 */
1601 			if ((memcmp(lmaskp, rmaskp, mask_size) == 0) &&
1602 			    (memcmp(lvalp, rvalp, value_size) == 0))
1603 				continue;
1604 			else
1605 				break;
1606 		}
1607 
1608 		if (field_caps[field_cap_id].emfc_mask_affects_class) {
1609 			if (memcmp(lmaskp, rmaskp, mask_size) != 0) {
1610 				have_same_class = B_FALSE;
1611 				break;
1612 			}
1613 		}
1614 
1615 		if (field_caps[field_cap_id].emfc_match_affects_class) {
1616 			if (memcmp(lvalp, rvalp, value_size) != 0) {
1617 				have_same_class = B_FALSE;
1618 				break;
1619 			}
1620 		}
1621 	}
1622 
1623 	*have_same_classp = have_same_class;
1624 
1625 	return (0);
1626 
1627 fail2:
1628 	EFSYS_PROBE(fail2);
1629 fail1:
1630 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1631 	return (rc);
1632 }
1633 
1634 	__checkReturn		efx_rc_t
1635 efx_mae_outer_rule_insert(
1636 	__in			efx_nic_t *enp,
1637 	__in			const efx_mae_match_spec_t *spec,
1638 	__in			efx_tunnel_protocol_t encap_type,
1639 	__out			efx_mae_rule_id_t *or_idp)
1640 {
1641 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
1642 	efx_mcdi_req_t req;
1643 	EFX_MCDI_DECLARE_BUF(payload,
1644 	    MC_CMD_MAE_OUTER_RULE_INSERT_IN_LENMAX_MCDI2,
1645 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN);
1646 	uint32_t encap_type_mcdi;
1647 	efx_mae_rule_id_t or_id;
1648 	size_t offset;
1649 	efx_rc_t rc;
1650 
1651 	EFX_STATIC_ASSERT(sizeof (or_idp->id) ==
1652 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OR_ID_LEN);
1653 
1654 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
1655 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL);
1656 
1657 	if (encp->enc_mae_supported == B_FALSE) {
1658 		rc = ENOTSUP;
1659 		goto fail1;
1660 	}
1661 
1662 	if (spec->emms_type != EFX_MAE_RULE_OUTER) {
1663 		rc = EINVAL;
1664 		goto fail2;
1665 	}
1666 
1667 	switch (encap_type) {
1668 	case EFX_TUNNEL_PROTOCOL_NONE:
1669 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NONE;
1670 		break;
1671 	case EFX_TUNNEL_PROTOCOL_VXLAN:
1672 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_VXLAN;
1673 		break;
1674 	case EFX_TUNNEL_PROTOCOL_GENEVE:
1675 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_GENEVE;
1676 		break;
1677 	case EFX_TUNNEL_PROTOCOL_NVGRE:
1678 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NVGRE;
1679 		break;
1680 	default:
1681 		rc = ENOTSUP;
1682 		goto fail3;
1683 	}
1684 
1685 	req.emr_cmd = MC_CMD_MAE_OUTER_RULE_INSERT;
1686 	req.emr_in_buf = payload;
1687 	req.emr_in_length = MC_CMD_MAE_OUTER_RULE_INSERT_IN_LENMAX_MCDI2;
1688 	req.emr_out_buf = payload;
1689 	req.emr_out_length = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN;
1690 
1691 	MCDI_IN_SET_DWORD(req,
1692 	    MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE, encap_type_mcdi);
1693 
1694 	MCDI_IN_SET_DWORD(req, MAE_OUTER_RULE_INSERT_IN_PRIO, spec->emms_prio);
1695 
1696 	/*
1697 	 * Mask-value pairs have been stored in the byte order needed for the
1698 	 * MCDI request and are thus safe to be copied directly to the buffer.
1699 	 * The library cares about byte order in efx_mae_match_spec_field_set().
1700 	 */
1701 	EFX_STATIC_ASSERT(sizeof (spec->emms_mask_value_pairs.outer) >=
1702 	    MAE_ENC_FIELD_PAIRS_LEN);
1703 	offset = MC_CMD_MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA_OFST;
1704 	memcpy(payload + offset, spec->emms_mask_value_pairs.outer,
1705 	    MAE_ENC_FIELD_PAIRS_LEN);
1706 
1707 	efx_mcdi_execute(enp, &req);
1708 
1709 	if (req.emr_rc != 0) {
1710 		rc = req.emr_rc;
1711 		goto fail4;
1712 	}
1713 
1714 	if (req.emr_out_length_used < MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN) {
1715 		rc = EMSGSIZE;
1716 		goto fail5;
1717 	}
1718 
1719 	or_id.id = MCDI_OUT_DWORD(req, MAE_OUTER_RULE_INSERT_OUT_OR_ID);
1720 	if (or_id.id == EFX_MAE_RSRC_ID_INVALID) {
1721 		rc = ENOENT;
1722 		goto fail6;
1723 	}
1724 
1725 	or_idp->id = or_id.id;
1726 
1727 	return (0);
1728 
1729 fail6:
1730 	EFSYS_PROBE(fail6);
1731 fail5:
1732 	EFSYS_PROBE(fail5);
1733 fail4:
1734 	EFSYS_PROBE(fail4);
1735 fail3:
1736 	EFSYS_PROBE(fail3);
1737 fail2:
1738 	EFSYS_PROBE(fail2);
1739 fail1:
1740 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1741 	return (rc);
1742 }
1743 
1744 	__checkReturn		efx_rc_t
1745 efx_mae_outer_rule_remove(
1746 	__in			efx_nic_t *enp,
1747 	__in			const efx_mae_rule_id_t *or_idp)
1748 {
1749 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
1750 	efx_mcdi_req_t req;
1751 	EFX_MCDI_DECLARE_BUF(payload,
1752 	    MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1),
1753 	    MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1));
1754 	efx_rc_t rc;
1755 
1756 	if (encp->enc_mae_supported == B_FALSE) {
1757 		rc = ENOTSUP;
1758 		goto fail1;
1759 	}
1760 
1761 	req.emr_cmd = MC_CMD_MAE_OUTER_RULE_REMOVE;
1762 	req.emr_in_buf = payload;
1763 	req.emr_in_length = MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1);
1764 	req.emr_out_buf = payload;
1765 	req.emr_out_length = MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1);
1766 
1767 	MCDI_IN_SET_DWORD(req, MAE_OUTER_RULE_REMOVE_IN_OR_ID, or_idp->id);
1768 
1769 	efx_mcdi_execute(enp, &req);
1770 
1771 	if (req.emr_rc != 0) {
1772 		rc = req.emr_rc;
1773 		goto fail2;
1774 	}
1775 
1776 	if (MCDI_OUT_DWORD(req, MAE_OUTER_RULE_REMOVE_OUT_REMOVED_OR_ID) !=
1777 	    or_idp->id) {
1778 		/* Firmware failed to remove the outer rule. */
1779 		rc = EAGAIN;
1780 		goto fail3;
1781 	}
1782 
1783 	return (0);
1784 
1785 fail3:
1786 	EFSYS_PROBE(fail3);
1787 fail2:
1788 	EFSYS_PROBE(fail2);
1789 fail1:
1790 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1791 	return (rc);
1792 }
1793 
1794 	__checkReturn			efx_rc_t
1795 efx_mae_match_spec_outer_rule_id_set(
1796 	__in				efx_mae_match_spec_t *spec,
1797 	__in				const efx_mae_rule_id_t *or_idp)
1798 {
1799 	uint32_t full_mask = UINT32_MAX;
1800 	efx_rc_t rc;
1801 
1802 	if (spec->emms_type != EFX_MAE_RULE_ACTION) {
1803 		rc = EINVAL;
1804 		goto fail1;
1805 	}
1806 
1807 	if (or_idp == NULL) {
1808 		rc = EINVAL;
1809 		goto fail2;
1810 	}
1811 
1812 	rc = efx_mae_match_spec_field_set(spec, EFX_MAE_FIELD_OUTER_RULE_ID,
1813 	    sizeof (or_idp->id), (const uint8_t *)&or_idp->id,
1814 	    sizeof (full_mask), (const uint8_t *)&full_mask);
1815 	if (rc != 0)
1816 		goto fail3;
1817 
1818 	return (0);
1819 
1820 fail3:
1821 	EFSYS_PROBE(fail3);
1822 fail2:
1823 	EFSYS_PROBE(fail2);
1824 fail1:
1825 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1826 	return (rc);
1827 }
1828 
1829 	 __checkReturn			efx_rc_t
1830 efx_mae_encap_header_alloc(
1831 	__in				efx_nic_t *enp,
1832 	__in				efx_tunnel_protocol_t encap_type,
1833 	__in_bcount(header_size)	uint8_t *header_data,
1834 	__in				size_t header_size,
1835 	__out				efx_mae_eh_id_t *eh_idp)
1836 {
1837 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
1838 	efx_mcdi_req_t req;
1839 	EFX_MCDI_DECLARE_BUF(payload,
1840 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LENMAX_MCDI2,
1841 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN);
1842 	uint32_t encap_type_mcdi;
1843 	efx_mae_eh_id_t eh_id;
1844 	efx_rc_t rc;
1845 
1846 	EFX_STATIC_ASSERT(sizeof (eh_idp->id) ==
1847 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_LEN);
1848 
1849 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
1850 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
1851 
1852 	if (encp->enc_mae_supported == B_FALSE) {
1853 		rc = ENOTSUP;
1854 		goto fail1;
1855 	}
1856 
1857 	switch (encap_type) {
1858 	case EFX_TUNNEL_PROTOCOL_NONE:
1859 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NONE;
1860 		break;
1861 	case EFX_TUNNEL_PROTOCOL_VXLAN:
1862 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_VXLAN;
1863 		break;
1864 	case EFX_TUNNEL_PROTOCOL_GENEVE:
1865 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_GENEVE;
1866 		break;
1867 	case EFX_TUNNEL_PROTOCOL_NVGRE:
1868 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NVGRE;
1869 		break;
1870 	default:
1871 		rc = ENOTSUP;
1872 		goto fail2;
1873 	}
1874 
1875 	if (header_size >
1876 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_MAXNUM_MCDI2) {
1877 		rc = EINVAL;
1878 		goto fail3;
1879 	}
1880 
1881 	req.emr_cmd = MC_CMD_MAE_ENCAP_HEADER_ALLOC;
1882 	req.emr_in_buf = payload;
1883 	req.emr_in_length = MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN(header_size);
1884 	req.emr_out_buf = payload;
1885 	req.emr_out_length = MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN;
1886 
1887 	MCDI_IN_SET_DWORD(req,
1888 	    MAE_ENCAP_HEADER_ALLOC_IN_ENCAP_TYPE, encap_type_mcdi);
1889 
1890 	memcpy(payload + MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_OFST,
1891 	    header_data, header_size);
1892 
1893 	efx_mcdi_execute(enp, &req);
1894 
1895 	if (req.emr_rc != 0) {
1896 		rc = req.emr_rc;
1897 		goto fail4;
1898 	}
1899 
1900 	if (req.emr_out_length_used < MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN) {
1901 		rc = EMSGSIZE;
1902 		goto fail5;
1903 	}
1904 
1905 	eh_id.id = MCDI_OUT_DWORD(req,
1906 	    MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID);
1907 
1908 	if (eh_id.id == EFX_MAE_RSRC_ID_INVALID) {
1909 		rc = ENOENT;
1910 		goto fail6;
1911 	}
1912 
1913 	eh_idp->id = eh_id.id;
1914 
1915 	return (0);
1916 
1917 fail6:
1918 	EFSYS_PROBE(fail6);
1919 fail5:
1920 	EFSYS_PROBE(fail5);
1921 fail4:
1922 	EFSYS_PROBE(fail4);
1923 fail3:
1924 	EFSYS_PROBE(fail3);
1925 fail2:
1926 	EFSYS_PROBE(fail2);
1927 fail1:
1928 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1929 	return (rc);
1930 }
1931 
1932 	__checkReturn			efx_rc_t
1933 efx_mae_encap_header_free(
1934 	__in				efx_nic_t *enp,
1935 	__in				const efx_mae_eh_id_t *eh_idp)
1936 {
1937 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
1938 	efx_mcdi_req_t req;
1939 	EFX_MCDI_DECLARE_BUF(payload,
1940 	    MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN(1),
1941 	    MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN(1));
1942 	efx_rc_t rc;
1943 
1944 	if (encp->enc_mae_supported == B_FALSE) {
1945 		rc = ENOTSUP;
1946 		goto fail1;
1947 	}
1948 
1949 	req.emr_cmd = MC_CMD_MAE_ENCAP_HEADER_FREE;
1950 	req.emr_in_buf = payload;
1951 	req.emr_in_length = MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN(1);
1952 	req.emr_out_buf = payload;
1953 	req.emr_out_length = MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN(1);
1954 
1955 	MCDI_IN_SET_DWORD(req, MAE_ENCAP_HEADER_FREE_IN_EH_ID, eh_idp->id);
1956 
1957 	efx_mcdi_execute(enp, &req);
1958 
1959 	if (req.emr_rc != 0) {
1960 		rc = req.emr_rc;
1961 		goto fail2;
1962 	}
1963 
1964 	if (MCDI_OUT_DWORD(req, MAE_ENCAP_HEADER_FREE_OUT_FREED_EH_ID) !=
1965 	    eh_idp->id) {
1966 		/* Firmware failed to remove the encap. header. */
1967 		rc = EAGAIN;
1968 		goto fail3;
1969 	}
1970 
1971 	return (0);
1972 
1973 fail3:
1974 	EFSYS_PROBE(fail3);
1975 fail2:
1976 	EFSYS_PROBE(fail2);
1977 fail1:
1978 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1979 	return (rc);
1980 }
1981 
1982 	__checkReturn			efx_rc_t
1983 efx_mae_action_set_fill_in_eh_id(
1984 	__in				efx_mae_actions_t *spec,
1985 	__in				const efx_mae_eh_id_t *eh_idp)
1986 {
1987 	efx_rc_t rc;
1988 
1989 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_ENCAP)) == 0) {
1990 		/*
1991 		 * The caller has not intended to have action ENCAP originally,
1992 		 * hence, this attempt to indicate encap. header ID is invalid.
1993 		 */
1994 		rc = EINVAL;
1995 		goto fail1;
1996 	}
1997 
1998 	if (spec->ema_rsrc.emar_eh_id.id != EFX_MAE_RSRC_ID_INVALID) {
1999 		/* The caller attempts to indicate encap. header ID twice. */
2000 		rc = EINVAL;
2001 		goto fail2;
2002 	}
2003 
2004 	if (eh_idp->id == EFX_MAE_RSRC_ID_INVALID) {
2005 		rc = EINVAL;
2006 		goto fail3;
2007 	}
2008 
2009 	spec->ema_rsrc.emar_eh_id.id = eh_idp->id;
2010 
2011 	return (0);
2012 
2013 fail3:
2014 	EFSYS_PROBE(fail3);
2015 fail2:
2016 	EFSYS_PROBE(fail2);
2017 fail1:
2018 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2019 	return (rc);
2020 }
2021 
2022 	__checkReturn			efx_rc_t
2023 efx_mae_action_set_alloc(
2024 	__in				efx_nic_t *enp,
2025 	__in				const efx_mae_actions_t *spec,
2026 	__out				efx_mae_aset_id_t *aset_idp)
2027 {
2028 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2029 	efx_mcdi_req_t req;
2030 	EFX_MCDI_DECLARE_BUF(payload,
2031 	    MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN,
2032 	    MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
2033 	efx_mae_aset_id_t aset_id;
2034 	efx_rc_t rc;
2035 
2036 	if (encp->enc_mae_supported == B_FALSE) {
2037 		rc = ENOTSUP;
2038 		goto fail1;
2039 	}
2040 
2041 	req.emr_cmd = MC_CMD_MAE_ACTION_SET_ALLOC;
2042 	req.emr_in_buf = payload;
2043 	req.emr_in_length = MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN;
2044 	req.emr_out_buf = payload;
2045 	req.emr_out_length = MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN;
2046 
2047 	/*
2048 	 * TODO: Remove these EFX_MAE_RSRC_ID_INVALID assignments once the
2049 	 * corresponding resource types are supported by the implementation.
2050 	 * Use proper resource ID assignments instead.
2051 	 */
2052 	MCDI_IN_SET_DWORD(req,
2053 	    MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, EFX_MAE_RSRC_ID_INVALID);
2054 	MCDI_IN_SET_DWORD(req,
2055 	    MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, EFX_MAE_RSRC_ID_INVALID);
2056 
2057 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_DECAP)) != 0) {
2058 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2059 		    MAE_ACTION_SET_ALLOC_IN_DECAP, 1);
2060 	}
2061 
2062 	MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2063 	    MAE_ACTION_SET_ALLOC_IN_VLAN_POP, spec->ema_n_vlan_tags_to_pop);
2064 
2065 	if (spec->ema_n_vlan_tags_to_push > 0) {
2066 		unsigned int outer_tag_idx;
2067 
2068 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2069 		    MAE_ACTION_SET_ALLOC_IN_VLAN_PUSH,
2070 		    spec->ema_n_vlan_tags_to_push);
2071 
2072 		if (spec->ema_n_vlan_tags_to_push ==
2073 		    EFX_MAE_VLAN_PUSH_MAX_NTAGS) {
2074 			MCDI_IN_SET_WORD(req,
2075 			    MAE_ACTION_SET_ALLOC_IN_VLAN1_PROTO_BE,
2076 			    spec->ema_vlan_push_descs[0].emavp_tpid_be);
2077 			MCDI_IN_SET_WORD(req,
2078 			    MAE_ACTION_SET_ALLOC_IN_VLAN1_TCI_BE,
2079 			    spec->ema_vlan_push_descs[0].emavp_tci_be);
2080 		}
2081 
2082 		outer_tag_idx = spec->ema_n_vlan_tags_to_push - 1;
2083 
2084 		MCDI_IN_SET_WORD(req, MAE_ACTION_SET_ALLOC_IN_VLAN0_PROTO_BE,
2085 		    spec->ema_vlan_push_descs[outer_tag_idx].emavp_tpid_be);
2086 		MCDI_IN_SET_WORD(req, MAE_ACTION_SET_ALLOC_IN_VLAN0_TCI_BE,
2087 		    spec->ema_vlan_push_descs[outer_tag_idx].emavp_tci_be);
2088 	}
2089 
2090 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
2091 	    spec->ema_rsrc.emar_eh_id.id);
2092 
2093 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_FLAG)) != 0) {
2094 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2095 		    MAE_ACTION_SET_ALLOC_IN_FLAG, 1);
2096 	}
2097 
2098 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_MARK)) != 0) {
2099 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2100 		    MAE_ACTION_SET_ALLOC_IN_MARK, 1);
2101 
2102 		MCDI_IN_SET_DWORD(req,
2103 		    MAE_ACTION_SET_ALLOC_IN_MARK_VALUE, spec->ema_mark_value);
2104 	}
2105 
2106 	MCDI_IN_SET_DWORD(req,
2107 	    MAE_ACTION_SET_ALLOC_IN_DELIVER, spec->ema_deliver_mport.sel);
2108 
2109 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
2110 	    MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
2111 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
2112 	    MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
2113 
2114 	efx_mcdi_execute(enp, &req);
2115 
2116 	if (req.emr_rc != 0) {
2117 		rc = req.emr_rc;
2118 		goto fail2;
2119 	}
2120 
2121 	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN) {
2122 		rc = EMSGSIZE;
2123 		goto fail3;
2124 	}
2125 
2126 	aset_id.id = MCDI_OUT_DWORD(req, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
2127 	if (aset_id.id == EFX_MAE_RSRC_ID_INVALID) {
2128 		rc = ENOENT;
2129 		goto fail4;
2130 	}
2131 
2132 	aset_idp->id = aset_id.id;
2133 
2134 	return (0);
2135 
2136 fail4:
2137 	EFSYS_PROBE(fail4);
2138 fail3:
2139 	EFSYS_PROBE(fail3);
2140 fail2:
2141 	EFSYS_PROBE(fail2);
2142 fail1:
2143 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2144 	return (rc);
2145 }
2146 
2147 	__checkReturn			efx_rc_t
2148 efx_mae_action_set_free(
2149 	__in				efx_nic_t *enp,
2150 	__in				const efx_mae_aset_id_t *aset_idp)
2151 {
2152 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2153 	efx_mcdi_req_t req;
2154 	EFX_MCDI_DECLARE_BUF(payload,
2155 	    MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1),
2156 	    MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
2157 	efx_rc_t rc;
2158 
2159 	if (encp->enc_mae_supported == B_FALSE) {
2160 		rc = ENOTSUP;
2161 		goto fail1;
2162 	}
2163 
2164 	req.emr_cmd = MC_CMD_MAE_ACTION_SET_FREE;
2165 	req.emr_in_buf = payload;
2166 	req.emr_in_length = MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1);
2167 	req.emr_out_buf = payload;
2168 	req.emr_out_length = MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1);
2169 
2170 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_FREE_IN_AS_ID, aset_idp->id);
2171 
2172 	efx_mcdi_execute(enp, &req);
2173 
2174 	if (req.emr_rc != 0) {
2175 		rc = req.emr_rc;
2176 		goto fail2;
2177 	}
2178 
2179 	if (MCDI_OUT_DWORD(req, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) !=
2180 	    aset_idp->id) {
2181 		/* Firmware failed to free the action set. */
2182 		rc = EAGAIN;
2183 		goto fail3;
2184 	}
2185 
2186 	return (0);
2187 
2188 fail3:
2189 	EFSYS_PROBE(fail3);
2190 fail2:
2191 	EFSYS_PROBE(fail2);
2192 fail1:
2193 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2194 	return (rc);
2195 }
2196 
2197 	__checkReturn			efx_rc_t
2198 efx_mae_action_rule_insert(
2199 	__in				efx_nic_t *enp,
2200 	__in				const efx_mae_match_spec_t *spec,
2201 	__in				const efx_mae_aset_list_id_t *asl_idp,
2202 	__in				const efx_mae_aset_id_t *as_idp,
2203 	__out				efx_mae_rule_id_t *ar_idp)
2204 {
2205 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2206 	efx_mcdi_req_t req;
2207 	EFX_MCDI_DECLARE_BUF(payload,
2208 	    MC_CMD_MAE_ACTION_RULE_INSERT_IN_LENMAX_MCDI2,
2209 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
2210 	efx_oword_t *rule_response;
2211 	efx_mae_rule_id_t ar_id;
2212 	size_t offset;
2213 	efx_rc_t rc;
2214 
2215 	EFX_STATIC_ASSERT(sizeof (ar_idp->id) ==
2216 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_AR_ID_LEN);
2217 
2218 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
2219 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL);
2220 
2221 	if (encp->enc_mae_supported == B_FALSE) {
2222 		rc = ENOTSUP;
2223 		goto fail1;
2224 	}
2225 
2226 	if (spec->emms_type != EFX_MAE_RULE_ACTION ||
2227 	    (asl_idp != NULL && as_idp != NULL) ||
2228 	    (asl_idp == NULL && as_idp == NULL)) {
2229 		rc = EINVAL;
2230 		goto fail2;
2231 	}
2232 
2233 	req.emr_cmd = MC_CMD_MAE_ACTION_RULE_INSERT;
2234 	req.emr_in_buf = payload;
2235 	req.emr_in_length = MC_CMD_MAE_ACTION_RULE_INSERT_IN_LENMAX_MCDI2;
2236 	req.emr_out_buf = payload;
2237 	req.emr_out_length = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN;
2238 
2239 	EFX_STATIC_ASSERT(sizeof (*rule_response) <=
2240 	    MC_CMD_MAE_ACTION_RULE_INSERT_IN_RESPONSE_LEN);
2241 	offset = MC_CMD_MAE_ACTION_RULE_INSERT_IN_RESPONSE_OFST;
2242 	rule_response = (efx_oword_t *)(payload + offset);
2243 	EFX_POPULATE_OWORD_3(*rule_response,
2244 	    MAE_ACTION_RULE_RESPONSE_ASL_ID,
2245 	    (asl_idp != NULL) ? asl_idp->id : EFX_MAE_RSRC_ID_INVALID,
2246 	    MAE_ACTION_RULE_RESPONSE_AS_ID,
2247 	    (as_idp != NULL) ? as_idp->id : EFX_MAE_RSRC_ID_INVALID,
2248 	    MAE_ACTION_RULE_RESPONSE_COUNTER_ID, EFX_MAE_RSRC_ID_INVALID);
2249 
2250 	MCDI_IN_SET_DWORD(req, MAE_ACTION_RULE_INSERT_IN_PRIO, spec->emms_prio);
2251 
2252 	/*
2253 	 * Mask-value pairs have been stored in the byte order needed for the
2254 	 * MCDI request and are thus safe to be copied directly to the buffer.
2255 	 */
2256 	EFX_STATIC_ASSERT(sizeof (spec->emms_mask_value_pairs.action) >=
2257 	    MAE_FIELD_MASK_VALUE_PAIRS_LEN);
2258 	offset = MC_CMD_MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA_OFST;
2259 	memcpy(payload + offset, spec->emms_mask_value_pairs.action,
2260 	    MAE_FIELD_MASK_VALUE_PAIRS_LEN);
2261 
2262 	efx_mcdi_execute(enp, &req);
2263 
2264 	if (req.emr_rc != 0) {
2265 		rc = req.emr_rc;
2266 		goto fail3;
2267 	}
2268 
2269 	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN) {
2270 		rc = EMSGSIZE;
2271 		goto fail4;
2272 	}
2273 
2274 	ar_id.id = MCDI_OUT_DWORD(req, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
2275 	if (ar_id.id == EFX_MAE_RSRC_ID_INVALID) {
2276 		rc = ENOENT;
2277 		goto fail5;
2278 	}
2279 
2280 	ar_idp->id = ar_id.id;
2281 
2282 	return (0);
2283 
2284 fail5:
2285 	EFSYS_PROBE(fail5);
2286 fail4:
2287 	EFSYS_PROBE(fail4);
2288 fail3:
2289 	EFSYS_PROBE(fail3);
2290 fail2:
2291 	EFSYS_PROBE(fail2);
2292 fail1:
2293 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2294 	return (rc);
2295 }
2296 
2297 	__checkReturn			efx_rc_t
2298 efx_mae_action_rule_remove(
2299 	__in				efx_nic_t *enp,
2300 	__in				const efx_mae_rule_id_t *ar_idp)
2301 {
2302 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2303 	efx_mcdi_req_t req;
2304 	EFX_MCDI_DECLARE_BUF(payload,
2305 	    MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1),
2306 	    MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
2307 	efx_rc_t rc;
2308 
2309 	if (encp->enc_mae_supported == B_FALSE) {
2310 		rc = ENOTSUP;
2311 		goto fail1;
2312 	}
2313 
2314 	req.emr_cmd = MC_CMD_MAE_ACTION_RULE_DELETE;
2315 	req.emr_in_buf = payload;
2316 	req.emr_in_length = MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1);
2317 	req.emr_out_buf = payload;
2318 	req.emr_out_length = MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1);
2319 
2320 	MCDI_IN_SET_DWORD(req, MAE_ACTION_RULE_DELETE_IN_AR_ID, ar_idp->id);
2321 
2322 	efx_mcdi_execute(enp, &req);
2323 
2324 	if (req.emr_rc != 0) {
2325 		rc = req.emr_rc;
2326 		goto fail2;
2327 	}
2328 
2329 	if (MCDI_OUT_DWORD(req, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) !=
2330 	    ar_idp->id) {
2331 		/* Firmware failed to delete the action rule. */
2332 		rc = EAGAIN;
2333 		goto fail3;
2334 	}
2335 
2336 	return (0);
2337 
2338 fail3:
2339 	EFSYS_PROBE(fail3);
2340 fail2:
2341 	EFSYS_PROBE(fail2);
2342 fail1:
2343 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2344 	return (rc);
2345 }
2346 
2347 #endif /* EFSYS_OPT_MAE */
2348