/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2018-2019, Intel Corporation. */ #ifndef _PLDMFW_PRIVATE_H_ #define _PLDMFW_PRIVATE_H_ /* The following data structures define the layout of a firmware binary * following the "PLDM For Firmware Update Specification", DMTF standard * #DSP0267. * * pldmfw.c uses these structures to implement a simple engine that will parse * a fw binary file in this format and perform a firmware update for a given * device. * * Due to the variable sized data layout, alignment of fields within these * structures is not guaranteed when reading. For this reason, all multi-byte * field accesses should be done using the unaligned access macros. * Additionally, the standard specifies that multi-byte fields are in * LittleEndian format. * * The structure definitions are not made public, in order to keep direct * accesses within code that is prepared to deal with the limitation of * unaligned access. */ /* UUID for PLDM firmware packages: f018878c-cb7d-4943-9800-a02f059aca02 */ static const uuid_t pldm_firmware_header_id = UUID_INIT(0xf018878c, 0xcb7d, 0x4943, 0x98, 0x00, 0xa0, 0x2f, 0x05, 0x9a, 0xca, 0x02); /* Revision number of the PLDM header format this code supports */ #define PACKAGE_HEADER_FORMAT_REVISION 0x01 /* timestamp104 structure defined in PLDM Base specification */ #define PLDM_TIMESTAMP_SIZE 13 struct __pldm_timestamp { u8 b[PLDM_TIMESTAMP_SIZE]; } __packed __aligned(1); /* Package Header Information */ struct __pldm_header { uuid_t id; /* PackageHeaderIdentifier */ u8 revision; /* PackageHeaderFormatRevision */ __le16 size; /* PackageHeaderSize */ struct __pldm_timestamp release_date; /* PackageReleaseDateTime */ __le16 component_bitmap_len; /* ComponentBitmapBitLength */ u8 version_type; /* PackageVersionStringType */ u8 version_len; /* PackageVersionStringLength */ /* * DSP0267 also includes the following variable length fields at the * end of this structure: * * PackageVersionString, length is version_len. * * The total size of this section is * sizeof(pldm_header) + version_len; */ u8 version_string[]; /* PackageVersionString */ } __packed __aligned(1); /* Firmware Device ID Record */ struct __pldmfw_record_info { __le16 record_len; /* RecordLength */ u8 descriptor_count; /* DescriptorCount */ __le32 device_update_flags; /* DeviceUpdateOptionFlags */ u8 version_type; /* ComponentImageSetVersionType */ u8 version_len; /* ComponentImageSetVersionLength */ __le16 package_data_len; /* FirmwareDevicePackageDataLength */ /* * DSP0267 also includes the following variable length fields at the * end of this structure: * * ApplicableComponents, length is component_bitmap_len from header * ComponentImageSetVersionString, length is version_len * RecordDescriptors, a series of TLVs with 16bit type and length * FirmwareDevicePackageData, length is package_data_len * * The total size of each record is * sizeof(pldmfw_record_info) + * component_bitmap_len (converted to bytes!) + * version_len + * <length of RecordDescriptors> + * package_data_len */ u8 variable_record_data[]; } __packed __aligned(1); /* Firmware Descriptor Definition */ struct __pldmfw_desc_tlv { __le16 type; /* DescriptorType */ __le16 size; /* DescriptorSize */ u8 data[]; /* DescriptorData */ } __aligned(1); /* Firmware Device Identification Area */ struct __pldmfw_record_area { u8 record_count; /* DeviceIDRecordCount */ /* This is not a struct type because the size of each record varies */ u8 records[]; } __aligned(1); /* Individual Component Image Information */ struct __pldmfw_component_info { __le16 classification; /* ComponentClassfication */ __le16 identifier; /* ComponentIdentifier */ __le32 comparison_stamp; /* ComponentComparisonStamp */ __le16 options; /* componentOptions */ __le16 activation_method; /* RequestedComponentActivationMethod */ __le32 location_offset; /* ComponentLocationOffset */ __le32 size; /* ComponentSize */ u8 version_type; /* ComponentVersionStringType */ u8 version_len; /* ComponentVersionStringLength */ /* * DSP0267 also includes the following variable length fields at the * end of this structure: * * ComponentVersionString, length is version_len * * The total size of this section is * sizeof(pldmfw_component_info) + version_len; */ u8 version_string[]; /* ComponentVersionString */ } __packed __aligned(1); /* Component Image Information Area */ struct __pldmfw_component_area { __le16 component_image_count; /* This is not a struct type because the component size varies */ u8 components[]; } __aligned(1); /** * pldm_first_desc_tlv * @start: byte offset of the start of the descriptor TLVs * * Converts the starting offset of the descriptor TLVs into a pointer to the * first descriptor. */ #define pldm_first_desc_tlv(start) \ ((const struct __pldmfw_desc_tlv *)(start)) /** * pldm_next_desc_tlv * @desc: pointer to a descriptor TLV * * Finds the pointer to the next descriptor following a given descriptor */ #define pldm_next_desc_tlv(desc) \ ((const struct __pldmfw_desc_tlv *)((desc)->data + \ get_unaligned_le16(&(desc)->size))) /** * pldm_for_each_desc_tlv * @i: variable to store descriptor index * @desc: variable to store descriptor pointer * @start: byte offset of the start of the descriptors * @count: the number of descriptors * * for loop macro to iterate over all of the descriptors of a given PLDM * record. */ #define pldm_for_each_desc_tlv(i, desc, start, count) \ for ((i) = 0, (desc) = pldm_first_desc_tlv(start); \ (i) < (count); \ (i)++, (desc) = pldm_next_desc_tlv(desc)) /** * pldm_first_record * @start: byte offset of the start of the PLDM records * * Converts a starting offset of the PLDM records into a pointer to the first * record. */ #define pldm_first_record(start) \ ((const struct __pldmfw_record_info *)(start)) /** * pldm_next_record * @record: pointer to a PLDM record * * Finds a pointer to the next record following a given record */ #define pldm_next_record(record) \ ((const struct __pldmfw_record_info *) \ ((const u8 *)(record) + get_unaligned_le16(&(record)->record_len))) /** * pldm_for_each_record * @i: variable to store record index * @record: variable to store record pointer * @start: byte offset of the start of the records * @count: the number of records * * for loop macro to iterate over all of the records of a PLDM file. */ #define pldm_for_each_record(i, record, start, count) \ for ((i) = 0, (record) = pldm_first_record(start); \ (i) < (count); \ (i)++, (record) = pldm_next_record(record)) /** * pldm_first_component * @start: byte offset of the start of the PLDM components * * Convert a starting offset of the PLDM components into a pointer to the * first component */ #define pldm_first_component(start) \ ((const struct __pldmfw_component_info *)(start)) /** * pldm_next_component * @component: pointer to a PLDM component * * Finds a pointer to the next component following a given component */ #define pldm_next_component(component) \ ((const struct __pldmfw_component_info *)((component)->version_string + \ (component)->version_len)) /** * pldm_for_each_component * @i: variable to store component index * @component: variable to store component pointer * @start: byte offset to the start of the first component * @count: the number of components * * for loop macro to iterate over all of the components of a PLDM file. */ #define pldm_for_each_component(i, component, start, count) \ for ((i) = 0, (component) = pldm_first_component(start); \ (i) < (count); \ (i)++, (component) = pldm_next_component(component)) #endif