xref: /openbmc/linux/drivers/net/ethernet/sfc/mae.c (revision 54a611b6)
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 bool efx_mae_asl_id(u32 id)
116 {
117 	return !!(id & BIT(31));
118 }
119 
120 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
121 {
122 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
123 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
124 	size_t outlen;
125 	int rc;
126 
127 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
128 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
129 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
130 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
131 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
132 		       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
133 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
134 		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
135 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
136 		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
137 	if (act->deliver)
138 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
139 			       act->dest_mport);
140 	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
141 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
142 			  outbuf, sizeof(outbuf), &outlen);
143 	if (rc)
144 		return rc;
145 	if (outlen < sizeof(outbuf))
146 		return -EIO;
147 	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
148 	/* We rely on the high bit of AS IDs always being clear.
149 	 * The firmware API guarantees this, but let's check it ourselves.
150 	 */
151 	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
152 		efx_mae_free_action_set(efx, act->fw_id);
153 		return -EIO;
154 	}
155 	return 0;
156 }
157 
158 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
159 {
160 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
161 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
162 	size_t outlen;
163 	int rc;
164 
165 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
166 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
167 			  outbuf, sizeof(outbuf), &outlen);
168 	if (rc)
169 		return rc;
170 	if (outlen < sizeof(outbuf))
171 		return -EIO;
172 	/* FW freed a different ID than we asked for, should never happen.
173 	 * Warn because it means we've now got a different idea to the FW of
174 	 * what action-sets exist, which could cause mayhem later.
175 	 */
176 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
177 		return -EIO;
178 	return 0;
179 }
180 
181 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
182 				  struct efx_tc_action_set_list *acts)
183 {
184 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
185 	struct efx_tc_action_set *act;
186 	size_t inlen, outlen, i = 0;
187 	efx_dword_t *inbuf;
188 	int rc;
189 
190 	list_for_each_entry(act, &acts->list, list)
191 		i++;
192 	if (i == 0)
193 		return -EINVAL;
194 	if (i == 1) {
195 		/* Don't wrap an ASL around a single AS, just use the AS_ID
196 		 * directly.  ASLs are a more limited resource.
197 		 */
198 		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
199 		acts->fw_id = act->fw_id;
200 		return 0;
201 	}
202 	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
203 		return -EOPNOTSUPP; /* Too many actions */
204 	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
205 	inbuf = kzalloc(inlen, GFP_KERNEL);
206 	if (!inbuf)
207 		return -ENOMEM;
208 	i = 0;
209 	list_for_each_entry(act, &acts->list, list) {
210 		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
211 				     i, act->fw_id);
212 		i++;
213 	}
214 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
215 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
216 			  outbuf, sizeof(outbuf), &outlen);
217 	if (rc)
218 		goto out_free;
219 	if (outlen < sizeof(outbuf)) {
220 		rc = -EIO;
221 		goto out_free;
222 	}
223 	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
224 	/* We rely on the high bit of ASL IDs always being set.
225 	 * The firmware API guarantees this, but let's check it ourselves.
226 	 */
227 	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
228 		efx_mae_free_action_set_list(efx, acts);
229 		rc = -EIO;
230 	}
231 out_free:
232 	kfree(inbuf);
233 	return rc;
234 }
235 
236 int efx_mae_free_action_set_list(struct efx_nic *efx,
237 				 struct efx_tc_action_set_list *acts)
238 {
239 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
240 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
241 	size_t outlen;
242 	int rc;
243 
244 	/* If this is just an AS_ID with no ASL wrapper, then there is
245 	 * nothing for us to free.  (The AS will be freed later.)
246 	 */
247 	if (efx_mae_asl_id(acts->fw_id)) {
248 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
249 			       acts->fw_id);
250 		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
251 				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
252 		if (rc)
253 			return rc;
254 		if (outlen < sizeof(outbuf))
255 			return -EIO;
256 		/* FW freed a different ID than we asked for, should never happen.
257 		 * Warn because it means we've now got a different idea to the FW of
258 		 * what action-set-lists exist, which could cause mayhem later.
259 		 */
260 		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
261 			return -EIO;
262 	}
263 	/* We're probably about to free @acts, but let's just make sure its
264 	 * fw_id is blatted so that it won't look valid if it leaks out.
265 	 */
266 	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
267 	return 0;
268 }
269 
270 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
271 					   const struct efx_tc_match *match)
272 {
273 	if (match->mask.ingress_port) {
274 		if (~match->mask.ingress_port)
275 			return -EOPNOTSUPP;
276 		MCDI_STRUCT_SET_DWORD(match_crit,
277 				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
278 				      match->value.ingress_port);
279 	}
280 	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
281 			      match->mask.ingress_port);
282 	return 0;
283 }
284 
285 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
286 			u32 prio, u32 acts_id, u32 *id)
287 {
288 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
289 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
290 	MCDI_DECLARE_STRUCT_PTR(match_crit);
291 	MCDI_DECLARE_STRUCT_PTR(response);
292 	size_t outlen;
293 	int rc;
294 
295 	if (!id)
296 		return -EINVAL;
297 
298 	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
299 	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
300 	if (efx_mae_asl_id(acts_id)) {
301 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
302 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
303 				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
304 	} else {
305 		/* We only had one AS, so we didn't wrap it in an ASL */
306 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
307 				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
308 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
309 	}
310 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
311 	rc = efx_mae_populate_match_criteria(match_crit, match);
312 	if (rc)
313 		return rc;
314 
315 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
316 			  outbuf, sizeof(outbuf), &outlen);
317 	if (rc)
318 		return rc;
319 	if (outlen < sizeof(outbuf))
320 		return -EIO;
321 	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
322 	return 0;
323 }
324 
325 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
326 {
327 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
328 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
329 	size_t outlen;
330 	int rc;
331 
332 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
333 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
334 			  outbuf, sizeof(outbuf), &outlen);
335 	if (rc)
336 		return rc;
337 	if (outlen < sizeof(outbuf))
338 		return -EIO;
339 	/* FW freed a different ID than we asked for, should also never happen.
340 	 * Warn because it means we've now got a different idea to the FW of
341 	 * what rules exist, which could cause mayhem later.
342 	 */
343 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
344 		return -EIO;
345 	return 0;
346 }
347