brintos

brintos / linux-shallow public Read only

0
0
Text · 16.3 KiB · e6f677e Raw
590 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */3 4#define pr_fmt(fmt) "mlxfw_mfa2: " fmt5 6#include <linux/kernel.h>7#include <linux/module.h>8#include <linux/netlink.h>9#include <linux/vmalloc.h>10#include <linux/xz.h>11#include "mlxfw_mfa2.h"12#include "mlxfw_mfa2_file.h"13#include "mlxfw_mfa2_tlv.h"14#include "mlxfw_mfa2_format.h"15#include "mlxfw_mfa2_tlv_multi.h"16 17/*               MFA2 FILE18 *  +----------------------------------+19 *  |        MFA2 finger print         |20 *  +----------------------------------+21 *  |   package descriptor multi_tlv   |22 *  | +------------------------------+ |     +-----------------+23 *  | |    package descriptor tlv    +-----> |num_devices=n    |24 *  | +------------------------------+ |     |num_components=m |25 *  +----------------------------------+     |CB offset        |26 *  |    device descriptor multi_tlv   |     |...              |27 *  | +------------------------------+ |     |                 |28 *  | |           PSID tlv           | |     +-----------------+29 *  | +------------------------------+ |30 *  | |     component index tlv      | |31 *  | +------------------------------+ |32 *  +----------------------------------+33 *  |  component descriptor multi_tlv  |34 *  | +------------------------------+ |     +-----------------+35 *  | |  component descriptor tlv    +-----> |Among others:    |36 *  | +------------------------------+ |     |CB offset=o      |37 *  +----------------------------------+     |comp index=i     |38 *  |                                  |     |...              |39 *  |                                  |     |                 |40 *  |                                  |     +-----------------+41 *  |        COMPONENT BLOCK (CB)      |42 *  |                                  |43 *  |                                  |44 *  |                                  |45 *  +----------------------------------+46 *47 * On the top level, an MFA2 file contains:48 *  - Fingerprint49 *  - Several multi_tlvs (TLVs of type MLXFW_MFA2_TLV_MULTI, as defined in50 *    mlxfw_mfa2_format.h)51 *  - Compresses content block52 *53 * The first multi_tlv54 * -------------------55 * The first multi TLV is treated as package descriptor, and expected to have a56 * first TLV child of type MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR which contains all57 * the global information needed to parse the file. Among others, it contains58 * the number of device descriptors and component descriptor following this59 * multi TLV.60 *61 * The device descriptor multi_tlv62 * -------------------------------63 * The multi TLVs following the package descriptor are treated as device64 * descriptor, and are expected to have the following children:65 *  - PSID TLV child of type MLXFW_MFA2_TLV_PSID containing that device PSID.66 *  - Component index of type MLXFW_MFA2_TLV_COMPONENT_PTR that contains that67 *    device component index.68 *69 * The component descriptor multi_tlv70 * ----------------------------------71 * The multi TLVs following the device descriptor multi TLVs are treated as72 * component descriptor, and are expected to have a first child of type73 * MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR that contains mostly the component index,74 * needed for the flash process and the offset to the binary within the75 * component block.76 */77 78static const u8 mlxfw_mfa2_fingerprint[] = "MLNX.MFA2.XZ.00!";79static const int mlxfw_mfa2_fingerprint_len =80			sizeof(mlxfw_mfa2_fingerprint) - 1;81 82static const u8 mlxfw_mfa2_comp_magic[] = "#BIN.COMPONENT!#";83static const int mlxfw_mfa2_comp_magic_len = sizeof(mlxfw_mfa2_comp_magic) - 1;84 85bool mlxfw_mfa2_check(const struct firmware *fw)86{87	if (fw->size < sizeof(mlxfw_mfa2_fingerprint))88		return false;89 90	return memcmp(fw->data, mlxfw_mfa2_fingerprint,91		      mlxfw_mfa2_fingerprint_len) == 0;92}93 94static bool95mlxfw_mfa2_tlv_multi_validate(const struct mlxfw_mfa2_file *mfa2_file,96			      const struct mlxfw_mfa2_tlv_multi *multi)97{98	const struct mlxfw_mfa2_tlv *tlv;99	u16 idx;100 101	/* Check that all children are valid */102	mlxfw_mfa2_tlv_multi_foreach(mfa2_file, tlv, idx, multi) {103		if (!tlv) {104			pr_err("Multi has invalid child");105			return false;106		}107	}108	return true;109}110 111static bool112mlxfw_mfa2_file_dev_validate(const struct mlxfw_mfa2_file *mfa2_file,113			     const struct mlxfw_mfa2_tlv *dev_tlv,114			     u16 dev_idx)115{116	const struct mlxfw_mfa2_tlv_component_ptr *cptr;117	const struct mlxfw_mfa2_tlv_multi *multi;118	const struct mlxfw_mfa2_tlv_psid *psid;119	const struct mlxfw_mfa2_tlv *tlv;120	u16 cptr_count;121	u16 cptr_idx;122	int err;123 124	pr_debug("Device %d\n", dev_idx);125 126	multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv);127	if (!multi) {128		pr_err("Device %d is not a valid TLV error\n", dev_idx);129		return false;130	}131 132	if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi))133		return false;134 135	/* Validate the device has PSID tlv */136	tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi,137					      MLXFW_MFA2_TLV_PSID, 0);138	if (!tlv) {139		pr_err("Device %d does not have PSID\n", dev_idx);140		return false;141	}142 143	psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv);144	if (!psid) {145		pr_err("Device %d PSID TLV is not valid\n", dev_idx);146		return false;147	}148 149	print_hex_dump_debug("  -- Device PSID ", DUMP_PREFIX_NONE, 16, 16,150			     psid->psid, be16_to_cpu(tlv->len), true);151 152	/* Validate the device has COMPONENT_PTR */153	err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, multi,154					       MLXFW_MFA2_TLV_COMPONENT_PTR,155					       &cptr_count);156	if (err)157		return false;158 159	if (cptr_count == 0) {160		pr_err("Device %d has no components\n", dev_idx);161		return false;162	}163 164	for (cptr_idx = 0; cptr_idx < cptr_count; cptr_idx++) {165		tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, multi,166						      MLXFW_MFA2_TLV_COMPONENT_PTR,167						      cptr_idx);168		if (!tlv)169			return false;170 171		cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, tlv);172		if (!cptr) {173			pr_err("Device %d COMPONENT_PTR TLV is not valid\n",174			       dev_idx);175			return false;176		}177 178		pr_debug("  -- Component index %d\n",179			 be16_to_cpu(cptr->component_index));180	}181	return true;182}183 184static bool185mlxfw_mfa2_file_comp_validate(const struct mlxfw_mfa2_file *mfa2_file,186			      const struct mlxfw_mfa2_tlv *comp_tlv,187			      u16 comp_idx)188{189	const struct mlxfw_mfa2_tlv_component_descriptor *cdesc;190	const struct mlxfw_mfa2_tlv_multi *multi;191	const struct mlxfw_mfa2_tlv *tlv;192 193	pr_debug("Component %d\n", comp_idx);194 195	multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv);196	if (!multi) {197		pr_err("Component %d is not a valid TLV error\n", comp_idx);198		return false;199	}200 201	if (!mlxfw_mfa2_tlv_multi_validate(mfa2_file, multi))202		return false;203 204	/* Check that component have COMPONENT_DESCRIPTOR as first child */205	tlv = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);206	if (!tlv) {207		pr_err("Component descriptor %d multi TLV error\n", comp_idx);208		return false;209	}210 211	cdesc = mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, tlv);212	if (!cdesc) {213		pr_err("Component %d does not have a valid descriptor\n",214		       comp_idx);215		return false;216	}217	pr_debug("  -- Component type %d\n", be16_to_cpu(cdesc->identifier));218	pr_debug("  -- Offset 0x%llx and size %d\n",219		 ((u64) be32_to_cpu(cdesc->cb_offset_h) << 32)220		 | be32_to_cpu(cdesc->cb_offset_l), be32_to_cpu(cdesc->size));221 222	return true;223}224 225static bool mlxfw_mfa2_file_validate(const struct mlxfw_mfa2_file *mfa2_file)226{227	const struct mlxfw_mfa2_tlv *tlv;228	u16 idx;229 230	pr_debug("Validating file\n");231 232	/* check that all the devices exist */233	mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_dev,234			       mfa2_file->dev_count) {235		if (!tlv) {236			pr_err("Device TLV error\n");237			return false;238		}239 240		/* Check each device */241		if (!mlxfw_mfa2_file_dev_validate(mfa2_file, tlv, idx))242			return false;243	}244 245	/* check that all the components exist */246	mlxfw_mfa2_tlv_foreach(mfa2_file, tlv, idx, mfa2_file->first_component,247			       mfa2_file->component_count) {248		if (!tlv) {249			pr_err("Device TLV error\n");250			return false;251		}252 253		/* Check each component */254		if (!mlxfw_mfa2_file_comp_validate(mfa2_file, tlv, idx))255			return false;256	}257	return true;258}259 260struct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw)261{262	const struct mlxfw_mfa2_tlv_package_descriptor *pd;263	const struct mlxfw_mfa2_tlv_multi *multi;264	const struct mlxfw_mfa2_tlv *multi_child;265	const struct mlxfw_mfa2_tlv *first_tlv;266	struct mlxfw_mfa2_file *mfa2_file;267	const void *first_tlv_ptr;268	const void *cb_top_ptr;269 270	mfa2_file = kzalloc(sizeof(*mfa2_file), GFP_KERNEL);271	if (!mfa2_file)272		return ERR_PTR(-ENOMEM);273 274	mfa2_file->fw = fw;275	first_tlv_ptr = fw->data + NLA_ALIGN(mlxfw_mfa2_fingerprint_len);276	first_tlv = mlxfw_mfa2_tlv_get(mfa2_file, first_tlv_ptr);277	if (!first_tlv) {278		pr_err("Could not parse package descriptor TLV\n");279		goto err_out;280	}281 282	multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, first_tlv);283	if (!multi) {284		pr_err("First TLV is not of valid multi type\n");285		goto err_out;286	}287 288	multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);289	if (!multi_child)290		goto err_out;291 292	pd = mlxfw_mfa2_tlv_package_descriptor_get(mfa2_file, multi_child);293	if (!pd) {294		pr_err("Could not parse package descriptor TLV\n");295		goto err_out;296	}297 298	mfa2_file->first_dev = mlxfw_mfa2_tlv_next(mfa2_file, first_tlv);299	if (!mfa2_file->first_dev) {300		pr_err("First device TLV is not valid\n");301		goto err_out;302	}303 304	mfa2_file->dev_count = be16_to_cpu(pd->num_devices);305	mfa2_file->first_component = mlxfw_mfa2_tlv_advance(mfa2_file,306							    mfa2_file->first_dev,307							    mfa2_file->dev_count);308	mfa2_file->component_count = be16_to_cpu(pd->num_components);309	mfa2_file->cb = fw->data + NLA_ALIGN(be32_to_cpu(pd->cb_offset));310	if (!mlxfw_mfa2_valid_ptr(mfa2_file, mfa2_file->cb)) {311		pr_err("Component block is out side the file\n");312		goto err_out;313	}314	mfa2_file->cb_archive_size = be32_to_cpu(pd->cb_archive_size);315	cb_top_ptr = mfa2_file->cb + mfa2_file->cb_archive_size - 1;316	if (!mlxfw_mfa2_valid_ptr(mfa2_file, cb_top_ptr)) {317		pr_err("Component block size is too big\n");318		goto err_out;319	}320 321	if (!mlxfw_mfa2_file_validate(mfa2_file))322		goto err_out;323	return mfa2_file;324err_out:325	kfree(mfa2_file);326	return ERR_PTR(-EINVAL);327}328 329static const struct mlxfw_mfa2_tlv_multi *330mlxfw_mfa2_tlv_dev_get(const struct mlxfw_mfa2_file *mfa2_file,331		       const char *psid, u16 psid_size)332{333	const struct mlxfw_mfa2_tlv_psid *tlv_psid;334	const struct mlxfw_mfa2_tlv_multi *dev_multi;335	const struct mlxfw_mfa2_tlv *dev_tlv;336	const struct mlxfw_mfa2_tlv *tlv;337	u32 idx;338 339	/* for each device tlv */340	mlxfw_mfa2_tlv_foreach(mfa2_file, dev_tlv, idx, mfa2_file->first_dev,341			       mfa2_file->dev_count) {342		if (!dev_tlv)343			return NULL;344 345		dev_multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, dev_tlv);346		if (!dev_multi)347			return NULL;348 349		/* find psid child and compare */350		tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi,351						      MLXFW_MFA2_TLV_PSID, 0);352		if (!tlv)353			return NULL;354		if (be16_to_cpu(tlv->len) != psid_size)355			continue;356 357		tlv_psid = mlxfw_mfa2_tlv_psid_get(mfa2_file, tlv);358		if (!tlv_psid)359			return NULL;360 361		if (memcmp(psid, tlv_psid->psid, psid_size) == 0)362			return dev_multi;363	}364 365	return NULL;366}367 368int mlxfw_mfa2_file_component_count(const struct mlxfw_mfa2_file *mfa2_file,369				    const char *psid, u32 psid_size,370				    u32 *p_count)371{372	const struct mlxfw_mfa2_tlv_multi *dev_multi;373	u16 count;374	int err;375 376	dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size);377	if (!dev_multi)378		return -EINVAL;379 380	err = mlxfw_mfa2_tlv_multi_child_count(mfa2_file, dev_multi,381					       MLXFW_MFA2_TLV_COMPONENT_PTR,382					       &count);383	if (err)384		return err;385 386	*p_count = count;387	return 0;388}389 390static int mlxfw_mfa2_xz_dec_run(struct xz_dec *xz_dec, struct xz_buf *xz_buf,391				 bool *finished)392{393	enum xz_ret xz_ret;394 395	xz_ret = xz_dec_run(xz_dec, xz_buf);396 397	switch (xz_ret) {398	case XZ_STREAM_END:399		*finished = true;400		return 0;401	case XZ_OK:402		*finished = false;403		return 0;404	case XZ_MEM_ERROR:405		pr_err("xz no memory\n");406		return -ENOMEM;407	case XZ_DATA_ERROR:408		pr_err("xz file corrupted\n");409		return -EINVAL;410	case XZ_FORMAT_ERROR:411		pr_err("xz format not found\n");412		return -EINVAL;413	case XZ_OPTIONS_ERROR:414		pr_err("unsupported xz option\n");415		return -EINVAL;416	case XZ_MEMLIMIT_ERROR:417		pr_err("xz dictionary too small\n");418		return -EINVAL;419	default:420		pr_err("xz error %d\n", xz_ret);421		return -EINVAL;422	}423}424 425static int mlxfw_mfa2_file_cb_offset_xz(const struct mlxfw_mfa2_file *mfa2_file,426					off_t off, size_t size, u8 *buf)427{428	struct xz_dec *xz_dec;429	struct xz_buf dec_buf;430	off_t curr_off = 0;431	bool finished;432	int err;433 434	xz_dec = xz_dec_init(XZ_DYNALLOC, (u32) -1);435	if (!xz_dec)436		return -EINVAL;437 438	dec_buf.in_size = mfa2_file->cb_archive_size;439	dec_buf.in = mfa2_file->cb;440	dec_buf.in_pos = 0;441	dec_buf.out = buf;442 443	/* decode up to the offset */444	do {445		dec_buf.out_pos = 0;446		dec_buf.out_size = min_t(size_t, size, off - curr_off);447		if (dec_buf.out_size == 0)448			break;449 450		err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished);451		if (err)452			goto out;453		if (finished) {454			pr_err("xz section too short\n");455			err = -EINVAL;456			goto out;457		}458		curr_off += dec_buf.out_pos;459	} while (curr_off != off);460 461	/* decode the needed section */462	dec_buf.out_pos = 0;463	dec_buf.out_size = size;464	err = mlxfw_mfa2_xz_dec_run(xz_dec, &dec_buf, &finished);465out:466	xz_dec_end(xz_dec);467	return err;468}469 470static const struct mlxfw_mfa2_tlv_component_descriptor *471mlxfw_mfa2_file_component_tlv_get(const struct mlxfw_mfa2_file *mfa2_file,472				  u16 comp_index)473{474	const struct mlxfw_mfa2_tlv_multi *multi;475	const struct mlxfw_mfa2_tlv *multi_child;476	const struct mlxfw_mfa2_tlv *comp_tlv;477 478	if (comp_index > mfa2_file->component_count)479		return NULL;480 481	comp_tlv = mlxfw_mfa2_tlv_advance(mfa2_file, mfa2_file->first_component,482					  comp_index);483	if (!comp_tlv)484		return NULL;485 486	multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, comp_tlv);487	if (!multi)488		return NULL;489 490	multi_child = mlxfw_mfa2_tlv_multi_child(mfa2_file, multi);491	if (!multi_child)492		return NULL;493 494	return mlxfw_mfa2_tlv_component_descriptor_get(mfa2_file, multi_child);495}496 497struct mlxfw_mfa2_comp_data {498	struct mlxfw_mfa2_component comp;499	u8 buff[];500};501 502static const struct mlxfw_mfa2_tlv_component_descriptor *503mlxfw_mfa2_file_component_find(const struct mlxfw_mfa2_file *mfa2_file,504			       const char *psid, int psid_size,505			       int component_index)506{507	const struct mlxfw_mfa2_tlv_component_ptr *cptr;508	const struct mlxfw_mfa2_tlv_multi *dev_multi;509	const struct mlxfw_mfa2_tlv *cptr_tlv;510	u16 comp_idx;511 512	dev_multi = mlxfw_mfa2_tlv_dev_get(mfa2_file, psid, psid_size);513	if (!dev_multi)514		return NULL;515 516	cptr_tlv = mlxfw_mfa2_tlv_multi_child_find(mfa2_file, dev_multi,517						   MLXFW_MFA2_TLV_COMPONENT_PTR,518						   component_index);519	if (!cptr_tlv)520		return NULL;521 522	cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, cptr_tlv);523	if (!cptr)524		return NULL;525 526	comp_idx = be16_to_cpu(cptr->component_index);527	return mlxfw_mfa2_file_component_tlv_get(mfa2_file, comp_idx);528}529 530struct mlxfw_mfa2_component *531mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file,532			      const char *psid, int psid_size,533			      int component_index)534{535	const struct mlxfw_mfa2_tlv_component_descriptor *comp;536	struct mlxfw_mfa2_comp_data *comp_data;537	u32 comp_buf_size;538	off_t cb_offset;539	u32 comp_size;540	int err;541 542	comp = mlxfw_mfa2_file_component_find(mfa2_file, psid, psid_size,543					      component_index);544	if (!comp)545		return ERR_PTR(-EINVAL);546 547	cb_offset = (u64) be32_to_cpu(comp->cb_offset_h) << 32 |548		    be32_to_cpu(comp->cb_offset_l);549	comp_size = be32_to_cpu(comp->size);550	comp_buf_size = comp_size + mlxfw_mfa2_comp_magic_len;551 552	comp_data = vzalloc(sizeof(*comp_data) + comp_buf_size);553	if (!comp_data)554		return ERR_PTR(-ENOMEM);555	comp_data->comp.data_size = comp_size;556	comp_data->comp.index = be16_to_cpu(comp->identifier);557	err = mlxfw_mfa2_file_cb_offset_xz(mfa2_file, cb_offset, comp_buf_size,558					   comp_data->buff);559	if (err) {560		pr_err("Component could not be reached in CB\n");561		goto err_out;562	}563 564	if (memcmp(comp_data->buff, mlxfw_mfa2_comp_magic,565		   mlxfw_mfa2_comp_magic_len) != 0) {566		pr_err("Component has wrong magic\n");567		err = -EINVAL;568		goto err_out;569	}570 571	comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len;572	return &comp_data->comp;573err_out:574	vfree(comp_data);575	return ERR_PTR(err);576}577 578void mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *comp)579{580	const struct mlxfw_mfa2_comp_data *comp_data;581 582	comp_data = container_of(comp, struct mlxfw_mfa2_comp_data, comp);583	vfree(comp_data);584}585 586void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file)587{588	kfree(mfa2_file);589}590