1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2018-2019, Intel Corporation. */ |
3 | |
4 | #include <asm/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 *; |
32 | u16 ; |
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 ; |
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 (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 *; |
128 | size_t ; |
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(size: 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(size: 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(map: record->component_bitmap, value: bitmap_ptr[i], start: 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 *; |
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(size: 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 | list_add_tail(new: &component->entry, head: &data->components); |
485 | } |
486 | |
487 | header_crc_ptr = data->fw->data + data->offset; |
488 | |
489 | err = pldm_move_fw_offset(data, bytes_to_move: sizeof(data->header_crc)); |
490 | if (err) |
491 | return err; |
492 | |
493 | /* Make sure that we reached the expected offset */ |
494 | if (data->offset != data->total_header_size) { |
495 | dev_dbg(dev, "Invalid firmware header size. Expected %u but got %zu\n" , |
496 | data->total_header_size, data->offset); |
497 | return -EFAULT; |
498 | } |
499 | |
500 | data->header_crc = get_unaligned_le32(p: header_crc_ptr); |
501 | |
502 | return 0; |
503 | } |
504 | |
505 | /** |
506 | * pldm_verify_header_crc - Verify that the CRC in the header matches |
507 | * @data: pointer to private data |
508 | * |
509 | * Calculates the 32-bit CRC using the standard IEEE 802.3 CRC polynomial and |
510 | * compares it to the value stored in the header. |
511 | * |
512 | * Returns: zero on success if the CRC matches, or -EBADMSG on an invalid CRC. |
513 | */ |
514 | static int (struct pldmfw_priv *data) |
515 | { |
516 | struct device *dev = data->context->dev; |
517 | u32 calculated_crc; |
518 | size_t length; |
519 | |
520 | /* Calculate the 32-bit CRC of the header header contents up to but |
521 | * not including the checksum. Note that the Linux crc32_le function |
522 | * does not perform an expected final XOR. |
523 | */ |
524 | length = data->offset - sizeof(data->header_crc); |
525 | calculated_crc = crc32_le(crc: ~0, p: data->fw->data, len: length) ^ ~0; |
526 | |
527 | if (calculated_crc != data->header_crc) { |
528 | dev_dbg(dev, "Invalid CRC in firmware header. Got 0x%08x but expected 0x%08x\n" , |
529 | calculated_crc, data->header_crc); |
530 | return -EBADMSG; |
531 | } |
532 | |
533 | return 0; |
534 | } |
535 | |
536 | /** |
537 | * pldmfw_free_priv - Free memory allocated while parsing the PLDM image |
538 | * @data: pointer to the PLDM data structure |
539 | * |
540 | * Loops through and clears all allocated memory associated with each |
541 | * allocated descriptor, record, and component. |
542 | */ |
543 | static void pldmfw_free_priv(struct pldmfw_priv *data) |
544 | { |
545 | struct pldmfw_component *component, *c_safe; |
546 | struct pldmfw_record *record, *r_safe; |
547 | struct pldmfw_desc_tlv *desc, *d_safe; |
548 | |
549 | list_for_each_entry_safe(component, c_safe, &data->components, entry) { |
550 | list_del(entry: &component->entry); |
551 | kfree(objp: component); |
552 | } |
553 | |
554 | list_for_each_entry_safe(record, r_safe, &data->records, entry) { |
555 | list_for_each_entry_safe(desc, d_safe, &record->descs, entry) { |
556 | list_del(entry: &desc->entry); |
557 | kfree(objp: desc); |
558 | } |
559 | |
560 | if (record->component_bitmap) { |
561 | bitmap_free(bitmap: record->component_bitmap); |
562 | record->component_bitmap = NULL; |
563 | } |
564 | |
565 | list_del(entry: &record->entry); |
566 | kfree(objp: record); |
567 | } |
568 | } |
569 | |
570 | /** |
571 | * pldm_parse_image - parse and extract details from PLDM image |
572 | * @data: pointer to private data |
573 | * |
574 | * Verify that the firmware file contains valid data for a PLDM firmware |
575 | * file. Extract useful pointers and data from the firmware file and store |
576 | * them in the data structure. |
577 | * |
578 | * The PLDM firmware file format is defined in DMTF DSP0267 1.0.0. Care |
579 | * should be taken to use get_unaligned_le* when accessing data from the |
580 | * pointers in data. |
581 | * |
582 | * Returns: zero on success, or a negative error code on failure. |
583 | */ |
584 | static int pldm_parse_image(struct pldmfw_priv *data) |
585 | { |
586 | int err; |
587 | |
588 | if (WARN_ON(!(data->context->dev && data->fw->data && data->fw->size))) |
589 | return -EINVAL; |
590 | |
591 | err = pldm_parse_header(data); |
592 | if (err) |
593 | return err; |
594 | |
595 | err = pldm_parse_records(data); |
596 | if (err) |
597 | return err; |
598 | |
599 | err = pldm_parse_components(data); |
600 | if (err) |
601 | return err; |
602 | |
603 | return pldm_verify_header_crc(data); |
604 | } |
605 | |
606 | /* these are u32 so that we can store PCI_ANY_ID */ |
607 | struct pldm_pci_record_id { |
608 | int vendor; |
609 | int device; |
610 | int subsystem_vendor; |
611 | int subsystem_device; |
612 | }; |
613 | |
614 | /** |
615 | * pldmfw_op_pci_match_record - Check if a PCI device matches the record |
616 | * @context: PLDM fw update structure |
617 | * @record: list of records extracted from the PLDM image |
618 | * |
619 | * Determine of the PCI device associated with this device matches the record |
620 | * data provided. |
621 | * |
622 | * Searches the descriptor TLVs and extracts the relevant descriptor data into |
623 | * a pldm_pci_record_id. This is then compared against the PCI device ID |
624 | * information. |
625 | * |
626 | * Returns: true if the device matches the record, false otherwise. |
627 | */ |
628 | bool pldmfw_op_pci_match_record(struct pldmfw *context, struct pldmfw_record *record) |
629 | { |
630 | struct pci_dev *pdev = to_pci_dev(context->dev); |
631 | struct pldm_pci_record_id id = { |
632 | .vendor = PCI_ANY_ID, |
633 | .device = PCI_ANY_ID, |
634 | .subsystem_vendor = PCI_ANY_ID, |
635 | .subsystem_device = PCI_ANY_ID, |
636 | }; |
637 | struct pldmfw_desc_tlv *desc; |
638 | |
639 | list_for_each_entry(desc, &record->descs, entry) { |
640 | u16 value; |
641 | int *ptr; |
642 | |
643 | switch (desc->type) { |
644 | case PLDM_DESC_ID_PCI_VENDOR_ID: |
645 | ptr = &id.vendor; |
646 | break; |
647 | case PLDM_DESC_ID_PCI_DEVICE_ID: |
648 | ptr = &id.device; |
649 | break; |
650 | case PLDM_DESC_ID_PCI_SUBVENDOR_ID: |
651 | ptr = &id.subsystem_vendor; |
652 | break; |
653 | case PLDM_DESC_ID_PCI_SUBDEV_ID: |
654 | ptr = &id.subsystem_device; |
655 | break; |
656 | default: |
657 | /* Skip unrelated TLVs */ |
658 | continue; |
659 | } |
660 | |
661 | value = get_unaligned_le16(p: desc->data); |
662 | /* A value of zero for one of the descriptors is sometimes |
663 | * used when the record should ignore this field when matching |
664 | * device. For example if the record applies to any subsystem |
665 | * device or vendor. |
666 | */ |
667 | if (value) |
668 | *ptr = (int)value; |
669 | else |
670 | *ptr = PCI_ANY_ID; |
671 | } |
672 | |
673 | if ((id.vendor == PCI_ANY_ID || id.vendor == pdev->vendor) && |
674 | (id.device == PCI_ANY_ID || id.device == pdev->device) && |
675 | (id.subsystem_vendor == PCI_ANY_ID || id.subsystem_vendor == pdev->subsystem_vendor) && |
676 | (id.subsystem_device == PCI_ANY_ID || id.subsystem_device == pdev->subsystem_device)) |
677 | return true; |
678 | else |
679 | return false; |
680 | } |
681 | EXPORT_SYMBOL(pldmfw_op_pci_match_record); |
682 | |
683 | /** |
684 | * pldm_find_matching_record - Find the first matching PLDM record |
685 | * @data: pointer to private data |
686 | * |
687 | * Search through PLDM records and find the first matching entry. It is |
688 | * expected that only one entry matches. |
689 | * |
690 | * Store a pointer to the matching record, if found. |
691 | * |
692 | * Returns: zero on success, or -ENOENT if no matching record is found. |
693 | */ |
694 | static int pldm_find_matching_record(struct pldmfw_priv *data) |
695 | { |
696 | struct pldmfw_record *record; |
697 | |
698 | list_for_each_entry(record, &data->records, entry) { |
699 | if (data->context->ops->match_record(data->context, record)) { |
700 | data->matching_record = record; |
701 | return 0; |
702 | } |
703 | } |
704 | |
705 | return -ENOENT; |
706 | } |
707 | |
708 | /** |
709 | * pldm_send_package_data - Send firmware the package data for the record |
710 | * @data: pointer to private data |
711 | * |
712 | * Send the package data associated with the matching record to the firmware, |
713 | * using the send_pkg_data operation. |
714 | * |
715 | * Returns: zero on success, or a negative error code on failure. |
716 | */ |
717 | static int |
718 | pldm_send_package_data(struct pldmfw_priv *data) |
719 | { |
720 | struct pldmfw_record *record = data->matching_record; |
721 | const struct pldmfw_ops *ops = data->context->ops; |
722 | |
723 | return ops->send_package_data(data->context, record->package_data, |
724 | record->package_data_len); |
725 | } |
726 | |
727 | /** |
728 | * pldm_send_component_tables - Send component table information to firmware |
729 | * @data: pointer to private data |
730 | * |
731 | * Loop over each component, sending the applicable components to the firmware |
732 | * via the send_component_table operation. |
733 | * |
734 | * Returns: zero on success, or a negative error code on failure. |
735 | */ |
736 | static int |
737 | pldm_send_component_tables(struct pldmfw_priv *data) |
738 | { |
739 | unsigned long *bitmap = data->matching_record->component_bitmap; |
740 | struct pldmfw_component *component; |
741 | int err; |
742 | |
743 | list_for_each_entry(component, &data->components, entry) { |
744 | u8 index = component->index, transfer_flag = 0; |
745 | |
746 | /* Skip components which are not intended for this device */ |
747 | if (!test_bit(index, bitmap)) |
748 | continue; |
749 | |
750 | /* determine whether this is the start, middle, end, or both |
751 | * the start and end of the component tables |
752 | */ |
753 | if (index == find_first_bit(addr: bitmap, size: data->component_bitmap_len)) |
754 | transfer_flag |= PLDM_TRANSFER_FLAG_START; |
755 | if (index == find_last_bit(addr: bitmap, size: data->component_bitmap_len)) |
756 | transfer_flag |= PLDM_TRANSFER_FLAG_END; |
757 | if (!transfer_flag) |
758 | transfer_flag = PLDM_TRANSFER_FLAG_MIDDLE; |
759 | |
760 | err = data->context->ops->send_component_table(data->context, |
761 | component, |
762 | transfer_flag); |
763 | if (err) |
764 | return err; |
765 | } |
766 | |
767 | return 0; |
768 | } |
769 | |
770 | /** |
771 | * pldm_flash_components - Program each component to device flash |
772 | * @data: pointer to private data |
773 | * |
774 | * Loop through each component that is active for the matching device record, |
775 | * and send it to the device driver for flashing. |
776 | * |
777 | * Returns: zero on success, or a negative error code on failure. |
778 | */ |
779 | static int pldm_flash_components(struct pldmfw_priv *data) |
780 | { |
781 | unsigned long *bitmap = data->matching_record->component_bitmap; |
782 | struct pldmfw_component *component; |
783 | int err; |
784 | |
785 | list_for_each_entry(component, &data->components, entry) { |
786 | u8 index = component->index; |
787 | |
788 | /* Skip components which are not intended for this device */ |
789 | if (!test_bit(index, bitmap)) |
790 | continue; |
791 | |
792 | err = data->context->ops->flash_component(data->context, component); |
793 | if (err) |
794 | return err; |
795 | } |
796 | |
797 | return 0; |
798 | } |
799 | |
800 | /** |
801 | * pldm_finalize_update - Finalize the device flash update |
802 | * @data: pointer to private data |
803 | * |
804 | * Tell the device driver to perform any remaining logic to complete the |
805 | * device update. |
806 | * |
807 | * Returns: zero on success, or a PLFM_FWU error indicating the reason for |
808 | * failure. |
809 | */ |
810 | static int pldm_finalize_update(struct pldmfw_priv *data) |
811 | { |
812 | if (data->context->ops->finalize_update) |
813 | return data->context->ops->finalize_update(data->context); |
814 | |
815 | return 0; |
816 | } |
817 | |
818 | /** |
819 | * pldmfw_flash_image - Write a PLDM-formatted firmware image to the device |
820 | * @context: ops and data for firmware update |
821 | * @fw: firmware object pointing to the relevant firmware file to program |
822 | * |
823 | * Parse the data for a given firmware file, verifying that it is a valid PLDM |
824 | * formatted image that matches this device. |
825 | * |
826 | * Extract the device record Package Data and Component Tables and send them |
827 | * to the device firmware. Extract and write the flash data for each of the |
828 | * components indicated in the firmware file. |
829 | * |
830 | * Returns: zero on success, or a negative error code on failure. |
831 | */ |
832 | int pldmfw_flash_image(struct pldmfw *context, const struct firmware *fw) |
833 | { |
834 | struct pldmfw_priv *data; |
835 | int err; |
836 | |
837 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
838 | if (!data) |
839 | return -ENOMEM; |
840 | |
841 | INIT_LIST_HEAD(list: &data->records); |
842 | INIT_LIST_HEAD(list: &data->components); |
843 | |
844 | data->fw = fw; |
845 | data->context = context; |
846 | |
847 | err = pldm_parse_image(data); |
848 | if (err) |
849 | goto out_release_data; |
850 | |
851 | err = pldm_find_matching_record(data); |
852 | if (err) |
853 | goto out_release_data; |
854 | |
855 | err = pldm_send_package_data(data); |
856 | if (err) |
857 | goto out_release_data; |
858 | |
859 | err = pldm_send_component_tables(data); |
860 | if (err) |
861 | goto out_release_data; |
862 | |
863 | err = pldm_flash_components(data); |
864 | if (err) |
865 | goto out_release_data; |
866 | |
867 | err = pldm_finalize_update(data); |
868 | |
869 | out_release_data: |
870 | pldmfw_free_priv(data); |
871 | kfree(objp: data); |
872 | |
873 | return err; |
874 | } |
875 | EXPORT_SYMBOL(pldmfw_flash_image); |
876 | |
877 | MODULE_AUTHOR("Jacob Keller <jacob.e.keller@intel.com>" ); |
878 | MODULE_DESCRIPTION("PLDM firmware flash update library" ); |
879 | |