xref: /dpdk/drivers/common/sfc_efx/base/efx_mae.c (revision f8dbaebbf1c9efcbb2e2354b341ed62175466a57)
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 	maep->em_max_ncounters =
71 	    MCDI_OUT_DWORD(req, MAE_GET_CAPS_OUT_COUNTERS);
72 
73 	return (0);
74 
75 fail2:
76 	EFSYS_PROBE(fail2);
77 fail1:
78 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
79 	return (rc);
80 }
81 
82 static	__checkReturn			efx_rc_t
83 efx_mae_get_outer_rule_caps(
84 	__in				efx_nic_t *enp,
85 	__in				unsigned int field_ncaps,
86 	__out_ecount(field_ncaps)	efx_mae_field_cap_t *field_caps)
87 {
88 	efx_mcdi_req_t req;
89 	EFX_MCDI_DECLARE_BUF(payload,
90 	    MC_CMD_MAE_GET_OR_CAPS_IN_LEN,
91 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LENMAX_MCDI2);
92 	unsigned int mcdi_field_ncaps;
93 	unsigned int i;
94 	efx_rc_t rc;
95 
96 	if (MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(field_ncaps) >
97 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LENMAX_MCDI2) {
98 		rc = EINVAL;
99 		goto fail1;
100 	}
101 
102 	req.emr_cmd = MC_CMD_MAE_GET_OR_CAPS;
103 	req.emr_in_buf = payload;
104 	req.emr_in_length = MC_CMD_MAE_GET_OR_CAPS_IN_LEN;
105 	req.emr_out_buf = payload;
106 	req.emr_out_length = MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(field_ncaps);
107 
108 	efx_mcdi_execute(enp, &req);
109 
110 	if (req.emr_rc != 0) {
111 		rc = req.emr_rc;
112 		goto fail2;
113 	}
114 
115 	if (req.emr_out_length_used < MC_CMD_MAE_GET_OR_CAPS_OUT_LENMIN) {
116 		rc = EMSGSIZE;
117 		goto fail3;
118 	}
119 
120 	mcdi_field_ncaps = MCDI_OUT_DWORD(req, MAE_GET_OR_CAPS_OUT_COUNT);
121 
122 	if (req.emr_out_length_used <
123 	    MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(mcdi_field_ncaps)) {
124 		rc = EMSGSIZE;
125 		goto fail4;
126 	}
127 
128 	if (mcdi_field_ncaps > field_ncaps) {
129 		rc = EMSGSIZE;
130 		goto fail5;
131 	}
132 
133 	for (i = 0; i < mcdi_field_ncaps; ++i) {
134 		uint32_t match_flag;
135 		uint32_t mask_flag;
136 
137 		field_caps[i].emfc_support = MCDI_OUT_INDEXED_DWORD_FIELD(req,
138 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
139 		    MAE_FIELD_FLAGS_SUPPORT_STATUS);
140 
141 		match_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
142 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
143 		    MAE_FIELD_FLAGS_MATCH_AFFECTS_CLASS);
144 
145 		field_caps[i].emfc_match_affects_class =
146 		    (match_flag != 0) ? B_TRUE : B_FALSE;
147 
148 		mask_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
149 		    MAE_GET_OR_CAPS_OUT_FIELD_FLAGS, i,
150 		    MAE_FIELD_FLAGS_MASK_AFFECTS_CLASS);
151 
152 		field_caps[i].emfc_mask_affects_class =
153 		    (mask_flag != 0) ? B_TRUE : B_FALSE;
154 	}
155 
156 	return (0);
157 
158 fail5:
159 	EFSYS_PROBE(fail5);
160 fail4:
161 	EFSYS_PROBE(fail4);
162 fail3:
163 	EFSYS_PROBE(fail3);
164 fail2:
165 	EFSYS_PROBE(fail2);
166 fail1:
167 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
168 	return (rc);
169 }
170 
171 static	__checkReturn			efx_rc_t
172 efx_mae_get_action_rule_caps(
173 	__in				efx_nic_t *enp,
174 	__in				unsigned int field_ncaps,
175 	__out_ecount(field_ncaps)	efx_mae_field_cap_t *field_caps)
176 {
177 	efx_mcdi_req_t req;
178 	EFX_MCDI_DECLARE_BUF(payload,
179 	    MC_CMD_MAE_GET_AR_CAPS_IN_LEN,
180 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2);
181 	unsigned int mcdi_field_ncaps;
182 	unsigned int i;
183 	efx_rc_t rc;
184 
185 	if (MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps) >
186 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LENMAX_MCDI2) {
187 		rc = EINVAL;
188 		goto fail1;
189 	}
190 
191 	req.emr_cmd = MC_CMD_MAE_GET_AR_CAPS;
192 	req.emr_in_buf = payload;
193 	req.emr_in_length = MC_CMD_MAE_GET_AR_CAPS_IN_LEN;
194 	req.emr_out_buf = payload;
195 	req.emr_out_length = MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(field_ncaps);
196 
197 	efx_mcdi_execute(enp, &req);
198 
199 	if (req.emr_rc != 0) {
200 		rc = req.emr_rc;
201 		goto fail2;
202 	}
203 
204 	if (req.emr_out_length_used < MC_CMD_MAE_GET_AR_CAPS_OUT_LENMIN) {
205 		rc = EMSGSIZE;
206 		goto fail3;
207 	}
208 
209 	mcdi_field_ncaps = MCDI_OUT_DWORD(req, MAE_GET_AR_CAPS_OUT_COUNT);
210 
211 	if (req.emr_out_length_used <
212 	    MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(mcdi_field_ncaps)) {
213 		rc = EMSGSIZE;
214 		goto fail4;
215 	}
216 
217 	if (mcdi_field_ncaps > field_ncaps) {
218 		rc = EMSGSIZE;
219 		goto fail5;
220 	}
221 
222 	for (i = 0; i < mcdi_field_ncaps; ++i) {
223 		uint32_t match_flag;
224 		uint32_t mask_flag;
225 
226 		field_caps[i].emfc_support = MCDI_OUT_INDEXED_DWORD_FIELD(req,
227 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
228 		    MAE_FIELD_FLAGS_SUPPORT_STATUS);
229 
230 		match_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
231 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
232 		    MAE_FIELD_FLAGS_MATCH_AFFECTS_CLASS);
233 
234 		field_caps[i].emfc_match_affects_class =
235 		    (match_flag != 0) ? B_TRUE : B_FALSE;
236 
237 		mask_flag = MCDI_OUT_INDEXED_DWORD_FIELD(req,
238 		    MAE_GET_AR_CAPS_OUT_FIELD_FLAGS, i,
239 		    MAE_FIELD_FLAGS_MASK_AFFECTS_CLASS);
240 
241 		field_caps[i].emfc_mask_affects_class =
242 		    (mask_flag != 0) ? B_TRUE : B_FALSE;
243 	}
244 
245 	return (0);
246 
247 fail5:
248 	EFSYS_PROBE(fail5);
249 fail4:
250 	EFSYS_PROBE(fail4);
251 fail3:
252 	EFSYS_PROBE(fail3);
253 fail2:
254 	EFSYS_PROBE(fail2);
255 fail1:
256 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
257 	return (rc);
258 }
259 
260 	__checkReturn			efx_rc_t
261 efx_mae_init(
262 	__in				efx_nic_t *enp)
263 {
264 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
265 	efx_mae_field_cap_t *or_fcaps;
266 	size_t or_fcaps_size;
267 	efx_mae_field_cap_t *ar_fcaps;
268 	size_t ar_fcaps_size;
269 	efx_mae_t *maep;
270 	efx_rc_t rc;
271 
272 	if (encp->enc_mae_supported == B_FALSE) {
273 		rc = ENOTSUP;
274 		goto fail1;
275 	}
276 
277 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*maep), maep);
278 	if (maep == NULL) {
279 		rc = ENOMEM;
280 		goto fail2;
281 	}
282 
283 	enp->en_maep = maep;
284 
285 	rc = efx_mae_get_capabilities(enp);
286 	if (rc != 0)
287 		goto fail3;
288 
289 	or_fcaps_size = maep->em_max_nfields * sizeof (*or_fcaps);
290 	EFSYS_KMEM_ALLOC(enp->en_esip, or_fcaps_size, or_fcaps);
291 	if (or_fcaps == NULL) {
292 		rc = ENOMEM;
293 		goto fail4;
294 	}
295 
296 	maep->em_outer_rule_field_caps_size = or_fcaps_size;
297 	maep->em_outer_rule_field_caps = or_fcaps;
298 
299 	rc = efx_mae_get_outer_rule_caps(enp, maep->em_max_nfields, or_fcaps);
300 	if (rc != 0)
301 		goto fail5;
302 
303 	ar_fcaps_size = maep->em_max_nfields * sizeof (*ar_fcaps);
304 	EFSYS_KMEM_ALLOC(enp->en_esip, ar_fcaps_size, ar_fcaps);
305 	if (ar_fcaps == NULL) {
306 		rc = ENOMEM;
307 		goto fail6;
308 	}
309 
310 	maep->em_action_rule_field_caps_size = ar_fcaps_size;
311 	maep->em_action_rule_field_caps = ar_fcaps;
312 
313 	rc = efx_mae_get_action_rule_caps(enp, maep->em_max_nfields, ar_fcaps);
314 	if (rc != 0)
315 		goto fail7;
316 
317 	return (0);
318 
319 fail7:
320 	EFSYS_PROBE(fail5);
321 	EFSYS_KMEM_FREE(enp->en_esip, ar_fcaps_size, ar_fcaps);
322 fail6:
323 	EFSYS_PROBE(fail4);
324 fail5:
325 	EFSYS_PROBE(fail5);
326 	EFSYS_KMEM_FREE(enp->en_esip, or_fcaps_size, or_fcaps);
327 fail4:
328 	EFSYS_PROBE(fail4);
329 fail3:
330 	EFSYS_PROBE(fail3);
331 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (struct efx_mae_s), enp->en_maep);
332 	enp->en_maep = NULL;
333 fail2:
334 	EFSYS_PROBE(fail2);
335 fail1:
336 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
337 	return (rc);
338 }
339 
340 					void
341 efx_mae_fini(
342 	__in				efx_nic_t *enp)
343 {
344 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
345 	efx_mae_t *maep = enp->en_maep;
346 
347 	if (encp->enc_mae_supported == B_FALSE)
348 		return;
349 
350 	EFSYS_KMEM_FREE(enp->en_esip, maep->em_action_rule_field_caps_size,
351 	    maep->em_action_rule_field_caps);
352 	EFSYS_KMEM_FREE(enp->en_esip, maep->em_outer_rule_field_caps_size,
353 	    maep->em_outer_rule_field_caps);
354 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*maep), maep);
355 	enp->en_maep = NULL;
356 }
357 
358 	__checkReturn			efx_rc_t
359 efx_mae_get_limits(
360 	__in				efx_nic_t *enp,
361 	__out				efx_mae_limits_t *emlp)
362 {
363 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
364 	struct efx_mae_s *maep = enp->en_maep;
365 	efx_rc_t rc;
366 
367 	if (encp->enc_mae_supported == B_FALSE) {
368 		rc = ENOTSUP;
369 		goto fail1;
370 	}
371 
372 	emlp->eml_max_n_outer_prios = maep->em_max_n_outer_prios;
373 	emlp->eml_max_n_action_prios = maep->em_max_n_action_prios;
374 	emlp->eml_encap_types_supported = maep->em_encap_types_supported;
375 	emlp->eml_encap_header_size_limit =
376 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_MAXNUM_MCDI2;
377 	emlp->eml_max_n_counters = maep->em_max_ncounters;
378 
379 	return (0);
380 
381 fail1:
382 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
383 	return (rc);
384 }
385 
386 	__checkReturn			efx_rc_t
387 efx_mae_match_spec_init(
388 	__in				efx_nic_t *enp,
389 	__in				efx_mae_rule_type_t type,
390 	__in				uint32_t prio,
391 	__out				efx_mae_match_spec_t **specp)
392 {
393 	efx_mae_match_spec_t *spec;
394 	efx_rc_t rc;
395 
396 	switch (type) {
397 	case EFX_MAE_RULE_OUTER:
398 		break;
399 	case EFX_MAE_RULE_ACTION:
400 		break;
401 	default:
402 		rc = ENOTSUP;
403 		goto fail1;
404 	}
405 
406 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
407 	if (spec == NULL) {
408 		rc = ENOMEM;
409 		goto fail2;
410 	}
411 
412 	spec->emms_type = type;
413 	spec->emms_prio = prio;
414 
415 	*specp = spec;
416 
417 	return (0);
418 
419 fail2:
420 	EFSYS_PROBE(fail2);
421 fail1:
422 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
423 	return (rc);
424 }
425 
426 					void
427 efx_mae_match_spec_fini(
428 	__in				efx_nic_t *enp,
429 	__in				efx_mae_match_spec_t *spec)
430 {
431 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
432 }
433 
434 /* Named identifiers which are valid indices to efx_mae_field_cap_t */
435 typedef enum efx_mae_field_cap_id_e {
436 	EFX_MAE_FIELD_ID_INGRESS_MPORT_SELECTOR = MAE_FIELD_INGRESS_PORT,
437 	EFX_MAE_FIELD_ID_ETHER_TYPE_BE = MAE_FIELD_ETHER_TYPE,
438 	EFX_MAE_FIELD_ID_ETH_SADDR_BE = MAE_FIELD_ETH_SADDR,
439 	EFX_MAE_FIELD_ID_ETH_DADDR_BE = MAE_FIELD_ETH_DADDR,
440 	EFX_MAE_FIELD_ID_VLAN0_TCI_BE = MAE_FIELD_VLAN0_TCI,
441 	EFX_MAE_FIELD_ID_VLAN0_PROTO_BE = MAE_FIELD_VLAN0_PROTO,
442 	EFX_MAE_FIELD_ID_VLAN1_TCI_BE = MAE_FIELD_VLAN1_TCI,
443 	EFX_MAE_FIELD_ID_VLAN1_PROTO_BE = MAE_FIELD_VLAN1_PROTO,
444 	EFX_MAE_FIELD_ID_SRC_IP4_BE = MAE_FIELD_SRC_IP4,
445 	EFX_MAE_FIELD_ID_DST_IP4_BE = MAE_FIELD_DST_IP4,
446 	EFX_MAE_FIELD_ID_IP_PROTO = MAE_FIELD_IP_PROTO,
447 	EFX_MAE_FIELD_ID_IP_TOS = MAE_FIELD_IP_TOS,
448 	EFX_MAE_FIELD_ID_IP_TTL = MAE_FIELD_IP_TTL,
449 	EFX_MAE_FIELD_ID_SRC_IP6_BE = MAE_FIELD_SRC_IP6,
450 	EFX_MAE_FIELD_ID_DST_IP6_BE = MAE_FIELD_DST_IP6,
451 	EFX_MAE_FIELD_ID_L4_SPORT_BE = MAE_FIELD_L4_SPORT,
452 	EFX_MAE_FIELD_ID_L4_DPORT_BE = MAE_FIELD_L4_DPORT,
453 	EFX_MAE_FIELD_ID_TCP_FLAGS_BE = MAE_FIELD_TCP_FLAGS,
454 	EFX_MAE_FIELD_ID_ENC_ETHER_TYPE_BE = MAE_FIELD_ENC_ETHER_TYPE,
455 	EFX_MAE_FIELD_ID_ENC_ETH_SADDR_BE = MAE_FIELD_ENC_ETH_SADDR,
456 	EFX_MAE_FIELD_ID_ENC_ETH_DADDR_BE = MAE_FIELD_ENC_ETH_DADDR,
457 	EFX_MAE_FIELD_ID_ENC_VLAN0_TCI_BE = MAE_FIELD_ENC_VLAN0_TCI,
458 	EFX_MAE_FIELD_ID_ENC_VLAN0_PROTO_BE = MAE_FIELD_ENC_VLAN0_PROTO,
459 	EFX_MAE_FIELD_ID_ENC_VLAN1_TCI_BE = MAE_FIELD_ENC_VLAN1_TCI,
460 	EFX_MAE_FIELD_ID_ENC_VLAN1_PROTO_BE = MAE_FIELD_ENC_VLAN1_PROTO,
461 	EFX_MAE_FIELD_ID_ENC_SRC_IP4_BE = MAE_FIELD_ENC_SRC_IP4,
462 	EFX_MAE_FIELD_ID_ENC_DST_IP4_BE = MAE_FIELD_ENC_DST_IP4,
463 	EFX_MAE_FIELD_ID_ENC_IP_PROTO = MAE_FIELD_ENC_IP_PROTO,
464 	EFX_MAE_FIELD_ID_ENC_IP_TOS = MAE_FIELD_ENC_IP_TOS,
465 	EFX_MAE_FIELD_ID_ENC_IP_TTL = MAE_FIELD_ENC_IP_TTL,
466 	EFX_MAE_FIELD_ID_ENC_SRC_IP6_BE = MAE_FIELD_ENC_SRC_IP6,
467 	EFX_MAE_FIELD_ID_ENC_DST_IP6_BE = MAE_FIELD_ENC_DST_IP6,
468 	EFX_MAE_FIELD_ID_ENC_L4_SPORT_BE = MAE_FIELD_ENC_L4_SPORT,
469 	EFX_MAE_FIELD_ID_ENC_L4_DPORT_BE = MAE_FIELD_ENC_L4_DPORT,
470 	EFX_MAE_FIELD_ID_ENC_VNET_ID_BE = MAE_FIELD_ENC_VNET_ID,
471 	EFX_MAE_FIELD_ID_OUTER_RULE_ID = MAE_FIELD_OUTER_RULE_ID,
472 	EFX_MAE_FIELD_ID_HAS_OVLAN = MAE_FIELD_HAS_OVLAN,
473 	EFX_MAE_FIELD_ID_HAS_IVLAN = MAE_FIELD_HAS_IVLAN,
474 	EFX_MAE_FIELD_ID_ENC_HAS_OVLAN = MAE_FIELD_ENC_HAS_OVLAN,
475 	EFX_MAE_FIELD_ID_ENC_HAS_IVLAN = MAE_FIELD_ENC_HAS_IVLAN,
476 	EFX_MAE_FIELD_ID_RECIRC_ID = MAE_FIELD_RECIRC_ID,
477 
478 	EFX_MAE_FIELD_CAP_NIDS
479 } efx_mae_field_cap_id_t;
480 
481 typedef enum efx_mae_field_endianness_e {
482 	EFX_MAE_FIELD_LE = 0,
483 	EFX_MAE_FIELD_BE,
484 
485 	EFX_MAE_FIELD_ENDIANNESS_NTYPES
486 } efx_mae_field_endianness_t;
487 
488 /*
489  * The following structure is a means to describe an MAE field.
490  * The information in it is meant to be used internally by
491  * APIs for addressing a given field in a mask-value pairs
492  * structure and for validation purposes.
493  *
494  * A field may have an alternative one. This structure
495  * has additional members to reference the alternative
496  * field's mask. See efx_mae_match_spec_is_valid().
497  */
498 typedef struct efx_mae_mv_desc_s {
499 	efx_mae_field_cap_id_t		emmd_field_cap_id;
500 
501 	size_t				emmd_value_size;
502 	size_t				emmd_value_offset;
503 	size_t				emmd_mask_size;
504 	size_t				emmd_mask_offset;
505 
506 	/*
507 	 * Having the alternative field's mask size set to 0
508 	 * means that there's no alternative field specified.
509 	 */
510 	size_t				emmd_alt_mask_size;
511 	size_t				emmd_alt_mask_offset;
512 
513 	/* Primary field and the alternative one are of the same endianness. */
514 	efx_mae_field_endianness_t	emmd_endianness;
515 } efx_mae_mv_desc_t;
516 
517 /* Indices to this array are provided by efx_mae_field_id_t */
518 static const efx_mae_mv_desc_t __efx_mae_action_rule_mv_desc_set[] = {
519 #define	EFX_MAE_MV_DESC(_name, _endianness)				\
520 	[EFX_MAE_FIELD_##_name] =					\
521 	{								\
522 		EFX_MAE_FIELD_ID_##_name,				\
523 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_LEN,		\
524 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_OFST,		\
525 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_MASK_LEN,	\
526 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_MASK_OFST,	\
527 		0, 0 /* no alternative field */,			\
528 		_endianness						\
529 	}
530 
531 	EFX_MAE_MV_DESC(INGRESS_MPORT_SELECTOR, EFX_MAE_FIELD_LE),
532 	EFX_MAE_MV_DESC(ETHER_TYPE_BE, EFX_MAE_FIELD_BE),
533 	EFX_MAE_MV_DESC(ETH_SADDR_BE, EFX_MAE_FIELD_BE),
534 	EFX_MAE_MV_DESC(ETH_DADDR_BE, EFX_MAE_FIELD_BE),
535 	EFX_MAE_MV_DESC(VLAN0_TCI_BE, EFX_MAE_FIELD_BE),
536 	EFX_MAE_MV_DESC(VLAN0_PROTO_BE, EFX_MAE_FIELD_BE),
537 	EFX_MAE_MV_DESC(VLAN1_TCI_BE, EFX_MAE_FIELD_BE),
538 	EFX_MAE_MV_DESC(VLAN1_PROTO_BE, EFX_MAE_FIELD_BE),
539 	EFX_MAE_MV_DESC(SRC_IP4_BE, EFX_MAE_FIELD_BE),
540 	EFX_MAE_MV_DESC(DST_IP4_BE, EFX_MAE_FIELD_BE),
541 	EFX_MAE_MV_DESC(IP_PROTO, EFX_MAE_FIELD_BE),
542 	EFX_MAE_MV_DESC(IP_TOS, EFX_MAE_FIELD_BE),
543 	EFX_MAE_MV_DESC(IP_TTL, EFX_MAE_FIELD_BE),
544 	EFX_MAE_MV_DESC(SRC_IP6_BE, EFX_MAE_FIELD_BE),
545 	EFX_MAE_MV_DESC(DST_IP6_BE, EFX_MAE_FIELD_BE),
546 	EFX_MAE_MV_DESC(L4_SPORT_BE, EFX_MAE_FIELD_BE),
547 	EFX_MAE_MV_DESC(L4_DPORT_BE, EFX_MAE_FIELD_BE),
548 	EFX_MAE_MV_DESC(TCP_FLAGS_BE, EFX_MAE_FIELD_BE),
549 	EFX_MAE_MV_DESC(ENC_VNET_ID_BE, EFX_MAE_FIELD_BE),
550 	EFX_MAE_MV_DESC(OUTER_RULE_ID, EFX_MAE_FIELD_LE),
551 	EFX_MAE_MV_DESC(RECIRC_ID, EFX_MAE_FIELD_LE),
552 
553 #undef EFX_MAE_MV_DESC
554 };
555 
556 /* Indices to this array are provided by efx_mae_field_id_t */
557 static const efx_mae_mv_desc_t __efx_mae_outer_rule_mv_desc_set[] = {
558 #define	EFX_MAE_MV_DESC(_name, _endianness)				\
559 	[EFX_MAE_FIELD_##_name] =					\
560 	{								\
561 		EFX_MAE_FIELD_ID_##_name,				\
562 		MAE_ENC_FIELD_PAIRS_##_name##_LEN,			\
563 		MAE_ENC_FIELD_PAIRS_##_name##_OFST,			\
564 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN,			\
565 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST,		\
566 		0, 0 /* no alternative field */,			\
567 		_endianness						\
568 	}
569 
570 /* Same as EFX_MAE_MV_DESC(), but also indicates an alternative field. */
571 #define	EFX_MAE_MV_DESC_ALT(_name, _alt_name, _endianness)		\
572 	[EFX_MAE_FIELD_##_name] =					\
573 	{								\
574 		EFX_MAE_FIELD_ID_##_name,				\
575 		MAE_ENC_FIELD_PAIRS_##_name##_LEN,			\
576 		MAE_ENC_FIELD_PAIRS_##_name##_OFST,			\
577 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_LEN,			\
578 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST,		\
579 		MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_LEN,		\
580 		MAE_ENC_FIELD_PAIRS_##_alt_name##_MASK_OFST,		\
581 		_endianness						\
582 	}
583 
584 	EFX_MAE_MV_DESC(INGRESS_MPORT_SELECTOR, EFX_MAE_FIELD_LE),
585 	EFX_MAE_MV_DESC(ENC_ETHER_TYPE_BE, EFX_MAE_FIELD_BE),
586 	EFX_MAE_MV_DESC(ENC_ETH_SADDR_BE, EFX_MAE_FIELD_BE),
587 	EFX_MAE_MV_DESC(ENC_ETH_DADDR_BE, EFX_MAE_FIELD_BE),
588 	EFX_MAE_MV_DESC(ENC_VLAN0_TCI_BE, EFX_MAE_FIELD_BE),
589 	EFX_MAE_MV_DESC(ENC_VLAN0_PROTO_BE, EFX_MAE_FIELD_BE),
590 	EFX_MAE_MV_DESC(ENC_VLAN1_TCI_BE, EFX_MAE_FIELD_BE),
591 	EFX_MAE_MV_DESC(ENC_VLAN1_PROTO_BE, EFX_MAE_FIELD_BE),
592 	EFX_MAE_MV_DESC_ALT(ENC_SRC_IP4_BE, ENC_SRC_IP6_BE, EFX_MAE_FIELD_BE),
593 	EFX_MAE_MV_DESC_ALT(ENC_DST_IP4_BE, ENC_DST_IP6_BE, EFX_MAE_FIELD_BE),
594 	EFX_MAE_MV_DESC(ENC_IP_PROTO, EFX_MAE_FIELD_BE),
595 	EFX_MAE_MV_DESC(ENC_IP_TOS, EFX_MAE_FIELD_BE),
596 	EFX_MAE_MV_DESC(ENC_IP_TTL, EFX_MAE_FIELD_BE),
597 	EFX_MAE_MV_DESC_ALT(ENC_SRC_IP6_BE, ENC_SRC_IP4_BE, EFX_MAE_FIELD_BE),
598 	EFX_MAE_MV_DESC_ALT(ENC_DST_IP6_BE, ENC_DST_IP4_BE, EFX_MAE_FIELD_BE),
599 	EFX_MAE_MV_DESC(ENC_L4_SPORT_BE, EFX_MAE_FIELD_BE),
600 	EFX_MAE_MV_DESC(ENC_L4_DPORT_BE, EFX_MAE_FIELD_BE),
601 
602 #undef EFX_MAE_MV_DESC_ALT
603 #undef EFX_MAE_MV_DESC
604 };
605 
606 /*
607  * The following structure is a means to describe an MAE bit.
608  * The information in it is meant to be used internally by
609  * APIs for addressing a given flag in a mask-value pairs
610  * structure and for validation purposes.
611  */
612 typedef struct efx_mae_mv_bit_desc_s {
613 	/*
614 	 * Arrays using this struct are indexed by field IDs.
615 	 * Fields which aren't meant to be referenced by these
616 	 * arrays comprise gaps (invalid entries). Below field
617 	 * helps to identify such entries.
618 	 */
619 	boolean_t			emmbd_entry_is_valid;
620 	efx_mae_field_cap_id_t		emmbd_bit_cap_id;
621 	size_t				emmbd_value_ofst;
622 	unsigned int			emmbd_value_lbn;
623 	size_t				emmbd_mask_ofst;
624 	unsigned int			emmbd_mask_lbn;
625 } efx_mae_mv_bit_desc_t;
626 
627 static const efx_mae_mv_bit_desc_t __efx_mae_outer_rule_mv_bit_desc_set[] = {
628 #define	EFX_MAE_MV_BIT_DESC(_name)					\
629 	[EFX_MAE_FIELD_##_name] =					\
630 	{								\
631 		B_TRUE,							\
632 		EFX_MAE_FIELD_ID_##_name,				\
633 		MAE_ENC_FIELD_PAIRS_##_name##_OFST,			\
634 		MAE_ENC_FIELD_PAIRS_##_name##_LBN,			\
635 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_OFST,		\
636 		MAE_ENC_FIELD_PAIRS_##_name##_MASK_LBN,			\
637 	}
638 
639 	EFX_MAE_MV_BIT_DESC(ENC_HAS_OVLAN),
640 	EFX_MAE_MV_BIT_DESC(ENC_HAS_IVLAN),
641 
642 #undef EFX_MAE_MV_BIT_DESC
643 };
644 
645 static const efx_mae_mv_bit_desc_t __efx_mae_action_rule_mv_bit_desc_set[] = {
646 #define	EFX_MAE_MV_BIT_DESC(_name)					\
647 	[EFX_MAE_FIELD_##_name] =					\
648 	{								\
649 		B_TRUE,							\
650 		EFX_MAE_FIELD_ID_##_name,				\
651 		MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_OFST,		\
652 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_LBN,		\
653 		MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK_OFST,		\
654 		MAE_FIELD_MASK_VALUE_PAIRS_V2_##_name##_LBN,		\
655 	}
656 
657 	EFX_MAE_MV_BIT_DESC(HAS_OVLAN),
658 	EFX_MAE_MV_BIT_DESC(HAS_IVLAN),
659 	EFX_MAE_MV_BIT_DESC(ENC_HAS_OVLAN),
660 	EFX_MAE_MV_BIT_DESC(ENC_HAS_IVLAN),
661 
662 #undef EFX_MAE_MV_BIT_DESC
663 };
664 
665 	__checkReturn			efx_rc_t
666 efx_mae_mport_invalid(
667 	__out				efx_mport_sel_t *mportp)
668 {
669 	efx_dword_t dword;
670 	efx_rc_t rc;
671 
672 	if (mportp == NULL) {
673 		rc = EINVAL;
674 		goto fail1;
675 	}
676 
677 	EFX_POPULATE_DWORD_1(dword,
678 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_INVALID);
679 
680 	memset(mportp, 0, sizeof (*mportp));
681 	mportp->sel = dword.ed_u32[0];
682 
683 	return (0);
684 
685 fail1:
686 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
687 	return (rc);
688 }
689 
690 	__checkReturn			efx_rc_t
691 efx_mae_mport_by_phy_port(
692 	__in				uint32_t phy_port,
693 	__out				efx_mport_sel_t *mportp)
694 {
695 	efx_dword_t dword;
696 	efx_rc_t rc;
697 
698 	if (phy_port > EFX_MASK32(MAE_MPORT_SELECTOR_PPORT_ID)) {
699 		rc = EINVAL;
700 		goto fail1;
701 	}
702 
703 	EFX_POPULATE_DWORD_2(dword,
704 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
705 	    MAE_MPORT_SELECTOR_PPORT_ID, phy_port);
706 
707 	memset(mportp, 0, sizeof (*mportp));
708 	/*
709 	 * The constructed DWORD is little-endian,
710 	 * but the resulting value is meant to be
711 	 * passed to MCDIs, where it will undergo
712 	 * host-order to little endian conversion.
713 	 */
714 	mportp->sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
715 
716 	return (0);
717 
718 fail1:
719 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
720 	return (rc);
721 }
722 
723 	__checkReturn			efx_rc_t
724 efx_mae_mport_by_pcie_function(
725 	__in				uint32_t pf,
726 	__in				uint32_t vf,
727 	__out				efx_mport_sel_t *mportp)
728 {
729 	efx_dword_t dword;
730 	efx_rc_t rc;
731 
732 	rc = efx_mae_mport_by_pcie_mh_function(EFX_PCIE_INTERFACE_CALLER,
733 					       pf, vf, mportp);
734 	if (rc != 0)
735 		goto fail1;
736 
737 	return (0);
738 
739 fail1:
740 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
741 	return (rc);
742 }
743 
744 static	__checkReturn			efx_rc_t
745 efx_mae_intf_to_selector(
746 	__in				efx_pcie_interface_t intf,
747 	__out				uint32_t *selector_intfp)
748 {
749 	efx_rc_t rc;
750 
751 	switch (intf) {
752 	case EFX_PCIE_INTERFACE_HOST_PRIMARY:
753 		EFX_STATIC_ASSERT(MAE_MPORT_SELECTOR_HOST_PRIMARY <=
754 		    EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_INTF_ID));
755 		*selector_intfp = MAE_MPORT_SELECTOR_HOST_PRIMARY;
756 		break;
757 	case EFX_PCIE_INTERFACE_NIC_EMBEDDED:
758 		EFX_STATIC_ASSERT(MAE_MPORT_SELECTOR_NIC_EMBEDDED <=
759 		    EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_INTF_ID));
760 		*selector_intfp = MAE_MPORT_SELECTOR_NIC_EMBEDDED;
761 		break;
762 	case EFX_PCIE_INTERFACE_CALLER:
763 		EFX_STATIC_ASSERT(MAE_MPORT_SELECTOR_CALLER_INTF <=
764 		    EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_INTF_ID));
765 		*selector_intfp = MAE_MPORT_SELECTOR_CALLER_INTF;
766 		break;
767 	default:
768 		rc = EINVAL;
769 		goto fail1;
770 	}
771 
772 	return (0);
773 
774 fail1:
775 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
776 	return (rc);
777 }
778 
779 	__checkReturn			efx_rc_t
780 efx_mae_mport_by_pcie_mh_function(
781 	__in				efx_pcie_interface_t intf,
782 	__in				uint32_t pf,
783 	__in				uint32_t vf,
784 	__out				efx_mport_sel_t *mportp)
785 {
786 	uint32_t selector_intf;
787 	efx_dword_t dword;
788 	efx_rc_t rc;
789 
790 	EFX_STATIC_ASSERT(EFX_PCI_VF_INVALID ==
791 	    MAE_MPORT_SELECTOR_FUNC_VF_ID_NULL);
792 
793 	rc = efx_mae_intf_to_selector(intf, &selector_intf);
794 	if (rc != 0)
795 		goto fail1;
796 
797 	if (pf > EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_MH_PF_ID)) {
798 		rc = EINVAL;
799 		goto fail2;
800 	}
801 
802 	if (vf > EFX_MASK32(MAE_MPORT_SELECTOR_FUNC_VF_ID)) {
803 		rc = EINVAL;
804 		goto fail3;
805 	}
806 
807 
808 	EFX_POPULATE_DWORD_4(dword,
809 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_MH_FUNC,
810 	    MAE_MPORT_SELECTOR_FUNC_INTF_ID, selector_intf,
811 	    MAE_MPORT_SELECTOR_FUNC_MH_PF_ID, pf,
812 	    MAE_MPORT_SELECTOR_FUNC_VF_ID, vf);
813 
814 	memset(mportp, 0, sizeof (*mportp));
815 	mportp->sel = dword.ed_u32[0];
816 
817 	return (0);
818 
819 fail3:
820 	EFSYS_PROBE(fail3);
821 fail2:
822 	EFSYS_PROBE(fail2);
823 fail1:
824 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
825 	return (rc);
826 }
827 
828 static	__checkReturn			efx_rc_t
829 efx_mcdi_mae_mport_lookup(
830 	__in				efx_nic_t *enp,
831 	__in				const efx_mport_sel_t *mport_selectorp,
832 	__out				efx_mport_id_t *mport_idp)
833 {
834 	efx_mcdi_req_t req;
835 	EFX_MCDI_DECLARE_BUF(payload,
836 	    MC_CMD_MAE_MPORT_LOOKUP_IN_LEN,
837 	    MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN);
838 	efx_rc_t rc;
839 
840 	req.emr_cmd = MC_CMD_MAE_MPORT_LOOKUP;
841 	req.emr_in_buf = payload;
842 	req.emr_in_length = MC_CMD_MAE_MPORT_LOOKUP_IN_LEN;
843 	req.emr_out_buf = payload;
844 	req.emr_out_length = MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN;
845 
846 	MCDI_IN_SET_DWORD(req, MAE_MPORT_LOOKUP_IN_MPORT_SELECTOR,
847 	    mport_selectorp->sel);
848 
849 	efx_mcdi_execute(enp, &req);
850 
851 	if (req.emr_rc != 0) {
852 		rc = req.emr_rc;
853 		goto fail1;
854 	}
855 
856 	mport_idp->id = MCDI_OUT_DWORD(req, MAE_MPORT_LOOKUP_OUT_MPORT_ID);
857 
858 	return (0);
859 
860 fail1:
861 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
862 	return (rc);
863 }
864 
865 	__checkReturn			efx_rc_t
866 efx_mae_mport_id_by_selector(
867 	__in				efx_nic_t *enp,
868 	__in				const efx_mport_sel_t *mport_selectorp,
869 	__out				efx_mport_id_t *mport_idp)
870 {
871 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
872 	efx_rc_t rc;
873 
874 	if (encp->enc_mae_supported == B_FALSE) {
875 		rc = ENOTSUP;
876 		goto fail1;
877 	}
878 
879 	rc = efx_mcdi_mae_mport_lookup(enp, mport_selectorp, mport_idp);
880 	if (rc != 0)
881 		goto fail2;
882 
883 	return (0);
884 
885 fail2:
886 	EFSYS_PROBE(fail2);
887 fail1:
888 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
889 	return (rc);
890 }
891 
892 	__checkReturn			efx_rc_t
893 efx_mae_match_spec_recirc_id_set(
894 	__in				efx_mae_match_spec_t *spec,
895 	__in				uint8_t recirc_id)
896 {
897 	uint8_t full_mask = UINT8_MAX;
898 	const uint8_t *vp;
899 	const uint8_t *mp;
900 	efx_rc_t rc;
901 
902 	vp = (const uint8_t *)&recirc_id;
903 	mp = (const uint8_t *)&full_mask;
904 
905 	rc = efx_mae_match_spec_field_set(spec, EFX_MAE_FIELD_RECIRC_ID,
906 					  sizeof (recirc_id), vp,
907 					  sizeof (full_mask), mp);
908 	if (rc != 0)
909 		goto fail1;
910 
911 	return (0);
912 
913 fail1:
914 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
915 	return (rc);
916 }
917 
918 	__checkReturn			efx_rc_t
919 efx_mae_mport_by_id(
920 	__in				const efx_mport_id_t *mport_idp,
921 	__out				efx_mport_sel_t *mportp)
922 {
923 	efx_dword_t dword;
924 
925 	EFX_POPULATE_DWORD_2(dword,
926 	    MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_MPORT_ID,
927 	    MAE_MPORT_SELECTOR_MPORT_ID, mport_idp->id);
928 
929 	memset(mportp, 0, sizeof (*mportp));
930 	mportp->sel = __LE_TO_CPU_32(dword.ed_u32[0]);
931 
932 	return (0);
933 }
934 
935 	__checkReturn			efx_rc_t
936 efx_mae_match_spec_field_set(
937 	__in				efx_mae_match_spec_t *spec,
938 	__in				efx_mae_field_id_t field_id,
939 	__in				size_t value_size,
940 	__in_bcount(value_size)		const uint8_t *value,
941 	__in				size_t mask_size,
942 	__in_bcount(mask_size)		const uint8_t *mask)
943 {
944 	const efx_mae_mv_desc_t *descp;
945 	unsigned int desc_set_nentries;
946 	uint8_t *mvp;
947 	efx_rc_t rc;
948 
949 	switch (spec->emms_type) {
950 	case EFX_MAE_RULE_OUTER:
951 		desc_set_nentries =
952 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
953 		descp = &__efx_mae_outer_rule_mv_desc_set[field_id];
954 		mvp = spec->emms_mask_value_pairs.outer;
955 		break;
956 	case EFX_MAE_RULE_ACTION:
957 		desc_set_nentries =
958 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
959 		descp = &__efx_mae_action_rule_mv_desc_set[field_id];
960 		mvp = spec->emms_mask_value_pairs.action;
961 		break;
962 	default:
963 		rc = ENOTSUP;
964 		goto fail1;
965 	}
966 
967 	if ((unsigned int)field_id >= desc_set_nentries) {
968 		rc = EINVAL;
969 		goto fail2;
970 	}
971 
972 	if (descp->emmd_mask_size == 0) {
973 		/* The ID points to a gap in the array of field descriptors. */
974 		rc = EINVAL;
975 		goto fail3;
976 	}
977 
978 	if (value_size != descp->emmd_value_size) {
979 		rc = EINVAL;
980 		goto fail4;
981 	}
982 
983 	if (mask_size != descp->emmd_mask_size) {
984 		rc = EINVAL;
985 		goto fail5;
986 	}
987 
988 	if (descp->emmd_endianness == EFX_MAE_FIELD_BE) {
989 		unsigned int i;
990 
991 		/*
992 		 * The mask/value are in network (big endian) order.
993 		 * The MCDI request field is also big endian.
994 		 */
995 
996 		EFSYS_ASSERT3U(value_size, ==, mask_size);
997 
998 		for (i = 0; i < value_size; ++i) {
999 			uint8_t *v_bytep = mvp + descp->emmd_value_offset + i;
1000 			uint8_t *m_bytep = mvp + descp->emmd_mask_offset + i;
1001 
1002 			/*
1003 			 * Apply the mask (which may be all-zeros) to the value.
1004 			 *
1005 			 * If this API is provided with some value to set for a
1006 			 * given field in one specification and with some other
1007 			 * value to set for this field in another specification,
1008 			 * then, if the two masks are all-zeros, the field will
1009 			 * avoid being counted as a mismatch when comparing the
1010 			 * specifications using efx_mae_match_specs_equal() API.
1011 			 */
1012 			*v_bytep = value[i] & mask[i];
1013 			*m_bytep = mask[i];
1014 		}
1015 	} else {
1016 		efx_dword_t dword;
1017 
1018 		/*
1019 		 * The mask/value are in host byte order.
1020 		 * The MCDI request field is little endian.
1021 		 */
1022 		switch (value_size) {
1023 		case 4:
1024 			EFX_POPULATE_DWORD_1(dword,
1025 			    EFX_DWORD_0, *(const uint32_t *)value);
1026 
1027 			memcpy(mvp + descp->emmd_value_offset,
1028 			    &dword, sizeof (dword));
1029 			break;
1030 		default:
1031 			EFSYS_ASSERT(B_FALSE);
1032 		}
1033 
1034 		switch (mask_size) {
1035 		case 4:
1036 			EFX_POPULATE_DWORD_1(dword,
1037 			    EFX_DWORD_0, *(const uint32_t *)mask);
1038 
1039 			memcpy(mvp + descp->emmd_mask_offset,
1040 			    &dword, sizeof (dword));
1041 			break;
1042 		default:
1043 			EFSYS_ASSERT(B_FALSE);
1044 		}
1045 	}
1046 
1047 	return (0);
1048 
1049 fail5:
1050 	EFSYS_PROBE(fail5);
1051 fail4:
1052 	EFSYS_PROBE(fail4);
1053 fail3:
1054 	EFSYS_PROBE(fail3);
1055 fail2:
1056 	EFSYS_PROBE(fail2);
1057 fail1:
1058 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1059 	return (rc);
1060 }
1061 
1062 	__checkReturn			efx_rc_t
1063 efx_mae_match_spec_bit_set(
1064 	__in				efx_mae_match_spec_t *spec,
1065 	__in				efx_mae_field_id_t field_id,
1066 	__in				boolean_t value)
1067 {
1068 	const efx_mae_mv_bit_desc_t *bit_descp;
1069 	unsigned int bit_desc_set_nentries;
1070 	unsigned int byte_idx;
1071 	unsigned int bit_idx;
1072 	uint8_t *mvp;
1073 	efx_rc_t rc;
1074 
1075 	switch (spec->emms_type) {
1076 	case EFX_MAE_RULE_OUTER:
1077 		bit_desc_set_nentries =
1078 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_bit_desc_set);
1079 		bit_descp = &__efx_mae_outer_rule_mv_bit_desc_set[field_id];
1080 		mvp = spec->emms_mask_value_pairs.outer;
1081 		break;
1082 	case EFX_MAE_RULE_ACTION:
1083 		bit_desc_set_nentries =
1084 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_bit_desc_set);
1085 		bit_descp = &__efx_mae_action_rule_mv_bit_desc_set[field_id];
1086 		mvp = spec->emms_mask_value_pairs.action;
1087 		break;
1088 	default:
1089 		rc = ENOTSUP;
1090 		goto fail1;
1091 	}
1092 
1093 	if ((unsigned int)field_id >= bit_desc_set_nentries) {
1094 		rc = EINVAL;
1095 		goto fail2;
1096 	}
1097 
1098 	if (bit_descp->emmbd_entry_is_valid == B_FALSE) {
1099 		rc = EINVAL;
1100 		goto fail3;
1101 	}
1102 
1103 	byte_idx = bit_descp->emmbd_value_ofst + bit_descp->emmbd_value_lbn / 8;
1104 	bit_idx = bit_descp->emmbd_value_lbn % 8;
1105 
1106 	if (value != B_FALSE)
1107 		mvp[byte_idx] |= (1U << bit_idx);
1108 	else
1109 		mvp[byte_idx] &= ~(1U << bit_idx);
1110 
1111 	byte_idx = bit_descp->emmbd_mask_ofst + bit_descp->emmbd_mask_lbn / 8;
1112 	bit_idx = bit_descp->emmbd_mask_lbn % 8;
1113 	mvp[byte_idx] |= (1U << bit_idx);
1114 
1115 	return (0);
1116 
1117 fail3:
1118 	EFSYS_PROBE(fail3);
1119 fail2:
1120 	EFSYS_PROBE(fail2);
1121 fail1:
1122 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1123 	return (rc);
1124 }
1125 
1126 	__checkReturn			efx_rc_t
1127 efx_mae_match_spec_mport_set(
1128 	__in				efx_mae_match_spec_t *spec,
1129 	__in				const efx_mport_sel_t *valuep,
1130 	__in_opt			const efx_mport_sel_t *maskp)
1131 {
1132 	uint32_t full_mask = UINT32_MAX;
1133 	const uint8_t *vp;
1134 	const uint8_t *mp;
1135 	efx_rc_t rc;
1136 
1137 	if (valuep == NULL) {
1138 		rc = EINVAL;
1139 		goto fail1;
1140 	}
1141 
1142 	vp = (const uint8_t *)&valuep->sel;
1143 	if (maskp != NULL)
1144 		mp = (const uint8_t *)&maskp->sel;
1145 	else
1146 		mp = (const uint8_t *)&full_mask;
1147 
1148 	rc = efx_mae_match_spec_field_set(spec,
1149 	    EFX_MAE_FIELD_INGRESS_MPORT_SELECTOR,
1150 	    sizeof (valuep->sel), vp, sizeof (maskp->sel), mp);
1151 	if (rc != 0)
1152 		goto fail2;
1153 
1154 	return (0);
1155 
1156 fail2:
1157 	EFSYS_PROBE(fail2);
1158 fail1:
1159 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1160 	return (rc);
1161 }
1162 
1163 	__checkReturn			boolean_t
1164 efx_mae_match_specs_equal(
1165 	__in				const efx_mae_match_spec_t *left,
1166 	__in				const efx_mae_match_spec_t *right)
1167 {
1168 	return ((memcmp(left, right, sizeof (*left)) == 0) ? B_TRUE : B_FALSE);
1169 }
1170 
1171 #define	EFX_MASK_BIT_IS_SET(_mask, _mask_page_nbits, _bit)		\
1172 	    ((_mask)[(_bit) / (_mask_page_nbits)] &			\
1173 		    (1ULL << ((_bit) & ((_mask_page_nbits) - 1))))
1174 
1175 static					boolean_t
1176 efx_mask_is_prefix(
1177 	__in				size_t mask_nbytes,
1178 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
1179 {
1180 	boolean_t prev_bit_is_set = B_TRUE;
1181 	unsigned int i;
1182 
1183 	for (i = 0; i < 8 * mask_nbytes; ++i) {
1184 		boolean_t bit_is_set = EFX_MASK_BIT_IS_SET(maskp, 8, i);
1185 
1186 		if (!prev_bit_is_set && bit_is_set)
1187 			return B_FALSE;
1188 
1189 		prev_bit_is_set = bit_is_set;
1190 	}
1191 
1192 	return B_TRUE;
1193 }
1194 
1195 static					boolean_t
1196 efx_mask_is_all_ones(
1197 	__in				size_t mask_nbytes,
1198 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
1199 {
1200 	unsigned int i;
1201 	uint8_t t = ~0;
1202 
1203 	for (i = 0; i < mask_nbytes; ++i)
1204 		t &= maskp[i];
1205 
1206 	return (t == (uint8_t)(~0));
1207 }
1208 
1209 static					boolean_t
1210 efx_mask_is_all_zeros(
1211 	__in				size_t mask_nbytes,
1212 	__in_bcount(mask_nbytes)	const uint8_t *maskp)
1213 {
1214 	unsigned int i;
1215 	uint8_t t = 0;
1216 
1217 	for (i = 0; i < mask_nbytes; ++i)
1218 		t |= maskp[i];
1219 
1220 	return (t == 0);
1221 }
1222 
1223 	__checkReturn			boolean_t
1224 efx_mae_match_spec_is_valid(
1225 	__in				efx_nic_t *enp,
1226 	__in				const efx_mae_match_spec_t *spec)
1227 {
1228 	efx_mae_t *maep = enp->en_maep;
1229 	unsigned int field_ncaps = maep->em_max_nfields;
1230 	const efx_mae_field_cap_t *field_caps;
1231 	const efx_mae_mv_desc_t *desc_setp;
1232 	unsigned int desc_set_nentries;
1233 	const efx_mae_mv_bit_desc_t *bit_desc_setp;
1234 	unsigned int bit_desc_set_nentries;
1235 	boolean_t is_valid = B_TRUE;
1236 	efx_mae_field_id_t field_id;
1237 	const uint8_t *mvp;
1238 
1239 	switch (spec->emms_type) {
1240 	case EFX_MAE_RULE_OUTER:
1241 		field_caps = maep->em_outer_rule_field_caps;
1242 		desc_setp = __efx_mae_outer_rule_mv_desc_set;
1243 		desc_set_nentries =
1244 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
1245 		bit_desc_setp = __efx_mae_outer_rule_mv_bit_desc_set;
1246 		bit_desc_set_nentries =
1247 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_bit_desc_set);
1248 		mvp = spec->emms_mask_value_pairs.outer;
1249 		break;
1250 	case EFX_MAE_RULE_ACTION:
1251 		field_caps = maep->em_action_rule_field_caps;
1252 		desc_setp = __efx_mae_action_rule_mv_desc_set;
1253 		desc_set_nentries =
1254 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
1255 		bit_desc_setp = __efx_mae_action_rule_mv_bit_desc_set;
1256 		bit_desc_set_nentries =
1257 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_bit_desc_set);
1258 		mvp = spec->emms_mask_value_pairs.action;
1259 		break;
1260 	default:
1261 		return (B_FALSE);
1262 	}
1263 
1264 	if (field_caps == NULL)
1265 		return (B_FALSE);
1266 
1267 	for (field_id = 0; (unsigned int)field_id < desc_set_nentries;
1268 	     ++field_id) {
1269 		const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
1270 		efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
1271 		const uint8_t *alt_m_buf = mvp + descp->emmd_alt_mask_offset;
1272 		const uint8_t *m_buf = mvp + descp->emmd_mask_offset;
1273 		size_t alt_m_size = descp->emmd_alt_mask_size;
1274 		size_t m_size = descp->emmd_mask_size;
1275 
1276 		if (m_size == 0)
1277 			continue; /* Skip array gap */
1278 
1279 		if ((unsigned int)field_cap_id >= field_ncaps) {
1280 			/*
1281 			 * The FW has not reported capability status for
1282 			 * this field. Make sure that its mask is zeroed.
1283 			 */
1284 			is_valid = efx_mask_is_all_zeros(m_size, m_buf);
1285 			if (is_valid != B_FALSE)
1286 				continue;
1287 			else
1288 				break;
1289 		}
1290 
1291 		switch (field_caps[field_cap_id].emfc_support) {
1292 		case MAE_FIELD_SUPPORTED_MATCH_MASK:
1293 			is_valid = B_TRUE;
1294 			break;
1295 		case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
1296 			is_valid = efx_mask_is_prefix(m_size, m_buf);
1297 			break;
1298 		case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
1299 			is_valid = (efx_mask_is_all_ones(m_size, m_buf) ||
1300 			    efx_mask_is_all_zeros(m_size, m_buf));
1301 			break;
1302 		case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
1303 			is_valid = efx_mask_is_all_ones(m_size, m_buf);
1304 
1305 			if ((is_valid == B_FALSE) && (alt_m_size != 0)) {
1306 				/*
1307 				 * This field has an alternative one. The FW
1308 				 * reports ALWAYS for both implying that one
1309 				 * of them is required to have all-ones mask.
1310 				 *
1311 				 * The primary field's mask is incorrect; go
1312 				 * on to check that of the alternative field.
1313 				 */
1314 				is_valid = efx_mask_is_all_ones(alt_m_size,
1315 								alt_m_buf);
1316 			}
1317 			break;
1318 		case MAE_FIELD_SUPPORTED_MATCH_NEVER:
1319 		case MAE_FIELD_UNSUPPORTED:
1320 		default:
1321 			is_valid = efx_mask_is_all_zeros(m_size, m_buf);
1322 			break;
1323 		}
1324 
1325 		if (is_valid == B_FALSE)
1326 			return (B_FALSE);
1327 	}
1328 
1329 	for (field_id = 0; (unsigned int)field_id < bit_desc_set_nentries;
1330 	     ++field_id) {
1331 		const efx_mae_mv_bit_desc_t *bit_descp =
1332 		    &bit_desc_setp[field_id];
1333 		unsigned int byte_idx =
1334 		    bit_descp->emmbd_mask_ofst +
1335 		    bit_descp->emmbd_mask_lbn / 8;
1336 		unsigned int bit_idx =
1337 		    bit_descp->emmbd_mask_lbn % 8;
1338 		efx_mae_field_cap_id_t bit_cap_id =
1339 		    bit_descp->emmbd_bit_cap_id;
1340 
1341 		if (bit_descp->emmbd_entry_is_valid == B_FALSE)
1342 			continue; /* Skip array gap */
1343 
1344 		if ((unsigned int)bit_cap_id >= field_ncaps) {
1345 			/* No capability for this bit = unsupported. */
1346 			is_valid = ((mvp[byte_idx] & (1U << bit_idx)) == 0);
1347 			if (is_valid == B_FALSE)
1348 				break;
1349 			else
1350 				continue;
1351 		}
1352 
1353 		switch (field_caps[bit_cap_id].emfc_support) {
1354 		case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
1355 			is_valid = B_TRUE;
1356 			break;
1357 		case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
1358 			is_valid = ((mvp[byte_idx] & (1U << bit_idx)) != 0);
1359 			break;
1360 		case MAE_FIELD_SUPPORTED_MATCH_NEVER:
1361 		case MAE_FIELD_UNSUPPORTED:
1362 		default:
1363 			is_valid = ((mvp[byte_idx] & (1U << bit_idx)) == 0);
1364 			break;
1365 		}
1366 
1367 		if (is_valid == B_FALSE)
1368 			break;
1369 	}
1370 
1371 	return (is_valid);
1372 }
1373 
1374 	__checkReturn			efx_rc_t
1375 efx_mae_action_set_spec_init(
1376 	__in				efx_nic_t *enp,
1377 	__out				efx_mae_actions_t **specp)
1378 {
1379 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
1380 	efx_mae_actions_t *spec;
1381 	efx_rc_t rc;
1382 
1383 	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), spec);
1384 	if (spec == NULL) {
1385 		rc = ENOMEM;
1386 		goto fail1;
1387 	}
1388 
1389 	spec->ema_rsrc.emar_dst_mac_id.id = EFX_MAE_RSRC_ID_INVALID;
1390 	spec->ema_rsrc.emar_src_mac_id.id = EFX_MAE_RSRC_ID_INVALID;
1391 	spec->ema_rsrc.emar_eh_id.id = EFX_MAE_RSRC_ID_INVALID;
1392 	spec->ema_rsrc.emar_counter_id.id = EFX_MAE_RSRC_ID_INVALID;
1393 
1394 	/*
1395 	 * Helpers which populate v2 actions must reject them when v2 is not
1396 	 * supported. As they have no EFX NIC argument, save v2 status here.
1397 	 */
1398 	spec->ema_v2_is_supported = encp->enc_mae_aset_v2_supported;
1399 
1400 	*specp = spec;
1401 
1402 	return (0);
1403 
1404 fail1:
1405 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1406 	return (rc);
1407 }
1408 
1409 					void
1410 efx_mae_action_set_spec_fini(
1411 	__in				efx_nic_t *enp,
1412 	__in				efx_mae_actions_t *spec)
1413 {
1414 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
1415 }
1416 
1417 static	__checkReturn			efx_rc_t
1418 efx_mae_action_set_no_op(
1419 	__in				efx_mae_actions_t *spec,
1420 	__in				size_t arg_size,
1421 	__in_bcount(arg_size)		const uint8_t *arg)
1422 {
1423 	efx_rc_t rc;
1424 
1425 	_NOTE(ARGUNUSED(spec))
1426 
1427 	if (arg_size != 0) {
1428 		rc = EINVAL;
1429 		goto fail1;
1430 	}
1431 
1432 	if (arg != NULL) {
1433 		rc = EINVAL;
1434 		goto fail2;
1435 	}
1436 
1437 	/* This action does not have any arguments, so do nothing here. */
1438 
1439 	return (0);
1440 
1441 fail2:
1442 	EFSYS_PROBE(fail2);
1443 fail1:
1444 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1445 	return (rc);
1446 }
1447 
1448 static	__checkReturn			efx_rc_t
1449 efx_mae_action_set_add_vlan_pop(
1450 	__in				efx_mae_actions_t *spec,
1451 	__in				size_t arg_size,
1452 	__in_bcount(arg_size)		const uint8_t *arg)
1453 {
1454 	efx_rc_t rc;
1455 
1456 	if (arg_size != 0) {
1457 		rc = EINVAL;
1458 		goto fail1;
1459 	}
1460 
1461 	if (arg != NULL) {
1462 		rc = EINVAL;
1463 		goto fail2;
1464 	}
1465 
1466 	if (spec->ema_n_vlan_tags_to_pop == EFX_MAE_VLAN_POP_MAX_NTAGS) {
1467 		rc = ENOTSUP;
1468 		goto fail3;
1469 	}
1470 
1471 	++spec->ema_n_vlan_tags_to_pop;
1472 
1473 	return (0);
1474 
1475 fail3:
1476 	EFSYS_PROBE(fail3);
1477 fail2:
1478 	EFSYS_PROBE(fail2);
1479 fail1:
1480 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1481 	return (rc);
1482 }
1483 
1484 static	__checkReturn			efx_rc_t
1485 efx_mae_action_set_add_vlan_push(
1486 	__in				efx_mae_actions_t *spec,
1487 	__in				size_t arg_size,
1488 	__in_bcount(arg_size)		const uint8_t *arg)
1489 {
1490 	unsigned int n_tags = spec->ema_n_vlan_tags_to_push;
1491 	efx_rc_t rc;
1492 
1493 	if (arg_size != sizeof (*spec->ema_vlan_push_descs)) {
1494 		rc = EINVAL;
1495 		goto fail1;
1496 	}
1497 
1498 	if (arg == NULL) {
1499 		rc = EINVAL;
1500 		goto fail2;
1501 	}
1502 
1503 	if (n_tags == EFX_MAE_VLAN_PUSH_MAX_NTAGS) {
1504 		rc = ENOTSUP;
1505 		goto fail3;
1506 	}
1507 
1508 	memcpy(&spec->ema_vlan_push_descs[n_tags], arg, arg_size);
1509 	++(spec->ema_n_vlan_tags_to_push);
1510 
1511 	return (0);
1512 
1513 fail3:
1514 	EFSYS_PROBE(fail3);
1515 fail2:
1516 	EFSYS_PROBE(fail2);
1517 fail1:
1518 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1519 	return (rc);
1520 }
1521 
1522 static	__checkReturn			efx_rc_t
1523 efx_mae_action_set_add_count(
1524 	__in				efx_mae_actions_t *spec,
1525 	__in				size_t arg_size,
1526 	__in_bcount(arg_size)		const uint8_t *arg)
1527 {
1528 	efx_rc_t rc;
1529 
1530 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
1531 			  MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
1532 
1533 	/*
1534 	 * Preparing an action set spec to update a counter requires
1535 	 * two steps: first add this action to the action spec, and then
1536 	 * add the counter ID to the spec. This allows validity checking
1537 	 * and resource allocation to be done separately.
1538 	 *
1539 	 * In order to fill in the counter ID, the caller is supposed to invoke
1540 	 * efx_mae_action_set_fill_in_counter_id(). If they do not do that,
1541 	 * efx_mae_action_set_alloc() invocation will throw an error.
1542 	 *
1543 	 * For now, no arguments are supposed to be handled.
1544 	 */
1545 
1546 	if (arg_size != 0) {
1547 		rc = EINVAL;
1548 		goto fail1;
1549 	}
1550 
1551 	if (arg != NULL) {
1552 		rc = EINVAL;
1553 		goto fail2;
1554 	}
1555 
1556 	++(spec->ema_n_count_actions);
1557 
1558 	return (0);
1559 
1560 fail2:
1561 	EFSYS_PROBE(fail2);
1562 fail1:
1563 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1564 	return (rc);
1565 }
1566 
1567 static	__checkReturn			efx_rc_t
1568 efx_mae_action_set_add_mark(
1569 	__in				efx_mae_actions_t *spec,
1570 	__in				size_t arg_size,
1571 	__in_bcount(arg_size)		const uint8_t *arg)
1572 {
1573 	efx_rc_t rc;
1574 
1575 	if (arg_size != sizeof (spec->ema_mark_value)) {
1576 		rc = EINVAL;
1577 		goto fail1;
1578 	}
1579 
1580 	if (arg == NULL) {
1581 		rc = EINVAL;
1582 		goto fail2;
1583 	}
1584 
1585 	memcpy(&spec->ema_mark_value, arg, arg_size);
1586 
1587 	return (0);
1588 
1589 fail2:
1590 	EFSYS_PROBE(fail2);
1591 fail1:
1592 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1593 	return (rc);
1594 }
1595 
1596 static	__checkReturn			efx_rc_t
1597 efx_mae_action_set_add_deliver(
1598 	__in				efx_mae_actions_t *spec,
1599 	__in				size_t arg_size,
1600 	__in_bcount(arg_size)		const uint8_t *arg)
1601 {
1602 	efx_rc_t rc;
1603 
1604 	if (arg_size != sizeof (spec->ema_deliver_mport)) {
1605 		rc = EINVAL;
1606 		goto fail1;
1607 	}
1608 
1609 	if (arg == NULL) {
1610 		rc = EINVAL;
1611 		goto fail2;
1612 	}
1613 
1614 	memcpy(&spec->ema_deliver_mport, arg, arg_size);
1615 
1616 	return (0);
1617 
1618 fail2:
1619 	EFSYS_PROBE(fail2);
1620 fail1:
1621 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1622 	return (rc);
1623 }
1624 
1625 typedef struct efx_mae_action_desc_s {
1626 	/* Action specific handler */
1627 	efx_rc_t	(*emad_add)(efx_mae_actions_t *,
1628 				    size_t, const uint8_t *);
1629 } efx_mae_action_desc_t;
1630 
1631 static const efx_mae_action_desc_t efx_mae_actions[EFX_MAE_NACTIONS] = {
1632 	[EFX_MAE_ACTION_DECAP] = {
1633 		.emad_add = efx_mae_action_set_no_op
1634 	},
1635 	[EFX_MAE_ACTION_VLAN_POP] = {
1636 		.emad_add = efx_mae_action_set_add_vlan_pop
1637 	},
1638 	[EFX_MAE_ACTION_SET_DST_MAC] = {
1639 		.emad_add = efx_mae_action_set_no_op
1640 	},
1641 	[EFX_MAE_ACTION_SET_SRC_MAC] = {
1642 		.emad_add = efx_mae_action_set_no_op
1643 	},
1644 	[EFX_MAE_ACTION_DECR_IP_TTL] = {
1645 		.emad_add = efx_mae_action_set_no_op
1646 	},
1647 	[EFX_MAE_ACTION_VLAN_PUSH] = {
1648 		.emad_add = efx_mae_action_set_add_vlan_push
1649 	},
1650 	[EFX_MAE_ACTION_ENCAP] = {
1651 		.emad_add = efx_mae_action_set_no_op
1652 	},
1653 	[EFX_MAE_ACTION_COUNT] = {
1654 		.emad_add = efx_mae_action_set_add_count
1655 	},
1656 	[EFX_MAE_ACTION_FLAG] = {
1657 		.emad_add = efx_mae_action_set_no_op
1658 	},
1659 	[EFX_MAE_ACTION_MARK] = {
1660 		.emad_add = efx_mae_action_set_add_mark
1661 	},
1662 	[EFX_MAE_ACTION_DELIVER] = {
1663 		.emad_add = efx_mae_action_set_add_deliver
1664 	}
1665 };
1666 
1667 static const uint32_t efx_mae_action_ordered_map =
1668 	(1U << EFX_MAE_ACTION_DECAP) |
1669 	(1U << EFX_MAE_ACTION_VLAN_POP) |
1670 	(1U << EFX_MAE_ACTION_SET_DST_MAC) |
1671 	(1U << EFX_MAE_ACTION_SET_SRC_MAC) |
1672 	(1U << EFX_MAE_ACTION_DECR_IP_TTL) |
1673 	(1U << EFX_MAE_ACTION_VLAN_PUSH) |
1674 	/*
1675 	 * HW will conduct action COUNT after
1676 	 * the matching packet has been modified by
1677 	 * length-affecting actions except for ENCAP.
1678 	 */
1679 	(1U << EFX_MAE_ACTION_COUNT) |
1680 	(1U << EFX_MAE_ACTION_ENCAP) |
1681 	(1U << EFX_MAE_ACTION_FLAG) |
1682 	(1U << EFX_MAE_ACTION_MARK) |
1683 	(1U << EFX_MAE_ACTION_DELIVER);
1684 
1685 /*
1686  * These actions must not be added after DELIVER, but
1687  * they can have any place among the rest of
1688  * strictly ordered actions.
1689  */
1690 static const uint32_t efx_mae_action_nonstrict_map =
1691 	(1U << EFX_MAE_ACTION_COUNT) |
1692 	(1U << EFX_MAE_ACTION_FLAG) |
1693 	(1U << EFX_MAE_ACTION_MARK);
1694 
1695 static const uint32_t efx_mae_action_repeat_map =
1696 	(1U << EFX_MAE_ACTION_VLAN_POP) |
1697 	(1U << EFX_MAE_ACTION_VLAN_PUSH) |
1698 	(1U << EFX_MAE_ACTION_COUNT);
1699 
1700 /*
1701  * Add an action to an action set.
1702  *
1703  * This has to be invoked in the desired action order.
1704  * An out-of-order action request will be turned down.
1705  */
1706 static	__checkReturn			efx_rc_t
1707 efx_mae_action_set_spec_populate(
1708 	__in				efx_mae_actions_t *spec,
1709 	__in				efx_mae_action_t type,
1710 	__in				size_t arg_size,
1711 	__in_bcount(arg_size)		const uint8_t *arg)
1712 {
1713 	uint32_t action_mask;
1714 	efx_rc_t rc;
1715 
1716 	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
1717 	    (sizeof (efx_mae_action_ordered_map) * 8));
1718 	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
1719 	    (sizeof (efx_mae_action_repeat_map) * 8));
1720 
1721 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_DELIVER + 1 == EFX_MAE_NACTIONS);
1722 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_FLAG + 1 == EFX_MAE_ACTION_MARK);
1723 	EFX_STATIC_ASSERT(EFX_MAE_ACTION_MARK + 1 == EFX_MAE_ACTION_DELIVER);
1724 
1725 	if (type >= EFX_ARRAY_SIZE(efx_mae_actions)) {
1726 		rc = EINVAL;
1727 		goto fail1;
1728 	}
1729 
1730 	action_mask = (1U << type);
1731 
1732 	if ((spec->ema_actions & action_mask) != 0) {
1733 		/* The action set already contains this action. */
1734 		if ((efx_mae_action_repeat_map & action_mask) == 0) {
1735 			/* Cannot add another non-repeatable action. */
1736 			rc = ENOTSUP;
1737 			goto fail2;
1738 		}
1739 	}
1740 
1741 	if ((efx_mae_action_ordered_map & action_mask) != 0) {
1742 		uint32_t strict_ordered_map =
1743 		    efx_mae_action_ordered_map & ~efx_mae_action_nonstrict_map;
1744 		uint32_t later_actions_mask =
1745 		    strict_ordered_map & ~(action_mask | (action_mask - 1));
1746 
1747 		if ((spec->ema_actions & later_actions_mask) != 0) {
1748 			/* Cannot add an action after later ordered actions. */
1749 			rc = ENOTSUP;
1750 			goto fail3;
1751 		}
1752 	}
1753 
1754 	if (efx_mae_actions[type].emad_add != NULL) {
1755 		rc = efx_mae_actions[type].emad_add(spec, arg_size, arg);
1756 		if (rc != 0)
1757 			goto fail4;
1758 	}
1759 
1760 	spec->ema_actions |= action_mask;
1761 
1762 	return (0);
1763 
1764 fail4:
1765 	EFSYS_PROBE(fail4);
1766 fail3:
1767 	EFSYS_PROBE(fail3);
1768 fail2:
1769 	EFSYS_PROBE(fail2);
1770 fail1:
1771 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1772 	return (rc);
1773 }
1774 
1775 	__checkReturn			efx_rc_t
1776 efx_mae_action_set_populate_decap(
1777 	__in				efx_mae_actions_t *spec)
1778 {
1779 	return (efx_mae_action_set_spec_populate(spec,
1780 	    EFX_MAE_ACTION_DECAP, 0, NULL));
1781 }
1782 
1783 	__checkReturn			efx_rc_t
1784 efx_mae_action_set_populate_vlan_pop(
1785 	__in				efx_mae_actions_t *spec)
1786 {
1787 	return (efx_mae_action_set_spec_populate(spec,
1788 	    EFX_MAE_ACTION_VLAN_POP, 0, NULL));
1789 }
1790 
1791 	__checkReturn			efx_rc_t
1792 efx_mae_action_set_populate_set_dst_mac(
1793 	__in				efx_mae_actions_t *spec)
1794 {
1795 	efx_rc_t rc;
1796 
1797 	if (spec->ema_v2_is_supported == B_FALSE) {
1798 		rc = ENOTSUP;
1799 		goto fail1;
1800 	}
1801 
1802 	return (efx_mae_action_set_spec_populate(spec,
1803 	    EFX_MAE_ACTION_SET_DST_MAC, 0, NULL));
1804 
1805 fail1:
1806 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1807 	return (rc);
1808 }
1809 
1810 	__checkReturn			efx_rc_t
1811 efx_mae_action_set_populate_set_src_mac(
1812 	__in				efx_mae_actions_t *spec)
1813 {
1814 	efx_rc_t rc;
1815 
1816 	if (spec->ema_v2_is_supported == B_FALSE) {
1817 		rc = ENOTSUP;
1818 		goto fail1;
1819 	}
1820 
1821 	return (efx_mae_action_set_spec_populate(spec,
1822 	    EFX_MAE_ACTION_SET_SRC_MAC, 0, NULL));
1823 
1824 fail1:
1825 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1826 	return (rc);
1827 }
1828 
1829 	__checkReturn			efx_rc_t
1830 efx_mae_action_set_populate_decr_ip_ttl(
1831 	__in				efx_mae_actions_t *spec)
1832 {
1833 	efx_rc_t rc;
1834 
1835 	if (spec->ema_v2_is_supported == B_FALSE) {
1836 		rc = ENOTSUP;
1837 		goto fail1;
1838 	}
1839 
1840 	return (efx_mae_action_set_spec_populate(spec,
1841 	    EFX_MAE_ACTION_DECR_IP_TTL, 0, NULL));
1842 
1843 fail1:
1844 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1845 	return (rc);
1846 }
1847 
1848 	__checkReturn			efx_rc_t
1849 efx_mae_action_set_populate_vlan_push(
1850 	__in				efx_mae_actions_t *spec,
1851 	__in				uint16_t tpid_be,
1852 	__in				uint16_t tci_be)
1853 {
1854 	efx_mae_action_vlan_push_t action;
1855 	const uint8_t *arg = (const uint8_t *)&action;
1856 
1857 	action.emavp_tpid_be = tpid_be;
1858 	action.emavp_tci_be = tci_be;
1859 
1860 	return (efx_mae_action_set_spec_populate(spec,
1861 	    EFX_MAE_ACTION_VLAN_PUSH, sizeof (action), arg));
1862 }
1863 
1864 	__checkReturn			efx_rc_t
1865 efx_mae_action_set_populate_encap(
1866 	__in				efx_mae_actions_t *spec)
1867 {
1868 	/*
1869 	 * There is no argument to pass encap. header ID, thus, one does not
1870 	 * need to allocate an encap. header while parsing application input.
1871 	 * This is useful since building an action set may be done simply to
1872 	 * validate a rule, whilst resource allocation usually consumes time.
1873 	 */
1874 	return (efx_mae_action_set_spec_populate(spec,
1875 	    EFX_MAE_ACTION_ENCAP, 0, NULL));
1876 }
1877 
1878 	__checkReturn			efx_rc_t
1879 efx_mae_action_set_populate_count(
1880 	__in				efx_mae_actions_t *spec)
1881 {
1882 	/*
1883 	 * There is no argument to pass counter ID, thus, one does not
1884 	 * need to allocate a counter while parsing application input.
1885 	 * This is useful since building an action set may be done simply to
1886 	 * validate a rule, whilst resource allocation usually consumes time.
1887 	 */
1888 	return (efx_mae_action_set_spec_populate(spec,
1889 	    EFX_MAE_ACTION_COUNT, 0, NULL));
1890 }
1891 
1892 	__checkReturn			efx_rc_t
1893 efx_mae_action_set_populate_flag(
1894 	__in				efx_mae_actions_t *spec)
1895 {
1896 	return (efx_mae_action_set_spec_populate(spec,
1897 	    EFX_MAE_ACTION_FLAG, 0, NULL));
1898 }
1899 
1900 	__checkReturn			efx_rc_t
1901 efx_mae_action_set_populate_mark(
1902 	__in				efx_mae_actions_t *spec,
1903 	__in				uint32_t mark_value)
1904 {
1905 	const uint8_t *arg = (const uint8_t *)&mark_value;
1906 
1907 	return (efx_mae_action_set_spec_populate(spec,
1908 	    EFX_MAE_ACTION_MARK, sizeof (mark_value), arg));
1909 }
1910 
1911 	__checkReturn			efx_rc_t
1912 efx_mae_action_set_populate_deliver(
1913 	__in				efx_mae_actions_t *spec,
1914 	__in				const efx_mport_sel_t *mportp)
1915 {
1916 	const uint8_t *arg;
1917 	efx_rc_t rc;
1918 
1919 	if (mportp == NULL) {
1920 		rc = EINVAL;
1921 		goto fail1;
1922 	}
1923 
1924 	arg = (const uint8_t *)&mportp->sel;
1925 
1926 	return (efx_mae_action_set_spec_populate(spec,
1927 	    EFX_MAE_ACTION_DELIVER, sizeof (mportp->sel), arg));
1928 
1929 fail1:
1930 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1931 	return (rc);
1932 }
1933 
1934 	__checkReturn			efx_rc_t
1935 efx_mae_action_set_populate_drop(
1936 	__in				efx_mae_actions_t *spec)
1937 {
1938 	efx_mport_sel_t mport;
1939 	const uint8_t *arg;
1940 	efx_dword_t dword;
1941 
1942 	EFX_POPULATE_DWORD_1(dword,
1943 	    MAE_MPORT_SELECTOR_FLAT, MAE_MPORT_SELECTOR_NULL);
1944 
1945 	/*
1946 	 * The constructed DWORD is little-endian,
1947 	 * but the resulting value is meant to be
1948 	 * passed to MCDIs, where it will undergo
1949 	 * host-order to little endian conversion.
1950 	 */
1951 	mport.sel = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
1952 
1953 	arg = (const uint8_t *)&mport.sel;
1954 
1955 	return (efx_mae_action_set_spec_populate(spec,
1956 	    EFX_MAE_ACTION_DELIVER, sizeof (mport.sel), arg));
1957 }
1958 
1959 	__checkReturn			boolean_t
1960 efx_mae_action_set_specs_equal(
1961 	__in				const efx_mae_actions_t *left,
1962 	__in				const efx_mae_actions_t *right)
1963 {
1964 	size_t cmp_size = EFX_FIELD_OFFSET(efx_mae_actions_t, ema_rsrc);
1965 
1966 	/*
1967 	 * An action set specification consists of two parts. The first part
1968 	 * indicates what actions are included in the action set, as well as
1969 	 * extra quantitative values (in example, the number of VLAN tags to
1970 	 * push). The second part comprises resource IDs used by the actions.
1971 	 *
1972 	 * A resource, in example, a counter, is allocated from the hardware
1973 	 * by the client, and it's the client who is responsible for keeping
1974 	 * track of allocated resources and comparing resource IDs if needed.
1975 	 *
1976 	 * In this API, don't compare resource IDs in the two specifications.
1977 	 */
1978 
1979 	return ((memcmp(left, right, cmp_size) == 0) ? B_TRUE : B_FALSE);
1980 }
1981 
1982 	__checkReturn			efx_rc_t
1983 efx_mae_match_specs_class_cmp(
1984 	__in				efx_nic_t *enp,
1985 	__in				const efx_mae_match_spec_t *left,
1986 	__in				const efx_mae_match_spec_t *right,
1987 	__out				boolean_t *have_same_classp)
1988 {
1989 	efx_mae_t *maep = enp->en_maep;
1990 	unsigned int field_ncaps = maep->em_max_nfields;
1991 	const efx_mae_field_cap_t *field_caps;
1992 	const efx_mae_mv_desc_t *desc_setp;
1993 	unsigned int desc_set_nentries;
1994 	const efx_mae_mv_bit_desc_t *bit_desc_setp;
1995 	unsigned int bit_desc_set_nentries;
1996 	boolean_t have_same_class = B_TRUE;
1997 	efx_mae_field_id_t field_id;
1998 	const uint8_t *mvpl;
1999 	const uint8_t *mvpr;
2000 	efx_rc_t rc;
2001 
2002 	switch (left->emms_type) {
2003 	case EFX_MAE_RULE_OUTER:
2004 		field_caps = maep->em_outer_rule_field_caps;
2005 		desc_setp = __efx_mae_outer_rule_mv_desc_set;
2006 		desc_set_nentries =
2007 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_desc_set);
2008 		bit_desc_setp = __efx_mae_outer_rule_mv_bit_desc_set;
2009 		bit_desc_set_nentries =
2010 		    EFX_ARRAY_SIZE(__efx_mae_outer_rule_mv_bit_desc_set);
2011 		mvpl = left->emms_mask_value_pairs.outer;
2012 		mvpr = right->emms_mask_value_pairs.outer;
2013 		break;
2014 	case EFX_MAE_RULE_ACTION:
2015 		field_caps = maep->em_action_rule_field_caps;
2016 		desc_setp = __efx_mae_action_rule_mv_desc_set;
2017 		desc_set_nentries =
2018 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_desc_set);
2019 		bit_desc_setp = __efx_mae_action_rule_mv_bit_desc_set;
2020 		bit_desc_set_nentries =
2021 		    EFX_ARRAY_SIZE(__efx_mae_action_rule_mv_bit_desc_set);
2022 		mvpl = left->emms_mask_value_pairs.action;
2023 		mvpr = right->emms_mask_value_pairs.action;
2024 		break;
2025 	default:
2026 		rc = ENOTSUP;
2027 		goto fail1;
2028 	}
2029 
2030 	if (field_caps == NULL) {
2031 		rc = EAGAIN;
2032 		goto fail2;
2033 	}
2034 
2035 	if (left->emms_type != right->emms_type ||
2036 	    left->emms_prio != right->emms_prio) {
2037 		/*
2038 		 * Rules of different types can never map to the same class.
2039 		 *
2040 		 * The FW can support some set of match criteria for one
2041 		 * priority and not support the very same set for
2042 		 * another priority. Thus, two rules which have
2043 		 * different priorities can never map to
2044 		 * the same class.
2045 		 */
2046 		*have_same_classp = B_FALSE;
2047 		return (0);
2048 	}
2049 
2050 	for (field_id = 0; (unsigned int)field_id < desc_set_nentries;
2051 	     ++field_id) {
2052 		const efx_mae_mv_desc_t *descp = &desc_setp[field_id];
2053 		efx_mae_field_cap_id_t field_cap_id = descp->emmd_field_cap_id;
2054 		const uint8_t *lmaskp = mvpl + descp->emmd_mask_offset;
2055 		const uint8_t *rmaskp = mvpr + descp->emmd_mask_offset;
2056 		size_t mask_size = descp->emmd_mask_size;
2057 		const uint8_t *lvalp = mvpl + descp->emmd_value_offset;
2058 		const uint8_t *rvalp = mvpr + descp->emmd_value_offset;
2059 		size_t value_size = descp->emmd_value_size;
2060 
2061 		if (mask_size == 0)
2062 			continue; /* Skip array gap */
2063 
2064 		if ((unsigned int)field_cap_id >= field_ncaps) {
2065 			/*
2066 			 * The FW has not reported capability status for this
2067 			 * field. It's unknown whether any difference between
2068 			 * the two masks / values affects the class. The only
2069 			 * case when the class must be the same is when these
2070 			 * mask-value pairs match. Otherwise, report mismatch.
2071 			 */
2072 			if ((memcmp(lmaskp, rmaskp, mask_size) == 0) &&
2073 			    (memcmp(lvalp, rvalp, value_size) == 0))
2074 				continue;
2075 			else
2076 				break;
2077 		}
2078 
2079 		if (field_caps[field_cap_id].emfc_mask_affects_class) {
2080 			if (memcmp(lmaskp, rmaskp, mask_size) != 0) {
2081 				have_same_class = B_FALSE;
2082 				break;
2083 			}
2084 		}
2085 
2086 		if (field_caps[field_cap_id].emfc_match_affects_class) {
2087 			if (memcmp(lvalp, rvalp, value_size) != 0) {
2088 				have_same_class = B_FALSE;
2089 				break;
2090 			}
2091 		}
2092 	}
2093 
2094 	if (have_same_class == B_FALSE)
2095 		goto done;
2096 
2097 	for (field_id = 0; (unsigned int)field_id < bit_desc_set_nentries;
2098 	     ++field_id) {
2099 		const efx_mae_mv_bit_desc_t *bit_descp =
2100 		    &bit_desc_setp[field_id];
2101 		efx_mae_field_cap_id_t bit_cap_id =
2102 		    bit_descp->emmbd_bit_cap_id;
2103 		unsigned int byte_idx;
2104 		unsigned int bit_idx;
2105 
2106 		if (bit_descp->emmbd_entry_is_valid == B_FALSE)
2107 			continue; /* Skip array gap */
2108 
2109 		if ((unsigned int)bit_cap_id >= field_ncaps)
2110 			break;
2111 
2112 		byte_idx =
2113 		    bit_descp->emmbd_mask_ofst +
2114 		    bit_descp->emmbd_mask_lbn / 8;
2115 		bit_idx =
2116 		    bit_descp->emmbd_mask_lbn % 8;
2117 
2118 		if (field_caps[bit_cap_id].emfc_mask_affects_class &&
2119 		    (mvpl[byte_idx] & (1U << bit_idx)) !=
2120 		    (mvpr[byte_idx] & (1U << bit_idx))) {
2121 			have_same_class = B_FALSE;
2122 			break;
2123 		}
2124 
2125 		byte_idx =
2126 		    bit_descp->emmbd_value_ofst +
2127 		    bit_descp->emmbd_value_lbn / 8;
2128 		bit_idx =
2129 		    bit_descp->emmbd_value_lbn % 8;
2130 
2131 		if (field_caps[bit_cap_id].emfc_match_affects_class &&
2132 		    (mvpl[byte_idx] & (1U << bit_idx)) !=
2133 		    (mvpr[byte_idx] & (1U << bit_idx))) {
2134 			have_same_class = B_FALSE;
2135 			break;
2136 		}
2137 	}
2138 
2139 done:
2140 	*have_same_classp = have_same_class;
2141 
2142 	return (0);
2143 
2144 fail2:
2145 	EFSYS_PROBE(fail2);
2146 fail1:
2147 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2148 	return (rc);
2149 }
2150 
2151 	__checkReturn			efx_rc_t
2152 efx_mae_outer_rule_recirc_id_set(
2153 	__in				efx_mae_match_spec_t *spec,
2154 	__in				uint8_t recirc_id)
2155 {
2156 	efx_rc_t rc;
2157 
2158 	if (spec->emms_type != EFX_MAE_RULE_OUTER) {
2159 		rc = EINVAL;
2160 		goto fail1;
2161 	}
2162 
2163 	spec->emms_outer_rule_recirc_id = recirc_id;
2164 
2165 	return (0);
2166 
2167 fail1:
2168 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2169 	return (rc);
2170 }
2171 
2172 	__checkReturn		efx_rc_t
2173 efx_mae_outer_rule_insert(
2174 	__in			efx_nic_t *enp,
2175 	__in			const efx_mae_match_spec_t *spec,
2176 	__in			efx_tunnel_protocol_t encap_type,
2177 	__out			efx_mae_rule_id_t *or_idp)
2178 {
2179 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2180 	efx_mcdi_req_t req;
2181 	EFX_MCDI_DECLARE_BUF(payload,
2182 	    MC_CMD_MAE_OUTER_RULE_INSERT_IN_LENMAX_MCDI2,
2183 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN);
2184 	uint32_t encap_type_mcdi;
2185 	efx_mae_rule_id_t or_id;
2186 	size_t offset;
2187 	efx_rc_t rc;
2188 
2189 	EFX_STATIC_ASSERT(sizeof (or_idp->id) ==
2190 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OR_ID_LEN);
2191 
2192 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
2193 	    MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL);
2194 
2195 	if (encp->enc_mae_supported == B_FALSE) {
2196 		rc = ENOTSUP;
2197 		goto fail1;
2198 	}
2199 
2200 	if (spec->emms_type != EFX_MAE_RULE_OUTER) {
2201 		rc = EINVAL;
2202 		goto fail2;
2203 	}
2204 
2205 	switch (encap_type) {
2206 	case EFX_TUNNEL_PROTOCOL_NONE:
2207 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NONE;
2208 		break;
2209 	case EFX_TUNNEL_PROTOCOL_VXLAN:
2210 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_VXLAN;
2211 		break;
2212 	case EFX_TUNNEL_PROTOCOL_GENEVE:
2213 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_GENEVE;
2214 		break;
2215 	case EFX_TUNNEL_PROTOCOL_NVGRE:
2216 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NVGRE;
2217 		break;
2218 	default:
2219 		rc = ENOTSUP;
2220 		goto fail3;
2221 	}
2222 
2223 	req.emr_cmd = MC_CMD_MAE_OUTER_RULE_INSERT;
2224 	req.emr_in_buf = payload;
2225 	req.emr_in_length = MC_CMD_MAE_OUTER_RULE_INSERT_IN_LENMAX_MCDI2;
2226 	req.emr_out_buf = payload;
2227 	req.emr_out_length = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN;
2228 
2229 	MCDI_IN_SET_DWORD(req,
2230 	    MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE, encap_type_mcdi);
2231 
2232 	MCDI_IN_SET_DWORD(req, MAE_OUTER_RULE_INSERT_IN_PRIO, spec->emms_prio);
2233 
2234 	/*
2235 	 * Mask-value pairs have been stored in the byte order needed for the
2236 	 * MCDI request and are thus safe to be copied directly to the buffer.
2237 	 * The library cares about byte order in efx_mae_match_spec_field_set().
2238 	 */
2239 	EFX_STATIC_ASSERT(sizeof (spec->emms_mask_value_pairs.outer) >=
2240 	    MAE_ENC_FIELD_PAIRS_LEN);
2241 	offset = MC_CMD_MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA_OFST;
2242 	memcpy(payload + offset, spec->emms_mask_value_pairs.outer,
2243 	    MAE_ENC_FIELD_PAIRS_LEN);
2244 
2245 	MCDI_IN_SET_BYTE(req, MAE_OUTER_RULE_INSERT_IN_RECIRC_ID,
2246 	    spec->emms_outer_rule_recirc_id);
2247 
2248 	efx_mcdi_execute(enp, &req);
2249 
2250 	if (req.emr_rc != 0) {
2251 		rc = req.emr_rc;
2252 		goto fail4;
2253 	}
2254 
2255 	if (req.emr_out_length_used < MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN) {
2256 		rc = EMSGSIZE;
2257 		goto fail5;
2258 	}
2259 
2260 	or_id.id = MCDI_OUT_DWORD(req, MAE_OUTER_RULE_INSERT_OUT_OR_ID);
2261 	if (or_id.id == EFX_MAE_RSRC_ID_INVALID) {
2262 		rc = ENOENT;
2263 		goto fail6;
2264 	}
2265 
2266 	or_idp->id = or_id.id;
2267 
2268 	return (0);
2269 
2270 fail6:
2271 	EFSYS_PROBE(fail6);
2272 fail5:
2273 	EFSYS_PROBE(fail5);
2274 fail4:
2275 	EFSYS_PROBE(fail4);
2276 fail3:
2277 	EFSYS_PROBE(fail3);
2278 fail2:
2279 	EFSYS_PROBE(fail2);
2280 fail1:
2281 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2282 	return (rc);
2283 }
2284 
2285 	__checkReturn		efx_rc_t
2286 efx_mae_outer_rule_remove(
2287 	__in			efx_nic_t *enp,
2288 	__in			const efx_mae_rule_id_t *or_idp)
2289 {
2290 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2291 	efx_mcdi_req_t req;
2292 	EFX_MCDI_DECLARE_BUF(payload,
2293 	    MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1),
2294 	    MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1));
2295 	efx_rc_t rc;
2296 
2297 	if (encp->enc_mae_supported == B_FALSE) {
2298 		rc = ENOTSUP;
2299 		goto fail1;
2300 	}
2301 
2302 	req.emr_cmd = MC_CMD_MAE_OUTER_RULE_REMOVE;
2303 	req.emr_in_buf = payload;
2304 	req.emr_in_length = MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1);
2305 	req.emr_out_buf = payload;
2306 	req.emr_out_length = MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1);
2307 
2308 	MCDI_IN_SET_DWORD(req, MAE_OUTER_RULE_REMOVE_IN_OR_ID, or_idp->id);
2309 
2310 	efx_mcdi_execute(enp, &req);
2311 
2312 	if (req.emr_rc != 0) {
2313 		rc = req.emr_rc;
2314 		goto fail2;
2315 	}
2316 
2317 	if (req.emr_out_length_used < MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LENMIN) {
2318 		rc = EMSGSIZE;
2319 		goto fail3;
2320 	}
2321 
2322 	if (MCDI_OUT_DWORD(req, MAE_OUTER_RULE_REMOVE_OUT_REMOVED_OR_ID) !=
2323 	    or_idp->id) {
2324 		/* Firmware failed to remove the outer rule. */
2325 		rc = EAGAIN;
2326 		goto fail4;
2327 	}
2328 
2329 	return (0);
2330 
2331 fail4:
2332 	EFSYS_PROBE(fail4);
2333 fail3:
2334 	EFSYS_PROBE(fail3);
2335 fail2:
2336 	EFSYS_PROBE(fail2);
2337 fail1:
2338 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2339 	return (rc);
2340 }
2341 
2342 	__checkReturn			efx_rc_t
2343 efx_mae_match_spec_outer_rule_id_set(
2344 	__in				efx_mae_match_spec_t *spec,
2345 	__in				const efx_mae_rule_id_t *or_idp)
2346 {
2347 	uint32_t full_mask = UINT32_MAX;
2348 	efx_rc_t rc;
2349 
2350 	if (spec->emms_type != EFX_MAE_RULE_ACTION) {
2351 		rc = EINVAL;
2352 		goto fail1;
2353 	}
2354 
2355 	if (or_idp == NULL) {
2356 		rc = EINVAL;
2357 		goto fail2;
2358 	}
2359 
2360 	rc = efx_mae_match_spec_field_set(spec, EFX_MAE_FIELD_OUTER_RULE_ID,
2361 	    sizeof (or_idp->id), (const uint8_t *)&or_idp->id,
2362 	    sizeof (full_mask), (const uint8_t *)&full_mask);
2363 	if (rc != 0)
2364 		goto fail3;
2365 
2366 	return (0);
2367 
2368 fail3:
2369 	EFSYS_PROBE(fail3);
2370 fail2:
2371 	EFSYS_PROBE(fail2);
2372 fail1:
2373 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2374 	return (rc);
2375 }
2376 
2377 	 __checkReturn	efx_rc_t
2378 efx_mae_mac_addr_alloc(
2379 	__in		efx_nic_t *enp,
2380 	__in		uint8_t addr_bytes[EFX_MAC_ADDR_LEN],
2381 	__out		efx_mae_mac_id_t *mac_idp)
2382 {
2383 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2384 	efx_mcdi_req_t req;
2385 	EFX_MCDI_DECLARE_BUF(payload,
2386 	    MC_CMD_MAE_MAC_ADDR_ALLOC_IN_LEN,
2387 	    MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN);
2388 	efx_mae_mac_id_t mac_id;
2389 	efx_rc_t rc;
2390 
2391 	EFX_STATIC_ASSERT(sizeof (mac_idp->id) ==
2392 	    MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_LEN);
2393 
2394 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
2395 	    MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
2396 
2397 	if (encp->enc_mae_supported == B_FALSE) {
2398 		rc = ENOTSUP;
2399 		goto fail1;
2400 	}
2401 
2402 	if (encp->enc_mae_aset_v2_supported == B_FALSE) {
2403 		rc = ENOTSUP;
2404 		goto fail2;
2405 	}
2406 
2407 	req.emr_cmd = MC_CMD_MAE_MAC_ADDR_ALLOC;
2408 	req.emr_in_buf = payload;
2409 	req.emr_in_length = MC_CMD_MAE_MAC_ADDR_ALLOC_IN_LEN;
2410 	req.emr_out_buf = payload;
2411 	req.emr_out_length = MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN;
2412 
2413 	memcpy(payload + MC_CMD_MAE_MAC_ADDR_ALLOC_IN_MAC_ADDR_OFST,
2414 	    addr_bytes, EFX_MAC_ADDR_LEN);
2415 
2416 	efx_mcdi_execute(enp, &req);
2417 
2418 	if (req.emr_rc != 0) {
2419 		rc = req.emr_rc;
2420 		goto fail3;
2421 	}
2422 
2423 	if (req.emr_out_length_used < MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN) {
2424 		rc = EMSGSIZE;
2425 		goto fail4;
2426 	}
2427 
2428 	mac_id.id = MCDI_OUT_DWORD(req, MAE_MAC_ADDR_ALLOC_OUT_MAC_ID);
2429 	if (mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
2430 		rc = ENOENT;
2431 		goto fail5;
2432 	}
2433 
2434 	mac_idp->id = mac_id.id;
2435 
2436 	return (0);
2437 
2438 fail5:
2439 	EFSYS_PROBE(fail5);
2440 fail4:
2441 	EFSYS_PROBE(fail4);
2442 fail3:
2443 	EFSYS_PROBE(fail3);
2444 fail2:
2445 	EFSYS_PROBE(fail2);
2446 fail1:
2447 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2448 	return (rc);
2449 }
2450 
2451 	__checkReturn	efx_rc_t
2452 efx_mae_mac_addr_free(
2453 	__in		efx_nic_t *enp,
2454 	__in		const efx_mae_mac_id_t *mac_idp)
2455 {
2456 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2457 	efx_mcdi_req_t req;
2458 	EFX_MCDI_DECLARE_BUF(payload,
2459 	    MC_CMD_MAE_MAC_ADDR_FREE_IN_LEN(1),
2460 	    MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1));
2461 	efx_rc_t rc;
2462 
2463 	if (encp->enc_mae_supported == B_FALSE) {
2464 		rc = ENOTSUP;
2465 		goto fail1;
2466 	}
2467 
2468 	if (encp->enc_mae_aset_v2_supported == B_FALSE) {
2469 		rc = ENOTSUP;
2470 		goto fail2;
2471 	}
2472 
2473 	req.emr_cmd = MC_CMD_MAE_MAC_ADDR_FREE;
2474 	req.emr_in_buf = payload;
2475 	req.emr_in_length = MC_CMD_MAE_MAC_ADDR_FREE_IN_LEN(1);
2476 	req.emr_out_buf = payload;
2477 	req.emr_out_length = MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1);
2478 
2479 	MCDI_IN_SET_DWORD(req, MAE_MAC_ADDR_FREE_IN_MAC_ID, mac_idp->id);
2480 
2481 	efx_mcdi_execute(enp, &req);
2482 
2483 	if (req.emr_rc != 0) {
2484 		rc = req.emr_rc;
2485 		goto fail3;
2486 	}
2487 
2488 	if (req.emr_out_length_used < MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1)) {
2489 		rc = EMSGSIZE;
2490 		goto fail4;
2491 	}
2492 
2493 	if (MCDI_OUT_DWORD(req, MAE_MAC_ADDR_FREE_OUT_FREED_MAC_ID) !=
2494 	    mac_idp->id) {
2495 		/* Firmware failed to remove the MAC address entry. */
2496 		rc = EAGAIN;
2497 		goto fail5;
2498 	}
2499 
2500 	return (0);
2501 
2502 fail5:
2503 	EFSYS_PROBE(fail5);
2504 fail4:
2505 	EFSYS_PROBE(fail4);
2506 fail3:
2507 	EFSYS_PROBE(fail3);
2508 fail2:
2509 	EFSYS_PROBE(fail2);
2510 fail1:
2511 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2512 	return (rc);
2513 }
2514 
2515 	__checkReturn			efx_rc_t
2516 efx_mae_action_set_fill_in_dst_mac_id(
2517 	__in				efx_mae_actions_t *spec,
2518 	__in				const efx_mae_mac_id_t *mac_idp)
2519 {
2520 	efx_rc_t rc;
2521 
2522 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_DST_MAC)) == 0) {
2523 		/*
2524 		 * The caller has not intended to have this action originally,
2525 		 * hence, they cannot indicate the MAC address entry ID.
2526 		 */
2527 		rc = EINVAL;
2528 		goto fail1;
2529 	}
2530 
2531 	if (spec->ema_rsrc.emar_dst_mac_id.id != EFX_MAE_RSRC_ID_INVALID) {
2532 		/* An attempt to indicate the MAC address entry ID twice. */
2533 		rc = EINVAL;
2534 		goto fail2;
2535 	}
2536 
2537 	if (mac_idp->id == EFX_MAE_RSRC_ID_INVALID) {
2538 		rc = EINVAL;
2539 		goto fail3;
2540 	}
2541 
2542 	spec->ema_rsrc.emar_dst_mac_id.id = mac_idp->id;
2543 
2544 	return (0);
2545 
2546 fail3:
2547 	EFSYS_PROBE(fail3);
2548 fail2:
2549 	EFSYS_PROBE(fail2);
2550 fail1:
2551 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2552 	return (rc);
2553 }
2554 
2555 	__checkReturn			efx_rc_t
2556 efx_mae_action_set_fill_in_src_mac_id(
2557 	__in				efx_mae_actions_t *spec,
2558 	__in				const efx_mae_mac_id_t *mac_idp)
2559 {
2560 	efx_rc_t rc;
2561 
2562 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_SRC_MAC)) == 0) {
2563 		/*
2564 		 * The caller has not intended to have this action originally,
2565 		 * hence, they cannot indicate the MAC address entry ID.
2566 		 */
2567 		rc = EINVAL;
2568 		goto fail1;
2569 	}
2570 
2571 	if (spec->ema_rsrc.emar_src_mac_id.id != EFX_MAE_RSRC_ID_INVALID) {
2572 		/* An attempt to indicate the MAC address entry ID twice. */
2573 		rc = EINVAL;
2574 		goto fail2;
2575 	}
2576 
2577 	if (mac_idp->id == EFX_MAE_RSRC_ID_INVALID) {
2578 		rc = EINVAL;
2579 		goto fail3;
2580 	}
2581 
2582 	spec->ema_rsrc.emar_src_mac_id.id = mac_idp->id;
2583 
2584 	return (0);
2585 
2586 fail3:
2587 	EFSYS_PROBE(fail3);
2588 fail2:
2589 	EFSYS_PROBE(fail2);
2590 fail1:
2591 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2592 	return (rc);
2593 }
2594 
2595 	 __checkReturn			efx_rc_t
2596 efx_mae_encap_header_alloc(
2597 	__in				efx_nic_t *enp,
2598 	__in				efx_tunnel_protocol_t encap_type,
2599 	__in_bcount(header_size)	uint8_t *header_data,
2600 	__in				size_t header_size,
2601 	__out				efx_mae_eh_id_t *eh_idp)
2602 {
2603 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2604 	efx_mcdi_req_t req;
2605 	EFX_MCDI_DECLARE_BUF(payload,
2606 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LENMAX_MCDI2,
2607 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN);
2608 	uint32_t encap_type_mcdi;
2609 	efx_mae_eh_id_t eh_id;
2610 	efx_rc_t rc;
2611 
2612 	EFX_STATIC_ASSERT(sizeof (eh_idp->id) ==
2613 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_LEN);
2614 
2615 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
2616 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
2617 
2618 	if (encp->enc_mae_supported == B_FALSE) {
2619 		rc = ENOTSUP;
2620 		goto fail1;
2621 	}
2622 
2623 	switch (encap_type) {
2624 	case EFX_TUNNEL_PROTOCOL_NONE:
2625 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NONE;
2626 		break;
2627 	case EFX_TUNNEL_PROTOCOL_VXLAN:
2628 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_VXLAN;
2629 		break;
2630 	case EFX_TUNNEL_PROTOCOL_GENEVE:
2631 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_GENEVE;
2632 		break;
2633 	case EFX_TUNNEL_PROTOCOL_NVGRE:
2634 		encap_type_mcdi = MAE_MCDI_ENCAP_TYPE_NVGRE;
2635 		break;
2636 	default:
2637 		rc = ENOTSUP;
2638 		goto fail2;
2639 	}
2640 
2641 	if (header_size >
2642 	    MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_MAXNUM_MCDI2) {
2643 		rc = EINVAL;
2644 		goto fail3;
2645 	}
2646 
2647 	req.emr_cmd = MC_CMD_MAE_ENCAP_HEADER_ALLOC;
2648 	req.emr_in_buf = payload;
2649 	req.emr_in_length = MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN(header_size);
2650 	req.emr_out_buf = payload;
2651 	req.emr_out_length = MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN;
2652 
2653 	MCDI_IN_SET_DWORD(req,
2654 	    MAE_ENCAP_HEADER_ALLOC_IN_ENCAP_TYPE, encap_type_mcdi);
2655 
2656 	memcpy(payload + MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA_OFST,
2657 	    header_data, header_size);
2658 
2659 	efx_mcdi_execute(enp, &req);
2660 
2661 	if (req.emr_rc != 0) {
2662 		rc = req.emr_rc;
2663 		goto fail4;
2664 	}
2665 
2666 	if (req.emr_out_length_used < MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN) {
2667 		rc = EMSGSIZE;
2668 		goto fail5;
2669 	}
2670 
2671 	eh_id.id = MCDI_OUT_DWORD(req,
2672 	    MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID);
2673 
2674 	if (eh_id.id == EFX_MAE_RSRC_ID_INVALID) {
2675 		rc = ENOENT;
2676 		goto fail6;
2677 	}
2678 
2679 	eh_idp->id = eh_id.id;
2680 
2681 	return (0);
2682 
2683 fail6:
2684 	EFSYS_PROBE(fail6);
2685 fail5:
2686 	EFSYS_PROBE(fail5);
2687 fail4:
2688 	EFSYS_PROBE(fail4);
2689 fail3:
2690 	EFSYS_PROBE(fail3);
2691 fail2:
2692 	EFSYS_PROBE(fail2);
2693 fail1:
2694 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2695 	return (rc);
2696 }
2697 
2698 	__checkReturn			efx_rc_t
2699 efx_mae_encap_header_free(
2700 	__in				efx_nic_t *enp,
2701 	__in				const efx_mae_eh_id_t *eh_idp)
2702 {
2703 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2704 	efx_mcdi_req_t req;
2705 	EFX_MCDI_DECLARE_BUF(payload,
2706 	    MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN(1),
2707 	    MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN(1));
2708 	efx_rc_t rc;
2709 
2710 	if (encp->enc_mae_supported == B_FALSE) {
2711 		rc = ENOTSUP;
2712 		goto fail1;
2713 	}
2714 
2715 	req.emr_cmd = MC_CMD_MAE_ENCAP_HEADER_FREE;
2716 	req.emr_in_buf = payload;
2717 	req.emr_in_length = MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN(1);
2718 	req.emr_out_buf = payload;
2719 	req.emr_out_length = MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN(1);
2720 
2721 	MCDI_IN_SET_DWORD(req, MAE_ENCAP_HEADER_FREE_IN_EH_ID, eh_idp->id);
2722 
2723 	efx_mcdi_execute(enp, &req);
2724 
2725 	if (req.emr_rc != 0) {
2726 		rc = req.emr_rc;
2727 		goto fail2;
2728 	}
2729 
2730 	if (MCDI_OUT_DWORD(req, MAE_ENCAP_HEADER_FREE_OUT_FREED_EH_ID) !=
2731 	    eh_idp->id) {
2732 		/* Firmware failed to remove the encap. header. */
2733 		rc = EAGAIN;
2734 		goto fail3;
2735 	}
2736 
2737 	return (0);
2738 
2739 fail3:
2740 	EFSYS_PROBE(fail3);
2741 fail2:
2742 	EFSYS_PROBE(fail2);
2743 fail1:
2744 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2745 	return (rc);
2746 }
2747 
2748 	__checkReturn			efx_rc_t
2749 efx_mae_action_set_fill_in_eh_id(
2750 	__in				efx_mae_actions_t *spec,
2751 	__in				const efx_mae_eh_id_t *eh_idp)
2752 {
2753 	efx_rc_t rc;
2754 
2755 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_ENCAP)) == 0) {
2756 		/*
2757 		 * The caller has not intended to have action ENCAP originally,
2758 		 * hence, this attempt to indicate encap. header ID is invalid.
2759 		 */
2760 		rc = EINVAL;
2761 		goto fail1;
2762 	}
2763 
2764 	if (spec->ema_rsrc.emar_eh_id.id != EFX_MAE_RSRC_ID_INVALID) {
2765 		/* The caller attempts to indicate encap. header ID twice. */
2766 		rc = EINVAL;
2767 		goto fail2;
2768 	}
2769 
2770 	if (eh_idp->id == EFX_MAE_RSRC_ID_INVALID) {
2771 		rc = EINVAL;
2772 		goto fail3;
2773 	}
2774 
2775 	spec->ema_rsrc.emar_eh_id.id = eh_idp->id;
2776 
2777 	return (0);
2778 
2779 fail3:
2780 	EFSYS_PROBE(fail3);
2781 fail2:
2782 	EFSYS_PROBE(fail2);
2783 fail1:
2784 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2785 	return (rc);
2786 }
2787 
2788 	__checkReturn			efx_rc_t
2789 efx_mae_action_set_alloc(
2790 	__in				efx_nic_t *enp,
2791 	__in				const efx_mae_actions_t *spec,
2792 	__out				efx_mae_aset_id_t *aset_idp)
2793 {
2794 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
2795 	efx_mcdi_req_t req;
2796 	EFX_MCDI_DECLARE_BUF(payload,
2797 	    MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN,
2798 	    MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
2799 	efx_mae_aset_id_t aset_id;
2800 	efx_rc_t rc;
2801 
2802 	if (encp->enc_mae_supported == B_FALSE) {
2803 		rc = ENOTSUP;
2804 		goto fail1;
2805 	}
2806 
2807 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_DST_MAC)) != 0 &&
2808 	    spec->ema_rsrc.emar_dst_mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
2809 		rc = EINVAL;
2810 		goto fail2;
2811 	}
2812 
2813 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_SRC_MAC)) != 0 &&
2814 	    spec->ema_rsrc.emar_src_mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
2815 		rc = EINVAL;
2816 		goto fail3;
2817 	}
2818 
2819 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_ENCAP)) != 0 &&
2820 	    spec->ema_rsrc.emar_eh_id.id == EFX_MAE_RSRC_ID_INVALID) {
2821 		rc = EINVAL;
2822 		goto fail4;
2823 	}
2824 
2825 	if (spec->ema_n_count_actions == 1 &&
2826 	    spec->ema_rsrc.emar_counter_id.id == EFX_MAE_RSRC_ID_INVALID) {
2827 		rc = EINVAL;
2828 		goto fail5;
2829 	}
2830 
2831 	req.emr_cmd = MC_CMD_MAE_ACTION_SET_ALLOC;
2832 	req.emr_in_buf = payload;
2833 	req.emr_in_length = MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN;
2834 	req.emr_out_buf = payload;
2835 	req.emr_out_length = MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN;
2836 
2837 	/*
2838 	 * TODO: Remove these EFX_MAE_RSRC_ID_INVALID assignments once the
2839 	 * corresponding resource types are supported by the implementation.
2840 	 * Use proper resource ID assignments instead.
2841 	 */
2842 	MCDI_IN_SET_DWORD(req,
2843 	    MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, EFX_MAE_RSRC_ID_INVALID);
2844 
2845 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_DECAP)) != 0) {
2846 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2847 		    MAE_ACTION_SET_ALLOC_IN_DECAP, 1);
2848 	}
2849 
2850 	MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2851 	    MAE_ACTION_SET_ALLOC_IN_VLAN_POP, spec->ema_n_vlan_tags_to_pop);
2852 
2853 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
2854 	    spec->ema_rsrc.emar_dst_mac_id.id);
2855 
2856 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
2857 	    spec->ema_rsrc.emar_src_mac_id.id);
2858 
2859 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_DECR_IP_TTL)) != 0) {
2860 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2861 		    MAE_ACTION_SET_ALLOC_IN_DO_DECR_IP_TTL, 1);
2862 	}
2863 
2864 	if (spec->ema_n_vlan_tags_to_push > 0) {
2865 		unsigned int outer_tag_idx;
2866 
2867 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2868 		    MAE_ACTION_SET_ALLOC_IN_VLAN_PUSH,
2869 		    spec->ema_n_vlan_tags_to_push);
2870 
2871 		if (spec->ema_n_vlan_tags_to_push ==
2872 		    EFX_MAE_VLAN_PUSH_MAX_NTAGS) {
2873 			MCDI_IN_SET_WORD(req,
2874 			    MAE_ACTION_SET_ALLOC_IN_VLAN1_PROTO_BE,
2875 			    spec->ema_vlan_push_descs[0].emavp_tpid_be);
2876 			MCDI_IN_SET_WORD(req,
2877 			    MAE_ACTION_SET_ALLOC_IN_VLAN1_TCI_BE,
2878 			    spec->ema_vlan_push_descs[0].emavp_tci_be);
2879 		}
2880 
2881 		outer_tag_idx = spec->ema_n_vlan_tags_to_push - 1;
2882 
2883 		MCDI_IN_SET_WORD(req, MAE_ACTION_SET_ALLOC_IN_VLAN0_PROTO_BE,
2884 		    spec->ema_vlan_push_descs[outer_tag_idx].emavp_tpid_be);
2885 		MCDI_IN_SET_WORD(req, MAE_ACTION_SET_ALLOC_IN_VLAN0_TCI_BE,
2886 		    spec->ema_vlan_push_descs[outer_tag_idx].emavp_tci_be);
2887 	}
2888 
2889 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
2890 	    spec->ema_rsrc.emar_eh_id.id);
2891 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
2892 	    spec->ema_rsrc.emar_counter_id.id);
2893 
2894 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_FLAG)) != 0) {
2895 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2896 		    MAE_ACTION_SET_ALLOC_IN_FLAG, 1);
2897 	}
2898 
2899 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_MARK)) != 0) {
2900 		MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
2901 		    MAE_ACTION_SET_ALLOC_IN_MARK, 1);
2902 
2903 		MCDI_IN_SET_DWORD(req,
2904 		    MAE_ACTION_SET_ALLOC_IN_MARK_VALUE, spec->ema_mark_value);
2905 	}
2906 
2907 	MCDI_IN_SET_DWORD(req,
2908 	    MAE_ACTION_SET_ALLOC_IN_DELIVER, spec->ema_deliver_mport.sel);
2909 
2910 	efx_mcdi_execute(enp, &req);
2911 
2912 	if (req.emr_rc != 0) {
2913 		rc = req.emr_rc;
2914 		goto fail6;
2915 	}
2916 
2917 	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN) {
2918 		rc = EMSGSIZE;
2919 		goto fail7;
2920 	}
2921 
2922 	aset_id.id = MCDI_OUT_DWORD(req, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
2923 	if (aset_id.id == EFX_MAE_RSRC_ID_INVALID) {
2924 		rc = ENOENT;
2925 		goto fail8;
2926 	}
2927 
2928 	aset_idp->id = aset_id.id;
2929 
2930 	return (0);
2931 
2932 fail8:
2933 	EFSYS_PROBE(fail8);
2934 fail7:
2935 	EFSYS_PROBE(fail7);
2936 fail6:
2937 	EFSYS_PROBE(fail6);
2938 fail5:
2939 	EFSYS_PROBE(fail5);
2940 fail4:
2941 	EFSYS_PROBE(fail4);
2942 fail3:
2943 	EFSYS_PROBE(fail3);
2944 fail2:
2945 	EFSYS_PROBE(fail2);
2946 fail1:
2947 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2948 	return (rc);
2949 }
2950 
2951 	__checkReturn			unsigned int
2952 efx_mae_action_set_get_nb_count(
2953 	__in				const efx_mae_actions_t *spec)
2954 {
2955 	return (spec->ema_n_count_actions);
2956 }
2957 
2958 	__checkReturn			efx_rc_t
2959 efx_mae_action_set_fill_in_counter_id(
2960 	__in				efx_mae_actions_t *spec,
2961 	__in				const efx_counter_t *counter_idp)
2962 {
2963 	efx_rc_t rc;
2964 
2965 	if ((spec->ema_actions & (1U << EFX_MAE_ACTION_COUNT)) == 0) {
2966 		/*
2967 		 * Invalid to add counter ID if spec does not have COUNT action.
2968 		 */
2969 		rc = EINVAL;
2970 		goto fail1;
2971 	}
2972 
2973 	if (spec->ema_n_count_actions != 1) {
2974 		/*
2975 		 * Having multiple COUNT actions in the spec requires a counter
2976 		 * list to be used. This API must only be used for a single
2977 		 * counter per spec. Turn down the request as inappropriate.
2978 		 */
2979 		rc = EINVAL;
2980 		goto fail2;
2981 	}
2982 
2983 	if (spec->ema_rsrc.emar_counter_id.id != EFX_MAE_RSRC_ID_INVALID) {
2984 		/* The caller attempts to indicate counter ID twice. */
2985 		rc = EALREADY;
2986 		goto fail3;
2987 	}
2988 
2989 	if (counter_idp->id == EFX_MAE_RSRC_ID_INVALID) {
2990 		rc = EINVAL;
2991 		goto fail4;
2992 	}
2993 
2994 	spec->ema_rsrc.emar_counter_id.id = counter_idp->id;
2995 
2996 	return (0);
2997 
2998 fail4:
2999 	EFSYS_PROBE(fail4);
3000 fail3:
3001 	EFSYS_PROBE(fail3);
3002 fail2:
3003 	EFSYS_PROBE(fail2);
3004 fail1:
3005 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3006 	return (rc);
3007 }
3008 
3009 	__checkReturn			efx_rc_t
3010 efx_mae_counters_alloc(
3011 	__in				efx_nic_t *enp,
3012 	__in				uint32_t n_counters,
3013 	__out				uint32_t *n_allocatedp,
3014 	__out_ecount(n_counters)	efx_counter_t *countersp,
3015 	__out_opt			uint32_t *gen_countp)
3016 {
3017 	EFX_MCDI_DECLARE_BUF(payload,
3018 	    MC_CMD_MAE_COUNTER_ALLOC_IN_LEN,
3019 	    MC_CMD_MAE_COUNTER_ALLOC_OUT_LENMAX_MCDI2);
3020 	efx_mae_t *maep = enp->en_maep;
3021 	uint32_t n_allocated;
3022 	efx_mcdi_req_t req;
3023 	unsigned int i;
3024 	efx_rc_t rc;
3025 
3026 	if (n_counters > maep->em_max_ncounters ||
3027 	    n_counters < MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_MINNUM ||
3028 	    n_counters > MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_MAXNUM_MCDI2) {
3029 		rc = EINVAL;
3030 		goto fail1;
3031 	}
3032 
3033 	req.emr_cmd = MC_CMD_MAE_COUNTER_ALLOC;
3034 	req.emr_in_buf = payload;
3035 	req.emr_in_length = MC_CMD_MAE_COUNTER_ALLOC_IN_LEN;
3036 	req.emr_out_buf = payload;
3037 	req.emr_out_length = MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(n_counters);
3038 
3039 	MCDI_IN_SET_DWORD(req, MAE_COUNTER_ALLOC_IN_REQUESTED_COUNT,
3040 	    n_counters);
3041 
3042 	efx_mcdi_execute(enp, &req);
3043 
3044 	if (req.emr_rc != 0) {
3045 		rc = req.emr_rc;
3046 		goto fail2;
3047 	}
3048 
3049 	if (req.emr_out_length_used < MC_CMD_MAE_COUNTER_ALLOC_OUT_LENMIN) {
3050 		rc = EMSGSIZE;
3051 		goto fail3;
3052 	}
3053 
3054 	n_allocated = MCDI_OUT_DWORD(req,
3055 	    MAE_COUNTER_ALLOC_OUT_COUNTER_ID_COUNT);
3056 	if (n_allocated < MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_MINNUM) {
3057 		rc = EFAULT;
3058 		goto fail4;
3059 	}
3060 
3061 	for (i = 0; i < n_allocated; i++) {
3062 		countersp[i].id = MCDI_OUT_INDEXED_DWORD(req,
3063 		    MAE_COUNTER_ALLOC_OUT_COUNTER_ID, i);
3064 	}
3065 
3066 	if (gen_countp != NULL) {
3067 		*gen_countp = MCDI_OUT_DWORD(req,
3068 				    MAE_COUNTER_ALLOC_OUT_GENERATION_COUNT);
3069 	}
3070 
3071 	*n_allocatedp = n_allocated;
3072 
3073 	return (0);
3074 
3075 fail4:
3076 	EFSYS_PROBE(fail4);
3077 fail3:
3078 	EFSYS_PROBE(fail3);
3079 fail2:
3080 	EFSYS_PROBE(fail2);
3081 fail1:
3082 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3083 
3084 	return (rc);
3085 }
3086 
3087 	__checkReturn			efx_rc_t
3088 efx_mae_counters_free(
3089 	__in				efx_nic_t *enp,
3090 	__in				uint32_t n_counters,
3091 	__out				uint32_t *n_freedp,
3092 	__in_ecount(n_counters)		const efx_counter_t *countersp,
3093 	__out_opt			uint32_t *gen_countp)
3094 {
3095 	EFX_MCDI_DECLARE_BUF(payload,
3096 	    MC_CMD_MAE_COUNTER_FREE_IN_LENMAX_MCDI2,
3097 	    MC_CMD_MAE_COUNTER_FREE_OUT_LENMAX_MCDI2);
3098 	efx_mae_t *maep = enp->en_maep;
3099 	efx_mcdi_req_t req;
3100 	uint32_t n_freed;
3101 	unsigned int i;
3102 	efx_rc_t rc;
3103 
3104 	if (n_counters > maep->em_max_ncounters ||
3105 	    n_counters < MC_CMD_MAE_COUNTER_FREE_IN_FREE_COUNTER_ID_MINNUM ||
3106 	    n_counters >
3107 	    MC_CMD_MAE_COUNTER_FREE_IN_FREE_COUNTER_ID_MAXNUM_MCDI2) {
3108 		rc = EINVAL;
3109 		goto fail1;
3110 	}
3111 
3112 	req.emr_cmd = MC_CMD_MAE_COUNTER_FREE;
3113 	req.emr_in_buf = payload;
3114 	req.emr_in_length = MC_CMD_MAE_COUNTER_FREE_IN_LEN(n_counters);
3115 	req.emr_out_buf = payload;
3116 	req.emr_out_length = MC_CMD_MAE_COUNTER_FREE_OUT_LEN(n_counters);
3117 
3118 	for (i = 0; i < n_counters; i++) {
3119 		MCDI_IN_SET_INDEXED_DWORD(req,
3120 		    MAE_COUNTER_FREE_IN_FREE_COUNTER_ID, i, countersp[i].id);
3121 	}
3122 	MCDI_IN_SET_DWORD(req, MAE_COUNTER_FREE_IN_COUNTER_ID_COUNT,
3123 			  n_counters);
3124 
3125 	efx_mcdi_execute(enp, &req);
3126 
3127 	if (req.emr_rc != 0) {
3128 		rc = req.emr_rc;
3129 		goto fail2;
3130 	}
3131 
3132 	if (req.emr_out_length_used < MC_CMD_MAE_COUNTER_FREE_OUT_LENMIN) {
3133 		rc = EMSGSIZE;
3134 		goto fail3;
3135 	}
3136 
3137 	n_freed = MCDI_OUT_DWORD(req, MAE_COUNTER_FREE_OUT_COUNTER_ID_COUNT);
3138 
3139 	if (n_freed < MC_CMD_MAE_COUNTER_FREE_OUT_FREED_COUNTER_ID_MINNUM) {
3140 		rc = EFAULT;
3141 		goto fail4;
3142 	}
3143 
3144 	if (gen_countp != NULL) {
3145 		*gen_countp = MCDI_OUT_DWORD(req,
3146 				    MAE_COUNTER_FREE_OUT_GENERATION_COUNT);
3147 	}
3148 
3149 	*n_freedp = n_freed;
3150 
3151 	return (0);
3152 
3153 fail4:
3154 	EFSYS_PROBE(fail4);
3155 fail3:
3156 	EFSYS_PROBE(fail3);
3157 fail2:
3158 	EFSYS_PROBE(fail2);
3159 fail1:
3160 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3161 
3162 	return (rc);
3163 }
3164 
3165 	__checkReturn			efx_rc_t
3166 efx_mae_counters_stream_start(
3167 	__in				efx_nic_t *enp,
3168 	__in				uint16_t rxq_id,
3169 	__in				uint16_t packet_size,
3170 	__in				uint32_t flags_in,
3171 	__out				uint32_t *flags_out)
3172 {
3173 	efx_mcdi_req_t req;
3174 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAE_COUNTERS_STREAM_START_IN_LEN,
3175 			     MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN);
3176 	efx_rc_t rc;
3177 
3178 	EFX_STATIC_ASSERT(EFX_MAE_COUNTERS_STREAM_IN_ZERO_SQUASH_DISABLE ==
3179 	    1U << MC_CMD_MAE_COUNTERS_STREAM_START_IN_ZERO_SQUASH_DISABLE_LBN);
3180 
3181 	EFX_STATIC_ASSERT(EFX_MAE_COUNTERS_STREAM_OUT_USES_CREDITS ==
3182 	    1U << MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_LBN);
3183 
3184 	req.emr_cmd = MC_CMD_MAE_COUNTERS_STREAM_START;
3185 	req.emr_in_buf = payload;
3186 	req.emr_in_length = MC_CMD_MAE_COUNTERS_STREAM_START_IN_LEN;
3187 	req.emr_out_buf = payload;
3188 	req.emr_out_length = MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN;
3189 
3190 	MCDI_IN_SET_WORD(req, MAE_COUNTERS_STREAM_START_IN_QID, rxq_id);
3191 	MCDI_IN_SET_WORD(req, MAE_COUNTERS_STREAM_START_IN_PACKET_SIZE,
3192 			 packet_size);
3193 	MCDI_IN_SET_DWORD(req, MAE_COUNTERS_STREAM_START_IN_FLAGS, flags_in);
3194 
3195 	efx_mcdi_execute(enp, &req);
3196 
3197 	if (req.emr_rc != 0) {
3198 		rc = req.emr_rc;
3199 		goto fail1;
3200 	}
3201 
3202 	if (req.emr_out_length_used <
3203 	    MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN) {
3204 		rc = EMSGSIZE;
3205 		goto fail2;
3206 	}
3207 
3208 	*flags_out = MCDI_OUT_DWORD(req, MAE_COUNTERS_STREAM_START_OUT_FLAGS);
3209 
3210 	return (0);
3211 
3212 fail2:
3213 	EFSYS_PROBE(fail2);
3214 fail1:
3215 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3216 
3217 	return (rc);
3218 }
3219 
3220 	__checkReturn			efx_rc_t
3221 efx_mae_counters_stream_stop(
3222 	__in				efx_nic_t *enp,
3223 	__in				uint16_t rxq_id,
3224 	__out_opt			uint32_t *gen_countp)
3225 {
3226 	efx_mcdi_req_t req;
3227 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN,
3228 			     MC_CMD_MAE_COUNTERS_STREAM_STOP_OUT_LEN);
3229 	efx_rc_t rc;
3230 
3231 	req.emr_cmd = MC_CMD_MAE_COUNTERS_STREAM_STOP;
3232 	req.emr_in_buf = payload;
3233 	req.emr_in_length = MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN;
3234 	req.emr_out_buf = payload;
3235 	req.emr_out_length = MC_CMD_MAE_COUNTERS_STREAM_STOP_OUT_LEN;
3236 
3237 	MCDI_IN_SET_WORD(req, MAE_COUNTERS_STREAM_STOP_IN_QID, rxq_id);
3238 
3239 	efx_mcdi_execute(enp, &req);
3240 
3241 	if (req.emr_rc != 0) {
3242 		rc = req.emr_rc;
3243 		goto fail1;
3244 	}
3245 
3246 	if (req.emr_out_length_used <
3247 	    MC_CMD_MAE_COUNTERS_STREAM_STOP_OUT_LEN) {
3248 		rc = EMSGSIZE;
3249 		goto fail2;
3250 	}
3251 
3252 	if (gen_countp != NULL) {
3253 		*gen_countp = MCDI_OUT_DWORD(req,
3254 			    MAE_COUNTERS_STREAM_STOP_OUT_GENERATION_COUNT);
3255 	}
3256 
3257 	return (0);
3258 
3259 fail2:
3260 	EFSYS_PROBE(fail2);
3261 fail1:
3262 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3263 
3264 	return (rc);
3265 }
3266 
3267 	__checkReturn			efx_rc_t
3268 efx_mae_counters_stream_give_credits(
3269 	__in				efx_nic_t *enp,
3270 	__in				uint32_t n_credits)
3271 {
3272 	efx_mcdi_req_t req;
3273 	EFX_MCDI_DECLARE_BUF(payload,
3274 			     MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN,
3275 			     MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN);
3276 	efx_rc_t rc;
3277 
3278 	req.emr_cmd = MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS;
3279 	req.emr_in_buf = payload;
3280 	req.emr_in_length = MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN;
3281 	req.emr_out_buf = payload;
3282 	req.emr_out_length = MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN;
3283 
3284 	MCDI_IN_SET_DWORD(req, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS,
3285 			 n_credits);
3286 
3287 	efx_mcdi_execute(enp, &req);
3288 
3289 	if (req.emr_rc != 0) {
3290 		rc = req.emr_rc;
3291 		goto fail1;
3292 	}
3293 
3294 	return (0);
3295 
3296 fail1:
3297 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3298 
3299 	return (rc);
3300 }
3301 
3302 	__checkReturn			efx_rc_t
3303 efx_mae_action_set_free(
3304 	__in				efx_nic_t *enp,
3305 	__in				const efx_mae_aset_id_t *aset_idp)
3306 {
3307 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3308 	efx_mcdi_req_t req;
3309 	EFX_MCDI_DECLARE_BUF(payload,
3310 	    MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1),
3311 	    MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
3312 	efx_rc_t rc;
3313 
3314 	if (encp->enc_mae_supported == B_FALSE) {
3315 		rc = ENOTSUP;
3316 		goto fail1;
3317 	}
3318 
3319 	req.emr_cmd = MC_CMD_MAE_ACTION_SET_FREE;
3320 	req.emr_in_buf = payload;
3321 	req.emr_in_length = MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1);
3322 	req.emr_out_buf = payload;
3323 	req.emr_out_length = MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1);
3324 
3325 	MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_FREE_IN_AS_ID, aset_idp->id);
3326 
3327 	efx_mcdi_execute(enp, &req);
3328 
3329 	if (req.emr_rc != 0) {
3330 		rc = req.emr_rc;
3331 		goto fail2;
3332 	}
3333 
3334 	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_FREE_OUT_LENMIN) {
3335 		rc = EMSGSIZE;
3336 		goto fail3;
3337 	}
3338 
3339 	if (MCDI_OUT_DWORD(req, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) !=
3340 	    aset_idp->id) {
3341 		/* Firmware failed to free the action set. */
3342 		rc = EAGAIN;
3343 		goto fail4;
3344 	}
3345 
3346 	return (0);
3347 
3348 fail4:
3349 	EFSYS_PROBE(fail4);
3350 fail3:
3351 	EFSYS_PROBE(fail3);
3352 fail2:
3353 	EFSYS_PROBE(fail2);
3354 fail1:
3355 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3356 	return (rc);
3357 }
3358 
3359 	__checkReturn			efx_rc_t
3360 efx_mae_action_rule_insert(
3361 	__in				efx_nic_t *enp,
3362 	__in				const efx_mae_match_spec_t *spec,
3363 	__in				const efx_mae_aset_list_id_t *asl_idp,
3364 	__in				const efx_mae_aset_id_t *as_idp,
3365 	__out				efx_mae_rule_id_t *ar_idp)
3366 {
3367 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3368 	efx_mcdi_req_t req;
3369 	EFX_MCDI_DECLARE_BUF(payload,
3370 	    MC_CMD_MAE_ACTION_RULE_INSERT_IN_LENMAX_MCDI2,
3371 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
3372 	efx_oword_t *rule_response;
3373 	efx_mae_rule_id_t ar_id;
3374 	size_t offset;
3375 	efx_rc_t rc;
3376 
3377 	EFX_STATIC_ASSERT(sizeof (ar_idp->id) ==
3378 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_AR_ID_LEN);
3379 
3380 	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
3381 	    MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL);
3382 
3383 	if (encp->enc_mae_supported == B_FALSE) {
3384 		rc = ENOTSUP;
3385 		goto fail1;
3386 	}
3387 
3388 	if (spec->emms_type != EFX_MAE_RULE_ACTION ||
3389 	    (asl_idp != NULL && as_idp != NULL) ||
3390 	    (asl_idp == NULL && as_idp == NULL)) {
3391 		rc = EINVAL;
3392 		goto fail2;
3393 	}
3394 
3395 	req.emr_cmd = MC_CMD_MAE_ACTION_RULE_INSERT;
3396 	req.emr_in_buf = payload;
3397 	req.emr_in_length = MC_CMD_MAE_ACTION_RULE_INSERT_IN_LENMAX_MCDI2;
3398 	req.emr_out_buf = payload;
3399 	req.emr_out_length = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN;
3400 
3401 	EFX_STATIC_ASSERT(sizeof (*rule_response) <=
3402 	    MC_CMD_MAE_ACTION_RULE_INSERT_IN_RESPONSE_LEN);
3403 	offset = MC_CMD_MAE_ACTION_RULE_INSERT_IN_RESPONSE_OFST;
3404 	rule_response = (efx_oword_t *)(payload + offset);
3405 	EFX_POPULATE_OWORD_3(*rule_response,
3406 	    MAE_ACTION_RULE_RESPONSE_ASL_ID,
3407 	    (asl_idp != NULL) ? asl_idp->id : EFX_MAE_RSRC_ID_INVALID,
3408 	    MAE_ACTION_RULE_RESPONSE_AS_ID,
3409 	    (as_idp != NULL) ? as_idp->id : EFX_MAE_RSRC_ID_INVALID,
3410 	    MAE_ACTION_RULE_RESPONSE_COUNTER_ID, EFX_MAE_RSRC_ID_INVALID);
3411 
3412 	MCDI_IN_SET_DWORD(req, MAE_ACTION_RULE_INSERT_IN_PRIO, spec->emms_prio);
3413 
3414 	/*
3415 	 * Mask-value pairs have been stored in the byte order needed for the
3416 	 * MCDI request and are thus safe to be copied directly to the buffer.
3417 	 */
3418 	EFX_STATIC_ASSERT(sizeof (spec->emms_mask_value_pairs.action) >=
3419 	    MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN);
3420 	offset = MC_CMD_MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA_OFST;
3421 	memcpy(payload + offset, spec->emms_mask_value_pairs.action,
3422 	    MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN);
3423 
3424 	efx_mcdi_execute(enp, &req);
3425 
3426 	if (req.emr_rc != 0) {
3427 		rc = req.emr_rc;
3428 		goto fail3;
3429 	}
3430 
3431 	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN) {
3432 		rc = EMSGSIZE;
3433 		goto fail4;
3434 	}
3435 
3436 	ar_id.id = MCDI_OUT_DWORD(req, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
3437 	if (ar_id.id == EFX_MAE_RSRC_ID_INVALID) {
3438 		rc = ENOENT;
3439 		goto fail5;
3440 	}
3441 
3442 	ar_idp->id = ar_id.id;
3443 
3444 	return (0);
3445 
3446 fail5:
3447 	EFSYS_PROBE(fail5);
3448 fail4:
3449 	EFSYS_PROBE(fail4);
3450 fail3:
3451 	EFSYS_PROBE(fail3);
3452 fail2:
3453 	EFSYS_PROBE(fail2);
3454 fail1:
3455 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3456 	return (rc);
3457 }
3458 
3459 	__checkReturn			efx_rc_t
3460 efx_mae_action_rule_remove(
3461 	__in				efx_nic_t *enp,
3462 	__in				const efx_mae_rule_id_t *ar_idp)
3463 {
3464 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3465 	efx_mcdi_req_t req;
3466 	EFX_MCDI_DECLARE_BUF(payload,
3467 	    MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1),
3468 	    MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
3469 	efx_rc_t rc;
3470 
3471 	if (encp->enc_mae_supported == B_FALSE) {
3472 		rc = ENOTSUP;
3473 		goto fail1;
3474 	}
3475 
3476 	req.emr_cmd = MC_CMD_MAE_ACTION_RULE_DELETE;
3477 	req.emr_in_buf = payload;
3478 	req.emr_in_length = MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1);
3479 	req.emr_out_buf = payload;
3480 	req.emr_out_length = MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1);
3481 
3482 	MCDI_IN_SET_DWORD(req, MAE_ACTION_RULE_DELETE_IN_AR_ID, ar_idp->id);
3483 
3484 	efx_mcdi_execute(enp, &req);
3485 
3486 	if (req.emr_rc != 0) {
3487 		rc = req.emr_rc;
3488 		goto fail2;
3489 	}
3490 
3491 	if (req.emr_out_length_used <
3492 	    MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LENMIN) {
3493 		rc = EMSGSIZE;
3494 		goto fail3;
3495 	}
3496 
3497 	if (MCDI_OUT_DWORD(req, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) !=
3498 	    ar_idp->id) {
3499 		/* Firmware failed to delete the action rule. */
3500 		rc = EAGAIN;
3501 		goto fail4;
3502 	}
3503 
3504 	return (0);
3505 
3506 fail4:
3507 	EFSYS_PROBE(fail4);
3508 fail3:
3509 	EFSYS_PROBE(fail3);
3510 fail2:
3511 	EFSYS_PROBE(fail2);
3512 fail1:
3513 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3514 	return (rc);
3515 }
3516 
3517 	__checkReturn			efx_rc_t
3518 efx_mcdi_mport_alloc_alias(
3519 	__in				efx_nic_t *enp,
3520 	__out				efx_mport_id_t *mportp,
3521 	__out_opt			uint32_t *labelp)
3522 {
3523 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3524 	efx_mcdi_req_t req;
3525 	EFX_MCDI_DECLARE_BUF(payload,
3526 	    MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_LEN,
3527 	    MC_CMD_MAE_MPORT_ALLOC_ALIAS_OUT_LEN);
3528 	efx_rc_t rc;
3529 
3530 	if (encp->enc_mae_supported == B_FALSE) {
3531 		rc = ENOTSUP;
3532 		goto fail1;
3533 	}
3534 
3535 	req.emr_cmd = MC_CMD_MAE_MPORT_ALLOC;
3536 	req.emr_in_buf = payload;
3537 	req.emr_in_length = MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_LEN;
3538 	req.emr_out_buf = payload;
3539 	req.emr_out_length = MC_CMD_MAE_MPORT_ALLOC_ALIAS_OUT_LEN;
3540 
3541 	MCDI_IN_SET_DWORD(req, MAE_MPORT_ALLOC_IN_TYPE,
3542 			  MC_CMD_MAE_MPORT_ALLOC_IN_MPORT_TYPE_ALIAS);
3543 	MCDI_IN_SET_DWORD(req, MAE_MPORT_ALLOC_ALIAS_IN_DELIVER_MPORT,
3544 			  MAE_MPORT_SELECTOR_ASSIGNED);
3545 
3546 	efx_mcdi_execute(enp, &req);
3547 
3548 	if (req.emr_rc != 0) {
3549 		rc = req.emr_rc;
3550 		goto fail2;
3551 	}
3552 
3553 	mportp->id = MCDI_OUT_DWORD(req, MAE_MPORT_ALLOC_OUT_MPORT_ID);
3554 	if (labelp != NULL)
3555 		*labelp = MCDI_OUT_DWORD(req, MAE_MPORT_ALLOC_ALIAS_OUT_LABEL);
3556 
3557 	return (0);
3558 
3559 fail2:
3560 	EFSYS_PROBE(fail2);
3561 fail1:
3562 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3563 	return (rc);
3564 }
3565 
3566 	__checkReturn			efx_rc_t
3567 efx_mae_mport_free(
3568 	__in				efx_nic_t *enp,
3569 	__in				const efx_mport_id_t *mportp)
3570 {
3571 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3572 	efx_mcdi_req_t req;
3573 	EFX_MCDI_DECLARE_BUF(payload,
3574 	    MC_CMD_MAE_MPORT_FREE_IN_LEN,
3575 	    MC_CMD_MAE_MPORT_FREE_OUT_LEN);
3576 	efx_rc_t rc;
3577 
3578 	if (encp->enc_mae_supported == B_FALSE) {
3579 		rc = ENOTSUP;
3580 		goto fail1;
3581 	}
3582 
3583 	req.emr_cmd = MC_CMD_MAE_MPORT_FREE;
3584 	req.emr_in_buf = payload;
3585 	req.emr_in_length = MC_CMD_MAE_MPORT_FREE_IN_LEN;
3586 	req.emr_out_buf = payload;
3587 	req.emr_out_length = MC_CMD_MAE_MPORT_FREE_OUT_LEN;
3588 
3589 	MCDI_IN_SET_DWORD(req, MAE_MPORT_FREE_IN_MPORT_ID, mportp->id);
3590 
3591 	efx_mcdi_execute(enp, &req);
3592 
3593 	if (req.emr_rc != 0) {
3594 		rc = req.emr_rc;
3595 		goto fail2;
3596 	}
3597 
3598 	return (0);
3599 
3600 fail2:
3601 	EFSYS_PROBE(fail2);
3602 fail1:
3603 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3604 	return (rc);
3605 }
3606 
3607 static	__checkReturn			efx_rc_t
3608 efx_mae_read_mport_journal_single(
3609 	__in				uint8_t *entry_buf,
3610 	__out				efx_mport_desc_t *desc)
3611 {
3612 	uint32_t pcie_intf;
3613 	efx_rc_t rc;
3614 
3615 	memset(desc, 0, sizeof (*desc));
3616 
3617 	desc->emd_id.id = MCDI_STRUCT_DWORD(entry_buf,
3618 	    MAE_MPORT_DESC_V2_MPORT_ID);
3619 
3620 	desc->emd_can_receive_on = MCDI_STRUCT_DWORD_FIELD(entry_buf,
3621 	    MAE_MPORT_DESC_V2_FLAGS,
3622 	    MAE_MPORT_DESC_V2_CAN_RECEIVE_ON);
3623 
3624 	desc->emd_can_deliver_to = MCDI_STRUCT_DWORD_FIELD(entry_buf,
3625 	    MAE_MPORT_DESC_V2_FLAGS,
3626 	    MAE_MPORT_DESC_V2_CAN_DELIVER_TO);
3627 
3628 	desc->emd_can_delete = MCDI_STRUCT_DWORD_FIELD(entry_buf,
3629 	    MAE_MPORT_DESC_V2_FLAGS,
3630 	    MAE_MPORT_DESC_V2_CAN_DELETE);
3631 
3632 	desc->emd_zombie = MCDI_STRUCT_DWORD_FIELD(entry_buf,
3633 	    MAE_MPORT_DESC_V2_FLAGS,
3634 	    MAE_MPORT_DESC_V2_IS_ZOMBIE);
3635 
3636 	desc->emd_type = MCDI_STRUCT_DWORD(entry_buf,
3637 	    MAE_MPORT_DESC_V2_MPORT_TYPE);
3638 
3639 	/*
3640 	 * We can't check everything here. If some additional checks are
3641 	 * required, they should be performed by the callback function.
3642 	 */
3643 	switch (desc->emd_type) {
3644 	case EFX_MPORT_TYPE_NET_PORT:
3645 		desc->emd_net_port.ep_index =
3646 		    MCDI_STRUCT_DWORD(entry_buf,
3647 			MAE_MPORT_DESC_V2_NET_PORT_IDX);
3648 		break;
3649 	case EFX_MPORT_TYPE_ALIAS:
3650 		desc->emd_alias.ea_target_mport_id.id =
3651 		    MCDI_STRUCT_DWORD(entry_buf,
3652 			MAE_MPORT_DESC_V2_ALIAS_DELIVER_MPORT_ID);
3653 		break;
3654 	case EFX_MPORT_TYPE_VNIC:
3655 		desc->emd_vnic.ev_client_type =
3656 		    MCDI_STRUCT_DWORD(entry_buf,
3657 			MAE_MPORT_DESC_V2_VNIC_CLIENT_TYPE);
3658 		if (desc->emd_vnic.ev_client_type !=
3659 		    EFX_MPORT_VNIC_CLIENT_FUNCTION)
3660 			break;
3661 
3662 		pcie_intf = MCDI_STRUCT_DWORD(entry_buf,
3663 		    MAE_MPORT_DESC_V2_VNIC_FUNCTION_INTERFACE);
3664 		rc = efx_mcdi_intf_from_pcie(pcie_intf,
3665 		    &desc->emd_vnic.ev_intf);
3666 		if (rc != 0)
3667 			goto fail1;
3668 
3669 		desc->emd_vnic.ev_pf = MCDI_STRUCT_WORD(entry_buf,
3670 		    MAE_MPORT_DESC_V2_VNIC_FUNCTION_PF_IDX);
3671 		desc->emd_vnic.ev_vf = MCDI_STRUCT_WORD(entry_buf,
3672 		    MAE_MPORT_DESC_V2_VNIC_FUNCTION_VF_IDX);
3673 		desc->emd_vnic.ev_handle = MCDI_STRUCT_DWORD(entry_buf,
3674 		    MAE_MPORT_DESC_V2_VNIC_CLIENT_HANDLE);
3675 		break;
3676 	default:
3677 		rc = EINVAL;
3678 		goto fail2;
3679 	}
3680 
3681 	return (0);
3682 
3683 fail2:
3684 	EFSYS_PROBE(fail2);
3685 fail1:
3686 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3687 	return (rc);
3688 }
3689 
3690 static	__checkReturn			efx_rc_t
3691 efx_mae_read_mport_journal_batch(
3692 	__in				efx_nic_t *enp,
3693 	__in				efx_mae_read_mport_journal_cb *cbp,
3694 	__in				void *cb_datap,
3695 	__out				uint32_t *morep)
3696 {
3697 	efx_mcdi_req_t req;
3698 	EFX_MCDI_DECLARE_BUF(payload,
3699 	    MC_CMD_MAE_MPORT_READ_JOURNAL_IN_LEN,
3700 	    MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMAX_MCDI2);
3701 	uint32_t n_entries;
3702 	uint32_t entry_sz;
3703 	uint8_t *entry_buf;
3704 	unsigned int i;
3705 	efx_rc_t rc;
3706 
3707 	EFX_STATIC_ASSERT(EFX_MPORT_TYPE_NET_PORT ==
3708 	    MAE_MPORT_DESC_V2_MPORT_TYPE_NET_PORT);
3709 	EFX_STATIC_ASSERT(EFX_MPORT_TYPE_ALIAS ==
3710 	    MAE_MPORT_DESC_V2_MPORT_TYPE_ALIAS);
3711 	EFX_STATIC_ASSERT(EFX_MPORT_TYPE_VNIC ==
3712 	    MAE_MPORT_DESC_V2_MPORT_TYPE_VNIC);
3713 
3714 	EFX_STATIC_ASSERT(EFX_MPORT_VNIC_CLIENT_FUNCTION ==
3715 	    MAE_MPORT_DESC_V2_VNIC_CLIENT_TYPE_FUNCTION);
3716 	EFX_STATIC_ASSERT(EFX_MPORT_VNIC_CLIENT_PLUGIN ==
3717 	    MAE_MPORT_DESC_V2_VNIC_CLIENT_TYPE_PLUGIN);
3718 
3719 	if (cbp == NULL) {
3720 		rc = EINVAL;
3721 		goto fail1;
3722 	}
3723 
3724 	req.emr_cmd = MC_CMD_MAE_MPORT_READ_JOURNAL;
3725 	req.emr_in_buf = payload;
3726 	req.emr_in_length = MC_CMD_MAE_MPORT_READ_JOURNAL_IN_LEN;
3727 	req.emr_out_buf = payload;
3728 	req.emr_out_length = MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMAX_MCDI2;
3729 
3730 	MCDI_IN_SET_DWORD(req, MAE_MPORT_READ_JOURNAL_IN_FLAGS, 0);
3731 
3732 	efx_mcdi_execute(enp, &req);
3733 
3734 	if (req.emr_rc != 0) {
3735 		rc = req.emr_rc;
3736 		goto fail2;
3737 	}
3738 
3739 	if (req.emr_out_length_used <
3740 	    MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMIN) {
3741 		rc = EMSGSIZE;
3742 		goto fail3;
3743 	}
3744 
3745 	if (morep != NULL) {
3746 		*morep = MCDI_OUT_DWORD_FIELD(req,
3747 		    MAE_MPORT_READ_JOURNAL_OUT_FLAGS,
3748 		    MAE_MPORT_READ_JOURNAL_OUT_MORE);
3749 	}
3750 	n_entries = MCDI_OUT_DWORD(req,
3751 	    MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_COUNT);
3752 	entry_sz = MCDI_OUT_DWORD(req,
3753 	    MAE_MPORT_READ_JOURNAL_OUT_SIZEOF_MPORT_DESC);
3754 	entry_buf = MCDI_OUT2(req, uint8_t,
3755 	    MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_DATA);
3756 
3757 	if (entry_sz < MAE_MPORT_DESC_V2_VNIC_CLIENT_HANDLE_OFST +
3758 	    MAE_MPORT_DESC_V2_VNIC_CLIENT_HANDLE_LEN) {
3759 		rc = EINVAL;
3760 		goto fail4;
3761 	}
3762 	if (n_entries * entry_sz / entry_sz != n_entries) {
3763 		rc = EINVAL;
3764 		goto fail5;
3765 	}
3766 	if (req.emr_out_length_used !=
3767 	    MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMIN + n_entries * entry_sz) {
3768 		rc = EINVAL;
3769 		goto fail6;
3770 	}
3771 
3772 	for (i = 0; i < n_entries; i++) {
3773 		efx_mport_desc_t desc;
3774 
3775 		rc = efx_mae_read_mport_journal_single(entry_buf, &desc);
3776 		if (rc != 0)
3777 			continue;
3778 
3779 		(*cbp)(cb_datap, &desc, sizeof (desc));
3780 		entry_buf += entry_sz;
3781 	}
3782 
3783 	return (0);
3784 
3785 fail6:
3786 	EFSYS_PROBE(fail6);
3787 fail5:
3788 	EFSYS_PROBE(fail5);
3789 fail4:
3790 	EFSYS_PROBE(fail4);
3791 fail3:
3792 	EFSYS_PROBE(fail3);
3793 fail2:
3794 	EFSYS_PROBE(fail2);
3795 fail1:
3796 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3797 	return (rc);
3798 }
3799 
3800 	__checkReturn			efx_rc_t
3801 efx_mae_read_mport_journal(
3802 	__in				efx_nic_t *enp,
3803 	__in				efx_mae_read_mport_journal_cb *cbp,
3804 	__in				void *cb_datap)
3805 {
3806 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
3807 	uint32_t more = 0;
3808 	efx_rc_t rc;
3809 
3810 	if (encp->enc_mae_supported == B_FALSE) {
3811 		rc = ENOTSUP;
3812 		goto fail1;
3813 	}
3814 
3815 	do {
3816 		rc = efx_mae_read_mport_journal_batch(enp, cbp, cb_datap,
3817 		    &more);
3818 		if (rc != 0)
3819 			goto fail2;
3820 	} while (more != 0);
3821 
3822 	return (0);
3823 
3824 fail2:
3825 	EFSYS_PROBE(fail2);
3826 fail1:
3827 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3828 	return (rc);
3829 }
3830 
3831 #endif /* EFSYS_OPT_MAE */
3832