Commit 410ed13c authored by Yotam Gigi's avatar Yotam Gigi Committed by David S. Miller

Add the mlxfw module for Mellanox firmware flash process

The mlxfw module is in charge of common logic needed to flash Mellanox
devices firmware, which consists of:
 - Parse the Mellanox Firmware Archive version 2 (MFA2) format, which is
   the format used to store the Mellanox firmware. The MFA2 format file can
   hold firmware for many different silicon variants, differentiated by a
   unique ID called PSID. In addition, the MFA2 file data section is
   compressed using xz compression to save both file-system space and
   memory at extraction time.
 - Implement the firmware flash state machine logic, which is a common
   logic for Mellanox products needed to flash the firmware to the device.

As the module is shared between different Mellanox products, it defines a
set of callbacks to be implemented by the specific driver for hardware
interaction.
Signed-off-by: default avatarYotam Gigi <yotamg@mellanox.com>
Reviewed-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ff5f58f5
......@@ -8330,6 +8330,14 @@ W: http://www.mellanox.com
Q: http://patchwork.ozlabs.org/project/netdev/list/
F: drivers/net/ethernet/mellanox/mlxsw/
MELLANOX FIRMWARE FLASH LIBRARY (mlxfw)
M: Yotam Gigi <yotamg@mellanox.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
Q: http://patchwork.ozlabs.org/project/netdev/list/
F: drivers/net/ethernet/mellanox/mlxfw/
MELLANOX MLXCPLD I2C AND MUX DRIVER
M: Vadim Pasternak <vadimp@mellanox.com>
M: Michael Shych <michaelsh@mellanox.com>
......
......@@ -19,5 +19,6 @@ if NET_VENDOR_MELLANOX
source "drivers/net/ethernet/mellanox/mlx4/Kconfig"
source "drivers/net/ethernet/mellanox/mlx5/core/Kconfig"
source "drivers/net/ethernet/mellanox/mlxsw/Kconfig"
source "drivers/net/ethernet/mellanox/mlxfw/Kconfig"
endif # NET_VENDOR_MELLANOX
......@@ -5,3 +5,4 @@
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_MLX5_CORE) += mlx5/core/
obj-$(CONFIG_MLXSW_CORE) += mlxsw/
obj-$(CONFIG_MLXFW) += mlxfw/
#
# Mellanox firmware flash library configuration
#
config MLXFW
tristate "mlxfw" if COMPILE_TEST
obj-$(CONFIG_MLXFW) += mlxfw.o
mlxfw-objs := mlxfw_fsm.o mlxfw_mfa2_tlv_multi.o mlxfw_mfa2.o
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MLXFW_H
#define _MLXFW_H
#include <linux/firmware.h>
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,
};
struct mlxfw_dev;
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_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);
};
struct mlxfw_dev {
const struct mlxfw_dev_ops *ops;
const char *psid;
u16 psid_size;
};
int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
const struct firmware *firmware);
#endif
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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)
#define MLXFW_FSM_MAX_COMPONENT_SIZE (10 * (1 << 20))
static const char * const mlxfw_fsm_state_err_str[] = {
[MLXFW_FSM_STATE_ERR_ERROR] =
"general error",
[MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] =
"component hash mismatch",
[MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] =
"component not applicable",
[MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] =
"unknown key",
[MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] =
"authentication failed",
[MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] =
"component was not signed",
[MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] =
"key not applicable",
[MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] =
"bad format",
[MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] =
"pending reset",
[MLXFW_FSM_STATE_ERR_MAX] =
"unknown error"
};
static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
enum mlxfw_fsm_state fsm_state)
{
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)
return err;
if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK) {
pr_err("Firmware flash failed: %s\n",
mlxfw_fsm_state_err_str[fsm_state_err]);
return -EINVAL;
}
if (curr_fsm_state != fsm_state) {
if (--times == 0) {
pr_err("Timeout reached on FSM state change");
return -ETIMEDOUT;
}
msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS);
goto retry;
}
return 0;
}
#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)
{
u16 comp_max_write_size;
u8 comp_align_bits;
u32 comp_max_size;
u16 block_size;
u8 *block_ptr;
u32 offset;
int err;
err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index,
&comp_max_size, &comp_align_bits,
&comp_max_write_size);
if (err)
return err;
comp_max_size = min_t(u32, comp_max_size, MLXFW_FSM_MAX_COMPONENT_SIZE);
if (comp->data_size > comp_max_size) {
pr_err("Component %d is of size %d which is bigger than limit %d\n",
comp->index, comp->data_size, comp_max_size);
return -EINVAL;
}
comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size,
comp_align_bits);
pr_debug("Component update\n");
err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle,
comp->index,
comp->data_size);
if (err)
return err;
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_DOWNLOAD);
if (err)
goto err_out;
pr_debug("Component download\n");
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)
goto err_out;
}
pr_debug("Component verify\n");
err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle,
comp->index);
if (err)
goto err_out;
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
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)
{
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) {
pr_err("Could not find device PSID in MFA2 file\n");
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))
return PTR_ERR(comp);
pr_info("Flashing component type %d\n", comp->index);
err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp);
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 mlxfw_mfa2_file *mfa2_file;
u32 fwhandle;
int err;
if (!mlxfw_mfa2_check(firmware)) {
pr_err("Firmware file is not MFA2\n");
return -EINVAL;
}
mfa2_file = mlxfw_mfa2_file_init(firmware);
if (IS_ERR(mfa2_file))
return PTR_ERR(mfa2_file);
pr_info("Initialize firmware flash process\n");
err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle);
if (err) {
pr_err("Could not lock the firmware FSM\n");
goto err_fsm_lock;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
MLXFW_FSM_STATE_LOCKED);
if (err)
goto err_state_wait_idle_to_locked;
err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file);
if (err)
goto err_flash_components;
pr_debug("Activate image\n");
err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle);
if (err) {
pr_err("Could not activate the downloaded image\n");
goto err_fsm_activate;
}
err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
if (err)
goto err_state_wait_activate_to_locked;
pr_debug("Handle release\n");
mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
pr_info("Firmware flash done.\n");
mlxfw_mfa2_file_fini(mfa2_file);
return 0;
err_state_wait_activate_to_locked:
err_fsm_activate:
err_flash_components:
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");
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define pr_fmt(fmt) "mlxfw_mfa2: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netlink.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 = kcalloc(1, sizeof(*mfa2_file), GFP_KERNEL);
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);
if (err)
goto out;
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[0];
};
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 = kmalloc(sizeof(*comp_data) + comp_buf_size, GFP_KERNEL);
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");
goto err_out;
}
comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len;
return &comp_data->comp;
err_out:
kfree(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);
kfree(comp_data);
}
void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file)
{
kfree(mfa2_file);
}
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_file.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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 {
u8 psid[0];
} __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
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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[0];
} __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
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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);
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;
}
/*
* drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment