162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define pr_fmt(fmt) "mlxfw_mfa2: " fmt 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/netlink.h> 962306a36Sopenharmony_ci#include <linux/vmalloc.h> 1062306a36Sopenharmony_ci#include <linux/xz.h> 1162306a36Sopenharmony_ci#include "mlxfw_mfa2.h" 1262306a36Sopenharmony_ci#include "mlxfw_mfa2_file.h" 1362306a36Sopenharmony_ci#include "mlxfw_mfa2_tlv.h" 1462306a36Sopenharmony_ci#include "mlxfw_mfa2_format.h" 1562306a36Sopenharmony_ci#include "mlxfw_mfa2_tlv_multi.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* MFA2 FILE 1862306a36Sopenharmony_ci * +----------------------------------+ 1962306a36Sopenharmony_ci * | MFA2 finger print | 2062306a36Sopenharmony_ci * +----------------------------------+ 2162306a36Sopenharmony_ci * | package descriptor multi_tlv | 2262306a36Sopenharmony_ci * | +------------------------------+ | +-----------------+ 2362306a36Sopenharmony_ci * | | package descriptor tlv +-----> |num_devices=n | 2462306a36Sopenharmony_ci * | +------------------------------+ | |num_components=m | 2562306a36Sopenharmony_ci * +----------------------------------+ |CB offset | 2662306a36Sopenharmony_ci * | device descriptor multi_tlv | |... | 2762306a36Sopenharmony_ci * | +------------------------------+ | | | 2862306a36Sopenharmony_ci * | | PSID tlv | | +-----------------+ 2962306a36Sopenharmony_ci * | +------------------------------+ | 3062306a36Sopenharmony_ci * | | component index tlv | | 3162306a36Sopenharmony_ci * | +------------------------------+ | 3262306a36Sopenharmony_ci * +----------------------------------+ 3362306a36Sopenharmony_ci * | component descriptor multi_tlv | 3462306a36Sopenharmony_ci * | +------------------------------+ | +-----------------+ 3562306a36Sopenharmony_ci * | | component descriptor tlv +-----> |Among others: | 3662306a36Sopenharmony_ci * | +------------------------------+ | |CB offset=o | 3762306a36Sopenharmony_ci * +----------------------------------+ |comp index=i | 3862306a36Sopenharmony_ci * | | |... | 3962306a36Sopenharmony_ci * | | | | 4062306a36Sopenharmony_ci * | | +-----------------+ 4162306a36Sopenharmony_ci * | COMPONENT BLOCK (CB) | 4262306a36Sopenharmony_ci * | | 4362306a36Sopenharmony_ci * | | 4462306a36Sopenharmony_ci * | | 4562306a36Sopenharmony_ci * +----------------------------------+ 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * On the top level, an MFA2 file contains: 4862306a36Sopenharmony_ci * - Fingerprint 4962306a36Sopenharmony_ci * - Several multi_tlvs (TLVs of type MLXFW_MFA2_TLV_MULTI, as defined in 5062306a36Sopenharmony_ci * mlxfw_mfa2_format.h) 5162306a36Sopenharmony_ci * - Compresses content block 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The first multi_tlv 5462306a36Sopenharmony_ci * ------------------- 5562306a36Sopenharmony_ci * The first multi TLV is treated as package descriptor, and expected to have a 5662306a36Sopenharmony_ci * first TLV child of type MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR which contains all 5762306a36Sopenharmony_ci * the global information needed to parse the file. Among others, it contains 5862306a36Sopenharmony_ci * the number of device descriptors and component descriptor following this 5962306a36Sopenharmony_ci * multi TLV. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * The device descriptor multi_tlv 6262306a36Sopenharmony_ci * ------------------------------- 6362306a36Sopenharmony_ci * The multi TLVs following the package descriptor are treated as device 6462306a36Sopenharmony_ci * descriptor, and are expected to have the following children: 6562306a36Sopenharmony_ci * - PSID TLV child of type MLXFW_MFA2_TLV_PSID containing that device PSID. 6662306a36Sopenharmony_ci * - Component index of type MLXFW_MFA2_TLV_COMPONENT_PTR that contains that 6762306a36Sopenharmony_ci * device component index. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * The component descriptor multi_tlv 7062306a36Sopenharmony_ci * ---------------------------------- 7162306a36Sopenharmony_ci * The multi TLVs following the device descriptor multi TLVs are treated as 7262306a36Sopenharmony_ci * component descriptor, and are expected to have a first child of type 7362306a36Sopenharmony_ci * MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR that contains mostly the component index, 7462306a36Sopenharmony_ci * needed for the flash process and the offset to the binary within the 7562306a36Sopenharmony_ci * component block. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const u8 mlxfw_mfa2_fingerprint[] = "MLNX.MFA2.XZ.00!"; 7962306a36Sopenharmony_cistatic const int mlxfw_mfa2_fingerprint_len = 8062306a36Sopenharmony_ci sizeof(mlxfw_mfa2_fingerprint) - 1; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const u8 mlxfw_mfa2_comp_magic[] = "#BIN.COMPONENT!#"; 8362306a36Sopenharmony_cistatic const int mlxfw_mfa2_comp_magic_len = sizeof(mlxfw_mfa2_comp_magic) - 1; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cibool mlxfw_mfa2_check(const struct firmware *fw) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci if (fw->size < sizeof(mlxfw_mfa2_fingerprint)) 8862306a36Sopenharmony_ci return false; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return memcmp(fw->data, mlxfw_mfa2_fingerprint, 9162306a36Sopenharmony_ci mlxfw_mfa2_fingerprint_len) == 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic bool 9562306a36Sopenharmony_cimlxfw_mfa2_tlv_multi_validate(const struct mlxfw_mfa2_file *mfa2_file, 9662306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *multi) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *tlv; 9962306a36Sopenharmony_ci u16 idx; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Check that all children are valid */ 10262306a36Sopenharmony_ci mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) { 10362306a36Sopenharmony_ci if (!tlv) { 10462306a36Sopenharmony_ci pr_err("Multi has invalid child"); 10562306a36Sopenharmony_ci return false; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci return true; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic bool 11262306a36Sopenharmony_cimlxfw_mfa2_file_dev_validate(const struct mlxfw_mfa2_file *mfa2_file, 11362306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *dev_tlv, 11462306a36Sopenharmony_ci u16 dev_idx) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_component_ptr *cptr; 11762306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *multi; 11862306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_psid *psid; 11962306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *tlv; 12062306a36Sopenharmony_ci u16 cptr_count; 12162306a36Sopenharmony_ci u16 cptr_idx; 12262306a36Sopenharmony_ci int err; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pr_debug("Device %d\n", dev_idx); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv); 12762306a36Sopenharmony_ci if (!multi) { 12862306a36Sopenharmony_ci pr_err("Device %d is not a valid TLV error\n", dev_idx); 12962306a36Sopenharmony_ci return false; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi)) 13362306a36Sopenharmony_ci return false; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Validate the device has PSID tlv */ 13662306a36Sopenharmony_ci tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi, 13762306a36Sopenharmony_ci MLXFW_MFA2_TLV_PSID, 0); 13862306a36Sopenharmony_ci if (!tlv) { 13962306a36Sopenharmony_ci pr_err("Device %d does not have PSID\n", dev_idx); 14062306a36Sopenharmony_ci return false; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv); 14462306a36Sopenharmony_ci if (!psid) { 14562306a36Sopenharmony_ci pr_err("Device %d PSID TLV is not valid\n", dev_idx); 14662306a36Sopenharmony_ci return false; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci print_hex_dump_debug(" -- Device PSID ", DUMP_PREFIX_NONE, 16, 16, 15062306a36Sopenharmony_ci psid->psid, be16_to_cpu(tlv->len), true); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Validate the device has COMPONENT_PTR */ 15362306a36Sopenharmony_ci err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, multi, 15462306a36Sopenharmony_ci MLXFW_MFA2_TLV_COMPONENT_PTR, 15562306a36Sopenharmony_ci &cptr_count); 15662306a36Sopenharmony_ci if (err) 15762306a36Sopenharmony_ci return false; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (cptr_count == 0) { 16062306a36Sopenharmony_ci pr_err("Device %d has no components\n", dev_idx); 16162306a36Sopenharmony_ci return false; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (cptr_idx = 0; cptr_idx < cptr_count; cptr_idx++) { 16562306a36Sopenharmony_ci tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi, 16662306a36Sopenharmony_ci MLXFW_MFA2_TLV_COMPONENT_PTR, 16762306a36Sopenharmony_ci cptr_idx); 16862306a36Sopenharmony_ci if (!tlv) 16962306a36Sopenharmony_ci return false; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, tlv); 17262306a36Sopenharmony_ci if (!cptr) { 17362306a36Sopenharmony_ci pr_err("Device %d COMPONENT_PTR TLV is not valid\n", 17462306a36Sopenharmony_ci dev_idx); 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci pr_debug(" -- Component index %d\n", 17962306a36Sopenharmony_ci be16_to_cpu(cptr->component_index)); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return true; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic bool 18562306a36Sopenharmony_cimlxfw_mfa2_file_comp_validate(const struct mlxfw_mfa2_file *mfa2_file, 18662306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *comp_tlv, 18762306a36Sopenharmony_ci u16 comp_idx) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_component_descriptor *cdesc; 19062306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *multi; 19162306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *tlv; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pr_debug("Component %d\n", comp_idx); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv); 19662306a36Sopenharmony_ci if (!multi) { 19762306a36Sopenharmony_ci pr_err("Component %d is not a valid TLV error\n", comp_idx); 19862306a36Sopenharmony_ci return false; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi)) 20262306a36Sopenharmony_ci return false; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Check that component have COMPONENT_DESCRIPTOR as first child */ 20562306a36Sopenharmony_ci tlv = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi); 20662306a36Sopenharmony_ci if (!tlv) { 20762306a36Sopenharmony_ci pr_err("Component descriptor %d multi TLV error\n", comp_idx); 20862306a36Sopenharmony_ci return false; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cdesc = mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, tlv); 21262306a36Sopenharmony_ci if (!cdesc) { 21362306a36Sopenharmony_ci pr_err("Component %d does not have a valid descriptor\n", 21462306a36Sopenharmony_ci comp_idx); 21562306a36Sopenharmony_ci return false; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci pr_debug(" -- Component type %d\n", be16_to_cpu(cdesc->identifier)); 21862306a36Sopenharmony_ci pr_debug(" -- Offset 0x%llx and size %d\n", 21962306a36Sopenharmony_ci ((u64) be32_to_cpu(cdesc->cb_offset_h) << 32) 22062306a36Sopenharmony_ci | be32_to_cpu(cdesc->cb_offset_l), be32_to_cpu(cdesc->size)); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return true; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic bool mlxfw_mfa2_file_validate(const struct mlxfw_mfa2_file *mfa2_file) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *tlv; 22862306a36Sopenharmony_ci u16 idx; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci pr_debug("Validating file\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* check that all the devices exist */ 23362306a36Sopenharmony_ci mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_dev, 23462306a36Sopenharmony_ci mfa2_file->dev_count) { 23562306a36Sopenharmony_ci if (!tlv) { 23662306a36Sopenharmony_ci pr_err("Device TLV error\n"); 23762306a36Sopenharmony_ci return false; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Check each device */ 24162306a36Sopenharmony_ci if (!mlxfw_mfa2_file_dev_validate(mfa2_file, tlv, idx)) 24262306a36Sopenharmony_ci return false; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* check that all the components exist */ 24662306a36Sopenharmony_ci mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_component, 24762306a36Sopenharmony_ci mfa2_file->component_count) { 24862306a36Sopenharmony_ci if (!tlv) { 24962306a36Sopenharmony_ci pr_err("Device TLV error\n"); 25062306a36Sopenharmony_ci return false; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Check each component */ 25462306a36Sopenharmony_ci if (!mlxfw_mfa2_file_comp_validate(mfa2_file, tlv, idx)) 25562306a36Sopenharmony_ci return false; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistruct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_package_descriptor *pd; 26362306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *multi; 26462306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *multi_child; 26562306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *first_tlv; 26662306a36Sopenharmony_ci struct mlxfw_mfa2_file *mfa2_file; 26762306a36Sopenharmony_ci const void *first_tlv_ptr; 26862306a36Sopenharmony_ci const void *cb_top_ptr; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci mfa2_file = kzalloc(sizeof(*mfa2_file), GFP_KERNEL); 27162306a36Sopenharmony_ci if (!mfa2_file) 27262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mfa2_file->fw = fw; 27562306a36Sopenharmony_ci first_tlv_ptr = fw->data + NLA_ALIGN(mlxfw_mfa2_fingerprint_len); 27662306a36Sopenharmony_ci first_tlv = mlxfw_mfa2_tlv_get(mfa2_file, first_tlv_ptr); 27762306a36Sopenharmony_ci if (!first_tlv) { 27862306a36Sopenharmony_ci pr_err("Could not parse package descriptor TLV\n"); 27962306a36Sopenharmony_ci goto err_out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, first_tlv); 28362306a36Sopenharmony_ci if (!multi) { 28462306a36Sopenharmony_ci pr_err("First TLV is not of valid multi type\n"); 28562306a36Sopenharmony_ci goto err_out; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi); 28962306a36Sopenharmony_ci if (!multi_child) 29062306a36Sopenharmony_ci goto err_out; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci pd = mlxfw_mfa2_tlv_package_descriptor_get(mfa2_file, multi_child); 29362306a36Sopenharmony_ci if (!pd) { 29462306a36Sopenharmony_ci pr_err("Could not parse package descriptor TLV\n"); 29562306a36Sopenharmony_ci goto err_out; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci mfa2_file->first_dev = mlxfw_mfa2_tlv_next(mfa2_file, first_tlv); 29962306a36Sopenharmony_ci if (!mfa2_file->first_dev) { 30062306a36Sopenharmony_ci pr_err("First device TLV is not valid\n"); 30162306a36Sopenharmony_ci goto err_out; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci mfa2_file->dev_count = be16_to_cpu(pd->num_devices); 30562306a36Sopenharmony_ci mfa2_file->first_component = mlxfw_mfa2_tlv_advance(mfa2_file, 30662306a36Sopenharmony_ci mfa2_file->first_dev, 30762306a36Sopenharmony_ci mfa2_file->dev_count); 30862306a36Sopenharmony_ci mfa2_file->component_count = be16_to_cpu(pd->num_components); 30962306a36Sopenharmony_ci mfa2_file->cb = fw->data + NLA_ALIGN(be32_to_cpu(pd->cb_offset)); 31062306a36Sopenharmony_ci if (!mlxfw_mfa2_valid_ptr(mfa2_file, mfa2_file->cb)) { 31162306a36Sopenharmony_ci pr_err("Component block is out side the file\n"); 31262306a36Sopenharmony_ci goto err_out; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci mfa2_file->cb_archive_size = be32_to_cpu(pd->cb_archive_size); 31562306a36Sopenharmony_ci cb_top_ptr = mfa2_file->cb + mfa2_file->cb_archive_size - 1; 31662306a36Sopenharmony_ci if (!mlxfw_mfa2_valid_ptr(mfa2_file, cb_top_ptr)) { 31762306a36Sopenharmony_ci pr_err("Component block size is too big\n"); 31862306a36Sopenharmony_ci goto err_out; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!mlxfw_mfa2_file_validate(mfa2_file)) 32262306a36Sopenharmony_ci goto err_out; 32362306a36Sopenharmony_ci return mfa2_file; 32462306a36Sopenharmony_cierr_out: 32562306a36Sopenharmony_ci kfree(mfa2_file); 32662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic const struct mlxfw_mfa2_tlv_multi * 33062306a36Sopenharmony_cimlxfw_mfa2_tlv_dev_get(const struct mlxfw_mfa2_file *mfa2_file, 33162306a36Sopenharmony_ci const char *psid, u16 psid_size) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_psid *tlv_psid; 33462306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *dev_multi; 33562306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *dev_tlv; 33662306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *tlv; 33762306a36Sopenharmony_ci u32 idx; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* for each device tlv */ 34062306a36Sopenharmony_ci mlxfw_mfa2_tlv_foreach(mfa2_file, dev_tlv, idx, mfa2_file->first_dev, 34162306a36Sopenharmony_ci mfa2_file->dev_count) { 34262306a36Sopenharmony_ci if (!dev_tlv) 34362306a36Sopenharmony_ci return NULL; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dev_multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv); 34662306a36Sopenharmony_ci if (!dev_multi) 34762306a36Sopenharmony_ci return NULL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* find psid child and compare */ 35062306a36Sopenharmony_ci tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi, 35162306a36Sopenharmony_ci MLXFW_MFA2_TLV_PSID, 0); 35262306a36Sopenharmony_ci if (!tlv) 35362306a36Sopenharmony_ci return NULL; 35462306a36Sopenharmony_ci if (be16_to_cpu(tlv->len) != psid_size) 35562306a36Sopenharmony_ci continue; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci tlv_psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv); 35862306a36Sopenharmony_ci if (!tlv_psid) 35962306a36Sopenharmony_ci return NULL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (memcmp(psid, tlv_psid->psid, psid_size) == 0) 36262306a36Sopenharmony_ci return dev_multi; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return NULL; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciint mlxfw_mfa2_file_component_count(const struct mlxfw_mfa2_file *mfa2_file, 36962306a36Sopenharmony_ci const char *psid, u32 psid_size, 37062306a36Sopenharmony_ci u32 *p_count) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *dev_multi; 37362306a36Sopenharmony_ci u16 count; 37462306a36Sopenharmony_ci int err; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size); 37762306a36Sopenharmony_ci if (!dev_multi) 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, dev_multi, 38162306a36Sopenharmony_ci MLXFW_MFA2_TLV_COMPONENT_PTR, 38262306a36Sopenharmony_ci &count); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci return err; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci *p_count = count; 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int mlxfw_mfa2_xz_dec_run(struct xz_dec *xz_dec, struct xz_buf *xz_buf, 39162306a36Sopenharmony_ci bool *finished) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci enum xz_ret xz_ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci xz_ret = xz_dec_run(xz_dec, xz_buf); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci switch (xz_ret) { 39862306a36Sopenharmony_ci case XZ_STREAM_END: 39962306a36Sopenharmony_ci *finished = true; 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci case XZ_OK: 40262306a36Sopenharmony_ci *finished = false; 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci case XZ_MEM_ERROR: 40562306a36Sopenharmony_ci pr_err("xz no memory\n"); 40662306a36Sopenharmony_ci return -ENOMEM; 40762306a36Sopenharmony_ci case XZ_DATA_ERROR: 40862306a36Sopenharmony_ci pr_err("xz file corrupted\n"); 40962306a36Sopenharmony_ci return -EINVAL; 41062306a36Sopenharmony_ci case XZ_FORMAT_ERROR: 41162306a36Sopenharmony_ci pr_err("xz format not found\n"); 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci case XZ_OPTIONS_ERROR: 41462306a36Sopenharmony_ci pr_err("unsupported xz option\n"); 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci case XZ_MEMLIMIT_ERROR: 41762306a36Sopenharmony_ci pr_err("xz dictionary too small\n"); 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci pr_err("xz error %d\n", xz_ret); 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int mlxfw_mfa2_file_cb_offset_xz(const struct mlxfw_mfa2_file *mfa2_file, 42662306a36Sopenharmony_ci off_t off, size_t size, u8 *buf) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct xz_dec *xz_dec; 42962306a36Sopenharmony_ci struct xz_buf dec_buf; 43062306a36Sopenharmony_ci off_t curr_off = 0; 43162306a36Sopenharmony_ci bool finished; 43262306a36Sopenharmony_ci int err; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci xz_dec = xz_dec_init(XZ_DYNALLOC, (u32) -1); 43562306a36Sopenharmony_ci if (!xz_dec) 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci dec_buf.in_size = mfa2_file->cb_archive_size; 43962306a36Sopenharmony_ci dec_buf.in = mfa2_file->cb; 44062306a36Sopenharmony_ci dec_buf.in_pos = 0; 44162306a36Sopenharmony_ci dec_buf.out = buf; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* decode up to the offset */ 44462306a36Sopenharmony_ci do { 44562306a36Sopenharmony_ci dec_buf.out_pos = 0; 44662306a36Sopenharmony_ci dec_buf.out_size = min_t(size_t, size, off - curr_off); 44762306a36Sopenharmony_ci if (dec_buf.out_size == 0) 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished); 45162306a36Sopenharmony_ci if (err) 45262306a36Sopenharmony_ci goto out; 45362306a36Sopenharmony_ci if (finished) { 45462306a36Sopenharmony_ci pr_err("xz section too short\n"); 45562306a36Sopenharmony_ci err = -EINVAL; 45662306a36Sopenharmony_ci goto out; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci curr_off += dec_buf.out_pos; 45962306a36Sopenharmony_ci } while (curr_off != off); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* decode the needed section */ 46262306a36Sopenharmony_ci dec_buf.out_pos = 0; 46362306a36Sopenharmony_ci dec_buf.out_size = size; 46462306a36Sopenharmony_ci err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished); 46562306a36Sopenharmony_ciout: 46662306a36Sopenharmony_ci xz_dec_end(xz_dec); 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const struct mlxfw_mfa2_tlv_component_descriptor * 47162306a36Sopenharmony_cimlxfw_mfa2_file_component_tlv_get(const struct mlxfw_mfa2_file *mfa2_file, 47262306a36Sopenharmony_ci u16 comp_index) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *multi; 47562306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *multi_child; 47662306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *comp_tlv; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (comp_index > mfa2_file->component_count) 47962306a36Sopenharmony_ci return NULL; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci comp_tlv = mlxfw_mfa2_tlv_advance(mfa2_file, mfa2_file->first_component, 48262306a36Sopenharmony_ci comp_index); 48362306a36Sopenharmony_ci if (!comp_tlv) 48462306a36Sopenharmony_ci return NULL; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv); 48762306a36Sopenharmony_ci if (!multi) 48862306a36Sopenharmony_ci return NULL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi); 49162306a36Sopenharmony_ci if (!multi_child) 49262306a36Sopenharmony_ci return NULL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, multi_child); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistruct mlxfw_mfa2_comp_data { 49862306a36Sopenharmony_ci struct mlxfw_mfa2_component comp; 49962306a36Sopenharmony_ci u8 buff[]; 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic const struct mlxfw_mfa2_tlv_component_descriptor * 50362306a36Sopenharmony_cimlxfw_mfa2_file_component_find(const struct mlxfw_mfa2_file *mfa2_file, 50462306a36Sopenharmony_ci const char *psid, int psid_size, 50562306a36Sopenharmony_ci int component_index) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_component_ptr *cptr; 50862306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_multi *dev_multi; 50962306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv *cptr_tlv; 51062306a36Sopenharmony_ci u16 comp_idx; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size); 51362306a36Sopenharmony_ci if (!dev_multi) 51462306a36Sopenharmony_ci return NULL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci cptr_tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi, 51762306a36Sopenharmony_ci MLXFW_MFA2_TLV_COMPONENT_PTR, 51862306a36Sopenharmony_ci component_index); 51962306a36Sopenharmony_ci if (!cptr_tlv) 52062306a36Sopenharmony_ci return NULL; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, cptr_tlv); 52362306a36Sopenharmony_ci if (!cptr) 52462306a36Sopenharmony_ci return NULL; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci comp_idx = be16_to_cpu(cptr->component_index); 52762306a36Sopenharmony_ci return mlxfw_mfa2_file_component_tlv_get(mfa2_file, comp_idx); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistruct mlxfw_mfa2_component * 53162306a36Sopenharmony_cimlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file, 53262306a36Sopenharmony_ci const char *psid, int psid_size, 53362306a36Sopenharmony_ci int component_index) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci const struct mlxfw_mfa2_tlv_component_descriptor *comp; 53662306a36Sopenharmony_ci struct mlxfw_mfa2_comp_data *comp_data; 53762306a36Sopenharmony_ci u32 comp_buf_size; 53862306a36Sopenharmony_ci off_t cb_offset; 53962306a36Sopenharmony_ci u32 comp_size; 54062306a36Sopenharmony_ci int err; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci comp = mlxfw_mfa2_file_component_find(mfa2_file, psid, psid_size, 54362306a36Sopenharmony_ci component_index); 54462306a36Sopenharmony_ci if (!comp) 54562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci cb_offset = (u64) be32_to_cpu(comp->cb_offset_h) << 32 | 54862306a36Sopenharmony_ci be32_to_cpu(comp->cb_offset_l); 54962306a36Sopenharmony_ci comp_size = be32_to_cpu(comp->size); 55062306a36Sopenharmony_ci comp_buf_size = comp_size + mlxfw_mfa2_comp_magic_len; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci comp_data = vzalloc(sizeof(*comp_data) + comp_buf_size); 55362306a36Sopenharmony_ci if (!comp_data) 55462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 55562306a36Sopenharmony_ci comp_data->comp.data_size = comp_size; 55662306a36Sopenharmony_ci comp_data->comp.index = be16_to_cpu(comp->identifier); 55762306a36Sopenharmony_ci err = mlxfw_mfa2_file_cb_offset_xz(mfa2_file, cb_offset, comp_buf_size, 55862306a36Sopenharmony_ci comp_data->buff); 55962306a36Sopenharmony_ci if (err) { 56062306a36Sopenharmony_ci pr_err("Component could not be reached in CB\n"); 56162306a36Sopenharmony_ci goto err_out; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (memcmp(comp_data->buff, mlxfw_mfa2_comp_magic, 56562306a36Sopenharmony_ci mlxfw_mfa2_comp_magic_len) != 0) { 56662306a36Sopenharmony_ci pr_err("Component has wrong magic\n"); 56762306a36Sopenharmony_ci err = -EINVAL; 56862306a36Sopenharmony_ci goto err_out; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len; 57262306a36Sopenharmony_ci return &comp_data->comp; 57362306a36Sopenharmony_cierr_out: 57462306a36Sopenharmony_ci vfree(comp_data); 57562306a36Sopenharmony_ci return ERR_PTR(err); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_civoid mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *comp) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci const struct mlxfw_mfa2_comp_data *comp_data; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci comp_data = container_of(comp, struct mlxfw_mfa2_comp_data, comp); 58362306a36Sopenharmony_ci vfree(comp_data); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_civoid mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci kfree(mfa2_file); 58962306a36Sopenharmony_ci} 590