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