Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sfc: Cope with permissions enforcement added to firmware for SR-IOV

* Accept EPERM in some simple cases, the following cases are handled:
1) efx_mcdi_read_assertion()
Unprivileged PCI functions aren't allowed to GET_ASSERTS.
We return success as it's up to the primary PF to deal with asserts.
2) efx_mcdi_mon_probe() in efx_ef10_probe()
Unprivileged PCI functions aren't allowed to read sensor info, and
worrying about sensor data is the primary PF's job.
3) phy_op->reconfigure() in efx_init_port() and efx_reset_up()
Unprivileged functions aren't allowed to MC_CMD_SET_LINK, they just have
to accept the settings (including flow-control, which is what
efx_init_port() is worried about) they've been given.
4) Fallback to GET_WORKAROUNDS in efx_ef10_probe()
Unprivileged PCI functions aren't allowed to set workarounds. So if
efx_mcdi_set_workaround() fails EPERM, use efx_mcdi_get_workarounds()
to find out if workaround_35388 is enabled.
5) If DRV_ATTACH gets EPERM, try without specifying fw-variant
Unprivileged PCI functions have to use a FIRMWARE_ID of 0xffffffff
(MC_CMD_FW_DONT_CARE).
6) Don't try to exit_assertion unless one had fired
Previously we called efx_mcdi_exit_assertion even if
efx_mcdi_read_assertion had received MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS.
This is unnecessary, and the resulting MC_CMD_REBOOT, even if the
AFTER_ASSERTION flag made it a no-op, would fail EPERM for unprivileged
PCI functions.
So make efx_mcdi_read_assertion return whether an assert happened, and only
call efx_mcdi_exit_assertion if it has.

Signed-off-by: Shradha Shah <sshah@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Edward Cree and committed by
David S. Miller
267d9d73 7b8c7b54

+111 -17
+14 -2
drivers/net/ethernet/sfc/ef10.c
··· 286 286 goto fail3; 287 287 efx->timer_quantum_ns = 1536000 / rc; /* 1536 cycles */ 288 288 289 - /* Check whether firmware supports bug 35388 workaround */ 289 + /* Check whether firmware supports bug 35388 workaround. 290 + * First try to enable it, then if we get EPERM, just 291 + * ask if it's already enabled 292 + */ 290 293 rc = efx_mcdi_set_workaround(efx, MC_CMD_WORKAROUND_BUG35388, true); 291 294 if (rc == 0) 292 295 nic_data->workaround_35388 = true; 296 + else if (rc == -EPERM) { 297 + unsigned int enabled; 298 + 299 + rc = efx_mcdi_get_workarounds(efx, NULL, &enabled); 300 + if (rc) 301 + goto fail3; 302 + nic_data->workaround_35388 = enabled & 303 + MC_CMD_GET_WORKAROUNDS_OUT_BUG35388; 304 + } 293 305 else if (rc != -ENOSYS && rc != -ENOENT) 294 306 goto fail3; 295 307 netif_dbg(efx, probe, efx->net_dev, ··· 309 297 nic_data->workaround_35388 ? "en" : "dis"); 310 298 311 299 rc = efx_mcdi_mon_probe(efx); 312 - if (rc) 300 + if (rc && rc != -EPERM) 313 301 goto fail3; 314 302 315 303 efx_ptp_probe(efx, NULL);
+3 -2
drivers/net/ethernet/sfc/efx.c
··· 1046 1046 1047 1047 /* Ensure the PHY advertises the correct flow control settings */ 1048 1048 rc = efx->phy_op->reconfigure(efx); 1049 - if (rc) 1049 + if (rc && rc != -EPERM) 1050 1050 goto fail2; 1051 1051 1052 1052 mutex_unlock(&efx->mac_lock); ··· 2429 2429 rc = efx->phy_op->init(efx); 2430 2430 if (rc) 2431 2431 goto fail; 2432 - if (efx->phy_op->reconfigure(efx)) 2432 + rc = efx->phy_op->reconfigure(efx); 2433 + if (rc && rc != -EPERM) 2433 2434 netif_err(efx, drv, efx->net_dev, 2434 2435 "could not restore PHY settings\n"); 2435 2436 }
+69 -13
drivers/net/ethernet/sfc/mcdi.c
··· 1142 1142 MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1); 1143 1143 MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_LOW_LATENCY); 1144 1144 1145 - rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), 1146 - outbuf, sizeof(outbuf), &outlen); 1147 - if (rc) 1145 + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), 1146 + outbuf, sizeof(outbuf), &outlen); 1147 + /* If we're not the primary PF, trying to ATTACH with a FIRMWARE_ID 1148 + * specified will fail with EPERM, and we have to tell the MC we don't 1149 + * care what firmware we get. 1150 + */ 1151 + if (rc == -EPERM) { 1152 + netif_dbg(efx, probe, efx->net_dev, 1153 + "efx_mcdi_drv_attach with fw-variant setting failed EPERM, trying without it\n"); 1154 + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID, 1155 + MC_CMD_FW_DONT_CARE); 1156 + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf, 1157 + sizeof(inbuf), outbuf, sizeof(outbuf), 1158 + &outlen); 1159 + } 1160 + if (rc) { 1161 + efx_mcdi_display_error(efx, MC_CMD_DRV_ATTACH, sizeof(inbuf), 1162 + outbuf, outlen, rc); 1148 1163 goto fail; 1164 + } 1149 1165 if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) { 1150 1166 rc = -EIO; 1151 1167 goto fail; ··· 1393 1377 return rc; 1394 1378 } 1395 1379 1380 + /* Returns 1 if an assertion was read, 0 if no assertion had fired, 1381 + * negative on error. 1382 + */ 1396 1383 static int efx_mcdi_read_assertion(struct efx_nic *efx) 1397 1384 { 1398 1385 MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_ASSERTS_IN_LEN); ··· 1417 1398 rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_ASSERTS, 1418 1399 inbuf, MC_CMD_GET_ASSERTS_IN_LEN, 1419 1400 outbuf, sizeof(outbuf), &outlen); 1401 + if (rc == -EPERM) 1402 + return 0; 1420 1403 } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); 1421 1404 1422 1405 if (rc) { ··· 1456 1435 MCDI_ARRAY_DWORD(outbuf, GET_ASSERTS_OUT_GP_REGS_OFFS, 1457 1436 index)); 1458 1437 1459 - return 0; 1438 + return 1; 1460 1439 } 1461 1440 1462 - static void efx_mcdi_exit_assertion(struct efx_nic *efx) 1441 + static int efx_mcdi_exit_assertion(struct efx_nic *efx) 1463 1442 { 1464 1443 MCDI_DECLARE_BUF(inbuf, MC_CMD_REBOOT_IN_LEN); 1444 + int rc; 1465 1445 1466 1446 /* If the MC is running debug firmware, it might now be 1467 1447 * waiting for a debugger to attach, but we just want it to 1468 1448 * reboot. We set a flag that makes the command a no-op if it 1469 - * has already done so. We don't know what return code to 1470 - * expect (0 or -EIO), so ignore it. 1449 + * has already done so. 1450 + * The MCDI will thus return either 0 or -EIO. 1471 1451 */ 1472 1452 BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); 1473 1453 MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 1474 1454 MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); 1475 - (void) efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN, 1476 - NULL, 0, NULL); 1455 + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN, 1456 + NULL, 0, NULL); 1457 + if (rc == -EIO) 1458 + rc = 0; 1459 + if (rc) 1460 + efx_mcdi_display_error(efx, MC_CMD_REBOOT, MC_CMD_REBOOT_IN_LEN, 1461 + NULL, 0, rc); 1462 + return rc; 1477 1463 } 1478 1464 1479 1465 int efx_mcdi_handle_assertion(struct efx_nic *efx) ··· 1488 1460 int rc; 1489 1461 1490 1462 rc = efx_mcdi_read_assertion(efx); 1491 - if (rc) 1463 + if (rc <= 0) 1492 1464 return rc; 1493 1465 1494 - efx_mcdi_exit_assertion(efx); 1495 - 1496 - return 0; 1466 + return efx_mcdi_exit_assertion(efx); 1497 1467 } 1498 1468 1499 1469 void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) ··· 1704 1678 MCDI_SET_DWORD(inbuf, WORKAROUND_IN_ENABLED, enabled); 1705 1679 return efx_mcdi_rpc(efx, MC_CMD_WORKAROUND, inbuf, sizeof(inbuf), 1706 1680 NULL, 0, NULL); 1681 + } 1682 + 1683 + int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out, 1684 + unsigned int *enabled_out) 1685 + { 1686 + MCDI_DECLARE_BUF_OUT_OR_ERR(outbuf, MC_CMD_GET_WORKAROUNDS_OUT_LEN); 1687 + size_t outlen; 1688 + int rc; 1689 + 1690 + rc = efx_mcdi_rpc(efx, MC_CMD_GET_WORKAROUNDS, NULL, 0, 1691 + outbuf, sizeof(outbuf), &outlen); 1692 + if (rc) 1693 + goto fail; 1694 + 1695 + if (outlen < MC_CMD_GET_WORKAROUNDS_OUT_LEN) { 1696 + rc = -EIO; 1697 + goto fail; 1698 + } 1699 + 1700 + if (impl_out) 1701 + *impl_out = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_IMPLEMENTED); 1702 + 1703 + if (enabled_out) 1704 + *enabled_out = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_ENABLED); 1705 + 1706 + return 0; 1707 + 1708 + fail: 1709 + netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); 1710 + return rc; 1707 1711 } 1708 1712 1709 1713 #ifdef CONFIG_SFC_MTD
+2
drivers/net/ethernet/sfc/mcdi.h
··· 339 339 enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason); 340 340 int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method); 341 341 int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled); 342 + int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out, 343 + unsigned int *enabled_out); 342 344 343 345 #ifdef CONFIG_SFC_MCDI_MON 344 346 int efx_mcdi_mon_probe(struct efx_nic *efx);
+23
drivers/net/ethernet/sfc/mcdi_pcol.h
··· 1875 1875 #define MC_CMD_FW_FULL_FEATURED 0x0 1876 1876 /* enum: Prefer to use firmware with fewer features but lower latency */ 1877 1877 #define MC_CMD_FW_LOW_LATENCY 0x1 1878 + /* enum: Only this option is allowed for non-admin functions */ 1879 + #define MC_CMD_FW_DONT_CARE 0xffffffff 1878 1880 1879 1881 /* MC_CMD_DRV_ATTACH_OUT msgresponse */ 1880 1882 #define MC_CMD_DRV_ATTACH_OUT_LEN 4 ··· 4086 4084 #define LICENSED_APP_ID_SOLARCAPTURE_PRO 0x4 4087 4085 #define LICENSED_APP_ID_ID_LBN 0 4088 4086 #define LICENSED_APP_ID_ID_WIDTH 32 4087 + 4088 + 4089 + /***********************************/ 4090 + /* MC_CMD_GET_WORKAROUNDS 4091 + * Read the list of all implemented and all currently enabled workarounds. The 4092 + * enums here must correspond with those in MC_CMD_WORKAROUND. 4093 + */ 4094 + #define MC_CMD_GET_WORKAROUNDS 0x59 4095 + 4096 + /* MC_CMD_GET_WORKAROUNDS_OUT msgresponse */ 4097 + #define MC_CMD_GET_WORKAROUNDS_OUT_LEN 8 4098 + /* Each workaround is represented by a single bit according to the enums below. 4099 + */ 4100 + #define MC_CMD_GET_WORKAROUNDS_OUT_IMPLEMENTED_OFST 0 4101 + #define MC_CMD_GET_WORKAROUNDS_OUT_ENABLED_OFST 4 4102 + /* enum: Bug 17230 work around. */ 4103 + #define MC_CMD_GET_WORKAROUNDS_OUT_BUG17230 0x2 4104 + /* enum: Bug 35388 work around (unsafe EVQ writes). */ 4105 + #define MC_CMD_GET_WORKAROUNDS_OUT_BUG35388 0x4 4106 + /* enum: Bug35017 workaround (A64 tables must be identity map) */ 4107 + #define MC_CMD_GET_WORKAROUNDS_OUT_BUG35017 0x8 4089 4108 4090 4109 4091 4110 /***********************************/