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