root/lib/pldmfw/pldmfw_private.h
/* 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