162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2018-2019, Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <asm/unaligned.h> 562306a36Sopenharmony_ci#include <linux/crc32.h> 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/firmware.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/pldmfw.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/uuid.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "pldmfw_private.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Internal structure used to store details about the PLDM image file as it is 1862306a36Sopenharmony_ci * being validated and processed. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistruct pldmfw_priv { 2162306a36Sopenharmony_ci struct pldmfw *context; 2262306a36Sopenharmony_ci const struct firmware *fw; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* current offset of firmware image */ 2562306a36Sopenharmony_ci size_t offset; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci struct list_head records; 2862306a36Sopenharmony_ci struct list_head components; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* PLDM Firmware Package Header */ 3162306a36Sopenharmony_ci const struct __pldm_header *header; 3262306a36Sopenharmony_ci u16 total_header_size; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* length of the component bitmap */ 3562306a36Sopenharmony_ci u16 component_bitmap_len; 3662306a36Sopenharmony_ci u16 bitmap_size; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* Start of the component image information */ 3962306a36Sopenharmony_ci u16 component_count; 4062306a36Sopenharmony_ci const u8 *component_start; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Start pf the firmware device id records */ 4362306a36Sopenharmony_ci const u8 *record_start; 4462306a36Sopenharmony_ci u8 record_count; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* The CRC at the end of the package header */ 4762306a36Sopenharmony_ci u32 header_crc; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci struct pldmfw_record *matching_record; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * pldm_check_fw_space - Verify that the firmware image has space left 5462306a36Sopenharmony_ci * @data: pointer to private data 5562306a36Sopenharmony_ci * @offset: offset to start from 5662306a36Sopenharmony_ci * @length: length to check for 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Verify that the firmware data can hold a chunk of bytes with the specified 5962306a36Sopenharmony_ci * offset and length. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Returns: zero on success, or -EFAULT if the image does not have enough 6262306a36Sopenharmony_ci * space left to fit the expected length. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int 6562306a36Sopenharmony_cipldm_check_fw_space(struct pldmfw_priv *data, size_t offset, size_t length) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci size_t expected_size = offset + length; 6862306a36Sopenharmony_ci struct device *dev = data->context->dev; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (data->fw->size < expected_size) { 7162306a36Sopenharmony_ci dev_dbg(dev, "Firmware file size smaller than expected. Got %zu bytes, needed %zu bytes\n", 7262306a36Sopenharmony_ci data->fw->size, expected_size); 7362306a36Sopenharmony_ci return -EFAULT; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * pldm_move_fw_offset - Move the current firmware offset forward 8162306a36Sopenharmony_ci * @data: pointer to private data 8262306a36Sopenharmony_ci * @bytes_to_move: number of bytes to move the offset forward by 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Check that there is enough space past the current offset, and then move the 8562306a36Sopenharmony_ci * offset forward by this amount. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Returns: zero on success, or -EFAULT if the image is too small to fit the 8862306a36Sopenharmony_ci * expected length. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic int 9162306a36Sopenharmony_cipldm_move_fw_offset(struct pldmfw_priv *data, size_t bytes_to_move) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci err = pldm_check_fw_space(data, data->offset, bytes_to_move); 9662306a36Sopenharmony_ci if (err) 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci data->offset += bytes_to_move; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * pldm_parse_header - Validate and extract details about the PLDM header 10662306a36Sopenharmony_ci * @data: pointer to private data 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Performs initial basic verification of the PLDM image, up to the first 10962306a36Sopenharmony_ci * firmware record. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * This includes the following checks and extractions 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * * Verify that the UUID at the start of the header matches the expected 11462306a36Sopenharmony_ci * value as defined in the DSP0267 PLDM specification 11562306a36Sopenharmony_ci * * Check that the revision is 0x01 11662306a36Sopenharmony_ci * * Extract the total header_size and verify that the image is large enough 11762306a36Sopenharmony_ci * to contain at least the length of this header 11862306a36Sopenharmony_ci * * Extract the size of the component bitmap length 11962306a36Sopenharmony_ci * * Extract a pointer to the start of the record area 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic int pldm_parse_header(struct pldmfw_priv *data) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci const struct __pldmfw_record_area *record_area; 12662306a36Sopenharmony_ci struct device *dev = data->context->dev; 12762306a36Sopenharmony_ci const struct __pldm_header *header; 12862306a36Sopenharmony_ci size_t header_size; 12962306a36Sopenharmony_ci int err; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*header)); 13262306a36Sopenharmony_ci if (err) 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci header = (const struct __pldm_header *)data->fw->data; 13662306a36Sopenharmony_ci data->header = header; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (!uuid_equal(&header->id, &pldm_firmware_header_id)) { 13962306a36Sopenharmony_ci dev_dbg(dev, "Invalid package header identifier. Expected UUID %pUB, but got %pUB\n", 14062306a36Sopenharmony_ci &pldm_firmware_header_id, &header->id); 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (header->revision != PACKAGE_HEADER_FORMAT_REVISION) { 14562306a36Sopenharmony_ci dev_dbg(dev, "Invalid package header revision. Expected revision %u but got %u\n", 14662306a36Sopenharmony_ci PACKAGE_HEADER_FORMAT_REVISION, header->revision); 14762306a36Sopenharmony_ci return -EOPNOTSUPP; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci data->total_header_size = get_unaligned_le16(&header->size); 15162306a36Sopenharmony_ci header_size = data->total_header_size - sizeof(*header); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci err = pldm_check_fw_space(data, data->offset, header_size); 15462306a36Sopenharmony_ci if (err) 15562306a36Sopenharmony_ci return err; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci data->component_bitmap_len = 15862306a36Sopenharmony_ci get_unaligned_le16(&header->component_bitmap_len); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (data->component_bitmap_len % 8 != 0) { 16162306a36Sopenharmony_ci dev_dbg(dev, "Invalid component bitmap length. The length is %u, which is not a multiple of 8\n", 16262306a36Sopenharmony_ci data->component_bitmap_len); 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci data->bitmap_size = data->component_bitmap_len / 8; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci err = pldm_move_fw_offset(data, header->version_len); 16962306a36Sopenharmony_ci if (err) 17062306a36Sopenharmony_ci return err; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* extract a pointer to the record area, which just follows the main 17362306a36Sopenharmony_ci * PLDM header data. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci record_area = (const struct __pldmfw_record_area *)(data->fw->data + 17662306a36Sopenharmony_ci data->offset); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*record_area)); 17962306a36Sopenharmony_ci if (err) 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci data->record_count = record_area->record_count; 18362306a36Sopenharmony_ci data->record_start = record_area->records; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * pldm_check_desc_tlv_len - Check that the length matches expectation 19062306a36Sopenharmony_ci * @data: pointer to image details 19162306a36Sopenharmony_ci * @type: the descriptor type 19262306a36Sopenharmony_ci * @size: the length from the descriptor header 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * If the descriptor type is one of the documented descriptor types according 19562306a36Sopenharmony_ci * to the standard, verify that the provided length matches. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * If the type is not recognized or is VENDOR_DEFINED, return zero. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Returns: zero on success, or -EINVAL if the specified size of a standard 20062306a36Sopenharmony_ci * TLV does not match the expected value defined for that TLV. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic int 20362306a36Sopenharmony_cipldm_check_desc_tlv_len(struct pldmfw_priv *data, u16 type, u16 size) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct device *dev = data->context->dev; 20662306a36Sopenharmony_ci u16 expected_size; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci switch (type) { 20962306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_VENDOR_ID: 21062306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_DEVICE_ID: 21162306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_SUBVENDOR_ID: 21262306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_SUBDEV_ID: 21362306a36Sopenharmony_ci expected_size = 2; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_REVISION_ID: 21662306a36Sopenharmony_ci expected_size = 1; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case PLDM_DESC_ID_PNP_VENDOR_ID: 21962306a36Sopenharmony_ci expected_size = 3; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case PLDM_DESC_ID_IANA_ENTERPRISE_ID: 22262306a36Sopenharmony_ci case PLDM_DESC_ID_ACPI_VENDOR_ID: 22362306a36Sopenharmony_ci case PLDM_DESC_ID_PNP_PRODUCT_ID: 22462306a36Sopenharmony_ci case PLDM_DESC_ID_ACPI_PRODUCT_ID: 22562306a36Sopenharmony_ci expected_size = 4; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case PLDM_DESC_ID_UUID: 22862306a36Sopenharmony_ci expected_size = 16; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case PLDM_DESC_ID_VENDOR_DEFINED: 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci default: 23362306a36Sopenharmony_ci /* Do not report an error on an unexpected TLV */ 23462306a36Sopenharmony_ci dev_dbg(dev, "Found unrecognized TLV type 0x%04x\n", type); 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (size != expected_size) { 23962306a36Sopenharmony_ci dev_dbg(dev, "Found TLV type 0x%04x with unexpected length. Got %u bytes, but expected %u bytes\n", 24062306a36Sopenharmony_ci type, size, expected_size); 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * pldm_parse_desc_tlvs - Check and skip past a number of TLVs 24962306a36Sopenharmony_ci * @data: pointer to private data 25062306a36Sopenharmony_ci * @record: pointer to the record this TLV belongs too 25162306a36Sopenharmony_ci * @desc_count: descriptor count 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * From the current offset, read and extract the descriptor TLVs, updating the 25462306a36Sopenharmony_ci * current offset each time. 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic int 25962306a36Sopenharmony_cipldm_parse_desc_tlvs(struct pldmfw_priv *data, struct pldmfw_record *record, u8 desc_count) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci const struct __pldmfw_desc_tlv *__desc; 26262306a36Sopenharmony_ci const u8 *desc_start; 26362306a36Sopenharmony_ci u8 i; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci desc_start = data->fw->data + data->offset; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci pldm_for_each_desc_tlv(i, __desc, desc_start, desc_count) { 26862306a36Sopenharmony_ci struct pldmfw_desc_tlv *desc; 26962306a36Sopenharmony_ci int err; 27062306a36Sopenharmony_ci u16 type, size; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*__desc)); 27362306a36Sopenharmony_ci if (err) 27462306a36Sopenharmony_ci return err; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci type = get_unaligned_le16(&__desc->type); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* According to DSP0267, this only includes the data field */ 27962306a36Sopenharmony_ci size = get_unaligned_le16(&__desc->size); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci err = pldm_check_desc_tlv_len(data, type, size); 28262306a36Sopenharmony_ci if (err) 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* check that we have space and move the offset forward */ 28662306a36Sopenharmony_ci err = pldm_move_fw_offset(data, size); 28762306a36Sopenharmony_ci if (err) 28862306a36Sopenharmony_ci return err; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 29162306a36Sopenharmony_ci if (!desc) 29262306a36Sopenharmony_ci return -ENOMEM; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci desc->type = type; 29562306a36Sopenharmony_ci desc->size = size; 29662306a36Sopenharmony_ci desc->data = __desc->data; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci list_add_tail(&desc->entry, &record->descs); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/** 30562306a36Sopenharmony_ci * pldm_parse_one_record - Verify size of one PLDM record 30662306a36Sopenharmony_ci * @data: pointer to image details 30762306a36Sopenharmony_ci * @__record: pointer to the record to check 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * This function checks that the record size does not exceed either the size 31062306a36Sopenharmony_ci * of the firmware file or the total length specified in the header section. 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * It also verifies that the recorded length of the start of the record 31362306a36Sopenharmony_ci * matches the size calculated by adding the static structure length, the 31462306a36Sopenharmony_ci * component bitmap length, the version string length, the length of all 31562306a36Sopenharmony_ci * descriptor TLVs, and the length of the package data. 31662306a36Sopenharmony_ci * 31762306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_cistatic int 32062306a36Sopenharmony_cipldm_parse_one_record(struct pldmfw_priv *data, 32162306a36Sopenharmony_ci const struct __pldmfw_record_info *__record) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct pldmfw_record *record; 32462306a36Sopenharmony_ci size_t measured_length; 32562306a36Sopenharmony_ci int err; 32662306a36Sopenharmony_ci const u8 *bitmap_ptr; 32762306a36Sopenharmony_ci u16 record_len; 32862306a36Sopenharmony_ci int i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Make a copy and insert it into the record list */ 33162306a36Sopenharmony_ci record = kzalloc(sizeof(*record), GFP_KERNEL); 33262306a36Sopenharmony_ci if (!record) 33362306a36Sopenharmony_ci return -ENOMEM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci INIT_LIST_HEAD(&record->descs); 33662306a36Sopenharmony_ci list_add_tail(&record->entry, &data->records); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Then check that we have space and move the offset */ 33962306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*__record)); 34062306a36Sopenharmony_ci if (err) 34162306a36Sopenharmony_ci return err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci record_len = get_unaligned_le16(&__record->record_len); 34462306a36Sopenharmony_ci record->package_data_len = get_unaligned_le16(&__record->package_data_len); 34562306a36Sopenharmony_ci record->version_len = __record->version_len; 34662306a36Sopenharmony_ci record->version_type = __record->version_type; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci bitmap_ptr = data->fw->data + data->offset; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* check that we have space for the component bitmap length */ 35162306a36Sopenharmony_ci err = pldm_move_fw_offset(data, data->bitmap_size); 35262306a36Sopenharmony_ci if (err) 35362306a36Sopenharmony_ci return err; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci record->component_bitmap_len = data->component_bitmap_len; 35662306a36Sopenharmony_ci record->component_bitmap = bitmap_zalloc(record->component_bitmap_len, 35762306a36Sopenharmony_ci GFP_KERNEL); 35862306a36Sopenharmony_ci if (!record->component_bitmap) 35962306a36Sopenharmony_ci return -ENOMEM; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci for (i = 0; i < data->bitmap_size; i++) 36262306a36Sopenharmony_ci bitmap_set_value8(record->component_bitmap, bitmap_ptr[i], i * 8); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci record->version_string = data->fw->data + data->offset; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci err = pldm_move_fw_offset(data, record->version_len); 36762306a36Sopenharmony_ci if (err) 36862306a36Sopenharmony_ci return err; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Scan through the descriptor TLVs and find the end */ 37162306a36Sopenharmony_ci err = pldm_parse_desc_tlvs(data, record, __record->descriptor_count); 37262306a36Sopenharmony_ci if (err) 37362306a36Sopenharmony_ci return err; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci record->package_data = data->fw->data + data->offset; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci err = pldm_move_fw_offset(data, record->package_data_len); 37862306a36Sopenharmony_ci if (err) 37962306a36Sopenharmony_ci return err; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci measured_length = data->offset - ((const u8 *)__record - data->fw->data); 38262306a36Sopenharmony_ci if (measured_length != record_len) { 38362306a36Sopenharmony_ci dev_dbg(data->context->dev, "Unexpected record length. Measured record length is %zu bytes, expected length is %u bytes\n", 38462306a36Sopenharmony_ci measured_length, record_len); 38562306a36Sopenharmony_ci return -EFAULT; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/** 39262306a36Sopenharmony_ci * pldm_parse_records - Locate the start of the component area 39362306a36Sopenharmony_ci * @data: pointer to private data 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * Extract the record count, and loop through each record, searching for the 39662306a36Sopenharmony_ci * component area. 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic int pldm_parse_records(struct pldmfw_priv *data) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci const struct __pldmfw_component_area *component_area; 40362306a36Sopenharmony_ci const struct __pldmfw_record_info *record; 40462306a36Sopenharmony_ci int err; 40562306a36Sopenharmony_ci u8 i; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci pldm_for_each_record(i, record, data->record_start, data->record_count) { 40862306a36Sopenharmony_ci err = pldm_parse_one_record(data, record); 40962306a36Sopenharmony_ci if (err) 41062306a36Sopenharmony_ci return err; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Extract a pointer to the component area, which just follows the 41462306a36Sopenharmony_ci * PLDM device record data. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci component_area = (const struct __pldmfw_component_area *)(data->fw->data + data->offset); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*component_area)); 41962306a36Sopenharmony_ci if (err) 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci data->component_count = 42362306a36Sopenharmony_ci get_unaligned_le16(&component_area->component_image_count); 42462306a36Sopenharmony_ci data->component_start = component_area->components; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * pldm_parse_components - Locate the CRC header checksum 43162306a36Sopenharmony_ci * @data: pointer to private data 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Extract the component count, and find the pointer to the component area. 43462306a36Sopenharmony_ci * Scan through each component searching for the end, which should point to 43562306a36Sopenharmony_ci * the package header checksum. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Extract the package header CRC and save it for verification. 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic int pldm_parse_components(struct pldmfw_priv *data) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci const struct __pldmfw_component_info *__component; 44462306a36Sopenharmony_ci struct device *dev = data->context->dev; 44562306a36Sopenharmony_ci const u8 *header_crc_ptr; 44662306a36Sopenharmony_ci int err; 44762306a36Sopenharmony_ci u8 i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci pldm_for_each_component(i, __component, data->component_start, data->component_count) { 45062306a36Sopenharmony_ci struct pldmfw_component *component; 45162306a36Sopenharmony_ci u32 offset, size; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(*__component)); 45462306a36Sopenharmony_ci if (err) 45562306a36Sopenharmony_ci return err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci err = pldm_move_fw_offset(data, __component->version_len); 45862306a36Sopenharmony_ci if (err) 45962306a36Sopenharmony_ci return err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci offset = get_unaligned_le32(&__component->location_offset); 46262306a36Sopenharmony_ci size = get_unaligned_le32(&__component->size); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci err = pldm_check_fw_space(data, offset, size); 46562306a36Sopenharmony_ci if (err) 46662306a36Sopenharmony_ci return err; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci component = kzalloc(sizeof(*component), GFP_KERNEL); 46962306a36Sopenharmony_ci if (!component) 47062306a36Sopenharmony_ci return -ENOMEM; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci component->index = i; 47362306a36Sopenharmony_ci component->classification = get_unaligned_le16(&__component->classification); 47462306a36Sopenharmony_ci component->identifier = get_unaligned_le16(&__component->identifier); 47562306a36Sopenharmony_ci component->comparison_stamp = get_unaligned_le32(&__component->comparison_stamp); 47662306a36Sopenharmony_ci component->options = get_unaligned_le16(&__component->options); 47762306a36Sopenharmony_ci component->activation_method = get_unaligned_le16(&__component->activation_method); 47862306a36Sopenharmony_ci component->version_type = __component->version_type; 47962306a36Sopenharmony_ci component->version_len = __component->version_len; 48062306a36Sopenharmony_ci component->version_string = __component->version_string; 48162306a36Sopenharmony_ci component->component_data = data->fw->data + offset; 48262306a36Sopenharmony_ci component->component_size = size; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci list_add_tail(&component->entry, &data->components); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci header_crc_ptr = data->fw->data + data->offset; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci err = pldm_move_fw_offset(data, sizeof(data->header_crc)); 49062306a36Sopenharmony_ci if (err) 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Make sure that we reached the expected offset */ 49462306a36Sopenharmony_ci if (data->offset != data->total_header_size) { 49562306a36Sopenharmony_ci dev_dbg(dev, "Invalid firmware header size. Expected %u but got %zu\n", 49662306a36Sopenharmony_ci data->total_header_size, data->offset); 49762306a36Sopenharmony_ci return -EFAULT; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci data->header_crc = get_unaligned_le32(header_crc_ptr); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/** 50662306a36Sopenharmony_ci * pldm_verify_header_crc - Verify that the CRC in the header matches 50762306a36Sopenharmony_ci * @data: pointer to private data 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * Calculates the 32-bit CRC using the standard IEEE 802.3 CRC polynomial and 51062306a36Sopenharmony_ci * compares it to the value stored in the header. 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Returns: zero on success if the CRC matches, or -EBADMSG on an invalid CRC. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_cistatic int pldm_verify_header_crc(struct pldmfw_priv *data) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct device *dev = data->context->dev; 51762306a36Sopenharmony_ci u32 calculated_crc; 51862306a36Sopenharmony_ci size_t length; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Calculate the 32-bit CRC of the header header contents up to but 52162306a36Sopenharmony_ci * not including the checksum. Note that the Linux crc32_le function 52262306a36Sopenharmony_ci * does not perform an expected final XOR. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci length = data->offset - sizeof(data->header_crc); 52562306a36Sopenharmony_ci calculated_crc = crc32_le(~0, data->fw->data, length) ^ ~0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (calculated_crc != data->header_crc) { 52862306a36Sopenharmony_ci dev_dbg(dev, "Invalid CRC in firmware header. Got 0x%08x but expected 0x%08x\n", 52962306a36Sopenharmony_ci calculated_crc, data->header_crc); 53062306a36Sopenharmony_ci return -EBADMSG; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * pldmfw_free_priv - Free memory allocated while parsing the PLDM image 53862306a36Sopenharmony_ci * @data: pointer to the PLDM data structure 53962306a36Sopenharmony_ci * 54062306a36Sopenharmony_ci * Loops through and clears all allocated memory associated with each 54162306a36Sopenharmony_ci * allocated descriptor, record, and component. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_cistatic void pldmfw_free_priv(struct pldmfw_priv *data) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct pldmfw_component *component, *c_safe; 54662306a36Sopenharmony_ci struct pldmfw_record *record, *r_safe; 54762306a36Sopenharmony_ci struct pldmfw_desc_tlv *desc, *d_safe; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci list_for_each_entry_safe(component, c_safe, &data->components, entry) { 55062306a36Sopenharmony_ci list_del(&component->entry); 55162306a36Sopenharmony_ci kfree(component); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci list_for_each_entry_safe(record, r_safe, &data->records, entry) { 55562306a36Sopenharmony_ci list_for_each_entry_safe(desc, d_safe, &record->descs, entry) { 55662306a36Sopenharmony_ci list_del(&desc->entry); 55762306a36Sopenharmony_ci kfree(desc); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (record->component_bitmap) { 56162306a36Sopenharmony_ci bitmap_free(record->component_bitmap); 56262306a36Sopenharmony_ci record->component_bitmap = NULL; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci list_del(&record->entry); 56662306a36Sopenharmony_ci kfree(record); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/** 57162306a36Sopenharmony_ci * pldm_parse_image - parse and extract details from PLDM image 57262306a36Sopenharmony_ci * @data: pointer to private data 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * Verify that the firmware file contains valid data for a PLDM firmware 57562306a36Sopenharmony_ci * file. Extract useful pointers and data from the firmware file and store 57662306a36Sopenharmony_ci * them in the data structure. 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * The PLDM firmware file format is defined in DMTF DSP0267 1.0.0. Care 57962306a36Sopenharmony_ci * should be taken to use get_unaligned_le* when accessing data from the 58062306a36Sopenharmony_ci * pointers in data. 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_cistatic int pldm_parse_image(struct pldmfw_priv *data) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci int err; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (WARN_ON(!(data->context->dev && data->fw->data && data->fw->size))) 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci err = pldm_parse_header(data); 59262306a36Sopenharmony_ci if (err) 59362306a36Sopenharmony_ci return err; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci err = pldm_parse_records(data); 59662306a36Sopenharmony_ci if (err) 59762306a36Sopenharmony_ci return err; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci err = pldm_parse_components(data); 60062306a36Sopenharmony_ci if (err) 60162306a36Sopenharmony_ci return err; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return pldm_verify_header_crc(data); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* these are u32 so that we can store PCI_ANY_ID */ 60762306a36Sopenharmony_cistruct pldm_pci_record_id { 60862306a36Sopenharmony_ci int vendor; 60962306a36Sopenharmony_ci int device; 61062306a36Sopenharmony_ci int subsystem_vendor; 61162306a36Sopenharmony_ci int subsystem_device; 61262306a36Sopenharmony_ci}; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/** 61562306a36Sopenharmony_ci * pldmfw_op_pci_match_record - Check if a PCI device matches the record 61662306a36Sopenharmony_ci * @context: PLDM fw update structure 61762306a36Sopenharmony_ci * @record: list of records extracted from the PLDM image 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * Determine of the PCI device associated with this device matches the record 62062306a36Sopenharmony_ci * data provided. 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Searches the descriptor TLVs and extracts the relevant descriptor data into 62362306a36Sopenharmony_ci * a pldm_pci_record_id. This is then compared against the PCI device ID 62462306a36Sopenharmony_ci * information. 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * Returns: true if the device matches the record, false otherwise. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cibool pldmfw_op_pci_match_record(struct pldmfw *context, struct pldmfw_record *record) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(context->dev); 63162306a36Sopenharmony_ci struct pldm_pci_record_id id = { 63262306a36Sopenharmony_ci .vendor = PCI_ANY_ID, 63362306a36Sopenharmony_ci .device = PCI_ANY_ID, 63462306a36Sopenharmony_ci .subsystem_vendor = PCI_ANY_ID, 63562306a36Sopenharmony_ci .subsystem_device = PCI_ANY_ID, 63662306a36Sopenharmony_ci }; 63762306a36Sopenharmony_ci struct pldmfw_desc_tlv *desc; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci list_for_each_entry(desc, &record->descs, entry) { 64062306a36Sopenharmony_ci u16 value; 64162306a36Sopenharmony_ci int *ptr; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci switch (desc->type) { 64462306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_VENDOR_ID: 64562306a36Sopenharmony_ci ptr = &id.vendor; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_DEVICE_ID: 64862306a36Sopenharmony_ci ptr = &id.device; 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_SUBVENDOR_ID: 65162306a36Sopenharmony_ci ptr = &id.subsystem_vendor; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case PLDM_DESC_ID_PCI_SUBDEV_ID: 65462306a36Sopenharmony_ci ptr = &id.subsystem_device; 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci /* Skip unrelated TLVs */ 65862306a36Sopenharmony_ci continue; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci value = get_unaligned_le16(desc->data); 66262306a36Sopenharmony_ci /* A value of zero for one of the descriptors is sometimes 66362306a36Sopenharmony_ci * used when the record should ignore this field when matching 66462306a36Sopenharmony_ci * device. For example if the record applies to any subsystem 66562306a36Sopenharmony_ci * device or vendor. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci if (value) 66862306a36Sopenharmony_ci *ptr = (int)value; 66962306a36Sopenharmony_ci else 67062306a36Sopenharmony_ci *ptr = PCI_ANY_ID; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if ((id.vendor == PCI_ANY_ID || id.vendor == pdev->vendor) && 67462306a36Sopenharmony_ci (id.device == PCI_ANY_ID || id.device == pdev->device) && 67562306a36Sopenharmony_ci (id.subsystem_vendor == PCI_ANY_ID || id.subsystem_vendor == pdev->subsystem_vendor) && 67662306a36Sopenharmony_ci (id.subsystem_device == PCI_ANY_ID || id.subsystem_device == pdev->subsystem_device)) 67762306a36Sopenharmony_ci return true; 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci return false; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ciEXPORT_SYMBOL(pldmfw_op_pci_match_record); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci/** 68462306a36Sopenharmony_ci * pldm_find_matching_record - Find the first matching PLDM record 68562306a36Sopenharmony_ci * @data: pointer to private data 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * Search through PLDM records and find the first matching entry. It is 68862306a36Sopenharmony_ci * expected that only one entry matches. 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * Store a pointer to the matching record, if found. 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * Returns: zero on success, or -ENOENT if no matching record is found. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_cistatic int pldm_find_matching_record(struct pldmfw_priv *data) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct pldmfw_record *record; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci list_for_each_entry(record, &data->records, entry) { 69962306a36Sopenharmony_ci if (data->context->ops->match_record(data->context, record)) { 70062306a36Sopenharmony_ci data->matching_record = record; 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return -ENOENT; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci/** 70962306a36Sopenharmony_ci * pldm_send_package_data - Send firmware the package data for the record 71062306a36Sopenharmony_ci * @data: pointer to private data 71162306a36Sopenharmony_ci * 71262306a36Sopenharmony_ci * Send the package data associated with the matching record to the firmware, 71362306a36Sopenharmony_ci * using the send_pkg_data operation. 71462306a36Sopenharmony_ci * 71562306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_cistatic int 71862306a36Sopenharmony_cipldm_send_package_data(struct pldmfw_priv *data) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct pldmfw_record *record = data->matching_record; 72162306a36Sopenharmony_ci const struct pldmfw_ops *ops = data->context->ops; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return ops->send_package_data(data->context, record->package_data, 72462306a36Sopenharmony_ci record->package_data_len); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/** 72862306a36Sopenharmony_ci * pldm_send_component_tables - Send component table information to firmware 72962306a36Sopenharmony_ci * @data: pointer to private data 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * Loop over each component, sending the applicable components to the firmware 73262306a36Sopenharmony_ci * via the send_component_table operation. 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_cistatic int 73762306a36Sopenharmony_cipldm_send_component_tables(struct pldmfw_priv *data) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci unsigned long *bitmap = data->matching_record->component_bitmap; 74062306a36Sopenharmony_ci struct pldmfw_component *component; 74162306a36Sopenharmony_ci int err; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci list_for_each_entry(component, &data->components, entry) { 74462306a36Sopenharmony_ci u8 index = component->index, transfer_flag = 0; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Skip components which are not intended for this device */ 74762306a36Sopenharmony_ci if (!test_bit(index, bitmap)) 74862306a36Sopenharmony_ci continue; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* determine whether this is the start, middle, end, or both 75162306a36Sopenharmony_ci * the start and end of the component tables 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci if (index == find_first_bit(bitmap, data->component_bitmap_len)) 75462306a36Sopenharmony_ci transfer_flag |= PLDM_TRANSFER_FLAG_START; 75562306a36Sopenharmony_ci if (index == find_last_bit(bitmap, data->component_bitmap_len)) 75662306a36Sopenharmony_ci transfer_flag |= PLDM_TRANSFER_FLAG_END; 75762306a36Sopenharmony_ci if (!transfer_flag) 75862306a36Sopenharmony_ci transfer_flag = PLDM_TRANSFER_FLAG_MIDDLE; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci err = data->context->ops->send_component_table(data->context, 76162306a36Sopenharmony_ci component, 76262306a36Sopenharmony_ci transfer_flag); 76362306a36Sopenharmony_ci if (err) 76462306a36Sopenharmony_ci return err; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/** 77162306a36Sopenharmony_ci * pldm_flash_components - Program each component to device flash 77262306a36Sopenharmony_ci * @data: pointer to private data 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * Loop through each component that is active for the matching device record, 77562306a36Sopenharmony_ci * and send it to the device driver for flashing. 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic int pldm_flash_components(struct pldmfw_priv *data) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci unsigned long *bitmap = data->matching_record->component_bitmap; 78262306a36Sopenharmony_ci struct pldmfw_component *component; 78362306a36Sopenharmony_ci int err; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci list_for_each_entry(component, &data->components, entry) { 78662306a36Sopenharmony_ci u8 index = component->index; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Skip components which are not intended for this device */ 78962306a36Sopenharmony_ci if (!test_bit(index, bitmap)) 79062306a36Sopenharmony_ci continue; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci err = data->context->ops->flash_component(data->context, component); 79362306a36Sopenharmony_ci if (err) 79462306a36Sopenharmony_ci return err; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/** 80162306a36Sopenharmony_ci * pldm_finalize_update - Finalize the device flash update 80262306a36Sopenharmony_ci * @data: pointer to private data 80362306a36Sopenharmony_ci * 80462306a36Sopenharmony_ci * Tell the device driver to perform any remaining logic to complete the 80562306a36Sopenharmony_ci * device update. 80662306a36Sopenharmony_ci * 80762306a36Sopenharmony_ci * Returns: zero on success, or a PLFM_FWU error indicating the reason for 80862306a36Sopenharmony_ci * failure. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_cistatic int pldm_finalize_update(struct pldmfw_priv *data) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci if (data->context->ops->finalize_update) 81362306a36Sopenharmony_ci return data->context->ops->finalize_update(data->context); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci/** 81962306a36Sopenharmony_ci * pldmfw_flash_image - Write a PLDM-formatted firmware image to the device 82062306a36Sopenharmony_ci * @context: ops and data for firmware update 82162306a36Sopenharmony_ci * @fw: firmware object pointing to the relevant firmware file to program 82262306a36Sopenharmony_ci * 82362306a36Sopenharmony_ci * Parse the data for a given firmware file, verifying that it is a valid PLDM 82462306a36Sopenharmony_ci * formatted image that matches this device. 82562306a36Sopenharmony_ci * 82662306a36Sopenharmony_ci * Extract the device record Package Data and Component Tables and send them 82762306a36Sopenharmony_ci * to the device firmware. Extract and write the flash data for each of the 82862306a36Sopenharmony_ci * components indicated in the firmware file. 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * Returns: zero on success, or a negative error code on failure. 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_ciint pldmfw_flash_image(struct pldmfw *context, const struct firmware *fw) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct pldmfw_priv *data; 83562306a36Sopenharmony_ci int err; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 83862306a36Sopenharmony_ci if (!data) 83962306a36Sopenharmony_ci return -ENOMEM; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci INIT_LIST_HEAD(&data->records); 84262306a36Sopenharmony_ci INIT_LIST_HEAD(&data->components); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci data->fw = fw; 84562306a36Sopenharmony_ci data->context = context; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci err = pldm_parse_image(data); 84862306a36Sopenharmony_ci if (err) 84962306a36Sopenharmony_ci goto out_release_data; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci err = pldm_find_matching_record(data); 85262306a36Sopenharmony_ci if (err) 85362306a36Sopenharmony_ci goto out_release_data; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci err = pldm_send_package_data(data); 85662306a36Sopenharmony_ci if (err) 85762306a36Sopenharmony_ci goto out_release_data; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci err = pldm_send_component_tables(data); 86062306a36Sopenharmony_ci if (err) 86162306a36Sopenharmony_ci goto out_release_data; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci err = pldm_flash_components(data); 86462306a36Sopenharmony_ci if (err) 86562306a36Sopenharmony_ci goto out_release_data; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci err = pldm_finalize_update(data); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ciout_release_data: 87062306a36Sopenharmony_ci pldmfw_free_priv(data); 87162306a36Sopenharmony_ci kfree(data); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return err; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ciEXPORT_SYMBOL(pldmfw_flash_image); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciMODULE_AUTHOR("Jacob Keller <jacob.e.keller@intel.com>"); 87862306a36Sopenharmony_ciMODULE_DESCRIPTION("PLDM firmware flash update library"); 879