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