mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 05:39:42 -04:00
Merge branch 'eth-fbnic-add-devlink-dev-flash-support'
Lee Trager says: ==================== eth: fbnic: Add devlink dev flash support fbnic supports updating firmware using signed PLDM images. PLDM images are written into the flash. Flashing does not interrupt the operation of the device. V4: https://lore.kernel.org/netdev/20250510002851.3247880-1-lee@trager.us/T/#t V3: https://lore.kernel.org/lkml/20241111043058.1251632-1-lee@trager.us/T/ V2: https://lore.kernel.org/all/20241022013941.3764567-1-lee@trager.us/ ==================== Link: https://patch.msgid.link/20250512190109.2475614-1-lee@trager.us Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -28,6 +28,17 @@ devlink dev info provides version information for all three components. In
|
||||
addition to the version the hg commit hash of the build is included as a
|
||||
separate entry.
|
||||
|
||||
Upgrading Firmware
|
||||
------------------
|
||||
|
||||
fbnic supports updating firmware using signed PLDM images with devlink dev
|
||||
flash. PLDM images are written into the flash. Flashing does not interrupt
|
||||
the operation of the device.
|
||||
|
||||
On host boot the latest UEFI driver is always used, no explicit activation
|
||||
is required. Firmware activation is required to run new control firmware. cmrt
|
||||
firmware can only be activated by power cycling the NIC.
|
||||
|
||||
Statistics
|
||||
----------
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ config FBNIC
|
||||
select NET_DEVLINK
|
||||
select PAGE_POOL
|
||||
select PHYLINK
|
||||
select PLDMFW
|
||||
help
|
||||
This driver supports Meta Platforms Host Network Interface.
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
struct fbnic_napi_vector;
|
||||
|
||||
#define FBNIC_MAX_NAPI_VECTORS 128u
|
||||
#define FBNIC_MBX_CMPL_SLOTS 4
|
||||
|
||||
struct fbnic_dev {
|
||||
struct device *dev;
|
||||
@@ -42,7 +43,7 @@ struct fbnic_dev {
|
||||
|
||||
struct fbnic_fw_mbx mbx[FBNIC_IPC_MBX_INDICES];
|
||||
struct fbnic_fw_cap fw_cap;
|
||||
struct fbnic_fw_completion *cmpl_data;
|
||||
struct fbnic_fw_completion *cmpl_data[FBNIC_MBX_CMPL_SLOTS];
|
||||
/* Lock protecting Tx Mailbox queue to prevent possible races */
|
||||
spinlock_t fw_tx_lock;
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pldmfw.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "fbnic.h"
|
||||
#include "fbnic_tlv.h"
|
||||
|
||||
#define FBNIC_SN_STR_LEN 24
|
||||
|
||||
@@ -109,8 +111,264 @@ static int fbnic_devlink_info_get(struct devlink *devlink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
fbnic_pldm_match_record(struct pldmfw *context, struct pldmfw_record *record)
|
||||
{
|
||||
struct pldmfw_desc_tlv *desc;
|
||||
u32 anti_rollback_ver = 0;
|
||||
struct devlink *devlink;
|
||||
struct fbnic_dev *fbd;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
/* First, use the standard PCI matching function */
|
||||
if (!pldmfw_op_pci_match_record(context, record))
|
||||
return false;
|
||||
|
||||
pdev = to_pci_dev(context->dev);
|
||||
fbd = pci_get_drvdata(pdev);
|
||||
devlink = priv_to_devlink(fbd);
|
||||
|
||||
/* If PCI match is successful, check for vendor-specific descriptors */
|
||||
list_for_each_entry(desc, &record->descs, entry) {
|
||||
if (desc->type != PLDM_DESC_ID_VENDOR_DEFINED)
|
||||
continue;
|
||||
|
||||
if (desc->size < 21 || desc->data[0] != 1 ||
|
||||
desc->data[1] != 15)
|
||||
continue;
|
||||
|
||||
if (memcmp(desc->data + 2, "AntiRollbackVer", 15) != 0)
|
||||
continue;
|
||||
|
||||
anti_rollback_ver = get_unaligned_le32(desc->data + 17);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Compare versions and return error if they do not match */
|
||||
if (anti_rollback_ver < fbd->fw_cap.anti_rollback_version) {
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"New firmware anti-rollback version (0x%x) is older than device version (0x%x)!",
|
||||
anti_rollback_ver, fbd->fw_cap.anti_rollback_version);
|
||||
devlink_flash_update_status_notify(devlink, buf,
|
||||
"Anti-Rollback", 0, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_flash_start(struct fbnic_dev *fbd, struct pldmfw_component *component)
|
||||
{
|
||||
struct fbnic_fw_completion *cmpl;
|
||||
int err;
|
||||
|
||||
cmpl = kzalloc(sizeof(*cmpl), GFP_KERNEL);
|
||||
if (!cmpl)
|
||||
return -ENOMEM;
|
||||
|
||||
fbnic_fw_init_cmpl(cmpl, FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ);
|
||||
err = fbnic_fw_xmit_fw_start_upgrade(fbd, cmpl,
|
||||
component->identifier,
|
||||
component->component_size);
|
||||
if (err)
|
||||
goto cmpl_free;
|
||||
|
||||
/* Wait for firmware to ack firmware upgrade start */
|
||||
if (wait_for_completion_timeout(&cmpl->done, 10 * HZ))
|
||||
err = cmpl->result;
|
||||
else
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
fbnic_fw_clear_cmpl(fbd, cmpl);
|
||||
cmpl_free:
|
||||
fbnic_fw_put_cmpl(cmpl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_flash_component(struct pldmfw *context,
|
||||
struct pldmfw_component *component)
|
||||
{
|
||||
const u8 *data = component->component_data;
|
||||
const u32 size = component->component_size;
|
||||
struct fbnic_fw_completion *cmpl;
|
||||
const char *component_name;
|
||||
struct devlink *devlink;
|
||||
struct fbnic_dev *fbd;
|
||||
struct pci_dev *pdev;
|
||||
u32 offset = 0;
|
||||
u32 length = 0;
|
||||
char buf[32];
|
||||
int err;
|
||||
|
||||
pdev = to_pci_dev(context->dev);
|
||||
fbd = pci_get_drvdata(pdev);
|
||||
devlink = priv_to_devlink(fbd);
|
||||
|
||||
switch (component->identifier) {
|
||||
case QSPI_SECTION_CMRT:
|
||||
component_name = "boot1";
|
||||
break;
|
||||
case QSPI_SECTION_CONTROL_FW:
|
||||
component_name = "boot2";
|
||||
break;
|
||||
case QSPI_SECTION_OPTION_ROM:
|
||||
component_name = "option-rom";
|
||||
break;
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "Unknown component ID %u!",
|
||||
component->identifier);
|
||||
devlink_flash_update_status_notify(devlink, buf, NULL, 0,
|
||||
size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Once firmware receives the request to start upgrading it responds
|
||||
* with two messages:
|
||||
* 1. An ACK that it received the message and possible error code
|
||||
* indicating that an upgrade is not currently possible.
|
||||
* 2. A request for the first chunk of data
|
||||
*
|
||||
* Setup completions for write before issuing the start message so
|
||||
* the driver can catch both messages.
|
||||
*/
|
||||
cmpl = kzalloc(sizeof(*cmpl), GFP_KERNEL);
|
||||
if (!cmpl)
|
||||
return -ENOMEM;
|
||||
|
||||
fbnic_fw_init_cmpl(cmpl, FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ);
|
||||
err = fbnic_mbx_set_cmpl(fbd, cmpl);
|
||||
if (err)
|
||||
goto cmpl_free;
|
||||
|
||||
devlink_flash_update_timeout_notify(devlink, "Initializing",
|
||||
component_name, 15);
|
||||
err = fbnic_flash_start(fbd, component);
|
||||
if (err)
|
||||
goto err_no_msg;
|
||||
|
||||
while (offset < size) {
|
||||
if (!wait_for_completion_timeout(&cmpl->done, 15 * HZ)) {
|
||||
err = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
err = cmpl->result;
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* Verify firmware is requesting the next chunk in the seq. */
|
||||
if (cmpl->u.fw_update.offset != offset + length) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
offset = cmpl->u.fw_update.offset;
|
||||
length = cmpl->u.fw_update.length;
|
||||
|
||||
if (length > TLV_MAX_DATA || offset + length > size) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
devlink_flash_update_status_notify(devlink, "Flashing",
|
||||
component_name,
|
||||
offset, size);
|
||||
|
||||
/* Mailbox will set length to 0 once it receives the finish
|
||||
* message.
|
||||
*/
|
||||
if (!length)
|
||||
continue;
|
||||
|
||||
reinit_completion(&cmpl->done);
|
||||
err = fbnic_fw_xmit_fw_write_chunk(fbd, data, offset, length,
|
||||
0);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
fbnic_fw_xmit_fw_write_chunk(fbd, NULL, 0, 0, err);
|
||||
err_no_msg:
|
||||
snprintf(buf, sizeof(buf), "Mailbox encountered error %d!",
|
||||
err);
|
||||
devlink_flash_update_status_notify(devlink, buf,
|
||||
component_name, 0, 0);
|
||||
}
|
||||
|
||||
fbnic_fw_clear_cmpl(fbd, cmpl);
|
||||
cmpl_free:
|
||||
fbnic_fw_put_cmpl(cmpl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pldmfw_ops fbnic_pldmfw_ops = {
|
||||
.match_record = fbnic_pldm_match_record,
|
||||
.flash_component = fbnic_flash_component,
|
||||
};
|
||||
|
||||
static int
|
||||
fbnic_devlink_flash_update(struct devlink *devlink,
|
||||
struct devlink_flash_update_params *params,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct fbnic_dev *fbd = devlink_priv(devlink);
|
||||
const struct firmware *fw = params->fw;
|
||||
struct device *dev = fbd->dev;
|
||||
struct pldmfw context;
|
||||
char *err_msg;
|
||||
int err;
|
||||
|
||||
context.ops = &fbnic_pldmfw_ops;
|
||||
context.dev = dev;
|
||||
|
||||
err = pldmfw_flash_image(&context, fw);
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case -EINVAL:
|
||||
err_msg = "Invalid image";
|
||||
break;
|
||||
case -EOPNOTSUPP:
|
||||
err_msg = "Unsupported image";
|
||||
break;
|
||||
case -ENOMEM:
|
||||
err_msg = "Out of memory";
|
||||
break;
|
||||
case -EFAULT:
|
||||
err_msg = "Invalid header";
|
||||
break;
|
||||
case -ENOENT:
|
||||
err_msg = "No matching record";
|
||||
break;
|
||||
case -ENODEV:
|
||||
err_msg = "No matching device";
|
||||
break;
|
||||
case -ETIMEDOUT:
|
||||
err_msg = "Timed out waiting for reply";
|
||||
break;
|
||||
default:
|
||||
err_msg = "Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||
"Failed to flash PLDM Image: %s (error: %d)",
|
||||
err_msg, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct devlink_ops fbnic_devlink_ops = {
|
||||
.info_get = fbnic_devlink_info_get,
|
||||
.info_get = fbnic_devlink_info_get,
|
||||
.flash_update = fbnic_devlink_flash_update,
|
||||
};
|
||||
|
||||
void fbnic_devlink_free(struct fbnic_dev *fbd)
|
||||
|
||||
@@ -237,6 +237,44 @@ static int fbnic_mbx_map_tlv_msg(struct fbnic_dev *fbd,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fbnic_mbx_set_cmpl_slot(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data)
|
||||
{
|
||||
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
|
||||
int free = -EXFULL;
|
||||
int i;
|
||||
|
||||
if (!tx_mbx->ready)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < FBNIC_MBX_CMPL_SLOTS; i++) {
|
||||
if (!fbd->cmpl_data[i])
|
||||
free = i;
|
||||
else if (fbd->cmpl_data[i]->msg_type == cmpl_data->msg_type)
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (free == -EXFULL)
|
||||
return -EXFULL;
|
||||
|
||||
fbd->cmpl_data[free] = cmpl_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbnic_mbx_clear_cmpl_slot(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FBNIC_MBX_CMPL_SLOTS; i++) {
|
||||
if (fbd->cmpl_data[i] == cmpl_data) {
|
||||
fbd->cmpl_data[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fbnic_mbx_process_tx_msgs(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
|
||||
@@ -258,6 +296,19 @@ static void fbnic_mbx_process_tx_msgs(struct fbnic_dev *fbd)
|
||||
tx_mbx->head = head;
|
||||
}
|
||||
|
||||
int fbnic_mbx_set_cmpl(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&fbd->fw_tx_lock, flags);
|
||||
err = fbnic_mbx_set_cmpl_slot(fbd, cmpl_data);
|
||||
spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fbnic_mbx_map_req_w_cmpl(struct fbnic_dev *fbd,
|
||||
struct fbnic_tlv_msg *msg,
|
||||
struct fbnic_fw_completion *cmpl_data)
|
||||
@@ -266,23 +317,20 @@ static int fbnic_mbx_map_req_w_cmpl(struct fbnic_dev *fbd,
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&fbd->fw_tx_lock, flags);
|
||||
|
||||
/* If we are already waiting on a completion then abort */
|
||||
if (cmpl_data && fbd->cmpl_data) {
|
||||
err = -EBUSY;
|
||||
goto unlock_mbx;
|
||||
if (cmpl_data) {
|
||||
err = fbnic_mbx_set_cmpl_slot(fbd, cmpl_data);
|
||||
if (err)
|
||||
goto unlock_mbx;
|
||||
}
|
||||
|
||||
/* Record completion location and submit request */
|
||||
if (cmpl_data)
|
||||
fbd->cmpl_data = cmpl_data;
|
||||
|
||||
err = fbnic_mbx_map_msg(fbd, FBNIC_IPC_MBX_TX_IDX, msg,
|
||||
le16_to_cpu(msg->hdr.len) * sizeof(u32), 1);
|
||||
|
||||
/* If msg failed then clear completion data for next caller */
|
||||
/* If we successfully reserved a completion and msg failed
|
||||
* then clear completion data for next caller
|
||||
*/
|
||||
if (err && cmpl_data)
|
||||
fbd->cmpl_data = NULL;
|
||||
fbnic_mbx_clear_cmpl_slot(fbd, cmpl_data);
|
||||
|
||||
unlock_mbx:
|
||||
spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
|
||||
@@ -304,12 +352,18 @@ fbnic_fw_get_cmpl_by_type(struct fbnic_dev *fbd, u32 msg_type)
|
||||
{
|
||||
struct fbnic_fw_completion *cmpl_data = NULL;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&fbd->fw_tx_lock, flags);
|
||||
if (fbd->cmpl_data && fbd->cmpl_data->msg_type == msg_type) {
|
||||
cmpl_data = fbd->cmpl_data;
|
||||
kref_get(&fbd->cmpl_data->ref_count);
|
||||
for (i = 0; i < FBNIC_MBX_CMPL_SLOTS; i++) {
|
||||
if (fbd->cmpl_data[i] &&
|
||||
fbd->cmpl_data[i]->msg_type == msg_type) {
|
||||
cmpl_data = fbd->cmpl_data[i];
|
||||
kref_get(&cmpl_data->ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
|
||||
|
||||
return cmpl_data;
|
||||
@@ -464,6 +518,7 @@ static const struct fbnic_tlv_index fbnic_fw_cap_resp_index[] = {
|
||||
FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_UEFI_VERSION),
|
||||
FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR,
|
||||
FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
|
||||
FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION),
|
||||
FBNIC_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
@@ -586,6 +641,9 @@ static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results)
|
||||
if (results[FBNIC_FW_CAP_RESP_BMC_ALL_MULTI] || !bmc_present)
|
||||
fbd->fw_cap.all_multi = all_multi;
|
||||
|
||||
fbd->fw_cap.anti_rollback_version =
|
||||
fta_get_uint(results, FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -708,6 +766,188 @@ void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd)
|
||||
dev_warn(fbd->dev, "Failed to send heartbeat message\n");
|
||||
}
|
||||
|
||||
int fbnic_fw_xmit_fw_start_upgrade(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data,
|
||||
unsigned int id, unsigned int len)
|
||||
{
|
||||
struct fbnic_tlv_msg *msg;
|
||||
int err;
|
||||
|
||||
if (!fbnic_fw_present(fbd))
|
||||
return -ENODEV;
|
||||
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_START_UPGRADE_SECTION, id);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_START_UPGRADE_IMAGE_LENGTH,
|
||||
len);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
err = fbnic_mbx_map_req_w_cmpl(fbd, msg, cmpl_data);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
return 0;
|
||||
|
||||
free_message:
|
||||
free_page((unsigned long)msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct fbnic_tlv_index fbnic_fw_start_upgrade_resp_index[] = {
|
||||
FBNIC_TLV_ATTR_S32(FBNIC_FW_START_UPGRADE_ERROR),
|
||||
FBNIC_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
static int fbnic_fw_parse_fw_start_upgrade_resp(void *opaque,
|
||||
struct fbnic_tlv_msg **results)
|
||||
{
|
||||
struct fbnic_fw_completion *cmpl_data;
|
||||
struct fbnic_dev *fbd = opaque;
|
||||
u32 msg_type;
|
||||
s32 err;
|
||||
|
||||
/* Verify we have a completion pointer */
|
||||
msg_type = FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ;
|
||||
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type);
|
||||
if (!cmpl_data)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Check for errors */
|
||||
err = fta_get_sint(results, FBNIC_FW_START_UPGRADE_ERROR);
|
||||
|
||||
cmpl_data->result = err;
|
||||
complete(&cmpl_data->done);
|
||||
fbnic_fw_put_cmpl(cmpl_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fbnic_fw_xmit_fw_write_chunk(struct fbnic_dev *fbd,
|
||||
const u8 *data, u32 offset, u16 length,
|
||||
int cancel_error)
|
||||
{
|
||||
struct fbnic_tlv_msg *msg;
|
||||
int err;
|
||||
|
||||
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_RESP);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Report error to FW to cancel upgrade */
|
||||
if (cancel_error) {
|
||||
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_WRITE_CHUNK_ERROR,
|
||||
cancel_error);
|
||||
if (err)
|
||||
goto free_message;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_WRITE_CHUNK_OFFSET,
|
||||
offset);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_WRITE_CHUNK_LENGTH,
|
||||
length);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
err = fbnic_tlv_attr_put_value(msg, FBNIC_FW_WRITE_CHUNK_DATA,
|
||||
data + offset, length);
|
||||
if (err)
|
||||
goto free_message;
|
||||
}
|
||||
|
||||
err = fbnic_mbx_map_tlv_msg(fbd, msg);
|
||||
if (err)
|
||||
goto free_message;
|
||||
|
||||
return 0;
|
||||
|
||||
free_message:
|
||||
free_page((unsigned long)msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct fbnic_tlv_index fbnic_fw_write_chunk_req_index[] = {
|
||||
FBNIC_TLV_ATTR_U32(FBNIC_FW_WRITE_CHUNK_OFFSET),
|
||||
FBNIC_TLV_ATTR_U32(FBNIC_FW_WRITE_CHUNK_LENGTH),
|
||||
FBNIC_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
static int fbnic_fw_parse_fw_write_chunk_req(void *opaque,
|
||||
struct fbnic_tlv_msg **results)
|
||||
{
|
||||
struct fbnic_fw_completion *cmpl_data;
|
||||
struct fbnic_dev *fbd = opaque;
|
||||
u32 msg_type;
|
||||
u32 offset;
|
||||
u32 length;
|
||||
|
||||
/* Verify we have a completion pointer */
|
||||
msg_type = FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ;
|
||||
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type);
|
||||
if (!cmpl_data)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Pull length/offset pair and mark it as complete */
|
||||
offset = fta_get_uint(results, FBNIC_FW_WRITE_CHUNK_OFFSET);
|
||||
length = fta_get_uint(results, FBNIC_FW_WRITE_CHUNK_LENGTH);
|
||||
cmpl_data->u.fw_update.offset = offset;
|
||||
cmpl_data->u.fw_update.length = length;
|
||||
|
||||
complete(&cmpl_data->done);
|
||||
fbnic_fw_put_cmpl(cmpl_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fbnic_tlv_index fbnic_fw_finish_upgrade_req_index[] = {
|
||||
FBNIC_TLV_ATTR_S32(FBNIC_FW_FINISH_UPGRADE_ERROR),
|
||||
FBNIC_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
static int fbnic_fw_parse_fw_finish_upgrade_req(void *opaque,
|
||||
struct fbnic_tlv_msg **results)
|
||||
{
|
||||
struct fbnic_fw_completion *cmpl_data;
|
||||
struct fbnic_dev *fbd = opaque;
|
||||
u32 msg_type;
|
||||
s32 err;
|
||||
|
||||
/* Verify we have a completion pointer */
|
||||
msg_type = FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ;
|
||||
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type);
|
||||
if (!cmpl_data)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Check for errors */
|
||||
err = fta_get_sint(results, FBNIC_FW_FINISH_UPGRADE_ERROR);
|
||||
|
||||
/* Close out update by incrementing offset by length which should
|
||||
* match the total size of the component. Set length to 0 since no
|
||||
* new chunks will be requested.
|
||||
*/
|
||||
cmpl_data->u.fw_update.offset += cmpl_data->u.fw_update.length;
|
||||
cmpl_data->u.fw_update.length = 0;
|
||||
|
||||
cmpl_data->result = err;
|
||||
complete(&cmpl_data->done);
|
||||
fbnic_fw_put_cmpl(cmpl_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fbnic_fw_xmit_tsene_read_msg - Create and transmit a sensor read request
|
||||
* @fbd: FBNIC device structure
|
||||
@@ -792,6 +1032,15 @@ static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
|
||||
fbnic_fw_parse_ownership_resp),
|
||||
FBNIC_TLV_PARSER(HEARTBEAT_RESP, fbnic_heartbeat_resp_index,
|
||||
fbnic_fw_parse_heartbeat_resp),
|
||||
FBNIC_TLV_PARSER(FW_START_UPGRADE_RESP,
|
||||
fbnic_fw_start_upgrade_resp_index,
|
||||
fbnic_fw_parse_fw_start_upgrade_resp),
|
||||
FBNIC_TLV_PARSER(FW_WRITE_CHUNK_REQ,
|
||||
fbnic_fw_write_chunk_req_index,
|
||||
fbnic_fw_parse_fw_write_chunk_req),
|
||||
FBNIC_TLV_PARSER(FW_FINISH_UPGRADE_REQ,
|
||||
fbnic_fw_finish_upgrade_req_index,
|
||||
fbnic_fw_parse_fw_finish_upgrade_req),
|
||||
FBNIC_TLV_PARSER(TSENE_READ_RESP,
|
||||
fbnic_tsene_read_resp_index,
|
||||
fbnic_fw_parse_tsene_read_resp),
|
||||
@@ -921,10 +1170,16 @@ static void __fbnic_fw_evict_cmpl(struct fbnic_fw_completion *cmpl_data)
|
||||
|
||||
static void fbnic_mbx_evict_all_cmpl(struct fbnic_dev *fbd)
|
||||
{
|
||||
if (fbd->cmpl_data) {
|
||||
__fbnic_fw_evict_cmpl(fbd->cmpl_data);
|
||||
fbd->cmpl_data = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FBNIC_MBX_CMPL_SLOTS; i++) {
|
||||
struct fbnic_fw_completion *cmpl_data = fbd->cmpl_data[i];
|
||||
|
||||
if (cmpl_data)
|
||||
__fbnic_fw_evict_cmpl(cmpl_data);
|
||||
}
|
||||
|
||||
memset(fbd->cmpl_data, 0, sizeof(fbd->cmpl_data));
|
||||
}
|
||||
|
||||
void fbnic_mbx_flush_tx(struct fbnic_dev *fbd)
|
||||
@@ -985,12 +1240,13 @@ void fbnic_fw_init_cmpl(struct fbnic_fw_completion *fw_cmpl,
|
||||
kref_init(&fw_cmpl->ref_count);
|
||||
}
|
||||
|
||||
void fbnic_fw_clear_compl(struct fbnic_dev *fbd)
|
||||
void fbnic_fw_clear_cmpl(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *fw_cmpl)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fbd->fw_tx_lock, flags);
|
||||
fbd->cmpl_data = NULL;
|
||||
fbnic_mbx_clear_cmpl_slot(fbd, fw_cmpl);
|
||||
spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ struct fbnic_fw_cap {
|
||||
u8 all_multi : 1;
|
||||
u8 link_speed;
|
||||
u8 link_fec;
|
||||
u32 anti_rollback_version;
|
||||
};
|
||||
|
||||
struct fbnic_fw_completion {
|
||||
@@ -50,6 +51,10 @@ struct fbnic_fw_completion {
|
||||
struct kref ref_count;
|
||||
int result;
|
||||
union {
|
||||
struct {
|
||||
u32 offset;
|
||||
u32 length;
|
||||
} fw_update;
|
||||
struct {
|
||||
s32 millivolts;
|
||||
s32 millidegrees;
|
||||
@@ -59,17 +64,26 @@ struct fbnic_fw_completion {
|
||||
|
||||
void fbnic_mbx_init(struct fbnic_dev *fbd);
|
||||
void fbnic_mbx_clean(struct fbnic_dev *fbd);
|
||||
int fbnic_mbx_set_cmpl(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data);
|
||||
void fbnic_mbx_poll(struct fbnic_dev *fbd);
|
||||
int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd);
|
||||
void fbnic_mbx_flush_tx(struct fbnic_dev *fbd);
|
||||
int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership);
|
||||
int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll);
|
||||
void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd);
|
||||
int fbnic_fw_xmit_fw_start_upgrade(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data,
|
||||
unsigned int id, unsigned int len);
|
||||
int fbnic_fw_xmit_fw_write_chunk(struct fbnic_dev *fbd,
|
||||
const u8 *data, u32 offset, u16 length,
|
||||
int cancel_error);
|
||||
int fbnic_fw_xmit_tsene_read_msg(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data);
|
||||
void fbnic_fw_init_cmpl(struct fbnic_fw_completion *cmpl_data,
|
||||
u32 msg_type);
|
||||
void fbnic_fw_clear_compl(struct fbnic_dev *fbd);
|
||||
void fbnic_fw_clear_cmpl(struct fbnic_dev *fbd,
|
||||
struct fbnic_fw_completion *cmpl_data);
|
||||
void fbnic_fw_put_cmpl(struct fbnic_fw_completion *cmpl_data);
|
||||
|
||||
#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str, _str_sz) \
|
||||
@@ -86,6 +100,15 @@ do { \
|
||||
#define fbnic_mk_fw_ver_str(_rev_id, _str) \
|
||||
fbnic_mk_full_fw_ver_str(_rev_id, "", "", _str, sizeof(_str))
|
||||
|
||||
enum {
|
||||
QSPI_SECTION_CMRT = 0,
|
||||
QSPI_SECTION_CONTROL_FW = 1,
|
||||
QSPI_SECTION_UCODE = 2,
|
||||
QSPI_SECTION_OPTION_ROM = 3,
|
||||
QSPI_SECTION_USER = 4,
|
||||
QSPI_SECTION_INVALID,
|
||||
};
|
||||
|
||||
#define FW_HEARTBEAT_PERIOD (10 * HZ)
|
||||
|
||||
enum {
|
||||
@@ -95,6 +118,12 @@ enum {
|
||||
FBNIC_TLV_MSG_ID_OWNERSHIP_RESP = 0x13,
|
||||
FBNIC_TLV_MSG_ID_HEARTBEAT_REQ = 0x14,
|
||||
FBNIC_TLV_MSG_ID_HEARTBEAT_RESP = 0x15,
|
||||
FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ = 0x22,
|
||||
FBNIC_TLV_MSG_ID_FW_START_UPGRADE_RESP = 0x23,
|
||||
FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ = 0x24,
|
||||
FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_RESP = 0x25,
|
||||
FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_REQ = 0x28,
|
||||
FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_RESP = 0x29,
|
||||
FBNIC_TLV_MSG_ID_TSENE_READ_REQ = 0x3C,
|
||||
FBNIC_TLV_MSG_ID_TSENE_READ_RESP = 0x3D,
|
||||
};
|
||||
@@ -122,6 +151,7 @@ enum {
|
||||
FBNIC_FW_CAP_RESP_STORED_CMRT_COMMIT_STR = 0x10,
|
||||
FBNIC_FW_CAP_RESP_UEFI_VERSION = 0x11,
|
||||
FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR = 0x12,
|
||||
FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION = 0x15,
|
||||
FBNIC_FW_CAP_RESP_MSG_MAX
|
||||
};
|
||||
|
||||
@@ -149,4 +179,25 @@ enum {
|
||||
FBNIC_FW_OWNERSHIP_FLAG = 0x0,
|
||||
FBNIC_FW_OWNERSHIP_MSG_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
FBNIC_FW_START_UPGRADE_ERROR = 0x0,
|
||||
FBNIC_FW_START_UPGRADE_SECTION = 0x1,
|
||||
FBNIC_FW_START_UPGRADE_IMAGE_LENGTH = 0x2,
|
||||
FBNIC_FW_START_UPGRADE_MSG_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
FBNIC_FW_WRITE_CHUNK_OFFSET = 0x0,
|
||||
FBNIC_FW_WRITE_CHUNK_LENGTH = 0x1,
|
||||
FBNIC_FW_WRITE_CHUNK_DATA = 0x2,
|
||||
FBNIC_FW_WRITE_CHUNK_ERROR = 0x3,
|
||||
FBNIC_FW_WRITE_CHUNK_MSG_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
FBNIC_FW_FINISH_UPGRADE_ERROR = 0x0,
|
||||
FBNIC_FW_FINISH_UPGRADE_MSG_MAX
|
||||
};
|
||||
|
||||
#endif /* _FBNIC_FW_H_ */
|
||||
|
||||
@@ -744,7 +744,7 @@ static int fbnic_mac_get_sensor_asic(struct fbnic_dev *fbd, int id,
|
||||
|
||||
*val = *sensor;
|
||||
exit_cleanup:
|
||||
fbnic_fw_clear_compl(fbd);
|
||||
fbnic_fw_clear_cmpl(fbd, fw_cmpl);
|
||||
exit_free:
|
||||
fbnic_fw_put_cmpl(fw_cmpl);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "fbnic.h"
|
||||
#include "fbnic_drvinfo.h"
|
||||
@@ -388,8 +389,12 @@ static int fbnic_pm_suspend(struct device *dev)
|
||||
rtnl_unlock();
|
||||
|
||||
null_uc_addr:
|
||||
devl_lock(priv_to_devlink(fbd));
|
||||
|
||||
fbnic_fw_free_mbx(fbd);
|
||||
|
||||
devl_unlock(priv_to_devlink(fbd));
|
||||
|
||||
/* Free the IRQs so they aren't trying to occupy sleeping CPUs */
|
||||
fbnic_free_irqs(fbd);
|
||||
|
||||
@@ -420,11 +425,15 @@ static int __fbnic_pm_resume(struct device *dev)
|
||||
|
||||
fbd->mac->init_regs(fbd);
|
||||
|
||||
devl_lock(priv_to_devlink(fbd));
|
||||
|
||||
/* Re-enable mailbox */
|
||||
err = fbnic_fw_request_mbx(fbd);
|
||||
if (err)
|
||||
goto err_free_irqs;
|
||||
|
||||
devl_unlock(priv_to_devlink(fbd));
|
||||
|
||||
/* No netdev means there isn't a network interface to bring up */
|
||||
if (fbnic_init_failure(fbd))
|
||||
return 0;
|
||||
|
||||
@@ -728,6 +728,9 @@ pldm_send_package_data(struct pldmfw_priv *data)
|
||||
struct pldmfw_record *record = data->matching_record;
|
||||
const struct pldmfw_ops *ops = data->context->ops;
|
||||
|
||||
if (!ops->send_package_data)
|
||||
return 0;
|
||||
|
||||
return ops->send_package_data(data->context, record->package_data,
|
||||
record->package_data_len);
|
||||
}
|
||||
@@ -755,6 +758,9 @@ pldm_send_component_tables(struct pldmfw_priv *data)
|
||||
if (!test_bit(index, bitmap))
|
||||
continue;
|
||||
|
||||
if (!data->context->ops->send_component_table)
|
||||
continue;
|
||||
|
||||
/* determine whether this is the start, middle, end, or both
|
||||
* the start and end of the component tables
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user