xref: /openbmc/linux/drivers/net/ethernet/sfc/mae.c (revision e1f4ecab)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /****************************************************************************
3  * Driver for Solarflare network controllers and boards
4  * Copyright 2019 Solarflare Communications Inc.
5  * Copyright 2020-2022 Xilinx Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published
9  * by the Free Software Foundation, incorporated herein by reference.
10  */
11 
12 #include "mae.h"
13 #include "mcdi.h"
14 #include "mcdi_pcol_mae.h"
15 
16 int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label)
17 {
18 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_OUT_LEN);
19 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_LEN);
20 	size_t outlen;
21 	int rc;
22 
23 	if (WARN_ON_ONCE(!id))
24 		return -EINVAL;
25 	if (WARN_ON_ONCE(!label))
26 		return -EINVAL;
27 
28 	MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_TYPE,
29 		       MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_MPORT_TYPE_ALIAS);
30 	MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_DELIVER_MPORT,
31 		       MAE_MPORT_SELECTOR_ASSIGNED);
32 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_ALLOC, inbuf, sizeof(inbuf),
33 			  outbuf, sizeof(outbuf), &outlen);
34 	if (rc)
35 		return rc;
36 	if (outlen < sizeof(outbuf))
37 		return -EIO;
38 	*id = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_MPORT_ID);
39 	*label = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_LABEL);
40 	return 0;
41 }
42 
43 int efx_mae_free_mport(struct efx_nic *efx, u32 id)
44 {
45 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_FREE_IN_LEN);
46 
47 	BUILD_BUG_ON(MC_CMD_MAE_MPORT_FREE_OUT_LEN);
48 	MCDI_SET_DWORD(inbuf, MAE_MPORT_FREE_IN_MPORT_ID, id);
49 	return efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_FREE, inbuf, sizeof(inbuf),
50 			    NULL, 0, NULL);
51 }
52 
53 void efx_mae_mport_wire(struct efx_nic *efx, u32 *out)
54 {
55 	efx_dword_t mport;
56 
57 	EFX_POPULATE_DWORD_2(mport,
58 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
59 			     MAE_MPORT_SELECTOR_PPORT_ID, efx->port_num);
60 	*out = EFX_DWORD_VAL(mport);
61 }
62 
63 void efx_mae_mport_uplink(struct efx_nic *efx __always_unused, u32 *out)
64 {
65 	efx_dword_t mport;
66 
67 	EFX_POPULATE_DWORD_3(mport,
68 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
69 			     MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
70 			     MAE_MPORT_SELECTOR_FUNC_VF_ID, MAE_MPORT_SELECTOR_FUNC_VF_ID_NULL);
71 	*out = EFX_DWORD_VAL(mport);
72 }
73 
74 void efx_mae_mport_vf(struct efx_nic *efx __always_unused, u32 vf_id, u32 *out)
75 {
76 	efx_dword_t mport;
77 
78 	EFX_POPULATE_DWORD_3(mport,
79 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
80 			     MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
81 			     MAE_MPORT_SELECTOR_FUNC_VF_ID, vf_id);
82 	*out = EFX_DWORD_VAL(mport);
83 }
84 
85 /* Constructs an mport selector from an mport ID, because they're not the same */
86 void efx_mae_mport_mport(struct efx_nic *efx __always_unused, u32 mport_id, u32 *out)
87 {
88 	efx_dword_t mport;
89 
90 	EFX_POPULATE_DWORD_2(mport,
91 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_MPORT_ID,
92 			     MAE_MPORT_SELECTOR_MPORT_ID, mport_id);
93 	*out = EFX_DWORD_VAL(mport);
94 }
95 
96 /* id is really only 24 bits wide */
97 int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
98 {
99 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN);
100 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN);
101 	size_t outlen;
102 	int rc;
103 
104 	MCDI_SET_DWORD(inbuf, MAE_MPORT_LOOKUP_IN_MPORT_SELECTOR, selector);
105 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_LOOKUP, inbuf, sizeof(inbuf),
106 			  outbuf, sizeof(outbuf), &outlen);
107 	if (rc)
108 		return rc;
109 	if (outlen < sizeof(outbuf))
110 		return -EIO;
111 	*id = MCDI_DWORD(outbuf, MAE_MPORT_LOOKUP_OUT_MPORT_ID);
112 	return 0;
113 }
114 
115 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
116 {
117 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
118 	size_t outlen;
119 	int rc;
120 
121 	BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN);
122 
123 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf,
124 			  sizeof(outbuf), &outlen);
125 	if (rc)
126 		return rc;
127 	if (outlen < sizeof(outbuf))
128 		return -EIO;
129 	caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
130 	caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
131 	return 0;
132 }
133 
134 static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
135 				   u8 *field_support)
136 {
137 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
138 	MCDI_DECLARE_STRUCT_PTR(caps);
139 	unsigned int count;
140 	size_t outlen;
141 	int rc, i;
142 
143 	BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);
144 
145 	rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
146 	if (rc)
147 		return rc;
148 	count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
149 	memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
150 	caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
151 	/* We're only interested in the support status enum, not any other
152 	 * flags, so just extract that from each entry.
153 	 */
154 	for (i = 0; i < count; i++)
155 		if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen)
156 			field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS);
157 	return 0;
158 }
159 
160 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
161 {
162 	int rc;
163 
164 	rc = efx_mae_get_basic_caps(efx, caps);
165 	if (rc)
166 		return rc;
167 	return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
168 				       caps->action_rule_fields);
169 }
170 
171 /* Bit twiddling:
172  * Prefix: 1...110...0
173  *      ~: 0...001...1
174  *    + 1: 0...010...0 is power of two
175  * so (~x) & ((~x) + 1) == 0.  Converse holds also.
176  */
177 #define is_prefix_byte(_x)	!(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1))
178 
179 enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };
180 
181 static const char *mask_type_name(enum mask_type typ)
182 {
183 	switch (typ) {
184 	case MASK_ONES:
185 		return "all-1s";
186 	case MASK_ZEROES:
187 		return "all-0s";
188 	case MASK_PREFIX:
189 		return "prefix";
190 	case MASK_OTHER:
191 		return "arbitrary";
192 	default: /* can't happen */
193 		return "unknown";
194 	}
195 }
196 
197 /* Checks a (big-endian) bytestring is a bit prefix */
198 static enum mask_type classify_mask(const u8 *mask, size_t len)
199 {
200 	bool zeroes = true; /* All bits seen so far are zeroes */
201 	bool ones = true; /* All bits seen so far are ones */
202 	bool prefix = true; /* Valid prefix so far */
203 	size_t i;
204 
205 	for (i = 0; i < len; i++) {
206 		if (ones) {
207 			if (!is_prefix_byte(mask[i]))
208 				prefix = false;
209 		} else if (mask[i]) {
210 			prefix = false;
211 		}
212 		if (mask[i] != 0xff)
213 			ones = false;
214 		if (mask[i])
215 			zeroes = false;
216 	}
217 	if (ones)
218 		return MASK_ONES;
219 	if (zeroes)
220 		return MASK_ZEROES;
221 	if (prefix)
222 		return MASK_PREFIX;
223 	return MASK_OTHER;
224 }
225 
226 static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)
227 {
228 	switch (support) {
229 	case MAE_FIELD_UNSUPPORTED:
230 	case MAE_FIELD_SUPPORTED_MATCH_NEVER:
231 		if (typ == MASK_ZEROES)
232 			return 0;
233 		return -EOPNOTSUPP;
234 	case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
235 		if (typ == MASK_ZEROES)
236 			return 0;
237 		fallthrough;
238 	case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
239 		if (typ == MASK_ONES)
240 			return 0;
241 		return -EINVAL;
242 	case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
243 		if (typ == MASK_OTHER)
244 			return -EOPNOTSUPP;
245 		return 0;
246 	case MAE_FIELD_SUPPORTED_MATCH_MASK:
247 		return 0;
248 	default:
249 		return -EIO;
250 	}
251 }
252 
253 /* Validate field mask against hardware capabilities.  Captures caller's 'rc' */
254 #define CHECK(_mcdi, _field)	({					       \
255 	enum mask_type typ = classify_mask((const u8 *)&mask->_field,	       \
256 					   sizeof(mask->_field));	       \
257 									       \
258 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
259 					 typ);				       \
260 	if (rc)								       \
261 		NL_SET_ERR_MSG_FMT_MOD(extack,				       \
262 				       "No support for %s mask in field %s",   \
263 				       mask_type_name(typ), #_field);	       \
264 	rc;								       \
265 })
266 /* Booleans need special handling */
267 #define CHECK_BIT(_mcdi, _field)	({				       \
268 	enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES;	       \
269 									       \
270 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
271 					 typ);				       \
272 	if (rc)								       \
273 		NL_SET_ERR_MSG_FMT_MOD(extack,				       \
274 				       "No support for %s mask in field %s",   \
275 				       mask_type_name(typ), #_field);	       \
276 	rc;								       \
277 })
278 
279 int efx_mae_match_check_caps(struct efx_nic *efx,
280 			     const struct efx_tc_match_fields *mask,
281 			     struct netlink_ext_ack *extack)
282 {
283 	const u8 *supported_fields = efx->tc->caps->action_rule_fields;
284 	__be32 ingress_port = cpu_to_be32(mask->ingress_port);
285 	enum mask_type ingress_port_mask_type;
286 	int rc;
287 
288 	/* Check for _PREFIX assumes big-endian, so we need to convert */
289 	ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
290 					       sizeof(ingress_port));
291 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
292 					 ingress_port_mask_type);
293 	if (rc) {
294 		NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
295 				       mask_type_name(ingress_port_mask_type));
296 		return rc;
297 	}
298 	if (CHECK(ETHER_TYPE, eth_proto) ||
299 	    CHECK(VLAN0_TCI, vlan_tci[0]) ||
300 	    CHECK(VLAN0_PROTO, vlan_proto[0]) ||
301 	    CHECK(VLAN1_TCI, vlan_tci[1]) ||
302 	    CHECK(VLAN1_PROTO, vlan_proto[1]) ||
303 	    CHECK(ETH_SADDR, eth_saddr) ||
304 	    CHECK(ETH_DADDR, eth_daddr) ||
305 	    CHECK(IP_PROTO, ip_proto) ||
306 	    CHECK(IP_TOS, ip_tos) ||
307 	    CHECK(IP_TTL, ip_ttl) ||
308 	    CHECK(SRC_IP4, src_ip) ||
309 	    CHECK(DST_IP4, dst_ip) ||
310 #ifdef CONFIG_IPV6
311 	    CHECK(SRC_IP6, src_ip6) ||
312 	    CHECK(DST_IP6, dst_ip6) ||
313 #endif
314 	    CHECK(L4_SPORT, l4_sport) ||
315 	    CHECK(L4_DPORT, l4_dport) ||
316 	    CHECK(TCP_FLAGS, tcp_flags) ||
317 	    CHECK_BIT(IS_IP_FRAG, ip_frag) ||
318 	    CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) ||
319 	    CHECK(RECIRC_ID, recirc_id))
320 		return rc;
321 	return 0;
322 }
323 #undef CHECK_BIT
324 #undef CHECK
325 
326 static bool efx_mae_asl_id(u32 id)
327 {
328 	return !!(id & BIT(31));
329 }
330 
331 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
332 {
333 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
334 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
335 	size_t outlen;
336 	int rc;
337 
338 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
339 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
340 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
341 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
342 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
343 		       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
344 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
345 		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
346 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
347 		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
348 	if (act->deliver)
349 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
350 			       act->dest_mport);
351 	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
352 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
353 			  outbuf, sizeof(outbuf), &outlen);
354 	if (rc)
355 		return rc;
356 	if (outlen < sizeof(outbuf))
357 		return -EIO;
358 	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
359 	/* We rely on the high bit of AS IDs always being clear.
360 	 * The firmware API guarantees this, but let's check it ourselves.
361 	 */
362 	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
363 		efx_mae_free_action_set(efx, act->fw_id);
364 		return -EIO;
365 	}
366 	return 0;
367 }
368 
369 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
370 {
371 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
372 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
373 	size_t outlen;
374 	int rc;
375 
376 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
377 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
378 			  outbuf, sizeof(outbuf), &outlen);
379 	if (rc)
380 		return rc;
381 	if (outlen < sizeof(outbuf))
382 		return -EIO;
383 	/* FW freed a different ID than we asked for, should never happen.
384 	 * Warn because it means we've now got a different idea to the FW of
385 	 * what action-sets exist, which could cause mayhem later.
386 	 */
387 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
388 		return -EIO;
389 	return 0;
390 }
391 
392 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
393 				  struct efx_tc_action_set_list *acts)
394 {
395 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
396 	struct efx_tc_action_set *act;
397 	size_t inlen, outlen, i = 0;
398 	efx_dword_t *inbuf;
399 	int rc;
400 
401 	list_for_each_entry(act, &acts->list, list)
402 		i++;
403 	if (i == 0)
404 		return -EINVAL;
405 	if (i == 1) {
406 		/* Don't wrap an ASL around a single AS, just use the AS_ID
407 		 * directly.  ASLs are a more limited resource.
408 		 */
409 		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
410 		acts->fw_id = act->fw_id;
411 		return 0;
412 	}
413 	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
414 		return -EOPNOTSUPP; /* Too many actions */
415 	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
416 	inbuf = kzalloc(inlen, GFP_KERNEL);
417 	if (!inbuf)
418 		return -ENOMEM;
419 	i = 0;
420 	list_for_each_entry(act, &acts->list, list) {
421 		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
422 				     i, act->fw_id);
423 		i++;
424 	}
425 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
426 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
427 			  outbuf, sizeof(outbuf), &outlen);
428 	if (rc)
429 		goto out_free;
430 	if (outlen < sizeof(outbuf)) {
431 		rc = -EIO;
432 		goto out_free;
433 	}
434 	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
435 	/* We rely on the high bit of ASL IDs always being set.
436 	 * The firmware API guarantees this, but let's check it ourselves.
437 	 */
438 	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
439 		efx_mae_free_action_set_list(efx, acts);
440 		rc = -EIO;
441 	}
442 out_free:
443 	kfree(inbuf);
444 	return rc;
445 }
446 
447 int efx_mae_free_action_set_list(struct efx_nic *efx,
448 				 struct efx_tc_action_set_list *acts)
449 {
450 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
451 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
452 	size_t outlen;
453 	int rc;
454 
455 	/* If this is just an AS_ID with no ASL wrapper, then there is
456 	 * nothing for us to free.  (The AS will be freed later.)
457 	 */
458 	if (efx_mae_asl_id(acts->fw_id)) {
459 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
460 			       acts->fw_id);
461 		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
462 				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
463 		if (rc)
464 			return rc;
465 		if (outlen < sizeof(outbuf))
466 			return -EIO;
467 		/* FW freed a different ID than we asked for, should never happen.
468 		 * Warn because it means we've now got a different idea to the FW of
469 		 * what action-set-lists exist, which could cause mayhem later.
470 		 */
471 		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
472 			return -EIO;
473 	}
474 	/* We're probably about to free @acts, but let's just make sure its
475 	 * fw_id is blatted so that it won't look valid if it leaks out.
476 	 */
477 	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
478 	return 0;
479 }
480 
481 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
482 					   const struct efx_tc_match *match)
483 {
484 	if (match->mask.ingress_port) {
485 		if (~match->mask.ingress_port)
486 			return -EOPNOTSUPP;
487 		MCDI_STRUCT_SET_DWORD(match_crit,
488 				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
489 				      match->value.ingress_port);
490 	}
491 	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
492 			      match->mask.ingress_port);
493 	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS),
494 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
495 			     match->value.ip_frag,
496 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
497 			     match->value.ip_firstfrag);
498 	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK),
499 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
500 			     match->mask.ip_frag,
501 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
502 			     match->mask.ip_firstfrag);
503 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
504 			     match->value.recirc_id);
505 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
506 			     match->mask.recirc_id);
507 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE,
508 				match->value.eth_proto);
509 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK,
510 				match->mask.eth_proto);
511 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE,
512 				match->value.vlan_tci[0]);
513 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK,
514 				match->mask.vlan_tci[0]);
515 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE,
516 				match->value.vlan_proto[0]);
517 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK,
518 				match->mask.vlan_proto[0]);
519 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE,
520 				match->value.vlan_tci[1]);
521 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK,
522 				match->mask.vlan_tci[1]);
523 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE,
524 				match->value.vlan_proto[1]);
525 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK,
526 				match->mask.vlan_proto[1]);
527 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE),
528 	       match->value.eth_saddr, ETH_ALEN);
529 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK),
530 	       match->mask.eth_saddr, ETH_ALEN);
531 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE),
532 	       match->value.eth_daddr, ETH_ALEN);
533 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK),
534 	       match->mask.eth_daddr, ETH_ALEN);
535 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO,
536 			     match->value.ip_proto);
537 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK,
538 			     match->mask.ip_proto);
539 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS,
540 			     match->value.ip_tos);
541 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK,
542 			     match->mask.ip_tos);
543 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL,
544 			     match->value.ip_ttl);
545 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK,
546 			     match->mask.ip_ttl);
547 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE,
548 				 match->value.src_ip);
549 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK,
550 				 match->mask.src_ip);
551 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE,
552 				 match->value.dst_ip);
553 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK,
554 				 match->mask.dst_ip);
555 #ifdef CONFIG_IPV6
556 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE),
557 	       &match->value.src_ip6, sizeof(struct in6_addr));
558 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK),
559 	       &match->mask.src_ip6, sizeof(struct in6_addr));
560 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE),
561 	       &match->value.dst_ip6, sizeof(struct in6_addr));
562 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK),
563 	       &match->mask.dst_ip6, sizeof(struct in6_addr));
564 #endif
565 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE,
566 				match->value.l4_sport);
567 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK,
568 				match->mask.l4_sport);
569 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE,
570 				match->value.l4_dport);
571 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK,
572 				match->mask.l4_dport);
573 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE,
574 				match->value.tcp_flags);
575 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK,
576 				match->mask.tcp_flags);
577 	return 0;
578 }
579 
580 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
581 			u32 prio, u32 acts_id, u32 *id)
582 {
583 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
584 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
585 	MCDI_DECLARE_STRUCT_PTR(match_crit);
586 	MCDI_DECLARE_STRUCT_PTR(response);
587 	size_t outlen;
588 	int rc;
589 
590 	if (!id)
591 		return -EINVAL;
592 
593 	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
594 	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
595 	if (efx_mae_asl_id(acts_id)) {
596 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
597 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
598 				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
599 	} else {
600 		/* We only had one AS, so we didn't wrap it in an ASL */
601 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
602 				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
603 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
604 	}
605 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
606 	rc = efx_mae_populate_match_criteria(match_crit, match);
607 	if (rc)
608 		return rc;
609 
610 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
611 			  outbuf, sizeof(outbuf), &outlen);
612 	if (rc)
613 		return rc;
614 	if (outlen < sizeof(outbuf))
615 		return -EIO;
616 	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
617 	return 0;
618 }
619 
620 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
621 {
622 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
623 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
624 	size_t outlen;
625 	int rc;
626 
627 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
628 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
629 			  outbuf, sizeof(outbuf), &outlen);
630 	if (rc)
631 		return rc;
632 	if (outlen < sizeof(outbuf))
633 		return -EIO;
634 	/* FW freed a different ID than we asked for, should also never happen.
635 	 * Warn because it means we've now got a different idea to the FW of
636 	 * what rules exist, which could cause mayhem later.
637 	 */
638 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
639 		return -EIO;
640 	return 0;
641 }
642