Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Mellanox firmware flash library configuration
#
config MLXFW
tristate "Mellanox Technologies firmware flash module"
help
This driver supports Mellanox Technologies Firmware
flashing common logic.
To compile this driver as a module, choose M here: the
module will be called mlxfw.
select XZ_DEC
select NET_DEVLINK
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MLXFW) += mlxfw.o
mlxfw-objs := mlxfw_fsm.o mlxfw_mfa2_tlv_multi.o mlxfw_mfa2.o
@@ -0,0 +1,115 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_H
#define _MLXFW_H
#include <linux/firmware.h>
#include <linux/netlink.h>
#include <linux/device.h>
#include <net/devlink.h>
struct mlxfw_dev {
const struct mlxfw_dev_ops *ops;
const char *psid;
u16 psid_size;
struct devlink *devlink;
};
static inline
struct device *mlxfw_dev_dev(struct mlxfw_dev *mlxfw_dev)
{
return devlink_to_dev(mlxfw_dev->devlink);
}
#define MLXFW_PRFX "mlxfw: "
#define mlxfw_info(mlxfw_dev, fmt, ...) \
dev_info(mlxfw_dev_dev(mlxfw_dev), MLXFW_PRFX fmt, ## __VA_ARGS__)
#define mlxfw_err(mlxfw_dev, fmt, ...) \
dev_err(mlxfw_dev_dev(mlxfw_dev), MLXFW_PRFX fmt, ## __VA_ARGS__)
#define mlxfw_dbg(mlxfw_dev, fmt, ...) \
dev_dbg(mlxfw_dev_dev(mlxfw_dev), MLXFW_PRFX fmt, ## __VA_ARGS__)
enum mlxfw_fsm_state {
MLXFW_FSM_STATE_IDLE,
MLXFW_FSM_STATE_LOCKED,
MLXFW_FSM_STATE_INITIALIZE,
MLXFW_FSM_STATE_DOWNLOAD,
MLXFW_FSM_STATE_VERIFY,
MLXFW_FSM_STATE_APPLY,
MLXFW_FSM_STATE_ACTIVATE,
};
enum mlxfw_fsm_state_err {
MLXFW_FSM_STATE_ERR_OK,
MLXFW_FSM_STATE_ERR_ERROR,
MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR,
MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE,
MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY,
MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED,
MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED,
MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE,
MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT,
MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET,
MLXFW_FSM_STATE_ERR_MAX,
};
enum mlxfw_fsm_reactivate_status {
MLXFW_FSM_REACTIVATE_STATUS_OK,
MLXFW_FSM_REACTIVATE_STATUS_BUSY,
MLXFW_FSM_REACTIVATE_STATUS_PROHIBITED_FW_VER_ERR,
MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_COPY_FAILED,
MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_ERASE_FAILED,
MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_RESTORE_FAILED,
MLXFW_FSM_REACTIVATE_STATUS_CANDIDATE_FW_DEACTIVATION_FAILED,
MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED,
MLXFW_FSM_REACTIVATE_STATUS_ERR_DEVICE_RESET_REQUIRED,
MLXFW_FSM_REACTIVATE_STATUS_ERR_FW_PROGRAMMING_NEEDED,
MLXFW_FSM_REACTIVATE_STATUS_MAX,
};
struct mlxfw_dev_ops {
int (*component_query)(struct mlxfw_dev *mlxfw_dev, u16 component_index,
u32 *p_max_size, u8 *p_align_bits,
u16 *p_max_write_size);
int (*fsm_lock)(struct mlxfw_dev *mlxfw_dev, u32 *fwhandle);
int (*fsm_component_update)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u16 component_index, u32 component_size);
int (*fsm_block_download)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u8 *data, u16 size, u32 offset);
int (*fsm_component_verify)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u16 component_index);
int (*fsm_activate)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle);
int (*fsm_reactivate)(struct mlxfw_dev *mlxfw_dev, u8 *status);
int (*fsm_query_state)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
enum mlxfw_fsm_state *fsm_state,
enum mlxfw_fsm_state_err *fsm_state_err);
void (*fsm_cancel)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle);
void (*fsm_release)(struct mlxfw_dev *mlxfw_dev, u32 fwhandle);
};
#if IS_REACHABLE(CONFIG_MLXFW)
int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
const struct firmware *firmware,
struct netlink_ext_ack *extack);
#else
static inline
int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
const struct firmware *firmware,
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
#endif
#endif
@@ -0,0 +1,434 @@
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#define pr_fmt(fmt) "mlxfw: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include "mlxfw.h"
#include "mlxfw_mfa2.h"
#define MLXFW_FSM_STATE_WAIT_CYCLE_MS 200
#define MLXFW_FSM_STATE_WAIT_TIMEOUT_MS 30000
#define MLXFW_FSM_STATE_WAIT_ROUNDS \
(MLXFW_FSM_STATE_WAIT_TIMEOUT_MS / MLXFW_FSM_STATE_WAIT_CYCLE_MS)
static const int mlxfw_fsm_state_errno[] = {
[MLXFW_FSM_STATE_ERR_ERROR] = -EIO,
[MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] = -EBADMSG,
[MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] = -ENOENT,
[MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] = -ENOKEY,
[MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] = -EACCES,
[MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] = -EKEYREVOKED,
[MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] = -EKEYREJECTED,
[MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] = -ENOEXEC,
[MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] = -EBUSY,
[MLXFW_FSM_STATE_ERR_MAX] = -EINVAL
};
#define MLXFW_ERR_PRFX "Firmware flash failed: "
#define MLXFW_ERR_MSG(fwdev, extack, msg, err) do { \
mlxfw_err(fwdev, "%s, err (%d)\n", MLXFW_ERR_PRFX msg, err); \
NL_SET_ERR_MSG_MOD(extack, MLXFW_ERR_PRFX msg); \
} while (0)
static int mlxfw_fsm_state_err(struct mlxfw_dev *mlxfw_dev,
struct netlink_ext_ack *extack,
enum mlxfw_fsm_state_err err)
{
enum mlxfw_fsm_state_err fsm_state_err;
fsm_state_err = min_t(enum mlxfw_fsm_state_err, err,
MLXFW_FSM_STATE_ERR_MAX);
switch (fsm_state_err) {
case MLXFW_FSM_STATE_ERR_ERROR:
MLXFW_ERR_MSG(mlxfw_dev, extack, "general error", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR:
MLXFW_ERR_MSG(mlxfw_dev, extack, "component hash mismatch", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE:
MLXFW_ERR_MSG(mlxfw_dev, extack, "component not applicable", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY:
MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown key", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED:
MLXFW_ERR_MSG(mlxfw_dev, extack, "authentication failed", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED:
MLXFW_ERR_MSG(mlxfw_dev, extack, "component was not signed", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE:
MLXFW_ERR_MSG(mlxfw_dev, extack, "key not applicable", err);
break;
case MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT:
MLXFW_ERR_MSG(mlxfw_dev, extack, "bad format", err);
break;
case MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET:
MLXFW_ERR_MSG(mlxfw_dev, extack, "pending reset", err);
break;
case MLXFW_FSM_STATE_ERR_OK:
case MLXFW_FSM_STATE_ERR_MAX:
MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown error", err);
break;
}
return mlxfw_fsm_state_errno[fsm_state_err];
};
static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
enum mlxfw_fsm_state fsm_state,
struct netlink_ext_ack *extack)
{
enum mlxfw_fsm_state_err fsm_state_err;
enum mlxfw_fsm_state curr_fsm_state;
int times;
int err;
times = MLXFW_FSM_STATE_WAIT_ROUNDS;
retry:
err = mlxfw_dev->ops->fsm_query_state(mlxfw_dev, fwhandle,
&curr_fsm_state, &fsm_state_err);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM state query failed", err);
return err;
}
if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK)
return mlxfw_fsm_state_err(mlxfw_dev, extack, fsm_state_err);
if (curr_fsm_state != fsm_state) {
if (--times == 0) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Timeout reached on FSM state change", -ETIMEDOUT);
return -ETIMEDOUT;
}
msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS);
goto retry;
}
return 0;
}
static int
mlxfw_fsm_reactivate_err(struct mlxfw_dev *mlxfw_dev,
struct netlink_ext_ack *extack, u8 err)
{
enum mlxfw_fsm_reactivate_status status;
#define MXFW_REACT_PRFX "Reactivate FSM: "
#define MLXFW_REACT_ERR(msg, err) \
MLXFW_ERR_MSG(mlxfw_dev, extack, MXFW_REACT_PRFX msg, err)
status = min_t(enum mlxfw_fsm_reactivate_status, err,
MLXFW_FSM_REACTIVATE_STATUS_MAX);
switch (status) {
case MLXFW_FSM_REACTIVATE_STATUS_BUSY:
MLXFW_REACT_ERR("busy", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_PROHIBITED_FW_VER_ERR:
MLXFW_REACT_ERR("prohibited fw ver", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_COPY_FAILED:
MLXFW_REACT_ERR("first page copy failed", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_ERASE_FAILED:
MLXFW_REACT_ERR("first page erase failed", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_RESTORE_FAILED:
MLXFW_REACT_ERR("first page restore failed", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_CANDIDATE_FW_DEACTIVATION_FAILED:
MLXFW_REACT_ERR("candidate fw deactivation failed", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_ERR_DEVICE_RESET_REQUIRED:
MLXFW_REACT_ERR("device reset required", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_ERR_FW_PROGRAMMING_NEEDED:
MLXFW_REACT_ERR("fw programming needed", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED:
MLXFW_REACT_ERR("fw already activated", err);
break;
case MLXFW_FSM_REACTIVATE_STATUS_OK:
case MLXFW_FSM_REACTIVATE_STATUS_MAX:
MLXFW_REACT_ERR("unexpected error", err);
break;
}
return -EREMOTEIO;
};
static int mlxfw_fsm_reactivate(struct mlxfw_dev *mlxfw_dev,
struct netlink_ext_ack *extack,
bool *supported)
{
u8 status;
int err;
if (!mlxfw_dev->ops->fsm_reactivate)
return 0;
err = mlxfw_dev->ops->fsm_reactivate(mlxfw_dev, &status);
if (err == -EOPNOTSUPP) {
*supported = false;
return 0;
}
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Could not reactivate firmware flash", err);
return err;
}
if (status == MLXFW_FSM_REACTIVATE_STATUS_OK ||
status == MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED)
return 0;
return mlxfw_fsm_reactivate_err(mlxfw_dev, extack, status);
}
static void mlxfw_status_notify(struct mlxfw_dev *mlxfw_dev,
const char *msg, const char *comp_name,
u32 done_bytes, u32 total_bytes)
{
devlink_flash_update_status_notify(mlxfw_dev->devlink, msg, comp_name,
done_bytes, total_bytes);
}
#define MLXFW_ALIGN_DOWN(x, align_bits) ((x) & ~((1 << (align_bits)) - 1))
#define MLXFW_ALIGN_UP(x, align_bits) \
MLXFW_ALIGN_DOWN((x) + ((1 << (align_bits)) - 1), (align_bits))
static int mlxfw_flash_component(struct mlxfw_dev *mlxfw_dev,
u32 fwhandle,
struct mlxfw_mfa2_component *comp,
bool reactivate_supp,
struct netlink_ext_ack *extack)
{
u16 comp_max_write_size;
u8 comp_align_bits;
u32 comp_max_size;
char comp_name[8];
u16 block_size;
u8 *block_ptr;
u32 offset;
int err;
sprintf(comp_name, "%u", comp->index);
err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index,
&comp_max_size, &comp_align_bits,
&comp_max_write_size);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM component query failed", err);
return err;
}
if (comp->data_size > comp_max_size) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Component size is bigger than limit", -EINVAL);
return -EINVAL;
}
comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size,
comp_align_bits);
mlxfw_dbg(mlxfw_dev, "Component update\n");
mlxfw_status_notify(mlxfw_dev, "Updating component", comp_name, 0, 0);
err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle,
comp->index,
comp->data_size);
if (err) {
if (!reactivate_supp)
MLXFW_ERR_MSG(mlxfw_dev, extack,
"FSM component update failed, FW reactivate is not supported",
err);
else
MLXFW_ERR_MSG(mlxfw_dev, extack,
"FSM component update failed", err);
return err;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_DOWNLOAD, extack);
if (err)
goto err_out;
mlxfw_dbg(mlxfw_dev, "Component download\n");
mlxfw_status_notify(mlxfw_dev, "Downloading component",
comp_name, 0, comp->data_size);
for (offset = 0;
offset < MLXFW_ALIGN_UP(comp->data_size, comp_align_bits);
offset += comp_max_write_size) {
block_ptr = comp->data + offset;
block_size = (u16) min_t(u32, comp->data_size - offset,
comp_max_write_size);
err = mlxfw_dev->ops->fsm_block_download(mlxfw_dev, fwhandle,
block_ptr, block_size,
offset);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Component download failed", err);
goto err_out;
}
mlxfw_status_notify(mlxfw_dev, "Downloading component",
comp_name, offset + block_size,
comp->data_size);
}
mlxfw_dbg(mlxfw_dev, "Component verify\n");
mlxfw_status_notify(mlxfw_dev, "Verifying component", comp_name, 0, 0);
err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle,
comp->index);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"FSM component verify failed", err);
goto err_out;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_LOCKED, extack);
if (err)
goto err_out;
return 0;
err_out:
mlxfw_dev->ops->fsm_cancel(mlxfw_dev, fwhandle);
return err;
}
static int mlxfw_flash_components(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
struct mlxfw_mfa2_file *mfa2_file,
bool reactivate_supp,
struct netlink_ext_ack *extack)
{
u32 component_count;
int err;
int i;
err = mlxfw_mfa2_file_component_count(mfa2_file, mlxfw_dev->psid,
mlxfw_dev->psid_size,
&component_count);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Could not find device PSID in MFA2 file", err);
return err;
}
for (i = 0; i < component_count; i++) {
struct mlxfw_mfa2_component *comp;
comp = mlxfw_mfa2_file_component_get(mfa2_file, mlxfw_dev->psid,
mlxfw_dev->psid_size, i);
if (IS_ERR(comp)) {
err = PTR_ERR(comp);
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Failed to get MFA2 component", err);
return err;
}
mlxfw_info(mlxfw_dev, "Flashing component type %d\n",
comp->index);
err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp,
reactivate_supp, extack);
mlxfw_mfa2_file_component_put(comp);
if (err)
return err;
}
return 0;
}
int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
const struct firmware *firmware,
struct netlink_ext_ack *extack)
{
struct mlxfw_mfa2_file *mfa2_file;
bool reactivate_supp = true;
u32 fwhandle;
int err;
if (!mlxfw_mfa2_check(firmware)) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Firmware file is not MFA2", -EINVAL);
return -EINVAL;
}
mfa2_file = mlxfw_mfa2_file_init(firmware);
if (IS_ERR(mfa2_file)) {
err = PTR_ERR(mfa2_file);
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Failed to initialize MFA2 firmware file", err);
return err;
}
mlxfw_info(mlxfw_dev, "Initialize firmware flash process\n");
mlxfw_status_notify(mlxfw_dev, "Initializing firmware flash process",
NULL, 0, 0);
err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Could not lock the firmware FSM", err);
goto err_fsm_lock;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_LOCKED, extack);
if (err)
goto err_state_wait_idle_to_locked;
err = mlxfw_fsm_reactivate(mlxfw_dev, extack, &reactivate_supp);
if (err)
goto err_fsm_reactivate;
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_LOCKED, extack);
if (err)
goto err_state_wait_reactivate_to_locked;
err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file,
reactivate_supp, extack);
if (err)
goto err_flash_components;
mlxfw_dbg(mlxfw_dev, "Activate image\n");
mlxfw_status_notify(mlxfw_dev, "Activating image", NULL, 0, 0);
err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle);
if (err) {
MLXFW_ERR_MSG(mlxfw_dev, extack,
"Could not activate the downloaded image", err);
goto err_fsm_activate;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_LOCKED, extack);
if (err)
goto err_state_wait_activate_to_locked;
mlxfw_dbg(mlxfw_dev, "Handle release\n");
mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
mlxfw_info(mlxfw_dev, "Firmware flash done\n");
mlxfw_status_notify(mlxfw_dev, "Firmware flash done", NULL, 0, 0);
mlxfw_mfa2_file_fini(mfa2_file);
return 0;
err_state_wait_activate_to_locked:
err_fsm_activate:
err_flash_components:
err_state_wait_reactivate_to_locked:
err_fsm_reactivate:
err_state_wait_idle_to_locked:
mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
err_fsm_lock:
mlxfw_mfa2_file_fini(mfa2_file);
return err;
}
EXPORT_SYMBOL(mlxfw_firmware_flash);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Yotam Gigi <yotamg@mellanox.com>");
MODULE_DESCRIPTION("Mellanox firmware flash lib");
@@ -0,0 +1,589 @@
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#define pr_fmt(fmt) "mlxfw_mfa2: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/vmalloc.h>
#include <linux/xz.h>
#include "mlxfw_mfa2.h"
#include "mlxfw_mfa2_file.h"
#include "mlxfw_mfa2_tlv.h"
#include "mlxfw_mfa2_format.h"
#include "mlxfw_mfa2_tlv_multi.h"
/* MFA2 FILE
* +----------------------------------+
* | MFA2 finger print |
* +----------------------------------+
* | package descriptor multi_tlv |
* | +------------------------------+ | +-----------------+
* | | package descriptor tlv +-----> |num_devices=n |
* | +------------------------------+ | |num_components=m |
* +----------------------------------+ |CB offset |
* | device descriptor multi_tlv | |... |
* | +------------------------------+ | | |
* | | PSID tlv | | +-----------------+
* | +------------------------------+ |
* | | component index tlv | |
* | +------------------------------+ |
* +----------------------------------+
* | component descriptor multi_tlv |
* | +------------------------------+ | +-----------------+
* | | component descriptor tlv +-----> |Among others: |
* | +------------------------------+ | |CB offset=o |
* +----------------------------------+ |comp index=i |
* | | |... |
* | | | |
* | | +-----------------+
* | COMPONENT BLOCK (CB) |
* | |
* | |
* | |
* +----------------------------------+
*
* On the top level, an MFA2 file contains:
* - Fingerprint
* - Several multi_tlvs (TLVs of type MLXFW_MFA2_TLV_MULTI, as defined in
* mlxfw_mfa2_format.h)
* - Compresses content block
*
* The first multi_tlv
* -------------------
* The first multi TLV is treated as package descriptor, and expected to have a
* first TLV child of type MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR which contains all
* the global information needed to parse the file. Among others, it contains
* the number of device descriptors and component descriptor following this
* multi TLV.
*
* The device descriptor multi_tlv
* -------------------------------
* The multi TLVs following the package descriptor are treated as device
* descriptor, and are expected to have the following children:
* - PSID TLV child of type MLXFW_MFA2_TLV_PSID containing that device PSID.
* - Component index of type MLXFW_MFA2_TLV_COMPONENT_PTR that contains that
* device component index.
*
* The component descriptor multi_tlv
* ----------------------------------
* The multi TLVs following the device descriptor multi TLVs are treated as
* component descriptor, and are expected to have a first child of type
* MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR that contains mostly the component index,
* needed for the flash process and the offset to the binary within the
* component block.
*/
static const u8 mlxfw_mfa2_fingerprint[] = "MLNX.MFA2.XZ.00!";
static const int mlxfw_mfa2_fingerprint_len =
sizeof(mlxfw_mfa2_fingerprint) - 1;
static const u8 mlxfw_mfa2_comp_magic[] = "#BIN.COMPONENT!#";
static const int mlxfw_mfa2_comp_magic_len = sizeof(mlxfw_mfa2_comp_magic) - 1;
bool mlxfw_mfa2_check(const struct firmware *fw)
{
if (fw->size < sizeof(mlxfw_mfa2_fingerprint))
return false;
return memcmp(fw->data, mlxfw_mfa2_fingerprint,
mlxfw_mfa2_fingerprint_len) == 0;
}
static bool
mlxfw_mfa2_tlv_multi_validate(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi)
{
const struct mlxfw_mfa2_tlv *tlv;
u16 idx;
/* Check that all children are valid */
mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) {
if (!tlv) {
pr_err("Multi has invalid child");
return false;
}
}
return true;
}
static bool
mlxfw_mfa2_file_dev_validate(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *dev_tlv,
u16 dev_idx)
{
const struct mlxfw_mfa2_tlv_component_ptr *cptr;
const struct mlxfw_mfa2_tlv_multi *multi;
const struct mlxfw_mfa2_tlv_psid *psid;
const struct mlxfw_mfa2_tlv *tlv;
u16 cptr_count;
u16 cptr_idx;
int err;
pr_debug("Device %d\n", dev_idx);
multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv);
if (!multi) {
pr_err("Device %d is not a valid TLV error\n", dev_idx);
return false;
}
if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi))
return false;
/* Validate the device has PSID tlv */
tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi,
MLXFW_MFA2_TLV_PSID, 0);
if (!tlv) {
pr_err("Device %d does not have PSID\n", dev_idx);
return false;
}
psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv);
if (!psid) {
pr_err("Device %d PSID TLV is not valid\n", dev_idx);
return false;
}
print_hex_dump_debug(" -- Device PSID ", DUMP_PREFIX_NONE, 16, 16,
psid->psid, be16_to_cpu(tlv->len), true);
/* Validate the device has COMPONENT_PTR */
err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, multi,
MLXFW_MFA2_TLV_COMPONENT_PTR,
&cptr_count);
if (err)
return false;
if (cptr_count == 0) {
pr_err("Device %d has no components\n", dev_idx);
return false;
}
for (cptr_idx = 0; cptr_idx < cptr_count; cptr_idx++) {
tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi,
MLXFW_MFA2_TLV_COMPONENT_PTR,
cptr_idx);
if (!tlv)
return false;
cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, tlv);
if (!cptr) {
pr_err("Device %d COMPONENT_PTR TLV is not valid\n",
dev_idx);
return false;
}
pr_debug(" -- Component index %d\n",
be16_to_cpu(cptr->component_index));
}
return true;
}
static bool
mlxfw_mfa2_file_comp_validate(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *comp_tlv,
u16 comp_idx)
{
const struct mlxfw_mfa2_tlv_component_descriptor *cdesc;
const struct mlxfw_mfa2_tlv_multi *multi;
const struct mlxfw_mfa2_tlv *tlv;
pr_debug("Component %d\n", comp_idx);
multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv);
if (!multi) {
pr_err("Component %d is not a valid TLV error\n", comp_idx);
return false;
}
if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi))
return false;
/* Check that component have COMPONENT_DESCRIPTOR as first child */
tlv = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);
if (!tlv) {
pr_err("Component descriptor %d multi TLV error\n", comp_idx);
return false;
}
cdesc = mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, tlv);
if (!cdesc) {
pr_err("Component %d does not have a valid descriptor\n",
comp_idx);
return false;
}
pr_debug(" -- Component type %d\n", be16_to_cpu(cdesc->identifier));
pr_debug(" -- Offset 0x%llx and size %d\n",
((u64) be32_to_cpu(cdesc->cb_offset_h) << 32)
| be32_to_cpu(cdesc->cb_offset_l), be32_to_cpu(cdesc->size));
return true;
}
static bool mlxfw_mfa2_file_validate(const struct mlxfw_mfa2_file *mfa2_file)
{
const struct mlxfw_mfa2_tlv *tlv;
u16 idx;
pr_debug("Validating file\n");
/* check that all the devices exist */
mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_dev,
mfa2_file->dev_count) {
if (!tlv) {
pr_err("Device TLV error\n");
return false;
}
/* Check each device */
if (!mlxfw_mfa2_file_dev_validate(mfa2_file, tlv, idx))
return false;
}
/* check that all the components exist */
mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_component,
mfa2_file->component_count) {
if (!tlv) {
pr_err("Device TLV error\n");
return false;
}
/* Check each component */
if (!mlxfw_mfa2_file_comp_validate(mfa2_file, tlv, idx))
return false;
}
return true;
}
struct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw)
{
const struct mlxfw_mfa2_tlv_package_descriptor *pd;
const struct mlxfw_mfa2_tlv_multi *multi;
const struct mlxfw_mfa2_tlv *multi_child;
const struct mlxfw_mfa2_tlv *first_tlv;
struct mlxfw_mfa2_file *mfa2_file;
const void *first_tlv_ptr;
const void *cb_top_ptr;
mfa2_file = kzalloc_obj(*mfa2_file);
if (!mfa2_file)
return ERR_PTR(-ENOMEM);
mfa2_file->fw = fw;
first_tlv_ptr = fw->data + NLA_ALIGN(mlxfw_mfa2_fingerprint_len);
first_tlv = mlxfw_mfa2_tlv_get(mfa2_file, first_tlv_ptr);
if (!first_tlv) {
pr_err("Could not parse package descriptor TLV\n");
goto err_out;
}
multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, first_tlv);
if (!multi) {
pr_err("First TLV is not of valid multi type\n");
goto err_out;
}
multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);
if (!multi_child)
goto err_out;
pd = mlxfw_mfa2_tlv_package_descriptor_get(mfa2_file, multi_child);
if (!pd) {
pr_err("Could not parse package descriptor TLV\n");
goto err_out;
}
mfa2_file->first_dev = mlxfw_mfa2_tlv_next(mfa2_file, first_tlv);
if (!mfa2_file->first_dev) {
pr_err("First device TLV is not valid\n");
goto err_out;
}
mfa2_file->dev_count = be16_to_cpu(pd->num_devices);
mfa2_file->first_component = mlxfw_mfa2_tlv_advance(mfa2_file,
mfa2_file->first_dev,
mfa2_file->dev_count);
mfa2_file->component_count = be16_to_cpu(pd->num_components);
mfa2_file->cb = fw->data + NLA_ALIGN(be32_to_cpu(pd->cb_offset));
if (!mlxfw_mfa2_valid_ptr(mfa2_file, mfa2_file->cb)) {
pr_err("Component block is out side the file\n");
goto err_out;
}
mfa2_file->cb_archive_size = be32_to_cpu(pd->cb_archive_size);
cb_top_ptr = mfa2_file->cb + mfa2_file->cb_archive_size - 1;
if (!mlxfw_mfa2_valid_ptr(mfa2_file, cb_top_ptr)) {
pr_err("Component block size is too big\n");
goto err_out;
}
if (!mlxfw_mfa2_file_validate(mfa2_file))
goto err_out;
return mfa2_file;
err_out:
kfree(mfa2_file);
return ERR_PTR(-EINVAL);
}
static const struct mlxfw_mfa2_tlv_multi *
mlxfw_mfa2_tlv_dev_get(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, u16 psid_size)
{
const struct mlxfw_mfa2_tlv_psid *tlv_psid;
const struct mlxfw_mfa2_tlv_multi *dev_multi;
const struct mlxfw_mfa2_tlv *dev_tlv;
const struct mlxfw_mfa2_tlv *tlv;
u32 idx;
/* for each device tlv */
mlxfw_mfa2_tlv_foreach(mfa2_file, dev_tlv, idx, mfa2_file->first_dev,
mfa2_file->dev_count) {
if (!dev_tlv)
return NULL;
dev_multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv);
if (!dev_multi)
return NULL;
/* find psid child and compare */
tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi,
MLXFW_MFA2_TLV_PSID, 0);
if (!tlv)
return NULL;
if (be16_to_cpu(tlv->len) != psid_size)
continue;
tlv_psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv);
if (!tlv_psid)
return NULL;
if (memcmp(psid, tlv_psid->psid, psid_size) == 0)
return dev_multi;
}
return NULL;
}
int mlxfw_mfa2_file_component_count(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, u32 psid_size,
u32 *p_count)
{
const struct mlxfw_mfa2_tlv_multi *dev_multi;
u16 count;
int err;
dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size);
if (!dev_multi)
return -EINVAL;
err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, dev_multi,
MLXFW_MFA2_TLV_COMPONENT_PTR,
&count);
if (err)
return err;
*p_count = count;
return 0;
}
static int mlxfw_mfa2_xz_dec_run(struct xz_dec *xz_dec, struct xz_buf *xz_buf,
bool *finished)
{
enum xz_ret xz_ret;
xz_ret = xz_dec_run(xz_dec, xz_buf);
switch (xz_ret) {
case XZ_STREAM_END:
*finished = true;
return 0;
case XZ_OK:
*finished = false;
return 0;
case XZ_MEM_ERROR:
pr_err("xz no memory\n");
return -ENOMEM;
case XZ_DATA_ERROR:
pr_err("xz file corrupted\n");
return -EINVAL;
case XZ_FORMAT_ERROR:
pr_err("xz format not found\n");
return -EINVAL;
case XZ_OPTIONS_ERROR:
pr_err("unsupported xz option\n");
return -EINVAL;
case XZ_MEMLIMIT_ERROR:
pr_err("xz dictionary too small\n");
return -EINVAL;
default:
pr_err("xz error %d\n", xz_ret);
return -EINVAL;
}
}
static int mlxfw_mfa2_file_cb_offset_xz(const struct mlxfw_mfa2_file *mfa2_file,
off_t off, size_t size, u8 *buf)
{
struct xz_dec *xz_dec;
struct xz_buf dec_buf;
off_t curr_off = 0;
bool finished;
int err;
xz_dec = xz_dec_init(XZ_DYNALLOC, (u32) -1);
if (!xz_dec)
return -EINVAL;
dec_buf.in_size = mfa2_file->cb_archive_size;
dec_buf.in = mfa2_file->cb;
dec_buf.in_pos = 0;
dec_buf.out = buf;
/* decode up to the offset */
do {
dec_buf.out_pos = 0;
dec_buf.out_size = min_t(size_t, size, off - curr_off);
if (dec_buf.out_size == 0)
break;
err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished);
if (err)
goto out;
if (finished) {
pr_err("xz section too short\n");
err = -EINVAL;
goto out;
}
curr_off += dec_buf.out_pos;
} while (curr_off != off);
/* decode the needed section */
dec_buf.out_pos = 0;
dec_buf.out_size = size;
err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished);
out:
xz_dec_end(xz_dec);
return err;
}
static const struct mlxfw_mfa2_tlv_component_descriptor *
mlxfw_mfa2_file_component_tlv_get(const struct mlxfw_mfa2_file *mfa2_file,
u16 comp_index)
{
const struct mlxfw_mfa2_tlv_multi *multi;
const struct mlxfw_mfa2_tlv *multi_child;
const struct mlxfw_mfa2_tlv *comp_tlv;
if (comp_index > mfa2_file->component_count)
return NULL;
comp_tlv = mlxfw_mfa2_tlv_advance(mfa2_file, mfa2_file->first_component,
comp_index);
if (!comp_tlv)
return NULL;
multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv);
if (!multi)
return NULL;
multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);
if (!multi_child)
return NULL;
return mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, multi_child);
}
struct mlxfw_mfa2_comp_data {
struct mlxfw_mfa2_component comp;
u8 buff[];
};
static const struct mlxfw_mfa2_tlv_component_descriptor *
mlxfw_mfa2_file_component_find(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, int psid_size,
int component_index)
{
const struct mlxfw_mfa2_tlv_component_ptr *cptr;
const struct mlxfw_mfa2_tlv_multi *dev_multi;
const struct mlxfw_mfa2_tlv *cptr_tlv;
u16 comp_idx;
dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size);
if (!dev_multi)
return NULL;
cptr_tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi,
MLXFW_MFA2_TLV_COMPONENT_PTR,
component_index);
if (!cptr_tlv)
return NULL;
cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, cptr_tlv);
if (!cptr)
return NULL;
comp_idx = be16_to_cpu(cptr->component_index);
return mlxfw_mfa2_file_component_tlv_get(mfa2_file, comp_idx);
}
struct mlxfw_mfa2_component *
mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, int psid_size,
int component_index)
{
const struct mlxfw_mfa2_tlv_component_descriptor *comp;
struct mlxfw_mfa2_comp_data *comp_data;
u32 comp_buf_size;
off_t cb_offset;
u32 comp_size;
int err;
comp = mlxfw_mfa2_file_component_find(mfa2_file, psid, psid_size,
component_index);
if (!comp)
return ERR_PTR(-EINVAL);
cb_offset = (u64) be32_to_cpu(comp->cb_offset_h) << 32 |
be32_to_cpu(comp->cb_offset_l);
comp_size = be32_to_cpu(comp->size);
comp_buf_size = comp_size + mlxfw_mfa2_comp_magic_len;
comp_data = vzalloc(sizeof(*comp_data) + comp_buf_size);
if (!comp_data)
return ERR_PTR(-ENOMEM);
comp_data->comp.data_size = comp_size;
comp_data->comp.index = be16_to_cpu(comp->identifier);
err = mlxfw_mfa2_file_cb_offset_xz(mfa2_file, cb_offset, comp_buf_size,
comp_data->buff);
if (err) {
pr_err("Component could not be reached in CB\n");
goto err_out;
}
if (memcmp(comp_data->buff, mlxfw_mfa2_comp_magic,
mlxfw_mfa2_comp_magic_len) != 0) {
pr_err("Component has wrong magic\n");
err = -EINVAL;
goto err_out;
}
comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len;
return &comp_data->comp;
err_out:
vfree(comp_data);
return ERR_PTR(err);
}
void mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *comp)
{
const struct mlxfw_mfa2_comp_data *comp_data;
comp_data = container_of(comp, struct mlxfw_mfa2_comp_data, comp);
vfree(comp_data);
}
void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file)
{
kfree(mfa2_file);
}
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_MFA2_H
#define _MLXFW_MFA2_H
#include <linux/firmware.h>
#include "mlxfw.h"
struct mlxfw_mfa2_component {
u16 index;
u32 data_size;
u8 *data;
};
struct mlxfw_mfa2_file;
bool mlxfw_mfa2_check(const struct firmware *fw);
struct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw);
int mlxfw_mfa2_file_component_count(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, u32 psid_size,
u32 *p_count);
struct mlxfw_mfa2_component *
mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file,
const char *psid, int psid_size,
int component_index);
void mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *component);
void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file);
#endif
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_MFA2_FILE_H
#define _MLXFW_MFA2_FILE_H
#include <linux/firmware.h>
#include <linux/kernel.h>
struct mlxfw_mfa2_file {
const struct firmware *fw;
const struct mlxfw_mfa2_tlv *first_dev;
u16 dev_count;
const struct mlxfw_mfa2_tlv *first_component;
u16 component_count;
const void *cb; /* components block */
u32 cb_archive_size; /* size of compressed components block */
};
static inline bool mlxfw_mfa2_valid_ptr(const struct mlxfw_mfa2_file *mfa2_file,
const void *ptr)
{
const void *valid_to = mfa2_file->fw->data + mfa2_file->fw->size;
const void *valid_from = mfa2_file->fw->data;
return ptr > valid_from && ptr < valid_to;
}
#endif
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_MFA2_FORMAT_H
#define _MLXFW_MFA2_FORMAT_H
#include "mlxfw_mfa2_file.h"
#include "mlxfw_mfa2_tlv.h"
enum mlxfw_mfa2_tlv_type {
MLXFW_MFA2_TLV_MULTI_PART = 0x01,
MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR = 0x02,
MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR = 0x04,
MLXFW_MFA2_TLV_COMPONENT_PTR = 0x22,
MLXFW_MFA2_TLV_PSID = 0x2A,
};
enum mlxfw_mfa2_compression_type {
MLXFW_MFA2_COMPRESSION_TYPE_NONE,
MLXFW_MFA2_COMPRESSION_TYPE_XZ,
};
struct mlxfw_mfa2_tlv_package_descriptor {
__be16 num_components;
__be16 num_devices;
__be32 cb_offset;
__be32 cb_archive_size;
__be32 cb_size_h;
__be32 cb_size_l;
u8 padding[3];
u8 cv_compression;
__be32 user_data_offset;
} __packed;
MLXFW_MFA2_TLV(package_descriptor, struct mlxfw_mfa2_tlv_package_descriptor,
MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR);
struct mlxfw_mfa2_tlv_multi {
__be16 num_extensions;
__be16 total_len;
} __packed;
MLXFW_MFA2_TLV(multi, struct mlxfw_mfa2_tlv_multi,
MLXFW_MFA2_TLV_MULTI_PART);
struct mlxfw_mfa2_tlv_psid {
DECLARE_FLEX_ARRAY(u8, psid);
} __packed;
MLXFW_MFA2_TLV_VARSIZE(psid, struct mlxfw_mfa2_tlv_psid,
MLXFW_MFA2_TLV_PSID);
struct mlxfw_mfa2_tlv_component_ptr {
__be16 storage_id;
__be16 component_index;
__be32 storage_address;
} __packed;
MLXFW_MFA2_TLV(component_ptr, struct mlxfw_mfa2_tlv_component_ptr,
MLXFW_MFA2_TLV_COMPONENT_PTR);
struct mlxfw_mfa2_tlv_component_descriptor {
__be16 pldm_classification;
__be16 identifier;
__be32 cb_offset_h;
__be32 cb_offset_l;
__be32 size;
} __packed;
MLXFW_MFA2_TLV(component_descriptor, struct mlxfw_mfa2_tlv_component_descriptor,
MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR);
#endif
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_MFA2_TLV_H
#define _MLXFW_MFA2_TLV_H
#include <linux/kernel.h>
#include "mlxfw_mfa2_file.h"
struct mlxfw_mfa2_tlv {
u8 version;
u8 type;
__be16 len;
u8 data[];
} __packed;
static inline const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_get(const struct mlxfw_mfa2_file *mfa2_file, const void *ptr)
{
if (!mlxfw_mfa2_valid_ptr(mfa2_file, ptr) ||
!mlxfw_mfa2_valid_ptr(mfa2_file, ptr + sizeof(struct mlxfw_mfa2_tlv)))
return NULL;
return ptr;
}
static inline const void *
mlxfw_mfa2_tlv_payload_get(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *tlv, u8 payload_type,
size_t payload_size, bool varsize)
{
void *tlv_top;
tlv_top = (void *) tlv + be16_to_cpu(tlv->len) - 1;
if (!mlxfw_mfa2_valid_ptr(mfa2_file, tlv) ||
!mlxfw_mfa2_valid_ptr(mfa2_file, tlv_top))
return NULL;
if (tlv->type != payload_type)
return NULL;
if (varsize && (be16_to_cpu(tlv->len) < payload_size))
return NULL;
if (!varsize && (be16_to_cpu(tlv->len) != payload_size))
return NULL;
return tlv->data;
}
#define MLXFW_MFA2_TLV(name, payload_type, tlv_type) \
static inline const payload_type * \
mlxfw_mfa2_tlv_ ## name ## _get(const struct mlxfw_mfa2_file *mfa2_file, \
const struct mlxfw_mfa2_tlv *tlv) \
{ \
return mlxfw_mfa2_tlv_payload_get(mfa2_file, tlv, \
tlv_type, sizeof(payload_type), \
false); \
}
#define MLXFW_MFA2_TLV_VARSIZE(name, payload_type, tlv_type) \
static inline const payload_type * \
mlxfw_mfa2_tlv_ ## name ## _get(const struct mlxfw_mfa2_file *mfa2_file, \
const struct mlxfw_mfa2_tlv *tlv) \
{ \
return mlxfw_mfa2_tlv_payload_get(mfa2_file, tlv, \
tlv_type, sizeof(payload_type), \
true); \
}
#endif
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#define pr_fmt(fmt) "MFA2: " fmt
#include "mlxfw_mfa2_tlv_multi.h"
#include <uapi/linux/netlink.h>
#define MLXFW_MFA2_TLV_TOTAL_SIZE(tlv) \
NLA_ALIGN(sizeof(*(tlv)) + be16_to_cpu((tlv)->len))
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_multi_child(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi)
{
size_t multi_len;
multi_len = NLA_ALIGN(sizeof(struct mlxfw_mfa2_tlv_multi));
return mlxfw_mfa2_tlv_get(mfa2_file, (void *) multi + multi_len);
}
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_next(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *tlv)
{
const struct mlxfw_mfa2_tlv_multi *multi;
u16 tlv_len;
void *next;
tlv_len = MLXFW_MFA2_TLV_TOTAL_SIZE(tlv);
if (tlv->type == MLXFW_MFA2_TLV_MULTI_PART) {
multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, tlv);
if (!multi)
return NULL;
tlv_len = NLA_ALIGN(tlv_len + be16_to_cpu(multi->total_len));
}
next = (void *) tlv + tlv_len;
return mlxfw_mfa2_tlv_get(mfa2_file, next);
}
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_advance(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *from_tlv, u16 count)
{
const struct mlxfw_mfa2_tlv *tlv;
u16 idx;
mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, from_tlv, count)
if (!tlv)
return NULL;
return tlv;
}
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_multi_child_find(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi,
enum mlxfw_mfa2_tlv_type type, u16 index)
{
const struct mlxfw_mfa2_tlv *tlv;
u16 skip = 0;
u16 idx;
mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) {
if (!tlv) {
pr_err("TLV parsing error\n");
return NULL;
}
if (tlv->type == type)
if (skip++ == index)
return tlv;
}
return NULL;
}
int mlxfw_mfa2_tlv_multi_child_count(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi,
enum mlxfw_mfa2_tlv_type type,
u16 *p_count)
{
const struct mlxfw_mfa2_tlv *tlv;
u16 count = 0;
u16 idx;
mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) {
if (!tlv) {
pr_err("TLV parsing error\n");
return -EINVAL;
}
if (tlv->type == type)
count++;
}
*p_count = count;
return 0;
}
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
#ifndef _MLXFW_MFA2_TLV_MULTI_H
#define _MLXFW_MFA2_TLV_MULTI_H
#include "mlxfw_mfa2_tlv.h"
#include "mlxfw_mfa2_format.h"
#include "mlxfw_mfa2_file.h"
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_multi_child(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi);
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_next(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *tlv);
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_advance(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv *from_tlv, u16 count);
const struct mlxfw_mfa2_tlv *
mlxfw_mfa2_tlv_multi_child_find(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi,
enum mlxfw_mfa2_tlv_type type, u16 index);
int mlxfw_mfa2_tlv_multi_child_count(const struct mlxfw_mfa2_file *mfa2_file,
const struct mlxfw_mfa2_tlv_multi *multi,
enum mlxfw_mfa2_tlv_type type,
u16 *p_count);
#define mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, from_tlv, count) \
for (idx = 0, tlv = from_tlv; idx < (count); \
idx++, tlv = mlxfw_mfa2_tlv_next(mfa2_file, tlv))
#define mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) \
mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, \
mlxfw_mfa2_tlv_multi_child(mfa2_file, multi), \
be16_to_cpu(multi->num_extensions) + 1)
#endif