]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Add the mlxfw module for Mellanox firmware flash process
authorYotam Gigi <yotamg@mellanox.com>
Tue, 23 May 2017 19:56:23 +0000 (21:56 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 25 May 2017 21:46:17 +0000 (17:46 -0400)
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: Yotam Gigi <yotamg@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
14 files changed:
MAINTAINERS
drivers/net/ethernet/mellanox/Kconfig
drivers/net/ethernet/mellanox/Makefile
drivers/net/ethernet/mellanox/mlxfw/Kconfig [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_file.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.h [new file with mode: 0644]

index 553dbbddf3e4c1289ff445b89c37b5ec127f2061..42378cf4b8446bf2c918a568055ed4f9793c8334 100644 (file)
@@ -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>
index d54701047401d73eeb54feca8cbc48c27ab5463e..84a20076411179c64dcd3e44d2f288121c57a392 100644 (file)
@@ -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
index 2e2a5ec509ac520bfddd3348e6fa08e373a37167..016aa263bc042c64c18e0a57608e10c3990fdcd0 100644 (file)
@@ -5,3 +5,4 @@
 obj-$(CONFIG_MLX4_CORE) += mlx4/
 obj-$(CONFIG_MLX5_CORE) += mlx5/core/
 obj-$(CONFIG_MLXSW_CORE) += mlxsw/
+obj-$(CONFIG_MLXFW) += mlxfw/
diff --git a/drivers/net/ethernet/mellanox/mlxfw/Kconfig b/drivers/net/ethernet/mellanox/mlxfw/Kconfig
new file mode 100644 (file)
index 0000000..56b60ac
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Mellanox firmware flash library configuration
+#
+
+config MLXFW
+        tristate "mlxfw" if COMPILE_TEST
diff --git a/drivers/net/ethernet/mellanox/mlxfw/Makefile b/drivers/net/ethernet/mellanox/mlxfw/Makefile
new file mode 100644 (file)
index 0000000..7448b30
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MLXFW)    += mlxfw.o
+mlxfw-objs             := mlxfw_fsm.o mlxfw_mfa2_tlv_multi.o mlxfw_mfa2.o
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw.h
new file mode 100644 (file)
index 0000000..beea4ba
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c
new file mode 100644 (file)
index 0000000..2cf8912
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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");
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
new file mode 100644 (file)
index 0000000..7e95890
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * 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);
+}
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.h
new file mode 100644 (file)
index 0000000..20472aa
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_file.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_file.h
new file mode 100644 (file)
index 0000000..f667942
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h
new file mode 100644 (file)
index 0000000..dd66737
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv.h
new file mode 100644 (file)
index 0000000..cc013e7
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.c
new file mode 100644 (file)
index 0000000..0094b92
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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;
+}
diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.h
new file mode 100644 (file)
index 0000000..2c66789
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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