bnxt_en: implement firmware live patching

Live patches are activated by using the 'limit no_reset' option when
performing a devlink dev reload fw_activate operation. These packages
must first be installed on the device in the usual way. For example,
via devlink dev flash or ethtool -f.

The devlink device info has also been enhanced to render stored and
running live patch versions.

Signed-off-by: Edwin Peer <edwin.peer@broadcom.com>
Signed-off-by: Michael Chan <michael.chan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Edwin Peer 2021-10-29 03:47:54 -04:00 committed by David S. Miller
parent 21e70778d0
commit 3c4153394e
3 changed files with 174 additions and 2 deletions

View File

@ -7490,6 +7490,8 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp)
bp->fw_cap |= BNXT_FW_CAP_PTP_PPS;
if (BNXT_PF(bp) && (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_HOT_RESET_IF_SUPPORT))
bp->fw_cap |= BNXT_FW_CAP_HOT_RESET_IF;
if (BNXT_PF(bp) && (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_FW_LIVEPATCH_SUPPORTED))
bp->fw_cap |= BNXT_FW_CAP_LIVEPATCH;
bp->tx_push_thresh = 0;
if ((flags & FUNC_QCAPS_RESP_FLAGS_PUSH_MODE_SUPPORTED) &&

View File

@ -1958,6 +1958,7 @@ struct bnxt {
#define BNXT_FW_CAP_VLAN_RX_STRIP 0x01000000
#define BNXT_FW_CAP_VLAN_TX_INSERT 0x02000000
#define BNXT_FW_CAP_EXT_HW_STATS_SUPPORTED 0x04000000
#define BNXT_FW_CAP_LIVEPATCH 0x08000000
#define BNXT_FW_CAP_PTP_PPS 0x10000000
#define BNXT_FW_CAP_HOT_RESET_IF 0x20000000
#define BNXT_FW_CAP_RING_MONITOR 0x40000000

View File

@ -326,6 +326,111 @@ void bnxt_dl_health_fw_recovery_done(struct bnxt *bp)
static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
static void
bnxt_dl_livepatch_report_err(struct bnxt *bp, struct netlink_ext_ack *extack,
struct hwrm_fw_livepatch_output *resp)
{
int err = ((struct hwrm_err_output *)resp)->cmd_err;
switch (err) {
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_OPCODE:
netdev_err(bp->dev, "Illegal live patch opcode");
NL_SET_ERR_MSG_MOD(extack, "Invalid opcode");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_SUPPORTED:
NL_SET_ERR_MSG_MOD(extack, "Live patch operation not supported");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_INSTALLED:
NL_SET_ERR_MSG_MOD(extack, "Live patch not found");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_PATCHED:
NL_SET_ERR_MSG_MOD(extack,
"Live patch deactivation failed. Firmware not patched.");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_AUTH_FAIL:
NL_SET_ERR_MSG_MOD(extack, "Live patch not authenticated");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_HEADER:
NL_SET_ERR_MSG_MOD(extack, "Incompatible live patch");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_SIZE:
NL_SET_ERR_MSG_MOD(extack, "Live patch has invalid size");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_ALREADY_PATCHED:
NL_SET_ERR_MSG_MOD(extack, "Live patch already applied");
break;
default:
netdev_err(bp->dev, "Unexpected live patch error: %hhd\n", err);
NL_SET_ERR_MSG_MOD(extack, "Failed to activate live patch");
break;
}
}
static int
bnxt_dl_livepatch_activate(struct bnxt *bp, struct netlink_ext_ack *extack)
{
struct hwrm_fw_livepatch_query_output *query_resp;
struct hwrm_fw_livepatch_query_input *query_req;
struct hwrm_fw_livepatch_output *patch_resp;
struct hwrm_fw_livepatch_input *patch_req;
u32 installed = 0;
u16 flags;
u8 target;
int rc;
if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support live patch");
return -EOPNOTSUPP;
}
rc = hwrm_req_init(bp, query_req, HWRM_FW_LIVEPATCH_QUERY);
if (rc)
return rc;
query_resp = hwrm_req_hold(bp, query_req);
rc = hwrm_req_init(bp, patch_req, HWRM_FW_LIVEPATCH);
if (rc) {
hwrm_req_drop(bp, query_req);
return rc;
}
patch_req->opcode = FW_LIVEPATCH_REQ_OPCODE_ACTIVATE;
patch_req->loadtype = FW_LIVEPATCH_REQ_LOADTYPE_NVM_INSTALL;
patch_resp = hwrm_req_hold(bp, patch_req);
for (target = 1; target <= FW_LIVEPATCH_REQ_FW_TARGET_LAST; target++) {
query_req->fw_target = target;
rc = hwrm_req_send(bp, query_req);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to query packages");
break;
}
flags = le16_to_cpu(query_resp->status_flags);
if (~flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL)
continue;
if ((flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) &&
!strncmp(query_resp->active_ver, query_resp->install_ver,
sizeof(query_resp->active_ver)))
continue;
patch_req->fw_target = target;
rc = hwrm_req_send(bp, patch_req);
if (rc) {
bnxt_dl_livepatch_report_err(bp, extack, patch_resp);
break;
}
installed++;
}
if (!rc && !installed) {
NL_SET_ERR_MSG_MOD(extack, "No live patches found");
rc = -ENOENT;
}
hwrm_req_drop(bp, query_req);
hwrm_req_drop(bp, patch_req);
return rc;
}
static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
@ -372,6 +477,8 @@ static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change,
break;
}
case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: {
if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET)
return bnxt_dl_livepatch_activate(bp, extack);
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET) {
NL_SET_ERR_MSG_MOD(extack, "Device not capable, requires reboot");
return -EOPNOTSUPP;
@ -432,6 +539,8 @@ static int bnxt_dl_reload_up(struct devlink *dl, enum devlink_reload_action acti
unsigned long start = jiffies;
unsigned long timeout = start + BNXT_DFLT_FW_RST_MAX_DSECS * HZ / 10;
if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET)
break;
if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)
timeout = start + bp->fw_health->normal_func_wait_dsecs * HZ / 10;
if (!netif_running(bp->dev))
@ -485,6 +594,7 @@ static const struct devlink_ops bnxt_dl_ops = {
.flash_update = bnxt_dl_flash_update,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
.reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET),
.reload_down = bnxt_dl_reload_down,
.reload_up = bnxt_dl_reload_up,
};
@ -630,6 +740,57 @@ static int bnxt_dl_info_put(struct bnxt *bp, struct devlink_info_req *req,
return 0;
}
#define BNXT_FW_SRT_PATCH "fw.srt.patch"
#define BNXT_FW_CRT_PATCH "fw.crt.patch"
static int bnxt_dl_livepatch_info_put(struct bnxt *bp,
struct devlink_info_req *req,
const char *key)
{
struct hwrm_fw_livepatch_query_input *query;
struct hwrm_fw_livepatch_query_output *resp;
u16 flags;
int rc;
if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH)
return 0;
rc = hwrm_req_init(bp, query, HWRM_FW_LIVEPATCH_QUERY);
if (rc)
return rc;
if (!strcmp(key, BNXT_FW_SRT_PATCH))
query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_SECURE_FW;
else if (!strcmp(key, BNXT_FW_CRT_PATCH))
query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_COMMON_FW;
else
goto exit;
resp = hwrm_req_hold(bp, query);
rc = hwrm_req_send(bp, query);
if (rc)
goto exit;
flags = le16_to_cpu(resp->status_flags);
if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) {
resp->active_ver[sizeof(resp->active_ver) - 1] = '\0';
rc = devlink_info_version_running_put(req, key, resp->active_ver);
if (rc)
goto exit;
}
if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL) {
resp->install_ver[sizeof(resp->install_ver) - 1] = '\0';
rc = devlink_info_version_stored_put(req, key, resp->install_ver);
if (rc)
goto exit;
}
exit:
hwrm_req_drop(bp, query);
return rc;
}
#define HWRM_FW_VER_STR_LEN 16
static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
@ -783,8 +944,16 @@ static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.roce_fw_major, nvm_dev_info.roce_fw_minor,
nvm_dev_info.roce_fw_build, nvm_dev_info.roce_fw_patch);
return bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, roce_ver);
if (rc)
return rc;
rc = bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_SRT_PATCH);
if (rc)
return rc;
return bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_CRT_PATCH);
}
static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,