1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* Copyright (C) 2018-2019, Intel Corporation. */ |
3 | |
4 | #include <linux/unaligned.h> |
5 | #include <linux/crc32.h> |
6 | #include <linux/device.h> |
7 | #include <linux/firmware.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/pci.h> |
11 | #include <linux/pldmfw.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/uuid.h> |
14 | |
15 | #include "pldmfw_private.h" |
16 | |
17 | /* Internal structure used to store details about the PLDM image file as it is |
18 | * being validated and processed. |
19 | */ |
20 | struct pldmfw_priv { |
21 | struct pldmfw *context; |
22 | const struct firmware *fw; |
23 | |
24 | /* current offset of firmware image */ |
25 | size_t offset; |
26 | |
27 | struct list_head records; |
28 | struct list_head components; |
29 | |
30 | /* PLDM Firmware Package Header */ |
31 | const struct __pldm_header *header; |
32 | u16 total_header_size; |
33 | |
34 | /* length of the component bitmap */ |
35 | u16 component_bitmap_len; |
36 | u16 bitmap_size; |
37 | |
38 | /* Start of the component image information */ |
39 | u16 component_count; |
40 | const u8 *component_start; |
41 | |
42 | /* Start pf the firmware device id records */ |
43 | const u8 *record_start; |
44 | u8 record_count; |
45 | |
46 | /* The CRC at the end of the package header */ |
47 | u32 header_crc; |
48 | |
49 | struct pldmfw_record *matching_record; |
50 | }; |
51 | |
52 | /** |
53 | * pldm_check_fw_space - Verify that the firmware image has space left |
54 | * @data: pointer to private data |
55 | * @offset: offset to start from |
56 | * @length: length to check for |
57 | * |
58 | * Verify that the firmware data can hold a chunk of bytes with the specified |
59 | * offset and length. |
60 | * |
61 | * Returns: zero on success, or -EFAULT if the image does not have enough |
62 | * space left to fit the expected length. |
63 | */ |
64 | static int |
65 | pldm_check_fw_space(struct pldmfw_priv *data, size_t offset, size_t length) |
66 | { |
67 | size_t expected_size = offset + length; |
68 | struct device *dev = data->context->dev; |
69 | |
70 | if (data->fw->size < expected_size) { |
71 | dev_dbg(dev, "Firmware file size smaller than expected. Got %zu bytes, needed %zu bytes\n", |
72 | data->fw->size, expected_size); |
73 | return -EFAULT; |
74 | } |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | /** |
80 | * pldm_move_fw_offset - Move the current firmware offset forward |
81 | * @data: pointer to private data |
82 | * @bytes_to_move: number of bytes to move the offset forward by |
83 | * |
84 | * Check that there is enough space past the current offset, and then move the |
85 | * offset forward by this amount. |
86 | * |
87 | * Returns: zero on success, or -EFAULT if the image is too small to fit the |
88 | * expected length. |
89 | */ |
90 | static int |
91 | pldm_move_fw_offset(struct pldmfw_priv *data, size_t bytes_to_move) |
92 | { |
93 | int err; |
94 | |
95 | err = pldm_check_fw_space(data, offset: data->offset, length: bytes_to_move); |
96 | if (err) |
97 | return err; |
98 | |
99 | data->offset += bytes_to_move; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | /** |
105 | * pldm_parse_header - Validate and extract details about the PLDM header |
106 | * @data: pointer to private data |
107 | * |
108 | * Performs initial basic verification of the PLDM image, up to the first |
109 | * firmware record. |
110 | * |
111 | * This includes the following checks and extractions |
112 | * |
113 | * * Verify that the UUID at the start of the header matches the expected |
114 | * value as defined in the DSP0267 PLDM specification |
115 | * * Check that the revision is 0x01 |
116 | * * Extract the total header_size and verify that the image is large enough |
117 | * to contain at least the length of this header |
118 | * * Extract the size of the component bitmap length |
119 | * * Extract a pointer to the start of the record area |
120 | * |
121 | * Returns: zero on success, or a negative error code on failure. |
122 | */ |
123 | static int pldm_parse_header(struct pldmfw_priv *data) |
124 | { |
125 | const struct __pldmfw_record_area *record_area; |
126 | struct device *dev = data->context->dev; |
127 | const struct __pldm_header *header; |
128 | size_t header_size; |
129 | int err; |
130 | |
131 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*header)); |
132 | if (err) |
133 | return err; |
134 | |
135 | header = (const struct __pldm_header *)data->fw->data; |
136 | data->header = header; |
137 | |
138 | if (!uuid_equal(u1: &header->id, u2: &pldm_firmware_header_id)) { |
139 | dev_dbg(dev, "Invalid package header identifier. Expected UUID %pUB, but got %pUB\n", |
140 | &pldm_firmware_header_id, &header->id); |
141 | return -EINVAL; |
142 | } |
143 | |
144 | if (header->revision != PACKAGE_HEADER_FORMAT_REVISION) { |
145 | dev_dbg(dev, "Invalid package header revision. Expected revision %u but got %u\n", |
146 | PACKAGE_HEADER_FORMAT_REVISION, header->revision); |
147 | return -EOPNOTSUPP; |
148 | } |
149 | |
150 | data->total_header_size = get_unaligned_le16(p: &header->size); |
151 | header_size = data->total_header_size - sizeof(*header); |
152 | |
153 | err = pldm_check_fw_space(data, offset: data->offset, length: header_size); |
154 | if (err) |
155 | return err; |
156 | |
157 | data->component_bitmap_len = |
158 | get_unaligned_le16(p: &header->component_bitmap_len); |
159 | |
160 | if (data->component_bitmap_len % 8 != 0) { |
161 | dev_dbg(dev, "Invalid component bitmap length. The length is %u, which is not a multiple of 8\n", |
162 | data->component_bitmap_len); |
163 | return -EINVAL; |
164 | } |
165 | |
166 | data->bitmap_size = data->component_bitmap_len / 8; |
167 | |
168 | err = pldm_move_fw_offset(data, bytes_to_move: header->version_len); |
169 | if (err) |
170 | return err; |
171 | |
172 | /* extract a pointer to the record area, which just follows the main |
173 | * PLDM header data. |
174 | */ |
175 | record_area = (const struct __pldmfw_record_area *)(data->fw->data + |
176 | data->offset); |
177 | |
178 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*record_area)); |
179 | if (err) |
180 | return err; |
181 | |
182 | data->record_count = record_area->record_count; |
183 | data->record_start = record_area->records; |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | /** |
189 | * pldm_check_desc_tlv_len - Check that the length matches expectation |
190 | * @data: pointer to image details |
191 | * @type: the descriptor type |
192 | * @size: the length from the descriptor header |
193 | * |
194 | * If the descriptor type is one of the documented descriptor types according |
195 | * to the standard, verify that the provided length matches. |
196 | * |
197 | * If the type is not recognized or is VENDOR_DEFINED, return zero. |
198 | * |
199 | * Returns: zero on success, or -EINVAL if the specified size of a standard |
200 | * TLV does not match the expected value defined for that TLV. |
201 | */ |
202 | static int |
203 | pldm_check_desc_tlv_len(struct pldmfw_priv *data, u16 type, u16 size) |
204 | { |
205 | struct device *dev = data->context->dev; |
206 | u16 expected_size; |
207 | |
208 | switch (type) { |
209 | case PLDM_DESC_ID_PCI_VENDOR_ID: |
210 | case PLDM_DESC_ID_PCI_DEVICE_ID: |
211 | case PLDM_DESC_ID_PCI_SUBVENDOR_ID: |
212 | case PLDM_DESC_ID_PCI_SUBDEV_ID: |
213 | expected_size = 2; |
214 | break; |
215 | case PLDM_DESC_ID_PCI_REVISION_ID: |
216 | expected_size = 1; |
217 | break; |
218 | case PLDM_DESC_ID_PNP_VENDOR_ID: |
219 | expected_size = 3; |
220 | break; |
221 | case PLDM_DESC_ID_IANA_ENTERPRISE_ID: |
222 | case PLDM_DESC_ID_ACPI_VENDOR_ID: |
223 | case PLDM_DESC_ID_PNP_PRODUCT_ID: |
224 | case PLDM_DESC_ID_ACPI_PRODUCT_ID: |
225 | expected_size = 4; |
226 | break; |
227 | case PLDM_DESC_ID_UUID: |
228 | expected_size = 16; |
229 | break; |
230 | case PLDM_DESC_ID_VENDOR_DEFINED: |
231 | return 0; |
232 | default: |
233 | /* Do not report an error on an unexpected TLV */ |
234 | dev_dbg(dev, "Found unrecognized TLV type 0x%04x\n", type); |
235 | return 0; |
236 | } |
237 | |
238 | if (size != expected_size) { |
239 | dev_dbg(dev, "Found TLV type 0x%04x with unexpected length. Got %u bytes, but expected %u bytes\n", |
240 | type, size, expected_size); |
241 | return -EINVAL; |
242 | } |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | /** |
248 | * pldm_parse_desc_tlvs - Check and skip past a number of TLVs |
249 | * @data: pointer to private data |
250 | * @record: pointer to the record this TLV belongs too |
251 | * @desc_count: descriptor count |
252 | * |
253 | * From the current offset, read and extract the descriptor TLVs, updating the |
254 | * current offset each time. |
255 | * |
256 | * Returns: zero on success, or a negative error code on failure. |
257 | */ |
258 | static int |
259 | pldm_parse_desc_tlvs(struct pldmfw_priv *data, struct pldmfw_record *record, u8 desc_count) |
260 | { |
261 | const struct __pldmfw_desc_tlv *__desc; |
262 | const u8 *desc_start; |
263 | u8 i; |
264 | |
265 | desc_start = data->fw->data + data->offset; |
266 | |
267 | pldm_for_each_desc_tlv(i, __desc, desc_start, desc_count) { |
268 | struct pldmfw_desc_tlv *desc; |
269 | int err; |
270 | u16 type, size; |
271 | |
272 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*__desc)); |
273 | if (err) |
274 | return err; |
275 | |
276 | type = get_unaligned_le16(p: &__desc->type); |
277 | |
278 | /* According to DSP0267, this only includes the data field */ |
279 | size = get_unaligned_le16(p: &__desc->size); |
280 | |
281 | err = pldm_check_desc_tlv_len(data, type, size); |
282 | if (err) |
283 | return err; |
284 | |
285 | /* check that we have space and move the offset forward */ |
286 | err = pldm_move_fw_offset(data, bytes_to_move: size); |
287 | if (err) |
288 | return err; |
289 | |
290 | desc = kzalloc(sizeof(*desc), GFP_KERNEL); |
291 | if (!desc) |
292 | return -ENOMEM; |
293 | |
294 | desc->type = type; |
295 | desc->size = size; |
296 | desc->data = __desc->data; |
297 | |
298 | list_add_tail(new: &desc->entry, head: &record->descs); |
299 | } |
300 | |
301 | return 0; |
302 | } |
303 | |
304 | /** |
305 | * pldm_parse_one_record - Verify size of one PLDM record |
306 | * @data: pointer to image details |
307 | * @__record: pointer to the record to check |
308 | * |
309 | * This function checks that the record size does not exceed either the size |
310 | * of the firmware file or the total length specified in the header section. |
311 | * |
312 | * It also verifies that the recorded length of the start of the record |
313 | * matches the size calculated by adding the static structure length, the |
314 | * component bitmap length, the version string length, the length of all |
315 | * descriptor TLVs, and the length of the package data. |
316 | * |
317 | * Returns: zero on success, or a negative error code on failure. |
318 | */ |
319 | static int |
320 | pldm_parse_one_record(struct pldmfw_priv *data, |
321 | const struct __pldmfw_record_info *__record) |
322 | { |
323 | struct pldmfw_record *record; |
324 | size_t measured_length; |
325 | int err; |
326 | const u8 *bitmap_ptr; |
327 | u16 record_len; |
328 | int i; |
329 | |
330 | /* Make a copy and insert it into the record list */ |
331 | record = kzalloc(sizeof(*record), GFP_KERNEL); |
332 | if (!record) |
333 | return -ENOMEM; |
334 | |
335 | INIT_LIST_HEAD(list: &record->descs); |
336 | list_add_tail(new: &record->entry, head: &data->records); |
337 | |
338 | /* Then check that we have space and move the offset */ |
339 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*__record)); |
340 | if (err) |
341 | return err; |
342 | |
343 | record_len = get_unaligned_le16(p: &__record->record_len); |
344 | record->package_data_len = get_unaligned_le16(p: &__record->package_data_len); |
345 | record->version_len = __record->version_len; |
346 | record->version_type = __record->version_type; |
347 | |
348 | bitmap_ptr = data->fw->data + data->offset; |
349 | |
350 | /* check that we have space for the component bitmap length */ |
351 | err = pldm_move_fw_offset(data, bytes_to_move: data->bitmap_size); |
352 | if (err) |
353 | return err; |
354 | |
355 | record->component_bitmap_len = data->component_bitmap_len; |
356 | record->component_bitmap = bitmap_zalloc(nbits: record->component_bitmap_len, |
357 | GFP_KERNEL); |
358 | if (!record->component_bitmap) |
359 | return -ENOMEM; |
360 | |
361 | for (i = 0; i < data->bitmap_size; i++) |
362 | bitmap_set_value8(record->component_bitmap, bitmap_ptr[i], i * 8); |
363 | |
364 | record->version_string = data->fw->data + data->offset; |
365 | |
366 | err = pldm_move_fw_offset(data, bytes_to_move: record->version_len); |
367 | if (err) |
368 | return err; |
369 | |
370 | /* Scan through the descriptor TLVs and find the end */ |
371 | err = pldm_parse_desc_tlvs(data, record, desc_count: __record->descriptor_count); |
372 | if (err) |
373 | return err; |
374 | |
375 | record->package_data = data->fw->data + data->offset; |
376 | |
377 | err = pldm_move_fw_offset(data, bytes_to_move: record->package_data_len); |
378 | if (err) |
379 | return err; |
380 | |
381 | measured_length = data->offset - ((const u8 *)__record - data->fw->data); |
382 | if (measured_length != record_len) { |
383 | dev_dbg(data->context->dev, "Unexpected record length. Measured record length is %zu bytes, expected length is %u bytes\n", |
384 | measured_length, record_len); |
385 | return -EFAULT; |
386 | } |
387 | |
388 | return 0; |
389 | } |
390 | |
391 | /** |
392 | * pldm_parse_records - Locate the start of the component area |
393 | * @data: pointer to private data |
394 | * |
395 | * Extract the record count, and loop through each record, searching for the |
396 | * component area. |
397 | * |
398 | * Returns: zero on success, or a negative error code on failure. |
399 | */ |
400 | static int pldm_parse_records(struct pldmfw_priv *data) |
401 | { |
402 | const struct __pldmfw_component_area *component_area; |
403 | const struct __pldmfw_record_info *record; |
404 | int err; |
405 | u8 i; |
406 | |
407 | pldm_for_each_record(i, record, data->record_start, data->record_count) { |
408 | err = pldm_parse_one_record(data, record: record); |
409 | if (err) |
410 | return err; |
411 | } |
412 | |
413 | /* Extract a pointer to the component area, which just follows the |
414 | * PLDM device record data. |
415 | */ |
416 | component_area = (const struct __pldmfw_component_area *)(data->fw->data + data->offset); |
417 | |
418 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*component_area)); |
419 | if (err) |
420 | return err; |
421 | |
422 | data->component_count = |
423 | get_unaligned_le16(p: &component_area->component_image_count); |
424 | data->component_start = component_area->components; |
425 | |
426 | return 0; |
427 | } |
428 | |
429 | /** |
430 | * pldm_parse_components - Locate the CRC header checksum |
431 | * @data: pointer to private data |
432 | * |
433 | * Extract the component count, and find the pointer to the component area. |
434 | * Scan through each component searching for the end, which should point to |
435 | * the package header checksum. |
436 | * |
437 | * Extract the package header CRC and save it for verification. |
438 | * |
439 | * Returns: zero on success, or a negative error code on failure. |
440 | */ |
441 | static int pldm_parse_components(struct pldmfw_priv *data) |
442 | { |
443 | const struct __pldmfw_component_info *__component; |
444 | struct device *dev = data->context->dev; |
445 | const u8 *header_crc_ptr; |
446 | int err; |
447 | u8 i; |
448 | |
449 | pldm_for_each_component(i, __component, data->component_start, data->component_count) { |
450 | struct pldmfw_component *component; |
451 | u32 offset, size; |
452 | |
453 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(*__component)); |
454 | if (err) |
455 | return err; |
456 | |
457 | err = pldm_move_fw_offset(data, bytes_to_move: __component->version_len); |
458 | if (err) |
459 | return err; |
460 | |
461 | offset = get_unaligned_le32(p: &__component->location_offset); |
462 | size = get_unaligned_le32(p: &__component->size); |
463 | |
464 | err = pldm_check_fw_space(data, offset, length: size); |
465 | if (err) |
466 | return err; |
467 | |
468 | component = kzalloc(sizeof(*component), GFP_KERNEL); |
469 | if (!component) |
470 | return -ENOMEM; |
471 | |
472 | component->index = i; |
473 | component->classification = get_unaligned_le16(p: &__component->classification); |
474 | component->identifier = get_unaligned_le16(p: &__component->identifier); |
475 | component->comparison_stamp = get_unaligned_le32(p: &__component->comparison_stamp); |
476 | component->options = get_unaligned_le16(p: &__component->options); |
477 | component->activation_method = get_unaligned_le16(p: &__component->activation_method); |
478 | component->version_type = __component->version_type; |
479 | component->version_len = __component->version_len; |
480 | component->version_string = __component->version_string; |
481 | component->component_data = data->fw->data + offset; |
482 | component->component_size = size; |
483 | |
484 | if (data->context->mode == PLDMFW_UPDATE_MODE_SINGLE_COMPONENT && |
485 | data->context->component_identifier != component->identifier) |
486 | continue; |
487 | |
488 | list_add_tail(new: &component->entry, head: &data->components); |
489 | } |
490 | |
491 | if (data->context->mode == PLDMFW_UPDATE_MODE_SINGLE_COMPONENT && |
492 | list_empty(head: &data->components)) |
493 | return -ENOENT; |
494 | |
495 | header_crc_ptr = data->fw->data + data->offset; |
496 | |
497 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(data->header_crc)); |
498 | if (err) |
499 | return err; |
500 | |
501 | /* Make sure that we reached the expected offset */ |
502 | if (data->offset != data->total_header_size) { |
503 | dev_dbg(dev, "Invalid firmware header size. Expected %u but got %zu\n", |
504 | data->total_header_size, data->offset); |
505 | return -EFAULT; |
506 | } |
507 | |
508 | data->header_crc = get_unaligned_le32(p: header_crc_ptr); |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | /** |
514 | * pldm_verify_header_crc - Verify that the CRC in the header matches |
515 | * @data: pointer to private data |
516 | * |
517 | * Calculates the 32-bit CRC using the standard IEEE 802.3 CRC polynomial and |
518 | * compares it to the value stored in the header. |
519 | * |
520 | * Returns: zero on success if the CRC matches, or -EBADMSG on an invalid CRC. |
521 | */ |
522 | static int pldm_verify_header_crc(struct pldmfw_priv *data) |
523 | { |
524 | struct device *dev = data->context->dev; |
525 | u32 calculated_crc; |
526 | size_t length; |
527 | |
528 | /* Calculate the 32-bit CRC of the header header contents up to but |
529 | * not including the checksum. Note that the Linux crc32_le function |
530 | * does not perform an expected final XOR. |
531 | */ |
532 | length = data->offset - sizeof(data->header_crc); |
533 | calculated_crc = crc32_le(crc: ~0, p: data->fw->data, len: length) ^ ~0; |
534 | |
535 | if (calculated_crc != data->header_crc) { |
536 | dev_dbg(dev, "Invalid CRC in firmware header. Got 0x%08x but expected 0x%08x\n", |
537 | calculated_crc, data->header_crc); |
538 | return -EBADMSG; |
539 | } |
540 | |
541 | return 0; |
542 | } |
543 | |
544 | /** |
545 | * pldmfw_free_priv - Free memory allocated while parsing the PLDM image |
546 | * @data: pointer to the PLDM data structure |
547 | * |
548 | * Loops through and clears all allocated memory associated with each |
549 | * allocated descriptor, record, and component. |
550 | */ |
551 | static void pldmfw_free_priv(struct pldmfw_priv *data) |
552 | { |
553 | struct pldmfw_component *component, *c_safe; |
554 | struct pldmfw_record *record, *r_safe; |
555 | struct pldmfw_desc_tlv *desc, *d_safe; |
556 | |
557 | list_for_each_entry_safe(component, c_safe, &data->components, entry) { |
558 | list_del(entry: &component->entry); |
559 | kfree(objp: component); |
560 | } |
561 | |
562 | list_for_each_entry_safe(record, r_safe, &data->records, entry) { |
563 | list_for_each_entry_safe(desc, d_safe, &record->descs, entry) { |
564 | list_del(entry: &desc->entry); |
565 | kfree(objp: desc); |
566 | } |
567 | |
568 | if (record->component_bitmap) { |
569 | bitmap_free(bitmap: record->component_bitmap); |
570 | record->component_bitmap = NULL; |
571 | } |
572 | |
573 | list_del(entry: &record->entry); |
574 | kfree(objp: record); |
575 | } |
576 | } |
577 | |
578 | /** |
579 | * pldm_parse_image - parse and extract details from PLDM image |
580 | * @data: pointer to private data |
581 | * |
582 | * Verify that the firmware file contains valid data for a PLDM firmware |
583 | * file. Extract useful pointers and data from the firmware file and store |
584 | * them in the data structure. |
585 | * |
586 | * The PLDM firmware file format is defined in DMTF DSP0267 1.0.0. Care |
587 | * should be taken to use get_unaligned_le* when accessing data from the |
588 | * pointers in data. |
589 | * |
590 | * Returns: zero on success, or a negative error code on failure. |
591 | */ |
592 | static int pldm_parse_image(struct pldmfw_priv *data) |
593 | { |
594 | int err; |
595 | |
596 | if (WARN_ON(!(data->context->dev && data->fw->data && data->fw->size))) |
597 | return -EINVAL; |
598 | |
599 | err = pldm_parse_header(data); |
600 | if (err) |
601 | return err; |
602 | |
603 | err = pldm_parse_records(data); |
604 | if (err) |
605 | return err; |
606 | |
607 | err = pldm_parse_components(data); |
608 | if (err) |
609 | return err; |
610 | |
611 | return pldm_verify_header_crc(data); |
612 | } |
613 | |
614 | /* these are u32 so that we can store PCI_ANY_ID */ |
615 | struct pldm_pci_record_id { |
616 | int vendor; |
617 | int device; |
618 | int subsystem_vendor; |
619 | int subsystem_device; |
620 | }; |
621 | |
622 | /** |
623 | * pldmfw_op_pci_match_record - Check if a PCI device matches the record |
624 | * @context: PLDM fw update structure |
625 | * @record: list of records extracted from the PLDM image |
626 | * |
627 | * Determine of the PCI device associated with this device matches the record |
628 | * data provided. |
629 | * |
630 | * Searches the descriptor TLVs and extracts the relevant descriptor data into |
631 | * a pldm_pci_record_id. This is then compared against the PCI device ID |
632 | * information. |
633 | * |
634 | * Returns: true if the device matches the record, false otherwise. |
635 | */ |
636 | bool pldmfw_op_pci_match_record(struct pldmfw *context, struct pldmfw_record *record) |
637 | { |
638 | struct pci_dev *pdev = to_pci_dev(context->dev); |
639 | struct pldm_pci_record_id id = { |
640 | .vendor = PCI_ANY_ID, |
641 | .device = PCI_ANY_ID, |
642 | .subsystem_vendor = PCI_ANY_ID, |
643 | .subsystem_device = PCI_ANY_ID, |
644 | }; |
645 | struct pldmfw_desc_tlv *desc; |
646 | |
647 | list_for_each_entry(desc, &record->descs, entry) { |
648 | u16 value; |
649 | int *ptr; |
650 | |
651 | switch (desc->type) { |
652 | case PLDM_DESC_ID_PCI_VENDOR_ID: |
653 | ptr = &id.vendor; |
654 | break; |
655 | case PLDM_DESC_ID_PCI_DEVICE_ID: |
656 | ptr = &id.device; |
657 | break; |
658 | case PLDM_DESC_ID_PCI_SUBVENDOR_ID: |
659 | ptr = &id.subsystem_vendor; |
660 | break; |
661 | case PLDM_DESC_ID_PCI_SUBDEV_ID: |
662 | ptr = &id.subsystem_device; |
663 | break; |
664 | default: |
665 | /* Skip unrelated TLVs */ |
666 | continue; |
667 | } |
668 | |
669 | value = get_unaligned_le16(p: desc->data); |
670 | /* A value of zero for one of the descriptors is sometimes |
671 | * used when the record should ignore this field when matching |
672 | * device. For example if the record applies to any subsystem |
673 | * device or vendor. |
674 | */ |
675 | if (value) |
676 | *ptr = (int)value; |
677 | else |
678 | *ptr = PCI_ANY_ID; |
679 | } |
680 | |
681 | if ((id.vendor == PCI_ANY_ID || id.vendor == pdev->vendor) && |
682 | (id.device == PCI_ANY_ID || id.device == pdev->device) && |
683 | (id.subsystem_vendor == PCI_ANY_ID || id.subsystem_vendor == pdev->subsystem_vendor) && |
684 | (id.subsystem_device == PCI_ANY_ID || id.subsystem_device == pdev->subsystem_device)) |
685 | return true; |
686 | else |
687 | return false; |
688 | } |
689 | EXPORT_SYMBOL(pldmfw_op_pci_match_record); |
690 | |
691 | /** |
692 | * pldm_find_matching_record - Find the first matching PLDM record |
693 | * @data: pointer to private data |
694 | * |
695 | * Search through PLDM records and find the first matching entry. It is |
696 | * expected that only one entry matches. |
697 | * |
698 | * Store a pointer to the matching record, if found. |
699 | * |
700 | * Returns: zero on success, or -ENOENT if no matching record is found. |
701 | */ |
702 | static int pldm_find_matching_record(struct pldmfw_priv *data) |
703 | { |
704 | struct pldmfw_record *record; |
705 | |
706 | list_for_each_entry(record, &data->records, entry) { |
707 | if (data->context->ops->match_record(data->context, record)) { |
708 | data->matching_record = record; |
709 | return 0; |
710 | } |
711 | } |
712 | |
713 | return -ENOENT; |
714 | } |
715 | |
716 | /** |
717 | * pldm_send_package_data - Send firmware the package data for the record |
718 | * @data: pointer to private data |
719 | * |
720 | * Send the package data associated with the matching record to the firmware, |
721 | * using the send_pkg_data operation. |
722 | * |
723 | * Returns: zero on success, or a negative error code on failure. |
724 | */ |
725 | static int |
726 | pldm_send_package_data(struct pldmfw_priv *data) |
727 | { |
728 | struct pldmfw_record *record = data->matching_record; |
729 | const struct pldmfw_ops *ops = data->context->ops; |
730 | |
731 | if (!ops->send_package_data) |
732 | return 0; |
733 | |
734 | return ops->send_package_data(data->context, record->package_data, |
735 | record->package_data_len); |
736 | } |
737 | |
738 | /** |
739 | * pldm_send_component_tables - Send component table information to firmware |
740 | * @data: pointer to private data |
741 | * |
742 | * Loop over each component, sending the applicable components to the firmware |
743 | * via the send_component_table operation. |
744 | * |
745 | * Returns: zero on success, or a negative error code on failure. |
746 | */ |
747 | static int |
748 | pldm_send_component_tables(struct pldmfw_priv *data) |
749 | { |
750 | unsigned long *bitmap = data->matching_record->component_bitmap; |
751 | struct pldmfw_component *component; |
752 | int err; |
753 | |
754 | list_for_each_entry(component, &data->components, entry) { |
755 | u8 index = component->index, transfer_flag = 0; |
756 | |
757 | /* Skip components which are not intended for this device */ |
758 | if (!test_bit(index, bitmap)) |
759 | continue; |
760 | |
761 | if (!data->context->ops->send_component_table) |
762 | continue; |
763 | |
764 | /* determine whether this is the start, middle, end, or both |
765 | * the start and end of the component tables |
766 | */ |
767 | if (index == find_first_bit(addr: bitmap, size: data->component_bitmap_len)) |
768 | transfer_flag |= PLDM_TRANSFER_FLAG_START; |
769 | if (index == find_last_bit(addr: bitmap, size: data->component_bitmap_len)) |
770 | transfer_flag |= PLDM_TRANSFER_FLAG_END; |
771 | if (!transfer_flag) |
772 | transfer_flag = PLDM_TRANSFER_FLAG_MIDDLE; |
773 | |
774 | err = data->context->ops->send_component_table(data->context, |
775 | component, |
776 | transfer_flag); |
777 | if (err) |
778 | return err; |
779 | } |
780 | |
781 | return 0; |
782 | } |
783 | |
784 | /** |
785 | * pldm_flash_components - Program each component to device flash |
786 | * @data: pointer to private data |
787 | * |
788 | * Loop through each component that is active for the matching device record, |
789 | * and send it to the device driver for flashing. |
790 | * |
791 | * Returns: zero on success, or a negative error code on failure. |
792 | */ |
793 | static int pldm_flash_components(struct pldmfw_priv *data) |
794 | { |
795 | unsigned long *bitmap = data->matching_record->component_bitmap; |
796 | struct pldmfw_component *component; |
797 | int err; |
798 | |
799 | list_for_each_entry(component, &data->components, entry) { |
800 | u8 index = component->index; |
801 | |
802 | /* Skip components which are not intended for this device */ |
803 | if (!test_bit(index, bitmap)) |
804 | continue; |
805 | |
806 | err = data->context->ops->flash_component(data->context, component); |
807 | if (err) |
808 | return err; |
809 | } |
810 | |
811 | return 0; |
812 | } |
813 | |
814 | /** |
815 | * pldm_finalize_update - Finalize the device flash update |
816 | * @data: pointer to private data |
817 | * |
818 | * Tell the device driver to perform any remaining logic to complete the |
819 | * device update. |
820 | * |
821 | * Returns: zero on success, or a PLFM_FWU error indicating the reason for |
822 | * failure. |
823 | */ |
824 | static int pldm_finalize_update(struct pldmfw_priv *data) |
825 | { |
826 | if (data->context->ops->finalize_update) |
827 | return data->context->ops->finalize_update(data->context); |
828 | |
829 | return 0; |
830 | } |
831 | |
832 | /** |
833 | * pldmfw_flash_image - Write a PLDM-formatted firmware image to the device |
834 | * @context: ops and data for firmware update |
835 | * @fw: firmware object pointing to the relevant firmware file to program |
836 | * |
837 | * Parse the data for a given firmware file, verifying that it is a valid PLDM |
838 | * formatted image that matches this device. |
839 | * |
840 | * Extract the device record Package Data and Component Tables and send them |
841 | * to the device firmware. Extract and write the flash data for each of the |
842 | * components indicated in the firmware file. |
843 | * |
844 | * Returns: zero on success, or a negative error code on failure. |
845 | */ |
846 | int pldmfw_flash_image(struct pldmfw *context, const struct firmware *fw) |
847 | { |
848 | struct pldmfw_priv *data; |
849 | int err; |
850 | |
851 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
852 | if (!data) |
853 | return -ENOMEM; |
854 | |
855 | INIT_LIST_HEAD(list: &data->records); |
856 | INIT_LIST_HEAD(list: &data->components); |
857 | |
858 | data->fw = fw; |
859 | data->context = context; |
860 | |
861 | err = pldm_parse_image(data); |
862 | if (err) |
863 | goto out_release_data; |
864 | |
865 | err = pldm_find_matching_record(data); |
866 | if (err) |
867 | goto out_release_data; |
868 | |
869 | err = pldm_send_package_data(data); |
870 | if (err) |
871 | goto out_release_data; |
872 | |
873 | err = pldm_send_component_tables(data); |
874 | if (err) |
875 | goto out_release_data; |
876 | |
877 | err = pldm_flash_components(data); |
878 | if (err) |
879 | goto out_release_data; |
880 | |
881 | err = pldm_finalize_update(data); |
882 | |
883 | out_release_data: |
884 | pldmfw_free_priv(data); |
885 | kfree(objp: data); |
886 | |
887 | return err; |
888 | } |
889 | EXPORT_SYMBOL(pldmfw_flash_image); |
890 | |
891 | MODULE_AUTHOR("Jacob Keller <jacob.e.keller@intel.com>"); |
892 | MODULE_DESCRIPTION("PLDM firmware flash update library"); |
893 |
Definitions
- pldmfw_priv
- pldm_check_fw_space
- pldm_move_fw_offset
- pldm_parse_header
- pldm_check_desc_tlv_len
- pldm_parse_desc_tlvs
- pldm_parse_one_record
- pldm_parse_records
- pldm_parse_components
- pldm_verify_header_crc
- pldmfw_free_priv
- pldm_parse_image
- pldm_pci_record_id
- pldmfw_op_pci_match_record
- pldm_find_matching_record
- pldm_send_package_data
- pldm_send_component_tables
- pldm_flash_components
- pldm_finalize_update
Improve your Profiling and Debugging skills
Find out more