1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #define pr_fmt(fmt) "mlxfw_mfa2: " fmt |
5 | |
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 FILE |
18 | * +----------------------------------+ |
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 | * - Fingerprint |
49 | * - Several multi_tlvs (TLVs of type MLXFW_MFA2_TLV_MULTI, as defined in |
50 | * mlxfw_mfa2_format.h) |
51 | * - Compresses content block |
52 | * |
53 | * The first multi_tlv |
54 | * ------------------- |
55 | * The first multi TLV is treated as package descriptor, and expected to have a |
56 | * first TLV child of type MLXFW_MFA2_TLV_PACKAGE_DESCRIPTOR which contains all |
57 | * the global information needed to parse the file. Among others, it contains |
58 | * the number of device descriptors and component descriptor following this |
59 | * multi TLV. |
60 | * |
61 | * The device descriptor multi_tlv |
62 | * ------------------------------- |
63 | * The multi TLVs following the package descriptor are treated as device |
64 | * 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 that |
67 | * device component index. |
68 | * |
69 | * The component descriptor multi_tlv |
70 | * ---------------------------------- |
71 | * The multi TLVs following the device descriptor multi TLVs are treated as |
72 | * component descriptor, and are expected to have a first child of type |
73 | * MLXFW_MFA2_TLV_COMPONENT_DESCRIPTOR that contains mostly the component index, |
74 | * needed for the flash process and the offset to the binary within the |
75 | * component block. |
76 | */ |
77 | |
78 | static const u8 mlxfw_mfa2_fingerprint[] = "MLNX.MFA2.XZ.00!" ; |
79 | static const int mlxfw_mfa2_fingerprint_len = |
80 | sizeof(mlxfw_mfa2_fingerprint) - 1; |
81 | |
82 | static const u8 mlxfw_mfa2_comp_magic[] = "#BIN.COMPONENT!#" ; |
83 | static const int mlxfw_mfa2_comp_magic_len = sizeof(mlxfw_mfa2_comp_magic) - 1; |
84 | |
85 | bool mlxfw_mfa2_check(const struct firmware *fw) |
86 | { |
87 | if (fw->size < sizeof(mlxfw_mfa2_fingerprint)) |
88 | return false; |
89 | |
90 | return memcmp(p: fw->data, q: mlxfw_mfa2_fingerprint, |
91 | size: mlxfw_mfa2_fingerprint_len) == 0; |
92 | } |
93 | |
94 | static bool |
95 | mlxfw_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 | |
111 | static bool |
112 | mlxfw_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, tlv: 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 | type: MLXFW_MFA2_TLV_PSID, index: 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 | type: MLXFW_MFA2_TLV_COMPONENT_PTR, |
155 | p_count: &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 | type: MLXFW_MFA2_TLV_COMPONENT_PTR, |
167 | index: 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 | |
184 | static bool |
185 | mlxfw_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, tlv: 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 | |
225 | static 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, dev_tlv: tlv, dev_idx: 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, comp_tlv: tlv, comp_idx: idx)) |
255 | return false; |
256 | } |
257 | return true; |
258 | } |
259 | |
260 | struct 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(size: sizeof(*mfa2_file), GFP_KERNEL); |
271 | if (!mfa2_file) |
272 | return ERR_PTR(error: -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, ptr: 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, tlv: 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, tlv: 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, tlv: 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 | from_tlv: mfa2_file->first_dev, |
307 | count: 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, ptr: 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, ptr: 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; |
324 | err_out: |
325 | kfree(objp: mfa2_file); |
326 | return ERR_PTR(error: -EINVAL); |
327 | } |
328 | |
329 | static const struct mlxfw_mfa2_tlv_multi * |
330 | mlxfw_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, tlv: 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, multi: dev_multi, |
351 | type: MLXFW_MFA2_TLV_PSID, index: 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(p: psid, q: tlv_psid->psid, size: psid_size) == 0) |
362 | return dev_multi; |
363 | } |
364 | |
365 | return NULL; |
366 | } |
367 | |
368 | int 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, multi: dev_multi, |
381 | type: MLXFW_MFA2_TLV_COMPONENT_PTR, |
382 | p_count: &count); |
383 | if (err) |
384 | return err; |
385 | |
386 | *p_count = count; |
387 | return 0; |
388 | } |
389 | |
390 | static 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(s: xz_dec, b: 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 | |
425 | static 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(mode: XZ_DYNALLOC, dict_max: (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, xz_buf: &dec_buf, finished: &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, xz_buf: &dec_buf, finished: &finished); |
465 | out: |
466 | xz_dec_end(s: xz_dec); |
467 | return err; |
468 | } |
469 | |
470 | static const struct mlxfw_mfa2_tlv_component_descriptor * |
471 | mlxfw_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, from_tlv: mfa2_file->first_component, |
482 | count: comp_index); |
483 | if (!comp_tlv) |
484 | return NULL; |
485 | |
486 | multi = mlxfw_mfa2_tlv_multi_get(mfa2_file, tlv: 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, tlv: multi_child); |
495 | } |
496 | |
497 | struct mlxfw_mfa2_comp_data { |
498 | struct mlxfw_mfa2_component comp; |
499 | u8 buff[]; |
500 | }; |
501 | |
502 | static const struct mlxfw_mfa2_tlv_component_descriptor * |
503 | mlxfw_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, multi: dev_multi, |
517 | type: MLXFW_MFA2_TLV_COMPONENT_PTR, |
518 | index: component_index); |
519 | if (!cptr_tlv) |
520 | return NULL; |
521 | |
522 | cptr = mlxfw_mfa2_tlv_component_ptr_get(mfa2_file, tlv: 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_index: comp_idx); |
528 | } |
529 | |
530 | struct mlxfw_mfa2_component * |
531 | mlxfw_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(error: -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(size: sizeof(*comp_data) + comp_buf_size); |
553 | if (!comp_data) |
554 | return ERR_PTR(error: -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, off: cb_offset, size: comp_buf_size, |
558 | buf: 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(p: comp_data->buff, q: mlxfw_mfa2_comp_magic, |
565 | size: 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; |
573 | err_out: |
574 | vfree(addr: comp_data); |
575 | return ERR_PTR(error: err); |
576 | } |
577 | |
578 | void 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(addr: comp_data); |
584 | } |
585 | |
586 | void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file) |
587 | { |
588 | kfree(objp: mfa2_file); |
589 | } |
590 | |